Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(transport): Make SvelteKit adapter deployable on Netlify Edge Functions #2155

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions transport/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,6 @@ edge-light.js
edge-light.d.ts
index.js
index.d.ts
svelte.js
svelte.d.ts
test/*.js
1 change: 1 addition & 0 deletions transport/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"types": "./index.d.ts",
"exports": {
"bun": "./bun.js",
"svelte": "./svelte.js",
"edge-light": "./edge-light.js",
"default": "./index.js"
},
Expand Down
79 changes: 79 additions & 0 deletions transport/svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// This entire workaround is to make SvelteKit run on Netlify Edge Functions
// which execute in an old version of Deno that doesn't support some `node:*`
// imports, such as `node:assert`. This makes the undici import fail when we try
// to use `@connectrpc/connect-node`.
//
// By making these dynamic imports, we can await them inside of the `unary` or
// `stream` function calls to figure out which client we are using.

export function createTransport(baseUrl: string) {
const web = import("@connectrpc/connect-web").then(
({ createConnectTransport }) => {
// The Connect Node client doesn't work on edge runtimes: https://github.com/bufbuild/connect-es/pull/589
// so set the transport using connect-web. The interceptor is required for it work in the edge runtime.
return createConnectTransport({
baseUrl,
interceptors: [
/**
* Ensures redirects are followed to properly support the Next.js/Vercel Edge
* Runtime.
* @see
* https://github.com/connectrpc/connect-es/issues/749#issuecomment-1693507516
*/
(next) => (req) => {
req.init.redirect = "follow";
return next(req);
},
],
fetch,
});
},
);
const node = import("@connectrpc/connect-node").then(
({ Http2SessionManager, createConnectTransport }) => {
// We create our own session manager so we can attempt to pre-connect
const sessionManager = new Http2SessionManager(baseUrl, {
// AWS Global Accelerator doesn't support PING so we use a very high idle
// timeout. Ref:
// https://docs.aws.amazon.com/global-accelerator/latest/dg/introduction-how-it-works.html#about-idle-timeout
idleConnectionTimeoutMs: 340 * 1000,
});

// We ignore the promise result because this is an optimistic pre-connect
sessionManager.connect();

return createConnectTransport({
baseUrl,
httpVersion: "2",
sessionManager,
});
},
);

return {
async unary(
...args: [any, any, any, any, any, any, any]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get some concrete types for these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe these types come from connectrpc directly and we don't want to add another dependency to get the types. There may be a way to use utility types to pull them out but I'm not sure.

): Promise<unknown> {
let client;
try {
client = await node;
} catch {
client = await web;
}

return client.unary(...args);
},
async stream(
...args: [any, any, any, any, any, any, any]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

): Promise<unknown> {
let client;
try {
client = await node;
} catch {
client = await web;
}

return client.stream(...args);
},
};
}
2 changes: 1 addition & 1 deletion transport/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"extends": "@arcjet/tsconfig/base",
"include": ["bun.ts", "edge-light.ts", "index.ts"]
"include": ["bun.ts", "edge-light.ts", "index.ts", "svelte.ts"]
}