Skip to content

Commit be9cedb

Browse files
committedSep 14, 2024
feat(deploy): add cloudflare context to platform object
Example usage from a server component: ```ts import { unstable_getPlatformObject } from 'waku/server'; const getData = async () => { const { env as CloudflareEnv, executionCtx as ExecutionContext } = unstable_getPlatformObject(); executionCtx?.waitUntil( new Promise<void>((resolve) => { console.log("Waiting for 5 seconds") setTimeout(() => { console.log("OK, done waiting") resolve() }, 5000) }), ) const { results } = await env.DB.prepare( "SELECT * FROM users WHERE user_id = ?", ) .bind(userId) .all(); return results; }; ``` Server console output: ``` Waiting for 5 seconds [wrangler:inf] GET /about 200 OK (30ms) [wrangler:inf] GET /assets/jsx-runtime-BjG_zV1W.js 304 Not Modified (6ms) [wrangler:inf] GET /assets/index-CbskofAj.js 304 Not Modified (7ms) [wrangler:inf] GET /assets/_layout-Shb4QlRw.css 304 Not Modified (9ms) [wrangler:inf] GET /assets/rsc2-c69df7b3e.js 200 OK (11ms) [wrangler:inf] GET /assets/client-kczTGGZ_.js 304 Not Modified (16ms) [wrangler:inf] GET /assets/indexHtml-DCalQi_d.js 304 Not Modified (18ms) [wrangler:inf] GET /assets/client-CMyJdxTj.js 304 Not Modified (21ms) [wrangler:inf] GET /assets/rsc0-ba005381c.js 304 Not Modified (4ms) [wrangler:inf] GET /images/favicon.png 304 Not Modified (3ms) OK, done waiting ```
1 parent 52973c8 commit be9cedb

File tree

4 files changed

+64
-16
lines changed

4 files changed

+64
-16
lines changed
 

‎packages/waku/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"vite": "5.4.4"
8888
},
8989
"devDependencies": {
90+
"@cloudflare/workers-types": "^4.20240909.0",
9091
"@netlify/functions": "^2.8.1",
9192
"@swc/cli": "^0.4.0",
9293
"rollup": "^4.21.3",
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,75 @@
11
import { Hono } from 'hono';
2+
import { unstable_getPlatformObject } from 'waku/server';
23
import { runner } from '../hono/runner.js';
4+
import type {
5+
ExportedHandler,
6+
fetch,
7+
Response as CloudflareResponse,
8+
} from '@cloudflare/workers-types/experimental';
39

410
const loadEntries = () => import(import.meta.env.WAKU_ENTRIES_FILE!);
511
let serveWaku: ReturnType<typeof runner> | undefined;
612

713
export interface CloudflareEnv {
814
ASSETS: {
9-
fetch: (input: RequestInit | URL, init?: RequestInit) => Promise<Response>;
15+
fetch: typeof fetch;
1016
};
1117
}
1218

1319
export const app = new Hono<{
1420
Bindings: CloudflareEnv & { [k: string]: unknown };
1521
}>();
16-
app.use('*', (c, next) => serveWaku!(c, next));
22+
app.use('*', (c, next) => {
23+
if (!serveWaku) {
24+
throw new Error('serveWaku is not initialized');
25+
}
26+
const platform = unstable_getPlatformObject();
27+
platform.honoContext = c;
28+
platform.env = c.env;
29+
platform.executionContext = c.executionCtx;
30+
return serveWaku(c, next);
31+
});
1732
app.notFound(async (c) => {
1833
const assetsFetcher = c.env.ASSETS;
1934
const url = new URL(c.req.raw.url);
2035
const errorHtmlUrl = `${url.origin}/404.html`;
21-
const notFoundStaticAssetResponse = await assetsFetcher.fetch(
36+
const notFoundStaticAssetResponse = (await assetsFetcher.fetch(
2237
new URL(errorHtmlUrl),
23-
);
38+
)) as unknown as Response;
2439
if (notFoundStaticAssetResponse && notFoundStaticAssetResponse.status < 400) {
2540
return c.body(notFoundStaticAssetResponse.body, 404);
2641
}
2742
return c.text('404 Not Found', 404);
2843
});
2944

30-
export default {
31-
async fetch(
32-
request: Request,
33-
env: Record<string, string>,
34-
ctx: Parameters<typeof app.fetch>[2],
35-
) {
45+
// Waku getEnv only supports strings
46+
// Cloudflare injects bindings to env and JSON
47+
// Use unstable_getPlatformObject() to access cloudflare env and execution context
48+
// https://developers.cloudflare.com/workers/configuration/environment-variables/#add-environment-variables-via-wrangler
49+
// https://developers.cloudflare.com/workers/runtime-apis/bindings/
50+
const extractWakuEnv = (env: Record<string, unknown>): Record<string, string> =>
51+
Object.fromEntries(
52+
Object.entries(env).filter(([, value]) => typeof value === 'string'),
53+
) as Record<string, string>;
54+
55+
const handler: ExportedHandler<CloudflareEnv & { [k: string]: never }> = {
56+
async fetch(request, env, ctx) {
3657
if (!serveWaku) {
37-
serveWaku = runner({ cmd: 'start', loadEntries, env });
58+
serveWaku = runner({
59+
cmd: 'start',
60+
loadEntries,
61+
env: extractWakuEnv(env),
62+
});
3863
}
39-
return app.fetch(request, env, ctx);
64+
return app.fetch(
65+
request as unknown as Request,
66+
env,
67+
ctx,
68+
) as unknown as CloudflareResponse;
4069
},
70+
// It might be useful to have a way to populate other handlers tail, trace, scheduled, email, queue
71+
// But it's not hard to create a separate worker with those handlers and access it via
72+
// service bindings. https://developers.cloudflare.com/pages/functions/bindings/#service-bindings
4173
};
74+
75+
export default handler;

‎packages/waku/src/server.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,11 @@ export function unstable_getHeaders(): Record<string, string> {
147147
>;
148148
}
149149

150-
type PlatformObject = {
151-
buildData?: Record<string, unknown>; // must be JSON serializable
150+
type PlatformObject<
151+
T = Record<string, unknown>,
152+
BuildData = Record<string, unknown>,
153+
> = {
154+
buildData?: BuildData; // must be JSON serializable
152155
buildOptions?: {
153156
deploy?:
154157
| 'vercel-static'
@@ -167,11 +170,13 @@ type PlatformObject = {
167170
| 'buildClientBundle'
168171
| 'buildDeploy';
169172
};
170-
} & Record<string, unknown>;
173+
} & T;
171174

172175
(globalThis as any).__WAKU_PLATFORM_OBJECT__ ||= {};
173176

174177
// TODO tentative name
175-
export function unstable_getPlatformObject(): PlatformObject {
178+
export function unstable_getPlatformObject<
179+
T = Record<string, unknown>,
180+
>(): PlatformObject<T> {
176181
return (globalThis as any).__WAKU_PLATFORM_OBJECT__;
177182
}

‎pnpm-lock.yaml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)