@netlify/edge-functions-dev 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ var xn=Object.defineProperty;var it=(e,t)=>{for(var n in t)xn(e,n,{get:t[n],enumerable:!0})};var M=["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","+","/"];function Re(e){let t=typeof e=="string"?new TextEncoder().encode(e):e instanceof Uint8Array?e:new Uint8Array(e),n="",r,o=t.length;for(r=2;r<o;r+=3)n+=M[t[r-2]>>2],n+=M[(t[r-2]&3)<<4|t[r-1]>>4],n+=M[(t[r-1]&15)<<2|t[r]>>6],n+=M[t[r]&63];return r===o+1&&(n+=M[t[r-2]>>2],n+=M[(t[r-2]&3)<<4],n+="=="),r===o&&(n+=M[t[r-2]>>2],n+=M[(t[r-2]&3)<<4|t[r-1]>>4],n+=M[(t[r-1]&15)<<2],n+="="),n}function J(e){let t=atob(e),n=t.length,r=new Uint8Array(n);for(let o=0;o<n;o++)r[o]=t.charCodeAt(o);return r}function at(e){if(!e)return{};try{return JSON.parse(new TextDecoder().decode(J(e)))}catch{return{}}}function ct(e){if(!e)return{};try{return JSON.parse(atob(e))}catch{return{}}}function lt(e,t,n){let{primary_region:r,token:o,url:s,url_uncached:i}=e;if(!o||!s||!n.id)return;let a={deployID:t.id,edgeURL:s,primaryRegion:r,siteID:n.id,token:o,uncachedEdgeURL:i};globalThis.netlifyBlobsContext=Re(JSON.stringify(a))}function ut(e){if(e===null)return{};try{let t=new TextDecoder().decode(J(e)),{postal_code:n,...r}=JSON.parse(t);return Object.fromEntries(Object.entries({...r,postalCode:n}).filter(([s,i])=>i!==void 0))}catch{return{}}}var ft=["if-match","if-none-match","if-modified-since","if-unmodified-since","if-range"],je=(e,t)=>{let n={};return t.forEach((r,o)=>{e.get(o)!==r&&(n[o]=r)}),e.forEach((r,o)=>{t.has(o)||(n[o]="")}),n},Ge=(e,t)=>{let n=je(e,t);return Object.keys(n).length!==0},dt=e=>{let t={};return e.forEach((n,r)=>{(r==="set-cookie"?[n]:n.split(", ")).forEach(s=>{t[r]=[...t[r]||[],s]})}),t},ye=(e,t)=>{let n=new Response(e.body,e);return t(n.headers),n},gt=e=>e.startsWith("x-nf-")||e.startsWith("x-deno-")||e.startsWith("x-bb-");var ht=e=>{let t=e?.cause instanceof Error?ht(e.cause):e.cause;return{error:e.message,error_cause:t,error_stack:e.stack}},we=class e{fields;filter;logLevel;message;rawLogger;requestID;__netlifyStructuredLogger;constructor(t,n,r,o,s,i){this.filter=i,this.fields=n??{},this.logLevel=s??2,this.message=t??"",this.rawLogger=o,this.requestID=r,this.__netlifyStructuredLogger=1}debug(t){if(this.logLevel>1||this.filter&&!this.filter(t,this.fields))return;(this.rawLogger??globalThis.console.log)(new e(t,this.fields,this.requestID,this.rawLogger,this.logLevel,this.filter))}error(t){if(this.logLevel>3||this.filter&&!this.filter(t,this.fields))return;(this.rawLogger??globalThis.console.log)(new e(t,this.fields,this.requestID,this.rawLogger,this.logLevel,this.filter))}log(t){if(this.logLevel>2||this.filter&&!this.filter(t,this.fields))return;(this.rawLogger??globalThis.console.log)(new e(t,this.fields,this.requestID,this.rawLogger,this.logLevel,this.filter))}serialize(){return{fields:this.fields,message:this.message,requestID:this.requestID}}withError(t){let n=t instanceof Error?ht(t):{error:t};return this.withFields(n)}withFields(t){return new e(this.message,{...this.fields,...t},this.requestID,this.rawLogger,this.logLevel,this.filter)}withFilter(t){if(!t)return this;if(typeof t!="function")throw new TypeError("Filter must be a function");return new e(this.message,this.fields,this.requestID,this.rawLogger,this.logLevel,t)}withLogLevel(t){return new e(this.message,this.fields,this.requestID,this.rawLogger,t,this.filter)}withRawLogger(t){return new e(this.message,this.fields,this.requestID,t,this.logLevel,this.filter)}withRequestID(t){return t===null?this:new e(this.message,this.fields,t,this.rawLogger,this.logLevel,this.filter)}},pt=new we;var mt=!1,H=()=>Deno.env.get("DENO_DEPLOYMENT_ID")?"production":"local",xt=e=>{if(mt)return;let t=se(e);t.id&&Deno.env.set("SITE_ID",t.id),t.name&&Deno.env.set("SITE_NAME",t.name),t.url&&Deno.env.set("URL",t.url);let n=Rt(e);n&&Deno.env.set("NETLIFY_PURGE_API_TOKEN",n),mt=!0};import{AsyncLocalStorage as Xn}from"node:async_hooks";var yt=(()=>{let{Deno:e}=globalThis;if(typeof e?.build?.os=="string")return e.build.os;let{navigator:t}=globalThis;return t?.appVersion?.includes?.("Win")?"windows":"linux"})(),ie=yt==="windows";var ce={};it(ce,{basename:()=>vn,delimiter:()=>En,dirname:()=>Dn,extname:()=>kn,format:()=>_n,fromFileUrl:()=>qn,isAbsolute:()=>bt,join:()=>Sn,normalize:()=>wt,parse:()=>Fn,relative:()=>Ln,resolve:()=>Ae,sep:()=>An,toFileUrl:()=>In,toNamespacedPath:()=>Pn});function S(e){if(typeof e!="string")throw new TypeError(`Path must be a string. Received ${JSON.stringify(e)}`)}function be(e){return e===47}function m(e){return be(e)||e===92}function B(e){return e>=97&&e<=122||e>=65&&e<=90}function Q(e,t,n,r){let o="",s=0,i=-1,a=0,l;for(let d=0,f=e.length;d<=f;++d){if(d<f)l=e.charCodeAt(d);else{if(r(l))break;l=47}if(r(l)){if(!(i===d-1||a===1))if(i!==d-1&&a===2){if(o.length<2||s!==2||o.charCodeAt(o.length-1)!==46||o.charCodeAt(o.length-2)!==46){if(o.length>2){let c=o.lastIndexOf(n);c===-1?(o="",s=0):(o=o.slice(0,c),s=o.length-1-o.lastIndexOf(n)),i=d,a=0;continue}else if(o.length===2||o.length===1){o="",s=0,i=d,a=0;continue}}t&&(o.length>0?o+=`${n}..`:o="..",s=2)}else o.length>0?o+=n+e.slice(i+1,d):o=e.slice(i+1,d),s=d-i-1;i=d,a=0}else l===46&&a!==-1?++a:a=-1}return o}function Ce(e,t){let n=t.dir||t.root,r=t.base||(t.name||"")+(t.ext||"");return n?n===t.root?n+r:n+e+r:r}var Cn={" ":"%09","\n":"%0A","\v":"%0B","\f":"%0C","\r":"%0D"," ":"%20"};function Te(e){return e.replaceAll(/[\s]/g,t=>Cn[t]??t)}var Ke=class extends Error{constructor(t){super(t),this.name="DenoStdInternalError"}};function ae(e,t=""){if(!e)throw new Ke(t)}var An="\\",En=";";function Ae(...e){let t="",n="",r=!1;for(let o=e.length-1;o>=-1;o--){let s,{Deno:i}=globalThis;if(o>=0)s=e[o];else if(t){if(typeof i?.env?.get!="function"||typeof i?.cwd!="function")throw new TypeError("Resolved a relative path without a CWD.");s=i.cwd(),(s===void 0||s.slice(0,3).toLowerCase()!==`${t.toLowerCase()}\\`)&&(s=`${t}\\`)}else{if(typeof i?.cwd!="function")throw new TypeError("Resolved a drive-letter-less path without a CWD.");s=i.cwd()}S(s);let a=s.length;if(a===0)continue;let l=0,d="",f=!1,c=s.charCodeAt(0);if(a>1)if(m(c))if(f=!0,m(s.charCodeAt(1))){let u=2,b=u;for(;u<a&&!m(s.charCodeAt(u));++u);if(u<a&&u!==b){let x=s.slice(b,u);for(b=u;u<a&&m(s.charCodeAt(u));++u);if(u<a&&u!==b){for(b=u;u<a&&!m(s.charCodeAt(u));++u);u===a?(d=`\\\\${x}\\${s.slice(b)}`,l=u):u!==b&&(d=`\\\\${x}\\${s.slice(b,u)}`,l=u)}}}else l=1;else B(c)&&s.charCodeAt(1)===58&&(d=s.slice(0,2),l=2,a>2&&m(s.charCodeAt(2))&&(f=!0,l=3));else m(c)&&(l=1,f=!0);if(!(d.length>0&&t.length>0&&d.toLowerCase()!==t.toLowerCase())&&(t.length===0&&d.length>0&&(t=d),r||(n=`${s.slice(l)}\\${n}`,r=f),r&&t.length>0))break}return n=Q(n,!r,"\\",m),t+(r?"\\":"")+n||"."}function wt(e){S(e);let t=e.length;if(t===0)return".";let n=0,r,o=!1,s=e.charCodeAt(0);if(t>1)if(m(s))if(o=!0,m(e.charCodeAt(1))){let a=2,l=a;for(;a<t&&!m(e.charCodeAt(a));++a);if(a<t&&a!==l){let d=e.slice(l,a);for(l=a;a<t&&m(e.charCodeAt(a));++a);if(a<t&&a!==l){for(l=a;a<t&&!m(e.charCodeAt(a));++a);if(a===t)return`\\\\${d}\\${e.slice(l)}\\`;a!==l&&(r=`\\\\${d}\\${e.slice(l,a)}`,n=a)}}}else n=1;else B(s)&&e.charCodeAt(1)===58&&(r=e.slice(0,2),n=2,t>2&&m(e.charCodeAt(2))&&(o=!0,n=3));else if(m(s))return"\\";let i;return n<t?i=Q(e.slice(n),!o,"\\",m):i="",i.length===0&&!o&&(i="."),i.length>0&&m(e.charCodeAt(t-1))&&(i+="\\"),r===void 0?o?i.length>0?`\\${i}`:"\\":i.length>0?i:"":o?i.length>0?`${r}\\${i}`:`${r}\\`:i.length>0?r+i:r}function bt(e){S(e);let t=e.length;if(t===0)return!1;let n=e.charCodeAt(0);return m(n)?!0:!!(B(n)&&t>2&&e.charCodeAt(1)===58&&m(e.charCodeAt(2)))}function Sn(...e){let t=e.length;if(t===0)return".";let n,r=null;for(let i=0;i<t;++i){let a=e[i];S(a),a.length>0&&(n===void 0?n=r=a:n+=`\\${a}`)}if(n===void 0)return".";let o=!0,s=0;if(ae(r!=null),m(r.charCodeAt(0))){++s;let i=r.length;i>1&&m(r.charCodeAt(1))&&(++s,i>2&&(m(r.charCodeAt(2))?++s:o=!1))}if(o){for(;s<n.length&&m(n.charCodeAt(s));++s);s>=2&&(n=`\\${n.slice(s)}`)}return wt(n)}function Ln(e,t){if(S(e),S(t),e===t)return"";let n=Ae(e),r=Ae(t);if(n===r||(e=n.toLowerCase(),t=r.toLowerCase(),e===t))return"";let o=0,s=e.length;for(;o<s&&e.charCodeAt(o)===92;++o);for(;s-1>o&&e.charCodeAt(s-1)===92;--s);let i=s-o,a=0,l=t.length;for(;a<l&&t.charCodeAt(a)===92;++a);for(;l-1>a&&t.charCodeAt(l-1)===92;--l);let d=l-a,f=i<d?i:d,c=-1,u=0;for(;u<=f;++u){if(u===f){if(d>f){if(t.charCodeAt(a+u)===92)return r.slice(a+u+1);if(u===2)return r.slice(a+u)}i>f&&(e.charCodeAt(o+u)===92?c=u:u===2&&(c=3));break}let x=e.charCodeAt(o+u),y=t.charCodeAt(a+u);if(x!==y)break;x===92&&(c=u)}if(u!==f&&c===-1)return r;let b="";for(c===-1&&(c=0),u=o+c+1;u<=s;++u)(u===s||e.charCodeAt(u)===92)&&(b.length===0?b+="..":b+="\\..");return b.length>0?b+r.slice(a+c,l):(a+=c,r.charCodeAt(a)===92&&++a,r.slice(a,l))}function Pn(e){if(typeof e!="string")return e;if(e.length===0)return"";let t=Ae(e);if(t.length>=3){if(t.charCodeAt(0)===92){if(t.charCodeAt(1)===92){let n=t.charCodeAt(2);if(n!==63&&n!==46)return`\\\\?\\UNC\\${t.slice(2)}`}}else if(B(t.charCodeAt(0))&&t.charCodeAt(1)===58&&t.charCodeAt(2)===92)return`\\\\?\\${t}`}return e}function Dn(e){S(e);let t=e.length;if(t===0)return".";let n=-1,r=-1,o=!0,s=0,i=e.charCodeAt(0);if(t>1)if(m(i)){if(n=s=1,m(e.charCodeAt(1))){let a=2,l=a;for(;a<t&&!m(e.charCodeAt(a));++a);if(a<t&&a!==l){for(l=a;a<t&&m(e.charCodeAt(a));++a);if(a<t&&a!==l){for(l=a;a<t&&!m(e.charCodeAt(a));++a);if(a===t)return e;a!==l&&(n=s=a+1)}}}}else B(i)&&e.charCodeAt(1)===58&&(n=s=2,t>2&&m(e.charCodeAt(2))&&(n=s=3));else if(m(i))return e;for(let a=t-1;a>=s;--a)if(m(e.charCodeAt(a))){if(!o){r=a;break}}else o=!1;if(r===-1){if(n===-1)return".";r=n}return e.slice(0,r)}function vn(e,t=""){if(t!==void 0&&typeof t!="string")throw new TypeError('"ext" argument must be a string');S(e);let n=0,r=-1,o=!0,s;if(e.length>=2){let i=e.charCodeAt(0);B(i)&&e.charCodeAt(1)===58&&(n=2)}if(t!==void 0&&t.length>0&&t.length<=e.length){if(t.length===e.length&&t===e)return"";let i=t.length-1,a=-1;for(s=e.length-1;s>=n;--s){let l=e.charCodeAt(s);if(m(l)){if(!o){n=s+1;break}}else a===-1&&(o=!1,a=s+1),i>=0&&(l===t.charCodeAt(i)?--i===-1&&(r=s):(i=-1,r=a))}return n===r?r=a:r===-1&&(r=e.length),e.slice(n,r)}else{for(s=e.length-1;s>=n;--s)if(m(e.charCodeAt(s))){if(!o){n=s+1;break}}else r===-1&&(o=!1,r=s+1);return r===-1?"":e.slice(n,r)}}function kn(e){S(e);let t=0,n=-1,r=0,o=-1,s=!0,i=0;e.length>=2&&e.charCodeAt(1)===58&&B(e.charCodeAt(0))&&(t=r=2);for(let a=e.length-1;a>=t;--a){let l=e.charCodeAt(a);if(m(l)){if(!s){r=a+1;break}continue}o===-1&&(s=!1,o=a+1),l===46?n===-1?n=a:i!==1&&(i=1):n!==-1&&(i=-1)}return n===-1||o===-1||i===0||i===1&&n===o-1&&n===r+1?"":e.slice(n,o)}function _n(e){if(e===null||typeof e!="object")throw new TypeError(`The "pathObject" argument must be of type Object. Received type ${typeof e}`);return Ce("\\",e)}function Fn(e){S(e);let t={root:"",dir:"",base:"",ext:"",name:""},n=e.length;if(n===0)return t;let r=0,o=e.charCodeAt(0);if(n>1){if(m(o)){if(r=1,m(e.charCodeAt(1))){let c=2,u=c;for(;c<n&&!m(e.charCodeAt(c));++c);if(c<n&&c!==u){for(u=c;c<n&&m(e.charCodeAt(c));++c);if(c<n&&c!==u){for(u=c;c<n&&!m(e.charCodeAt(c));++c);c===n?r=c:c!==u&&(r=c+1)}}}}else if(B(o)&&e.charCodeAt(1)===58)if(r=2,n>2){if(m(e.charCodeAt(2))){if(n===3)return t.root=t.dir=e,t;r=3}}else return t.root=t.dir=e,t}else if(m(o))return t.root=t.dir=e,t;r>0&&(t.root=e.slice(0,r));let s=-1,i=r,a=-1,l=!0,d=e.length-1,f=0;for(;d>=r;--d){if(o=e.charCodeAt(d),m(o)){if(!l){i=d+1;break}continue}a===-1&&(l=!1,a=d+1),o===46?s===-1?s=d:f!==1&&(f=1):s!==-1&&(f=-1)}return s===-1||a===-1||f===0||f===1&&s===a-1&&s===i+1?a!==-1&&(t.base=t.name=e.slice(i,a)):(t.name=e.slice(i,s),t.base=e.slice(i,a),t.ext=e.slice(s,a)),i>0&&i!==r?t.dir=e.slice(0,i-1):t.dir=t.root,t}function qn(e){if(e=e instanceof URL?e:new URL(e),e.protocol!="file:")throw new TypeError("Must be a file URL.");let t=decodeURIComponent(e.pathname.replace(/\//g,"\\").replace(/%(?![0-9A-Fa-f]{2})/g,"%25")).replace(/^\\*([A-Za-z]:)(\\|$)/,"$1\\");return e.hostname!=""&&(t=`\\\\${e.hostname}${t}`),t}function In(e){if(!bt(e))throw new TypeError("Must be an absolute path.");let[,t,n]=e.match(/^(?:[/\\]{2}([^/\\]+)(?=[/\\](?:[^/\\]|$)))?(.*)/),r=new URL("file:///");if(r.pathname=Te(n.replace(/%/g,"%25")),t!=null&&t!="localhost"&&(r.hostname=t,!r.hostname))throw new TypeError("Invalid hostname.");return r}var le={};it(le,{basename:()=>$n,delimiter:()=>Un,dirname:()=>Bn,extname:()=>Wn,format:()=>jn,fromFileUrl:()=>Kn,isAbsolute:()=>Tt,join:()=>On,normalize:()=>Ct,parse:()=>Gn,relative:()=>Mn,resolve:()=>Ve,sep:()=>Nn,toFileUrl:()=>Vn,toNamespacedPath:()=>Hn});var Nn="/",Un=":";function Ve(...e){let t="",n=!1;for(let r=e.length-1;r>=-1&&!n;r--){let o;if(r>=0)o=e[r];else{let{Deno:s}=globalThis;if(typeof s?.cwd!="function")throw new TypeError("Resolved a relative path without a CWD.");o=s.cwd()}S(o),o.length!==0&&(t=`${o}/${t}`,n=o.charCodeAt(0)===47)}return t=Q(t,!n,"/",be),n?t.length>0?`/${t}`:"/":t.length>0?t:"."}function Ct(e){if(S(e),e.length===0)return".";let t=e.charCodeAt(0)===47,n=e.charCodeAt(e.length-1)===47;return e=Q(e,!t,"/",be),e.length===0&&!t&&(e="."),e.length>0&&n&&(e+="/"),t?`/${e}`:e}function Tt(e){return S(e),e.length>0&&e.charCodeAt(0)===47}function On(...e){if(e.length===0)return".";let t;for(let n=0,r=e.length;n<r;++n){let o=e[n];S(o),o.length>0&&(t?t+=`/${o}`:t=o)}return t?Ct(t):"."}function Mn(e,t){if(S(e),S(t),e===t||(e=Ve(e),t=Ve(t),e===t))return"";let n=1,r=e.length;for(;n<r&&e.charCodeAt(n)===47;++n);let o=r-n,s=1,i=t.length;for(;s<i&&t.charCodeAt(s)===47;++s);let a=i-s,l=o<a?o:a,d=-1,f=0;for(;f<=l;++f){if(f===l){if(a>l){if(t.charCodeAt(s+f)===47)return t.slice(s+f+1);if(f===0)return t.slice(s+f)}else o>l&&(e.charCodeAt(n+f)===47?d=f:f===0&&(d=0));break}let u=e.charCodeAt(n+f),b=t.charCodeAt(s+f);if(u!==b)break;u===47&&(d=f)}let c="";for(f=n+d+1;f<=r;++f)(f===r||e.charCodeAt(f)===47)&&(c.length===0?c+="..":c+="/..");return c.length>0?c+t.slice(s+d):(s+=d,t.charCodeAt(s)===47&&++s,t.slice(s))}function Hn(e){return e}function Bn(e){if(S(e),e.length===0)return".";let t=e.charCodeAt(0)===47,n=-1,r=!0;for(let o=e.length-1;o>=1;--o)if(e.charCodeAt(o)===47){if(!r){n=o;break}}else r=!1;return n===-1?t?"/":".":t&&n===1?"//":e.slice(0,n)}function $n(e,t=""){if(t!==void 0&&typeof t!="string")throw new TypeError('"ext" argument must be a string');S(e);let n=0,r=-1,o=!0,s;if(t!==void 0&&t.length>0&&t.length<=e.length){if(t.length===e.length&&t===e)return"";let i=t.length-1,a=-1;for(s=e.length-1;s>=0;--s){let l=e.charCodeAt(s);if(l===47){if(!o){n=s+1;break}}else a===-1&&(o=!1,a=s+1),i>=0&&(l===t.charCodeAt(i)?--i===-1&&(r=s):(i=-1,r=a))}return n===r?r=a:r===-1&&(r=e.length),e.slice(n,r)}else{for(s=e.length-1;s>=0;--s)if(e.charCodeAt(s)===47){if(!o){n=s+1;break}}else r===-1&&(o=!1,r=s+1);return r===-1?"":e.slice(n,r)}}function Wn(e){S(e);let t=-1,n=0,r=-1,o=!0,s=0;for(let i=e.length-1;i>=0;--i){let a=e.charCodeAt(i);if(a===47){if(!o){n=i+1;break}continue}r===-1&&(o=!1,r=i+1),a===46?t===-1?t=i:s!==1&&(s=1):t!==-1&&(s=-1)}return t===-1||r===-1||s===0||s===1&&t===r-1&&t===n+1?"":e.slice(t,r)}function jn(e){if(e===null||typeof e!="object")throw new TypeError(`The "pathObject" argument must be of type Object. Received type ${typeof e}`);return Ce("/",e)}function Gn(e){S(e);let t={root:"",dir:"",base:"",ext:"",name:""};if(e.length===0)return t;let n=e.charCodeAt(0)===47,r;n?(t.root="/",r=1):r=0;let o=-1,s=0,i=-1,a=!0,l=e.length-1,d=0;for(;l>=r;--l){let f=e.charCodeAt(l);if(f===47){if(!a){s=l+1;break}continue}i===-1&&(a=!1,i=l+1),f===46?o===-1?o=l:d!==1&&(d=1):o!==-1&&(d=-1)}return o===-1||i===-1||d===0||d===1&&o===i-1&&o===s+1?i!==-1&&(s===0&&n?t.base=t.name=e.slice(1,i):t.base=t.name=e.slice(s,i)):(s===0&&n?(t.name=e.slice(1,o),t.base=e.slice(1,i)):(t.name=e.slice(s,o),t.base=e.slice(s,i)),t.ext=e.slice(o,i)),s>0?t.dir=e.slice(0,s-1):n&&(t.dir="/"),t}function Kn(e){if(e=e instanceof URL?e:new URL(e),e.protocol!="file:")throw new TypeError("Must be a file URL.");return decodeURIComponent(e.pathname.replace(/%(?![0-9A-Fa-f]{2})/g,"%25"))}function Vn(e){if(!Tt(e))throw new TypeError("Must be an absolute path.");let t=new URL("file:///");return t.pathname=Te(e.replace(/%/g,"%25").replace(/\\/g,"%5C")),t}var Jn=ie?ce:le,{join:ho,normalize:po}=Jn;var Qn=ie?ce:le;var{basename:Ro,delimiter:yo,dirname:wo,extname:bo,format:Co,fromFileUrl:Et,isAbsolute:To,join:Ao,normalize:Eo,parse:So,relative:Lo,resolve:St,sep:Po,toFileUrl:Do,toNamespacedPath:vo}=Qn;var Ee=Symbol("depth"),Se=Symbol("inHandler"),Yn=["..","..","function_chain.ts"],Zn="runFunction",Lt=50,j=globalThis.Error,Le=class e extends j{[Ee];[Se];constructor(t){j.stackTraceLimit=Lt,super(),this[Ee]=0,this[Se]=!1,j.prepareStackTrace=(n,r)=>{this[Ee]=r.length;for(let o of r)if(o.getFileName()===t&&o.getFunctionName()===Zn){this[Se]=!0;break}},this.stack}static capture(){let t=j.prepareStackTrace,n=j.stackTraceLimit;try{let r=Et(import.meta.url),o=St(r,...Yn),s=new e(o);return{capped:s[Ee]>=Lt,inHandler:s[Se]}}catch(r){return P.withError(r).error("Failed to set up stack tracer"),{capped:!1,inHandler:!1}}finally{j.prepareStackTrace=t,j.stackTraceLimit=n}}};var ze=new Xn,er=()=>{let e=ze.getStore();if(!e)return;let{chain:t,functionIndex:n}=e;return{chain:t,context:t.getContext(n),functionName:t.functionNames[n],requestID:t.requestID,spanID:t.spanID}},Pt=new Set,F=e=>{let t=er();if(!t&&!Pt.has(e)&&H()==="production"){let{capped:n,inHandler:r}=Le.capture();(n||r)&&(Pt.add(e),P.withFields({capped_stack_trace:n,type:e}).error("could not find execution context for request correlation"))}return t};var Dt=e=>!!e?.__netlifyStructuredLogger,G=(e,t,n)=>{let r=H(),{chain:o,functionName:s,requestID:i,spanID:a}=n??{};if(r==="production"){let l={edgeFunctionName:s,requestID:i,spanID:a};if(Dt(t[0])){let{fields:d,message:f,requestID:c}=t[0].serialize();c&&(l.requestID=c),l.type="systemJSON";let u={__nfmessage:f,...d};t=[JSON.stringify(u)]}if(o){let d=new URL(o.request.url);d.search.length>256&&(d.search="?query-params-truncated"),l.url=d.toString()}return e(JSON.stringify({__nfmeta:l}),...t)}if(Dt(t[0])){let l=t[0].serialize();if(!o?.request?.headers.has("x-nf-debug-logging"))return;t=[l.message,l.fields]}return s?e(`[${s}]`,...t):e(...t)},I=e=>(...t)=>{try{let n=F("logger");return G(e,t,{chain:n?.chain,functionName:n?.functionName,requestID:n?.requestID,spanID:n?.spanID})}catch{e(...t)}};var ue={error:globalThis.console.error,log:globalThis.console.log},Pe={error:(e,...t)=>G(ue.error,t,e),log:(e,...t)=>G(ue.log,t,e)},P=pt.withRawLogger((...e)=>G(ue.log,e));function vt(e){if(!e)return{};try{return JSON.parse(new TextDecoder().decode(J(e)))}catch{return{}}}var Je=Symbol("Netlify Logger"),C=Symbol("Netlify Internals");var nr=e=>{let t=vt(e.get("x-nf-site-info")),n={context:e.get("x-nf-deploy-context")??void 0,id:e.get("x-nf-deploy-id")??void 0,published:e.get("x-nf-deploy-published")==="1"};return{account:at(e.get("x-nf-account-info")),blobs:ct(e.get("x-nf-blobs-info")),bypassSettings:e.get("x-nf-edge-function-bypass"),cacheAPIURL:e.get("x-nf-pc-url"),cacheAPIToken:e.get("x-nf-pc-token"),cacheMode:e.get("x-nf-edge-function-cache"),cdnLoop:e.get("cdn-loop"),deploy:n,featureFlags:_e(e.get("x-nf-feature-flags")),forwardedHost:e.get("x-forwarded-host"),forwardedProtocol:e.get("x-forwarded-proto"),geo:ut(e.get("x-nf-geo")),ip:e.get("x-nf-client-connection-ip")??"",passthrough:e.get("x-nf-passthrough"),passthroughHost:e.get("x-nf-passthrough-host"),passthroughProtocol:e.get("x-nf-passthrough-proto"),requestID:e.get("x-nf-request-id"),spanID:e.get("x-nf-trace-span-id"),purgeAPIToken:e.get("x-nf-purge-api-token"),site:t}},W=class e extends Request{[C];[Je];constructor(t,n){let r=t instanceof URL?new Request(t,n):t;super(r);let o=n instanceof e?n[C]:nr(this.headers);this[C]=o;let s=this.headers.get("x-nf-request-id"),i=this.headers.has("x-nf-debug-logging")?1:2;this[Je]=P.withRequestID(s).withLogLevel(i),["x-nf-account-info","x-nf-pc-token","x-nf-pc-url","x-forwarded-host","x-forwarded-proto","x-nf-geo","x-nf-client-connection-ip","x-nf-edge-functions","x-deno-functions","x-nf-edge-functions-metadata","x-nf-passthrough","x-deno-pass","x-nf-passthrough-host","x-nf-passthrough-proto","x-nf-feature-flags","x-nf-edge-function-bypass","x-nf-site-info"].forEach(a=>{this.headers.delete(a)})}},kt=e=>e[C].account,_t=e=>e[C].blobs,De=e=>e[C].cacheAPIURL,ve=e=>e[C].cacheAPIToken,de=e=>e[C].cacheMode==="manual"?"manual":"off",Qe=e=>e[C].deploy,Ft=e=>e[C].geo,qt=e=>e[C].ip,ge=e=>e[Je],It=e=>e[C].requestID??"",Nt=e=>e[C].spanID??"",Ut=e=>e[C].bypassSettings,Ot=e=>e[C].passthroughHeaders??new Headers,Rt=e=>e[C].purgeAPIToken,Mt=(e,t)=>{e[C].passthroughHeaders=t[C].passthroughHeaders},ke=e=>e[C].featureFlags,se=e=>e[C].site,K=class extends Request{constructor({req:t,stripConditionalHeaders:n=!1,url:r=new URL(t.url)}){let o=t[C].passthrough,s=t[C].requestID;r.host=t[C].passthroughHost??r.host,r.protocol=t[C].passthroughProtocol??r.protocol;let i=t;t.body&&t.bodyUsed&&(console.warn("Request body already used. To use the body in further processing, pass the request to `context.next()`. See https://ntl.fyi/request-body-used for more information."),i=new Request(t,{body:""})),super(new Request(r.toString(),i)),o!==null&&this.headers.set("x-nf-passthrough",o),s!==null&&this.headers.set("x-nf-request-id",s),n&&ft.forEach(a=>{this.headers.delete(a)})}};var he=(e,t)=>!!ke(e)[t];function _e(e){if(!e)return{};try{let t=atob(e);return JSON.parse(t)}catch{return{}}}var Ht=e=>{let t=Ut(e);return t?t==="1"?["passthrough"]:t.split(",").map(n=>n.trim()):[]},Ye=e=>Ht(e).includes("passthrough"),Z=e=>Ht(e).includes("rewrite"),V=class extends Response{rewriteURL;constructor({cookies:t,currentRequest:n,initialRequestHeaders:r,initialRequestURL:o}){let s=new Headers({"x-nf-edge-function-bypass":"1"});if(!Z(n)){super(null,{headers:s,status:204});return}let i={};n.url!==o.toString()&&(i.rewrite_url=n.url);let a=je(r,n.headers);Object.keys(a).length!==0&&(i.request_headers=a);let l=dt(t.apply(new Headers));Object.keys(l).length!==0&&(i.response_headers=l);let[d,f]=Object.keys(i).length===0?[null,204]:[JSON.stringify(i),200];s.set("cache-control","no-transform"),super(d,{headers:s,status:f}),this.rewriteURL=i.rewrite_url}};function Bt(e){function t(d,f=2){return d.padStart(f,"0")}let n=t(e.getUTCDate().toString()),r=t(e.getUTCHours().toString()),o=t(e.getUTCMinutes().toString()),s=t(e.getUTCSeconds().toString()),i=e.getUTCFullYear(),a=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],l=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];return`${a[e.getUTCDay()]}, ${n} ${l[e.getUTCMonth()]} ${i} ${r}:${o}:${s} GMT`}var or=/^(?=[\x20-\x7E]*$)[^()@<>,;:\\"\[\]?={}\s]+$/;function sr(e){if(!e.name)return"";let t=[];if(ir(e.name),cr(e.name,e.value),t.push(`${e.name}=${e.value}`),e.name.startsWith("__Secure")&&(e.secure=!0),e.name.startsWith("__Host")&&(e.path="/",e.secure=!0,delete e.domain),e.secure&&t.push("Secure"),e.httpOnly&&t.push("HttpOnly"),typeof e.maxAge=="number"&&Number.isInteger(e.maxAge)&&(ae(e.maxAge>=0,"Max-Age must be an integer superior or equal to 0"),t.push(`Max-Age=${e.maxAge}`)),e.domain&&(lr(e.domain),t.push(`Domain=${e.domain}`)),e.sameSite&&t.push(`SameSite=${e.sameSite}`),e.path&&(ar(e.path),t.push(`Path=${e.path}`)),e.expires){let{expires:n}=e,r=Bt(typeof n=="number"?new Date(n):n);t.push(`Expires=${r}`)}return e.unparsed&&t.push(e.unparsed.join("; ")),t.join("; ")}function ir(e){if(e&&!or.test(e))throw new TypeError(`Invalid cookie name: "${e}".`)}function ar(e){if(e!=null)for(let t=0;t<e.length;t++){let n=e.charAt(t);if(n<" "||n>"~"||n==";")throw new Error(e+": Invalid cookie path char '"+n+"'")}}function cr(e,t){if(!(t==null||e==null))for(let n=0;n<t.length;n++){let r=t.charAt(n);if(r<"!"||r=='"'||r==","||r==";"||r=="\\"||r=="\x7F")throw new Error("RFC2616 cookie '"+e+"' cannot contain character '"+r+"'");if(r>"\x80")throw new Error("RFC2616 cookie '"+e+"' can only have US-ASCII chars as value"+r.charCodeAt(0).toString(16))}}function lr(e){if(e==null)return;let t=e.charAt(0),n=e.charAt(e.length-1);if(t=="-"||n=="."||n=="-")throw new Error("Invalid first/last char in cookie domain: "+e)}function $t(e){let t=e.get("Cookie");if(t!=null){let n={},r=t.split(";");for(let o of r){let[s,...i]=o.split("=");ae(s!=null);let a=s.trim();n[a]=i.join("=")}return n}return{}}function Fe(e,t){let n=sr(t);n&&e.append("Set-Cookie",n)}function Wt(e,t,n){Fe(e,{name:t,value:"",expires:new Date(0),...n})}var qe=class{ops;request;constructor(t){this.ops=[],this.request=t}apply(t){return this.ops.forEach(n=>{switch(n.type){case"delete":Wt(t,n.options.name,{domain:n.options.domain,path:n.options.path});break;case"set":Fe(t,n.cookie);break}}),t}delete(t){let n={path:"/"},r=typeof t=="string"?{name:t}:t;this.ops.push({options:{...n,...r},type:"delete"})}get(t){let n=typeof t=="string"?t:t.name;return $t(this.request.headers)[n]}getPublicInterface(){return{delete:this.delete.bind(this),get:this.get.bind(this),set:this.set.bind(this)}}set(t,n){let r;if(typeof t=="string"){if(typeof n!="string")throw new Error("You must provide the cookie value as a string to 'cookies.set'");r={name:t,value:n}}else r=t;this.validate(r),this.ops.push({cookie:r,type:"set"})}validate(t){Fe(new Headers,t)}};var ur={efcar:100,efcaw:20},X=class e{counts;limits;fetchCalls;invokedFunctions;passthroughCalls;id;constructor(t,n=ur){this.id=Math.random(),this.counts=t?.counts??new Map,this.limits=n,this.fetchCalls=t?.fetchCalls??[],this.invokedFunctions=t?.invokedFunctions??[],this.passthroughCalls=t?.passthroughCalls??[]}static formatDuration(t){let n=t.toFixed(1);return n.endsWith(".0")?n.split(".")[0]:n}trackFetchCall(t){let n={host:t,start:performance.now()},r=()=>{n.end=performance.now()};return this.fetchCalls.push(n),{end:r}}getFetchTiming(){let t=[];for(let n of this.fetchCalls){let r=n.host?`host=${n.host}`:"passthrough",o=(n.end??performance.now())-n.start;t.push(`${r};dur=${e.formatDuration(o)}`)}return t}registerOperation(t){let n=this.counts.get(t)||0,r=this.limits[t]||0;return this.counts.set(t,n+1),r-n}registerInvokedFunction(t){this.invokedFunctions.push(t)}startFetch(t){return this.trackFetchCall(t)}startPassthrough(){return this.trackFetchCall()}writeHeaders(t){t.set("x-nf-edge-functions",this.invokedFunctions.join(","));for(let r of this.getFetchTiming())t.append("x-nf-fetch-timing",r);let n=["efcar","efcaw"].map(r=>`${r};count=${this.counts.get(r)??0}`);t.set("x-nf-invocation-metrics",n.join(","))}};var jt=(e,t)=>{if(e===void 0)return{};let r=new URLPattern({pathname:e}).exec(t)?.pathname.groups;return r?Object.entries(r).reduce((s,[i,a])=>a===void 0?s:{...s,[i]:a},{}):{}};function Kt(e,t={}){let{signal:n,persistent:r}=t;return n?.aborted?Promise.reject(new DOMException("Delay was aborted.","AbortError")):new Promise((o,s)=>{let i=()=>{clearTimeout(l),s(new DOMException("Delay was aborted.","AbortError"))},l=setTimeout(()=>{n?.removeEventListener("abort",i),o()},e);if(n?.addEventListener("abort",i,{once:!0}),r===!1)try{Deno.unrefTimer(l)}catch(d){if(!(d instanceof ReferenceError))throw d;console.error("`persistent` option is only available in Deno")}})}var k=class extends Error{},z=class extends Error{constructor(t){super("An unretriable error has occurred",{cause:t})}},ee=class extends Error{constructor(t){let n=t instanceof z?t.cause:t;super("There was an internal error while processing your request",{cause:n})}};var fr=5,Vt=1e3,dr=3;async function zt(e,{maxRetries:t=dr}={}){let n,r=0,o=new Error;for(;r<t;)try{return await e(r)}catch(s){if(o=s,s instanceof z)break;r+=1,n===void 0?n=fr:n*=2,n>Vt&&(n=Vt),await Kt(n)}throw o}var gr=new Set(["x-nf-ats-version","x-nf-cache-result","x-bb-cache","x-bb-site-cancelled","x-bb-proxy-type","x-nf-function-type","x-nf-func-id","x-nf-block-reason","x-nf-passthrough-timing"]),Ie=class extends Response{[C]={passthroughHeaders:new Headers};constructor(t){super(t.body,t),this.headers.forEach((n,r)=>{gr.has(r)&&(this[C].passthroughHeaders.set(r,n),this.headers.delete(r))}),this.headers.delete("via")}};var Ne=class{functions;requestRoutes;routes;constructor(t,n){let r=n.function_config??{},o=new Map;Array.isArray(n.req_routes)&&(this.requestRoutes=n.req_routes),Object.entries(t).forEach(([s,i])=>{if(i===void 0)return;let a={excludedPatterns:[],onError:"fail"},{excluded_patterns:l,generator:d,on_error:f}=r[s]??{};if(l){let c=l.map(u=>new RegExp(u));a.excludedPatterns.push(...c)}f&&(f==="bypass"||f==="fail"||typeof f=="string"&&f.startsWith("/"))&&(a.onError=f),d&&(a.generator=d),o.set(s,{config:a,source:i})}),this.functions=o,this.routes=(n.routes??[]).map(s=>{let i=o.get(s.function);return i===void 0?null:{config:i.config,name:s.function,path:s.path,pattern:new RegExp(s.pattern),source:i.source,methods:s.methods??[],header:s.header}})}getFunction(t){return this.functions.get(t)}getRequestRoute(t){if(!this.requestRoutes)return null;let n=this.requestRoutes[t];return this.routes[n]}match(t,n){let r=new Headers;for(let[s,i]of n.headers.entries())gt(s)||r.set(s,i);return this.routes.map(s=>{if(s===null||!s.pattern.test(t.pathname)||s.methods.length>0&&!s.methods.includes(n.method))return;let a=this.functions.get(s.name);if(a===void 0||a.config.excludedPatterns.some(b=>b.test(t.pathname))||s.header&&!Object.entries(s.header).every(([x,y])=>{let _=r.get(x.toLowerCase())?.split(", ").join(",");if(typeof y=="boolean")return y===!!_;if(typeof y=="string"){if(!_)return!1;try{return new RegExp(y).test(_)}catch{return!1}}return!1}))return;let{config:d,name:f,path:c,source:u}=s;return{config:d,name:f,path:c,source:u}}).filter(Boolean)}};var hr=new Set([301,302,303,307,308]),Jt=e=>{let t=[...e.headers.keys()];return e.body===null&&hr.has(e.status)&&t.length===1&&t[0]==="location"},Qt=e=>(...t)=>{let[n,r]=t;if(typeof n=="string"&&n.startsWith("/"))try{let o=F("response-redirect");if(o?.chain===void 0)throw new Error("Could not find chain");let{chain:s}=o,i=new URL(n,s.request.url);return e(i,r)}catch(o){P.withError(o).log("An error occurred in the patched Response.redirect")}return e(...t)};var pr=Deno.env.get("DENO_REGION")??"",mr=Deno.env.get("DENO_RUNNER_IP")??"",Ue=class e{cacheMode;cookies;contextNextCalls;executionController;functionNames;initialHeaders;initialRequestURL;loggedMessages;metrics;rawLogger;request;router;timeoutSignal;constructor({request:t,cookies:n=new qe(t),executionController:r,functionNames:o,initialMetrics:s,initialRequestURL:i=new URL(t.url),loggedMessages:a,rawLogger:l,router:d,timeoutSignal:f},c){this.cacheMode=de(t),this.cookies=n,this.contextNextCalls=[],this.executionController=r??new AbortController,this.functionNames=o,this.initialHeaders=new Headers(t.headers),this.initialRequestURL=i,this.loggedMessages=a??new Set,this.metrics=new X(s??c?.metrics),this.rawLogger=l,this.request=t,this.router=d,this.timeoutSignal=f??c?.timeoutSignal}async fetchPassthrough(t){try{return await this.fetchPassthroughWithRetries(t)}catch(n){throw new ee(n)}}async fetchPassthroughWithRetries(t){let n=performance.now(),r=this.contextNextCalls.length>0&&this.contextNextCalls.some(a=>!a.sendConditionalRequest),o=new K({req:this.request,stripConditionalHeaders:r,url:t}),s=await zt(async a=>{let l=this.logger.withFields({context_next_count:this.contextNextCalls.length,method:o.method,origin_url:o.url,retry_count:a,strip_conditional_headers:r,deno_runner_public_ip:mr});l.debug(a===0?"Started edge function request to origin":"Retrying edge function request to origin");let d=AbortSignal.any([o.signal,this.executionController.signal]),f=this.metrics.startPassthrough();try{return await fetch(o,{redirect:"manual",signal:d})}catch(c){if(c.name==="TypeError"&&(c.message==="Failed to fetch: request body stream errored"||c.message.includes("http2 error: stream error sent by user"))||o.signal.aborted)return new Response(null,{status:499});throw l.withFields({body_used:o.bodyUsed,error:c.message}).log("Error in passthrough call"),!o.bodyUsed&&c.name!=="AbortError"?c:new z(c)}finally{f.end()}}),i=new Ie(s);return Mt(this.request,i),this.logger.withFields({origin_duration:performance.now()-n,origin_status:s.status,origin_url:t}).withRequestID(this.requestID).debug("Finished edge function request to origin"),i}contextNext(t,n,r={}){if(n&&new URL(n.url).origin!==new URL(this.request.url).origin)throw new k("Edge functions can only rewrite requests to the same host. For more information, visit https://ntl.fyi/edge-rewrite-external");return this.contextNextCalls.push(r),n&&(this.request=new W(n,this.request)),this.runFunction({functionIndex:t+1,nextOptions:r,requireFinalResponse:!0})}getContext(t){let n=this.router.getRequestRoute(t),r=this.request.url;return{cookies:this.cookies.getPublicInterface(),deploy:Qe(this.request),geo:Ft(this.request),ip:qt(this.request),json:this.json.bind(this),log:this.getLogFunction(t),next:(s,i={})=>(this.logger.withFields({functionIndex:t,functionName:this.functionNames[t]}).debug("Function called `context.next()`"),s instanceof Request?this.contextNext(t,s,i):this.contextNext(t,void 0,s)),get params(){return jt(n?.path,r)},requestId:this.requestID,spanID:this.spanID,rewrite:this.rewrite.bind(this),site:se(this.request),account:kt(this.request),server:{region:pr},url:new URL(r),waitUntil:function(i){if(arguments.length===0)throw new TypeError("waitUntil: At least 1 argument required, but only 0 passed");setTimeout(()=>{Promise.resolve(i).catch(a=>console.error(a))},0)}}}getFunction(t){let n=this.functionNames[t];if(n===void 0)return;let r=this.router.getFunction(n);if(r===void 0)throw new Error(`Could not find function '${n}'`);let{config:o,source:s}=r;return{config:o,name:n,source:s}}getLogFunction(t){let n=this.functionNames[t],r=this.rawLogger??console.log;return(...o)=>G(r,o,{chain:this,functionName:n,requestID:this.requestID,spanID:this.spanID})}get logger(){return ge(this.request)}get throttledLogger(){return ge(this.request).withFilter(t=>this.loggedMessages.has(t)?!1:(this.loggedMessages.add(t),!0))}json(t,n){let r=JSON.stringify(t),o=new Response(r,n);return o.headers.set("content-type","application/json"),o}makeURL(t){return t.startsWith("/")?new URL(t,this.request.url):new URL(t)}get requestID(){return It(this.request)}get spanID(){return Nt(this.request)}rewrite(t){let n=t instanceof URL?t:this.makeURL(t);if(n.origin!==this.initialRequestURL.origin)throw new k("Edge functions can only rewrite requests to the same host. For more information, visit https://ntl.fyi/edge-rewrite-external");return this.logger.withFields({url:n.href}).debug("Calling origin as part of a `context.rewrite()` call"),this.fetchPassthrough(n)}async run({previousRewrites:t,requireFinalResponse:n}={}){let r=_t(this.request),o=Qe(this.request),s=se(this.request);lt(r,o,s);let i=await this.runFunction({functionIndex:0,previousRewrites:t,requireFinalResponse:n});return i instanceof V||(Jt(i)&&(i=new Response(null,i)),this.cookies.apply(i.headers)),i}runWithSignal(t){let n=!1,r=this.timeoutSignal;return r?(r.addEventListener("abort",()=>{n||this.executionController.abort()}),new Promise((o,s)=>{this.executionController.signal.addEventListener("abort",()=>{s(this.executionController.signal.reason)}),this.run(t).then(o).catch(s).finally(()=>{n=!0})})):this.run(t)}async runFunction({functionIndex:t,nextOptions:n,previousRewrites:r=new Set,requireFinalResponse:o=!1}){let s=this.getFunction(t),i=this.logger.withFields({functionIndex:t,functionName:s?.name,url:this.request.url});if(s===void 0)return Ye(this.request)&&!o&&this.request.body===null&&de(this.request)==="off"&&(!Ge(this.initialHeaders,this.request.headers)||Z(this.request))?(i.debug("Returning bypass response"),new V({cookies:this.cookies,currentRequest:this.request,initialRequestHeaders:this.initialHeaders,initialRequestURL:this.initialRequestURL})):(i.withFields({supportsPassthroughBypass:Ye(this.request),requireFinalResponse:o,hasBody:this.request.body!==null,mutatedHeaders:Ge(this.initialHeaders,this.request.headers),supportsRewriteBypass:Z(this.request)}).debug("Calling origin at the end of function chain"),this.fetchPassthrough());let{config:a,name:l,source:d}=s,f=this.getContext(t);this.metrics.registerInvokedFunction(l);try{let c=await ze.run({chain:this,functionIndex:t},()=>d(this.request,f));if(c instanceof URL){if(i.debug("Function returned a URL object"),c.origin!==this.initialRequestURL.origin)throw new k(`Rewrite to '${c.toString()}' is not allowed: edge functions can only rewrite requests to the same base URL`);let u=Z(this.request)&&this.request.body===null;if(r.has(c.pathname))throw new k(`Loop detected: the path '${c.pathname}' has been both the source and the target of a rewrite in the same request`);let x=new W(c,this.request),y=this.router.match(c,x);if(y.length===0)return u&&!o?new V({cookies:this.cookies,currentRequest:x,initialRequestHeaders:this.initialHeaders,initialRequestURL:this.initialRequestURL}):(i.withFields({canBypass:u,requireFinalResponse:o}).debug("Calling origin as a result of a rewrite with a URL object"),this.fetchPassthrough(c));let _=new e({cookies:this.cookies,executionController:this.executionController,functionNames:y.map(g=>g.name),initialRequestURL:this.initialRequestURL,rawLogger:this.rawLogger,request:x,router:this.router,timeoutSignal:this.timeoutSignal},this),O={previousRewrites:new Set([...r,c.pathname]),requireFinalResponse:o||!u};return this.timeoutSignal?_.runWithSignal(O):_.run(O)}if(c===void 0)return i.debug("Function returned undefined"),this.runFunction({functionIndex:t+1,nextOptions:n,requireFinalResponse:o});if(c instanceof Response)return i.debug("Function returned a response"),ye(c,u=>{u.delete("content-length"),he(this.request,"edge_functions_bootstrap_force_http11")&&u.set("transfer-encoding","chunked")});throw new k(`Function '${l}' returned an unsupported value. Accepted types are 'Response', 'URL' or 'undefined'`)}catch(c){let u={chain:this,functionName:l,requestID:this.requestID,spanID:this.spanID};if(i.withFields({onError:a.onError}).debug("Function has thrown an error"),a.onError==="fail")throw Pe.error(u,c),c;if(a.onError==="bypass")return Pe.error(u,c),this.runFunction({functionIndex:t+1,nextOptions:n,requireFinalResponse:o});if(o)throw c;Pe.error(u,c);let b=new URL(a.onError,this.request.url);return Z(this.request)?new V({cookies:this.cookies,currentRequest:new W(b,this.request),initialRequestHeaders:this.initialHeaders,initialRequestURL:this.initialRequestURL}):(this.logger.debug("Calling origin as part of a rewrite failure mode"),this.fetchPassthrough(b))}}};var xr={delete:Deno.env.delete,get:Deno.env.get,has:Deno.env.has,set:Deno.env.set,toObject:Deno.env.toObject},Zt={get context(){return F("netlify-global")?.context??null},env:xr};function Xt(e){if(!e)return{};try{return JSON.parse(atob(e))}catch{throw new Error("Could not parse edge functions invocation metadata")}}var Rr=e=>e instanceof URL?e:new URL(typeof e=="string"?e:e.url),yr=e=>{try{return Rr(e)}catch{}},nn=e=>async(...t)=>{if(t[0]instanceof K)return e(...t);let n=yr(t[0]);if(n===void 0)return P.withFields({args:t}).error("Could not get URL from arguments in fetch call"),e(...t);let r=F("track-subrequests");if(r?.chain===void 0)return e(...t);let{chain:o}=r,{signal:s}=t[1]??{},{signal:i}=o.executionController,a=AbortSignal.any([s,i].filter(Boolean));t[1]={...t[1],signal:a};let l=o.metrics.startFetch(n.host);try{return await e(...t)}finally{l.end()}},rn=(e,t)=>(n,r)=>{let o;if(n instanceof URL)o=n;else if(typeof n=="string")o=new URL(n);else if(n instanceof Request)o=new URL(n.url);else return e(n,r);let s=t.get(o.origin);if(s===void 0)return e(n,r);let i=new URL(o.pathname+o.search+o.hash,s);if(n instanceof Request){let a=new Request(i,n);return e(a,r)}return e(i,r)},on=e=>(t,n)=>{if(t instanceof K)return e(t,n);let r=F("forward-headers");if(r?.chain===void 0)return e(t,n);let{chain:o}=r,s=new Request(t,n),{cdnLoop:i,requestID:a}=o.request[C];return a&&he(o.request,"edge_functions_bootstrap_forward_request_id")&&s.headers.set("x-nf-request-id",a),i&&he(o.request,"edge_functions_bootstrap_forward_cdn_loop")&&s.headers.append("cdn-loop",i),e(s)},sn=e=>(t,n)=>{let r=Deno.createHttpClient({http1:!0,http2:!1});return e(t,{...n,client:r})},en,tn=!1,an=e=>tn?e:(tn=!0,en=Deno.createHttpClient({}),(t,n)=>e(t,{...n,client:en}));globalThis.Netlify=Zt;globalThis.addEventListener("unhandledrejection",e=>{if(e.reason instanceof DOMException&&e.reason.name==="AbortError"){P.withError(e.reason).debug("found unhandled AbortError exception"),e.preventDefault();return}});var Ze=async(e,t,{fetchRewrites:n,rawLogger:r=console.log,requestTimeout:o=0}={})=>{let s=e.headers.get("x-nf-request-id"),i=H(),a=P.withRequestID(s),l=_e(e.headers.get("x-nf-feature-flags"));l.edge_functions_bootstrap_use_one_client_pool_per_isolate&&(globalThis.fetch=an(globalThis.fetch)),l.edge_functions_bootstrap_force_http11&&(globalThis.fetch=sn(globalThis.fetch));let d=new X,f=l.edge_functions_bootstrap_invocation_timeout&&o?AbortSignal.timeout(o):void 0;try{let c=e.headers.get("x-nf-edge-functions"),u=Xt(e.headers.get("x-nf-edge-functions-metadata")),b=new Ne(t,u);if(s==null||c==null)return new Response("Request must have headers for request ID and functions names",{status:400,headers:{"content-type":"text/plain"}});let x=new URL(e.url);if(l.edge_functions_bootstrap_decode_query)try{x.search=decodeURIComponent(x.search)}catch{a.withFields({query:x.search}).log("Failed to decode query")}if(H()==="local"&&(x.protocol=e.headers.get("x-forwarded-proto")??x.protocol,x.host=e.headers.get("x-forwarded-host")??x.host,e.headers.has("x-nf-passthrough-host")&&e.headers.has("x-nf-passthrough-proto"))){let E=`${e.headers.get("x-nf-passthrough-proto")}//${e.headers.get("x-nf-passthrough-host")}`;E!==x.origin&&n?.set(x.origin,E)}let y=new W(new Request(x,e)),_=ve(y),O=De(y),g=[...new Set(c.split(","))],h=new Ue({functionNames:g,initialMetrics:d,rawLogger:r,request:y,router:b,timeoutSignal:f}),p=ge(y).withFields({cache_mode:de(y),feature_flags:Object.keys(ke(y)),function_names:g,url:e.url});xt(y),p.withFields({cache_api_token:!!_,cache_api_url:!!O}).debug("Started processing edge function request");let w=performance.now(),R=await(f?h.runWithSignal():h.run());Ot(y).forEach((E,xe)=>{R.headers.has(xe)&&p.withFields({header:xe}).log("user-defined header overwritten by passthrough header"),R.headers.set(xe,E)});let A=performance.now();return p.withFields({ef_duration:A-w}).debug("Finished processing edge function request"),ye(R,E=>{d.writeHeaders(E)})}catch(c){let u=String(c);if(i==="local")c instanceof Error?u=JSON.stringify({error:{name:c.name,message:c.message,stack:c.stack,cause:String(c.cause)}}):u=JSON.stringify({error:String(c)});else if(i==="production"){let x={fetch_timing:d.getFetchTiming().join(","),req_aborted:e.signal.aborted.toString()};if(c instanceof Error){let y=c instanceof k?"error_user":"error_unknown";x.error_name=c.name,x.error_message=c.message,x.error_stack=c.stack,x.error_cause=String(c.cause),x.error_type=y}else x.error_message=String(c);a.withFields(x).log("uncaught exception while handling request")}let b=new Response(u,{status:500,headers:{"x-nf-uncaught-error":wr(c)}});return d.writeHeaders(b.headers),b}},wr=e=>e instanceof ee?"passthrough":e instanceof Error&&(e?.name==="AbortError"||e?.name==="TimeoutError")?"timeout":"1";var un=(e,t,n)=>{if(!t.has(e))throw TypeError("Cannot "+n)},D=(e,t,n)=>(un(e,t,"read from private field"),n?n.call(e):t.get(e)),ne=(e,t,n)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,n)},re=(e,t,n,r)=>(un(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n),$e=(e=>(e.Delete="delete",e.Read="read",e.Write="write",e))($e||{}),br={invalid_vary:"Responses must not use unsupported directives of the `Netlify-Vary` header (https://ntl.fyi/cache_api_invalid_vary).",no_cache:"Responses must not set cache control headers with the `private`, `no-cache` or `no-store` directives (https://ntl.fyi/cache_api_no_cache).",low_ttl:"Responses must have a cache control header with a `max-age` or `s-maxage` directive (https://ntl.fyi/cache_api_low_ttl).",no_directive:"Responses must have a cache control header with caching directives (https://ntl.fyi/cache_api_no_directive).",no_ttl:"Responses must have a cache control header with a `max-age` or `s-maxage` directive (https://ntl.fyi/cache_api_no_ttl).",no_status:"Responses must specify a status code (https://ntl.fyi/cache_api_no_status).",invalid_directive:"Responses must have a cache control header with caching directives (https://ntl.fyi/cache_api_invalid_directive).",status:"Responses must have a status code between 200 and 299 (https://ntl.fyi/cache_api_status)."},Cr="The server has returned an unexpected error (https://ntl.fyi/cache_api_error).",Tr="netlify-programmable-error",Ar="netlify-programmable-headers",Er="netlify-programmable-status",Sr="netlify-programmable-store",Lr="netlify-forwarded-host",Pr="user-agent",cn=new Set(["http:","https:"]),Dr=new Set(["cookie","content-encoding","content-length"]),Oe=Symbol("getInternalHeaders"),ln=Symbol("serializeResourceHeaders"),Me,te,He,pe,vr=class{constructor({base64Encode:e,getContext:t,name:n,userAgent:r}){ne(this,Me,void 0),ne(this,te,void 0),ne(this,He,void 0),ne(this,pe,void 0),re(this,Me,e),re(this,te,t),re(this,He,n),re(this,pe,r)}[Oe](e){let{host:t,token:n}=e,r={Authorization:`Bearer ${n}`,[Sr]:D(this,He)};return t&&(r[Lr]=t),D(this,pe)&&(r[Pr]=D(this,pe)),r}[ln](e){let t={};return e.forEach((n,r)=>{Dr.has(r)||(r==="set-cookie"?(t[r]=t[r]||[],t[r].push(n)):t[r]=n.split(","))}),D(this,Me).call(this,JSON.stringify(t))}async add(e){await this.put(new Request(e),await fetch(e))}async addAll(e){await Promise.allSettled(e.map(t=>this.add(t)))}async delete(e){let t=D(this,te).call(this,{operation:"delete"});if(t){let n=Xe(e);await fetch(`${t.url}/${et(n)}`,{headers:this[Oe](t),method:"DELETE"})}return!0}async keys(e){return[]}async match(e){try{let t=D(this,te).call(this,{operation:"read"});if(!t)return;let n=Xe(e),r=`${t.url}/${et(n)}`,o=await fetch(r,{headers:this[Oe](t),method:"GET"});return o.ok?o:void 0}catch{}}async matchAll(e,t){if(!e)return[];let n=await this.match(e);return n?[n]:[]}async put(e,t){if(!t.ok)throw new TypeError(`Cannot cache response with status ${t.status}.`);if(e instanceof Request&&e.method!=="GET")throw new TypeError(`Cannot cache response to ${e.method} request.`);if(t.status===206)throw new TypeError("Cannot cache response to a range request (206 Partial Content).");if(t.headers.get("vary")?.includes("*"))throw new TypeError("Cannot cache response with 'Vary: *' header.");let n=D(this,te).call(this,{operation:"write"});if(!n)return;let r=Xe(e),o=await fetch(`${n.url}/${et(r)}`,{body:t.body,headers:{...this[Oe](n),[Ar]:this[ln](t.headers),[Er]:t.status.toString()},duplex:"half",method:"POST"});if(!o.ok){let s=o.headers.get(Tr)??"",i=br[s]||Cr;n.logger?.(`Failed to write to the cache: ${i}`)}}};Me=new WeakMap;te=new WeakMap;He=new WeakMap;pe=new WeakMap;var Xe=e=>{let t;if(e instanceof Request)t=new URL(e.url);else try{t=new URL(String(e))}catch{throw new TypeError(`${e} is not a valid URL.`)}if(!cn.has(t.protocol))throw new TypeError(`Cannot cache response for URL with unsupported protocol (${t.protocol}). Supported protocols are ${[...cn].join(", ")}.`);return t},et=e=>encodeURIComponent(e.toString()),Be,N,fn=class{constructor(e){ne(this,Be,void 0),ne(this,N,void 0),re(this,Be,e),re(this,N,new Map)}open(e){let t=D(this,N).get(e);return t||(t=new vr({...D(this,Be),name:e}),D(this,N).set(e,t)),Promise.resolve(t)}has(e){return Promise.resolve(D(this,N).has(e))}delete(e){return Promise.resolve(D(this,N).delete(e))}keys(){return Promise.resolve([...D(this,N).keys()])}async match(e,t){if(t?.cacheName)return D(this,N).get(t.cacheName)?.match(e);for(let n of D(this,N).values())if(await n.match(e)===void 0)return}};Be=new WeakMap;N=new WeakMap;var kr="1.7.1";var dn=()=>new fn({base64Encode:Re,getContext:({operation:e})=>{let n=F("cache-api")?.chain;if(!n?.request)throw new k("The Cache API must be used within the scope of the request handler. Refer to https://ntl.fyi/cache-api-scope for more information.");let r=e===$e.Delete||e===$e.Write?"efcaw":"efcar",o=n.metrics.registerOperation(r);if(o<=0)return o===0&&ue.log(`You've exceeded the number of allowed Cache API ${r==="efcaw"?"writes":"reads"} for a single invocation. Refer to https://ntl.fyi/cache-api-limits for more information.`),null;let{request:s}=n,i=new URL(s.url).hostname,a=ve(s),l=De(s);if(!a||!l)return P.withFields({has_token:!!a,has_url:!!l}).log("missing Cache API metadata in request"),null;let d=new URL("/.netlify/cache",l);return{logger:f=>n.throttledLogger.log(f),host:i,url:d.toString(),token:a}},userAgent:`netlify-edge-functions@${kr}`});var _r=()=>{throw new k("Reading or writing files with Edge Functions is not supported yet. Visit https://ntl.fyi/edge-functions-filesystem to learn more and tell us about your use cases for file system access.")};function U(e){return _r}var tt=" ",Fr=(e,t,n)=>{let r=I((...i)=>e(i.join(tt))),o=I((i,a,...l)=>t([i,a].join(tt),...l)),s=I((...i)=>n(i.join(tt)));return{time:r,timeLog:o,timeEnd:s}},nt=()=>{globalThis.console.log=I(globalThis.console.log),globalThis.console.error=I(globalThis.console.error),globalThis.console.debug=I(globalThis.console.debug),globalThis.console.warn=I(globalThis.console.warn),globalThis.console.info=I(globalThis.console.info),globalThis.console.trace=I(globalThis.console.trace);let{time:e,timeLog:t,timeEnd:n}=Fr(globalThis.console.time,globalThis.console.timeLog,globalThis.console.timeEnd);globalThis.console.time=e,globalThis.console.timeLog=t,globalThis.console.timeEnd=n,globalThis.Deno.cwd=U(globalThis.Deno.cwd),globalThis.Deno.readDir=U(globalThis.Deno.readDir),globalThis.Deno.readFile=U(globalThis.Deno.readFile),globalThis.Deno.readTextFile=U(globalThis.Deno.readTextFile),globalThis.Deno.open=U(globalThis.Deno.open),globalThis.Deno.stat=U(globalThis.Deno.stat),globalThis.Deno.lstat=U(globalThis.Deno.lstat),globalThis.Deno.realPath=U(globalThis.Deno.realPath),globalThis.Deno.readLink=U(globalThis.Deno.readLink),Response.redirect=Qt(Response.redirect),globalThis.fetch=on(globalThis.fetch),globalThis.fetch=nn(globalThis.fetch),qr()},qr=()=>{globalThis.caches&&delete globalThis.caches,globalThis.caches=dn()};var rt=class extends Error{constructor(t){super(t),this.name="DenoStdInternalError"}};function me(e,t=""){if(!e)throw new rt(t)}var{hasOwn:hn}=Object;function T(e,t){if(hn(e,t))return e[t]}function ot(e,t){let n=T(e,t);return me(n!=null),n}function gn(e){return typeof e=="number"||/^0x[0-9a-f]+$/i.test(String(e))?!0:/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(String(e))}function st(e,t){let n=e;t.slice(0,-1).forEach(o=>{n=T(n,o)??{}});let r=t[t.length-1];return hn(n,r)}function pn(e,{"--":t=!1,alias:n={},boolean:r=!1,default:o={},stopEarly:s=!1,string:i=[],collect:a=[],negatable:l=[],unknown:d=f=>f}={}){let f={},c={bools:{},strings:{},unknownFn:d,allBools:!1,collect:{},negatable:{}};if(n!==void 0)for(let g in n){let h=ot(n,g);typeof h=="string"?f[g]=[h]:f[g]=h;for(let p of ot(f,g))f[p]=[g].concat(f[g].filter(w=>p!==w))}if(r!==void 0)if(typeof r=="boolean")c.allBools=!!r;else{let g=typeof r=="string"?[r]:r;for(let h of g.filter(Boolean)){c.bools[h]=!0;let p=T(f,h);if(p)for(let w of p)c.bools[w]=!0}}if(i!==void 0){let g=typeof i=="string"?[i]:i;for(let h of g.filter(Boolean)){c.strings[h]=!0;let p=T(f,h);if(p)for(let w of p)c.strings[w]=!0}}if(a!==void 0){let g=typeof a=="string"?[a]:a;for(let h of g.filter(Boolean)){c.collect[h]=!0;let p=T(f,h);if(p)for(let w of p)c.collect[w]=!0}}if(l!==void 0){let g=typeof l=="string"?[l]:l;for(let h of g.filter(Boolean)){c.negatable[h]=!0;let p=T(f,h);if(p)for(let w of p)c.negatable[w]=!0}}let u={_:[]};function b(g,h){return c.allBools&&/^--[^=]+$/.test(h)||T(c.bools,g)||!!T(c.strings,g)||!!T(f,g)}function x(g,h,p,w=!0){let R=g,A=h.split(".");A.slice(0,-1).forEach(function(We){T(R,We)===void 0&&(R[We]={}),R=T(R,We)});let E=A[A.length-1];w&&!!T(c.collect,h)?T(R,E)===void 0?R[E]=[p]:Array.isArray(T(R,E))?R[E].push(p):R[E]=[T(R,E),p]:R[E]=p}function y(g,h,p=void 0,w){if(p&&c.unknownFn&&!b(g,p)&&c.unknownFn(p,g,h)===!1)return;let R=!T(c.strings,g)&&gn(h)?Number(h):h;x(u,g,R,w);let A=T(f,g);if(A)for(let E of A)x(u,E,R,w)}function _(g){return ot(f,g).some(h=>typeof T(c.bools,h)=="boolean")}let O=[];e.includes("--")&&(O=e.slice(e.indexOf("--")+1),e=e.slice(0,e.indexOf("--")));for(let g=0;g<e.length;g++){let h=e[g];if(/^--.+=/.test(h)){let p=h.match(/^--([^=]+)=(.*)$/s);me(p!=null);let[,w,R]=p;c.bools[w]?y(w,R!=="false",h):y(w,R,h)}else if(/^--no-.+/.test(h)&&T(c.negatable,h.replace(/^--no-/,""))){let p=h.match(/^--no-(.+)/);me(p!=null),y(p[1],!1,h,!1)}else if(/^--.+/.test(h)){let p=h.match(/^--(.+)/);me(p!=null);let[,w]=p,R=e[g+1];R!==void 0&&!/^-/.test(R)&&!T(c.bools,w)&&!c.allBools&&(!T(f,w)||!_(w))?(y(w,R,h),g++):/^(true|false)$/.test(R)?(y(w,R==="true",h),g++):y(w,T(c.strings,w)?"":!0,h)}else if(/^-[^-]+/.test(h)){let p=h.slice(1,-1).split(""),w=!1;for(let A=0;A<p.length;A++){let E=h.slice(A+2);if(E==="-"){y(p[A],E,h);continue}if(/[A-Za-z]/.test(p[A])&&/=/.test(E)){y(p[A],E.split(/=(.+)/)[1],h),w=!0;break}if(/[A-Za-z]/.test(p[A])&&/-?\d+(\.\d*)?(e-?\d+)?$/.test(E)){y(p[A],E,h),w=!0;break}if(p[A+1]&&p[A+1].match(/\W/)){y(p[A],h.slice(A+2),h),w=!0;break}else y(p[A],T(c.strings,p[A])?"":!0,h)}let[R]=h.slice(-1);!w&&R!=="-"&&(e[g+1]&&!/^(-|--)[^-]/.test(e[g+1])&&!T(c.bools,R)&&(!T(f,R)||!_(R))?(y(R,e[g+1],h),g++):e[g+1]&&/^(true|false)$/.test(e[g+1])?(y(R,e[g+1]==="true",h),g++):y(R,T(c.strings,R)?"":!0,h))}else if((!c.unknownFn||c.unknownFn(h)!==!1)&&u._.push(c.strings._??!gn(h)?h:Number(h)),s){u._.push(...e.slice(g+1));break}}for(let[g,h]of Object.entries(o))if(!st(u,g.split("."))&&(x(u,g,h),f[g]))for(let p of f[g])x(u,p,h);for(let g of Object.keys(c.bools))if(!st(u,g.split("."))){let h=T(c.collect,g)?[]:!1;x(u,g,h,!1)}for(let g of Object.keys(c.strings))!st(u,g.split("."))&&T(c.collect,g)&&x(u,g,[],!1);if(t){u["--"]=[];for(let g of O)u["--"].push(g)}else for(let g of O)u._.push(g);return u}var Ir=37e3,Nr=Ir-1e3,Ur=globalThis.console.log,mn=new Map;H()==="local"&&(globalThis.fetch=rn(globalThis.fetch,mn));nt();var Or=e=>{let t={onListen(){}},n=pn(Deno.args).port||8e3,r=parseInt(n,10);if(isNaN(r))throw new Error(`Invalid port supplied: ${n}`);if(r<0||r>65535)throw new Error(`port must be between 0 and 65535, got ${r}`);t.port=r;let o=Deno.serve(t,async s=>{try{return await Ze(s,await e(),{fetchRewrites:mn,rawLogger:Ur,requestTimeout:Nr})}catch(i){return console.error("Error handling request:",i),new Response("Internal Server Error",{status:500})}});return Deno.addSignalListener("SIGINT",async()=>{await o.shutdown()}),Deno.build.os!=="windows"&&Deno.addSignalListener("SIGTERM",async()=>{await o.shutdown()}),o.finished};export{Or as boot,Ze as handleRequest,nt as patchGlobals};
@@ -0,0 +1,55 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @typedef {import('./workers/types.ts').ConfigRequestMessage} ConfigRequestMessage
5
+ * @typedef {import('./workers/types.ts').Message} Message
6
+ */
7
+
8
+ /**
9
+ * @param {Record<string, string>} functions
10
+ */
11
+ export function getConfigs(functions) {
12
+ return new Promise((resolve, reject) => {
13
+ const worker = new Worker(new URL('./workers/config.mjs', import.meta.url).href, {
14
+ type: 'module',
15
+ })
16
+
17
+ worker.postMessage(
18
+ /** @type {ConfigRequestMessage} */ ({
19
+ type: 'configRequest',
20
+ data: { functions },
21
+ }),
22
+ )
23
+
24
+ worker.onmessage = (e) => {
25
+ const message = /** @type {Message} */ (e.data)
26
+
27
+ if (message.type === 'configResponse') {
28
+ const { configs, errors } = message.data
29
+
30
+ for (const functionName in errors) {
31
+ const prefix = `Failed to parse edge function \`${functionName}\`:`
32
+ const error = new Error(`${prefix} ${errors[functionName].message}`)
33
+
34
+ if (errors[functionName].name) {
35
+ error.name = errors[functionName].name
36
+ }
37
+
38
+ error.stack = `${prefix} ${error.stack}`
39
+
40
+ reject(error)
41
+
42
+ return
43
+ }
44
+
45
+ resolve(configs)
46
+
47
+ return
48
+ }
49
+ }
50
+
51
+ worker.onerror = (e) => {
52
+ reject(e)
53
+ }
54
+ })
55
+ }
@@ -0,0 +1,28 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @param {unknown} error
5
+ */
6
+ export const getErrorResponse = (error) => {
7
+ const errorData =
8
+ error instanceof Error
9
+ ? {
10
+ name: error.name,
11
+ message: error.message,
12
+ stack: error.stack,
13
+ cause: String(error.cause),
14
+ }
15
+ : String(error)
16
+
17
+ return Response.json(
18
+ {
19
+ error: errorData,
20
+ },
21
+ {
22
+ headers: {
23
+ 'x-nf-uncaught-error': '1',
24
+ },
25
+ status: 500,
26
+ },
27
+ )
28
+ }
@@ -0,0 +1,109 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @typedef {import('./workers/types.ts').Message} Message
5
+ */
6
+
7
+ /**
8
+ * Spawns a `Worker` to invoke a chain of edge functions. It serializes the
9
+ * `Request` into a worker message and uses the messages it receives back to
10
+ * construct a `Response`.
11
+ *
12
+ * @param {Request} req
13
+ * @param {Record<string, string>} functions
14
+ * @param {number} requestTimeout
15
+ */
16
+ export function invoke(req, functions, requestTimeout) {
17
+ return new Promise(async (resolve, reject) => {
18
+ const worker = new Worker(new URL('./workers/runner.mjs', import.meta.url).href, {
19
+ type: 'module',
20
+ })
21
+
22
+ /** @type {Response | null} */
23
+ let response = null
24
+ /** @type {ReadableStreamDefaultController<Uint8Array> | null} */
25
+ let streamController = null
26
+ /** @type {ReadableStream | null} */
27
+ let stream = null
28
+
29
+ const timeoutCheck = setTimeout(() => {
30
+ if (!response) {
31
+ reject(
32
+ new Error(
33
+ 'An edge function took too long to produce a response. Refer to https://ntl.fyi/ef-limits for information about limits.',
34
+ ),
35
+ )
36
+ }
37
+ }, requestTimeout)
38
+
39
+ worker.onmessage = (e) => {
40
+ const message = /** @type {Message} */ (e.data)
41
+
42
+ switch (message.type) {
43
+ case 'responseChunk': {
44
+ streamController && streamController.enqueue(message.data.chunk)
45
+
46
+ break
47
+ }
48
+
49
+ case 'responseStart': {
50
+ if (message.data.hasBody) {
51
+ stream = new ReadableStream({
52
+ start(controller) {
53
+ streamController = controller
54
+ },
55
+ cancel() {
56
+ streamController = null
57
+ worker.terminate()
58
+ },
59
+ })
60
+ }
61
+
62
+ response = new Response(stream, {
63
+ headers: message.data.headers,
64
+ status: message.data.status,
65
+ })
66
+
67
+ clearTimeout(timeoutCheck)
68
+
69
+ resolve(response)
70
+
71
+ break
72
+ }
73
+
74
+ case 'responseEnd': {
75
+ streamController?.close()
76
+ worker.terminate()
77
+
78
+ clearTimeout(timeoutCheck)
79
+
80
+ if (!response) {
81
+ reject(new Error('There was an error in producing the edge function response'))
82
+
83
+ return
84
+ }
85
+
86
+ resolve(response)
87
+ }
88
+ }
89
+ }
90
+
91
+ worker.onerror = (e) => {
92
+ clearTimeout(timeoutCheck)
93
+
94
+ reject(e)
95
+ }
96
+
97
+ worker.postMessage({
98
+ type: 'request',
99
+ data: {
100
+ body: await req.arrayBuffer(),
101
+ functions,
102
+ headers: Object.fromEntries(req.headers.entries()),
103
+ method: req.method,
104
+ timeout: requestTimeout,
105
+ url: req.url,
106
+ },
107
+ })
108
+ })
109
+ }
@@ -0,0 +1,73 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @typedef {import('../shared/types.ts').RunOptions} RunOptions
5
+ */
6
+
7
+ import { getErrorResponse } from './errors.mjs'
8
+
9
+ import { getConfigs } from './config.mjs'
10
+ import { invoke } from './invoke.mjs'
11
+
12
+ /**
13
+ * Starts an HTTP server on the provided port. The server acts as a proxy that
14
+ * handles edge function invocations.
15
+ *
16
+ * @param {RunOptions} options
17
+ */
18
+ export const serveLocal = ({ denoPort: port, requestTimeout }) => {
19
+ const serveOptions = {
20
+ // Adding a no-op listener to avoid the default one, which prints a message
21
+ // we don't want.
22
+ onListen() {},
23
+ port,
24
+ }
25
+
26
+ /** @type {Record<string, string>} */
27
+ let functions = {}
28
+
29
+ /**
30
+ * @param {Request} request
31
+ */
32
+ const server = Deno.serve(serveOptions, async (/** @type {Request} */ request) => {
33
+ const url = new URL(request.url)
34
+ const method = request.method.toUpperCase()
35
+
36
+ // This custom method represents an introspection request that will make
37
+ // the Deno server take a list of functions, import them, and return their
38
+ // configs.
39
+ if (method === 'NETLIFYCONFIG') {
40
+ const functionsParam = url.searchParams.get('functions')
41
+
42
+ // This is the list of all the functions found in the project.
43
+ /** @type {Record<string, string>} */
44
+ const availableFunctions = functionsParam ? JSON.parse(decodeURIComponent(functionsParam)) : {}
45
+
46
+ functions = availableFunctions
47
+
48
+ try {
49
+ const configs = await getConfigs(availableFunctions)
50
+
51
+ return Response.json(configs)
52
+ } catch (error) {
53
+ return getErrorResponse(error)
54
+ }
55
+ }
56
+
57
+ if (Object.keys(functions).length === 0) {
58
+ return new Response(null, { status: 404 })
59
+ }
60
+
61
+ try {
62
+ return await invoke(request, functions, requestTimeout)
63
+ } catch (error) {
64
+ return getErrorResponse(error)
65
+ }
66
+ })
67
+
68
+ return server.finished
69
+ }
70
+
71
+ const runOptions = JSON.parse(Deno.args[0])
72
+
73
+ await serveLocal(runOptions)
@@ -0,0 +1,52 @@
1
+ // @ts-check
2
+ /// <reference lib="deno.worker" />
3
+
4
+ /**
5
+ * @typedef {import('../../shared/types.ts').SerializedError} SerializedError
6
+ * @typedef {import('./types.ts').ConfigResponseMessage} ConfigResponseMessage
7
+ * @typedef {import('./types.ts').Message} Message
8
+ */
9
+
10
+ /** @type {DedicatedWorkerGlobalScope} */
11
+ // @ts-ignore We are inside a worker, so the global scope is `DedicatedWorkerGlobalScope`.
12
+ const worker = globalThis
13
+
14
+ worker.addEventListener('message', async (e) => {
15
+ const message = /** @type {Message} */ (e.data)
16
+
17
+ if (message.type === 'configRequest') {
18
+ /** @type {Record<string, object>} */
19
+ const configs = {}
20
+
21
+ /** @type {Record<string, SerializedError>} */
22
+ const errors = {}
23
+
24
+ const imports = Object.entries(message.data.functions).map(async ([name, path]) => {
25
+ try {
26
+ const func = await import(path)
27
+
28
+ configs[name] = func.config ?? {}
29
+ } catch (error) {
30
+ if (error instanceof Error) {
31
+ errors[name] = {
32
+ message: error.message,
33
+ name: error.name,
34
+ stack: error.stack,
35
+ }
36
+ } else {
37
+ errors[name] = {
38
+ message: String(error),
39
+ }
40
+ }
41
+ }
42
+ })
43
+
44
+ await Promise.allSettled(imports)
45
+
46
+ worker.postMessage(/** @type {ConfigResponseMessage} */ ({ type: 'configResponse', data: { configs, errors } }))
47
+
48
+ return
49
+ }
50
+
51
+ throw new Error('Unsupported message')
52
+ })
@@ -0,0 +1,86 @@
1
+ // @ts-check
2
+ /// <reference lib="deno.worker" />
3
+ import { handleRequest } from '../bootstrap.mjs'
4
+
5
+ /**
6
+ * @typedef {import('./types.ts').Message} Message
7
+ * @typedef {import('./types.ts').RunResponseStartMessage} RunResponseStartMessage
8
+ * @typedef {import('./types.ts').RunResponseChunkMessage} RunResponseChunkMessage
9
+ * @typedef {import('./types.ts').RunResponseEndMessage} RunResponseEndMessage
10
+ */
11
+
12
+ const consoleLog = globalThis.console.log
13
+ /** @type {Map<string, string>} */
14
+ const fetchRewrites = new Map()
15
+
16
+ /** @type {DedicatedWorkerGlobalScope} */
17
+ // @ts-ignore We are inside a worker, so the global scope is `DedicatedWorkerGlobalScope`.
18
+ const worker = globalThis
19
+
20
+ worker.addEventListener('message', async (e) => {
21
+ const message = /** @type {Message} */ (e.data)
22
+
23
+ if (message.type === 'request') {
24
+ const body = message.data.method === 'GET' || message.data.method === 'HEAD' ? undefined : message.data.body
25
+ const req = new Request(message.data.url, {
26
+ body,
27
+ headers: message.data.headers,
28
+ method: message.data.method,
29
+ })
30
+
31
+ /** @type {Record<string, string>} */
32
+ const functions = {}
33
+
34
+ const imports = Object.entries(message.data.functions).map(async ([name, path]) => {
35
+ const func = await import(path)
36
+
37
+ functions[name] = func.default
38
+ })
39
+
40
+ await Promise.allSettled(imports)
41
+
42
+ const res = await handleRequest(req, functions, {
43
+ // @ts-ignore TODO: Figure out why `fetchRewrites` is not being picked up
44
+ // as part of the type.
45
+ fetchRewrites,
46
+ rawLogger: consoleLog,
47
+ requestTimeout: message.data.timeout,
48
+ })
49
+
50
+ worker.postMessage(
51
+ /** @type {RunResponseStartMessage} */ ({
52
+ type: 'responseStart',
53
+ data: {
54
+ headers: Object.fromEntries(res.headers.entries()),
55
+ status: res.status,
56
+ hasBody: Boolean(res.body),
57
+ },
58
+ }),
59
+ )
60
+
61
+ const reader = res.body?.getReader()
62
+ if (reader) {
63
+ while (true) {
64
+ const { done, value } = await reader.read()
65
+ if (done) {
66
+ break
67
+ }
68
+
69
+ // @ts-expect-error TODO: Figure out type mismatch.
70
+ worker.postMessage(
71
+ /** @type {RunResponseChunkMessage} */ ({
72
+ type: 'responseChunk',
73
+ data: { chunk: value },
74
+ }),
75
+ [value.buffer],
76
+ )
77
+ }
78
+ }
79
+
80
+ worker.postMessage(/** @type {RunResponseEndMessage} */ ({ type: 'responseEnd' }))
81
+
82
+ return
83
+ }
84
+
85
+ throw new Error('Unsupported message')
86
+ })
@@ -0,0 +1,78 @@
1
+ import type { SerializedError } from '../../shared/types.ts'
2
+
3
+ /**
4
+ * A message that asks a worker to retrieve the configuration objects for a set
5
+ * of functions.
6
+ */
7
+ export interface ConfigRequestMessage {
8
+ type: 'configRequest'
9
+ data: {
10
+ functions: Record<string, string>
11
+ }
12
+ }
13
+
14
+ /**
15
+ * A message produced by a worker that contains the configuration objects for a
16
+ * set of functions.
17
+ */
18
+ export interface ConfigResponseMessage {
19
+ type: 'configResponse'
20
+ data: {
21
+ configs: Record<string, object>
22
+ errors: Record<string, SerializedError>
23
+ }
24
+ }
25
+
26
+ /**
27
+ * A message that asks a worker to run a chain of edge functions for a given
28
+ * request, serialized in the `data` field.
29
+ */
30
+ export interface RunRequestMessage {
31
+ type: 'request'
32
+ data: {
33
+ body: ArrayBuffer
34
+ functions: Record<string, string>
35
+ headers: Record<string, string>
36
+ method: string
37
+ timeout: number
38
+ url: string
39
+ }
40
+ }
41
+
42
+ /**
43
+ * A message produced by a worker that represents the beginning of a `Response`
44
+ * object, holding the headers and the status code.
45
+ */
46
+ export interface RunResponseStartMessage {
47
+ type: 'responseStart'
48
+ data: {
49
+ headers: Record<string, string>
50
+ status: number
51
+ hasBody: boolean
52
+ }
53
+ }
54
+
55
+ /**
56
+ * A message produced by a worker that holds a chunk of a `Response` body.
57
+ */
58
+ export interface RunResponseChunkMessage {
59
+ type: 'responseChunk'
60
+ data: {
61
+ chunk: Uint8Array | undefined
62
+ }
63
+ }
64
+
65
+ /**
66
+ * A message produced by a worker that represents the end of a `Response` body.
67
+ */
68
+ export interface RunResponseEndMessage {
69
+ type: 'responseEnd'
70
+ }
71
+
72
+ export type Message =
73
+ | ConfigRequestMessage
74
+ | ConfigResponseMessage
75
+ | RunRequestMessage
76
+ | RunResponseChunkMessage
77
+ | RunResponseEndMessage
78
+ | RunResponseStartMessage
@@ -0,0 +1,63 @@
1
+ import { Geolocation, Logger } from '@netlify/dev-utils';
2
+ import { Declaration } from '@netlify/edge-bundler';
3
+ export { Declaration } from '@netlify/edge-bundler';
4
+
5
+ interface EdgeFunctionsHandlerOptions {
6
+ configDeclarations: Declaration[];
7
+ directories: string[];
8
+ env: Record<string, string>;
9
+ geolocation: Geolocation;
10
+ logger: Logger;
11
+ requestTimeout?: number;
12
+ siteID?: string;
13
+ siteName?: string;
14
+ }
15
+ type EdgeFunctionsMatch = Awaited<ReturnType<EdgeFunctionsHandler['getFunctionsForRequest']>>;
16
+ declare class EdgeFunctionsHandler {
17
+ private configDeclarations;
18
+ private denoServerProcess?;
19
+ private directories;
20
+ private geolocation;
21
+ private initialization;
22
+ private initialized;
23
+ private stopped;
24
+ private logger;
25
+ private requestTimeout;
26
+ private siteID?;
27
+ private siteName?;
28
+ constructor(options: EdgeFunctionsHandlerOptions);
29
+ /**
30
+ * Retrieves the in-source configuration objects for a set of edge functions.
31
+ * The evaluation of the functions and the retrieval of the configs must take
32
+ * place in Deno, but all the logic for processing those configurations and
33
+ * merging them with other sources lives in Node.js. To keep a single source
34
+ * of truth, we make a request to the Deno server with a special method that
35
+ * instructs the handler to evaluate the functions and return their configs,
36
+ * which are then returned by this method.
37
+ */
38
+ private getFunctionConfigs;
39
+ /**
40
+ * Returns the list of edge functions that should run for a given request.
41
+ * It computes both the names of the edge functions that should run as well
42
+ * as the invocation metadata object that must be included in the request.
43
+ */
44
+ private getFunctionsForRequest;
45
+ /**
46
+ * Runs a request through the edge functions handler. The request may or may
47
+ * not match any edge functions: if it does, this method takes ownership of
48
+ * the request and returns the corresponding response; if it doesn't, the
49
+ * method returns `undefined`.
50
+ */
51
+ match(request: Request): Promise<{
52
+ handle: (request: Request, originServerAddress: string) => Promise<Response>;
53
+ } | undefined>;
54
+ /**
55
+ * Initializes the Deno server where the edge functions will run.
56
+ */
57
+ private initialize;
58
+ private renderError;
59
+ private waitForDenoServer;
60
+ stop(): Promise<void>;
61
+ }
62
+
63
+ export { EdgeFunctionsHandler, type EdgeFunctionsMatch };
@@ -0,0 +1,339 @@
1
+ // src/node/main.ts
2
+ import path from "path";
3
+ import { fileURLToPath, pathToFileURL } from "url";
4
+ import {
5
+ renderFunctionErrorPage,
6
+ killProcess
7
+ } from "@netlify/dev-utils";
8
+ import {
9
+ find,
10
+ generateManifest,
11
+ mergeDeclarations,
12
+ DenoBridge
13
+ } from "@netlify/edge-bundler";
14
+ import { base64Encode } from "@netlify/runtime-utils";
15
+ import getAvailablePort from "get-port";
16
+
17
+ // src/node/headers.ts
18
+ var headers = {
19
+ BlobsInfo: "x-nf-blobs-info",
20
+ DeployID: "x-nf-deploy-id",
21
+ DeployContext: "x-nf-deploy-context",
22
+ FeatureFlags: "x-nf-feature-flags",
23
+ ForwardedHost: "x-forwarded-host",
24
+ ForwardedProtocol: "x-forwarded-proto",
25
+ Functions: "x-nf-edge-functions",
26
+ InvocationMetadata: "x-nf-edge-functions-metadata",
27
+ Geo: "x-nf-geo",
28
+ Passthrough: "x-nf-passthrough",
29
+ PassthroughHost: "x-nf-passthrough-host",
30
+ PassthroughProtocol: "x-nf-passthrough-proto",
31
+ IP: "x-nf-client-connection-ip",
32
+ Site: "X-NF-Site-Info",
33
+ DebugLogging: "x-nf-debug-logging",
34
+ Account: "x-nf-account-info",
35
+ RequestID: "x-nf-request-id",
36
+ AvailableFunctions: "x-nf-available-edge-functions",
37
+ FunctionDeclarations: "x-nf-edge-functions-declarations",
38
+ UncaughtError: "x-nf-uncaught-error",
39
+ AcceptEncoding: "accept-encoding"
40
+ };
41
+
42
+ // src/node/main.ts
43
+ var denoRunPath = path.resolve(fileURLToPath(import.meta.url), "../../deno/server.mjs");
44
+ var DENO_SERVER_POLL_INTERVAL = 50;
45
+ var DENO_SERVER_POLL_TIMEOUT = 3e3;
46
+ var LOCAL_HOST = "127.0.0.1";
47
+ var UPSTREAM_REQUEST_TIMEOUT = 37e3;
48
+ var REQUEST_TIMEOUT = UPSTREAM_REQUEST_TIMEOUT - 1e3;
49
+ var EdgeFunctionsHandler = class {
50
+ configDeclarations;
51
+ denoServerProcess;
52
+ directories;
53
+ geolocation;
54
+ initialization;
55
+ initialized;
56
+ stopped;
57
+ logger;
58
+ requestTimeout;
59
+ siteID;
60
+ siteName;
61
+ constructor(options) {
62
+ this.configDeclarations = options.configDeclarations;
63
+ this.directories = options.directories;
64
+ this.geolocation = options.geolocation;
65
+ this.initialization = this.initialize({
66
+ ...options.env,
67
+ DENO_REGION: "dev"
68
+ });
69
+ this.initialized = false;
70
+ this.stopped = false;
71
+ this.logger = options.logger;
72
+ this.requestTimeout = options.requestTimeout ?? REQUEST_TIMEOUT;
73
+ this.siteID = options.siteID;
74
+ this.siteName = options.siteName;
75
+ }
76
+ /**
77
+ * Retrieves the in-source configuration objects for a set of edge functions.
78
+ * The evaluation of the functions and the retrieval of the configs must take
79
+ * place in Deno, but all the logic for processing those configurations and
80
+ * merging them with other sources lives in Node.js. To keep a single source
81
+ * of truth, we make a request to the Deno server with a special method that
82
+ * instructs the handler to evaluate the functions and return their configs,
83
+ * which are then returned by this method.
84
+ */
85
+ async getFunctionConfigs(denoPort, functions) {
86
+ const url = new URL(`http://${LOCAL_HOST}:${denoPort.toString()}`);
87
+ url.searchParams.set("functions", encodeURIComponent(JSON.stringify(functions)));
88
+ const res = await fetch(url, {
89
+ method: "NETLIFYCONFIG"
90
+ });
91
+ const data = await res.json();
92
+ if (res.ok) {
93
+ return { configs: data };
94
+ }
95
+ return { error: data.error };
96
+ }
97
+ /**
98
+ * Returns the list of edge functions that should run for a given request.
99
+ * It computes both the names of the edge functions that should run as well
100
+ * as the invocation metadata object that must be included in the request.
101
+ */
102
+ getFunctionsForRequest(req, availableFunctions, functionConfigs) {
103
+ const url = new URL(req.url);
104
+ const declarations = mergeDeclarations(this.configDeclarations, functionConfigs, {}, []);
105
+ const { manifest } = generateManifest({
106
+ declarations,
107
+ userFunctionConfig: functionConfigs,
108
+ functions: availableFunctions
109
+ });
110
+ const matchingFunctionNames = [];
111
+ const routeIndexes = [];
112
+ const routes = [...manifest.routes, ...manifest.post_cache_routes];
113
+ routes.forEach((route, index) => {
114
+ if (route.methods && route.methods.length !== 0 && !route.methods.includes(req.method)) {
115
+ return;
116
+ }
117
+ const pattern = new RegExp(route.pattern);
118
+ if (!pattern.test(url.pathname)) {
119
+ return;
120
+ }
121
+ if (route.headers) {
122
+ const headerMatches = Object.entries(route.headers).every(([headerName, headerMatch]) => {
123
+ const requestHeaderValue = req.headers.get(headerName);
124
+ if (headerMatch?.matcher === "exists") {
125
+ return requestHeaderValue !== null;
126
+ }
127
+ if (headerMatch?.matcher === "missing") {
128
+ return requestHeaderValue === null;
129
+ }
130
+ if (requestHeaderValue && headerMatch?.matcher === "regex") {
131
+ const pattern2 = new RegExp(headerMatch.pattern);
132
+ return pattern2.test(requestHeaderValue.split(", ").join(","));
133
+ }
134
+ return false;
135
+ });
136
+ if (!headerMatches) {
137
+ return;
138
+ }
139
+ }
140
+ const isExcludedForFunction = manifest.function_config[route.function]?.excluded_patterns?.some(
141
+ (pattern2) => new RegExp(pattern2).test(url.pathname)
142
+ );
143
+ if (isExcludedForFunction) {
144
+ return;
145
+ }
146
+ const isExcludedForRoute = route.excluded_patterns.some((pattern2) => new RegExp(pattern2).test(url.pathname));
147
+ if (isExcludedForRoute) {
148
+ return;
149
+ }
150
+ matchingFunctionNames.push(route.function);
151
+ routeIndexes.push(index);
152
+ });
153
+ const invocationMetadata = {
154
+ function_config: manifest.function_config,
155
+ req_routes: routeIndexes,
156
+ routes: routes.map((route) => ({
157
+ function: route.function,
158
+ path: route.path,
159
+ pattern: route.pattern
160
+ }))
161
+ };
162
+ return { functionNames: matchingFunctionNames, invocationMetadata };
163
+ }
164
+ /**
165
+ * Runs a request through the edge functions handler. The request may or may
166
+ * not match any edge functions: if it does, this method takes ownership of
167
+ * the request and returns the corresponding response; if it doesn't, the
168
+ * method returns `undefined`.
169
+ */
170
+ async match(request) {
171
+ if (request.headers.has(headers.Passthrough)) {
172
+ return;
173
+ }
174
+ const functions = await find(this.directories);
175
+ if (functions.length === 0) {
176
+ return;
177
+ }
178
+ const functionsMap = functions.reduce(
179
+ (acc, { name, path: path2 }) => ({
180
+ ...acc,
181
+ [name]: pathToFileURL(path2).toString()
182
+ }),
183
+ {}
184
+ );
185
+ const initMessage = setTimeout(() => {
186
+ if (this.initialized) {
187
+ return;
188
+ }
189
+ this.logger.log(
190
+ "Setting up the Netlify Edge Functions environment. This may take up to a couple of minutes, depending on your internet connection."
191
+ );
192
+ }, 5e3);
193
+ const { denoPort, success } = await this.initialization;
194
+ if (!success) {
195
+ clearTimeout(initMessage);
196
+ return;
197
+ }
198
+ const acceptsHTML = Boolean(request.headers.get("accept")?.includes("text/html"));
199
+ const { configs, error } = await this.getFunctionConfigs(denoPort, functionsMap);
200
+ if (error) {
201
+ return { handle: () => this.renderError(JSON.stringify({ error }), acceptsHTML) };
202
+ }
203
+ const { functionNames, invocationMetadata } = this.getFunctionsForRequest(request, functions, configs);
204
+ if (functionNames.length === 0) {
205
+ return;
206
+ }
207
+ return {
208
+ handle: async (request2, originServerAddress) => {
209
+ const originURL = new URL(originServerAddress);
210
+ const url = new URL(request2.url);
211
+ url.hostname = LOCAL_HOST;
212
+ url.port = denoPort.toString();
213
+ url.protocol = "http:";
214
+ request2.headers.set(headers.AcceptEncoding, "identity");
215
+ request2.headers.set(headers.AvailableFunctions, JSON.stringify(functionsMap));
216
+ request2.headers.set(headers.DeployContext, "dev");
217
+ request2.headers.set(headers.DeployID, "0");
218
+ request2.headers.set(headers.ForwardedHost, `localhost:${originURL.port}`);
219
+ request2.headers.set(headers.ForwardedProtocol, `http:`);
220
+ request2.headers.set(headers.Functions, functionNames.join(","));
221
+ request2.headers.set(headers.Geo, base64Encode(this.geolocation));
222
+ request2.headers.set(headers.InvocationMetadata, base64Encode(invocationMetadata));
223
+ request2.headers.set(headers.IP, LOCAL_HOST);
224
+ request2.headers.set(headers.Passthrough, "passthrough");
225
+ request2.headers.set(headers.PassthroughHost, `localhost:${originURL.port}`);
226
+ request2.headers.set(headers.PassthroughProtocol, "http:");
227
+ const site = {
228
+ id: this.siteID,
229
+ name: this.siteName,
230
+ url: originServerAddress
231
+ };
232
+ request2.headers.set(headers.Site, base64Encode(site));
233
+ const response = await fetch(url, request2);
234
+ const isUncaughtError = response.headers.has(headers.UncaughtError);
235
+ if (isUncaughtError) {
236
+ return await this.renderError(await response.text(), acceptsHTML);
237
+ }
238
+ return response;
239
+ }
240
+ };
241
+ }
242
+ /**
243
+ * Initializes the Deno server where the edge functions will run.
244
+ */
245
+ async initialize(env) {
246
+ let success = true;
247
+ const processRef = {};
248
+ this.denoServerProcess = processRef;
249
+ const denoPort = await getAvailablePort();
250
+ const denoBridge = new DenoBridge({
251
+ // TODO: Remove this override once `@netlify/edge-bundler` has been
252
+ // updated to require Deno 2.x.
253
+ versionRange: "^2.2.4"
254
+ });
255
+ const runOptions = {
256
+ denoPort,
257
+ requestTimeout: this.requestTimeout
258
+ };
259
+ const denoFlags = ["--allow-scripts", "--quiet", "--no-lock"];
260
+ const script = `import('${pathToFileURL(denoRunPath).toString()}');`;
261
+ try {
262
+ await denoBridge.runInBackground(["eval", ...denoFlags, script, JSON.stringify(runOptions)], processRef, {
263
+ env,
264
+ extendEnv: false,
265
+ pipeOutput: true
266
+ });
267
+ if (this.stopped) {
268
+ await killProcess(processRef.ps);
269
+ this.denoServerProcess = void 0;
270
+ success = false;
271
+ }
272
+ } catch (error) {
273
+ success = false;
274
+ this.logger.error(`An error occurred while setting up the Netlify Edge Functions environment: ${String(error)}`);
275
+ }
276
+ if (success) {
277
+ await this.waitForDenoServer(denoPort);
278
+ }
279
+ this.initialized = true;
280
+ return {
281
+ denoPort,
282
+ success
283
+ };
284
+ }
285
+ async renderError(errorBuffer, acceptsHTML) {
286
+ const status = 500;
287
+ const {
288
+ error: { message, name, stack = "" }
289
+ } = JSON.parse(errorBuffer);
290
+ if (!acceptsHTML) {
291
+ return new Response(`${name}: ${message}
292
+ ${stack}`, { status });
293
+ }
294
+ const formattedError = JSON.stringify({
295
+ errorType: name,
296
+ errorMessage: message,
297
+ trace: stack.split("\\n")
298
+ });
299
+ const errorBody = await renderFunctionErrorPage(formattedError, "edge function");
300
+ return new Response(errorBody, {
301
+ headers: {
302
+ "Content-Type": "text/html"
303
+ },
304
+ status
305
+ });
306
+ }
307
+ async waitForDenoServer(port, count = 1) {
308
+ try {
309
+ await fetch(`http://${LOCAL_HOST}:${port.toString()}`, {
310
+ method: "HEAD"
311
+ });
312
+ } catch {
313
+ if (!this.denoServerProcess) {
314
+ return;
315
+ }
316
+ if ((count + 1) * DENO_SERVER_POLL_INTERVAL > DENO_SERVER_POLL_TIMEOUT) {
317
+ throw new Error("Could not establish a connection to the Netlify Edge Functions local development server");
318
+ }
319
+ await new Promise((resolve) => setTimeout(resolve, DENO_SERVER_POLL_INTERVAL));
320
+ return this.waitForDenoServer(port, count + 1);
321
+ }
322
+ }
323
+ async stop() {
324
+ if (this.stopped) return;
325
+ this.stopped = true;
326
+ if (!this.denoServerProcess?.ps) {
327
+ return;
328
+ }
329
+ const { ps } = this.denoServerProcess;
330
+ this.denoServerProcess = void 0;
331
+ try {
332
+ await killProcess(ps);
333
+ } catch {
334
+ }
335
+ }
336
+ };
337
+ export {
338
+ EdgeFunctionsHandler
339
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@netlify/edge-functions-dev",
3
+ "version": "1.0.0",
4
+ "description": "Local emulation for Netlify Edge Functions",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=20.6.1"
8
+ },
9
+ "main": "./dist/node/main.js",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/node/main.d.ts",
13
+ "default": "./dist/node/main.js"
14
+ }
15
+ },
16
+ "types": "./dist/node/main.d.ts",
17
+ "files": [
18
+ "dist/**/*"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup-node",
22
+ "prepack": "npm run build",
23
+ "test": "vitest run",
24
+ "test:dev": "vitest",
25
+ "dev": "tsup-node --watch"
26
+ },
27
+ "keywords": [
28
+ "netlify",
29
+ "edge functions"
30
+ ],
31
+ "license": "MIT",
32
+ "repository": "netlify/primitives",
33
+ "bugs": {
34
+ "url": "https://github.com/netlify/primitives/issues"
35
+ },
36
+ "author": "Netlify Inc.",
37
+ "dependencies": {
38
+ "@netlify/dev-utils": "4.3.0",
39
+ "@netlify/edge-bundler": "^14.5.2",
40
+ "@netlify/edge-functions": "3.0.0",
41
+ "@netlify/edge-functions-bootstrap": "2.16.0",
42
+ "@netlify/runtime-utils": "2.2.0",
43
+ "get-port": "^7.1.0"
44
+ },
45
+ "devDependencies": {
46
+ "execa": "^8.0.1",
47
+ "tsup": "^8.0.0",
48
+ "vitest": "^3.0.0"
49
+ }
50
+ }