diff --git a/package-lock.json b/package-lock.json index d857828..36b834a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,20 +16,24 @@ "@codemirror/lint": "6.5.0", "@codemirror/state": "6.4.1", "@codemirror/view": "6.26.3", - "@devvit/previews": "0.10.20", - "@devvit/protos": "0.10.20", - "@devvit/public-api": "0.10.20", - "@devvit/runtime-lite": "0.10.20", - "@devvit/shared-types": "0.10.20", - "@devvit/ui-renderer": "0.10.20", + "@devvit/previews": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/protos": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/public-api": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/runtime-lite": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/shared-types": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/ui-renderer": "0.10.21-next-2024-05-13-b48ce196f.0", "@esm-bundle/chai": "4.3.4-fix.0", "@types/jsdom": "21.1.6", "@types/mocha": "10.0.6", "@typescript/vfs": "1.5.0", "@web/dev-server-esbuild": "1.0.2", "@web/test-runner": "0.18.1", + "@zenfs/core": "0.9.7", + "@zenfs/dom": "0.2.6", + "@zenfs/zip": "0.3.1", "codemirror": "6.0.1", "esbuild": "0.20.2", + "idb-keyval": "6.2.1", "jsdom": "24.0.0", "lit": "3.1.3", "lit-analyzer": "2.0.3", @@ -84,23 +88,6 @@ "node": ">=12" } }, - "node_modules/@ampproject/filesize/node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -249,15 +236,15 @@ } }, "node_modules/@devvit/previews": { - "version": "0.10.20", - "resolved": "https://registry.npmjs.org/@devvit/previews/-/previews-0.10.20.tgz", - "integrity": "sha512-2ePAz2Py4PMpg5mI4yY5SkkdkqIGB/F9gJoSaM6Wi9+y9OEgpXIrsIZM9Xr4v1qpt5n5DbXxVOIUxyFDvELWVg==", + "version": "0.10.21-next-2024-05-13-b48ce196f.0", + "resolved": "https://registry.npmjs.org/@devvit/previews/-/previews-0.10.21-next-2024-05-13-b48ce196f.0.tgz", + "integrity": "sha512-3zDWXAK5LMUgjU033lN5bmKPjpE+rkbonu2HE6XUxgUXJoiiIYYbcS5iM+nobf1k9w+0fY66KPkvMjl/K3XKuA==", "dev": true, "dependencies": { - "@devvit/protos": "0.10.20", - "@devvit/public-api": "0.10.20", - "@devvit/runtime-lite": "0.10.20", - "@devvit/ui-renderer": "0.10.20", + "@devvit/protos": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/public-api": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/runtime-lite": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/ui-renderer": "0.10.21-next-2024-05-13-b48ce196f.0", "lit": "2.2.8", "p-queue": "7.3.4" } @@ -303,9 +290,9 @@ } }, "node_modules/@devvit/protos": { - "version": "0.10.20", - "resolved": "https://registry.npmjs.org/@devvit/protos/-/protos-0.10.20.tgz", - "integrity": "sha512-/IV/O08N5bxrTUZ1DgUEgNKP9Tb3HUkOR5sIv1ApdVzeRht3PHO3aY7DfYCwhHTeXpLbCq6SmzhJmhJ29Qe51g==", + "version": "0.10.21-next-2024-05-13-b48ce196f.0", + "resolved": "https://registry.npmjs.org/@devvit/protos/-/protos-0.10.21-next-2024-05-13-b48ce196f.0.tgz", + "integrity": "sha512-lFIQwTfLkgNpLMoTOAtStuOuivIAp4eA/oDDqqs1OOatVc1YjWM3hEkoT4+X6hpWubJX6ozPF2pXnoNjMXqZLg==", "dev": true, "dependencies": { "protobufjs": "7.2.4", @@ -321,14 +308,13 @@ } }, "node_modules/@devvit/public-api": { - "version": "0.10.20", - "resolved": "https://registry.npmjs.org/@devvit/public-api/-/public-api-0.10.20.tgz", - "integrity": "sha512-+VKDazLJkAK9equQRndyYU9skthy2+djvOd/D6B1oBgOjsOTSe3pXogYwMENguN+3koywc/p6RRIYGJYN85nAQ==", + "version": "0.10.21-next-2024-05-13-b48ce196f.0", + "resolved": "https://registry.npmjs.org/@devvit/public-api/-/public-api-0.10.21-next-2024-05-13-b48ce196f.0.tgz", + "integrity": "sha512-qrQZdEe4Je4kPD0g37q3tK0pFwhYB1dffKUJ/9Du8v9/RId2miXfZoX7xGj5AIvwCuPZtQeurvEPh2CYS57vqQ==", "dev": true, "dependencies": { - "@devvit/protos": "0.10.20", - "@devvit/runtimes": "0.10.20", - "@devvit/shared-types": "0.10.20", + "@devvit/protos": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/shared-types": "0.10.21-next-2024-05-13-b48ce196f.0", "base64-js": "1.5.1", "clone-deep": "4.0.1", "core-js": "3.27.2", @@ -336,9 +322,9 @@ } }, "node_modules/@devvit/runtime-lite": { - "version": "0.10.20", - "resolved": "https://registry.npmjs.org/@devvit/runtime-lite/-/runtime-lite-0.10.20.tgz", - "integrity": "sha512-YN0BwYEa8BATViwKn9//ivs06UqZ2K8d3Ru1U8CDTFuzTZnMvFtoZ0UK6QHE1y5qcnS97TrWICXM7xO6iiN5zg==", + "version": "0.10.21-next-2024-05-13-b48ce196f.0", + "resolved": "https://registry.npmjs.org/@devvit/runtime-lite/-/runtime-lite-0.10.21-next-2024-05-13-b48ce196f.0.tgz", + "integrity": "sha512-9eZi36WTg71psMXpeY15N3uElc5HkhMi2awXKwmuGw9FMNdRqYgmzdpOl/ZEsaMIMu8WPsIPpE/HmaGnMH5qsQ==", "dev": true, "dependencies": { "@formatjs/intl-datetimeformat": "6.12.0", @@ -349,45 +335,45 @@ } }, "node_modules/@devvit/runtimes": { - "version": "0.10.20", - "resolved": "https://registry.npmjs.org/@devvit/runtimes/-/runtimes-0.10.20.tgz", - "integrity": "sha512-GSiCutY2/kN8giiRtgzYRoIDruicH2kNztCqq7XN5pFkcCDX2N1UIDLL1sNBT3UnnO+H1RUoDSVQOPfxNma0Aw==", + "version": "0.10.21-next-2024-05-13-b48ce196f.0", + "resolved": "https://registry.npmjs.org/@devvit/runtimes/-/runtimes-0.10.21-next-2024-05-13-b48ce196f.0.tgz", + "integrity": "sha512-dp07OSkMX9zchfgv1kJoTSnN9OyDhdQlOoCuApLXo2JG7p1ZLBSPzJ+1Sy2LKcJOz2c8Ex0NqkDORgiYUdmTqA==", "dev": true, "dependencies": { - "@devvit/protos": "0.10.20", - "@devvit/shared-types": "0.10.20", - "@devvit/web-worker": "0.10.20", + "@devvit/protos": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/shared-types": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/web-worker": "0.10.21-next-2024-05-13-b48ce196f.0", "base64-js": "1.5.1", "buffer": "6.0.3", "cron-parser": "4.7.1", "lru-cache": "7.10.1", "node-localstorage": "2.2.1", - "redis": "4.6.6", "rxjs": "7.8.1", "undici": "5.28.2", "uuid": "9.0.0" } }, "node_modules/@devvit/shared-types": { - "version": "0.10.20", - "resolved": "https://registry.npmjs.org/@devvit/shared-types/-/shared-types-0.10.20.tgz", - "integrity": "sha512-S3j4mj2jc98BSg9tAg5Ycn1QqprQlFmPw7l/iBtRM/EI2+UJ0sMEU8WoTFysb1RoVuj+nhKDUR5s+5VQ4/bVYg==", + "version": "0.10.21-next-2024-05-13-b48ce196f.0", + "resolved": "https://registry.npmjs.org/@devvit/shared-types/-/shared-types-0.10.21-next-2024-05-13-b48ce196f.0.tgz", + "integrity": "sha512-bvcuhF5P3STum3kQ+CyCftLPLX447uEESY5QZjkxzg0omIW86XXru5mf0wMsBrcvuIjwcamYtELPxPNfeZgvWA==", "dev": true, "dependencies": { - "@devvit/protos": "0.10.20" + "@devvit/protos": "0.10.21-next-2024-05-13-b48ce196f.0" } }, "node_modules/@devvit/ui-renderer": { - "version": "0.10.20", - "resolved": "https://registry.npmjs.org/@devvit/ui-renderer/-/ui-renderer-0.10.20.tgz", - "integrity": "sha512-fJ117y+iL07zti8vPAV3v6QhWJ7jlOLeGNuaH0NAPguOEU5V5cwmtpOobm2+IX+tOoYJ0mSNtQz31EimBuEdKQ==", + "version": "0.10.21-next-2024-05-13-b48ce196f.0", + "resolved": "https://registry.npmjs.org/@devvit/ui-renderer/-/ui-renderer-0.10.21-next-2024-05-13-b48ce196f.0.tgz", + "integrity": "sha512-YxyPz/mc0x59v4ZglAzbXwEoc1CfsimRsl4b1uU4Y7SdRQJWelbMWj4kLF4iGF/PKIu0sxVyQfpt59HxLbE98w==", "dev": true, "dependencies": { - "@devvit/protos": "0.10.20", - "@devvit/runtime-lite": "0.10.20", - "@devvit/runtimes": "0.10.20", - "@devvit/shared-types": "0.10.20", + "@devvit/protos": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/runtime-lite": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/runtimes": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/shared-types": "0.10.21-next-2024-05-13-b48ce196f.0", "@dotlottie/player-component": "2.7.2", + "compare-versions": "6.1.0", "nice-grpc-web": "3.3.3", "p-queue": "7.3.4", "rxjs": "7.8.1" @@ -457,9 +443,9 @@ } }, "node_modules/@devvit/web-worker": { - "version": "0.10.20", - "resolved": "https://registry.npmjs.org/@devvit/web-worker/-/web-worker-0.10.20.tgz", - "integrity": "sha512-kcAjHzXi1r4QdvV/II3qRUxV+T1hULWRd7rQ1EFSjBoaAHAXyQ3JcuzOlzOMF454jKevggNk9IJ+fCosnW/VEw==", + "version": "0.10.21-next-2024-05-13-b48ce196f.0", + "resolved": "https://registry.npmjs.org/@devvit/web-worker/-/web-worker-0.10.21-next-2024-05-13-b48ce196f.0.tgz", + "integrity": "sha512-IFhIqHH8qvH5Mh9XTBet1btYPRtBAJlkNV9Df9ODkgcxRFZQH6sXuK9aHwfQd7/6sy1BnE8DJv/8lttfiPD6vg==", "dev": true }, "node_modules/@dotlottie/common": { @@ -1444,90 +1430,6 @@ "node": ">=10" } }, - "node_modules/@puppeteer/browsers/node_modules/tar-fs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", - "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", - "dev": true, - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" - } - }, - "node_modules/@puppeteer/browsers/node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dev": true, - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "dev": true, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/client": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.7.tgz", - "integrity": "sha512-gaOBOuJPjK5fGtxSseaKgSvjiZXQCdLlGg9WYQst+/GRUjmXaiB5kVkeQMRtPc7Q2t93XZcJfBMSwzs/XS9UZw==", - "dev": true, - "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/graph": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", - "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", - "dev": true, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/json": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", - "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", - "dev": true, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/search": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz", - "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==", - "dev": true, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/time-series": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", - "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", - "dev": true, - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, "node_modules/@rgba-image/common": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/@rgba-image/common/-/common-0.1.13.tgz", @@ -1611,9 +1513,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", - "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", "cpu": [ "arm" ], @@ -1624,9 +1526,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", - "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", "cpu": [ "arm64" ], @@ -1637,9 +1539,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", - "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", "cpu": [ "arm64" ], @@ -1650,9 +1552,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", - "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", "cpu": [ "x64" ], @@ -1663,9 +1565,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", - "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", "cpu": [ "arm" ], @@ -1676,9 +1578,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", - "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", "cpu": [ "arm" ], @@ -1689,9 +1591,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", - "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", "cpu": [ "arm64" ], @@ -1702,9 +1604,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", - "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", "cpu": [ "arm64" ], @@ -1715,9 +1617,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", - "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", "cpu": [ "ppc64" ], @@ -1728,9 +1630,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", - "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", "cpu": [ "riscv64" ], @@ -1741,9 +1643,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", - "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", "cpu": [ "s390x" ], @@ -1754,9 +1656,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", - "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", "cpu": [ "x64" ], @@ -1767,9 +1669,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", - "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", "cpu": [ "x64" ], @@ -1780,9 +1682,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", - "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", "cpu": [ "arm64" ], @@ -1793,9 +1695,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", - "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", "cpu": [ "ia32" ], @@ -1806,9 +1708,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", - "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", "cpu": [ "x64" ], @@ -1935,9 +1837,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", - "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.1.tgz", + "integrity": "sha512-ej0phymbFLoCB26dbbq5PGScsf2JAJ4IJHjG10LalgUV36XKTmA4GdA+PVllKvRk0sEKt64X8975qFnkSi0hqA==", "dev": true, "dependencies": { "@types/node": "*", @@ -2037,9 +1939,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", - "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2063,6 +1965,16 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, + "node_modules/@types/readable-stream": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.14.tgz", + "integrity": "sha512-xZn/AuUbCMShGsqH/ehZtGDwQtbx00M9rZ2ENLe4tOjFZ/JFeWMhEZkk2fEe1jAUqqEAURIkFJ7Az/go8mM1/w==", + "dev": true, + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -2242,15 +2154,15 @@ } }, "node_modules/@web/dev-server": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@web/dev-server/-/dev-server-0.4.4.tgz", - "integrity": "sha512-Gye0DhDbst/KVNRCFzRd+4V9LJmuuQYJBsf6UXeEbCYuBSKeshEW4AA1esLsfy1gONsD6NIGiru5509l35P9Ug==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@web/dev-server/-/dev-server-0.4.5.tgz", + "integrity": "sha512-R11sODOLFcm51f2uir51KE4QXRSYakDaeBeJdrUutPCmYUDEk86GjYBR3R1wslimnwGPIjhFDsXNMfASxYfgAQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.11", "@types/command-line-args": "^5.0.0", "@web/config-loader": "^0.3.0", - "@web/dev-server-core": "^0.7.1", + "@web/dev-server-core": "^0.7.2", "@web/dev-server-rollup": "^0.6.1", "camelcase": "^6.2.0", "command-line-args": "^5.1.1", @@ -2271,9 +2183,9 @@ } }, "node_modules/@web/dev-server-core": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@web/dev-server-core/-/dev-server-core-0.7.1.tgz", - "integrity": "sha512-alHd2j0f4e1ekqYDR8lWScrzR7D5gfsUZq3BP3De9bkFWM3AELINCmqqlVKmCtlkAdEc9VyQvNiEqrxraOdc2A==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@web/dev-server-core/-/dev-server-core-0.7.2.tgz", + "integrity": "sha512-Q/0jpF13Ipk+qGGQ+Yx/FW1TQBYazpkfgYHHo96HBE7qv4V4KKHqHglZcSUxti/zd4bToxX1cFTz8dmbTlb8JA==", "dev": true, "dependencies": { "@types/koa": "^2.11.6", @@ -2744,13 +2656,13 @@ "dev": true }, "node_modules/@web/dev-server-rollup": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.6.2.tgz", - "integrity": "sha512-w34OSPxTlbkCWjKm6MRpCwMQuG0K1QroI/WVKKUr7QjD9tVuyE4bgMN34z5NtEZLgPqQWloCd9szZ7e4hQtlAQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.6.3.tgz", + "integrity": "sha512-dzMwQRBk9Rhpfoo7vvQGvRP18sDELejJCwxsMdt509aLouIB6fviv0i87DJQWbXH24hBeq6+jSILI3JTtVaPZQ==", "dev": true, "dependencies": { "@rollup/plugin-node-resolve": "^15.0.1", - "@web/dev-server-core": "^0.7.0", + "@web/dev-server-core": "^0.7.2", "nanocolors": "^0.2.1", "parse5": "^6.0.1", "rollup": "^4.4.0", @@ -2846,9 +2758,9 @@ } }, "node_modules/@web/test-runner-core": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@web/test-runner-core/-/test-runner-core-0.13.1.tgz", - "integrity": "sha512-2hESALx/UFsAzK+ApWXAkFp2eCmwcs2yj1v4YjwV8F38sQumJ40P3px3BMjFwkOYDORtQOicW0RUeSw1g3BMLA==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@web/test-runner-core/-/test-runner-core-0.13.2.tgz", + "integrity": "sha512-G0D3mv9jvR+5xILENchPP9v1ZjBf3QVlzarMLR5jedCNbgntzcayF0LeW5wh5uyafGZJH28cYm9jGrJvGipoPQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.11", @@ -2859,7 +2771,7 @@ "@types/istanbul-lib-coverage": "^2.0.3", "@types/istanbul-reports": "^3.0.0", "@web/browser-logs": "^0.4.0", - "@web/dev-server-core": "^0.7.0", + "@web/dev-server-core": "^0.7.2", "chokidar": "^3.4.3", "cli-cursor": "^3.1.0", "co-body": "^6.1.0", @@ -2919,6 +2831,79 @@ "node": ">=18.0.0" } }, + "node_modules/@zenfs/core": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/@zenfs/core/-/core-0.9.7.tgz", + "integrity": "sha512-Lmwt1DPhJy07Qx4m4JrzyB8LNBbe5SMq5Z+ZpLjJRfMcSHkoy64l+t8njADVKl0mns91tzo2hEcHNfKG/5Pxog==", + "dev": true, + "dependencies": { + "@types/node": "^20.12.5", + "@types/readable-stream": "^4.0.10", + "buffer": "^6.0.3", + "minimatch": "^9.0.3", + "readable-stream": "^4.5.2", + "utilium": "^0.2.1" + }, + "bin": { + "build": "scripts/build.js", + "make-index": "scripts/make-index.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@zenfs/dom": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@zenfs/dom/-/dom-0.2.6.tgz", + "integrity": "sha512-TCo40azeN9b/W8z1TS0DhCUJPW0S8I5ypMOcizJ8aK4NZmC/b+bYAwrdUITHRZOatgHEncIxeK3gxXC9SaD68Q==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@zenfs/core": "^0.9.7" + } + }, + "node_modules/@zenfs/zip": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@zenfs/zip/-/zip-0.3.1.tgz", + "integrity": "sha512-1Rx2qxVYNT2hb537z1xfwfFz289DIIFzhzJtVb2WoiZpz+CuXurBLYzrqI+PMojmWu+Qg9qy6kbkb2XhlGYSzQ==", + "dev": true, + "dependencies": { + "fflate": "^0.8.2", + "utilium": "^0.3.4" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@zenfs/core": "^0.9.2" + } + }, + "node_modules/@zenfs/zip/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true + }, + "node_modules/@zenfs/zip/node_modules/utilium": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/utilium/-/utilium-0.3.4.tgz", + "integrity": "sha512-qk6wXThaKkeigQchXWEmTw388ZPvKHwYzpkcII/azXuE8OgQypOKH1j+fA6M88ucpqEdHqldXjNKzHya2S9lHA==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/abort-controller-x": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/abort-controller-x/-/abort-controller-x-0.4.3.tgz", @@ -2986,6 +2971,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -3099,6 +3093,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, "node_modules/bare-events": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.2.tgz", @@ -3235,13 +3235,22 @@ "node": ">= 6" } }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3594,15 +3603,6 @@ "node": ">=12" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3636,32 +3636,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -3702,15 +3676,6 @@ "node": ">=6" } }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -3870,6 +3835,12 @@ "node": ">=12.17" } }, + "node_modules/compare-versions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz", + "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==", + "dev": true + }, "node_modules/confbox": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", @@ -3888,6 +3859,26 @@ "node": ">= 0.6" } }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", @@ -4047,6 +4038,21 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -4166,10 +4172,19 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/devtools-protocol": { - "version": "0.0.1273771", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1273771.tgz", - "integrity": "sha512-QDbb27xcTVReQQW/GHJsdQqGKwYBE7re7gxehj467kKP2DKuYBUj6i2k5LRiAC66J1yZG/9gsxooz/s9pcm0Og==", + "version": "0.0.1286932", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1286932.tgz", + "integrity": "sha512-wu58HMQll9voDjR4NlPyoDEw1syfzaBNHymMMZ/QOXiHRNluOnDgu9hp1yHOKYoMlxCh4lSSiugLITe6Fvu1eA==", "dev": true }, "node_modules/didyoumean2": { @@ -4295,9 +4310,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", - "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", + "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==", "dev": true }, "node_modules/esbuild": { @@ -4439,12 +4454,30 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", @@ -4588,19 +4621,20 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", + "glob-parent": "^5.1.0", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" }, "engines": { - "node": ">=8.6.0" + "node": ">=8" } }, "node_modules/fastq": { @@ -4628,9 +4662,9 @@ "dev": true }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4717,15 +4751,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -4840,15 +4865,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/globby/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { "url": "https://github.com/sponsors/ljharb" } }, @@ -5048,6 +5089,12 @@ "node": ">=0.10.0" } }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==", + "dev": true + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -5126,10 +5173,10 @@ "node": ">= 12" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "dev": true }, "node_modules/is-binary-path": { @@ -5738,6 +5785,22 @@ "lit-analyzer": "cli.js" } }, + "node_modules/lit-analyzer/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/lit-analyzer/node_modules/parse5": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", @@ -5949,12 +6012,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -5991,6 +6054,33 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -6120,6 +6210,18 @@ "nice-grpc-common": "^2.0.2" } }, + "node_modules/node-abi": { + "version": "3.62.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz", + "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-addon-api": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", @@ -6175,9 +6277,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz", - "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==", + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", + "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", "dev": true }, "node_modules/object-inspect": { @@ -6414,9 +6516,9 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/picomatch": { @@ -6524,6 +6626,74 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/prebuild-install/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/prettier": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", @@ -6565,6 +6735,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -6658,15 +6837,15 @@ } }, "node_modules/puppeteer-core": { - "version": "22.8.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.8.0.tgz", - "integrity": "sha512-S5bWx3g/fNuyFxjZX9TkZMN07CEH47+9Zm6IiTl1QfqI9pnVaShbwrD9kRe5vmz/XPp/jLGhhxRUj1sY4wObnA==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.9.0.tgz", + "integrity": "sha512-Q2SYVZ1SIE7jCd/Pp+1/mNLFtdJfGvAF+CqOTDG8HcCNCiBvoXfopXfOfMHQ/FueXhGfJW/I6DartWv6QzpNGg==", "dev": true, "dependencies": { "@puppeteer/browsers": "2.2.3", "chromium-bidi": "0.5.19", "debug": "4.3.4", - "devtools-protocol": "0.0.1273771", + "devtools-protocol": "0.0.1286932", "ws": "8.17.0" }, "engines": { @@ -6808,6 +6987,22 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, + "node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6820,20 +7015,6 @@ "node": ">=8.10.0" } }, - "node_modules/redis": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.6.tgz", - "integrity": "sha512-aLs2fuBFV/VJ28oLBqYykfnhGGkFxvx0HdCEBYdJ99FFbSEMZ7c1nVKwR6ZRv+7bb7JnC0mmCzaqu8frgOYhpA==", - "dev": true, - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.5.7", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.2", - "@redis/time-series": "1.0.4" - } - }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -6954,9 +7135,9 @@ } }, "node_modules/rollup": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", - "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -6969,22 +7150,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.17.2", - "@rollup/rollup-android-arm64": "4.17.2", - "@rollup/rollup-darwin-arm64": "4.17.2", - "@rollup/rollup-darwin-x64": "4.17.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", - "@rollup/rollup-linux-arm-musleabihf": "4.17.2", - "@rollup/rollup-linux-arm64-gnu": "4.17.2", - "@rollup/rollup-linux-arm64-musl": "4.17.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", - "@rollup/rollup-linux-riscv64-gnu": "4.17.2", - "@rollup/rollup-linux-s390x-gnu": "4.17.2", - "@rollup/rollup-linux-x64-gnu": "4.17.2", - "@rollup/rollup-linux-x64-musl": "4.17.2", - "@rollup/rollup-win32-arm64-msvc": "4.17.2", - "@rollup/rollup-win32-ia32-msvc": "4.17.2", - "@rollup/rollup-win32-x64-msvc": "4.17.2", + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", "fsevents": "~2.3.2" } }, @@ -7027,24 +7208,10 @@ } }, "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -7146,142 +7313,6 @@ "sharp": ">= 0.25.4" } }, - "node_modules/sharp/node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sharp/node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sharp/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sharp/node_modules/node-abi": { - "version": "3.62.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz", - "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp/node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", - "dev": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp/node_modules/prebuild-install/node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/sharp/node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/sharp/node_modules/tar-fs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", - "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" - } - }, - "node_modules/sharp/node_modules/tar-fs/node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dev": true, - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7353,6 +7384,31 @@ } ] }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -7362,12 +7418,6 @@ "is-arrayish": "^0.3.1" } }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7492,6 +7542,12 @@ "node": ">=0.10.0" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, "node_modules/stack-generator": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", @@ -7581,19 +7637,59 @@ } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/strip-final-newline": { "version": "3.0.0", @@ -7709,34 +7805,29 @@ "node": ">=12.17" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/tar-fs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", + "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", "dev": true, "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "pump": "^3.0.0", + "tar-stream": "^3.1.5" }, - "engines": { - "node": ">=6" + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" } }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, "node_modules/through": { @@ -8136,6 +8227,12 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/utilium": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/utilium/-/utilium-0.2.1.tgz", + "integrity": "sha512-uLn55gYhtxFcS2X6rgvd3+aIEx5xVA3GBQgBkyJRdKAHxXXYiyB5P6ZmL/94HIWvgZaVs8xnNRCiiiRUBqyUIA==", + "dev": true + }, "node_modules/uuid": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", @@ -8588,15 +8685,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -8630,32 +8718,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8761,41 +8823,6 @@ "node": ">=12" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/package.json b/package.json index 1cd3c78..6f63304 100644 --- a/package.json +++ b/package.json @@ -10,20 +10,24 @@ "@codemirror/lint": "6.5.0", "@codemirror/state": "6.4.1", "@codemirror/view": "6.26.3", - "@devvit/previews": "0.10.20", - "@devvit/protos": "0.10.20", - "@devvit/public-api": "0.10.20", - "@devvit/runtime-lite": "0.10.20", - "@devvit/shared-types": "0.10.20", - "@devvit/ui-renderer": "0.10.20", + "@devvit/previews": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/protos": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/public-api": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/runtime-lite": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/shared-types": "0.10.21-next-2024-05-13-b48ce196f.0", + "@devvit/ui-renderer": "0.10.21-next-2024-05-13-b48ce196f.0", "@esm-bundle/chai": "4.3.4-fix.0", "@types/jsdom": "21.1.6", "@types/mocha": "10.0.6", "@typescript/vfs": "1.5.0", "@web/dev-server-esbuild": "1.0.2", "@web/test-runner": "0.18.1", + "@zenfs/core": "0.9.7", + "@zenfs/dom": "0.2.6", + "@zenfs/zip": "0.3.1", "codemirror": "6.0.1", "esbuild": "0.20.2", + "idb-keyval": "6.2.1", "jsdom": "24.0.0", "lit": "3.1.3", "lit-analyzer": "2.0.3", @@ -56,8 +60,8 @@ "gzip": "3.5 KB" }, "dist/play-pen.js": { - "none": "35000 KB", - "gzip": "5200 KB" + "none": "37000 KB", + "gzip": "5400 KB" } }, "typesVersions": { diff --git a/src/bundler/linker.ts b/src/bundler/linker.ts index b8c3bcd..5ee32c0 100644 --- a/src/bundler/linker.ts +++ b/src/bundler/linker.ts @@ -1,15 +1,21 @@ import type {LinkedBundle, SerializableServiceDefinition} from '@devvit/protos' +import type {AssetMap} from '@devvit/shared-types/Assets.js' /** * @arg es JavaScript * @arg hostname Arbitrary but something unique to the window like * hello-world.local may allow concurrent sessions with the * remote. + * @arg assets AssetMap describing how to map project assets to URLs */ -export function link(es: string, hostname: string): LinkedBundle { +export function link( + es: string, + hostname: string, + assets: AssetMap +): LinkedBundle { return { actor: {name: 'pen', owner: 'play', version: '0.0.0.0'}, - assets: {}, + assets: assets ?? {}, code: es, hostname, provides: provides(), diff --git a/src/elements/play-assets-dialog.test.ts b/src/elements/play-assets-dialog.test.ts new file mode 100644 index 0000000..5eea5c1 --- /dev/null +++ b/src/elements/play-assets-dialog.test.ts @@ -0,0 +1,7 @@ +import {assert} from '@esm-bundle/chai' +import {PlayAssetsDialog} from './play-assets-dialog' + +test('tag is defined', () => { + const el = document.createElement('play-assets-dialog') + assert.instanceOf(el, PlayAssetsDialog) +}) diff --git a/src/elements/play-assets-dialog.ts b/src/elements/play-assets-dialog.ts new file mode 100644 index 0000000..bc57776 --- /dev/null +++ b/src/elements/play-assets-dialog.ts @@ -0,0 +1,157 @@ +import {customElement, property, query} from 'lit/decorators.js' +import { + css, + type CSSResultGroup, + html, + LitElement, + type TemplateResult +} from 'lit' +import {PlayDialog} from './play-dialog/play-dialog.js' +import {choose} from 'lit-html/directives/choose.js' +import {when} from 'lit-html/directives/when.js' +import {cssReset} from '../utils/css-reset.js' +import {Bubble} from '../utils/bubble.js' +import { + type AssetsFilesystemChange, + type AssetsFilesystemType, + type AssetsState, + emptyAssetsState +} from './play-assets/play-assets.js' + +import './play-assets/play-assets-virtual-fs.js' +import './play-assets/play-assets-local-directory.js' +import './play-assets/play-assets-local-archive.js' +import './play-dialog/play-dialog.js' + +declare global { + interface HTMLElementEventMap { + 'assets-filesystem-change': CustomEvent + } + interface HTMLElementTagNameMap { + 'play-assets-dialog': PlayAssetsDialog + } +} + +@customElement('play-assets-dialog') +export class PlayAssetsDialog extends LitElement { + static override readonly styles: CSSResultGroup = css` + ${cssReset} + + legend { + font-weight: bold; + } + + fieldset { + margin-bottom: var(--space); + } + + #localFs { + display: flex; + flex-direction: column; + gap: 8px; + } + ` + + @property({attribute: false}) + assetsState: AssetsState = emptyAssetsState() + + @property({attribute: 'enable-local-assets', type: Boolean}) + enableLocalAssets: boolean = false + + @query('play-dialog', true) + private _dialog!: PlayDialog + + open(): void { + this._dialog.open() + } + + close(): void { + this._dialog.close() + } + + protected override render(): TemplateResult { + return html` + + ${when(this.enableLocalAssets, this.#renderFilesystemPicker)} + +
+ ${this.#filesystemTitle}: + ${choose(this.assetsState.filesystemType, [ + [ + 'virtual', + () => + html`` + ], + ['local', this.#renderLocalFs] + ])} +
+
+ ` + } + + #renderFilesystemPicker = (): TemplateResult => { + return html`
+ Filesystem type: + + +
` + } + + #setFilesystem = ( + ev: InputEvent & {currentTarget: HTMLInputElement} + ): void => { + const filesystemType = ev.currentTarget.value as AssetsFilesystemType + this.dispatchEvent( + Bubble('assets-filesystem-change', { + kind: 'filesystem-type', + filesystemType + }) + ) + } + + #renderLocalFs = (): TemplateResult => { + return html` +
+ ${when( + this.assetsState.hasFileAccessAPI, + () => + html`` + )} + +
+ ` + } + + get #filesystemTitle(): string { + return this.assetsState.filesystemType === 'virtual' + ? 'Manage files' + : 'Filesystem source' + } +} diff --git a/src/elements/play-assets/file-upload-dropper.test.ts b/src/elements/play-assets/file-upload-dropper.test.ts new file mode 100644 index 0000000..d677777 --- /dev/null +++ b/src/elements/play-assets/file-upload-dropper.test.ts @@ -0,0 +1,7 @@ +import {assert} from '@esm-bundle/chai' +import {FileUploadDropper} from './file-upload-dropper' + +test('tag is defined', () => { + const el = document.createElement('file-upload-dropper') + assert.instanceOf(el, FileUploadDropper) +}) diff --git a/src/elements/play-assets/file-upload-dropper.ts b/src/elements/play-assets/file-upload-dropper.ts new file mode 100644 index 0000000..1e4a676 --- /dev/null +++ b/src/elements/play-assets/file-upload-dropper.ts @@ -0,0 +1,280 @@ +import { + css, + html, + LitElement, + type PropertyValues, + type TemplateResult +} from 'lit' +import {customElement, property, state} from 'lit/decorators.js' +import {classMap} from 'lit-html/directives/class-map.js' +import {ifDefined} from 'lit-html/directives/if-defined.js' +import { + fileAccessContext, + type FilePickerType, + flattenAcceptTypes, + hasFileAccessAPI, + tryGetAsFilesystemHandle +} from '../../utils/file-access-api.js' +import {cssReset} from '../../utils/css-reset.js' +import {Bubble} from '../../utils/bubble.js' + +declare global { + interface HTMLElementEventMap { + 'files-selected': CustomEvent + cancelled: CustomEvent + } + interface HTMLElementTagNameMap { + 'file-upload-dropper': FileUploadDropper + } +} + +export type FileSelection = { + // Provided in fallback environments where File Access API is not available + files?: File[] + // Provided in environments where File Access API is available + fileHandles?: FileSystemFileHandle[] +} + +@customElement('file-upload-dropper') +export class FileUploadDropper extends LitElement { + static override readonly styles = css` + ${cssReset} + + input[type='file'] { + display: none; + } + + #fileDrop { + border: 3px dashed black; + border-radius: 8px; + padding: var(--space); + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; + } + + #fileDrop.dragenter { + border-color: blue; + } + + #errorMessage { + color: red; + font-size: 0.9em; + } + ` + + @property({attribute: 'accept-types', type: Array, reflect: true}) + acceptTypes: FilePickerType[] = [] + + @property({attribute: 'multiple', type: Boolean, reflect: true}) + multiple: boolean = false + + @state() + private _dragging: boolean = false + + @state() + private _errorMessage: string | undefined + + @state() + private _accept: string | undefined + + #allowedMimes: string[] = [] + #acceptPatterns: RegExp[] = [] + + protected override update(changedProperties: PropertyValues) { + super.update(changedProperties) + if (changedProperties.has('acceptTypes')) { + this._accept = flattenAcceptTypes(this.acceptTypes) + this.#acceptPatterns = this.#extractWildcardTypes() + } + } + + protected override render(): TemplateResult { + return html` + + + ` + } + + /** + * Uses File Access API to get FileSystemFileHandles + */ + #pickFile = async (): Promise => { + const fileHandles = await fileAccessContext.showOpenFilePicker({ + ...(this.id ? {id: this.id} : {}), + types: this.acceptTypes, + multiple: this.multiple + }) + if (fileHandles.length > 0) { + this.dispatchEvent(Bubble('files-selected', {fileHandles})) + } else { + this.dispatchEvent(Bubble('cancelled', undefined)) + } + } + + /** + * Handles the field as a fallback + */ + #processFileInput = async ( + ev: InputEvent & {currentTarget: HTMLInputElement} + ): Promise => { + if (ev.currentTarget.files) { + await this.#processFileList(ev.currentTarget.files) + } + } + + /** + * Handles the drag'n'drop result + */ + #processDrop = async ( + ev: InputEvent & {currentTarget: HTMLInputElement} + ): Promise => { + ev.preventDefault() + this.#dragEnd() + + if (!ev.dataTransfer || !this.#validateFileList(ev.dataTransfer.files)) { + return + } + + if (hasFileAccessAPI) { + const fileHandles: FileSystemFileHandle[] = [] + for (let index = 0; index < ev.dataTransfer.items.length; index++) { + const item = ev.dataTransfer.items[index] + if (item) { + const handle = await tryGetAsFilesystemHandle(item) + if (handle) { + if (handle.kind === 'file') { + fileHandles.push(handle) + } else { + this._errorMessage = 'Directories cannot be dropped here.' + return + } + } + } + } + this.dispatchEvent(Bubble('files-selected', {fileHandles})) + } else { + await this.#processFileList(ev.dataTransfer.files) + } + } + + #processFileList = async (fileList: FileList): Promise => { + if (!this.#validateFileList(fileList)) { + return + } + + this.#clearError() + + const files: File[] = [] + for (let index = 0; index < fileList.length; index++) { + const file = fileList[index] + if (file) { + files.push(file) + } + } + + this.dispatchEvent(Bubble('files-selected', {files})) + } + + #clearError = (): void => { + this._errorMessage = undefined + } + + #dragStart = (ev: Event): void => { + if (ev.type === 'dragover') { + ev.preventDefault() + } + this.#clearError() + this._dragging = true + } + + #dragEnd = (): void => { + this._dragging = false + } + + #validateFileList(fileList: FileList): boolean { + return ( + this.#validateFileCount(fileList) && this.#validateFileTypes(fileList) + ) + } + + #validateFileCount(fileList: FileList): boolean { + if (fileList.length > 0) { + if (this.multiple) { + return fileList.length >= 1 + } + return fileList.length === 1 + } + return false + } + + #validateFileTypes(fileList: FileList): boolean { + if (this.acceptTypes.length === 0) { + return true + } + for (let index = 0; index < fileList.length; index++) { + const file = fileList[index] + if (file && !this.#mimeAllowed(file.type)) { + this._errorMessage = `Invalid file type provided: ${file.type}` + return false + } + } + return true + } + + #mimeAllowed(mimetype: string): boolean { + if (!(mimetype in this.#allowedMimes)) { + for (let pattern of this.#acceptPatterns) { + if (pattern.test(mimetype)) { + return true + } + } + return false + } + + return true + } + + /** + * Searches for types in the acceptTypes attribute that contain wildcards and + * converts them to regular expressions for matching in {#mimeAllowed} + * + * Example: + * input: application/*zip + * output: application/.*zip + * matches: application/zip, application/x-zip + * @private + */ + #extractWildcardTypes(): RegExp[] { + return this.acceptTypes.flatMap(type => + Object.keys(type.accept).flatMap(mimetype => { + if (mimetype.includes('*')) { + return new RegExp(mimetype.split('*').join('.*')) + } + this.#allowedMimes.push(mimetype) + return [] + }) + ) + } +} diff --git a/src/elements/play-assets/play-assets-local-archive.test.ts b/src/elements/play-assets/play-assets-local-archive.test.ts new file mode 100644 index 0000000..e08db8d --- /dev/null +++ b/src/elements/play-assets/play-assets-local-archive.test.ts @@ -0,0 +1,7 @@ +import {assert} from '@esm-bundle/chai' +import {PlayAssetsLocalArchive} from './play-assets-local-archive' + +test('tag is defined', () => { + const el = document.createElement('play-assets-local-archive') + assert.instanceOf(el, PlayAssetsLocalArchive) +}) diff --git a/src/elements/play-assets/play-assets-local-archive.ts b/src/elements/play-assets/play-assets-local-archive.ts new file mode 100644 index 0000000..8822f4d --- /dev/null +++ b/src/elements/play-assets/play-assets-local-archive.ts @@ -0,0 +1,165 @@ +import {customElement, property, state} from 'lit/decorators.js' +import { + css, + html, + LitElement, + type PropertyValues, + type TemplateResult +} from 'lit' +import {when} from 'lit-html/directives/when.js' +import {cssReset} from '../../utils/css-reset.js' +import {type FilePickerType} from '../../utils/file-access-api.js' +import type {FileSelection} from './file-upload-dropper.js' +import {styleMap} from 'lit/directives/style-map.js' + +import '../play-button.js' +import '../play-icon/play-icon.js' +import './file-upload-dropper.js' +import { + type AssetsFilesystemChange, + type AssetsState, + emptyAssetsState +} from './play-assets.js' +import {Bubble} from '../../utils/bubble.js' + +declare global { + interface HTMLElementEventMap { + 'assets-filesystem-change': CustomEvent + } + interface HTMLElementTagNameMap { + 'play-assets-local-archive': PlayAssetsLocalArchive + } +} + +const STYLE_VISIBLE = {visibility: 'visible'} +const STYLE_COLLAPSED = {visibility: 'collapse'} + +@customElement('play-assets-local-archive') +export class PlayAssetsLocalArchive extends LitElement { + static override readonly styles = css` + ${cssReset} + + :host { + display: grid; + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + :host > * { + grid-column-start: 1; + grid-row-start: 1; + } + + .row { + display: flex; + flex-direction: row; + } + + .column { + display: flex; + flex-direction: column; + } + + .row .grow { + flex-grow: 1; + } + + .row.gap { + gap: 8px; + } + ` + + @property({attribute: false}) + assetsState: AssetsState = emptyAssetsState() + + @state() + private _detailsStyle = STYLE_COLLAPSED + + @state() + private _selectStyle = STYLE_VISIBLE + + protected override willUpdate(changedProperties: PropertyValues) { + if (changedProperties.has('assetsState')) { + this._selectStyle = this.assetsState.archiveFilename + ? STYLE_COLLAPSED + : STYLE_VISIBLE + this._detailsStyle = this.assetsState.archiveFilename + ? STYLE_VISIBLE + : STYLE_COLLAPSED + } + } + + protected override render(): TemplateResult { + return html` ${this.#renderArchiveDetails()} ${this.#renderMountArchive()} ` + } + + #renderMountArchive = (): TemplateResult => { + const types: FilePickerType[] = [ + { + description: 'ZIP Archive', + accept: { + 'application/*zip': ['.zip'] + } + } + ] + + return html` +
+ ZIP archive: + + + Click to select a ZIP archive + or + Drop a ZIP file here + +
+ ` + } + + #renderArchiveDetails = (): TemplateResult => { + return html` +
+
+ Mounted archive: +
${this.assetsState.archiveFilename}
+ File count: ${this.assetsState.count} +
+ ${when( + this.assetsState.hasFileAccessAPI, + () => + html` this.#emitChange({kind: 'remount-archive'})} + >` + )} + this.#emitChange({kind: 'unmount'})} + > +
+ ` + } + + #onFiles = async (ev: CustomEvent): Promise => { + const file = ev.detail.fileHandles?.[0] ?? ev.detail.files?.[0] + if (file) { + this.#emitChange({kind: 'mount-archive', archiveHandle: file}) + } + } + + #emitChange(change: AssetsFilesystemChange): void { + this.dispatchEvent( + Bubble('assets-filesystem-change', change) + ) + } +} diff --git a/src/elements/play-assets/play-assets-local-directory.test.ts b/src/elements/play-assets/play-assets-local-directory.test.ts new file mode 100644 index 0000000..d1609ae --- /dev/null +++ b/src/elements/play-assets/play-assets-local-directory.test.ts @@ -0,0 +1,7 @@ +import {assert} from '@esm-bundle/chai' +import {PlayAssetsLocalDirectory} from './play-assets-local-directory' + +test('tag is defined', () => { + const el = document.createElement('play-assets-local-directory') + assert.instanceOf(el, PlayAssetsLocalDirectory) +}) diff --git a/src/elements/play-assets/play-assets-local-directory.ts b/src/elements/play-assets/play-assets-local-directory.ts new file mode 100644 index 0000000..d6978d1 --- /dev/null +++ b/src/elements/play-assets/play-assets-local-directory.ts @@ -0,0 +1,106 @@ +import {customElement, property} from 'lit/decorators.js' +import {css, html, LitElement, type TemplateResult} from 'lit' +import {cssReset} from '../../utils/css-reset.js' +import {when} from 'lit-html/directives/when.js' +import {fileAccessContext} from '../../utils/file-access-api.js' + +import '../play-button.js' +import { + type AssetsFilesystemChange, + type AssetsState, + emptyAssetsState +} from './play-assets.js' +import {Bubble} from '../../utils/bubble.js' + +declare global { + interface HTMLElementEventMap { + 'assets-filesystem-change': CustomEvent + } + interface HTMLElementTagNameMap { + 'play-assets-local-directory': PlayAssetsLocalDirectory + } +} + +@customElement('play-assets-local-directory') +export class PlayAssetsLocalDirectory extends LitElement { + static override readonly styles = css` + ${cssReset} + + .row { + display: flex; + flex-direction: row; + } + + .row.vcenter { + align-items: center; + } + + .column { + display: flex; + flex-direction: column; + } + + .row .grow, + .column .grow { + flex-grow: 1; + } + + .no-selection { + font-size: 0.9em; + color: darkgrey; + font-style: italic; + } + + pre { + margin: 0; + } + ` + + @property({attribute: false}) + assetsState: AssetsState = emptyAssetsState() + + protected override render(): TemplateResult { + return html` + Local directory: +
+ ${when( + this.assetsState.directoryName, + () => + html`
.../${this.assetsState.directoryName}
+ this.#emitChange({kind: 'unmount'})} + >`, + () => + html`No directory selected` + )} +
+ ` + } + + #pickDirectory = async (): Promise => { + const directoryHandle = await fileAccessContext.showDirectoryPicker({ + id: 'selectDirectory', + mode: 'read' + }) + if (directoryHandle) { + this.#emitChange({kind: 'mount-directory', directoryHandle}) + } + } + + #emitChange(change: AssetsFilesystemChange): void { + this.dispatchEvent( + Bubble('assets-filesystem-change', change) + ) + } +} diff --git a/src/elements/play-assets/play-assets-virtual-fs.test.ts b/src/elements/play-assets/play-assets-virtual-fs.test.ts new file mode 100644 index 0000000..286e3a4 --- /dev/null +++ b/src/elements/play-assets/play-assets-virtual-fs.test.ts @@ -0,0 +1,7 @@ +import {assert} from '@esm-bundle/chai' +import {PlayAssetsVirtualFilesystem} from './play-assets-virtual-fs' + +test('tag is defined', () => { + const el = document.createElement('play-assets-virtual-fs') + assert.instanceOf(el, PlayAssetsVirtualFilesystem) +}) diff --git a/src/elements/play-assets/play-assets-virtual-fs.ts b/src/elements/play-assets/play-assets-virtual-fs.ts new file mode 100644 index 0000000..9cef3a9 --- /dev/null +++ b/src/elements/play-assets/play-assets-virtual-fs.ts @@ -0,0 +1,347 @@ +import {customElement, property, query, state} from 'lit/decorators.js' +import { + css, + type CSSResultGroup, + html, + LitElement, + type PropertyValues, + type TemplateResult +} from 'lit' +import {cssReset} from '../../utils/css-reset.js' +import type {FileSelection} from './file-upload-dropper.js' +import {when} from 'lit-html/directives/when.js' +import {repeat} from 'lit/directives/repeat.js' +import {createRef, type Ref, ref} from 'lit/directives/ref.js' +import { + type AssetsState, + type AssetsVirtualFileChange, + emptyAssetsState +} from './play-assets.js' +import {Bubble} from '../../utils/bubble.js' + +import '../play-button.js' +import '../play-icon/play-icon.js' +import './file-upload-dropper.js' + +declare global { + interface HTMLElementEventMap { + 'assets-virtual-file-change': CustomEvent + } + interface HTMLElementTagNameMap { + 'play-assets-virtual-fs': PlayAssetsVirtualFilesystem + } +} + +@customElement('play-assets-virtual-fs') +export class PlayAssetsVirtualFilesystem extends LitElement { + static override readonly styles: CSSResultGroup = css` + ${cssReset} + + :host { + display: flex; + flex-direction: column; + } + + #fileList { + height: 200px; + margin-top: 8px; + gap: 8px; + overflow-y: auto; + } + + #empty { + font-style: italic; + font-size: 0.9em; + color: gray; + flex-grow: 1; + align-items: center; + justify-content: center; + } + + #removeAll { + margin-top: 8px; + width: 100%; + align-items: center; + justify-content: flex-end; + gap: 8px; + } + + legend { + font-weight: bold; + } + + .asset { + align-items: center; + gap: 8px; + } + + .row { + display: flex; + flex-direction: row; + } + + .column { + display: flex; + flex-direction: column; + } + + .grow { + flex-grow: 1; + } + + .code { + font-family: monospace; + } + ` + + @property({attribute: false}) + assetsState: AssetsState = emptyAssetsState() + + @state() + private _assetNames: string[] = [] + + @state() + private _renameIndex: number = -1 + + @state() + private _deleteIndex: number = -1 + + @state() + private _clearAll: boolean = false + + @query('#renameAsset', false) + private _renameInput: HTMLInputElement | undefined + + #renameInputRef: Ref = createRef() + + protected override willUpdate(changedProperties: PropertyValues): void { + if (changedProperties.has('assetsState')) { + this._assetNames = Object.keys(this.assetsState.map) + this._renameIndex = -1 + this._deleteIndex = -1 + this._clearAll = false + } + } + + protected override update( + changedProperties: PropertyValues<{_renameIndex: number}> + ) { + super.update(changedProperties) + if (changedProperties.has('_renameIndex')) { + if (this._renameIndex >= 0) { + this.#renameInputRef.value?.focus() + } + } + } + + protected override render(): TemplateResult { + return html` + + + Click to select files + or + Drop files here + + ${this.#renderFileList()} +
+ ${when( + this._clearAll, + this.#renderClearAllPrompt, + this.#renderClearAllButton + )} +
+ ` + } + + #renderFileList(): TemplateResult { + return html` +
+ ${when( + this.assetsState.count > 0, + this.#renderFiles, + this.#renderNoFiles + )} +
+ ` + } + + #renderFiles = (): TemplateResult => { + return html` ${repeat(this._assetNames, this.#renderAssetEntry)} ` + } + + #renderNoFiles = (): TemplateResult => { + return html`
No assets added!
` + } + + #renderAssetEntry = (name: string, index: number): TemplateResult => { + if (index === this._renameIndex) { + return this.#renderRenameAssetEntry(name) + } else if (index === this._deleteIndex) { + return this.#renderDeleteAssetEntry(name) + } + return html` +
+ ${name} + (this._renameIndex = index)} + > + (this._deleteIndex = index)} + > +
+ ` + } + + #renderRenameAssetEntry = (name: string): TemplateResult => { + return html` +
+ + + (this._renameIndex = -1)} + > +
+ ` + } + + #renderDeleteAssetEntry = (name: string): TemplateResult => { + return html` +
+ Remove ${name}? + + (this._deleteIndex = -1)} + > +
+ ` + } + + #renderClearAllButton = (): TemplateResult => + html` (this._clearAll = true)} + >` + + #renderClearAllPrompt = (): TemplateResult => html` + Remove all assets? + + (this._clearAll = false)} + > + ` + + #rename = (): void => { + const oldName = this._assetNames[this._renameIndex] + const newName = this._renameInput?.value?.trim() + if (oldName && newName) { + this.#emitChange({ + kind: 'rename', + oldName, + newName + }) + } + } + + #delete = (name: string) => (): void => { + this.#emitChange({ + kind: 'remove', + name + }) + } + + #clearAllAssets = (): void => { + this.#emitChange({kind: 'clear'}) + } + + #onFiles = (ev: CustomEvent): void => { + const files = ev.detail.files ?? ev.detail.fileHandles + if (files) { + for (let index = 0; index < files.length; index++) { + const file = files[index] + if (file) { + this.#emitChange({ + kind: 'add', + fileHandle: file + }) + } + } + } + } + + #renameKeyDown = (ev: KeyboardEvent): void => { + if (ev.key === '/' || ev.key === '\\') { + ev.preventDefault() + ev.stopImmediatePropagation() + } + } + + #renameKeyUp = (ev: KeyboardEvent): void => { + if (ev.key === 'Enter') { + this.#rename() + } + } + + #emitChange(change: AssetsVirtualFileChange): void { + this.dispatchEvent( + Bubble('assets-virtual-file-change', change) + ) + } +} diff --git a/src/elements/play-assets/play-assets.test.ts b/src/elements/play-assets/play-assets.test.ts new file mode 100644 index 0000000..4a42844 --- /dev/null +++ b/src/elements/play-assets/play-assets.test.ts @@ -0,0 +1,7 @@ +import {assert} from '@esm-bundle/chai' +import {PlayAssets} from './play-assets' + +test('tag is defined', () => { + const el = document.createElement('play-assets') + assert.instanceOf(el, PlayAssets) +}) diff --git a/src/elements/play-assets/play-assets.ts b/src/elements/play-assets/play-assets.ts new file mode 100644 index 0000000..c655e7b --- /dev/null +++ b/src/elements/play-assets/play-assets.ts @@ -0,0 +1,394 @@ +import {type PropertyValues, ReactiveElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {WebAccess, WebStorage} from '@zenfs/dom' +import * as ZenFS from '@zenfs/core' +import {type FileSystem, fs, InMemory} from '@zenfs/core' +import * as IDB from 'idb-keyval' +import { + hasFileAccessAPI, + tryGetFile, + tryQueryPermission +} from '../../utils/file-access-api.js' +import {Zip} from '@zenfs/zip' +import {Bubble} from '../../utils/bubble.js' + +declare global { + interface HTMLElementEventMap { + 'assets-updated': CustomEvent + } + interface HTMLElementTagNameMap { + 'play-assets': PlayAssets + } +} + +const CACHE_LAST_DIR = 'lastMountedDirectory' +const CACHE_LAST_ZIP = 'lastMountedArchive' + +const MIME: {readonly [ext: string]: string} = { + html: 'text/html', + htm: 'text/html', + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + png: 'image/png', + gif: 'image/gif' +} +const DEFAULT_MIME = 'application/octet-stream' + +//region Custom types +export type AssetsFilesystemType = 'virtual' | 'local' + +type AssetsFilesystemTypeChange = { + kind: 'filesystem-type' + filesystemType: AssetsFilesystemType +} + +type AssetsFilesystemMountArchiveChange = { + kind: 'mount-archive' + archiveHandle: FileSystemFileHandle | File +} + +type AssetsFilesystemMountDirectoryChange = { + kind: 'mount-directory' + directoryHandle: FileSystemDirectoryHandle +} + +type AssetsFilesystemRemountArchiveChange = { + kind: 'remount-archive' +} + +type AssetsFilesystemUnmountChange = { + kind: 'unmount' +} + +export type AssetsFilesystemChange = + | AssetsFilesystemTypeChange + | AssetsFilesystemMountArchiveChange + | AssetsFilesystemMountDirectoryChange + | AssetsFilesystemRemountArchiveChange + | AssetsFilesystemUnmountChange + +type AssetsVirtualFileAddChange = { + kind: 'add' + fileHandle: FileSystemFileHandle | File +} + +type AssetsVirtualFileRemoveChange = { + kind: 'remove' + name: string +} + +type AssetsVirtualFileRenameChange = { + kind: 'rename' + oldName: string + newName: string +} + +type AssetsVirtualFileClearChange = { + kind: 'clear' +} + +export type AssetsVirtualFileChange = + | AssetsVirtualFileAddChange + | AssetsVirtualFileRemoveChange + | AssetsVirtualFileRenameChange + | AssetsVirtualFileClearChange + +export type AssetsState = { + readonly hasFileAccessAPI: boolean + readonly filesystemType: AssetsFilesystemType + + readonly archiveFilename?: string | undefined + readonly directoryName?: string | undefined + + readonly map: {readonly [path: string]: string} + readonly count: number +} + +type LocalFileHandles = { + directory?: FileSystemDirectoryHandle | undefined + archive?: FileSystemFileHandle | undefined +} +//endregion + +export function emptyAssetsState(): AssetsState { + return { + hasFileAccessAPI, + filesystemType: 'virtual', + map: {}, + count: 0 + } +} + +@customElement('play-assets') +export class PlayAssets extends ReactiveElement { + //region Properties + @property({attribute: 'allow-storage', type: Boolean}) + allowStorage: boolean = false + + @property({attribute: false}) + assetsState: AssetsState = emptyAssetsState() + + @property({attribute: 'filesystem-type', type: String, reflect: true}) + filesystemType: AssetsFilesystemType = 'virtual' + //endregion + + //region Private fields + #localFileHandles: LocalFileHandles = {} + //endregion + + //region ReactiveElement overrides + protected override willUpdate(changedProperties: PropertyValues) { + if (changedProperties.has('filesystemType')) { + void this.#mountAssets() + } + } + + //endregion + + //region Event handlers + async onFilesystemChange(change: AssetsFilesystemChange): Promise { + switch (change.kind) { + case 'filesystem-type': + this.filesystemType = change.filesystemType + await this.#mountAssets() + break + case 'remount-archive': + await this.#remount() + break + case 'mount-archive': + await this.#mountLocalArchive(change.archiveHandle) + break + case 'mount-directory': + await this.#mountLocalDirectory(change.directoryHandle) + break + case 'unmount': + await this.#unmount() + break + } + } + + async onVirtualFileChange(change: AssetsVirtualFileChange): Promise { + if (this.filesystemType !== 'virtual') { + return + } + switch (change.kind) { + case 'add': + await this.#addVirtualFile(change.fileHandle) + break + case 'rename': + await this.#renameVirtualFile(change.oldName, change.newName) + break + case 'remove': + await this.#removeVirtualFile(change.name) + break + case 'clear': + await this.#clearVirtualFiles() + break + default: + return + } + await this.#updateMap() + } + //endregion + + //region Mount management + async #mountAssets(): Promise { + this.#unmountRoot() + if (this.filesystemType === 'virtual') { + await this.#mountVirtualFS() + } else { + await this.#tryMountCachedLocalFS() + } + } + + async #remount(): Promise { + if (this.#localFileHandles.archive) { + await this.#mountLocalArchive(this.#localFileHandles.archive) + } + } + + async #unmount(): Promise { + this.#unmountRoot() + await this.#updateMap() + await this.#cacheUpdate({}) + } + + async #mountLocalArchive( + fileHandle: FileSystemFileHandle | File + ): Promise { + let file + if ('getFile' in fileHandle) { + file = await fileHandle.getFile() + await this.#cacheUpdate({archive: fileHandle}) + } else { + file = fileHandle as File + await this.#cacheClear() + } + await this.#mountRoot(Zip.create({zipData: await file.arrayBuffer()})) + this.#updateState({archiveFilename: fileHandle.name}) + } + + async #mountLocalDirectory( + directoryHandle: FileSystemDirectoryHandle + ): Promise { + await this.#mountRoot(WebAccess.create({handle: directoryHandle})) + await this.#cacheUpdate({directory: directoryHandle}) + this.#updateState({directoryName: directoryHandle.name}) + } + + async #mountRoot(fs: FileSystem): Promise { + this.#unmountRoot() + ZenFS.mount('/', fs) + await this.#updateMap() + } + + #unmountRoot(): void { + if (ZenFS.mounts.has('/')) { + ZenFS.umount('/') + this.#updateState({ + archiveFilename: undefined, + directoryName: undefined, + map: {}, + count: 0 + }) + } + } + + async #mountVirtualFS(): Promise { + if (this.allowStorage) { + await this.#mountRoot(WebStorage.create({})) + } else { + await this.#mountRoot(InMemory.create({})) + } + } + + async #tryMountCachedLocalFS(): Promise { + const [directory, archive] = await this.#cacheLoadHandles() + + if (directory && (await this.#canReadHandle(directory))) { + await this.#mountLocalDirectory(directory) + } else if (archive && (await this.#canReadHandle(archive))) { + await this.#mountLocalArchive(archive) + } else { + await this.#cacheClear() + } + } + //endregion + + //region Virtual filesystem management + async #addVirtualFile(handle: FileSystemFileHandle | File): Promise { + const file = await tryGetFile(handle) + await fs.promises.writeFile( + file.name, + new Uint8Array(await file.arrayBuffer()), + {flush: true} + ) + + await this.#updateMap() + } + + async #renameVirtualFile(oldName: string, newName: string): Promise { + await fs.promises.rename(oldName, newName) + + await this.#updateMap() + } + + async #removeVirtualFile(name: string): Promise { + await fs.promises.unlink(name) + + await this.#updateMap() + } + + async #clearVirtualFiles(): Promise { + await Promise.all( + Object.keys(this.assetsState.map ?? {}).map(name => + fs.promises.unlink(name) + ) + ) + } + //endregion + + //region IndexedDB cache + async #cacheLoadHandles(): Promise< + [FileSystemDirectoryHandle | undefined, FileSystemFileHandle | undefined] + > { + const [dir, zip] = await IDB.getMany([CACHE_LAST_DIR, CACHE_LAST_ZIP]) + return [dir, zip] + } + + async #cacheUpdate(handles: LocalFileHandles): Promise { + this.#localFileHandles = handles + await IDB.setMany([ + [CACHE_LAST_DIR, handles.directory], + [CACHE_LAST_ZIP, handles.archive] + ]) + } + + async #cacheClear(): Promise { + await IDB.delMany([CACHE_LAST_DIR, CACHE_LAST_ZIP]) + } + + async #canReadHandle(handle: FileSystemHandle): Promise { + return (await tryQueryPermission(handle, {mode: 'read'})) === 'granted' + } + //endregion + + //region State + async #updateMap(): Promise { + this.#clearAssetMap() + const assetMap: {[path: string]: string} = {} + let assetCount = 0 + if (ZenFS.mounts.has('/')) { + const entries: string[] = [] + entries.push(...(await fs.promises.readdir('/'))) + while (entries.length > 0) { + const entry = entries.shift()! + // remove the leading / present in some backends + const name = entry.replace(/^\//, '') + const stat = await fs.promises.stat(entry) + if (stat.isDirectory()) { + entries.unshift( + ...(await fs.promises.readdir(entry)).map(child => { + if (child.startsWith('/')) return child + return `${entry}/${child}` + }) + ) + } else { + const file = await fs.promises.open(entry) + const buffer = await file.readFile() + await file.close() + const basename = entry.split('/').at(-1)! + const extension = basename.split('.').at(-1)! + const mimetype = MIME[extension] ?? DEFAULT_MIME + assetMap[name] = window.URL.createObjectURL( + new Blob([buffer], {type: mimetype}) + ) + assetCount++ + } + } + } + + this.#updateState({map: assetMap, count: assetCount}) + } + + #clearAssetMap() { + const assetMap = this.assetsState.map ?? {} + for (const entry of Object.keys(assetMap)) { + const url = assetMap[entry] ?? undefined + if (url) { + URL.revokeObjectURL(url) + } + } + } + + #updateState(updates: Partial): void { + const newState = { + ...this.assetsState, + ...updates, + filesystemType: this.filesystemType + } + + this.dispatchEvent(Bubble('assets-updated', newState)) + } + //endregion +} diff --git a/src/elements/play-button.ts b/src/elements/play-button.ts index 1a99a77..d4156f4 100644 --- a/src/elements/play-button.ts +++ b/src/elements/play-button.ts @@ -10,6 +10,7 @@ import type {PlayIconSVG} from './play-icon/play-icon.js' import {cssReset} from '../utils/css-reset.js' import './play-icon/play-icon.js' +import {ifDefined} from 'lit/directives/if-defined.js' export type PlayButtonAppearance = | 'bordered' @@ -186,6 +187,7 @@ export class PlayButton extends LitElement { @property({type: Boolean}) disabled?: boolean @property() endIcon?: PlayIconSVG @property() icon?: PlayIconSVG + @property({attribute: 'icon-color', type: String}) iconColor?: string @property() size: PlayButtonSize = 'medium' @property({type: String}) label = '' @property({type: Number}) badge = 0 @@ -199,6 +201,7 @@ export class PlayButton extends LitElement { `} @@ -209,6 +212,7 @@ export class PlayButton extends LitElement { `} diff --git a/src/elements/play-dialog/play-dialog.ts b/src/elements/play-dialog/play-dialog.ts new file mode 100644 index 0000000..ccc13c0 --- /dev/null +++ b/src/elements/play-dialog/play-dialog.ts @@ -0,0 +1,124 @@ +import {customElement, property, query} from 'lit/decorators.js' +import { + css, + type CSSResultGroup, + html, + LitElement, + type TemplateResult +} from 'lit' +import {cssReset} from '../../utils/css-reset.js' + +import '../play-button.js' +import {when} from 'lit-html/directives/when.js' + +declare global { + interface HTMLElementEventMap {} + interface HTMLElementTagNameMap { + 'play-dialog': PlayDialog + } +} + +export interface PlayDialogLike { + open(): void + close(): void +} + +@customElement('play-dialog') +export class PlayDialog extends LitElement implements PlayDialogLike { + static override readonly styles: CSSResultGroup = css` + ${cssReset} + + dialog { + color: var(--color-neutral-content); + background-color: var(--color-neutral-background); + box-shadow: var(--shadow-m); + + border-bottom-left-radius: var(--radius); + border-bottom-right-radius: var(--radius); + border-top-left-radius: var(--radius); + border-top-right-radius: var(--radius); + + padding-top: var(--space); + padding-bottom: var(--space); + padding-left: var(--space); + padding-right: var(--space); + + /* No border needed. Dialog background has sufficient contrast against the scrim. */ + border-width: 0; + + /* to-do: breakpoints */ + width: 480px; + max-width: 90vw; + } + + dialog::backdrop { + /* to-do: Update to css variable --color-shade-60 once supported by Chromium and Safari. + https://bugs.webkit.org/show_bug.cgi?id=263834 + https://bugs.chromium.org/p/chromium/issues/detail?id=827397 + https://stackoverflow.com/a/77393321 */ + background-color: rgba(0, 0, 0, 0.6); + } + + header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + row-gap: var(--space); + } + + h1 { + color: var(--color-neutral-content-strong); + + /* No margins needed for composition. */ + margin-top: 0; + margin-bottom: 0; + + /* RPL/Heading Bold/24-HeadingBold */ + font-family: var(--font-family-sans); + font-size: 24px; + font-style: normal; + font-weight: 700; + line-height: 28px; + letter-spacing: 0.2px; + } + ` + + @property({attribute: 'dialog-title', type: String, reflect: true}) + dialogTitle: string = '' + + @property({attribute: 'description', type: String, reflect: true}) + description: string = '' + + @query('dialog') + private _dialog!: HTMLDialogElement + + open(): void { + this._dialog.showModal() + } + + close(): void { + this._dialog.close() + } + + protected override render(): TemplateResult { + return html` + +
+

${this.dialogTitle}

+ +
+ + ${when(this.description, () => html`

${this.description}

`)} + + +
+ ` + } +} diff --git a/src/elements/play-export-dialog.ts b/src/elements/play-export-dialog.ts index d9a1040..2e77111 100644 --- a/src/elements/play-export-dialog.ts +++ b/src/elements/play-export-dialog.ts @@ -1,15 +1,17 @@ import { - LitElement, css, - html, type CSSResultGroup, + html, + LitElement, type TemplateResult } from 'lit' import {customElement, property, query} from 'lit/decorators.js' import type {PlayToast} from './play-toast.js' - +import {PlayDialog} from './play-dialog/play-dialog.js' import {cssReset} from '../utils/css-reset.js' + import './play-button.js' +import './play-dialog/play-dialog.js' import './play-toast.js' declare global { @@ -23,59 +25,8 @@ export class PlayExportDialog extends LitElement { static override readonly styles: CSSResultGroup = css` ${cssReset} - dialog { - color: var(--color-neutral-content); - background-color: var(--color-neutral-background); - box-shadow: var(--shadow-m); - - border-bottom-left-radius: var(--radius); - border-bottom-right-radius: var(--radius); - border-top-left-radius: var(--radius); - border-top-right-radius: var(--radius); - - padding-top: var(--space); - padding-bottom: var(--space); - padding-left: var(--space); - padding-right: var(--space); - - /* No border needed. Dialog background has sufficient contrast against the scrim. */ - border-width: 0; - - /* to-do: breakpoints */ - width: 480px; - max-width: 90vw; - } - - dialog::backdrop { - /* to-do: Update to css variable --color-shade-60 once supported by Chromium and Safari. - https://bugs.webkit.org/show_bug.cgi?id=263834 - https://bugs.chromium.org/p/chromium/issues/detail?id=827397 - https://stackoverflow.com/a/77393321 */ - background-color: rgba(0, 0, 0, 0.6); - } - - header { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - row-gap: var(--space); - } - - h1 { - color: var(--color-neutral-content-strong); - - /* No margins needed for composition. */ - margin-top: 0; - margin-bottom: 0; - - /* RPL/Heading Bold/24-HeadingBold */ - font-family: var(--font-family-sans); - font-size: 24px; - font-style: normal; - font-weight: 700; - line-height: 28px; - letter-spacing: 0.2px; + legend { + font-weight: bold; } ol { @@ -139,11 +90,14 @@ export class PlayExportDialog extends LitElement { ` @property() url: string = '' + @query('play-toast') _toast!: PlayToast - @query('dialog') private _dialog!: HTMLDialogElement + + @query('play-dialog', true) + private _dialog!: PlayDialog open(): void { - this._dialog.showModal() + this._dialog.open() } close(): void { @@ -152,20 +106,10 @@ export class PlayExportDialog extends LitElement { protected override render(): TemplateResult { const cmd = `npx devvit new --template='${this.url}'` - return html` - -
-

Export project

- -
- -

Start a new project from this pen:

+ return html`
  1. Paste the command into a terminal and press enter.
- Copied the command! -
- ` + + Copied the command!` } } diff --git a/src/elements/play-icon/icons/archive-outline.svg b/src/elements/play-icon/icons/archive-outline.svg new file mode 100644 index 0000000..9b2c652 --- /dev/null +++ b/src/elements/play-icon/icons/archive-outline.svg @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/src/elements/play-icon/icons/assets-outline.svg b/src/elements/play-icon/icons/assets-outline.svg new file mode 100644 index 0000000..ecc4b6d --- /dev/null +++ b/src/elements/play-icon/icons/assets-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/elements/play-icon/icons/browse-outline.svg b/src/elements/play-icon/icons/browse-outline.svg new file mode 100644 index 0000000..aced7fc --- /dev/null +++ b/src/elements/play-icon/icons/browse-outline.svg @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/src/elements/play-icon/icons/delete-outline.svg b/src/elements/play-icon/icons/delete-outline.svg new file mode 100644 index 0000000..f21a44b --- /dev/null +++ b/src/elements/play-icon/icons/delete-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/elements/play-icon/icons/rename-outline.svg b/src/elements/play-icon/icons/rename-outline.svg new file mode 100644 index 0000000..be2f874 --- /dev/null +++ b/src/elements/play-icon/icons/rename-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/elements/play-icon/icons/unmount-outline.svg b/src/elements/play-icon/icons/unmount-outline.svg new file mode 100644 index 0000000..2f866ab --- /dev/null +++ b/src/elements/play-icon/icons/unmount-outline.svg @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/src/elements/play-icon/play-icon.ts b/src/elements/play-icon/play-icon.ts index 0dbb025..2b25704 100644 --- a/src/elements/play-icon/play-icon.ts +++ b/src/elements/play-icon/play-icon.ts @@ -12,6 +12,9 @@ import {unsafeHTML} from 'lit/directives/unsafe-html.js' import {cssReset} from '../../utils/css-reset.js' import addOutline from './icons/add-outline.svg' +import archiveOutline from './icons/archive-outline.svg' +import assetsOutline from './icons/assets-outline.svg' +import browseOutline from './icons/browse-outline.svg' import caretDownOutline from './icons/caret-down-outline.svg' import caretUpOutline from './icons/caret-up-outline.svg' import checkmarkFill from './icons/checkmark-fill.svg' @@ -19,21 +22,27 @@ import closeOutline from './icons/close-outline.svg' import communityOutline from './icons/community-outline.svg' import copyClipboardOutline from './icons/copy-clipboard-outline.svg' import dayOutline from './icons/day-outline.svg' +import deleteOutline from './icons/delete-outline.svg' import downloadOutline from './icons/download-outline.svg' import externalOutline from './icons/external-outline.svg' import infoOutline from './icons/info-outline.svg' import nightOutline from './icons/night-outline.svg' import overflowHorizontalOutline from './icons/overflow-horizontal-outline.svg' import readingOutline from './icons/reading-outline.svg' +import renameOutline from './icons/rename-outline.svg' import reportOutline from './icons/report-outline.svg' import resizeHorizontalOutline from './icons/resize-horizontal-outline.svg' import restartOutline from './icons/restart-outline.svg' import shareNewOutline from './icons/share-new-outline.svg' +import unmountOutline from './icons/unmount-outline.svg' export type PlayIconSVG = keyof typeof icons const icons = { 'add-outline': addOutline, + 'archived-outline': archiveOutline, + 'assets-outline': assetsOutline, + 'browse-outline': browseOutline, 'caret-down-outline': caretDownOutline, 'caret-up-outline': caretUpOutline, 'checkmark-fill': checkmarkFill, @@ -41,15 +50,18 @@ const icons = { 'community-outline': communityOutline, 'copy-clipboard-outline': copyClipboardOutline, 'day-outline': dayOutline, + 'delete-outline': deleteOutline, 'download-outline': downloadOutline, 'external-outline': externalOutline, 'info-outline': infoOutline, 'night-outline': nightOutline, 'overflow-horizontal-outline': overflowHorizontalOutline, 'reading-outline': readingOutline, + 'rename-outline': renameOutline, 'report-outline': reportOutline, 'resize-horizontal-outline': resizeHorizontalOutline, 'restart-outline': restartOutline, + 'share-ios-outline': unmountOutline, 'share-new-outline': shareNewOutline } @@ -73,7 +85,8 @@ export class PlayIcon extends LitElement { const style = { width: this.size, height: this.size, - fill: this.color + fill: this.color, + color: this.color } return this.icon ? html` diff --git a/src/elements/play-pen-header.ts b/src/elements/play-pen-header.ts index 4529444..b6ff1b3 100644 --- a/src/elements/play-pen-header.ts +++ b/src/elements/play-pen-header.ts @@ -12,6 +12,7 @@ import {cssReset} from '../utils/css-reset.js' import {openURL} from '../utils/open-url.js' import type {PlayExportDialog} from './play-export-dialog.js' import type {PlaySettingsDialog} from './play-settings-dialog.js' +import type {PlayAssetsDialog} from './play-assets-dialog.js' import './play-button.js' import './play-export-dialog.js' @@ -20,6 +21,8 @@ import './play-logo/play-logo.js' import './play-new-pen-button.js' import './play-resizable-text-input.js' import './play-settings-dialog.js' +import './play-assets-dialog.js' +import {type AssetsState, emptyAssetsState} from './play-assets/play-assets.js' declare global { interface HTMLElementEventMap { @@ -70,27 +73,53 @@ export class PlayPenHeader extends LitElement { } ` - @property({attribute: 'allow-storage', type: Boolean}) allowStorage: boolean = - false - @property() name: string = '' - @property({attribute: false}) srcByLabel?: {readonly [key: string]: string} - @property({attribute: 'remote-runtime-origin'}) remoteRuntimeOrigin: string = - defaultSettings.remoteRuntimeOrigin + @property({attribute: 'allow-storage', type: Boolean}) + allowStorage: boolean = false + + @property({attribute: false}) + assetsState: AssetsState = emptyAssetsState() + + @property({attribute: 'enable-local-assets', type: Boolean}) + enableLocalAssets: boolean = false + + @property() + name: string = '' + + @property({attribute: false}) + srcByLabel?: {readonly [key: string]: string} + + @property({attribute: 'remote-runtime-origin'}) + remoteRuntimeOrigin: string = defaultSettings.remoteRuntimeOrigin + @property({attribute: 'runtime-debug-logging', type: Boolean}) runtimeDebugLogging: boolean = false + @property({attribute: 'sandbox-app', type: Boolean}) sandboxApp: boolean = false - @property() url: string = '' + + @property() + url: string = '' + @property({attribute: 'use-experimental-blocks', type: Boolean}) useExperimentalBlocks: boolean = false + @property({attribute: 'use-ui-request', type: Boolean}) useUIRequest: boolean = false + @property({attribute: 'use-local-runtime', type: Boolean}) useLocalRuntime: boolean = false + @property({attribute: 'use-remote-runtime', type: Boolean}) useRemoteRuntime: boolean = false - @query('play-export-dialog') private _export!: PlayExportDialog - @query('play-settings-dialog') private _settings!: PlaySettingsDialog + + @query('play-assets-dialog') + private _assets!: PlayAssetsDialog + + @query('play-export-dialog') + private _export!: PlayExportDialog + + @query('play-settings-dialog') + private _settings!: PlaySettingsDialog protected override render(): TemplateResult { return html` @@ -126,6 +155,14 @@ export class PlayPenHeader extends LitElement { @click=${() => this._export.open()} > this._assets.open()} + > + + ` } diff --git a/src/elements/play-pen/play-pen.ts b/src/elements/play-pen/play-pen.ts index 0cc17ee..fdec985 100644 --- a/src/elements/play-pen/play-pen.ts +++ b/src/elements/play-pen/play-pen.ts @@ -3,13 +3,13 @@ import {throttle} from '@devvit/shared-types/throttle.js' import type {DevvitUIError} from '@devvit/ui-renderer/client/devvit-custom-post.js' import type {VirtualTypeScriptEnvironment} from '@typescript/vfs' import { - LitElement, css, - html, - unsafeCSS, type CSSResultGroup, + html, + LitElement, type PropertyValues, - type TemplateResult + type TemplateResult, + unsafeCSS } from 'lit' import {customElement, property, query, state} from 'lit/decorators.js' import {ifDefined} from 'lit/directives/if-defined.js' @@ -27,7 +27,7 @@ import polls from '../../examples/polls.example.tsx' import progressBar from '../../examples/progress-bar.example.tsx' import svg from '../../examples/svg.example.tsx' import {BundleStore} from '../../runtime/bundle-store.js' -import {PenSave, loadPen, penToHash, savePen} from '../../storage/pen-save.js' +import {loadPen, PenSave, penToHash, savePen} from '../../storage/pen-save.js' import { defaultSettings, loadSettings, @@ -42,12 +42,21 @@ import type {PlayEditor} from '../play-editor/play-editor.js' import type {PlayToast} from '../play-toast.js' import penVars from './pen-vars.css' +import '../play-assets/play-assets.js' import '../play-editor/play-editor.js' import '../play-pen-footer.js' import '../play-pen-header.js' import '../play-preview-controls.js' import '../play-preview.js' import '../play-toast.js' +import { + type AssetsFilesystemChange, + type AssetsFilesystemType, + type AssetsState, + type AssetsVirtualFileChange, + emptyAssetsState, + PlayAssets +} from '../play-assets/play-assets.js' declare global { interface HTMLElementTagNameMap { @@ -138,9 +147,12 @@ export class PlayPen extends LitElement { } /** Program executable. */ + @state() private _assetsFilesystemType: AssetsFilesystemType = 'virtual' + @state() private _assetsState: AssetsState = emptyAssetsState() @state() private _bundle?: Readonly | undefined /** Execution preview widths. */ @state() private _diagnostics: Diagnostics = {previewErrs: [], tsErrs: []} + @state() private _enableLocalAssets: boolean = false @state() private _openConsole: boolean = false @state() private _previewWidth: number = 288 @state() private _remoteRuntimeOrigin: string = @@ -151,6 +163,7 @@ export class PlayPen extends LitElement { @state() private _useLocalRuntime: boolean = false @state() private _useRemoteRuntime: boolean = false @state() private _useUIRequest: boolean = false + @query('play-assets') private _assets!: PlayAssets @query('play-editor') private _editor!: PlayEditor @query('play-toast') private _toast!: PlayToast #bundleStore?: BundleStore | undefined @@ -174,6 +187,10 @@ export class PlayPen extends LitElement { (this.allowStorage ? loadSettings(localStorage) : undefined) ?? defaultSettings if (settings) { + this._assetsFilesystemType = !settings.enableLocalAssets + ? 'virtual' + : settings.assetsFilesystemType + this._enableLocalAssets = settings.enableLocalAssets this._openConsole = settings.openConsole this._remoteRuntimeOrigin = settings.remoteRuntimeOrigin this._runtimeDebugLogging = settings.runtimeDebugLogging @@ -189,6 +206,7 @@ export class PlayPen extends LitElement { let pen if (this.allowURL) pen = loadPen(location) if (this.allowStorage) pen ??= loadPen(localStorage) + if (!pen) { this.#template = true this.#setSrc(helloBlocks, false) @@ -200,19 +218,31 @@ export class PlayPen extends LitElement { protected override render(): TemplateResult { return html` + ) => { + this._assetsState = ev.detail + // Rebuild the app to access the new assets + this.#setSrc(this._src ?? '', false) + }} + > Copied the URL!) => this.#setName(ev.detail, true)} @edit-src=${(ev: CustomEvent) => { @@ -234,6 +264,20 @@ export class PlayPen extends LitElement { (this._remoteRuntimeOrigin = ev.detail)} @use-ui-request=${(ev: CustomEvent) => (this._useUIRequest = ev.detail)} + @enable-local-assets=${(ev: CustomEvent) => { + this._enableLocalAssets = ev.detail + if (!this._enableLocalAssets) { + void this._assets.onFilesystemChange({ + kind: 'filesystem-type', + filesystemType: 'virtual' + }) + } + }} + @assets-filesystem-change=${(ev: CustomEvent) => + this._assets.onFilesystemChange(ev.detail)} + @assets-virtual-file-change=${( + ev: CustomEvent + ) => this._assets.onVirtualFileChange(ev.detail)} @share=${this.#onShare} >
@@ -306,6 +350,8 @@ export class PlayPen extends LitElement { _useRemoteRuntime: boolean _remoteRuntimeOrigin: string _useUIRequest: boolean + _enableLocalAssets: boolean + _assetsState: string }> ): Promise { super.willUpdate(props) @@ -319,7 +365,9 @@ export class PlayPen extends LitElement { props.has('_remoteRuntimeOrigin') || props.has('_useLocalRuntime') || props.has('_useRemoteRuntime') || - props.has('_useUIRequest')) + props.has('_useUIRequest') || + props.has('_enableLocalAssets') || + props.has('_assetsState')) ) saveSettings(localStorage, { openConsole: this._openConsole, @@ -330,6 +378,8 @@ export class PlayPen extends LitElement { useRemoteRuntime: this._useRemoteRuntime, remoteRuntimeOrigin: this._remoteRuntimeOrigin, useUIRequest: this._useUIRequest, + enableLocalAssets: this._enableLocalAssets, + assetsFilesystemType: this._assetsState.filesystemType, version: 1 }) @@ -389,7 +439,8 @@ export class PlayPen extends LitElement { this.#version++ this._bundle = link( compile(this.#env), - newHostname(this._name, this.#version) + newHostname(this._name, this.#version), + this._assetsState.map ) if (save) this.#save() this.#upload() diff --git a/src/elements/play-settings-dialog.ts b/src/elements/play-settings-dialog.ts index 0608a5e..e70452d 100644 --- a/src/elements/play-settings-dialog.ts +++ b/src/elements/play-settings-dialog.ts @@ -1,16 +1,18 @@ import { - LitElement, css, - html, type CSSResultGroup, + html, + LitElement, type TemplateResult } from 'lit' import {customElement, property, query} from 'lit/decorators.js' import {defaultSettings} from '../storage/settings-save.js' import {Bubble} from '../utils/bubble.js' +import {PlayDialog, type PlayDialogLike} from './play-dialog/play-dialog.js' import {cssReset} from '../utils/css-reset.js' import './play-button.js' +import './play-dialog/play-dialog.js' declare global { interface HTMLElementEventMap { @@ -21,6 +23,7 @@ declare global { 'use-local-runtime': CustomEvent 'use-remote-runtime': CustomEvent 'use-ui-request': CustomEvent + 'enable-local-assets': CustomEvent } interface HTMLElementTagNameMap { 'play-settings-dialog': PlaySettingsDialog @@ -28,63 +31,12 @@ declare global { } @customElement('play-settings-dialog') -export class PlaySettingsDialog extends LitElement { +export class PlaySettingsDialog extends LitElement implements PlayDialogLike { static override readonly styles: CSSResultGroup = css` ${cssReset} - dialog { - color: var(--color-neutral-content); - background-color: var(--color-neutral-background); - box-shadow: var(--shadow-m); - - border-bottom-left-radius: var(--radius); - border-bottom-right-radius: var(--radius); - border-top-left-radius: var(--radius); - border-top-right-radius: var(--radius); - - padding-top: var(--space); - padding-bottom: var(--space); - padding-left: var(--space); - padding-right: var(--space); - - /* No border needed. Dialog background has sufficient contrast against the scrim. */ - border-width: 0; - - /* to-do: breakpoints */ - width: 480px; - max-width: 90vw; - } - - dialog::backdrop { - /* to-do: Update to css variable --color-shade-60 once supported by Chromium and Safari. - https://bugs.webkit.org/show_bug.cgi?id=263834 - https://bugs.chromium.org/p/chromium/issues/detail?id=827397 - https://stackoverflow.com/a/77393321 */ - background-color: rgba(0, 0, 0, 0.6); - } - - header { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - row-gap: var(--space); - } - - h1 { - color: var(--color-neutral-content-strong); - - /* No margins needed for composition. */ - margin-top: 0; - margin-bottom: 0; - - /* RPL/Heading Bold/24-HeadingBold */ - font-family: var(--font-family-sans); - font-size: 24px; - font-style: normal; - font-weight: 700; - line-height: 28px; - letter-spacing: 0.2px; + legend { + font-weight: bold; } input[type='checkbox'] { @@ -102,10 +54,6 @@ export class PlaySettingsDialog extends LitElement { display: block; /* Each control starts a new block. */ margin-bottom: var(--space); } - - legend { - font-weight: bold; - } ` @property({attribute: 'allow-storage', type: Boolean}) allowStorage: boolean = @@ -124,10 +72,14 @@ export class PlaySettingsDialog extends LitElement { useRemoteRuntime: boolean = false @property({attribute: 'use-ui-request', type: Boolean}) useUIRequest: boolean = false - @query('dialog') private _dialog!: HTMLDialogElement + @property({attribute: 'enable-local-assets', type: Boolean}) + enableLocalAssets: boolean = false + + @query('play-dialog', true) + private _dialog!: PlayDialog open(): void { - this._dialog.showModal() + this._dialog.open() } close(): void { @@ -135,23 +87,9 @@ export class PlaySettingsDialog extends LitElement { } protected override render(): TemplateResult { + const description = `Settings are ${this.allowStorage ? 'saved and ' : ''}not shareable.` return html` - -
-

Settings

- -
- -

- Settings are ${this.allowStorage ? 'saved and ' : ''}not shareable. -

- +
Reddit Internal

Runtime settings take effect on subsequent execution.

@@ -252,8 +190,23 @@ export class PlaySettingsDialog extends LitElement { Use UI Request (multithreading). Default: ${onOff(defaultSettings.useUIRequest)}. +
-
+ ` } } diff --git a/src/storage/settings-save.ts b/src/storage/settings-save.ts index 3ef32e2..f1a2931 100644 --- a/src/storage/settings-save.ts +++ b/src/storage/settings-save.ts @@ -1,5 +1,11 @@ +import type {AssetsFilesystemType} from '../elements/play-assets/play-assets.js' + /** Settings state for un/packing to/from LocalStorage. Not shareable. */ export type SettingsSave = { + /** Which file system type to use when loading assets */ + assetsFilesystemType: AssetsFilesystemType + /** Enable access to local file system to load assets */ + enableLocalAssets: boolean /** Most recent console open state. */ openConsole: boolean /** Probably the devenv compute address. Eg, http://localhost:7788. */ @@ -32,6 +38,8 @@ export const defaultSettings: Readonly = { useLocalRuntime: true, useRemoteRuntime: false, useUIRequest: false, + enableLocalAssets: false, + assetsFilesystemType: 'virtual', version: 1 } diff --git a/src/utils/file-access-api.test.ts b/src/utils/file-access-api.test.ts new file mode 100644 index 0000000..5a39938 --- /dev/null +++ b/src/utils/file-access-api.test.ts @@ -0,0 +1,27 @@ +// @vitest-environment jsdom + +import {expect, test} from 'vitest' +import {type FilePickerType, flattenAcceptTypes} from './file-access-api.js' + +test('can convert from File Access API types to flat input types', () => { + const types: FilePickerType[] = [ + { + description: 'Lossless image', + accept: { + 'image/png': ['*.png'], + 'image/bmp': ['*.bmp', '*.dib'] + } + }, + { + description: 'Lossy image', + accept: { + 'image/jpeg': ['*.jpg', '*.jpeg', '*.jpe', '*.jfif'], + 'image/gif': ['*.gif'] + } + } + ] + const flattened = flattenAcceptTypes(types) + expect(flattened).toEqual( + 'image/png,*.png,image/bmp,*.bmp,*.dib,image/jpeg,*.jpg,*.jpeg,*.jpe,*.jfif,image/gif,*.gif' + ) +}) diff --git a/src/utils/file-access-api.ts b/src/utils/file-access-api.ts new file mode 100644 index 0000000..0d9be81 --- /dev/null +++ b/src/utils/file-access-api.ts @@ -0,0 +1,150 @@ +/** + * Provides types for the experimental File System Access API available in Chromium-based browsers + * See: https://wicg.github.io/file-system-access/ + */ + +type WellKnownPath = + | 'desktop' + | 'documents' + | 'downloads' + | 'music' + | 'pictures' + | 'videos' + +type PermissionMode = 'read' | 'readwrite' + +type PermissionState = PermissionStatus['state'] + +export type FilePickerType = { + description?: string + accept: {[mimetype: string]: string[]} +} + +type SharedPickerOptions = { + id?: unknown + startIn?: WellKnownPath | FileSystemHandle +} + +type SharedFilePickerOptions = SharedPickerOptions & { + excludeAcceptAllOption?: boolean + types?: FilePickerType[] +} + +type ShowOpenFilePickerOptions = SharedFilePickerOptions & { + multiple?: boolean +} + +type ShowSaveFilePickerOptions = SharedFilePickerOptions & { + suggestedName?: string +} + +type ShowDirectoryPickerOptions = SharedPickerOptions & { + mode?: PermissionMode +} + +type FileAccessAPI = { + showOpenFilePicker: ( + options?: ShowOpenFilePickerOptions + ) => Promise + + showSaveFilePicker: ( + options?: ShowSaveFilePickerOptions + ) => Promise + + showDirectoryPicker: ( + options?: ShowDirectoryPickerOptions + ) => Promise +} + +type FileAccessDataTransferItem = { + getAsFileSystemHandle: () => Promise< + FileSystemFileHandle | FileSystemDirectoryHandle | null + > +} + +type FileAccessPermissionOptions = { + mode: PermissionMode +} + +type FileHandleWithPermissions = FileSystemHandle & { + queryPermission: ( + options?: FileAccessPermissionOptions + ) => Promise + requestPermission: ( + options?: FileAccessPermissionOptions + ) => Promise +} + +export const fileAccessContext = window as typeof window & FileAccessAPI + +export const hasFileAccessAPI = + globalThis.DataTransferItem && + 'getAsFileSystemHandle' in DataTransferItem.prototype + +/** + * Attempts to acquire a FileSystemHandle from a DataTransferItem if running in a + * browser with File Access API, otherwise returns null + * @param item + */ +export async function tryGetAsFilesystemHandle( + item: DataTransferItem +): ReturnType { + if ('getAsFileSystemHandle' in item) { + return (item as FileAccessDataTransferItem).getAsFileSystemHandle() + } + return Promise.resolve(null) +} + +export async function tryQueryPermission( + handle: FileSystemHandle, + options: FileAccessPermissionOptions +): Promise { + if ('queryPermission' in handle) { + return (handle as FileHandleWithPermissions).queryPermission(options) + } + return 'denied' +} + +export async function tryGetFile( + handle: FileSystemFileHandle | File +): Promise { + if ('getFile' in handle) { + return handle.getFile() + } + return handle as File +} + +/** + * Convert the File Access API style accept object to a flat string suitable for + * @param types Array of FilePickerType objects + */ +export function flattenAcceptTypes( + types: FilePickerType[] | undefined +): string | undefined { + // Input: + // [ + // { + // description: 'Lossless image', + // accept: { + // 'image/png': ['*.png'], + // 'image/bmp': ['*.bmp', '*.dib'], + // }, + // }, + // { + // description: 'Lossy image', + // accept: { + // 'image/jpeg': ['*.jpg', '*.jpeg', '*.jpe', '*.jfif'], + // 'image/gif': ['*.gif'], + // }, + // }, + // ] + // Output: + // 'image/png,*.png,image/bmp,*.bmp,*.dib,image/jpeg,*.jpg,*.jpeg,*.jpe,*.jfif,image/gif,*.gif' + return types + ?.map(type => + Object.entries(type.accept) + .map(([mimetype, extensions]) => `${mimetype},${extensions.join()}`) + .join() + ) + ?.join() +} diff --git a/tools/wds-monkey-patch.js b/tools/wds-monkey-patch.js new file mode 100644 index 0000000..dd24d5e --- /dev/null +++ b/tools/wds-monkey-patch.js @@ -0,0 +1,19 @@ +/** + * This resolves some import errors when testing due to some CJS dependencies + * that are not being properly resolved. The content of these modules are not + * currently being utilized when executing the tests. + * @returns {import('@web/test-runner').TestRunnerPlugin} + */ +export const monkeyPatchCJS = () => ({ + name: 'CJS Import Patcher', + serve(context) { + if (context.path.endsWith('buffer/index.js')) { + return 'export class Buffer {}' + } else if (context.path.endsWith('readable-stream/lib/ours/index.js')) { + return ` + export class Readable {}; + export class Writable {}; + ` + } + } +}) diff --git a/tools/web-test-runner.config.js b/tools/web-test-runner.config.js index 7055207..b256b0a 100644 --- a/tools/web-test-runner.config.js +++ b/tools/web-test-runner.config.js @@ -1,5 +1,6 @@ import {esbuildPlugin} from '@web/dev-server-esbuild' import {esbuildConfig} from './esbuild-config.js' +import {monkeyPatchCJS} from './wds-monkey-patch.js' const base = esbuildConfig('0.0.0', '0.0.1-next-2000-01-01-abcdef123.4') @@ -8,6 +9,7 @@ const config = { files: ['src/elements/**/*.test.ts'], nodeResolve: true, plugins: [ + monkeyPatchCJS(), esbuildPlugin({ banner: ` Symbol.dispose ??= Symbol('Symbol.dispose');