@launchsecure/launch-kit 0.0.33 → 0.0.35

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.
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/server/launch-radar-entry.ts
27
+ var import_node_child_process = require("node:child_process");
28
+ var import_node_path = __toESM(require("node:path"));
29
+ var sequencerEntry = import_node_path.default.join(__dirname, "cli.js");
30
+ var child = (0, import_node_child_process.spawn)(process.execPath, [sequencerEntry, "radar", ...process.argv.slice(2)], {
31
+ stdio: "inherit"
32
+ });
33
+ var forward = (sig) => () => {
34
+ try {
35
+ child.kill(sig);
36
+ } catch {
37
+ }
38
+ };
39
+ process.on("SIGTERM", forward("SIGTERM"));
40
+ process.on("SIGINT", forward("SIGINT"));
41
+ process.on("SIGHUP", forward("SIGHUP"));
42
+ child.on("exit", (code, signal) => {
43
+ if (signal) process.kill(process.pid, signal);
44
+ else process.exit(code ?? 0);
45
+ });
@@ -1530,6 +1530,73 @@ var init_manifest = __esm({
1530
1530
  }
1531
1531
  });
1532
1532
 
1533
+ // src/server/orbit/active.ts
1534
+ function anchorPaths(projectRoot) {
1535
+ const dir = (0, import_node_path7.join)(projectRoot, LAUNCHSECURE_DIR, "orbit");
1536
+ return { dir, current: (0, import_node_path7.join)(dir, "current"), activeJson: (0, import_node_path7.join)(dir, "active.json") };
1537
+ }
1538
+ function present(p) {
1539
+ try {
1540
+ (0, import_node_fs10.lstatSync)(p);
1541
+ return true;
1542
+ } catch {
1543
+ return false;
1544
+ }
1545
+ }
1546
+ function setActiveOrbit(entry, envFileName) {
1547
+ const paths = anchorPaths(entry.projectRoot);
1548
+ (0, import_node_fs10.mkdirSync)(paths.dir, { recursive: true });
1549
+ const tmpLink = `${paths.current}.tmp.${process.pid}`;
1550
+ if (present(tmpLink)) (0, import_node_fs10.rmSync)(tmpLink, { force: true });
1551
+ (0, import_node_fs10.symlinkSync)(entry.path, tmpLink);
1552
+ (0, import_node_fs10.renameSync)(tmpLink, paths.current);
1553
+ const active = {
1554
+ slug: entry.slug,
1555
+ branch: entry.branch,
1556
+ path: entry.path,
1557
+ envFile: (0, import_node_path7.join)(entry.path, envFileName),
1558
+ projectRoot: entry.projectRoot,
1559
+ anchor: paths.current,
1560
+ switchedAt: (/* @__PURE__ */ new Date()).toISOString()
1561
+ };
1562
+ const tmpJson = `${paths.activeJson}.tmp.${process.pid}`;
1563
+ (0, import_node_fs10.writeFileSync)(tmpJson, JSON.stringify(active, null, 2) + "\n", "utf-8");
1564
+ (0, import_node_fs10.renameSync)(tmpJson, paths.activeJson);
1565
+ return active;
1566
+ }
1567
+ function clearActiveOrbit(projectRoot) {
1568
+ const paths = anchorPaths(projectRoot);
1569
+ let cleared = false;
1570
+ for (const p of [paths.current, paths.activeJson]) {
1571
+ if (present(p)) {
1572
+ try {
1573
+ (0, import_node_fs10.rmSync)(p, { force: true });
1574
+ cleared = true;
1575
+ } catch {
1576
+ }
1577
+ }
1578
+ }
1579
+ return cleared;
1580
+ }
1581
+ function readActiveOrbit(projectRoot) {
1582
+ const { activeJson } = anchorPaths(projectRoot);
1583
+ if (!present(activeJson)) return null;
1584
+ try {
1585
+ return JSON.parse((0, import_node_fs10.readFileSync)(activeJson, "utf-8"));
1586
+ } catch {
1587
+ return null;
1588
+ }
1589
+ }
1590
+ var import_node_fs10, import_node_path7;
1591
+ var init_active = __esm({
1592
+ "src/server/orbit/active.ts"() {
1593
+ "use strict";
1594
+ import_node_fs10 = require("node:fs");
1595
+ import_node_path7 = require("node:path");
1596
+ init_launch_kit_paths();
1597
+ }
1598
+ });
1599
+
1533
1600
  // src/server/orbit/slug.ts
1534
1601
  function slugify(branch) {
1535
1602
  let s = branch.toLowerCase();
@@ -1556,7 +1623,7 @@ async function create(opts) {
1556
1623
  const envFileName = opts.envFileName ?? manifest.envFile ?? DEFAULT_ENV_FILE;
1557
1624
  const ctx = {
1558
1625
  projectRoot: opts.projectRoot,
1559
- manifestPath: (0, import_node_path7.join)(opts.projectRoot, "orbit.json"),
1626
+ manifestPath: (0, import_node_path8.join)(opts.projectRoot, "orbit.json"),
1560
1627
  manifest,
1561
1628
  logger: opts.logger,
1562
1629
  envFileName
@@ -1610,13 +1677,13 @@ async function create(opts) {
1610
1677
  Object.assign(rewriteFns, adapter.envRewrites(state, ctx));
1611
1678
  }
1612
1679
  }
1613
- const srcEnv = (0, import_node_path7.join)(opts.projectRoot, envFileName);
1614
- const dstEnv = (0, import_node_path7.join)(worktreeState.path, envFileName);
1615
- if ((0, import_node_fs10.existsSync)(srcEnv) && !(0, import_node_fs10.existsSync)(dstEnv)) {
1680
+ const srcEnv = (0, import_node_path8.join)(opts.projectRoot, envFileName);
1681
+ const dstEnv = (0, import_node_path8.join)(worktreeState.path, envFileName);
1682
+ if ((0, import_node_fs11.existsSync)(srcEnv) && !(0, import_node_fs11.existsSync)(dstEnv)) {
1616
1683
  opts.logger.step(`copy ${envFileName} into worktree`);
1617
1684
  copyEnvFile(srcEnv, dstEnv);
1618
1685
  }
1619
- if ((0, import_node_fs10.existsSync)(dstEnv) && Object.keys(rewriteFns).length > 0) {
1686
+ if ((0, import_node_fs11.existsSync)(dstEnv) && Object.keys(rewriteFns).length > 0) {
1620
1687
  opts.logger.step(`rewrite ${envFileName} (${Object.keys(rewriteFns).join(", ")})`);
1621
1688
  const r = rewriteEnvFile(dstEnv, rewriteFns);
1622
1689
  if (r.missing.length > 0) {
@@ -1636,7 +1703,7 @@ async function create(opts) {
1636
1703
  opts.logger.ok(`registered ${slug}`);
1637
1704
  try {
1638
1705
  opts.logger.step(`generate chart graph for worktree`);
1639
- const chartEntry = (0, import_node_path7.join)(__dirname, "graph-mcp-entry.js");
1706
+ const chartEntry = (0, import_node_path8.join)(__dirname, "graph-mcp-entry.js");
1640
1707
  const result = (0, import_node_child_process8.spawnSync)(
1641
1708
  process.execPath,
1642
1709
  [chartEntry, "generate"],
@@ -1688,7 +1755,7 @@ async function drop(opts) {
1688
1755
  const manifest = loadManifestOrThrow(opts.projectRoot);
1689
1756
  const ctx = {
1690
1757
  projectRoot: opts.projectRoot,
1691
- manifestPath: (0, import_node_path7.join)(opts.projectRoot, "orbit.json"),
1758
+ manifestPath: (0, import_node_path8.join)(opts.projectRoot, "orbit.json"),
1692
1759
  manifest,
1693
1760
  logger: opts.logger,
1694
1761
  envFileName: manifest.envFile ?? DEFAULT_ENV_FILE
@@ -1709,7 +1776,7 @@ async function drop(opts) {
1709
1776
  if (result.backupPath) backups[ref.name] = result.backupPath;
1710
1777
  freed[ref.name] = state.state;
1711
1778
  }
1712
- if ((0, import_node_fs10.existsSync)(entry.path)) {
1779
+ if ((0, import_node_fs11.existsSync)(entry.path)) {
1713
1780
  const blockers = findBlockingPids(entry.path);
1714
1781
  if (blockers.length > 0) {
1715
1782
  opts.logger.warn(`${blockers.length} process(es) holding files in worktree \u2014 terminating:`);
@@ -1728,15 +1795,19 @@ async function drop(opts) {
1728
1795
  } catch (e) {
1729
1796
  opts.logger.warn(`worktree remove failed (continuing anyway): ${e.message}`);
1730
1797
  }
1731
- if ((0, import_node_fs10.existsSync)(entry.path)) {
1798
+ if ((0, import_node_fs11.existsSync)(entry.path)) {
1732
1799
  opts.logger.step(`remove leftover dir at ${entry.path}`);
1733
1800
  try {
1734
- (0, import_node_fs10.rmSync)(entry.path, { recursive: true, force: true });
1801
+ (0, import_node_fs11.rmSync)(entry.path, { recursive: true, force: true });
1735
1802
  } catch (e) {
1736
1803
  opts.logger.warn(`leftover dir cleanup failed: ${e.message}`);
1737
1804
  }
1738
1805
  }
1739
1806
  await deregisterWorktree(slug);
1807
+ if (readActiveOrbit(opts.projectRoot)?.slug === slug) {
1808
+ clearActiveOrbit(opts.projectRoot);
1809
+ opts.logger.step("cleared active-orbit anchor (current)");
1810
+ }
1740
1811
  opts.logger.ok(`dropped ${slug}`);
1741
1812
  return { slug, backups, freed };
1742
1813
  }
@@ -1753,12 +1824,19 @@ function switchTo(opts) {
1753
1824
  const slug = slugify(opts.branch);
1754
1825
  const entry = lookupWorktree(slug);
1755
1826
  if (!entry) throw new Error(`no orbit registered for branch "${opts.branch}" (slug ${slug})`);
1827
+ const active = setActiveOrbit(entry, DEFAULT_ENV_FILE);
1756
1828
  return {
1757
1829
  path: entry.path,
1758
- envFile: (0, import_node_path7.join)(entry.path, DEFAULT_ENV_FILE),
1830
+ envFile: active.envFile,
1831
+ anchor: active.anchor,
1832
+ activeJson: anchorPaths(entry.projectRoot).activeJson,
1833
+ nextAction: `cd "${active.anchor}"`,
1759
1834
  entry
1760
1835
  };
1761
1836
  }
1837
+ function deactivate(opts) {
1838
+ return { cleared: clearActiveOrbit(opts.projectRoot), projectRoot: opts.projectRoot };
1839
+ }
1762
1840
  function doctor() {
1763
1841
  const issues = [];
1764
1842
  const trackedByRoot = /* @__PURE__ */ new Map();
@@ -1781,7 +1859,7 @@ function doctor() {
1781
1859
  return s;
1782
1860
  };
1783
1861
  for (const w of listWorktrees()) {
1784
- if (!(0, import_node_fs10.existsSync)(w.path)) {
1862
+ if (!(0, import_node_fs11.existsSync)(w.path)) {
1785
1863
  issues.push({ kind: "missing-worktree", slug: w.slug, detail: `path ${w.path} does not exist` });
1786
1864
  continue;
1787
1865
  }
@@ -2018,18 +2096,19 @@ function formatAge(iso) {
2018
2096
  const d = Math.floor(h / 24);
2019
2097
  return `${d}d`;
2020
2098
  }
2021
- var import_node_child_process8, import_node_fs10, import_node_path7, DEFAULT_ENV_FILE;
2099
+ var import_node_child_process8, import_node_fs11, import_node_path8, DEFAULT_ENV_FILE;
2022
2100
  var init_orchestrator = __esm({
2023
2101
  "src/server/orbit/orchestrator.ts"() {
2024
2102
  "use strict";
2025
2103
  import_node_child_process8 = require("node:child_process");
2026
- import_node_fs10 = require("node:fs");
2027
- import_node_path7 = require("node:path");
2104
+ import_node_fs11 = require("node:fs");
2105
+ import_node_path8 = require("node:path");
2028
2106
  init_adapter_registry();
2029
2107
  init_env_rewriter();
2030
2108
  init_gate_runner();
2031
2109
  init_manifest();
2032
2110
  init_registry();
2111
+ init_active();
2033
2112
  init_slug();
2034
2113
  DEFAULT_ENV_FILE = ".env.local";
2035
2114
  }
@@ -2093,11 +2172,16 @@ async function handleTool(name, args) {
2093
2172
  const result = switchTo({ branch, projectRoot });
2094
2173
  return text({
2095
2174
  slug: result.entry.slug,
2175
+ branch: result.entry.branch,
2176
+ anchor: result.anchor,
2096
2177
  path: result.path,
2097
2178
  envFile: result.envFile,
2098
- nextAction: `cd ${result.path}`
2179
+ nextAction: result.nextAction
2099
2180
  });
2100
2181
  }
2182
+ case "orbit.deactivate": {
2183
+ return text(deactivate({ projectRoot }));
2184
+ }
2101
2185
  case "orbit.drop": {
2102
2186
  const branch = String(args.branch ?? "");
2103
2187
  if (!branch) return text({ error: "branch is required" });
@@ -2256,15 +2340,20 @@ var init_orbit_mcp = __esm({
2256
2340
  },
2257
2341
  {
2258
2342
  name: "orbit.switch",
2259
- description: "Look up the path + env file of an existing orbit by branch name. Does NOT change any shell \u2014 the caller is expected to `cd` into the returned path. Useful for agents that need to operate inside an already-created orbit.",
2343
+ description: 'Activate an orbit by branch name. Atomically repoints the stable anchor <project>/.launchsecure/orbit/current at the orbit\'s worktree and records active.json, then returns `anchor` (the current/ path) + `nextAction`. AFTER calling this, operate on the anchor: Read/Edit/Write under `.launchsecure/orbit/current/<path>`, pass `project_root: ".launchsecure/orbit/current"` to chart, and run the returned `nextAction` (cd into current) so commands execute in the orbit. Switching to a different orbit just repoints the anchor; re-run nextAction afterwards.',
2260
2344
  inputSchema: {
2261
2345
  type: "object",
2262
2346
  properties: {
2263
- branch: { type: "string", description: "Branch name to look up." }
2347
+ branch: { type: "string", description: "Branch name of the orbit to activate." }
2264
2348
  },
2265
2349
  required: ["branch"]
2266
2350
  }
2267
2351
  },
2352
+ {
2353
+ name: "orbit.deactivate",
2354
+ description: "Clear the active-orbit anchor for this project \u2014 removes <project>/.launchsecure/orbit/current and active.json so no orbit is active. Use when you're done working in an orbit and want subsequent work to target the main checkout. No-op if nothing is active.",
2355
+ inputSchema: { type: "object", properties: {} }
2356
+ },
2268
2357
  {
2269
2358
  name: "orbit.drop",
2270
2359
  description: "Tear down an orbit: backup the database (default true), drop the database, terminate any process holding files inside the worktree (chart/beacon MCPs, dev servers \u2014 via lsof+SIGTERM), remove the worktree, sweep any leftover on-disk dir, deregister. Idempotent: safe to retry on a partial state (e.g. git already removed the worktree but the registry still claims it). Returns { slug, backups, freed }.",
@@ -2326,8 +2415,8 @@ var init_orbit_mcp = __esm({
2326
2415
  });
2327
2416
 
2328
2417
  // src/server/orbit-entry.ts
2329
- var import_node_fs11 = require("node:fs");
2330
- var import_node_path8 = require("node:path");
2418
+ var import_node_fs12 = require("node:fs");
2419
+ var import_node_path9 = require("node:path");
2331
2420
  init_orchestrator();
2332
2421
  init_logger();
2333
2422
  function parseFlags(argv) {
@@ -2413,14 +2502,22 @@ ${JSON.stringify(result, null, 2)}
2413
2502
  if (!branch) die("usage: launch-orbit switch <branch> [--emit-cd]");
2414
2503
  const result = switchTo({ branch, projectRoot });
2415
2504
  if (flags.emitCd) {
2416
- process.stdout.write(`cd ${result.path}
2505
+ process.stdout.write(`cd ${result.anchor}
2417
2506
  `);
2418
2507
  } else {
2419
- process.stdout.write(`${JSON.stringify({ path: result.path, envFile: result.envFile, nextAction: `cd ${result.path}` }, null, 2)}
2420
- `);
2508
+ process.stdout.write(
2509
+ `${JSON.stringify({ anchor: result.anchor, path: result.path, envFile: result.envFile, nextAction: result.nextAction }, null, 2)}
2510
+ `
2511
+ );
2421
2512
  }
2422
2513
  return;
2423
2514
  }
2515
+ case "deactivate": {
2516
+ const result = deactivate({ projectRoot });
2517
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
2518
+ `);
2519
+ return;
2520
+ }
2424
2521
  case "drop": {
2425
2522
  const branch = positional[0];
2426
2523
  if (!branch) die("usage: launch-orbit drop <branch> [--no-backup]");
@@ -2495,8 +2592,8 @@ ${JSON.stringify(result, null, 2)}
2495
2592
  }
2496
2593
  }
2497
2594
  function runInit(projectRoot) {
2498
- const path = (0, import_node_path8.join)(projectRoot, "orbit.json");
2499
- if ((0, import_node_fs11.existsSync)(path)) {
2595
+ const path = (0, import_node_path9.join)(projectRoot, "orbit.json");
2596
+ if ((0, import_node_fs12.existsSync)(path)) {
2500
2597
  process.stderr.write(`[launch-orbit] orbit.json already exists at ${path}
2501
2598
  `);
2502
2599
  process.exit(1);
@@ -2551,7 +2648,7 @@ function runInit(projectRoot) {
2551
2648
  full: { resources: ["db", "ports"] }
2552
2649
  }
2553
2650
  };
2554
- (0, import_node_fs11.writeFileSync)(path, JSON.stringify(starter, null, 2) + "\n", "utf-8");
2651
+ (0, import_node_fs12.writeFileSync)(path, JSON.stringify(starter, null, 2) + "\n", "utf-8");
2555
2652
  process.stdout.write(`\u2713 wrote ${path}
2556
2653
  `);
2557
2654
  }
@@ -346,6 +346,7 @@ function emptyParsedFile(absPath) {
346
346
  return {
347
347
  name: absPath.split("/").pop() ?? absPath,
348
348
  exports: [],
349
+ defines: [],
349
350
  imports: [],
350
351
  reExports: [],
351
352
  jsxElements: /* @__PURE__ */ new Set(),
@@ -458,6 +459,34 @@ function parseFileTS(absPath) {
458
459
  reExports.push({ name: "*", from: caps["reexport.wildcard.source"], isWildcard: true });
459
460
  }
460
461
  }
462
+ const definesSet = /* @__PURE__ */ new Set();
463
+ function recordCallable(decl) {
464
+ if (decl.type === "function_declaration" || decl.type === "generator_function_declaration") {
465
+ const id = childOfType(decl, "identifier");
466
+ if (id) definesSet.add(id.text);
467
+ return;
468
+ }
469
+ if (decl.type === "class_declaration") {
470
+ const id = childOfType(decl, "type_identifier") ?? childOfType(decl, "identifier");
471
+ if (id) definesSet.add(id.text);
472
+ return;
473
+ }
474
+ if (decl.type === "lexical_declaration" || decl.type === "variable_declaration") {
475
+ for (const d of childrenOfType(decl, "variable_declarator")) {
476
+ const id = childOfType(d, "identifier");
477
+ if (!id) continue;
478
+ const isCallable = !!childOfType(d, "arrow_function") || !!childOfType(d, "function_expression") || !!childOfType(d, "function") || !!childOfType(d, "generator_function");
479
+ if (isCallable) definesSet.add(id.text);
480
+ }
481
+ }
482
+ }
483
+ for (const child of root.children) {
484
+ if (child.type === "export_statement") {
485
+ for (const gc of child.children) recordCallable(gc);
486
+ } else {
487
+ recordCallable(child);
488
+ }
489
+ }
461
490
  const jsxElements = /* @__PURE__ */ new Set();
462
491
  const jsxQuery = getQuery("jsx-elements");
463
492
  const jsxCaptures = jsxQuery.captures(root);
@@ -546,7 +575,7 @@ function parseFileTS(absPath) {
546
575
  }
547
576
  }
548
577
  const name = defaultName ?? firstValueExport ?? firstTypeExport ?? "";
549
- return { name, exports: exportsOrdered, imports, reExports, jsxElements, navigations, fetchCalls };
578
+ return { name, exports: exportsOrdered, defines: [...definesSet], imports, reExports, jsxElements, navigations, fetchCalls };
550
579
  }
551
580
  function extractDbCallsTS(absPath) {
552
581
  const tree = parseSource(absPath);
@@ -2041,6 +2070,11 @@ function generate(rootDir) {
2041
2070
  const parsed = parsedByPath.get(absPath);
2042
2071
  const name = parsed.name || nameFromFilename(absPath);
2043
2072
  const layer = CLASSIFICATION_TO_LAYER[type] ?? "ui";
2073
+ const importedNames = [...new Set(
2074
+ parsed.imports.flatMap(
2075
+ (imp) => imp.isTypeOnly ? [] : imp.names.filter((n) => !imp.typeNames.has(n))
2076
+ )
2077
+ )];
2044
2078
  nodeIdSet.add(id);
2045
2079
  if (layer === "api") {
2046
2080
  const dbCalls = extractDbCallsTS(absPath);
@@ -2079,6 +2113,8 @@ function generate(rootDir) {
2079
2113
  responses: deep.responses,
2080
2114
  params: deep.params,
2081
2115
  ...deep.effects ? { effects: deep.effects } : {},
2116
+ ...parsed.defines.length > 0 ? { defines: parsed.defines } : {},
2117
+ ...importedNames.length > 0 ? { imported_names: importedNames } : {},
2082
2118
  ...deep.notes ? { notes: deep.notes } : {},
2083
2119
  _dbCalls: dbCalls
2084
2120
  // temp: used for cross-ref building below
@@ -2095,6 +2131,8 @@ function generate(rootDir) {
2095
2131
  layer: "ui",
2096
2132
  route,
2097
2133
  exports: parsed.exports,
2134
+ ...parsed.defines.length > 0 ? { defines: parsed.defines } : {},
2135
+ ...importedNames.length > 0 ? { imported_names: importedNames } : {},
2098
2136
  elements: deep.elements,
2099
2137
  stateVars: deep.stateVars,
2100
2138
  conditions: deep.conditions,
@@ -5048,6 +5086,132 @@ var middlewareGatesParser = {
5048
5086
  }
5049
5087
  };
5050
5088
 
5089
+ // src/server/graph/parsers/crosslayer/call-resolver.ts
5090
+ var BARE_IDENT = /^[A-Za-z_$][\w$]*$/;
5091
+ var callResolverParser = {
5092
+ id: "call-resolver",
5093
+ layer: "crosslayer",
5094
+ concern: "call-graph",
5095
+ detect(_rootDir) {
5096
+ return true;
5097
+ },
5098
+ generate(_rootDir, layerOutputs) {
5099
+ const definers = /* @__PURE__ */ new Map();
5100
+ const addDef = (sym, entry) => {
5101
+ if (!BARE_IDENT.test(sym)) return;
5102
+ const list = definers.get(sym);
5103
+ if (!list) {
5104
+ definers.set(sym, [entry]);
5105
+ } else if (!list.some((d) => d.nodeId === entry.nodeId)) {
5106
+ list.push(entry);
5107
+ }
5108
+ };
5109
+ const nodeLayer = /* @__PURE__ */ new Map();
5110
+ for (const [layer, output] of layerOutputs) {
5111
+ if (layer === "db" || layer === "static") continue;
5112
+ for (const node of output.nodes) {
5113
+ nodeLayer.set(node.id, layer);
5114
+ const entry = { nodeId: node.id, layer };
5115
+ for (const s of node.exports ?? []) addDef(s, entry);
5116
+ for (const s of node.defines ?? []) addDef(s, entry);
5117
+ }
5118
+ }
5119
+ const importTargets = /* @__PURE__ */ new Map();
5120
+ for (const [, output] of layerOutputs) {
5121
+ for (const e of output.edges) {
5122
+ if (e.type !== "imports" && e.type !== "imports_type" && e.type !== "renders") continue;
5123
+ let set = importTargets.get(e.source);
5124
+ if (!set) {
5125
+ set = /* @__PURE__ */ new Set();
5126
+ importTargets.set(e.source, set);
5127
+ }
5128
+ set.add(e.target);
5129
+ }
5130
+ }
5131
+ const crossRefs = [];
5132
+ const seen = /* @__PURE__ */ new Set();
5133
+ let resolvedSelf = 0;
5134
+ let resolvedImport = 0;
5135
+ let ambiguous = 0;
5136
+ let dropped = 0;
5137
+ for (const [, output] of layerOutputs) {
5138
+ for (const node of output.nodes) {
5139
+ const calls = node.effects?.calls;
5140
+ if (!calls || calls.length === 0) continue;
5141
+ const selfSyms = /* @__PURE__ */ new Set([
5142
+ ...node.exports ?? [],
5143
+ ...node.defines ?? []
5144
+ ]);
5145
+ const importedSyms = new Set(node.imported_names ?? []);
5146
+ const myImports = importTargets.get(node.id);
5147
+ const localSeen = /* @__PURE__ */ new Set();
5148
+ for (const callee of calls) {
5149
+ if (!BARE_IDENT.test(callee)) continue;
5150
+ if (localSeen.has(callee)) continue;
5151
+ localSeen.add(callee);
5152
+ const key = `${node.id}\u2192${callee}`;
5153
+ if (selfSyms.has(callee)) {
5154
+ if (seen.has(key)) continue;
5155
+ seen.add(key);
5156
+ crossRefs.push({
5157
+ source: node.id,
5158
+ target: callee,
5159
+ type: "calls",
5160
+ layer: nodeLayer.get(node.id) ?? "ui",
5161
+ defined_in: node.id
5162
+ });
5163
+ resolvedSelf++;
5164
+ continue;
5165
+ }
5166
+ if (!importedSyms.has(callee)) {
5167
+ dropped++;
5168
+ continue;
5169
+ }
5170
+ const defs = (definers.get(callee) ?? []).filter((d) => d.nodeId !== node.id);
5171
+ if (defs.length === 0) {
5172
+ dropped++;
5173
+ continue;
5174
+ }
5175
+ const fromImported = myImports ? defs.filter((d) => myImports.has(d.nodeId)) : [];
5176
+ const chosen = fromImported.length > 0 ? fromImported : defs;
5177
+ const owner = chosen[0];
5178
+ const isAmbiguous = chosen.length > 1;
5179
+ if (isAmbiguous) ambiguous++;
5180
+ if (seen.has(key)) continue;
5181
+ seen.add(key);
5182
+ const ref = {
5183
+ source: node.id,
5184
+ target: callee,
5185
+ type: "calls",
5186
+ layer: owner.layer,
5187
+ defined_in: owner.nodeId
5188
+ };
5189
+ if (isAmbiguous) ref.ambiguous = true;
5190
+ crossRefs.push(ref);
5191
+ resolvedImport++;
5192
+ }
5193
+ }
5194
+ }
5195
+ crossRefs.sort(
5196
+ (a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target)
5197
+ );
5198
+ return {
5199
+ cross_refs: crossRefs,
5200
+ flagged_edges: [],
5201
+ warnings: [],
5202
+ patterns: {
5203
+ call_resolution: {
5204
+ resolved_self: resolvedSelf,
5205
+ resolved_import: resolvedImport,
5206
+ ambiguous,
5207
+ dropped,
5208
+ symbols_defined: definers.size
5209
+ }
5210
+ }
5211
+ };
5212
+ }
5213
+ };
5214
+
5051
5215
  // src/server/graph/core/parser-registry.ts
5052
5216
  function isMultiLayerParser(p) {
5053
5217
  return "layers" in p && Array.isArray(p.layers);
@@ -5114,7 +5278,8 @@ function registerBuiltins(registry, disabled) {
5114
5278
  apiAnnotationsParser,
5115
5279
  urlLiteralScannerParser,
5116
5280
  staticRefScannerParser,
5117
- middlewareGatesParser
5281
+ middlewareGatesParser,
5282
+ callResolverParser
5118
5283
  ];
5119
5284
  for (const parser of builtins) {
5120
5285
  if (disabled.has(parser.id)) continue;