wrangler 0.0.0-e88a54e → 0.0.0-e8e684c7

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 (222) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +51 -14
  3. package/bin/wrangler.js +161 -29
  4. package/kv-asset-handler.js +1 -0
  5. package/miniflare-dist/index.mjs +5484 -21
  6. package/package.json +173 -143
  7. package/src/__tests__/config-cache-without-cache-dir.test.ts +38 -0
  8. package/src/__tests__/config-cache.test.ts +42 -0
  9. package/src/__tests__/configuration.test.ts +4016 -2786
  10. package/src/__tests__/dev.test.tsx +1305 -483
  11. package/src/__tests__/generate.test.ts +91 -0
  12. package/src/__tests__/guess-worker-format.test.ts +75 -48
  13. package/src/__tests__/helpers/cmd-shim.d.ts +6 -6
  14. package/src/__tests__/helpers/mock-account-id.ts +24 -24
  15. package/src/__tests__/helpers/mock-bin.ts +20 -20
  16. package/src/__tests__/helpers/mock-cfetch.ts +182 -93
  17. package/src/__tests__/helpers/mock-console.ts +59 -32
  18. package/src/__tests__/helpers/mock-dialogs.ts +121 -33
  19. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  20. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  21. package/src/__tests__/helpers/mock-istty.ts +74 -0
  22. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  23. package/src/__tests__/helpers/mock-kv.ts +26 -26
  24. package/src/__tests__/helpers/mock-oauth-flow.ts +250 -0
  25. package/src/__tests__/helpers/mock-process.ts +39 -0
  26. package/src/__tests__/helpers/mock-stdin.ts +82 -77
  27. package/src/__tests__/helpers/mock-web-socket.ts +21 -21
  28. package/src/__tests__/helpers/run-in-tmp.ts +28 -22
  29. package/src/__tests__/helpers/run-wrangler.ts +8 -8
  30. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  31. package/src/__tests__/helpers/write-wrangler-toml.ts +9 -9
  32. package/src/__tests__/https-options.test.ts +144 -108
  33. package/src/__tests__/index.test.ts +229 -975
  34. package/src/__tests__/init.test.ts +2170 -0
  35. package/src/__tests__/jest.setup.ts +122 -34
  36. package/src/__tests__/kv.test.ts +1376 -1087
  37. package/src/__tests__/logger.test.ts +141 -0
  38. package/src/__tests__/metrics.test.ts +450 -0
  39. package/src/__tests__/package-manager.test.ts +312 -165
  40. package/src/__tests__/pages.test.ts +1270 -39
  41. package/src/__tests__/parse.test.ts +253 -245
  42. package/src/__tests__/publish.test.ts +6878 -2627
  43. package/src/__tests__/pubsub.test.ts +367 -0
  44. package/src/__tests__/r2.test.ts +328 -190
  45. package/src/__tests__/route.test.ts +22 -22
  46. package/src/__tests__/secret.test.ts +474 -407
  47. package/src/__tests__/tail.test.ts +679 -441
  48. package/src/__tests__/test-old-node-version.js +31 -0
  49. package/src/__tests__/user.test.ts +155 -0
  50. package/src/__tests__/validate-dev-props.test.ts +56 -0
  51. package/src/__tests__/version.test.ts +35 -0
  52. package/src/__tests__/whoami.test.tsx +276 -87
  53. package/src/__tests__/worker-namespace.test.ts +329 -0
  54. package/src/abort.d.ts +3 -0
  55. package/src/api/dev.ts +93 -0
  56. package/src/api/index.ts +1 -0
  57. package/src/bundle-reporter.tsx +29 -0
  58. package/src/bundle.ts +441 -110
  59. package/src/cfetch/index.ts +83 -66
  60. package/src/cfetch/internal.ts +251 -92
  61. package/src/cli.ts +22 -14
  62. package/src/config/README.md +1 -1
  63. package/src/config/config.ts +224 -179
  64. package/src/config/diagnostics.ts +61 -61
  65. package/src/config/environment.ts +430 -311
  66. package/src/config/index.ts +227 -38
  67. package/src/config/validation-helpers.ts +414 -303
  68. package/src/config/validation.ts +1745 -973
  69. package/src/config-cache.ts +85 -0
  70. package/src/create-worker-preview.ts +209 -118
  71. package/src/create-worker-upload-form.ts +275 -201
  72. package/src/dev/dev-vars.ts +36 -0
  73. package/src/dev/dev.tsx +488 -307
  74. package/src/dev/local.tsx +427 -210
  75. package/src/dev/remote.tsx +407 -188
  76. package/src/dev/use-esbuild.ts +156 -78
  77. package/src/dev/validate-dev-props.ts +31 -0
  78. package/src/dev-registry.tsx +160 -0
  79. package/src/dev.tsx +696 -0
  80. package/src/dialogs.tsx +115 -57
  81. package/src/dispatch-namespace.ts +206 -0
  82. package/src/durable.ts +102 -0
  83. package/src/entry.ts +267 -147
  84. package/src/environment-variables.ts +25 -23
  85. package/src/errors.ts +11 -0
  86. package/src/faye-websocket.d.ts +6 -0
  87. package/src/generate.ts +131 -0
  88. package/src/git-client.ts +42 -0
  89. package/src/global-wrangler-config-path.ts +26 -0
  90. package/src/https-options.ts +85 -80
  91. package/src/index.tsx +2191 -2304
  92. package/src/init.ts +622 -0
  93. package/src/inspect.ts +687 -557
  94. package/src/intl-polyfill.d.ts +139 -0
  95. package/src/is-ci.ts +14 -0
  96. package/src/is-interactive.ts +12 -0
  97. package/src/kv.ts +353 -242
  98. package/src/logger.ts +73 -0
  99. package/src/metrics/index.ts +5 -0
  100. package/src/metrics/metrics-config.ts +239 -0
  101. package/src/metrics/metrics-dispatcher.ts +96 -0
  102. package/src/metrics/metrics-usage-headers.ts +24 -0
  103. package/src/metrics/send-event.ts +92 -0
  104. package/src/miniflare-cli/assets.ts +554 -0
  105. package/src/miniflare-cli/enum-keys.ts +8 -8
  106. package/src/miniflare-cli/index.ts +199 -29
  107. package/src/miniflare-cli/request-context.ts +40 -0
  108. package/src/module-collection.ts +213 -213
  109. package/src/open-in-browser.ts +8 -16
  110. package/src/package-manager.ts +183 -90
  111. package/src/pages/build.tsx +238 -0
  112. package/src/pages/constants.ts +12 -0
  113. package/src/pages/deployments.tsx +106 -0
  114. package/src/pages/dev.tsx +528 -0
  115. package/src/pages/errors.ts +22 -0
  116. package/src/pages/functions/buildPlugin.ts +109 -0
  117. package/src/pages/functions/buildWorker.ts +155 -0
  118. package/src/pages/functions/filepath-routing.test.ts +234 -0
  119. package/src/pages/functions/filepath-routing.ts +189 -0
  120. package/src/pages/functions/identifiers.ts +78 -0
  121. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  122. package/src/pages/functions/routes-consolidation.ts +73 -0
  123. package/src/pages/functions/routes-transformation.test.ts +271 -0
  124. package/src/pages/functions/routes-transformation.ts +122 -0
  125. package/src/pages/functions/routes.ts +151 -0
  126. package/src/pages/functions.tsx +96 -0
  127. package/src/pages/index.tsx +94 -0
  128. package/src/pages/projects.tsx +169 -0
  129. package/src/pages/publish.tsx +391 -0
  130. package/src/pages/types.ts +49 -0
  131. package/src/pages/upload.tsx +413 -0
  132. package/src/pages/utils.ts +12 -0
  133. package/src/parse.ts +224 -121
  134. package/src/paths.ts +6 -6
  135. package/src/preview.ts +31 -0
  136. package/src/proxy.ts +416 -370
  137. package/src/publish.ts +796 -321
  138. package/src/pubsub/index.ts +286 -0
  139. package/src/pubsub/pubsub-commands.tsx +623 -0
  140. package/src/r2.ts +100 -19
  141. package/src/selfsigned.d.ts +23 -23
  142. package/src/sites.tsx +281 -189
  143. package/src/tail/filters.ts +111 -109
  144. package/src/tail/index.ts +230 -217
  145. package/src/tail/printing.ts +92 -47
  146. package/src/update-check.ts +19 -0
  147. package/src/user/choose-account.tsx +69 -0
  148. package/src/user/env-vars.ts +46 -0
  149. package/src/user/generate-auth-url.ts +33 -0
  150. package/src/user/generate-random-state.ts +16 -0
  151. package/src/user/index.ts +3 -0
  152. package/src/user/user.tsx +1191 -0
  153. package/src/whoami.tsx +142 -42
  154. package/src/worker.ts +135 -94
  155. package/src/zones.ts +167 -0
  156. package/templates/checked-fetch.js +17 -0
  157. package/templates/first-party-worker-module-facade.ts +18 -0
  158. package/templates/format-dev-errors.ts +32 -0
  159. package/templates/gitignore +171 -0
  160. package/templates/new-worker-scheduled.js +17 -0
  161. package/templates/new-worker-scheduled.ts +32 -0
  162. package/templates/new-worker.js +3 -3
  163. package/templates/new-worker.ts +18 -3
  164. package/templates/no-op-worker.js +10 -0
  165. package/templates/pages-shim.ts +9 -0
  166. package/templates/pages-template-plugin.ts +167 -0
  167. package/templates/pages-template-worker.ts +172 -0
  168. package/templates/serve-static-assets.ts +63 -0
  169. package/templates/service-bindings-module-facade.js +54 -0
  170. package/templates/service-bindings-sw-facade.js +42 -0
  171. package/templates/tsconfig.json +95 -95
  172. package/wrangler-dist/cli.d.ts +338 -0
  173. package/wrangler-dist/cli.js +124611 -112403
  174. package/miniflare-dist/index.mjs.map +0 -7
  175. package/pages/functions/buildWorker.ts +0 -62
  176. package/pages/functions/filepath-routing.test.ts +0 -223
  177. package/pages/functions/filepath-routing.ts +0 -181
  178. package/pages/functions/identifiers.ts +0 -78
  179. package/pages/functions/routes.ts +0 -151
  180. package/pages/functions/template-worker.ts +0 -130
  181. package/src/__tests__/dev2.test.tsx +0 -154
  182. package/src/__tests__/helpers/faye-websocket.d.ts +0 -6
  183. package/src/__tests__/logout.test.ts +0 -52
  184. package/src/__tests__/sentry.test.ts +0 -146
  185. package/src/pages.tsx +0 -1047
  186. package/src/reporting.ts +0 -131
  187. package/src/user.tsx +0 -1118
  188. package/templates/static-asset-facade.js +0 -47
  189. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  190. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  191. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  192. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  193. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  194. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  195. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  196. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  197. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  198. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  199. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  200. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  201. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  202. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  203. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  204. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  205. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  206. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  207. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  208. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  209. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  210. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  211. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  212. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  213. package/vendor/wrangler-mime/LICENSE +0 -21
  214. package/vendor/wrangler-mime/Mime.js +0 -97
  215. package/vendor/wrangler-mime/README.md +0 -187
  216. package/vendor/wrangler-mime/cli.js +0 -46
  217. package/vendor/wrangler-mime/index.js +0 -4
  218. package/vendor/wrangler-mime/lite.js +0 -4
  219. package/vendor/wrangler-mime/package.json +0 -52
  220. package/vendor/wrangler-mime/types/other.js +0 -1
  221. package/vendor/wrangler-mime/types/standard.js +0 -1
  222. package/wrangler-dist/cli.js.map +0 -7
@@ -0,0 +1,413 @@
1
+ import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
2
+ import {
3
+ basename,
4
+ dirname,
5
+ extname,
6
+ join,
7
+ relative,
8
+ resolve,
9
+ sep,
10
+ } from "node:path";
11
+ import { hash as blake3hash } from "blake3-wasm";
12
+ import { render, Text } from "ink";
13
+ import Spinner from "ink-spinner";
14
+ import { getType } from "mime";
15
+ import PQueue from "p-queue";
16
+ import prettyBytes from "pretty-bytes";
17
+ import React from "react";
18
+ import { fetchResult } from "../cfetch";
19
+ import { FatalError } from "../errors";
20
+ import { logger } from "../logger";
21
+ import {
22
+ BULK_UPLOAD_CONCURRENCY,
23
+ MAX_BUCKET_FILE_COUNT,
24
+ MAX_BUCKET_SIZE,
25
+ MAX_CHECK_MISSING_ATTEMPTS,
26
+ MAX_UPLOAD_ATTEMPTS,
27
+ } from "./constants";
28
+ import { pagesBetaWarning } from "./utils";
29
+ import type { UploadPayloadFile, YargsOptionsToInterface } from "./types";
30
+ import type { Argv } from "yargs";
31
+
32
+ type UploadArgs = YargsOptionsToInterface<typeof Options>;
33
+
34
+ export function Options(yargs: Argv) {
35
+ return yargs
36
+ .positional("directory", {
37
+ type: "string",
38
+ demandOption: true,
39
+ description: "The directory of static files to upload",
40
+ })
41
+ .options({
42
+ "output-manifest-path": {
43
+ type: "string",
44
+ description: "The name of the project you want to deploy to",
45
+ },
46
+ })
47
+ .epilogue(pagesBetaWarning);
48
+ }
49
+
50
+ export const Handler = async ({
51
+ directory,
52
+ outputManifestPath,
53
+ }: UploadArgs) => {
54
+ if (!directory) {
55
+ throw new FatalError("Must specify a directory.", 1);
56
+ }
57
+
58
+ if (!process.env.CF_PAGES_UPLOAD_JWT) {
59
+ throw new FatalError("No JWT given.", 1);
60
+ }
61
+
62
+ const manifest = await upload({
63
+ directory,
64
+ jwt: process.env.CF_PAGES_UPLOAD_JWT,
65
+ });
66
+
67
+ if (outputManifestPath) {
68
+ await mkdir(dirname(outputManifestPath), { recursive: true });
69
+ await writeFile(outputManifestPath, JSON.stringify(manifest));
70
+ }
71
+
72
+ logger.log(`✨ Upload complete!`);
73
+ };
74
+
75
+ export const upload = async (
76
+ args:
77
+ | {
78
+ directory: string;
79
+ jwt: string;
80
+ }
81
+ | { directory: string; accountId: string; projectName: string }
82
+ ) => {
83
+ async function fetchJwt(): Promise<string> {
84
+ if ("jwt" in args) {
85
+ return args.jwt;
86
+ } else {
87
+ return (
88
+ await fetchResult<{ jwt: string }>(
89
+ `/accounts/${args.accountId}/pages/projects/${args.projectName}/upload-token`
90
+ )
91
+ ).jwt;
92
+ }
93
+ }
94
+
95
+ type FileContainer = {
96
+ path: string;
97
+ contentType: string;
98
+ sizeInBytes: number;
99
+ hash: string;
100
+ };
101
+
102
+ const IGNORE_LIST = [
103
+ "_worker.js",
104
+ "_redirects",
105
+ "_headers",
106
+ "_routes.json",
107
+ ".DS_Store",
108
+ "node_modules",
109
+ ".git",
110
+ ];
111
+
112
+ const directory = resolve(args.directory);
113
+
114
+ // TODO(future): Use this to more efficiently load files in and speed up uploading
115
+ // Limit memory to 1 GB unless more is specified
116
+ // let maxMemory = 1_000_000_000;
117
+ // if (process.env.NODE_OPTIONS && (process.env.NODE_OPTIONS.includes('--max-old-space-size=') || process.env.NODE_OPTIONS.includes('--max_old_space_size='))) {
118
+ // const parsed = parser(process.env.NODE_OPTIONS);
119
+ // maxMemory = (parsed['max-old-space-size'] ? parsed['max-old-space-size'] : parsed['max_old_space_size']) * 1000 * 1000; // Turn MB into bytes
120
+ // }
121
+
122
+ const walk = async (
123
+ dir: string,
124
+ fileMap: Map<string, FileContainer> = new Map(),
125
+ startingDir: string = dir
126
+ ) => {
127
+ const files = await readdir(dir);
128
+
129
+ await Promise.all(
130
+ files.map(async (file) => {
131
+ const filepath = join(dir, file);
132
+ const filestat = await stat(filepath);
133
+
134
+ if (IGNORE_LIST.includes(file)) {
135
+ return;
136
+ }
137
+
138
+ if (filestat.isSymbolicLink()) {
139
+ return;
140
+ }
141
+
142
+ if (filestat.isDirectory()) {
143
+ fileMap = await walk(filepath, fileMap, startingDir);
144
+ } else {
145
+ const name = relative(startingDir, filepath).split(sep).join("/");
146
+
147
+ const fileContent = await readFile(filepath);
148
+
149
+ const base64Content = fileContent.toString("base64");
150
+ const extension = extname(basename(name)).substring(1);
151
+
152
+ if (filestat.size > 25 * 1024 * 1024) {
153
+ throw new FatalError(
154
+ `Error: Pages only supports files up to ${prettyBytes(
155
+ 25 * 1024 * 1024
156
+ )} in size\n${name} is ${prettyBytes(filestat.size)} in size`,
157
+ 1
158
+ );
159
+ }
160
+
161
+ // We don't want to hold the content in memory. We instead only want to read it when it's needed
162
+ fileMap.set(name, {
163
+ path: filepath,
164
+ contentType: getType(name) || "application/octet-stream",
165
+ sizeInBytes: filestat.size,
166
+ hash: blake3hash(base64Content + extension)
167
+ .toString("hex")
168
+ .slice(0, 32),
169
+ });
170
+ }
171
+ })
172
+ );
173
+
174
+ return fileMap;
175
+ };
176
+
177
+ const fileMap = await walk(directory);
178
+
179
+ if (fileMap.size > 20000) {
180
+ throw new FatalError(
181
+ `Error: Pages only supports up to 20,000 files in a deployment. Ensure you have specified your build output directory correctly.`,
182
+ 1
183
+ );
184
+ }
185
+
186
+ const files = [...fileMap.values()];
187
+
188
+ let jwt = await fetchJwt();
189
+
190
+ const start = Date.now();
191
+
192
+ let attempts = 0;
193
+ const getMissingHashes = async (): Promise<string[]> => {
194
+ try {
195
+ return await fetchResult<string[]>(`/pages/assets/check-missing`, {
196
+ method: "POST",
197
+ headers: {
198
+ "Content-Type": "application/json",
199
+ Authorization: `Bearer ${jwt}`,
200
+ },
201
+ body: JSON.stringify({
202
+ hashes: files.map(({ hash }) => hash),
203
+ }),
204
+ });
205
+ } catch (e) {
206
+ if (attempts < MAX_CHECK_MISSING_ATTEMPTS) {
207
+ // Exponential backoff, 1 second first time, then 2 second, then 4 second etc.
208
+ await new Promise((resolvePromise) =>
209
+ setTimeout(resolvePromise, Math.pow(2, attempts++) * 1000)
210
+ );
211
+
212
+ if ((e as { code: number }).code === 8000013) {
213
+ // Looks like the JWT expired, fetch another one
214
+ jwt = await fetchJwt();
215
+ }
216
+ return getMissingHashes();
217
+ } else {
218
+ throw e;
219
+ }
220
+ }
221
+ };
222
+ const missingHashes = await getMissingHashes();
223
+
224
+ const sortedFiles = files
225
+ .filter((file) => missingHashes.includes(file.hash))
226
+ .sort((a, b) => b.sizeInBytes - a.sizeInBytes);
227
+
228
+ // Start with a few buckets so small projects still get
229
+ // the benefit of multiple upload streams
230
+ const buckets: {
231
+ files: FileContainer[];
232
+ remainingSize: number;
233
+ }[] = new Array(BULK_UPLOAD_CONCURRENCY).fill(null).map(() => ({
234
+ files: [],
235
+ remainingSize: MAX_BUCKET_SIZE,
236
+ }));
237
+
238
+ let bucketOffset = 0;
239
+ for (const file of sortedFiles) {
240
+ let inserted = false;
241
+
242
+ for (let i = 0; i < buckets.length; i++) {
243
+ // Start at a different bucket for each new file
244
+ const bucket = buckets[(i + bucketOffset) % buckets.length];
245
+ if (
246
+ bucket.remainingSize >= file.sizeInBytes &&
247
+ bucket.files.length < MAX_BUCKET_FILE_COUNT
248
+ ) {
249
+ bucket.files.push(file);
250
+ bucket.remainingSize -= file.sizeInBytes;
251
+ inserted = true;
252
+ break;
253
+ }
254
+ }
255
+
256
+ if (!inserted) {
257
+ buckets.push({
258
+ files: [file],
259
+ remainingSize: MAX_BUCKET_SIZE - file.sizeInBytes,
260
+ });
261
+ }
262
+ bucketOffset++;
263
+ }
264
+
265
+ let counter = fileMap.size - sortedFiles.length;
266
+ const { rerender, unmount } = render(
267
+ <Progress done={counter} total={fileMap.size} />
268
+ );
269
+
270
+ const queue = new PQueue({ concurrency: BULK_UPLOAD_CONCURRENCY });
271
+
272
+ for (const bucket of buckets) {
273
+ // Don't upload empty buckets (can happen for tiny projects)
274
+ if (bucket.files.length === 0) continue;
275
+
276
+ attempts = 0;
277
+ const doUpload = async (): Promise<void> => {
278
+ // Populate the payload only when actually uploading (this is limited to 3 concurrent uploads at 50 MiB per bucket meaning we'd only load in a max of ~150 MiB)
279
+ // This is so we don't run out of memory trying to upload the files.
280
+ const payload: UploadPayloadFile[] = await Promise.all(
281
+ bucket.files.map(async (file) => ({
282
+ key: file.hash,
283
+ value: (await readFile(file.path)).toString("base64"),
284
+ metadata: {
285
+ contentType: file.contentType,
286
+ },
287
+ base64: true,
288
+ }))
289
+ );
290
+
291
+ try {
292
+ return await fetchResult(`/pages/assets/upload`, {
293
+ method: "POST",
294
+ headers: {
295
+ "Content-Type": "application/json",
296
+ Authorization: `Bearer ${jwt}`,
297
+ },
298
+ body: JSON.stringify(payload),
299
+ });
300
+ } catch (e) {
301
+ if (attempts < MAX_UPLOAD_ATTEMPTS) {
302
+ // Exponential backoff, 1 second first time, then 2 second, then 4 second etc.
303
+ await new Promise((resolvePromise) =>
304
+ setTimeout(resolvePromise, Math.pow(2, attempts++) * 1000)
305
+ );
306
+
307
+ if ((e as { code: number }).code === 8000013) {
308
+ // Looks like the JWT expired, fetch another one
309
+ jwt = await fetchJwt();
310
+ }
311
+ return doUpload();
312
+ } else {
313
+ throw e;
314
+ }
315
+ }
316
+ };
317
+
318
+ queue.add(() =>
319
+ doUpload().then(
320
+ () => {
321
+ counter += bucket.files.length;
322
+ rerender(<Progress done={counter} total={fileMap.size} />);
323
+ },
324
+ (error) => {
325
+ return Promise.reject(
326
+ new FatalError(
327
+ "Failed to upload files. Please try again.",
328
+ error.code || 1
329
+ )
330
+ );
331
+ }
332
+ )
333
+ );
334
+ }
335
+
336
+ await queue.onIdle();
337
+
338
+ unmount();
339
+
340
+ const uploadMs = Date.now() - start;
341
+
342
+ const skipped = fileMap.size - missingHashes.length;
343
+ const skippedMessage = skipped > 0 ? `(${skipped} already uploaded) ` : "";
344
+
345
+ logger.log(
346
+ `✨ Success! Uploaded ${
347
+ sortedFiles.length
348
+ } files ${skippedMessage}${formatTime(uploadMs)}\n`
349
+ );
350
+
351
+ const doUpsertHashes = async (): Promise<void> => {
352
+ try {
353
+ return await fetchResult(`/pages/assets/upsert-hashes`, {
354
+ method: "POST",
355
+ headers: {
356
+ "Content-Type": "application/json",
357
+ Authorization: `Bearer ${jwt}`,
358
+ },
359
+ body: JSON.stringify({
360
+ hashes: files.map(({ hash }) => hash),
361
+ }),
362
+ });
363
+ } catch (e) {
364
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, 1000));
365
+
366
+ if ((e as { code: number }).code === 8000013) {
367
+ // Looks like the JWT expired, fetch another one
368
+ jwt = await fetchJwt();
369
+ }
370
+
371
+ return await fetchResult(`/pages/assets/upsert-hashes`, {
372
+ method: "POST",
373
+ headers: {
374
+ "Content-Type": "application/json",
375
+ Authorization: `Bearer ${jwt}`,
376
+ },
377
+ body: JSON.stringify({
378
+ hashes: files.map(({ hash }) => hash),
379
+ }),
380
+ });
381
+ }
382
+ };
383
+
384
+ try {
385
+ await doUpsertHashes();
386
+ } catch {
387
+ logger.warn(
388
+ "Failed to update file hashes. Every upload appeared to succeed for this deployment, but you might need to re-upload for future deployments. This shouldn't have any impact other than slowing the upload speed of your next deployment."
389
+ );
390
+ }
391
+
392
+ return Object.fromEntries(
393
+ [...fileMap.entries()].map(([fileName, file]) => [
394
+ `/${fileName}`,
395
+ file.hash,
396
+ ])
397
+ );
398
+ };
399
+
400
+ function formatTime(duration: number) {
401
+ return `(${(duration / 1000).toFixed(2)} sec)`;
402
+ }
403
+
404
+ function Progress({ done, total }: { done: number; total: number }) {
405
+ return (
406
+ <>
407
+ <Text>
408
+ <Spinner type="earth" />
409
+ {` Uploading... (${done}/${total})\n`}
410
+ </Text>
411
+ </>
412
+ );
413
+ }
@@ -0,0 +1,12 @@
1
+ import type { BuildResult } from "esbuild";
2
+
3
+ export const RUNNING_BUILDERS: BuildResult[] = [];
4
+
5
+ export const CLEANUP_CALLBACKS: (() => void)[] = [];
6
+ export const CLEANUP = () => {
7
+ CLEANUP_CALLBACKS.forEach((callback) => callback());
8
+ RUNNING_BUILDERS.forEach((builder) => builder.stop?.());
9
+ };
10
+
11
+ export const pagesBetaWarning =
12
+ "🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";