wrangler 0.0.0-e178d6fb → 0.0.0-e1c2f5b9

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 (239) hide show
  1. package/README.md +16 -2
  2. package/bin/wrangler.js +31 -11
  3. package/miniflare-dist/index.mjs +1340 -330
  4. package/package.json +40 -13
  5. package/src/__tests__/access.test.ts +25 -0
  6. package/src/__tests__/api-dev.test.ts +238 -0
  7. package/src/__tests__/api-devregistry.test.js +64 -0
  8. package/src/__tests__/api.test.ts +80 -0
  9. package/src/__tests__/configuration.test.ts +683 -470
  10. package/src/__tests__/d1.test.ts +123 -0
  11. package/src/__tests__/delete.test.ts +263 -0
  12. package/src/__tests__/deployments.test.ts +105 -0
  13. package/src/__tests__/dev.test.tsx +453 -70
  14. package/src/__tests__/dialogs.test.tsx +40 -0
  15. package/src/__tests__/generate.test.ts +235 -0
  16. package/src/__tests__/get-host-from-url.test.ts +16 -0
  17. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  18. package/src/__tests__/helpers/mock-cfetch.ts +92 -26
  19. package/src/__tests__/helpers/mock-console.ts +11 -2
  20. package/src/__tests__/helpers/mock-dialogs.ts +1 -1
  21. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  22. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  23. package/src/__tests__/helpers/mock-kv.ts +1 -1
  24. package/src/__tests__/helpers/mock-oauth-flow.ts +74 -53
  25. package/src/__tests__/helpers/mock-process.ts +8 -13
  26. package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
  27. package/src/__tests__/helpers/msw/handlers/deployments.ts +59 -0
  28. package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
  29. package/src/__tests__/helpers/msw/handlers/oauth.ts +31 -0
  30. package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
  31. package/src/__tests__/helpers/msw/handlers/script.ts +75 -0
  32. package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
  33. package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
  34. package/src/__tests__/helpers/msw/index.ts +21 -0
  35. package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
  36. package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +5 -0
  37. package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
  38. package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
  39. package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
  40. package/src/__tests__/index.test.ts +103 -97
  41. package/src/__tests__/init.test.ts +1484 -382
  42. package/src/__tests__/jest.setup.ts +88 -5
  43. package/src/__tests__/kv.test.ts +655 -540
  44. package/src/__tests__/logout.test.ts +47 -0
  45. package/src/__tests__/metrics.test.ts +95 -45
  46. package/src/__tests__/middleware.scheduled.test.ts +145 -0
  47. package/src/__tests__/middleware.test.ts +750 -0
  48. package/src/__tests__/pages-deployment-tail.test.ts +884 -0
  49. package/src/__tests__/pages.test.ts +1301 -119
  50. package/src/__tests__/paths.test.ts +39 -0
  51. package/src/__tests__/publish.test.ts +2266 -1180
  52. package/src/__tests__/pubsub.test.ts +211 -88
  53. package/src/__tests__/queues.test.ts +522 -0
  54. package/src/__tests__/r2.test.ts +363 -203
  55. package/src/__tests__/secret.test.ts +261 -59
  56. package/src/__tests__/tail.test.ts +352 -151
  57. package/src/__tests__/test-old-node-version.js +3 -3
  58. package/src/__tests__/tsconfig-sanity.ts +12 -0
  59. package/src/__tests__/tsconfig.json +8 -0
  60. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  61. package/src/__tests__/type-generation.test.ts +233 -0
  62. package/src/__tests__/user.test.ts +59 -73
  63. package/src/__tests__/utils-collectKeyValues.test.ts +47 -0
  64. package/src/__tests__/validate-dev-props.test.ts +56 -0
  65. package/src/__tests__/whoami.test.tsx +114 -66
  66. package/src/__tests__/worker-namespace.test.ts +154 -155
  67. package/src/api/dev.ts +170 -38
  68. package/src/api/index.ts +1 -0
  69. package/src/bundle-reporter.tsx +41 -2
  70. package/src/bundle.ts +668 -81
  71. package/src/cfetch/index.ts +40 -3
  72. package/src/cfetch/internal.ts +144 -32
  73. package/src/cli.ts +4 -3
  74. package/src/config/config.ts +27 -3
  75. package/src/config/environment.ts +131 -7
  76. package/src/config/index.ts +128 -8
  77. package/src/config/validation-helpers.ts +19 -6
  78. package/src/config/validation.ts +411 -14
  79. package/src/config-cache.ts +3 -2
  80. package/src/create-worker-preview.ts +31 -6
  81. package/src/create-worker-upload-form.ts +84 -22
  82. package/src/d1/backups.tsx +223 -0
  83. package/src/d1/constants.ts +2 -0
  84. package/src/d1/create.tsx +63 -0
  85. package/src/d1/delete.tsx +56 -0
  86. package/src/d1/execute.tsx +352 -0
  87. package/src/d1/formatTimeAgo.ts +14 -0
  88. package/src/d1/index.ts +101 -0
  89. package/src/d1/list.tsx +48 -0
  90. package/src/d1/migrations/apply.tsx +196 -0
  91. package/src/d1/migrations/create.tsx +77 -0
  92. package/src/d1/migrations/helpers.ts +147 -0
  93. package/src/d1/migrations/index.tsx +3 -0
  94. package/src/d1/migrations/list.tsx +79 -0
  95. package/src/d1/migrations/options.ts +18 -0
  96. package/src/d1/options.ts +22 -0
  97. package/src/d1/types.tsx +24 -0
  98. package/src/d1/utils.ts +48 -0
  99. package/src/delete.ts +108 -0
  100. package/src/deployments.ts +83 -0
  101. package/src/deprecated/index.ts +145 -0
  102. package/src/dev/dev-vars.ts +11 -8
  103. package/src/dev/dev.tsx +220 -58
  104. package/src/dev/get-local-persistence-path.tsx +31 -0
  105. package/src/dev/local.tsx +740 -181
  106. package/src/dev/remote.tsx +340 -123
  107. package/src/dev/start-server.ts +543 -0
  108. package/src/dev/use-esbuild.ts +61 -7
  109. package/src/dev/validate-dev-props.ts +31 -0
  110. package/src/dev-registry.tsx +201 -0
  111. package/src/dev.tsx +614 -258
  112. package/src/dialogs.tsx +41 -8
  113. package/src/{worker-namespace.ts → dispatch-namespace.ts} +23 -22
  114. package/src/docs/index.ts +97 -0
  115. package/src/entry.ts +3 -2
  116. package/src/environment-variables/factory.ts +88 -0
  117. package/src/environment-variables/misc-variables.ts +30 -0
  118. package/src/generate/index.ts +300 -0
  119. package/src/git-client.ts +100 -7
  120. package/src/index.tsx +249 -1560
  121. package/src/init.ts +626 -150
  122. package/src/inspect.ts +137 -32
  123. package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
  124. package/src/is-interactive.ts +4 -0
  125. package/src/jest.d.ts +4 -0
  126. package/src/{kv.ts → kv/helpers.ts} +3 -3
  127. package/src/kv/index.ts +596 -0
  128. package/src/logger.ts +27 -8
  129. package/src/metrics/index.ts +1 -0
  130. package/src/metrics/metrics-config.ts +4 -4
  131. package/src/metrics/metrics-dispatcher.ts +1 -0
  132. package/src/metrics/metrics-usage-headers.ts +24 -0
  133. package/src/metrics/send-event.ts +13 -8
  134. package/src/miniflare-cli/assets.ts +172 -465
  135. package/src/miniflare-cli/index.ts +149 -24
  136. package/src/miniflare-cli/tsconfig.json +9 -0
  137. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  138. package/src/miniflare-cli/types.ts +11 -0
  139. package/src/module-collection.ts +12 -3
  140. package/src/pages/build.tsx +108 -42
  141. package/src/pages/constants.ts +11 -0
  142. package/src/pages/deployment-tails.tsx +284 -0
  143. package/src/pages/deployments.tsx +9 -31
  144. package/src/pages/dev.tsx +417 -108
  145. package/src/pages/errors.ts +66 -0
  146. package/src/pages/functions/buildPlugin.ts +86 -84
  147. package/src/pages/functions/buildWorker.ts +121 -106
  148. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  149. package/src/pages/functions/routes-consolidation.ts +73 -0
  150. package/src/pages/functions/routes-transformation.test.ts +282 -0
  151. package/src/pages/functions/routes-transformation.ts +115 -0
  152. package/src/pages/functions/routes-validation.test.ts +403 -0
  153. package/src/pages/functions/routes-validation.ts +202 -0
  154. package/src/pages/functions/tsconfig.json +8 -0
  155. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  156. package/src/pages/functions.tsx +82 -0
  157. package/src/pages/hash.tsx +13 -0
  158. package/src/pages/index.tsx +73 -55
  159. package/src/pages/projects.tsx +9 -3
  160. package/src/pages/prompt-select-project.tsx +31 -0
  161. package/src/pages/publish.tsx +183 -60
  162. package/src/pages/types.ts +1 -0
  163. package/src/pages/upload.tsx +107 -56
  164. package/src/pages/utils.ts +13 -2
  165. package/src/paths.ts +49 -4
  166. package/src/proxy.ts +375 -157
  167. package/src/publish/index.ts +270 -0
  168. package/src/{publish.ts → publish/publish.ts} +431 -188
  169. package/src/pubsub/pubsub-commands.tsx +4 -3
  170. package/src/queues/cli/commands/consumer/add.ts +71 -0
  171. package/src/queues/cli/commands/consumer/index.ts +22 -0
  172. package/src/queues/cli/commands/consumer/remove.ts +38 -0
  173. package/src/queues/cli/commands/create.ts +25 -0
  174. package/src/queues/cli/commands/delete.ts +26 -0
  175. package/src/queues/cli/commands/index.ts +36 -0
  176. package/src/queues/cli/commands/list.ts +25 -0
  177. package/src/queues/client.ts +135 -0
  178. package/src/queues/utils.ts +18 -0
  179. package/src/r2/constants.ts +4 -0
  180. package/src/r2/helpers.ts +131 -0
  181. package/src/r2/index.ts +289 -0
  182. package/src/routes.ts +140 -0
  183. package/src/secret/index.ts +379 -0
  184. package/src/sites.tsx +3 -3
  185. package/src/tail/createTail.ts +379 -0
  186. package/src/tail/filters.ts +4 -6
  187. package/src/tail/index.ts +199 -284
  188. package/src/tail/printing.ts +43 -3
  189. package/src/tsconfig-sanity.ts +16 -0
  190. package/src/type-generation.ts +181 -0
  191. package/src/user/access.ts +68 -0
  192. package/src/user/auth-variables.ts +113 -0
  193. package/src/user/choose-account.tsx +1 -1
  194. package/src/user/index.ts +0 -1
  195. package/src/user/user.tsx +116 -49
  196. package/src/utils/collectKeyValues.ts +14 -0
  197. package/src/whoami.tsx +80 -2
  198. package/src/worker.ts +69 -9
  199. package/src/yargs-types.ts +24 -0
  200. package/src/zones.ts +124 -9
  201. package/templates/__tests__/pages-dev-util.test.ts +128 -0
  202. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  203. package/templates/__tests__/tsconfig.json +8 -0
  204. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  205. package/templates/d1-beta-facade.js +205 -0
  206. package/templates/facade.d.ts +14 -0
  207. package/templates/first-party-worker-module-facade.ts +19 -0
  208. package/templates/format-dev-errors.ts +33 -0
  209. package/templates/init-tests/test-jest-new-worker.js +23 -0
  210. package/templates/init-tests/test-vitest-new-worker.js +24 -0
  211. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  212. package/templates/middleware/common.ts +62 -0
  213. package/templates/middleware/loader-modules.ts +82 -0
  214. package/templates/middleware/loader-sw.ts +233 -0
  215. package/templates/middleware/middleware-miniflare3-json-error.ts +20 -0
  216. package/templates/middleware/middleware-pretty-error.ts +40 -0
  217. package/templates/middleware/middleware-scheduled.ts +15 -0
  218. package/templates/new-worker.js +1 -1
  219. package/templates/pages-dev-pipeline.ts +31 -0
  220. package/templates/pages-dev-util.ts +52 -0
  221. package/templates/pages-shim.ts +4 -1
  222. package/templates/pages-template-plugin.ts +21 -5
  223. package/templates/pages-template-worker.ts +37 -9
  224. package/templates/{static-asset-facade.js → serve-static-assets.ts} +23 -7
  225. package/templates/service-bindings-module-facade.js +54 -0
  226. package/templates/service-bindings-sw-facade.js +42 -0
  227. package/templates/tsconfig-sanity.ts +11 -0
  228. package/templates/tsconfig.init.json +106 -0
  229. package/templates/tsconfig.json +5 -102
  230. package/templates/tsconfig.tsbuildinfo +1 -0
  231. package/wrangler-dist/cli.d.ts +621 -42
  232. package/wrangler-dist/cli.js +144441 -102606
  233. package/src/environment-variables.ts +0 -35
  234. package/src/faye-websocket.d.ts +0 -6
  235. package/src/generate.ts +0 -33
  236. package/src/miniflare-cli/enum-keys.ts +0 -17
  237. package/src/preview.ts +0 -31
  238. package/src/r2.ts +0 -50
  239. package/src/user/env-vars.ts +0 -46
@@ -1,35 +1,64 @@
1
1
  import { existsSync, lstatSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { fetch as miniflareFetch } from "@miniflare/core";
3
+ import { createMetadataObject } from "@cloudflare/pages-shared/metadata-generator/createMetadataObject";
4
+ import { parseHeaders } from "@cloudflare/pages-shared/metadata-generator/parseHeaders";
5
+ import { parseRedirects } from "@cloudflare/pages-shared/metadata-generator/parseRedirects";
4
6
  import { watch } from "chokidar";
5
7
  import { getType } from "mime";
6
- import { Response } from "miniflare";
7
- import type { Headers as MiniflareHeaders } from "@miniflare/core";
8
- import type { Log } from "miniflare";
8
+ import { hashFile } from "../pages/hash";
9
+ import type { Metadata } from "@cloudflare/pages-shared/asset-server/metadata";
9
10
  import type {
10
- Request as MiniflareRequest,
11
- RequestInfo,
12
- RequestInit,
13
- } from "miniflare";
11
+ ParsedRedirects,
12
+ ParsedHeaders,
13
+ } from "@cloudflare/pages-shared/metadata-generator/types";
14
+ import type {
15
+ RequestInfo as TreRequestInfo,
16
+ RequestInit as TreRequestInit,
17
+ } from "@miniflare/tre";
18
+
19
+ interface Logger {
20
+ log: (message: string) => void;
21
+ warn: (message: string) => void;
22
+ error: (error: Error) => void;
23
+ }
14
24
 
15
25
  export interface Options {
16
- log: Log;
26
+ log: Logger;
17
27
  proxyPort?: number;
18
28
  directory?: string;
29
+ tre: boolean;
19
30
  }
20
31
 
21
32
  export default async function generateASSETSBinding(options: Options) {
22
33
  const assetsFetch =
23
34
  options.directory !== undefined
24
- ? await generateAssetsFetch(options.directory, options.log)
35
+ ? await generateAssetsFetch(options.directory, options.log, options.tre)
25
36
  : invalidAssetsFetch;
26
37
 
27
- return async function (request: MiniflareRequest) {
38
+ const miniflare = options.tre
39
+ ? await import("@miniflare/tre")
40
+ : await import("@miniflare/core");
41
+
42
+ const Request = miniflare.Request;
43
+ const Response = miniflare.Response;
44
+ // WebSockets won't work with `--experimental-local` until we expose something like `upgradingFetch` from `@miniflare/tre`.
45
+ const fetch = (
46
+ options.tre
47
+ ? miniflare.fetch
48
+ : (await import("@miniflare/web-sockets")).upgradingFetch
49
+ ) as (request: Request) => Promise<Response>;
50
+
51
+ return async function (miniflareRequest: Request) {
28
52
  if (options.proxyPort) {
29
53
  try {
30
- const url = new URL(request.url);
54
+ const url = new URL(miniflareRequest.url);
31
55
  url.host = `localhost:${options.proxyPort}`;
32
- return await miniflareFetch(url, request);
56
+ const proxyRequest = new Request(url, miniflareRequest);
57
+ if (proxyRequest.headers.get("Upgrade") === "websocket") {
58
+ proxyRequest.headers.delete("Sec-WebSocket-Accept");
59
+ proxyRequest.headers.delete("Sec-WebSocket-Key");
60
+ }
61
+ return await fetch(proxyRequest as Request);
33
62
  } catch (thrown) {
34
63
  options.log.error(new Error(`Could not proxy request: ${thrown}`));
35
64
 
@@ -40,7 +69,7 @@ export default async function generateASSETSBinding(options: Options) {
40
69
  }
41
70
  } else {
42
71
  try {
43
- return await assetsFetch(request);
72
+ return await assetsFetch(miniflareRequest);
44
73
  } catch (thrown) {
45
74
  options.log.error(new Error(`Could not serve static asset: ${thrown}`));
46
75
 
@@ -54,239 +83,36 @@ export default async function generateASSETSBinding(options: Options) {
54
83
  };
55
84
  }
56
85
 
57
- function escapeRegex(str: string) {
58
- return str.replace(/[-/\\^$*+?.()|[]{}]/g, "\\$&");
59
- }
60
-
61
- type Replacements = Record<string, string>;
62
-
63
- function replacer(str: string, replacements: Replacements) {
64
- for (const [replacement, value] of Object.entries(replacements)) {
65
- str = str.replace(`:${replacement}`, value);
66
- }
67
- return str;
68
- }
69
-
70
- function generateRulesMatcher<T>(
71
- rules?: Record<string, T>,
72
- replacerFn: (match: T, replacements: Replacements) => T = (match) => match
73
- ) {
74
- // TODO: How can you test cross-host rules?
75
- if (!rules) return () => [];
76
-
77
- const compiledRules = Object.entries(rules)
78
- .map(([rule, match]) => {
79
- const crossHost = rule.startsWith("https://");
80
-
81
- rule = rule.split("*").map(escapeRegex).join("(?<splat>.*)");
82
-
83
- const host_matches = rule.matchAll(
84
- /(?<=^https:\\\/\\\/[^/]*?):([^\\]+)(?=\\)/g
85
- );
86
- for (const hostMatch of host_matches) {
87
- rule = rule.split(hostMatch[0]).join(`(?<${hostMatch[1]}>[^/.]+)`);
88
- }
89
-
90
- const path_matches = rule.matchAll(/:(\w+)/g);
91
- for (const pathMatch of path_matches) {
92
- rule = rule.split(pathMatch[0]).join(`(?<${pathMatch[1]}>[^/]+)`);
93
- }
94
-
95
- rule = "^" + rule + "$";
96
-
97
- try {
98
- const regExp = new RegExp(rule);
99
- return [{ crossHost, regExp }, match];
100
- } catch {}
101
- })
102
- .filter((value) => value !== undefined) as [
103
- { crossHost: boolean; regExp: RegExp },
104
- T
105
- ][];
106
-
107
- return ({ request }: { request: MiniflareRequest }) => {
108
- const { pathname, host } = new URL(request.url);
109
-
110
- return compiledRules
111
- .map(([{ crossHost, regExp }, match]) => {
112
- const test = crossHost ? `https://${host}${pathname}` : pathname;
113
- const result = regExp.exec(test);
114
- if (result) {
115
- return replacerFn(match, result.groups || {});
116
- }
117
- })
118
- .filter((value) => value !== undefined) as T[];
119
- };
120
- }
121
-
122
- function generateHeadersMatcher(headersFile: string) {
123
- if (existsSync(headersFile)) {
124
- const contents = readFileSync(headersFile).toString();
125
-
126
- // TODO: Log errors
127
- const lines = contents
128
- .split("\n")
129
- .map((line) => line.trim())
130
- .filter((line) => !line.startsWith("#") && line !== "");
131
-
132
- const rules: Record<string, Record<string, string>> = {};
133
- let rule: { path: string; headers: Record<string, string> } | undefined =
134
- undefined;
135
-
136
- for (const line of lines) {
137
- if (/^([^\s]+:\/\/|^\/)/.test(line)) {
138
- if (rule && Object.keys(rule.headers).length > 0) {
139
- rules[rule.path] = rule.headers;
140
- }
141
-
142
- const path = validateURL(line);
143
- if (path) {
144
- rule = {
145
- path,
146
- headers: {},
147
- };
148
- continue;
149
- }
150
- }
151
-
152
- if (!line.includes(":")) continue;
153
-
154
- const [rawName, ...rawValue] = line.split(":");
155
- const name = rawName.trim().toLowerCase();
156
- const value = rawValue.join(":").trim();
157
-
158
- if (name === "") continue;
159
- if (!rule) continue;
160
-
161
- const existingValues = rule.headers[name];
162
- rule.headers[name] = existingValues
163
- ? `${existingValues}, ${value}`
164
- : value;
165
- }
166
-
167
- if (rule && Object.keys(rule.headers).length > 0) {
168
- rules[rule.path] = rule.headers;
169
- }
170
-
171
- const rulesMatcher = generateRulesMatcher(rules, (match, replacements) =>
172
- Object.fromEntries(
173
- Object.entries(match).map(([name, value]) => [
174
- name,
175
- replacer(value, replacements),
176
- ])
177
- )
178
- );
179
-
180
- return (request: MiniflareRequest) => {
181
- const matches = rulesMatcher({
182
- request,
183
- });
184
- if (matches) return matches;
185
- };
186
- } else {
187
- return () => undefined;
188
- }
189
- }
190
-
191
- function generateRedirectsMatcher(redirectsFile: string) {
192
- if (existsSync(redirectsFile)) {
193
- const contents = readFileSync(redirectsFile).toString();
194
-
195
- // TODO: Log errors
196
- const lines = contents
197
- .split("\n")
198
- .map((line) => line.trim())
199
- .filter((line) => !line.startsWith("#") && line !== "");
200
-
201
- const rules = Object.fromEntries(
202
- lines
203
- .map((line) => line.split(" "))
204
- .filter((tokens) => tokens.length === 2 || tokens.length === 3)
205
- .map((tokens) => {
206
- const from = validateURL(tokens[0], true, false, false);
207
- const to = validateURL(tokens[1], false, true, true);
208
- let status: number | undefined = parseInt(tokens[2]) || 302;
209
- status = [301, 302, 303, 307, 308].includes(status)
210
- ? status
211
- : undefined;
212
-
213
- return from && to && status ? [from, { to, status }] : undefined;
214
- })
215
- .filter((rule) => rule !== undefined) as [
216
- string,
217
- { to: string; status?: number }
218
- ][]
219
- );
220
-
221
- const rulesMatcher = generateRulesMatcher(
222
- rules,
223
- ({ status, to }, replacements) => ({
224
- status,
225
- to: replacer(to, replacements),
226
- })
227
- );
228
-
229
- return (request: MiniflareRequest) => {
230
- const match = rulesMatcher({
231
- request,
232
- })[0];
233
- if (match) return match;
234
- };
235
- } else {
236
- return () => undefined;
237
- }
238
- }
239
-
240
- function extractPathname(
241
- path = "/",
242
- includeSearch: boolean,
243
- includeHash: boolean
244
- ) {
245
- if (!path.startsWith("/")) path = `/${path}`;
246
- const url = new URL(`//${path}`, "relative://");
247
- return `${url.pathname}${includeSearch ? url.search : ""}${
248
- includeHash ? url.hash : ""
249
- }`;
250
- }
251
-
252
- function validateURL(
253
- token: string,
254
- onlyRelative = false,
255
- includeSearch = false,
256
- includeHash = false
257
- ) {
258
- const host = /^https:\/\/+(?<host>[^/]+)\/?(?<path>.*)/.exec(token);
259
- if (host && host.groups && host.groups.host) {
260
- if (onlyRelative) return;
261
-
262
- return `https://${host.groups.host}${extractPathname(
263
- host.groups.path,
264
- includeSearch,
265
- includeHash
266
- )}`;
267
- } else {
268
- if (!token.startsWith("/") && onlyRelative) token = `/${token}`;
269
-
270
- const path = /^\//.exec(token);
271
- if (path) {
272
- try {
273
- return extractPathname(token, includeSearch, includeHash);
274
- } catch {}
275
- }
276
- }
277
- return "";
278
- }
279
-
280
- function hasFileExtension(pathname: string) {
281
- return /\/.+\.[a-z0-9]+$/i.test(pathname);
282
- }
283
-
284
86
  async function generateAssetsFetch(
285
87
  directory: string,
286
- log: Log
287
- ): Promise<typeof miniflareFetch> {
88
+ log: Logger,
89
+ tre: boolean
90
+ ): Promise<typeof fetch> {
288
91
  // Defer importing miniflare until we really need it
289
- const { Headers, Request } = await import("@miniflare/core");
92
+
93
+ // NOTE: These dynamic imports bring in `global` type augmentations from
94
+ // `@cloudflare/pages-shared/environment-polyfills/types.ts`, allowing us to
95
+ // use `fetch`, `Headers`, `Request` and `Response` as globals in this file
96
+ // and the *entire* `miniflare-cli` TypeScript project.
97
+ const polyfill = tre
98
+ ? (
99
+ await import(
100
+ "@cloudflare/pages-shared/environment-polyfills/miniflare-tre"
101
+ )
102
+ ).default
103
+ : (await import("@cloudflare/pages-shared/environment-polyfills/miniflare"))
104
+ .default;
105
+
106
+ await polyfill();
107
+
108
+ const miniflare = tre
109
+ ? await import("@miniflare/tre")
110
+ : await import("@miniflare/core");
111
+ const Request = miniflare.Request;
112
+
113
+ const { generateHandler, parseQualityWeightedList } = await import(
114
+ "@cloudflare/pages-shared/asset-server/handler"
115
+ );
290
116
 
291
117
  const headersFile = join(directory, "_headers");
292
118
  const redirectsFile = join(directory, "_redirects");
@@ -294,249 +120,130 @@ async function generateAssetsFetch(
294
120
 
295
121
  const ignoredFiles = [headersFile, redirectsFile, workerFile];
296
122
 
297
- const assetExists = (path: string) => {
298
- path = join(directory, path);
299
- return (
300
- existsSync(path) &&
301
- lstatSync(path).isFile() &&
302
- !ignoredFiles.includes(path)
303
- );
304
- };
305
-
306
- const getAsset = (path: string) => {
307
- if (assetExists(path)) {
308
- return join(directory, path);
309
- }
310
- };
123
+ let redirects: ParsedRedirects | undefined;
124
+ if (existsSync(redirectsFile)) {
125
+ const contents = readFileSync(redirectsFile, "utf-8");
126
+ redirects = parseRedirects(contents);
127
+ }
311
128
 
312
- let redirectsMatcher = generateRedirectsMatcher(redirectsFile);
313
- let headersMatcher = generateHeadersMatcher(headersFile);
129
+ let headers: ParsedHeaders | undefined;
130
+ if (existsSync(headersFile)) {
131
+ const contents = readFileSync(headersFile, "utf-8");
132
+ headers = parseHeaders(contents);
133
+ }
314
134
 
315
- watch([headersFile, redirectsFile], {
316
- persistent: true,
317
- }).on("change", (path) => {
318
- switch (path) {
319
- case headersFile: {
320
- log.log("_headers modified. Re-evaluating...");
321
- headersMatcher = generateHeadersMatcher(headersFile);
322
- break;
323
- }
324
- case redirectsFile: {
325
- log.log("_redirects modified. Re-evaluating...");
326
- redirectsMatcher = generateRedirectsMatcher(redirectsFile);
327
- break;
328
- }
329
- }
135
+ let metadata = createMetadataObject({
136
+ redirects,
137
+ headers,
138
+ logger: log.warn.bind(log),
330
139
  });
331
140
 
332
- const serveAsset = (file: string) => {
333
- return readFileSync(file);
334
- };
335
-
336
- const generateResponse = (request: MiniflareRequest) => {
337
- const url = new URL(request.url);
338
-
339
- const deconstructedResponse: {
340
- status: number;
341
- headers: MiniflareHeaders;
342
- body?: Buffer;
343
- } = {
344
- status: 200,
345
- headers: new Headers(),
346
- body: undefined,
347
- };
348
-
349
- const match = redirectsMatcher(request);
350
- if (match) {
351
- const { status, to } = match;
352
-
353
- let location = to;
354
- let search;
355
-
356
- if (to.startsWith("/")) {
357
- search = new URL(location, "http://fakehost").search;
358
- } else {
359
- search = new URL(location).search;
360
- }
361
-
362
- location = `${location}${search ? "" : url.search}`;
363
-
364
- if (status && [301, 302, 303, 307, 308].includes(status)) {
365
- deconstructedResponse.status = status;
366
- } else {
367
- deconstructedResponse.status = 302;
368
- }
369
-
370
- deconstructedResponse.headers.set("Location", location);
371
- return deconstructedResponse;
372
- }
373
-
374
- if (!request.method?.match(/^(get|head)$/i)) {
375
- deconstructedResponse.status = 405;
376
- return deconstructedResponse;
377
- }
378
-
379
- const notFound = () => {
380
- let cwd = url.pathname;
381
- while (cwd) {
382
- cwd = cwd.slice(0, cwd.lastIndexOf("/"));
383
-
384
- if ((asset = getAsset(`${cwd}/404.html`))) {
385
- deconstructedResponse.status = 404;
386
- deconstructedResponse.body = serveAsset(asset);
387
- deconstructedResponse.headers.set(
388
- "Content-Type",
389
- getType(asset) || "application/octet-stream"
390
- );
391
- return deconstructedResponse;
141
+ watch([headersFile, redirectsFile], { persistent: true }).on(
142
+ "change",
143
+ (path) => {
144
+ switch (path) {
145
+ case headersFile: {
146
+ log.log("_headers modified. Re-evaluating...");
147
+ const contents = readFileSync(headersFile).toString();
148
+ headers = parseHeaders(contents);
149
+ break;
150
+ }
151
+ case redirectsFile: {
152
+ log.log("_redirects modified. Re-evaluating...");
153
+ const contents = readFileSync(redirectsFile).toString();
154
+ redirects = parseRedirects(contents);
155
+ break;
392
156
  }
393
157
  }
394
158
 
395
- if ((asset = getAsset(`/index.html`))) {
396
- deconstructedResponse.body = serveAsset(asset);
397
- deconstructedResponse.headers.set(
398
- "Content-Type",
399
- getType(asset) || "application/octet-stream"
400
- );
401
- return deconstructedResponse;
402
- }
403
-
404
- deconstructedResponse.status = 404;
405
- return deconstructedResponse;
406
- };
407
-
408
- let asset;
409
-
410
- if (url.pathname.endsWith("/")) {
411
- if ((asset = getAsset(`${url.pathname}/index.html`))) {
412
- deconstructedResponse.body = serveAsset(asset);
413
- deconstructedResponse.headers.set(
414
- "Content-Type",
415
- getType(asset) || "application/octet-stream"
416
- );
417
- return deconstructedResponse;
418
- } else if (
419
- (asset = getAsset(`${url.pathname.replace(/\/$/, ".html")}`))
420
- ) {
421
- deconstructedResponse.status = 301;
422
- deconstructedResponse.headers.set(
423
- "Location",
424
- `${url.pathname.slice(0, -1)}${url.search}`
425
- );
426
- return deconstructedResponse;
427
- }
159
+ metadata = createMetadataObject({
160
+ redirects,
161
+ headers,
162
+ logger: log.warn,
163
+ });
428
164
  }
165
+ );
429
166
 
430
- if (url.pathname.endsWith("/index")) {
431
- deconstructedResponse.status = 301;
432
- deconstructedResponse.headers.set(
433
- "Location",
434
- `${url.pathname.slice(0, -"index".length)}${url.search}`
435
- );
436
- return deconstructedResponse;
437
- }
167
+ const generateResponse = async (request: Request) => {
168
+ const assetKeyEntryMap = new Map<string, string>();
169
+
170
+ return await generateHandler<string>({
171
+ request,
172
+ metadata: metadata as Metadata,
173
+ xServerEnvHeader: "dev",
174
+ logError: console.error,
175
+ findAssetEntryForPath: async (path) => {
176
+ const filepath = join(directory, path);
177
+
178
+ if (
179
+ existsSync(filepath) &&
180
+ lstatSync(filepath).isFile() &&
181
+ !ignoredFiles.includes(filepath)
182
+ ) {
183
+ const hash = hashFile(filepath);
184
+ assetKeyEntryMap.set(hash, filepath);
185
+ return hash;
186
+ }
438
187
 
439
- if ((asset = getAsset(url.pathname))) {
440
- if (url.pathname.endsWith(".html")) {
441
- const extensionlessPath = url.pathname.slice(0, -".html".length);
442
- if (getAsset(extensionlessPath) || extensionlessPath === "/") {
443
- deconstructedResponse.body = serveAsset(asset);
444
- deconstructedResponse.headers.set(
445
- "Content-Type",
446
- getType(asset) || "application/octet-stream"
447
- );
448
- return deconstructedResponse;
188
+ return null;
189
+ },
190
+ getAssetKey: (assetEntry) => {
191
+ return assetEntry;
192
+ },
193
+ negotiateContent: (contentRequest) => {
194
+ let rawAcceptEncoding: string | undefined;
195
+ if (
196
+ contentRequest.cf &&
197
+ "clientAcceptEncoding" in contentRequest.cf &&
198
+ contentRequest.cf.clientAcceptEncoding
199
+ ) {
200
+ rawAcceptEncoding = contentRequest.cf.clientAcceptEncoding as string;
449
201
  } else {
450
- deconstructedResponse.status = 301;
451
- deconstructedResponse.headers.set(
452
- "Location",
453
- `${extensionlessPath}${url.search}`
454
- );
455
- return deconstructedResponse;
202
+ rawAcceptEncoding =
203
+ contentRequest.headers.get("Accept-Encoding") || undefined;
456
204
  }
457
- } else {
458
- deconstructedResponse.body = serveAsset(asset);
459
- deconstructedResponse.headers.set(
460
- "Content-Type",
461
- getType(asset) || "application/octet-stream"
462
- );
463
- return deconstructedResponse;
464
- }
465
- } else if (hasFileExtension(url.pathname)) {
466
- notFound();
467
- return deconstructedResponse;
468
- }
469
205
 
470
- if ((asset = getAsset(`${url.pathname}.html`))) {
471
- deconstructedResponse.body = serveAsset(asset);
472
- deconstructedResponse.headers.set(
473
- "Content-Type",
474
- getType(asset) || "application/octet-stream"
475
- );
476
- return deconstructedResponse;
477
- }
206
+ const acceptEncoding = parseQualityWeightedList(rawAcceptEncoding);
478
207
 
479
- if ((asset = getAsset(`${url.pathname}/index.html`))) {
480
- deconstructedResponse.status = 301;
481
- deconstructedResponse.headers.set(
482
- "Location",
483
- `${url.pathname}/${url.search}`
484
- );
485
- return deconstructedResponse;
486
- } else {
487
- notFound();
488
- return deconstructedResponse;
489
- }
490
- };
491
-
492
- const attachHeaders = (
493
- request: MiniflareRequest,
494
- deconstructedResponse: {
495
- status: number;
496
- headers: MiniflareHeaders;
497
- body?: Buffer;
498
- }
499
- ) => {
500
- const headers = deconstructedResponse.headers;
501
- const newHeaders = new Headers({});
502
- const matches = headersMatcher(request) || [];
503
-
504
- matches.forEach((match) => {
505
- Object.entries(match).forEach(([name, value]) => {
506
- newHeaders.append(name, `${value}`);
507
- });
508
- });
208
+ if (
209
+ acceptEncoding["identity"] === 0 ||
210
+ (acceptEncoding["*"] === 0 &&
211
+ acceptEncoding["identity"] === undefined)
212
+ ) {
213
+ throw new Error("No acceptable encodings available");
214
+ }
509
215
 
510
- const combinedHeaders = {
511
- ...Object.fromEntries(headers.entries()),
512
- ...Object.fromEntries(newHeaders.entries()),
513
- };
216
+ return { encoding: null };
217
+ },
218
+ fetchAsset: async (assetKey) => {
219
+ const filepath = assetKeyEntryMap.get(assetKey);
220
+ if (!filepath) {
221
+ throw new Error(
222
+ "Could not fetch asset. Please file an issue on GitHub (https://github.com/cloudflare/wrangler2/issues/new/choose) with reproduction steps."
223
+ );
224
+ }
225
+ const body = readFileSync(filepath) as unknown as ReadableStream;
226
+
227
+ let contentType = getType(filepath) || "application/octet-stream";
228
+ if (
229
+ contentType.startsWith("text/") &&
230
+ !contentType.includes("charset")
231
+ ) {
232
+ contentType = `${contentType}; charset=utf-8`;
233
+ }
514
234
 
515
- deconstructedResponse.headers = new Headers({});
516
- Object.entries(combinedHeaders).forEach(([name, value]) => {
517
- if (value) deconstructedResponse.headers.set(name, value);
235
+ return { body, contentType };
236
+ },
518
237
  });
519
238
  };
520
239
 
521
- return async (input: RequestInfo, init?: RequestInit) => {
240
+ return (async (input: TreRequestInfo, init?: TreRequestInit) => {
522
241
  const request = new Request(input, init);
523
- const deconstructedResponse = generateResponse(request);
524
- attachHeaders(request, deconstructedResponse);
525
-
526
- const headers = new Headers();
527
-
528
- [...deconstructedResponse.headers.entries()].forEach(([name, value]) => {
529
- if (value) headers.set(name, value);
530
- });
531
-
532
- return new Response(deconstructedResponse.body, {
533
- headers,
534
- status: deconstructedResponse.status,
535
- });
536
- };
242
+ return await generateResponse(request as unknown as Request);
243
+ }) as typeof fetch;
537
244
  }
538
245
 
539
- const invalidAssetsFetch: typeof miniflareFetch = () => {
246
+ const invalidAssetsFetch: typeof fetch = () => {
540
247
  throw new Error(
541
248
  "Trying to fetch assets directly when there is no `directory` option specified."
542
249
  );