From a9bb99221562e76907bf0865c1dc146585e3f494 Mon Sep 17 00:00:00 2001 From: James Ross Date: Tue, 18 May 2021 23:43:06 +0100 Subject: [PATCH] chore: repository maintenance chore: re-enable prettier chore: update github actions workflows chore: add Node 14 and 16 to tests docs: fix table of contest anchor links --- .gitattributes | 2 + .github/workflows/lint.yml | 4 +- .github/workflows/test.yml | 6 +- .prettierignore | 4 ++ .prettierrc | 1 + CODE_OF_CONDUCT.md | 22 +++---- README.md | 49 +++++++-------- package-lock.json | 6 ++ package.json | 5 +- src/index.ts | 18 +++--- src/mocks.ts | 29 +++++---- src/test/getAssetFromKV.ts | 108 ++++++++++++++++++--------------- src/test/mapRequestToAsset.ts | 6 +- src/test/serveSinglePageApp.ts | 6 +- src/types.ts | 2 +- 15 files changed, 147 insertions(+), 121 deletions(-) create mode 100644 .gitattributes create mode 100644 .prettierignore diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7f80b85 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto +* text eol=lf \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a2c5b27..43456fb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,8 +8,8 @@ jobs: steps: - uses: actions/checkout@v2 - name: Use Node.js - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: '12.x' - - run: npm install -g markdownlint-cli@0.23.2 + - run: npm install -g markdownlint-cli@0.27.1 - run: markdownlint '**/*.md' --ignore node_modules diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b9e3a3f..09ea72f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,12 +9,12 @@ jobs: strategy: matrix: - node-version: [8.x, 10.x, 12.x] + node-version: [8.x, 10.x, 12.x, 14.x, 16.x] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - name: npm install, build, and test diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f3d5d9a --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +CHANGELOG.md +package.json +package-lock.json +dist/**/* \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index ed2db5a..d7db2d6 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,5 @@ { + "endOfLine": "lf", "trailingComma": "all", "singleQuote": true, "semi": false, diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 29537ec..1655ea2 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/README.md b/README.md index c741eb1..3c759ad 100644 --- a/README.md +++ b/README.md @@ -14,24 +14,25 @@ For users who _do_ want to customize their assets, and want to build complex exp The Cloudflare Workers Discord server is an active place where Workers users get help, share feedback, and collaborate on making our platform better. The `#workers-sites` channel in particular is a great place to chat about `kv-asset-handler`, and building cool experiences for your users using these tools! If you have questions, want to share what you're working on, or give feedback, [join us in Discord and say hello](https://discord.gg/cloudflaredev)! - * [Installation](#installation) - * [Usage](#usage) - * [`getAssetFromKV`](#-getassetfromkv-) - - [Example](#example) - + [Return](#return) - + [Optional Arguments](#optional-arguments) - - [`mapRequestToAsset`](#-maprequesttoasset-) - - [Example](#example-1) - - [`cacheControl`](#-cachecontrol-) - * [`browserTTL`](#-browserttl-) - * [`edgeTTL`](#-edgettl-) - * [`bypassCache`](#-bypasscache-) - - [`ASSET_NAMESPACE`](#-asset-namespace-) - - [`ASSET_MANIFEST` (optional)](#-asset-manifest---optional-) -- [Helper functions](#helper-functions) - * [`mapRequestToAsset`](#-maprequesttoasset--1) - * [`serveSinglePageApp`](#-servesinglepageapp-) -- [Cache revalidation and etags](#cache-revalidation-and-etags) +- [Installation](#installation) +- [Usage](#usage) +- [`getAssetFromKV`](#-getassetfromkv) + - [Example](#example) + * [Return](#return) + * [Optional Arguments](#optional-arguments) + - [`mapRequestToAsset`](#-maprequesttoasset) + - [Example](#example-1) + - [`cacheControl`](#-cachecontrol) + - [`browserTTL`](#-browserttl) + - [`edgeTTL`](#-edgettl) + - [`bypassCache`](#-bypasscache) + - [`ASSET_NAMESPACE`](#-asset-namespace) + - [`ASSET_MANIFEST` (optional)](#-asset-manifest---optional) + +* [Helper functions](#helper-functions) + - [`mapRequestToAsset`](#-maprequesttoasset-1) + - [`serveSinglePageApp`](#-servesinglepageapp) +* [Cache revalidation and etags](#cache-revalidation-and-etags) ## Installation @@ -69,7 +70,7 @@ Known errors to be thrown are: ```js import { getAssetFromKV, NotFoundError, MethodNotAllowedError } from '@cloudflare/kv-asset-handler' -addEventListener('fetch', event => { +addEventListener('fetch', (event) => { event.respondWith(handleEvent(event)) }) @@ -83,7 +84,7 @@ async function handleEvent(event) { } else if (e instanceof MethodNotAllowedError) { // ... } else { - return new Response("An unexpected error occurred", { status: 500 }) + return new Response('An unexpected error occurred', { status: 500 }) } } } else return fetch(event.request) @@ -104,7 +105,7 @@ mapRequestToAsset(Request) => Request Maps the incoming request to the value that will be looked up in Cloudflare's KV -By default, mapRequestToAsset is set to the exported function [`mapRequestToAsset`](#maprequesttoasset-1). This works for most static site generators, but you can customize this behavior by passing your own function as `mapRequestToAsset`. The function should take a `Request` object as its only argument, and return a new `Request` object with an updated path to be looked up in the asset manifest/KV. +By default, mapRequestToAsset is set to the exported function [`mapRequestToAsset`](#maprequesttoasset-1). This works for most static site generators, but you can customize this behavior by passing your own function as `mapRequestToAsset`. The function should take a `Request` object as its only argument, and return a new `Request` object with an updated path to be looked up in the asset manifest/KV. For SPA mapping pass in the [`serveSinglePageApp`](#servesinglepageapp) function @@ -219,8 +220,8 @@ All responses served from cache (including those with `cf-cache-status: MISS`) i Resources served with an `etag` allow browsers to use the `if-none-match` request header to make conditional requests for that resource in the future. This has two major benefits: -* When a request's `if-none-match` value matches the `etag` of the resource in Cloudflare cache, Cloudflare will send a `304 Not Modified` response without a body, saving bandwidth. -* Changes to a file on the server are immediately reflected in the browser - even when the cache control directive `max-age` is unexpired. +- When a request's `if-none-match` value matches the `etag` of the resource in Cloudflare cache, Cloudflare will send a `304 Not Modified` response without a body, saving bandwidth. +- Changes to a file on the server are immediately reflected in the browser - even when the cache control directive `max-age` is unexpired. #### Disable the `etag` @@ -229,7 +230,7 @@ To turn `etags` **off**, you must bypass cache: ```js /* Turn etags off */ let cacheControl = { - bypassCache: true + bypassCache: true, } ``` diff --git a/package-lock.json b/package-lock.json index 0e4c4d9..2b87e69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1874,6 +1874,12 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, + "prettier": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz", + "integrity": "sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==", + "dev": true + }, "pretty-ms": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.0.tgz", diff --git a/package.json b/package.json index 717f40d..f93df8c 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,13 @@ "scripts": { "prepack": "npm run build", "build": "tsc -d", - "format": "prettier --write '**/*.js'", + "format": "prettier --write \"**/*.{js,ts,json,md}\"", "pretest": "npm run build", "test": "ava dist/test/*.js --verbose" }, "repository": { "type": "git", - "url": "git+https://github.com/cloudflare/workers-site-npm-pkg.git" + "url": "git+https://github.com/cloudflare/kv-asset-handler.git" }, "keywords": [ "kv", @@ -35,6 +35,7 @@ }, "devDependencies": { "ava": "^3.9.0", + "prettier": "^2.3.0", "service-worker-mock": "^2.0.5", "typescript": "^3.9.5" } diff --git a/src/index.ts b/src/index.ts index ca07862..b28875c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -85,9 +85,10 @@ const getAssetFromKV = async (event: FetchEvent, options?: Partial): Pr const request = event.request const ASSET_NAMESPACE = options.ASSET_NAMESPACE - const ASSET_MANIFEST = typeof (options.ASSET_MANIFEST) === 'string' - ? JSON.parse(options.ASSET_MANIFEST) - : options.ASSET_MANIFEST + const ASSET_MANIFEST = + typeof options.ASSET_MANIFEST === 'string' + ? JSON.parse(options.ASSET_MANIFEST) + : options.ASSET_MANIFEST if (typeof ASSET_NAMESPACE === 'undefined') { throw new InternalError(`there is no KV namespace bound to the script`) @@ -103,13 +104,13 @@ const getAssetFromKV = async (event: FetchEvent, options?: Partial): Pr } else if (ASSET_MANIFEST[rawPathKey]) { requestKey = request } else if (ASSET_MANIFEST[decodeURIComponent(rawPathKey)]) { - pathIsEncoded = true; + pathIsEncoded = true requestKey = request } else { const mappedRequest = mapRequestToAsset(request) const mappedRawPathKey = new URL(mappedRequest.url).pathname.replace(/^\/+/, '') if (ASSET_MANIFEST[decodeURIComponent(mappedRawPathKey)]) { - pathIsEncoded = true; + pathIsEncoded = true requestKey = mappedRequest } else { // use default mapRequestToAsset @@ -132,7 +133,7 @@ const getAssetFromKV = async (event: FetchEvent, options?: Partial): Pr const cache = caches.default let mimeType = mime.getType(pathKey) || options.defaultMimeType if (mimeType.startsWith('text') || mimeType === 'application/javascript') { - mimeType += '; charset=utf-8' + mimeType += '; charset=utf-8' } let shouldEdgeCache = false // false if storing in KV by raw file path i.e. no hash @@ -197,11 +198,10 @@ const getAssetFromKV = async (event: FetchEvent, options?: Partial): Pr request.headers.get('if-none-match') === `${pathKey}`, ].every(Boolean) - if (shouldRevalidate) { // fixes issue #118 if (response.body && 'cancel' in Object.getPrototypeOf(response.body)) { - response.body.cancel(); + response.body.cancel() console.log('Body exists and environment supports readable streams. Body cancelled') } else { console.log('Environment doesnt support readable streams') @@ -219,7 +219,7 @@ const getAssetFromKV = async (event: FetchEvent, options?: Partial): Pr let opts = { headers, status: 0, - statusText: '' + statusText: '', } if (response.status) { opts.status = response.status diff --git a/src/mocks.ts b/src/mocks.ts index 0e6c6d5..78e5b37 100644 --- a/src/mocks.ts +++ b/src/mocks.ts @@ -47,29 +47,29 @@ export const mockManifest = () => { 'index.html': `index.${HASH}.html`, 'sub/blah.png': `sub/blah.${HASH}.png`, 'sub/index.html': `sub/index.${HASH}.html`, - 'client': `client.${HASH}`, + client: `client.${HASH}`, 'client/index.html': `client.${HASH}`, - '你好/index.html': `你好/index.${HASH}.html` + '你好/index.html': `你好/index.${HASH}.html`, }) } let cacheStore: any = new Map() interface CacheKey { - url:object; - headers:object + url: object + headers: object } export const mockCaches = () => { return { default: { - async match (key: any) { + async match(key: any) { let cacheKey: CacheKey = { url: key.url, - headers: {} + headers: {}, } let response if (key.headers.has('if-none-match')) { cacheKey.headers = { - 'etag': key.headers.get('if-none-match') + etag: key.headers.get('if-none-match'), } response = cacheStore.get(JSON.stringify(cacheKey)) } else { @@ -87,7 +87,10 @@ export const mockCaches = () => { // 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')}`) + 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')) { @@ -98,16 +101,16 @@ export const mockCaches = () => { } return response }, - async put (key: any, val: Response) { + async put(key: any, val: Response) { let headers = new Headers(val.headers) let body = await val.text() let resp = new Response(body, { headers }) - headers.set('content-length', (body.length).toString()) + headers.set('content-length', body.length.toString()) let cacheKey: CacheKey = { url: key.url, headers: { - 'etag': val.headers.get('etag') - } + etag: val.headers.get('etag'), + }, } return cacheStore.set(JSON.stringify(cacheKey), resp) }, @@ -122,5 +125,5 @@ export function mockGlobal() { Object.assign(global, { caches: mockCaches() }) } export const sleep = (milliseconds: number) => { - return new Promise(resolve => setTimeout(resolve, milliseconds)) + return new Promise((resolve) => setTimeout(resolve, milliseconds)) } diff --git a/src/test/getAssetFromKV.ts b/src/test/getAssetFromKV.ts index 0abb304..b5d2d43 100644 --- a/src/test/getAssetFromKV.ts +++ b/src/test/getAssetFromKV.ts @@ -3,7 +3,7 @@ import { mockGlobal, getEvent, sleep, mockKV, mockManifest } from '../mocks' import { getAssetFromKV, mapRequestToAsset } from '../index' import { KVError } from '../types' -test('getAssetFromKV return correct val from KV and default caching', async t => { +test('getAssetFromKV return correct val from KV and default caching', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/key1.txt')) const res = await getAssetFromKV(event) @@ -17,14 +17,14 @@ test('getAssetFromKV return correct val from KV and default caching', async t => t.fail('Response was undefined') } }) -test('getAssetFromKV evaluated the file matching the extensionless path first /client/ -> client', async t => { +test('getAssetFromKV evaluated the file matching the extensionless path first /client/ -> client', async (t) => { mockGlobal() const event = getEvent(new Request(`https://foo.com/client/`)) const res = await getAssetFromKV(event) t.is(await res.text(), 'important file') t.true(res.headers.get('content-type').includes('text')) }) -test('getAssetFromKV evaluated the file matching the extensionless path first /client -> client', async t => { +test('getAssetFromKV evaluated the file matching the extensionless path first /client -> client', async (t) => { mockGlobal() const event = getEvent(new Request(`https://foo.com/client`)) const res = await getAssetFromKV(event) @@ -32,7 +32,7 @@ test('getAssetFromKV evaluated the file matching the extensionless path first /c t.true(res.headers.get('content-type').includes('text')) }) -test('getAssetFromKV if not in asset manifest still returns nohash.txt', async t => { +test('getAssetFromKV if not in asset manifest still returns nohash.txt', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/nohash.txt')) const res = await getAssetFromKV(event) @@ -45,14 +45,14 @@ test('getAssetFromKV if not in asset manifest still returns nohash.txt', async t } }) -test('getAssetFromKV if no asset manifest /client -> client fails', async t => { +test('getAssetFromKV if no asset manifest /client -> client fails', async (t) => { mockGlobal() const event = getEvent(new Request(`https://foo.com/client`)) const error: KVError = await t.throwsAsync(getAssetFromKV(event, { ASSET_MANIFEST: {} })) t.is(error.status, 404) }) -test('getAssetFromKV if sub/ -> sub/index.html served', async t => { +test('getAssetFromKV if sub/ -> sub/index.html served', async (t) => { mockGlobal() const event = getEvent(new Request(`https://foo.com/sub`)) const res = await getAssetFromKV(event) @@ -63,7 +63,7 @@ test('getAssetFromKV if sub/ -> sub/index.html served', async t => { } }) -test('getAssetFromKV gets index.html by default for / requests', async t => { +test('getAssetFromKV gets index.html by default for / requests', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/')) const res = await getAssetFromKV(event) @@ -76,7 +76,7 @@ test('getAssetFromKV gets index.html by default for / requests', async t => { } }) -test('getAssetFromKV non ASCII path support', async t => { +test('getAssetFromKV non ASCII path support', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/测试.html')) const res = await getAssetFromKV(event) @@ -88,7 +88,7 @@ test('getAssetFromKV non ASCII path support', async t => { } }) -test('getAssetFromKV supports browser percent encoded URLs', async t => { +test('getAssetFromKV supports browser percent encoded URLs', async (t) => { mockGlobal() const event = getEvent(new Request('https://example.com/%not-really-percent-encoded.html')) const res = await getAssetFromKV(event) @@ -100,7 +100,7 @@ test('getAssetFromKV supports browser percent encoded URLs', async t => { } }) -test('getAssetFromKV supports user percent encoded URLs', async t => { +test('getAssetFromKV supports user percent encoded URLs', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/%2F.html')) const res = await getAssetFromKV(event) @@ -112,7 +112,7 @@ test('getAssetFromKV supports user percent encoded URLs', async t => { } }) -test('getAssetFromKV only decode URL when necessary', async t => { +test('getAssetFromKV only decode URL when necessary', async (t) => { mockGlobal() const event1 = getEvent(new Request('https://blah.com/%E4%BD%A0%E5%A5%BD.html')) const event2 = getEvent(new Request('https://blah.com/你好.html')) @@ -127,7 +127,7 @@ test('getAssetFromKV only decode URL when necessary', async t => { } }) -test('getAssetFromKV Support for user decode url path', async t => { +test('getAssetFromKV Support for user decode url path', async (t) => { mockGlobal() const event1 = getEvent(new Request('https://blah.com/%E4%BD%A0%E5%A5%BD/')) const event2 = getEvent(new Request('https://blah.com/你好/')) @@ -142,7 +142,7 @@ test('getAssetFromKV Support for user decode url path', async t => { } }) -test('getAssetFromKV custom key modifier', async t => { +test('getAssetFromKV custom key modifier', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/docs/sub/blah.png')) @@ -163,7 +163,7 @@ test('getAssetFromKV custom key modifier', async t => { } }) -test('getAssetFromKV when setting browser caching', async t => { +test('getAssetFromKV when setting browser caching', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/')) @@ -176,7 +176,7 @@ test('getAssetFromKV when setting browser caching', async t => { } }) -test('getAssetFromKV when setting custom cache setting', async t => { +test('getAssetFromKV when setting custom cache setting', async (t) => { mockGlobal() const event1 = getEvent(new Request('https://blah.com/')) const event2 = getEvent(new Request('https://blah.com/key1.png?blah=34')) @@ -204,16 +204,18 @@ test('getAssetFromKV when setting custom cache setting', async t => { t.fail('Response was undefined') } }) -test('getAssetFromKV caches on two sequential requests', async t => { +test('getAssetFromKV caches on two sequential requests', async (t) => { mockGlobal() const resourceKey = 'cache.html' const resourceVersion = JSON.parse(mockManifest())[resourceKey] const event1 = getEvent(new Request(`https://blah.com/${resourceKey}`)) - const event2 = getEvent(new Request(`https://blah.com/${resourceKey}`, { - headers: { - 'if-none-match': resourceVersion - } - })) + const event2 = getEvent( + new Request(`https://blah.com/${resourceKey}`, { + headers: { + 'if-none-match': resourceVersion, + }, + }), + ) const res1 = await getAssetFromKV(event1, { cacheControl: { edgeTTL: 720, browserTTL: 720 } }) await sleep(1) @@ -227,16 +229,18 @@ test('getAssetFromKV caches on two sequential requests', async t => { t.fail('Response was undefined') } }) -test('getAssetFromKV does not store max-age on two sequential requests', async t => { +test('getAssetFromKV does not store max-age on two sequential requests', async (t) => { mockGlobal() const resourceKey = 'cache.html' const resourceVersion = JSON.parse(mockManifest())[resourceKey] const event1 = getEvent(new Request(`https://blah.com/${resourceKey}`)) - const event2 = getEvent(new Request(`https://blah.com/${resourceKey}`, { - headers: { - 'if-none-match': resourceVersion - } - })) + const event2 = getEvent( + new Request(`https://blah.com/${resourceKey}`, { + headers: { + 'if-none-match': resourceVersion, + }, + }), + ) const res1 = await getAssetFromKV(event1, { cacheControl: { edgeTTL: 720 } }) await sleep(100) @@ -252,7 +256,7 @@ test('getAssetFromKV does not store max-age on two sequential requests', async t } }) -test('getAssetFromKV does not cache on Cloudflare when bypass cache set', async t => { +test('getAssetFromKV does not cache on Cloudflare when bypass cache set', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/')) @@ -266,7 +270,7 @@ test('getAssetFromKV does not cache on Cloudflare when bypass cache set', async } }) -test('getAssetFromKV with no trailing slash on root', async t => { +test('getAssetFromKV with no trailing slash on root', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com')) const res = await getAssetFromKV(event) @@ -277,7 +281,7 @@ test('getAssetFromKV with no trailing slash on root', async t => { } }) -test('getAssetFromKV with no trailing slash on a subdirectory', async t => { +test('getAssetFromKV with no trailing slash on a subdirectory', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/sub/blah.png')) const res = await getAssetFromKV(event) @@ -288,13 +292,13 @@ test('getAssetFromKV with no trailing slash on a subdirectory', async t => { } }) -test('getAssetFromKV no result throws an error', async t => { +test('getAssetFromKV no result throws an error', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/random')) const error: KVError = await t.throwsAsync(getAssetFromKV(event)) t.is(error.status, 404) }) -test('getAssetFromKV TTls set to null should not cache on browser or edge', async t => { +test('getAssetFromKV TTls set to null should not cache on browser or edge', async (t) => { mockGlobal() const event = getEvent(new Request('https://blah.com/')) @@ -311,7 +315,7 @@ test('getAssetFromKV TTls set to null should not cache on browser or edge', asyn t.fail('Response was undefined') } }) -test('getAssetFromKV passing in a custom NAMESPACE serves correct asset', async t => { +test('getAssetFromKV passing in a custom NAMESPACE serves correct asset', async (t) => { mockGlobal() let CUSTOM_NAMESPACE = mockKV({ 'key1.123HASHBROWN.txt': 'val1', @@ -326,7 +330,7 @@ test('getAssetFromKV passing in a custom NAMESPACE serves correct asset', async t.fail('Response was undefined') } }) -test('getAssetFromKV when custom namespace without the asset should fail', async t => { +test('getAssetFromKV when custom namespace without the asset should fail', async (t) => { mockGlobal() let CUSTOM_NAMESPACE = mockKV({ 'key5.123HASHBROWN.txt': 'customvalu', @@ -338,7 +342,7 @@ test('getAssetFromKV when custom namespace without the asset should fail', async ) t.is(error.status, 404) }) -test('getAssetFromKV when namespace not bound fails', async t => { +test('getAssetFromKV when namespace not bound fails', async (t) => { mockGlobal() var MY_CUSTOM_NAMESPACE = undefined Object.assign(global, { MY_CUSTOM_NAMESPACE }) @@ -350,16 +354,18 @@ test('getAssetFromKV when namespace not bound fails', async t => { t.is(error.status, 500) }) -test('getAssetFromKV when if-none-match === etag and etag === pathKey in manifest, should revalidate', async t => { +test('getAssetFromKV when if-none-match === etag and etag === pathKey in manifest, should revalidate', async (t) => { mockGlobal() const resourceKey = 'key1.png' const resourceVersion = JSON.parse(mockManifest())[resourceKey] const event1 = getEvent(new Request(`https://blah.com/${resourceKey}`)) - const event2 = getEvent(new Request(`https://blah.com/${resourceKey}`, { - headers: { - 'if-none-match': resourceVersion - } - })) + const event2 = getEvent( + new Request(`https://blah.com/${resourceKey}`, { + headers: { + 'if-none-match': resourceVersion, + }, + }), + ) const res1 = await getAssetFromKV(event1, { cacheControl: { edgeTTL: 720 } }) await sleep(100) @@ -374,19 +380,19 @@ test('getAssetFromKV when if-none-match === etag and etag === pathKey in manifes } }) -test('getAssetFromKV when etag and if-none-match are present but if-none-match !== etag, should bypass cache', async t => { +test('getAssetFromKV when etag and if-none-match are present but if-none-match !== etag, should bypass cache', async (t) => { mockGlobal() const resourceKey = 'key1.png' const resourceVersion = JSON.parse(mockManifest())[resourceKey] const req1 = new Request(`https://blah.com/${resourceKey}`, { headers: { - 'if-none-match': resourceVersion - } + 'if-none-match': resourceVersion, + }, }) const req2 = new Request(`https://blah.com/${resourceKey}`, { headers: { - 'if-none-match': resourceVersion + "another-version" - } + 'if-none-match': resourceVersion + 'another-version', + }, }) const event = getEvent(req1) const event2 = getEvent(req2) @@ -403,7 +409,7 @@ test('getAssetFromKV when etag and if-none-match are present but if-none-match ! t.fail('Response was undefined') } }) -test('getAssetFromKV if-none-match not sent but resource in cache, should return hit', async t => { +test('getAssetFromKV if-none-match not sent but resource in cache, should return hit', async (t) => { const resourceKey = 'cache.html' const event = getEvent(new Request(`https://blah.com/${resourceKey}`)) const res1 = await getAssetFromKV(event, { cacheControl: { edgeTTL: 720 } }) @@ -418,10 +424,12 @@ test('getAssetFromKV if-none-match not sent but resource in cache, should return } }) -test('getAssetFromKV if range request submitted and resource in cache, request fulfilled', async t => { +test('getAssetFromKV if range request submitted and resource in cache, request fulfilled', async (t) => { const resourceKey = 'cache.html' const event1 = getEvent(new Request(`https://blah.com/${resourceKey}`)) - const event2 = getEvent(new Request(`https://blah.com/${resourceKey}`, { headers: { 'range': 'bytes=0-10'}})) + const event2 = getEvent( + new Request(`https://blah.com/${resourceKey}`, { headers: { range: 'bytes=0-10' } }), + ) const res1 = await getAssetFromKV(event1, { cacheControl: { edgeTTL: 720 } }) await res1 await sleep(2) @@ -433,4 +441,4 @@ test('getAssetFromKV if range request submitted and resource in cache, request f } }) -test.todo('getAssetFromKV when body not empty, should invoke .cancel()') \ No newline at end of file +test.todo('getAssetFromKV when body not empty, should invoke .cancel()') diff --git a/src/test/mapRequestToAsset.ts b/src/test/mapRequestToAsset.ts index 1d72c61..a7d6ff7 100644 --- a/src/test/mapRequestToAsset.ts +++ b/src/test/mapRequestToAsset.ts @@ -2,7 +2,7 @@ import test from 'ava' import { mockGlobal } from '../mocks' import { mapRequestToAsset } from '../index' -test('mapRequestToAsset() correctly changes /about -> /about/index.html', async t => { +test('mapRequestToAsset() correctly changes /about -> /about/index.html', async (t) => { mockGlobal() let path = '/about' let request = new Request(`https://foo.com${path}`) @@ -10,14 +10,14 @@ test('mapRequestToAsset() correctly changes /about -> /about/index.html', async t.is(newRequest.url, request.url + '/index.html') }) -test('mapRequestToAsset() correctly changes /about/ -> /about/index.html', async t => { +test('mapRequestToAsset() correctly changes /about/ -> /about/index.html', async (t) => { let path = '/about/' let request = new Request(`https://foo.com${path}`) let newRequest = mapRequestToAsset(request) t.is(newRequest.url, request.url + 'index.html') }) -test('mapRequestToAsset() correctly changes /about.me/ -> /about.me/index.html', async t => { +test('mapRequestToAsset() correctly changes /about.me/ -> /about.me/index.html', async (t) => { let path = '/about.me/' let request = new Request(`https://foo.com${path}`) let newRequest = mapRequestToAsset(request) diff --git a/src/test/serveSinglePageApp.ts b/src/test/serveSinglePageApp.ts index 1b90903..9b95b6d 100644 --- a/src/test/serveSinglePageApp.ts +++ b/src/test/serveSinglePageApp.ts @@ -11,7 +11,7 @@ function testRequest(path: string) { return request } -test('serveSinglePageApp returns root asset path when request path ends in .html', async t => { +test('serveSinglePageApp returns root asset path when request path ends in .html', async (t) => { let path = '/foo/thing.html' let request = testRequest(path) @@ -21,7 +21,7 @@ test('serveSinglePageApp returns root asset path when request path ends in .html t.deepEqual(expected_request, actual_request) }) -test('serveSinglePageApp returns root asset path when request path does not have extension', async t => { +test('serveSinglePageApp returns root asset path when request path does not have extension', async (t) => { let path = '/foo/thing' let request = testRequest(path) @@ -31,7 +31,7 @@ test('serveSinglePageApp returns root asset path when request path does not have t.deepEqual(expected_request, actual_request) }) -test('serveSinglePageApp returns requested asset when request path has non-html extension', async t => { +test('serveSinglePageApp returns requested asset when request path has non-html extension', async (t) => { let path = '/foo/thing.js' let request = testRequest(path) diff --git a/src/types.ts b/src/types.ts index 58762cc..e2b0e37 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,7 +7,7 @@ export type Options = { cacheControl: ((req: Request) => Partial) | Partial ASSET_NAMESPACE: any ASSET_MANIFEST: Object | string - mapRequestToAsset: (req: Request) => Request, + mapRequestToAsset: (req: Request) => Request defaultMimeType: string }