-
Notifications
You must be signed in to change notification settings - Fork 787
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(workers-shared): Add Assets Server Worker behaviour
- Loading branch information
1 parent
d0ecc6a
commit e2dd4eb
Showing
8 changed files
with
219 additions
and
29 deletions.
There are no files selected for viewing
13 changes: 0 additions & 13 deletions
13
packages/workers-shared/asset-server-worker/src/assets-manifest-no-op.ts
This file was deleted.
Oops, something went wrong.
5 changes: 5 additions & 0 deletions
5
packages/workers-shared/asset-server-worker/src/assets-manifest.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// have the browser check in with the server to make sure its local cache is valid before using it | ||
export const CACHE_CONTROL_BROWSER = "public, max-age=0, must-revalidate"; |
32 changes: 32 additions & 0 deletions
32
packages/workers-shared/asset-server-worker/src/global.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
type Environment = "production" | "local"; | ||
|
||
type Env = { | ||
ASSETS_MANIFEST: ArrayBuffer; | ||
ASSETS_KV_NAMESPACE: KVNamespace; | ||
ENVIRONMENT: Environment; | ||
}; | ||
|
||
type BodyEncoding = "manual" | "automatic"; | ||
|
||
interface ResponseInit { | ||
encodeBody?: BodyEncoding; | ||
} | ||
|
||
interface KVNamespace { | ||
getWithMetadata<Metadata = unknown>( | ||
key: string, | ||
type: "stream" | ||
): KVValueWithMetadata<ReadableStream, Metadata>; | ||
getWithMetadata<Metadata = unknown>( | ||
key: string, | ||
options?: { | ||
type: "stream"; | ||
cacheTtl?: number; | ||
} | ||
): KVValueWithMetadata<ReadableStream, Metadata>; | ||
} | ||
|
||
type KVValueWithMetadata<Value, Metadata> = Promise<{ | ||
value: Value | null; | ||
metadata: Metadata | null; | ||
}>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
packages/workers-shared/asset-server-worker/src/responses.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
export class OkResponse extends Response { | ||
constructor(body: BodyInit | null, init?: ResponseInit) { | ||
super(body, { | ||
...init, | ||
status: 200, | ||
}); | ||
} | ||
} | ||
|
||
export class NotFoundResponse extends Response { | ||
constructor(...[body, init]: ConstructorParameters<typeof Response>) { | ||
super(body, { | ||
...init, | ||
status: 404, | ||
statusText: "Not Found", | ||
}); | ||
} | ||
} | ||
|
||
export class MethodNotAllowedResponse extends Response { | ||
constructor(...[body, init]: ConstructorParameters<typeof Response>) { | ||
super(body, { | ||
...init, | ||
status: 405, | ||
statusText: "Method Not Allowed", | ||
}); | ||
} | ||
} | ||
|
||
export class InternalServerErrorResponse extends Response { | ||
constructor(err: Error, init?: ResponseInit) { | ||
let body: string | undefined = undefined; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
if ((globalThis as any).DEBUG) { | ||
body = `${err.message}\n\n${err.stack}`; | ||
} | ||
|
||
super(body, { | ||
...init, | ||
status: 500, | ||
}); | ||
} | ||
} | ||
|
||
export class NotModifiedResponse extends Response { | ||
constructor(...[_body, _init]: ConstructorParameters<typeof Response>) { | ||
super(undefined, { | ||
status: 304, | ||
statusText: "Not Modified", | ||
}); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
packages/workers-shared/asset-server-worker/src/utils/headers.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { CACHE_CONTROL_BROWSER } from "../constants"; | ||
import type { AssetMetadata } from "./kv"; | ||
|
||
/** | ||
* Returns a Headers object that is the union of `existingHeaders` | ||
* and `additionalHeaders`. Headers specified by `additionalHeaders` | ||
* will override those specified by `existingHeaders`. | ||
* | ||
*/ | ||
export function getMergedHeaders( | ||
existingHeaders: Headers, | ||
additionalHeaders: Headers | ||
) { | ||
return new Headers({ | ||
// override existing headers | ||
...Object.fromEntries(new Headers(existingHeaders).entries()), | ||
...Object.fromEntries(new Headers(additionalHeaders).entries()), | ||
}); | ||
} | ||
|
||
/** | ||
* Returns a Headers object that contains additional headers (to those | ||
* present in the original request) that the Assets Server Worker | ||
* should attach to its response. | ||
* | ||
*/ | ||
export function getAdditionalHeaders( | ||
assetKey: string, | ||
assetMetadata: AssetMetadata | null, | ||
request: Request | ||
) { | ||
let contentType = assetMetadata?.contentType ?? "application/octet-stream"; | ||
if (contentType.startsWith("text/") && !contentType.includes("charset")) { | ||
contentType = `${contentType}; charset=utf-8`; | ||
} | ||
|
||
const headers = new Headers({ | ||
"Access-Control-Allow-Origin": "*", | ||
"Content-Type": contentType, | ||
"Referrer-Policy": "strict-origin-when-cross-origin", | ||
"X-Content-Type-Options": "nosniff", | ||
ETag: `"${assetKey}"`, | ||
}); | ||
|
||
if (isCacheable(request)) { | ||
headers.append("Cache-Control", CACHE_CONTROL_BROWSER); | ||
} | ||
|
||
return headers; | ||
} | ||
|
||
function isCacheable(request: Request) { | ||
return !request.headers.has("authorization") && !request.headers.has("range"); | ||
} |
31 changes: 31 additions & 0 deletions
31
packages/workers-shared/asset-server-worker/src/utils/kv.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
export type AssetMetadata = { | ||
contentType: string; | ||
}; | ||
|
||
export async function getAssetWithMetadataFromKV( | ||
assetsKVNamespace: KVNamespace, | ||
assetKey: string, | ||
retries = 1 | ||
) { | ||
let attempts = 0; | ||
|
||
while (attempts < retries) { | ||
try { | ||
return await assetsKVNamespace.getWithMetadata<AssetMetadata>(assetKey, { | ||
type: "stream", | ||
cacheTtl: 31536000, // 1 year | ||
}); | ||
} catch (err) { | ||
if (attempts >= retries) { | ||
throw new Error( | ||
`Requested asset ${assetKey} could not be found in KV namespace.` | ||
); | ||
} | ||
|
||
// Exponential backoff, 1 second first time, then 2 second, then 4 second etc. | ||
await new Promise((resolvePromise) => | ||
setTimeout(resolvePromise, Math.pow(2, attempts++) * 1000) | ||
); | ||
} | ||
} | ||
} |