@fastly/expressly 1.0.1-0 → 1.1.0

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 (71) hide show
  1. package/.nvmrc +1 -1
  2. package/README.md +0 -1
  3. package/dist/lib/routing/common.d.ts +11 -11
  4. package/dist/lib/routing/common.js +32 -33
  5. package/dist/lib/routing/error-middleware.d.ts +5 -5
  6. package/dist/lib/routing/request/index.d.ts +16 -9
  7. package/dist/lib/routing/request/index.js +17 -22
  8. package/dist/lib/routing/request-handler.d.ts +5 -5
  9. package/dist/lib/routing/response/headers.d.ts +9 -0
  10. package/dist/lib/routing/response/headers.js +29 -0
  11. package/dist/lib/routing/response/index.d.ts +16 -3
  12. package/dist/lib/routing/response/index.js +10 -7
  13. package/dist/lib/routing/router.js +48 -9
  14. package/package.json +13 -14
  15. package/tsconfig.json +4 -1
  16. package/yarn-error.log +2484 -0
  17. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
  18. package/.github/workflows/docs.yml +0 -52
  19. package/.github/workflows/release.yml +0 -39
  20. package/.husky/pre-commit +0 -4
  21. package/CHANGELOG.md +0 -9
  22. package/docs/.nvmrc +0 -1
  23. package/docs/README.md +0 -27
  24. package/docs/babel.config.js +0 -3
  25. package/docs/docs/config.md +0 -52
  26. package/docs/docs/handling-data/_category_.json +0 -4
  27. package/docs/docs/handling-data/cookies.md +0 -103
  28. package/docs/docs/handling-data/headers.md +0 -110
  29. package/docs/docs/handling-data/request.md +0 -82
  30. package/docs/docs/handling-data/response.md +0 -162
  31. package/docs/docs/handling-data/search-params.md +0 -45
  32. package/docs/docs/intro.md +0 -61
  33. package/docs/docs/middleware/_category_.json +0 -5
  34. package/docs/docs/middleware/controlling-flow.md +0 -41
  35. package/docs/docs/middleware/error-middleware.md +0 -41
  36. package/docs/docs/middleware/what-is-middleware.md +0 -31
  37. package/docs/docs/origin-data/_category_.json +0 -4
  38. package/docs/docs/origin-data/working-with-origins.md +0 -49
  39. package/docs/docs/routing.md +0 -163
  40. package/docs/docusaurus.config.js +0 -119
  41. package/docs/package.json +0 -48
  42. package/docs/sidebars.js +0 -31
  43. package/docs/src/components/HomepageFeatures.module.css +0 -11
  44. package/docs/src/components/HomepageFeatures.tsx +0 -62
  45. package/docs/src/css/custom.css +0 -45
  46. package/docs/src/pages/index.module.css +0 -35
  47. package/docs/src/pages/index.tsx +0 -109
  48. package/docs/static/.nojekyll +0 -0
  49. package/docs/static/img/favicon.ico +0 -0
  50. package/docs/static/img/logo-black.png +0 -0
  51. package/docs/static/img/logo-transparent.png +0 -0
  52. package/docs/static/img/logo.png +0 -0
  53. package/docs/static-host/Cargo.lock +0 -435
  54. package/docs/static-host/Cargo.toml +0 -16
  55. package/docs/static-host/fastly.toml +0 -9
  56. package/docs/static-host/rust-toolchain +0 -3
  57. package/docs/static-host/src/main.rs +0 -115
  58. package/docs/tsconfig.json +0 -7
  59. package/docs/yarn.lock +0 -8416
  60. package/src/index.ts +0 -11
  61. package/src/lib/routing/common.ts +0 -34
  62. package/src/lib/routing/error-middleware.ts +0 -23
  63. package/src/lib/routing/errors.ts +0 -18
  64. package/src/lib/routing/index.d.ts +0 -18
  65. package/src/lib/routing/request/cookie-map.ts +0 -42
  66. package/src/lib/routing/request/index.ts +0 -64
  67. package/src/lib/routing/request-handler.ts +0 -22
  68. package/src/lib/routing/response/index.ts +0 -113
  69. package/src/lib/routing/response/status-codes.ts +0 -57
  70. package/src/lib/routing/response/surrogate-keys.ts +0 -32
  71. package/src/lib/routing/router.ts +0 -191
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- 16
1
+ 18
package/README.md CHANGED
@@ -53,4 +53,3 @@ This will start your service on [http://localhost:7676](http://localhost:7676).
53
53
  ## Examples
54
54
 
55
55
  Check out the JavaScript code examples for Compute@Edge on Fastly's [Developer Hub](https://developer.fastly.com/solutions/examples/javascript/).
56
- ß
@@ -1,11 +1,11 @@
1
- /// <reference types="@fastly/js-compute" />
2
- export declare class ECommonObject {
3
- headers: Headers;
4
- set(headerNameOrObject: string | {
5
- [key: string]: string;
6
- }, value?: string): void;
7
- private appendHeader;
8
- append(headerNameOrObject: string | {
9
- [key: string]: string | string[];
10
- }, value?: string | string[]): void;
11
- }
1
+ export declare function addCommonMethods<T extends new (...args: any[]) => any>(Base: T): {
2
+ new (...args: any[]): {
3
+ [x: string]: any;
4
+ set(headerNameOrObject: string | {
5
+ [key: string]: string;
6
+ }, value?: string): void;
7
+ append(headerNameOrObject: string | {
8
+ [key: string]: string | string[];
9
+ }, value?: string | string[]): void;
10
+ };
11
+ } & T;
@@ -1,36 +1,35 @@
1
- export class ECommonObject {
2
- constructor() {
3
- this.headers = new Headers();
4
- }
5
- // Header helpers.
6
- set(headerNameOrObject, value) {
7
- if (typeof headerNameOrObject === "string") {
8
- this.headers.set(headerNameOrObject, value);
1
+ export function addCommonMethods(Base) {
2
+ return class extends Base {
3
+ // Header helpers.
4
+ set(headerNameOrObject, value) {
5
+ if (typeof headerNameOrObject === "string") {
6
+ this.headers.set(headerNameOrObject, value);
7
+ }
8
+ else {
9
+ Object.keys(headerNameOrObject).forEach((headerName) => {
10
+ this.headers.set(headerName, headerNameOrObject[headerName]);
11
+ });
12
+ }
9
13
  }
10
- else {
11
- Object.keys(headerNameOrObject).forEach((headerName) => {
12
- this.headers.set(headerName, headerNameOrObject[headerName]);
13
- });
14
+ append(headerNameOrObject, value) {
15
+ const appendHeader = (headerName, headerValue) => {
16
+ if (typeof headerValue === "string") {
17
+ this.headers.append(headerName, headerValue);
18
+ }
19
+ else if (Array.isArray(headerValue)) {
20
+ headerValue.forEach((v) => {
21
+ this.headers.append(headerName, v);
22
+ });
23
+ }
24
+ };
25
+ if (typeof headerNameOrObject === "string") {
26
+ appendHeader(headerNameOrObject, value);
27
+ }
28
+ else {
29
+ Object.keys(headerNameOrObject).forEach((headerName) => {
30
+ appendHeader(headerName, headerNameOrObject[headerName]);
31
+ });
32
+ }
14
33
  }
15
- }
16
- appendHeader(headerName, headerValue) {
17
- if (typeof headerValue === "string") {
18
- this.headers.append(headerName, headerValue);
19
- }
20
- else if (Array.isArray(headerValue)) {
21
- headerValue.forEach((v) => {
22
- this.headers.append(headerName, v);
23
- });
24
- }
25
- }
26
- append(headerNameOrObject, value) {
27
- if (typeof headerNameOrObject === "string") {
28
- this.appendHeader(headerNameOrObject, value);
29
- }
30
- else {
31
- Object.keys(headerNameOrObject).forEach((headerName) => {
32
- this.appendHeader(headerName, headerNameOrObject[headerName]);
33
- });
34
- }
35
- }
34
+ };
36
35
  }
@@ -1,10 +1,10 @@
1
- import { ERequest } from "./request";
2
- import { EResponse } from "./response";
3
- export declare type ErrorMiddlewareCallback = (err: Error, req: ERequest, res: EResponse) => Promise<any>;
1
+ import { EReq } from "./request";
2
+ import { ERes } from "./response";
3
+ export type ErrorMiddlewareCallback = (err: Error, req: EReq, res: ERes) => Promise<any>;
4
4
  export declare class ErrorMiddleware {
5
5
  private matchFn;
6
6
  private callback;
7
7
  constructor(matchFn: Function, callback: ErrorMiddlewareCallback);
8
- check(event: ERequest): 0 | 404 | string[];
9
- run(err: Error, req: ERequest, res: EResponse): Promise<any>;
8
+ check(event: EReq): 0 | 404 | string[];
9
+ run(err: Error, req: EReq, res: ERes): Promise<any>;
10
10
  }
@@ -1,14 +1,11 @@
1
1
  /// <reference types="@fastly/js-compute" />
2
- import { ECommonObject } from "../common";
3
2
  import { CookieMap } from "./cookie-map";
4
3
  import { EConfig } from "..";
5
- export declare class ERequest extends ECommonObject {
4
+ declare class ERequestBase extends Request {
6
5
  private config;
7
- private event;
6
+ private readonly event;
8
7
  readonly clientInfo: ClientInfo;
9
- readonly method: string;
10
- headers: Headers;
11
- url: URL;
8
+ urlObj: URL;
12
9
  query: URLSearchParams;
13
10
  params: {
14
11
  [key: string]: string;
@@ -21,7 +18,17 @@ export declare class ERequest extends ECommonObject {
21
18
  get secure(): boolean;
22
19
  get subdomains(): Array<string>;
23
20
  get hostname(): string;
24
- json(): Promise<any>;
25
- text(): Promise<string>;
26
- arrayBuffer(): Promise<ArrayBuffer>;
27
21
  }
22
+ export declare const ERequest: {
23
+ new (...args: any[]): {
24
+ [x: string]: any;
25
+ set(headerNameOrObject: string | {
26
+ [key: string]: string;
27
+ }, value?: string): void;
28
+ append(headerNameOrObject: string | {
29
+ [key: string]: string | string[];
30
+ }, value?: string | string[]): void;
31
+ };
32
+ } & typeof ERequestBase;
33
+ export type EReq = InstanceType<typeof ERequest>;
34
+ export {};
@@ -1,16 +1,19 @@
1
- import { ECommonObject } from "../common";
1
+ import { addCommonMethods } from "../common";
2
2
  import { CookieMap } from "./cookie-map";
3
- export class ERequest extends ECommonObject {
3
+ class ERequestBase extends Request {
4
4
  constructor(config, event) {
5
- super();
5
+ super(event.request);
6
6
  this.config = config;
7
7
  this.event = event;
8
8
  this.params = {};
9
- this.clientInfo = event.client;
10
- this.method = event.request.method;
11
- this.url = new URL(event.request.url);
12
- this.query = this.url.searchParams;
13
- this.headers = event.request.headers;
9
+ this.clientInfo = this.event.client;
10
+ this.urlObj = new URL(this.url);
11
+ this.query = this.urlObj.searchParams;
12
+ Object.defineProperty(this, 'url', {
13
+ get() {
14
+ return this.urlObj.toString();
15
+ }
16
+ });
14
17
  // Parse cookies.
15
18
  if (this.config.parseCookie) {
16
19
  this.cookies = new CookieMap(this.headers);
@@ -18,30 +21,22 @@ export class ERequest extends ECommonObject {
18
21
  }
19
22
  // Express-like URL helpers.
20
23
  get path() {
21
- return this.url.pathname;
24
+ return this.urlObj.pathname;
22
25
  }
23
26
  get ip() {
24
27
  return this.clientInfo.address;
25
28
  }
26
29
  get protocol() {
27
- return this.url.protocol;
30
+ return this.urlObj.protocol;
28
31
  }
29
32
  get secure() {
30
- return this.url.protocol === "https";
33
+ return this.urlObj.protocol === "https";
31
34
  }
32
35
  get subdomains() {
33
- return this.url.hostname.split(".").slice(0, -2);
36
+ return this.urlObj.hostname.split(".").slice(0, -2);
34
37
  }
35
38
  get hostname() {
36
- return this.url.hostname;
37
- }
38
- async json() {
39
- return await this.event.request.json();
40
- }
41
- async text() {
42
- return await this.event.request.text();
43
- }
44
- async arrayBuffer() {
45
- return await this.event.request.arrayBuffer();
39
+ return this.urlObj.hostname;
46
40
  }
47
41
  }
42
+ export const ERequest = addCommonMethods(ERequestBase);
@@ -1,10 +1,10 @@
1
- import { ERequest } from "./request";
2
- import { EResponse } from "./response";
3
- export declare type RequestHandlerCallback = (req: ERequest, res: EResponse) => Promise<any>;
1
+ import { EReq } from "./request";
2
+ import { ERes } from "./response";
3
+ export type RequestHandlerCallback = (req: EReq, res: ERes) => Promise<any>;
4
4
  export declare class RequestHandler {
5
5
  private matchFn;
6
6
  private callback;
7
7
  constructor(matchFn: Function, callback: RequestHandlerCallback);
8
- check(event: ERequest): 0 | 404 | string[];
9
- run(req: ERequest, res: EResponse): Promise<any>;
8
+ check(event: EReq): 0 | 404 | string[];
9
+ run(req: EReq, res: ERes): Promise<any>;
10
10
  }
@@ -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,10 +1,10 @@
1
1
  /// <reference types="@fastly/js-compute" />
2
- import { ECommonObject } from "../common";
3
2
  import { SurrogateKeys } from "./surrogate-keys";
3
+ import { EHeaders } from "./headers";
4
4
  import { CookieOptions, EConfig } from "..";
5
- export declare class EResponse extends ECommonObject {
5
+ declare class EResponseBase {
6
6
  private config;
7
- headers: Headers;
7
+ headers: EHeaders;
8
8
  status: number;
9
9
  body: BodyInit;
10
10
  hasEnded: boolean;
@@ -22,3 +22,16 @@ export declare class EResponse extends ECommonObject {
22
22
  text(data: string): void;
23
23
  html(data: string, charset?: string): void;
24
24
  }
25
+ export declare const EResponse: {
26
+ new (...args: any[]): {
27
+ [x: string]: any;
28
+ set(headerNameOrObject: string | {
29
+ [key: string]: string;
30
+ }, value?: string): void;
31
+ append(headerNameOrObject: string | {
32
+ [key: string]: string | string[];
33
+ }, value?: string | string[]): void;
34
+ };
35
+ } & typeof EResponseBase;
36
+ export type ERes = InstanceType<typeof EResponse>;
37
+ export {};
@@ -1,12 +1,12 @@
1
1
  import cookie from "cookie";
2
- import { ECommonObject } from "../common";
2
+ import { addCommonMethods } from "../common";
3
3
  import { statusText } from "./status-codes";
4
4
  import { SurrogateKeys } from "./surrogate-keys";
5
- export class EResponse extends ECommonObject {
5
+ import { EHeaders } from "./headers";
6
+ class EResponseBase {
6
7
  constructor(config) {
7
- super();
8
8
  this.config = config;
9
- this.headers = new Headers();
9
+ this.headers = new EHeaders();
10
10
  this.status = 0;
11
11
  this.body = null;
12
12
  this.hasEnded = false;
@@ -25,7 +25,7 @@ export class EResponse extends ECommonObject {
25
25
  clearCookie(key, options = {}) {
26
26
  if (this.hasEnded)
27
27
  return;
28
- 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") });
29
29
  }
30
30
  // Response lifecycle methods.
31
31
  send(response) {
@@ -33,8 +33,10 @@ export class EResponse extends ECommonObject {
33
33
  return;
34
34
  if (response instanceof Response) {
35
35
  this.body = response.body;
36
- this.headers = response.headers;
37
- this.status = response.status;
36
+ // Append, rather than overwrite headers.
37
+ response.headers.forEach((value, key) => this.headers.append(key, value));
38
+ // Do not overwrite user-defined status.
39
+ this.status = this.status || response.status;
38
40
  }
39
41
  else {
40
42
  this.body = response;
@@ -99,3 +101,4 @@ export class EResponse extends ECommonObject {
99
101
  this.send(data);
100
102
  }
101
103
  }
104
+ export const EResponse = addCommonMethods(EResponseBase);
@@ -13,7 +13,40 @@ const defaultErrorHandler = (auto405) => async (err, req, res) => {
13
13
  res.headers.set("Allow", err.allow);
14
14
  return res.sendStatus(405);
15
15
  }
16
- res.withStatus(500).json({ error: err });
16
+ console.error(err);
17
+ res.withStatus(500).json({ error: err.message });
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);
17
50
  };
18
51
  export class Router {
19
52
  constructor(config) {
@@ -23,12 +56,15 @@ export class Router {
23
56
  parseCookie: true,
24
57
  auto405: true,
25
58
  extractRequestParameters: true,
26
- autoContentType: false
59
+ autoContentType: false,
27
60
  };
28
61
  this.config = {
29
62
  ...this.config,
30
63
  ...config
31
64
  };
65
+ if (this.config.autoCorsPreflight) {
66
+ this.options("*", preflightHandler(this.config.autoCorsPreflight));
67
+ }
32
68
  }
33
69
  listen() {
34
70
  addEventListener("fetch", (event) => event.respondWith(this.handler(event)));
@@ -140,7 +176,7 @@ export class Router {
140
176
  pathMatcherCache.set(pattern, match(pattern, { decode: decodeURIComponent }));
141
177
  }
142
178
  // Match on pathname.
143
- let { path, params } = pathMatcherCache.get(pattern)(req.url.pathname) || {};
179
+ let { path, params } = pathMatcherCache.get(pattern)(req.urlObj.pathname) || {};
144
180
  if (path) {
145
181
  if (this.config.extractRequestParameters) {
146
182
  req.params = params;
@@ -153,13 +189,16 @@ export class Router {
153
189
  };
154
190
  }
155
191
  static serializeResponse(res) {
156
- // Default to 200 / 204 if no status was set by middleware / route handler.
157
- if (res.status === 0) {
158
- res.status = Boolean(res.body) ? 200 : 204;
159
- }
160
- return new Response(res.body, {
192
+ const response = new Response(res.body, {
161
193
  headers: res.headers,
162
- 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,
163
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;
164
203
  }
165
204
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastly/expressly",
3
- "version": "1.0.1-0",
3
+ "version": "1.1.0",
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",
@@ -24,26 +24,25 @@
24
24
  "router"
25
25
  ],
26
26
  "engines": {
27
- "node": "^16"
27
+ "node": ">=18"
28
28
  },
29
29
  "author": "oss@fastly.com",
30
30
  "license": "MIT",
31
31
  "devDependencies": {
32
- "@types/node": "^17.0.40",
33
- "auto": "^10.37.1",
34
- "husky": "^8.0.1",
35
- "nodemon": "^2.0.16",
36
- "prettier": "^2.6.2",
32
+ "@types/node": "^18.11.18",
33
+ "auto": "^10.37.6",
34
+ "husky": "^8.0.3",
35
+ "prettier": "^2.8.2",
37
36
  "pretty-quick": "^3.1.3",
38
- "ts-loader": "^9.3.0",
39
- "typescript": "^4.7.3",
40
- "webpack": "^5.73.0",
41
- "webpack-cli": "^4.9.2"
37
+ "ts-loader": "^9.4.2",
38
+ "typescript": "^4.9.4",
39
+ "webpack": "^5.75.0",
40
+ "webpack-cli": "^5.0.1"
42
41
  },
43
42
  "dependencies": {
44
- "@fastly/js-compute": "^0.2.4",
43
+ "@fastly/js-compute": "^1.1.0",
45
44
  "cookie": "^0.5.0",
46
- "core-js": "^3.22.8",
45
+ "core-js": "^3.27.1",
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
  }