Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions apps/cloud/executor.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,38 @@ interface CloudPluginDeps {
readonly globalOutbound?: null;
}) => { readonly getEntrypoint: () => unknown };
};
readonly workerAssets?: {
readonly fetch: (request: Request) => Promise<Response>;
};
}

const base64ToArrayBuffer = (value: string): ArrayBuffer => {
const bytes = Uint8Array.from(atob(value), (char) => char.charCodeAt(0));
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
};

const assetRequest = (path: string): Request => new Request(new URL(path, "https://assets.local"));

const fetchAsset = async (
assets: { readonly fetch: (request: Request) => Promise<Response> },
path: string,
): Promise<Response> => {
const response = await assets.fetch(assetRequest(path));
if (!response.ok) {
// oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: worker artifact asset fetch must reject the dynamic bundler setup
throw new Error(`failed to fetch worker-bundler asset ${path}: ${response.status}`);
}
return response;
};

export default defineExecutorConfig({
plugins: ({
workosCredentials,
workosVaultClient,
activeToolkitSlug,
allowLocalNetwork,
workerLoader,
workerAssets,
}: CloudPluginDeps = {}) =>
[
openApiHttpPlugin({
Expand All @@ -107,9 +125,27 @@ export default defineExecutorConfig({
loader: workerLoader,
artifact: async () => {
const artifact = await import("virtual:executor/worker-bundler-artifact");
if (artifact.source !== undefined && artifact.wasmBase64 !== undefined) {
return {
source: artifact.source,
wasm: base64ToArrayBuffer(artifact.wasmBase64),
};
}
if (workerAssets === undefined) {
// oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: worker artifact asset binding is required for production bundler setup
throw new Error("worker-bundler artifact assets binding is unavailable");
}
const [source, wasm] = await Promise.all([
fetchAsset(workerAssets, artifact.sourcePath).then((response) =>
response.text(),
),
fetchAsset(workerAssets, artifact.wasmPath).then((response) =>
response.arrayBuffer(),
),
]);
return {
source: artifact.source,
wasm: base64ToArrayBuffer(artifact.wasmBase64),
source,
wasm,
};
},
}),
Expand Down
54 changes: 54 additions & 0 deletions apps/cloud/scripts/assert-worker-bundler-assets.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { readdirSync, readFileSync, statSync } from "node:fs";
import { join } from "node:path";

const appRoot = new URL("..", import.meta.url);
const serverAssetsDir = new URL("dist/server/assets/", appRoot);
const clientAssetsDir = new URL("dist/client/assets/", appRoot);
const largeFileLimit = 3 * 1024 * 1024;
const sourceAssetMinimum = 512 * 1024;

const fail = (message) => {
// oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: build assertion script reports a failed artifact contract
throw new Error(message);
};

const fileSize = (dir, name) => statSync(join(dir.pathname, name)).size;

const serverAssets = readdirSync(serverAssetsDir);
for (const name of serverAssets) {
const path = join(serverAssetsDir.pathname, name);
const size = statSync(path).size;
if (size <= largeFileLimit) continue;
if (name.includes("worker-bundler")) {
fail(`server assets include oversized worker-bundler chunk ${name}`);
}
const sample = readFileSync(path, "utf8");
if (sample.includes("worker-bundler")) {
fail(`server assets include worker-bundler content in oversized chunk ${name}`);
}
}

const clientAssets = readdirSync(clientAssetsDir);
const source = clientAssets.find((name) => /^worker-bundler-source-[a-f0-9]{12}\.js$/.test(name));
const wasm = clientAssets.find((name) =>
/^worker-bundler-esbuild-wasm-[a-f0-9]{12}\.wasm$/.test(name),
);

if (source === undefined) fail("missing worker-bundler source asset");
if (wasm === undefined) fail("missing worker-bundler esbuild wasm asset");

const sourceSize = fileSize(clientAssetsDir, source);
const wasmSize = fileSize(clientAssetsDir, wasm);
if (sourceSize <= sourceAssetMinimum) {
fail(`worker-bundler source asset is unexpectedly small: ${source}`);
}
if (wasmSize <= largeFileLimit) fail(`worker-bundler wasm asset is unexpectedly small: ${wasm}`);

const wasmMagic = readFileSync(join(clientAssetsDir.pathname, wasm)).subarray(0, 4);
if (!wasmMagic.equals(Buffer.from([0x00, 0x61, 0x73, 0x6d]))) {
fail(`worker-bundler wasm asset does not start with wasm magic bytes: ${wasm}`);
}

console.log(
`[assert-worker-bundler-assets] ${source} (${sourceSize} bytes), ${wasm} (${wasmSize} bytes)`,
);
1 change: 1 addition & 0 deletions apps/cloud/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const steps = [
// imports, so they must be built before vite starts.
"turbo run build --filter @executor-js/vite-plugin --filter @executor-js/react --filter @executor-js/plugin-apps",
"vite build",
"node scripts/assert-worker-bundler-assets.mjs",
];

for (const step of steps) {
Expand Down
2 changes: 2 additions & 0 deletions apps/cloud/src/engine/execution-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const cloudPluginFactory = executorConfig.plugins as (deps: {
readonly activeToolkitSlug?: string;
readonly allowLocalNetwork?: boolean;
readonly workerLoader?: WorkerLoader;
readonly workerAssets?: { readonly fetch: (request: Request) => Promise<Response> };
}) => readonly AnyPlugin[];

// Fresh plugin instances per request, carrying the Worker env's WorkOS Vault
Expand All @@ -80,6 +81,7 @@ export const CloudPluginsProvider: Layer.Layer<PluginsProvider> = Layer.succeed(
context?.mcpResource?.kind === "toolkit" ? context.mcpResource.slug : undefined,
allowLocalNetwork: env.ALLOW_LOCAL_NETWORK === "true",
workerLoader: env.LOADER,
workerAssets: env.ASSETS,
}),
});

Expand Down
4 changes: 4 additions & 0 deletions apps/cloud/src/env-augment.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ declare global {
DATABASE_URL?: string;
EXECUTOR_DIRECT_DATABASE_URL?: string;

// Static asset binding emitted by @cloudflare/vite-plugin in
// dist/server/wrangler.json as assets.directory = "../client".
ASSETS: { readonly fetch: (request: Request) => Promise<Response> };

// Plugin blob seam backend (wrangler.jsonc `r2_buckets`). Declared here
// (optional) rather than regenerating worker-configuration.d.ts: test
// workers and older local setups run without the binding, and the db
Expand Down
6 changes: 4 additions & 2 deletions apps/cloud/src/worker-bundler-artifact.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
declare module "virtual:executor/worker-bundler-artifact" {
export const source: string;
export const wasmBase64: string;
export const sourcePath: string;
export const wasmPath: string;
export const source: string | undefined;
export const wasmBase64: string | undefined;
}
6 changes: 6 additions & 0 deletions apps/cloud/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
"compatibility_date": "2025-04-01",
"compatibility_flags": ["nodejs_compat"],
"main": "src/server.ts",
// The vite plugin fills in assets.directory (dist/client); the binding must
// be declared here or env.ASSETS does not exist at runtime. The apps plugin
// fetches the worker-bundler artifact through it on first sync.
"assets": {
"binding": "ASSETS",
},
"routes": [
{
"pattern": "executor.sh",
Expand Down
80 changes: 66 additions & 14 deletions packages/plugins/apps/src/vite.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,74 @@
import { createHash } from "node:crypto";
import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import type { Plugin } from "vite";

import { bundledWorkerBundler } from "./pipeline/worker-bundler-artifact";

const virtualId = "virtual:executor/worker-bundler-artifact";
const resolvedVirtualId = `\0${virtualId}`;
const clientAssetsDir = "dist/client/assets";

export const workerBundlerArtifact = (): Plugin => ({
name: "executor-worker-bundler-artifact",
resolveId(id) {
return id === virtualId ? resolvedVirtualId : null;
},
async load(id) {
if (id !== resolvedVirtualId) return null;
const artifact = await bundledWorkerBundler();
return [
`export const source = ${JSON.stringify(artifact.source)};`,
`export const wasmBase64 = ${JSON.stringify(Buffer.from(artifact.wasm).toString("base64"))};`,
"export default { source, wasmBase64 };",
].join("\n");
},
const digest = (value: string | Uint8Array): string =>
createHash("sha256").update(value).digest("hex").slice(0, 12);

const artifactPaths = (artifact: {
readonly source: string;
readonly wasm: Uint8Array;
}): {
readonly sourcePath: string;
readonly wasmPath: string;
} => ({
sourcePath: `/assets/worker-bundler-source-${digest(artifact.source)}.js`,
wasmPath: `/assets/worker-bundler-esbuild-wasm-${digest(artifact.wasm)}.wasm`,
});

export const workerBundlerArtifact = (): Plugin => {
let command: "build" | "serve" = "build";

return {
name: "executor-worker-bundler-artifact",
configResolved(config) {
command = config.command;
},
resolveId(id) {
return id === virtualId ? resolvedVirtualId : null;
},
async load(id) {
if (id !== resolvedVirtualId) return null;
const artifact = await bundledWorkerBundler();
const paths = artifactPaths(artifact);
const output = [
`export const sourcePath = ${JSON.stringify(paths.sourcePath)};`,
`export const wasmPath = ${JSON.stringify(paths.wasmPath)};`,
];

if (command === "serve") {
output.push(
`export const source = ${JSON.stringify(artifact.source)};`,
`export const wasmBase64 = ${JSON.stringify(Buffer.from(artifact.wasm).toString("base64"))};`,
"export default { sourcePath, wasmPath, source, wasmBase64 };",
);
} else {
output.push(
"export const source = undefined;",
"export const wasmBase64 = undefined;",
"export default { sourcePath, wasmPath, source, wasmBase64 };",
);
}

return output.join("\n");
},
async closeBundle() {
if (command !== "build") return;
const artifact = await bundledWorkerBundler();
const paths = artifactPaths(artifact);
const outputDir = join(process.cwd(), clientAssetsDir);
await mkdir(outputDir, { recursive: true });
await Promise.all([
writeFile(join(process.cwd(), "dist/client", paths.sourcePath), artifact.source),
writeFile(join(process.cwd(), "dist/client", paths.wasmPath), artifact.wasm),
]);
},
};
};
Loading