createCloudflareAssetReader
Serves only build files listed in the manifest from Cloudflare's Static Assets binding, with the ETag, HEAD, 304 and caching behavior of the other adapters.
import { createCloudflareAssetReader } from "@meekojs/meeko/cloudflare";createCloudflareAssetReader({ binding, assets }) is the public file reader for Workers. Meeko's generated wrangler.jsonc declares assets with run_worker_first: true and html_handling/not_found_handling set to "none", so all requests reach the Worker; this reader only responds to paths in the build's asset manifest, reading them from the ASSETS binding, and returns null for all others.
This lets a Worker serve files with the same rules as Node, Bun and Deno: nothing outside the manifest, the same ETag and the same cache policy.
Usage#
import { createCloudflareAssetReader, createWorker } from "@meekojs/meeko/cloudflare";
import type { CloudflareAppContext } from "@meekojs/meeko/cloudflare";
function createCloudflareApp({ env, platform }: CloudflareAppContext<Env>) {
return createApp({
production: true,
platform,
assets: createCloudflareAssetReader({ binding: env.ASSETS, assets: manifest.assets ?? {} }),
});
}
export default createWorker(createCloudflareApp);Type#
function createCloudflareAssetReader(options: CloudflareAssetReaderOptions): AssetReaderinterface CloudflareAssetReaderOptions {
readonly binding: CloudflareAssetsBinding
readonly assets: AssetManifest
}interface CloudflareAssetsBinding {
fetch(request: Request): Promise<Response>
}interface AssetReader {
get(pathname: string, request: Request): Promise<Response | null>
}Parameters#
| Option | Type | Description |
|---|---|---|
binding |
CloudflareAssetsBinding |
The Static Assets binding, usually env.ASSETS: an object with fetch(request). |
assets |
AssetManifest |
Public paths with their type, etag, size and immutable. |
Return value#
An AssetReader with get(pathname, request): null for a path not in assets or a method other than GET or HEAD; 304 if If-None-Match matches, without reading the binding; 200 with the binding's body, content-type, ETag, content-length and cache-control (immutable for one year for hashed files, max-age=0, must-revalidate for the rest).
Examples#
The output of meeko build --adapter cloudflare#
.meeko/output/cloudflare/
worker.js ← createWorker and the room class, a single module
wrangler.jsonc ← assets: { directory: "./assets", binding: "ASSETS", run_worker_first: true }
assets/ ← client build and public/
assets/_meeko/prerender/ ← prerendered pages, which the Worker reads and never serves as filesChecking that a build file is not public#
curl -s -o /dev/null -w "%{http_code}\n" https://mi-app.example/_meeko/prerender/manifest.json # 404
curl -s -o /dev/null -w "%{http_code}\n" https://mi-app.example/worker.js # 404Common mistakes#
A listed file missing from assets#
assets/ uploaded without the assets/assets/ directory (a partial deployment)get rejects with Error: Missing build asset /assets/entry-4f2a.js; the application responds with 500 and sends it to onError. Deploy the complete output folder with wrangler deploy --config .meeko/output/cloudflare/wrangler.jsonc.
npx wrangler deploy --config .meeko/output/cloudflare/wrangler.jsoncA public/_meeko/#
public/_meeko/logo.svgThe assets' _meeko/ is reserved for prerendered pages: meeko build --adapter cloudflare stops with public/_meeko está reservado para la salida de Cloudflare de Meeko; cámbiale el nombre.
public/brand/logo.svgChanging assets in server.wrangler#
server: { adapter: "cloudflare", wrangler: { assets: { directory: "./public", not_found_handling: "single-page-application" } } }meeko build --adapter cloudflare stops with TypeError: server.wrangler.assets is generated by Meeko and cannot be overridden (the same applies to main, no_bundle and find_additional_modules): the Worker must see every request to apply the manifest, sessions and CSP.
server: { adapter: "cloudflare", wrangler: { routes: [{ pattern: "mi-app.example", custom_domain: true }] } }Notes#
- Security. Paths are looked up in the manifest without decoding, and the URL requested from the binding comes from the manifest key, never from the request.
- Cost. With
run_worker_first, each file is a Worker invocation; hashed files carryimmutable, and Cloudflare's CDN and the browser cache them for a year. - CSP and headers (since 1.7).
server.headersalso applies to these files, except headers they already carry, such asCache-Control.