@lengkapp/edge 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/edge-client.js +2 -2
- package/dist/edge-server.d.ts +17 -2
- package/dist/edge-server.js +2 -2
- package/package.json +1 -1
- package/readme.md +52 -16
package/dist/edge-client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @lengkapp/edge v0.0.
|
|
2
|
+
* @lengkapp/edge v0.0.33
|
|
3
3
|
* MIT License
|
|
4
4
|
*
|
|
5
5
|
* LengkApp Edge License
|
|
@@ -30,4 +30,4 @@
|
|
|
30
30
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
31
31
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
32
32
|
*/
|
|
33
|
-
!function(){"use strict";var e=document,t=window,n="ax-app-style",r=["@keyframes ax-spin{to{transform:rotate(360deg)}}","@keyframes ax-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}","@keyframes ax-fade-in{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:translateY(0)}}",".ax-state,.ax-state *,.ax-state *::before,.ax-state *::after{box-sizing:border-box;margin:0;padding:0}",".ax-state{","display:flex;flex-direction:column;align-items:center;justify-content:center;","gap:calc(var(--pad,1) * 12px);","width:100%;min-width:min(240px,100%);min-height:240px;","padding:calc(var(--pad,1) * 20px);","background:inherit;","color:var(--fg2,#71717a);","font-family:var(--font,system-ui,-apple-system,sans-serif);","font-size:var(--fs,14px);line-height:1.45;text-align:center;","transition:var(--t,all .25s ease);","-webkit-user-select:none;user-select:none;","}",".ax-state--loading,.ax-state--skeleton{animation:ax-fade-in 220ms ease-out both}",".ax-state svg{display:block;flex:0 0 auto;width:24px;height:24px}",".ax-state--loading svg{color:var(--p,#6d28d9);animation:ax-spin .85s linear infinite}",".ax-state--skeleton{align-items:stretch}",".ax-skel{display:flex;flex-direction:column;gap:calc(var(--pad,1) * 10px);width:100%}",".ax-skel-line{","height:12px;border-radius:calc(var(--br,10px) / 2);","background-color:transparent;","background-image:linear-gradient(90deg,","var(--sk1,#e4e4e7) 25%,var(--sk2,#f4f4f5) 37%,var(--sk1,#e4e4e7) 63%);","background-size:400% 100%;","animation:ax-shimmer 1.4s ease-in-out infinite;","}",".ax-state-text{font-size:var(--fs,14px);color:var(--fg2,#71717a);max-width:34ch}",".ax-state--skeleton .ax-state-text{align-self:center}",".ax-state--error{color:var(--fg,#18181b);animation:ax-fade-in 200ms ease-out both}",".ax-state--error .ax-state-text{color:var(--err,#ef4444)}",".ax-state-btn{","display:inline-flex;align-items:center;justify-content:center;","width:44px;height:44px;padding:0;font:inherit;color:var(--fg,#18181b);","border:none;","background:inherit;","cursor:pointer;transition:var(--t,all .25s ease);","}",".ax-state-btn:hover{color:var(--p,#6d28d9);border-color:var(--p,#6d28d9);box-shadow:var(--sh2,0 4px 12px rgba(0,0,0,.08))}",".ax-state-btn:focus-visible{outline:2px solid var(--p,#6d28d9);outline-offset:2px}",".ax-state-btn:active{transform:scale(.96)}",".ax-state-btn svg{transition:transform .25s ease}",".ax-state-btn:hover svg{transform:rotate(-45deg)}",".ax-hide{display:none !important}","@media (prefers-reduced-motion:reduce){",".ax-state,.ax-state--loading,.ax-state--skeleton,.ax-state--error{animation:none}",".ax-state--loading svg{animation-duration:2s}",".ax-skel-line{animation:none;background-image:none;background-color:var(--sk1,#e4e4e7)}",".ax-state-btn:hover svg{transform:none}","::view-transition-old(root),::view-transition-new(root){animation:none}","}"].join("\n");!function(){if(!e.getElementById(n)){var t=e.createElement("style");t.id=n,t.appendChild(e.createTextNode(r)),(e.head||e.documentElement).appendChild(t)}}();var a,i=new Set,o=new WeakSet,s=new WeakMap,l=new WeakMap,u="[_get],[_post],[_go],[_open]",c=["_in","_out","_before","_after"];function f(t){return new URL(t,e.baseURI).href}function d(e){return new Promise(function(t){var n=new FileReader;n.onload=function(){t({name:e.name,type:e.type||"application/octet-stream",size:e.size,data:n.result})},n.onerror=function(){t({name:e.name,type:e.type,size:e.size,data:null})},n.readAsDataURL(e)})}function v(e){if(e.disabled)return Promise.resolve(void 0);var t=e.type;if("file"===t){var n=e.files;return n&&n.length?e.multiple?Promise.all(Array.prototype.map.call(n,d)):d(n[0]):Promise.resolve(e.multiple?[]:null)}return"checkbox"===t?Promise.resolve(e.checked?e.value||"on":null):"radio"===t?Promise.resolve(e.checked?e.value:null):"SELECT"===e.tagName&&e.multiple?Promise.resolve(Array.prototype.map.call(e.selectedOptions,function(e){return e.value})):Promise.resolve(e.value)}function p(t){var n,r=null,a=null;for(n=0;n<c.length;n++)if(t.hasAttribute(c[n])){r=c[n],a=t.getAttribute(c[n]);break}if(!r)return null;var i=t;if(a&&"this"!==a)try{i=e.querySelector(a)}catch(e){return null}return i&&i.parentNode?{mode:r,el:i,parent:i.parentNode}:null}function x(e,t){"_before"===e.mode||"_out"===e.mode?e.parent.insertBefore(t,e.el):e.parent.insertBefore(t,e.el.nextSibling)}function m(e){var t=l.get(e);t&&(t.node&&t.node.parentNode&&t.node.parentNode.removeChild(t.node),t.target&&t.target.removeAttribute("aria-busy"),l.delete(e))}function g(e,t){var n=e.querySelector(".ax-state-btn");n&&n.addEventListener("click",function(e){e.preventDefault(),e.stopPropagation(),k(t)})}function h(t){var n=e.createElement("div");return n.className="ax-state ax-state--error",n.setAttribute("role","alert"),n.innerHTML='<button type="button" class="ax-state-btn" aria-label="Retry"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-ccw preview-icon" aria-hidden="true"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg></button><span class="ax-state-text">'+String(t).replace(/[&<>"']/g,function(e){return{"&":"&","<":"<",">":">",'"':""","'":"'"}[e]})+"</span>",n}function b(t){if("function"==typeof e.startViewTransition)try{return void e.startViewTransition(t)}catch(e){}t()}function k(n){if(function(e){return e.hasAttribute("_go")||e.hasAttribute("_open")}(n))!function(e){var n=function(e){var t=e.hasAttribute("_go")?e.getAttribute("_go"):e.getAttribute("_open");if(!(t=(t||"").trim())||"this"===t){var n=e.getAttribute("href");n&&(t=n)}return t?f(t):""}(e);if(n)if(e.hasAttribute("_go"))t.location.assign(n);else{var r=t.open(n,"_blank","noopener,noreferrer");r&&(r.opener=null)}}(n);else{var r=n.hasAttribute("_get"),a=n.getAttribute(r?"_get":"_post");if(null!==a&&p(n)){var o=s.get(n),c=new AbortController;s.set(n,c),o&&o.abort(),m(n);var d=0,k=setTimeout(function(){s.get(n)===c&&(function(t){var n=p(t);if(!n)return null;var r=(t.getAttribute("_loader")||"spinner").toLowerCase();if("none"===r||"off"===r||"false"===r)return null;n.el.classList.add("ax-hide"),n.el.setAttribute("aria-busy","true");var a=function(t){var n="skeleton"===t,r=e.createElement("div");return r.className="ax-state ax-state--"+(n?"skeleton":"loading"),r.setAttribute("role","status"),r.setAttribute("aria-live","polite"),r.setAttribute("aria-busy","true"),r.innerHTML=n?'<div class="ax-skel" aria-hidden="true"><span class="ax-skel-line" style="width:48%"></span><span class="ax-skel-line"></span><span class="ax-skel-line" style="width:86%"></span><span class="ax-skel-line" style="width:64%"></span></div><span class="ax-state-text">Loading…</span>':'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-circle preview-icon" aria-hidden="true"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg><span class="ax-state-text">Loading…</span>',r}(r);x(n,a),l.set(t,{node:a,target:n.el})}(n),d=performance.now())},100),w=parseInt(n.getAttribute("_timeout"),10);w>0||(w=2e4);var _={method:r?"GET":"POST",credentials:"same-origin",headers:{"X-Requested-With":"XMLHttpRequest"},redirect:"follow",referrerPolicy:"same-origin",signal:c.signal},P=setTimeout(function(){c.abort()},w),T=Promise.resolve();if(!r)if(n.hasAttribute("_json"))T=function(t){var n=(t.getAttribute("_json")||"").trim();if(!n)return Promise.resolve(null);var r=n.split(",").map(function(e){return e.trim()}).filter(Boolean);if(!r.length)return Promise.resolve(null);var a=t.closest("form")||e;return Promise.all(r.map(function(t){return function(t,n){var r=e.getElementById(n);if(r&&t.contains(r))return v(r);var a=function(e,t){for(var n=e.querySelectorAll("input[name],select[name],textarea[name]"),r=[],a=0;a<n.length;a++)n[a].name===t&&r.push(n[a]);return r}(t,n);if(!a.length)return Promise.resolve(void 0);if(1===a.length)return v(a[0]);var i=a[0];if("radio"===i.type){for(var o=0;o<a.length;o++)if(a[o].checked)return Promise.resolve(a[o].value);return Promise.resolve(null)}if("checkbox"===i.type){for(var s=[],l=0;l<a.length;l++)a[l].checked&&!a[l].disabled&&s.push(a[l].value||"on");return Promise.resolve(s)}return v(i)}(a,t).then(function(e){return[t,e]})})).then(function(e){for(var t={},n=0;n<e.length;n++)void 0!==e[n][1]&&(t[e[n][0]]=e[n][1]);return JSON.stringify(t)})}(n).then(function(e){null!==e&&(_.headers["Content-Type"]="application/json",_.body=e)});else if(n.hasAttribute("_form")){var M=function(t){var n,r=t.getAttribute("_form");try{n=e.querySelector(r)||e.getElementById(r)}catch(e){n=null}return n?new FormData(n):null}(n);M&&(_.body=M)}T.then(function(){return fetch(a,_)}).then(function(e){if(clearTimeout(P),!e.ok){var t=new Error("HTTP "+e.status);throw t.status=e.status,t}return e.text()}).then(function(t){clearTimeout(k),A(function(){var r=function(){m(n),t&&function(t,n){var r=p(t);if(r){var a=e.createElement("template");a.innerHTML=n;var o,s,l=a.content,c=[],d=[];for(s=l.querySelectorAll("script"),o=0;o<s.length;o++)c.push(s[o]),s[o].parentNode.removeChild(s[o]);for(s=l.querySelectorAll(u),o=0;o<s.length;o++)d.push(s[o]);var v=r.el,x=r.parent;if("_in"===r.mode){for(;v.firstChild;)v.removeChild(v.firstChild);v.appendChild(l),v.classList.remove("ax-hide")}else"_out"===r.mode?x.replaceChild(l,v):"_before"===r.mode?(x.insertBefore(l,v),v.classList.remove("ax-hide")):(x.insertBefore(l,v.nextSibling),v.classList.remove("ax-hide"));for(o=0;o<d.length;o++)y(d[o]);!function(t){for(var n=0;n<t.length;n++){var r=t[n],a=r.getAttribute("src");if(a){if(a=f(a),i.has(a))continue;i.add(a)}for(var o=e.createElement("script"),s=0;s<r.attributes.length;s++)o.setAttribute(r.attributes[s].name,r.attributes[s].value);a||(o.text=r.text),o.async=!1,e.head.appendChild(o)}}(c)}}(n,t)};l.get(n)?b(r):r()})}).catch(function(e){var t;clearTimeout(P),clearTimeout(k),s.get(n)===c&&(t=e&&"AbortError"===e.name?"Request timed out":e&&e.message?e.message:"Request failed",e&&"AbortError"!==e.name&&console.error("[app]",e),A(function(){!function(e,t){var n=l.get(e),r=function(){if(n&&n.node&&n.node.parentNode){var r=n.node,a=h(t);r.parentNode.replaceChild(a,r),n.target&&n.target.removeAttribute("aria-busy"),l.set(e,{node:a,target:n.target}),g(a,e)}else{var i=p(e);if(!i)return;i.el.classList.add("ax-hide"),i.el.setAttribute("aria-busy","true");var o=h(t);x(i,o),l.set(e,{node:o,target:i.el}),g(o,e)}};n?b(r):r()}(n,t)}))})}}function A(e){if(0!==d){var t=Math.max(0,240-(performance.now()-d));0===t?s.get(n)===c&&e():setTimeout(function(){s.get(n)===c&&e()},t)}else s.get(n)===c&&e()}}function w(e){var t=e.getAttribute("_trigger");"load"===t||"visible"===t?k(e):e.addEventListener(t,function(t){if("click"===t.type){var n=e.tagName;"BUTTON"!==n&&"A"!==n||t.preventDefault()}k(e)})}function y(e){o.has(e)||(o.add(e),"click"!==(e.getAttribute("_trigger")||"click")&&("IntersectionObserver"in t?(a||(a=new IntersectionObserver(function(e){for(var t=0;t<e.length;t++)e[t].isIntersecting&&(a.unobserve(e[t].target),w(e[t].target))},{rootMargin:"300px"})),a.observe(e)):w(e)))}function _(e){for(var t=e.querySelectorAll(u),n=0;n<t.length;n++)y(t[n])}e.addEventListener("click",function(e){if(!e.target.closest(".ax-state")){var t=e.target.closest(u);if(t&&"click"===(t.getAttribute("_trigger")||"click")&&!(t.hasAttribute("_go")&&"A"===t.tagName&&t.getAttribute("href")&&(0!==e.button||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey))){var n=t.tagName;"BUTTON"!==n&&"A"!==n||e.preventDefault(),k(t)}}},!0),function(){for(var t=e.querySelectorAll("script[src]"),n=0;n<t.length;n++)i.add(f(t[n].src));"loading"===e.readyState?e.addEventListener("DOMContentLoaded",function(){_(e)}):_(e)}()}();
|
|
33
|
+
!function(){"use strict";var e=document,r=window,t=null;function n(e){return!!e.hasAttribute("_id")&&(e.hasAttribute("_get")||e.hasAttribute("_post")||e.hasAttribute("_go")||e.hasAttribute("_open"))}function a(){return t||(t=function(){var t=[navigator.userAgent,navigator.language,screen.width,screen.height,screen.colorDepth,(new Date).getTimezoneOffset(),navigator.hardwareConcurrency||"?",navigator.deviceMemory||"?"].join("|"),n=e.createElement("canvas"),a="",i=n.getContext&&n.getContext("2d");if(i){i.textBaseline="top",i.font="14px Arial",i.fillStyle="#f60",i.fillRect(125,1,62,20),i.fillStyle="#069",i.fillText("FP",2,15),i.fillStyle="rgba(102,204,0,0.7)",i.fillText("FP",4,17);try{a=n.toDataURL()}catch(e){a=""}}var o="";try{var s=n.getContext("webgl")||n.getContext("experimental-webgl");if(s){var u=s.getExtension("WEBGL_debug_renderer_info");o=u?s.getParameter(u.UNMASKED_VENDOR_WEBGL)+"~"+s.getParameter(u.UNMASKED_RENDERER_WEBGL):s.getParameter(s.VERSION)+"~"+s.getParameter(s.RENDERER)}}catch(e){o=""}var c=["Arial","Courier New","Times New Roman","Helvetica","Georgia","Verdana","Comic Sans MS","Trebuchet MS","Impact","Lucida Console"],l="",f=e.body||e.documentElement;if(f){for(var d=[],v=0;v<c.length;v++){var p=e.createElement("span");p.style.cssText='position:absolute;left:-9999px;visibility:hidden;font:72px "'+c[v]+'",monospace',p.textContent="mmmmmmmmmmlli",f.appendChild(p),d.push(p.offsetWidth),f.removeChild(p)}l=d.join(",")}return function(){var e=r.OfflineAudioContext||r.webkitOfflineAudioContext;if(!e)return Promise.resolve("");try{var t=new e(1,44100,44100),n=t.createOscillator();n.type="triangle",n.frequency.value=1e4;var a=t.createDynamicsCompressor();return a.threshold.value=-50,a.knee.value=40,a.ratio.value=12,a.attack.value=0,a.release.value=.25,n.connect(a),a.connect(t.destination),n.start(0),t.startRendering().then(function(e){for(var r=e.getChannelData(0),t=0,n=4500;n<5e3;n++)t+=Math.abs(r[n]);return t.toString()}).catch(function(){return""})}catch(e){return Promise.resolve("")}}().then(function(e){return function(e){if(!r.crypto||!r.crypto.subtle||!r.TextEncoder){for(var t=5381,n=0;n<e.length;n++)t=(t<<5)+t^e.charCodeAt(n);return Promise.resolve((t>>>0).toString(16))}var a=(new TextEncoder).encode(e);return r.crypto.subtle.digest("SHA-256",a).then(function(e){for(var r=new Uint8Array(e),t="",n=0;n<r.length;n++)t+=r[n].toString(16).padStart(2,"0");return t})}([t,a,o,l,e].join("||"))})}().catch(function(){return""})),t}var i="ax-app-style",o=["@keyframes ax-spin{to{transform:rotate(360deg)}}","@keyframes ax-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}","@keyframes ax-fade-in{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:translateY(0)}}",".ax-state,.ax-state *,.ax-state *::before,.ax-state *::after{box-sizing:border-box;margin:0;padding:0}",".ax-state{","display:flex;flex-direction:column;align-items:center;justify-content:center;","gap:calc(var(--pad,1) * 12px);","width:100%;min-width:min(240px,100%);min-height:240px;","padding:calc(var(--pad,1) * 20px);","background:inherit;","color:var(--fg2,#71717a);","font-family:var(--font,system-ui,-apple-system,sans-serif);","font-size:var(--fs,14px);line-height:1.45;text-align:center;","transition:var(--t,all .25s ease);","-webkit-user-select:none;user-select:none;","}",".ax-state--loading,.ax-state--skeleton{animation:ax-fade-in 220ms ease-out both}",".ax-state svg{display:block;flex:0 0 auto;width:24px;height:24px}",".ax-state--loading svg{color:var(--p,#6d28d9);animation:ax-spin .85s linear infinite}",".ax-state--skeleton{align-items:stretch}",".ax-skel{display:flex;flex-direction:column;gap:calc(var(--pad,1) * 10px);width:100%}",".ax-skel-line{","height:12px;border-radius:calc(var(--br,10px) / 2);","background-color:transparent;","background-image:linear-gradient(90deg,","var(--sk1,#e4e4e7) 25%,var(--sk2,#f4f4f5) 37%,var(--sk1,#e4e4e7) 63%);","background-size:400% 100%;","animation:ax-shimmer 1.4s ease-in-out infinite;","}",".ax-state-text{font-size:var(--fs,14px);color:var(--fg2,#71717a);max-width:34ch}",".ax-state--skeleton .ax-state-text{align-self:center}",".ax-state--error{color:var(--fg,#18181b);animation:ax-fade-in 200ms ease-out both}",".ax-state--error .ax-state-text{color:var(--err,#ef4444)}",".ax-state-btn{","display:inline-flex;align-items:center;justify-content:center;","width:44px;height:44px;padding:0;font:inherit;color:var(--fg,#18181b);","border:none;","background:inherit;","cursor:pointer;transition:var(--t,all .25s ease);","}",".ax-state-btn:hover{color:var(--p,#6d28d9);border-color:var(--p,#6d28d9);box-shadow:var(--sh2,0 4px 12px rgba(0,0,0,.08))}",".ax-state-btn:focus-visible{outline:2px solid var(--p,#6d28d9);outline-offset:2px}",".ax-state-btn:active{transform:scale(.96)}",".ax-state-btn svg{transition:transform .25s ease}",".ax-state-btn:hover svg{transform:rotate(-45deg)}",".ax-hide{display:none !important}","@media (prefers-reduced-motion:reduce){",".ax-state,.ax-state--loading,.ax-state--skeleton,.ax-state--error{animation:none}",".ax-state--loading svg{animation-duration:2s}",".ax-skel-line{animation:none;background-image:none;background-color:var(--sk1,#e4e4e7)}",".ax-state-btn:hover svg{transform:none}","::view-transition-old(root),::view-transition-new(root){animation:none}","}"].join("\n");!function(){if(!e.getElementById(i)){var r=e.createElement("style");r.id=i,r.appendChild(e.createTextNode(o)),(e.head||e.documentElement).appendChild(r)}}();var s,u=new Set,c=new WeakSet,l=new WeakMap,f=new WeakMap,d="[_get],[_post],[_go],[_open]",v=["_in","_out","_before","_after"];function p(r){return new URL(r,e.baseURI).href}function m(e){return new Promise(function(r){var t=new FileReader;t.onload=function(){r({name:e.name,type:e.type||"application/octet-stream",size:e.size,data:t.result})},t.onerror=function(){r({name:e.name,type:e.type,size:e.size,data:null})},t.readAsDataURL(e)})}function x(e){if(e.disabled)return Promise.resolve(void 0);var r=e.type;if("file"===r){var t=e.files;return t&&t.length?e.multiple?Promise.all(Array.prototype.map.call(t,m)):m(t[0]):Promise.resolve(e.multiple?[]:null)}return"checkbox"===r?Promise.resolve(e.checked?e.value||"on":null):"radio"===r?Promise.resolve(e.checked?e.value:null):"SELECT"===e.tagName&&e.multiple?Promise.resolve(Array.prototype.map.call(e.selectedOptions,function(e){return e.value})):Promise.resolve(e.value)}function g(r){var t,n=null,a=null;for(t=0;t<v.length;t++)if(r.hasAttribute(v[t])){n=v[t],a=r.getAttribute(v[t]);break}if(!n)return null;var i=r;if(a&&"this"!==a)try{i=e.querySelector(a)}catch(e){return null}return i&&i.parentNode?{mode:n,el:i,parent:i.parentNode}:null}function h(e,r){"_before"===e.mode||"_out"===e.mode?e.parent.insertBefore(r,e.el):e.parent.insertBefore(r,e.el.nextSibling)}function b(e){var r=f.get(e);r&&(r.node&&r.node.parentNode&&r.node.parentNode.removeChild(r.node),r.target&&r.target.removeAttribute("aria-busy"),f.delete(e))}function k(e,r){var t=e.querySelector(".ax-state-btn");t&&t.addEventListener("click",function(e){e.preventDefault(),e.stopPropagation(),P(r)})}function w(r){var t=e.createElement("div");return t.className="ax-state ax-state--error",t.setAttribute("role","alert"),t.innerHTML='<button type="button" class="ax-state-btn" aria-label="Retry"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-ccw preview-icon" aria-hidden="true"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg></button><span class="ax-state-text">'+String(r).replace(/[&<>"']/g,function(e){return{"&":"&","<":"<",">":">",'"':""","'":"'"}[e]})+"</span>",t}function _(r){if("function"==typeof e.startViewTransition)try{return void e.startViewTransition(r)}catch(e){}r()}function y(e,t){if(e.hasAttribute("_go"))r.location.assign(t);else{var n=r.open(t,"_blank","noopener,noreferrer");n&&(n.opener=null)}}function P(r){if(function(e){return e.hasAttribute("_go")||e.hasAttribute("_open")}(r))!function(e){var r=function(e){var r=e.hasAttribute("_go")?e.getAttribute("_go"):e.getAttribute("_open");if(!(r=(r||"").trim())||"this"===r){var t=e.getAttribute("href");t&&(r=t)}return r?p(r):""}(e);r&&(n(e)?a().then(function(t){y(e,function(e,r){return r?e+(e.indexOf("?")>=0?"&":"?")+"_did="+encodeURIComponent(r):e}(r,t))}):y(e,r))}(r);else{var t=r.hasAttribute("_get"),i=r.getAttribute(t?"_get":"_post");if(null!==i&&g(r)){var o=l.get(r),s=new AbortController;l.set(r,s),o&&o.abort(),b(r);var c=0,v=setTimeout(function(){l.get(r)===s&&(function(r){var t=g(r);if(!t)return null;var n=(r.getAttribute("_loader")||"spinner").toLowerCase();if("none"===n||"off"===n||"false"===n)return null;t.el.classList.add("ax-hide"),t.el.setAttribute("aria-busy","true");var a=function(r){var t="skeleton"===r,n=e.createElement("div");return n.className="ax-state ax-state--"+(t?"skeleton":"loading"),n.setAttribute("role","status"),n.setAttribute("aria-live","polite"),n.setAttribute("aria-busy","true"),n.innerHTML=t?'<div class="ax-skel" aria-hidden="true"><span class="ax-skel-line" style="width:48%"></span><span class="ax-skel-line"></span><span class="ax-skel-line" style="width:86%"></span><span class="ax-skel-line" style="width:64%"></span></div><span class="ax-state-text">Loading…</span>':'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-circle preview-icon" aria-hidden="true"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg><span class="ax-state-text">Loading…</span>',n}(n);h(t,a),f.set(r,{node:a,target:t.el})}(r),c=performance.now())},100),m=parseInt(r.getAttribute("_timeout"),10);m>0||(m=2e4);var P=document.querySelector('meta[name="_csrf"]')?.content,T={method:t?"GET":"POST",credentials:"same-origin",headers:P?{"x-csrf-token":P}:void 0,redirect:"follow",referrerPolicy:"same-origin",signal:s.signal},M=setTimeout(function(){s.abort()},m),S=Promise.resolve();if(!t)if(r.hasAttribute("_json"))S=function(r){var t=(r.getAttribute("_json")||"").trim();if(!t)return Promise.resolve(null);var n=t.split(",").map(function(e){return e.trim()}).filter(Boolean);if(!n.length)return Promise.resolve(null);var a=r.closest("form")||e;return Promise.all(n.map(function(r){return function(r,t){var n=e.getElementById(t);if(n&&r.contains(n))return x(n);var a=function(e,r){for(var t=e.querySelectorAll("input[name],select[name],textarea[name]"),n=[],a=0;a<t.length;a++)t[a].name===r&&n.push(t[a]);return n}(r,t);if(!a.length)return Promise.resolve(void 0);if(1===a.length)return x(a[0]);var i=a[0];if("radio"===i.type){for(var o=0;o<a.length;o++)if(a[o].checked)return Promise.resolve(a[o].value);return Promise.resolve(null)}if("checkbox"===i.type){for(var s=[],u=0;u<a.length;u++)a[u].checked&&!a[u].disabled&&s.push(a[u].value||"on");return Promise.resolve(s)}return x(i)}(a,r).then(function(e){return[r,e]})})).then(function(e){for(var r={},t=0;t<e.length;t++)void 0!==e[t][1]&&(r[e[t][0]]=e[t][1]);return JSON.stringify(r)})}(r).then(function(e){null!==e&&(T.headers["Content-Type"]="application/json",T.body=e)});else if(r.hasAttribute("_form")){var C=function(r){var t,n=r.getAttribute("_form");try{t=e.querySelector(n)||e.getElementById(n)}catch(e){t=null}return t?new FormData(t):null}(r);C&&(T.body=C)}n(r)&&(S=Promise.all([S,a()]).then(function(e){var r=e[1];r&&(T.headers["X-DeviceId"]=r)})),S.then(function(){return fetch(i,T)}).then(function(e){if(clearTimeout(M),!e.ok){var r=new Error("HTTP "+e.status);throw r.status=e.status,r}return e.text()}).then(function(t){clearTimeout(v),E(function(){var n=function(){b(r),t&&function(r,t){var n=g(r);if(n){var a=e.createElement("template");a.innerHTML=t;var i,o,s=a.content,c=[],l=[];for(o=s.querySelectorAll("script"),i=0;i<o.length;i++)c.push(o[i]),o[i].parentNode.removeChild(o[i]);for(o=s.querySelectorAll(d),i=0;i<o.length;i++)l.push(o[i]);var f=n.el,v=n.parent;if("_in"===n.mode){for(;f.firstChild;)f.removeChild(f.firstChild);f.appendChild(s),f.classList.remove("ax-hide")}else"_out"===n.mode?v.replaceChild(s,f):"_before"===n.mode?(v.insertBefore(s,f),f.classList.remove("ax-hide")):(v.insertBefore(s,f.nextSibling),f.classList.remove("ax-hide"));for(i=0;i<l.length;i++)A(l[i]);!function(r){for(var t=0;t<r.length;t++){var n=r[t],a=n.getAttribute("src");if(a){if(a=p(a),u.has(a))continue;u.add(a)}for(var i=e.createElement("script"),o=0;o<n.attributes.length;o++)i.setAttribute(n.attributes[o].name,n.attributes[o].value);a||(i.text=n.text),i.async=!1,e.head.appendChild(i)}}(c)}}(r,t)};f.get(r)?_(n):n()})}).catch(function(e){var t;clearTimeout(M),clearTimeout(v),l.get(r)===s&&(t=e&&"AbortError"===e.name?"Request timed out":e&&e.message?e.message:"Request failed",e&&"AbortError"!==e.name&&console.error("[app]",e),E(function(){!function(e,r){var t=f.get(e),n=function(){if(t&&t.node&&t.node.parentNode){var n=t.node,a=w(r);n.parentNode.replaceChild(a,n),t.target&&t.target.removeAttribute("aria-busy"),f.set(e,{node:a,target:t.target}),k(a,e)}else{var i=g(e);if(!i)return;i.el.classList.add("ax-hide"),i.el.setAttribute("aria-busy","true");var o=w(r);h(i,o),f.set(e,{node:o,target:i.el}),k(o,e)}};t?_(n):n()}(r,t)}))})}}function E(e){if(0!==c){var t=Math.max(0,240-(performance.now()-c));0===t?l.get(r)===s&&e():setTimeout(function(){l.get(r)===s&&e()},t)}else l.get(r)===s&&e()}}function T(e){var r=e.getAttribute("_trigger");"load"===r||"visible"===r?P(e):e.addEventListener(r,function(r){if("click"===r.type){var t=e.tagName;"BUTTON"!==t&&"A"!==t||r.preventDefault()}P(e)})}function A(e){c.has(e)||(c.add(e),"click"!==(e.getAttribute("_trigger")||"click")&&("IntersectionObserver"in r?(s||(s=new IntersectionObserver(function(e){for(var r=0;r<e.length;r++)e[r].isIntersecting&&(s.unobserve(e[r].target),T(e[r].target))},{rootMargin:"300px"})),s.observe(e)):T(e)))}function M(e){for(var r=e.querySelectorAll(d),t=0;t<r.length;t++)A(r[t])}e.addEventListener("click",function(e){if(!e.target.closest(".ax-state")){var r=e.target.closest(d);if(r&&"click"===(r.getAttribute("_trigger")||"click")&&!(r.hasAttribute("_go")&&"A"===r.tagName&&r.getAttribute("href")&&(0!==e.button||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey))){var t=r.tagName;"BUTTON"!==t&&"A"!==t||e.preventDefault(),P(r)}}},!0),function(){for(var r=e.querySelectorAll("script[src]"),t=0;t<r.length;t++)u.add(p(r[t].src));e.querySelector("[_id]")&&a(),"loading"===e.readyState?e.addEventListener("DOMContentLoaded",function(){M(e)}):M(e)}()}();
|
package/dist/edge-server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @lengkapp/edge v0.0.
|
|
2
|
+
* @lengkapp/edge v0.0.33
|
|
3
3
|
* MIT License
|
|
4
4
|
*
|
|
5
5
|
* LengkApp Edge License
|
|
@@ -36,6 +36,12 @@
|
|
|
36
36
|
* JSX runtime *
|
|
37
37
|
* ------------------------------------------------------------------ */
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Runtime value is `Symbol('Fragment')`. The function signature is a
|
|
41
|
+
* type-level shim so `<></>` works with the automatic JSX runtime.
|
|
42
|
+
* Never called directly — `jsx(Fragment, …)` passes it through and
|
|
43
|
+
* `renderToString` compares by identity.
|
|
44
|
+
*/
|
|
39
45
|
export const Fragment: (props: { children?: any }) => JSXNode;
|
|
40
46
|
|
|
41
47
|
export interface JSXNode {
|
|
@@ -209,11 +215,20 @@ export class Edge {
|
|
|
209
215
|
}
|
|
210
216
|
|
|
211
217
|
/* ---------------- JSX typing for this runtime ---------------- */
|
|
218
|
+
type ComponentReturn =
|
|
219
|
+
| JSXNode
|
|
220
|
+
| string
|
|
221
|
+
| number
|
|
222
|
+
| boolean
|
|
223
|
+
| null
|
|
224
|
+
| undefined
|
|
225
|
+
| ComponentReturn[]
|
|
226
|
+
| Promise<ComponentReturn>;
|
|
212
227
|
|
|
213
228
|
declare global {
|
|
214
229
|
namespace JSX {
|
|
215
230
|
type Element = any;
|
|
216
|
-
type ElementType = string | symbol | ((props: any) =>
|
|
231
|
+
type ElementType = string | symbol | ((props: any) => ComponentReturn);
|
|
217
232
|
interface IntrinsicElements { [elemName: string]: any; }
|
|
218
233
|
interface ElementChildrenAttribute { children: {}; }
|
|
219
234
|
}
|
package/dist/edge-server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @lengkapp/edge v0.0.
|
|
2
|
+
* @lengkapp/edge v0.0.33
|
|
3
3
|
* MIT License
|
|
4
4
|
*
|
|
5
5
|
* LengkApp Edge License
|
|
@@ -30,4 +30,4 @@
|
|
|
30
30
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
31
31
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
32
32
|
*/
|
|
33
|
-
var t=Object.defineProperty,e=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,r=(e,n)=>{for(var o in n)t(e,o,{get:n[o],enumerable:!0})},i=(r,i,s,c)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let a of n(i))o.call(r,a)||a===s||t(r,a,{get:()=>i[a],enumerable:!(c=e(i,a))||c.enumerable});return r},s=e=>i(t({},"__esModule",{value:!0}),e),c={};function a(t){const e=[],n=t.length;let o=1;for(let r=1;r<=n;r++)r!==n&&47!==t.charCodeAt(r)||(r>o&&e.push(t.slice(o,r)),o=r+1);return e}function l(t){const e=t.length;return e>1&&47===t.charCodeAt(e-1)?t.slice(0,-1):t}r(c,{Context:()=>_,Edge:()=>Edge,Fragment:()=>Fragment,jsx:()=>jsx,renderToString:()=>renderToString,useCtx:()=>E}),module.exports=s(c);var u=new Set(["__proto__","constructor","prototype"]);function f(t){return"string"==typeof t&&!u.has(t)}function h(t){const e=t.length;let n=0;for(;n<e;){const e=t.charCodeAt(n);if(38===e||60===e||62===e||34===e||39===e)break;n++}if(n===e)return t;let o=t.slice(0,n),r=n;for(;n<e;n++){const e=t.charCodeAt(n);let i;if(38===e)i="&";else if(60===e)i="<";else if(62===e)i=">";else if(34===e)i=""";else{if(39!==e)continue;i="'"}n>r&&(o+=t.slice(r,n)),o+=i,r=n+1}return r<e&&(o+=t.slice(r)),o}function p(t){return t>=65&&t<=90||t>=97&&t<=122}function d(t){return t>=48&&t<=57}function y(t){const e=t.length;if(0===e||!p(t.charCodeAt(0)))return!1;for(let n=1;n<e;n++){const e=t.charCodeAt(n);if(!p(e)&&!d(e)&&45!==e)return!1}return!0}function g(t){const e=t.length;if(0===e)return!1;const n=t.charCodeAt(0);if(!p(n)&&95!==n&&58!==n)return!1;for(let n=1;n<e;n++){const e=t.charCodeAt(n);if(!p(e)&&!d(e)&&95!==e&&58!==e&&46!==e&&45!==e)return!1}return!0}function w(t){const e=t.charCodeAt(0),n=t.charCodeAt(1);return!(111!==e&&79!==e||110!==n&&78!==n)}var Fragment=Symbol("Fragment");function jsx(t,e,...n){return{type:t,props:e||{},children:n,__isJSX:!0}}var m=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),C=new Set(["allowfullscreen","async","autofocus","autoplay","checked","controls","default","defer","disabled","formnovalidate","hidden","inert","ismap","itemscope","loop","multiple","muted","nomodule","novalidate","open","playsinline","readonly","required","reversed","selected"]),b=new Set(["animation-iteration-count","border-image-outset","border-image-slice","border-image-width","box-flex","box-flex-group","box-ordinal-group","column-count","columns","flex","flex-grow","flex-positive","flex-shrink","flex-negative","flex-order","grid-row","grid-row-end","grid-row-span","grid-row-start","grid-column","grid-column-end","grid-column-span","grid-column-start","font-weight","line-clamp","line-height","opacity","order","orphans","tab-size","widows","z-index","zoom","fill-opacity","flood-opacity","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-miterlimit","stroke-opacity","stroke-width"]),T=new Map,O=2e3;function x(t){const e=T.get(t);if(void 0!==e)return e;const n=t.replace(/([A-Z])/g,"-$1").toLowerCase().replace(/^-/,"");return T.size>=O&&T.clear(),T.set(t,n),n}function S(t){if(!t||"object"!=typeof t)return"";let e="";for(const n in t){if(!Object.prototype.hasOwnProperty.call(t,n))continue;const o=x(n);let r=t[n];"number"!=typeof r||b.has(o)||(r+="px"),e&&(e+=";"),e+=o+":"+r}return e}var A=new Set(["href","src","xlink:href","action","formaction"]),v=[];function E(){const t=v[v.length-1];if(!t)throw new Error("useCtx() must be called synchronously at the top of a component (before any await). It is not available after an await, nor outside a render.");return t}async function renderToString(t,e){if(void 0===e&&(e=v[v.length-1]),null==t||"boolean"==typeof t)return"";const n=typeof t;if("string"===n)return h(t);if("number"===n)return h(String(t));if("object"===n&&"function"==typeof t.then)return renderToString(await t,e);if(Array.isArray(t)){let n="";for(let o=0;o<t.length;o++)n+=await renderToString(t[o],e);return n}if(!t.__isJSX)return h(String(t));const{type:o,props:r,children:i}=t;if(o===Fragment){let t="";for(let n=0;n<i.length;n++)t+=await renderToString(i[n],e);return t}if("symbol"==typeof o)return"";if("function"==typeof o){let t;if(e){v.push(e);try{t=o({...r,children:i})}finally{v.pop()}}else t=o({...r,children:i});return renderToString(await t,e)}if("string"!=typeof o||!y(o))return"";const s=m.has(o);let c="";for(const t in r){if(!Object.prototype.hasOwnProperty.call(r,t))continue;if("children"===t||"key"===t||"ref"===t)continue;const e=r[t];if(null==e||!1===e)continue;if("dangerouslySetInnerHTML"===t)continue;let n=t;if("className"===t||"class"===t?n="class":"htmlFor"!==t&&"for"!==t||(n="for"),f(n)&&!w(n)&&g(n)){if(A.has(n)){const t=String(e).replace(/[\u0000-\u0020]/g,"").toLowerCase();if(t.startsWith("javascript:")||t.startsWith("vbscript:")||t.startsWith("data:text/html"))continue}if(C.has(n))!0===e&&(c+=" "+n);else if(!0!==e){if("style"===n&&"object"==typeof e){const t=S(e);t&&(c+=' style="'+h(t)+'"');continue}c+=" "+n+'="'+h(String(e))+'"'}else c+=" "+n+'="true"'}}let a="";const l=r.dangerouslySetInnerHTML;if(l&&null!=l.__html)a=l.__html;else for(let t=0;t<i.length;t++)a+=await renderToString(i[t],e);return s?"<"+o+c+"/>":"<"+o+c+">"+a+"</"+o+">"}var j=Object.freeze([["X-Content-Type-Options","nosniff"],["X-Frame-Options","DENY"],["Referrer-Policy","strict-origin-when-cross-origin"],["Permissions-Policy","geolocation=(), microphone=(), camera=()"]]),P=Object.freeze(Object.create(null)),_=class{constructor(t,e,n,o={},r=null){this.req=t,this.env=e,this.executionCtx=n,this.params=o,this.status=200,this.headers=new Headers(j),this.t=null,this.url=r||new URL(t.url)}o(){if(null!==this.t)return this.t;const t=this.req.headers.get("Cookie");if(!t)return this.t=P,P;const e=Object.create(null);let n=0;const o=t.length;for(;n<o;){let r=t.indexOf(";",n);-1===r&&(r=o);let i=n,s=r;for(;i<s&&32===t.charCodeAt(i);)i++;for(;s>i&&32===t.charCodeAt(s-1);)s--;if(s>i){const n=t.indexOf("=",i);if(n>i&&n<s)try{const o=decodeURIComponent(t.slice(i,n));f(o)&&(e[o]=decodeURIComponent(t.slice(n+1,s)))}catch{}}n=r+1}return this.t=e,e}getCookie(t){const e=this.o()[t];return void 0===e?null:e}get query(){return this.url.searchParams}setCookie(t,e,n={}){if("string"!=typeof t||!f(t)||/[\r\n;=]/.test(t))return;let o=encodeURIComponent(t)+"="+encodeURIComponent(e);n.path&&(o+="; Path="+n.path),n.domain&&(o+="; Domain="+n.domain),void 0!==n.maxAge&&(o+="; Max-Age="+n.maxAge),n.expires&&(o+="; Expires="+n.expires.toUTCString()),n.secure&&(o+="; Secure"),n.httpOnly&&(o+="; HttpOnly"),n.sameSite&&(o+="; SameSite="+n.sameSite),this.headers.append("Set-Cookie",o)}deleteCookie(t,e={}){this.setCookie(t,"",{...e,maxAge:0,expires:new Date(0)})}i(t){if(!t)return this.headers;const e=Object.keys(t);if(0===e.length)return this.headers;const n=new Headers(this.headers);for(let o=0;o<e.length;o++)n.set(e[o],t[e[o]]);return n}text(t,e=this.status,n={}){const o=this.i(n);return o.has("Content-Type")||o.set("Content-Type","text/plain; charset=utf-8"),new Response(t,{status:e,headers:o})}json(t,e=this.status,n={}){const o=this.i(n);return o.has("Content-Type")||o.set("Content-Type","application/json; charset=utf-8"),new Response(JSON.stringify(t),{status:e,headers:o})}async html(t,e=this.status,n={}){const o=this.i(n);let r;return o.has("Content-Type")||o.set("Content-Type","text/html; charset=utf-8"),r="string"==typeof t?t:t&&t.__isJSX||Array.isArray(t)?await renderToString(t,this):null==t?"":String(t),new Response(r,{status:e,headers:o})}async page(t,e=this.status,n={}){const o=this.i(n);let r;return o.has("Content-Type")||o.set("Content-Type","text/html; charset=utf-8"),r="string"==typeof t?t:t&&t.__isJSX||Array.isArray(t)?await renderToString(t,this):null==t?"":String(t),/^\s*<!doctype\s/i.test(r)||(r="<!DOCTYPE html>"+r),new Response(r,{status:e,headers:o})}redirect(t,e=302){const n=new Headers(this.headers);return n.set("Location",String(t)),n.delete("Content-Type"),new Response(null,{status:e,headers:n})}},k=class{constructor(){this.children=null,this.paramChild=null,this.handler=null}},H=class{constructor(){this.root=new k}add(t,e,n){let o=this.root;const r=[];for(let e=0;e<t.length;e++){const n=t[e];if(58===n.charCodeAt(0)){const t=n.slice(1);r.push(f(t)?t:"_p"+e),o.paramChild||(o.paramChild=new k),o=o.paramChild}else{o.children||(o.children=new Map);let t=o.children.get(n);t||(t=new k,o.children.set(n,t)),o=t}}o.handler={handler:e,options:n,paramNames:r}}match(t){const e=[],n=this.l(this.root,t,0,e);if(!n)return null;const{handler:o,options:r,paramNames:i}=n,s=Object.create(null);for(let t=0;t<i.length;t++)s[i[t]]=e[t];return{handler:o,options:r,params:s}}l(t,e,n,o){if(n===e.length)return t.handler;const r=e[n],i=t.children;if(i){const t=i.get(r);if(t){const r=this.l(t,e,n+1,o);if(r)return r}}const s=t.paramChild;if(s){o.push(r);const t=this.l(s,e,n+1,o);if(t)return t;o.pop()}return null}},R=["GET","POST","PUT","DELETE","PATCH","OPTIONS","HEAD"],D=["text/","application/json","application/xml","application/javascript","application/xhtml","application/ld+json","application/manifest+json","image/svg"];function I(t){if(!t)return!0;const e=t.toLowerCase();for(let t=0;t<D.length;t++)if(e.startsWith(D[t]))return!0;return!1}var L=new Map,N=0;function U(t,e,n){const o=t+"|"+(e||"")+"|"+(n||""),r=Date.now(),i=L.get(o);if(i&&r-i<6e4)return!1;if(L.set(o,r),r-N>3e4){N=r;for(const[t,e]of L)r-e>6e4&&L.delete(t)}return!0}var Edge=class{constructor(){this.staticRoutes=new Map;for(let t=0;t<R.length;t++)this.staticRoutes.set(R[t],new Map);this.dynamicTries={};for(let t=0;t<R.length;t++)this.dynamicTries[R[t]]=new H;this.defaults={cors:{origin:"*",methods:"GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD"}},this.scheduledHandler=null,this.security={logSecurityEvents:!0,extraHeaders:null,trustedProxies:null}}u(t,e,n,o){if("boolean"==typeof n?n={}:"function"==typeof n&&(o=n,n={}),"function"!=typeof o)throw new Error("Handler for "+t+" "+e+" must be a function");const r={...this.defaults,...n||{}};-1===e.indexOf(":")?this.staticRoutes.get(t).set(l(e),{handler:o,options:r}):this.dynamicTries[t].add(a(e),o,r)}get(t,e,n){this.u("GET",t,e,n)}post(t,e,n){this.u("POST",t,e,n)}put(t,e,n){this.u("PUT",t,e,n)}delete(t,e,n){this.u("DELETE",t,e,n)}patch(t,e,n){this.u("PATCH",t,e,n)}options(t,e,n){this.u("OPTIONS",t,e,n)}head(t,e,n){this.u("HEAD",t,e,n)}scheduled(t){this.scheduledHandler=t}h(t,e,n){if(this.security.logSecurityEvents)try{const o=t.req.headers.get("CF-Connecting-IP")||"",r=t.url?t.url.pathname:"";if(!U(e,o,r))return;console.log(JSON.stringify({ts:(new Date).toISOString(),kind:"security",event:e,detail:"string"==typeof n?n:void 0,ip:o||void 0,method:t.req.method,path:r,ua:t.req.headers.get("User-Agent")||void 0}))}catch{}}p(t,e){if(!e)return;const n="object"==typeof e?e:this.defaults.cors,o=t.req.headers.get("Origin"),r=n.origin;"*"===r?t.headers.set("Access-Control-Allow-Origin","*"):Array.isArray(r)?o&&-1!==r.indexOf(o)&&(t.headers.set("Access-Control-Allow-Origin",o),t.headers.set("Vary","Origin"),t.headers.set("Access-Control-Allow-Credentials","true")):"string"==typeof r&&o===r&&(t.headers.set("Access-Control-Allow-Origin",o),t.headers.set("Vary","Origin"),t.headers.set("Access-Control-Allow-Credentials","true")),t.headers.set("Access-Control-Allow-Methods",n.methods||"GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD"),t.headers.set("Access-Control-Allow-Headers",n.headers||"Content-Type, Authorization, X-CSRF-Token"),t.headers.set("Access-Control-Max-Age","86400")}m(t,e){e&&console.log(t.req.method+" "+t.req.url+" - "+t.status)}C(t,e){if(!I(e.headers.get("Content-Type")))return e;const n=e.headers.get("Content-Length");if(null!==n&&+n<1024)return e;const o=t.headers.get("Accept-Encoding")||"";let r=null;if(-1!==o.indexOf("gzip")?r="gzip":-1!==o.indexOf("deflate")&&(r="deflate"),!r||!e.body)return e;const i=e.body.pipeThrough(new CompressionStream(r)),s=new Headers(e.headers);return s.set("Content-Encoding",r),s.set("Vary","Accept-Encoding"),s.delete("Content-Length"),new Response(i,{status:e.status,statusText:e.statusText,headers:s})}async T(t,e){return!e||"function"!=typeof e||!!await e(t)}async O(t){return await caches.default.match(t)||null}async S(t,e,n){if(!n)return;const o=!0===n?{}:n,r=o.ttl??3600,i=o.staleWhileRevalidate??0,s=new Headers(e.headers);s.set("Cache-Control","max-age="+r+(i>0?", stale-while-revalidate="+i:"")),s.delete("Set-Cookie"),await caches.default.put(t,new Response(e.body,{status:e.status,statusText:e.statusText,headers:s}))}async fetch(t,e,n){const o=new URL(t.url),r=l(o.pathname),i=t.method,s=this.staticRoutes.get(i);if(s){const i=s.get(r);if(i)return this.A(i.handler,i.options,t,e,n,Object.create(null),o)}const c=this.dynamicTries[i];if(c){const i=c.match(a(r));if(i)return this.A(i.handler,i.options,t,e,n,i.params,o)}return new Response("Not Found",{status:404})}async A(t,e,n,o,r,i,s){const c=new _(n,o,r,i,s),a=this.security.extraHeaders;if(a)for(const t in a)Object.prototype.hasOwnProperty.call(a,t)&&c.headers.set(t,a[t]);let l,u=!1;try{u=await this.T(c,e.validate)}catch(t){this.h(c,"validation_error",t&&t.message),u=!1}if(!u)return c.text("Validation failed",400);if(e.cache&&"GET"===n.method){const t=await this.O(n);if(t)return this.p(c,e.cors),this.m(c,e.log),t}this.p(c,e.cors);try{const e=await t(c);l=e instanceof Response?e:e&&e.__isJSX?await c.html(e):c.text("OK")}catch(t){this.h(c,"handler_error",t&&t.message),l=c.text("Internal Server Error",500)}return e.cache&&"GET"===n.method&&200===l.status&&c.executionCtx.waitUntil(this.S(n,l.clone(),e.cache)),e.compress&&(l=this.C(n,l)),this.m(c,e.log),l}};
|
|
33
|
+
var t=Object.defineProperty,e=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,r=(e,n)=>{for(var o in n)t(e,o,{get:n[o],enumerable:!0})},i=(r,i,s,c)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let a of n(i))o.call(r,a)||a===s||t(r,a,{get:()=>i[a],enumerable:!(c=e(i,a))||c.enumerable});return r},s=e=>i(t({},"__esModule",{value:!0}),e),c={};function a(t){const e=[],n=t.length;let o=1;for(let r=1;r<=n;r++)r!==n&&47!==t.charCodeAt(r)||(r>o&&e.push(t.slice(o,r)),o=r+1);return e}function l(t){const e=t.length;return e>1&&47===t.charCodeAt(e-1)?t.slice(0,-1):t}r(c,{Context:()=>P,Edge:()=>Edge,Fragment:()=>Fragment,jsx:()=>jsx,renderToString:()=>renderToString,useCtx:()=>useCtx}),module.exports=s(c);var u=new Set(["__proto__","constructor","prototype"]);function f(t){return"string"==typeof t&&!u.has(t)}function h(t){const e=t.length;let n=0;for(;n<e;){const e=t.charCodeAt(n);if(38===e||60===e||62===e||34===e||39===e)break;n++}if(n===e)return t;let o=t.slice(0,n),r=n;for(;n<e;n++){const e=t.charCodeAt(n);let i;if(38===e)i="&";else if(60===e)i="<";else if(62===e)i=">";else if(34===e)i=""";else{if(39!==e)continue;i="'"}n>r&&(o+=t.slice(r,n)),o+=i,r=n+1}return r<e&&(o+=t.slice(r)),o}function p(t){return t>=65&&t<=90||t>=97&&t<=122}function d(t){return t>=48&&t<=57}function y(t){const e=t.length;if(0===e||!p(t.charCodeAt(0)))return!1;for(let n=1;n<e;n++){const e=t.charCodeAt(n);if(!p(e)&&!d(e)&&45!==e)return!1}return!0}function g(t){const e=t.length;if(0===e)return!1;const n=t.charCodeAt(0);if(!p(n)&&95!==n&&58!==n)return!1;for(let n=1;n<e;n++){const e=t.charCodeAt(n);if(!p(e)&&!d(e)&&95!==e&&58!==e&&46!==e&&45!==e)return!1}return!0}function w(t){const e=t.charCodeAt(0),n=t.charCodeAt(1);return!(111!==e&&79!==e||110!==n&&78!==n)}var Fragment=Symbol("Fragment");function jsx(t,e,...n){return{type:t,props:e||{},children:n,__isJSX:!0}}var m=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),C=new Set(["allowfullscreen","async","autofocus","autoplay","checked","controls","default","defer","disabled","formnovalidate","hidden","inert","ismap","itemscope","loop","multiple","muted","nomodule","novalidate","open","playsinline","readonly","required","reversed","selected"]),b=new Set(["animation-iteration-count","border-image-outset","border-image-slice","border-image-width","box-flex","box-flex-group","box-ordinal-group","column-count","columns","flex","flex-grow","flex-positive","flex-shrink","flex-negative","flex-order","grid-row","grid-row-end","grid-row-span","grid-row-start","grid-column","grid-column-end","grid-column-span","grid-column-start","font-weight","line-clamp","line-height","opacity","order","orphans","tab-size","widows","z-index","zoom","fill-opacity","flood-opacity","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-miterlimit","stroke-opacity","stroke-width"]),T=new Map,O=2e3;function x(t){const e=T.get(t);if(void 0!==e)return e;const n=t.replace(/([A-Z])/g,"-$1").toLowerCase().replace(/^-/,"");return T.size>=O&&T.clear(),T.set(t,n),n}function S(t){if(!t||"object"!=typeof t)return"";let e="";for(const n in t){if(!Object.prototype.hasOwnProperty.call(t,n))continue;const o=x(n);let r=t[n];"number"!=typeof r||b.has(o)||(r+="px"),e&&(e+=";"),e+=o+":"+r}return e}var A=new Set(["href","src","xlink:href","action","formaction"]),v=[];function useCtx(){const t=v[v.length-1];if(!t)throw new Error("useCtx() must be called synchronously at the top of a component (before any await). It is not available after an await, nor outside a render.");return t}async function renderToString(t,e){if(void 0===e&&(e=v[v.length-1]),null==t||"boolean"==typeof t)return"";const n=typeof t;if("string"===n)return h(t);if("number"===n)return h(String(t));if("object"===n&&"function"==typeof t.then)return renderToString(await t,e);if(Array.isArray(t)){let n="";for(let o=0;o<t.length;o++)n+=await renderToString(t[o],e);return n}if(!t.__isJSX)return h(String(t));const{type:o,props:r,children:i}=t;if(o===Fragment){let t="";for(let n=0;n<i.length;n++)t+=await renderToString(i[n],e);return t}if("symbol"==typeof o)return"";if("function"==typeof o){let t;if(e){v.push(e);try{t=o({...r,children:i})}finally{v.pop()}}else t=o({...r,children:i});return renderToString(await t,e)}if("string"!=typeof o||!y(o))return"";const s=m.has(o);let c="";for(const t in r){if(!Object.prototype.hasOwnProperty.call(r,t))continue;if("children"===t||"key"===t||"ref"===t)continue;const e=r[t];if(null==e||!1===e)continue;if("dangerouslySetInnerHTML"===t)continue;let n=t;if("className"===t||"class"===t?n="class":"htmlFor"!==t&&"for"!==t||(n="for"),f(n)&&!w(n)&&g(n)){if(A.has(n)){const t=String(e).replace(/[\u0000-\u0020]/g,"").toLowerCase();if(t.startsWith("javascript:")||t.startsWith("vbscript:")||t.startsWith("data:text/html"))continue}if(C.has(n))!0===e&&(c+=" "+n);else if(!0!==e){if("style"===n&&"object"==typeof e){const t=S(e);t&&(c+=' style="'+h(t)+'"');continue}c+=" "+n+'="'+h(String(e))+'"'}else c+=" "+n+'="true"'}}let a="";const l=r.dangerouslySetInnerHTML;if(l&&null!=l.__html)a=l.__html;else for(let t=0;t<i.length;t++)a+=await renderToString(i[t],e);return s?"<"+o+c+"/>":"<"+o+c+">"+a+"</"+o+">"}var E=Object.freeze([["X-Content-Type-Options","nosniff"],["X-Frame-Options","DENY"],["Referrer-Policy","strict-origin-when-cross-origin"],["Permissions-Policy","geolocation=(), microphone=(), camera=()"]]),j=Object.freeze(Object.create(null)),P=class{constructor(t,e,n,o={},r=null){this.req=t,this.env=e,this.executionCtx=n,this.params=o,this.status=200,this.headers=new Headers(E),this.t=null,this.url=r||new URL(t.url)}o(){if(null!==this.t)return this.t;const t=this.req.headers.get("Cookie");if(!t)return this.t=j,j;const e=Object.create(null);let n=0;const o=t.length;for(;n<o;){let r=t.indexOf(";",n);-1===r&&(r=o);let i=n,s=r;for(;i<s&&32===t.charCodeAt(i);)i++;for(;s>i&&32===t.charCodeAt(s-1);)s--;if(s>i){const n=t.indexOf("=",i);if(n>i&&n<s)try{const o=decodeURIComponent(t.slice(i,n));f(o)&&(e[o]=decodeURIComponent(t.slice(n+1,s)))}catch{}}n=r+1}return this.t=e,e}getCookie(t){const e=this.o()[t];return void 0===e?null:e}get query(){return this.url.searchParams}setCookie(t,e,n={}){if("string"!=typeof t||!f(t)||/[\r\n;=]/.test(t))return;let o=encodeURIComponent(t)+"="+encodeURIComponent(e);n.path&&(o+="; Path="+n.path),n.domain&&(o+="; Domain="+n.domain),void 0!==n.maxAge&&(o+="; Max-Age="+n.maxAge),n.expires&&(o+="; Expires="+n.expires.toUTCString()),n.secure&&(o+="; Secure"),n.httpOnly&&(o+="; HttpOnly"),n.sameSite&&(o+="; SameSite="+n.sameSite),this.headers.append("Set-Cookie",o)}deleteCookie(t,e={}){this.setCookie(t,"",{...e,maxAge:0,expires:new Date(0)})}i(t){if(!t)return this.headers;const e=Object.keys(t);if(0===e.length)return this.headers;const n=new Headers(this.headers);for(let o=0;o<e.length;o++)n.set(e[o],t[e[o]]);return n}text(t,e=this.status,n={}){const o=this.i(n);return o.has("Content-Type")||o.set("Content-Type","text/plain; charset=utf-8"),new Response(t,{status:e,headers:o})}json(t,e=this.status,n={}){const o=this.i(n);return o.has("Content-Type")||o.set("Content-Type","application/json; charset=utf-8"),new Response(JSON.stringify(t),{status:e,headers:o})}async html(t,e=this.status,n={}){const o=this.i(n);let r;return o.has("Content-Type")||o.set("Content-Type","text/html; charset=utf-8"),r="string"==typeof t?t:t&&t.__isJSX||Array.isArray(t)?await renderToString(t,this):null==t?"":String(t),new Response(r,{status:e,headers:o})}async page(t,e=this.status,n={}){const o=this.i(n);let r;return o.has("Content-Type")||o.set("Content-Type","text/html; charset=utf-8"),r="string"==typeof t?t:t&&t.__isJSX||Array.isArray(t)?await renderToString(t,this):null==t?"":String(t),/^\s*<!doctype\s/i.test(r)||(r="<!DOCTYPE html>"+r),new Response(r,{status:e,headers:o})}redirect(t,e=302){const n=new Headers(this.headers);return n.set("Location",String(t)),n.delete("Content-Type"),new Response(null,{status:e,headers:n})}},_=class{constructor(){this.children=null,this.paramChild=null,this.handler=null}},k=class{constructor(){this.root=new _}add(t,e,n){let o=this.root;const r=[];for(let e=0;e<t.length;e++){const n=t[e];if(58===n.charCodeAt(0)){const t=n.slice(1);r.push(f(t)?t:"_p"+e),o.paramChild||(o.paramChild=new _),o=o.paramChild}else{o.children||(o.children=new Map);let t=o.children.get(n);t||(t=new _,o.children.set(n,t)),o=t}}o.handler={handler:e,options:n,paramNames:r}}match(t){const e=[],n=this.l(this.root,t,0,e);if(!n)return null;const{handler:o,options:r,paramNames:i}=n,s=Object.create(null);for(let t=0;t<i.length;t++)s[i[t]]=e[t];return{handler:o,options:r,params:s}}l(t,e,n,o){if(n===e.length)return t.handler;const r=e[n],i=t.children;if(i){const t=i.get(r);if(t){const r=this.l(t,e,n+1,o);if(r)return r}}const s=t.paramChild;if(s){o.push(r);const t=this.l(s,e,n+1,o);if(t)return t;o.pop()}return null}},H=["GET","POST","PUT","DELETE","PATCH","OPTIONS","HEAD"],R=["text/","application/json","application/xml","application/javascript","application/xhtml","application/ld+json","application/manifest+json","image/svg"];function D(t){if(!t)return!0;const e=t.toLowerCase();for(let t=0;t<R.length;t++)if(e.startsWith(R[t]))return!0;return!1}var I=new Map,L=0;function N(t,e,n){const o=t+"|"+(e||"")+"|"+(n||""),r=Date.now(),i=I.get(o);if(i&&r-i<6e4)return!1;if(I.set(o,r),r-L>3e4){L=r;for(const[t,e]of I)r-e>6e4&&I.delete(t)}return!0}var Edge=class{constructor(){this.staticRoutes=new Map;for(let t=0;t<H.length;t++)this.staticRoutes.set(H[t],new Map);this.dynamicTries={};for(let t=0;t<H.length;t++)this.dynamicTries[H[t]]=new k;this.defaults={cors:{origin:"*",methods:"GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD"}},this.scheduledHandler=null,this.security={logSecurityEvents:!0,extraHeaders:null,trustedProxies:null}}u(t,e,n,o){if("boolean"==typeof n?n={}:"function"==typeof n&&(o=n,n={}),"function"!=typeof o)throw new Error("Handler for "+t+" "+e+" must be a function");const r={...this.defaults,...n||{}};-1===e.indexOf(":")?this.staticRoutes.get(t).set(l(e),{handler:o,options:r}):this.dynamicTries[t].add(a(e),o,r)}get(t,e,n){this.u("GET",t,e,n)}post(t,e,n){this.u("POST",t,e,n)}put(t,e,n){this.u("PUT",t,e,n)}delete(t,e,n){this.u("DELETE",t,e,n)}patch(t,e,n){this.u("PATCH",t,e,n)}options(t,e,n){this.u("OPTIONS",t,e,n)}head(t,e,n){this.u("HEAD",t,e,n)}scheduled(t){this.scheduledHandler=t}h(t,e,n){if(this.security.logSecurityEvents)try{const o=t.req.headers.get("CF-Connecting-IP")||"",r=t.url?t.url.pathname:"";if(!N(e,o,r))return;console.log(JSON.stringify({ts:(new Date).toISOString(),kind:"security",event:e,detail:"string"==typeof n?n:void 0,ip:o||void 0,method:t.req.method,path:r,ua:t.req.headers.get("User-Agent")||void 0}))}catch{}}p(t,e){if(!e)return;const n="object"==typeof e?e:this.defaults.cors,o=t.req.headers.get("Origin"),r=n.origin;"*"===r?t.headers.set("Access-Control-Allow-Origin","*"):Array.isArray(r)?o&&-1!==r.indexOf(o)&&(t.headers.set("Access-Control-Allow-Origin",o),t.headers.set("Vary","Origin"),t.headers.set("Access-Control-Allow-Credentials","true")):"string"==typeof r&&o===r&&(t.headers.set("Access-Control-Allow-Origin",o),t.headers.set("Vary","Origin"),t.headers.set("Access-Control-Allow-Credentials","true")),t.headers.set("Access-Control-Allow-Methods",n.methods||"GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD"),t.headers.set("Access-Control-Allow-Headers",n.headers||"Content-Type, Authorization, X-CSRF-Token"),t.headers.set("Access-Control-Max-Age","86400")}m(t,e){e&&console.log(t.req.method+" "+t.req.url+" - "+t.status)}C(t,e){if(!D(e.headers.get("Content-Type")))return e;const n=e.headers.get("Content-Length");if(null!==n&&+n<1024)return e;const o=t.headers.get("Accept-Encoding")||"";let r=null;if(-1!==o.indexOf("gzip")?r="gzip":-1!==o.indexOf("deflate")&&(r="deflate"),!r||!e.body)return e;const i=e.body.pipeThrough(new CompressionStream(r)),s=new Headers(e.headers);return s.set("Content-Encoding",r),s.set("Vary","Accept-Encoding"),s.delete("Content-Length"),new Response(i,{status:e.status,statusText:e.statusText,headers:s})}async T(t,e){return!e||"function"!=typeof e||!!await e(t)}async O(t){return await caches.default.match(t)||null}async S(t,e,n){if(!n)return;const o=!0===n?{}:n,r=o.ttl??3600,i=o.staleWhileRevalidate??0,s=new Headers(e.headers);s.set("Cache-Control","max-age="+r+(i>0?", stale-while-revalidate="+i:"")),s.delete("Set-Cookie"),await caches.default.put(t,new Response(e.body,{status:e.status,statusText:e.statusText,headers:s}))}async fetch(t,e,n){const o=new URL(t.url),r=l(o.pathname),i=t.method,s=this.staticRoutes.get(i);if(s){const i=s.get(r);if(i)return this.A(i.handler,i.options,t,e,n,Object.create(null),o)}const c=this.dynamicTries[i];if(c){const i=c.match(a(r));if(i)return this.A(i.handler,i.options,t,e,n,i.params,o)}return new Response("Not Found",{status:404})}async A(t,e,n,o,r,i,s){const c=new P(n,o,r,i,s),a=this.security.extraHeaders;if(a)for(const t in a)Object.prototype.hasOwnProperty.call(a,t)&&c.headers.set(t,a[t]);let l,u=!1;try{u=await this.T(c,e.validate)}catch(t){this.h(c,"validation_error",t&&t.message),u=!1}if(!u)return c.text("Validation failed",400);if(e.cache&&"GET"===n.method){const t=await this.O(n);if(t)return this.p(c,e.cors),this.m(c,e.log),t}this.p(c,e.cors);try{const e=await t(c);l=e instanceof Response?e:e&&e.__isJSX?await c.html(e):c.text("OK")}catch(t){this.h(c,"handler_error",t&&t.message),l=c.text("Internal Server Error",500)}return e.cache&&"GET"===n.method&&200===l.status&&c.executionCtx.waitUntil(this.S(n,l.clone(),e.cache)),e.compress&&(l=this.C(n,l)),this.m(c,e.log),l}};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -17,6 +17,7 @@ Inspired by Hono, `@lengkapp/edge` aims for the same class of performance while
|
|
|
17
17
|
- [JSX Support](#jsx-support)
|
|
18
18
|
- [Full Example](#full-example)
|
|
19
19
|
- [Client (Declarative Partial Updates)](#client-declarative-partial-updates)
|
|
20
|
+
- [Device Fingerprint](#device-fingerprint)
|
|
20
21
|
- [CSRF Protection](#csrf-protection)
|
|
21
22
|
- [Security Controls](#security-controls)
|
|
22
23
|
- [OWASP 2025 Coverage](#owasp-2025-coverage)
|
|
@@ -42,6 +43,8 @@ Inspired by Hono, `@lengkapp/edge` aims for the same class of performance while
|
|
|
42
43
|
### Client
|
|
43
44
|
|
|
44
45
|
- **Declarative partial updates** via `_get` / `_post` and the placement modes `_in`, `_out`, `_before`, `_after`
|
|
46
|
+
- **Client-side navigation** via `_go` (same tab) and `_open` (new tab) — no fetch, no loader
|
|
47
|
+
- **Opt-in device fingerprint** via `_id` — Canvas, WebGL, Audio, font probe, and basic navigator signals, hashed once per page; sent as `X-DeviceId` on `_get` / `_post` and as `?_did=` on `_go` / `_open`
|
|
45
48
|
- **Event, load, and visibility triggers** – `click` (default), `load`, `visible`, or any DOM event name
|
|
46
49
|
- **JSON and form bodies** – `_json="a,b,c"` or `_form="#signup"`
|
|
47
50
|
- **Built-in loading and error states** – deferred spinner, skeleton loader, abortable requests, one-click retry
|
|
@@ -152,7 +155,7 @@ wrangler deploy
|
|
|
152
155
|
### Context
|
|
153
156
|
|
|
154
157
|
| Member | Description |
|
|
155
|
-
|
|
158
|
+
|---|---|
|
|
156
159
|
| `ctx.req` | Incoming Request |
|
|
157
160
|
| `ctx.env` | Environment bindings |
|
|
158
161
|
| `ctx.executionCtx` | ExecutionContext |
|
|
@@ -623,7 +626,7 @@ export default {
|
|
|
623
626
|
### Feature → route cheat-sheet
|
|
624
627
|
|
|
625
628
|
| Feature | Route / location |
|
|
626
|
-
|
|
629
|
+
|---|---|
|
|
627
630
|
| `ctx.text` | `GET /health` |
|
|
628
631
|
| `ctx.json` | `GET /api/time`, `POST /api/echo`, … |
|
|
629
632
|
| `ctx.html` with JSX | `GET /` |
|
|
@@ -658,8 +661,11 @@ export default {
|
|
|
658
661
|
### Attributes
|
|
659
662
|
|
|
660
663
|
| Attribute | Description |
|
|
661
|
-
|
|
664
|
+
|---|---|
|
|
662
665
|
| `_get` / `_post` | Request URL and HTTP method. Only GET and POST are supported. |
|
|
666
|
+
| `_go` | Navigate the current tab to the URL (`location.assign`). |
|
|
667
|
+
| `_open` | Open the URL in a new tab (`noopener,noreferrer`). |
|
|
668
|
+
| `_id` | Opt in to the device fingerprint for this element. See [Device Fingerprint](#device-fingerprint). |
|
|
663
669
|
| `_in` | Replace the target's children with the response. |
|
|
664
670
|
| `_out` | Replace the target element itself. |
|
|
665
671
|
| `_before` | Insert the response before the target. |
|
|
@@ -672,7 +678,7 @@ export default {
|
|
|
672
678
|
|
|
673
679
|
**Targets.** Exactly one placement attribute (`_in`, `_out`, `_before`, `_after`) should be present. Its value is a CSS selector, the literal string `this`, or empty — the last two both resolve to the element that carries the attribute.
|
|
674
680
|
|
|
675
|
-
**Field scope.** `_json` reads values from the closest enclosing `<form>`, or from the document if there is none. It also accepts fields by id first, then by name (grouped radio/checkbox inputs are handled).
|
|
681
|
+
**Field scope.** `_json` reads values from the closest enclosing `<form>`, or from the document if there is none. It also accepts fields by `id` first, then by `name` (grouped radio/checkbox inputs are handled).
|
|
676
682
|
|
|
677
683
|
### Examples
|
|
678
684
|
|
|
@@ -695,17 +701,27 @@ export default {
|
|
|
695
701
|
|
|
696
702
|
<!-- skeleton loader with a 5-second timeout -->
|
|
697
703
|
<div _get="/feed" _in="this" _loader="skeleton" _timeout="5000"></div>
|
|
704
|
+
|
|
705
|
+
<!-- same-tab navigation with a device id -->
|
|
706
|
+
<a href="/dashboard" _go _id>Dashboard</a>
|
|
707
|
+
|
|
708
|
+
<!-- new-tab navigation -->
|
|
709
|
+
<a href="/docs" _open>Docs</a>
|
|
710
|
+
|
|
711
|
+
<!-- POST with X-DeviceId header -->
|
|
712
|
+
<button _post="/like" _id _in="#card-3">Like</button>
|
|
698
713
|
```
|
|
699
714
|
|
|
700
715
|
### Triggers
|
|
701
716
|
|
|
702
|
-
-
|
|
703
|
-
-
|
|
704
|
-
-
|
|
717
|
+
- `click` (default) — handled by a single delegated document listener.
|
|
718
|
+
- `load` / `visible` — the element is observed with `IntersectionObserver` (300px root margin) and the request fires the first time it enters the viewport.
|
|
719
|
+
- Any other value — treated as a DOM event name. The listener is attached the first time the element becomes visible, then fires normally.
|
|
705
720
|
|
|
706
721
|
### How Content Is Inserted
|
|
707
722
|
|
|
708
723
|
- The request is sent with `credentials: 'same-origin'` and `X-Requested-With: XMLHttpRequest`.
|
|
724
|
+
- When the triggering element carries `_id`, the request includes an `X-DeviceId` header (see [Device Fingerprint](#device-fingerprint)).
|
|
709
725
|
- For `_post`, the body is JSON (`_json`), a `FormData` object (`_form`), or empty.
|
|
710
726
|
- The response text is parsed into a `<template>`.
|
|
711
727
|
- Any `<script>` elements are lifted out, then re-created and appended to `<head>` so the browser executes them. External scripts are de-duplicated by absolute URL.
|
|
@@ -730,6 +746,23 @@ export default {
|
|
|
730
746
|
|
|
731
747
|
When `document.startViewTransition` is available, loader → content and loader → error swaps are wrapped in a view transition. The injected stylesheet disables the animation under `prefers-reduced-motion: reduce`.
|
|
732
748
|
|
|
749
|
+
## Device Fingerprint
|
|
750
|
+
|
|
751
|
+
Add `_id` to any `_get` / `_post` / `_go` / `_open` element to attach a stable device identifier.
|
|
752
|
+
|
|
753
|
+
**Signals.** `navigator.userAgent`, `navigator.language`, screen dimensions, color depth, timezone offset, `hardwareConcurrency`, `deviceMemory`, plus Canvas, WebGL renderer, an offline Audio context, and a 10-font width probe. Combined and SHA-256 hashed.
|
|
754
|
+
|
|
755
|
+
**Cost.** Computed lazily once per page and cached for the document lifetime; warmed at boot if any `[_id]` element is in the DOM. Adds ~0.9 KB gzipped to the client bundle and a few milliseconds on the first call.
|
|
756
|
+
|
|
757
|
+
**Delivery.**
|
|
758
|
+
|
|
759
|
+
- `_get` / `_post` → sent as the `X-DeviceId` request header (no body, no URL change).
|
|
760
|
+
- `_go` / `_open` → appended to the URL as `?_did=<hash>` (headers cannot be set on `location.assign` / `window.open`).
|
|
761
|
+
|
|
762
|
+
**Fallback.** If `crypto.subtle` is unavailable (non-secure context), a djb2 hash of the same signals is used, with lower collision resistance.
|
|
763
|
+
|
|
764
|
+
Without `_id`, no fingerprint is computed, no header is set, and no URL parameter is added.
|
|
765
|
+
|
|
733
766
|
## CSRF Protection
|
|
734
767
|
|
|
735
768
|
The client does not attach a CSRF token automatically. Include the token as a form field or as part of the JSON payload built by `_json`, then validate it on the server with a `validate` option:
|
|
@@ -763,7 +796,7 @@ app.security.extraHeaders = {
|
|
|
763
796
|
## OWASP 2025 Coverage
|
|
764
797
|
|
|
765
798
|
| Category | Mitigation |
|
|
766
|
-
|
|
799
|
+
|---|---|
|
|
767
800
|
| A01 Broken Access Control | Strict CORS allowlist, strict target resolution, no implicit trust |
|
|
768
801
|
| A02 Security Misconfiguration | Safe default headers, strict CORS allowlist, `Vary: Origin` |
|
|
769
802
|
| A03 Supply Chain | Zero deps |
|
|
@@ -793,11 +826,11 @@ export default {
|
|
|
793
826
|
The `Edge` constructor takes no arguments. Behaviour is configured through public properties and route options:
|
|
794
827
|
|
|
795
828
|
| Property | Type | Default |
|
|
796
|
-
|
|
829
|
+
|---|---|---|
|
|
797
830
|
| `app.defaults.cors` | `{ origin, methods }` | `origin: '*'`, `methods: 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD'` |
|
|
798
831
|
| `app.security.logSecurityEvents` | `boolean` | `true` |
|
|
799
|
-
| `app.security.extraHeaders` | `Record<string, string>
|
|
800
|
-
| `app.security.trustedProxies` | `string[]
|
|
832
|
+
| `app.security.extraHeaders` | `Record<string, string> \| null` | `null` |
|
|
833
|
+
| `app.security.trustedProxies` | `string[] \| null` | `null` |
|
|
801
834
|
|
|
802
835
|
```ts
|
|
803
836
|
app.defaults.cors = { origin: ['https://app.example.com'], methods: 'GET, POST' };
|
|
@@ -810,8 +843,8 @@ app.security.extraHeaders = {
|
|
|
810
843
|
|
|
811
844
|
Zero runtime dependencies. On a local `wrangler dev` benchmark with `autocannon` (10 connections, 10s per route), throughput sits in the same tier as `hono` / `hono/tiny` / a native worker:
|
|
812
845
|
|
|
813
|
-
| Framework |
|
|
814
|
-
|
|
846
|
+
| Framework | /text req/s | /json req/s |
|
|
847
|
+
|---|---|---|
|
|
815
848
|
| @lengkapp/edge | 503 | 500 |
|
|
816
849
|
| Hono | 506 | 495 |
|
|
817
850
|
| Hono (tiny) | 505 | 498 |
|
|
@@ -823,13 +856,15 @@ The JSX renderer itself is tuned for throughput: single-pass escaping (`charCode
|
|
|
823
856
|
|
|
824
857
|
The client is equally lean: it injects a single stylesheet, binds each element exactly once via a `WeakSet`, and uses one delegated click listener for all `_trigger="click"` elements. An in-flight request on an element is aborted when a new one starts, and the loader is deferred by 100ms / held for at least 240ms so fast endpoints never flash UI.
|
|
825
858
|
|
|
859
|
+
The device fingerprint is opt-in per element (`_id`) and cached for the document lifetime; it is warmed during boot when any `[_id]` element exists, so the first click is not delayed by the ~5–15 ms of Canvas / Audio work.
|
|
860
|
+
|
|
826
861
|
## Security Posture Summary
|
|
827
862
|
|
|
828
863
|
| Layer | Mechanism |
|
|
829
|
-
|
|
864
|
+
|---|---|
|
|
830
865
|
| HTML insertion (client) | Response text parsed into a `<template>`; placement driven by `_in` / `_out` / `_before` / `_after` |
|
|
831
866
|
| Script execution (client) | Extracted `<script>` elements re-created and appended to `<head>`; external scripts de-duplicated by absolute URL |
|
|
832
|
-
| Request hygiene (client) | `credentials: 'same-origin'`, `X-Requested-With: XMLHttpRequest`, abortable via `AbortController`, `_timeout` (default 20s) |
|
|
867
|
+
| Request hygiene (client) | `credentials: 'same-origin'`, `X-Requested-With: XMLHttpRequest`, optional `X-DeviceId` (only when `_id` is present), abortable via `AbortController`, `_timeout` (default 20s) |
|
|
833
868
|
| Cookies | `credentials: 'same-origin'`; names validated on the server |
|
|
834
869
|
| Server response headers | `nosniff`, `X-Frame-Options: DENY`, `Referrer-Policy`, `Permissions-Policy` |
|
|
835
870
|
| Server middleware | Fail-closed validation; structured security logs; strict CORS allowlist |
|
|
@@ -838,4 +873,5 @@ The client is equally lean: it injects a single stylesheet, binds each element e
|
|
|
838
873
|
|
|
839
874
|
## License
|
|
840
875
|
|
|
841
|
-
|
|
876
|
+
MIT — see [LICENSE](./LICENSE) for the full text.
|
|
877
|
+
© LengkApp — Yasir Haris
|