@launchsecure/launch-kit 0.0.5 → 0.0.6
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-BN_N_I08.js +379 -0
- package/dist/chart-client/assets/index-DJRXEWQm.css +1 -0
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-CCpAvTkG.css +32 -0
- package/dist/client/assets/{index-DCC--GO-.js → index-DldfczJ1.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/chart-serve.js +589 -99
- package/dist/server/cli.js +599 -124
- package/dist/server/graph-mcp-entry.js +731 -128
- package/package.json +1 -1
- package/dist/chart-client/assets/index-BUih0oqR.js +0 -358
- package/dist/chart-client/assets/index-DFslt72L.css +0 -1
- 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,403 @@ 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
|
+
"app",
|
|
1968
|
+
"all",
|
|
1969
|
+
"ee",
|
|
1970
|
+
"home",
|
|
1971
|
+
"root"
|
|
1972
|
+
]);
|
|
1973
|
+
function isTrivialGroup(name) {
|
|
1974
|
+
if (TRIVIAL_GROUPS.has(name)) return true;
|
|
1975
|
+
const lower = name.toLowerCase();
|
|
1976
|
+
const wrapperPatterns = [
|
|
1977
|
+
/^.*-?wrapper$/,
|
|
1978
|
+
// "page-wrapper", "use-page-wrapper"
|
|
1979
|
+
/^.*-?layout$/,
|
|
1980
|
+
// "admin-layout", "settings-layout"
|
|
1981
|
+
/^use-/,
|
|
1982
|
+
// "use-page-wrapper"
|
|
1983
|
+
/^default$/
|
|
1984
|
+
];
|
|
1985
|
+
return wrapperPatterns.some((p) => p.test(lower));
|
|
1986
|
+
}
|
|
1987
|
+
function normalizeGroupName(name) {
|
|
1988
|
+
return name.replace(/-pages?$/, "").replace(/-layout$/, "").replace(/-wrapper$/, "");
|
|
1989
|
+
}
|
|
1990
|
+
function extractModuleFromPath(id) {
|
|
1991
|
+
const segments = id.split("/");
|
|
1992
|
+
const routeGroups = extractRouteGroups(id);
|
|
1993
|
+
const moduleGroups = routeGroups.filter((g) => !isTrivialGroup(g)).map(normalizeGroupName);
|
|
1994
|
+
if (moduleGroups.length > 0) {
|
|
1995
|
+
return moduleGroups[moduleGroups.length - 1];
|
|
1996
|
+
}
|
|
1997
|
+
const meaningful = [];
|
|
1998
|
+
for (const seg of segments) {
|
|
1999
|
+
if (seg.includes(".")) continue;
|
|
2000
|
+
if (isRouteGroup(seg)) continue;
|
|
2001
|
+
if (isDynamicSegment(seg)) continue;
|
|
2002
|
+
if (isDomainDir(seg)) continue;
|
|
2003
|
+
if (SKIP_SEGMENTS.has(seg)) continue;
|
|
2004
|
+
meaningful.push(seg);
|
|
2005
|
+
}
|
|
2006
|
+
if (meaningful.length > 0) {
|
|
2007
|
+
return meaningful[0];
|
|
2008
|
+
}
|
|
2009
|
+
return "root";
|
|
2010
|
+
}
|
|
2011
|
+
var cachedRootDir = null;
|
|
2012
|
+
var cachedConventionDirs = /* @__PURE__ */ new Map();
|
|
2013
|
+
var moduleTagger = {
|
|
2014
|
+
id: "module",
|
|
2015
|
+
tagKey: "module",
|
|
2016
|
+
trackUntagged: true,
|
|
2017
|
+
layers: null,
|
|
2018
|
+
// applies to all layers
|
|
2019
|
+
tag(nodes, layer, rootDir) {
|
|
2020
|
+
if (cachedRootDir !== rootDir) {
|
|
2021
|
+
cachedConventionDirs = detectConventionDirs(rootDir);
|
|
2022
|
+
cachedRootDir = rootDir;
|
|
2023
|
+
}
|
|
2024
|
+
let configRules = [];
|
|
2025
|
+
try {
|
|
2026
|
+
const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
|
|
2027
|
+
const config = loadConfig2(rootDir);
|
|
2028
|
+
configRules = config.taggers?.module?.rules ?? [];
|
|
2029
|
+
} catch {
|
|
2030
|
+
}
|
|
2031
|
+
const result = /* @__PURE__ */ new Map();
|
|
2032
|
+
for (const node of nodes) {
|
|
2033
|
+
const id = node.id;
|
|
2034
|
+
let matched = false;
|
|
2035
|
+
for (const rule of configRules) {
|
|
2036
|
+
if (matchGlob(rule.match, id)) {
|
|
2037
|
+
result.set(id, rule.module);
|
|
2038
|
+
matched = true;
|
|
2039
|
+
break;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
if (matched) continue;
|
|
2043
|
+
matched = false;
|
|
2044
|
+
for (const [convDir, moduleNames] of cachedConventionDirs) {
|
|
2045
|
+
if (id.startsWith(convDir + "/")) {
|
|
2046
|
+
const rest = id.slice(convDir.length + 1);
|
|
2047
|
+
const firstSeg = rest.split("/")[0];
|
|
2048
|
+
if (moduleNames.includes(firstSeg)) {
|
|
2049
|
+
result.set(id, firstSeg);
|
|
2050
|
+
matched = true;
|
|
2051
|
+
break;
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
if (matched) continue;
|
|
2056
|
+
const module2 = extractModuleFromPath(id);
|
|
2057
|
+
result.set(id, module2);
|
|
2058
|
+
}
|
|
2059
|
+
return result;
|
|
2060
|
+
}
|
|
2061
|
+
};
|
|
2062
|
+
|
|
2063
|
+
// src/server/graph/taggers/screen-tagger.ts
|
|
2064
|
+
var SCREEN_TYPES = /* @__PURE__ */ new Set(["page", "layout"]);
|
|
2065
|
+
var screenTagger = {
|
|
2066
|
+
id: "screen",
|
|
2067
|
+
tagKey: "screen",
|
|
2068
|
+
trackUntagged: true,
|
|
2069
|
+
layers: ["ui"],
|
|
2070
|
+
tag(nodes, layer) {
|
|
2071
|
+
if (layer !== "ui") return /* @__PURE__ */ new Map();
|
|
2072
|
+
const result = /* @__PURE__ */ new Map();
|
|
2073
|
+
for (const node of nodes) {
|
|
2074
|
+
if (SCREEN_TYPES.has(node.type)) {
|
|
2075
|
+
result.set(node.id, "true");
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
return result;
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
|
|
2082
|
+
// src/server/graph/core/tagger-registry.ts
|
|
2083
|
+
var TaggerRegistry = class {
|
|
2084
|
+
constructor() {
|
|
2085
|
+
this.taggers = [];
|
|
2086
|
+
this.ids = /* @__PURE__ */ new Set();
|
|
2087
|
+
}
|
|
2088
|
+
register(tagger) {
|
|
2089
|
+
if (this.ids.has(tagger.id)) {
|
|
2090
|
+
throw new Error(`Duplicate tagger id: ${tagger.id}`);
|
|
2091
|
+
}
|
|
2092
|
+
this.ids.add(tagger.id);
|
|
2093
|
+
this.taggers.push(tagger);
|
|
2094
|
+
}
|
|
2095
|
+
getAll() {
|
|
2096
|
+
return this.taggers;
|
|
2097
|
+
}
|
|
2098
|
+
getForLayer(layer) {
|
|
2099
|
+
return this.taggers.filter((t) => t.layers === null || t.layers.includes(layer));
|
|
2100
|
+
}
|
|
2101
|
+
};
|
|
2102
|
+
var BUILTIN_TAGGERS = [moduleTagger, screenTagger];
|
|
2103
|
+
function registerBuiltins2(registry, disabled, config) {
|
|
2104
|
+
for (const tagger of BUILTIN_TAGGERS) {
|
|
2105
|
+
if (disabled.has(tagger.id)) continue;
|
|
2106
|
+
const override = config.taggers?.trackUntagged?.[tagger.id];
|
|
2107
|
+
if (override !== void 0) {
|
|
2108
|
+
tagger.trackUntagged = override;
|
|
2109
|
+
}
|
|
2110
|
+
registry.register(tagger);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
function loadCustomTaggers(registry, config, rootDir, disabled) {
|
|
2114
|
+
for (const entry of config.taggers?.custom ?? []) {
|
|
2115
|
+
if (disabled.has(entry.id)) continue;
|
|
2116
|
+
try {
|
|
2117
|
+
const absPath = (0, import_node_path11.resolve)(rootDir, entry.path);
|
|
2118
|
+
const mod = require(absPath);
|
|
2119
|
+
const tagger = "default" in mod ? mod.default : mod;
|
|
2120
|
+
const override = config.taggers?.trackUntagged?.[tagger.id];
|
|
2121
|
+
if (override !== void 0) {
|
|
2122
|
+
tagger.trackUntagged = override;
|
|
2123
|
+
}
|
|
2124
|
+
registry.register(tagger);
|
|
2125
|
+
} catch (err) {
|
|
2126
|
+
process.stderr.write(`[launch-chart] failed to load custom tagger from ${entry.path}: ${err}
|
|
2127
|
+
`);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
function createTaggerRegistry(config, rootDir) {
|
|
2132
|
+
const registry = new TaggerRegistry();
|
|
2133
|
+
const disabled = new Set(config.taggers?.disabled ?? []);
|
|
2134
|
+
registerBuiltins2(registry, disabled, config);
|
|
2135
|
+
loadCustomTaggers(registry, config, rootDir, disabled);
|
|
2136
|
+
return registry;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
// src/server/graph/core/tag-store.ts
|
|
2140
|
+
var import_node_fs10 = require("node:fs");
|
|
2141
|
+
var import_node_path12 = require("node:path");
|
|
2142
|
+
var TAGS_FILENAME = "tags.json";
|
|
1895
2143
|
var GRAPHS_DIR = ".launchsecure/graphs";
|
|
2144
|
+
var tagCache = /* @__PURE__ */ new Map();
|
|
2145
|
+
function tagsFilePath(rootDir) {
|
|
2146
|
+
return (0, import_node_path12.join)(rootDir, GRAPHS_DIR, TAGS_FILENAME);
|
|
2147
|
+
}
|
|
2148
|
+
function readTagStore(rootDir) {
|
|
2149
|
+
const filePath = tagsFilePath(rootDir);
|
|
2150
|
+
if (!(0, import_node_fs10.existsSync)(filePath)) return {};
|
|
2151
|
+
const stat = (0, import_node_fs10.statSync)(filePath);
|
|
2152
|
+
const cached = tagCache.get(filePath);
|
|
2153
|
+
if (cached && cached.mtimeMs === stat.mtimeMs) {
|
|
2154
|
+
return cached.store;
|
|
2155
|
+
}
|
|
2156
|
+
try {
|
|
2157
|
+
const content = (0, import_node_fs10.readFileSync)(filePath, "utf-8");
|
|
2158
|
+
const store = JSON.parse(content);
|
|
2159
|
+
tagCache.set(filePath, { mtimeMs: stat.mtimeMs, store });
|
|
2160
|
+
return store;
|
|
2161
|
+
} catch {
|
|
2162
|
+
return {};
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
function writeTagStore(rootDir, store) {
|
|
2166
|
+
const filePath = tagsFilePath(rootDir);
|
|
2167
|
+
const dir = (0, import_node_path12.dirname)(filePath);
|
|
2168
|
+
(0, import_node_fs10.mkdirSync)(dir, { recursive: true });
|
|
2169
|
+
const cleaned = {};
|
|
2170
|
+
for (const [nodeId, tags] of Object.entries(store)) {
|
|
2171
|
+
if (Object.keys(tags).length > 0) {
|
|
2172
|
+
cleaned[nodeId] = tags;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
(0, import_node_fs10.writeFileSync)(filePath, JSON.stringify(cleaned, null, 2) + "\n", "utf-8");
|
|
2176
|
+
tagCache.delete(filePath);
|
|
2177
|
+
}
|
|
2178
|
+
function setTag(rootDir, nodeId, key, value) {
|
|
2179
|
+
const store = readTagStore(rootDir);
|
|
2180
|
+
if (!store[nodeId]) store[nodeId] = {};
|
|
2181
|
+
store[nodeId][key] = value;
|
|
2182
|
+
writeTagStore(rootDir, store);
|
|
2183
|
+
}
|
|
2184
|
+
function removeTag(rootDir, nodeId, key) {
|
|
2185
|
+
const store = readTagStore(rootDir);
|
|
2186
|
+
if (!store[nodeId]) return;
|
|
2187
|
+
delete store[nodeId][key];
|
|
2188
|
+
if (Object.keys(store[nodeId]).length === 0) {
|
|
2189
|
+
delete store[nodeId];
|
|
2190
|
+
}
|
|
2191
|
+
writeTagStore(rootDir, store);
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
// src/server/graph/index.ts
|
|
2195
|
+
var GRAPHS_DIR2 = ".launchsecure/graphs";
|
|
1896
2196
|
var LAYERS = ["ui", "api", "db"];
|
|
1897
2197
|
var graphCache = /* @__PURE__ */ new Map();
|
|
2198
|
+
var taggedCache = /* @__PURE__ */ new Map();
|
|
1898
2199
|
function graphsDir(rootDir) {
|
|
1899
|
-
return (0,
|
|
2200
|
+
return (0, import_node_path13.join)(rootDir, GRAPHS_DIR2);
|
|
1900
2201
|
}
|
|
1901
2202
|
function graphFilePath(rootDir, layer) {
|
|
1902
|
-
return (0,
|
|
2203
|
+
return (0, import_node_path13.join)(graphsDir(rootDir), `${layer}.json`);
|
|
2204
|
+
}
|
|
2205
|
+
function tagsFilePath2(rootDir) {
|
|
2206
|
+
return (0, import_node_path13.join)(graphsDir(rootDir), "tags.json");
|
|
2207
|
+
}
|
|
2208
|
+
function getMtimeMs(filePath) {
|
|
2209
|
+
if (!(0, import_node_fs11.existsSync)(filePath)) return 0;
|
|
2210
|
+
return (0, import_node_fs11.statSync)(filePath).mtimeMs;
|
|
1903
2211
|
}
|
|
1904
2212
|
function invalidateCache(filePath) {
|
|
1905
2213
|
graphCache.delete(filePath);
|
|
1906
2214
|
}
|
|
1907
|
-
function
|
|
2215
|
+
function invalidateTaggedCache(rootDir, layer) {
|
|
2216
|
+
taggedCache.delete(`${rootDir}:${layer}`);
|
|
2217
|
+
}
|
|
2218
|
+
function applyTags(graph, layer, rootDir) {
|
|
2219
|
+
const config = loadConfig(rootDir);
|
|
2220
|
+
const registry = createTaggerRegistry(config, rootDir);
|
|
2221
|
+
const manualTags = readTagStore(rootDir);
|
|
2222
|
+
const taggedNodes = graph.nodes.map((n) => ({ ...n }));
|
|
2223
|
+
const taggers = registry.getForLayer(layer);
|
|
2224
|
+
for (const tagger of taggers) {
|
|
2225
|
+
const assignments = tagger.tag(taggedNodes, layer, rootDir);
|
|
2226
|
+
for (const node of taggedNodes) {
|
|
2227
|
+
if (!node.tags) node.tags = {};
|
|
2228
|
+
const tags = node.tags;
|
|
2229
|
+
const value = assignments.get(node.id);
|
|
2230
|
+
if (value !== void 0) {
|
|
2231
|
+
tags[tagger.tagKey] = value;
|
|
2232
|
+
} else if (tagger.trackUntagged) {
|
|
2233
|
+
tags[tagger.tagKey] = "untagged";
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
for (const node of taggedNodes) {
|
|
2238
|
+
const manual = manualTags[node.id];
|
|
2239
|
+
if (manual) {
|
|
2240
|
+
if (!node.tags) node.tags = {};
|
|
2241
|
+
const tags = node.tags;
|
|
2242
|
+
Object.assign(tags, manual);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
return { ...graph, nodes: taggedNodes };
|
|
2246
|
+
}
|
|
2247
|
+
function readGraphRaw(rootDir, layer) {
|
|
1908
2248
|
const filePath = graphFilePath(rootDir, layer);
|
|
1909
|
-
if (!(0,
|
|
1910
|
-
const stat = (0,
|
|
2249
|
+
if (!(0, import_node_fs11.existsSync)(filePath)) return null;
|
|
2250
|
+
const stat = (0, import_node_fs11.statSync)(filePath);
|
|
1911
2251
|
const cached = graphCache.get(filePath);
|
|
1912
2252
|
if (cached && cached.mtimeMs === stat.mtimeMs) {
|
|
1913
2253
|
return cached.graph;
|
|
1914
2254
|
}
|
|
1915
|
-
const content = (0,
|
|
2255
|
+
const content = (0, import_node_fs11.readFileSync)(filePath, "utf-8");
|
|
1916
2256
|
const graph = JSON.parse(content);
|
|
1917
2257
|
graphCache.set(filePath, { mtimeMs: stat.mtimeMs, graph });
|
|
1918
2258
|
return graph;
|
|
1919
2259
|
}
|
|
2260
|
+
function readGraph(rootDir, layer) {
|
|
2261
|
+
const rawFilePath = graphFilePath(rootDir, layer);
|
|
2262
|
+
if (!(0, import_node_fs11.existsSync)(rawFilePath)) return null;
|
|
2263
|
+
const rawMtime = getMtimeMs(rawFilePath);
|
|
2264
|
+
const tagsMtime = getMtimeMs(tagsFilePath2(rootDir));
|
|
2265
|
+
const cacheKey = `${rootDir}:${layer}`;
|
|
2266
|
+
const cached = taggedCache.get(cacheKey);
|
|
2267
|
+
if (cached && cached.rawMtimeMs === rawMtime && cached.tagsMtimeMs === tagsMtime) {
|
|
2268
|
+
return cached.graph;
|
|
2269
|
+
}
|
|
2270
|
+
const raw = readGraphRaw(rootDir, layer);
|
|
2271
|
+
if (!raw) return null;
|
|
2272
|
+
const tagged = applyTags(raw, layer, rootDir);
|
|
2273
|
+
taggedCache.set(cacheKey, { rawMtimeMs: rawMtime, tagsMtimeMs: tagsMtime, graph: tagged });
|
|
2274
|
+
return tagged;
|
|
2275
|
+
}
|
|
1920
2276
|
function readAllGraphs(rootDir) {
|
|
1921
2277
|
const result = {};
|
|
1922
2278
|
for (const layer of LAYERS) {
|
|
@@ -1927,32 +2283,52 @@ function readAllGraphs(rootDir) {
|
|
|
1927
2283
|
}
|
|
1928
2284
|
function generateGraph(rootDir, layer) {
|
|
1929
2285
|
const dir = graphsDir(rootDir);
|
|
1930
|
-
(0,
|
|
2286
|
+
(0, import_node_fs11.mkdirSync)(dir, { recursive: true });
|
|
1931
2287
|
const results = layer ? [generateLayer(rootDir, layer)].filter((r) => r !== null) : generateAll(rootDir);
|
|
1932
2288
|
for (const result of results) {
|
|
1933
2289
|
const filePath = graphFilePath(rootDir, result.layer);
|
|
1934
|
-
(0,
|
|
2290
|
+
(0, import_node_fs11.writeFileSync)(filePath, JSON.stringify(result.output, null, 2) + "\n", "utf-8");
|
|
1935
2291
|
invalidateCache(filePath);
|
|
2292
|
+
invalidateTaggedCache(rootDir, result.layer);
|
|
1936
2293
|
}
|
|
1937
2294
|
return results;
|
|
1938
2295
|
}
|
|
1939
2296
|
|
|
1940
2297
|
// src/server/lockfile.ts
|
|
1941
2298
|
var import_node_child_process = require("node:child_process");
|
|
1942
|
-
var
|
|
2299
|
+
var import_node_fs12 = require("node:fs");
|
|
1943
2300
|
var import_node_os = require("node:os");
|
|
1944
|
-
var
|
|
1945
|
-
function lockDir() {
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
return (0,
|
|
1950
|
-
}
|
|
1951
|
-
function
|
|
1952
|
-
|
|
1953
|
-
|
|
2301
|
+
var import_node_path14 = require("node:path");
|
|
2302
|
+
function lockDir(projectRoot) {
|
|
2303
|
+
if (projectRoot) {
|
|
2304
|
+
return (0, import_node_path14.join)(projectRoot, ".launchsecure");
|
|
2305
|
+
}
|
|
2306
|
+
return (0, import_node_path14.join)((0, import_node_os.homedir)(), ".launchsecure");
|
|
2307
|
+
}
|
|
2308
|
+
function lockPath(projectRoot) {
|
|
2309
|
+
return (0, import_node_path14.join)(lockDir(projectRoot), "launch-chart.lock");
|
|
2310
|
+
}
|
|
2311
|
+
var _activeProjectRoot;
|
|
2312
|
+
function readLock(projectRoot) {
|
|
2313
|
+
const root = projectRoot ?? _activeProjectRoot;
|
|
2314
|
+
const p = lockPath(root);
|
|
2315
|
+
if (!(0, import_node_fs12.existsSync)(p)) {
|
|
2316
|
+
if (root) {
|
|
2317
|
+
const globalP = lockPath();
|
|
2318
|
+
if ((0, import_node_fs12.existsSync)(globalP)) {
|
|
2319
|
+
try {
|
|
2320
|
+
const data = JSON.parse((0, import_node_fs12.readFileSync)(globalP, "utf-8"));
|
|
2321
|
+
if (typeof data.pid === "number" && typeof data.port === "number" && data.cwd === root) {
|
|
2322
|
+
return data;
|
|
2323
|
+
}
|
|
2324
|
+
} catch {
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
1954
2330
|
try {
|
|
1955
|
-
const data = JSON.parse((0,
|
|
2331
|
+
const data = JSON.parse((0, import_node_fs12.readFileSync)(p, "utf-8"));
|
|
1956
2332
|
if (typeof data.pid !== "number" || typeof data.port !== "number") return null;
|
|
1957
2333
|
return data;
|
|
1958
2334
|
} catch {
|
|
@@ -1981,34 +2357,41 @@ function getListenerPid(port) {
|
|
|
1981
2357
|
return null;
|
|
1982
2358
|
}
|
|
1983
2359
|
}
|
|
1984
|
-
function getLiveLock() {
|
|
1985
|
-
const
|
|
2360
|
+
function getLiveLock(projectRoot) {
|
|
2361
|
+
const root = projectRoot ?? _activeProjectRoot;
|
|
2362
|
+
const lock = readLock(root);
|
|
1986
2363
|
if (!lock) return null;
|
|
1987
2364
|
const listenerPid = getListenerPid(lock.port);
|
|
1988
2365
|
const live = listenerPid !== null ? listenerPid === lock.pid : isPidAlive(lock.pid);
|
|
1989
2366
|
if (!live) {
|
|
1990
2367
|
try {
|
|
1991
|
-
(0,
|
|
2368
|
+
(0, import_node_fs12.unlinkSync)(lockPath(root));
|
|
1992
2369
|
} catch {
|
|
1993
2370
|
}
|
|
1994
2371
|
return null;
|
|
1995
2372
|
}
|
|
1996
2373
|
return lock;
|
|
1997
2374
|
}
|
|
1998
|
-
function writeLock(data) {
|
|
1999
|
-
|
|
2000
|
-
(0,
|
|
2375
|
+
function writeLock(data, projectRoot) {
|
|
2376
|
+
const root = projectRoot ?? _activeProjectRoot;
|
|
2377
|
+
(0, import_node_fs12.mkdirSync)(lockDir(root), { recursive: true });
|
|
2378
|
+
(0, import_node_fs12.writeFileSync)(lockPath(root), JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
2379
|
+
if (root) _activeProjectRoot = root;
|
|
2001
2380
|
}
|
|
2002
|
-
function clearLock() {
|
|
2381
|
+
function clearLock(projectRoot) {
|
|
2382
|
+
const root = projectRoot ?? _activeProjectRoot;
|
|
2003
2383
|
try {
|
|
2004
|
-
(0,
|
|
2384
|
+
(0, import_node_fs12.unlinkSync)(lockPath(root));
|
|
2005
2385
|
} catch {
|
|
2006
2386
|
}
|
|
2007
2387
|
}
|
|
2008
2388
|
|
|
2009
2389
|
// src/server/chart-serve.ts
|
|
2010
|
-
|
|
2011
|
-
var MAX_PORT_SCAN =
|
|
2390
|
+
init_config();
|
|
2391
|
+
var MAX_PORT_SCAN = 3;
|
|
2392
|
+
function randomPort() {
|
|
2393
|
+
return 49152 + Math.floor(Math.random() * (65535 - 49152));
|
|
2394
|
+
}
|
|
2012
2395
|
var MIME_TYPES = {
|
|
2013
2396
|
".html": "text/html; charset=utf-8",
|
|
2014
2397
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -2023,16 +2406,16 @@ var MIME_TYPES = {
|
|
|
2023
2406
|
function findProjectRoot(startDir) {
|
|
2024
2407
|
let dir = startDir;
|
|
2025
2408
|
for (let i = 0; i < 8; i++) {
|
|
2026
|
-
const graphsDir2 =
|
|
2027
|
-
if (
|
|
2028
|
-
const parent =
|
|
2409
|
+
const graphsDir2 = import_node_path15.default.join(dir, ".launchsecure", "graphs");
|
|
2410
|
+
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;
|
|
2411
|
+
const parent = import_node_path15.default.dirname(dir);
|
|
2029
2412
|
if (parent === dir) break;
|
|
2030
2413
|
dir = parent;
|
|
2031
2414
|
}
|
|
2032
2415
|
dir = startDir;
|
|
2033
2416
|
for (let i = 0; i < 8; i++) {
|
|
2034
|
-
if (
|
|
2035
|
-
const parent =
|
|
2417
|
+
if (import_node_fs13.default.existsSync(import_node_path15.default.join(dir, ".git"))) return dir;
|
|
2418
|
+
const parent = import_node_path15.default.dirname(dir);
|
|
2036
2419
|
if (parent === dir) break;
|
|
2037
2420
|
dir = parent;
|
|
2038
2421
|
}
|
|
@@ -2084,16 +2467,16 @@ function buildMergedGraph(projectRoot) {
|
|
|
2084
2467
|
};
|
|
2085
2468
|
}
|
|
2086
2469
|
function serveStatic(res, filePath) {
|
|
2087
|
-
if (!
|
|
2088
|
-
const ext =
|
|
2470
|
+
if (!import_node_fs13.default.existsSync(filePath) || !import_node_fs13.default.statSync(filePath).isFile()) return false;
|
|
2471
|
+
const ext = import_node_path15.default.extname(filePath).toLowerCase();
|
|
2089
2472
|
const mime = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
2090
2473
|
res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
|
|
2091
|
-
|
|
2474
|
+
import_node_fs13.default.createReadStream(filePath).pipe(res);
|
|
2092
2475
|
return true;
|
|
2093
2476
|
}
|
|
2094
2477
|
function serveIndex(res, clientDir) {
|
|
2095
|
-
const indexPath =
|
|
2096
|
-
if (!
|
|
2478
|
+
const indexPath = import_node_path15.default.join(clientDir, "index.html");
|
|
2479
|
+
if (!import_node_fs13.default.existsSync(indexPath)) {
|
|
2097
2480
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
2098
2481
|
res.end(`LaunchChart client bundle not found at ${clientDir}. Run 'npm run build:chart-client'.`);
|
|
2099
2482
|
return;
|
|
@@ -2101,14 +2484,14 @@ function serveIndex(res, clientDir) {
|
|
|
2101
2484
|
serveStatic(res, indexPath);
|
|
2102
2485
|
}
|
|
2103
2486
|
function tryListen(server, port) {
|
|
2104
|
-
return new Promise((
|
|
2487
|
+
return new Promise((resolve3, reject) => {
|
|
2105
2488
|
const onError = (err) => {
|
|
2106
2489
|
server.off("listening", onListening);
|
|
2107
2490
|
reject(err);
|
|
2108
2491
|
};
|
|
2109
2492
|
const onListening = () => {
|
|
2110
2493
|
server.off("error", onError);
|
|
2111
|
-
|
|
2494
|
+
resolve3(port);
|
|
2112
2495
|
};
|
|
2113
2496
|
server.once("error", onError);
|
|
2114
2497
|
server.once("listening", onListening);
|
|
@@ -2135,7 +2518,7 @@ async function bindWithFallback(server, startPort) {
|
|
|
2135
2518
|
async function startChartServer(opts = {}) {
|
|
2136
2519
|
const cwd = opts.cwd ?? process.cwd();
|
|
2137
2520
|
const projectRoot = findProjectRoot(cwd);
|
|
2138
|
-
const existing = getLiveLock();
|
|
2521
|
+
const existing = getLiveLock(projectRoot);
|
|
2139
2522
|
if (existing) {
|
|
2140
2523
|
if (!opts.quiet) {
|
|
2141
2524
|
process.stderr.write(
|
|
@@ -2145,7 +2528,7 @@ async function startChartServer(opts = {}) {
|
|
|
2145
2528
|
}
|
|
2146
2529
|
return { port: existing.port, url: existing.url };
|
|
2147
2530
|
}
|
|
2148
|
-
const clientDir = opts.clientDir ??
|
|
2531
|
+
const clientDir = opts.clientDir ?? import_node_path15.default.join(__dirname, "..", "chart-client");
|
|
2149
2532
|
const server = import_node_http.default.createServer((req, res) => {
|
|
2150
2533
|
try {
|
|
2151
2534
|
const url2 = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
@@ -2183,6 +2566,26 @@ async function startChartServer(opts = {}) {
|
|
|
2183
2566
|
}
|
|
2184
2567
|
return;
|
|
2185
2568
|
}
|
|
2569
|
+
if (req.method === "GET" && url2.pathname === "/api/file-content") {
|
|
2570
|
+
const relPath = url2.searchParams.get("path");
|
|
2571
|
+
if (!relPath || relPath.includes("..") || import_node_path15.default.isAbsolute(relPath)) {
|
|
2572
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2573
|
+
res.end(JSON.stringify({ error: "Invalid path" }));
|
|
2574
|
+
return;
|
|
2575
|
+
}
|
|
2576
|
+
const filePath = import_node_path15.default.join(projectRoot, relPath);
|
|
2577
|
+
if (!filePath.startsWith(projectRoot) || !import_node_fs13.default.existsSync(filePath) || !import_node_fs13.default.statSync(filePath).isFile()) {
|
|
2578
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
2579
|
+
res.end(JSON.stringify({ error: "File not found" }));
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2582
|
+
const ext = import_node_path15.default.extname(filePath).toLowerCase();
|
|
2583
|
+
const langMap = { ".ts": "typescript", ".tsx": "tsx", ".js": "javascript", ".jsx": "jsx", ".prisma": "prisma", ".json": "json", ".css": "css" };
|
|
2584
|
+
const content = import_node_fs13.default.readFileSync(filePath, "utf-8");
|
|
2585
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2586
|
+
res.end(JSON.stringify({ content, language: langMap[ext] ?? "text", path: relPath }));
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2186
2589
|
if (req.method === "GET" && url2.pathname === "/api/health") {
|
|
2187
2590
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2188
2591
|
res.end(JSON.stringify({ ok: true, projectRoot }));
|
|
@@ -2212,8 +2615,94 @@ async function startChartServer(opts = {}) {
|
|
|
2212
2615
|
req.on("end", () => {
|
|
2213
2616
|
try {
|
|
2214
2617
|
const newConfig = JSON.parse(body);
|
|
2215
|
-
const configPath =
|
|
2216
|
-
|
|
2618
|
+
const configPath = import_node_path15.default.join(projectRoot, ".launchchart.json");
|
|
2619
|
+
import_node_fs13.default.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
|
|
2620
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2621
|
+
res.end(JSON.stringify({ ok: true }));
|
|
2622
|
+
} catch (err) {
|
|
2623
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2624
|
+
res.end(JSON.stringify({ ok: false, error: String(err) }));
|
|
2625
|
+
}
|
|
2626
|
+
});
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
if (req.method === "GET" && url2.pathname === "/api/tagger-config") {
|
|
2630
|
+
const config = loadConfig(projectRoot);
|
|
2631
|
+
const builtinTaggers = [
|
|
2632
|
+
{ id: "module", tagKey: "module", trackUntagged: config.taggers?.trackUntagged?.module ?? true },
|
|
2633
|
+
{ id: "screen", tagKey: "screen", trackUntagged: config.taggers?.trackUntagged?.screen ?? true }
|
|
2634
|
+
];
|
|
2635
|
+
const disabled = config.taggers?.disabled ?? [];
|
|
2636
|
+
const customTaggers = config.taggers?.custom ?? [];
|
|
2637
|
+
const moduleRules = config.taggers?.module?.rules ?? [];
|
|
2638
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2639
|
+
res.end(JSON.stringify({ builtinTaggers, disabled, customTaggers, moduleRules }));
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
if (req.method === "POST" && url2.pathname === "/api/tagger-config") {
|
|
2643
|
+
let body = "";
|
|
2644
|
+
req.on("data", (chunk) => {
|
|
2645
|
+
body += chunk.toString();
|
|
2646
|
+
});
|
|
2647
|
+
req.on("end", () => {
|
|
2648
|
+
try {
|
|
2649
|
+
const taggerConfig = JSON.parse(body);
|
|
2650
|
+
const config = loadConfig(projectRoot);
|
|
2651
|
+
config.taggers = taggerConfig;
|
|
2652
|
+
const configPath = import_node_path15.default.join(projectRoot, ".launchchart.json");
|
|
2653
|
+
import_node_fs13.default.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2654
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2655
|
+
res.end(JSON.stringify({ ok: true }));
|
|
2656
|
+
} catch (err) {
|
|
2657
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2658
|
+
res.end(JSON.stringify({ ok: false, error: String(err) }));
|
|
2659
|
+
}
|
|
2660
|
+
});
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2663
|
+
if (req.method === "GET" && url2.pathname === "/api/tags") {
|
|
2664
|
+
const store = readTagStore(projectRoot);
|
|
2665
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2666
|
+
res.end(JSON.stringify(store));
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2669
|
+
if (req.method === "POST" && url2.pathname === "/api/tags") {
|
|
2670
|
+
let body = "";
|
|
2671
|
+
req.on("data", (chunk) => {
|
|
2672
|
+
body += chunk.toString();
|
|
2673
|
+
});
|
|
2674
|
+
req.on("end", () => {
|
|
2675
|
+
try {
|
|
2676
|
+
const { nodeId, key, value } = JSON.parse(body);
|
|
2677
|
+
if (!nodeId || !key || !value) {
|
|
2678
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2679
|
+
res.end(JSON.stringify({ ok: false, error: "nodeId, key, and value are required" }));
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
setTag(projectRoot, nodeId, key, value);
|
|
2683
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2684
|
+
res.end(JSON.stringify({ ok: true }));
|
|
2685
|
+
} catch (err) {
|
|
2686
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2687
|
+
res.end(JSON.stringify({ ok: false, error: String(err) }));
|
|
2688
|
+
}
|
|
2689
|
+
});
|
|
2690
|
+
return;
|
|
2691
|
+
}
|
|
2692
|
+
if (req.method === "DELETE" && url2.pathname === "/api/tags") {
|
|
2693
|
+
let body = "";
|
|
2694
|
+
req.on("data", (chunk) => {
|
|
2695
|
+
body += chunk.toString();
|
|
2696
|
+
});
|
|
2697
|
+
req.on("end", () => {
|
|
2698
|
+
try {
|
|
2699
|
+
const { nodeId, key } = JSON.parse(body);
|
|
2700
|
+
if (!nodeId || !key) {
|
|
2701
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2702
|
+
res.end(JSON.stringify({ ok: false, error: "nodeId and key are required" }));
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
removeTag(projectRoot, nodeId, key);
|
|
2217
2706
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2218
2707
|
res.end(JSON.stringify({ ok: true }));
|
|
2219
2708
|
} catch (err) {
|
|
@@ -2224,7 +2713,7 @@ async function startChartServer(opts = {}) {
|
|
|
2224
2713
|
return;
|
|
2225
2714
|
}
|
|
2226
2715
|
if (url2.pathname !== "/") {
|
|
2227
|
-
const staticPath =
|
|
2716
|
+
const staticPath = import_node_path15.default.join(clientDir, url2.pathname);
|
|
2228
2717
|
if (serveStatic(res, staticPath)) return;
|
|
2229
2718
|
}
|
|
2230
2719
|
serveIndex(res, clientDir);
|
|
@@ -2233,7 +2722,8 @@ async function startChartServer(opts = {}) {
|
|
|
2233
2722
|
res.end(JSON.stringify({ error: String(err) }));
|
|
2234
2723
|
}
|
|
2235
2724
|
});
|
|
2236
|
-
const
|
|
2725
|
+
const startPort = opts.port ?? randomPort();
|
|
2726
|
+
const port = await bindWithFallback(server, startPort);
|
|
2237
2727
|
const url = `http://localhost:${port}`;
|
|
2238
2728
|
writeLock({
|
|
2239
2729
|
pid: process.pid,
|
|
@@ -2241,9 +2731,9 @@ async function startChartServer(opts = {}) {
|
|
|
2241
2731
|
cwd,
|
|
2242
2732
|
url,
|
|
2243
2733
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2244
|
-
});
|
|
2734
|
+
}, projectRoot);
|
|
2245
2735
|
const cleanup = () => {
|
|
2246
|
-
clearLock();
|
|
2736
|
+
clearLock(projectRoot);
|
|
2247
2737
|
server.close();
|
|
2248
2738
|
};
|
|
2249
2739
|
process.once("SIGINT", () => {
|