wrangler 3.1.0 → 3.1.2

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 CHANGED
@@ -6,8 +6,6 @@
6
6
  <a href="https://discord.gg/CloudflareDev"><img alt="Discord" src="https://img.shields.io/discord/595317990191398933?color=%23F48120&style=flat-square"></a>
7
7
  </section>
8
8
 
9
- > This package is for wrangler v2.x, released first in May 2022. If you're looking for v1.x of the `@cloudflare/wrangler` package, visit https://www.npmjs.com/package/@cloudflare/wrangler / https://github.com/cloudflare/wrangler-legacy.
10
-
11
9
  `wrangler` is a command line tool for building [Cloudflare Workers](https://workers.cloudflare.com/).
12
10
 
13
11
  ## Quick Start
@@ -26,7 +24,7 @@ npx wrangler deploy index.js --name my-worker
26
24
 
27
25
  ```bash
28
26
  # Generate a new project
29
- npx wrangler init my-worker
27
+ npx wrangler init my-worker --no-delegate-c3
30
28
  # try it out
31
29
  cd my-worker && npm run start
32
30
  # and then deploy it
@@ -43,15 +41,15 @@ $ npm install wrangler --save-dev
43
41
 
44
42
  Wrangler is configured via a `wrangler.toml` file in the project root. When utilizing the `wrangler init` command, a `wrangler.toml` file will be created for you.
45
43
 
46
- example:
44
+ Example:
47
45
 
48
46
  ```toml
49
- main = "./src/index.ts" # init w/ TypeScript
50
47
  name = "my-worker"
51
- compatibility_date = "YYY-MM-DD"
48
+ main = "./src/index.ts" # init w/ TypeScript
49
+ compatibility_date = "YYYY-MM-DD"
52
50
  ```
53
51
 
54
- for more detailed information about configuration, see the [documentation](https://developers.cloudflare.com/workers/cli-wrangler/configuration)
52
+ For more detailed information about configuration, refer to the [documentation](https://developers.cloudflare.com/workers/wrangler/configuration/).
55
53
 
56
54
  ## Commands
57
55
 
@@ -67,7 +65,7 @@ Start a local development server, with live reloading and devtools.
67
65
 
68
66
  Publish the given script to the worldwide Cloudflare network.
69
67
 
70
- For more commands and options, refer to the [documentation](https://developers.cloudflare.com/workers/cli-wrangler/commands).
68
+ For more commands and options, refer to the [documentation](https://developers.cloudflare.com/workers/wrangler/commands/).
71
69
 
72
70
  ## Pages
73
71
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wrangler",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "Command-line interface for all things Cloudflare Workers",
5
5
  "keywords": [
6
6
  "wrangler",
@@ -53,17 +53,19 @@
53
53
  "assert-git-version": "node -r esbuild-register scripts/assert-git-version.ts",
54
54
  "build": "npm run clean && npm run bundle && npm run emit-types",
55
55
  "bundle": "node -r esbuild-register scripts/bundle.ts",
56
- "check:type": "node -r esbuild-register scripts/tsc-all.ts",
56
+ "check:lint": "eslint .",
57
+ "check:type": "tsc",
57
58
  "clean": "rimraf wrangler-dist miniflare-dist emitted-types",
58
59
  "dev": "npm run clean && concurrently -c black,blue --kill-others-on-fail false 'npm run bundle -- --watch' 'npm run check:type -- --watch --preserveWatchOutput'",
59
60
  "emit-types": "tsc -p tsconfig.emit.json && node -r esbuild-register scripts/emit-types.ts",
60
61
  "prepublishOnly": "SOURCEMAPS=false npm run build",
61
62
  "start": "npm run bundle && cross-env NODE_OPTIONS=--enable-source-maps ./bin/wrangler.js",
62
63
  "test": "npm run assert-git-version && jest",
63
- "test-watch": "npm run test -- --runInBand --testTimeout=50000 --watch",
64
64
  "test:ci": "npm run test -- --coverage",
65
65
  "test:debug": "npm run test -- --silent=false --verbose=true",
66
- "test:e2e": "vitest --single-thread ./e2e"
66
+ "test:e2e": "vitest --test-timeout 240000 --single-thread --dir ./e2e run",
67
+ "test:watch": "npm run test -- --runInBand --testTimeout=50000 --watch",
68
+ "type:tests": "tsc -p ./src/__tests__/tsconfig.json && tsc -p ./e2e/tsconfig.json"
67
69
  },
68
70
  "jest": {
69
71
  "coverageReporters": [
@@ -81,7 +83,7 @@
81
83
  "<rootDir>/src/__tests__/jest.setup.ts"
82
84
  ],
83
85
  "testRegex": "src/__tests__/.*\\.(test|spec)\\.[jt]sx?$",
84
- "testTimeout": 30000,
86
+ "testTimeout": 50000,
85
87
  "transform": {
86
88
  "^.+\\.c?(t|j)sx?$": [
87
89
  "esbuild-jest",
@@ -101,7 +103,7 @@
101
103
  "blake3-wasm": "^2.1.5",
102
104
  "chokidar": "^3.5.3",
103
105
  "esbuild": "0.16.3",
104
- "miniflare": "^3.0.0",
106
+ "miniflare": "3.20230628.0",
105
107
  "nanoid": "^3.3.3",
106
108
  "path-to-regexp": "^6.2.0",
107
109
  "selfsigned": "^2.0.1",
@@ -109,7 +111,9 @@
109
111
  "xxhash-wasm": "^1.0.1"
110
112
  },
111
113
  "devDependencies": {
114
+ "@cloudflare/eslint-config-worker": "*",
112
115
  "@cloudflare/types": "^6.18.4",
116
+ "@cloudflare/workers-tsconfig": "*",
113
117
  "@cloudflare/workers-types": "^4.20230511.0",
114
118
  "@iarna/toml": "^3.0.0",
115
119
  "@microsoft/api-extractor": "^7.28.3",
@@ -168,7 +172,7 @@
168
172
  "remove-accents-esm": "^0.0.1",
169
173
  "semiver": "^1.1.0",
170
174
  "serve-static": "^1.15.0",
171
- "shellac": "^0.7.3",
175
+ "shellac": "^0.8.0",
172
176
  "signal-exit": "^3.0.7",
173
177
  "strip-ansi": "^7.0.1",
174
178
  "supports-color": "^9.2.2",
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "../../tsconfig.json",
2
+ "extends": "@cloudflare/workers-tsconfig/tsconfig.json",
3
3
  "compilerOptions": {
4
4
  "types": ["node", "jest"]
5
5
  },
@@ -1,6 +1,6 @@
1
1
  const urls = new Set();
2
2
 
3
- export function checkedFetch(request, init) {
3
+ function checkURL(request, init) {
4
4
  const url =
5
5
  request instanceof URL
6
6
  ? request
@@ -19,5 +19,12 @@ export function checkedFetch(request, init) {
19
19
  );
20
20
  }
21
21
  }
22
- return globalThis.fetch(request, init);
23
22
  }
23
+
24
+ globalThis.fetch = new Proxy(globalThis.fetch, {
25
+ apply(target, thisArg, argArray) {
26
+ const [request, init] = argArray;
27
+ checkURL(request, init);
28
+ return Reflect.apply(target, thisArg, argArray);
29
+ },
30
+ });
@@ -1,6 +1,9 @@
1
1
  declare module "__ENTRY_POINT__" {
2
2
  import type { Middleware } from "./middleware/common";
3
- const worker: ExportedHandler & { middleware?: Middleware[] };
3
+ const worker: ExportedHandler & {
4
+ middleware?: Middleware[];
5
+ envWrappers: ((env: Record<string, unknown>) => Record<string, unknown>)[];
6
+ };
4
7
  export default worker;
5
8
  }
6
9
 
@@ -36,13 +36,54 @@ class __Facade_ScheduledController__ implements ScheduledController {
36
36
  }
37
37
  }
38
38
 
39
- const __facade_modules_fetch__: Middleware = function (request, env, ctx) {
40
- if (worker.fetch === undefined) throw new Error("No fetch handler!"); // TODO: proper error message
39
+ const __facade_modules_fetch__: ExportedHandlerFetchHandler = function (
40
+ request,
41
+ env,
42
+ ctx
43
+ ) {
44
+ if (worker.fetch === undefined)
45
+ throw new Error("Handler does not export a fetch() function.");
41
46
  return worker.fetch(request, env, ctx);
42
47
  };
43
48
 
44
- const facade: ExportedHandler<unknown> = {
45
- fetch(request, env, ctx) {
49
+ function getMaskedEnv(rawEnv: unknown) {
50
+ let env = rawEnv as Record<string, unknown>;
51
+ if (worker.envWrappers && worker.envWrappers.length > 0) {
52
+ for (const wrapFn of worker.envWrappers) {
53
+ env = wrapFn(env);
54
+ }
55
+ }
56
+ return env;
57
+ }
58
+
59
+ /**
60
+ * This type is here to cause a type error if a new export handler is added to
61
+ * `ExportHandler` without it being included in the `facade` below.
62
+ */
63
+ type MissingExportHandlers = Omit<
64
+ Required<ExportedHandler>,
65
+ "tail" | "trace" | "scheduled" | "queue" | "test" | "fetch"
66
+ >;
67
+
68
+ const facade: ExportedHandler<unknown> & MissingExportHandlers = {
69
+ ...(worker.tail && {
70
+ tail: maskHandlerEnv(worker.tail),
71
+ }),
72
+ ...(worker.trace && {
73
+ trace: maskHandlerEnv(worker.trace),
74
+ }),
75
+ ...(worker.scheduled && {
76
+ scheduled: maskHandlerEnv(worker.scheduled),
77
+ }),
78
+ ...(worker.queue && {
79
+ queue: maskHandlerEnv(worker.queue),
80
+ }),
81
+ ...(worker.test && {
82
+ test: maskHandlerEnv(worker.test),
83
+ }),
84
+
85
+ fetch(request, rawEnv, ctx) {
86
+ const env = getMaskedEnv(rawEnv);
46
87
  // Get the chain of middleware from the worker object
47
88
  if (worker.middleware && worker.middleware.length > 0) {
48
89
  for (const middleware of worker.middleware) {
@@ -71,14 +112,16 @@ const facade: ExportedHandler<unknown> = {
71
112
  // We didn't have any middleware so we can skip the invocation chain,
72
113
  // and just call the fetch handler directly
73
114
 
74
- // We "don't care" if this is undefined as we want to have the same behaviour
115
+ // We "don't care" if this is undefined as we want to have the same behavior
75
116
  // as if the worker completely bypassed middleware.
76
- return worker.fetch!(request, env, ctx);
117
+ return __facade_modules_fetch__(request, env, ctx);
77
118
  }
78
119
  },
79
- scheduled: worker.scheduled,
80
- queue: worker.queue,
81
- trace: worker.trace,
82
120
  };
83
121
 
122
+ type HandlerFn<D, R> = (data: D, env: unknown, ctx: ExecutionContext) => R;
123
+ function maskHandlerEnv<D, R>(handler: HandlerFn<D, R>): HandlerFn<D, R> {
124
+ return (data, env, ctx) => handler(data, getMaskedEnv(env), ctx);
125
+ }
126
+
84
127
  export default facade;
@@ -0,0 +1,3 @@
1
+ declare module "config:middleware/d1-beta" {
2
+ export const D1_IMPORTS: string[];
3
+ }
@@ -1,13 +1,11 @@
1
- // src/shim.ts
2
- import worker, * as OTHER_EXPORTS from "__ENTRY_POINT__";
3
- export * from "__ENTRY_POINT__";
1
+ /// <reference path="middleware-d1-beta.d.ts"/>
2
+
3
+ import { D1_IMPORTS } from "config:middleware/d1-beta";
4
4
 
5
5
  // src/index.ts
6
- var D1Database = class {
7
- constructor(binding) {
8
- this.binding = binding;
9
- }
10
- prepare(query) {
6
+ class D1Database {
7
+ constructor(readonly binding: Fetcher) {}
8
+ prepare(query: string) {
11
9
  return new D1PreparedStatement(this, query);
12
10
  }
13
11
  async dump() {
@@ -19,19 +17,19 @@ var D1Database = class {
19
17
  });
20
18
  if (response.status !== 200) {
21
19
  try {
22
- const err = await response.json();
23
- throw new Error("D1_DUMP_ERROR", {
20
+ const err = (await response.json()) as { error: string };
21
+ throw new Error(`D1_DUMP_ERROR: ${err.error}`, {
24
22
  cause: new Error(err.error),
25
23
  });
26
24
  } catch (e) {
27
- throw new Error("D1_DUMP_ERROR", {
25
+ throw new Error(`D1_DUMP_ERROR: Status + ${response.status}`, {
28
26
  cause: new Error("Status " + response.status),
29
27
  });
30
28
  }
31
29
  }
32
30
  return await response.arrayBuffer();
33
31
  }
34
- async batch(statements) {
32
+ async batch(statements: D1PreparedStatement[]) {
35
33
  const exec = await this._send(
36
34
  "/query",
37
35
  statements.map((s) => s.statement),
@@ -39,7 +37,7 @@ var D1Database = class {
39
37
  );
40
38
  return exec;
41
39
  }
42
- async exec(query) {
40
+ async exec(query: string) {
43
41
  const lines = query.trim().split("\n");
44
42
  const _exec = await this._send("/query", lines, [], false);
45
43
  const exec = Array.isArray(_exec) ? _exec : [_exec];
@@ -49,26 +47,36 @@ var D1Database = class {
49
47
  })
50
48
  .indexOf(1);
51
49
  if (error !== -1) {
52
- throw new Error("D1_EXEC_ERROR", {
53
- cause: new Error(
54
- "Error in line " +
55
- (error + 1) +
56
- ": " +
57
- lines[error] +
58
- ": " +
59
- exec[error].error
60
- ),
61
- });
50
+ throw new Error(
51
+ `D1_EXEC_ERROR: Error in line ${error + 1}: ${lines[error]}: ${
52
+ exec[error].error
53
+ }`,
54
+ {
55
+ cause: new Error(
56
+ "Error in line " +
57
+ (error + 1) +
58
+ ": " +
59
+ lines[error] +
60
+ ": " +
61
+ exec[error].error
62
+ ),
63
+ }
64
+ );
62
65
  } else {
63
66
  return {
64
67
  count: exec.length,
65
68
  duration: exec.reduce((p, c) => {
66
- return p + c.meta.duration;
69
+ return p + (c.meta.duration as number);
67
70
  }, 0),
68
71
  };
69
72
  }
70
73
  }
71
- async _send(endpoint, query, params, dothrow = true) {
74
+ async _send(
75
+ endpoint: string,
76
+ query: string | string[],
77
+ params: unknown[],
78
+ dothrow = true
79
+ ) {
72
80
  const body = JSON.stringify(
73
81
  typeof query == "object"
74
82
  ? query.map((s, index) => {
@@ -87,75 +95,79 @@ var D1Database = class {
87
95
  body,
88
96
  });
89
97
  try {
90
- const answer = await response.json();
98
+ const answer = (await response.json()) as { error?: string };
91
99
  if (answer.error && dothrow) {
92
100
  const err = answer;
93
- throw new Error("D1_ERROR", { cause: new Error(err.error) });
101
+ throw new Error(`D1_ERROR: ${err.error}`, {
102
+ cause: new Error(err.error),
103
+ });
94
104
  } else {
95
105
  return Array.isArray(answer)
96
106
  ? answer.map((r) => mapD1Result(r))
97
107
  : mapD1Result(answer);
98
108
  }
99
109
  } catch (e) {
100
- throw new Error("D1_ERROR", {
101
- cause: new Error(e.cause || "Something went wrong"),
110
+ const error = e as Error;
111
+ throw new Error(`D1_ERROR: ${error.cause || "Something went wrong"}`, {
112
+ cause: new Error(`${error.cause}` || "Something went wrong"),
102
113
  });
103
114
  }
104
115
  }
105
- };
106
- var D1PreparedStatement = class {
107
- constructor(database, statement, values) {
108
- this.database = database;
109
- this.statement = statement;
110
- this.params = values || [];
111
- }
112
- bind(...values) {
116
+ }
117
+
118
+ class D1PreparedStatement {
119
+ constructor(
120
+ readonly database: D1Database,
121
+ readonly statement: string,
122
+ readonly params: unknown[] = []
123
+ ) {}
124
+ bind(...values: unknown[]) {
113
125
  for (var r in values) {
114
- switch (typeof values[r]) {
126
+ const value = values[r];
127
+ switch (typeof value) {
115
128
  case "number":
116
129
  case "string":
117
130
  break;
118
131
  case "object":
119
- if (values[r] == null) break;
132
+ if (value == null) break;
120
133
  if (
121
- Array.isArray(values[r]) &&
122
- values[r]
134
+ Array.isArray(value) &&
135
+ value
123
136
  .map((b) => {
124
137
  return typeof b == "number" && b >= 0 && b < 256 ? 1 : 0;
125
138
  })
126
139
  .indexOf(0) == -1
127
140
  )
128
141
  break;
129
- if (values[r] instanceof ArrayBuffer) {
130
- values[r] = Array.from(new Uint8Array(values[r]));
142
+ if (value instanceof ArrayBuffer) {
143
+ values[r] = Array.from(new Uint8Array(value));
131
144
  break;
132
145
  }
133
- if (ArrayBuffer.isView(values[r])) {
134
- values[r] = Array.from(values[r]);
146
+ if (ArrayBuffer.isView(value)) {
147
+ values[r] = Array.from(new Uint8Array(value.buffer));
135
148
  break;
136
149
  }
137
150
  default:
138
- throw new Error("D1_TYPE_ERROR", {
139
- cause: new Error(
140
- "Type '" +
141
- typeof values[r] +
142
- "' not supported for value '" +
143
- values[r] +
144
- "'"
145
- ),
146
- });
151
+ throw new Error(
152
+ `D1_TYPE_ERROR: Type '${typeof value}' not supported for value '${value}'`,
153
+ {
154
+ cause: new Error(
155
+ `Type '${typeof value}' not supported for value '${value}'`
156
+ ),
157
+ }
158
+ );
147
159
  }
148
160
  }
149
161
  return new D1PreparedStatement(this.database, this.statement, values);
150
162
  }
151
- async first(colName) {
163
+ async first(colName: string) {
152
164
  const info = firstIfArray(
153
165
  await this.database._send("/query", this.statement, this.params)
154
166
  );
155
167
  const results = info.results;
156
168
  if (colName !== void 0) {
157
169
  if (results.length > 0 && results[0][colName] === void 0) {
158
- throw new Error("D1_COLUMN_NOTFOUND", {
170
+ throw new Error(`D1_COLUMN_NOTFOUND: Column not found (${colName})`, {
159
171
  cause: new Error("Column not found"),
160
172
  });
161
173
  }
@@ -187,12 +199,12 @@ var D1PreparedStatement = class {
187
199
  }
188
200
  return raw;
189
201
  }
190
- };
191
- function firstIfArray(results) {
202
+ }
203
+ function firstIfArray(results: unknown) {
192
204
  return Array.isArray(results) ? results[0] : results;
193
205
  }
194
- function mapD1Result(result) {
195
- let map = {
206
+ function mapD1Result(result: Partial<D1Result>): D1Result {
207
+ let map: D1Result = {
196
208
  results: result.results || [],
197
209
  success: result.success === void 0 ? true : result.success,
198
210
  meta: result.meta || {},
@@ -201,12 +213,17 @@ function mapD1Result(result) {
201
213
  return map;
202
214
  }
203
215
 
216
+ type D1Result = {
217
+ results: unknown[];
218
+ success: boolean;
219
+ meta: Record<string, unknown>;
220
+ error?: string;
221
+ };
222
+
204
223
  // src/shim.ts
205
- var D1_IMPORTS = __D1_IMPORTS__;
206
- var LOCAL_MODE = __LOCAL_MODE__;
207
224
  var D1_BETA_PREFIX = `__D1_BETA__`;
208
225
  var envMap = /* @__PURE__ */ new Map();
209
- function getMaskedEnv(env) {
226
+ function getMaskedEnv(env: Record<string, Fetcher | D1Database>) {
210
227
  if (envMap.has(env)) return envMap.get(env);
211
228
  const newEnv = new Map(Object.entries(env));
212
229
  D1_IMPORTS.filter((bindingName) =>
@@ -214,31 +231,14 @@ function getMaskedEnv(env) {
214
231
  ).forEach((bindingName) => {
215
232
  newEnv.delete(bindingName);
216
233
  const newName = bindingName.slice(D1_BETA_PREFIX.length);
217
- const newBinding = !LOCAL_MODE
218
- ? new D1Database(env[bindingName])
219
- : env[bindingName];
234
+ const newBinding = new D1Database(env[bindingName] as Fetcher);
220
235
  newEnv.set(newName, newBinding);
221
236
  });
222
237
  const newEnvObj = Object.fromEntries(newEnv.entries());
223
238
  envMap.set(env, newEnvObj);
224
239
  return newEnvObj;
225
240
  }
226
- var shim_default = {
227
- ...worker,
228
- async fetch(request, env, ctx) {
229
- return worker.fetch(request, getMaskedEnv(env), ctx);
230
- },
231
- async queue(batch, env, ctx) {
232
- return worker.queue(batch, getMaskedEnv(env), ctx);
233
- },
234
- async scheduled(controller, env, ctx) {
235
- return worker.scheduled(controller, getMaskedEnv(env), ctx);
236
- },
237
- async trace(traces, env, ctx) {
238
- return worker.trace(traces, getMaskedEnv(env), ctx);
239
- },
240
- async email(message, env, ctx) {
241
- return worker.email(message, getMaskedEnv(env), ctx);
242
- },
243
- };
244
- export { shim_default as default };
241
+
242
+ export function wrap(env: Record<string, D1Database | Fetcher>) {
243
+ return getMaskedEnv(env);
244
+ }
@@ -0,0 +1,4 @@
1
+ declare module "config:middleware/multiworker-dev" {
2
+ import type { WorkerRegistry } from "../../src/dev-registry";
3
+ export const workers: WorkerRegistry;
4
+ }
@@ -0,0 +1,59 @@
1
+ // @ts-nocheck
2
+ /// <reference path="middleware-multiworker-dev.d.ts"/>
3
+
4
+ import { workers } from "config:middleware/multiworker-dev";
5
+ import type { WorkerRegistry } from "../../src/dev-registry";
6
+
7
+ export function wrap(env: Record<string, unknown>) {
8
+ const facadeEnv = { ...env };
9
+ // For every Worker definition that's available,
10
+ // create a fetcher for it on the facade env.
11
+ // for const [name, binding] of env
12
+ // if Workers[name]
13
+ // const details = Workers[name];
14
+
15
+ for (const [name, details] of Object.entries(workers as WorkerRegistry)) {
16
+ if (details) {
17
+ facadeEnv[name] = {
18
+ async fetch(...reqArgs: Parameters<Fetcher["fetch"]>) {
19
+ const reqFromArgs = new Request(...reqArgs);
20
+ if (details.headers) {
21
+ for (const [key, value] of Object.entries(details.headers)) {
22
+ // In remote mode, you need to add a couple of headers
23
+ // to make sure it's talking to the 'dev' preview session
24
+ // (much like wrangler dev already does via proxy.ts)
25
+ reqFromArgs.headers.set(key, value);
26
+ }
27
+ return (env[name] as Fetcher).fetch(reqFromArgs);
28
+ }
29
+
30
+ const url = new URL(reqFromArgs.url);
31
+ if (details.protocol !== undefined) {
32
+ url.protocol = details.protocol;
33
+ }
34
+ if (details.host !== undefined) {
35
+ url.host = details.host;
36
+ }
37
+ if (details.port !== undefined) {
38
+ url.port = details.port.toString();
39
+ }
40
+
41
+ const request = new Request(url.toString(), reqFromArgs);
42
+ return fetch(request);
43
+ },
44
+ };
45
+ } else {
46
+ // This means there's no dev binding available.
47
+ // Let's use whatever's available, or put a shim with a message.
48
+ facadeEnv[name] = facadeEnv[name] || {
49
+ async fetch() {
50
+ return new Response(
51
+ `You should start up wrangler dev --local on the ${name} worker`,
52
+ { status: 404 }
53
+ );
54
+ },
55
+ };
56
+ }
57
+ }
58
+ return facadeEnv;
59
+ }
@@ -0,0 +1,6 @@
1
+ declare module "config:middleware/serve-static-assets" {
2
+ import type { CacheControl } from "@cloudflare/kv-asset-handler";
3
+
4
+ export const spaMode: boolean;
5
+ export const cacheControl: Partial<CacheControl>;
6
+ }
@@ -0,0 +1,56 @@
1
+ /// <reference path="middleware-serve-static-assets.d.ts"/>
2
+
3
+ import {
4
+ getAssetFromKV,
5
+ NotFoundError,
6
+ MethodNotAllowedError,
7
+ serveSinglePageApp,
8
+ } from "@cloudflare/kv-asset-handler";
9
+ import type { Options } from "@cloudflare/kv-asset-handler";
10
+ import { spaMode, cacheControl } from "config:middleware/serve-static-assets";
11
+ import type * as kvAssetHandler from "@cloudflare/kv-asset-handler";
12
+ import manifest from "__STATIC_CONTENT_MANIFEST";
13
+ const ASSET_MANIFEST = JSON.parse(manifest);
14
+
15
+ import type { Middleware } from "./common";
16
+
17
+ const staticAssets: Middleware = async (request, env, _ctx, middlewareCtx) => {
18
+ let options: Partial<Options> = {
19
+ ASSET_MANIFEST,
20
+ ASSET_NAMESPACE: env.__STATIC_CONTENT,
21
+ cacheControl: cacheControl,
22
+ mapRequestToAsset: spaMode ? serveSinglePageApp : undefined,
23
+ };
24
+
25
+ try {
26
+ const page = await (getAssetFromKV as typeof kvAssetHandler.getAssetFromKV)(
27
+ {
28
+ request,
29
+ waitUntil(promise) {
30
+ return _ctx.waitUntil(promise);
31
+ },
32
+ },
33
+ options
34
+ );
35
+
36
+ // allow headers to be altered
37
+ const response = new Response(page.body, page);
38
+
39
+ response.headers.set("X-XSS-Protection", "1; mode=block");
40
+ response.headers.set("X-Content-Type-Options", "nosniff");
41
+ response.headers.set("X-Frame-Options", "DENY");
42
+ response.headers.set("Referrer-Policy", "unsafe-url");
43
+ response.headers.set("Feature-Policy", "none");
44
+
45
+ return response;
46
+ } catch (e) {
47
+ if (e instanceof NotFoundError || e instanceof MethodNotAllowedError) {
48
+ // if a known error is thrown then serve from actual worker
49
+ return await middlewareCtx.next(request, env);
50
+ }
51
+ // otherwise it's a real error, so throw it
52
+ throw e;
53
+ }
54
+ };
55
+
56
+ export default staticAssets;