vite-plugin-smart-prefetch 0.4.0 → 0.4.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/dist/index.d.ts CHANGED
@@ -117,17 +117,11 @@ interface DataSourceMetadata {
117
117
  interface RoutePrediction {
118
118
  /** Route pattern variants (for matching dynamic routes like /order/:id) */
119
119
  patterns?: string[];
120
- /** Routes to prefetch from this source (default/common rules) */
120
+ /** Routes to prefetch from this source */
121
121
  prefetch: PrefetchTarget[];
122
- /** Segment-specific prefetch rules */
123
- segments?: SegmentPrefetchRules;
124
122
  /** Additional metadata about this route */
125
123
  metadata?: RouteMetadata;
126
124
  }
127
- interface SegmentPrefetchRules {
128
- /** Prefetch rules by user segment (free, premium, enterprise, etc.) */
129
- [segmentName: string]: PrefetchTarget[];
130
- }
131
125
  interface PrefetchTarget {
132
126
  /** Target route to prefetch */
133
127
  route: string;
@@ -177,16 +171,10 @@ interface PrefetchConfig {
177
171
  [sourceRoute: string]: {
178
172
  /** Route pattern variants (for matching dynamic routes like /order/:id) */
179
173
  patterns?: string[];
180
- /** Default/common prefetch rules (works for all users) */
174
+ /** Prefetch rules for this route */
181
175
  prefetch: Array<PrefetchTarget & {
182
176
  chunk: string;
183
177
  }>;
184
- /** Segment-specific rules (different per user segment) */
185
- segments?: {
186
- [segmentName: string]: Array<PrefetchTarget & {
187
- chunk: string;
188
- }>;
189
- };
190
178
  /** Metadata about this route */
191
179
  metadata?: RouteMetadata;
192
180
  };
@@ -195,11 +183,6 @@ interface PrefetchConfig {
195
183
  chunks: {
196
184
  [route: string]: string;
197
185
  };
198
- /** Available segments (for client-side selection) */
199
- segmentInfo?: {
200
- available: string[];
201
- description?: string;
202
- };
203
186
  }
204
187
  interface ViteManifest {
205
188
  [key: string]: {
package/dist/index.js CHANGED
@@ -537,7 +537,7 @@ ${"\u2550".repeat(60)}`);
537
537
  }
538
538
  };
539
539
 
540
- // src/plugin/model/guessjs-ml-trainer.ts
540
+ // src/plugin/model/prefetch-ml-trainer.ts
541
541
  var MarkovChainTrainer = class {
542
542
  constructor(config, debug = false) {
543
543
  this.config = config;
@@ -717,57 +717,6 @@ var MarkovChainTrainer = class {
717
717
  topDestination
718
718
  };
719
719
  }
720
- /**
721
- * Train segment-specific Markov models from navigation data with segment field
722
- * Creates separate models for each user segment/role
723
- */
724
- trainSegmentedModels(navigationDataWithSegments, environment) {
725
- if (this.debug) {
726
- console.log(`
727
- \u{1F916} Training Segment-Specific Markov Models...`);
728
- }
729
- const dataBySegment = /* @__PURE__ */ new Map();
730
- navigationDataWithSegments.forEach((data) => {
731
- const segment = data.segment || "default";
732
- if (!dataBySegment.has(segment)) {
733
- dataBySegment.set(segment, []);
734
- }
735
- dataBySegment.get(segment).push({
736
- from: data.from,
737
- to: data.to,
738
- count: data.count
739
- });
740
- });
741
- if (this.debug) {
742
- console.log(`
743
- \u{1F4CA} Detected segments:`);
744
- dataBySegment.forEach((data, segment) => {
745
- console.log(` \u2022 ${segment}: ${data.length} transitions`);
746
- });
747
- }
748
- const segmentModels = /* @__PURE__ */ new Map();
749
- dataBySegment.forEach((navigationData, segment) => {
750
- if (this.debug) {
751
- console.log(`
752
- \u{1F504} Training model for segment: "${segment}"`);
753
- }
754
- const model = this.trainMLModel(navigationData, environment);
755
- model.dataSource.provider = `markov-chain[${segment}]`;
756
- segmentModels.set(segment, model);
757
- if (this.debug) {
758
- const totalTargets = Object.values(model.routes).reduce(
759
- (sum, route) => sum + (route.prefetch?.length || 0),
760
- 0
761
- );
762
- console.log(` \u2705 Model for "${segment}" trained with ${totalTargets} prefetch targets`);
763
- }
764
- });
765
- if (this.debug) {
766
- console.log(`
767
- \u2705 All ${segmentModels.size} segment models trained successfully`);
768
- }
769
- return segmentModels;
770
- }
771
720
  /**
772
721
  * Get date range string
773
722
  */
@@ -800,7 +749,6 @@ var ConfigGenerator = class {
800
749
  }
801
750
  /**
802
751
  * Generate final prefetch configuration with chunk mappings
803
- * Includes common prefetch rules and segment-specific rules if available
804
752
  */
805
753
  generate(model) {
806
754
  if (this.debug) {
@@ -809,9 +757,6 @@ var ConfigGenerator = class {
809
757
  console.log(` Manifest entries: ${Object.keys(this.manifest).length}`);
810
758
  console.log(` Model routes: ${Object.keys(model.routes).length}`);
811
759
  console.log(` Manual rules: ${Object.keys(this.manualRules).length}`);
812
- if (Object.values(model.routes).some((r) => r.segments)) {
813
- console.log(` \u2139\uFE0F Segment-based rules detected`);
814
- }
815
760
  }
816
761
  const mergedModel = this.mergeManualRules(model);
817
762
  const routePatterns = this.extractRoutePatterns(Object.keys(mergedModel.routes));
@@ -832,10 +777,8 @@ var ConfigGenerator = class {
832
777
  };
833
778
  let mappedRoutes = 0;
834
779
  let unmappedRoutes = 0;
835
- let totalSegmentRules = 0;
836
780
  Object.entries(mergedModel.routes).forEach(([sourceRoute, prediction]) => {
837
781
  const prefetchTargets = [];
838
- const segmentConfigs = {};
839
782
  const sourceChunk = this.routeToChunk(sourceRoute);
840
783
  if (sourceChunk && !config.chunks[sourceRoute]) {
841
784
  config.chunks[sourceRoute] = sourceChunk;
@@ -874,46 +817,12 @@ var ConfigGenerator = class {
874
817
  unmappedRoutes++;
875
818
  }
876
819
  });
877
- if (prediction.segments) {
878
- Object.entries(prediction.segments).forEach(([segment, segmentTargets]) => {
879
- const segmentPrefetchTargets = [];
880
- segmentTargets.forEach((target) => {
881
- const chunkFile = this.routeToChunk(target.route);
882
- if (chunkFile) {
883
- const manifestEntry = this.getManifestEntryByFile(chunkFile);
884
- const importChunkIds = manifestEntry?.imports || [];
885
- const imports = importChunkIds.map((chunkId) => {
886
- const entry = this.manifest[chunkId];
887
- return entry?.file;
888
- }).filter((file) => !!file);
889
- segmentPrefetchTargets.push({
890
- ...target,
891
- chunk: chunkFile,
892
- chunk_prod: chunkFile,
893
- // NEW: Include production chunk path
894
- imports
895
- });
896
- config.chunks[target.route] = chunkFile;
897
- totalSegmentRules++;
898
- }
899
- });
900
- if (segmentPrefetchTargets.length > 0) {
901
- segmentConfigs[segment] = segmentPrefetchTargets;
902
- }
903
- });
904
- if (this.debug && Object.keys(segmentConfigs).length > 0) {
905
- console.log(` \u{1F465} Segment configs for ${sourceRoute}: ${Object.keys(segmentConfigs).join(", ")}`);
906
- }
907
- }
908
820
  const patterns = routePatterns[sourceRoute] || [sourceRoute];
909
- if (prefetchTargets.length > 0 || Object.keys(segmentConfigs).length > 0) {
821
+ if (prefetchTargets.length > 0) {
910
822
  config.routes[sourceRoute] = {
911
823
  patterns,
912
- // NEW: Include patterns for dynamic route matching
824
+ // Include patterns for dynamic route matching
913
825
  prefetch: prefetchTargets,
914
- ...Object.keys(segmentConfigs).length > 0 && {
915
- segments: segmentConfigs
916
- },
917
826
  metadata: prediction.metadata
918
827
  };
919
828
  }
@@ -922,21 +831,8 @@ var ConfigGenerator = class {
922
831
  console.log(`\u2705 Configuration generated`);
923
832
  console.log(` Routes with prefetch: ${Object.keys(config.routes).length}`);
924
833
  console.log(` Mapped chunks: ${mappedRoutes}`);
925
- console.log(` Segment-specific rules: ${totalSegmentRules}`);
926
834
  console.log(` Unmapped routes: ${unmappedRoutes}`);
927
835
  }
928
- const allSegments = /* @__PURE__ */ new Set();
929
- Object.values(config.routes).forEach((route) => {
930
- if (route.segments) {
931
- Object.keys(route.segments).forEach((seg) => allSegments.add(seg));
932
- }
933
- });
934
- if (allSegments.size > 0) {
935
- config.segmentInfo = {
936
- available: Array.from(allSegments),
937
- description: "Load segment-specific config based on user role/segment"
938
- };
939
- }
940
836
  return config;
941
837
  }
942
838
  /**
@@ -1130,32 +1026,6 @@ var ConfigGenerator = class {
1130
1026
  }
1131
1027
  return null;
1132
1028
  }
1133
- /**
1134
- * Generate segment-specific prefetch configurations
1135
- * Creates one config per user segment/role
1136
- */
1137
- generateSegmentConfigs(segmentModels) {
1138
- const segmentConfigs = /* @__PURE__ */ new Map();
1139
- segmentModels.forEach((model, segment) => {
1140
- if (this.debug) {
1141
- console.log(`
1142
- \u{1F4E6} Generating config for segment: "${segment}"`);
1143
- }
1144
- const config = this.generate(model);
1145
- config.segment = segment;
1146
- segmentConfigs.set(segment, config);
1147
- if (this.debug) {
1148
- console.log(` \u2705 Config generated for "${segment}"`);
1149
- console.log(` Routes: ${Object.keys(config.routes).length}`);
1150
- console.log(` Chunks: ${Object.keys(config.chunks).length}`);
1151
- }
1152
- });
1153
- if (this.debug) {
1154
- console.log(`
1155
- \u2705 Generated ${segmentConfigs.size} segment-specific configurations`);
1156
- }
1157
- return segmentConfigs;
1158
- }
1159
1029
  };
1160
1030
 
1161
1031
  // src/plugin/cache-manager.ts
@@ -1331,7 +1201,6 @@ function smartPrefetch(options = {}) {
1331
1201
  let config;
1332
1202
  let cacheManager;
1333
1203
  let prefetchModel = null;
1334
- let segmentModels = null;
1335
1204
  return {
1336
1205
  name: "@farmart/vite-plugin-smart-prefetch",
1337
1206
  enforce: "post",
@@ -1412,9 +1281,6 @@ function smartPrefetch(options = {}) {
1412
1281
  res.setHeader("Access-Control-Allow-Origin", "*");
1413
1282
  res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
1414
1283
  res.end(JSON.stringify(finalConfig, null, 2));
1415
- if (debug) {
1416
- console.log("\u{1F4E4} Serving fresh prefetch-config.json from development server");
1417
- }
1418
1284
  return;
1419
1285
  } catch (error) {
1420
1286
  if (debug) {
@@ -1452,37 +1318,30 @@ function smartPrefetch(options = {}) {
1452
1318
  debug,
1453
1319
  analytics.credentials.keyFilePath
1454
1320
  );
1455
- console.log("\n\u{1F3AF} Using real navigation data from BigQuery GA4 export...");
1321
+ if (debug) {
1322
+ console.log("\n\u{1F3AF} Using real navigation data from BigQuery GA4 export...");
1323
+ }
1456
1324
  const navigationData = await bqConnector.fetchNavigationSequences(analytics.dataRange);
1457
1325
  if (navigationData.length === 0) {
1458
1326
  console.warn("\u26A0\uFE0F No navigation data found, using manual rules only");
1459
1327
  prefetchModel = createManualModel(manualRules, environment);
1460
1328
  return;
1461
1329
  }
1462
- console.log(`
1330
+ if (debug) {
1331
+ console.log(`
1463
1332
  \u{1F916} Training model using Markov Chain ML...`);
1333
+ }
1464
1334
  const mlTrainer = new MarkovChainTrainer(analytics.model, debug);
1465
1335
  const prefetchModelResult = mlTrainer.trainMLModel(navigationData, environment);
1466
1336
  prefetchModel = prefetchModelResult;
1467
1337
  await cacheManager.set(environment, prefetchModel);
1468
- try {
1469
- const navigationWithSegments = await bqConnector.fetchNavigationWithSegments(analytics.dataRange);
1470
- if (navigationWithSegments.length > 0) {
1471
- console.log(`
1472
- \u{1F465} Training segment-specific models...`);
1473
- segmentModels = mlTrainer.trainSegmentedModels(navigationWithSegments, environment);
1474
- if (debug && segmentModels.size > 0) {
1475
- console.log(` \u2705 Trained ${segmentModels.size} segment-specific models`);
1476
- }
1477
- }
1478
- } catch (error) {
1479
- if (debug) {
1480
- console.warn(`\u26A0\uFE0F Could not train segment models:`, error instanceof Error ? error.message : "Unknown error");
1481
- }
1482
- }
1483
1338
  } catch (error) {
1484
- console.error("\u274C Failed to fetch analytics data:", error);
1485
- console.log("\u26A0\uFE0F Falling back to manual rules only");
1339
+ if (debug) {
1340
+ console.error("\u274C Failed to fetch analytics data:", error);
1341
+ }
1342
+ if (debug) {
1343
+ console.log("\u26A0\uFE0F Falling back to manual rules only");
1344
+ }
1486
1345
  prefetchModel = createManualModel(manualRules, environment);
1487
1346
  if (debug) {
1488
1347
  console.error("Error details:", error);
@@ -1491,7 +1350,9 @@ function smartPrefetch(options = {}) {
1491
1350
  },
1492
1351
  async writeBundle(outputOptions) {
1493
1352
  if (!prefetchModel) {
1494
- console.warn("\u26A0\uFE0F No prefetch model available, skipping config generation");
1353
+ if (debug) {
1354
+ console.warn("\u26A0\uFE0F No prefetch model available, skipping config generation");
1355
+ }
1495
1356
  return;
1496
1357
  }
1497
1358
  const fs2 = await import("fs");
@@ -1499,7 +1360,9 @@ function smartPrefetch(options = {}) {
1499
1360
  const outDir = outputOptions.dir || "dist";
1500
1361
  const manifestPath = path2.join(outDir, ".vite", "manifest.json");
1501
1362
  if (!fs2.existsSync(manifestPath)) {
1502
- console.warn("\u26A0\uFE0F Vite manifest not found at:", manifestPath);
1363
+ if (debug) {
1364
+ console.warn("\u26A0\uFE0F Vite manifest not found at:", manifestPath);
1365
+ }
1503
1366
  return;
1504
1367
  }
1505
1368
  const manifestContent = fs2.readFileSync(manifestPath, "utf-8");
@@ -1511,10 +1374,10 @@ function smartPrefetch(options = {}) {
1511
1374
  const generator = new ConfigGenerator(manifest, manualRules, debug);
1512
1375
  const finalConfig = generator.generate(prefetchModel);
1513
1376
  const validation = generator.validateChunks(finalConfig);
1514
- if (!validation.valid) {
1377
+ if (!validation.valid && debug) {
1515
1378
  console.warn("\u26A0\uFE0F Some chunks could not be mapped:");
1516
1379
  validation.missing.forEach((m) => console.warn(` ${m}`));
1517
- } else {
1380
+ } else if (debug) {
1518
1381
  console.log(`\u2705 All ${Object.keys(finalConfig.chunks).length} chunks successfully mapped with real build hashes`);
1519
1382
  }
1520
1383
  const configPath = path2.join(outDir, "prefetch-config.json");
@@ -1527,37 +1390,6 @@ function smartPrefetch(options = {}) {
1527
1390
  console.log(`\u{1F4CA} Total routes configured: ${Object.keys(finalConfig.routes).length}`);
1528
1391
  console.log(`\u{1F4E6} Total chunks: ${Object.keys(finalConfig.chunks).length}`);
1529
1392
  }
1530
- if (segmentModels && segmentModels.size > 0) {
1531
- console.log(`
1532
- \u{1F4C1} Generating segment-specific prefetch configurations...`);
1533
- const segmentGenerator = new ConfigGenerator(manifest, manualRules, debug);
1534
- const segmentConfigs = segmentGenerator.generateSegmentConfigs(segmentModels);
1535
- const segmentDir = path2.join(outDir, "prefetch-configs");
1536
- if (!fs2.existsSync(segmentDir)) {
1537
- fs2.mkdirSync(segmentDir, { recursive: true });
1538
- }
1539
- segmentConfigs.forEach((segConfig, segment) => {
1540
- const segmentPath = path2.join(segmentDir, `${segment}.json`);
1541
- fs2.writeFileSync(segmentPath, JSON.stringify(segConfig, null, 2));
1542
- if (debug) {
1543
- console.log(` \u2705 Emitted ${segment}.json (${Object.keys(segConfig.routes).length} routes)`);
1544
- }
1545
- });
1546
- const segmentIndex = {
1547
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1548
- environment: analytics?.environment || (config.command === "serve" ? "development" : "production"),
1549
- segments: Array.from(segmentConfigs.keys()),
1550
- description: "Segment-specific prefetch configurations. Use based on user role/segment."
1551
- };
1552
- const segmentIndexPath = path2.join(segmentDir, "index.json");
1553
- fs2.writeFileSync(segmentIndexPath, JSON.stringify(segmentIndex, null, 2));
1554
- if (debug) {
1555
- console.log(`
1556
- \u2705 Emitted ${segmentConfigs.size} segment-specific configurations`);
1557
- console.log(` Location: ${segmentDir}`);
1558
- console.log(` Segments: ${Array.from(segmentConfigs.keys()).join(", ")}`);
1559
- }
1560
- }
1561
1393
  if (analytics?.dashboard) {
1562
1394
  const dashboardHtml = generateDashboard(finalConfig);
1563
1395
  const dashboardPath = path2.join(outDir, "prefetch-report.html");