diff --git a/.eslintrc.json b/.eslintrc.json index d2b15459..36cde35a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -8,7 +8,7 @@ "ecmaVersion": "latest", "sourceType": "module" }, - "ignorePatterns": ["__template.js", "xworker.js", "esm/python/*.js", "esm/3rd-party/*"], + "ignorePatterns": ["__template.js", "xworker.js", "esm/python/*.js", "esm/3rd-party/*", "esm/interpreter/pyodide_graph.js"], "rules": { "object-curly-spacing": ["error", "always"], "quotes": ["error", "single"] diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 3f576f3e..82dea384 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -15,13 +15,18 @@ jobs: node-version: [20] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v3 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v3 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' + - run: python -m venv env + - run: source env/bin/activate + - run: pip install --upgrade pip + - run: pip install --ignore-requires-python python-minifier + - run: pip install setuptools - run: npm ci - - run: npx playwright install + - run: npx playwright install chromium - run: npm run build - run: npm run test diff --git a/.gitignore b/.gitignore index 61bbadc9..44a06fc4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store coverage/ +env/ node_modules/ test-results/ cjs/* diff --git a/README.md b/README.md index 96a7b2cc..3a2b5098 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,9 @@ This project requires some automatic artifact creation to: Accordingly, to build latest project: ```sh +# activate once the Python env (to use pyminify) +. env.sh # or source env.sh + # create all artifacts needed to test core npm run build diff --git a/docs/README.md b/docs/README.md index 9452ef53..de52bdd1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -381,7 +381,7 @@ The module is registered within the interpreter as *JS* module and it offers var | js_modules | `from polyscript import js_modules` | described in the [Extra config Features](#extra-config-features) part. | | lazy_py_modules | `from polyscript import lazy_py_modules` | allows, only in *Python* related interpreters, and without needing static config entries, to import lazily any available module. | storage | `from polyscript import storage` | a utility to instantiate a named [idb-map](https://github.com/WebReflection/idb-map/#readme) that can be consumed synchronously. -| JSON | `from polyscript import JSON` | a utility to stringify/parse more complex or recursive data via [@ungap/structured-clone/json](https://github.com/ungap/structured-clone/#readme). +| JSON | `from polyscript import JSON` | a utility to stringify/parse more complex or recursive data. #### lazy_py_modules @@ -550,6 +550,7 @@ Within a *Worker* execution context, the `xworker` exposes the following feature | sync | `xworker.sync.from_main(1, "two")` | Executes the exposed `from_main` function in the main thread. Returns synchronously its result when *SharedArrayBuffer* can work synchronously. Returns asynchronously otherwise exposed callbacsk from the *main* thread. | | window | `xworker.window.document.title = 'Worker'` | Differently from *pyodide* or *micropython* `import js`, this field allows every single possible operation directly in the main thread when that is possible (*SharedArrayBuffer* either available or polyfilled for `sync` operations too). It does not refer to the local `js` environment the interpreter might have decided to expose, it is a proxy to handle otherwise impossible operations in the main thread, such as manipulating the *DOM*, reading `localStorage` otherwise not available in workers, change location or anything else usually possible to do in the main thread. | | isWindowProxy | `xworker.isWindowProxy(ref)` | **Advanced** - Allows introspection of *JS* references, helping differentiating between local worker references, and main thread global JS references. This is valid both for non primitive objects (array, dictionaries) as well as functions, as functions are also enabled via `xworker.window` in both ways: we can add a listener from the worker or invoke a function in the main. Please note that functions passed to the main thread will always be invoked asynchronously. | +| unstuck | `script.xworker.unstuck()` | On the **main** thread only, hints code running in the Worker that it should throw an error: `while unstuck(True): pass` as example, will exit that loop when on the *main* `xworker.unstuck()` is invoked. See [test/unstuck](https://github.com/pyscript/polyscript/blob/main/test/unstuck/index.html) for a complete example. | ```python diff --git a/docs/index.js b/docs/index.js index 0b7dce43..29dde1e1 100644 --- a/docs/index.js +++ b/docs/index.js @@ -1,2 +1,2 @@ -const e=(e,t=document)=>[...t.querySelectorAll(e)],t=(e,t=document)=>{const n=(new XPathEvaluator).createExpression(e).evaluate(t,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE),r=[];for(let e=0,{snapshotLength:t}=n;e"function"==typeof e,s={get:(e,t)=>n.hasOwnProperty(t)?((e,t,{get:n,value:s})=>n||!r(s)?e.then((e=>e[t])):(...n)=>e.then((e=>e[t](...n))))(e,t,n[t]):((e,t)=>r(t)?t.bind(e):t)(e,e[t])};var o=(e,...t)=>new Proxy(fetch(e,...t),s);const a=new Proxy(new Map,{get:(e,t)=>(e.has(t)||e.set(t,Promise.withResolvers()),e.get(t))}),i=new Set(["__dict__","constructor","get","has","includes","next","set","then"]),c=new Proxy(Object.freeze({}),{get:(e,t)=>"string"!=typeof t||i.has(t)?void 0:a[t].promise.then((e=>e.sync))}),l="object"==typeof self?self:globalThis,u=e=>((e,t)=>{const n=(t,n)=>(e.set(n,t),t),r=s=>{if(e.has(s))return e.get(s);const[o,a]=t[s];switch(o){case 0:case-1:return n(a,s);case 1:{const e=n([],s);for(const t of a)e.push(r(t));return e}case 2:{const e=n({},s);for(const[t,n]of a)e[r(t)]=r(n);return e}case 3:return n(new Date(a),s);case 4:{const{source:e,flags:t}=a;return n(new RegExp(e,t),s)}case 5:{const e=n(new Map,s);for(const[t,n]of a)e.set(r(t),r(n));return e}case 6:{const e=n(new Set,s);for(const t of a)e.add(r(t));return e}case 7:{const{name:e,message:t}=a;return n(new l[e](t),s)}case 8:return n(BigInt(a),s);case"BigInt":return n(Object(BigInt(a)),s)}return n(new l[o](a),s)};return r})(new Map,e)(0),p="",{toString:f}={},{keys:h}=Object,d=e=>{const t=typeof e;if("object"!==t||!e)return[0,t];const n=f.call(e).slice(8,-1);switch(n){case"Array":return[1,p];case"Object":return[2,p];case"Date":return[3,p];case"RegExp":return[4,p];case"Map":return[5,p];case"Set":return[6,p]}return n.includes("Array")?[1,n]:n.includes("Error")?[7,n]:[2,n]},g=([e,t])=>0===e&&("function"===t||"symbol"===t),y=(e,{json:t,lossy:n}={})=>{const r=[];return((e,t,n,r)=>{const s=(e,t)=>{const s=r.push(e)-1;return n.set(t,s),s},o=r=>{if(n.has(r))return n.get(r);let[a,i]=d(r);switch(a){case 0:{let t=r;switch(i){case"bigint":a=8,t=r.toString();break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+i);t=null;break;case"undefined":return s([-1],r)}return s([a,t],r)}case 1:{if(i)return s([i,[...r]],r);const e=[],t=s([a,e],r);for(const t of r)e.push(o(t));return t}case 2:{if(i)switch(i){case"BigInt":return s([i,r.toString()],r);case"Boolean":case"Number":case"String":return s([i,r.valueOf()],r)}if(t&&"toJSON"in r)return o(r.toJSON());const n=[],c=s([a,n],r);for(const t of h(r))!e&&g(d(r[t]))||n.push([o(t),o(r[t])]);return c}case 3:return s([a,r.toISOString()],r);case 4:{const{source:e,flags:t}=r;return s([a,{source:e,flags:t}],r)}case 5:{const t=[],n=s([a,t],r);for(const[n,s]of r)(e||!g(d(n))&&!g(d(s)))&&t.push([o(n),o(s)]);return n}case 6:{const t=[],n=s([a,t],r);for(const n of r)!e&&g(d(n))||t.push(o(n));return n}}const{message:c}=r;return s([a,{name:i,message:c}],r)};return o})(!(t||n),!!t,new Map,r)(e),r},{parse:w,stringify:m}=JSON,_={json:!0,lossy:!0};var b=Object.freeze({__proto__:null,parse:e=>u(w(e)),stringify:e=>m(y(e,_))});const v="array",E="function",k="null",x="number",T="object",S="string",A="symbol",j="undefined";let O=0;const R=new Map,$=new Map,P=e=>$.get(e),I=e=>{if(!R.has(e)){let t;for(;$.has(t=O++););R.set(e,t),$.set(t,e)}return R.get(e)},{ArrayBuffer:M,Atomics:N,Promise:F}=globalThis,{isArray:W}=Array,{create:H,getPrototypeOf:D,values:L}=Object,C=D(Int32Array),U=H(N),B=()=>F.withResolvers();let q=0;const J=new Map,z=(e,t)=>class extends e{constructor(e,...n){super(e,...n),e instanceof t&&J.set(this,[q++,0,B()])}},G=new WeakSet,Y=e=>(G.add(e),e),K=(e,t)=>{const{data:n}=e,r=W(n)&&(n.at(0)===t||0===n.at(1)&&!t);return r&&(e.stopImmediatePropagation(),e.preventDefault()),r},X=e=>null!==e&&"object"==typeof e&&!G.has(e),V=new WeakMap,Q=(e,t,n)=>{if(J.has(e))t.set(e,J.get(e)[0]);else if(!(e instanceof C||e instanceof M))for(const r of L(e))X(r)&&!n.has(r)&&(n.add(r),Q(r,t,n))},Z=(...e)=>({value:new F((t=>{let n=new Worker("data:application/javascript,onmessage%3De%3D%3EpostMessage(!Atomics.wait(...e.data))");n.onmessage=()=>t("ok"),n.postMessage(e)}))}),ee=(e,t,n)=>{for(const[n,r]of t)V.set(n,[r,e.currentTarget]);(({currentTarget:e,type:t,origin:n,lastEventId:r,source:s,ports:o},a)=>{e.dispatchEvent(new MessageEvent(t,{data:a,origin:n,lastEventId:r,source:s,ports:o}))})(e,n)};let{BigInt64Array:te,Int32Array:ne,SharedArrayBuffer:re,Worker:se}=globalThis,oe=e=>e,ae=!1;const ie=e=>({...e,type:"module"});try{new re(4),se=class extends se{constructor(e,t){super(e,ie(t))}},U.waitAsync||(U.waitAsync=Z)}catch(e){const t=crypto.randomUUID(),n=new Map,r=(e,t,n,...r)=>{e.addEventListener(t,n,...r)},s=({serviceWorker:e},s,o)=>{let a,i=!0;r(e,"message",(e=>{if(K(e,t)){const[r,s,o]=e.data,i=[s,o].join(","),c=e=>{n.delete(i),a.postMessage([t,s,o,e])},l=n.get(i);if(l)c(l);else{const{promise:e,resolve:t}=B();n.set(i,t),e.then(c)}}})),e.getRegistration(s).then((t=>t??e.register(s))).then((function t(n){i=i&&!!e.controller,a=n.installing||n.waiting||n.active,"activated"===a.state?i?o():location.reload():r(a,"statechange",(()=>t(n)),{once:!0})}))};oe=Y,ae=!0,U.notify=(e,r)=>{const[s,o]=(e=>V.get(e))(e),a=[s,r].join(","),i=n.get(a);return i?i(e):n.set(a,e),o.postMessage([t,1,e,s,r]),0},U.waitAsync=(e,...t)=>{const[n,r]=((e,t)=>{const n=J.get(e),[r,s,{promise:o}]=n;return n[1]=t,[r,o]})(e,...t);return{value:r}},re=class extends M{},te=z(te,re),ne=z(ne,re);let o=null;se=class extends se{constructor(e,n){let a=n?.serviceWorker||"";if(a){if(a=new URL(a,location.href).href,n={...n,serviceWorker:a},!o){const{promise:e,resolve:t}=B();s(navigator,a,t),o=e}o.then((()=>super.postMessage([t,3])))}super(e,ie(n)),super.postMessage([t,0,n]),r(this,"message",(e=>{if(K(e,t)){const[t,n,...r]=e.data;switch(n){case 1:((e,t,n)=>{for(const[r,[s,o,{resolve:a}]]of J)if(t===s&&n===o){for(let t=0;t{const n=new Map;return X(t)&&Q(t,n,new Set),n.size?[e,2,n,t]:t})(t,e),...n)}}}const{BYTES_PER_ELEMENT:ce}=Int32Array,{BYTES_PER_ELEMENT:le}=Uint16Array,{notify:ue}=U,pe=new TextDecoder("utf-16"),fe=new WeakSet,he=(...e)=>(fe.add(e),e);let de="";let ge=0;const ye=([e,t,n,r,s,o,a,i,c],l)=>(...u)=>{let p=""!==de,f=0;p&&(f=((e,t)=>setTimeout(console.warn,1e3,`💀🔒 - proxy.${e}() in proxy.${t}()`))(l,de));const h=ge++,d=[];fe.has(u.at(-1)||d)&&fe.delete(d=u.pop());const g=n(i?u.map(i):u);let y=t(2*ce);return a([e,2,l,h,y,g,r],{transfer:d}),c(y,0).value.then((()=>{p&&clearTimeout(f);const n=y[1];if(!n)return;const r=le*n;return y=t(r+r%ce),a([e,1,h,y]),c(y,0).value.then((()=>{const e=new Uint16Array(y.buffer),t=o?e.subarray(0,n):e.slice(0,n);return s(pe.decode(t))}))}))};var we=({parse:e,stringify:t,transform:n}=JSON)=>{const r=((e,t)=>async(n,r,[s,o,a,i,c])=>{c&&(de=s);try{const s=await n(...i);if(void 0!==s){const n=e(t?t(s):s);r.set(o,n),a[1]=n.length}}finally{c&&(de=""),a[0]=1,ue(a,0)}})(t,n),s=crypto.randomUUID();return{Worker:class extends se{constructor(t,o){const a=new Map,i=new Map;super(t,o),this.proxy=((e,t)=>new Proxy(t,{get:(t,n)=>{let r;return"then"!==n&&(r=t.get(n),r||(r=ye(e,n),t.set(n,r))),r},set:(e,t,n)=>"then"!==t&&!!e.set(t,n)}))([s,e=>new ne(new re(e)),oe,!1,e,ae,(...e)=>this.postMessage(...e),n,U.waitAsync],a),this.postMessage(oe([s,0,o])),this.addEventListener("message",(e=>{if(K(e,s)){const[t,n,...s]=e.data;switch(n){case 2:((e,t,n,r)=>{const[s]=r,o=n.get(s);if(!o)throw new Error(`Unknown proxy.${s}()`);e(o,t,r)})(r,i,a,s);break;case 1:((e,[t,n])=>{const r=e.get(t);e.delete(t);for(let e=new Uint16Array(n.buffer),t=0,{length:s}=r;t[e,t])));const _e="ownKeys",be="destruct",{[_e]:ve}=Reflect,Ee=new Map(ve(Symbol).filter((e=>typeof Symbol[e]===A)).map((e=>[Symbol[e],e]))),ke=e=>Ee.get(e)||`.${Symbol.keyFor(e)||""}`,xe="42fb1e9a-1373-441e-813f-357c3deaee87",Te="M"+xe,Se="W"+xe,Ae=new FinalizationRegistry((([e,t,n])=>{n&&console.debug(`Held value ${String(t)} not relevant anymore`),e(t)})),je=Object.create(null),Oe=(e,t,{debug:n,handler:r,return:s,token:o=e}=je)=>{const a=s||new Proxy(e,r||je),i=[a,[t,e,!!n]];return!1!==o&&i.push(o),Ae.register(...i),a},{addEventListener:Re}=EventTarget.prototype,$e=new WeakMap;Reflect.defineProperty(EventTarget.prototype,"addEventListener",{value(e,t,...n){const r=n.at(0)?.invoke;if(r){let t=$e.get(this);t||(t=new Map,$e.set(this,t)),t.set(e,[].concat(r)),delete n[0].invoke}return Re.call(this,e,t,...n)}});const{isArray:Pe}=Array;const{url:Ie}=import.meta,Me=/import\((['"])([^)]+?\.js)\1\)/g,Ne=(e,t,n)=>`import(${t}${new URL(n,Ie).href}${t})`,{Worker:Fe}=(e=>{const t=we(e),n=e=>{const t=typeof e;switch(t){case T:return null===e?[me[k],e]:e===globalThis?[me[T],null]:Pe(e)?[me[v],I(e)]:[me[T],e instanceof C?e:I(e)];case E:return[me[E],I(e)];case A:return[me[A],ke(e)];default:return[me[t],e]}};class r extends t.Worker{constructor(e,t){const{proxy:r}=super(e,t),{[Se]:s}=r,o=new Map,a=e=>{o.delete(e),s(be,e)},i=([e,t])=>{switch(e){case me[T]:if(null===t)return globalThis;if(typeof t===x)return P(t);if(!(t instanceof C))for(const e in t)t[e]=i(t[e]);return t;case me[v]:return typeof t===x?P(t):t.map(i);case me[E]:switch(typeof t){case x:return P(t);case S:{let e=o.get(t)?.deref();return e||(e=Oe(t,a,{token:!1,return:function(...e){return e.length&&e[0]instanceof Event&&(e=>{const{currentTarget:t,target:n,type:r}=e,s=$e.get(t||n)?.get(r);if(s)for(const t of s)e[t]()})(e[0]),s("apply",t,n(this),e.map(n)).then(i)}}),o.set(t,new WeakRef(e))),e}}case me[A]:return(e=>{if(e.startsWith("."))return Symbol.for(e.slice(1));for(const[t,n]of Ee)if(n===e)return t})(t);default:return t}};r[Te]=(e,t,...r)=>{if(e===be)(e=>{const[t,n]=typeof e===x?[$,R]:[R,$],r=t.has(e);r&&(n.delete(t.get(e)),t.delete(e))})(t);else{const s=Reflect[e],o=null==t?globalThis:P(t);switch(e){case"defineProperty":{const[e,t]=r.map(i);return n(s(o,e,t))}case"getOwnPropertyDescriptor":{const e=s(o,...r.map(i));if(e){const{get:t,set:r,value:s}=e;t&&(e.get=n(t)),r&&(e.set=n(r)),s&&(e.value=n(s))}return[me[e?T:j],e]}case _e:return[me[v],s(o).map(n)];default:return((e,t,r)=>n(e(t,...r.map(i))))(s,o,r)}}}}}return{...t,Worker:r}})(b);const We=new WeakMap,He=e=>{const t=e||console,n={buffered:Le,stderr:(t.stderr||console.error).bind(t),stdout:(t.stdout||console.log).bind(t)};return{stderr:(...e)=>n.stderr(...e),stdout:(...e)=>n.stdout(...e),async get(e){const t=await e;return We.set(t,n),t}}},De=new TextDecoder,Le=(e,t=10)=>{const n=[];return r=>{if(r instanceof Uint8Array)for(const s of r)s===t?e(De.decode(new Uint8Array(n.splice(0)))):n.push(s);else e(r)}},Ce=new Map,Ue=e=>Ce.get(e),Be=(e,t)=>{try{return Function("require",t)(Ue)}catch(t){We.get(e).stderr(t)}};var qe={type:"dummy",module:()=>"data:application/javascript,",engine:e=>He().get(e),registerJSModule(e,t,n){Ce.set(t,n)},run:Be,runAsync:Be,runEvent:async(e,t,n)=>{try{await Function("require","e",`return ${t}(e)`)(Ue,n)}catch(t){We.get(e).stderr(t)}},transform:(e,t)=>t,writeFile(){}};Promise.withResolvers||(Promise.withResolvers=function(){var e,t,n=new this((function(n,r){e=n,t=r}));return{resolve:e,reject:t,promise:n}});const Je={object(...e){return this.string(function(e){for(var t=e[0],n=1,r=arguments.length;n",">":">","'":"'","'":"'",""":'"',""":'"'},Ke=e=>Ye[e],Xe=(e,...t)=>Je[typeof e](e,...t),Ve=e=>ze.call(e,Ge,Ke),{isArray:Qe}=Array,{assign:Ze,create:et,defineProperties:tt,defineProperty:nt,entries:rt}=Object,{all:st,resolve:ot}=new Proxy(Promise,{get:(e,t)=>e[t].bind(e)}),at=(e,t=location.href)=>new URL(e,t.replace(/^blob:/,"")).href;let it=0;const ct=(e,t)=>({id:e.id||(e.id=`${t}-w${it++}`),tag:e.tagName}),lt=(e,t,n,r=!1,s=CustomEvent)=>{e.dispatchEvent(new s(`${t}:${n}`,{bubbles:!0,detail:{worker:r}}))},ut=(e,t,n,r)=>({type:t,config:n,interpreter:r,io:We.get(r),run:(t,...n)=>e.run(r,t,...n),runAsync:(t,...n)=>e.runAsync(r,t,...n),runEvent:(...t)=>e.runEvent(r,...t)}),pt=e=>e.replace(/^(?:\n|\r\n)/,""),ft=(e,t,n,r)=>{const s=e[t].bind(e);e[t]="run"===t?(e,t,...o)=>{n&&s(e,n,...o);const a=s(e,pt(t),...o);return r&&s(e,r,...o),a}:async(e,t,...o)=>{n&&await s(e,n,...o);const a=await s(e,pt(t),...o);return r&&await s(e,r,...o),a}},ht=Symbol.for("polyscript.js_modules"),dt=new Map;nt(globalThis,ht,{value:dt});const gt=new Proxy(dt,{get:(e,t)=>e.get(t),has:(e,t)=>e.has(t),ownKeys:e=>[...e.keys()]}),yt=(e,t)=>!t.startsWith("_"),wt=(e,t)=>new Proxy(e,{has:yt,get:(e,n)=>e[t][n]}),mt=(e,t,n,r)=>{if("pyodide"===e)return;const s="polyscript.js_modules";for(const e of Reflect.ownKeys(r))t.registerJSModule(n,`${s}.${e}`,wt(r,e));t.registerJSModule(n,s,r)},_t=(e,t)=>import(e).then((e=>{dt.set(t,{...e})})),bt=e=>new Promise(((t,n)=>{document.querySelector(`link[rel="stylesheet"][href="${e}"]`)?t():document.head.append(Ze(document.createElement("link"),{rel:"stylesheet",href:e,onload:t,onerror:n}))})),vt=e=>/\.css$/i.test(new URL(e).pathname),Et=!globalThis.window,kt=({FS:e,PATH:t,PATH_FS:n},r,s)=>{const o=n.resolve(r),a=t.dirname(o);return e.mkdirTree?e.mkdirTree(a):Tt(e,a),e.writeFile(o,new Uint8Array(s),{canOwn:!0})},xt=e=>{const t=e.split("/");return t.pop(),t.join("/")},Tt=(e,t)=>{const n=[];for(const r of t.split("/"))"."!==r&&".."!==r&&(n.push(r),r&&e.mkdir(n.join("/")))},St=(e,t)=>{const n=[];for(const e of t.split("/"))switch(e){case"":case".":break;case"..":n.pop();break;default:n.push(e)}return[e.cwd()].concat(n).join("/").replace(/^\/+/,"/")},At=e=>{const t=e.map((e=>e.trim().replace(/(^[/]*|[/]*$)/g,""))).filter((e=>""!==e&&"."!==e)).join("/");return e[0].startsWith("/")?`/${t}`:t},jt=(e,t)=>o(at(e,t)).arrayBuffer(),Ot=(e,t,n,r)=>st((e=>{for(const{files:t,to_file:n,from:r=""}of e){if(void 0!==t&&void 0!==n)throw new Error("Cannot use 'to_file' and 'files' parameters together!");if(void 0===t&&void 0===n&&r.endsWith("/"))throw new Error(`Couldn't determine the filename from the path ${r}, please supply 'to_file' parameter.`)}return e.flatMap((({from:e="",to_folder:t=".",to_file:n,files:r})=>{if(Qe(r))return r.map((n=>({url:At([e,n]),path:At([t,n])})));const s=n||e.slice(1+e.lastIndexOf("/"));return[{url:e,path:At([t,s])}]}))})(n).map((({url:n,path:s})=>jt(n,r).then((n=>e.writeFile(t,s,n)))))),Rt=(e,t)=>t.endsWith("/")?`${t}${e.split("/").pop()}`:t,$t=(e,t)=>e.replace(/\{.+?\}/g,(e=>{if(!t.has(e))throw new SyntaxError(`Invalid template: ${e}`);return t.get(e)})),Pt=(e,t,n,r)=>st((e=>{const t=new Map,n=new Set,r=[];for(const[s,o]of rt(e))if(/^\{.+\}$/.test(s)){if(t.has(s))throw new SyntaxError(`Duplicated template: ${s}`);t.set(s,$t(o,t))}else{const e=$t(s,t),a=Rt(e,$t(o||"./",t));if(n.has(a))throw new SyntaxError(`Duplicated destination: ${a}`);n.add(a),r.push({url:e,path:a})}return r})(n).map((({url:n,path:s})=>jt(n,r).then((r=>e.writeFile(t,s,r,n)))))),It=({main:e,worker:t},n)=>{const r=[];if(t&&Et)for(let[e,s]of rt(t))e=at(e,n),r.push(_t(e,s));if(e&&!Et)for(let[t,s]of rt(e))t=at(t,n),vt(t)?bt(t):r.push(_t(t,s));return st(r)},{assign:Mt}=Object,Nt="entries",Ft="readonly",Wt="readwrite",Ht={durability:"default",prefix:"IDBMap"},Dt=({target:{result:e}})=>e;class Lt extends EventTarget{#e;#t;#n;async#r(e,t){const n=(await this.#e).transaction(Nt,t,this.#t);return new Promise(((t,r)=>Mt(e(n.objectStore(Nt)),{onsuccess:t,onerror:r})))}constructor(e,{durability:t=Ht.durability,prefix:n=Ht.prefix}=Ht){super(),this.#n=n,this.#t={durability:t},this.#e=new Promise(((t,n)=>{Mt(indexedDB.open(`${this.#n}/${e}`),{onupgradeneeded({target:{result:e,transaction:n}}){e.objectStoreNames.length||e.createObjectStore(Nt),n.oncomplete=()=>t(e)},onsuccess(e){t(Dt(e))},onerror(e){n(e),this.dispatchEvent(e)}})})).then((e=>{const t=this.dispatchEvent.bind(this);for(const n in e)n.startsWith("on")&&(e[n]=t);return e}))}dispatchEvent(e){const{type:t,message:n,isTrusted:r}=e;return super.dispatchEvent(r?Mt(new Event(t),{message:n}):e)}async close(){(await this.#e).close()}get size(){return this.#r((e=>e.count()),Ft).then(Dt)}async clear(){await this.#r((e=>e.clear()),Wt)}async delete(e){await this.#r((t=>t.delete(e)),Wt)}async entries(){const e=await this.keys();return Promise.all(e.map((e=>this.get(e).then((t=>[e,t])))))}async forEach(e,t=this){for(const[n,r]of await this.entries())await e.call(t,r,n,this)}async get(e){return await this.#r((t=>t.get(e)),Ft).then(Dt)}async has(e){return void 0!==await this.#r((t=>t.getKey(e)),Ft).then(Dt)}async keys(){return await this.#r((e=>e.getAllKeys()),Ft).then(Dt)}async set(e,t){return await this.#r((n=>n.put(t,e)),Wt),this}async values(){const e=await this.keys();return Promise.all(e.map((e=>this.get(e))))}get[Symbol.toStringTag](){return this.#n}}class Ct extends Map{#s;#o;constructor(...e){super(),this.#s=new Lt(...e),this.#o=this.#s.entries().then((e=>{for(const[t,n]of e)super.set(t,n)}))}async sync(){await this.#o}clear(){return this.#o=this.#o.then((()=>this.#s.clear())),super.clear()}delete(e){return this.#o=this.#o.then((()=>this.#s.delete(e))),super.delete(e)}set(e,t){return this.#o=this.#o.then((()=>this.#s.set(e,t))),super.set(e,t)}}const Ut=new WeakMap,Bt=(e,t,n)=>{"polyscript"===t&&(n.lazy_py_modules=async(...t)=>(await Ut.get(e)(t),t.map((t=>e.pyimport(t)))),n.storage=async e=>{const t=new Ct(e);return await t.sync(),t},n.JSON=b),e.registerJsModule(t,n)},qt=(e,t)=>{if(e.endsWith("/*")){if(/\.(zip|whl|tgz|tar(?:\.gz)?)$/.test(t))return RegExp.$1;throw new Error(`Unsupported archive ${t}`)}return""},Jt=(e,t,...n)=>{try{return e.runPython(Xe(t),...n)}catch(t){We.get(e).stderr(t)}},zt=async(e,t,...n)=>{try{return await e.runPythonAsync(Xe(t),...n)}catch(t){We.get(e).stderr(t)}},Gt=async(e,t,n)=>{const[r,...s]=t.split(".");let o,a=e.globals.get(r);for(const e of s)[o,a]=[a,a[e]];try{await a.call(o,n)}catch(t){We.get(e).stderr(t)}};var Yt=(new TextEncoder).encode('from uio import StringIO\nimport sys\n\nclass Response:\n def __init__(self, f):\n self.raw = f\n self.encoding = "utf-8"\n self._cached = None\n\n def close(self):\n if self.raw:\n self.raw.close()\n self.raw = None\n self._cached = None\n\n @property\n def content(self):\n if self._cached is None:\n try:\n self._cached = self.raw.read()\n finally:\n self.raw.close()\n self.raw = None\n return self._cached\n\n @property\n def text(self):\n return str(self.content, self.encoding)\n\n def json(self):\n import ujson\n\n return ujson.loads(self.content)\n\n\n# TODO try to support streaming xhr requests, a-la pyodide-http\nHEADERS_TO_IGNORE = ("user-agent",)\n\n\ntry:\n import js\nexcept Exception as err:\n raise OSError("This version of urequests can only be used in the browser")\n\n# TODO try to support streaming xhr requests, a-la pyodide-http\n\nHEADERS_TO_IGNORE = ("user-agent",)\n\n\ndef request(\n method,\n url,\n data=None,\n json=None,\n headers={},\n stream=None,\n auth=None,\n timeout=None,\n parse_headers=True,\n):\n from js import XMLHttpRequest\n\n xhr = XMLHttpRequest.new()\n xhr.withCredentials = False\n\n if auth is not None:\n import ubinascii\n\n username, password = auth\n xhr.open(method, url, False, username, password)\n else:\n xhr.open(method, url, False)\n\n for name, value in headers.items():\n if name.lower() not in HEADERS_TO_IGNORE:\n xhr.setRequestHeader(name, value)\n\n if timeout:\n xhr.timeout = int(timeout * 1000)\n\n if json is not None:\n assert data is None\n import ujson\n\n data = ujson.dumps(json)\n # s.write(b"Content-Type: application/json\\r\\n")\n xhr.setRequestHeader("Content-Type", "application/json")\n\n xhr.send(data)\n\n # Emulates the construction process in the original urequests\n resp = Response(StringIO(xhr.responseText))\n resp.status_code = xhr.status\n resp.reason = xhr.statusText\n resp.headers = xhr.getAllResponseHeaders()\n\n return resp\n\n\n# Other methods - head, post, put, patch, delete - are not used by\n# mip and therefore not included\n\n\ndef get(url, **kw):\n return request("GET", url, **kw)\n\n\n# Content below this line is from the Micropython MIP package and is covered\n# by the applicable MIT license:\n# \n# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING \n# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \n# DEALINGS IN THE SOFTWARE.\n\n# MicroPython package installer\n# MIT license; Copyright (c) 2022 Jim Mussared\n\n\n_PACKAGE_INDEX = const("https://micropython.org/pi/v2")\n_CHUNK_SIZE = 128\n\n\n# This implements os.makedirs(os.dirname(path))\ndef _ensure_path_exists(path):\n import os\n\n split = path.split("/")\n\n # Handle paths starting with "/".\n if not split[0]:\n split.pop(0)\n split[0] = "/" + split[0]\n\n prefix = ""\n for i in range(len(split) - 1):\n prefix += split[i]\n try:\n os.stat(prefix)\n except:\n os.mkdir(prefix)\n prefix += "/"\n\n\n# Copy from src (stream) to dest (function-taking-bytes)\ndef _chunk(src, dest):\n buf = memoryview(bytearray(_CHUNK_SIZE))\n while True:\n n = src.readinto(buf)\n if n == 0:\n break\n dest(buf if n == _CHUNK_SIZE else buf[:n])\n\n\n# Check if the specified path exists and matches the hash.\ndef _check_exists(path, short_hash):\n import os\n\n try:\n import binascii\n import hashlib\n\n with open(path, "rb") as f:\n hs256 = hashlib.sha256()\n _chunk(f, hs256.update)\n existing_hash = str(binascii.hexlify(hs256.digest())[: len(short_hash)], "utf-8")\n return existing_hash == short_hash\n except:\n return False\n\n\ndef _rewrite_url(url, branch=None):\n if not branch:\n branch = "HEAD"\n if url.startswith("github:"):\n url = url[7:].split("/")\n url = (\n "https://raw.githubusercontent.com/"\n + url[0]\n + "/"\n + url[1]\n + "/"\n + branch\n + "/"\n + "/".join(url[2:])\n )\n return url\n\n\ndef _download_file(url, dest):\n response = get(url)\n try:\n if response.status_code != 200:\n print("Error", response.status_code, "requesting", url)\n return False\n\n print("Copying:", dest)\n _ensure_path_exists(dest)\n with open(dest, "wb") as f:\n _chunk(response.raw, f.write)\n\n return True\n finally:\n response.close()\n\n\ndef _install_json(package_json_url, index, target, version, mpy):\n response = get(_rewrite_url(package_json_url, version))\n try:\n if response.status_code != 200:\n print("Package not found:", package_json_url)\n return False\n\n package_json = response.json()\n finally:\n response.close()\n for target_path, short_hash in package_json.get("hashes", ()):\n fs_target_path = target + "/" + target_path\n if _check_exists(fs_target_path, short_hash):\n print("Exists:", fs_target_path)\n else:\n file_url = "{}/file/{}/{}".format(index, short_hash[:2], short_hash)\n if not _download_file(file_url, fs_target_path):\n print("File not found: {} {}".format(target_path, short_hash))\n return False\n for target_path, url in package_json.get("urls", ()):\n fs_target_path = target + "/" + target_path\n if not _download_file(_rewrite_url(url, version), fs_target_path):\n print("File not found: {} {}".format(target_path, url))\n return False\n for dep, dep_version in package_json.get("deps", ()):\n if not _install_package(dep, index, target, dep_version, mpy):\n return False\n return True\n\n\ndef _install_package(package, index, target, version, mpy):\n if (\n package.startswith("http://")\n or package.startswith("https://")\n or package.startswith("github:")\n ):\n if package.endswith(".py") or package.endswith(".mpy"):\n print("Downloading {} to {}".format(package, target))\n return _download_file(\n _rewrite_url(package, version), target + "/" + package.rsplit("/")[-1]\n )\n else:\n if not package.endswith(".json"):\n if not package.endswith("/"):\n package += "/"\n package += "package.json"\n print("Installing {} to {}".format(package, target))\n else:\n if not version:\n version = "latest"\n print("Installing {} ({}) from {} to {}".format(package, version, index, target))\n\n mpy_version = (\n sys.implementation._mpy & 0xFF if mpy and hasattr(sys.implementation, "_mpy") else "py"\n )\n\n # WARNING: mpy_version fails miserably with 1.22.0-380\n package = "{}/package/{}/{}/{}.json".format(index, "py", package, version)\n\n return _install_json(package, index, target, version, mpy)\n\n\ndef install(package, index=None, target=None, version=None, mpy=True):\n if not target:\n for p in sys.path:\n if p.endswith("/lib"):\n target = p\n break\n else:\n print("Unable to find lib dir in sys.path")\n return\n\n if not index:\n index = _PACKAGE_INDEX\n\n if _install_package(package, index.rstrip("/"), target, version, mpy):\n print("Done")\n else:\n print("Package may be partially installed")\n');const Kt=async e=>(await import("./toml-DiUM0_qs.js")).parse(e),Xt=(e,t)=>{try{e.mkdir(t)}catch(e){}};var Vt={type:"micropython",module:(e="1.24.0-preview-114")=>`https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript@${e}/micropython.mjs`,async engine({loadMicroPython:e},t,n,r){const{stderr:s,stdout:o,get:a}=He({stderr:Le(console.error),stdout:Le(console.log)});n=n.replace(/\.m?js$/,".wasm");const i=await a(e({linebuffer:!1,stderr:s,stdout:o,url:n})),c=Qt.bind(this,i,r);return Ut.set(i,c),t.files&&await Pt(this,i,t.files,r),t.fetch&&await Ot(this,i,t.fetch,r),t.js_modules&&await It(t.js_modules,r),this.writeFile(i,"./mip.py",Yt),t.packages&&await c(t.packages),i},registerJSModule:Bt,run:Jt,runAsync:zt,runEvent:Gt,transform:(e,t)=>e.PyProxy.toJs(t),writeFile:(e,t,n,r)=>{const{FS:s,_module:{PATH:o,PATH_FS:a}}=e,i={FS:s,PATH:o,PATH_FS:a},c=qt(t,r);if(c){const r=t.slice(0,-1);switch("./"!==r&&s.mkdir(r),c){case"whl":case"zip":{const e=new Blob([n],{type:"application/zip"});return import("./zip-gl8b5xR3.js").then((async({BlobReader:t,Uint8ArrayWriter:n,ZipReader:a})=>{const i=new a(new t(e));for(const e of await i.getEntries()){const{directory:t,filename:a}=e,i=r+a;if(t)Xt(s,i);else{Xt(s,o.dirname(i));const t=await e.getData(new n);s.writeFile(i,t,{canOwn:!0})}}i.close()}))}case"tgz":case"tar.gz":{const t="./_.tar.gz";return kt(i,t,n),void e.runPython(`\n import os, gzip, tarfile\n tar = tarfile.TarFile(fileobj=gzip.GzipFile(fileobj=open("${t}", "rb")))\n for f in tar:\n name = f"${r}{f.name}"\n if f.type == tarfile.DIRTYPE:\n if f.name != "./":\n os.mkdir(name.strip("/"))\n else:\n dir = os.path.dirname(name)\n if not os.path.exists(dir):\n os.mkdir(dir)\n source = tar.extractfile(f)\n with open(name, "wb") as dest:\n dest.write(source.read())\n dest.close()\n tar.close()\n os.remove("${t}")\n `)}}}return kt(i,t,n)}};async function Qt(e,t,n){let r;for(const s of n)if(s.endsWith(".whl")){const n=at(s,t),r=await o(n).arrayBuffer();await this.writeFile(e,"./*",r,n)}else r||(r=e.pyimport("mip")),r.install(s)}const Zt={dict_converter:Object.fromEntries};let en=!1;const tn=e=>(...t)=>{try{return en=!0,e(...t)}finally{en=!1}};let nn=!1;const rn=()=>{if(nn)return;nn=!0;const e=new WeakMap,t=e=>e.destroy(),n=n=>{for(let r=0;r`https://cdn.jsdelivr.net/pyodide/v${e}/full/pyodide.mjs`,async engine({loadPyodide:e},t,n,r){Et||"auto"!==t.experimental_create_proxy||rn();const{stderr:s,stdout:o,get:a}=He(),i=n.slice(0,n.lastIndexOf("/")),c=await a(e({stderr:s,stdout:o,indexURL:i})),l=on.bind(c);return Ut.set(c,l),t.files&&await Pt(this,c,t.files,r),t.fetch&&await Ot(this,c,t.fetch,r),t.js_modules&&await It(t.js_modules,r),t.packages&&await l(t.packages),c},registerJSModule:Bt,run:tn(Jt),runAsync:tn(zt),runEvent:tn(Gt),transform:({ffi:{PyProxy:e}},t)=>t instanceof e?t.toJs(Zt):t,writeFile:(e,t,n,r)=>{const s=qt(t,r);if(s)return e.unpackArchive(n,s,{extractDir:t.slice(0,-1)});const{FS:o,PATH:a,_module:{PATH_FS:i}}=e;return kt({FS:o,PATH:a,PATH_FS:i},t,n)}};async function on(e){await this.loadPackage("micropip");const t=this.pyimport("micropip");await t.install(e,{keep_going:!0}),t.destroy()}const an="ruby-wasm-wasi",cn=an.replace(/\W+/g,"_");var ln={type:an,experimental:!0,module:(e="2.6.2")=>`https://cdn.jsdelivr.net/npm/@ruby/3.2-wasm-wasi@${e}/dist/browser/+esm`,async engine({DefaultRubyVM:e},t,n,r){n=n.replace(/\/browser\/\+esm$/,"/ruby.wasm");const s=await o(n).arrayBuffer(),a=await WebAssembly.compile(s),{vm:i}=await e(a);return t.files&&await Pt(this,i,t.files,r),t.fetch&&await Ot(this,i,t.fetch,r),t.js_modules&&await It(t.js_modules,r),i},registerJSModule(e,t,n){t=t.replace(/\W+/g,"__");const r=`__module_${cn}_${t}`;globalThis[r]=n,this.run(e,`require "js";$${t}=JS.global[:${r}]`),delete globalThis[r]},run:(e,t,...n)=>e.eval(Xe(t),...n),runAsync:(e,t,...n)=>e.evalAsync(Xe(t),...n),async runEvent(e,t,n){if(/^xworker\.(on\w+)$/.test(t)){const{$1:t}=RegExp,r=`__module_${cn}_event`;globalThis[r]=n,this.run(e,`require "js";$xworker.call("${t}",JS.global[:${r}])`),delete globalThis[r]}else{const r=this.run(e,`method(:${t})`);await r.call(t,e.wrap(n))}},transform:(e,t)=>t,writeFile:()=>{throw new Error(`writeFile is not supported in ${an}`)}};var un={type:"wasmoon",module:(e="1.16.0")=>`https://cdn.jsdelivr.net/npm/wasmoon@${e}/+esm`,async engine({LuaFactory:e,LuaLibraries:t},n,r,s){const{stderr:o,stdout:a,get:i}=He(),c=await i((new e).createEngine());return c.global.getTable(t.Base,(e=>{c.global.setField(e,"print",a),c.global.setField(e,"printErr",o)})),n.files&&await Pt(this,c,n.files,s),n.fetch&&await Ot(this,c,n.fetch,s),n.js_modules&&await It(n.js_modules,s),c},registerJSModule:(e,t,n)=>{e.global.set(t,n)},run:(e,t,...n)=>{try{return e.doStringSync(Xe(t),...n)}catch(t){We.get(e).stderr(t)}},runAsync:async(e,t,...n)=>{try{return await e.doString(Xe(t),...n)}catch(t){We.get(e).stderr(t)}},runEvent:async(e,t,n)=>{const[r,...s]=t.split(".");let o,a=e.global.get(r);for(const e of s)[o,a]=[a,a[e]];try{await a.call(o,n)}catch(t){We.get(e).stderr(t)}},transform:(e,t)=>t,writeFile:({cmodule:{module:{FS:e}}},t,n)=>((e,t,n)=>(Tt(e,xt(t)),t=St(e,t),e.writeFile(t,new Uint8Array(n),{canOwn:!0})))(e,t,n)};const pn=new WeakMap,fn=async(e,t)=>{const{shelter:n,destroy:r,io:s}=pn.get(e),{output:o,result:a}=await n.captureR(Xe(t));for(const{type:e,data:t}of o)s[e](t);return Oe(a,r,{token:!1})};var hn={type:"webr",experimental:!0,module:(e="0.4.0")=>`https://cdn.jsdelivr.net/npm/webr@${e}/dist/webr.mjs`,async engine(e,t,n,r){const{get:s}=He(),o=new e.WebR;await s(o.init().then((()=>o)));const a=await new o.Shelter;return pn.set(o,{module:e,shelter:a,destroy:a.destroy.bind(a),io:We.get(o)}),t.files&&await Pt(this,o,t.files,r),t.fetch&&await Ot(this,o,t.fetch,r),t.js_modules&&await It(t.js_modules,r),o},registerJSModule(e,t){console.warn(`Experimental interpreter: module ${t} is not supported (yet)`)},run:fn,runAsync:fn,async runEvent(e,t,n){await e.evalRVoid(`${t}(event)`,{env:{event:{type:[n.type]}}})},transform:(e,t)=>(console.log("transforming",t),t),writeFile:()=>{}};const dn=new Map,gn=new Map,yn=[],wn=[],mn=new Proxy(new Map,{get(e,t){if(!e.has(t)){const[n,...r]=t.split("@"),s=dn.get(n),o=/^(?:\.?\.?\/|https?:\/\/)/i.test(r)?r.join("@"):s.module(...r);e.set(t,{url:o,module:import(o),engine:s.engine.bind(s)})}const{url:n,module:r,engine:s}=e.get(t);return(e,o)=>r.then((r=>(gn.set(t,e),s(r,e,n,o))))}}),_n=e=>{for(const t of[].concat(e.type))dn.set(t,e),yn.push(`script[type="${t}"]`),wn.push(`${t}-`)};for(const e of[qe,Vt,sn,ln,un,hn])_n(e);const{parse:bn}=JSON,vn=(e,t="./config.txt")=>{let n=typeof e;return"string"===n&&/\.(json|toml|txt)$/.test(e)?n=RegExp.$1:e=t,[at(e),n]},En=e=>{try{return bn(e)}catch(t){return Kt(e)}},kn=(e,t,n,r={})=>{if(t){const[e,s]=vn(t,n);"json"===s?r=o(e).json():"toml"===s?r=o(e).text().then(Kt):"string"===s?r=En(t):"object"===s&&t?r=t:"txt"===s&&"string"==typeof r&&(r=En(r)),t=e}return ot(r).then((n=>mn[e](n,t)))},xn=(e,t="")=>`${e}@${t}`.replace(/@$/,"");function Tn(e=this){return String(e).replace(/^(async\s*)?(\bfunction\b)?(.*?)\(/,((e,t,n,r)=>r&&!n?`${t||""}function ${r}(`:e))}const Sn="BeforeRun",An="AfterRun",jn=[`code${Sn}`,`code${Sn}Async`,`code${An}`,`code${An}Async`],On=["onWorker","onReady",`on${Sn}`,`on${Sn}Async`,`on${An}`,`on${An}Async`];function Rn(e,t){const{run:n,runAsync:r}=dn.get(this.type);return{...e,run:n.bind(this,t),runAsync:r.bind(this,t)}}const $n=(e,t,n,r,s,o)=>{if(s||o){const a=Rn.bind(e,t),i=r?"runAsync":"run",c=e[i];e[i]=r?async function(e,t,...r){s&&await s.call(this,a(e),n);const i=await c.call(this,e,t,...r);return o&&await o.call(this,a(e),n),i}:function(e,t,...r){s&&s.call(this,a(e),n);const i=c.call(this,e,t,...r);return o&&o.call(this,a(e),n),i}}};let Pn=class{constructor(e,t={}){const{main:n,worker:r}=t;this.interpreter=e,this.onWorker=n?.onWorker;for(const e of On.slice(1))this[e]=r?.[e];for(const e of jn)this[e]=r?.[e]}toJSON(){const e={};for(const t of On.slice(1))this[t]&&(e[t]=Tn(this[t]));for(const t of jn)this[t]&&(e[t]=Xe(this[t]()));return e}};var In=(...e)=>function(t,n){if(e.length){const[t,r]=e;(n=Ze({},n||{type:t,version:r})).type||(n.type=t)}const[r]=vn(n.config,n.configURL),s=((...e)=>new Fe(URL.createObjectURL(new Blob(['const e="object"==typeof self?self:globalThis,t=t=>((t,n)=>{const r=(e,n)=>(t.set(n,e),e),s=o=>{if(t.has(o))return t.get(o);const[a,i]=n[o];switch(a){case 0:case-1:return r(i,o);case 1:{const e=r([],o);for(const t of i)e.push(s(t));return e}case 2:{const e=r({},o);for(const[t,n]of i)e[s(t)]=s(n);return e}case 3:return r(new Date(i),o);case 4:{const{source:e,flags:t}=i;return r(new RegExp(e,t),o)}case 5:{const e=r(new Map,o);for(const[t,n]of i)e.set(s(t),s(n));return e}case 6:{const e=r(new Set,o);for(const t of i)e.add(s(t));return e}case 7:{const{name:t,message:n}=i;return r(new e[t](n),o)}case 8:return r(BigInt(i),o);case"BigInt":return r(Object(BigInt(i)),o)}return r(new e[a](i),o)};return s})(new Map,t)(0),n="",{toString:r}={},{keys:s}=Object,o=e=>{const t=typeof e;if("object"!==t||!e)return[0,t];const s=r.call(e).slice(8,-1);switch(s){case"Array":return[1,n];case"Object":return[2,n];case"Date":return[3,n];case"RegExp":return[4,n];case"Map":return[5,n];case"Set":return[6,n]}return s.includes("Array")?[1,s]:s.includes("Error")?[7,s]:[2,s]},a=([e,t])=>0===e&&("function"===t||"symbol"===t),i=(e,{json:t,lossy:n}={})=>{const r=[];return((e,t,n,r)=>{const i=(e,t)=>{const s=r.push(e)-1;return n.set(t,s),s},c=r=>{if(n.has(r))return n.get(r);let[l,u]=o(r);switch(l){case 0:{let t=r;switch(u){case"bigint":l=8,t=r.toString();break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+u);t=null;break;case"undefined":return i([-1],r)}return i([l,t],r)}case 1:{if(u)return i([u,[...r]],r);const e=[],t=i([l,e],r);for(const t of r)e.push(c(t));return t}case 2:{if(u)switch(u){case"BigInt":return i([u,r.toString()],r);case"Boolean":case"Number":case"String":return i([u,r.valueOf()],r)}if(t&&"toJSON"in r)return c(r.toJSON());const n=[],p=i([l,n],r);for(const t of s(r))!e&&a(o(r[t]))||n.push([c(t),c(r[t])]);return p}case 3:return i([l,r.toISOString()],r);case 4:{const{source:e,flags:t}=r;return i([l,{source:e,flags:t}],r)}case 5:{const t=[],n=i([l,t],r);for(const[n,s]of r)(e||!a(o(n))&&!a(o(s)))&&t.push([c(n),c(s)]);return n}case 6:{const t=[],n=i([l,t],r);for(const n of r)!e&&a(o(n))||t.push(c(n));return n}}const{message:p}=r;return i([l,{name:u,message:p}],r)};return c})(!(t||n),!!t,new Map,r)(e),r},{parse:c,stringify:l}=JSON,u={json:!0,lossy:!0};var p=Object.freeze({__proto__:null,parse:e=>t(c(e)),stringify:e=>l(i(e,u))});const f="array",d="function",h="null",y="number",g="object",w="symbol",m="undefined",_="apply",b="construct",E="defineProperty",v="deleteProperty",T="get",x="getOwnPropertyDescriptor",k="getPrototypeOf",S="has",O="isExtensible",A="ownKeys",j="preventExtensions",R="set",P="setPrototypeOf";var $=Object.freeze({__proto__:null,APPLY:_,CONSTRUCT:b,DEFINE_PROPERTY:E,DELETE_PROPERTY:v,GET:T,GET_OWN_PROPERTY_DESCRIPTOR:x,GET_PROTOTYPE_OF:k,HAS:S,IS_EXTENSIBLE:O,OWN_KEYS:A,PREVENT_EXTENSION:j,SET:R,SET_PROTOTYPE_OF:P});function I(){return this}const N=new FinalizationRegistry((([e,t,n])=>{n&&console.debug(`Held value ${String(t)} not relevant anymore`),e(t)})),M=Object.create(null),F=(e,t,{debug:n,handler:r,return:s,token:o=e}=M)=>{const a=s||new Proxy(e,r||M),i=[a,[t,e,!!n]];return!1!==o&&i.push(o),N.register(...i),a},{Object:W,Proxy:H,Reflect:D}=globalThis,{isArray:C}=Array,{ownKeys:L}=D,{create:B,hasOwn:q,values:U}=W,J=(e,t)=>t===f?e[0]:t===d?e():t===g?e.$:e,z=(e,t,n,r)=>{const s={type:{value:t}},o=q(e,"valueOf");for(const a of U($)){let i=r(e[a]||D[a]);if(o&&a===T){const{valueOf:r}=e,{value:s}=i;i={value(e,o,...a){return o===n?r.call(this,J(e,t)):s.call(this,e,o,...a)}}}s[a]=i}return B(e,s)},G=(e,t,n,r=e)=>{if(r===e)switch(typeof e){case g:case m:r||(r=!1);case d:break;default:r=!1,t===e&&(t=W(e))}const s=new H(t,n),{destruct:o}=n;return o?F(e,o,{token:r,return:s}):s},Y=e=>t=>{const n=typeof t;return n===g?t?e.get(t)?.[0]??(e=>C(e)?f:g)(t):h:n},K=e=>t=>{let n=typeof t;switch(n){case g:if(!t){n=h;break}case d:const r=e.get(t);r&&([n,t]=r)}return[n,t]},X=e=>((e=>{N.unregister(e)})(e),e);var V=e=>{const t=new WeakMap,n=Symbol(),r={},s=(e,n,r)=>(t.set(e,[n,r]),e),o={proxy:r,release:X,pair:K(t),typeOf:Y(t),isProxy:e=>t.has(e),valueOf:e=>e[n]??e.valueOf()};for(const t of L(e)){if(q(o,t))continue;const a=e[t];switch(t){case f:{const e=z(a,t,n,(e=>({value([t],...n){return e.call(this,t,...n)}})));r[t]=(t,...n)=>s(G(t,[t],e,...n),f,t);break}case d:{const e=z(a,t,n,(e=>({value(t,...n){return e.call(this,t(),...n)}})));r[t]=(t,...n)=>{return s(G(t,(r=t,I.bind(r)),e,...n),d,t);var r};break}case g:{const e=z(a,t,n,(e=>({value({$:t},...n){return e.call(this,t,...n)}})));r[t]=(t,...n)=>s(G(t,{$:t},e,...n),g,t);break}default:{const e=z(a,t,n,(e=>({value:e})));r[t]=(n,...r)=>s(G(n,n,e,...r),t,n);break}}}return o};let Z=0;const Q=new Map,ee=new Map,te=e=>ee.get(e),ne=e=>{if(!Q.has(e)){let t;for(;ee.has(t=Z++););Q.set(e,t),ee.set(t,e)}return Q.get(e)},{ArrayBuffer:re,Atomics:se,Promise:oe}=globalThis,{isArray:ae}=Array,{create:ie,getPrototypeOf:ce,values:le}=Object,ue=ce(Int32Array),pe=ie(se),fe=({currentTarget:e,type:t,origin:n,lastEventId:r,source:s,ports:o},a)=>e.dispatchEvent(new MessageEvent(t,{data:a,origin:n,lastEventId:r,source:s,ports:o})),de=()=>oe.withResolvers();let he=0;const ye=new Map,ge=(e,t)=>class extends e{constructor(e,...n){super(e,...n),e instanceof t&&ye.set(this,[he++,0,de()])}},we=new WeakSet,me=e=>(we.add(e),e),_e=(e,t)=>{const{data:n}=e,r=ae(n)&&(n.at(0)===t||0===n.at(1)&&!t);return r&&(e.stopImmediatePropagation(),e.preventDefault()),r},be=e=>null!==e&&"object"==typeof e&&!we.has(e),Ee=new WeakMap,ve=(e,t,n)=>{if(ye.has(e))t.set(e,ye.get(e)[0]);else if(!(e instanceof ue||e instanceof re))for(const r of le(e))be(r)&&!n.has(r)&&(n.add(r),ve(r,t,n))},Te=(...e)=>({value:new oe((t=>{let n=new Worker("data:application/javascript,onmessage%3De%3D%3EpostMessage(!Atomics.wait(...e.data))");n.onmessage=()=>t("ok"),n.postMessage(e)}))}),xe=(e,t)=>{const n=ye.get(e),[r,s,{promise:o}]=n;return n[1]=t,[r,o]};let{BigInt64Array:ke,Int32Array:Se,SharedArrayBuffer:Oe,addEventListener:Ae,postMessage:je}=globalThis,Re=!0,Pe=e=>e,$e=!1;const Ie=de();try{new Oe(4),pe.waitAsync||(pe.waitAsync=Te),Ie.resolve()}catch(e){const t=je,n=Ae,r=[];let s="",o="";Oe=class extends re{},ke=ge(ke,Oe),Se=ge(Se,Oe),Pe=me,$e=!0,pe.notify=(e,n)=>{const[r]=(e=>Ee.get(e))(e);return t([s,1,e,r,n]),0},pe.waitAsync=(...e)=>{const[t,n]=xe(...e);return{value:n}},pe.wait=(e,t,...n)=>{const[r]=xe(e,t,...n),a=new XMLHttpRequest;a.responseType="json",a.open("POST",`${o}?sabayon`,!1),a.setRequestHeader("Content-Type","application/json"),a.send(`["${s}",${r},${t}]`);const{response:i}=a;ye.delete(e);for(let t=0;t{if(_e(e,s)){const[t,n,...r]=e.data;switch(n){case 0:s=t,o=r.at(0)?.serviceWorker||"",o||(pe.wait=null,Ie.resolve());break;case 1:((e,t,n)=>{for(const[r,[s,o,{resolve:a}]]of ye)if(t===s&&n===o){for(let t=0;t{for(const[n,r]of t)Ee.set(n,[r,e.currentTarget]);fe(e,n)})(e,...r);break;case 3:Ie.resolve()}}else if(Re){const{currentTarget:t,type:n,origin:s,lastEventId:o,source:a,ports:i}=e;r.push([{currentTarget:t,type:n,origin:s,lastEventId:o,source:a,ports:i},e.data])}})),Ae=(e,...t)=>{if(n(e,...t),r.length)for(const e of r.splice(0))fe(...e)},je=(e,...n)=>t(((e,t)=>{const n=new Map;return be(t)&&ve(t,n,new Set),n.size?[e,2,n,t]:t})(s,e),...n)}await Ie.promise,Re=!1;const{BYTES_PER_ELEMENT:Ne}=Int32Array,{BYTES_PER_ELEMENT:Me}=Uint16Array,{notify:Fe}=pe,We=new TextDecoder("utf-16"),He=new WeakSet,De=(...e)=>(He.add(e),e);let Ce="";const Le=(e,t,n,r)=>{const[s]=r,o=n.get(s);if(!o)throw new Error(`Unknown proxy.${s}()`);e(o,t,r)};let Be=0;const qe=([e,t,n,r,s,o,a,i,c],l)=>(...u)=>{let p=""!==Ce,f=0;p&&(f=((e,t)=>setTimeout(console.warn,1e3,`💀🔒 - proxy.${e}() in proxy.${t}()`))(l,Ce));const d=Be++,h=[];He.has(u.at(-1)||h)&&He.delete(h=u.pop());const y=n(i?u.map(i):u);let g=t(2*Ne);return a([e,2,l,d,g,y,r],{transfer:h}),c(g,0).value.then((()=>{p&&clearTimeout(f);const n=g[1];if(!n)return;const r=Me*n;return g=t(r+r%Ne),a([e,1,d,g]),c(g,0).value.then((()=>{const e=new Uint16Array(g.buffer),t=o?e.subarray(0,n):e.slice(0,n);return s(We.decode(t))}))}))},Ue=(e,t)=>new Proxy(t,{get:(t,n)=>{let r;return"then"!==n&&(r=t.get(n),r||(r=qe(e,n),t.set(n,r))),r},set:(e,t,n)=>"then"!==t&&!!e.set(t,n)}),{wait:Je,waitAsync:ze}=pe;var Ge=({parse:e,stringify:t,transform:n,interrupt:r}=JSON)=>{const s=((e,t)=>async(n,r,[s,o,a,i,c])=>{c&&(Ce=s);try{const s=await n(...i);if(void 0!==s){const n=e(t?t(s):s);r.set(o,n),a[1]=n.length}}finally{c&&(Ce=""),a[0]=1,Fe(a,0)}})(t,n),o=de(),a=new Map,i=new Map;let c="",l=Je;if(Je&&r){const{handler:e,timeout:t=42}=r;l=(n,r,s)=>{for(;"timed-out"===(s=Je(n,r,0,t));)e();return s}}return Ae("message",(t=>{if(_e(t,c)){const[r,u,...p]=t.data;switch(u){case 0:{const t=!!Je;c=r,o.resolve({polyfill:$e,sync:t,transfer:De,proxy:Ue([c,e=>new Se(new Oe(e)),Pe,t,e,$e,je,n,t?(...e)=>({value:{then:t=>t(l(...e))}}):ze],a)});break}case 2:a.size?Le(s,i,a,p):setTimeout(Le,0,s,i,a,p);break;case 1:((e,[t,n])=>{const r=e.get(t);e.delete(t);for(let e=new Uint16Array(n.buffer),t=0,{length:s}=r;t[e,t])));const Ke="destruct",{[A]:Xe}=Reflect,Ve=new Map(Xe(Symbol).filter((e=>typeof Symbol[e]===w)).map((e=>[Symbol[e],e]))),Ze=e=>Ve.get(e)||`.${Symbol.keyFor(e)||""}`,Qe="42fb1e9a-1373-441e-813f-357c3deaee87",et="M"+Qe,tt="W"+Qe,{[_]:nt}=Reflect;const rt={object(...e){return this.string(function(e){for(var t=e[0],n=1,r=arguments.length;n{const t=e||console,n={buffered:it,stderr:(t.stderr||console.error).bind(t),stdout:(t.stdout||console.log).bind(t)};return{stderr:(...e)=>n.stderr(...e),stdout:(...e)=>n.stdout(...e),async get(e){const t=await e;return st.set(t,n),t}}},at=new TextDecoder,it=(e,t=10)=>{const n=[];return r=>{if(r instanceof Uint8Array)for(const s of r)s===t?e(at.decode(new Uint8Array(n.splice(0)))):n.push(s);else e(r)}},ct=(e,...t)=>rt[typeof e](e,...t),{isArray:lt}=Array,{assign:ut,create:pt,defineProperties:ft,defineProperty:dt,entries:ht}=Object,{all:yt,resolve:gt}=new Proxy(Promise,{get:(e,t)=>e[t].bind(e)}),wt=(e,t=location.href)=>new URL(e,t.replace(/^blob:/,"")).href,mt=(e,t,n,r=!1,s=CustomEvent)=>{e.dispatchEvent(new s(`${t}:${n}`,{bubbles:!0,detail:{worker:r}}))},_t=e=>Function(`\'use strict\';return (${e})`)(),bt=e=>e.replace(/^(?:\\n|\\r\\n)/,""),Et=Symbol.for("polyscript.js_modules"),vt=new Map;dt(globalThis,Et,{value:vt}),new Proxy(vt,{get:(e,t)=>e.get(t),has:(e,t)=>e.has(t),ownKeys:e=>[...e.keys()]});const Tt=(e,t)=>!t.startsWith("_"),xt=(e,t)=>new Proxy(e,{has:Tt,get:(e,n)=>e[t][n]}),kt=(e,t)=>import(e).then((e=>{vt.set(t,{...e})})),St=e=>new Promise(((t,n)=>{document.querySelector(`link[rel="stylesheet"][href="${e}"]`)?t():document.head.append(ut(document.createElement("link"),{rel:"stylesheet",href:e,onload:t,onerror:n}))})),Ot=e=>/\\.css$/i.test(new URL(e).pathname),At=(e,t)=>e.has(t),jt=e=>[...e.keys()];var Rt=(e,t,n,r)=>{const s=globalThis[Et];if(n)for(let[e,t]of ht(n)){let n=s.get(t);n&&!lt(n)||(s.set(t,n||(n=[])),n.push(e))}return((e,t,n,r)=>new Proxy(e,{has:At,ownKeys:jt,get:(e,s)=>{let o=e.get(s);if(lt(o)){let a=o;o=null;for(let e of a)e=wt(e,r),Ot(e)?n.importCSS(e):(n.importJS(e,s),o=t[Et].get(s));e.set(s,o)}return o}}))(s,e,t,r)};const Pt=new Map,$t=e=>Pt.get(e),It=(e,t)=>{try{return Function("require",t)($t)}catch(t){st.get(e).stderr(t)}};var Nt={type:"dummy",module:()=>"data:application/javascript,",engine:e=>ot().get(e),registerJSModule(e,t,n){Pt.set(t,n)},run:It,runAsync:It,runEvent:async(e,t,n)=>{try{await Function("require","e",`return ${t}(e)`)($t,n)}catch(t){st.get(e).stderr(t)}},transform:(e,t)=>t,writeFile(){}};const Mt=Object.getOwnPropertyDescriptors(Response.prototype),Ft=e=>"function"==typeof e,Wt={get:(e,t)=>Mt.hasOwnProperty(t)?((e,t,{get:n,value:r})=>n||!Ft(r)?e.then((e=>e[t])):(...n)=>e.then((e=>e[t](...n))))(e,t,Mt[t]):((e,t)=>Ft(t)?t.bind(e):t)(e,e[t])};var Ht=(e,...t)=>new Proxy(fetch(e,...t),Wt);Promise.withResolvers||(Promise.withResolvers=function(){var e,t,n=new this((function(n,r){e=n,t=r}));return{resolve:e,reject:t,promise:n}});const Dt=!globalThis.window,Ct=({FS:e,PATH:t,PATH_FS:n},r,s)=>{const o=n.resolve(r),a=t.dirname(o);return e.mkdirTree?e.mkdirTree(a):Bt(e,a),e.writeFile(o,new Uint8Array(s),{canOwn:!0})},Lt=e=>{const t=e.split("/");return t.pop(),t.join("/")},Bt=(e,t)=>{const n=[];for(const r of t.split("/"))"."!==r&&".."!==r&&(n.push(r),r&&e.mkdir(n.join("/")))},qt=(e,t)=>{const n=[];for(const e of t.split("/"))switch(e){case"":case".":break;case"..":n.pop();break;default:n.push(e)}return[e.cwd()].concat(n).join("/").replace(/^\\/+/,"/")},Ut=e=>{const t=e.map((e=>e.trim().replace(/(^[/]*|[/]*$)/g,""))).filter((e=>""!==e&&"."!==e)).join("/");return e[0].startsWith("/")?`/${t}`:t},Jt=(e,t)=>Ht(wt(e,t)).arrayBuffer(),zt=(e,t,n,r)=>yt((e=>{for(const{files:t,to_file:n,from:r=""}of e){if(void 0!==t&&void 0!==n)throw new Error("Cannot use \'to_file\' and \'files\' parameters together!");if(void 0===t&&void 0===n&&r.endsWith("/"))throw new Error(`Couldn\'t determine the filename from the path ${r}, please supply \'to_file\' parameter.`)}return e.flatMap((({from:e="",to_folder:t=".",to_file:n,files:r})=>{if(lt(r))return r.map((n=>({url:Ut([e,n]),path:Ut([t,n])})));const s=n||e.slice(1+e.lastIndexOf("/"));return[{url:e,path:Ut([t,s])}]}))})(n).map((({url:n,path:s})=>Jt(n,r).then((n=>e.writeFile(t,s,n)))))),Gt=(e,t)=>t.endsWith("/")?`${t}${e.split("/").pop()}`:t,Yt=(e,t)=>e.replace(/\\{.+?\\}/g,(e=>{if(!t.has(e))throw new SyntaxError(`Invalid template: ${e}`);return t.get(e)})),Kt=(e,t,n,r)=>yt((e=>{const t=new Map,n=new Set,r=[];for(const[s,o]of ht(e))if(/^\\{.+\\}$/.test(s)){if(t.has(s))throw new SyntaxError(`Duplicated template: ${s}`);t.set(s,Yt(o,t))}else{const e=Yt(s,t),a=Gt(e,Yt(o||"./",t));if(n.has(a))throw new SyntaxError(`Duplicated destination: ${a}`);n.add(a),r.push({url:e,path:a})}return r})(n).map((({url:n,path:s})=>Jt(n,r).then((r=>e.writeFile(t,s,r,n)))))),Xt=({main:e,worker:t},n)=>{const r=[];if(t&&Dt)for(let[e,s]of ht(t))e=wt(e,n),r.push(kt(e,s));if(e&&!Dt)for(let[t,s]of ht(e))t=wt(t,n),Ot(t)?St(t):r.push(kt(t,s));return yt(r)},{assign:Vt}=Object,Zt="entries",Qt="readonly",en="readwrite",tn={durability:"default",prefix:"IDBMap"},nn=({target:{result:e}})=>e;class rn extends EventTarget{#e;#t;#n;async#r(e,t){const n=(await this.#e).transaction(Zt,t,this.#t);return new Promise(((t,r)=>Vt(e(n.objectStore(Zt)),{onsuccess:t,onerror:r})))}constructor(e,{durability:t=tn.durability,prefix:n=tn.prefix}=tn){super(),this.#n=n,this.#t={durability:t},this.#e=new Promise(((t,n)=>{Vt(indexedDB.open(`${this.#n}/${e}`),{onupgradeneeded({target:{result:e,transaction:n}}){e.objectStoreNames.length||e.createObjectStore(Zt),n.oncomplete=()=>t(e)},onsuccess(e){t(nn(e))},onerror(e){n(e),this.dispatchEvent(e)}})})).then((e=>{const t=this.dispatchEvent.bind(this);for(const n in e)n.startsWith("on")&&(e[n]=t);return e}))}dispatchEvent(e){const{type:t,message:n,isTrusted:r}=e;return super.dispatchEvent(r?Vt(new Event(t),{message:n}):e)}async close(){(await this.#e).close()}get size(){return this.#r((e=>e.count()),Qt).then(nn)}async clear(){await this.#r((e=>e.clear()),en)}async delete(e){await this.#r((t=>t.delete(e)),en)}async entries(){const e=await this.keys();return Promise.all(e.map((e=>this.get(e).then((t=>[e,t])))))}async forEach(e,t=this){for(const[n,r]of await this.entries())await e.call(t,r,n,this)}async get(e){return await this.#r((t=>t.get(e)),Qt).then(nn)}async has(e){return void 0!==await this.#r((t=>t.getKey(e)),Qt).then(nn)}async keys(){return await this.#r((e=>e.getAllKeys()),Qt).then(nn)}async set(e,t){return await this.#r((n=>n.put(t,e)),en),this}async values(){const e=await this.keys();return Promise.all(e.map((e=>this.get(e))))}get[Symbol.toStringTag](){return this.#n}}class sn extends Map{#s;#o;constructor(...e){super(),this.#s=new rn(...e),this.#o=this.#s.entries().then((e=>{for(const[t,n]of e)super.set(t,n)}))}async sync(){await this.#o}clear(){return this.#o=this.#o.then((()=>this.#s.clear())),super.clear()}delete(e){return this.#o=this.#o.then((()=>this.#s.delete(e))),super.delete(e)}set(e,t){return this.#o=this.#o.then((()=>this.#s.set(e,t))),super.set(e,t)}}const on=new WeakMap,an=(e,t,n)=>{"polyscript"===t&&(n.lazy_py_modules=async(...t)=>(await on.get(e)(t),t.map((t=>e.pyimport(t)))),n.storage=async e=>{const t=new sn(e);return await t.sync(),t},n.JSON=p),e.registerJsModule(t,n)},cn=(e,t)=>{if(e.endsWith("/*")){if(/\\.(zip|whl|tgz|tar(?:\\.gz)?)$/.test(t))return RegExp.$1;throw new Error(`Unsupported archive ${t}`)}return""},ln=(e,t,...n)=>{try{return e.runPython(ct(t),...n)}catch(t){st.get(e).stderr(t)}},un=async(e,t,...n)=>{try{return await e.runPythonAsync(ct(t),...n)}catch(t){st.get(e).stderr(t)}},pn=async(e,t,n)=>{const[r,...s]=t.split(".");let o,a=e.globals.get(r);for(const e of s)[o,a]=[a,a[e]];try{await a.call(o,n)}catch(t){st.get(e).stderr(t)}};var fn=(new TextEncoder).encode(\'from uio import StringIO\\nimport sys\\n\\nclass Response:\\n def __init__(self, f):\\n self.raw = f\\n self.encoding = "utf-8"\\n self._cached = None\\n\\n def close(self):\\n if self.raw:\\n self.raw.close()\\n self.raw = None\\n self._cached = None\\n\\n @property\\n def content(self):\\n if self._cached is None:\\n try:\\n self._cached = self.raw.read()\\n finally:\\n self.raw.close()\\n self.raw = None\\n return self._cached\\n\\n @property\\n def text(self):\\n return str(self.content, self.encoding)\\n\\n def json(self):\\n import ujson\\n\\n return ujson.loads(self.content)\\n\\n\\n# TODO try to support streaming xhr requests, a-la pyodide-http\\nHEADERS_TO_IGNORE = ("user-agent",)\\n\\n\\ntry:\\n import js\\nexcept Exception as err:\\n raise OSError("This version of urequests can only be used in the browser")\\n\\n# TODO try to support streaming xhr requests, a-la pyodide-http\\n\\nHEADERS_TO_IGNORE = ("user-agent",)\\n\\n\\ndef request(\\n method,\\n url,\\n data=None,\\n json=None,\\n headers={},\\n stream=None,\\n auth=None,\\n timeout=None,\\n parse_headers=True,\\n):\\n from js import XMLHttpRequest\\n\\n xhr = XMLHttpRequest.new()\\n xhr.withCredentials = False\\n\\n if auth is not None:\\n import ubinascii\\n\\n username, password = auth\\n xhr.open(method, url, False, username, password)\\n else:\\n xhr.open(method, url, False)\\n\\n for name, value in headers.items():\\n if name.lower() not in HEADERS_TO_IGNORE:\\n xhr.setRequestHeader(name, value)\\n\\n if timeout:\\n xhr.timeout = int(timeout * 1000)\\n\\n if json is not None:\\n assert data is None\\n import ujson\\n\\n data = ujson.dumps(json)\\n # s.write(b"Content-Type: application/json\\\\r\\\\n")\\n xhr.setRequestHeader("Content-Type", "application/json")\\n\\n xhr.send(data)\\n\\n # Emulates the construction process in the original urequests\\n resp = Response(StringIO(xhr.responseText))\\n resp.status_code = xhr.status\\n resp.reason = xhr.statusText\\n resp.headers = xhr.getAllResponseHeaders()\\n\\n return resp\\n\\n\\n# Other methods - head, post, put, patch, delete - are not used by\\n# mip and therefore not included\\n\\n\\ndef get(url, **kw):\\n return request("GET", url, **kw)\\n\\n\\n# Content below this line is from the Micropython MIP package and is covered\\n# by the applicable MIT license:\\n# \\n# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING \\n# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \\n# DEALINGS IN THE SOFTWARE.\\n\\n# MicroPython package installer\\n# MIT license; Copyright (c) 2022 Jim Mussared\\n\\n\\n_PACKAGE_INDEX = const("https://micropython.org/pi/v2")\\n_CHUNK_SIZE = 128\\n\\n\\n# This implements os.makedirs(os.dirname(path))\\ndef _ensure_path_exists(path):\\n import os\\n\\n split = path.split("/")\\n\\n # Handle paths starting with "/".\\n if not split[0]:\\n split.pop(0)\\n split[0] = "/" + split[0]\\n\\n prefix = ""\\n for i in range(len(split) - 1):\\n prefix += split[i]\\n try:\\n os.stat(prefix)\\n except:\\n os.mkdir(prefix)\\n prefix += "/"\\n\\n\\n# Copy from src (stream) to dest (function-taking-bytes)\\ndef _chunk(src, dest):\\n buf = memoryview(bytearray(_CHUNK_SIZE))\\n while True:\\n n = src.readinto(buf)\\n if n == 0:\\n break\\n dest(buf if n == _CHUNK_SIZE else buf[:n])\\n\\n\\n# Check if the specified path exists and matches the hash.\\ndef _check_exists(path, short_hash):\\n import os\\n\\n try:\\n import binascii\\n import hashlib\\n\\n with open(path, "rb") as f:\\n hs256 = hashlib.sha256()\\n _chunk(f, hs256.update)\\n existing_hash = str(binascii.hexlify(hs256.digest())[: len(short_hash)], "utf-8")\\n return existing_hash == short_hash\\n except:\\n return False\\n\\n\\ndef _rewrite_url(url, branch=None):\\n if not branch:\\n branch = "HEAD"\\n if url.startswith("github:"):\\n url = url[7:].split("/")\\n url = (\\n "https://raw.githubusercontent.com/"\\n + url[0]\\n + "/"\\n + url[1]\\n + "/"\\n + branch\\n + "/"\\n + "/".join(url[2:])\\n )\\n return url\\n\\n\\ndef _download_file(url, dest):\\n response = get(url)\\n try:\\n if response.status_code != 200:\\n print("Error", response.status_code, "requesting", url)\\n return False\\n\\n print("Copying:", dest)\\n _ensure_path_exists(dest)\\n with open(dest, "wb") as f:\\n _chunk(response.raw, f.write)\\n\\n return True\\n finally:\\n response.close()\\n\\n\\ndef _install_json(package_json_url, index, target, version, mpy):\\n response = get(_rewrite_url(package_json_url, version))\\n try:\\n if response.status_code != 200:\\n print("Package not found:", package_json_url)\\n return False\\n\\n package_json = response.json()\\n finally:\\n response.close()\\n for target_path, short_hash in package_json.get("hashes", ()):\\n fs_target_path = target + "/" + target_path\\n if _check_exists(fs_target_path, short_hash):\\n print("Exists:", fs_target_path)\\n else:\\n file_url = "{}/file/{}/{}".format(index, short_hash[:2], short_hash)\\n if not _download_file(file_url, fs_target_path):\\n print("File not found: {} {}".format(target_path, short_hash))\\n return False\\n for target_path, url in package_json.get("urls", ()):\\n fs_target_path = target + "/" + target_path\\n if not _download_file(_rewrite_url(url, version), fs_target_path):\\n print("File not found: {} {}".format(target_path, url))\\n return False\\n for dep, dep_version in package_json.get("deps", ()):\\n if not _install_package(dep, index, target, dep_version, mpy):\\n return False\\n return True\\n\\n\\ndef _install_package(package, index, target, version, mpy):\\n if (\\n package.startswith("http://")\\n or package.startswith("https://")\\n or package.startswith("github:")\\n ):\\n if package.endswith(".py") or package.endswith(".mpy"):\\n print("Downloading {} to {}".format(package, target))\\n return _download_file(\\n _rewrite_url(package, version), target + "/" + package.rsplit("/")[-1]\\n )\\n else:\\n if not package.endswith(".json"):\\n if not package.endswith("/"):\\n package += "/"\\n package += "package.json"\\n print("Installing {} to {}".format(package, target))\\n else:\\n if not version:\\n version = "latest"\\n print("Installing {} ({}) from {} to {}".format(package, version, index, target))\\n\\n mpy_version = (\\n sys.implementation._mpy & 0xFF if mpy and hasattr(sys.implementation, "_mpy") else "py"\\n )\\n\\n # WARNING: mpy_version fails miserably with 1.22.0-380\\n package = "{}/package/{}/{}/{}.json".format(index, "py", package, version)\\n\\n return _install_json(package, index, target, version, mpy)\\n\\n\\ndef install(package, index=None, target=None, version=None, mpy=True):\\n if not target:\\n for p in sys.path:\\n if p.endswith("/lib"):\\n target = p\\n break\\n else:\\n print("Unable to find lib dir in sys.path")\\n return\\n\\n if not index:\\n index = _PACKAGE_INDEX\\n\\n if _install_package(package, index.rstrip("/"), target, version, mpy):\\n print("Done")\\n else:\\n print("Package may be partially installed")\\n\');const dn=async e=>(await import("./toml-DiUM0_qs.js")).parse(e),hn=(e,t)=>{try{e.mkdir(t)}catch(e){}};var yn={type:"micropython",module:(e="1.24.0-preview-114")=>`https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript@${e}/micropython.mjs`,async engine({loadMicroPython:e},t,n,r){const{stderr:s,stdout:o,get:a}=ot({stderr:it(console.error),stdout:it(console.log)});n=n.replace(/\\.m?js$/,".wasm");const i=await a(e({linebuffer:!1,stderr:s,stdout:o,url:n})),c=gn.bind(this,i,r);return on.set(i,c),t.files&&await Kt(this,i,t.files,r),t.fetch&&await zt(this,i,t.fetch,r),t.js_modules&&await Xt(t.js_modules,r),this.writeFile(i,"./mip.py",fn),t.packages&&await c(t.packages),i},registerJSModule:an,run:ln,runAsync:un,runEvent:pn,transform:(e,t)=>e.PyProxy.toJs(t),writeFile:(e,t,n,r)=>{const{FS:s,_module:{PATH:o,PATH_FS:a}}=e,i={FS:s,PATH:o,PATH_FS:a},c=cn(t,r);if(c){const r=t.slice(0,-1);switch("./"!==r&&s.mkdir(r),c){case"whl":case"zip":{const e=new Blob([n],{type:"application/zip"});return import("./zip-gl8b5xR3.js").then((async({BlobReader:t,Uint8ArrayWriter:n,ZipReader:a})=>{const i=new a(new t(e));for(const e of await i.getEntries()){const{directory:t,filename:a}=e,i=r+a;if(t)hn(s,i);else{hn(s,o.dirname(i));const t=await e.getData(new n);s.writeFile(i,t,{canOwn:!0})}}i.close()}))}case"tgz":case"tar.gz":{const t="./_.tar.gz";return Ct(i,t,n),void e.runPython(`\\n import os, gzip, tarfile\\n tar = tarfile.TarFile(fileobj=gzip.GzipFile(fileobj=open("${t}", "rb")))\\n for f in tar:\\n name = f"${r}{f.name}"\\n if f.type == tarfile.DIRTYPE:\\n if f.name != "./":\\n os.mkdir(name.strip("/"))\\n else:\\n dir = os.path.dirname(name)\\n if not os.path.exists(dir):\\n os.mkdir(dir)\\n source = tar.extractfile(f)\\n with open(name, "wb") as dest:\\n dest.write(source.read())\\n dest.close()\\n tar.close()\\n os.remove("${t}")\\n `)}}}return Ct(i,t,n)}};async function gn(e,t,n){let r;for(const s of n)if(s.endsWith(".whl")){const n=wt(s,t),r=await Ht(n).arrayBuffer();await this.writeFile(e,"./*",r,n)}else r||(r=e.pyimport("mip")),r.install(s)}const wn={dict_converter:Object.fromEntries};let mn=!1;const _n=e=>(...t)=>{try{return mn=!0,e(...t)}finally{mn=!1}};let bn=!1;const En=()=>{if(bn)return;bn=!0;const e=new WeakMap,t=e=>e.destroy(),n=n=>{for(let r=0;r`https://cdn.jsdelivr.net/pyodide/v${e}/full/pyodide.mjs`,async engine({loadPyodide:e},t,n,r){Dt||"auto"!==t.experimental_create_proxy||En();const{stderr:s,stdout:o,get:a}=ot(),i=n.slice(0,n.lastIndexOf("/")),c=await a(e({stderr:s,stdout:o,indexURL:i})),l=Tn.bind(c);return on.set(c,l),t.files&&await Kt(this,c,t.files,r),t.fetch&&await zt(this,c,t.fetch,r),t.js_modules&&await Xt(t.js_modules,r),t.packages&&await l(t.packages),c},registerJSModule:an,run:_n(ln),runAsync:_n(un),runEvent:_n(pn),transform:({ffi:{PyProxy:e}},t)=>t instanceof e?t.toJs(wn):t,writeFile:(e,t,n,r)=>{const s=cn(t,r);if(s)return e.unpackArchive(n,s,{extractDir:t.slice(0,-1)});const{FS:o,PATH:a,_module:{PATH_FS:i}}=e;return Ct({FS:o,PATH:a,PATH_FS:i},t,n)}};async function Tn(e){await this.loadPackage("micropip");const t=this.pyimport("micropip");await t.install(e,{keep_going:!0}),t.destroy()}const xn="ruby-wasm-wasi",kn=xn.replace(/\\W+/g,"_");var Sn={type:xn,experimental:!0,module:(e="2.6.2")=>`https://cdn.jsdelivr.net/npm/@ruby/3.2-wasm-wasi@${e}/dist/browser/+esm`,async engine({DefaultRubyVM:e},t,n,r){n=n.replace(/\\/browser\\/\\+esm$/,"/ruby.wasm");const s=await Ht(n).arrayBuffer(),o=await WebAssembly.compile(s),{vm:a}=await e(o);return t.files&&await Kt(this,a,t.files,r),t.fetch&&await zt(this,a,t.fetch,r),t.js_modules&&await Xt(t.js_modules,r),a},registerJSModule(e,t,n){t=t.replace(/\\W+/g,"__");const r=`__module_${kn}_${t}`;globalThis[r]=n,this.run(e,`require "js";$${t}=JS.global[:${r}]`),delete globalThis[r]},run:(e,t,...n)=>e.eval(ct(t),...n),runAsync:(e,t,...n)=>e.evalAsync(ct(t),...n),async runEvent(e,t,n){if(/^xworker\\.(on\\w+)$/.test(t)){const{$1:t}=RegExp,r=`__module_${kn}_event`;globalThis[r]=n,this.run(e,`require "js";$xworker.call("${t}",JS.global[:${r}])`),delete globalThis[r]}else{const r=this.run(e,`method(:${t})`);await r.call(t,e.wrap(n))}},transform:(e,t)=>t,writeFile:()=>{throw new Error(`writeFile is not supported in ${xn}`)}};var On={type:"wasmoon",module:(e="1.16.0")=>`https://cdn.jsdelivr.net/npm/wasmoon@${e}/+esm`,async engine({LuaFactory:e,LuaLibraries:t},n,r,s){const{stderr:o,stdout:a,get:i}=ot(),c=await i((new e).createEngine());return c.global.getTable(t.Base,(e=>{c.global.setField(e,"print",a),c.global.setField(e,"printErr",o)})),n.files&&await Kt(this,c,n.files,s),n.fetch&&await zt(this,c,n.fetch,s),n.js_modules&&await Xt(n.js_modules,s),c},registerJSModule:(e,t,n)=>{e.global.set(t,n)},run:(e,t,...n)=>{try{return e.doStringSync(ct(t),...n)}catch(t){st.get(e).stderr(t)}},runAsync:async(e,t,...n)=>{try{return await e.doString(ct(t),...n)}catch(t){st.get(e).stderr(t)}},runEvent:async(e,t,n)=>{const[r,...s]=t.split(".");let o,a=e.global.get(r);for(const e of s)[o,a]=[a,a[e]];try{await a.call(o,n)}catch(t){st.get(e).stderr(t)}},transform:(e,t)=>t,writeFile:({cmodule:{module:{FS:e}}},t,n)=>((e,t,n)=>(Bt(e,Lt(t)),t=qt(e,t),e.writeFile(t,new Uint8Array(n),{canOwn:!0})))(e,t,n)};const An=new WeakMap,jn=async(e,t)=>{const{shelter:n,destroy:r,io:s}=An.get(e),{output:o,result:a}=await n.captureR(ct(t));for(const{type:e,data:t}of o)s[e](t);return F(a,r,{token:!1})};var Rn={type:"webr",experimental:!0,module:(e="0.4.0")=>`https://cdn.jsdelivr.net/npm/webr@${e}/dist/webr.mjs`,async engine(e,t,n,r){const{get:s}=ot(),o=new e.WebR;await s(o.init().then((()=>o)));const a=await new o.Shelter;return An.set(o,{module:e,shelter:a,destroy:a.destroy.bind(a),io:st.get(o)}),t.files&&await Kt(this,o,t.files,r),t.fetch&&await zt(this,o,t.fetch,r),t.js_modules&&await Xt(t.js_modules,r),o},registerJSModule(e,t){console.warn(`Experimental interpreter: module ${t} is not supported (yet)`)},run:jn,runAsync:jn,async runEvent(e,t,n){await e.evalRVoid(`${t}(event)`,{env:{event:{type:[n.type]}}})},transform:(e,t)=>(console.log("transforming",t),t),writeFile:()=>{}};const Pn=new Map,$n=new Map,In=new Proxy(new Map,{get(e,t){if(!e.has(t)){const[n,...r]=t.split("@"),s=Pn.get(n),o=/^(?:\\.?\\.?\\/|https?:\\/\\/)/i.test(r)?r.join("@"):s.module(...r);e.set(t,{url:o,module:import(o),engine:s.engine.bind(s)})}const{url:n,module:r,engine:s}=e.get(t);return(e,o)=>r.then((r=>($n.set(t,e),s(r,e,n,o))))}}),Nn=e=>{for(const t of[].concat(e.type))Pn.set(t,e)};for(const e of[Nt,yn,vn,Sn,On,Rn])Nn(e);const{parse:Mn}=JSON,Fn=e=>{try{return Mn(e)}catch(t){return dn(e)}},Wn=(e,t,n,r={})=>{if(t){const[e,s]=((e,t="./config.txt")=>{let n=typeof e;return"string"===n&&/\\.(json|toml|txt)$/.test(e)?n=RegExp.$1:e=t,[wt(e),n]})(t,n);"json"===s?r=Ht(e).json():"toml"===s?r=Ht(e).text().then(dn):"string"===s?r=Fn(t):"object"===s&&t?r=t:"txt"===s&&"string"==typeof r&&(r=Fn(r)),t=e}return gt(r).then((n=>In[e](n,t)))},Hn="BeforeRun",Dn="AfterRun",Cn=[`code${Hn}`,`code${Hn}Async`,`code${Dn}`,`code${Dn}Async`],Ln=["onWorker","onReady",`on${Hn}`,`on${Hn}Async`,`on${Dn}`,`on${Dn}Async`];function Bn(e,t){const{run:n,runAsync:r}=Pn.get(this.type);return{...e,run:n.bind(this,t),runAsync:r.bind(this,t)}}const qn=(e,t,n,r,s,o)=>{if(s||o){const a=Bn.bind(e,t),i=r?"runAsync":"run",c=e[i];e[i]=r?async function(e,t,...r){s&&await s.call(this,a(e),n);const i=await c.call(this,e,t,...r);return o&&await o.call(this,a(e),n),i}:function(e,t,...r){s&&s.call(this,a(e),n);const i=c.call(this,e,t,...r);return o&&o.call(this,a(e),n),i}}};let Un,Jn,zn;const Gn=(e,t)=>{addEventListener(e,t||(async t=>{try{await Un,Jn(`xworker.on${e}`,t)}catch(e){postMessage(e)}}),!!t&&{once:!0})},{parse:Yn,stringify:Kn}=p,{proxy:Xn,sync:Vn,polyfill:Zn,window:Qn,isWindowProxy:er}=await(async e=>{const t=await Ge(e),n=e?.transform||(e=>e),{[et]:r}=t.proxy,s=new Map,o=(e,t)=>{let n=s.get(e)?.deref();return n||s.set(e,new WeakRef(n=t(e))),n},a=([e,t])=>{switch(e){case Ye[g]:return null==t?globalThis:typeof t===y?o(t,p.object):t;case Ye[f]:return typeof t===y?o(t,p.array):t;case Ye[d]:return typeof t===y?o(t,p.function):te(parseInt(t));case Ye[w]:return(e=>{if(e.startsWith("."))return Symbol.for(e.slice(1));for(const[t,n]of Ve)if(n===e)return t})(t);default:return t}},i=e=>{let[t,r]=m(e);switch(t){case g:if(r==globalThis||null==r)r=null;else if(typeof r===g&&!(r instanceof ue)){r=n(r);for(const e in r)r[e]=i(r[e])}return[Ye[g],r];case f:return[Ye[f],typeof r===y?r:n(r).map(i)];case d:return[Ye[d],typeof r===d?String(ne(n(r))):r];case w:return[Ye[w],Ze(e)];default:return[Ye[t],r]}},c=(...e)=>a(r(...e)),l={[E]:(e,t,n)=>c(E,e,i(t),i(n)),[v]:(e,t)=>c(v,e,i(t)),[T]:(e,t)=>c(T,e,i(t)),[k]:e=>c(k,e),[x]:(e,t)=>{const n=c(x,e,i(t));if(n){const{get:e,set:t,value:r}=n;e&&(n.get=a(e)),t&&(n.set=a(t)),r&&(n.value=a(r))}return n},[S]:(e,t)=>c(S,e,i(t)),[O]:e=>c(O,e),[A]:e=>c(A,e).map(a),[j]:e=>c(j,e),[R]:(e,t,n)=>c(R,e,i(t),i(n)),[P]:(e,t)=>c(P,e,i(t)),[Ke](e){s.delete(e),r(Ke,e)}},u={object:l,array:l,function:{...l,[_]:(e,...t)=>c(_,e,...t.map(i)),[b]:(e,...t)=>c(b,e,...t.map(i))}},{proxy:p,isProxy:h,pair:m}=V(u),$=p.object(null);return t.proxy[tt]=(e,t,...n)=>{const r=parseInt(t);switch(e){case _:{const[e,t]=n;return i(nt(te(r),a(e),t.map(a)))}case Ke:(e=>{const[t,n]=typeof e===y?[ee,Q]:[Q,ee],r=t.has(e);r&&(n.delete(t.get(e)),t.delete(e))})(r)}},{...t,window:$,isWindowProxy:h}})({parse:Yn,stringify:Kn,transform:e=>zn?zn(e):e}),tr={polyfill:Zn,sync:Xn,window:Vn?Qn:null,isWindowProxy:er,onmessage:console.info,onerror:console.error,onmessageerror:console.warn,postMessage:postMessage.bind(self)};Gn("message",(({data:{options:e,config:t,configURL:n,code:r,hooks:s}})=>{Un=(async()=>{try{const{id:o,tag:a,type:i,custom:c,version:l,config:u,async:p}=e,f=((e,t="")=>`${e}@${t}`.replace(/@$/,""))(i,l),d=await Wn(f,t,n,u),{js_modules:h}=$n.get(f),y=h?.main,g=pt(Pn.get(i)),w=((e,t,n,r)=>({type:t,config:n,interpreter:r,io:st.get(r),run:(t,...n)=>e.run(r,t,...n),runAsync:(t,...n)=>e.runAsync(r,t,...n),runEvent:(...t)=>e.runEvent(r,...t)}))(g,c||i,u||{},d);let m="run";if(p&&(m+="Async"),s){let e,t,n="",r="";for(const e of Cn){const t=s[e];if(t){const s=e.endsWith("Async");(s&&p||!s&&!p)&&(e.startsWith("codeBefore")?n=t:r=t)}}(n||r)&&((e,t,n,r)=>{const s=e[t].bind(e);e[t]="run"===t?(e,t,...o)=>{n&&s(e,n,...o);const a=s(e,bt(t),...o);return r&&s(e,r,...o),a}:async(e,t,...o)=>{n&&await s(e,n,...o);const a=await s(e,bt(t),...o);return r&&await s(e,r,...o),a}})(g,m,n,r);for(const n of Ln.slice(2)){const r=s[n];if(r){const s=n.endsWith("Async");if(s&&p||!s&&!p){const s=_t(r);n.startsWith("onBefore")?e=s:t=s}}}qn(g,w,tr,p,e,t)}let _,b,E,v=null,T="";Vn&&(({CustomEvent:_,document:b}=Qn),v=o&&b.getElementById(o)||null,E=e=>mt(v,c||i,e,!0,_));const x=Rt(Qn,Xn,y,t);if(((e,t,n,r)=>{if("pyodide"===e)return;const s="polyscript.js_modules";for(const e of Reflect.ownKeys(r))t.registerJSModule(n,`${s}.${e}`,xt(r,e));t.registerJSModule(n,s,r)})(i,g,d,x),g.registerJSModule(d,"polyscript",{xworker:tr,currentScript:v,config:w.config,js_modules:x,get target(){return!T&&v&&("SCRIPT"===a?v.after(ut(b.createElement(`script-${c||i}`),{id:T=`${o}-target`})):(T=o,v.replaceChildren(),v.style.display="block")),T}}),Jn=g.runEvent.bind(g,d),zn=g.transform.bind(g,d),v&&E("ready"),s?.onReady&&_t(s?.onReady).call(g,Bn.call(g,w,d),tr),await g[m](d,r),["micropython","pyodide"].includes(g.type)){const e="polyscript",t=`__${e}_workers__`,n="__export__";d.runPython([`import js as ${t}`,`${t}.${t} = "${n}" in locals() and ${n} or []`,`del ${t}`].join("\\n"));const r=[...globalThis[t]];delete globalThis[t],r.length&&d.runPython([`from ${e} import xworker as ${t}`,...r.map((e=>`${t}.sync.${e} = ${e}`)),`del ${t}`].join("\\n"))}return v&&E("done"),postMessage("polyscript:done"),d}catch(e){postMessage(e)}})(),Gn("error"),Gn("message"),Gn("messageerror")}));\n'.replace(Me,Ne)],{type:"application/javascript"})),...e))({serviceWorker:n?.serviceWorker||n?.service_worker}),{postMessage:a}=s,i=this instanceof Pn,c=Ze(s.proxy,{importJS:_t,importCSS:bt}),l=Promise.withResolvers();let u=o(t).text().then((e=>{const t=i?this.toJSON():void 0;a.call(s,{options:n,config:r,code:e,hooks:t})})).then((()=>{u={then:e=>e()}}));return tt(s,{sync:{value:c},ready:{value:l.promise},postMessage:{value:(e,...t)=>u.then((()=>a.call(s,e,...t)))},onerror:{writable:!0,configurable:!0,value:console.error}}),s.addEventListener("message",(e=>{const{data:t}=e,n=t instanceof Error;(n||"polyscript:done"===t)&&(e.stopImmediatePropagation(),n?(l.reject(t),s.onerror(et(e,{type:{value:"error"},error:{value:t}}))):l.resolve(s))})),i&&this.onWorker?.(this.interpreter,s),s};const Mn="Invalid content",Nn="Invalid worker attribute",Fn="Invalid worker attribute";var Wn=e=>{const{src:t,worker:n}=e.attributes;if(n){let{value:r}=n;if(r)throw new SyntaxError(Fn);if(r=t?.value,!r){if(t)throw new SyntaxError(Nn);if(e.childElementCount){const{innerHTML:t,localName:n,type:s}=e,o=s||n.replace(/-script$/,"");r=Ve(t),console.warn(`Deprecated: use + + + +
+ +
+Expt,Run,Speed
+1,1,850
+1,2,740
+1,3,900
+1,4,1070
+1,5,930
+1,6,850
+1,7,950
+1,8,980
+1,9,980
+1,10,880
+1,11,1000
+1,12,980
+1,13,930
+1,14,650
+1,15,760
+1,16,810
+1,17,1000
+1,18,1000
+1,19,960
+1,20,960
+2,1,960
+2,2,940
+2,3,960
+2,4,940
+2,5,880
+2,6,800
+2,7,850
+2,8,880
+2,9,900
+2,10,840
+2,11,830
+2,12,790
+2,13,810
+2,14,880
+2,15,880
+2,16,830
+2,17,800
+2,18,790
+2,19,760
+2,20,800
+3,1,880
+3,2,880
+3,3,880
+3,4,860
+3,5,720
+3,6,720
+3,7,620
+3,8,860
+3,9,970
+3,10,950
+3,11,880
+3,12,910
+3,13,850
+3,14,870
+3,15,840
+3,16,840
+3,17,850
+3,18,840
+3,19,840
+3,20,840
+4,1,890
+4,2,810
+4,3,810
+4,4,820
+4,5,800
+4,6,770
+4,7,760
+4,8,740
+4,9,750
+4,10,760
+4,11,910
+4,12,920
+4,13,890
+4,14,860
+4,15,880
+4,16,720
+4,17,840
+4,18,850
+4,19,850
+4,20,780
+5,1,890
+5,2,840
+5,3,780
+5,4,810
+5,5,760
+5,6,810
+5,7,790
+5,8,810
+5,9,820
+5,10,850
+5,11,870
+5,12,870
+5,13,810
+5,14,740
+5,15,810
+5,16,940
+5,17,950
+5,18,800
+5,19,810
+5,20,870  
+
+ + \ No newline at end of file diff --git a/test/create_proxy/main.py b/test/create_proxy/main.py new file mode 100644 index 00000000..4f856b7f --- /dev/null +++ b/test/create_proxy/main.py @@ -0,0 +1,32 @@ +import js as window +from polyscript.js_modules import d3, dc +from polyscript.js_modules.crossfilter import default as crossfilter + +chart = dc.BarChart.new("#test") + +experiments = d3.csvParse(d3.select('pre#data').text()) + +for x in experiments: + x.Speed = int(x.Speed) + +ndx = crossfilter(experiments) +runDimension = ndx.dimension(lambda d,*_: int(d.Run)) + +reduce_sum = lambda d,*_: d.Speed * int(d.Run) / 1000 +speedSumGroup = runDimension.group().reduceSum(reduce_sum) + +def click(e, d): + window.console.log("click!", d) + +def renderlet(chart): + chart.selectAll('rect').on("click", click) + +chart.width(768) +chart.height(480) +chart.x(d3.scaleLinear().domain([6,20])) +chart.brushOn(False) +chart.yAxisLabel("This is the Y Axis!") +chart.dimension(runDimension) +chart.group(speedSumGroup) +chart.on('renderlet', renderlet) +chart.render() diff --git a/test/debug.html b/test/debug.html new file mode 100644 index 00000000..d56ee70f --- /dev/null +++ b/test/debug.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/test/file/index.html b/test/file/index.html new file mode 100644 index 00000000..6611625a --- /dev/null +++ b/test/file/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/test/file/main.py b/test/file/main.py new file mode 100644 index 00000000..c530c1d5 --- /dev/null +++ b/test/file/main.py @@ -0,0 +1,22 @@ +try: + from polyscript import xworker + document = xworker.window.document +except: + import js + document = js.document + +try: + from pyodide.ffi import create_proxy +except: + create_proxy = lambda x: x + +file = document.querySelector('#file') +file.disabled = False + +async def on_file_change(event): + file = event.target.files[0] + print('name:', file.name) + print('size:', file.size) + print('text:', await file.text()) + +file.addEventListener('change', create_proxy(on_file_change)) diff --git a/test/fs/index.html b/test/fs/index.html new file mode 100644 index 00000000..b092e6d6 --- /dev/null +++ b/test/fs/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + diff --git a/test/index.html b/test/index.html index 2bb0e642..f510eb4c 100644 --- a/test/index.html +++ b/test/index.html @@ -5,6 +5,7 @@ python + diff --git a/test/index.js b/test/index.js index f85b8953..4bee6f40 100644 --- a/test/index.js +++ b/test/index.js @@ -36,6 +36,7 @@ globalThis.indexedDB = { open: () => ({}) }; globalThis.document = document; globalThis.Element = window.Element; globalThis.CustomEvent = CustomEvent; +globalThis.dispatchEvent = Object; globalThis.MutationObserver = window.MutationObserver; globalThis.Worker = class {}; globalThis.XPathResult = {}; @@ -75,7 +76,7 @@ import("../esm/index.js").then(async polyscript => { // all tests for (const test of [ async function versionedRuntime() { - document.head.innerHTML = ``; + document.head.innerHTML = ``; await tick(); assert(pyodide.content, dedent(content)); assert(pyodide.target.tagName, "PYODIDE-SCRIPT"); diff --git a/test/integration.html b/test/integration.html index 0668c320..0b7f60b0 100644 --- a/test/integration.html +++ b/test/integration.html @@ -5,5 +5,5 @@ polyscript integration tests - + diff --git a/test/integration/interpreter/micropython/custom-hooks.html b/test/integration/interpreter/micropython/custom-hooks.html index 8b3a3446..1f1848e2 100644 --- a/test/integration/interpreter/micropython/custom-hooks.html +++ b/test/integration/interpreter/micropython/custom-hooks.html @@ -12,9 +12,10 @@ hooks: { main: { onReady: ({ run, runAsync }, element) => { + const isAsync = element.getAttribute('async') !== 'false'; console.log('onMainReady'); dispatchEvent(new Event('mpy:ready')); - const exec = element.hasAttribute('async') ? runAsync : run; + const exec = isAsync ? runAsync : run; exec(element.textContent.trim()); }, onWorker: () => console.log('onWorkerMain'), @@ -70,10 +71,8 @@ const isScript = type === 'script'; const tag = document.createElement(isScript ? 'script' : type + '-script'); if (isScript) tag.type = 'mpy'; - if (current.async) { - type += '-async'; - tag.setAttribute('async', ''); - } + if (current.async) type += '-async'; + else tag.setAttribute('async', 'false'); if (current.worker) { type += '-worker'; tag.setAttribute('worker', ''); diff --git a/test/integration/interpreter/pyodide/config-json.html b/test/integration/interpreter/pyodide/config-json.html index a30dd780..a63e07b7 100644 --- a/test/integration/interpreter/pyodide/config-json.html +++ b/test/integration/interpreter/pyodide/config-json.html @@ -8,6 +8,12 @@ + + diff --git a/test/integration/interpreter/pyodide/index_urls.html b/test/integration/interpreter/pyodide/index_urls.html new file mode 100644 index 00000000..07204799 --- /dev/null +++ b/test/integration/interpreter/pyodide/index_urls.html @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/test/integration/interpreter/pyodide/packages.html b/test/integration/interpreter/pyodide/packages.html new file mode 100644 index 00000000..a062b136 --- /dev/null +++ b/test/integration/interpreter/pyodide/packages.html @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/test/integration/interpreter/pyodide/packages.toml b/test/integration/interpreter/pyodide/packages.toml new file mode 100644 index 00000000..3a9d8d7d --- /dev/null +++ b/test/integration/interpreter/pyodide/packages.toml @@ -0,0 +1 @@ +packages = ['unknown_package_name'] diff --git a/test/integration/interpreter/pyodide/worker-function.html b/test/integration/interpreter/pyodide/worker-function.html new file mode 100644 index 00000000..9b723610 --- /dev/null +++ b/test/integration/interpreter/pyodide/worker-function.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/test/integration/pyodide.js b/test/integration/pyodide.js index 9035d24b..34f0d5d4 100644 --- a/test/integration/pyodide.js +++ b/test/integration/pyodide.js @@ -13,6 +13,29 @@ export default (playwright, baseURL) => { test('Pyodide config as JSON', python.configAsJSON(playwright, baseURL)); + test('Pyodide unknown package', async ({ page }) => { + const warnings = []; + page.on('console', message => { + if (message.type() === 'warning') { + warnings.push(message.text()); + } + }); + await page.goto(`${baseURL}/packages.html`); + await page.waitForSelector('html.error'); + await expect(warnings.length).toBe(1); + await expect(/Pyodide [0-9.]+ might not support unknown_package_name/.test(warnings[0])).toBe(true); + }); + + test('Pyodide config with passthrough', async ({ page }) => { + // Test that a config passed as object works out of the box. + const logs = []; + page.on('console', msg => logs.push(msg.text())); + await page.goto(`${baseURL}/config-passthrough.html`); + await page.waitForSelector('html.cleared'); + await page.waitForSelector('html.ready'); + await expect(logs.at(-1)).toBe('hello from A'); + }); + test('Pyodide sync (time)', async ({ page }) => { const logs = []; page.on('console', msg => logs.push({text: msg.text(), time: new Date})); @@ -33,4 +56,9 @@ export default (playwright, baseURL) => { test('Pyodide transform', python.error(playwright, baseURL)); test('Pyodide events ready', python.disabledUntilReady(playwright, baseURL)); + + test('Pyodide index_urls', async ({ page }) => { + await page.goto(`${baseURL}/index_urls.html`); + await page.waitForSelector('html.test_foo'); + }); }; diff --git a/test/interpreter/index.html b/test/interpreter/index.html new file mode 100644 index 00000000..e5088eff --- /dev/null +++ b/test/interpreter/index.html @@ -0,0 +1,15 @@ + + + + Pyodide Worker Version + + + + + + + + diff --git a/test/issue-2240/a.zip b/test/issue-2240/a.zip new file mode 100644 index 00000000..adf0ef1d Binary files /dev/null and b/test/issue-2240/a.zip differ diff --git a/test/issue-2240/b.zip b/test/issue-2240/b.zip new file mode 100644 index 00000000..0ad94659 Binary files /dev/null and b/test/issue-2240/b.zip differ diff --git a/test/issue-2240/index.html b/test/issue-2240/index.html new file mode 100644 index 00000000..bbefe67b --- /dev/null +++ b/test/issue-2240/index.html @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/test/issue-2240/settings.json b/test/issue-2240/settings.json new file mode 100644 index 00000000..b067782b --- /dev/null +++ b/test/issue-2240/settings.json @@ -0,0 +1,6 @@ +{ + "files": { + "a.zip": "./*", + "b.zip": "./*" + } +} diff --git a/test/matplot.html b/test/matplot.html index 6ed14835..a52f9a7f 100644 --- a/test/matplot.html +++ b/test/matplot.html @@ -5,6 +5,7 @@ python + diff --git a/test/matplot.json.html b/test/matplot.json.html index dd67ccba..e35d4eb1 100644 --- a/test/matplot.json.html +++ b/test/matplot.json.html @@ -5,6 +5,7 @@ python + diff --git a/test/matplot.worker.html b/test/matplot.worker.html index ac4c22e0..85da3ff3 100644 --- a/test/matplot.worker.html +++ b/test/matplot.worker.html @@ -5,6 +5,7 @@ python + diff --git a/test/micropython.html b/test/micropython.html index f2bbd1c9..0f25d8b2 100644 --- a/test/micropython.html +++ b/test/micropython.html @@ -5,6 +5,7 @@ python + diff --git a/test/micropython/random.html b/test/micropython/random.html index 5e8efce1..7fb0d9c1 100644 --- a/test/micropython/random.html +++ b/test/micropython/random.html @@ -3,6 +3,7 @@ + + + + + + + diff --git a/test/py-lock-file/index.py b/test/py-lock-file/index.py new file mode 100644 index 00000000..b7815965 --- /dev/null +++ b/test/py-lock-file/index.py @@ -0,0 +1,4 @@ +import jsonpointer +import js + +js.document.body.textContent = "OK"; diff --git a/test/pygame-ce/config.toml b/test/pygame-ce/config.toml new file mode 100644 index 00000000..9837409d --- /dev/null +++ b/test/pygame-ce/config.toml @@ -0,0 +1 @@ +packages = ["pygame-ce"] diff --git a/test/pygame-ce/index.html b/test/pygame-ce/index.html new file mode 100644 index 00000000..233a1199 --- /dev/null +++ b/test/pygame-ce/index.html @@ -0,0 +1,15 @@ + + + + PyScript 0.28 warning on pygame-ce + + + + + + + + diff --git a/test/pyodide/index.html b/test/pyodide/index.html new file mode 100644 index 00000000..c0199dac --- /dev/null +++ b/test/pyodide/index.html @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/test/raw/converter.js b/test/raw/converter.js new file mode 100644 index 00000000..eb18fc7b --- /dev/null +++ b/test/raw/converter.js @@ -0,0 +1,53 @@ +import { symbol } from 'https://esm.run/@ungap/serialization-registry'; + +const { construct } = Reflect; +const { defineProperty, fromEntries } = Object; + +const name = '#Py2JS:Proxy'; +const patch = Symbol.for(name); +const patched = patch in globalThis; + +// pyodide +const toJsOptions = { dict_converter: fromEntries }; + +export const converter = patched ? globalThis[patch] : [ + // pyodide + target => { + if ('toJs' in target) + return target.toJs(toJsOptions); + }, + + // micropython + target => { + const { constructor } = target; + if (constructor && 'toJs' in constructor) + return constructor.toJs(target); + }, +]; + +if (!patched) { + defineProperty(globalThis, patch, { value: converter }); + defineProperty(globalThis, 'Proxy', { + value: new Proxy(Proxy, { + construct(target, args, newTarget) { + const original = args[1]?.get; + if (original && !original.name !== name) { + args[1].get = defineProperty( + function (target, prop, receiver) { + if (prop === symbol) { + for (let value, i = 0; i < converter.length; i++) { + value = converter[i](target); + if (value) return value; + } + } + return original.call(this, target, prop, receiver); + }, + 'name', + { value: name } + ); + } + return construct(target, args, newTarget); + } + }) + }); +} diff --git a/test/raw/micropython/index.html b/test/raw/micropython/index.html index 611dc3d9..af3b4185 100644 --- a/test/raw/micropython/index.html +++ b/test/raw/micropython/index.html @@ -4,6 +4,13 @@ diff --git a/test/raw/pyodide/index.html b/test/raw/pyodide/index.html index 159afb2a..2a531f00 100644 --- a/test/raw/pyodide/index.html +++ b/test/raw/pyodide/index.html @@ -4,6 +4,13 @@ diff --git a/test/no-sw.html b/test/sw.html similarity index 60% rename from test/no-sw.html rename to test/sw.html index 01d9fb51..efe8a626 100644 --- a/test/no-sw.html +++ b/test/sw.html @@ -6,13 +6,14 @@ python + - diff --git a/test/sw.js b/test/sw.js new file mode 100644 index 00000000..7b19b02d --- /dev/null +++ b/test/sw.js @@ -0,0 +1 @@ +var e=Promise.withResolvers.bind(Promise);const[t,n]=((t=e=>e)=>{const n=new Map;let s=0;return[()=>{let a;do{a=t(s++)}while(n.has(a));const o=e();return n.set(a,o),[a,o.promise]},(e,t,s)=>{const a=n.get(e);n.delete(e),s?a?.reject(s):a?.resolve(t)}]})(),{parse:s}=JSON,a=new BroadcastChannel("dd78209b-186c-4f83-80e9-406becb7d9f3");a.onmessage=e=>n.apply(null,e.data);var o=Object.freeze({__proto__:null,activate:e=>e.waitUntil(clients.claim()),fetch:async e=>{const{request:n}=e;if("POST"===n.method&&n.url===`${location.href}?sabayon`){e.stopImmediatePropagation(),e.preventDefault();const[o,r]=t(),i=e=>new Response(`[${[].join.call(e,",")}]`,n.headers);e.respondWith(r.then(i,i));const[l,c]=s(await n.text());a.postMessage([o,l,c])}},install:()=>skipWaiting()});for(const e in o)addEventListener(e,o[e]); \ No newline at end of file diff --git a/test/t.py b/test/t.py new file mode 100644 index 00000000..4b084b76 --- /dev/null +++ b/test/t.py @@ -0,0 +1,75 @@ +__all__ = ["t"] + + +class Interpolation: + def __init__(self, expr): + self.expr = expr + + def __getattr__(self, name): + expr = getattr(self, "expr") + if name == "value": + return eval(expr) + if name == "expr": + return expr + if name == "conv": + return None + if name == "format_spec": + return "" + + +class Template: + def __init__(self, args): + self.args = tuple(args) + + def __getattr__(self, name): + args = getattr(self, "args") + if name == "args": + return args + if name == "strings": + return args[::2] + if name == "values": + return [i.value for i in args[1::2]] + if name == "interpolations": + return args[1::2] + + def __str__(self): + out = [] + i = 0 + for arg in getattr(self, "args"): + out.append(i % 2 and str(arg.value) or arg) + i += 1 + return "".join(out) + + +drop = lambda s: s.replace("{{", "\x01").replace("}}", "\x02") +add = lambda s: s.replace("\x01", "{{").replace("\x02", "}}") + + +# PEP750 shim as function for MicroPython or Pyodide until it lands +def t(content): + # sanitize brackets (drop double brackets) + content = drop(content) + # fail if the format string is not balanced + if content.count("{") != content.count("}"): + raise ValueError("single '{' or '}' encountered in format string") + # find outer most interesting curly braces + l = len(content) + i = 0 + j = 0 + start = 0 + opened = 0 + args = [] + for c in content: + if c == "{": + if opened == 0: + j = i + opened += 1 + elif c == "}": + opened -= 1 + if opened == 0: + args.append(add(content[start:j:])) + args.append(Interpolation(add(content[j + 1 : i :]))) + start = i + 1 + i += 1 + args.append(add(content[start::])) + return Template(args) diff --git a/test/unstuck/index.html b/test/unstuck/index.html new file mode 100644 index 00000000..449e71c0 --- /dev/null +++ b/test/unstuck/index.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/test/worker/sync.html b/test/worker/sync.html new file mode 100644 index 00000000..f2b726cb --- /dev/null +++ b/test/worker/sync.html @@ -0,0 +1,23 @@ + + + + + + python workers + + + + + + diff --git a/test/worker/sync.py b/test/worker/sync.py new file mode 100644 index 00000000..e88f8ab1 --- /dev/null +++ b/test/worker/sync.py @@ -0,0 +1,3 @@ +from polyscript import xworker + +print("worker", xworker.sync.data(memoryview(b"hello"))) diff --git a/tsconfig.json b/tsconfig.json index 04a45d42..aa7e8930 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,7 @@ "declarationDir": "types" }, "include": [ + "node_modules/@webreflection/idb-map/sync.js", "esm/index.js", "esm/exports.js", "esm/xworker.js" diff --git a/versions/micropython b/versions/micropython index 29353f23..1227fb4e 100644 --- a/versions/micropython +++ b/versions/micropython @@ -1 +1 @@ -1.24.0-preview-114 +1.27.0-preview-283 diff --git a/versions/pyodide b/versions/pyodide index 30f6cf8d..ae6dd4e2 100644 --- a/versions/pyodide +++ b/versions/pyodide @@ -1 +1 @@ -0.26.1 +0.29.0 diff --git a/versions/ruby-wasm-wasi b/versions/ruby-wasm-wasi index 097a15a2..37c2961c 100644 --- a/versions/ruby-wasm-wasi +++ b/versions/ruby-wasm-wasi @@ -1 +1 @@ -2.6.2 +2.7.2 diff --git a/versions/webr b/versions/webr index 1d0ba9ea..b49b2533 100644 --- a/versions/webr +++ b/versions/webr @@ -1 +1 @@ -0.4.0 +0.5.6