Repository: pyscript/pyscript (language: python)

Issue:
V0.6.x micropip often fails to discover package
### Checklist

- [X] I added a descriptive title
- [X] I searched for other issues and couldn't find a solution or duplication
- [X] I already searched in Google and didn't find any good information or help

### What happened?

There seems to be a regression on Micropip behavior on v0.6.x where micropip will fail to install packages if the user has pyodide cached within the browser's `IndexDB`.

Upon clearing indexdb, the browser will then be able to install packages via micropip again.

This was tested on all Chrome, FF, and Edge on Windows 11.

This issue does not exist on `0.4.x` and `0.5.x`.

### What browsers are you seeing the problem on? (if applicable)

Firefox, Chrome, Microsoft Edge

### Console info

```shell
custom.js:182 Uncaught (in promise) Error: No known package with name 'jsonpointer==3.0.0'
    at addPackageToLoad (pyodide.asm.js:10:90432)
    at recursiveDependencies (pyodide.asm.js:10:90746)
    at loadPackage (pyodide.asm.js:10:93056)
    at initializePackageIndex (pyodide.asm.js:10:89787)
```


### Additional Context

```html
<py-script async config='{"packages":["reactpy==1.0.2","jsonpointer==3.0.0","ssl"],"js_modules":{"main":{"/static/reactpy_django/morphdom/morphdom-esm.js":"morphdom","/static/moment.js":"moment"}}}'></py-script>
```


Discussion / hints:
Aha, I think the cache is not smart enough to handle versioning, which is indeed a bummer.
Apologies for the regression, will try to work this out ASAP, thank you for filing this issue.

/cc @ntoll 
Something notable is I don't believe this is related to versioning, because if I refresh the web page it proceeds to break again. 

More accurately, anytime that pyodide is loaded from indexdb, then micropip does not work.

I haven't dug much deeper than that, but perhaps micropip somehow gets mangled when loading from indexdb.

Fix patch:
diff --git a/core/package-lock.json b/core/package-lock.json
index eba5bdb3604..42b2552da75 100644
--- a/core/package-lock.json
+++ b/core/package-lock.json
@@ -1,26 +1,26 @@
 {
     "name": "@pyscript/core",
-    "version": "0.6.7",
+    "version": "0.6.13",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
             "name": "@pyscript/core",
-            "version": "0.6.7",
+            "version": "0.6.13",
             "license": "APACHE-2.0",
             "dependencies": {
                 "@ungap/with-resolvers": "^0.1.0",
                 "@webreflection/idb-map": "^0.3.2",
                 "add-promise-listener": "^0.1.3",
                 "basic-devtools": "^0.1.6",
-                "polyscript": "^0.16.3",
-                "sabayon": "^0.5.2",
+                "polyscript": "^0.16.4",
+                "sabayon": "^0.6.0",
                 "sticky-module": "^0.1.1",
                 "to-json-callback": "^0.1.1",
                 "type-checked-collections": "^0.1.7"
             },
             "devDependencies": {
-                "@codemirror/commands": "^6.7.0",
+                "@codemirror/commands": "^6.7.1",
                 "@codemirror/lang-python": "^6.1.6",
                 "@codemirror/language": "^6.10.3",
                 "@codemirror/state": "^6.4.1",
@@ -32,13 +32,13 @@
                 "@webreflection/toml-j0.4": "^1.1.3",
                 "@xterm/addon-fit": "^0.10.0",
                 "@xterm/addon-web-links": "^0.11.0",
-                "bun": "^1.1.30",
+                "bun": "^1.1.33",
                 "chokidar": "^4.0.1",
                 "codedent": "^0.1.2",
                 "codemirror": "^6.0.1",
-                "eslint": "^9.12.0",
+                "eslint": "^9.13.0",
                 "flatted": "^3.3.1",
-                "rollup": "^4.24.0",
+                "rollup": "^4.24.2",
                 "rollup-plugin-postcss": "^4.0.2",
                 "rollup-plugin-string": "^3.0.0",
                 "static-handler": "^0.5.3",
@@ -71,9 +71,9 @@
             }
         },
         "node_modules/@codemirror/commands": {
-            "version": "6.7.0",
-            "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.0.tgz",
-            "integrity": "sha512-+cduIZ2KbesDhbykV02K25A5xIVrquSPz4UxxYBemRlAT2aW8dhwUgLDwej7q/RJUHKk4nALYcR1puecDvbdqw==",
+            "version": "6.7.1",
+            "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.1.tgz",
+            "integrity": "sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -210,9 +210,9 @@
             }
         },
         "node_modules/@eslint/core": {
-            "version": "0.6.0",
-            "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz",
-            "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==",
+            "version": "0.7.0",
+            "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz",
+            "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==",
             "dev": true,
             "license": "Apache-2.0",
             "engines": {
@@ -244,9 +244,9 @@
             }
         },
         "node_modules/@eslint/js": {
-            "version": "9.12.0",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz",
-            "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==",
+            "version": "9.13.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz",
+            "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==",
             "dev": true,
             "license": "MIT",
             "engines": {
@@ -432,9 +432,9 @@
             }
         },
         "node_modules/@oven/bun-darwin-aarch64": {
-            "version": "1.1.30",
-            "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.1.30.tgz",
-            "integrity": "sha512-D07QioP+QXlouvIqQIS+7r2zq4lTNd6he79rhKsRQRZGFf9i3NPu87zspUpCaFEu//DZ35DYTt+5anQpAzpoxA==",
+            "version": "1.1.33",
+            "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.1.33.tgz",
+            "integrity": "sha512-HF2lVCkfKlugBwVVuwYb1AIiewCSKq+6KaojbRzMlZM2V2nBSfBE5wefRxfU76dJQIcnxnplX8GKiCd7rZeUfg==",
             "cpu": [
                 "arm64"
             ],
@@ -446,9 +446,9 @@
             ]
         },
         "node_modules/@oven/bun-darwin-x64": {
-            "version": "1.1.30",
-            "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.1.30.tgz",
-            "integrity": "sha512-xZ4gTehS6QwN6bsJfDycCNneKoUMaFUQhQg24bJzXS4JPDxeKg1W7PS5AE+U9apz5Dx6//+D4RwVpAPG2LXt0w==",
+            "version": "1.1.33",
+            "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.1.33.tgz",
+            "integrity": "sha512-Smc0Au8CPPmD4rYEOnhW+lxvVZVcnPkWXPMObl8cPOuV3Ue8cVZQVsbP6deumLfECrDvthsCPVKx2fRPYUixJQ==",
             "cpu": [
                 "x64"
             ],
@@ -460,9 +460,9 @@
             ]
         },
         "node_modules/@oven/bun-darwin-x64-baseline": {
-            "version": "1.1.30",
-            "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.1.30.tgz",
-            "integrity": "sha512-1kFUCxHx7WuEbLDmqm0m2UKBd3S4Ln6qKQ4gxU4umMLFkmvDJn6PszDruFInxGKFLoTAmbXNYNVWkkG/ekt/Lg==",
+            "version": "1.1.33",
+            "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.1.33.tgz",
+            "integrity": "sha512-gjc4oiFhgqTVhC4LVfhqevuacmHHucmzPK4tsQshU0GdPdbvSihfpbFrK74+uPXipV+pJ2zIg2UK2OJpUblMng==",
             "cpu": [
                 "x64"
             ],
@@ -474,9 +474,9 @@
             ]
         },
         "node_modules/@oven/bun-linux-aarch64": {
-            "version": "1.1.30",
-            "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.1.30.tgz",
-            "integrity": "sha512-SfHHLlph6fptDXyyChcUkeDbEZr2ww1p2BucV6OrvzwTOPi8pVmXA4360YT8ggR/3AHPp4GO36VaD+FU2Ocbxw==",
+            "version": "1.1.33",
+            "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.1.33.tgz",
+            "integrity": "sha512-HpD4t8Ip4BswMVoCmT97fjNUlPP+HNWp18PaarLLx6WhU8A1Ua9XyX1W1UjEYqjrAsEMs972H69e+76ZSRX+Pw==",
             "cpu": [
                 "arm64"
             ],
@@ -488,9 +488,9 @@
             ]
         },
         "node_modules/@oven/bun-linux-x64": {
-            "version": "1.1.30",
-            "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.1.30.tgz",
-            "integrity": "sha512-1mC39jQSaECytEKAZdCZmv3ZreMsp7aoxnBwmJtVd2Z7urnw17PKi4dKkZd/R+AubsNYtXtW4jeM8SEa5sUJRw==",
+            "version": "1.1.33",
+            "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.1.33.tgz",
+            "integrity": "sha512-aODJGbky+osqyh7T4LITqu5CJYq9XAz5f507EJIBwBU3ZbYQ/1eNv+0nu5wHvTafj29g8tlBhgVDpkrWc3u3pA==",
             "cpu": [
                 "x64"
             ],
@@ -502,9 +502,9 @@
             ]
         },
         "node_modules/@oven/bun-linux-x64-baseline": {
-            "version": "1.1.30",
-            "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.1.30.tgz",
-            "integrity": "sha512-/b/VuNOaAYmsVk9MvfwKcCYARJPUg78hebxNyD5DSajAf3dqtUSnf7QYcq/3mxWH++N+gM7uRTrGksGS63+ZUw==",
+            "version": "1.1.33",
+            "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.1.33.tgz",
+            "integrity": "sha512-u/VKLCdozwjKk+8en3EMK1ZQGSnnYIZR85w8bU60ZPzFau+jV43TkqBTWYi9JzDxNDMR66b18iWuxlPNUBK7ew==",
             "cpu": [
                 "x64"
             ],
@@ -516,9 +516,9 @@
             ]
         },
         "node_modules/@oven/bun-windows-x64": {
-            "version": "1.1.30",
-            "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.1.30.tgz",
-            "integrity": "sha512-mdRjNtD9NIA8CiH6N1zrIVE6oAtDko/c29H1s00UA+5O/WhXhg95G8IyInD8hN3vAEz8H2lGBgLG2EGfSFxnGg==",
+            "version": "1.1.33",
+            "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.1.33.tgz",
+            "integrity": "sha512-ldFk0tywL1m56RzVXYbO+0s48N1/UGPpAay/yN7zYR5LM/A97YGEtCydc2tULwI7uabQgzDI423LDL184uR6ig==",
             "cpu": [
                 "x64"
             ],
@@ -530,9 +530,9 @@
             ]
         },
         "node_modules/@oven/bun-windows-x64-baseline": {
-            "version": "1.1.30",
-            "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.1.30.tgz",
-            "integrity": "sha512-ERQ4/ogzbFvHjpyHcnruc8bnryvDvUoiWi6vczfQ4M/idJc+Kg5VSEJiF5k7946rIZGamG6QWgRxtpIglD4/Zw==",
+            "version": "1.1.33",
+            "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.1.33.tgz",
+            "integrity": "sha512-Ouiiv7bVPuoqjFCXc4HiRJ+ttrGiNsXQfv/8Wtsc5bSBuBBCjmKy0I2MpFXZV3YSi8Jp7LEzoYM0Kv1AvX7ySQ==",
             "cpu": [
                 "x64"
             ],
@@ -671,9 +671,9 @@
             }
         },
         "node_modules/@rollup/rollup-android-arm-eabi": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
-            "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.2.tgz",
+            "integrity": "sha512-ufoveNTKDg9t/b7nqI3lwbCG/9IJMhADBNjjz/Jn6LxIZxD7T5L8l2uO/wD99945F1Oo8FvgbbZJRguyk/BdzA==",
             "cpu": [
                 "arm"
             ],
@@ -685,9 +685,9 @@
             ]
         },
         "node_modules/@rollup/rollup-android-arm64": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
-            "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.2.tgz",
+            "integrity": "sha512-iZoYCiJz3Uek4NI0J06/ZxUgwAfNzqltK0MptPDO4OR0a88R4h0DSELMsflS6ibMCJ4PnLvq8f7O1d7WexUvIA==",
             "cpu": [
                 "arm64"
             ],
@@ -699,9 +699,9 @@
             ]
         },
         "node_modules/@rollup/rollup-darwin-arm64": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
-            "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.2.tgz",
+            "integrity": "sha512-/UhrIxobHYCBfhi5paTkUDQ0w+jckjRZDZ1kcBL132WeHZQ6+S5v9jQPVGLVrLbNUebdIRpIt00lQ+4Z7ys4Rg==",
             "cpu": [
                 "arm64"
             ],
@@ -713,9 +713,9 @@
             ]
         },
         "node_modules/@rollup/rollup-darwin-x64": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
-            "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.2.tgz",
+            "integrity": "sha512-1F/jrfhxJtWILusgx63WeTvGTwE4vmsT9+e/z7cZLKU8sBMddwqw3UV5ERfOV+H1FuRK3YREZ46J4Gy0aP3qDA==",
             "cpu": [
                 "x64"
             ],
@@ -726,10 +726,38 @@
                 "darwin"
             ]
         },
+        "node_modules/@rollup/rollup-freebsd-arm64": {
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.2.tgz",
+            "integrity": "sha512-1YWOpFcGuC6iGAS4EI+o3BV2/6S0H+m9kFOIlyFtp4xIX5rjSnL3AwbTBxROX0c8yWtiWM7ZI6mEPTI7VkSpZw==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "freebsd"
+            ]
+        },
+        "node_modules/@rollup/rollup-freebsd-x64": {
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.2.tgz",
+            "integrity": "sha512-3qAqTewYrCdnOD9Gl9yvPoAoFAVmPJsBvleabvx4bnu1Kt6DrB2OALeRVag7BdWGWLhP1yooeMLEi6r2nYSOjg==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "freebsd"
+            ]
+        },
         "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
-            "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.2.tgz",
+            "integrity": "sha512-ArdGtPHjLqWkqQuoVQ6a5UC5ebdX8INPuJuJNWRe0RGa/YNhVvxeWmCTFQ7LdmNCSUzVZzxAvUznKaYx645Rig==",
             "cpu": [
                 "arm"
             ],
@@ -741,9 +769,9 @@
             ]
         },
         "node_modules/@rollup/rollup-linux-arm-musleabihf": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
-            "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.2.tgz",
+            "integrity": "sha512-B6UHHeNnnih8xH6wRKB0mOcJGvjZTww1FV59HqJoTJ5da9LCG6R4SEBt6uPqzlawv1LoEXSS0d4fBlHNWl6iYw==",
             "cpu": [
                 "arm"
             ],
@@ -755,9 +783,9 @@
             ]
         },
         "node_modules/@rollup/rollup-linux-arm64-gnu": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
-            "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.2.tgz",
+            "integrity": "sha512-kr3gqzczJjSAncwOS6i7fpb4dlqcvLidqrX5hpGBIM1wtt0QEVtf4wFaAwVv8QygFU8iWUMYEoJZWuWxyua4GQ==",
             "cpu": [
                 "arm64"
             ],
@@ -769,9 +797,9 @@
             ]
         },
         "node_modules/@rollup/rollup-linux-arm64-musl": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
-            "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.2.tgz",
+            "integrity": "sha512-TDdHLKCWgPuq9vQcmyLrhg/bgbOvIQ8rtWQK7MRxJ9nvaxKx38NvY7/Lo6cYuEnNHqf6rMqnivOIPIQt6H2AoA==",
             "cpu": [
                 "arm64"
             ],
@@ -783,9 +811,9 @@
             ]
         },
         "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
-            "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.2.tgz",
+            "integrity": "sha512-xv9vS648T3X4AxFFZGWeB5Dou8ilsv4VVqJ0+loOIgDO20zIhYfDLkk5xoQiej2RiSQkld9ijF/fhLeonrz2mw==",
             "cpu": [
                 "ppc64"
             ],
@@ -797,9 +825,9 @@
             ]
         },
         "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
-            "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.2.tgz",
+            "integrity": "sha512-tbtXwnofRoTt223WUZYiUnbxhGAOVul/3StZ947U4A5NNjnQJV5irKMm76G0LGItWs6y+SCjUn/Q0WaMLkEskg==",
             "cpu": [
                 "riscv64"
             ],
@@ -811,9 +839,9 @@
             ]
         },
         "node_modules/@rollup/rollup-linux-s390x-gnu": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
-            "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.2.tgz",
+            "integrity": "sha512-gc97UebApwdsSNT3q79glOSPdfwgwj5ELuiyuiMY3pEWMxeVqLGKfpDFoum4ujivzxn6veUPzkGuSYoh5deQ2Q==",
             "cpu": [
                 "s390x"
             ],
@@ -825,9 +853,9 @@
             ]
         },
         "node_modules/@rollup/rollup-linux-x64-gnu": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
-            "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.2.tgz",
+            "integrity": "sha512-jOG/0nXb3z+EM6SioY8RofqqmZ+9NKYvJ6QQaa9Mvd3RQxlH68/jcB/lpyVt4lCiqr04IyaC34NzhUqcXbB5FQ==",
             "cpu": [
                 "x64"
             ],
@@ -839,9 +867,9 @@
             ]
         },
         "node_modules/@rollup/rollup-linux-x64-musl": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
-            "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.2.tgz",
+            "integrity": "sha512-XAo7cJec80NWx9LlZFEJQxqKOMz/lX3geWs2iNT5CHIERLFfd90f3RYLLjiCBm1IMaQ4VOX/lTC9lWfzzQm14Q==",
             "cpu": [
                 "x64"
             ],
@@ -853,9 +881,9 @@
             ]
         },
         "node_modules/@rollup/rollup-win32-arm64-msvc": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
-            "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.2.tgz",
+            "integrity": "sha512-A+JAs4+EhsTjnPQvo9XY/DC0ztaws3vfqzrMNMKlwQXuniBKOIIvAAI8M0fBYiTCxQnElYu7mLk7JrhlQ+HeOw==",
             "cpu": [
                 "arm64"
             ],
@@ -867,9 +895,9 @@
             ]
         },
         "node_modules/@rollup/rollup-win32-ia32-msvc": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
-            "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.2.tgz",
+            "integrity": "sha512-ZhcrakbqA1SCiJRMKSU64AZcYzlZ/9M5LaYil9QWxx9vLnkQ9Vnkve17Qn4SjlipqIIBFKjBES6Zxhnvh0EAEw==",
             "cpu": [
                 "ia32"
             ],
@@ -881,9 +909,9 @@
             ]
         },
         "node_modules/@rollup/rollup-win32-x64-msvc": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
-            "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.2.tgz",
+            "integrity": "sha512-2mLH46K1u3r6uwc95hU+OR9q/ggYMpnS7pSp83Ece1HUQgF9Nh/QwTK5rcgbFnV9j+08yBrU5sA/P0RK2MSBNA==",
             "cpu": [
                 "x64"
             ],
@@ -925,6 +953,12 @@
             "dev": true,
             "license": "MIT"
         },
+        "node_modules/@ungap/raw-json": {
+            "version": "0.3.7",
+            "resolved": "https://registry.npmjs.org/@ungap/raw-json/-/raw-json-0.3.7.tgz",
+            "integrity": "sha512-+AXLlPw5EUzILQkgE1b4NW1rK11QLCL3RjDVn7JozAkbtdrFa25cDD6fDE6uM/W9xw+JH334zvb/X+NBLJ93CQ==",
+            "license": "MIT"
+        },
         "node_modules/@ungap/structured-clone": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -1137,9 +1171,9 @@
             "license": "MIT"
         },
         "node_modules/bun": {
-            "version": "1.1.30",
-            "resolved": "https://registry.npmjs.org/bun/-/bun-1.1.30.tgz",
-            "integrity": "sha512-ysRL1pq10Xba0jqVLPrKU3YIv0ohfp3cTajCPtpjCyppbn3lfiAVNpGoHfyaxS17OlPmWmR67UZRPw/EueQuug==",
+            "version": "1.1.33",
+            "resolved": "https://registry.npmjs.org/bun/-/bun-1.1.33.tgz",
+            "integrity": "sha512-pNcxhsh87TURsaXwwLnOggb1HWdSDgOOjupTuXINmQUZeZ4UzLeVK8h8trzIVA0ECijDex94AkAQ5/JG4r5gkg==",
             "cpu": [
                 "arm64",
                 "x64"
@@ -1157,14 +1191,14 @@
                 "bunx": "bin/bun.exe"
             },
             "optionalDependencies": {
-                "@oven/bun-darwin-aarch64": "1.1.30",
-                "@oven/bun-darwin-x64": "1.1.30",
-                "@oven/bun-darwin-x64-baseline": "1.1.30",
-                "@oven/bun-linux-aarch64": "1.1.30",
-                "@oven/bun-linux-x64": "1.1.30",
-                "@oven/bun-linux-x64-baseline": "1.1.30",
-                "@oven/bun-windows-x64": "1.1.30",
-                "@oven/bun-windows-x64-baseline": "1.1.30"
+                "@oven/bun-darwin-aarch64": "1.1.33",
+                "@oven/bun-darwin-x64": "1.1.33",
+                "@oven/bun-darwin-x64-baseline": "1.1.33",
+                "@oven/bun-linux-aarch64": "1.1.33",
+                "@oven/bun-linux-x64": "1.1.33",
+                "@oven/bun-linux-x64-baseline": "1.1.33",
+                "@oven/bun-windows-x64": "1.1.33",
+                "@oven/bun-windows-x64-baseline": "1.1.33"
             }
         },
         "node_modules/callsites": {
@@ -1270,9 +1304,9 @@
             }
         },
         "node_modules/coincident": {
-            "version": "2.2.2",
-            "resolved": "https://registry.npmjs.org/coincident/-/coincident-2.2.2.tgz",
-            "integrity": "sha512-J+83hXTUEtxPZYZSR9Zihz8z23gFTyvQPROjCovEeyH/Jn8CVpjwo8DM49UJ5OTPa24ZeFxPViE6gbCtrUO7pQ==",
+            "version": "2.2.3",
+            "resolved": "https://registry.npmjs.org/coincident/-/coincident-2.2.3.tgz",
+            "integrity": "sha512-yxLzgZqDSEPOczzNoM4T3gVyg955Kbk5snkaAkQw8xvh8zfxGuUtbaOaDNCaP3HxWeRZZjwbJi80hapAikpvoQ==",
             "license": "MIT",
             "dependencies": {
                 "gc-hook": "^0.4.1",
@@ -1280,6 +1314,15 @@
                 "sabayon": "^0.5.2"
             }
         },
+        "node_modules/coincident/node_modules/sabayon": {
+            "version": "0.5.2",
+            "resolved": "https://registry.npmjs.org/sabayon/-/sabayon-0.5.2.tgz",
+            "integrity": "sha512-7y8dwQFhInkUuI0JL1rAGLYeKz9LypgfzMOVO04v9pYvepzYJ6B7MhyoRiVIx2sGm7MhrD5h/SaHP6p+5Uc7Cw==",
+            "license": "MIT",
+            "bin": {
+                "sabayon": "cli.cjs"
+            }
+        },
         "node_modules/color-convert": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -1667,18 +1710,18 @@
             }
         },
         "node_modules/eslint": {
-            "version": "9.12.0",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz",
-            "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==",
+            "version": "9.13.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz",
+            "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.2.0",
                 "@eslint-community/regexpp": "^4.11.0",
                 "@eslint/config-array": "^0.18.0",
-                "@eslint/core": "^0.6.0",
+                "@eslint/core": "^0.7.0",
                 "@eslint/eslintrc": "^3.1.0",
-                "@eslint/js": "9.12.0",
+                "@eslint/js": "9.13.0",
                 "@eslint/plugin-kit": "^0.2.0",
                 "@humanfs/node": "^0.16.5",
                 "@humanwhocodes/module-importer": "^1.0.1",
@@ -2634,9 +2677,9 @@
             }
         },
         "node_modules/polyscript": {
-            "version": "0.16.3",
-            "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.16.3.tgz",
-            "integrity": "sha512-I3kHxt62FMRAX2iVl24iCEtG4UnUInMSbv/LnwevkmjOErLPAQtES4CNzU/fgKRpXYCqp0WWQaRvRYkJhpMIbA==",
+            "version": "0.16.4",
+            "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.16.4.tgz",
+            "integrity": "sha512-Sth8HFEjK8MY1zJ90fVMy1xiDlHWheq+BxiNOg2wyvsGDZHgLbnt4LCpiLisBnFEsBX6/pj2OJbgATnXnxp6jA==",
             "license": "APACHE-2.0",
             "dependencies": {
                 "@ungap/structured-clone": "^1.2.0",
@@ -2645,7 +2688,7 @@
                 "@webreflection/idb-map": "^0.3.2",
                 "basic-devtools": "^0.1.6",
                 "codedent": "^0.1.2",
-                "coincident": "^2.2.2",
+                "coincident": "^2.2.3",
                 "gc-hook": "^0.4.1",
                 "html-escaper": "^3.0.3",
                 "proxy-target": "^3.0.2",
@@ -3338,9 +3381,9 @@
             }
         },
         "node_modules/rollup": {
-            "version": "4.24.0",
-            "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
-            "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
+            "version": "4.24.2",
+            "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.2.tgz",
+            "integrity": "sha512-do/DFGq5g6rdDhdpPq5qb2ecoczeK6y+2UAjdJ5trjQJj5f1AiVdLRWRc9A9/fFukfvJRgM0UXzxBIYMovm5ww==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -3354,22 +3397,24 @@
                 "npm": ">=8.0.0"
             },
             "optionalDependencies": {
-                "@rollup/rollup-android-arm-eabi": "4.24.0",
-                "@rollup/rollup-android-arm64": "4.24.0",
-                "@rollup/rollup-darwin-arm64": "4.24.0",
-                "@rollup/rollup-darwin-x64": "4.24.0",
-                "@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
-                "@rollup/rollup-linux-arm-musleabihf": "4.24.0",
-                "@rollup/rollup-linux-arm64-gnu": "4.24.0",
-                "@rollup/rollup-linux-arm64-musl": "4.24.0",
-                "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
-                "@rollup/rollup-linux-riscv64-gnu": "4.24.0",
-                "@rollup/rollup-linux-s390x-gnu": "4.24.0",
-                "@rollup/rollup-linux-x64-gnu": "4.24.0",
-                "@rollup/rollup-linux-x64-musl": "4.24.0",
-                "@rollup/rollup-win32-arm64-msvc": "4.24.0",
-                "@rollup/rollup-win32-ia32-msvc": "4.24.0",
-                "@rollup/rollup-win32-x64-msvc": "4.24.0",
+                "@rollup/rollup-android-arm-eabi": "4.24.2",
+                "@rollup/rollup-android-arm64": "4.24.2",
+                "@rollup/rollup-darwin-arm64": "4.24.2",
+                "@rollup/rollup-darwin-x64": "4.24.2",
+                "@rollup/rollup-freebsd-arm64": "4.24.2",
+                "@rollup/rollup-freebsd-x64": "4.24.2",
+                "@rollup/rollup-linux-arm-gnueabihf": "4.24.2",
+                "@rollup/rollup-linux-arm-musleabihf": "4.24.2",
+                "@rollup/rollup-linux-arm64-gnu": "4.24.2",
+                "@rollup/rollup-linux-arm64-musl": "4.24.2",
+                "@rollup/rollup-linux-powerpc64le-gnu": "4.24.2",
+                "@rollup/rollup-linux-riscv64-gnu": "4.24.2",
+                "@rollup/rollup-linux-s390x-gnu": "4.24.2",
+                "@rollup/rollup-linux-x64-gnu": "4.24.2",
+                "@rollup/rollup-linux-x64-musl": "4.24.2",
+                "@rollup/rollup-win32-arm64-msvc": "4.24.2",
+                "@rollup/rollup-win32-ia32-msvc": "4.24.2",
+                "@rollup/rollup-win32-x64-msvc": "4.24.2",
                 "fsevents": "~2.3.2"
             }
         },
@@ -3429,10 +3474,13 @@
             "license": "MIT"
         },
         "node_modules/sabayon": {
-            "version": "0.5.2",
-            "resolved": "https://registry.npmjs.org/sabayon/-/sabayon-0.5.2.tgz",
-            "integrity": "sha512-7y8dwQFhInkUuI0JL1rAGLYeKz9LypgfzMOVO04v9pYvepzYJ6B7MhyoRiVIx2sGm7MhrD5h/SaHP6p+5Uc7Cw==",
+            "version": "0.6.0",
+            "resolved": "https://registry.npmjs.org/sabayon/-/sabayon-0.6.0.tgz",
+            "integrity": "sha512-7SAj50w4xoers9QTTF3yoH0sNBU6lk284FmJpG6pXTG3XOZ1L/qwHIYV0TiQYUtN1hma8FccnfIz+DKiHRlokA==",
             "license": "MIT",
+            "dependencies": {
+                "@ungap/raw-json": "^0.3.7"
+            },
             "bin": {
                 "sabayon": "cli.cjs"
             }
diff --git a/core/package.json b/core/package.json
index 3c3994d0131..3ba2de41775 100644
--- a/core/package.json
+++ b/core/package.json
@@ -1,6 +1,6 @@
 {
     "name": "@pyscript/core",
-    "version": "0.6.7",
+    "version": "0.6.13",
     "type": "module",
     "description": "PyScript",
     "module": "./index.js",
@@ -62,14 +62,14 @@
         "@webreflection/idb-map": "^0.3.2",
         "add-promise-listener": "^0.1.3",
         "basic-devtools": "^0.1.6",
-        "polyscript": "^0.16.3",
-        "sabayon": "^0.5.2",
+        "polyscript": "^0.16.4",
+        "sabayon": "^0.6.0",
         "sticky-module": "^0.1.1",
         "to-json-callback": "^0.1.1",
         "type-checked-collections": "^0.1.7"
     },
     "devDependencies": {
-        "@codemirror/commands": "^6.7.0",
+        "@codemirror/commands": "^6.7.1",
         "@codemirror/lang-python": "^6.1.6",
         "@codemirror/language": "^6.10.3",
         "@codemirror/state": "^6.4.1",
@@ -81,13 +81,13 @@
         "@webreflection/toml-j0.4": "^1.1.3",
         "@xterm/addon-fit": "^0.10.0",
         "@xterm/addon-web-links": "^0.11.0",
-        "bun": "^1.1.30",
+        "bun": "^1.1.33",
         "chokidar": "^4.0.1",
         "codedent": "^0.1.2",
         "codemirror": "^6.0.1",
-        "eslint": "^9.12.0",
+        "eslint": "^9.13.0",
         "flatted": "^3.3.1",
-        "rollup": "^4.24.0",
+        "rollup": "^4.24.2",
         "rollup-plugin-postcss": "^4.0.2",
         "rollup-plugin-string": "^3.0.0",
         "static-handler": "^0.5.3",
diff --git a/core/src/config.js b/core/src/config.js
index c6a32c8852c..d0cea02e610 100644
--- a/core/src/config.js
+++ b/core/src/config.js
@@ -56,7 +56,7 @@ const syntaxError = (type, url, { message }) => {
 const configs = new Map();
 
 for (const [TYPE] of TYPES) {
-    /** @type {Promise<[...any]>} A Promise wrapping any plugins which should be loaded. */
+    /** @type {() => Promise<[...any]>} A Promise wrapping any plugins which should be loaded. */
     let plugins;
 
     /** @type {any} The PyScript configuration parsed from the JSON or TOML object*. May be any of the return types of JSON.parse() or toml-j0.4's parse() ( {number | string | boolean | null | object | Array} ) */
@@ -135,24 +135,24 @@ for (const [TYPE] of TYPES) {
 
     // parse all plugins and optionally ignore only
     // those flagged as "undesired" via `!` prefix
-    const toBeAwaited = [];
-    for (const [key, value] of Object.entries(allPlugins)) {
-        if (error) {
-            if (key === "error") {
-                // show on page the config is broken, meaning that
-                // it was not possible to disable error plugin neither
-                // as that part wasn't correctly parsed anyway
-                value().then(({ notify }) => notify(error.message));
+    plugins = async () => {
+        const toBeAwaited = [];
+        for (const [key, value] of Object.entries(allPlugins)) {
+            if (error) {
+                if (key === "error") {
+                    // show on page the config is broken, meaning that
+                    // it was not possible to disable error plugin neither
+                    // as that part wasn't correctly parsed anyway
+                    value().then(({ notify }) => notify(error.message));
+                }
+            } else if (!parsed?.plugins?.includes(`!${key}`)) {
+                toBeAwaited.push(value().then(({ default: p }) => p));
+            } else if (key === "error") {
+                toBeAwaited.push(value().then(({ notOnDOM }) => notOnDOM()));
             }
-        } else if (!parsed?.plugins?.includes(`!${key}`)) {
-            toBeAwaited.push(value().then(({ default: p }) => p));
-        } else if (key === "error") {
-            toBeAwaited.push(value().then(({ notOnDOM }) => notOnDOM()));
         }
-    }
-
-    // assign plugins as Promise.all only if needed
-    plugins = Promise.all(toBeAwaited);
+        return await Promise.all(toBeAwaited);
+    };
 
     configs.set(TYPE, { config: parsed, configURL, plugins, error });
 }
diff --git a/core/src/core.js b/core/src/core.js
index 8d472718664..7b8351b1412 100644
--- a/core/src/core.js
+++ b/core/src/core.js
@@ -179,7 +179,7 @@ for (const [TYPE, interpreter] of TYPES) {
         // ensure plugins are bootstrapped already before custom type definition
         // NOTE: we cannot top-level await in here as plugins import other utilities
         //       from core.js itself so that custom definition should not be blocking.
-        plugins.then(() => {
+        plugins().then(() => {
             // possible early errors sent by polyscript
             const errors = new Map();
 
diff --git a/core/src/plugins/deprecations-manager.js b/core/src/plugins/deprecations-manager.js
index f57d18bd064..b9ce9356788 100644
--- a/core/src/plugins/deprecations-manager.js
+++ b/core/src/plugins/deprecations-manager.js
@@ -1,6 +1,6 @@
 // PyScript Derepcations Plugin
-import { hooks } from "../core.js";
 import { notify } from "./error.js";
+import { hooks } from "../core.js";
 
 // react lazily on PyScript bootstrap
 hooks.main.onReady.add(checkDeprecations);
diff --git a/core/src/plugins/py-terminal/mpy.js b/core/src/plugins/py-terminal/mpy.js
index deb1cfeb34e..1aef3430ec1 100644
--- a/core/src/plugins/py-terminal/mpy.js
+++ b/core/src/plugins/py-terminal/mpy.js
@@ -1,6 +1,6 @@
 // PyScript pyodide terminal plugin
-import { hooks, inputFailure } from "../../core.js";
 import { defineProperties } from "polyscript/exports";
+import { hooks, inputFailure } from "../../core.js";
 
 const bootstrapped = new WeakSet();
 
diff --git a/core/src/plugins/py-terminal/py.js b/core/src/plugins/py-terminal/py.js
index 4ca6d1f7e4d..6fde1ff841d 100644
--- a/core/src/plugins/py-terminal/py.js
+++ b/core/src/plugins/py-terminal/py.js
@@ -1,6 +1,6 @@
 // PyScript py-terminal plugin
-import { hooks } from "../../core.js";
 import { defineProperties } from "polyscript/exports";
+import { hooks } from "../../core.js";
 
 const bootstrapped = new WeakSet();
 
diff --git a/core/src/stdlib/pyscript.js b/core/src/stdlib/pyscript.js
index e3ce8d1a6dc..186efa72c87 100644
--- a/core/src/stdlib/pyscript.js
+++ b/core/src/stdlib/pyscript.js
@@ -11,7 +11,7 @@ export default {
     "media.py": "from pyscript import window\nfrom pyscript.ffi import to_js\nclass Device:\n\tdef __init__(A,device):A._dom_element=device\n\t@property\n\tdef id(self):return self._dom_element.deviceId\n\t@property\n\tdef group(self):return self._dom_element.groupId\n\t@property\n\tdef kind(self):return self._dom_element.kind\n\t@property\n\tdef label(self):return self._dom_element.label\n\tdef __getitem__(A,key):return getattr(A,key)\n\t@classmethod\n\tasync def load(E,audio=False,video=True):\n\t\tB=video;A=window.Object.new();A.audio=audio\n\t\tif isinstance(B,bool):A.video=B\n\t\telse:\n\t\t\tA.video=window.Object.new()\n\t\t\tfor C in B:setattr(A.video,C,to_js(B[C]))\n\t\tD=await window.navigator.mediaDevices.getUserMedia(A);return D\n\tasync def get_stream(A):B=A.kind.replace('input','').replace('output','');C={B:{'deviceId':{'exact':A.id}}};return await A.load(**C)\nasync def list_devices():return[Device(A)for A in await window.navigator.mediaDevices.enumerateDevices()]",
     "storage.py": "_C='memoryview'\n_B='bytearray'\n_A='generic'\nfrom polyscript import storage as _storage\nfrom pyscript.flatted import parse as _parse\nfrom pyscript.flatted import stringify as _stringify\ndef _to_idb(value):\n\tA=value\n\tif A is None:return _stringify(['null',0])\n\tif isinstance(A,(bool,float,int,str,list,dict,tuple)):return _stringify([_A,A])\n\tif isinstance(A,bytearray):return _stringify([_B,[A for A in A]])\n\tif isinstance(A,memoryview):return _stringify([_C,[A for A in A]])\n\traise TypeError(f\"Unexpected value: {A}\")\ndef _from_idb(value):\n\tC=value;A,B=_parse(C)\n\tif A=='null':return\n\tif A==_A:return B\n\tif A==_B:return bytearray(B)\n\tif A==_C:return memoryview(bytearray(B))\n\treturn C\nclass Storage(dict):\n\tdef __init__(B,store):A=store;super().__init__({A:_from_idb(B)for(A,B)in A.entries()});B.__store__=A\n\tdef __delitem__(A,attr):A.__store__.delete(attr);super().__delitem__(attr)\n\tdef __setitem__(B,attr,value):A=value;B.__store__.set(attr,_to_idb(A));super().__setitem__(attr,A)\n\tdef clear(A):A.__store__.clear();super().clear()\n\tasync def sync(A):await A.__store__.sync()\nasync def storage(name='',storage_class=Storage):\n\tif not name:raise ValueError('The storage name must be defined')\n\treturn storage_class(await _storage(f\"@pyscript/{name}\"))",
     "util.py": "import js\ndef as_bytearray(buffer):\n\tA=js.Uint8Array.new(buffer);B=A.length;C=bytearray(B)\n\tfor D in range(0,B):C[D]=A[D]\n\treturn C\nclass NotSupported:\n\tdef __init__(A,name,error):object.__setattr__(A,'name',name);object.__setattr__(A,'error',error)\n\tdef __repr__(A):return f\"<NotSupported {A.name} [{A.error}]>\"\n\tdef __getattr__(A,attr):raise AttributeError(A.error)\n\tdef __setattr__(A,attr,value):raise AttributeError(A.error)\n\tdef __call__(A,*B):raise TypeError(A.error)",
-    "web.py": "_A=None\nfrom pyscript import document,when\ndef wrap_dom_element(dom_element):return Element.wrap_dom_element(dom_element)\nclass Element:\n\telement_classes_by_tag_name={}\n\t@classmethod\n\tdef get_tag_name(A):return A.__name__.replace('_','')\n\t@classmethod\n\tdef register_element_classes(B,element_classes):\n\t\tfor A in element_classes:C=A.get_tag_name();B.element_classes_by_tag_name[C]=A\n\t@classmethod\n\tdef unregister_element_classes(A,element_classes):\n\t\tfor B in element_classes:C=B.get_tag_name();A.element_classes_by_tag_name.pop(C,_A)\n\t@classmethod\n\tdef wrap_dom_element(A,dom_element):B=dom_element;C=A.element_classes_by_tag_name.get(B.tagName.lower(),A);return C(dom_element=B)\n\tdef __init__(A,dom_element=_A,classes=_A,style=_A,**B):A._dom_element=dom_element or document.createElement(type(A).get_tag_name());A._classes=Classes(A);A._style=Style(A);A.update(classes=classes,style=style,**B)\n\tdef __eq__(A,obj):return isinstance(obj,Element)and obj._dom_element==A._dom_element\n\tdef __getitem__(B,key):\n\t\tA=key\n\t\tif isinstance(A,int)or isinstance(A,slice):return B.children[A]\n\t\treturn B.find(A)\n\tdef __getattr__(B,name):\n\t\tA=name\n\t\tif A.endswith('_'):A=A[:-1]\n\t\treturn getattr(B._dom_element,A)\n\tdef __setattr__(C,name,value):\n\t\tB=value;A=name\n\t\tif A.startswith('_'):super().__setattr__(A,B)\n\t\telse:\n\t\t\tif A.endswith('_'):A=A[:-1]\n\t\t\tsetattr(C._dom_element,A,B)\n\t@property\n\tdef children(self):return ElementCollection.wrap_dom_elements(self._dom_element.children)\n\t@property\n\tdef classes(self):return self._classes\n\t@property\n\tdef parent(self):\n\t\tif self._dom_element.parentElement is _A:return\n\t\treturn Element.wrap_dom_element(self._dom_element.parentElement)\n\t@property\n\tdef style(self):return self._style\n\tdef append(B,*C):\n\t\tfor A in C:\n\t\t\tif isinstance(A,Element):B._dom_element.appendChild(A._dom_element)\n\t\t\telif isinstance(A,ElementCollection):\n\t\t\t\tfor D in A:B._dom_element.appendChild(D._dom_element)\n\t\t\telif isinstance(A,list)or isinstance(A,tuple):\n\t\t\t\tfor E in A:B.append(E)\n\t\t\telse:\n\t\t\t\ttry:A.tagName;B._dom_element.appendChild(A)\n\t\t\t\texcept AttributeError:\n\t\t\t\t\ttry:\n\t\t\t\t\t\tA.length\n\t\t\t\t\t\tfor F in A:B._dom_element.appendChild(F)\n\t\t\t\t\texcept AttributeError:raise TypeError(f'Element \"{A}\" is a proxy object, \"but not a valid element or a NodeList.')\n\tdef clone(B,clone_id=_A):A=Element.wrap_dom_element(B._dom_element.cloneNode(True));A.id=clone_id;return A\n\tdef find(A,selector):return ElementCollection.wrap_dom_elements(A._dom_element.querySelectorAll(selector))\n\tdef show_me(A):A._dom_element.scrollIntoView()\n\tdef update(A,classes=_A,style=_A,**D):\n\t\tC=style;B=classes\n\t\tif B:A.classes.add(B)\n\t\tif C:A.style.set(**C)\n\t\tfor(E,F)in D.items():setattr(A,E,F)\nclass Classes:\n\tdef __init__(A,element):A._element=element;A._class_list=A._element._dom_element.classList\n\tdef __contains__(A,item):return item in A._class_list\n\tdef __eq__(C,other):\n\t\tA=other\n\t\tif isinstance(A,Classes):B=list(A._class_list)\n\t\telse:\n\t\t\ttry:B=iter(A)\n\t\t\texcept TypeError:return False\n\t\treturn set(C._class_list)==set(B)\n\tdef __iter__(A):return iter(A._class_list)\n\tdef __len__(A):return A._class_list.length\n\tdef __repr__(A):return f\"Classes({\", \".join(A._class_list)})\"\n\tdef __str__(A):return' '.join(A._class_list)\n\tdef add(B,*C):\n\t\tfor A in C:\n\t\t\tif isinstance(A,list):\n\t\t\t\tfor D in A:B.add(D)\n\t\t\telse:B._class_list.add(A)\n\tdef contains(A,class_name):return class_name in A\n\tdef remove(B,*C):\n\t\tfor A in C:\n\t\t\tif isinstance(A,list):\n\t\t\t\tfor D in A:B.remove(D)\n\t\t\telse:B._class_list.remove(A)\n\tdef replace(A,old_class,new_class):A.remove(old_class);A.add(new_class)\n\tdef toggle(A,*C):\n\t\tfor B in C:\n\t\t\tif B in A:A.remove(B)\n\t\t\telse:A.add(B)\nclass HasOptions:\n\t@property\n\tdef options(self):\n\t\tA=self\n\t\tif not hasattr(A,'_options'):A._options=Options(A)\n\t\treturn A._options\nclass Options:\n\tdef __init__(A,element):A._element=element\n\tdef __getitem__(A,key):return A.options[key]\n\tdef __iter__(A):yield from A.options\n\tdef __len__(A):return len(A.options)\n\tdef __repr__(A):return f\"{A.__class__.__name__} (length: {len(A)}) {A.options}\"\n\t@property\n\tdef options(self):return[Element.wrap_dom_element(A)for A in self._element._dom_element.options]\n\t@property\n\tdef selected(self):return self.options[self._element._dom_element.selectedIndex]\n\tdef add(D,value=_A,html=_A,text=_A,before=_A,**B):\n\t\tC=value;A=before\n\t\tif C is not _A:B['value']=C\n\t\tif html is not _A:B['innerHTML']=html\n\t\tif text is not _A:B['text']=text\n\t\tE=option(**B)\n\t\tif A:\n\t\t\tif isinstance(A,Element):A=A._dom_element\n\t\tD._element._dom_element.add(E._dom_element,A)\n\tdef clear(A):\n\t\twhile len(A)>0:A.remove(0)\n\tdef remove(A,index):A._element._dom_element.remove(index)\nclass Style:\n\tdef __init__(A,element):A._element=element;A._style=A._element._dom_element.style\n\tdef __getitem__(A,key):return A._style.getPropertyValue(key)\n\tdef __setitem__(A,key,value):A._style.setProperty(key,value)\n\tdef remove(A,key):A._style.removeProperty(key)\n\tdef set(A,**B):\n\t\tfor(C,D)in B.items():A._element._dom_element.style.setProperty(C,D)\n\t@property\n\tdef visible(self):return self._element._dom_element.style.visibility\n\t@visible.setter\n\tdef visible(self,value):self._element._dom_element.style.visibility=value\nclass ContainerElement(Element):\n\tdef __init__(B,*C,children=_A,dom_element=_A,style=_A,classes=_A,**D):\n\t\tsuper().__init__(dom_element=dom_element,style=style,classes=classes,**D)\n\t\tfor A in list(C)+(children or[]):\n\t\t\tif isinstance(A,Element)or isinstance(A,ElementCollection):B.append(A)\n\t\t\telse:B._dom_element.insertAdjacentHTML('beforeend',A)\n\tdef __iter__(A):yield from A.children\nclass ClassesCollection:\n\tdef __init__(A,collection):A._collection=collection\n\tdef __contains__(A,class_name):\n\t\tfor B in A._collection:\n\t\t\tif class_name in B.classes:return True\n\t\treturn False\n\tdef __eq__(B,other):A=other;return isinstance(A,ClassesCollection)and B._collection==A._collection\n\tdef __iter__(A):\n\t\tfor B in A._all_class_names():yield B\n\tdef __len__(A):return len(A._all_class_names())\n\tdef __repr__(A):return f\"ClassesCollection({repr(A._collection)})\"\n\tdef __str__(A):return' '.join(A._all_class_names())\n\tdef add(A,*B):\n\t\tfor C in A._collection:C.classes.add(*B)\n\tdef contains(A,class_name):return class_name in A\n\tdef remove(A,*B):\n\t\tfor C in A._collection:C.classes.remove(*B)\n\tdef replace(A,old_class,new_class):\n\t\tfor B in A._collection:B.classes.replace(old_class,new_class)\n\tdef toggle(A,*B):\n\t\tfor C in A._collection:C.classes.toggle(*B)\n\tdef _all_class_names(B):\n\t\tA=set()\n\t\tfor C in B._collection:\n\t\t\tfor D in C.classes:A.add(D)\n\t\treturn A\nclass StyleCollection:\n\tdef __init__(A,collection):A._collection=collection\n\tdef __getitem__(A,key):return[A.style[key]for A in A._collection._elements]\n\tdef __setitem__(A,key,value):\n\t\tfor B in A._collection._elements:B.style[key]=value\n\tdef __repr__(A):return f\"StyleCollection({repr(A._collection)})\"\n\tdef remove(A,key):\n\t\tfor B in A._collection._elements:B.style.remove(key)\nclass ElementCollection:\n\t@classmethod\n\tdef wrap_dom_elements(A,dom_elements):return A([Element.wrap_dom_element(A)for A in dom_elements])\n\tdef __init__(A,elements):A._elements=elements;A._classes=ClassesCollection(A);A._style=StyleCollection(A)\n\tdef __eq__(A,obj):return isinstance(obj,ElementCollection)and obj._elements==A._elements\n\tdef __getitem__(B,key):\n\t\tA=key\n\t\tif isinstance(A,int):return B._elements[A]\n\t\telif isinstance(A,slice):return ElementCollection(B._elements[A])\n\t\treturn B.find(A)\n\tdef __iter__(A):yield from A._elements\n\tdef __len__(A):return len(A._elements)\n\tdef __repr__(A):return f\"{A.__class__.__name__} (length: {len(A._elements)}) {A._elements}\"\n\tdef __getattr__(A,name):return[getattr(A,name)for A in A._elements]\n\tdef __setattr__(C,name,value):\n\t\tB=value;A=name\n\t\tif A.startswith('_'):super().__setattr__(A,B)\n\t\telse:\n\t\t\tfor D in C._elements:setattr(D,A,B)\n\t@property\n\tdef classes(self):return self._classes\n\t@property\n\tdef elements(self):return self._elements\n\t@property\n\tdef style(self):return self._style\n\tdef find(B,selector):\n\t\tA=[]\n\t\tfor C in B._elements:A.extend(C.find(selector))\n\t\treturn ElementCollection(A)\nclass a(ContainerElement):0\nclass abbr(ContainerElement):0\nclass address(ContainerElement):0\nclass area(Element):0\nclass article(ContainerElement):0\nclass aside(ContainerElement):0\nclass audio(ContainerElement):0\nclass b(ContainerElement):0\nclass base(Element):0\nclass blockquote(ContainerElement):0\nclass body(ContainerElement):0\nclass br(Element):0\nclass button(ContainerElement):0\nclass canvas(ContainerElement):\n\tdef download(A,filename='snapped.png'):B=a(download=filename,href=A._dom_element.toDataURL());A.append(B);B._dom_element.click()\n\tdef draw(E,what,width=_A,height=_A):\n\t\tC=height;B=width;A=what\n\t\tif isinstance(A,Element):A=A._dom_element\n\t\tD=E._dom_element.getContext('2d')\n\t\tif B or C:D.drawImage(A,0,0,B,C)\n\t\telse:D.drawImage(A,0,0)\nclass caption(ContainerElement):0\nclass cite(ContainerElement):0\nclass code(ContainerElement):0\nclass col(Element):0\nclass colgroup(ContainerElement):0\nclass data(ContainerElement):0\nclass datalist(ContainerElement,HasOptions):0\nclass dd(ContainerElement):0\nclass del_(ContainerElement):0\nclass details(ContainerElement):0\nclass dialog(ContainerElement):0\nclass div(ContainerElement):0\nclass dl(ContainerElement):0\nclass dt(ContainerElement):0\nclass em(ContainerElement):0\nclass embed(Element):0\nclass fieldset(ContainerElement):0\nclass figcaption(ContainerElement):0\nclass figure(ContainerElement):0\nclass footer(ContainerElement):0\nclass form(ContainerElement):0\nclass h1(ContainerElement):0\nclass h2(ContainerElement):0\nclass h3(ContainerElement):0\nclass h4(ContainerElement):0\nclass h5(ContainerElement):0\nclass h6(ContainerElement):0\nclass head(ContainerElement):0\nclass header(ContainerElement):0\nclass hgroup(ContainerElement):0\nclass hr(Element):0\nclass html(ContainerElement):0\nclass i(ContainerElement):0\nclass iframe(ContainerElement):0\nclass img(Element):0\nclass input_(Element):0\nclass ins(ContainerElement):0\nclass kbd(ContainerElement):0\nclass label(ContainerElement):0\nclass legend(ContainerElement):0\nclass li(ContainerElement):0\nclass link(Element):0\nclass main(ContainerElement):0\nclass map_(ContainerElement):0\nclass mark(ContainerElement):0\nclass menu(ContainerElement):0\nclass meta(ContainerElement):0\nclass meter(ContainerElement):0\nclass nav(ContainerElement):0\nclass object_(ContainerElement):0\nclass ol(ContainerElement):0\nclass optgroup(ContainerElement,HasOptions):0\nclass option(ContainerElement):0\nclass output(ContainerElement):0\nclass p(ContainerElement):0\nclass param(ContainerElement):0\nclass picture(ContainerElement):0\nclass pre(ContainerElement):0\nclass progress(ContainerElement):0\nclass q(ContainerElement):0\nclass s(ContainerElement):0\nclass script(ContainerElement):0\nclass section(ContainerElement):0\nclass select(ContainerElement,HasOptions):0\nclass small(ContainerElement):0\nclass source(Element):0\nclass span(ContainerElement):0\nclass strong(ContainerElement):0\nclass style(ContainerElement):0\nclass sub(ContainerElement):0\nclass summary(ContainerElement):0\nclass sup(ContainerElement):0\nclass table(ContainerElement):0\nclass tbody(ContainerElement):0\nclass td(ContainerElement):0\nclass template(ContainerElement):0\nclass textarea(ContainerElement):0\nclass tfoot(ContainerElement):0\nclass th(ContainerElement):0\nclass thead(ContainerElement):0\nclass time(ContainerElement):0\nclass title(ContainerElement):0\nclass tr(ContainerElement):0\nclass track(Element):0\nclass u(ContainerElement):0\nclass ul(ContainerElement):0\nclass var(ContainerElement):0\nclass video(ContainerElement):\n\tdef snap(D,to=_A,width=_A,height=_A):\n\t\tG='CANVAS';F='Element to snap to must be a canvas.';C=height;B=width;A=to;B=B if B is not _A else D.videoWidth;C=C if C is not _A else D.videoHeight\n\t\tif A is _A:A=canvas(width=B,height=C)\n\t\telif isinstance(A,Element):\n\t\t\tif A.tag!='canvas':raise TypeError(F)\n\t\telif getattr(A,'tagName','')==G:A=canvas(dom_element=A)\n\t\telif isinstance(A,str):\n\t\t\tE=document.querySelectorAll(A)\n\t\t\tif E.length==0:raise TypeError('No element with selector {to} to snap to.')\n\t\t\tif E[0].tagName!=G:raise TypeError(F)\n\t\t\tA=canvas(dom_element=E[0])\n\t\tA.draw(D,B,C);return A\nclass wbr(Element):0\nELEMENT_CLASSES=[a,abbr,address,area,article,aside,audio,b,base,blockquote,body,br,button,canvas,caption,cite,code,col,colgroup,data,datalist,dd,del_,details,dialog,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input_,ins,kbd,label,legend,li,link,main,map_,mark,menu,meta,meter,nav,object_,ol,optgroup,option,output,p,param,picture,pre,progress,q,s,script,section,select,small,source,span,strong,style,sub,summary,sup,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,u,ul,var,video,wbr]\nElement.register_element_classes(ELEMENT_CLASSES)\nclass Page:\n\tdef __init__(A):A.html=Element.wrap_dom_element(document.documentElement);A.body=Element.wrap_dom_element(document.body);A.head=Element.wrap_dom_element(document.head)\n\tdef __getitem__(A,selector):return A.find(selector)\n\t@property\n\tdef title(self):return document.title\n\t@title.setter\n\tdef title(self,value):document.title=value\n\tdef append(A,*B):A.body.append(*B)\n\tdef find(A,selector):return ElementCollection.wrap_dom_elements(document.querySelectorAll(selector))\npage=Page()",
+    "web.py": "_A=None\nfrom pyscript import document,when\ndef wrap_dom_element(dom_element):return Element.wrap_dom_element(dom_element)\nclass Element:\n\telement_classes_by_tag_name={}\n\t@classmethod\n\tdef get_tag_name(A):return A.__name__.replace('_','')\n\t@classmethod\n\tdef register_element_classes(B,element_classes):\n\t\tfor A in element_classes:C=A.get_tag_name();B.element_classes_by_tag_name[C]=A\n\t@classmethod\n\tdef unregister_element_classes(A,element_classes):\n\t\tfor B in element_classes:C=B.get_tag_name();A.element_classes_by_tag_name.pop(C,_A)\n\t@classmethod\n\tdef wrap_dom_element(A,dom_element):B=dom_element;C=A.element_classes_by_tag_name.get(B.tagName.lower(),A);return C(dom_element=B)\n\tdef __init__(A,dom_element=_A,classes=_A,style=_A,**B):A._dom_element=dom_element or document.createElement(type(A).get_tag_name());A._classes=Classes(A);A._style=Style(A);A.update(classes=classes,style=style,**B)\n\tdef __eq__(A,obj):return isinstance(obj,Element)and obj._dom_element==A._dom_element\n\tdef __getitem__(B,key):\n\t\tA=key\n\t\tif isinstance(A,int)or isinstance(A,slice):return B.children[A]\n\t\treturn B.find(A)\n\tdef __getattr__(B,name):\n\t\tA=name\n\t\tif A.endswith('_'):A=A[:-1]\n\t\treturn getattr(B._dom_element,A)\n\tdef __setattr__(C,name,value):\n\t\tB=value;A=name\n\t\tif A.startswith('_'):super().__setattr__(A,B)\n\t\telse:\n\t\t\tif A.endswith('_'):A=A[:-1]\n\t\t\tsetattr(C._dom_element,A,B)\n\t@property\n\tdef children(self):return ElementCollection.wrap_dom_elements(self._dom_element.children)\n\t@property\n\tdef classes(self):return self._classes\n\t@property\n\tdef parent(self):\n\t\tif self._dom_element.parentElement is _A:return\n\t\treturn Element.wrap_dom_element(self._dom_element.parentElement)\n\t@property\n\tdef style(self):return self._style\n\tdef append(B,*C):\n\t\tfor A in C:\n\t\t\tif isinstance(A,Element):B._dom_element.appendChild(A._dom_element)\n\t\t\telif isinstance(A,ElementCollection):\n\t\t\t\tfor D in A:B._dom_element.appendChild(D._dom_element)\n\t\t\telif isinstance(A,list)or isinstance(A,tuple):\n\t\t\t\tfor E in A:B.append(E)\n\t\t\telse:\n\t\t\t\ttry:A.tagName;B._dom_element.appendChild(A)\n\t\t\t\texcept AttributeError:\n\t\t\t\t\ttry:\n\t\t\t\t\t\tA.length\n\t\t\t\t\t\tfor F in A:B._dom_element.appendChild(F)\n\t\t\t\t\texcept AttributeError:raise TypeError(f'Element \"{A}\" is a proxy object, \"but not a valid element or a NodeList.')\n\tdef clone(B,clone_id=_A):A=Element.wrap_dom_element(B._dom_element.cloneNode(True));A.id=clone_id;return A\n\tdef find(A,selector):return ElementCollection.wrap_dom_elements(A._dom_element.querySelectorAll(selector))\n\tdef show_me(A):A._dom_element.scrollIntoView()\n\tdef update(A,classes=_A,style=_A,**D):\n\t\tC=style;B=classes\n\t\tif B:A.classes.add(B)\n\t\tif C:A.style.set(**C)\n\t\tfor(E,F)in D.items():setattr(A,E,F)\nclass Classes:\n\tdef __init__(A,element):A._element=element;A._class_list=A._element._dom_element.classList\n\tdef __contains__(A,item):return item in A._class_list\n\tdef __eq__(C,other):\n\t\tA=other\n\t\tif isinstance(A,Classes):B=list(A._class_list)\n\t\telse:\n\t\t\ttry:B=iter(A)\n\t\t\texcept TypeError:return False\n\t\treturn set(C._class_list)==set(B)\n\tdef __iter__(A):return iter(A._class_list)\n\tdef __len__(A):return A._class_list.length\n\tdef __repr__(A):return f\"Classes({', '.join(A._class_list)})\"\n\tdef __str__(A):return' '.join(A._class_list)\n\tdef add(B,*C):\n\t\tfor A in C:\n\t\t\tif isinstance(A,list):\n\t\t\t\tfor D in A:B.add(D)\n\t\t\telse:B._class_list.add(A)\n\tdef contains(A,class_name):return class_name in A\n\tdef remove(B,*C):\n\t\tfor A in C:\n\t\t\tif isinstance(A,list):\n\t\t\t\tfor D in A:B.remove(D)\n\t\t\telse:B._class_list.remove(A)\n\tdef replace(A,old_class,new_class):A.remove(old_class);A.add(new_class)\n\tdef toggle(A,*C):\n\t\tfor B in C:\n\t\t\tif B in A:A.remove(B)\n\t\t\telse:A.add(B)\nclass HasOptions:\n\t@property\n\tdef options(self):\n\t\tA=self\n\t\tif not hasattr(A,'_options'):A._options=Options(A)\n\t\treturn A._options\nclass Options:\n\tdef __init__(A,element):A._element=element\n\tdef __getitem__(A,key):return A.options[key]\n\tdef __iter__(A):yield from A.options\n\tdef __len__(A):return len(A.options)\n\tdef __repr__(A):return f\"{A.__class__.__name__} (length: {len(A)}) {A.options}\"\n\t@property\n\tdef options(self):return[Element.wrap_dom_element(A)for A in self._element._dom_element.options]\n\t@property\n\tdef selected(self):return self.options[self._element._dom_element.selectedIndex]\n\tdef add(D,value=_A,html=_A,text=_A,before=_A,**B):\n\t\tC=value;A=before\n\t\tif C is not _A:B['value']=C\n\t\tif html is not _A:B['innerHTML']=html\n\t\tif text is not _A:B['text']=text\n\t\tE=option(**B)\n\t\tif A:\n\t\t\tif isinstance(A,Element):A=A._dom_element\n\t\tD._element._dom_element.add(E._dom_element,A)\n\tdef clear(A):\n\t\twhile len(A)>0:A.remove(0)\n\tdef remove(A,index):A._element._dom_element.remove(index)\nclass Style:\n\tdef __init__(A,element):A._element=element;A._style=A._element._dom_element.style\n\tdef __getitem__(A,key):return A._style.getPropertyValue(key)\n\tdef __setitem__(A,key,value):A._style.setProperty(key,value)\n\tdef remove(A,key):A._style.removeProperty(key)\n\tdef set(A,**B):\n\t\tfor(C,D)in B.items():A._element._dom_element.style.setProperty(C,D)\n\t@property\n\tdef visible(self):return self._element._dom_element.style.visibility\n\t@visible.setter\n\tdef visible(self,value):self._element._dom_element.style.visibility=value\nclass ContainerElement(Element):\n\tdef __init__(B,*C,children=_A,dom_element=_A,style=_A,classes=_A,**D):\n\t\tsuper().__init__(dom_element=dom_element,style=style,classes=classes,**D)\n\t\tfor A in list(C)+(children or[]):\n\t\t\tif isinstance(A,Element)or isinstance(A,ElementCollection):B.append(A)\n\t\t\telse:B._dom_element.insertAdjacentHTML('beforeend',A)\n\tdef __iter__(A):yield from A.children\nclass ClassesCollection:\n\tdef __init__(A,collection):A._collection=collection\n\tdef __contains__(A,class_name):\n\t\tfor B in A._collection:\n\t\t\tif class_name in B.classes:return True\n\t\treturn False\n\tdef __eq__(B,other):A=other;return isinstance(A,ClassesCollection)and B._collection==A._collection\n\tdef __iter__(A):\n\t\tfor B in A._all_class_names():yield B\n\tdef __len__(A):return len(A._all_class_names())\n\tdef __repr__(A):return f\"ClassesCollection({repr(A._collection)})\"\n\tdef __str__(A):return' '.join(A._all_class_names())\n\tdef add(A,*B):\n\t\tfor C in A._collection:C.classes.add(*B)\n\tdef contains(A,class_name):return class_name in A\n\tdef remove(A,*B):\n\t\tfor C in A._collection:C.classes.remove(*B)\n\tdef replace(A,old_class,new_class):\n\t\tfor B in A._collection:B.classes.replace(old_class,new_class)\n\tdef toggle(A,*B):\n\t\tfor C in A._collection:C.classes.toggle(*B)\n\tdef _all_class_names(B):\n\t\tA=set()\n\t\tfor C in B._collection:\n\t\t\tfor D in C.classes:A.add(D)\n\t\treturn A\nclass StyleCollection:\n\tdef __init__(A,collection):A._collection=collection\n\tdef __getitem__(A,key):return[A.style[key]for A in A._collection._elements]\n\tdef __setitem__(A,key,value):\n\t\tfor B in A._collection._elements:B.style[key]=value\n\tdef __repr__(A):return f\"StyleCollection({repr(A._collection)})\"\n\tdef remove(A,key):\n\t\tfor B in A._collection._elements:B.style.remove(key)\nclass ElementCollection:\n\t@classmethod\n\tdef wrap_dom_elements(A,dom_elements):return A([Element.wrap_dom_element(A)for A in dom_elements])\n\tdef __init__(A,elements):A._elements=elements;A._classes=ClassesCollection(A);A._style=StyleCollection(A)\n\tdef __eq__(A,obj):return isinstance(obj,ElementCollection)and obj._elements==A._elements\n\tdef __getitem__(B,key):\n\t\tA=key\n\t\tif isinstance(A,int):return B._elements[A]\n\t\telif isinstance(A,slice):return ElementCollection(B._elements[A])\n\t\treturn B.find(A)\n\tdef __iter__(A):yield from A._elements\n\tdef __len__(A):return len(A._elements)\n\tdef __repr__(A):return f\"{A.__class__.__name__} (length: {len(A._elements)}) {A._elements}\"\n\tdef __getattr__(A,name):return[getattr(A,name)for A in A._elements]\n\tdef __setattr__(C,name,value):\n\t\tB=value;A=name\n\t\tif A.startswith('_'):super().__setattr__(A,B)\n\t\telse:\n\t\t\tfor D in C._elements:setattr(D,A,B)\n\t@property\n\tdef classes(self):return self._classes\n\t@property\n\tdef elements(self):return self._elements\n\t@property\n\tdef style(self):return self._style\n\tdef find(B,selector):\n\t\tA=[]\n\t\tfor C in B._elements:A.extend(C.find(selector))\n\t\treturn ElementCollection(A)\nclass a(ContainerElement):0\nclass abbr(ContainerElement):0\nclass address(ContainerElement):0\nclass area(Element):0\nclass article(ContainerElement):0\nclass aside(ContainerElement):0\nclass audio(ContainerElement):0\nclass b(ContainerElement):0\nclass base(Element):0\nclass blockquote(ContainerElement):0\nclass body(ContainerElement):0\nclass br(Element):0\nclass button(ContainerElement):0\nclass canvas(ContainerElement):\n\tdef download(A,filename='snapped.png'):B=a(download=filename,href=A._dom_element.toDataURL());A.append(B);B._dom_element.click()\n\tdef draw(E,what,width=_A,height=_A):\n\t\tC=height;B=width;A=what\n\t\tif isinstance(A,Element):A=A._dom_element\n\t\tD=E._dom_element.getContext('2d')\n\t\tif B or C:D.drawImage(A,0,0,B,C)\n\t\telse:D.drawImage(A,0,0)\nclass caption(ContainerElement):0\nclass cite(ContainerElement):0\nclass code(ContainerElement):0\nclass col(Element):0\nclass colgroup(ContainerElement):0\nclass data(ContainerElement):0\nclass datalist(ContainerElement,HasOptions):0\nclass dd(ContainerElement):0\nclass del_(ContainerElement):0\nclass details(ContainerElement):0\nclass dialog(ContainerElement):0\nclass div(ContainerElement):0\nclass dl(ContainerElement):0\nclass dt(ContainerElement):0\nclass em(ContainerElement):0\nclass embed(Element):0\nclass fieldset(ContainerElement):0\nclass figcaption(ContainerElement):0\nclass figure(ContainerElement):0\nclass footer(ContainerElement):0\nclass form(ContainerElement):0\nclass h1(ContainerElement):0\nclass h2(ContainerElement):0\nclass h3(ContainerElement):0\nclass h4(ContainerElement):0\nclass h5(ContainerElement):0\nclass h6(ContainerElement):0\nclass head(ContainerElement):0\nclass header(ContainerElement):0\nclass hgroup(ContainerElement):0\nclass hr(Element):0\nclass html(ContainerElement):0\nclass i(ContainerElement):0\nclass iframe(ContainerElement):0\nclass img(Element):0\nclass input_(Element):0\nclass ins(ContainerElement):0\nclass kbd(ContainerElement):0\nclass label(ContainerElement):0\nclass legend(ContainerElement):0\nclass li(ContainerElement):0\nclass link(Element):0\nclass main(ContainerElement):0\nclass map_(ContainerElement):0\nclass mark(ContainerElement):0\nclass menu(ContainerElement):0\nclass meta(ContainerElement):0\nclass meter(ContainerElement):0\nclass nav(ContainerElement):0\nclass object_(ContainerElement):0\nclass ol(ContainerElement):0\nclass optgroup(ContainerElement,HasOptions):0\nclass option(ContainerElement):0\nclass output(ContainerElement):0\nclass p(ContainerElement):0\nclass param(ContainerElement):0\nclass picture(ContainerElement):0\nclass pre(ContainerElement):0\nclass progress(ContainerElement):0\nclass q(ContainerElement):0\nclass s(ContainerElement):0\nclass script(ContainerElement):0\nclass section(ContainerElement):0\nclass select(ContainerElement,HasOptions):0\nclass small(ContainerElement):0\nclass source(Element):0\nclass span(ContainerElement):0\nclass strong(ContainerElement):0\nclass style(ContainerElement):0\nclass sub(ContainerElement):0\nclass summary(ContainerElement):0\nclass sup(ContainerElement):0\nclass table(ContainerElement):0\nclass tbody(ContainerElement):0\nclass td(ContainerElement):0\nclass template(ContainerElement):0\nclass textarea(ContainerElement):0\nclass tfoot(ContainerElement):0\nclass th(ContainerElement):0\nclass thead(ContainerElement):0\nclass time(ContainerElement):0\nclass title(ContainerElement):0\nclass tr(ContainerElement):0\nclass track(Element):0\nclass u(ContainerElement):0\nclass ul(ContainerElement):0\nclass var(ContainerElement):0\nclass video(ContainerElement):\n\tdef snap(D,to=_A,width=_A,height=_A):\n\t\tG='CANVAS';F='Element to snap to must be a canvas.';C=height;B=width;A=to;B=B if B is not _A else D.videoWidth;C=C if C is not _A else D.videoHeight\n\t\tif A is _A:A=canvas(width=B,height=C)\n\t\telif isinstance(A,Element):\n\t\t\tif A.tag!='canvas':raise TypeError(F)\n\t\telif getattr(A,'tagName','')==G:A=canvas(dom_element=A)\n\t\telif isinstance(A,str):\n\t\t\tE=document.querySelectorAll(A)\n\t\t\tif E.length==0:raise TypeError('No element with selector {to} to snap to.')\n\t\t\tif E[0].tagName!=G:raise TypeError(F)\n\t\t\tA=canvas(dom_element=E[0])\n\t\tA.draw(D,B,C);return A\nclass wbr(Element):0\nELEMENT_CLASSES=[a,abbr,address,area,article,aside,audio,b,base,blockquote,body,br,button,canvas,caption,cite,code,col,colgroup,data,datalist,dd,del_,details,dialog,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input_,ins,kbd,label,legend,li,link,main,map_,mark,menu,meta,meter,nav,object_,ol,optgroup,option,output,p,param,picture,pre,progress,q,s,script,section,select,small,source,span,strong,style,sub,summary,sup,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,u,ul,var,video,wbr]\nElement.register_element_classes(ELEMENT_CLASSES)\nclass Page:\n\tdef __init__(A):A.html=Element.wrap_dom_element(document.documentElement);A.body=Element.wrap_dom_element(document.body);A.head=Element.wrap_dom_element(document.head)\n\tdef __getitem__(A,selector):return A.find(selector)\n\t@property\n\tdef title(self):return document.title\n\t@title.setter\n\tdef title(self,value):document.title=value\n\tdef append(A,*B):A.body.append(*B)\n\tdef find(A,selector):return ElementCollection.wrap_dom_elements(document.querySelectorAll(selector))\npage=Page()",
     "websocket.py": "import js\nfrom pyscript.ffi import create_proxy\nfrom pyscript.util import as_bytearray\ncode='code'\nprotocols='protocols'\nreason='reason'\nmethods=['onclose','onerror','onmessage','onopen']\nclass EventMessage:\n\tdef __init__(A,event):A._event=event\n\tdef __getattr__(B,attr):\n\t\tA=getattr(B._event,attr)\n\t\tif attr=='data'and not isinstance(A,str):\n\t\t\tif hasattr(A,'to_py'):return A.to_py()\n\t\t\treturn memoryview(as_bytearray(A))\n\t\treturn A\nclass WebSocket:\n\tCONNECTING=0;OPEN=1;CLOSING=2;CLOSED=3\n\tdef __init__(E,**A):\n\t\tD=A['url']\n\t\tif protocols in A:B=js.WebSocket.new(D,A[protocols])\n\t\telse:B=js.WebSocket.new(D)\n\t\tobject.__setattr__(E,'_ws',B)\n\t\tfor C in methods:\n\t\t\tif C in A:setattr(B,C,create_proxy(A[C]))\n\tdef __getattr__(A,attr):return getattr(A._ws,attr)\n\tdef __setattr__(B,attr,value):\n\t\tC=value;A=attr\n\t\tif A in methods:D=lambda e:C(EventMessage(e));setattr(B._ws,A,create_proxy(D))\n\t\telse:setattr(B._ws,A,C)\n\tdef close(B,**A):\n\t\tif code in A and reason in A:B._ws.close(A[code],A[reason])\n\t\telif code in A:B._ws.close(A[code])\n\t\telse:B._ws.close()\n\tdef send(B,data):\n\t\tA=data\n\t\tif isinstance(A,str):B._ws.send(A)\n\t\telse:\n\t\t\tC=js.Uint8Array.new(len(A))\n\t\t\tfor(D,E)in enumerate(A):C[D]=E\n\t\t\tB._ws.send(C)",
     "workers.py": "import js as _js\nfrom polyscript import workers as _workers\n_get=_js.Reflect.get\ndef _set(script,name,value=''):script.setAttribute(name,value)\nclass _ReadOnlyProxy:\n\tdef __getitem__(A,name):return _get(_workers,name)\n\tdef __getattr__(A,name):return _get(_workers,name)\nworkers=_ReadOnlyProxy()\nasync def create_named_worker(src='',name='',config=None,type='py'):\n\tC=name;B=config;from json import dumps\n\tif not src:raise ValueError('Named workers require src')\n\tif not C:raise ValueError('Named workers require a name')\n\tA=_js.document.createElement('script');A.type=type;A.src=src;_set(A,'worker');_set(A,'name',C)\n\tif B:_set(A,'config',isinstance(B,str)and B or dumps(B))\n\t_js.document.body.append(A);return await workers[C]"
   }


Test patch:
diff --git a/core/tests/index.html b/core/tests/index.html
index 43971239809..0273ec45ffe 100644
--- a/core/tests/index.html
+++ b/core/tests/index.html
@@ -14,5 +14,5 @@
             a:hover { opacity: 1; }
         </style>
     </head>
-    <body><ul><li><strong><span>javascript</span></strong><ul><li><a href="./javascript/async-listener.html">async-listener<small>.html</small></a></li><li><a href="./javascript/config-url.html">config-url<small>.html</small></a></li><li><a href="./javascript/config_type.html">config_type<small>.html</small></a></li><li><strong><a href="./javascript/fetch/index.html">fetch</a></strong></li><li><a href="./javascript/ffi.html">ffi<small>.html</small></a></li><li><a href="./javascript/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./javascript/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./javascript/js-storage.html">js-storage<small>.html</small></a></li><li><a href="./javascript/js_modules.html">js_modules<small>.html</small></a></li><li><strong><a href="./javascript/loader/index.html">loader</a></strong></li><li><a href="./javascript/mpy-error.html">mpy-error<small>.html</small></a></li><li><a href="./javascript/mpy-no-error.html">mpy-no-error<small>.html</small></a></li><li><a href="./javascript/mpy.html">mpy<small>.html</small></a></li><li><a href="./javascript/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./javascript/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./javascript/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./javascript/py-terminals.html">py-terminals<small>.html</small></a></li><li><strong><a href="./javascript/pyodide-cache/index.html">pyodide-cache</a></strong></li><li><a href="./javascript/storage.html">storage<small>.html</small></a></li><li><strong><span>workers</span></strong><ul><li><strong><a href="./javascript/workers/create_named/index.html">create_named</a></strong></li><li><strong><a href="./javascript/workers/mpy/index.html">mpy</a></strong></li><li><strong><a href="./javascript/workers/py/index.html">py</a></strong></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><strong><a href="./manual/donkey/index.html">donkey</a></strong></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/interpreter.html">interpreter<small>.html</small></a></li><li><strong><a href="./manual/issue-7015/index.html">issue-7015</a></strong></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><strong><a href="./manual/no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./manual/piratical/index.html">piratical</a></strong></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py-editor-failure.html">py-editor-failure<small>.html</small></a></li><li><strong><a href="./manual/py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./manual/py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./manual/py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><strong><a href="./manual/service-worker/index.html">service-worker</a></strong></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/submit.html">submit<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./python/index.html">python</a></strong></li></ul></body>
+    <body><ul><li><strong><span>javascript</span></strong><ul><li><a href="./javascript/async-listener.html">async-listener<small>.html</small></a></li><li><a href="./javascript/config-url.html">config-url<small>.html</small></a></li><li><a href="./javascript/config_type.html">config_type<small>.html</small></a></li><li><strong><a href="./javascript/fetch/index.html">fetch</a></strong></li><li><a href="./javascript/ffi.html">ffi<small>.html</small></a></li><li><a href="./javascript/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./javascript/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./javascript/js-storage.html">js-storage<small>.html</small></a></li><li><a href="./javascript/js_modules.html">js_modules<small>.html</small></a></li><li><strong><a href="./javascript/loader/index.html">loader</a></strong></li><li><a href="./javascript/mpy-error.html">mpy-error<small>.html</small></a></li><li><a href="./javascript/mpy-no-error.html">mpy-no-error<small>.html</small></a></li><li><a href="./javascript/mpy.html">mpy<small>.html</small></a></li><li><a href="./javascript/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./javascript/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./javascript/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./javascript/py-terminals.html">py-terminals<small>.html</small></a></li><li><strong><a href="./javascript/pyodide-cache/index.html">pyodide-cache</a></strong></li><li><a href="./javascript/storage.html">storage<small>.html</small></a></li><li><strong><span>workers</span></strong><ul><li><strong><a href="./javascript/workers/create_named/index.html">create_named</a></strong></li><li><strong><a href="./javascript/workers/mpy/index.html">mpy</a></strong></li><li><strong><a href="./javascript/workers/py/index.html">py</a></strong></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><strong><a href="./manual/donkey/index.html">donkey</a></strong></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/interpreter.html">interpreter<small>.html</small></a></li><li><strong><a href="./manual/issue-2228/index.html">issue-2228</a></strong></li><li><strong><a href="./manual/issue-7015/index.html">issue-7015</a></strong></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><strong><a href="./manual/no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./manual/piratical/index.html">piratical</a></strong></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py-editor-failure.html">py-editor-failure<small>.html</small></a></li><li><strong><a href="./manual/py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./manual/py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./manual/py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><strong><a href="./manual/service-worker/index.html">service-worker</a></strong></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/submit.html">submit<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./python/index.html">python</a></strong></li></ul></body>
 </html>
diff --git a/core/tests/manual/issue-2228/index.html b/core/tests/manual/issue-2228/index.html
new file mode 100644
index 00000000000..04b28592aa8
--- /dev/null
+++ b/core/tests/manual/issue-2228/index.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width,initial-scale=1.0">
+        <script type="module" src="../../../dist/core.js"></script>
+    </head>
+    <body>
+      <script type="py" config='{"packages":["jsonpointer==3.0.0"]}'>
+        print('Hello World')
+      </script>
+    </body>
+</html>