diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6d8bd369b25..8b22ed4053a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: rev: v2.4.1 hooks: - id: codespell # See 'pyproject.toml' for args - exclude: \.js\.map$ + exclude: fs\.py|\.js\.map$ additional_dependencies: - tomli diff --git a/core/package-lock.json b/core/package-lock.json index fa0ba1a3df8..0b797860e0a 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,19 +1,19 @@ { "name": "@pyscript/core", - "version": "0.6.26", + "version": "0.6.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pyscript/core", - "version": "0.6.26", + "version": "0.6.30", "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.11", + "polyscript": "^0.16.13", "sabayon": "^0.6.6", "sticky-module": "^0.1.1", "to-json-callback": "^0.1.1", @@ -37,9 +37,9 @@ "chokidar": "^4.0.3", "codedent": "^0.1.2", "codemirror": "^6.0.1", - "eslint": "^9.19.0", + "eslint": "^9.20.1", "flatted": "^3.3.2", - "rollup": "^4.34.4", + "rollup": "^4.34.7", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-string": "^3.0.0", "static-handler": "^0.5.3", @@ -247,9 +247,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.19.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", - "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", + "version": "9.20.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", + "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", "dev": true, "license": "MIT", "engines": { @@ -725,9 +725,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.4.tgz", - "integrity": "sha512-gGi5adZWvjtJU7Axs//CWaQbQd/vGy8KGcnEaCWiyCqxWYDxwIlAHFuSe6Guoxtd0SRvSfVTDMPd5H+4KE2kKA==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz", + "integrity": "sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw==", "cpu": [ "arm" ], @@ -739,9 +739,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.4.tgz", - "integrity": "sha512-1aRlh1gqtF7vNPMnlf1vJKk72Yshw5zknR/ZAVh7zycRAGF2XBMVDAHmFQz/Zws5k++nux3LOq/Ejj1WrDR6xg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.7.tgz", + "integrity": "sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg==", "cpu": [ "arm64" ], @@ -753,9 +753,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.4.tgz", - "integrity": "sha512-drHl+4qhFj+PV/jrQ78p9ch6A0MfNVZScl/nBps5a7u01aGf/GuBRrHnRegA9bP222CBDfjYbFdjkIJ/FurvSQ==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.7.tgz", + "integrity": "sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA==", "cpu": [ "arm64" ], @@ -767,9 +767,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.4.tgz", - "integrity": "sha512-hQqq/8QALU6t1+fbNmm6dwYsa0PDD4L5r3TpHx9dNl+aSEMnIksHZkSO3AVH+hBMvZhpumIGrTFj8XCOGuIXjw==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.7.tgz", + "integrity": "sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA==", "cpu": [ "x64" ], @@ -781,9 +781,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.4.tgz", - "integrity": "sha512-/L0LixBmbefkec1JTeAQJP0ETzGjFtNml2gpQXA8rpLo7Md+iXQzo9kwEgzyat5Q+OG/C//2B9Fx52UxsOXbzw==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.7.tgz", + "integrity": "sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA==", "cpu": [ "arm64" ], @@ -795,9 +795,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.4.tgz", - "integrity": "sha512-6Rk3PLRK+b8L/M6m/x6Mfj60LhAUcLJ34oPaxufA+CfqkUrDoUPQYFdRrhqyOvtOKXLJZJwxlOLbQjNYQcRQfw==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.7.tgz", + "integrity": "sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q==", "cpu": [ "x64" ], @@ -809,9 +809,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.4.tgz", - "integrity": "sha512-kmT3x0IPRuXY/tNoABp2nDvI9EvdiS2JZsd4I9yOcLCCViKsP0gB38mVHOhluzx+SSVnM1KNn9k6osyXZhLoCA==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.7.tgz", + "integrity": "sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ==", "cpu": [ "arm" ], @@ -823,9 +823,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.4.tgz", - "integrity": "sha512-3iSA9tx+4PZcJH/Wnwsvx/BY4qHpit/u2YoZoXugWVfc36/4mRkgGEoRbRV7nzNBSCOgbWMeuQ27IQWgJ7tRzw==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.7.tgz", + "integrity": "sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw==", "cpu": [ "arm" ], @@ -837,9 +837,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.4.tgz", - "integrity": "sha512-7CwSJW+sEhM9sESEk+pEREF2JL0BmyCro8UyTq0Kyh0nu1v0QPNY3yfLPFKChzVoUmaKj8zbdgBxUhBRR+xGxg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.7.tgz", + "integrity": "sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA==", "cpu": [ "arm64" ], @@ -851,9 +851,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.4.tgz", - "integrity": "sha512-GZdafB41/4s12j8Ss2izofjeFXRAAM7sHCb+S4JsI9vaONX/zQ8cXd87B9MRU/igGAJkKvmFmJJBeeT9jJ5Cbw==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.7.tgz", + "integrity": "sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw==", "cpu": [ "arm64" ], @@ -865,9 +865,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.4.tgz", - "integrity": "sha512-uuphLuw1X6ur11675c2twC6YxbzyLSpWggvdawTUamlsoUv81aAXRMPBC1uvQllnBGls0Qt5Siw8reSIBnbdqQ==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.7.tgz", + "integrity": "sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw==", "cpu": [ "loong64" ], @@ -879,9 +879,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.4.tgz", - "integrity": "sha512-KvLEw1os2gSmD6k6QPCQMm2T9P2GYvsMZMRpMz78QpSoEevHbV/KOUbI/46/JRalhtSAYZBYLAnT9YE4i/l4vg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.7.tgz", + "integrity": "sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w==", "cpu": [ "ppc64" ], @@ -893,9 +893,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.4.tgz", - "integrity": "sha512-wcpCLHGM9yv+3Dql/CI4zrY2mpQ4WFergD3c9cpRowltEh5I84pRT/EuHZsG0In4eBPPYthXnuR++HrFkeqwkA==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.7.tgz", + "integrity": "sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw==", "cpu": [ "riscv64" ], @@ -907,9 +907,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.4.tgz", - "integrity": "sha512-nLbfQp2lbJYU8obhRQusXKbuiqm4jSJteLwfjnunDT5ugBKdxqw1X9KWwk8xp1OMC6P5d0WbzxzhWoznuVK6XA==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.7.tgz", + "integrity": "sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw==", "cpu": [ "s390x" ], @@ -921,9 +921,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.4.tgz", - "integrity": "sha512-JGejzEfVzqc/XNiCKZj14eb6s5w8DdWlnQ5tWUbs99kkdvfq9btxxVX97AaxiUX7xJTKFA0LwoS0KU8C2faZRg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.7.tgz", + "integrity": "sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA==", "cpu": [ "x64" ], @@ -935,9 +935,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.4.tgz", - "integrity": "sha512-/iFIbhzeyZZy49ozAWJ1ZR2KW6ZdYUbQXLT4O5n1cRZRoTpwExnHLjlurDXXPKEGxiAg0ujaR9JDYKljpr2fDg==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.7.tgz", + "integrity": "sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ==", "cpu": [ "x64" ], @@ -949,9 +949,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.4.tgz", - "integrity": "sha512-qORc3UzoD5UUTneiP2Afg5n5Ti1GAW9Gp5vHPxzvAFFA3FBaum9WqGvYXGf+c7beFdOKNos31/41PRMUwh1tpA==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.7.tgz", + "integrity": "sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw==", "cpu": [ "arm64" ], @@ -963,9 +963,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.4.tgz", - "integrity": "sha512-5g7E2PHNK2uvoD5bASBD9aelm44nf1w4I5FEI7MPHLWcCSrR8JragXZWgKPXk5i2FU3JFfa6CGZLw2RrGBHs2Q==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.7.tgz", + "integrity": "sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg==", "cpu": [ "ia32" ], @@ -977,9 +977,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.4.tgz", - "integrity": "sha512-p0scwGkR4kZ242xLPBuhSckrJ734frz6v9xZzD+kHVYRAkSUmdSLCIJRfql6H5//aF8Q10K+i7q8DiPfZp0b7A==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.7.tgz", + "integrity": "sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w==", "cpu": [ "x64" ], @@ -1781,18 +1781,18 @@ } }, "node_modules/eslint": { - "version": "9.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", - "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", + "version": "9.20.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", + "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.19.0", + "@eslint/js": "9.20.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -1870,6 +1870,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", + "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -2747,9 +2760,9 @@ } }, "node_modules/polyscript": { - "version": "0.16.11", - "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.16.11.tgz", - "integrity": "sha512-q0tujwSPEf+08n5ckSa4Z4p9k4AaiM00TGkD0GAK1GjPC87IzFMHF6sz9M5K0EvErTuiwqW5rJms5smVLRiG9g==", + "version": "0.16.13", + "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.16.13.tgz", + "integrity": "sha512-naAVOovlKJlWU+RTepmjfylkTBfZrf+cipTuBs2sqyIjWWKn9q2zkqllL03BpdR+0hF799caRp0pxlCUQW0fSA==", "license": "APACHE-2.0", "dependencies": { "@ungap/structured-clone": "^1.3.0", @@ -3482,9 +3495,9 @@ } }, "node_modules/rollup": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.4.tgz", - "integrity": "sha512-spF66xoyD7rz3o08sHP7wogp1gZ6itSq22SGa/IZTcUDXDlOyrShwMwkVSB+BUxFRZZCUYqdb3KWDEOMVQZxuw==", + "version": "4.34.7", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.7.tgz", + "integrity": "sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3498,25 +3511,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.4", - "@rollup/rollup-android-arm64": "4.34.4", - "@rollup/rollup-darwin-arm64": "4.34.4", - "@rollup/rollup-darwin-x64": "4.34.4", - "@rollup/rollup-freebsd-arm64": "4.34.4", - "@rollup/rollup-freebsd-x64": "4.34.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.4", - "@rollup/rollup-linux-arm-musleabihf": "4.34.4", - "@rollup/rollup-linux-arm64-gnu": "4.34.4", - "@rollup/rollup-linux-arm64-musl": "4.34.4", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.4", - "@rollup/rollup-linux-riscv64-gnu": "4.34.4", - "@rollup/rollup-linux-s390x-gnu": "4.34.4", - "@rollup/rollup-linux-x64-gnu": "4.34.4", - "@rollup/rollup-linux-x64-musl": "4.34.4", - "@rollup/rollup-win32-arm64-msvc": "4.34.4", - "@rollup/rollup-win32-ia32-msvc": "4.34.4", - "@rollup/rollup-win32-x64-msvc": "4.34.4", + "@rollup/rollup-android-arm-eabi": "4.34.7", + "@rollup/rollup-android-arm64": "4.34.7", + "@rollup/rollup-darwin-arm64": "4.34.7", + "@rollup/rollup-darwin-x64": "4.34.7", + "@rollup/rollup-freebsd-arm64": "4.34.7", + "@rollup/rollup-freebsd-x64": "4.34.7", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.7", + "@rollup/rollup-linux-arm-musleabihf": "4.34.7", + "@rollup/rollup-linux-arm64-gnu": "4.34.7", + "@rollup/rollup-linux-arm64-musl": "4.34.7", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.7", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.7", + "@rollup/rollup-linux-riscv64-gnu": "4.34.7", + "@rollup/rollup-linux-s390x-gnu": "4.34.7", + "@rollup/rollup-linux-x64-gnu": "4.34.7", + "@rollup/rollup-linux-x64-musl": "4.34.7", + "@rollup/rollup-win32-arm64-msvc": "4.34.7", + "@rollup/rollup-win32-ia32-msvc": "4.34.7", + "@rollup/rollup-win32-x64-msvc": "4.34.7", "fsevents": "~2.3.2" } }, diff --git a/core/package.json b/core/package.json index 6750e4b52f9..f11b0e824a1 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@pyscript/core", - "version": "0.6.26", + "version": "0.6.30", "type": "module", "description": "PyScript", "module": "./index.js", @@ -25,6 +25,10 @@ "types": "./types/core.d.ts", "import": "./src/core.js" }, + "./js": { + "types": "./types/core.d.ts", + "import": "./dist/core.js" + }, "./css": { "import": "./dist/core.css" }, @@ -62,7 +66,7 @@ "@webreflection/idb-map": "^0.3.2", "add-promise-listener": "^0.1.3", "basic-devtools": "^0.1.6", - "polyscript": "^0.16.11", + "polyscript": "^0.16.13", "sabayon": "^0.6.6", "sticky-module": "^0.1.1", "to-json-callback": "^0.1.1", @@ -86,9 +90,9 @@ "chokidar": "^4.0.3", "codedent": "^0.1.2", "codemirror": "^6.0.1", - "eslint": "^9.19.0", + "eslint": "^9.20.1", "flatted": "^3.3.2", - "rollup": "^4.34.4", + "rollup": "^4.34.7", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-string": "^3.0.0", "static-handler": "^0.5.3", diff --git a/core/src/core.css b/core/src/core.css index 67a46c8d45b..3836dcdcd7c 100644 --- a/core/src/core.css +++ b/core/src/core.css @@ -78,3 +78,13 @@ py-terminal span, mpy-terminal span { letter-spacing: 0 !important; } + +dialog.pyscript-fs { + border-radius: 8px; + border-width: 1px; +} + +dialog.pyscript-fs > div { + display: flex; + justify-content: space-between; +} diff --git a/core/src/core.js b/core/src/core.js index 3484ef72cee..e8bdf7e9007 100644 --- a/core/src/core.js +++ b/core/src/core.js @@ -33,6 +33,7 @@ import { createFunction, inputFailure, } from "./hooks.js"; +import * as fs from "./fs.js"; import codemirror from "./plugins/codemirror.js"; export { codemirror }; @@ -167,6 +168,8 @@ for (const [TYPE, interpreter] of TYPES) { // enrich the Python env with some JS utility for main interpreter.registerJsModule("_pyscript", { PyWorker, + fs, + interpreter, js_import: (...urls) => Promise.all(urls.map((url) => import(url))), get target() { return isScript(currentElement) diff --git a/core/src/fs.js b/core/src/fs.js new file mode 100644 index 00000000000..2d35ef3f148 --- /dev/null +++ b/core/src/fs.js @@ -0,0 +1,81 @@ +import IDBMap from "@webreflection/idb-map"; +import { assign } from "polyscript/exports"; +import { $$ } from "basic-devtools"; + +const stop = (event) => { + event.preventDefault(); + event.stopImmediatePropagation(); +}; + +// ⚠️ these two constants MUST be passed as `fs` +// within the worker onBeforeRunAsync hook! +export const NAMESPACE = "@pyscript.fs"; +export const ERROR = "storage permissions not granted"; + +export const idb = new IDBMap(NAMESPACE); + +/** + * Ask a user action via dialog and returns the directory handler once granted. + * @param {{id?:string, mode?:"read"|"readwrite", hint?:"desktop"|"documents"|"downloads"|"music"|"pictures"|"videos"}} options + * @returns {Promise} + */ +export const getFileSystemDirectoryHandle = async (options) => { + if (!("showDirectoryPicker" in globalThis)) { + return Promise.reject( + new Error("showDirectoryPicker is not supported"), + ); + } + + const { promise, resolve, reject } = Promise.withResolvers(); + + const how = { id: "pyscript", mode: "readwrite", ...options }; + if (options.hint) how.startIn = options.hint; + + const transient = async () => { + try { + /* eslint-disable */ + const handler = await showDirectoryPicker(how); + /* eslint-enable */ + if ((await handler.requestPermission(how)) === "granted") { + resolve(handler); + return true; + } + } catch ({ message }) { + console.warn(message); + } + return false; + }; + + // in case the user decided to attach the event itself + // as opposite of relying our dialog walkthrough + if (navigator.userActivation?.isActive) { + if (!(await transient())) reject(new Error(ERROR)); + } else { + const dialog = assign(document.createElement("dialog"), { + className: "pyscript-fs", + innerHTML: [ + "ℹ️ Persistent FileSystem
", + "

PyScript would like to access a local folder.

", + "
", + "
", + ].join(""), + }); + + const [ok, cancel] = $$("button", dialog); + + ok.addEventListener("click", async (event) => { + stop(event); + if (await transient()) dialog.close(); + }); + + cancel.addEventListener("click", async (event) => { + stop(event); + reject(new Error(ERROR)); + dialog.close(); + }); + + document.body.appendChild(dialog).showModal(); + } + + return promise; +}; diff --git a/core/src/hooks.js b/core/src/hooks.js index acad61b1d50..9f108c633bc 100644 --- a/core/src/hooks.js +++ b/core/src/hooks.js @@ -88,7 +88,19 @@ export const hooks = { /** @type {Set} */ onBeforeRun: new SetFunction(), /** @type {Set} */ - onBeforeRunAsync: new SetFunction(), + onBeforeRunAsync: new SetFunction([ + ({ interpreter }) => { + interpreter.registerJsModule("_pyscript", { + // cannot be imported from fs.js + // because this code is stringified + fs: { + ERROR: "storage permissions not granted", + NAMESPACE: "@pyscript.fs", + }, + interpreter, + }); + }, + ]), /** @type {Set} */ onAfterRun: new SetFunction(), /** @type {Set} */ diff --git a/core/src/stdlib/pyscript.js b/core/src/stdlib/pyscript.js index 9c4550cef8d..2e5f5cd9d4a 100644 --- a/core/src/stdlib/pyscript.js +++ b/core/src/stdlib/pyscript.js @@ -7,6 +7,7 @@ export default { "fetch.py": "import json,js\nfrom pyscript.util import as_bytearray\nclass _Response:\n\tdef __init__(A,response):A._response=response\n\tdef __getattr__(A,attr):return getattr(A._response,attr)\n\tasync def arrayBuffer(B):\n\t\tA=await B._response.arrayBuffer()\n\t\tif hasattr(A,'to_py'):return A.to_py()\n\t\treturn memoryview(as_bytearray(A))\n\tasync def blob(A):return await A._response.blob()\n\tasync def bytearray(A):B=await A._response.arrayBuffer();return as_bytearray(B)\n\tasync def json(A):return json.loads(await A.text())\n\tasync def text(A):return await A._response.text()\nclass _DirectResponse:\n\t@staticmethod\n\tdef setup(promise,response):A=promise;A._response=_Response(response);return A._response\n\tdef __init__(B,promise):A=promise;B._promise=A;A._response=None;A.arrayBuffer=B.arrayBuffer;A.blob=B.blob;A.bytearray=B.bytearray;A.json=B.json;A.text=B.text\n\tasync def _response(A):\n\t\tif not A._promise._response:await A._promise\n\t\treturn A._promise._response\n\tasync def arrayBuffer(A):B=await A._response();return await B.arrayBuffer()\n\tasync def blob(A):B=await A._response();return await B.blob()\n\tasync def bytearray(A):B=await A._response();return await B.bytearray()\n\tasync def json(A):B=await A._response();return await B.json()\n\tasync def text(A):B=await A._response();return await B.text()\ndef fetch(url,**B):C=js.JSON.parse(json.dumps(B));D=lambda response,*B:_DirectResponse.setup(A,response);A=js.fetch(url,C).then(D);_DirectResponse(A);return A", "ffi.py": "try:\n\timport js;from pyodide.ffi import create_proxy as _cp,to_js as _py_tjs;from_entries=js.Object.fromEntries\n\tdef _tjs(value,**A):\n\t\tB='dict_converter'\n\t\tif not hasattr(A,B):A[B]=from_entries\n\t\treturn _py_tjs(value,**A)\nexcept:from jsffi import create_proxy as _cp;from jsffi import to_js as _tjs\ncreate_proxy=_cp\nto_js=_tjs", "flatted.py": "import json as _json\nclass _Known:\n\tdef __init__(A):A.key=[];A.value=[]\nclass _String:\n\tdef __init__(A,value):A.value=value\ndef _array_keys(value):\n\tA=[];B=0\n\tfor C in value:A.append(B);B+=1\n\treturn A\ndef _object_keys(value):\n\tA=[]\n\tfor B in value:A.append(B)\n\treturn A\ndef _is_array(value):A=value;return isinstance(A,list)or isinstance(A,tuple)\ndef _is_object(value):return isinstance(value,dict)\ndef _is_string(value):return isinstance(value,str)\ndef _index(known,input,value):B=value;A=known;input.append(B);C=str(len(input)-1);A.key.append(B);A.value.append(C);return C\ndef _loop(keys,input,known,output):\n\tA=output\n\tfor B in keys:\n\t\tC=A[B]\n\t\tif isinstance(C,_String):_ref(B,input[int(C.value)],input,known,A)\n\treturn A\ndef _ref(key,value,input,known,output):\n\tB=known;A=value\n\tif _is_array(A)and not A in B:B.append(A);A=_loop(_array_keys(A),input,B,A)\n\telif _is_object(A)and not A in B:B.append(A);A=_loop(_object_keys(A),input,B,A)\n\toutput[key]=A\ndef _relate(known,input,value):\n\tB=known;A=value\n\tif _is_string(A)or _is_array(A)or _is_object(A):\n\t\ttry:return B.value[B.key.index(A)]\n\t\texcept:return _index(B,input,A)\n\treturn A\ndef _transform(known,input,value):\n\tB=known;A=value\n\tif _is_array(A):\n\t\tC=[]\n\t\tfor F in A:C.append(_relate(B,input,F))\n\t\treturn C\n\tif _is_object(A):\n\t\tD={}\n\t\tfor E in A:D[E]=_relate(B,input,A[E])\n\t\treturn D\n\treturn A\ndef _wrap(value):\n\tA=value\n\tif _is_string(A):return _String(A)\n\tif _is_array(A):\n\t\tB=0\n\t\tfor D in A:A[B]=_wrap(D);B+=1\n\telif _is_object(A):\n\t\tfor C in A:A[C]=_wrap(A[C])\n\treturn A\ndef parse(value,*C,**D):\n\tA=value;E=_json.loads(A,*C,**D);B=[]\n\tfor A in E:B.append(_wrap(A))\n\tinput=[]\n\tfor A in B:\n\t\tif isinstance(A,_String):input.append(A.value)\n\t\telse:input.append(A)\n\tA=input[0]\n\tif _is_array(A):return _loop(_array_keys(A),input,[A],A)\n\tif _is_object(A):return _loop(_object_keys(A),input,[A],A)\n\treturn A\ndef stringify(value,*D,**E):\n\tB=_Known();input=[];C=[];A=int(_index(B,input,value))\n\twhile A Promise.all(urls.map((url) => import(url)))')()\n\texcept:message='Unable to use `window` or `document` -> https://docs.pyscript.net/latest/faq/#sharedarraybuffer';globalThis.console.warn(message);window=NotSupported('pyscript.window',message);document=NotSupported('pyscript.document',message);js_import=None\n\tsync=polyscript.xworker.sync\n\tdef current_target():return polyscript.target\nelse:\n\timport _pyscript;from _pyscript import PyWorker,js_import;window=globalThis;document=globalThis.document;sync=NotSupported('pyscript.sync','pyscript.sync works only when running in a worker')\n\tdef current_target():return _pyscript.target", "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}\"))", diff --git a/core/src/stdlib/pyscript/fs.py b/core/src/stdlib/pyscript/fs.py new file mode 100644 index 00000000000..6dd302f9bee --- /dev/null +++ b/core/src/stdlib/pyscript/fs.py @@ -0,0 +1,60 @@ +mounted = {} + + +async def mount(path, mode="readwrite", root="", id="pyscript"): + import js + from _pyscript import fs, interpreter + from pyscript.ffi import to_js + from pyscript.magic_js import ( + RUNNING_IN_WORKER, + sync, + ) + + js.console.warn("experimental pyscript.fs ⚠️") + + handler = None + + uid = f"{path}@{id}" + + options = {"id": id, "mode": mode} + if root != "": + options["startIn"] = root + + if RUNNING_IN_WORKER: + fsh = sync.storeFSHandler(uid, to_js(options)) + + # allow both async and/or SharedArrayBuffer use case + if isinstance(fsh, bool): + success = fsh + else: + success = await fsh + + if success: + from polyscript import IDBMap + + idb = IDBMap.new(fs.NAMESPACE) + handler = await idb.get(uid) + else: + raise RuntimeError(fs.ERROR) + + else: + success = await fs.idb.has(uid) + + if success: + handler = await fs.idb.get(uid) + else: + handler = await fs.getFileSystemDirectoryHandle(to_js(options)) + await fs.idb.set(uid, handler) + + mounted[path] = await interpreter.mountNativeFS(path, handler) + + +async def sync(path): + await mounted[path].syncfs() + + +async def unmount(path): + from _pyscript import interpreter + + await sync(path) + interpreter._module.FS.unmount(path) diff --git a/core/src/sync.js b/core/src/sync.js index 3569c762de8..10d73439af9 100644 --- a/core/src/sync.js +++ b/core/src/sync.js @@ -1,3 +1,5 @@ +import { idb, getFileSystemDirectoryHandle } from "./fs.js"; + export default { // allow pyterminal checks to bootstrap is_pyterminal: () => false, @@ -9,4 +11,21 @@ export default { sleep(seconds) { return new Promise(($) => setTimeout($, seconds * 1000)); }, + + /** + * Ask a user action via dialog and returns the directory handler once granted. + * @param {string} uid + * @param {{id?:string, mode?:"read"|"readwrite", hint?:"desktop"|"documents"|"downloads"|"music"|"pictures"|"videos"}} options + * @returns {boolean} + */ + async storeFSHandler(uid, options = {}) { + if (await idb.has(uid)) return true; + return getFileSystemDirectoryHandle(options).then( + async (handler) => { + await idb.set(uid, handler); + return true; + }, + () => false, + ); + }, }; diff --git a/core/tests/index.html b/core/tests/index.html index ebbba369f5a..d62386b944d 100644 --- a/core/tests/index.html +++ b/core/tests/index.html @@ -14,5 +14,5 @@ a:hover { opacity: 1; } - + diff --git a/core/tests/manual/fs/index.html b/core/tests/manual/fs/index.html new file mode 100644 index 00000000000..438e6fd0d28 --- /dev/null +++ b/core/tests/manual/fs/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/core/tests/manual/fs/index.py b/core/tests/manual/fs/index.py new file mode 100644 index 00000000000..5f2aca0a138 --- /dev/null +++ b/core/tests/manual/fs/index.py @@ -0,0 +1,46 @@ +import os +from pyscript import RUNNING_IN_WORKER, fs + + +TEST = "implicit" + +if TEST == "implicit": + await fs.mount("/persistent") + + print( + RUNNING_IN_WORKER and "Worker" or "Main", + os.listdir("/persistent"), + ) + + from random import random + + with open("/persistent/random.txt", "w") as f: + f.write(str(random())) + + await fs.sync("/persistent") + +elif not RUNNING_IN_WORKER: + from pyscript import document + + button = document.createElement("button") + button.textContent = "mount" + document.body.append(button) + + async def mount(event): + try: + await fs.mount("/persistent") + print(os.listdir("/persistent")) + button.textContent = "unmount" + button.onclick = unmount + + except: + import js + + js.alert("unable to grant access") + + async def unmount(event): + await fs.unmount("/persistent") + button.textContent = "mount" + button.onclick = mount + + button.onclick = mount diff --git a/core/types/fs.d.ts b/core/types/fs.d.ts new file mode 100644 index 00000000000..3283c5d4009 --- /dev/null +++ b/core/types/fs.d.ts @@ -0,0 +1,8 @@ +export const NAMESPACE: "@pyscript.fs"; +export const ERROR: "storage permissions not granted"; +export const idb: any; +export function getFileSystemDirectoryHandle(options: { + id?: string; + mode?: "read" | "readwrite"; + hint?: "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos"; +}): Promise; diff --git a/core/types/stdlib/pyscript.d.ts b/core/types/stdlib/pyscript.d.ts index 0b2de8ff9bc..8c6c476d5a8 100644 --- a/core/types/stdlib/pyscript.d.ts +++ b/core/types/stdlib/pyscript.d.ts @@ -6,6 +6,7 @@ declare namespace _default { "fetch.py": string; "ffi.py": string; "flatted.py": string; + "fs.py": string; "magic_js.py": string; "media.py": string; "storage.py": string; diff --git a/core/types/sync.d.ts b/core/types/sync.d.ts index fb57cb15d7d..484122d3782 100644 --- a/core/types/sync.d.ts +++ b/core/types/sync.d.ts @@ -5,5 +5,16 @@ declare namespace _default { * @param {number} seconds The number of seconds to sleep. */ function sleep(seconds: number): Promise; + /** + * Ask a user action via dialog and returns the directory handler once granted. + * @param {string} uid + * @param {{id?:string, mode?:"read"|"readwrite", hint?:"desktop"|"documents"|"downloads"|"music"|"pictures"|"videos"}} options + * @returns {boolean} + */ + function storeFSHandler(uid: string, options?: { + id?: string; + mode?: "read" | "readwrite"; + hint?: "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos"; + }): boolean; } export default _default;