UNPKG

greed.js

Version:

Run Python libraries in the browser with WebGPU acceleration - PyTorch, NumPy, and more. Modular architecture with full backward compatibility.

1 lines 13.6 kB
"use strict";(this.webpackChunkGreed=this.webpackChunkGreed||[]).push([[114],{123:(t,e,i)=>{i.d(e,{A:()=>s});const s=class{constructor(){this._events=new Map,this._maxListeners=10}on(t,e){if("function"!=typeof e)throw new TypeError("Listener must be a function");this._events.has(t)||this._events.set(t,[]);const i=this._events.get(t);return i.push(e),i.length>this._maxListeners&&this.emit("maxListenersExceeded",{event:t,count:i.length,limit:this._maxListeners}),this}once(t,e){const i=(...s)=>{e.apply(this,s),this.off(t,i)};return this.on(t,i)}off(t,e){if(!this._events.has(t))return this;const i=this._events.get(t),s=i.indexOf(e);return-1!==s&&i.splice(s,1),0===i.length&&this._events.delete(t),this}emit(t,...e){if(!this._events.has(t))return!1;const i=this._events.get(t).slice();for(const s of i)try{s.apply(this,e)}catch(e){this.emit("error",e,t)}return!0}removeAllListeners(t){return t?this._events.delete(t):this._events.clear(),this}listenerCount(t){return this._events.has(t)?this._events.get(t).length:0}setMaxListeners(t){if("number"!=typeof t||t<0||isNaN(t))throw new TypeError("n must be a non-negative number");return this._maxListeners=t,this}eventNames(){return Array.from(this._events.keys())}async emitAsync(t,...e){if(!this._events.has(t))return[];const i=this._events.get(t).slice().map(async i=>{try{return await i.apply(this,e)}catch(e){throw this.emit("error",e,t),e}});return Promise.allSettled(i)}}},777:(t,e,i)=>{i.d(e,{default:()=>y});var s=i(123);class r extends s.A{constructor(t={}){super(),this.config={pyodideIndexURL:t.pyodideIndexURL||"https://cdn.jsdelivr.net/pyodide/v0.24.1/full/",preloadPackages:t.preloadPackages||["numpy"],timeout:t.initTimeout||3e4,...t},this.pyodide=null,this.isReady=!1,this.installedPackages=new Set,this.initPromise=null}async initialize(){return this.initPromise||(this.initPromise=this._initializeInternal()),this.initPromise}async _initializeInternal(){try{if(this.emit("init:start",{stage:"pyodide"}),"undefined"==typeof loadPyodide)throw new Error("Pyodide not loaded. Please include pyodide.js in your HTML.");const t=loadPyodide({indexURL:this.config.pyodideIndexURL});return this.pyodide=await Promise.race([t,this._createTimeoutPromise(this.config.timeout,"Pyodide initialization timeout")]),this.emit("init:progress",{stage:"pyodide",status:"loaded"}),this.config.preloadPackages.length>0&&(this.emit("init:progress",{stage:"packages",packages:this.config.preloadPackages}),await this._loadPackages(this.config.preloadPackages)),this.isReady=!0,this.emit("init:complete",{installedPackages:Array.from(this.installedPackages)}),!0}catch(t){throw this.emit("init:error",{error:t,stage:"initialization"}),t}}async loadPackages(t){if(!this.isReady)throw new Error("Runtime not initialized. Call initialize() first.");return this._loadPackages(t)}async _loadPackages(t){const e=t.filter(t=>!this.installedPackages.has(t));if(0===e.length)return Array.from(this.installedPackages);try{return this.emit("packages:loading",{packages:e}),await this.pyodide.loadPackage(e),e.forEach(t=>this.installedPackages.add(t)),this.emit("packages:loaded",{loaded:e,total:Array.from(this.installedPackages)}),Array.from(this.installedPackages)}catch(t){throw this.emit("packages:error",{error:t,packages:e}),new Error(`Failed to load packages [${e.join(", ")}]: ${t.message}`)}}async runPython(t,e={}){if(!this.isReady)throw new Error("Runtime not initialized. Call initialize() first.");const{captureOutput:i=!1,timeout:s=1e4,globals:r={},validateInput:n=!0}=e;if(n&&this._containsDangerousPatterns(t))throw new o("Potentially dangerous code patterns detected");try{for(const[t,e]of Object.entries(r))this.pyodide.globals.set(t,e);let e;if(i){const i=`\nimport sys\nfrom io import StringIO\n_output_buffer = StringIO()\n_original_stdout = sys.stdout\nsys.stdout = _output_buffer\n\ntry:\n${t.split("\n").map(t=>" "+t).join("\n")}\nfinally:\n sys.stdout = _original_stdout\n _captured_output = _output_buffer.getvalue()\n`,r=this.pyodide.runPython(i);await Promise.race([r,this._createTimeoutPromise(s,"Python execution timeout")]),e={output:this.pyodide.globals.get("_captured_output")}}else{const i=this.pyodide.runPythonAsync(t);e=await Promise.race([i,this._createTimeoutPromise(s,"Python execution timeout")])}return e}catch(e){throw this.emit("execution:error",{error:e,code:t.substring(0,100)}),e}}getGlobal(t){if(!this.isReady)throw new Error("Runtime not initialized");return this.pyodide.globals.get(t)}setGlobal(t,e){if(!this.isReady)throw new Error("Runtime not initialized");this.pyodide.globals.set(t,e)}hasPackage(t){return this.installedPackages.has(t)}getStatus(){return{isReady:this.isReady,installedPackages:Array.from(this.installedPackages),pyodideVersion:this.pyodide?.version||null,config:this.config}}cleanup(){try{this.pyodide&&(this.pyodide.globals.clear(),this.pyodide=null),this.isReady=!1,this.installedPackages.clear(),this.initPromise=null,this.emit("cleanup:complete")}catch(t){this.emit("cleanup:error",{error:t})}}_createTimeoutPromise(t,e){return new Promise((i,s)=>{setTimeout(()=>s(new Error(e)),t)})}_containsDangerousPatterns(t){return[/\beval\s*\(/,/\bexec\s*\(/,/\b__import__\s*\(/,/\bsubprocess\./,/\bos\.system\s*\(/,/\bopen\s*\(/,/\bfile\s*\(/].some(e=>e.test(t))}}class o extends Error{constructor(t){super(t),this.name="SecurityError"}}const n=r;var a=i(493),h=i(683),c=i(219),l=i(847),m=i(228),u=i(867),d=i(626);class p extends s.A{constructor(t={}){super(),this.config=this._validateConfig({enableWebGPU:!0,enableWorkers:!0,maxWorkers:navigator.hardwareConcurrency||4,strictSecurity:!0,allowEval:!1,allowFileSystem:!1,allowNetwork:!1,maxMemoryMB:1024,gcThreshold:.8,enableProfiling:!0,pyodideIndexURL:"https://cdn.jsdelivr.net/pyodide/v0.24.1/full/",preloadPackages:["numpy"],initTimeout:3e4,...t}),this.isInitialized=!1,this.initializationPromise=null,this.componentsReady={runtime:!1,compute:!1,memory:!1,security:!1},this.runtime=null,this.compute=null,this.memory=null,this.security=null,this.torchAPI=null,this.numpy=null,this.stats={initTime:0,operations:0,totalExecutionTime:0,averageExecutionTime:0,memoryUsage:0,startTime:performance.now()},this.errorCount=0,this.lastError=null,this._setupGlobalErrorHandling()}async initialize(){return this.initializationPromise||(this.initializationPromise=this._performInitialization()),this.initializationPromise}async _performInitialization(){if(this.isInitialized)return!0;const t=performance.now();this.emit("init:start",{config:this.config});try{return await this._initializeComponents(),await this._setupPyTorchAPI(),await this._validateSystemReadiness(),this.isInitialized=!0,this.stats.initTime=performance.now()-t,this.emit("init:complete",{initTime:this.stats.initTime,components:Object.keys(this.componentsReady).filter(t=>this.componentsReady[t]),memoryUsage:this.memory.getStats().memoryUsageMB}),!0}catch(t){throw this.emit("init:error",{error:t,phase:"initialization"}),this.lastError=t,this.errorCount++,t}}async run(t,e={}){this.isInitialized||await this.initialize();const i=this._generateOperationId(),s=performance.now();this.emit("operation:start",{operationId:i,codeLength:t.length});try{const r=this.security.validatePythonCode(t,{allowWarnings:e.allowWarnings||!1,bypassValidation:e.bypassSecurity||!1});if(!r.allowed)throw new Error(`Security validation failed: ${r.riskLevel} risk detected`);const o=await this.runtime.runPython(t,{captureOutput:!1!==e.captureOutput,timeout:e.timeout||this.config.initTimeout,globals:e.globals||{},validateInput:!1}),n=performance.now()-s;return this._updateOperationStats(n),this.emit("operation:complete",{operationId:i,executionTime:n,securityWarnings:r.warnings?.length||0,memoryUsage:this.memory.getStats().memoryUsageMB}),o}catch(t){const e=performance.now()-s;throw this.emit("operation:error",{operationId:i,error:t,executionTime:e}),this.lastError=t,this.errorCount++,t}}async tensor(t,e,i={}){this.isInitialized||await this.initialize();try{this.security.validateTensorData(e,i);const s=this.security.validateOperation(t,i.params,i);return await this.compute.execute(s.operation,e,s.options)}catch(e){throw this.emit("tensor:error",{operation:t,error:e}),this.lastError=e,this.errorCount++,e}}async loadPackages(t){this.isInitialized||await this.initialize();const e=Array.isArray(t)?t:[t],i=this.config.allowedPackages||this.security.config.allowedPackages,s=e.filter(t=>!i.has(t));if(s.length>0)throw new Error(`Packages not in allowlist: ${s.join(", ")}`);return this.runtime.loadPackages(e)}getStats(){const t={...this.stats,isInitialized:this.isInitialized,errorCount:this.errorCount,uptime:performance.now()-this.stats.startTime,components:{...this.componentsReady}};return this.isInitialized?{...t,runtime:this.runtime.getStatus(),compute:this.compute.getStats(),memory:this.memory.getStats(),security:this.security.getStats()}:t}updateConfig(t){const e={...this.config};this.config={...this.config,...t},this.security&&t.security&&this.security.updateConfig(t.security),this.emit("config:updated",{oldConfig:e,newConfig:this.config})}async forceGC(t={}){if(!this.isInitialized)return{cleaned:0};this.emit("gc:start");try{const e=(await Promise.allSettled([this.memory.forceGC(t),this.compute.availableStrategies.has("webgpu")?this.compute.webgpu.bufferManager?.gc(t):Promise.resolve({destroyed:0})])).reduce((t,e)=>"fulfilled"===e.status?t+(e.value.cleaned||e.value.destroyed||0):t,0);return this.emit("gc:complete",{totalCleaned:e}),{cleaned:e}}catch(t){throw this.emit("gc:error",{error:t}),t}}async destroy(){if(this.isInitialized){this.emit("destroy:start");try{const t=[];this.compute&&t.push(this.compute.cleanup()),this.memory&&t.push(this.memory.cleanup()),this.runtime&&t.push(Promise.resolve(this.runtime.cleanup())),await Promise.all(t),this.isInitialized=!1,this.initializationPromise=null,this.componentsReady={runtime:!1,compute:!1,memory:!1,security:!1},this.emit("destroy:complete")}catch(t){throw this.emit("destroy:error",{error:t}),t}}}async _initializeComponents(){this.emit("components:init:start"),this.security=new c.A({strictMode:this.config.strictSecurity,allowEval:this.config.allowEval,allowFileSystem:this.config.allowFileSystem,allowNetwork:this.config.allowNetwork,maxTensorSize:this.config.maxTensorSize,maxMemoryMB:this.config.maxMemoryMB}),this.componentsReady.security=!0,this._forwardEvents(this.security,"security"),this.memory=new h.A({maxMemoryMB:this.config.maxMemoryMB,gcThreshold:this.config.gcThreshold,enableAutoGC:!0}),this.componentsReady.memory=!0,this._forwardEvents(this.memory,"memory"),this.runtime=new n({pyodideIndexURL:this.config.pyodideIndexURL,preloadPackages:this.config.preloadPackages,timeout:this.config.initTimeout}),await this.runtime.initialize(),this.componentsReady.runtime=!0,this._forwardEvents(this.runtime,"runtime"),this.compute=new a.A({enableWebGPU:this.config.enableWebGPU,enableWorkers:this.config.enableWorkers,maxWorkers:this.config.maxWorkers,webgpu:{maxBufferSize:1024*this.config.maxMemoryMB*1024*.8,enableProfiling:this.config.enableProfiling}}),await this.compute.initialize(),this.componentsReady.compute=!0,this._forwardEvents(this.compute,"compute"),this.emit("components:init:complete",{components:Object.keys(this.componentsReady).filter(t=>this.componentsReady[t])})}async _setupPyTorchAPI(){this.emit("pytorch:setup:start");try{this.compute.availableStrategies.has("webgpu")&&(l.O.setComputeEngine(this.compute.webgpu),this.tensorBridge=(0,m.nn)(this.compute.webgpu));const t=(0,u.pH)();(0,u.EH)(t),d.A.debug("Installing PyTorch polyfill from extracted module"),await this.runtime.runPython(t,{captureOutput:!1}),this.torchAPI=this.runtime.getGlobal("torch"),this.numpy=this.runtime.getGlobal("np"),this.emit("pytorch:setup:complete")}catch(t){throw this.emit("pytorch:setup:error",{error:t}),new Error(`PyTorch API setup failed: ${t.message}`)}}async _validateSystemReadiness(){this.emit("validation:start");const t=[{name:"runtime",check:()=>this.runtime.isReady},{name:"compute",check:()=>this.compute.isInitialized},{name:"memory",check:()=>this.memory.getStats().memoryUsage>=0},{name:"security",check:()=>this.security.getStats().totalValidations>=0},{name:"pytorch",check:()=>null!==this.torchAPI}],e=[];for(const{name:i,check:s}of t)try{const t=s();if(e.push({name:i,passed:t,error:null}),!t)throw new Error(`${i} component failed readiness check`)}catch(t){throw e.push({name:i,passed:!1,error:t.message}),new Error(`System validation failed for ${i}: ${t.message}`)}this.emit("validation:complete",{results:e})}_validateConfig(t){const e=["pyodideIndexURL","maxMemoryMB"];for(const i of e)if(void 0===t[i])throw new Error(`Required configuration missing: ${i}`);if(t.maxMemoryMB<=0||t.maxMemoryMB>4096)throw new Error(`maxMemoryMB must be between 1 and 4096, got ${t.maxMemoryMB}`);return t}_forwardEvents(t,e){const i=["error","warning","init:complete","init:error","cleanup:complete","cleanup:error"];for(const s of i)t.on(s,t=>{this.emit(`${e}:${s}`,t)})}_setupGlobalErrorHandling(){this.on("error",t=>{this.lastError=t,this.errorCount++});const t=["security:error","memory:error","runtime:error","compute:error"];for(const e of t)this.on(e,t=>{this.emit("error",new Error(`${e}: ${t.error?.message||"Unknown error"}`))})}_updateOperationStats(t){this.stats.operations++,this.stats.totalExecutionTime+=t,this.stats.averageExecutionTime=this.stats.totalExecutionTime/this.stats.operations,this.memory&&(this.stats.memoryUsage=this.memory.getStats().memoryUsageMB)}_generateOperationId(){return`greed_op_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}}const y=p}}]);