@dev-blinq/cucumber_client 1.0.1585-dev → 1.0.1587-dev

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.
@@ -219,27 +219,54 @@ export class RemoteBrowserService extends EventEmitter {
219
219
  super();
220
220
  this.CDP_CONNECT_URL = CDP_CONNECT_URL;
221
221
  this.context = context;
222
+ this.log("🚀 RemoteBrowserService initialized", { CDP_CONNECT_URL });
222
223
  this.initializeListeners();
223
224
  }
225
+ log(message, data) {
226
+ const timestamp = new Date().toISOString();
227
+ console.log(`[${timestamp}] [RemoteBrowserService] ${message}`, data ? JSON.stringify(data, null, 2) : "");
228
+ }
224
229
  async initializeListeners() {
230
+ this.log("📡 Initializing listeners");
225
231
  // Listen for new pages
226
232
  this.context.on("page", async (page) => {
233
+ this.log("🆕 New page event triggered", { url: page.url() });
227
234
  const id = await this.getPageId(page);
235
+ this.log("🔍 Got page ID from CDP", { id, url: page.url() });
228
236
  if (id) {
229
237
  this.pages.set(id, page);
238
+ this.log("✅ Page added to internal map", {
239
+ id,
240
+ url: page.url(),
241
+ totalPages: this.pages.size,
242
+ allPageIds: Array.from(this.pages.keys()),
243
+ });
230
244
  await this.syncState();
231
245
  }
246
+ else {
247
+ this.log("❌ Failed to get page ID, page not added to map", { url: page.url() });
248
+ }
232
249
  // Listen for page updates
233
250
  page.on("load", async () => {
251
+ this.log("🔄 Page load event", { id, url: page.url() });
234
252
  await this.syncState();
235
253
  });
236
254
  page.on("close", async () => {
255
+ this.log("🗑️ Page close event", { id, url: page.url() });
237
256
  if (id) {
238
257
  this.pages.delete(id);
258
+ this.log("✅ Page removed from internal map", {
259
+ id,
260
+ remainingPages: this.pages.size,
261
+ allPageIds: Array.from(this.pages.keys()),
262
+ });
239
263
  if (this._selectedPageId === id) {
240
- // Select first available page
241
264
  const firstPage = Array.from(this.pages.keys())[0];
242
265
  this._selectedPageId = firstPage || null;
266
+ this.log("🔄 Selected page changed after close", {
267
+ oldSelectedId: id,
268
+ newSelectedId: this._selectedPageId,
269
+ });
243
270
  }
244
271
  await this.syncState();
245
272
  }
@@ -247,155 +274,386 @@ export class RemoteBrowserService extends EventEmitter {
247
274
  });
248
275
  // Initialize with existing pages
249
276
  const existingPages = this.context.pages();
277
+ this.log("📄 Found existing pages", { count: existingPages.length });
250
278
  for (const page of existingPages) {
251
279
  const id = await this.getPageId(page);
280
+ this.log("🔍 Processing existing page", { id, url: page.url() });
252
281
  if (id) {
253
282
  this.pages.set(id, page);
283
+ this.log("✅ Existing page added to map", {
284
+ id,
285
+ url: page.url(),
286
+ totalPages: this.pages.size,
287
+ });
254
288
  }
255
- // page.on("framenavigated", async () => {
256
- // await new Promise((resolve) => setTimeout(resolve, 1000)); // wait for a bit
257
- // await this.syncState();
258
- // });
259
289
  }
260
290
  // Set initial selected page
261
291
  if (this.pages.size > 0 && !this._selectedPageId) {
262
292
  this._selectedPageId = Array.from(this.pages.keys())[0];
293
+ this.log("🎯 Initial selected page set", { selectedPageId: this._selectedPageId });
263
294
  }
295
+ this.log("✅ Initialization complete", {
296
+ totalPages: this.pages.size,
297
+ selectedPageId: this._selectedPageId,
298
+ allPageIds: Array.from(this.pages.keys()),
299
+ });
264
300
  await this.syncState();
265
301
  }
266
302
  async getPageId(page) {
267
303
  try {
268
- const debugData = await this.getDebugURLs();
269
304
  const pageUrl = page.url();
305
+ this.log("🔍 Getting page ID", { pageUrl });
306
+ // Try to get the CDP target ID directly from the page
307
+ // This is more reliable than URL matching
308
+ try {
309
+ const cdpSession = await page.context().newCDPSession(page);
310
+ const { targetInfo } = await cdpSession.send("Target.getTargetInfo");
311
+ await cdpSession.detach();
312
+ if (targetInfo && targetInfo.targetId) {
313
+ this.log("✅ Got target ID from CDP session", {
314
+ targetId: targetInfo.targetId,
315
+ url: pageUrl,
316
+ });
317
+ return targetInfo.targetId;
318
+ }
319
+ }
320
+ catch (cdpError) {
321
+ this.log("⚠️ Failed to get target ID from CDP session, falling back to URL matching", {
322
+ error: cdpError instanceof Error ? cdpError.message : String(cdpError),
323
+ });
324
+ }
325
+ const debugData = await this.getDebugURLs();
326
+ this.log("📊 CDP debug data received", {
327
+ totalPages: debugData.length,
328
+ pages: debugData.map((p) => ({ id: p.id, type: p.type, url: p.url })),
329
+ });
330
+ // Exact match
270
331
  for (const pageData of debugData) {
271
332
  if (pageData.type === "page" && pageData.url === pageUrl) {
333
+ this.log("✅ Found exact URL match", {
334
+ id: pageData.id,
335
+ url: pageUrl,
336
+ });
272
337
  return pageData.id;
273
338
  }
274
339
  }
275
- // If exact match fails, try to find by comparing URLs without protocol/www
340
+ this.log("⚠️ No exact match found, trying normalized URLs", { pageUrl });
341
+ // Normalized match
276
342
  const normalizeUrl = (url) => {
277
343
  try {
278
344
  const u = new URL(url);
279
- return u.hostname.replace(/^www\./, "") + u.pathname + u.search;
345
+ const normalized = u.hostname.replace(/^www\./, "") + u.pathname + u.search;
346
+ this.log("🔧 Normalized URL", { original: url, normalized });
347
+ return normalized;
280
348
  }
281
349
  catch {
350
+ this.log("❌ Failed to normalize URL", { url });
282
351
  return url;
283
352
  }
284
353
  };
285
354
  const normalizedPageUrl = normalizeUrl(pageUrl);
286
355
  for (const pageData of debugData) {
287
- if (pageData.type === "page" && normalizeUrl(pageData.url) === normalizedPageUrl) {
288
- return pageData.id;
356
+ if (pageData.type === "page") {
357
+ const normalizedDebugUrl = normalizeUrl(pageData.url);
358
+ if (normalizedDebugUrl === normalizedPageUrl) {
359
+ this.log("✅ Found normalized URL match", {
360
+ id: pageData.id,
361
+ pageUrl: normalizedPageUrl,
362
+ debugUrl: normalizedDebugUrl,
363
+ });
364
+ return pageData.id;
365
+ }
289
366
  }
290
367
  }
368
+ this.log("❌ No match found for page", {
369
+ pageUrl,
370
+ normalizedPageUrl,
371
+ availablePages: debugData.filter((p) => p.type === "page").map((p) => p.url),
372
+ });
291
373
  return null;
292
374
  }
293
375
  catch (error) {
294
- console.error("Error getting page ID:", error);
376
+ this.log("Error getting page ID", {
377
+ error: error instanceof Error ? error.message : String(error),
378
+ stack: error instanceof Error ? error.stack : undefined,
379
+ });
295
380
  return null;
296
381
  }
297
382
  }
298
383
  async getDebugURLs() {
299
384
  const url = `${this.CDP_CONNECT_URL}/json`;
300
- const response = await fetch(url);
301
- if (!response.ok) {
302
- throw new Error("Error while fetching debug URL");
385
+ this.log("📡 Fetching debug URLs", { url });
386
+ try {
387
+ const response = await fetch(url);
388
+ if (!response.ok) {
389
+ this.log("❌ Failed to fetch debug URLs", {
390
+ status: response.status,
391
+ statusText: response.statusText,
392
+ });
393
+ throw new Error("Error while fetching debug URL");
394
+ }
395
+ const data = await response.json();
396
+ this.log("✅ Debug URLs fetched successfully", {
397
+ count: data.length,
398
+ pages: data.map((p) => ({ id: p.id, type: p.type, url: p.url })),
399
+ });
400
+ return data;
401
+ }
402
+ catch (error) {
403
+ this.log("❌ Exception while fetching debug URLs", {
404
+ error: error instanceof Error ? error.message : String(error),
405
+ });
406
+ throw error;
303
407
  }
304
- return response.json();
305
408
  }
306
409
  async syncState() {
307
410
  try {
411
+ this.log("🔄 Starting state sync");
308
412
  const state = await this.getState();
309
- console.log(`Syncing state: ${state.pages.length} pages, selected: ${state.selectedPageId}`);
413
+ this.log("✅ State sync complete", {
414
+ pagesCount: state.pages.length,
415
+ selectedPageId: state.selectedPageId,
416
+ pages: state.pages.map((p) => ({ id: p.id, title: p.title, url: p.url })),
417
+ });
310
418
  this.emit("BrowserService.stateSync", state);
311
419
  }
312
420
  catch (error) {
313
- console.error("Error syncing state:", error);
421
+ this.log("Error syncing state", {
422
+ error: error instanceof Error ? error.message : String(error),
423
+ stack: error instanceof Error ? error.stack : undefined,
424
+ });
314
425
  }
315
426
  }
316
427
  async getState() {
428
+ this.log("📊 Getting current state", {
429
+ internalPagesCount: this.pages.size,
430
+ internalPageIds: Array.from(this.pages.keys()),
431
+ selectedPageId: this._selectedPageId,
432
+ });
317
433
  const debugData = await this.getDebugURLs();
318
- console.log(`Debug data fetched: ${JSON.stringify(debugData)} entries`);
434
+ this.log("📊 Debug data for state", {
435
+ debugPagesCount: debugData.length,
436
+ debugPages: debugData.map((p) => ({ id: p.id, type: p.type, url: p.url })),
437
+ });
319
438
  const pagesData = [];
320
- for (const [id, page] of this.pages.entries()) {
321
- const debugInfo = debugData.find((d) => d.id === id);
439
+ const updatedPages = new Map();
440
+ const matchedDebugIds = new Set(); // Track which CDP IDs we've already matched
441
+ let updatedSelectedPageId = this._selectedPageId;
442
+ for (const [oldId, page] of this.pages.entries()) {
443
+ this.log("🔍 Processing page from internal map", {
444
+ oldId,
445
+ url: page.url(),
446
+ });
447
+ // Try to get the current CDP target ID directly from the page
448
+ let currentId = null;
449
+ try {
450
+ const cdpSession = await page.context().newCDPSession(page);
451
+ const { targetInfo } = await cdpSession.send("Target.getTargetInfo");
452
+ await cdpSession.detach();
453
+ if (targetInfo && targetInfo.targetId) {
454
+ currentId = targetInfo.targetId;
455
+ this.log("✅ Got current target ID from CDP session", {
456
+ oldId,
457
+ currentId,
458
+ url: page.url(),
459
+ });
460
+ }
461
+ }
462
+ catch (cdpError) {
463
+ this.log("⚠️ Failed to get target ID from CDP session", {
464
+ oldId,
465
+ error: cdpError instanceof Error ? cdpError.message : String(cdpError),
466
+ });
467
+ }
468
+ // If we got the current ID from CDP session, use it
469
+ let debugInfo = currentId ? debugData.find((d) => d.id === currentId) : null;
470
+ // Fallback 1: Try to find by old ID
471
+ if (!debugInfo) {
472
+ debugInfo = debugData.find((d) => d.id === oldId && !matchedDebugIds.has(d.id));
473
+ }
474
+ // Fallback 2: Try to find by URL (only if not already matched)
475
+ if (!debugInfo) {
476
+ this.log("⚠️ Page ID not found in CDP, attempting to match by URL", {
477
+ oldId,
478
+ pageUrl: page.url(),
479
+ });
480
+ debugInfo = debugData.find((d) => d.type === "page" && d.url === page.url() && !matchedDebugIds.has(d.id));
481
+ if (debugInfo) {
482
+ this.log("✅ Found page by URL match, updating ID", {
483
+ oldId,
484
+ newId: debugInfo.id,
485
+ url: page.url(),
486
+ });
487
+ }
488
+ }
489
+ else {
490
+ this.log("✅ Found matching debug info by ID", {
491
+ id: debugInfo.id,
492
+ debugUrl: debugInfo.url,
493
+ pageUrl: page.url(),
494
+ });
495
+ }
322
496
  if (debugInfo) {
497
+ // Mark this CDP ID as matched to avoid duplicate matches
498
+ matchedDebugIds.add(debugInfo.id);
499
+ // Update selected page ID if this was the selected page and ID changed
500
+ if (oldId === this._selectedPageId && oldId !== debugInfo.id) {
501
+ updatedSelectedPageId = debugInfo.id;
502
+ this.log("🔄 Updated selected page ID", {
503
+ oldId,
504
+ newId: debugInfo.id,
505
+ });
506
+ }
323
507
  try {
324
- pagesData.push({
325
- id,
508
+ const pageData = {
509
+ id: debugInfo.id,
326
510
  title: await page.title(),
327
511
  url: page.url(),
328
512
  wsDebuggerUrl: debugInfo.webSocketDebuggerUrl || "",
329
- });
513
+ };
514
+ pagesData.push(pageData);
515
+ updatedPages.set(debugInfo.id, page);
516
+ this.log("✅ Page added to state", pageData);
330
517
  }
331
518
  catch (error) {
332
- console.error(`Error getting data for page ${id}:`, error);
519
+ this.log("❌ Error getting page data", {
520
+ id: oldId,
521
+ error: error instanceof Error ? error.message : String(error),
522
+ });
333
523
  }
334
524
  }
525
+ else {
526
+ this.log("⚠️ No matching debug info found by ID or URL", {
527
+ oldId,
528
+ pageUrl: page.url(),
529
+ availableDebugIds: debugData.map((d) => d.id),
530
+ availableDebugUrls: debugData.map((d) => d.url),
531
+ });
532
+ }
335
533
  }
336
- return {
534
+ // Update the internal pages map with current CDP IDs
535
+ if (updatedPages.size !== this.pages.size || updatedSelectedPageId !== this._selectedPageId) {
536
+ this.log("🔄 Updating internal state with new CDP IDs", {
537
+ oldPagesCount: this.pages.size,
538
+ newPagesCount: updatedPages.size,
539
+ oldSelectedId: this._selectedPageId,
540
+ newSelectedId: updatedSelectedPageId,
541
+ });
542
+ this.pages = updatedPages;
543
+ this._selectedPageId = updatedSelectedPageId;
544
+ }
545
+ const state = {
337
546
  pages: pagesData,
338
547
  selectedPageId: this._selectedPageId,
339
548
  };
549
+ this.log("📦 Final state", state);
550
+ return state;
340
551
  }
341
552
  async createTab(url = "about:blank") {
342
553
  try {
343
- console.log(`Creating tab: ${url}`);
554
+ this.log("🆕 Creating new tab", { url });
344
555
  const page = await this.context.newPage();
556
+ this.log("✅ New page created by Playwright", { url: page.url() });
345
557
  if (typeof url === "string" && url !== "about:blank") {
558
+ this.log("🌐 Navigating to URL", { url });
346
559
  await page.goto(url, { waitUntil: "domcontentloaded" });
560
+ this.log("✅ Navigation complete", { finalUrl: page.url() });
347
561
  }
348
- // Wait a bit for the page to be registered in CDP
349
- await new Promise((resolve) => setTimeout(resolve, 100));
562
+ // Wait for CDP to register the page
563
+ this.log("⏳ Waiting for CDP registration...");
564
+ await new Promise((resolve) => setTimeout(resolve, 500));
350
565
  const id = await this.getPageId(page);
566
+ this.log("🔍 Retrieved page ID after wait", { id, url: page.url() });
351
567
  if (id) {
352
568
  this.pages.set(id, page);
353
569
  this._selectedPageId = id;
354
- console.log(`Tab created successfully: ${id}`);
570
+ this.log("✅ Tab created successfully", {
571
+ id,
572
+ url: page.url(),
573
+ totalPages: this.pages.size,
574
+ allPageIds: Array.from(this.pages.keys()),
575
+ selectedPageId: this._selectedPageId,
576
+ });
577
+ }
578
+ else {
579
+ this.log("❌ Failed to get page ID for new tab", { url: page.url() });
580
+ // Try to get debug data to see what's available
581
+ const debugData = await this.getDebugURLs();
582
+ this.log("🔍 Current CDP state after failed ID retrieval", {
583
+ debugPages: debugData.map((p) => ({ id: p.id, url: p.url, type: p.type })),
584
+ });
355
585
  }
356
586
  await this.syncState();
357
587
  }
358
588
  catch (error) {
359
- console.error("Error creating tab:", error);
589
+ this.log("Error creating tab", {
590
+ url,
591
+ error: error instanceof Error ? error.message : String(error),
592
+ stack: error instanceof Error ? error.stack : undefined,
593
+ });
360
594
  throw error;
361
595
  }
362
596
  }
363
597
  async closeTab(pageId) {
364
598
  try {
365
- console.log(`Closing tab: ${pageId}`);
599
+ this.log("🗑️ Closing tab", { pageId });
366
600
  const page = this.pages.get(pageId);
367
601
  if (page) {
602
+ this.log("✅ Found page to close", { pageId, url: page.url() });
368
603
  await page.close();
369
- // The close event handler will handle cleanup and sync
604
+ this.log("✅ Page closed successfully", { pageId });
370
605
  }
371
606
  else {
372
- console.warn(`Page ${pageId} not found`);
607
+ this.log("⚠️ Page not found", {
608
+ pageId,
609
+ availablePageIds: Array.from(this.pages.keys()),
610
+ });
373
611
  }
374
612
  }
375
613
  catch (error) {
376
- console.error("Error closing tab:", error);
614
+ this.log("Error closing tab", {
615
+ pageId,
616
+ error: error instanceof Error ? error.message : String(error),
617
+ });
377
618
  throw error;
378
619
  }
379
620
  }
380
621
  async selectTab(pageId) {
381
622
  try {
382
- console.log(`Selecting tab: ${pageId}`);
623
+ this.log("🎯 Selecting tab", { pageId });
383
624
  const page = this.pages.get(pageId);
384
625
  if (page) {
385
626
  this._selectedPageId = pageId;
386
627
  await page.bringToFront();
628
+ this.log("✅ Tab selected successfully", {
629
+ pageId,
630
+ url: page.url(),
631
+ selectedPageId: this._selectedPageId,
632
+ });
387
633
  await this.syncState();
388
634
  }
389
635
  else {
390
- console.warn(`Page ${pageId} not found`);
636
+ this.log("⚠️ Page not found for selection", {
637
+ pageId,
638
+ availablePageIds: Array.from(this.pages.keys()),
639
+ });
391
640
  }
392
641
  }
393
642
  catch (error) {
394
- console.error("Error selecting tab:", error);
643
+ this.log("Error selecting tab", {
644
+ pageId,
645
+ error: error instanceof Error ? error.message : String(error),
646
+ });
395
647
  throw error;
396
648
  }
397
649
  }
398
650
  getSelectedPage() {
399
- return this._selectedPageId ? this.pages.get(this._selectedPageId) || null : null;
651
+ const page = this._selectedPageId ? this.pages.get(this._selectedPageId) || null : null;
652
+ this.log("🔍 Getting selected page", {
653
+ selectedPageId: this._selectedPageId,
654
+ found: !!page,
655
+ url: page?.url(),
656
+ });
657
+ return page;
400
658
  }
401
659
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev-blinq/cucumber_client",
3
- "version": "1.0.1585-dev",
3
+ "version": "1.0.1587-dev",
4
4
  "description": " ",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",