From eb6f71c3a45bb85024d5db19ef83183a430ad16a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 15:57:19 +0900 Subject: [PATCH 01/44] chore: init --- examples/workerd/README.md | 5 +++++ examples/workerd/package.json | 11 +++++++++++ examples/workerd/tsconfig.json | 15 +++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 examples/workerd/README.md create mode 100644 examples/workerd/package.json create mode 100644 examples/workerd/tsconfig.json diff --git a/examples/workerd/README.md b/examples/workerd/README.md new file mode 100644 index 00000000..5f644efc --- /dev/null +++ b/examples/workerd/README.md @@ -0,0 +1,5 @@ +# workerd + +## todo + +- [ ] basic durable objects diff --git a/examples/workerd/package.json b/examples/workerd/package.json new file mode 100644 index 00000000..9949c778 --- /dev/null +++ b/examples/workerd/package.json @@ -0,0 +1,11 @@ +{ + "name": "@hiogawa/vite-environment-examples-workerd", + "private": true, + "type": "module", + "scripts": {}, + "dependencies": {}, + "devDependencies": {}, + "volta": { + "extends": "../../package.json" + } +} diff --git a/examples/workerd/tsconfig.json b/examples/workerd/tsconfig.json new file mode 100644 index 00000000..aa702900 --- /dev/null +++ b/examples/workerd/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@tsconfig/strictest/tsconfig.json", + "include": ["src", "vite.config.ts", "e2e", "playwright.config.ts"], + "compilerOptions": { + "exactOptionalPropertyTypes": false, + "verbatimModuleSyntax": true, + "noEmit": true, + "moduleResolution": "Bundler", + "module": "ESNext", + "target": "ESNext", + "lib": ["ESNext", "DOM"], + "types": ["vite/client"], + "jsx": "react-jsx" + } +} From 3ddd96d8e5ad9a2212015ed13cba610aa070310d Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 15:58:41 +0900 Subject: [PATCH 02/44] chore: lockfile --- pnpm-lock.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd767a26..49024c39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,6 +96,8 @@ importers: specifier: 18.2.22 version: 18.2.22 + examples/workerd: {} + packages: /@ampproject/remapping@2.3.0: From be62ebeb114695007f4f00de0600b0311d184f45 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 16:04:32 +0900 Subject: [PATCH 03/44] chore: setup script --- examples/workerd/package.json | 9 +- examples/workerd/src/index.ts | 25 ++++ examples/workerd/src/worker.mjs | 5 + pnpm-lock.yaml | 203 +++++++++++++++++++++++++++++++- 4 files changed, 235 insertions(+), 7 deletions(-) create mode 100644 examples/workerd/src/index.ts create mode 100644 examples/workerd/src/worker.mjs diff --git a/examples/workerd/package.json b/examples/workerd/package.json index 9949c778..8707df22 100644 --- a/examples/workerd/package.json +++ b/examples/workerd/package.json @@ -2,9 +2,12 @@ "name": "@hiogawa/vite-environment-examples-workerd", "private": true, "type": "module", - "scripts": {}, - "dependencies": {}, - "devDependencies": {}, + "scripts": { + "dev": "tsx src/index.ts" + }, + "devDependencies": { + "miniflare": "^3.20240404.0" + }, "volta": { "extends": "../../package.json" } diff --git a/examples/workerd/src/index.ts b/examples/workerd/src/index.ts new file mode 100644 index 00000000..1f59749a --- /dev/null +++ b/examples/workerd/src/index.ts @@ -0,0 +1,25 @@ +import { Miniflare } from "miniflare"; +import { fileURLToPath } from "url"; + +// npx tsx examples/workerd/src/index.ts + +async function main() { + const mf = new Miniflare({ + modulesRoot: "/", + modules: [ + { + type: "ESModule", + path: fileURLToPath(new URL("./worker.mjs", import.meta.url)), + }, + ], + }); + const res = await mf.dispatchFetch("https://test.local/hello"); + console.log("[node] response", { + status: res.status, + headers: Object.fromEntries(res.headers), + text: await res.text(), + }); + await mf.dispose(); +} + +main(); diff --git a/examples/workerd/src/worker.mjs b/examples/workerd/src/worker.mjs new file mode 100644 index 00000000..3fdd2e83 --- /dev/null +++ b/examples/workerd/src/worker.mjs @@ -0,0 +1,5 @@ +export default { + fetch: () => { + return new Response("yay"); + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 49024c39..2e535a42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,7 +96,11 @@ importers: specifier: 18.2.22 version: 18.2.22 - examples/workerd: {} + examples/workerd: + devDependencies: + miniflare: + specifier: ^3.20240404.0 + version: 3.20240404.0 packages: @@ -325,6 +329,58 @@ packages: to-fast-properties: 2.0.0 dev: true + /@cloudflare/workerd-darwin-64@1.20240404.0: + resolution: {integrity: sha512-rc/ov3I9GwgKRtUnkShNW3TIoZEPHzExrMRNlHq1VpXQRBSchHdMw8meMn54+oqgxW1AKLmPWj/c0A7EnYAsIw==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/workerd-darwin-arm64@1.20240404.0: + resolution: {integrity: sha512-V9oPjeC2PYBCoAYnjbO2bsjT7PtzxfUHnh780FUi1r59Hwxd7FNlojwsIidA0nS/1WV5UKeJusIdrYlQbsketA==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/workerd-linux-64@1.20240404.0: + resolution: {integrity: sha512-ndO7q6G2X8uYd5byGFzow4SWPqINQcmJ7pKKATNa+9vh/YMO0of2ihPntnm9uv577l8nRiAwRkHbnsWoEI33qQ==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/workerd-linux-arm64@1.20240404.0: + resolution: {integrity: sha512-hto5pjKYFqFu2Rvxnh84TzgDwalBRXQSwOVHltcgqo48en9TJDCN48ZtLj2G7QTGUOMW88h+nqdbj8+P7S/GXQ==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/workerd-windows-64@1.20240404.0: + resolution: {integrity: sha512-DpCLvNkOeFinKGJwv9qbyT7RLZ1168dhPx85IHSqAYVWZIszNSmNOkEDqklvoJoab01AqETrrEhwBdmjCA7qfA==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@esbuild/aix-ppc64@0.19.12: resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} engines: {node: '>=12'} @@ -716,6 +772,11 @@ packages: requiresBuild: true optional: true + /@fastify/busboy@2.1.1: + resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} + engines: {node: '>=14'} + dev: true + /@hattip/adapter-node@0.0.44: resolution: {integrity: sha512-Qf0kcrE4yHFrmKgfntrxHGNkk0/9a45aKmAO3ex6OiRKat5DZCFDdFMHV85Z594IohzT7Q+WcWiHQl/b0Jvo7Q==} dependencies: @@ -787,6 +848,13 @@ packages: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /@kamilkisiela/fast-url-parser@1.1.4: resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==} dev: true @@ -1148,11 +1216,15 @@ packages: acorn: 8.11.3 dev: false + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + dev: true + /acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} hasBin: true - dev: false /ajv-keywords@3.5.2(ajv@6.12.6): resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} @@ -1178,6 +1250,12 @@ packages: color-convert: 1.9.3 dev: true + /as-table@1.0.55: + resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} + dependencies: + printable-characters: 1.0.42 + dev: true + /browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1207,6 +1285,15 @@ packages: /caniuse-lite@1.0.30001600: resolution: {integrity: sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==} + /capnp-ts@0.7.0: + resolution: {integrity: sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==} + dependencies: + debug: 4.3.4 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1239,10 +1326,19 @@ packages: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: true + /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} dev: true + /data-uri-to-buffer@2.0.2: + resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} + dev: true + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -1370,6 +1466,11 @@ packages: engines: {node: '>=0.8.x'} dev: false + /exit-hook@2.2.1: + resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} + engines: {node: '>=6'} + dev: true + /fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} dev: true @@ -1409,6 +1510,13 @@ packages: engines: {node: '>=6.9.0'} dev: true + /get-source@2.0.12: + resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} + dependencies: + data-uri-to-buffer: 2.0.2 + source-map: 0.6.1 + dev: true + /get-tsconfig@4.7.3: resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} dependencies: @@ -1417,7 +1525,6 @@ packages: /glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - dev: false /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} @@ -1502,10 +1609,38 @@ packages: dependencies: mime-db: 1.52.0 + /miniflare@3.20240404.0: + resolution: {integrity: sha512-+FOTcztPMW3akmucX4vE0PWMNvP4JBwl4s9ieA84fcOaDtTbtfU1rHXpcacj16klpUpvSnD6xd8Sjsn6SJXPfg==} + engines: {node: '>=16.13'} + hasBin: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + acorn: 8.11.3 + acorn-walk: 8.3.2 + capnp-ts: 0.7.0 + exit-hook: 2.2.1 + glob-to-regexp: 0.4.1 + stoppable: 1.1.0 + undici: 5.28.4 + workerd: 1.20240404.0 + ws: 8.16.0 + youch: 3.3.3 + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true + /mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + dev: true + /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1557,6 +1692,10 @@ packages: hasBin: true dev: true + /printable-characters@1.0.42: + resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} + dev: true + /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1678,7 +1817,18 @@ packages: /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - dev: false + + /stacktracey@2.1.8: + resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==} + dependencies: + as-table: 1.0.55 + get-source: 2.0.12 + dev: true + + /stoppable@1.1.0: + resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} + engines: {node: '>=4', npm: '>=6'} + dev: true /streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} @@ -1769,6 +1919,13 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + /undici@5.28.4: + resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} + engines: {node: '>=14.0'} + dependencies: + '@fastify/busboy': 2.1.1 + dev: true + /update-browserslist-db@1.0.13(browserslist@4.23.0): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true @@ -1878,6 +2035,44 @@ packages: - uglify-js dev: false + /workerd@1.20240404.0: + resolution: {integrity: sha512-U4tfnvBcPMsv7pmRGuF0J5UnoZi6tbc64tXNfyijI74r6w6Vlb2+a6eibdQL8g0g46+4vjuTKME9G5RvSvdc8g==} + engines: {node: '>=16'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20240404.0 + '@cloudflare/workerd-darwin-arm64': 1.20240404.0 + '@cloudflare/workerd-linux-64': 1.20240404.0 + '@cloudflare/workerd-linux-arm64': 1.20240404.0 + '@cloudflare/workerd-windows-64': 1.20240404.0 + dev: true + + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true + + /youch@3.3.3: + resolution: {integrity: sha512-qSFXUk3UZBLfggAW3dJKg0BMblG5biqSF8M34E06o5CSsZtH92u9Hqmj2RzGiHDi64fhe83+4tENFP2DB6t6ZA==} + dependencies: + cookie: 0.5.0 + mustache: 4.2.0 + stacktracey: 2.1.8 + dev: true + + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: true From 28bf1ce06bdaca9860c9d916d8c1ef15ff08260b Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 16:20:31 +0900 Subject: [PATCH 04/44] wip --- examples/workerd/README.md | 7 ++++++ examples/workerd/package.json | 4 +-- .../src/{index.ts => example1/main.ts} | 2 +- .../workerd/src/{ => example1}/worker.mjs | 0 examples/workerd/src/example2/main.ts | 25 +++++++++++++++++++ examples/workerd/src/example2/worker.mjs | 5 ++++ 6 files changed, 39 insertions(+), 4 deletions(-) rename examples/workerd/src/{index.ts => example1/main.ts} (91%) rename examples/workerd/src/{ => example1}/worker.mjs (100%) create mode 100644 examples/workerd/src/example2/main.ts create mode 100644 examples/workerd/src/example2/worker.mjs diff --git a/examples/workerd/README.md b/examples/workerd/README.md index 5f644efc..cf329d01 100644 --- a/examples/workerd/README.md +++ b/examples/workerd/README.md @@ -3,3 +3,10 @@ ## todo - [ ] basic durable objects +- [ ] integrate vite + +## references + +- https://github.com/cloudflare/workers-sdk/blob/d994066f255f6851759a055eac3b52a4aa4b83c3/packages/vitest-pool-workers/src/worker/index.ts#L174 +- https://github.com/cloudflare/workers-sdk/blob/2789f26a87c769fc6177b0bdc79a839a15f4ced1/packages/vitest-pool-workers/src/pool/index.ts#L630 +- https://github.com/cloudflare/workers-sdk/pull/5530 diff --git a/examples/workerd/package.json b/examples/workerd/package.json index 8707df22..c3968812 100644 --- a/examples/workerd/package.json +++ b/examples/workerd/package.json @@ -2,9 +2,7 @@ "name": "@hiogawa/vite-environment-examples-workerd", "private": true, "type": "module", - "scripts": { - "dev": "tsx src/index.ts" - }, + "scripts": {}, "devDependencies": { "miniflare": "^3.20240404.0" }, diff --git a/examples/workerd/src/index.ts b/examples/workerd/src/example1/main.ts similarity index 91% rename from examples/workerd/src/index.ts rename to examples/workerd/src/example1/main.ts index 1f59749a..afba95c1 100644 --- a/examples/workerd/src/index.ts +++ b/examples/workerd/src/example1/main.ts @@ -1,7 +1,7 @@ import { Miniflare } from "miniflare"; import { fileURLToPath } from "url"; -// npx tsx examples/workerd/src/index.ts +// npx tsx examples/workerd/src/example1/main.ts async function main() { const mf = new Miniflare({ diff --git a/examples/workerd/src/worker.mjs b/examples/workerd/src/example1/worker.mjs similarity index 100% rename from examples/workerd/src/worker.mjs rename to examples/workerd/src/example1/worker.mjs diff --git a/examples/workerd/src/example2/main.ts b/examples/workerd/src/example2/main.ts new file mode 100644 index 00000000..4ae328f6 --- /dev/null +++ b/examples/workerd/src/example2/main.ts @@ -0,0 +1,25 @@ +import { Miniflare } from "miniflare"; +import { fileURLToPath } from "url"; + +// npx tsx examples/workerd/src/example2/main.ts + +async function main() { + const mf = new Miniflare({ + modulesRoot: "/", + modules: [ + { + type: "ESModule", + path: fileURLToPath(new URL("./worker.mjs", import.meta.url)), + }, + ], + }); + const res = await mf.dispatchFetch("https://test.local/hello"); + console.log("[node] response", { + status: res.status, + headers: Object.fromEntries(res.headers), + text: await res.text(), + }); + await mf.dispose(); +} + +main(); diff --git a/examples/workerd/src/example2/worker.mjs b/examples/workerd/src/example2/worker.mjs new file mode 100644 index 00000000..3fdd2e83 --- /dev/null +++ b/examples/workerd/src/example2/worker.mjs @@ -0,0 +1,5 @@ +export default { + fetch: () => { + return new Response("yay"); + }, +}; From 3183a9f1aaca7e8c122c12fa57fae02f0c38f2f6 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 17:15:54 +0900 Subject: [PATCH 05/44] chore: durable objects websocket example --- examples/workerd/README.md | 2 ++ examples/workerd/package.json | 1 + examples/workerd/src/example2/main.ts | 17 ++++++++-- examples/workerd/src/example2/worker.mjs | 15 ++++++--- examples/workerd/src/example3/main.ts | 42 ++++++++++++++++++++++++ examples/workerd/src/example3/worker.mjs | 17 ++++++++++ examples/workerd/tsconfig.json | 4 +-- pnpm-lock.yaml | 7 ++++ 8 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 examples/workerd/src/example3/main.ts create mode 100644 examples/workerd/src/example3/worker.mjs diff --git a/examples/workerd/README.md b/examples/workerd/README.md index cf329d01..9bb3163b 100644 --- a/examples/workerd/README.md +++ b/examples/workerd/README.md @@ -7,6 +7,8 @@ ## references +- https://github.com/cloudflare/workers-sdk/blame/2789f26a87c769fc6177b0bdc79a839a15f4ced1/packages/miniflare/test/plugins/do/index.spec.ts - https://github.com/cloudflare/workers-sdk/blob/d994066f255f6851759a055eac3b52a4aa4b83c3/packages/vitest-pool-workers/src/worker/index.ts#L174 - https://github.com/cloudflare/workers-sdk/blob/2789f26a87c769fc6177b0bdc79a839a15f4ced1/packages/vitest-pool-workers/src/pool/index.ts#L630 - https://github.com/cloudflare/workers-sdk/pull/5530 +- https://github.com/remix-run/remix/pull/8834 diff --git a/examples/workerd/package.json b/examples/workerd/package.json index c3968812..e0feef1a 100644 --- a/examples/workerd/package.json +++ b/examples/workerd/package.json @@ -4,6 +4,7 @@ "type": "module", "scripts": {}, "devDependencies": { + "@cloudflare/workers-types": "^4.20240405.0", "miniflare": "^3.20240404.0" }, "volta": { diff --git a/examples/workerd/src/example2/main.ts b/examples/workerd/src/example2/main.ts index 4ae328f6..22e17b37 100644 --- a/examples/workerd/src/example2/main.ts +++ b/examples/workerd/src/example2/main.ts @@ -3,6 +3,8 @@ import { fileURLToPath } from "url"; // npx tsx examples/workerd/src/example2/main.ts +const DO_NAME = "__do"; + async function main() { const mf = new Miniflare({ modulesRoot: "/", @@ -12,9 +14,20 @@ async function main() { path: fileURLToPath(new URL("./worker.mjs", import.meta.url)), }, ], + durableObjects: { + [DO_NAME]: { + className: "MyDurableObject", + // need this dark magic? + // unsafeUniqueKey: kUnsafeEphemeralUniqueKey, + // unsafePreventEviction: true, + }, + }, }); - const res = await mf.dispatchFetch("https://test.local/hello"); - console.log("[node] response", { + + const ns = await mf.getDurableObjectNamespace(DO_NAME); + const stub = ns.get(ns.idFromName("")); + const res = await stub.fetch("http://test.local/hello"); + console.log("[response]", { status: res.status, headers: Object.fromEntries(res.headers), text: await res.text(), diff --git a/examples/workerd/src/example2/worker.mjs b/examples/workerd/src/example2/worker.mjs index 3fdd2e83..8d8012c7 100644 --- a/examples/workerd/src/example2/worker.mjs +++ b/examples/workerd/src/example2/worker.mjs @@ -1,5 +1,10 @@ -export default { - fetch: () => { - return new Response("yay"); - }, -}; +export class MyDurableObject { + /** + * + * @param {Request} req + * @returns + */ + fetch(req) { + return new Response("yaayy: " + req.url); + } +} diff --git a/examples/workerd/src/example3/main.ts b/examples/workerd/src/example3/main.ts new file mode 100644 index 00000000..496ec7cd --- /dev/null +++ b/examples/workerd/src/example3/main.ts @@ -0,0 +1,42 @@ +import { Miniflare } from "miniflare"; +import { fileURLToPath } from "url"; +import { tinyassert, createManualPromise } from "@hiogawa/utils"; + +// npx tsx examples/workerd/src/example3/main.ts + +const DO_NAME = "__do"; + +async function main() { + const mf = new Miniflare({ + modulesRoot: "/", + modules: [ + { + type: "ESModule", + path: fileURLToPath(new URL("./worker.mjs", import.meta.url)), + }, + ], + durableObjects: { + [DO_NAME]: "MyDurableObject", + }, + }); + + const ns = await mf.getDurableObjectNamespace(DO_NAME); + const stub = ns.get(ns.idFromName("")); + const res = await stub.fetch("http://test.local/hello", { + headers: { + Upgrade: "websocket", + }, + }); + tinyassert(res.webSocket); + const promise = createManualPromise(); + res.webSocket.addEventListener("message", (data) => { + promise.resolve(data); + }); + res.webSocket.accept(); + res.webSocket.send("hello"); + const data = await promise; + console.log(data); + await mf.dispose(); +} + +main(); diff --git a/examples/workerd/src/example3/worker.mjs b/examples/workerd/src/example3/worker.mjs new file mode 100644 index 00000000..100149a7 --- /dev/null +++ b/examples/workerd/src/example3/worker.mjs @@ -0,0 +1,17 @@ +// @ts-nocheck + +export class MyDurableObject { + /** + * + * @param {Request} req + * @returns + */ + fetch(req) { + const [webSocket1, webSocket2] = Object.values(new WebSocketPair()); + webSocket1.accept(); + webSocket1.addEventListener("message", (event) => { + webSocket1.send("echo:" + event.data); + }); + return new Response(null, { status: 101, webSocket: webSocket2 }); + } +} diff --git a/examples/workerd/tsconfig.json b/examples/workerd/tsconfig.json index aa702900..d07881cb 100644 --- a/examples/workerd/tsconfig.json +++ b/examples/workerd/tsconfig.json @@ -8,8 +8,8 @@ "moduleResolution": "Bundler", "module": "ESNext", "target": "ESNext", - "lib": ["ESNext", "DOM"], - "types": ["vite/client"], + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "types": ["vite/client", "@cloudflare/workers-types/experimental"], "jsx": "react-jsx" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e535a42..dba052b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: examples/workerd: devDependencies: + '@cloudflare/workers-types': + specifier: ^4.20240405.0 + version: 4.20240405.0 miniflare: specifier: ^3.20240404.0 version: 3.20240404.0 @@ -374,6 +377,10 @@ packages: dev: true optional: true + /@cloudflare/workers-types@4.20240405.0: + resolution: {integrity: sha512-sEVOhyOgXUwfLkgHqbLZa/sfkSYrh7/zLmI6EZNibPaVPvAnAcItbNNl3SAlLyLKuwf8m4wAIAgu9meKWCvXjg==} + dev: true + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} From e099974a858092837d1204de91d30684be5a25e2 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 17:30:15 +0900 Subject: [PATCH 06/44] chore: setup tsup --- examples/workerd/package.json | 4 +- examples/workerd/src/example4/main.ts | 43 ++ examples/workerd/src/example4/worker.ts | 13 + examples/workerd/tsup.config.ts | 11 + package.json | 1 + pnpm-lock.yaml | 678 +++++++++++++++++++++++- 6 files changed, 747 insertions(+), 3 deletions(-) create mode 100644 examples/workerd/src/example4/main.ts create mode 100644 examples/workerd/src/example4/worker.ts create mode 100644 examples/workerd/tsup.config.ts diff --git a/examples/workerd/package.json b/examples/workerd/package.json index e0feef1a..5d0844e0 100644 --- a/examples/workerd/package.json +++ b/examples/workerd/package.json @@ -2,7 +2,9 @@ "name": "@hiogawa/vite-environment-examples-workerd", "private": true, "type": "module", - "scripts": {}, + "scripts": { + "dev": "tsup --watch" + }, "devDependencies": { "@cloudflare/workers-types": "^4.20240405.0", "miniflare": "^3.20240404.0" diff --git a/examples/workerd/src/example4/main.ts b/examples/workerd/src/example4/main.ts new file mode 100644 index 00000000..9bc081b0 --- /dev/null +++ b/examples/workerd/src/example4/main.ts @@ -0,0 +1,43 @@ +import { Miniflare } from "miniflare"; +import { fileURLToPath } from "url"; +import { tinyassert, createManualPromise } from "@hiogawa/utils"; + +// pnpm -C examples/workerd dev +// npx tsx examples/workerd/src/example4/main.ts + +const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; + +async function main() { + const mf = new Miniflare({ + modulesRoot: "/", + modules: [ + { + type: "ESModule", + path: fileURLToPath(new URL("./dist/worker.js", import.meta.url)), + }, + ], + durableObjects: { + [RUNNER_OBJECT_BINDING]: "RunnerObject", + }, + }); + + const ns = await mf.getDurableObjectNamespace(RUNNER_OBJECT_BINDING); + const stub = ns.get(ns.idFromName("")); + const res = await stub.fetch("http://test.local/hello", { + headers: { + Upgrade: "websocket", + }, + }); + tinyassert(res.webSocket); + const promise = createManualPromise(); + res.webSocket.addEventListener("message", (data) => { + promise.resolve(data); + }); + res.webSocket.accept(); + res.webSocket.send("hello"); + const data = await promise; + console.log(data); + await mf.dispose(); +} + +main(); diff --git a/examples/workerd/src/example4/worker.ts b/examples/workerd/src/example4/worker.ts new file mode 100644 index 00000000..c7c0433b --- /dev/null +++ b/examples/workerd/src/example4/worker.ts @@ -0,0 +1,13 @@ +export class RunnerObject implements DurableObject { + fetch(req: Request) { + const url = new URL(req.url); + if (url.pathname === "___viteInit") { + } + const { 0: webSocket1, 1: webSocket2 } = new WebSocketPair(); + (webSocket1 as any).accept(); + webSocket1.addEventListener("message", (event) => { + webSocket1.send("echo:" + event.data); + }); + return new Response(null, { status: 101, webSocket: webSocket2 }); + } +} diff --git a/examples/workerd/tsup.config.ts b/examples/workerd/tsup.config.ts new file mode 100644 index 00000000..d60396f9 --- /dev/null +++ b/examples/workerd/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "tsup"; + +export default [ + defineConfig({ + outDir: "src/example4/dist", + entry: ["src/example4/worker.ts"], + format: ["esm"], + platform: "browser", + noExternal: [/.*/], + }), +]; diff --git a/package.json b/package.json index 5fc35dbf..20163635 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@vitejs/plugin-react": "^4.2.1", "esbuild": "^0.20.2", "prettier": "^3.2.5", + "tsup": "^8.0.2", "tsx": "^4.7.1", "typescript": "^5.4.3", "vite": "6.0.0-alpha.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dba052b5..6b86c774 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: prettier: specifier: ^3.2.5 version: 3.2.5 + tsup: + specifier: ^8.0.2 + version: 8.0.2(typescript@5.4.3) tsx: specifier: ^4.7.1 version: 4.7.1 @@ -823,6 +826,18 @@ packages: resolution: {integrity: sha512-L8g6kQuPKJXKoqF1XuFYXXHQ3Z4VXuEX8tlOz/gPO6uP2VQ95JUyONsr93RpVTLc/GGblXY30/YK4r56kJzzgQ==} dev: true + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true + /@jridgewell/gen-mapping@0.3.5: resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -866,6 +881,34 @@ packages: resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==} dev: true + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + dev: true + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true + optional: true + /@playwright/test@1.42.1: resolution: {integrity: sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==} engines: {node: '>=16'} @@ -1250,6 +1293,16 @@ packages: uri-js: 4.4.1 dev: false + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -1257,12 +1310,63 @@ packages: color-convert: 1.9.3 dev: true + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + /as-table@1.0.55: resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} dependencies: printable-characters: 1.0.42 dev: true + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + /browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1277,6 +1381,16 @@ packages: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: false + /bundle-require@4.0.2(esbuild@0.19.12): + resolution: {integrity: sha512-jwzPOChofl67PSTW2SGubV9HBQAhhR2i6nskiOThauo9dzwDUgOWQScFVaJkjEfYX+UXiD+LEx8EblQMc2wIag==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.17' + dependencies: + esbuild: 0.19.12 + load-tsconfig: 0.2.5 + dev: true + /busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -1310,6 +1424,21 @@ packages: supports-color: 5.5.0 dev: true + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} engines: {node: '>=6.0'} @@ -1321,14 +1450,30 @@ packages: color-name: 1.1.3 dev: true + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} dev: true + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: false + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true @@ -1338,6 +1483,15 @@ packages: engines: {node: '>= 0.6'} dev: true + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} dev: true @@ -1358,9 +1512,28 @@ packages: ms: 2.1.2 dev: true + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + /electron-to-chromium@1.4.717: resolution: {integrity: sha512-6Fmg8QkkumNOwuZ/5mIbMU9WI3H2fmn5ajcVya64I5Yr5CcNmO7vcLt0Y7c96DCiMO5/9G+4sI2r6eEvdg1F7A==} + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + /enhanced-resolve@5.16.0: resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==} engines: {node: '>=10.13.0'} @@ -1473,6 +1646,21 @@ packages: engines: {node: '>=0.8.x'} dev: false + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + /exit-hook@2.2.1: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} @@ -1486,6 +1674,17 @@ packages: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: false + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} dev: false @@ -1496,6 +1695,27 @@ packages: fast-decode-uri-component: 1.0.1 dev: true + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + dependencies: + reusify: 1.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: true + /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1524,20 +1744,56 @@ packages: source-map: 0.6.1 dev: true + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + /get-tsconfig@4.7.3: resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} dependencies: resolve-pkg-maps: 1.0.0 dev: true + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + /glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + /glob@10.3.12: + resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.4 + minipass: 7.0.4 + path-scurry: 1.10.2 + dev: true + /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} dev: true + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: false @@ -1552,6 +1808,63 @@ packages: engines: {node: '>=8'} dev: false + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.3.0 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + /jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -1561,6 +1874,11 @@ packages: supports-color: 8.1.1 dev: false + /joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + dev: true + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1584,11 +1902,29 @@ packages: hasBin: true dev: true + /lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} + engines: {node: '>=14'} + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} dev: false + /lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + dev: true + /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -1596,6 +1932,11 @@ packages: js-tokens: 4.0.0 dev: false + /lru-cache@10.2.0: + resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + engines: {node: 14 || >=16.14} + dev: true + /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: @@ -1604,7 +1945,19 @@ packages: /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: false + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} @@ -1616,6 +1969,11 @@ packages: dependencies: mime-db: 1.52.0 + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + /miniflare@3.20240404.0: resolution: {integrity: sha512-+FOTcztPMW3akmucX4vE0PWMNvP4JBwl4s9ieA84fcOaDtTbtfU1rHXpcacj16klpUpvSnD6xd8Sjsn6SJXPfg==} engines: {node: '>=16.13'} @@ -1639,6 +1997,18 @@ packages: - utf-8-validate dev: true + /minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true @@ -1648,6 +2018,14 @@ packages: hasBin: true dev: true + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true + /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1665,9 +2043,61 @@ packages: /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-scurry@1.10.2: + resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.2.0 + minipass: 7.0.4 + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + /playwright-core@1.42.1: resolution: {integrity: sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==} engines: {node: '>=16'} @@ -1684,6 +2114,22 @@ packages: fsevents: 2.3.2 dev: true + /postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 3.1.1 + yaml: 2.4.1 + dev: true + /postcss@8.4.38: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} @@ -1706,7 +2152,10 @@ packages: /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - dev: false + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true /randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -1752,10 +2201,27 @@ packages: loose-envify: 1.4.0 dev: false + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + /resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} dev: true + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + /rollup@4.13.0: resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1779,6 +2245,12 @@ packages: fsevents: 2.3.3 dev: true + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: false @@ -1809,6 +2281,32 @@ packages: randombytes: 2.1.0 dev: false + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + /source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} @@ -1825,6 +2323,13 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + /source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + dependencies: + whatwg-url: 7.1.0 + dev: true + /stacktracey@2.1.8: resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==} dependencies: @@ -1842,6 +2347,57 @@ packages: engines: {node: '>=10.0.0'} dev: true + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.3.12 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: true + /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -1897,15 +2453,89 @@ packages: source-map-support: 0.5.21 dev: false + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: true + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: true + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} dev: true + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + dependencies: + punycode: 2.3.1 + dev: true + + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true + + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: true + /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true + /tsup@8.0.2(typescript@5.4.3): + resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + dependencies: + bundle-require: 4.0.2(esbuild@0.19.12) + cac: 6.7.14 + chokidar: 3.6.0 + debug: 4.3.4 + esbuild: 0.19.12 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 4.0.2 + resolve-from: 5.0.0 + rollup: 4.13.0 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tree-kill: 1.2.2 + typescript: 5.4.3 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + /tsx@4.7.1: resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==} engines: {node: '>=18.0.0'} @@ -1997,6 +2627,10 @@ packages: graceful-fs: 4.2.11 dev: false + /webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + dev: true + /webpack-sources@3.2.3: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} @@ -2042,6 +2676,22 @@ packages: - uglify-js dev: false + /whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + /workerd@1.20240404.0: resolution: {integrity: sha512-U4tfnvBcPMsv7pmRGuF0J5UnoZi6tbc64tXNfyijI74r6w6Vlb2+a6eibdQL8g0g46+4vjuTKME9G5RvSvdc8g==} engines: {node: '>=16'} @@ -2055,6 +2705,24 @@ packages: '@cloudflare/workerd-windows-64': 1.20240404.0 dev: true + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: true + /ws@8.16.0: resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} engines: {node: '>=10.0.0'} @@ -2072,6 +2740,12 @@ packages: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true + /yaml@2.4.1: + resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} + engines: {node: '>= 14'} + hasBin: true + dev: true + /youch@3.3.3: resolution: {integrity: sha512-qSFXUk3UZBLfggAW3dJKg0BMblG5biqSF8M34E06o5CSsZtH92u9Hqmj2RzGiHDi64fhe83+4tENFP2DB6t6ZA==} dependencies: From 2e503f5df890b38e59a51f07973b9785cbb098f4 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 17:34:14 +0900 Subject: [PATCH 07/44] chore: statefull example --- examples/workerd/src/example2/main.ts | 20 ++++++++++++++------ examples/workerd/src/example2/worker.mjs | 4 +++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/examples/workerd/src/example2/main.ts b/examples/workerd/src/example2/main.ts index 22e17b37..afd98422 100644 --- a/examples/workerd/src/example2/main.ts +++ b/examples/workerd/src/example2/main.ts @@ -26,12 +26,20 @@ async function main() { const ns = await mf.getDurableObjectNamespace(DO_NAME); const stub = ns.get(ns.idFromName("")); - const res = await stub.fetch("http://test.local/hello"); - console.log("[response]", { - status: res.status, - headers: Object.fromEntries(res.headers), - text: await res.text(), - }); + + async function run() { + const res = await stub.fetch("http://test.local/hello"); + console.log("[response]", { + status: res.status, + headers: Object.fromEntries(res.headers), + text: await res.text(), + }); + } + + await run(); + await run(); + await run(); + await mf.dispose(); } diff --git a/examples/workerd/src/example2/worker.mjs b/examples/workerd/src/example2/worker.mjs index 8d8012c7..33d7cb14 100644 --- a/examples/workerd/src/example2/worker.mjs +++ b/examples/workerd/src/example2/worker.mjs @@ -1,10 +1,12 @@ export class MyDurableObject { + #state = 0; + /** * * @param {Request} req * @returns */ fetch(req) { - return new Response("yaayy: " + req.url); + return new Response(JSON.stringify({ url: req.url, state: this.#state++ })); } } From 82da05c4f61481af341ce974d03814e839b1b6df Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 18:18:00 +0900 Subject: [PATCH 08/44] feat: vite ModuleRunner in durable objects --- examples/workerd/src/example4/main.ts | 75 ++++++++++++++++++---- examples/workerd/src/example4/shared.ts | 2 + examples/workerd/src/example4/src/entry.ts | 3 + examples/workerd/src/example4/worker.ts | 65 ++++++++++++++++--- examples/workerd/tsconfig.json | 1 + 5 files changed, 124 insertions(+), 22 deletions(-) create mode 100644 examples/workerd/src/example4/shared.ts create mode 100644 examples/workerd/src/example4/src/entry.ts diff --git a/examples/workerd/src/example4/main.ts b/examples/workerd/src/example4/main.ts index 9bc081b0..eaf495bf 100644 --- a/examples/workerd/src/example4/main.ts +++ b/examples/workerd/src/example4/main.ts @@ -1,6 +1,8 @@ -import { Miniflare } from "miniflare"; +import { Log, Miniflare } from "miniflare"; import { fileURLToPath } from "url"; -import { tinyassert, createManualPromise } from "@hiogawa/utils"; +import { tinyassert } from "@hiogawa/utils"; +import { RUNNER_INIT_PATH, UNSAFE_EVAL_BINDING } from "./shared"; +import { DevEnvironment, RemoteEnvironmentTransport, createServer } from "vite"; // pnpm -C examples/workerd dev // npx tsx examples/workerd/src/example4/main.ts @@ -9,6 +11,7 @@ const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; async function main() { const mf = new Miniflare({ + log: new Log(), modulesRoot: "/", modules: [ { @@ -19,25 +22,69 @@ async function main() { durableObjects: { [RUNNER_OBJECT_BINDING]: "RunnerObject", }, + unsafeEvalBinding: UNSAFE_EVAL_BINDING, }); const ns = await mf.getDurableObjectNamespace(RUNNER_OBJECT_BINDING); - const stub = ns.get(ns.idFromName("")); - const res = await stub.fetch("http://test.local/hello", { - headers: { - Upgrade: "websocket", + const runnerObject = ns.get(ns.idFromName("")); + + const root = fileURLToPath(new URL(".", import.meta.url)); + const wsRes = await runnerObject.fetch( + "http://test.local" + + RUNNER_INIT_PATH + + "?" + + new URLSearchParams({ root }), + { + headers: { + Upgrade: "websocket", + }, + }, + ); + tinyassert(wsRes.webSocket); + const ws = wsRes.webSocket; + ws.accept(); + + const viteDevServer = await createServer({ + root, + configFile: false, + server: { + middlewareMode: true, + watch: null, + }, + environments: { + workerd: { + dev: { + createEnvironment: (server, name) => { + return new DevEnvironment(server, name, { + runner: { + transport: new RemoteEnvironmentTransport({ + send: (data) => ws.send(JSON.stringify(data)), + onMessage: (handler) => { + ws.addEventListener("message", (event) => { + tinyassert(typeof event.data === "string"); + handler(JSON.parse(event.data)); + }); + }, + }), + }, + }); + }, + }, + }, }, }); - tinyassert(res.webSocket); - const promise = createManualPromise(); - res.webSocket.addEventListener("message", (data) => { - promise.resolve(data); + const workerdEnv = viteDevServer.environments["workerd"]; + tinyassert(workerdEnv); + + const res = await runnerObject.fetch("http://test.local"); + console.log("[response]", { + status: res.status, + headers: Object.fromEntries(res.headers), + text: await res.text(), }); - res.webSocket.accept(); - res.webSocket.send("hello"); - const data = await promise; - console.log(data); + await mf.dispose(); + await viteDevServer.close(); } main(); diff --git a/examples/workerd/src/example4/shared.ts b/examples/workerd/src/example4/shared.ts new file mode 100644 index 00000000..28e83f84 --- /dev/null +++ b/examples/workerd/src/example4/shared.ts @@ -0,0 +1,2 @@ +export const RUNNER_INIT_PATH = "/__viteInit"; +export const UNSAFE_EVAL_BINDING = "__viteUnsafeEval"; diff --git a/examples/workerd/src/example4/src/entry.ts b/examples/workerd/src/example4/src/entry.ts new file mode 100644 index 00000000..0c188e53 --- /dev/null +++ b/examples/workerd/src/example4/src/entry.ts @@ -0,0 +1,3 @@ +export default function main() { + return "yay!"; +} diff --git a/examples/workerd/src/example4/worker.ts b/examples/workerd/src/example4/worker.ts index c7c0433b..bf1fad23 100644 --- a/examples/workerd/src/example4/worker.ts +++ b/examples/workerd/src/example4/worker.ts @@ -1,13 +1,62 @@ +import { tinyassert } from "@hiogawa/utils"; +import { RUNNER_INIT_PATH, UNSAFE_EVAL_BINDING } from "./shared"; +import { ModuleRunner, RemoteRunnerTransport } from "vite/module-runner"; + export class RunnerObject implements DurableObject { - fetch(req: Request) { + #runner: ModuleRunner | undefined; + #env: any; + + constructor(_state: DurableObjectState, env: any) { + this.#env = env; + } + + async fetch(req: Request) { const url = new URL(req.url); - if (url.pathname === "___viteInit") { + + if (url.pathname === RUNNER_INIT_PATH) { + const { 0: ws1, 1: ws2 } = new WebSocketPair(); + (ws1 as any).accept(); + + tinyassert(!this.#runner); + const root = url.searchParams.get("root"); + tinyassert(root); + this.#runner = new ModuleRunner( + { + root, + sourcemapInterceptor: "prepareStackTrace", + transport: new RemoteRunnerTransport({ + onMessage: (listener) => { + ws1.addEventListener("message", (event) => { + listener(JSON.parse(event.data)); + }); + }, + send: (message) => { + ws1.send(JSON.stringify(message)); + }, + }), + // TODO + hmr: false, + }, + { + runInlinedModule: async (context, transformed, id) => { + const codeDefinition = `'use strict';async (${Object.keys( + context, + ).join(",")})=>{{`; + const code = `${codeDefinition}${transformed}\n}}`; + const fn = this.#env[UNSAFE_EVAL_BINDING].eval(code, id); + await fn(...Object.values(context)); + Object.freeze(context.__vite_ssr_exports__); + }, + runExternalModule(filepath) { + return import(filepath); + }, + }, + ); + return new Response(null, { status: 101, webSocket: ws2 }); } - const { 0: webSocket1, 1: webSocket2 } = new WebSocketPair(); - (webSocket1 as any).accept(); - webSocket1.addEventListener("message", (event) => { - webSocket1.send("echo:" + event.data); - }); - return new Response(null, { status: 101, webSocket: webSocket2 }); + + tinyassert(this.#runner); + const mod = await this.#runner.import("/src/entry.ts"); + return new Response(mod.default()); } } diff --git a/examples/workerd/tsconfig.json b/examples/workerd/tsconfig.json index d07881cb..b1a46512 100644 --- a/examples/workerd/tsconfig.json +++ b/examples/workerd/tsconfig.json @@ -2,6 +2,7 @@ "extends": "@tsconfig/strictest/tsconfig.json", "include": ["src", "vite.config.ts", "e2e", "playwright.config.ts"], "compilerOptions": { + "checkJs": false, "exactOptionalPropertyTypes": false, "verbatimModuleSyntax": true, "noEmit": true, From ad824de6c89c580f76b2740c2f60f0675a21191f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 18:26:59 +0900 Subject: [PATCH 09/44] chore: cleanup --- examples/workerd/src/example4/main.ts | 2 -- examples/workerd/src/example4/src/entry.ts | 2 +- examples/workerd/src/example4/worker.ts | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/workerd/src/example4/main.ts b/examples/workerd/src/example4/main.ts index eaf495bf..5423c447 100644 --- a/examples/workerd/src/example4/main.ts +++ b/examples/workerd/src/example4/main.ts @@ -73,8 +73,6 @@ async function main() { }, }, }); - const workerdEnv = viteDevServer.environments["workerd"]; - tinyassert(workerdEnv); const res = await runnerObject.fetch("http://test.local"); console.log("[response]", { diff --git a/examples/workerd/src/example4/src/entry.ts b/examples/workerd/src/example4/src/entry.ts index 0c188e53..31c8e820 100644 --- a/examples/workerd/src/example4/src/entry.ts +++ b/examples/workerd/src/example4/src/entry.ts @@ -1,3 +1,3 @@ -export default function main() { +export default function main(): string { return "yay!"; } diff --git a/examples/workerd/src/example4/worker.ts b/examples/workerd/src/example4/worker.ts index bf1fad23..fd61025e 100644 --- a/examples/workerd/src/example4/worker.ts +++ b/examples/workerd/src/example4/worker.ts @@ -39,6 +39,7 @@ export class RunnerObject implements DurableObject { }, { runInlinedModule: async (context, transformed, id) => { + // TODO: check stacktrace const codeDefinition = `'use strict';async (${Object.keys( context, ).join(",")})=>{{`; From d1357f7c3038abc384dbd0d9c7466de69fa3ba5c Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 19:20:26 +0900 Subject: [PATCH 10/44] feat: prototype vitePluginWorkerd --- examples/workerd/package.json | 2 +- examples/workerd/src/example1/main.ts | 25 --- examples/workerd/src/example1/worker.mjs | 5 - examples/workerd/src/example2/main.ts | 46 ------ examples/workerd/src/example2/worker.mjs | 12 -- examples/workerd/src/example3/main.ts | 42 ----- examples/workerd/src/example3/worker.mjs | 17 -- examples/workerd/src/index.ts | 1 + examples/workerd/src/plugin.ts | 145 ++++++++++++++++++ .../workerd/src/{example4 => poc}/main.ts | 2 +- .../workerd/src/{example4 => poc}/shared.ts | 0 .../src/{example4 => poc}/src/entry.ts | 0 .../workerd/src/{example4 => poc}/worker.ts | 0 examples/workerd/src/shared.ts | 8 + examples/workerd/src/worker.ts | 70 +++++++++ examples/workerd/tsup.config.ts | 4 +- 16 files changed, 228 insertions(+), 151 deletions(-) delete mode 100644 examples/workerd/src/example1/main.ts delete mode 100644 examples/workerd/src/example1/worker.mjs delete mode 100644 examples/workerd/src/example2/main.ts delete mode 100644 examples/workerd/src/example2/worker.mjs delete mode 100644 examples/workerd/src/example3/main.ts delete mode 100644 examples/workerd/src/example3/worker.mjs create mode 100644 examples/workerd/src/index.ts create mode 100644 examples/workerd/src/plugin.ts rename examples/workerd/src/{example4 => poc}/main.ts (97%) rename examples/workerd/src/{example4 => poc}/shared.ts (100%) rename examples/workerd/src/{example4 => poc}/src/entry.ts (100%) rename examples/workerd/src/{example4 => poc}/worker.ts (100%) create mode 100644 examples/workerd/src/shared.ts create mode 100644 examples/workerd/src/worker.ts diff --git a/examples/workerd/package.json b/examples/workerd/package.json index 5d0844e0..8c0ad05d 100644 --- a/examples/workerd/package.json +++ b/examples/workerd/package.json @@ -1,5 +1,5 @@ { - "name": "@hiogawa/vite-environment-examples-workerd", + "name": "@hiogawa/vite-plugin-workerd", "private": true, "type": "module", "scripts": { diff --git a/examples/workerd/src/example1/main.ts b/examples/workerd/src/example1/main.ts deleted file mode 100644 index afba95c1..00000000 --- a/examples/workerd/src/example1/main.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Miniflare } from "miniflare"; -import { fileURLToPath } from "url"; - -// npx tsx examples/workerd/src/example1/main.ts - -async function main() { - const mf = new Miniflare({ - modulesRoot: "/", - modules: [ - { - type: "ESModule", - path: fileURLToPath(new URL("./worker.mjs", import.meta.url)), - }, - ], - }); - const res = await mf.dispatchFetch("https://test.local/hello"); - console.log("[node] response", { - status: res.status, - headers: Object.fromEntries(res.headers), - text: await res.text(), - }); - await mf.dispose(); -} - -main(); diff --git a/examples/workerd/src/example1/worker.mjs b/examples/workerd/src/example1/worker.mjs deleted file mode 100644 index 3fdd2e83..00000000 --- a/examples/workerd/src/example1/worker.mjs +++ /dev/null @@ -1,5 +0,0 @@ -export default { - fetch: () => { - return new Response("yay"); - }, -}; diff --git a/examples/workerd/src/example2/main.ts b/examples/workerd/src/example2/main.ts deleted file mode 100644 index afd98422..00000000 --- a/examples/workerd/src/example2/main.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Miniflare } from "miniflare"; -import { fileURLToPath } from "url"; - -// npx tsx examples/workerd/src/example2/main.ts - -const DO_NAME = "__do"; - -async function main() { - const mf = new Miniflare({ - modulesRoot: "/", - modules: [ - { - type: "ESModule", - path: fileURLToPath(new URL("./worker.mjs", import.meta.url)), - }, - ], - durableObjects: { - [DO_NAME]: { - className: "MyDurableObject", - // need this dark magic? - // unsafeUniqueKey: kUnsafeEphemeralUniqueKey, - // unsafePreventEviction: true, - }, - }, - }); - - const ns = await mf.getDurableObjectNamespace(DO_NAME); - const stub = ns.get(ns.idFromName("")); - - async function run() { - const res = await stub.fetch("http://test.local/hello"); - console.log("[response]", { - status: res.status, - headers: Object.fromEntries(res.headers), - text: await res.text(), - }); - } - - await run(); - await run(); - await run(); - - await mf.dispose(); -} - -main(); diff --git a/examples/workerd/src/example2/worker.mjs b/examples/workerd/src/example2/worker.mjs deleted file mode 100644 index 33d7cb14..00000000 --- a/examples/workerd/src/example2/worker.mjs +++ /dev/null @@ -1,12 +0,0 @@ -export class MyDurableObject { - #state = 0; - - /** - * - * @param {Request} req - * @returns - */ - fetch(req) { - return new Response(JSON.stringify({ url: req.url, state: this.#state++ })); - } -} diff --git a/examples/workerd/src/example3/main.ts b/examples/workerd/src/example3/main.ts deleted file mode 100644 index 496ec7cd..00000000 --- a/examples/workerd/src/example3/main.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Miniflare } from "miniflare"; -import { fileURLToPath } from "url"; -import { tinyassert, createManualPromise } from "@hiogawa/utils"; - -// npx tsx examples/workerd/src/example3/main.ts - -const DO_NAME = "__do"; - -async function main() { - const mf = new Miniflare({ - modulesRoot: "/", - modules: [ - { - type: "ESModule", - path: fileURLToPath(new URL("./worker.mjs", import.meta.url)), - }, - ], - durableObjects: { - [DO_NAME]: "MyDurableObject", - }, - }); - - const ns = await mf.getDurableObjectNamespace(DO_NAME); - const stub = ns.get(ns.idFromName("")); - const res = await stub.fetch("http://test.local/hello", { - headers: { - Upgrade: "websocket", - }, - }); - tinyassert(res.webSocket); - const promise = createManualPromise(); - res.webSocket.addEventListener("message", (data) => { - promise.resolve(data); - }); - res.webSocket.accept(); - res.webSocket.send("hello"); - const data = await promise; - console.log(data); - await mf.dispose(); -} - -main(); diff --git a/examples/workerd/src/example3/worker.mjs b/examples/workerd/src/example3/worker.mjs deleted file mode 100644 index 100149a7..00000000 --- a/examples/workerd/src/example3/worker.mjs +++ /dev/null @@ -1,17 +0,0 @@ -// @ts-nocheck - -export class MyDurableObject { - /** - * - * @param {Request} req - * @returns - */ - fetch(req) { - const [webSocket1, webSocket2] = Object.values(new WebSocketPair()); - webSocket1.accept(); - webSocket1.addEventListener("message", (event) => { - webSocket1.send("echo:" + event.data); - }); - return new Response(null, { status: 101, webSocket: webSocket2 }); - } -} diff --git a/examples/workerd/src/index.ts b/examples/workerd/src/index.ts new file mode 100644 index 00000000..39b9a61b --- /dev/null +++ b/examples/workerd/src/index.ts @@ -0,0 +1 @@ +export * from "./plugin"; diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts new file mode 100644 index 00000000..0cd56a3e --- /dev/null +++ b/examples/workerd/src/plugin.ts @@ -0,0 +1,145 @@ +import { + Log, + Miniflare, + type MiniflareOptions, + type WorkerOptions, +} from "miniflare"; +import { fileURLToPath } from "url"; +import { tinyassert } from "@hiogawa/utils"; +import { + RUNNER_INIT_PATH, + UNSAFE_EVAL_BINDING, + type RunnerInitOpitons, +} from "./shared"; +import { DevEnvironment, RemoteEnvironmentTransport, type Plugin } from "vite"; +import { createMiddleware } from "@hattip/adapter-node/native-fetch"; + +interface WorkerdPluginOptions { + entry: string; + options?: (v: MiniflareOptions & WorkerOptions) => void; +} + +export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { + let manager: MiniflareManager | undefined; + let dispose = async () => { + await manager?.miniflare.dispose(); + manager = undefined; + }; + + return { + name: vitePluginWorkerd.name, + apply: "serve", + async config(_config, _env) { + dispose(); + + // [feedback] createEnvironment should be async? + manager = await setupMiniflareManager(pluginOptions); + const ws = manager.webSocket; + + return { + environments: { + workerd: { + dev: { + createEnvironment(server, name) { + return new DevEnvironment(server, name, { + runner: { + transport: new RemoteEnvironmentTransport({ + send: (data) => ws.send(JSON.stringify(data)), + onMessage: (handler) => { + ws.addEventListener("message", (event) => { + tinyassert(typeof event.data === "string"); + handler(JSON.parse(event.data)); + }); + }, + }), + }, + }); + }, + }, + }, + }, + }; + }, + + configureServer(server) { + return () => { + server.middlewares.use( + createMiddleware( + (ctx) => { + tinyassert(manager); + return manager.dispatchFetch(ctx.request); + }, + { + alwaysCallNext: false, + }, + ), + ); + }; + }, + + async buildEnd() { + await dispose(); + }, + }; +} + +type MiniflareManager = Awaited>; + +const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; +const BASE_URL = "http://test.local"; + +async function setupMiniflareManager(options: WorkerdPluginOptions) { + const miniflareOptions: MiniflareOptions = { + log: new Log(), + modulesRoot: "/", + modules: [ + { + type: "ESModule", + path: fileURLToPath(new URL("./worker.js", import.meta.url)), + }, + ], + durableObjects: { + [RUNNER_OBJECT_BINDING]: "RunnerObject", + }, + unsafeEvalBinding: UNSAFE_EVAL_BINDING, + }; + options.options?.(miniflareOptions); + + const miniflare = new Miniflare(miniflareOptions); + + const ns = await miniflare.getDurableObjectNamespace(RUNNER_OBJECT_BINDING); + const runnerObject = ns.get(ns.idFromName("")); + + const initOpitons: RunnerInitOpitons = { + // TODO: should be server.config.root + root: process.cwd(), + entry: options.entry, + }; + + const res = await runnerObject.fetch( + BASE_URL + + RUNNER_INIT_PATH + + "?" + + new URLSearchParams({ options: JSON.stringify(initOpitons) }), + { + headers: { + Upgrade: "websocket", + }, + }, + ); + tinyassert(res.webSocket); + const { webSocket } = res; + webSocket.accept(); + + function dispatchFetch(request: Request) { + return runnerObject.fetch(request.url, { + method: request.method, + headers: request.headers, + body: request.body as any, + duplex: "half", + redirect: "manual", + }) as any as Response; + } + + return { miniflare, runnerObject, webSocket, dispatchFetch }; +} diff --git a/examples/workerd/src/example4/main.ts b/examples/workerd/src/poc/main.ts similarity index 97% rename from examples/workerd/src/example4/main.ts rename to examples/workerd/src/poc/main.ts index 5423c447..2e1e4ae1 100644 --- a/examples/workerd/src/example4/main.ts +++ b/examples/workerd/src/poc/main.ts @@ -5,7 +5,7 @@ import { RUNNER_INIT_PATH, UNSAFE_EVAL_BINDING } from "./shared"; import { DevEnvironment, RemoteEnvironmentTransport, createServer } from "vite"; // pnpm -C examples/workerd dev -// npx tsx examples/workerd/src/example4/main.ts +// npx tsx examples/workerd/src/poc/main.ts const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; diff --git a/examples/workerd/src/example4/shared.ts b/examples/workerd/src/poc/shared.ts similarity index 100% rename from examples/workerd/src/example4/shared.ts rename to examples/workerd/src/poc/shared.ts diff --git a/examples/workerd/src/example4/src/entry.ts b/examples/workerd/src/poc/src/entry.ts similarity index 100% rename from examples/workerd/src/example4/src/entry.ts rename to examples/workerd/src/poc/src/entry.ts diff --git a/examples/workerd/src/example4/worker.ts b/examples/workerd/src/poc/worker.ts similarity index 100% rename from examples/workerd/src/example4/worker.ts rename to examples/workerd/src/poc/worker.ts diff --git a/examples/workerd/src/shared.ts b/examples/workerd/src/shared.ts new file mode 100644 index 00000000..b4cad78b --- /dev/null +++ b/examples/workerd/src/shared.ts @@ -0,0 +1,8 @@ +export const RUNNER_INIT_PATH = "/__viteInit"; + +export const UNSAFE_EVAL_BINDING = "__viteUnsafeEval"; + +export type RunnerInitOpitons = { + root: string; + entry: string; +}; diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts new file mode 100644 index 00000000..3cdba695 --- /dev/null +++ b/examples/workerd/src/worker.ts @@ -0,0 +1,70 @@ +import { tinyassert } from "@hiogawa/utils"; +import { + RUNNER_INIT_PATH, + UNSAFE_EVAL_BINDING, + type RunnerInitOpitons, +} from "./shared"; +import { ModuleRunner, RemoteRunnerTransport } from "vite/module-runner"; + +export class RunnerObject implements DurableObject { + #runner: ModuleRunner | undefined; + #env: any; + #entry!: string; + + constructor(_state: DurableObjectState, env: any) { + this.#env = env; + } + + async fetch(request: Request) { + const url = new URL(request.url); + + if (url.pathname === RUNNER_INIT_PATH) { + const { 0: ws1, 1: ws2 } = new WebSocketPair(); + (ws1 as any).accept(); + + tinyassert(!this.#runner); + const initOpitonsRaw = url.searchParams.get("options"); + tinyassert(initOpitonsRaw); + const initOptions = JSON.parse(initOpitonsRaw) as RunnerInitOpitons; + this.#entry = initOptions.entry; + + this.#runner = new ModuleRunner( + { + root: initOptions.root, + sourcemapInterceptor: "prepareStackTrace", + transport: new RemoteRunnerTransport({ + onMessage: (listener) => { + ws1.addEventListener("message", (event) => { + listener(JSON.parse(event.data)); + }); + }, + send: (message) => { + ws1.send(JSON.stringify(message)); + }, + }), + // TODO: spawn two pairs of websocket? + hmr: false, + }, + { + runInlinedModule: async (context, transformed, id) => { + const codeDefinition = `'use strict';async (${Object.keys( + context, + ).join(",")})=>{{`; + const code = `${codeDefinition}${transformed}\n}}`; + const fn = this.#env[UNSAFE_EVAL_BINDING].eval(code, id); + await fn(...Object.values(context)); + Object.freeze(context.__vite_ssr_exports__); + }, + runExternalModule(filepath) { + return import(filepath); + }, + }, + ); + return new Response(null, { status: 101, webSocket: ws2 }); + } + + tinyassert(this.#runner); + const mod = await this.#runner.import(this.#entry); + return new Response(mod.default(request, this.#env)); + } +} diff --git a/examples/workerd/tsup.config.ts b/examples/workerd/tsup.config.ts index d60396f9..ad27ee13 100644 --- a/examples/workerd/tsup.config.ts +++ b/examples/workerd/tsup.config.ts @@ -2,8 +2,8 @@ import { defineConfig } from "tsup"; export default [ defineConfig({ - outDir: "src/example4/dist", - entry: ["src/example4/worker.ts"], + outDir: "src/poc/dist", + entry: ["src/poc/worker.ts"], format: ["esm"], platform: "browser", noExternal: [/.*/], From 41bc4aa1adc18f22c3a6b07d714c5325a0607df5 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 19:47:16 +0900 Subject: [PATCH 11/44] wip: plugin --- examples/react-ssr/package.json | 2 ++ examples/react-ssr/src/adapters/workerd.ts | 8 +++++ examples/react-ssr/src/entry-server.tsx | 3 ++ examples/react-ssr/tsconfig.json | 8 ++++- examples/react-ssr/vite.config.workerd.ts | 39 ++++++++++++++++++++++ examples/workerd/README.md | 5 --- examples/workerd/package.json | 9 ++++- examples/workerd/src/plugin.ts | 7 ++-- examples/workerd/src/worker.ts | 10 ++++-- examples/workerd/tsup.config.ts | 13 ++++++++ pnpm-lock.yaml | 3 ++ 11 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 examples/react-ssr/src/adapters/workerd.ts create mode 100644 examples/react-ssr/vite.config.workerd.ts diff --git a/examples/react-ssr/package.json b/examples/react-ssr/package.json index 39c32b43..28d3dda5 100644 --- a/examples/react-ssr/package.json +++ b/examples/react-ssr/package.json @@ -4,6 +4,7 @@ "type": "module", "scripts": { "dev": "vite", + "dev-workerd": "vite --config vite.config.workerd.ts", "build": "vite build --all", "preview": "vite preview", "test-e2e": "playwright test", @@ -16,6 +17,7 @@ "react-dom": "18.3.0-canary-6c3b8dbfe-20240226" }, "devDependencies": { + "@hiogawa/vite-plugin-workerd": "workspace:*", "@types/react": "18.2.72", "@types/react-dom": "18.2.22" }, diff --git a/examples/react-ssr/src/adapters/workerd.ts b/examples/react-ssr/src/adapters/workerd.ts new file mode 100644 index 00000000..b48e2b25 --- /dev/null +++ b/examples/react-ssr/src/adapters/workerd.ts @@ -0,0 +1,8 @@ +import { handler } from "../entry-server"; + +export default { + fetch: handler, + // fetch: () => { + // return new Response("hello"); + // }, +}; diff --git a/examples/react-ssr/src/entry-server.tsx b/examples/react-ssr/src/entry-server.tsx index 37cf42c2..44c5e6a5 100644 --- a/examples/react-ssr/src/entry-server.tsx +++ b/examples/react-ssr/src/entry-server.tsx @@ -4,6 +4,9 @@ import { __global } from "./global"; export async function handler(_req: Request) { const ssrHtml = ReactDomServer.renderToString(); + if (1) { + return new Response(ssrHtml, { headers: { "content-type": "text/html" } }); + } let html = await importHtml(); html = html.replace(//, `
${ssrHtml}
`); return new Response(html, { headers: { "content-type": "text/html" } }); diff --git a/examples/react-ssr/tsconfig.json b/examples/react-ssr/tsconfig.json index aa702900..b6fc0921 100644 --- a/examples/react-ssr/tsconfig.json +++ b/examples/react-ssr/tsconfig.json @@ -1,6 +1,12 @@ { "extends": "@tsconfig/strictest/tsconfig.json", - "include": ["src", "vite.config.ts", "e2e", "playwright.config.ts"], + "include": [ + "src", + "vite.config.ts", + "vite.config.workerd.ts", + "e2e", + "playwright.config.ts" + ], "compilerOptions": { "exactOptionalPropertyTypes": false, "verbatimModuleSyntax": true, diff --git a/examples/react-ssr/vite.config.workerd.ts b/examples/react-ssr/vite.config.workerd.ts new file mode 100644 index 00000000..30fc56a6 --- /dev/null +++ b/examples/react-ssr/vite.config.workerd.ts @@ -0,0 +1,39 @@ +import { defineConfig } from "vite"; +import { __global } from "./src/global"; +import react from "@vitejs/plugin-react"; +import { vitePluginWorkerd } from "@hiogawa/vite-plugin-workerd"; + +export default defineConfig((_env) => ({ + clearScreen: false, + appType: "custom", + plugins: [ + react(), + vitePluginWorkerd({ + entry: "/src/adapters/workerd.ts", + }), + ], + environments: { + workerd: { + // [feedback] how to prevent deps optimization to inject this? + // import { createRequire } from 'module';const require = createRequire(import.meta.url); + nodeCompatible: false, + webCompatible: true, + resolve: { + noExternal: true, + }, + dev: { + optimizeDeps: { + include: [ + "react", + "react/jsx-runtime", + "react/jsx-dev-runtime", + "react-dom/server.edge", + ], + }, + }, + }, + }, + ssr: { + target: "webworker", + }, +})); diff --git a/examples/workerd/README.md b/examples/workerd/README.md index 9bb3163b..cdf9a5db 100644 --- a/examples/workerd/README.md +++ b/examples/workerd/README.md @@ -1,10 +1,5 @@ # workerd -## todo - -- [ ] basic durable objects -- [ ] integrate vite - ## references - https://github.com/cloudflare/workers-sdk/blame/2789f26a87c769fc6177b0bdc79a839a15f4ced1/packages/miniflare/test/plugins/do/index.spec.ts diff --git a/examples/workerd/package.json b/examples/workerd/package.json index 8c0ad05d..8d407fda 100644 --- a/examples/workerd/package.json +++ b/examples/workerd/package.json @@ -2,8 +2,15 @@ "name": "@hiogawa/vite-plugin-workerd", "private": true, "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, "scripts": { - "dev": "tsup --watch" + "dev": "tsup --watch", + "build": "tsup" }, "devDependencies": { "@cloudflare/workers-types": "^4.20240405.0", diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 0cd56a3e..76f9f0f4 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -131,14 +131,15 @@ async function setupMiniflareManager(options: WorkerdPluginOptions) { const { webSocket } = res; webSocket.accept(); - function dispatchFetch(request: Request) { - return runnerObject.fetch(request.url, { + async function dispatchFetch(request: Request) { + const response = await runnerObject.fetch(request.url, { method: request.method, headers: request.headers, body: request.body as any, duplex: "half", redirect: "manual", - }) as any as Response; + }); + return response as any as Response; } return { miniflare, runnerObject, webSocket, dispatchFetch }; diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index 3cdba695..d1a02b57 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -32,6 +32,7 @@ export class RunnerObject implements DurableObject { { root: initOptions.root, sourcemapInterceptor: "prepareStackTrace", + // TODO: websocket for fetchModule is still too big transport: new RemoteRunnerTransport({ onMessage: (listener) => { ws1.addEventListener("message", (event) => { @@ -55,8 +56,11 @@ export class RunnerObject implements DurableObject { await fn(...Object.values(context)); Object.freeze(context.__vite_ssr_exports__); }, - runExternalModule(filepath) { - return import(filepath); + async runExternalModule(filepath) { + return { + createRequire: () => {}, + }; + // return import(filepath); }, }, ); @@ -65,6 +69,6 @@ export class RunnerObject implements DurableObject { tinyassert(this.#runner); const mod = await this.#runner.import(this.#entry); - return new Response(mod.default(request, this.#env)); + return mod.default.fetch(request, this.#env); } } diff --git a/examples/workerd/tsup.config.ts b/examples/workerd/tsup.config.ts index ad27ee13..404b3e34 100644 --- a/examples/workerd/tsup.config.ts +++ b/examples/workerd/tsup.config.ts @@ -8,4 +8,17 @@ export default [ platform: "browser", noExternal: [/.*/], }), + defineConfig({ + entry: ["src/worker.ts"], + format: ["esm"], + platform: "browser", + noExternal: [/.*/], + }), + defineConfig({ + entry: ["src/index.ts"], + format: ["esm"], + platform: "node", + dts: true, + external: ["vite", "miniflare"], + }), ]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b86c774..a64e7762 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,6 +92,9 @@ importers: specifier: 18.3.0-canary-6c3b8dbfe-20240226 version: 18.3.0-canary-6c3b8dbfe-20240226(react@18.3.0-canary-6c3b8dbfe-20240226) devDependencies: + '@hiogawa/vite-plugin-workerd': + specifier: workspace:* + version: link:../workerd '@types/react': specifier: 18.2.72 version: 18.2.72 From 6430137b0760fa4f91ce9e2ed50768aef35d5063 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 20:37:29 +0900 Subject: [PATCH 12/44] wip --- examples/react-ssr/vite.config.workerd.ts | 5 ++++- examples/workerd/src/plugin.ts | 1 + examples/workerd/src/worker.ts | 6 ++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/react-ssr/vite.config.workerd.ts b/examples/react-ssr/vite.config.workerd.ts index 30fc56a6..d27eabfd 100644 --- a/examples/react-ssr/vite.config.workerd.ts +++ b/examples/react-ssr/vite.config.workerd.ts @@ -14,7 +14,7 @@ export default defineConfig((_env) => ({ ], environments: { workerd: { - // [feedback] how to prevent deps optimization to inject this? + // [feedback] how to prevent deps optimization to inject this? still `ssr.target: "webworker"` needed? // import { createRequire } from 'module';const require = createRequire(import.meta.url); nodeCompatible: false, webCompatible: true, @@ -33,6 +33,9 @@ export default defineConfig((_env) => ({ }, }, }, + optimizeDeps: { + force: true, + }, ssr: { target: "webworker", }, diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 76f9f0f4..0f66a36f 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -110,6 +110,7 @@ async function setupMiniflareManager(options: WorkerdPluginOptions) { const ns = await miniflare.getDurableObjectNamespace(RUNNER_OBJECT_BINDING); const runnerObject = ns.get(ns.idFromName("")); + // TODO: use binding constants const initOpitons: RunnerInitOpitons = { // TODO: should be server.config.root root: process.cwd(), diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index d1a02b57..a0edb5f7 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -57,10 +57,8 @@ export class RunnerObject implements DurableObject { Object.freeze(context.__vite_ssr_exports__); }, async runExternalModule(filepath) { - return { - createRequire: () => {}, - }; - // return import(filepath); + console.log("[runExternalModule]", filepath); + return import(filepath); }, }, ); From 959e1f31f9a936907ac9c7ec636e44a333b7a598 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 20:49:24 +0900 Subject: [PATCH 13/44] refactor: simplify runner options --- examples/workerd/src/plugin.ts | 35 +++++++++++----------------------- examples/workerd/src/shared.ts | 11 ++++++----- examples/workerd/src/worker.ts | 22 ++++++--------------- 3 files changed, 23 insertions(+), 45 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 0f66a36f..7675ae2a 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -6,11 +6,7 @@ import { } from "miniflare"; import { fileURLToPath } from "url"; import { tinyassert } from "@hiogawa/utils"; -import { - RUNNER_INIT_PATH, - UNSAFE_EVAL_BINDING, - type RunnerInitOpitons, -} from "./shared"; +import { RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; import { DevEnvironment, RemoteEnvironmentTransport, type Plugin } from "vite"; import { createMiddleware } from "@hattip/adapter-node/native-fetch"; @@ -86,7 +82,6 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { type MiniflareManager = Awaited>; const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; -const BASE_URL = "http://test.local"; async function setupMiniflareManager(options: WorkerdPluginOptions) { const miniflareOptions: MiniflareOptions = { @@ -101,7 +96,12 @@ async function setupMiniflareManager(options: WorkerdPluginOptions) { durableObjects: { [RUNNER_OBJECT_BINDING]: "RunnerObject", }, - unsafeEvalBinding: UNSAFE_EVAL_BINDING, + unsafeEvalBinding: "__viteUnsafeEval", + bindings: { + // TODO: server.config.root + __viteRoot: process.cwd(), + __viteEntry: options.entry, + } satisfies Omit, }; options.options?.(miniflareOptions); @@ -110,24 +110,11 @@ async function setupMiniflareManager(options: WorkerdPluginOptions) { const ns = await miniflare.getDurableObjectNamespace(RUNNER_OBJECT_BINDING); const runnerObject = ns.get(ns.idFromName("")); - // TODO: use binding constants - const initOpitons: RunnerInitOpitons = { - // TODO: should be server.config.root - root: process.cwd(), - entry: options.entry, - }; - - const res = await runnerObject.fetch( - BASE_URL + - RUNNER_INIT_PATH + - "?" + - new URLSearchParams({ options: JSON.stringify(initOpitons) }), - { - headers: { - Upgrade: "websocket", - }, + const res = await runnerObject.fetch("http://test.local" + RUNNER_INIT_PATH, { + headers: { + Upgrade: "websocket", }, - ); + }); tinyassert(res.webSocket); const { webSocket } = res; webSocket.accept(); diff --git a/examples/workerd/src/shared.ts b/examples/workerd/src/shared.ts index b4cad78b..b9e94f1b 100644 --- a/examples/workerd/src/shared.ts +++ b/examples/workerd/src/shared.ts @@ -1,8 +1,9 @@ export const RUNNER_INIT_PATH = "/__viteInit"; -export const UNSAFE_EVAL_BINDING = "__viteUnsafeEval"; - -export type RunnerInitOpitons = { - root: string; - entry: string; +export type RunnerEnv = { + __viteRoot: string; + __viteEntry: string; + __viteUnsafeEval: { + eval: (code: string, filename: string) => any; + }; }; diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index a0edb5f7..12871b08 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -1,17 +1,12 @@ import { tinyassert } from "@hiogawa/utils"; -import { - RUNNER_INIT_PATH, - UNSAFE_EVAL_BINDING, - type RunnerInitOpitons, -} from "./shared"; +import { RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; import { ModuleRunner, RemoteRunnerTransport } from "vite/module-runner"; export class RunnerObject implements DurableObject { + #env: RunnerEnv; #runner: ModuleRunner | undefined; - #env: any; - #entry!: string; - constructor(_state: DurableObjectState, env: any) { + constructor(_state: DurableObjectState, env: RunnerEnv) { this.#env = env; } @@ -23,14 +18,9 @@ export class RunnerObject implements DurableObject { (ws1 as any).accept(); tinyassert(!this.#runner); - const initOpitonsRaw = url.searchParams.get("options"); - tinyassert(initOpitonsRaw); - const initOptions = JSON.parse(initOpitonsRaw) as RunnerInitOpitons; - this.#entry = initOptions.entry; - this.#runner = new ModuleRunner( { - root: initOptions.root, + root: this.#env.__viteRoot, sourcemapInterceptor: "prepareStackTrace", // TODO: websocket for fetchModule is still too big transport: new RemoteRunnerTransport({ @@ -52,7 +42,7 @@ export class RunnerObject implements DurableObject { context, ).join(",")})=>{{`; const code = `${codeDefinition}${transformed}\n}}`; - const fn = this.#env[UNSAFE_EVAL_BINDING].eval(code, id); + const fn = this.#env.__viteUnsafeEval.eval(code, id); await fn(...Object.values(context)); Object.freeze(context.__vite_ssr_exports__); }, @@ -66,7 +56,7 @@ export class RunnerObject implements DurableObject { } tinyassert(this.#runner); - const mod = await this.#runner.import(this.#entry); + const mod = await this.#runner.import(this.#env.__viteEntry); return mod.default.fetch(request, this.#env); } } From 6ab9ee6a357db836048bbc84ff1c5b48026f0bb2 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 21:51:19 +0900 Subject: [PATCH 14/44] fix: setup __viteFetchModule service binding --- examples/react-ssr/src/adapters/workerd.ts | 3 -- examples/workerd/src/plugin.ts | 34 ++++++++++++++++++---- examples/workerd/src/shared.ts | 3 ++ examples/workerd/src/worker.ts | 26 +++++++++-------- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/examples/react-ssr/src/adapters/workerd.ts b/examples/react-ssr/src/adapters/workerd.ts index b48e2b25..b6ed37b5 100644 --- a/examples/react-ssr/src/adapters/workerd.ts +++ b/examples/react-ssr/src/adapters/workerd.ts @@ -2,7 +2,4 @@ import { handler } from "../entry-server"; export default { fetch: handler, - // fetch: () => { - // return new Response("hello"); - // }, }; diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 7675ae2a..e5a77def 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -3,11 +3,17 @@ import { Miniflare, type MiniflareOptions, type WorkerOptions, + Response as MiniflareResponse, } from "miniflare"; import { fileURLToPath } from "url"; import { tinyassert } from "@hiogawa/utils"; import { RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; -import { DevEnvironment, RemoteEnvironmentTransport, type Plugin } from "vite"; +import { + DevEnvironment, + RemoteEnvironmentTransport, + type Plugin, + type ViteDevServer, +} from "vite"; import { createMiddleware } from "@hattip/adapter-node/native-fetch"; interface WorkerdPluginOptions { @@ -15,6 +21,8 @@ interface WorkerdPluginOptions { options?: (v: MiniflareOptions & WorkerOptions) => void; } +let globalServer: ViteDevServer; + export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { let manager: MiniflareManager | undefined; let dispose = async () => { @@ -28,7 +36,10 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { async config(_config, _env) { dispose(); - // [feedback] createEnvironment should be async? + // [feedback] + // `createEnvironment` should be async? + // otherwise this complicated miniflare setup has to be done outside + // without access to `ViteDevServer`, `ResvoledConfig` etc... manager = await setupMiniflareManager(pluginOptions); const ws = manager.webSocket; @@ -37,6 +48,8 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { workerd: { dev: { createEnvironment(server, name) { + globalServer = server; + return new DevEnvironment(server, name, { runner: { transport: new RemoteEnvironmentTransport({ @@ -81,9 +94,9 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { type MiniflareManager = Awaited>; -const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; - async function setupMiniflareManager(options: WorkerdPluginOptions) { + const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; + const miniflareOptions: MiniflareOptions = { log: new Log(), modulesRoot: "/", @@ -97,11 +110,20 @@ async function setupMiniflareManager(options: WorkerdPluginOptions) { [RUNNER_OBJECT_BINDING]: "RunnerObject", }, unsafeEvalBinding: "__viteUnsafeEval", + serviceBindings: { + __viteFetchModule: async (request) => { + const devEnv = globalServer.environments["workerd"]; + tinyassert(devEnv); + const args = await request.json(); + const result = await devEnv.fetchModule(...(args as [any, any])); + return new MiniflareResponse(JSON.stringify(result)); + }, + }, bindings: { // TODO: server.config.root __viteRoot: process.cwd(), __viteEntry: options.entry, - } satisfies Omit, + } satisfies Omit, }; options.options?.(miniflareOptions); @@ -110,7 +132,7 @@ async function setupMiniflareManager(options: WorkerdPluginOptions) { const ns = await miniflare.getDurableObjectNamespace(RUNNER_OBJECT_BINDING); const runnerObject = ns.get(ns.idFromName("")); - const res = await runnerObject.fetch("http://test.local" + RUNNER_INIT_PATH, { + const res = await runnerObject.fetch("http://any.local" + RUNNER_INIT_PATH, { headers: { Upgrade: "websocket", }, diff --git a/examples/workerd/src/shared.ts b/examples/workerd/src/shared.ts index b9e94f1b..ac1ecfd2 100644 --- a/examples/workerd/src/shared.ts +++ b/examples/workerd/src/shared.ts @@ -6,4 +6,7 @@ export type RunnerEnv = { __viteUnsafeEval: { eval: (code: string, filename: string) => any; }; + __viteFetchModule: { + fetch: (request: Request) => Promise; + }; }; diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index 12871b08..4eae6951 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -1,6 +1,6 @@ import { tinyassert } from "@hiogawa/utils"; import { RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; -import { ModuleRunner, RemoteRunnerTransport } from "vite/module-runner"; +import { ModuleRunner } from "vite/module-runner"; export class RunnerObject implements DurableObject { #env: RunnerEnv; @@ -22,18 +22,20 @@ export class RunnerObject implements DurableObject { { root: this.#env.__viteRoot, sourcemapInterceptor: "prepareStackTrace", - // TODO: websocket for fetchModule is still too big - transport: new RemoteRunnerTransport({ - onMessage: (listener) => { - ws1.addEventListener("message", (event) => { - listener(JSON.parse(event.data)); - }); + transport: { + fetchModule: async (...args) => { + const response = await this.#env.__viteFetchModule.fetch( + new Request("https://any.local", { + method: "POST", + body: JSON.stringify(args), + }), + ); + tinyassert(response.ok); + const result = response.json(); + return result as any; }, - send: (message) => { - ws1.send(JSON.stringify(message)); - }, - }), - // TODO: spawn two pairs of websocket? + }, + // TODO: use WebSocketPair hmr: false, }, { From c50affdc4254c2c8be111c74cf7b433c6961d4be Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 22:30:29 +0900 Subject: [PATCH 15/44] fix: add vitePluginVirtualIndexHtml --- examples/react-ssr/src/entry-server.tsx | 16 +--------- examples/react-ssr/src/global.tsx | 7 ----- examples/react-ssr/src/types/virtual.d.ts | 4 +++ examples/react-ssr/vite.config.ts | 38 ++++++++++++++++++----- examples/react-ssr/vite.config.workerd.ts | 3 +- 5 files changed, 38 insertions(+), 30 deletions(-) delete mode 100644 examples/react-ssr/src/global.tsx create mode 100644 examples/react-ssr/src/types/virtual.d.ts diff --git a/examples/react-ssr/src/entry-server.tsx b/examples/react-ssr/src/entry-server.tsx index 44c5e6a5..a2682027 100644 --- a/examples/react-ssr/src/entry-server.tsx +++ b/examples/react-ssr/src/entry-server.tsx @@ -1,23 +1,9 @@ import ReactDomServer from "react-dom/server.edge"; import { Root } from "./routes"; -import { __global } from "./global"; export async function handler(_req: Request) { const ssrHtml = ReactDomServer.renderToString(); - if (1) { - return new Response(ssrHtml, { headers: { "content-type": "text/html" } }); - } - let html = await importHtml(); + let html = (await import("virtual:index.html")).default; html = html.replace(//, `
${ssrHtml}
`); return new Response(html, { headers: { "content-type": "text/html" } }); } - -async function importHtml() { - if (import.meta.env.DEV) { - const mod = await import("/index.html?raw"); - return __global.server.transformIndexHtml("/", mod.default); - } else { - const mod = await import("/dist/client/index.html?raw"); - return mod.default; - } -} diff --git a/examples/react-ssr/src/global.tsx b/examples/react-ssr/src/global.tsx deleted file mode 100644 index aba519f4..00000000 --- a/examples/react-ssr/src/global.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import type { ViteDevServer } from "vite"; - -// quick global hacks... - -export const __global: { - server: ViteDevServer; -} = ((globalThis as any).__VITE_REACT_SSR_GLOBAL ??= {}); diff --git a/examples/react-ssr/src/types/virtual.d.ts b/examples/react-ssr/src/types/virtual.d.ts new file mode 100644 index 00000000..044e09ce --- /dev/null +++ b/examples/react-ssr/src/types/virtual.d.ts @@ -0,0 +1,4 @@ +declare module "virtual:index.html" { + const src: string; + export default src; +} diff --git a/examples/react-ssr/vite.config.ts b/examples/react-ssr/vite.config.ts index e2d14aaf..dc62c0b1 100644 --- a/examples/react-ssr/vite.config.ts +++ b/examples/react-ssr/vite.config.ts @@ -4,11 +4,12 @@ import { type Plugin, createServerModuleRunner, Connect, + type ViteDevServer, } from "vite"; import { createDebug, typedBoolean } from "@hiogawa/utils"; -import { __global } from "./src/global"; import react from "@vitejs/plugin-react"; import type { ModuleRunner } from "vite/module-runner"; +import fs from "node:fs"; const debug = createDebug("app"); @@ -21,12 +22,7 @@ export default defineConfig((_env) => ({ entry: "/src/adapters/node", preview: "./dist/server/index.js", }), - { - name: "global-server", - configureServer(server) { - __global.server = server; - }, - }, + vitePluginVirtualIndexHtml(), ], environments: { client: { @@ -132,3 +128,31 @@ export function vitePluginSsrMiddleware({ }; return [plugin]; } + +export function vitePluginVirtualIndexHtml(): Plugin { + let server: ViteDevServer; + return { + name: vitePluginVirtualIndexHtml.name, + apply: "serve", + configureServer(server_) { + server = server_; + }, + resolveId(source, _importer, _options) { + return source === "virtual:index.html" ? "\0" + source : undefined; + }, + async load(id, _options) { + if (id === "\0" + "virtual:index.html") { + let html: string; + if (server) { + this.addWatchFile("index.html"); + html = await fs.promises.readFile("index.html", "utf-8"); + html = await server.transformIndexHtml("/", html); + } else { + html = await fs.promises.readFile("dist/client/index.html", "utf-8"); + } + return `export default ${JSON.stringify(html)}`; + } + return; + }, + }; +} diff --git a/examples/react-ssr/vite.config.workerd.ts b/examples/react-ssr/vite.config.workerd.ts index d27eabfd..82dd91f3 100644 --- a/examples/react-ssr/vite.config.workerd.ts +++ b/examples/react-ssr/vite.config.workerd.ts @@ -1,7 +1,7 @@ import { defineConfig } from "vite"; -import { __global } from "./src/global"; import react from "@vitejs/plugin-react"; import { vitePluginWorkerd } from "@hiogawa/vite-plugin-workerd"; +import { vitePluginVirtualIndexHtml } from "./vite.config"; export default defineConfig((_env) => ({ clearScreen: false, @@ -11,6 +11,7 @@ export default defineConfig((_env) => ({ vitePluginWorkerd({ entry: "/src/adapters/workerd.ts", }), + vitePluginVirtualIndexHtml(), ], environments: { workerd: { From f5437a298b6c11576dc6eab8842142c34e164373 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 22:32:47 +0900 Subject: [PATCH 16/44] ci: fix build --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2f24694..33f9f5cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ jobs: - run: corepack enable - run: pnpm i - run: pnpm lint-check + - run: pnpm -C examples/workerd build - run: pnpm tsc - run: npx playwright install chromium - run: pnpm -C examples/react-ssr test-e2e From eed68d80bafde930990720e8957b63cb2b811665 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 22:34:28 +0900 Subject: [PATCH 17/44] fix: fix vitePluginVirtualIndexHtml on build --- examples/react-ssr/vite.config.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/react-ssr/vite.config.ts b/examples/react-ssr/vite.config.ts index dc62c0b1..fb0c4bf6 100644 --- a/examples/react-ssr/vite.config.ts +++ b/examples/react-ssr/vite.config.ts @@ -130,10 +130,9 @@ export function vitePluginSsrMiddleware({ } export function vitePluginVirtualIndexHtml(): Plugin { - let server: ViteDevServer; + let server: ViteDevServer | undefined; return { name: vitePluginVirtualIndexHtml.name, - apply: "serve", configureServer(server_) { server = server_; }, From e705d0ba9cbd097139a542bd23d5e7cf6b2463aa Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 22:36:23 +0900 Subject: [PATCH 18/44] chore: readme --- examples/workerd/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/workerd/README.md b/examples/workerd/README.md index cdf9a5db..5448e07b 100644 --- a/examples/workerd/README.md +++ b/examples/workerd/README.md @@ -1,5 +1,9 @@ # workerd +Vite module runner on Durable Objects. + +See [`examples/react-ssr/vite.config.workerd.ts`](../react-ssr/vite.config.workerd.ts). + ## references - https://github.com/cloudflare/workers-sdk/blame/2789f26a87c769fc6177b0bdc79a839a15f4ced1/packages/miniflare/test/plugins/do/index.spec.ts From 79c9da76ea703d4ae332334f2b0fb469b0000b98 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 22:39:44 +0900 Subject: [PATCH 19/44] fix: fix vitePluginVirtualIndexHtml extension --- examples/react-ssr/src/entry-server.tsx | 2 +- examples/react-ssr/src/types/virtual.d.ts | 2 +- examples/react-ssr/vite.config.ts | 4 ++-- examples/react-ssr/vite.config.workerd.ts | 3 --- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/react-ssr/src/entry-server.tsx b/examples/react-ssr/src/entry-server.tsx index a2682027..2f43d993 100644 --- a/examples/react-ssr/src/entry-server.tsx +++ b/examples/react-ssr/src/entry-server.tsx @@ -3,7 +3,7 @@ import { Root } from "./routes"; export async function handler(_req: Request) { const ssrHtml = ReactDomServer.renderToString(); - let html = (await import("virtual:index.html")).default; + let html = (await import("virtual:index-html")).default; html = html.replace(//, `
${ssrHtml}
`); return new Response(html, { headers: { "content-type": "text/html" } }); } diff --git a/examples/react-ssr/src/types/virtual.d.ts b/examples/react-ssr/src/types/virtual.d.ts index 044e09ce..072bef8d 100644 --- a/examples/react-ssr/src/types/virtual.d.ts +++ b/examples/react-ssr/src/types/virtual.d.ts @@ -1,4 +1,4 @@ -declare module "virtual:index.html" { +declare module "virtual:index-html" { const src: string; export default src; } diff --git a/examples/react-ssr/vite.config.ts b/examples/react-ssr/vite.config.ts index fb0c4bf6..4b1264ef 100644 --- a/examples/react-ssr/vite.config.ts +++ b/examples/react-ssr/vite.config.ts @@ -137,10 +137,10 @@ export function vitePluginVirtualIndexHtml(): Plugin { server = server_; }, resolveId(source, _importer, _options) { - return source === "virtual:index.html" ? "\0" + source : undefined; + return source === "virtual:index-html" ? "\0" + source : undefined; }, async load(id, _options) { - if (id === "\0" + "virtual:index.html") { + if (id === "\0" + "virtual:index-html") { let html: string; if (server) { this.addWatchFile("index.html"); diff --git a/examples/react-ssr/vite.config.workerd.ts b/examples/react-ssr/vite.config.workerd.ts index 82dd91f3..9b6e1a24 100644 --- a/examples/react-ssr/vite.config.workerd.ts +++ b/examples/react-ssr/vite.config.workerd.ts @@ -34,9 +34,6 @@ export default defineConfig((_env) => ({ }, }, }, - optimizeDeps: { - force: true, - }, ssr: { target: "webworker", }, From 9ced3eee03642830cf76d14fd06e7d8974505847 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 22:42:21 +0900 Subject: [PATCH 20/44] test: test-e2e-workerd --- .github/workflows/ci.yml | 1 + examples/react-ssr/package.json | 1 + examples/react-ssr/playwright.config.ts | 8 ++++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33f9f5cf..05b9311a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ jobs: - run: pnpm -C examples/react-ssr test-e2e - run: pnpm -C examples/react-ssr build - run: pnpm -C examples/react-ssr test-e2e-preview + - run: pnpm -C examples/react-ssr test-e2e-workerd - run: pnpm -C examples/react-server test-e2e - run: pnpm -C examples/react-server build - run: pnpm -C examples/react-server test-e2e-preview diff --git a/examples/react-ssr/package.json b/examples/react-ssr/package.json index 28d3dda5..3e95513e 100644 --- a/examples/react-ssr/package.json +++ b/examples/react-ssr/package.json @@ -9,6 +9,7 @@ "preview": "vite preview", "test-e2e": "playwright test", "test-e2e-preview": "E2E_PREVIEW=1 playwright test", + "test-e2e-workerd": "E2E_WORKERD=1 playwright test", "vc-build": "SERVER_ENTRY=/src/adapters/vercel-edge.ts pnpm build && bash misc/vercel-edge/build.sh", "vc-release": "vercel deploy --prebuilt misc/vercel-edge --prod" }, diff --git a/examples/react-ssr/playwright.config.ts b/examples/react-ssr/playwright.config.ts index 056e6d4d..3adb79f6 100644 --- a/examples/react-ssr/playwright.config.ts +++ b/examples/react-ssr/playwright.config.ts @@ -1,10 +1,11 @@ import { defineConfig, devices } from "@playwright/test"; const port = Number(process.env["E2E_PORT"] || 6174); -const isPreview = Boolean(process.env["E2E_PREVIEW"]); -const command = isPreview +const command = process.env["E2E_PREVIEW"] ? `pnpm preview --port ${port} --strict-port` - : `pnpm dev --port ${port} --strict-port`; + : process.env["E2E_WORKERD"] + ? `pnpm dev-workerd --port ${port} --strict-port` + : `pnpm dev --port ${port} --strict-port`; export default defineConfig({ testDir: "e2e", @@ -21,7 +22,6 @@ export default defineConfig({ command, port, }, - grepInvert: isPreview ? /@dev/ : /@build/, forbidOnly: !!process.env["CI"], retries: process.env["CI"] ? 2 : 0, reporter: "list", From 8381effd3cf3c83cc530fc642ac35da92a8643a8 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 22:43:44 +0900 Subject: [PATCH 21/44] chore: remove old --- examples/workerd/src/poc/main.ts | 88 --------------------------- examples/workerd/src/poc/shared.ts | 2 - examples/workerd/src/poc/src/entry.ts | 3 - examples/workerd/src/poc/worker.ts | 63 ------------------- examples/workerd/tsup.config.ts | 7 --- 5 files changed, 163 deletions(-) delete mode 100644 examples/workerd/src/poc/main.ts delete mode 100644 examples/workerd/src/poc/shared.ts delete mode 100644 examples/workerd/src/poc/src/entry.ts delete mode 100644 examples/workerd/src/poc/worker.ts diff --git a/examples/workerd/src/poc/main.ts b/examples/workerd/src/poc/main.ts deleted file mode 100644 index 2e1e4ae1..00000000 --- a/examples/workerd/src/poc/main.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Log, Miniflare } from "miniflare"; -import { fileURLToPath } from "url"; -import { tinyassert } from "@hiogawa/utils"; -import { RUNNER_INIT_PATH, UNSAFE_EVAL_BINDING } from "./shared"; -import { DevEnvironment, RemoteEnvironmentTransport, createServer } from "vite"; - -// pnpm -C examples/workerd dev -// npx tsx examples/workerd/src/poc/main.ts - -const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; - -async function main() { - const mf = new Miniflare({ - log: new Log(), - modulesRoot: "/", - modules: [ - { - type: "ESModule", - path: fileURLToPath(new URL("./dist/worker.js", import.meta.url)), - }, - ], - durableObjects: { - [RUNNER_OBJECT_BINDING]: "RunnerObject", - }, - unsafeEvalBinding: UNSAFE_EVAL_BINDING, - }); - - const ns = await mf.getDurableObjectNamespace(RUNNER_OBJECT_BINDING); - const runnerObject = ns.get(ns.idFromName("")); - - const root = fileURLToPath(new URL(".", import.meta.url)); - const wsRes = await runnerObject.fetch( - "http://test.local" + - RUNNER_INIT_PATH + - "?" + - new URLSearchParams({ root }), - { - headers: { - Upgrade: "websocket", - }, - }, - ); - tinyassert(wsRes.webSocket); - const ws = wsRes.webSocket; - ws.accept(); - - const viteDevServer = await createServer({ - root, - configFile: false, - server: { - middlewareMode: true, - watch: null, - }, - environments: { - workerd: { - dev: { - createEnvironment: (server, name) => { - return new DevEnvironment(server, name, { - runner: { - transport: new RemoteEnvironmentTransport({ - send: (data) => ws.send(JSON.stringify(data)), - onMessage: (handler) => { - ws.addEventListener("message", (event) => { - tinyassert(typeof event.data === "string"); - handler(JSON.parse(event.data)); - }); - }, - }), - }, - }); - }, - }, - }, - }, - }); - - const res = await runnerObject.fetch("http://test.local"); - console.log("[response]", { - status: res.status, - headers: Object.fromEntries(res.headers), - text: await res.text(), - }); - - await mf.dispose(); - await viteDevServer.close(); -} - -main(); diff --git a/examples/workerd/src/poc/shared.ts b/examples/workerd/src/poc/shared.ts deleted file mode 100644 index 28e83f84..00000000 --- a/examples/workerd/src/poc/shared.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const RUNNER_INIT_PATH = "/__viteInit"; -export const UNSAFE_EVAL_BINDING = "__viteUnsafeEval"; diff --git a/examples/workerd/src/poc/src/entry.ts b/examples/workerd/src/poc/src/entry.ts deleted file mode 100644 index 31c8e820..00000000 --- a/examples/workerd/src/poc/src/entry.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default function main(): string { - return "yay!"; -} diff --git a/examples/workerd/src/poc/worker.ts b/examples/workerd/src/poc/worker.ts deleted file mode 100644 index fd61025e..00000000 --- a/examples/workerd/src/poc/worker.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { tinyassert } from "@hiogawa/utils"; -import { RUNNER_INIT_PATH, UNSAFE_EVAL_BINDING } from "./shared"; -import { ModuleRunner, RemoteRunnerTransport } from "vite/module-runner"; - -export class RunnerObject implements DurableObject { - #runner: ModuleRunner | undefined; - #env: any; - - constructor(_state: DurableObjectState, env: any) { - this.#env = env; - } - - async fetch(req: Request) { - const url = new URL(req.url); - - if (url.pathname === RUNNER_INIT_PATH) { - const { 0: ws1, 1: ws2 } = new WebSocketPair(); - (ws1 as any).accept(); - - tinyassert(!this.#runner); - const root = url.searchParams.get("root"); - tinyassert(root); - this.#runner = new ModuleRunner( - { - root, - sourcemapInterceptor: "prepareStackTrace", - transport: new RemoteRunnerTransport({ - onMessage: (listener) => { - ws1.addEventListener("message", (event) => { - listener(JSON.parse(event.data)); - }); - }, - send: (message) => { - ws1.send(JSON.stringify(message)); - }, - }), - // TODO - hmr: false, - }, - { - runInlinedModule: async (context, transformed, id) => { - // TODO: check stacktrace - const codeDefinition = `'use strict';async (${Object.keys( - context, - ).join(",")})=>{{`; - const code = `${codeDefinition}${transformed}\n}}`; - const fn = this.#env[UNSAFE_EVAL_BINDING].eval(code, id); - await fn(...Object.values(context)); - Object.freeze(context.__vite_ssr_exports__); - }, - runExternalModule(filepath) { - return import(filepath); - }, - }, - ); - return new Response(null, { status: 101, webSocket: ws2 }); - } - - tinyassert(this.#runner); - const mod = await this.#runner.import("/src/entry.ts"); - return new Response(mod.default()); - } -} diff --git a/examples/workerd/tsup.config.ts b/examples/workerd/tsup.config.ts index 404b3e34..ceaf57f5 100644 --- a/examples/workerd/tsup.config.ts +++ b/examples/workerd/tsup.config.ts @@ -1,13 +1,6 @@ import { defineConfig } from "tsup"; export default [ - defineConfig({ - outDir: "src/poc/dist", - entry: ["src/poc/worker.ts"], - format: ["esm"], - platform: "browser", - noExternal: [/.*/], - }), defineConfig({ entry: ["src/worker.ts"], format: ["esm"], From db4993ad051bbb178c4aa0e5e6e36cb4e8484a43 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 8 Apr 2024 22:44:37 +0900 Subject: [PATCH 22/44] chore: comment --- examples/workerd/src/plugin.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index e5a77def..0f2c37f0 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -38,8 +38,7 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { // [feedback] // `createEnvironment` should be async? - // otherwise this complicated miniflare setup has to be done outside - // without access to `ViteDevServer`, `ResvoledConfig` etc... + // otherwise this complicated miniflare setup has to be done outside of `createEnvironment` manager = await setupMiniflareManager(pluginOptions); const ws = manager.webSocket; From 0aa270325cdaee1da895bcaa4638114204433ea7 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 00:13:23 +0900 Subject: [PATCH 23/44] wip: hmr --- examples/workerd/src/plugin.ts | 23 +++++------------------ examples/workerd/src/worker.ts | 21 ++++++++++++++++----- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 0f2c37f0..20c22694 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -8,12 +8,7 @@ import { import { fileURLToPath } from "url"; import { tinyassert } from "@hiogawa/utils"; import { RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; -import { - DevEnvironment, - RemoteEnvironmentTransport, - type Plugin, - type ViteDevServer, -} from "vite"; +import { DevEnvironment, type Plugin, type ViteDevServer } from "vite"; import { createMiddleware } from "@hattip/adapter-node/native-fetch"; interface WorkerdPluginOptions { @@ -40,7 +35,7 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { // `createEnvironment` should be async? // otherwise this complicated miniflare setup has to be done outside of `createEnvironment` manager = await setupMiniflareManager(pluginOptions); - const ws = manager.webSocket; + const webSocket = manager.webSocket; return { environments: { @@ -48,19 +43,11 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { dev: { createEnvironment(server, name) { globalServer = server; + webSocket; return new DevEnvironment(server, name, { - runner: { - transport: new RemoteEnvironmentTransport({ - send: (data) => ws.send(JSON.stringify(data)), - onMessage: (handler) => { - ws.addEventListener("message", (event) => { - tinyassert(typeof event.data === "string"); - handler(JSON.parse(event.data)); - }); - }, - }), - }, + // TODO + hot: false, }); }, }, diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index 4eae6951..b3ceb7a7 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -14,8 +14,8 @@ export class RunnerObject implements DurableObject { const url = new URL(request.url); if (url.pathname === RUNNER_INIT_PATH) { - const { 0: ws1, 1: ws2 } = new WebSocketPair(); - (ws1 as any).accept(); + const { 0: webSocket, 1: webSocketReturn } = new WebSocketPair(); + (webSocket as any).accept(); tinyassert(!this.#runner); this.#runner = new ModuleRunner( @@ -35,8 +35,19 @@ export class RunnerObject implements DurableObject { return result as any; }, }, - // TODO: use WebSocketPair - hmr: false, + hmr: { + connection: { + isReady: () => true, + onUpdate(callback) { + webSocket.addEventListener("message", (event) => { + callback(JSON.parse(event.data)); + }); + }, + send(messages) { + webSocket.send(JSON.stringify(messages)); + }, + }, + }, }, { runInlinedModule: async (context, transformed, id) => { @@ -54,7 +65,7 @@ export class RunnerObject implements DurableObject { }, }, ); - return new Response(null, { status: 101, webSocket: ws2 }); + return new Response(null, { status: 101, webSocket: webSocketReturn }); } tinyassert(this.#runner); From 0d843ffbe04bc0f31ddd8118dfbcfe4e899d0e94 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 00:35:37 +0900 Subject: [PATCH 24/44] wip: implement hot --- examples/workerd/src/plugin.ts | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 20c22694..2fc58b74 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -43,11 +43,30 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { dev: { createEnvironment(server, name) { globalServer = server; - webSocket; return new DevEnvironment(server, name, { - // TODO - hot: false, + hot: { + name, + close() {}, + listen() {}, + // cf. createServerHMRChannel + send(...args: any[]) { + let payload: any; + if (typeof args[0] === "string") { + payload = { + type: "custom", + event: args[0], + data: args[1], + }; + } else { + payload = args[0]; + } + webSocket.send(JSON.stringify(payload)); + }, + // TODO: for custom event e.g. vite:invalidate + on() {}, + off() {}, + }, }); }, }, From f6fc3a70848cf52b151aed63a4568701318e238d Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 08:02:52 +0900 Subject: [PATCH 25/44] chore: vite v6.0.0-alpha.1 --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 20163635..922e0fb2 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "tsup": "^8.0.2", "tsx": "^4.7.1", "typescript": "^5.4.3", - "vite": "6.0.0-alpha.0" + "vite": "6.0.0-alpha.1" }, "packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589", "volta": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a64e7762..e62c8bab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - vite: 6.0.0-alpha.0 + vite: 6.0.0-alpha.1 importers: @@ -28,7 +28,7 @@ importers: version: 20.11.30 '@vitejs/plugin-react': specifier: ^4.2.1 - version: 4.2.1(vite@6.0.0-alpha.0) + version: 4.2.1(vite@6.0.0-alpha.1) esbuild: specifier: ^0.20.2 version: 0.20.2 @@ -45,8 +45,8 @@ importers: specifier: ^5.4.3 version: 5.4.3 vite: - specifier: 6.0.0-alpha.0 - version: 6.0.0-alpha.0(@types/node@20.11.30) + specifier: 6.0.0-alpha.1 + version: 6.0.0-alpha.1(@types/node@20.11.30) examples/custom: dependencies: @@ -1100,18 +1100,18 @@ packages: csstype: 3.1.3 dev: true - /@vitejs/plugin-react@4.2.1(vite@6.0.0-alpha.0): + /@vitejs/plugin-react@4.2.1(vite@6.0.0-alpha.1): resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: 6.0.0-alpha.0 + vite: 6.0.0-alpha.1 dependencies: '@babel/core': 7.24.3 '@babel/plugin-transform-react-jsx-self': 7.24.1(@babel/core@7.24.3) '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.3) '@types/babel__core': 7.20.5 react-refresh: 0.14.0 - vite: 6.0.0-alpha.0(@types/node@20.11.30) + vite: 6.0.0-alpha.1(@types/node@20.11.30) transitivePeerDependencies: - supports-color dev: true @@ -2586,8 +2586,8 @@ packages: resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} dev: true - /vite@6.0.0-alpha.0(@types/node@20.11.30): - resolution: {integrity: sha512-NBYF7KOkASr2SRgknZD4hjSrpkJ9IpP5XMWBSidw66TTtKR5oplE/ONIRd8HKH07QwYVbKadXhP6/jQZfab9xA==} + /vite@6.0.0-alpha.1(@types/node@20.11.30): + resolution: {integrity: sha512-LVvQVG3AftURxT11TgWXpfVIqBKC4D/NA5ikYQb9hDORF0hvO+gsyvNu8GR73/oag09A5JJAPAFU+r7ly4+9Lw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: From 07fae6cc711f2ec825a60672a7a690de8dd046a1 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 08:52:35 +0900 Subject: [PATCH 26/44] refactor: async createEnvironment --- examples/workerd/src/plugin.ts | 126 +++++++++++++++++---------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 2fc58b74..e52ada4a 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -16,58 +16,20 @@ interface WorkerdPluginOptions { options?: (v: MiniflareOptions & WorkerOptions) => void; } -let globalServer: ViteDevServer; - export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { - let manager: MiniflareManager | undefined; - let dispose = async () => { - await manager?.miniflare.dispose(); - manager = undefined; - }; - return { name: vitePluginWorkerd.name, - apply: "serve", async config(_config, _env) { - dispose(); - - // [feedback] - // `createEnvironment` should be async? - // otherwise this complicated miniflare setup has to be done outside of `createEnvironment` - manager = await setupMiniflareManager(pluginOptions); - const webSocket = manager.webSocket; - return { environments: { workerd: { dev: { - createEnvironment(server, name) { - globalServer = server; - - return new DevEnvironment(server, name, { - hot: { - name, - close() {}, - listen() {}, - // cf. createServerHMRChannel - send(...args: any[]) { - let payload: any; - if (typeof args[0] === "string") { - payload = { - type: "custom", - event: args[0], - data: args[1], - }; - } else { - payload = args[0]; - } - webSocket.send(JSON.stringify(payload)); - }, - // TODO: for custom event e.g. vite:invalidate - on() {}, - off() {}, - }, - }); + async createEnvironment(server, name) { + const manager = await setupMiniflareManager( + server, + pluginOptions, + ); + return new WorkerdDevEnvironment(server, name, manager); }, }, }, @@ -77,29 +39,70 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { configureServer(server) { return () => { + // TODO: allow interface merging for custom environment to provide a typing? + const devEnv = server.environments["workerd"]; + tinyassert(devEnv instanceof WorkerdDevEnvironment); + server.middlewares.use( - createMiddleware( - (ctx) => { - tinyassert(manager); - return manager.dispatchFetch(ctx.request); - }, - { - alwaysCallNext: false, - }, - ), + createMiddleware((ctx) => devEnv.api.fetch(ctx.request), { + alwaysCallNext: false, + }), ); }; }, - - async buildEnd() { - await dispose(); - }, }; } +class WorkerdDevEnvironment extends DevEnvironment { + constructor( + server: ViteDevServer, + name: string, + public manager: MiniflareManager, + ) { + super(server, name, { + hot: { + name, + close() {}, + listen() {}, + // cf. createServerHMRChannel + send(...args: any[]) { + let payload: any; + if (typeof args[0] === "string") { + payload = { + type: "custom", + event: args[0], + data: args[1], + }; + } else { + payload = args[0]; + } + manager.webSocket.send(JSON.stringify(payload)); + }, + // TODO: for custom event e.g. vite:invalidate + on() {}, + off() {}, + }, + }); + } + + override async close() { + await super.close(); + await this.manager.miniflare.dispose(); + } + + get api() { + return { + fetch: this.manager.dispatchFetch, + }; + } +} + type MiniflareManager = Awaited>; -async function setupMiniflareManager(options: WorkerdPluginOptions) { +async function setupMiniflareManager( + server: ViteDevServer, + options: WorkerdPluginOptions, +) { const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; const miniflareOptions: MiniflareOptions = { @@ -117,7 +120,7 @@ async function setupMiniflareManager(options: WorkerdPluginOptions) { unsafeEvalBinding: "__viteUnsafeEval", serviceBindings: { __viteFetchModule: async (request) => { - const devEnv = globalServer.environments["workerd"]; + const devEnv = server.environments["workerd"]; tinyassert(devEnv); const args = await request.json(); const result = await devEnv.fetchModule(...(args as [any, any])); @@ -125,8 +128,7 @@ async function setupMiniflareManager(options: WorkerdPluginOptions) { }, }, bindings: { - // TODO: server.config.root - __viteRoot: process.cwd(), + __viteRoot: server.config.root, __viteEntry: options.entry, } satisfies Omit, }; @@ -157,5 +159,5 @@ async function setupMiniflareManager(options: WorkerdPluginOptions) { return response as any as Response; } - return { miniflare, runnerObject, webSocket, dispatchFetch }; + return { miniflare, webSocket, dispatchFetch }; } From a78fd3c1f97c0135cc96ef70e114f128fa491418 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 09:25:13 +0900 Subject: [PATCH 27/44] refactor: createWorkerdDevEnvironment --- examples/workerd/src/plugin.ts | 162 +++++++++++++++++---------------- 1 file changed, 82 insertions(+), 80 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index e52ada4a..6ac10a91 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -8,7 +8,12 @@ import { import { fileURLToPath } from "url"; import { tinyassert } from "@hiogawa/utils"; import { RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; -import { DevEnvironment, type Plugin, type ViteDevServer } from "vite"; +import { + DevEnvironment, + type HMRChannel, + type Plugin, + type ViteDevServer, +} from "vite"; import { createMiddleware } from "@hattip/adapter-node/native-fetch"; interface WorkerdPluginOptions { @@ -24,13 +29,8 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { environments: { workerd: { dev: { - async createEnvironment(server, name) { - const manager = await setupMiniflareManager( - server, - pluginOptions, - ); - return new WorkerdDevEnvironment(server, name, manager); - }, + createEnvironment: (server, name) => + createWorkerdDevEnvironment(server, name, pluginOptions), }, }, }, @@ -39,9 +39,8 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { configureServer(server) { return () => { - // TODO: allow interface merging for custom environment to provide a typing? - const devEnv = server.environments["workerd"]; - tinyassert(devEnv instanceof WorkerdDevEnvironment); + // TODO: allow interface merging so users can register typing? + const devEnv = server.environments["workerd"] as WorkerdDevEnvironment; server.middlewares.use( createMiddleware((ctx) => devEnv.api.fetch(ctx.request), { @@ -53,58 +52,20 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { }; } -class WorkerdDevEnvironment extends DevEnvironment { - constructor( - server: ViteDevServer, - name: string, - public manager: MiniflareManager, - ) { - super(server, name, { - hot: { - name, - close() {}, - listen() {}, - // cf. createServerHMRChannel - send(...args: any[]) { - let payload: any; - if (typeof args[0] === "string") { - payload = { - type: "custom", - event: args[0], - data: args[1], - }; - } else { - payload = args[0]; - } - manager.webSocket.send(JSON.stringify(payload)); - }, - // TODO: for custom event e.g. vite:invalidate - on() {}, - off() {}, - }, - }); - } - - override async close() { - await super.close(); - await this.manager.miniflare.dispose(); - } - - get api() { - return { - fetch: this.manager.dispatchFetch, - }; - } -} +export type WorkerdDevEnvironment = DevEnvironment & { + api: WorkerdDevEnvironmentApi; +}; -type MiniflareManager = Awaited>; +type WorkerdDevEnvironmentApi = { + fetch(request: Request): Promise; +}; -async function setupMiniflareManager( +async function createWorkerdDevEnvironment( server: ViteDevServer, - options: WorkerdPluginOptions, + name: string, + pluginOptions: WorkerdPluginOptions, ) { - const RUNNER_OBJECT_BINDING = "__VITE_RUNNER"; - + // setup miniflare with a durable object script const miniflareOptions: MiniflareOptions = { log: new Log(), modulesRoot: "/", @@ -115,7 +76,7 @@ async function setupMiniflareManager( }, ], durableObjects: { - [RUNNER_OBJECT_BINDING]: "RunnerObject", + __viteRunner: "RunnerObject", }, unsafeEvalBinding: "__viteUnsafeEval", serviceBindings: { @@ -129,35 +90,76 @@ async function setupMiniflareManager( }, bindings: { __viteRoot: server.config.root, - __viteEntry: options.entry, + // TODO: can set entry later? + __viteEntry: pluginOptions.entry, } satisfies Omit, }; - options.options?.(miniflareOptions); - + pluginOptions.options?.(miniflareOptions); const miniflare = new Miniflare(miniflareOptions); - const ns = await miniflare.getDurableObjectNamespace(RUNNER_OBJECT_BINDING); + // get durable object singleton + const ns = await miniflare.getDurableObjectNamespace("__viteRunner"); const runnerObject = ns.get(ns.idFromName("")); - const res = await runnerObject.fetch("http://any.local" + RUNNER_INIT_PATH, { - headers: { - Upgrade: "websocket", + // initial request to setup websocket + const initResponse = await runnerObject.fetch( + "http://any.local" + RUNNER_INIT_PATH, + { + headers: { + Upgrade: "websocket", + }, }, - }); - tinyassert(res.webSocket); - const { webSocket } = res; + ); + tinyassert(initResponse.webSocket); + const { webSocket } = initResponse; webSocket.accept(); - async function dispatchFetch(request: Request) { - const response = await runnerObject.fetch(request.url, { - method: request.method, - headers: request.headers, - body: request.body as any, - duplex: "half", - redirect: "manual", - }); - return response as any as Response; + // vite environment hot + const hot: HMRChannel = { + name, + close() {}, + listen() {}, + // cf. createServerHMRChannel + send(...args: any[]) { + let payload: any; + if (typeof args[0] === "string") { + payload = { + type: "custom", + event: args[0], + data: args[1], + }; + } else { + payload = args[0]; + } + webSocket.send(JSON.stringify(payload)); + }, + // TODO: for custom event e.g. vite:invalidate + on() {}, + off() {}, + }; + + class WorkerdDevEnvironment extends DevEnvironment { + override async close() { + await super.close(); + await miniflare.dispose(); + } + + // TODO: custom api for environment users? + // TODO: can proxy entire `SELF` like vitest integration? + // https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/ + api: WorkerdDevEnvironmentApi = { + async fetch(request: Request) { + const response = await runnerObject.fetch(request.url, { + method: request.method, + headers: request.headers, + body: request.body as any, + duplex: "half", + redirect: "manual", + }); + return response as any as Response; + }, + }; } - return { miniflare, webSocket, dispatchFetch }; + return new WorkerdDevEnvironment(server, name, { hot }); } From 20dfa8aa248950af4881bb24667b2557d48abc6d Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 09:55:31 +0900 Subject: [PATCH 28/44] refactor: minor --- examples/workerd/src/plugin.ts | 29 +++++++++++++++++++---------- examples/workerd/src/shared.ts | 1 + examples/workerd/src/worker.ts | 7 ++++--- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 6ac10a91..9d077dc5 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -3,11 +3,12 @@ import { Miniflare, type MiniflareOptions, type WorkerOptions, + Request as MiniflareRequest, Response as MiniflareResponse, } from "miniflare"; import { fileURLToPath } from "url"; import { tinyassert } from "@hiogawa/utils"; -import { RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; +import { ANY_URL, RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; import { DevEnvironment, type HMRChannel, @@ -57,6 +58,7 @@ export type WorkerdDevEnvironment = DevEnvironment & { }; type WorkerdDevEnvironmentApi = { + setEntry(entry: string): void; fetch(request: Request): Promise; }; @@ -102,14 +104,11 @@ async function createWorkerdDevEnvironment( const runnerObject = ns.get(ns.idFromName("")); // initial request to setup websocket - const initResponse = await runnerObject.fetch( - "http://any.local" + RUNNER_INIT_PATH, - { - headers: { - Upgrade: "websocket", - }, + const initResponse = await runnerObject.fetch(ANY_URL + RUNNER_INIT_PATH, { + headers: { + Upgrade: "websocket", }, - ); + }); tinyassert(initResponse.webSocket); const { webSocket } = initResponse; webSocket.accept(); @@ -138,6 +137,7 @@ async function createWorkerdDevEnvironment( off() {}, }; + // inheritance to extend dispose + custom api class WorkerdDevEnvironment extends DevEnvironment { override async close() { await super.close(); @@ -148,15 +148,24 @@ async function createWorkerdDevEnvironment( // TODO: can proxy entire `SELF` like vitest integration? // https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/ api: WorkerdDevEnvironmentApi = { + setEntry(entry) { + entry; + }, + async fetch(request: Request) { - const response = await runnerObject.fetch(request.url, { + const req = new MiniflareRequest(request.url, { method: request.method, headers: request.headers, body: request.body as any, duplex: "half", redirect: "manual", }); - return response as any as Response; + const res = await runnerObject.fetch(req); + return new Response(res.body as any, { + status: res.status, + statusText: res.statusText, + headers: res.headers as any, + }); }, }; } diff --git a/examples/workerd/src/shared.ts b/examples/workerd/src/shared.ts index ac1ecfd2..aed521e5 100644 --- a/examples/workerd/src/shared.ts +++ b/examples/workerd/src/shared.ts @@ -1,4 +1,5 @@ export const RUNNER_INIT_PATH = "/__viteInit"; +export const ANY_URL = "https://any.local"; export type RunnerEnv = { __viteRoot: string; diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index b3ceb7a7..3b5d40fa 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -1,10 +1,11 @@ import { tinyassert } from "@hiogawa/utils"; -import { RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; +import { ANY_URL, RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; import { ModuleRunner } from "vite/module-runner"; export class RunnerObject implements DurableObject { #env: RunnerEnv; - #runner: ModuleRunner | undefined; + #runner?: ModuleRunner; + // #entry?: string; constructor(_state: DurableObjectState, env: RunnerEnv) { this.#env = env; @@ -25,7 +26,7 @@ export class RunnerObject implements DurableObject { transport: { fetchModule: async (...args) => { const response = await this.#env.__viteFetchModule.fetch( - new Request("https://any.local", { + new Request(ANY_URL, { method: "POST", body: JSON.stringify(args), }), From 371285afad85afb43349628b0e71149dcb9d5cc9 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 09:57:30 +0900 Subject: [PATCH 29/44] refactor: move code --- examples/workerd/src/worker.ts | 99 +++++++++++++++++----------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index 3b5d40fa..c49e1510 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -17,55 +17,8 @@ export class RunnerObject implements DurableObject { if (url.pathname === RUNNER_INIT_PATH) { const { 0: webSocket, 1: webSocketReturn } = new WebSocketPair(); (webSocket as any).accept(); - tinyassert(!this.#runner); - this.#runner = new ModuleRunner( - { - root: this.#env.__viteRoot, - sourcemapInterceptor: "prepareStackTrace", - transport: { - fetchModule: async (...args) => { - const response = await this.#env.__viteFetchModule.fetch( - new Request(ANY_URL, { - method: "POST", - body: JSON.stringify(args), - }), - ); - tinyassert(response.ok); - const result = response.json(); - return result as any; - }, - }, - hmr: { - connection: { - isReady: () => true, - onUpdate(callback) { - webSocket.addEventListener("message", (event) => { - callback(JSON.parse(event.data)); - }); - }, - send(messages) { - webSocket.send(JSON.stringify(messages)); - }, - }, - }, - }, - { - runInlinedModule: async (context, transformed, id) => { - const codeDefinition = `'use strict';async (${Object.keys( - context, - ).join(",")})=>{{`; - const code = `${codeDefinition}${transformed}\n}}`; - const fn = this.#env.__viteUnsafeEval.eval(code, id); - await fn(...Object.values(context)); - Object.freeze(context.__vite_ssr_exports__); - }, - async runExternalModule(filepath) { - console.log("[runExternalModule]", filepath); - return import(filepath); - }, - }, - ); + this.#runner = createRunner(this.#env, webSocket); return new Response(null, { status: 101, webSocket: webSocketReturn }); } @@ -74,3 +27,53 @@ export class RunnerObject implements DurableObject { return mod.default.fetch(request, this.#env); } } + +function createRunner(env: RunnerEnv, webSocket: WebSocket) { + return new ModuleRunner( + { + root: env.__viteRoot, + sourcemapInterceptor: "prepareStackTrace", + transport: { + fetchModule: async (...args) => { + const response = await env.__viteFetchModule.fetch( + new Request(ANY_URL, { + method: "POST", + body: JSON.stringify(args), + }), + ); + tinyassert(response.ok); + const result = response.json(); + return result as any; + }, + }, + hmr: { + connection: { + isReady: () => true, + onUpdate(callback) { + webSocket.addEventListener("message", (event) => { + callback(JSON.parse(event.data)); + }); + }, + send(messages) { + webSocket.send(JSON.stringify(messages)); + }, + }, + }, + }, + { + runInlinedModule: async (context, transformed, id) => { + const codeDefinition = `'use strict';async (${Object.keys(context).join( + ",", + )})=>{{`; + const code = `${codeDefinition}${transformed}\n}}`; + const fn = env.__viteUnsafeEval.eval(code, id); + await fn(...Object.values(context)); + Object.freeze(context.__vite_ssr_exports__); + }, + async runExternalModule(filepath) { + console.log("[runExternalModule]", filepath); + return import(filepath); + }, + }, + ); +} From 99f545c457ce0d1fe928592d2f7254e3f2aa0976 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 10:50:59 +0900 Subject: [PATCH 30/44] feat: vitest-pool-workers like options --- examples/react-ssr/vite.config.workerd.ts | 6 + examples/workerd/package.json | 18 +- examples/workerd/src/plugin.ts | 39 +- examples/workerd/src/worker.ts | 8 +- pnpm-lock.yaml | 506 +++++++++++++++++++--- 5 files changed, 499 insertions(+), 78 deletions(-) diff --git a/examples/react-ssr/vite.config.workerd.ts b/examples/react-ssr/vite.config.workerd.ts index 9b6e1a24..55e75d10 100644 --- a/examples/react-ssr/vite.config.workerd.ts +++ b/examples/react-ssr/vite.config.workerd.ts @@ -10,6 +10,12 @@ export default defineConfig((_env) => ({ react(), vitePluginWorkerd({ entry: "/src/adapters/workerd.ts", + // miniflare: { + // kvNamespaces: [], + // }, + // wrangler: { + // configPath: "./wrangler.toml", + // }, }), vitePluginVirtualIndexHtml(), ], diff --git a/examples/workerd/package.json b/examples/workerd/package.json index 8d407fda..be1aa028 100644 --- a/examples/workerd/package.json +++ b/examples/workerd/package.json @@ -12,9 +12,23 @@ "dev": "tsup --watch", "build": "tsup" }, + "dependencies": { + "miniflare": "^3.20240404.0", + "wrangler": "^3.48.0", + "vite": "*" + }, "devDependencies": { - "@cloudflare/workers-types": "^4.20240405.0", - "miniflare": "^3.20240404.0" + "@cloudflare/workers-types": "^4.20240405.0" + }, + "peerDependencies": { + "miniflare": "*", + "wrangler": "*", + "vite": "*" + }, + "peerDependenciesMeta": { + "wrangler": { + "optional": true + } }, "volta": { "extends": "../../package.json" diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 9d077dc5..ca0a01c9 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -1,10 +1,10 @@ import { Log, Miniflare, - type MiniflareOptions, type WorkerOptions, Request as MiniflareRequest, Response as MiniflareResponse, + mergeWorkerOptions, } from "miniflare"; import { fileURLToPath } from "url"; import { tinyassert } from "@hiogawa/utils"; @@ -16,10 +16,14 @@ import { type ViteDevServer, } from "vite"; import { createMiddleware } from "@hattip/adapter-node/native-fetch"; +import type { SourcelessWorkerOptions } from "wrangler"; interface WorkerdPluginOptions { entry: string; - options?: (v: MiniflareOptions & WorkerOptions) => void; + miniflare?: SourcelessWorkerOptions; + wrangler?: { + configPath?: string; + }; } export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { @@ -67,9 +71,8 @@ async function createWorkerdDevEnvironment( name: string, pluginOptions: WorkerdPluginOptions, ) { - // setup miniflare with a durable object script - const miniflareOptions: MiniflareOptions = { - log: new Log(), + // setup miniflare with a durable object script to run vite module runner + let runnerWorkerOptions: WorkerOptions = { modulesRoot: "/", modules: [ { @@ -96,8 +99,30 @@ async function createWorkerdDevEnvironment( __viteEntry: pluginOptions.entry, } satisfies Omit, }; - pluginOptions.options?.(miniflareOptions); - const miniflare = new Miniflare(miniflareOptions); + + // https://github.com/cloudflare/workers-sdk/blob/2789f26a87c769fc6177b0bdc79a839a15f4ced1/packages/vitest-pool-workers/src/pool/config.ts#L174-L195 + if (pluginOptions.wrangler?.configPath) { + const wrangler = await import("wrangler"); + const wranglerOptions = wrangler.unstable_getMiniflareWorkerOptions( + pluginOptions.wrangler.configPath, + ); + runnerWorkerOptions = mergeWorkerOptions( + wranglerOptions.workerOptions, + runnerWorkerOptions, + ) as WorkerOptions; + } + + if (pluginOptions.miniflare) { + runnerWorkerOptions = mergeWorkerOptions( + pluginOptions.miniflare, + runnerWorkerOptions, + ) as WorkerOptions; + } + + const miniflare = new Miniflare({ + log: new Log(), + workers: [runnerWorkerOptions], + }); // get durable object singleton const ns = await miniflare.getDurableObjectNamespace("__viteRunner"); diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index c49e1510..3cacc464 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -15,11 +15,11 @@ export class RunnerObject implements DurableObject { const url = new URL(request.url); if (url.pathname === RUNNER_INIT_PATH) { - const { 0: webSocket, 1: webSocketReturn } = new WebSocketPair(); - (webSocket as any).accept(); + const pair = new WebSocketPair(); + (pair[0] as any).accept(); tinyassert(!this.#runner); - this.#runner = createRunner(this.#env, webSocket); - return new Response(null, { status: 101, webSocket: webSocketReturn }); + this.#runner = createRunner(this.#env, pair[0]); + return new Response(null, { status: 101, webSocket: pair[1] }); } tinyassert(this.#runner); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e62c8bab..57ac7627 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -103,13 +103,20 @@ importers: version: 18.2.22 examples/workerd: + dependencies: + miniflare: + specifier: ^3.20240404.0 + version: 3.20240404.0 + vite: + specifier: 6.0.0-alpha.1 + version: 6.0.0-alpha.1(@types/node@20.11.30) + wrangler: + specifier: ^3.48.0 + version: 3.48.0(@cloudflare/workers-types@4.20240405.0) devDependencies: '@cloudflare/workers-types': specifier: ^4.20240405.0 version: 4.20240405.0 - miniflare: - specifier: ^3.20240404.0 - version: 3.20240404.0 packages: @@ -338,13 +345,19 @@ packages: to-fast-properties: 2.0.0 dev: true + /@cloudflare/kv-asset-handler@0.3.1: + resolution: {integrity: sha512-lKN2XCfKCmpKb86a1tl4GIwsJYDy9TGuwjhDELLmpKygQhw8X2xR4dusgpC5Tg7q1pB96Eb0rBo81kxSILQMwA==} + dependencies: + mime: 3.0.0 + dev: false + /@cloudflare/workerd-darwin-64@1.20240404.0: resolution: {integrity: sha512-rc/ov3I9GwgKRtUnkShNW3TIoZEPHzExrMRNlHq1VpXQRBSchHdMw8meMn54+oqgxW1AKLmPWj/c0A7EnYAsIw==} engines: {node: '>=16'} cpu: [x64] os: [darwin] requiresBuild: true - dev: true + dev: false optional: true /@cloudflare/workerd-darwin-arm64@1.20240404.0: @@ -353,7 +366,7 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: true + dev: false optional: true /@cloudflare/workerd-linux-64@1.20240404.0: @@ -362,7 +375,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true + dev: false optional: true /@cloudflare/workerd-linux-arm64@1.20240404.0: @@ -371,7 +384,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true + dev: false optional: true /@cloudflare/workerd-windows-64@1.20240404.0: @@ -380,19 +393,36 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: true + dev: false optional: true /@cloudflare/workers-types@4.20240405.0: resolution: {integrity: sha512-sEVOhyOgXUwfLkgHqbLZa/sfkSYrh7/zLmI6EZNibPaVPvAnAcItbNNl3SAlLyLKuwf8m4wAIAgu9meKWCvXjg==} - dev: true /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 - dev: true + dev: false + + /@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.17.19): + resolution: {integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==} + peerDependencies: + esbuild: '*' + dependencies: + esbuild: 0.17.19 + dev: false + + /@esbuild-plugins/node-modules-polyfill@0.2.2(esbuild@0.17.19): + resolution: {integrity: sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==} + peerDependencies: + esbuild: '*' + dependencies: + esbuild: 0.17.19 + escape-string-regexp: 4.0.0 + rollup-plugin-node-polyfills: 0.2.1 + dev: false /@esbuild/aix-ppc64@0.19.12: resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} @@ -411,6 +441,15 @@ packages: requiresBuild: true optional: true + /@esbuild/android-arm64@0.17.19: + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + /@esbuild/android-arm64@0.19.12: resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} engines: {node: '>=12'} @@ -428,6 +467,15 @@ packages: requiresBuild: true optional: true + /@esbuild/android-arm@0.17.19: + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + /@esbuild/android-arm@0.19.12: resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} engines: {node: '>=12'} @@ -445,6 +493,15 @@ packages: requiresBuild: true optional: true + /@esbuild/android-x64@0.17.19: + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: false + optional: true + /@esbuild/android-x64@0.19.12: resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} engines: {node: '>=12'} @@ -462,6 +519,15 @@ packages: requiresBuild: true optional: true + /@esbuild/darwin-arm64@0.17.19: + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + /@esbuild/darwin-arm64@0.19.12: resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} engines: {node: '>=12'} @@ -479,6 +545,15 @@ packages: requiresBuild: true optional: true + /@esbuild/darwin-x64@0.17.19: + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + /@esbuild/darwin-x64@0.19.12: resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} engines: {node: '>=12'} @@ -496,6 +571,15 @@ packages: requiresBuild: true optional: true + /@esbuild/freebsd-arm64@0.17.19: + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + /@esbuild/freebsd-arm64@0.19.12: resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} engines: {node: '>=12'} @@ -513,6 +597,15 @@ packages: requiresBuild: true optional: true + /@esbuild/freebsd-x64@0.17.19: + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + /@esbuild/freebsd-x64@0.19.12: resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} engines: {node: '>=12'} @@ -530,6 +623,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-arm64@0.17.19: + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-arm64@0.19.12: resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} engines: {node: '>=12'} @@ -547,6 +649,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-arm@0.17.19: + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-arm@0.19.12: resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} engines: {node: '>=12'} @@ -564,6 +675,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-ia32@0.17.19: + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-ia32@0.19.12: resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} engines: {node: '>=12'} @@ -581,6 +701,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-loong64@0.17.19: + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-loong64@0.19.12: resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} engines: {node: '>=12'} @@ -598,6 +727,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-mips64el@0.17.19: + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-mips64el@0.19.12: resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} engines: {node: '>=12'} @@ -615,6 +753,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-ppc64@0.17.19: + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-ppc64@0.19.12: resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} engines: {node: '>=12'} @@ -632,6 +779,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-riscv64@0.17.19: + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-riscv64@0.19.12: resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} engines: {node: '>=12'} @@ -649,6 +805,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-s390x@0.17.19: + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-s390x@0.19.12: resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} engines: {node: '>=12'} @@ -666,6 +831,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-x64@0.17.19: + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-x64@0.19.12: resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} engines: {node: '>=12'} @@ -683,6 +857,15 @@ packages: requiresBuild: true optional: true + /@esbuild/netbsd-x64@0.17.19: + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: false + optional: true + /@esbuild/netbsd-x64@0.19.12: resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} engines: {node: '>=12'} @@ -700,6 +883,15 @@ packages: requiresBuild: true optional: true + /@esbuild/openbsd-x64@0.17.19: + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: false + optional: true + /@esbuild/openbsd-x64@0.19.12: resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} engines: {node: '>=12'} @@ -717,6 +909,15 @@ packages: requiresBuild: true optional: true + /@esbuild/sunos-x64@0.17.19: + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: false + optional: true + /@esbuild/sunos-x64@0.19.12: resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} engines: {node: '>=12'} @@ -734,6 +935,15 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-arm64@0.17.19: + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@esbuild/win32-arm64@0.19.12: resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} engines: {node: '>=12'} @@ -751,6 +961,15 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-ia32@0.17.19: + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@esbuild/win32-ia32@0.19.12: resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} engines: {node: '>=12'} @@ -768,6 +987,15 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-x64@0.17.19: + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@esbuild/win32-x64@0.19.12: resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} engines: {node: '>=12'} @@ -788,7 +1016,7 @@ packages: /@fastify/busboy@2.1.1: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - dev: true + dev: false /@hattip/adapter-node@0.0.44: resolution: {integrity: sha512-Qf0kcrE4yHFrmKgfntrxHGNkk0/9a45aKmAO3ex6OiRKat5DZCFDdFMHV85Z594IohzT7Q+WcWiHQl/b0Jvo7Q==} @@ -878,7 +1106,7 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - dev: true + dev: false /@kamilkisiela/fast-url-parser@1.1.4: resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==} @@ -925,7 +1153,6 @@ packages: cpu: [arm] os: [android] requiresBuild: true - dev: true optional: true /@rollup/rollup-android-arm64@4.13.0: @@ -933,7 +1160,6 @@ packages: cpu: [arm64] os: [android] requiresBuild: true - dev: true optional: true /@rollup/rollup-darwin-arm64@4.13.0: @@ -941,7 +1167,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: true optional: true /@rollup/rollup-darwin-x64@4.13.0: @@ -949,7 +1174,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: true optional: true /@rollup/rollup-linux-arm-gnueabihf@4.13.0: @@ -957,7 +1181,6 @@ packages: cpu: [arm] os: [linux] requiresBuild: true - dev: true optional: true /@rollup/rollup-linux-arm64-gnu@4.13.0: @@ -965,7 +1188,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true /@rollup/rollup-linux-arm64-musl@4.13.0: @@ -973,7 +1195,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true /@rollup/rollup-linux-riscv64-gnu@4.13.0: @@ -981,7 +1202,6 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true - dev: true optional: true /@rollup/rollup-linux-x64-gnu@4.13.0: @@ -989,7 +1209,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true /@rollup/rollup-linux-x64-musl@4.13.0: @@ -997,7 +1216,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true /@rollup/rollup-win32-arm64-msvc@4.13.0: @@ -1005,7 +1223,6 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true - dev: true optional: true /@rollup/rollup-win32-ia32-msvc@4.13.0: @@ -1013,7 +1230,6 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true - dev: true optional: true /@rollup/rollup-win32-x64-msvc@4.13.0: @@ -1021,7 +1237,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: true optional: true /@tsconfig/strictest@2.0.4: @@ -1078,6 +1293,12 @@ packages: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: false + /@types/node-forge@1.3.11: + resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} + dependencies: + '@types/node': 20.11.30 + dev: false + /@types/node@20.11.30: resolution: {integrity: sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==} dependencies: @@ -1272,12 +1493,13 @@ packages: /acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} - dev: true + dev: false /acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} hasBin: true + dev: false /ajv-keywords@3.5.2(ajv@6.12.6): resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} @@ -1335,7 +1557,6 @@ packages: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} @@ -1346,7 +1567,7 @@ packages: resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} dependencies: printable-characters: 1.0.42 - dev: true + dev: false /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1355,7 +1576,10 @@ packages: /binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - dev: true + + /blake3-wasm@2.1.5: + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + dev: false /brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} @@ -1368,7 +1592,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: true /browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} @@ -1416,7 +1639,7 @@ packages: tslib: 2.6.2 transitivePeerDependencies: - supports-color - dev: true + dev: false /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -1440,7 +1663,6 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true /chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} @@ -1484,7 +1706,7 @@ packages: /cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} - dev: true + dev: false /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} @@ -1501,7 +1723,7 @@ packages: /data-uri-to-buffer@2.0.2: resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} - dev: true + dev: false /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} @@ -1513,7 +1735,6 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} @@ -1549,6 +1770,36 @@ packages: resolution: {integrity: sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==} dev: false + /esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 + dev: false + /esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} @@ -1619,6 +1870,11 @@ packages: engines: {node: '>=0.8.0'} dev: true + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: false + /eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -1644,6 +1900,10 @@ packages: engines: {node: '>=4.0'} dev: false + /estree-walker@0.6.1: + resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} + dev: false + /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -1667,7 +1927,7 @@ packages: /exit-hook@2.2.1: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} - dev: true + dev: false /fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -1709,7 +1969,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} @@ -1732,9 +1991,12 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -1745,7 +2007,7 @@ packages: dependencies: data-uri-to-buffer: 2.0.2 source-map: 0.6.1 - dev: true + dev: false /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -1763,10 +2025,10 @@ packages: engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: true /glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: false /glob@10.3.12: resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} @@ -1811,6 +2073,13 @@ packages: engines: {node: '>=8'} dev: false + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false + /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -1826,12 +2095,16 @@ packages: engines: {node: '>=8'} dependencies: binary-extensions: 2.3.0 - dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.2 + dev: false /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -1843,12 +2116,10 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} @@ -1946,6 +2217,12 @@ packages: yallist: 3.1.1 dev: true + /magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + dependencies: + sourcemap-codec: 1.4.8 + dev: false + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1972,6 +2249,12 @@ packages: dependencies: mime-db: 1.52.0 + /mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: false + /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -1998,7 +2281,7 @@ packages: - bufferutil - supports-color - utf-8-validate - dev: true + dev: false /minimatch@9.0.4: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} @@ -2014,12 +2297,11 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /mustache@4.2.0: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true - dev: true + dev: false /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -2033,7 +2315,6 @@ packages: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - dev: true /neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -2043,13 +2324,17 @@ packages: resolution: {integrity: sha512-69mtXOFZ6hSkYiXAVB5SqaRvrbITC/NPyqv7yuu/qw0nmgPyYbIMYYNIDhNtwPrzk0ptrimrLz/hhjvm4w5Z+w==} dev: true + /node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + dev: false + /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - dev: true /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} @@ -2075,6 +2360,10 @@ packages: engines: {node: '>=8'} dev: true + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: false + /path-scurry@1.10.2: resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} engines: {node: '>=16 || 14 >=14.17'} @@ -2083,6 +2372,10 @@ packages: minipass: 7.0.4 dev: true + /path-to-regexp@6.2.2: + resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} + dev: false + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -2094,7 +2387,6 @@ packages: /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true /pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} @@ -2140,7 +2432,6 @@ packages: nanoid: 3.3.7 picocolors: 1.0.0 source-map-js: 1.2.0 - dev: true /prettier@3.2.5: resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} @@ -2150,7 +2441,7 @@ packages: /printable-characters@1.0.42: resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} - dev: true + dev: false /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} @@ -2209,7 +2500,6 @@ packages: engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 - dev: true /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} @@ -2220,11 +2510,46 @@ packages: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} dev: true + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: false + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: false + /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true + /rollup-plugin-inject@3.0.2: + resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject. + dependencies: + estree-walker: 0.6.1 + magic-string: 0.25.9 + rollup-pluginutils: 2.8.2 + dev: false + + /rollup-plugin-node-polyfills@0.2.1: + resolution: {integrity: sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==} + dependencies: + rollup-plugin-inject: 3.0.2 + dev: false + + /rollup-pluginutils@2.8.2: + resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} + dependencies: + estree-walker: 0.6.1 + dev: false + /rollup@4.13.0: resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2246,7 +2571,6 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.13.0 '@rollup/rollup-win32-x64-msvc': 4.13.0 fsevents: 2.3.3 - dev: true /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2273,6 +2597,14 @@ packages: ajv-keywords: 3.5.2(ajv@6.12.6) dev: false + /selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} + dependencies: + '@types/node-forge': 1.3.11 + node-forge: 1.3.1 + dev: false + /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2313,7 +2645,6 @@ packages: /source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} - dev: true /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -2325,6 +2656,7 @@ packages: /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + dev: false /source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} @@ -2333,17 +2665,22 @@ packages: whatwg-url: 7.1.0 dev: true + /sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + dev: false + /stacktracey@2.1.8: resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==} dependencies: as-table: 1.0.55 get-source: 2.0.12 - dev: true + dev: false /stoppable@1.1.0: resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} engines: {node: '>=4', npm: '>=6'} - dev: true + dev: false /streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} @@ -2415,6 +2752,11 @@ packages: has-flag: 4.0.0 dev: false + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: false + /tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -2479,7 +2821,6 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -2498,7 +2839,6 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - dev: true /tsup@8.0.2(typescript@5.4.3): resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==} @@ -2564,7 +2904,7 @@ packages: engines: {node: '>=14.0'} dependencies: '@fastify/busboy': 2.1.1 - dev: true + dev: false /update-browserslist-db@1.0.13(browserslist@4.23.0): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} @@ -2620,7 +2960,6 @@ packages: rollup: 4.13.0 optionalDependencies: fsevents: 2.3.3 - dev: true /watchpack@2.4.1: resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==} @@ -2706,7 +3045,40 @@ packages: '@cloudflare/workerd-linux-64': 1.20240404.0 '@cloudflare/workerd-linux-arm64': 1.20240404.0 '@cloudflare/workerd-windows-64': 1.20240404.0 - dev: true + dev: false + + /wrangler@3.48.0(@cloudflare/workers-types@4.20240405.0): + resolution: {integrity: sha512-Wv7JS6FyX1j9HkaM6WL3fmTzBMAYc4hPSyZCuxuH55hkJDX/7ts+YAgsaN1U8rKoDrV3FVSgBfI9TyqP9iuM8Q==} + engines: {node: '>=16.17.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20240404.0 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + dependencies: + '@cloudflare/kv-asset-handler': 0.3.1 + '@cloudflare/workers-types': 4.20240405.0 + '@esbuild-plugins/node-globals-polyfill': 0.2.3(esbuild@0.17.19) + '@esbuild-plugins/node-modules-polyfill': 0.2.2(esbuild@0.17.19) + blake3-wasm: 2.1.5 + chokidar: 3.6.0 + esbuild: 0.17.19 + miniflare: 3.20240404.0 + nanoid: 3.3.7 + path-to-regexp: 6.2.2 + resolve: 1.22.8 + resolve.exports: 2.0.2 + selfsigned: 2.4.1 + source-map: 0.6.1 + xxhash-wasm: 1.0.2 + optionalDependencies: + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} @@ -2737,7 +3109,11 @@ packages: optional: true utf-8-validate: optional: true - dev: true + dev: false + + /xxhash-wasm@1.0.2: + resolution: {integrity: sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==} + dev: false /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -2755,8 +3131,8 @@ packages: cookie: 0.5.0 mustache: 4.2.0 stacktracey: 2.1.8 - dev: true + dev: false /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - dev: true + dev: false From 860a227453926569efd48dec1d83685635dadcfb Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 11:11:23 +0900 Subject: [PATCH 31/44] refactor: move off entry selection --- examples/workerd/src/plugin.ts | 43 ++++++++++++++++------------------ examples/workerd/src/shared.ts | 1 - examples/workerd/src/worker.ts | 16 ++++++++++--- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index ca0a01c9..bd36ec2b 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -8,7 +8,7 @@ import { } from "miniflare"; import { fileURLToPath } from "url"; import { tinyassert } from "@hiogawa/utils"; -import { ANY_URL, RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; +import { ANY_URL, RUNNER_INIT_PATH } from "./shared"; import { DevEnvironment, type HMRChannel, @@ -18,8 +18,11 @@ import { import { createMiddleware } from "@hattip/adapter-node/native-fetch"; import type { SourcelessWorkerOptions } from "wrangler"; -interface WorkerdPluginOptions { +interface WorkerdPluginOptions extends WorkerdEnvironmentOptions { entry: string; +} + +interface WorkerdEnvironmentOptions { miniflare?: SourcelessWorkerOptions; wrangler?: { configPath?: string; @@ -43,14 +46,15 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { }, configureServer(server) { - return () => { - // TODO: allow interface merging so users can register typing? + return async () => { const devEnv = server.environments["workerd"] as WorkerdDevEnvironment; - server.middlewares.use( - createMiddleware((ctx) => devEnv.api.fetch(ctx.request), { - alwaysCallNext: false, - }), + createMiddleware( + (ctx) => devEnv.api.dispatchFetch(pluginOptions.entry, ctx.request), + { + alwaysCallNext: false, + }, + ), ); }; }, @@ -62,14 +66,13 @@ export type WorkerdDevEnvironment = DevEnvironment & { }; type WorkerdDevEnvironmentApi = { - setEntry(entry: string): void; - fetch(request: Request): Promise; + dispatchFetch(entry: string, request: Request): Promise; }; async function createWorkerdDevEnvironment( server: ViteDevServer, name: string, - pluginOptions: WorkerdPluginOptions, + pluginOptions: WorkerdEnvironmentOptions, ) { // setup miniflare with a durable object script to run vite module runner let runnerWorkerOptions: WorkerOptions = { @@ -95,9 +98,7 @@ async function createWorkerdDevEnvironment( }, bindings: { __viteRoot: server.config.root, - // TODO: can set entry later? - __viteEntry: pluginOptions.entry, - } satisfies Omit, + }, }; // https://github.com/cloudflare/workers-sdk/blob/2789f26a87c769fc6177b0bdc79a839a15f4ced1/packages/vitest-pool-workers/src/pool/config.ts#L174-L195 @@ -169,18 +170,14 @@ async function createWorkerdDevEnvironment( await miniflare.dispose(); } - // TODO: custom api for environment users? - // TODO: can proxy entire `SELF` like vitest integration? - // https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/ + // custom api for environment users api: WorkerdDevEnvironmentApi = { - setEntry(entry) { - entry; - }, - - async fetch(request: Request) { + async dispatchFetch(entry: string, request: Request) { + const headers = new Headers(request.headers); + headers.set("__viteEntry", entry); const req = new MiniflareRequest(request.url, { method: request.method, - headers: request.headers, + headers, body: request.body as any, duplex: "half", redirect: "manual", diff --git a/examples/workerd/src/shared.ts b/examples/workerd/src/shared.ts index aed521e5..538aaf24 100644 --- a/examples/workerd/src/shared.ts +++ b/examples/workerd/src/shared.ts @@ -3,7 +3,6 @@ export const ANY_URL = "https://any.local"; export type RunnerEnv = { __viteRoot: string; - __viteEntry: string; __viteUnsafeEval: { eval: (code: string, filename: string) => any; }; diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index 3cacc464..d5b69769 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -5,12 +5,12 @@ import { ModuleRunner } from "vite/module-runner"; export class RunnerObject implements DurableObject { #env: RunnerEnv; #runner?: ModuleRunner; - // #entry?: string; constructor(_state: DurableObjectState, env: RunnerEnv) { this.#env = env; } + // TODO: handle error async fetch(request: Request) { const url = new URL(request.url); @@ -23,8 +23,18 @@ export class RunnerObject implements DurableObject { } tinyassert(this.#runner); - const mod = await this.#runner.import(this.#env.__viteEntry); - return mod.default.fetch(request, this.#env); + const entry = request.headers.get("__viteEntry"); + tinyassert(entry); + + const mod = await this.#runner.import(entry); + const handler = mod.default as ExportedHandler; + tinyassert(handler.fetch); + + return handler.fetch(request, this.#env, { + waitUntil(_promise: Promise) {}, + passThroughOnException() {}, + abort(_reason?: any) {}, + }); } } From 66843cfa93bcde526d1e28c2c6989c8353ab7765 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 11:15:20 +0900 Subject: [PATCH 32/44] feat: catch error --- examples/workerd/src/worker.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index d5b69769..03803c5d 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -10,8 +10,20 @@ export class RunnerObject implements DurableObject { this.#env = env; } - // TODO: handle error async fetch(request: Request) { + try { + return await this.#fetch(request); + } catch (e) { + console.error(e); + let body = "[vite workerd runner error]\n"; + if (e instanceof Error) { + body += `${e.stack ?? e.message}`; + } + return new Response(body, { status: 500 }); + } + } + + async #fetch(request: Request) { const url = new URL(request.url); if (url.pathname === RUNNER_INIT_PATH) { From 2a4ffb76034d5f33a85dd1a4ed0e29e666ea5f03 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 11:24:43 +0900 Subject: [PATCH 33/44] refactor: get/setRunnerFetchOptions --- examples/workerd/src/plugin.ts | 8 ++++---- examples/workerd/src/shared.ts | 22 ++++++++++++++++++++++ examples/workerd/src/worker.ts | 13 ++++++++----- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index bd36ec2b..13202c1d 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -8,7 +8,7 @@ import { } from "miniflare"; import { fileURLToPath } from "url"; import { tinyassert } from "@hiogawa/utils"; -import { ANY_URL, RUNNER_INIT_PATH } from "./shared"; +import { ANY_URL, RUNNER_INIT_PATH, setRunnerFetchOptions } from "./shared"; import { DevEnvironment, type HMRChannel, @@ -173,11 +173,11 @@ async function createWorkerdDevEnvironment( // custom api for environment users api: WorkerdDevEnvironmentApi = { async dispatchFetch(entry: string, request: Request) { - const headers = new Headers(request.headers); - headers.set("__viteEntry", entry); const req = new MiniflareRequest(request.url, { method: request.method, - headers, + headers: setRunnerFetchOptions(new Headers(request.headers), { + entry, + }), body: request.body as any, duplex: "half", redirect: "manual", diff --git a/examples/workerd/src/shared.ts b/examples/workerd/src/shared.ts index 538aaf24..be16295f 100644 --- a/examples/workerd/src/shared.ts +++ b/examples/workerd/src/shared.ts @@ -1,3 +1,5 @@ +import { tinyassert } from "@hiogawa/utils"; + export const RUNNER_INIT_PATH = "/__viteInit"; export const ANY_URL = "https://any.local"; @@ -10,3 +12,23 @@ export type RunnerEnv = { fetch: (request: Request) => Promise; }; }; + +export type RunnerFetchOptions = { + entry: string; +}; + +const FETCH_OPTIONS_KEY = "__viteFetchOptions"; + +export function setRunnerFetchOptions( + headers: Headers, + options: RunnerFetchOptions, +): Headers { + headers.set(FETCH_OPTIONS_KEY, encodeURIComponent(JSON.stringify(options))); + return headers; +} + +export function getRunnerFetchOptions(headers: Headers): RunnerFetchOptions { + const raw = headers.get(FETCH_OPTIONS_KEY); + tinyassert(raw); + return JSON.parse(decodeURIComponent(raw)); +} diff --git a/examples/workerd/src/worker.ts b/examples/workerd/src/worker.ts index 03803c5d..3ad1baac 100644 --- a/examples/workerd/src/worker.ts +++ b/examples/workerd/src/worker.ts @@ -1,5 +1,10 @@ import { tinyassert } from "@hiogawa/utils"; -import { ANY_URL, RUNNER_INIT_PATH, type RunnerEnv } from "./shared"; +import { + ANY_URL, + RUNNER_INIT_PATH, + getRunnerFetchOptions, + type RunnerEnv, +} from "./shared"; import { ModuleRunner } from "vite/module-runner"; export class RunnerObject implements DurableObject { @@ -35,10 +40,8 @@ export class RunnerObject implements DurableObject { } tinyassert(this.#runner); - const entry = request.headers.get("__viteEntry"); - tinyassert(entry); - - const mod = await this.#runner.import(entry); + const options = getRunnerFetchOptions(request.headers); + const mod = await this.#runner.import(options.entry); const handler = mod.default as ExportedHandler; tinyassert(handler.fetch); From 409a69499ef503e4d66565381a1e984c540508f6 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 11:37:07 +0900 Subject: [PATCH 34/44] feat: export createWorkerdDevEnvironment --- examples/workerd/src/plugin.ts | 62 +++++++++++++++++----------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 13202c1d..04584d7c 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -61,15 +61,11 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { }; } -export type WorkerdDevEnvironment = DevEnvironment & { - api: WorkerdDevEnvironmentApi; -}; +export type WorkerdDevEnvironment = Awaited< + ReturnType +>; -type WorkerdDevEnvironmentApi = { - dispatchFetch(entry: string, request: Request): Promise; -}; - -async function createWorkerdDevEnvironment( +export async function createWorkerdDevEnvironment( server: ViteDevServer, name: string, pluginOptions: WorkerdEnvironmentOptions, @@ -163,34 +159,38 @@ async function createWorkerdDevEnvironment( off() {}, }; - // inheritance to extend dispose + custom api + // inheritance to extend dispose class WorkerdDevEnvironment extends DevEnvironment { override async close() { await super.close(); await miniflare.dispose(); } - - // custom api for environment users - api: WorkerdDevEnvironmentApi = { - async dispatchFetch(entry: string, request: Request) { - const req = new MiniflareRequest(request.url, { - method: request.method, - headers: setRunnerFetchOptions(new Headers(request.headers), { - entry, - }), - body: request.body as any, - duplex: "half", - redirect: "manual", - }); - const res = await runnerObject.fetch(req); - return new Response(res.body as any, { - status: res.status, - statusText: res.statusText, - headers: res.headers as any, - }); - }, - }; } - return new WorkerdDevEnvironment(server, name, { hot }); + const devEnv = new WorkerdDevEnvironment(server, name, { hot }); + + // custom environment api + const api = { + async dispatchFetch(entry: string, request: Request) { + const req = new MiniflareRequest(request.url, { + method: request.method, + headers: setRunnerFetchOptions(new Headers(request.headers), { + entry, + }), + body: request.body as any, + duplex: "half", + redirect: "manual", + }); + const res = await runnerObject.fetch(req); + return new Response(res.body as any, { + status: res.status, + statusText: res.statusText, + headers: res.headers as any, + }); + }, + }; + + // workaround for tsup dts? + Object.assign(devEnv, { api }); + return devEnv as DevEnvironment & { api: typeof api }; } From 1dd4865ab076e9dd50ebb3c1c7d9bb6ac3186d62 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 12:26:25 +0900 Subject: [PATCH 35/44] feat: createSimpleHMRChannel --- examples/workerd/src/plugin.ts | 84 +++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 04584d7c..37697ecb 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -7,10 +7,11 @@ import { mergeWorkerOptions, } from "miniflare"; import { fileURLToPath } from "url"; -import { tinyassert } from "@hiogawa/utils"; +import { DefaultMap, tinyassert } from "@hiogawa/utils"; import { ANY_URL, RUNNER_INIT_PATH, setRunnerFetchOptions } from "./shared"; import { DevEnvironment, + type CustomPayload, type HMRChannel, type Plugin, type ViteDevServer, @@ -135,29 +136,19 @@ export async function createWorkerdDevEnvironment( const { webSocket } = initResponse; webSocket.accept(); - // vite environment hot - const hot: HMRChannel = { + // websocket hmr channgel + const hot = createSimpleHMRChannel({ name, - close() {}, - listen() {}, - // cf. createServerHMRChannel - send(...args: any[]) { - let payload: any; - if (typeof args[0] === "string") { - payload = { - type: "custom", - event: args[0], - data: args[1], - }; - } else { - payload = args[0]; - } - webSocket.send(JSON.stringify(payload)); + post: (data) => webSocket.send(data), + on: (listener) => { + webSocket.addEventListener("message", listener); + return () => { + webSocket.removeEventListener("message", listener); + }; }, - // TODO: for custom event e.g. vite:invalidate - on() {}, - off() {}, - }; + serialize: (v) => JSON.stringify(v), + deserialize: (v) => JSON.parse(v.data), + }); // inheritance to extend dispose class WorkerdDevEnvironment extends DevEnvironment { @@ -194,3 +185,52 @@ export async function createWorkerdDevEnvironment( Object.assign(devEnv, { api }); return devEnv as DevEnvironment & { api: typeof api }; } + +// cf. +// https://github.com/vitejs/vite/blob/feat/environment-api/packages/vite/src/node/server/hmr.ts/#L909-L910 +// https://github.com/vitejs/vite/blob/feat/environment-api/packages/vite/src/node/ssr/runtime/serverHmrConnector.ts/#L33-L34 +function createSimpleHMRChannel(options: { + name: string; + post: (data: any) => any; + on: (listener: (data: any) => void) => () => void; + serialize: (v: any) => any; + deserialize: (v: any) => any; +}): HMRChannel { + const listerMap = new DefaultMap>(() => new Set()); + let dispose: (() => void) | undefined; + + return { + name: options.name, + listen() { + dispose = options.on((data) => { + const payload = options.deserialize(data) as CustomPayload; + for (const f of listerMap.get(payload.event)) { + f(payload.data); + } + }); + }, + close() { + dispose?.(); + dispose = undefined; + }, + on(event: string, listener: (...args: any[]) => any) { + listerMap.get(event).add(listener); + }, + off(event: string, listener: (...args: any[]) => any) { + listerMap.get(event).delete(listener); + }, + send(...args: any[]) { + let payload: any; + if (typeof args[0] === "string") { + payload = { + type: "custom", + event: args[0], + data: args[1], + }; + } else { + payload = args[0]; + } + options.post(options.serialize(payload)); + }, + }; +} From cd7aafc2ecc5dd76e7f2529fda0d1637b83dec7f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 13:08:40 +0900 Subject: [PATCH 36/44] wip: kv demo --- examples/react-ssr/vite.config.workerd.ts | 12 ++++----- examples/workerd/src/plugin.ts | 6 ++--- package.json | 1 + pnpm-lock.yaml | 31 +++-------------------- 4 files changed, 13 insertions(+), 37 deletions(-) diff --git a/examples/react-ssr/vite.config.workerd.ts b/examples/react-ssr/vite.config.workerd.ts index 55e75d10..f0d72406 100644 --- a/examples/react-ssr/vite.config.workerd.ts +++ b/examples/react-ssr/vite.config.workerd.ts @@ -2,6 +2,7 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import { vitePluginWorkerd } from "@hiogawa/vite-plugin-workerd"; import { vitePluginVirtualIndexHtml } from "./vite.config"; +import { Log } from "miniflare"; export default defineConfig((_env) => ({ clearScreen: false, @@ -10,12 +11,11 @@ export default defineConfig((_env) => ({ react(), vitePluginWorkerd({ entry: "/src/adapters/workerd.ts", - // miniflare: { - // kvNamespaces: [], - // }, - // wrangler: { - // configPath: "./wrangler.toml", - // }, + miniflare: { + log: new Log(), + kvNamespaces: { kv: "0".repeat(32) }, + kvPersist: ".wrangler/state/v3/kv", + }, }), vitePluginVirtualIndexHtml(), ], diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 37697ecb..56c26ff7 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -1,10 +1,10 @@ import { - Log, Miniflare, type WorkerOptions, Request as MiniflareRequest, Response as MiniflareResponse, mergeWorkerOptions, + type SharedOptions, } from "miniflare"; import { fileURLToPath } from "url"; import { DefaultMap, tinyassert } from "@hiogawa/utils"; @@ -24,7 +24,7 @@ interface WorkerdPluginOptions extends WorkerdEnvironmentOptions { } interface WorkerdEnvironmentOptions { - miniflare?: SourcelessWorkerOptions; + miniflare?: SharedOptions & SourcelessWorkerOptions; wrangler?: { configPath?: string; }; @@ -118,7 +118,7 @@ export async function createWorkerdDevEnvironment( } const miniflare = new Miniflare({ - log: new Log(), + ...pluginOptions.miniflare, workers: [runnerWorkerOptions], }); diff --git a/package.json b/package.json index 922e0fb2..62d9f517 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@types/node": "^20.11.30", "@vitejs/plugin-react": "^4.2.1", "esbuild": "^0.20.2", + "miniflare": "^3.20240404.0", "prettier": "^3.2.5", "tsup": "^8.0.2", "tsx": "^4.7.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 57ac7627..1d270d9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: esbuild: specifier: ^0.20.2 version: 0.20.2 + miniflare: + specifier: ^3.20240404.0 + version: 3.20240404.0 prettier: specifier: ^3.2.5 version: 3.2.5 @@ -357,7 +360,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: false optional: true /@cloudflare/workerd-darwin-arm64@1.20240404.0: @@ -366,7 +368,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: false optional: true /@cloudflare/workerd-linux-64@1.20240404.0: @@ -375,7 +376,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: false optional: true /@cloudflare/workerd-linux-arm64@1.20240404.0: @@ -384,7 +384,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: false optional: true /@cloudflare/workerd-windows-64@1.20240404.0: @@ -393,7 +392,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: false optional: true /@cloudflare/workers-types@4.20240405.0: @@ -404,7 +402,6 @@ packages: engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 - dev: false /@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.17.19): resolution: {integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==} @@ -1016,7 +1013,6 @@ packages: /@fastify/busboy@2.1.1: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - dev: false /@hattip/adapter-node@0.0.44: resolution: {integrity: sha512-Qf0kcrE4yHFrmKgfntrxHGNkk0/9a45aKmAO3ex6OiRKat5DZCFDdFMHV85Z594IohzT7Q+WcWiHQl/b0Jvo7Q==} @@ -1106,7 +1102,6 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - dev: false /@kamilkisiela/fast-url-parser@1.1.4: resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==} @@ -1493,13 +1488,11 @@ packages: /acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} - dev: false /acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} hasBin: true - dev: false /ajv-keywords@3.5.2(ajv@6.12.6): resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} @@ -1567,7 +1560,6 @@ packages: resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} dependencies: printable-characters: 1.0.42 - dev: false /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1639,7 +1631,6 @@ packages: tslib: 2.6.2 transitivePeerDependencies: - supports-color - dev: false /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -1706,7 +1697,6 @@ packages: /cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} - dev: false /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} @@ -1723,7 +1713,6 @@ packages: /data-uri-to-buffer@2.0.2: resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} - dev: false /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} @@ -1927,7 +1916,6 @@ packages: /exit-hook@2.2.1: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} - dev: false /fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -2007,7 +1995,6 @@ packages: dependencies: data-uri-to-buffer: 2.0.2 source-map: 0.6.1 - dev: false /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -2028,7 +2015,6 @@ packages: /glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - dev: false /glob@10.3.12: resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} @@ -2281,7 +2267,6 @@ packages: - bufferutil - supports-color - utf-8-validate - dev: false /minimatch@9.0.4: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} @@ -2301,7 +2286,6 @@ packages: /mustache@4.2.0: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true - dev: false /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -2441,7 +2425,6 @@ packages: /printable-characters@1.0.42: resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} - dev: false /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} @@ -2656,7 +2639,6 @@ packages: /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - dev: false /source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} @@ -2675,12 +2657,10 @@ packages: dependencies: as-table: 1.0.55 get-source: 2.0.12 - dev: false /stoppable@1.1.0: resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} engines: {node: '>=4', npm: '>=6'} - dev: false /streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} @@ -2904,7 +2884,6 @@ packages: engines: {node: '>=14.0'} dependencies: '@fastify/busboy': 2.1.1 - dev: false /update-browserslist-db@1.0.13(browserslist@4.23.0): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} @@ -3045,7 +3024,6 @@ packages: '@cloudflare/workerd-linux-64': 1.20240404.0 '@cloudflare/workerd-linux-arm64': 1.20240404.0 '@cloudflare/workerd-windows-64': 1.20240404.0 - dev: false /wrangler@3.48.0(@cloudflare/workers-types@4.20240405.0): resolution: {integrity: sha512-Wv7JS6FyX1j9HkaM6WL3fmTzBMAYc4hPSyZCuxuH55hkJDX/7ts+YAgsaN1U8rKoDrV3FVSgBfI9TyqP9iuM8Q==} @@ -3109,7 +3087,6 @@ packages: optional: true utf-8-validate: optional: true - dev: false /xxhash-wasm@1.0.2: resolution: {integrity: sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==} @@ -3131,8 +3108,6 @@ packages: cookie: 0.5.0 mustache: 4.2.0 stacktracey: 2.1.8 - dev: false /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - dev: false From 4ef7e23e068a9bc194cfd9974c80f6bea12e5158 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 13:49:54 +0900 Subject: [PATCH 37/44] chore: setup example --- examples/react-ssr-workerd/README.md | 18 ++++++++ examples/react-ssr-workerd/e2e/basic.test.ts | 9 ++++ examples/react-ssr-workerd/index.html | 14 ++++++ examples/react-ssr-workerd/package.json | 27 +++++++++++ .../react-ssr-workerd/playwright.config.ts | 28 +++++++++++ .../react-ssr-workerd/src/adapters/node.tsx | 4 ++ .../react-ssr-workerd/src/adapters/workerd.ts | 5 ++ .../react-ssr-workerd/src/entry-client.tsx | 14 ++++++ .../react-ssr-workerd/src/entry-server.tsx | 9 ++++ .../react-ssr-workerd/src/routes/page.tsx | 19 ++++++++ .../react-ssr-workerd/src/types/react.d.ts | 3 ++ .../react-ssr-workerd/src/types/virtual.d.ts | 4 ++ examples/react-ssr-workerd/tsconfig.json | 15 ++++++ examples/react-ssr-workerd/vite.config.ts | 46 +++++++++++++++++++ 14 files changed, 215 insertions(+) create mode 100644 examples/react-ssr-workerd/README.md create mode 100644 examples/react-ssr-workerd/e2e/basic.test.ts create mode 100644 examples/react-ssr-workerd/index.html create mode 100644 examples/react-ssr-workerd/package.json create mode 100644 examples/react-ssr-workerd/playwright.config.ts create mode 100644 examples/react-ssr-workerd/src/adapters/node.tsx create mode 100644 examples/react-ssr-workerd/src/adapters/workerd.ts create mode 100644 examples/react-ssr-workerd/src/entry-client.tsx create mode 100644 examples/react-ssr-workerd/src/entry-server.tsx create mode 100644 examples/react-ssr-workerd/src/routes/page.tsx create mode 100644 examples/react-ssr-workerd/src/types/react.d.ts create mode 100644 examples/react-ssr-workerd/src/types/virtual.d.ts create mode 100644 examples/react-ssr-workerd/tsconfig.json create mode 100644 examples/react-ssr-workerd/vite.config.ts diff --git a/examples/react-ssr-workerd/README.md b/examples/react-ssr-workerd/README.md new file mode 100644 index 00000000..e1d93f82 --- /dev/null +++ b/examples/react-ssr-workerd/README.md @@ -0,0 +1,18 @@ +# react-ssr + +https://vite-environment-examples-react-ssr.vercel.app + +```sh +pnpm dev +pnpm build +pnpm preview + +pnpm vc-build +pnpm vc-release +``` + +## todo + +- [x] basic dev +- [x] basic build / preview +- [x] dev hmr diff --git a/examples/react-ssr-workerd/e2e/basic.test.ts b/examples/react-ssr-workerd/e2e/basic.test.ts new file mode 100644 index 00000000..18ee5084 --- /dev/null +++ b/examples/react-ssr-workerd/e2e/basic.test.ts @@ -0,0 +1,9 @@ +import { test, expect } from "@playwright/test"; + +test("basic", async ({ page }) => { + await page.goto("/"); + await expect(page.locator("#root")).toContainText("hydrated: true"); + await expect(page.locator("#root")).toContainText("Count: 0"); + await page.getByRole("button", { name: "+" }).click(); + await expect(page.locator("#root")).toContainText("Count: 1"); +}); diff --git a/examples/react-ssr-workerd/index.html b/examples/react-ssr-workerd/index.html new file mode 100644 index 00000000..be76ed20 --- /dev/null +++ b/examples/react-ssr-workerd/index.html @@ -0,0 +1,14 @@ + + + + + react-ssr-workerd + + + + + + diff --git a/examples/react-ssr-workerd/package.json b/examples/react-ssr-workerd/package.json new file mode 100644 index 00000000..d3ed0634 --- /dev/null +++ b/examples/react-ssr-workerd/package.json @@ -0,0 +1,27 @@ +{ + "name": "@hiogawa/vite-environment-examples-react-ssr-workerd", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build --all", + "preview": "vite preview", + "test-e2e": "playwright test", + "test-e2e-preview": "E2E_PREVIEW=1 playwright test", + "test-e2e-workerd": "E2E_WORKERD=1 playwright test", + "vc-build": "SERVER_ENTRY=/src/adapters/vercel-edge.ts pnpm build && bash misc/vercel-edge/build.sh", + "vc-release": "vercel deploy --prebuilt misc/vercel-edge --prod" + }, + "dependencies": { + "react": "18.3.0-canary-6c3b8dbfe-20240226", + "react-dom": "18.3.0-canary-6c3b8dbfe-20240226" + }, + "devDependencies": { + "@hiogawa/vite-plugin-workerd": "workspace:*", + "@types/react": "18.2.72", + "@types/react-dom": "18.2.22" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/examples/react-ssr-workerd/playwright.config.ts b/examples/react-ssr-workerd/playwright.config.ts new file mode 100644 index 00000000..3adb79f6 --- /dev/null +++ b/examples/react-ssr-workerd/playwright.config.ts @@ -0,0 +1,28 @@ +import { defineConfig, devices } from "@playwright/test"; + +const port = Number(process.env["E2E_PORT"] || 6174); +const command = process.env["E2E_PREVIEW"] + ? `pnpm preview --port ${port} --strict-port` + : process.env["E2E_WORKERD"] + ? `pnpm dev-workerd --port ${port} --strict-port` + : `pnpm dev --port ${port} --strict-port`; + +export default defineConfig({ + testDir: "e2e", + use: { + trace: "on-first-retry", + }, + projects: [ + { + name: "chromium", + use: devices["Desktop Chrome"], + }, + ], + webServer: { + command, + port, + }, + forbidOnly: !!process.env["CI"], + retries: process.env["CI"] ? 2 : 0, + reporter: "list", +}); diff --git a/examples/react-ssr-workerd/src/adapters/node.tsx b/examples/react-ssr-workerd/src/adapters/node.tsx new file mode 100644 index 00000000..58d874c4 --- /dev/null +++ b/examples/react-ssr-workerd/src/adapters/node.tsx @@ -0,0 +1,4 @@ +import { createMiddleware } from "@hattip/adapter-node/native-fetch"; +import { handler } from "../entry-server"; + +export default createMiddleware((ctx) => handler(ctx.request)); diff --git a/examples/react-ssr-workerd/src/adapters/workerd.ts b/examples/react-ssr-workerd/src/adapters/workerd.ts new file mode 100644 index 00000000..b6ed37b5 --- /dev/null +++ b/examples/react-ssr-workerd/src/adapters/workerd.ts @@ -0,0 +1,5 @@ +import { handler } from "../entry-server"; + +export default { + fetch: handler, +}; diff --git a/examples/react-ssr-workerd/src/entry-client.tsx b/examples/react-ssr-workerd/src/entry-client.tsx new file mode 100644 index 00000000..b50e5598 --- /dev/null +++ b/examples/react-ssr-workerd/src/entry-client.tsx @@ -0,0 +1,14 @@ +import { tinyassert } from "@hiogawa/utils"; +import ReactDomClient from "react-dom/client"; +import Page from "./routes/page"; +import React from "react"; + +async function main() { + const el = document.getElementById("root"); + tinyassert(el); + React.startTransition(() => { + ReactDomClient.hydrateRoot(el, ); + }); +} + +main(); diff --git a/examples/react-ssr-workerd/src/entry-server.tsx b/examples/react-ssr-workerd/src/entry-server.tsx new file mode 100644 index 00000000..569015ce --- /dev/null +++ b/examples/react-ssr-workerd/src/entry-server.tsx @@ -0,0 +1,9 @@ +import ReactDomServer from "react-dom/server.edge"; +import Page from "./routes/page"; + +export async function handler(_req: Request) { + const ssrHtml = ReactDomServer.renderToString(); + let html = (await import("virtual:index-html")).default; + html = html.replace(//, `
${ssrHtml}
`); + return new Response(html, { headers: { "content-type": "text/html" } }); +} diff --git a/examples/react-ssr-workerd/src/routes/page.tsx b/examples/react-ssr-workerd/src/routes/page.tsx new file mode 100644 index 00000000..0823a3f8 --- /dev/null +++ b/examples/react-ssr-workerd/src/routes/page.tsx @@ -0,0 +1,19 @@ +import React from "react"; + +export default function Page() { + const [count, setCount] = React.useState(0); + + const [hydrated, setHydrated] = React.useState(false); + React.useEffect(() => { + setHydrated(true); + }, []); + + return ( +
+
hydrated: {String(hydrated)}
+
Count: {count}
+ + +
+ ); +} diff --git a/examples/react-ssr-workerd/src/types/react.d.ts b/examples/react-ssr-workerd/src/types/react.d.ts new file mode 100644 index 00000000..10dd886d --- /dev/null +++ b/examples/react-ssr-workerd/src/types/react.d.ts @@ -0,0 +1,3 @@ +declare module "react-dom/server.edge" { + export * from "react-dom/server"; +} diff --git a/examples/react-ssr-workerd/src/types/virtual.d.ts b/examples/react-ssr-workerd/src/types/virtual.d.ts new file mode 100644 index 00000000..072bef8d --- /dev/null +++ b/examples/react-ssr-workerd/src/types/virtual.d.ts @@ -0,0 +1,4 @@ +declare module "virtual:index-html" { + const src: string; + export default src; +} diff --git a/examples/react-ssr-workerd/tsconfig.json b/examples/react-ssr-workerd/tsconfig.json new file mode 100644 index 00000000..aa702900 --- /dev/null +++ b/examples/react-ssr-workerd/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@tsconfig/strictest/tsconfig.json", + "include": ["src", "vite.config.ts", "e2e", "playwright.config.ts"], + "compilerOptions": { + "exactOptionalPropertyTypes": false, + "verbatimModuleSyntax": true, + "noEmit": true, + "moduleResolution": "Bundler", + "module": "ESNext", + "target": "ESNext", + "lib": ["ESNext", "DOM"], + "types": ["vite/client"], + "jsx": "react-jsx" + } +} diff --git a/examples/react-ssr-workerd/vite.config.ts b/examples/react-ssr-workerd/vite.config.ts new file mode 100644 index 00000000..643b8ad7 --- /dev/null +++ b/examples/react-ssr-workerd/vite.config.ts @@ -0,0 +1,46 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import { vitePluginWorkerd } from "@hiogawa/vite-plugin-workerd"; +import { vitePluginVirtualIndexHtml } from "../react-ssr/vite.config"; +import { Log } from "miniflare"; + +export default defineConfig((_env) => ({ + clearScreen: false, + appType: "custom", + plugins: [ + react(), + vitePluginWorkerd({ + entry: "/src/adapters/workerd.ts", + miniflare: { + log: new Log(), + kvNamespaces: { kv: "0".repeat(32) }, + kvPersist: ".wrangler/state/v3/kv", + }, + }), + vitePluginVirtualIndexHtml(), + ], + environments: { + workerd: { + // [feedback] how to prevent deps optimization to inject this? still `ssr.target: "webworker"` needed? + // import { createRequire } from 'module';const require = createRequire(import.meta.url); + nodeCompatible: false, + webCompatible: true, + resolve: { + noExternal: true, + }, + dev: { + optimizeDeps: { + include: [ + "react", + "react/jsx-runtime", + "react/jsx-dev-runtime", + "react-dom/server.edge", + ], + }, + }, + }, + }, + ssr: { + target: "webworker", + }, +})); From 874386024369e4773332ccdb200d888d6b3a3765 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 14:07:57 +0900 Subject: [PATCH 38/44] chore: kv demo --- examples/react-ssr-workerd/package.json | 1 + .../react-ssr-workerd/src/adapters/workerd.ts | 5 +++- .../react-ssr-workerd/src/entry-server.tsx | 17 ++++++++++++- examples/react-ssr-workerd/src/routes/api.ts | 7 ++++++ .../react-ssr-workerd/src/routes/page.tsx | 24 +++++++++++++++---- .../react-ssr-workerd/src/types/workerd.d.ts | 3 +++ pnpm-lock.yaml | 22 +++++++++++++++++ 7 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 examples/react-ssr-workerd/src/routes/api.ts create mode 100644 examples/react-ssr-workerd/src/types/workerd.d.ts diff --git a/examples/react-ssr-workerd/package.json b/examples/react-ssr-workerd/package.json index d3ed0634..0cc57cdc 100644 --- a/examples/react-ssr-workerd/package.json +++ b/examples/react-ssr-workerd/package.json @@ -17,6 +17,7 @@ "react-dom": "18.3.0-canary-6c3b8dbfe-20240226" }, "devDependencies": { + "@cloudflare/workers-types": "^4.20240405.0", "@hiogawa/vite-plugin-workerd": "workspace:*", "@types/react": "18.2.72", "@types/react-dom": "18.2.22" diff --git a/examples/react-ssr-workerd/src/adapters/workerd.ts b/examples/react-ssr-workerd/src/adapters/workerd.ts index b6ed37b5..eb315cc2 100644 --- a/examples/react-ssr-workerd/src/adapters/workerd.ts +++ b/examples/react-ssr-workerd/src/adapters/workerd.ts @@ -1,5 +1,8 @@ import { handler } from "../entry-server"; export default { - fetch: handler, + fetch(request: Request, env: unknown) { + Object.assign(globalThis, { env }); + return handler(request); + }, }; diff --git a/examples/react-ssr-workerd/src/entry-server.tsx b/examples/react-ssr-workerd/src/entry-server.tsx index 569015ce..90c78293 100644 --- a/examples/react-ssr-workerd/src/entry-server.tsx +++ b/examples/react-ssr-workerd/src/entry-server.tsx @@ -1,9 +1,24 @@ import ReactDomServer from "react-dom/server.edge"; import Page from "./routes/page"; -export async function handler(_req: Request) { +export async function handler(request: Request) { + const url = new URL(request.url); + if (url.pathname === "/api") { + return apiHandler(request); + } + const ssrHtml = ReactDomServer.renderToString(); let html = (await import("virtual:index-html")).default; html = html.replace(//, `
${ssrHtml}
`); return new Response(html, { headers: { "content-type": "text/html" } }); } + +async function apiHandler(request: Request) { + let count = Number(await env.kv.get("count")); + if (request.method === "POST") { + const { delta } = await request.json(); + count += delta; + await env.kv.put("count", String(count)); + } + return new Response(JSON.stringify({ count })); +} diff --git a/examples/react-ssr-workerd/src/routes/api.ts b/examples/react-ssr-workerd/src/routes/api.ts new file mode 100644 index 00000000..4e667795 --- /dev/null +++ b/examples/react-ssr-workerd/src/routes/api.ts @@ -0,0 +1,7 @@ +export async function get() { + env.kv; +} + +export function post() { + env.kv; +} diff --git a/examples/react-ssr-workerd/src/routes/page.tsx b/examples/react-ssr-workerd/src/routes/page.tsx index 0823a3f8..795bdef5 100644 --- a/examples/react-ssr-workerd/src/routes/page.tsx +++ b/examples/react-ssr-workerd/src/routes/page.tsx @@ -1,19 +1,35 @@ import React from "react"; export default function Page() { - const [count, setCount] = React.useState(0); + const [count, setCount] = React.useState(); const [hydrated, setHydrated] = React.useState(false); React.useEffect(() => { setHydrated(true); + getCount().then(setCount); }, []); return (
hydrated: {String(hydrated)}
-
Count: {count}
- - +
Count: {count ?? "..."}
+ +
); } + +async function getCount() { + const res = await fetch("/api"); + const { count } = await res.json(); + return count as number; +} + +async function changeCount(delta: number) { + const res = await fetch("/api", { + method: "POST", + body: JSON.stringify({ delta }), + }); + const { count } = await res.json(); + return count as number; +} diff --git a/examples/react-ssr-workerd/src/types/workerd.d.ts b/examples/react-ssr-workerd/src/types/workerd.d.ts new file mode 100644 index 00000000..b0f43b9a --- /dev/null +++ b/examples/react-ssr-workerd/src/types/workerd.d.ts @@ -0,0 +1,3 @@ +declare const env: { + kv: import("@cloudflare/workers-types").KVNamespace; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d270d9e..83c52519 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -105,6 +105,28 @@ importers: specifier: 18.2.22 version: 18.2.22 + examples/react-ssr-workerd: + dependencies: + react: + specifier: 18.3.0-canary-6c3b8dbfe-20240226 + version: 18.3.0-canary-6c3b8dbfe-20240226 + react-dom: + specifier: 18.3.0-canary-6c3b8dbfe-20240226 + version: 18.3.0-canary-6c3b8dbfe-20240226(react@18.3.0-canary-6c3b8dbfe-20240226) + devDependencies: + '@cloudflare/workers-types': + specifier: ^4.20240405.0 + version: 4.20240405.0 + '@hiogawa/vite-plugin-workerd': + specifier: workspace:* + version: link:../workerd + '@types/react': + specifier: 18.2.72 + version: 18.2.72 + '@types/react-dom': + specifier: 18.2.22 + version: 18.2.22 + examples/workerd: dependencies: miniflare: From 391cc6847f9c0acf122f85e9c9ef3c66526637cf Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 14:11:06 +0900 Subject: [PATCH 39/44] ci: add e2e --- .github/workflows/ci.yml | 1 + examples/react-ssr-workerd/package.json | 8 +------- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05b9311a..55e87c5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ jobs: - run: pnpm -C examples/react-ssr build - run: pnpm -C examples/react-ssr test-e2e-preview - run: pnpm -C examples/react-ssr test-e2e-workerd + - run: pnpm -C examples/react-ssr-workerd test-e2e - run: pnpm -C examples/react-server test-e2e - run: pnpm -C examples/react-server build - run: pnpm -C examples/react-server test-e2e-preview diff --git a/examples/react-ssr-workerd/package.json b/examples/react-ssr-workerd/package.json index 0cc57cdc..e8209ce6 100644 --- a/examples/react-ssr-workerd/package.json +++ b/examples/react-ssr-workerd/package.json @@ -4,13 +4,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build --all", - "preview": "vite preview", - "test-e2e": "playwright test", - "test-e2e-preview": "E2E_PREVIEW=1 playwright test", - "test-e2e-workerd": "E2E_WORKERD=1 playwright test", - "vc-build": "SERVER_ENTRY=/src/adapters/vercel-edge.ts pnpm build && bash misc/vercel-edge/build.sh", - "vc-release": "vercel deploy --prebuilt misc/vercel-edge --prod" + "test-e2e": "playwright test" }, "dependencies": { "react": "18.3.0-canary-6c3b8dbfe-20240226", From 5096571d59aa5d42fd7d36f31dcc540ca1f3243c Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 14:26:22 +0900 Subject: [PATCH 40/44] chore: use wrangler.toml --- examples/react-ssr-workerd/src/entry-server.tsx | 4 ++++ examples/react-ssr-workerd/src/routes/api.ts | 7 ------- examples/react-ssr-workerd/vite.config.ts | 5 +++-- examples/react-ssr-workerd/wrangler.toml | 5 +++++ examples/react-ssr/vite.config.workerd.ts | 2 -- 5 files changed, 12 insertions(+), 11 deletions(-) delete mode 100644 examples/react-ssr-workerd/src/routes/api.ts create mode 100644 examples/react-ssr-workerd/wrangler.toml diff --git a/examples/react-ssr-workerd/src/entry-server.tsx b/examples/react-ssr-workerd/src/entry-server.tsx index 90c78293..ed6ed83c 100644 --- a/examples/react-ssr-workerd/src/entry-server.tsx +++ b/examples/react-ssr-workerd/src/entry-server.tsx @@ -6,6 +6,10 @@ export async function handler(request: Request) { if (url.pathname === "/api") { return apiHandler(request); } + if (url.pathname === "/nodejs-compat") { + const util = await import("node:util"); + return new Response(util.format("hello %s", "world")); + } const ssrHtml = ReactDomServer.renderToString(); let html = (await import("virtual:index-html")).default; diff --git a/examples/react-ssr-workerd/src/routes/api.ts b/examples/react-ssr-workerd/src/routes/api.ts deleted file mode 100644 index 4e667795..00000000 --- a/examples/react-ssr-workerd/src/routes/api.ts +++ /dev/null @@ -1,7 +0,0 @@ -export async function get() { - env.kv; -} - -export function post() { - env.kv; -} diff --git a/examples/react-ssr-workerd/vite.config.ts b/examples/react-ssr-workerd/vite.config.ts index 643b8ad7..00cbe94e 100644 --- a/examples/react-ssr-workerd/vite.config.ts +++ b/examples/react-ssr-workerd/vite.config.ts @@ -13,8 +13,9 @@ export default defineConfig((_env) => ({ entry: "/src/adapters/workerd.ts", miniflare: { log: new Log(), - kvNamespaces: { kv: "0".repeat(32) }, - kvPersist: ".wrangler/state/v3/kv", + }, + wrangler: { + configPath: "./wrangler.toml", }, }), vitePluginVirtualIndexHtml(), diff --git a/examples/react-ssr-workerd/wrangler.toml b/examples/react-ssr-workerd/wrangler.toml new file mode 100644 index 00000000..681468d3 --- /dev/null +++ b/examples/react-ssr-workerd/wrangler.toml @@ -0,0 +1,5 @@ +compatibility_date = "2024-01-01" +compatibility_flags = ["nodejs_compat"] +kv_namespaces = [ + { binding = "kv", id = "test-namespace" } +] diff --git a/examples/react-ssr/vite.config.workerd.ts b/examples/react-ssr/vite.config.workerd.ts index f0d72406..b2a16173 100644 --- a/examples/react-ssr/vite.config.workerd.ts +++ b/examples/react-ssr/vite.config.workerd.ts @@ -13,8 +13,6 @@ export default defineConfig((_env) => ({ entry: "/src/adapters/workerd.ts", miniflare: { log: new Log(), - kvNamespaces: { kv: "0".repeat(32) }, - kvPersist: ".wrangler/state/v3/kv", }, }), vitePluginVirtualIndexHtml(), From 6bce43a1d7aac478d728b5467637cdc73a302ebf Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 14:38:05 +0900 Subject: [PATCH 41/44] chore: readme --- examples/react-ssr-workerd/README.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/examples/react-ssr-workerd/README.md b/examples/react-ssr-workerd/README.md index e1d93f82..5dee24bb 100644 --- a/examples/react-ssr-workerd/README.md +++ b/examples/react-ssr-workerd/README.md @@ -1,18 +1,5 @@ # react-ssr -https://vite-environment-examples-react-ssr.vercel.app - ```sh pnpm dev -pnpm build -pnpm preview - -pnpm vc-build -pnpm vc-release ``` - -## todo - -- [x] basic dev -- [x] basic build / preview -- [x] dev hmr From ced97d973f1a2dedc727a83b53d47f268778d9e2 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 14:39:11 +0900 Subject: [PATCH 42/44] chore: readme --- examples/workerd/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/workerd/README.md b/examples/workerd/README.md index 5448e07b..a5800633 100644 --- a/examples/workerd/README.md +++ b/examples/workerd/README.md @@ -2,7 +2,7 @@ Vite module runner on Durable Objects. -See [`examples/react-ssr/vite.config.workerd.ts`](../react-ssr/vite.config.workerd.ts). +See [`examples/react-ssr-workerd`](../react-ssr-workerd). ## references @@ -10,4 +10,3 @@ See [`examples/react-ssr/vite.config.workerd.ts`](../react-ssr/vite.config.worke - https://github.com/cloudflare/workers-sdk/blob/d994066f255f6851759a055eac3b52a4aa4b83c3/packages/vitest-pool-workers/src/worker/index.ts#L174 - https://github.com/cloudflare/workers-sdk/blob/2789f26a87c769fc6177b0bdc79a839a15f4ced1/packages/vitest-pool-workers/src/pool/index.ts#L630 - https://github.com/cloudflare/workers-sdk/pull/5530 -- https://github.com/remix-run/remix/pull/8834 From dba45bc0df9e9192dc67f38a803f9a6dea7c4d9d Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 14:45:44 +0900 Subject: [PATCH 43/44] refactor: minor --- examples/workerd/src/plugin.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/workerd/src/plugin.ts b/examples/workerd/src/plugin.ts index 56c26ff7..68051e3d 100644 --- a/examples/workerd/src/plugin.ts +++ b/examples/workerd/src/plugin.ts @@ -47,16 +47,15 @@ export function vitePluginWorkerd(pluginOptions: WorkerdPluginOptions): Plugin { }, configureServer(server) { - return async () => { - const devEnv = server.environments["workerd"] as WorkerdDevEnvironment; - server.middlewares.use( - createMiddleware( - (ctx) => devEnv.api.dispatchFetch(pluginOptions.entry, ctx.request), - { - alwaysCallNext: false, - }, - ), - ); + const devEnv = server.environments["workerd"] as WorkerdDevEnvironment; + const nodeMiddleware = createMiddleware( + (ctx) => devEnv.api.dispatchFetch(pluginOptions.entry, ctx.request), + { + alwaysCallNext: false, + }, + ); + return () => { + server.middlewares.use(nodeMiddleware); }; }, }; From 4d8c8f387c2fdccb78d87f31408656b26f536730 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 9 Apr 2024 16:23:38 +0900 Subject: [PATCH 44/44] chore: readme --- examples/react-ssr-workerd/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/react-ssr-workerd/README.md b/examples/react-ssr-workerd/README.md index 5dee24bb..4b4d0b94 100644 --- a/examples/react-ssr-workerd/README.md +++ b/examples/react-ssr-workerd/README.md @@ -1,4 +1,4 @@ -# react-ssr +# react-ssr-workerd ```sh pnpm dev