├── .gitignore
├── examples
├── graphql
│ ├── .gitignore
│ ├── fastly.toml
│ ├── package.json
│ ├── webpack.config.js
│ ├── src
│ │ ├── index.js
│ │ └── schema.js
│ └── package-lock.json
├── ab-testing
│ ├── .gitignore
│ ├── fastly.toml
│ ├── package.json
│ ├── webpack.config.js
│ └── src
│ │ └── index.js
├── middleware
│ ├── .gitignore
│ ├── fastly.toml
│ ├── src
│ │ └── index.js
│ ├── package.json
│ └── webpack.config.js
├── templating
│ ├── .gitignore
│ ├── fastly.toml
│ ├── src
│ │ ├── templates
│ │ │ ├── about.html
│ │ │ └── home.html
│ │ └── index.js
│ ├── package.json
│ └── webpack.config.js
├── ab-testing-proxy
│ ├── .gitignore
│ ├── fastly.toml
│ ├── package.json
│ ├── webpack.config.js
│ └── src
│ │ └── index.js
├── basic-routing
│ ├── .gitignore
│ ├── fastly.toml
│ ├── package.json
│ ├── webpack.config.js
│ └── src
│ │ └── index.js
├── content-stitching
│ ├── .gitignore
│ ├── fastly.toml
│ ├── src
│ │ └── index.js
│ ├── package.json
│ └── webpack.config.js
├── jwt-authentication
│ ├── .gitignore
│ ├── local-dict-config.json
│ ├── fastly.toml
│ ├── webpack.config.js
│ ├── package.json
│ └── src
│ │ └── index.js
├── typescript-example
│ ├── .gitignore
│ ├── fastly.toml
│ ├── tsconfig.json
│ ├── webpack.config.js
│ ├── package.json
│ └── src
│ │ └── index.ts
└── content-stitching-with-ab-testing
│ ├── .gitignore
│ ├── fastly.toml
│ ├── package.json
│ ├── webpack.config.js
│ └── src
│ └── index.js
├── src
├── index.ts
└── lib
│ ├── routing
│ ├── route.ts
│ ├── middleware.ts
│ ├── request.ts
│ ├── response.ts
│ └── router.ts
│ └── utilities
│ └── fetcher.ts
├── tsconfig.json
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
--------------------------------------------------------------------------------
/examples/graphql/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/ab-testing/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/middleware/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/templating/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/ab-testing-proxy/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/basic-routing/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/content-stitching/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/jwt-authentication/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/typescript-example/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/content-stitching-with-ab-testing/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/jwt-authentication/local-dict-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "secret": "this is my super secret jwt signing key"
3 | }
--------------------------------------------------------------------------------
/examples/graphql/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "graphql-example"
9 | service_id = ""
10 |
--------------------------------------------------------------------------------
/examples/ab-testing/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "ab-testing-example"
9 | service_id = ""
10 |
--------------------------------------------------------------------------------
/examples/middleware/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "middleware-example"
9 | service_id = ""
10 |
--------------------------------------------------------------------------------
/examples/templating/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "middleware-example"
9 | service_id = ""
10 |
--------------------------------------------------------------------------------
/examples/templating/src/templates/about.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{title}}
4 |
5 |
6 |
7 | {{title}}
8 |
9 | {{#segments}}
10 | {{name}}
11 | {{description}}
12 | {{/segments}}
13 |
14 |
15 | Current time: {{time}}
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Router } from "./lib/routing/router";
2 | import { Fetcher } from "./lib/utilities/fetcher";
3 | import FPRequest from "./lib/routing/request";
4 | import FPResponse from "./lib/routing/response";
5 | import { MiddlewareCallback, Middleware } from "./lib/routing/middleware";
6 |
7 | export {
8 | Router,
9 | Fetcher,
10 | FPRequest,
11 | FPResponse,
12 | MiddlewareCallback,
13 | Middleware,
14 | };
15 |
--------------------------------------------------------------------------------
/examples/jwt-authentication/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "basic-routing"
9 | service_id = ""
10 |
11 | [local_server.dictionaries.config]
12 | file = "./local-dict-config.json"
13 | format = "json"
--------------------------------------------------------------------------------
/examples/basic-routing/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "basic-routing"
9 | service_id = ""
10 |
11 |
12 | [local_server.backends]
13 | [local_server.backends.my-origin]
14 | url = "https://fastly.com"
--------------------------------------------------------------------------------
/examples/typescript-example/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "basic-routing"
9 | service_id = ""
10 |
11 |
12 | [local_server.backends]
13 | [local_server.backends.my-origin]
14 | url = "https://fastly.com"
--------------------------------------------------------------------------------
/examples/ab-testing-proxy/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "ab-testing-proxy"
9 | service_id = ""
10 |
11 |
12 | [local_server.backends]
13 | [local_server.backends.main_origin]
14 | url = "https://httpbin.org"
--------------------------------------------------------------------------------
/examples/typescript-example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./bin/",
4 | "rootDir": ".",
5 | "sourceMap": false,
6 | "noImplicitAny": false,
7 | "module": "es6",
8 | "target": "es2019",
9 | "esModuleInterop": true,
10 | "allowJs": true,
11 | "moduleResolution": "node",
12 | "typeRoots": ["./node_modules/@fastly"],
13 | "forceConsistentCasingInFileNames": true,
14 | "declaration": true,
15 | },
16 | "exclude": ["dist/", "examples/"]
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./dist/",
4 | "rootDir": "./src",
5 | "sourceMap": false,
6 | "noImplicitAny": false,
7 | "module": "es6",
8 | "target": "es2019",
9 | "esModuleInterop": true,
10 | "allowJs": true,
11 | "moduleResolution": "node",
12 | "typeRoots": ["./node_modules/@fastly", "./node_modules/@types"],
13 | "forceConsistentCasingInFileNames": true,
14 | "declaration": true,
15 | },
16 | "exclude": ["dist/", "examples/"]
17 | }
18 |
--------------------------------------------------------------------------------
/src/lib/routing/route.ts:
--------------------------------------------------------------------------------
1 | import FPRequest from "./request";
2 | import FPResponse from "./response";
3 |
4 | export type RequestHandlerCallback = (
5 | req: FPRequest,
6 | res: FPResponse
7 | ) => Promise;
8 |
9 | export class Route {
10 | constructor(
11 | private matchFn: Function,
12 | private callback: RequestHandlerCallback
13 | ) {}
14 |
15 | public check(event: FPRequest): boolean {
16 | return this.matchFn(event);
17 | }
18 |
19 | public async run(req: FPRequest, res: FPResponse): Promise {
20 | await this.callback(req, res);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/templating/src/templates/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{title}}
4 |
5 |
6 |
7 | {{title}}
8 |
9 | {{#bold}}Lorem, ipsum{{/bold}} dolor sit amet consectetur adipisicing elit. Unde recusandae,
10 | explicabo vero voluptatem impedit iusto adipisci autem veritatis. Voluptas
11 | quaerat dignissimos eligendi incidunt. Mollitia ad at nam aspernatur
12 | dolorum eaque?
13 |
14 |
15 | This page is powered by {{framework}}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/examples/content-stitching/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "content-stitching"
9 | service_id = ""
10 |
11 |
12 | [local_server.backends]
13 | [local_server.backends.header_service]
14 | url = "https://content-stitching-header.edgecompute.app/"
15 | [local_server.backends.page_service]
16 | url = "https://content-stitching-page.edgecompute.app/"
--------------------------------------------------------------------------------
/examples/content-stitching-with-ab-testing/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["oss@fastly.com"]
5 | description = "Compute example app"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "content-stitching"
9 | service_id = ""
10 |
11 |
12 | [local_server.backends]
13 | [local_server.backends.header_service]
14 | url = "https://content-stitching-header.edgecompute.app/"
15 | [local_server.backends.page_service]
16 | url = "https://content-stitching-page.edgecompute.app/"
--------------------------------------------------------------------------------
/src/lib/routing/middleware.ts:
--------------------------------------------------------------------------------
1 | import FPRequest from "./request";
2 | import FPResponse from "./response";
3 |
4 | export type MiddlewareCallback = (
5 | req: FPRequest,
6 | res: FPResponse,
7 | next?: () => void
8 | ) => Promise;
9 |
10 | export class Middleware {
11 | constructor(
12 | private matchFn: Function,
13 | private callback: MiddlewareCallback
14 | ) {}
15 |
16 | public check(event: FPRequest): boolean {
17 | return this.matchFn(event);
18 | }
19 |
20 | public async run(req: FPRequest, res: FPResponse): Promise {
21 | // Supply an empty callback which would normally be next() in express
22 | await this.callback(req, res, () => {});
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/typescript-example/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | stats: { errorDetails: true },
5 | target: "webworker",
6 | output: {
7 | path: path.join(process.cwd(), "bin"),
8 | filename: "index.js",
9 | },
10 | // mode: 'production',
11 | mode: "production",
12 | devtool: "cheap-module-source-map",
13 | optimization: {
14 | sideEffects: true,
15 | minimize: false
16 | },
17 | resolve: {
18 | extensions: [".ts", ".js", ".json"],
19 | fallback: {
20 | url: require.resolve("core-js/"),
21 | },
22 | },
23 | module: {
24 | rules: [
25 | {
26 | test: /\.ts?$/,
27 | use: "ts-loader",
28 | exclude: "/node_modules/",
29 | },
30 | ],
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/examples/middleware/src/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from "../../../dist/index.js";
2 |
3 | const router = new Router();
4 |
5 | /**
6 | * Add an x-powered-by header to every request
7 | */
8 | router.use((req, res) => {
9 | res.setHeader("x-powered-by", "FlightPath");
10 | });
11 |
12 | /**
13 | * incremement the "visitiedcount" cookie on every request
14 | */
15 | router.use("/cookies", (req, res) => {
16 | res.cookie("visitcount", (Number(req.cookies.visitcount) + 1) | 0, {
17 | maxAge: 60 * 60 * 24 * 7, // 1 week
18 | });
19 |
20 | res.cookie("hello", "world");
21 | });
22 |
23 | router.use("*", (req, res) => {
24 | res.end("This content is served from middleware")
25 | });
26 |
27 | router.route("GET", "/", (req, res) => {
28 | return res.send("This is never show as the middleware above calls `end`");
29 | });
30 |
31 | router.listen();
32 |
--------------------------------------------------------------------------------
/examples/content-stitching/src/index.js:
--------------------------------------------------------------------------------
1 | import { Router, Fetcher } from "../../../dist/index.js";
2 |
3 | const router = new Router();
4 |
5 | router.get("*", async (req, res) => {
6 | const fetcher = new Fetcher({
7 | header: {
8 | url: "https://content-stitching-header.edgecompute.app/",
9 | backend: "header_service"
10 | },
11 | page: {
12 | url: "https://content-stitching-page.edgecompute.app/",
13 | backend: "page_service"
14 | }
15 | })
16 |
17 | let data = await fetcher.fetch();
18 |
19 | res.send(`
20 |
21 |
22 | Content Stitching
23 |
24 |
25 |
26 | ${data.header.data}
27 |
28 | ${data.page.data}
29 |
30 |
31 | `)
32 | });
33 |
34 | // Listen for requests
35 | router.listen();
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flight-path",
3 | "version": "1.0.13",
4 | "description": "Express style router for Fastly Compute@Edge",
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/williamoverton/FlightPath"
10 | },
11 | "scripts": {
12 | "build": "tsc",
13 | "prepublish": "tsc",
14 | "dev": "nodemon --exec \"tsc\""
15 | },
16 | "keywords": [
17 | "fastly",
18 | "c@e",
19 | "compute",
20 | "edge",
21 | "router"
22 | ],
23 | "author": "",
24 | "license": "ISC",
25 | "devDependencies": {
26 | "@types/node": "^17.0.10",
27 | "nodemon": "^2.0.14",
28 | "ts-loader": "^9.2.6",
29 | "typescript": "^4.4.4",
30 | "webpack": "^5.65.0",
31 | "webpack-cli": "^4.9.1"
32 | },
33 | "dependencies": {
34 | "@fastly/js-compute": "^0.2.0",
35 | "cookie": "^0.4.1",
36 | "core-js": "^3.19.0",
37 | "mustache": "^4.2.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/jwt-authentication/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
4 |
5 | module.exports = {
6 | entry: "./src/index.js",
7 | mode: "production",
8 | optimization: {
9 | minimize: false,
10 | },
11 | target: "webworker",
12 | output: {
13 | filename: "index.js",
14 | path: path.resolve(__dirname, "bin"),
15 | libraryTarget: "this",
16 | },
17 | module: {
18 | // Asset modules are modules that allow the use asset files (fonts, icons, etc)
19 | // without additional configuration or dependencies.
20 | rules: [
21 | // asset/source exports the source code of the asset.
22 | // Usage: e.g., import notFoundPage from "./page_404.html"
23 | {
24 | test: /\.(txt|html)/,
25 | type: "asset/source",
26 | },
27 | ],
28 | },
29 | plugins: [
30 | new NodePolyfillPlugin({
31 | excludeAliases: ["console"]
32 | })
33 | ]
34 | };
35 |
--------------------------------------------------------------------------------
/examples/ab-testing/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "core-js": "^3.19.0",
17 | "nodemon": "^2.0.14",
18 | "webpack": "^5.10.0",
19 | "webpack-cli": "^4.2.0"
20 | },
21 | "dependencies": {
22 | "@fastly/js-compute": "^0.2.0"
23 | },
24 | "scripts": {
25 | "prebuild": "webpack",
26 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
27 | "deploy": "npm run build && fastly compute deploy",
28 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/middleware/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "core-js": "^3.19.0",
17 | "nodemon": "^2.0.14",
18 | "webpack": "^5.10.0",
19 | "webpack-cli": "^4.2.0"
20 | },
21 | "dependencies": {
22 | "@fastly/js-compute": "^0.2.0"
23 | },
24 | "scripts": {
25 | "prebuild": "webpack",
26 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
27 | "deploy": "npm run build && fastly compute deploy",
28 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/templating/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "core-js": "^3.19.0",
17 | "nodemon": "^2.0.14",
18 | "webpack": "^5.10.0",
19 | "webpack-cli": "^4.2.0"
20 | },
21 | "dependencies": {
22 | "@fastly/js-compute": "^0.2.0"
23 | },
24 | "scripts": {
25 | "prebuild": "webpack",
26 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
27 | "deploy": "npm run build && fastly compute deploy",
28 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/basic-routing/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "core-js": "^3.19.0",
17 | "nodemon": "^2.0.14",
18 | "webpack": "^5.10.0",
19 | "webpack-cli": "^4.2.0"
20 | },
21 | "dependencies": {
22 | "@fastly/js-compute": "^0.2.0"
23 | },
24 | "scripts": {
25 | "prebuild": "webpack",
26 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
27 | "deploy": "npm run build && fastly compute deploy",
28 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/ab-testing-proxy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "core-js": "^3.19.0",
17 | "nodemon": "^2.0.14",
18 | "webpack": "^5.10.0",
19 | "webpack-cli": "^4.2.0"
20 | },
21 | "dependencies": {
22 | "@fastly/js-compute": "^0.2.0"
23 | },
24 | "scripts": {
25 | "prebuild": "webpack",
26 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
27 | "deploy": "npm run build && fastly compute deploy",
28 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/content-stitching/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "core-js": "^3.19.0",
17 | "nodemon": "^2.0.14",
18 | "webpack": "^5.10.0",
19 | "webpack-cli": "^4.2.0"
20 | },
21 | "dependencies": {
22 | "@fastly/js-compute": "^0.2.0"
23 | },
24 | "scripts": {
25 | "prebuild": "webpack",
26 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
27 | "deploy": "npm run build && fastly compute deploy",
28 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/content-stitching-with-ab-testing/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "core-js": "^3.19.0",
17 | "nodemon": "^2.0.14",
18 | "webpack": "^5.10.0",
19 | "webpack-cli": "^4.2.0"
20 | },
21 | "dependencies": {
22 | "@fastly/js-compute": "^0.2.0"
23 | },
24 | "scripts": {
25 | "prebuild": "webpack",
26 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
27 | "deploy": "npm run build && fastly compute deploy",
28 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/graphql/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "core-js": "^3.19.0",
17 | "nodemon": "^2.0.14",
18 | "webpack": "^5.10.0",
19 | "webpack-cli": "^4.2.0"
20 | },
21 | "dependencies": {
22 | "@fastly/js-compute": "^0.2.0",
23 | "graphql-helix": "^1.9.1"
24 | },
25 | "scripts": {
26 | "prebuild": "webpack",
27 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
28 | "deploy": "npm run build && fastly compute deploy",
29 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/examples/graphql/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 |
4 | module.exports = {
5 | entry: "./src/index.js",
6 | mode: "production",
7 | optimization: {
8 | minimize: false
9 | },
10 | target: "webworker",
11 | output: {
12 | filename: "index.js",
13 | path: path.resolve(__dirname, "bin"),
14 | libraryTarget: "this",
15 | },
16 | module: {
17 | // Asset modules are modules that allow the use asset files (fonts, icons, etc)
18 | // without additional configuration or dependencies.
19 | rules: [
20 | // asset/source exports the source code of the asset.
21 | // Usage: e.g., import notFoundPage from "./page_404.html"
22 | {
23 | test: /\.(txt|html)/,
24 | type: "asset/source",
25 | },
26 | ],
27 | },
28 | plugins: [
29 | // Polyfills go here.
30 | // Used for, e.g., any cross-platform WHATWG,
31 | // or core nodejs modules needed for your application.
32 | new webpack.ProvidePlugin({
33 | URL: "core-js/web/url",
34 | }),
35 | ],
36 | };
37 |
--------------------------------------------------------------------------------
/examples/ab-testing/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 |
4 | module.exports = {
5 | entry: "./src/index.js",
6 | mode: "production",
7 | optimization: {
8 | minimize: false
9 | },
10 | target: "webworker",
11 | output: {
12 | filename: "index.js",
13 | path: path.resolve(__dirname, "bin"),
14 | libraryTarget: "this",
15 | },
16 | module: {
17 | // Asset modules are modules that allow the use asset files (fonts, icons, etc)
18 | // without additional configuration or dependencies.
19 | rules: [
20 | // asset/source exports the source code of the asset.
21 | // Usage: e.g., import notFoundPage from "./page_404.html"
22 | {
23 | test: /\.(txt|html)/,
24 | type: "asset/source",
25 | },
26 | ],
27 | },
28 | plugins: [
29 | // Polyfills go here.
30 | // Used for, e.g., any cross-platform WHATWG,
31 | // or core nodejs modules needed for your application.
32 | new webpack.ProvidePlugin({
33 | URL: "core-js/web/url",
34 | }),
35 | ],
36 | };
37 |
--------------------------------------------------------------------------------
/examples/basic-routing/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 |
4 | module.exports = {
5 | entry: "./src/index.js",
6 | mode: "production",
7 | optimization: {
8 | minimize: false
9 | },
10 | target: "webworker",
11 | output: {
12 | filename: "index.js",
13 | path: path.resolve(__dirname, "bin"),
14 | libraryTarget: "this",
15 | },
16 | module: {
17 | // Asset modules are modules that allow the use asset files (fonts, icons, etc)
18 | // without additional configuration or dependencies.
19 | rules: [
20 | // asset/source exports the source code of the asset.
21 | // Usage: e.g., import notFoundPage from "./page_404.html"
22 | {
23 | test: /\.(txt|html)/,
24 | type: "asset/source",
25 | },
26 | ],
27 | },
28 | plugins: [
29 | // Polyfills go here.
30 | // Used for, e.g., any cross-platform WHATWG,
31 | // or core nodejs modules needed for your application.
32 | new webpack.ProvidePlugin({
33 | URL: "core-js/web/url",
34 | }),
35 | ],
36 | };
37 |
--------------------------------------------------------------------------------
/examples/middleware/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 |
4 | module.exports = {
5 | entry: "./src/index.js",
6 | mode: "production",
7 | optimization: {
8 | minimize: false
9 | },
10 | target: "webworker",
11 | output: {
12 | filename: "index.js",
13 | path: path.resolve(__dirname, "bin"),
14 | libraryTarget: "this",
15 | },
16 | module: {
17 | // Asset modules are modules that allow the use asset files (fonts, icons, etc)
18 | // without additional configuration or dependencies.
19 | rules: [
20 | // asset/source exports the source code of the asset.
21 | // Usage: e.g., import notFoundPage from "./page_404.html"
22 | {
23 | test: /\.(txt|html)/,
24 | type: "asset/source",
25 | },
26 | ],
27 | },
28 | plugins: [
29 | // Polyfills go here.
30 | // Used for, e.g., any cross-platform WHATWG,
31 | // or core nodejs modules needed for your application.
32 | new webpack.ProvidePlugin({
33 | URL: "core-js/web/url",
34 | }),
35 | ],
36 | };
37 |
--------------------------------------------------------------------------------
/examples/templating/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 |
4 | module.exports = {
5 | entry: "./src/index.js",
6 | mode: "production",
7 | optimization: {
8 | minimize: false
9 | },
10 | target: "webworker",
11 | output: {
12 | filename: "index.js",
13 | path: path.resolve(__dirname, "bin"),
14 | libraryTarget: "this",
15 | },
16 | module: {
17 | // Asset modules are modules that allow the use asset files (fonts, icons, etc)
18 | // without additional configuration or dependencies.
19 | rules: [
20 | // asset/source exports the source code of the asset.
21 | // Usage: e.g., import notFoundPage from "./page_404.html"
22 | {
23 | test: /\.(txt|html)/,
24 | type: "asset/source",
25 | },
26 | ],
27 | },
28 | plugins: [
29 | // Polyfills go here.
30 | // Used for, e.g., any cross-platform WHATWG,
31 | // or core nodejs modules needed for your application.
32 | new webpack.ProvidePlugin({
33 | URL: "core-js/web/url",
34 | }),
35 | ],
36 | };
37 |
--------------------------------------------------------------------------------
/examples/ab-testing-proxy/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 |
4 | module.exports = {
5 | entry: "./src/index.js",
6 | mode: "production",
7 | optimization: {
8 | minimize: false
9 | },
10 | target: "webworker",
11 | output: {
12 | filename: "index.js",
13 | path: path.resolve(__dirname, "bin"),
14 | libraryTarget: "this",
15 | },
16 | module: {
17 | // Asset modules are modules that allow the use asset files (fonts, icons, etc)
18 | // without additional configuration or dependencies.
19 | rules: [
20 | // asset/source exports the source code of the asset.
21 | // Usage: e.g., import notFoundPage from "./page_404.html"
22 | {
23 | test: /\.(txt|html)/,
24 | type: "asset/source",
25 | },
26 | ],
27 | },
28 | plugins: [
29 | // Polyfills go here.
30 | // Used for, e.g., any cross-platform WHATWG,
31 | // or core nodejs modules needed for your application.
32 | new webpack.ProvidePlugin({
33 | URL: "core-js/web/url",
34 | }),
35 | ],
36 | };
37 |
--------------------------------------------------------------------------------
/examples/content-stitching/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 |
4 | module.exports = {
5 | entry: "./src/index.js",
6 | mode: "production",
7 | optimization: {
8 | minimize: false
9 | },
10 | target: "webworker",
11 | output: {
12 | filename: "index.js",
13 | path: path.resolve(__dirname, "bin"),
14 | libraryTarget: "this",
15 | },
16 | module: {
17 | // Asset modules are modules that allow the use asset files (fonts, icons, etc)
18 | // without additional configuration or dependencies.
19 | rules: [
20 | // asset/source exports the source code of the asset.
21 | // Usage: e.g., import notFoundPage from "./page_404.html"
22 | {
23 | test: /\.(txt|html)/,
24 | type: "asset/source",
25 | },
26 | ],
27 | },
28 | plugins: [
29 | // Polyfills go here.
30 | // Used for, e.g., any cross-platform WHATWG,
31 | // or core nodejs modules needed for your application.
32 | new webpack.ProvidePlugin({
33 | URL: "core-js/web/url",
34 | }),
35 | ],
36 | };
37 |
--------------------------------------------------------------------------------
/examples/content-stitching-with-ab-testing/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 |
4 | module.exports = {
5 | entry: "./src/index.js",
6 | mode: "production",
7 | optimization: {
8 | minimize: false
9 | },
10 | target: "webworker",
11 | output: {
12 | filename: "index.js",
13 | path: path.resolve(__dirname, "bin"),
14 | libraryTarget: "this",
15 | },
16 | module: {
17 | // Asset modules are modules that allow the use asset files (fonts, icons, etc)
18 | // without additional configuration or dependencies.
19 | rules: [
20 | // asset/source exports the source code of the asset.
21 | // Usage: e.g., import notFoundPage from "./page_404.html"
22 | {
23 | test: /\.(txt|html)/,
24 | type: "asset/source",
25 | },
26 | ],
27 | },
28 | plugins: [
29 | // Polyfills go here.
30 | // Used for, e.g., any cross-platform WHATWG,
31 | // or core nodejs modules needed for your application.
32 | new webpack.ProvidePlugin({
33 | URL: "core-js/web/url",
34 | }),
35 | ],
36 | };
37 |
--------------------------------------------------------------------------------
/examples/typescript-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "core-js": "^3.19.0",
17 | "nodemon": "^2.0.14",
18 | "ts-loader": "^9.2.6",
19 | "typescript": "^4.5.2",
20 | "webpack": "^5.65.0",
21 | "webpack-cli": "^4.9.1"
22 | },
23 | "dependencies": {
24 | "@fastly/js-compute": "^0.2.1"
25 | },
26 | "scripts": {
27 | "prebuild": "webpack",
28 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
29 | "deploy": "npm run build && fastly compute deploy",
30 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/jwt-authentication/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compute-starter-kit-javascript-default",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/fastly/compute-starter-kit-js-proto.git"
8 | },
9 | "author": "oss@fastly.com",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/fastly/compute-starter-kit-js-proto/issues"
13 | },
14 | "homepage": "https://developer.fastly.com/solutions/starters/compute-starter-kit-javascript-default",
15 | "devDependencies": {
16 | "buffer": "^6.0.3",
17 | "core-js": "^3.19.3",
18 | "jws": "^4.0.0",
19 | "node-polyfill-webpack-plugin": "^1.1.4",
20 | "nodemon": "^2.0.14",
21 | "webpack": "^5.10.0",
22 | "webpack-cli": "^4.2.0"
23 | },
24 | "dependencies": {
25 | "@fastly/js-compute": "^0.2.0"
26 | },
27 | "scripts": {
28 | "prebuild": "webpack",
29 | "build": "js-compute-runtime --skip-pkg bin/index.js bin/main.wasm",
30 | "deploy": "npm run build && fastly compute deploy",
31 | "dev": "nodemon --exec \"npm run build && fastly compute serve --skip-build\""
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/templating/src/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from "../../../dist/index.js";
2 |
3 | const router = new Router({
4 | templatesDir: "templates",
5 | });
6 |
7 | /**
8 | * Add an x-powered-by header to every request
9 | */
10 | router.use((req, res) => {
11 | res.setHeader("x-powered-by", "FlightPath");
12 | });
13 |
14 | /**
15 | * Render index page using Mustache (https://github.com/janl/mustache.js)
16 | */
17 | router.route("GET", "/", (req, res) => {
18 | return res.render("home", {
19 | title: "Home Page!",
20 | framework: "FlightPath",
21 | bold: function () {
22 | return function (text, render) {
23 | return "" + render(text) + "";
24 | };
25 | },
26 | });
27 | });
28 |
29 | /**
30 | * Render about page
31 | */
32 | router.route("GET", "/about", (req, res) => {
33 | return res.render("about", {
34 | title: "About Page",
35 | segments: [
36 | {
37 | name: "First Thing",
38 | description: "This is the first thing",
39 | },
40 | {
41 | name: "Second Thing",
42 | description: "This is the second thing",
43 | },
44 | ],
45 | time: () => {
46 | return new Date().toISOString();
47 | },
48 | });
49 | });
50 |
51 | router.listen();
52 |
--------------------------------------------------------------------------------
/src/lib/utilities/fetcher.ts:
--------------------------------------------------------------------------------
1 | export class Fetcher {
2 | constructor(private sources: FetchSourceMap) {}
3 |
4 | public async fetch(): Promise {
5 | let results: FetchResultMap = {};
6 |
7 | await Promise.all(
8 | Object.keys(this.sources).map(async (sourceName) => {
9 | results[sourceName] = await this.getSourceData(
10 | this.sources[sourceName]
11 | );
12 | })
13 | );
14 |
15 | return results;
16 | }
17 |
18 | private async getSourceData(source: FetchSource): Promise {
19 | let request = await fetch(source.url, {
20 | backend: source.backend,
21 | method: source.method,
22 | body: source.body,
23 | headers: source.headers,
24 | cacheOverride: source.cache,
25 | });
26 |
27 | return {
28 | source: source,
29 | data: await request.text(),
30 | };
31 | }
32 | }
33 |
34 | type FetchSourceMap = { [key: string]: FetchSource };
35 |
36 | type FetchResultMap = { [key: string]: FetchResult };
37 |
38 | type FetchResult = {
39 | source: FetchSource;
40 | data: string;
41 | };
42 |
43 | type FetchSource = {
44 | url: string;
45 | backend: string;
46 | method?: string;
47 | body?: string;
48 | headers?: Headers;
49 | cache?: CacheOverride;
50 | };
51 |
--------------------------------------------------------------------------------
/examples/ab-testing/src/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from "../../../dist/index.js";
2 |
3 | const router = new Router();
4 |
5 | router.use((req, res) => {
6 |
7 | // Check if cookie is set for test
8 | if(!req.cookies.hasOwnProperty("abDarkMode") || req.cookies.abDarkMode === "") {
9 | const value = `${Math.random() > 0.5}`;
10 |
11 | // Set cookie on request object so it is available for this request
12 | req.cookies.abDarkMode = value;
13 |
14 | // Set cookie in browser
15 | res.cookie("abDarkMode", value, {
16 | maxAge: 60 * 60 // Keep cookie for 1 hour
17 | });
18 | }
19 | })
20 |
21 | router.get("/", (req, res) => {
22 | const classes = req.cookies.abDarkMode == "true" ? "text-white bg-dark" : "";
23 |
24 | res.send(`
25 |
26 |
27 | Flight Path
28 |
29 |
30 |
31 | Hello World!
32 |
33 | This is a basic example of A/B testing at the edge.
34 | Dark mode test: ${req.cookies.abDarkMode}
35 |
36 |
39 |
40 |
41 | `);
42 | });
43 |
44 | router.post("/clear-ab-test", (req, res) => {
45 | res.cookie("abDarkMode", "");
46 | res.redirect("/");
47 | })
48 |
49 | router.listen();
--------------------------------------------------------------------------------
/examples/graphql/src/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from "../../../dist/index.js";
2 | import {
3 | getGraphQLParameters,
4 | processRequest,
5 | renderGraphiQL,
6 | shouldRenderGraphiQL,
7 | sendResult,
8 | } from "graphql-helix";
9 | import { schema } from "./schema";
10 | import FPRequest from "../../../dist/lib/routing/request.js";
11 |
12 | const router = new Router();
13 |
14 | router.use("*", async (req, res) => {
15 | let body = {};
16 |
17 | try {
18 | body = await req.json();
19 | } catch (e) {}
20 |
21 | // Create a generic Request object that can be consumed by Graphql Helix's API
22 | const request = {
23 | body: body,
24 | headers: req.headers,
25 | method: req.method,
26 | query: req.query,
27 | };
28 |
29 | // Determine whether we should render GraphiQL instead of returning an API response
30 | if (shouldRenderGraphiQL(request)) {
31 | res.send(renderGraphiQL());
32 | } else {
33 | // Extract the Graphql parameters from the request
34 | const { operationName, query, variables } = getGraphQLParameters(request);
35 |
36 | // Validate and execute the query
37 | const result = await processRequest({
38 | operationName,
39 | query,
40 | variables,
41 | request,
42 | schema,
43 | });
44 |
45 | // processRequest returns one of three types of results depending on how the server should respond
46 | // 1) RESPONSE: a regular JSON payload
47 | // 2) MULTIPART RESPONSE: a multipart response (when @stream or @defer directives are used)
48 | // 3) PUSH: a stream of events to push back down the client for a subscription
49 | // The "sendResult" is a NodeJS-only shortcut for handling all possible types of Graphql responses,
50 | // See "Advanced Usage" below for more details and customizations available on that layer.
51 | sendResult(result, res);
52 | }
53 | });
54 |
55 | router.listen();
56 |
--------------------------------------------------------------------------------
/examples/graphql/src/schema.js:
--------------------------------------------------------------------------------
1 | import {
2 | GraphQLInt,
3 | GraphQLList,
4 | GraphQLObjectType,
5 | GraphQLSchema,
6 | GraphQLString,
7 | } from "graphql";
8 |
9 | export const schema = new GraphQLSchema({
10 | mutation: new GraphQLObjectType({
11 | name: "Mutation",
12 | fields: () => ({
13 | echo: {
14 | args: {
15 | text: {
16 | type: GraphQLString,
17 | },
18 | },
19 | type: GraphQLString,
20 | resolve: (_root, args) => {
21 | return args.text;
22 | },
23 | },
24 | }),
25 | }),
26 | query: new GraphQLObjectType({
27 | name: "Query",
28 | fields: () => ({
29 | alphabet: {
30 | type: new GraphQLList(GraphQLString),
31 | resolve: () => {
32 | return [
33 | "a",
34 | "b",
35 | "c",
36 | "d",
37 | "e",
38 | "f",
39 | "g",
40 | "h",
41 | "i",
42 | "j",
43 | "k",
44 | "l",
45 | "m",
46 | "n",
47 | "o",
48 | "p",
49 | "q",
50 | "r",
51 | "s",
52 | "t",
53 | "u",
54 | "v",
55 | "w",
56 | "x",
57 | "y",
58 | "z",
59 | ];
60 | },
61 | },
62 | song: {
63 | type: new GraphQLObjectType({
64 | name: "Song",
65 | fields: () => ({
66 | firstVerse: {
67 | type: GraphQLString,
68 | resolve: () => "Now I know my ABC's.",
69 | },
70 | secondVerse: {
71 | type: GraphQLString,
72 | resolve: () => "Next time won't you sing with me?",
73 | },
74 | }),
75 | }),
76 | resolve: () => ({}),
77 | },
78 | }),
79 | }),
80 | });
81 |
--------------------------------------------------------------------------------
/src/lib/routing/request.ts:
--------------------------------------------------------------------------------
1 | import cookie from "cookie";
2 |
3 | export default class FPRequest {
4 | readonly clientInfo: {} = {};
5 | private _headers: Headers;
6 | readonly method: string;
7 | url: URL;
8 | params: { [key: string]: string } = {};
9 | query: { [key: string]: string };
10 | private _cookies: {} = {};
11 | cookies: any = {};
12 |
13 | constructor(private config: any, private event: FetchEvent) {
14 | this.clientInfo = event.client;
15 | this._headers = event.request.headers;
16 | this.method = event.request.method;
17 | this.url = new URL(event.request.url);
18 | this.query = Object.fromEntries(this.url.searchParams.entries());
19 |
20 | if(config.parseCookies && this._headers.has("cookie")){
21 | this._cookies = cookie.parse(this._headers.get("cookie"));
22 | }
23 |
24 | // If a cookie is set on the req object, we need to re-serialize the `cookie` header incase the req is forwarded to an origin
25 | this.cookies = new Proxy(this._cookies, {
26 | get: (target, key) => {
27 | return target[key];
28 | },
29 | set: (target, key, value) => {
30 | target[key] = value;
31 | this.reSerializeCookies();
32 | return true;
33 | }
34 | });
35 | }
36 |
37 | private reSerializeCookies() {
38 | console.log("reSerializeCookies")
39 | this._headers.set("cookie", Object.entries(this._cookies).map(([key, value]) => `${key}=${value}`).join("; "));
40 | }
41 |
42 | get headers() {
43 | return Object.fromEntries(this._headers.entries())
44 | }
45 |
46 | setHeader(key: string, value: string): void {
47 | this._headers.set(key, value);
48 | }
49 |
50 | async json() {
51 | return await this.event.request.json();
52 | }
53 |
54 | async text() {
55 | return await this.event.request.text();
56 | }
57 |
58 | async arrayBuffer() {
59 | return await this.event.request.arrayBuffer();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/examples/ab-testing-proxy/src/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from "../../../dist/index.js";
2 |
3 | /**
4 | * In this demo we proxy requests to another origin but add cookies for AB testing.
5 | * You can add tests into the `tests` object below and they will be added via cookies.
6 | *
7 | * This can be very useful for ab testing either on the client or server side.
8 | */
9 |
10 | const router = new Router();
11 |
12 | const tests = [
13 | {
14 | name: "A/B Test 1",
15 | cookieName: "ab-test-1",
16 | percentage: 50,
17 | activeValue: "A",
18 | inactiveValue: "B",
19 | cookieOpts: {
20 | path: "/",
21 | },
22 | },
23 | {
24 | name: "A/B Test 2",
25 | cookieName: "ab-test-2",
26 | percentage: 20,
27 | activeValue: "A",
28 | inactiveValue: "B",
29 | cookieOpts: {
30 | maxAge: 60 * 60 * 24, // 1 day
31 | path: "/",
32 | },
33 | },
34 | ];
35 |
36 | // On every request we make sure the user is a part of all tests
37 | router.use((req, res) => {
38 | tests.map((test) => {
39 | // Only set cookie if it is not already set
40 | if (!(test.cookieName in req.cookies)) {
41 | let selectedValue =
42 | Math.random() < test.percentage / 100
43 | ? test.activeValue
44 | : test.inactiveValue;
45 |
46 | req.cookies[test.cookieName] = selectedValue;
47 | res.cookie(test.cookieName, selectedValue, test.cookieOpts);
48 | }
49 | });
50 | });
51 |
52 | const originHostname = "httpbin.org";
53 |
54 | router.all("*", async (req, res) => {
55 | // Change the hostname of the url to our origin
56 | req.url.host = originHostname;
57 |
58 | // remove port, defaulting to 443 or 80 depending on protocol.
59 | req.url.port = "";
60 |
61 | // Change Host header to the correct one for our origin
62 | req.setHeader("host", originHostname);
63 |
64 | // Make a request to the origin
65 | let originRequest = await fetch(req.url.toString(), {
66 | backend: "main_origin",
67 | headers: req.headers,
68 | method: req.method,
69 | body: await req.text(),
70 | });
71 |
72 | // Send response
73 | res.send(originRequest);
74 | });
75 |
76 | router.listen();
77 |
--------------------------------------------------------------------------------
/examples/content-stitching-with-ab-testing/src/index.js:
--------------------------------------------------------------------------------
1 | import { Router, Fetcher } from "../../../dist/index.js";
2 |
3 | // Create router object for binding routes to.
4 | const router = new Router();
5 |
6 | // Add middleware for all routes
7 | router.use((req, res) => {
8 |
9 | // Add a header to the response
10 | res.setHeader("x-powered-by", "Fastly C@E - Flight Path");
11 |
12 | // Set abtest cookie if it isn not already set
13 | if(!("abtest" in req.cookies)) {
14 | let value = Math.random() > 0.5 ? "a" : "b"; // 50% chance of being "a" or "b"
15 | res.cookie("abtest", value);
16 | req.cookies.abtest = value;
17 | }
18 | });
19 |
20 | // Bind to GET requests on /
21 | router.get("/", async (req,res) => {
22 |
23 | // Fetch data from our micro frontends
24 | const fetcher = new Fetcher({
25 | header: {
26 | url: "https://content-stitching-header.edgecompute.app/",
27 | backend: "header_service" // Backends configured in fastly.toml
28 | },
29 | body: {
30 | url: "https://content-stitching-page.edgecompute.app/",
31 | backend: "page_service", // Backends configured in fastly.toml
32 | cache: new CacheOverride("override", {
33 | ttl: 60
34 | })
35 | }
36 | })
37 |
38 | // Wait for fetch to complete
39 | let content = await fetcher.fetch();
40 |
41 | // Pick classes based on abtest cookie
42 | // This this test we are using the abtest cookie to determine if the user is in "Dark Mode" or not.
43 | let classes = req.cookies.abtest == "b" ? "bg-dark text-white" : "";
44 |
45 | res.setHeader("Content-Type", "text/html");
46 |
47 | // Render HTML
48 | res.send(`
49 |
50 |
51 |
52 |
53 |
54 | ${content.header.data}
55 |
56 | ${content.body.data}
57 |
58 |