-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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: add support for Netlify Edge Functions #2866
Changes from all commits
1b3d7f3
ef68922
0964557
359aad7
97fea3a
931f11f
be7932e
36fc06f
5036dd9
32fdeba
6620dae
e310d92
a7ed7d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,12 @@ | ||
{ | ||
"typescript.tsdk": "node_modules/typescript/lib" | ||
} | ||
"typescript.tsdk": "node_modules/typescript/lib", | ||
"deno.enablePaths": [ | ||
"./templates/deno/", | ||
"./templates/netlify/remix.init/edge-server.js", | ||
"./packages/remix-netlify-edge/mod.ts", | ||
"./packages/remix-netlify-edge/server.ts", | ||
"./packages/remix-netlify-edge/remix-deno/" | ||
], | ||
"deno.unstable": true, | ||
"deno.importMap": "deno-import-map.json" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"imports": { | ||
"netlify:edge": "https://edge-bootstrap.netlify.app/v1/index.ts", | ||
"@remix-run/netlify-edge": "./packages/remix-netlify-edge/mod.ts", | ||
"@remix-run/netlify-edge/deno": "./packages/remix-netlify-edge/remix-deno/mod.ts", | ||
"@remix-run/dev/server-build": "https://esm.sh/@remix-run/dev/server-build", | ||
"@remix-run/server-runtime": "https://esm.sh/@remix-run/server-runtime@1.4.1?pin=v77" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Welcome to Remix! | ||
|
||
[Remix](https://remix.run) is a web framework that helps you build better websites with React. | ||
|
||
To get started, open a new shell and run: | ||
|
||
```sh | ||
npx create-remix@latest | ||
``` | ||
|
||
Then follow the prompts you see in your terminal. | ||
|
||
For more information about Remix, [visit remix.run](https://remix.run)! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import type { | ||
CreateCookieFunction, | ||
CreateCookieSessionStorageFunction, | ||
CreateSessionStorageFunction, | ||
CreateMemorySessionStorageFunction, | ||
ServerBuild, | ||
} from "@remix-run/server-runtime"; | ||
|
||
interface BaseContext { | ||
next: (options?: { sendConditionalRequest?: boolean }) => Promise<Response>; | ||
} | ||
export declare function createRequestHandler< | ||
Context extends BaseContext = BaseContext | ||
>({ | ||
build, | ||
mode, | ||
getLoadContext, | ||
}: { | ||
build: ServerBuild; | ||
mode?: string; | ||
getLoadContext?: ( | ||
request: Request, | ||
context?: Context | ||
) => Promise<Context> | Context; | ||
}): (request: Request, context: Context) => Promise<Response | void>; | ||
export {}; | ||
|
||
export * from "@remix-run/server-runtime"; | ||
|
||
export const createCookie: CreateCookieFunction; | ||
export const createCookieSessionStorage: CreateCookieSessionStorageFunction; | ||
export const createSessionStorage: CreateSessionStorageFunction; | ||
export const createMemorySessionStorage: CreateMemorySessionStorageFunction; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* @remix-run/netlify-edge | ||
* | ||
* Copyright (c) Remix Software Inc. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE.md file in the root directory of this source tree. | ||
* | ||
* @license MIT | ||
*/ | ||
|
||
// This package is meant to be imported by Deno, but includes types for Node |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,48 @@ | ||||||
export { createRequestHandler } from "./server.ts"; | ||||||
export type { | ||||||
ActionFunction, | ||||||
AppData, | ||||||
AppLoadContext, | ||||||
CreateRequestHandlerFunction, | ||||||
Cookie, | ||||||
CookieOptions, | ||||||
CookieParseOptions, | ||||||
CookieSerializeOptions, | ||||||
CookieSignatureOptions, | ||||||
DataFunctionArgs, | ||||||
EntryContext, | ||||||
ErrorBoundaryComponent, | ||||||
HandleDataRequestFunction, | ||||||
HandleDocumentRequestFunction, | ||||||
HeadersFunction, | ||||||
HtmlLinkDescriptor, | ||||||
HtmlMetaDescriptor, | ||||||
LinkDescriptor, | ||||||
LinksFunction, | ||||||
LoaderFunction, | ||||||
MetaDescriptor, | ||||||
MetaFunction, | ||||||
PageLinkDescriptor, | ||||||
RequestHandler, | ||||||
RouteComponent, | ||||||
RouteHandle, | ||||||
ServerBuild, | ||||||
ServerEntryModule, | ||||||
Session, | ||||||
SessionData, | ||||||
SessionIdStorageStrategy, | ||||||
SessionStorage, | ||||||
} from "https://esm.sh/@remix-run/server-runtime@1.4.1"; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we keep this or change it to something like we have in the
Suggested change
|
||||||
export { | ||||||
createSession, | ||||||
isCookie, | ||||||
isSession, | ||||||
json, | ||||||
redirect, | ||||||
} from "https://esm.sh/@remix-run/server-runtime@1.4.1"; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
export { | ||||||
createCookie, | ||||||
createCookieSessionStorage, | ||||||
createMemorySessionStorage, | ||||||
createSessionStorage, | ||||||
} from "./remix-deno/implementations.ts"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"name": "@remix-run/netlify-edge", | ||
"description": "Netlify Edge platform abstractions for Remix", | ||
"version": "1.0.0", | ||
"license": "MIT", | ||
"type": "module", | ||
"main": "./index.js", | ||
"types": "./index.d.ts", | ||
"exports": { | ||
".": "./index.js", | ||
"./mod.ts": "./mod.ts", | ||
"./deno": "./remix-deno/mod.ts" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/remix-run/remix", | ||
"directory": "packages/remix-netlify-edge" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/remix-run/remix/issues" | ||
}, | ||
"files": [ | ||
"**/*.ts", | ||
"index.js" | ||
], | ||
"peerDependencies": { | ||
"@remix-run/server-runtime": "*" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably go into |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# @remix-run/deno | ||
|
||
`@remix-run/deno` package is temporarily inlined within this directory while Deno support is experimental. | ||
In the future, this directory would be removed and Remix + Deno apps would import `@remix-run/deno` from some URL. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import type { | ||
SignFunction, | ||
UnsignFunction, | ||
} from "https://esm.sh/@remix-run/server-runtime?pin=v77"; | ||
|
||
const encoder = new TextEncoder(); | ||
|
||
export const sign: SignFunction = async (value, secret) => { | ||
let key = await crypto.subtle.importKey( | ||
"raw", | ||
encoder.encode(secret), | ||
{ name: "HMAC", hash: "SHA-256" }, | ||
false, | ||
["sign"] | ||
); | ||
|
||
let data = encoder.encode(value); | ||
let signature = await crypto.subtle.sign("HMAC", key, data); | ||
let hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace( | ||
/=+$/, | ||
"" | ||
); | ||
|
||
return value + "." + hash; | ||
}; | ||
|
||
export const unsign: UnsignFunction = async (cookie, secret) => { | ||
let key = await crypto.subtle.importKey( | ||
"raw", | ||
encoder.encode(secret), | ||
{ name: "HMAC", hash: "SHA-256" }, | ||
false, | ||
["verify"] | ||
); | ||
|
||
let value = cookie.slice(0, cookie.lastIndexOf(".")); | ||
let hash = cookie.slice(cookie.lastIndexOf(".") + 1); | ||
|
||
let data = encoder.encode(value); | ||
let signature = byteStringToUint8Array(atob(hash)); | ||
let valid = await crypto.subtle.verify("HMAC", key, signature, data); | ||
|
||
return valid ? value : false; | ||
}; | ||
|
||
function byteStringToUint8Array(byteString: string): Uint8Array { | ||
let array = new Uint8Array(byteString.length); | ||
|
||
for (let i = 0; i < byteString.length; i++) { | ||
array[i] = byteString.charCodeAt(i); | ||
} | ||
|
||
return array; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export {}; | ||
declare global { | ||
interface ProcessEnv { | ||
NODE_ENV: "development" | "production" | "test"; | ||
} | ||
interface Process { | ||
env: ProcessEnv; | ||
} | ||
let process: Process; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { | ||
createCookieFactory, | ||
createCookieSessionStorageFactory, | ||
createMemorySessionStorageFactory, | ||
createSessionStorageFactory, | ||
} from "https://esm.sh/@remix-run/server-runtime?pin=v77"; | ||
|
||
import { sign, unsign } from "./crypto.ts"; | ||
|
||
export const createCookie = createCookieFactory({ sign, unsign }); | ||
export const createCookieSessionStorage = | ||
createCookieSessionStorageFactory(createCookie); | ||
export const createSessionStorage = createSessionStorageFactory(createCookie); | ||
export const createMemorySessionStorage = | ||
createMemorySessionStorageFactory(createSessionStorage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be simplified to