@detiq/crawler 0.2.1 → 0.2.2
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/package.json +1 -1
- package/src/crawler.ts +38 -19
package/package.json
CHANGED
package/src/crawler.ts
CHANGED
|
@@ -3109,9 +3109,19 @@ export async function crawlProject(opts: {
|
|
|
3109
3109
|
await onProgress?.(90, `App map saved · ${visited.size} screens, ${edges.length} flows wired`);
|
|
3110
3110
|
}
|
|
3111
3111
|
|
|
3112
|
+
// P5: Export session cookies BEFORE session health check so we can reuse them even if
|
|
3113
|
+
// the main browser session is stale. The cookie jar stays valid for fresh browser contexts
|
|
3114
|
+
// even when the CDP-connected Stagehand page shows a redirect to /login.
|
|
3115
|
+
let mainStorageState: any;
|
|
3116
|
+
try {
|
|
3117
|
+
mainStorageState = await playwrightBrowser!.contexts()[0]?.storageState();
|
|
3118
|
+
} catch { /* non-fatal */ }
|
|
3119
|
+
|
|
3112
3120
|
// P5: Orphan page auth re-verify — confirm session is still alive before tail-end viewport pass.
|
|
3113
3121
|
// The main BFS can span many minutes; the session may have expired by the time we reach here.
|
|
3114
|
-
// If expired, attempt re-auth via Stagehand before proceeding.
|
|
3122
|
+
// If expired, attempt re-auth via Stagehand before proceeding.
|
|
3123
|
+
// Creative resilience: even if the main browser is dead, we still run the viewport pass using
|
|
3124
|
+
// the exported cookie jar — fresh browsers are often accepted by servers even after CDP session loss.
|
|
3115
3125
|
let sessionAliveForViewports = true;
|
|
3116
3126
|
try {
|
|
3117
3127
|
await page.goto(opts.appUrl, { waitUntil: 'domcontentloaded', timeout: 15_000 });
|
|
@@ -3130,19 +3140,24 @@ export async function crawlProject(opts: {
|
|
|
3130
3140
|
).catch(() => false);
|
|
3131
3141
|
}
|
|
3132
3142
|
if (reauthed) {
|
|
3143
|
+
// Refresh cookie jar with newly authenticated session
|
|
3144
|
+
try { mainStorageState = await playwrightBrowser!.contexts()[0]?.storageState(); } catch { /* keep prior */ }
|
|
3133
3145
|
logger.info('[P5] Re-auth succeeded — proceeding with viewport pass');
|
|
3134
3146
|
} else {
|
|
3135
3147
|
sessionAliveForViewports = false;
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3148
|
+
const hasCookies = (mainStorageState?.cookies?.length ?? 0) > 0;
|
|
3149
|
+
logger.warn({ landed: sessionCheckUrl, hasCookies }, '[P5] Re-auth failed — will attempt viewport pass with exported cookies');
|
|
3150
|
+
if (!hasCookies) {
|
|
3151
|
+
AppBrain.saveCrawlEvidence(opts.tenantId, opts.projectId, {
|
|
3152
|
+
jobId: opts.jobId,
|
|
3153
|
+
type: 'AUTH_FAILURE',
|
|
3154
|
+
payload: {
|
|
3155
|
+
url: opts.appUrl,
|
|
3156
|
+
landed: sessionCheckUrl,
|
|
3157
|
+
reason: 'Session expired before mobile/tablet viewport pass and re-auth failed. Re-capture the session in project settings.',
|
|
3158
|
+
},
|
|
3159
|
+
}).catch(() => { });
|
|
3160
|
+
}
|
|
3146
3161
|
}
|
|
3147
3162
|
} else {
|
|
3148
3163
|
logger.info({ url: sessionCheckUrl }, '[P5] Session alive — proceeding with viewport pass');
|
|
@@ -3162,7 +3177,10 @@ export async function crawlProject(opts: {
|
|
|
3162
3177
|
// (isMobile, hasTouch, viewport, deviceScaleFactor) on new contexts — the flags are
|
|
3163
3178
|
// silently ignored, so screenshots come out at desktop dimensions regardless of profile.
|
|
3164
3179
|
const extraProfiles = (opts.deviceProfiles ?? []).filter((p) => p !== 'DESKTOP');
|
|
3165
|
-
|
|
3180
|
+
// Run the viewport pass even when the main session is dead, as long as we exported cookies —
|
|
3181
|
+
// fresh browsers often authenticate successfully with the saved cookie jar.
|
|
3182
|
+
const hasExportedAuth = (mainStorageState?.cookies?.length ?? 0) > 0;
|
|
3183
|
+
if ((sessionAliveForViewports || hasExportedAuth) && extraProfiles.length > 0 && discovered.length > 0) {
|
|
3166
3184
|
const { devices, chromium: pwChromium } = await import('playwright');
|
|
3167
3185
|
const DEVICE_MAP: Record<string, any> = {
|
|
3168
3186
|
// Override viewport heights: Playwright's device descriptors use viewport-minus-browser-chrome
|
|
@@ -3171,11 +3189,6 @@ export async function crawlProject(opts: {
|
|
|
3171
3189
|
MOBILE_PORTRAIT: { ...devices['iPhone 15'], viewport: { width: 393, height: 852 } },
|
|
3172
3190
|
TABLET: { ...devices['iPad Pro 11'], viewport: { width: 834, height: 1194 } },
|
|
3173
3191
|
};
|
|
3174
|
-
// Export session cookies from the main crawl so mobile browser is already authenticated
|
|
3175
|
-
let mainStorageState: any;
|
|
3176
|
-
try {
|
|
3177
|
-
mainStorageState = await playwrightBrowser!.contexts()[0]?.storageState();
|
|
3178
|
-
} catch { /* non-fatal — mobile pass will proceed without auth */ }
|
|
3179
3192
|
|
|
3180
3193
|
for (const profile of extraProfiles) {
|
|
3181
3194
|
const deviceConfig = DEVICE_MAP[profile];
|
|
@@ -3224,13 +3237,19 @@ export async function crawlProject(opts: {
|
|
|
3224
3237
|
).catch(() => { });
|
|
3225
3238
|
}
|
|
3226
3239
|
} catch (err) {
|
|
3227
|
-
|
|
3240
|
+
logger.warn({ profile, url: disc.url, err: String(err) }, `[P5] ${profile} screenshot failed — skipping screen`);
|
|
3241
|
+
AppBrain.saveCrawlEvidence(opts.tenantId, opts.projectId, {
|
|
3242
|
+
jobId: opts.jobId,
|
|
3243
|
+
screenId: disc.screenId,
|
|
3244
|
+
type: 'VIEWPORT_SCREENSHOT_FAILURE',
|
|
3245
|
+
payload: { profile, url: disc.url, reason: String(err) },
|
|
3246
|
+
}).catch(() => { });
|
|
3228
3247
|
}
|
|
3229
3248
|
}
|
|
3230
3249
|
await mobilePage.close().catch(() => { });
|
|
3231
3250
|
await mobileCtx.close().catch(() => { });
|
|
3232
3251
|
} catch (profileErr) {
|
|
3233
|
-
|
|
3252
|
+
logger.warn({ profile, err: String(profileErr) }, `[P5] ${profile} browser launch failed`);
|
|
3234
3253
|
} finally {
|
|
3235
3254
|
await mobileBrowser?.close().catch(() => { });
|
|
3236
3255
|
}
|