@quran-lab/tajweedformer 0.0.0-stage → 2.0.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/CHANGELOG.md +39 -0
- package/LICENSE +148 -0
- package/README.md +121 -2
- package/dist/tajweedformer-opus.mjs +2 -0
- package/dist/tajweedformer-opus.wasm +0 -0
- package/dist/tajweedformer-scorer.mjs +2 -0
- package/dist/tajweedformer-scorer.wasm +0 -0
- package/dist/tajweedformer.mjs +2 -0
- package/dist/tajweedformer.wasm +0 -0
- package/docs/api.md +167 -0
- package/examples/browser.html +69 -0
- package/examples/node.mjs +37 -0
- package/package.json +81 -6
- package/web/consent/copy.js +51 -0
- package/web/consent/decks.js +470 -0
- package/web/consent/element.js +170 -0
- package/web/consent/index.d.ts +128 -0
- package/web/consent/index.js +239 -0
- package/web/consent/state.js +162 -0
- package/web/consent/theme.js +31 -0
- package/web/consent/wording.js +126 -0
- package/web/index.d.ts +4 -0
- package/web/index.js +10 -0
- package/web/platform/client.js +484 -0
- package/web/platform/contributor-key.js +112 -0
- package/web/platform/index.d.ts +148 -0
- package/web/platform/index.js +8 -0
- package/web/platform/storage.js +50 -0
- package/web/platform/telemetry.js +314 -0
- package/web/platform/update-notice.js +60 -0
- package/web/scorer.d.ts +4 -0
- package/web/scorer.js +10 -0
- package/web/sharing/bitrate.js +69 -0
- package/web/sharing/encoder.js +176 -0
- package/web/sharing/index.d.ts +262 -0
- package/web/sharing/index.js +287 -0
- package/web/sharing/oggopus.js +325 -0
- package/web/sharing/outbox.js +670 -0
- package/web/sharing/resampler.js +122 -0
- package/web/sharing/uploader.js +178 -0
- package/web/tajweed/decode.js +213 -0
- package/web/tajweed/delivery.js +317 -0
- package/web/tajweed/engine.js +600 -0
- package/web/tajweed/heads.js +73 -0
- package/web/tajweed/index.d.ts +737 -0
- package/web/tajweed/index.js +12 -0
- package/web/tajweed/manifest.js +452 -0
- package/web/tajweed/node.d.ts +3 -0
- package/web/tajweed/node.js +59 -0
- package/web/tajweed/settings.js +483 -0
- package/web/tajweed/tajweedformer.js +369 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
async function createTajweedFormer(moduleArg={}){var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var programArgs=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}programArgs=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var runtimeInitialized=false;function getMemoryBuffer(){return wasmMemory.buffer}function updateMemoryViews(){if(HEAP8?.buffer?.resizable)return;var b=getMemoryBuffer();HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b)}function preRun(){var preRun=Module["preRun"];if(preRun){if(typeof preRun=="function")preRun=[preRun];onPreRuns.push(...preRun)}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["l"]();FS.ignorePermissions=false}function postRun(){var postRun=Module["postRun"];if(postRun){if(typeof postRun=="function")postRun=[postRun];onPostRuns.push(...postRun)}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("tajweedformer.wasm")}return new URL("tajweedformer.wasm",import.meta.url).href}function getBinarySync(file){if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance){wasmExports=instance.exports;wasmExports=applySignatureConversions(wasmExports);assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();var instantiateWasm=Module["instantiateWasm"];if(instantiateWasm){return new Promise(resolve=>{instantiateWasm(info,inst=>resolve(receiveInstance(inst)))})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var onPreRuns=[];var noExitRuntime=true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var HEAP32;var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>>2>>>0];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>(nodeCrypto.randomFillSync(view),0)}return view=>(crypto.getRandomValues(view),0)};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start<arr.length;start++){if(arr[start]!=="")break}var end=arr.length-1;for(;end>=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i<length;i++){if(fromParts[i]!==toParts[i]){samePartsLength=i;break}}var outputParts=[];for(var i=samePartsLength;i<fromParts.length;i++){outputParts.push("..")}outputParts=outputParts.concat(toParts.slice(samePartsLength));return outputParts.join("/")}};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{idx>>>=0;var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{outIdx>>>=0;if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++>>>0]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++>>>0]=192|u>>6;heap[outIdx++>>>0]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++>>>0]=224|u>>12;heap[outIdx++>>>0]=128|u>>6&63;heap[outIdx++>>>0]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++>>>0]=240|u>>18;heap[outIdx++>>>0]=128|u>>12&63;heap[outIdx++>>>0]=128|u>>6&63;heap[outIdx++>>>0]=128|u&63;i++}}heap[outIdx>>>0]=0;return outIdx-startIdx};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;try{bytesRead=fs.readSync(process.stdin.fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.toString("utf-8",0,bytesRead)}}else if(globalThis.window?.prompt){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=stream.tty.ops.get_char(stream.tty)}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&!bytesRead){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result;if(result===10)break}if(bytesRead){stream.node.atime=Date.now()}return bytesRead},write(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.put_char){throw new FS.ErrnoError(60)}try{for(var i=0;i<length;i++){stream.tty.ops.put_char(stream.tty,buffer[offset+i])}}catch(e){throw new FS.ErrnoError(29)}if(length){stream.node.mtime=stream.node.ctime=Date.now()}return i}},default_tty_ops:{get_char(tty){return FS_stdin_getChar()},put_char(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var mmapAlloc=size=>{abort()};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=MEMFS.emptyFileContents??=new Uint8Array(0)}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){return node.contents.subarray(0,node.usedBytes)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents.length;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity<CAPACITY_DOUBLING_MAX?2:1.125)>>>0);if(prevCapacity)newCapacity=Math.max(newCapacity,256);var oldContents=MEMFS.getFileDataAsTypedArray(node);node.contents=new Uint8Array(newCapacity);node.contents.set(oldContents)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;var oldContents=node.contents;node.contents=new Uint8Array(newSize);node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)));node.usedBytes=newSize},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"]){if(attr[key]!=null){node[key]=attr[key]}}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){if(!MEMFS.doesNotExistError){MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack="<generic error, no stack>"}throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){if(FS.isDir(old_node.mode)){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}FS.hashRemoveNode(new_node)}delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);buffer.set(contents.subarray(position,position+size),offset);return size},write(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===HEAP8.buffer){canOwn=false}if(!length)return 0;var node=stream.node;node.mtime=node.ctime=Date.now();if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length}else if(!node.usedBytes&&!position){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length}else{MEMFS.expandFileStorage(node,position+length);node.contents.set(buffer.subarray(offset,offset+length),position);node.usedBytes=Math.max(node.usedBytes,position+length)}return length},llseek(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.usedBytes}}if(position<0){throw new FS.ErrnoError(28)}return position},mmap(stream,length,position,prot,flags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===HEAP8.buffer){allocated=false;ptr=contents.byteOffset}else{allocated=true;ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}if(contents){if(position>0||position+length<contents.length){if(contents.subarray){contents=contents.subarray(position,position+length)}else{contents=Array.prototype.slice.call(contents,position,position+length)}}HEAP8.set(contents,ptr>>>0)}}return{ptr,allocated}},msync(stream,buffer,offset,length,mmapFlags){MEMFS.stream_ops.write(stream,buffer,0,length,offset,false);return 0}}};var FS_modeStringToFlags=str=>{if(typeof str!="string")return str;var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_fileDataToTypedArray=data=>{if(typeof data=="string"){data=intArrayFromString(data,true)}if(!data.subarray){data=new Uint8Array(data)}return data};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var asyncLoad=async url=>{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var dependenciesPromise=null;var resolveRunDependencies=async()=>dependenciesPromise;var runDependencies=0;var dependenciesPromiseResolve=null;var removeRunDependency=id=>{runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(!runDependencies){dependenciesPromiseResolve()}};var addRunDependency=id=>{if(!runDependencies){dependenciesPromise=new Promise(resolve=>dependenciesPromiseResolve=resolve)}runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)};var preloadPlugins=[];var FS_handledByPreloadPlugin=async(byteArray,fullname)=>{if(typeof Browser!="undefined")Browser.init();for(var plugin of preloadPlugins){if(plugin["canHandle"](fullname)){return plugin["handle"](byteArray,fullname)}}return byteArray};var FS_preloadFile=async(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);addRunDependency(dep);try{var byteArray=url;if(typeof url=="string"){byteArray=await asyncLoad(url)}byteArray=await FS_handledByPreloadPlugin(byteArray,fullname);preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}}finally{removeRunDependency(dep)}};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{FS_preloadFile(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish).then(onload).catch(onerror)};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,filesystems:null,syncFSRequests:0,ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=292|73;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}addListener(cb,exclusive=false){var entry={cb,exclusive};var listeners=this.listeners??=new Set;listeners.add(entry);return{listeners,entry}}notifyListeners(flags){if(!this.listeners)return;var excl;for(var entry of this.listeners){if(entry.exclusive)(excl||=[]).push(entry);else entry.cb(flags)}if(excl){var i=(this.exclTurn||0)%excl.length;this.exclTurn=i+1;excl[i].cb(flags)}}},lookupPath(path,opts={}){if(!path){throw new FS.ErrnoError(44)}opts.follow_mount??=true;if(!PATH.isAbs(path)){path=FS.cwd()+"/"+path}linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i<parts.length;i++){var islast=i===parts.length-1;if(islast&&opts.parent){break}if(parts[i]==="."){continue}if(parts[i]===".."){current_path=PATH.dirname(current_path);if(FS.isRoot(current)){path=current_path+"/"+parts.slice(i+1).join("/");nlinks--;continue linkloop}else{current=current.parent}continue}current_path=PATH.join2(current_path,parts[i]);try{current=FS.lookupNode(current,parts[i])}catch(e){if(e?.errno===44&&islast&&opts.noent_okay){return{path:current_path}}throw e}if(FS.isMountpoint(current)&&(!islast||opts.follow_mount)){current=current.mounted.root}if(FS.isLink(current.mode)&&(!islast||opts.follow)){if(!current.node_ops.readlink){throw new FS.ErrnoError(52)}var link=current.node_ops.readlink(current);if(!PATH.isAbs(link)){link=PATH.dirname(current_path)+"/"+link}path=link+"/"+parts.slice(i+1).join("/");continue linkloop}}return{path:current_path,node:current}}throw new FS.ErrnoError(32)},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i<name.length;i++){hash=(hash<<5)-hash+name.charCodeAt(i)|0}return(parentid+hash>>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}if(perms.includes("w")&&!(node.mode&146)){return 2}if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode)){return 54}try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else if(FS.isDir(node.mode)){return 31}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}var mode=FS.flagsToPermissionString(flags);if(FS.isDir(node.mode)){if(mode!=="r"||flags&(512|64)){return 31}}return FS.nodePermissions(node,mode)},checkOpExists(op,err){if(!op){throw new FS.ErrnoError(err)}return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);try{setattr(arg,attr)}catch(e){if(e instanceof RangeError){throw new FS.ErrnoError(22)}throw e}},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}for(var mount of mounts){if(mount.type.syncfs){mount.type.syncfs(mount,populate,done)}else{done(null)}}},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);for(var[hash,current]of Object.entries(FS.nameTable)){while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}}node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name){throw new FS.ErrnoError(28)}if(name==="."||name===".."){throw new FS.ErrnoError(20)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs){Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root))}return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},link(oldpath,newpath,flags){var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.link){throw new FS.ErrnoError(34)}return parent.node_ops.link(parent,newname,oldpath,flags)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return link.node_ops.readlink(link)},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&~4095,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});FS.doSetAttr(null,lookup.node,{atime,mtime,dontFollow})},open(path,flags,mode=438){if(path===""){throw new FS.ErrnoError(44)}flags=FS_modeStringToFlags(flags);if(flags&64){mode=mode&4095|32768}else{mode=0}var node;var isDirPath;if(typeof path=="object"){node=path}else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else if(isDirPath){throw new FS.ErrnoError(31)}else{node=FS.mknod(path,mode|511,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(created){FS.chmod(node,mode&511)}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;stream.node?.notifyListeners(32);try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if(prot&2&&!(flags&2)&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}if(!length){throw new FS.ErrnoError(28)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags??=0;opts.encoding??="binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){abort(`Invalid encoding type "${opts.encoding}"`)}var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){buf=UTF8ArrayToString(buf)}FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags??=577;var stream=FS.open(path,opts.flags,opts.mode);data=FS_fileDataToTypedArray(data);FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn);FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(!randomLeft){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams){if(stream){FS.close(stream)}}},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){data=FS_fileDataToTypedArray(data);FS.chmod(node,mode|146);var stream=FS.open(node,577);FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}},createDevice(parent,name,input,output){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);FS.createDevice.major??=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open(stream){stream.seekable=false},close(stream){if(output?.buffer?.length){output(10)}},read(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=input()}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&!bytesRead){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.atime=Date.now()}return bytesRead},write(stream,buffer,offset,length,pos){for(var i=0;i<length;i++){try{output(buffer[offset+i])}catch(e){throw new FS.ErrnoError(29)}}if(length){stream.node.mtime=stream.node.ctime=Date.now()}return i}});return FS.mkdev(path,mode,dev)},forceLoadFile(obj){if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(globalThis.XMLHttpRequest){abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else{try{obj.contents=readBinary(obj.url)}catch(e){throw new FS.ErrnoError(29)}}},createLazyFile(parent,name,url,canRead,canWrite){class LazyUint8Array{lengthKnown=false;chunks=[];get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort(`Couldn't load ${url}. Status: ${xhr.status}`);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)abort(`invalid range (${from}, ${to}) or no bytes requested!`);if(to>datalength-1)abort(`only ${datalength} bytes available! programmer error!`);var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range",`bytes=${from}-${to}`);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort(`Couldn't load ${url}. Status: ${xhr.status}`);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText??"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")abort("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(globalThis.XMLHttpRequest){if(!ENVIRONMENT_IS_WORKER)abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};for(const[key,fn]of Object.entries(node.stream_ops)){stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}}function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i<size;i++){buffer[offset+i]=contents[position+i]}}else{for(var i=0;i<size;i++){buffer[offset+i]=contents.get(position+i)}}return size}stream_ops.read=(stream,buffer,offset,length,position)=>{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var HEAPU8;var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>{ptr>>>=0;return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):""};var HEAPU32;var HEAP64;var SYSCALLS={currentUmask:18,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return dir+"/"+path},writeStat(buf,stat){HEAPU32[buf>>>2>>>0]=stat.dev;HEAPU32[buf+4>>>2>>>0]=stat.mode;HEAPU32[buf+8>>>2>>>0]=stat.nlink;HEAPU32[buf+12>>>2>>>0]=stat.uid;HEAPU32[buf+16>>>2>>>0]=stat.gid;HEAPU32[buf+20>>>2>>>0]=stat.rdev;HEAP64[buf+24>>>3>>>0]=BigInt(stat.size);HEAP32[buf+32>>>2>>>0]=4096;HEAP32[buf+36>>>2>>>0]=stat.blocks;var atime=stat.atimeMs??stat.atime.getTime();var mtime=stat.mtimeMs??stat.mtime.getTime();var ctime=stat.ctimeMs??stat.ctime.getTime();HEAP64[buf+40>>>3>>>0]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>>2>>>0]=Math.floor(atime%1e3*1e6);HEAP64[buf+56>>>3>>>0]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>>2>>>0]=Math.floor(mtime%1e3*1e6);HEAP64[buf+72>>>3>>>0]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>>2>>>0]=Math.floor(ctime%1e3*1e6);HEAP64[buf+88>>>3>>>0]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAPU32[buf+4>>>2>>>0]=stats.bsize;HEAPU32[buf+60>>>2>>>0]=stats.bsize;HEAP64[buf+8>>>3>>>0]=BigInt(stats.blocks);HEAP64[buf+16>>>3>>>0]=BigInt(stats.bfree);HEAP64[buf+24>>>3>>>0]=BigInt(stats.bavail);HEAP64[buf+32>>>3>>>0]=BigInt(stats.files);HEAP64[buf+40>>>3>>>0]=BigInt(stats.ffree);HEAPU32[buf+48>>>2>>>0]=stats.fsid;HEAPU32[buf+64>>>2>>>0]=stats.flags;HEAPU32[buf+56>>>2>>>0]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.subarray(addr>>>0,addr+len>>>0);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>num<INT53_MIN||num>INT53_MAX?NaN:Number(num);var HEAP16;function ___syscall_fcntl64(fd,cmd,varargs){varargs>>>=0;SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();var mask=289792;stream.flags=stream.flags&~mask|arg&mask;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>>1>>>0]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){varargs>>>=0;SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>>2>>>0]=termios.c_iflag||0;HEAP32[argp+4>>>2>>>0]=termios.c_oflag||0;HEAP32[argp+8>>>2>>>0]=termios.c_cflag||0;HEAP32[argp+12>>>2>>>0]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17>>>0]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>>2>>>0];var c_oflag=HEAP32[argp+4>>>2>>>0];var c_cflag=HEAP32[argp+8>>>2>>>0];var c_lflag=HEAP32[argp+12>>>2>>>0];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17>>>0])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>>2>>>0]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21537:case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>>1>>>0]=winsize[0];HEAP16[argp+2>>>1>>>0]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){path>>>=0;varargs>>>=0;SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;if(flags&64){mode&=~SYSCALLS.currentUmask}return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var _emscripten_get_now=()=>performance.now();var _emscripten_date_now=()=>Date.now();var nowIsMonotonic=1;var checkWasiClock=clock_id=>clock_id>=0&&clock_id<=3;function _clock_time_get(clk_id,ignored_precision,ptime){ignored_precision=bigintToI53Checked(ignored_precision);ptime>>>=0;if(!checkWasiClock(clk_id)){return 28}var now;if(clk_id===0){now=_emscripten_date_now()}else if(nowIsMonotonic){now=_emscripten_get_now()}else{return 52}var nsec=Math.round(now*1e6);HEAP64[ptime>>>3>>>0]=BigInt(nsec);return 0}var getHeapMax=()=>4294901760;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};function _emscripten_resize_heap(requestedSize){requestedSize>>>=0;var oldSize=HEAPU8.length;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>>2>>>0];var len=HEAPU32[iov+4>>>2>>>0];iov+=8;try{var curr=FS.read(stream,HEAP8,ptr,len,offset)}catch(e){if(ret>0&&e instanceof FS.ErrnoError&&(e.errno==6||e.errno==6)){break}throw e}if(curr<0)return-1;ret+=curr;if(curr<len)break;if(typeof offset!="undefined"){offset+=curr}}return ret};function _fd_read(fd,iov,iovcnt,pnum){iov>>>=0;iovcnt>>>=0;pnum>>>=0;try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doReadv(stream,iov,iovcnt);HEAPU32[pnum>>>2>>>0]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);newOffset>>>=0;try{if(isNaN(offset))return 22;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>>3>>>0]=BigInt(stream.position);if(stream.getdents&&!offset&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{if(iovcnt==1){return FS.write(stream,HEAP8,HEAPU32[iov>>>2>>>0],HEAPU32[iov+4>>>2>>>0],offset)}var total=0;for(var i=0,p=iov;i<iovcnt;i++,p+=8){total+=HEAPU32[p+4>>>2>>>0]}var view=new Uint8Array(total);var voff=0;for(var i=0;i<iovcnt;i++,iov+=8){var ptr=HEAPU32[iov>>>2>>>0];var len=HEAPU32[iov+4>>>2>>>0];view.set(HEAPU8.subarray(ptr>>>0,ptr+len>>>0),voff);voff+=len}return FS.write(stream,view,0,total,offset)};function _fd_write(fd,iov,iovcnt,pnum){iov>>>=0;iovcnt>>>=0;pnum>>>=0;try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>>2>>>0]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer>>>0)};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="pointer")return ret>>>0;if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(!stack)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var cwrap=(ident,returnType,argTypes,opts)=>{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var HEAPF32;var HEAPF64;function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr>>>0];case"i8":return HEAP8[ptr>>>0];case"i16":return HEAP16[ptr>>>1>>>0];case"i32":return HEAP32[ptr>>>2>>>0];case"i64":return HEAP64[ptr>>>3>>>0];case"float":return HEAPF32[ptr>>>2>>>0];case"double":return HEAPF64[ptr>>>3>>>0];case"*":return HEAPU32[ptr>>>2>>>0];default:abort(`invalid type for getValue: ${type}`)}}function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr>>>0]=value;break;case"i8":HEAP8[ptr>>>0]=value;break;case"i16":HEAP16[ptr>>>1>>>0]=value;break;case"i32":HEAP32[ptr>>>2>>>0]=value;break;case"i64":HEAP64[ptr>>>3>>>0]=BigInt(value);break;case"float":HEAPF32[ptr>>>2>>>0]=value;break;case"double":HEAPF64[ptr>>>3>>>0]=value;break;case"*":HEAPU32[ptr>>>2>>>0]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stringToNewUTF8=str=>{var size=lengthBytesUTF8(str)+1;var ret=_malloc(size);if(ret)stringToUTF8(str,ret,size);return ret};var FS_createPath=(...args)=>FS.createPath(...args);var FS_unlink=(...args)=>FS.unlink(...args);var FS_createLazyFile=(...args)=>FS.createLazyFile(...args);var FS_createDevice=(...args)=>FS.createDevice(...args);FS.createPreloadedFile=FS_createPreloadedFile;FS.preloadFile=FS_preloadFile;FS.staticInit();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["arguments"])programArgs=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var preInit=Module["preInit"];if(preInit){if(typeof preInit=="function")Module["preInit"]=preInit=[preInit];while(preInit.length>0){preInit.shift()()}}}Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["setValue"]=setValue;Module["getValue"]=getValue;Module["UTF8ToString"]=UTF8ToString;Module["stringToNewUTF8"]=stringToNewUTF8;Module["FS_preloadFile"]=FS_preloadFile;Module["FS_unlink"]=FS_unlink;Module["FS_createPath"]=FS_createPath;Module["FS_createDevice"]=FS_createDevice;Module["FS"]=FS;Module["FS_createDataFile"]=FS_createDataFile;Module["FS_createLazyFile"]=FS_createLazyFile;var _free,_malloc,_zft_error,_zft_api_version,_zft_version,_zft_layout,_zft_layout_count,_zft_model_open,_zft_model_close,_zft_model_id,_zft_model_context,_zft_model_chunk_ms,_zft_ayah_count,_zft_expected,_zft_expected_tokens_ptr,_zft_expected_words_ptr,_zft_expected_n_words,_zft_stream_create,_zft_stream_destroy,_zft_stream_reset,_zft_stream_push_audio,_zft_stream_finish,_zft_stream_heads,_zft_stream_greedy,_zft_stream_error,_zft_config_create,_zft_config_destroy,_zft_config_set_remote,_zft_config_set_preset,_zft_config_set_class_preset,_zft_config_set_class_threshold,_zft_config_clear_class,_zft_config_set_check,_zft_config_set_context,_zft_config_clear_app,_zft_config_set_backend,_zft_config_set_debug_build,_zft_config_effective,_zft_config_json,_zft_score,_zft_result_free,_zft_result_sites,_zft_result_sites_ptr,_zft_result_madd,_zft_result_madd_ptr,_zft_result_qalqalah,_zft_result_qalqalah_ptr,_zft_result_gemination,_zft_result_gemination_ptr,_zft_result_summary,_zft_result_json,_zft_config_set_word_preset,_zft_config_set_word_threshold,_zft_config_clear_word,_zft_config_word,_zft_result_words,_zft_result_words_ptr,_zft_result_path,_zft_result_path_ptr,_zft_result_path_words_ptr,_zft_stream_set_heads,_zft_stream_get_heads,_zft_stream_top2,_zft_stream_top2_ptr,_zft_top2,_zft_stream_topk,_zft_topk,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){_free=Module["_free"]=wasmExports["m"];_malloc=Module["_malloc"]=wasmExports["n"];_zft_error=Module["_zft_error"]=wasmExports["o"];_zft_api_version=Module["_zft_api_version"]=wasmExports["p"];_zft_version=Module["_zft_version"]=wasmExports["q"];_zft_layout=Module["_zft_layout"]=wasmExports["r"];_zft_layout_count=Module["_zft_layout_count"]=wasmExports["s"];_zft_model_open=Module["_zft_model_open"]=wasmExports["t"];_zft_model_close=Module["_zft_model_close"]=wasmExports["u"];_zft_model_id=Module["_zft_model_id"]=wasmExports["v"];_zft_model_context=Module["_zft_model_context"]=wasmExports["w"];_zft_model_chunk_ms=Module["_zft_model_chunk_ms"]=wasmExports["x"];_zft_ayah_count=Module["_zft_ayah_count"]=wasmExports["y"];_zft_expected=Module["_zft_expected"]=wasmExports["z"];_zft_expected_tokens_ptr=Module["_zft_expected_tokens_ptr"]=wasmExports["A"];_zft_expected_words_ptr=Module["_zft_expected_words_ptr"]=wasmExports["B"];_zft_expected_n_words=Module["_zft_expected_n_words"]=wasmExports["C"];_zft_stream_create=Module["_zft_stream_create"]=wasmExports["D"];_zft_stream_destroy=Module["_zft_stream_destroy"]=wasmExports["E"];_zft_stream_reset=Module["_zft_stream_reset"]=wasmExports["F"];_zft_stream_push_audio=Module["_zft_stream_push_audio"]=wasmExports["G"];_zft_stream_finish=Module["_zft_stream_finish"]=wasmExports["H"];_zft_stream_heads=Module["_zft_stream_heads"]=wasmExports["I"];_zft_stream_greedy=Module["_zft_stream_greedy"]=wasmExports["J"];_zft_stream_error=Module["_zft_stream_error"]=wasmExports["K"];_zft_config_create=Module["_zft_config_create"]=wasmExports["L"];_zft_config_destroy=Module["_zft_config_destroy"]=wasmExports["M"];_zft_config_set_remote=Module["_zft_config_set_remote"]=wasmExports["N"];_zft_config_set_preset=Module["_zft_config_set_preset"]=wasmExports["O"];_zft_config_set_class_preset=Module["_zft_config_set_class_preset"]=wasmExports["P"];_zft_config_set_class_threshold=Module["_zft_config_set_class_threshold"]=wasmExports["Q"];_zft_config_clear_class=Module["_zft_config_clear_class"]=wasmExports["R"];_zft_config_set_check=Module["_zft_config_set_check"]=wasmExports["S"];_zft_config_set_context=Module["_zft_config_set_context"]=wasmExports["T"];_zft_config_clear_app=Module["_zft_config_clear_app"]=wasmExports["U"];_zft_config_set_backend=Module["_zft_config_set_backend"]=wasmExports["V"];_zft_config_set_debug_build=Module["_zft_config_set_debug_build"]=wasmExports["W"];_zft_config_effective=Module["_zft_config_effective"]=wasmExports["X"];_zft_config_json=Module["_zft_config_json"]=wasmExports["Y"];_zft_score=Module["_zft_score"]=wasmExports["Z"];_zft_result_free=Module["_zft_result_free"]=wasmExports["_"];_zft_result_sites=Module["_zft_result_sites"]=wasmExports["$"];_zft_result_sites_ptr=Module["_zft_result_sites_ptr"]=wasmExports["aa"];_zft_result_madd=Module["_zft_result_madd"]=wasmExports["ba"];_zft_result_madd_ptr=Module["_zft_result_madd_ptr"]=wasmExports["ca"];_zft_result_qalqalah=Module["_zft_result_qalqalah"]=wasmExports["da"];_zft_result_qalqalah_ptr=Module["_zft_result_qalqalah_ptr"]=wasmExports["ea"];_zft_result_gemination=Module["_zft_result_gemination"]=wasmExports["fa"];_zft_result_gemination_ptr=Module["_zft_result_gemination_ptr"]=wasmExports["ga"];_zft_result_summary=Module["_zft_result_summary"]=wasmExports["ha"];_zft_result_json=Module["_zft_result_json"]=wasmExports["ia"];_zft_config_set_word_preset=Module["_zft_config_set_word_preset"]=wasmExports["ja"];_zft_config_set_word_threshold=Module["_zft_config_set_word_threshold"]=wasmExports["ka"];_zft_config_clear_word=Module["_zft_config_clear_word"]=wasmExports["la"];_zft_config_word=Module["_zft_config_word"]=wasmExports["ma"];_zft_result_words=Module["_zft_result_words"]=wasmExports["na"];_zft_result_words_ptr=Module["_zft_result_words_ptr"]=wasmExports["oa"];_zft_result_path=Module["_zft_result_path"]=wasmExports["pa"];_zft_result_path_ptr=Module["_zft_result_path_ptr"]=wasmExports["qa"];_zft_result_path_words_ptr=Module["_zft_result_path_words_ptr"]=wasmExports["ra"];_zft_stream_set_heads=Module["_zft_stream_set_heads"]=wasmExports["sa"];_zft_stream_get_heads=Module["_zft_stream_get_heads"]=wasmExports["ta"];_zft_stream_top2=Module["_zft_stream_top2"]=wasmExports["ua"];_zft_stream_top2_ptr=Module["_zft_stream_top2_ptr"]=wasmExports["va"];_zft_top2=Module["_zft_top2"]=wasmExports["wa"];_zft_stream_topk=Module["_zft_stream_topk"]=wasmExports["xa"];_zft_topk=Module["_zft_topk"]=wasmExports["ya"];__emscripten_stack_restore=wasmExports["za"];__emscripten_stack_alloc=wasmExports["Aa"];_emscripten_stack_get_current=wasmExports["Ba"];memory=wasmMemory=wasmExports["k"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={d:___syscall_fcntl64,h:___syscall_ioctl,i:___syscall_openat,j:_clock_time_get,a:_emscripten_get_now,e:_emscripten_resize_heap,b:_fd_close,g:_fd_read,f:_fd_seek,c:_fd_write};function applySignatureConversions(wasmExports){wasmExports=Object.assign({},wasmExports);var makeWrapper_pp=f=>a0=>f(a0)>>>0;var makeWrapper_p=f=>()=>f()>>>0;wasmExports["n"]=makeWrapper_pp(wasmExports["n"]);wasmExports["Aa"]=makeWrapper_pp(wasmExports["Aa"]);wasmExports["Ba"]=makeWrapper_p(wasmExports["Ba"]);return wasmExports}async function run(){preRun();if(runDependencies){await resolveRunDependencies()}var setStatus=Module["setStatus"];if(setStatus){setStatus("Running...");await new Promise(resolve=>setTimeout(resolve,1));setTimeout(setStatus,1,"")}if(ABORT)return;initRuntime();Module["onRuntimeInitialized"]?.();postRun()}var wasmExports;wasmExports=await createWasm();await run();
|
|
2
|
+
;return Module}export default createTajweedFormer;
|
|
Binary file
|
package/docs/api.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# @quran-lab/tajweedformer 2.0.1: API
|
|
2
|
+
|
|
3
|
+
Every type is in [`web/tajweed/index.d.ts`](../web/tajweed/index.d.ts),
|
|
4
|
+
[`web/platform/index.d.ts`](../web/platform/index.d.ts) and
|
|
5
|
+
[`web/consent/index.d.ts`](../web/consent/index.d.ts). The concepts and the numbers are the same in the
|
|
6
|
+
Android, Apple and native SDKs.
|
|
7
|
+
|
|
8
|
+
## Entry points
|
|
9
|
+
|
|
10
|
+
| Import | What |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| `@quran-lab/tajweedformer` | `TajweedFormer` with the engine (WebAssembly, single thread, SIMD128), and everything below that needs no engine |
|
|
13
|
+
| `@quran-lab/tajweedformer/scorer` | `TajweedFormer` with the scorer only (96 KB wasm): score heads from a server, an NPU or saved files; no streams |
|
|
14
|
+
| `@quran-lab/tajweedformer/tajweed` | the plain-JS parts: folders, settings, delivery, decode, words, types |
|
|
15
|
+
| `@quran-lab/tajweedformer/node` | `dirReader(dir)` (a folder on disk), `dirStore(dir)` (the delivery cache on disk) |
|
|
16
|
+
| `@quran-lab/tajweedformer/platform` | `QuranLab`: key, token, register, signed config, telemetry, issues |
|
|
17
|
+
| `@quran-lab/tajweedformer/consent` | `enableSharing`, the consent sheet and settings elements, the sharing state |
|
|
18
|
+
| `@quran-lab/tajweedformer/sharing` | the recitation upload (Ogg Opus, outbox); loaded by `consent` when needed |
|
|
19
|
+
|
|
20
|
+
## Loading a model
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
const tf = await TajweedFormer.load({
|
|
24
|
+
platform: ql, // or url: 'https://.../sdk/v4.0/', or cdn: { key, revision }, or files, or reader
|
|
25
|
+
context: 'c16_l64', // optional: app > portal > calibration default
|
|
26
|
+
sampleRate: 48000, // the rate you push; 8000 to 48000, resampled by the engine
|
|
27
|
+
settings: { preset: 'balanced', word: { preset: 'general' } },
|
|
28
|
+
debug: false, // a debug build may turn on checks calibration marks off
|
|
29
|
+
cache: undefined, // the Cache API by default; dirStore(dir) in Node; false for none
|
|
30
|
+
onProgress: ({ path, loaded, total, cached }) => {},
|
|
31
|
+
signal, // AbortSignal for the download
|
|
32
|
+
});
|
|
33
|
+
tf.info; // modelId, revision, contexts, context, chunkMs (320 or 480), engine, engineVersion
|
|
34
|
+
tf.profile; // the files this load needed
|
|
35
|
+
tf.delivery; // requests, bytesDownloaded, filesFromCache, rangesResumed, store
|
|
36
|
+
tf.presets; // [{ name, label, siteFa }] from calibration
|
|
37
|
+
tf.free();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
A folder is refused when its manifest is not format 2 with `engine_format` 2, when its
|
|
41
|
+
`min_sdk_version` is above 2.0.1, when a signature does not verify (folders from the Quran Lab CDN must
|
|
42
|
+
be signed), or when a file it needs does not match its size and SHA-256. A file the manifest does not
|
|
43
|
+
list is never read.
|
|
44
|
+
|
|
45
|
+
## Raw layer: streams and heads
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
const s = tf.createStream({
|
|
49
|
+
heads: ['attr_ghunna', 'boundary'], // added to the default (scorer heads + conf); or allHeads: true
|
|
50
|
+
onChunk(chunk) {
|
|
51
|
+
chunk.index; chunk.startMs; chunk.endMs; // 320 ms chunks at c16, 480 ms at c24
|
|
52
|
+
chunk.logProbs; // Float32Array [8 x 38] at c16 (40 ms frames)
|
|
53
|
+
chunk.attrGhunna; // Float32Array [16 x 5] (20 ms frames)
|
|
54
|
+
chunk.frames; // the decoded layer's frames of this chunk
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
s.pushAudio(pcm); // returns the chunks it completed
|
|
58
|
+
s.finish(); // the end of the take (a 1.05 s flush)
|
|
59
|
+
s.heads(); // the take's kept heads: { n40, n20, logProbs, inter1LogProbs, ... } (null when not kept)
|
|
60
|
+
s.reset(); // a new take
|
|
61
|
+
s.free();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`HEAD_SPECS` lists every head: `name`, `key` (the property), `width`, `rate` (40 or 20 ms), `kind`
|
|
65
|
+
(`log_softmax`, `softmax`, `logit`) and `classes`:
|
|
66
|
+
|
|
67
|
+
| Head | Width | Rate | Classes |
|
|
68
|
+
| --- | --- | --- | --- |
|
|
69
|
+
| `log_probs` / `inter1_log_probs` / `inter4_log_probs` | 38 | 40 ms | tokens.txt |
|
|
70
|
+
| `id_log_probs` / `timing_log_probs` | 38 | 20 ms | tokens.txt (timing: 0 = pause) |
|
|
71
|
+
| `gem` | 2 | 20 ms | single, held |
|
|
72
|
+
| `attr_tafkheem` | 3 | 20 ms | thin, heavy, low_heavy |
|
|
73
|
+
| `attr_ghunna` | 5 | 20 ms | none, plain, geminate, ikhfa, idgham |
|
|
74
|
+
| `attr_qalqalah` | 3 | 20 ms | none, sughra, kubra |
|
|
75
|
+
| `attr_face` | 4 | 20 ms | none, imala, tasheel, sakt |
|
|
76
|
+
| `boundary`, `conf` | 1 | 20 ms | logits: `sigmoidHead(a)` gives probabilities |
|
|
77
|
+
|
|
78
|
+
The engine computes only the heads asked for (engine API 3: a head nobody asked for is not computed,
|
|
79
|
+
and every computed head is the same bits whatever else is asked for); `allHeads` computes all twelve.
|
|
80
|
+
`stream.top2()` gives the engine's per-frame top-1 and runner-up. One stream is live at a time per model
|
|
81
|
+
module.
|
|
82
|
+
|
|
83
|
+
## Decoded layer
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
const d = s.decoded(); // or tf.decode(heads) for heads from anywhere
|
|
87
|
+
d.frames[t]; // { top1, top1LogProb, runnerUp, runnerUpLogProb, margin } per 40 ms frame
|
|
88
|
+
d.tokens[i]; // { id, unit, frame0, frame1, startMs, endMs, peakFrame, logProb, runnerUp, runnerUpUnit,
|
|
89
|
+
// runnerUpLogProb, margin, confidence }
|
|
90
|
+
d.transcript; // the units joined by spaces
|
|
91
|
+
d.ids; // equal to the engine's greedy transcript (s.greedy())
|
|
92
|
+
d.words; // tokens split at the "|" token; V4 rarely emits it, so use result.words for a known ayah
|
|
93
|
+
s.decoded({ k: 5 }).frames[t].ranked; // the 5 best tokens of frame t, ranked, with log-probs and margins
|
|
94
|
+
s.topK('attr_ghunna', 5); // any head with a distribution: { ids, values, margins } [frames x k], names
|
|
95
|
+
tf.topK(heads, 'log_probs', 38); // the full ranked list, for heads from anywhere
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Rules: ties go to the lower token id; a token is a run of equal non-blank top-1 frames; its numbers are
|
|
99
|
+
those of its peak frame; `confidence` is the mean `sigmoid(conf)` over its 20 ms frames. Times are the
|
|
100
|
+
model's output frame times from the start of the take.
|
|
101
|
+
|
|
102
|
+
## Tajweed layer
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
const r = await tf.score(s.heads(), surah, ayah, { decoded: true });
|
|
106
|
+
r.sites[i]; // { class, check, status, pos, word, expected, expectedUnit, alternative, alternativeUnit,
|
|
107
|
+
// llr: { final, inter1 }, fusedLlr, threshold, flagged, frameLo, frameHi }
|
|
108
|
+
r.madd[i]; // { word, unit, maddClass, ratio, count, displayCount, allowedLo, allowedHi, flagged, frameLo, frameHi }
|
|
109
|
+
r.qalqalah[i]; // { word, unit, score, threshold, flagged, frameLo, frameHi }; r.gemination the same
|
|
110
|
+
r.words[w]; // { word, score, threshold, flagged, best: { class, edit, expectedUnit, alternativeUnit, llr }, tokens,
|
|
111
|
+
// startMs, endMs, issues: { sites, madd, qalqalah, gemination } }
|
|
112
|
+
r.path; // { tokens, wordOfToken }: the scored path (the legal reading closest to what was heard)
|
|
113
|
+
r.summary; // counts, plus words and wordsFlagged
|
|
114
|
+
r.settings; // the effective settings it was scored with; r.wordLevel the word level (or null)
|
|
115
|
+
r.raw; // the engine's whole result JSON: per window every class's alternative and LLRs
|
|
116
|
+
r.decoded; // with { decoded: true }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- One entry in `sites` per scored window and class; `flagged` is true on the class the window reports.
|
|
120
|
+
A threshold is a cut on the fused LLR (`a * llr.final + (1 - a) * llr.inter1`, clipped to [-30, 30]).
|
|
121
|
+
- `words[w]` is the engine's word level: `score` = the largest clipped fused LLR over every single edit
|
|
122
|
+
inside the word (any class), `best` = that edit, `flagged` = `score >= threshold` (the word tau in force).
|
|
123
|
+
`score`, `threshold` and `flagged` are null when the word check is off or the calibration has no word
|
|
124
|
+
block.
|
|
125
|
+
- `score(heads, s, a, { backend: 'fp32' })` for heads that did not come from this engine (CoreML, an
|
|
126
|
+
NPU, a server); the default is `int8` (this engine's heads).
|
|
127
|
+
|
|
128
|
+
## Settings
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
tf.settings.setPreset('sensitive');
|
|
132
|
+
tf.settings.setClassPreset('cons_sub', 'very_sensitive');
|
|
133
|
+
tf.settings.setClassThreshold('vowel_sub', 1.25); // -20 .. 20
|
|
134
|
+
tf.settings.setCheck('qalqalah', true);
|
|
135
|
+
tf.settings.setWord({ threshold: 5 }); // or { preset: 'beginner' }; clamped to calibration's range
|
|
136
|
+
tf.settings.setCheck('word', false); // no word scores at all
|
|
137
|
+
tf.setRemoteConfig(configOrBlock); // the platform client does this for you
|
|
138
|
+
tf.effectiveSettings(); // { context, preset, sites: { cls: { enabled, threshold, preset, source, status } }, madd, ...,
|
|
139
|
+
// dropped, refused }
|
|
140
|
+
tf.wordLevel(); // { enabled, preset, source, threshold, presets, floor, min, max } or null
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Invalid app values throw `TajweedSettingsError` (code `invalid_app_option`); an invalid portal field is
|
|
144
|
+
dropped and listed in `dropped`, never the block. The context is fixed at load.
|
|
145
|
+
|
|
146
|
+
## Delivery helpers
|
|
147
|
+
|
|
148
|
+
`cachedReader({ baseUrl, headers, store, rangeSize, parallel, onProgress, signal })` is the reader
|
|
149
|
+
`load()` uses over HTTP; `profilePaths(manifest, { context })` the profile; `pruneCache(store,
|
|
150
|
+
[manifest])` removes cached files no kept manifest lists; stores: `cacheApiStore()`,
|
|
151
|
+
`indexedDbStore()`, `memoryStore()`, `dirStore(dir)` (Node).
|
|
152
|
+
|
|
153
|
+
## Platform
|
|
154
|
+
|
|
155
|
+
See the README's Platform section. `QuranLab.configure(options)` never throws for network trouble:
|
|
156
|
+
`ql.stopped` names an error-table code that stopped the network for this launch (a revoked key, a key
|
|
157
|
+
of the wrong type), `ql.lastError` the last failure. Recognition never depends on the network once the
|
|
158
|
+
model is cached.
|
|
159
|
+
|
|
160
|
+
## Recitation sharing
|
|
161
|
+
|
|
162
|
+
`enableSharing(ql, { lang, ask, idle })` returns a controller: `sessionCompleted()`,
|
|
163
|
+
`setSharing(on)`, `deleteShared()`, `recoveryCode()`, `impactLine()`. `ask: async (a) => ({ yes })`
|
|
164
|
+
lets the app show the sheet at its own time; the lines are always the SDK's (`copyFor(lang)`).
|
|
165
|
+
Elements: `<quranlab-consent>` (`QuranLabConsent.ask(...)`) and `<quranlab-sharing-settings>`
|
|
166
|
+
(`.controller = sharing`). Theme with `--ql-bg`, `--ql-fg`, `--ql-muted`, `--ql-accent`,
|
|
167
|
+
`--ql-accent-fg`, `--ql-radius`, `--ql-font` and `::part(sheet|title|body|primary|secondary|guardian)`.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>TajweedFormer example</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { --bg: #fdfcf8; --fg: #1c1b19; --accent: #2a3b8f; --muted: #6b675e; }
|
|
9
|
+
@media (prefers-color-scheme: dark) { :root { --bg: #161513; --fg: #f3f1ea; --accent: #8fa2ff; --muted: #a8a397; } }
|
|
10
|
+
body { margin: 0; padding: 16px; background: var(--bg); color: var(--fg); font: 16px/1.5 system-ui, sans-serif; max-width: 52rem; }
|
|
11
|
+
button, input { font: inherit; }
|
|
12
|
+
button { padding: .5rem 1rem; border: 2px solid var(--accent); border-radius: 10px; background: var(--accent); color: #fff; }
|
|
13
|
+
#heard span { display: inline-block; margin: 2px; padding: 2px 6px; border-radius: 6px; border: 1px solid var(--muted); }
|
|
14
|
+
.low { opacity: .5; } .flag { outline: 2px solid #d33; }
|
|
15
|
+
pre { white-space: pre-wrap; color: var(--muted); }
|
|
16
|
+
</style>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<h1>TajweedFormer V4</h1>
|
|
20
|
+
<p>Key <input id="key" size="40" placeholder="qlk_v1_... (a web key for this origin)">
|
|
21
|
+
Surah <input id="surah" value="1" size="3"> Ayah <input id="ayah" value="1" size="3"></p>
|
|
22
|
+
<p><button id="go">Load and listen</button> <button id="stop" disabled>Stop and score</button></p>
|
|
23
|
+
<pre id="status"></pre>
|
|
24
|
+
<div id="heard"></div>
|
|
25
|
+
<div id="words"></div>
|
|
26
|
+
<quranlab-sharing-settings></quranlab-sharing-settings>
|
|
27
|
+
<script type="module">
|
|
28
|
+
import { TajweedFormer } from '../web/index.js';
|
|
29
|
+
import { QuranLab } from '../web/platform/index.js';
|
|
30
|
+
import { enableSharing } from '../web/consent/index.js';
|
|
31
|
+
|
|
32
|
+
const $ = (id) => document.getElementById(id);
|
|
33
|
+
const status = (s) => { $('status').textContent = s; };
|
|
34
|
+
let tf, stream, ctx, node, src;
|
|
35
|
+
|
|
36
|
+
$('go').onclick = async () => {
|
|
37
|
+
const ql = await QuranLab.configure({ key: $('key').value.trim(), appVersion: 'example', contributions: true });
|
|
38
|
+
if (ql.stopped) { status(`the platform refused this key: ${ql.stopped}`); return; }
|
|
39
|
+
ql.startRefresh();
|
|
40
|
+
const sharing = enableSharing(ql);
|
|
41
|
+
document.querySelector('quranlab-sharing-settings').controller = sharing;
|
|
42
|
+
ctx = new AudioContext();
|
|
43
|
+
tf = tf || await TajweedFormer.load({ platform: ql, sampleRate: ctx.sampleRate,
|
|
44
|
+
onProgress: (p) => status(`${p.path}: ${(100 * p.loaded / p.total).toFixed(0)}%${p.cached ? ' (cached)' : ''}`) });
|
|
45
|
+
status(`model ${tf.info.modelId} at ${tf.context}, ${tf.delivery.bytesDownloaded} bytes downloaded`);
|
|
46
|
+
stream = tf.createStream({ onChunk: () => {
|
|
47
|
+
const d = stream.decoded();
|
|
48
|
+
$('heard').innerHTML = d.tokens.map((t) => `<span class="${t.confidence !== null && t.confidence < 0.6 ? 'low' : ''}" title="runner-up ${t.runnerUpUnit}, margin ${t.margin.toFixed(2)}">${t.unit}</span>`).join('');
|
|
49
|
+
} });
|
|
50
|
+
const mic = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: false, noiseSuppression: false, autoGainControl: false } });
|
|
51
|
+
src = ctx.createMediaStreamSource(mic);
|
|
52
|
+
node = ctx.createScriptProcessor(4096, 1, 1); // an AudioWorklet in production
|
|
53
|
+
node.onaudioprocess = (e) => stream.pushAudio(new Float32Array(e.inputBuffer.getChannelData(0)));
|
|
54
|
+
src.connect(node); node.connect(ctx.destination);
|
|
55
|
+
$('stop').disabled = false;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
$('stop').onclick = async () => {
|
|
59
|
+
node.disconnect(); src.disconnect();
|
|
60
|
+
stream.finish();
|
|
61
|
+
const r = await tf.score(stream.heads(), Number($('surah').value), Number($('ayah').value));
|
|
62
|
+
$('words').innerHTML = r.words.map((w) => `<span class="${w.flagged ? 'flag' : ''}">word ${w.word + 1}: ${w.score === null ? '-' : w.score.toFixed(1)}</span>`).join(' ');
|
|
63
|
+
status(`${r.summary.flagged} of ${r.summary.sites} sites flagged; ${r.summary.wordsFlagged} words flagged`);
|
|
64
|
+
stream.free();
|
|
65
|
+
$('stop').disabled = true;
|
|
66
|
+
};
|
|
67
|
+
</script>
|
|
68
|
+
</body>
|
|
69
|
+
</html>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Score a recitation from Node with every layer, from a V4 folder on disk.
|
|
2
|
+
//
|
|
3
|
+
// node examples/node.mjs <sdk/v4.0 folder> <audio.f32le, mono 16 kHz> <surah> <ayah>
|
|
4
|
+
// ffmpeg -i take.wav -f f32le -ac 1 -ar 16000 take.f32 (one way to get the raw samples)
|
|
5
|
+
//
|
|
6
|
+
// Prints the decoded layer (what the model heard, with confidence), every flagged site and the words.
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { TajweedFormer } from '@quran-lab/tajweedformer';
|
|
9
|
+
import { dirReader } from '@quran-lab/tajweedformer/node';
|
|
10
|
+
|
|
11
|
+
const [dir, audio, surah = '1', ayah = '1'] = process.argv.slice(2);
|
|
12
|
+
if (!dir || !audio) { console.error('usage: node examples/node.mjs <folder> <audio.f32> <surah> <ayah>'); process.exit(2); }
|
|
13
|
+
|
|
14
|
+
const tf = await TajweedFormer.load({ reader: dirReader(dir), settings: { preset: 'balanced' } });
|
|
15
|
+
console.log(`model ${tf.info.modelId} ${tf.info.revision}, context ${tf.context} (${tf.info.chunkMs} ms chunks)`);
|
|
16
|
+
|
|
17
|
+
const buf = readFileSync(audio);
|
|
18
|
+
const pcm = new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength >> 2);
|
|
19
|
+
const stream = tf.createStream({ allHeads: true });
|
|
20
|
+
for (let at = 0; at < pcm.length; at += 1600) stream.pushAudio(pcm.subarray(at, at + 1600)); // 100 ms at a time
|
|
21
|
+
stream.finish();
|
|
22
|
+
|
|
23
|
+
const heard = stream.decoded();
|
|
24
|
+
console.log('heard:', heard.tokens.map((t) => `${t.unit}(${t.confidence === null ? '?' : t.confidence.toFixed(2)})`).join(' '));
|
|
25
|
+
const unsure = heard.tokens.filter((t) => t.margin < 1);
|
|
26
|
+
if (unsure.length) console.log('close calls:', unsure.map((t) => `${t.unit} or ${t.runnerUpUnit} at ${t.startMs} ms`).join(', '));
|
|
27
|
+
|
|
28
|
+
const r = await tf.score(stream.heads(), Number(surah), Number(ayah));
|
|
29
|
+
for (const s of r.sites.filter((x) => x.flagged)) {
|
|
30
|
+
console.log(`word ${s.word}: ${s.class} ${s.expectedUnit} -> ${s.alternativeUnit ?? '(dropped)'} fused ${s.fusedLlr.toFixed(2)} > ${s.threshold}`);
|
|
31
|
+
}
|
|
32
|
+
for (const m of r.madd.filter((x) => x.flagged)) console.log(`word ${m.word}: madd ${m.maddClass} held ${m.count}, allowed ${m.allowedLo} to ${m.allowedHi}`);
|
|
33
|
+
for (const w of r.words) {
|
|
34
|
+
console.log(`word ${w.word}: score ${w.score === null ? '-' : w.score.toFixed(2)}${w.flagged ? ' FLAGGED' : ''} (${w.startMs} to ${w.endMs} ms)`);
|
|
35
|
+
}
|
|
36
|
+
stream.free();
|
|
37
|
+
tf.free();
|
package/package.json
CHANGED
|
@@ -1,6 +1,81 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@quran-lab/tajweedformer",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@quran-lab/tajweedformer",
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "TajweedFormer V4 for browsers and Node: the exact engine in WebAssembly (bit-identical to the native engine), cached and verified model delivery, every head of the network, the decoded layer (top-1 and runner-up, confidence, timings), per-site and per-word tajweed verdicts with calibrated settings, and the Quran Lab platform client (signed config, telemetry, opt-in recitation sharing).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "web/index.js",
|
|
7
|
+
"types": "web/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./web/index.d.ts",
|
|
11
|
+
"import": "./web/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./scorer": {
|
|
14
|
+
"types": "./web/scorer.d.ts",
|
|
15
|
+
"import": "./web/scorer.js"
|
|
16
|
+
},
|
|
17
|
+
"./tajweed": {
|
|
18
|
+
"types": "./web/tajweed/index.d.ts",
|
|
19
|
+
"import": "./web/tajweed/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./node": {
|
|
22
|
+
"types": "./web/tajweed/node.d.ts",
|
|
23
|
+
"import": "./web/tajweed/node.js"
|
|
24
|
+
},
|
|
25
|
+
"./platform": {
|
|
26
|
+
"types": "./web/platform/index.d.ts",
|
|
27
|
+
"import": "./web/platform/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./consent": {
|
|
30
|
+
"types": "./web/consent/index.d.ts",
|
|
31
|
+
"import": "./web/consent/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./sharing": {
|
|
34
|
+
"types": "./web/sharing/index.d.ts",
|
|
35
|
+
"import": "./web/sharing/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"web/",
|
|
41
|
+
"dist/tajweedformer.mjs",
|
|
42
|
+
"dist/tajweedformer.wasm",
|
|
43
|
+
"dist/tajweedformer-scorer.mjs",
|
|
44
|
+
"dist/tajweedformer-scorer.wasm",
|
|
45
|
+
"dist/tajweedformer-opus.mjs",
|
|
46
|
+
"dist/tajweedformer-opus.wasm",
|
|
47
|
+
"README.md",
|
|
48
|
+
"CHANGELOG.md",
|
|
49
|
+
"docs/api.md",
|
|
50
|
+
"examples/"
|
|
51
|
+
],
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=20"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "bash scripts/build.sh",
|
|
57
|
+
"build:scorer": "bash scripts/build_scorer.sh",
|
|
58
|
+
"build:all": "bash scripts/build.sh && bash scripts/build_scorer.sh && bash scripts/build_opus.sh",
|
|
59
|
+
"bench": "bash scripts/bench.sh",
|
|
60
|
+
"bench:rt": "node test/pool_bench.mjs",
|
|
61
|
+
"test": "node --test \"test/*.test.mjs\"",
|
|
62
|
+
"test:wasm": "node test/smoke.mjs",
|
|
63
|
+
"conformance": "bash scripts/conformance.sh",
|
|
64
|
+
"serve": "node scripts/serve.mjs",
|
|
65
|
+
"build:opus": "bash scripts/build_opus.sh",
|
|
66
|
+
"build:1x": "bash scripts/build.sh --with-1x && bash scripts/build.sh --with-1x --threads 4 && bash scripts/build.sh --with-1x --no-simd",
|
|
67
|
+
"test:browser": "node scripts/browser-v4.mjs",
|
|
68
|
+
"size": "node scripts/size.mjs"
|
|
69
|
+
},
|
|
70
|
+
"license": "SEE LICENSE IN README.md",
|
|
71
|
+
"repository": {
|
|
72
|
+
"type": "git",
|
|
73
|
+
"url": "https://github.com/Quran-Lab/tajweedformer-web"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"puppeteer-core": "^24.43.1"
|
|
77
|
+
},
|
|
78
|
+
"sideEffects": [
|
|
79
|
+
"./web/consent/*.js"
|
|
80
|
+
]
|
|
81
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// The sharing copy: Quran Lab's copy decks (quranlab.sharing-copy/1), bundled in decks.js for en, ar, id, ur,
|
|
2
|
+
// tr, ms, fr, bn. Apps choose the language and the timing; an app that replaces strings with its own wording
|
|
3
|
+
// (wording.js) makes the learner agree to that wording instead. The hadith line is in every deck with its
|
|
4
|
+
// verified source.
|
|
5
|
+
|
|
6
|
+
import { DECKS } from './decks.js';
|
|
7
|
+
|
|
8
|
+
const extra = new Map();
|
|
9
|
+
|
|
10
|
+
/** Add or replace a deck (a newer quranlab.sharing-copy/1 file the backend serves). */
|
|
11
|
+
export function registerDeck(lang, deck) {
|
|
12
|
+
if (!deck || deck.format !== 'quranlab.sharing-copy/1') throw new TypeError('registerDeck: a quranlab.sharing-copy/1 deck is needed');
|
|
13
|
+
extra.set(lang, deck);
|
|
14
|
+
}
|
|
15
|
+
/** Shipped languages. */
|
|
16
|
+
export function languages() { return [...new Set([...Object.keys(DECKS), ...extra.keys()])]; }
|
|
17
|
+
|
|
18
|
+
/** The English source deck. */
|
|
19
|
+
export const EN = DECKS.en;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The deck for a BCP 47 tag ("ar-EG" -> "ar"; English for a language not shipped), with helpers:
|
|
23
|
+
* t(key) a string (the app's override, else the deck's, else English), minutes(n) the plural impact line.
|
|
24
|
+
* `overrides` is an app's validated wording ({key: text}, plural lines as `impact.minutes.<category>`).
|
|
25
|
+
* Decks up to 2026-11-07 had a `{days}` placeholder (filled from delete_days); later decks delete at once
|
|
26
|
+
* and have none.
|
|
27
|
+
*/
|
|
28
|
+
export function copyFor(lang = 'en', overrides = null) {
|
|
29
|
+
const tag = String(lang || 'en');
|
|
30
|
+
const base = tag.toLowerCase().split(/[-_]/)[0];
|
|
31
|
+
const d = extra.get(tag) || extra.get(base) || DECKS[base] || DECKS.en;
|
|
32
|
+
const own = overrides || {};
|
|
33
|
+
const fill = (v) => (typeof v === 'string' ? v.replace(/\{days\}/g, String(d.delete_days ?? 0)) : v);
|
|
34
|
+
const t = (k) => {
|
|
35
|
+
if (typeof own[k] === 'string') return own[k];
|
|
36
|
+
const v = d.strings[k] !== undefined ? d.strings[k] : DECKS.en.strings[k];
|
|
37
|
+
return fill(v);
|
|
38
|
+
};
|
|
39
|
+
const minutes = (n) => {
|
|
40
|
+
const forms = t('impact.minutes') || {};
|
|
41
|
+
let cat = 'other';
|
|
42
|
+
try { cat = new Intl.PluralRules(d.lang).select(n); } catch (_) { /* other */ }
|
|
43
|
+
const s = own[`impact.minutes.${cat}`] ?? forms[cat] ?? own['impact.minutes.other'] ?? forms.other ?? '';
|
|
44
|
+
let num = String(n);
|
|
45
|
+
try { num = new Intl.NumberFormat(d.lang).format(n); } catch (_) { /* plain */ }
|
|
46
|
+
return s.replace(/\{minutes\}/g, num);
|
|
47
|
+
};
|
|
48
|
+
return { lang: d.lang, dir: d.dir || 'ltr', version: d.version, consentVersion: d.consent_version, deleteDays: d.delete_days ?? 0,
|
|
49
|
+
deleteTiming: d.delete_timing || (d.delete_days ? 'delayed' : 'immediate'), hadithSource: d.hadith_source || null,
|
|
50
|
+
overridden: Object.keys(own), t, minutes };
|
|
51
|
+
}
|