@promise-inc/ps-guard 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.
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +122 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +119 -0
- package/dist/config.js.map +1 -0
- package/dist/hints.d.ts +2 -0
- package/dist/hints.d.ts.map +1 -0
- package/dist/hints.js +39 -0
- package/dist/hints.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +9 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +33 -0
- package/dist/parser.js.map +1 -0
- package/dist/presets/index.d.ts +2 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +7 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/presets/landing-page.d.ts +3 -0
- package/dist/presets/landing-page.d.ts.map +1 -0
- package/dist/presets/landing-page.js +14 -0
- package/dist/presets/landing-page.js.map +1 -0
- package/dist/presets/marketing-site.d.ts +3 -0
- package/dist/presets/marketing-site.d.ts.map +1 -0
- package/dist/presets/marketing-site.js +14 -0
- package/dist/presets/marketing-site.js.map +1 -0
- package/dist/presets/nextjs.d.ts +3 -0
- package/dist/presets/nextjs.d.ts.map +1 -0
- package/dist/presets/nextjs.js +17 -0
- package/dist/presets/nextjs.js.map +1 -0
- package/dist/presets/registry.d.ts +4 -0
- package/dist/presets/registry.d.ts.map +1 -0
- package/dist/presets/registry.js +13 -0
- package/dist/presets/registry.js.map +1 -0
- package/dist/reporter.d.ts +4 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +28 -0
- package/dist/reporter.js.map +1 -0
- package/dist/runner.d.ts +16 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +78 -0
- package/dist/runner.js.map +1 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/output.d.ts +9 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +72 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/validator.d.ts +14 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +20 -0
- package/dist/validator.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Promise Inc.
|
|
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, 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
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# @promise-inc/ps-guard
|
|
2
|
+
|
|
3
|
+
Lighthouse-based performance guard. Enforce Web Vitals thresholds in CI and locally.
|
|
4
|
+
|
|
5
|
+
## Why ps-guard?
|
|
6
|
+
|
|
7
|
+
Running Lighthouse manually is tedious and inconsistent. `ps-guard` wraps Lighthouse into an opinionated CLI and API with:
|
|
8
|
+
|
|
9
|
+
- **Threshold enforcement** — fail CI if LCP, CLS, INP, TTFB, or FCP exceed your limits
|
|
10
|
+
- **Presets** — built-in configs for Next.js, landing pages, and marketing sites
|
|
11
|
+
- **Fix hints** — actionable suggestions when a metric fails
|
|
12
|
+
- **JSON output** — pipe results to dashboards or custom reporters
|
|
13
|
+
- **Zero config** — sensible defaults based on Google's Web Vitals recommendations
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### CLI
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Run with defaults (mobile, score >= 90)
|
|
21
|
+
npx ps-guard --url https://example.com
|
|
22
|
+
|
|
23
|
+
# Use a preset
|
|
24
|
+
npx ps-guard --url https://example.com --preset nextjs
|
|
25
|
+
|
|
26
|
+
# Desktop mode
|
|
27
|
+
npx ps-guard --url https://example.com --device desktop
|
|
28
|
+
|
|
29
|
+
# JSON output for CI pipelines
|
|
30
|
+
npx ps-guard --url https://example.com --json
|
|
31
|
+
|
|
32
|
+
# CI mode (no colors)
|
|
33
|
+
npx ps-guard --url https://example.com --ci
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Programmatic
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { runPSGuard } from "@promise-inc/ps-guard";
|
|
40
|
+
|
|
41
|
+
const result = await runPSGuard("https://example.com", {
|
|
42
|
+
device: "mobile",
|
|
43
|
+
minScore: 90,
|
|
44
|
+
preset: "nextjs",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!result.passed) {
|
|
48
|
+
console.log("Performance check failed:", result.metrics);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## CLI Options
|
|
53
|
+
|
|
54
|
+
| Option | Description | Default |
|
|
55
|
+
|--------|-------------|---------|
|
|
56
|
+
| `--url <url>` | URL to audit (required) | — |
|
|
57
|
+
| `-p, --preset <name>` | Use a built-in preset | — |
|
|
58
|
+
| `--device <mobile\|desktop>` | Device emulation | `mobile` |
|
|
59
|
+
| `--retries <n>` | Retry attempts | `1` |
|
|
60
|
+
| `--json` | Output as JSON | `false` |
|
|
61
|
+
| `--ci` | No colors, clean output | `false` |
|
|
62
|
+
| `-h, --help` | Show help | — |
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
Create a `ps-guard.config.json` (or `.js`/`.ts`) in your project root:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"url": "https://example.com",
|
|
71
|
+
"device": "mobile",
|
|
72
|
+
"minScore": 90,
|
|
73
|
+
"thresholds": {
|
|
74
|
+
"lcp": 2500,
|
|
75
|
+
"cls": 0.1,
|
|
76
|
+
"inp": 200,
|
|
77
|
+
"ttfb": 800,
|
|
78
|
+
"fcp": 1800
|
|
79
|
+
},
|
|
80
|
+
"retries": 2
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or add to `package.json`:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"ps-guard": {
|
|
89
|
+
"url": "https://example.com",
|
|
90
|
+
"preset": "nextjs"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Config Cascade
|
|
96
|
+
|
|
97
|
+
Priority (highest to lowest):
|
|
98
|
+
|
|
99
|
+
1. CLI arguments
|
|
100
|
+
2. Config file (`ps-guard.config.*`)
|
|
101
|
+
3. `package.json` `"ps-guard"` field
|
|
102
|
+
4. Preset values
|
|
103
|
+
5. Defaults
|
|
104
|
+
|
|
105
|
+
## Presets
|
|
106
|
+
|
|
107
|
+
| Preset | Device | LCP | CLS | INP | TTFB | FCP |
|
|
108
|
+
|--------|--------|-----|-----|-----|------|-----|
|
|
109
|
+
| `nextjs` | mobile | 2500ms | 0.1 | 200ms | 800ms | 1800ms |
|
|
110
|
+
| `landing-page` | mobile | 1800ms | 0.05 | 100ms | 500ms | 1200ms |
|
|
111
|
+
| `marketing-site` | mobile | 2500ms | 0.1 | 200ms | 800ms | 1800ms |
|
|
112
|
+
|
|
113
|
+
## Default Thresholds
|
|
114
|
+
|
|
115
|
+
Based on [Google's Web Vitals recommendations](https://web.dev/vitals/):
|
|
116
|
+
|
|
117
|
+
| Metric | Limit | Description |
|
|
118
|
+
|--------|-------|-------------|
|
|
119
|
+
| LCP | 2500ms | Largest Contentful Paint |
|
|
120
|
+
| CLS | 0.1 | Cumulative Layout Shift |
|
|
121
|
+
| INP | 200ms | Interaction to Next Paint |
|
|
122
|
+
| TTFB | 800ms | Time to First Byte |
|
|
123
|
+
| FCP | 1800ms | First Contentful Paint |
|
|
124
|
+
|
|
125
|
+
## CI / GitHub Actions
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
name: Performance Guard
|
|
129
|
+
|
|
130
|
+
on:
|
|
131
|
+
pull_request:
|
|
132
|
+
branches: [main]
|
|
133
|
+
|
|
134
|
+
jobs:
|
|
135
|
+
perf:
|
|
136
|
+
runs-on: ubuntu-latest
|
|
137
|
+
steps:
|
|
138
|
+
- uses: actions/checkout@v4
|
|
139
|
+
- uses: actions/setup-node@v4
|
|
140
|
+
with:
|
|
141
|
+
node-version: 20
|
|
142
|
+
|
|
143
|
+
- name: Install Chrome
|
|
144
|
+
uses: browser-actions/setup-chrome@v1
|
|
145
|
+
|
|
146
|
+
- name: Install dependencies
|
|
147
|
+
run: npm ci
|
|
148
|
+
|
|
149
|
+
- name: Run ps-guard
|
|
150
|
+
run: npx ps-guard --url https://staging.example.com --ci
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Requirements
|
|
154
|
+
|
|
155
|
+
- Node.js >= 18
|
|
156
|
+
- Chrome/Chromium installed (for Lighthouse)
|
|
157
|
+
|
|
158
|
+
## See Also
|
|
159
|
+
|
|
160
|
+
- [@promise-inc/ai-guard](https://github.com/promise-inc/ai-guard) — Detect AI-generated code patterns before commit/push
|
|
161
|
+
- [@promise-inc/dev-reel](https://github.com/promise-inc/dev-reel) — Animated SVG previews for READMEs
|
|
162
|
+
- [@promise-inc/devlog](https://github.com/promise-inc/devlog) — Simple logger with automatic context (file + line)
|
|
163
|
+
- [@promise-inc/fs-guard](https://github.com/promise-inc/fs-guard) — Validate project folder and file structure
|
|
164
|
+
- [@promise-inc/ui-states](https://github.com/promise-inc/ui-states) — Auto-generated skeleton loading states from real DOM
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
const runner_1 = require("./runner");
|
|
6
|
+
const validator_1 = require("./validator");
|
|
7
|
+
const reporter_1 = require("./reporter");
|
|
8
|
+
const registry_1 = require("./presets/registry");
|
|
9
|
+
const output_1 = require("./utils/output");
|
|
10
|
+
function parseArgs(args) {
|
|
11
|
+
let url;
|
|
12
|
+
let preset;
|
|
13
|
+
let device;
|
|
14
|
+
let json = false;
|
|
15
|
+
let ci = false;
|
|
16
|
+
let help = false;
|
|
17
|
+
let retries;
|
|
18
|
+
for (let i = 0; i < args.length; i++) {
|
|
19
|
+
const arg = args[i];
|
|
20
|
+
if (arg === "--url") {
|
|
21
|
+
url = args[i + 1];
|
|
22
|
+
i++;
|
|
23
|
+
}
|
|
24
|
+
else if (arg === "--preset" || arg === "-p") {
|
|
25
|
+
preset = args[i + 1];
|
|
26
|
+
i++;
|
|
27
|
+
}
|
|
28
|
+
else if (arg === "--device") {
|
|
29
|
+
const val = args[i + 1];
|
|
30
|
+
if (val === "mobile" || val === "desktop") {
|
|
31
|
+
device = val;
|
|
32
|
+
}
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
else if (arg === "--json") {
|
|
36
|
+
json = true;
|
|
37
|
+
}
|
|
38
|
+
else if (arg === "--ci") {
|
|
39
|
+
ci = true;
|
|
40
|
+
}
|
|
41
|
+
else if (arg === "--help" || arg === "-h") {
|
|
42
|
+
help = true;
|
|
43
|
+
}
|
|
44
|
+
else if (arg === "--retries") {
|
|
45
|
+
const val = parseInt(args[i + 1], 10);
|
|
46
|
+
if (!isNaN(val) && val > 0) {
|
|
47
|
+
retries = val;
|
|
48
|
+
}
|
|
49
|
+
i++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { url, preset, device, json, ci, help, retries };
|
|
53
|
+
}
|
|
54
|
+
function printHelp() {
|
|
55
|
+
console.log(`
|
|
56
|
+
\x1b[1m\x1b[36mps-guard\x1b[0m — Lighthouse-based performance guard
|
|
57
|
+
|
|
58
|
+
\x1b[1mUsage:\x1b[0m
|
|
59
|
+
npx ps-guard --url https://example.com
|
|
60
|
+
npx ps-guard --url https://example.com --preset nextjs
|
|
61
|
+
npx ps-guard --url https://example.com --json
|
|
62
|
+
|
|
63
|
+
\x1b[1mOptions:\x1b[0m
|
|
64
|
+
--url <url> URL to audit (required)
|
|
65
|
+
-p, --preset <name> Use a built-in preset
|
|
66
|
+
--device <mobile|desktop> Device emulation (default: mobile)
|
|
67
|
+
--retries <n> Number of retry attempts (default: 1)
|
|
68
|
+
--json Output results as JSON
|
|
69
|
+
--ci CI mode (no colors, clean output)
|
|
70
|
+
-h, --help Show this help message
|
|
71
|
+
|
|
72
|
+
\x1b[1mPresets:\x1b[0m
|
|
73
|
+
${registry_1.presetNames.join(", ")}
|
|
74
|
+
|
|
75
|
+
\x1b[1mConfig files:\x1b[0m
|
|
76
|
+
ps-guard.config.ts | ps-guard.config.js | ps-guard.config.json | package.json
|
|
77
|
+
|
|
78
|
+
\x1b[1mThresholds (defaults):\x1b[0m
|
|
79
|
+
LCP 2500ms Largest Contentful Paint
|
|
80
|
+
CLS 0.1 Cumulative Layout Shift
|
|
81
|
+
INP 200ms Interaction to Next Paint
|
|
82
|
+
TTFB 800ms Time to First Byte
|
|
83
|
+
FCP 1800ms First Contentful Paint
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
async function main() {
|
|
87
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
88
|
+
if (parsed.help) {
|
|
89
|
+
printHelp();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (parsed.ci) {
|
|
93
|
+
(0, output_1.setCIMode)(true);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const cwd = process.cwd();
|
|
97
|
+
const config = (0, config_1.loadConfig)(cwd, {
|
|
98
|
+
url: parsed.url,
|
|
99
|
+
preset: parsed.preset,
|
|
100
|
+
device: parsed.device,
|
|
101
|
+
retries: parsed.retries,
|
|
102
|
+
});
|
|
103
|
+
const lighthouseResult = await (0, runner_1.runLighthouse)(config);
|
|
104
|
+
const result = (0, validator_1.validateResults)(lighthouseResult.lhr, config);
|
|
105
|
+
if (parsed.json) {
|
|
106
|
+
(0, reporter_1.reportJSON)(result);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
(0, reporter_1.reportHuman)(result);
|
|
110
|
+
}
|
|
111
|
+
if (!result.passed) {
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
117
|
+
(0, output_1.printError)(message);
|
|
118
|
+
process.exit(2);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
main();
|
|
122
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAqB,MAAM,SAAS,CAAC;AAmDhE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAWlE;AAED,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,OAAO,CAAC,aAAa,CAAM,GACrC,aAAa,CA6Bf"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.resolvePreset = resolvePreset;
|
|
37
|
+
exports.loadConfig = loadConfig;
|
|
38
|
+
const fs = __importStar(require("node:fs"));
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
const registry_1 = require("./presets/registry");
|
|
41
|
+
const DEFAULT_THRESHOLDS = {
|
|
42
|
+
lcp: 2500,
|
|
43
|
+
cls: 0.1,
|
|
44
|
+
inp: 200,
|
|
45
|
+
ttfb: 800,
|
|
46
|
+
fcp: 1800,
|
|
47
|
+
};
|
|
48
|
+
const DEFAULT_CONFIG = {
|
|
49
|
+
device: "mobile",
|
|
50
|
+
minScore: 90,
|
|
51
|
+
thresholds: DEFAULT_THRESHOLDS,
|
|
52
|
+
failOnError: true,
|
|
53
|
+
retries: 1,
|
|
54
|
+
};
|
|
55
|
+
const CONFIG_FILES = [
|
|
56
|
+
"ps-guard.config.ts",
|
|
57
|
+
"ps-guard.config.js",
|
|
58
|
+
"ps-guard.config.json",
|
|
59
|
+
];
|
|
60
|
+
function loadConfigFile(cwd) {
|
|
61
|
+
for (const file of CONFIG_FILES) {
|
|
62
|
+
const filePath = path.join(cwd, file);
|
|
63
|
+
if (fs.existsSync(filePath)) {
|
|
64
|
+
if (file.endsWith(".json")) {
|
|
65
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
66
|
+
return JSON.parse(content);
|
|
67
|
+
}
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
69
|
+
const loaded = require(filePath);
|
|
70
|
+
return (loaded.default ?? loaded);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
74
|
+
if (fs.existsSync(pkgPath)) {
|
|
75
|
+
const content = fs.readFileSync(pkgPath, "utf-8");
|
|
76
|
+
const pkg = JSON.parse(content);
|
|
77
|
+
if (pkg["ps-guard"] && typeof pkg["ps-guard"] === "object") {
|
|
78
|
+
return pkg["ps-guard"];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
function resolvePreset(name) {
|
|
84
|
+
const preset = registry_1.presets[name];
|
|
85
|
+
if (!preset) {
|
|
86
|
+
throw new Error(`Unknown preset "${name}". Available: ${Object.keys(registry_1.presets).join(", ")}`);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
device: preset.device,
|
|
90
|
+
thresholds: { ...preset.thresholds },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function loadConfig(cwd, overrides = {}) {
|
|
94
|
+
const fileConfig = loadConfigFile(cwd) ?? {};
|
|
95
|
+
let presetConfig = {};
|
|
96
|
+
const presetName = overrides.preset ?? fileConfig.preset;
|
|
97
|
+
if (presetName) {
|
|
98
|
+
presetConfig = resolvePreset(presetName);
|
|
99
|
+
}
|
|
100
|
+
const merged = {
|
|
101
|
+
url: overrides.url ?? fileConfig.url ?? "",
|
|
102
|
+
device: overrides.device ?? fileConfig.device ?? presetConfig.device ?? DEFAULT_CONFIG.device,
|
|
103
|
+
minScore: overrides.minScore ?? fileConfig.minScore ?? DEFAULT_CONFIG.minScore,
|
|
104
|
+
thresholds: {
|
|
105
|
+
...DEFAULT_THRESHOLDS,
|
|
106
|
+
...presetConfig.thresholds,
|
|
107
|
+
...fileConfig.thresholds,
|
|
108
|
+
...overrides.thresholds,
|
|
109
|
+
},
|
|
110
|
+
failOnError: overrides.failOnError ?? fileConfig.failOnError ?? DEFAULT_CONFIG.failOnError,
|
|
111
|
+
retries: overrides.retries ?? fileConfig.retries ?? DEFAULT_CONFIG.retries,
|
|
112
|
+
preset: presetName,
|
|
113
|
+
};
|
|
114
|
+
if (!merged.url) {
|
|
115
|
+
throw new Error("URL is required. Use --url <url> or set it in config file.");
|
|
116
|
+
}
|
|
117
|
+
return merged;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/hints.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hints.d.ts","sourceRoot":"","sources":["../src/hints.ts"],"names":[],"mappings":"AAiCA,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAErD"}
|
package/dist/hints.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getHints = getHints;
|
|
4
|
+
const METRIC_HINTS = {
|
|
5
|
+
LCP: [
|
|
6
|
+
"Optimize or compress the largest image/video on the page",
|
|
7
|
+
"Use <link rel=\"preload\"> for critical above-the-fold resources",
|
|
8
|
+
"Reduce server response time (TTFB impacts LCP)",
|
|
9
|
+
"Remove render-blocking JavaScript and CSS",
|
|
10
|
+
],
|
|
11
|
+
CLS: [
|
|
12
|
+
"Set explicit width/height on images and videos",
|
|
13
|
+
"Avoid inserting content above existing content dynamically",
|
|
14
|
+
"Use CSS contain-intrinsic-size for lazy-loaded content",
|
|
15
|
+
"Prefer CSS transforms for animations instead of layout-triggering properties",
|
|
16
|
+
],
|
|
17
|
+
INP: [
|
|
18
|
+
"Break up long tasks (>50ms) into smaller chunks",
|
|
19
|
+
"Use requestIdleCallback or Web Workers for heavy computation",
|
|
20
|
+
"Minimize JavaScript execution on user interactions",
|
|
21
|
+
"Reduce DOM size to speed up event handling",
|
|
22
|
+
],
|
|
23
|
+
TTFB: [
|
|
24
|
+
"Use a CDN to serve content closer to users",
|
|
25
|
+
"Optimize server-side rendering or database queries",
|
|
26
|
+
"Enable HTTP/2 or HTTP/3",
|
|
27
|
+
"Implement edge caching or stale-while-revalidate",
|
|
28
|
+
],
|
|
29
|
+
FCP: [
|
|
30
|
+
"Eliminate render-blocking resources",
|
|
31
|
+
"Inline critical CSS above the fold",
|
|
32
|
+
"Reduce server response time",
|
|
33
|
+
"Preconnect to required origins",
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
function getHints(metricName) {
|
|
37
|
+
return METRIC_HINTS[metricName] ?? [];
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=hints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hints.js","sourceRoot":"","sources":["../src/hints.ts"],"names":[],"mappings":";;AAiCA,4BAEC;AAnCD,MAAM,YAAY,GAA6B;IAC7C,GAAG,EAAE;QACH,0DAA0D;QAC1D,kEAAkE;QAClE,gDAAgD;QAChD,2CAA2C;KAC5C;IACD,GAAG,EAAE;QACH,gDAAgD;QAChD,4DAA4D;QAC5D,wDAAwD;QACxD,8EAA8E;KAC/E;IACD,GAAG,EAAE;QACH,iDAAiD;QACjD,8DAA8D;QAC9D,oDAAoD;QACpD,4CAA4C;KAC7C;IACD,IAAI,EAAE;QACJ,4CAA4C;QAC5C,oDAAoD;QACpD,yBAAyB;QACzB,kDAAkD;KACnD;IACD,GAAG,EAAE;QACH,qCAAqC;QACrC,oCAAoC;QACpC,6BAA6B;QAC7B,gCAAgC;KACjC;CACF,CAAC;AAEF,SAAgB,QAAQ,CAAC,UAAkB;IACzC,OAAO,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;AACxC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PSGuardConfig, PSGuardResult } from "./types";
|
|
2
|
+
export { loadConfig, resolvePreset } from "./config";
|
|
3
|
+
export { runLighthouse } from "./runner";
|
|
4
|
+
export { parseMetrics } from "./parser";
|
|
5
|
+
export { validateResults } from "./validator";
|
|
6
|
+
export { reportHuman, reportJSON } from "./reporter";
|
|
7
|
+
export { getHints } from "./hints";
|
|
8
|
+
export { presets, presetNames } from "./presets";
|
|
9
|
+
export type { PSGuardConfig, PSGuardThresholds, MetricResult, PSGuardResult, Preset, } from "./types";
|
|
10
|
+
export declare function runPSGuard(url: string, options?: Partial<Omit<PSGuardConfig, "url">>): Promise<PSGuardResult>;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAEjD,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,MAAM,GACP,MAAM,SAAS,CAAC;AAEjB,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,CAAM,GAChD,OAAO,CAAC,aAAa,CAAC,CAIxB"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.presetNames = exports.presets = exports.getHints = exports.reportJSON = exports.reportHuman = exports.validateResults = exports.parseMetrics = exports.runLighthouse = exports.resolvePreset = exports.loadConfig = void 0;
|
|
4
|
+
exports.runPSGuard = runPSGuard;
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
const runner_1 = require("./runner");
|
|
7
|
+
const validator_1 = require("./validator");
|
|
8
|
+
var config_2 = require("./config");
|
|
9
|
+
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_2.loadConfig; } });
|
|
10
|
+
Object.defineProperty(exports, "resolvePreset", { enumerable: true, get: function () { return config_2.resolvePreset; } });
|
|
11
|
+
var runner_2 = require("./runner");
|
|
12
|
+
Object.defineProperty(exports, "runLighthouse", { enumerable: true, get: function () { return runner_2.runLighthouse; } });
|
|
13
|
+
var parser_1 = require("./parser");
|
|
14
|
+
Object.defineProperty(exports, "parseMetrics", { enumerable: true, get: function () { return parser_1.parseMetrics; } });
|
|
15
|
+
var validator_2 = require("./validator");
|
|
16
|
+
Object.defineProperty(exports, "validateResults", { enumerable: true, get: function () { return validator_2.validateResults; } });
|
|
17
|
+
var reporter_1 = require("./reporter");
|
|
18
|
+
Object.defineProperty(exports, "reportHuman", { enumerable: true, get: function () { return reporter_1.reportHuman; } });
|
|
19
|
+
Object.defineProperty(exports, "reportJSON", { enumerable: true, get: function () { return reporter_1.reportJSON; } });
|
|
20
|
+
var hints_1 = require("./hints");
|
|
21
|
+
Object.defineProperty(exports, "getHints", { enumerable: true, get: function () { return hints_1.getHints; } });
|
|
22
|
+
var presets_1 = require("./presets");
|
|
23
|
+
Object.defineProperty(exports, "presets", { enumerable: true, get: function () { return presets_1.presets; } });
|
|
24
|
+
Object.defineProperty(exports, "presetNames", { enumerable: true, get: function () { return presets_1.presetNames; } });
|
|
25
|
+
async function runPSGuard(url, options = {}) {
|
|
26
|
+
const config = (0, config_1.loadConfig)(process.cwd(), { url, ...options });
|
|
27
|
+
const lighthouseResult = await (0, runner_1.runLighthouse)(config);
|
|
28
|
+
return (0, validator_1.validateResults)(lighthouseResult.lhr, config);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAqBA,gCAOC;AA5BD,qCAAqD;AACrD,qCAA2D;AAC3D,2CAAkE;AAGlE,mCAAqD;AAA5C,oGAAA,UAAU,OAAA;AAAE,uGAAA,aAAa,OAAA;AAClC,mCAAyC;AAAhC,uGAAA,aAAa,OAAA;AACtB,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AACrB,yCAA8C;AAArC,4GAAA,eAAe,OAAA;AACxB,uCAAqD;AAA5C,uGAAA,WAAW,OAAA;AAAE,sGAAA,UAAU,OAAA;AAChC,iCAAmC;AAA1B,iGAAA,QAAQ,OAAA;AACjB,qCAAiD;AAAxC,kGAAA,OAAO,OAAA;AAAE,sGAAA,WAAW,OAAA;AAUtB,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,UAA+C,EAAE;IAEjD,MAAM,MAAM,GAAG,IAAA,mBAAW,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAC/D,MAAM,gBAAgB,GAAG,MAAM,IAAA,sBAAc,EAAC,MAAM,CAAC,CAAC;IACtD,OAAO,IAAA,2BAAgB,EAAC,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACxD,CAAC"}
|
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MetricResult, PSGuardThresholds } from "./types";
|
|
2
|
+
interface LighthouseAudits {
|
|
3
|
+
[key: string]: {
|
|
4
|
+
numericValue?: number;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
export declare function parseMetrics(audits: LighthouseAudits, thresholds: PSGuardThresholds): MetricResult[];
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE/D,UAAU,gBAAgB;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C;AAUD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,iBAAiB,GAC5B,YAAY,EAAE,CA0BhB"}
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseMetrics = parseMetrics;
|
|
4
|
+
const METRIC_MAP = {
|
|
5
|
+
lcp: { audit: "largest-contentful-paint", unit: "ms" },
|
|
6
|
+
cls: { audit: "cumulative-layout-shift", unit: "" },
|
|
7
|
+
inp: { audit: "experimental-interaction-to-next-paint", unit: "ms" },
|
|
8
|
+
ttfb: { audit: "server-response-time", unit: "ms" },
|
|
9
|
+
fcp: { audit: "first-contentful-paint", unit: "ms" },
|
|
10
|
+
};
|
|
11
|
+
function parseMetrics(audits, thresholds) {
|
|
12
|
+
const results = [];
|
|
13
|
+
for (const [key, mapping] of Object.entries(METRIC_MAP)) {
|
|
14
|
+
const thresholdKey = key;
|
|
15
|
+
const limit = thresholds[thresholdKey];
|
|
16
|
+
if (limit === undefined)
|
|
17
|
+
continue;
|
|
18
|
+
const audit = audits[mapping.audit];
|
|
19
|
+
const rawValue = audit?.numericValue ?? 0;
|
|
20
|
+
const value = key === "cls"
|
|
21
|
+
? Math.round(rawValue * 1000) / 1000
|
|
22
|
+
: Math.round(rawValue);
|
|
23
|
+
results.push({
|
|
24
|
+
name: key.toUpperCase(),
|
|
25
|
+
value,
|
|
26
|
+
limit,
|
|
27
|
+
passed: value <= limit,
|
|
28
|
+
unit: mapping.unit,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return results;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":";;AAcA,oCA6BC;AArCD,MAAM,UAAU,GAAqE;IACnF,GAAG,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,IAAI,EAAE,IAAI,EAAE;IACtD,GAAG,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE,IAAI,EAAE,EAAE,EAAE;IACnD,GAAG,EAAE,EAAE,KAAK,EAAE,wCAAwC,EAAE,IAAI,EAAE,IAAI,EAAE;IACpE,IAAI,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE,IAAI,EAAE,IAAI,EAAE;IACnD,GAAG,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE,IAAI,EAAE,IAAI,EAAE;CACrD,CAAC;AAEF,SAAgB,YAAY,CAC1B,MAAwB,EACxB,UAA6B;IAE7B,MAAM,OAAO,GAAmB,EAAE,CAAC;IAEnC,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACxD,MAAM,YAAY,GAAG,GAA8B,CAAC;QACpD,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;QAEvC,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAElC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,KAAK,EAAE,YAAY,IAAI,CAAC,CAAC;QAE1C,MAAM,KAAK,GAAG,GAAG,KAAK,KAAK;YACzB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,IAAI;YACpC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEzB,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,GAAG,CAAC,WAAW,EAAE;YACvB,KAAK;YACL,KAAK;YACL,MAAM,EAAE,KAAK,IAAI,KAAK;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/presets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.presetNames = exports.presets = void 0;
|
|
4
|
+
var registry_1 = require("./registry");
|
|
5
|
+
Object.defineProperty(exports, "presets", { enumerable: true, get: function () { return registry_1.presets; } });
|
|
6
|
+
Object.defineProperty(exports, "presetNames", { enumerable: true, get: function () { return registry_1.presetNames; } });
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/presets/index.ts"],"names":[],"mappings":";;;AAAA,uCAAkD;AAAzC,mGAAA,OAAO,OAAA;AAAE,uGAAA,WAAW,OAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"landing-page.d.ts","sourceRoot":"","sources":["../../src/presets/landing-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,eAAO,MAAM,WAAW,EAAE,MASzB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.landingPage = void 0;
|
|
4
|
+
exports.landingPage = {
|
|
5
|
+
device: "mobile",
|
|
6
|
+
thresholds: {
|
|
7
|
+
lcp: 1800,
|
|
8
|
+
cls: 0.05,
|
|
9
|
+
inp: 100,
|
|
10
|
+
ttfb: 500,
|
|
11
|
+
fcp: 1200,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=landing-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"landing-page.js","sourceRoot":"","sources":["../../src/presets/landing-page.ts"],"names":[],"mappings":";;;AAEa,QAAA,WAAW,GAAW;IACjC,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE;QACV,GAAG,EAAE,IAAI;QACT,GAAG,EAAE,IAAI;QACT,GAAG,EAAE,GAAG;QACR,IAAI,EAAE,GAAG;QACT,GAAG,EAAE,IAAI;KACV;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marketing-site.d.ts","sourceRoot":"","sources":["../../src/presets/marketing-site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,eAAO,MAAM,aAAa,EAAE,MAS3B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.marketingSite = void 0;
|
|
4
|
+
exports.marketingSite = {
|
|
5
|
+
device: "mobile",
|
|
6
|
+
thresholds: {
|
|
7
|
+
lcp: 2500,
|
|
8
|
+
cls: 0.1,
|
|
9
|
+
inp: 200,
|
|
10
|
+
ttfb: 800,
|
|
11
|
+
fcp: 1800,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=marketing-site.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marketing-site.js","sourceRoot":"","sources":["../../src/presets/marketing-site.ts"],"names":[],"mappings":";;;AAEa,QAAA,aAAa,GAAW;IACnC,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE;QACV,GAAG,EAAE,IAAI;QACT,GAAG,EAAE,GAAG;QACR,GAAG,EAAE,GAAG;QACR,IAAI,EAAE,GAAG;QACT,GAAG,EAAE,IAAI;KACV;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../../src/presets/nextjs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,eAAO,MAAM,MAAM,EAAE,MAYpB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nextjs = void 0;
|
|
4
|
+
exports.nextjs = {
|
|
5
|
+
device: "mobile",
|
|
6
|
+
thresholds: {
|
|
7
|
+
lcp: 2500,
|
|
8
|
+
cls: 0.1,
|
|
9
|
+
inp: 200,
|
|
10
|
+
ttfb: 800,
|
|
11
|
+
fcp: 1800,
|
|
12
|
+
},
|
|
13
|
+
lighthouseFlags: {
|
|
14
|
+
disableStorageReset: true,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=nextjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../src/presets/nextjs.ts"],"names":[],"mappings":";;;AAEa,QAAA,MAAM,GAAW;IAC5B,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE;QACV,GAAG,EAAE,IAAI;QACT,GAAG,EAAE,GAAG;QACR,GAAG,EAAE,GAAG;QACR,IAAI,EAAE,GAAG;QACT,GAAG,EAAE,IAAI;KACV;IACD,eAAe,EAAE;QACf,mBAAmB,EAAE,IAAI;KAC1B;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/presets/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAKvC,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAI1C,CAAC;AAEF,eAAO,MAAM,WAAW,UAAuB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.presetNames = exports.presets = void 0;
|
|
4
|
+
const nextjs_1 = require("./nextjs");
|
|
5
|
+
const landing_page_1 = require("./landing-page");
|
|
6
|
+
const marketing_site_1 = require("./marketing-site");
|
|
7
|
+
exports.presets = {
|
|
8
|
+
nextjs: nextjs_1.nextjs,
|
|
9
|
+
"landing-page": landing_page_1.landingPage,
|
|
10
|
+
"marketing-site": marketing_site_1.marketingSite,
|
|
11
|
+
};
|
|
12
|
+
exports.presetNames = Object.keys(exports.presets);
|
|
13
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/presets/registry.ts"],"names":[],"mappings":";;;AACA,qCAAkC;AAClC,iDAA6C;AAC7C,qDAAiD;AAEpC,QAAA,OAAO,GAA2B;IAC7C,MAAM,EAAN,eAAM;IACN,cAAc,EAAE,0BAAW;IAC3B,gBAAgB,EAAE,8BAAa;CAChC,CAAC;AAEW,QAAA,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,eAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAU7C,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAsBvD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAEtD"}
|
package/dist/reporter.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reportHuman = reportHuman;
|
|
4
|
+
exports.reportJSON = reportJSON;
|
|
5
|
+
const hints_1 = require("./hints");
|
|
6
|
+
const output_1 = require("./utils/output");
|
|
7
|
+
function reportHuman(result) {
|
|
8
|
+
(0, output_1.printHeader)(result.url, result.device);
|
|
9
|
+
(0, output_1.printScoreRow)(result.score, result.minScore);
|
|
10
|
+
console.log();
|
|
11
|
+
for (const metric of result.metrics) {
|
|
12
|
+
(0, output_1.printMetricRow)(metric);
|
|
13
|
+
if (!metric.passed) {
|
|
14
|
+
const hints = (0, hints_1.getHints)(metric.name);
|
|
15
|
+
for (const hint of hints) {
|
|
16
|
+
(0, output_1.printHint)(hint);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const failedCount = result.metrics.filter((m) => !m.passed).length +
|
|
21
|
+
(result.score < result.minScore ? 1 : 0);
|
|
22
|
+
const totalCount = result.metrics.length + 1;
|
|
23
|
+
(0, output_1.printSummary)(result.passed, failedCount, totalCount);
|
|
24
|
+
}
|
|
25
|
+
function reportJSON(result) {
|
|
26
|
+
console.log(JSON.stringify(result, null, 2));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":";;AAUA,kCAsBC;AAED,gCAEC;AAnCD,mCAAmC;AACnC,2CAMwB;AAExB,SAAgB,WAAW,CAAC,MAAqB;IAC/C,IAAA,oBAAW,EAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACvC,IAAA,sBAAa,EAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpC,IAAA,uBAAc,EAAC,MAAM,CAAC,CAAC;QAEvB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,IAAA,gBAAQ,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAA,kBAAS,EAAC,IAAI,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GACf,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM;QAC9C,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAE7C,IAAA,qBAAY,EAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;AACvD,CAAC;AAED,SAAgB,UAAU,CAAC,MAAqB;IAC9C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PSGuardConfig } from "./types";
|
|
2
|
+
interface LighthouseResult {
|
|
3
|
+
lhr: {
|
|
4
|
+
categories: {
|
|
5
|
+
performance?: {
|
|
6
|
+
score: number | null;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
audits: Record<string, {
|
|
10
|
+
numericValue?: number;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare function runLighthouse(config: PSGuardConfig): Promise<LighthouseResult>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,UAAU,gBAAgB;IACxB,GAAG,EAAE;QACH,UAAU,EAAE;YACV,WAAW,CAAC,EAAE;gBAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;aAAE,CAAC;SACxC,CAAC;QACF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,YAAY,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACnD,CAAC;CACH;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA6CpF"}
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runLighthouse = runLighthouse;
|
|
37
|
+
async function runLighthouse(config) {
|
|
38
|
+
const chromeLauncher = await Promise.resolve().then(() => __importStar(require("chrome-launcher")));
|
|
39
|
+
const lighthouse = await Promise.resolve().then(() => __importStar(require("lighthouse")));
|
|
40
|
+
const chrome = await chromeLauncher.launch({
|
|
41
|
+
chromeFlags: ["--headless", "--no-sandbox", "--disable-gpu"],
|
|
42
|
+
});
|
|
43
|
+
const flags = {
|
|
44
|
+
port: chrome.port,
|
|
45
|
+
output: "json",
|
|
46
|
+
logLevel: "error",
|
|
47
|
+
formFactor: config.device === "mobile" ? "mobile" : "desktop",
|
|
48
|
+
screenEmulation: config.device === "mobile"
|
|
49
|
+
? { mobile: true, width: 375, height: 812, deviceScaleFactor: 3, disabled: false }
|
|
50
|
+
: { mobile: false, width: 1350, height: 940, deviceScaleFactor: 1, disabled: false },
|
|
51
|
+
throttling: config.device === "mobile"
|
|
52
|
+
? undefined
|
|
53
|
+
: { cpuSlowdownMultiplier: 1, downloadThroughputKbps: 0, uploadThroughputKbps: 0, rttMs: 0, requestLatencyMs: 0, throughputKbps: 0 },
|
|
54
|
+
};
|
|
55
|
+
const lighthouseConfig = {
|
|
56
|
+
extends: "lighthouse:default",
|
|
57
|
+
settings: {
|
|
58
|
+
onlyCategories: ["performance"],
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
let lastError;
|
|
62
|
+
for (let attempt = 0; attempt < config.retries; attempt++) {
|
|
63
|
+
try {
|
|
64
|
+
const runFn = lighthouse.default ?? lighthouse;
|
|
65
|
+
const result = await runFn(config.url, flags, lighthouseConfig);
|
|
66
|
+
await chrome.kill();
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
lastError = error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
await chrome.kill();
|
|
74
|
+
throw lastError instanceof Error
|
|
75
|
+
? lastError
|
|
76
|
+
: new Error(`Lighthouse failed after ${config.retries} attempt(s)`);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,sCA6CC;AA7CM,KAAK,UAAU,aAAa,CAAC,MAAqB;IACvD,MAAM,cAAc,GAAG,wDAAa,iBAAiB,GAAC,CAAC;IACvD,MAAM,UAAU,GAAG,wDAAa,YAAY,GAAC,CAAC;IAE9C,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC;QACzC,WAAW,EAAE,CAAC,YAAY,EAAE,cAAc,EAAE,eAAe,CAAC;KAC7D,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG;QACZ,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE,MAAe;QACvB,QAAQ,EAAE,OAAgB;QAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAiB,CAAC,CAAC,CAAC,SAAkB;QAC/E,eAAe,EAAE,MAAM,CAAC,MAAM,KAAK,QAAQ;YACzC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;YAClF,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;QACtF,UAAU,EAAE,MAAM,CAAC,MAAM,KAAK,QAAQ;YACpC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,EAAE,qBAAqB,EAAE,CAAC,EAAE,sBAAsB,EAAE,CAAC,EAAE,oBAAoB,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE;KACvI,CAAC;IAEF,MAAM,gBAAgB,GAAG;QACvB,OAAO,EAAE,oBAAoB;QAC7B,QAAQ,EAAE;YACR,cAAc,EAAE,CAAC,aAAa,CAAC;SAChC;KACF,CAAC;IAEF,IAAI,SAAkB,CAAC;IAEvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QAC1D,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAO,KAAkB,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC;YAC9E,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,MAA0B,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAK,CAAC;QACpB,CAAC;IACH,CAAC;IAED,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACpB,MAAM,SAAS,YAAY,KAAK;QAC9B,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,IAAI,KAAK,CAAC,2BAA2B,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AACxE,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface PSGuardThresholds {
|
|
2
|
+
lcp?: number;
|
|
3
|
+
cls?: number;
|
|
4
|
+
inp?: number;
|
|
5
|
+
ttfb?: number;
|
|
6
|
+
fcp?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface PSGuardConfig {
|
|
9
|
+
url: string;
|
|
10
|
+
device: "mobile" | "desktop";
|
|
11
|
+
minScore: number;
|
|
12
|
+
thresholds: PSGuardThresholds;
|
|
13
|
+
failOnError: boolean;
|
|
14
|
+
retries: number;
|
|
15
|
+
preset?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface MetricResult {
|
|
18
|
+
name: string;
|
|
19
|
+
value: number;
|
|
20
|
+
limit: number;
|
|
21
|
+
passed: boolean;
|
|
22
|
+
unit: string;
|
|
23
|
+
}
|
|
24
|
+
export interface PSGuardResult {
|
|
25
|
+
passed: boolean;
|
|
26
|
+
score: number;
|
|
27
|
+
minScore: number;
|
|
28
|
+
metrics: MetricResult[];
|
|
29
|
+
url: string;
|
|
30
|
+
device: "mobile" | "desktop";
|
|
31
|
+
}
|
|
32
|
+
export interface Preset {
|
|
33
|
+
device: "mobile" | "desktop";
|
|
34
|
+
thresholds: PSGuardThresholds;
|
|
35
|
+
lighthouseFlags?: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
export interface CLIArgs {
|
|
38
|
+
url?: string;
|
|
39
|
+
preset?: string;
|
|
40
|
+
device?: "mobile" | "desktop";
|
|
41
|
+
json: boolean;
|
|
42
|
+
ci: boolean;
|
|
43
|
+
help: boolean;
|
|
44
|
+
retries?: number;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC9B;AAED,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MetricResult } from "../types";
|
|
2
|
+
export declare function setCIMode(enabled: boolean): void;
|
|
3
|
+
export declare function printHeader(url: string, device: string): void;
|
|
4
|
+
export declare function printScoreRow(score: number, minScore: number): void;
|
|
5
|
+
export declare function printMetricRow(metric: MetricResult): void;
|
|
6
|
+
export declare function printHint(hint: string): void;
|
|
7
|
+
export declare function printSummary(passed: boolean, failedCount: number, totalCount: number): void;
|
|
8
|
+
export declare function printError(message: string): void;
|
|
9
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAc7C,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEhD;AAYD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAO7D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAKnE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAQzD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAU3F;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEhD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setCIMode = setCIMode;
|
|
4
|
+
exports.printHeader = printHeader;
|
|
5
|
+
exports.printScoreRow = printScoreRow;
|
|
6
|
+
exports.printMetricRow = printMetricRow;
|
|
7
|
+
exports.printHint = printHint;
|
|
8
|
+
exports.printSummary = printSummary;
|
|
9
|
+
exports.printError = printError;
|
|
10
|
+
const COLORS = {
|
|
11
|
+
red: "\x1b[31m",
|
|
12
|
+
green: "\x1b[32m",
|
|
13
|
+
yellow: "\x1b[33m",
|
|
14
|
+
cyan: "\x1b[36m",
|
|
15
|
+
dim: "\x1b[2m",
|
|
16
|
+
bold: "\x1b[1m",
|
|
17
|
+
reset: "\x1b[0m",
|
|
18
|
+
};
|
|
19
|
+
let ciMode = false;
|
|
20
|
+
function setCIMode(enabled) {
|
|
21
|
+
ciMode = enabled;
|
|
22
|
+
}
|
|
23
|
+
function c(color, text) {
|
|
24
|
+
if (ciMode)
|
|
25
|
+
return text;
|
|
26
|
+
return `${COLORS[color]}${text}${COLORS.reset}`;
|
|
27
|
+
}
|
|
28
|
+
function bc(color, text) {
|
|
29
|
+
if (ciMode)
|
|
30
|
+
return text;
|
|
31
|
+
return `${COLORS.bold}${COLORS[color]}${text}${COLORS.reset}`;
|
|
32
|
+
}
|
|
33
|
+
function printHeader(url, device) {
|
|
34
|
+
console.log();
|
|
35
|
+
console.log(bc("cyan", "ps-guard"));
|
|
36
|
+
console.log();
|
|
37
|
+
console.log(` URL: ${c("cyan", url)}`);
|
|
38
|
+
console.log(` Device: ${c("cyan", device)}`);
|
|
39
|
+
console.log();
|
|
40
|
+
}
|
|
41
|
+
function printScoreRow(score, minScore) {
|
|
42
|
+
const passed = score >= minScore;
|
|
43
|
+
const icon = passed ? c("green", "✔") : c("red", "✖");
|
|
44
|
+
const label = passed ? c("green", String(score)) : c("red", String(score));
|
|
45
|
+
console.log(` ${icon} Performance Score: ${label} ${c("dim", `(min: ${minScore})`)}`);
|
|
46
|
+
}
|
|
47
|
+
function printMetricRow(metric) {
|
|
48
|
+
const icon = metric.passed ? c("green", "✔") : c("red", "✖");
|
|
49
|
+
const valueStr = metric.passed
|
|
50
|
+
? c("green", `${metric.value}${metric.unit}`)
|
|
51
|
+
: c("red", `${metric.value}${metric.unit}`);
|
|
52
|
+
const limitStr = c("dim", `(max: ${metric.limit}${metric.unit})`);
|
|
53
|
+
const padding = " ".repeat(Math.max(0, 6 - metric.name.length));
|
|
54
|
+
console.log(` ${icon} ${metric.name}${padding} ${valueStr} ${limitStr}`);
|
|
55
|
+
}
|
|
56
|
+
function printHint(hint) {
|
|
57
|
+
console.log(` ${c("yellow", "→")} ${c("dim", hint)}`);
|
|
58
|
+
}
|
|
59
|
+
function printSummary(passed, failedCount, totalCount) {
|
|
60
|
+
console.log();
|
|
61
|
+
if (passed) {
|
|
62
|
+
console.log(` ${bc("green", "✔ All checks passed")}`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log(` ${bc("red", `✖ ${failedCount} of ${totalCount} checks failed`)}`);
|
|
66
|
+
}
|
|
67
|
+
console.log();
|
|
68
|
+
}
|
|
69
|
+
function printError(message) {
|
|
70
|
+
console.error(`\n ${c("red", `✖ ${message}`)}\n`);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PSGuardConfig, PSGuardResult } from "./types";
|
|
2
|
+
interface LighthouseLHR {
|
|
3
|
+
categories: {
|
|
4
|
+
performance?: {
|
|
5
|
+
score: number | null;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
audits: Record<string, {
|
|
9
|
+
numericValue?: number;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export declare function validateResults(lhr: LighthouseLHR, config: PSGuardConfig): PSGuardResult;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG1E,UAAU,aAAa;IACrB,UAAU,EAAE;QACV,WAAW,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,CAAC;KACxC,CAAC;IACF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,aAAa,GACpB,aAAa,CAiBf"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateResults = validateResults;
|
|
4
|
+
const parser_1 = require("./parser");
|
|
5
|
+
function validateResults(lhr, config) {
|
|
6
|
+
const rawScore = lhr.categories.performance?.score ?? 0;
|
|
7
|
+
const score = Math.round(rawScore * 100);
|
|
8
|
+
const metrics = (0, parser_1.parseMetrics)(lhr.audits, config.thresholds);
|
|
9
|
+
const scorePassed = score >= config.minScore;
|
|
10
|
+
const allMetricsPassed = metrics.every((m) => m.passed);
|
|
11
|
+
return {
|
|
12
|
+
passed: scorePassed && allMetricsPassed,
|
|
13
|
+
score,
|
|
14
|
+
minScore: config.minScore,
|
|
15
|
+
metrics,
|
|
16
|
+
url: config.url,
|
|
17
|
+
device: config.device,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":";;AAUA,0CAoBC;AA7BD,qCAAwC;AASxC,SAAgB,eAAe,CAC7B,GAAkB,EAClB,MAAqB;IAErB,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC;IAEzC,MAAM,OAAO,GAAmB,IAAA,qBAAY,EAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAE5E,MAAM,WAAW,GAAG,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC;IAC7C,MAAM,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAExD,OAAO;QACL,MAAM,EAAE,WAAW,IAAI,gBAAgB;QACvC,KAAK;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,OAAO;QACP,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@promise-inc/ps-guard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lighthouse-based performance guard. Enforce Web Vitals thresholds in CI and locally.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ps-guard": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"start": "node dist/cli.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"performance",
|
|
17
|
+
"lighthouse",
|
|
18
|
+
"web-vitals",
|
|
19
|
+
"guard",
|
|
20
|
+
"ci",
|
|
21
|
+
"lcp",
|
|
22
|
+
"cls",
|
|
23
|
+
"inp",
|
|
24
|
+
"ttfb",
|
|
25
|
+
"devtools"
|
|
26
|
+
],
|
|
27
|
+
"homepage": "https://github.com/promise-inc/ps-guard#readme",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/promise-inc/ps-guard.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/promise-inc/ps-guard/issues"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"lighthouse": "^12.0.0",
|
|
38
|
+
"chrome-launcher": "^1.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"typescript": "^5.7.0",
|
|
42
|
+
"@types/node": "^22.0.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md"
|
|
50
|
+
]
|
|
51
|
+
}
|