@astrojs/cloudflare 7.7.1 → 8.0.1

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.
@@ -0,0 +1,222 @@
1
+ import { mkdirSync, readFileSync, statSync, writeFileSync } from 'fs';
2
+ import assert from 'node:assert';
3
+ import { fileURLToPath } from 'url';
4
+ import TOML from '@iarna/toml';
5
+ import { AstroError } from 'astro/errors';
6
+ import dotenv from 'dotenv';
7
+ import { Miniflare } from 'miniflare';
8
+ class LocalRuntime {
9
+ _astroConfig;
10
+ _logger;
11
+ _miniflare;
12
+ miniflareBindings;
13
+ secrets;
14
+ cfObject;
15
+ constructor(astroConfig, runtimeConfig, logger) {
16
+ this._astroConfig = astroConfig;
17
+ this._logger = logger;
18
+ const varBindings = {};
19
+ const kvBindings = [];
20
+ const d1Bindings = [];
21
+ const r2Bindings = [];
22
+ const durableObjectBindings = {};
23
+ for (const bindingName in runtimeConfig.bindings) {
24
+ const bindingData = runtimeConfig.bindings[bindingName];
25
+ switch (bindingData.type) {
26
+ case 'var':
27
+ varBindings[bindingName] = bindingData.value;
28
+ break;
29
+ case 'kv':
30
+ kvBindings.push(bindingName);
31
+ break;
32
+ case 'd1':
33
+ d1Bindings.push(bindingName);
34
+ break;
35
+ case 'r2':
36
+ r2Bindings.push(bindingName);
37
+ break;
38
+ case 'durable-object':
39
+ durableObjectBindings[bindingName] = {
40
+ className: bindingData.className,
41
+ scriptName: bindingData.service?.name,
42
+ };
43
+ break;
44
+ }
45
+ }
46
+ this._miniflare = new Miniflare({
47
+ cachePersist: `${runtimeConfig.persistTo}/cache`,
48
+ d1Persist: `${runtimeConfig.persistTo}/d1`,
49
+ r2Persist: `${runtimeConfig.persistTo}/r2`,
50
+ kvPersist: `${runtimeConfig.persistTo}/kv`,
51
+ durableObjectsPersist: `${runtimeConfig.persistTo}/do`,
52
+ workers: [
53
+ {
54
+ name: 'worker',
55
+ script: '',
56
+ modules: true,
57
+ cacheWarnUsage: true,
58
+ cache: true,
59
+ bindings: varBindings,
60
+ d1Databases: d1Bindings,
61
+ r2Buckets: r2Bindings,
62
+ kvNamespaces: kvBindings,
63
+ durableObjects: durableObjectBindings,
64
+ },
65
+ ],
66
+ });
67
+ }
68
+ async getBindings() {
69
+ await this._miniflare.ready;
70
+ if (!this.miniflareBindings) {
71
+ this.miniflareBindings = await this._miniflare.getBindings();
72
+ }
73
+ return this.miniflareBindings;
74
+ }
75
+ async getSecrets() {
76
+ await this._miniflare.ready;
77
+ if (!this.secrets) {
78
+ try {
79
+ this.secrets = dotenv.parse(readFileSync(fileURLToPath(new URL('./.dev.vars', this._astroConfig.root))));
80
+ }
81
+ catch (error) {
82
+ const e = error;
83
+ if (e.code === 'ENOENT') {
84
+ this._logger.info('There is no `.dev.vars` file in the root directory, if you have encrypted secrets or environmental variables you Cloudflare recommends to put them in this file');
85
+ this.secrets = {};
86
+ }
87
+ else {
88
+ throw new AstroError('Failed to load secrets file', e.message);
89
+ }
90
+ }
91
+ }
92
+ return this.secrets;
93
+ }
94
+ async getCaches() {
95
+ await this._miniflare.ready;
96
+ return this._miniflare.getCaches();
97
+ }
98
+ async getCF() {
99
+ await this._miniflare.ready;
100
+ const MAX_CACHE_AGE = 1000 * 60 * 60 * 24 * 7; // 7 days;
101
+ // Try load cached cfObject, if this fails, we'll catch the error and refetch.
102
+ // If this succeeds, and the file is stale, that's fine: it's very likely
103
+ // we'll be fetching the same data anyways.
104
+ try {
105
+ const cachedCFObject = JSON.parse(readFileSync(fileURLToPath(new URL('cf.json', this._astroConfig.cacheDir)), 'utf8'));
106
+ const cfObjectStats = statSync(fileURLToPath(new URL('cf.json', this._astroConfig.cacheDir)));
107
+ assert(Date.now() - cfObjectStats.mtimeMs <= MAX_CACHE_AGE);
108
+ this.cfObject = cachedCFObject;
109
+ }
110
+ catch { }
111
+ const CF_ENDPOINT = 'https://workers.cloudflare.com/cf.json';
112
+ if (!this.cfObject) {
113
+ this.cfObject = await fetch(CF_ENDPOINT).then((res) => res.json());
114
+ mkdirSync(this._astroConfig.cacheDir);
115
+ writeFileSync(fileURLToPath(new URL('cf.json', this._astroConfig.cacheDir)), JSON.stringify(this.cfObject), 'utf8');
116
+ }
117
+ return this.cfObject;
118
+ }
119
+ async dispose() {
120
+ await this._miniflare.dispose();
121
+ }
122
+ }
123
+ export class LocalWorkersRuntime extends LocalRuntime {
124
+ constructor(astroConfig, runtimeConfig, logger) {
125
+ let _wranglerConfig;
126
+ try {
127
+ _wranglerConfig = TOML.parse(readFileSync(fileURLToPath(new URL('./wrangler.toml', astroConfig.root)), 'utf-8').replace(/\r\n/g, '\n'));
128
+ }
129
+ catch (error) {
130
+ const e = error;
131
+ if (e.code === 'ENOENT') {
132
+ logger.error('Missing file `wrangler.toml in root directory`');
133
+ }
134
+ else {
135
+ throw new AstroError('Failed to load wrangler config', e.message);
136
+ }
137
+ }
138
+ const runtimeConfigWithWrangler = {
139
+ ...runtimeConfig,
140
+ bindings: {},
141
+ };
142
+ if (_wranglerConfig?.vars) {
143
+ for (const key in _wranglerConfig.vars) {
144
+ runtimeConfigWithWrangler.bindings[key] = {
145
+ type: 'var',
146
+ value: _wranglerConfig.vars[key],
147
+ };
148
+ }
149
+ }
150
+ if (_wranglerConfig?.kv_namespaces) {
151
+ for (const ns of _wranglerConfig.kv_namespaces) {
152
+ runtimeConfigWithWrangler.bindings[ns.binding] = {
153
+ type: 'kv',
154
+ };
155
+ }
156
+ }
157
+ if (_wranglerConfig?.d1_databases) {
158
+ for (const db of _wranglerConfig.d1_databases) {
159
+ runtimeConfigWithWrangler.bindings[db.binding] = {
160
+ type: 'd1',
161
+ };
162
+ }
163
+ }
164
+ if (_wranglerConfig?.r2_buckets) {
165
+ for (const bucket of _wranglerConfig.r2_buckets) {
166
+ runtimeConfigWithWrangler.bindings[bucket.binding] = {
167
+ type: 'r2',
168
+ };
169
+ }
170
+ }
171
+ if (_wranglerConfig?.durable_objects) {
172
+ for (const durableObject of _wranglerConfig.durable_objects.bindings) {
173
+ runtimeConfigWithWrangler.bindings[durableObject.name] = {
174
+ type: 'durable-object',
175
+ className: durableObject.class_name,
176
+ service: durableObject.script_name
177
+ ? {
178
+ name: durableObject.script_name,
179
+ }
180
+ : undefined,
181
+ };
182
+ }
183
+ }
184
+ super(astroConfig, runtimeConfigWithWrangler, logger);
185
+ }
186
+ }
187
+ export class LocalPagesRuntime extends LocalRuntime {
188
+ // biome-ignore lint/complexity/noUselessConstructor: not types information yet, so we need to disable the rule for the time being
189
+ constructor(astroConfig, runtimeConfig, logger) {
190
+ super(astroConfig, runtimeConfig, logger);
191
+ }
192
+ }
193
+ let localRuntime;
194
+ export function getLocalRuntime(astroConfig, runtimeConfig, logger) {
195
+ if (localRuntime)
196
+ return localRuntime;
197
+ if (runtimeConfig.type === 'pages') {
198
+ localRuntime = new LocalPagesRuntime(astroConfig, runtimeConfig, logger);
199
+ }
200
+ else {
201
+ localRuntime = new LocalWorkersRuntime(astroConfig, runtimeConfig, logger);
202
+ }
203
+ return localRuntime;
204
+ }
205
+ export function getRuntimeConfig(userConfig) {
206
+ if (!userConfig || userConfig.mode === 'off')
207
+ return { mode: 'off' };
208
+ // we know that we have `mode: local` below
209
+ if (userConfig.type === 'pages')
210
+ return {
211
+ mode: 'local',
212
+ type: 'pages',
213
+ persistTo: userConfig.persistTo ?? '.wrangler/state/v3',
214
+ bindings: userConfig.bindings ?? {},
215
+ };
216
+ // we know that we have `type: workers` below
217
+ return {
218
+ mode: 'local',
219
+ type: 'workers',
220
+ persistTo: userConfig.persistTo ?? '.wrangler/state/v3',
221
+ };
222
+ }
@@ -6,18 +6,18 @@
6
6
  * Until further notice, we will be using this file as a workaround
7
7
  * TODO: Tackle this file, once their is an decision on the upstream request
8
8
  */
9
+ import * as fs from 'node:fs';
10
+ import { dirname, resolve } from 'node:path';
9
11
  import TOML from '@iarna/toml';
10
12
  import dotenv from 'dotenv';
11
13
  import { findUpSync } from 'find-up';
12
- import * as fs from 'node:fs';
13
- import { dirname, resolve } from 'node:path';
14
14
  let _wrangler;
15
15
  function findWranglerToml(referencePath = process.cwd(), preferJson = false) {
16
16
  if (preferJson) {
17
- return (findUpSync(`wrangler.json`, { cwd: referencePath }) ??
18
- findUpSync(`wrangler.toml`, { cwd: referencePath }));
17
+ return (findUpSync('wrangler.json', { cwd: referencePath }) ??
18
+ findUpSync('wrangler.toml', { cwd: referencePath }));
19
19
  }
20
- return findUpSync(`wrangler.toml`, { cwd: referencePath });
20
+ return findUpSync('wrangler.toml', { cwd: referencePath });
21
21
  }
22
22
  class ParseError extends Error {
23
23
  text;
@@ -84,9 +84,7 @@ function getVarsForDev(config, configPath) {
84
84
  ...loaded.parsed,
85
85
  };
86
86
  }
87
- else {
88
- return config.vars;
89
- }
87
+ return config.vars;
90
88
  }
91
89
  function parseConfig() {
92
90
  if (_wrangler)
@@ -139,10 +137,11 @@ export function getDOBindings() {
139
137
  const { rawConfig } = parseConfig();
140
138
  if (!rawConfig)
141
139
  return {};
142
- if (!rawConfig?.durable_objects)
140
+ if (!rawConfig.durable_objects)
143
141
  return {};
144
142
  const output = new Object({});
145
- for (const binding of rawConfig?.durable_objects.bindings) {
143
+ const bindings = rawConfig.durable_objects.bindings;
144
+ for (const binding of bindings) {
146
145
  Reflect.set(output, binding.name, { className: binding.class_name });
147
146
  }
148
147
  return output;
@@ -1,3 +1,3 @@
1
1
  export function prependForwardSlash(path) {
2
- return path[0] === '/' ? path : '/' + path;
2
+ return path[0] === '/' ? path : `/${path}`;
3
3
  }
@@ -1,5 +1,5 @@
1
- import esbuild from 'esbuild';
2
1
  import { basename } from 'node:path';
2
+ import esbuild from 'esbuild';
3
3
  /**
4
4
  *
5
5
  * @param relativePathToAssets - relative path from the final location for the current esbuild output bundle, to the assets directory.
@@ -1,4 +1,4 @@
1
- import { type Plugin } from 'vite';
1
+ import type { AstroConfig } from 'astro';
2
2
  /**
3
3
  * Loads '*.wasm?module' imports as WebAssembly modules, which is the only way to load WASM in cloudflare workers.
4
4
  * Current proposal for WASM modules: https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration
@@ -11,4 +11,4 @@ import { type Plugin } from 'vite';
11
11
  export declare function wasmModuleLoader({ disabled, assetsDirectory, }: {
12
12
  disabled: boolean;
13
13
  assetsDirectory: string;
14
- }): Plugin;
14
+ }): NonNullable<AstroConfig['vite']['plugins']>[number];
@@ -1,6 +1,5 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
- import {} from 'vite';
4
3
  /**
5
4
  * Loads '*.wasm?module' imports as WebAssembly modules, which is the only way to load WASM in cloudflare workers.
6
5
  * Current proposal for WASM modules: https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration
@@ -44,31 +43,29 @@ export default wasmModule
44
43
  // no need to wire up the assets in dev mode, just rewrite
45
44
  return base64Module;
46
45
  }
47
- else {
48
- // just some shared ID
49
- let hash = hashString(base64);
50
- // emit the wasm binary as an asset file, to be picked up later by the esbuild bundle for the worker.
51
- // give it a shared deterministic name to make things easy for esbuild to switch on later
52
- const assetName = path.basename(filePath).split('.')[0] + '.' + hash + '.wasm';
53
- this.emitFile({
54
- type: 'asset',
55
- // put it explicitly in the _astro assets directory with `fileName` rather than `name` so that
56
- // vite doesn't give it a random id in its name. We need to be able to easily rewrite from
57
- // the .mjs loader and the actual wasm asset later in the ESbuild for the worker
58
- fileName: path.join(assetsDirectory, assetName),
59
- source: fs.readFileSync(filePath),
60
- });
61
- // however, by default, the SSG generator cannot import the .wasm as a module, so embed as a base64 string
62
- const chunkId = this.emitFile({
63
- type: 'prebuilt-chunk',
64
- fileName: assetName + '.mjs',
65
- code: base64Module,
66
- });
67
- return `
46
+ // just some shared ID
47
+ const hash = hashString(base64);
48
+ // emit the wasm binary as an asset file, to be picked up later by the esbuild bundle for the worker.
49
+ // give it a shared deterministic name to make things easy for esbuild to switch on later
50
+ const assetName = `${path.basename(filePath).split('.')[0]}.${hash}.wasm`;
51
+ this.emitFile({
52
+ type: 'asset',
53
+ // put it explicitly in the _astro assets directory with `fileName` rather than `name` so that
54
+ // vite doesn't give it a random id in its name. We need to be able to easily rewrite from
55
+ // the .mjs loader and the actual wasm asset later in the ESbuild for the worker
56
+ fileName: path.join(assetsDirectory, assetName),
57
+ source: fs.readFileSync(filePath),
58
+ });
59
+ // however, by default, the SSG generator cannot import the .wasm as a module, so embed as a base64 string
60
+ const chunkId = this.emitFile({
61
+ type: 'prebuilt-chunk',
62
+ fileName: `${assetName}.mjs`,
63
+ code: base64Module,
64
+ });
65
+ return `
68
66
  import wasmModule from "__WASM_ASSET__${chunkId}.wasm.mjs";
69
67
  export default wasmModule;
70
68
  `;
71
- }
72
69
  },
73
70
  // output original wasm file relative to the chunk
74
71
  renderChunk(code, chunk, _) {
@@ -76,7 +73,7 @@ export default wasmModule;
76
73
  return;
77
74
  if (!/__WASM_ASSET__/g.test(code))
78
75
  return;
79
- const final = code.replaceAll(/__WASM_ASSET__([a-z\d]+).wasm.mjs/g, (s, assetId) => {
76
+ const final = code.replaceAll(/__WASM_ASSET__([A-Za-z\d]+).wasm.mjs/g, (s, assetId) => {
80
77
  const fileName = this.getFileName(assetId);
81
78
  const relativePath = path
82
79
  .relative(path.dirname(chunk.fileName), fileName)
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "//comment": "test changeset-bot",
3
3
  "name": "@astrojs/cloudflare",
4
4
  "description": "Deploy your site to Cloudflare Workers/Pages",
5
- "version": "7.7.1",
5
+ "version": "8.0.1",
6
6
  "type": "module",
7
7
  "types": "./dist/index.d.ts",
8
8
  "author": "withastro",
@@ -36,18 +36,17 @@
36
36
  "dotenv": "^16.3.1",
37
37
  "esbuild": "^0.19.5",
38
38
  "find-up": "^6.3.0",
39
- "tiny-glob": "^0.2.9",
40
- "vite": "^4.5.0"
39
+ "tiny-glob": "^0.2.9"
41
40
  },
42
41
  "peerDependencies": {
43
- "astro": "^3.4.3"
42
+ "astro": "^3.0.0 || ^4.0.0"
44
43
  },
45
44
  "devDependencies": {
46
45
  "execa": "^8.0.1",
47
46
  "fast-glob": "^3.3.1",
48
47
  "@types/iarna__toml": "^2.0.2",
49
48
  "strip-ansi": "^7.1.0",
50
- "astro": "^3.4.3",
49
+ "astro": "^4.0.0",
51
50
  "chai": "^4.3.10",
52
51
  "cheerio": "1.0.0-rc.12",
53
52
  "mocha": "^10.2.0",
@@ -1,2 +0,0 @@
1
- import type { IncomingRequestCfProperties } from '@cloudflare/workers-types/experimental';
2
- export declare function getCFObject(runtimeMode: string): Promise<IncomingRequestCfProperties | void>;
@@ -1,67 +0,0 @@
1
- export async function getCFObject(runtimeMode) {
2
- const CF_ENDPOINT = 'https://workers.cloudflare.com/cf.json';
3
- const CF_FALLBACK = {
4
- asOrganization: '',
5
- asn: 395747,
6
- colo: 'DFW',
7
- city: 'Austin',
8
- region: 'Texas',
9
- regionCode: 'TX',
10
- metroCode: '635',
11
- postalCode: '78701',
12
- country: 'US',
13
- continent: 'NA',
14
- timezone: 'America/Chicago',
15
- latitude: '30.27130',
16
- longitude: '-97.74260',
17
- clientTcpRtt: 0,
18
- httpProtocol: 'HTTP/1.1',
19
- requestPriority: 'weight=192;exclusive=0',
20
- tlsCipher: 'AEAD-AES128-GCM-SHA256',
21
- tlsVersion: 'TLSv1.3',
22
- tlsClientAuth: {
23
- certPresented: '0',
24
- certVerified: 'NONE',
25
- certRevoked: '0',
26
- certIssuerDN: '',
27
- certSubjectDN: '',
28
- certIssuerDNRFC2253: '',
29
- certSubjectDNRFC2253: '',
30
- certIssuerDNLegacy: '',
31
- certSubjectDNLegacy: '',
32
- certSerial: '',
33
- certIssuerSerial: '',
34
- certSKI: '',
35
- certIssuerSKI: '',
36
- certFingerprintSHA1: '',
37
- certFingerprintSHA256: '',
38
- certNotBefore: '',
39
- certNotAfter: '',
40
- },
41
- edgeRequestKeepAliveStatus: 0,
42
- hostMetadata: undefined,
43
- clientTrustScore: 99,
44
- botManagement: {
45
- corporateProxy: false,
46
- verifiedBot: false,
47
- ja3Hash: '25b4882c2bcb50cd6b469ff28c596742',
48
- staticResource: false,
49
- detectionIds: [],
50
- score: 99,
51
- },
52
- };
53
- if (runtimeMode === 'local') {
54
- return CF_FALLBACK;
55
- }
56
- else if (runtimeMode === 'remote') {
57
- try {
58
- const res = await fetch(CF_ENDPOINT);
59
- const cfText = await res.text();
60
- const storedCf = JSON.parse(cfText);
61
- return storedCf;
62
- }
63
- catch (e) {
64
- return CF_FALLBACK;
65
- }
66
- }
67
- }