@fairfox/polly 0.2.0 → 0.3.0
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/vendor/verify/specs/Dockerfile +13 -0
- package/dist/vendor/verify/specs/README.md +37 -0
- package/dist/vendor/verify/specs/docker-compose.yml +9 -0
- package/dist/vendor/verify/specs/tla/MessageRouter.cfg +24 -0
- package/dist/vendor/verify/specs/tla/MessageRouter.tla +221 -0
- package/dist/vendor/verify/specs/tla/README.md +179 -0
- package/dist/vendor/verify/specs/verification.config.ts +61 -0
- package/dist/vendor/verify/src/cli.js +109 -5
- package/dist/vendor/verify/src/cli.js.map +5 -5
- package/dist/vendor/visualize/src/cli.js +665 -55
- package/dist/vendor/visualize/src/cli.js.map +12 -11
- package/package.json +1 -1
|
@@ -13,13 +13,448 @@ var __export = (target, all) => {
|
|
|
13
13
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
14
14
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
15
15
|
|
|
16
|
+
// vendor/analysis/src/extract/project-detector.ts
|
|
17
|
+
var exports_project_detector = {};
|
|
18
|
+
__export(exports_project_detector, {
|
|
19
|
+
detectProjectConfig: () => detectProjectConfig,
|
|
20
|
+
ProjectDetector: () => ProjectDetector
|
|
21
|
+
});
|
|
22
|
+
import * as fs3 from "node:fs";
|
|
23
|
+
import * as path3 from "node:path";
|
|
24
|
+
|
|
25
|
+
class ProjectDetector {
|
|
26
|
+
projectRoot;
|
|
27
|
+
constructor(projectRoot) {
|
|
28
|
+
this.projectRoot = projectRoot;
|
|
29
|
+
}
|
|
30
|
+
detect() {
|
|
31
|
+
const manifestPath = path3.join(this.projectRoot, "manifest.json");
|
|
32
|
+
if (fs3.existsSync(manifestPath)) {
|
|
33
|
+
return this.detectChromeExtension(manifestPath);
|
|
34
|
+
}
|
|
35
|
+
const pwaManifestPath = path3.join(this.projectRoot, "public", "manifest.json");
|
|
36
|
+
if (fs3.existsSync(pwaManifestPath)) {
|
|
37
|
+
return this.detectPWA(pwaManifestPath);
|
|
38
|
+
}
|
|
39
|
+
const packageJsonPath = path3.join(this.projectRoot, "package.json");
|
|
40
|
+
if (fs3.existsSync(packageJsonPath)) {
|
|
41
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
42
|
+
if (packageJson.dependencies?.electron || packageJson.devDependencies?.electron) {
|
|
43
|
+
return this.detectElectron(packageJson);
|
|
44
|
+
}
|
|
45
|
+
const deps = {
|
|
46
|
+
...packageJson.dependencies,
|
|
47
|
+
...packageJson.devDependencies
|
|
48
|
+
};
|
|
49
|
+
if (deps?.ws || deps?.["socket.io"] || deps?.elysia || deps?.express || deps?.fastify || deps?.hono || deps?.koa) {
|
|
50
|
+
return this.detectWebSocketApp(packageJson);
|
|
51
|
+
}
|
|
52
|
+
const serverResult = this.detectWebSocketApp(packageJson);
|
|
53
|
+
if (Object.keys(serverResult.entryPoints).length > 0) {
|
|
54
|
+
return serverResult;
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
const serverResult = this.detectWebSocketApp({});
|
|
58
|
+
if (Object.keys(serverResult.entryPoints).length > 0) {
|
|
59
|
+
return serverResult;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return this.detectGenericProject();
|
|
63
|
+
}
|
|
64
|
+
detectChromeExtension(manifestPath) {
|
|
65
|
+
const manifest = JSON.parse(fs3.readFileSync(manifestPath, "utf-8"));
|
|
66
|
+
const entryPoints = {};
|
|
67
|
+
const background = manifest.background;
|
|
68
|
+
if (background) {
|
|
69
|
+
const file = background.service_worker || background.scripts?.[0] || background.page;
|
|
70
|
+
if (file) {
|
|
71
|
+
entryPoints.background = this.findSourceFile(file);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const contentScripts = manifest.content_scripts;
|
|
75
|
+
if (contentScripts && contentScripts.length > 0) {
|
|
76
|
+
const firstScript = contentScripts[0].js?.[0];
|
|
77
|
+
if (firstScript) {
|
|
78
|
+
entryPoints.content = this.findSourceFile(firstScript);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const popup = manifest.action?.default_popup || manifest.browser_action?.default_popup;
|
|
82
|
+
if (popup) {
|
|
83
|
+
const jsFile = this.findAssociatedJS(path3.join(this.projectRoot, popup));
|
|
84
|
+
if (jsFile)
|
|
85
|
+
entryPoints.popup = jsFile;
|
|
86
|
+
}
|
|
87
|
+
const options = manifest.options_ui?.page || manifest.options_page;
|
|
88
|
+
if (options) {
|
|
89
|
+
const jsFile = this.findAssociatedJS(path3.join(this.projectRoot, options));
|
|
90
|
+
if (jsFile)
|
|
91
|
+
entryPoints.options = jsFile;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
type: "chrome-extension",
|
|
95
|
+
entryPoints,
|
|
96
|
+
metadata: {
|
|
97
|
+
name: manifest.name,
|
|
98
|
+
version: manifest.version,
|
|
99
|
+
description: manifest.description
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
detectPWA(manifestPath) {
|
|
104
|
+
const manifest = JSON.parse(fs3.readFileSync(manifestPath, "utf-8"));
|
|
105
|
+
const entryPoints = {};
|
|
106
|
+
const swCandidates = [
|
|
107
|
+
"src/service-worker.ts",
|
|
108
|
+
"src/sw.ts",
|
|
109
|
+
"public/service-worker.js",
|
|
110
|
+
"public/sw.js"
|
|
111
|
+
];
|
|
112
|
+
for (const candidate of swCandidates) {
|
|
113
|
+
const fullPath = path3.join(this.projectRoot, candidate);
|
|
114
|
+
if (fs3.existsSync(fullPath)) {
|
|
115
|
+
entryPoints.worker = fullPath;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const clientCandidates = [
|
|
120
|
+
"src/main.ts",
|
|
121
|
+
"src/index.ts",
|
|
122
|
+
"src/app.ts",
|
|
123
|
+
"src/main.tsx",
|
|
124
|
+
"src/index.tsx"
|
|
125
|
+
];
|
|
126
|
+
for (const candidate of clientCandidates) {
|
|
127
|
+
const fullPath = path3.join(this.projectRoot, candidate);
|
|
128
|
+
if (fs3.existsSync(fullPath)) {
|
|
129
|
+
entryPoints.client = fullPath;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
type: "pwa",
|
|
135
|
+
entryPoints,
|
|
136
|
+
contextMapping: {
|
|
137
|
+
worker: "Service Worker",
|
|
138
|
+
client: "Client App"
|
|
139
|
+
},
|
|
140
|
+
metadata: {
|
|
141
|
+
name: manifest.name || manifest.short_name,
|
|
142
|
+
version: "1.0.0",
|
|
143
|
+
description: manifest.description
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
detectElectron(packageJson) {
|
|
148
|
+
const entryPoints = {};
|
|
149
|
+
const mainCandidates = [
|
|
150
|
+
packageJson.main,
|
|
151
|
+
"src/main/index.ts",
|
|
152
|
+
"src/electron/main.ts",
|
|
153
|
+
"electron/main.ts",
|
|
154
|
+
"main.ts"
|
|
155
|
+
].filter(Boolean);
|
|
156
|
+
for (const candidate of mainCandidates) {
|
|
157
|
+
const fullPath = path3.join(this.projectRoot, candidate);
|
|
158
|
+
if (fs3.existsSync(fullPath) || fs3.existsSync(fullPath.replace(/\.js$/, ".ts"))) {
|
|
159
|
+
entryPoints.main = fs3.existsSync(fullPath) ? fullPath : fullPath.replace(/\.js$/, ".ts");
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const rendererCandidates = [
|
|
164
|
+
"src/renderer/index.tsx",
|
|
165
|
+
"src/renderer/index.ts",
|
|
166
|
+
"src/index.tsx",
|
|
167
|
+
"src/index.ts"
|
|
168
|
+
];
|
|
169
|
+
for (const candidate of rendererCandidates) {
|
|
170
|
+
const fullPath = path3.join(this.projectRoot, candidate);
|
|
171
|
+
if (fs3.existsSync(fullPath)) {
|
|
172
|
+
entryPoints.renderer = fullPath;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
type: "electron",
|
|
178
|
+
entryPoints,
|
|
179
|
+
contextMapping: {
|
|
180
|
+
main: "Main Process",
|
|
181
|
+
renderer: "Renderer Process"
|
|
182
|
+
},
|
|
183
|
+
metadata: {
|
|
184
|
+
name: packageJson.name,
|
|
185
|
+
version: packageJson.version,
|
|
186
|
+
description: packageJson.description
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
detectWebSocketApp(packageJson) {
|
|
191
|
+
const entryPoints = {};
|
|
192
|
+
const contextMapping = {};
|
|
193
|
+
const serverCandidates = [
|
|
194
|
+
"src/server.ts",
|
|
195
|
+
"src/server.js",
|
|
196
|
+
"src/index.ts",
|
|
197
|
+
"src/index.js",
|
|
198
|
+
"src/main.ts",
|
|
199
|
+
"src/main.js",
|
|
200
|
+
"src/app.ts",
|
|
201
|
+
"src/app.js",
|
|
202
|
+
"server.ts",
|
|
203
|
+
"server.js",
|
|
204
|
+
"index.ts",
|
|
205
|
+
"index.js"
|
|
206
|
+
];
|
|
207
|
+
const scoredServers = this.scoreServerCandidates(serverCandidates);
|
|
208
|
+
if (scoredServers.length > 0) {
|
|
209
|
+
const best = scoredServers[0];
|
|
210
|
+
entryPoints.server = best.path;
|
|
211
|
+
if (best.hasWebSocket) {
|
|
212
|
+
contextMapping.server = "WebSocket Server";
|
|
213
|
+
} else if (best.hasHTTP) {
|
|
214
|
+
contextMapping.server = "HTTP Server";
|
|
215
|
+
} else {
|
|
216
|
+
contextMapping.server = "Server";
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const clientCandidates = [
|
|
220
|
+
"src/client/index.ts",
|
|
221
|
+
"src/client/index.js",
|
|
222
|
+
"src/client.ts",
|
|
223
|
+
"src/client.js",
|
|
224
|
+
"client/index.ts",
|
|
225
|
+
"client/index.js"
|
|
226
|
+
];
|
|
227
|
+
for (const candidate of clientCandidates) {
|
|
228
|
+
const fullPath = path3.join(this.projectRoot, candidate);
|
|
229
|
+
if (fs3.existsSync(fullPath)) {
|
|
230
|
+
entryPoints.client = fullPath;
|
|
231
|
+
contextMapping.client = "Client";
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
type: "websocket-app",
|
|
237
|
+
entryPoints,
|
|
238
|
+
contextMapping: Object.keys(contextMapping).length > 0 ? contextMapping : {
|
|
239
|
+
server: "Server",
|
|
240
|
+
client: "Client"
|
|
241
|
+
},
|
|
242
|
+
metadata: {
|
|
243
|
+
name: packageJson.name,
|
|
244
|
+
version: packageJson.version,
|
|
245
|
+
description: packageJson.description
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
scoreServerCandidates(candidates) {
|
|
250
|
+
const scored = [];
|
|
251
|
+
for (const candidate of candidates) {
|
|
252
|
+
const fullPath = path3.join(this.projectRoot, candidate);
|
|
253
|
+
if (!fs3.existsSync(fullPath))
|
|
254
|
+
continue;
|
|
255
|
+
try {
|
|
256
|
+
const content = fs3.readFileSync(fullPath, "utf-8");
|
|
257
|
+
let score = 0;
|
|
258
|
+
let hasWebSocket = false;
|
|
259
|
+
let hasHTTP = false;
|
|
260
|
+
let framework = null;
|
|
261
|
+
const patterns = {
|
|
262
|
+
bunWebSocket: /Bun\.serve\s*\([^)]*websocket\s*:/i,
|
|
263
|
+
bunHTTP: /Bun\.serve\s*\(/i,
|
|
264
|
+
wsServer: /new\s+WebSocket\.Server/i,
|
|
265
|
+
wsImport: /from\s+['"]ws['"]/,
|
|
266
|
+
socketIO: /io\s*\(|require\s*\(\s*['"]socket\.io['"]\s*\)/i,
|
|
267
|
+
elysia: /new\s+Elysia\s*\(|\.ws\s*\(/i,
|
|
268
|
+
elysiaImport: /from\s+['"]elysia['"]/,
|
|
269
|
+
express: /express\s*\(\)|app\.listen/i,
|
|
270
|
+
expressWs: /expressWs\s*\(/i,
|
|
271
|
+
httpServer: /createServer|app\.listen|server\.listen/i,
|
|
272
|
+
webSocket: /WebSocket|websocket|\.ws\s*\(|wss\s*:/i
|
|
273
|
+
};
|
|
274
|
+
if (patterns.bunWebSocket.test(content)) {
|
|
275
|
+
score += 15;
|
|
276
|
+
hasWebSocket = true;
|
|
277
|
+
hasHTTP = true;
|
|
278
|
+
framework = "Bun";
|
|
279
|
+
} else if (patterns.bunHTTP.test(content)) {
|
|
280
|
+
score += 10;
|
|
281
|
+
hasHTTP = true;
|
|
282
|
+
framework = "Bun";
|
|
283
|
+
}
|
|
284
|
+
if (patterns.wsServer.test(content) || patterns.wsImport.test(content)) {
|
|
285
|
+
score += 12;
|
|
286
|
+
hasWebSocket = true;
|
|
287
|
+
framework = framework || "ws";
|
|
288
|
+
}
|
|
289
|
+
if (patterns.socketIO.test(content)) {
|
|
290
|
+
score += 12;
|
|
291
|
+
hasWebSocket = true;
|
|
292
|
+
framework = framework || "socket.io";
|
|
293
|
+
}
|
|
294
|
+
if (patterns.elysia.test(content) || patterns.elysiaImport.test(content)) {
|
|
295
|
+
score += 10;
|
|
296
|
+
hasHTTP = true;
|
|
297
|
+
framework = framework || "Elysia";
|
|
298
|
+
}
|
|
299
|
+
if (patterns.elysiaImport.test(content) && patterns.webSocket.test(content)) {
|
|
300
|
+
score += 8;
|
|
301
|
+
hasWebSocket = true;
|
|
302
|
+
}
|
|
303
|
+
if (patterns.express.test(content)) {
|
|
304
|
+
score += 8;
|
|
305
|
+
hasHTTP = true;
|
|
306
|
+
framework = framework || "Express";
|
|
307
|
+
}
|
|
308
|
+
if (patterns.expressWs.test(content)) {
|
|
309
|
+
score += 5;
|
|
310
|
+
hasWebSocket = true;
|
|
311
|
+
}
|
|
312
|
+
if (patterns.httpServer.test(content) && !hasHTTP) {
|
|
313
|
+
score += 5;
|
|
314
|
+
hasHTTP = true;
|
|
315
|
+
}
|
|
316
|
+
if (patterns.webSocket.test(content) && !hasWebSocket) {
|
|
317
|
+
score += 3;
|
|
318
|
+
hasWebSocket = true;
|
|
319
|
+
}
|
|
320
|
+
if (/\.listen\s*\(/.test(content)) {
|
|
321
|
+
score += 5;
|
|
322
|
+
}
|
|
323
|
+
if (/export\s+default/.test(content)) {
|
|
324
|
+
score += 3;
|
|
325
|
+
}
|
|
326
|
+
if (candidate.includes("server")) {
|
|
327
|
+
score += 3;
|
|
328
|
+
}
|
|
329
|
+
if (candidate === "src/index.ts" || candidate === "src/index.js") {
|
|
330
|
+
score += 2;
|
|
331
|
+
}
|
|
332
|
+
if (score > 0) {
|
|
333
|
+
scored.push({ path: fullPath, score, hasWebSocket, hasHTTP, framework });
|
|
334
|
+
}
|
|
335
|
+
} catch (error) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return scored.sort((a, b) => b.score - a.score);
|
|
340
|
+
}
|
|
341
|
+
detectGenericProject() {
|
|
342
|
+
const entryPoints = {};
|
|
343
|
+
const tsConfigPath = path3.join(this.projectRoot, "tsconfig.json");
|
|
344
|
+
if (fs3.existsSync(tsConfigPath)) {
|
|
345
|
+
try {
|
|
346
|
+
const tsConfig = JSON.parse(fs3.readFileSync(tsConfigPath, "utf-8"));
|
|
347
|
+
if (tsConfig.files && Array.isArray(tsConfig.files)) {
|
|
348
|
+
tsConfig.files.forEach((file, idx) => {
|
|
349
|
+
const fullPath = path3.join(this.projectRoot, file);
|
|
350
|
+
if (fs3.existsSync(fullPath)) {
|
|
351
|
+
entryPoints[`module${idx + 1}`] = fullPath;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.warn(`Failed to parse tsconfig.json: ${error}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (Object.keys(entryPoints).length === 0) {
|
|
360
|
+
const commonEntries = [
|
|
361
|
+
"src/index.ts",
|
|
362
|
+
"src/main.ts",
|
|
363
|
+
"src/app.ts",
|
|
364
|
+
"index.ts",
|
|
365
|
+
"main.ts"
|
|
366
|
+
];
|
|
367
|
+
for (const candidate of commonEntries) {
|
|
368
|
+
const fullPath = path3.join(this.projectRoot, candidate);
|
|
369
|
+
if (fs3.existsSync(fullPath)) {
|
|
370
|
+
entryPoints.main = fullPath;
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const packageJsonPath = path3.join(this.projectRoot, "package.json");
|
|
376
|
+
let metadata = { name: "Unknown Project" };
|
|
377
|
+
if (fs3.existsSync(packageJsonPath)) {
|
|
378
|
+
try {
|
|
379
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
380
|
+
metadata = {
|
|
381
|
+
name: packageJson.name,
|
|
382
|
+
version: packageJson.version,
|
|
383
|
+
description: packageJson.description
|
|
384
|
+
};
|
|
385
|
+
} catch (error) {
|
|
386
|
+
console.warn(`Failed to parse package.json: ${error}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
type: "generic",
|
|
391
|
+
entryPoints,
|
|
392
|
+
metadata
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
findSourceFile(manifestPath) {
|
|
396
|
+
const candidates = [
|
|
397
|
+
path3.join(this.projectRoot, manifestPath),
|
|
398
|
+
path3.join(this.projectRoot, manifestPath.replace(/\.js$/, ".ts")),
|
|
399
|
+
path3.join(this.projectRoot, "src", manifestPath),
|
|
400
|
+
path3.join(this.projectRoot, "src", manifestPath.replace(/\.js$/, ".ts")),
|
|
401
|
+
path3.join(this.projectRoot, "src", manifestPath.replace(/\.js$/, ".tsx"))
|
|
402
|
+
];
|
|
403
|
+
for (const candidate of candidates) {
|
|
404
|
+
if (fs3.existsSync(candidate)) {
|
|
405
|
+
return candidate;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return path3.join(this.projectRoot, manifestPath);
|
|
409
|
+
}
|
|
410
|
+
findAssociatedJS(htmlPath) {
|
|
411
|
+
if (!fs3.existsSync(htmlPath)) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
const html = fs3.readFileSync(htmlPath, "utf-8");
|
|
415
|
+
const scriptMatch = html.match(/<script[^>]+src=["']([^"']+)["']/i);
|
|
416
|
+
if (scriptMatch && scriptMatch[1]) {
|
|
417
|
+
const scriptPath = scriptMatch[1];
|
|
418
|
+
const fullPath = path3.resolve(path3.dirname(htmlPath), scriptPath);
|
|
419
|
+
if (fs3.existsSync(fullPath))
|
|
420
|
+
return fullPath;
|
|
421
|
+
if (fs3.existsSync(fullPath.replace(/\.js$/, ".ts"))) {
|
|
422
|
+
return fullPath.replace(/\.js$/, ".ts");
|
|
423
|
+
}
|
|
424
|
+
if (fs3.existsSync(fullPath.replace(/\.js$/, ".tsx"))) {
|
|
425
|
+
return fullPath.replace(/\.js$/, ".tsx");
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
const baseName = path3.basename(htmlPath, ".html");
|
|
429
|
+
const dir = path3.dirname(htmlPath);
|
|
430
|
+
const candidates = [
|
|
431
|
+
path3.join(dir, `${baseName}.ts`),
|
|
432
|
+
path3.join(dir, `${baseName}.tsx`),
|
|
433
|
+
path3.join(dir, `${baseName}.js`),
|
|
434
|
+
path3.join(dir, "index.ts"),
|
|
435
|
+
path3.join(dir, "index.tsx")
|
|
436
|
+
];
|
|
437
|
+
for (const candidate of candidates) {
|
|
438
|
+
if (fs3.existsSync(candidate)) {
|
|
439
|
+
return candidate;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function detectProjectConfig(projectRoot) {
|
|
446
|
+
const detector = new ProjectDetector(projectRoot);
|
|
447
|
+
return detector.detect();
|
|
448
|
+
}
|
|
449
|
+
var init_project_detector = () => {};
|
|
450
|
+
|
|
16
451
|
// vendor/visualize/src/cli.ts
|
|
17
|
-
import * as
|
|
18
|
-
import * as
|
|
452
|
+
import * as fs6 from "node:fs";
|
|
453
|
+
import * as path6 from "node:path";
|
|
19
454
|
|
|
20
455
|
// vendor/analysis/src/extract/architecture.ts
|
|
21
|
-
import * as
|
|
22
|
-
import * as
|
|
456
|
+
import * as fs4 from "node:fs";
|
|
457
|
+
import * as path4 from "node:path";
|
|
23
458
|
|
|
24
459
|
// vendor/analysis/src/extract/manifest.ts
|
|
25
460
|
import * as fs from "node:fs";
|
|
@@ -29,10 +464,14 @@ class ManifestParser {
|
|
|
29
464
|
manifestPath;
|
|
30
465
|
manifestData;
|
|
31
466
|
baseDir;
|
|
32
|
-
constructor(projectRoot) {
|
|
467
|
+
constructor(projectRoot, optional = false) {
|
|
33
468
|
this.baseDir = projectRoot;
|
|
34
469
|
this.manifestPath = path.join(projectRoot, "manifest.json");
|
|
35
470
|
if (!fs.existsSync(this.manifestPath)) {
|
|
471
|
+
if (optional) {
|
|
472
|
+
this.manifestData = null;
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
36
475
|
throw new Error(`manifest.json not found at ${this.manifestPath}`);
|
|
37
476
|
}
|
|
38
477
|
try {
|
|
@@ -42,7 +481,13 @@ class ManifestParser {
|
|
|
42
481
|
throw new Error(`Failed to parse manifest.json: ${error}`);
|
|
43
482
|
}
|
|
44
483
|
}
|
|
484
|
+
hasManifest() {
|
|
485
|
+
return this.manifestData !== null;
|
|
486
|
+
}
|
|
45
487
|
parse() {
|
|
488
|
+
if (!this.manifestData) {
|
|
489
|
+
throw new Error("Cannot parse manifest: manifest.json not loaded. Use hasManifest() to check availability.");
|
|
490
|
+
}
|
|
46
491
|
const manifest = this.manifestData;
|
|
47
492
|
return {
|
|
48
493
|
name: manifest.name || "Unknown Extension",
|
|
@@ -59,6 +504,9 @@ class ManifestParser {
|
|
|
59
504
|
};
|
|
60
505
|
}
|
|
61
506
|
getContextEntryPoints() {
|
|
507
|
+
if (!this.manifestData) {
|
|
508
|
+
return {};
|
|
509
|
+
}
|
|
62
510
|
const entryPoints = {};
|
|
63
511
|
const background = this.parseBackground();
|
|
64
512
|
if (background) {
|
|
@@ -101,6 +549,8 @@ class ManifestParser {
|
|
|
101
549
|
return entryPoints;
|
|
102
550
|
}
|
|
103
551
|
parseBackground() {
|
|
552
|
+
if (!this.manifestData)
|
|
553
|
+
return;
|
|
104
554
|
const bg = this.manifestData.background;
|
|
105
555
|
if (!bg)
|
|
106
556
|
return;
|
|
@@ -125,6 +575,8 @@ class ManifestParser {
|
|
|
125
575
|
return;
|
|
126
576
|
}
|
|
127
577
|
parseContentScripts() {
|
|
578
|
+
if (!this.manifestData)
|
|
579
|
+
return;
|
|
128
580
|
const cs = this.manifestData.content_scripts;
|
|
129
581
|
if (!cs || !Array.isArray(cs))
|
|
130
582
|
return;
|
|
@@ -135,6 +587,8 @@ class ManifestParser {
|
|
|
135
587
|
}));
|
|
136
588
|
}
|
|
137
589
|
parsePopup() {
|
|
590
|
+
if (!this.manifestData)
|
|
591
|
+
return;
|
|
138
592
|
const action = this.manifestData.action || this.manifestData.browser_action;
|
|
139
593
|
if (!action)
|
|
140
594
|
return;
|
|
@@ -147,6 +601,8 @@ class ManifestParser {
|
|
|
147
601
|
return;
|
|
148
602
|
}
|
|
149
603
|
parseOptions() {
|
|
604
|
+
if (!this.manifestData)
|
|
605
|
+
return;
|
|
150
606
|
const options = this.manifestData.options_ui || this.manifestData.options_page;
|
|
151
607
|
if (!options)
|
|
152
608
|
return;
|
|
@@ -162,6 +618,8 @@ class ManifestParser {
|
|
|
162
618
|
};
|
|
163
619
|
}
|
|
164
620
|
parseDevtools() {
|
|
621
|
+
if (!this.manifestData)
|
|
622
|
+
return;
|
|
165
623
|
const devtools = this.manifestData.devtools_page;
|
|
166
624
|
if (!devtools)
|
|
167
625
|
return;
|
|
@@ -466,7 +924,34 @@ class FlowAnalyzer {
|
|
|
466
924
|
const expression = node.getExpression();
|
|
467
925
|
if (Node2.isPropertyAccessExpression(expression)) {
|
|
468
926
|
const methodName = expression.getName();
|
|
469
|
-
if (methodName === "send" || methodName === "emit") {
|
|
927
|
+
if (methodName === "send" || methodName === "emit" || methodName === "postMessage" || methodName === "broadcast") {
|
|
928
|
+
const args = node.getArguments();
|
|
929
|
+
if (args.length > 0) {
|
|
930
|
+
const firstArg = args[0];
|
|
931
|
+
let msgType;
|
|
932
|
+
if (Node2.isStringLiteral(firstArg)) {
|
|
933
|
+
msgType = firstArg.getLiteralValue();
|
|
934
|
+
} else if (Node2.isObjectLiteralExpression(firstArg)) {
|
|
935
|
+
const typeProperty = firstArg.getProperty("type");
|
|
936
|
+
if (typeProperty && Node2.isPropertyAssignment(typeProperty)) {
|
|
937
|
+
const initializer = typeProperty.getInitializer();
|
|
938
|
+
if (initializer && Node2.isStringLiteral(initializer)) {
|
|
939
|
+
msgType = initializer.getLiteralValue();
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
if (msgType === messageType) {
|
|
944
|
+
senders.push({
|
|
945
|
+
context,
|
|
946
|
+
file: filePath,
|
|
947
|
+
line: node.getStartLineNumber()
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
if (Node2.isIdentifier(expression)) {
|
|
954
|
+
if (expression.getText() === "postMessage") {
|
|
470
955
|
const args = node.getArguments();
|
|
471
956
|
if (args.length > 0) {
|
|
472
957
|
const firstArg = args[0];
|
|
@@ -618,6 +1103,15 @@ class FlowAnalyzer {
|
|
|
618
1103
|
if (path2.includes("/offscreen/") || path2.includes("\\offscreen\\")) {
|
|
619
1104
|
return "offscreen";
|
|
620
1105
|
}
|
|
1106
|
+
if (path2.includes("/server/") || path2.includes("\\server\\") || path2.includes("/server.")) {
|
|
1107
|
+
return "server";
|
|
1108
|
+
}
|
|
1109
|
+
if (path2.includes("/client/") || path2.includes("\\client\\") || path2.includes("/client.")) {
|
|
1110
|
+
return "client";
|
|
1111
|
+
}
|
|
1112
|
+
if (path2.includes("/worker/") || path2.includes("\\worker\\") || path2.includes("service-worker")) {
|
|
1113
|
+
return "worker";
|
|
1114
|
+
}
|
|
621
1115
|
return "unknown";
|
|
622
1116
|
}
|
|
623
1117
|
}
|
|
@@ -854,7 +1348,7 @@ class HandlerExtractor {
|
|
|
854
1348
|
const expression = node.getExpression();
|
|
855
1349
|
if (Node4.isPropertyAccessExpression(expression)) {
|
|
856
1350
|
const methodName = expression.getName();
|
|
857
|
-
if (methodName === "on") {
|
|
1351
|
+
if (methodName === "on" || methodName === "addEventListener") {
|
|
858
1352
|
const handler = this.extractHandler(node, context, filePath);
|
|
859
1353
|
if (handler) {
|
|
860
1354
|
handlers.push(handler);
|
|
@@ -862,6 +1356,14 @@ class HandlerExtractor {
|
|
|
862
1356
|
}
|
|
863
1357
|
}
|
|
864
1358
|
}
|
|
1359
|
+
if (Node4.isSwitchStatement(node)) {
|
|
1360
|
+
const switchHandlers = this.extractSwitchCaseHandlers(node, context, filePath);
|
|
1361
|
+
handlers.push(...switchHandlers);
|
|
1362
|
+
}
|
|
1363
|
+
if (Node4.isVariableDeclaration(node)) {
|
|
1364
|
+
const mapHandlers = this.extractHandlerMapPattern(node, context, filePath);
|
|
1365
|
+
handlers.push(...mapHandlers);
|
|
1366
|
+
}
|
|
865
1367
|
});
|
|
866
1368
|
return handlers;
|
|
867
1369
|
}
|
|
@@ -1003,6 +1505,75 @@ class HandlerExtractor {
|
|
|
1003
1505
|
}
|
|
1004
1506
|
return;
|
|
1005
1507
|
}
|
|
1508
|
+
extractSwitchCaseHandlers(switchNode, context, filePath) {
|
|
1509
|
+
const handlers = [];
|
|
1510
|
+
try {
|
|
1511
|
+
const expression = switchNode.getExpression();
|
|
1512
|
+
const expressionText = expression.getText();
|
|
1513
|
+
if (!/\.(type|kind|event|action)/.test(expressionText)) {
|
|
1514
|
+
return handlers;
|
|
1515
|
+
}
|
|
1516
|
+
const caseClauses = switchNode.getClauses();
|
|
1517
|
+
for (const clause of caseClauses) {
|
|
1518
|
+
if (Node4.isCaseClause(clause)) {
|
|
1519
|
+
const caseExpr = clause.getExpression();
|
|
1520
|
+
let messageType = null;
|
|
1521
|
+
if (Node4.isStringLiteral(caseExpr)) {
|
|
1522
|
+
messageType = caseExpr.getLiteralValue();
|
|
1523
|
+
}
|
|
1524
|
+
if (messageType) {
|
|
1525
|
+
const line = clause.getStartLineNumber();
|
|
1526
|
+
handlers.push({
|
|
1527
|
+
messageType,
|
|
1528
|
+
node: context,
|
|
1529
|
+
assignments: [],
|
|
1530
|
+
preconditions: [],
|
|
1531
|
+
postconditions: [],
|
|
1532
|
+
location: { file: filePath, line }
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
} catch (error) {}
|
|
1538
|
+
return handlers;
|
|
1539
|
+
}
|
|
1540
|
+
extractHandlerMapPattern(varDecl, context, filePath) {
|
|
1541
|
+
const handlers = [];
|
|
1542
|
+
try {
|
|
1543
|
+
const initializer = varDecl.getInitializer();
|
|
1544
|
+
if (!initializer || !Node4.isObjectLiteralExpression(initializer)) {
|
|
1545
|
+
return handlers;
|
|
1546
|
+
}
|
|
1547
|
+
const varName = varDecl.getName().toLowerCase();
|
|
1548
|
+
if (!/(handler|listener|callback|event)s?/.test(varName)) {
|
|
1549
|
+
return handlers;
|
|
1550
|
+
}
|
|
1551
|
+
const properties = initializer.getProperties();
|
|
1552
|
+
for (const prop of properties) {
|
|
1553
|
+
if (Node4.isPropertyAssignment(prop)) {
|
|
1554
|
+
const nameNode = prop.getNameNode();
|
|
1555
|
+
let messageType = null;
|
|
1556
|
+
if (Node4.isStringLiteral(nameNode)) {
|
|
1557
|
+
messageType = nameNode.getLiteralValue();
|
|
1558
|
+
} else if (Node4.isIdentifier(nameNode)) {
|
|
1559
|
+
messageType = nameNode.getText();
|
|
1560
|
+
}
|
|
1561
|
+
if (messageType) {
|
|
1562
|
+
const line = prop.getStartLineNumber();
|
|
1563
|
+
handlers.push({
|
|
1564
|
+
messageType,
|
|
1565
|
+
node: context,
|
|
1566
|
+
assignments: [],
|
|
1567
|
+
preconditions: [],
|
|
1568
|
+
postconditions: [],
|
|
1569
|
+
location: { file: filePath, line }
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
} catch (error) {}
|
|
1575
|
+
return handlers;
|
|
1576
|
+
}
|
|
1006
1577
|
inferContext(filePath) {
|
|
1007
1578
|
const path2 = filePath.toLowerCase();
|
|
1008
1579
|
if (path2.includes("/background/") || path2.includes("\\background\\")) {
|
|
@@ -1023,6 +1594,15 @@ class HandlerExtractor {
|
|
|
1023
1594
|
if (path2.includes("/offscreen/") || path2.includes("\\offscreen\\")) {
|
|
1024
1595
|
return "offscreen";
|
|
1025
1596
|
}
|
|
1597
|
+
if (path2.includes("/server/") || path2.includes("\\server\\") || path2.includes("/server.")) {
|
|
1598
|
+
return "server";
|
|
1599
|
+
}
|
|
1600
|
+
if (path2.includes("/client/") || path2.includes("\\client\\") || path2.includes("/client.")) {
|
|
1601
|
+
return "client";
|
|
1602
|
+
}
|
|
1603
|
+
if (path2.includes("/worker/") || path2.includes("\\worker\\") || path2.includes("service-worker")) {
|
|
1604
|
+
return "worker";
|
|
1605
|
+
}
|
|
1026
1606
|
return "unknown";
|
|
1027
1607
|
}
|
|
1028
1608
|
}
|
|
@@ -1168,9 +1748,29 @@ class ArchitectureAnalyzer {
|
|
|
1168
1748
|
this.options = options;
|
|
1169
1749
|
}
|
|
1170
1750
|
async analyze() {
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1751
|
+
let manifest;
|
|
1752
|
+
let projectConfig;
|
|
1753
|
+
let entryPoints = {};
|
|
1754
|
+
let systemInfo;
|
|
1755
|
+
const manifestParser = new ManifestParser(this.options.projectRoot, true);
|
|
1756
|
+
if (manifestParser.hasManifest() && !this.options.useProjectDetector) {
|
|
1757
|
+
manifest = manifestParser.parse();
|
|
1758
|
+
entryPoints = manifestParser.getContextEntryPoints();
|
|
1759
|
+
systemInfo = {
|
|
1760
|
+
name: manifest.name,
|
|
1761
|
+
version: manifest.version,
|
|
1762
|
+
description: manifest.description
|
|
1763
|
+
};
|
|
1764
|
+
} else {
|
|
1765
|
+
const { detectProjectConfig: detectProjectConfig2 } = await Promise.resolve().then(() => (init_project_detector(), exports_project_detector));
|
|
1766
|
+
projectConfig = detectProjectConfig2(this.options.projectRoot);
|
|
1767
|
+
entryPoints = projectConfig.entryPoints;
|
|
1768
|
+
systemInfo = {
|
|
1769
|
+
name: projectConfig.metadata?.name || "Unknown Project",
|
|
1770
|
+
version: projectConfig.metadata?.version || "0.0.0",
|
|
1771
|
+
description: projectConfig.metadata?.description
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1174
1774
|
const handlerExtractor = new HandlerExtractor(this.options.tsConfigPath);
|
|
1175
1775
|
const { handlers } = handlerExtractor.extractHandlers();
|
|
1176
1776
|
const contextAnalyzer = new ContextAnalyzer(this.options.tsConfigPath);
|
|
@@ -1191,12 +1791,9 @@ class ArchitectureAnalyzer {
|
|
|
1191
1791
|
const adrs = extractADRs(this.options.projectRoot);
|
|
1192
1792
|
const repository = this.extractRepositoryInfo();
|
|
1193
1793
|
return {
|
|
1194
|
-
system:
|
|
1195
|
-
name: manifest.name,
|
|
1196
|
-
version: manifest.version,
|
|
1197
|
-
description: manifest.description
|
|
1198
|
-
},
|
|
1794
|
+
system: systemInfo,
|
|
1199
1795
|
manifest,
|
|
1796
|
+
projectConfig,
|
|
1200
1797
|
contexts,
|
|
1201
1798
|
messageFlows,
|
|
1202
1799
|
integrations,
|
|
@@ -1219,12 +1816,12 @@ class ArchitectureAnalyzer {
|
|
|
1219
1816
|
}
|
|
1220
1817
|
}
|
|
1221
1818
|
extractRepositoryInfo() {
|
|
1222
|
-
const packageJsonPath =
|
|
1223
|
-
if (!
|
|
1819
|
+
const packageJsonPath = path4.join(this.options.projectRoot, "package.json");
|
|
1820
|
+
if (!fs4.existsSync(packageJsonPath)) {
|
|
1224
1821
|
return;
|
|
1225
1822
|
}
|
|
1226
1823
|
try {
|
|
1227
|
-
const content =
|
|
1824
|
+
const content = fs4.readFileSync(packageJsonPath, "utf-8");
|
|
1228
1825
|
const packageJson = JSON.parse(content);
|
|
1229
1826
|
if (packageJson.repository) {
|
|
1230
1827
|
if (typeof packageJson.repository === "string") {
|
|
@@ -1724,8 +2321,8 @@ function generateStructurizrDSL(analysis, options) {
|
|
|
1724
2321
|
}
|
|
1725
2322
|
|
|
1726
2323
|
// vendor/visualize/src/runner/export.ts
|
|
1727
|
-
import * as
|
|
1728
|
-
import * as
|
|
2324
|
+
import * as fs5 from "node:fs";
|
|
2325
|
+
import * as path5 from "node:path";
|
|
1729
2326
|
import { spawn } from "node:child_process";
|
|
1730
2327
|
|
|
1731
2328
|
class DiagramExporter {
|
|
@@ -1733,15 +2330,15 @@ class DiagramExporter {
|
|
|
1733
2330
|
static DEFAULT_TIMEOUT = 120000;
|
|
1734
2331
|
async export(options) {
|
|
1735
2332
|
const { dslPath, outputDir, timeout = DiagramExporter.DEFAULT_TIMEOUT } = options;
|
|
1736
|
-
if (!
|
|
2333
|
+
if (!fs5.existsSync(dslPath)) {
|
|
1737
2334
|
return {
|
|
1738
2335
|
success: false,
|
|
1739
2336
|
siteDir: "",
|
|
1740
2337
|
error: `DSL file not found: ${dslPath}`
|
|
1741
2338
|
};
|
|
1742
2339
|
}
|
|
1743
|
-
if (!
|
|
1744
|
-
|
|
2340
|
+
if (!fs5.existsSync(outputDir)) {
|
|
2341
|
+
fs5.mkdirSync(outputDir, { recursive: true });
|
|
1745
2342
|
}
|
|
1746
2343
|
const dockerAvailable = await this.isDockerAvailable();
|
|
1747
2344
|
if (!dockerAvailable) {
|
|
@@ -1774,8 +2371,8 @@ class DiagramExporter {
|
|
|
1774
2371
|
}
|
|
1775
2372
|
}
|
|
1776
2373
|
async exportStaticSite(dslPath, outputDir, timeout) {
|
|
1777
|
-
const dslDir =
|
|
1778
|
-
const dslFileName =
|
|
2374
|
+
const dslDir = path5.dirname(dslPath);
|
|
2375
|
+
const dslFileName = path5.basename(dslPath);
|
|
1779
2376
|
const workspaceMount = `${dslDir}:/usr/local/structurizr`;
|
|
1780
2377
|
const outputMount = `${outputDir}:/output`;
|
|
1781
2378
|
const args = [
|
|
@@ -1813,7 +2410,7 @@ class DiagramExporter {
|
|
|
1813
2410
|
}
|
|
1814
2411
|
}
|
|
1815
2412
|
async pullImage(onProgress) {
|
|
1816
|
-
return new Promise((
|
|
2413
|
+
return new Promise((resolve3, reject) => {
|
|
1817
2414
|
const proc = spawn("docker", ["pull", DiagramExporter.DOCKER_IMAGE]);
|
|
1818
2415
|
proc.stdout.on("data", (data) => {
|
|
1819
2416
|
onProgress?.(data.toString().trim());
|
|
@@ -1823,7 +2420,7 @@ class DiagramExporter {
|
|
|
1823
2420
|
});
|
|
1824
2421
|
proc.on("close", (code) => {
|
|
1825
2422
|
if (code === 0) {
|
|
1826
|
-
|
|
2423
|
+
resolve3();
|
|
1827
2424
|
} else {
|
|
1828
2425
|
reject(new Error(`Failed to pull image, exit code: ${code}`));
|
|
1829
2426
|
}
|
|
@@ -1837,7 +2434,7 @@ class DiagramExporter {
|
|
|
1837
2434
|
return this.runCommand("docker", args, timeout);
|
|
1838
2435
|
}
|
|
1839
2436
|
async runCommand(command, args, timeout) {
|
|
1840
|
-
return new Promise((
|
|
2437
|
+
return new Promise((resolve3, reject) => {
|
|
1841
2438
|
const proc = spawn(command, args);
|
|
1842
2439
|
let stdout = "";
|
|
1843
2440
|
let stderr = "";
|
|
@@ -1854,7 +2451,7 @@ class DiagramExporter {
|
|
|
1854
2451
|
proc.on("close", (code) => {
|
|
1855
2452
|
clearTimeout(timer);
|
|
1856
2453
|
if (code === 0) {
|
|
1857
|
-
|
|
2454
|
+
resolve3(stdout);
|
|
1858
2455
|
} else {
|
|
1859
2456
|
reject(new Error(`Command failed with exit code ${code}: ${stderr}`));
|
|
1860
2457
|
}
|
|
@@ -1923,11 +2520,17 @@ async function generateCommand() {
|
|
|
1923
2520
|
console.log(color(` Using: ${tsConfigPath}`, COLORS.gray));
|
|
1924
2521
|
const projectRoot = findProjectRoot();
|
|
1925
2522
|
if (!projectRoot) {
|
|
1926
|
-
console.error(color("❌ Could not find
|
|
1927
|
-
console.error(" Run this command from
|
|
2523
|
+
console.error(color("❌ Could not find project root", COLORS.red));
|
|
2524
|
+
console.error(" Run this command from a directory with manifest.json, package.json, or tsconfig.json");
|
|
1928
2525
|
process.exit(1);
|
|
1929
2526
|
}
|
|
1930
2527
|
console.log(color(` Project: ${projectRoot}`, COLORS.gray));
|
|
2528
|
+
const hasManifest = fs6.existsSync(path6.join(projectRoot, "manifest.json"));
|
|
2529
|
+
if (hasManifest) {
|
|
2530
|
+
console.log(color(` Type: Chrome Extension`, COLORS.gray));
|
|
2531
|
+
} else {
|
|
2532
|
+
console.log(color(` Type: Detecting from project structure...`, COLORS.gray));
|
|
2533
|
+
}
|
|
1931
2534
|
const analysis = await analyzeArchitecture({
|
|
1932
2535
|
tsConfigPath,
|
|
1933
2536
|
projectRoot
|
|
@@ -1963,12 +2566,12 @@ async function generateCommand() {
|
|
|
1963
2566
|
includeComponentDiagrams: true,
|
|
1964
2567
|
componentDiagramContexts: ["background"]
|
|
1965
2568
|
});
|
|
1966
|
-
const outputDir =
|
|
1967
|
-
if (!
|
|
1968
|
-
|
|
2569
|
+
const outputDir = path6.join(process.cwd(), "docs");
|
|
2570
|
+
if (!fs6.existsSync(outputDir)) {
|
|
2571
|
+
fs6.mkdirSync(outputDir, { recursive: true });
|
|
1969
2572
|
}
|
|
1970
|
-
const dslPath =
|
|
1971
|
-
|
|
2573
|
+
const dslPath = path6.join(outputDir, "architecture.dsl");
|
|
2574
|
+
fs6.writeFileSync(dslPath, dsl, "utf-8");
|
|
1972
2575
|
console.log(color(`✅ Architecture documentation generated!
|
|
1973
2576
|
`, COLORS.green));
|
|
1974
2577
|
console.log(` File: ${color(dslPath, COLORS.blue)}`);
|
|
@@ -2010,14 +2613,14 @@ async function exportCommand(args) {
|
|
|
2010
2613
|
\uD83D\uDCE4 Generating static site...
|
|
2011
2614
|
`, COLORS.blue));
|
|
2012
2615
|
try {
|
|
2013
|
-
const dslPath =
|
|
2014
|
-
if (!
|
|
2616
|
+
const dslPath = path6.join(process.cwd(), "docs", "architecture.dsl");
|
|
2617
|
+
if (!fs6.existsSync(dslPath)) {
|
|
2015
2618
|
console.error(color("❌ DSL file not found", COLORS.red));
|
|
2016
2619
|
console.error(" Expected: docs/architecture.dsl");
|
|
2017
2620
|
console.error(" Run 'bun visualize' first to generate the DSL");
|
|
2018
2621
|
process.exit(1);
|
|
2019
2622
|
}
|
|
2020
|
-
const outputDir =
|
|
2623
|
+
const outputDir = path6.join(process.cwd(), "docs", "site");
|
|
2021
2624
|
console.log(color(` DSL: ${dslPath}`, COLORS.gray));
|
|
2022
2625
|
console.log(color(` Output: ${outputDir}`, COLORS.gray));
|
|
2023
2626
|
console.log();
|
|
@@ -2061,9 +2664,9 @@ async function serveCommand(args) {
|
|
|
2061
2664
|
\uD83C\uDF10 Starting static site server...
|
|
2062
2665
|
`, COLORS.blue));
|
|
2063
2666
|
try {
|
|
2064
|
-
const siteDir =
|
|
2065
|
-
const indexPath =
|
|
2066
|
-
if (!
|
|
2667
|
+
const siteDir = path6.join(process.cwd(), "docs", "site");
|
|
2668
|
+
const indexPath = path6.join(siteDir, "index.html");
|
|
2669
|
+
if (!fs6.existsSync(indexPath)) {
|
|
2067
2670
|
console.error(color("❌ Static site not found", COLORS.red));
|
|
2068
2671
|
console.error(" Expected: docs/site/index.html");
|
|
2069
2672
|
console.error(" Run 'bun visualize --export' first to generate the site");
|
|
@@ -2082,8 +2685,8 @@ async function serveCommand(args) {
|
|
|
2082
2685
|
port,
|
|
2083
2686
|
fetch(req) {
|
|
2084
2687
|
const url = new URL(req.url);
|
|
2085
|
-
let filePath =
|
|
2086
|
-
if (
|
|
2688
|
+
let filePath = path6.join(siteDir, url.pathname === "/" ? "index.html" : url.pathname);
|
|
2689
|
+
if (fs6.existsSync(filePath) && fs6.statSync(filePath).isFile()) {
|
|
2087
2690
|
const file = BunGlobal.file(filePath);
|
|
2088
2691
|
return new Response(file);
|
|
2089
2692
|
}
|
|
@@ -2114,7 +2717,15 @@ async function serveCommand(args) {
|
|
|
2114
2717
|
}
|
|
2115
2718
|
function showHelp() {
|
|
2116
2719
|
console.log(`
|
|
2117
|
-
${color("bun visualize", COLORS.blue)} - Architecture visualization
|
|
2720
|
+
${color("bun visualize", COLORS.blue)} - Architecture visualization tool
|
|
2721
|
+
|
|
2722
|
+
${color("Supports:", COLORS.blue)}
|
|
2723
|
+
|
|
2724
|
+
• Chrome Extensions (manifest.json)
|
|
2725
|
+
• PWAs (public/manifest.json)
|
|
2726
|
+
• WebSocket/Server Apps (ws, socket.io, elysia)
|
|
2727
|
+
• Electron Apps
|
|
2728
|
+
• Generic TypeScript Projects
|
|
2118
2729
|
|
|
2119
2730
|
${color("Commands:", COLORS.blue)}
|
|
2120
2731
|
|
|
@@ -2134,14 +2745,14 @@ ${color("Commands:", COLORS.blue)}
|
|
|
2134
2745
|
|
|
2135
2746
|
${color("Getting Started:", COLORS.blue)}
|
|
2136
2747
|
|
|
2137
|
-
1. Run ${color("bun visualize", COLORS.green)} from your
|
|
2748
|
+
1. Run ${color("bun visualize", COLORS.green)} from your project root
|
|
2138
2749
|
2. Find generated ${color("docs/architecture.dsl", COLORS.blue)}
|
|
2139
2750
|
3. View with Structurizr Lite (see instructions after generation)
|
|
2140
2751
|
|
|
2141
2752
|
${color("What gets generated:", COLORS.blue)}
|
|
2142
2753
|
|
|
2143
|
-
• System Context diagram -
|
|
2144
|
-
• Container diagram -
|
|
2754
|
+
• System Context diagram - Your app + external systems
|
|
2755
|
+
• Container diagram - App contexts (background, content, server, client, etc.)
|
|
2145
2756
|
• Component diagrams - Internal components within contexts
|
|
2146
2757
|
• Dynamic diagrams - Message flows between contexts
|
|
2147
2758
|
|
|
@@ -2154,21 +2765,20 @@ ${color("Learn More:", COLORS.blue)}
|
|
|
2154
2765
|
}
|
|
2155
2766
|
function findTsConfig() {
|
|
2156
2767
|
const locations = [
|
|
2157
|
-
|
|
2158
|
-
|
|
2768
|
+
path6.join(process.cwd(), "tsconfig.json"),
|
|
2769
|
+
path6.join(process.cwd(), "..", "tsconfig.json")
|
|
2159
2770
|
];
|
|
2160
2771
|
for (const loc of locations) {
|
|
2161
|
-
if (
|
|
2772
|
+
if (fs6.existsSync(loc)) {
|
|
2162
2773
|
return loc;
|
|
2163
2774
|
}
|
|
2164
2775
|
}
|
|
2165
2776
|
return null;
|
|
2166
2777
|
}
|
|
2167
2778
|
function findProjectRoot() {
|
|
2168
|
-
const locations = [process.cwd(),
|
|
2779
|
+
const locations = [process.cwd(), path6.join(process.cwd(), "..")];
|
|
2169
2780
|
for (const loc of locations) {
|
|
2170
|
-
|
|
2171
|
-
if (fs5.existsSync(manifestPath)) {
|
|
2781
|
+
if (fs6.existsSync(path6.join(loc, "manifest.json")) || fs6.existsSync(path6.join(loc, "package.json")) || fs6.existsSync(path6.join(loc, "tsconfig.json"))) {
|
|
2172
2782
|
return loc;
|
|
2173
2783
|
}
|
|
2174
2784
|
}
|
|
@@ -2186,4 +2796,4 @@ Stack trace:`, COLORS.gray));
|
|
|
2186
2796
|
process.exit(1);
|
|
2187
2797
|
});
|
|
2188
2798
|
|
|
2189
|
-
//# debugId=
|
|
2799
|
+
//# debugId=37FBF573C9C57A4C64756E2164756E21
|