@apitap/core 1.0.8 → 1.0.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apitap/core",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Intercept web API traffic during browsing. Generate portable skill files so AI agents can call APIs directly instead of scraping.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -144,21 +144,26 @@ async function doHandoff(
144
144
  // Navigate to login page
145
145
  await page.goto(loginUrl, { waitUntil: 'domcontentloaded', timeout: 30_000 });
146
146
 
147
- // Poll for login success: check cookies periodically
147
+ // Baseline: snapshot cookie names present BEFORE login so we can detect new ones
148
+ const baselineCookies = await context.cookies();
149
+ const baselineCookieNames = new Set(baselineCookies.map(c => c.name));
150
+
151
+ // Poll for login success: check for NEW session-like cookies
148
152
  const startTime = Date.now();
149
153
  let loginDetected = false;
150
154
 
151
155
  while (Date.now() - startTime < timeout) {
152
156
  await page.waitForTimeout(2000);
153
157
 
154
- // Check if we've detected session cookies
158
+ // Only trigger on NEW session-like cookies not present at page load
155
159
  const cookies = await context.cookies();
156
- const hasSessionCookie = cookies.some(c =>
160
+ const hasNewSessionCookie = cookies.some(c =>
161
+ !baselineCookieNames.has(c.name) &&
157
162
  SESSION_COOKIE_PATTERNS.some(p => p.test(c.name)) &&
158
163
  !TRACKING_COOKIE_PATTERNS.some(p => p.test(c.name))
159
164
  );
160
165
 
161
- if (hasSessionCookie || authDetected) {
166
+ if (hasNewSessionCookie || authDetected) {
162
167
  // Grace period: 4 additional polls at 2s each (~8s total)
163
168
  // Allows time for MFA, CAPTCHAs, and post-login redirects
164
169
  for (let grace = 0; grace < 4; grace++) {
@@ -75,13 +75,8 @@ export class CaptureSession {
75
75
 
76
76
  this.setupResponseListener();
77
77
 
78
- // Auto-timeout to prevent leaked browsers (unref so it doesn't block process exit)
79
- const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
80
- this.timeoutTimer = setTimeout(() => {
81
- this.expired = true;
82
- this.cleanup().catch(() => {});
83
- }, timeoutMs);
84
- if (this.timeoutTimer.unref) this.timeoutTimer.unref();
78
+ // Auto-timeout to prevent leaked browsers (resets on each interaction)
79
+ this.resetTimeout();
85
80
 
86
81
  await this.page.goto(this.targetUrl, { waitUntil: 'domcontentloaded' });
87
82
  return this.takeSnapshot();
@@ -92,6 +87,9 @@ export class CaptureSession {
92
87
  if (this.closed) return { success: false, error: 'Session closed', snapshot: this.emptySnapshot() };
93
88
  if (!this.page) return { success: false, error: 'Session not started', snapshot: this.emptySnapshot() };
94
89
 
90
+ // Reset timeout on each interaction — active sessions aren't leaked browsers
91
+ this.resetTimeout();
92
+
95
93
  try {
96
94
  switch (action.action) {
97
95
  case 'snapshot':
@@ -468,6 +466,17 @@ export class CaptureSession {
468
466
  };
469
467
  }
470
468
 
469
+ /** Reset the auto-timeout timer (called on each interaction to prevent premature close) */
470
+ private resetTimeout(): void {
471
+ if (this.timeoutTimer) clearTimeout(this.timeoutTimer);
472
+ const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
473
+ this.timeoutTimer = setTimeout(() => {
474
+ this.expired = true;
475
+ this.cleanup().catch(() => {});
476
+ }, timeoutMs);
477
+ if (this.timeoutTimer.unref) this.timeoutTimer.unref();
478
+ }
479
+
471
480
  private async cleanup(): Promise<void> {
472
481
  if (this.closed) return;
473
482
  this.closed = true;