@fairfox/polly 0.1.1 → 0.1.3

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 (39) hide show
  1. package/cli/polly.ts +9 -3
  2. package/package.json +2 -2
  3. package/vendor/analysis/src/extract/adr.ts +212 -0
  4. package/vendor/analysis/src/extract/architecture.ts +160 -0
  5. package/vendor/analysis/src/extract/contexts.ts +298 -0
  6. package/vendor/analysis/src/extract/flows.ts +309 -0
  7. package/vendor/analysis/src/extract/handlers.ts +321 -0
  8. package/vendor/analysis/src/extract/index.ts +9 -0
  9. package/vendor/analysis/src/extract/integrations.ts +329 -0
  10. package/vendor/analysis/src/extract/manifest.ts +298 -0
  11. package/vendor/analysis/src/extract/types.ts +389 -0
  12. package/vendor/analysis/src/index.ts +7 -0
  13. package/vendor/analysis/src/types/adr.ts +53 -0
  14. package/vendor/analysis/src/types/architecture.ts +245 -0
  15. package/vendor/analysis/src/types/core.ts +210 -0
  16. package/vendor/analysis/src/types/index.ts +18 -0
  17. package/vendor/verify/src/adapters/base.ts +164 -0
  18. package/vendor/verify/src/adapters/detection.ts +281 -0
  19. package/vendor/verify/src/adapters/event-bus/index.ts +480 -0
  20. package/vendor/verify/src/adapters/web-extension/index.ts +508 -0
  21. package/vendor/verify/src/adapters/websocket/index.ts +486 -0
  22. package/vendor/verify/src/cli.ts +430 -0
  23. package/vendor/verify/src/codegen/config.ts +354 -0
  24. package/vendor/verify/src/codegen/tla.ts +719 -0
  25. package/vendor/verify/src/config/parser.ts +303 -0
  26. package/vendor/verify/src/config/types.ts +113 -0
  27. package/vendor/verify/src/core/model.ts +267 -0
  28. package/vendor/verify/src/core/primitives.ts +106 -0
  29. package/vendor/verify/src/extract/handlers.ts +2 -0
  30. package/vendor/verify/src/extract/types.ts +2 -0
  31. package/vendor/verify/src/index.ts +150 -0
  32. package/vendor/verify/src/primitives/index.ts +102 -0
  33. package/vendor/verify/src/runner/docker.ts +283 -0
  34. package/vendor/verify/src/types.ts +51 -0
  35. package/vendor/visualize/src/cli.ts +365 -0
  36. package/vendor/visualize/src/codegen/structurizr.ts +770 -0
  37. package/vendor/visualize/src/index.ts +13 -0
  38. package/vendor/visualize/src/runner/export.ts +235 -0
  39. package/vendor/visualize/src/viewer/server.ts +485 -0
@@ -0,0 +1,485 @@
1
+ // Interactive viewer HTTP server using Bun
2
+
3
+ import * as fs from "node:fs";
4
+ import * as path from "node:path";
5
+
6
+ export interface ViewerOptions {
7
+ /** Port to run server on */
8
+ port?: number;
9
+
10
+ /** Path to docs directory */
11
+ docsDir: string;
12
+
13
+ /** Auto-open browser */
14
+ open?: boolean;
15
+ }
16
+
17
+ export class ViewerServer {
18
+ private options: ViewerOptions;
19
+ private server: any;
20
+
21
+ constructor(options: ViewerOptions) {
22
+ this.options = {
23
+ port: 3000,
24
+ open: true,
25
+ ...options,
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Start the viewer server
31
+ */
32
+ async start(): Promise<void> {
33
+ const { port, docsDir } = this.options;
34
+
35
+ // Check if DSL file exists
36
+ const dslPath = path.join(docsDir, "architecture.dsl");
37
+ if (!fs.existsSync(dslPath)) {
38
+ throw new Error(`DSL file not found: ${dslPath}`);
39
+ }
40
+
41
+ // Read DSL content
42
+ const dsl = fs.readFileSync(dslPath, "utf-8");
43
+
44
+ // Use Bun's server (dynamically accessed to avoid TS errors during build)
45
+ const BunGlobal = (globalThis as any).Bun;
46
+ if (!BunGlobal) {
47
+ throw new Error("Bun runtime is required to run the viewer");
48
+ }
49
+
50
+ try {
51
+ this.server = BunGlobal.serve({
52
+ port,
53
+ fetch: (req: Request) => this.handleRequest(req, dsl, docsDir),
54
+ });
55
+ } catch (error) {
56
+ throw new Error(`Failed to start server. Is port ${port} in use?`);
57
+ }
58
+
59
+ console.log(`Viewer running at http://localhost:${port}`);
60
+
61
+ // Auto-open browser
62
+ if (this.options.open) {
63
+ const url = `http://localhost:${port}`;
64
+ if (process.platform === "darwin") {
65
+ await BunGlobal.spawn(["open", url]);
66
+ } else if (process.platform === "linux") {
67
+ await BunGlobal.spawn(["xdg-open", url]);
68
+ } else if (process.platform === "win32") {
69
+ await BunGlobal.spawn(["cmd", "/c", "start", url]);
70
+ }
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Stop the server
76
+ */
77
+ stop(): void {
78
+ if (this.server) {
79
+ this.server.stop();
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Handle HTTP request
85
+ */
86
+ private handleRequest(req: Request, dsl: string, docsDir: string): Response {
87
+ const url = new URL(req.url);
88
+
89
+ // Serve main viewer page
90
+ if (url.pathname === "/" || url.pathname === "/index.html") {
91
+ return new Response(this.generateViewerHTML(dsl), {
92
+ headers: { "Content-Type": "text/html" },
93
+ });
94
+ }
95
+
96
+ // Serve DSL file
97
+ if (url.pathname === "/architecture.dsl") {
98
+ return new Response(dsl, {
99
+ headers: { "Content-Type": "text/plain" },
100
+ });
101
+ }
102
+
103
+ // List available diagrams
104
+ if (url.pathname === "/diagrams/list") {
105
+ const diagramsDir = path.join(docsDir, "diagrams");
106
+
107
+ if (fs.existsSync(diagramsDir)) {
108
+ const files = fs
109
+ .readdirSync(diagramsDir)
110
+ .filter((file) => file.endsWith(".png") && !file.includes("-key"))
111
+ .sort();
112
+
113
+ return new Response(JSON.stringify(files), {
114
+ headers: { "Content-Type": "application/json" },
115
+ });
116
+ }
117
+
118
+ return new Response(JSON.stringify([]), {
119
+ headers: { "Content-Type": "application/json" },
120
+ });
121
+ }
122
+
123
+ // Serve diagram images from diagrams directory
124
+ if (url.pathname.startsWith("/diagrams/")) {
125
+ const fileName = path.basename(url.pathname);
126
+ const filePath = path.join(docsDir, "diagrams", fileName);
127
+
128
+ if (fs.existsSync(filePath)) {
129
+ const content = fs.readFileSync(filePath);
130
+ const ext = path.extname(fileName).toLowerCase();
131
+ const contentType =
132
+ ext === ".png"
133
+ ? "image/png"
134
+ : ext === ".svg"
135
+ ? "image/svg+xml"
136
+ : "application/octet-stream";
137
+
138
+ return new Response(content, {
139
+ headers: { "Content-Type": contentType },
140
+ });
141
+ }
142
+ }
143
+
144
+ return new Response("Not found", { status: 404 });
145
+ }
146
+
147
+ /**
148
+ * Generate viewer HTML page
149
+ */
150
+ private generateViewerHTML(dsl: string): string {
151
+ return `<!DOCTYPE html>
152
+ <html lang="en">
153
+ <head>
154
+ <meta charset="UTF-8">
155
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
156
+ <title>Architecture Viewer</title>
157
+ <style>
158
+ * {
159
+ margin: 0;
160
+ padding: 0;
161
+ box-sizing: border-box;
162
+ }
163
+
164
+ body {
165
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
166
+ background: #f5f5f5;
167
+ color: #333;
168
+ }
169
+
170
+ header {
171
+ background: #2c3e50;
172
+ color: white;
173
+ padding: 1rem 2rem;
174
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
175
+ }
176
+
177
+ h1 {
178
+ font-size: 1.5rem;
179
+ font-weight: 600;
180
+ }
181
+
182
+ .container {
183
+ display: flex;
184
+ height: calc(100vh - 60px);
185
+ }
186
+
187
+ .sidebar {
188
+ width: 300px;
189
+ background: white;
190
+ border-right: 1px solid #ddd;
191
+ overflow-y: auto;
192
+ }
193
+
194
+ .content {
195
+ flex: 1;
196
+ padding: 2rem;
197
+ overflow-y: auto;
198
+ }
199
+
200
+ .nav-section {
201
+ padding: 1rem;
202
+ border-bottom: 1px solid #eee;
203
+ }
204
+
205
+ .nav-section h2 {
206
+ font-size: 0.875rem;
207
+ text-transform: uppercase;
208
+ color: #666;
209
+ margin-bottom: 0.5rem;
210
+ font-weight: 600;
211
+ }
212
+
213
+ .nav-item {
214
+ padding: 0.5rem;
215
+ cursor: pointer;
216
+ border-radius: 4px;
217
+ transition: background 0.2s;
218
+ }
219
+
220
+ .nav-item:hover {
221
+ background: #f0f0f0;
222
+ }
223
+
224
+ .nav-item.active {
225
+ background: #3498db;
226
+ color: white;
227
+ }
228
+
229
+ .card {
230
+ background: white;
231
+ border-radius: 8px;
232
+ padding: 1.5rem;
233
+ margin-bottom: 1.5rem;
234
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
235
+ }
236
+
237
+ .card h2 {
238
+ margin-bottom: 1rem;
239
+ color: #2c3e50;
240
+ }
241
+
242
+ pre {
243
+ background: #f8f9fa;
244
+ padding: 1rem;
245
+ border-radius: 4px;
246
+ overflow-x: auto;
247
+ font-size: 0.875rem;
248
+ line-height: 1.5;
249
+ }
250
+
251
+ .diagram {
252
+ width: 100%;
253
+ max-width: 800px;
254
+ border: 1px solid #ddd;
255
+ border-radius: 4px;
256
+ background: white;
257
+ }
258
+
259
+ .badge {
260
+ display: inline-block;
261
+ padding: 0.25rem 0.5rem;
262
+ border-radius: 4px;
263
+ font-size: 0.75rem;
264
+ font-weight: 600;
265
+ margin-right: 0.5rem;
266
+ }
267
+
268
+ .badge-context {
269
+ background: #3498db;
270
+ color: white;
271
+ }
272
+
273
+ .badge-handler {
274
+ background: #2ecc71;
275
+ color: white;
276
+ }
277
+
278
+ .badge-api {
279
+ background: #e74c3c;
280
+ color: white;
281
+ }
282
+
283
+ .list-item {
284
+ padding: 0.5rem 0;
285
+ border-bottom: 1px solid #eee;
286
+ }
287
+
288
+ .list-item:last-child {
289
+ border-bottom: none;
290
+ }
291
+
292
+ .empty-state {
293
+ text-align: center;
294
+ padding: 3rem;
295
+ color: #999;
296
+ }
297
+
298
+ .tabs {
299
+ display: flex;
300
+ border-bottom: 2px solid #ddd;
301
+ margin-bottom: 1rem;
302
+ }
303
+
304
+ .tab {
305
+ padding: 0.75rem 1.5rem;
306
+ cursor: pointer;
307
+ border-bottom: 2px solid transparent;
308
+ margin-bottom: -2px;
309
+ transition: all 0.2s;
310
+ }
311
+
312
+ .tab:hover {
313
+ background: #f8f9fa;
314
+ }
315
+
316
+ .tab.active {
317
+ border-bottom-color: #3498db;
318
+ color: #3498db;
319
+ font-weight: 600;
320
+ }
321
+
322
+ .tab-content {
323
+ display: none;
324
+ }
325
+
326
+ .tab-content.active {
327
+ display: block;
328
+ }
329
+ </style>
330
+ </head>
331
+ <body>
332
+ <header>
333
+ <h1>🏗️ Architecture Viewer</h1>
334
+ </header>
335
+
336
+ <div class="container">
337
+ <aside class="sidebar">
338
+ <div class="nav-section">
339
+ <h2>Views</h2>
340
+ <div class="nav-item active" data-view="overview">Overview</div>
341
+ <div class="nav-item" data-view="dsl">DSL Source</div>
342
+ <div class="nav-item" data-view="diagrams">Diagrams</div>
343
+ </div>
344
+ </aside>
345
+
346
+ <main class="content">
347
+ <div id="overview-view" class="view-content">
348
+ <div class="card">
349
+ <h2>Architecture Overview</h2>
350
+ <div id="overview-content"></div>
351
+ </div>
352
+ </div>
353
+
354
+ <div id="dsl-view" class="view-content" style="display: none;">
355
+ <div class="card">
356
+ <h2>Structurizr DSL</h2>
357
+ <pre id="dsl-content"></pre>
358
+ </div>
359
+ </div>
360
+
361
+ <div id="diagrams-view" class="view-content" style="display: none;">
362
+ <div class="card">
363
+ <h2>Exported Diagrams</h2>
364
+ <div id="diagrams-content"></div>
365
+ </div>
366
+ </div>
367
+ </main>
368
+ </div>
369
+
370
+ <script>
371
+ const dsl = ${JSON.stringify(dsl)};
372
+
373
+ // Parse DSL to extract information
374
+ function parseDSL(dsl) {
375
+ const lines = dsl.split('\\n');
376
+ const info = {
377
+ workspace: '',
378
+ contexts: [],
379
+ components: [],
380
+ relationships: []
381
+ };
382
+
383
+ // Extract workspace name
384
+ const workspaceMatch = dsl.match(/workspace "([^"]+)"/);
385
+ if (workspaceMatch) {
386
+ info.workspace = workspaceMatch[1];
387
+ }
388
+
389
+ // Extract containers (contexts)
390
+ const containerMatches = dsl.matchAll(/container "([^"]+)" "([^"]*)"/g);
391
+ for (const match of containerMatches) {
392
+ info.contexts.push({ name: match[1], description: match[2] });
393
+ }
394
+
395
+ // Extract components
396
+ const componentMatches = dsl.matchAll(/component "([^"]+)" "([^"]*)"/g);
397
+ for (const match of componentMatches) {
398
+ info.components.push({ name: match[1], description: match[2] });
399
+ }
400
+
401
+ return info;
402
+ }
403
+
404
+ const info = parseDSL(dsl);
405
+
406
+ // Render overview
407
+ const overviewContent = document.getElementById('overview-content');
408
+ let html = '<h3>' + info.workspace + '</h3>';
409
+
410
+ if (info.contexts.length > 0) {
411
+ html += '<h4 style="margin-top: 1.5rem;">Contexts</h4>';
412
+ info.contexts.forEach(ctx => {
413
+ html += '<div class="list-item">';
414
+ html += '<span class="badge badge-context">' + ctx.name + '</span>';
415
+ html += '<div style="margin-top: 0.25rem; color: #666;">' + ctx.description + '</div>';
416
+ html += '</div>';
417
+ });
418
+ }
419
+
420
+ if (info.components.length > 0) {
421
+ html += '<h4 style="margin-top: 1.5rem;">Components</h4>';
422
+ info.components.forEach(comp => {
423
+ html += '<div class="list-item">';
424
+ html += '<span class="badge badge-handler">' + comp.name + '</span>';
425
+ html += '<div style="margin-top: 0.25rem; color: #666;">' + comp.description + '</div>';
426
+ html += '</div>';
427
+ });
428
+ }
429
+
430
+ overviewContent.innerHTML = html;
431
+
432
+ // Render DSL
433
+ document.getElementById('dsl-content').textContent = dsl;
434
+
435
+ // Check for diagrams
436
+ fetch('/diagrams/list')
437
+ .then(res => res.ok ? res.json() : null)
438
+ .then(diagrams => {
439
+ const diagramsContent = document.getElementById('diagrams-content');
440
+ if (diagrams && diagrams.length > 0) {
441
+ // Display diagrams
442
+ let html = '';
443
+ diagrams.forEach(diagram => {
444
+ html += '<div style="margin-bottom: 2rem;">';
445
+ html += '<h3>' + diagram.replace(/^structurizr-/, '').replace(/\\.png$/, '').replace(/_/g, ' ') + '</h3>';
446
+ html += '<img src="/diagrams/' + diagram + '" class="diagram" alt="' + diagram + '" />';
447
+ html += '</div>';
448
+ });
449
+ diagramsContent.innerHTML = html;
450
+ } else {
451
+ diagramsContent.innerHTML = '<div class="empty-state">No diagrams exported yet.<br><br>Run <code>bun visualize --export</code> to generate diagrams.</div>';
452
+ }
453
+ })
454
+ .catch(() => {
455
+ const diagramsContent = document.getElementById('diagrams-content');
456
+ diagramsContent.innerHTML = '<div class="empty-state">No diagrams exported yet.<br><br>Run <code>bun visualize --export</code> to generate diagrams.</div>';
457
+ });
458
+
459
+ // Navigation
460
+ document.querySelectorAll('.nav-item').forEach(item => {
461
+ item.addEventListener('click', () => {
462
+ // Update active nav
463
+ document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active'));
464
+ item.classList.add('active');
465
+
466
+ // Show corresponding view
467
+ const view = item.dataset.view;
468
+ document.querySelectorAll('.view-content').forEach(v => v.style.display = 'none');
469
+ document.getElementById(view + '-view').style.display = 'block';
470
+ });
471
+ });
472
+ </script>
473
+ </body>
474
+ </html>`;
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Start interactive viewer
480
+ */
481
+ export async function startViewer(options: ViewerOptions): Promise<ViewerServer> {
482
+ const server = new ViewerServer(options);
483
+ await server.start();
484
+ return server;
485
+ }