vite-plugin-smart-prefetch 0.3.5 → 0.3.6

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/index.d.cts CHANGED
@@ -218,4 +218,42 @@ interface ViteManifest {
218
218
 
219
219
  declare function smartPrefetch(options?: PluginOptions): Plugin;
220
220
 
221
- export { type AdvancedConfig, type AnalyticsConfig, type BigQueryCredentials, type CacheConfig, type DataRangeConfig, type ManualRules, type ModelConfig, type NavigationData, type PluginOptions, type PrefetchConfig, type PrefetchModel, type PrefetchStrategy, type PrefetchTarget, type ViteManifest, smartPrefetch };
221
+ /**
222
+ * BigQuery Analytics Connector
223
+ * Queries GA4 event data from BigQuery to extract real navigation transitions
224
+ */
225
+
226
+ declare class BigQueryAnalyticsConnector {
227
+ private bigquery;
228
+ private projectId;
229
+ private datasetId;
230
+ private location;
231
+ private debug;
232
+ constructor(projectId: string, datasetId: string, location?: string, debug?: boolean);
233
+ /**
234
+ * Fetch real navigation transitions from BigQuery GA4 export
235
+ * Queries the events table for page_view events with previous_page_path parameter
236
+ */
237
+ fetchNavigationSequences(config?: DataRangeConfig): Promise<NavigationData[]>;
238
+ /**
239
+ * Normalize route paths
240
+ * Converts dynamic segments to parameters and standardizes format
241
+ */
242
+ private normalizeRoute;
243
+ /**
244
+ * Save raw and processed data for inspection
245
+ * Helps identify issues in data transformation pipeline
246
+ */
247
+ private saveDataForInspection;
248
+ /**
249
+ * Fetch navigation data WITH optional segment information
250
+ * Only includes segment/grouping field when explicitly requested
251
+ * @param config - Data range configuration with optional segmentField
252
+ * @returns Navigation data, optionally with segment field included
253
+ */
254
+ fetchNavigationWithSegments(config?: DataRangeConfig & {
255
+ segmentField?: string;
256
+ }): Promise<any[]>;
257
+ }
258
+
259
+ export { type AdvancedConfig, type AnalyticsConfig, BigQueryAnalyticsConnector, type BigQueryCredentials, type CacheConfig, type DataRangeConfig, type ManualRules, type ModelConfig, type NavigationData, type PluginOptions, type PrefetchConfig, type PrefetchModel, type PrefetchStrategy, type PrefetchTarget, type ViteManifest, smartPrefetch };
package/dist/index.d.ts CHANGED
@@ -218,4 +218,42 @@ interface ViteManifest {
218
218
 
219
219
  declare function smartPrefetch(options?: PluginOptions): Plugin;
220
220
 
221
- export { type AdvancedConfig, type AnalyticsConfig, type BigQueryCredentials, type CacheConfig, type DataRangeConfig, type ManualRules, type ModelConfig, type NavigationData, type PluginOptions, type PrefetchConfig, type PrefetchModel, type PrefetchStrategy, type PrefetchTarget, type ViteManifest, smartPrefetch };
221
+ /**
222
+ * BigQuery Analytics Connector
223
+ * Queries GA4 event data from BigQuery to extract real navigation transitions
224
+ */
225
+
226
+ declare class BigQueryAnalyticsConnector {
227
+ private bigquery;
228
+ private projectId;
229
+ private datasetId;
230
+ private location;
231
+ private debug;
232
+ constructor(projectId: string, datasetId: string, location?: string, debug?: boolean);
233
+ /**
234
+ * Fetch real navigation transitions from BigQuery GA4 export
235
+ * Queries the events table for page_view events with previous_page_path parameter
236
+ */
237
+ fetchNavigationSequences(config?: DataRangeConfig): Promise<NavigationData[]>;
238
+ /**
239
+ * Normalize route paths
240
+ * Converts dynamic segments to parameters and standardizes format
241
+ */
242
+ private normalizeRoute;
243
+ /**
244
+ * Save raw and processed data for inspection
245
+ * Helps identify issues in data transformation pipeline
246
+ */
247
+ private saveDataForInspection;
248
+ /**
249
+ * Fetch navigation data WITH optional segment information
250
+ * Only includes segment/grouping field when explicitly requested
251
+ * @param config - Data range configuration with optional segmentField
252
+ * @returns Navigation data, optionally with segment field included
253
+ */
254
+ fetchNavigationWithSegments(config?: DataRangeConfig & {
255
+ segmentField?: string;
256
+ }): Promise<any[]>;
257
+ }
258
+
259
+ export { type AdvancedConfig, type AnalyticsConfig, BigQueryAnalyticsConnector, type BigQueryCredentials, type CacheConfig, type DataRangeConfig, type ManualRules, type ModelConfig, type NavigationData, type PluginOptions, type PrefetchConfig, type PrefetchModel, type PrefetchStrategy, type PrefetchTarget, type ViteManifest, smartPrefetch };
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ var BigQueryAnalyticsConnector = class {
6
6
  constructor(projectId, datasetId, location = "asia-south1", debug = false) {
7
7
  this.projectId = projectId;
8
8
  this.datasetId = datasetId;
9
- this.location = location;
9
+ this.location = location && location !== "asia" ? location : "asia-south1";
10
10
  this.debug = debug;
11
11
  this.bigquery = new BigQuery({
12
12
  projectId
@@ -15,7 +15,8 @@ var BigQueryAnalyticsConnector = class {
15
15
  console.log("\u2705 BigQuery Analytics connector initialized");
16
16
  console.log(` Project ID: ${projectId}`);
17
17
  console.log(` Dataset ID: ${datasetId}`);
18
- console.log(` Location: ${location}`);
18
+ console.log(` Location: ${this.location}`);
19
+ console.log(` Querying daily events tables: ${projectId}.${datasetId}.events_*`);
19
20
  }
20
21
  }
21
22
  /**
@@ -25,20 +26,27 @@ var BigQueryAnalyticsConnector = class {
25
26
  async fetchNavigationSequences(config = {}) {
26
27
  const { days = 30 } = config;
27
28
  const minSessions = 1;
28
- console.log(`\u{1F4CA} Fetching navigation data from BigQuery...`);
29
- console.log(` Dataset: ${this.datasetId}`);
29
+ console.log(`
30
+ ${"\u2550".repeat(60)}`);
31
+ console.log(`\u{1F4CA} STAGE 1: PREPARING BIGQUERY QUERY`);
32
+ console.log(`${"\u2550".repeat(60)}`);
33
+ console.log(` Project ID: ${this.projectId}`);
34
+ console.log(` Dataset ID: ${this.datasetId}`);
35
+ console.log(` Location: ${this.location}`);
30
36
  console.log(` Date range: Last ${days} days`);
37
+ console.log(` Daily tables pattern: events_*`);
38
+ console.log(` Event type filter: page_view`);
39
+ console.log(` Expected fields: user_pseudo_id, ga_session_id, page_path`);
31
40
  try {
32
41
  const query = `
33
42
  WITH page_sessions AS (
34
43
  SELECT
35
- user_id,
44
+ user_pseudo_id,
36
45
  (SELECT value.int_value FROM UNNEST(event_params) WHERE KEY = 'ga_session_id' LIMIT 1) as session_id,
37
- (SELECT value.string_value FROM UNNEST(event_params) WHERE KEY = 'page_location' LIMIT 1) as page_location,
38
46
  (SELECT value.string_value FROM UNNEST(event_params) WHERE KEY = 'page_path' LIMIT 1) as page_path,
39
47
  event_timestamp,
40
48
  ROW_NUMBER() OVER (
41
- PARTITION BY user_id, (SELECT value.int_value FROM UNNEST(event_params) WHERE KEY = 'ga_session_id' LIMIT 1)
49
+ PARTITION BY user_pseudo_id, (SELECT value.int_value FROM UNNEST(event_params) WHERE KEY = 'ga_session_id' LIMIT 1)
42
50
  ORDER BY event_timestamp
43
51
  ) as page_sequence
44
52
  FROM \`${this.projectId}.${this.datasetId}.events_*\`
@@ -52,7 +60,7 @@ var BigQueryAnalyticsConnector = class {
52
60
  SELECT
53
61
  COALESCE(curr.page_path, '(direct)') as from_page,
54
62
  LEAD(curr.page_path) OVER (
55
- PARTITION BY curr.user_id, curr.session_id
63
+ PARTITION BY curr.user_pseudo_id, curr.session_id
56
64
  ORDER BY curr.page_sequence
57
65
  ) as to_page
58
66
  FROM page_sessions curr
@@ -64,52 +72,132 @@ var BigQueryAnalyticsConnector = class {
64
72
  COUNT(*) as transition_count
65
73
  FROM transitions
66
74
  WHERE to_page IS NOT NULL
67
- GROUP BY from_page, to_page
75
+ GROUP BY previous_page_path, page_path
68
76
  ORDER BY transition_count DESC
69
77
  LIMIT 10000
70
78
  `;
71
79
  if (this.debug) {
72
- console.log(`\u{1F4E4} BigQuery Query:`);
80
+ console.log(`
81
+ \u{1F4E4} Full BigQuery Query:`);
82
+ console.log(`${"\u2500".repeat(60)}`);
73
83
  console.log(query);
84
+ console.log(`${"\u2500".repeat(60)}`);
74
85
  }
86
+ console.log(`
87
+ ${"\u2550".repeat(60)}`);
88
+ console.log(`\u{1F4CA} STAGE 2: EXECUTING QUERY`);
89
+ console.log(`${"\u2550".repeat(60)}`);
90
+ console.log(` \u23F3 Querying BigQuery...`);
75
91
  const [rows] = await this.bigquery.query({
76
92
  query,
77
93
  location: this.location
78
94
  });
79
- if (this.debug) {
80
- console.log(`\u2705 Query executed successfully`);
81
- console.log(` Rows returned: ${rows.length}`);
95
+ console.log(` \u2705 Query executed successfully`);
96
+ console.log(` \u{1F4C8} Total transitions returned: ${rows.length}`);
97
+ if (rows.length === 0) {
98
+ console.log(`
99
+ ${"\u2550".repeat(60)}`);
100
+ console.log(`\u26A0\uFE0F STAGE 2: NO DATA RETURNED!`);
101
+ console.log(`${"\u2550".repeat(60)}`);
102
+ console.log(` \u274C BigQuery returned 0 rows`);
103
+ console.log(`
104
+ Possible causes:`);
105
+ console.log(` 1. No event tables in date range (events_YYYYMMDD)`);
106
+ console.log(` 2. No page_view events in those tables`);
107
+ console.log(` 3. page_path parameter not being tracked`);
108
+ console.log(` 4. Wrong location (currently: ${this.location})`);
109
+ console.log(` 5. Wrong dataset name: ${this.datasetId}`);
110
+ console.log(`
111
+ Troubleshooting:`);
112
+ console.log(` \u2022 Check BigQuery console for available event tables`);
113
+ console.log(` \u2022 Verify GA4 property is exporting to BigQuery`);
114
+ console.log(` \u2022 Confirm page_path is being captured in GA4`);
115
+ console.log(`${"\u2550".repeat(60)}
116
+ `);
117
+ return [];
82
118
  }
119
+ console.log(`
120
+ ${"\u2550".repeat(60)}`);
121
+ console.log(`\u{1F4CA} STAGE 3: PROCESSING RAW DATA`);
122
+ console.log(`${"\u2550".repeat(60)}`);
123
+ console.log(` Processing ${rows.length} raw transitions...`);
83
124
  const navigationData = [];
125
+ let processedCount = 0;
126
+ let filteredCount = 0;
127
+ let emptyPathCount = 0;
128
+ let lowCountCount = 0;
84
129
  const rawData = rows.map((row) => ({
85
130
  previous_page_path: row.previous_page_path,
86
131
  page_path: row.page_path,
87
132
  transition_count: row.transition_count
88
133
  }));
89
- rows.forEach((row) => {
134
+ console.log(`
135
+ ${"\u2500".repeat(56)}`);
136
+ console.log(` Processing each transition row...`);
137
+ console.log(` ${"\u2500".repeat(56)}`);
138
+ rows.forEach((row, index) => {
90
139
  const previousPage = row.previous_page_path || "(direct)";
91
140
  const currentPage = row.page_path || "";
92
141
  const transitionCount = parseInt(row.transition_count || "0");
142
+ if (this.debug && index < 5) {
143
+ console.log(`
144
+ Row ${index + 1}:`);
145
+ console.log(` Raw: "${previousPage}" \u2192 "${currentPage}" (count: ${transitionCount})`);
146
+ }
93
147
  const normalizedPrevious = previousPage === "(direct)" || previousPage === "(not set)" ? "(direct)" : this.normalizeRoute(previousPage);
94
148
  const normalizedCurrent = this.normalizeRoute(currentPage);
149
+ if (this.debug && index < 5) {
150
+ console.log(` Normalized: "${normalizedPrevious}" \u2192 "${normalizedCurrent}"`);
151
+ }
95
152
  if (normalizedCurrent && transitionCount >= minSessions) {
96
153
  navigationData.push({
97
154
  from: normalizedPrevious,
98
155
  to: normalizedCurrent,
99
156
  count: transitionCount
100
157
  });
158
+ processedCount++;
159
+ if (this.debug && index < 5) {
160
+ console.log(` \u2705 ACCEPTED`);
161
+ }
162
+ } else {
163
+ filteredCount++;
164
+ if (!normalizedCurrent) {
165
+ emptyPathCount++;
166
+ if (this.debug && index < 5) {
167
+ console.log(` \u274C FILTERED: normalized to empty path (likely build artifact)`);
168
+ }
169
+ } else if (transitionCount < minSessions) {
170
+ lowCountCount++;
171
+ if (this.debug && index < 5) {
172
+ console.log(` \u274C FILTERED: transition count ${transitionCount} < minSessions ${minSessions}`);
173
+ }
174
+ }
101
175
  }
102
176
  });
177
+ console.log(`
178
+ ${"\u2500".repeat(56)}`);
179
+ console.log(` Processing Summary:`);
180
+ console.log(` \u2705 Accepted: ${processedCount}`);
181
+ console.log(` \u274C Filtered out: ${filteredCount}`);
182
+ console.log(` \u2022 Empty paths: ${emptyPathCount}`);
183
+ console.log(` \u2022 Low transition count: ${lowCountCount}`);
103
184
  this.saveDataForInspection(rawData, navigationData);
104
- if (this.debug) {
105
- console.log(`\u2705 Processed ${navigationData.length} navigation transitions`);
106
- if (navigationData.length > 0) {
107
- console.log(`
185
+ console.log(`
186
+ ${"\u2550".repeat(60)}`);
187
+ console.log(`\u{1F4CA} STAGE 4: FINAL RESULTS`);
188
+ console.log(`${"\u2550".repeat(60)}`);
189
+ console.log(` Total valid transitions: ${navigationData.length}`);
190
+ if (navigationData.length === 0) {
191
+ console.log(`
192
+ \u26A0\uFE0F WARNING: All transitions were filtered out!`);
193
+ console.log(` Check the logs above to see why rows were rejected.`);
194
+ console.log(` Raw data saved to: .bigquery-raw-data/raw-bigquery-response.json`);
195
+ } else {
196
+ console.log(`
108
197
  Top 5 transitions:`);
109
- navigationData.slice(0, 5).forEach((nav, i) => {
110
- console.log(` ${i + 1}. ${nav.from} \u2192 ${nav.to} (${nav.count} transitions)`);
111
- });
112
- }
198
+ navigationData.slice(0, 5).forEach((nav, i) => {
199
+ console.log(` ${i + 1}. ${nav.from} \u2192 ${nav.to} (${nav.count})`);
200
+ });
113
201
  const uniqueRoutes = /* @__PURE__ */ new Set();
114
202
  navigationData.forEach((nav) => {
115
203
  if (nav.from !== "(direct)") uniqueRoutes.add(nav.from);
@@ -119,85 +207,45 @@ var BigQueryAnalyticsConnector = class {
119
207
  \u{1F4CA} Unique routes: ${uniqueRoutes.size}`);
120
208
  console.log(` Routes: ${Array.from(uniqueRoutes).sort().join(", ")}`);
121
209
  console.log(`
122
- \u{1F4C1} Raw data saved to: .bigquery-raw-data/`);
210
+ \u{1F4C1} Data inspection files created:`);
211
+ console.log(` \u2022 .bigquery-raw-data/raw-bigquery-response.json`);
212
+ console.log(` \u2022 .bigquery-raw-data/processed-navigation-data.json`);
213
+ console.log(` \u2022 .bigquery-raw-data/data-transformation-summary.json`);
123
214
  }
215
+ console.log(`${"\u2550".repeat(60)}
216
+ `);
124
217
  return navigationData;
125
218
  } catch (error) {
126
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
219
+ const errorMessage = error instanceof Error ? error.message : String(error) || "Unknown error";
220
+ console.log(`
221
+ ${"\u2550".repeat(60)}`);
222
+ console.log(`\u274C ERROR: QUERY EXECUTION FAILED`);
223
+ console.log(`${"\u2550".repeat(60)}`);
127
224
  if (errorMessage.includes("bigquery.jobs.create") || errorMessage.includes("PERMISSION_DENIED")) {
128
- console.error("\u274C Failed to fetch BigQuery data: Permission Denied");
129
- console.error(" Service account needs BigQuery Job User role in the project");
130
- console.error(" Error details:", errorMessage);
225
+ console.error(` \u274C Permission Denied`);
226
+ console.error(` Service account needs BigQuery Job User role in project: ${this.projectId}`);
227
+ console.error(` Error: ${errorMessage}`);
228
+ } else if (errorMessage.includes("was not found")) {
229
+ console.error(` \u274C Dataset not found`);
230
+ console.error(` Cannot find dataset: ${this.projectId}.${this.datasetId}`);
231
+ console.error(` Try a different location or check dataset name`);
232
+ console.error(` Current location: ${this.location}`);
233
+ console.error(` Error: ${errorMessage}`);
131
234
  } else {
132
- console.error("\u274C Failed to fetch BigQuery data:", error);
235
+ const errorType = error instanceof Error ? error.constructor.name : "Unknown";
236
+ console.error(` Error type: ${errorType}`);
237
+ console.error(` Message: ${errorMessage}`);
133
238
  }
239
+ console.log(`${"\u2550".repeat(60)}
240
+ `);
134
241
  throw new Error(
135
242
  `BigQuery Analytics error: ${errorMessage}`
136
243
  );
137
244
  }
138
245
  }
139
- /**
140
- * Map friendly page names (with spaces/capitals) back to route paths
141
- * GA4 may log display names instead of URL paths
142
- * Examples: "Audit Logs" → "/audit-logs", "API Documentation" → "/api-docs", "Home" → "/"
143
- */
144
- friendlyNameToRoute(name) {
145
- const friendlyToRoute = {
146
- // Exact matches (with spaces and capitals)
147
- "API Documentation": "/api-docs",
148
- "Audit Logs": "/audit-logs",
149
- "/Home": "/",
150
- "/home": "/",
151
- "Home": "/",
152
- "/Dashboard": "/dashboard",
153
- "Dashboard": "/dashboard",
154
- // PascalCase variants (all 27 routes)
155
- "Profile": "/profile",
156
- "Settings": "/settings",
157
- "Preferences": "/preferences",
158
- "Privacy": "/privacy",
159
- "Security": "/security",
160
- "Analytics": "/analytics",
161
- "Reports": "/reports",
162
- "Metrics": "/metrics",
163
- "Projects": "/projects",
164
- "Tasks": "/tasks",
165
- "Teams": "/teams",
166
- "Workspaces": "/workspaces",
167
- "Workflows": "/workflows",
168
- "Templates": "/templates",
169
- "Logs": "/logs",
170
- "AuditLogs": "/audit-logs",
171
- "Integrations": "/integrations",
172
- "ApiDocs": "/api-docs",
173
- "Support": "/support",
174
- "Help": "/help",
175
- "Billing": "/billing",
176
- "Plans": "/plans",
177
- "Usage": "/usage",
178
- "Permissions": "/permissions",
179
- "Notifications": "/notifications"
180
- };
181
- if (friendlyToRoute[name]) {
182
- return friendlyToRoute[name];
183
- }
184
- const lowerName = name.toLowerCase();
185
- for (const [key, value] of Object.entries(friendlyToRoute)) {
186
- if (key.toLowerCase() === lowerName) {
187
- return value;
188
- }
189
- }
190
- if (name.startsWith("/")) {
191
- if (name === "/") {
192
- return "/";
193
- }
194
- return name.toLowerCase();
195
- }
196
- return "/" + lowerName;
197
- }
198
246
  /**
199
247
  * Normalize route paths
200
- * Converts dynamic segments to parameters and friendly names to route paths
248
+ * Converts dynamic segments to parameters and standardizes format
201
249
  */
202
250
  normalizeRoute(path2) {
203
251
  const buildArtifacts = [
@@ -211,7 +259,7 @@ var BigQueryAnalyticsConnector = class {
211
259
  if (buildArtifacts.some((pattern) => pattern.test(path2))) {
212
260
  return "";
213
261
  }
214
- let normalized = this.friendlyNameToRoute(path2);
262
+ let normalized = path2;
215
263
  normalized = normalized.split("?")[0].split("#")[0];
216
264
  if (normalized.length > 1 && normalized.endsWith("/")) {
217
265
  normalized = normalized.slice(0, -1);
@@ -268,28 +316,49 @@ var BigQueryAnalyticsConnector = class {
268
316
  }
269
317
  }
270
318
  /**
271
- * Fetch navigation data WITH segment information
272
- * Includes segment/role field so plugin can group by any role dynamically
273
- * @param config - Data range configuration
274
- * @returns Navigation data with segment field included
319
+ * Fetch navigation data WITH optional segment information
320
+ * Only includes segment/grouping field when explicitly requested
321
+ * @param config - Data range configuration with optional segmentField
322
+ * @returns Navigation data, optionally with segment field included
275
323
  */
276
324
  async fetchNavigationWithSegments(config = {}) {
277
- const { days = 30 } = config;
278
- console.log(`\u{1F4CA} Fetching navigation data with segments from BigQuery...`);
279
- console.log(` Dataset: ${this.datasetId}`);
325
+ const { days = 30, segmentField } = config;
326
+ if (!segmentField) {
327
+ console.log(`
328
+ ${"\u2550".repeat(60)}`);
329
+ console.log(`\u{1F4CA} STAGE 1: PREPARING BIGQUERY QUERY (SEGMENTED)`);
330
+ console.log(`${"\u2550".repeat(60)}`);
331
+ console.log(` \u26A0\uFE0F No segment field specified`);
332
+ console.log(` Falling back to fetchNavigationSequences (base data only)`);
333
+ console.log(` To include segments, pass: { segmentField: 'field_name' }`);
334
+ console.log(`${"\u2550".repeat(60)}
335
+ `);
336
+ return this.fetchNavigationSequences(config);
337
+ }
338
+ console.log(`
339
+ ${"\u2550".repeat(60)}`);
340
+ console.log(`\u{1F4CA} STAGE 1: PREPARING SEGMENTED BIGQUERY QUERY`);
341
+ console.log(`${"\u2550".repeat(60)}`);
342
+ console.log(` Project ID: ${this.projectId}`);
343
+ console.log(` Dataset ID: ${this.datasetId}`);
344
+ console.log(` Location: ${this.location}`);
280
345
  console.log(` Date range: Last ${days} days`);
346
+ console.log(` Daily tables pattern: events_*`);
347
+ console.log(` Event type filter: page_view`);
348
+ console.log(` Segment field: ${segmentField}`);
349
+ console.log(` Expected fields: user_pseudo_id, ga_session_id, page_path, ${segmentField}`);
281
350
  try {
351
+ const segmentParam = `(SELECT value.string_value FROM UNNEST(event_params) WHERE KEY = '${segmentField}' LIMIT 1) as segment_value`;
282
352
  const query = `
283
353
  WITH page_sessions AS (
284
354
  SELECT
285
- user_id,
355
+ user_pseudo_id,
286
356
  (SELECT value.int_value FROM UNNEST(event_params) WHERE KEY = 'ga_session_id' LIMIT 1) as session_id,
287
357
  (SELECT value.string_value FROM UNNEST(event_params) WHERE KEY = 'page_path' LIMIT 1) as page_path,
288
- (SELECT value.string_value FROM UNNEST(event_params) WHERE KEY = 'user_role' LIMIT 1) as user_role,
289
- (SELECT value.string_value FROM UNNEST(event_params) WHERE KEY = 'user_segment' LIMIT 1) as user_segment,
358
+ ${segmentParam},
290
359
  event_timestamp,
291
360
  ROW_NUMBER() OVER (
292
- PARTITION BY user_id, (SELECT value.int_value FROM UNNEST(event_params) WHERE KEY = 'ga_session_id' LIMIT 1)
361
+ PARTITION BY user_pseudo_id, (SELECT value.int_value FROM UNNEST(event_params) WHERE KEY = 'ga_session_id' LIMIT 1)
293
362
  ORDER BY event_timestamp
294
363
  ) as page_sequence
295
364
  FROM \`${this.projectId}.${this.datasetId}.events_*\`
@@ -303,10 +372,10 @@ var BigQueryAnalyticsConnector = class {
303
372
  SELECT
304
373
  COALESCE(curr.page_path, '(direct)') as from_page,
305
374
  LEAD(curr.page_path) OVER (
306
- PARTITION BY curr.user_id, curr.session_id
375
+ PARTITION BY curr.user_pseudo_id, curr.session_id
307
376
  ORDER BY curr.page_sequence
308
377
  ) as to_page,
309
- COALESCE(curr.user_role, curr.user_segment, 'unknown') as segment
378
+ COALESCE(curr.segment_value, 'unknown') as segment
310
379
  FROM page_sessions curr
311
380
  WHERE curr.page_sequence > 0
312
381
  )
@@ -317,45 +386,104 @@ var BigQueryAnalyticsConnector = class {
317
386
  COUNT(*) as transition_count
318
387
  FROM transitions
319
388
  WHERE to_page IS NOT NULL
320
- GROUP BY from_page, to_page, segment
389
+ GROUP BY previous_page_path, page_path, segment
321
390
  ORDER BY segment, transition_count DESC
322
391
  LIMIT 50000
323
392
  `;
324
393
  if (this.debug) {
325
- console.log(`\u{1F4E4} Query with segments:`);
394
+ console.log(`
395
+ \u{1F4E4} Full Query with segment field (${segmentField}):`);
396
+ console.log(`${"\u2500".repeat(60)}`);
326
397
  console.log(query);
398
+ console.log(`${"\u2500".repeat(60)}`);
327
399
  }
400
+ console.log(`
401
+ ${"\u2550".repeat(60)}`);
402
+ console.log(`\u{1F4CA} STAGE 2: EXECUTING QUERY`);
403
+ console.log(`${"\u2550".repeat(60)}`);
404
+ console.log(` \u23F3 Querying BigQuery with segment field: ${segmentField}`);
328
405
  const [rows] = await this.bigquery.query({
329
406
  query,
330
407
  location: this.location
331
408
  });
332
- if (this.debug) {
333
- console.log(`\u2705 Query executed successfully`);
334
- console.log(` Rows returned: ${rows.length}`);
409
+ console.log(` \u2705 Query executed successfully`);
410
+ console.log(` \u{1F4C8} Total transitions returned: ${rows.length}`);
411
+ if (rows.length === 0) {
412
+ console.log(`
413
+ ${"\u2550".repeat(60)}`);
414
+ console.log(`\u26A0\uFE0F STAGE 2: NO DATA RETURNED!`);
415
+ console.log(`${"\u2550".repeat(60)}`);
416
+ console.log(` \u274C BigQuery returned 0 rows for segmented query`);
417
+ console.log(` Segment field: ${segmentField}`);
418
+ console.log(` Check if this field exists in your GA4 event_params`);
419
+ console.log(`${"\u2550".repeat(60)}
420
+ `);
421
+ return [];
335
422
  }
423
+ console.log(`
424
+ ${"\u2550".repeat(60)}`);
425
+ console.log(`\u{1F4CA} STAGE 3: PROCESSING SEGMENTED DATA`);
426
+ console.log(`${"\u2550".repeat(60)}`);
427
+ console.log(` Processing ${rows.length} raw transitions with segments...`);
336
428
  const navigationData = [];
337
- rows.forEach((row) => {
429
+ let processedCount = 0;
430
+ let filteredCount = 0;
431
+ const segmentCounts = /* @__PURE__ */ new Map();
432
+ console.log(`
433
+ ${"\u2500".repeat(56)}`);
434
+ console.log(` Processing each transition row...`);
435
+ console.log(` ${"\u2500".repeat(56)}`);
436
+ rows.forEach((row, index) => {
338
437
  const previousPage = row.previous_page_path || "(direct)";
339
438
  const currentPage = row.page_path || "";
340
439
  const transitionCount = parseInt(row.transition_count || "0");
341
440
  const segment = row.segment || "unknown";
441
+ if (this.debug && index < 3) {
442
+ console.log(`
443
+ Row ${index + 1}:`);
444
+ console.log(` Raw: "${previousPage}" \u2192 "${currentPage}" | segment: "${segment}" (count: ${transitionCount})`);
445
+ }
342
446
  const normalizedPrevious = previousPage === "(direct)" || previousPage === "(not set)" ? "(direct)" : this.normalizeRoute(previousPage);
343
447
  const normalizedCurrent = this.normalizeRoute(currentPage);
448
+ if (this.debug && index < 3) {
449
+ console.log(` Normalized: "${normalizedPrevious}" \u2192 "${normalizedCurrent}" | segment: "${segment}"`);
450
+ }
344
451
  if (normalizedCurrent && transitionCount >= 1) {
345
452
  navigationData.push({
346
453
  from: normalizedPrevious,
347
454
  to: normalizedCurrent,
348
455
  count: transitionCount,
349
456
  segment
350
- // Include segment/role field
351
457
  });
458
+ processedCount++;
459
+ segmentCounts.set(segment, (segmentCounts.get(segment) || 0) + 1);
460
+ if (this.debug && index < 3) {
461
+ console.log(` \u2705 ACCEPTED`);
462
+ }
463
+ } else {
464
+ filteredCount++;
465
+ if (this.debug && index < 3) {
466
+ console.log(` \u274C FILTERED`);
467
+ }
352
468
  }
353
469
  });
354
- if (this.debug && navigationData.length > 0) {
355
- console.log(`\u2705 Processed ${navigationData.length} transitions with segment data`);
356
- const uniqueSegments = new Set(navigationData.map((d) => d.segment));
470
+ console.log(`
471
+ ${"\u2500".repeat(56)}`);
472
+ console.log(` Processing Summary:`);
473
+ console.log(` \u2705 Accepted: ${processedCount}`);
474
+ console.log(` \u274C Filtered out: ${filteredCount}`);
475
+ console.log(`
476
+ ${"\u2550".repeat(60)}`);
477
+ console.log(`\u{1F4CA} STAGE 4: FINAL RESULTS (SEGMENTED)`);
478
+ console.log(`${"\u2550".repeat(60)}`);
479
+ console.log(` Total valid transitions: ${navigationData.length}`);
480
+ if (navigationData.length > 0) {
481
+ const uniqueSegments = Array.from(segmentCounts.keys()).sort();
357
482
  console.log(`
358
- Detected segments: ${Array.from(uniqueSegments).sort().join(", ")}`);
483
+ \u{1F4CA} Detected segments: ${uniqueSegments.length}`);
484
+ uniqueSegments.forEach((seg) => {
485
+ console.log(` \u2022 ${seg} (${segmentCounts.get(seg)} transitions)`);
486
+ });
359
487
  const bySegment = /* @__PURE__ */ new Map();
360
488
  navigationData.forEach((d) => {
361
489
  if (!bySegment.has(d.segment)) {
@@ -365,42 +493,31 @@ var BigQueryAnalyticsConnector = class {
365
493
  });
366
494
  bySegment.forEach((transitions, segment) => {
367
495
  console.log(`
368
- Top 3 transitions for ${segment}:`);
496
+ Top 3 transitions for segment "${segment}":`);
369
497
  transitions.sort((a, b) => b.count - a.count).slice(0, 3).forEach((nav, i) => {
370
- console.log(` ${i + 1}. ${nav.from} \u2192 ${nav.to} (${nav.count} transitions)`);
498
+ console.log(` ${i + 1}. ${nav.from} \u2192 ${nav.to} (${nav.count})`);
371
499
  });
372
500
  });
501
+ } else {
502
+ console.log(` \u26A0\uFE0F WARNING: All transitions were filtered out!`);
373
503
  }
504
+ console.log(`${"\u2550".repeat(60)}
505
+ `);
374
506
  return navigationData;
375
507
  } catch (error) {
376
508
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
509
+ console.log(`
510
+ ${"\u2550".repeat(60)}`);
511
+ console.log(`\u274C ERROR: SEGMENTED QUERY EXECUTION FAILED`);
512
+ console.log(`${"\u2550".repeat(60)}`);
513
+ console.log(` Segment field: ${segmentField}`);
514
+ console.log(` Error: ${errorMessage}`);
515
+ console.log(`${"\u2550".repeat(60)}
516
+ `);
377
517
  console.warn(`\u26A0\uFE0F Failed to fetch navigation data with segments: ${errorMessage}`);
378
518
  return [];
379
519
  }
380
520
  }
381
- /**
382
- * Test connection to BigQuery
383
- * Note: May fail if service account lacks bigquery.jobs.create permission
384
- * but actual queries may still work. This is expected for viewer-only accounts.
385
- */
386
- async testConnection() {
387
- try {
388
- const query = `SELECT 1 as test_value`;
389
- await this.bigquery.query({
390
- query,
391
- location: this.location
392
- });
393
- if (this.debug) {
394
- console.log("\u2705 BigQuery connection test successful");
395
- }
396
- return true;
397
- } catch (error) {
398
- if (this.debug) {
399
- console.warn("\u26A0\uFE0F BigQuery connection test failed (may be expected for read-only accounts)");
400
- }
401
- return false;
402
- }
403
- }
404
521
  };
405
522
 
406
523
  // src/plugin/model/guessjs-ml-trainer.ts
@@ -1818,6 +1935,7 @@ function generateDashboard(config) {
1818
1935
  </html>`;
1819
1936
  }
1820
1937
  export {
1938
+ BigQueryAnalyticsConnector,
1821
1939
  smartPrefetch
1822
1940
  };
1823
1941
  //# sourceMappingURL=index.js.map