@lengkapp/edge 0.0.6 → 0.0.7
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 +1 -0
- package/{edge-server.d.ts → dist/edge-server.d.ts} +6 -12
- package/dist/edge-server.js +1 -0
- package/package.json +25 -18
- package/edge-client.js +0 -306
- package/edge-server.js +0 -573
- /package/{edge-client.d.ts → dist/edge-client.d.ts} +0 -0
- /package/{README.md → readme.md} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){if(!window.__edge){window.__edge=!0;var e=document,n=e.head,t=e.documentElement,r=["get","post","put","patch","delete"].map(function(e){return"_"+e}),i={post:1,put:1,patch:1},o=r.map(function(e){return"["+e+"]"}).join(","),a=new Set(["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","contextmenu","focusin","focusout","keydown","keyup","keypress","change","input","submit","reset","wheel","touchstart","touchend","touchmove"]),u=new WeakSet,c=new WeakSet,s=new WeakMap,f=new WeakMap,d=new Map,l=new Set,h=new Set,v=e.createElement("style");v.textContent=".aj-skel-wrap{box-sizing:border-box;width:100%;height:100%;min-width:80px;min-height:48px;max-width:100%;max-height:600px;display:flex;align-items:center;justify-content:center}.aj-skel-wrap svg{width:1.75em;height:1.75em;color:#c7c7c7;animation:aj-pulse 1.4s ease-in-out infinite}@keyframes aj-pulse{0%,100%{opacity:.35;transform:scale(.92)}50%{opacity:1;transform:scale(1)}}.aj-retry-wrap{box-sizing:border-box;width:100%;height:100%;min-width:80px;min-height:48px;max-height:600px;display:flex;align-items:center;justify-content:center}.aj-retry{width:2.5em;height:2.5em;padding:0;cursor:pointer;background:#007bff;color:#fff;border:0;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;transition:background .15s ease}.aj-retry:hover{background:#0056b3}.aj-retry:hover svg{transform:rotate(-90deg)}.aj-retry svg{width:1.25em;height:1.25em;transition:transform .3s ease}",n.appendChild(v);var p=!1,w=new Set,m=new Set,g=!1;new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){1===e.nodeType&&w.add(e)}),e.removedNodes.forEach(function(e){1===e.nodeType&&m.add(e)})}),g||(g=!0,queueMicrotask(_))}).observe(t,{childList:!0,subtree:!0}),"loading"===e.readyState?e.addEventListener("DOMContentLoaded",C,{once:!0}):C()}function k(e){for(var n=null,t=0;t<r.length;t++)if(e.hasAttribute(r[t])){n=r[t];break}if(!n)return null;var o=n.slice(1);return{url:e.getAttribute(n),method:o.toUpperCase(),hasBody:!!i[o],target:e.getAttribute("_target"),trigger:e.getAttribute("_trigger"),form:e.getAttribute("_form"),json:e.getAttribute("_json"),skeleton:"false"!==e.getAttribute("_skeleton"),retry:"false"!==e.getAttribute("_retry")}}function y(e){return(e.trigger||("this"===e.target?"load":"click")).split(",").map(function(e){return e.trim().toLowerCase()})}function b(e){return e?e.replace(/^\[|\]$/g,"").split(",").map(function(e){return e.trim().replace(/^['"]|['"]$/g,"")}).filter(Boolean):[]}function x(t,r){if(!c.has(t)){c.add(t);var i=function(n,t){if(!t.target||"this"===t.target)return n;var r=s.get(n);if(r&&r.isConnected)return r;var i=e.querySelector(t.target);return i&&s.set(n,i),i}(t,r);if(i){r.skeleton&&function(e){e.innerHTML='<div class="aj-skel-wrap"><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" aria-hidden="true"><path d="m12 14 4-4"/><path d="M3.34 19a10 10 0 1 1 17.32 0"/></svg></div>'}(i);var o={method:r.method};if(r.hasBody)if(r.json){var a={};r.json.split(",").forEach(function(n){n=n.trim();var t=e.querySelector('[name="'+n+'"]');a[n]=t?t.value:void 0}),o.headers={"Content-Type":"application/json"},o.body=JSON.stringify(a)}else if(r.form){var u=e.getElementById(r.form);o.headers={"Content-Type":"application/x-www-form-urlencoded"},o.body=u?new URLSearchParams(new FormData(u)).toString():""}fetch(r.url,o).then(function(t){if(!t.ok)throw Error("HTTP "+t.status);var r;return b((r=t.headers).get("x-css-required")).forEach(function(t){if(!l.has(t)){l.add(t);var r=e.createElement("link");r.rel="stylesheet",r.href=t,n.appendChild(r)}}),b(r.get("x-js-required")).forEach(function(t){if(!l.has(t)){l.add(t);var r=e.createElement("script");r.src=t,n.appendChild(r)}}),t.text()}).then(function(n){!function(n,t){var r,i,o;n.innerHTML=t,r=n,i=Array.prototype.slice.call(r.querySelectorAll("script")),o=0,function n(){if(!(o>=i.length)){for(var t=i[o],r=e.createElement("script"),a=0;a<t.attributes.length;a++)r.setAttribute(t.attributes[a].name,t.attributes[a].value);if(t.nonce&&(r.nonce=t.nonce),t.src){if(h.has(t.src))return t.remove(),o++,void n();h.add(t.src),r.onload=r.onerror=function(){o++,n()},t.replaceWith(r)}else r.textContent="{\n"+t.textContent+"\n}",t.replaceWith(r),o++,n()}}(),M(n)}(i,n)}).catch(function(n){console.error(n),r.retry&&function(n,t){n.innerHTML="";var r=e.createElement("div");r.className="aj-retry-wrap";var i=e.createElement("button");i.type="button",i.className="aj-retry",i.setAttribute("aria-label","Retry"),i.innerHTML='<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" 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>',i.addEventListener("click",t,{once:!0}),r.appendChild(i),n.appendChild(r)}(i,function(){x(t,r)})}).finally(function(){c.delete(t)})}else c.delete(t)}}function j(e){if(!u.has(e)){u.add(e);var n=k(e);n&&y(n).forEach(function(t){if("load"===t||"domcontentloaded"===t||"ready"===t)queueMicrotask(function(){x(e,n)});else if("visible"===t||"intersect"===t){f.set(e,!1);var r=new IntersectionObserver(function(t){t.forEach(function(t){if(!t.target.isConnected)return r.disconnect(),void d.delete(e);var i=t.isIntersecting&&function(e){var n=getComputedStyle(e);if("none"===n.display||"hidden"===n.visibility||"0"===n.opacity)return!1;var t=e.getBoundingClientRect();return t.width>0&&t.height>0}(e);i&&!f.get(e)&&x(e,n),f.set(e,i)})});r.observe(e),d.set(e,r)}else a.has(t)||e.addEventListener(t,function(){x(e,n)})})}}function M(e){1===e.nodeType&&(e.matches&&e.matches(o)&&j(e),e.querySelectorAll&&e.querySelectorAll(o).forEach(j))}function S(e){d.size&&d.forEach(function(n,t){(e===t||e.contains(t))&&(n.disconnect(),d.delete(t),u.delete(t))})}function _(){g=!1;var e=Array.from(w);w.clear();var n=Array.from(m);m.clear(),e.forEach(M),n.forEach(S)}function C(){p||(p=!0,a.forEach(function(n){var t="click"===n||"submit"===n;e.addEventListener(n,function(e){var r=e.target;if(r instanceof Element){var i=r.closest(o);if(i){var a=k(i);a&&-1!==y(a).indexOf(n)&&(t&&e.preventDefault(),x(i,a))}}},!t&&{passive:!0})})),M(t)}}();
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
// Type definitions for edge-server
|
|
2
|
-
|
|
3
|
-
/// <reference lib="dom" />
|
|
4
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
declare class Context {
|
|
7
4
|
constructor(
|
|
8
5
|
request: Request,
|
|
9
6
|
env: any,
|
|
@@ -20,7 +17,6 @@ export class Context {
|
|
|
20
17
|
headers: Headers;
|
|
21
18
|
url: URL;
|
|
22
19
|
|
|
23
|
-
// Private members (not accessible, but present)
|
|
24
20
|
private _rawCookie: string;
|
|
25
21
|
private _cookies: Record<string, string> | null;
|
|
26
22
|
|
|
@@ -70,8 +66,6 @@ export function jsx(type: any, props: Record<string, any> | null, ...children: a
|
|
|
70
66
|
|
|
71
67
|
export function renderToString(node: any): string;
|
|
72
68
|
|
|
73
|
-
// Route option interfaces
|
|
74
|
-
|
|
75
69
|
interface AuthOptions {
|
|
76
70
|
role?: string;
|
|
77
71
|
scopes?: string[];
|
|
@@ -86,7 +80,6 @@ interface CorsOptions {
|
|
|
86
80
|
origin?: string;
|
|
87
81
|
methods?: string;
|
|
88
82
|
headers?: string;
|
|
89
|
-
credentials?: boolean;
|
|
90
83
|
}
|
|
91
84
|
|
|
92
85
|
interface CacheOptions {
|
|
@@ -104,12 +97,15 @@ interface RouteOptions {
|
|
|
104
97
|
log?: boolean;
|
|
105
98
|
}
|
|
106
99
|
|
|
107
|
-
type RouteHandler = (ctx: Context) => Response | JSXElement |
|
|
100
|
+
type RouteHandler = (ctx: Context) => Response | JSXElement | void | Promise<Response | JSXElement | void>;
|
|
108
101
|
|
|
109
102
|
export class Edge {
|
|
110
103
|
constructor();
|
|
111
104
|
|
|
112
|
-
|
|
105
|
+
authKvBinding: string;
|
|
106
|
+
rateLimitKvBinding: string;
|
|
107
|
+
scheduledHandler: ((controller: ScheduledController, env: any, ctx: ExecutionContext) => void | Promise<void>) | null;
|
|
108
|
+
|
|
113
109
|
get(path: string, handler: RouteHandler): void;
|
|
114
110
|
get(path: string, options: boolean | RouteOptions, handler: RouteHandler): void;
|
|
115
111
|
|
|
@@ -133,11 +129,9 @@ export class Edge {
|
|
|
133
129
|
|
|
134
130
|
scheduled(handler: (controller: ScheduledController, env: any, ctx: ExecutionContext) => void | Promise<void>): void;
|
|
135
131
|
|
|
136
|
-
// Main fetch handler
|
|
137
132
|
fetch(request: Request, env: any, executionCtx: ExecutionContext): Promise<Response>;
|
|
138
133
|
}
|
|
139
134
|
|
|
140
|
-
// JSX namespace support
|
|
141
135
|
declare global {
|
|
142
136
|
namespace JSX {
|
|
143
137
|
interface Element extends JSXElement {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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})},s=(r,s,i,c)=>{if(s&&"object"==typeof s||"function"==typeof s)for(let a of n(s))o.call(r,a)||a===i||t(r,a,{get:()=>s[a],enumerable:!(c=e(s,a))||c.enumerable});return r},i=e=>s(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,{Edge:()=>$,Fragment:()=>f,jsx:()=>h,renderToString:()=>A}),module.exports=i(c);var u=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,this.t=t.headers.get("Cookie")||"",this.o=null,this.url=r||new URL(t.url)}i(){if(null===this.o){const t=Object.create(null);if(this.t)for(const e of this.t.split(";")){const n=e.trim();if(!n)continue;const o=n.indexOf("=");if(o>0){const e=decodeURIComponent(n.slice(0,o)),r=decodeURIComponent(n.slice(o+1));t[e]=r}}this.o=t}return this.o}getCookie(t){return this.i()[t]??null}get query(){return this.url.searchParams}setCookie(t,e,n={}){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)})}text(t,e=this.status,n={}){const o=this.l(n);return o.has("Content-Type")||o.set("Content-Type","text/plain"),new Response(t,{status:e,headers:o})}json(t,e=this.status,n={}){const o=this.l(n);return o.has("Content-Type")||o.set("Content-Type","application/json"),new Response(JSON.stringify(t),{status:e,headers:o})}html(t,e=this.status,n={}){const o=this.l(n);return o.has("Content-Type")||o.set("Content-Type","text/html"),new Response(t,{status:e,headers:o})}l(t){if(0===Object.keys(t).length)return this.headers;const e=new Headers(this.headers);for(const n in t)Object.prototype.hasOwnProperty.call(t,n)&&e.set(n,t[n]);return e}},f=Symbol("Fragment");function h(t,e,...n){return{type:t,props:e||{},children:n.flat(1/0),u:!0}}var d=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),p=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"]),m=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"]),w={"&":"&","<":"<",">":">",'"':""","'":"'"},y=/[&<>"']/g;function g(t){return y.lastIndex=0,y.test(t)?t.replace(y,t=>w[t]):t}var T=new Map,b=2e3;function C(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>=b&&T.clear(),T.set(t,n),n}function E(t){if(!t||"object"!=typeof t)return"";const e=Object.entries(t);return 0===e.length?"":e.map(([t,e])=>{let n=C(t);return"number"!=typeof e||m.has(n)||(e+="px"),`${n}:${e}`}).join(";")}function A(t){if(null==t||"boolean"==typeof t)return"";if("string"==typeof t||"number"==typeof t)return g(t+"");if(Array.isArray(t)){const e=[];for(let n=0;n<t.length;n++)e.push(A(t[n]));return e.join("")}if(!t.u)return g(t+"");const{type:e,props:n,children:o}=t;if(e===f){const t=[];for(let e=0;e<o.length;e++)t.push(A(o[e]));return t.join("")}if("symbol"==typeof e)return"";if("function"==typeof e)return A(e({...n,children:o}));const r=d.has(e),s=[];for(const t in n){if(!Object.prototype.hasOwnProperty.call(n,t))continue;if("children"===t||"key"===t||"ref"===t)continue;const e=n[t];if(null==e||!1===e)continue;if("dangerouslySetInnerHTML"===t)continue;let o=t;if("className"===t||"class"===t?o="class":"htmlFor"!==t&&"for"!==t||(o="for"),p.has(o))!0===e&&s.push(" "+o);else if(!0!==e){if("style"===o&&"object"==typeof e){const t=E(e);t&&s.push(` style="${g(t)}"`);continue}s.push(` ${o}="${g(e+"")}"`)}else s.push(` ${o}="true"`)}const i=s.join("");let c="";if(n&&n.dangerouslySetInnerHTML&&null!=n.dangerouslySetInnerHTML.h)c=n.dangerouslySetInnerHTML.h;else{const t=[];for(let e=0;e<o.length;e++)t.push(A(o[e]));c=t.join("")}return r?`<${e}${i}/>`:`<${e}${i}>${c}</${e}>`}var O=class{constructor(){this.children=null,this.paramChild=null,this.handler=null}},x=class{constructor(){this.root=new O}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))r.push(n.slice(1)),o.paramChild||(o.paramChild=new O),o=o.paramChild;else{o.children||(o.children=new Map);let t=o.children.get(n);t||(t=new O,o.children.set(n,t)),o=t}}o.handler={handler:e,options:n,paramNames:r}}match(t){const e=[],n=this.p(this.root,t,0,e);if(!n)return null;const{handler:o,options:r,paramNames:s}=n,i={};for(let t=0;t<s.length;t++)i[s[t]]=e[t];return{handler:o,options:r,params:i}}p(t,e,n,o){if(n===e.length)return t.handler;const r=e[n];if(t.children){const s=t.children.get(r);if(s){const t=this.p(s,e,n+1,o);if(t)return t}}if(t.paramChild){o.push(r);const s=this.p(t.paramChild,e,n+1,o);if(s)return s;o.pop()}return null}},S=["GET","POST","PUT","DELETE","PATCH","OPTIONS","HEAD"],$=class{constructor(){this.staticRoutes=new Map;for(const t of S)this.staticRoutes.set(t,new Map);this.dynamicTries={};for(const t of S)this.dynamicTries[t]=new x;this.authKvBinding="AUTH_KV",this.rateLimitKvBinding="RATE_LIMIT_KV",this.defaults={cors:{origin:"*",methods:"GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD"}},this.scheduledHandler=null}m(t,e,n,o){if("boolean"==typeof n&&(n={auth:n}),"function"==typeof n&&(o=n,n={}),"function"!=typeof o)throw Error(`Handler for ${t} ${e} must be a function`);const r={...this.defaults,...n||{}};if(-1===e.indexOf(":")){const n=l(e);this.staticRoutes.get(t).set(n,{handler:o,options:r})}else{const n=a(e);this.dynamicTries[t].add(n,o,r)}}get(t,e,n){this.m("GET",t,e,n)}post(t,e,n){this.m("POST",t,e,n)}put(t,e,n){this.m("PUT",t,e,n)}delete(t,e,n){this.m("DELETE",t,e,n)}patch(t,e,n){this.m("PATCH",t,e,n)}options(t,e,n){this.m("OPTIONS",t,e,n)}head(t,e,n){this.m("HEAD",t,e,n)}scheduled(t){this.scheduledHandler=t}async T(t,e){if(!e)return!0;const n=t.getCookie("auth_token")||t.req.headers.get("Authorization")?.replace(/^Bearer\s+/i,"");if(!n)return!1;const o=t.env[this.authKvBinding];if(!o)return!1;const r=await o.get(n);if(!r)return!1;if("object"==typeof e)try{const t=JSON.parse(r);if(e.role&&t.role!==e.role)return!1;if(e.scopes){const n=t.scopes||[];if(!e.scopes.every(t=>n.includes(t)))return!1}}catch{}return!0}async C(t,e){if(!e)return!0;const n=!0===e?{}:e,{max:o=100,window:r=60}=n,s="rl:"+(t.req.headers.get("CF-Connecting-IP")||"unknown"),i=t.env[this.rateLimitKvBinding];if(!i)return!0;let c=await i.get(s,"json")||0;return!(c>=o||(c++,await i.put(s,JSON.stringify(c),{expirationTtl:r}),0))}A(t,e){if(!e)return;const n="object"==typeof e?e:this.defaults.cors;t.headers.set("Access-Control-Allow-Origin",n.origin||"*"),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"),t.headers.set("Access-Control-Max-Age","86400")}O(t,e){e&&console.log(`${t.req.method} ${t.req.url} - ${t.status}`)}S(t,e){const n=t.headers.get("Accept-Encoding")||"";let o=null;if(n.includes("gzip")?o="gzip":n.includes("deflate")&&(o="deflate"),!o||!e.body)return e;const r=e.body.pipeThrough(new CompressionStream(o)),s=new Headers(e.headers);return s.set("Content-Encoding",o),s.set("Vary","Accept-Encoding"),s.delete("Content-Length"),new Response(r,{status:e.status,statusText:e.statusText,headers:s})}async $(t,e){if(!e)return!0;if("function"==typeof e)try{return!!await e(t)}catch{return!1}return!0}async _(t){const e=caches.default;return await e.match(t)||null}async k(t,e,n){if(!n)return;const o=!0===n?{}:n,{ttl:r=3600,staleWhileRevalidate:s=0}=o,i=caches.default,c=e.clone();c.headers.set("Cache-Control",`max-age=${r}${s>0?", stale-while-revalidate="+s:""}`),c.headers.delete("Set-Cookie"),await i.put(t,c)}async fetch(t,e,n){const o=new URL(t.url),r=l(o.pathname),s=t.method,i=this.staticRoutes.get(s);if(i){const s=i.get(r);if(s)return this.P(s.handler,s.options,t,e,n,{},o)}const c=this.dynamicTries[s];if(c){const s=a(r),i=c.match(s);if(i)return this.P(i.handler,i.options,t,e,n,i.params,o)}return new Response("Not Found",{status:404})}async P(t,e,n,o,r,s,i){const c=new u(n,o,r,s,i);if(!await this.$(c,e.validate))return c.text("Validation failed",400);if(!await this.T(c,e.auth))return c.text("Unauthorized",401);if(!await this.C(c,e.rateLimit))return c.text("Too Many Requests",429);let a,l=null;if(e.cache&&"GET"===n.method&&(l=await this._(n),l))return this.A(c,e.cors),this.O(c,e.log),l;this.A(c,e.cors);try{const e=await t(c);a=e&&e.u?c.html(A(e)):e instanceof Response?e:c.text("OK"),c.status=a.status}catch(t){console.error(t),a=c.text("Internal Server Error",500),c.status=500}return e.cache&&"GET"===n.method&&200===a.status&&c.executionCtx.waitUntil(this.k(n,a.clone(),e.cache)),e.compress&&(a=this.S(n,a)),this.O(c,e.log),a}};
|
package/package.json
CHANGED
|
@@ -1,38 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lengkapp/edge",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Edge framework used by Lengkapp",
|
|
5
|
-
"main": "edge-server.js",
|
|
6
|
-
"types": "
|
|
5
|
+
"main": "dist/edge-server.js",
|
|
6
|
+
"types": "dist/edge-server.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
-
"types": "./edge-server.d.ts",
|
|
10
|
-
"node": "./edge-server.js",
|
|
11
|
-
"default": "./edge-server.js"
|
|
9
|
+
"types": "./dist/edge-server.d.ts",
|
|
10
|
+
"node": "./dist/edge-server.js",
|
|
11
|
+
"default": "./dist/edge-server.js"
|
|
12
12
|
},
|
|
13
13
|
"./client": {
|
|
14
|
-
"types": "./edge-client.d.ts",
|
|
15
|
-
"default": "./edge-client.js"
|
|
14
|
+
"types": "./dist/edge-client.d.ts",
|
|
15
|
+
"default": "./dist/edge-client.js"
|
|
16
16
|
},
|
|
17
17
|
"./server": {
|
|
18
|
-
"types": "./edge-server.d.ts",
|
|
19
|
-
"default": "./edge-server.js"
|
|
18
|
+
"types": "./dist/edge-server.d.ts",
|
|
19
|
+
"default": "./dist/edge-server.js"
|
|
20
20
|
},
|
|
21
21
|
"./package.json": "./package.json"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
|
-
"
|
|
25
|
-
"edge-client.d.ts",
|
|
26
|
-
"edge-server.js",
|
|
27
|
-
"edge-server.d.ts"
|
|
24
|
+
"dist"
|
|
28
25
|
],
|
|
29
26
|
"scripts": {
|
|
30
|
-
"
|
|
27
|
+
"build": "node build.mjs",
|
|
28
|
+
"pre": "npm run build",
|
|
29
|
+
"publish": "npm run pre && npm publish"
|
|
31
30
|
},
|
|
32
31
|
"publishConfig": {
|
|
33
32
|
"access": "public"
|
|
34
33
|
},
|
|
35
|
-
"keywords": [
|
|
34
|
+
"keywords": [
|
|
35
|
+
"cloudflare workers",
|
|
36
|
+
"minimal server library",
|
|
37
|
+
"minimal client library"
|
|
38
|
+
],
|
|
36
39
|
"author": "Yasir Haris",
|
|
37
|
-
"license": "MIT"
|
|
38
|
-
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"esbuild": "^0.28.2",
|
|
43
|
+
"terser": "^5.51.2"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/edge-client.js
DELETED
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
(function () {
|
|
2
|
-
if (window.__edge) return;
|
|
3
|
-
window.__edge = true;
|
|
4
|
-
|
|
5
|
-
var d = document, head = d.head, root = d.documentElement;
|
|
6
|
-
|
|
7
|
-
var METHODS = ['get', 'post', 'put', 'patch', 'delete'];
|
|
8
|
-
var METHOD_ATTRS = METHODS.map(function (m) { return '_' + m; });
|
|
9
|
-
var HAS_BODY = { post: 1, put: 1, patch: 1 };
|
|
10
|
-
var SEL = METHOD_ATTRS.map(function (a) { return '[' + a + ']'; }).join(',');
|
|
11
|
-
|
|
12
|
-
var DELEGATABLE = new Set([
|
|
13
|
-
'click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout',
|
|
14
|
-
'mousemove', 'contextmenu', 'focusin', 'focusout', 'keydown', 'keyup',
|
|
15
|
-
'keypress', 'change', 'input', 'submit', 'reset', 'wheel',
|
|
16
|
-
'touchstart', 'touchend', 'touchmove'
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
var bound = new WeakSet();
|
|
20
|
-
var running = new WeakSet();
|
|
21
|
-
var targetCache = new WeakMap();
|
|
22
|
-
var wasVisible = new WeakMap();
|
|
23
|
-
var ioObservers = new Map();
|
|
24
|
-
var injected = new Set();
|
|
25
|
-
var executedSrc = new Set();
|
|
26
|
-
|
|
27
|
-
var style = d.createElement('style');
|
|
28
|
-
style.textContent =
|
|
29
|
-
'.aj-skel-wrap{box-sizing:border-box;width:100%;height:100%;min-width:80px;min-height:48px;' +
|
|
30
|
-
'max-width:100%;max-height:600px;display:flex;align-items:center;justify-content:center}' +
|
|
31
|
-
'.aj-skel-wrap svg{width:1.75em;height:1.75em;color:#c7c7c7;' +
|
|
32
|
-
'animation:aj-pulse 1.4s ease-in-out infinite}' +
|
|
33
|
-
'@keyframes aj-pulse{0%,100%{opacity:.35;transform:scale(.92)}50%{opacity:1;transform:scale(1)}}' +
|
|
34
|
-
'.aj-retry-wrap{box-sizing:border-box;width:100%;height:100%;min-width:80px;min-height:48px;' +
|
|
35
|
-
'max-height:600px;display:flex;align-items:center;justify-content:center}' +
|
|
36
|
-
'.aj-retry{width:2.5em;height:2.5em;padding:0;cursor:pointer;background:#007bff;color:#fff;' +
|
|
37
|
-
'border:0;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;' +
|
|
38
|
-
'transition:background .15s ease}' +
|
|
39
|
-
'.aj-retry:hover{background:#0056b3}' +
|
|
40
|
-
'.aj-retry:hover svg{transform:rotate(-90deg)}' +
|
|
41
|
-
'.aj-retry svg{width:1.25em;height:1.25em;transition:transform .3s ease}';
|
|
42
|
-
head.appendChild(style);
|
|
43
|
-
|
|
44
|
-
function cfg(el) {
|
|
45
|
-
var attr = null;
|
|
46
|
-
for (var i = 0; i < METHOD_ATTRS.length; i++) {
|
|
47
|
-
if (el.hasAttribute(METHOD_ATTRS[i])) { attr = METHOD_ATTRS[i]; break; }
|
|
48
|
-
}
|
|
49
|
-
if (!attr) return null;
|
|
50
|
-
var method = attr.slice(1);
|
|
51
|
-
return {
|
|
52
|
-
url: el.getAttribute(attr),
|
|
53
|
-
method: method.toUpperCase(),
|
|
54
|
-
hasBody: !!HAS_BODY[method],
|
|
55
|
-
target: el.getAttribute('_target'),
|
|
56
|
-
trigger: el.getAttribute('_trigger'),
|
|
57
|
-
form: el.getAttribute('_form'),
|
|
58
|
-
json: el.getAttribute('_json'),
|
|
59
|
-
skeleton: el.getAttribute('_skeleton') !== 'false',
|
|
60
|
-
retry: el.getAttribute('_retry') !== 'false'
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function resolveTarget(el, c) {
|
|
65
|
-
if (!c.target || c.target === 'this') return el;
|
|
66
|
-
var cached = targetCache.get(el);
|
|
67
|
-
if (cached && cached.isConnected) return cached;
|
|
68
|
-
var found = d.querySelector(c.target);
|
|
69
|
-
if (found) targetCache.set(el, found);
|
|
70
|
-
return found;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function triggersFor(c) {
|
|
74
|
-
return (c.trigger || (c.target === 'this' ? 'load' : 'click'))
|
|
75
|
-
.split(',').map(function (s) { return s.trim().toLowerCase(); });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function isActuallyVisible(el) {
|
|
79
|
-
var s = getComputedStyle(el);
|
|
80
|
-
if (s.display === 'none' || s.visibility === 'hidden' || s.opacity === '0') return false;
|
|
81
|
-
var r = el.getBoundingClientRect();
|
|
82
|
-
return r.width > 0 && r.height > 0;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
var SKELETON_ICON =
|
|
86
|
-
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" ' +
|
|
87
|
-
'fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" ' +
|
|
88
|
-
'stroke-linejoin="round" aria-hidden="true">' +
|
|
89
|
-
'<path d="m12 14 4-4"/><path d="M3.34 19a10 10 0 1 1 17.32 0"/>' +
|
|
90
|
-
'</svg>';
|
|
91
|
-
|
|
92
|
-
function showSkeleton(el) {
|
|
93
|
-
el.innerHTML = '<div class="aj-skel-wrap">' + SKELETON_ICON + '</div>';
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
var RETRY_ICON =
|
|
97
|
-
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" ' +
|
|
98
|
-
'fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" ' +
|
|
99
|
-
'stroke-linejoin="round" aria-hidden="true">' +
|
|
100
|
-
'<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"/>' +
|
|
101
|
-
'</svg>';
|
|
102
|
-
|
|
103
|
-
function showRetry(el, fn) {
|
|
104
|
-
el.innerHTML = '';
|
|
105
|
-
var wrap = d.createElement('div');
|
|
106
|
-
wrap.className = 'aj-retry-wrap';
|
|
107
|
-
var b = d.createElement('button');
|
|
108
|
-
b.type = 'button';
|
|
109
|
-
b.className = 'aj-retry';
|
|
110
|
-
b.setAttribute('aria-label', 'Retry');
|
|
111
|
-
b.innerHTML = RETRY_ICON;
|
|
112
|
-
b.addEventListener('click', fn, { once: true });
|
|
113
|
-
wrap.appendChild(b);
|
|
114
|
-
el.appendChild(wrap);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function parseList(raw) {
|
|
118
|
-
if (!raw) return [];
|
|
119
|
-
return raw.replace(/^\[|\]$/g, '').split(',')
|
|
120
|
-
.map(function (s) { return s.trim().replace(/^['"]|['"]$/g, ''); })
|
|
121
|
-
.filter(Boolean);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function injectResources(headers) {
|
|
125
|
-
parseList(headers.get('x-css-required')).forEach(function (url) {
|
|
126
|
-
if (injected.has(url)) return;
|
|
127
|
-
injected.add(url);
|
|
128
|
-
var link = d.createElement('link');
|
|
129
|
-
link.rel = 'stylesheet'; link.href = url;
|
|
130
|
-
head.appendChild(link);
|
|
131
|
-
});
|
|
132
|
-
parseList(headers.get('x-js-required')).forEach(function (url) {
|
|
133
|
-
if (injected.has(url)) return;
|
|
134
|
-
injected.add(url);
|
|
135
|
-
var s = d.createElement('script');
|
|
136
|
-
s.src = url;
|
|
137
|
-
head.appendChild(s);
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function execScripts(container) {
|
|
142
|
-
var scripts = Array.prototype.slice.call(container.querySelectorAll('script'));
|
|
143
|
-
var i = 0;
|
|
144
|
-
function next() {
|
|
145
|
-
if (i >= scripts.length) return;
|
|
146
|
-
var old = scripts[i];
|
|
147
|
-
var s = d.createElement('script');
|
|
148
|
-
for (var a = 0; a < old.attributes.length; a++) {
|
|
149
|
-
s.setAttribute(old.attributes[a].name, old.attributes[a].value);
|
|
150
|
-
}
|
|
151
|
-
if (old.nonce) s.nonce = old.nonce;
|
|
152
|
-
if (old.src) {
|
|
153
|
-
if (executedSrc.has(old.src)) { old.remove(); i++; next(); return; }
|
|
154
|
-
executedSrc.add(old.src);
|
|
155
|
-
s.onload = s.onerror = function () { i++; next(); };
|
|
156
|
-
old.replaceWith(s);
|
|
157
|
-
} else {
|
|
158
|
-
s.textContent = '{\n' + old.textContent + '\n}';
|
|
159
|
-
old.replaceWith(s);
|
|
160
|
-
i++; next();
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
next();
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function setContent(el, html) {
|
|
167
|
-
el.innerHTML = html;
|
|
168
|
-
execScripts(el);
|
|
169
|
-
bind(el);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function doFetch(el, c) {
|
|
173
|
-
if (running.has(el)) return;
|
|
174
|
-
running.add(el);
|
|
175
|
-
|
|
176
|
-
var target = resolveTarget(el, c);
|
|
177
|
-
if (!target) { running.delete(el); return; }
|
|
178
|
-
if (c.skeleton) showSkeleton(target);
|
|
179
|
-
|
|
180
|
-
var opts = { method: c.method };
|
|
181
|
-
if (c.hasBody) {
|
|
182
|
-
if (c.json) {
|
|
183
|
-
var obj = {};
|
|
184
|
-
c.json.split(',').forEach(function (name) {
|
|
185
|
-
name = name.trim();
|
|
186
|
-
var input = d.querySelector('[name="' + name + '"]');
|
|
187
|
-
obj[name] = input ? input.value : undefined;
|
|
188
|
-
});
|
|
189
|
-
opts.headers = { 'Content-Type': 'application/json' };
|
|
190
|
-
opts.body = JSON.stringify(obj);
|
|
191
|
-
} else if (c.form) {
|
|
192
|
-
var form = d.getElementById(c.form);
|
|
193
|
-
opts.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
|
194
|
-
opts.body = form ? new URLSearchParams(new FormData(form)).toString() : '';
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
fetch(c.url, opts)
|
|
199
|
-
.then(function (res) {
|
|
200
|
-
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
201
|
-
injectResources(res.headers);
|
|
202
|
-
return res.text();
|
|
203
|
-
})
|
|
204
|
-
.then(function (html) { setContent(target, html); })
|
|
205
|
-
.catch(function (err) {
|
|
206
|
-
console.error(err);
|
|
207
|
-
if (c.retry) showRetry(target, function () { doFetch(el, c); });
|
|
208
|
-
})
|
|
209
|
-
.finally(function () { running.delete(el); });
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
var delegationInit = false;
|
|
213
|
-
function initDelegation() {
|
|
214
|
-
if (delegationInit) return;
|
|
215
|
-
delegationInit = true;
|
|
216
|
-
DELEGATABLE.forEach(function (evt) {
|
|
217
|
-
var isClickOrSubmit = evt === 'click' || evt === 'submit';
|
|
218
|
-
d.addEventListener(evt, function (e) {
|
|
219
|
-
var t = e.target;
|
|
220
|
-
if (!(t instanceof Element)) return;
|
|
221
|
-
var el = t.closest(SEL);
|
|
222
|
-
if (!el) return;
|
|
223
|
-
var c = cfg(el);
|
|
224
|
-
if (!c || triggersFor(c).indexOf(evt) === -1) return;
|
|
225
|
-
if (isClickOrSubmit) e.preventDefault();
|
|
226
|
-
doFetch(el, c);
|
|
227
|
-
}, isClickOrSubmit ? false : { passive: true });
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function bindOne(el) {
|
|
232
|
-
if (bound.has(el)) return;
|
|
233
|
-
bound.add(el);
|
|
234
|
-
var c = cfg(el);
|
|
235
|
-
if (!c) return;
|
|
236
|
-
|
|
237
|
-
triggersFor(c).forEach(function (trig) {
|
|
238
|
-
if (trig === 'load' || trig === 'domcontentloaded' || trig === 'ready') {
|
|
239
|
-
queueMicrotask(function () { doFetch(el, c); });
|
|
240
|
-
} else if (trig === 'visible' || trig === 'intersect') {
|
|
241
|
-
wasVisible.set(el, false);
|
|
242
|
-
var obs = new IntersectionObserver(function (entries) {
|
|
243
|
-
entries.forEach(function (entry) {
|
|
244
|
-
if (!entry.target.isConnected) { obs.disconnect(); ioObservers.delete(el); return; }
|
|
245
|
-
var vis = entry.isIntersecting && isActuallyVisible(el);
|
|
246
|
-
if (vis && !wasVisible.get(el)) doFetch(el, c);
|
|
247
|
-
wasVisible.set(el, vis);
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
obs.observe(el);
|
|
251
|
-
ioObservers.set(el, obs);
|
|
252
|
-
} else if (!DELEGATABLE.has(trig)) {
|
|
253
|
-
el.addEventListener(trig, function () { doFetch(el, c); });
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function bind(subtreeRoot) {
|
|
259
|
-
if (subtreeRoot.nodeType !== 1) return;
|
|
260
|
-
if (subtreeRoot.matches && subtreeRoot.matches(SEL)) bindOne(subtreeRoot);
|
|
261
|
-
if (subtreeRoot.querySelectorAll) subtreeRoot.querySelectorAll(SEL).forEach(bindOne);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function cleanup(removedRoot) {
|
|
265
|
-
if (!ioObservers.size) return;
|
|
266
|
-
ioObservers.forEach(function (obs, el) {
|
|
267
|
-
if (removedRoot === el || removedRoot.contains(el)) {
|
|
268
|
-
obs.disconnect();
|
|
269
|
-
ioObservers.delete(el);
|
|
270
|
-
bound.delete(el);
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
var addQueue = new Set(), removeQueue = new Set(), flushScheduled = false;
|
|
276
|
-
function flush() {
|
|
277
|
-
flushScheduled = false;
|
|
278
|
-
var adds = Array.from(addQueue); addQueue.clear();
|
|
279
|
-
var rems = Array.from(removeQueue); removeQueue.clear();
|
|
280
|
-
adds.forEach(bind);
|
|
281
|
-
rems.forEach(cleanup);
|
|
282
|
-
}
|
|
283
|
-
function scheduleFlush() {
|
|
284
|
-
if (flushScheduled) return;
|
|
285
|
-
flushScheduled = true;
|
|
286
|
-
queueMicrotask(flush);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
new MutationObserver(function (mutations) {
|
|
290
|
-
mutations.forEach(function (m) {
|
|
291
|
-
m.addedNodes.forEach(function (n) { if (n.nodeType === 1) addQueue.add(n); });
|
|
292
|
-
m.removedNodes.forEach(function (n) { if (n.nodeType === 1) removeQueue.add(n); });
|
|
293
|
-
});
|
|
294
|
-
scheduleFlush();
|
|
295
|
-
}).observe(root, { childList: true, subtree: true });
|
|
296
|
-
|
|
297
|
-
function initAll() {
|
|
298
|
-
initDelegation();
|
|
299
|
-
bind(root);
|
|
300
|
-
}
|
|
301
|
-
if (d.readyState === 'loading') {
|
|
302
|
-
d.addEventListener('DOMContentLoaded', initAll, { once: true });
|
|
303
|
-
} else {
|
|
304
|
-
initAll();
|
|
305
|
-
}
|
|
306
|
-
})();
|
package/edge-server.js
DELETED
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
class Context {
|
|
2
|
-
constructor(request, env, executionCtx, params = {}, parsedUrl = null) {
|
|
3
|
-
this.req = request;
|
|
4
|
-
this.env = env;
|
|
5
|
-
this.executionCtx = executionCtx;
|
|
6
|
-
this.params = params;
|
|
7
|
-
this.status = 200;
|
|
8
|
-
this.headers = new Headers();
|
|
9
|
-
this._rawCookie = request.headers.get('Cookie') || '';
|
|
10
|
-
this._cookies = null; // lazy parsed
|
|
11
|
-
this.url = parsedUrl || new URL(request.url); // reuse pre-parsed URL if provided
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Lazy cookie parsing
|
|
15
|
-
_ensureCookies() {
|
|
16
|
-
if (this._cookies === null) {
|
|
17
|
-
const cookies = {};
|
|
18
|
-
if (this._rawCookie) {
|
|
19
|
-
for (const pair of this._rawCookie.split(';')) {
|
|
20
|
-
const trimmed = pair.trim();
|
|
21
|
-
if (!trimmed) continue;
|
|
22
|
-
const idx = trimmed.indexOf('=');
|
|
23
|
-
if (idx > 0) {
|
|
24
|
-
const name = decodeURIComponent(trimmed.slice(0, idx));
|
|
25
|
-
const value = decodeURIComponent(trimmed.slice(idx + 1));
|
|
26
|
-
cookies[name] = value;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
this._cookies = cookies;
|
|
31
|
-
}
|
|
32
|
-
return this._cookies;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
getCookie(name) {
|
|
36
|
-
return this._ensureCookies()[name] ?? null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Lazy query parsing
|
|
40
|
-
get query() {
|
|
41
|
-
return this.url.searchParams;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
setCookie(name, value, options = {}) {
|
|
45
|
-
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
|
46
|
-
if (options.path) cookie += `; Path=${options.path}`;
|
|
47
|
-
if (options.domain) cookie += `; Domain=${options.domain}`;
|
|
48
|
-
if (options.maxAge !== undefined) cookie += `; Max-Age=${options.maxAge}`;
|
|
49
|
-
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
|
|
50
|
-
if (options.secure) cookie += `; Secure`;
|
|
51
|
-
if (options.httpOnly) cookie += `; HttpOnly`;
|
|
52
|
-
if (options.sameSite) cookie += `; SameSite=${options.sameSite}`;
|
|
53
|
-
this.headers.append('Set-Cookie', cookie);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
deleteCookie(name, options = {}) {
|
|
57
|
-
this.setCookie(name, '', { ...options, maxAge: 0, expires: new Date(0) });
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
text(data, status = this.status, headers = {}) {
|
|
61
|
-
const h = this._buildHeaders(headers);
|
|
62
|
-
h.set('Content-Type', 'text/plain');
|
|
63
|
-
return new Response(data, { status, headers: h });
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
json(data, status = this.status, headers = {}) {
|
|
67
|
-
const h = this._buildHeaders(headers);
|
|
68
|
-
h.set('Content-Type', 'application/json');
|
|
69
|
-
return new Response(JSON.stringify(data), { status, headers: h });
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
html(data, status = this.status, headers = {}) {
|
|
73
|
-
const h = this._buildHeaders(headers);
|
|
74
|
-
h.set('Content-Type', 'text/html');
|
|
75
|
-
return new Response(data, { status, headers: h });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
_buildHeaders(headers) {
|
|
79
|
-
if (Object.keys(headers).length === 0) {
|
|
80
|
-
return this.headers;
|
|
81
|
-
}
|
|
82
|
-
const h = new Headers(this.headers);
|
|
83
|
-
for (const key in headers) {
|
|
84
|
-
if (Object.prototype.hasOwnProperty.call(headers, key)) {
|
|
85
|
-
h.set(key, headers[key]);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return h;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export const Fragment = Symbol('Fragment');
|
|
93
|
-
|
|
94
|
-
// ---------- JSX Runtime ----------
|
|
95
|
-
|
|
96
|
-
export function jsx(type, props, ...children) {
|
|
97
|
-
const normalizedProps = props || {};
|
|
98
|
-
const flatChildren = children.flat(Infinity);
|
|
99
|
-
return {
|
|
100
|
-
type,
|
|
101
|
-
props: normalizedProps,
|
|
102
|
-
children: flatChildren,
|
|
103
|
-
__isJSX: true,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// HTML void elements that cannot have children
|
|
108
|
-
const VOID_ELEMENTS = new Set([
|
|
109
|
-
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
|
|
110
|
-
'link', 'meta', 'param', 'source', 'track', 'wbr'
|
|
111
|
-
]);
|
|
112
|
-
|
|
113
|
-
// Boolean HTML attributes that should be rendered without a value when true
|
|
114
|
-
const BOOLEAN_ATTRIBUTES = new Set([
|
|
115
|
-
'allowfullscreen', 'async', 'autofocus', 'autoplay', 'checked',
|
|
116
|
-
'controls', 'default', 'defer', 'disabled', 'formnovalidate',
|
|
117
|
-
'hidden', 'inert', 'ismap', 'itemscope', 'loop', 'multiple',
|
|
118
|
-
'muted', 'nomodule', 'novalidate', 'open', 'playsinline',
|
|
119
|
-
'readonly', 'required', 'reversed', 'selected'
|
|
120
|
-
]);
|
|
121
|
-
|
|
122
|
-
// CSS properties that do not require a unit when numeric
|
|
123
|
-
const UNITLESS_PROPERTIES = new Set([
|
|
124
|
-
'animation-iteration-count', 'border-image-outset', 'border-image-slice',
|
|
125
|
-
'border-image-width', 'box-flex', 'box-flex-group', 'box-ordinal-group',
|
|
126
|
-
'column-count', 'columns', 'flex', 'flex-grow', 'flex-positive',
|
|
127
|
-
'flex-shrink', 'flex-negative', 'flex-order', 'grid-row', 'grid-row-end',
|
|
128
|
-
'grid-row-span', 'grid-row-start', 'grid-column', 'grid-column-end',
|
|
129
|
-
'grid-column-span', 'grid-column-start', 'font-weight', 'line-clamp',
|
|
130
|
-
'line-height', 'opacity', 'order', 'orphans', 'tab-size', 'widows',
|
|
131
|
-
'z-index', 'zoom', 'fill-opacity', 'flood-opacity', 'stop-opacity',
|
|
132
|
-
'stroke-dasharray', 'stroke-dashoffset', 'stroke-miterlimit',
|
|
133
|
-
'stroke-opacity', 'stroke-width'
|
|
134
|
-
]);
|
|
135
|
-
|
|
136
|
-
function escapeHtml(str) {
|
|
137
|
-
const HTML_ESCAPE_MAP = {
|
|
138
|
-
'&': '&',
|
|
139
|
-
'<': '<',
|
|
140
|
-
'>': '>',
|
|
141
|
-
'"': '"',
|
|
142
|
-
"'": '''
|
|
143
|
-
};
|
|
144
|
-
return str.replace(/[&<>"']/g, char => HTML_ESCAPE_MAP[char]);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function camelToKebab(str) {
|
|
148
|
-
return str
|
|
149
|
-
.replace(/([A-Z])/g, '-$1')
|
|
150
|
-
.toLowerCase()
|
|
151
|
-
.replace(/^-/, '');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function styleObjectToString(style) {
|
|
155
|
-
if (!style || typeof style !== 'object') return '';
|
|
156
|
-
const entries = Object.entries(style);
|
|
157
|
-
if (entries.length === 0) return '';
|
|
158
|
-
return entries
|
|
159
|
-
.map(([prop, value]) => {
|
|
160
|
-
// Convert camelCase to kebab-case, handling vendor prefixes
|
|
161
|
-
let kebabProp = camelToKebab(prop);
|
|
162
|
-
// Add px unit for numeric values unless property is unitless
|
|
163
|
-
if (typeof value === 'number' && !UNITLESS_PROPERTIES.has(kebabProp)) {
|
|
164
|
-
value = `${value}px`;
|
|
165
|
-
}
|
|
166
|
-
return `${kebabProp}:${value}`;
|
|
167
|
-
})
|
|
168
|
-
.join(';');
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function renderToString(node) {
|
|
172
|
-
if (node == null || typeof node === 'boolean') return '';
|
|
173
|
-
if (typeof node === 'string' || typeof node === 'number') {
|
|
174
|
-
return escapeHtml(String(node));
|
|
175
|
-
}
|
|
176
|
-
if (Array.isArray(node)) {
|
|
177
|
-
const parts = [];
|
|
178
|
-
for (let i = 0; i < node.length; i++) {
|
|
179
|
-
parts.push(renderToString(node[i]));
|
|
180
|
-
}
|
|
181
|
-
return parts.join('');
|
|
182
|
-
}
|
|
183
|
-
if (!node.__isJSX) {
|
|
184
|
-
return escapeHtml(String(node));
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const { type, props, children } = node;
|
|
188
|
-
|
|
189
|
-
if (type === Fragment) {
|
|
190
|
-
const parts = [];
|
|
191
|
-
for (let i = 0; i < children.length; i++) {
|
|
192
|
-
parts.push(renderToString(children[i]));
|
|
193
|
-
}
|
|
194
|
-
return parts.join('');
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (typeof type === 'symbol') {
|
|
198
|
-
return '';
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (typeof type === 'function') {
|
|
202
|
-
const componentResult = type({ ...props, children });
|
|
203
|
-
return renderToString(componentResult);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Handle void elements: they cannot have children
|
|
207
|
-
const isVoid = VOID_ELEMENTS.has(type);
|
|
208
|
-
|
|
209
|
-
const attrsParts = [];
|
|
210
|
-
for (const key in props) {
|
|
211
|
-
if (key === 'children' || key === 'key' || key === 'ref') continue;
|
|
212
|
-
const value = props[key];
|
|
213
|
-
|
|
214
|
-
// Skip null/undefined/false
|
|
215
|
-
if (value == null || value === false) continue;
|
|
216
|
-
|
|
217
|
-
// dangerouslySetInnerHTML will be handled separately
|
|
218
|
-
if (key === 'dangerouslySetInnerHTML') continue;
|
|
219
|
-
|
|
220
|
-
// Attribute name mapping
|
|
221
|
-
let attrName = key;
|
|
222
|
-
if (key === 'className' || key === 'class') {
|
|
223
|
-
attrName = 'class';
|
|
224
|
-
} else if (key === 'htmlFor' || key === 'for') {
|
|
225
|
-
attrName = 'for';
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Boolean attributes: if true, just the attribute name; if false, skip
|
|
229
|
-
if (BOOLEAN_ATTRIBUTES.has(attrName)) {
|
|
230
|
-
if (value === true) {
|
|
231
|
-
attrsParts.push(` ${attrName}`);
|
|
232
|
-
}
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Non-boolean attribute with true -> render as "true"
|
|
237
|
-
if (value === true) {
|
|
238
|
-
attrsParts.push(` ${attrName}="true"`);
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Style object handling
|
|
243
|
-
if (attrName === 'style' && typeof value === 'object') {
|
|
244
|
-
const styleStr = styleObjectToString(value);
|
|
245
|
-
if (styleStr) {
|
|
246
|
-
attrsParts.push(` style="${escapeHtml(styleStr)}"`);
|
|
247
|
-
}
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Regular attribute
|
|
252
|
-
attrsParts.push(` ${attrName}="${escapeHtml(String(value))}"`);
|
|
253
|
-
}
|
|
254
|
-
const attrs = attrsParts.join('');
|
|
255
|
-
|
|
256
|
-
// Handle dangerouslySetInnerHTML
|
|
257
|
-
let innerHTML = '';
|
|
258
|
-
if (props && props.dangerouslySetInnerHTML && props.dangerouslySetInnerHTML.__html != null) {
|
|
259
|
-
innerHTML = props.dangerouslySetInnerHTML.__html;
|
|
260
|
-
} else {
|
|
261
|
-
const childParts = [];
|
|
262
|
-
for (let i = 0; i < children.length; i++) {
|
|
263
|
-
childParts.push(renderToString(children[i]));
|
|
264
|
-
}
|
|
265
|
-
innerHTML = childParts.join('');
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (isVoid) {
|
|
269
|
-
return `<${type}${attrs}/>`;
|
|
270
|
-
} else {
|
|
271
|
-
return `<${type}${attrs}>${innerHTML}</${type}>`;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ---------- Trie-based router for dynamic routes ----------
|
|
276
|
-
class TrieNode {
|
|
277
|
-
constructor() {
|
|
278
|
-
this.children = new Map(); // exact segment -> TrieNode
|
|
279
|
-
this.paramChild = null; // node for :param
|
|
280
|
-
this.paramName = null; // param name if paramChild exists
|
|
281
|
-
this.handler = null; // { handler, options }
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
class RouteTrie {
|
|
286
|
-
constructor() {
|
|
287
|
-
this.root = new TrieNode();
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
add(path, handler, options) {
|
|
291
|
-
const segments = path.split('/').filter(Boolean);
|
|
292
|
-
let node = this.root;
|
|
293
|
-
for (const seg of segments) {
|
|
294
|
-
if (seg.startsWith(':')) {
|
|
295
|
-
if (!node.paramChild) {
|
|
296
|
-
node.paramChild = new TrieNode();
|
|
297
|
-
node.paramName = seg.slice(1);
|
|
298
|
-
}
|
|
299
|
-
node = node.paramChild;
|
|
300
|
-
} else {
|
|
301
|
-
if (!node.children.has(seg)) {
|
|
302
|
-
node.children.set(seg, new TrieNode());
|
|
303
|
-
}
|
|
304
|
-
node = node.children.get(seg);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
node.handler = { handler, options };
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
match(path) {
|
|
311
|
-
const segments = path.split('/').filter(Boolean);
|
|
312
|
-
let node = this.root;
|
|
313
|
-
const params = {};
|
|
314
|
-
for (const seg of segments) {
|
|
315
|
-
if (node.children.has(seg)) {
|
|
316
|
-
node = node.children.get(seg);
|
|
317
|
-
} else if (node.paramChild) {
|
|
318
|
-
params[node.paramName] = seg;
|
|
319
|
-
node = node.paramChild;
|
|
320
|
-
} else {
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
if (node.handler) {
|
|
325
|
-
return {
|
|
326
|
-
handler: node.handler.handler,
|
|
327
|
-
options: node.handler.options,
|
|
328
|
-
params
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
return null;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
|
|
336
|
-
|
|
337
|
-
export class Edge {
|
|
338
|
-
constructor() {
|
|
339
|
-
// Static routes: nested map method -> path -> handler
|
|
340
|
-
this.staticRoutes = new Map();
|
|
341
|
-
for (const m of HTTP_METHODS) {
|
|
342
|
-
this.staticRoutes.set(m, new Map());
|
|
343
|
-
}
|
|
344
|
-
// Dynamic routes per method: RouteTrie
|
|
345
|
-
this.dynamicTries = {};
|
|
346
|
-
for (const m of HTTP_METHODS) {
|
|
347
|
-
this.dynamicTries[m] = new RouteTrie();
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
this.authKvBinding = 'AUTH_KV';
|
|
351
|
-
this.rateLimitKvBinding = 'RATE_LIMIT_KV';
|
|
352
|
-
this.defaults = {
|
|
353
|
-
cors: { origin: '*', methods: 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD' },
|
|
354
|
-
};
|
|
355
|
-
this.scheduledHandler = null;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
_addRoute(method, path, options, handler) {
|
|
359
|
-
if (typeof options === 'boolean') options = { auth: options };
|
|
360
|
-
if (typeof options === 'function') {
|
|
361
|
-
handler = options;
|
|
362
|
-
options = {};
|
|
363
|
-
}
|
|
364
|
-
if (typeof handler !== 'function') {
|
|
365
|
-
throw new Error(`Handler for ${method} ${path} must be a function`);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// Merge with defaults once
|
|
369
|
-
const mergedOptions = { ...this.defaults, ...(options || {}) };
|
|
370
|
-
|
|
371
|
-
if (!path.includes(':')) {
|
|
372
|
-
// Static route (optimized lookup)
|
|
373
|
-
const methodMap = this.staticRoutes.get(method);
|
|
374
|
-
methodMap.set(path, { handler, options: mergedOptions });
|
|
375
|
-
} else {
|
|
376
|
-
// Dynamic route
|
|
377
|
-
this.dynamicTries[method].add(path, handler, mergedOptions);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
get(path, options, handler) { this._addRoute('GET', path, options, handler); }
|
|
382
|
-
post(path, options, handler) { this._addRoute('POST', path, options, handler); }
|
|
383
|
-
put(path, options, handler) { this._addRoute('PUT', path, options, handler); }
|
|
384
|
-
delete(path, options, handler) { this._addRoute('DELETE', path, options, handler); }
|
|
385
|
-
patch(path, options, handler) { this._addRoute('PATCH', path, options, handler); }
|
|
386
|
-
options(path, options, handler) { this._addRoute('OPTIONS', path, options, handler); }
|
|
387
|
-
head(path, options, handler) { this._addRoute('HEAD', path, options, handler); }
|
|
388
|
-
|
|
389
|
-
scheduled(handler) {
|
|
390
|
-
this.scheduledHandler = handler;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
async _processAuth(ctx, flag) {
|
|
394
|
-
if (!flag) return true;
|
|
395
|
-
const token = ctx.getCookie('auth_token') ||
|
|
396
|
-
ctx.req.headers.get('Authorization')?.replace(/^Bearer\s+/i, '');
|
|
397
|
-
if (!token) return false;
|
|
398
|
-
|
|
399
|
-
const kv = ctx.env[this.authKvBinding];
|
|
400
|
-
if (!kv) return false;
|
|
401
|
-
const data = await kv.get(token);
|
|
402
|
-
if (!data) return false;
|
|
403
|
-
|
|
404
|
-
if (typeof flag === 'object') {
|
|
405
|
-
try {
|
|
406
|
-
const payload = JSON.parse(data);
|
|
407
|
-
if (flag.role && payload.role !== flag.role) return false;
|
|
408
|
-
if (flag.scopes) {
|
|
409
|
-
const userScopes = payload.scopes || [];
|
|
410
|
-
if (!flag.scopes.every(s => userScopes.includes(s))) return false;
|
|
411
|
-
}
|
|
412
|
-
} catch {}
|
|
413
|
-
}
|
|
414
|
-
return true;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
async _processRateLimit(ctx, flag) {
|
|
418
|
-
if (!flag) return true;
|
|
419
|
-
const options = flag === true ? {} : flag;
|
|
420
|
-
const { max = 100, window = 60 } = options;
|
|
421
|
-
const key = `rl:${ctx.req.headers.get('CF-Connecting-IP') || 'unknown'}`;
|
|
422
|
-
const kv = ctx.env[this.rateLimitKvBinding];
|
|
423
|
-
if (!kv) return true;
|
|
424
|
-
let count = await kv.get(key, 'json') || 0;
|
|
425
|
-
if (count >= max) return false;
|
|
426
|
-
count++;
|
|
427
|
-
await kv.put(key, JSON.stringify(count), { expirationTtl: window });
|
|
428
|
-
return true;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
_processCors(ctx, flag) {
|
|
432
|
-
if (!flag) return;
|
|
433
|
-
const opts = typeof flag === 'object' ? flag : this.defaults.cors;
|
|
434
|
-
ctx.headers.set('Access-Control-Allow-Origin', opts.origin || '*');
|
|
435
|
-
ctx.headers.set('Access-Control-Allow-Methods', opts.methods || 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD');
|
|
436
|
-
ctx.headers.set('Access-Control-Allow-Headers', opts.headers || 'Content-Type, Authorization');
|
|
437
|
-
ctx.headers.set('Access-Control-Max-Age', '86400');
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
_processLog(ctx, flag) {
|
|
441
|
-
if (!flag) return;
|
|
442
|
-
console.log(`${ctx.req.method} ${ctx.req.url} - ${ctx.status}`);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
_processCompress(request, response) {
|
|
446
|
-
const acceptEncoding = request.headers.get('Accept-Encoding') || '';
|
|
447
|
-
let encoding = null;
|
|
448
|
-
if (acceptEncoding.includes('gzip')) encoding = 'gzip';
|
|
449
|
-
else if (acceptEncoding.includes('deflate')) encoding = 'deflate';
|
|
450
|
-
else if (acceptEncoding.includes('br')) encoding = 'br';
|
|
451
|
-
if (!encoding || !response.body) return response;
|
|
452
|
-
|
|
453
|
-
const stream = response.body.pipeThrough(new CompressionStream(encoding));
|
|
454
|
-
const newHeaders = new Headers(response.headers);
|
|
455
|
-
newHeaders.set('Content-Encoding', encoding);
|
|
456
|
-
newHeaders.set('Vary', 'Accept-Encoding');
|
|
457
|
-
return new Response(stream, { status: response.status, statusText: response.statusText, headers: newHeaders });
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
async _validate(ctx, flag) {
|
|
461
|
-
if (!flag) return true;
|
|
462
|
-
if (typeof flag === 'function') {
|
|
463
|
-
try {
|
|
464
|
-
const result = await flag(ctx);
|
|
465
|
-
return !!result;
|
|
466
|
-
} catch {
|
|
467
|
-
return false;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
return true;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
async _cacheGet(request) {
|
|
474
|
-
const cache = caches.default;
|
|
475
|
-
const cached = await cache.match(request);
|
|
476
|
-
if (!cached) return null;
|
|
477
|
-
return cached;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
async _cachePut(request, response, flag) {
|
|
481
|
-
if (!flag) return;
|
|
482
|
-
const options = flag === true ? {} : flag;
|
|
483
|
-
const { ttl = 3600, staleWhileRevalidate = 0 } = options;
|
|
484
|
-
const cache = caches.default;
|
|
485
|
-
const responseClone = response.clone();
|
|
486
|
-
responseClone.headers.set('Cache-Control', `max-age=${ttl}${staleWhileRevalidate > 0 ? `, stale-while-revalidate=${staleWhileRevalidate}` : ''}`);
|
|
487
|
-
responseClone.headers.delete('Set-Cookie');
|
|
488
|
-
await cache.put(request, responseClone);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
async fetch(request, env, executionCtx) {
|
|
492
|
-
const url = new URL(request.url);
|
|
493
|
-
const path = url.pathname;
|
|
494
|
-
const method = request.method;
|
|
495
|
-
|
|
496
|
-
// 1. Static route (fast lookup via nested map)
|
|
497
|
-
const methodMap = this.staticRoutes.get(method);
|
|
498
|
-
if (methodMap) {
|
|
499
|
-
const staticRoute = methodMap.get(path);
|
|
500
|
-
if (staticRoute) {
|
|
501
|
-
return this._handleRoute(staticRoute.handler, staticRoute.options, request, env, executionCtx, {}, url);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// 2. Dynamic route via trie
|
|
506
|
-
const trie = this.dynamicTries[method];
|
|
507
|
-
if (trie) {
|
|
508
|
-
const match = trie.match(path);
|
|
509
|
-
if (match) {
|
|
510
|
-
return this._handleRoute(match.handler, match.options, request, env, executionCtx, match.params, url);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return new Response('Not Found', { status: 404 });
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
async _handleRoute(handler, options, request, env, executionCtx, params, parsedUrl) {
|
|
518
|
-
const ctx = new Context(request, env, executionCtx, params, parsedUrl);
|
|
519
|
-
|
|
520
|
-
if (!(await this._validate(ctx, options.validate))) {
|
|
521
|
-
return ctx.text('Validation failed', 400);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (!(await this._processAuth(ctx, options.auth))) {
|
|
525
|
-
return ctx.text('Unauthorized', 401);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (!(await this._processRateLimit(ctx, options.rateLimit))) {
|
|
529
|
-
return ctx.text('Too Many Requests', 429);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// Caching (GET only)
|
|
533
|
-
let cachedResponse = null;
|
|
534
|
-
if (options.cache && request.method === 'GET') {
|
|
535
|
-
cachedResponse = await this._cacheGet(request);
|
|
536
|
-
if (cachedResponse) {
|
|
537
|
-
this._processCors(ctx, options.cors);
|
|
538
|
-
this._processLog(ctx, options.log);
|
|
539
|
-
return cachedResponse;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
this._processCors(ctx, options.cors);
|
|
544
|
-
|
|
545
|
-
let response;
|
|
546
|
-
try {
|
|
547
|
-
const result = await handler(ctx);
|
|
548
|
-
if (result && result.__isJSX) {
|
|
549
|
-
response = ctx.html(renderToString(result));
|
|
550
|
-
} else {
|
|
551
|
-
response = result instanceof Response ? result : ctx.text('OK');
|
|
552
|
-
}
|
|
553
|
-
ctx.status = response.status;
|
|
554
|
-
} catch (err) {
|
|
555
|
-
console.error(err);
|
|
556
|
-
response = ctx.text('Internal Server Error', 500);
|
|
557
|
-
ctx.status = 500;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// Cache successful GET responses
|
|
561
|
-
if (options.cache && request.method === 'GET' && response.status === 200) {
|
|
562
|
-
ctx.executionCtx.waitUntil(this._cachePut(request, response.clone(), options.cache));
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
if (options.compress) {
|
|
566
|
-
response = this._processCompress(request, response);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
this._processLog(ctx, options.log);
|
|
570
|
-
|
|
571
|
-
return response;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
File without changes
|
/package/{README.md → readme.md}
RENAMED
|
File without changes
|