@fastly/expressly 1.0.0-alpha.8 → 1.0.0-beta.1

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.
@@ -1,14 +1,8 @@
1
1
  import { addCommonMethods } from "../common";
2
2
  import { CookieMap } from "./cookie-map";
3
- // Will extend Request correctly after https://github.com/fastly/js-compute-runtime/pull/116 is merged
4
- // See: https://github.com/fastly/js-compute-runtime/issues/113
5
3
  class ERequestBase extends Request {
6
4
  constructor(config, event) {
7
- super(event.request, {
8
- headers: event.request.headers,
9
- method: event.request.method,
10
- body: event.request.body,
11
- });
5
+ super(event.request);
12
6
  this.config = config;
13
7
  this.event = event;
14
8
  this.params = {};
@@ -0,0 +1,9 @@
1
+ /// <reference types="@fastly/js-compute" />
2
+ export declare class EHeaders extends Headers {
3
+ cookies: Map<string, string>;
4
+ constructor();
5
+ private setCookie;
6
+ set(name: string, value: string): void;
7
+ append(name: string, value: string): void;
8
+ delete(name: string): void;
9
+ }
@@ -0,0 +1,29 @@
1
+ export class EHeaders extends Headers {
2
+ constructor() {
3
+ super();
4
+ this.cookies = new Map();
5
+ }
6
+ setCookie(value) {
7
+ const [cookieName] = value.split("=");
8
+ this.cookies.set(cookieName, value);
9
+ }
10
+ set(name, value) {
11
+ if (/^Set-Cookie$/i.test(name)) {
12
+ this.cookies.clear();
13
+ this.setCookie(value);
14
+ }
15
+ super.set(name, value);
16
+ }
17
+ append(name, value) {
18
+ if (/^Set-Cookie$/i.test(name)) {
19
+ this.setCookie(value);
20
+ }
21
+ super.append(name, value);
22
+ }
23
+ delete(name) {
24
+ if (/^Set-Cookie$/i.test(name)) {
25
+ this.cookies.clear();
26
+ }
27
+ super.delete(name);
28
+ }
29
+ }
@@ -1,9 +1,10 @@
1
1
  /// <reference types="@fastly/js-compute" />
2
2
  import { SurrogateKeys } from "./surrogate-keys";
3
+ import { EHeaders } from "./headers";
3
4
  import { CookieOptions, EConfig } from "..";
4
5
  declare class EResponseBase {
5
6
  private config;
6
- headers: Headers;
7
+ headers: EHeaders;
7
8
  status: number;
8
9
  body: BodyInit;
9
10
  hasEnded: boolean;
@@ -2,17 +2,15 @@ import cookie from "cookie";
2
2
  import { addCommonMethods } from "../common";
3
3
  import { statusText } from "./status-codes";
4
4
  import { SurrogateKeys } from "./surrogate-keys";
5
- // TODO: extends Response
6
- // See: https://github.com/fastly/js-compute-runtime/issues/113
5
+ import { EHeaders } from "./headers";
7
6
  class EResponseBase {
8
7
  constructor(config) {
9
8
  this.config = config;
10
- this.headers = new Headers();
9
+ this.headers = new EHeaders();
11
10
  this.status = 0;
12
11
  this.body = null;
13
12
  this.hasEnded = false;
14
13
  this.surrogateKeys = new SurrogateKeys(this.headers);
15
- // super();
16
14
  }
17
15
  // Header helpers.
18
16
  vary(field) {
@@ -27,7 +25,7 @@ class EResponseBase {
27
25
  clearCookie(key, options = {}) {
28
26
  if (this.hasEnded)
29
27
  return;
30
- this.headers.append("Set-Cookie", cookie.serialize(key, "", { ...options, expires: "Thu, 01 Jan 1970 00:00:00 GMT" }));
28
+ this.cookie(key, "", { ...options, expires: new Date("Thu, 01 Jan 1970 00:00:00 GMT") });
31
29
  }
32
30
  // Response lifecycle methods.
33
31
  send(response) {
@@ -62,6 +60,9 @@ class EResponseBase {
62
60
  end(body) {
63
61
  if (this.hasEnded)
64
62
  return;
63
+ // if(typeof body !== "string") {
64
+ // body = body.toString()
65
+ // }
65
66
  this.send(body);
66
67
  }
67
68
  // Send a HTTP status code response and end the response process.
@@ -16,6 +16,38 @@ const defaultErrorHandler = (auto405) => async (err, req, res) => {
16
16
  console.error(err);
17
17
  res.withStatus(500).json({ error: err.message });
18
18
  };
19
+ /**
20
+ * Handles preflight requests from trusted origins configured by the user when initializing a router.
21
+ * Note that the wildcard value "*" will fail if the request is sent with credentials (see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin#directives)
22
+ * @param autoCorsPreflight the object containing CORS preflight option of trustedOrigins, an array of trusted origins.
23
+ * @returns 200 if the preflight request succeeds, 403 if it fails
24
+ */
25
+ const preflightHandler = (autoCorsPreflight) => async (req, res) => {
26
+ if (autoCorsPreflight.trustedOrigins.length === 0) {
27
+ return res.sendStatus(403);
28
+ }
29
+ let originHeaderValue = null;
30
+ if (autoCorsPreflight.trustedOrigins.length === 1 && autoCorsPreflight.trustedOrigins[0] === "*") {
31
+ originHeaderValue = "*";
32
+ }
33
+ else if (req.headers.has("origin")) {
34
+ const origin = req.headers.get("origin").toLowerCase();
35
+ if (autoCorsPreflight.trustedOrigins.some((trustedOrigin) => trustedOrigin.toLowerCase() === origin)) {
36
+ originHeaderValue = origin;
37
+ }
38
+ }
39
+ if (!originHeaderValue) {
40
+ return res.sendStatus(403);
41
+ }
42
+ if (req.headers.has("access-control-request-method")) {
43
+ res.headers.set("access-control-allow-methods", req.headers.get("access-control-request-method"));
44
+ }
45
+ if (req.headers.has("access-control-request-headers")) {
46
+ res.headers.set("access-control-allow-headers", req.headers.get("access-control-request-headers"));
47
+ }
48
+ res.headers.set("access-control-allow-origin", originHeaderValue);
49
+ return res.sendStatus(200);
50
+ };
19
51
  export class Router {
20
52
  constructor(config) {
21
53
  this.requestHandlers = [];
@@ -24,12 +56,15 @@ export class Router {
24
56
  parseCookie: true,
25
57
  auto405: true,
26
58
  extractRequestParameters: true,
27
- autoContentType: false
59
+ autoContentType: false,
28
60
  };
29
61
  this.config = {
30
62
  ...this.config,
31
63
  ...config
32
64
  };
65
+ if (this.config.autoCorsPreflight) {
66
+ this.options("*", preflightHandler(this.config.autoCorsPreflight));
67
+ }
33
68
  }
34
69
  listen() {
35
70
  addEventListener("fetch", (event) => event.respondWith(this.handler(event)));
@@ -154,13 +189,16 @@ export class Router {
154
189
  };
155
190
  }
156
191
  static serializeResponse(res) {
157
- // Default to 200 / 204 if no status was set by middleware / route handler.
158
- if (res.status === 0) {
159
- res.status = Boolean(res.body) ? 200 : 204;
160
- }
161
- return new Response(res.body, {
192
+ const response = new Response(res.body, {
162
193
  headers: res.headers,
163
- status: res.status,
194
+ // Default to 200 / 204 if no status was set by middleware / route handler.
195
+ status: res.status ? res.status : Boolean(res.body) ? 200 : 204,
164
196
  });
197
+ if (res.headers.cookies.size) {
198
+ // Loop cookies manually to work around this issue: https://github.com/fastly/js-compute-runtime/issues/47
199
+ response.headers.delete("Set-Cookie");
200
+ res.headers.cookies.forEach((c) => response.headers.append("Set-Cookie", c));
201
+ }
202
+ return response;
165
203
  }
166
204
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastly/expressly",
3
- "version": "1.0.0-alpha.8",
3
+ "version": "1.0.0-beta.1",
4
4
  "description": "Express-style router for Fastly's Compute@Edge.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  "scripts": {
15
15
  "build": "tsc",
16
16
  "prepublish": "tsc",
17
- "dev": "nodemon --exec \"tsc\""
17
+ "dev": "tsc --watch"
18
18
  },
19
19
  "keywords": [
20
20
  "fastly",
@@ -30,20 +30,19 @@
30
30
  "license": "MIT",
31
31
  "devDependencies": {
32
32
  "@types/node": "^17.0.40",
33
- "auto": "^10.37.1",
33
+ "auto": "^10.37.6",
34
34
  "husky": "^8.0.1",
35
- "nodemon": "^2.0.18",
36
35
  "prettier": "^2.7.1",
37
36
  "pretty-quick": "^3.1.3",
38
- "ts-loader": "^9.3.1",
39
- "typescript": "^4.7.4",
40
- "webpack": "^5.73.0",
37
+ "ts-loader": "^9.4.1",
38
+ "typescript": "^4.8.4",
39
+ "webpack": "^5.74.0",
41
40
  "webpack-cli": "^4.10.0"
42
41
  },
43
42
  "dependencies": {
44
- "@fastly/js-compute": "^0.3.0",
43
+ "@fastly/js-compute": "^0.5.4",
45
44
  "cookie": "^0.5.0",
46
- "core-js": "^3.23.3",
45
+ "core-js": "^3.25.5",
47
46
  "path-to-regexp": "^6.2.1"
48
47
  },
49
48
  "auto": {
package/tsconfig.json CHANGED
@@ -16,5 +16,8 @@
16
16
  "forceConsistentCasingInFileNames": true,
17
17
  "declaration": true
18
18
  },
19
- "exclude": ["dist/", ".dora", "docs/"]
19
+ "exclude": ["node_modules"],
20
+ "include": [
21
+ "src/**/*.ts"
22
+ ]
20
23
  }