pkgviz 0.1.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.
Files changed (162) hide show
  1. package/.next/BUILD_ID +1 -0
  2. package/.next/app-build-manifest.json +64 -0
  3. package/.next/app-path-routes-manifest.json +10 -0
  4. package/.next/build-manifest.json +33 -0
  5. package/.next/cache/.previewinfo +1 -0
  6. package/.next/cache/.rscinfo +1 -0
  7. package/.next/cache/.tsbuildinfo +1 -0
  8. package/.next/cache/chrome-devtools-workspace-uuid +1 -0
  9. package/.next/cache/eslint/.cache_23iu9t +1 -0
  10. package/.next/cache/eslint/.cache_6t30dc +1 -0
  11. package/.next/cache/webpack/client-production/0.pack +0 -0
  12. package/.next/cache/webpack/client-production/1.pack +0 -0
  13. package/.next/cache/webpack/client-production/10.pack +0 -0
  14. package/.next/cache/webpack/client-production/11.pack +0 -0
  15. package/.next/cache/webpack/client-production/12.pack +0 -0
  16. package/.next/cache/webpack/client-production/13.pack +0 -0
  17. package/.next/cache/webpack/client-production/14.pack +0 -0
  18. package/.next/cache/webpack/client-production/15.pack +0 -0
  19. package/.next/cache/webpack/client-production/16.pack +0 -0
  20. package/.next/cache/webpack/client-production/2.pack +0 -0
  21. package/.next/cache/webpack/client-production/3.pack +0 -0
  22. package/.next/cache/webpack/client-production/4.pack +0 -0
  23. package/.next/cache/webpack/client-production/5.pack +0 -0
  24. package/.next/cache/webpack/client-production/6.pack +0 -0
  25. package/.next/cache/webpack/client-production/7.pack +0 -0
  26. package/.next/cache/webpack/client-production/8.pack +0 -0
  27. package/.next/cache/webpack/client-production/9.pack +0 -0
  28. package/.next/cache/webpack/client-production/index.pack +0 -0
  29. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  30. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  31. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  32. package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  33. package/.next/cache/webpack/server-production/0.pack +0 -0
  34. package/.next/cache/webpack/server-production/1.pack +0 -0
  35. package/.next/cache/webpack/server-production/10.pack +0 -0
  36. package/.next/cache/webpack/server-production/11.pack +0 -0
  37. package/.next/cache/webpack/server-production/12.pack +0 -0
  38. package/.next/cache/webpack/server-production/13.pack +0 -0
  39. package/.next/cache/webpack/server-production/14.pack +0 -0
  40. package/.next/cache/webpack/server-production/15.pack +0 -0
  41. package/.next/cache/webpack/server-production/16.pack +0 -0
  42. package/.next/cache/webpack/server-production/2.pack +0 -0
  43. package/.next/cache/webpack/server-production/3.pack +0 -0
  44. package/.next/cache/webpack/server-production/4.pack +0 -0
  45. package/.next/cache/webpack/server-production/5.pack +0 -0
  46. package/.next/cache/webpack/server-production/6.pack +0 -0
  47. package/.next/cache/webpack/server-production/7.pack +0 -0
  48. package/.next/cache/webpack/server-production/8.pack +0 -0
  49. package/.next/cache/webpack/server-production/9.pack +0 -0
  50. package/.next/cache/webpack/server-production/index.pack +0 -0
  51. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  52. package/.next/diagnostics/build-diagnostics.json +6 -0
  53. package/.next/diagnostics/framework.json +1 -0
  54. package/.next/export-marker.json +6 -0
  55. package/.next/images-manifest.json +57 -0
  56. package/.next/next-minimal-server.js.nft.json +1 -0
  57. package/.next/next-server.js.nft.json +1 -0
  58. package/.next/package.json +1 -0
  59. package/.next/prerender-manifest.json +65 -0
  60. package/.next/react-loadable-manifest.json +1 -0
  61. package/.next/required-server-files.json +317 -0
  62. package/.next/routes-manifest.json +59 -0
  63. package/.next/server/app/_not-found/page.js +2 -0
  64. package/.next/server/app/_not-found/page.js.nft.json +1 -0
  65. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  66. package/.next/server/app/_not-found.html +1 -0
  67. package/.next/server/app/_not-found.meta +8 -0
  68. package/.next/server/app/_not-found.rsc +20 -0
  69. package/.next/server/app/api/audit/json/route.js +1 -0
  70. package/.next/server/app/api/audit/json/route.js.nft.json +1 -0
  71. package/.next/server/app/api/audit/json/route_client-reference-manifest.js +1 -0
  72. package/.next/server/app/api/audit/xml/route.js +1 -0
  73. package/.next/server/app/api/audit/xml/route.js.nft.json +1 -0
  74. package/.next/server/app/api/audit/xml/route_client-reference-manifest.js +1 -0
  75. package/.next/server/app/api/fs/detectLanguage/route.js +2 -0
  76. package/.next/server/app/api/fs/detectLanguage/route.js.nft.json +1 -0
  77. package/.next/server/app/api/fs/detectLanguage/route_client-reference-manifest.js +1 -0
  78. package/.next/server/app/api/fs/getAllFilesRecursive/route.js +1 -0
  79. package/.next/server/app/api/fs/getAllFilesRecursive/route.js.nft.json +1 -0
  80. package/.next/server/app/api/fs/getAllFilesRecursive/route_client-reference-manifest.js +1 -0
  81. package/.next/server/app/api/fs/getGraph/route.js +1 -0
  82. package/.next/server/app/api/fs/getGraph/route.js.nft.json +1 -0
  83. package/.next/server/app/api/fs/getGraph/route_client-reference-manifest.js +1 -0
  84. package/.next/server/app/favicon.ico/route.js +1 -0
  85. package/.next/server/app/favicon.ico/route.js.nft.json +1 -0
  86. package/.next/server/app/favicon.ico.body +0 -0
  87. package/.next/server/app/favicon.ico.meta +1 -0
  88. package/.next/server/app/index.html +1 -0
  89. package/.next/server/app/index.meta +7 -0
  90. package/.next/server/app/index.rsc +24 -0
  91. package/.next/server/app/page.js +2 -0
  92. package/.next/server/app/page.js.nft.json +1 -0
  93. package/.next/server/app/page_client-reference-manifest.js +1 -0
  94. package/.next/server/app-paths-manifest.json +10 -0
  95. package/.next/server/chunks/376.js +1 -0
  96. package/.next/server/chunks/548.js +6 -0
  97. package/.next/server/chunks/55.js +1 -0
  98. package/.next/server/chunks/669.js +2 -0
  99. package/.next/server/chunks/671.js +7 -0
  100. package/.next/server/chunks/985.js +22 -0
  101. package/.next/server/functions-config-manifest.json +4 -0
  102. package/.next/server/interception-route-rewrite-manifest.js +1 -0
  103. package/.next/server/middleware-build-manifest.js +1 -0
  104. package/.next/server/middleware-manifest.json +6 -0
  105. package/.next/server/middleware-react-loadable-manifest.js +1 -0
  106. package/.next/server/next-font-manifest.js +1 -0
  107. package/.next/server/next-font-manifest.json +1 -0
  108. package/.next/server/pages/404.html +1 -0
  109. package/.next/server/pages/500.html +1 -0
  110. package/.next/server/pages/_app.js +1 -0
  111. package/.next/server/pages/_app.js.nft.json +1 -0
  112. package/.next/server/pages/_document.js +1 -0
  113. package/.next/server/pages/_document.js.nft.json +1 -0
  114. package/.next/server/pages/_error.js +19 -0
  115. package/.next/server/pages/_error.js.nft.json +1 -0
  116. package/.next/server/pages-manifest.json +6 -0
  117. package/.next/server/server-reference-manifest.js +1 -0
  118. package/.next/server/server-reference-manifest.json +1 -0
  119. package/.next/server/webpack-runtime.js +1 -0
  120. package/.next/static/chunks/466-b4a500cbd8df897e.js +1 -0
  121. package/.next/static/chunks/4bd1b696-cf72ae8a39fa05aa.js +1 -0
  122. package/.next/static/chunks/90542734-bad8998e58193cb9.js +1 -0
  123. package/.next/static/chunks/964-4542b6435bd81c5b.js +1 -0
  124. package/.next/static/chunks/app/_not-found/page-11e4fea8e290f304.js +1 -0
  125. package/.next/static/chunks/app/api/audit/json/route-368bcf018f5b1da2.js +1 -0
  126. package/.next/static/chunks/app/api/audit/xml/route-368bcf018f5b1da2.js +1 -0
  127. package/.next/static/chunks/app/api/fs/detectLanguage/route-368bcf018f5b1da2.js +1 -0
  128. package/.next/static/chunks/app/api/fs/getAllFilesRecursive/route-368bcf018f5b1da2.js +1 -0
  129. package/.next/static/chunks/app/api/fs/getGraph/route-368bcf018f5b1da2.js +1 -0
  130. package/.next/static/chunks/app/layout-160414dfb322fd52.js +1 -0
  131. package/.next/static/chunks/app/page-17b2a17ccc3dc891.js +1 -0
  132. package/.next/static/chunks/dec7c704-381848996e700553.js +1 -0
  133. package/.next/static/chunks/framework-7c95b8e5103c9e90.js +1 -0
  134. package/.next/static/chunks/main-app-69a9ff75ff3465ca.js +1 -0
  135. package/.next/static/chunks/main-ce6996722ac81645.js +1 -0
  136. package/.next/static/chunks/pages/_app-0a0020ddd67f79cf.js +1 -0
  137. package/.next/static/chunks/pages/_error-03529f2c21436739.js +1 -0
  138. package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  139. package/.next/static/chunks/webpack-7adfafc806738767.js +1 -0
  140. package/.next/static/css/9d2991f387a19b1b.css +3 -0
  141. package/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
  142. package/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  143. package/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  144. package/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  145. package/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  146. package/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  147. package/.next/static/tOO3k6S3kc9LUxosG3ajE/_buildManifest.js +1 -0
  148. package/.next/static/tOO3k6S3kc9LUxosG3ajE/_ssgManifest.js +1 -0
  149. package/.next/trace +13 -0
  150. package/.next/types/app/api/audit/json/route.ts +347 -0
  151. package/.next/types/app/api/audit/xml/route.ts +347 -0
  152. package/.next/types/app/api/fs/detectLanguage/route.ts +347 -0
  153. package/.next/types/app/api/fs/getAllFilesRecursive/route.ts +347 -0
  154. package/.next/types/app/api/fs/getGraph/route.ts +347 -0
  155. package/.next/types/app/layout.ts +84 -0
  156. package/.next/types/app/page.ts +84 -0
  157. package/.next/types/cache-life.d.ts +141 -0
  158. package/.next/types/package.json +1 -0
  159. package/LICENSE +21 -0
  160. package/README.md +66 -0
  161. package/bin/pkgviz.ts +239 -0
  162. package/package.json +62 -0
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ ![issues](https://img.shields.io/github/issues/artiphishle/pkgviz?style=flat-square)
2
+ ![PRs](https://img.shields.io/github/issues-pr/artiphishle/pkgviz?style=flat-square)
3
+
4
+ # Package Visualizer
5
+
6
+ Tool to visualize packages and their dependencies between each other. This project is inspired by [socomo](https://github.com/gdela/socomo).
7
+
8
+ Good software architecture starts with matching functional requirements to code structure.
9
+ Visualizing and showing the dependencies of packages in your project is the first step to regain control of your project.
10
+
11
+ ## Prerequisites
12
+
13
+ Make sure to have PNPM & Node installed, check `package.json` for allowed versions
14
+
15
+ ## Quickstart
16
+
17
+ ```bash
18
+ # 1. Install dependencies
19
+ bun install
20
+ yarn install
21
+ pnpm install
22
+ npm install
23
+
24
+ # 2. Create your .env file
25
+ cp .env.tpl .env
26
+
27
+ # 3. Supply the project path to analyze in the .env file
28
+
29
+ # 4. Run the app
30
+ bun run dev
31
+ yarn run dev
32
+ pnpm run dev
33
+ npm run dev
34
+ ```
35
+
36
+ ## Documentation
37
+
38
+ Find the official documentation at Github Pages here:
39
+
40
+ [artiphishle.github.io/forensics-docs](https://artiphishle.github.io/forensics-docs/)
41
+
42
+ ## Test
43
+
44
+ ```bash
45
+ # Run tests
46
+ bun run test
47
+ yarn run test
48
+ pnpm run test
49
+ npm run test
50
+
51
+ # Print Coverage to stdout
52
+ bun run test:cov
53
+ yarn run test:cov
54
+ pnpm run test:cov
55
+ npm run test:cov
56
+
57
+ # Generate HTML Coverage to 'test/coverage/index.html'
58
+ bun test:cov:html
59
+ yarn test:cov:html
60
+ pnpm test:cov:html
61
+ npm test:cov:html
62
+ ```
63
+
64
+ ## Contributing
65
+
66
+ The list of open source tools to visualize code structure is rather short. Structure101 is now part of Sonar. Don't let paid tools dominate software craft. Join us and contribute to open-source!
package/bin/pkgviz.ts ADDED
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { existsSync } from 'node:fs';
4
+ import { mkdir, writeFile } from 'node:fs/promises';
5
+ import { dirname, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import * as net from 'node:net';
8
+
9
+ type Opts = {
10
+ out: string;
11
+ open: boolean;
12
+ serve: boolean;
13
+ prod: boolean;
14
+ port?: number;
15
+ route: string;
16
+ waitMs: number;
17
+ pretty: boolean;
18
+ verbose: boolean;
19
+ };
20
+
21
+ function parseArgs(argv: string[]): Opts {
22
+ const o: Opts = {
23
+ out: 'audit.json',
24
+ open: false,
25
+ serve: false,
26
+ prod: false,
27
+ port: undefined,
28
+ route: '/api/audit/json',
29
+ waitMs: 90_000,
30
+ pretty: true,
31
+ verbose: false,
32
+ };
33
+ for (let i = 2; i < argv.length; i++) {
34
+ const a = argv[i];
35
+ if (a === '-o' || a === '--out') o.out = argv[++i]!;
36
+ else if (a === '--open') o.open = true;
37
+ else if (a === '--serve') o.serve = true;
38
+ else if (a === '--prod') o.prod = true;
39
+ else if (a === '-p' || a === '--port') o.port = Number(argv[++i]);
40
+ else if (a === '--route') o.route = argv[++i]!;
41
+ else if (a === '--wait') o.waitMs = Number(argv[++i]);
42
+ else if (a === '--no-pretty') o.pretty = false;
43
+ else if (a === '-v' || a === '--verbose') o.verbose = true;
44
+ else if (a === '-h' || a === '--help') {
45
+ printHelp();
46
+ process.exit(0);
47
+ }
48
+ }
49
+ return o;
50
+ }
51
+
52
+ function printHelp() {
53
+ console.log(`
54
+ Usage:
55
+ bunx @your-scope/myapp [options]
56
+
57
+ Options:
58
+ -o, --out <file> Output file (default: audit.json in caller's cwd)
59
+ --open Open the viewer UI after export
60
+ --serve Keep server running after export (implies --open unless exporting only)
61
+ --prod Use "next start" if a build exists inside the package
62
+ -p, --port <n> Port to use (default: find free)
63
+ --route <path> API route path (default: /api/audit/json)
64
+ --wait <ms> Max wait for server & route (default: 90000)
65
+ --no-pretty Write minified JSON
66
+ -v, --verbose Verbose logs
67
+ -h, --help Show help
68
+
69
+ Behavior:
70
+ - Starts the packaged Next.js app from inside this npm package.
71
+ - Passes ANALYZE_ROOT = process.cwd() (the caller's project root).
72
+ - Calls GET {route} to retrieve JSON, writes it to --out, then exits (unless --open/--serve).
73
+ `);
74
+ }
75
+
76
+ function logv(v: boolean, ...args: unknown[]) {
77
+ if (v) console.log('[myapp]', ...args);
78
+ }
79
+
80
+ async function findFreePort(preferred?: number): Promise<number> {
81
+ const tryPort = (port: number) =>
82
+ new Promise<boolean>(resolvePort => {
83
+ const srv = net.createServer().once('error', () => resolvePort(false));
84
+ srv.listen(port, () => srv.close(() => resolvePort(true)));
85
+ });
86
+
87
+ if (preferred && (await tryPort(preferred))) return preferred;
88
+ for (const port of [3000, 3001, 3030, 4000, 5173, 8787, 0]) {
89
+ if (await tryPort(port)) {
90
+ if (port === 0) {
91
+ const srv = net.createServer();
92
+ await new Promise<void>(r => srv.listen(0, r));
93
+ const addr = srv.address();
94
+ const free = typeof addr === 'object' && addr ? addr.port : 3000;
95
+ await new Promise<void>(r => srv.close(() => r()));
96
+ return free;
97
+ }
98
+ return port;
99
+ }
100
+ }
101
+ throw new Error('No free port found');
102
+ }
103
+
104
+ function resolvePackageRoot(): string {
105
+ // bin is at <pkgRoot>/bin/myapp.ts; step up to package root
106
+ const here = fileURLToPath(new URL(import.meta.url));
107
+ return resolve(dirname(here), '..');
108
+ }
109
+
110
+ function resolveNextBin(pkgRoot: string): string {
111
+ const local = resolve(
112
+ pkgRoot,
113
+ 'node_modules',
114
+ '.bin',
115
+ process.platform === 'win32' ? 'next.cmd' : 'next'
116
+ );
117
+ if (existsSync(local)) return local;
118
+ return 'next'; // fallback to PATH
119
+ }
120
+
121
+ async function waitForJson(url: string, timeoutMs: number, verbose: boolean): Promise<unknown> {
122
+ const start = Date.now();
123
+ let last: unknown;
124
+ while (Date.now() - start < timeoutMs) {
125
+ try {
126
+ const res = await fetch(url);
127
+ const ct = res.headers.get('content-type') || '';
128
+ if (res.ok && ct.includes('application/json')) return await res.json();
129
+ last = `status=${res.status} ct=${ct}`;
130
+ } catch (e) {
131
+ last = e;
132
+ }
133
+ await new Promise(r => setTimeout(r, 400));
134
+ logv(verbose, 'Waiting for JSON…', Date.now() - start + 'ms');
135
+ }
136
+ throw new Error(`Timed out waiting for ${url}. Last: ${String(last)}`);
137
+ }
138
+
139
+ function openBrowser(url: string) {
140
+ const cmd =
141
+ process.platform === 'darwin'
142
+ ? ['open', url]
143
+ : process.platform === 'win32'
144
+ ? ['cmd', '/c', 'start', '', url]
145
+ : ['xdg-open', url];
146
+ Bun.spawn({ cmd, stdout: 'ignore', stderr: 'ignore' });
147
+ }
148
+
149
+ async function main() {
150
+ const opts = parseArgs(process.argv);
151
+
152
+ const callerRoot = process.cwd(); // The project being analyzed
153
+ const pkgRoot = resolvePackageRoot(); // The packaged Next app root
154
+ const port = await findFreePort(opts.port);
155
+ const nextBin = resolveNextBin(pkgRoot);
156
+ const hasBuild = existsSync(resolve(pkgRoot, '.next'));
157
+ const mode = opts.prod || hasBuild ? 'start' : 'dev';
158
+
159
+ const env = {
160
+ ...process.env,
161
+ NEXT_PUBLIC_PROJECT_PATH: callerRoot,
162
+ PORT: String(port),
163
+ NEXT_TELEMETRY_DISABLED: '1',
164
+ NODE_ENV: mode === 'start' ? 'production' : 'development',
165
+ };
166
+
167
+ logv(opts.verbose, `Starting Next (${mode}) at ${pkgRoot} on :${port}`);
168
+ const child = Bun.spawn({
169
+ cmd: [nextBin, mode, '-p', String(port)],
170
+ cwd: pkgRoot,
171
+ env,
172
+ stdio: ['ignore', 'pipe', 'pipe'],
173
+ });
174
+
175
+ if (opts.verbose) {
176
+ (async () => {
177
+ const reader = child.stdout.getReader();
178
+ try {
179
+ while (true) {
180
+ const { done, value } = await reader.read();
181
+ if (done) break;
182
+ process.stdout.write(value);
183
+ }
184
+ } finally {
185
+ reader.releaseLock();
186
+ }
187
+ })();
188
+ (async () => {
189
+ const reader = child.stderr.getReader();
190
+ try {
191
+ while (true) {
192
+ const { done, value } = await reader.read();
193
+ if (done) break;
194
+ process.stderr.write(value);
195
+ }
196
+ } finally {
197
+ reader.releaseLock();
198
+ }
199
+ })();
200
+ }
201
+
202
+ const base = `http://localhost:${port}`;
203
+ const apiUrl = `${base}${opts.route}`;
204
+
205
+ // fetch JSON export
206
+ const data = await waitForJson(apiUrl, opts.waitMs, opts.verbose);
207
+
208
+ // write to caller's directory
209
+ const outPath = resolve(callerRoot, opts.out);
210
+ await mkdir(dirname(outPath), { recursive: true });
211
+ const body = opts.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
212
+ await writeFile(outPath, body, 'utf8');
213
+ console.log(`✓ audit.json written → ${outPath}`);
214
+
215
+ const shouldOpen = opts.open || opts.serve;
216
+ if (shouldOpen) {
217
+ // Optional: pass cwd also as a query param if your UI reads it
218
+ const ui = `${base}/?cwd=${encodeURIComponent(callerRoot)}`;
219
+ openBrowser(ui);
220
+ }
221
+
222
+ if (opts.serve) {
223
+ console.log(`Serving UI at ${base} (NEXT_PUBLIC_PROJECT_PATH=${callerRoot})`);
224
+ // don't exit; keep Next running
225
+ return;
226
+ }
227
+
228
+ // else, terminate Next
229
+ try {
230
+ child.kill('SIGTERM');
231
+ await new Promise(r => setTimeout(r, 800));
232
+ if (child.exitCode === null) child.kill('SIGKILL');
233
+ } catch {}
234
+ }
235
+
236
+ main().catch(err => {
237
+ console.error('✖ myapp failed:', err?.message || err);
238
+ process.exit(1);
239
+ });
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "pkgviz",
3
+ "bin": {
4
+ "pkgviz": "bin/pkgviz.ts"
5
+ },
6
+ "files": [
7
+ "/bin", ".next"
8
+ ],
9
+ "type": "module",
10
+ "engines": {
11
+ "node": ">=22.14.0",
12
+ "bun": ">=1.2.18",
13
+ "npm": ">=10.9.2"
14
+ },
15
+ "version": "0.1.0",
16
+ "scripts": {
17
+ "dev": "next dev --turbopack",
18
+ "build": "next build",
19
+ "docs:deploy": "bun --filter forensics-docs run deploy",
20
+ "start": "next start",
21
+ "lint": "next lint",
22
+ "format:check": "prettier --check .",
23
+ "format:write": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md,yml,yaml,mjs,cjs,html,css,scss,mdx}\"",
24
+ "test": "npx @artiphishle/testosterone",
25
+ "test:cov": "c8 --reporter=text --reporter=text-summary tsx --test",
26
+ "test:cov:html": "c8 --report-dir=./test/coverage --reporter=html tsx --test"
27
+ },
28
+ "dependencies": {
29
+ "@artiphishle/testosterone": "^0.1.3",
30
+ "cytoscape": "^3.31.2",
31
+ "cytoscape-klay": "^3.1.4",
32
+ "fp-ts": "^2.16.9",
33
+ "lucide-react": "^0.487.0",
34
+ "meta": "^2.2.25",
35
+ "next": "15.4.7",
36
+ "next-themes": "^0.4.6",
37
+ "radix-ui": "^1.2.0",
38
+ "react": "^19.0.0",
39
+ "react-dom": "^19.0.0",
40
+ "xml-js": "^1.6.11"
41
+ },
42
+ "devDependencies": {
43
+ "@eslint/eslintrc": "^3",
44
+ "@tailwindcss/postcss": "^4.1.3",
45
+ "@types/bun": "^1.2.21",
46
+ "@types/cytoscape": "^3.21.9",
47
+ "@types/node": "^20",
48
+ "@types/react": "^19",
49
+ "@types/react-dom": "^19",
50
+ "autoprefixer": "^10.4.21",
51
+ "c8": "^10.1.3",
52
+ "eslint": "^9",
53
+ "eslint-config-next": "15.3.0",
54
+ "postcss": "^8.5.3",
55
+ "prettier": "^3.5.3",
56
+ "tailwindcss": "^4.1.3",
57
+ "tailwindcss-cli": "^0.1.2",
58
+ "tsx": "^4.19.3",
59
+ "typescript": "^5"
60
+ },
61
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
62
+ }