@astrojs/cloudflare 7.0.2 → 7.1.0
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.
- package/README.md +25 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +127 -6
- package/dist/parser.d.ts +19 -0
- package/dist/parser.js +87 -0
- package/dist/server.advanced.js +0 -10
- package/dist/server.directory.js +1 -11
- package/package.json +9 -3
- package/runtime.d.ts +0 -3
package/README.md
CHANGED
|
@@ -142,7 +142,7 @@ declare namespace App {
|
|
|
142
142
|
}
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
### Environment Variables
|
|
146
146
|
|
|
147
147
|
See Cloudflare's documentation for [working with environment variables](https://developers.cloudflare.com/pages/platform/functions/bindings/#environment-variables).
|
|
148
148
|
|
|
@@ -159,6 +159,30 @@ export function GET({ params }) {
|
|
|
159
159
|
}
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
+
### `cloudflare.runtime`
|
|
163
|
+
|
|
164
|
+
`runtime: "off" | "local" | "remote"`
|
|
165
|
+
default `"off"`
|
|
166
|
+
|
|
167
|
+
This optional flag enables the Astro dev server to populate environment variables and the Cloudflare Request Object, avoiding the need for Wrangler.
|
|
168
|
+
|
|
169
|
+
- `local`: environment variables are available, but the request object is populated from a static placeholder value.
|
|
170
|
+
- `remote`: environment variables and the live, fetched request object are available.
|
|
171
|
+
- `off`: the Astro dev server will populate neither environment variables nor the request object. Use Wrangler to access Cloudflare bindings and environment variables.
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
// astro.config.mjs
|
|
175
|
+
import { defineConfig } from 'astro/config';
|
|
176
|
+
import cloudflare from '@astrojs/cloudflare';
|
|
177
|
+
|
|
178
|
+
export default defineConfig({
|
|
179
|
+
output: 'server',
|
|
180
|
+
adapter: cloudflare({
|
|
181
|
+
runtime: 'off' | 'local' | 'remote',
|
|
182
|
+
}),
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
162
186
|
## Headers, Redirects and function invocation routes
|
|
163
187
|
|
|
164
188
|
Cloudflare has support for adding custom [headers](https://developers.cloudflare.com/pages/platform/headers/), configuring static [redirects](https://developers.cloudflare.com/pages/platform/redirects/) and defining which routes should [invoke functions](https://developers.cloudflare.com/pages/platform/functions/routing/#function-invocation-routes). Cloudflare looks for `_headers`, `_redirects`, and `_routes.json` files in your build output directory to configure these features. This means they should be placed in your Astro project’s `public/` directory.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,14 @@ import type { AstroAdapter, AstroIntegration } from 'astro';
|
|
|
2
2
|
export type { AdvancedRuntime } from './server.advanced';
|
|
3
3
|
export type { DirectoryRuntime } from './server.directory';
|
|
4
4
|
type Options = {
|
|
5
|
-
mode
|
|
5
|
+
mode?: 'directory' | 'advanced';
|
|
6
6
|
functionPerRoute?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* 'off': current behaviour (wrangler is needed)
|
|
9
|
+
* 'local': use a static req.cf object, and env vars defined in wrangler.toml & .dev.vars (astro dev is enough)
|
|
10
|
+
* 'remote': use a dynamic real-live req.cf object, and env vars defined in wrangler.toml & .dev.vars (astro dev is enough)
|
|
11
|
+
*/
|
|
12
|
+
runtime?: 'off' | 'local' | 'remote';
|
|
7
13
|
};
|
|
8
14
|
export declare function getAdapter({ isModeDirectory, functionPerRoute, }: {
|
|
9
15
|
isModeDirectory: boolean;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import { createRedirectsFromAstroRoutes } from "@astrojs/underscore-redirects";
|
|
2
|
+
import { CacheStorage } from "@miniflare/cache";
|
|
3
|
+
import { NoOpLog } from "@miniflare/shared";
|
|
4
|
+
import { MemoryStorage } from "@miniflare/storage-memory";
|
|
5
|
+
import { AstroError } from "astro/errors";
|
|
2
6
|
import esbuild from "esbuild";
|
|
3
7
|
import * as fs from "node:fs";
|
|
4
8
|
import * as os from "node:os";
|
|
5
9
|
import { sep } from "node:path";
|
|
6
10
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
11
|
import glob from "tiny-glob";
|
|
12
|
+
import { getEnvVars } from "./parser.js";
|
|
13
|
+
class StorageFactory {
|
|
14
|
+
storages = /* @__PURE__ */ new Map();
|
|
15
|
+
storage(namespace) {
|
|
16
|
+
let storage = this.storages.get(namespace);
|
|
17
|
+
if (storage)
|
|
18
|
+
return storage;
|
|
19
|
+
this.storages.set(namespace, storage = new MemoryStorage());
|
|
20
|
+
return storage;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
8
23
|
function getAdapter({
|
|
9
24
|
isModeDirectory,
|
|
10
25
|
functionPerRoute
|
|
@@ -43,6 +58,71 @@ function getAdapter({
|
|
|
43
58
|
}
|
|
44
59
|
};
|
|
45
60
|
}
|
|
61
|
+
async function getCFObject(runtimeMode) {
|
|
62
|
+
const CF_ENDPOINT = "https://workers.cloudflare.com/cf.json";
|
|
63
|
+
const CF_FALLBACK = {
|
|
64
|
+
asOrganization: "",
|
|
65
|
+
asn: 395747,
|
|
66
|
+
colo: "DFW",
|
|
67
|
+
city: "Austin",
|
|
68
|
+
region: "Texas",
|
|
69
|
+
regionCode: "TX",
|
|
70
|
+
metroCode: "635",
|
|
71
|
+
postalCode: "78701",
|
|
72
|
+
country: "US",
|
|
73
|
+
continent: "NA",
|
|
74
|
+
timezone: "America/Chicago",
|
|
75
|
+
latitude: "30.27130",
|
|
76
|
+
longitude: "-97.74260",
|
|
77
|
+
clientTcpRtt: 0,
|
|
78
|
+
httpProtocol: "HTTP/1.1",
|
|
79
|
+
requestPriority: "weight=192;exclusive=0",
|
|
80
|
+
tlsCipher: "AEAD-AES128-GCM-SHA256",
|
|
81
|
+
tlsVersion: "TLSv1.3",
|
|
82
|
+
tlsClientAuth: {
|
|
83
|
+
certPresented: "0",
|
|
84
|
+
certVerified: "NONE",
|
|
85
|
+
certRevoked: "0",
|
|
86
|
+
certIssuerDN: "",
|
|
87
|
+
certSubjectDN: "",
|
|
88
|
+
certIssuerDNRFC2253: "",
|
|
89
|
+
certSubjectDNRFC2253: "",
|
|
90
|
+
certIssuerDNLegacy: "",
|
|
91
|
+
certSubjectDNLegacy: "",
|
|
92
|
+
certSerial: "",
|
|
93
|
+
certIssuerSerial: "",
|
|
94
|
+
certSKI: "",
|
|
95
|
+
certIssuerSKI: "",
|
|
96
|
+
certFingerprintSHA1: "",
|
|
97
|
+
certFingerprintSHA256: "",
|
|
98
|
+
certNotBefore: "",
|
|
99
|
+
certNotAfter: ""
|
|
100
|
+
},
|
|
101
|
+
edgeRequestKeepAliveStatus: 0,
|
|
102
|
+
hostMetadata: void 0,
|
|
103
|
+
clientTrustScore: 99,
|
|
104
|
+
botManagement: {
|
|
105
|
+
corporateProxy: false,
|
|
106
|
+
verifiedBot: false,
|
|
107
|
+
ja3Hash: "25b4882c2bcb50cd6b469ff28c596742",
|
|
108
|
+
staticResource: false,
|
|
109
|
+
detectionIds: [],
|
|
110
|
+
score: 99
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
if (runtimeMode === "local") {
|
|
114
|
+
return CF_FALLBACK;
|
|
115
|
+
} else if (runtimeMode === "remote") {
|
|
116
|
+
try {
|
|
117
|
+
const res = await fetch(CF_ENDPOINT);
|
|
118
|
+
const cfText = await res.text();
|
|
119
|
+
const storedCf = JSON.parse(cfText);
|
|
120
|
+
return storedCf;
|
|
121
|
+
} catch (e) {
|
|
122
|
+
return CF_FALLBACK;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
46
126
|
const SHIM = `globalThis.process = {
|
|
47
127
|
argv: [],
|
|
48
128
|
env: {},
|
|
@@ -55,6 +135,7 @@ function createIntegration(args) {
|
|
|
55
135
|
let _entryPoints = /* @__PURE__ */ new Map();
|
|
56
136
|
const isModeDirectory = args?.mode === "directory";
|
|
57
137
|
const functionPerRoute = args?.functionPerRoute ?? false;
|
|
138
|
+
const runtimeMode = args?.runtime ?? "off";
|
|
58
139
|
return {
|
|
59
140
|
name: "@astrojs/cloudflare",
|
|
60
141
|
hooks: {
|
|
@@ -73,14 +154,54 @@ function createIntegration(args) {
|
|
|
73
154
|
_config = config;
|
|
74
155
|
_buildConfig = config.build;
|
|
75
156
|
if (config.output === "static") {
|
|
76
|
-
throw new
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
`);
|
|
157
|
+
throw new AstroError(
|
|
158
|
+
'[@astrojs/cloudflare] `output: "server"` or `output: "hybrid"` is required to use this adapter. Otherwise, this adapter is not necessary to deploy a static site to Cloudflare.'
|
|
159
|
+
);
|
|
80
160
|
}
|
|
81
161
|
if (config.base === SERVER_BUILD_FOLDER) {
|
|
82
|
-
throw new
|
|
83
|
-
|
|
162
|
+
throw new AstroError(
|
|
163
|
+
'[@astrojs/cloudflare] `base: "${SERVER_BUILD_FOLDER}"` is not allowed. Please change your `base` config to something else.'
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"astro:server:setup": ({ server }) => {
|
|
168
|
+
if (runtimeMode !== "off") {
|
|
169
|
+
server.middlewares.use(async function middleware(req, res, next) {
|
|
170
|
+
try {
|
|
171
|
+
const cf = await getCFObject(runtimeMode);
|
|
172
|
+
const vars = await getEnvVars();
|
|
173
|
+
const clientLocalsSymbol = Symbol.for("astro.locals");
|
|
174
|
+
Reflect.set(req, clientLocalsSymbol, {
|
|
175
|
+
runtime: {
|
|
176
|
+
env: {
|
|
177
|
+
// default binding for static assets will be dynamic once we support mocking of bindings
|
|
178
|
+
ASSETS: {},
|
|
179
|
+
// this is just a VAR for CF to change build behavior, on dev it should be 0
|
|
180
|
+
CF_PAGES: "0",
|
|
181
|
+
// will be fetched from git dynamically once we support mocking of bindings
|
|
182
|
+
CF_PAGES_BRANCH: "TBA",
|
|
183
|
+
// will be fetched from git dynamically once we support mocking of bindings
|
|
184
|
+
CF_PAGES_COMMIT_SHA: "TBA",
|
|
185
|
+
CF_PAGES_URL: `http://${req.headers.host}`,
|
|
186
|
+
...vars
|
|
187
|
+
},
|
|
188
|
+
cf,
|
|
189
|
+
waitUntil: (_promise) => {
|
|
190
|
+
return;
|
|
191
|
+
},
|
|
192
|
+
caches: new CacheStorage(
|
|
193
|
+
{ cache: true, cachePersist: false },
|
|
194
|
+
new NoOpLog(),
|
|
195
|
+
new StorageFactory(),
|
|
196
|
+
{}
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
next();
|
|
201
|
+
} catch {
|
|
202
|
+
next();
|
|
203
|
+
}
|
|
204
|
+
});
|
|
84
205
|
}
|
|
85
206
|
},
|
|
86
207
|
"astro:build:setup": ({ vite, target }) => {
|
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is a derivative work of wrangler by Cloudflare
|
|
3
|
+
* An upstream request for exposing this API was made here:
|
|
4
|
+
* https://github.com/cloudflare/workers-sdk/issues/3897
|
|
5
|
+
*
|
|
6
|
+
* Until further notice, we will be using this file as a workaround
|
|
7
|
+
* TODO: Tackle this file, once their is an decision on the upstream request
|
|
8
|
+
*/
|
|
9
|
+
import dotenv from 'dotenv';
|
|
10
|
+
export interface DotEnv {
|
|
11
|
+
path: string;
|
|
12
|
+
parsed: dotenv.DotenvParseOutput;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Loads a dotenv file from <path>, preferring to read <path>.<environment> if
|
|
16
|
+
* <environment> is defined and that file exists.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadDotEnv(path: string): DotEnv | undefined;
|
|
19
|
+
export declare function getEnvVars(): Promise<any>;
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import TOML from "@iarna/toml";
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import { findUpSync } from "find-up";
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import { dirname, resolve } from "node:path";
|
|
6
|
+
function findWranglerToml(referencePath = process.cwd(), preferJson = false) {
|
|
7
|
+
if (preferJson) {
|
|
8
|
+
return findUpSync(`wrangler.json`, { cwd: referencePath }) ?? findUpSync(`wrangler.toml`, { cwd: referencePath });
|
|
9
|
+
}
|
|
10
|
+
return findUpSync(`wrangler.toml`, { cwd: referencePath });
|
|
11
|
+
}
|
|
12
|
+
class ParseError extends Error {
|
|
13
|
+
text;
|
|
14
|
+
notes;
|
|
15
|
+
location;
|
|
16
|
+
kind;
|
|
17
|
+
constructor({ text, notes, location, kind }) {
|
|
18
|
+
super(text);
|
|
19
|
+
this.name = this.constructor.name;
|
|
20
|
+
this.text = text;
|
|
21
|
+
this.notes = notes ?? [];
|
|
22
|
+
this.location = location;
|
|
23
|
+
this.kind = kind ?? "error";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const TOML_ERROR_NAME = "TomlError";
|
|
27
|
+
const TOML_ERROR_SUFFIX = " at row ";
|
|
28
|
+
function parseTOML(input, file) {
|
|
29
|
+
try {
|
|
30
|
+
const normalizedInput = input.replace(/\r\n/g, "\n");
|
|
31
|
+
return TOML.parse(normalizedInput);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
const { name, message, line, col } = err;
|
|
34
|
+
if (name !== TOML_ERROR_NAME) {
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
const text = message.substring(0, message.lastIndexOf(TOML_ERROR_SUFFIX));
|
|
38
|
+
const lineText = input.split("\n")[line];
|
|
39
|
+
const location = {
|
|
40
|
+
lineText,
|
|
41
|
+
line: line + 1,
|
|
42
|
+
column: col - 1,
|
|
43
|
+
file,
|
|
44
|
+
fileText: input
|
|
45
|
+
};
|
|
46
|
+
throw new ParseError({ text, location });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function tryLoadDotEnv(path) {
|
|
50
|
+
try {
|
|
51
|
+
const parsed = dotenv.parse(fs.readFileSync(path));
|
|
52
|
+
return { path, parsed };
|
|
53
|
+
} catch (e) {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function loadDotEnv(path) {
|
|
57
|
+
return tryLoadDotEnv(path);
|
|
58
|
+
}
|
|
59
|
+
function getVarsForDev(config, configPath) {
|
|
60
|
+
const configDir = resolve(dirname(configPath ?? "."));
|
|
61
|
+
const devVarsPath = resolve(configDir, ".dev.vars");
|
|
62
|
+
const loaded = loadDotEnv(devVarsPath);
|
|
63
|
+
if (loaded !== void 0) {
|
|
64
|
+
return {
|
|
65
|
+
...config.vars,
|
|
66
|
+
...loaded.parsed
|
|
67
|
+
};
|
|
68
|
+
} else {
|
|
69
|
+
return config.vars;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function getEnvVars() {
|
|
73
|
+
let rawConfig;
|
|
74
|
+
const configPath = findWranglerToml(process.cwd(), false);
|
|
75
|
+
if (!configPath) {
|
|
76
|
+
throw new Error("Could not find wrangler.toml");
|
|
77
|
+
}
|
|
78
|
+
if (configPath?.endsWith("toml")) {
|
|
79
|
+
rawConfig = parseTOML(fs.readFileSync(configPath).toString(), configPath);
|
|
80
|
+
}
|
|
81
|
+
const vars = getVarsForDev(rawConfig, configPath);
|
|
82
|
+
return vars;
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
getEnvVars,
|
|
86
|
+
loadDotEnv
|
|
87
|
+
};
|
package/dist/server.advanced.js
CHANGED
|
@@ -18,16 +18,6 @@ function createExports(manifest) {
|
|
|
18
18
|
Symbol.for("astro.clientAddress"),
|
|
19
19
|
request.headers.get("cf-connecting-ip")
|
|
20
20
|
);
|
|
21
|
-
Reflect.set(request, Symbol.for("runtime"), {
|
|
22
|
-
env,
|
|
23
|
-
name: "cloudflare",
|
|
24
|
-
caches,
|
|
25
|
-
cf: request.cf,
|
|
26
|
-
...context,
|
|
27
|
-
waitUntil: (promise) => {
|
|
28
|
-
context.waitUntil(promise);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
21
|
const locals = {
|
|
32
22
|
runtime: {
|
|
33
23
|
waitUntil: (promise) => {
|
package/dist/server.directory.js
CHANGED
|
@@ -7,7 +7,7 @@ function createExports(manifest) {
|
|
|
7
7
|
const app = new App(manifest);
|
|
8
8
|
const onRequest = async (context) => {
|
|
9
9
|
const request = context.request;
|
|
10
|
-
const {
|
|
10
|
+
const { env } = context;
|
|
11
11
|
process.env = env;
|
|
12
12
|
const { pathname } = new URL(request.url);
|
|
13
13
|
if (manifest.assets.has(pathname)) {
|
|
@@ -20,16 +20,6 @@ function createExports(manifest) {
|
|
|
20
20
|
Symbol.for("astro.clientAddress"),
|
|
21
21
|
request.headers.get("cf-connecting-ip")
|
|
22
22
|
);
|
|
23
|
-
Reflect.set(request, Symbol.for("runtime"), {
|
|
24
|
-
...context,
|
|
25
|
-
waitUntil: (promise) => {
|
|
26
|
-
context.waitUntil(promise);
|
|
27
|
-
},
|
|
28
|
-
name: "cloudflare",
|
|
29
|
-
next,
|
|
30
|
-
caches,
|
|
31
|
-
cf: request.cf
|
|
32
|
-
});
|
|
33
23
|
const locals = {
|
|
34
24
|
runtime: {
|
|
35
25
|
waitUntil: (promise) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/cloudflare",
|
|
3
3
|
"description": "Deploy your site to Cloudflare Workers/Pages",
|
|
4
|
-
"version": "7.0
|
|
4
|
+
"version": "7.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"author": "withastro",
|
|
@@ -35,10 +35,16 @@
|
|
|
35
35
|
"@cloudflare/workers-types": "^4.20230821.0",
|
|
36
36
|
"esbuild": "^0.19.2",
|
|
37
37
|
"tiny-glob": "^0.2.9",
|
|
38
|
+
"find-up": "^6.3.0",
|
|
39
|
+
"@iarna/toml": "^2.2.5",
|
|
40
|
+
"dotenv": "^16.3.1",
|
|
41
|
+
"@miniflare/cache": "^2.14.1",
|
|
42
|
+
"@miniflare/shared": "^2.14.1",
|
|
43
|
+
"@miniflare/storage-memory": "^2.14.1",
|
|
38
44
|
"@astrojs/underscore-redirects": "0.3.0"
|
|
39
45
|
},
|
|
40
46
|
"peerDependencies": {
|
|
41
|
-
"astro": "^3.0.
|
|
47
|
+
"astro": "^3.0.13"
|
|
42
48
|
},
|
|
43
49
|
"devDependencies": {
|
|
44
50
|
"chai": "^4.3.7",
|
|
@@ -46,7 +52,7 @@
|
|
|
46
52
|
"kill-port": "^2.0.1",
|
|
47
53
|
"mocha": "^10.2.0",
|
|
48
54
|
"wrangler": "^3.5.1",
|
|
49
|
-
"astro": "3.0.
|
|
55
|
+
"astro": "3.0.13",
|
|
50
56
|
"astro-scripts": "0.0.14"
|
|
51
57
|
},
|
|
52
58
|
"scripts": {
|
package/runtime.d.ts
DELETED