@astrojs/cloudflare 13.0.0-beta.1 → 13.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/entrypoints/{image-service.js → image-service-external.js} +2 -2
  2. package/dist/entrypoints/image-service-workerd.d.ts +14 -0
  3. package/dist/entrypoints/image-service-workerd.js +11 -0
  4. package/dist/entrypoints/image-transform-endpoint.js +21 -2
  5. package/dist/entrypoints/preview.js +4 -7
  6. package/dist/esbuild-plugin-astro-frontmatter.d.ts +9 -0
  7. package/dist/esbuild-plugin-astro-frontmatter.js +30 -0
  8. package/dist/index.d.ts +7 -57
  9. package/dist/index.js +108 -81
  10. package/dist/info.d.ts +5 -0
  11. package/dist/info.js +4 -0
  12. package/dist/prerender-types.d.ts +32 -0
  13. package/dist/prerender-types.js +0 -0
  14. package/dist/prerenderer.d.ts +17 -0
  15. package/dist/prerenderer.js +116 -0
  16. package/dist/utils/generate-routes-json.d.ts +1 -8
  17. package/dist/utils/generate-routes-json.js +0 -203
  18. package/dist/utils/handler.d.ts +3 -8
  19. package/dist/utils/handler.js +40 -18
  20. package/dist/utils/image-binding-transform.d.ts +1 -2
  21. package/dist/utils/image-binding-transform.js +14 -6
  22. package/dist/utils/image-config.d.ts +19 -6
  23. package/dist/utils/image-config.js +30 -8
  24. package/dist/utils/prerender-constants.d.ts +9 -0
  25. package/dist/utils/prerender-constants.js +8 -0
  26. package/dist/utils/prerender.d.ts +37 -0
  27. package/dist/utils/prerender.js +76 -0
  28. package/dist/utils/static-image-collection.d.ts +7 -0
  29. package/dist/utils/static-image-collection.js +51 -0
  30. package/dist/vite-plugin-config.d.ts +10 -3
  31. package/dist/vite-plugin-config.js +5 -2
  32. package/dist/wrangler.d.ts +3 -3
  33. package/package.json +17 -17
  34. package/types.d.ts +3 -18
  35. package/dist/entrypoints/image-endpoint.d.ts +0 -3
  36. package/dist/entrypoints/image-endpoint.js +0 -29
  37. package/dist/utils/cloudflare-module-loader.d.ts +0 -16
  38. package/dist/utils/cloudflare-module-loader.js +0 -152
  39. /package/dist/entrypoints/{image-service.d.ts → image-service-external.d.ts} +0 -0
@@ -0,0 +1,76 @@
1
+ import { serializeRouteData, deserializeRouteData } from "astro/app/manifest";
2
+ import { StaticPaths } from "astro:static-paths";
3
+ import {
4
+ STATIC_PATHS_ENDPOINT,
5
+ PRERENDER_ENDPOINT,
6
+ STATIC_IMAGES_ENDPOINT
7
+ } from "./prerender-constants.js";
8
+ function isStaticPathsRequest(request) {
9
+ const { pathname } = new URL(request.url);
10
+ return pathname === STATIC_PATHS_ENDPOINT && request.method === "POST";
11
+ }
12
+ function isPrerenderRequest(request) {
13
+ const { pathname } = new URL(request.url);
14
+ return pathname === PRERENDER_ENDPOINT && request.method === "POST";
15
+ }
16
+ async function handleStaticPathsRequest(app) {
17
+ const staticPaths = new StaticPaths(app);
18
+ const paths = await staticPaths.getAll();
19
+ const response = {
20
+ paths: paths.map(({ pathname, route }) => ({
21
+ pathname,
22
+ route: serializeRouteData(route, app.manifest.trailingSlash)
23
+ }))
24
+ };
25
+ return new Response(JSON.stringify(response), {
26
+ headers: { "Content-Type": "application/json" }
27
+ });
28
+ }
29
+ async function handlePrerenderRequest(app, request) {
30
+ const headers = new Headers();
31
+ for (const [key, value] of request.headers.entries()) {
32
+ headers.append(key, value);
33
+ }
34
+ const body = await request.json();
35
+ const routeData = deserializeRouteData(body.routeData);
36
+ const prerenderRequest = new Request(body.url, {
37
+ method: "GET",
38
+ headers
39
+ });
40
+ return app.render(prerenderRequest, { routeData });
41
+ }
42
+ function isStaticImagesRequest(request) {
43
+ const { pathname } = new URL(request.url);
44
+ return pathname === STATIC_IMAGES_ENDPOINT && request.method === "POST";
45
+ }
46
+ function handleStaticImagesRequest() {
47
+ const staticImages = globalThis.astroAsset?.staticImages;
48
+ if (!staticImages || staticImages.size === 0) {
49
+ return new Response("[]", {
50
+ headers: { "Content-Type": "application/json" }
51
+ });
52
+ }
53
+ const entries = [];
54
+ for (const [originalPath, { originalSrcPath, transforms }] of staticImages) {
55
+ const serializedTransforms = [];
56
+ for (const [hash, { finalPath, transform }] of transforms) {
57
+ serializedTransforms.push({
58
+ hash,
59
+ finalPath,
60
+ transform
61
+ });
62
+ }
63
+ entries.push({ originalPath, originalSrcPath, transforms: serializedTransforms });
64
+ }
65
+ return new Response(JSON.stringify(entries), {
66
+ headers: { "Content-Type": "application/json" }
67
+ });
68
+ }
69
+ export {
70
+ handlePrerenderRequest,
71
+ handleStaticImagesRequest,
72
+ handleStaticPathsRequest,
73
+ isPrerenderRequest,
74
+ isStaticImagesRequest,
75
+ isStaticPathsRequest
76
+ };
@@ -0,0 +1,7 @@
1
+ import type { CompileImageConfig } from '../vite-plugin-config.js';
2
+ /**
3
+ * Installs `globalThis.astroAsset.addStaticImage` for use inside workerd
4
+ * during prerendering. This mirrors the logic in astro's vite-plugin-assets.ts
5
+ * but uses only workerd-safe APIs (no node: imports).
6
+ */
7
+ export declare function installAddStaticImage(config: CompileImageConfig): void;
@@ -0,0 +1,51 @@
1
+ import { joinPaths, prependForwardSlash, removeBase } from "@astrojs/internal-helpers/path";
2
+ import { hashTransform, propsToFilename } from "astro/assets";
3
+ import { isESMImportedImage } from "astro/assets/utils";
4
+ function installAddStaticImage(config) {
5
+ if (globalThis.astroAsset?.addStaticImage) return;
6
+ if (!globalThis.astroAsset) {
7
+ globalThis.astroAsset = { referencedImages: /* @__PURE__ */ new Set() };
8
+ }
9
+ globalThis.astroAsset.addStaticImage = (options, hashProperties, _originalFSPath) => {
10
+ if (!globalThis.astroAsset.staticImages) {
11
+ globalThis.astroAsset.staticImages = /* @__PURE__ */ new Map();
12
+ }
13
+ const ESMImportedImageSrc = isESMImportedImage(options.src) ? options.src.src : options.src;
14
+ const finalOriginalPath = removeBase(
15
+ removeBase(ESMImportedImageSrc, config.base),
16
+ config.assetsPrefix ?? ""
17
+ );
18
+ const hash = hashTransform(options, config.imageServiceEntrypoint, hashProperties);
19
+ let finalFilePath;
20
+ let transformsForPath = globalThis.astroAsset.staticImages.get(finalOriginalPath);
21
+ const transformForHash = transformsForPath?.transforms.get(hash);
22
+ if (transformsForPath && transformForHash) {
23
+ finalFilePath = transformForHash.finalPath;
24
+ } else {
25
+ finalFilePath = prependForwardSlash(
26
+ joinPaths(
27
+ isESMImportedImage(options.src) ? "" : config.buildAssets,
28
+ prependForwardSlash(propsToFilename(finalOriginalPath, options, hash))
29
+ )
30
+ );
31
+ if (!transformsForPath) {
32
+ globalThis.astroAsset.staticImages.set(finalOriginalPath, {
33
+ originalSrcPath: _originalFSPath,
34
+ transforms: /* @__PURE__ */ new Map()
35
+ });
36
+ transformsForPath = globalThis.astroAsset.staticImages.get(finalOriginalPath);
37
+ }
38
+ transformsForPath.transforms.set(hash, {
39
+ finalPath: finalFilePath,
40
+ transform: options
41
+ });
42
+ }
43
+ if (config.assetsPrefix) {
44
+ return encodeURI(joinPaths(config.assetsPrefix, finalFilePath));
45
+ }
46
+ return encodeURI(prependForwardSlash(joinPaths(config.base, finalFilePath)));
47
+ };
48
+ }
49
+ export {
50
+ installAddStaticImage
51
+ };
@@ -1,6 +1,13 @@
1
1
  import type { PluginOption } from 'vite';
2
- interface CloudflareConfig {
2
+ export interface CompileImageConfig {
3
+ base: string;
4
+ assetsPrefix: string | undefined;
5
+ imageServiceEntrypoint: string;
6
+ buildAssets: string;
7
+ }
8
+ export interface Config {
3
9
  sessionKVBindingName: string;
10
+ compileImageConfig: CompileImageConfig | null;
11
+ isPrerender: boolean;
4
12
  }
5
- export declare function createConfigPlugin(config: CloudflareConfig): PluginOption;
6
- export {};
13
+ export declare function createConfigPlugin(config: Omit<Config, 'isPrerender'>): PluginOption;
@@ -2,7 +2,7 @@ const VIRTUAL_CONFIG_ID = "virtual:astro-cloudflare:config";
2
2
  const RESOLVED_VIRTUAL_CONFIG_ID = "\0" + VIRTUAL_CONFIG_ID;
3
3
  function createConfigPlugin(config) {
4
4
  return {
5
- name: "vite:astro-cloudflare-config",
5
+ name: VIRTUAL_CONFIG_ID,
6
6
  resolveId: {
7
7
  filter: {
8
8
  id: new RegExp(`^${VIRTUAL_CONFIG_ID}$`)
@@ -16,7 +16,10 @@ function createConfigPlugin(config) {
16
16
  id: new RegExp(`^${RESOLVED_VIRTUAL_CONFIG_ID}$`)
17
17
  },
18
18
  handler() {
19
- return `export const sessionKVBindingName = ${JSON.stringify(config.sessionKVBindingName)};`;
19
+ return [
20
+ ...Object.entries(config).map(([k, v]) => `export const ${k} = ${JSON.stringify(v)};`),
21
+ `export const isPrerender = ${this.environment?.name === "prerender"};`
22
+ ].join("\n");
20
23
  }
21
24
  }
22
25
  };
@@ -3,12 +3,12 @@ export declare const DEFAULT_SESSION_KV_BINDING_NAME = "SESSION";
3
3
  export declare const DEFAULT_IMAGES_BINDING_NAME = "IMAGES";
4
4
  export declare const DEFAULT_ASSETS_BINDING_NAME = "ASSETS";
5
5
  interface CloudflareConfigOptions {
6
- sessionKVBindingName?: string;
7
- imagesBindingName?: string | false;
6
+ sessionKVBindingName: string | undefined;
7
+ imagesBindingName: string | false | undefined;
8
8
  }
9
9
  /**
10
10
  * Returns a config customizer that sets up the Astro Cloudflare defaults.
11
11
  * Sets the main entrypoint and adds bindings for auto-provisioning.
12
12
  */
13
- export declare function cloudflareConfigCustomizer(options?: CloudflareConfigOptions): PluginConfig['config'];
13
+ export declare function cloudflareConfigCustomizer(options: CloudflareConfigOptions): PluginConfig['config'];
14
14
  export {};
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@astrojs/cloudflare",
3
- "description": "Deploy your site to Cloudflare Workers/Pages",
4
- "version": "13.0.0-beta.1",
3
+ "description": "Deploy your site to Cloudflare Workers",
4
+ "version": "13.0.0-beta.11",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "author": "withastro",
8
8
  "license": "MIT",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/withastro/astro.git",
11
+ "url": "git+https://github.com/withastro/astro.git",
12
12
  "directory": "packages/integrations/cloudflare"
13
13
  },
14
14
  "keywords": [
@@ -19,12 +19,13 @@
19
19
  "homepage": "https://docs.astro.build/en/guides/integrations-guide/cloudflare/",
20
20
  "exports": {
21
21
  ".": "./dist/index.js",
22
+ "./info": "./dist/info.js",
22
23
  "./entrypoints/server": "./dist/entrypoints/server.js",
23
24
  "./entrypoints/preview": "./dist/entrypoints/preview.js",
24
25
  "./entrypoints/server.js": "./dist/entrypoints/server.js",
25
- "./image-service": "./dist/entrypoints/image-service.js",
26
- "./image-endpoint": "./dist/entrypoints/image-endpoint.js",
26
+ "./image-service": "./dist/entrypoints/image-service-external.js",
27
27
  "./image-transform-endpoint": "./dist/entrypoints/image-transform-endpoint.js",
28
+ "./image-service-workerd": "./dist/entrypoints/image-service-workerd.js",
28
29
  "./handler": "./dist/utils/handler.js",
29
30
  "./types.d.ts": "./types.d.ts",
30
31
  "./package.json": "./package.json"
@@ -34,24 +35,23 @@
34
35
  "types.d.ts"
35
36
  ],
36
37
  "dependencies": {
37
- "@cloudflare/vite-plugin": "^1.17.0",
38
- "@cloudflare/workers-types": "^4.20260116.0",
39
- "dotenv": "^17.2.3",
38
+ "@cloudflare/vite-plugin": "^1.25.2",
40
39
  "piccolore": "^0.1.3",
41
40
  "tinyglobby": "^0.2.15",
42
- "vite": "^7.1.12",
43
- "@astrojs/internal-helpers": "0.7.5",
41
+ "vite": "^7.3.1",
42
+ "@astrojs/internal-helpers": "0.8.0-beta.1",
44
43
  "@astrojs/underscore-redirects": "1.0.0"
45
44
  },
46
45
  "peerDependencies": {
47
46
  "astro": "^6.0.0-alpha.0",
48
- "wrangler": "^4.53.0"
47
+ "wrangler": "^4.61.1"
49
48
  },
50
49
  "devDependencies": {
51
- "cheerio": "1.1.2",
52
- "devalue": "^5.6.2",
53
- "rollup": "^4.55.1",
54
- "astro": "6.0.0-beta.2",
50
+ "@cloudflare/workers-types": "^4.20260228.0",
51
+ "@types/node": "^25.2.2",
52
+ "cheerio": "1.2.0",
53
+ "devalue": "^5.6.3",
54
+ "astro": "6.0.0-beta.17",
55
55
  "astro-scripts": "0.0.14"
56
56
  },
57
57
  "publishConfig": {
@@ -59,8 +59,8 @@
59
59
  },
60
60
  "scripts": {
61
61
  "dev": "astro-scripts dev \"src/**/*.ts\"",
62
- "build": "astro-scripts build \"src/**/*.ts\" && tsc",
62
+ "build": "astro-scripts build \"src/**/*.ts\" --clean-dts && tsc",
63
63
  "build:ci": "astro-scripts build \"src/**/*.ts\"",
64
- "test": "astro-scripts test \"test/**/*.test.js\""
64
+ "test": "astro-scripts test --force-exit \"test/**/*.test.js\""
65
65
  }
66
66
  }
package/types.d.ts CHANGED
@@ -1,20 +1,5 @@
1
- /**
2
- * Cloudflare Worker Request types
3
- * Extends the global Request object with Cloudflare-specific properties
4
- */
1
+ type Runtime = import('./dist/index.d.ts').Runtime;
5
2
 
6
- import type { IncomingRequestCfProperties } from '@cloudflare/workers-types';
7
-
8
- declare global {
9
- interface Request {
10
- /**
11
- * Cloudflare-specific properties available on incoming requests
12
- * Contains metadata about the request such as:
13
- * - Geographic information (country, colo, timezone)
14
- * - TLS/Security details (cipher, protocol version)
15
- * - Bot Management scores
16
- * - Client information (ASN, TCP metrics)
17
- */
18
- readonly cf?: IncomingRequestCfProperties;
19
- }
3
+ declare namespace App {
4
+ interface Locals extends Runtime {}
20
5
  }
@@ -1,3 +0,0 @@
1
- import type { APIRoute } from 'astro';
2
- export declare const prerender = false;
3
- export declare const GET: APIRoute;
@@ -1,29 +0,0 @@
1
- import { imageConfig } from "astro:assets";
2
- import { isRemotePath } from "@astrojs/internal-helpers/path";
3
- import { isRemoteAllowed } from "@astrojs/internal-helpers/remote";
4
- const prerender = false;
5
- const GET = (ctx) => {
6
- const href = ctx.url.searchParams.get("href");
7
- if (!href) {
8
- return new Response("Missing 'href' query parameter", {
9
- status: 400,
10
- statusText: "Missing 'href' query parameter"
11
- });
12
- }
13
- if (isRemotePath(href)) {
14
- if (isRemoteAllowed(href, imageConfig) === false) {
15
- return new Response("Forbidden", { status: 403 });
16
- } else {
17
- return Response.redirect(href, 302);
18
- }
19
- }
20
- const proxied = new URL(href, ctx.url.origin);
21
- if (proxied.origin !== ctx.url.origin) {
22
- return new Response("Forbidden", { status: 403 });
23
- }
24
- return fetch(proxied);
25
- };
26
- export {
27
- GET,
28
- prerender
29
- };
@@ -1,16 +0,0 @@
1
- import type { PluginOption } from 'vite';
2
- /**
3
- * Enables support for various non-standard extensions in module imports that cloudflare workers supports.
4
- *
5
- * See https://developers.cloudflare.com/pages/functions/module-support/ for reference
6
- *
7
- * This adds supports for imports in the following formats:
8
- * - .wasm
9
- * - .wasm?module
10
- * - .bin
11
- * - .txt
12
- *
13
- * @param enabled - if true, will load all cloudflare pages supported types
14
- * @returns Vite plugin with additional extension method to hook into astro build
15
- */
16
- export declare function cloudflareModuleLoader(enabled: boolean): PluginOption;
@@ -1,152 +0,0 @@
1
- import * as fs from "node:fs/promises";
2
- import * as path from "node:path";
3
- function cloudflareModuleLoader(enabled) {
4
- const adaptersByExtension = enabled ? { ...defaultAdapters } : {};
5
- const extensions = Object.keys(adaptersByExtension);
6
- let isDev = false;
7
- const MAGIC_STRING = "__CLOUDFLARE_ASSET__";
8
- const replacements = [];
9
- return {
10
- name: "vite:cf-module-loader",
11
- enforce: "pre",
12
- applyToEnvironment(environment) {
13
- return environment.name === "ssr" || environment.name === "client";
14
- },
15
- configResolved(config) {
16
- isDev = config.command === "serve";
17
- },
18
- config(_, __) {
19
- return {
20
- assetsInclude: extensions.map((x) => `**/*${x}`),
21
- build: {
22
- rollupOptions: {
23
- // mark the wasm files as external so that they are not bundled and instead are loaded from the files
24
- external: extensions.map(
25
- (x) => new RegExp(`^${MAGIC_STRING}.+${escapeRegExp(x)}$`, "i")
26
- )
27
- }
28
- }
29
- };
30
- },
31
- load: {
32
- filter: {
33
- // Escape the dot in the regex
34
- id: new RegExp(`.(${extensions.map((ext) => ext.slice(1)).join("|")})$`)
35
- },
36
- async handler(id, _) {
37
- const maybeExtension = extensions.find((x) => id.endsWith(x));
38
- const moduleType = adaptersByExtension[maybeExtension];
39
- if (!enabled) {
40
- throw new Error(
41
- `Cloudflare module loading is experimental. The ${maybeExtension} module cannot be loaded unless you add \`cloudflareModules: true\` to your astro config.`
42
- );
43
- }
44
- const moduleLoader = renderers[moduleType];
45
- const filePath = id.replace(/\?\w+$/, "");
46
- const extension = maybeExtension.replace(/\?\w+$/, "");
47
- const data = await fs.readFile(filePath);
48
- const base64 = data.toString("base64");
49
- const inlineModule = moduleLoader(data);
50
- if (isDev) {
51
- return inlineModule;
52
- }
53
- const hash = hashString(base64);
54
- const assetName = `${path.basename(filePath).split(".")[0]}.${hash}${extension}`;
55
- this.emitFile({
56
- type: "asset",
57
- // emit the data explicitly as an asset with `fileName` rather than `name` so that
58
- // vite doesn't give it a random hash-id in its name--We need to be able to easily rewrite from
59
- // the loader and the actual asset later in the build for the worker
60
- fileName: assetName,
61
- source: data
62
- });
63
- const chunkId = this.emitFile({
64
- type: "prebuilt-chunk",
65
- fileName: assetName,
66
- code: inlineModule
67
- });
68
- return `import module from "${MAGIC_STRING}${chunkId}${extension}";export default module;`;
69
- }
70
- },
71
- // output original wasm file relative to the chunk now that chunking has been achieved
72
- renderChunk(code, chunk, _) {
73
- if (isDev) return;
74
- if (!code.includes(MAGIC_STRING)) return;
75
- let replaced = code;
76
- for (const ext of extensions) {
77
- const extension = ext.replace(/\?\w+$/, "");
78
- replaced = replaced.replaceAll(
79
- new RegExp(`${MAGIC_STRING}([^\\s]+?)${escapeRegExp(extension)}`, "g"),
80
- (_s, assetId) => {
81
- const fileName = this.getFileName(assetId);
82
- const relativePath = path.relative(path.dirname(chunk.fileName), fileName).replaceAll("\\", "/");
83
- replacements.push({
84
- chunkName: chunk.name,
85
- cloudflareImport: relativePath,
86
- nodejsImport: relativePath
87
- });
88
- return `./${relativePath}`;
89
- }
90
- );
91
- }
92
- return { code: replaced };
93
- },
94
- generateBundle(_, bundle) {
95
- const replacementsByChunkName = /* @__PURE__ */ new Map();
96
- for (const replacement of replacements) {
97
- const repls = replacementsByChunkName.get(replacement.chunkName) || [];
98
- if (!repls.length) {
99
- replacementsByChunkName.set(replacement.chunkName, repls);
100
- }
101
- repls.push(replacement);
102
- }
103
- for (const chunk of Object.values(bundle)) {
104
- const repls = chunk.name && replacementsByChunkName.get(chunk.name);
105
- for (const replacement of repls || []) {
106
- if (!replacement.fileName) {
107
- replacement.fileName = [];
108
- }
109
- replacement.fileName.push(chunk.fileName);
110
- }
111
- }
112
- }
113
- };
114
- }
115
- const renderers = {
116
- CompiledWasm(fileContents) {
117
- const base64 = fileContents.toString("base64");
118
- return `const wasmModule = new WebAssembly.Module(Uint8Array.from(atob("${base64}"), c => c.charCodeAt(0)));export default wasmModule;`;
119
- },
120
- Data(fileContents) {
121
- const base64 = fileContents.toString("base64");
122
- return `const binModule = Uint8Array.from(atob("${base64}"), c => c.charCodeAt(0)).buffer;export default binModule;`;
123
- },
124
- Text(fileContents) {
125
- const escaped = JSON.stringify(fileContents.toString("utf-8"));
126
- return `const stringModule = ${escaped};export default stringModule;`;
127
- }
128
- };
129
- const defaultAdapters = {
130
- // Loads '*.wasm?module' imports as WebAssembly modules, which is the only way to load WASM in cloudflare workers.
131
- // Current proposal for WASM modules: https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration
132
- ".wasm?module": "CompiledWasm",
133
- // treats the module as a WASM module
134
- ".wasm": "CompiledWasm",
135
- ".bin": "Data",
136
- ".txt": "Text"
137
- };
138
- function hashString(str) {
139
- let hash = 0;
140
- for (let i = 0; i < str.length; i++) {
141
- const char = str.charCodeAt(i);
142
- hash = (hash << 5) - hash + char;
143
- hash &= hash;
144
- }
145
- return new Uint32Array([hash])[0].toString(36);
146
- }
147
- function escapeRegExp(string) {
148
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
149
- }
150
- export {
151
- cloudflareModuleLoader
152
- };