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.
- package/README.md +16 -2
- package/bin/wrangler.js +31 -11
- package/miniflare-dist/index.mjs +1340 -330
- package/package.json +40 -13
- package/src/__tests__/access.test.ts +25 -0
- package/src/__tests__/api-dev.test.ts +238 -0
- package/src/__tests__/api-devregistry.test.js +64 -0
- package/src/__tests__/api.test.ts +80 -0
- package/src/__tests__/configuration.test.ts +683 -470
- package/src/__tests__/d1.test.ts +123 -0
- package/src/__tests__/delete.test.ts +263 -0
- package/src/__tests__/deployments.test.ts +105 -0
- package/src/__tests__/dev.test.tsx +453 -70
- package/src/__tests__/dialogs.test.tsx +40 -0
- package/src/__tests__/generate.test.ts +235 -0
- package/src/__tests__/get-host-from-url.test.ts +16 -0
- package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
- package/src/__tests__/helpers/mock-cfetch.ts +92 -26
- package/src/__tests__/helpers/mock-console.ts +11 -2
- package/src/__tests__/helpers/mock-dialogs.ts +1 -1
- package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
- package/src/__tests__/helpers/mock-known-routes.ts +7 -0
- package/src/__tests__/helpers/mock-kv.ts +1 -1
- package/src/__tests__/helpers/mock-oauth-flow.ts +74 -53
- package/src/__tests__/helpers/mock-process.ts +8 -13
- package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
- package/src/__tests__/helpers/msw/handlers/deployments.ts +59 -0
- package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
- package/src/__tests__/helpers/msw/handlers/oauth.ts +31 -0
- package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
- package/src/__tests__/helpers/msw/handlers/script.ts +75 -0
- package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
- package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
- package/src/__tests__/helpers/msw/index.ts +21 -0
- package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
- package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +5 -0
- package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
- package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
- package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
- package/src/__tests__/index.test.ts +103 -97
- package/src/__tests__/init.test.ts +1484 -382
- package/src/__tests__/jest.setup.ts +88 -5
- package/src/__tests__/kv.test.ts +655 -540
- package/src/__tests__/logout.test.ts +47 -0
- package/src/__tests__/metrics.test.ts +95 -45
- package/src/__tests__/middleware.scheduled.test.ts +145 -0
- package/src/__tests__/middleware.test.ts +750 -0
- package/src/__tests__/pages-deployment-tail.test.ts +884 -0
- package/src/__tests__/pages.test.ts +1301 -119
- package/src/__tests__/paths.test.ts +39 -0
- package/src/__tests__/publish.test.ts +2266 -1180
- package/src/__tests__/pubsub.test.ts +211 -88
- package/src/__tests__/queues.test.ts +522 -0
- package/src/__tests__/r2.test.ts +363 -203
- package/src/__tests__/secret.test.ts +261 -59
- package/src/__tests__/tail.test.ts +352 -151
- package/src/__tests__/test-old-node-version.js +3 -3
- package/src/__tests__/tsconfig-sanity.ts +12 -0
- package/src/__tests__/tsconfig.json +8 -0
- package/src/__tests__/tsconfig.tsbuildinfo +1 -0
- package/src/__tests__/type-generation.test.ts +233 -0
- package/src/__tests__/user.test.ts +59 -73
- package/src/__tests__/utils-collectKeyValues.test.ts +47 -0
- package/src/__tests__/validate-dev-props.test.ts +56 -0
- package/src/__tests__/whoami.test.tsx +114 -66
- package/src/__tests__/worker-namespace.test.ts +154 -155
- package/src/api/dev.ts +170 -38
- package/src/api/index.ts +1 -0
- package/src/bundle-reporter.tsx +41 -2
- package/src/bundle.ts +668 -81
- package/src/cfetch/index.ts +40 -3
- package/src/cfetch/internal.ts +144 -32
- package/src/cli.ts +4 -3
- package/src/config/config.ts +27 -3
- package/src/config/environment.ts +131 -7
- package/src/config/index.ts +128 -8
- package/src/config/validation-helpers.ts +19 -6
- package/src/config/validation.ts +411 -14
- package/src/config-cache.ts +3 -2
- package/src/create-worker-preview.ts +31 -6
- package/src/create-worker-upload-form.ts +84 -22
- package/src/d1/backups.tsx +223 -0
- package/src/d1/constants.ts +2 -0
- package/src/d1/create.tsx +63 -0
- package/src/d1/delete.tsx +56 -0
- package/src/d1/execute.tsx +352 -0
- package/src/d1/formatTimeAgo.ts +14 -0
- package/src/d1/index.ts +101 -0
- package/src/d1/list.tsx +48 -0
- package/src/d1/migrations/apply.tsx +196 -0
- package/src/d1/migrations/create.tsx +77 -0
- package/src/d1/migrations/helpers.ts +147 -0
- package/src/d1/migrations/index.tsx +3 -0
- package/src/d1/migrations/list.tsx +79 -0
- package/src/d1/migrations/options.ts +18 -0
- package/src/d1/options.ts +22 -0
- package/src/d1/types.tsx +24 -0
- package/src/d1/utils.ts +48 -0
- package/src/delete.ts +108 -0
- package/src/deployments.ts +83 -0
- package/src/deprecated/index.ts +145 -0
- package/src/dev/dev-vars.ts +11 -8
- package/src/dev/dev.tsx +220 -58
- package/src/dev/get-local-persistence-path.tsx +31 -0
- package/src/dev/local.tsx +740 -181
- package/src/dev/remote.tsx +340 -123
- package/src/dev/start-server.ts +543 -0
- package/src/dev/use-esbuild.ts +61 -7
- package/src/dev/validate-dev-props.ts +31 -0
- package/src/dev-registry.tsx +201 -0
- package/src/dev.tsx +614 -258
- package/src/dialogs.tsx +41 -8
- package/src/{worker-namespace.ts → dispatch-namespace.ts} +23 -22
- package/src/docs/index.ts +97 -0
- package/src/entry.ts +3 -2
- package/src/environment-variables/factory.ts +88 -0
- package/src/environment-variables/misc-variables.ts +30 -0
- package/src/generate/index.ts +300 -0
- package/src/git-client.ts +100 -7
- package/src/index.tsx +249 -1560
- package/src/init.ts +626 -150
- package/src/inspect.ts +137 -32
- package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
- package/src/is-interactive.ts +4 -0
- package/src/jest.d.ts +4 -0
- package/src/{kv.ts → kv/helpers.ts} +3 -3
- package/src/kv/index.ts +596 -0
- package/src/logger.ts +27 -8
- package/src/metrics/index.ts +1 -0
- package/src/metrics/metrics-config.ts +4 -4
- package/src/metrics/metrics-dispatcher.ts +1 -0
- package/src/metrics/metrics-usage-headers.ts +24 -0
- package/src/metrics/send-event.ts +13 -8
- package/src/miniflare-cli/assets.ts +172 -465
- package/src/miniflare-cli/index.ts +149 -24
- package/src/miniflare-cli/tsconfig.json +9 -0
- package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
- package/src/miniflare-cli/types.ts +11 -0
- package/src/module-collection.ts +12 -3
- package/src/pages/build.tsx +108 -42
- package/src/pages/constants.ts +11 -0
- package/src/pages/deployment-tails.tsx +284 -0
- package/src/pages/deployments.tsx +9 -31
- package/src/pages/dev.tsx +417 -108
- package/src/pages/errors.ts +66 -0
- package/src/pages/functions/buildPlugin.ts +86 -84
- package/src/pages/functions/buildWorker.ts +121 -106
- package/src/pages/functions/routes-consolidation.test.ts +250 -0
- package/src/pages/functions/routes-consolidation.ts +73 -0
- package/src/pages/functions/routes-transformation.test.ts +282 -0
- package/src/pages/functions/routes-transformation.ts +115 -0
- package/src/pages/functions/routes-validation.test.ts +403 -0
- package/src/pages/functions/routes-validation.ts +202 -0
- package/src/pages/functions/tsconfig.json +8 -0
- package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
- package/src/pages/functions.tsx +82 -0
- package/src/pages/hash.tsx +13 -0
- package/src/pages/index.tsx +73 -55
- package/src/pages/projects.tsx +9 -3
- package/src/pages/prompt-select-project.tsx +31 -0
- package/src/pages/publish.tsx +183 -60
- package/src/pages/types.ts +1 -0
- package/src/pages/upload.tsx +107 -56
- package/src/pages/utils.ts +13 -2
- package/src/paths.ts +49 -4
- package/src/proxy.ts +375 -157
- package/src/publish/index.ts +270 -0
- package/src/{publish.ts → publish/publish.ts} +431 -188
- package/src/pubsub/pubsub-commands.tsx +4 -3
- package/src/queues/cli/commands/consumer/add.ts +71 -0
- package/src/queues/cli/commands/consumer/index.ts +22 -0
- package/src/queues/cli/commands/consumer/remove.ts +38 -0
- package/src/queues/cli/commands/create.ts +25 -0
- package/src/queues/cli/commands/delete.ts +26 -0
- package/src/queues/cli/commands/index.ts +36 -0
- package/src/queues/cli/commands/list.ts +25 -0
- package/src/queues/client.ts +135 -0
- package/src/queues/utils.ts +18 -0
- package/src/r2/constants.ts +4 -0
- package/src/r2/helpers.ts +131 -0
- package/src/r2/index.ts +289 -0
- package/src/routes.ts +140 -0
- package/src/secret/index.ts +379 -0
- package/src/sites.tsx +3 -3
- package/src/tail/createTail.ts +379 -0
- package/src/tail/filters.ts +4 -6
- package/src/tail/index.ts +199 -284
- package/src/tail/printing.ts +43 -3
- package/src/tsconfig-sanity.ts +16 -0
- package/src/type-generation.ts +181 -0
- package/src/user/access.ts +68 -0
- package/src/user/auth-variables.ts +113 -0
- package/src/user/choose-account.tsx +1 -1
- package/src/user/index.ts +0 -1
- package/src/user/user.tsx +116 -49
- package/src/utils/collectKeyValues.ts +14 -0
- package/src/whoami.tsx +80 -2
- package/src/worker.ts +69 -9
- package/src/yargs-types.ts +24 -0
- package/src/zones.ts +124 -9
- package/templates/__tests__/pages-dev-util.test.ts +128 -0
- package/templates/__tests__/tsconfig-sanity.ts +12 -0
- package/templates/__tests__/tsconfig.json +8 -0
- package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
- package/templates/d1-beta-facade.js +205 -0
- package/templates/facade.d.ts +14 -0
- package/templates/first-party-worker-module-facade.ts +19 -0
- package/templates/format-dev-errors.ts +33 -0
- package/templates/init-tests/test-jest-new-worker.js +23 -0
- package/templates/init-tests/test-vitest-new-worker.js +24 -0
- package/templates/init-tests/test-vitest-new-worker.ts +25 -0
- package/templates/middleware/common.ts +62 -0
- package/templates/middleware/loader-modules.ts +82 -0
- package/templates/middleware/loader-sw.ts +233 -0
- package/templates/middleware/middleware-miniflare3-json-error.ts +20 -0
- package/templates/middleware/middleware-pretty-error.ts +40 -0
- package/templates/middleware/middleware-scheduled.ts +15 -0
- package/templates/new-worker.js +1 -1
- package/templates/pages-dev-pipeline.ts +31 -0
- package/templates/pages-dev-util.ts +52 -0
- package/templates/pages-shim.ts +4 -1
- package/templates/pages-template-plugin.ts +21 -5
- package/templates/pages-template-worker.ts +37 -9
- package/templates/{static-asset-facade.js → serve-static-assets.ts} +23 -7
- package/templates/service-bindings-module-facade.js +54 -0
- package/templates/service-bindings-sw-facade.js +42 -0
- package/templates/tsconfig-sanity.ts +11 -0
- package/templates/tsconfig.init.json +106 -0
- package/templates/tsconfig.json +5 -102
- package/templates/tsconfig.tsbuildinfo +1 -0
- package/wrangler-dist/cli.d.ts +621 -42
- package/wrangler-dist/cli.js +144441 -102606
- package/src/environment-variables.ts +0 -35
- package/src/faye-websocket.d.ts +0 -6
- package/src/generate.ts +0 -33
- package/src/miniflare-cli/enum-keys.ts +0 -17
- package/src/preview.ts +0 -31
- package/src/r2.ts +0 -50
- package/src/user/env-vars.ts +0 -46
package/src/pages/publish.tsx
CHANGED
|
@@ -16,22 +16,20 @@ import * as metrics from "../metrics";
|
|
|
16
16
|
import { requireAuth } from "../user";
|
|
17
17
|
import { buildFunctions } from "./build";
|
|
18
18
|
import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
|
|
19
|
+
import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
|
|
20
|
+
import { validateRoutes } from "./functions/routes-validation";
|
|
19
21
|
import { listProjects } from "./projects";
|
|
22
|
+
import { promptSelectProject } from "./prompt-select-project";
|
|
20
23
|
import { upload } from "./upload";
|
|
21
24
|
import { pagesBetaWarning } from "./utils";
|
|
22
|
-
import type {
|
|
23
|
-
import type {
|
|
24
|
-
|
|
25
|
-
type
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
branch?: string;
|
|
29
|
-
"commit-hash"?: string;
|
|
30
|
-
"commit-message"?: string;
|
|
31
|
-
"commit-dirty"?: boolean;
|
|
32
|
-
};
|
|
25
|
+
import type { YargsOptionsToInterface } from "../yargs-types";
|
|
26
|
+
import type { PagesConfigCache } from "./types";
|
|
27
|
+
import type { Project, Deployment } from "@cloudflare/types";
|
|
28
|
+
import type { Argv } from "yargs";
|
|
29
|
+
|
|
30
|
+
type PublishArgs = YargsOptionsToInterface<typeof Options>;
|
|
33
31
|
|
|
34
|
-
export function Options(yargs: Argv)
|
|
32
|
+
export function Options(yargs: Argv) {
|
|
35
33
|
return yargs
|
|
36
34
|
.positional("directory", {
|
|
37
35
|
type: "string",
|
|
@@ -60,6 +58,10 @@ export function Options(yargs: Argv): Argv<PublishArgs> {
|
|
|
60
58
|
description:
|
|
61
59
|
"Whether or not the workspace should be considered dirty for this deployment",
|
|
62
60
|
},
|
|
61
|
+
"skip-caching": {
|
|
62
|
+
type: "boolean",
|
|
63
|
+
description: "Skip asset caching which speeds up builds",
|
|
64
|
+
},
|
|
63
65
|
config: {
|
|
64
66
|
describe: "Pages does not support wrangler.toml",
|
|
65
67
|
type: "string",
|
|
@@ -76,8 +78,9 @@ export const Handler = async ({
|
|
|
76
78
|
commitHash,
|
|
77
79
|
commitMessage,
|
|
78
80
|
commitDirty,
|
|
81
|
+
skipCaching,
|
|
79
82
|
config: wranglerConfig,
|
|
80
|
-
}:
|
|
83
|
+
}: PublishArgs) => {
|
|
81
84
|
if (wranglerConfig) {
|
|
82
85
|
throw new FatalError("Pages does not support wrangler.toml", 1);
|
|
83
86
|
}
|
|
@@ -132,24 +135,7 @@ export const Handler = async ({
|
|
|
132
135
|
|
|
133
136
|
switch (existingOrNew) {
|
|
134
137
|
case "existing": {
|
|
135
|
-
projectName = await
|
|
136
|
-
const { unmount } = render(
|
|
137
|
-
<>
|
|
138
|
-
<Text>Select a project:</Text>
|
|
139
|
-
<SelectInput
|
|
140
|
-
items={projects.map((project) => ({
|
|
141
|
-
key: project.name,
|
|
142
|
-
label: project.name,
|
|
143
|
-
value: project,
|
|
144
|
-
}))}
|
|
145
|
-
onSelect={async (selected) => {
|
|
146
|
-
resolve(selected.value.name);
|
|
147
|
-
unmount();
|
|
148
|
-
}}
|
|
149
|
-
/>
|
|
150
|
-
</>
|
|
151
|
-
);
|
|
152
|
-
});
|
|
138
|
+
projectName = await promptSelectProject({ accountId });
|
|
153
139
|
break;
|
|
154
140
|
}
|
|
155
141
|
case "new": {
|
|
@@ -205,7 +191,6 @@ export const Handler = async ({
|
|
|
205
191
|
}
|
|
206
192
|
|
|
207
193
|
// We infer git info by default is not passed in
|
|
208
|
-
|
|
209
194
|
let isGitDir = true;
|
|
210
195
|
try {
|
|
211
196
|
execSync(`git rev-parse --is-inside-work-tree`, {
|
|
@@ -249,24 +234,96 @@ export const Handler = async ({
|
|
|
249
234
|
}
|
|
250
235
|
}
|
|
251
236
|
|
|
237
|
+
let _headers: string | undefined,
|
|
238
|
+
_redirects: string | undefined,
|
|
239
|
+
_routesGenerated: string | undefined,
|
|
240
|
+
_routesCustom: string | undefined,
|
|
241
|
+
_workerJS: string | undefined;
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
_headers = readFileSync(join(directory, "_headers"), "utf-8");
|
|
245
|
+
} catch {}
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
_redirects = readFileSync(join(directory, "_redirects"), "utf-8");
|
|
249
|
+
} catch {}
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
/**
|
|
253
|
+
* Developers can specify a custom _routes.json file, for projects with Pages
|
|
254
|
+
* Functions or projects in Advanced Mode
|
|
255
|
+
*/
|
|
256
|
+
_routesCustom = readFileSync(join(directory, "_routes.json"), "utf-8");
|
|
257
|
+
} catch {}
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
_workerJS = readFileSync(join(directory, "_worker.js"), "utf-8");
|
|
261
|
+
} catch {}
|
|
262
|
+
|
|
263
|
+
// Grab the bindings from the API, we need these for shims and other such hacky inserts
|
|
264
|
+
const project = await fetchResult<Project>(
|
|
265
|
+
`/accounts/${accountId}/pages/projects/${projectName}`
|
|
266
|
+
);
|
|
267
|
+
let isProduction = true;
|
|
268
|
+
if (branch) {
|
|
269
|
+
isProduction = project.production_branch === branch;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Evaluate if this is an Advanced Mode or Pages Functions project. If Advanced Mode, we'll
|
|
274
|
+
* go ahead and upload `_worker.js` as is, but if Pages Functions, we need to attempt to build
|
|
275
|
+
* Functions first and exit if it failed
|
|
276
|
+
*/
|
|
252
277
|
let builtFunctions: string | undefined = undefined;
|
|
253
278
|
const functionsDirectory = join(cwd(), "functions");
|
|
254
|
-
|
|
279
|
+
const routesOutputPath = !existsSync(join(directory, "_routes.json"))
|
|
280
|
+
? join(tmpdir(), `_routes-${Math.random()}.json`)
|
|
281
|
+
: undefined;
|
|
282
|
+
|
|
283
|
+
// Routing configuration displayed in the Functions tab of a deployment in Dash
|
|
284
|
+
let filepathRoutingConfig: string | undefined;
|
|
285
|
+
|
|
286
|
+
if (!_workerJS && existsSync(functionsDirectory)) {
|
|
255
287
|
const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.js`);
|
|
288
|
+
const outputConfigPath = join(
|
|
289
|
+
tmpdir(),
|
|
290
|
+
`functions-filepath-routing-config-${Math.random()}.json`
|
|
291
|
+
);
|
|
256
292
|
|
|
257
|
-
|
|
258
|
-
buildFunctions({
|
|
293
|
+
try {
|
|
294
|
+
await buildFunctions({
|
|
259
295
|
outfile,
|
|
296
|
+
outputConfigPath,
|
|
260
297
|
functionsDirectory,
|
|
261
|
-
onEnd: () =>
|
|
298
|
+
onEnd: () => {},
|
|
262
299
|
buildOutputDirectory: dirname(outfile),
|
|
263
|
-
|
|
264
|
-
|
|
300
|
+
routesOutputPath,
|
|
301
|
+
local: false,
|
|
302
|
+
d1Databases: Object.keys(
|
|
303
|
+
project.deployment_configs[isProduction ? "production" : "preview"]
|
|
304
|
+
.d1_databases ?? {}
|
|
305
|
+
),
|
|
306
|
+
});
|
|
265
307
|
|
|
266
|
-
|
|
308
|
+
builtFunctions = readFileSync(outfile, "utf-8");
|
|
309
|
+
filepathRoutingConfig = readFileSync(outputConfigPath, "utf-8");
|
|
310
|
+
} catch (e) {
|
|
311
|
+
if (e instanceof FunctionsNoRoutesError) {
|
|
312
|
+
logger.warn(
|
|
313
|
+
getFunctionsNoRoutesWarning(functionsDirectory, "skipping")
|
|
314
|
+
);
|
|
315
|
+
} else {
|
|
316
|
+
throw e;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
267
319
|
}
|
|
268
320
|
|
|
269
|
-
const manifest = await upload({
|
|
321
|
+
const manifest = await upload({
|
|
322
|
+
directory,
|
|
323
|
+
accountId,
|
|
324
|
+
projectName,
|
|
325
|
+
skipCaching: skipCaching ?? false,
|
|
326
|
+
});
|
|
270
327
|
|
|
271
328
|
const formData = new FormData();
|
|
272
329
|
|
|
@@ -288,34 +345,100 @@ export const Handler = async ({
|
|
|
288
345
|
formData.append("commit_dirty", commitDirty);
|
|
289
346
|
}
|
|
290
347
|
|
|
291
|
-
let _headers: string | undefined,
|
|
292
|
-
_redirects: string | undefined,
|
|
293
|
-
_workerJS: string | undefined;
|
|
294
|
-
|
|
295
|
-
try {
|
|
296
|
-
_headers = readFileSync(join(directory, "_headers"), "utf-8");
|
|
297
|
-
} catch {}
|
|
298
|
-
|
|
299
|
-
try {
|
|
300
|
-
_redirects = readFileSync(join(directory, "_redirects"), "utf-8");
|
|
301
|
-
} catch {}
|
|
302
|
-
|
|
303
|
-
try {
|
|
304
|
-
_workerJS = readFileSync(join(directory, "_worker.js"), "utf-8");
|
|
305
|
-
} catch {}
|
|
306
|
-
|
|
307
348
|
if (_headers) {
|
|
308
349
|
formData.append("_headers", new File([_headers], "_headers"));
|
|
350
|
+
logger.log(`✨ Uploading _headers`);
|
|
309
351
|
}
|
|
310
352
|
|
|
311
353
|
if (_redirects) {
|
|
312
354
|
formData.append("_redirects", new File([_redirects], "_redirects"));
|
|
355
|
+
logger.log(`✨ Uploading _redirects`);
|
|
313
356
|
}
|
|
314
357
|
|
|
315
|
-
if (
|
|
316
|
-
formData.append(
|
|
317
|
-
|
|
358
|
+
if (filepathRoutingConfig) {
|
|
359
|
+
formData.append(
|
|
360
|
+
"functions-filepath-routing-config.json",
|
|
361
|
+
new File(
|
|
362
|
+
[filepathRoutingConfig],
|
|
363
|
+
"functions-filepath-routing-config.json"
|
|
364
|
+
)
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Advanced Mode
|
|
370
|
+
* https://developers.cloudflare.com/pages/platform/functions/#advanced-mode
|
|
371
|
+
*
|
|
372
|
+
* When using a _worker.js file, the entire /functions directory is ignored
|
|
373
|
+
* – this includes its routing and middleware characteristics.
|
|
374
|
+
*/
|
|
375
|
+
if (_workerJS) {
|
|
318
376
|
formData.append("_worker.js", new File([_workerJS], "_worker.js"));
|
|
377
|
+
logger.log(`✨ Uploading _worker.js`);
|
|
378
|
+
|
|
379
|
+
if (_routesCustom) {
|
|
380
|
+
// user provided a custom _routes.json file
|
|
381
|
+
try {
|
|
382
|
+
const routesCustomJSON = JSON.parse(_routesCustom);
|
|
383
|
+
validateRoutes(routesCustomJSON, join(directory, "_routes.json"));
|
|
384
|
+
|
|
385
|
+
formData.append(
|
|
386
|
+
"_routes.json",
|
|
387
|
+
new File([_routesCustom], "_routes.json")
|
|
388
|
+
);
|
|
389
|
+
logger.log(`✨ Uploading _routes.json`);
|
|
390
|
+
logger.warn(
|
|
391
|
+
`_routes.json is an experimental feature and is subject to change. Please use with care.`
|
|
392
|
+
);
|
|
393
|
+
} catch (err) {
|
|
394
|
+
if (err instanceof FatalError) {
|
|
395
|
+
throw err;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Pages Functions
|
|
403
|
+
* https://developers.cloudflare.com/pages/platform/functions/
|
|
404
|
+
*/
|
|
405
|
+
if (builtFunctions && !_workerJS) {
|
|
406
|
+
// if Functions were build successfully, proceed to uploading the build file
|
|
407
|
+
formData.append("_worker.js", new File([builtFunctions], "_worker.js"));
|
|
408
|
+
logger.log(`✨ Uploading Functions`);
|
|
409
|
+
|
|
410
|
+
if (_routesCustom) {
|
|
411
|
+
// user provided a custom _routes.json file
|
|
412
|
+
try {
|
|
413
|
+
const routesCustomJSON = JSON.parse(_routesCustom);
|
|
414
|
+
validateRoutes(routesCustomJSON, join(directory, "_routes.json"));
|
|
415
|
+
|
|
416
|
+
formData.append(
|
|
417
|
+
"_routes.json",
|
|
418
|
+
new File([_routesCustom], "_routes.json")
|
|
419
|
+
);
|
|
420
|
+
logger.log(`✨ Uploading _routes.json`);
|
|
421
|
+
logger.warn(
|
|
422
|
+
`_routes.json is an experimental feature and is subject to change. Please use with care.`
|
|
423
|
+
);
|
|
424
|
+
} catch (err) {
|
|
425
|
+
if (err instanceof FatalError) {
|
|
426
|
+
throw err;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
} else if (routesOutputPath) {
|
|
430
|
+
// no custom _routes.json file found, so fallback to the generated one
|
|
431
|
+
try {
|
|
432
|
+
_routesGenerated = readFileSync(routesOutputPath, "utf-8");
|
|
433
|
+
|
|
434
|
+
if (_routesGenerated) {
|
|
435
|
+
formData.append(
|
|
436
|
+
"_routes.json",
|
|
437
|
+
new File([_routesGenerated], "_routes.json")
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
} catch {}
|
|
441
|
+
}
|
|
319
442
|
}
|
|
320
443
|
|
|
321
444
|
const deploymentResponse = await fetchResult<Deployment>(
|
|
@@ -334,5 +457,5 @@ export const Handler = async ({
|
|
|
334
457
|
logger.log(
|
|
335
458
|
`✨ Deployment complete! Take a peek over at ${deploymentResponse.url}`
|
|
336
459
|
);
|
|
337
|
-
await metrics.sendMetricsEvent("
|
|
460
|
+
await metrics.sendMetricsEvent("create pages deployment");
|
|
338
461
|
};
|
package/src/pages/types.ts
CHANGED
package/src/pages/upload.tsx
CHANGED
|
@@ -1,39 +1,34 @@
|
|
|
1
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";
|
|
2
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
12
3
|
import { render, Text } from "ink";
|
|
13
4
|
import Spinner from "ink-spinner";
|
|
14
5
|
import { getType } from "mime";
|
|
6
|
+
import { Minimatch } from "minimatch";
|
|
15
7
|
import PQueue from "p-queue";
|
|
16
8
|
import prettyBytes from "pretty-bytes";
|
|
17
9
|
import React from "react";
|
|
18
10
|
import { fetchResult } from "../cfetch";
|
|
19
11
|
import { FatalError } from "../errors";
|
|
12
|
+
import isInteractive from "../is-interactive";
|
|
20
13
|
import { logger } from "../logger";
|
|
21
14
|
import {
|
|
15
|
+
MAX_ASSET_COUNT,
|
|
16
|
+
MAX_ASSET_SIZE,
|
|
22
17
|
BULK_UPLOAD_CONCURRENCY,
|
|
23
18
|
MAX_BUCKET_FILE_COUNT,
|
|
24
19
|
MAX_BUCKET_SIZE,
|
|
20
|
+
MAX_CHECK_MISSING_ATTEMPTS,
|
|
25
21
|
MAX_UPLOAD_ATTEMPTS,
|
|
26
22
|
} from "./constants";
|
|
23
|
+
import { hashFile } from "./hash";
|
|
27
24
|
import { pagesBetaWarning } from "./utils";
|
|
25
|
+
import type { YargsOptionsToInterface } from "../yargs-types";
|
|
28
26
|
import type { UploadPayloadFile } from "./types";
|
|
29
|
-
import type {
|
|
27
|
+
import type { Argv } from "yargs";
|
|
30
28
|
|
|
31
|
-
type UploadArgs =
|
|
32
|
-
directory: string;
|
|
33
|
-
"output-manifest-path"?: string;
|
|
34
|
-
};
|
|
29
|
+
type UploadArgs = YargsOptionsToInterface<typeof Options>;
|
|
35
30
|
|
|
36
|
-
export function Options(yargs: Argv)
|
|
31
|
+
export function Options(yargs: Argv) {
|
|
37
32
|
return yargs
|
|
38
33
|
.positional("directory", {
|
|
39
34
|
type: "string",
|
|
@@ -45,6 +40,10 @@ export function Options(yargs: Argv): Argv<UploadArgs> {
|
|
|
45
40
|
type: "string",
|
|
46
41
|
description: "The name of the project you want to deploy to",
|
|
47
42
|
},
|
|
43
|
+
"skip-caching": {
|
|
44
|
+
type: "boolean",
|
|
45
|
+
description: "Skip asset caching which speeds up builds",
|
|
46
|
+
},
|
|
48
47
|
})
|
|
49
48
|
.epilogue(pagesBetaWarning);
|
|
50
49
|
}
|
|
@@ -52,7 +51,8 @@ export function Options(yargs: Argv): Argv<UploadArgs> {
|
|
|
52
51
|
export const Handler = async ({
|
|
53
52
|
directory,
|
|
54
53
|
outputManifestPath,
|
|
55
|
-
|
|
54
|
+
skipCaching,
|
|
55
|
+
}: UploadArgs) => {
|
|
56
56
|
if (!directory) {
|
|
57
57
|
throw new FatalError("Must specify a directory.", 1);
|
|
58
58
|
}
|
|
@@ -64,6 +64,7 @@ export const Handler = async ({
|
|
|
64
64
|
const manifest = await upload({
|
|
65
65
|
directory,
|
|
66
66
|
jwt: process.env.CF_PAGES_UPLOAD_JWT,
|
|
67
|
+
skipCaching: skipCaching ?? false,
|
|
67
68
|
});
|
|
68
69
|
|
|
69
70
|
if (outputManifestPath) {
|
|
@@ -79,8 +80,14 @@ export const upload = async (
|
|
|
79
80
|
| {
|
|
80
81
|
directory: string;
|
|
81
82
|
jwt: string;
|
|
83
|
+
skipCaching: boolean;
|
|
84
|
+
}
|
|
85
|
+
| {
|
|
86
|
+
directory: string;
|
|
87
|
+
accountId: string;
|
|
88
|
+
projectName: string;
|
|
89
|
+
skipCaching: boolean;
|
|
82
90
|
}
|
|
83
|
-
| { directory: string; accountId: string; projectName: string }
|
|
84
91
|
) => {
|
|
85
92
|
async function fetchJwt(): Promise<string> {
|
|
86
93
|
if ("jwt" in args) {
|
|
@@ -105,10 +112,12 @@ export const upload = async (
|
|
|
105
112
|
"_worker.js",
|
|
106
113
|
"_redirects",
|
|
107
114
|
"_headers",
|
|
108
|
-
".
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
|
|
115
|
+
"_routes.json",
|
|
116
|
+
"functions",
|
|
117
|
+
"**/.DS_Store",
|
|
118
|
+
"**/node_modules",
|
|
119
|
+
"**/.git",
|
|
120
|
+
].map((pattern) => new Minimatch(pattern));
|
|
112
121
|
|
|
113
122
|
const directory = resolve(args.directory);
|
|
114
123
|
|
|
@@ -130,10 +139,13 @@ export const upload = async (
|
|
|
130
139
|
await Promise.all(
|
|
131
140
|
files.map(async (file) => {
|
|
132
141
|
const filepath = join(dir, file);
|
|
142
|
+
const relativeFilepath = relative(startingDir, filepath);
|
|
133
143
|
const filestat = await stat(filepath);
|
|
134
144
|
|
|
135
|
-
|
|
136
|
-
|
|
145
|
+
for (const minimatch of IGNORE_LIST) {
|
|
146
|
+
if (minimatch.match(relativeFilepath)) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
137
149
|
}
|
|
138
150
|
|
|
139
151
|
if (filestat.isSymbolicLink()) {
|
|
@@ -143,17 +155,12 @@ export const upload = async (
|
|
|
143
155
|
if (filestat.isDirectory()) {
|
|
144
156
|
fileMap = await walk(filepath, fileMap, startingDir);
|
|
145
157
|
} else {
|
|
146
|
-
const name =
|
|
147
|
-
|
|
148
|
-
const fileContent = await readFile(filepath);
|
|
158
|
+
const name = relativeFilepath.split(sep).join("/");
|
|
149
159
|
|
|
150
|
-
|
|
151
|
-
const extension = extname(basename(name)).substring(1);
|
|
152
|
-
|
|
153
|
-
if (filestat.size > 25 * 1024 * 1024) {
|
|
160
|
+
if (filestat.size > MAX_ASSET_SIZE) {
|
|
154
161
|
throw new FatalError(
|
|
155
162
|
`Error: Pages only supports files up to ${prettyBytes(
|
|
156
|
-
|
|
163
|
+
MAX_ASSET_SIZE
|
|
157
164
|
)} in size\n${name} is ${prettyBytes(filestat.size)} in size`,
|
|
158
165
|
1
|
|
159
166
|
);
|
|
@@ -164,9 +171,7 @@ export const upload = async (
|
|
|
164
171
|
path: filepath,
|
|
165
172
|
contentType: getType(name) || "application/octet-stream",
|
|
166
173
|
sizeInBytes: filestat.size,
|
|
167
|
-
hash:
|
|
168
|
-
.toString("hex")
|
|
169
|
-
.slice(0, 32),
|
|
174
|
+
hash: hashFile(filepath),
|
|
170
175
|
});
|
|
171
176
|
}
|
|
172
177
|
})
|
|
@@ -177,9 +182,9 @@ export const upload = async (
|
|
|
177
182
|
|
|
178
183
|
const fileMap = await walk(directory);
|
|
179
184
|
|
|
180
|
-
if (fileMap.size >
|
|
185
|
+
if (fileMap.size > MAX_ASSET_COUNT) {
|
|
181
186
|
throw new FatalError(
|
|
182
|
-
`Error: Pages only supports up to
|
|
187
|
+
`Error: Pages only supports up to ${MAX_ASSET_COUNT.toLocaleString()} files in a deployment. Ensure you have specified your build output directory correctly.`,
|
|
183
188
|
1
|
|
184
189
|
);
|
|
185
190
|
}
|
|
@@ -190,19 +195,42 @@ export const upload = async (
|
|
|
190
195
|
|
|
191
196
|
const start = Date.now();
|
|
192
197
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
{
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
"Content-Type": "application/json",
|
|
199
|
-
Authorization: `Bearer ${jwt}`,
|
|
200
|
-
},
|
|
201
|
-
body: JSON.stringify({
|
|
202
|
-
hashes: files.map(({ hash }) => hash),
|
|
203
|
-
}),
|
|
198
|
+
let attempts = 0;
|
|
199
|
+
const getMissingHashes = async (skipCaching: boolean): Promise<string[]> => {
|
|
200
|
+
if (skipCaching) {
|
|
201
|
+
console.debug("Force skipping cache");
|
|
202
|
+
return files.map(({ hash }) => hash);
|
|
204
203
|
}
|
|
205
|
-
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
return await fetchResult<string[]>(`/pages/assets/check-missing`, {
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: {
|
|
209
|
+
"Content-Type": "application/json",
|
|
210
|
+
Authorization: `Bearer ${jwt}`,
|
|
211
|
+
},
|
|
212
|
+
body: JSON.stringify({
|
|
213
|
+
hashes: files.map(({ hash }) => hash),
|
|
214
|
+
}),
|
|
215
|
+
});
|
|
216
|
+
} catch (e) {
|
|
217
|
+
if (attempts < MAX_CHECK_MISSING_ATTEMPTS) {
|
|
218
|
+
// Exponential backoff, 1 second first time, then 2 second, then 4 second etc.
|
|
219
|
+
await new Promise((resolvePromise) =>
|
|
220
|
+
setTimeout(resolvePromise, Math.pow(2, attempts++) * 1000)
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
if ((e as { code: number }).code === 8000013) {
|
|
224
|
+
// Looks like the JWT expired, fetch another one
|
|
225
|
+
jwt = await fetchJwt();
|
|
226
|
+
}
|
|
227
|
+
return getMissingHashes(skipCaching);
|
|
228
|
+
} else {
|
|
229
|
+
throw e;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
const missingHashes = await getMissingHashes(args.skipCaching);
|
|
206
234
|
|
|
207
235
|
const sortedFiles = files
|
|
208
236
|
.filter((file) => missingHashes.includes(file.hash))
|
|
@@ -256,7 +284,7 @@ export const upload = async (
|
|
|
256
284
|
// Don't upload empty buckets (can happen for tiny projects)
|
|
257
285
|
if (bucket.files.length === 0) continue;
|
|
258
286
|
|
|
259
|
-
|
|
287
|
+
attempts = 0;
|
|
260
288
|
const doUpload = async (): Promise<void> => {
|
|
261
289
|
// 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)
|
|
262
290
|
// This is so we don't run out of memory trying to upload the files.
|
|
@@ -272,7 +300,8 @@ export const upload = async (
|
|
|
272
300
|
);
|
|
273
301
|
|
|
274
302
|
try {
|
|
275
|
-
|
|
303
|
+
console.debug("POST /pages/assets/upload");
|
|
304
|
+
const res = await fetchResult(`/pages/assets/upload`, {
|
|
276
305
|
method: "POST",
|
|
277
306
|
headers: {
|
|
278
307
|
"Content-Type": "application/json",
|
|
@@ -280,25 +309,28 @@ export const upload = async (
|
|
|
280
309
|
},
|
|
281
310
|
body: JSON.stringify(payload),
|
|
282
311
|
});
|
|
312
|
+
console.debug("result:", res);
|
|
283
313
|
} catch (e) {
|
|
284
314
|
if (attempts < MAX_UPLOAD_ATTEMPTS) {
|
|
315
|
+
console.debug("failed:", e, "retrying...");
|
|
285
316
|
// Exponential backoff, 1 second first time, then 2 second, then 4 second etc.
|
|
286
317
|
await new Promise((resolvePromise) =>
|
|
287
318
|
setTimeout(resolvePromise, Math.pow(2, attempts++) * 1000)
|
|
288
319
|
);
|
|
289
320
|
|
|
290
|
-
if ((e as { code: number }).code === 8000013) {
|
|
321
|
+
if ((e as { code: number }).code === 8000013 || isJwtExpired(jwt)) {
|
|
291
322
|
// Looks like the JWT expired, fetch another one
|
|
292
323
|
jwt = await fetchJwt();
|
|
293
324
|
}
|
|
294
325
|
return doUpload();
|
|
295
326
|
} else {
|
|
327
|
+
console.debug("failed:", e);
|
|
296
328
|
throw e;
|
|
297
329
|
}
|
|
298
330
|
}
|
|
299
331
|
};
|
|
300
332
|
|
|
301
|
-
queue.add(() =>
|
|
333
|
+
void queue.add(() =>
|
|
302
334
|
doUpload().then(
|
|
303
335
|
() => {
|
|
304
336
|
counter += bucket.files.length;
|
|
@@ -307,7 +339,9 @@ export const upload = async (
|
|
|
307
339
|
(error) => {
|
|
308
340
|
return Promise.reject(
|
|
309
341
|
new FatalError(
|
|
310
|
-
|
|
342
|
+
`Failed to upload files. Please try again. Error: ${JSON.stringify(
|
|
343
|
+
error
|
|
344
|
+
)})`,
|
|
311
345
|
error.code || 1
|
|
312
346
|
)
|
|
313
347
|
);
|
|
@@ -346,7 +380,7 @@ export const upload = async (
|
|
|
346
380
|
} catch (e) {
|
|
347
381
|
await new Promise((resolvePromise) => setTimeout(resolvePromise, 1000));
|
|
348
382
|
|
|
349
|
-
if ((e as { code: number }).code === 8000013) {
|
|
383
|
+
if ((e as { code: number }).code === 8000013 || isJwtExpired(jwt)) {
|
|
350
384
|
// Looks like the JWT expired, fetch another one
|
|
351
385
|
jwt = await fetchJwt();
|
|
352
386
|
}
|
|
@@ -380,6 +414,23 @@ export const upload = async (
|
|
|
380
414
|
);
|
|
381
415
|
};
|
|
382
416
|
|
|
417
|
+
// Decode and check that the current JWT has not expired
|
|
418
|
+
function isJwtExpired(token: string): boolean | undefined {
|
|
419
|
+
try {
|
|
420
|
+
const decodedJwt = JSON.parse(
|
|
421
|
+
Buffer.from(token.split(".")[1], "base64").toString()
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
const dateNow = new Date().getTime() / 1000;
|
|
425
|
+
|
|
426
|
+
return decodedJwt.exp <= dateNow;
|
|
427
|
+
} catch (e) {
|
|
428
|
+
if (e instanceof Error) {
|
|
429
|
+
throw new Error(`Invalid token: ${e.message}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
383
434
|
function formatTime(duration: number) {
|
|
384
435
|
return `(${(duration / 1000).toFixed(2)} sec)`;
|
|
385
436
|
}
|
|
@@ -388,7 +439,7 @@ function Progress({ done, total }: { done: number; total: number }) {
|
|
|
388
439
|
return (
|
|
389
440
|
<>
|
|
390
441
|
<Text>
|
|
391
|
-
<Spinner type="earth" />
|
|
442
|
+
{isInteractive() ? <Spinner type="earth" /> : null}
|
|
392
443
|
{` Uploading... (${done}/${total})\n`}
|
|
393
444
|
</Text>
|
|
394
445
|
</>
|
package/src/pages/utils.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BundleResult } from "../bundle";
|
|
2
2
|
|
|
3
|
-
export const RUNNING_BUILDERS:
|
|
3
|
+
export const RUNNING_BUILDERS: BundleResult[] = [];
|
|
4
4
|
|
|
5
5
|
export const CLEANUP_CALLBACKS: (() => void)[] = [];
|
|
6
6
|
export const CLEANUP = () => {
|
|
@@ -10,3 +10,14 @@ export const CLEANUP = () => {
|
|
|
10
10
|
|
|
11
11
|
export const pagesBetaWarning =
|
|
12
12
|
"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";
|
|
13
|
+
|
|
14
|
+
export function isUrl(maybeUrl?: string): maybeUrl is string {
|
|
15
|
+
if (!maybeUrl) return false;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
new URL(maybeUrl);
|
|
19
|
+
return true;
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|