@php-wasm/universal 0.2.0 → 0.3.1

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/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R=Symbol("error"),S=Symbol("message");class v extends Event{constructor(e,t={}){super(e),this[R]=t.error===void 0?null:t.error,this[S]=t.message===void 0?"":t.message}get error(){return this[R]}get message(){return this[S]}}Object.defineProperty(v.prototype,"error",{enumerable:!0});Object.defineProperty(v.prototype,"message",{enumerable:!0});const D=typeof globalThis.ErrorEvent=="function"?globalThis.ErrorEvent:v;function U(s){return s instanceof Error?"exitCode"in s&&s?.exitCode===0||s?.name==="ExitStatus"&&"status"in s&&s.status===0:!1}class A extends EventTarget{constructor(){super(...arguments),this.listenersCount=0}addEventListener(e,t){++this.listenersCount,super.addEventListener(e,t)}removeEventListener(e,t){--this.listenersCount,super.removeEventListener(e,t)}hasListeners(){return this.listenersCount>0}}function q(s){s.asm={...s.asm};const e=new A;for(const t in s.asm)if(typeof s.asm[t]=="function"){const r=s.asm[t];s.asm[t]=function(...n){try{return r(...n)}catch(o){if(!(o instanceof Error))throw o;const i=W(o,s.lastAsyncifyStackSource?.stack);if(s.lastAsyncifyStackSource&&(o.cause=s.lastAsyncifyStackSource),e.hasListeners()){e.dispatchEvent(new D("error",{error:o,message:i}));return}throw U(o)||V(i),o}}}return e}let w=[];function j(){return w}function W(s,e){if(s.message==="unreachable"){let t=z;e||(t+=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R=Symbol("error"),S=Symbol("message");class E extends Event{constructor(e,t={}){super(e),this[R]=t.error===void 0?null:t.error,this[S]=t.message===void 0?"":t.message}get error(){return this[R]}get message(){return this[S]}}Object.defineProperty(E.prototype,"error",{enumerable:!0});Object.defineProperty(E.prototype,"message",{enumerable:!0});const D=typeof globalThis.ErrorEvent=="function"?globalThis.ErrorEvent:E;function A(s){return s instanceof Error?"exitCode"in s&&s?.exitCode===0||s?.name==="ExitStatus"&&"status"in s&&s.status===0:!1}class U extends EventTarget{constructor(){super(...arguments),this.listenersCount=0}addEventListener(e,t){++this.listenersCount,super.addEventListener(e,t)}removeEventListener(e,t){--this.listenersCount,super.removeEventListener(e,t)}hasListeners(){return this.listenersCount>0}}function W(s){s.asm={...s.asm};const e=new U;for(const t in s.asm)if(typeof s.asm[t]=="function"){const r=s.asm[t];s.asm[t]=function(...n){try{return r(...n)}catch(o){if(!(o instanceof Error))throw o;const i=z(o,s.lastAsyncifyStackSource?.stack);if(s.lastAsyncifyStackSource&&(o.cause=s.lastAsyncifyStackSource),e.hasListeners()){e.dispatchEvent(new D("error",{error:o,message:i}));return}throw A(o)||Y(i),o}}}return e}let P=[];function j(){return P}function z(s,e){if(s.message==="unreachable"){let t=G;e||(t+=`
2
2
 
3
3
  This stack trace is lacking. For a better one initialize
4
4
  the PHP runtime with { debug: true }, e.g. PHPNode.load('8.1', { debug: true }).
5
5
 
6
- `),w=Y(e||s.stack||"");for(const r of w)t+=` * ${r}
7
- `;return t}return s.message}const z=`
6
+ `),P=J(e||s.stack||"");for(const r of P)t+=` * ${r}
7
+ `;return t}return s.message}const G=`
8
8
  "unreachable" WASM instruction executed.
9
9
 
10
10
  The typical reason is a PHP function missing from the ASYNCIFY_ONLY
@@ -28,13 +28,13 @@ the Dockerfile, you'll need to trigger this error again with long stack
28
28
  traces enabled. In node.js, you can do it using the --stack-trace-limit=100
29
29
  CLI option:
30
30
 
31
- `,x="\x1B[41m",G="\x1B[1m",H="\x1B[0m",T="\x1B[K";let k=!1;function V(s){if(!k){k=!0,console.log(`${x}
32
- ${T}
33
- ${G} WASM ERROR${H}${x}`);for(const e of s.split(`
34
- `))console.log(`${T} ${e} `);console.log(`${H}`)}}function Y(s){try{const e=s.split(`
35
- `).slice(1).map(t=>{const r=t.trim().substring(3).split(" ");return{fn:r.length>=2?r[0]:"<unknown>",isWasm:t.includes("wasm://")}}).filter(({fn:t,isWasm:r})=>r&&!t.startsWith("dynCall_")&&!t.startsWith("invoke_")).map(({fn:t})=>t);return Array.from(new Set(e))}catch{return[]}}class y{constructor(e,t,r,n="",o=0){this.httpStatusCode=e,this.headers=t,this.bytes=r,this.exitCode=o,this.errors=n}static fromRawData(e){return new y(e.httpStatusCode,e.headers,e.bytes,e.errors,e.exitCode)}toRawData(){return{headers:this.headers,bytes:this.bytes,errors:this.errors,exitCode:this.exitCode,httpStatusCode:this.httpStatusCode}}get json(){return JSON.parse(this.text)}get text(){return new TextDecoder().decode(this.bytes)}}const E=["8.2","8.1","8.0","7.4","7.3","7.2","7.1","7.0","5.6"],J=E[0],K=E;class O{#e;#t;constructor(e,t={}){this.requestHandler=e,this.#e={},this.#t={handleRedirects:!1,maxRedirects:4,...t}}async request(e,t=0){const r=await this.requestHandler.request({...e,headers:{...e.headers,cookie:this.#r()}});if(r.headers["set-cookie"]&&this.#s(r.headers["set-cookie"]),this.#t.handleRedirects&&r.headers.location&&t<this.#t.maxRedirects){const n=new URL(r.headers.location[0],this.requestHandler.absoluteUrl);return this.request({url:n.toString(),method:"GET",headers:{}},t+1)}return r}pathToInternalUrl(e){return this.requestHandler.pathToInternalUrl(e)}internalUrlToPath(e){return this.requestHandler.internalUrlToPath(e)}get absoluteUrl(){return this.requestHandler.absoluteUrl}get documentRoot(){return this.requestHandler.documentRoot}#s(e){for(const t of e)try{if(!t.includes("="))continue;const r=t.indexOf("="),n=t.substring(0,r),o=t.substring(r+1).split(";")[0];this.#e[n]=o}catch(r){console.error(r)}}#r(){const e=[];for(const t in this.#e)e.push(`${t}=${this.#e[t]}`);return e.join("; ")}}class Z{constructor({concurrency:e}){this._running=0,this.concurrency=e,this.queue=[]}get running(){return this._running}async acquire(){for(;;)if(this._running>=this.concurrency)await new Promise(e=>this.queue.push(e));else{this._running++;let e=!1;return()=>{e||(e=!0,this._running--,this.queue.length>0&&this.queue.shift()())}}}async run(e){const t=await this.acquire();try{return await e()}finally{t()}}}const I="http://example.com";function P(s){return s.toString().substring(s.origin.length)}function b(s,e){return!e||!s.startsWith(e)?s:s.substring(e.length)}function L(s,e){return!e||s.startsWith(e)?s:e+s}class N{#e;#t;#s;#r;#o;#n;#i;#a;#l;constructor(e,t={}){this.#a=new Z({concurrency:1});const{documentRoot:r="/www/",absoluteUrl:n=typeof location=="object"?location?.href:"",isStaticFilePath:o=()=>!1}=t;this.php=e,this.#e=r,this.#l=o;const i=new URL(n);this.#s=i.hostname,this.#r=i.port?Number(i.port):i.protocol==="https:"?443:80,this.#t=(i.protocol||"").replace(":","");const l=this.#r!==443&&this.#r!==80;this.#o=[this.#s,l?`:${this.#r}`:""].join(""),this.#n=i.pathname.replace(/\/+$/,""),this.#i=[`${this.#t}://`,this.#o,this.#n].join("")}pathToInternalUrl(e){return`${this.absoluteUrl}${e}`}internalUrlToPath(e){const t=new URL(e);return t.pathname.startsWith(this.#n)&&(t.pathname=t.pathname.slice(this.#n.length)),P(t)}get isRequestRunning(){return this.#a.running>0}get absoluteUrl(){return this.#i}get documentRoot(){return this.#e}async request(e){const t=e.url.startsWith("http://")||e.url.startsWith("https://"),r=new URL(e.url,t?void 0:I),n=b(r.pathname,this.#n);return this.#l(n)?this.#c(n):await this.#h(e,r)}#c(e){const t=`${this.#e}${e}`;if(!this.php.fileExists(t))return new y(404,{},new TextEncoder().encode("404 File not found"));const r=this.php.readFileAsBuffer(t);return new y(200,{"content-length":[`${r.byteLength}`],"content-type":[X(t)],"accept-ranges":["bytes"],"cache-control":["public, max-age=0"]},r)}async#h(e,t){const r=await this.#a.acquire();try{this.php.addServerGlobalEntry("DOCUMENT_ROOT",this.#e),this.php.addServerGlobalEntry("HTTPS",this.#i.startsWith("https://")?"on":"");let n="GET";const o={host:this.#o,...$(e.headers||{})},i=[];if(e.files&&Object.keys(e.files).length){n="POST";for(const c in e.files){const m=e.files[c];i.push({key:c,name:m.name,type:m.type,data:new Uint8Array(await m.arrayBuffer())})}o["content-type"]?.startsWith("multipart/form-data")&&(e.formData=Q(e.body||""),o["content-type"]="application/x-www-form-urlencoded",delete e.body)}let l;e.formData!==void 0?(n="POST",o["content-type"]=o["content-type"]||"application/x-www-form-urlencoded",l=new URLSearchParams(e.formData).toString()):l=e.body;let h;try{h=this.#u(t.pathname)}catch{return new y(404,{},new TextEncoder().encode("404 File not found"))}return await this.php.run({relativeUri:L(P(t),this.#n),protocol:this.#t,method:e.method||n,body:l,fileInfos:i,scriptPath:h,headers:o})}finally{r()}}#u(e){let t=b(e,this.#n);t.includes(".php")?t=t.split(".php")[0]+".php":(t.endsWith("/")||(t+="/"),t.endsWith("index.php")||(t+="index.php"));const r=`${this.#e}${t}`;if(this.php.fileExists(r))return r;if(!this.php.fileExists(`${this.#e}/index.php`))throw new Error(`File not found: ${r}`);return`${this.#e}/index.php`}}function Q(s){const e={},t=s.match(/--(.*)\r\n/);if(!t)return e;const r=t[1],n=s.split(`--${r}`);return n.shift(),n.pop(),n.forEach(o=>{const i=o.indexOf(`\r
31
+ `,x="\x1B[41m",V="\x1B[1m",H="\x1B[0m",k="\x1B[K";let T=!1;function Y(s){if(!T){T=!0,console.log(`${x}
32
+ ${k}
33
+ ${V} WASM ERROR${H}${x}`);for(const e of s.split(`
34
+ `))console.log(`${k} ${e} `);console.log(`${H}`)}}function J(s){try{const e=s.split(`
35
+ `).slice(1).map(t=>{const r=t.trim().substring(3).split(" ");return{fn:r.length>=2?r[0]:"<unknown>",isWasm:t.includes("wasm://")}}).filter(({fn:t,isWasm:r})=>r&&!t.startsWith("dynCall_")&&!t.startsWith("invoke_")).map(({fn:t})=>t);return Array.from(new Set(e))}catch{return[]}}class y{constructor(e,t,r,n="",o=0){this.httpStatusCode=e,this.headers=t,this.bytes=r,this.exitCode=o,this.errors=n}static fromRawData(e){return new y(e.httpStatusCode,e.headers,e.bytes,e.errors,e.exitCode)}toRawData(){return{headers:this.headers,bytes:this.bytes,errors:this.errors,exitCode:this.exitCode,httpStatusCode:this.httpStatusCode}}get json(){return JSON.parse(this.text)}get text(){return new TextDecoder().decode(this.bytes)}}const v=["8.2","8.1","8.0","7.4","7.3","7.2","7.1","7.0","5.6"],K=v[0],Z=v,L=["iconv","mbstring","xml-bundle","gd"],Q={"kitchen-sink":L};class O{#e;#t;constructor(e,t={}){this.requestHandler=e,this.#e={},this.#t={handleRedirects:!1,maxRedirects:4,...t}}async request(e,t=0){const r=await this.requestHandler.request({...e,headers:{...e.headers,cookie:this.#r()}});if(r.headers["set-cookie"]&&this.#s(r.headers["set-cookie"]),this.#t.handleRedirects&&r.headers.location&&t<this.#t.maxRedirects){const n=new URL(r.headers.location[0],this.requestHandler.absoluteUrl);return this.request({url:n.toString(),method:"GET",headers:{}},t+1)}return r}pathToInternalUrl(e){return this.requestHandler.pathToInternalUrl(e)}internalUrlToPath(e){return this.requestHandler.internalUrlToPath(e)}get absoluteUrl(){return this.requestHandler.absoluteUrl}get documentRoot(){return this.requestHandler.documentRoot}#s(e){for(const t of e)try{if(!t.includes("="))continue;const r=t.indexOf("="),n=t.substring(0,r),o=t.substring(r+1).split(";")[0];this.#e[n]=o}catch(r){console.error(r)}}#r(){const e=[];for(const t in this.#e)e.push(`${t}=${this.#e[t]}`);return e.join("; ")}}class X{constructor({concurrency:e}){this._running=0,this.concurrency=e,this.queue=[]}get running(){return this._running}async acquire(){for(;;)if(this._running>=this.concurrency)await new Promise(e=>this.queue.push(e));else{this._running++;let e=!1;return()=>{e||(e=!0,this._running--,this.queue.length>0&&this.queue.shift()())}}}async run(e){const t=await this.acquire();try{return await e()}finally{t()}}}const I="http://example.com";function w(s){return s.toString().substring(s.origin.length)}function b(s,e){return!e||!s.startsWith(e)?s:s.substring(e.length)}function N(s,e){return!e||s.startsWith(e)?s:e+s}class ${#e;#t;#s;#r;#o;#n;#i;#a;constructor(e,t={}){this.#a=new X({concurrency:1});const{documentRoot:r="/www/",absoluteUrl:n=typeof location=="object"?location?.href:""}=t;this.php=e,this.#e=r;const o=new URL(n);this.#s=o.hostname,this.#r=o.port?Number(o.port):o.protocol==="https:"?443:80,this.#t=(o.protocol||"").replace(":","");const i=this.#r!==443&&this.#r!==80;this.#o=[this.#s,i?`:${this.#r}`:""].join(""),this.#n=o.pathname.replace(/\/+$/,""),this.#i=[`${this.#t}://`,this.#o,this.#n].join("")}pathToInternalUrl(e){return`${this.absoluteUrl}${e}`}internalUrlToPath(e){const t=new URL(e);return t.pathname.startsWith(this.#n)&&(t.pathname=t.pathname.slice(this.#n.length)),w(t)}get isRequestRunning(){return this.#a.running>0}get absoluteUrl(){return this.#i}get documentRoot(){return this.#e}async request(e){const t=e.url.startsWith("http://")||e.url.startsWith("https://"),r=new URL(e.url,t?void 0:I),n=b(r.pathname,this.#n),o=`${this.#e}${n}`;return re(o)?await this.#c(e,r):this.#l(o)}#l(e){if(!this.php.fileExists(e))return new y(404,{"x-file-type":["static"]},new TextEncoder().encode("404 File not found"));const t=this.php.readFileAsBuffer(e);return new y(200,{"content-length":[`${t.byteLength}`],"content-type":[te(e)],"accept-ranges":["bytes"],"cache-control":["public, max-age=0"]},t)}async#c(e,t){const r=await this.#a.acquire();try{this.php.addServerGlobalEntry("DOCUMENT_ROOT",this.#e),this.php.addServerGlobalEntry("HTTPS",this.#i.startsWith("https://")?"on":"");let n="GET";const o={host:this.#o,...B(e.headers||{})},i=[];if(e.files&&Object.keys(e.files).length){n="POST";for(const c in e.files){const m=e.files[c];i.push({key:c,name:m.name,type:m.type,data:new Uint8Array(await m.arrayBuffer())})}o["content-type"]?.startsWith("multipart/form-data")&&(e.formData=ee(e.body||""),o["content-type"]="application/x-www-form-urlencoded",delete e.body)}let l;e.formData!==void 0?(n="POST",o["content-type"]=o["content-type"]||"application/x-www-form-urlencoded",l=new URLSearchParams(e.formData).toString()):l=e.body;let u;try{u=this.#u(t.pathname)}catch{return new y(404,{},new TextEncoder().encode("404 File not found"))}return await this.php.run({relativeUri:N(w(t),this.#n),protocol:this.#t,method:e.method||n,body:l,fileInfos:i,scriptPath:u,headers:o})}finally{r()}}#u(e){let t=b(e,this.#n);t.includes(".php")?t=t.split(".php")[0]+".php":(t.endsWith("/")||(t+="/"),t.endsWith("index.php")||(t+="index.php"));const r=`${this.#e}${t}`;if(this.php.fileExists(r))return r;if(!this.php.fileExists(`${this.#e}/index.php`))throw new Error(`File not found: ${r}`);return`${this.#e}/index.php`}}function ee(s){const e={},t=s.match(/--(.*)\r\n/);if(!t)return e;const r=t[1],n=s.split(`--${r}`);return n.shift(),n.pop(),n.forEach(o=>{const i=o.indexOf(`\r
36
36
  \r
37
- `),l=o.substring(0,i).trim(),h=o.substring(i+4).trim(),c=l.match(/name="([^"]+)"/);if(c){const m=c[1];e[m]=h}}),e}function X(s){switch(s.split(".").pop()){case"css":return"text/css";case"js":return"application/javascript";case"png":return"image/png";case"jpg":case"jpeg":return"image/jpeg";case"gif":return"image/gif";case"svg":return"image/svg+xml";case"woff":return"font/woff";case"woff2":return"font/woff2";case"ttf":return"font/ttf";case"otf":return"font/otf";case"eot":return"font/eot";case"ico":return"image/x-icon";case"html":return"text/html";case"json":return"application/json";case"xml":return"application/xml";case"txt":case"md":return"text/plain";default:return"application-octet-stream"}}const F={0:"No error occurred. System call completed successfully.",1:"Argument list too long.",2:"Permission denied.",3:"Address in use.",4:"Address not available.",5:"Address family not supported.",6:"Resource unavailable, or operation would block.",7:"Connection already in progress.",8:"Bad file descriptor.",9:"Bad message.",10:"Device or resource busy.",11:"Operation canceled.",12:"No child processes.",13:"Connection aborted.",14:"Connection refused.",15:"Connection reset.",16:"Resource deadlock would occur.",17:"Destination address required.",18:"Mathematics argument out of domain of function.",19:"Reserved.",20:"File exists.",21:"Bad address.",22:"File too large.",23:"Host is unreachable.",24:"Identifier removed.",25:"Illegal byte sequence.",26:"Operation in progress.",27:"Interrupted function.",28:"Invalid argument.",29:"I/O error.",30:"Socket is connected.",31:"There is a directory under that path.",32:"Too many levels of symbolic links.",33:"File descriptor value too large.",34:"Too many links.",35:"Message too large.",36:"Reserved.",37:"Filename too long.",38:"Network is down.",39:"Connection aborted by network.",40:"Network unreachable.",41:"Too many files open in system.",42:"No buffer space available.",43:"No such device.",44:"There is no such file or directory OR the parent directory does not exist.",45:"Executable file format error.",46:"No locks available.",47:"Reserved.",48:"Not enough space.",49:"No message of the desired type.",50:"Protocol not available.",51:"No space left on device.",52:"Function not supported.",53:"The socket is not connected.",54:"Not a directory or a symbolic link to a directory.",55:"Directory not empty.",56:"State not recoverable.",57:"Not a socket.",58:"Not supported, or operation not supported on socket.",59:"Inappropriate I/O control operation.",60:"No such device or address.",61:"Value too large to be stored in data type.",62:"Previous owner died.",63:"Operation not permitted.",64:"Broken pipe.",65:"Protocol error.",66:"Protocol not supported.",67:"Protocol wrong type for socket.",68:"Result too large.",69:"Read-only file system.",70:"Invalid seek.",71:"No such process.",72:"Reserved.",73:"Connection timed out.",74:"Text file busy.",75:"Cross-device link.",76:"Extension: Capabilities insufficient."};function p(s=""){return function(t,r,n){const o=n.value;n.value=function(...i){try{return o.apply(this,i)}catch(l){const h=typeof l=="object"?l?.errno:null;if(h in F){const c=F[h],m=typeof i[0]=="string"?i[0]:null,M=m!==null?s.replaceAll("{path}",m):s;throw new Error(`${M}: ${c}`,{cause:l})}throw l}}}}async function ee(s,e={},t=[]){const[r,n,o]=C(),[i,l]=C(),h=s.init(re,{onAbort(c){o(c),l(),console.error(c)},ENV:{},locateFile:c=>c,...e,noInitialRun:!0,onRuntimeInitialized(){e.onRuntimeInitialized&&e.onRuntimeInitialized(),n()},monitorRunDependencies(c){c===0&&(delete h.monitorRunDependencies,l())}});return await Promise.all(t.map(({default:c})=>c(h))),t.length||l(),await i,await r,_.push(h),_.length-1}const _=[];function te(s){return _[s]}const re=function(){return typeof process<"u"&&process.release?.name==="node"?"NODE":typeof window<"u"?"WEB":typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope?"WORKER":"NODE"}(),C=()=>{const s=[],e=new Promise((t,r)=>{s.push(t,r)});return s.unshift(e),s};var se=Object.defineProperty,ne=Object.getOwnPropertyDescriptor,f=(s,e,t,r)=>{for(var n=r>1?void 0:r?ne(e,t):e,o=s.length-1,i;o>=0;o--)(i=s[o])&&(n=(r?i(e,t,n):i(n))||n);return r&&n&&se(e,t,n),n};const u="string",g="number",a=Symbol("__private__dont__use");class d{constructor(e,t){this.#e=[],this.#t=!1,this.#s=null,this.#r={},this.#o=[],e!==void 0&&this.initializeRuntime(e),t&&(this.requestHandler=new O(new N(this,t)))}#e;#t;#s;#r;#o;async onMessage(e){this.#o.push(e)}get absoluteUrl(){return this.requestHandler.requestHandler.absoluteUrl}get documentRoot(){return this.requestHandler.requestHandler.documentRoot}pathToInternalUrl(e){return this.requestHandler.requestHandler.pathToInternalUrl(e)}internalUrlToPath(e){return this.requestHandler.requestHandler.internalUrlToPath(e)}initializeRuntime(e){if(this[a])throw new Error("PHP runtime already initialized.");const t=te(e);if(!t)throw new Error("Invalid PHP runtime id.");this[a]=t,t.onMessage=r=>{for(const n of this.#o)n(r)},this.#s=q(t)}setPhpIniPath(e){if(this.#t)throw new Error("Cannot set PHP ini path after calling run().");this[a].ccall("wasm_set_phpini_path",null,["string"],[e])}setPhpIniEntry(e,t){if(this.#t)throw new Error("Cannot set PHP ini entries after calling run().");this.#e.push([e,t])}chdir(e){this[a].FS.chdir(e)}async request(e,t){if(!this.requestHandler)throw new Error("No request handler available.");return this.requestHandler.request(e,t)}async run(e){this.#t||(this.#n(),this.#t=!0),this.#d(e.scriptPath||""),this.#a(e.relativeUri||""),this.#c(e.method||"GET");const{host:t,...r}={host:"example.com:443",...$(e.headers||{})};if(this.#l(t,e.protocol||"http"),this.#h(r),e.body&&this.#u(e.body),e.fileInfos)for(const n of e.fileInfos)this.#f(n);return e.code&&this.#m(" ?>"+e.code),this.#p(),await this.#y()}#n(){if(this.#e.length>0){const e=this.#e.map(([t,r])=>`${t}=${r}`).join(`
37
+ `),l=o.substring(0,i).trim(),u=o.substring(i+4).trim(),c=l.match(/name="([^"]+)"/);if(c){const m=c[1];e[m]=u}}),e}function te(s){switch(s.split(".").pop()){case"css":return"text/css";case"js":return"application/javascript";case"png":return"image/png";case"jpg":case"jpeg":return"image/jpeg";case"gif":return"image/gif";case"svg":return"image/svg+xml";case"woff":return"font/woff";case"woff2":return"font/woff2";case"ttf":return"font/ttf";case"otf":return"font/otf";case"eot":return"font/eot";case"ico":return"image/x-icon";case"html":return"text/html";case"json":return"application/json";case"xml":return"application/xml";case"txt":case"md":return"text/plain";default:return"application-octet-stream"}}function re(s){return se(s)||ne(s)}function se(s){return s.endsWith(".php")||s.includes(".php/")}function ne(s){return!s.split("/").pop().includes(".")}const C={0:"No error occurred. System call completed successfully.",1:"Argument list too long.",2:"Permission denied.",3:"Address in use.",4:"Address not available.",5:"Address family not supported.",6:"Resource unavailable, or operation would block.",7:"Connection already in progress.",8:"Bad file descriptor.",9:"Bad message.",10:"Device or resource busy.",11:"Operation canceled.",12:"No child processes.",13:"Connection aborted.",14:"Connection refused.",15:"Connection reset.",16:"Resource deadlock would occur.",17:"Destination address required.",18:"Mathematics argument out of domain of function.",19:"Reserved.",20:"File exists.",21:"Bad address.",22:"File too large.",23:"Host is unreachable.",24:"Identifier removed.",25:"Illegal byte sequence.",26:"Operation in progress.",27:"Interrupted function.",28:"Invalid argument.",29:"I/O error.",30:"Socket is connected.",31:"There is a directory under that path.",32:"Too many levels of symbolic links.",33:"File descriptor value too large.",34:"Too many links.",35:"Message too large.",36:"Reserved.",37:"Filename too long.",38:"Network is down.",39:"Connection aborted by network.",40:"Network unreachable.",41:"Too many files open in system.",42:"No buffer space available.",43:"No such device.",44:"There is no such file or directory OR the parent directory does not exist.",45:"Executable file format error.",46:"No locks available.",47:"Reserved.",48:"Not enough space.",49:"No message of the desired type.",50:"Protocol not available.",51:"No space left on device.",52:"Function not supported.",53:"The socket is not connected.",54:"Not a directory or a symbolic link to a directory.",55:"Directory not empty.",56:"State not recoverable.",57:"Not a socket.",58:"Not supported, or operation not supported on socket.",59:"Inappropriate I/O control operation.",60:"No such device or address.",61:"Value too large to be stored in data type.",62:"Previous owner died.",63:"Operation not permitted.",64:"Broken pipe.",65:"Protocol error.",66:"Protocol not supported.",67:"Protocol wrong type for socket.",68:"Result too large.",69:"Read-only file system.",70:"Invalid seek.",71:"No such process.",72:"Reserved.",73:"Connection timed out.",74:"Text file busy.",75:"Cross-device link.",76:"Extension: Capabilities insufficient."};function p(s=""){return function(t,r,n){const o=n.value;n.value=function(...i){try{return o.apply(this,i)}catch(l){const u=typeof l=="object"?l?.errno:null;if(u in C){const c=C[u],m=typeof i[0]=="string"?i[0]:null,q=m!==null?s.replaceAll("{path}",m):s;throw new Error(`${q}: ${c}`,{cause:l})}throw l}}}}async function oe(s,e={},t=[]){const[r,n,o]=F(),[i,l]=F(),u=s.init(ae,{onAbort(c){o(c),l(),console.error(c)},ENV:{},locateFile:c=>c,...e,noInitialRun:!0,onRuntimeInitialized(){e.onRuntimeInitialized&&e.onRuntimeInitialized(),n()},monitorRunDependencies(c){c===0&&(delete u.monitorRunDependencies,l())}});return await Promise.all(t.map(({default:c})=>c(u))),t.length||l(),await i,await r,_.push(u),_.length-1}const _=[];function ie(s){return _[s]}const ae=function(){return typeof process<"u"&&process.release?.name==="node"?"NODE":typeof window<"u"?"WEB":typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope?"WORKER":"NODE"}(),F=()=>{const s=[],e=new Promise((t,r)=>{s.push(t,r)});return s.unshift(e),s};var le=Object.defineProperty,ce=Object.getOwnPropertyDescriptor,f=(s,e,t,r)=>{for(var n=r>1?void 0:r?ce(e,t):e,o=s.length-1,i;o>=0;o--)(i=s[o])&&(n=(r?i(e,t,n):i(n))||n);return r&&n&&le(e,t,n),n};const h="string",g="number",a=Symbol("__private__dont__use");class d{constructor(e,t){this.#e=[],this.#t=!1,this.#s=null,this.#r={},this.#o=[],e!==void 0&&this.initializeRuntime(e),t&&(this.requestHandler=new O(new $(this,t)))}#e;#t;#s;#r;#o;async onMessage(e){this.#o.push(e)}get absoluteUrl(){return this.requestHandler.requestHandler.absoluteUrl}get documentRoot(){return this.requestHandler.requestHandler.documentRoot}pathToInternalUrl(e){return this.requestHandler.requestHandler.pathToInternalUrl(e)}internalUrlToPath(e){return this.requestHandler.requestHandler.internalUrlToPath(e)}initializeRuntime(e){if(this[a])throw new Error("PHP runtime already initialized.");const t=ie(e);if(!t)throw new Error("Invalid PHP runtime id.");this[a]=t,t.onMessage=r=>{for(const n of this.#o)n(r)},this.#s=W(t)}setPhpIniPath(e){if(this.#t)throw new Error("Cannot set PHP ini path after calling run().");this[a].ccall("wasm_set_phpini_path",null,["string"],[e])}setPhpIniEntry(e,t){if(this.#t)throw new Error("Cannot set PHP ini entries after calling run().");this.#e.push([e,t])}chdir(e){this[a].FS.chdir(e)}async request(e,t){if(!this.requestHandler)throw new Error("No request handler available.");return this.requestHandler.request(e,t)}async run(e){this.#t||(this.#n(),this.#t=!0),this.#d(e.scriptPath||""),this.#a(e.relativeUri||""),this.#c(e.method||"GET");const{host:t,...r}={host:"example.com:443",...B(e.headers||{})};if(this.#l(t,e.protocol||"http"),this.#u(r),e.body&&this.#h(e.body),e.fileInfos)for(const n of e.fileInfos)this.#f(n);return e.code&&this.#m(" ?>"+e.code),this.#p(),await this.#y()}#n(){if(this.#e.length>0){const e=this.#e.map(([t,r])=>`${t}=${r}`).join(`
38
38
  `)+`
39
39
 
40
- `;this[a].ccall("wasm_set_phpini_entries",null,[u],[e])}this[a].ccall("php_wasm_init",null,[],[])}#i(){const e="/tmp/headers.json";if(!this.fileExists(e))throw new Error("SAPI Error: Could not find response headers file.");const t=JSON.parse(this.readFileAsText(e)),r={};for(const n of t.headers){if(!n.includes(": "))continue;const o=n.indexOf(": "),i=n.substring(0,o).toLowerCase(),l=n.substring(o+2);i in r||(r[i]=[]),r[i].push(l)}return{headers:r,httpStatusCode:t.status}}#a(e){if(this[a].ccall("wasm_set_request_uri",null,[u],[e]),e.includes("?")){const t=e.substring(e.indexOf("?")+1);this[a].ccall("wasm_set_query_string",null,[u],[t])}}#l(e,t){this[a].ccall("wasm_set_request_host",null,[u],[e]);let r;try{r=parseInt(new URL(e).port,10)}catch{}(!r||isNaN(r)||r===80)&&(r=t==="https"?443:80),this[a].ccall("wasm_set_request_port",null,[g],[r]),(t==="https"||!t&&r===443)&&this.addServerGlobalEntry("HTTPS","on")}#c(e){this[a].ccall("wasm_set_request_method",null,[u],[e])}#h(e){e.cookie&&this[a].ccall("wasm_set_cookies",null,[u],[e.cookie]),e["content-type"]&&this[a].ccall("wasm_set_content_type",null,[u],[e["content-type"]]),e["content-length"]&&this[a].ccall("wasm_set_content_length",null,[g],[parseInt(e["content-length"],10)]);for(const t in e){let r="HTTP_";["content-type","content-length"].includes(t.toLowerCase())&&(r=""),this.addServerGlobalEntry(`${r}${t.toUpperCase().replace(/-/g,"_")}`,e[t])}}#u(e){this[a].ccall("wasm_set_request_body",null,[u],[e]),this[a].ccall("wasm_set_content_length",null,[g],[new TextEncoder().encode(e).length])}#d(e){this[a].ccall("wasm_set_path_translated",null,[u],[e])}addServerGlobalEntry(e,t){this.#r[e]=t}#p(){for(const e in this.#r)this[a].ccall("wasm_add_SERVER_entry",null,[u,u],[e,this.#r[e]])}#f(e){const{key:t,name:r,type:n,data:o}=e,i=`/tmp/${Math.random().toFixed(20)}`;this.writeFile(i,o);const l=0;this[a].ccall("wasm_add_uploaded_file",null,[u,u,u,u,g,g],[t,r,n,i,l,o.byteLength])}#m(e){this[a].ccall("wasm_set_php_code",null,[u],[e])}async#y(){let e,t;try{e=await new Promise((o,i)=>{t=h=>{const c=new Error("Rethrown");c.cause=h.error,c.betterMessage=h.message,i(c)},this.#s?.addEventListener("error",t);const l=this[a].ccall("wasm_sapi_handle_request",g,[],[]);return l instanceof Promise?l.then(o,i):o(l)})}catch(o){for(const c in this)typeof this[c]=="function"&&(this[c]=()=>{throw new Error("PHP runtime has crashed – see the earlier error for details.")});this.functionsMaybeMissingFromAsyncify=j();const i=o,l="betterMessage"in i?i.betterMessage:i.message,h=new Error(l);throw h.cause=i,h}finally{this.#s?.removeEventListener("error",t),this.#r={}}const{headers:r,httpStatusCode:n}=this.#i();return new y(n,r,this.readFileAsBuffer("/tmp/stdout"),this.readFileAsText("/tmp/stderr"),e)}mkdir(e){this[a].FS.mkdirTree(e)}mkdirTree(e){this.mkdir(e)}readFileAsText(e){return new TextDecoder().decode(this.readFileAsBuffer(e))}readFileAsBuffer(e){return this[a].FS.readFile(e)}writeFile(e,t){this[a].FS.writeFile(e,t)}unlink(e){this[a].FS.unlink(e)}mv(e,t){this[a].FS.rename(e,t)}rmdir(e,t={recursive:!0}){t?.recursive&&this.listFiles(e).forEach(r=>{const n=`${e}/${r}`;this.isDir(n)?this.rmdir(n,t):this.unlink(n)}),this[a].FS.rmdir(e)}listFiles(e,t={prependPath:!1}){if(!this.fileExists(e))return[];try{const r=this[a].FS.readdir(e).filter(n=>n!=="."&&n!=="..");if(t.prependPath){const n=e.replace(/\/$/,"");return r.map(o=>`${n}/${o}`)}return r}catch(r){return console.error(r,{path:e}),[]}}isDir(e){return this.fileExists(e)?this[a].FS.isDir(this[a].FS.lookupPath(e).node.mode):!1}fileExists(e){try{return this[a].FS.lookupPath(e),!0}catch{return!1}}}f([p('Could not create directory "{path}"')],d.prototype,"mkdir",1);f([p('Could not create directory "{path}"')],d.prototype,"mkdirTree",1);f([p('Could not read "{path}"')],d.prototype,"readFileAsText",1);f([p('Could not read "{path}"')],d.prototype,"readFileAsBuffer",1);f([p('Could not write to "{path}"')],d.prototype,"writeFile",1);f([p('Could not unlink "{path}"')],d.prototype,"unlink",1);f([p('Could not move "{path}"')],d.prototype,"mv",1);f([p('Could not remove directory "{path}"')],d.prototype,"rmdir",1);f([p('Could not list files in "{path}"')],d.prototype,"listFiles",1);f([p('Could not stat "{path}"')],d.prototype,"isDir",1);f([p('Could not stat "{path}"')],d.prototype,"fileExists",1);function $(s){const e={};for(const t in s)e[t.toLowerCase()]=s[t];return e}function B(s){return!(s instanceof d)}function oe(s){return!B(s)}exports.BasePHP=d;exports.DEFAULT_BASE_URL=I;exports.LatestSupportedPHPVersion=J;exports.PHPBrowser=O;exports.PHPRequestHandler=N;exports.PHPResponse=y;exports.SupportedPHPVersions=E;exports.SupportedPHPVersionsList=K;exports.UnhandledRejectionsTarget=A;exports.__private__dont__use=a;exports.ensurePathPrefix=L;exports.isExitCodeZero=U;exports.isLocalPHP=B;exports.isRemotePHP=oe;exports.loadPHPRuntime=ee;exports.removePathPrefix=b;exports.rethrowFileSystemError=p;exports.toRelativeUrl=P;
40
+ `;this[a].ccall("wasm_set_phpini_entries",null,[h],[e])}this[a].ccall("php_wasm_init",null,[],[])}#i(){const e="/tmp/headers.json";if(!this.fileExists(e))throw new Error("SAPI Error: Could not find response headers file.");const t=JSON.parse(this.readFileAsText(e)),r={};for(const n of t.headers){if(!n.includes(": "))continue;const o=n.indexOf(": "),i=n.substring(0,o).toLowerCase(),l=n.substring(o+2);i in r||(r[i]=[]),r[i].push(l)}return{headers:r,httpStatusCode:t.status}}#a(e){if(this[a].ccall("wasm_set_request_uri",null,[h],[e]),e.includes("?")){const t=e.substring(e.indexOf("?")+1);this[a].ccall("wasm_set_query_string",null,[h],[t])}}#l(e,t){this[a].ccall("wasm_set_request_host",null,[h],[e]);let r;try{r=parseInt(new URL(e).port,10)}catch{}(!r||isNaN(r)||r===80)&&(r=t==="https"?443:80),this[a].ccall("wasm_set_request_port",null,[g],[r]),(t==="https"||!t&&r===443)&&this.addServerGlobalEntry("HTTPS","on")}#c(e){this[a].ccall("wasm_set_request_method",null,[h],[e])}#u(e){e.cookie&&this[a].ccall("wasm_set_cookies",null,[h],[e.cookie]),e["content-type"]&&this[a].ccall("wasm_set_content_type",null,[h],[e["content-type"]]),e["content-length"]&&this[a].ccall("wasm_set_content_length",null,[g],[parseInt(e["content-length"],10)]);for(const t in e){let r="HTTP_";["content-type","content-length"].includes(t.toLowerCase())&&(r=""),this.addServerGlobalEntry(`${r}${t.toUpperCase().replace(/-/g,"_")}`,e[t])}}#h(e){this[a].ccall("wasm_set_request_body",null,[h],[e]),this[a].ccall("wasm_set_content_length",null,[g],[new TextEncoder().encode(e).length])}#d(e){this[a].ccall("wasm_set_path_translated",null,[h],[e])}addServerGlobalEntry(e,t){this.#r[e]=t}#p(){for(const e in this.#r)this[a].ccall("wasm_add_SERVER_entry",null,[h,h],[e,this.#r[e]])}#f(e){const{key:t,name:r,type:n,data:o}=e,i=`/tmp/${Math.random().toFixed(20)}`;this.writeFile(i,o);const l=0;this[a].ccall("wasm_add_uploaded_file",null,[h,h,h,h,g,g],[t,r,n,i,l,o.byteLength])}#m(e){this[a].ccall("wasm_set_php_code",null,[h],[e])}async#y(){let e,t;try{e=await new Promise((o,i)=>{t=u=>{const c=new Error("Rethrown");c.cause=u.error,c.betterMessage=u.message,i(c)},this.#s?.addEventListener("error",t);const l=this[a].ccall("wasm_sapi_handle_request",g,[],[]);return l instanceof Promise?l.then(o,i):o(l)})}catch(o){for(const c in this)typeof this[c]=="function"&&(this[c]=()=>{throw new Error("PHP runtime has crashed – see the earlier error for details.")});this.functionsMaybeMissingFromAsyncify=j();const i=o,l="betterMessage"in i?i.betterMessage:i.message,u=new Error(l);throw u.cause=i,u}finally{this.#s?.removeEventListener("error",t),this.#r={}}const{headers:r,httpStatusCode:n}=this.#i();return new y(n,r,this.readFileAsBuffer("/tmp/stdout"),this.readFileAsText("/tmp/stderr"),e)}mkdir(e){this[a].FS.mkdirTree(e)}mkdirTree(e){this.mkdir(e)}readFileAsText(e){return new TextDecoder().decode(this.readFileAsBuffer(e))}readFileAsBuffer(e){return this[a].FS.readFile(e)}writeFile(e,t){this[a].FS.writeFile(e,t)}unlink(e){this[a].FS.unlink(e)}mv(e,t){this[a].FS.rename(e,t)}rmdir(e,t={recursive:!0}){t?.recursive&&this.listFiles(e).forEach(r=>{const n=`${e}/${r}`;this.isDir(n)?this.rmdir(n,t):this.unlink(n)}),this[a].FS.rmdir(e)}listFiles(e,t={prependPath:!1}){if(!this.fileExists(e))return[];try{const r=this[a].FS.readdir(e).filter(n=>n!=="."&&n!=="..");if(t.prependPath){const n=e.replace(/\/$/,"");return r.map(o=>`${n}/${o}`)}return r}catch(r){return console.error(r,{path:e}),[]}}isDir(e){return this.fileExists(e)?this[a].FS.isDir(this[a].FS.lookupPath(e).node.mode):!1}fileExists(e){try{return this[a].FS.lookupPath(e),!0}catch{return!1}}}f([p('Could not create directory "{path}"')],d.prototype,"mkdir",1);f([p('Could not create directory "{path}"')],d.prototype,"mkdirTree",1);f([p('Could not read "{path}"')],d.prototype,"readFileAsText",1);f([p('Could not read "{path}"')],d.prototype,"readFileAsBuffer",1);f([p('Could not write to "{path}"')],d.prototype,"writeFile",1);f([p('Could not unlink "{path}"')],d.prototype,"unlink",1);f([p('Could not move "{path}"')],d.prototype,"mv",1);f([p('Could not remove directory "{path}"')],d.prototype,"rmdir",1);f([p('Could not list files in "{path}"')],d.prototype,"listFiles",1);f([p('Could not stat "{path}"')],d.prototype,"isDir",1);f([p('Could not stat "{path}"')],d.prototype,"fileExists",1);function B(s){const e={};for(const t in s)e[t.toLowerCase()]=s[t];return e}function M(s){return!(s instanceof d)}function ue(s){return!M(s)}exports.BasePHP=d;exports.DEFAULT_BASE_URL=I;exports.LatestSupportedPHPVersion=K;exports.PHPBrowser=O;exports.PHPRequestHandler=$;exports.PHPResponse=y;exports.SupportedPHPExtensionBundles=Q;exports.SupportedPHPExtensionsList=L;exports.SupportedPHPVersions=v;exports.SupportedPHPVersionsList=Z;exports.UnhandledRejectionsTarget=U;exports.__private__dont__use=a;exports.ensurePathPrefix=N;exports.isExitCodeZero=A;exports.isLocalPHP=M;exports.isRemotePHP=ue;exports.loadPHPRuntime=oe;exports.removePathPrefix=b;exports.rethrowFileSystemError=p;exports.toRelativeUrl=w;
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const v = Symbol("error"), _ = Symbol("message");
1
+ const v = Symbol("error"), E = Symbol("message");
2
2
  class b extends Event {
3
3
  /**
4
4
  * Create a new `ErrorEvent`.
@@ -8,22 +8,22 @@ class b extends Event {
8
8
  * attributes via object members of the same name.
9
9
  */
10
10
  constructor(e, t = {}) {
11
- super(e), this[v] = t.error === void 0 ? null : t.error, this[_] = t.message === void 0 ? "" : t.message;
11
+ super(e), this[v] = t.error === void 0 ? null : t.error, this[E] = t.message === void 0 ? "" : t.message;
12
12
  }
13
13
  get error() {
14
14
  return this[v];
15
15
  }
16
16
  get message() {
17
- return this[_];
17
+ return this[E];
18
18
  }
19
19
  }
20
20
  Object.defineProperty(b.prototype, "error", { enumerable: !0 });
21
21
  Object.defineProperty(b.prototype, "message", { enumerable: !0 });
22
- const A = typeof globalThis.ErrorEvent == "function" ? globalThis.ErrorEvent : b;
22
+ const U = typeof globalThis.ErrorEvent == "function" ? globalThis.ErrorEvent : b;
23
23
  function I(s) {
24
24
  return s instanceof Error ? "exitCode" in s && s?.exitCode === 0 || s?.name === "ExitStatus" && "status" in s && s.status === 0 : !1;
25
25
  }
26
- class N extends EventTarget {
26
+ class L extends EventTarget {
27
27
  constructor() {
28
28
  super(...arguments), this.listenersCount = 0;
29
29
  }
@@ -37,11 +37,11 @@ class N extends EventTarget {
37
37
  return this.listenersCount > 0;
38
38
  }
39
39
  }
40
- function $(s) {
40
+ function N(s) {
41
41
  s.asm = {
42
42
  ...s.asm
43
43
  };
44
- const e = new N();
44
+ const e = new L();
45
45
  for (const t in s.asm)
46
46
  if (typeof s.asm[t] == "function") {
47
47
  const r = s.asm[t];
@@ -57,26 +57,26 @@ function $(s) {
57
57
  );
58
58
  if (s.lastAsyncifyStackSource && (o.cause = s.lastAsyncifyStackSource), e.hasListeners()) {
59
59
  e.dispatchEvent(
60
- new A("error", {
60
+ new U("error", {
61
61
  error: o,
62
62
  message: i
63
63
  })
64
64
  );
65
65
  return;
66
66
  }
67
- throw I(o) || q(i), o;
67
+ throw I(o) || D(i), o;
68
68
  }
69
69
  };
70
70
  }
71
71
  return e;
72
72
  }
73
73
  let w = [];
74
- function L() {
74
+ function $() {
75
75
  return w;
76
76
  }
77
77
  function M(s, e) {
78
78
  if (s.message === "unreachable") {
79
- let t = B;
79
+ let t = q;
80
80
  e || (t += `
81
81
 
82
82
  This stack trace is lacking. For a better one initialize
@@ -92,7 +92,7 @@ the PHP runtime with { debug: true }, e.g. PHPNode.load('8.1', { debug: true }).
92
92
  }
93
93
  return s.message;
94
94
  }
95
- const B = `
95
+ const q = `
96
96
  "unreachable" WASM instruction executed.
97
97
 
98
98
  The typical reason is a PHP function missing from the ASYNCIFY_ONLY
@@ -116,16 +116,16 @@ the Dockerfile, you'll need to trigger this error again with long stack
116
116
  traces enabled. In node.js, you can do it using the --stack-trace-limit=100
117
117
  CLI option:
118
118
 
119
- `, E = "\x1B[41m", D = "\x1B[1m", R = "\x1B[0m", S = "\x1B[K";
120
- let x = !1;
121
- function q(s) {
122
- if (!x) {
123
- x = !0, console.log(`${E}
124
- ${S}
125
- ${D} WASM ERROR${R}${E}`);
119
+ `, _ = "\x1B[41m", B = "\x1B[1m", R = "\x1B[0m", x = "\x1B[K";
120
+ let S = !1;
121
+ function D(s) {
122
+ if (!S) {
123
+ S = !0, console.log(`${_}
124
+ ${x}
125
+ ${B} WASM ERROR${R}${_}`);
126
126
  for (const e of s.split(`
127
127
  `))
128
- console.log(`${S} ${e} `);
128
+ console.log(`${x} ${e} `);
129
129
  console.log(`${R}`);
130
130
  }
131
131
  }
@@ -181,7 +181,7 @@ class g {
181
181
  return new TextDecoder().decode(this.bytes);
182
182
  }
183
183
  }
184
- const H = [
184
+ const F = [
185
185
  "8.2",
186
186
  "8.1",
187
187
  "8.0",
@@ -191,8 +191,15 @@ const H = [
191
191
  "7.1",
192
192
  "7.0",
193
193
  "5.6"
194
- ], re = H[0], se = H;
195
- class j {
194
+ ], ie = F[0], ae = F, j = [
195
+ "iconv",
196
+ "mbstring",
197
+ "xml-bundle",
198
+ "gd"
199
+ ], le = {
200
+ "kitchen-sink": j
201
+ };
202
+ class z {
196
203
  #e;
197
204
  #t;
198
205
  /**
@@ -278,7 +285,7 @@ class j {
278
285
  return e.join("; ");
279
286
  }
280
287
  }
281
- class z {
288
+ class G {
282
289
  constructor({ concurrency: e }) {
283
290
  this._running = 0, this.concurrency = e, this.queue = [];
284
291
  }
@@ -306,17 +313,17 @@ class z {
306
313
  }
307
314
  }
308
315
  }
309
- const G = "http://example.com";
310
- function T(s) {
316
+ const V = "http://example.com";
317
+ function k(s) {
311
318
  return s.toString().substring(s.origin.length);
312
319
  }
313
- function k(s, e) {
320
+ function H(s, e) {
314
321
  return !e || !s.startsWith(e) ? s : s.substring(e.length);
315
322
  }
316
- function V(s, e) {
323
+ function Y(s, e) {
317
324
  return !e || s.startsWith(e) ? s : e + s;
318
325
  }
319
- class Y {
326
+ class J {
320
327
  #e;
321
328
  #t;
322
329
  #s;
@@ -325,26 +332,24 @@ class Y {
325
332
  #n;
326
333
  #i;
327
334
  #a;
328
- #l;
329
335
  /**
330
336
  * @param php - The PHP instance.
331
337
  * @param config - Request Handler configuration.
332
338
  */
333
339
  constructor(e, t = {}) {
334
- this.#a = new z({ concurrency: 1 });
340
+ this.#a = new G({ concurrency: 1 });
335
341
  const {
336
342
  documentRoot: r = "/www/",
337
- absoluteUrl: n = typeof location == "object" ? location?.href : "",
338
- isStaticFilePath: o = () => !1
343
+ absoluteUrl: n = typeof location == "object" ? location?.href : ""
339
344
  } = t;
340
- this.php = e, this.#e = r, this.#l = o;
341
- const i = new URL(n);
342
- this.#s = i.hostname, this.#r = i.port ? Number(i.port) : i.protocol === "https:" ? 443 : 80, this.#t = (i.protocol || "").replace(":", "");
343
- const l = this.#r !== 443 && this.#r !== 80;
345
+ this.php = e, this.#e = r;
346
+ const o = new URL(n);
347
+ this.#s = o.hostname, this.#r = o.port ? Number(o.port) : o.protocol === "https:" ? 443 : 80, this.#t = (o.protocol || "").replace(":", "");
348
+ const i = this.#r !== 443 && this.#r !== 80;
344
349
  this.#o = [
345
350
  this.#s,
346
- l ? `:${this.#r}` : ""
347
- ].join(""), this.#n = i.pathname.replace(/\/+$/, ""), this.#i = [
351
+ i ? `:${this.#r}` : ""
352
+ ].join(""), this.#n = o.pathname.replace(/\/+$/, ""), this.#i = [
348
353
  `${this.#t}://`,
349
354
  this.#o,
350
355
  this.#n
@@ -357,7 +362,7 @@ class Y {
357
362
  /** @inheritDoc */
358
363
  internalUrlToPath(e) {
359
364
  const t = new URL(e);
360
- return t.pathname.startsWith(this.#n) && (t.pathname = t.pathname.slice(this.#n.length)), T(t);
365
+ return t.pathname.startsWith(this.#n) && (t.pathname = t.pathname.slice(this.#n.length)), k(t);
361
366
  }
362
367
  get isRequestRunning() {
363
368
  return this.#a.running > 0;
@@ -374,40 +379,43 @@ class Y {
374
379
  async request(e) {
375
380
  const t = e.url.startsWith("http://") || e.url.startsWith("https://"), r = new URL(
376
381
  e.url,
377
- t ? void 0 : G
378
- ), n = k(
382
+ t ? void 0 : V
383
+ ), n = H(
379
384
  r.pathname,
380
385
  this.#n
381
- );
382
- return this.#l(n) ? this.#c(n) : await this.#h(e, r);
386
+ ), o = `${this.#e}${n}`;
387
+ return Q(o) ? await this.#c(e, r) : this.#l(o);
383
388
  }
384
389
  /**
385
390
  * Serves a static file from the PHP filesystem.
386
391
  *
387
- * @param path - The requested static file path.
392
+ * @param fsPath - Absolute path of the static file to serve.
388
393
  * @returns The response.
389
394
  */
390
- #c(e) {
391
- const t = `${this.#e}${e}`;
392
- if (!this.php.fileExists(t))
395
+ #l(e) {
396
+ if (!this.php.fileExists(e))
393
397
  return new g(
394
398
  404,
395
- {},
399
+ // Let the service worker know that no static file was found
400
+ // and that it's okay to issue a real fetch() to the server.
401
+ {
402
+ "x-file-type": ["static"]
403
+ },
396
404
  new TextEncoder().encode("404 File not found")
397
405
  );
398
- const r = this.php.readFileAsBuffer(t);
406
+ const t = this.php.readFileAsBuffer(e);
399
407
  return new g(
400
408
  200,
401
409
  {
402
- "content-length": [`${r.byteLength}`],
410
+ "content-length": [`${t.byteLength}`],
403
411
  // @TODO: Infer the content-type from the arrayBuffer instead of the file path.
404
412
  // The code below won't return the correct mime-type if the extension
405
413
  // was tampered with.
406
- "content-type": [K(t)],
414
+ "content-type": [Z(e)],
407
415
  "accept-ranges": ["bytes"],
408
416
  "cache-control": ["public, max-age=0"]
409
417
  },
410
- r
418
+ t
411
419
  );
412
420
  }
413
421
  /**
@@ -417,7 +425,7 @@ class Y {
417
425
  * @param request - The request.
418
426
  * @returns The response.
419
427
  */
420
- async #h(e, t) {
428
+ async #c(e, t) {
421
429
  const r = await this.#a.acquire();
422
430
  try {
423
431
  this.php.addServerGlobalEntry("DOCUMENT_ROOT", this.#e), this.php.addServerGlobalEntry(
@@ -427,7 +435,7 @@ class Y {
427
435
  let n = "GET";
428
436
  const o = {
429
437
  host: this.#o,
430
- ...U(e.headers || {})
438
+ ...A(e.headers || {})
431
439
  }, i = [];
432
440
  if (e.files && Object.keys(e.files).length) {
433
441
  n = "POST";
@@ -440,7 +448,7 @@ class Y {
440
448
  data: new Uint8Array(await m.arrayBuffer())
441
449
  });
442
450
  }
443
- o["content-type"]?.startsWith("multipart/form-data") && (e.formData = J(
451
+ o["content-type"]?.startsWith("multipart/form-data") && (e.formData = K(
444
452
  e.body || ""
445
453
  ), o["content-type"] = "application/x-www-form-urlencoded", delete e.body);
446
454
  }
@@ -450,7 +458,7 @@ class Y {
450
458
  ).toString()) : l = e.body;
451
459
  let h;
452
460
  try {
453
- h = this.#u(t.pathname);
461
+ h = this.#h(t.pathname);
454
462
  } catch {
455
463
  return new g(
456
464
  404,
@@ -459,8 +467,8 @@ class Y {
459
467
  );
460
468
  }
461
469
  return await this.php.run({
462
- relativeUri: V(
463
- T(t),
470
+ relativeUri: Y(
471
+ k(t),
464
472
  this.#n
465
473
  ),
466
474
  protocol: this.#t,
@@ -483,8 +491,8 @@ class Y {
483
491
  * @throws {Error} If the requested path doesn't exist.
484
492
  * @returns The resolved filesystem path.
485
493
  */
486
- #u(e) {
487
- let t = k(e, this.#n);
494
+ #h(e) {
495
+ let t = H(e, this.#n);
488
496
  t.includes(".php") ? t = t.split(".php")[0] + ".php" : (t.endsWith("/") || (t += "/"), t.endsWith("index.php") || (t += "index.php"));
489
497
  const r = `${this.#e}${t}`;
490
498
  if (this.php.fileExists(r))
@@ -494,7 +502,7 @@ class Y {
494
502
  return `${this.#e}/index.php`;
495
503
  }
496
504
  }
497
- function J(s) {
505
+ function K(s) {
498
506
  const e = {}, t = s.match(/--(.*)\r\n/);
499
507
  if (!t)
500
508
  return e;
@@ -509,7 +517,7 @@ function J(s) {
509
517
  }
510
518
  }), e;
511
519
  }
512
- function K(s) {
520
+ function Z(s) {
513
521
  switch (s.split(".").pop()) {
514
522
  case "css":
515
523
  return "text/css";
@@ -549,7 +557,16 @@ function K(s) {
549
557
  return "application-octet-stream";
550
558
  }
551
559
  }
552
- const C = {
560
+ function Q(s) {
561
+ return X(s) || ee(s);
562
+ }
563
+ function X(s) {
564
+ return s.endsWith(".php") || s.includes(".php/");
565
+ }
566
+ function ee(s) {
567
+ return !s.split("/").pop().includes(".");
568
+ }
569
+ const T = {
553
570
  0: "No error occurred. System call completed successfully.",
554
571
  1: "Argument list too long.",
555
572
  2: "Permission denied.",
@@ -636,8 +653,8 @@ function p(s = "") {
636
653
  return o.apply(this, i);
637
654
  } catch (l) {
638
655
  const h = typeof l == "object" ? l?.errno : null;
639
- if (h in C) {
640
- const c = C[h], m = typeof i[0] == "string" ? i[0] : null, O = m !== null ? s.replaceAll("{path}", m) : s;
656
+ if (h in T) {
657
+ const c = T[h], m = typeof i[0] == "string" ? i[0] : null, O = m !== null ? s.replaceAll("{path}", m) : s;
641
658
  throw new Error(`${O}: ${c}`, {
642
659
  cause: l
643
660
  });
@@ -647,8 +664,8 @@ function p(s = "") {
647
664
  };
648
665
  };
649
666
  }
650
- async function ne(s, e = {}, t = []) {
651
- const [r, n, o] = F(), [i, l] = F(), h = s.init(Q, {
667
+ async function ce(s, e = {}, t = []) {
668
+ const [r, n, o] = C(), [i, l] = C(), h = s.init(re, {
652
669
  onAbort(c) {
653
670
  o(c), l(), console.error(c);
654
671
  },
@@ -673,21 +690,21 @@ async function ne(s, e = {}, t = []) {
673
690
  ), t.length || l(), await i, await r, P.push(h), P.length - 1;
674
691
  }
675
692
  const P = [];
676
- function Z(s) {
693
+ function te(s) {
677
694
  return P[s];
678
695
  }
679
- const Q = function() {
696
+ const re = function() {
680
697
  return typeof process < "u" && process.release?.name === "node" ? "NODE" : typeof window < "u" ? "WEB" : typeof WorkerGlobalScope < "u" && self instanceof WorkerGlobalScope ? "WORKER" : "NODE";
681
- }(), F = () => {
698
+ }(), C = () => {
682
699
  const s = [], e = new Promise((t, r) => {
683
700
  s.push(t, r);
684
701
  });
685
702
  return s.unshift(e), s;
686
703
  };
687
- var X = Object.defineProperty, ee = Object.getOwnPropertyDescriptor, f = (s, e, t, r) => {
688
- for (var n = r > 1 ? void 0 : r ? ee(e, t) : e, o = s.length - 1, i; o >= 0; o--)
704
+ var se = Object.defineProperty, ne = Object.getOwnPropertyDescriptor, f = (s, e, t, r) => {
705
+ for (var n = r > 1 ? void 0 : r ? ne(e, t) : e, o = s.length - 1, i; o >= 0; o--)
689
706
  (i = s[o]) && (n = (r ? i(e, t, n) : i(n)) || n);
690
- return r && n && X(e, t, n), n;
707
+ return r && n && se(e, t, n), n;
691
708
  };
692
709
  const u = "string", y = "number", a = Symbol("__private__dont__use");
693
710
  class d {
@@ -699,8 +716,8 @@ class d {
699
716
  * @param serverOptions - Optional. Options for the PHPRequestHandler. If undefined, no request handler will be initialized.
700
717
  */
701
718
  constructor(e, t) {
702
- this.#e = [], this.#t = !1, this.#s = null, this.#r = {}, this.#o = [], e !== void 0 && this.initializeRuntime(e), t && (this.requestHandler = new j(
703
- new Y(this, t)
719
+ this.#e = [], this.#t = !1, this.#s = null, this.#r = {}, this.#o = [], e !== void 0 && this.initializeRuntime(e), t && (this.requestHandler = new z(
720
+ new J(this, t)
704
721
  ));
705
722
  }
706
723
  #e;
@@ -733,13 +750,13 @@ class d {
733
750
  initializeRuntime(e) {
734
751
  if (this[a])
735
752
  throw new Error("PHP runtime already initialized.");
736
- const t = Z(e);
753
+ const t = te(e);
737
754
  if (!t)
738
755
  throw new Error("Invalid PHP runtime id.");
739
756
  this[a] = t, t.onMessage = (r) => {
740
757
  for (const n of this.#o)
741
758
  n(r);
742
- }, this.#s = $(t);
759
+ }, this.#s = N(t);
743
760
  }
744
761
  /** @inheritDoc */
745
762
  setPhpIniPath(e) {
@@ -773,7 +790,7 @@ class d {
773
790
  this.#t || (this.#n(), this.#t = !0), this.#d(e.scriptPath || ""), this.#a(e.relativeUri || ""), this.#c(e.method || "GET");
774
791
  const { host: t, ...r } = {
775
792
  host: "example.com:443",
776
- ...U(e.headers || {})
793
+ ...A(e.headers || {})
777
794
  };
778
795
  if (this.#l(t, e.protocol || "http"), this.#h(r), e.body && this.#u(e.body), e.fileInfos)
779
796
  for (const n of e.fileInfos)
@@ -968,7 +985,7 @@ class d {
968
985
  "PHP runtime has crashed – see the earlier error for details."
969
986
  );
970
987
  });
971
- this.functionsMaybeMissingFromAsyncify = L();
988
+ this.functionsMaybeMissingFromAsyncify = $();
972
989
  const i = o, l = "betterMessage" in i ? i.betterMessage : i.message, h = new Error(l);
973
990
  throw h.cause = i, h;
974
991
  } finally {
@@ -1072,35 +1089,37 @@ f([
1072
1089
  f([
1073
1090
  p('Could not stat "{path}"')
1074
1091
  ], d.prototype, "fileExists", 1);
1075
- function U(s) {
1092
+ function A(s) {
1076
1093
  const e = {};
1077
1094
  for (const t in s)
1078
1095
  e[t.toLowerCase()] = s[t];
1079
1096
  return e;
1080
1097
  }
1081
- function te(s) {
1098
+ function oe(s) {
1082
1099
  return !(s instanceof d);
1083
1100
  }
1084
- function oe(s) {
1085
- return !te(s);
1101
+ function he(s) {
1102
+ return !oe(s);
1086
1103
  }
1087
1104
  export {
1088
1105
  d as BasePHP,
1089
- G as DEFAULT_BASE_URL,
1090
- re as LatestSupportedPHPVersion,
1091
- j as PHPBrowser,
1092
- Y as PHPRequestHandler,
1106
+ V as DEFAULT_BASE_URL,
1107
+ ie as LatestSupportedPHPVersion,
1108
+ z as PHPBrowser,
1109
+ J as PHPRequestHandler,
1093
1110
  g as PHPResponse,
1094
- H as SupportedPHPVersions,
1095
- se as SupportedPHPVersionsList,
1096
- N as UnhandledRejectionsTarget,
1111
+ le as SupportedPHPExtensionBundles,
1112
+ j as SupportedPHPExtensionsList,
1113
+ F as SupportedPHPVersions,
1114
+ ae as SupportedPHPVersionsList,
1115
+ L as UnhandledRejectionsTarget,
1097
1116
  a as __private__dont__use,
1098
- V as ensurePathPrefix,
1117
+ Y as ensurePathPrefix,
1099
1118
  I as isExitCodeZero,
1100
- te as isLocalPHP,
1101
- oe as isRemotePHP,
1102
- ne as loadPHPRuntime,
1103
- k as removePathPrefix,
1119
+ oe as isLocalPHP,
1120
+ he as isRemotePHP,
1121
+ ce as loadPHPRuntime,
1122
+ H as removePathPrefix,
1104
1123
  p as rethrowFileSystemError,
1105
- T as toRelativeUrl
1124
+ k as toRelativeUrl
1106
1125
  };
package/lib/index.d.ts CHANGED
@@ -5,6 +5,8 @@ export type { PHPResponseData } from './php-response';
5
5
  export type { ErrnoError } from './rethrow-file-system-error';
6
6
  export { LatestSupportedPHPVersion, SupportedPHPVersions, SupportedPHPVersionsList, } from './supported-php-versions';
7
7
  export type { SupportedPHPVersion } from './supported-php-versions';
8
+ export { SupportedPHPExtensionsList, SupportedPHPExtensionBundles, } from './supported-php-extensions';
9
+ export type { SupportedPHPExtension, SupportedPHPExtensionBundle, } from './supported-php-extensions';
8
10
  export { BasePHP, __private__dont__use } from './base-php';
9
11
  export { loadPHPRuntime } from './load-php-runtime';
10
12
  export type { DataModule, EmscriptenOptions, PHPLoaderModule, PHPRuntime, PHPRuntimeId, RuntimeType, } from './load-php-runtime';
@@ -11,11 +11,6 @@ export interface PHPRequestHandlerConfiguration {
11
11
  * Request Handler URL. Used to populate $_SERVER details like HTTP_HOST.
12
12
  */
13
13
  absoluteUrl?: string;
14
- /**
15
- * Callback used by the PHPRequestHandler to decide whether
16
- * the requested path refers to a PHP file or a static file.
17
- */
18
- isStaticFilePath?: (path: string) => boolean;
19
14
  }
20
15
  /** @inheritDoc */
21
16
  export declare class PHPRequestHandler implements RequestHandler {
@@ -41,3 +36,20 @@ export declare class PHPRequestHandler implements RequestHandler {
41
36
  /** @inheritDoc */
42
37
  request(request: PHPRequest): Promise<PHPResponse>;
43
38
  }
39
+ /**
40
+ * Guesses whether the given path looks like a PHP file.
41
+ *
42
+ * @example
43
+ * ```js
44
+ * seemsLikeAPHPRequestHandlerPath('/index.php') // true
45
+ * seemsLikeAPHPRequestHandlerPath('/index.php') // true
46
+ * seemsLikeAPHPRequestHandlerPath('/index.php/foo/bar') // true
47
+ * seemsLikeAPHPRequestHandlerPath('/index.html') // false
48
+ * seemsLikeAPHPRequestHandlerPath('/index.html/foo/bar') // false
49
+ * seemsLikeAPHPRequestHandlerPath('/') // true
50
+ * ```
51
+ *
52
+ * @param path The path to check.
53
+ * @returns Whether the path seems like a PHP server path.
54
+ */
55
+ export declare function seemsLikeAPHPRequestHandlerPath(path: string): boolean;
@@ -0,0 +1,6 @@
1
+ export type SupportedPHPExtension = 'iconv' | 'mbstring' | 'xml-bundle' | 'gd';
2
+ export declare const SupportedPHPExtensionsList: string[];
3
+ export declare const SupportedPHPExtensionBundles: {
4
+ 'kitchen-sink': string[];
5
+ };
6
+ export type SupportedPHPExtensionBundle = 'kitchen-sink';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@php-wasm/universal",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "PHP.wasm – emscripten bindings for PHP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,7 +36,7 @@
36
36
  "main": "./index.cjs",
37
37
  "module": "./index.js",
38
38
  "license": "GPL-2.0-or-later",
39
- "gitHead": "cd4062de74f7f18058d3f962e124d0fdff78b5a7",
39
+ "gitHead": "73b285f8b496c7442474fd7325b3f6b1287cbf95",
40
40
  "engines": {
41
41
  "node": ">=16.15.1",
42
42
  "npm": ">=8.11.0"