INTEGRITY Dokumentace

Statické prostředky

Dynamic Workers mohou vedle kódu vašeho Workeru obsluhovat i statická aktiva, jako jsou HTML stránky, balíčky JavaScriptu, obrázky a další soubory. To se hodí ve chvíli, kdy potřebujete, aby Dynamic Worker obsloužil celou fullstack aplikaci.

Statická aktiva pro Dynamic Workers fungují jinak než statická aktiva v běžných Workers. Assety tak nenahráváte při nasazení, ale poskytujete je za běhu prostřednictvím Worker Loaderu get() callbacku, kde je načtete z R2, KV nebo jiného úložiště.

Jak to funguje

Nastavení statických aktiv pro Dynamic Workers se skládá ze tří částí:

  1. Uložení aktiv: Nahrajte statické soubory do KV namespace, klíčované podle ID projektu a cesty k souboru.
  2. Definování bindingu pro aktiva v loader Workeru: Vytvořte třídu, která obsluhuje požadavky na statické soubory tak, že je čte z KV a vrací se správnými hlavičkami.
  3. Předání bindingu Dynamic Workeru: Dynamic Worker jej používá k obsluze statických souborů zavoláním env.ASSETS.fetch(request).

Uložení statických aktiv

Statická aktiva jsou uložena v KV namespace, odděleně podle ID projektu, takže soubory každého projektu jsou od sebe izolované:

project/{projectId}/assets/index.html      →  file content
project/{projectId}/assets/app.js          →  file content
project/{projectId}/manifest               →  asset manifest

Když uživatel nasadí svůj projekt přes nahrávací API vaší platformy, uložte každý soubor do KV pod jeho cestou:

await env.KV_ASSETS.put(`project/${projectId}/assets${pathname}`, fileContent);

Dále musíte uložit manifest, tedy mapování, které handleru pro aktiva říká, jaké soubory existují a jaké mají typy obsahu. Použijte buildAssetManifest() z @cloudflare/worker-bundler k jeho vygenerování z vašich assetů:

import { buildAssetManifest } from "@cloudflare/worker-bundler";

const assets = {
	"/index.html": htmlContent,
	"/app.js": jsContent,
	"/style.css": cssContent,
};

const manifest = await buildAssetManifest(assets);

await env.KV_ASSETS.put(
	`project/${projectId}/manifest`,
	JSON.stringify(manifest),
);
import { buildAssetManifest } from "@cloudflare/worker-bundler";

const assets = {
	"/index.html": htmlContent,
	"/app.js": jsContent,
	"/style.css": cssContent,
};

const manifest = await buildAssetManifest(assets);

await env.KV_ASSETS.put(
	`project/${projectId}/manifest`,
	JSON.stringify(manifest),
);

Přidání bindingů do loader Workeru

Udělení přístupu načítacímu Workeru ke KV namespace, kam jste aktiva uložili:

{
	"worker_loaders": [{ "binding": "LOADER" }],
	"kv_namespaces": [
		{
			"binding": "KV_ASSETS",
			"id": "<your-kv-namespace-id>",
		},
	],
}
[[worker_loaders]]
binding = "LOADER"

[[kv_namespaces]]
binding = "KV_ASSETS"
id = "<your-kv-namespace-id>"

Definování bindingu pro aktiva

V loader Workeru vytvořte třídu, která rozšiřuje WorkerEntrypoint a definujte fetch() metodu. WorkerEntrypoint umožňuje volat tuto metodu z Dynamic Workeru pomocí RPC. Když Dynamic Worker zavolá env.ASSETS.fetch(request), spustí se tato metoda v loader Workeru, kde běží binding KV a vaše logika pro obsluhu assetů.

Třída přijímá projectId prop, aby věděl, assety kterého projektu má vyhledat. Když fetch() se zavolá, provede toto:

  1. Načte manifest aktiv projektu z KV.
  2. Přeloží pathname požadavku na soubor.
  3. Načte obsah souboru z KV.
  4. Vrátí Response se správným Content-Type hlavička.

Použijte @cloudflare/worker-bundler k obsluze statických assetů

Místo psaní vlastní logiky pro párování cest požadavků se soubory, rozpoznávání typů obsahu a nastavování cache hlaviček použijte @cloudflare/worker-bundler balíček pro obsluhu statických assetů. Ve svém fetch() metodu předejte handleAssetRequest() dvě věci:

handleAssetRequest() obslouží soubor, pokud v manifestu najde shodu, a to se správnými hlavičkami pro typ obsahu a cachování.

import { WorkerEntrypoint } from "cloudflare:workers";
import { handleAssetRequest } from "@cloudflare/worker-bundler";

export class AssetBinding extends WorkerEntrypoint {
	async fetch(request) {
		const { projectId } = this.ctx.props;

		// Load the project's asset manifest from KV
		const manifest = await this.env.KV_ASSETS.get(
			`project/${projectId}/manifest`,
			{ type: "json", cacheTtl: 300 },
		);

		if (!manifest) {
			return new Response("No assets found", { status: 404 });
		}

		// Storage object — handleAssetRequest calls get() to
		// read file content when it needs to serve an asset
		const storage = {
			async get(pathname) {
				return this.env.KV_ASSETS.get(
					`project/${projectId}/assets${pathname}`,
					{ type: "arrayBuffer", cacheTtl: 86_400 },
				);
			},
		};

		const response = await handleAssetRequest(request, manifest, storage);
		return response ?? new Response("Not Found", { status: 404 });
	}
}
import { WorkerEntrypoint } from "cloudflare:workers";
import { handleAssetRequest } from "@cloudflare/worker-bundler";

export class AssetBinding extends WorkerEntrypoint {
	async fetch(request: Request) {
		const { projectId } = this.ctx.props;

		// Load the project's asset manifest from KV
		const manifest = await this.env.KV_ASSETS.get(
			`project/${projectId}/manifest`,
			{ type: "json", cacheTtl: 300 },
		);

		if (!manifest) {
			return new Response("No assets found", { status: 404 });
		}

		// Storage object — handleAssetRequest calls get() to
		// read file content when it needs to serve an asset
		const storage = {
			async get(pathname: string) {
				return this.env.KV_ASSETS.get(
					`project/${projectId}/assets${pathname}`,
					{ type: "arrayBuffer", cacheTtl: 86_400 },
				);
			},
		};

		const response = await handleAssetRequest(request, manifest, storage);
		return response ?? new Response("Not Found", { status: 404 });
	}
}

Jakmile AssetBinding se exportuje, je pak dostupné na ctx.exports v loader Workeru fetch() handler. ctx je třetí parametr handleru, za request a env. Takto jej v dalším kroku předáte Dynamic Workeru.

Předání asset bindingu Dynamic Workeru

Když zavoláte get() k vytvoření Dynamic Workeru zahrňte AssetBinding v env objekt, aby ho Dynamic Worker mohl použít k obsluze statických souborů. Pro odkaz na AssetBinding třídu, kterou jste definovali v předchozím kroku, použijte ctx.exports.AssetBinding() a předejte projectId jako prop, aby věděl, která assets projektu má obsluhovat. Funguje to stejně jako u vlastních bindingů: props je způsob, jak předat informace do třídy, která je pak čte na this.ctx.props při spuštění.

export default {
	async fetch(request, env, ctx) {
		const projectId = getProjectIdFromRequest(request);

		const worker = env.LOADER.get(projectId, async () => {
			const serverCode = await loadServerCode(projectId);

			return {
				mainModule: "index.js",
				modules: {
					"index.js": { js: serverCode },
				},
				compatibilityDate: "2026-08-28",
				env: {
					ASSETS: ctx.exports.AssetBinding({
						props: { projectId },
					}),
				},
			};
		});

		return await worker.getEntrypoint().fetch(request);
	},
};
export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext) {
		const projectId = getProjectIdFromRequest(request);

		const worker = env.LOADER.get(projectId, async () => {
			const serverCode = await loadServerCode(projectId);

			return {
				mainModule: "index.js",
				modules: {
					"index.js": { js: serverCode },
				},
				compatibilityDate: "$today",
				env: {
					ASSETS: ctx.exports.AssetBinding({
						props: { projectId },
					}),
				},
			};
		});

		return await worker.getEntrypoint().fetch(request);
	},
};

Dynamic Worker vidí ASSETS jako binding a může volat env.ASSETS.fetch(request) protože to je metoda, kterou jste definovali na AssetBinding. Když Dynamic Worker tuto metodu zavolá, spustí se v loader Workeru, kde běží vaše AssetBinding třída čte manifest a obsah souborů z KV.

Použijte binding pro aktiva v Dynamic Workeru

Z pohledu Dynamic Workeru, env.ASSETS funguje jako kterýkoli jiný binding. Uživatel napíše serverový kód a zavolá env.ASSETS.fetch() k obsluze statických souborů:

// Inside the Dynamic Worker
export default {
	async fetch(request, env) {
		const url = new URL(request.url);

		// Handle API routes directly
		if (url.pathname.startsWith("/api/")) {
			return Response.json({ hello: "world" });
		}

		// Everything else — serve static assets
		return env.ASSETS.fetch(request);
	},
};
// Inside the Dynamic Worker
export default {
	async fetch(request: Request, env: Env) {
		const url = new URL(request.url);

		// Handle API routes directly
		if (url.pathname.startsWith("/api/")) {
			return Response.json({ hello: "world" });
		}

		// Everything else — serve static assets
		return env.ASSETS.fetch(request);
	},
};

Když Dynamic Worker volá env.ASSETS.fetch(request), volání proběhne přes RPC do loader Workeru, konkrétně na jeho AssetBinding, která vyhledá soubor v manifestu a přečte jej z KV. Dynamic Worker se o nic z toho nemusí starat, jen zavolá env.ASSETS.fetch(request) a získá zpět soubor se správnými hlavičkami, připravený k odeslání klientovi.