@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.
- package/dist/server/chart-serve.js +167 -2
- package/dist/server/cli.js +286 -50
- package/dist/server/course-entry.js +1 -1
- package/dist/server/graph-mcp-entry.js +180 -4
- package/dist/server/init-entry.js +563 -60
- package/dist/server/launch-bot-entry.js +4078 -0
- package/dist/server/launch-radar-entry.js +45 -0
- package/dist/server/orbit-entry.js +123 -26
- package/dist/server/parse-worker-entry.js +167 -2
- package/dist/server/radar-docker-init-entry.js +496 -39
- package/dist/server/radar-teardown-entry.js +23 -22
- package/dist/server/rover-entry.js +20555 -0
- package/package.json +8 -5
- package/scaffolds/ls-marketplace/plugins/kit/commands/standup.md +6 -6
- package/scaffolds/ls-marketplace/plugins/kit/skills/analyse/SKILL.md +6 -0
- package/scaffolds/ls-marketplace/plugins/kit/skills/brief/SKILL.md +72 -49
- package/scaffolds/ls-marketplace/plugins/kit/skills/brief/briefs.mjs +152 -0
- package/scaffolds/ls-marketplace/plugins/kit/skills/debug/SKILL.md +45 -20
- package/scaffolds/ls-marketplace/plugins/kit/skills/deploy-check/SKILL.md +76 -67
- package/scaffolds/ls-marketplace/plugins/kit/skills/handoff/SKILL.md +132 -0
- package/scaffolds/ls-marketplace/plugins/kit/skills/kickoff/SKILL.md +151 -0
- package/scaffolds/ls-marketplace/plugins/kit/skills/orbit/SKILL.md +14 -2
- package/scaffolds/ls-marketplace/plugins/kit/skills/ship/SKILL.md +149 -133
|
@@ -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,
|
|
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,
|
|
1614
|
-
const dstEnv = (0,
|
|
1615
|
-
if ((0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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:
|
|
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,
|
|
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,
|
|
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
|
-
|
|
2027
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
2330
|
-
var
|
|
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.
|
|
2505
|
+
process.stdout.write(`cd ${result.anchor}
|
|
2417
2506
|
`);
|
|
2418
2507
|
} else {
|
|
2419
|
-
process.stdout.write(
|
|
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,
|
|
2499
|
-
if ((0,
|
|
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,
|
|
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;
|