@launchsecure/launch-kit 0.0.5 → 0.0.7
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/chart-client/assets/index-0Xm1mXjM.js +379 -0
- package/dist/chart-client/assets/{index-DFslt72L.css → index-C-OUsIfD.css} +1 -1
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/{index-DCC--GO-.js → index-Ci95xk2_.js} +1 -1
- package/dist/client/assets/index-DbqEe7we.css +32 -0
- package/dist/client/index.html +2 -2
- package/dist/server/chart-serve.js +611 -99
- package/dist/server/cli.js +621 -124
- package/dist/server/graph-mcp-entry.js +760 -129
- package/package.json +1 -1
- package/dist/chart-client/assets/index-BUih0oqR.js +0 -358
- package/dist/client/assets/index-BCxRNp8I.css +0 -32
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,6 +30,30 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
33
|
+
// src/server/graph/core/config.ts
|
|
34
|
+
var config_exports = {};
|
|
35
|
+
__export(config_exports, {
|
|
36
|
+
loadConfig: () => loadConfig
|
|
37
|
+
});
|
|
38
|
+
function loadConfig(rootDir) {
|
|
39
|
+
const configPath = (0, import_node_path.join)(rootDir, CONFIG_FILENAME);
|
|
40
|
+
if (!(0, import_node_fs.existsSync)(configPath)) return {};
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse((0, import_node_fs.readFileSync)(configPath, "utf-8"));
|
|
43
|
+
} catch {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
var import_node_fs, import_node_path, CONFIG_FILENAME;
|
|
48
|
+
var init_config = __esm({
|
|
49
|
+
"src/server/graph/core/config.ts"() {
|
|
50
|
+
"use strict";
|
|
51
|
+
import_node_fs = require("node:fs");
|
|
52
|
+
import_node_path = require("node:path");
|
|
53
|
+
CONFIG_FILENAME = ".launchchart.json";
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
30
57
|
// src/server/chart-serve.ts
|
|
31
58
|
var chart_serve_exports = {};
|
|
32
59
|
__export(chart_serve_exports, {
|
|
@@ -35,30 +62,17 @@ __export(chart_serve_exports, {
|
|
|
35
62
|
});
|
|
36
63
|
module.exports = __toCommonJS(chart_serve_exports);
|
|
37
64
|
var import_node_http = __toESM(require("node:http"));
|
|
38
|
-
var
|
|
39
|
-
var
|
|
65
|
+
var import_node_fs13 = __toESM(require("node:fs"));
|
|
66
|
+
var import_node_path15 = __toESM(require("node:path"));
|
|
40
67
|
|
|
41
68
|
// src/server/graph/index.ts
|
|
42
|
-
var
|
|
43
|
-
var
|
|
69
|
+
var import_node_fs11 = require("node:fs");
|
|
70
|
+
var import_node_path13 = require("node:path");
|
|
44
71
|
|
|
45
72
|
// src/server/graph/core/graph-builder.ts
|
|
46
73
|
var import_node_fs8 = require("node:fs");
|
|
47
74
|
var import_node_path9 = require("node:path");
|
|
48
|
-
|
|
49
|
-
// src/server/graph/core/config.ts
|
|
50
|
-
var import_node_fs = require("node:fs");
|
|
51
|
-
var import_node_path = require("node:path");
|
|
52
|
-
var CONFIG_FILENAME = ".launchchart.json";
|
|
53
|
-
function loadConfig(rootDir) {
|
|
54
|
-
const configPath = (0, import_node_path.join)(rootDir, CONFIG_FILENAME);
|
|
55
|
-
if (!(0, import_node_fs.existsSync)(configPath)) return {};
|
|
56
|
-
try {
|
|
57
|
-
return JSON.parse((0, import_node_fs.readFileSync)(configPath, "utf-8"));
|
|
58
|
-
} catch {
|
|
59
|
-
return {};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
75
|
+
init_config();
|
|
62
76
|
|
|
63
77
|
// src/server/graph/core/parser-registry.ts
|
|
64
78
|
var import_node_path8 = require("node:path");
|
|
@@ -524,34 +538,6 @@ function classifyType(id) {
|
|
|
524
538
|
if (id.startsWith("lib/") || id.startsWith("config/")) return "lib";
|
|
525
539
|
return "component";
|
|
526
540
|
}
|
|
527
|
-
function classifyModule(id) {
|
|
528
|
-
if (/app\/\(auth\)\//.test(id)) return "auth";
|
|
529
|
-
if (/app\/\(admin\)\//.test(id)) return "admin";
|
|
530
|
-
if (/app\/\(settings\)\//.test(id)) return "settings";
|
|
531
|
-
if (/app\/\(app\)\/\[orgSlug\]\/\(project-pages\)\//.test(id)) return "project";
|
|
532
|
-
if (/app\/\(app\)\/\[orgSlug\]\/\(org-pages\)\//.test(id)) return "org";
|
|
533
|
-
if (/app\/\(app\)\/\[orgSlug\]\//.test(id)) return "org";
|
|
534
|
-
if (id.startsWith("app/integrations/")) return "integrations";
|
|
535
|
-
if (id.startsWith("app/docs/")) return "admin";
|
|
536
|
-
if (id.startsWith("client/components/ui/")) return "shared-ui";
|
|
537
|
-
if (id.startsWith("client/components/layout/") || /client\/lib\/navigation/.test(id)) return "layout";
|
|
538
|
-
if (/client\/components\/auth\//.test(id) || /client\/lib\/auth-/.test(id) || /client\/lib\/github-oauth/.test(id) || /client\/lib\/permission-service/.test(id) || /client\/hooks\/use-permissions/.test(id)) return "auth";
|
|
539
|
-
if (/client\/components\/prd-/.test(id) || /client\/hooks\/use-admin/.test(id)) return "admin";
|
|
540
|
-
if (/client\/components\/org-/.test(id) || /client\/hooks\/use-org-/.test(id) || /client\/hooks\/use-provider-def/.test(id)) return "org";
|
|
541
|
-
if (/client\/components\/project/.test(id) || /client\/hooks\/use-project-/.test(id) || /client\/hooks\/use-pipeline/.test(id) || /client\/hooks\/use-databases/.test(id) || /client\/hooks\/use-provider-env/.test(id) || /client\/hooks\/use-role-assign/.test(id) || /client\/components\/pipeline/.test(id) || /client\/components\/deployments/.test(id)) return "project";
|
|
542
|
-
if (/client\/hooks\/use-(profile|sessions|organizations|notification)/.test(id)) return "settings";
|
|
543
|
-
if (id.startsWith("server/auth/")) return "auth";
|
|
544
|
-
if (id.startsWith("server/mcp/")) return "mcp";
|
|
545
|
-
if (id.startsWith("server/lib/")) return "server-lib";
|
|
546
|
-
if (id.startsWith("server/middleware")) return "middleware";
|
|
547
|
-
if (id.startsWith("server/services/")) return "services";
|
|
548
|
-
if (id.startsWith("server/db")) return "db";
|
|
549
|
-
if (id.startsWith("server/errors")) return "errors";
|
|
550
|
-
if (id.startsWith("server/")) return "server-lib";
|
|
551
|
-
if (id.startsWith("config/")) return "config";
|
|
552
|
-
if (id.startsWith("lib/")) return "lib";
|
|
553
|
-
return "root";
|
|
554
|
-
}
|
|
555
541
|
function extractRoute(id) {
|
|
556
542
|
if (!id.endsWith("/page.tsx")) return null;
|
|
557
543
|
let route = id.replace(/^app\//, "/").replace(/\/page\.tsx$/, "");
|
|
@@ -772,8 +758,7 @@ function generate(rootDir) {
|
|
|
772
758
|
const parsed = parsedByPath.get(absPath);
|
|
773
759
|
const name = parsed.name || nameFromFilename(absPath);
|
|
774
760
|
const route = extractRoute(id);
|
|
775
|
-
|
|
776
|
-
nodes.push({ id, type, name, route, module: module_, exports: parsed.exports });
|
|
761
|
+
nodes.push({ id, type, name, route, exports: parsed.exports });
|
|
777
762
|
nodeIdSet.add(id);
|
|
778
763
|
nodeTypeMap.set(id, type);
|
|
779
764
|
if (route) routeToNodeId.set(route, id);
|
|
@@ -877,7 +862,6 @@ function generate(rootDir) {
|
|
|
877
862
|
type: "external",
|
|
878
863
|
name: parsed.name || nameFromFilename(absPath),
|
|
879
864
|
route: null,
|
|
880
|
-
module: "external",
|
|
881
865
|
exports: parsed.exports
|
|
882
866
|
});
|
|
883
867
|
nodeIdSet.add(externalId);
|
|
@@ -1892,31 +1876,425 @@ function generateAll(rootDir) {
|
|
|
1892
1876
|
}
|
|
1893
1877
|
|
|
1894
1878
|
// src/server/graph/index.ts
|
|
1879
|
+
init_config();
|
|
1880
|
+
|
|
1881
|
+
// src/server/graph/core/tagger-registry.ts
|
|
1882
|
+
var import_node_path11 = require("node:path");
|
|
1883
|
+
|
|
1884
|
+
// src/server/graph/taggers/module-tagger.ts
|
|
1885
|
+
var import_node_fs9 = require("node:fs");
|
|
1886
|
+
var import_node_path10 = require("node:path");
|
|
1887
|
+
function matchGlob(pattern, id) {
|
|
1888
|
+
const patParts = pattern.split("/");
|
|
1889
|
+
const idParts = id.split("/");
|
|
1890
|
+
return matchParts(patParts, 0, idParts, 0);
|
|
1891
|
+
}
|
|
1892
|
+
function matchParts(pat, pi, id, ii) {
|
|
1893
|
+
while (pi < pat.length && ii < id.length) {
|
|
1894
|
+
const p = pat[pi];
|
|
1895
|
+
if (p === "**") {
|
|
1896
|
+
for (let skip = ii; skip <= id.length; skip++) {
|
|
1897
|
+
if (matchParts(pat, pi + 1, id, skip)) return true;
|
|
1898
|
+
}
|
|
1899
|
+
return false;
|
|
1900
|
+
}
|
|
1901
|
+
if (p === "*") {
|
|
1902
|
+
pi++;
|
|
1903
|
+
ii++;
|
|
1904
|
+
continue;
|
|
1905
|
+
}
|
|
1906
|
+
if (p !== id[ii]) return false;
|
|
1907
|
+
pi++;
|
|
1908
|
+
ii++;
|
|
1909
|
+
}
|
|
1910
|
+
while (pi < pat.length && pat[pi] === "**") pi++;
|
|
1911
|
+
return pi === pat.length && ii === id.length;
|
|
1912
|
+
}
|
|
1913
|
+
var CONVENTION_DIRS = ["features", "modules", "domains", "areas"];
|
|
1914
|
+
function detectConventionDirs(rootDir) {
|
|
1915
|
+
const result = /* @__PURE__ */ new Map();
|
|
1916
|
+
const searchDirs = [
|
|
1917
|
+
rootDir,
|
|
1918
|
+
(0, import_node_path10.join)(rootDir, "src"),
|
|
1919
|
+
(0, import_node_path10.join)(rootDir, "app"),
|
|
1920
|
+
(0, import_node_path10.join)(rootDir, "lib")
|
|
1921
|
+
];
|
|
1922
|
+
for (const base of searchDirs) {
|
|
1923
|
+
for (const convention of CONVENTION_DIRS) {
|
|
1924
|
+
const dir = (0, import_node_path10.join)(base, convention);
|
|
1925
|
+
if (!(0, import_node_fs9.existsSync)(dir)) continue;
|
|
1926
|
+
try {
|
|
1927
|
+
const stat = (0, import_node_fs9.statSync)(dir);
|
|
1928
|
+
if (!stat.isDirectory()) continue;
|
|
1929
|
+
const entries = (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
1930
|
+
if (entries.length > 0) {
|
|
1931
|
+
const relPath = dir.replace(rootDir + "/", "").replace(rootDir + "\\", "");
|
|
1932
|
+
result.set(relPath, entries);
|
|
1933
|
+
}
|
|
1934
|
+
} catch {
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
return result;
|
|
1939
|
+
}
|
|
1940
|
+
function extractRouteGroups(id) {
|
|
1941
|
+
const groups = [];
|
|
1942
|
+
const re = /\(([^)]+)\)/g;
|
|
1943
|
+
let m;
|
|
1944
|
+
while ((m = re.exec(id)) !== null) {
|
|
1945
|
+
groups.push(m[1]);
|
|
1946
|
+
}
|
|
1947
|
+
return groups;
|
|
1948
|
+
}
|
|
1949
|
+
var SKIP_SEGMENTS = /* @__PURE__ */ new Set([
|
|
1950
|
+
"src",
|
|
1951
|
+
"app",
|
|
1952
|
+
"client",
|
|
1953
|
+
"server",
|
|
1954
|
+
"lib",
|
|
1955
|
+
"config"
|
|
1956
|
+
]);
|
|
1957
|
+
function isRouteGroup(segment) {
|
|
1958
|
+
return segment.startsWith("(") && segment.endsWith(")");
|
|
1959
|
+
}
|
|
1960
|
+
function isDynamicSegment(segment) {
|
|
1961
|
+
return segment.startsWith("[") || segment.startsWith(":");
|
|
1962
|
+
}
|
|
1963
|
+
function isDomainDir(segment) {
|
|
1964
|
+
return segment.includes(".") && !segment.endsWith(".tsx") && !segment.endsWith(".ts") && !segment.endsWith(".js") && !segment.endsWith(".jsx") && !segment.endsWith(".vue");
|
|
1965
|
+
}
|
|
1966
|
+
var TRIVIAL_GROUPS = /* @__PURE__ */ new Set([
|
|
1967
|
+
// Generic app wrappers
|
|
1968
|
+
"app",
|
|
1969
|
+
"all",
|
|
1970
|
+
"ee",
|
|
1971
|
+
"home",
|
|
1972
|
+
"root",
|
|
1973
|
+
"main",
|
|
1974
|
+
"site",
|
|
1975
|
+
// Auth/access boundary wrappers — protect routes, not feature modules
|
|
1976
|
+
"protected",
|
|
1977
|
+
"authenticated",
|
|
1978
|
+
"authed",
|
|
1979
|
+
"private",
|
|
1980
|
+
"public",
|
|
1981
|
+
"logged-in",
|
|
1982
|
+
"logged-out",
|
|
1983
|
+
"unprotected",
|
|
1984
|
+
"unauthenticated",
|
|
1985
|
+
"auth-required",
|
|
1986
|
+
"no-auth",
|
|
1987
|
+
"guest-only"
|
|
1988
|
+
]);
|
|
1989
|
+
function isTrivialGroup(name, extraTrivial) {
|
|
1990
|
+
if (TRIVIAL_GROUPS.has(name)) return true;
|
|
1991
|
+
if (extraTrivial?.has(name)) return true;
|
|
1992
|
+
const lower = name.toLowerCase();
|
|
1993
|
+
const wrapperPatterns = [
|
|
1994
|
+
/^.*-?wrapper$/,
|
|
1995
|
+
// "page-wrapper", "use-page-wrapper"
|
|
1996
|
+
/^.*-?layout$/,
|
|
1997
|
+
// "admin-layout", "settings-layout"
|
|
1998
|
+
/^use-/,
|
|
1999
|
+
// "use-page-wrapper"
|
|
2000
|
+
/^default$/
|
|
2001
|
+
];
|
|
2002
|
+
return wrapperPatterns.some((p) => p.test(lower));
|
|
2003
|
+
}
|
|
2004
|
+
function normalizeGroupName(name) {
|
|
2005
|
+
return name.replace(/-pages?$/, "").replace(/-layout$/, "").replace(/-wrapper$/, "");
|
|
2006
|
+
}
|
|
2007
|
+
function extractModuleFromPath(id, extraTrivial) {
|
|
2008
|
+
const segments = id.split("/");
|
|
2009
|
+
const routeGroups = extractRouteGroups(id);
|
|
2010
|
+
const moduleGroups = routeGroups.filter((g) => !isTrivialGroup(g, extraTrivial)).map(normalizeGroupName);
|
|
2011
|
+
if (moduleGroups.length > 0) {
|
|
2012
|
+
return moduleGroups[moduleGroups.length - 1];
|
|
2013
|
+
}
|
|
2014
|
+
const meaningful = [];
|
|
2015
|
+
for (const seg of segments) {
|
|
2016
|
+
if (seg.includes(".")) continue;
|
|
2017
|
+
if (isRouteGroup(seg)) continue;
|
|
2018
|
+
if (isDynamicSegment(seg)) continue;
|
|
2019
|
+
if (isDomainDir(seg)) continue;
|
|
2020
|
+
if (SKIP_SEGMENTS.has(seg)) continue;
|
|
2021
|
+
meaningful.push(seg);
|
|
2022
|
+
}
|
|
2023
|
+
if (meaningful.length > 0) {
|
|
2024
|
+
return meaningful[0];
|
|
2025
|
+
}
|
|
2026
|
+
return "root";
|
|
2027
|
+
}
|
|
2028
|
+
var cachedRootDir = null;
|
|
2029
|
+
var cachedConventionDirs = /* @__PURE__ */ new Map();
|
|
2030
|
+
var moduleTagger = {
|
|
2031
|
+
id: "module",
|
|
2032
|
+
tagKey: "module",
|
|
2033
|
+
trackUntagged: true,
|
|
2034
|
+
layers: null,
|
|
2035
|
+
// applies to all layers
|
|
2036
|
+
tag(nodes, layer, rootDir) {
|
|
2037
|
+
if (cachedRootDir !== rootDir) {
|
|
2038
|
+
cachedConventionDirs = detectConventionDirs(rootDir);
|
|
2039
|
+
cachedRootDir = rootDir;
|
|
2040
|
+
}
|
|
2041
|
+
let configRules = [];
|
|
2042
|
+
let extraTrivial;
|
|
2043
|
+
try {
|
|
2044
|
+
const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
|
|
2045
|
+
const config = loadConfig2(rootDir);
|
|
2046
|
+
configRules = config.taggers?.module?.rules ?? [];
|
|
2047
|
+
const trivialFromConfig = config.taggers?.module?.trivialGroups;
|
|
2048
|
+
if (trivialFromConfig?.length) {
|
|
2049
|
+
extraTrivial = new Set(trivialFromConfig);
|
|
2050
|
+
}
|
|
2051
|
+
} catch {
|
|
2052
|
+
}
|
|
2053
|
+
const result = /* @__PURE__ */ new Map();
|
|
2054
|
+
for (const node of nodes) {
|
|
2055
|
+
const id = node.id;
|
|
2056
|
+
let matched = false;
|
|
2057
|
+
for (const rule of configRules) {
|
|
2058
|
+
if (matchGlob(rule.match, id)) {
|
|
2059
|
+
result.set(id, rule.module);
|
|
2060
|
+
matched = true;
|
|
2061
|
+
break;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
if (matched) continue;
|
|
2065
|
+
matched = false;
|
|
2066
|
+
for (const [convDir, moduleNames] of cachedConventionDirs) {
|
|
2067
|
+
if (id.startsWith(convDir + "/")) {
|
|
2068
|
+
const rest = id.slice(convDir.length + 1);
|
|
2069
|
+
const firstSeg = rest.split("/")[0];
|
|
2070
|
+
if (moduleNames.includes(firstSeg)) {
|
|
2071
|
+
result.set(id, firstSeg);
|
|
2072
|
+
matched = true;
|
|
2073
|
+
break;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
if (matched) continue;
|
|
2078
|
+
const module2 = extractModuleFromPath(id, extraTrivial);
|
|
2079
|
+
result.set(id, module2);
|
|
2080
|
+
}
|
|
2081
|
+
return result;
|
|
2082
|
+
}
|
|
2083
|
+
};
|
|
2084
|
+
|
|
2085
|
+
// src/server/graph/taggers/screen-tagger.ts
|
|
2086
|
+
var SCREEN_TYPES = /* @__PURE__ */ new Set(["page", "layout"]);
|
|
2087
|
+
var screenTagger = {
|
|
2088
|
+
id: "screen",
|
|
2089
|
+
tagKey: "screen",
|
|
2090
|
+
trackUntagged: true,
|
|
2091
|
+
layers: ["ui"],
|
|
2092
|
+
tag(nodes, layer) {
|
|
2093
|
+
if (layer !== "ui") return /* @__PURE__ */ new Map();
|
|
2094
|
+
const result = /* @__PURE__ */ new Map();
|
|
2095
|
+
for (const node of nodes) {
|
|
2096
|
+
if (SCREEN_TYPES.has(node.type)) {
|
|
2097
|
+
result.set(node.id, "true");
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
return result;
|
|
2101
|
+
}
|
|
2102
|
+
};
|
|
2103
|
+
|
|
2104
|
+
// src/server/graph/core/tagger-registry.ts
|
|
2105
|
+
var TaggerRegistry = class {
|
|
2106
|
+
constructor() {
|
|
2107
|
+
this.taggers = [];
|
|
2108
|
+
this.ids = /* @__PURE__ */ new Set();
|
|
2109
|
+
}
|
|
2110
|
+
register(tagger) {
|
|
2111
|
+
if (this.ids.has(tagger.id)) {
|
|
2112
|
+
throw new Error(`Duplicate tagger id: ${tagger.id}`);
|
|
2113
|
+
}
|
|
2114
|
+
this.ids.add(tagger.id);
|
|
2115
|
+
this.taggers.push(tagger);
|
|
2116
|
+
}
|
|
2117
|
+
getAll() {
|
|
2118
|
+
return this.taggers;
|
|
2119
|
+
}
|
|
2120
|
+
getForLayer(layer) {
|
|
2121
|
+
return this.taggers.filter((t) => t.layers === null || t.layers.includes(layer));
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
2124
|
+
var BUILTIN_TAGGERS = [moduleTagger, screenTagger];
|
|
2125
|
+
function registerBuiltins2(registry, disabled, config) {
|
|
2126
|
+
for (const tagger of BUILTIN_TAGGERS) {
|
|
2127
|
+
if (disabled.has(tagger.id)) continue;
|
|
2128
|
+
const override = config.taggers?.trackUntagged?.[tagger.id];
|
|
2129
|
+
if (override !== void 0) {
|
|
2130
|
+
tagger.trackUntagged = override;
|
|
2131
|
+
}
|
|
2132
|
+
registry.register(tagger);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
function loadCustomTaggers(registry, config, rootDir, disabled) {
|
|
2136
|
+
for (const entry of config.taggers?.custom ?? []) {
|
|
2137
|
+
if (disabled.has(entry.id)) continue;
|
|
2138
|
+
try {
|
|
2139
|
+
const absPath = (0, import_node_path11.resolve)(rootDir, entry.path);
|
|
2140
|
+
const mod = require(absPath);
|
|
2141
|
+
const tagger = "default" in mod ? mod.default : mod;
|
|
2142
|
+
const override = config.taggers?.trackUntagged?.[tagger.id];
|
|
2143
|
+
if (override !== void 0) {
|
|
2144
|
+
tagger.trackUntagged = override;
|
|
2145
|
+
}
|
|
2146
|
+
registry.register(tagger);
|
|
2147
|
+
} catch (err) {
|
|
2148
|
+
process.stderr.write(`[launch-chart] failed to load custom tagger from ${entry.path}: ${err}
|
|
2149
|
+
`);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
function createTaggerRegistry(config, rootDir) {
|
|
2154
|
+
const registry = new TaggerRegistry();
|
|
2155
|
+
const disabled = new Set(config.taggers?.disabled ?? []);
|
|
2156
|
+
registerBuiltins2(registry, disabled, config);
|
|
2157
|
+
loadCustomTaggers(registry, config, rootDir, disabled);
|
|
2158
|
+
return registry;
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
// src/server/graph/core/tag-store.ts
|
|
2162
|
+
var import_node_fs10 = require("node:fs");
|
|
2163
|
+
var import_node_path12 = require("node:path");
|
|
2164
|
+
var TAGS_FILENAME = "tags.json";
|
|
1895
2165
|
var GRAPHS_DIR = ".launchsecure/graphs";
|
|
2166
|
+
var tagCache = /* @__PURE__ */ new Map();
|
|
2167
|
+
function tagsFilePath(rootDir) {
|
|
2168
|
+
return (0, import_node_path12.join)(rootDir, GRAPHS_DIR, TAGS_FILENAME);
|
|
2169
|
+
}
|
|
2170
|
+
function readTagStore(rootDir) {
|
|
2171
|
+
const filePath = tagsFilePath(rootDir);
|
|
2172
|
+
if (!(0, import_node_fs10.existsSync)(filePath)) return {};
|
|
2173
|
+
const stat = (0, import_node_fs10.statSync)(filePath);
|
|
2174
|
+
const cached = tagCache.get(filePath);
|
|
2175
|
+
if (cached && cached.mtimeMs === stat.mtimeMs) {
|
|
2176
|
+
return cached.store;
|
|
2177
|
+
}
|
|
2178
|
+
try {
|
|
2179
|
+
const content = (0, import_node_fs10.readFileSync)(filePath, "utf-8");
|
|
2180
|
+
const store = JSON.parse(content);
|
|
2181
|
+
tagCache.set(filePath, { mtimeMs: stat.mtimeMs, store });
|
|
2182
|
+
return store;
|
|
2183
|
+
} catch {
|
|
2184
|
+
return {};
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
function writeTagStore(rootDir, store) {
|
|
2188
|
+
const filePath = tagsFilePath(rootDir);
|
|
2189
|
+
const dir = (0, import_node_path12.dirname)(filePath);
|
|
2190
|
+
(0, import_node_fs10.mkdirSync)(dir, { recursive: true });
|
|
2191
|
+
const cleaned = {};
|
|
2192
|
+
for (const [nodeId, tags] of Object.entries(store)) {
|
|
2193
|
+
if (Object.keys(tags).length > 0) {
|
|
2194
|
+
cleaned[nodeId] = tags;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
(0, import_node_fs10.writeFileSync)(filePath, JSON.stringify(cleaned, null, 2) + "\n", "utf-8");
|
|
2198
|
+
tagCache.delete(filePath);
|
|
2199
|
+
}
|
|
2200
|
+
function setTag(rootDir, nodeId, key, value) {
|
|
2201
|
+
const store = readTagStore(rootDir);
|
|
2202
|
+
if (!store[nodeId]) store[nodeId] = {};
|
|
2203
|
+
store[nodeId][key] = value;
|
|
2204
|
+
writeTagStore(rootDir, store);
|
|
2205
|
+
}
|
|
2206
|
+
function removeTag(rootDir, nodeId, key) {
|
|
2207
|
+
const store = readTagStore(rootDir);
|
|
2208
|
+
if (!store[nodeId]) return;
|
|
2209
|
+
delete store[nodeId][key];
|
|
2210
|
+
if (Object.keys(store[nodeId]).length === 0) {
|
|
2211
|
+
delete store[nodeId];
|
|
2212
|
+
}
|
|
2213
|
+
writeTagStore(rootDir, store);
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
// src/server/graph/index.ts
|
|
2217
|
+
var GRAPHS_DIR2 = ".launchsecure/graphs";
|
|
1896
2218
|
var LAYERS = ["ui", "api", "db"];
|
|
1897
2219
|
var graphCache = /* @__PURE__ */ new Map();
|
|
2220
|
+
var taggedCache = /* @__PURE__ */ new Map();
|
|
1898
2221
|
function graphsDir(rootDir) {
|
|
1899
|
-
return (0,
|
|
2222
|
+
return (0, import_node_path13.join)(rootDir, GRAPHS_DIR2);
|
|
1900
2223
|
}
|
|
1901
2224
|
function graphFilePath(rootDir, layer) {
|
|
1902
|
-
return (0,
|
|
2225
|
+
return (0, import_node_path13.join)(graphsDir(rootDir), `${layer}.json`);
|
|
2226
|
+
}
|
|
2227
|
+
function tagsFilePath2(rootDir) {
|
|
2228
|
+
return (0, import_node_path13.join)(graphsDir(rootDir), "tags.json");
|
|
2229
|
+
}
|
|
2230
|
+
function getMtimeMs(filePath) {
|
|
2231
|
+
if (!(0, import_node_fs11.existsSync)(filePath)) return 0;
|
|
2232
|
+
return (0, import_node_fs11.statSync)(filePath).mtimeMs;
|
|
1903
2233
|
}
|
|
1904
2234
|
function invalidateCache(filePath) {
|
|
1905
2235
|
graphCache.delete(filePath);
|
|
1906
2236
|
}
|
|
1907
|
-
function
|
|
2237
|
+
function invalidateTaggedCache(rootDir, layer) {
|
|
2238
|
+
taggedCache.delete(`${rootDir}:${layer}`);
|
|
2239
|
+
}
|
|
2240
|
+
function applyTags(graph, layer, rootDir) {
|
|
2241
|
+
const config = loadConfig(rootDir);
|
|
2242
|
+
const registry = createTaggerRegistry(config, rootDir);
|
|
2243
|
+
const manualTags = readTagStore(rootDir);
|
|
2244
|
+
const taggedNodes = graph.nodes.map((n) => ({ ...n }));
|
|
2245
|
+
const taggers = registry.getForLayer(layer);
|
|
2246
|
+
for (const tagger of taggers) {
|
|
2247
|
+
const assignments = tagger.tag(taggedNodes, layer, rootDir);
|
|
2248
|
+
for (const node of taggedNodes) {
|
|
2249
|
+
if (!node.tags) node.tags = {};
|
|
2250
|
+
const tags = node.tags;
|
|
2251
|
+
const value = assignments.get(node.id);
|
|
2252
|
+
if (value !== void 0) {
|
|
2253
|
+
tags[tagger.tagKey] = value;
|
|
2254
|
+
} else if (tagger.trackUntagged) {
|
|
2255
|
+
tags[tagger.tagKey] = "untagged";
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
for (const node of taggedNodes) {
|
|
2260
|
+
const manual = manualTags[node.id];
|
|
2261
|
+
if (manual) {
|
|
2262
|
+
if (!node.tags) node.tags = {};
|
|
2263
|
+
const tags = node.tags;
|
|
2264
|
+
Object.assign(tags, manual);
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
return { ...graph, nodes: taggedNodes };
|
|
2268
|
+
}
|
|
2269
|
+
function readGraphRaw(rootDir, layer) {
|
|
1908
2270
|
const filePath = graphFilePath(rootDir, layer);
|
|
1909
|
-
if (!(0,
|
|
1910
|
-
const stat = (0,
|
|
2271
|
+
if (!(0, import_node_fs11.existsSync)(filePath)) return null;
|
|
2272
|
+
const stat = (0, import_node_fs11.statSync)(filePath);
|
|
1911
2273
|
const cached = graphCache.get(filePath);
|
|
1912
2274
|
if (cached && cached.mtimeMs === stat.mtimeMs) {
|
|
1913
2275
|
return cached.graph;
|
|
1914
2276
|
}
|
|
1915
|
-
const content = (0,
|
|
2277
|
+
const content = (0, import_node_fs11.readFileSync)(filePath, "utf-8");
|
|
1916
2278
|
const graph = JSON.parse(content);
|
|
1917
2279
|
graphCache.set(filePath, { mtimeMs: stat.mtimeMs, graph });
|
|
1918
2280
|
return graph;
|
|
1919
2281
|
}
|
|
2282
|
+
function readGraph(rootDir, layer) {
|
|
2283
|
+
const rawFilePath = graphFilePath(rootDir, layer);
|
|
2284
|
+
if (!(0, import_node_fs11.existsSync)(rawFilePath)) return null;
|
|
2285
|
+
const rawMtime = getMtimeMs(rawFilePath);
|
|
2286
|
+
const tagsMtime = getMtimeMs(tagsFilePath2(rootDir));
|
|
2287
|
+
const cacheKey = `${rootDir}:${layer}`;
|
|
2288
|
+
const cached = taggedCache.get(cacheKey);
|
|
2289
|
+
if (cached && cached.rawMtimeMs === rawMtime && cached.tagsMtimeMs === tagsMtime) {
|
|
2290
|
+
return cached.graph;
|
|
2291
|
+
}
|
|
2292
|
+
const raw = readGraphRaw(rootDir, layer);
|
|
2293
|
+
if (!raw) return null;
|
|
2294
|
+
const tagged = applyTags(raw, layer, rootDir);
|
|
2295
|
+
taggedCache.set(cacheKey, { rawMtimeMs: rawMtime, tagsMtimeMs: tagsMtime, graph: tagged });
|
|
2296
|
+
return tagged;
|
|
2297
|
+
}
|
|
1920
2298
|
function readAllGraphs(rootDir) {
|
|
1921
2299
|
const result = {};
|
|
1922
2300
|
for (const layer of LAYERS) {
|
|
@@ -1927,32 +2305,52 @@ function readAllGraphs(rootDir) {
|
|
|
1927
2305
|
}
|
|
1928
2306
|
function generateGraph(rootDir, layer) {
|
|
1929
2307
|
const dir = graphsDir(rootDir);
|
|
1930
|
-
(0,
|
|
2308
|
+
(0, import_node_fs11.mkdirSync)(dir, { recursive: true });
|
|
1931
2309
|
const results = layer ? [generateLayer(rootDir, layer)].filter((r) => r !== null) : generateAll(rootDir);
|
|
1932
2310
|
for (const result of results) {
|
|
1933
2311
|
const filePath = graphFilePath(rootDir, result.layer);
|
|
1934
|
-
(0,
|
|
2312
|
+
(0, import_node_fs11.writeFileSync)(filePath, JSON.stringify(result.output, null, 2) + "\n", "utf-8");
|
|
1935
2313
|
invalidateCache(filePath);
|
|
2314
|
+
invalidateTaggedCache(rootDir, result.layer);
|
|
1936
2315
|
}
|
|
1937
2316
|
return results;
|
|
1938
2317
|
}
|
|
1939
2318
|
|
|
1940
2319
|
// src/server/lockfile.ts
|
|
1941
2320
|
var import_node_child_process = require("node:child_process");
|
|
1942
|
-
var
|
|
2321
|
+
var import_node_fs12 = require("node:fs");
|
|
1943
2322
|
var import_node_os = require("node:os");
|
|
1944
|
-
var
|
|
1945
|
-
function lockDir() {
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
return (0,
|
|
1950
|
-
}
|
|
1951
|
-
function
|
|
1952
|
-
|
|
1953
|
-
|
|
2323
|
+
var import_node_path14 = require("node:path");
|
|
2324
|
+
function lockDir(projectRoot) {
|
|
2325
|
+
if (projectRoot) {
|
|
2326
|
+
return (0, import_node_path14.join)(projectRoot, ".launchsecure");
|
|
2327
|
+
}
|
|
2328
|
+
return (0, import_node_path14.join)((0, import_node_os.homedir)(), ".launchsecure");
|
|
2329
|
+
}
|
|
2330
|
+
function lockPath(projectRoot) {
|
|
2331
|
+
return (0, import_node_path14.join)(lockDir(projectRoot), "launch-chart.lock");
|
|
2332
|
+
}
|
|
2333
|
+
var _activeProjectRoot;
|
|
2334
|
+
function readLock(projectRoot) {
|
|
2335
|
+
const root = projectRoot ?? _activeProjectRoot;
|
|
2336
|
+
const p = lockPath(root);
|
|
2337
|
+
if (!(0, import_node_fs12.existsSync)(p)) {
|
|
2338
|
+
if (root) {
|
|
2339
|
+
const globalP = lockPath();
|
|
2340
|
+
if ((0, import_node_fs12.existsSync)(globalP)) {
|
|
2341
|
+
try {
|
|
2342
|
+
const data = JSON.parse((0, import_node_fs12.readFileSync)(globalP, "utf-8"));
|
|
2343
|
+
if (typeof data.pid === "number" && typeof data.port === "number" && data.cwd === root) {
|
|
2344
|
+
return data;
|
|
2345
|
+
}
|
|
2346
|
+
} catch {
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
1954
2352
|
try {
|
|
1955
|
-
const data = JSON.parse((0,
|
|
2353
|
+
const data = JSON.parse((0, import_node_fs12.readFileSync)(p, "utf-8"));
|
|
1956
2354
|
if (typeof data.pid !== "number" || typeof data.port !== "number") return null;
|
|
1957
2355
|
return data;
|
|
1958
2356
|
} catch {
|
|
@@ -1981,34 +2379,41 @@ function getListenerPid(port) {
|
|
|
1981
2379
|
return null;
|
|
1982
2380
|
}
|
|
1983
2381
|
}
|
|
1984
|
-
function getLiveLock() {
|
|
1985
|
-
const
|
|
2382
|
+
function getLiveLock(projectRoot) {
|
|
2383
|
+
const root = projectRoot ?? _activeProjectRoot;
|
|
2384
|
+
const lock = readLock(root);
|
|
1986
2385
|
if (!lock) return null;
|
|
1987
2386
|
const listenerPid = getListenerPid(lock.port);
|
|
1988
2387
|
const live = listenerPid !== null ? listenerPid === lock.pid : isPidAlive(lock.pid);
|
|
1989
2388
|
if (!live) {
|
|
1990
2389
|
try {
|
|
1991
|
-
(0,
|
|
2390
|
+
(0, import_node_fs12.unlinkSync)(lockPath(root));
|
|
1992
2391
|
} catch {
|
|
1993
2392
|
}
|
|
1994
2393
|
return null;
|
|
1995
2394
|
}
|
|
1996
2395
|
return lock;
|
|
1997
2396
|
}
|
|
1998
|
-
function writeLock(data) {
|
|
1999
|
-
|
|
2000
|
-
(0,
|
|
2397
|
+
function writeLock(data, projectRoot) {
|
|
2398
|
+
const root = projectRoot ?? _activeProjectRoot;
|
|
2399
|
+
(0, import_node_fs12.mkdirSync)(lockDir(root), { recursive: true });
|
|
2400
|
+
(0, import_node_fs12.writeFileSync)(lockPath(root), JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
2401
|
+
if (root) _activeProjectRoot = root;
|
|
2001
2402
|
}
|
|
2002
|
-
function clearLock() {
|
|
2403
|
+
function clearLock(projectRoot) {
|
|
2404
|
+
const root = projectRoot ?? _activeProjectRoot;
|
|
2003
2405
|
try {
|
|
2004
|
-
(0,
|
|
2406
|
+
(0, import_node_fs12.unlinkSync)(lockPath(root));
|
|
2005
2407
|
} catch {
|
|
2006
2408
|
}
|
|
2007
2409
|
}
|
|
2008
2410
|
|
|
2009
2411
|
// src/server/chart-serve.ts
|
|
2010
|
-
|
|
2011
|
-
var MAX_PORT_SCAN =
|
|
2412
|
+
init_config();
|
|
2413
|
+
var MAX_PORT_SCAN = 3;
|
|
2414
|
+
function randomPort() {
|
|
2415
|
+
return 49152 + Math.floor(Math.random() * (65535 - 49152));
|
|
2416
|
+
}
|
|
2012
2417
|
var MIME_TYPES = {
|
|
2013
2418
|
".html": "text/html; charset=utf-8",
|
|
2014
2419
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -2023,16 +2428,16 @@ var MIME_TYPES = {
|
|
|
2023
2428
|
function findProjectRoot(startDir) {
|
|
2024
2429
|
let dir = startDir;
|
|
2025
2430
|
for (let i = 0; i < 8; i++) {
|
|
2026
|
-
const graphsDir2 =
|
|
2027
|
-
if (
|
|
2028
|
-
const parent =
|
|
2431
|
+
const graphsDir2 = import_node_path15.default.join(dir, ".launchsecure", "graphs");
|
|
2432
|
+
if (import_node_fs13.default.existsSync(import_node_path15.default.join(graphsDir2, "ui.json")) || import_node_fs13.default.existsSync(import_node_path15.default.join(graphsDir2, "api.json")) || import_node_fs13.default.existsSync(import_node_path15.default.join(graphsDir2, "db.json"))) return dir;
|
|
2433
|
+
const parent = import_node_path15.default.dirname(dir);
|
|
2029
2434
|
if (parent === dir) break;
|
|
2030
2435
|
dir = parent;
|
|
2031
2436
|
}
|
|
2032
2437
|
dir = startDir;
|
|
2033
2438
|
for (let i = 0; i < 8; i++) {
|
|
2034
|
-
if (
|
|
2035
|
-
const parent =
|
|
2439
|
+
if (import_node_fs13.default.existsSync(import_node_path15.default.join(dir, ".git"))) return dir;
|
|
2440
|
+
const parent = import_node_path15.default.dirname(dir);
|
|
2036
2441
|
if (parent === dir) break;
|
|
2037
2442
|
dir = parent;
|
|
2038
2443
|
}
|
|
@@ -2084,16 +2489,16 @@ function buildMergedGraph(projectRoot) {
|
|
|
2084
2489
|
};
|
|
2085
2490
|
}
|
|
2086
2491
|
function serveStatic(res, filePath) {
|
|
2087
|
-
if (!
|
|
2088
|
-
const ext =
|
|
2492
|
+
if (!import_node_fs13.default.existsSync(filePath) || !import_node_fs13.default.statSync(filePath).isFile()) return false;
|
|
2493
|
+
const ext = import_node_path15.default.extname(filePath).toLowerCase();
|
|
2089
2494
|
const mime = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
2090
2495
|
res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
|
|
2091
|
-
|
|
2496
|
+
import_node_fs13.default.createReadStream(filePath).pipe(res);
|
|
2092
2497
|
return true;
|
|
2093
2498
|
}
|
|
2094
2499
|
function serveIndex(res, clientDir) {
|
|
2095
|
-
const indexPath =
|
|
2096
|
-
if (!
|
|
2500
|
+
const indexPath = import_node_path15.default.join(clientDir, "index.html");
|
|
2501
|
+
if (!import_node_fs13.default.existsSync(indexPath)) {
|
|
2097
2502
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
2098
2503
|
res.end(`LaunchChart client bundle not found at ${clientDir}. Run 'npm run build:chart-client'.`);
|
|
2099
2504
|
return;
|
|
@@ -2101,14 +2506,14 @@ function serveIndex(res, clientDir) {
|
|
|
2101
2506
|
serveStatic(res, indexPath);
|
|
2102
2507
|
}
|
|
2103
2508
|
function tryListen(server, port) {
|
|
2104
|
-
return new Promise((
|
|
2509
|
+
return new Promise((resolve3, reject) => {
|
|
2105
2510
|
const onError = (err) => {
|
|
2106
2511
|
server.off("listening", onListening);
|
|
2107
2512
|
reject(err);
|
|
2108
2513
|
};
|
|
2109
2514
|
const onListening = () => {
|
|
2110
2515
|
server.off("error", onError);
|
|
2111
|
-
|
|
2516
|
+
resolve3(port);
|
|
2112
2517
|
};
|
|
2113
2518
|
server.once("error", onError);
|
|
2114
2519
|
server.once("listening", onListening);
|
|
@@ -2135,7 +2540,7 @@ async function bindWithFallback(server, startPort) {
|
|
|
2135
2540
|
async function startChartServer(opts = {}) {
|
|
2136
2541
|
const cwd = opts.cwd ?? process.cwd();
|
|
2137
2542
|
const projectRoot = findProjectRoot(cwd);
|
|
2138
|
-
const existing = getLiveLock();
|
|
2543
|
+
const existing = getLiveLock(projectRoot);
|
|
2139
2544
|
if (existing) {
|
|
2140
2545
|
if (!opts.quiet) {
|
|
2141
2546
|
process.stderr.write(
|
|
@@ -2145,7 +2550,7 @@ async function startChartServer(opts = {}) {
|
|
|
2145
2550
|
}
|
|
2146
2551
|
return { port: existing.port, url: existing.url };
|
|
2147
2552
|
}
|
|
2148
|
-
const clientDir = opts.clientDir ??
|
|
2553
|
+
const clientDir = opts.clientDir ?? import_node_path15.default.join(__dirname, "..", "chart-client");
|
|
2149
2554
|
const server = import_node_http.default.createServer((req, res) => {
|
|
2150
2555
|
try {
|
|
2151
2556
|
const url2 = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
@@ -2183,6 +2588,26 @@ async function startChartServer(opts = {}) {
|
|
|
2183
2588
|
}
|
|
2184
2589
|
return;
|
|
2185
2590
|
}
|
|
2591
|
+
if (req.method === "GET" && url2.pathname === "/api/file-content") {
|
|
2592
|
+
const relPath = url2.searchParams.get("path");
|
|
2593
|
+
if (!relPath || relPath.includes("..") || import_node_path15.default.isAbsolute(relPath)) {
|
|
2594
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2595
|
+
res.end(JSON.stringify({ error: "Invalid path" }));
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2598
|
+
const filePath = import_node_path15.default.join(projectRoot, relPath);
|
|
2599
|
+
if (!filePath.startsWith(projectRoot) || !import_node_fs13.default.existsSync(filePath) || !import_node_fs13.default.statSync(filePath).isFile()) {
|
|
2600
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
2601
|
+
res.end(JSON.stringify({ error: "File not found" }));
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
const ext = import_node_path15.default.extname(filePath).toLowerCase();
|
|
2605
|
+
const langMap = { ".ts": "typescript", ".tsx": "tsx", ".js": "javascript", ".jsx": "jsx", ".prisma": "prisma", ".json": "json", ".css": "css" };
|
|
2606
|
+
const content = import_node_fs13.default.readFileSync(filePath, "utf-8");
|
|
2607
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2608
|
+
res.end(JSON.stringify({ content, language: langMap[ext] ?? "text", path: relPath }));
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2186
2611
|
if (req.method === "GET" && url2.pathname === "/api/health") {
|
|
2187
2612
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2188
2613
|
res.end(JSON.stringify({ ok: true, projectRoot }));
|
|
@@ -2212,8 +2637,94 @@ async function startChartServer(opts = {}) {
|
|
|
2212
2637
|
req.on("end", () => {
|
|
2213
2638
|
try {
|
|
2214
2639
|
const newConfig = JSON.parse(body);
|
|
2215
|
-
const configPath =
|
|
2216
|
-
|
|
2640
|
+
const configPath = import_node_path15.default.join(projectRoot, ".launchchart.json");
|
|
2641
|
+
import_node_fs13.default.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
|
|
2642
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2643
|
+
res.end(JSON.stringify({ ok: true }));
|
|
2644
|
+
} catch (err) {
|
|
2645
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2646
|
+
res.end(JSON.stringify({ ok: false, error: String(err) }));
|
|
2647
|
+
}
|
|
2648
|
+
});
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
if (req.method === "GET" && url2.pathname === "/api/tagger-config") {
|
|
2652
|
+
const config = loadConfig(projectRoot);
|
|
2653
|
+
const builtinTaggers = [
|
|
2654
|
+
{ id: "module", tagKey: "module", trackUntagged: config.taggers?.trackUntagged?.module ?? true },
|
|
2655
|
+
{ id: "screen", tagKey: "screen", trackUntagged: config.taggers?.trackUntagged?.screen ?? true }
|
|
2656
|
+
];
|
|
2657
|
+
const disabled = config.taggers?.disabled ?? [];
|
|
2658
|
+
const customTaggers = config.taggers?.custom ?? [];
|
|
2659
|
+
const moduleRules = config.taggers?.module?.rules ?? [];
|
|
2660
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2661
|
+
res.end(JSON.stringify({ builtinTaggers, disabled, customTaggers, moduleRules }));
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
2664
|
+
if (req.method === "POST" && url2.pathname === "/api/tagger-config") {
|
|
2665
|
+
let body = "";
|
|
2666
|
+
req.on("data", (chunk) => {
|
|
2667
|
+
body += chunk.toString();
|
|
2668
|
+
});
|
|
2669
|
+
req.on("end", () => {
|
|
2670
|
+
try {
|
|
2671
|
+
const taggerConfig = JSON.parse(body);
|
|
2672
|
+
const config = loadConfig(projectRoot);
|
|
2673
|
+
config.taggers = taggerConfig;
|
|
2674
|
+
const configPath = import_node_path15.default.join(projectRoot, ".launchchart.json");
|
|
2675
|
+
import_node_fs13.default.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2676
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2677
|
+
res.end(JSON.stringify({ ok: true }));
|
|
2678
|
+
} catch (err) {
|
|
2679
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2680
|
+
res.end(JSON.stringify({ ok: false, error: String(err) }));
|
|
2681
|
+
}
|
|
2682
|
+
});
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2685
|
+
if (req.method === "GET" && url2.pathname === "/api/tags") {
|
|
2686
|
+
const store = readTagStore(projectRoot);
|
|
2687
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2688
|
+
res.end(JSON.stringify(store));
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
if (req.method === "POST" && url2.pathname === "/api/tags") {
|
|
2692
|
+
let body = "";
|
|
2693
|
+
req.on("data", (chunk) => {
|
|
2694
|
+
body += chunk.toString();
|
|
2695
|
+
});
|
|
2696
|
+
req.on("end", () => {
|
|
2697
|
+
try {
|
|
2698
|
+
const { nodeId, key, value } = JSON.parse(body);
|
|
2699
|
+
if (!nodeId || !key || !value) {
|
|
2700
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2701
|
+
res.end(JSON.stringify({ ok: false, error: "nodeId, key, and value are required" }));
|
|
2702
|
+
return;
|
|
2703
|
+
}
|
|
2704
|
+
setTag(projectRoot, nodeId, key, value);
|
|
2705
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2706
|
+
res.end(JSON.stringify({ ok: true }));
|
|
2707
|
+
} catch (err) {
|
|
2708
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2709
|
+
res.end(JSON.stringify({ ok: false, error: String(err) }));
|
|
2710
|
+
}
|
|
2711
|
+
});
|
|
2712
|
+
return;
|
|
2713
|
+
}
|
|
2714
|
+
if (req.method === "DELETE" && url2.pathname === "/api/tags") {
|
|
2715
|
+
let body = "";
|
|
2716
|
+
req.on("data", (chunk) => {
|
|
2717
|
+
body += chunk.toString();
|
|
2718
|
+
});
|
|
2719
|
+
req.on("end", () => {
|
|
2720
|
+
try {
|
|
2721
|
+
const { nodeId, key } = JSON.parse(body);
|
|
2722
|
+
if (!nodeId || !key) {
|
|
2723
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2724
|
+
res.end(JSON.stringify({ ok: false, error: "nodeId and key are required" }));
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
removeTag(projectRoot, nodeId, key);
|
|
2217
2728
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2218
2729
|
res.end(JSON.stringify({ ok: true }));
|
|
2219
2730
|
} catch (err) {
|
|
@@ -2224,7 +2735,7 @@ async function startChartServer(opts = {}) {
|
|
|
2224
2735
|
return;
|
|
2225
2736
|
}
|
|
2226
2737
|
if (url2.pathname !== "/") {
|
|
2227
|
-
const staticPath =
|
|
2738
|
+
const staticPath = import_node_path15.default.join(clientDir, url2.pathname);
|
|
2228
2739
|
if (serveStatic(res, staticPath)) return;
|
|
2229
2740
|
}
|
|
2230
2741
|
serveIndex(res, clientDir);
|
|
@@ -2233,7 +2744,8 @@ async function startChartServer(opts = {}) {
|
|
|
2233
2744
|
res.end(JSON.stringify({ error: String(err) }));
|
|
2234
2745
|
}
|
|
2235
2746
|
});
|
|
2236
|
-
const
|
|
2747
|
+
const startPort = opts.port ?? randomPort();
|
|
2748
|
+
const port = await bindWithFallback(server, startPort);
|
|
2237
2749
|
const url = `http://localhost:${port}`;
|
|
2238
2750
|
writeLock({
|
|
2239
2751
|
pid: process.pid,
|
|
@@ -2241,9 +2753,9 @@ async function startChartServer(opts = {}) {
|
|
|
2241
2753
|
cwd,
|
|
2242
2754
|
url,
|
|
2243
2755
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2244
|
-
});
|
|
2756
|
+
}, projectRoot);
|
|
2245
2757
|
const cleanup = () => {
|
|
2246
|
-
clearLock();
|
|
2758
|
+
clearLock(projectRoot);
|
|
2247
2759
|
server.close();
|
|
2248
2760
|
};
|
|
2249
2761
|
process.once("SIGINT", () => {
|