@astralkit/mcp 1.8.0 → 1.9.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/CHANGELOG.md +27 -0
- package/dist/api.js +2 -2
- package/dist/audit.d.ts +22 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +402 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth.d.ts +18 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +21 -13
- package/dist/auth.js.map +1 -1
- package/dist/browser.d.ts +1686 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +51 -0
- package/dist/browser.js.map +1 -0
- package/dist/capture.d.ts +7 -3
- package/dist/capture.d.ts.map +1 -1
- package/dist/capture.js +17 -53
- package/dist/capture.js.map +1 -1
- package/dist/data/art-direction.d.ts +2 -0
- package/dist/data/art-direction.d.ts.map +1 -0
- package/dist/data/art-direction.js +316 -0
- package/dist/data/art-direction.js.map +1 -0
- package/dist/data/crosswalk.d.ts +1 -1
- package/dist/data/crosswalk.d.ts.map +1 -1
- package/dist/data/crosswalk.js +135 -2
- package/dist/data/crosswalk.js.map +1 -1
- package/dist/data/polish.d.ts.map +1 -1
- package/dist/data/polish.js +19 -6
- package/dist/data/polish.js.map +1 -1
- package/dist/data/rules.d.ts +1 -1
- package/dist/data/rules.d.ts.map +1 -1
- package/dist/data/rules.js +55 -3
- package/dist/data/rules.js.map +1 -1
- package/dist/data/screens.d.ts.map +1 -1
- package/dist/data/screens.js +36 -4
- package/dist/data/screens.js.map +1 -1
- package/dist/data/theming.d.ts +2 -0
- package/dist/data/theming.d.ts.map +1 -0
- package/dist/data/theming.js +71 -0
- package/dist/data/theming.js.map +1 -0
- package/dist/data/visual.d.ts.map +1 -1
- package/dist/data/visual.js +25 -0
- package/dist/data/visual.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +436 -54
- package/dist/server.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `@astralkit/mcp` package.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.9.0] — 2026-08-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **`audit_page` tool (free tier)** — shipyard-grade numerical page audit at multiple breakpoints: horizontal reflow (naming offenders), tap targets (WCAG 2.5.8), prose line measure, document structure, focus visibility, motion/`prefers-reduced-motion`, CLS risk, WCAG contrast with alpha compositing through the DOM tree, plus a **rendered font-size histogram** and icon-size sweep that measure the AI sizing bias instead of asking the model to feel it (sub-10px text = error; 10-11px = justify-or-fix).
|
|
12
|
+
- **`get_theming` tool (free tier)** — the 16 designed palettes (9 light, 7 dark) with mood descriptors and the 2-step application (`astralkit/palettes` import + `data-ak-theme`). Shipping the bare black-and-white default theme is now called out as a failure.
|
|
13
|
+
- **`clickSelector` on `screenshot_ui` / `verify_visual`** — click a trigger before capture, so dropdown/modal/menu OPEN states get visually verified (the class of bug static screenshots always missed).
|
|
14
|
+
- **`tablet` viewport (768×1024)** on all capture tools — the width where late-breakpoint bugs hide; the verify loop now demands desktop + tablet + mobile.
|
|
15
|
+
- **`get_art_direction` tool** — per-context asset direction (heroes, dashboards, auth…) for agents with connected image-generation tools; screen blueprints now carry asset-generation notes.
|
|
16
|
+
- **`preflight` MCP prompt** — chains tsc → build → validate_code → visual (3 widths + open states) → audit_page as one quality gate.
|
|
17
|
+
- **Prerequisite prompts** — when no local browser exists, capture/audit tools return a structured install question (playwright-core) instead of silently degrading.
|
|
18
|
+
- **Response-embedded workflow** — `search_components` returns `nextSteps`, recipes return an `afterInstall` checklist, `install_component` returns explicit RUN/verify instructions with `mustExecute: true`. MCP clients truncate long server instructions; tool responses always arrive intact.
|
|
19
|
+
- **9 new validate_code rules** — `css-var-instead-of-class`, `bare-color`, `overlay-surface` (dropdowns missing bg/border/shadow), `no-responsive-nav`, `tiny-text` / `small-text-density` / `tiny-icon` (the sizing bias), `dot-notation-spacing`, `opacity-on-ak-token`, `at-apply`, `important-used`, `css-module-import`.
|
|
20
|
+
- **Rule Zero (library-first) + gotchas + preflight gates** in coding standards; 7-phase polish procedure with install-and-replace; completeness check (re-fetch the recipe, diff feature-by-feature) in blueprints and instructions.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- `SERVER_INSTRUCTIONS` compressed to ~2k chars so clients never truncate the golden path.
|
|
24
|
+
- `howToAdapt` now leads with "INSTALL first — the component is NOT available until the command executes."
|
|
25
|
+
|
|
26
|
+
## [1.8.0] — earlier
|
|
27
|
+
Previous published release.
|
package/dist/api.js
CHANGED
|
@@ -35,8 +35,8 @@ async function request(path) {
|
|
|
35
35
|
const url = `${apiBase}${path}`;
|
|
36
36
|
const headers = {
|
|
37
37
|
'Accept': 'application/json',
|
|
38
|
-
'User-Agent': '@astralkit/mcp/1.
|
|
39
|
-
'X-AstralKit-CLI': 'mcp-1.
|
|
38
|
+
'User-Agent': '@astralkit/mcp/1.9.0',
|
|
39
|
+
'X-AstralKit-CLI': 'mcp-1.9.0',
|
|
40
40
|
};
|
|
41
41
|
if (apiKey) {
|
|
42
42
|
headers['x-api-key'] = apiKey;
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface AuditFinding {
|
|
2
|
+
severity: 'error' | 'warning' | 'note';
|
|
3
|
+
check: string;
|
|
4
|
+
detail: string;
|
|
5
|
+
elements?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface AuditBreakpoint {
|
|
8
|
+
width: number;
|
|
9
|
+
findings: AuditFinding[];
|
|
10
|
+
}
|
|
11
|
+
export interface AuditResult {
|
|
12
|
+
url: string;
|
|
13
|
+
breakpoints: AuditBreakpoint[];
|
|
14
|
+
pageLevel: AuditFinding[];
|
|
15
|
+
summary: {
|
|
16
|
+
errors: number;
|
|
17
|
+
warnings: number;
|
|
18
|
+
notes: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function auditPage(url: string, breakpoints?: number[]): Promise<AuditResult>;
|
|
22
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9D;AAMD,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAkYzF"}
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// Numerical web page auditor — the shipyard-grade quality gate.
|
|
2
|
+
//
|
|
3
|
+
// Ported from shipyard-skool/book-system/web-audit.mjs. Runs 8 checks at
|
|
4
|
+
// multiple breakpoints via Playwright page.evaluate() in the browser context.
|
|
5
|
+
// Returns structured findings with error counts, not visual judgment.
|
|
6
|
+
//
|
|
7
|
+
// Checks: reflow, tap targets, line measure, document structure, focus
|
|
8
|
+
// visibility, motion/reduced-motion, CLS risk, WCAG color contrast (with
|
|
9
|
+
// alpha compositing through the DOM tree).
|
|
10
|
+
import { withBrowser } from './browser.js';
|
|
11
|
+
const NAV_TIMEOUT_MS = 25_000;
|
|
12
|
+
const SETTLE_MS = 600;
|
|
13
|
+
const DEFAULT_BREAKPOINTS = [390, 768, 1440];
|
|
14
|
+
export async function auditPage(url, breakpoints) {
|
|
15
|
+
const widths = breakpoints ?? DEFAULT_BREAKPOINTS;
|
|
16
|
+
return withBrowser(async (browser) => {
|
|
17
|
+
const bpResults = [];
|
|
18
|
+
const pageLevelFindings = [];
|
|
19
|
+
const page = await browser.newPage({
|
|
20
|
+
viewport: { width: widths[0], height: 900 },
|
|
21
|
+
deviceScaleFactor: 1,
|
|
22
|
+
});
|
|
23
|
+
page.setDefaultTimeout(NAV_TIMEOUT_MS);
|
|
24
|
+
// ── Per-width checks: reflow, tap targets, line measure ──────────────
|
|
25
|
+
for (const w of widths) {
|
|
26
|
+
await page.setViewportSize({ width: w, height: 900 });
|
|
27
|
+
try {
|
|
28
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: NAV_TIMEOUT_MS });
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
const msg = err instanceof Error ? err.message.split('\n')[0] : 'unknown error';
|
|
32
|
+
if (!/Timeout/i.test(msg)) {
|
|
33
|
+
throw new Error(`Could not load ${url}: ${msg}. Is the dev server running?`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
await page.evaluate(() => document.fonts?.ready).catch(() => { });
|
|
37
|
+
await page.waitForTimeout(SETTLE_MS);
|
|
38
|
+
const r = await page.evaluate((vw) => {
|
|
39
|
+
const doc = document.scrollingElement || document.documentElement;
|
|
40
|
+
const out = {
|
|
41
|
+
hScroll: doc.scrollWidth - vw,
|
|
42
|
+
offenders: [],
|
|
43
|
+
small: 0,
|
|
44
|
+
wide: [],
|
|
45
|
+
};
|
|
46
|
+
// Horizontal reflow — name the widest offending elements
|
|
47
|
+
if (out.hScroll > 1) {
|
|
48
|
+
for (const el of document.querySelectorAll('body *')) {
|
|
49
|
+
const b = el.getBoundingClientRect();
|
|
50
|
+
if (b.width === 0 || b.height === 0)
|
|
51
|
+
continue;
|
|
52
|
+
if (b.right > vw + 1) {
|
|
53
|
+
const sel = el.tagName.toLowerCase() +
|
|
54
|
+
(el.id ? '#' + el.id : '') +
|
|
55
|
+
(el.className && typeof el.className === 'string'
|
|
56
|
+
? '.' + el.className.trim().split(/\s+/).slice(0, 2).join('.') : '');
|
|
57
|
+
out.offenders.push({ sel, right: Math.round(b.right), w: Math.round(b.width) });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
out.offenders.sort((a, b) => b.right - a.right);
|
|
61
|
+
out.offenders = out.offenders.slice(0, 5);
|
|
62
|
+
}
|
|
63
|
+
// Tap targets (WCAG 2.2 SC 2.5.8) — only at touch widths
|
|
64
|
+
if (vw <= 768) {
|
|
65
|
+
for (const el of document.querySelectorAll('a,button,input,select,textarea,[role=button]')) {
|
|
66
|
+
const b = el.getBoundingClientRect();
|
|
67
|
+
if (b.width === 0 || b.height === 0)
|
|
68
|
+
continue;
|
|
69
|
+
if (b.width < 44 || b.height < 44)
|
|
70
|
+
out.small++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Line measure — prose wider than ~80ch is a legibility bug
|
|
74
|
+
for (const el of document.querySelectorAll('p,li,blockquote')) {
|
|
75
|
+
const cs = getComputedStyle(el);
|
|
76
|
+
const ch = parseFloat(cs.fontSize) * 0.5;
|
|
77
|
+
const chars = el.getBoundingClientRect().width / ch;
|
|
78
|
+
if (chars > 80 && (el.textContent?.trim().length ?? 0) > 120) {
|
|
79
|
+
out.wide.push(Math.round(chars));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}, w);
|
|
84
|
+
const findings = [];
|
|
85
|
+
if (r.hScroll > 1) {
|
|
86
|
+
findings.push({
|
|
87
|
+
severity: 'error',
|
|
88
|
+
check: 'reflow',
|
|
89
|
+
detail: `Horizontal scroll by ${r.hScroll}px`,
|
|
90
|
+
elements: r.offenders.map(o => `${o.sel} (right ${o.right}px)`),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
findings.push({
|
|
95
|
+
severity: 'note',
|
|
96
|
+
check: 'reflow',
|
|
97
|
+
detail: 'No horizontal scroll',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (r.small > 0) {
|
|
101
|
+
findings.push({
|
|
102
|
+
severity: 'warning',
|
|
103
|
+
check: 'tap-target',
|
|
104
|
+
detail: `${r.small} tap target(s) under 44×44`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (r.wide.length) {
|
|
108
|
+
findings.push({
|
|
109
|
+
severity: 'warning',
|
|
110
|
+
check: 'line-measure',
|
|
111
|
+
detail: `${r.wide.length} text block(s) over ~80ch (max ${Math.max(...r.wide)}ch)`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
bpResults.push({ width: w, findings });
|
|
115
|
+
}
|
|
116
|
+
// ── Page-level checks at desktop (largest breakpoint) ────────────────
|
|
117
|
+
const desktopWidth = Math.max(...widths);
|
|
118
|
+
await page.setViewportSize({ width: desktopWidth, height: 900 });
|
|
119
|
+
try {
|
|
120
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: NAV_TIMEOUT_MS });
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
const msg = err instanceof Error ? err.message.split('\n')[0] : 'unknown error';
|
|
124
|
+
if (!/Timeout/i.test(msg)) {
|
|
125
|
+
throw new Error(`Could not load ${url}: ${msg}. Is the dev server running?`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
await page.evaluate(() => document.fonts?.ready).catch(() => { });
|
|
129
|
+
await page.waitForTimeout(SETTLE_MS);
|
|
130
|
+
const g = await page.evaluate(() => {
|
|
131
|
+
// ── WCAG contrast helpers (ported from shipyard-skool/web-audit.mjs) ──
|
|
132
|
+
function lin(c) { c /= 255; return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); }
|
|
133
|
+
function lum(rgb) { return 0.2126 * lin(rgb[0]) + 0.7152 * lin(rgb[1]) + 0.0722 * lin(rgb[2]); }
|
|
134
|
+
function parse(s) {
|
|
135
|
+
const m = s.match(/rgba?\(([^)]+)\)/);
|
|
136
|
+
if (!m)
|
|
137
|
+
return null;
|
|
138
|
+
const p = m[1].split(',').map(x => parseFloat(x));
|
|
139
|
+
const a = p.length > 3 ? p[3] : 1;
|
|
140
|
+
if (a === 0)
|
|
141
|
+
return null;
|
|
142
|
+
return [p[0], p[1], p[2], a];
|
|
143
|
+
}
|
|
144
|
+
function contrastRatio(a, b) {
|
|
145
|
+
const la = lum(a), lb = lum(b);
|
|
146
|
+
const hi = Math.max(la, lb), lo = Math.min(la, lb);
|
|
147
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
148
|
+
}
|
|
149
|
+
function alphaOver(top, bot) {
|
|
150
|
+
const a = top[3];
|
|
151
|
+
return [top[0] * a + bot[0] * (1 - a), top[1] * a + bot[1] * (1 - a), top[2] * a + bot[2] * (1 - a), 1];
|
|
152
|
+
}
|
|
153
|
+
// Composite every semi-transparent layer from the element to the root.
|
|
154
|
+
// Treating a 12%-alpha badge fill as solid is how a legible chip reports 1:1.
|
|
155
|
+
function bgOf(el) {
|
|
156
|
+
const stack = [];
|
|
157
|
+
let n = el;
|
|
158
|
+
while (n) {
|
|
159
|
+
const c = parse(getComputedStyle(n).backgroundColor);
|
|
160
|
+
if (c) {
|
|
161
|
+
stack.push(c);
|
|
162
|
+
if (c[3] === 1)
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
n = n.parentElement;
|
|
166
|
+
}
|
|
167
|
+
let base = [255, 255, 255, 1];
|
|
168
|
+
if (stack.length > 0 && stack[stack.length - 1][3] === 1)
|
|
169
|
+
base = stack.pop();
|
|
170
|
+
while (stack.length)
|
|
171
|
+
base = alphaOver(stack.pop(), base);
|
|
172
|
+
return base;
|
|
173
|
+
}
|
|
174
|
+
// ── end contrast helpers ──
|
|
175
|
+
const out = {};
|
|
176
|
+
// Structure
|
|
177
|
+
out.h1 = document.querySelectorAll('h1').length;
|
|
178
|
+
out.landmarks = ['header', 'nav', 'main', 'footer'].filter(t => document.querySelector(t));
|
|
179
|
+
const levels = [...document.querySelectorAll('h1,h2,h3,h4,h5,h6')].map(h => +h.tagName[1]);
|
|
180
|
+
out.skipped = levels.some((l, i) => i > 0 && l - levels[i - 1] > 1);
|
|
181
|
+
// Stylesheet-level checks
|
|
182
|
+
let css = '';
|
|
183
|
+
let blocked = 0;
|
|
184
|
+
for (const sheet of document.styleSheets) {
|
|
185
|
+
try {
|
|
186
|
+
for (const rule of sheet.cssRules)
|
|
187
|
+
css += rule.cssText;
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
blocked++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
out.blockedSheets = blocked;
|
|
194
|
+
out.focusVisible = (css.match(/:focus-visible/g) || []).length;
|
|
195
|
+
out.reducedMotion = (css.match(/prefers-reduced-motion/g) || []).length;
|
|
196
|
+
out.animated = (css.match(/transition|animation/g) || []).length;
|
|
197
|
+
out.outlineNone = (css.match(/outline\s*:\s*(none|0)/g) || []).length;
|
|
198
|
+
// CLS risk — media without reserved space
|
|
199
|
+
out.unsized = [...document.querySelectorAll('img,video,iframe')].filter(el => {
|
|
200
|
+
const cs = getComputedStyle(el);
|
|
201
|
+
const hasAttr = el.getAttribute('width') && el.getAttribute('height');
|
|
202
|
+
return !hasAttr && cs.aspectRatio === 'auto';
|
|
203
|
+
}).length;
|
|
204
|
+
// Type-scale audit — the AI sizing bias, measured. Histogram of rendered
|
|
205
|
+
// font sizes across visible text, plus samples of the smallest offenders.
|
|
206
|
+
{
|
|
207
|
+
const hist = {};
|
|
208
|
+
const tiny = [];
|
|
209
|
+
const small = [];
|
|
210
|
+
for (const el of document.querySelectorAll('body *')) {
|
|
211
|
+
const direct = [...el.childNodes].filter(n => n.nodeType === 3 && n.textContent.trim().length > 1);
|
|
212
|
+
if (!direct.length)
|
|
213
|
+
continue;
|
|
214
|
+
const cs = getComputedStyle(el);
|
|
215
|
+
if (cs.visibility === 'hidden' || cs.display === 'none' || +cs.opacity === 0)
|
|
216
|
+
continue;
|
|
217
|
+
if (el.getAttribute('aria-hidden') === 'true')
|
|
218
|
+
continue;
|
|
219
|
+
const size = Math.round(parseFloat(cs.fontSize) * 10) / 10;
|
|
220
|
+
const bucket = size < 12 ? '<12px' : size < 14 ? '12-13px' : size < 16 ? '14-15px' : size < 20 ? '16-19px' : size < 28 ? '20-27px' : '28px+';
|
|
221
|
+
hist[bucket] = (hist[bucket] || 0) + 1;
|
|
222
|
+
const sample = direct[0].textContent.trim().slice(0, 40);
|
|
223
|
+
if (size < 12 && tiny.length < 5)
|
|
224
|
+
tiny.push({ size, text: sample });
|
|
225
|
+
else if (size < 14 && small.length < 5)
|
|
226
|
+
small.push({ size, text: sample });
|
|
227
|
+
}
|
|
228
|
+
out.typeHist = hist;
|
|
229
|
+
out.typeTiny = tiny;
|
|
230
|
+
out.typeSmall = small;
|
|
231
|
+
}
|
|
232
|
+
// Icon-size audit — standalone icons rendered under 18px read as clutter.
|
|
233
|
+
{
|
|
234
|
+
let tinyIcons = 0;
|
|
235
|
+
for (const el of document.querySelectorAll('svg, i[class*="ph-"], [data-icon]')) {
|
|
236
|
+
const b = el.getBoundingClientRect();
|
|
237
|
+
if (b.width === 0 || b.height === 0)
|
|
238
|
+
continue;
|
|
239
|
+
// inline decorations inside text flow are fine; flag icons that lead
|
|
240
|
+
// a row/nav item (parent has multiple children, icon is first)
|
|
241
|
+
const box = Math.max(b.width, b.height);
|
|
242
|
+
if (box < 18 && box > 4)
|
|
243
|
+
tinyIcons++;
|
|
244
|
+
}
|
|
245
|
+
out.tinyIcons = tinyIcons;
|
|
246
|
+
}
|
|
247
|
+
// Contrast on real rendered text
|
|
248
|
+
out.contrast = [];
|
|
249
|
+
const seen = new Set();
|
|
250
|
+
for (const el of document.querySelectorAll('body *')) {
|
|
251
|
+
if (!el.childNodes.length)
|
|
252
|
+
continue;
|
|
253
|
+
const direct = [...el.childNodes].filter(n => n.nodeType === 3 && n.textContent.trim().length > 2);
|
|
254
|
+
if (!direct.length)
|
|
255
|
+
continue;
|
|
256
|
+
const cs = getComputedStyle(el);
|
|
257
|
+
if (cs.visibility === 'hidden' || cs.display === 'none' || +cs.opacity === 0)
|
|
258
|
+
continue;
|
|
259
|
+
// Gradient-clipped text — contrast not machine-checkable
|
|
260
|
+
const clipped = cs.webkitTextFillColor === 'rgba(0, 0, 0, 0)' ||
|
|
261
|
+
/text/.test(cs.webkitBackgroundClip || '') ||
|
|
262
|
+
/text/.test(cs.backgroundClip || '');
|
|
263
|
+
if (clipped) {
|
|
264
|
+
out.unc = (out.unc || 0) + 1;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
let fg = parse(cs.color);
|
|
268
|
+
if (!fg)
|
|
269
|
+
continue;
|
|
270
|
+
const bg = bgOf(el);
|
|
271
|
+
if (fg[3] < 1)
|
|
272
|
+
fg = alphaOver(fg, bg);
|
|
273
|
+
const size = parseFloat(cs.fontSize);
|
|
274
|
+
const bold = +cs.fontWeight >= 700;
|
|
275
|
+
const large = size >= 24 || (size >= 18.66 && bold);
|
|
276
|
+
const floor = large ? 3 : 4.5;
|
|
277
|
+
const ratio = contrastRatio(fg, bg);
|
|
278
|
+
if (ratio < floor) {
|
|
279
|
+
const key = cs.color + '|' + bg.join(',') + '|' + Math.round(size);
|
|
280
|
+
if (seen.has(key))
|
|
281
|
+
continue;
|
|
282
|
+
seen.add(key);
|
|
283
|
+
out.contrast.push({
|
|
284
|
+
ratio: Math.round(ratio * 100) / 100,
|
|
285
|
+
floor,
|
|
286
|
+
text: direct[0].textContent.trim().slice(0, 40),
|
|
287
|
+
size: Math.round(size),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return out;
|
|
292
|
+
});
|
|
293
|
+
// Convert page-level results to findings
|
|
294
|
+
if (g.h1 !== 1) {
|
|
295
|
+
pageLevelFindings.push({
|
|
296
|
+
severity: 'error', check: 'structure',
|
|
297
|
+
detail: `Expected exactly one <h1>, found ${g.h1}`,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (g.landmarks.length < 3) {
|
|
301
|
+
pageLevelFindings.push({
|
|
302
|
+
severity: 'warning', check: 'structure',
|
|
303
|
+
detail: `Landmarks present: ${g.landmarks.join(', ') || 'none'} — want header/nav/main/footer`,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
if (g.skipped) {
|
|
307
|
+
pageLevelFindings.push({
|
|
308
|
+
severity: 'warning', check: 'structure',
|
|
309
|
+
detail: 'Heading levels skip a step',
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
if (g.focusVisible === 0) {
|
|
313
|
+
pageLevelFindings.push({
|
|
314
|
+
severity: 'error', check: 'focus',
|
|
315
|
+
detail: 'No :focus-visible rule anywhere — keyboard users cannot see focus',
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (g.outlineNone > 0 && g.focusVisible === 0) {
|
|
319
|
+
pageLevelFindings.push({
|
|
320
|
+
severity: 'error', check: 'focus',
|
|
321
|
+
detail: `outline:none used ${g.outlineNone}× with no :focus-visible replacement`,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
if (g.animated > 0 && g.reducedMotion === 0) {
|
|
325
|
+
pageLevelFindings.push({
|
|
326
|
+
severity: 'error', check: 'motion',
|
|
327
|
+
detail: `${g.animated} transition/animation rule(s) and no prefers-reduced-motion block`,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
if (g.unsized > 0) {
|
|
331
|
+
pageLevelFindings.push({
|
|
332
|
+
severity: 'warning', check: 'cls',
|
|
333
|
+
detail: `${g.unsized} media element(s) with no width/height or aspect-ratio — CLS risk`,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
// Type-scale findings — the AI sizing bias, measured
|
|
337
|
+
if (g.typeTiny?.length) {
|
|
338
|
+
const comical = g.typeTiny.filter((t) => t.size < 10);
|
|
339
|
+
const borderline = g.typeTiny.filter((t) => t.size >= 10);
|
|
340
|
+
if (comical.length) {
|
|
341
|
+
pageLevelFindings.push({
|
|
342
|
+
severity: 'error', check: 'type-scale',
|
|
343
|
+
detail: `Text under 10px found: ${comical.map((t) => `${t.size}px "${t.text}"`).join(' · ')} — inaccessibly small, no legitimate UI use. Fix these.`,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
if (borderline.length) {
|
|
347
|
+
pageLevelFindings.push({
|
|
348
|
+
severity: 'warning', check: 'type-scale',
|
|
349
|
+
detail: `Text at 10-11px: ${borderline.map((t) => `${t.size}px "${t.text}"`).join(' · ')} — legitimate ONLY for deliberate micro-chips (mono ticket IDs, avatar overflow counts, dataviz ticks, legal fine print). Justify each one or raise it; body/labels/meta never live here.`,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (g.typeSmall?.length) {
|
|
354
|
+
pageLevelFindings.push({
|
|
355
|
+
severity: 'warning', check: 'type-scale',
|
|
356
|
+
detail: `Text at 12-13px: ${g.typeSmall.map((t) => `${t.size}px "${t.text}"`).join(' · ')} — acceptable only for tabular annotations/meta; body and labels belong at 14px+ (body floor 16px).`,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
if (g.typeHist) {
|
|
360
|
+
const total = Object.values(g.typeHist).reduce((a, b) => a + b, 0);
|
|
361
|
+
const smallShare = ((g.typeHist['<12px'] || 0) + (g.typeHist['12-13px'] || 0) + (g.typeHist['14-15px'] || 0)) / Math.max(total, 1);
|
|
362
|
+
pageLevelFindings.push({
|
|
363
|
+
severity: smallShare > 0.5 ? 'warning' : 'note', check: 'type-scale',
|
|
364
|
+
detail: `Font-size histogram: ${Object.entries(g.typeHist).map(([k, v]) => `${k}: ${v}`).join(', ')}${smallShare > 0.5 ? ` — ${Math.round(smallShare * 100)}% of text renders under 16px; the page skews small (the AI sizing bias). Rebuild hierarchy from the top down.` : ''}`,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
if (g.tinyIcons > 0) {
|
|
368
|
+
pageLevelFindings.push({
|
|
369
|
+
severity: 'warning', check: 'icon-size',
|
|
370
|
+
detail: `${g.tinyIcons} icon(s) rendered under 18px — meaningful icons (nav, list leading, section markers) belong at 20-24px; 16px only for inline chevrons/meta glyphs.`,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
for (const c of g.contrast) {
|
|
374
|
+
pageLevelFindings.push({
|
|
375
|
+
severity: 'error', check: 'contrast',
|
|
376
|
+
detail: `Contrast ${c.ratio}:1 (needs ${c.floor}:1) at ${c.size}px — "${c.text}"`,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
if (g.blockedSheets) {
|
|
380
|
+
pageLevelFindings.push({
|
|
381
|
+
severity: 'note', check: 'contrast',
|
|
382
|
+
detail: `${g.blockedSheets} cross-origin stylesheet(s) could not be inspected`,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
if (g.unc) {
|
|
386
|
+
pageLevelFindings.push({
|
|
387
|
+
severity: 'note', check: 'contrast',
|
|
388
|
+
detail: `${g.unc} gradient-clipped text run(s) — contrast not machine-checkable, check by eye`,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
await page.close().catch(() => { });
|
|
392
|
+
// Summary
|
|
393
|
+
const allFindings = [...bpResults.flatMap(b => b.findings), ...pageLevelFindings];
|
|
394
|
+
const summary = {
|
|
395
|
+
errors: allFindings.filter(f => f.severity === 'error').length,
|
|
396
|
+
warnings: allFindings.filter(f => f.severity === 'warning').length,
|
|
397
|
+
notes: allFindings.filter(f => f.severity === 'note').length,
|
|
398
|
+
};
|
|
399
|
+
return { url, breakpoints: bpResults, pageLevel: pageLevelFindings, summary };
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access levels:
|
|
3
|
+
* - anonymous: no API key. Free tools + free components only.
|
|
4
|
+
* - free: valid key on a non-pro plan. Same surface as anonymous today;
|
|
5
|
+
* kept distinct so the server can greet by email and upsell.
|
|
6
|
+
* - premium: pro/plus/lifetime. Everything (boosters still plus/lifetime).
|
|
7
|
+
*/
|
|
8
|
+
export type AccessLevel = 'anonymous' | 'free' | 'premium';
|
|
1
9
|
export interface AuthResult {
|
|
2
|
-
|
|
10
|
+
level: AccessLevel;
|
|
11
|
+
email: string | null;
|
|
3
12
|
tier: string;
|
|
4
13
|
valid: boolean;
|
|
5
14
|
}
|
|
6
|
-
export declare
|
|
15
|
+
export declare const ANONYMOUS_AUTH: AuthResult;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the caller's access level. Unlike the old validatePremiumAccess,
|
|
18
|
+
* a MISSING key is not an error — it's the free tier. A key that is present
|
|
19
|
+
* but malformed/invalid still throws, because a misconfigured subscriber
|
|
20
|
+
* should hear about it loudly, not be silently downgraded to free.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateAccess(apiKey: string | null): Promise<AuthResult>;
|
|
7
23
|
export declare function canAccessBoosters(tier: string): boolean;
|
|
8
24
|
export declare class AuthError extends Error {
|
|
9
25
|
constructor(message: string);
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,cAAc,EAAE,UAK5B,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CA+C/E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B"}
|
package/dist/auth.js
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import { whoami, ApiError } from './api.js';
|
|
2
2
|
const PRO_TIERS = ['pro', 'plus', 'lifetime'];
|
|
3
|
-
export
|
|
3
|
+
export const ANONYMOUS_AUTH = {
|
|
4
|
+
level: 'anonymous',
|
|
5
|
+
email: null,
|
|
6
|
+
tier: 'none',
|
|
7
|
+
valid: true,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the caller's access level. Unlike the old validatePremiumAccess,
|
|
11
|
+
* a MISSING key is not an error — it's the free tier. A key that is present
|
|
12
|
+
* but malformed/invalid still throws, because a misconfigured subscriber
|
|
13
|
+
* should hear about it loudly, not be silently downgraded to free.
|
|
14
|
+
*/
|
|
15
|
+
export async function validateAccess(apiKey) {
|
|
4
16
|
if (!apiKey) {
|
|
5
|
-
|
|
6
|
-
'Get your API key at https://astralkit.com/settings\n\n' +
|
|
7
|
-
'AstralKit MCP requires a Pro or higher subscription.\n' +
|
|
8
|
-
'Upgrade at https://astralkit.com/pricing');
|
|
17
|
+
return ANONYMOUS_AUTH;
|
|
9
18
|
}
|
|
10
19
|
if (!apiKey.startsWith('ak_') || apiKey.length < 10) {
|
|
11
20
|
throw new AuthError(`Invalid API key format. Keys start with "ak_" and are at least 10 characters.\n` +
|
|
12
|
-
'Get your API key at https://astralkit.com/settings'
|
|
21
|
+
'Get your API key at https://astralkit.com/settings\n' +
|
|
22
|
+
'Or unset ASTRALKIT_API_KEY to run in free mode.');
|
|
13
23
|
}
|
|
14
24
|
let result;
|
|
15
25
|
try {
|
|
@@ -18,7 +28,8 @@ export async function validatePremiumAccess(apiKey) {
|
|
|
18
28
|
catch (err) {
|
|
19
29
|
if (err instanceof ApiError) {
|
|
20
30
|
if (err.code === 'AUTH_REQUIRED') {
|
|
21
|
-
throw new AuthError('Invalid or expired API key. Get a new one at https://astralkit.com/settings'
|
|
31
|
+
throw new AuthError('Invalid or expired API key. Get a new one at https://astralkit.com/settings\n' +
|
|
32
|
+
'Or unset ASTRALKIT_API_KEY to run in free mode.');
|
|
22
33
|
}
|
|
23
34
|
// Network/timeout is TRANSIENT — re-throw the original ApiError (NOT an
|
|
24
35
|
// AuthError) so the caller can tell "connectivity blip" apart from "key is
|
|
@@ -30,14 +41,11 @@ export async function validatePremiumAccess(apiKey) {
|
|
|
30
41
|
throw err;
|
|
31
42
|
}
|
|
32
43
|
if (!result.valid) {
|
|
33
|
-
throw new AuthError('API key is no longer valid. Generate a new one at https://astralkit.com/settings'
|
|
34
|
-
|
|
35
|
-
if (!PRO_TIERS.includes(result.plan)) {
|
|
36
|
-
throw new AuthError(`AstralKit MCP requires a Pro or higher subscription.\n` +
|
|
37
|
-
`Your current plan: ${result.plan}\n\n` +
|
|
38
|
-
'Upgrade at https://astralkit.com/pricing');
|
|
44
|
+
throw new AuthError('API key is no longer valid. Generate a new one at https://astralkit.com/settings\n' +
|
|
45
|
+
'Or unset ASTRALKIT_API_KEY to run in free mode.');
|
|
39
46
|
}
|
|
40
47
|
return {
|
|
48
|
+
level: PRO_TIERS.includes(result.plan) ? 'premium' : 'free',
|
|
41
49
|
email: result.email,
|
|
42
50
|
tier: result.plan,
|
|
43
51
|
valid: true,
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AAkB9C,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,KAAK,EAAE,WAAW;IAClB,KAAK,EAAE,IAAI;IACX,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,IAAI;CACZ,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAqB;IACxD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACpD,MAAM,IAAI,SAAS,CACjB,iFAAiF;YACjF,sDAAsD;YACtD,iDAAiD,CAClD,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,IAAI,GAAG,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;gBACjC,MAAM,IAAI,SAAS,CACjB,+EAA+E;oBAC/E,iDAAiD,CAClD,CAAC;YACJ,CAAC;YACD,wEAAwE;YACxE,2EAA2E;YAC3E,8DAA8D;YAC9D,IAAI,GAAG,CAAC,IAAI,KAAK,eAAe,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC3D,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,IAAI,SAAS,CACjB,oFAAoF;YACpF,iDAAiD,CAClD,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;QAC3D,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,IAAI;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,UAAU,CAAC;AAChD,CAAC;AAED,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF"}
|