hyperinstant 0.1.0 → 1.0.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/LICENSE +2 -18
- package/README.md +17 -15
- package/dist/hyperinstant.js +389 -0
- package/dist/hyperinstant.min.js +1 -2
- package/package.json +13 -19
- package/dist/hyperinstant.esm.js +0 -2
- package/dist/hyperinstant.esm.js.map +0 -7
- package/dist/hyperinstant.min.js.map +0 -7
package/LICENSE
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 Putia Web
|
|
4
4
|
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy...
|
package/README.md
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# HyperInstant v1.0.2
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
v1.0.2 focuses on **real-world speed**:
|
|
4
|
+
- **Prerender only on high-confidence intent** (mousedown/touchstart), not on hover
|
|
5
|
+
- **Lower overhead scheduler** (capped queue + dedupe; no heavy sorting)
|
|
6
|
+
- **Safer auto-deny** (less over-blocking)
|
|
7
|
+
- **Optional auto-tuning** (OFF by default)
|
|
4
8
|
|
|
5
|
-
##
|
|
6
|
-
```bash
|
|
7
|
-
npm install
|
|
8
|
-
npm run build
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## CDN usage (unpkg)
|
|
9
|
+
## CDN
|
|
12
10
|
```html
|
|
13
11
|
<script>
|
|
14
12
|
window.HyperInstant = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
maxPrefetch: 6,
|
|
18
|
-
maxPrerender: 2,
|
|
19
|
-
deny: ["/cart", "/checkout", "/my-account", "/logout", "add-to-cart"],
|
|
13
|
+
mode: "balanced",
|
|
14
|
+
adapter: "auto",
|
|
20
15
|
debug: false
|
|
21
16
|
};
|
|
22
17
|
</script>
|
|
18
|
+
<script src="https://unpkg.com/hyperinstant@1.0.2/dist/hyperinstant.min.js" defer></script>
|
|
19
|
+
```
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
## “Very fast” preset (nopCommerce)
|
|
22
|
+
```js
|
|
23
|
+
mode: "aggressive",
|
|
24
|
+
adapter: "nopcommerce",
|
|
25
|
+
hoverDelay: 35,
|
|
26
|
+
enablePrerender: false
|
|
25
27
|
```
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/*! HyperInstant v1.0.2 | MIT | Putia Web */
|
|
2
|
+
(function () {
|
|
3
|
+
if (window.__HYPERINSTANT_LOADED__) return;
|
|
4
|
+
window.__HYPERINSTANT_LOADED__ = true;
|
|
5
|
+
|
|
6
|
+
var userCfg = window.HyperInstant || {};
|
|
7
|
+
var VERSION = "1.0.2";
|
|
8
|
+
|
|
9
|
+
function clamp(n, a, b) { return Math.max(a, Math.min(b, n)); }
|
|
10
|
+
function isSameOrigin(url) {
|
|
11
|
+
try { return new URL(url, location.href).origin === location.origin; }
|
|
12
|
+
catch (e) { return false; }
|
|
13
|
+
}
|
|
14
|
+
function normalizeUrl(url) {
|
|
15
|
+
try { var u = new URL(url, location.href); u.hash = ""; return u.toString(); }
|
|
16
|
+
catch (e) { return ""; }
|
|
17
|
+
}
|
|
18
|
+
function supportsPrerender() {
|
|
19
|
+
try {
|
|
20
|
+
return "relList" in HTMLLinkElement.prototype &&
|
|
21
|
+
HTMLLinkElement.prototype.relList &&
|
|
22
|
+
HTMLLinkElement.prototype.relList.supports &&
|
|
23
|
+
HTMLLinkElement.prototype.relList.supports("prerender");
|
|
24
|
+
} catch (e) { return false; }
|
|
25
|
+
}
|
|
26
|
+
function getConn() {
|
|
27
|
+
var c = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
|
28
|
+
return {
|
|
29
|
+
saveData: !!(c && c.saveData),
|
|
30
|
+
effectiveType: (c && c.effectiveType) ? String(c.effectiveType) : "unknown",
|
|
31
|
+
downlink: (c && typeof c.downlink === "number") ? c.downlink : null,
|
|
32
|
+
rtt: (c && typeof c.rtt === "number") ? c.rtt : null
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function inNav(a) {
|
|
36
|
+
try { return !!a.closest("nav, header, [role='navigation'], .header, .site-header"); }
|
|
37
|
+
catch (e) { return false; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function applyModeDefaults(cfg) {
|
|
41
|
+
var mode = String(cfg.mode || "safe").toLowerCase();
|
|
42
|
+
var out = Object.assign({}, cfg);
|
|
43
|
+
|
|
44
|
+
// v1.0.2: prerender is intentionally conservative to avoid slowing pages
|
|
45
|
+
if (mode === "aggressive") {
|
|
46
|
+
out.maxPrefetch = out.maxPrefetch ?? 10;
|
|
47
|
+
out.maxPrerender = out.maxPrerender ?? 1;
|
|
48
|
+
out.hoverDelay = out.hoverDelay ?? 35;
|
|
49
|
+
} else if (mode === "balanced") {
|
|
50
|
+
out.maxPrefetch = out.maxPrefetch ?? 6;
|
|
51
|
+
out.maxPrerender = out.maxPrerender ?? 0;
|
|
52
|
+
out.hoverDelay = out.hoverDelay ?? 65;
|
|
53
|
+
} else {
|
|
54
|
+
out.maxPrefetch = out.maxPrefetch ?? 4;
|
|
55
|
+
out.maxPrerender = out.maxPrerender ?? 0;
|
|
56
|
+
out.hoverDelay = out.hoverDelay ?? 75;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
out.adapter = out.adapter ?? "auto";
|
|
60
|
+
out.observe = out.observe ?? "intent";
|
|
61
|
+
out.events = out.events ?? ["mouseover", "touchstart", "mousedown", "focusin"];
|
|
62
|
+
out.deny = out.deny ?? [];
|
|
63
|
+
out.debug = !!out.debug;
|
|
64
|
+
out.debugOverlay = !!out.debugOverlay;
|
|
65
|
+
|
|
66
|
+
// new in 1.0.2
|
|
67
|
+
out.enablePrerender = (out.enablePrerender === undefined) ? true : !!out.enablePrerender;
|
|
68
|
+
out.autoTune = !!out.autoTune; // OFF by default
|
|
69
|
+
out.maxQueue = clamp(out.maxQueue ?? 60, 10, 200);
|
|
70
|
+
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function hasMeta(name) { return !!document.querySelector('meta[name="' + name + '"]'); }
|
|
75
|
+
|
|
76
|
+
function shopifyAdapter(cfg) {
|
|
77
|
+
var deny = new Set([].concat(cfg.deny || []));
|
|
78
|
+
["/cart", "/checkout", "/account", "/account/login", "/account/register", "/account/logout", "/search",
|
|
79
|
+
"cart/add", "cart/change", "cart/update", "variant=", "sections="].forEach(function (x) { deny.add(x); });
|
|
80
|
+
return Object.assign({}, cfg, { deny: Array.from(deny) });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function wooAdapter(cfg) {
|
|
84
|
+
var deny = new Set([].concat(cfg.deny || []));
|
|
85
|
+
["/cart", "/checkout", "/my-account", "add-to-cart", "wc-ajax", "remove_item", "update_cart", "apply_coupon"].forEach(function (x) { deny.add(x); });
|
|
86
|
+
return Object.assign({}, cfg, { deny: Array.from(deny) });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function nopAdapter(cfg) {
|
|
90
|
+
var deny = new Set([].concat(cfg.deny || []));
|
|
91
|
+
["/shoppingcart", "/checkout", "/login", "/register", "/logout", "/customer", "/wishlist", "/compareproducts",
|
|
92
|
+
"/search", "?q=", "addproducttocart", "updateshoppingcart", "updatecart", "deleteitem", "returnurl=", "token=", "ajax", "flyout"].forEach(function (x) { deny.add(x); });
|
|
93
|
+
return Object.assign({}, cfg, { deny: Array.from(deny) });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function detectAdapter(name) {
|
|
97
|
+
name = String(name || "auto").toLowerCase();
|
|
98
|
+
if (name === "shopify") return shopifyAdapter;
|
|
99
|
+
if (name === "woocommerce") return wooAdapter;
|
|
100
|
+
if (name === "nopcommerce") return nopAdapter;
|
|
101
|
+
if (name === "generic") return function (c) { return c; };
|
|
102
|
+
|
|
103
|
+
try { if (window.Shopify || hasMeta("shopify-digital-wallet") || hasMeta("shopify-checkout-api-token")) return shopifyAdapter; } catch (e) { }
|
|
104
|
+
try { if (window.wc_add_to_cart_params || window.wc_cart_fragments_params || (document.body && String(document.body.className || "").includes("woocommerce"))) return wooAdapter; } catch (e) { }
|
|
105
|
+
try { if (window.nopCommerce || document.querySelector("a[href*='shoppingcart']") || document.querySelector("form[action*='addproducttocart']")) return nopAdapter; } catch (e) { }
|
|
106
|
+
return function (c) { return c; };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Safer auto-deny (avoid broad keywords like "order" that can hit benign pages)
|
|
110
|
+
var AUTO_PATH_PREFIX = ["/checkout", "/cart", "/shoppingcart", "/account", "/my-account", "/logout", "/login", "/register"];
|
|
111
|
+
var AUTO_PARAMS = ["add", "remove", "update", "change", "delete", "token", "variant", "sections", "returnurl", "redirect"];
|
|
112
|
+
var AUTO_KEYWORDS = ["add-to-cart", "addtocart", "addproducttocart", "wc-ajax", "admin-ajax", "sections=", "variant="];
|
|
113
|
+
|
|
114
|
+
function shouldDeny(url, cfg) {
|
|
115
|
+
if (!url) return true;
|
|
116
|
+
if (!isSameOrigin(url)) return true;
|
|
117
|
+
|
|
118
|
+
var u = String(url).toLowerCase();
|
|
119
|
+
|
|
120
|
+
var denyList = cfg.deny || [];
|
|
121
|
+
for (var i = 0; i < denyList.length; i++) {
|
|
122
|
+
var r = denyList[i];
|
|
123
|
+
if (r && u.includes(String(r).toLowerCase())) return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
var p = new URL(url, location.href).pathname.toLowerCase();
|
|
128
|
+
for (var j = 0; j < AUTO_PATH_PREFIX.length; j++) {
|
|
129
|
+
var ap = AUTO_PATH_PREFIX[j];
|
|
130
|
+
if (p === ap || p.startsWith(ap + "/")) return true;
|
|
131
|
+
}
|
|
132
|
+
} catch (e) { }
|
|
133
|
+
|
|
134
|
+
for (var k = 0; k < AUTO_KEYWORDS.length; k++) if (u.includes(AUTO_KEYWORDS[k])) return true;
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
var uu = new URL(url, location.href);
|
|
138
|
+
var sp = uu.searchParams;
|
|
139
|
+
for (var q = 0; q < AUTO_PARAMS.length; q++) if (sp.has(AUTO_PARAMS[q])) return true;
|
|
140
|
+
} catch (e) { }
|
|
141
|
+
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function Stats() {
|
|
146
|
+
this.startedAt = Date.now();
|
|
147
|
+
this.seen = 0;
|
|
148
|
+
this.denied = 0;
|
|
149
|
+
this.enqueued = 0;
|
|
150
|
+
this.prefetched = 0;
|
|
151
|
+
this.prerendered = 0;
|
|
152
|
+
this.clicked = 0;
|
|
153
|
+
this.hit = 0;
|
|
154
|
+
this._loaded = new Set();
|
|
155
|
+
}
|
|
156
|
+
Stats.prototype.isLoaded = function (url) { return this._loaded.has(url); };
|
|
157
|
+
Stats.prototype.markLoaded = function (url, type) {
|
|
158
|
+
if (this._loaded.has(url)) return;
|
|
159
|
+
this._loaded.add(url);
|
|
160
|
+
if (type === "prerender") this.prerendered++; else this.prefetched++;
|
|
161
|
+
};
|
|
162
|
+
Stats.prototype.markClick = function (url) { this.clicked++; if (this._loaded.has(url)) this.hit++; };
|
|
163
|
+
Object.defineProperty(Stats.prototype, "hitRate", { get: function () { var d = this.prefetched + this.prerendered; return d ? (this.hit / d) : 0; } });
|
|
164
|
+
Stats.prototype.snapshot = function () {
|
|
165
|
+
return {
|
|
166
|
+
version: VERSION,
|
|
167
|
+
uptimeSec: Math.round((Date.now() - this.startedAt) / 1000),
|
|
168
|
+
seen: this.seen,
|
|
169
|
+
denied: this.denied,
|
|
170
|
+
enqueued: this.enqueued,
|
|
171
|
+
prefetched: this.prefetched,
|
|
172
|
+
prerendered: this.prerendered,
|
|
173
|
+
clicked: this.clicked,
|
|
174
|
+
hit: this.hit,
|
|
175
|
+
hitRate: Number(this.hitRate.toFixed(3))
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
function createScheduler(cfg, stats) {
|
|
180
|
+
var q = [];
|
|
181
|
+
var qSet = new Set();
|
|
182
|
+
var inFlight = new Set();
|
|
183
|
+
var usedPrefetch = 0;
|
|
184
|
+
var usedPrerender = 0;
|
|
185
|
+
var prerenderSupported = supportsPrerender();
|
|
186
|
+
|
|
187
|
+
function budgets() {
|
|
188
|
+
var mp = cfg.maxPrefetch;
|
|
189
|
+
var mr = cfg.maxPrerender;
|
|
190
|
+
|
|
191
|
+
if (!cfg.enablePrerender || !prerenderSupported) mr = 0;
|
|
192
|
+
|
|
193
|
+
var ci = getConn();
|
|
194
|
+
if (ci.saveData) { mr = 0; mp = Math.min(mp, 2); }
|
|
195
|
+
if (String(ci.effectiveType).includes("2g")) { mr = 0; mp = Math.min(mp, 2); }
|
|
196
|
+
if (String(ci.effectiveType).includes("3g")) { mr = 0; mp = Math.min(mp, 4); }
|
|
197
|
+
if (typeof ci.rtt === "number" && ci.rtt > 350) { mr = 0; mp = Math.min(mp, 3); }
|
|
198
|
+
if (typeof ci.downlink === "number" && ci.downlink < 1.2) { mr = 0; mp = Math.min(mp, 3); }
|
|
199
|
+
|
|
200
|
+
mp = clamp(mp, 0, 12);
|
|
201
|
+
mr = clamp(mr, 0, 3);
|
|
202
|
+
|
|
203
|
+
return { mp: mp, mr: mr };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function enqueue(rawUrl, score, signal) {
|
|
207
|
+
var url = normalizeUrl(rawUrl);
|
|
208
|
+
if (!url) return;
|
|
209
|
+
if (stats.isLoaded(url) || inFlight.has(url) || qSet.has(url)) return;
|
|
210
|
+
|
|
211
|
+
if (q.length >= cfg.maxQueue) {
|
|
212
|
+
var minIdx = 0;
|
|
213
|
+
for (var i = 1; i < q.length; i++) if (q[i].score < q[minIdx].score) minIdx = i;
|
|
214
|
+
var dropped = q.splice(minIdx, 1)[0];
|
|
215
|
+
if (dropped) qSet.delete(dropped.url);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
stats.enqueued++;
|
|
219
|
+
q.push({ url: url, score: score, signal: signal, t: Date.now() });
|
|
220
|
+
qSet.add(url);
|
|
221
|
+
pump();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function pump() {
|
|
225
|
+
var b = budgets();
|
|
226
|
+
|
|
227
|
+
function takeBest() {
|
|
228
|
+
if (!q.length) return null;
|
|
229
|
+
var bestIdx = 0;
|
|
230
|
+
for (var i = 1; i < q.length; i++) if (q[i].score > q[bestIdx].score) bestIdx = i;
|
|
231
|
+
var item = q.splice(bestIdx, 1)[0];
|
|
232
|
+
if (item) qSet.delete(item.url);
|
|
233
|
+
return item;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
while (q.length) {
|
|
237
|
+
var item = takeBest();
|
|
238
|
+
if (!item) break;
|
|
239
|
+
|
|
240
|
+
var url = item.url;
|
|
241
|
+
var score = item.score;
|
|
242
|
+
var signal = item.signal;
|
|
243
|
+
|
|
244
|
+
if (stats.isLoaded(url) || inFlight.has(url)) continue;
|
|
245
|
+
|
|
246
|
+
var canPrefetch = usedPrefetch < b.mp;
|
|
247
|
+
var canPrerender = usedPrerender < b.mr;
|
|
248
|
+
|
|
249
|
+
// v1.0.2: prerender only on HIGH confidence signals (not hover)
|
|
250
|
+
var highIntent = (signal === "mousedown" || signal === "touchstart");
|
|
251
|
+
var doPrerender = canPrerender && highIntent && score >= 110;
|
|
252
|
+
|
|
253
|
+
if (!doPrerender && !canPrefetch) break;
|
|
254
|
+
|
|
255
|
+
var type = doPrerender ? "prerender" : "prefetch";
|
|
256
|
+
if (type === "prerender") usedPrerender++; else usedPrefetch++;
|
|
257
|
+
|
|
258
|
+
inFlight.add(url);
|
|
259
|
+
var link = document.createElement("link");
|
|
260
|
+
link.rel = type;
|
|
261
|
+
link.href = url;
|
|
262
|
+
link.as = "document";
|
|
263
|
+
link.fetchPriority = doPrerender ? "high" : (score >= 90 ? "auto" : "low");
|
|
264
|
+
|
|
265
|
+
(function (u, t) {
|
|
266
|
+
link.onload = function () { inFlight.delete(u); stats.markLoaded(u, t); };
|
|
267
|
+
link.onerror = function () { inFlight.delete(u); };
|
|
268
|
+
})(url, type);
|
|
269
|
+
|
|
270
|
+
document.head.appendChild(link);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
window.addEventListener("pageshow", function () { usedPrefetch = 0; usedPrerender = 0; });
|
|
275
|
+
|
|
276
|
+
return { enqueue: enqueue };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function maybeOverlay(cfg, stats) {
|
|
280
|
+
if (!cfg.debug || !cfg.debugOverlay) return;
|
|
281
|
+
var el = document.createElement("div");
|
|
282
|
+
el.style.cssText = "position:fixed;bottom:12px;right:12px;z-index:2147483647;background:rgba(0,0,0,.75);color:#fff;padding:10px 12px;border-radius:10px;font:12px/1.35 system-ui,-apple-system,Segoe UI,Roboto,Arial;box-shadow:0 10px 30px rgba(0,0,0,.35);max-width:280px";
|
|
283
|
+
function render() {
|
|
284
|
+
var s = stats.snapshot();
|
|
285
|
+
el.innerHTML =
|
|
286
|
+
"<div style='font-weight:700;margin-bottom:6px'>HyperInstant " + s.version + "</div>" +
|
|
287
|
+
"<div>Loaded: <b>" + (s.prefetched + s.prerendered) + "</b> (P:" + s.prefetched + " R:" + s.prerendered + ")</div>" +
|
|
288
|
+
"<div>Hit-rate: <b>" + Math.round(s.hitRate * 100) + "%</b></div>" +
|
|
289
|
+
"<div>HoverDelay: <b>" + cfg.hoverDelay + "ms</b></div>" +
|
|
290
|
+
"<div style='opacity:.8;margin-top:6px'>Clicks: " + s.clicked + " • Seen: " + s.seen + "</div>";
|
|
291
|
+
}
|
|
292
|
+
render();
|
|
293
|
+
document.addEventListener("DOMContentLoaded", function () { document.body.appendChild(el); });
|
|
294
|
+
setInterval(render, 1200);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function eligibleLink(target) {
|
|
298
|
+
var a = target && target.closest ? target.closest("a[href]") : null;
|
|
299
|
+
if (!a || !a.href) return null;
|
|
300
|
+
|
|
301
|
+
var rel = String(a.getAttribute("rel") || "").toLowerCase();
|
|
302
|
+
if (rel.includes("nofollow")) return null;
|
|
303
|
+
if (a.getAttribute("data-no-instant") !== null) return null;
|
|
304
|
+
if (a.getAttribute("data-hyperinstant") === "off") return null;
|
|
305
|
+
if (a.target && a.target !== "_self") return null;
|
|
306
|
+
|
|
307
|
+
return a;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function score(signal, a) {
|
|
311
|
+
var s = 60;
|
|
312
|
+
if (signal === "mousedown") s = 120;
|
|
313
|
+
else if (signal === "touchstart") s = 118;
|
|
314
|
+
else if (signal === "focusin") s = 98;
|
|
315
|
+
else if (signal === "mouseover") s = 82;
|
|
316
|
+
|
|
317
|
+
if (inNav(a)) s += 8;
|
|
318
|
+
if (String(a.getAttribute("data-hyperinstant-priority") || "").toLowerCase() === "high") s += 12;
|
|
319
|
+
return Math.min(130, s);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function createTuner(cfg, stats) {
|
|
323
|
+
var intentCount = 0;
|
|
324
|
+
function maybeTune() {
|
|
325
|
+
var total = stats.prefetched + stats.prerendered;
|
|
326
|
+
if (total < 16) return;
|
|
327
|
+
var hr = stats.hitRate;
|
|
328
|
+
|
|
329
|
+
if (hr >= 0.40) cfg.hoverDelay = clamp(cfg.hoverDelay - 8, 20, 120);
|
|
330
|
+
else if (hr <= 0.12) cfg.hoverDelay = clamp(cfg.hoverDelay + 10, 25, 180);
|
|
331
|
+
}
|
|
332
|
+
return { onEnqueue: function () { intentCount++; if (cfg.autoTune && intentCount % 25 === 0) maybeTune(); }, onClick: function () { } };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function start(cfg, scheduler, stats, tuner) {
|
|
336
|
+
var hoverTimer = null;
|
|
337
|
+
|
|
338
|
+
function onIntent(e) {
|
|
339
|
+
var a = eligibleLink(e.target);
|
|
340
|
+
if (!a) return;
|
|
341
|
+
|
|
342
|
+
var url = a.href;
|
|
343
|
+
stats.seen++;
|
|
344
|
+
|
|
345
|
+
if (shouldDeny(url, cfg)) { stats.denied++; return; }
|
|
346
|
+
|
|
347
|
+
var sig = e.type;
|
|
348
|
+
var sc = score(sig, a);
|
|
349
|
+
|
|
350
|
+
clearTimeout(hoverTimer);
|
|
351
|
+
var delay = (sig === "mouseover") ? cfg.hoverDelay : 0;
|
|
352
|
+
|
|
353
|
+
hoverTimer = setTimeout(function () {
|
|
354
|
+
scheduler.enqueue(url, sc, sig);
|
|
355
|
+
tuner.onEnqueue();
|
|
356
|
+
}, delay);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function onClick(e) {
|
|
360
|
+
var a = eligibleLink(e.target);
|
|
361
|
+
if (!a) return;
|
|
362
|
+
stats.markClick(normalizeUrl(a.href));
|
|
363
|
+
tuner.onClick();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
(cfg.events || ["mouseover", "touchstart", "mousedown", "focusin"]).forEach(function (evt) {
|
|
367
|
+
document.addEventListener(evt, onIntent, { passive: true, capture: true });
|
|
368
|
+
});
|
|
369
|
+
document.addEventListener("click", onClick, { passive: true, capture: true });
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
var cfg = applyModeDefaults(userCfg);
|
|
373
|
+
cfg = detectAdapter(cfg.adapter)(cfg);
|
|
374
|
+
|
|
375
|
+
var stats = new Stats();
|
|
376
|
+
var tuner = createTuner(cfg, stats);
|
|
377
|
+
var scheduler = createScheduler(cfg, stats);
|
|
378
|
+
|
|
379
|
+
window.HyperInstant = window.HyperInstant || {};
|
|
380
|
+
window.HyperInstant.version = VERSION;
|
|
381
|
+
|
|
382
|
+
if (cfg.debug) {
|
|
383
|
+
window.HyperInstantStats = stats;
|
|
384
|
+
console.log("[HyperInstant] v" + VERSION + " loaded", { mode: cfg.mode, adapter: cfg.adapter });
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
start(cfg, scheduler, stats, tuner);
|
|
388
|
+
maybeOverlay(cfg, stats);
|
|
389
|
+
})();
|
package/dist/hyperinstant.min.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
"use strict";var HyperInstantLib=(()=>{var w=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var k=(t,e)=>{for(var n in e)w(t,n,{get:e[n],enumerable:!0})},L=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of I(e))!O.call(t,i)&&i!==n&&w(t,i,{get:()=>e[i],enumerable:!(s=A(e,i))||s.enumerable});return t};var P=t=>L(w({},"__esModule",{value:!0}),t);var R={};k(R,{start:()=>y});function v(t,e){let n=new URL(t.toString());return n.hash="",e&&(n.search=""),n.toString()}function h(t,e){return typeof e=="string"?t.includes(e):e.test(t)}function x(t){let e=(t.getAttribute("href")||"").trim();return!(!e||e.startsWith("#")||e.startsWith("mailto:")||e.startsWith("tel:")||e.startsWith("javascript:")||t.hasAttribute("download")||(t.getAttribute("target")||"").toLowerCase()==="_blank")}function E(t){let{url:e,origin:n,allowSameOriginOnly:s,allow:i,deny:c}=t;if(s&&e.origin!==n)return!1;let l=e.toString(),u=e.pathname+e.search;for(let f of c)if(h(l,f)||h(u,f))return!1;if(i.length>0){for(let f of i)if(h(l,f)||h(u,f))return!0;return!1}return!0}function H(t,e,n=!1){try{let s=document.createElement("script");s.type="speculationrules";let i={};return i[t]=[{source:"list",urls:e}],s.textContent=JSON.stringify(i),document.head.appendChild(s),n&&console.log("[HyperInstant] speculationrules injected:",t,e),!0}catch{return!1}}function S(t,e=!1){try{let n=document.createElement("link");return n.rel="prefetch",n.href=t,n.as="document",document.head.appendChild(n),e&&console.log("[HyperInstant] link prefetch:",t),!0}catch{return!1}}function C(t){var n,s,i,c,l,u,f,m,p,o,r,a;let e=t||{};return{strategy:(n=e.strategy)!=null?n:"auto",hoverDelay:(s=e.hoverDelay)!=null?s:65,touchEnabled:(i=e.touchEnabled)!=null?i:!0,allowSameOriginOnly:(c=e.allowSameOriginOnly)!=null?c:!0,deny:(l=e.deny)!=null?l:["/cart","/checkout","/my-account","/account","/logout","add-to-cart"],allow:(u=e.allow)!=null?u:[],ignoreQueryParams:(f=e.ignoreQueryParams)!=null?f:!0,maxPrefetch:(m=e.maxPrefetch)!=null?m:6,maxPrerender:(p=e.maxPrerender)!=null?p:2,respectSaveData:(o=e.respectSaveData)!=null?o:!0,minEffectiveType:(r=e.minEffectiveType)!=null?r:"4g",debug:(a=e.debug)!=null?a:!1}}function T(t){let e=navigator,n=e.connection||e.mozConnection||e.webkitConnection;if(t.respectSaveData&&(n==null?void 0:n.saveData)===!0)return!1;let s=n==null?void 0:n.effectiveType;if(!s)return!0;let i=["2g","3g","4g"],c=i.indexOf(t.minEffectiveType),l=i.indexOf(s);return c===-1||l===-1?!0:l>=c}function y(t){let e=C(t!=null?t:window.HyperInstant),n=location.origin;if(!T(e)){e.debug&&console.log("[HyperInstant] disabled by network/save-data");return}let s=new Set,i=0,c=0,l=new WeakMap;function u(o){return o==="prefetch-only"?"prefetch":o==="prerender-first"||c<e.maxPrerender?"prerender":"prefetch"}function f(o){return o==="prefetch"?i<e.maxPrefetch:c<e.maxPrerender}function m(o){if(s.has(o))return;s.add(o);let r=u(e.strategy);if(!f(r))return;!H(r,[o],e.debug)&&r==="prefetch"&&S(o,e.debug),r==="prefetch"?i++:c++}function p(o){if(!x(o))return;let r;try{r=new URL(o.href,location.href)}catch{return}if(!E({url:r,origin:n,allowSameOriginOnly:e.allowSameOriginOnly,allow:e.allow,deny:e.deny}))return;let a=v(r,e.ignoreQueryParams);m(a)}document.addEventListener("mouseover",o=>{var b;let r=o.target;if(!r)return;let a=(b=r.closest)==null?void 0:b.call(r,"a");if(!a||l.get(a))return;let g=window.setTimeout(()=>{l.delete(a),p(a)},e.hoverDelay);l.set(a,g)},{passive:!0}),document.addEventListener("mouseout",o=>{var g;let r=o.target;if(!r)return;let a=(g=r.closest)==null?void 0:g.call(r,"a");if(!a)return;let d=l.get(a);d&&(window.clearTimeout(d),l.delete(a))},{passive:!0}),e.touchEnabled&&document.addEventListener("touchstart",o=>{var d;let r=o.target;if(!r)return;let a=(d=r.closest)==null?void 0:d.call(r,"a");a&&p(a)},{passive:!0}),e.debug&&console.log("[HyperInstant] started",e)}typeof window!="undefined"&&(window.HyperInstantStart=()=>y(window.HyperInstant),y(window.HyperInstant));return P(R);})();
|
|
2
|
-
//# sourceMappingURL=hyperinstant.min.js.map
|
|
1
|
+
/*! HyperInstant v1.0.2 | MIT | Putia Web */ (function (){if (window.__HYPERINSTANT_LOADED__) return; window.__HYPERINSTANT_LOADED__ = true; var userCfg = window.HyperInstant ||{}; var VERSION = "1.0.2"; function clamp(n, a, b){return Math.max(a, Math.min(b, n));} function isSameOrigin(url){try{return new URL(url, location.href).origin === location.origin;} catch (e){return false;}} function normalizeUrl(url){try{var u = new URL(url, location.href); u.hash = ""; return u.toString();} catch (e){return "";}} function supportsPrerender(){try{return "relList" in HTMLLinkElement.prototype && HTMLLinkElement.prototype.relList && HTMLLinkElement.prototype.relList.supports && HTMLLinkElement.prototype.relList.supports("prerender");} catch (e){return false;}} function getConn(){var c = navigator.connection || navigator.mozConnection || navigator.webkitConnection; return{saveData: !!(c && c.saveData), effectiveType: (c && c.effectiveType) ? String(c.effectiveType) : "unknown", downlink: (c && typeof c.downlink === "number") ? c.downlink : null, rtt: (c && typeof c.rtt === "number") ? c.rtt : null};} function inNav(a){try{return !!a.closest("nav, header, [role='navigation'], .header, .site-header");} catch (e){return false;}} function applyModeDefaults(cfg){var mode = String(cfg.mode || "safe").toLowerCase(); var out = Object.assign({}, cfg); // v1.0.2: prerender is intentionally conservative to avoid slowing pages if (mode === "aggressive"){out.maxPrefetch = out.maxPrefetch ?? 10; out.maxPrerender = out.maxPrerender ?? 1; out.hoverDelay = out.hoverDelay ?? 35;} else if (mode === "balanced"){out.maxPrefetch = out.maxPrefetch ?? 6; out.maxPrerender = out.maxPrerender ?? 0; out.hoverDelay = out.hoverDelay ?? 65;} else{out.maxPrefetch = out.maxPrefetch ?? 4; out.maxPrerender = out.maxPrerender ?? 0; out.hoverDelay = out.hoverDelay ?? 75;} out.adapter = out.adapter ?? "auto"; out.observe = out.observe ?? "intent"; out.events = out.events ?? ["mouseover", "touchstart", "mousedown", "focusin"]; out.deny = out.deny ?? []; out.debug = !!out.debug; out.debugOverlay = !!out.debugOverlay; // new in 1.0.2 out.enablePrerender = (out.enablePrerender === undefined) ? true : !!out.enablePrerender; out.autoTune = !!out.autoTune; // OFF by default out.maxQueue = clamp(out.maxQueue ?? 60, 10, 200); return out;} function hasMeta(name){return !!document.querySelector('meta[name="' + name + '"]');} function shopifyAdapter(cfg){var deny = new Set([].concat(cfg.deny || [])); ["/cart", "/checkout", "/account", "/account/login", "/account/register", "/account/logout", "/search", "cart/add", "cart/change", "cart/update", "variant=", "sections="].forEach(function (x){deny.add(x);}); return Object.assign({}, cfg,{deny: Array.from(deny)});} function wooAdapter(cfg){var deny = new Set([].concat(cfg.deny || [])); ["/cart", "/checkout", "/my-account", "add-to-cart", "wc-ajax", "remove_item", "update_cart", "apply_coupon"].forEach(function (x){deny.add(x);}); return Object.assign({}, cfg,{deny: Array.from(deny)});} function nopAdapter(cfg){var deny = new Set([].concat(cfg.deny || [])); ["/shoppingcart", "/checkout", "/login", "/register", "/logout", "/customer", "/wishlist", "/compareproducts", "/search", "?q=", "addproducttocart", "updateshoppingcart", "updatecart", "deleteitem", "returnurl=", "token=", "ajax", "flyout"].forEach(function (x){deny.add(x);}); return Object.assign({}, cfg,{deny: Array.from(deny)});} function detectAdapter(name){name = String(name || "auto").toLowerCase(); if (name === "shopify") return shopifyAdapter; if (name === "woocommerce") return wooAdapter; if (name === "nopcommerce") return nopAdapter; if (name === "generic") return function (c){return c;}; try{if (window.Shopify || hasMeta("shopify-digital-wallet") || hasMeta("shopify-checkout-api-token")) return shopifyAdapter;} catch (e){} try{if (window.wc_add_to_cart_params || window.wc_cart_fragments_params || (document.body && String(document.body.className || "").includes("woocommerce"))) return wooAdapter;} catch (e){} try{if (window.nopCommerce || document.querySelector("a[href*='shoppingcart']") || document.querySelector("form[action*='addproducttocart']")) return nopAdapter;} catch (e){} return function (c){return c;};} // Safer auto-deny (avoid broad keywords like "order" that can hit benign pages) var AUTO_PATH_PREFIX = ["/checkout", "/cart", "/shoppingcart", "/account", "/my-account", "/logout", "/login", "/register"]; var AUTO_PARAMS = ["add", "remove", "update", "change", "delete", "token", "variant", "sections", "returnurl", "redirect"]; var AUTO_KEYWORDS = ["add-to-cart", "addtocart", "addproducttocart", "wc-ajax", "admin-ajax", "sections=", "variant="]; function shouldDeny(url, cfg){if (!url) return true; if (!isSameOrigin(url)) return true; var u = String(url).toLowerCase(); var denyList = cfg.deny || []; for (var i = 0; i < denyList.length; i++){var r = denyList[i]; if (r && u.includes(String(r).toLowerCase())) return true;} try{var p = new URL(url, location.href).pathname.toLowerCase(); for (var j = 0; j < AUTO_PATH_PREFIX.length; j++){var ap = AUTO_PATH_PREFIX[j]; if (p === ap || p.startsWith(ap + "/")) return true;}} catch (e){} for (var k = 0; k < AUTO_KEYWORDS.length; k++) if (u.includes(AUTO_KEYWORDS[k])) return true; try{var uu = new URL(url, location.href); var sp = uu.searchParams; for (var q = 0; q < AUTO_PARAMS.length; q++) if (sp.has(AUTO_PARAMS[q])) return true;} catch (e){} return false;} function Stats(){this.startedAt = Date.now(); this.seen = 0; this.denied = 0; this.enqueued = 0; this.prefetched = 0; this.prerendered = 0; this.clicked = 0; this.hit = 0; this._loaded = new Set();} Stats.prototype.isLoaded = function (url){return this._loaded.has(url);}; Stats.prototype.markLoaded = function (url, type){if (this._loaded.has(url)) return; this._loaded.add(url); if (type === "prerender") this.prerendered++; else this.prefetched++;}; Stats.prototype.markClick = function (url){this.clicked++; if (this._loaded.has(url)) this.hit++;}; Object.defineProperty(Stats.prototype, "hitRate",{get: function (){var d = this.prefetched + this.prerendered; return d ? (this.hit / d) : 0;}}); Stats.prototype.snapshot = function (){return{version: VERSION, uptimeSec: Math.round((Date.now() - this.startedAt) / 1000), seen: this.seen, denied: this.denied, enqueued: this.enqueued, prefetched: this.prefetched, prerendered: this.prerendered, clicked: this.clicked, hit: this.hit, hitRate: Number(this.hitRate.toFixed(3))};}; function createScheduler(cfg, stats){var q = []; var qSet = new Set(); var inFlight = new Set(); var usedPrefetch = 0; var usedPrerender = 0; var prerenderSupported = supportsPrerender(); function budgets(){var mp = cfg.maxPrefetch; var mr = cfg.maxPrerender; if (!cfg.enablePrerender || !prerenderSupported) mr = 0; var ci = getConn(); if (ci.saveData){mr = 0; mp = Math.min(mp, 2);} if (String(ci.effectiveType).includes("2g")){mr = 0; mp = Math.min(mp, 2);} if (String(ci.effectiveType).includes("3g")){mr = 0; mp = Math.min(mp, 4);} if (typeof ci.rtt === "number" && ci.rtt > 350){mr = 0; mp = Math.min(mp, 3);} if (typeof ci.downlink === "number" && ci.downlink < 1.2){mr = 0; mp = Math.min(mp, 3);} mp = clamp(mp, 0, 12); mr = clamp(mr, 0, 3); return{mp: mp, mr: mr};} function enqueue(rawUrl, score, signal){var url = normalizeUrl(rawUrl); if (!url) return; if (stats.isLoaded(url) || inFlight.has(url) || qSet.has(url)) return; if (q.length >= cfg.maxQueue){var minIdx = 0; for (var i = 1; i < q.length; i++) if (q[i].score < q[minIdx].score) minIdx = i; var dropped = q.splice(minIdx, 1)[0]; if (dropped) qSet.delete(dropped.url);} stats.enqueued++; q.push({url: url, score: score, signal: signal, t: Date.now()}); qSet.add(url); pump();} function pump(){var b = budgets(); function takeBest(){if (!q.length) return null; var bestIdx = 0; for (var i = 1; i < q.length; i++) if (q[i].score > q[bestIdx].score) bestIdx = i; var item = q.splice(bestIdx, 1)[0]; if (item) qSet.delete(item.url); return item;} while (q.length){var item = takeBest(); if (!item) break; var url = item.url; var score = item.score; var signal = item.signal; if (stats.isLoaded(url) || inFlight.has(url)) continue; var canPrefetch = usedPrefetch < b.mp; var canPrerender = usedPrerender < b.mr; // v1.0.2: prerender only on HIGH confidence signals (not hover) var highIntent = (signal === "mousedown" || signal === "touchstart"); var doPrerender = canPrerender && highIntent && score >= 110; if (!doPrerender && !canPrefetch) break; var type = doPrerender ? "prerender" : "prefetch"; if (type === "prerender") usedPrerender++; else usedPrefetch++; inFlight.add(url); var link = document.createElement("link"); link.rel = type; link.href = url; link.as = "document"; link.fetchPriority = doPrerender ? "high" : (score >= 90 ? "auto" : "low"); (function (u, t){link.onload = function (){inFlight.delete(u); stats.markLoaded(u, t);}; link.onerror = function (){inFlight.delete(u);};})(url, type); document.head.appendChild(link);}} window.addEventListener("pageshow", function (){usedPrefetch = 0; usedPrerender = 0;}); return{enqueue: enqueue};} function maybeOverlay(cfg, stats){if (!cfg.debug || !cfg.debugOverlay) return; var el = document.createElement("div"); el.style.cssText = "position:fixed;bottom:12px;right:12px;z-index:2147483647;background:rgba(0,0,0,.75);color:#fff;padding:10px 12px;border-radius:10px;font:12px/1.35 system-ui,-apple-system,Segoe UI,Roboto,Arial;box-shadow:0 10px 30px rgba(0,0,0,.35);max-width:280px"; function render(){var s = stats.snapshot(); el.innerHTML = "<div style='font-weight:700;margin-bottom:6px'>HyperInstant " + s.version + "</div>" + "<div>Loaded: <b>" + (s.prefetched + s.prerendered) + "</b> (P:" + s.prefetched + " R:" + s.prerendered + ")</div>" + "<div>Hit-rate: <b>" + Math.round(s.hitRate * 100) + "%</b></div>" + "<div>HoverDelay: <b>" + cfg.hoverDelay + "ms</b></div>" + "<div style='opacity:.8;margin-top:6px'>Clicks: " + s.clicked + " • Seen: " + s.seen + "</div>";} render(); document.addEventListener("DOMContentLoaded", function (){document.body.appendChild(el);}); setInterval(render, 1200);} function eligibleLink(target){var a = target && target.closest ? target.closest("a[href]") : null; if (!a || !a.href) return null; var rel = String(a.getAttribute("rel") || "").toLowerCase(); if (rel.includes("nofollow")) return null; if (a.getAttribute("data-no-instant") !== null) return null; if (a.getAttribute("data-hyperinstant") === "off") return null; if (a.target && a.target !== "_self") return null; return a;} function score(signal, a){var s = 60; if (signal === "mousedown") s = 120; else if (signal === "touchstart") s = 118; else if (signal === "focusin") s = 98; else if (signal === "mouseover") s = 82; if (inNav(a)) s += 8; if (String(a.getAttribute("data-hyperinstant-priority") || "").toLowerCase() === "high") s += 12; return Math.min(130, s);} function createTuner(cfg, stats){var intentCount = 0; function maybeTune(){var total = stats.prefetched + stats.prerendered; if (total < 16) return; var hr = stats.hitRate; if (hr >= 0.40) cfg.hoverDelay = clamp(cfg.hoverDelay - 8, 20, 120); else if (hr <= 0.12) cfg.hoverDelay = clamp(cfg.hoverDelay + 10, 25, 180);} return{onEnqueue: function (){intentCount++; if (cfg.autoTune && intentCount % 25 === 0) maybeTune();}, onClick: function (){}};} function start(cfg, scheduler, stats, tuner){var hoverTimer = null; function onIntent(e){var a = eligibleLink(e.target); if (!a) return; var url = a.href; stats.seen++; if (shouldDeny(url, cfg)){stats.denied++; return;} var sig = e.type; var sc = score(sig, a); clearTimeout(hoverTimer); var delay = (sig === "mouseover") ? cfg.hoverDelay : 0; hoverTimer = setTimeout(function (){scheduler.enqueue(url, sc, sig); tuner.onEnqueue();}, delay);} function onClick(e){var a = eligibleLink(e.target); if (!a) return; stats.markClick(normalizeUrl(a.href)); tuner.onClick();} (cfg.events || ["mouseover", "touchstart", "mousedown", "focusin"]).forEach(function (evt){document.addEventListener(evt, onIntent,{passive: true, capture: true});}); document.addEventListener("click", onClick,{passive: true, capture: true});} var cfg = applyModeDefaults(userCfg); cfg = detectAdapter(cfg.adapter)(cfg); var stats = new Stats(); var tuner = createTuner(cfg, stats); var scheduler = createScheduler(cfg, stats); window.HyperInstant = window.HyperInstant ||{}; window.HyperInstant.version = VERSION; if (cfg.debug){window.HyperInstantStats = stats; console.log("[HyperInstant] v" + VERSION + " loaded",{mode: cfg.mode, adapter: cfg.adapter});} start(cfg, scheduler, stats, tuner); maybeOverlay(cfg, stats);})();
|
package/package.json
CHANGED
|
@@ -1,30 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyperinstant",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"main": "./dist/hyperinstant.min.js",
|
|
8
|
-
"module": "./dist/hyperinstant.esm.js",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Adaptive, safe and intelligent instant navigation engine for any website.",
|
|
5
|
+
"main": "dist/hyperinstant.min.js",
|
|
6
|
+
"module": "dist/hyperinstant.js",
|
|
9
7
|
"files": [
|
|
10
|
-
"dist"
|
|
11
|
-
"README.md",
|
|
12
|
-
"LICENSE"
|
|
8
|
+
"dist"
|
|
13
9
|
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "node build.mjs",
|
|
16
|
-
"dev": "node build.mjs --watch"
|
|
17
|
-
},
|
|
18
10
|
"keywords": [
|
|
11
|
+
"performance",
|
|
19
12
|
"prefetch",
|
|
20
13
|
"prerender",
|
|
21
|
-
"performance",
|
|
22
|
-
"speculationrules",
|
|
23
14
|
"instant-page",
|
|
15
|
+
"web-performance",
|
|
16
|
+
"shopify",
|
|
17
|
+
"woocommerce",
|
|
18
|
+
"nopcommerce",
|
|
19
|
+
"adaptive",
|
|
24
20
|
"navigation"
|
|
25
21
|
],
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
"typescript": "^5.0.0"
|
|
29
|
-
}
|
|
22
|
+
"author": "Putia Web",
|
|
23
|
+
"license": "MIT"
|
|
30
24
|
}
|
package/dist/hyperinstant.esm.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
function b(n,e){let r=new URL(n.toString());return r.hash="",e&&(r.search=""),r.toString()}function h(n,e){return typeof e=="string"?n.includes(e):e.test(n)}function v(n){let e=(n.getAttribute("href")||"").trim();return!(!e||e.startsWith("#")||e.startsWith("mailto:")||e.startsWith("tel:")||e.startsWith("javascript:")||n.hasAttribute("download")||(n.getAttribute("target")||"").toLowerCase()==="_blank")}function x(n){let{url:e,origin:r,allowSameOriginOnly:a,allow:s,deny:c}=n;if(a&&e.origin!==r)return!1;let l=e.toString(),u=e.pathname+e.search;for(let f of c)if(h(l,f)||h(u,f))return!1;if(s.length>0){for(let f of s)if(h(l,f)||h(u,f))return!0;return!1}return!0}function E(n,e,r=!1){try{let a=document.createElement("script");a.type="speculationrules";let s={};return s[n]=[{source:"list",urls:e}],a.textContent=JSON.stringify(s),document.head.appendChild(a),r&&console.log("[HyperInstant] speculationrules injected:",n,e),!0}catch{return!1}}function H(n,e=!1){try{let r=document.createElement("link");return r.rel="prefetch",r.href=n,r.as="document",document.head.appendChild(r),e&&console.log("[HyperInstant] link prefetch:",n),!0}catch{return!1}}function S(n){var r,a,s,c,l,u,f,m,p,o,t,i;let e=n||{};return{strategy:(r=e.strategy)!=null?r:"auto",hoverDelay:(a=e.hoverDelay)!=null?a:65,touchEnabled:(s=e.touchEnabled)!=null?s:!0,allowSameOriginOnly:(c=e.allowSameOriginOnly)!=null?c:!0,deny:(l=e.deny)!=null?l:["/cart","/checkout","/my-account","/account","/logout","add-to-cart"],allow:(u=e.allow)!=null?u:[],ignoreQueryParams:(f=e.ignoreQueryParams)!=null?f:!0,maxPrefetch:(m=e.maxPrefetch)!=null?m:6,maxPrerender:(p=e.maxPrerender)!=null?p:2,respectSaveData:(o=e.respectSaveData)!=null?o:!0,minEffectiveType:(t=e.minEffectiveType)!=null?t:"4g",debug:(i=e.debug)!=null?i:!1}}function A(n){let e=navigator,r=e.connection||e.mozConnection||e.webkitConnection;if(n.respectSaveData&&(r==null?void 0:r.saveData)===!0)return!1;let a=r==null?void 0:r.effectiveType;if(!a)return!0;let s=["2g","3g","4g"],c=s.indexOf(n.minEffectiveType),l=s.indexOf(a);return c===-1||l===-1?!0:l>=c}function y(n){let e=S(n!=null?n:window.HyperInstant),r=location.origin;if(!A(e)){e.debug&&console.log("[HyperInstant] disabled by network/save-data");return}let a=new Set,s=0,c=0,l=new WeakMap;function u(o){return o==="prefetch-only"?"prefetch":o==="prerender-first"||c<e.maxPrerender?"prerender":"prefetch"}function f(o){return o==="prefetch"?s<e.maxPrefetch:c<e.maxPrerender}function m(o){if(a.has(o))return;a.add(o);let t=u(e.strategy);if(!f(t))return;!E(t,[o],e.debug)&&t==="prefetch"&&H(o,e.debug),t==="prefetch"?s++:c++}function p(o){if(!v(o))return;let t;try{t=new URL(o.href,location.href)}catch{return}if(!x({url:t,origin:r,allowSameOriginOnly:e.allowSameOriginOnly,allow:e.allow,deny:e.deny}))return;let i=b(t,e.ignoreQueryParams);m(i)}document.addEventListener("mouseover",o=>{var w;let t=o.target;if(!t)return;let i=(w=t.closest)==null?void 0:w.call(t,"a");if(!i||l.get(i))return;let g=window.setTimeout(()=>{l.delete(i),p(i)},e.hoverDelay);l.set(i,g)},{passive:!0}),document.addEventListener("mouseout",o=>{var g;let t=o.target;if(!t)return;let i=(g=t.closest)==null?void 0:g.call(t,"a");if(!i)return;let d=l.get(i);d&&(window.clearTimeout(d),l.delete(i))},{passive:!0}),e.touchEnabled&&document.addEventListener("touchstart",o=>{var d;let t=o.target;if(!t)return;let i=(d=t.closest)==null?void 0:d.call(t,"a");i&&p(i)},{passive:!0}),e.debug&&console.log("[HyperInstant] started",e)}typeof window!="undefined"&&(window.HyperInstantStart=()=>y(window.HyperInstant),y(window.HyperInstant));export{y as start};
|
|
2
|
-
//# sourceMappingURL=hyperinstant.esm.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/url.ts", "../src/filters.ts", "../src/speculation.ts", "../src/prefetch.ts", "../src/core.ts", "../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export function normalizeUrl(url: URL, ignoreQueryParams: boolean): string {\n const u = new URL(url.toString());\n u.hash = \"\";\n if (ignoreQueryParams) u.search = \"\";\n return u.toString();\n}\n", "export function matchesRule(value: string, rule: string | RegExp): boolean {\n if (typeof rule === \"string\") return value.includes(rule);\n return rule.test(value);\n}\n\nexport function shouldHandleAnchor(a: HTMLAnchorElement): boolean {\n const href = (a.getAttribute(\"href\") || \"\").trim();\n if (!href) return false;\n if (href.startsWith(\"#\")) return false;\n if (href.startsWith(\"mailto:\") || href.startsWith(\"tel:\")) return false;\n if (href.startsWith(\"javascript:\")) return false;\n if (a.hasAttribute(\"download\")) return false;\n\n const target = (a.getAttribute(\"target\") || \"\").toLowerCase();\n if (target === \"_blank\") return false;\n\n return true;\n}\n\nexport function shouldAllowUrl(opts: {\n url: URL;\n origin: string;\n allowSameOriginOnly: boolean;\n allow: Array<string | RegExp>;\n deny: Array<string | RegExp>;\n}): boolean {\n const { url, origin, allowSameOriginOnly, allow, deny } = opts;\n\n if (allowSameOriginOnly && url.origin !== origin) return false;\n\n const full = url.toString();\n const path = url.pathname + url.search;\n\n for (const r of deny) {\n if (matchesRule(full, r) || matchesRule(path, r)) return false;\n }\n\n if (allow.length > 0) {\n for (const r of allow) {\n if (matchesRule(full, r) || matchesRule(path, r)) return true;\n }\n return false;\n }\n\n return true;\n}\n", "export type SpecAction = \"prefetch\" | \"prerender\";\n\nexport function injectSpeculationRules(action: SpecAction, urls: string[], debug = false): boolean {\n try {\n const script = document.createElement(\"script\");\n script.type = \"speculationrules\";\n\n const payload: any = {};\n payload[action] = [{ source: \"list\", urls }];\n\n script.textContent = JSON.stringify(payload);\n document.head.appendChild(script);\n\n if (debug) console.log(\"[HyperInstant] speculationrules injected:\", action, urls);\n return true;\n } catch {\n return false;\n }\n}\n", "export function linkPrefetch(url: string, debug = false): boolean {\n try {\n const l = document.createElement(\"link\");\n l.rel = \"prefetch\";\n l.href = url;\n l.as = \"document\";\n document.head.appendChild(l);\n if (debug) console.log(\"[HyperInstant] link prefetch:\", url);\n return true;\n } catch {\n return false;\n }\n}\n", "import { HyperInstantConfig, NormalizedConfig, Strategy } from \"./types.js\";\nimport { normalizeUrl } from \"./url.js\";\nimport { shouldHandleAnchor, shouldAllowUrl } from \"./filters.js\";\nimport { injectSpeculationRules } from \"./speculation.js\";\nimport { linkPrefetch } from \"./prefetch.js\";\n\ndeclare global {\n interface Window {\n HyperInstant?: HyperInstantConfig;\n HyperInstantStart?: () => void;\n }\n}\n\nfunction normalizeConfig(input?: HyperInstantConfig): NormalizedConfig {\n const cfg = input || {};\n return {\n strategy: cfg.strategy ?? \"auto\",\n hoverDelay: cfg.hoverDelay ?? 65,\n touchEnabled: cfg.touchEnabled ?? true,\n\n allowSameOriginOnly: cfg.allowSameOriginOnly ?? true,\n deny: cfg.deny ?? [\"/cart\", \"/checkout\", \"/my-account\", \"/account\", \"/logout\", \"add-to-cart\"],\n allow: cfg.allow ?? [],\n ignoreQueryParams: cfg.ignoreQueryParams ?? true,\n\n maxPrefetch: cfg.maxPrefetch ?? 6,\n maxPrerender: cfg.maxPrerender ?? 2,\n\n respectSaveData: cfg.respectSaveData ?? true,\n minEffectiveType: cfg.minEffectiveType ?? \"4g\",\n\n debug: cfg.debug ?? false\n };\n}\n\nfunction networkOk(cfg: NormalizedConfig): boolean {\n const nav: any = navigator as any;\n const conn = nav.connection || nav.mozConnection || nav.webkitConnection;\n\n if (cfg.respectSaveData && (conn?.saveData === true)) return false;\n\n const eff = conn?.effectiveType as string | undefined;\n if (!eff) return true;\n\n const order = [\"2g\", \"3g\", \"4g\"];\n const minIdx = order.indexOf(cfg.minEffectiveType);\n const effIdx = order.indexOf(eff);\n if (minIdx === -1 || effIdx === -1) return true;\n return effIdx >= minIdx;\n}\n\nexport function start(userCfg?: HyperInstantConfig): void {\n const cfg = normalizeConfig(userCfg ?? window.HyperInstant);\n const origin = location.origin;\n\n if (!networkOk(cfg)) {\n if (cfg.debug) console.log(\"[HyperInstant] disabled by network/save-data\");\n return;\n }\n\n const seen = new Set<string>();\n let prefetchCount = 0;\n let prerenderCount = 0;\n\n const hoverTimers = new WeakMap<Element, number>();\n\n function pickAction(strategy: Strategy): \"prefetch\" | \"prerender\" {\n if (strategy === \"prefetch-only\") return \"prefetch\";\n if (strategy === \"prerender-first\") return \"prerender\";\n if (prerenderCount < cfg.maxPrerender) return \"prerender\";\n return \"prefetch\";\n }\n\n function canProceed(action: \"prefetch\" | \"prerender\"): boolean {\n if (action === \"prefetch\") return prefetchCount < cfg.maxPrefetch;\n return prerenderCount < cfg.maxPrerender;\n }\n\n function doWarm(urlString: string): void {\n if (seen.has(urlString)) return;\n seen.add(urlString);\n\n const action = pickAction(cfg.strategy);\n if (!canProceed(action)) return;\n\n const okSpec = injectSpeculationRules(action, [urlString], cfg.debug);\n if (!okSpec && action === \"prefetch\") linkPrefetch(urlString, cfg.debug);\n\n if (action === \"prefetch\") prefetchCount++;\n else prerenderCount++;\n }\n\n function handlePotentialLink(a: HTMLAnchorElement): void {\n if (!shouldHandleAnchor(a)) return;\n\n let url: URL;\n try { url = new URL(a.href, location.href); }\n catch { return; }\n\n if (!shouldAllowUrl({ url, origin, allowSameOriginOnly: cfg.allowSameOriginOnly, allow: cfg.allow, deny: cfg.deny })) return;\n\n const norm = normalizeUrl(url, cfg.ignoreQueryParams);\n doWarm(norm);\n }\n\n document.addEventListener(\"mouseover\", (e) => {\n const t = e.target as Element | null;\n if (!t) return;\n const a = (t.closest?.(\"a\") as HTMLAnchorElement | null);\n if (!a) return;\n\n const existing = hoverTimers.get(a);\n if (existing) return;\n\n const id = window.setTimeout(() => {\n hoverTimers.delete(a);\n handlePotentialLink(a);\n }, cfg.hoverDelay);\n\n hoverTimers.set(a, id);\n }, { passive: true });\n\n document.addEventListener(\"mouseout\", (e) => {\n const t = e.target as Element | null;\n if (!t) return;\n const a = (t.closest?.(\"a\") as HTMLAnchorElement | null);\n if (!a) return;\n\n const id = hoverTimers.get(a);\n if (id) {\n window.clearTimeout(id);\n hoverTimers.delete(a);\n }\n }, { passive: true });\n\n if (cfg.touchEnabled) {\n document.addEventListener(\"touchstart\", (e) => {\n const t = e.target as Element | null;\n if (!t) return;\n const a = (t.closest?.(\"a\") as HTMLAnchorElement | null);\n if (!a) return;\n handlePotentialLink(a);\n }, { passive: true });\n }\n\n if (cfg.debug) console.log(\"[HyperInstant] started\", cfg);\n}\n", "import { start } from \"./core.js\";\nexport { start };\n\nif (typeof window !== \"undefined\") {\n window.HyperInstantStart = () => start(window.HyperInstant);\n start(window.HyperInstant);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["normalizeUrl", "url", "ignoreQueryParams", "u", "matchesRule", "value", "rule", "shouldHandleAnchor", "a", "href", "shouldAllowUrl", "opts", "url", "origin", "allowSameOriginOnly", "allow", "deny", "full", "path", "r", "injectSpeculationRules", "action", "urls", "debug", "script", "payload", "linkPrefetch", "url", "debug", "l", "normalizeConfig", "input", "_a", "_b", "_c", "_d", "_e", "_f", "_g", "_h", "_i", "_j", "_k", "_l", "cfg", "networkOk", "nav", "conn", "eff", "order", "minIdx", "effIdx", "start", "userCfg", "origin", "seen", "prefetchCount", "prerenderCount", "hoverTimers", "pickAction", "strategy", "canProceed", "action", "doWarm", "urlString", "injectSpeculationRules", "linkPrefetch", "handlePotentialLink", "a", "shouldHandleAnchor", "url", "shouldAllowUrl", "norm", "normalizeUrl", "e", "id", "start"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../src/url.ts", "../src/filters.ts", "../src/speculation.ts", "../src/prefetch.ts", "../src/core.ts"],
|
|
4
|
-
"sourcesContent": ["import { start } from \"./core.js\";\nexport { start };\n\nif (typeof window !== \"undefined\") {\n window.HyperInstantStart = () => start(window.HyperInstant);\n start(window.HyperInstant);\n}\n", "export function normalizeUrl(url: URL, ignoreQueryParams: boolean): string {\n const u = new URL(url.toString());\n u.hash = \"\";\n if (ignoreQueryParams) u.search = \"\";\n return u.toString();\n}\n", "export function matchesRule(value: string, rule: string | RegExp): boolean {\n if (typeof rule === \"string\") return value.includes(rule);\n return rule.test(value);\n}\n\nexport function shouldHandleAnchor(a: HTMLAnchorElement): boolean {\n const href = (a.getAttribute(\"href\") || \"\").trim();\n if (!href) return false;\n if (href.startsWith(\"#\")) return false;\n if (href.startsWith(\"mailto:\") || href.startsWith(\"tel:\")) return false;\n if (href.startsWith(\"javascript:\")) return false;\n if (a.hasAttribute(\"download\")) return false;\n\n const target = (a.getAttribute(\"target\") || \"\").toLowerCase();\n if (target === \"_blank\") return false;\n\n return true;\n}\n\nexport function shouldAllowUrl(opts: {\n url: URL;\n origin: string;\n allowSameOriginOnly: boolean;\n allow: Array<string | RegExp>;\n deny: Array<string | RegExp>;\n}): boolean {\n const { url, origin, allowSameOriginOnly, allow, deny } = opts;\n\n if (allowSameOriginOnly && url.origin !== origin) return false;\n\n const full = url.toString();\n const path = url.pathname + url.search;\n\n for (const r of deny) {\n if (matchesRule(full, r) || matchesRule(path, r)) return false;\n }\n\n if (allow.length > 0) {\n for (const r of allow) {\n if (matchesRule(full, r) || matchesRule(path, r)) return true;\n }\n return false;\n }\n\n return true;\n}\n", "export type SpecAction = \"prefetch\" | \"prerender\";\n\nexport function injectSpeculationRules(action: SpecAction, urls: string[], debug = false): boolean {\n try {\n const script = document.createElement(\"script\");\n script.type = \"speculationrules\";\n\n const payload: any = {};\n payload[action] = [{ source: \"list\", urls }];\n\n script.textContent = JSON.stringify(payload);\n document.head.appendChild(script);\n\n if (debug) console.log(\"[HyperInstant] speculationrules injected:\", action, urls);\n return true;\n } catch {\n return false;\n }\n}\n", "export function linkPrefetch(url: string, debug = false): boolean {\n try {\n const l = document.createElement(\"link\");\n l.rel = \"prefetch\";\n l.href = url;\n l.as = \"document\";\n document.head.appendChild(l);\n if (debug) console.log(\"[HyperInstant] link prefetch:\", url);\n return true;\n } catch {\n return false;\n }\n}\n", "import { HyperInstantConfig, NormalizedConfig, Strategy } from \"./types.js\";\nimport { normalizeUrl } from \"./url.js\";\nimport { shouldHandleAnchor, shouldAllowUrl } from \"./filters.js\";\nimport { injectSpeculationRules } from \"./speculation.js\";\nimport { linkPrefetch } from \"./prefetch.js\";\n\ndeclare global {\n interface Window {\n HyperInstant?: HyperInstantConfig;\n HyperInstantStart?: () => void;\n }\n}\n\nfunction normalizeConfig(input?: HyperInstantConfig): NormalizedConfig {\n const cfg = input || {};\n return {\n strategy: cfg.strategy ?? \"auto\",\n hoverDelay: cfg.hoverDelay ?? 65,\n touchEnabled: cfg.touchEnabled ?? true,\n\n allowSameOriginOnly: cfg.allowSameOriginOnly ?? true,\n deny: cfg.deny ?? [\"/cart\", \"/checkout\", \"/my-account\", \"/account\", \"/logout\", \"add-to-cart\"],\n allow: cfg.allow ?? [],\n ignoreQueryParams: cfg.ignoreQueryParams ?? true,\n\n maxPrefetch: cfg.maxPrefetch ?? 6,\n maxPrerender: cfg.maxPrerender ?? 2,\n\n respectSaveData: cfg.respectSaveData ?? true,\n minEffectiveType: cfg.minEffectiveType ?? \"4g\",\n\n debug: cfg.debug ?? false\n };\n}\n\nfunction networkOk(cfg: NormalizedConfig): boolean {\n const nav: any = navigator as any;\n const conn = nav.connection || nav.mozConnection || nav.webkitConnection;\n\n if (cfg.respectSaveData && (conn?.saveData === true)) return false;\n\n const eff = conn?.effectiveType as string | undefined;\n if (!eff) return true;\n\n const order = [\"2g\", \"3g\", \"4g\"];\n const minIdx = order.indexOf(cfg.minEffectiveType);\n const effIdx = order.indexOf(eff);\n if (minIdx === -1 || effIdx === -1) return true;\n return effIdx >= minIdx;\n}\n\nexport function start(userCfg?: HyperInstantConfig): void {\n const cfg = normalizeConfig(userCfg ?? window.HyperInstant);\n const origin = location.origin;\n\n if (!networkOk(cfg)) {\n if (cfg.debug) console.log(\"[HyperInstant] disabled by network/save-data\");\n return;\n }\n\n const seen = new Set<string>();\n let prefetchCount = 0;\n let prerenderCount = 0;\n\n const hoverTimers = new WeakMap<Element, number>();\n\n function pickAction(strategy: Strategy): \"prefetch\" | \"prerender\" {\n if (strategy === \"prefetch-only\") return \"prefetch\";\n if (strategy === \"prerender-first\") return \"prerender\";\n if (prerenderCount < cfg.maxPrerender) return \"prerender\";\n return \"prefetch\";\n }\n\n function canProceed(action: \"prefetch\" | \"prerender\"): boolean {\n if (action === \"prefetch\") return prefetchCount < cfg.maxPrefetch;\n return prerenderCount < cfg.maxPrerender;\n }\n\n function doWarm(urlString: string): void {\n if (seen.has(urlString)) return;\n seen.add(urlString);\n\n const action = pickAction(cfg.strategy);\n if (!canProceed(action)) return;\n\n const okSpec = injectSpeculationRules(action, [urlString], cfg.debug);\n if (!okSpec && action === \"prefetch\") linkPrefetch(urlString, cfg.debug);\n\n if (action === \"prefetch\") prefetchCount++;\n else prerenderCount++;\n }\n\n function handlePotentialLink(a: HTMLAnchorElement): void {\n if (!shouldHandleAnchor(a)) return;\n\n let url: URL;\n try { url = new URL(a.href, location.href); }\n catch { return; }\n\n if (!shouldAllowUrl({ url, origin, allowSameOriginOnly: cfg.allowSameOriginOnly, allow: cfg.allow, deny: cfg.deny })) return;\n\n const norm = normalizeUrl(url, cfg.ignoreQueryParams);\n doWarm(norm);\n }\n\n document.addEventListener(\"mouseover\", (e) => {\n const t = e.target as Element | null;\n if (!t) return;\n const a = (t.closest?.(\"a\") as HTMLAnchorElement | null);\n if (!a) return;\n\n const existing = hoverTimers.get(a);\n if (existing) return;\n\n const id = window.setTimeout(() => {\n hoverTimers.delete(a);\n handlePotentialLink(a);\n }, cfg.hoverDelay);\n\n hoverTimers.set(a, id);\n }, { passive: true });\n\n document.addEventListener(\"mouseout\", (e) => {\n const t = e.target as Element | null;\n if (!t) return;\n const a = (t.closest?.(\"a\") as HTMLAnchorElement | null);\n if (!a) return;\n\n const id = hoverTimers.get(a);\n if (id) {\n window.clearTimeout(id);\n hoverTimers.delete(a);\n }\n }, { passive: true });\n\n if (cfg.touchEnabled) {\n document.addEventListener(\"touchstart\", (e) => {\n const t = e.target as Element | null;\n if (!t) return;\n const a = (t.closest?.(\"a\") as HTMLAnchorElement | null);\n if (!a) return;\n handlePotentialLink(a);\n }, { passive: true });\n }\n\n if (cfg.debug) console.log(\"[HyperInstant] started\", cfg);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["index_exports", "__export", "start", "normalizeUrl", "url", "ignoreQueryParams", "u", "matchesRule", "value", "rule", "shouldHandleAnchor", "a", "href", "shouldAllowUrl", "opts", "url", "origin", "allowSameOriginOnly", "allow", "deny", "full", "path", "r", "injectSpeculationRules", "action", "urls", "debug", "script", "payload", "linkPrefetch", "url", "debug", "l", "normalizeConfig", "input", "_a", "_b", "_c", "_d", "_e", "_f", "_g", "_h", "_i", "_j", "_k", "_l", "cfg", "networkOk", "nav", "conn", "eff", "order", "minIdx", "effIdx", "start", "userCfg", "origin", "seen", "prefetchCount", "prerenderCount", "hoverTimers", "pickAction", "strategy", "canProceed", "action", "doWarm", "urlString", "injectSpeculationRules", "linkPrefetch", "handlePotentialLink", "a", "shouldHandleAnchor", "url", "shouldAllowUrl", "norm", "normalizeUrl", "e", "t", "id", "start"]
|
|
7
|
-
}
|