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.cjs +23 -191
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -19
- package/dist/index.d.ts +2 -19
- package/dist/index.js +23 -191
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +70 -211
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +112 -127
- package/dist/react/index.d.ts +112 -127
- package/dist/react/index.js +55 -181
- package/dist/react/index.js.map +1 -1
- package/dist/runtime/index.cjs +35 -35
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +30 -25
- package/dist/runtime/index.d.ts +30 -25
- package/dist/runtime/index.js +35 -35
- package/dist/runtime/index.js.map +1 -1
- package/package.json +4 -8
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
|
|
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
|
-
/**
|
|
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/
|
|
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
|
|
821
|
+
if (prefetchTargets.length > 0) {
|
|
910
822
|
config.routes[sourceRoute] = {
|
|
911
823
|
patterns,
|
|
912
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1485
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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");
|