@tangle-network/browser-agent-driver 0.10.0 → 0.12.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/dist/brain/index.d.ts +3 -0
- package/dist/brain/index.d.ts.map +1 -1
- package/dist/brain/index.js +103 -11
- package/dist/brain/index.js.map +1 -1
- package/dist/browser-launch.d.ts +3 -0
- package/dist/browser-launch.d.ts.map +1 -1
- package/dist/browser-launch.js +12 -2
- package/dist/browser-launch.js.map +1 -1
- package/dist/cli-auth.d.ts +19 -0
- package/dist/cli-auth.d.ts.map +1 -0
- package/dist/cli-auth.js +195 -0
- package/dist/cli-auth.js.map +1 -0
- package/dist/cli-design-audit.d.ts +8 -0
- package/dist/cli-design-audit.d.ts.map +1 -1
- package/dist/cli-design-audit.js +858 -31
- package/dist/cli-design-audit.js.map +1 -1
- package/dist/cli-showcase.d.ts +28 -0
- package/dist/cli-showcase.d.ts.map +1 -0
- package/dist/cli-showcase.js +131 -0
- package/dist/cli-showcase.js.map +1 -0
- package/dist/cli-ui.d.ts.map +1 -1
- package/dist/cli-ui.js +41 -2
- package/dist/cli-ui.js.map +1 -1
- package/dist/cli.js +418 -211
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/design/compare.d.ts +9 -0
- package/dist/design/compare.d.ts.map +1 -0
- package/dist/design/compare.js +339 -0
- package/dist/design/compare.js.map +1 -0
- package/dist/design/index.d.ts +6 -0
- package/dist/design/index.d.ts.map +1 -0
- package/dist/design/index.js +5 -0
- package/dist/design/index.js.map +1 -0
- package/dist/design/page-interaction.d.ts +24 -0
- package/dist/design/page-interaction.d.ts.map +1 -0
- package/dist/design/page-interaction.js +244 -0
- package/dist/design/page-interaction.js.map +1 -0
- package/dist/design/rip.d.ts +9 -0
- package/dist/design/rip.d.ts.map +1 -0
- package/dist/design/rip.js +362 -0
- package/dist/design/rip.js.map +1 -0
- package/dist/design/types.d.ts +126 -0
- package/dist/design/types.d.ts.map +1 -0
- package/dist/design/types.js +3 -0
- package/dist/design/types.js.map +1 -0
- package/dist/drivers/playwright.d.ts +1 -0
- package/dist/drivers/playwright.d.ts.map +1 -1
- package/dist/drivers/playwright.js +21 -5
- package/dist/drivers/playwright.js.map +1 -1
- package/dist/drivers/types.d.ts +8 -0
- package/dist/drivers/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/runner/effect-verification.d.ts +12 -0
- package/dist/runner/effect-verification.d.ts.map +1 -0
- package/dist/runner/effect-verification.js +169 -0
- package/dist/runner/effect-verification.js.map +1 -0
- package/dist/runner/index.d.ts +1 -0
- package/dist/runner/index.d.ts.map +1 -1
- package/dist/runner/index.js +2 -0
- package/dist/runner/index.js.map +1 -1
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/runner.js +34 -57
- package/dist/runner/runner.js.map +1 -1
- package/dist/runner.d.ts +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +2 -0
- package/dist/runner.js.map +1 -1
- package/dist/showcase/annotate.d.ts +28 -0
- package/dist/showcase/annotate.d.ts.map +1 -0
- package/dist/showcase/annotate.js +95 -0
- package/dist/showcase/annotate.js.map +1 -0
- package/dist/showcase/assemble.d.ts +31 -0
- package/dist/showcase/assemble.d.ts.map +1 -0
- package/dist/showcase/assemble.js +107 -0
- package/dist/showcase/assemble.js.map +1 -0
- package/dist/showcase/demo-player.d.ts +53 -0
- package/dist/showcase/demo-player.d.ts.map +1 -0
- package/dist/showcase/demo-player.js +325 -0
- package/dist/showcase/demo-player.js.map +1 -0
- package/dist/showcase/index.d.ts +21 -0
- package/dist/showcase/index.d.ts.map +1 -0
- package/dist/showcase/index.js +289 -0
- package/dist/showcase/index.js.map +1 -0
- package/dist/showcase/types.d.ts +111 -0
- package/dist/showcase/types.d.ts.map +1 -0
- package/dist/showcase/types.js +3 -0
- package/dist/showcase/types.js.map +1 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page interaction — reveal hidden content before screenshots.
|
|
3
|
+
*
|
|
4
|
+
* Expands accordions, clicks tabs, scrolls carousels, opens menus,
|
|
5
|
+
* dismisses modals. Each function is fault-tolerant.
|
|
6
|
+
*/
|
|
7
|
+
export async function revealHiddenContent(page, opts) {
|
|
8
|
+
const stats = { accordions: 0, tabs: 0, carousels: 0, hovers: 0, menus: 0, modals: 0 };
|
|
9
|
+
await dismissModals(page, stats);
|
|
10
|
+
await expandAccordions(page, stats);
|
|
11
|
+
await clickAllTabs(page, stats);
|
|
12
|
+
await scrollCarousels(page, stats);
|
|
13
|
+
if (opts?.mobile)
|
|
14
|
+
await openMobileMenu(page, stats);
|
|
15
|
+
return stats;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Take screenshots of each interactive state (tabs, accordion panels, carousel slides).
|
|
19
|
+
* Returns arrays of screenshot buffers keyed by interaction type.
|
|
20
|
+
*/
|
|
21
|
+
export async function captureInteractionScreenshots(page, opts) {
|
|
22
|
+
const tabs = [];
|
|
23
|
+
const accordions = [];
|
|
24
|
+
const carousel = [];
|
|
25
|
+
let menu;
|
|
26
|
+
// Tabs: click each tab and screenshot the tab panel
|
|
27
|
+
const tabLists = await page.locator('[role="tablist"]').all();
|
|
28
|
+
for (const tabList of tabLists) {
|
|
29
|
+
const tabButtons = await tabList.locator('[role="tab"]').all();
|
|
30
|
+
for (const tab of tabButtons) {
|
|
31
|
+
try {
|
|
32
|
+
await tab.click({ timeout: 2000 });
|
|
33
|
+
await page.waitForTimeout(400);
|
|
34
|
+
// Screenshot the nearest tabpanel
|
|
35
|
+
const panelId = await tab.getAttribute('aria-controls');
|
|
36
|
+
if (panelId) {
|
|
37
|
+
const panel = page.locator(`#${panelId}`);
|
|
38
|
+
if (await panel.isVisible({ timeout: 500 }).catch(() => false)) {
|
|
39
|
+
tabs.push(await panel.screenshot());
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
tabs.push(await page.screenshot({ fullPage: false }));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch { /* tab not clickable */ }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Accordions: expand each and screenshot
|
|
50
|
+
const detailsEls = await page.locator('details:not([open])').all();
|
|
51
|
+
for (const det of detailsEls) {
|
|
52
|
+
try {
|
|
53
|
+
const summary = det.locator('summary').first();
|
|
54
|
+
await summary.click({ timeout: 2000 });
|
|
55
|
+
await page.waitForTimeout(300);
|
|
56
|
+
accordions.push(await det.screenshot());
|
|
57
|
+
}
|
|
58
|
+
catch { /* not clickable */ }
|
|
59
|
+
}
|
|
60
|
+
// Carousel: click next buttons
|
|
61
|
+
const carouselNextSelectors = [
|
|
62
|
+
'button[aria-label*="next" i]',
|
|
63
|
+
'button[aria-label*="Next" i]',
|
|
64
|
+
'.swiper-button-next',
|
|
65
|
+
'.slick-next',
|
|
66
|
+
'[data-carousel-next]',
|
|
67
|
+
];
|
|
68
|
+
for (const sel of carouselNextSelectors) {
|
|
69
|
+
const nextBtn = page.locator(sel).first();
|
|
70
|
+
if (await nextBtn.isVisible({ timeout: 500 }).catch(() => false)) {
|
|
71
|
+
for (let i = 0; i < 5; i++) {
|
|
72
|
+
try {
|
|
73
|
+
await nextBtn.click({ timeout: 2000 });
|
|
74
|
+
await page.waitForTimeout(600);
|
|
75
|
+
carousel.push(await page.screenshot({ fullPage: false }));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Mobile menu
|
|
85
|
+
if (opts?.mobile) {
|
|
86
|
+
const menuSelectors = [
|
|
87
|
+
'button[aria-label*="menu" i]',
|
|
88
|
+
'button[aria-label*="Menu" i]',
|
|
89
|
+
'.hamburger',
|
|
90
|
+
'[data-toggle="navbar"]',
|
|
91
|
+
'nav button[aria-expanded="false"]',
|
|
92
|
+
'header button[aria-expanded="false"]',
|
|
93
|
+
];
|
|
94
|
+
for (const sel of menuSelectors) {
|
|
95
|
+
const btn = page.locator(sel).first();
|
|
96
|
+
if (await btn.isVisible({ timeout: 500 }).catch(() => false)) {
|
|
97
|
+
try {
|
|
98
|
+
await btn.click({ timeout: 2000 });
|
|
99
|
+
await page.waitForTimeout(500);
|
|
100
|
+
menu = await page.screenshot({ fullPage: false });
|
|
101
|
+
// Close it back
|
|
102
|
+
await btn.click({ timeout: 2000 }).catch(() => null);
|
|
103
|
+
await page.waitForTimeout(300);
|
|
104
|
+
}
|
|
105
|
+
catch { /* menu click failed */ }
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { tabs, accordions, carousel, menu };
|
|
111
|
+
}
|
|
112
|
+
// ── Internals ──
|
|
113
|
+
async function dismissModals(page, stats) {
|
|
114
|
+
// Cookie banners
|
|
115
|
+
const cookieSelectors = [
|
|
116
|
+
'button:has-text("Accept all")',
|
|
117
|
+
'button:has-text("Accept")',
|
|
118
|
+
'button:has-text("Reject all")',
|
|
119
|
+
'button:has-text("Got it")',
|
|
120
|
+
'button:has-text("Agree")',
|
|
121
|
+
'button:has-text("I understand")',
|
|
122
|
+
];
|
|
123
|
+
for (const sel of cookieSelectors) {
|
|
124
|
+
const btn = page.locator(sel).first();
|
|
125
|
+
if (await btn.isVisible({ timeout: 300 }).catch(() => false)) {
|
|
126
|
+
await btn.click({ timeout: 2000 }).catch(() => null);
|
|
127
|
+
await page.waitForTimeout(400);
|
|
128
|
+
stats.modals++;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Generic modals/dialogs
|
|
133
|
+
const closeSelectors = [
|
|
134
|
+
'[role="dialog"] button[aria-label="Close"]',
|
|
135
|
+
'[role="dialog"] button:has-text("Close")',
|
|
136
|
+
'[role="dialog"] button:has-text("Dismiss")',
|
|
137
|
+
'[role="dialog"] button:has-text("No thanks")',
|
|
138
|
+
'[role="dialog"] button:has-text("Maybe later")',
|
|
139
|
+
];
|
|
140
|
+
for (const sel of closeSelectors) {
|
|
141
|
+
const btn = page.locator(sel).first();
|
|
142
|
+
if (await btn.isVisible({ timeout: 300 }).catch(() => false)) {
|
|
143
|
+
await btn.click({ timeout: 2000 }).catch(() => null);
|
|
144
|
+
await page.waitForTimeout(400);
|
|
145
|
+
stats.modals++;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function expandAccordions(page, stats) {
|
|
150
|
+
// HTML5 <details> elements
|
|
151
|
+
const detailsEls = await page.locator('details:not([open])').all();
|
|
152
|
+
for (const det of detailsEls) {
|
|
153
|
+
try {
|
|
154
|
+
const summary = det.locator('summary').first();
|
|
155
|
+
await summary.click({ timeout: 2000 });
|
|
156
|
+
await page.waitForTimeout(300);
|
|
157
|
+
stats.accordions++;
|
|
158
|
+
}
|
|
159
|
+
catch { /* not clickable */ }
|
|
160
|
+
}
|
|
161
|
+
// ARIA accordions
|
|
162
|
+
const ariaAccordions = await page.locator('[aria-expanded="false"]').all();
|
|
163
|
+
for (const el of ariaAccordions) {
|
|
164
|
+
// Only click elements that look like accordion triggers
|
|
165
|
+
const role = await el.getAttribute('role');
|
|
166
|
+
const tag = await el.evaluate(e => e.tagName.toLowerCase());
|
|
167
|
+
if (role === 'tab')
|
|
168
|
+
continue; // handled by clickAllTabs
|
|
169
|
+
if (tag === 'button' || role === 'button') {
|
|
170
|
+
try {
|
|
171
|
+
await el.click({ timeout: 2000 });
|
|
172
|
+
await page.waitForTimeout(300);
|
|
173
|
+
stats.accordions++;
|
|
174
|
+
}
|
|
175
|
+
catch { /* not clickable */ }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function clickAllTabs(page, stats) {
|
|
180
|
+
const tabLists = await page.locator('[role="tablist"]').all();
|
|
181
|
+
for (const tabList of tabLists) {
|
|
182
|
+
const tabs = await tabList.locator('[role="tab"]').all();
|
|
183
|
+
for (const tab of tabs) {
|
|
184
|
+
const selected = await tab.getAttribute('aria-selected');
|
|
185
|
+
if (selected === 'true')
|
|
186
|
+
continue;
|
|
187
|
+
try {
|
|
188
|
+
await tab.click({ timeout: 2000 });
|
|
189
|
+
await page.waitForTimeout(400);
|
|
190
|
+
stats.tabs++;
|
|
191
|
+
}
|
|
192
|
+
catch { /* not clickable */ }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function scrollCarousels(page, stats) {
|
|
197
|
+
const nextSelectors = [
|
|
198
|
+
'button[aria-label*="next" i]',
|
|
199
|
+
'button[aria-label*="Next" i]',
|
|
200
|
+
'.swiper-button-next',
|
|
201
|
+
'.slick-next',
|
|
202
|
+
'[data-carousel-next]',
|
|
203
|
+
];
|
|
204
|
+
for (const sel of nextSelectors) {
|
|
205
|
+
const btns = await page.locator(sel).all();
|
|
206
|
+
for (const btn of btns) {
|
|
207
|
+
if (!await btn.isVisible({ timeout: 300 }).catch(() => false))
|
|
208
|
+
continue;
|
|
209
|
+
for (let i = 0; i < 8; i++) {
|
|
210
|
+
try {
|
|
211
|
+
await btn.click({ timeout: 2000 });
|
|
212
|
+
await page.waitForTimeout(500);
|
|
213
|
+
stats.carousels++;
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async function openMobileMenu(page, stats) {
|
|
223
|
+
const menuSelectors = [
|
|
224
|
+
'button[aria-label*="menu" i]',
|
|
225
|
+
'button[aria-label*="Menu" i]',
|
|
226
|
+
'.hamburger',
|
|
227
|
+
'[data-toggle="navbar"]',
|
|
228
|
+
'nav button[aria-expanded="false"]',
|
|
229
|
+
'header button[aria-expanded="false"]',
|
|
230
|
+
];
|
|
231
|
+
for (const sel of menuSelectors) {
|
|
232
|
+
const btn = page.locator(sel).first();
|
|
233
|
+
if (await btn.isVisible({ timeout: 300 }).catch(() => false)) {
|
|
234
|
+
try {
|
|
235
|
+
await btn.click({ timeout: 2000 });
|
|
236
|
+
await page.waitForTimeout(500);
|
|
237
|
+
stats.menus++;
|
|
238
|
+
}
|
|
239
|
+
catch { /* click failed */ }
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=page-interaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-interaction.js","sourceRoot":"","sources":["../../src/design/page-interaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site rip — download a full working local copy of a website.
|
|
3
|
+
*
|
|
4
|
+
* Uses Playwright network interception to capture every request/response,
|
|
5
|
+
* then rewrites HTML/CSS references to point to local paths.
|
|
6
|
+
*/
|
|
7
|
+
import type { RipOptions, RipResult } from './types.js';
|
|
8
|
+
export declare function ripSite(opts: RipOptions): Promise<RipResult>;
|
|
9
|
+
//# sourceMappingURL=rip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rip.d.ts","sourceRoot":"","sources":["../../src/design/rip.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAA8B,MAAM,YAAY,CAAA;AAqJnF,wBAAsB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAwLlE"}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site rip — download a full working local copy of a website.
|
|
3
|
+
*
|
|
4
|
+
* Uses Playwright network interception to capture every request/response,
|
|
5
|
+
* then rewrites HTML/CSS references to point to local paths.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { chromium } from 'playwright';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { revealHiddenContent } from './page-interaction.js';
|
|
12
|
+
// ── Asset classification ──
|
|
13
|
+
const FONT_EXT = /\.(woff2?|ttf|otf|eot)(\?|$)/i;
|
|
14
|
+
const IMAGE_EXT = /\.(png|jpe?g|gif|svg|webp|avif|ico|bmp)(\?|$)/i;
|
|
15
|
+
const VIDEO_EXT = /\.(mp4|webm|mov|ogg|avi|m3u8)(\?|$)/i;
|
|
16
|
+
const CSS_EXT = /\.css(\?|$)/i;
|
|
17
|
+
const JS_EXT = /\.js(\?|$)/i;
|
|
18
|
+
function classifyAsset(url, contentType) {
|
|
19
|
+
const ct = contentType.toLowerCase();
|
|
20
|
+
if (ct.includes('text/html'))
|
|
21
|
+
return 'html';
|
|
22
|
+
if (ct.includes('text/css') || CSS_EXT.test(url))
|
|
23
|
+
return 'css';
|
|
24
|
+
if (ct.includes('javascript') || JS_EXT.test(url))
|
|
25
|
+
return 'js';
|
|
26
|
+
if (ct.includes('font') || FONT_EXT.test(url))
|
|
27
|
+
return 'font';
|
|
28
|
+
if (ct.includes('image') || IMAGE_EXT.test(url))
|
|
29
|
+
return 'image';
|
|
30
|
+
if (ct.includes('video') || ct.includes('audio') || VIDEO_EXT.test(url))
|
|
31
|
+
return 'video';
|
|
32
|
+
return 'other';
|
|
33
|
+
}
|
|
34
|
+
function safeFilename(urlStr) {
|
|
35
|
+
try {
|
|
36
|
+
const u = new URL(urlStr);
|
|
37
|
+
let pathname = decodeURIComponent(u.pathname).replace(/^\/+/, '');
|
|
38
|
+
if (!pathname || pathname.endsWith('/'))
|
|
39
|
+
pathname += 'index.html';
|
|
40
|
+
// Sanitize path components
|
|
41
|
+
return pathname.split('/').map(p => p.replace(/[<>:"|?*]/g, '_')).join('/');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return `asset-${Date.now()}`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// ── CSS url() rewriting ──
|
|
48
|
+
function rewriteCssUrls(css, cssUrl, urlMap) {
|
|
49
|
+
return css.replace(/url\(\s*(['"]?)([^'"()]+)\1\s*\)/g, (_match, _quote, rawUrl) => {
|
|
50
|
+
if (rawUrl.startsWith('data:') || rawUrl.startsWith('blob:'))
|
|
51
|
+
return _match;
|
|
52
|
+
try {
|
|
53
|
+
const resolved = new URL(rawUrl, cssUrl).href;
|
|
54
|
+
const local = urlMap.get(resolved);
|
|
55
|
+
if (local)
|
|
56
|
+
return `url("${local}")`;
|
|
57
|
+
}
|
|
58
|
+
catch { /* bad URL */ }
|
|
59
|
+
return _match;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// ── HTML attribute rewriting ──
|
|
63
|
+
const URL_ATTRS = ['src', 'href', 'poster', 'srcset', 'data-src', 'data-bg', 'data-background-image', 'action'];
|
|
64
|
+
function rewriteHtml(html, pageUrl, urlMap) {
|
|
65
|
+
let result = html;
|
|
66
|
+
for (const attr of URL_ATTRS) {
|
|
67
|
+
if (attr === 'srcset') {
|
|
68
|
+
// srcset has special format: "url 1x, url 2x"
|
|
69
|
+
const srcsetRegex = new RegExp(`(${attr}=")([^"]+)(")`, 'gi');
|
|
70
|
+
result = result.replace(srcsetRegex, (_m, pre, val, post) => {
|
|
71
|
+
const rewritten = val.split(',').map((entry) => {
|
|
72
|
+
const parts = entry.trim().split(/\s+/);
|
|
73
|
+
if (parts[0]) {
|
|
74
|
+
try {
|
|
75
|
+
const resolved = new URL(parts[0], pageUrl).href;
|
|
76
|
+
const local = urlMap.get(resolved);
|
|
77
|
+
if (local)
|
|
78
|
+
parts[0] = local;
|
|
79
|
+
}
|
|
80
|
+
catch { /* bad URL */ }
|
|
81
|
+
}
|
|
82
|
+
return parts.join(' ');
|
|
83
|
+
}).join(', ');
|
|
84
|
+
return pre + rewritten + post;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const attrRegex = new RegExp(`(${attr}=")([^"]+)(")`, 'gi');
|
|
89
|
+
result = result.replace(attrRegex, (_m, pre, val, post) => {
|
|
90
|
+
if (val.startsWith('data:') || val.startsWith('blob:') || val.startsWith('#') || val.startsWith('javascript:'))
|
|
91
|
+
return _m;
|
|
92
|
+
try {
|
|
93
|
+
const resolved = new URL(val, pageUrl).href;
|
|
94
|
+
const local = urlMap.get(resolved);
|
|
95
|
+
if (local)
|
|
96
|
+
return pre + local + post;
|
|
97
|
+
}
|
|
98
|
+
catch { /* bad URL */ }
|
|
99
|
+
return _m;
|
|
100
|
+
});
|
|
101
|
+
// Also handle single-quoted attributes
|
|
102
|
+
const attrRegexSingle = new RegExp(`(${attr}=')([^']+)(')`, 'gi');
|
|
103
|
+
result = result.replace(attrRegexSingle, (_m, pre, val, post) => {
|
|
104
|
+
if (val.startsWith('data:') || val.startsWith('blob:') || val.startsWith('#') || val.startsWith('javascript:'))
|
|
105
|
+
return _m;
|
|
106
|
+
try {
|
|
107
|
+
const resolved = new URL(val, pageUrl).href;
|
|
108
|
+
const local = urlMap.get(resolved);
|
|
109
|
+
if (local)
|
|
110
|
+
return pre + local + post;
|
|
111
|
+
}
|
|
112
|
+
catch { /* bad URL */ }
|
|
113
|
+
return _m;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Rewrite CSS url() inside <style> blocks
|
|
118
|
+
result = result.replace(/<style[^>]*>([\s\S]*?)<\/style>/gi, (_m, cssBlock) => {
|
|
119
|
+
return _m.replace(cssBlock, rewriteCssUrls(cssBlock, pageUrl, urlMap));
|
|
120
|
+
});
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
// ── Page discovery (simplified BFS) ──
|
|
124
|
+
async function discoverPageUrls(page, startUrl, maxPages) {
|
|
125
|
+
const origin = new URL(startUrl).origin;
|
|
126
|
+
const visited = new Set();
|
|
127
|
+
const toVisit = [startUrl];
|
|
128
|
+
const discovered = [];
|
|
129
|
+
while (toVisit.length > 0 && discovered.length < maxPages) {
|
|
130
|
+
const url = toVisit.shift();
|
|
131
|
+
const normalized = url.split('#')[0].split('?')[0].replace(/\/$/, '');
|
|
132
|
+
if (visited.has(normalized))
|
|
133
|
+
continue;
|
|
134
|
+
visited.add(normalized);
|
|
135
|
+
discovered.push(url);
|
|
136
|
+
if (discovered.length >= maxPages)
|
|
137
|
+
break;
|
|
138
|
+
try {
|
|
139
|
+
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 15_000 });
|
|
140
|
+
await page.waitForTimeout(1500);
|
|
141
|
+
const links = await page.evaluate((orig) => {
|
|
142
|
+
return Array.from(document.querySelectorAll('a[href]'))
|
|
143
|
+
.map(a => {
|
|
144
|
+
try {
|
|
145
|
+
return new URL(a.href, document.location.href).href;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
.filter((href) => href !== null &&
|
|
152
|
+
href.startsWith(orig) &&
|
|
153
|
+
!href.includes('#') &&
|
|
154
|
+
!href.match(/\.(png|jpg|jpeg|gif|svg|pdf|zip|css|js|mp4|webm)$/i));
|
|
155
|
+
}, origin);
|
|
156
|
+
for (const link of [...new Set(links)]) {
|
|
157
|
+
const norm = link.split('#')[0].split('?')[0].replace(/\/$/, '');
|
|
158
|
+
if (!visited.has(norm))
|
|
159
|
+
toVisit.push(link);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch { /* page load failed */ }
|
|
163
|
+
}
|
|
164
|
+
return discovered;
|
|
165
|
+
}
|
|
166
|
+
// ── Main rip function ──
|
|
167
|
+
export async function ripSite(opts) {
|
|
168
|
+
const maxPages = opts.pages ?? 5;
|
|
169
|
+
const outputDir = opts.outputDir ?? `./rip-results/${new URL(opts.url).hostname}-${Date.now()}`;
|
|
170
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
171
|
+
console.log('');
|
|
172
|
+
console.log(` ${chalk.bold('bad design-audit')} ${chalk.dim('--rip')}`);
|
|
173
|
+
console.log(` ${chalk.dim('→')} ${opts.url}`);
|
|
174
|
+
console.log(` ${chalk.dim('Max pages:')} ${maxPages}`);
|
|
175
|
+
console.log('');
|
|
176
|
+
const browser = await chromium.launch({ headless: opts.headless ?? true });
|
|
177
|
+
const assets = [];
|
|
178
|
+
let totalSize = 0;
|
|
179
|
+
let revealStats;
|
|
180
|
+
// URL -> local relative path
|
|
181
|
+
const urlMap = new Map();
|
|
182
|
+
// URL -> response body
|
|
183
|
+
const bodyMap = new Map();
|
|
184
|
+
// Discover pages first (without interception)
|
|
185
|
+
const discoveryCtx = await browser.newContext({ viewport: { width: 1440, height: 900 } });
|
|
186
|
+
const discoveryPage = await discoveryCtx.newPage();
|
|
187
|
+
const pages = await discoverPageUrls(discoveryPage, opts.url, maxPages);
|
|
188
|
+
await discoveryCtx.close();
|
|
189
|
+
console.log(` Found ${chalk.bold(String(pages.length))} page${pages.length !== 1 ? 's' : ''}`);
|
|
190
|
+
for (const p of pages)
|
|
191
|
+
console.log(` ${chalk.dim('·')} ${p}`);
|
|
192
|
+
console.log('');
|
|
193
|
+
// Rip each page with network interception
|
|
194
|
+
for (let i = 0; i < pages.length; i++) {
|
|
195
|
+
const pageUrl = pages[i];
|
|
196
|
+
console.log(` ${chalk.dim(`[${i + 1}/${pages.length}]`)} Ripping ${pageUrl}`);
|
|
197
|
+
const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
|
|
198
|
+
const page = await context.newPage();
|
|
199
|
+
// Intercept ALL requests
|
|
200
|
+
await page.route('**/*', async (route) => {
|
|
201
|
+
try {
|
|
202
|
+
const response = await route.fetch();
|
|
203
|
+
const url = route.request().url();
|
|
204
|
+
const contentType = response.headers()['content-type'] || '';
|
|
205
|
+
const body = await response.body();
|
|
206
|
+
if (!bodyMap.has(url)) {
|
|
207
|
+
const localPath = safeFilename(url);
|
|
208
|
+
urlMap.set(url, localPath);
|
|
209
|
+
bodyMap.set(url, { body, contentType });
|
|
210
|
+
}
|
|
211
|
+
await route.fulfill({ response });
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
await route.continue().catch(() => null);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
try {
|
|
218
|
+
await page.goto(pageUrl, { waitUntil: 'networkidle', timeout: 30_000 }).catch(() => page.goto(pageUrl, { waitUntil: 'domcontentloaded', timeout: 20_000 }));
|
|
219
|
+
await page.waitForTimeout(2000);
|
|
220
|
+
// Reveal hidden content to trigger lazy-loaded assets
|
|
221
|
+
if (opts.interactiveReveal !== false) {
|
|
222
|
+
revealStats = await revealHiddenContent(page);
|
|
223
|
+
}
|
|
224
|
+
// Scroll to bottom to trigger lazy loading
|
|
225
|
+
await autoScroll(page);
|
|
226
|
+
await page.waitForTimeout(1000);
|
|
227
|
+
// Capture final HTML (with JS-rendered content)
|
|
228
|
+
const html = await page.content();
|
|
229
|
+
const htmlPath = i === 0 ? 'index.html' : `pages/${safeFilename(pageUrl).replace(/\.html$/, '')}.html`;
|
|
230
|
+
urlMap.set(pageUrl, htmlPath);
|
|
231
|
+
bodyMap.set(pageUrl, { body: Buffer.from(html, 'utf-8'), contentType: 'text/html' });
|
|
232
|
+
// Extract video URLs from the rendered DOM (not caught by interception if <video> never fetched)
|
|
233
|
+
const videoUrls = await page.evaluate(() => {
|
|
234
|
+
const urls = [];
|
|
235
|
+
for (const v of document.querySelectorAll('video')) {
|
|
236
|
+
if (v.src)
|
|
237
|
+
urls.push(v.src);
|
|
238
|
+
if (v.poster)
|
|
239
|
+
urls.push(v.poster);
|
|
240
|
+
for (const s of v.querySelectorAll('source')) {
|
|
241
|
+
if (s.src)
|
|
242
|
+
urls.push(s.src);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// data-* attributes with media URLs
|
|
246
|
+
for (const el of document.querySelectorAll('[data-src], [data-bg], [data-background-image]')) {
|
|
247
|
+
for (const attr of ['data-src', 'data-bg', 'data-background-image']) {
|
|
248
|
+
const val = el.getAttribute(attr);
|
|
249
|
+
if (val && !val.startsWith('data:') && !val.startsWith('blob:')) {
|
|
250
|
+
try {
|
|
251
|
+
urls.push(new URL(val, location.href).href);
|
|
252
|
+
}
|
|
253
|
+
catch { /* bad URL */ }
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return urls;
|
|
258
|
+
});
|
|
259
|
+
// Fetch any video/media URLs not yet captured
|
|
260
|
+
for (const vUrl of videoUrls) {
|
|
261
|
+
if (!bodyMap.has(vUrl)) {
|
|
262
|
+
try {
|
|
263
|
+
const res = await fetch(vUrl);
|
|
264
|
+
if (res.ok) {
|
|
265
|
+
const body = Buffer.from(await res.arrayBuffer());
|
|
266
|
+
const ct = res.headers.get('content-type') || '';
|
|
267
|
+
urlMap.set(vUrl, safeFilename(vUrl));
|
|
268
|
+
bodyMap.set(vUrl, { body, contentType: ct });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch { /* fetch failed */ }
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
await context.close();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
await browser.close();
|
|
280
|
+
// Write all assets to disk with rewritten references
|
|
281
|
+
console.log('');
|
|
282
|
+
console.log(` Writing ${bodyMap.size} assets…`);
|
|
283
|
+
for (const [url, { body, contentType }] of bodyMap) {
|
|
284
|
+
const localPath = urlMap.get(url) || safeFilename(url);
|
|
285
|
+
const fullPath = path.join(outputDir, localPath);
|
|
286
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
287
|
+
const category = classifyAsset(url, contentType);
|
|
288
|
+
if (category === 'html') {
|
|
289
|
+
const rewritten = rewriteHtml(body.toString('utf-8'), url, urlMap);
|
|
290
|
+
fs.writeFileSync(fullPath, rewritten);
|
|
291
|
+
}
|
|
292
|
+
else if (category === 'css') {
|
|
293
|
+
const rewritten = rewriteCssUrls(body.toString('utf-8'), url, urlMap);
|
|
294
|
+
fs.writeFileSync(fullPath, rewritten);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
fs.writeFileSync(fullPath, body);
|
|
298
|
+
}
|
|
299
|
+
const size = body.length;
|
|
300
|
+
totalSize += size;
|
|
301
|
+
assets.push({ url, contentType, localPath, sizeBytes: size, category });
|
|
302
|
+
}
|
|
303
|
+
// Write manifest
|
|
304
|
+
const manifest = {
|
|
305
|
+
rippedAt: new Date().toISOString(),
|
|
306
|
+
sourceUrl: opts.url,
|
|
307
|
+
pageCount: pages.length,
|
|
308
|
+
assetCount: assets.length,
|
|
309
|
+
totalSizeBytes: totalSize,
|
|
310
|
+
pages,
|
|
311
|
+
assets: assets.map(a => ({ url: a.url, localPath: a.localPath, category: a.category, sizeBytes: a.sizeBytes })),
|
|
312
|
+
};
|
|
313
|
+
fs.writeFileSync(path.join(outputDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
314
|
+
// Summary
|
|
315
|
+
const byCategory = new Map();
|
|
316
|
+
for (const a of assets)
|
|
317
|
+
byCategory.set(a.category, (byCategory.get(a.category) ?? 0) + 1);
|
|
318
|
+
const sizeStr = totalSize > 1024 * 1024 ? `${(totalSize / 1024 / 1024).toFixed(1)} MB` : `${(totalSize / 1024).toFixed(0)} KB`;
|
|
319
|
+
console.log('');
|
|
320
|
+
console.log(` ${chalk.dim('─'.repeat(52))}`);
|
|
321
|
+
console.log(` ${chalk.bold(String(assets.length))} assets ${chalk.dim(`(${sizeStr})`)}`);
|
|
322
|
+
for (const [cat, count] of [...byCategory.entries()].sort()) {
|
|
323
|
+
console.log(` ${chalk.dim(cat.padEnd(12))}${count}`);
|
|
324
|
+
}
|
|
325
|
+
if (revealStats) {
|
|
326
|
+
const parts = [];
|
|
327
|
+
if (revealStats.accordions)
|
|
328
|
+
parts.push(`${revealStats.accordions} accordions`);
|
|
329
|
+
if (revealStats.tabs)
|
|
330
|
+
parts.push(`${revealStats.tabs} tabs`);
|
|
331
|
+
if (revealStats.carousels)
|
|
332
|
+
parts.push(`${revealStats.carousels} carousel slides`);
|
|
333
|
+
if (revealStats.modals)
|
|
334
|
+
parts.push(`${revealStats.modals} modals dismissed`);
|
|
335
|
+
if (parts.length)
|
|
336
|
+
console.log(` ${chalk.dim('Revealed:')} ${parts.join(', ')}`);
|
|
337
|
+
}
|
|
338
|
+
console.log(` ${chalk.dim('Output →')} ${outputDir}`);
|
|
339
|
+
console.log(` ${chalk.dim('Open →')} ${path.join(outputDir, 'index.html')}`);
|
|
340
|
+
console.log('');
|
|
341
|
+
return { outputDir, pageCount: pages.length, assets, totalSizeBytes: totalSize, revealStats };
|
|
342
|
+
}
|
|
343
|
+
async function autoScroll(page) {
|
|
344
|
+
await page.evaluate(async () => {
|
|
345
|
+
await new Promise((resolve) => {
|
|
346
|
+
let totalHeight = 0;
|
|
347
|
+
const distance = 400;
|
|
348
|
+
const timer = setInterval(() => {
|
|
349
|
+
window.scrollBy(0, distance);
|
|
350
|
+
totalHeight += distance;
|
|
351
|
+
if (totalHeight >= document.body.scrollHeight) {
|
|
352
|
+
clearInterval(timer);
|
|
353
|
+
window.scrollTo(0, 0);
|
|
354
|
+
resolve();
|
|
355
|
+
}
|
|
356
|
+
}, 100);
|
|
357
|
+
// Safety: max 30 seconds of scrolling
|
|
358
|
+
setTimeout(() => { clearInterval(timer); window.scrollTo(0, 0); resolve(); }, 30_000);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
//# sourceMappingURL=rip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rip.js","sourceRoot":"","sources":["../../src/design/rip.ts"],"names":[],"mappings":"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"}
|