wrangler 0.0.0-e151223 → 0.0.0-e1b8ac4

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 (156) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +27 -14
  3. package/bin/wrangler.js +49 -5
  4. package/kv-asset-handler.js +1 -0
  5. package/miniflare-dist/index.mjs +4886 -0
  6. package/package.json +57 -28
  7. package/pages/functions/buildPlugin.ts +105 -0
  8. package/pages/functions/buildWorker.ts +93 -4
  9. package/pages/functions/filepath-routing.test.ts +223 -36
  10. package/pages/functions/filepath-routing.ts +78 -120
  11. package/pages/functions/routes.ts +13 -17
  12. package/pages/functions/template-plugin.ts +147 -0
  13. package/pages/functions/template-worker.ts +24 -17
  14. package/src/__tests__/config-cache.test.ts +36 -0
  15. package/src/__tests__/configuration.test.ts +3548 -0
  16. package/src/__tests__/dev.test.tsx +1049 -65
  17. package/src/__tests__/guess-worker-format.test.ts +85 -0
  18. package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
  19. package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
  20. package/src/__tests__/helpers/mock-account-id.ts +26 -8
  21. package/src/__tests__/helpers/mock-bin.ts +36 -0
  22. package/src/__tests__/helpers/mock-cfetch.ts +48 -10
  23. package/src/__tests__/helpers/mock-console.ts +39 -8
  24. package/src/__tests__/helpers/mock-dialogs.ts +99 -11
  25. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  26. package/src/__tests__/helpers/mock-istty.ts +27 -0
  27. package/src/__tests__/helpers/mock-kv.ts +9 -16
  28. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  29. package/src/__tests__/helpers/mock-process.ts +24 -0
  30. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  31. package/src/__tests__/helpers/mock-web-socket.ts +29 -0
  32. package/src/__tests__/helpers/run-in-tmp.ts +10 -0
  33. package/src/__tests__/helpers/run-wrangler.ts +5 -3
  34. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  35. package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
  36. package/src/__tests__/https-options.test.ts +130 -0
  37. package/src/__tests__/index.test.ts +160 -309
  38. package/src/__tests__/init.test.ts +1992 -0
  39. package/src/__tests__/jest.setup.ts +79 -6
  40. package/src/__tests__/kv.test.ts +651 -280
  41. package/src/__tests__/logger.test.ts +141 -0
  42. package/src/__tests__/package-manager.test.ts +353 -0
  43. package/src/__tests__/pages.test.ts +754 -0
  44. package/src/__tests__/parse.test.ts +330 -0
  45. package/src/__tests__/publish.test.ts +5671 -959
  46. package/src/__tests__/r2.test.ts +233 -0
  47. package/src/__tests__/route.test.ts +45 -0
  48. package/src/__tests__/secret.test.ts +362 -91
  49. package/src/__tests__/tail.test.ts +690 -0
  50. package/src/__tests__/user.test.ts +132 -0
  51. package/src/__tests__/whoami.test.tsx +60 -7
  52. package/src/abort.d.ts +3 -0
  53. package/src/bundle.ts +204 -0
  54. package/src/cfetch/index.ts +28 -18
  55. package/src/cfetch/internal.ts +99 -29
  56. package/src/cli.ts +6 -2
  57. package/src/config/README.md +107 -0
  58. package/src/config/config.ts +243 -0
  59. package/src/config/diagnostics.ts +80 -0
  60. package/src/config/environment.ts +420 -0
  61. package/src/config/index.ts +225 -0
  62. package/src/config/validation-helpers.ts +584 -0
  63. package/src/config/validation.ts +1580 -0
  64. package/src/config-cache.ts +45 -0
  65. package/src/create-worker-preview.ts +185 -0
  66. package/src/create-worker-upload-form.ts +254 -0
  67. package/src/dev/dev-vars.ts +36 -0
  68. package/src/dev/dev.tsx +399 -0
  69. package/src/dev/local.tsx +321 -0
  70. package/src/dev/remote.tsx +242 -0
  71. package/src/dev/use-esbuild.ts +112 -0
  72. package/src/dialogs.tsx +64 -6
  73. package/src/durable.ts +102 -0
  74. package/src/entry.ts +322 -0
  75. package/src/environment-variables.ts +35 -0
  76. package/src/errors.ts +11 -0
  77. package/src/generate-auth-url.ts +33 -0
  78. package/src/generate-random-state.ts +16 -0
  79. package/src/https-options.ts +123 -0
  80. package/src/index.tsx +1835 -988
  81. package/src/inspect.ts +80 -14
  82. package/src/intl-polyfill.d.ts +139 -0
  83. package/src/{kv.tsx → kv.ts} +199 -67
  84. package/src/logger.ts +73 -0
  85. package/src/miniflare-cli/README.md +30 -0
  86. package/src/miniflare-cli/enum-keys.ts +17 -0
  87. package/src/miniflare-cli/index.ts +41 -0
  88. package/src/miniflare-cli/request-context.ts +40 -0
  89. package/src/module-collection.ts +226 -31
  90. package/src/open-in-browser.ts +17 -0
  91. package/src/package-manager.ts +174 -0
  92. package/src/pages.tsx +1132 -84
  93. package/src/parse.ts +222 -0
  94. package/src/proxy.ts +176 -70
  95. package/src/publish.ts +624 -308
  96. package/src/r2.ts +50 -0
  97. package/src/selfsigned.d.ts +29 -0
  98. package/src/sites.tsx +185 -57
  99. package/src/tail/filters.ts +279 -0
  100. package/src/tail/index.ts +299 -0
  101. package/src/tail/printing.ts +67 -0
  102. package/src/update-check.ts +19 -0
  103. package/src/user.tsx +349 -166
  104. package/src/whoami.tsx +5 -3
  105. package/src/{api/worker.ts → worker.ts} +65 -39
  106. package/src/zones.ts +73 -0
  107. package/templates/gitignore +171 -0
  108. package/templates/new-worker-scheduled.js +17 -0
  109. package/templates/new-worker-scheduled.ts +32 -0
  110. package/templates/new-worker.js +15 -0
  111. package/templates/new-worker.ts +30 -0
  112. package/{static-asset-facade.js → templates/static-asset-facade.js} +3 -7
  113. package/templates/tsconfig.json +105 -0
  114. package/wrangler-dist/cli.js +76649 -75066
  115. package/src/__tests__/helpers/mock-user.ts +0 -27
  116. package/src/__tests__/logout.test.ts +0 -50
  117. package/src/api/form_data.ts +0 -134
  118. package/src/api/preview.ts +0 -128
  119. package/src/config.ts +0 -641
  120. package/src/dev.tsx +0 -834
  121. package/src/npm-installer.ts +0 -20
  122. package/src/tail.tsx +0 -73
  123. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  124. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  125. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  126. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  127. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  128. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  129. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  130. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  131. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  132. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  133. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  134. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  135. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  136. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  137. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  138. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  139. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  140. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  141. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  142. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  143. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  144. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  145. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  146. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  147. package/vendor/wrangler-mime/LICENSE +0 -21
  148. package/vendor/wrangler-mime/Mime.js +0 -97
  149. package/vendor/wrangler-mime/README.md +0 -187
  150. package/vendor/wrangler-mime/cli.js +0 -46
  151. package/vendor/wrangler-mime/index.js +0 -4
  152. package/vendor/wrangler-mime/lite.js +0 -4
  153. package/vendor/wrangler-mime/package.json +0 -52
  154. package/vendor/wrangler-mime/types/other.js +0 -1
  155. package/vendor/wrangler-mime/types/standard.js +0 -1
  156. package/wrangler-dist/cli.js.map +0 -7
@@ -1,26 +1,18 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
- import * as acorn from "acorn";
4
- import * as acornWalk from "acorn-walk";
5
- import { transform } from "esbuild";
3
+ import { build } from "esbuild";
6
4
  import { toUrlPath } from "../../src/paths";
7
5
  import type { UrlPath } from "../../src/paths";
8
- import type { Config, RouteConfig } from "./routes";
9
- import type { ExportNamedDeclaration, Identifier } from "estree";
10
-
11
- type Arguments = {
12
- baseDir: string;
13
- baseURL: UrlPath;
14
- };
15
-
16
- type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD";
17
- type RouteKey = [UrlPath, Method] | [UrlPath, undefined] | [UrlPath];
6
+ import type { HTTPMethod, RouteConfig } from "./routes";
18
7
 
19
8
  export async function generateConfigFromFileTree({
20
9
  baseDir,
21
10
  baseURL,
22
- }: Arguments) {
23
- let routeEntries: [RouteKey, RouteConfig][] = [];
11
+ }: {
12
+ baseDir: string;
13
+ baseURL: UrlPath;
14
+ }) {
15
+ let routeEntries: RouteConfig[] = [];
24
16
 
25
17
  if (!baseURL.startsWith("/")) {
26
18
  baseURL = `/${baseURL}` as UrlPath;
@@ -33,137 +25,103 @@ export async function generateConfigFromFileTree({
33
25
  await forEachFile(baseDir, async (filepath) => {
34
26
  const ext = path.extname(filepath);
35
27
  if (/^\.(mjs|js|ts|tsx|jsx)$/.test(ext)) {
36
- // transform the code to ensure we're working with vanilla JS + ESM
37
- const { code } = await transform(await fs.readFile(filepath, "utf-8"), {
38
- loader: ext === ".ts" ? "ts" : "js",
39
- });
40
-
41
- // parse each file into an AST and search for module exports that match "onRequest" and friends
42
- const ast = acorn.parse(code, {
43
- ecmaVersion: "latest",
44
- sourceType: "module",
28
+ const { metafile } = await build({
29
+ metafile: true,
30
+ write: false,
31
+ bundle: false,
32
+ entryPoints: [path.resolve(filepath)],
45
33
  });
46
- acornWalk.simple(ast, {
47
- ExportNamedDeclaration(_node: unknown) {
48
- // This dynamic cast assumes that the AST generated by acornWalk will generate nodes that
49
- // are compatible with the eslint AST nodes.
50
- const node = _node as ExportNamedDeclaration;
51
-
52
- // this is an array because multiple things can be exported from a single statement
53
- // i.e. `export {foo, bar}` or `export const foo = "f", bar = "b"`
54
- const exportNames: string[] = [];
55
-
56
- if (node.declaration) {
57
- const declaration = node.declaration;
58
-
59
- // `export async function onRequest() {...}`
60
- if (declaration.type === "FunctionDeclaration" && declaration.id) {
61
- exportNames.push(declaration.id.name);
62
- }
63
-
64
- // `export const onRequestGet = () => {}, onRequestPost = () => {}`
65
- if (declaration.type === "VariableDeclaration") {
66
- exportNames.push(
67
- ...declaration.declarations.map(
68
- (variableDeclarator) =>
69
- (variableDeclarator.id as unknown as Identifier).name
70
- )
71
- );
72
- }
34
+ const exportNames: string[] = [];
35
+ if (metafile) {
36
+ for (const output in metafile?.outputs) {
37
+ exportNames.push(...metafile.outputs[output].exports);
38
+ }
39
+ }
40
+ for (const exportName of exportNames) {
41
+ const [match, method = ""] = (exportName.match(
42
+ /^onRequest(Get|Post|Put|Patch|Delete|Options|Head)?$/
43
+ ) ?? []) as (string | undefined)[];
44
+
45
+ if (match) {
46
+ const basename = path.basename(filepath).slice(0, -ext.length);
47
+
48
+ const isIndexFile = basename === "index";
49
+ // TODO: deprecate _middleware_ in favor of _middleware
50
+ const isMiddlewareFile =
51
+ basename === "_middleware" || basename === "_middleware_";
52
+
53
+ let routePath = path
54
+ .relative(baseDir, filepath)
55
+ .slice(0, -ext.length);
56
+ let mountPath = path.dirname(routePath);
57
+
58
+ if (isIndexFile || isMiddlewareFile) {
59
+ routePath = path.dirname(routePath);
73
60
  }
74
61
 
75
- // `export {foo, bar}`
76
- if (node.specifiers.length) {
77
- exportNames.push(
78
- ...node.specifiers.map(
79
- (exportSpecifier) =>
80
- (exportSpecifier.exported as unknown as Identifier).name
81
- )
82
- );
62
+ if (routePath === ".") {
63
+ routePath = "";
83
64
  }
84
-
85
- for (const exportName of exportNames) {
86
- const [match, method] =
87
- exportName.match(
88
- /^onRequest(Get|Post|Put|Patch|Delete|Options|Head)?$/
89
- ) ?? [];
90
-
91
- if (match) {
92
- const basename = path.basename(filepath).slice(0, -ext.length);
93
-
94
- const isIndexFile = basename === "index";
95
- // TODO: deprecate _middleware_ in favor of _middleware
96
- const isMiddlewareFile =
97
- basename === "_middleware" || basename === "_middleware_";
98
-
99
- let routePath = path
100
- .relative(baseDir, filepath)
101
- .slice(0, -ext.length);
102
-
103
- if (isIndexFile || isMiddlewareFile) {
104
- routePath = path.dirname(routePath);
105
- }
106
-
107
- if (routePath === ".") {
108
- routePath = "";
109
- }
110
-
111
- routePath = `${baseURL}/${routePath}`;
112
-
113
- routePath = routePath.replace(/\[\[(.+)]]/g, ":$1*"); // transform [[id]] => :id*
114
- routePath = routePath.replace(/\[(.+)]/g, ":$1"); // transform [id] => :id
115
-
116
- const routeUrlPath = toUrlPath(routePath);
117
- const routeKey: RouteKey = [
118
- routeUrlPath,
119
- method ? (method.toUpperCase() as Method) : undefined,
120
- ];
121
-
122
- routeEntries.push([
123
- routeKey,
124
- {
125
- [isMiddlewareFile ? "middleware" : "module"]: [
126
- `${path.relative(baseDir, filepath)}:${exportName}`,
127
- ],
128
- },
129
- ]);
130
- }
65
+ if (mountPath === ".") {
66
+ mountPath = "";
131
67
  }
132
- },
133
- });
68
+
69
+ routePath = `${baseURL}/${routePath}`;
70
+ mountPath = `${baseURL}/${mountPath}`;
71
+
72
+ routePath = routePath.replace(/\[\[([^\]]+)\]\]/g, ":$1*"); // transform [[id]] => :id*
73
+ routePath = routePath.replaceAll(/\[([^\]]+)\]/g, ":$1"); // transform [id] => :id
74
+ mountPath = mountPath.replace(/\[\[([^\]]+)\]\]/g, ":$1*"); // transform [[id]] => :id*
75
+ mountPath = mountPath.replaceAll(/\[([^\]]+)\]/g, ":$1"); // transform [id] => :id
76
+
77
+ // These are used as module specifiers so UrlPaths are okay to use even on Windows
78
+ const modulePath = toUrlPath(path.relative(baseDir, filepath));
79
+
80
+ const routeEntry: RouteConfig = {
81
+ routePath: toUrlPath(routePath),
82
+ mountPath: toUrlPath(mountPath),
83
+ method: method.toUpperCase() as HTTPMethod,
84
+ [isMiddlewareFile ? "middleware" : "module"]: [
85
+ `${modulePath}:${exportName}`,
86
+ ],
87
+ };
88
+
89
+ routeEntries.push(routeEntry);
90
+ }
91
+ }
134
92
  }
135
93
  });
136
-
137
94
  // Combine together any routes (index routes) which contain both a module and a middleware
138
95
  routeEntries = routeEntries.reduce(
139
- (acc: typeof routeEntries, [routePath, routeHandler]) => {
96
+ (acc: typeof routeEntries, { routePath, ...rest }) => {
140
97
  const existingRouteEntry = acc.find(
141
- (routeEntry) => routeEntry[0] === routePath
98
+ (routeEntry) =>
99
+ routeEntry.routePath === routePath &&
100
+ routeEntry.method === rest.method
142
101
  );
143
102
  if (existingRouteEntry !== undefined) {
144
- existingRouteEntry[1] = {
145
- ...existingRouteEntry[1],
146
- ...routeHandler,
147
- };
103
+ Object.assign(existingRouteEntry, rest);
148
104
  } else {
149
- acc.push([routePath, routeHandler]);
105
+ acc.push({ routePath, ...rest });
150
106
  }
151
107
  return acc;
152
108
  },
153
109
  []
154
110
  );
155
111
 
156
- routeEntries.sort(([a], [b]) => compareRoutes(a, b));
112
+ routeEntries.sort((a, b) => compareRoutes(a, b));
157
113
 
158
- return { routes: Object.fromEntries(routeEntries) } as Config;
114
+ return {
115
+ routes: routeEntries,
116
+ };
159
117
  }
160
118
 
161
119
  // Ensure routes are produced in order of precedence so that
162
120
  // more specific routes aren't occluded from matching due to
163
121
  // less specific routes appearing first in the route list.
164
122
  export function compareRoutes(
165
- [routePathA, methodA]: RouteKey,
166
- [routePathB, methodB]: RouteKey
123
+ { routePath: routePathA, method: methodA }: RouteConfig,
124
+ { routePath: routePathB, method: methodB }: RouteConfig
167
125
  ) {
168
126
  function parseRoutePath(routePath: UrlPath): string[] {
169
127
  return routePath.slice(1).split("/").filter(Boolean);
@@ -1,6 +1,5 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { toUrlPath } from "../../src/paths";
4
3
  import { isValidIdentifier, normalizeIdentifier } from "./identifiers";
5
4
  import type { UrlPath } from "../../src/paths";
6
5
 
@@ -22,21 +21,21 @@ export function isHTTPMethod(
22
21
 
23
22
  export type RoutesCollection = Array<{
24
23
  routePath: UrlPath;
25
- methods: HTTPMethod[];
24
+ mountPath: UrlPath;
25
+ method?: HTTPMethod;
26
26
  modules: string[];
27
27
  middlewares: string[];
28
28
  }>;
29
29
 
30
30
  export type Config = {
31
- routes?: RoutesConfig;
31
+ routes?: RouteConfig[];
32
32
  schedules?: unknown;
33
33
  };
34
34
 
35
- export type RoutesConfig = {
36
- [route: UrlPath]: RouteConfig;
37
- };
38
-
39
35
  export type RouteConfig = {
36
+ routePath: UrlPath;
37
+ mountPath: UrlPath;
38
+ method?: HTTPMethod;
40
39
  middleware?: string | string[];
41
40
  module?: string | string[];
42
41
  };
@@ -116,16 +115,12 @@ export function parseConfig(config: Config, baseDir: string) {
116
115
  });
117
116
  }
118
117
 
119
- for (const [route, props] of Object.entries(config.routes ?? {})) {
120
- let [_methods, routePath] = route.split(" ");
121
- if (!routePath) {
122
- routePath = _methods;
123
- _methods = "";
124
- }
125
-
118
+ for (const { routePath, mountPath, method, ...props } of config.routes ??
119
+ []) {
126
120
  routes.push({
127
- routePath: toUrlPath(routePath),
128
- methods: _methods.split("|").filter(isHTTPMethod),
121
+ routePath,
122
+ mountPath,
123
+ method,
129
124
  middlewares: parseModuleIdentifiers(props.middleware),
130
125
  modules: parseModuleIdentifiers(props.module),
131
126
  });
@@ -150,7 +145,8 @@ export const routes = [
150
145
  .map(
151
146
  (route) => ` {
152
147
  routePath: "${route.routePath}",
153
- methods: ${JSON.stringify(route.methods)},
148
+ mountPath: "${route.mountPath}",
149
+ method: "${route.method}",
154
150
  middlewares: [${route.middlewares.join(", ")}],
155
151
  modules: [${route.modules.join(", ")}],
156
152
  },`
@@ -0,0 +1,147 @@
1
+ import { match } from "path-to-regexp";
2
+ import type { HTTPMethod } from "./routes";
3
+
4
+ /* TODO: Grab these from @cloudflare/workers-types instead */
5
+ type Params<P extends string = string> = Record<P, string | string[]>;
6
+
7
+ type EventContext<Env, P extends string, Data> = {
8
+ request: Request;
9
+ functionPath: string;
10
+ waitUntil: (promise: Promise<unknown>) => void;
11
+ next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
12
+ env: Env & { ASSETS: { fetch: typeof fetch } };
13
+ params: Params<P>;
14
+ data: Data;
15
+ };
16
+
17
+ type EventPluginContext<Env, P extends string, Data, PluginArgs> = {
18
+ request: Request;
19
+ functionPath: string;
20
+ waitUntil: (promise: Promise<unknown>) => void;
21
+ next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
22
+ env: Env & { ASSETS: { fetch: typeof fetch } };
23
+ params: Params<P>;
24
+ data: Data;
25
+ pluginArgs: PluginArgs;
26
+ };
27
+
28
+ declare type PagesFunction<
29
+ Env = unknown,
30
+ P extends string = string,
31
+ Data extends Record<string, unknown> = Record<string, unknown>
32
+ > = (context: EventContext<Env, P, Data>) => Response | Promise<Response>;
33
+
34
+ declare type PagesPluginFunction<
35
+ Env = unknown,
36
+ P extends string = string,
37
+ Data extends Record<string, unknown> = Record<string, unknown>,
38
+ PluginArgs = unknown
39
+ > = (
40
+ context: EventPluginContext<Env, P, Data, PluginArgs>
41
+ ) => Response | Promise<Response>;
42
+ /* end @cloudflare/workers-types */
43
+
44
+ type RouteHandler = {
45
+ routePath: string;
46
+ mountPath: string;
47
+ method?: HTTPMethod;
48
+ modules: PagesFunction[];
49
+ middlewares: PagesFunction[];
50
+ };
51
+
52
+ // inject `routes` via ESBuild
53
+ declare const routes: RouteHandler[];
54
+
55
+ function* executeRequest(request: Request, relativePathname: string) {
56
+ // First, iterate through the routes (backwards) and execute "middlewares" on partial route matches
57
+ for (const route of [...routes].reverse()) {
58
+ if (route.method && route.method !== request.method) {
59
+ continue;
60
+ }
61
+
62
+ const routeMatcher = match(route.routePath, { end: false });
63
+ const mountMatcher = match(route.mountPath, { end: false });
64
+ const matchResult = routeMatcher(relativePathname);
65
+ const mountMatchResult = mountMatcher(relativePathname);
66
+ if (matchResult && mountMatchResult) {
67
+ for (const handler of route.middlewares.flat()) {
68
+ yield {
69
+ handler,
70
+ params: matchResult.params as Params,
71
+ path: mountMatchResult.path,
72
+ };
73
+ }
74
+ }
75
+ }
76
+
77
+ // Then look for the first exact route match and execute its "modules"
78
+ for (const route of routes) {
79
+ if (route.method && route.method !== request.method) {
80
+ continue;
81
+ }
82
+
83
+ const routeMatcher = match(route.routePath, { end: true });
84
+ const mountMatcher = match(route.mountPath, { end: false });
85
+ const matchResult = routeMatcher(relativePathname);
86
+ const mountMatchResult = mountMatcher(relativePathname);
87
+ if (matchResult && mountMatchResult && route.modules.length) {
88
+ for (const handler of route.modules.flat()) {
89
+ yield {
90
+ handler,
91
+ params: matchResult.params as Params,
92
+ path: matchResult.path,
93
+ };
94
+ }
95
+ break;
96
+ }
97
+ }
98
+ }
99
+
100
+ export default function (pluginArgs) {
101
+ const onRequest: PagesPluginFunction = async (workerContext) => {
102
+ let { request } = workerContext;
103
+ const { env, next, data } = workerContext;
104
+
105
+ const url = new URL(request.url);
106
+ const relativePathname = `/${
107
+ url.pathname.split(workerContext.functionPath)[1] || ""
108
+ }`.replace(/^\/\//, "/");
109
+
110
+ const handlerIterator = executeRequest(request, relativePathname);
111
+ const pluginNext = async (input?: RequestInfo, init?: RequestInit) => {
112
+ if (input !== undefined) {
113
+ request = new Request(input, init);
114
+ }
115
+
116
+ const result = handlerIterator.next();
117
+ // Note we can't use `!result.done` because this doesn't narrow to the correct type
118
+ if (result.done === false) {
119
+ const { handler, params, path } = result.value;
120
+ const context = {
121
+ request,
122
+ functionPath: workerContext.functionPath + path,
123
+ next: pluginNext,
124
+ params,
125
+ data,
126
+ pluginArgs,
127
+ env,
128
+ waitUntil: workerContext.waitUntil.bind(workerContext),
129
+ };
130
+
131
+ const response = await handler(context);
132
+
133
+ // https://fetch.spec.whatwg.org/#null-body-status
134
+ return new Response(
135
+ [101, 204, 205, 304].includes(response.status) ? null : response.body,
136
+ { ...response, headers: new Headers(response.headers) }
137
+ );
138
+ } else {
139
+ return next();
140
+ }
141
+ };
142
+
143
+ return pluginNext();
144
+ };
145
+
146
+ return onRequest;
147
+ }
@@ -6,6 +6,7 @@ type Params<P extends string = string> = Record<P, string | string[]>;
6
6
 
7
7
  type EventContext<Env, P extends string, Data> = {
8
8
  request: Request;
9
+ functionPath: string;
9
10
  waitUntil: (promise: Promise<unknown>) => void;
10
11
  next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
11
12
  env: Env & { ASSETS: { fetch: typeof fetch } };
@@ -22,7 +23,8 @@ declare type PagesFunction<
22
23
 
23
24
  type RouteHandler = {
24
25
  routePath: string;
25
- methods: HTTPMethod[];
26
+ mountPath: string;
27
+ method?: HTTPMethod;
26
28
  modules: PagesFunction[];
27
29
  middlewares: PagesFunction[];
28
30
  };
@@ -42,25 +44,25 @@ type WorkerContext = {
42
44
  waitUntil: (promise: Promise<unknown>) => void;
43
45
  };
44
46
 
45
- function* executeRequest(request: Request, _env: FetchEnv) {
47
+ function* executeRequest(request: Request) {
46
48
  const requestPath = new URL(request.url).pathname;
47
49
 
48
50
  // First, iterate through the routes (backwards) and execute "middlewares" on partial route matches
49
51
  for (const route of [...routes].reverse()) {
50
- if (
51
- route.methods.length &&
52
- !route.methods.includes(request.method as HTTPMethod)
53
- ) {
52
+ if (route.method && route.method !== request.method) {
54
53
  continue;
55
54
  }
56
55
 
57
56
  const routeMatcher = match(route.routePath, { end: false });
57
+ const mountMatcher = match(route.mountPath, { end: false });
58
58
  const matchResult = routeMatcher(requestPath);
59
- if (matchResult) {
59
+ const mountMatchResult = mountMatcher(requestPath);
60
+ if (matchResult && mountMatchResult) {
60
61
  for (const handler of route.middlewares.flat()) {
61
62
  yield {
62
63
  handler,
63
64
  params: matchResult.params as Params,
65
+ path: mountMatchResult.path,
64
66
  };
65
67
  }
66
68
  }
@@ -68,20 +70,20 @@ function* executeRequest(request: Request, _env: FetchEnv) {
68
70
 
69
71
  // Then look for the first exact route match and execute its "modules"
70
72
  for (const route of routes) {
71
- if (
72
- route.methods.length &&
73
- !route.methods.includes(request.method as HTTPMethod)
74
- ) {
73
+ if (route.method && route.method !== request.method) {
75
74
  continue;
76
75
  }
77
76
 
78
77
  const routeMatcher = match(route.routePath, { end: true });
78
+ const mountMatcher = match(route.mountPath, { end: false });
79
79
  const matchResult = routeMatcher(requestPath);
80
- if (matchResult && route.modules.length) {
80
+ const mountMatchResult = mountMatcher(requestPath);
81
+ if (matchResult && mountMatchResult && route.modules.length) {
81
82
  for (const handler of route.modules.flat()) {
82
83
  yield {
83
84
  handler,
84
85
  params: matchResult.params as Params,
86
+ path: matchResult.path,
85
87
  };
86
88
  }
87
89
  break;
@@ -91,19 +93,24 @@ function* executeRequest(request: Request, _env: FetchEnv) {
91
93
 
92
94
  export default {
93
95
  async fetch(request: Request, env: FetchEnv, workerContext: WorkerContext) {
94
- const handlerIterator = executeRequest(request, env);
96
+ const handlerIterator = executeRequest(request);
95
97
  const data = {}; // arbitrary data the user can set between functions
96
98
  const next = async (input?: RequestInfo, init?: RequestInit) => {
97
99
  if (input !== undefined) {
98
- request = new Request(input, init);
100
+ let url = input;
101
+ if (typeof input === "string") {
102
+ url = new URL(input, request.url).toString();
103
+ }
104
+ request = new Request(url, init);
99
105
  }
100
106
 
101
107
  const result = handlerIterator.next();
102
108
  // Note we can't use `!result.done` because this doesn't narrow to the correct type
103
- if (result.done == false) {
104
- const { handler, params } = result.value;
109
+ if (result.done === false) {
110
+ const { handler, params, path } = result.value;
105
111
  const context = {
106
112
  request: new Request(request.clone()),
113
+ functionPath: path,
107
114
  next,
108
115
  params,
109
116
  data,
@@ -116,7 +123,7 @@ export default {
116
123
  // https://fetch.spec.whatwg.org/#null-body-status
117
124
  return new Response(
118
125
  [101, 204, 205, 304].includes(response.status) ? null : response.body,
119
- response
126
+ { ...response, headers: new Headers(response.headers) }
120
127
  );
121
128
  } else if (__FALLBACK_SERVICE__) {
122
129
  // There are no more handlers so finish with the fallback service (`env.ASSETS.fetch` in Pages' case)
@@ -0,0 +1,36 @@
1
+ import { getConfigCache, saveToConfigCache } from "../config-cache";
2
+ import { runInTempDir } from "./helpers/run-in-tmp";
3
+
4
+ interface PagesConfigCache {
5
+ account_id: string;
6
+ pages_project_name: string;
7
+ }
8
+
9
+ describe("config cache", () => {
10
+ runInTempDir();
11
+
12
+ const pagesConfigCacheFilename = "pages-config-cache.json";
13
+
14
+ it("should return an empty config if no file exists", () => {
15
+ expect(
16
+ getConfigCache<PagesConfigCache>(pagesConfigCacheFilename)
17
+ ).toMatchInlineSnapshot(`Object {}`);
18
+ });
19
+
20
+ it("should read and write values without overriding old ones", () => {
21
+ saveToConfigCache<PagesConfigCache>(pagesConfigCacheFilename, {
22
+ account_id: "some-account-id",
23
+ pages_project_name: "foo",
24
+ });
25
+ expect(
26
+ getConfigCache<PagesConfigCache>(pagesConfigCacheFilename).account_id
27
+ ).toEqual("some-account-id");
28
+
29
+ saveToConfigCache<PagesConfigCache>(pagesConfigCacheFilename, {
30
+ pages_project_name: "bar",
31
+ });
32
+ expect(
33
+ getConfigCache<PagesConfigCache>(pagesConfigCacheFilename).account_id
34
+ ).toEqual("some-account-id");
35
+ });
36
+ });