Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@cloudflare/kv-asset-handler

Package Overview
Dependencies
Maintainers
6
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cloudflare/kv-asset-handler - npm Package Compare versions

Comparing version 0.1.3 to 0.2.0

dist/test/getAssetFromKV-optional.d.ts

6

dist/index.d.ts

@@ -30,4 +30,8 @@ import { Options, CacheControl, MethodNotAllowedError, NotFoundError, InternalError } from './types';

* */
declare const getAssetFromKV: (event: FetchEvent, options?: Partial<Options>) => Promise<Response>;
declare type Evt = {
request: Request;
waitUntil: (promise: Promise<any>) => void;
};
declare const getAssetFromKV: (event: Evt, options?: Partial<Options>) => Promise<Response>;
export { getAssetFromKV, mapRequestToAsset, serveSinglePageApp };
export { Options, CacheControl, MethodNotAllowedError, NotFoundError, InternalError };

37

dist/index.js
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -28,6 +19,7 @@ exports.InternalError = exports.NotFoundError = exports.MethodNotAllowedError = exports.serveSinglePageApp = exports.mapRequestToAsset = exports.getAssetFromKV = void 0;

? parseStringAsObject(__STATIC_CONTENT_MANIFEST)
: undefined,
: {},
cacheControl: defaultCacheControl,
defaultMimeType: 'text/plain',
defaultDocument: 'index.html',
pathIsEncoded: false,
};

@@ -88,14 +80,3 @@ function assignOptions(options) {

exports.serveSinglePageApp = serveSinglePageApp;
/**
* takes the path of the incoming request, gathers the appropriate content from KV, and returns
* the response
*
* @param {FetchEvent} event the fetch event of the triggered request
* @param {{mapRequestToAsset: (string: Request) => Request, cacheControl: {bypassCache:boolean, edgeTTL: number, browserTTL:number}, ASSET_NAMESPACE: any, ASSET_MANIFEST:any}} [options] configurable options
* @param {CacheControl} [options.cacheControl] determine how to cache on Cloudflare and the browser
* @param {typeof(options.mapRequestToAsset)} [options.mapRequestToAsset] maps the path of incoming request to the request pathKey to look up
* @param {Object | string} [options.ASSET_NAMESPACE] the binding to the namespace that script references
* @param {any} [options.ASSET_MANIFEST] the map of the key to cache and store in KV
* */
const getAssetFromKV = (event, options) => __awaiter(void 0, void 0, void 0, function* () {
const getAssetFromKV = async (event, options) => {
options = assignOptions(options);

@@ -109,3 +90,3 @@ const request = event.request;

const rawPathKey = new URL(request.url).pathname.replace(/^\/+/, ''); // strip any preceding /'s
let pathIsEncoded = false;
let pathIsEncoded = options.pathIsEncoded;
let requestKey;

@@ -211,3 +192,3 @@ // if options.mapRequestToAsset is explicitly passed in, always use it and assume user has own intentions

if (shouldEdgeCache) {
response = yield cache.match(cacheKey);
response = await cache.match(cacheKey);
}

@@ -217,7 +198,7 @@ if (response) {

if (response.body && 'cancel' in Object.getPrototypeOf(response.body)) {
// Body exists and environment supports readable streams
response.body.cancel();
console.log('Body exists and environment supports readable streams. Body cancelled');
}
else {
console.log('Environment doesnt support readable streams');
// Environment doesnt support readable streams, or null repsonse body. Nothing to do
}

@@ -250,3 +231,3 @@ response = new Response(null, response);

else {
const body = yield ASSET_NAMESPACE.get(pathKey, 'arrayBuffer');
const body = await ASSET_NAMESPACE.get(pathKey, 'arrayBuffer');
if (body === null) {

@@ -291,3 +272,3 @@ throw new types_1.NotFoundError(`could not find ${pathKey} in your content namespace`);

return response;
});
};
exports.getAssetFromKV = getAssetFromKV;
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -16,5 +7,5 @@ exports.sleep = exports.mockGlobalScope = exports.mockRequestScope = exports.mockCaches = exports.mockManifest = exports.mockKV = exports.getEvent = void 0;

const getEvent = (request) => {
const waitUntil = (callback) => __awaiter(void 0, void 0, void 0, function* () {
yield callback;
});
const waitUntil = async (callback) => {
await callback;
};
return {

@@ -76,64 +67,60 @@ request,

default: {
match(key) {
return __awaiter(this, void 0, void 0, function* () {
let 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));
async match(key) {
let 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.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 {
// if client doesn't send if-none-match, we need to iterate through these keys
// and just test the URL
const activeCacheKeys = Array.from(cacheStore.keys());
for (const cacheStoreKey of activeCacheKeys) {
if (JSON.parse(cacheStoreKey).url === key.url) {
response = cacheStore.get(cacheStoreKey);
}
}
response.status = 200;
}
// 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}`);
}
let etag = response.headers.get('etag');
if (etag && !etag.includes('W/')) {
response.headers.set('etag', `W/${etag}`);
}
return response;
});
}
return response;
},
put(key, val) {
return __awaiter(this, void 0, void 0, function* () {
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 = {
url: key.url,
headers: {
etag: `"${url.pathname.replace('/', '')}"`,
},
};
cacheStore.set(JSON.stringify(cacheKey), resNoBody);
cacheKey.headers = {};
cacheStore.set(JSON.stringify(cacheKey), resWithBody);
return;
});
async put(key, val) {
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 = {
url: key.url,
headers: {
etag: `"${url.pathname.replace('/', '')}"`,
},
};
cacheStore.set(JSON.stringify(cacheKey), resNoBody);
cacheKey.headers = {};
cacheStore.set(JSON.stringify(cacheKey), resWithBody);
return;
},

@@ -147,5 +134,5 @@ },

Object.assign(global, makeServiceWorkerEnv());
Object.assign(global, { __STATIC_CONTENT_MANIFEST: exports.mockManifest() });
Object.assign(global, { __STATIC_CONTENT: exports.mockKV(store) });
Object.assign(global, { caches: exports.mockCaches() });
Object.assign(global, { __STATIC_CONTENT_MANIFEST: (0, exports.mockManifest)() });
Object.assign(global, { __STATIC_CONTENT: (0, exports.mockKV)(store) });
Object.assign(global, { caches: (0, exports.mockCaches)() });
}

@@ -155,4 +142,4 @@ exports.mockRequestScope = mockRequestScope;

function mockGlobalScope() {
Object.assign(global, { __STATIC_CONTENT_MANIFEST: exports.mockManifest() });
Object.assign(global, { __STATIC_CONTENT: exports.mockKV(store) });
Object.assign(global, { __STATIC_CONTENT_MANIFEST: (0, exports.mockManifest)() });
Object.assign(global, { __STATIC_CONTENT: (0, exports.mockKV)(store) });
}

@@ -159,0 +146,0 @@ exports.mockGlobalScope = mockGlobalScope;

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const ava_1 = require("ava");
const mocks_1 = require("../mocks");
mocks_1.mockGlobalScope();
(0, mocks_1.mockGlobalScope)();
const index_1 = require("../index");
ava_1.default('getAssetFromKV return correct val from KV and default caching', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/key1.txt'));
const res = yield index_1.getAssetFromKV(event);
(0, ava_1.default)('getAssetFromKV return correct val from KV and default caching', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/key1.txt'));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(res.headers.get('cache-control'), null);
t.is(res.headers.get('cf-cache-status'), 'MISS');
t.is(yield res.text(), 'val1');
t.is(await res.text(), 'val1');
t.true(res.headers.get('content-type').includes('text'));

@@ -29,23 +20,23 @@ }

}
}));
ava_1.default('getAssetFromKV evaluated the file matching the extensionless path first /client/ -> client', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request(`https://foo.com/client/`));
const res = yield index_1.getAssetFromKV(event);
t.is(yield res.text(), 'important file');
});
(0, ava_1.default)('getAssetFromKV evaluated the file matching the extensionless path first /client/ -> client', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request(`https://foo.com/client/`));
const res = await (0, index_1.getAssetFromKV)(event);
t.is(await res.text(), 'important file');
t.true(res.headers.get('content-type').includes('text'));
}));
ava_1.default('getAssetFromKV evaluated the file matching the extensionless path first /client -> client', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request(`https://foo.com/client`));
const res = yield index_1.getAssetFromKV(event);
t.is(yield res.text(), 'important file');
});
(0, ava_1.default)('getAssetFromKV evaluated the file matching the extensionless path first /client -> client', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request(`https://foo.com/client`));
const res = await (0, index_1.getAssetFromKV)(event);
t.is(await res.text(), 'important file');
t.true(res.headers.get('content-type').includes('text'));
}));
ava_1.default('getAssetFromKV if not in asset manifest still returns nohash.txt', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/nohash.txt'));
const res = yield index_1.getAssetFromKV(event);
});
(0, ava_1.default)('getAssetFromKV if not in asset manifest still returns nohash.txt', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/nohash.txt'));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(yield res.text(), 'no hash but still got some result');
t.is(await res.text(), 'no hash but still got some result');
t.true(res.headers.get('content-type').includes('text'));

@@ -56,15 +47,15 @@ }

}
}));
ava_1.default('getAssetFromKV if no asset manifest /client -> client fails', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request(`https://foo.com/client`));
const error = yield t.throwsAsync(index_1.getAssetFromKV(event, { ASSET_MANIFEST: {} }));
});
(0, ava_1.default)('getAssetFromKV if no asset manifest /client -> client fails', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request(`https://foo.com/client`));
const error = await t.throwsAsync((0, index_1.getAssetFromKV)(event, { ASSET_MANIFEST: {} }));
t.is(error.status, 404);
}));
ava_1.default('getAssetFromKV if sub/ -> sub/index.html served', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request(`https://foo.com/sub`));
const res = yield index_1.getAssetFromKV(event);
});
(0, ava_1.default)('getAssetFromKV if sub/ -> sub/index.html served', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request(`https://foo.com/sub`));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(yield res.text(), 'picturedis');
t.is(await res.text(), 'picturedis');
}

@@ -74,9 +65,9 @@ else {

}
}));
ava_1.default('getAssetFromKV gets index.html by default for / requests', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/'));
const res = yield index_1.getAssetFromKV(event);
});
(0, ava_1.default)('getAssetFromKV gets index.html by default for / requests', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/'));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(yield res.text(), 'index.html');
t.is(await res.text(), 'index.html');
t.true(res.headers.get('content-type').includes('html'));

@@ -87,9 +78,9 @@ }

}
}));
ava_1.default('getAssetFromKV non ASCII path support', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/测试.html'));
const res = yield index_1.getAssetFromKV(event);
});
(0, ava_1.default)('getAssetFromKV non ASCII path support', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/测试.html'));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(yield res.text(), 'My filename is non-ascii');
t.is(await res.text(), 'My filename is non-ascii');
}

@@ -99,9 +90,9 @@ else {

}
}));
ava_1.default('getAssetFromKV supports browser percent encoded URLs', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://example.com/%not-really-percent-encoded.html'));
const res = yield index_1.getAssetFromKV(event);
});
(0, ava_1.default)('getAssetFromKV supports browser percent encoded URLs', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://example.com/%not-really-percent-encoded.html'));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(yield res.text(), 'browser percent encoded');
t.is(await res.text(), 'browser percent encoded');
}

@@ -111,9 +102,9 @@ else {

}
}));
ava_1.default('getAssetFromKV supports user percent encoded URLs', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/%2F.html'));
const res = yield index_1.getAssetFromKV(event);
});
(0, ava_1.default)('getAssetFromKV supports user percent encoded URLs', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/%2F.html'));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(yield res.text(), 'user percent encoded');
t.is(await res.text(), 'user percent encoded');
}

@@ -123,12 +114,12 @@ else {

}
}));
ava_1.default('getAssetFromKV only decode URL when necessary', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event1 = mocks_1.getEvent(new Request('https://blah.com/%E4%BD%A0%E5%A5%BD.html'));
const event2 = mocks_1.getEvent(new Request('https://blah.com/你好.html'));
const res1 = yield index_1.getAssetFromKV(event1);
const res2 = yield index_1.getAssetFromKV(event2);
});
(0, ava_1.default)('getAssetFromKV only decode URL when necessary', async (t) => {
(0, mocks_1.mockRequestScope)();
const event1 = (0, mocks_1.getEvent)(new Request('https://blah.com/%E4%BD%A0%E5%A5%BD.html'));
const event2 = (0, mocks_1.getEvent)(new Request('https://blah.com/你好.html'));
const res1 = await (0, index_1.getAssetFromKV)(event1);
const res2 = await (0, index_1.getAssetFromKV)(event2);
if (res1 && res2) {
t.is(yield res1.text(), 'Im important');
t.is(yield res2.text(), 'Im important');
t.is(await res1.text(), 'Im important');
t.is(await res2.text(), 'Im important');
}

@@ -138,12 +129,12 @@ else {

}
}));
ava_1.default('getAssetFromKV Support for user decode url path', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event1 = mocks_1.getEvent(new Request('https://blah.com/%E4%BD%A0%E5%A5%BD/'));
const event2 = mocks_1.getEvent(new Request('https://blah.com/你好/'));
const res1 = yield index_1.getAssetFromKV(event1);
const res2 = yield index_1.getAssetFromKV(event2);
});
(0, ava_1.default)('getAssetFromKV Support for user decode url path', async (t) => {
(0, mocks_1.mockRequestScope)();
const event1 = (0, mocks_1.getEvent)(new Request('https://blah.com/%E4%BD%A0%E5%A5%BD/'));
const event2 = (0, mocks_1.getEvent)(new Request('https://blah.com/你好/'));
const res1 = await (0, index_1.getAssetFromKV)(event1);
const res2 = await (0, index_1.getAssetFromKV)(event2);
if (res1 && res2) {
t.is(yield res1.text(), 'My path is non-ascii');
t.is(yield res2.text(), 'My path is non-ascii');
t.is(await res1.text(), 'My path is non-ascii');
t.is(await res2.text(), 'My path is non-ascii');
}

@@ -153,8 +144,8 @@ else {

}
}));
ava_1.default('getAssetFromKV custom key modifier', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/docs/sub/blah.png'));
});
(0, ava_1.default)('getAssetFromKV custom key modifier', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/docs/sub/blah.png'));
const customRequestMapper = (request) => {
let defaultModifiedRequest = index_1.mapRequestToAsset(request);
let defaultModifiedRequest = (0, index_1.mapRequestToAsset)(request);
let url = new URL(defaultModifiedRequest.url);

@@ -164,5 +155,5 @@ url.pathname = url.pathname.replace('/docs', '');

};
const res = yield index_1.getAssetFromKV(event, { mapRequestToAsset: customRequestMapper });
const res = await (0, index_1.getAssetFromKV)(event, { mapRequestToAsset: customRequestMapper });
if (res) {
t.is(yield res.text(), 'picturedis');
t.is(await res.text(), 'picturedis');
}

@@ -172,9 +163,9 @@ else {

}
}));
ava_1.default('getAssetFromKV request override with existing manifest file', (t) => __awaiter(void 0, void 0, void 0, function* () {
});
(0, ava_1.default)('getAssetFromKV request override with existing manifest file', async (t) => {
// see https://github.com/cloudflare/kv-asset-handler/pull/159 for more info
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/image.png')); // real file in manifest
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/image.png')); // real file in manifest
const customRequestMapper = (request) => {
let defaultModifiedRequest = index_1.mapRequestToAsset(request);
let defaultModifiedRequest = (0, index_1.mapRequestToAsset)(request);
let url = new URL(defaultModifiedRequest.url);

@@ -184,5 +175,5 @@ url.pathname = '/image.webp'; // other different file in manifest

};
const res = yield index_1.getAssetFromKV(event, { mapRequestToAsset: customRequestMapper });
const res = await (0, index_1.getAssetFromKV)(event, { mapRequestToAsset: customRequestMapper });
if (res) {
t.is(yield res.text(), 'imagewebp');
t.is(await res.text(), 'imagewebp');
}

@@ -192,7 +183,7 @@ else {

}
}));
ava_1.default('getAssetFromKV when setting browser caching', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/'));
const res = yield index_1.getAssetFromKV(event, { cacheControl: { browserTTL: 22 } });
});
(0, ava_1.default)('getAssetFromKV when setting browser caching', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/'));
const res = await (0, index_1.getAssetFromKV)(event, { cacheControl: { browserTTL: 22 } });
if (res) {

@@ -204,7 +195,7 @@ t.is(res.headers.get('cache-control'), 'max-age=22');

}
}));
ava_1.default('getAssetFromKV when setting custom cache setting', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event1 = mocks_1.getEvent(new Request('https://blah.com/'));
const event2 = mocks_1.getEvent(new Request('https://blah.com/key1.png?blah=34'));
});
(0, ava_1.default)('getAssetFromKV when setting custom cache setting', async (t) => {
(0, mocks_1.mockRequestScope)();
const event1 = (0, mocks_1.getEvent)(new Request('https://blah.com/'));
const event2 = (0, mocks_1.getEvent)(new Request('https://blah.com/key1.png?blah=34'));
const cacheOnlyPngs = (req) => {

@@ -221,4 +212,4 @@ if (new URL(req.url).pathname.endsWith('.png'))

};
const res1 = yield index_1.getAssetFromKV(event1, { cacheControl: cacheOnlyPngs });
const res2 = yield index_1.getAssetFromKV(event2, { cacheControl: cacheOnlyPngs });
const res1 = await (0, index_1.getAssetFromKV)(event1, { cacheControl: cacheOnlyPngs });
const res2 = await (0, index_1.getAssetFromKV)(event2, { cacheControl: cacheOnlyPngs });
if (res1 && res2) {

@@ -233,9 +224,9 @@ t.is(res1.headers.get('cache-control'), null);

}
}));
ava_1.default('getAssetFromKV caches on two sequential requests', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
});
(0, ava_1.default)('getAssetFromKV caches on two sequential requests', async (t) => {
(0, mocks_1.mockRequestScope)();
const resourceKey = 'cache.html';
const resourceVersion = JSON.parse(mocks_1.mockManifest())[resourceKey];
const event1 = mocks_1.getEvent(new Request(`https://blah.com/${resourceKey}`));
const event2 = mocks_1.getEvent(new Request(`https://blah.com/${resourceKey}`, {
const resourceVersion = JSON.parse((0, mocks_1.mockManifest)())[resourceKey];
const event1 = (0, mocks_1.getEvent)(new Request(`https://blah.com/${resourceKey}`));
const event2 = (0, mocks_1.getEvent)(new Request(`https://blah.com/${resourceKey}`, {
headers: {

@@ -245,5 +236,5 @@ 'if-none-match': `"${resourceVersion}"`,

}));
const res1 = yield index_1.getAssetFromKV(event1, { cacheControl: { edgeTTL: 720, browserTTL: 720 } });
yield mocks_1.sleep(1);
const res2 = yield index_1.getAssetFromKV(event2);
const res1 = await (0, index_1.getAssetFromKV)(event1, { cacheControl: { edgeTTL: 720, browserTTL: 720 } });
await (0, mocks_1.sleep)(1);
const res2 = await (0, index_1.getAssetFromKV)(event2);
if (res1 && res2) {

@@ -257,9 +248,9 @@ t.is(res1.headers.get('cf-cache-status'), 'MISS');

}
}));
ava_1.default('getAssetFromKV does not store max-age on two sequential requests', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
});
(0, ava_1.default)('getAssetFromKV does not store max-age on two sequential requests', async (t) => {
(0, mocks_1.mockRequestScope)();
const resourceKey = 'cache.html';
const resourceVersion = JSON.parse(mocks_1.mockManifest())[resourceKey];
const event1 = mocks_1.getEvent(new Request(`https://blah.com/${resourceKey}`));
const event2 = mocks_1.getEvent(new Request(`https://blah.com/${resourceKey}`, {
const resourceVersion = JSON.parse((0, mocks_1.mockManifest)())[resourceKey];
const event1 = (0, mocks_1.getEvent)(new Request(`https://blah.com/${resourceKey}`));
const event2 = (0, mocks_1.getEvent)(new Request(`https://blah.com/${resourceKey}`, {
headers: {

@@ -269,5 +260,5 @@ 'if-none-match': `"${resourceVersion}"`,

}));
const res1 = yield index_1.getAssetFromKV(event1, { cacheControl: { edgeTTL: 720 } });
yield mocks_1.sleep(100);
const res2 = yield index_1.getAssetFromKV(event2);
const res1 = await (0, index_1.getAssetFromKV)(event1, { cacheControl: { edgeTTL: 720 } });
await (0, mocks_1.sleep)(100);
const res2 = await (0, index_1.getAssetFromKV)(event2);
if (res1 && res2) {

@@ -282,7 +273,7 @@ t.is(res1.headers.get('cf-cache-status'), 'MISS');

}
}));
ava_1.default('getAssetFromKV does not cache on Cloudflare when bypass cache set', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/'));
const res = yield index_1.getAssetFromKV(event, { cacheControl: { bypassCache: true } });
});
(0, ava_1.default)('getAssetFromKV does not cache on Cloudflare when bypass cache set', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/'));
const res = await (0, index_1.getAssetFromKV)(event, { cacheControl: { bypassCache: true } });
if (res) {

@@ -295,9 +286,9 @@ t.is(res.headers.get('cache-control'), null);

}
}));
ava_1.default('getAssetFromKV with no trailing slash on root', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com'));
const res = yield index_1.getAssetFromKV(event);
});
(0, ava_1.default)('getAssetFromKV with no trailing slash on root', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com'));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(yield res.text(), 'index.html');
t.is(await res.text(), 'index.html');
}

@@ -307,9 +298,9 @@ else {

}
}));
ava_1.default('getAssetFromKV with no trailing slash on a subdirectory', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/sub/blah.png'));
const res = yield index_1.getAssetFromKV(event);
});
(0, ava_1.default)('getAssetFromKV with no trailing slash on a subdirectory', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/sub/blah.png'));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(yield res.text(), 'picturedis');
t.is(await res.text(), 'picturedis');
}

@@ -319,15 +310,15 @@ else {

}
}));
ava_1.default('getAssetFromKV no result throws an error', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/random'));
const error = yield t.throwsAsync(index_1.getAssetFromKV(event));
});
(0, ava_1.default)('getAssetFromKV no result throws an error', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/random'));
const error = await t.throwsAsync((0, index_1.getAssetFromKV)(event));
t.is(error.status, 404);
}));
ava_1.default('getAssetFromKV TTls set to null should not cache on browser or edge', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
const event = mocks_1.getEvent(new Request('https://blah.com/'));
const res1 = yield index_1.getAssetFromKV(event, { cacheControl: { browserTTL: null, edgeTTL: null } });
yield mocks_1.sleep(100);
const res2 = yield index_1.getAssetFromKV(event, { cacheControl: { browserTTL: null, edgeTTL: null } });
});
(0, ava_1.default)('getAssetFromKV TTls set to null should not cache on browser or edge', async (t) => {
(0, mocks_1.mockRequestScope)();
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/'));
const res1 = await (0, index_1.getAssetFromKV)(event, { cacheControl: { browserTTL: null, edgeTTL: null } });
await (0, mocks_1.sleep)(100);
const res2 = await (0, index_1.getAssetFromKV)(event, { cacheControl: { browserTTL: null, edgeTTL: null } });
if (res1 && res2) {

@@ -342,13 +333,13 @@ t.is(res1.headers.get('cf-cache-status'), null);

}
}));
ava_1.default('getAssetFromKV passing in a custom NAMESPACE serves correct asset', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
let CUSTOM_NAMESPACE = mocks_1.mockKV({
});
(0, ava_1.default)('getAssetFromKV passing in a custom NAMESPACE serves correct asset', async (t) => {
(0, mocks_1.mockRequestScope)();
let CUSTOM_NAMESPACE = (0, mocks_1.mockKV)({
'key1.123HASHBROWN.txt': 'val1',
});
Object.assign(global, { CUSTOM_NAMESPACE });
const event = mocks_1.getEvent(new Request('https://blah.com/'));
const res = yield index_1.getAssetFromKV(event);
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/'));
const res = await (0, index_1.getAssetFromKV)(event);
if (res) {
t.is(yield res.text(), 'index.html');
t.is(await res.text(), 'index.html');
t.true(res.headers.get('content-type').includes('html'));

@@ -359,26 +350,26 @@ }

}
}));
ava_1.default('getAssetFromKV when custom namespace without the asset should fail', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
let CUSTOM_NAMESPACE = mocks_1.mockKV({
});
(0, ava_1.default)('getAssetFromKV when custom namespace without the asset should fail', async (t) => {
(0, mocks_1.mockRequestScope)();
let CUSTOM_NAMESPACE = (0, mocks_1.mockKV)({
'key5.123HASHBROWN.txt': 'customvalu',
});
const event = mocks_1.getEvent(new Request('https://blah.com'));
const error = yield t.throwsAsync(index_1.getAssetFromKV(event, { ASSET_NAMESPACE: CUSTOM_NAMESPACE }));
const event = (0, mocks_1.getEvent)(new Request('https://blah.com'));
const error = await t.throwsAsync((0, index_1.getAssetFromKV)(event, { ASSET_NAMESPACE: CUSTOM_NAMESPACE }));
t.is(error.status, 404);
}));
ava_1.default('getAssetFromKV when namespace not bound fails', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
});
(0, ava_1.default)('getAssetFromKV when namespace not bound fails', async (t) => {
(0, mocks_1.mockRequestScope)();
var MY_CUSTOM_NAMESPACE = undefined;
Object.assign(global, { MY_CUSTOM_NAMESPACE });
const event = mocks_1.getEvent(new Request('https://blah.com/'));
const error = yield t.throwsAsync(index_1.getAssetFromKV(event, { ASSET_NAMESPACE: MY_CUSTOM_NAMESPACE }));
const event = (0, mocks_1.getEvent)(new Request('https://blah.com/'));
const error = await t.throwsAsync((0, index_1.getAssetFromKV)(event, { ASSET_NAMESPACE: MY_CUSTOM_NAMESPACE }));
t.is(error.status, 500);
}));
ava_1.default('getAssetFromKV when if-none-match === active resource version, should revalidate', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
});
(0, ava_1.default)('getAssetFromKV when if-none-match === active resource version, should revalidate', async (t) => {
(0, mocks_1.mockRequestScope)();
const resourceKey = 'key1.png';
const resourceVersion = JSON.parse(mocks_1.mockManifest())[resourceKey];
const event1 = mocks_1.getEvent(new Request(`https://blah.com/${resourceKey}`));
const event2 = mocks_1.getEvent(new Request(`https://blah.com/${resourceKey}`, {
const resourceVersion = JSON.parse((0, mocks_1.mockManifest)())[resourceKey];
const event1 = (0, mocks_1.getEvent)(new Request(`https://blah.com/${resourceKey}`));
const event2 = (0, mocks_1.getEvent)(new Request(`https://blah.com/${resourceKey}`, {
headers: {

@@ -388,5 +379,5 @@ 'if-none-match': `W/"${resourceVersion}"`,

}));
const res1 = yield index_1.getAssetFromKV(event1, { cacheControl: { edgeTTL: 720 } });
yield mocks_1.sleep(100);
const res2 = yield index_1.getAssetFromKV(event2);
const res1 = await (0, index_1.getAssetFromKV)(event1, { cacheControl: { edgeTTL: 720 } });
await (0, mocks_1.sleep)(100);
const res2 = await (0, index_1.getAssetFromKV)(event2);
if (res1 && res2) {

@@ -399,7 +390,7 @@ t.is(res1.headers.get('cf-cache-status'), 'MISS');

}
}));
ava_1.default('getAssetFromKV when if-none-match equals etag of stale resource then should bypass cache', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
});
(0, ava_1.default)('getAssetFromKV when if-none-match equals etag of stale resource then should bypass cache', async (t) => {
(0, mocks_1.mockRequestScope)();
const resourceKey = 'key1.png';
const resourceVersion = JSON.parse(mocks_1.mockManifest())[resourceKey];
const resourceVersion = JSON.parse((0, mocks_1.mockManifest)())[resourceKey];
const req1 = new Request(`https://blah.com/${resourceKey}`, {

@@ -415,7 +406,7 @@ headers: {

});
const event = mocks_1.getEvent(req1);
const event2 = mocks_1.getEvent(req2);
const res1 = yield index_1.getAssetFromKV(event, { cacheControl: { edgeTTL: 720 } });
const res2 = yield index_1.getAssetFromKV(event);
const res3 = yield index_1.getAssetFromKV(event2);
const event = (0, mocks_1.getEvent)(req1);
const event2 = (0, mocks_1.getEvent)(req2);
const res1 = await (0, index_1.getAssetFromKV)(event, { cacheControl: { edgeTTL: 720 } });
const res2 = await (0, index_1.getAssetFromKV)(event);
const res3 = await (0, index_1.getAssetFromKV)(event2);
if (res1 && res2 && res3) {

@@ -431,7 +422,7 @@ t.is(res1.headers.get('cf-cache-status'), 'MISS');

}
}));
ava_1.default('getAssetFromKV when resource in cache, etag should be weakened before returned to eyeball', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
});
(0, ava_1.default)('getAssetFromKV when resource in cache, etag should be weakened before returned to eyeball', async (t) => {
(0, mocks_1.mockRequestScope)();
const resourceKey = 'key1.png';
const resourceVersion = JSON.parse(mocks_1.mockManifest())[resourceKey];
const resourceVersion = JSON.parse((0, mocks_1.mockManifest)())[resourceKey];
const req1 = new Request(`https://blah.com/${resourceKey}`, {

@@ -442,5 +433,5 @@ headers: {

});
const event = mocks_1.getEvent(req1);
const res1 = yield index_1.getAssetFromKV(event, { cacheControl: { edgeTTL: 720 } });
const res2 = yield index_1.getAssetFromKV(event);
const event = (0, mocks_1.getEvent)(req1);
const res1 = await (0, index_1.getAssetFromKV)(event, { cacheControl: { edgeTTL: 720 } });
const res2 = await (0, index_1.getAssetFromKV)(event);
if (res1 && res2) {

@@ -453,9 +444,9 @@ t.is(res1.headers.get('cf-cache-status'), 'MISS');

}
}));
ava_1.default('getAssetFromKV if-none-match not sent but resource in cache, should return cache hit 200 OK', (t) => __awaiter(void 0, void 0, void 0, function* () {
});
(0, ava_1.default)('getAssetFromKV if-none-match not sent but resource in cache, should return cache hit 200 OK', async (t) => {
const resourceKey = 'cache.html';
const event = mocks_1.getEvent(new Request(`https://blah.com/${resourceKey}`));
const res1 = yield index_1.getAssetFromKV(event, { cacheControl: { edgeTTL: 720 } });
yield mocks_1.sleep(1);
const res2 = yield index_1.getAssetFromKV(event);
const event = (0, mocks_1.getEvent)(new Request(`https://blah.com/${resourceKey}`));
const res1 = await (0, index_1.getAssetFromKV)(event, { cacheControl: { edgeTTL: 720 } });
await (0, mocks_1.sleep)(1);
const res2 = await (0, index_1.getAssetFromKV)(event);
if (res1 && res2) {

@@ -470,11 +461,11 @@ t.is(res1.headers.get('cf-cache-status'), 'MISS');

}
}));
ava_1.default('getAssetFromKV if range request submitted and resource in cache, request fulfilled', (t) => __awaiter(void 0, void 0, void 0, function* () {
});
(0, ava_1.default)('getAssetFromKV if range request submitted and resource in cache, request fulfilled', async (t) => {
const resourceKey = 'cache.html';
const event1 = mocks_1.getEvent(new Request(`https://blah.com/${resourceKey}`));
const event2 = mocks_1.getEvent(new Request(`https://blah.com/${resourceKey}`, { headers: { range: 'bytes=0-10' } }));
const res1 = index_1.getAssetFromKV(event1, { cacheControl: { edgeTTL: 720 } });
yield res1;
yield mocks_1.sleep(2);
const res2 = yield index_1.getAssetFromKV(event2);
const event1 = (0, mocks_1.getEvent)(new Request(`https://blah.com/${resourceKey}`));
const event2 = (0, mocks_1.getEvent)(new Request(`https://blah.com/${resourceKey}`, { headers: { range: 'bytes=0-10' } }));
const res1 = (0, index_1.getAssetFromKV)(event1, { cacheControl: { edgeTTL: 720 } });
await res1;
await (0, mocks_1.sleep)(2);
const res2 = await (0, index_1.getAssetFromKV)(event2);
if (res2.headers.has('content-range')) {

@@ -486,3 +477,3 @@ t.is(res2.status, 206);

}
}));
});
ava_1.default.todo('getAssetFromKV when body not empty, should invoke .cancel()');
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const ava_1 = require("ava");
const mocks_1 = require("../mocks");
mocks_1.mockGlobalScope();
(0, mocks_1.mockGlobalScope)();
const index_1 = require("../index");
ava_1.default('mapRequestToAsset() correctly changes /about -> /about/index.html', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
(0, ava_1.default)('mapRequestToAsset() correctly changes /about -> /about/index.html', async (t) => {
(0, mocks_1.mockRequestScope)();
let path = '/about';
let request = new Request(`https://foo.com${path}`);
let newRequest = index_1.mapRequestToAsset(request);
let newRequest = (0, index_1.mapRequestToAsset)(request);
t.is(newRequest.url, request.url + '/index.html');
}));
ava_1.default('mapRequestToAsset() correctly changes /about/ -> /about/index.html', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
});
(0, ava_1.default)('mapRequestToAsset() correctly changes /about/ -> /about/index.html', async (t) => {
(0, mocks_1.mockRequestScope)();
let path = '/about/';
let request = new Request(`https://foo.com${path}`);
let newRequest = index_1.mapRequestToAsset(request);
let newRequest = (0, index_1.mapRequestToAsset)(request);
t.is(newRequest.url, request.url + 'index.html');
}));
ava_1.default('mapRequestToAsset() correctly changes /about.me/ -> /about.me/index.html', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
});
(0, ava_1.default)('mapRequestToAsset() correctly changes /about.me/ -> /about.me/index.html', async (t) => {
(0, mocks_1.mockRequestScope)();
let path = '/about.me/';
let request = new Request(`https://foo.com${path}`);
let newRequest = index_1.mapRequestToAsset(request);
let newRequest = (0, index_1.mapRequestToAsset)(request);
t.is(newRequest.url, request.url + 'index.html');
}));
ava_1.default('mapRequestToAsset() correctly changes /about -> /about/default.html', (t) => __awaiter(void 0, void 0, void 0, function* () {
mocks_1.mockRequestScope();
});
(0, ava_1.default)('mapRequestToAsset() correctly changes /about -> /about/default.html', async (t) => {
(0, mocks_1.mockRequestScope)();
let path = '/about';
let request = new Request(`https://foo.com${path}`);
let newRequest = index_1.mapRequestToAsset(request, { defaultDocument: 'default.html' });
let newRequest = (0, index_1.mapRequestToAsset)(request, { defaultDocument: 'default.html' });
t.is(newRequest.url, request.url + '/default.html');
}));
});
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const ava_1 = require("ava");
const mocks_1 = require("../mocks");
mocks_1.mockGlobalScope();
(0, mocks_1.mockGlobalScope)();
const index_1 = require("../index");
function testRequest(path) {
mocks_1.mockRequestScope();
(0, mocks_1.mockRequestScope)();
let url = new URL('https://example.com');

@@ -23,22 +14,22 @@ url.pathname = path;

}
ava_1.default('serveSinglePageApp returns root asset path when request path ends in .html', (t) => __awaiter(void 0, void 0, void 0, function* () {
(0, ava_1.default)('serveSinglePageApp returns root asset path when request path ends in .html', async (t) => {
let path = '/foo/thing.html';
let request = testRequest(path);
let expected_request = testRequest('/index.html');
let actual_request = index_1.serveSinglePageApp(request);
let actual_request = (0, index_1.serveSinglePageApp)(request);
t.deepEqual(expected_request, actual_request);
}));
ava_1.default('serveSinglePageApp returns root asset path when request path does not have extension', (t) => __awaiter(void 0, void 0, void 0, function* () {
});
(0, ava_1.default)('serveSinglePageApp returns root asset path when request path does not have extension', async (t) => {
let path = '/foo/thing';
let request = testRequest(path);
let expected_request = testRequest('/index.html');
let actual_request = index_1.serveSinglePageApp(request);
let actual_request = (0, index_1.serveSinglePageApp)(request);
t.deepEqual(expected_request, actual_request);
}));
ava_1.default('serveSinglePageApp returns requested asset when request path has non-html extension', (t) => __awaiter(void 0, void 0, void 0, function* () {
});
(0, ava_1.default)('serveSinglePageApp returns requested asset when request path has non-html extension', async (t) => {
let path = '/foo/thing.js';
let request = testRequest(path);
let expected_request = request;
let actual_request = index_1.serveSinglePageApp(request);
let actual_request = (0, index_1.serveSinglePageApp)(request);
t.deepEqual(expected_request, actual_request);
}));
});

@@ -14,2 +14,3 @@ export declare type CacheControl = {

defaultDocument: string;
pathIsEncoded: boolean;
};

@@ -16,0 +17,0 @@ export declare class KVError extends Error {

{
"name": "@cloudflare/kv-asset-handler",
"version": "0.1.3",
"version": "0.2.0",
"description": "Routes requests to KV assets",

@@ -40,14 +40,14 @@ "main": "dist/index.js",

"dependencies": {
"mime": "^2.5.2"
"mime": "^3.0.0"
},
"devDependencies": {
"@ava/typescript": "^1.1.1",
"@cloudflare/workers-types": "^2.2.2",
"@ava/typescript": "^3.0.0",
"@cloudflare/workers-types": "^3.0.0",
"@types/mime": "^2.0.3",
"@types/node": "^15.3.0",
"@types/node": "^15.14.9",
"ava": "^3.15.0",
"prettier": "^2.3.0",
"prettier": "^2.4.1",
"service-worker-mock": "^2.0.5",
"typescript": "^4.2.4"
"typescript": "^4.4.4"
}
}

@@ -26,11 +26,11 @@ # @cloudflare/kv-asset-handler

- [`cacheControl`](#-cachecontrol)
- [`browserTTL`](#-browserttl)
- [`edgeTTL`](#-edgettl)
- [`bypassCache`](#-bypasscache)
- [`ASSET_NAMESPACE`](#-asset-namespace)
- [`ASSET_MANIFEST` (optional)](#-asset-manifest---optional)
- [`browserTTL`](#browserttl)
- [`edgeTTL`](#edgettl)
- [`bypassCache`](#bypasscache)
- [`ASSET_NAMESPACE` (required for ES Modules)](#asset_namespace-required-for-es-modules)
- [`ASSET_MANIFEST` (required for ES Modules)](#asset_manifest-required-for-es-modules)
* [Helper functions](#helper-functions)
- [`mapRequestToAsset`](#-maprequesttoasset-1)
- [`serveSinglePageApp`](#-servesinglepageapp)
- [`mapRequestToAsset`](#maprequesttoasset-1)
- [`serveSinglePageApp`](#servesinglepageapp)
* [Cache revalidation and etags](#cache-revalidation-and-etags)

@@ -53,5 +53,5 @@

getAssetFromKV(FetchEvent) => Promise<Response>
getAssetFromKV(Evt) => Promise<Response>
`getAssetFromKV` is an async function that takes a `FetchEvent` object and returns a `Response` object if the request matches an asset in KV, otherwise it will throw a `KVError`.
`getAssetFromKV` is an async function that takes an `Evt` object (containing a `Request` and a [`waitUntil`](https://developers.cloudflare.com/workers/runtime-apis/fetch-event#waituntil)) and returns a `Response` object if the request matches an asset in KV, otherwise it will throw a `KVError`.

@@ -72,5 +72,44 @@ #### Example

#### ES Modules
```js
import { getAssetFromKV, NotFoundError, MethodNotAllowedError } from '@cloudflare/kv-asset-handler'
import manifestJSON from '__STATIC_CONTENT_MANIFEST'
const assetManifest = JSON.parse(manifestJSON)
export default {
async fetch(request, env, ctx) {
if (request.url.includes('/docs')) {
try {
return await getAssetFromKV(
{
request,
waitUntil(promise) {
return ctx.waitUntil(promise)
},
},
{
ASSET_NAMESPACE: env.__STATIC_CONTENT,
ASSET_MANIFEST: assetManifest,
},
)
} catch (e) {
if (e instanceof NotFoundError) {
// ...
} else if (e instanceof MethodNotAllowedError) {
// ...
} else {
return new Response('An unexpected error occurred', { status: 500 })
}
}
} else return fetch(request)
},
}
```
#### Service Worker
```js
import { getAssetFromKV, NotFoundError, MethodNotAllowedError } from '@cloudflare/kv-asset-handler'
addEventListener('fetch', (event) => {

@@ -165,3 +204,3 @@ event.respondWith(handleEvent(event))

#### `ASSET_NAMESPACE`
#### `ASSET_NAMESPACE` (required for ES Modules)

@@ -174,9 +213,29 @@ type: KV Namespace Binding

In ES Modules format, this argument is required, and can be gotten from `env`.
##### ES Module
```js
return getAssetFromKV(
{
request,
waitUntil(promise) {
return ctx.waitUntil(promise)
},
},
{
ASSET_NAMESPACE: env.__STATIC_CONTENT,
},
)
```
##### Service Worker
```
return getAssetFromKV(event, { ASSET_NAMESPACE: MY_NAMESPACE })
```
#### `ASSET_MANIFEST` (optional)
#### `ASSET_MANIFEST` (required for ES Modules)
type: text blob (JSON formatted)
type: text blob (JSON formatted) or object

@@ -187,5 +246,30 @@ The mapping of requested file path to the key stored on Cloudflare.

In ES Modules format, this argument is required, and can be imported.
##### ES Module
```js
import manifestJSON from '__STATIC_CONTENT_MANIFEST'
let manifest = JSON.parse(manifestJSON)
manifest['index.html'] = 'index.special.html'
return getAssetFromKV(
{
request,
waitUntil(promise) {
return ctx.waitUntil(promise)
},
},
{
ASSET_MANIFEST: manifest,
// ...
},
)
```
##### Service Worker
```
let assetManifest = { "index.html": "index.special.html" }
return getAssetFromKV(event, { ASSET_MANIFEST: JSON.stringify(assetManifest) })
return getAssetFromKV(event, { ASSET_MANIFEST: assetManifest })
```

@@ -192,0 +276,0 @@

@@ -29,6 +29,7 @@ import * as mime from 'mime'

? parseStringAsObject<AssetManifestType>(__STATIC_CONTENT_MANIFEST)
: undefined,
: {},
cacheControl: defaultCacheControl,
defaultMimeType: 'text/plain',
defaultDocument: 'index.html',
pathIsEncoded: false,
}

@@ -106,3 +107,9 @@

* */
const getAssetFromKV = async (event: FetchEvent, options?: Partial<Options>): Promise<Response> => {
type Evt = {
request: Request
waitUntil: (promise: Promise<any>) => void
}
const getAssetFromKV = async (event: Evt, options?: Partial<Options>): Promise<Response> => {
options = assignOptions(options)

@@ -119,3 +126,3 @@

const rawPathKey = new URL(request.url).pathname.replace(/^\/+/, '') // strip any preceding /'s
let pathIsEncoded = false
let pathIsEncoded = options.pathIsEncoded
let requestKey

@@ -236,6 +243,6 @@ // if options.mapRequestToAsset is explicitly passed in, always use it and assume user has own intentions

if (response.body && 'cancel' in Object.getPrototypeOf(response.body)) {
// Body exists and environment supports readable streams
response.body.cancel()
console.log('Body exists and environment supports readable streams. Body cancelled')
} else {
console.log('Environment doesnt support readable streams')
// Environment doesnt support readable streams, or null repsonse body. Nothing to do
}

@@ -242,0 +249,0 @@ response = new Response(null, response)

@@ -16,2 +16,3 @@ export type CacheControl = {

defaultDocument: string
pathIsEncoded: boolean
}

@@ -18,0 +19,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc