The publish bundler artifact, worker-bundler source and esbuild wasm, is fetched from static assets at first use instead of being inlined into the worker script, returning the script to its previous size. Cold-start latency regressed while the 19MB artifact chunk was part of the script blob.
Showing the changes to apps/cloud/scripts (2 of 8 files). Show all
Unified · Split
| @@ -0,0 +1,54 @@ | ||
| 1 | +import { readdirSync, readFileSync, statSync } from "node:fs"; | |
| 2 | +import { join } from "node:path"; | |
| 3 | + | |
| 4 | +const appRoot = new URL("..", import.meta.url); | |
| 5 | +const serverAssetsDir = new URL("dist/server/assets/", appRoot); | |
| 6 | +const clientAssetsDir = new URL("dist/client/assets/", appRoot); | |
| 7 | +const largeFileLimit = 3 * 1024 * 1024; | |
| 8 | +const sourceAssetMinimum = 512 * 1024; | |
| 9 | + | |
| 10 | +const fail = (message) => { | |
| 11 | + // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: build assertion script reports a failed artifact contract | |
| 12 | + throw new Error(message); | |
| 13 | +}; | |
| 14 | + | |
| 15 | +const fileSize = (dir, name) => statSync(join(dir.pathname, name)).size; | |
| 16 | + | |
| 17 | +const serverAssets = readdirSync(serverAssetsDir); | |
| 18 | +for (const name of serverAssets) { | |
| 19 | + const path = join(serverAssetsDir.pathname, name); | |
| 20 | + const size = statSync(path).size; | |
| 21 | + if (size <= largeFileLimit) continue; | |
| 22 | + if (name.includes("worker-bundler")) { | |
| 23 | + fail(`server assets include oversized worker-bundler chunk ${name}`); | |
| 24 | + } | |
| 25 | + const sample = readFileSync(path, "utf8"); | |
| 26 | + if (sample.includes("worker-bundler")) { | |
| 27 | + fail(`server assets include worker-bundler content in oversized chunk ${name}`); | |
| 28 | + } | |
| 29 | +} | |
| 30 | + | |
| 31 | +const clientAssets = readdirSync(clientAssetsDir); | |
| 32 | +const source = clientAssets.find((name) => /^worker-bundler-source-[a-f0-9]{12}\.js$/.test(name)); | |
| 33 | +const wasm = clientAssets.find((name) => | |
| 34 | + /^worker-bundler-esbuild-wasm-[a-f0-9]{12}\.wasm$/.test(name), | |
| 35 | +); | |
| 36 | + | |
| 37 | +if (source === undefined) fail("missing worker-bundler source asset"); | |
| 38 | +if (wasm === undefined) fail("missing worker-bundler esbuild wasm asset"); | |
| 39 | + | |
| 40 | +const sourceSize = fileSize(clientAssetsDir, source); | |
| 41 | +const wasmSize = fileSize(clientAssetsDir, wasm); | |
| 42 | +if (sourceSize <= sourceAssetMinimum) { | |
| 43 | + fail(`worker-bundler source asset is unexpectedly small: ${source}`); | |
| 44 | +} | |
| 45 | +if (wasmSize <= largeFileLimit) fail(`worker-bundler wasm asset is unexpectedly small: ${wasm}`); | |
| 46 | + | |
| 47 | +const wasmMagic = readFileSync(join(clientAssetsDir.pathname, wasm)).subarray(0, 4); | |
| 48 | +if (!wasmMagic.equals(Buffer.from([0x00, 0x61, 0x73, 0x6d]))) { | |
| 49 | + fail(`worker-bundler wasm asset does not start with wasm magic bytes: ${wasm}`); | |
| 50 | +} | |
| 51 | + | |
| 52 | +console.log( | |
| 53 | + `[assert-worker-bundler-assets] ${source} (${sourceSize} bytes), ${wasm} (${wasmSize} bytes)`, | |
| 54 | +); | |
| @@ -24,6 +24,7 @@ const steps = [ · 23 unmodified lines | ||
| 24 | 24 | // imports, so they must be built before vite starts. |
| 25 | 25 | "turbo run build --filter @executor-js/vite-plugin --filter @executor-js/react --filter @executor-js/plugin-apps", |
| 26 | 26 | "vite build", |
| 27 | + "node scripts/assert-worker-bundler-assets.mjs", | |
| 27 | 28 | ]; |
| 28 | 29 | |
| 29 | 30 | for (const step of steps) { |