@progen-labs/spectra 0.0.0-stage → 0.1.1

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 (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -2
  3. package/bin/spectra.js +215 -0
  4. package/dist/assets/index-D0Lxrmdt.js +46 -0
  5. package/dist/assets/index-Da3Le5Px.css +1 -0
  6. package/dist/index.html +14 -0
  7. package/index.html +13 -0
  8. package/package.json +76 -4
  9. package/server/analysis/cpu.js +369 -0
  10. package/server/analysis/findings.js +570 -0
  11. package/server/analysis/locate.js +99 -0
  12. package/server/analysis/report.js +775 -0
  13. package/server/analysis/sources.js +249 -0
  14. package/server/analysis/trace.js +314 -0
  15. package/server/api.js +292 -0
  16. package/server/chrome.js +81 -0
  17. package/server/cli.js +94 -0
  18. package/server/config.js +59 -0
  19. package/server/dashboard.js +154 -0
  20. package/server/explore/boot.js +150 -0
  21. package/server/explore/classify.js +169 -0
  22. package/server/explore/discover.js +162 -0
  23. package/server/explore/evaluate-cli.js +101 -0
  24. package/server/explore/evaluate.js +165 -0
  25. package/server/explore/paths.js +105 -0
  26. package/server/explore/runner.js +94 -0
  27. package/server/explore/test-explore.js +139 -0
  28. package/server/explore/test-paths.js +75 -0
  29. package/server/framework.js +67 -0
  30. package/server/git.js +59 -0
  31. package/server/pdf/render.js +60 -0
  32. package/server/pdf/template.js +419 -0
  33. package/server/plugin.js +19 -0
  34. package/server/probe.browser.js +828 -0
  35. package/server/recorder.js +610 -0
  36. package/server/signin.js +27 -0
  37. package/server/test-analysis.js +424 -0
  38. package/server/test-compare.js +473 -0
  39. package/server/test-sources.js +136 -0
  40. package/shared/compare.js +977 -0
  41. package/shared/snapshot.js +77 -0
  42. package/src/App.jsx +361 -0
  43. package/src/api.js +51 -0
  44. package/src/components/AppearanceMenu.jsx +76 -0
  45. package/src/components/CodeRef.jsx +48 -0
  46. package/src/components/ComparePanel.jsx +143 -0
  47. package/src/components/CompareResults.jsx +473 -0
  48. package/src/components/ControlBar.jsx +336 -0
  49. package/src/components/CulpritSphere.jsx +218 -0
  50. package/src/components/Culprits.jsx +120 -0
  51. package/src/components/CulpritsHero.jsx +109 -0
  52. package/src/components/EvaluationPanel.jsx +160 -0
  53. package/src/components/FilePanel.jsx +225 -0
  54. package/src/components/FindingsList.jsx +381 -0
  55. package/src/components/HeatTrees.jsx +222 -0
  56. package/src/components/Icons.jsx +58 -0
  57. package/src/components/LongTasks.jsx +199 -0
  58. package/src/components/NetworkPanel.jsx +98 -0
  59. package/src/components/PageMap.jsx +329 -0
  60. package/src/components/Popover.jsx +51 -0
  61. package/src/components/ReactPanel.jsx +206 -0
  62. package/src/components/SidePicker.jsx +207 -0
  63. package/src/components/SnapshotButton.jsx +140 -0
  64. package/src/components/SpectraCulprits.jsx +39 -0
  65. package/src/components/TimeBreakdown.jsx +45 -0
  66. package/src/components/TraceButton.jsx +76 -0
  67. package/src/components/TreeView.jsx +173 -0
  68. package/src/components/Vitals.jsx +163 -0
  69. package/src/culprits.js +127 -0
  70. package/src/download.js +18 -0
  71. package/src/format.js +26 -0
  72. package/src/heat.js +177 -0
  73. package/src/highlight.js +45 -0
  74. package/src/hooks/useCompare.js +187 -0
  75. package/src/hooks/useSession.js +73 -0
  76. package/src/layout.js +55 -0
  77. package/src/main.jsx +15 -0
  78. package/src/styles.css +2478 -0
  79. package/src/theme.js +75 -0
  80. package/src/themes.css +286 -0
  81. package/vite.config.js +8 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sourav Debnath
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction rights, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,101 @@
1
- # Temporary Holding Version
1
+ # @progen-labs/spectra
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Find out which file, which function and which component makes your frontend slow — automatically.**
4
+
5
+ Spectra is a CLI and dashboard that profiles a frontend build through the Chrome DevTools Protocol
6
+ (tracing + CPU sampling) plus a small in-page probe for component re-renders and Web Vitals. Run one
7
+ command and it starts your dev server, auto-discovers the actionable elements on the page, drives a set
8
+ of **golden-path journeys**, records each one, and reports a ranked list of culprits — each with the
9
+ exact function, file and line, the evidence, and a suggested fix.
10
+
11
+ Deep component-level attribution for **React, Next.js and Vue** (component names, source files, render
12
+ counts and per-component render time). For **Svelte, Angular and any other build**, a source-mapped
13
+ generic profile still pinpoints the slow files and functions. A [Progen Labs](https://github.com/progen-labs)
14
+ project — open source, non-profit, no telemetry, no account required.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install --save-dev @progen-labs/spectra
20
+ ```
21
+
22
+ Requirements: **Node 18+** and a local **Chrome / Chromium / Edge** (Spectra drives your installed
23
+ browser; it does not download one). If it can't find Chrome, set `CHROME_PATH` to the executable.
24
+
25
+ ## Quick start
26
+
27
+ From the root of your project:
28
+
29
+ ```bash
30
+ npx spectra performance
31
+ ```
32
+
33
+ With no `--url`, Spectra:
34
+
35
+ 1. detects your framework and starts your dev server (`npm run dev`/`start`/`serve`),
36
+ 2. records a baseline page load,
37
+ 3. auto-discovers the actionable elements (links, buttons, inputs, forms),
38
+ 4. synthesizes and runs **golden-path journeys** — safely (see below),
39
+ 5. prints a performance matrix and saves a report per journey, then stops the server.
40
+
41
+ ```
42
+ # Golden path Result TBT LCP LongT Top finding
43
+ — Initial page load PASS 0ms 128ms 0 [warning] main chunk served uncompressed
44
+ 1 Click "Pricing" PASS 0ms — 0 [warning] Large DOM: 2,139 nodes
45
+ 2 Click "Folders" PASS 0ms — 0 [critical] TreeNode re-rendered 1100× (96% wasted)
46
+ ...
47
+ Automation: 8/8 golden paths passed.
48
+ ```
49
+
50
+ Then open the visual dashboard — ranked culprits, React re-renders, long tasks, the golden-path matrix,
51
+ **PDF export** and **commit-to-commit compare**:
52
+
53
+ ```bash
54
+ npx spectra dashboard
55
+ ```
56
+
57
+ ## Commands
58
+
59
+ ```
60
+ spectra performance [options] Golden-path evaluation (default)
61
+ spectra dashboard [--port N] Open the dashboard UI
62
+ spectra --help Full option list
63
+ spectra --version
64
+ ```
65
+
66
+ **`performance` options**
67
+
68
+ | Option | Description |
69
+ | --- | --- |
70
+ | `--url <page>` | Evaluate an already-running page; skips self-boot |
71
+ | `--paths <n>` | Max golden paths to run (default 12) |
72
+ | `--command "<cmd>"` | Override the dev-server command |
73
+ | `--boot-timeout <s>` | How long to wait for the dev server (default 60) |
74
+ | `--headed` | Show the browser window |
75
+ | `--sign-in <url>` | Sign-in URL template, e.g. `http://host/login/?redirect={url}` |
76
+ | `--mode live\|load` | Classic single-page profile instead of the golden-path suite |
77
+
78
+ ## Safe by default
79
+
80
+ Auto-exploration never triggers destructive actions. Spectra detects controls that would mutate state —
81
+ anything labelled delete / pay / checkout / log out, logout links, and `POST`/`PUT`/`DELETE` form submits —
82
+ and **skips** them unless you explicitly allow them. Form fields are filled with synthetic values; GET
83
+ searches and navigations are fair game.
84
+
85
+ ## Where results go
86
+
87
+ Reports are written to `.spectra/reports/` in your project (add it to `.gitignore`). Each golden path is a
88
+ full report you can open in `spectra dashboard`, export to PDF, or compare against another commit.
89
+
90
+ ## How it works
91
+
92
+ Spectra launches Chrome via `puppeteer-core`, records a CDP trace + CPU profile, and injects a probe that
93
+ tracks component renders, slow interactions and Web Vitals. It maps sampled frames back to your source
94
+ through source maps (and bundler markers as a fallback), then ranks the files, functions and components
95
+ that cost the most main-thread time. See the
96
+ [deep-dive docs](https://github.com/progen-labs/spectra/blob/main/DOCS.md) for how capture, correlation
97
+ and report generation work.
98
+
99
+ ## License
100
+
101
+ MIT © Progen Labs
package/bin/spectra.js ADDED
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Spectra CLI — a Progen Labs project.
4
+ *
5
+ * spectra performance [--url <page>] [profiler options] profile a frontend
6
+ * spectra dashboard open the dashboard UI
7
+ * spectra --help show this help
8
+ * spectra --version print the version
9
+ *
10
+ * When Spectra is installed in a project, it profiles THAT project: reports are
11
+ * written under the current working directory (.spectra/reports) and the project
12
+ * root is used for source lookup, unless the matching PERF_* env var overrides it.
13
+ */
14
+ import { spawn } from 'node:child_process';
15
+ import { readFileSync } from 'node:fs';
16
+ import path from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+ import { bootDevServer } from '../server/explore/boot.js';
19
+ import { detectFramework } from '../server/framework.js';
20
+
21
+ const here = path.dirname(fileURLToPath(import.meta.url));
22
+ const PKG_ROOT = path.resolve(here, '..');
23
+ const pkg = JSON.parse(
24
+ readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8'),
25
+ );
26
+
27
+ const argv = process.argv.slice(2);
28
+ const command = argv[0];
29
+ const rest = argv.slice(1);
30
+
31
+ const HELP = `
32
+ Spectra ${pkg.version} — frontend performance profiler (Progen Labs)
33
+
34
+ Usage:
35
+ spectra performance [options] Profile a frontend build and report the culprits
36
+ spectra dashboard Open the Spectra dashboard UI
37
+ spectra --help, -h Show this help
38
+ spectra --version, -v Print the version
39
+
40
+ Run it from inside your project. With no --url, Spectra detects the framework,
41
+ starts your dev server (npm run dev/start/serve), evaluates it, then shuts it
42
+ down. By default it records a baseline page load, auto-discovers the actionable
43
+ elements, synthesizes golden-path journeys, runs them (safe actions only), and
44
+ prints a performance matrix. Every path is saved as a report you can open in
45
+ the dashboard. Reports go to .spectra/reports in the current directory.
46
+
47
+ performance options:
48
+ --url <page> Evaluate an already-running page; skips self-boot
49
+ --paths <n> Max golden paths to run (default 12)
50
+ --command "<cmd>" Override the dev-server command (default: your npm dev script)
51
+ --boot-timeout <s> How long to wait for the dev server (default 60)
52
+ --headed Show the browser window
53
+ --sign-in <url> Sign-in URL template, e.g. "http://host/login/?redirect={url}"
54
+
55
+ classic single-page profile (opt in with --mode or --scenario):
56
+ --mode live|load Live session or page-load recording
57
+ --duration <seconds> Live recording length (default 8)
58
+ --throttle <n> CPU throttle multiplier (default 1)
59
+ --scenario <file.mjs> Script a live session: export default async page => {…}
60
+ --save-trace Keep the raw Chrome trace alongside the report
61
+
62
+ Examples:
63
+ spectra performance # self-boot + golden-path suite
64
+ spectra performance --paths 8 # cap the suite at 8 journeys
65
+ spectra performance --url http://localhost:3000/ # evaluate a running server
66
+ spectra performance --mode load # classic single page-load profile
67
+ `;
68
+
69
+ function runNodeAsync(scriptRelPath, args, extraEnv = {}) {
70
+ return new Promise((resolve, reject) => {
71
+ const child = spawn(
72
+ process.execPath,
73
+ [path.join(PKG_ROOT, scriptRelPath), ...args],
74
+ {
75
+ cwd: PKG_ROOT,
76
+ stdio: 'inherit',
77
+ env: { ...process.env, ...extraEnv },
78
+ },
79
+ );
80
+ child.on('exit', code => resolve(code ?? 0));
81
+ child.on('error', reject);
82
+ });
83
+ }
84
+
85
+ /** Pull `--name value` out of an argv array, returning the value and the rest. */
86
+ function takeOption(args, name) {
87
+ const i = args.indexOf(`--${name}`);
88
+ if (i === -1) return { value: undefined, rest: args };
89
+ const copy = args.slice();
90
+ const [, value] = copy.splice(i, 2);
91
+ return { value, rest: copy };
92
+ }
93
+
94
+ /**
95
+ * Default the profiled project and the reports location to the user's current
96
+ * directory, so an installed Spectra profiles the host project and writes
97
+ * reports there rather than inside node_modules. Explicit PERF_* vars win.
98
+ */
99
+ function projectEnv() {
100
+ const cwd = process.cwd();
101
+ const env = {};
102
+ if (!process.env.PERF_FRONTEND_DIR && !process.env.FRONTEND_DIR) {
103
+ env.PERF_FRONTEND_DIR = cwd;
104
+ }
105
+ if (!process.env.PERF_REPORTS_DIR) {
106
+ env.PERF_REPORTS_DIR = path.join(cwd, '.spectra', 'reports');
107
+ }
108
+ return env;
109
+ }
110
+
111
+ async function runProfile() {
112
+ if (rest.includes('--help') || rest.includes('-h')) {
113
+ console.log(HELP);
114
+ return 0;
115
+ }
116
+ const env = projectEnv();
117
+
118
+ // Golden-path evaluation is the default. --mode/--scenario opt into the
119
+ // classic single-page profile instead.
120
+ const classic = rest.includes('--mode') || rest.includes('--scenario');
121
+ const script = classic ? 'server/cli.js' : 'server/explore/evaluate-cli.js';
122
+
123
+ // An explicit --url means the server is already running: run against it.
124
+ if (rest.includes('--url')) {
125
+ return runNodeAsync(script, rest, env);
126
+ }
127
+
128
+ // No --url: self-boot the target project's own dev server, run, stop.
129
+ const cwd = process.cwd();
130
+ const { value: command, rest: afterCmd } = takeOption(rest, 'command');
131
+ const { value: bootTimeout, rest: profileArgs } = takeOption(afterCmd, 'boot-timeout');
132
+ const fw = detectFramework(cwd);
133
+ const label =
134
+ fw.framework !== 'unknown' ? `${fw.framework}/${fw.bundler}` : fw.bundler;
135
+ // Status goes to stderr so it never mixes with the report on stdout.
136
+ console.error(
137
+ `spectra: no --url given — starting the ${label} dev server in ${cwd} …`,
138
+ );
139
+
140
+ let server;
141
+ try {
142
+ server = await bootDevServer({
143
+ root: cwd,
144
+ command,
145
+ timeoutMs: bootTimeout ? Number(bootTimeout) * 1000 : 60_000,
146
+ });
147
+ } catch (err) {
148
+ console.error(`spectra: ${err.message}`);
149
+ return 1;
150
+ }
151
+ console.error(`spectra: dev server ready at ${server.url} — profiling …\n`);
152
+
153
+ let code = 1;
154
+ try {
155
+ code = await runNodeAsync(
156
+ script,
157
+ [...profileArgs, '--url', server.url],
158
+ env,
159
+ );
160
+ } finally {
161
+ await server.stop();
162
+ console.error('\nspectra: stopped the dev server.');
163
+ }
164
+ return code;
165
+ }
166
+
167
+ function runDashboard() {
168
+ // Standalone server: serves the prebuilt dashboard and the backend API, so an
169
+ // installed Spectra needs no dev toolchain. projectEnv() points it at this
170
+ // project's reports and source roots.
171
+ const child = spawn(
172
+ process.execPath,
173
+ [path.join(PKG_ROOT, 'server/dashboard.js'), ...rest],
174
+ {
175
+ cwd: PKG_ROOT,
176
+ stdio: 'inherit',
177
+ env: { ...process.env, ...projectEnv() },
178
+ },
179
+ );
180
+ child.on('exit', code => process.exit(code ?? 0));
181
+ child.on('error', err => {
182
+ console.error(`spectra: failed to start the dashboard — ${err.message}`);
183
+ process.exit(1);
184
+ });
185
+ }
186
+
187
+ switch (command) {
188
+ case 'performance':
189
+ case 'perf':
190
+ runProfile()
191
+ .then(code => process.exit(code ?? 0))
192
+ .catch(err => {
193
+ console.error(`spectra: ${err.message}`);
194
+ process.exit(1);
195
+ });
196
+ break;
197
+ case 'dashboard':
198
+ case 'ui':
199
+ runDashboard();
200
+ break;
201
+ case '--version':
202
+ case '-v':
203
+ console.log(pkg.version);
204
+ break;
205
+ case undefined:
206
+ case '--help':
207
+ case '-h':
208
+ case 'help':
209
+ console.log(HELP);
210
+ break;
211
+ default:
212
+ console.error(`spectra: unknown command "${command}"`);
213
+ console.log(HELP);
214
+ process.exit(1);
215
+ }