modviz 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +261 -0
  2. package/dist/client/_shell.html +0 -0
  3. package/dist/client/android-chrome-192x192.png +0 -0
  4. package/dist/client/android-chrome-512x512.png +0 -0
  5. package/dist/client/apple-touch-icon.png +0 -0
  6. package/dist/client/assets/app-Sjrldkrg.css +2 -0
  7. package/dist/client/assets/button-aOWckyNs.js +1 -0
  8. package/dist/client/assets/check-C0EQe2S8.js +1 -0
  9. package/dist/client/assets/chevron-down-DrspihmT.js +1 -0
  10. package/dist/client/assets/chevron-right-DIJHr8AN.js +1 -0
  11. package/dist/client/assets/colors-CQoWjU5E.js +1 -0
  12. package/dist/client/assets/command-kkF7_wdz.js +45 -0
  13. package/dist/client/assets/compare-K6jVFsiI.js +1 -0
  14. package/dist/client/assets/compare-TOnoe1EP.js +2 -0
  15. package/dist/client/assets/configure-DnlSnhtN.js +1 -0
  16. package/dist/client/assets/explorer-C7NclVKg.js +2 -0
  17. package/dist/client/assets/explorer-Xu2X6XXF.js +1 -0
  18. package/dist/client/assets/external-link-B9eNA-li.js +1 -0
  19. package/dist/client/assets/flamegraph-CRVZSAlj.js +13 -0
  20. package/dist/client/assets/floating-ui.dom-DLIT5tPE.js +1 -0
  21. package/dist/client/assets/formatting-CiC0SYI8.js +1 -0
  22. package/dist/client/assets/graph-6Vr74V1k.js +2 -0
  23. package/dist/client/assets/graph-CVzypIGU.js +2 -0
  24. package/dist/client/assets/graph-command-menu-D2MoVT2B.js +4 -0
  25. package/dist/client/assets/graph-command-menu-VWiiW3qy.css +1 -0
  26. package/dist/client/assets/hierarchy-C8xxGb_u.js +2 -0
  27. package/dist/client/assets/hierarchy-iO7d4oSK.js +2 -0
  28. package/dist/client/assets/import-display-D-jRyyjM.js +5 -0
  29. package/dist/client/assets/imports-CPggnrs-.js +2 -0
  30. package/dist/client/assets/imports-CodbPyUJ.js +1 -0
  31. package/dist/client/assets/index-Dj_rhLdR.js +12 -0
  32. package/dist/client/assets/input-BCFMF0aR.js +1 -0
  33. package/dist/client/assets/jsx-runtime-DWSWI4JT.js +1 -0
  34. package/dist/client/assets/lazyRouteComponent-PTSyFp1J.js +1 -0
  35. package/dist/client/assets/loading-state-CyC_hrTF.js +1 -0
  36. package/dist/client/assets/modviz-data-BiRqoDI5.js +1 -0
  37. package/dist/client/assets/modviz-layout-Do93E-IB.js +1 -0
  38. package/dist/client/assets/modviz-sigma-Xl8qHaxK.js +312 -0
  39. package/dist/client/assets/portal-BgAm3V3j.js +1 -0
  40. package/dist/client/assets/routes-DBtN8hrZ.js +1 -0
  41. package/dist/client/assets/schemas-B4zfTepZ.js +39 -0
  42. package/dist/client/assets/search-BYHxNrYn.js +1 -0
  43. package/dist/client/assets/search-params-BaZRBvGI.js +1 -0
  44. package/dist/client/assets/setup-view-j1o0TuZz.js +1 -0
  45. package/dist/client/assets/summary-D703Zh3x.js +1 -0
  46. package/dist/client/assets/tooltip-B1VDU9HG.js +1 -0
  47. package/dist/client/assets/trace-B67CM5s2.js +2 -0
  48. package/dist/client/assets/trace-Bwwdw3AM.js +1 -0
  49. package/dist/client/assets/treemap-BZf2shzY.js +5 -0
  50. package/dist/client/assets/treemap-Csroy8Gy.js +2 -0
  51. package/dist/client/assets/utils-DkkZd0ys.js +1 -0
  52. package/dist/client/favicon-16x16.png +0 -0
  53. package/dist/client/favicon-32x32.png +0 -0
  54. package/dist/client/favicon.ico +0 -0
  55. package/dist/client/favicon.png +0 -0
  56. package/dist/client/site.webmanifest +19 -0
  57. package/dist/mod/cli-options.js +225 -0
  58. package/dist/mod/cli.js +519 -0
  59. package/dist/mod/index.js +3 -0
  60. package/dist/mod/llm-analysis.js +29 -0
  61. package/dist/mod/llm-output.js +742 -0
  62. package/dist/mod/module-graph-plugins.js +60 -0
  63. package/dist/mod/production-server.js +103 -0
  64. package/dist/mod/runtime-host.js +217 -0
  65. package/dist/mod/snapshot-history.js +73 -0
  66. package/dist/mod/types.js +3 -0
  67. package/dist/server/assets/__23tanstack-start-plugin-adapters-3QxJs4a0.js +5 -0
  68. package/dist/server/assets/_tanstack-start-manifest_v-DMytuIue.js +188 -0
  69. package/dist/server/assets/button-Bqnnid5i.js +41 -0
  70. package/dist/server/assets/colors-DhAxrYua.js +100 -0
  71. package/dist/server/assets/command-SdxShIbL.js +138 -0
  72. package/dist/server/assets/compare-BFMiiUsB.js +562 -0
  73. package/dist/server/assets/compare-CpOqTpYu.js +10 -0
  74. package/dist/server/assets/configure-Bvd45DTI.js +288 -0
  75. package/dist/server/assets/explorer-C7dODpSv.js +379 -0
  76. package/dist/server/assets/explorer-CpSb0JTa.js +20 -0
  77. package/dist/server/assets/flamegraph-CdW-VG6I.js +198 -0
  78. package/dist/server/assets/formatting-iDlL4tA-.js +4 -0
  79. package/dist/server/assets/graph-C1G9H5O4.js +438 -0
  80. package/dist/server/assets/graph-DAGFGioS.js +45 -0
  81. package/dist/server/assets/graph-command-menu-BV5GtOWx.js +249 -0
  82. package/dist/server/assets/hierarchy-B4K-Zfn9.js +16 -0
  83. package/dist/server/assets/hierarchy-BGpWSG-f.js +104 -0
  84. package/dist/server/assets/import-display-BVIOWcsm.js +124 -0
  85. package/dist/server/assets/imports-B6JBDl_h.js +379 -0
  86. package/dist/server/assets/imports-BGe5tZJT.js +28 -0
  87. package/dist/server/assets/input-C5r-hBix.js +19 -0
  88. package/dist/server/assets/loading-state-CrvCWTtw.js +23 -0
  89. package/dist/server/assets/modviz-data-CUyTorv0.js +197 -0
  90. package/dist/server/assets/modviz-layout-BAH2ogse.js +253 -0
  91. package/dist/server/assets/modviz-server-DoMlAyFW.js +195 -0
  92. package/dist/server/assets/modviz-sigma-XYxARWqd.js +1441 -0
  93. package/dist/server/assets/rolldown-runtime-rSIU-vHC.js +13 -0
  94. package/dist/server/assets/router-DYJ-zDbU.js +353 -0
  95. package/dist/server/assets/routes-DInCacpY.js +244 -0
  96. package/dist/server/assets/search-params-BNApPgkX.js +26 -0
  97. package/dist/server/assets/setup-view-DjI49Iqr.js +91 -0
  98. package/dist/server/assets/start-Ba3KII43.js +4 -0
  99. package/dist/server/assets/summary-z3lXkLCQ.js +208 -0
  100. package/dist/server/assets/tooltip-Ck0DDfF7.js +24 -0
  101. package/dist/server/assets/trace-ColKOf9g.js +16 -0
  102. package/dist/server/assets/trace-eVs-hIZO.js +578 -0
  103. package/dist/server/assets/treemap-BbZ9M4GF.js +17 -0
  104. package/dist/server/assets/treemap-CrgWFoCF.js +912 -0
  105. package/dist/server/assets/utils-BQZm0uva.js +8 -0
  106. package/dist/server/server.js +5259 -0
  107. package/dist/shared/modviz-compare.js +120 -0
  108. package/dist/shared/modviz-trace.js +244 -0
  109. package/package.json +135 -0
@@ -0,0 +1,60 @@
1
+ import path from "node:path";
2
+ const WINDOWS_ABSOLUTE_PATH_RE = /^[A-Za-z]:[\\/]/;
3
+ const ANALYZABLE_SOURCE_EXTENSIONS = new Set([
4
+ ".js",
5
+ ".jsx",
6
+ ".ts",
7
+ ".tsx",
8
+ ".mjs",
9
+ ".cjs",
10
+ ".mts",
11
+ ".cts",
12
+ ".json",
13
+ ".node",
14
+ ]);
15
+ function findSpecifierSuffixIndex(importee) {
16
+ const queryIndex = importee.indexOf("?");
17
+ const hashIndex = importee.indexOf("#");
18
+ if (queryIndex === -1) {
19
+ return hashIndex;
20
+ }
21
+ if (hashIndex === -1) {
22
+ return queryIndex;
23
+ }
24
+ return Math.min(queryIndex, hashIndex);
25
+ }
26
+ function isFileLikeImportSpecifier(importee) {
27
+ return (importee.startsWith("./") ||
28
+ importee.startsWith("../") ||
29
+ importee.startsWith("/") ||
30
+ WINDOWS_ABSOLUTE_PATH_RE.test(importee));
31
+ }
32
+ export function sanitizeImportSpecifierForAnalysis(importee) {
33
+ const suffixIndex = findSpecifierSuffixIndex(importee);
34
+ if (suffixIndex === -1) {
35
+ return importee;
36
+ }
37
+ const cleanedImportee = importee.slice(0, suffixIndex);
38
+ if (!cleanedImportee || !isFileLikeImportSpecifier(cleanedImportee)) {
39
+ return importee;
40
+ }
41
+ return cleanedImportee;
42
+ }
43
+ export function shouldSkipImportForAnalysis(importee) {
44
+ const sanitizedImportee = sanitizeImportSpecifierForAnalysis(importee);
45
+ if (sanitizedImportee === importee) {
46
+ return false;
47
+ }
48
+ const extension = path.extname(sanitizedImportee).toLowerCase();
49
+ return extension.length > 0 && !ANALYZABLE_SOURCE_EXTENSIONS.has(extension);
50
+ }
51
+ export const sanitizeFileImportSuffixPlugin = {
52
+ name: "sanitize-file-import-suffix",
53
+ handleImport({ importee }) {
54
+ if (shouldSkipImportForAnalysis(importee)) {
55
+ return false;
56
+ }
57
+ const sanitizedImportee = sanitizeImportSpecifierForAnalysis(importee);
58
+ return sanitizedImportee === importee ? undefined : sanitizedImportee;
59
+ },
60
+ };
@@ -0,0 +1,103 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { resolveProductionRuntimePaths } from "./runtime-host.js";
6
+ const __dirname = fileURLToPath(new URL(".", import.meta.url));
7
+ const runtimePaths = resolveProductionRuntimePaths(import.meta.url);
8
+ const openBrowser = async (url) => {
9
+ const platform = process.platform;
10
+ const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
11
+ const args = platform === "win32" ? ["/c", "start", "", url] : [url];
12
+ spawn(command, args, {
13
+ detached: true,
14
+ stdio: "ignore",
15
+ }).unref();
16
+ };
17
+ const waitForServer = async (url, timeoutMs = 20000) => {
18
+ const start = Date.now();
19
+ while (Date.now() - start < timeoutMs) {
20
+ try {
21
+ const response = await fetch(url, { method: "GET" });
22
+ if (response.ok) {
23
+ return;
24
+ }
25
+ }
26
+ catch {
27
+ // Keep polling until the server is reachable.
28
+ }
29
+ await new Promise((resolve) => setTimeout(resolve, 250));
30
+ }
31
+ throw new Error(`Timed out waiting for the web UI server at ${url}`);
32
+ };
33
+ const formatEarlyExit = (code, signal) => {
34
+ const details = [code !== null ? `code ${code}` : null, signal ? `signal ${signal}` : null]
35
+ .filter(Boolean)
36
+ .join(", ");
37
+ return `The web UI server exited before it became ready${details ? ` (${details})` : ""}.`;
38
+ };
39
+ export async function startProductionServer(options) {
40
+ if (!existsSync(runtimePaths.runtimeServerEntry)) {
41
+ throw new Error([
42
+ `Expected packaged production runtime at ${runtimePaths.runtimeServerEntry}.`,
43
+ "Run `pnpm build` in the modviz package before launching the UI.",
44
+ ].join(" "));
45
+ }
46
+ const runtimeHostEntry = existsSync(runtimePaths.runtimeHostBuildEntry)
47
+ ? runtimePaths.runtimeHostBuildEntry
48
+ : runtimePaths.runtimeHostSourceEntry;
49
+ if (!existsSync(runtimeHostEntry)) {
50
+ throw new Error([
51
+ `Expected a runtime host entry at ${runtimePaths.runtimeHostBuildEntry} or ${runtimePaths.runtimeHostSourceEntry}.`,
52
+ "Run `pnpm build` in the modviz package before launching the UI.",
53
+ ].join(" "));
54
+ }
55
+ const historyDir = path.join(path.dirname(path.resolve(options.outputPath)), ".modviz", "history");
56
+ const child = spawn(process.execPath, [runtimeHostEntry], {
57
+ env: {
58
+ ...process.env,
59
+ MODVIZ_PATH: options.outputPath,
60
+ MODVIZ_HISTORY_DIR: process.env.MODVIZ_HISTORY_DIR ?? historyDir,
61
+ PORT: String(options.port),
62
+ },
63
+ stdio: "inherit",
64
+ });
65
+ const shutdown = () => {
66
+ child.kill("SIGTERM");
67
+ };
68
+ process.once("SIGINT", shutdown);
69
+ process.once("SIGTERM", shutdown);
70
+ const url = `http://localhost:${options.port}`;
71
+ let onEarlyError;
72
+ let onEarlyExit;
73
+ const childFailure = new Promise((_, reject) => {
74
+ onEarlyError = (error) => reject(error);
75
+ onEarlyExit = (code, signal) => reject(new Error(formatEarlyExit(code, signal)));
76
+ child.once("error", onEarlyError);
77
+ child.once("exit", onEarlyExit);
78
+ });
79
+ try {
80
+ await Promise.race([waitForServer(url), childFailure]);
81
+ }
82
+ finally {
83
+ if (onEarlyError) {
84
+ child.off("error", onEarlyError);
85
+ }
86
+ if (onEarlyExit) {
87
+ child.off("exit", onEarlyExit);
88
+ }
89
+ }
90
+ if (options.open !== false) {
91
+ await openBrowser(url);
92
+ }
93
+ await new Promise((resolve, reject) => {
94
+ child.once("error", reject);
95
+ child.once("exit", (code, signal) => {
96
+ if (code === 0 || signal === "SIGINT" || signal === "SIGTERM") {
97
+ resolve();
98
+ return;
99
+ }
100
+ reject(new Error(formatEarlyExit(code, signal)));
101
+ });
102
+ });
103
+ }
@@ -0,0 +1,217 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { createReadStream, existsSync, statSync } from "node:fs";
10
+ import { createServer } from "node:http";
11
+ import path from "node:path";
12
+ import { Readable } from "node:stream";
13
+ import { pipeline } from "node:stream/promises";
14
+ import { fileURLToPath, pathToFileURL } from "node:url";
15
+ export const resolveProductionRuntimePaths = (fromImportUrl) => {
16
+ const currentDir = fileURLToPath(new URL(".", fromImportUrl));
17
+ const candidatePackageRoots = [path.resolve(currentDir, ".."), path.resolve(currentDir, "../..")];
18
+ const packageRoot = candidatePackageRoots.find((candidate) => existsSync(path.join(candidate, "package.json"))) ??
19
+ path.resolve(currentDir, "..");
20
+ const packagedRuntimeRoot = path.join(packageRoot, "dist");
21
+ return {
22
+ packageRoot,
23
+ packagedRuntimeRoot,
24
+ runtimeClientRoot: path.join(packagedRuntimeRoot, "client"),
25
+ runtimeHostBuildEntry: path.join(packagedRuntimeRoot, "mod", "runtime-host.js"),
26
+ runtimeHostSourceEntry: path.join(packageRoot, "mod", "runtime-host.ts"),
27
+ runtimeServerEntry: path.join(packagedRuntimeRoot, "server", "server.js"),
28
+ };
29
+ };
30
+ const STATIC_CONTENT_TYPES = {
31
+ ".css": "text/css; charset=utf-8",
32
+ ".gif": "image/gif",
33
+ ".html": "text/html; charset=utf-8",
34
+ ".ico": "image/x-icon",
35
+ ".jpeg": "image/jpeg",
36
+ ".jpg": "image/jpeg",
37
+ ".js": "text/javascript; charset=utf-8",
38
+ ".json": "application/json; charset=utf-8",
39
+ ".map": "application/json; charset=utf-8",
40
+ ".mjs": "text/javascript; charset=utf-8",
41
+ ".png": "image/png",
42
+ ".svg": "image/svg+xml",
43
+ ".txt": "text/plain; charset=utf-8",
44
+ ".webmanifest": "application/manifest+json; charset=utf-8",
45
+ ".webp": "image/webp",
46
+ ".woff": "font/woff",
47
+ ".woff2": "font/woff2",
48
+ };
49
+ const isDirectExecution = (importUrl) => {
50
+ const argvPath = process.argv[1];
51
+ if (!argvPath) {
52
+ return false;
53
+ }
54
+ return pathToFileURL(path.resolve(argvPath)).href === importUrl;
55
+ };
56
+ const createRequestFromNode = (request, port) => {
57
+ const forwardedProto = request.headers["x-forwarded-proto"];
58
+ const protocol = typeof forwardedProto === "string" && forwardedProto.trim().length > 0
59
+ ? forwardedProto.split(",")[0].trim()
60
+ : "http";
61
+ const host = request.headers.host ?? `localhost:${port}`;
62
+ const url = new URL(request.url ?? "/", `${protocol}://${host}`);
63
+ const headers = new Headers();
64
+ for (const [key, value] of Object.entries(request.headers)) {
65
+ if (Array.isArray(value)) {
66
+ for (const headerValue of value) {
67
+ headers.append(key, headerValue);
68
+ }
69
+ continue;
70
+ }
71
+ if (typeof value === "string") {
72
+ headers.set(key, value);
73
+ }
74
+ }
75
+ const method = request.method ?? "GET";
76
+ const hasBody = !["GET", "HEAD"].includes(method.toUpperCase());
77
+ const body = hasBody ? Readable.toWeb(request) : undefined;
78
+ const requestInit = {
79
+ body,
80
+ duplex: hasBody ? "half" : undefined,
81
+ headers,
82
+ method,
83
+ };
84
+ return new Request(url, requestInit);
85
+ };
86
+ const getContentType = (filePath) => STATIC_CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? "application/octet-stream";
87
+ const resolveStaticAssetPath = (clientRoot, pathname) => {
88
+ const relativePath = decodeURIComponent(pathname).replace(/^\/+/, "");
89
+ if (!relativePath) {
90
+ return null;
91
+ }
92
+ const absoluteClientRoot = path.resolve(clientRoot);
93
+ const candidatePath = path.resolve(absoluteClientRoot, relativePath);
94
+ if (candidatePath !== absoluteClientRoot &&
95
+ !candidatePath.startsWith(`${absoluteClientRoot}${path.sep}`)) {
96
+ return null;
97
+ }
98
+ if (!existsSync(candidatePath)) {
99
+ return null;
100
+ }
101
+ const stat = statSync(candidatePath);
102
+ if (!stat.isFile()) {
103
+ return null;
104
+ }
105
+ return {
106
+ filePath: candidatePath,
107
+ stat,
108
+ };
109
+ };
110
+ const tryServeStaticAsset = async (request, response, clientRoot, port) => {
111
+ const method = (request.method ?? "GET").toUpperCase();
112
+ if (method !== "GET" && method !== "HEAD") {
113
+ return false;
114
+ }
115
+ const requestUrl = new URL(request.url ?? "/", `http://${request.headers.host ?? `localhost:${port}`}`);
116
+ if (path.posix.extname(requestUrl.pathname) === "") {
117
+ return false;
118
+ }
119
+ const asset = resolveStaticAssetPath(clientRoot, requestUrl.pathname);
120
+ if (!asset) {
121
+ return false;
122
+ }
123
+ response.statusCode = 200;
124
+ response.setHeader("content-length", String(asset.stat.size));
125
+ response.setHeader("content-type", getContentType(asset.filePath));
126
+ response.setHeader("cache-control", requestUrl.pathname.startsWith("/assets/")
127
+ ? "public, max-age=31536000, immutable"
128
+ : "public, max-age=3600");
129
+ if (method === "HEAD") {
130
+ response.end();
131
+ return true;
132
+ }
133
+ await pipeline(createReadStream(asset.filePath), response);
134
+ return true;
135
+ };
136
+ const writeResponseToNode = async (request, response, nodeResponse) => {
137
+ nodeResponse.statusCode = response.status;
138
+ nodeResponse.statusMessage = response.statusText;
139
+ const responseHeaders = response.headers;
140
+ const setCookieValues = responseHeaders.getSetCookie?.() ?? [];
141
+ response.headers.forEach((value, key) => {
142
+ if (key.toLowerCase() === "set-cookie" && setCookieValues.length > 0) {
143
+ return;
144
+ }
145
+ nodeResponse.setHeader(key, value);
146
+ });
147
+ if (setCookieValues.length > 0) {
148
+ nodeResponse.setHeader("set-cookie", setCookieValues);
149
+ }
150
+ if (!response.body || request.method?.toUpperCase() === "HEAD") {
151
+ nodeResponse.end();
152
+ return;
153
+ }
154
+ await pipeline(Readable.fromWeb(response.body), nodeResponse);
155
+ };
156
+ const handleNodeRequest = async (fetchHandler, request, response, clientRoot, port) => {
157
+ try {
158
+ if (await tryServeStaticAsset(request, response, clientRoot, port)) {
159
+ return;
160
+ }
161
+ const runtimeRequest = createRequestFromNode(request, port);
162
+ const runtimeResponse = await fetchHandler(runtimeRequest);
163
+ await writeResponseToNode(request, runtimeResponse, response);
164
+ }
165
+ catch (error) {
166
+ console.error(error);
167
+ if (!response.headersSent) {
168
+ response.statusCode = 500;
169
+ response.setHeader("content-type", "text/plain; charset=utf-8");
170
+ }
171
+ response.end("Internal Server Error");
172
+ }
173
+ };
174
+ export async function startRuntimeHost(options) {
175
+ const runtimePaths = resolveProductionRuntimePaths(import.meta.url);
176
+ if (!existsSync(runtimePaths.runtimeServerEntry)) {
177
+ throw new Error([
178
+ `Expected bundled TanStack server module at ${runtimePaths.runtimeServerEntry}.`,
179
+ "Run `pnpm build` before starting the production UI.",
180
+ ].join(" "));
181
+ }
182
+ const port = options?.port ?? Number.parseInt(process.env.PORT ?? "3000", 10);
183
+ if (!Number.isInteger(port) || port <= 0) {
184
+ throw new Error(`Invalid PORT value: ${process.env.PORT ?? String(options?.port)}`);
185
+ }
186
+ const serverModule = (await import(__rewriteRelativeImportExtension(pathToFileURL(runtimePaths.runtimeServerEntry).href)));
187
+ const fetchHandler = serverModule.default?.fetch;
188
+ if (typeof fetchHandler !== "function") {
189
+ throw new Error([
190
+ `Expected ${runtimePaths.runtimeServerEntry} to export a default fetch handler.`,
191
+ "The production UI build is incomplete or incompatible with the current launcher.",
192
+ ].join(" "));
193
+ }
194
+ const server = createServer((request, response) => {
195
+ void handleNodeRequest(fetchHandler, request, response, runtimePaths.runtimeClientRoot, port);
196
+ });
197
+ await new Promise((resolve, reject) => {
198
+ server.once("error", reject);
199
+ server.listen(port, () => resolve());
200
+ });
201
+ const shutdown = () => {
202
+ server.close();
203
+ server.closeAllConnections?.();
204
+ };
205
+ process.once("SIGINT", shutdown);
206
+ process.once("SIGTERM", shutdown);
207
+ await new Promise((resolve, reject) => {
208
+ server.once("close", () => resolve());
209
+ server.once("error", reject);
210
+ });
211
+ }
212
+ if (isDirectExecution(import.meta.url)) {
213
+ void startRuntimeHost().catch((error) => {
214
+ console.error(error);
215
+ process.exit(1);
216
+ });
217
+ }
@@ -0,0 +1,73 @@
1
+ import { mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ const SNAPSHOT_NAME_SANITIZER = /[^a-z0-9-_]+/gi;
4
+ export const resolveSnapshotHistoryDir = () => path.resolve(process.env.MODVIZ_HISTORY_DIR ??
5
+ (process.env.MODVIZ_PATH
6
+ ? path.join(path.dirname(path.resolve(process.env.MODVIZ_PATH)), ".modviz", "history")
7
+ : path.join(process.cwd(), ".modviz", "history")));
8
+ const toSnapshotId = (snapshotName, generatedAt) => {
9
+ const timestamp = new Date(generatedAt ?? Date.now()).toISOString().replace(/[:.]/g, "-");
10
+ const normalizedName = snapshotName
11
+ .trim()
12
+ .replace(SNAPSHOT_NAME_SANITIZER, "-")
13
+ .replace(/-+/g, "-")
14
+ .replace(/^-|-$/g, "")
15
+ .toLowerCase() || "snapshot";
16
+ return `${timestamp}-${normalizedName}`;
17
+ };
18
+ const resolveSnapshotPaths = (snapshotId) => {
19
+ const historyDir = resolveSnapshotHistoryDir();
20
+ return {
21
+ historyDir,
22
+ graphPath: path.join(historyDir, `${snapshotId}.json`),
23
+ llmPath: path.join(historyDir, `${snapshotId}.llm.json`),
24
+ };
25
+ };
26
+ const readSnapshotItem = (graphPath) => {
27
+ try {
28
+ const raw = JSON.parse(readFileSync(graphPath, "utf-8"));
29
+ const fileStats = statSync(graphPath);
30
+ const snapshotId = path.basename(graphPath, ".json");
31
+ const llmPath = graphPath.replace(/\.json$/, ".llm.json");
32
+ return {
33
+ id: snapshotId,
34
+ label: snapshotId.replace(/^\d{4}-\d{2}-\d{2}t/i, "").replace(/-/g, " "),
35
+ graphPath,
36
+ llmPath,
37
+ generatedAt: raw.metadata.generatedAt ?? null,
38
+ lastModified: fileStats.mtimeMs,
39
+ totalNodes: raw.nodes.length,
40
+ entrypoints: raw.metadata.entrypoints,
41
+ };
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ };
47
+ export const listSnapshotHistory = () => {
48
+ const historyDir = resolveSnapshotHistoryDir();
49
+ try {
50
+ return readdirSync(historyDir)
51
+ .filter((fileName) => fileName.endsWith(".json") && !fileName.endsWith(".llm.json"))
52
+ .map((fileName) => readSnapshotItem(path.join(historyDir, fileName)))
53
+ .filter((item) => Boolean(item))
54
+ .sort((left, right) => right.lastModified - left.lastModified);
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ };
60
+ export const saveSnapshotToHistory = (options) => {
61
+ const snapshotId = toSnapshotId(options.snapshotName, options.graph.metadata.generatedAt);
62
+ const { historyDir, graphPath, llmPath } = resolveSnapshotPaths(snapshotId);
63
+ mkdirSync(historyDir, { recursive: true });
64
+ writeFileSync(graphPath, JSON.stringify(options.graph, null, 2));
65
+ if (options.llm) {
66
+ writeFileSync(llmPath, JSON.stringify(options.llm, null, 2));
67
+ }
68
+ return readSnapshotItem(graphPath);
69
+ };
70
+ export const loadSnapshotGraph = (snapshotId) => {
71
+ const { graphPath } = resolveSnapshotPaths(snapshotId);
72
+ return JSON.parse(readFileSync(graphPath, "utf-8"));
73
+ };
@@ -0,0 +1,3 @@
1
+ // TODO flame graph / show "hot spots" = problematic files (= introducing too many transitive imports)
2
+ // TODO a way to show files that trigger transitive imports going through another package
3
+ export {};
@@ -0,0 +1,5 @@
1
+ //#region \0%23tanstack-start-plugin-adapters
2
+ var pluginSerializationAdapters = [];
3
+ var hasPluginAdapters = false;
4
+ //#endregion
5
+ export { hasPluginAdapters, pluginSerializationAdapters };
@@ -0,0 +1,188 @@
1
+ //#region \0tanstack-start-manifest:v
2
+ var tsrStartManifest = () => ({
3
+ routes: {
4
+ __root__: {
5
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/__root.tsx",
6
+ children: [
7
+ "/",
8
+ "/compare",
9
+ "/configure",
10
+ "/explorer",
11
+ "/graph",
12
+ "/hierarchy",
13
+ "/imports",
14
+ "/summary",
15
+ "/trace",
16
+ "/treemap",
17
+ "/api/json-status",
18
+ "/api/modviz-bundle",
19
+ "/api/snapshot-history",
20
+ "/api/users/$userId"
21
+ ],
22
+ assets: void 0,
23
+ preloads: [
24
+ "/assets/index-Dj_rhLdR.js",
25
+ "/assets/jsx-runtime-DWSWI4JT.js",
26
+ "/assets/modviz-data-BiRqoDI5.js",
27
+ "/assets/lazyRouteComponent-PTSyFp1J.js",
28
+ "/assets/trace-B67CM5s2.js",
29
+ "/assets/graph-CVzypIGU.js",
30
+ "/assets/compare-TOnoe1EP.js",
31
+ "/assets/explorer-C7NclVKg.js",
32
+ "/assets/hierarchy-iO7d4oSK.js",
33
+ "/assets/imports-CPggnrs-.js",
34
+ "/assets/treemap-Csroy8Gy.js"
35
+ ]
36
+ },
37
+ "/": {
38
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/index.tsx",
39
+ children: void 0,
40
+ assets: void 0,
41
+ preloads: [
42
+ "/assets/routes-DBtN8hrZ.js",
43
+ "/assets/utils-DkkZd0ys.js",
44
+ "/assets/modviz-layout-Do93E-IB.js",
45
+ "/assets/setup-view-j1o0TuZz.js",
46
+ "/assets/formatting-CiC0SYI8.js"
47
+ ]
48
+ },
49
+ "/compare": {
50
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/compare.tsx",
51
+ children: void 0,
52
+ assets: void 0,
53
+ preloads: [
54
+ "/assets/compare-K6jVFsiI.js",
55
+ "/assets/button-aOWckyNs.js",
56
+ "/assets/tooltip-B1VDU9HG.js",
57
+ "/assets/command-kkF7_wdz.js",
58
+ "/assets/utils-DkkZd0ys.js",
59
+ "/assets/modviz-layout-Do93E-IB.js",
60
+ "/assets/formatting-CiC0SYI8.js"
61
+ ]
62
+ },
63
+ "/configure": {
64
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/configure.tsx",
65
+ children: void 0,
66
+ assets: void 0,
67
+ preloads: [
68
+ "/assets/configure-DnlSnhtN.js",
69
+ "/assets/button-aOWckyNs.js",
70
+ "/assets/utils-DkkZd0ys.js",
71
+ "/assets/modviz-layout-Do93E-IB.js",
72
+ "/assets/check-C0EQe2S8.js",
73
+ "/assets/input-BCFMF0aR.js"
74
+ ]
75
+ },
76
+ "/explorer": {
77
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/explorer.tsx",
78
+ children: void 0,
79
+ assets: void 0,
80
+ preloads: [
81
+ "/assets/explorer-Xu2X6XXF.js",
82
+ "/assets/button-aOWckyNs.js",
83
+ "/assets/utils-DkkZd0ys.js",
84
+ "/assets/modviz-layout-Do93E-IB.js",
85
+ "/assets/chevron-down-DrspihmT.js",
86
+ "/assets/chevron-right-DIJHr8AN.js",
87
+ "/assets/import-display-D-jRyyjM.js",
88
+ "/assets/external-link-B9eNA-li.js",
89
+ "/assets/setup-view-j1o0TuZz.js",
90
+ "/assets/input-BCFMF0aR.js"
91
+ ]
92
+ },
93
+ "/graph": {
94
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/graph.tsx",
95
+ children: void 0,
96
+ assets: [{
97
+ tag: "link",
98
+ attrs: {
99
+ rel: "stylesheet",
100
+ href: "/assets/graph-command-menu-VWiiW3qy.css#",
101
+ type: "text/css"
102
+ }
103
+ }],
104
+ preloads: [
105
+ "/assets/graph-6Vr74V1k.js",
106
+ "/assets/button-aOWckyNs.js",
107
+ "/assets/portal-BgAm3V3j.js",
108
+ "/assets/floating-ui.dom-DLIT5tPE.js",
109
+ "/assets/graph-command-menu-D2MoVT2B.js",
110
+ "/assets/command-kkF7_wdz.js",
111
+ "/assets/utils-DkkZd0ys.js",
112
+ "/assets/modviz-layout-Do93E-IB.js",
113
+ "/assets/setup-view-j1o0TuZz.js",
114
+ "/assets/loading-state-CyC_hrTF.js",
115
+ "/assets/input-BCFMF0aR.js"
116
+ ]
117
+ },
118
+ "/hierarchy": {
119
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/hierarchy.tsx",
120
+ children: void 0,
121
+ assets: void 0,
122
+ preloads: [
123
+ "/assets/hierarchy-C8xxGb_u.js",
124
+ "/assets/modviz-layout-Do93E-IB.js",
125
+ "/assets/setup-view-j1o0TuZz.js",
126
+ "/assets/loading-state-CyC_hrTF.js"
127
+ ]
128
+ },
129
+ "/imports": {
130
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/imports.tsx",
131
+ children: void 0,
132
+ assets: void 0,
133
+ preloads: [
134
+ "/assets/imports-CodbPyUJ.js",
135
+ "/assets/button-aOWckyNs.js",
136
+ "/assets/modviz-layout-Do93E-IB.js",
137
+ "/assets/chevron-down-DrspihmT.js",
138
+ "/assets/import-display-D-jRyyjM.js",
139
+ "/assets/setup-view-j1o0TuZz.js",
140
+ "/assets/input-BCFMF0aR.js"
141
+ ]
142
+ },
143
+ "/summary": {
144
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/summary.tsx",
145
+ children: void 0,
146
+ assets: void 0,
147
+ preloads: [
148
+ "/assets/summary-D703Zh3x.js",
149
+ "/assets/modviz-layout-Do93E-IB.js",
150
+ "/assets/setup-view-j1o0TuZz.js",
151
+ "/assets/formatting-CiC0SYI8.js"
152
+ ]
153
+ },
154
+ "/trace": {
155
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/trace.tsx",
156
+ children: void 0,
157
+ assets: void 0,
158
+ preloads: [
159
+ "/assets/trace-Bwwdw3AM.js",
160
+ "/assets/button-aOWckyNs.js",
161
+ "/assets/utils-DkkZd0ys.js",
162
+ "/assets/modviz-layout-Do93E-IB.js",
163
+ "/assets/setup-view-j1o0TuZz.js",
164
+ "/assets/search-BYHxNrYn.js",
165
+ "/assets/input-BCFMF0aR.js"
166
+ ]
167
+ },
168
+ "/treemap": {
169
+ filePath: "/Users/astahmer/dev/alex/module-graph-viz/src/routes/treemap.tsx",
170
+ children: void 0,
171
+ assets: void 0,
172
+ preloads: [
173
+ "/assets/treemap-BZf2shzY.js",
174
+ "/assets/button-aOWckyNs.js",
175
+ "/assets/portal-BgAm3V3j.js",
176
+ "/assets/tooltip-B1VDU9HG.js",
177
+ "/assets/utils-DkkZd0ys.js",
178
+ "/assets/colors-CQoWjU5E.js",
179
+ "/assets/modviz-layout-Do93E-IB.js",
180
+ "/assets/external-link-B9eNA-li.js",
181
+ "/assets/setup-view-j1o0TuZz.js"
182
+ ]
183
+ }
184
+ },
185
+ clientEntry: "/assets/index-Dj_rhLdR.js"
186
+ });
187
+ //#endregion
188
+ export { tsrStartManifest };