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
|
@@ -1,35 +1,64 @@
|
|
|
1
1
|
import { existsSync, lstatSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
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 {
|
|
7
|
-
import type {
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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:
|
|
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
|
-
|
|
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(
|
|
54
|
+
const url = new URL(miniflareRequest.url);
|
|
31
55
|
url.host = `localhost:${options.proxyPort}`;
|
|
32
|
-
|
|
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(
|
|
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:
|
|
287
|
-
|
|
88
|
+
log: Logger,
|
|
89
|
+
tre: boolean
|
|
90
|
+
): Promise<typeof fetch> {
|
|
288
91
|
// Defer importing miniflare until we really need it
|
|
289
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
|
313
|
-
|
|
129
|
+
let headers: ParsedHeaders | undefined;
|
|
130
|
+
if (existsSync(headersFile)) {
|
|
131
|
+
const contents = readFileSync(headersFile, "utf-8");
|
|
132
|
+
headers = parseHeaders(contents);
|
|
133
|
+
}
|
|
314
134
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
451
|
-
|
|
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
|
-
|
|
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
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
516
|
-
|
|
517
|
-
if (value) deconstructedResponse.headers.set(name, value);
|
|
235
|
+
return { body, contentType };
|
|
236
|
+
},
|
|
518
237
|
});
|
|
519
238
|
};
|
|
520
239
|
|
|
521
|
-
return async (input:
|
|
240
|
+
return (async (input: TreRequestInfo, init?: TreRequestInit) => {
|
|
522
241
|
const request = new Request(input, init);
|
|
523
|
-
|
|
524
|
-
|
|
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
|
|
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
|
);
|