@saptools/service-flow 0.1.70 → 0.1.72

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +9 -5
  3. package/TECHNICAL-NOTE.md +13 -0
  4. package/dist/{chunk-GSLFY6J2.js → chunk-Z6D433R5.js} +8696 -6476
  5. package/dist/chunk-Z6D433R5.js.map +1 -0
  6. package/dist/cli.js +685 -142
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.d.ts +53 -14
  9. package/dist/index.js +2 -23
  10. package/dist/index.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/cli/doctor-event-quality.ts +371 -0
  13. package/src/cli/doctor.ts +4 -6
  14. package/src/cli.ts +1 -1
  15. package/src/db/call-fact-repository.ts +6 -3
  16. package/src/db/current-fact-semantics.ts +5 -5
  17. package/src/db/event-fact-semantics.ts +347 -0
  18. package/src/db/event-surface-invalidation.ts +38 -0
  19. package/src/db/fact-json-inventory.ts +16 -0
  20. package/src/db/migrations.ts +12 -1
  21. package/src/db/package-target-invalidation.ts +79 -0
  22. package/src/db/repositories.ts +28 -2
  23. package/src/db/schema-structure.ts +41 -1
  24. package/src/db/schema.ts +6 -2
  25. package/src/indexer/repository-indexer.ts +45 -6
  26. package/src/linker/cross-repo-linker.ts +25 -3
  27. package/src/linker/event-environment-link.ts +211 -0
  28. package/src/linker/event-shape-candidate-linker.ts +161 -0
  29. package/src/linker/event-subscription-handler-linker.ts +123 -19
  30. package/src/linker/event-template-link.ts +40 -6
  31. package/src/linker/package-event-constant-resolver.ts +298 -0
  32. package/src/output/table-output.ts +13 -1
  33. package/src/parsers/decorator-parser.ts +9 -53
  34. package/src/parsers/environment-declarations.ts +327 -0
  35. package/src/parsers/event-call-analysis.ts +242 -0
  36. package/src/parsers/event-environment-reference.ts +231 -0
  37. package/src/parsers/event-loop-registration.ts +132 -0
  38. package/src/parsers/event-name-import-resolution.ts +243 -0
  39. package/src/parsers/event-receiver-analysis.ts +404 -0
  40. package/src/parsers/event-subscription-facts.ts +4 -0
  41. package/src/parsers/generated-constants-parser.ts +80 -14
  42. package/src/parsers/outbound-call-classifier.ts +27 -124
  43. package/src/parsers/outbound-call-parser.ts +13 -1
  44. package/src/parsers/outbound-expression-analysis.ts +2 -2
  45. package/src/parsers/stable-local-value.ts +30 -9
  46. package/src/parsers/string-constant-lookups.ts +358 -0
  47. package/src/trace/event-runtime-resolution.ts +24 -3
  48. package/src/trace/event-shape-candidate-trace.ts +172 -0
  49. package/src/trace/event-subscriber-traversal.ts +19 -7
  50. package/src/trace/evidence.ts +7 -0
  51. package/src/trace/trace-scope-execution.ts +21 -29
  52. package/src/types.ts +17 -1
  53. package/src/utils/event-skeleton.ts +207 -0
  54. package/src/version.ts +1 -1
  55. package/dist/chunk-GSLFY6J2.js.map +0 -1
package/dist/cli.js CHANGED
@@ -8,14 +8,20 @@ import {
8
8
  bindingSite,
9
9
  classifyODataPathIntent,
10
10
  classifyOutboundCallsInSource,
11
+ collectEnvironmentDeclarations,
12
+ collectStringConstantLookups,
11
13
  collectSymbolImportBindings,
12
14
  compactTrace,
13
15
  containsSupportedOutboundCall,
14
16
  createBindingLexicalIndex,
17
+ createEventEnvironmentReferenceResolver,
18
+ createImportedEventNameResolver,
15
19
  derivedMemberImportReference,
16
20
  discoverRepositories,
21
+ emptyEnvironmentDeclarations,
17
22
  executableSymbolCandidates,
18
23
  factLifecycleDiagnostic,
24
+ generatedConstantFacts,
19
25
  identifierMatchesDeclaration,
20
26
  implementationHintSuggestions,
21
27
  insertCalls,
@@ -42,13 +48,15 @@ import {
42
48
  projectBoundedInOrder,
43
49
  recordPreparedSnapshotFailure,
44
50
  redactValue,
51
+ resolveBinding,
45
52
  selectCallOwner,
46
53
  selectVisibleBinding,
47
54
  selectorRepoAmbiguousDiagnostic,
55
+ sha256Text,
48
56
  stableLocalValueReference,
49
57
  symbolImportReference,
50
58
  trace
51
- } from "./chunk-GSLFY6J2.js";
59
+ } from "./chunk-Z6D433R5.js";
52
60
 
53
61
  // src/cli.ts
54
62
  import { Command, Option } from "commander";
@@ -250,8 +258,8 @@ function upsertRepository(db, workspaceId, r) {
250
258
  db.prepare(
251
259
  `INSERT INTO repositories(workspace_id,name,absolute_path,relative_path,
252
260
  package_name,package_version,dependencies_json,
253
- package_public_surface_json,kind,is_git_repo)
254
- VALUES(?,?,?,?,?,?,?,?,?,?)
261
+ package_public_surface_json,environment_declarations_json,kind,is_git_repo)
262
+ VALUES(?,?,?,?,?,?,?,?,?,?,?)
255
263
  ON CONFLICT(workspace_id,absolute_path) DO UPDATE SET
256
264
  name=excluded.name,relative_path=excluded.relative_path,
257
265
  package_name=excluded.package_name,
@@ -266,6 +274,7 @@ function upsertRepository(db, workspaceId, r) {
266
274
  r.packageVersion,
267
275
  JSON.stringify(r.dependencies ?? {}),
268
276
  initialPackagePublicSurface(r.packageName),
277
+ JSON.stringify(emptyEnvironmentDeclarations()),
269
278
  r.kind ?? "unknown",
270
279
  r.isGitRepo ? 1 : 0
271
280
  );
@@ -292,6 +301,7 @@ function clearRepoFacts(db, repoId) {
292
301
  "symbol_calls",
293
302
  "handler_registrations",
294
303
  "service_bindings",
304
+ "generated_constants",
295
305
  "symbols",
296
306
  "diagnostics",
297
307
  "files"
@@ -299,6 +309,32 @@ function clearRepoFacts(db, repoId) {
299
309
  db.prepare(`DELETE FROM ${t} WHERE repo_id=?`).run(repoId);
300
310
  db.prepare("DELETE FROM search_index WHERE repo=?").run(String(repoId));
301
311
  }
312
+ function insertGeneratedConstants(db, repoId, rows) {
313
+ const stmt = db.prepare(`INSERT INTO generated_constants(
314
+ repo_id,source_file,source_line,name,container_name,member_name,value,
315
+ constant_kind,exported,stable,resolution_status,unresolved_reason,
316
+ declaration_start_offset,declaration_end_offset,
317
+ value_start_offset,value_end_offset
318
+ ) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`);
319
+ for (const row of rows) stmt.run(
320
+ repoId,
321
+ row.sourceFile,
322
+ row.sourceLine,
323
+ row.name,
324
+ row.containerName ?? null,
325
+ row.memberName ?? null,
326
+ row.value ?? null,
327
+ row.constantKind,
328
+ row.exported ? 1 : 0,
329
+ row.stable ? 1 : 0,
330
+ row.resolutionStatus,
331
+ row.unresolvedReason ?? null,
332
+ row.declarationStartOffset,
333
+ row.declarationEndOffset,
334
+ row.valueStartOffset,
335
+ row.valueEndOffset
336
+ );
337
+ }
302
338
  function insertRequires(db, repoId, rows) {
303
339
  const stmt = db.prepare(
304
340
  "INSERT INTO cds_requires(repo_id,alias,kind,model,destination,service_path,request_timeout,raw_json) VALUES(?,?,?,?,?,?,?,?)"
@@ -612,10 +648,10 @@ import path5 from "path";
612
648
  // src/parsers/symbol-parser.ts
613
649
  import fs4 from "fs/promises";
614
650
  import path4 from "path";
615
- import ts8 from "typescript";
651
+ import ts9 from "typescript";
616
652
 
617
653
  // src/parsers/event-subscription-facts.ts
618
- import ts2 from "typescript";
654
+ import ts3 from "typescript";
619
655
 
620
656
  // src/parsers/local-symbol-reference.ts
621
657
  import ts from "typescript";
@@ -752,6 +788,89 @@ function localSymbolTarget(expression, source, symbols) {
752
788
  return declaration ? exactPropertyTarget(expression, declaration, symbols, source) : void 0;
753
789
  }
754
790
 
791
+ // src/parsers/event-loop-registration.ts
792
+ import ts2 from "typescript";
793
+ var loopValueCap = 32;
794
+ var loopExpressionLimit = 256;
795
+ function unwrap(expression) {
796
+ if (ts2.isParenthesizedExpression(expression) || ts2.isAsExpression(expression) || ts2.isSatisfiesExpression(expression) || ts2.isTypeAssertionExpression(expression))
797
+ return unwrap(expression.expression);
798
+ return expression;
799
+ }
800
+ function stringArray(expression) {
801
+ const value = unwrap(expression);
802
+ if (!ts2.isArrayLiteralExpression(value)) return void 0;
803
+ const strings = value.elements.flatMap((element) => {
804
+ const item = ts2.isSpreadElement(element) ? void 0 : unwrap(element);
805
+ return item && ts2.isStringLiteralLike(item) ? [item.text] : [];
806
+ });
807
+ return strings.length === value.elements.length ? strings : void 0;
808
+ }
809
+ function identifierArray(identifier) {
810
+ const binding = resolveBinding(identifier, identifier);
811
+ return binding.immutable && binding.initializer ? stringArray(binding.initializer) : void 0;
812
+ }
813
+ function orderedValues(values) {
814
+ return [...values].sort((left, right) => left.declarationStartOffset - right.declarationStartOffset).map((item) => item.value);
815
+ }
816
+ function objectValues(expression, source) {
817
+ if (!ts2.isPropertyAccessExpression(expression.expression))
818
+ return void 0;
819
+ const root = expression.expression.expression;
820
+ if (!ts2.isIdentifier(root) || root.text !== "Object" || lexicalIdentifierDeclaration(root) || expression.expression.name.text !== "values" || expression.arguments.length !== 1) return void 0;
821
+ const argument = expression.arguments[0];
822
+ if (!argument || !ts2.isIdentifier(argument)) return void 0;
823
+ const lookups = collectStringConstantLookups(source);
824
+ const prefix = `${argument.text}.`;
825
+ const refused = [...lookups.refusedMembers.keys()].some((key) => key.startsWith(prefix));
826
+ const values = [
827
+ ...lookups.enumMembers.values(),
828
+ ...lookups.objectProperties.values()
829
+ ].filter((item) => item.key.startsWith(prefix));
830
+ return !refused && values.length > 0 ? orderedValues(values) : void 0;
831
+ }
832
+ function staticCollectionValues(expression, source) {
833
+ const value = unwrap(expression);
834
+ if (ts2.isArrayLiteralExpression(value)) return stringArray(value);
835
+ if (ts2.isIdentifier(value)) return identifierArray(value);
836
+ return ts2.isCallExpression(value) ? objectValues(value, source) : void 0;
837
+ }
838
+ function enclosingForEach(node) {
839
+ let current = node.parent;
840
+ while (current && !ts2.isSourceFile(current)) {
841
+ if ((ts2.isArrowFunction(current) || ts2.isFunctionExpression(current)) && ts2.isCallExpression(current.parent) && current.parent.arguments.includes(current) && ts2.isPropertyAccessExpression(current.parent.expression) && current.parent.expression.name.text === "forEach")
842
+ return current.parent;
843
+ if (ts2.isFunctionLike(current)) return void 0;
844
+ current = current.parent;
845
+ }
846
+ return void 0;
847
+ }
848
+ function boundedExpression(expression, source) {
849
+ return expression.getText(source).slice(0, loopExpressionLimit);
850
+ }
851
+ function eventLoopRegistrationEvidence(node, source) {
852
+ const loop = enclosingForEach(node);
853
+ if (!loop || !ts2.isPropertyAccessExpression(loop.expression)) return {};
854
+ const collection = loop.expression.expression;
855
+ const values = staticCollectionValues(collection, source);
856
+ if (!values) return {
857
+ subscriptionRegisteredInLoop: true,
858
+ subscriptionLoopRegistrationStatus: "unresolved",
859
+ subscriptionLoopUnresolvedReason: "subscription_loop_collection_not_statically_enumerable",
860
+ subscriptionLoopCollectionExpression: boundedExpression(collection, source)
861
+ };
862
+ const shown = values.slice(0, loopValueCap);
863
+ return {
864
+ subscriptionRegisteredInLoop: true,
865
+ subscriptionLoopRegistrationStatus: "enumerated",
866
+ subscriptionLoopCollectionExpression: boundedExpression(collection, source),
867
+ subscriptionLoopRegistrationCount: values.length,
868
+ subscriptionLoopValues: shown,
869
+ shownSubscriptionLoopValueCount: shown.length,
870
+ omittedSubscriptionLoopValueCount: Math.max(0, values.length - shown.length)
871
+ };
872
+ }
873
+
755
874
  // src/parsers/event-subscription-facts.ts
756
875
  function lineOf(source, node) {
757
876
  return source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
@@ -766,7 +885,7 @@ function importRelation(binding) {
766
885
  function directTarget(expression, source, imports, symbols) {
767
886
  const imported = symbolImportReference(expression, imports);
768
887
  if (imported) return importedTarget(expression, source, imported);
769
- if (ts2.isIdentifier(expression)) {
888
+ if (ts3.isIdentifier(expression)) {
770
889
  const exact = localSymbolTarget(expression, source, symbols);
771
890
  return {
772
891
  calleeExpression: expression.text,
@@ -789,7 +908,7 @@ function importedTarget(expression, source, binding) {
789
908
  };
790
909
  }
791
910
  function propertyTarget(expression, source, symbols) {
792
- if (!ts2.isPropertyAccessExpression(expression) || expression.questionDotToken || !ts2.isIdentifier(expression.expression)) return void 0;
911
+ if (!ts3.isPropertyAccessExpression(expression) || expression.questionDotToken || !ts3.isIdentifier(expression.expression)) return void 0;
793
912
  const exact = localSymbolTarget(expression, source, symbols);
794
913
  return {
795
914
  calleeExpression: expression.getText(source),
@@ -809,7 +928,7 @@ function classifyHandlerReference(expression, source, imports = collectSymbolImp
809
928
  "handler_argument_missing",
810
929
  "missing"
811
930
  );
812
- if (ts2.isArrowFunction(expression) || ts2.isFunctionExpression(expression))
931
+ if (ts3.isArrowFunction(expression) || ts3.isFunctionExpression(expression))
813
932
  return unsupportedClassification(
814
933
  "unsupported_inline",
815
934
  "inline_handler_body_not_indexed",
@@ -818,7 +937,7 @@ function classifyHandlerReference(expression, source, imports = collectSymbolImp
818
937
  const direct = directTarget(expression, source, imports, symbols);
819
938
  if (direct)
820
939
  return { status: "role_required", referenceShape: direct.referenceShape, target: direct };
821
- if (!ts2.isCallExpression(expression))
940
+ if (!ts3.isCallExpression(expression))
822
941
  return unsupportedClassification(
823
942
  "unsupported_reference_shape",
824
943
  "handler_reference_shape_unsupported",
@@ -906,6 +1025,7 @@ function enrichSubscription(source, classified, symbols) {
906
1025
  );
907
1026
  const evidence = {
908
1027
  ...classified.fact.evidence ?? {},
1028
+ ...eventLoopRegistrationEvidence(classified.node, source),
909
1029
  handlerReferenceStatus: classification.status,
910
1030
  ...classification.reason ? { handlerReferenceReason: classification.reason } : {},
911
1031
  handlerReferenceShape: classification.referenceShape
@@ -939,7 +1059,7 @@ function reconcileEventSubscriptions(source, classifications, symbols, calls) {
939
1059
  import "typescript";
940
1060
 
941
1061
  // src/parsers/binding-identity.ts
942
- import ts3 from "typescript";
1062
+ import ts4 from "typescript";
943
1063
  var scopeChainCap = 16;
944
1064
  function matchFactsToSites(index, facts) {
945
1065
  return facts.map((fact) => {
@@ -1059,9 +1179,9 @@ function assertUniqueSites(bindings) {
1059
1179
  function callNodeMap(source) {
1060
1180
  const calls = /* @__PURE__ */ new Map();
1061
1181
  const visit = (node) => {
1062
- if (ts3.isCallExpression(node))
1182
+ if (ts4.isCallExpression(node))
1063
1183
  calls.set(`${node.getStart(source)}:${node.getEnd()}`, node);
1064
- ts3.forEachChild(node, visit);
1184
+ ts4.forEachChild(node, visit);
1065
1185
  };
1066
1186
  visit(source);
1067
1187
  return calls;
@@ -1254,7 +1374,7 @@ function reconcileSourceFacts(source, classifications, bindings, outboundCalls,
1254
1374
  }
1255
1375
 
1256
1376
  // src/parsers/symbol-call-facts.ts
1257
- import ts5 from "typescript";
1377
+ import ts6 from "typescript";
1258
1378
  var commonTerminalMembers = /* @__PURE__ */ new Set([
1259
1379
  "push",
1260
1380
  "includes",
@@ -1322,8 +1442,8 @@ var cdsFrameworkPrefixes = [
1322
1442
  "cds.parse."
1323
1443
  ];
1324
1444
  function symbolCallName(expr) {
1325
- if (ts5.isIdentifier(expr)) return { expression: expr.text, local: expr.text };
1326
- if (!ts5.isPropertyAccessExpression(expr))
1445
+ if (ts6.isIdentifier(expr)) return { expression: expr.text, local: expr.text };
1446
+ if (!ts6.isPropertyAccessExpression(expr))
1327
1447
  return { expression: expr.getText() };
1328
1448
  const left = expr.expression.getText();
1329
1449
  return {
@@ -1355,25 +1475,25 @@ function ignoredFrameworkCall(callee) {
1355
1475
  }
1356
1476
  function argumentEvidence(args) {
1357
1477
  return args.map((arg) => {
1358
- if (ts5.isIdentifier(arg)) return { kind: "identifier", name: arg.text };
1359
- if (ts5.isArrayLiteralExpression(arg)) return {
1478
+ if (ts6.isIdentifier(arg)) return { kind: "identifier", name: arg.text };
1479
+ if (ts6.isArrayLiteralExpression(arg)) return {
1360
1480
  kind: "array_literal",
1361
- elements: arg.elements.flatMap((item, index) => ts5.isIdentifier(item) ? [{ index, kind: "identifier", name: item.text }] : [])
1481
+ elements: arg.elements.flatMap((item, index) => ts6.isIdentifier(item) ? [{ index, kind: "identifier", name: item.text }] : [])
1362
1482
  };
1363
- if (ts5.isObjectLiteralExpression(arg))
1483
+ if (ts6.isObjectLiteralExpression(arg))
1364
1484
  return objectArgumentEvidence(arg);
1365
1485
  return { kind: "unsupported", expression: arg.getText() };
1366
1486
  });
1367
1487
  }
1368
1488
  function objectArgumentEvidence(argument) {
1369
1489
  const properties = argument.properties.flatMap((property) => {
1370
- if (ts5.isShorthandPropertyAssignment(property))
1490
+ if (ts6.isShorthandPropertyAssignment(property))
1371
1491
  return [{
1372
1492
  kind: "shorthand",
1373
1493
  property: property.name.text,
1374
1494
  argument: property.name.text
1375
1495
  }];
1376
- if (!ts5.isPropertyAssignment(property) || !ts5.isIdentifier(property.initializer)) return [];
1496
+ if (!ts6.isPropertyAssignment(property) || !ts6.isIdentifier(property.initializer)) return [];
1377
1497
  const name = propertyName(property.name);
1378
1498
  return name ? [{
1379
1499
  kind: "property_assignment",
@@ -1384,7 +1504,7 @@ function objectArgumentEvidence(argument) {
1384
1504
  return { kind: "object_literal", properties };
1385
1505
  }
1386
1506
  function propertyName(name) {
1387
- return ts5.isIdentifier(name) || ts5.isStringLiteralLike(name) || ts5.isNumericLiteral(name) ? name.text : void 0;
1507
+ return ts6.isIdentifier(name) || ts6.isStringLiteralLike(name) || ts6.isNumericLiteral(name) ? name.text : void 0;
1388
1508
  }
1389
1509
  function exactCaller(collection, node) {
1390
1510
  const selected = selectCallOwner(
@@ -1445,9 +1565,9 @@ function callFact(collection, node) {
1445
1565
  }
1446
1566
  function expressionRoot(expression) {
1447
1567
  let current = expression;
1448
- while (ts5.isPropertyAccessExpression(current))
1568
+ while (ts6.isPropertyAccessExpression(current))
1449
1569
  current = current.expression;
1450
- return ts5.isIdentifier(current) ? current : void 0;
1570
+ return ts6.isIdentifier(current) ? current : void 0;
1451
1571
  }
1452
1572
  function exactDeclaredContext(identifier, candidates) {
1453
1573
  const matches = candidates.filter((candidate) => identifierMatchesDeclaration(
@@ -1478,8 +1598,8 @@ function callInstance(collection, node, callee) {
1478
1598
  }
1479
1599
  function enclosingClass(node) {
1480
1600
  let current = node.parent;
1481
- while (current && !ts5.isSourceFile(current)) {
1482
- if (ts5.isClassLike(current)) return current;
1601
+ while (current && !ts6.isSourceFile(current)) {
1602
+ if (ts6.isClassLike(current)) return current;
1483
1603
  current = current.parent;
1484
1604
  }
1485
1605
  return void 0;
@@ -1619,26 +1739,26 @@ function parserCandidateStrategy(context) {
1619
1739
  function collectSymbolCallFacts(collection) {
1620
1740
  const calls = [];
1621
1741
  const visit = (node) => {
1622
- if (ts5.isCallExpression(node)) {
1742
+ if (ts6.isCallExpression(node)) {
1623
1743
  const call = callFact(collection, node);
1624
1744
  if (call) calls.push(call);
1625
1745
  }
1626
- ts5.forEachChild(node, visit);
1746
+ ts6.forEachChild(node, visit);
1627
1747
  };
1628
1748
  visit(collection.source);
1629
1749
  return calls;
1630
1750
  }
1631
1751
 
1632
1752
  // src/parsers/executable-body-eligibility.ts
1633
- import ts6 from "typescript";
1753
+ import ts7 from "typescript";
1634
1754
  function hasModifier(node, kind) {
1635
- return ts6.canHaveModifiers(node) && Boolean(ts6.getModifiers(node)?.some((item) => item.kind === kind));
1755
+ return ts7.canHaveModifiers(node) && Boolean(ts7.getModifiers(node)?.some((item) => item.kind === kind));
1636
1756
  }
1637
1757
  function executableBodyEligibility(node, source) {
1638
1758
  if (node.body) return { eligible: true, reason: "body_present" };
1639
- if (hasModifier(node, ts6.SyntaxKind.AbstractKeyword))
1759
+ if (hasModifier(node, ts7.SyntaxKind.AbstractKeyword))
1640
1760
  return { eligible: false, reason: "abstract_bodyless" };
1641
- if (hasModifier(node, ts6.SyntaxKind.DeclareKeyword))
1761
+ if (hasModifier(node, ts7.SyntaxKind.DeclareKeyword))
1642
1762
  return { eligible: false, reason: "ambient_declaration" };
1643
1763
  return {
1644
1764
  eligible: false,
@@ -1647,7 +1767,7 @@ function executableBodyEligibility(node, source) {
1647
1767
  }
1648
1768
 
1649
1769
  // src/parsers/symbol-derived-contexts.ts
1650
- import ts7 from "typescript";
1770
+ import ts8 from "typescript";
1651
1771
  var builtInConstructors = /* @__PURE__ */ new Set([
1652
1772
  "Set",
1653
1773
  "Map",
@@ -1688,23 +1808,23 @@ function appendNamed(values, name, value) {
1688
1808
  values.set(name, matches);
1689
1809
  }
1690
1810
  function assignmentOperator2(kind) {
1691
- return kind >= ts7.SyntaxKind.FirstAssignment && kind <= ts7.SyntaxKind.LastAssignment;
1811
+ return kind >= ts8.SyntaxKind.FirstAssignment && kind <= ts8.SyntaxKind.LastAssignment;
1692
1812
  }
1693
1813
  function mutationUnary2(node) {
1694
- return node.operator === ts7.SyntaxKind.PlusPlusToken || node.operator === ts7.SyntaxKind.MinusMinusToken;
1814
+ return node.operator === ts8.SyntaxKind.PlusPlusToken || node.operator === ts8.SyntaxKind.MinusMinusToken;
1695
1815
  }
1696
1816
  function receiverIdentifier(expression) {
1697
1817
  let current = expression;
1698
- while (ts7.isPropertyAccessExpression(current) || ts7.isElementAccessExpression(current))
1818
+ while (ts8.isPropertyAccessExpression(current) || ts8.isElementAccessExpression(current))
1699
1819
  current = current.expression;
1700
- return ts7.isIdentifier(current) ? current : void 0;
1820
+ return ts8.isIdentifier(current) ? current : void 0;
1701
1821
  }
1702
1822
  function nodeWritesMember(node, matches) {
1703
- if (ts7.isBinaryExpression(node))
1823
+ if (ts8.isBinaryExpression(node))
1704
1824
  return assignmentOperator2(node.operatorToken.kind) && matches(node.left);
1705
- if (ts7.isPrefixUnaryExpression(node) || ts7.isPostfixUnaryExpression(node))
1825
+ if (ts8.isPrefixUnaryExpression(node) || ts8.isPostfixUnaryExpression(node))
1706
1826
  return mutationUnary2(node) && matches(node.operand);
1707
- return ts7.isDeleteExpression(node) && matches(node.expression);
1827
+ return ts8.isDeleteExpression(node) && matches(node.expression);
1708
1828
  }
1709
1829
  function memberWrite(source, declaration) {
1710
1830
  const start = declaration.getStart(source);
@@ -1717,16 +1837,16 @@ function memberWrite(source, declaration) {
1717
1837
  const visit = (node) => {
1718
1838
  if (written) return;
1719
1839
  written = nodeWritesMember(node, matches);
1720
- if (!written) ts7.forEachChild(node, visit);
1840
+ if (!written) ts8.forEachChild(node, visit);
1721
1841
  };
1722
1842
  visit(source);
1723
1843
  return written;
1724
1844
  }
1725
1845
  function stableVariable(collection, node) {
1726
- return ts7.isIdentifier(node.name) && ts7.isVariableDeclarationList(node.parent) && (node.parent.flags & ts7.NodeFlags.Const) !== 0 && !memberWrite(collection.source, node.name);
1846
+ return ts8.isIdentifier(node.name) && ts8.isVariableDeclarationList(node.parent) && (node.parent.flags & ts8.NodeFlags.Const) !== 0 && !memberWrite(collection.source, node.name);
1727
1847
  }
1728
1848
  function collectProxy(collection, node) {
1729
- if (!ts7.isVariableDeclaration(node) || !stableVariable(collection, node) || !node.initializer || !ts7.isCallExpression(node.initializer) || !ts7.isPropertyAccessExpression(node.initializer.expression)) return;
1849
+ if (!ts8.isVariableDeclaration(node) || !stableVariable(collection, node) || !node.initializer || !ts8.isCallExpression(node.initializer) || !ts8.isPropertyAccessExpression(node.initializer.expression)) return;
1730
1850
  const callee = symbolCallName(node.initializer.expression);
1731
1851
  const binding = symbolImportReference(
1732
1852
  node.initializer.expression,
@@ -1743,8 +1863,8 @@ function collectProxy(collection, node) {
1743
1863
  });
1744
1864
  }
1745
1865
  function propertyContainer2(declaration, propertyName3) {
1746
- const property = propertyName3 && ts7.isPropertyDeclaration(declaration.parent) ? declaration.parent : void 0;
1747
- return property && ts7.isClassLike(property.parent) ? property.parent : void 0;
1866
+ const property = propertyName3 && ts8.isPropertyDeclaration(declaration.parent) ? declaration.parent : void 0;
1867
+ return property && ts8.isClassLike(property.parent) ? property.parent : void 0;
1748
1868
  }
1749
1869
  function eligibleClassName(collection, className, imported) {
1750
1870
  if (builtInConstructors.has(className)) return false;
@@ -1775,43 +1895,43 @@ function rememberInstance(collection, declaration, classExpression, propertyName
1775
1895
  });
1776
1896
  }
1777
1897
  function collectVariableInstance(collection, node) {
1778
- if (!ts7.isVariableDeclaration(node) || !stableVariable(collection, node))
1898
+ if (!ts8.isVariableDeclaration(node) || !stableVariable(collection, node))
1779
1899
  return;
1780
1900
  const initializer = node.initializer;
1781
- if (initializer && ts7.isNewExpression(initializer) && ts7.isIdentifier(initializer.expression))
1901
+ if (initializer && ts8.isNewExpression(initializer) && ts8.isIdentifier(initializer.expression))
1782
1902
  rememberInstance(collection, node.name, initializer.expression);
1783
1903
  }
1784
1904
  function propertyName2(name) {
1785
- return ts7.isIdentifier(name) || ts7.isStringLiteralLike(name) || ts7.isNumericLiteral(name) ? name.text : void 0;
1905
+ return ts8.isIdentifier(name) || ts8.isStringLiteralLike(name) || ts8.isNumericLiteral(name) ? name.text : void 0;
1786
1906
  }
1787
1907
  function thisPropertyWrite(expression, name) {
1788
- if (ts7.isPropertyAccessExpression(expression))
1789
- return expression.expression.kind === ts7.SyntaxKind.ThisKeyword && expression.name.text === name;
1790
- return ts7.isElementAccessExpression(expression) && expression.expression.kind === ts7.SyntaxKind.ThisKeyword && Boolean(expression.argumentExpression && ts7.isStringLiteralLike(expression.argumentExpression) && expression.argumentExpression.text === name);
1908
+ if (ts8.isPropertyAccessExpression(expression))
1909
+ return expression.expression.kind === ts8.SyntaxKind.ThisKeyword && expression.name.text === name;
1910
+ return ts8.isElementAccessExpression(expression) && expression.expression.kind === ts8.SyntaxKind.ThisKeyword && Boolean(expression.argumentExpression && ts8.isStringLiteralLike(expression.argumentExpression) && expression.argumentExpression.text === name);
1791
1911
  }
1792
1912
  function nodeWritesThisProperty(node, name) {
1793
- if (ts7.isBinaryExpression(node))
1913
+ if (ts8.isBinaryExpression(node))
1794
1914
  return assignmentOperator2(node.operatorToken.kind) && thisPropertyWrite(node.left, name);
1795
- if (ts7.isPrefixUnaryExpression(node) || ts7.isPostfixUnaryExpression(node))
1915
+ if (ts8.isPrefixUnaryExpression(node) || ts8.isPostfixUnaryExpression(node))
1796
1916
  return mutationUnary2(node) && thisPropertyWrite(node.operand, name);
1797
- return ts7.isDeleteExpression(node) && thisPropertyWrite(node.expression, name);
1917
+ return ts8.isDeleteExpression(node) && thisPropertyWrite(node.expression, name);
1798
1918
  }
1799
1919
  function stableProperty(node, name) {
1800
- const flags = ts7.getCombinedModifierFlags(node);
1801
- if ((flags & (ts7.ModifierFlags.Private | ts7.ModifierFlags.Readonly)) === 0 || !ts7.isClassLike(node.parent)) return false;
1920
+ const flags = ts8.getCombinedModifierFlags(node);
1921
+ if ((flags & (ts8.ModifierFlags.Private | ts8.ModifierFlags.Readonly)) === 0 || !ts8.isClassLike(node.parent)) return false;
1802
1922
  let written = false;
1803
1923
  const visit = (child) => {
1804
1924
  if (written || child === node) return;
1805
1925
  written = nodeWritesThisProperty(child, name);
1806
- if (!written) ts7.forEachChild(child, visit);
1926
+ if (!written) ts8.forEachChild(child, visit);
1807
1927
  };
1808
1928
  visit(node.parent);
1809
1929
  return !written;
1810
1930
  }
1811
1931
  function collectPropertyInstance(collection, node) {
1812
- if (!ts7.isPropertyDeclaration(node)) return;
1932
+ if (!ts8.isPropertyDeclaration(node)) return;
1813
1933
  const initializer = node.initializer;
1814
- if (!initializer || !ts7.isNewExpression(initializer) || !ts7.isIdentifier(initializer.expression)) return;
1934
+ if (!initializer || !ts8.isNewExpression(initializer) || !ts8.isIdentifier(initializer.expression)) return;
1815
1935
  const name = propertyName2(node.name);
1816
1936
  if (name && stableProperty(node, name)) rememberInstance(
1817
1937
  collection,
@@ -1825,7 +1945,7 @@ function collectDerivedSymbolContexts(collection) {
1825
1945
  collectProxy(collection, node);
1826
1946
  collectVariableInstance(collection, node);
1827
1947
  collectPropertyInstance(collection, node);
1828
- ts7.forEachChild(node, visit);
1948
+ ts8.forEachChild(node, visit);
1829
1949
  };
1830
1950
  visit(collection.source);
1831
1951
  }
@@ -1836,51 +1956,51 @@ function lineOf2(source, pos) {
1836
1956
  }
1837
1957
  function nameOf(node) {
1838
1958
  if (!node) return void 0;
1839
- if (ts8.isIdentifier(node) || ts8.isStringLiteral(node) || ts8.isNumericLiteral(node)) return node.text;
1959
+ if (ts9.isIdentifier(node) || ts9.isStringLiteral(node) || ts9.isNumericLiteral(node)) return node.text;
1840
1960
  return void 0;
1841
1961
  }
1842
1962
  function isFunctionLike(node) {
1843
- return ts8.isFunctionDeclaration(node) || ts8.isMethodDeclaration(node) || ts8.isFunctionExpression(node) || ts8.isArrowFunction(node);
1963
+ return ts9.isFunctionDeclaration(node) || ts9.isMethodDeclaration(node) || ts9.isFunctionExpression(node) || ts9.isArrowFunction(node);
1844
1964
  }
1845
1965
  function exported(node) {
1846
- return Boolean(ts8.getCombinedModifierFlags(node) & ts8.ModifierFlags.Export);
1966
+ return Boolean(ts9.getCombinedModifierFlags(node) & ts9.ModifierFlags.Export);
1847
1967
  }
1848
1968
  function defaultExported(node) {
1849
1969
  return Boolean(
1850
- ts8.getCombinedModifierFlags(node) & ts8.ModifierFlags.Default
1970
+ ts9.getCombinedModifierFlags(node) & ts9.ModifierFlags.Default
1851
1971
  );
1852
1972
  }
1853
1973
  function isPublicClassMethod(node) {
1854
- const flags = ts8.getCombinedModifierFlags(node);
1855
- return (flags & ts8.ModifierFlags.Private) === 0 && (flags & ts8.ModifierFlags.Protected) === 0;
1974
+ const flags = ts9.getCombinedModifierFlags(node);
1975
+ return (flags & ts9.ModifierFlags.Private) === 0 && (flags & ts9.ModifierFlags.Protected) === 0;
1856
1976
  }
1857
1977
  function exportDeclarations(source) {
1858
1978
  const exports = /* @__PURE__ */ new Map();
1859
1979
  const visit = (node) => {
1860
- if (ts8.isExportDeclaration(node) && node.exportClause && ts8.isNamedExports(node.exportClause)) {
1980
+ if (ts9.isExportDeclaration(node) && node.exportClause && ts9.isNamedExports(node.exportClause)) {
1861
1981
  for (const el of node.exportClause.elements) exports.set((el.propertyName ?? el.name).text, el.name.text);
1862
1982
  }
1863
- ts8.forEachChild(node, visit);
1983
+ ts9.forEachChild(node, visit);
1864
1984
  };
1865
1985
  visit(source);
1866
1986
  return exports;
1867
1987
  }
1868
1988
  function isObjectFunction(node) {
1869
- return ts8.isFunctionExpression(node) || ts8.isArrowFunction(node) || ts8.isMethodDeclaration(node);
1989
+ return ts9.isFunctionExpression(node) || ts9.isArrowFunction(node) || ts9.isMethodDeclaration(node);
1870
1990
  }
1871
1991
  function requireSource(expr) {
1872
- if (!ts8.isCallExpression(expr) || !ts8.isIdentifier(expr.expression) || expr.expression.text !== "require") return void 0;
1992
+ if (!ts9.isCallExpression(expr) || !ts9.isIdentifier(expr.expression) || expr.expression.text !== "require") return void 0;
1873
1993
  const first = expr.arguments[0];
1874
- return first && ts8.isStringLiteral(first) ? first.text : void 0;
1994
+ return first && ts9.isStringLiteral(first) ? first.text : void 0;
1875
1995
  }
1876
1996
  function bindingLocalName(name, initializer) {
1877
- if (ts8.isIdentifier(name)) return name.text;
1878
- if (initializer && ts8.isIdentifier(initializer)) return initializer.text;
1997
+ if (ts9.isIdentifier(name)) return name.text;
1998
+ if (initializer && ts9.isIdentifier(initializer)) return initializer.text;
1879
1999
  return void 0;
1880
2000
  }
1881
2001
  function objectPatternAliases(pattern, parameter, source, lineNode) {
1882
2002
  return pattern.elements.flatMap((element) => {
1883
- if (element.dotDotDotToken || ts8.isObjectBindingPattern(element.name) || ts8.isArrayBindingPattern(element.name)) return [];
2003
+ if (element.dotDotDotToken || ts9.isObjectBindingPattern(element.name) || ts9.isArrayBindingPattern(element.name)) return [];
1884
2004
  const property = element.propertyName ? nameOf(element.propertyName) : nameOf(element.name);
1885
2005
  if (!property) return [];
1886
2006
  const local = bindingLocalName(element.name, element.initializer);
@@ -1888,21 +2008,21 @@ function objectPatternAliases(pattern, parameter, source, lineNode) {
1888
2008
  });
1889
2009
  }
1890
2010
  function parameterPropertyAliases(fn, source) {
1891
- const parameterNames = new Set(fn.parameters.flatMap((param) => ts8.isIdentifier(param.name) ? [param.name.text] : []));
2011
+ const parameterNames = new Set(fn.parameters.flatMap((param) => ts9.isIdentifier(param.name) ? [param.name.text] : []));
1892
2012
  if (!fn.body || parameterNames.size === 0) return [];
1893
2013
  const aliases = [];
1894
2014
  const addFromAssignment = (left, right, node) => {
1895
- if (!ts8.isObjectLiteralExpression(left) || !ts8.isIdentifier(right) || !parameterNames.has(right.text)) return;
2015
+ if (!ts9.isObjectLiteralExpression(left) || !ts9.isIdentifier(right) || !parameterNames.has(right.text)) return;
1896
2016
  for (const prop of left.properties) {
1897
- if (!ts8.isPropertyAssignment(prop)) continue;
2017
+ if (!ts9.isPropertyAssignment(prop)) continue;
1898
2018
  const property = nameOf(prop.name);
1899
- if (property && ts8.isIdentifier(prop.initializer)) aliases.push({ parameter: right.text, property, local: prop.initializer.text, kind: "object_parameter_destructure", line: lineOf2(source, node.getStart(source)) });
2019
+ if (property && ts9.isIdentifier(prop.initializer)) aliases.push({ parameter: right.text, property, local: prop.initializer.text, kind: "object_parameter_destructure", line: lineOf2(source, node.getStart(source)) });
1900
2020
  }
1901
2021
  };
1902
2022
  const visit = (node) => {
1903
- if (ts8.isVariableDeclaration(node) && ts8.isObjectBindingPattern(node.name) && node.initializer && ts8.isIdentifier(node.initializer) && parameterNames.has(node.initializer.text)) aliases.push(...objectPatternAliases(node.name, node.initializer.text, source, node));
1904
- if (ts8.isBinaryExpression(node) && node.operatorToken.kind === ts8.SyntaxKind.EqualsToken) addFromAssignment(ts8.isParenthesizedExpression(node.left) ? node.left.expression : node.left, node.right, node);
1905
- ts8.forEachChild(node, visit);
2023
+ if (ts9.isVariableDeclaration(node) && ts9.isObjectBindingPattern(node.name) && node.initializer && ts9.isIdentifier(node.initializer) && parameterNames.has(node.initializer.text)) aliases.push(...objectPatternAliases(node.name, node.initializer.text, source, node));
2024
+ if (ts9.isBinaryExpression(node) && node.operatorToken.kind === ts9.SyntaxKind.EqualsToken) addFromAssignment(ts9.isParenthesizedExpression(node.left) ? node.left.expression : node.left, node.right, node);
2025
+ ts9.forEachChild(node, visit);
1906
2026
  };
1907
2027
  visit(fn.body);
1908
2028
  const seen = /* @__PURE__ */ new Set();
@@ -1915,10 +2035,10 @@ function parameterPropertyAliases(fn, source) {
1915
2035
  }
1916
2036
  function parameterBindings(params) {
1917
2037
  return params.flatMap((param, index) => {
1918
- if (ts8.isIdentifier(param.name)) return [{ index, kind: "identifier", name: param.name.text }];
1919
- if (ts8.isObjectBindingPattern(param.name)) {
2038
+ if (ts9.isIdentifier(param.name)) return [{ index, kind: "identifier", name: param.name.text }];
2039
+ if (ts9.isObjectBindingPattern(param.name)) {
1920
2040
  const properties = param.name.elements.flatMap((element) => {
1921
- if (element.dotDotDotToken || ts8.isObjectBindingPattern(element.name) || ts8.isArrayBindingPattern(element.name)) return [];
2041
+ if (element.dotDotDotToken || ts9.isObjectBindingPattern(element.name) || ts9.isArrayBindingPattern(element.name)) return [];
1922
2042
  const property = element.propertyName ? nameOf(element.propertyName) : nameOf(element.name);
1923
2043
  if (!property) return [];
1924
2044
  const local = bindingLocalName(element.name, element.initializer);
@@ -1926,8 +2046,8 @@ function parameterBindings(params) {
1926
2046
  });
1927
2047
  return properties.length > 0 ? [{ index, kind: "object_pattern", properties }] : [];
1928
2048
  }
1929
- if (ts8.isArrayBindingPattern(param.name)) {
1930
- const elements = param.name.elements.flatMap((element, elementIndex) => ts8.isBindingElement(element) && !element.dotDotDotToken && ts8.isIdentifier(element.name) ? [{ index: elementIndex, local: element.name.text }] : []);
2049
+ if (ts9.isArrayBindingPattern(param.name)) {
2050
+ const elements = param.name.elements.flatMap((element, elementIndex) => ts9.isBindingElement(element) && !element.dotDotDotToken && ts9.isIdentifier(element.name) ? [{ index: elementIndex, local: element.name.text }] : []);
1931
2051
  return elements.length > 0 ? [{ index, kind: "array_pattern", elements }] : [];
1932
2052
  }
1933
2053
  return [];
@@ -1938,9 +2058,9 @@ function symbolSourceEvidence(collection, node, options) {
1938
2058
  if (options.classMemberExported) return {
1939
2059
  source: "exported_class_member",
1940
2060
  exportedClass: options.parentRoot,
1941
- memberKind: ts8.getCombinedModifierFlags(node) & ts8.ModifierFlags.Static ? "static_method" : "class_method"
2061
+ memberKind: ts9.getCombinedModifierFlags(node) & ts9.ModifierFlags.Static ? "static_method" : "class_method"
1942
2062
  };
1943
- if (options.classContainerExported && ts8.isMethodDeclaration(node) && isPublicClassMethod(node)) return {
2063
+ if (options.classContainerExported && ts9.isMethodDeclaration(node) && isPublicClassMethod(node)) return {
1944
2064
  source: "exported_class_instance_member",
1945
2065
  exportedClass: options.parentRoot,
1946
2066
  exportedClassExportKind: options.classContainerDefaultExported ? "default" : "named",
@@ -1964,8 +2084,8 @@ function executableEvidence(node, source) {
1964
2084
  };
1965
2085
  }
1966
2086
  function exportedClassMember(collection, kind, parentName, parentRoot, node) {
1967
- if (kind !== "method" || !parentName || !collection.exportedClasses.has(parentRoot) || !ts8.isMethodDeclaration(node)) return false;
1968
- return Boolean(ts8.getCombinedModifierFlags(node) & ts8.ModifierFlags.Static) && isPublicClassMethod(node);
2087
+ if (kind !== "method" || !parentName || !collection.exportedClasses.has(parentRoot) || !ts9.isMethodDeclaration(node)) return false;
2088
+ return Boolean(ts9.getCombinedModifierFlags(node) & ts9.ModifierFlags.Static) && isPublicClassMethod(node);
1969
2089
  }
1970
2090
  function classExportState(collection, parentName, parentRoot) {
1971
2091
  return {
@@ -2062,41 +2182,41 @@ function addAliasSymbol(collection, objectName, propertyName3, node) {
2062
2182
  }
2063
2183
  function collectImportSources(collection) {
2064
2184
  const visit = (node) => {
2065
- if (ts8.isImportDeclaration(node) && ts8.isStringLiteral(node.moduleSpecifier))
2185
+ if (ts9.isImportDeclaration(node) && ts9.isStringLiteral(node.moduleSpecifier))
2066
2186
  collectEsmImportSources(collection.imports, node);
2067
- if (ts8.isVariableStatement(node))
2187
+ if (ts9.isVariableStatement(node))
2068
2188
  collectCjsImportSources(collection.imports, node);
2069
- ts8.forEachChild(node, visit);
2189
+ ts9.forEachChild(node, visit);
2070
2190
  };
2071
2191
  visit(collection.source);
2072
2192
  }
2073
2193
  function collectEsmImportSources(imports, node) {
2074
- if (!ts8.isStringLiteral(node.moduleSpecifier)) return;
2194
+ if (!ts9.isStringLiteral(node.moduleSpecifier)) return;
2075
2195
  const source = node.moduleSpecifier.text;
2076
2196
  const clause = node.importClause;
2077
2197
  if (clause?.name) imports.set(clause.name.text, source);
2078
2198
  const named = clause?.namedBindings;
2079
- if (named && ts8.isNamedImports(named))
2199
+ if (named && ts9.isNamedImports(named))
2080
2200
  for (const item of named.elements) imports.set(item.name.text, source);
2081
- if (named && ts8.isNamespaceImport(named))
2201
+ if (named && ts9.isNamespaceImport(named))
2082
2202
  imports.set(named.name.text, source);
2083
2203
  }
2084
2204
  function collectCjsImportSources(imports, node) {
2085
2205
  for (const declaration of node.declarationList.declarations) {
2086
2206
  const source = declaration.initializer ? requireSource(declaration.initializer) : void 0;
2087
2207
  if (!source) continue;
2088
- if (ts8.isIdentifier(declaration.name))
2208
+ if (ts9.isIdentifier(declaration.name))
2089
2209
  imports.set(declaration.name.text, source);
2090
- if (ts8.isObjectBindingPattern(declaration.name)) {
2210
+ if (ts9.isObjectBindingPattern(declaration.name)) {
2091
2211
  for (const item of declaration.name.elements)
2092
- if (ts8.isIdentifier(item.name)) imports.set(item.name.text, source);
2212
+ if (ts9.isIdentifier(item.name)) imports.set(item.name.text, source);
2093
2213
  }
2094
2214
  }
2095
2215
  }
2096
2216
  function classPropertySymbol(collection, node, parentClass) {
2097
2217
  const initializer = node.initializer;
2098
2218
  const localName = nameOf(node.name);
2099
- if (!localName || !initializer || !ts8.isArrowFunction(initializer) && !ts8.isFunctionExpression(initializer)) return;
2219
+ if (!localName || !initializer || !ts9.isArrowFunction(initializer) && !ts9.isFunctionExpression(initializer)) return;
2100
2220
  const staticPublic = publicStaticProperty(collection, node, parentClass);
2101
2221
  const memberKind = propertyMemberKind(initializer, staticPublic);
2102
2222
  addExecutableSymbol(
@@ -2110,22 +2230,22 @@ function classPropertySymbol(collection, node, parentClass) {
2110
2230
  );
2111
2231
  }
2112
2232
  function publicStaticProperty(collection, node, parentClass) {
2113
- const flags = ts8.getCombinedModifierFlags(node);
2114
- return collection.exportedClasses.has(parentClass) && Boolean(flags & ts8.ModifierFlags.Static) && (flags & ts8.ModifierFlags.Private) === 0 && (flags & ts8.ModifierFlags.Protected) === 0;
2233
+ const flags = ts9.getCombinedModifierFlags(node);
2234
+ return collection.exportedClasses.has(parentClass) && Boolean(flags & ts9.ModifierFlags.Static) && (flags & ts9.ModifierFlags.Private) === 0 && (flags & ts9.ModifierFlags.Protected) === 0;
2115
2235
  }
2116
2236
  function propertyMemberKind(initializer, staticPublic) {
2117
- if (ts8.isArrowFunction(initializer))
2237
+ if (ts9.isArrowFunction(initializer))
2118
2238
  return staticPublic ? "static_arrow_function" : "arrow_function_property";
2119
2239
  return staticPublic ? "static_function_expression" : "function_expression_property";
2120
2240
  }
2121
2241
  function objectCallable(property) {
2122
- if (ts8.isMethodDeclaration(property)) return property;
2123
- return ts8.isPropertyAssignment(property) && isObjectFunction(property.initializer) ? property.initializer : void 0;
2242
+ if (ts9.isMethodDeclaration(property)) return property;
2243
+ return ts9.isPropertyAssignment(property) && isObjectFunction(property.initializer) ? property.initializer : void 0;
2124
2244
  }
2125
2245
  function objectLiteralSymbols(collection, objectName, object, objectIsExported) {
2126
2246
  if (objectIsExported) collection.objectExports.add(objectName);
2127
2247
  for (const property of object.properties) {
2128
- if (objectIsExported && ts8.isShorthandPropertyAssignment(property))
2248
+ if (objectIsExported && ts9.isShorthandPropertyAssignment(property))
2129
2249
  addAliasSymbol(collection, objectName, property.name.text, property.name);
2130
2250
  const callable = objectCallable(property);
2131
2251
  const propertyName3 = callable ? nameOf(property.name) : void 0;
@@ -2152,7 +2272,7 @@ function variableSymbols(collection, node) {
2152
2272
  void 0,
2153
2273
  exported(node) ? localName : collection.exportNames.get(localName)
2154
2274
  );
2155
- if (ts8.isObjectLiteralExpression(initializer))
2275
+ if (ts9.isObjectLiteralExpression(initializer))
2156
2276
  objectLiteralSymbols(
2157
2277
  collection,
2158
2278
  localName,
@@ -2162,7 +2282,7 @@ function variableSymbols(collection, node) {
2162
2282
  }
2163
2283
  }
2164
2284
  function collectClassDeclaration(collection, node) {
2165
- if (!ts8.isClassDeclaration(node) || !node.name) return false;
2285
+ if (!ts9.isClassDeclaration(node) || !node.name) return false;
2166
2286
  collection.declaredClasses.add(node.name.text);
2167
2287
  if (exported(node) || collection.exportNames.has(node.name.text))
2168
2288
  collection.exportedClasses.add(node.name.text);
@@ -2173,7 +2293,7 @@ function collectClassDeclaration(collection, node) {
2173
2293
  return true;
2174
2294
  }
2175
2295
  function collectMethodDeclaration(collection, node, parentClass) {
2176
- if (!ts8.isMethodDeclaration(node)) return false;
2296
+ if (!ts9.isMethodDeclaration(node)) return false;
2177
2297
  const localName = nameOf(node.name);
2178
2298
  if (localName)
2179
2299
  addExecutableSymbol(collection, "method", localName, node, parentClass);
@@ -2182,11 +2302,11 @@ function collectMethodDeclaration(collection, node, parentClass) {
2182
2302
  function visitDeclaredSymbol(collection, node, parentClass) {
2183
2303
  if (collectClassDeclaration(collection, node)) return;
2184
2304
  if (collectMethodDeclaration(collection, node, parentClass)) return;
2185
- if (ts8.isPropertyDeclaration(node)) {
2305
+ if (ts9.isPropertyDeclaration(node)) {
2186
2306
  if (parentClass) classPropertySymbol(collection, node, parentClass);
2187
2307
  return;
2188
2308
  }
2189
- if (ts8.isFunctionDeclaration(node) && node.name) {
2309
+ if (ts9.isFunctionDeclaration(node) && node.name) {
2190
2310
  addExecutableSymbol(
2191
2311
  collection,
2192
2312
  "function",
@@ -2197,17 +2317,17 @@ function visitDeclaredSymbol(collection, node, parentClass) {
2197
2317
  );
2198
2318
  return;
2199
2319
  }
2200
- if (ts8.isVariableStatement(node)) {
2320
+ if (ts9.isVariableStatement(node)) {
2201
2321
  variableSymbols(collection, node);
2202
2322
  return;
2203
2323
  }
2204
- ts8.forEachChild(node, (child) => visitDeclaredSymbol(collection, child, parentClass));
2324
+ ts9.forEachChild(node, (child) => visitDeclaredSymbol(collection, child, parentClass));
2205
2325
  }
2206
2326
  function collectDeclaredSymbols(collection) {
2207
2327
  visitDeclaredSymbol(collection, collection.source);
2208
2328
  }
2209
2329
  function isTopLevelCallback(node) {
2210
- if (!ts8.isArrowFunction(node) && !ts8.isFunctionExpression(node) || !ts8.isCallExpression(node.parent)) return false;
2330
+ if (!ts9.isArrowFunction(node) && !ts9.isFunctionExpression(node) || !ts9.isCallExpression(node.parent)) return false;
2211
2331
  const callee = symbolCallName(node.parent.expression);
2212
2332
  const member = callee.member ?? callee.local;
2213
2333
  return Boolean(member && [
@@ -2249,7 +2369,7 @@ function collectCallbackSymbols(collection) {
2249
2369
  }
2250
2370
  });
2251
2371
  }
2252
- ts8.forEachChild(node, visit);
2372
+ ts9.forEachChild(node, visit);
2253
2373
  };
2254
2374
  visit(collection.source);
2255
2375
  }
@@ -2279,12 +2399,12 @@ function populateCollection(collection) {
2279
2399
  async function sourceFile(repoPath, filePath, context) {
2280
2400
  const snapshot = context?.get(filePath);
2281
2401
  const text = snapshot?.text ?? await fs4.readFile(path4.join(repoPath, filePath), "utf8");
2282
- return snapshot?.sourceFile() ?? ts8.createSourceFile(
2402
+ return snapshot?.sourceFile() ?? ts9.createSourceFile(
2283
2403
  filePath,
2284
2404
  text,
2285
- ts8.ScriptTarget.Latest,
2405
+ ts9.ScriptTarget.Latest,
2286
2406
  true,
2287
- filePath.endsWith(".ts") ? ts8.ScriptKind.TS : ts8.ScriptKind.JS
2407
+ filePath.endsWith(".ts") ? ts9.ScriptKind.TS : ts9.ScriptKind.JS
2288
2408
  );
2289
2409
  }
2290
2410
  async function parseExecutableSymbols(repoPath, filePath, context, preparedOutboundCalls) {
@@ -2366,13 +2486,6 @@ function mergePackageSymbolEvidence(symbols, analysis) {
2366
2486
  });
2367
2487
  }
2368
2488
 
2369
- // src/utils/hashing.ts
2370
- import { createHash } from "crypto";
2371
- import { readFile } from "fs/promises";
2372
- function sha256Text(text) {
2373
- return createHash("sha256").update(text).digest("hex");
2374
- }
2375
-
2376
2489
  // src/db/package-target-invalidation.ts
2377
2490
  var resolverKeys = /* @__PURE__ */ new Set([
2378
2491
  "candidateStrategy",
@@ -2403,6 +2516,11 @@ function parsedEvidence(value) {
2403
2516
  function packageName(evidence) {
2404
2517
  return parsePackageImportReference(evidence.importBinding)?.requestedPackageName ?? void 0;
2405
2518
  }
2519
+ function eventPackageName(evidence) {
2520
+ return parsePackageImportReference(
2521
+ evidence.eventNameConstantImportBinding
2522
+ )?.requestedPackageName ?? void 0;
2523
+ }
2406
2524
  function packageCallEvidenceValid(evidence) {
2407
2525
  const binding = record(evidence.importBinding);
2408
2526
  const classified = evidence.relation === "package_import" || binding?.moduleKind === "package";
@@ -2439,6 +2557,50 @@ function pendingEvidence(evidence) {
2439
2557
  unresolvedReason: "package_resolution_pending"
2440
2558
  });
2441
2559
  }
2560
+ function currentEventCalls(db, workspaceId, targetRepoId) {
2561
+ const rows = db.prepare(`SELECT c.id,c.repo_id repoId,
2562
+ c.unresolved_reason unresolvedReason,c.evidence_json evidenceJson
2563
+ FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
2564
+ WHERE r.workspace_id=? AND r.id<>? AND r.fact_analyzer_version=?
2565
+ AND json_extract(c.evidence_json,
2566
+ '$.eventNameConstantImportBinding.moduleKind')='package'
2567
+ ORDER BY c.id`).all(workspaceId, targetRepoId, ANALYZER_VERSION);
2568
+ return rows.flatMap((row) => {
2569
+ const evidence = parsedEvidence(row.evidenceJson);
2570
+ return evidence && eventPackageName(evidence) && typeof row.id === "number" && typeof row.repoId === "number" ? [{
2571
+ id: row.id,
2572
+ repoId: row.repoId,
2573
+ evidence,
2574
+ unresolvedReason: typeof row.unresolvedReason === "string" ? row.unresolvedReason : null
2575
+ }] : [];
2576
+ });
2577
+ }
2578
+ function pendingEventEvidence(evidence) {
2579
+ const parser = { ...evidence };
2580
+ delete parser.eventNameConstant;
2581
+ delete parser.eventNamePackageConstantResolution;
2582
+ parser.eventNameUnresolvedReason = "event_name_constant_resolution_pending";
2583
+ parser.eventNameStatus = "dynamic";
2584
+ parser.eventNameSourceKind = "dynamic_expression";
2585
+ parser.eventNamePlaceholderKeys = [];
2586
+ return JSON.stringify(parser);
2587
+ }
2588
+ function resetPackageEventCalls(db, workspaceId, targetRepoId, names, batch) {
2589
+ const update = db.prepare(`UPDATE outbound_calls SET event_name_expr=?,
2590
+ unresolved_reason=?,evidence_json=? WHERE id=?`);
2591
+ let matched = false;
2592
+ for (const call of currentEventCalls(db, workspaceId, targetRepoId)) {
2593
+ if (!names.has(eventPackageName(call.evidence) ?? "")) continue;
2594
+ const source = call.evidence.eventNameConstantSourceExpression;
2595
+ if (typeof source !== "string" || source.length === 0)
2596
+ throw new Error("invalid_current_package_event_constant_evidence");
2597
+ const reason = call.evidence.receiverClassification === "unproven" ? call.unresolvedReason : "event_name_constant_resolution_pending";
2598
+ update.run(source, reason, pendingEventEvidence(call.evidence), call.id);
2599
+ batch.affectedCallerRepoIds.add(call.repoId);
2600
+ matched = true;
2601
+ }
2602
+ return matched;
2603
+ }
2442
2604
  function targetWorkspace(db, repoId) {
2443
2605
  const row = db.prepare(`SELECT workspace_id workspaceId,
2444
2606
  package_name packageName FROM repositories WHERE id=?`).get(repoId);
@@ -2473,6 +2635,13 @@ function invalidatePackageTargetFacts(db, targetRepoId, newPackageName, batch) {
2473
2635
  batch.affectedCallerRepoIds.add(call.repoId);
2474
2636
  matched = true;
2475
2637
  }
2638
+ matched = resetPackageEventCalls(
2639
+ db,
2640
+ target.workspaceId,
2641
+ targetRepoId,
2642
+ names,
2643
+ batch
2644
+ ) || matched;
2476
2645
  if (matched || packageIdentityChanged(
2477
2646
  target.packageName,
2478
2647
  newPackageName
@@ -2503,6 +2672,29 @@ function finalizePackageTargetInvalidations(db, batch) {
2503
2672
  stale.run(workspaceId);
2504
2673
  }
2505
2674
 
2675
+ // src/db/event-surface-invalidation.ts
2676
+ function priorEventFactCount(db, repoId) {
2677
+ const row = db.prepare(`SELECT COUNT(*) count FROM outbound_calls
2678
+ WHERE repo_id=? AND call_type IN ('async_emit','async_subscribe')`).get(
2679
+ repoId
2680
+ );
2681
+ return Number(row?.count ?? 0);
2682
+ }
2683
+ function repositoryEnvironment(db, repoId) {
2684
+ const row = db.prepare(`SELECT environment_declarations_json value
2685
+ FROM repositories WHERE id=?`).get(repoId);
2686
+ return typeof row?.value === "string" ? row.value : null;
2687
+ }
2688
+ function invalidateEventSurfaceFacts(db, repoId, calls, nextEnvironmentJson) {
2689
+ const hasNewEvents = calls.some((call) => call.callType === "async_emit" || call.callType === "async_subscribe");
2690
+ const environmentChanged = repositoryEnvironment(db, repoId) !== nextEnvironmentJson;
2691
+ if (!hasNewEvents && priorEventFactCount(db, repoId) === 0 && !environmentChanged) return;
2692
+ db.prepare(`UPDATE repositories SET
2693
+ graph_stale_reason='event_surface_facts_changed',
2694
+ graph_stale_at=datetime('now')
2695
+ WHERE workspace_id=(SELECT workspace_id FROM repositories WHERE id=?)`).run(repoId);
2696
+ }
2697
+
2506
2698
  // src/indexer/repository-indexer.ts
2507
2699
  async function prepareRepositoryIndex(repo, force, instrumentation) {
2508
2700
  const sourceFiles = await findSourceFiles(repo.absolute_path);
@@ -2539,6 +2731,7 @@ async function prepareRepositoryIndex(repo, force, instrumentation) {
2539
2731
  kind: await classifyRepository(repo.absolute_path, packageFacts),
2540
2732
  parsed,
2541
2733
  packagePublicSurface: packageSurface.surface,
2734
+ environmentDeclarations: collectEnvironmentDeclarations(sources),
2542
2735
  fileCount: sourceFiles.length,
2543
2736
  diagnosticCount: parsed.handlers.filter((handler) => handler.hasHandlerDecorator && (handler.methods.length === 0 || handler.methods.some((method) => !handlerMethodIsExecutable(method)))).length,
2544
2737
  skipped: false
@@ -2546,23 +2739,32 @@ async function prepareRepositoryIndex(repo, force, instrumentation) {
2546
2739
  }
2547
2740
  function publishPreparedRepositoryIndex(db, prepared, invalidations) {
2548
2741
  if (prepared.skipped) return;
2549
- if (!prepared.packageFacts || !prepared.parsed || !prepared.fingerprint || !prepared.kind || !prepared.packagePublicSurface)
2742
+ if (!prepared.packageFacts || !prepared.parsed || !prepared.fingerprint || !prepared.kind || !prepared.packagePublicSurface || !prepared.environmentDeclarations)
2550
2743
  throw new Error("Prepared repository index is missing publication facts");
2551
2744
  const now = (/* @__PURE__ */ new Date()).toISOString();
2552
2745
  const repoId = prepared.repo.id;
2746
+ const environmentJson = JSON.stringify(prepared.environmentDeclarations);
2553
2747
  invalidatePackageTargetFacts(
2554
2748
  db,
2555
2749
  repoId,
2556
2750
  prepared.packageFacts.packageName,
2557
2751
  invalidations
2558
2752
  );
2753
+ invalidateEventSurfaceFacts(
2754
+ db,
2755
+ repoId,
2756
+ prepared.parsed.calls,
2757
+ environmentJson
2758
+ );
2559
2759
  db.prepare(`UPDATE repositories SET package_name=?, package_version=?,
2560
- dependencies_json=?,package_public_surface_json=?,kind=?,index_status=?
2760
+ dependencies_json=?,package_public_surface_json=?,
2761
+ environment_declarations_json=?,kind=?,index_status=?
2561
2762
  WHERE id=?`).run(
2562
2763
  prepared.packageFacts.packageName,
2563
2764
  prepared.packageFacts.packageVersion,
2564
2765
  JSON.stringify(prepared.packageFacts.dependencies),
2565
2766
  JSON.stringify(prepared.packagePublicSurface),
2767
+ environmentJson,
2566
2768
  prepared.kind,
2567
2769
  "indexing",
2568
2770
  repoId
@@ -2578,6 +2780,7 @@ function publishPreparedRepositoryIndex(db, prepared, invalidations) {
2578
2780
  insertRegistrations(db, repoId, prepared.parsed.registrations);
2579
2781
  insertBindings(db, repoId, prepared.parsed.bindings);
2580
2782
  insertCalls(db, repoId, prepared.parsed.calls);
2783
+ insertGeneratedConstants(db, repoId, prepared.parsed.generatedConstants);
2581
2784
  db.prepare("UPDATE repositories SET last_indexed_at=?, index_status='indexed', error_count=0, fingerprint=?, fact_generation=COALESCE(fact_generation,0)+1, graph_stale_reason='facts_changed', graph_stale_at=?, fact_analyzer_version=? WHERE id=?").run(now, prepared.fingerprint, now, ANALYZER_VERSION, repoId);
2582
2785
  }
2583
2786
  function publishOneRepository(db, prepared, invalidations) {
@@ -2620,14 +2823,22 @@ function recordIndexFailure(db, repoId, error) {
2620
2823
  db.prepare("INSERT INTO diagnostics(repo_id,severity,code,message) VALUES(?,?,?,?)").run(repoId, "error", "source_read_failed", `Index failed before publication; previous facts and fingerprint were preserved. ${message}`);
2621
2824
  }
2622
2825
  async function parseAllSourceFacts(root, sources) {
2623
- const facts = { services: [], handlers: [], registrations: [], bindings: [], calls: [], symbols: [], symbolCalls: [], fileRecords: [] };
2826
+ const facts = { services: [], handlers: [], registrations: [], bindings: [], calls: [], symbols: [], symbolCalls: [], generatedConstants: [], fileRecords: [] };
2624
2827
  for (const snapshot of sources.entries()) {
2625
2828
  const file = snapshot.filePath;
2626
2829
  facts.fileRecords.push({ relativePath: normalizePath(file), extension: path5.extname(file), sha256: sha256Text(snapshot.text), sizeBytes: snapshot.sizeBytes });
2627
2830
  if (file.endsWith(".cds")) facts.services.push(...await parseCdsFile(root, file, sources));
2628
2831
  if (/\.[jt]s$/.test(file)) {
2629
2832
  const source = snapshot.sourceFile();
2630
- const classified = classifyOutboundCallsInSource(source, file);
2833
+ facts.generatedConstants.push(...generatedConstantFacts(source, file));
2834
+ const classified = classifyOutboundCallsInSource(source, file, {
2835
+ importedEventNameResolver: createImportedEventNameResolver(
2836
+ sources,
2837
+ source,
2838
+ file
2839
+ ),
2840
+ eventEnvironmentReferenceResolver: createEventEnvironmentReferenceResolver(sources, source, file)
2841
+ });
2631
2842
  facts.handlers.push(...await parseDecorators(root, file, sources));
2632
2843
  facts.registrations.push(...await parseHandlerRegistrations(root, file, sources));
2633
2844
  const bindings = await parseServiceBindings(root, file, sources);
@@ -2668,12 +2879,15 @@ async function findSourceFiles(root) {
2668
2879
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
2669
2880
  if (e.isDirectory()) {
2670
2881
  if (!["node_modules", "dist", "gen", "coverage", ".git"].includes(e.name)) await walk(path5.join(dir, e.name), rel);
2671
- } else if (/\.(cds|ts|js)$/.test(e.name) && !isDefaultTestFile(rel)) out.push(rel);
2882
+ } else if (isRepositoryFactInput(e.name) && !isDefaultTestFile(rel)) out.push(rel);
2672
2883
  }
2673
2884
  }
2674
2885
  await walk(root);
2675
2886
  return out.sort();
2676
2887
  }
2888
+ function isRepositoryFactInput(name) {
2889
+ return /\.(cds|ts|js)$/.test(name) || ["nodemon.json", ".env", "mta.yaml", "manifest.yml"].includes(name);
2890
+ }
2677
2891
  function isDefaultTestFile(relativeFile) {
2678
2892
  const parts = relativeFile.split("/");
2679
2893
  if (parts.some((part) => ["test", "tests", "__tests__"].includes(part))) return true;
@@ -3249,6 +3463,332 @@ function symbolCallQuality(db) {
3249
3463
  };
3250
3464
  }
3251
3465
 
3466
+ // src/cli/doctor-event-quality.ts
3467
+ var eventNameReason = `COALESCE(
3468
+ json_extract(c.evidence_json,'$.eventNameUnresolvedReason'),
3469
+ CASE WHEN c.unresolved_reason GLOB 'dynamic_event_name_*'
3470
+ OR c.unresolved_reason GLOB 'event_name_*'
3471
+ THEN c.unresolved_reason END
3472
+ )`;
3473
+ function workspacePredicate(alias) {
3474
+ return `(? IS NULL OR ${alias}.workspace_id=?)`;
3475
+ }
3476
+ function count2(value) {
3477
+ return Number(value ?? 0);
3478
+ }
3479
+ function unresolvedEventNameExamples(db, workspaceId) {
3480
+ return db.prepare(`SELECT r.name repositoryName,
3481
+ c.source_file sourceFile,c.source_line sourceLine,
3482
+ c.event_name_expr eventName,${eventNameReason} reason
3483
+ FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
3484
+ WHERE c.call_type='async_emit' AND ${workspacePredicate("r")}
3485
+ AND ${eventNameReason} IS NOT NULL
3486
+ ORDER BY r.name COLLATE BINARY,c.source_file COLLATE BINARY,
3487
+ c.source_line,c.id LIMIT 5`).all(
3488
+ workspaceId,
3489
+ workspaceId
3490
+ );
3491
+ }
3492
+ function eventNameResolutionQuality(db, workspaceId) {
3493
+ const aggregate = db.prepare(`SELECT COUNT(*) total,
3494
+ SUM(CASE WHEN reason IS NOT NULL THEN 1 ELSE 0 END) unresolved
3495
+ FROM (SELECT ${eventNameReason} reason
3496
+ FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
3497
+ WHERE c.call_type='async_emit' AND ${workspacePredicate("r")})`).get(
3498
+ workspaceId,
3499
+ workspaceId
3500
+ );
3501
+ const reasons = db.prepare(`SELECT ${eventNameReason} reason,COUNT(*) count
3502
+ FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
3503
+ WHERE c.call_type='async_emit' AND ${workspacePredicate("r")}
3504
+ AND ${eventNameReason} IS NOT NULL
3505
+ GROUP BY reason ORDER BY count DESC,reason COLLATE BINARY LIMIT 16`).all(
3506
+ workspaceId,
3507
+ workspaceId
3508
+ );
3509
+ const unresolved = count2(aggregate?.unresolved);
3510
+ return {
3511
+ severity: unresolved > 0 ? "warning" : "info",
3512
+ code: "strict_event_name_resolution_quality",
3513
+ message: "Event publication name-resolution aggregate",
3514
+ publicationTotal: count2(aggregate?.total),
3515
+ unresolvedPublicationCount: unresolved,
3516
+ reasonBuckets: reasons,
3517
+ examples: unresolvedEventNameExamples(db, workspaceId),
3518
+ exampleCount: unresolved
3519
+ };
3520
+ }
3521
+ function dynamicEventExamples(db, workspaceId) {
3522
+ return db.prepare(`SELECT r.name repositoryName,c.call_type callType,
3523
+ c.source_file sourceFile,c.source_line sourceLine,
3524
+ e.to_kind targetKind,e.to_id targetId,e.unresolved_reason reason
3525
+ FROM graph_edges e JOIN outbound_calls c
3526
+ ON e.from_kind='call' AND c.id=CAST(e.from_id AS INTEGER)
3527
+ JOIN repositories r ON r.id=c.repo_id
3528
+ WHERE c.call_type IN ('async_emit','async_subscribe')
3529
+ AND ${workspacePredicate("r")}
3530
+ AND (e.to_kind='event_candidate'
3531
+ OR e.edge_type='DYNAMIC_EDGE_CANDIDATE')
3532
+ ORDER BY r.name COLLATE BINARY,c.source_file COLLATE BINARY,
3533
+ c.source_line,c.id LIMIT 5`).all(
3534
+ workspaceId,
3535
+ workspaceId
3536
+ );
3537
+ }
3538
+ function dynamicEventQuality(db, workspaceId) {
3539
+ const row = db.prepare(`SELECT
3540
+ COUNT(DISTINCT CASE WHEN e.to_kind='event_candidate'
3541
+ THEN e.workspace_id || ':' || e.to_id END) eventCandidateNodeCount,
3542
+ COUNT(*) dynamicEventEdgeCount,
3543
+ SUM(CASE WHEN json_array_length(e.evidence_json,
3544
+ '$.eventTemplateResolution.placeholders')>0 THEN 1 ELSE 0 END)
3545
+ variableRecoverableCount
3546
+ FROM graph_edges e JOIN outbound_calls c
3547
+ ON e.from_kind='call' AND c.id=CAST(e.from_id AS INTEGER)
3548
+ JOIN repositories r ON r.id=c.repo_id
3549
+ WHERE c.call_type IN ('async_emit','async_subscribe')
3550
+ AND ${workspacePredicate("r")}
3551
+ AND (e.to_kind='event_candidate'
3552
+ OR e.edge_type='DYNAMIC_EDGE_CANDIDATE')`).get(
3553
+ workspaceId,
3554
+ workspaceId
3555
+ );
3556
+ const total = count2(row?.dynamicEventEdgeCount);
3557
+ const recoverable = count2(row?.variableRecoverableCount);
3558
+ return {
3559
+ severity: total > 0 ? "warning" : "info",
3560
+ code: "strict_event_dynamic_candidate_quality",
3561
+ message: "Dynamic event candidate aggregate",
3562
+ eventCandidateNodeCount: count2(row?.eventCandidateNodeCount),
3563
+ dynamicEventEdgeCount: total,
3564
+ eventCandidateCount: total,
3565
+ variableRecoverableCount: recoverable,
3566
+ nonVariableRecoverableCount: Math.max(0, total - recoverable),
3567
+ examples: dynamicEventExamples(db, workspaceId),
3568
+ exampleCount: total
3569
+ };
3570
+ }
3571
+ function unmatchedPublicationRows(db, workspaceId) {
3572
+ return db.prepare(`SELECT r.name repositoryName,c.source_file sourceFile,
3573
+ c.source_line sourceLine,e.to_id eventName
3574
+ FROM graph_edges e JOIN outbound_calls c
3575
+ ON e.from_kind='call' AND c.id=CAST(e.from_id AS INTEGER)
3576
+ JOIN repositories r ON r.id=c.repo_id
3577
+ WHERE c.call_type='async_emit' AND e.edge_type='HANDLER_EMITS_EVENT'
3578
+ AND e.to_kind='event' AND ${workspacePredicate("r")}
3579
+ AND NOT EXISTS (SELECT 1 FROM graph_edges subscription
3580
+ WHERE subscription.workspace_id=e.workspace_id
3581
+ AND subscription.generation=e.generation
3582
+ AND subscription.edge_type='EVENT_SUBSCRIPTION_HANDLED_BY'
3583
+ AND subscription.from_kind='event'
3584
+ AND subscription.from_id=e.to_id)
3585
+ ORDER BY r.name COLLATE BINARY,c.source_file COLLATE BINARY,
3586
+ c.source_line,c.id LIMIT 5`).all(
3587
+ workspaceId,
3588
+ workspaceId
3589
+ );
3590
+ }
3591
+ function unmatchedPublicationQuality(db, workspaceId) {
3592
+ const row = db.prepare(`SELECT COUNT(*) count
3593
+ FROM graph_edges e JOIN outbound_calls c
3594
+ ON e.from_kind='call' AND c.id=CAST(e.from_id AS INTEGER)
3595
+ JOIN repositories r ON r.id=c.repo_id
3596
+ WHERE c.call_type='async_emit' AND e.edge_type='HANDLER_EMITS_EVENT'
3597
+ AND e.to_kind='event' AND ${workspacePredicate("r")}
3598
+ AND NOT EXISTS (SELECT 1 FROM graph_edges subscription
3599
+ WHERE subscription.workspace_id=e.workspace_id
3600
+ AND subscription.generation=e.generation
3601
+ AND subscription.edge_type='EVENT_SUBSCRIPTION_HANDLED_BY'
3602
+ AND subscription.from_kind='event'
3603
+ AND subscription.from_id=e.to_id)`).get(workspaceId, workspaceId);
3604
+ const total = count2(row?.count);
3605
+ return {
3606
+ severity: total > 0 ? "warning" : "info",
3607
+ code: "strict_event_publication_without_subscription_quality",
3608
+ message: "Resolved event publications without a matching subscription",
3609
+ unmatchedPublicationCount: total,
3610
+ examples: unmatchedPublicationRows(db, workspaceId),
3611
+ exampleCount: total
3612
+ };
3613
+ }
3614
+ function unmatchedSubscriptionRows(db, workspaceId) {
3615
+ return db.prepare(`SELECT COALESCE(json_extract(e.evidence_json,
3616
+ '$.subscriptionConsumerRepositoryName'),
3617
+ json_extract(e.evidence_json,'$.repositoryName')) repositoryName,
3618
+ json_extract(e.evidence_json,'$.sourceFile') sourceFile,
3619
+ json_extract(e.evidence_json,'$.sourceLine') sourceLine,
3620
+ e.from_id eventName,
3621
+ json_extract(c.evidence_json,'$.subscriptionLoopRegistrationStatus')
3622
+ loopRegistrationStatus,
3623
+ json_extract(c.evidence_json,'$.subscriptionLoopRegistrationCount')
3624
+ loopRegistrationCount
3625
+ FROM graph_edges e LEFT JOIN outbound_calls c
3626
+ ON c.id=CAST(json_extract(e.evidence_json,'$.subscribeCallId') AS INTEGER)
3627
+ WHERE e.edge_type='EVENT_SUBSCRIPTION_HANDLED_BY'
3628
+ AND ${workspacePredicate("e")}
3629
+ AND NOT EXISTS (SELECT 1 FROM graph_edges publication
3630
+ WHERE publication.workspace_id=e.workspace_id
3631
+ AND publication.generation=e.generation
3632
+ AND publication.edge_type='HANDLER_EMITS_EVENT'
3633
+ AND publication.to_kind='event'
3634
+ AND publication.to_id=e.from_id)
3635
+ ORDER BY repositoryName COLLATE BINARY,sourceFile COLLATE BINARY,
3636
+ sourceLine,e.id LIMIT 5`).all(
3637
+ workspaceId,
3638
+ workspaceId
3639
+ );
3640
+ }
3641
+ function unmatchedSubscriptionQuality(db, workspaceId) {
3642
+ const row = db.prepare(`SELECT COUNT(*) siteCount,
3643
+ SUM(CASE
3644
+ WHEN json_extract(c.evidence_json,
3645
+ '$.subscriptionLoopRegistrationStatus')='enumerated'
3646
+ THEN CAST(json_extract(c.evidence_json,
3647
+ '$.subscriptionLoopRegistrationCount') AS INTEGER)
3648
+ WHEN json_extract(c.evidence_json,
3649
+ '$.subscriptionLoopRegistrationStatus')='unresolved' THEN 0
3650
+ ELSE 1 END) count,
3651
+ SUM(CASE WHEN json_extract(c.evidence_json,
3652
+ '$.subscriptionLoopRegistrationStatus')='unresolved'
3653
+ THEN 1 ELSE 0 END) unknownMultiplicitySiteCount
3654
+ FROM graph_edges e LEFT JOIN outbound_calls c
3655
+ ON c.id=CAST(json_extract(e.evidence_json,'$.subscribeCallId') AS INTEGER)
3656
+ WHERE e.edge_type='EVENT_SUBSCRIPTION_HANDLED_BY'
3657
+ AND ${workspacePredicate("e")}
3658
+ AND NOT EXISTS (SELECT 1 FROM graph_edges publication
3659
+ WHERE publication.workspace_id=e.workspace_id
3660
+ AND publication.generation=e.generation
3661
+ AND publication.edge_type='HANDLER_EMITS_EVENT'
3662
+ AND publication.to_kind='event'
3663
+ AND publication.to_id=e.from_id)`).get(workspaceId, workspaceId);
3664
+ const total = count2(row?.count);
3665
+ const siteCount = count2(row?.siteCount);
3666
+ return {
3667
+ severity: siteCount > 0 ? "warning" : "info",
3668
+ code: "strict_event_subscription_without_publication_quality",
3669
+ message: "Event subscriptions without a matching publication",
3670
+ unmatchedSubscriptionCount: total,
3671
+ unmatchedSubscriptionSiteCount: siteCount,
3672
+ unknownMultiplicitySiteCount: count2(row?.unknownMultiplicitySiteCount),
3673
+ examples: unmatchedSubscriptionRows(db, workspaceId),
3674
+ exampleCount: siteCount
3675
+ };
3676
+ }
3677
+ function receiverProofQuality(db, workspaceId) {
3678
+ const row = db.prepare(`SELECT COUNT(*) eventTotal,
3679
+ SUM(CASE WHEN json_extract(c.evidence_json,
3680
+ '$.receiverClassification')='cap_evidence' THEN 1 ELSE 0 END) proven,
3681
+ SUM(CASE WHEN json_extract(c.evidence_json,
3682
+ '$.receiverClassification')='name_fallback' THEN 1 ELSE 0 END)
3683
+ nameFallback,
3684
+ SUM(CASE WHEN json_extract(c.evidence_json,
3685
+ '$.receiverClassification')='unproven' THEN 1 ELSE 0 END) unproven
3686
+ FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
3687
+ WHERE c.call_type IN ('async_emit','async_subscribe')
3688
+ AND ${workspacePredicate("r")}`).get(workspaceId, workspaceId);
3689
+ const buckets = db.prepare(`SELECT CASE
3690
+ WHEN json_extract(c.evidence_json,
3691
+ '$.receiverClassification')='name_fallback' THEN 'name_fallback'
3692
+ WHEN json_extract(c.evidence_json,
3693
+ '$.receiverClassification')='unproven'
3694
+ THEN COALESCE(c.unresolved_reason,'unproven')
3695
+ ELSE 'missing' END reason,COUNT(*) count
3696
+ FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
3697
+ WHERE c.call_type IN ('async_emit','async_subscribe')
3698
+ AND ${workspacePredicate("r")}
3699
+ AND json_extract(c.evidence_json,'$.receiverClassification')
3700
+ <>'cap_evidence'
3701
+ GROUP BY reason ORDER BY count DESC,reason COLLATE BINARY LIMIT 16`).all(
3702
+ workspaceId,
3703
+ workspaceId
3704
+ );
3705
+ const questionable = count2(row?.nameFallback) + count2(row?.unproven);
3706
+ return {
3707
+ severity: questionable > 0 ? "warning" : "info",
3708
+ code: "strict_event_receiver_classification_quality",
3709
+ message: "CAP event receiver proof aggregate",
3710
+ eventTotal: count2(row?.eventTotal),
3711
+ proven: count2(row?.proven),
3712
+ nameFallback: count2(row?.nameFallback),
3713
+ unproven: count2(row?.unproven),
3714
+ questionable,
3715
+ reasonBuckets: buckets,
3716
+ examples: receiverProofExamples(db, workspaceId),
3717
+ exampleCount: questionable
3718
+ };
3719
+ }
3720
+ function receiverProofExamples(db, workspaceId) {
3721
+ return db.prepare(`SELECT r.name repositoryName,c.call_type callType,
3722
+ c.source_file sourceFile,c.source_line sourceLine,
3723
+ json_extract(c.evidence_json,'$.receiverClassification')
3724
+ receiverClassification,
3725
+ c.unresolved_reason reason
3726
+ FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
3727
+ WHERE c.call_type IN ('async_emit','async_subscribe')
3728
+ AND ${workspacePredicate("r")}
3729
+ AND json_extract(c.evidence_json,'$.receiverClassification')
3730
+ <>'cap_evidence'
3731
+ ORDER BY r.name COLLATE BINARY,c.source_file COLLATE BINARY,
3732
+ c.source_line,c.id LIMIT 5`).all(
3733
+ workspaceId,
3734
+ workspaceId
3735
+ );
3736
+ }
3737
+ function shapeEnvironmentExamples(db, workspaceId) {
3738
+ return db.prepare(`SELECT e.edge_type edgeType,e.from_id fromId,
3739
+ e.to_kind toKind,e.to_id toId,
3740
+ json_extract(e.evidence_json,
3741
+ '$.eventEnvironmentResolution.status') environmentStatus,
3742
+ json_extract(e.evidence_json,
3743
+ '$.eventEnvironmentResolution.collisionCount') collisionCount
3744
+ FROM graph_edges e WHERE ${workspacePredicate("e")}
3745
+ AND (e.edge_type='EVENT_SHAPE_CANDIDATE_SUBSCRIBER'
3746
+ OR json_extract(e.evidence_json,
3747
+ '$.eventEnvironmentResolution.status')='ambiguous'
3748
+ OR CAST(json_extract(e.evidence_json,
3749
+ '$.eventEnvironmentResolution.collisionCount') AS INTEGER)>1)
3750
+ ORDER BY e.edge_type COLLATE BINARY,e.from_id COLLATE BINARY,
3751
+ e.to_id COLLATE BINARY,e.id LIMIT 5`).all(
3752
+ workspaceId,
3753
+ workspaceId
3754
+ );
3755
+ }
3756
+ function shapeEnvironmentQuality(db, workspaceId) {
3757
+ const row = db.prepare(`SELECT
3758
+ SUM(CASE WHEN e.edge_type='EVENT_SHAPE_CANDIDATE_SUBSCRIBER'
3759
+ THEN 1 ELSE 0 END) shapeCandidates,
3760
+ SUM(CASE WHEN json_extract(e.evidence_json,
3761
+ '$.eventEnvironmentResolution.status')='ambiguous'
3762
+ OR CAST(json_extract(e.evidence_json,
3763
+ '$.eventEnvironmentResolution.collisionCount') AS INTEGER)>1
3764
+ THEN 1 ELSE 0 END) environmentAmbiguities
3765
+ FROM graph_edges e WHERE ${workspacePredicate("e")}`).get(
3766
+ workspaceId,
3767
+ workspaceId
3768
+ );
3769
+ const shapes = count2(row?.shapeCandidates);
3770
+ const ambiguous = count2(row?.environmentAmbiguities);
3771
+ return {
3772
+ severity: shapes + ambiguous > 0 ? "warning" : "info",
3773
+ code: "strict_event_shape_environment_quality",
3774
+ message: "Event skeleton candidate and environment ambiguity aggregate",
3775
+ skeletonCandidateCount: shapes,
3776
+ environmentBindingAmbiguityCount: ambiguous,
3777
+ examples: shapeEnvironmentExamples(db, workspaceId),
3778
+ exampleCount: shapes + ambiguous
3779
+ };
3780
+ }
3781
+ function eventSurfaceQualityDiagnostics(db, workspaceId) {
3782
+ return [
3783
+ eventNameResolutionQuality(db, workspaceId),
3784
+ dynamicEventQuality(db, workspaceId),
3785
+ unmatchedPublicationQuality(db, workspaceId),
3786
+ unmatchedSubscriptionQuality(db, workspaceId),
3787
+ receiverProofQuality(db, workspaceId),
3788
+ shapeEnvironmentQuality(db, workspaceId)
3789
+ ];
3790
+ }
3791
+
3252
3792
  // src/cli/doctor.ts
3253
3793
  function doctorDiagnostics(db, strict, options = {}) {
3254
3794
  const lifecycle = factLifecycleDiagnostic(db, options.workspaceId);
@@ -3387,7 +3927,7 @@ function parserQualityDiagnostics(db, strict, options) {
3387
3927
  externalHttpTargetQuality(db),
3388
3928
  odataInvocationResolutionQuality(db),
3389
3929
  ...jsonEvidenceQuality(db),
3390
- eventReceiverQuality(db),
3930
+ ...eventSurfaceQualityDiagnostics(db, options.workspaceId),
3391
3931
  graphDynamicFlagQuality(db),
3392
3932
  symbol,
3393
3933
  dbq,
@@ -3422,10 +3962,6 @@ function outboundOwnershipQuality(db) {
3422
3962
  const ratio = total === 0 ? 0 : Number((without / total).toFixed(4));
3423
3963
  return { severity: ratio > 0.01 ? "warning" : "info", code: "strict_outbound_source_ownership_quality", message: "Outbound call source-symbol ownership aggregate", total, withoutOwnership: without, withoutOwnershipRatio: ratio, withoutOwnershipRatioThreshold: 0.01, ownerlessByType: byType, ownerlessExamples: examples };
3424
3964
  }
3425
- function eventReceiverQuality(db) {
3426
- const row = db.prepare("SELECT SUM(CASE WHEN call_type IN ('async_emit','async_subscribe') THEN 1 ELSE 0 END) eventTotal, SUM(CASE WHEN call_type IN ('async_emit','async_subscribe') AND (json_extract(evidence_json,'$.receiverClassification') IS NULL OR json_extract(evidence_json,'$.receiverClassification') <> 'cap_evidence') THEN 1 ELSE 0 END) questionable FROM outbound_calls").get();
3427
- return { severity: Number(row.questionable ?? 0) > 0 ? "warning" : "info", code: "strict_event_receiver_classification_quality", message: "CAP event receiver classification aggregate", eventTotal: Number(row.eventTotal ?? 0), questionable: Number(row.questionable ?? 0) };
3428
- }
3429
3965
  function graphDynamicFlagQuality(db) {
3430
3966
  const row = db.prepare("SELECT COUNT(*) count FROM graph_edges WHERE status='terminal' AND is_dynamic=1").get();
3431
3967
  return { severity: Number(row.count ?? 0) > 0 ? "warning" : "info", code: "strict_graph_dynamic_flag_consistency", message: "Graph dynamic flag consistency aggregate", dynamicTerminalEdges: Number(row.count ?? 0) };
@@ -3861,7 +4397,7 @@ function location(evidence) {
3861
4397
  function renderTraceTable(result) {
3862
4398
  const lines = ["Step Type From To Evidence"];
3863
4399
  for (const e of result.edges) {
3864
- lines.push(`${String(e.step).padEnd(5)} ${e.type.padEnd(20)} ${e.from.slice(0, 34).padEnd(35)} ${e.to.slice(0, 35).padEnd(36)} ${location(e.evidence)}`);
4400
+ lines.push(`${String(e.step).padEnd(5)} ${e.type.padEnd(20)} ${e.from.slice(0, 34).padEnd(35)} ${e.to.slice(0, 35).padEnd(36)} ${evidenceSummary(e.evidence)}`);
3865
4401
  if (e.unresolvedReason)
3866
4402
  lines.push(...hintLines(e.evidence).map((hint) => ` ${hint}`));
3867
4403
  }
@@ -3869,6 +4405,13 @@ function renderTraceTable(result) {
3869
4405
  return `${lines.join("\n")}
3870
4406
  `;
3871
4407
  }
4408
+ function evidenceSummary(evidence) {
4409
+ const labels = [
4410
+ typeof evidence.dispatchScope === "string" ? `scope=${evidence.dispatchScope}` : void 0,
4411
+ typeof evidence.dispatchCertainty === "string" ? `certainty=${evidence.dispatchCertainty}` : void 0
4412
+ ].filter((value) => Boolean(value));
4413
+ return labels.length > 0 ? `${location(evidence)} [${labels.join(",")}]` : location(evidence);
4414
+ }
3872
4415
  function diagnosticLines(diagnostic) {
3873
4416
  const first = `${String(diagnostic.severity ?? "info")} ${String(diagnostic.code ?? "diagnostic")} ${String(diagnostic.message ?? "")}`;
3874
4417
  const details = diagnosticDetailLines(diagnostic);
@@ -3906,8 +4449,8 @@ function hintLines(evidence) {
3906
4449
  }
3907
4450
  function dynamicHintLines(evidence) {
3908
4451
  const exploration = isRecord(evidence.dynamicTargetExploration) ? evidence.dynamicTargetExploration : evidence;
3909
- const count2 = numberValue(exploration.candidateCount);
3910
- if (count2 === 0) return [];
4452
+ const count3 = numberValue(exploration.candidateCount);
4453
+ if (count3 === 0) return [];
3911
4454
  const shown = numberValue(exploration.shownCandidateCount);
3912
4455
  const omitted = numberValue(exploration.omittedCandidateCount);
3913
4456
  const rejected = numberValue(exploration.rejectedCandidateCount);
@@ -3915,7 +4458,7 @@ function dynamicHintLines(evidence) {
3915
4458
  `viable candidates: ${shown} shown, ${omitted} omitted; rejected: ${rejected}`
3916
4459
  ];
3917
4460
  lines.push(...varSetHints(exploration.suggestedVarSets));
3918
- if (omitted > 0 || rejected > 0 || shown < count2)
4461
+ if (omitted > 0 || rejected > 0 || shown < count3)
3919
4462
  lines.push("use --dynamic-mode candidates --max-dynamic-candidates 20 to explore candidate branches");
3920
4463
  return lines;
3921
4464
  }
@@ -3985,9 +4528,9 @@ function diagnosticLocation(diagnostic) {
3985
4528
  }
3986
4529
  function compactMessage(diagnostic) {
3987
4530
  const message = String(diagnostic.message ?? "");
3988
- const count2 = typeof diagnostic.count === "number" ? ` count=${diagnostic.count}` : "";
4531
+ const count3 = typeof diagnostic.count === "number" ? ` count=${diagnostic.count}` : "";
3989
4532
  const total = typeof diagnostic.total === "number" ? ` total=${diagnostic.total}` : "";
3990
- return `${message}${count2}${total}`.trim();
4533
+ return `${message}${count3}${total}`.trim();
3991
4534
  }
3992
4535
  function suggestedHintLines(diagnostic) {
3993
4536
  const direct = cliHints(diagnostic.suggestedHints);
@@ -4348,7 +4891,7 @@ function registerLinkCommand(program) {
4348
4891
  const upgradeWarnings = linkUpgradeWarnings(db, workspaceId);
4349
4892
  writeStdout(
4350
4893
  `${upgradeWarnings.length ? `Warnings: ${upgradeWarnings.map((item) => String(item.code)).join(", ")}. Run service-flow doctor --strict for remediation.
4351
- ` : ""}Linked ${r.edgeCount} edges: ${r.remoteResolvedCount} remote operation calls resolved, ${r.localResolvedCount} local operation calls resolved, ${r.unresolvedCount} unresolved operation calls, ${r.ambiguousCount} ambiguous operation calls, ${r.dynamicCount} dynamic operation calls, ${r.terminalCount} terminal call edges, ${r.dependencyResolvedCount} dependency resolved, ${r.dependencyAmbiguousCount} dependency ambiguous, ${r.implementationResolvedCount} implementation resolved, ${r.implementationAmbiguousCount} implementation ambiguous, ${r.implementationUnresolvedCount} implementation unresolved, ${r.subscriptionHandlerResolvedCount} subscription handlers resolved, ${r.subscriptionHandlerAmbiguousCount} subscription handlers ambiguous, ${r.subscriptionHandlerUnresolvedCount} subscription handlers unresolved, ${r.subscriptionHandlerMissingAssociationCount} subscription handler associations missing
4894
+ ` : ""}Linked ${r.edgeCount} edges: ${r.remoteResolvedCount} remote operation calls resolved, ${r.localResolvedCount} local operation calls resolved, ${r.unresolvedCount} unresolved operation calls, ${r.ambiguousCount} ambiguous operation calls, ${r.dynamicCount} dynamic operation calls, ${r.terminalCount} terminal call edges, ${r.dependencyResolvedCount} dependency resolved, ${r.dependencyAmbiguousCount} dependency ambiguous, ${r.implementationResolvedCount} implementation resolved, ${r.implementationAmbiguousCount} implementation ambiguous, ${r.implementationUnresolvedCount} implementation unresolved, ${r.subscriptionHandlerResolvedCount} subscription handlers resolved, ${r.subscriptionHandlerAmbiguousCount} subscription handlers ambiguous, ${r.subscriptionHandlerUnresolvedCount} subscription handlers unresolved, ${r.subscriptionHandlerMissingAssociationCount} subscription handler associations missing, ${r.eventShapeCandidateCount} event shape candidates
4352
4895
  `
4353
4896
  );
4354
4897
  }).catch(fail)