Skip to content

Commit

Permalink
feat: Workers Sites support for local mode (#336)
Browse files Browse the repository at this point in the history
* chore: example-sites-app

An example app with a Workers Sites definition.

* feat: inline text-like files into the worker bundle

We were adding text-like modules (i.e. `.txt`, `.html` and `.pem` files) as separate modules in the Worker definition, but this only really 'works' with the ES module Worker format. This commit changes that to inline the text-like files into the Worker bundle directly.

We still have to do something similar with `.wasm` modules, but that requires a different fix, and we'll do so in a subsequent commit.

* feat: Sites support for local mode `wrangler dev`

This adds support for Workers Sites in local mode when running wrangler `dev`. Further, it fixes a bug where we were sending the `__STATIC_CONTENT_MANIFEST` definition as a separate module even with service worker format, and a bug where we weren't uploading the namespace binding when other kv namespaces weren't present.

* Update packages/wrangler/src/api/form_data.ts

Co-authored-by: Pete Bacon Darwin <[email protected]>

Co-authored-by: Pete Bacon Darwin <[email protected]>
  • Loading branch information
threepointone and petebacondarwin authored Jan 29, 2022
1 parent a417cb0 commit ce61000
Show file tree
Hide file tree
Showing 21 changed files with 636 additions and 129 deletions.
9 changes: 9 additions & 0 deletions .changeset/nervous-pugs-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"wrangler": patch
---

feat: inline text-like files into the worker bundle

We were adding text-like modules (i.e. `.txt`, `.html` and `.pem` files) as separate modules in the Worker definition, but this only really 'works' with the ES module Worker format. This commit changes that to inline the text-like files into the Worker bundle directly.

We still have to do something similar with `.wasm` modules, but that requires a different fix, and we'll do so in a subsequent commit.
7 changes: 7 additions & 0 deletions .changeset/red-pillows-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

feat: Sites support for local mode `wrangler dev`

This adds support for Workers Sites in local mode when running wrangler `dev`. Further, it fixes a bug where we were sending the `__STATIC_CONTENT_MANIFEST` definition as a separate module even with service worker format, and a bug where we weren't uploading the namespace binding when other kv namespaces weren't present.
36 changes: 33 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions packages/example-sites-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "example-sites-app",
"private": true,
"version": "1.0.0",
"dependencies": {
"@cloudflare/kv-asset-handler": "~0.2.0"
}
}
50 changes: 50 additions & 0 deletions packages/example-sites-app/public/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link
href="https://fonts.googleapis.com/css?family=Pacifico&display=swap"
rel="stylesheet"
/>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
rel="stylesheet"
/>
<style>
h1 {
font-family: Pacifico, sans-serif;
font-size: 4em;
color: #3eb5f1;
margin: 0;
}

h2 {
font-weight: 300;
font-family: sans-serif;
}

.centered {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}

#ferris {
width: 75%;
}
</style>
</head>
<body>
<div class="centered">
<h1>404 Not Found</h1>
<h2>Oh dang! We couldn't find that page.</h2>
<img
id="ferris"
alt="a sad crab is unable to unable to lasso a paper airplane. 404 not found."
src="./img/404-wrangler-ferris.gif"
/>
</div>
</body>
</html>
Binary file added packages/example-sites-app/public/favicon.ico
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions packages/example-sites-app/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link
href="https://fonts.googleapis.com/css?family=Pacifico&display=swap"
rel="stylesheet"
/>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
rel="stylesheet"
/>
<style>
h1 {
font-family: Pacifico, sans-serif;
font-size: 4em;
color: #3eb5f1;
margin: 0;
}

h2 {
font-weight: 300;
font-family: sans-serif;
}

.centered {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}

#ferris {
width: 75%;
}
</style>
</head>
<body>
<div class="centered">
<h1>200 Success</h1>
<h2>Hello World! Welcome to your Workers Site.</h2>
<img
id="ferris"
alt="a happy crab is wearing a cowboy hat and holding a lasso. 200 success."
src="./img/200-wrangler-ferris.gif"
/>
</div>
</body>
</html>
66 changes: 66 additions & 0 deletions packages/example-sites-app/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
getAssetFromKV,
mapRequestToAsset,
} from "@cloudflare/kv-asset-handler";

/**
* The DEBUG flag will do two things that help during development:
* 1. we will skip caching on the edge, which makes it easier to
* debug.
* 2. we will return an error message on exception in your Response rather
* than the default 404.html page.
*/
const DEBUG = false;

addEventListener("fetch", (event) => {
event.respondWith(handleEvent(event));
});

async function handleEvent(event) {
let options = {};

/**
* You can add custom logic to how we fetch your assets
* by configuring the function `mapRequestToAsset`
*/
// options.mapRequestToAsset = handlePrefix(/^\/docs/)

try {
if (DEBUG) {
// customize caching
options.cacheControl = {
bypassCache: true,
};
}

const page = await getAssetFromKV(event, options);

// allow headers to be altered
const response = new Response(page.body, page);

response.headers.set("X-XSS-Protection", "1; mode=block");
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("Referrer-Policy", "unsafe-url");
response.headers.set("Feature-Policy", "none");

return response;
} catch (e) {
// if an error is thrown try to serve the asset at 404.html
if (!DEBUG) {
try {
let notFoundResponse = await getAssetFromKV(event, {
mapRequestToAsset: (req) =>
new Request(`${new URL(req.url).origin}/404.html`, req),
});

return new Response(notFoundResponse.body, {
...notFoundResponse,
status: 404,
});
} catch (e) {}
}

return new Response(e.message || e.toString(), { status: 500 });
}
}
2 changes: 2 additions & 0 deletions packages/example-sites-app/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name = "example-sites-app"
site = { bucket = "./public" }
41 changes: 1 addition & 40 deletions packages/wrangler/src/__tests__/kv.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import {
setMockResponse,
setMockRawResponse,
unsetAllMocks,
createFetchResult,
} from "./mock-cfetch";
import { runWrangler } from "./run-wrangler";
import { runInTempDir } from "./run-in-tmp";
import { mockConsoleMethods } from "./mock-console";
import { mockKeyListRequest } from "./mock-kv";

describe("wrangler", () => {
runInTempDir();
Expand Down Expand Up @@ -1222,42 +1222,3 @@ function writeWranglerConfig() {
"utf-8"
);
}

export function mockKeyListRequest(
expectedNamespaceId: string,
expectedKeys: string[],
keysPerRequest = 1000,
blankCursorValue: "" | undefined | null = undefined
) {
const requests = { count: 0 };
// See https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
const expectedKeyObjects = expectedKeys.map((name) => ({
name,
expiration: 123456789,
metadata: {},
}));
setMockRawResponse(
"/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
"GET",
([_url, accountId, namespaceId], _init, query) => {
requests.count++;
expect(accountId).toEqual("some-account-id");
expect(namespaceId).toEqual(expectedNamespaceId);
if (expectedKeyObjects.length <= keysPerRequest) {
return createFetchResult(expectedKeyObjects);
} else {
const start = parseInt(query.get("cursor") ?? "0") || 0;
const end = start + keysPerRequest;
const cursor = end < expectedKeyObjects.length ? end : blankCursorValue;
return createFetchResult(
expectedKeyObjects.slice(start, end),
true,
[],
[],
{ cursor }
);
}
}
);
return requests;
}
2 changes: 1 addition & 1 deletion packages/wrangler/src/__tests__/logout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { runWrangler } from "./run-wrangler";
import { runInTempDir } from "./run-in-tmp";
import { initialise } from "../user";
import { mockConsoleMethods } from "./mock-console";
import { writeUserConfig } from "./whoami.test";
import { writeUserConfig } from "./mock-user";

const ORIGINAL_CF_API_TOKEN = process.env.CF_API_TOKEN;
const ORIGINAL_CF_ACCOUNT_ID = process.env.CF_ACCOUNT_ID;
Expand Down
40 changes: 40 additions & 0 deletions packages/wrangler/src/__tests__/mock-kv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { createFetchResult, setMockRawResponse } from "./mock-cfetch";

export function mockKeyListRequest(
expectedNamespaceId: string,
expectedKeys: string[],
keysPerRequest = 1000,
blankCursorValue: "" | undefined | null = undefined
) {
const requests = { count: 0 };
// See https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
const expectedKeyObjects = expectedKeys.map((name) => ({
name,
expiration: 123456789,
metadata: {},
}));
setMockRawResponse(
"/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
"GET",
([_url, accountId, namespaceId], _init, query) => {
requests.count++;
expect(accountId).toEqual("some-account-id");
expect(namespaceId).toEqual(expectedNamespaceId);
if (expectedKeyObjects.length <= keysPerRequest) {
return createFetchResult(expectedKeyObjects);
} else {
const start = parseInt(query.get("cursor") ?? "0") || 0;
const end = start + keysPerRequest;
const cursor = end < expectedKeyObjects.length ? end : blankCursorValue;
return createFetchResult(
expectedKeyObjects.slice(start, end),
true,
[],
[],
{ cursor }
);
}
}
);
return requests;
}
Loading

0 comments on commit ce61000

Please sign in to comment.