hyperinstant 1.0.1 → 1.1.0
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/README.md +12 -31
- package/dist/hyperinstant.js +88 -264
- package/dist/hyperinstant.min.js +1 -1
- package/package.json +4 -16
package/README.md
CHANGED
|
@@ -1,42 +1,23 @@
|
|
|
1
|
-
# HyperInstant v1.0
|
|
1
|
+
# HyperInstant v1.1.0 (Legacy-fast)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
### ✅ Key features
|
|
6
|
-
- **Adaptive engine** (network-aware budgets + intent scoring)
|
|
7
|
-
- **Safe by default** (auto-deny rules for cart/checkout/account/actions)
|
|
8
|
-
- **Platform adapters** (auto: Shopify / WooCommerce / nopCommerce)
|
|
9
|
-
- **Local stats** (hit-rate) + optional debug overlay
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Quick install (CDN)
|
|
3
|
+
This package is intentionally a **legacy-fast** build: behavior matches the original **0.1.0** approach
|
|
4
|
+
(simple, ultra-light, perceived-speed first).
|
|
14
5
|
|
|
6
|
+
## CDN
|
|
15
7
|
```html
|
|
16
8
|
<script>
|
|
17
9
|
window.HyperInstant = {
|
|
18
|
-
|
|
19
|
-
adapter: "auto", // auto | shopify | woocommerce | nopcommerce | generic
|
|
20
|
-
observe: "intent", // intent | intent+viewport
|
|
10
|
+
strategy: "auto",
|
|
21
11
|
hoverDelay: 65,
|
|
12
|
+
maxPrefetch: 6,
|
|
13
|
+
maxPrerender: 2,
|
|
14
|
+
deny: ["/cart", "/checkout", "/my-account", "/logout", "add-to-cart"],
|
|
22
15
|
debug: false
|
|
23
16
|
};
|
|
24
17
|
</script>
|
|
25
|
-
<script src="https://unpkg.com/hyperinstant@1.0
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Recommended deny additions for eCommerce (optional)
|
|
29
|
-
You can add project-specific entries (strings are matched by `.includes()`):
|
|
30
|
-
|
|
31
|
-
```js
|
|
32
|
-
deny: ["/cart", "/checkout", "/account", "add-to-cart"]
|
|
18
|
+
<script src="https://unpkg.com/hyperinstant@1.1.0/dist/hyperinstant.min.js" defer></script>
|
|
33
19
|
```
|
|
34
20
|
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
- optional mini overlay (`debugOverlay: true`)
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
MIT License.
|
|
21
|
+
## Notes
|
|
22
|
+
- Uses `<link rel="prefetch|prerender">` for documents
|
|
23
|
+
- No adapters / no auto-tuning / no overlays (by design)
|
package/dist/hyperinstant.js
CHANGED
|
@@ -1,300 +1,124 @@
|
|
|
1
|
-
/*! HyperInstant v1.0.1 | MIT | Putia Web */
|
|
2
|
-
(function(){
|
|
1
|
+
/*! HyperInstant v1.1.0 (legacy-fast, 0.1.0 behavior) | MIT | Putia Web */
|
|
2
|
+
(function () {
|
|
3
3
|
if (window.__HYPERINSTANT_LOADED__) return;
|
|
4
4
|
window.__HYPERINSTANT_LOADED__ = true;
|
|
5
5
|
|
|
6
6
|
var cfg = window.HyperInstant || {};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return out;
|
|
7
|
+
var strategy = (cfg.strategy || "auto").toLowerCase();
|
|
8
|
+
var hoverDelay = (typeof cfg.hoverDelay === "number") ? cfg.hoverDelay : 65;
|
|
9
|
+
var maxPrefetch = (typeof cfg.maxPrefetch === "number") ? cfg.maxPrefetch : 6;
|
|
10
|
+
var maxPrerender = (typeof cfg.maxPrerender === "number") ? cfg.maxPrerender : 2;
|
|
11
|
+
var deny = Array.isArray(cfg.deny) ? cfg.deny : [];
|
|
12
|
+
var debug = !!cfg.debug;
|
|
13
|
+
|
|
14
|
+
var prefetched = new Set();
|
|
15
|
+
var prerendered = new Set();
|
|
16
|
+
var inflight = new Set();
|
|
17
|
+
var usedPrefetch = 0;
|
|
18
|
+
var usedPrerender = 0;
|
|
19
|
+
|
|
20
|
+
function supportsPrerender() {
|
|
21
|
+
try {
|
|
22
|
+
return "relList" in HTMLLinkElement.prototype &&
|
|
23
|
+
HTMLLinkElement.prototype.relList &&
|
|
24
|
+
HTMLLinkElement.prototype.relList.supports &&
|
|
25
|
+
HTMLLinkElement.prototype.relList.supports("prerender");
|
|
26
|
+
} catch (e) { return false; }
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
["/cart","/checkout","/account","/account/login","/account/register","/account/logout","/search","cart/add","cart/change","cart/update","?variant=","variant=","sections="].forEach(function(x){ deny.add(x); });
|
|
34
|
-
return Object.assign({}, c, {deny:Array.from(deny)});
|
|
35
|
-
}
|
|
36
|
-
function wooAdapter(c){
|
|
37
|
-
var deny=new Set([].concat(c.deny||[]));
|
|
38
|
-
["/cart","/checkout","/my-account","add-to-cart","wc-ajax","remove_item","update_cart","apply_coupon"].forEach(function(x){ deny.add(x); });
|
|
39
|
-
return Object.assign({}, c, {deny:Array.from(deny)});
|
|
29
|
+
function normalizeUrl(url) {
|
|
30
|
+
try { var u = new URL(url, location.href); u.hash = ""; return u.toString(); }
|
|
31
|
+
catch (e) { return ""; }
|
|
40
32
|
}
|
|
41
|
-
function nopAdapter(c){
|
|
42
|
-
var deny=new Set([].concat(c.deny||[]));
|
|
43
|
-
["/shoppingcart","/checkout","/login","/register","/logout","/customer","addproducttocart","updateshoppingcart","updatecart","deleteitem","/wishlist","/compareproducts","/search","?q="].forEach(function(x){ deny.add(x); });
|
|
44
|
-
return Object.assign({}, c, {deny:Array.from(deny)});
|
|
45
|
-
}
|
|
46
|
-
function detectAdapter(name){
|
|
47
|
-
name = String(name||"auto").toLowerCase();
|
|
48
|
-
if (name==="shopify") return shopifyAdapter;
|
|
49
|
-
if (name==="woocommerce") return wooAdapter;
|
|
50
|
-
if (name==="nopcommerce") return nopAdapter;
|
|
51
|
-
if (name==="generic") return function(x){ return x; };
|
|
52
33
|
|
|
53
|
-
|
|
54
|
-
try{
|
|
55
|
-
|
|
56
|
-
return function(x){ return x; };
|
|
34
|
+
function isSameOrigin(url) {
|
|
35
|
+
try { return new URL(url, location.href).origin === location.origin; }
|
|
36
|
+
catch (e) { return false; }
|
|
57
37
|
}
|
|
58
38
|
|
|
59
|
-
|
|
60
|
-
var AUTO_PATHS=["/checkout","/cart","/shoppingcart","/account","/my-account","/logout","/login","/register"];
|
|
61
|
-
var AUTO_PARAMS=["add","remove","update","change","delete","token","variant","sections","returnurl","redirect"];
|
|
62
|
-
|
|
63
|
-
function shouldDeny(url, c){
|
|
39
|
+
function isDenied(url) {
|
|
64
40
|
if (!url) return true;
|
|
65
41
|
if (!isSameOrigin(url)) return true;
|
|
66
|
-
var u = String(url).toLowerCase();
|
|
67
|
-
|
|
68
|
-
var denyList = c.deny || [];
|
|
69
|
-
for (var i=0;i<denyList.length;i++){ var r=denyList[i]; if (r && u.includes(String(r).toLowerCase())) return true; }
|
|
70
|
-
|
|
71
|
-
try{
|
|
72
|
-
var p = new URL(url, location.href).pathname.toLowerCase();
|
|
73
|
-
for (var j=0;j<AUTO_PATHS.length;j++){
|
|
74
|
-
var ap=AUTO_PATHS[j];
|
|
75
|
-
if (p===ap || p.startsWith(ap+"/")) return true;
|
|
76
|
-
}
|
|
77
|
-
}catch(e){}
|
|
78
|
-
|
|
79
|
-
for (var k=0;k<AUTO_KEYWORDS.length;k++){ if (u.includes(AUTO_KEYWORDS[k])) return true; }
|
|
80
|
-
|
|
81
|
-
try{
|
|
82
|
-
var uu = new URL(url, location.href);
|
|
83
|
-
var sp = uu.searchParams;
|
|
84
|
-
for (var q=0;q<AUTO_PARAMS.length;q++){ if (sp.has(AUTO_PARAMS[q])) return true; }
|
|
85
|
-
}catch(e){}
|
|
86
42
|
|
|
43
|
+
var u = String(url);
|
|
44
|
+
for (var i = 0; i < deny.length; i++) {
|
|
45
|
+
var d = deny[i];
|
|
46
|
+
if (d && u.indexOf(String(d)) !== -1) return true;
|
|
47
|
+
}
|
|
87
48
|
return false;
|
|
88
49
|
}
|
|
89
50
|
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this._loaded=new Map();
|
|
96
|
-
}
|
|
97
|
-
Stats.prototype.markLoaded=function(url,type){
|
|
98
|
-
if(!this._loaded.has(url)){
|
|
99
|
-
this._loaded.set(url,{type:type,t:Date.now()});
|
|
100
|
-
if(type==="prerender") this.prerendered++; else this.prefetched++;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
Stats.prototype.isLoaded=function(url){ return this._loaded.has(url); };
|
|
104
|
-
Stats.prototype.markClick=function(url){ this.clicked++; if(this._loaded.has(url)) this.hit++; };
|
|
105
|
-
Object.defineProperty(Stats.prototype,"hitRate",{ get:function(){ var d=this.prefetched+this.prerendered; return d? (this.hit/d):0; } });
|
|
106
|
-
Stats.prototype.snapshot=function(){
|
|
107
|
-
return { version:"1.0.1", 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)) };
|
|
108
|
-
};
|
|
51
|
+
function enqueue(url) {
|
|
52
|
+
url = normalizeUrl(url);
|
|
53
|
+
if (!url) return;
|
|
54
|
+
if (isDenied(url)) return;
|
|
55
|
+
if (prefetched.has(url) || prerendered.has(url) || inflight.has(url)) return;
|
|
109
56
|
|
|
110
|
-
|
|
111
|
-
var
|
|
112
|
-
var inFlight=new Set();
|
|
113
|
-
var loaded=new Set();
|
|
114
|
-
var usedPrefetch=0, usedPrerender=0;
|
|
57
|
+
var canPrerender = supportsPrerender() && (usedPrerender < maxPrerender);
|
|
58
|
+
var canPrefetch = (usedPrefetch < maxPrefetch);
|
|
115
59
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
var ci=getConn();
|
|
121
|
-
var mp=c.maxPrefetch, mr=c.maxPrerender;
|
|
122
|
-
if (ci.saveData){ mr=0; mp=Math.min(mp,2); }
|
|
123
|
-
if (String(ci.effectiveType).includes("2g")){ mr=0; mp=Math.min(mp,2); }
|
|
124
|
-
if (String(ci.effectiveType).includes("3g")){ mr=Math.min(mr,0); mp=Math.min(mp,4); }
|
|
125
|
-
if (typeof ci.rtt==="number" && ci.rtt>300){ mr=0; mp=Math.min(mp,3); }
|
|
126
|
-
if (typeof ci.downlink==="number" && ci.downlink<1.2){ mr=0; mp=Math.min(mp,3); }
|
|
127
|
-
mp=clamp(mp,0,12); mr=clamp(mr,0,3);
|
|
128
|
-
if (!supportsPrerender()) mr=0;
|
|
129
|
-
return { maxPrefetch: mp, maxPrerender: mr };
|
|
130
|
-
}
|
|
60
|
+
var rel = "prefetch";
|
|
61
|
+
if (strategy === "prefetch") rel = "prefetch";
|
|
62
|
+
else if (strategy === "prerender") rel = canPrerender ? "prerender" : "prefetch";
|
|
63
|
+
else rel = canPrerender ? "prerender" : "prefetch";
|
|
131
64
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
stats.enqueued++;
|
|
136
|
-
queue.push({url:url,score:score,t:Date.now()});
|
|
137
|
-
queue.sort(function(a,b){ return b.score-a.score; });
|
|
138
|
-
pump();
|
|
139
|
-
}
|
|
65
|
+
if (rel === "prefetch" && !canPrefetch) return;
|
|
66
|
+
if (rel === "prerender" && !canPrerender) rel = canPrefetch ? "prefetch" : "";
|
|
67
|
+
if (!rel) return;
|
|
140
68
|
|
|
141
|
-
|
|
142
|
-
var b=computeBudgets();
|
|
143
|
-
while(queue.length){
|
|
144
|
-
var n=queue.shift();
|
|
145
|
-
var url=n.url, score=n.score;
|
|
146
|
-
if(loaded.has(url) || stats.isLoaded(url)) continue;
|
|
147
|
-
if(inFlight.has(url)) continue;
|
|
69
|
+
inflight.add(url);
|
|
148
70
|
|
|
149
|
-
|
|
150
|
-
|
|
71
|
+
var link = document.createElement("link");
|
|
72
|
+
link.rel = rel;
|
|
73
|
+
link.href = url;
|
|
74
|
+
link.as = "document";
|
|
151
75
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
var type = wantPrerender ? "prerender" : "prefetch";
|
|
155
|
-
if(type==="prerender") usedPrerender++; else usedPrefetch++;
|
|
156
|
-
|
|
157
|
-
inFlight.add(url);
|
|
158
|
-
var link=document.createElement("link");
|
|
159
|
-
link.rel=type;
|
|
160
|
-
link.href=url;
|
|
161
|
-
link.as="document";
|
|
162
|
-
link.fetchPriority = (score>=90) ? "high" : (score>=70 ? "auto" : "low");
|
|
163
|
-
|
|
164
|
-
link.onload=function(){
|
|
165
|
-
loaded.add(url);
|
|
166
|
-
inFlight.delete(url);
|
|
167
|
-
stats.markLoaded(url,type);
|
|
168
|
-
};
|
|
169
|
-
link.onerror=function(){ inFlight.delete(url); };
|
|
170
|
-
document.head.appendChild(link);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
76
|
+
if (rel === "prerender") usedPrerender++;
|
|
77
|
+
else usedPrefetch++;
|
|
173
78
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
function createTuner(c, stats){
|
|
180
|
-
var intentCount=0;
|
|
181
|
-
function maybeTune(){
|
|
182
|
-
var total = stats.prefetched + stats.prerendered;
|
|
183
|
-
if(total<12) return;
|
|
184
|
-
var hr = stats.hitRate;
|
|
185
|
-
if (hr>=0.35) c.hoverDelay = clamp((c.hoverDelay||65)-10, 25, 140);
|
|
186
|
-
else if (hr<=0.15) c.hoverDelay = clamp((c.hoverDelay||65)+15, 25, 180);
|
|
187
|
-
}
|
|
188
|
-
return {
|
|
189
|
-
onEnqueue:function(){ intentCount++; if(intentCount%20===0) maybeTune(); },
|
|
190
|
-
onClick:function(){}
|
|
79
|
+
link.onload = function () {
|
|
80
|
+
inflight.delete(url);
|
|
81
|
+
if (rel === "prerender") prerendered.add(url);
|
|
82
|
+
else prefetched.add(url);
|
|
83
|
+
if (debug) console.log("[HyperInstant]", rel, url);
|
|
191
84
|
};
|
|
192
|
-
|
|
85
|
+
link.onerror = function () { inflight.delete(url); };
|
|
193
86
|
|
|
194
|
-
|
|
195
|
-
if(!c.debug || !c.debugOverlay) return;
|
|
196
|
-
var el=document.createElement("div");
|
|
197
|
-
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";
|
|
198
|
-
function render(){
|
|
199
|
-
var s=stats.snapshot();
|
|
200
|
-
el.innerHTML="<div style='font-weight:700;margin-bottom:6px'>HyperInstant "+s.version+"</div>"+
|
|
201
|
-
"<div>Loaded: <b>"+(s.prefetched+s.prerendered)+"</b> (P:"+s.prefetched+" R:"+s.prerendered+")</div>"+
|
|
202
|
-
"<div>Hit-rate: <b>"+Math.round(s.hitRate*100)+"%</b></div>"+
|
|
203
|
-
"<div>HoverDelay: <b>"+c.hoverDelay+"ms</b></div>"+
|
|
204
|
-
"<div style='opacity:.8;margin-top:6px'>Clicks: "+s.clicked+" • Seen: "+s.seen+"</div>";
|
|
205
|
-
}
|
|
206
|
-
render();
|
|
207
|
-
document.addEventListener("DOMContentLoaded", function(){ document.body.appendChild(el); });
|
|
208
|
-
setInterval(render, 1200);
|
|
87
|
+
document.head.appendChild(link);
|
|
209
88
|
}
|
|
210
89
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if(
|
|
216
|
-
if(a.getAttribute("data-no-instant")!==null) return
|
|
217
|
-
if(a.getAttribute("data-hyperinstant")==="off") return null;
|
|
218
|
-
if(a.target && a.target!=="_self") return null;
|
|
219
|
-
return a;
|
|
220
|
-
}
|
|
90
|
+
var t = null;
|
|
91
|
+
function onHover(e) {
|
|
92
|
+
var a = e.target && e.target.closest ? e.target.closest("a[href]") : null;
|
|
93
|
+
if (!a || !a.href) return;
|
|
94
|
+
if (a.target && a.target !== "_self") return;
|
|
95
|
+
if (a.getAttribute("data-no-instant") !== null) return;
|
|
221
96
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if(signal==="mousedown") s=100;
|
|
225
|
-
else if(signal==="touchstart") s=96;
|
|
226
|
-
else if(signal==="focusin") s=86;
|
|
227
|
-
else if(signal==="mouseover") s=72;
|
|
228
|
-
else if(signal==="viewport") s=40;
|
|
229
|
-
if(inNav(a)) s+=8;
|
|
230
|
-
if(String(a.getAttribute("data-hyperinstant-priority")||"").toLowerCase()==="high") s+=12;
|
|
231
|
-
return Math.min(110,s);
|
|
97
|
+
clearTimeout(t);
|
|
98
|
+
t = setTimeout(function () { enqueue(a.href); }, hoverDelay);
|
|
232
99
|
}
|
|
233
100
|
|
|
234
|
-
function
|
|
235
|
-
var
|
|
101
|
+
function onImmediate(e) {
|
|
102
|
+
var a = e.target && e.target.closest ? e.target.closest("a[href]") : null;
|
|
103
|
+
if (!a || !a.href) return;
|
|
104
|
+
if (a.target && a.target !== "_self") return;
|
|
105
|
+
if (a.getAttribute("data-no-instant") !== null) return;
|
|
106
|
+
enqueue(a.href);
|
|
107
|
+
}
|
|
236
108
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
stats.seen++;
|
|
242
|
-
if(shouldDeny(url,c)){ stats.denied++; return; }
|
|
109
|
+
document.addEventListener("mouseover", onHover, { passive: true });
|
|
110
|
+
document.addEventListener("touchstart", onImmediate, { passive: true });
|
|
111
|
+
document.addEventListener("mousedown", onImmediate, { passive: true });
|
|
112
|
+
document.addEventListener("focusin", onImmediate, { passive: true });
|
|
243
113
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
hoverTimer=setTimeout(function(){
|
|
249
|
-
scheduler.enqueue(url, sc);
|
|
250
|
-
tuner.onEnqueue();
|
|
251
|
-
}, delay);
|
|
252
|
-
}
|
|
114
|
+
window.addEventListener("pageshow", function () {
|
|
115
|
+
usedPrefetch = 0;
|
|
116
|
+
usedPrerender = 0;
|
|
117
|
+
});
|
|
253
118
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
stats.markClick(a.href);
|
|
258
|
-
tuner.onClick();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
(c.events||["mouseover","touchstart","mousedown","focusin"]).forEach(function(evt){
|
|
262
|
-
document.addEventListener(evt, onIntent, {passive:true, capture:true});
|
|
119
|
+
if (debug) {
|
|
120
|
+
console.log("[HyperInstant] v1.1.0 legacy-fast loaded", {
|
|
121
|
+
strategy: strategy, hoverDelay: hoverDelay, maxPrefetch: maxPrefetch, maxPrerender: maxPrerender
|
|
263
122
|
});
|
|
264
|
-
document.addEventListener("click", onClick, {passive:true, capture:true});
|
|
265
|
-
|
|
266
|
-
if (String(c.observe||"intent").toLowerCase().includes("viewport") && "IntersectionObserver" in window) {
|
|
267
|
-
var io=new IntersectionObserver(function(entries){
|
|
268
|
-
entries.forEach(function(ent){
|
|
269
|
-
if(!ent.isIntersecting) return;
|
|
270
|
-
var a=eligibleLink(ent.target);
|
|
271
|
-
if(!a) return;
|
|
272
|
-
var url=a.href;
|
|
273
|
-
if(shouldDeny(url,c)) return;
|
|
274
|
-
scheduler.enqueue(url, score("viewport", a));
|
|
275
|
-
io.unobserve(ent.target);
|
|
276
|
-
});
|
|
277
|
-
}, {root:null, rootMargin:"200px 0px", threshold:0.01});
|
|
278
|
-
|
|
279
|
-
function scan(){
|
|
280
|
-
document.querySelectorAll("a[data-hyperinstant-viewport='on'][href]").forEach(function(a){ io.observe(a); });
|
|
281
|
-
}
|
|
282
|
-
scan();
|
|
283
|
-
var mo=new MutationObserver(function(){ scan(); });
|
|
284
|
-
mo.observe(document.documentElement, {childList:true, subtree:true});
|
|
285
|
-
}
|
|
286
123
|
}
|
|
287
|
-
|
|
288
|
-
// init
|
|
289
|
-
var c = applyModeDefaults(cfg);
|
|
290
|
-
var adapter = detectAdapter(c.adapter);
|
|
291
|
-
c = adapter(c);
|
|
292
|
-
|
|
293
|
-
var stats = new Stats();
|
|
294
|
-
var tuner = createTuner(c, stats);
|
|
295
|
-
var scheduler = createScheduler(c, stats);
|
|
296
|
-
|
|
297
|
-
if (c.debug) { window.HyperInstantStats = stats; console.log("[HyperInstant] v1.0.1 loaded", c); }
|
|
298
|
-
startObservers(c, scheduler, stats, tuner);
|
|
299
|
-
maybeOverlay(c, stats);
|
|
300
124
|
})();
|
package/dist/hyperinstant.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
/*! HyperInstant v1.0.1 | MIT | Putia Web */ (function(){if (window.__HYPERINSTANT_LOADED__) return; window.__HYPERINSTANT_LOADED__ = true; var cfg = window.HyperInstant ||{}; 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 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 supportsPrerender(){try{return "relList" in HTMLLinkElement.prototype && HTMLLinkElement.prototype.relList.supports && HTMLLinkElement.prototype.relList.supports("prerender");}catch(e){return false;}} function inNav(a){try{return !!a.closest("nav, header, [role='navigation'], .header, .site-header");}catch(e){return false;}} function applyModeDefaults(c){var mode = String(c.mode||"safe").toLowerCase(); var out = Object.assign({}, c); if (mode==="aggressive"){out.maxPrefetch = out.maxPrefetch ?? 10; out.maxPrerender = out.maxPrerender ?? 2; out.observe = out.observe ?? "intent+viewport";} else if (mode==="balanced"){out.maxPrefetch = out.maxPrefetch ?? 6; out.maxPrerender = out.maxPrerender ?? 1; out.observe = out.observe ?? "intent";} else{out.maxPrefetch = out.maxPrefetch ?? 4; out.maxPrerender = out.maxPrerender ?? 0; out.observe = out.observe ?? "intent";} out.hoverDelay = out.hoverDelay ?? 65; out.debug = !!out.debug; out.debugOverlay = !!out.debugOverlay; out.budget = out.budget ?? "auto"; out.events = out.events ?? ["mouseover","touchstart","mousedown","focusin"]; out.deny = out.deny ?? []; return out;} function hasMeta(name){return !!document.querySelector('meta[name="'+name+'"]');} function shopifyAdapter(c){var deny=new Set([].concat(c.deny||[])); ["/cart","/checkout","/account","/account/login","/account/register","/account/logout","/search","cart/add","cart/change","cart/update","?variant=","variant=","sections="].forEach(function(x){deny.add(x);}); return Object.assign({}, c,{deny:Array.from(deny)});} function wooAdapter(c){var deny=new Set([].concat(c.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({}, c,{deny:Array.from(deny)});} function nopAdapter(c){var deny=new Set([].concat(c.deny||[])); ["/shoppingcart","/checkout","/login","/register","/logout","/customer","addproducttocart","updateshoppingcart","updatecart","deleteitem","/wishlist","/compareproducts","/search","?q="].forEach(function(x){deny.add(x);}); return Object.assign({}, c,{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(x){return x;}; 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(x){return x;};} var AUTO_KEYWORDS=["logout","logoff","signin","sign-in","login","register","account","checkout","cart","basket","order","orders","pay","payment","add-to-cart","addtocart","addproducttocart","remove","update","change","delete","wc-ajax","admin-ajax","ajax","sections=","variant=","token","returnurl","redirect"]; var AUTO_PATHS=["/checkout","/cart","/shoppingcart","/account","/my-account","/logout","/login","/register"]; var AUTO_PARAMS=["add","remove","update","change","delete","token","variant","sections","returnurl","redirect"]; function shouldDeny(url, c){if (!url) return true; if (!isSameOrigin(url)) return true; var u = String(url).toLowerCase(); var denyList = c.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_PATHS.length;j++){var ap=AUTO_PATHS[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 Map();} Stats.prototype.markLoaded=function(url,type){if(!this._loaded.has(url)){this._loaded.set(url,{type:type,t:Date.now()}); if(type==="prerender") this.prerendered++; else this.prefetched++;}}; Stats.prototype.isLoaded=function(url){return this._loaded.has(url);}; 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:"1.0.1", 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(c, stats){var queue=[]; var inFlight=new Set(); var loaded=new Set(); var usedPrefetch=0, usedPrerender=0; function computeBudgets(){if (c.budget && typeof c.budget==="object"){return{maxPrefetch: c.budget.maxPrefetch ?? c.maxPrefetch, maxPrerender: c.budget.maxPrerender ?? c.maxPrerender};} var ci=getConn(); var mp=c.maxPrefetch, mr=c.maxPrerender; 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=Math.min(mr,0); mp=Math.min(mp,4);} if (typeof ci.rtt==="number" && ci.rtt>300){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); if (!supportsPrerender()) mr=0; return{maxPrefetch: mp, maxPrerender: mr};} function enqueue(rawUrl, score){var url=normalizeUrl(rawUrl); if(!url) return; stats.enqueued++; queue.push({url:url,score:score,t:Date.now()}); queue.sort(function(a,b){return b.score-a.score;}); pump();} function pump(){var b=computeBudgets(); while(queue.length){var n=queue.shift(); var url=n.url, score=n.score; if(loaded.has(url) || stats.isLoaded(url)) continue; if(inFlight.has(url)) continue; var wantPrerender = b.maxPrerender>usedPrerender && score>=90; var wantPrefetch = b.maxPrefetch>usedPrefetch; if(!wantPrerender && !wantPrefetch) break; var type = wantPrerender ? "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 = (score>=90) ? "high" : (score>=70 ? "auto" : "low"); link.onload=function(){loaded.add(url); inFlight.delete(url); stats.markLoaded(url,type);}; link.onerror=function(){inFlight.delete(url);}; document.head.appendChild(link);}} window.addEventListener("pageshow", function(){usedPrefetch=0; usedPrerender=0;}); return{enqueue: enqueue};} function createTuner(c, stats){var intentCount=0; function maybeTune(){var total = stats.prefetched + stats.prerendered; if(total<12) return; var hr = stats.hitRate; if (hr>=0.35) c.hoverDelay = clamp((c.hoverDelay||65)-10, 25, 140); else if (hr<=0.15) c.hoverDelay = clamp((c.hoverDelay||65)+15, 25, 180);} return{onEnqueue:function(){intentCount++; if(intentCount%20===0) maybeTune();}, onClick:function(){}};} function maybeOverlay(c, stats){if(!c.debug || !c.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>"+c.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=100; else if(signal==="touchstart") s=96; else if(signal==="focusin") s=86; else if(signal==="mouseover") s=72; else if(signal==="viewport") s=40; if(inNav(a)) s+=8; if(String(a.getAttribute("data-hyperinstant-priority")||"").toLowerCase()==="high") s+=12; return Math.min(110,s);} function startObservers(c, 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,c)){stats.denied++; return;} var sig=e.type; var sc=score(sig,a); clearTimeout(hoverTimer); var delay = (sig==="mouseover") ? c.hoverDelay : 0; hoverTimer=setTimeout(function(){scheduler.enqueue(url, sc); tuner.onEnqueue();}, delay);} function onClick(e){var a=eligibleLink(e.target); if(!a) return; stats.markClick(a.href); tuner.onClick();} (c.events||["mouseover","touchstart","mousedown","focusin"]).forEach(function(evt){document.addEventListener(evt, onIntent,{passive:true, capture:true});}); document.addEventListener("click", onClick,{passive:true, capture:true}); if (String(c.observe||"intent").toLowerCase().includes("viewport") && "IntersectionObserver" in window){var io=new IntersectionObserver(function(entries){entries.forEach(function(ent){if(!ent.isIntersecting) return; var a=eligibleLink(ent.target); if(!a) return; var url=a.href; if(shouldDeny(url,c)) return; scheduler.enqueue(url, score("viewport", a)); io.unobserve(ent.target);});},{root:null, rootMargin:"200px 0px", threshold:0.01}); function scan(){document.querySelectorAll("a[data-hyperinstant-viewport='on'][href]").forEach(function(a){io.observe(a);});} scan(); var mo=new MutationObserver(function(){scan();}); mo.observe(document.documentElement,{childList:true, subtree:true});}} // init var c = applyModeDefaults(cfg); var adapter = detectAdapter(c.adapter); c = adapter(c); var stats = new Stats(); var tuner = createTuner(c, stats); var scheduler = createScheduler(c, stats); if (c.debug){window.HyperInstantStats = stats; console.log("[HyperInstant] v1.0.1 loaded", c);} startObservers(c, scheduler, stats, tuner); maybeOverlay(c, stats);})();
|
|
1
|
+
/*! HyperInstant v1.1.0 (legacy-fast, 0.1.0 behavior) | MIT | Putia Web */ (function (){if (window.__HYPERINSTANT_LOADED__) return; window.__HYPERINSTANT_LOADED__ = true; var cfg = window.HyperInstant ||{}; var strategy = (cfg.strategy || "auto").toLowerCase(); var hoverDelay = (typeof cfg.hoverDelay === "number") ? cfg.hoverDelay : 65; var maxPrefetch = (typeof cfg.maxPrefetch === "number") ? cfg.maxPrefetch : 6; var maxPrerender = (typeof cfg.maxPrerender === "number") ? cfg.maxPrerender : 2; var deny = Array.isArray(cfg.deny) ? cfg.deny : []; var debug = !!cfg.debug; var prefetched = new Set(); var prerendered = new Set(); var inflight = new Set(); var usedPrefetch = 0; var usedPrerender = 0; 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 normalizeUrl(url){try{var u = new URL(url, location.href); u.hash = ""; return u.toString();} catch (e){return "";}} function isSameOrigin(url){try{return new URL(url, location.href).origin === location.origin;} catch (e){return false;}} function isDenied(url){if (!url) return true; if (!isSameOrigin(url)) return true; var u = String(url); for (var i = 0; i < deny.length; i++){var d = deny[i]; if (d && u.indexOf(String(d)) !== -1) return true;} return false;} function enqueue(url){url = normalizeUrl(url); if (!url) return; if (isDenied(url)) return; if (prefetched.has(url) || prerendered.has(url) || inflight.has(url)) return; var canPrerender = supportsPrerender() && (usedPrerender < maxPrerender); var canPrefetch = (usedPrefetch < maxPrefetch); var rel = "prefetch"; if (strategy === "prefetch") rel = "prefetch"; else if (strategy === "prerender") rel = canPrerender ? "prerender" : "prefetch"; else rel = canPrerender ? "prerender" : "prefetch"; if (rel === "prefetch" && !canPrefetch) return; if (rel === "prerender" && !canPrerender) rel = canPrefetch ? "prefetch" : ""; if (!rel) return; inflight.add(url); var link = document.createElement("link"); link.rel = rel; link.href = url; link.as = "document"; if (rel === "prerender") usedPrerender++; else usedPrefetch++; link.onload = function (){inflight.delete(url); if (rel === "prerender") prerendered.add(url); else prefetched.add(url); if (debug) console.log("[HyperInstant]", rel, url);}; link.onerror = function (){inflight.delete(url);}; document.head.appendChild(link);} var t = null; function onHover(e){var a = e.target && e.target.closest ? e.target.closest("a[href]") : null; if (!a || !a.href) return; if (a.target && a.target !== "_self") return; if (a.getAttribute("data-no-instant") !== null) return; clearTimeout(t); t = setTimeout(function (){enqueue(a.href);}, hoverDelay);} function onImmediate(e){var a = e.target && e.target.closest ? e.target.closest("a[href]") : null; if (!a || !a.href) return; if (a.target && a.target !== "_self") return; if (a.getAttribute("data-no-instant") !== null) return; enqueue(a.href);} document.addEventListener("mouseover", onHover,{passive: true}); document.addEventListener("touchstart", onImmediate,{passive: true}); document.addEventListener("mousedown", onImmediate,{passive: true}); document.addEventListener("focusin", onImmediate,{passive: true}); window.addEventListener("pageshow", function (){usedPrefetch = 0; usedPrerender = 0;}); if (debug){console.log("[HyperInstant] v1.1.0 legacy-fast loaded",{strategy: strategy, hoverDelay: hoverDelay, maxPrefetch: maxPrefetch, maxPrerender: maxPrerender});}})();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyperinstant",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "HyperInstant legacy-fast build (same behavior as 0.1.0), packaged as 1.1.0.",
|
|
5
5
|
"main": "dist/hyperinstant.min.js",
|
|
6
6
|
"module": "dist/hyperinstant.js",
|
|
7
7
|
"files": [
|
|
@@ -12,20 +12,8 @@
|
|
|
12
12
|
"prefetch",
|
|
13
13
|
"prerender",
|
|
14
14
|
"instant-page",
|
|
15
|
-
"web-performance"
|
|
16
|
-
"shopify",
|
|
17
|
-
"woocommerce",
|
|
18
|
-
"nopcommerce",
|
|
19
|
-
"adaptive",
|
|
20
|
-
"navigation"
|
|
15
|
+
"web-performance"
|
|
21
16
|
],
|
|
22
17
|
"author": "Putia Web",
|
|
23
|
-
"license": "MIT"
|
|
24
|
-
"repository": {
|
|
25
|
-
"type": "git",
|
|
26
|
-
"url": "https://example.com/putiaweb/hyperinstant"
|
|
27
|
-
},
|
|
28
|
-
"bugs": {
|
|
29
|
-
"url": "https://example.com/putiaweb/hyperinstant/issues"
|
|
30
|
-
}
|
|
18
|
+
"license": "MIT"
|
|
31
19
|
}
|