wrangler 2.0.29 → 2.1.2
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/miniflare-dist/index.mjs +1136 -372
- package/package.json +3 -2
- package/src/__tests__/helpers/mock-cfetch.ts +39 -19
- package/src/__tests__/helpers/mock-console.ts +11 -2
- package/src/__tests__/helpers/msw/handlers/index.ts +13 -0
- package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
- package/src/__tests__/helpers/msw/handlers/oauth.ts +36 -0
- package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
- package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
- package/src/__tests__/helpers/msw/index.ts +4 -0
- package/src/__tests__/index.test.ts +9 -7
- package/src/__tests__/init.test.ts +356 -5
- package/src/__tests__/jest.setup.ts +16 -0
- package/src/__tests__/middleware.test.ts +768 -0
- package/src/__tests__/pages.test.ts +11 -12
- package/src/__tests__/publish.test.ts +516 -438
- package/src/__tests__/r2.test.ts +128 -93
- package/src/__tests__/secret.test.ts +78 -0
- package/src/__tests__/tail.test.ts +47 -74
- package/src/__tests__/whoami.test.tsx +49 -64
- package/src/api/dev.ts +23 -4
- package/src/bundle.ts +225 -1
- package/src/dev/dev.tsx +3 -1
- package/src/dev/local.tsx +2 -2
- package/src/dev/remote.tsx +6 -3
- package/src/dev/start-server.ts +11 -7
- package/src/dev/use-esbuild.ts +4 -0
- package/src/dev.tsx +6 -16
- package/src/dialogs.tsx +12 -0
- package/src/index.tsx +95 -4
- package/src/init.ts +286 -11
- package/src/miniflare-cli/assets.ts +130 -415
- package/src/miniflare-cli/index.ts +3 -1
- package/src/pages/dev.tsx +5 -1
- package/src/pages/hash.tsx +13 -0
- package/src/pages/upload.tsx +3 -18
- package/src/publish.ts +38 -4
- package/src/tail/filters.ts +1 -5
- package/src/tail/index.ts +6 -3
- package/templates/middleware/common.ts +62 -0
- package/templates/middleware/loader-modules.ts +84 -0
- package/templates/middleware/loader-sw.ts +213 -0
- package/templates/middleware/middleware-pretty-error.ts +40 -0
- package/templates/middleware/middleware-scheduled.ts +14 -0
- package/wrangler-dist/cli.js +65900 -65432
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { existsSync, lstatSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "@cloudflare/pages-shared/src/asset-server/rulesEngine";
|
|
3
|
+
import { createMetadataObject } from "@cloudflare/pages-shared/src/metadata-generator/createMetadataObject";
|
|
4
|
+
import { parseHeaders } from "@cloudflare/pages-shared/src/metadata-generator/parseHeaders";
|
|
5
|
+
import { parseRedirects } from "@cloudflare/pages-shared/src/metadata-generator/parseRedirects";
|
|
7
6
|
import { fetch as miniflareFetch } from "@miniflare/core";
|
|
7
|
+
import {
|
|
8
|
+
Response as MiniflareResponse,
|
|
9
|
+
Request as MiniflareRequest,
|
|
10
|
+
} from "@miniflare/core";
|
|
8
11
|
import { watch } from "chokidar";
|
|
9
12
|
import { getType } from "mime";
|
|
10
|
-
import {
|
|
11
|
-
import type {
|
|
12
|
-
import type { Log } from "miniflare";
|
|
13
|
+
import { hashFile } from "../pages/hash";
|
|
14
|
+
import type { Metadata } from "@cloudflare/pages-shared/src/asset-server/metadata";
|
|
13
15
|
import type {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
fetch,
|
|
17
|
+
Request,
|
|
18
|
+
} from "@cloudflare/pages-shared/src/environment-polyfills/types";
|
|
19
|
+
import type {
|
|
20
|
+
ParsedRedirects,
|
|
21
|
+
ParsedHeaders,
|
|
22
|
+
} from "@cloudflare/pages-shared/src/metadata-generator/types";
|
|
23
|
+
import type { RequestInfo, RequestInit, FetcherFetch } from "@miniflare/core";
|
|
24
|
+
import type { Log } from "miniflare";
|
|
18
25
|
|
|
19
26
|
export interface Options {
|
|
20
27
|
log: Log;
|
|
@@ -28,204 +35,49 @@ export default async function generateASSETSBinding(options: Options) {
|
|
|
28
35
|
? await generateAssetsFetch(options.directory, options.log)
|
|
29
36
|
: invalidAssetsFetch;
|
|
30
37
|
|
|
31
|
-
return async function (
|
|
38
|
+
return async function (miniflareRequest: MiniflareRequest) {
|
|
32
39
|
if (options.proxyPort) {
|
|
33
40
|
try {
|
|
34
|
-
const url = new URL(
|
|
41
|
+
const url = new URL(miniflareRequest.url);
|
|
35
42
|
url.host = `localhost:${options.proxyPort}`;
|
|
36
|
-
return await miniflareFetch(url,
|
|
43
|
+
return await miniflareFetch(url, miniflareRequest);
|
|
37
44
|
} catch (thrown) {
|
|
38
45
|
options.log.error(new Error(`Could not proxy request: ${thrown}`));
|
|
39
46
|
|
|
40
47
|
// TODO: Pretty error page
|
|
41
|
-
return new
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
return new MiniflareResponse(
|
|
49
|
+
`[wrangler] Could not proxy request: ${thrown}`,
|
|
50
|
+
{
|
|
51
|
+
status: 502,
|
|
52
|
+
}
|
|
53
|
+
);
|
|
44
54
|
}
|
|
45
55
|
} else {
|
|
46
56
|
try {
|
|
47
|
-
return await assetsFetch(
|
|
57
|
+
return await assetsFetch(miniflareRequest);
|
|
48
58
|
} catch (thrown) {
|
|
49
59
|
options.log.error(new Error(`Could not serve static asset: ${thrown}`));
|
|
50
60
|
|
|
51
61
|
// TODO: Pretty error page
|
|
52
|
-
return new
|
|
62
|
+
return new MiniflareResponse(
|
|
53
63
|
`[wrangler] Could not serve static asset: ${thrown}`,
|
|
54
64
|
{ status: 502 }
|
|
55
65
|
);
|
|
56
66
|
}
|
|
57
67
|
}
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function generateHeadersMatcher(headersFile: string) {
|
|
62
|
-
if (existsSync(headersFile)) {
|
|
63
|
-
const contents = readFileSync(headersFile).toString();
|
|
64
|
-
|
|
65
|
-
// TODO: Log errors
|
|
66
|
-
const lines = contents
|
|
67
|
-
.split("\n")
|
|
68
|
-
.map((line) => line.trim())
|
|
69
|
-
.filter((line) => !line.startsWith("#") && line !== "");
|
|
70
|
-
|
|
71
|
-
const rules: Record<string, Record<string, string>> = {};
|
|
72
|
-
let rule: { path: string; headers: Record<string, string> } | undefined =
|
|
73
|
-
undefined;
|
|
74
|
-
|
|
75
|
-
for (const line of lines) {
|
|
76
|
-
if (/^([^\s]+:\/\/|^\/)/.test(line)) {
|
|
77
|
-
if (rule && Object.keys(rule.headers).length > 0) {
|
|
78
|
-
rules[rule.path] = rule.headers;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const path = validateURL(line);
|
|
82
|
-
if (path) {
|
|
83
|
-
rule = {
|
|
84
|
-
path,
|
|
85
|
-
headers: {},
|
|
86
|
-
};
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (!line.includes(":")) continue;
|
|
92
|
-
|
|
93
|
-
const [rawName, ...rawValue] = line.split(":");
|
|
94
|
-
const name = rawName.trim().toLowerCase();
|
|
95
|
-
const value = rawValue.join(":").trim();
|
|
96
|
-
|
|
97
|
-
if (name === "") continue;
|
|
98
|
-
if (!rule) continue;
|
|
99
|
-
|
|
100
|
-
const existingValues = rule.headers[name];
|
|
101
|
-
rule.headers[name] = existingValues
|
|
102
|
-
? `${existingValues}, ${value}`
|
|
103
|
-
: value;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (rule && Object.keys(rule.headers).length > 0) {
|
|
107
|
-
rules[rule.path] = rule.headers;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const rulesMatcher = generateRulesMatcher(rules, (match, replacements) =>
|
|
111
|
-
Object.fromEntries(
|
|
112
|
-
Object.entries(match).map(([name, value]) => [
|
|
113
|
-
name,
|
|
114
|
-
replacer(value, replacements),
|
|
115
|
-
])
|
|
116
|
-
)
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
return (request: MiniflareRequest) => {
|
|
120
|
-
const matches = rulesMatcher({
|
|
121
|
-
request,
|
|
122
|
-
});
|
|
123
|
-
if (matches) return matches;
|
|
124
|
-
};
|
|
125
|
-
} else {
|
|
126
|
-
return () => undefined;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function generateRedirectsMatcher(redirectsFile: string) {
|
|
131
|
-
if (existsSync(redirectsFile)) {
|
|
132
|
-
const contents = readFileSync(redirectsFile).toString();
|
|
133
|
-
|
|
134
|
-
// TODO: Log errors
|
|
135
|
-
const lines = contents
|
|
136
|
-
.split("\n")
|
|
137
|
-
.map((line) => line.trim())
|
|
138
|
-
.filter((line) => !line.startsWith("#") && line !== "");
|
|
139
|
-
|
|
140
|
-
const rules = Object.fromEntries(
|
|
141
|
-
lines
|
|
142
|
-
.map((line) => line.split(" "))
|
|
143
|
-
.filter((tokens) => tokens.length === 2 || tokens.length === 3)
|
|
144
|
-
.map((tokens) => {
|
|
145
|
-
const from = validateURL(tokens[0], true, false, false);
|
|
146
|
-
const to = validateURL(tokens[1], false, true, true);
|
|
147
|
-
let status: number | undefined = parseInt(tokens[2]) || 302;
|
|
148
|
-
status = [301, 302, 303, 307, 308].includes(status)
|
|
149
|
-
? status
|
|
150
|
-
: undefined;
|
|
151
|
-
|
|
152
|
-
return from && to && status ? [from, { to, status }] : undefined;
|
|
153
|
-
})
|
|
154
|
-
.filter((rule) => rule !== undefined) as [
|
|
155
|
-
string,
|
|
156
|
-
{ to: string; status?: number }
|
|
157
|
-
][]
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
const rulesMatcher = generateRulesMatcher(
|
|
161
|
-
rules,
|
|
162
|
-
({ status, to }, replacements) => ({
|
|
163
|
-
status,
|
|
164
|
-
to: replacer(to, replacements),
|
|
165
|
-
})
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
return (request: MiniflareRequest) => {
|
|
169
|
-
const match = rulesMatcher({
|
|
170
|
-
request,
|
|
171
|
-
})[0];
|
|
172
|
-
if (match) return match;
|
|
173
|
-
};
|
|
174
|
-
} else {
|
|
175
|
-
return () => undefined;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function extractPathname(
|
|
180
|
-
path = "/",
|
|
181
|
-
includeSearch: boolean,
|
|
182
|
-
includeHash: boolean
|
|
183
|
-
) {
|
|
184
|
-
if (!path.startsWith("/")) path = `/${path}`;
|
|
185
|
-
const url = new URL(`//${path}`, "relative://");
|
|
186
|
-
return `${url.pathname}${includeSearch ? url.search : ""}${
|
|
187
|
-
includeHash ? url.hash : ""
|
|
188
|
-
}`;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function validateURL(
|
|
192
|
-
token: string,
|
|
193
|
-
onlyRelative = false,
|
|
194
|
-
includeSearch = false,
|
|
195
|
-
includeHash = false
|
|
196
|
-
) {
|
|
197
|
-
const host = /^https:\/\/+(?<host>[^/]+)\/?(?<path>.*)/.exec(token);
|
|
198
|
-
if (host && host.groups && host.groups.host) {
|
|
199
|
-
if (onlyRelative) return;
|
|
200
|
-
|
|
201
|
-
return `https://${host.groups.host}${extractPathname(
|
|
202
|
-
host.groups.path,
|
|
203
|
-
includeSearch,
|
|
204
|
-
includeHash
|
|
205
|
-
)}`;
|
|
206
|
-
} else {
|
|
207
|
-
if (!token.startsWith("/") && onlyRelative) token = `/${token}`;
|
|
208
|
-
|
|
209
|
-
const path = /^\//.exec(token);
|
|
210
|
-
if (path) {
|
|
211
|
-
try {
|
|
212
|
-
return extractPathname(token, includeSearch, includeHash);
|
|
213
|
-
} catch {}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return "";
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function hasFileExtension(pathname: string) {
|
|
220
|
-
return /\/.+\.[a-z0-9]+$/i.test(pathname);
|
|
68
|
+
} as FetcherFetch;
|
|
221
69
|
}
|
|
222
70
|
|
|
223
71
|
async function generateAssetsFetch(
|
|
224
72
|
directory: string,
|
|
225
73
|
log: Log
|
|
226
|
-
): Promise<typeof
|
|
74
|
+
): Promise<typeof fetch> {
|
|
227
75
|
// Defer importing miniflare until we really need it
|
|
228
|
-
|
|
76
|
+
await import("@cloudflare/pages-shared/src/environment-polyfills/miniflare");
|
|
77
|
+
|
|
78
|
+
const { generateHandler, parseQualityWeightedList } = await import(
|
|
79
|
+
"@cloudflare/pages-shared/src/asset-server/handler"
|
|
80
|
+
);
|
|
229
81
|
|
|
230
82
|
const headersFile = join(directory, "_headers");
|
|
231
83
|
const redirectsFile = join(directory, "_redirects");
|
|
@@ -233,260 +85,123 @@ async function generateAssetsFetch(
|
|
|
233
85
|
|
|
234
86
|
const ignoredFiles = [headersFile, redirectsFile, workerFile];
|
|
235
87
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
!ignoredFiles.includes(path)
|
|
242
|
-
);
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
const getAsset = (path: string) => {
|
|
246
|
-
if (assetExists(path)) {
|
|
247
|
-
return join(directory, path);
|
|
248
|
-
}
|
|
249
|
-
};
|
|
88
|
+
let redirects: ParsedRedirects | undefined;
|
|
89
|
+
if (existsSync(redirectsFile)) {
|
|
90
|
+
const contents = readFileSync(redirectsFile, "utf-8");
|
|
91
|
+
redirects = parseRedirects(contents);
|
|
92
|
+
}
|
|
250
93
|
|
|
251
|
-
let
|
|
252
|
-
|
|
94
|
+
let headers: ParsedHeaders | undefined;
|
|
95
|
+
if (existsSync(headersFile)) {
|
|
96
|
+
const contents = readFileSync(headersFile, "utf-8");
|
|
97
|
+
headers = parseHeaders(contents);
|
|
98
|
+
}
|
|
253
99
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
case headersFile: {
|
|
259
|
-
log.log("_headers modified. Re-evaluating...");
|
|
260
|
-
headersMatcher = generateHeadersMatcher(headersFile);
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
case redirectsFile: {
|
|
264
|
-
log.log("_redirects modified. Re-evaluating...");
|
|
265
|
-
redirectsMatcher = generateRedirectsMatcher(redirectsFile);
|
|
266
|
-
break;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
100
|
+
let metadata = createMetadataObject({
|
|
101
|
+
redirects,
|
|
102
|
+
headers,
|
|
103
|
+
logger: log.warn.bind(log),
|
|
269
104
|
});
|
|
270
105
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
body?: Buffer;
|
|
287
|
-
} = {
|
|
288
|
-
status: 200,
|
|
289
|
-
headers: new Headers(),
|
|
290
|
-
body: undefined,
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
const match = redirectsMatcher(request);
|
|
294
|
-
if (match) {
|
|
295
|
-
const { status, to } = match;
|
|
296
|
-
|
|
297
|
-
let location = to;
|
|
298
|
-
let search;
|
|
299
|
-
|
|
300
|
-
if (to.startsWith("/")) {
|
|
301
|
-
search = new URL(location, "http://fakehost").search;
|
|
302
|
-
} else {
|
|
303
|
-
search = new URL(location).search;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
location = `${location}${search ? "" : url.search}`;
|
|
307
|
-
|
|
308
|
-
if (status && [301, 302, 303, 307, 308].includes(status)) {
|
|
309
|
-
deconstructedResponse.status = status;
|
|
310
|
-
} else {
|
|
311
|
-
deconstructedResponse.status = 302;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
deconstructedResponse.headers.set("Location", location);
|
|
315
|
-
return deconstructedResponse;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (!request.method?.match(/^(get|head)$/i)) {
|
|
319
|
-
deconstructedResponse.status = 405;
|
|
320
|
-
return deconstructedResponse;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const notFound = () => {
|
|
324
|
-
let cwd = assetName;
|
|
325
|
-
while (cwd) {
|
|
326
|
-
cwd = cwd.slice(0, cwd.lastIndexOf("/"));
|
|
327
|
-
|
|
328
|
-
if ((asset = getAsset(`${cwd}/404.html`))) {
|
|
329
|
-
deconstructedResponse.status = 404;
|
|
330
|
-
deconstructedResponse.body = serveAsset(asset);
|
|
331
|
-
deconstructedResponse.headers.set(
|
|
332
|
-
"Content-Type",
|
|
333
|
-
getType(asset) || "application/octet-stream"
|
|
334
|
-
);
|
|
335
|
-
return deconstructedResponse;
|
|
106
|
+
watch([headersFile, redirectsFile], { persistent: true }).on(
|
|
107
|
+
"change",
|
|
108
|
+
(path) => {
|
|
109
|
+
switch (path) {
|
|
110
|
+
case headersFile: {
|
|
111
|
+
log.log("_headers modified. Re-evaluating...");
|
|
112
|
+
const contents = readFileSync(headersFile).toString();
|
|
113
|
+
headers = parseHeaders(contents);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case redirectsFile: {
|
|
117
|
+
log.log("_redirects modified. Re-evaluating...");
|
|
118
|
+
const contents = readFileSync(redirectsFile).toString();
|
|
119
|
+
redirects = parseRedirects(contents);
|
|
120
|
+
break;
|
|
336
121
|
}
|
|
337
122
|
}
|
|
338
123
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
);
|
|
345
|
-
return deconstructedResponse;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
deconstructedResponse.status = 404;
|
|
349
|
-
return deconstructedResponse;
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
let asset;
|
|
353
|
-
|
|
354
|
-
if (assetName.endsWith("/")) {
|
|
355
|
-
if ((asset = getAsset(`${assetName}/index.html`))) {
|
|
356
|
-
deconstructedResponse.body = serveAsset(asset);
|
|
357
|
-
deconstructedResponse.headers.set(
|
|
358
|
-
"Content-Type",
|
|
359
|
-
getType(asset) || "application/octet-stream"
|
|
360
|
-
);
|
|
361
|
-
return deconstructedResponse;
|
|
362
|
-
} else if ((asset = getAsset(`${assetName.replace(/\/$/, ".html")}`))) {
|
|
363
|
-
deconstructedResponse.status = 301;
|
|
364
|
-
deconstructedResponse.headers.set(
|
|
365
|
-
"Location",
|
|
366
|
-
`${assetName.slice(0, -1)}${url.search}`
|
|
367
|
-
);
|
|
368
|
-
return deconstructedResponse;
|
|
369
|
-
}
|
|
124
|
+
metadata = createMetadataObject({
|
|
125
|
+
redirects,
|
|
126
|
+
headers,
|
|
127
|
+
logger: log.warn,
|
|
128
|
+
});
|
|
370
129
|
}
|
|
130
|
+
);
|
|
371
131
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
132
|
+
const generateResponse = async (request: Request) => {
|
|
133
|
+
const assetKeyEntryMap = new Map<string, string>();
|
|
134
|
+
|
|
135
|
+
return await generateHandler<string>({
|
|
136
|
+
request,
|
|
137
|
+
metadata: metadata as Metadata,
|
|
138
|
+
xServerEnvHeader: "dev",
|
|
139
|
+
logError: console.error,
|
|
140
|
+
findAssetEntryForPath: async (path) => {
|
|
141
|
+
const filepath = join(directory, path);
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
existsSync(filepath) &&
|
|
145
|
+
lstatSync(filepath).isFile() &&
|
|
146
|
+
!ignoredFiles.includes(filepath)
|
|
147
|
+
) {
|
|
148
|
+
const hash = hashFile(filepath);
|
|
149
|
+
assetKeyEntryMap.set(hash, filepath);
|
|
150
|
+
return hash;
|
|
151
|
+
}
|
|
380
152
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
153
|
+
return null;
|
|
154
|
+
},
|
|
155
|
+
getAssetKey: (assetEntry) => {
|
|
156
|
+
return assetEntry;
|
|
157
|
+
},
|
|
158
|
+
negotiateContent: (contentRequest) => {
|
|
159
|
+
let rawAcceptEncoding: string | undefined;
|
|
160
|
+
if (
|
|
161
|
+
contentRequest.cf &&
|
|
162
|
+
"clientAcceptEncoding" in contentRequest.cf &&
|
|
163
|
+
contentRequest.cf.clientAcceptEncoding
|
|
164
|
+
) {
|
|
165
|
+
rawAcceptEncoding = contentRequest.cf.clientAcceptEncoding as string;
|
|
391
166
|
} else {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
"Location",
|
|
395
|
-
`${extensionlessPath}${url.search}`
|
|
396
|
-
);
|
|
397
|
-
return deconstructedResponse;
|
|
167
|
+
rawAcceptEncoding =
|
|
168
|
+
contentRequest.headers.get("Accept-Encoding") || undefined;
|
|
398
169
|
}
|
|
399
|
-
} else {
|
|
400
|
-
deconstructedResponse.body = serveAsset(asset);
|
|
401
|
-
deconstructedResponse.headers.set(
|
|
402
|
-
"Content-Type",
|
|
403
|
-
getType(asset) || "application/octet-stream"
|
|
404
|
-
);
|
|
405
|
-
return deconstructedResponse;
|
|
406
|
-
}
|
|
407
|
-
} else if (hasFileExtension(assetName)) {
|
|
408
|
-
if ((asset = getAsset(assetName + ".html"))) {
|
|
409
|
-
deconstructedResponse.body = serveAsset(asset);
|
|
410
|
-
deconstructedResponse.headers.set(
|
|
411
|
-
"Content-Type",
|
|
412
|
-
getType(asset) || "application/octet-stream"
|
|
413
|
-
);
|
|
414
|
-
return deconstructedResponse;
|
|
415
|
-
}
|
|
416
|
-
notFound();
|
|
417
|
-
return deconstructedResponse;
|
|
418
|
-
}
|
|
419
170
|
|
|
420
|
-
|
|
421
|
-
deconstructedResponse.body = serveAsset(asset);
|
|
422
|
-
deconstructedResponse.headers.set(
|
|
423
|
-
"Content-Type",
|
|
424
|
-
getType(asset) || "application/octet-stream"
|
|
425
|
-
);
|
|
426
|
-
return deconstructedResponse;
|
|
427
|
-
}
|
|
171
|
+
const acceptEncoding = parseQualityWeightedList(rawAcceptEncoding);
|
|
428
172
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
} else {
|
|
437
|
-
notFound();
|
|
438
|
-
return deconstructedResponse;
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
const attachHeaders = (
|
|
443
|
-
request: MiniflareRequest,
|
|
444
|
-
deconstructedResponse: {
|
|
445
|
-
status: number;
|
|
446
|
-
headers: MiniflareHeaders;
|
|
447
|
-
body?: Buffer;
|
|
448
|
-
}
|
|
449
|
-
) => {
|
|
450
|
-
const headers = deconstructedResponse.headers;
|
|
451
|
-
const newHeaders = new Headers({});
|
|
452
|
-
const matches = headersMatcher(request) || [];
|
|
453
|
-
|
|
454
|
-
matches.forEach((match) => {
|
|
455
|
-
Object.entries(match).forEach(([name, value]) => {
|
|
456
|
-
newHeaders.append(name, `${value}`);
|
|
457
|
-
});
|
|
458
|
-
});
|
|
173
|
+
if (
|
|
174
|
+
acceptEncoding["identity"] === 0 ||
|
|
175
|
+
(acceptEncoding["*"] === 0 &&
|
|
176
|
+
acceptEncoding["identity"] === undefined)
|
|
177
|
+
) {
|
|
178
|
+
throw new Error("No acceptable encodings available");
|
|
179
|
+
}
|
|
459
180
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
181
|
+
return { encoding: null };
|
|
182
|
+
},
|
|
183
|
+
fetchAsset: async (assetKey) => {
|
|
184
|
+
const filepath = assetKeyEntryMap.get(assetKey);
|
|
185
|
+
if (!filepath) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
"Could not fetch asset. Please file an issue on GitHub (https://github.com/cloudflare/wrangler2/issues/new/choose) with reproduction steps."
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
const body = readFileSync(filepath) as unknown as ReadableStream;
|
|
464
191
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
192
|
+
const contentType = getType(filepath) || "application/octet-stream";
|
|
193
|
+
return { body, contentType };
|
|
194
|
+
},
|
|
468
195
|
});
|
|
469
196
|
};
|
|
470
197
|
|
|
471
198
|
return async (input: RequestInfo, init?: RequestInit) => {
|
|
472
|
-
const request = new
|
|
473
|
-
|
|
474
|
-
attachHeaders(request, deconstructedResponse);
|
|
475
|
-
|
|
476
|
-
const headers = new Headers();
|
|
477
|
-
|
|
478
|
-
[...deconstructedResponse.headers.entries()].forEach(([name, value]) => {
|
|
479
|
-
if (value) headers.set(name, value);
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
return new Response(deconstructedResponse.body, {
|
|
483
|
-
headers,
|
|
484
|
-
status: deconstructedResponse.status,
|
|
485
|
-
});
|
|
199
|
+
const request = new MiniflareRequest(input, init);
|
|
200
|
+
return await generateResponse(request as unknown as Request);
|
|
486
201
|
};
|
|
487
202
|
}
|
|
488
203
|
|
|
489
|
-
const invalidAssetsFetch: typeof
|
|
204
|
+
const invalidAssetsFetch: typeof fetch = () => {
|
|
490
205
|
throw new Error(
|
|
491
206
|
"Trying to fetch assets directly when there is no `directory` option specified."
|
|
492
207
|
);
|
|
@@ -129,7 +129,8 @@ async function main() {
|
|
|
129
129
|
}
|
|
130
130
|
mf = new Miniflare(config);
|
|
131
131
|
// Start Miniflare development server
|
|
132
|
-
await mf.startServer();
|
|
132
|
+
const mfServer = await mf.startServer();
|
|
133
|
+
const mfPort = (mfServer.address() as AddressInfo).port;
|
|
133
134
|
await mf.startScheduler();
|
|
134
135
|
|
|
135
136
|
const internalDurableObjectClassNames = Object.values(
|
|
@@ -195,6 +196,7 @@ async function main() {
|
|
|
195
196
|
process.send &&
|
|
196
197
|
process.send(
|
|
197
198
|
JSON.stringify({
|
|
199
|
+
mfPort: mfPort,
|
|
198
200
|
ready: true,
|
|
199
201
|
durableObjectsPort: durableObjectsMfPort,
|
|
200
202
|
})
|
package/src/pages/dev.tsx
CHANGED
|
@@ -451,9 +451,13 @@ async function spawnProxyProcess({
|
|
|
451
451
|
command: (string | number)[];
|
|
452
452
|
}): Promise<undefined | number> {
|
|
453
453
|
if (command.length === 0) {
|
|
454
|
+
if (port !== undefined) {
|
|
455
|
+
return port;
|
|
456
|
+
}
|
|
457
|
+
|
|
454
458
|
CLEANUP();
|
|
455
459
|
throw new FatalError(
|
|
456
|
-
"Must specify a directory of static assets to serve or a command to run.",
|
|
460
|
+
"Must specify a directory of static assets to serve or a command to run or a proxy port.",
|
|
457
461
|
1
|
|
458
462
|
);
|
|
459
463
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { extname } from "node:path";
|
|
3
|
+
import { hash as blake3hash } from "blake3-wasm";
|
|
4
|
+
|
|
5
|
+
export const hashFile = (filepath: string) => {
|
|
6
|
+
const contents = readFileSync(filepath);
|
|
7
|
+
const base64Contents = contents.toString("base64");
|
|
8
|
+
const extension = extname(filepath).substring(1);
|
|
9
|
+
|
|
10
|
+
return blake3hash(base64Contents + extension)
|
|
11
|
+
.toString("hex")
|
|
12
|
+
.slice(0, 32);
|
|
13
|
+
};
|