Spaces:
Configuration error
Configuration error
| const makeServiceWorkerEnv = require('service-worker-mock') | |
| const HASH = '123HASHBROWN' | |
| export const getEvent = (request: Request): any => { | |
| const waitUntil = async (callback: any) => { | |
| await callback | |
| } | |
| return { | |
| request, | |
| waitUntil, | |
| } | |
| } | |
| const store: any = { | |
| 'key1.123HASHBROWN.txt': 'val1', | |
| 'key1.123HASHBROWN.png': 'val1', | |
| 'index.123HASHBROWN.html': 'index.html', | |
| 'cache.123HASHBROWN.html': 'cache me if you can', | |
| '测试.123HASHBROWN.html': 'My filename is non-ascii', | |
| '%not-really-percent-encoded.123HASHBROWN.html': 'browser percent encoded', | |
| '%2F.123HASHBROWN.html': 'user percent encoded', | |
| '你好.123HASHBROWN.html': 'I shouldnt be served', | |
| '%E4%BD%A0%E5%A5%BD.123HASHBROWN.html': 'Im important', | |
| 'nohash.txt': 'no hash but still got some result', | |
| 'sub/blah.123HASHBROWN.png': 'picturedis', | |
| 'sub/index.123HASHBROWN.html': 'picturedis', | |
| 'client.123HASHBROWN': 'important file', | |
| 'client.123HASHBROWN/index.html': 'Im here but serve my big bro above', | |
| 'image.123HASHBROWN.png': 'imagepng', | |
| 'image.123HASHBROWN.webp': 'imagewebp', | |
| '你好/index.123HASHBROWN.html': 'My path is non-ascii', | |
| } | |
| export const mockKV = (store: any) => { | |
| return { | |
| get: (path: string) => store[path] || null, | |
| } | |
| } | |
| export const mockManifest = () => { | |
| return JSON.stringify({ | |
| 'key1.txt': `key1.${HASH}.txt`, | |
| 'key1.png': `key1.${HASH}.png`, | |
| 'cache.html': `cache.${HASH}.html`, | |
| '测试.html': `测试.${HASH}.html`, | |
| '你好.html': `你好.${HASH}.html`, | |
| '%not-really-percent-encoded.html': `%not-really-percent-encoded.${HASH}.html`, | |
| '%2F.html': `%2F.${HASH}.html`, | |
| '%E4%BD%A0%E5%A5%BD.html': `%E4%BD%A0%E5%A5%BD.${HASH}.html`, | |
| 'index.html': `index.${HASH}.html`, | |
| 'sub/blah.png': `sub/blah.${HASH}.png`, | |
| 'sub/index.html': `sub/index.${HASH}.html`, | |
| client: `client.${HASH}`, | |
| 'client/index.html': `client.${HASH}`, | |
| 'image.png': `image.${HASH}.png`, | |
| 'image.webp': `image.${HASH}.webp`, | |
| '你好/index.html': `你好/index.${HASH}.html`, | |
| }) | |
| } | |
| let cacheStore: any = new Map() | |
| interface CacheKey { | |
| url: object | |
| headers: object | |
| } | |
| export const mockCaches = () => { | |
| return { | |
| default: { | |
| async match(key: any) { | |
| let cacheKey: CacheKey = { | |
| url: key.url, | |
| headers: {}, | |
| } | |
| let response | |
| if (key.headers.has('if-none-match')) { | |
| let makeStrongEtag = key.headers.get('if-none-match').replace('W/', '') | |
| Reflect.set(cacheKey.headers, 'etag', makeStrongEtag) | |
| response = cacheStore.get(JSON.stringify(cacheKey)) | |
| } else { | |
| // if client doesn't send if-none-match, we need to iterate through these keys | |
| // and just test the URL | |
| const activeCacheKeys: Array<string> = Array.from(cacheStore.keys()) | |
| for (const cacheStoreKey of activeCacheKeys) { | |
| if (JSON.parse(cacheStoreKey).url === key.url) { | |
| response = cacheStore.get(cacheStoreKey) | |
| } | |
| } | |
| } | |
| // TODO: write test to accomodate for rare scenarios with where range requests accomodate etags | |
| if (response && !key.headers.has('if-none-match')) { | |
| // this appears overly verbose, but is necessary to document edge cache behavior | |
| // The Range request header triggers the response header Content-Range ... | |
| const range = key.headers.get('range') | |
| if (range) { | |
| response.headers.set( | |
| 'content-range', | |
| `bytes ${range.split('=').pop()}/${response.headers.get('content-length')}`, | |
| ) | |
| } | |
| // ... which we are using in this repository to set status 206 | |
| if (response.headers.has('content-range')) { | |
| response.status = 206 | |
| } else { | |
| response.status = 200 | |
| } | |
| let etag = response.headers.get('etag') | |
| if (etag && !etag.includes('W/')) { | |
| response.headers.set('etag', `W/${etag}`) | |
| } | |
| } | |
| return response | |
| }, | |
| async put(key: any, val: Response) { | |
| let headers = new Headers(val.headers) | |
| let url = new URL(key.url) | |
| let resWithBody = new Response(val.body, { headers, status: 200 }) | |
| let resNoBody = new Response(null, { headers, status: 304 }) | |
| let cacheKey: CacheKey = { | |
| url: key.url, | |
| headers: { | |
| etag: `"${url.pathname.replace('/', '')}"`, | |
| }, | |
| } | |
| cacheStore.set(JSON.stringify(cacheKey), resNoBody) | |
| cacheKey.headers = {} | |
| cacheStore.set(JSON.stringify(cacheKey), resWithBody) | |
| return | |
| }, | |
| }, | |
| } | |
| } | |
| // mocks functionality used inside worker request | |
| export function mockRequestScope() { | |
| Object.assign(global, makeServiceWorkerEnv()) | |
| Object.assign(global, { __STATIC_CONTENT_MANIFEST: mockManifest() }) | |
| Object.assign(global, { __STATIC_CONTENT: mockKV(store) }) | |
| Object.assign(global, { caches: mockCaches() }) | |
| } | |
| // mocks functionality used on global isolate scope. such as the KV namespace bind | |
| export function mockGlobalScope() { | |
| Object.assign(global, { __STATIC_CONTENT_MANIFEST: mockManifest() }) | |
| Object.assign(global, { __STATIC_CONTENT: mockKV(store) }) | |
| } | |
| export const sleep = (milliseconds: number) => { | |
| return new Promise((resolve) => setTimeout(resolve, milliseconds)) | |
| } | |