@designesy/score 0.5.0 → 0.5.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.
package/README.md CHANGED
@@ -1,111 +1,111 @@
1
- # @designesy/score
2
-
3
- Standalone 42-check design-contract scoring engine. Fetches a URL, extracts CSS + `:root` tokens, runs all checks locally, with **no server required**. Zero dependencies.
4
-
5
- ## Install
6
-
7
- ```bash
8
- npm install --save-dev @designesy/score
9
- # or
10
- npx @designesy/score <url>
11
- ```
12
-
13
- ## CLI
14
-
15
- ```bash
16
- # Score a site (prints formatted report)
17
- npx @designesy/score designesy.org
18
-
19
- # CI gate: fail if below threshold
20
- npx @designesy/score linear.app --min-score 70 --min-grade B
21
-
22
- # JSON output for pipelines
23
- npx @designesy/score vercel.com --json
24
-
25
- # Markdown review format (jakubkrehel-compatible)
26
- npx @designesy/score stripe.com --format review
27
-
28
- # Scoring scope: contract (strict, all checks penalize absence) or universal (fair to external sites)
29
- npx @designesy/score designesy.org --scope contract
30
- ```
31
-
32
- ### Options
33
-
34
- | Flag | Description |
35
- |---|---|
36
- | `--format <f>` | Emission format: `designesy` (default), `canonical`, `review`, `google` |
37
- | `--scope <s>` | Scoring scope: `contract` (strict) or `universal` (fair, default auto-detect) |
38
- | `--min-score <n>` | Exit 1 if score < n |
39
- | `--min-grade <g>` | Exit 1 if grade worse than g (A/B/C/D/F) |
40
- | `--json` | Output raw JSON (no formatted report) |
41
- | `--quiet` | Only output on failure (CI noise reduction) |
42
- | `--help, -h` | Show help |
43
-
44
- ## Library
45
-
46
- ```typescript
47
- import { scoreUrl } from '@designesy/score';
48
-
49
- const result = await scoreUrl('https://linear.app');
50
- console.log(result.score); // 72.5
51
- console.log(result.grade); // 'C'
52
- console.log(result.pass); // 28
53
- console.log(result.fail); // 3
54
- console.log(result.checks); // [{ id: 'v01', status: 'PASS', detail: '...' }, ...]
55
- ```
56
-
57
- ## The 42 checks
58
-
59
- | Category | Checks | Weight |
60
- |---|---|---|
61
- | cadence | v14, v15, v16, v17, v18, v19, v20, v26, v28, x01, x02, x03 | 18 |
62
- | accessibility | v06, v22, v24, v25, v27, v35 | 15 |
63
- | semantic | v42, v43 | 12 |
64
- | motion | v05, v11, v12, v23 | 10 |
65
- | tokens | v01, v29 | 9 |
66
- | takt | v10, v13 | 8 |
67
- | copywriting | v38, v39, v40, v41 | 8 |
68
- | poise | v04, v08, v09 | 7 |
69
- | identity | v07, v34 | 6 |
70
- | interaction | v03 | 6 |
71
- | performance | v21 | 6 |
72
- | security | v36 | 5 |
73
- | spec | v37 | 4 |
74
- | responsive | v02 | 3 |
75
-
76
- Plus 12 anti-slop deductions (S1 to S12) and 7 originality lifts (O1 to O7).
77
-
78
- ## Scoring
79
-
80
- - Weighted per-category scoring (PASS = 1.0, WARN = 0.5, FAIL = 0, SKIP/MANUAL excluded)
81
- - Anti-slop deductions (up to -20pts for generic AI patterns)
82
- - Originality lifts (up to +8pts for bespoke craft signals)
83
- - Accessibility floor (score capped at C/70 when any a11y FAIL exists)
84
- - Hard-fail ceilings (critical issues cap the score further)
85
-
86
- ## Scope system
87
-
88
- - **contract** (default for designesy.org): All 42 checks active. Absence = WARN/FAIL.
89
- - **universal** (default for external sites): Optional features SKIP on absence instead of penalizing.
90
-
91
- Auto-detect: `designesy.org` → contract, everything else → universal.
92
-
93
- ## Zero dependencies
94
-
95
- Uses only Node.js built-ins:
96
- - `node:https` for URL fetching (avoids the Windows libuv/undici crash)
97
- - `node:dns` for SSRF guard (DNS resolution validation)
98
- - No `ipaddr.js`, no `undici`, no `fetch()`, no external packages
99
-
100
- ## SSRF guard
101
-
102
- Built-in SSRF protection rejects:
103
- - Private IP ranges (10.x, 127.x, 169.254.x, 172.16-31.x, 192.168.x)
104
- - Cloud metadata endpoints (169.254.169.254)
105
- - All IPv6 addresses
106
- - Encoded IP bypasses (decimal, octal, hex)
107
- - Redirect chains to internal addresses
108
-
109
- ## License
110
-
1
+ # @designesy/score
2
+
3
+ Standalone 42-check design-contract scoring engine. Fetches a URL, extracts CSS + `:root` tokens, runs all checks locally, with **no server required**. Zero dependencies.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install --save-dev @designesy/score
9
+ # or
10
+ npx @designesy/score <url>
11
+ ```
12
+
13
+ ## CLI
14
+
15
+ ```bash
16
+ # Score a site (prints formatted report)
17
+ npx @designesy/score designesy.org
18
+
19
+ # CI gate: fail if below threshold
20
+ npx @designesy/score linear.app --min-score 70 --min-grade B
21
+
22
+ # JSON output for pipelines
23
+ npx @designesy/score vercel.com --json
24
+
25
+ # Markdown review format (jakubkrehel-compatible)
26
+ npx @designesy/score stripe.com --format review
27
+
28
+ # Scoring scope: contract (strict, all checks penalize absence) or universal (fair to external sites)
29
+ npx @designesy/score designesy.org --scope contract
30
+ ```
31
+
32
+ ### Options
33
+
34
+ | Flag | Description |
35
+ |---|---|
36
+ | `--format <f>` | Emission format: `designesy` (default), `canonical`, `review`, `google` |
37
+ | `--scope <s>` | Scoring scope: `contract` (strict) or `universal` (fair, default auto-detect) |
38
+ | `--min-score <n>` | Exit 1 if score < n |
39
+ | `--min-grade <g>` | Exit 1 if grade worse than g (A/B/C/D/F) |
40
+ | `--json` | Output raw JSON (no formatted report) |
41
+ | `--quiet` | Only output on failure (CI noise reduction) |
42
+ | `--help, -h` | Show help |
43
+
44
+ ## Library
45
+
46
+ ```typescript
47
+ import { scoreUrl } from '@designesy/score';
48
+
49
+ const result = await scoreUrl('https://linear.app');
50
+ console.log(result.score); // 72.5
51
+ console.log(result.grade); // 'C'
52
+ console.log(result.pass); // 28
53
+ console.log(result.fail); // 3
54
+ console.log(result.checks); // [{ id: 'v01', status: 'PASS', detail: '...' }, ...]
55
+ ```
56
+
57
+ ## The 42 checks
58
+
59
+ | Category | Checks | Weight |
60
+ |---|---|---|
61
+ | cadence | v14, v15, v16, v17, v18, v19, v20, v26, v28, x01, x02, x03 | 18 |
62
+ | accessibility | v06, v22, v24, v25, v27, v35 | 15 |
63
+ | semantic | v42, v43 | 12 |
64
+ | motion | v05, v11, v12, v23 | 10 |
65
+ | tokens | v01, v29 | 9 |
66
+ | takt | v10, v13 | 8 |
67
+ | copywriting | v38, v39, v40, v41 | 8 |
68
+ | poise | v04, v08, v09 | 7 |
69
+ | identity | v07, v34 | 6 |
70
+ | interaction | v03 | 6 |
71
+ | performance | v21 | 6 |
72
+ | security | v36 | 5 |
73
+ | spec | v37 | 4 |
74
+ | responsive | v02 | 3 |
75
+
76
+ Plus 12 anti-slop deductions (S1 to S12) and 7 originality lifts (O1 to O7).
77
+
78
+ ## Scoring
79
+
80
+ - Weighted per-category scoring (PASS = 1.0, WARN = 0.5, FAIL = 0, SKIP/MANUAL excluded)
81
+ - Anti-slop deductions (up to -20pts for generic AI patterns)
82
+ - Originality lifts (up to +8pts for bespoke craft signals)
83
+ - Accessibility floor (score capped at C/70 when any a11y FAIL exists)
84
+ - Hard-fail ceilings (critical issues cap the score further)
85
+
86
+ ## Scope system
87
+
88
+ - **contract** (default for designesy.org): All 42 checks active. Absence = WARN/FAIL.
89
+ - **universal** (default for external sites): Optional features SKIP on absence instead of penalizing.
90
+
91
+ Auto-detect: `designesy.org` → contract, everything else → universal.
92
+
93
+ ## Zero dependencies
94
+
95
+ Uses only Node.js built-ins:
96
+ - `node:https` for URL fetching (avoids the Windows libuv/undici crash)
97
+ - `node:dns` for SSRF guard (DNS resolution validation)
98
+ - No `ipaddr.js`, no `undici`, no `fetch()`, no external packages
99
+
100
+ ## SSRF guard
101
+
102
+ Built-in SSRF protection rejects:
103
+ - Private IP ranges (10.x, 127.x, 169.254.x, 172.16-31.x, 192.168.x)
104
+ - Cloud metadata endpoints (169.254.169.254)
105
+ - All IPv6 addresses
106
+ - Encoded IP bypasses (decimal, octal, hex)
107
+ - Redirect chains to internal addresses
108
+
109
+ ## License
110
+
111
111
  MIT © [Designesy](https://www.designesy.org)
package/dist/cli.js CHANGED
@@ -97,29 +97,29 @@ function formatReport(result, url) {
97
97
  return lines.join('\n');
98
98
  }
99
99
  function printUsage() {
100
- console.log(`
101
- designesy-score: Score a URL against the 42-check Designesy engine.
102
-
103
- Runs locally: no server required. Fetches the URL, extracts CSS + tokens,
104
- and runs all 42 checks in one process.
105
-
106
- Usage:
107
- designesy-score <url> [options]
108
-
109
- Options:
110
- --format <f> Emission format: designesy (default), canonical, review, google
111
- --scope <s> Scoring scope: contract (strict) or universal (fair, default auto)
112
- --min-score <n> Exit 1 if score < n (default: 0 = disabled)
113
- --min-grade <g> Exit 1 if grade worse than g (A/B/C/D/F, default: disabled)
114
- --json Output raw JSON (no formatted report)
115
- --quiet Only output on failure
116
- --help, -h Show this help
117
-
118
- Examples:
119
- designesy-score designesy.org
120
- designesy-score linear.app --min-score 70 --min-grade B
121
- designesy-score vercel.com --format canonical --json
122
- designesy-score stripe.com --format review
100
+ console.log(`
101
+ designesy-score: Score a URL against the 42-check Designesy engine.
102
+
103
+ Runs locally: no server required. Fetches the URL, extracts CSS + tokens,
104
+ and runs all 42 checks in one process.
105
+
106
+ Usage:
107
+ designesy-score <url> [options]
108
+
109
+ Options:
110
+ --format <f> Emission format: designesy (default), canonical, review, google
111
+ --scope <s> Scoring scope: contract (strict) or universal (fair, default auto)
112
+ --min-score <n> Exit 1 if score < n (default: 0 = disabled)
113
+ --min-grade <g> Exit 1 if grade worse than g (A/B/C/D/F, default: disabled)
114
+ --json Output raw JSON (no formatted report)
115
+ --quiet Only output on failure
116
+ --help, -h Show this help
117
+
118
+ Examples:
119
+ designesy-score designesy.org
120
+ designesy-score linear.app --min-score 70 --min-grade B
121
+ designesy-score vercel.com --format canonical --json
122
+ designesy-score stripe.com --format review
123
123
  `);
124
124
  }
125
125
  function parseArgs(argv) {
@@ -189,8 +189,10 @@ async function main() {
189
189
  let scope;
190
190
  if (args.scope === 'contract' || args.scope === 'universal')
191
191
  scope = args.scope;
192
+ // Progress and the gate verdict go to stderr: stdout carries only the
193
+ // result, so --json and the JSON formats pipe straight into a parser.
192
194
  if (!args.quiet) {
193
- console.log(`${DIM}Scoring ${url} locally (42-check engine, scope=${scope || 'auto'})…${RESET}`);
195
+ console.error(`${DIM}Scoring ${url} locally (42-check engine, scope=${scope || 'auto'})…${RESET}`);
194
196
  }
195
197
  let result;
196
198
  try {
@@ -238,7 +240,7 @@ async function main() {
238
240
  // gate, and it must not be compared against a floor — there is no measurement
239
241
  // to compare. This is the exit-code consequence of the unreachable state.
240
242
  if (score === null || grade === null) {
241
- console.error(`
243
+ console.error(`
242
244
  ${BOLD}Quality gate failed${RESET}: ${result.unreachableDetail ?? 'the target could not be read'}`);
243
245
  process.exit(1);
244
246
  }
@@ -251,7 +253,7 @@ ${BOLD}Quality gate failed${RESET}: ${result.unreachableDetail ?? 'the target co
251
253
  process.exit(1);
252
254
  }
253
255
  if (!args.quiet) {
254
- console.log(`\n${BOLD}Quality gate passed${RESET}`);
256
+ console.error(`\n${BOLD}Quality gate passed${RESET}`);
255
257
  }
256
258
  }
257
259
  main().catch((e) => {
package/dist/engine.js CHANGED
@@ -2035,13 +2035,14 @@ async function checkDesignMdSpec(targetUrl) {
2035
2035
  lintDesignMd = mod.lint;
2036
2036
  }
2037
2037
  catch (e) {
2038
- // WARN, not PASS. The file was fetched but NOTHING validated it: reporting
2039
- // PASS here asserted "served with valid frontmatter" on the strength of the
2040
- // frontmatter check alone — which the branch above already ran, and which
2041
- // would have WARNed rather than reached here. Implying validation that never
2042
- // happened is the same false-PASS class as the all-SKIP review verdict.
2038
+ // MANUAL, not PASS and not WARN. The file was fetched but NOTHING validated
2039
+ // it: PASS would assert validation that never happened (the false-PASS
2040
+ // class of the all-SKIP review verdict). WARN charged the scored site for a
2041
+ // package missing from THIS runtime, so the same page scored lower from the
2042
+ // npm CLI than from the API. Unverified-here is what MANUAL means (v02,
2043
+ // v04, v21): weight 0, resolved by a run that has the linter.
2043
2044
  const msg = e instanceof Error ? e.message : 'unknown error';
2044
- return { id: 'v37', item: ITEM, category: CATEGORY, status: 'WARN', detail: `/DESIGN.md fetched but linter unavailable: ${msg}. The @google/design.md package may not be installed in this runtime.` };
2045
+ return { id: 'v37', item: ITEM, category: CATEGORY, status: 'MANUAL', detail: `/DESIGN.md fetched but linter unavailable: ${msg}. The @google/design.md package may not be installed in this runtime; run the full audit to resolve.` };
2045
2046
  }
2046
2047
  try {
2047
2048
  const report = lintDesignMd(r.text);
package/package.json CHANGED
@@ -1,55 +1,55 @@
1
- {
2
- "name": "@designesy/score",
3
- "version": "0.5.0",
4
- "description": "Standalone 42-check design-contract scoring engine: fetches a URL, extracts CSS + tokens, and runs all checks locally. Zero dependencies.",
5
- "license": "MIT",
6
- "author": "Designesy <hello@designesy.org>",
7
- "homepage": "https://www.designesy.org/score",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/LE-VAI/designesy-org.git",
11
- "directory": "packages/score"
12
- },
13
- "bugs": {
14
- "url": "https://github.com/LE-VAI/designesy-org/issues"
15
- },
16
- "keywords": [
17
- "design-system",
18
- "design-contract",
19
- "design-verification",
20
- "accessibility",
21
- "wcag",
22
- "core-web-vitals",
23
- "ci",
24
- "quality-gate",
25
- "designesy"
26
- ],
27
- "type": "module",
28
- "exports": {
29
- ".": {
30
- "import": "./dist/engine.js",
31
- "types": "./dist/engine.d.ts"
32
- }
33
- },
34
- "main": "./dist/engine.js",
35
- "types": "./dist/engine.d.ts",
36
- "bin": {
37
- "designesy-score": "dist/cli.js"
38
- },
39
- "files": [
40
- "dist",
41
- "README.md"
42
- ],
43
- "engines": {
44
- "node": ">=18.0.0"
45
- },
46
- "devDependencies": {
47
- "@types/node": "^26.2.0",
48
- "typescript": "^5.7.0"
49
- },
50
- "scripts": {
51
- "build": "tsc -p tsconfig.json",
52
- "test": "node --test",
53
- "prepublishOnly": "npm run build"
54
- }
55
- }
1
+ {
2
+ "name": "@designesy/score",
3
+ "version": "0.5.1",
4
+ "description": "Standalone 42-check design-contract scoring engine: fetches a URL, extracts CSS + tokens, and runs all checks locally. Zero dependencies.",
5
+ "license": "MIT",
6
+ "author": "Designesy <hello@designesy.org>",
7
+ "homepage": "https://www.designesy.org/score",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/LE-VAI/designesy-org.git",
11
+ "directory": "packages/score"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/LE-VAI/designesy-org/issues"
15
+ },
16
+ "keywords": [
17
+ "design-system",
18
+ "design-contract",
19
+ "design-verification",
20
+ "accessibility",
21
+ "wcag",
22
+ "core-web-vitals",
23
+ "ci",
24
+ "quality-gate",
25
+ "designesy"
26
+ ],
27
+ "type": "module",
28
+ "exports": {
29
+ ".": {
30
+ "import": "./dist/engine.js",
31
+ "types": "./dist/engine.d.ts"
32
+ }
33
+ },
34
+ "main": "./dist/engine.js",
35
+ "types": "./dist/engine.d.ts",
36
+ "bin": {
37
+ "designesy-score": "dist/cli.js"
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "README.md"
42
+ ],
43
+ "engines": {
44
+ "node": ">=18.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^26.2.0",
48
+ "typescript": "^5.7.0"
49
+ },
50
+ "scripts": {
51
+ "build": "tsc -p tsconfig.json",
52
+ "test": "node --test",
53
+ "prepublishOnly": "npm run build"
54
+ }
55
+ }