From 0ff32749319a88b4d9b7dad8eb61fbc9fe3cc2b2 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Fri, 5 Dec 2025 16:04:09 +0000 Subject: [PATCH 1/4] Make simple cache more async --- runtime/fastly/builtins/cache-simple.cpp | 135 +++++++++++++++-------- 1 file changed, 92 insertions(+), 43 deletions(-) diff --git a/runtime/fastly/builtins/cache-simple.cpp b/runtime/fastly/builtins/cache-simple.cpp index 9b667a7c07..68d48f581a 100644 --- a/runtime/fastly/builtins/cache-simple.cpp +++ b/runtime/fastly/builtins/cache-simple.cpp @@ -17,6 +17,10 @@ using fastly::FastlyGetErrorMessage; using fastly::fastly::convertBodyInit; using fastly::fetch::RequestOrResponse; +namespace { +api::Engine *GLOBAL_ENGINE; +} + namespace fastly::cache_simple { template @@ -394,62 +398,39 @@ bool get_or_set_catch_handler(JSContext *cx, JS::HandleObject lookup_state, return true; } -} // namespace - -// static getOrSet(key: string, set: () => Promise<{value: BodyInit, ttl: number}>): -// SimpleCacheEntry | null; static getOrSet(key: string, set: () => Promise<{value: ReadableStream, -// ttl: number, length: number}>): SimpleCacheEntry | null; -bool SimpleCache::getOrSet(JSContext *cx, unsigned argc, JS::Value *vp) { - REQUEST_HANDLER_ONLY("The SimpleCache builtin"); - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - if (!args.requireAtLeast(cx, "SimpleCache.getOrSet", 2)) { - return false; - } - - // Convert key parameter into a string and check the value adheres to our validation rules. - auto key_chars = core::encode(cx, args.get(0)); - if (!key_chars) { +bool process_pending_cache_lookup(JSContext *cx, host_api::CacheHandle::Handle handle, + JS::HandleObject context_obj, JS::HandleValue) { + host_api::CacheHandle pending_lookup(handle); + JS::RootedValue key_val(cx); + if (!JS_GetProperty(cx, context_obj, "key", &key_val)) { return false; } - - if (key_chars.len == 0) { - JS_ReportErrorASCII(cx, "SimpleCache.getOrSet: key can not be an empty string"); + auto key_chars = core::encode(cx, key_val); + JS::RootedValue set_function_val(cx); + if (!JS_GetProperty(cx, context_obj, "set_function", &set_function_val)) { return false; } - if (key_chars.len > 8135) { - JS_ReportErrorASCII( - cx, "SimpleCache.getOrSet: key is too long, the maximum allowed length is 8135."); + JS::RootedValue promise_val(cx); + if (!JS_GetProperty(cx, context_obj, "promise", &promise_val)) { return false; } + JS::RootedObject promise_obj(cx, &promise_val.toObject()); - JS::RootedObject promise(cx, JS::NewPromiseObject(cx, nullptr)); - if (!promise) { - return ReturnPromiseRejectedWithPendingError(cx, args); - } - - auto res = host_api::CacheHandle::transaction_lookup(key_chars, host_api::CacheLookupOptions{}); - if (auto *err = res.to_err()) { - HANDLE_ERROR(cx, *err); - return false; - } - - auto handle = res.unwrap(); - BEGIN_TRANSACTION(transaction, cx, promise, handle); + BEGIN_TRANSACTION(transaction, cx, promise_obj, pending_lookup); // Check if a fresh cache item was found, if that's the case, then we will resolve // with a SimpleCacheEntry containing the value. Else, call the content-provided // function in the `set` parameter and insert it's returned value property into the // cache under the provided `key`, and then we will resolve with a SimpleCacheEntry // containing the value. - auto state_res = handle.get_state(); + auto state_res = pending_lookup.get_state(); if (auto *err = state_res.to_err()) { return false; } auto state = state_res.unwrap(); - args.rval().setObject(*promise); if (state.is_usable()) { - auto body_res = handle.get_body(host_api::CacheGetBodyOptions{}); + auto body_res = pending_lookup.get_body(host_api::CacheGetBodyOptions{}); if (auto *err = body_res.to_err()) { return false; } @@ -461,16 +442,15 @@ bool SimpleCache::getOrSet(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedValue result(cx); result.setObject(*entry); - JS::ResolvePromise(cx, promise, result); + JS::ResolvePromise(cx, promise_obj, result); return true; } else { - auto arg1 = args.get(1); - if (!arg1.isObject() || !JS::IsCallable(&arg1.toObject())) { + if (!set_function_val.isObject() || !JS::IsCallable(&set_function_val.toObject())) { JS_ReportErrorLatin1(cx, "SimpleCache.getOrSet: set argument is not a function"); return false; } JS::RootedValueArray<0> fnargs(cx); - JS::RootedObject fn(cx, &arg1.toObject()); + JS::RootedObject fn(cx, &set_function_val.toObject()); JS::RootedValue result(cx); if (!JS::Call(cx, JS::NullHandleValue, fn, fnargs, &result)) { return false; @@ -483,7 +463,7 @@ bool SimpleCache::getOrSet(JSContext *cx, unsigned argc, JS::Value *vp) { // JS::RootedObject owner(cx, JS_NewPlainObject(cx)); JS::RootedObject lookup_state(cx, JS_NewPlainObject(cx)); - JS::RootedValue handle_val(cx, JS::NumberValue(handle.handle)); + JS::RootedValue handle_val(cx, JS::NumberValue(handle)); if (!JS_SetProperty(cx, lookup_state, "handle", handle_val)) { return false; } @@ -492,7 +472,7 @@ bool SimpleCache::getOrSet(JSContext *cx, unsigned argc, JS::Value *vp) { if (!JS_SetProperty(cx, lookup_state, "key", keyVal)) { return false; } - JS::RootedValue promise_val(cx, JS::ObjectValue(*promise)); + JS::RootedValue promise_val(cx, JS::ObjectValue(*promise_obj)); if (!JS_SetProperty(cx, lookup_state, "promise", promise_val)) { return false; } @@ -517,6 +497,73 @@ bool SimpleCache::getOrSet(JSContext *cx, unsigned argc, JS::Value *vp) { } } +} // namespace + +// static getOrSet(key: string, set: () => Promise<{value: BodyInit, ttl: number}>): +// SimpleCacheEntry | null; static getOrSet(key: string, set: () => Promise<{value: ReadableStream, +// ttl: number, length: number}>): SimpleCacheEntry | null; +bool SimpleCache::getOrSet(JSContext *cx, unsigned argc, JS::Value *vp) { + REQUEST_HANDLER_ONLY("The SimpleCache builtin"); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + if (!args.requireAtLeast(cx, "SimpleCache.getOrSet", 2)) { + return false; + } + + // Convert key parameter into a string and check the value adheres to our validation rules. + auto key_chars = core::encode(cx, args.get(0)); + if (!key_chars) { + return false; + } + + if (key_chars.len == 0) { + JS_ReportErrorASCII(cx, "SimpleCache.getOrSet: key can not be an empty string"); + return false; + } + if (key_chars.len > 8135) { + JS_ReportErrorASCII( + cx, "SimpleCache.getOrSet: key is too long, the maximum allowed length is 8135."); + return false; + } + + JS::RootedObject promise(cx, JS::NewPromiseObject(cx, nullptr)); + if (!promise) { + return ReturnPromiseRejectedWithPendingError(cx, args); + } + + auto res = host_api::CacheHandle::transaction_lookup(key_chars, host_api::CacheLookupOptions{}); + if (auto *err = res.to_err()) { + HANDLE_ERROR(cx, *err); + return false; + } + + // The async task requires some extra state: the key, the `set` function, and the promise. + // We wrap this all up into one object, so `process_pending_cache_lookup` can retrieve everything. + // This could be avoided with some changes to `FastlyAsyncTask`, see + // (https://github.com/fastly/js-compute-runtime/issues/1245) + JS::RootedObject context_obj(cx, JS_NewPlainObject(cx)); + if (!context_obj) { + return false; + } + JS::RootedValue key_val(cx, args.get(0)); + if (!JS_SetProperty(cx, context_obj, "key", key_val)) { + return false; + } + JS::RootedValue set_func_val(cx, args.get(1)); + if (!JS_SetProperty(cx, context_obj, "set_function", set_func_val)) { + return false; + } + JS::RootedValue promise_val(cx, JS::ObjectValue(*promise)); + if (!JS_SetProperty(cx, context_obj, "promise", promise_val)) { + return false; + } + auto handle = res.unwrap(); + GLOBAL_ENGINE->queue_async_task(new FastlyAsyncTask( + handle.handle, context_obj, JS::UndefinedHandleValue, process_pending_cache_lookup)); + + args.rval().setObject(*promise); + return true; +} + // static set(key: string, value: BodyInit, ttl: number): undefined; // static set(key: string, value: ReadableStream, ttl: number, length: number): undefined; bool SimpleCache::set(JSContext *cx, unsigned argc, JS::Value *vp) { @@ -791,6 +838,8 @@ const JSPropertySpec SimpleCache::properties[] = { JS_STRING_SYM_PS(toStringTag, "SimpleCache", JSPROP_READONLY), JS_PS_END}; bool install(api::Engine *engine) { + GLOBAL_ENGINE = engine; + if (!SimpleCacheEntry::init_class_impl(engine->cx(), engine->global())) { return false; } From c1e4e4aee1022b93cc97bb16cfb9c6f628da61dc Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Mon, 8 Dec 2025 15:11:24 +0000 Subject: [PATCH 2/4] Add test --- .../js-compute/fixtures/app/__input_bundled.js.map | 7 +++++++ .../js-compute/fixtures/app/src/cache-simple.js | 10 ++++++++++ 2 files changed, 17 insertions(+) create mode 100644 integration-tests/js-compute/fixtures/app/__input_bundled.js.map diff --git a/integration-tests/js-compute/fixtures/app/__input_bundled.js.map b/integration-tests/js-compute/fixtures/app/__input_bundled.js.map new file mode 100644 index 0000000000..428b9f338f --- /dev/null +++ b/integration-tests/js-compute/fixtures/app/__input_bundled.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["env", "src/routes.js", "experimental", "src/async-select.js", "src/assertions.js", "src/btoa.js", "src/byob.js", "cache-override", "src/byte-repeater.js", "src/cache-override.js", "cache", "body", "src/cache-core.js", "src/cache-simple.js", "src/client.js", "compute", "src/compute.js", "config-store", "src/config-store.js", "src/crypto.js", "device", "src/device.js", "dictionary", "src/dictionary.js", "src/early-hints.js", "edge-rate-limiter", "src/edge-rate-limiter.js", "src/env.js", "fanout", "src/fanout.js", "websocket", "src/websocket.js", "src/fastly-global.js", "backend", "src/fetch-errors.js", "geolocation", "src/geoip.js", "src/headers.js", "html-rewriter", "src/html-rewriter.js", "src/include-bytes.js", "image-optimizer", "src/image-optimizer.js", "logger", "src/logger.js", "src/manual-framing-headers.js", "src/missing-backend.js", "src/multiple-set-cookie.js", "src/performance.js", "src/random.js", "src/react-byob.js", "src/request-auto-decompress.js", "src/request-cache-key.js", "src/request-clone.js", "src/request-headers.js", "src/request-method.js", "src/response-json.js", "src/response-redirect.js", "src/response.js", "secret-store", "src/fast-check.js", "src/secret-store.js", "src/server.js", "src/tee.js", "src/timers.js", "src/urlsearchparams.js", "src/index.js"], + "sourcesContent": ["export const env = globalThis.fastly.env.get;", "import { env } from 'fastly:env';\n\n/**\n * @type {Map Promise>}\n */\nexport const routes = new Map();\nroutes.set('/', () => {\n routes.delete('/');\n let test_routes = Array.from(routes.keys());\n return new Response(JSON.stringify(test_routes), {\n headers: { 'content-type': 'application/json' },\n });\n});\n\nexport function isRunningLocally() {\n return (\n env('FASTLY_SERVICE_VERSION') === '' ||\n env('FASTLY_SERVICE_VERSION') === '0'\n );\n}\n", "\nexport const includeBytes = globalThis.fastly.includeBytes;\nexport const enableDebugLogging = globalThis.fastly.enableDebugLogging;\nexport const setBaseURL = Object.getOwnPropertyDescriptor(globalThis.fastly, 'baseURL').set;\nexport const setDefaultBackend = Object.getOwnPropertyDescriptor(globalThis.fastly, 'defaultBackend').set;\nexport const allowDynamicBackends = Object.getOwnPropertyDescriptor(globalThis.fastly, 'allowDynamicBackends').set;\nexport const sdkVersion = globalThis.fastly.sdkVersion;\nexport const mapAndLogError = (e) => globalThis.__fastlyMapAndLogError(e);\nexport const mapError = (e) => globalThis.__fastlyMapError(e);\n", "import { routes } from './routes';\n\nroutes.set('/async-select/hello', () => {\n let requestsData = [\n {\n url: 'https://compute-sdk-test-backend.edgecompute.app/async_select_1',\n backend: 'TheOrigin',\n header: 'fooname',\n },\n {\n url: 'https://compute-sdk-test-backend.edgecompute.app/async_select_2',\n backend: 'TheOrigin2',\n header: 'barname',\n },\n ];\n\n let pending = requestsData.map((data) => {\n let request = new Request(data.url);\n return fetch(request, { backend: data.backend });\n });\n\n return processUpstreamRequests(pending, requestsData);\n});\n\nasync function processUpstreamRequests(requests, requestsData) {\n // Create our response headers we will be sending downstream\n let responseHeaders = new Headers();\n\n // Loop through our requests\n for await (let response of select(requests)) {\n // Check which response we got, so we know which header to\n // copy over to our response headers\n let { header } = requestsData.find((data) => data.url == response.url);\n\n // Set the appropriate header on our response\n let headerValue = response.headers.get(header);\n if (headerValue == null) {\n throw new Error(\n 'No header value on the response from the request with url ' +\n response.url +\n ' and with the header name: ' +\n header,\n );\n }\n responseHeaders.set(header, headerValue);\n }\n\n // Send our response downstream\n return new Response('pong', {\n headers: responseHeaders,\n });\n}\n\nasync function* select(promises) {\n promises = promises.map((promise) => {\n promise.finally(() => {\n promises.splice(promises.indexOf(promise), 1);\n });\n return promise;\n });\n\n while (promises.length) {\n yield await Promise.any(promises);\n }\n}\n", "// Testing/Assertion functions //\n\nexport async function sleep(milliseconds) {\n return new Promise((resolve) => {\n setTimeout(resolve, milliseconds);\n });\n}\n\n// TODO: Implement ReadableStream getIterator() and [@@asyncIterator]() methods\nexport async function streamToString(stream) {\n const decoder = new TextDecoder();\n let string = '';\n let reader = stream.getReader();\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n return string;\n }\n string += decoder.decode(value);\n }\n}\n\nexport function iteratableToStream(iterable) {\n return new ReadableStream({\n async pull(controller) {\n for await (const value of iterable) {\n controller.enqueue(value);\n }\n controller.close();\n },\n });\n}\n\nexport function pass(message = '') {\n return new Response(message);\n}\n\nfunction prettyPrintSymbol(a) {\n if (typeof a === 'symbol') {\n return String(a);\n }\n return a;\n}\nexport function assert(actual, expected, code) {\n if (!deepEqual(actual, expected)) {\n throw new Error(\n `Expected \\`${code}\\` to equal \\`${JSON.stringify(prettyPrintSymbol(expected))}\\` - Found \\`${JSON.stringify(prettyPrintSymbol(actual))}\\``,\n );\n }\n}\n\nexport { assert as strictEqual };\n\nexport function ok(truthy, code) {\n if (!truthy) {\n throw new Error(\n `Expected ${code ? code + ' ' : ''}to be truthy - Found \\`${JSON.stringify(prettyPrintSymbol(truthy))}\\``,\n );\n }\n}\n\nexport async function assertResolves(func) {\n try {\n await func();\n } catch (error) {\n throw new Error(\n `Expected \\`${func.toString()}\\` to resolve - Found it rejected: ${error.name}: ${error.message}`,\n );\n }\n}\n\nexport async function assertRejects(func, errorClass, errorMessage) {\n try {\n await func();\n } catch (error) {\n if (errorClass) {\n if (error instanceof errorClass === false) {\n throw new Error(\n `Expected \\`${func.toString()}\\` to reject instance of \\`${errorClass.name}\\` - Found instance of \\`${error.name}\\``,\n );\n }\n }\n\n if (errorMessage) {\n if (error.message !== errorMessage) {\n throw new Error(\n `Expected \\`${func.toString()}\\` to reject error message of \\`${errorMessage}\\` - Found \\`${error.message}\\``,\n );\n }\n }\n\n return;\n }\n throw new Error(\n `Expected \\`${func.toString()}\\` to reject - Found it did not reject`,\n );\n}\n\nexport function assertThrows(func, errorClass, errorMessage) {\n try {\n func();\n } catch (error) {\n if (errorClass) {\n if (error instanceof errorClass === false) {\n throw new Error(\n `Expected \\`${func.toString()}\\` to throw instance of \\`${errorClass.name}\\` - Found instance of \\`${error.name}\\`: ${error.message}\\n${error.stack}`,\n );\n }\n }\n\n if (errorMessage) {\n if (error.message !== errorMessage) {\n throw new Error(\n `Expected \\`${func.toString()}\\` to throw error message of \\`${errorMessage}\\` - Found \\`${error.message}\\``,\n );\n }\n }\n\n return;\n }\n throw new Error(\n `Expected \\`${func.toString()}\\` to throw - Found it did not throw`,\n );\n}\n\nexport function assertDoesNotThrow(func) {\n try {\n func();\n } catch (error) {\n throw new Error(\n `Expected \\`${func.toString()}\\` to not throw - Found it did throw: ${error.name}: ${error.message}`,\n );\n }\n}\n\nexport function deepStrictEqual(a, b) {\n if (!deepEqual(a, b)) {\n throw new Error(`Expected ${a} to equal ${b}`);\n }\n}\n\nexport function deepEqual(a, b) {\n var aKeys;\n var bKeys;\n var typeA;\n var typeB;\n var key;\n var i;\n\n typeA = typeof a;\n typeB = typeof b;\n if (a === null || typeA !== 'object') {\n if (b === null || typeB !== 'object') {\n return a === b;\n }\n return false;\n }\n\n // Case: `a` is of type 'object'\n if (b === null || typeB !== 'object') {\n return false;\n }\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) {\n return false;\n }\n }\n return true;\n }\n if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) {\n return false;\n }\n if (a instanceof Date) {\n return a.getTime() === b.getTime();\n }\n if (a instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags;\n }\n if (a instanceof Error) {\n if (a.message !== b.message || a.name !== b.name) {\n return false;\n }\n }\n\n aKeys = Object.keys(a);\n bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) {\n return false;\n }\n aKeys.sort();\n bKeys.sort();\n\n // Cheap key test:\n for (i = 0; i < aKeys.length; i++) {\n if (aKeys[i] !== bKeys[i]) {\n return false;\n }\n }\n // Possibly expensive deep equality test for each corresponding key:\n for (i = 0; i < aKeys.length; i++) {\n key = aKeys[i];\n if (!deepEqual(a[key], b[key])) {\n return false;\n }\n }\n return typeA === typeB;\n}\n", "import { assert, assertThrows } from './assertions.js';\nimport { routes } from './routes';\n\nroutes.set('/btoa', () => {\n // btoa\n {\n var everything = '';\n for (var i = 0; i < 256; i++) {\n everything += String.fromCharCode(i);\n }\n assert(\n btoa(everything),\n 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==',\n );\n\n assert(btoa(42), btoa('42'));\n assert(btoa(null), btoa('null'));\n assert(btoa({ x: 1 }), btoa('[object Object]'));\n\n assertThrows(() => btoa(), TypeError);\n assertThrows(() => btoa('\uD83D\uDC31'));\n\n assert(btoa(''), '', `btoa(\"\")`);\n assert(btoa('ab'), 'YWI=', `btoa(\"ab\")`);\n assert(btoa('abc'), 'YWJj', `btoa(\"abc\")`);\n assert(btoa('abcd'), 'YWJjZA==', `btoa(\"abcd\")`);\n assert(btoa('abcde'), 'YWJjZGU=', `btoa(\"abcde\")`);\n assert(btoa('\u00FF\u00FF\u00C0'), '///A', `btoa(\"\u00FF\u00FF\u00C0\")`);\n assert(btoa('\\0a'), 'AGE=', `btoa(\"\\\\0a\")`);\n assert(btoa('a\\0b'), 'YQBi', `btoa(\"a\\\\0b\")`);\n assert(btoa(undefined), 'dW5kZWZpbmVk', `btoa(undefined)`);\n assert(btoa(null), 'bnVsbA==', `btoa(null)`);\n assert(btoa(7), 'Nw==', `btoa(7)`);\n assert(btoa(12), 'MTI=', `btoa(12)`);\n assert(btoa(1.5), 'MS41', `btoa(1.5)`);\n assert(btoa(true), 'dHJ1ZQ==', `btoa(true)`);\n assert(btoa(false), 'ZmFsc2U=', `btoa(false)`);\n assert(btoa(NaN), 'TmFO', `btoa(NaN)`);\n assert(btoa(Infinity), 'SW5maW5pdHk=', `btoa(Infinity)`);\n assert(btoa(-Infinity), 'LUluZmluaXR5', `btoa(-Infinity)`);\n assert(btoa(0), 'MA==', `btoa(0)`);\n assert(btoa(-0), 'MA==', `btoa(-0)`);\n assert(btoa('\\0'), 'AA==', `btoa(\"\\\\0\")`);\n assert(btoa('\\x01'), 'AQ==', `btoa(\"\\\\x01\")`);\n assert(btoa('\\x02'), 'Ag==', `btoa(\"\\\\x02\")`);\n assert(btoa('\\x03'), 'Aw==', `btoa(\"\\\\x03\")`);\n assert(btoa('\\x04'), 'BA==', `btoa(\"\\\\x04\")`);\n assert(btoa('\\x05'), 'BQ==', `btoa(\"\\\\x05\")`);\n assert(btoa('\\x06'), 'Bg==', `btoa(\"\\\\x06\")`);\n assert(btoa('\\x07'), 'Bw==', `btoa(\"\\\\x07\")`);\n assert(btoa('\\b'), 'CA==', `btoa(\"\\\\b\")`);\n assert(btoa('\\t'), 'CQ==', `btoa(\"\\\\t\")`);\n assert(btoa('\\n'), 'Cg==', `btoa(\"\\\\n\")`);\n assert(btoa('\\v'), 'Cw==', `btoa(\"\\\\v\")`);\n assert(btoa('\\f'), 'DA==', `btoa(\"\\\\f\")`);\n assert(btoa('\\r'), 'DQ==', `btoa(\"\\\\r\")`);\n assert(btoa('\\x0e'), 'Dg==', `btoa(\"\\\\x0e\")`);\n assert(btoa('\\x0f'), 'Dw==', `btoa(\"\\\\x0f\")`);\n assert(btoa('\\x10'), 'EA==', `btoa(\"\\\\x10\")`);\n assert(btoa('\\x11'), 'EQ==', `btoa(\"\\\\x11\")`);\n assert(btoa('\\x12'), 'Eg==', `btoa(\"\\\\x12\")`);\n assert(btoa('\\x13'), 'Ew==', `btoa(\"\\\\x13\")`);\n assert(btoa('\\x14'), 'FA==', `btoa(\"\\\\x14\")`);\n assert(btoa('\\x15'), 'FQ==', `btoa(\"\\\\x15\")`);\n assert(btoa('\\x16'), 'Fg==', `btoa(\"\\\\x16\")`);\n assert(btoa('\\x17'), 'Fw==', `btoa(\"\\\\x17\")`);\n assert(btoa('\\x18'), 'GA==', `btoa(\"\\\\x18\")`);\n assert(btoa('\\x19'), 'GQ==', `btoa(\"\\\\x19\")`);\n assert(btoa('\\x1a'), 'Gg==', `btoa(\"\\\\x1a\")`);\n assert(btoa('\\x1b'), 'Gw==', `btoa(\"\\\\x1b\")`);\n assert(btoa('\\x1c'), 'HA==', `btoa(\"\\\\x1c\")`);\n assert(btoa('\\x1d'), 'HQ==', `btoa(\"\\\\x1d\")`);\n assert(btoa('\\x1e'), 'Hg==', `btoa(\"\\\\x1e\")`);\n assert(btoa('\\x1f'), 'Hw==', `btoa(\"\\\\x1f\")`);\n assert(btoa(' '), 'IA==', `btoa(\" \")`);\n assert(btoa('!'), 'IQ==', `btoa(\"!\")`);\n assert(btoa('#'), 'Iw==', `btoa(\"#\")`);\n assert(btoa('$'), 'JA==', `btoa(\"$\")`);\n assert(btoa('%'), 'JQ==', `btoa(\"%\")`);\n assert(btoa('&'), 'Jg==', `btoa(\"&\")`);\n assert(btoa(\"'\"), 'Jw==', `btoa(\"'\")`);\n assert(btoa('('), 'KA==', `btoa(\"(\")`);\n assert(btoa(')'), 'KQ==', `btoa(\")\")`);\n assert(btoa('*'), 'Kg==', `btoa(\"*\")`);\n assert(btoa('+'), 'Kw==', `btoa(\"+\")`);\n assert(btoa(','), 'LA==', `btoa(\",\")`);\n assert(btoa('-'), 'LQ==', `btoa(\"-\")`);\n assert(btoa('.'), 'Lg==', `btoa(\".\")`);\n assert(btoa('/'), 'Lw==', `btoa(\"/\")`);\n assert(btoa('0'), 'MA==', `btoa(\"0\")`);\n assert(btoa('1'), 'MQ==', `btoa(\"1\")`);\n assert(btoa('2'), 'Mg==', `btoa(\"2\")`);\n assert(btoa('3'), 'Mw==', `btoa(\"3\")`);\n assert(btoa('4'), 'NA==', `btoa(\"4\")`);\n assert(btoa('5'), 'NQ==', `btoa(\"5\")`);\n assert(btoa('6'), 'Ng==', `btoa(\"6\")`);\n assert(btoa('7'), 'Nw==', `btoa(\"7\")`);\n assert(btoa('8'), 'OA==', `btoa(\"8\")`);\n assert(btoa('9'), 'OQ==', `btoa(\"9\")`);\n assert(btoa(':'), 'Og==', `btoa(\":\")`);\n assert(btoa(';'), 'Ow==', `btoa(\";\")`);\n assert(btoa('<'), 'PA==', `btoa(\"<\")`);\n assert(btoa('='), 'PQ==', `btoa(\"=\")`);\n assert(btoa('>'), 'Pg==', `btoa(\">\")`);\n assert(btoa('?'), 'Pw==', `btoa(\"?\")`);\n assert(btoa('@'), 'QA==', `btoa(\"@\")`);\n assert(btoa('A'), 'QQ==', `btoa(\"A\")`);\n assert(btoa('B'), 'Qg==', `btoa(\"B\")`);\n assert(btoa('C'), 'Qw==', `btoa(\"C\")`);\n assert(btoa('D'), 'RA==', `btoa(\"D\")`);\n assert(btoa('E'), 'RQ==', `btoa(\"E\")`);\n assert(btoa('F'), 'Rg==', `btoa(\"F\")`);\n assert(btoa('G'), 'Rw==', `btoa(\"G\")`);\n assert(btoa('H'), 'SA==', `btoa(\"H\")`);\n assert(btoa('I'), 'SQ==', `btoa(\"I\")`);\n assert(btoa('J'), 'Sg==', `btoa(\"J\")`);\n assert(btoa('K'), 'Sw==', `btoa(\"K\")`);\n assert(btoa('L'), 'TA==', `btoa(\"L\")`);\n assert(btoa('M'), 'TQ==', `btoa(\"M\")`);\n assert(btoa('N'), 'Tg==', `btoa(\"N\")`);\n assert(btoa('O'), 'Tw==', `btoa(\"O\")`);\n assert(btoa('P'), 'UA==', `btoa(\"P\")`);\n assert(btoa('Q'), 'UQ==', `btoa(\"Q\")`);\n assert(btoa('R'), 'Ug==', `btoa(\"R\")`);\n assert(btoa('S'), 'Uw==', `btoa(\"S\")`);\n assert(btoa('T'), 'VA==', `btoa(\"T\")`);\n assert(btoa('U'), 'VQ==', `btoa(\"U\")`);\n assert(btoa('V'), 'Vg==', `btoa(\"V\")`);\n assert(btoa('W'), 'Vw==', `btoa(\"W\")`);\n assert(btoa('X'), 'WA==', `btoa(\"X\")`);\n assert(btoa('Y'), 'WQ==', `btoa(\"Y\")`);\n assert(btoa('Z'), 'Wg==', `btoa(\"Z\")`);\n assert(btoa('['), 'Ww==', `btoa(\"[\")`);\n assert(btoa('\\\\'), 'XA==', `btoa(\"\\\\\\\\\")`);\n assert(btoa(']'), 'XQ==', `btoa(\"]\")`);\n assert(btoa('^'), 'Xg==', `btoa(\"^\")`);\n assert(btoa('_'), 'Xw==', `btoa(\"_\")`);\n assert(btoa('a'), 'YQ==', `btoa(\"a\")`);\n assert(btoa('b'), 'Yg==', `btoa(\"b\")`);\n assert(btoa('c'), 'Yw==', `btoa(\"c\")`);\n assert(btoa('d'), 'ZA==', `btoa(\"d\")`);\n assert(btoa('e'), 'ZQ==', `btoa(\"e\")`);\n assert(btoa('f'), 'Zg==', `btoa(\"f\")`);\n assert(btoa('g'), 'Zw==', `btoa(\"g\")`);\n assert(btoa('h'), 'aA==', `btoa(\"h\")`);\n assert(btoa('i'), 'aQ==', `btoa(\"i\")`);\n assert(btoa('j'), 'ag==', `btoa(\"j\")`);\n assert(btoa('k'), 'aw==', `btoa(\"k\")`);\n assert(btoa('l'), 'bA==', `btoa(\"l\")`);\n assert(btoa('m'), 'bQ==', `btoa(\"m\")`);\n assert(btoa('n'), 'bg==', `btoa(\"n\")`);\n assert(btoa('o'), 'bw==', `btoa(\"o\")`);\n assert(btoa('p'), 'cA==', `btoa(\"p\")`);\n assert(btoa('q'), 'cQ==', `btoa(\"q\")`);\n assert(btoa('r'), 'cg==', `btoa(\"r\")`);\n assert(btoa('s'), 'cw==', `btoa(\"s\")`);\n assert(btoa('t'), 'dA==', `btoa(\"t\")`);\n assert(btoa('u'), 'dQ==', `btoa(\"u\")`);\n assert(btoa('v'), 'dg==', `btoa(\"v\")`);\n assert(btoa('w'), 'dw==', `btoa(\"w\")`);\n assert(btoa('x'), 'eA==', `btoa(\"x\")`);\n assert(btoa('y'), 'eQ==', `btoa(\"y\")`);\n assert(btoa('z'), 'eg==', `btoa(\"z\")`);\n assert(btoa('{'), 'ew==', `btoa(\"{\")`);\n assert(btoa('|'), 'fA==', `btoa(\"|\")`);\n assert(btoa('}'), 'fQ==', `btoa(\"}\")`);\n assert(btoa('~'), 'fg==', `btoa(\"~\")`);\n assert(btoa('\u007F'), 'fw==', `btoa(\"\u007F\")`);\n assert(btoa('\u0080'), 'gA==', `btoa(\"\u0080\")`);\n assert(btoa('\u0081'), 'gQ==', `btoa(\"\u0081\")`);\n assert(btoa('\u0082'), 'gg==', `btoa(\"\u0082\")`);\n assert(btoa('\u0083'), 'gw==', `btoa(\"\u0083\")`);\n assert(btoa('\u0084'), 'hA==', `btoa(\"\u0084\")`);\n // eslint-disable-next-line no-irregular-whitespace\n assert(btoa('\u0085'), 'hQ==', `btoa(\"\u0085\")`);\n assert(btoa('\u0086'), 'hg==', `btoa(\"\u0086\")`);\n assert(btoa('\u0087'), 'hw==', `btoa(\"\u0087\")`);\n assert(btoa('\u0088'), 'iA==', `btoa(\"\u0088\")`);\n assert(btoa('\u0089'), 'iQ==', `btoa(\"\u0089\")`);\n assert(btoa('\u008A'), 'ig==', `btoa(\"\u008A\")`);\n assert(btoa('\u008B'), 'iw==', `btoa(\"\u008B\")`);\n assert(btoa('\u008C'), 'jA==', `btoa(\"\u008C\")`);\n assert(btoa('\u008D'), 'jQ==', `btoa(\"\u008D\")`);\n assert(btoa('\u008E'), 'jg==', `btoa(\"\u008E\")`);\n assert(btoa('\u008F'), 'jw==', `btoa(\"\u008F\")`);\n assert(btoa('\u0090'), 'kA==', `btoa(\"\u0090\")`);\n assert(btoa('\u0091'), 'kQ==', `btoa(\"\u0091\")`);\n assert(btoa('\u0092'), 'kg==', `btoa(\"\u0092\")`);\n assert(btoa('\u0093'), 'kw==', `btoa(\"\u0093\")`);\n assert(btoa('\u0094'), 'lA==', `btoa(\"\u0094\")`);\n assert(btoa('\u0095'), 'lQ==', `btoa(\"\u0095\")`);\n assert(btoa('\u0096'), 'lg==', `btoa(\"\u0096\")`);\n assert(btoa('\u0097'), 'lw==', `btoa(\"\u0097\")`);\n assert(btoa('\u0098'), 'mA==', `btoa(\"\u0098\")`);\n assert(btoa('\u0099'), 'mQ==', `btoa(\"\u0099\")`);\n assert(btoa('\u009A'), 'mg==', `btoa(\"\u009A\")`);\n assert(btoa('\u009B'), 'mw==', `btoa(\"\u009B\")`);\n assert(btoa('\u009C'), 'nA==', `btoa(\"\u009C\")`);\n assert(btoa('\u009D'), 'nQ==', `btoa(\"\u009D\")`);\n assert(btoa('\u009E'), 'ng==', `btoa(\"\u009E\")`);\n assert(btoa('\u009F'), 'nw==', `btoa(\"\u009F\")`);\n // eslint-disable-next-line no-irregular-whitespace\n assert(btoa('\u00A0'), 'oA==', `btoa(\"\u00A0\")`);\n assert(btoa('\u00A1'), 'oQ==', `btoa(\"\u00A1\")`);\n assert(btoa('\u00A2'), 'og==', `btoa(\"\u00A2\")`);\n assert(btoa('\u00A3'), 'ow==', `btoa(\"\u00A3\")`);\n assert(btoa('\u00A4'), 'pA==', `btoa(\"\u00A4\")`);\n assert(btoa('\u00A5'), 'pQ==', `btoa(\"\u00A5\")`);\n assert(btoa('\u00A6'), 'pg==', `btoa(\"\u00A6\")`);\n assert(btoa('\u00A7'), 'pw==', `btoa(\"\u00A7\")`);\n assert(btoa('\u00A8'), 'qA==', `btoa(\"\u00A8\")`);\n assert(btoa('\u00A9'), 'qQ==', `btoa(\"\u00A9\")`);\n assert(btoa('\u00AA'), 'qg==', `btoa(\"\u00AA\")`);\n assert(btoa('\u00AB'), 'qw==', `btoa(\"\u00AB\")`);\n assert(btoa('\u00AC'), 'rA==', `btoa(\"\u00AC\")`);\n assert(btoa('\u00AD'), 'rQ==', `btoa(\"\u00AD\")`);\n assert(btoa('\u00AE'), 'rg==', `btoa(\"\u00AE\")`);\n assert(btoa('\u00AF'), 'rw==', `btoa(\"\u00AF\")`);\n assert(btoa('\u00B0'), 'sA==', `btoa(\"\u00B0\")`);\n assert(btoa('\u00B1'), 'sQ==', `btoa(\"\u00B1\")`);\n assert(btoa('\u00B2'), 'sg==', `btoa(\"\u00B2\")`);\n assert(btoa('\u00B3'), 'sw==', `btoa(\"\u00B3\")`);\n assert(btoa('\u00B4'), 'tA==', `btoa(\"\u00B4\")`);\n assert(btoa('\u00B5'), 'tQ==', `btoa(\"\u00B5\")`);\n assert(btoa('\u00B6'), 'tg==', `btoa(\"\u00B6\")`);\n assert(btoa('\u00B7'), 'tw==', `btoa(\"\u00B7\")`);\n assert(btoa('\u00B8'), 'uA==', `btoa(\"\u00B8\")`);\n assert(btoa('\u00B9'), 'uQ==', `btoa(\"\u00B9\")`);\n assert(btoa('\u00BA'), 'ug==', `btoa(\"\u00BA\")`);\n assert(btoa('\u00BB'), 'uw==', `btoa(\"\u00BB\")`);\n assert(btoa('\u00BC'), 'vA==', `btoa(\"\u00BC\")`);\n assert(btoa('\u00BD'), 'vQ==', `btoa(\"\u00BD\")`);\n assert(btoa('\u00BE'), 'vg==', `btoa(\"\u00BE\")`);\n assert(btoa('\u00BF'), 'vw==', `btoa(\"\u00BF\")`);\n assert(btoa('\u00C0'), 'wA==', `btoa(\"\u00C0\")`);\n assert(btoa('\u00C1'), 'wQ==', `btoa(\"\u00C1\")`);\n assert(btoa('\u00C2'), 'wg==', `btoa(\"\u00C2\")`);\n assert(btoa('\u00C3'), 'ww==', `btoa(\"\u00C3\")`);\n assert(btoa('\u00C4'), 'xA==', `btoa(\"\u00C4\")`);\n assert(btoa('\u00C5'), 'xQ==', `btoa(\"\u00C5\")`);\n assert(btoa('\u00C6'), 'xg==', `btoa(\"\u00C6\")`);\n assert(btoa('\u00C7'), 'xw==', `btoa(\"\u00C7\")`);\n assert(btoa('\u00C8'), 'yA==', `btoa(\"\u00C8\")`);\n assert(btoa('\u00C9'), 'yQ==', `btoa(\"\u00C9\")`);\n assert(btoa('\u00CA'), 'yg==', `btoa(\"\u00CA\")`);\n assert(btoa('\u00CB'), 'yw==', `btoa(\"\u00CB\")`);\n assert(btoa('\u00CC'), 'zA==', `btoa(\"\u00CC\")`);\n assert(btoa('\u00CD'), 'zQ==', `btoa(\"\u00CD\")`);\n assert(btoa('\u00CE'), 'zg==', `btoa(\"\u00CE\")`);\n assert(btoa('\u00CF'), 'zw==', `btoa(\"\u00CF\")`);\n assert(btoa('\u00D0'), '0A==', `btoa(\"\u00D0\")`);\n assert(btoa('\u00D1'), '0Q==', `btoa(\"\u00D1\")`);\n assert(btoa('\u00D2'), '0g==', `btoa(\"\u00D2\")`);\n assert(btoa('\u00D3'), '0w==', `btoa(\"\u00D3\")`);\n assert(btoa('\u00D4'), '1A==', `btoa(\"\u00D4\")`);\n assert(btoa('\u00D5'), '1Q==', `btoa(\"\u00D5\")`);\n assert(btoa('\u00D6'), '1g==', `btoa(\"\u00D6\")`);\n assert(btoa('\u00D7'), '1w==', `btoa(\"\u00D7\")`);\n assert(btoa('\u00D8'), '2A==', `btoa(\"\u00D8\")`);\n assert(btoa('\u00D9'), '2Q==', `btoa(\"\u00D9\")`);\n assert(btoa('\u00DA'), '2g==', `btoa(\"\u00DA\")`);\n assert(btoa('\u00DB'), '2w==', `btoa(\"\u00DB\")`);\n assert(btoa('\u00DC'), '3A==', `btoa(\"\u00DC\")`);\n assert(btoa('\u00DD'), '3Q==', `btoa(\"\u00DD\")`);\n assert(btoa('\u00DE'), '3g==', `btoa(\"\u00DE\")`);\n assert(btoa('\u00DF'), '3w==', `btoa(\"\u00DF\")`);\n assert(btoa('\u00E0'), '4A==', `btoa(\"\u00E0\")`);\n assert(btoa('\u00E1'), '4Q==', `btoa(\"\u00E1\")`);\n assert(btoa('\u00E2'), '4g==', `btoa(\"\u00E2\")`);\n assert(btoa('\u00E3'), '4w==', `btoa(\"\u00E3\")`);\n assert(btoa('\u00E4'), '5A==', `btoa(\"\u00E4\")`);\n assert(btoa('\u00E5'), '5Q==', `btoa(\"\u00E5\")`);\n assert(btoa('\u00E6'), '5g==', `btoa(\"\u00E6\")`);\n assert(btoa('\u00E7'), '5w==', `btoa(\"\u00E7\")`);\n assert(btoa('\u00E8'), '6A==', `btoa(\"\u00E8\")`);\n assert(btoa('\u00E9'), '6Q==', `btoa(\"\u00E9\")`);\n assert(btoa('\u00EA'), '6g==', `btoa(\"\u00EA\")`);\n assert(btoa('\u00EB'), '6w==', `btoa(\"\u00EB\")`);\n assert(btoa('\u00EC'), '7A==', `btoa(\"\u00EC\")`);\n assert(btoa('\u00ED'), '7Q==', `btoa(\"\u00ED\")`);\n assert(btoa('\u00EE'), '7g==', `btoa(\"\u00EE\")`);\n assert(btoa('\u00EF'), '7w==', `btoa(\"\u00EF\")`);\n assert(btoa('\u00F0'), '8A==', `btoa(\"\u00F0\")`);\n assert(btoa('\u00F1'), '8Q==', `btoa(\"\u00F1\")`);\n assert(btoa('\u00F2'), '8g==', `btoa(\"\u00F2\")`);\n assert(btoa('\u00F3'), '8w==', `btoa(\"\u00F3\")`);\n assert(btoa('\u00F4'), '9A==', `btoa(\"\u00F4\")`);\n assert(btoa('\u00F5'), '9Q==', `btoa(\"\u00F5\")`);\n assert(btoa('\u00F6'), '9g==', `btoa(\"\u00F6\")`);\n assert(btoa('\u00F7'), '9w==', `btoa(\"\u00F7\")`);\n assert(btoa('\u00F8'), '+A==', `btoa(\"\u00F8\")`);\n assert(btoa('\u00F9'), '+Q==', `btoa(\"\u00F9\")`);\n assert(btoa('\u00FA'), '+g==', `btoa(\"\u00FA\")`);\n assert(btoa('\u00FB'), '+w==', `btoa(\"\u00FB\")`);\n assert(btoa('\u00FC'), '/A==', `btoa(\"\u00FC\")`);\n assert(btoa('\u00FD'), '/Q==', `btoa(\"\u00FD\")`);\n assert(btoa('\u00FE'), '/g==', `btoa(\"\u00FE\")`);\n assert(btoa('\u00FF'), '/w==', `btoa(\"\u00FF\")`);\n }\n\n // atob\n {\n assert(atob(''), '', `atob(\"\")`);\n assert(atob('abcd'), 'i\u00B7\\x1D');\n assert(atob(' abcd'), 'i\u00B7\\x1D');\n assert(atob('abcd '), 'i\u00B7\\x1D');\n assertThrows(() => atob(' abcd==='));\n assertThrows(() => atob('abcd=== '));\n assertThrows(() => atob('abcd ==='));\n assertThrows(() => atob('a'));\n assert(atob('ab'), 'i');\n assert(atob('abc'), 'i\u00B7');\n assertThrows(() => atob('abcde'));\n assertThrows(() => atob('\uD800\uDC00'));\n assertThrows(() => atob('='));\n assertThrows(() => atob('=='));\n assertThrows(() => atob('==='));\n assertThrows(() => atob('===='));\n assertThrows(() => atob('====='));\n assertThrows(() => atob('a='));\n assertThrows(() => atob('a=='));\n assertThrows(() => atob('a==='));\n assertThrows(() => atob('a===='));\n assertThrows(() => atob('a====='));\n assertThrows(() => atob('ab='));\n assert(atob('ab=='), 'i');\n assertThrows(() => atob('ab==='));\n assertThrows(() => atob('ab===='));\n assertThrows(() => atob('ab====='));\n assert(atob('abc='), 'i\u00B7');\n assertThrows(() => atob('abc=='));\n assertThrows(() => atob('abc==='));\n assertThrows(() => atob('abc===='));\n assertThrows(() => atob('abc====='));\n assertThrows(() => atob('abcd='));\n assertThrows(() => atob('abcd=='));\n assertThrows(() => atob('abcd==='));\n assertThrows(() => atob('abcd===='));\n assertThrows(() => atob('abcd====='));\n assertThrows(() => atob('abcde='));\n assertThrows(() => atob('abcde=='));\n assertThrows(() => atob('abcde==='));\n assertThrows(() => atob('abcde===='));\n assertThrows(() => atob('abcde====='));\n assertThrows(() => atob('=a'));\n assertThrows(() => atob('=a='));\n assertThrows(() => atob('a=b'));\n assertThrows(() => atob('a=b='));\n assertThrows(() => atob('ab=c'));\n assertThrows(() => atob('ab=c='));\n assertThrows(() => atob('abc=d'));\n assertThrows(() => atob('abc=d='));\n assertThrows(() => atob('ab\\u000Bcd'));\n assertThrows(() => atob('ab\\u3000cd'));\n assertThrows(() => atob('ab\\u3001cd'));\n assert(atob('ab\\tcd'), 'i\u00B7\\x1D');\n assert(atob('ab\\ncd'), 'i\u00B7\\x1D');\n assert(atob('ab\\fcd'), 'i\u00B7\\x1D');\n assert(atob('ab\\rcd'), 'i\u00B7\\x1D');\n assert(atob('ab cd'), 'i\u00B7\\x1D');\n assertThrows(() => atob('ab\\u00a0cd'));\n assert(atob('ab\\t\\n\\f\\r cd'), 'i\u00B7\\x1D');\n assert(atob(' \\t\\n\\f\\r ab\\t\\n\\f\\r cd\\t\\n\\f\\r '), 'i\u00B7\\x1D');\n assert(atob('ab\\t\\n\\f\\r =\\t\\n\\f\\r =\\t\\n\\f\\r '), 'i');\n assertThrows(() => atob('A'));\n assert(atob('/A'), '\u00FC');\n assert(atob('//A'), '\u00FF\u00F0');\n assert(atob('///A'), '\u00FF\u00FF\u00C0');\n assertThrows(() => atob('////A'));\n assertThrows(() => atob('/'));\n assert(atob('A/'), '\\x03');\n assert(atob('AA/'), '\\x00\\x0F');\n assertThrows(() => atob('AAAA/'));\n assert(atob('AAA/'), '\\x00\\x00?');\n assertThrows(() => atob('\\u0000nonsense'));\n assertThrows(() => atob('abcd\\u0000nonsense'));\n assert(atob('YQ'), 'a');\n assert(atob('YR'), 'a');\n assertThrows(() => atob('~~'));\n assertThrows(() => atob('..'));\n assertThrows(() => atob('--'));\n assertThrows(() => atob('__'));\n }\n});\n", "import { routes } from './routes';\nroutes.set('/byob', () => {\n // eslint-disable-next-line no-undef\n const stream = new ReadableStream({\n type: 'bytes',\n autoAllocateChunkSize: 1024,\n async pull(controller) {\n const view = controller.byobRequest.view;\n view[0] = 104;\n view[1] = 101;\n view[2] = 121;\n view[3] = 51;\n controller.byobRequest.respond(4);\n controller.close();\n },\n });\n return new Response(stream);\n});\n", "export const CacheOverride = globalThis.CacheOverride;", "/* eslint-env serviceworker */\n\nimport { CacheOverride } from 'fastly:cache-override';\nimport { routes } from './routes';\n\nroutes.set('/byte-repeater', () => {\n let headers = new Headers();\n headers.set('AssemblyScriptHeader', 'AssemblyScriptValue');\n\n let streamController;\n let stream = new ReadableStream({\n start: (controller) => {\n streamController = controller;\n },\n });\n let response = new Response(stream, {\n headers,\n });\n\n // Make a request upstream\n let upstreamRequest = new Request(\n 'https://compute-sdk-test-backend.edgecompute.app/byte_repeater',\n );\n upstreamRequest.setCacheOverride(new CacheOverride('pass'));\n fetch(upstreamRequest, {\n backend: 'TheOrigin',\n }).then(async (upstreamResponse) => {\n let body = upstreamResponse.body;\n let streamReader = body.getReader();\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n let chunk = await streamReader.read();\n\n // Check if we are done\n if (chunk.done) {\n break;\n }\n\n if (chunk.value.byteLength == 0) {\n continue;\n }\n\n // Otherwise get the byte and repeat them\n let upstreamBytes = chunk.value;\n let downstreamBytes = new Uint8Array(upstreamBytes.length * 2);\n for (let i = 0; i < upstreamBytes.length; i++) {\n let downstreamIndex = i * 2;\n downstreamBytes[downstreamIndex] = upstreamBytes[i];\n downstreamBytes[downstreamIndex + 1] = upstreamBytes[i];\n }\n\n streamController.enqueue(downstreamBytes);\n }\n streamController.close();\n });\n\n // Send downstream (back to the client)\n return response;\n});\n", "import { CacheOverride } from 'fastly:cache-override';\nimport { assert, assertThrows, assertDoesNotThrow } from './assertions.js';\nimport { isRunningLocally, routes } from './routes.js';\n\n// CacheOverride constructor\n{\n routes.set(\n '/cache-override/constructor/called-as-regular-function',\n async () => {\n assertThrows(\n () => {\n CacheOverride();\n },\n TypeError,\n `calling a builtin CacheOverride constructor without new is forbidden`,\n );\n },\n );\n routes.set('/cache-override/constructor/empty-parameter', async () => {\n assertThrows(\n () => {\n new CacheOverride();\n },\n TypeError,\n `CacheOverride constructor: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/cache-override/constructor/invalid-mode', async () => {\n // empty string not allowed\n assertThrows(\n () => {\n new CacheOverride('');\n },\n TypeError,\n `CacheOverride constructor: 'mode' has to be \"none\", \"pass\", or \"override\", but got \"\"`,\n );\n\n assertThrows(\n () => {\n new CacheOverride('be nice to the cache');\n },\n TypeError,\n `CacheOverride constructor: 'mode' has to be \"none\", \"pass\", or \"override\", but got \"be nice to the cache\"`,\n );\n });\n routes.set('/cache-override/constructor/valid-mode', async () => {\n assertDoesNotThrow(() => {\n new CacheOverride('none');\n });\n assertDoesNotThrow(() => {\n new CacheOverride('pass');\n });\n assertDoesNotThrow(() => {\n new CacheOverride('override', {});\n });\n assertDoesNotThrow(() => {\n new CacheOverride({});\n });\n });\n}\n// Using CacheOverride\n{\n routes.set('/cache-override/fetch/mode-none', async () => {\n if (isRunningLocally()) return;\n {\n const response = await fetch('https://http-me.glitch.me/now?status=200', {\n backend: 'httpme',\n cacheOverride: new CacheOverride('none'),\n });\n assert(\n response.headers.has('x-cache'),\n true,\n `CacheOveride('none'); response.headers.has('x-cache') === true`,\n );\n }\n\n {\n const response = await fetch('https://http-me.glitch.me/now?status=200', {\n backend: 'httpme',\n cacheOverride: 'none',\n });\n assert(\n response.headers.has('x-cache'),\n true,\n `CacheOveride('none'); response.headers.has('x-cache') === true`,\n );\n }\n });\n routes.set('/cache-override/fetch/mode-pass', async () => {\n if (isRunningLocally()) return;\n\n {\n const response = await fetch('https://http-me.glitch.me/now?status=200', {\n backend: 'httpme',\n cacheOverride: new CacheOverride('pass'),\n });\n assert(\n response.headers.has('x-cache'),\n false,\n `CacheOveride('pass'); response.headers.has('x-cache') === false`,\n );\n }\n\n {\n const response = await fetch('https://http-me.glitch.me/now?status=200', {\n backend: 'httpme',\n cacheOverride: 'pass',\n });\n assert(\n response.headers.has('x-cache'),\n false,\n `CacheOveride('pass'); response.headers.has('x-cache') === false`,\n );\n }\n });\n}\n", "\nexport const CacheEntry = globalThis.CacheEntry;\nexport const CacheState = globalThis.CacheState;\nexport const CoreCache = globalThis.CoreCache;\nexport const SimpleCache = globalThis.SimpleCache;\nexport const SimpleCacheEntry = globalThis.SimpleCacheEntry;\nexport const TransactionCacheEntry = globalThis.TransactionCacheEntry;\n", "export const FastlyBody = globalThis.FastlyBody;", "/// \n/* eslint-env serviceworker */\n\nimport {\n assert,\n assertDoesNotThrow,\n assertThrows,\n sleep,\n streamToString,\n assertResolves,\n} from './assertions.js';\nimport { routes } from './routes.js';\nimport {\n CoreCache,\n CacheEntry,\n CacheState,\n TransactionCacheEntry,\n} from 'fastly:cache';\nimport { FastlyBody } from 'fastly:body';\n\nfunction iteratableToStream(iterable) {\n return new ReadableStream({\n async pull(controller) {\n for await (const value of iterable) {\n controller.enqueue(value);\n }\n controller.close();\n },\n });\n}\n\nlet createdLion = false;\nfunction ensureLion() {\n if (createdLion) return;\n const writer = CoreCache.insert('lion', {\n maxAge: 600_000,\n });\n writer.append('raar');\n writer.close();\n createdLion = true;\n}\n\n// FastlyBody\n{\n routes.set('/FastlyBody/interface', () => {\n let actual = Reflect.ownKeys(FastlyBody);\n let expected = ['prototype', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(FastlyBody)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(FastlyBody, 'prototype');\n expected = {\n value: FastlyBody.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(FastlyBody, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(FastlyBody, 'name');\n expected = {\n value: 'FastlyBody',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(FastlyBody.prototype);\n expected = [\n 'constructor',\n 'concat',\n 'read',\n 'append',\n 'prepend',\n 'close',\n 'abandon',\n Symbol.toStringTag,\n ];\n assert(actual, expected, `Reflect.ownKeys(FastlyBody.prototype)`);\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: FastlyBody.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'constructor')`,\n );\n\n assert(\n typeof FastlyBody.prototype.constructor,\n 'function',\n `typeof FastlyBody.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'FastlyBody',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype,\n Symbol.toStringTag,\n );\n expected = {\n writable: false,\n enumerable: false,\n configurable: true,\n value: 'FastlyBody',\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof FastlyBody.prototype[Symbol.toStringTag],\n 'string',\n `typeof FastlyBody.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the concat method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'concat');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: FastlyBody.prototype.concat,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'concat')`,\n );\n\n assert(\n typeof FastlyBody.prototype.concat,\n 'function',\n `typeof FastlyBody.prototype.concat`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.concat,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.concat, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.concat,\n 'name',\n );\n expected = {\n value: 'concat',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.concat, 'name')`,\n );\n }\n\n // Check the read method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'read');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: FastlyBody.prototype.read,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'read')`,\n );\n\n assert(\n typeof FastlyBody.prototype.read,\n 'function',\n `typeof FastlyBody.prototype.read`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.read,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.read, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.read,\n 'name',\n );\n expected = {\n value: 'read',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.read, 'name')`,\n );\n }\n\n // Check the append method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'append');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: FastlyBody.prototype.append,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'append')`,\n );\n\n assert(\n typeof FastlyBody.prototype.append,\n 'function',\n `typeof FastlyBody.prototype.append`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.append,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.append, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.append,\n 'name',\n );\n expected = {\n value: 'append',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.append, 'name')`,\n );\n }\n\n // Check the prepend method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype,\n 'prepend',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: FastlyBody.prototype.prepend,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'prepend')`,\n );\n\n assert(\n typeof FastlyBody.prototype.prepend,\n 'function',\n `typeof FastlyBody.prototype.prepend`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.prepend,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.prepend, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.prepend,\n 'name',\n );\n expected = {\n value: 'prepend',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.prepend, 'name')`,\n );\n }\n\n // Check the close method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'close');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: FastlyBody.prototype.close,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype, 'close')`,\n );\n\n assert(\n typeof FastlyBody.prototype.close,\n 'function',\n `typeof FastlyBody.prototype.close`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.close,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.close, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n FastlyBody.prototype.close,\n 'name',\n );\n expected = {\n value: 'close',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(FastlyBody.prototype.close, 'name')`,\n );\n }\n });\n\n // constructor\n {\n routes.set('/FastlyBody/constructor/called-as-regular-function', () => {\n assertThrows(() => {\n FastlyBody();\n }, TypeError);\n });\n routes.set('/FastlyBody/constructor/called-as-constructor', () => {\n assertDoesNotThrow(() => new FastlyBody());\n });\n }\n // append(data: BodyInit): void;\n {\n routes.set('/FastlyBody/append/called-as-constructor', () => {\n assertThrows(() => {\n new FastlyBody.append('1');\n }, TypeError);\n });\n routes.set('/FastlyBody/append/data-parameter-not-supplied', () => {\n assertThrows(\n () => {\n const body = new FastlyBody();\n body.append();\n },\n TypeError,\n `append: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/FastlyBody/append/data-parameter-wrong-type', () => {\n assertThrows(() => {\n const body = new FastlyBody();\n body.append(Symbol());\n });\n });\n // - ReadableStream\n routes.set(\n '/FastlyBody/append/data-parameter-readablestream-guest-backed',\n () => {\n // TODO: update this when streams are supported\n assertThrows(\n () => {\n const stream = iteratableToStream([]);\n const body = new FastlyBody();\n body.append(stream);\n },\n TypeError,\n `Content-provided streams are not yet supported for appending onto a FastlyBody`,\n );\n },\n );\n routes.set(\n '/FastlyBody/append/data-parameter-readablestream-host-backed',\n async () => {\n const res = await fetch(\n 'https://compute-sdk-test-backend.edgecompute.app/',\n {\n backend: 'TheOrigin',\n },\n );\n const body = new FastlyBody();\n let result = body.append(res.body);\n assert(result, undefined, `body.append(res.body)`);\n },\n );\n // - URLSearchParams\n routes.set('/FastlyBody/append/data-parameter-URLSearchParams', () => {\n const items = [\n new URLSearchParams(),\n new URLSearchParams({ a: 'b', c: 'd' }),\n ];\n const body = new FastlyBody();\n for (const searchParams of items) {\n let result = body.append(searchParams);\n assert(result, undefined, `await body.append(searchParams)`);\n }\n });\n // - USV strings\n routes.set('/FastlyBody/append/data-parameter-strings', () => {\n const strings = [\n // empty\n '',\n // lone surrogate\n '\\uD800',\n // surrogate pair\n '\uD840\uDE13',\n String('carrot'),\n ];\n const body = new FastlyBody();\n for (const string of strings) {\n let result = body.append(string);\n assert(result, undefined, `body.append(string)`);\n }\n });\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/FastlyBody/append/data-parameter-calls-7.1.17-ToString',\n () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol();\n const value = {\n toString() {\n throw sentinel;\n },\n };\n const body = new FastlyBody();\n body.append(value);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n const body = new FastlyBody();\n body.append(Symbol());\n },\n TypeError,\n `can't convert symbol to string`,\n );\n },\n );\n\n // - buffer source\n routes.set('/FastlyBody/append/data-parameter-buffer', () => {\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n const body = new FastlyBody();\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n let result = body.append(typedArray.buffer);\n assert(result, undefined, `body.append(typedArray.buffer)`);\n }\n });\n routes.set('/FastlyBody/append/data-parameter-arraybuffer', () => {\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n const body = new FastlyBody();\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n let result = body.append(typedArray.buffer);\n assert(result, undefined, `body.append(typedArray.buffer)`);\n }\n });\n routes.set('/FastlyBody/append/data-parameter-typed-arrays', () => {\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n const body = new FastlyBody();\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n let result = body.append(typedArray);\n assert(result, undefined, `body.append(typedArray)`);\n }\n });\n routes.set('/FastlyBody/append/data-parameter-dataview', () => {\n const typedArrayConstructors = [\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n BigUint64Array,\n ];\n const body = new FastlyBody();\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n const view = new DataView(typedArray.buffer);\n let result = body.append(view);\n assert(result, undefined, `body.append(typedArray)`);\n }\n });\n }\n // prepend(data: BodyInit): void;\n {\n routes.set('/FastlyBody/prepend/called-as-constructor', () => {\n assertThrows(() => {\n new FastlyBody.prepend('1');\n }, TypeError);\n });\n routes.set('/FastlyBody/prepend/data-parameter-not-supplied', () => {\n assertThrows(\n () => {\n const body = new FastlyBody();\n body.prepend();\n },\n TypeError,\n `prepend: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/FastlyBody/prepend/data-parameter-wrong-type', () => {\n assertThrows(() => {\n const body = new FastlyBody();\n body.prepend(Symbol());\n });\n });\n // - ReadableStream\n routes.set(\n '/FastlyBody/prepend/data-parameter-readablestream-guest-backed',\n () => {\n // TODO: update this when streams are supported\n assertThrows(\n () => {\n const stream = iteratableToStream([]);\n const body = new FastlyBody();\n body.prepend(stream);\n },\n TypeError,\n `Content-provided streams are not yet supported for prepending onto a FastlyBody`,\n );\n },\n );\n routes.set(\n '/FastlyBody/prepend/data-parameter-readablestream-host-backed',\n async () => {\n const res = await fetch(\n 'https://compute-sdk-test-backend.edgecompute.app/',\n {\n backend: 'TheOrigin',\n },\n );\n const body = new FastlyBody();\n let result = body.prepend(res.body);\n assert(result, undefined, `body.prepend(res.body)`);\n },\n );\n routes.set(\n '/FastlyBody/prepend/data-parameter-readablestream-locked',\n () => {\n const stream = iteratableToStream([]);\n // getReader() causes the stream to become locked\n stream.getReader();\n const body = new FastlyBody();\n assertThrows(\n () => {\n body.prepend(stream);\n },\n TypeError,\n `Can't use a ReadableStream that's locked or has ever been read from or canceled`,\n );\n },\n );\n\n // - URLSearchParams\n routes.set('/FastlyBody/prepend/data-parameter-URLSearchParams', () => {\n const items = [\n new URLSearchParams(),\n new URLSearchParams({ a: 'b', c: 'd' }),\n ];\n const body = new FastlyBody();\n for (const searchParams of items) {\n let result = body.prepend(searchParams);\n assert(result, undefined, `await body.prepend(searchParams)`);\n }\n });\n // - USV strings\n routes.set('/FastlyBody/prepend/data-parameter-strings', () => {\n const strings = [\n // empty\n '',\n // lone surrogate\n '\\uD800',\n // surrogate pair\n '\uD840\uDE13',\n String('carrot'),\n ];\n const body = new FastlyBody();\n for (const string of strings) {\n let result = body.prepend(string);\n assert(result, undefined, `body.prepend(string)`);\n }\n });\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/FastlyBody/prepend/data-parameter-calls-7.1.17-ToString',\n () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol();\n const value = {\n toString() {\n throw sentinel;\n },\n };\n const body = new FastlyBody();\n body.prepend(value);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n const body = new FastlyBody();\n body.prepend(Symbol());\n },\n TypeError,\n `can't convert symbol to string`,\n );\n },\n );\n\n // - buffer source\n routes.set('/FastlyBody/prepend/data-parameter-buffer', () => {\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n const body = new FastlyBody();\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n let result = body.prepend(typedArray.buffer);\n assert(result, undefined, `body.prepend(typedArray.buffer)`);\n }\n });\n routes.set('/FastlyBody/prepend/data-parameter-arraybuffer', () => {\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n const body = new FastlyBody();\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n let result = body.prepend(typedArray.buffer);\n assert(result, undefined, `body.prepend(typedArray.buffer)`);\n }\n });\n routes.set('/FastlyBody/prepend/data-parameter-typed-arrays', () => {\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n const body = new FastlyBody();\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n let result = body.prepend(typedArray);\n assert(result, undefined, `body.prepend(typedArray)`);\n }\n });\n routes.set('/FastlyBody/prepend/data-parameter-dataview', () => {\n const typedArrayConstructors = [\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n BigUint64Array,\n ];\n const body = new FastlyBody();\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n const view = new DataView(typedArray.buffer);\n let result = body.prepend(view);\n assert(result, undefined, `body.prepend(typedArray)`);\n }\n });\n }\n // concat(dest: FastlyBody): void;\n {\n routes.set('/FastlyBody/concat/called-as-constructor', () => {\n assertThrows(() => {\n new FastlyBody.concat(new FastlyBody());\n }, TypeError);\n });\n routes.set('/FastlyBody/concat/dest-parameter-not-supplied', () => {\n assertThrows(\n () => {\n const body = new FastlyBody();\n body.concat();\n },\n TypeError,\n `concat: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/FastlyBody/concat/dest-parameter-wrong-type', () => {\n assertThrows(() => {\n const body = new FastlyBody();\n body.concat('hello');\n });\n });\n routes.set('/FastlyBody/concat/concat-same-fastlybody-twice', () => {\n assertThrows(() => {\n const body = new FastlyBody();\n const body2 = new FastlyBody();\n body.concat(body2);\n body.concat(body2);\n });\n });\n routes.set('/FastlyBody/concat/happy-path', () => {\n assertDoesNotThrow(() => {\n const body = new FastlyBody();\n body.concat(new FastlyBody());\n });\n });\n }\n // read(chunkSize: number): ArrayBuffer;\n {\n routes.set('/FastlyBody/read/called-as-constructor', () => {\n assertThrows(() => {\n new FastlyBody.read(1);\n }, TypeError);\n });\n routes.set('/FastlyBody/read/chunkSize-parameter-not-supplied', () => {\n assertThrows(\n () => {\n const body = new FastlyBody();\n body.read();\n },\n TypeError,\n `read: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/FastlyBody/read/chunkSize-parameter-wrong-type', () => {\n assertThrows(() => {\n const body = new FastlyBody();\n body.read(Symbol());\n });\n });\n // negative\n routes.set('/FastlyBody/read/chunkSize-parameter-negative', () => {\n assertThrows(() => {\n const body = new FastlyBody();\n body.append('hello world');\n body.read(-1);\n });\n });\n // infinity\n routes.set('/FastlyBody/read/chunkSize-parameter-infinity', () => {\n assertThrows(() => {\n const body = new FastlyBody();\n body.append('hello world');\n body.read(Infinity);\n });\n });\n // NaN\n routes.set('/FastlyBody/read/chunkSize-parameter-NaN', () => {\n assertThrows(() => {\n const body = new FastlyBody();\n body.append('hello world');\n body.read(NaN);\n });\n });\n routes.set('/FastlyBody/read/happy-path', () => {\n const body = new FastlyBody();\n body.append('world');\n body.prepend('hello ');\n const body2 = new FastlyBody();\n body2.append('!');\n body.concat(body2);\n const decoder = new TextDecoder();\n let result = decoder.decode(body.read(1));\n assert(result, 'h', `body.read(1)`);\n result = decoder.decode(body.read(1));\n assert(result, 'e', `body.read(1)`);\n });\n }\n // close(): void;\n {\n routes.set('/FastlyBody/close/called-as-constructor', () => {\n assertThrows(() => {\n new CoreCache.lookup('1');\n }, TypeError);\n });\n routes.set('/FastlyBody/close/called-once', () => {\n assertThrows(\n () => {\n CoreCache.lookup();\n },\n TypeError,\n `CoreCache.lookup: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/FastlyBody/close/called-twice', () => {\n assertThrows(() => {\n CoreCache.lookup('cat', { headers: '' });\n });\n });\n }\n}\n\n// CoreCache\n{\n routes.set('/core-cache/interface', () => {\n let actual = Reflect.ownKeys(CoreCache);\n let expected = [\n 'prototype',\n 'lookup',\n 'insert',\n 'transactionLookup',\n 'length',\n 'name',\n ];\n assert(actual, expected, `Reflect.ownKeys(CoreCache)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(CoreCache, 'prototype');\n expected = {\n value: CoreCache.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(CoreCache, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(CoreCache, 'name');\n expected = {\n value: 'CoreCache',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(CoreCache.prototype);\n expected = ['constructor', Symbol.toStringTag];\n assert(actual, expected, `Reflect.ownKeys(CoreCache.prototype)`);\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n CoreCache.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: CoreCache.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.prototype, 'constructor')`,\n );\n\n assert(\n typeof CoreCache.prototype.constructor,\n 'function',\n `typeof CoreCache.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CoreCache.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CoreCache.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'CoreCache',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n CoreCache.prototype,\n Symbol.toStringTag,\n );\n expected = {\n writable: false,\n enumerable: false,\n configurable: true,\n value: 'CoreCache',\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof CoreCache.prototype[Symbol.toStringTag],\n 'string',\n `typeof CoreCache.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the lookup static method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CoreCache, 'lookup');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CoreCache.lookup,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache, 'lookup')`,\n );\n\n assert(typeof CoreCache.lookup, 'function', `typeof CoreCache.lookup`);\n\n actual = Reflect.getOwnPropertyDescriptor(CoreCache.lookup, 'length');\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.lookup, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(CoreCache.lookup, 'name');\n expected = {\n value: 'lookup',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.lookup, 'name')`,\n );\n }\n\n // Check the insert static method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CoreCache, 'insert');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CoreCache.insert,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache, 'insert')`,\n );\n\n assert(typeof CoreCache.insert, 'function', `typeof CoreCache.insert`);\n\n actual = Reflect.getOwnPropertyDescriptor(CoreCache.insert, 'length');\n expected = {\n value: 2,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.insert, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(CoreCache.insert, 'name');\n expected = {\n value: 'insert',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.insert, 'name')`,\n );\n }\n\n // Check the transactionLookup static method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CoreCache, 'transactionLookup');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CoreCache.transactionLookup,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache, 'transactionLookup')`,\n );\n\n assert(\n typeof CoreCache.transactionLookup,\n 'function',\n `typeof CoreCache.transactionLookup`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CoreCache.transactionLookup,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.transactionLookup, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CoreCache.transactionLookup,\n 'name',\n );\n expected = {\n value: 'transactionLookup',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CoreCache.transactionLookup, 'name')`,\n );\n }\n });\n\n // CoreCache constructor\n {\n routes.set('/core-cache/constructor/called-as-regular-function', () => {\n assertThrows(() => {\n CoreCache();\n }, TypeError);\n });\n routes.set('/core-cache/constructor/throws', () => {\n assertThrows(() => new CoreCache(), TypeError);\n });\n }\n\n // CoreCache lookup static method\n // static lookup(key: string): CoreCacheEntry | null;\n {\n routes.set('/core-cache/lookup/called-as-constructor', () => {\n assertThrows(() => {\n new CoreCache.lookup('1');\n }, TypeError);\n });\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/core-cache/lookup/key-parameter-calls-7.1.17-ToString', () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const key = {\n toString() {\n throw sentinel;\n },\n };\n CoreCache.lookup(key);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n CoreCache.lookup(Symbol());\n },\n TypeError,\n `can't convert symbol to string`,\n );\n });\n routes.set('/core-cache/lookup/key-parameter-not-supplied', () => {\n assertThrows(\n () => {\n CoreCache.lookup();\n },\n TypeError,\n `CoreCache.lookup: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/core-cache/lookup/key-parameter-empty-string', () => {\n assertThrows(\n () => {\n CoreCache.lookup('');\n },\n Error,\n `CoreCache.lookup: key can not be an empty string`,\n );\n });\n routes.set('/core-cache/lookup/key-parameter-8135-character-string', () => {\n assertDoesNotThrow(() => {\n const key = 'a'.repeat(8135);\n CoreCache.lookup(key);\n });\n });\n routes.set('/core-cache/lookup/key-parameter-8136-character-string', () => {\n assertThrows(\n () => {\n const key = 'a'.repeat(8136);\n CoreCache.lookup(key);\n },\n Error,\n `CoreCache.lookup: key is too long, the maximum allowed length is 8135.`,\n );\n });\n routes.set('/core-cache/lookup/key-does-not-exist-returns-null', () => {\n let result = CoreCache.lookup(Math.random());\n assert(result, null, `CoreCache.lookup(Math.random()) === null`);\n });\n routes.set('/core-cache/lookup/key-exists', () => {\n let key = 'c'.repeat(8135);\n let writer = CoreCache.insert(key, {\n maxAge: 1000,\n });\n writer.append('meow');\n writer.close();\n let result = CoreCache.lookup(key);\n assert(\n result instanceof CacheEntry,\n true,\n `CoreCache.lookup('cat') instanceof CacheEntry`,\n );\n });\n\n routes.set('/core-cache/lookup/options-parameter-none', () => {\n ensureLion();\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.lookup('lion');\n });\n assert(\n entry instanceof CacheEntry,\n true,\n `CoreCache.lookup('lion', {headers:...}) instanceof CacheEntry`,\n );\n });\n routes.set('/core-cache/lookup/options-parameter-wrong-type', () => {\n ensureLion();\n assertThrows(() => {\n CoreCache.lookup('lion', '');\n });\n });\n routes.set(\n '/core-cache/lookup/options-parameter-headers-field-wrong-type',\n () => {\n ensureLion();\n assertThrows(() => {\n CoreCache.lookup('lion', { headers: '' });\n });\n },\n );\n routes.set(\n '/core-cache/lookup/options-parameter-headers-field-undefined',\n () => {\n ensureLion();\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.lookup('lion', { headers: undefined });\n });\n assert(\n entry instanceof CacheEntry,\n true,\n `CoreCache.lookup('lion', {headers:...}) instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/core-cache/lookup/options-parameter-headers-field-valid-sequence',\n () => {\n ensureLion();\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.lookup('lion', {\n headers: [\n ['user-agent', 'Aki 1.0'],\n ['Accept-Encoding', 'br'],\n ],\n });\n });\n assert(\n entry instanceof CacheEntry,\n true,\n `CoreCache.lookup('lion', {headers:...}) instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/core-cache/lookup/options-parameter-headers-field-valid-record',\n () => {\n ensureLion();\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.lookup('lion', {\n headers: {\n 'user-agent': 'Aki 1.0',\n 'Accept-Encoding': 'br',\n },\n });\n });\n assert(\n entry instanceof CacheEntry,\n true,\n `CoreCache.lookup('lion', {headers:...}) instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/core-cache/lookup/options-parameter-headers-field-valid-Headers-instance',\n () => {\n ensureLion();\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.lookup('lion', {\n headers: new Headers({\n 'user-agent': 'Aki 1.0',\n 'Accept-Encoding': 'br',\n }),\n });\n });\n assert(\n entry instanceof CacheEntry,\n true,\n `CoreCache.lookup('lion', {headers:...}) instanceof CacheEntry`,\n );\n },\n );\n }\n\n // static insert(key: string, options: InsertOptions): FastlyBody;\n {\n routes.set('/core-cache/insert/called-as-constructor', () => {\n assertThrows(() => {\n new CoreCache.insert('1', { maxAge: 1 });\n }, TypeError);\n });\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/core-cache/insert/key-parameter-calls-7.1.17-ToString', () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const key = {\n toString() {\n throw sentinel;\n },\n };\n CoreCache.insert(key, { maxAge: 1 });\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n CoreCache.insert(Symbol(), { maxAge: 1 });\n },\n TypeError,\n `can't convert symbol to string`,\n );\n });\n routes.set('/core-cache/insert/key-parameter-not-supplied', () => {\n assertThrows(\n () => {\n CoreCache.insert();\n },\n TypeError,\n `CoreCache.insert: At least 2 arguments required, but only 0 passed`,\n );\n });\n routes.set('/core-cache/insert/key-parameter-empty-string', () => {\n assertThrows(\n () => {\n CoreCache.insert('', { maxAge: 1 });\n },\n Error,\n `CoreCache.insert: key can not be an empty string`,\n );\n });\n routes.set('/core-cache/insert/key-parameter-8135-character-string', () => {\n assertDoesNotThrow(() => {\n const key = 'a'.repeat(8135);\n CoreCache.insert(key, { maxAge: 1 });\n });\n });\n routes.set('/core-cache/insert/key-parameter-8136-character-string', () => {\n assertThrows(\n () => {\n const key = 'a'.repeat(8136);\n CoreCache.insert(key, { maxAge: 1 });\n },\n Error,\n `CoreCache.insert: key is too long, the maximum allowed length is 8135.`,\n );\n });\n routes.set('/core-cache/insert/options-parameter-wrong-type', () => {\n assertThrows(() => {\n CoreCache.insert('cat', '');\n });\n });\n routes.set(\n '/core-cache/insert/options-parameter-headers-field-wrong-type',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', { headers: '', maxAge: 1 });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-headers-field-undefined',\n () => {\n let body;\n assertDoesNotThrow(() => {\n body = CoreCache.insert('cat', { headers: undefined, maxAge: 1 });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `CoreCache.insert('cat', {headers:undefined}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-headers-field-valid-sequence',\n () => {\n let body;\n assertDoesNotThrow(() => {\n body = CoreCache.insert('cat', {\n headers: [\n ['user-agent', 'Aki 1.0'],\n ['Accept-Encoding', 'br'],\n ],\n maxAge: 1,\n });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `CoreCache.insert('cat', {headers:undefined}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-headers-field-valid-record',\n () => {\n let body;\n assertDoesNotThrow(() => {\n body = CoreCache.insert('cat', {\n headers: {\n 'user-agent': 'Aki 1.0',\n 'Accept-Encoding': 'br',\n },\n maxAge: 1,\n });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `CoreCache.insert('cat', {headers:undefined}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-headers-field-valid-Headers-instance',\n () => {\n let body;\n assertDoesNotThrow(() => {\n body = CoreCache.insert('cat', {\n headers: new Headers({\n 'user-agent': 'Aki 1.0',\n 'Accept-Encoding': 'br',\n }),\n maxAge: 1,\n });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `CoreCache.insert('cat', {headers:undefined}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-maxAge-field-valid-record',\n () => {\n let body;\n assertDoesNotThrow(() => {\n body = CoreCache.insert('cat', { maxAge: 1 });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `CoreCache.insert('cat', {maxAge: 1}) instanceof FastlyBody`,\n );\n },\n );\n routes.set('/core-cache/insert/options-parameter-maxAge-field-NaN', () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: NaN,\n });\n });\n });\n routes.set(\n '/core-cache/insert/options-parameter-maxAge-field-postitive-infinity',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-maxAge-field-negative-infinity',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-maxAge-field-negative-number',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: -1,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-initialAge-field-valid-record',\n () => {\n let body;\n assertDoesNotThrow(() => {\n body = CoreCache.insert('cat', { maxAge: 1, initialAge: 1 });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `CoreCache.insert('cat', {maxAge: 1,initialAge: 1}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-initialAge-field-NaN',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n initialAge: NaN,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-initialAge-field-postitive-infinity',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n initialAge: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-initialAge-field-negative-infinity',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n initialAge: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-initialAge-field-negative-number',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n initialAge: -1,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-staleWhileRevalidate-field-valid-record',\n () => {\n let body;\n assertDoesNotThrow(() => {\n body = CoreCache.insert('cat', {\n maxAge: 1,\n staleWhileRevalidate: 1,\n });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `CoreCache.insert('cat', {maxAge: 1,staleWhileRevalidate: 1}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-staleWhileRevalidate-field-NaN',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n staleWhileRevalidate: NaN,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-staleWhileRevalidate-field-postitive-infinity',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n staleWhileRevalidate: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-staleWhileRevalidate-field-negative-infinity',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n staleWhileRevalidate: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-staleWhileRevalidate-field-negative-number',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n staleWhileRevalidate: -1,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-length-field-valid-record',\n () => {\n let body;\n assertDoesNotThrow(() => {\n body = CoreCache.insert('cat', { maxAge: 1, length: 1 });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `CoreCache.insert('cat', {maxAge: 1,length: 1}) instanceof FastlyBody`,\n );\n },\n );\n routes.set('/core-cache/insert/options-parameter-length-field-NaN', () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n length: NaN,\n });\n });\n });\n routes.set(\n '/core-cache/insert/options-parameter-length-field-postitive-infinity',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n length: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-length-field-negative-infinity',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n length: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-length-field-negative-number',\n () => {\n assertThrows(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n length: -1,\n });\n });\n },\n );\n routes.set('/core-cache/insert/options-parameter-sensitive-field', () => {\n assertDoesNotThrow(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n sensitive: true,\n });\n });\n });\n routes.set('/core-cache/insert/options-parameter-vary-field', () => {\n assertDoesNotThrow(() => {\n CoreCache.insert('cat', {\n maxAge: 1,\n vary: ['animal', 'mineral', 'vegetable'],\n });\n });\n });\n routes.set(\n '/core-cache/insert/options-parameter-userMetadata-field/arraybuffer/empty',\n async () => {\n await assertResolves(async () => {\n let key =\n '/core-cache/insert/options-parameter-userMetadata-field/arraybuffer/empty' +\n Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n userMetadata: new ArrayBuffer(0),\n });\n writer.append('hello');\n writer.close();\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-userMetadata-field/arraybuffer/not-empty',\n async () => {\n await assertResolves(async () => {\n let key =\n '/core-cache/insert/options-parameter-userMetadata-field/arraybuffer/not-empty' +\n Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n userMetadata: Uint8Array.from([104, 101, 108, 108, 111]).buffer,\n });\n writer.append('hello');\n writer.close();\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-userMetadata-field/URLSearchParams',\n async () => {\n await assertResolves(async () => {\n let key =\n '/core-cache/insert/options-parameter-userMetadata-field/URLSearchParams' +\n Math.random();\n let userMetadata = new URLSearchParams();\n userMetadata.set('hello', 'world');\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n userMetadata,\n });\n writer.append('hello');\n writer.close();\n });\n },\n );\n routes.set(\n '/core-cache/insert/options-parameter-userMetadata-field/string',\n async () => {\n await assertResolves(async () => {\n let key =\n '/core-cache/insert/options-parameter-userMetadata-field/string' +\n Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n userMetadata: 'hello',\n });\n writer.append('hello');\n writer.close();\n });\n },\n );\n // surrogateKeys?: Array,-- empty string? -- toString which throws -- wrong types?\n }\n\n //static transactionLookup(key: string, options?: LookupOptions): CacheEntry | null;\n {\n routes.set('/core-cache/transactionLookup/called-as-constructor', () => {\n assertThrows(() => {\n new CoreCache.transactionLookup('1');\n }, TypeError);\n });\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/core-cache/transactionLookup/key-parameter-calls-7.1.17-ToString',\n () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const key = {\n toString() {\n throw sentinel;\n },\n };\n CoreCache.transactionLookup(key);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n CoreCache.transactionLookup(Symbol());\n },\n TypeError,\n `can't convert symbol to string`,\n );\n },\n );\n routes.set(\n '/core-cache/transactionLookup/key-parameter-not-supplied',\n () => {\n assertThrows(\n () => {\n CoreCache.transactionLookup();\n },\n TypeError,\n `CoreCache.transactionLookup: At least 1 argument required, but only 0 passed`,\n );\n },\n );\n routes.set(\n '/core-cache/transactionLookup/key-parameter-empty-string',\n () => {\n assertThrows(\n () => {\n CoreCache.transactionLookup('');\n },\n Error,\n `CoreCache.transactionLookup: key can not be an empty string`,\n );\n },\n );\n routes.set(\n '/core-cache/transactionLookup/key-parameter-8135-character-string',\n () => {\n assertDoesNotThrow(() => {\n const key = 'a'.repeat(8135);\n CoreCache.transactionLookup(key);\n });\n },\n );\n routes.set(\n '/core-cache/transactionLookup/key-parameter-8136-character-string',\n () => {\n assertThrows(\n () => {\n const key = 'a'.repeat(8136);\n CoreCache.transactionLookup(key);\n },\n Error,\n `CoreCache.transactionLookup: key is too long, the maximum allowed length is 8135.`,\n );\n },\n );\n routes.set('/core-cache/transactionLookup/key-does-not-exist', () => {\n let result = CoreCache.transactionLookup(Math.random());\n assert(\n result instanceof CacheEntry,\n true,\n `CoreCache.transactionLookup(Math.random()) instanceof CacheEntry`,\n );\n });\n routes.set('/core-cache/transactionLookup/key-exists', () => {\n let writer = CoreCache.insert('cat', {\n maxAge: 10_000,\n });\n writer.append('meow');\n writer.close();\n let result = CoreCache.transactionLookup('cat');\n assert(\n result instanceof CacheEntry,\n true,\n `CoreCache.transactionLookup('cat') instanceof CacheEntry`,\n );\n });\n\n routes.set(\n '/core-cache/transactionLookup/options-parameter-wrong-type',\n () => {\n assertThrows(() => {\n CoreCache.transactionLookup('cat', '');\n });\n },\n );\n routes.set(\n '/core-cache/transactionLookup/options-parameter-headers-field-wrong-type',\n () => {\n assertThrows(() => {\n CoreCache.transactionLookup('cat', { headers: '' });\n });\n },\n );\n routes.set(\n '/core-cache/transactionLookup/options-parameter-headers-field-undefined',\n () => {\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.transactionLookup('cat', { headers: undefined });\n });\n assert(\n entry instanceof CacheEntry,\n true,\n `CoreCache.transactionLookup('cat', {headers:...}) instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/core-cache/transactionLookup/options-parameter-headers-field-valid-sequence',\n () => {\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.transactionLookup('cat', {\n headers: [\n ['user-agent', 'Aki 1.0'],\n ['Accept-Encoding', 'br'],\n ],\n });\n });\n assert(\n entry instanceof CacheEntry,\n true,\n `CoreCache.transactionLookup('cat', {headers:...}) instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/core-cache/transactionLookup/options-parameter-headers-field-valid-record',\n () => {\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.transactionLookup('cat', {\n headers: {\n 'user-agent': 'Aki 1.0',\n 'Accept-Encoding': 'br',\n },\n });\n });\n assert(\n entry instanceof CacheEntry,\n true,\n `CoreCache.transactionLookup('cat', {headers:...}) instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/core-cache/transactionLookup/options-parameter-headers-field-valid-Headers-instance',\n () => {\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.transactionLookup('cat', {\n headers: new Headers({\n 'user-agent': 'Aki 1.0',\n 'Accept-Encoding': 'br',\n }),\n });\n });\n assert(\n entry instanceof CacheEntry,\n true,\n `CoreCache.transactionLookup('cat', {headers:...}) instanceof CacheEntry`,\n );\n },\n );\n }\n}\n\n// CacheEntry\n{\n routes.set('/cache-entry/interface', () => {\n let actual = Reflect.ownKeys(CacheEntry);\n let expected = ['prototype', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(CacheEntry)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry, 'prototype');\n expected = {\n value: CacheEntry.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry, 'name');\n expected = {\n value: 'CacheEntry',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(CacheEntry.prototype);\n expected = [\n 'constructor',\n 'close',\n 'state',\n 'userMetadata',\n 'body',\n 'length',\n 'maxAge',\n 'staleWhileRevalidate',\n 'age',\n 'hits',\n Symbol.toStringTag,\n ];\n assert(actual, expected, `Reflect.ownKeys(CacheEntry.prototype)`);\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: CacheEntry.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'constructor')`,\n );\n\n assert(\n typeof CacheEntry.prototype.constructor,\n 'function',\n `typeof CacheEntry.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'CacheEntry',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype,\n Symbol.toStringTag,\n );\n expected = {\n writable: false,\n enumerable: false,\n configurable: true,\n value: 'CacheEntry',\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof CacheEntry.prototype[Symbol.toStringTag],\n 'string',\n `typeof CacheEntry.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the close method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'close');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CacheEntry.prototype.close,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'close')`,\n );\n\n assert(\n typeof CacheEntry.prototype.close,\n 'function',\n `typeof CacheEntry.prototype.close`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.close,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.close, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.close,\n 'name',\n );\n expected = {\n value: 'close',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.close, 'name')`,\n );\n }\n\n // Check the state method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'state');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CacheEntry.prototype.state,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'state')`,\n );\n\n assert(\n typeof CacheEntry.prototype.state,\n 'function',\n `typeof CacheEntry.prototype.state`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.state,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.state, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.state,\n 'name',\n );\n expected = {\n value: 'state',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.state, 'name')`,\n );\n }\n\n // Check the userMetadata method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype,\n 'userMetadata',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CacheEntry.prototype.userMetadata,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'userMetadata')`,\n );\n\n assert(\n typeof CacheEntry.prototype.userMetadata,\n 'function',\n `typeof CacheEntry.prototype.userMetadata`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.userMetadata,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.userMetadata, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.userMetadata,\n 'name',\n );\n expected = {\n value: 'userMetadata',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.userMetadata, 'name')`,\n );\n }\n\n // Check the body method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'body');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CacheEntry.prototype.body,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'body')`,\n );\n\n assert(\n typeof CacheEntry.prototype.body,\n 'function',\n `typeof CacheEntry.prototype.body`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.body,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.body, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.body,\n 'name',\n );\n expected = {\n value: 'body',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.body, 'name')`,\n );\n }\n\n // Check the length method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'length');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CacheEntry.prototype.length,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'length')`,\n );\n\n assert(\n typeof CacheEntry.prototype.length,\n 'function',\n `typeof CacheEntry.prototype.length`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.length,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.length, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.length,\n 'name',\n );\n expected = {\n value: 'length',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.length, 'name')`,\n );\n }\n\n // Check the maxAge method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'maxAge');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CacheEntry.prototype.maxAge,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'maxAge')`,\n );\n\n assert(\n typeof CacheEntry.prototype.maxAge,\n 'function',\n `typeof CacheEntry.prototype.maxAge`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.maxAge,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.maxAge, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.maxAge,\n 'name',\n );\n expected = {\n value: 'maxAge',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.maxAge, 'name')`,\n );\n }\n\n // Check the staleWhileRevalidate method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype,\n 'staleWhileRevalidate',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CacheEntry.prototype.staleWhileRevalidate,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'staleWhileRevalidate')`,\n );\n\n assert(\n typeof CacheEntry.prototype.staleWhileRevalidate,\n 'function',\n `typeof CacheEntry.prototype.staleWhileRevalidate`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.staleWhileRevalidate,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.staleWhileRevalidate, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.staleWhileRevalidate,\n 'name',\n );\n expected = {\n value: 'staleWhileRevalidate',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.staleWhileRevalidate, 'name')`,\n );\n }\n\n // Check the age method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'age');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CacheEntry.prototype.age,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'age')`,\n );\n\n assert(\n typeof CacheEntry.prototype.age,\n 'function',\n `typeof CacheEntry.prototype.age`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.age,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.age, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.age,\n 'name',\n );\n expected = {\n value: 'age',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.age, 'name')`,\n );\n }\n\n // Check the hits method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'hits');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: CacheEntry.prototype.hits,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype, 'hits')`,\n );\n\n assert(\n typeof CacheEntry.prototype.hits,\n 'function',\n `typeof CacheEntry.prototype.hits`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.hits,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.hits, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n CacheEntry.prototype.hits,\n 'name',\n );\n expected = {\n value: 'hits',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(CacheEntry.prototype.hits, 'name')`,\n );\n }\n });\n\n // CacheEntry constructor\n {\n routes.set('/cache-entry/constructor/called-as-regular-function', () => {\n assertThrows(() => {\n CacheEntry();\n }, TypeError);\n });\n routes.set('/cache-entry/constructor/throws', () => {\n assertThrows(() => new CacheEntry(), TypeError);\n });\n }\n\n // close(): void;\n {\n routes.set('/cache-entry/close/called-as-constructor', () => {\n assertThrows(() => {\n new CacheEntry.prototype.close();\n }, TypeError);\n });\n routes.set('/cache-entry/close/called-unbound', () => {\n assertThrows(() => {\n CacheEntry.prototype.close.call(undefined);\n }, Error);\n });\n routes.set('/cache-entry/close/called-on-instance', () => {\n let key = '/cache-entry/close/called-on-instance' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).close();\n assert(result, undefined, `CoreCache.lookup(key).close()`);\n });\n }\n\n // state(): CacheState;\n {\n routes.set('/cache-entry/state/called-as-constructor', () => {\n assertThrows(() => {\n new CacheEntry.prototype.state();\n }, TypeError);\n });\n routes.set('/cache-entry/state/called-unbound', () => {\n assertThrows(() => {\n CacheEntry.prototype.state.call(undefined);\n }, Error);\n });\n\n routes.set('/cache-entry/state/called-on-instance', () => {\n let key = '/cache-entry/state/called-on-instance' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let entry = CoreCache.lookup(key);\n let result = entry.state();\n assert(\n result instanceof CacheState,\n true,\n `CoreCache.lookup(key).state() instanceof CacheState`,\n );\n });\n }\n\n // userMetadata(): ArrayBuffer;\n {\n routes.set('/cache-entry/userMetadata/called-as-constructor', () => {\n assertThrows(() => {\n new CacheEntry.prototype.userMetadata();\n }, TypeError);\n });\n routes.set('/cache-entry/userMetadata/called-unbound', () => {\n assertThrows(() => {\n CacheEntry.prototype.userMetadata.call(undefined);\n }, Error);\n });\n routes.set('/cache-entry/userMetadata/called-on-instance', () => {\n let key = '/cache-entry/userMetadata/called-on-instance' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).userMetadata();\n assert(\n result instanceof ArrayBuffer,\n true,\n `CoreCache.lookup(key).userMetadata() instanceof ArrayBuffer`,\n );\n assert(\n result.byteLength,\n 0,\n `CoreCache.lookup(key).userMetadata().byteLength`,\n );\n });\n routes.set('/cache-entry/userMetadata/basic', () => {\n let key = '/cache-entry/userMetadata/basic' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n userMetadata: 'hello world',\n });\n writer.append('hello');\n writer.close();\n let entry = CoreCache.lookup(key);\n assert(\n entry instanceof CacheEntry,\n true,\n 'CoreCache.lookup(key) instanceof CacheEntry',\n );\n let metadata = entry.userMetadata();\n assert(\n metadata instanceof ArrayBuffer,\n true,\n `CoreCache.lookup(key).userMetadata() instanceof ArrayBuffer`,\n );\n assert(\n metadata.byteLength,\n 11,\n `CoreCache.lookup(key).userMetadata().byteLength`,\n );\n let result = new TextDecoder().decode(metadata);\n assert(\n result,\n 'hello world',\n `new TextDecoder().decode(CoreCache.lookup(key).userMetadata()) === 'hello world'`,\n );\n });\n }\n\n // body(options?: CacheBodyOptions): ReadableStream;\n {\n routes.set('/cache-entry/body/called-as-constructor', () => {\n assertThrows(() => {\n new CacheEntry.prototype.body();\n }, TypeError);\n });\n routes.set('/cache-entry/body/called-unbound', () => {\n assertThrows(() => {\n CacheEntry.prototype.body.call(undefined);\n }, Error);\n });\n routes.set('/cache-entry/body/called-on-instance', async () => {\n let key = '/cache-entry/body/called-on-instance' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).body();\n assert(\n result instanceof ReadableStream,\n true,\n `CoreCache.lookup(key).body() instanceof ReadableStream`,\n );\n\n result = await streamToString(result);\n assert(\n result,\n 'hello',\n `await streamToString(CoreCache.lookup(key).body())`,\n );\n });\n routes.set('/cache-entry/body/options-start-negative', async () => {\n let key = '/cache-entry/body/options-start-negative' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n assertThrows(() => {\n CoreCache.lookup(key).body({ start: -1 });\n });\n });\n routes.set('/cache-entry/body/options-start-NaN', async () => {\n let key = '/cache-entry/body/options-start-NaN' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n assertThrows(() => {\n CoreCache.lookup(key).body({ start: NaN });\n });\n });\n routes.set('/cache-entry/body/options-start-Infinity', async () => {\n let key = '/cache-entry/body/options-start-Infinity' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n assertThrows(() => {\n CoreCache.lookup(key).body({ start: Infinity });\n });\n });\n routes.set('/cache-entry/body/options-start-valid', async () => {\n let key = '/cache-entry/body/options-start-valid' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).body({ start: 1 });\n assert(\n result instanceof ReadableStream,\n true,\n `CoreCache.lookup(key).body() instanceof ReadableStream`,\n );\n\n result = await streamToString(result);\n assert(\n result,\n 'ello',\n `await streamToString(CoreCache.lookup(key).body())`,\n );\n });\n routes.set('/cache-entry/body/options-start-longer-than-body', async () => {\n let key =\n '/cache-entry/body/options-start-longer-than-body' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).body({ start: 1000 });\n assert(\n result instanceof ReadableStream,\n true,\n `CoreCache.lookup(key).body() instanceof ReadableStream`,\n );\n\n result = await streamToString(result);\n assert(\n result,\n 'hello',\n `await streamToString(CoreCache.lookup(key).body())`,\n );\n });\n routes.set('/cache-entry/body/options-end-negative', async () => {\n let key = '/cache-entry/body/options-end-negative' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n assertThrows(() => {\n CoreCache.lookup(key).body({ end: -1 });\n });\n });\n routes.set('/cache-entry/body/options-end-NaN', async () => {\n let key = '/cache-entry/body/options-end-NaN' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n assertThrows(() => {\n CoreCache.lookup(key).body({ end: NaN });\n });\n });\n routes.set('/cache-entry/body/options-end-Infinity', async () => {\n let key = '/cache-entry/body/options-end-Infinity' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n assertThrows(() => {\n CoreCache.lookup(key).body({ end: Infinity });\n });\n });\n routes.set('/cache-entry/body/options-end-valid', async () => {\n let key = '/cache-entry/body/options-end-valid' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).body({ start: 1, end: 1 });\n assert(\n result instanceof ReadableStream,\n true,\n `CoreCache.lookup(key).body() instanceof ReadableStream`,\n );\n\n result = await streamToString(result);\n console.log({ result });\n assert(result, 'e', `await streamToString(CoreCache.lookup(key).body())`);\n });\n routes.set('/cache-entry/body/options-end-zero', async () => {\n let key = '/cache-entry/body/options-end-zero' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).body({ start: 1, end: 0 });\n assert(\n result instanceof ReadableStream,\n true,\n `CoreCache.lookup(key).body() instanceof ReadableStream`,\n );\n\n result = await streamToString(result);\n console.log({ result });\n assert(\n result,\n 'hello',\n `await streamToString(CoreCache.lookup(key).body())`,\n );\n });\n }\n\n // length(): number;\n {\n routes.set('/cache-entry/length/called-as-constructor', () => {\n assertThrows(() => {\n new CacheEntry.prototype.length();\n }, TypeError);\n });\n routes.set('/cache-entry/length/called-unbound', () => {\n assertThrows(() => {\n CacheEntry.prototype.length.call(undefined);\n }, Error);\n });\n routes.set('/cache-entry/length/called-on-instance', () => {\n let key = '/cache-entry/length/called-on-instance' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).length();\n assert(result, 5, `CoreCache.lookup(key).length()`);\n });\n // TODO pass in an entry with unknown length and then call length and check it is null?\n /// The size in bytes of the cached item, if known.\n ///\n /// The length of the cached item may be unknown if the item is currently being streamed into\n /// the cache without a fixed length.\n }\n\n // maxAge(): number;\n {\n routes.set('/cache-entry/maxAge/called-as-constructor', () => {\n assertThrows(() => {\n new CacheEntry.prototype.maxAge();\n }, TypeError);\n });\n routes.set('/cache-entry/maxAge/called-unbound', () => {\n assertThrows(() => {\n CacheEntry.prototype.maxAge.call(undefined);\n }, Error);\n });\n routes.set('/cache-entry/maxAge/called-on-instance', async () => {\n let key = '/cache-entry/maxAge/called-on-instance' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).maxAge();\n assert(result, 60_000, `CoreCache.lookup(key).maxAge()`);\n });\n }\n\n // staleWhileRevalidate(): number;\n {\n routes.set(\n '/cache-entry/staleWhileRevalidate/called-as-constructor',\n () => {\n assertThrows(() => {\n new CacheEntry.prototype.staleWhileRevalidate();\n }, TypeError);\n },\n );\n routes.set('/cache-entry/staleWhileRevalidate/called-unbound', () => {\n assertThrows(() => {\n CacheEntry.prototype.staleWhileRevalidate.call(undefined);\n }, Error);\n });\n routes.set(\n '/cache-entry/staleWhileRevalidate/called-on-instance',\n async () => {\n let key =\n '/cache-entry/staleWhileRevalidate/called-on-instance' +\n Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).staleWhileRevalidate();\n assert(\n typeof result,\n 'number',\n `typeof CoreCache.lookup(key).staleWhileRevalidate()`,\n );\n assert(\n result >= 0,\n true,\n `CoreCache.lookup(key).staleWhileRevalidate() >= 0`,\n );\n },\n );\n }\n\n // age(): number;\n {\n routes.set('/cache-entry/age/called-as-constructor', () => {\n assertThrows(() => {\n new CacheEntry.prototype.age();\n }, TypeError);\n });\n routes.set('/cache-entry/age/called-unbound', () => {\n assertThrows(() => {\n CacheEntry.prototype.age.call(undefined);\n }, Error);\n });\n routes.set('/cache-entry/age/called-on-instance', async () => {\n let key = '/cache-entry/age/called-on-instance' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).age();\n assert(typeof result, 'number', `typeof CoreCache.lookup(key).age()`);\n assert(result >= 0, true, `CoreCache.lookup(key).age() >= 0`);\n await sleep(1000);\n result = CoreCache.lookup(key).age();\n assert(\n result >= 1_000,\n true,\n `CoreCache.lookup(key).age() >= 1_000 (${result})`,\n );\n });\n }\n\n // hits(): number;\n {\n routes.set('/cache-entry/hits/called-as-constructor', () => {\n assertThrows(() => {\n new CacheEntry.prototype.hits();\n }, TypeError);\n });\n routes.set('/cache-entry/hits/called-unbound', () => {\n assertThrows(() => {\n CacheEntry.prototype.hits.call(undefined);\n }, Error);\n });\n routes.set('/cache-entry/hits/called-on-instance', () => {\n let key = '/cache-entry/hits/called-on-instance' + Math.random();\n let writer = CoreCache.insert(key, {\n maxAge: 60 * 1000,\n });\n writer.append('hello');\n writer.close();\n let result = CoreCache.lookup(key).hits();\n assert(result, 1, `CoreCache.lookup(key).hits()`);\n result = CoreCache.lookup(key).hits();\n assert(result, 2, `CoreCache.lookup(key).hits()`);\n });\n }\n}\n\n// TransactionCacheEntry\n{\n routes.set('/transaction-cache-entry/interface', () => {\n let actual = Reflect.ownKeys(TransactionCacheEntry);\n let expected = ['prototype', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(TransactionCacheEntry)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry,\n 'prototype',\n );\n expected = {\n value: TransactionCacheEntry.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(TransactionCacheEntry, 'name');\n expected = {\n value: 'TransactionCacheEntry',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(TransactionCacheEntry.prototype);\n expected = [\n 'constructor',\n 'insert',\n 'insertAndStreamBack',\n 'update',\n 'cancel',\n Symbol.toStringTag,\n ];\n assert(\n actual,\n expected,\n `Reflect.ownKeys(TransactionCacheEntry.prototype)`,\n );\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: TransactionCacheEntry.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype, 'constructor')`,\n );\n\n assert(\n typeof TransactionCacheEntry.prototype.constructor,\n 'function',\n `typeof TransactionCacheEntry.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'TransactionCacheEntry',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype,\n Symbol.toStringTag,\n );\n expected = {\n writable: false,\n enumerable: false,\n configurable: true,\n value: 'TransactionCacheEntry',\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof TransactionCacheEntry.prototype[Symbol.toStringTag],\n 'string',\n `typeof TransactionCacheEntry.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the insert method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype,\n 'insert',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: TransactionCacheEntry.prototype.insert,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype, 'insert')`,\n );\n\n assert(\n typeof TransactionCacheEntry.prototype.insert,\n 'function',\n `typeof TransactionCacheEntry.prototype.insert`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.insert,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.insert, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.insert,\n 'name',\n );\n expected = {\n value: 'insert',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.insert, 'name')`,\n );\n }\n\n // Check the insertAndStreamBack method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype,\n 'insertAndStreamBack',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: TransactionCacheEntry.prototype.insertAndStreamBack,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype, 'insertAndStreamBack')`,\n );\n\n assert(\n typeof TransactionCacheEntry.prototype.insertAndStreamBack,\n 'function',\n `typeof TransactionCacheEntry.prototype.insertAndStreamBack`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.insertAndStreamBack,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.insertAndStreamBack, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.insertAndStreamBack,\n 'name',\n );\n expected = {\n value: 'insertAndStreamBack',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.insertAndStreamBack, 'name')`,\n );\n }\n\n // Check the update method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype,\n 'update',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: TransactionCacheEntry.prototype.update,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype, 'update')`,\n );\n\n assert(\n typeof TransactionCacheEntry.prototype.update,\n 'function',\n `typeof TransactionCacheEntry.prototype.update`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.update,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.update, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.update,\n 'name',\n );\n expected = {\n value: 'update',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.update, 'name')`,\n );\n }\n\n // Check the cancel method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype,\n 'cancel',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: TransactionCacheEntry.prototype.cancel,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype, 'cancel')`,\n );\n\n assert(\n typeof TransactionCacheEntry.prototype.cancel,\n 'function',\n `typeof TransactionCacheEntry.prototype.cancel`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.cancel,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.cancel, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n TransactionCacheEntry.prototype.cancel,\n 'name',\n );\n expected = {\n value: 'cancel',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(TransactionCacheEntry.prototype.cancel, 'name')`,\n );\n }\n });\n\n // insert(options: TransactionInsertOptions): FastlyBody;\n {\n routes.set('/transaction-cache-entry/insert/called-as-constructor', () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n new entry.insert({ maxAge: 1 });\n }, TypeError);\n });\n routes.set(\n '/transaction-cache-entry/insert/entry-parameter-not-supplied',\n () => {\n let entry = CoreCache.transactionLookup('1');\n assert(\n entry instanceof TransactionCacheEntry,\n true,\n 'entry instanceof TransactionCacheEntry',\n );\n assertThrows(\n () => {\n entry.insert();\n },\n TypeError,\n `insert: At least 1 argument required, but only 0 passed`,\n );\n },\n );\n\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-maxAge-field-valid-record',\n () => {\n let body;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup('1');\n body = entry.insert({ maxAge: 1 });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `entry.insert({maxAge: 1}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-maxAge-field-NaN',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-maxAge-field-postitive-infinity',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-maxAge-field-negative-infinity',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-maxAge-field-negative-number',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-initialAge-field-valid-record',\n () => {\n let body;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup('1');\n body = entry.insert({ maxAge: 1, initialAge: 1 });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `entry.insert({maxAge: 1,initialAge: 1}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-initialAge-field-NaN',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n initialAge: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-initialAge-field-postitive-infinity',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n initialAge: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-initialAge-field-negative-infinity',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n initialAge: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-initialAge-field-negative-number',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n initialAge: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-staleWhileRevalidate-field-valid-record',\n () => {\n let body;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup('1');\n body = entry.insert({ maxAge: 1, staleWhileRevalidate: 1 });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `entry.insert({maxAge: 1,staleWhileRevalidate: 1}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-staleWhileRevalidate-field-NaN',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n staleWhileRevalidate: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-staleWhileRevalidate-field-postitive-infinity',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n staleWhileRevalidate: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-staleWhileRevalidate-field-negative-infinity',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n staleWhileRevalidate: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-staleWhileRevalidate-field-negative-number',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n staleWhileRevalidate: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-length-field-valid-record',\n () => {\n let body;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup('1');\n body = entry.insert({ maxAge: 1, length: 1 });\n });\n assert(\n body instanceof FastlyBody,\n true,\n `entry.insert({maxAge: 1,length: 1}) instanceof FastlyBody`,\n );\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-length-field-NaN',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n length: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-length-field-postitive-infinity',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n length: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-length-field-negative-infinity',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n length: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-length-field-negative-number',\n () => {\n assertThrows(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n length: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-sensitive-field',\n () => {\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n sensitive: true,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insert/options-parameter-vary-field',\n () => {\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup('1');\n entry.insert({\n maxAge: 1,\n vary: ['animal', 'mineral', 'vegetable'],\n });\n });\n },\n );\n }\n\n // insertAndStreamBack(options: TransactionInsertOptions): [FastlyBody, CacheEntry];\n {\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/called-as-constructor',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n new entry.insertAndStreamBack({ maxAge: 1 });\n }, TypeError);\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/entry-parameter-not-supplied',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(\n () => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack();\n },\n TypeError,\n `insertAndStreamBack: At least 1 argument required, but only 0 passed`,\n );\n },\n );\n\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-maxAge-field-valid-record',\n (event) => {\n const path = new URL(event.request.url).pathname;\n let writer;\n let reader;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup(path);\n [writer, reader] = entry.insertAndStreamBack({ maxAge: 1 });\n });\n assert(\n writer instanceof FastlyBody,\n true,\n `writer instanceof FastlyBody`,\n );\n assert(\n reader instanceof CacheEntry,\n true,\n `writer instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-maxAge-field-NaN',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-maxAge-field-postitive-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-maxAge-field-negative-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-maxAge-field-negative-number',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-initialAge-field-valid-record',\n (event) => {\n const path = new URL(event.request.url).pathname;\n let writer;\n let reader;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup(path);\n [writer, reader] = entry.insertAndStreamBack({\n maxAge: 1,\n initialAge: 1,\n });\n });\n assert(\n writer instanceof FastlyBody,\n true,\n `writer instanceof FastlyBody`,\n );\n assert(\n reader instanceof CacheEntry,\n true,\n `writer instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-initialAge-field-NaN',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n initialAge: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-initialAge-field-postitive-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n initialAge: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-initialAge-field-negative-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n initialAge: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-initialAge-field-negative-number',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n initialAge: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-staleWhileRevalidate-field-valid-record',\n (event) => {\n const path = new URL(event.request.url).pathname;\n let writer, reader;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup(path);\n [writer, reader] = entry.insertAndStreamBack({\n maxAge: 1,\n staleWhileRevalidate: 1,\n });\n });\n assert(\n writer instanceof FastlyBody,\n true,\n `writer instanceof FastlyBody`,\n );\n assert(\n reader instanceof CacheEntry,\n true,\n `writer instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-staleWhileRevalidate-field-NaN',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n staleWhileRevalidate: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-staleWhileRevalidate-field-postitive-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n staleWhileRevalidate: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-staleWhileRevalidate-field-negative-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n staleWhileRevalidate: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-staleWhileRevalidate-field-negative-number',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n staleWhileRevalidate: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-length-field-valid-record',\n (event) => {\n const path = new URL(event.request.url).pathname;\n let writer, reader;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup(path);\n [writer, reader] = entry.insertAndStreamBack({\n maxAge: 1,\n length: 1,\n });\n });\n assert(\n writer instanceof FastlyBody,\n true,\n `writer instanceof FastlyBody`,\n );\n assert(\n reader instanceof CacheEntry,\n true,\n `writer instanceof CacheEntry`,\n );\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-length-field-NaN',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n length: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-length-field-postitive-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n length: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-length-field-negative-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n length: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-length-field-negative-number',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n length: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-sensitive-field',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n sensitive: true,\n });\n });\n },\n );\n\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/write-to-writer-and-read-from-reader',\n async (event) => {\n const path = new URL(event.request.url).pathname;\n let entry = CoreCache.transactionLookup(path);\n let [writer, reader] = entry.insertAndStreamBack({\n maxAge: 60 * 1000,\n sensitive: true,\n });\n writer.append('hello');\n writer.close();\n const actual = await new Response(reader.body()).text();\n assert(actual, 'hello', `actual === \"hello\"`);\n },\n );\n routes.set(\n '/transaction-cache-entry/insertAndStreamBack/options-parameter-vary-field',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.insertAndStreamBack({\n maxAge: 1,\n vary: ['animal', 'mineral', 'vegetable'],\n });\n });\n },\n );\n }\n\n // update(options: TransactionInsertOptions): void;\n {\n routes.set(\n '/transaction-cache-entry/update/called-as-constructor',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n new entry.update({ maxAge: 1 });\n }, TypeError);\n },\n );\n routes.set(\n '/transaction-cache-entry/update/entry-parameter-not-supplied',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(\n () => {\n let entry = CoreCache.transactionLookup(path);\n entry.update();\n },\n TypeError,\n `update: At least 1 argument required, but only 0 passed`,\n );\n },\n );\n\n routes.set(\n '/transaction-cache-entry/update/options-parameter-maxAge-field-valid-record',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertDoesNotThrow(() => {\n let writer = CoreCache.insert(path, {\n maxAge: 0,\n staleWhileRevalidate: 60 * 1000,\n });\n writer.append('meow');\n writer.close();\n let entry = CoreCache.transactionLookup(path);\n entry.update({ maxAge: 1 });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-maxAge-field-NaN',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-maxAge-field-postitive-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-maxAge-field-negative-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-maxAge-field-negative-number',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-initialAge-field-valid-record',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertDoesNotThrow(() => {\n let writer = CoreCache.insert(path, {\n maxAge: 0,\n staleWhileRevalidate: 60 * 1000,\n });\n writer.append('meow');\n writer.close();\n let entry = CoreCache.transactionLookup(path);\n entry.update({ maxAge: 1, initialAge: 1 });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-initialAge-field-NaN',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n initialAge: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-initialAge-field-postitive-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n initialAge: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-initialAge-field-negative-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n initialAge: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-initialAge-field-negative-number',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n initialAge: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-staleWhileRevalidate-field-valid-record',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertDoesNotThrow(() => {\n let writer = CoreCache.insert(path, {\n maxAge: 0,\n staleWhileRevalidate: 60 * 1000,\n });\n writer.append('meow');\n writer.close();\n let entry = CoreCache.transactionLookup(path);\n entry.update({ maxAge: 1, staleWhileRevalidate: 1 });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-staleWhileRevalidate-field-NaN',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n staleWhileRevalidate: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-staleWhileRevalidate-field-postitive-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n staleWhileRevalidate: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-staleWhileRevalidate-field-negative-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n staleWhileRevalidate: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-staleWhileRevalidate-field-negative-number',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n staleWhileRevalidate: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-length-field-valid-record',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertDoesNotThrow(() => {\n let writer = CoreCache.insert(path, {\n maxAge: 0,\n staleWhileRevalidate: 60 * 1000,\n });\n writer.append('meow');\n writer.close();\n let entry = CoreCache.transactionLookup(path);\n entry.update({ maxAge: 1, length: 1 });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-length-field-NaN',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n length: NaN,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-length-field-postitive-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n length: Number.POSITIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-length-field-negative-infinity',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n length: Number.NEGATIVE_INFINITY,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-length-field-negative-number',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1,\n length: -1,\n });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/write-to-writer-and-read-from-reader',\n async (event) => {\n const path = new URL(event.request.url).pathname;\n let entry = CoreCache.transactionLookup(path);\n let writer = entry.insert({\n maxAge: 1,\n staleWhileRevalidate: 60 * 1000,\n });\n writer.append('meow');\n writer.close();\n entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 60 * 1000,\n });\n await sleep(1000);\n entry = CoreCache.transactionLookup(path);\n assert(entry.maxAge(), 60 * 1000, `entry2.maxAge() === 60 * 1000`);\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-vary-field',\n async (event) => {\n const path = new URL(event.request.url).pathname;\n let entry = CoreCache.transactionLookup(path);\n let writer = entry.insert({\n maxAge: 1,\n staleWhileRevalidate: 60 * 1000,\n vary: ['animal', 'mineral', 'vegetable'],\n });\n writer.append('meow');\n writer.close();\n await sleep(1000);\n entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1000,\n vary: ['animal'],\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/update/options-parameter-userMetadata-field',\n async (event) => {\n const path = new URL(event.request.url).pathname;\n let entry = CoreCache.transactionLookup(path);\n let writer = entry.insert({\n maxAge: 1,\n staleWhileRevalidate: 60 * 1000,\n });\n writer.append('meow');\n writer.close();\n await sleep(1000);\n entry = CoreCache.transactionLookup(path);\n entry.update({\n maxAge: 1000,\n userMetadata: 'hello',\n });\n },\n );\n // TODO: tests for options parameter fields\n // surrogateKeys?: Array,-- empty string? -- toString which throws -- wrong types?\n }\n\n // cancel(): void;\n {\n routes.set(\n '/transaction-cache-entry/cancel/called-as-constructor',\n (event) => {\n const path = new URL(event.request.url).pathname;\n assertThrows(() => {\n let entry = CoreCache.transactionLookup(path);\n new entry.cancel();\n }, TypeError);\n },\n );\n routes.set('/transaction-cache-entry/cancel/called-once', (event) => {\n const path = new URL(event.request.url).pathname;\n assertDoesNotThrow(() => {\n let entry = CoreCache.transactionLookup(path);\n entry.cancel();\n });\n });\n routes.set(\n '/transaction-cache-entry/cancel/makes-entry-cancelled',\n (event) => {\n const path = new URL(event.request.url).pathname;\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.transactionLookup(path);\n entry.cancel();\n });\n assertThrows(() => {\n entry.insert({ maxAge: 1 });\n });\n },\n );\n routes.set(\n '/transaction-cache-entry/cancel/called-twice-throws',\n (event) => {\n const path = new URL(event.request.url).pathname;\n let entry;\n assertDoesNotThrow(() => {\n entry = CoreCache.transactionLookup(path);\n entry.cancel();\n });\n assertThrows(() => {\n entry.cancel();\n });\n },\n );\n }\n}\n\n{\n routes.set(\n '/core-cache/transaction-lookup-transaction-insert-vary-works',\n async () => {\n const key = `/core-cache/vary-works-${Date.now()}`;\n const animal = 'animal';\n let entry = CoreCache.transactionLookup(key, {\n headers: {\n [animal]: 'cat',\n },\n });\n assert(entry.state().found(), false, `entry.state().found() === false`);\n let writer = entry.insert({\n maxAge: 60_000 * 60,\n vary: [animal],\n headers: {\n [animal]: 'cat',\n },\n });\n\n writer.append('cat');\n writer.close();\n entry.close();\n await sleep(1_000);\n\n entry = CoreCache.transactionLookup(key, {\n headers: {\n [animal]: 'cat',\n },\n });\n assert(entry.state().found(), true, `entry.state().found() === true`);\n\n assert(\n await streamToString(entry.body()),\n 'cat',\n `await streamToString(CoreCache.lookup(key).body())`,\n );\n entry.close();\n\n entry = CoreCache.transactionLookup(key, {\n headers: {\n [animal]: 'dog',\n },\n });\n assert(entry.state().found(), false, `entry.state().found() == false`);\n\n writer = entry.insert({\n maxAge: 60_000 * 60,\n vary: [animal],\n headers: {\n [animal]: 'dog',\n },\n });\n\n writer.append('dog');\n writer.close();\n entry.close();\n await sleep(1_000);\n\n entry = CoreCache.transactionLookup(key, {\n headers: {\n [animal]: 'dog',\n },\n });\n assert(entry.state().found(), true, `entry.state().found() === true`);\n\n assert(\n await streamToString(entry.body()),\n 'dog',\n `await streamToString(CoreCache.lookup(key).body())`,\n );\n entry.close();\n },\n );\n\n routes.set('/core-cache/lookup-insert-vary-works', async () => {\n const key = `/core-cache/vary-works-${Date.now()}`;\n const animal = 'animal';\n let entry = CoreCache.lookup(key, {\n headers: {\n [animal]: 'cat',\n },\n });\n assert(entry, null, `entry == null`);\n let writer = CoreCache.insert(key, {\n maxAge: 60_000 * 60,\n vary: [animal],\n headers: {\n [animal]: 'cat',\n },\n });\n\n writer.append('cat');\n writer.close();\n await sleep(1_000);\n\n entry = CoreCache.lookup(key, {\n headers: {\n [animal]: 'cat',\n },\n });\n assert(entry.state().found(), true, `entry.state().found() === true`);\n\n assert(\n await streamToString(entry.body()),\n 'cat',\n `await streamToString(CoreCache.lookup(key).body())`,\n );\n entry.close();\n\n entry = CoreCache.lookup(key, {\n headers: {\n [animal]: 'dog',\n },\n });\n assert(entry, null, `entry == null`);\n\n writer = CoreCache.insert(key, {\n maxAge: 60_000 * 60,\n vary: [animal],\n headers: {\n [animal]: 'dog',\n },\n });\n\n writer.append('dog');\n writer.close();\n await sleep(1_000);\n\n entry = CoreCache.lookup(key, {\n headers: {\n [animal]: 'dog',\n },\n });\n assert(entry.state().found(), true, `entry.state().found() === true`);\n\n assert(\n await streamToString(entry.body()),\n 'dog',\n `await streamToString(CoreCache.lookup(key).body())`,\n );\n });\n}\n", "/// \n/* eslint-env serviceworker */\n\nimport {\n assert,\n assertDoesNotThrow,\n assertThrows,\n assertRejects,\n iteratableToStream,\n streamToString,\n assertResolves,\n} from './assertions.js';\nimport { SimpleCache, SimpleCacheEntry } from 'fastly:cache';\nimport { routes, isRunningLocally } from './routes.js';\n\nroutes.set('/simple-cache/interface', () => {\n let actual = Reflect.ownKeys(SimpleCache);\n let expected = [\n 'prototype',\n 'purge',\n 'get',\n 'getOrSet',\n 'set',\n 'length',\n 'name',\n ];\n assert(actual, expected, `Reflect.ownKeys(SimpleCache)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'prototype');\n expected = {\n value: SimpleCache.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'name');\n expected = {\n value: 'SimpleCache',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(SimpleCache.prototype);\n expected = ['constructor', Symbol.toStringTag];\n assert(actual, expected, `Reflect.ownKeys(SimpleCache.prototype)`);\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCache.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: SimpleCache.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.prototype, 'constructor')`,\n );\n\n assert(\n typeof SimpleCache.prototype.constructor,\n 'function',\n `typeof SimpleCache.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCache.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCache.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'SimpleCache',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCache.prototype,\n Symbol.toStringTag,\n );\n expected = {\n writable: false,\n enumerable: false,\n configurable: true,\n value: 'SimpleCache',\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof SimpleCache.prototype[Symbol.toStringTag],\n 'string',\n `typeof SimpleCache.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the get static method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'get');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: SimpleCache.get,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache, 'get')`,\n );\n\n assert(typeof SimpleCache.get, 'function', `typeof SimpleCache.get`);\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache.get, 'length');\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.get, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache.get, 'name');\n expected = {\n value: 'get',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.get, 'name')`,\n );\n }\n\n // Check the set static method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'set');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: SimpleCache.set,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache, 'set')`,\n );\n\n assert(typeof SimpleCache.set, 'function', `typeof SimpleCache.set`);\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache.set, 'length');\n expected = {\n value: 3,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.set, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache.set, 'name');\n expected = {\n value: 'set',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.set, 'name')`,\n );\n }\n\n // Check the purge static method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'purge');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: SimpleCache.purge,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache, 'purge')`,\n );\n\n assert(typeof SimpleCache.purge, 'function', `typeof SimpleCache.purge`);\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache.purge, 'length');\n expected = {\n value: 2,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.purge, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache.purge, 'name');\n expected = {\n value: 'purge',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.purge, 'name')`,\n );\n }\n\n // Check the getOrSet static method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'getOrSet');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: SimpleCache.getOrSet,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache, 'getOrSet')`,\n );\n\n assert(\n typeof SimpleCache.getOrSet,\n 'function',\n `typeof SimpleCache.getOrSet`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache.getOrSet, 'length');\n expected = {\n value: 2,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.getOrSet, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCache.getOrSet, 'name');\n expected = {\n value: 'getOrSet',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCache.getOrSet, 'name')`,\n );\n }\n});\n\n// SimpleCache constructor\n{\n routes.set('/simple-store/constructor/called-as-regular-function', () => {\n assertThrows(() => {\n SimpleCache();\n }, TypeError);\n });\n routes.set('/simple-cache/constructor/throws', () => {\n assertThrows(() => new SimpleCache(), TypeError);\n });\n}\n\n// SimpleCache purge static method\n// static purge(key: string, options: PurgeOptions): undefined;\n{\n routes.set('/simple-cache/purge/called-as-constructor', () => {\n if (!isRunningLocally()) {\n assertThrows(() => {\n new SimpleCache.purge('1', { scope: 'global' });\n }, TypeError);\n }\n });\n // Ensure we correctly coerce the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/simple-cache/purge/key-parameter-calls-7.1.17-ToString', () => {\n if (!isRunningLocally()) {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const key = {\n toString() {\n throw sentinel;\n },\n };\n SimpleCache.purge(key, { scope: 'global' });\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n SimpleCache.purge(Symbol(), { scope: 'global' });\n },\n TypeError,\n `can't convert symbol to string`,\n );\n }\n });\n routes.set('/simple-cache/purge/key-parameter-not-supplied', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n SimpleCache.purge();\n },\n TypeError,\n `SimpleCache.purge: At least 2 arguments required, but only 0 passed`,\n );\n }\n });\n routes.set('/simple-cache/purge/key-parameter-empty-string', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n SimpleCache.purge('', { scope: 'global' });\n },\n Error,\n `SimpleCache.purge: key can not be an empty string`,\n );\n }\n });\n routes.set('/simple-cache/purge/key-parameter-8135-character-string', () => {\n if (!isRunningLocally()) {\n assertDoesNotThrow(() => {\n const key = 'a'.repeat(8135);\n SimpleCache.purge(key, { scope: 'global' });\n });\n }\n });\n routes.set('/simple-cache/purge/key-parameter-8136-character-string', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n const key = 'a'.repeat(8136);\n SimpleCache.purge(key, { scope: 'global' });\n },\n Error,\n `SimpleCache.purge: key is too long, the maximum allowed length is 8135.`,\n );\n }\n });\n routes.set('/simple-cache/purge/options-parameter', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n const key = 'a';\n SimpleCache.purge(key, 'hello');\n },\n Error,\n `SimpleCache.purge: options parameter is not an object.`,\n );\n assertThrows(\n () => {\n const key = 'a';\n SimpleCache.purge(key, { scope: Symbol() });\n },\n Error,\n `can't convert symbol to string`,\n );\n assertThrows(\n () => {\n const key = 'a';\n SimpleCache.purge(key, { scope: '' });\n },\n Error,\n `SimpleCache.purge: scope field of options parameter must be either 'pop', or 'global'.`,\n );\n assertDoesNotThrow(() => {\n const key = 'a';\n SimpleCache.purge(key, { scope: 'pop' });\n });\n assertDoesNotThrow(() => {\n const key = 'a';\n SimpleCache.purge(key, { scope: 'global' });\n });\n }\n });\n routes.set('/simple-cache/purge/returns-undefined', () => {\n if (!isRunningLocally()) {\n assert(\n SimpleCache.purge('meow', { scope: 'global' }),\n undefined,\n \"SimpleCache.purge('meow', {scope'global'})\",\n );\n }\n });\n}\n\n// SimpleCache set static method\n// static set(key: string, value: BodyInit, ttl: number): undefined;\n{\n routes.set('/simple-cache/set/called-as-constructor', () => {\n if (!isRunningLocally()) {\n assertThrows(() => {\n new SimpleCache.set('1', 'meow', 1);\n }, TypeError);\n }\n });\n // Ensure we correctly coerce the key parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/simple-cache/set/key-parameter-calls-7.1.17-ToString', () => {\n if (!isRunningLocally()) {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const key = {\n toString() {\n throw sentinel;\n },\n };\n SimpleCache.set(key, 'meow', 1);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n SimpleCache.set(Symbol(), 'meow', 1);\n },\n TypeError,\n `can't convert symbol to string`,\n );\n }\n });\n // Ensure we correctly coerce the tll parameter to a number as according to\n // https://tc39.es/ecma262/#sec-tonumber\n routes.set('/simple-cache/set/tll-parameter-7.1.4-ToNumber', () => {\n if (!isRunningLocally()) {\n let sentinel;\n let requestedType;\n const test = () => {\n sentinel = Symbol('sentinel');\n const ttl = {\n [Symbol.toPrimitive](type) {\n requestedType = type;\n throw sentinel;\n },\n };\n SimpleCache.set('1', 'meow', ttl);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n assert(requestedType, 'number', 'requestedType === \"number\"');\n }\n assertThrows(\n () => SimpleCache.set('1', 'meow', Symbol()),\n TypeError,\n `can't convert symbol to number`,\n );\n }\n });\n routes.set('/simple-cache/set/no-parameters-supplied', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n SimpleCache.set();\n },\n TypeError,\n `SimpleCache.set: At least 3 arguments required, but only 0 passed`,\n );\n }\n });\n routes.set('/simple-cache/set/key-parameter-empty-string', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n SimpleCache.set('', 'meow', 1);\n },\n Error,\n `SimpleCache.set: key can not be an empty string`,\n );\n }\n });\n routes.set('/simple-cache/set/key-parameter-8135-character-string', () => {\n if (!isRunningLocally()) {\n assertDoesNotThrow(() => {\n const key = 'a'.repeat(8135);\n SimpleCache.set(key, 'meow', 1);\n });\n }\n });\n routes.set('/simple-cache/set/key-parameter-8136-character-string', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n const key = 'a'.repeat(8136);\n SimpleCache.set(key, 'meow', 1);\n },\n Error,\n `SimpleCache.set: key is too long, the maximum allowed length is 8135.`,\n );\n }\n });\n routes.set('/simple-cache/set/ttl-parameter-negative-number', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n SimpleCache.set('cat', 'meow', -1);\n },\n Error,\n `SimpleCache.set: TTL parameter is an invalid value, only positive numbers can be used for TTL values.`,\n );\n }\n });\n routes.set('/simple-cache/set/ttl-parameter-NaN', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n SimpleCache.set('cat', 'meow', NaN);\n },\n Error,\n `SimpleCache.set: TTL parameter is an invalid value, only positive numbers can be used for TTL values.`,\n );\n }\n });\n routes.set('/simple-cache/set/ttl-parameter-Infinity', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n SimpleCache.set('cat', 'meow', Number.POSITIVE_INFINITY);\n },\n Error,\n `SimpleCache.set: TTL parameter is an invalid value, only positive numbers can be used for TTL values.`,\n );\n assertThrows(\n () => {\n SimpleCache.set('cat', 'meow', Number.NEGATIVE_INFINITY);\n },\n Error,\n `SimpleCache.set: TTL parameter is an invalid value, only positive numbers can be used for TTL values.`,\n );\n }\n });\n routes.set('/simple-cache/set/value-parameter-as-undefined', () => {\n if (!isRunningLocally()) {\n assert(\n SimpleCache.set('meow', undefined, 1),\n undefined,\n 'SimpleCache.set(\"meow\", undefined, 1) === undefined',\n );\n }\n });\n // - ReadableStream\n routes.set(\n '/simple-cache/set/value-parameter-readablestream-missing-length-parameter',\n () => {\n if (!isRunningLocally()) {\n // TODO: remove this when streams are supported\n assertThrows(\n () => {\n const stream = iteratableToStream([]);\n SimpleCache.set('meow', stream, 1);\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n routes.set(\n '/simple-cache/set/value-parameter-readablestream-negative-length-parameter',\n () => {\n if (!isRunningLocally()) {\n // TODO: remove this when streams are supported\n assertThrows(\n () => {\n const stream = iteratableToStream([]);\n SimpleCache.set('meow', stream, 1, -1);\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n routes.set(\n '/simple-cache/set/value-parameter-readablestream-nan-length-parameter',\n () => {\n if (!isRunningLocally()) {\n // TODO: remove this when streams are supported\n assertThrows(\n () => {\n const stream = iteratableToStream([]);\n SimpleCache.set('meow', stream, 1, NaN);\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n routes.set(\n '/simple-cache/set/value-parameter-readablestream-negative-infinity-length-parameter',\n () => {\n if (!isRunningLocally()) {\n // TODO: remove this when streams are supported\n assertThrows(\n () => {\n const stream = iteratableToStream([]);\n SimpleCache.set('meow', stream, 1, Number.NEGATIVE_INFINITY);\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n routes.set(\n '/simple-cache/set/value-parameter-readablestream-positive-infinity-length-parameter',\n () => {\n if (!isRunningLocally()) {\n // TODO: remove this when streams are supported\n assertThrows(\n () => {\n const stream = iteratableToStream([]);\n SimpleCache.set('meow', stream, 1, Number.POSITIVE_INFINITY);\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n // Ensure we correctly coerce the tll parameter to a number as according to\n // https://tc39.es/ecma262/#sec-tonumber\n routes.set('/simple-cache/set/length-parameter-7.1.4-ToNumber', async () => {\n if (!isRunningLocally()) {\n const res = await fetch(\n 'https://compute-sdk-test-backend.edgecompute.app/',\n {\n backend: 'TheOrigin',\n },\n );\n let sentinel;\n let requestedType;\n const test = () => {\n sentinel = Symbol('sentinel');\n const length = {\n [Symbol.toPrimitive](type) {\n requestedType = type;\n throw sentinel;\n },\n };\n SimpleCache.set('1', res.body, 1, length);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n assert(requestedType, 'number', 'requestedType === \"number\"');\n }\n assertThrows(\n () => SimpleCache.set('1', res.body, 1, Symbol()),\n TypeError,\n `can't convert symbol to number`,\n );\n }\n });\n routes.set('/simple-cache/set/value-parameter-readablestream-empty', () => {\n if (!isRunningLocally()) {\n // TODO: remove this when streams are supported\n assertThrows(\n () => {\n const stream = iteratableToStream([]);\n SimpleCache.set('meow', stream, 1, 0);\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n });\n routes.set('/simple-cache/set/value-parameter-readablestream-locked', () => {\n if (!isRunningLocally()) {\n const stream = iteratableToStream([]);\n // getReader() causes the stream to become locked\n stream.getReader();\n assertThrows(\n () => {\n SimpleCache.set('meow', stream, 1, 0);\n },\n TypeError,\n `Can't use a ReadableStream that's locked or has ever been read from or canceled`,\n );\n }\n });\n routes.set('/simple-cache/set/value-parameter-readablestream', async () => {\n if (!isRunningLocally()) {\n const res = await fetch(\n 'https://compute-sdk-test-backend.edgecompute.app/',\n {\n backend: 'TheOrigin',\n },\n );\n let result = SimpleCache.set(\n 'readablestream',\n res.body,\n 100,\n res.headers.get('content-length'),\n );\n assert(\n result,\n undefined,\n `SimpleCache.set('readablestream', res.body, 100)`,\n );\n }\n });\n\n // - URLSearchParams\n routes.set('/simple-cache/set/value-parameter-URLSearchParams', () => {\n if (!isRunningLocally()) {\n const items = [\n new URLSearchParams(),\n new URLSearchParams({ a: 'b', c: 'd' }),\n ];\n for (const searchParams of items) {\n let result = SimpleCache.set('meow', searchParams, 1);\n assert(\n result,\n undefined,\n `SimpleCache.set(\"meow\", searchParams, 1) === undefiend`,\n );\n }\n }\n });\n // - USV strings\n routes.set('/simple-cache/set/value-parameter-strings', () => {\n if (!isRunningLocally()) {\n const strings = [\n // empty\n '',\n // lone surrogate\n '\\uD800',\n // surrogate pair\n '\uD840\uDE13',\n String('carrot'),\n ];\n for (const string of strings) {\n let result = SimpleCache.set('meow', string, 1);\n assert(\n result,\n undefined,\n `SimpleCache.set(\"meow\", string, 1) === undefined`,\n );\n }\n }\n });\n\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/simple-cache/set/value-parameter-calls-7.1.17-ToString', () => {\n if (!isRunningLocally()) {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const value = {\n toString() {\n throw sentinel;\n },\n };\n SimpleCache.set('meow', value, 1);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n SimpleCache.set('meow', Symbol(), 1);\n },\n TypeError,\n `can't convert symbol to string`,\n );\n }\n });\n\n // - buffer source\n routes.set('/simple-cache/set/value-parameter-buffer', () => {\n if (!isRunningLocally()) {\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n let result = SimpleCache.set('meow', typedArray.buffer, 1);\n assert(\n result,\n undefined,\n `SimpleCache.set(\"meow\", typedArray.buffer, 1) === undefined`,\n );\n }\n }\n });\n routes.set('/simple-cache/set/value-parameter-arraybuffer', () => {\n if (!isRunningLocally()) {\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n let result = SimpleCache.set('meow', typedArray.buffer, 1);\n assert(\n result,\n undefined,\n `SimpleCache.set(\"meow\", typedArray.buffer, 1) === undefined`,\n );\n }\n }\n });\n routes.set('/simple-cache/set/value-parameter-typed-arrays', () => {\n if (!isRunningLocally()) {\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n let result = SimpleCache.set('meow', typedArray, 1);\n assert(\n result,\n undefined,\n `SimpleCache.set(\"meow\", typedArray, 1) === undefined`,\n );\n }\n }\n });\n routes.set('/simple-cache/set/value-parameter-dataview', () => {\n if (!isRunningLocally()) {\n const typedArrayConstructors = [\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n BigUint64Array,\n ];\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n const view = new DataView(typedArray.buffer);\n let result = SimpleCache.set('meow', view, 1);\n assert(\n result,\n undefined,\n `SimpleCache.set(\"meow\", view, 1) === undefined`,\n );\n }\n }\n });\n routes.set('/simple-cache/set/returns-undefined', () => {\n if (!isRunningLocally()) {\n assert(\n SimpleCache.set('1', 'meow', 1),\n undefined,\n \"SimpleCache.set('1', 'meow', 1) === undefined\",\n );\n }\n });\n}\n\n// SimpleCache get static method\n// static get(key: string): SimpleCacheEntry | null;\n{\n routes.set('/simple-cache/get/called-as-constructor', () => {\n if (!isRunningLocally()) {\n assertThrows(() => {\n new SimpleCache.get('1');\n }, TypeError);\n }\n });\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/simple-cache/get/key-parameter-calls-7.1.17-ToString', () => {\n if (!isRunningLocally()) {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const key = {\n toString() {\n throw sentinel;\n },\n };\n SimpleCache.get(key);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n SimpleCache.get(Symbol());\n },\n TypeError,\n `can't convert symbol to string`,\n );\n }\n });\n routes.set('/simple-cache/get/key-parameter-not-supplied', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n SimpleCache.get();\n },\n TypeError,\n `SimpleCache.get: At least 1 argument required, but only 0 passed`,\n );\n }\n });\n routes.set('/simple-cache/get/key-parameter-empty-string', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n SimpleCache.get('');\n },\n Error,\n `SimpleCache.get: key can not be an empty string`,\n );\n }\n });\n routes.set('/simple-cache/get/key-parameter-8135-character-string', () => {\n if (!isRunningLocally()) {\n assertDoesNotThrow(() => {\n const key = 'a'.repeat(8135);\n SimpleCache.get(key);\n });\n }\n });\n routes.set('/simple-cache/get/key-parameter-8136-character-string', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n const key = 'a'.repeat(8136);\n SimpleCache.get(key);\n },\n Error,\n `SimpleCache.get: key is too long, the maximum allowed length is 8135.`,\n );\n }\n });\n routes.set('/simple-cache/get/key-does-not-exist-returns-null', () => {\n if (!isRunningLocally()) {\n let result = SimpleCache.get(Math.random());\n assert(result, null, `SimpleCache.get(Math.random()) === null`);\n }\n });\n routes.set('/simple-cache/get/key-exists', () => {\n if (!isRunningLocally()) {\n SimpleCache.set('cat', 'meow', 100);\n let result = SimpleCache.get('cat');\n assert(\n result instanceof SimpleCacheEntry,\n true,\n `SimpleCache.get('cat') instanceof SimpleCacheEntry`,\n );\n }\n });\n}\n\n// SimpleCacheEntry\n{\n routes.set('/simple-cache-entry/interface', async () => {\n return simpleCacheEntryInterfaceTests();\n });\n routes.set('/simple-cache-entry/text/valid', async () => {\n if (!isRunningLocally()) {\n let key = `entry-text-valid`;\n SimpleCache.set(key, 'hello', 100);\n let entry = SimpleCache.get(key);\n let result = entry.text();\n assert(\n result instanceof Promise,\n true,\n `entry.text() instanceof Promise`,\n );\n result = await result;\n assert(result, 'hello', `await entry.text()`);\n }\n });\n routes.set('/simple-cache-entry/json/valid', async () => {\n if (!isRunningLocally()) {\n let key = `entry-json-valid`;\n const obj = { a: 1, b: 2, c: 3 };\n SimpleCache.set(key, JSON.stringify(obj), 100);\n let entry = SimpleCache.get(key);\n let result = entry.json();\n assert(\n result instanceof Promise,\n true,\n `entry.json() instanceof Promise`,\n );\n result = await result;\n assert(result, obj, `await entry.json()`);\n }\n });\n routes.set('/simple-cache-entry/json/invalid', async () => {\n if (!isRunningLocally()) {\n let key = `entry-json-invalid`;\n SimpleCache.set(key, \"132abc;['-=9\", 100);\n let entry = SimpleCache.get(key);\n await assertRejects(\n () => entry.json(),\n SyntaxError,\n `JSON.parse: unexpected non-whitespace character after JSON data at line 1 column 4 of the JSON data`,\n );\n }\n });\n routes.set('/simple-cache-entry/arrayBuffer/valid', async () => {\n if (!isRunningLocally()) {\n let key = `entry-arraybuffer-valid`;\n SimpleCache.set(key, new Int8Array([0, 1, 2, 3]), 100);\n let entry = SimpleCache.get(key);\n let result = entry.arrayBuffer();\n assert(\n result instanceof Promise,\n true,\n `entry.arrayBuffer() instanceof Promise`,\n );\n result = await result;\n assert(\n result instanceof ArrayBuffer,\n true,\n `(await entry.arrayBuffer()) instanceof ArrayBuffer`,\n );\n }\n });\n routes.set('/simple-cache-entry/body', async () => {\n if (!isRunningLocally()) {\n let key = `entry-body`;\n SimpleCache.set(key, 'body body body', 100);\n let entry = SimpleCache.get(key);\n let result = entry.body;\n assert(\n result instanceof ReadableStream,\n true,\n `entry.body instanceof ReadableStream`,\n );\n let text = await streamToString(result);\n assert(text, 'body body body', `entry.body contents as string`);\n }\n });\n routes.set('/simple-cache-entry/bodyUsed', async () => {\n if (!isRunningLocally()) {\n let key = `entry-bodyUsed`;\n SimpleCache.set(key, 'body body body', 100);\n let entry = SimpleCache.get(key);\n assert(entry.bodyUsed, false, `entry.bodyUsed`);\n await entry.text();\n assert(entry.bodyUsed, true, `entry.bodyUsed`);\n }\n });\n routes.set('/simple-cache-entry/readablestream', async () => {\n if (!isRunningLocally()) {\n const res = await fetch(\n 'https://compute-sdk-test-backend.edgecompute.app/',\n {\n backend: 'TheOrigin',\n },\n );\n let key = `readablestream`;\n SimpleCache.set(key, res.body, 100, res.headers.get('content-length'));\n let entry = SimpleCache.get(key);\n assert(\n await entry.text(),\n 'Compute SDK Test Backend',\n `await entry.text()`,\n );\n }\n });\n}\nasync function simpleCacheEntryInterfaceTests() {\n let actual = Reflect.ownKeys(SimpleCacheEntry);\n let expected = ['prototype', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(SimpleCacheEntry)`);\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'prototype');\n expected = {\n value: SimpleCacheEntry.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'prototype')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'name');\n expected = {\n value: 'SimpleCacheEntry',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'name')`,\n );\n\n actual = Reflect.ownKeys(SimpleCacheEntry.prototype);\n expected = [\n 'constructor',\n 'body',\n 'bodyUsed',\n 'arrayBuffer',\n 'json',\n 'text',\n Symbol.toStringTag,\n ];\n assert(actual, expected, `Reflect.ownKeys(SimpleCacheEntry.prototype)`);\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: SimpleCacheEntry.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'constructor')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'text');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: SimpleCacheEntry.prototype.text,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'text')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'json');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: SimpleCacheEntry.prototype.json,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'json')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype,\n 'arrayBuffer',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: SimpleCacheEntry.prototype.arrayBuffer,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'arrayBuffer')`,\n );\n actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body');\n assert(\n actual.enumerable,\n true,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body').enumerable`,\n );\n assert(\n actual.configurable,\n true,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body').configurable`,\n );\n assert(\n 'set' in actual,\n true,\n `'set' in Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body')`,\n );\n assert(\n actual.set,\n undefined,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body').set`,\n );\n assert(\n typeof actual.get,\n 'function',\n `typeof Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body').get`,\n );\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype,\n 'bodyUsed',\n );\n assert(\n actual.enumerable,\n true,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed').enumerable`,\n );\n assert(\n actual.configurable,\n true,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed').configurable`,\n );\n assert(\n 'set' in actual,\n true,\n `'set' in Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed')`,\n );\n assert(\n actual.set,\n undefined,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed').set`,\n );\n assert(\n typeof actual.get,\n 'function',\n `typeof Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed').get`,\n );\n\n assert(\n typeof SimpleCacheEntry.prototype.constructor,\n 'function',\n `typeof SimpleCacheEntry.prototype.constructor`,\n );\n assert(\n typeof SimpleCacheEntry.prototype.text,\n 'function',\n `typeof SimpleCacheEntry.prototype.text`,\n );\n assert(\n typeof SimpleCacheEntry.prototype.json,\n 'function',\n `typeof SimpleCacheEntry.prototype.json`,\n );\n assert(\n typeof SimpleCacheEntry.prototype.arrayBuffer,\n 'function',\n `typeof SimpleCacheEntry.prototype.arrayBuffer`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'SimpleCacheEntry',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.constructor, 'name')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype.text,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.text, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype.text,\n 'name',\n );\n expected = {\n value: 'text',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.text, 'name')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype.json,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.json, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype.json,\n 'name',\n );\n expected = {\n value: 'json',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.json, 'name')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype.arrayBuffer,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.arrayBuffer, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n SimpleCacheEntry.prototype.arrayBuffer,\n 'name',\n );\n expected = {\n value: 'arrayBuffer',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.arrayBuffer, 'name')`,\n );\n}\n\n// SimpleCache getOrSet static method\n// static getOrSet(key: string, set: () => Promise<{value: BodyInit, ttl: number}>): Promise;\n// static getOrSet(key: string, set: () => Promise<{value: ReadableStream, ttl: number, length: number}>): Promise;\n{\n routes.set('/simple-cache/getOrSet/rejection-rejects-outer', async () => {\n if (!isRunningLocally()) {\n let key = String(Math.random());\n SimpleCache.get(key);\n assertRejects(\n () =>\n SimpleCache.getOrSet(key, async () => {\n throw RangeError('inner rejection');\n }),\n RangeError,\n 'inner rejection',\n );\n }\n });\n\n routes.set('/simple-cache/getOrSet/called-as-constructor', () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/called-as-constructor';\n assertThrows(() => {\n new SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: 10,\n };\n });\n }, TypeError);\n }\n });\n routes.set('/simple-cache/getOrSet/no-parameters-supplied', async () => {\n if (!isRunningLocally()) {\n await assertRejects(\n async () => {\n await SimpleCache.getOrSet();\n },\n TypeError,\n `SimpleCache.getOrSet: At least 2 arguments required, but only 0 passed`,\n );\n }\n });\n // Ensure we correctly coerce the key parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/simple-cache/getOrSet/key-parameter-calls-7.1.17-ToString',\n async () => {\n if (!isRunningLocally()) {\n let sentinel = { sentinel: 'sentinel' };\n const test = async () => {\n const key = {\n toString() {\n throw sentinel;\n },\n };\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: 10,\n };\n });\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n await assertRejects(\n async () => {\n await SimpleCache.getOrSet(Symbol(), async () => {\n return {\n value: 'meow',\n ttl: 10,\n };\n });\n },\n TypeError,\n `can't convert symbol to string`,\n );\n }\n },\n );\n\n // TODO: second parameter not a function\n\n routes.set('/simple-cache/getOrSet/key-parameter-empty-string', async () => {\n if (!isRunningLocally()) {\n await assertRejects(\n async () => {\n await SimpleCache.getOrSet('', async () => {\n return {\n value: 'meow',\n ttl: 10,\n };\n });\n },\n Error,\n `SimpleCache.getOrSet: key can not be an empty string`,\n );\n }\n });\n routes.set(\n '/simple-cache/getOrSet/key-parameter-8135-character-string',\n async () => {\n if (!isRunningLocally()) {\n await assertDoesNotThrow(async () => {\n const key = 'a'.repeat(8135);\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: 10,\n };\n });\n });\n }\n },\n );\n routes.set(\n '/simple-cache/getOrSet/key-parameter-8136-character-string',\n async () => {\n if (!isRunningLocally()) {\n await assertRejects(\n async () => {\n const key = 'a'.repeat(8136);\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: 10,\n };\n });\n },\n Error,\n `SimpleCache.getOrSet: key is too long, the maximum allowed length is 8135.`,\n );\n }\n },\n );\n // Ensure we correctly coerce the ttl field to a number as according to\n // https://tc39.es/ecma262/#sec-tonumber\n routes.set('/simple-cache/getOrSet/ttl-field-7.1.4-ToNumber', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/ttl-field-7.1.4-ToNumber';\n let sentinel = { sentinel: 'sentinel' };\n let requestedType;\n const test = async () => {\n const ttl = {\n [Symbol.toPrimitive](type) {\n requestedType = type;\n throw sentinel;\n },\n };\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl,\n };\n });\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n assert(requestedType, 'number', 'requestedType === \"number\"');\n }\n await assertRejects(\n () =>\n SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: Symbol(),\n };\n }),\n TypeError,\n `can't convert symbol to number`,\n );\n }\n });\n routes.set('/simple-cache/getOrSet/ttl-field-negative-number', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/ttl-field-negative-number';\n await assertRejects(\n async () => {\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: -1,\n };\n });\n },\n Error,\n `SimpleCache.getOrSet: TTL field is an invalid value, only positive numbers can be used for TTL values.`,\n );\n }\n });\n routes.set('/simple-cache/getOrSet/ttl-field-NaN', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/ttl-field-NaN';\n await assertRejects(\n async () => {\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: NaN,\n };\n });\n },\n Error,\n `SimpleCache.getOrSet: TTL field is an invalid value, only positive numbers can be used for TTL values.`,\n );\n }\n });\n routes.set('/simple-cache/getOrSet/ttl-field-Infinity', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/ttl-field-Infinity';\n await assertRejects(\n async () => {\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: Number.POSITIVE_INFINITY,\n };\n });\n },\n Error,\n `SimpleCache.getOrSet: TTL field is an invalid value, only positive numbers can be used for TTL values.`,\n );\n await assertRejects(\n async () => {\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: Number.NEGATIVE_INFINITY,\n };\n });\n },\n Error,\n `SimpleCache.getOrSet: TTL field is an invalid value, only positive numbers can be used for TTL values.`,\n );\n }\n });\n routes.set('/simple-cache/getOrSet/value-field-as-undefined', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-as-undefined';\n await assertResolves(async () => {\n await SimpleCache.getOrSet(key, async () => {\n return { value: undefined, ttl: 1 };\n });\n });\n }\n });\n // - ReadableStream\n routes.set(\n '/simple-cache/getOrSet/value-field-readablestream-missing-length-field',\n async () => {\n if (!isRunningLocally()) {\n let key =\n '/simple-cache/getOrSet/value-field-readablestream-missing-length-field';\n // TODO: remove this when streams are supported\n await assertRejects(\n async () => {\n const stream = iteratableToStream([]);\n await SimpleCache.getOrSet(key, async () => {\n return { value: stream, ttl: 1 };\n });\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n routes.set(\n '/simple-cache/getOrSet/value-field-readablestream-negative-length-field',\n async () => {\n if (!isRunningLocally()) {\n let key =\n '/simple-cache/getOrSet/value-field-readablestream-negative-length-field';\n // TODO: remove this when streams are supported\n await assertRejects(\n async () => {\n const stream = iteratableToStream([]);\n await SimpleCache.getOrSet(key, async () => {\n return { value: stream, ttl: 1, length: -1 };\n });\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n routes.set(\n '/simple-cache/getOrSet/value-field-readablestream-nan-length-field',\n async () => {\n if (!isRunningLocally()) {\n let key =\n '/simple-cache/getOrSet/value-field-readablestream-nan-length-field';\n // TODO: remove this when streams are supported\n await assertRejects(\n async () => {\n const stream = iteratableToStream([]);\n await SimpleCache.getOrSet(key, async () => {\n return { value: stream, ttl: 1, length: NaN };\n });\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n routes.set(\n '/simple-cache/getOrSet/value-field-readablestream-negative-infinity-length-field',\n async () => {\n if (!isRunningLocally()) {\n let key =\n '/simple-cache/getOrSet/value-field-readablestream-negative-infinity-length-field';\n // TODO: remove this when streams are supported\n await assertRejects(\n async () => {\n const stream = iteratableToStream([]);\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: stream,\n ttl: 1,\n length: Number.NEGATIVE_INFINITY,\n };\n });\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n routes.set(\n '/simple-cache/getOrSet/value-field-readablestream-positive-infinity-length-field',\n async () => {\n if (!isRunningLocally()) {\n let key =\n '/simple-cache/getOrSet/value-field-readablestream-positive-infinity-length-field';\n // TODO: remove this when streams are supported\n await assertRejects(\n async () => {\n const stream = iteratableToStream([]);\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: stream,\n ttl: 1,\n length: Number.POSITIVE_INFINITY,\n };\n });\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n // Ensure we correctly coerce the length field to a number as according to\n // https://tc39.es/ecma262/#sec-tonumber\n routes.set('/simple-cache/getOrSet/length-field-7.1.4-ToNumber', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/length-field-7.1.4-ToNumber';\n const res = await fetch(\n 'https://compute-sdk-test-backend.edgecompute.app/',\n {\n backend: 'TheOrigin',\n },\n );\n let sentinel = { sentinel: 'sentinel' };\n let requestedType;\n const test = async () => {\n const length = {\n [Symbol.toPrimitive](type) {\n requestedType = type;\n throw sentinel;\n },\n };\n await SimpleCache.getOrSet(key, async () => {\n return { value: res.body, ttl: 1, length };\n });\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n assert(requestedType, 'number', 'requestedType === \"number\"');\n }\n await assertRejects(\n async () =>\n await SimpleCache.getOrSet(key, async () => {\n return { value: res.body, ttl: 1, length: Symbol() };\n }),\n TypeError,\n `can't convert symbol to number`,\n );\n }\n });\n routes.set(\n '/simple-cache/getOrSet/value-field-readablestream-empty',\n async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-readablestream-empty';\n // TODO: remove this when streams are supported\n await assertRejects(\n async () => {\n const stream = iteratableToStream([]);\n await SimpleCache.getOrSet(key, async () => {\n return { value: stream, ttl: 1, length: 0 };\n });\n },\n TypeError,\n `Content-provided streams are not yet supported for streaming into SimpleCache`,\n );\n }\n },\n );\n routes.set(\n '/simple-cache/getOrSet/value-field-readablestream-locked',\n async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-readablestream-locked';\n const stream = iteratableToStream([]);\n // getReader() causes the stream to become locked\n stream.getReader();\n await assertRejects(\n async () => {\n await SimpleCache.getOrSet(key, async () => {\n return { value: stream, ttl: 1, length: 0 };\n });\n },\n TypeError,\n `Can't use a ReadableStream that's locked or has ever been read from or canceled`,\n );\n }\n },\n );\n routes.set('/simple-cache/getOrSet/value-field-readablestream', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-readablestream';\n const res = await fetch(\n 'https://compute-sdk-test-backend.edgecompute.app/',\n {\n backend: 'TheOrigin',\n },\n );\n await assertResolves(async () => {\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: res.body,\n ttl: 100,\n length: res.headers.get('content-length'),\n };\n });\n });\n }\n });\n\n // - URLSearchParams\n routes.set('/simple-cache/getOrSet/value-field-URLSearchParams', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-URLSearchParams';\n const items = [\n new URLSearchParams(),\n new URLSearchParams({ a: 'b', c: 'd' }),\n ];\n for (const searchParams of items) {\n await assertResolves(async () => {\n await SimpleCache.getOrSet(key, async () => {\n return { value: searchParams, ttl: 1 };\n });\n });\n }\n }\n });\n // - USV strings\n routes.set('/simple-cache/getOrSet/value-field-strings', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-strings';\n const strings = [\n // empty\n '',\n // lone surrogate\n '\\uD800',\n // surrogate pair\n '\uD840\uDE13',\n String('carrot'),\n ];\n for (const string of strings) {\n await assertResolves(async () => {\n await SimpleCache.getOrSet(key, async () => {\n return { value: string, ttl: 1 };\n });\n });\n }\n }\n });\n\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/simple-cache/getOrSet/value-field-calls-7.1.17-ToString',\n async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-calls-7.1.17-ToString';\n let sentinel = { sentinel: 'sentinel' };\n const test = async () => {\n const value = {\n toString() {\n throw sentinel;\n },\n };\n await SimpleCache.getOrSet(key, async () => {\n return { value, ttl: 1 };\n });\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n await assertRejects(\n async () => {\n await SimpleCache.getOrSet(key, async () => {\n return { value: Symbol(), ttl: 1 };\n });\n },\n TypeError,\n `can't convert symbol to string`,\n );\n }\n },\n );\n\n // - buffer source\n routes.set('/simple-cache/getOrSet/value-field-buffer', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-buffer';\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n await assertResolves(async () => {\n await SimpleCache.getOrSet(key + constructor.name, async () => {\n return { value: typedArray.buffer, ttl: 1 };\n });\n });\n }\n }\n });\n\n routes.set('/simple-cache/getOrSet/value-field-typed-arrays', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-typed-arrays';\n const typedArrayConstructors = [\n Int8Array,\n Int16Array,\n Int32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n Uint8Array,\n Uint8ClampedArray,\n Uint16Array,\n Uint32Array,\n BigUint64Array,\n ];\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n await assertResolves(async () => {\n await SimpleCache.getOrSet(key + constructor.name, async () => {\n return { value: typedArray, ttl: 1 };\n });\n });\n }\n }\n });\n routes.set('/simple-cache/getOrSet/value-field-dataview', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/value-field-dataview';\n const typedArrayConstructors = [\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n BigUint64Array,\n ];\n for (const constructor of typedArrayConstructors) {\n const typedArray = new constructor(8);\n const view = new DataView(typedArray.buffer);\n await assertResolves(async () => {\n await SimpleCache.getOrSet(key + constructor.name, async () => {\n return { value: view, ttl: 1 };\n });\n });\n }\n }\n });\n routes.set('/simple-cache/getOrSet/returns-SimpleCacheEntry', async () => {\n if (!isRunningLocally()) {\n let key = '/simple-cache/getOrSet/returns-SimpleCacheEntry';\n let result = await SimpleCache.getOrSet(key, async () => {\n return {\n value: 'meow',\n ttl: 10,\n };\n });\n assert(\n result instanceof SimpleCacheEntry,\n true,\n 'result instanceof SimpleCacheEntry',\n );\n }\n });\n routes.set(\n '/simple-cache/getOrSet/executes-the-set-method-when-key-not-in-cache',\n async () => {\n if (!isRunningLocally()) {\n let called = false;\n await SimpleCache.getOrSet(Math.random(), async () => {\n called = true;\n return {\n value: 'meow',\n ttl: 10,\n };\n });\n assert(called, true, 'called === true');\n }\n },\n );\n routes.set(\n '/simple-cache/getOrSet/does-not-execute-the-set-method-when-key-is-in-cache',\n async () => {\n if (!isRunningLocally()) {\n let key = Math.random();\n SimpleCache.set(key, 'meow', 100);\n let called = false;\n await SimpleCache.getOrSet(key, async () => {\n called = true;\n return {\n value: 'meow',\n ttl: 10,\n };\n });\n assert(called, false, 'called === false');\n }\n },\n );\n routes.set(\n '/simple-cache/getOrSet/does-not-freeze-when-called-after-a-get',\n async () => {\n if (!isRunningLocally()) {\n let key = String(Math.random());\n SimpleCache.get(key);\n await SimpleCache.getOrSet(key, async () => {\n return {\n value: key,\n ttl: 10,\n };\n });\n }\n },\n );\n routes.set('/simple-cache/getOrSet/integers-1-to-50', async () => {\n await Promise.all(\n Array.from({ length: 50 }, (_, i) => i + 1).map((i) =>\n SimpleCache.getOrSet(`key-${i}`, async () => ({\n value: await fetch(\"https://http-me.glitch.me/time\", { backend: 'httpme', cacheOverride: new CacheOverride('pass') }),\n ttl: 10,\n })),\n ),\n );\n });\n}\n", "import { strictEqual } from './assertions.js';\nimport { routes, isRunningLocally } from './routes.js';\n\nroutes.set('/client/tlsJA3MD5', (event) => {\n if (isRunningLocally()) {\n strictEqual(event.client.tlsJA3MD5, null);\n } else {\n strictEqual(\n typeof event.client.tlsJA3MD5,\n 'string',\n 'typeof event.client.tlsJA3MD5',\n );\n strictEqual(\n event.client.tlsJA3MD5.length,\n 32,\n 'event.client.tlsJA3MD5.length',\n );\n }\n});\nroutes.set('/client/tlsClientHello', (event) => {\n if (isRunningLocally()) {\n strictEqual(event.client.tlsClientHello, null);\n } else {\n strictEqual(\n event.client.tlsClientHello instanceof ArrayBuffer,\n true,\n 'event.client.tlsClientHello instanceof ArrayBuffer',\n );\n strictEqual(\n typeof event.client.tlsClientHello.byteLength,\n 'number',\n 'typeof event.client.tlsClientHello.byteLength',\n );\n }\n});\n\nroutes.set('/client/tlsClientCertificate', (event) => {\n if (isRunningLocally()) {\n strictEqual(event.client.tlsClientCertificate, null);\n } else {\n strictEqual(\n event.client.tlsClientCertificate instanceof ArrayBuffer,\n true,\n 'event.client.tlsClientCertificate instanceof ArrayBuffer',\n );\n strictEqual(\n event.client.tlsClientCertificate.byteLength,\n 0,\n 'event.client.tlsClientCertificate.byteLength',\n );\n }\n});\n\nroutes.set('/client/tlsCipherOpensslName', (event) => {\n if (isRunningLocally()) {\n strictEqual(event.client.tlsCipherOpensslName, null);\n } else {\n strictEqual(\n typeof event.client.tlsCipherOpensslName,\n 'string',\n 'typeof event.client.tlsCipherOpensslName',\n );\n }\n});\n\nroutes.set('/client/tlsProtocol', (event) => {\n if (isRunningLocally()) {\n strictEqual(event.client.tlsProtocol, null);\n } else {\n strictEqual(\n typeof event.client.tlsProtocol,\n 'string',\n 'typeof event.client.tlsProtocol',\n );\n }\n});\n", "export const { purgeSurrogateKey, vCpuTime } = globalThis.fastly;", "import { pass, ok, strictEqual, assertThrows } from './assertions.js';\nimport { routes } from './routes.js';\nimport { purgeSurrogateKey, vCpuTime } from 'fastly:compute';\n\nroutes.set('/compute/get-vcpu-ms', () => {\n const cpuTime = vCpuTime();\n strictEqual(typeof cpuTime, 'number');\n // We can't assert > 0; this only claims millisecond resolution,\n // and we hopefully spent less than 500us starting.\n ok(cpuTime >= 0, 'cpuTime >= 0');\n ok(cpuTime < 3000, 'cputime < 3000');\n const arr = new Array(100_000).fill(1);\n for (let j = 1; j < 100; j++) {\n for (let i = 1; i < 100_000; i++) {\n arr[i] = (arr[i] + arr[i - 1] + i) / 3;\n }\n }\n const cpuTime2 = vCpuTime();\n ok(cpuTime2 > cpuTime, 'cpuTime2 > cpuTime');\n ok(cpuTime2 - cpuTime > 1, 'cpuTime2 - cpuTime > 1');\n return pass('ok');\n});\n\nroutes.set('/compute/purge-surrogate-key-invalid', () => {\n assertThrows(\n () => {\n purgeSurrogateKey();\n },\n TypeError,\n 'purgeSurrogateKey: At least 1 argument required, but only 0 passed',\n );\n return pass('ok');\n});\n\nroutes.set('/compute/purge-surrogate-key-hard', () => {\n purgeSurrogateKey('test');\n return pass('ok');\n});\n\nroutes.set('/compute/purge-surrogate-key-soft', () => {\n purgeSurrogateKey('test', true);\n return pass('ok');\n});\n", "export const ConfigStore = globalThis.ConfigStore;", "/// \n\n/* eslint-env serviceworker */\nimport { assert } from './assertions.js';\nimport { ConfigStore } from 'fastly:config-store';\nimport { routes } from './routes.js';\nimport { env } from 'fastly:env';\n\nconst CONFIG_STORE_NAME = env('CONFIG_STORE_NAME');\n\nroutes.set('/config-store', () => {\n let config = new ConfigStore(CONFIG_STORE_NAME);\n let twitterValue = config.get('twitter');\n assert(\n twitterValue,\n 'https://twitter.com/fastly',\n `config.get(\"twitter\") === \"https://twitter.com/fastly\"`,\n );\n});\n", "/// \n/* eslint-env serviceworker, shared-node-browser, browser */\n\nimport {\n assert,\n assertThrows,\n assertRejects,\n assertResolves,\n} from './assertions.js';\nimport { routes } from './routes.js';\n\n// From https://www.rfc-editor.org/rfc/rfc7517#appendix-A.1\nconst publicRsaJsonWebKeyData = {\n alg: 'RS256',\n e: 'AQAB',\n ext: true,\n key_ops: ['verify'],\n kty: 'RSA',\n n: '0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw',\n};\n\n// From https://www.rfc-editor.org/rfc/rfc7517#appendix-A.1\nconst publicEcdsaJsonWebKeyData = {\n kty: 'EC',\n crv: 'P-256',\n x: 'MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4',\n y: '4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM',\n kid: '1',\n ext: true,\n key_ops: ['verify'],\n};\n\n// From https://www.rfc-editor.org/rfc/rfc7517#appendix-A.2\nconst privateEcdsaJsonWebKeyData = {\n kty: 'EC',\n crv: 'P-256',\n x: 'MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4',\n y: '4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM',\n d: '870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE',\n use: 'sig',\n kid: '1',\n ext: true,\n key_ops: ['sign'],\n};\n\n// From https://www.rfc-editor.org/rfc/rfc7517#appendix-A.2\nconst privateRsaJsonWebKeyData = {\n alg: 'RS256',\n d: 'X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q',\n dp: 'G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0',\n dq: 's9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk',\n e: 'AQAB',\n ext: true,\n key_ops: ['sign'],\n kty: 'RSA',\n n: '0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw',\n p: '83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs',\n q: '3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk',\n qi: 'GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU',\n};\n\nconst rsaJsonWebKeyAlgorithm = {\n name: 'RSASSA-PKCS1-v1_5',\n hash: { name: 'SHA-256' },\n};\n\nconst ecdsaJsonWebKeyAlgorithm = {\n name: 'ECDSA',\n namedCurve: 'P-256',\n hash: { name: 'SHA-256' },\n};\n\nroutes.set('/crypto', async () => {\n assert(typeof crypto, 'object', `typeof crypto`);\n assert(crypto instanceof Crypto, true, `crypto instanceof Crypto`);\n});\n\nroutes.set('/crypto.subtle', async () => {\n assert(typeof crypto.subtle, 'object', `typeof crypto.subtle`);\n assert(\n crypto.subtle instanceof SubtleCrypto,\n true,\n `crypto.subtle instanceof SubtleCrypto`,\n );\n});\n\n// importKey\n{\n routes.set('/crypto.subtle.importKey', async () => {\n assert(\n typeof crypto.subtle.importKey,\n 'function',\n `typeof crypto.subtle.importKey`,\n );\n assert(\n crypto.subtle.importKey,\n SubtleCrypto.prototype.importKey,\n `crypto.subtle.importKey === SubtleCrypto.prototype.importKey`,\n );\n });\n routes.set('/crypto.subtle.importKey/length', async () => {\n assert(\n crypto.subtle.importKey.length,\n 5,\n `crypto.subtle.importKey.length === 5`,\n );\n });\n routes.set('/crypto.subtle.importKey/called-as-constructor', async () => {\n assertThrows(\n () => {\n new crypto.subtle.importKey();\n },\n TypeError,\n 'crypto.subtle.importKey is not a constructor',\n );\n });\n routes.set('/crypto.subtle.importKey/called-with-wrong-this', async () => {\n await assertRejects(async () => {\n await crypto.subtle.importKey.call(\n undefined,\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n }, TypeError);\n });\n routes.set('/crypto.subtle.importKey/called-with-no-arguments', async () => {\n await assertRejects(\n async () => {\n await crypto.subtle.importKey();\n },\n TypeError,\n 'SubtleCrypto.importKey: At least 5 arguments required, but only 0 passed',\n );\n });\n\n // first-parameter\n {\n routes.set(\n '/crypto.subtle.importKey/first-parameter-calls-7.1.17-ToString',\n async () => {\n const sentinel = Symbol('sentinel');\n const test = async () => {\n const format = {\n toString() {\n throw sentinel;\n },\n };\n await crypto.subtle.importKey(\n format,\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.importKey/first-parameter-non-existant-format',\n async () => {\n await assertRejects(async () => {\n await crypto.subtle.importKey(\n 'jake',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n }, Error);\n },\n );\n }\n\n // second-parameter\n {\n routes.set(\n '/crypto.subtle.importKey/second-parameter-invalid-format',\n async () => {\n await assertRejects(async () => {\n await crypto.subtle.importKey(\n 'jwk',\n Symbol(),\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n }, Error);\n },\n );\n // jwk public key\n {\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-missing-e-field',\n async () => {\n await assertRejects(async () => {\n delete publicRsaJsonWebKeyData.e;\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n }, DOMException);\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-e-field-calls-7.1.17-ToString',\n async () => {\n let sentinel = Symbol('sentinel');\n const test = async () => {\n sentinel = Symbol();\n publicRsaJsonWebKeyData.e = {\n toString() {\n throw sentinel;\n },\n };\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-invalid-e-field',\n async () => {\n await assertRejects(\n async () => {\n publicRsaJsonWebKeyData.e = '`~!@#@#$Q%^%&^*';\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n },\n Error,\n \"The JWK member 'e' could not be base64url decoded or contained padding\",\n );\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-missing-kty-field',\n async () => {\n await assertRejects(async () => {\n delete publicRsaJsonWebKeyData.kty;\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n }, Error);\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-invalid-kty-field',\n async () => {\n await assertRejects(async () => {\n publicRsaJsonWebKeyData.kty = 'jake';\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n }, Error);\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-missing-key_ops-field',\n async () => {\n await assertResolves(async () => {\n const key_ops = Array.from(publicRsaJsonWebKeyData.key_ops);\n delete publicRsaJsonWebKeyData.key_ops;\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n key_ops,\n );\n });\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-non-sequence-key_ops-field',\n async () => {\n await assertRejects(async () => {\n const key_ops = Array.from(publicRsaJsonWebKeyData.key_ops);\n publicRsaJsonWebKeyData.key_ops = 'jake';\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n key_ops,\n );\n }, Error);\n },\n );\n\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-empty-key_ops-field',\n async () => {\n await assertResolves(async () => {\n const key_ops = Array.from(publicRsaJsonWebKeyData.key_ops);\n publicRsaJsonWebKeyData.key_ops = [];\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n key_ops,\n );\n });\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-duplicated-key_ops-field',\n async () => {\n await assertRejects(async () => {\n const key_ops = Array.from(publicRsaJsonWebKeyData.key_ops);\n publicRsaJsonWebKeyData.key_ops = ['sign', 'sign'];\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n key_ops,\n );\n }, Error);\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-invalid-key_ops-field',\n async () => {\n await assertRejects(async () => {\n const key_ops = Array.from(publicRsaJsonWebKeyData.key_ops);\n publicRsaJsonWebKeyData.key_ops = ['sign', 'jake'];\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n key_ops,\n );\n }, Error);\n },\n );\n\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-key_ops-field-calls-7.1.17-ToString',\n async () => {\n let sentinel = Symbol('sentinel');\n const key_ops = Array.from(publicRsaJsonWebKeyData.key_ops);\n const test = async () => {\n sentinel = Symbol();\n const op = {\n toString() {\n throw sentinel;\n },\n };\n publicRsaJsonWebKeyData.key_ops = ['sign', op];\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-missing-n-field',\n async () => {\n await assertRejects(\n async () => {\n delete publicRsaJsonWebKeyData.n;\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n },\n Error,\n 'Data provided to an operation does not meet requirements',\n );\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-n-field-calls-7.1.17-ToString',\n async () => {\n let sentinel = Symbol('sentinel');\n const test = async () => {\n sentinel = Symbol();\n publicRsaJsonWebKeyData.n = {\n toString() {\n throw sentinel;\n },\n };\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.importKey/rsa-jwk-public/second-parameter-invalid-n-field',\n async () => {\n await assertRejects(\n async () => {\n publicRsaJsonWebKeyData.n = '`~!@#@#$Q%^%&^*';\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n },\n Error,\n \"The JWK member 'n' could not be base64url decoded or contained padding\",\n );\n },\n );\n }\n // jwk private key\n // TODO\n // raw HMAC secret keys\n // TODO\n // raw Elliptic Curve public keys\n {\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-missing-x-field',\n async () => {\n await assertRejects(\n async () => {\n delete publicEcdsaJsonWebKeyData.x;\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n },\n DOMException,\n 'Data provided to an operation does not meet requirements',\n );\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-x-field-calls-7.1.17-ToString',\n async () => {\n let sentinel = Symbol('sentinel');\n const test = async () => {\n sentinel = Symbol();\n publicEcdsaJsonWebKeyData.x = {\n toString() {\n throw sentinel;\n },\n };\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-invalid-x-field',\n async () => {\n await assertRejects(\n async () => {\n publicEcdsaJsonWebKeyData.x = '`~!@#@#$Q%^%&^*';\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n },\n Error,\n \"The JWK member 'x' could not be base64url decoded or contained padding\",\n );\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-missing-y-field',\n async () => {\n await assertRejects(\n async () => {\n delete publicEcdsaJsonWebKeyData.y;\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n },\n DOMException,\n 'Data provided to an operation does not meet requirements',\n );\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-y-field-calls-7.1.17-ToString',\n async () => {\n let sentinel = Symbol('sentinel');\n const test = async () => {\n sentinel = Symbol();\n publicEcdsaJsonWebKeyData.y = {\n toString() {\n throw sentinel;\n },\n };\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-invalid-y-field',\n async () => {\n await assertRejects(\n async () => {\n publicEcdsaJsonWebKeyData.y = '`~!@#@#$Q%^%&^*';\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n },\n Error,\n \"The JWK member 'y' could not be base64url decoded or contained padding\",\n );\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-missing-kty-field',\n async () => {\n await assertRejects(async () => {\n delete publicEcdsaJsonWebKeyData.kty;\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n }, Error);\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-invalid-kty-field',\n async () => {\n await assertRejects(async () => {\n publicEcdsaJsonWebKeyData.kty = 'jake';\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n }, Error);\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-missing-key_ops-field',\n async () => {\n await assertResolves(async () => {\n const key_ops = Array.from(publicEcdsaJsonWebKeyData.key_ops);\n delete publicEcdsaJsonWebKeyData.key_ops;\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n key_ops,\n );\n });\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-non-sequence-key_ops-field',\n async () => {\n await assertRejects(async () => {\n const key_ops = Array.from(publicEcdsaJsonWebKeyData.key_ops);\n publicEcdsaJsonWebKeyData.key_ops = 'jake';\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n key_ops,\n );\n }, Error);\n },\n );\n\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-empty-key_ops-field',\n async () => {\n await assertResolves(async () => {\n const key_ops = Array.from(publicEcdsaJsonWebKeyData.key_ops);\n publicEcdsaJsonWebKeyData.key_ops = [];\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n key_ops,\n );\n });\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-duplicated-key_ops-field',\n async () => {\n await assertRejects(async () => {\n const key_ops = Array.from(publicEcdsaJsonWebKeyData.key_ops);\n publicEcdsaJsonWebKeyData.key_ops = ['sign', 'sign'];\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n key_ops,\n );\n }, Error);\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-invalid-key_ops-field',\n async () => {\n await assertRejects(async () => {\n const key_ops = Array.from(publicEcdsaJsonWebKeyData.key_ops);\n publicEcdsaJsonWebKeyData.key_ops = ['sign', 'jake'];\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n key_ops,\n );\n }, Error);\n },\n );\n\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-public/second-parameter-key_ops-field-calls-7.1.17-ToString',\n async () => {\n let sentinel = Symbol('sentinel');\n const key_ops = Array.from(publicEcdsaJsonWebKeyData.key_ops);\n const test = async () => {\n sentinel = Symbol();\n const op = {\n toString() {\n throw sentinel;\n },\n };\n publicEcdsaJsonWebKeyData.key_ops = ['sign', op];\n await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-private/second-parameter-d-field-calls-7.1.17-ToString',\n async () => {\n let sentinel = Symbol('sentinel');\n const test = async () => {\n sentinel = Symbol();\n privateEcdsaJsonWebKeyData.d = {\n toString() {\n throw sentinel;\n },\n };\n await crypto.subtle.importKey(\n 'jwk',\n privateEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n privateEcdsaJsonWebKeyData.ext,\n privateEcdsaJsonWebKeyData.key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n console.log(\n thrownError,\n typeof thrownError,\n Object.keys(thrownError),\n );\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.importKey/ecdsa-jwk-private/second-parameter-invalid-d-field',\n async () => {\n await assertRejects(\n async () => {\n privateEcdsaJsonWebKeyData.d = '`~!@#@#$Q%^%&^*';\n await crypto.subtle.importKey(\n 'jwk',\n privateEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n privateEcdsaJsonWebKeyData.ext,\n privateEcdsaJsonWebKeyData.key_ops,\n );\n },\n Error,\n \"The JWK member 'd' could not be base64url decoded or contained padding\",\n );\n },\n );\n }\n // pkcs8 Elliptic Curve private keys\n // TODO\n // pkcs8 RSA private keys\n // raw AES\n }\n // third-parameter\n {\n routes.set(\n '/crypto.subtle.importKey/third-parameter-undefined',\n async () => {\n await assertRejects(\n async () => {\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n undefined,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n },\n Error,\n 'Algorithm: Unrecognized name',\n );\n },\n );\n routes.set(\n '/crypto.subtle.importKey/third-parameter-name-field-calls-7.1.17-ToString',\n async () => {\n const sentinel = Symbol('sentinel');\n const test = async () => {\n rsaJsonWebKeyAlgorithm.name = {\n toString() {\n throw sentinel;\n },\n };\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.importKey/third-parameter-invalid-name-field',\n async () => {\n await assertRejects(async () => {\n rsaJsonWebKeyAlgorithm.name = '`~!@#@#$Q%^%&^*';\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n }, Error);\n },\n );\n routes.set(\n '/crypto.subtle.importKey/third-parameter-hash-name-field-calls-7.1.17-ToString',\n async () => {\n const sentinel = Symbol('sentinel');\n const test = async () => {\n rsaJsonWebKeyAlgorithm.hash.name = {\n toString() {\n throw sentinel;\n },\n };\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.importKey/third-parameter-hash-algorithm-does-not-match-json-web-key-hash-algorithm',\n async () => {\n await assertRejects(\n async () => {\n rsaJsonWebKeyAlgorithm.hash.name = 'SHA-1';\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n },\n Error,\n \"The JWK 'alg' member was inconsistent with that specified by the Web Crypto call\",\n );\n },\n );\n }\n\n // fifth-parameter\n {\n routes.set(\n '/crypto.subtle.importKey/fifth-parameter-undefined',\n async () => {\n await assertRejects(async () => {\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n undefined,\n );\n }, Error);\n },\n );\n routes.set('/crypto.subtle.importKey/fifth-parameter-invalid', async () => {\n await assertRejects(async () => {\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n ['jake'],\n );\n }, Error);\n });\n routes.set(\n '/crypto.subtle.importKey/fifth-parameter-duplicate-operations',\n async () => {\n await assertResolves(async () => {\n const key_ops = publicRsaJsonWebKeyData.key_ops.concat(\n publicRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n key_ops,\n );\n });\n },\n );\n\n routes.set(\n '/crypto.subtle.importKey/fifth-parameter-operations-do-not-match-json-web-key-operations',\n async () => {\n await assertRejects(\n async () => {\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n ['sign'],\n );\n },\n Error,\n \"The JWK 'key_ops' member was inconsistent with that specified by the Web Crypto call. The JWK usage must be a superset of those requested\",\n );\n },\n );\n\n routes.set(\n '/crypto.subtle.importKey/fifth-parameter-operation-fields-calls-7.1.17-ToString',\n async () => {\n let sentinel = Symbol('sentinel');\n const test = async () => {\n sentinel = Symbol();\n const op = {\n toString() {\n throw sentinel;\n },\n };\n await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n ['sign', op],\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n }\n\n // happy paths\n {\n routes.set('/crypto.subtle.importKey/JWK-RS256-Public', async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n await assert(key instanceof CryptoKey, true, `key instanceof CryptoKey`);\n await assert(\n key.algorithm,\n {\n name: 'RSASSA-PKCS1-v1_5',\n hash: {\n name: 'SHA-256',\n },\n modulusLength: 2048,\n publicExponent: new Uint8Array([1, 0, 1]),\n },\n `key.algorithm`,\n );\n await assert(key.extractable, true, `key.extractable === true`);\n await assert(key.type, 'public', `key.type === \"public\"`);\n await assert(key.usages, ['verify'], `key.usages deep equals [\"verify\"]`);\n });\n\n routes.set('/crypto.subtle.importKey/JWK-EC256-Public', async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n await assert(key instanceof CryptoKey, true, `key instanceof CryptoKey`);\n await assert(\n key.algorithm,\n {\n name: 'ECDSA',\n namedCurve: 'P-256',\n },\n `key.algorithm`,\n );\n await assert(key.extractable, true, `key.extractable === true`);\n await assert(key.type, 'public', `key.type === \"public\"`);\n await assert(key.usages, ['verify'], `key.usages deep equals [\"verify\"]`);\n });\n\n routes.set('/crypto.subtle.importKey/HMAC', async () => {\n const keyUint8Array = new Uint8Array([1, 0, 1]);\n\n for (const algorithm of ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']) {\n const key = await globalThis.crypto.subtle.importKey(\n 'raw',\n keyUint8Array,\n { name: 'HMAC', hash: algorithm },\n false,\n ['sign', 'verify'],\n );\n await assert(\n key instanceof CryptoKey,\n true,\n `key instanceof CryptoKey`,\n );\n await assert(\n key.algorithm,\n {\n name: 'HMAC',\n hash: { name: algorithm },\n length: 24,\n },\n `key.algorithm`,\n );\n await assert(key.extractable, false, `key.extractable`);\n await assert(key.type, 'secret', `key.type`);\n await assert(key.usages, ['sign', 'verify'], `key.usages`);\n }\n });\n routes.set('/crypto.subtle.importKey/JWK-HS256-Public', async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n {\n kty: 'oct',\n k: 'Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE',\n alg: 'HS256',\n ext: true,\n },\n {\n name: 'HMAC',\n hash: { name: 'SHA-256' },\n },\n false,\n ['sign', 'verify'],\n );\n await assert(key instanceof CryptoKey, true, `key instanceof CryptoKey`);\n await assert(\n key.algorithm,\n {\n name: 'HMAC',\n hash: { name: 'SHA-256' },\n length: 256,\n },\n `key.algorithm`,\n );\n await assert(key.extractable, false, `key.extractable`);\n await assert(key.type, 'secret', `key.type`);\n await assert(key.usages, ['sign', 'verify'], `key.usages`);\n });\n }\n}\n\n// digest\n{\n const enc = new TextEncoder();\n const data = enc.encode('hello world');\n routes.set('/crypto.subtle.digest', async () => {\n assert(\n typeof crypto.subtle.digest,\n 'function',\n `typeof crypto.subtle.digest`,\n );\n assert(\n crypto.subtle.digest,\n SubtleCrypto.prototype.digest,\n `crypto.subtle.digest === SubtleCrypto.prototype.digest`,\n );\n });\n routes.set('/crypto.subtle.digest/length', async () => {\n assert(crypto.subtle.digest.length, 2, `crypto.subtle.digest.length === 2`);\n });\n routes.set('/crypto.subtle.digest/called-as-constructor', async () => {\n assertThrows(\n () => {\n new crypto.subtle.digest();\n },\n TypeError,\n 'crypto.subtle.digest is not a constructor',\n );\n });\n routes.set('/crypto.subtle.digest/called-with-wrong-this', async () => {\n await assertRejects(async () => {\n await crypto.subtle.digest.call(undefined);\n }, TypeError);\n });\n routes.set('/crypto.subtle.digest/called-with-no-arguments', async () => {\n await assertRejects(\n async () => {\n await crypto.subtle.digest();\n },\n TypeError,\n 'SubtleCrypto.digest: At least 2 arguments required, but only 0 passed',\n );\n });\n\n // first-parameter\n {\n routes.set(\n '/crypto.subtle.digest/first-parameter-calls-7.1.17-ToString',\n async () => {\n const sentinel = Symbol('sentinel');\n const test = async () => {\n await crypto.subtle.digest(\n {\n name: {\n toString() {\n throw sentinel;\n },\n },\n },\n data,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.digest/first-parameter-non-existant-format',\n async () => {\n await assertRejects(\n async () => {\n await crypto.subtle.digest('jake', data);\n },\n Error,\n 'Algorithm: Unrecognized name',\n );\n },\n );\n }\n // second-parameter\n {\n routes.set('/crypto.subtle.digest/second-parameter-undefined', async () => {\n await assertRejects(async () => {\n await crypto.subtle.digest('sha-1', undefined);\n }, Error);\n });\n }\n // happy paths\n {\n // \"MD5\"\n routes.set('/crypto.subtle.digest/md5', async () => {\n const result = new Uint8Array(\n await crypto.subtle.digest('md5', new Uint8Array()),\n );\n const expected = new Uint8Array([\n 212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126,\n ]);\n assert(result, expected, 'result deep equals expected');\n });\n // \"SHA-1\"\n routes.set('/crypto.subtle.digest/sha-1', async () => {\n const result = new Uint8Array(\n await crypto.subtle.digest('sha-1', new Uint8Array()),\n );\n const expected = new Uint8Array([\n 218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144,\n 175, 216, 7, 9,\n ]);\n assert(result, expected, 'result deep equals expected');\n });\n // \"SHA-256\"\n routes.set('/crypto.subtle.digest/sha-256', async () => {\n const result = new Uint8Array(\n await crypto.subtle.digest('sha-256', new Uint8Array()),\n );\n const expected = new Uint8Array([\n 227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185,\n 36, 39, 174, 65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82,\n 184, 85,\n ]);\n assert(result, expected, 'result deep equals expected');\n });\n // \"SHA-384\"\n routes.set('/crypto.subtle.digest/sha-384', async () => {\n const result = new Uint8Array(\n await crypto.subtle.digest('sha-384', new Uint8Array()),\n );\n const expected = new Uint8Array([\n 56, 176, 96, 167, 81, 172, 150, 56, 76, 217, 50, 126, 177, 177, 227,\n 106, 33, 253, 183, 17, 20, 190, 7, 67, 76, 12, 199, 191, 99, 246, 225,\n 218, 39, 78, 222, 191, 231, 111, 101, 251, 213, 26, 210, 241, 72, 152,\n 185, 91,\n ]);\n assert(result, expected, 'result deep equals expected');\n });\n // \"SHA-512\"\n routes.set('/crypto.subtle.digest/sha-512', async () => {\n const result = new Uint8Array(\n await crypto.subtle.digest('sha-512', new Uint8Array()),\n );\n const expected = new Uint8Array([\n 207, 131, 225, 53, 126, 239, 184, 189, 241, 84, 40, 80, 214, 109, 128,\n 7, 214, 32, 228, 5, 11, 87, 21, 220, 131, 244, 169, 33, 211, 108, 233,\n 206, 71, 208, 209, 60, 93, 133, 242, 176, 255, 131, 24, 210, 135, 126,\n 236, 47, 99, 185, 49, 189, 71, 65, 122, 129, 165, 56, 50, 122, 249, 39,\n 218, 62,\n ]);\n assert(result, expected, 'result deep equals expected');\n });\n }\n}\n\n// sign\n{\n const enc = new TextEncoder();\n const data = enc.encode('hello world');\n routes.set('/crypto.subtle.sign', async () => {\n assert(typeof crypto.subtle.sign, 'function', `typeof crypto.subtle.sign`);\n assert(\n crypto.subtle.sign,\n SubtleCrypto.prototype.sign,\n `crypto.subtle.sign === SubtleCrypto.prototype.sign`,\n );\n });\n routes.set('/crypto.subtle.sign/length', async () => {\n assert(crypto.subtle.sign.length, 3, `crypto.subtle.sign.length === 3`);\n });\n routes.set('/crypto.subtle.sign/called-as-constructor', async () => {\n assertThrows(\n () => {\n new crypto.subtle.sign();\n },\n TypeError,\n 'crypto.subtle.sign is not a constructor',\n );\n });\n routes.set('/crypto.subtle.sign/called-with-wrong-this', async () => {\n await assertRejects(async () => {\n await crypto.subtle.sign.call(\n undefined,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData,\n data,\n );\n }, TypeError);\n });\n routes.set('/crypto.subtle.sign/called-with-no-arguments', async () => {\n await assertRejects(\n async () => {\n await crypto.subtle.sign();\n },\n TypeError,\n 'SubtleCrypto.sign: At least 3 arguments required, but only 0 passed',\n );\n });\n // first-parameter\n {\n routes.set(\n '/crypto.subtle.sign/first-parameter-calls-7.1.17-ToString',\n async () => {\n const sentinel = Symbol('sentinel');\n const key = await crypto.subtle.importKey(\n 'jwk',\n privateRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n privateRsaJsonWebKeyData.ext,\n privateRsaJsonWebKeyData.key_ops,\n );\n const test = async () => {\n await crypto.subtle.sign(\n {\n name: {\n toString() {\n throw sentinel;\n },\n },\n },\n key,\n data,\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.sign/first-parameter-non-existant-algorithm',\n async () => {\n await assertRejects(\n async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n privateRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n privateRsaJsonWebKeyData.ext,\n privateRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.sign('jake', key, data);\n },\n Error,\n 'Algorithm: Unrecognized name',\n );\n },\n );\n }\n // second-parameter\n {\n routes.set(\n '/crypto.subtle.sign/second-parameter-invalid-format',\n async () => {\n await assertRejects(async () => {\n await crypto.subtle.sign(rsaJsonWebKeyAlgorithm, 'jake', data);\n }, Error);\n },\n );\n routes.set(\n '/crypto.subtle.sign/second-parameter-invalid-usages',\n async () => {\n await assertRejects(\n async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.sign(rsaJsonWebKeyAlgorithm, key, data);\n },\n Error,\n \"CryptoKey doesn't support signing\",\n );\n },\n );\n }\n // third-parameter\n {\n routes.set(\n '/crypto.subtle.sign/third-parameter-invalid-format',\n async () => {\n await assertRejects(async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.sign(rsaJsonWebKeyAlgorithm, key, undefined);\n }, Error);\n },\n );\n }\n // happy-path\n {\n routes.set('/crypto.subtle.sign/happy-path-jwk', async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n privateRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n privateRsaJsonWebKeyData.ext,\n privateRsaJsonWebKeyData.key_ops,\n );\n const signature = new Uint8Array(\n await crypto.subtle.sign(rsaJsonWebKeyAlgorithm, key, data),\n );\n const expected = new Uint8Array([\n 70, 96, 33, 185, 93, 42, 67, 49, 243, 70, 88, 68, 194, 148, 53, 249,\n 255, 192, 232, 132, 161, 194, 41, 244, 174, 211, 218, 203, 7, 238, 71,\n 182, 101, 49, 139, 222, 165, 70, 222, 105, 82, 156, 184, 44, 100, 108,\n 121, 237, 250, 119, 66, 228, 156, 243, 71, 105, 62, 246, 22, 2, 160,\n 116, 71, 147, 202, 168, 24, 92, 224, 41, 148, 161, 124, 80, 212, 169,\n 212, 64, 29, 189, 2, 171, 174, 188, 159, 89, 93, 122, 219, 166, 105, 92,\n 107, 173, 103, 238, 145, 226, 94, 139, 71, 124, 17, 233, 49, 138, 89,\n 246, 3, 82, 238, 154, 169, 188, 66, 198, 32, 23, 230, 90, 164, 140, 51,\n 47, 221, 149, 161, 14, 254, 169, 224, 223, 119, 94, 27, 63, 199, 93, 65,\n 53, 24, 151, 146, 242, 239, 41, 108, 136, 31, 99, 42, 213, 128, 244,\n 140, 238, 157, 107, 117, 241, 219, 137, 97, 39, 109, 185, 176, 97, 193,\n 60, 117, 244, 106, 62, 193, 188, 87, 199, 37, 70, 137, 37, 231, 110,\n 228, 228, 139, 53, 240, 56, 92, 102, 220, 176, 127, 248, 24, 217, 208,\n 29, 209, 216, 29, 251, 100, 252, 243, 183, 195, 96, 126, 102, 136, 48,\n 39, 186, 45, 202, 10, 187, 22, 52, 183, 190, 149, 153, 32, 12, 90, 66,\n 49, 122, 190, 154, 167, 9, 12, 32, 77, 177, 222, 54, 211, 233, 219, 205,\n 133, 0, 113, 77, 158, 1, 125, 5, 15, 195,\n ]);\n assert(signature, expected, 'signature deep equals expected');\n });\n routes.set('/crypto.subtle.sign/happy-path-hmac', async () => {\n const encoder = new TextEncoder();\n const messageUint8Array = encoder.encode('aki');\n const keyUint8Array = new Uint8Array([1, 0, 1]);\n const results = {\n 'SHA-1': new Uint8Array([\n 222, 61, 81, 133, 232, 89, 130, 225, 248, 25, 220, 34, 245, 103, 89,\n 127, 136, 77, 146, 166,\n ]),\n 'SHA-256': new Uint8Array([\n 92, 237, 16, 210, 91, 89, 194, 36, 95, 98, 27, 175, 64, 25, 15, 160,\n 152, 178, 145, 235, 62, 92, 23, 202, 125, 228, 8, 25, 148, 26, 215,\n 242,\n ]),\n 'SHA-384': new Uint8Array([\n 238, 20, 74, 173, 238, 236, 161, 229, 250, 167, 72, 210, 188, 239,\n 233, 39, 233, 166, 114, 241, 140, 229, 201, 129, 243, 173, 74, 198,\n 223, 145, 228, 96, 253, 91, 166, 111, 244, 23, 141, 62, 112, 156, 90,\n 166, 214, 69, 185, 48,\n ]),\n 'SHA-512': new Uint8Array([\n 211, 127, 139, 149, 23, 225, 84, 230, 82, 249, 109, 254, 168, 236,\n 217, 112, 174, 52, 231, 62, 167, 197, 33, 11, 181, 21, 162, 236, 214,\n 132, 43, 161, 92, 112, 230, 182, 140, 69, 169, 229, 87, 98, 57, 81,\n 140, 134, 219, 253, 139, 169, 85, 181, 195, 195, 166, 241, 219, 33, 9,\n 56, 67, 213, 51, 224,\n ]),\n };\n\n for (const algorithm of ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']) {\n const key = await globalThis.crypto.subtle.importKey(\n 'raw',\n keyUint8Array,\n { name: 'HMAC', hash: algorithm },\n false,\n ['sign', 'verify'],\n );\n // Sign the message with HMAC and the CryptoKey\n const signature = new Uint8Array(\n await globalThis.crypto.subtle.sign('HMAC', key, messageUint8Array),\n );\n const expected = results[algorithm];\n assert(\n signature,\n expected,\n `${algorithm} signature deep equals expected`,\n );\n }\n });\n }\n}\n\n// verify\n{\n routes.set('/crypto.subtle.verify', async () => {\n assert(\n typeof crypto.subtle.verify,\n 'function',\n `typeof crypto.subtle.verify`,\n );\n assert(\n crypto.subtle.verify,\n SubtleCrypto.prototype.verify,\n `crypto.subtle.verify === SubtleCrypto.prototype.verify`,\n );\n });\n routes.set('/crypto.subtle.verify/length', async () => {\n assert(crypto.subtle.verify.length, 4, `crypto.subtle.verify.length === 4`);\n });\n routes.set('/crypto.subtle.verify/called-as-constructor', async () => {\n assertThrows(\n () => {\n new crypto.subtle.verify();\n },\n TypeError,\n 'crypto.subtle.verify is not a constructor',\n );\n });\n routes.set('/crypto.subtle.verify/called-with-wrong-this', async () => {\n await assertRejects(async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.verify.call(\n undefined,\n rsaJsonWebKeyAlgorithm,\n key,\n new Uint8Array(),\n new Uint8Array(),\n );\n }, TypeError);\n });\n routes.set('/crypto.subtle.verify/called-with-no-arguments', async () => {\n await assertRejects(\n async () => {\n await crypto.subtle.verify();\n },\n TypeError,\n 'SubtleCrypto.verify: At least 4 arguments required, but only 0 passed',\n );\n });\n // first-parameter\n {\n routes.set(\n '/crypto.subtle.verify/first-parameter-calls-7.1.17-ToString',\n async () => {\n const sentinel = Symbol('sentinel');\n const test = async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.verify(\n {\n name: {\n toString() {\n throw sentinel;\n },\n },\n },\n key,\n new Uint8Array(),\n new Uint8Array(),\n );\n };\n await assertRejects(test);\n try {\n await test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n },\n );\n routes.set(\n '/crypto.subtle.verify/first-parameter-non-existant-algorithm',\n async () => {\n await assertRejects(\n async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.verify(\n 'jake',\n key,\n new Uint8Array(),\n new Uint8Array(),\n );\n },\n Error,\n 'Algorithm: Unrecognized name',\n );\n },\n );\n }\n // second-parameter\n {\n routes.set(\n '/crypto.subtle.verify/second-parameter-invalid-format',\n async () => {\n await assertRejects(async () => {\n await crypto.subtle.verify(\n rsaJsonWebKeyAlgorithm,\n 'jake',\n new Uint8Array(),\n new Uint8Array(),\n );\n }, Error);\n },\n );\n routes.set(\n '/crypto.subtle.verify/second-parameter-invalid-usages',\n async () => {\n await assertRejects(\n async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n privateRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n privateRsaJsonWebKeyData.ext,\n privateRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.verify(\n rsaJsonWebKeyAlgorithm,\n key,\n new Uint8Array(),\n new Uint8Array(),\n );\n },\n Error,\n \"CryptoKey doesn't support verification\",\n );\n },\n );\n }\n // third-parameter\n {\n routes.set(\n '/crypto.subtle.verify/third-parameter-invalid-format',\n async () => {\n await assertRejects(async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.verify(\n rsaJsonWebKeyAlgorithm,\n key,\n undefined,\n new Uint8Array(),\n );\n }, Error);\n },\n );\n }\n // fourth-parameter\n {\n routes.set(\n '/crypto.subtle.verify/fourth-parameter-invalid-format',\n async () => {\n await assertRejects(async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n await crypto.subtle.verify(\n rsaJsonWebKeyAlgorithm,\n key,\n new Uint8Array(),\n undefined,\n );\n }, Error);\n },\n );\n }\n // incorrect-signature\n {\n routes.set('/crypto.subtle.verify/incorrect-signature-jwk', async () => {\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n const signature = new Uint8Array();\n const enc = new TextEncoder();\n const data = enc.encode('hello world');\n const result = await crypto.subtle.verify(\n rsaJsonWebKeyAlgorithm,\n key,\n signature,\n data,\n );\n assert(result, false, 'result === false');\n });\n routes.set('/crypto.subtle.verify/incorrect-signature-hmac', async () => {\n const keyUint8Array = new Uint8Array([1, 0, 1]);\n const signature = new Uint8Array();\n const enc = new TextEncoder();\n const data = enc.encode('hello world');\n\n for (const algorithm of ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']) {\n const key = await globalThis.crypto.subtle.importKey(\n 'raw',\n keyUint8Array,\n { name: 'HMAC', hash: algorithm },\n false,\n ['sign', 'verify'],\n );\n const result = await crypto.subtle.verify('HMAC', key, signature, data);\n assert(result, false, 'result');\n }\n });\n }\n // correct-signature\n {\n routes.set('/crypto.subtle.verify/correct-signature-jwk-rsa', async () => {\n const pkey = await crypto.subtle.importKey(\n 'jwk',\n privateRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n privateRsaJsonWebKeyData.ext,\n privateRsaJsonWebKeyData.key_ops,\n );\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicRsaJsonWebKeyData,\n rsaJsonWebKeyAlgorithm,\n publicRsaJsonWebKeyData.ext,\n publicRsaJsonWebKeyData.key_ops,\n );\n const enc = new TextEncoder();\n const data = enc.encode('hello world');\n const signature = await crypto.subtle.sign(\n rsaJsonWebKeyAlgorithm,\n pkey,\n data,\n );\n const result = await crypto.subtle.verify(\n rsaJsonWebKeyAlgorithm,\n key,\n signature,\n data,\n );\n assert(result, true, 'result === true');\n });\n routes.set(\n '/crypto.subtle.verify/correct-signature-jwk-ecdsa',\n async () => {\n const pkey = await crypto.subtle.importKey(\n 'jwk',\n privateEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n privateEcdsaJsonWebKeyData.ext,\n privateEcdsaJsonWebKeyData.key_ops,\n );\n const key = await crypto.subtle.importKey(\n 'jwk',\n publicEcdsaJsonWebKeyData,\n ecdsaJsonWebKeyAlgorithm,\n publicEcdsaJsonWebKeyData.ext,\n publicEcdsaJsonWebKeyData.key_ops,\n );\n const enc = new TextEncoder();\n const data = enc.encode('hello world');\n const signature = await crypto.subtle.sign(\n ecdsaJsonWebKeyAlgorithm,\n pkey,\n data,\n );\n const result = await crypto.subtle.verify(\n ecdsaJsonWebKeyAlgorithm,\n key,\n signature,\n data,\n );\n assert(result, true, 'result === true');\n },\n );\n routes.set('/crypto.subtle.verify/correct-signature-hmac', async () => {\n const results = {\n 'SHA-1': new Uint8Array([\n 222, 61, 81, 133, 232, 89, 130, 225, 248, 25, 220, 34, 245, 103, 89,\n 127, 136, 77, 146, 166,\n ]),\n 'SHA-256': new Uint8Array([\n 92, 237, 16, 210, 91, 89, 194, 36, 95, 98, 27, 175, 64, 25, 15, 160,\n 152, 178, 145, 235, 62, 92, 23, 202, 125, 228, 8, 25, 148, 26, 215,\n 242,\n ]),\n 'SHA-384': new Uint8Array([\n 238, 20, 74, 173, 238, 236, 161, 229, 250, 167, 72, 210, 188, 239,\n 233, 39, 233, 166, 114, 241, 140, 229, 201, 129, 243, 173, 74, 198,\n 223, 145, 228, 96, 253, 91, 166, 111, 244, 23, 141, 62, 112, 156, 90,\n 166, 214, 69, 185, 48,\n ]),\n 'SHA-512': new Uint8Array([\n 211, 127, 139, 149, 23, 225, 84, 230, 82, 249, 109, 254, 168, 236,\n 217, 112, 174, 52, 231, 62, 167, 197, 33, 11, 181, 21, 162, 236, 214,\n 132, 43, 161, 92, 112, 230, 182, 140, 69, 169, 229, 87, 98, 57, 81,\n 140, 134, 219, 253, 139, 169, 85, 181, 195, 195, 166, 241, 219, 33, 9,\n 56, 67, 213, 51, 224,\n ]),\n };\n const encoder = new TextEncoder();\n const messageUint8Array = encoder.encode('aki');\n const keyUint8Array = new Uint8Array([1, 0, 1]);\n\n for (const algorithm of ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']) {\n const key = await globalThis.crypto.subtle.importKey(\n 'raw',\n keyUint8Array,\n { name: 'HMAC', hash: algorithm },\n false,\n ['sign', 'verify'],\n );\n // Sign the message with HMAC and the CryptoKey\n // const signature = new Uint8Array(await globalThis.crypto.subtle.sign(\"HMAC\", key, messageUint8Array));\n const signature = results[algorithm];\n const result = await crypto.subtle.verify(\n 'HMAC',\n key,\n signature,\n messageUint8Array,\n );\n assert(result, true, 'result');\n }\n });\n }\n}\n", "export const Device = globalThis.Device;", "/// \n/* eslint-env serviceworker */\n\nimport { assert, assertThrows } from './assertions.js';\nimport { Device } from 'fastly:device';\nimport { routes } from './routes.js';\n\nroutes.set('/device/interface', () => {\n let actual = Reflect.ownKeys(Device);\n let expected = ['prototype', 'lookup', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(Device)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(Device, 'prototype');\n expected = {\n value: Device.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(Device, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(Device, 'name');\n expected = {\n value: 'Device',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(Device.prototype);\n expected = [\n 'constructor',\n 'name',\n 'brand',\n 'model',\n 'hardwareType',\n 'isDesktop',\n 'isGameConsole',\n 'isMediaPlayer',\n 'isMobile',\n 'isSmartTV',\n 'isTablet',\n 'isTouchscreen',\n 'toJSON',\n Symbol.toStringTag,\n ];\n assert(actual, expected, `Reflect.ownKeys(Device.prototype)`);\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(Device.prototype, 'constructor');\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: Device.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device.prototype, 'constructor')`,\n );\n\n assert(\n typeof Device.prototype.constructor,\n 'function',\n `typeof Device.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n Device.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n Device.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'Device',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n Device.prototype,\n Symbol.toStringTag,\n );\n expected = {\n writable: false,\n enumerable: false,\n configurable: true,\n value: 'Device',\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof Device.prototype[Symbol.toStringTag],\n 'string',\n `typeof Device.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the lookup static method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(Device, 'lookup');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: Device.lookup,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device, 'lookup')`,\n );\n\n assert(typeof Device.lookup, 'function', `typeof Device.lookup`);\n\n actual = Reflect.getOwnPropertyDescriptor(Device.lookup, 'length');\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device.lookup, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(Device.lookup, 'name');\n expected = {\n value: 'lookup',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Device.lookup, 'name')`,\n );\n }\n\n for (let property of [\n 'name',\n 'brand',\n 'model',\n 'hardwareType',\n 'isDesktop',\n 'isGameConsole',\n 'isMediaPlayer',\n 'isMobile',\n 'isSmartTV',\n 'isTablet',\n 'isTouchscreen',\n ]) {\n const descriptors = Reflect.getOwnPropertyDescriptor(\n Device.prototype,\n property,\n );\n expected = { enumerable: true, configurable: true };\n assert(\n descriptors.enumerable,\n true,\n `Reflect.getOwnPropertyDescriptor(Device, '${property}').enumerable`,\n );\n assert(\n descriptors.configurable,\n true,\n `Reflect.getOwnPropertyDescriptor(Device, '${property}').configurable`,\n );\n assert(\n descriptors.value,\n undefined,\n `Reflect.getOwnPropertyDescriptor(Device, '${property}').value`,\n );\n assert(\n descriptors.set,\n undefined,\n `Reflect.getOwnPropertyDescriptor(Device, '${property}').set`,\n );\n assert(\n typeof descriptors.get,\n 'function',\n `typeof Reflect.getOwnPropertyDescriptor(Device, '${property}').get`,\n );\n }\n});\n\n// Device constructor\n{\n routes.set('/device/constructor/called-as-regular-function', () => {\n assertThrows(() => {\n Device();\n }, TypeError);\n });\n routes.set('/device/constructor/throws', () => {\n assertThrows(() => new Device(), TypeError);\n });\n}\n\n// Device lookup static method\n// static lookup(useragent: string): DeviceEntry | null;\n{\n routes.set('/device/lookup/called-as-constructor', () => {\n assertThrows(() => {\n new Device.lookup('1');\n }, TypeError);\n });\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/device/lookup/useragent-parameter-calls-7.1.17-ToString', () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const useragent = {\n toString() {\n throw sentinel;\n },\n };\n Device.lookup(useragent);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n Device.lookup(Symbol());\n },\n TypeError,\n `can't convert symbol to string`,\n );\n });\n routes.set('/device/lookup/useragent-parameter-not-supplied', () => {\n assertThrows(\n () => {\n Device.lookup();\n },\n TypeError,\n `Device.lookup: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/device/lookup/useragent-parameter-empty-string', () => {\n assertThrows(\n () => {\n Device.lookup('');\n },\n Error,\n `Device.lookup: useragent parameter can not be an empty string`,\n );\n });\n routes.set('/device/lookup/useragent-does-not-exist-returns-null', () => {\n let result = Device.lookup(Math.random());\n assert(result, null, `Device.lookup(Math.random()) === null`);\n });\n routes.set('/device/lookup/useragent-exists-all-fields-identified', () => {\n let useragent =\n 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0 FBAN/FBIOS;FBAV/8.0.0.28.18;FBBV/1665515;FBDV/iPhone4,1;FBMD/iPhone;FBSN/iPhone OS;FBSV/7.0.4;FBSS/2; FBCR/Telekom.de;FBID/phone;FBLC/de_DE;FBOP/5';\n let device = Device.lookup(useragent);\n\n assert(\n device instanceof Device,\n true,\n `Device.lookup(useragent) instanceof DeviceEntry`,\n );\n\n assert(device.name, 'iPhone', `device.name`);\n assert(device.brand, 'Apple', `device.brand`);\n assert(device.model, 'iPhone4,1', `device.model`);\n assert(device.hardwareType, 'Mobile Phone', `device.hardwareType`);\n assert(device.isDesktop, false, `device.isDesktop`);\n assert(device.isGameConsole, false, `device.isGameConsole`);\n assert(device.isMediaPlayer, false, `device.isMediaPlayer`);\n assert(device.isMobile, true, `device.isMobile`);\n assert(device.isSmartTV, false, `device.isSmartTV`);\n assert(device.isTablet, false, `device.isTablet`);\n assert(device.isTouchscreen, true, `device.isTouchscreen`);\n });\n routes.set(\n '/device/lookup/useragent-exists-all-fields-identified-tojson',\n () => {\n let useragent =\n 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0 FBAN/FBIOS;FBAV/8.0.0.28.18;FBBV/1665515;FBDV/iPhone4,1;FBMD/iPhone;FBSN/iPhone OS;FBSV/7.0.4;FBSS/2; FBCR/Telekom.de;FBID/phone;FBLC/de_DE;FBOP/5';\n let device = Device.lookup(useragent);\n device = device.toJSON();\n\n assert(device.brand, 'Apple', `device.brand`);\n assert(device.model, 'iPhone4,1', `device.model`);\n assert(device.hardwareType, 'Mobile Phone', `device.hardwareType`);\n assert(device.isDesktop, false, `device.isDesktop`);\n assert(device.isGameConsole, false, `device.isGameConsole`);\n assert(device.isMediaPlayer, false, `device.isMediaPlayer`);\n assert(device.isMobile, true, `device.isMobile`);\n assert(device.isSmartTV, false, `device.isSmartTV`);\n assert(device.isTablet, false, `device.isTablet`);\n assert(device.isTouchscreen, true, `device.isTouchscreen`);\n },\n );\n routes.set('/device/lookup/useragent-exists-some-fields-identified', () => {\n let useragent =\n 'ghosts-app/1.0.2.1 (ASUSTeK COMPUTER INC.; X550CC; Windows 8 (X86); en)';\n let device = Device.lookup(useragent);\n\n assert(\n device instanceof Device,\n true,\n `Device.lookup(useragent) instanceof DeviceEntry`,\n );\n assert(device.name, 'Asus TeK', `device.name`);\n assert(device.brand, 'Asus', `device.brand`);\n assert(device.model, 'TeK', `device.model`);\n assert(device.hardwareType, null, `device.hardwareType`);\n assert(device.isDesktop, false, `device.isDesktop`);\n assert(device.isGameConsole, null, `device.isGameConsole`);\n assert(device.isMediaPlayer, null, `device.isMediaPlayer`);\n assert(device.isMobile, null, `device.isMobile`);\n assert(device.isSmartTV, null, `device.isSmartTV`);\n assert(device.isTablet, null, `device.isTablet`);\n assert(device.isTouchscreen, null, `device.isTouchscreen`);\n assert(\n JSON.stringify(device),\n '{\"name\":\"Asus TeK\",\"brand\":\"Asus\",\"model\":\"TeK\",\"hardwareType\":null,\"isDesktop\":false,\"isGameConsole\":null,\"isMediaPlayer\":null,\"isMobile\":null,\"isSmartTV\":null,\"isTablet\":null,\"isTouchscreen\":null}',\n `JSON.stringify(device)`,\n );\n });\n}\n", "export const Dictionary = globalThis.Dictionary;", "/* eslint-env serviceworker */\n\nimport { Dictionary } from 'fastly:dictionary';\nimport { routes } from './routes.js';\nimport { assertThrows, assert, assertResolves } from './assertions.js';\nimport { env } from 'fastly:env';\n\nconst DICTIONARY_NAME = env('DICTIONARY_NAME');\n\n// Dictionary\n{\n routes.set('/dictionary/exposed-as-global', () => {\n assert(typeof Dictionary, 'function', `typeof Dictionary`);\n });\n routes.set('/dictionary/interface', dictionaryInterfaceTests);\n // Dictionary constructor\n {\n routes.set('/dictionary/constructor/called-as-regular-function', () => {\n assertThrows(\n () => {\n Dictionary();\n },\n TypeError,\n `calling a builtin Dictionary constructor without new is forbidden`,\n );\n });\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/dictionary/constructor/parameter-calls-7.1.17-ToString',\n () => {\n let sentinel = Symbol();\n const test = () => {\n const name = {\n toString() {\n throw sentinel;\n },\n };\n new Dictionary(name);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => new Dictionary(Symbol()),\n TypeError,\n `can't convert symbol to string`,\n );\n },\n );\n routes.set('/dictionary/constructor/empty-parameter', () => {\n assertThrows(\n () => {\n new Dictionary();\n },\n TypeError,\n `Dictionary constructor: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/dictionary/constructor/found', () => {\n const store = createValidDictionary();\n assert(store instanceof Dictionary, true, `store instanceof Dictionary`);\n });\n routes.set('/dictionary/constructor/invalid-name', () => {\n // control Characters (\\\\u0000-\\\\u001F) are not allowed\n const invalidNames = ['1', '-', ' ', '\u00C4'];\n for (const name of invalidNames) {\n assertThrows(\n () => {\n new Dictionary(name);\n },\n TypeError,\n `Dictionary constructor: name must start with an ascii alpabetical character`,\n );\n }\n assertThrows(\n () => {\n new Dictionary('a\u00C4');\n },\n TypeError,\n `Dictionary constructor: name can contain only ascii alphanumeric characters, underscores, and ascii whitespace`,\n );\n\n // must be less than 256 characters\n assertThrows(\n () => {\n new Dictionary('a'.repeat(256));\n },\n TypeError,\n `Dictionary constructor: name can not be more than 255 characters`,\n );\n\n // empty string not allowed\n assertThrows(\n () => {\n new Dictionary('');\n },\n TypeError,\n `Dictionary constructor: name can not be an empty string`,\n );\n });\n }\n\n // Dictionary get method\n {\n routes.set('/dictionary/get/called-as-constructor', () => {\n assertThrows(() => {\n new Dictionary.prototype.get('1');\n }, TypeError);\n });\n routes.set('/dictionary/get/called-unbound', () => {\n assertThrows(() => {\n Dictionary.prototype.get.call(undefined, '1');\n }, TypeError);\n });\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/dictionary/get/key-parameter-calls-7.1.17-ToString', () => {\n let sentinel = Symbol();\n const test = () => {\n const key = {\n toString() {\n throw sentinel;\n },\n };\n const store = createValidDictionary();\n store.get(key);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n const store = createValidDictionary();\n store.get(Symbol());\n },\n TypeError,\n `can't convert symbol to string`,\n );\n });\n routes.set('/dictionary/get/key-parameter-not-supplied', () => {\n assertThrows(\n () => {\n const store = createValidDictionary();\n store.get();\n },\n TypeError,\n `get: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/dictionary/get/key-parameter-empty-string', () => {\n assertThrows(\n () => {\n const store = createValidDictionary();\n store.get('');\n },\n TypeError,\n `Dictionary key can not be an empty string`,\n );\n });\n routes.set('/dictionary/get/key-parameter-255-character-string', () => {\n assertResolves(() => {\n const store = createValidDictionary();\n const key = 'a'.repeat(255);\n store.get(key);\n });\n });\n routes.set('/dictionary/get/key-parameter-256-character-string', () => {\n assertThrows(\n () => {\n const store = createValidDictionary();\n const key = 'a'.repeat(256);\n store.get(key);\n },\n TypeError,\n `Dictionary key can not be more than 255 characters`,\n );\n });\n routes.set('/dictionary/get/key-does-not-exist-returns-null', () => {\n let store = createValidDictionary();\n let result = store.get(Math.random());\n assert(result, null, `store.get(Math.random())`);\n });\n routes.set('/dictionary/get/key-exists', () => {\n let store = createValidDictionary();\n let result = store.get('twitter');\n assert(\n result,\n 'https://twitter.com/fastly',\n `store.get('twitter') === \"https://twitter.com/fastly\"`,\n );\n });\n }\n}\n\nfunction dictionaryInterfaceTests() {\n let actual = Reflect.ownKeys(Dictionary);\n let expected = ['prototype', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(Dictionary)`);\n\n actual = Reflect.getOwnPropertyDescriptor(Dictionary, 'prototype');\n expected = {\n value: Dictionary.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Dictionary, 'prototype')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(Dictionary, 'length');\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Dictionary, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(Dictionary, 'name');\n expected = {\n value: 'Dictionary',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Dictionary, 'name')`,\n );\n\n actual = Reflect.ownKeys(Dictionary.prototype);\n expected = ['constructor', 'get'];\n assert(actual, expected, `Reflect.ownKeys(Dictionary.prototype)`);\n\n actual = Reflect.getOwnPropertyDescriptor(\n Dictionary.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: Dictionary.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Dictionary.prototype, 'constructor')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(Dictionary.prototype, 'get');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: Dictionary.prototype.get,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Dictionary.prototype, 'get')`,\n );\n\n assert(\n typeof Dictionary.prototype.constructor,\n 'function',\n `typeof Dictionary.prototype.constructor`,\n );\n assert(\n typeof Dictionary.prototype.get,\n 'function',\n `typeof Dictionary.prototype.get`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n Dictionary.prototype.constructor,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Dictionary.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n Dictionary.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'Dictionary',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Dictionary.prototype.constructor, 'name')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(Dictionary.prototype.get, 'length');\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Dictionary.prototype.get, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(Dictionary.prototype.get, 'name');\n expected = {\n value: 'get',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Dictionary.prototype.get, 'name')`,\n );\n}\n\nfunction createValidDictionary() {\n return new Dictionary(DICTIONARY_NAME);\n}\n", "import { pass, ok, strictEqual, assertThrows } from './assertions.js';\nimport { routes } from './routes.js';\n\nroutes.set('/early-hints/manual-response', (event) => {\n event.respondWith(\n new Response(null, {\n status: 103,\n headers: { link: '; rel=preload; as=style' },\n }),\n );\n event.respondWith(new Response('ok'));\n});\n\nroutes.set('/early-hints/send-early-hints', (event) => {\n event.sendEarlyHints({ link: '; rel=preload; as=style' });\n event.respondWith(new Response('ok'));\n});\n\nroutes.set('/early-hints/send-early-hints-multiple-headers', (event) => {\n event.sendEarlyHints([\n ['link', '; rel=preload; as=style'],\n ['link', '; rel=preload; as=style'],\n ]);\n event.respondWith(new Response('ok'));\n});\n", "\nexport const RateCounter = globalThis.RateCounter;\nexport const PenaltyBox = globalThis.PenaltyBox;\nexport const EdgeRateLimiter = globalThis.EdgeRateLimiter;\n", "/// \n/* eslint-env serviceworker */\n\nimport { assert, assertThrows } from './assertions.js';\nimport {\n RateCounter,\n PenaltyBox,\n EdgeRateLimiter,\n} from 'fastly:edge-rate-limiter';\nimport { routes, isRunningLocally } from './routes.js';\nimport { env } from 'fastly:env';\n\nconst FASTLY_SERVICE_NAME = env('FASTLY_SERVICE_NAME');\nconst PENALTY_BOX_NAME = `pb${FASTLY_SERVICE_NAME}`;\nconst RATE_COUNTER_NAME = `rc${FASTLY_SERVICE_NAME}`;\n\n// RateCounter\n{\n routes.set('/rate-counter/interface', () => {\n let actual = Reflect.ownKeys(RateCounter);\n let expected = ['prototype', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(RateCounter)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(RateCounter, 'prototype');\n expected = {\n value: RateCounter.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(RateCounter, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(RateCounter, 'name');\n expected = {\n value: 'RateCounter',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(RateCounter.prototype);\n expected = [\n 'constructor',\n 'increment',\n 'lookupRate',\n 'lookupCount',\n Symbol.toStringTag,\n ];\n assert(actual, expected, `Reflect.ownKeys(RateCounter.prototype)`);\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: RateCounter.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype, 'constructor')`,\n );\n\n assert(\n typeof RateCounter.prototype.constructor,\n 'function',\n `typeof RateCounter.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'RateCounter',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype,\n Symbol.toStringTag,\n );\n expected = {\n writable: false,\n enumerable: false,\n configurable: true,\n value: 'RateCounter',\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof RateCounter.prototype[Symbol.toStringTag],\n 'string',\n `typeof RateCounter.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the increment method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype,\n 'increment',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: RateCounter.prototype.increment,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype, 'increment')`,\n );\n\n assert(\n typeof RateCounter.prototype.increment,\n 'function',\n `typeof RateCounter.prototype.increment`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype.increment,\n 'length',\n );\n expected = {\n value: 2,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype.increment, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype.increment,\n 'name',\n );\n expected = {\n value: 'increment',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype.increment, 'name')`,\n );\n }\n\n // Check the lookupRate method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype,\n 'lookupRate',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: RateCounter.prototype.lookupRate,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype, 'lookupRate')`,\n );\n\n assert(\n typeof RateCounter.prototype.lookupRate,\n 'function',\n `typeof RateCounter.prototype.lookupRate`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype.lookupRate,\n 'length',\n );\n expected = {\n value: 2,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype.lookupRate, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype.lookupRate,\n 'name',\n );\n expected = {\n value: 'lookupRate',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype.lookupRate, 'name')`,\n );\n }\n\n // Check the lookupCount method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype,\n 'lookupCount',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: RateCounter.prototype.lookupCount,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype, 'lookupCount')`,\n );\n\n assert(\n typeof RateCounter.prototype.lookupCount,\n 'function',\n `typeof RateCounter.prototype.lookupCount`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype.lookupCount,\n 'length',\n );\n expected = {\n value: 2,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype.lookupCount, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n RateCounter.prototype.lookupCount,\n 'name',\n );\n expected = {\n value: 'lookupCount',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(RateCounter.prototype.lookupCount, 'name')`,\n );\n }\n });\n\n // RateCounter constructor\n {\n routes.set('/rate-counter/constructor/called-as-regular-function', () => {\n assertThrows(\n () => {\n RateCounter();\n },\n Error,\n `calling a builtin RateCounter constructor without new is forbidden`,\n );\n });\n routes.set(\n '/rate-counter/constructor/called-as-constructor-no-arguments',\n () => {\n assertThrows(\n () => new RateCounter(),\n Error,\n `RateCounter constructor: At least 1 argument required, but only 0 passed`,\n );\n },\n );\n // Ensure we correctly coerce the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/rate-counter/constructor/name-parameter-calls-7.1.17-ToString',\n () => {\n if (!isRunningLocally()) {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const name = {\n toString() {\n throw sentinel;\n },\n };\n new RateCounter(name);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n new RateCounter(Symbol());\n },\n Error,\n `can't convert symbol to string`,\n );\n }\n },\n );\n routes.set('/rate-counter/constructor/happy-path', () => {\n assert(\n new RateCounter(RATE_COUNTER_NAME) instanceof RateCounter,\n true,\n `new RateCounter(\"rc\") instanceof RateCounter`,\n );\n });\n }\n\n // RateCounter increment method\n // increment(entry: string, delta: number): void;\n {\n routes.set('/rate-counter/increment/called-as-constructor', () => {\n assertThrows(() => {\n new RateCounter.prototype.increment('entry', 1);\n }, Error);\n });\n // Ensure we correctly coerce the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/rate-counter/increment/entry-parameter-calls-7.1.17-ToString',\n () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const entry = {\n toString() {\n throw sentinel;\n },\n };\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.increment(entry, 1);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n console.log({ thrownError });\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.increment(Symbol(), 1);\n },\n Error,\n `can't convert symbol to string`,\n );\n },\n );\n routes.set('/rate-counter/increment/entry-parameter-not-supplied', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.increment();\n },\n Error,\n `increment: At least 2 arguments required, but only 0 passed`,\n );\n });\n routes.set('/rate-counter/increment/delta-parameter-not-supplied', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.increment('entry');\n },\n Error,\n `increment: At least 2 arguments required, but only 1 passed`,\n );\n });\n routes.set('/rate-counter/increment/delta-parameter-negative', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.increment('entry', -1);\n },\n Error,\n `increment: delta parameter is an invalid value, only positive numbers can be used for delta values.`,\n );\n });\n routes.set('/rate-counter/increment/delta-parameter-infinity', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.increment('entry', Infinity);\n },\n Error,\n `increment: delta parameter is an invalid value, only positive numbers can be used for delta values.`,\n );\n });\n routes.set('/rate-counter/increment/delta-parameter-NaN', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.increment('entry', NaN);\n },\n Error,\n `increment: delta parameter is an invalid value, only positive numbers can be used for delta values.`,\n );\n });\n routes.set('/rate-counter/increment/returns-undefined', () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n assert(rc.increment('meow', 1), undefined, \"rc.increment('meow', 1)\");\n });\n }\n\n // RateCounter lookupRate method\n // lookupRate(entry: string, window: [1, 10, 60]): number;\n {\n routes.set('/rate-counter/lookupRate/called-as-constructor', () => {\n assertThrows(() => {\n new RateCounter.prototype.lookupRate('entry', 1);\n }, Error);\n });\n // Ensure we correctly coerce the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/rate-counter/lookupRate/entry-parameter-calls-7.1.17-ToString',\n () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const entry = {\n toString() {\n throw sentinel;\n },\n };\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupRate(entry, 1);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n console.log({ thrownError });\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupRate(Symbol(), 1);\n },\n Error,\n `can't convert symbol to string`,\n );\n },\n );\n routes.set('/rate-counter/lookupRate/entry-parameter-not-supplied', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupRate();\n },\n Error,\n `lookupRate: At least 2 arguments required, but only 0 passed`,\n );\n });\n routes.set('/rate-counter/lookupRate/window-parameter-not-supplied', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupRate('entry');\n },\n Error,\n `lookupRate: At least 2 arguments required, but only 1 passed`,\n );\n });\n routes.set('/rate-counter/lookupRate/window-parameter-negative', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupRate('entry', -1);\n },\n Error,\n `lookupRate: window parameter must be either: 1, 10, or 60`,\n );\n }\n });\n routes.set('/rate-counter/lookupRate/window-parameter-infinity', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupRate('entry', Infinity);\n },\n Error,\n `lookupRate: window parameter must be either: 1, 10, or 60`,\n );\n }\n });\n routes.set('/rate-counter/lookupRate/window-parameter-NaN', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupRate('entry', NaN);\n },\n Error,\n `lookupRate: window parameter must be either: 1, 10, or 60`,\n );\n }\n });\n routes.set('/rate-counter/lookupRate/returns-number', () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n assert(\n typeof rc.lookupRate('meow', 1),\n 'number',\n `typeof rc.lookupRate('meow', 1)`,\n );\n });\n }\n\n // RateCounter lookupCount method\n // lookupCount(entry: string, duration: [10, 20, 30, 40, 50, 60]): number;\n {\n routes.set('/rate-counter/lookupCount/called-as-constructor', () => {\n assertThrows(() => {\n new RateCounter.prototype.lookupCount('entry', 1);\n }, Error);\n });\n // Ensure we correctly coerce the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/rate-counter/lookupCount/entry-parameter-calls-7.1.17-ToString',\n () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const entry = {\n toString() {\n throw sentinel;\n },\n };\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupCount(entry, 1);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n console.log({ thrownError });\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupCount(Symbol(), 1);\n },\n Error,\n `can't convert symbol to string`,\n );\n },\n );\n routes.set('/rate-counter/lookupCount/entry-parameter-not-supplied', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupCount();\n },\n Error,\n `lookupCount: At least 2 arguments required, but only 0 passed`,\n );\n }\n });\n routes.set(\n '/rate-counter/lookupCount/duration-parameter-not-supplied',\n () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupCount('entry');\n },\n Error,\n `lookupCount: At least 2 arguments required, but only 1 passed`,\n );\n }\n },\n );\n routes.set('/rate-counter/lookupCount/duration-parameter-negative', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupCount('entry', -1);\n },\n Error,\n `lookupCount: duration parameter must be either: 10, 20, 30, 40, 50, or 60`,\n );\n }\n });\n routes.set('/rate-counter/lookupCount/duration-parameter-infinity', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupCount('entry', Infinity);\n },\n Error,\n `lookupCount: duration parameter must be either: 10, 20, 30, 40, 50, or 60`,\n );\n }\n });\n routes.set('/rate-counter/lookupCount/duration-parameter-NaN', () => {\n if (!isRunningLocally()) {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n rc.lookupCount('entry', NaN);\n },\n Error,\n `lookupCount: duration parameter must be either: 10, 20, 30, 40, 50, or 60`,\n );\n }\n });\n routes.set('/rate-counter/lookupCount/returns-number', () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n assert(\n typeof rc.lookupCount('meow', 10),\n 'number',\n `typeof rc.lookupCount('meow', 1)`,\n );\n });\n }\n}\n\n// PenaltyBox\n{\n routes.set('/penalty-box/interface', () => {\n let actual = Reflect.ownKeys(PenaltyBox);\n let expected = ['prototype', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(PenaltyBox)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(PenaltyBox, 'prototype');\n expected = {\n value: PenaltyBox.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(PenaltyBox, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(PenaltyBox, 'name');\n expected = {\n value: 'PenaltyBox',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(PenaltyBox.prototype);\n expected = ['constructor', 'add', 'has', Symbol.toStringTag];\n assert(actual, expected, `Reflect.ownKeys(PenaltyBox.prototype)`);\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n PenaltyBox.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: PenaltyBox.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype, 'constructor')`,\n );\n\n assert(\n typeof PenaltyBox.prototype.constructor,\n 'function',\n `typeof PenaltyBox.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n PenaltyBox.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n PenaltyBox.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'PenaltyBox',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n PenaltyBox.prototype,\n Symbol.toStringTag,\n );\n expected = {\n writable: false,\n enumerable: false,\n configurable: true,\n value: 'PenaltyBox',\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof PenaltyBox.prototype[Symbol.toStringTag],\n 'string',\n `typeof PenaltyBox.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the add method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype, 'add');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: PenaltyBox.prototype.add,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype, 'add')`,\n );\n\n assert(\n typeof PenaltyBox.prototype.add,\n 'function',\n `typeof PenaltyBox.prototype.add`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n PenaltyBox.prototype.add,\n 'length',\n );\n expected = {\n value: 2,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype.add, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n PenaltyBox.prototype.add,\n 'name',\n );\n expected = {\n value: 'add',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype.add, 'name')`,\n );\n }\n\n // Check the has method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype, 'has');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: PenaltyBox.prototype.has,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype, 'has')`,\n );\n\n assert(\n typeof PenaltyBox.prototype.has,\n 'function',\n `typeof PenaltyBox.prototype.has`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n PenaltyBox.prototype.has,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype.has, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n PenaltyBox.prototype.has,\n 'name',\n );\n expected = {\n value: 'has',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(PenaltyBox.prototype.has, 'name')`,\n );\n }\n });\n\n // PenaltyBox constructor\n {\n routes.set('/penalty-box/constructor/called-as-regular-function', () => {\n assertThrows(\n () => {\n PenaltyBox();\n },\n Error,\n `calling a builtin PenaltyBox constructor without new is forbidden`,\n );\n });\n routes.set(\n '/penalty-box/constructor/called-as-constructor-no-arguments',\n () => {\n assertThrows(\n () => new PenaltyBox(),\n Error,\n `PenaltyBox constructor: At least 1 argument required, but only 0 passed`,\n );\n },\n );\n // Ensure we correctly coerce the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/penalty-box/constructor/name-parameter-calls-7.1.17-ToString',\n () => {\n if (!isRunningLocally()) {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const name = {\n toString() {\n throw sentinel;\n },\n };\n new PenaltyBox(name);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n new PenaltyBox(Symbol());\n },\n Error,\n `can't convert symbol to string`,\n );\n }\n },\n );\n routes.set('/penalty-box/constructor/happy-path', () => {\n assert(\n new PenaltyBox(RATE_COUNTER_NAME) instanceof PenaltyBox,\n true,\n `new PenaltyBox(\"rc\") instanceof PenaltyBox`,\n );\n });\n }\n\n // PenaltyBox has method\n // has(entry: string): boolean;\n {\n routes.set('/penalty-box/has/called-as-constructor', () => {\n assertThrows(() => {\n new PenaltyBox.prototype.has('entry');\n }, Error);\n });\n // Ensure we correctly coerce the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/penalty-box/has/entry-parameter-calls-7.1.17-ToString', () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const entry = {\n toString() {\n throw sentinel;\n },\n };\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.has(entry);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n console.log({ thrownError });\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.has(Symbol());\n },\n Error,\n `can't convert symbol to string`,\n );\n });\n routes.set('/penalty-box/has/entry-parameter-not-supplied', () => {\n assertThrows(\n () => {\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.has();\n },\n Error,\n `has: At least 1 argument required, but only 0 passed`,\n );\n });\n routes.set('/penalty-box/has/returns-boolean', () => {\n let pb = new PenaltyBox(`pb-`);\n assert(pb.has('meow'), false, \"pb.has('meow')\");\n });\n }\n\n // PenaltyBox add method\n // add(entry: string, timeToLive: number): void;\n {\n routes.set('/penalty-box/add/called-as-constructor', () => {\n assertThrows(() => {\n new PenaltyBox.prototype.add('entry', 1);\n }, Error);\n });\n // Ensure we correctly coepbe the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set('/penalty-box/add/entry-parameter-calls-7.1.17-ToString', () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const entry = {\n toString() {\n throw sentinel;\n },\n };\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.add(entry, 1);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n console.log({ thrownError });\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.add(Symbol(), 1);\n },\n Error,\n `can't convert symbol to string`,\n );\n });\n routes.set('/penalty-box/add/entry-parameter-not-supplied', () => {\n assertThrows(\n () => {\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.add();\n },\n Error,\n `add: At least 2 arguments required, but only 0 passed`,\n );\n });\n routes.set('/penalty-box/add/timeToLive-parameter-not-supplied', () => {\n assertThrows(\n () => {\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.add('entry');\n },\n Error,\n `add: At least 2 arguments required, but only 1 passed`,\n );\n });\n routes.set('/penalty-box/add/timeToLive-parameter-negative', () => {\n assertThrows(\n () => {\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.add('entry', -1);\n },\n Error,\n `add: timeToLive parameter is an invalid value, only numbers from 1 to 60 can be used for timeToLive values.`,\n );\n });\n routes.set('/penalty-box/add/timeToLive-parameter-infinity', () => {\n assertThrows(\n () => {\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.add('entry', Infinity);\n },\n Error,\n `add: timeToLive parameter is an invalid value, only numbers from 1 to 60 can be used for timeToLive values.`,\n );\n });\n routes.set('/penalty-box/add/timeToLive-parameter-NaN', () => {\n assertThrows(\n () => {\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n pb.add('entry', NaN);\n },\n Error,\n `add: timeToLive parameter is an invalid value, only numbers from 1 to 60 can be used for timeToLive values.`,\n );\n });\n routes.set('/penalty-box/add/returns-undefined', () => {\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n assert(pb.add('meow', 1), undefined, `pb.add('meow', 1)`);\n });\n }\n}\n\n// EdgeRateLimiter\n{\n routes.set('/edge-rate-limiter/interface', () => {\n let actual = Reflect.ownKeys(EdgeRateLimiter);\n let expected = ['prototype', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(EdgeRateLimiter)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(EdgeRateLimiter, 'prototype');\n expected = {\n value: EdgeRateLimiter.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(EdgeRateLimiter, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(EdgeRateLimiter, 'name');\n expected = {\n value: 'EdgeRateLimiter',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(EdgeRateLimiter.prototype);\n expected = ['constructor', 'checkRate', Symbol.toStringTag];\n assert(actual, expected, `Reflect.ownKeys(EdgeRateLimiter.prototype)`);\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n EdgeRateLimiter.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: EdgeRateLimiter.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter.prototype, 'constructor')`,\n );\n\n assert(\n typeof EdgeRateLimiter.prototype.constructor,\n 'function',\n `typeof EdgeRateLimiter.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n EdgeRateLimiter.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n EdgeRateLimiter.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'EdgeRateLimiter',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n EdgeRateLimiter.prototype,\n Symbol.toStringTag,\n );\n expected = {\n writable: false,\n enumerable: false,\n configurable: true,\n value: 'EdgeRateLimiter',\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof EdgeRateLimiter.prototype[Symbol.toStringTag],\n 'string',\n `typeof EdgeRateLimiter.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the checkRate method has correct descriptors, length and name\n {\n actual = Reflect.getOwnPropertyDescriptor(\n EdgeRateLimiter.prototype,\n 'checkRate',\n );\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: EdgeRateLimiter.prototype.checkRate,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter.prototype, 'checkRate')`,\n );\n\n assert(\n typeof EdgeRateLimiter.prototype.checkRate,\n 'function',\n `typeof EdgeRateLimiter.prototype.checkRate`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n EdgeRateLimiter.prototype.checkRate,\n 'length',\n );\n expected = {\n value: 5,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter.prototype.checkRate, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n EdgeRateLimiter.prototype.checkRate,\n 'name',\n );\n expected = {\n value: 'checkRate',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(EdgeRateLimiter.prototype.checkRate, 'name')`,\n );\n }\n });\n\n // EdgeRateLimiter constructor\n {\n routes.set(\n '/edge-rate-limiter/constructor/called-as-regular-function',\n () => {\n assertThrows(\n () => {\n EdgeRateLimiter();\n },\n Error,\n `calling a builtin EdgeRateLimiter constructor without new is forbidden`,\n );\n },\n );\n routes.set(\n '/edge-rate-limiter/constructor/called-as-constructor-no-arguments',\n () => {\n assertThrows(\n () => new EdgeRateLimiter(),\n Error,\n `EdgeRateLimiter constructor: At least 2 arguments required, but only 0 passed`,\n );\n },\n );\n // Ensure we correctly coerce the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/edge-rate-limiter/constructor/rate-counter-not-instance-of-rateCounter',\n () => {\n assertThrows(\n () => {\n new EdgeRateLimiter(true, true);\n },\n Error,\n `EdgeRateLimiter constructor: rateCounter parameter must be an instance of RateCounter`,\n );\n },\n );\n routes.set(\n '/edge-rate-limiter/constructor/penalty-box-not-instance-of-penaltyBox',\n () => {\n assertThrows(\n () => {\n new EdgeRateLimiter(new RateCounter(RATE_COUNTER_NAME), true);\n },\n Error,\n `EdgeRateLimiter constructor: penaltyBox parameter must be an instance of PenaltyBox`,\n );\n },\n );\n routes.set('/edge-rate-limiter/constructor/happy-path', () => {\n assert(\n new EdgeRateLimiter(\n new RateCounter(RATE_COUNTER_NAME),\n new PenaltyBox(PENALTY_BOX_NAME),\n ) instanceof EdgeRateLimiter,\n true,\n `new EdgeRateLimiter(new RateCounter(\"rc\"), new PenaltyBox(PENALTY_BOX_NAME)) instanceof EdgeRateLimiter`,\n );\n });\n }\n\n // EdgeRateLimiter checkRate method\n // checkRate(entry: string, delta: number, window: [1, 10, 60], limit: number, timeToLive: number): boolean;\n {\n routes.set('/edge-rate-limiter/checkRate/called-as-constructor', () => {\n assertThrows(() => {\n new EdgeRateLimiter.prototype.checkRate('entry');\n }, Error);\n });\n // Ensure we correctly coerce the parameter to a string as according to\n // https://tc39.es/ecma262/#sec-tostring\n routes.set(\n '/edge-rate-limiter/checkRate/entry-parameter-calls-7.1.17-ToString',\n () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol('sentinel');\n const entry = {\n toString() {\n throw sentinel;\n },\n };\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate(entry, 1, 1, 1, 1);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n console.log({ thrownError });\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate(Symbol(), 1, 1, 1, 1);\n },\n Error,\n `can't convert symbol to string`,\n );\n },\n );\n routes.set(\n '/edge-rate-limiter/checkRate/entry-parameter-not-supplied',\n () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate();\n },\n Error,\n `checkRate: At least 5 arguments required, but only 0 passed`,\n );\n },\n );\n routes.set('/edge-rate-limiter/checkRate/delta-parameter-negative', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', -1, 1, 1, 1);\n },\n Error,\n `checkRate: delta parameter is an invalid value, only positive numbers can be used for delta values.`,\n );\n });\n routes.set('/edge-rate-limiter/checkRate/delta-parameter-infinity', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', Infinity, 1, 1, 1);\n },\n Error,\n `checkRate: delta parameter is an invalid value, only positive numbers can be used for delta values.`,\n );\n });\n routes.set('/edge-rate-limiter/checkRate/delta-parameter-NaN', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', NaN, 1, 1, 1);\n },\n Error,\n `checkRate: delta parameter is an invalid value, only positive numbers can be used for delta values.`,\n );\n });\n routes.set('/edge-rate-limiter/checkRate/window-parameter-negative', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', 1, -1, 1, 1);\n },\n Error,\n `checkRate: window parameter must be either: 1, 10, or 60`,\n );\n });\n routes.set('/edge-rate-limiter/checkRate/window-parameter-infinity', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', 1, Infinity, 1, 1);\n },\n Error,\n `checkRate: window parameter must be either: 1, 10, or 60`,\n );\n });\n routes.set('/edge-rate-limiter/checkRate/window-parameter-NaN', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', 1, NaN, 1, 1);\n },\n Error,\n `checkRate: window parameter must be either: 1, 10, or 60`,\n );\n });\n routes.set('/edge-rate-limiter/checkRate/limit-parameter-negative', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', 1, 1, -1, 1);\n },\n Error,\n `checkRate: limit parameter is an invalid value, only positive numbers can be used for limit values.`,\n );\n });\n routes.set('/edge-rate-limiter/checkRate/limit-parameter-infinity', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', 1, 1, Infinity, 1);\n },\n Error,\n `checkRate: limit parameter is an invalid value, only positive numbers can be used for limit values.`,\n );\n });\n routes.set('/edge-rate-limiter/checkRate/limit-parameter-NaN', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', 1, 1, NaN, 1);\n },\n Error,\n `checkRate: limit parameter is an invalid value, only positive numbers can be used for limit values.`,\n );\n });\n routes.set(\n '/edge-rate-limiter/checkRate/timeToLive-parameter-negative',\n () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', 1, 1, 1, -1);\n },\n Error,\n `checkRate: timeToLive parameter is an invalid value, only numbers from 1 to 60 can be used for timeToLive values.`,\n );\n },\n );\n routes.set(\n '/edge-rate-limiter/checkRate/timeToLive-parameter-infinity',\n () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', 1, 1, 1, Infinity);\n },\n Error,\n `checkRate: timeToLive parameter is an invalid value, only numbers from 1 to 60 can be used for timeToLive values.`,\n );\n },\n );\n routes.set('/edge-rate-limiter/checkRate/timeToLive-parameter-NaN', () => {\n assertThrows(\n () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n erl.checkRate('entry', 1, 1, 1, NaN);\n },\n Error,\n `checkRate: timeToLive parameter is an invalid value, only numbers from 1 to 60 can be used for timeToLive values.`,\n );\n });\n routes.set('/edge-rate-limiter/checkRate/returns-boolean', () => {\n let rc = new RateCounter(RATE_COUNTER_NAME);\n let pb = new PenaltyBox(PENALTY_BOX_NAME);\n let erl = new EdgeRateLimiter(rc, pb);\n assert(\n erl.checkRate('woof', 1, 10, 100, 5),\n false,\n \"erl.checkRate('meow', 1, 10, 100, 5)\",\n );\n });\n }\n}\n", "/* eslint-env serviceworker */\nimport { env } from 'fastly:env';\nimport { routes, isRunningLocally } from './routes.js';\nimport { strictEqual } from './assertions.js';\n\n// hostname didn't exist at initialization, so can still be captured at runtime\nconst wizerHostname = env('FASTLY_HOSTNAME');\nconst wizerLocal = env('LOCAL_TEST');\n\nroutes.set('/env', () => {\n strictEqual(wizerHostname, undefined);\n\n if (isRunningLocally()) {\n strictEqual(\n env('FASTLY_HOSTNAME'),\n 'localhost',\n `env(\"FASTLY_HOSTNAME\") === \"localhost\"`,\n );\n } else {\n strictEqual(env('FASTLY_HOSTNAME'), undefined);\n }\n\n strictEqual(wizerLocal, 'local val');\n\n // at runtime these remain captured from Wizer time, even if we didn't call env\n strictEqual(env('LOCAL_TEST'), 'local val');\n strictEqual(env('TEST'), 'foo');\n});\n", "export const createFanoutHandoff = globalThis.fastly.createFanoutHandoff;", "import { assert, assertDoesNotThrow, assertThrows } from './assertions.js';\nimport { routes } from './routes.js';\nimport { createFanoutHandoff } from 'fastly:fanout';\n\nroutes.set('/createFanoutHandoff', () => {\n assert(typeof createFanoutHandoff, 'function', 'typeof createFanoutHandoff');\n\n assert(\n createFanoutHandoff.name,\n 'createFanoutHandoff',\n 'createFanoutHandoff.name',\n );\n\n assert(createFanoutHandoff.length, 2, 'createFanoutHandoff.length');\n\n assertDoesNotThrow(() => createFanoutHandoff(new Request('.'), 'hello'));\n\n assertThrows(() => createFanoutHandoff());\n\n assertThrows(() => createFanoutHandoff(1, ''));\n\n let result = createFanoutHandoff(new Request('.'), 'hello');\n assert(result instanceof Response, true, 'result instanceof Response');\n\n assertThrows(\n () => new createFanoutHandoff(new Request('.'), 'hello'),\n TypeError,\n );\n\n assertDoesNotThrow(() => {\n createFanoutHandoff.call(undefined, new Request('.'), '1');\n });\n\n // https://tc39.es/ecma262/#sec-tostring\n let sentinel;\n const test = () => {\n sentinel = Symbol();\n const key = {\n toString() {\n throw sentinel;\n },\n };\n createFanoutHandoff(new Request('.'), key);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n createFanoutHandoff(new Request('.'), Symbol());\n },\n TypeError,\n `can't convert symbol to string`,\n );\n\n assertThrows(\n () => createFanoutHandoff(new Request('.')),\n TypeError,\n `createFanoutHandoff: At least 2 arguments required, but only 1 passed`,\n );\n\n assertThrows(\n () => createFanoutHandoff(new Request('.'), ''),\n Error,\n `createFanoutHandoff: Backend parameter can not be an empty string`,\n );\n});\n", "export const createWebsocketHandoff = globalThis.fastly.createWebsocketHandoff;", "import { assert, assertDoesNotThrow, assertThrows } from './assertions.js';\nimport { routes } from './routes.js';\nimport { createWebsocketHandoff } from 'fastly:websocket';\n\nroutes.set('/createWebsocketHandoff', () => {\n assert(\n typeof createWebsocketHandoff,\n 'function',\n 'typeof createWebsocketHandoff',\n );\n\n assert(\n createWebsocketHandoff.name,\n 'createWebsocketHandoff',\n 'createWebsocketHandoff.name',\n );\n\n assert(createWebsocketHandoff.length, 2, 'createWebsocketHandoff.length');\n\n assertDoesNotThrow(() => createWebsocketHandoff(new Request('.'), 'hello'));\n\n assertThrows(() => createWebsocketHandoff());\n\n assertThrows(() => createWebsocketHandoff(1, ''));\n\n let result = createWebsocketHandoff(new Request('.'), 'hello');\n assert(result instanceof Response, true, 'result instanceof Response');\n\n assertThrows(\n () => new createWebsocketHandoff(new Request('.'), 'hello'),\n TypeError,\n );\n\n assertDoesNotThrow(() => {\n createWebsocketHandoff.call(undefined, new Request('.'), '1');\n });\n\n // https://tc39.es/ecma262/#sec-tostring\n let sentinel;\n const test = () => {\n sentinel = Symbol();\n const key = {\n toString() {\n throw sentinel;\n },\n };\n createWebsocketHandoff(new Request('.'), key);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n createWebsocketHandoff(new Request('.'), Symbol());\n },\n TypeError,\n `can't convert symbol to string`,\n );\n\n assertThrows(\n () => createWebsocketHandoff(new Request('.')),\n TypeError,\n `createWebsocketHandoff: At least 2 arguments required, but only 1 passed`,\n );\n\n assertThrows(\n () => createWebsocketHandoff(new Request('.'), ''),\n Error,\n `createWebsocketHandoff: Backend parameter can not be an empty string`,\n );\n});\n", "/// \n/* eslint-env serviceworker */\n\nimport { assert } from './assertions.js';\nimport { routes } from './routes.js';\nimport { sdkVersion } from 'fastly:experimental';\n\nroutes.set('/fastly/now', function () {\n assert(typeof fastly.now, 'function', 'typeof fastly.now');\n\n assert(fastly.now.name, 'now', 'fastly.now.name');\n\n assert(fastly.now.length, 0, 'fastly.now.length');\n\n assert(typeof fastly.now(), 'number', `typeof fastly.now()`);\n\n assert(fastly.now() > Date.now(), true, `fastly.now() > Date.now()`);\n});\n\nroutes.set('/fastly/version', function () {\n assert(typeof fastly.sdkVersion, 'string', 'typeof fastly.sdkVersion');\n\n assert(\n fastly.sdkVersion,\n sdkVersion,\n 'fastly.sdkVersion matches fastly:experimental#sdkVersion',\n );\n});\n", "\nexport const Backend = globalThis.Backend;\nexport const setDefaultDynamicBackendConfig = Object.getOwnPropertyDescriptor(globalThis.fastly, 'allowDynamicBackends').set;\nconst allowDynamicBackends = Object.getOwnPropertyDescriptor(globalThis.fastly, 'allowDynamicBackends').set;\nexport const setDefaultBackend = Object.getOwnPropertyDescriptor(globalThis.fastly, 'defaultBackend').set;\nexport function enforceExplicitBackends (defaultBackend) {\n allowDynamicBackends(false);\n if (defaultBackend) setDefaultBackend(defaultBackend);\n}\n", "/// \nimport { assertRejects } from './assertions.js';\nimport { routes } from './routes.js';\nimport { Backend } from 'fastly:backend';\nimport { allowDynamicBackends } from 'fastly:experimental';\n\nroutes.set('/fetch-errors', async () => {\n allowDynamicBackends(true);\n await assertRejects(\n async () => {\n await fetch('http://127.0.0.1');\n },\n DOMException,\n 'Connection refused',\n );\n\n await assertRejects(\n async () => {\n await fetch('https://fastly.com/', {\n backend: new Backend({\n name: 'b1',\n target: 'fastly.com:8080',\n }),\n });\n },\n DOMException,\n 'Connection refused',\n );\n\n await assertRejects(\n async () => {\n await fetch('https://fastly.com', {\n backend: new Backend({\n name: 'b3',\n target: 'fastly.com',\n useSSL: true,\n certificateHostname: 'google.com',\n }),\n });\n },\n DOMException,\n 'TLS certificate error',\n );\n});\n", "export const getGeolocationForIpAddress = globalThis.fastly.getGeolocationForIpAddress;", "/* eslint-env serviceworker */\n/* global fastly */\nimport { assert, assertThrows, strictEqual } from './assertions.js';\nimport { getGeolocationForIpAddress } from 'fastly:geolocation';\nimport { isRunningLocally, routes } from './routes.js';\n\nroutes.set('/fastly/getgeolocationforipaddress/interface', async function () {\n let actual = Reflect.getOwnPropertyDescriptor(\n fastly,\n 'getGeolocationForIpAddress',\n );\n let expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: fastly.getGeolocationForIpAddress,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(fastly, 'getGeolocationForIpAddress)`,\n );\n\n assert(\n typeof fastly.getGeolocationForIpAddress,\n 'function',\n `typeof fastly.getGeolocationForIpAddress`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n fastly.getGeolocationForIpAddress,\n 'length',\n );\n expected = {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(fastly.getGeolocationForIpAddress, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n fastly.getGeolocationForIpAddress,\n 'name',\n );\n expected = {\n value: 'getGeolocationForIpAddress',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(fastly.getGeolocationForIpAddress, 'name')`,\n );\n});\n\nroutes.set(\n '/fastly/getgeolocationforipaddress/called-as-constructor',\n async () => {\n assertThrows(\n () => {\n new fastly.getGeolocationForIpAddress('1.2.3.4');\n },\n TypeError,\n `fastly.getGeolocationForIpAddress is not a constructor`,\n );\n },\n);\n// https://tc39.es/ecma262/#sec-tostring\nroutes.set(\n '/fastly/getgeolocationforipaddress/parameter-calls-7.1.17-ToString',\n async () => {\n let sentinel;\n const test = () => {\n sentinel = Symbol();\n const key = {\n toString() {\n throw sentinel;\n },\n };\n fastly.getGeolocationForIpAddress(key);\n };\n assertThrows(test);\n try {\n test();\n } catch (thrownError) {\n assert(thrownError, sentinel, 'thrownError === sentinel');\n }\n assertThrows(\n () => {\n fastly.getGeolocationForIpAddress(Symbol());\n },\n Error,\n `can't convert symbol to string`,\n );\n },\n);\nroutes.set(\n '/fastly/getgeolocationforipaddress/parameter-not-supplied',\n async () => {\n assertThrows(\n () => {\n fastly.getGeolocationForIpAddress();\n },\n TypeError,\n `fastly.getGeolocationForIpAddress: At least 1 argument required, but only 0 passed`,\n );\n },\n);\nroutes.set(\n '/fastly/getgeolocationforipaddress/parameter-empty-string',\n async () => {\n assertThrows(\n () => {\n fastly.getGeolocationForIpAddress('');\n },\n Error,\n `Invalid address passed to fastly.getGeolocationForIpAddress`,\n );\n },\n);\n\nlet geoFields = [\n 'as_name',\n 'as_number',\n 'area_code',\n 'city',\n 'conn_speed',\n 'conn_type',\n 'continent',\n 'country_code',\n 'country_code3',\n 'country_name',\n 'gmt_offset',\n 'latitude',\n 'longitude',\n 'metro_code',\n 'postal_code',\n 'proxy_description',\n 'proxy_type',\n 'region',\n 'utc_offset',\n];\n\nroutes.set('/fastly/getgeolocationforipaddress/bad-ip', async () => {\n let geo = fastly.getGeolocationForIpAddress('0.0.0.0');\n if (isRunningLocally()) {\n strictEqual(\n geo,\n null,\n `fastly.getGeolocationForIpAddress('0.0.0.0') == null`,\n );\n } else {\n strictEqual(geo.as_name, '?');\n }\n assertThrows(() => {\n fastly.getGeolocationForIpAddress('999.999.999.999');\n }, Error);\n});\n\nroutes.set(\n '/fastly/getgeolocationforipaddress/parameter-ipv4-string',\n async () => {\n let geo = fastly.getGeolocationForIpAddress('151.101.1.1');\n if (isRunningLocally()) {\n strictEqual(geo, null);\n } else {\n assert(\n Object.keys(geo),\n geoFields,\n `Object.keys(fastly.getGeolocationForIpAddress('151.101.1.1')) == geoFields`,\n );\n }\n },\n);\n\nroutes.set(\n '/fastly/getgeolocationforipaddress/parameter-compressed-ipv6-string',\n async () => {\n if (isRunningLocally()) {\n let geo = fastly.getGeolocationForIpAddress('2607:f0d0:1002:51::4');\n assert(\n Object.keys(geo),\n geoFields,\n `Object.keys(fastly.getGeolocationForIpAddress('2607:f0d0:1002:51::4')) == geoFields`,\n );\n }\n },\n);\nroutes.set(\n '/fastly/getgeolocationforipaddress/parameter-shortened-ipv6-string',\n async () => {\n if (isRunningLocally()) {\n let geo = fastly.getGeolocationForIpAddress(\n '2607:f0d0:1002:0051:0:0:0:0004',\n );\n assert(\n Object.keys(geo),\n geoFields,\n `Object.keys(fastly.getGeolocationForIpAddress('2607:f0d0:1002:0051:0:0:0:0004')) == geoFields`,\n );\n }\n },\n);\nroutes.set(\n '/fastly/getgeolocationforipaddress/parameter-expanded-ipv6-string',\n async () => {\n if (isRunningLocally()) {\n let geo = fastly.getGeolocationForIpAddress(\n '2607:f0d0:1002:0051:0000:0000:0000:0004',\n );\n assert(\n Object.keys(geo),\n geoFields,\n `Object.keys(fastly.getGeolocationForIpAddress('2607:f0d0:1002:0051:0000:0000:0000:0004')) == geoFields`,\n );\n }\n },\n);\n\nroutes.set('/fastly/getgeolocationforipaddress/called-unbound', async () => {\n if (isRunningLocally()) {\n let geo = fastly.getGeolocationForIpAddress.call(\n undefined,\n '2607:f0d0:1002:0051:0000:0000:0000:0004',\n );\n assert(\n Object.keys(geo),\n geoFields,\n `Object.keys(fastly.getGeolocationForIpAddress('2607:f0d0:1002:0051:0000:0000:0000:0004')) == geoFields`,\n );\n }\n});\n\nroutes.set('/fastly:geolocation', async () => {\n assert(\n getGeolocationForIpAddress,\n fastly.getGeolocationForIpAddress,\n 'getGeolocationForIpAddress === fastly.getGeolocationForIpAddress',\n );\n});\n", "/* eslint-env serviceworker */\n\nimport { routes } from './routes.js';\nimport { assert } from './assertions.js';\nimport { CacheOverride } from 'fastly:cache-override';\n\nroutes.set('/headers/construct', async () => {\n const headers = new Headers();\n headers.set('foo', 'bar');\n return new Response('check headers', { headers });\n});\n\nroutes.set('/headers/getsetcookie', async () => {\n let response = await fetch(\n 'https://http-me.glitch.me/meow?header=Set-Cookie:name1=value1',\n {\n backend: 'httpme',\n },\n );\n response.headers.append('Set-Cookie', 'name2=value2');\n console.log(\n 'response.headers.getSetCookie()',\n response.headers.getSetCookie(),\n );\n});\n\nroutes.set('/headers/from-response/set', async () => {\n const response = await fetch('https://httpbin.org/stream-bytes/11', {\n backend: 'httpbin',\n cacheOverride: new CacheOverride('pass'),\n });\n response.headers.set('cuStom', 'test');\n return response;\n});\n\nroutes.set('/headers/from-response/delete-invalid', async () => {\n const response = await fetch('https://httpbin.org/stream-bytes/11', {\n backend: 'httpbin',\n cacheOverride: new CacheOverride('pass'),\n });\n response.headers.delete('none');\n return response;\n});\n\nroutes.set('/headers/from-response/set-delete', async () => {\n const response = await fetch('https://httpbin.org/stream-bytes/11', {\n backend: 'httpbin',\n cacheOverride: new CacheOverride('pass'),\n });\n response.headers.set('custom', 'test');\n response.headers.delete('access-control-allow-origin');\n return response;\n});\n", "export const HTMLRewritingStream = globalThis.HTMLRewritingStream;", "/* eslint-env serviceworker */\n\nimport { routes } from './routes.js';\nimport { HTMLRewritingStream } from 'fastly:html-rewriter';\nimport {\n assert,\n assertThrows,\n assertRejects,\n strictEqual,\n} from './assertions.js';\n\nroutes.set('/html-rewriter/set-attribute', async () => {\n const toRewrite =\n 'Test

Hello, World!

';\n const expected =\n 'Test

Hello, World!

';\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('h1', (e) => {\n e.setAttribute('class', 'a-rewritten-class');\n e.setAttribute('id', 'a-rewritten-id');\n e.setAttribute('custom-attr', 'custom-value');\n }),\n );\n let text = await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(text, expected);\n});\n\nroutes.set('/html-rewriter/get-attribute', async () => {\n const toRewrite =\n 'Test

Hello, World!

';\n let classAttr, idAttr;\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('h1', (e) => {\n classAttr = e.getAttribute('class');\n idAttr = e.getAttribute('id');\n }),\n );\n await new Response(body, { headers: { 'Content-Type': 'text/html' } }).text();\n strictEqual(classAttr, 'a-class');\n strictEqual(idAttr, 'an-id');\n});\n\nroutes.set('/html-rewriter/remove-attribute', async () => {\n const toRewrite =\n 'Test

Hello, World!

';\n const expected =\n 'Test

Hello, World!

';\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('h1', (e) => {\n e.removeAttribute('class');\n e.removeAttribute('id');\n e.removeAttribute('custom-attr');\n }),\n );\n let text = await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(text, expected);\n});\n\nroutes.set('/html-rewriter/replace-with', async () => {\n const toRewrite =\n 'Test

Hello, World!

';\n const expected =\n 'Test

Goodbye, World!

';\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('h1', (e) => {\n e.replaceWith('

Goodbye, World!

');\n }),\n );\n let text = await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(text, expected);\n});\n\nroutes.set('/html-rewriter/replace-children', async () => {\n const toRewrite =\n 'Test

Hello, World!

';\n const expected =\n 'Test

Goodbye, World!

';\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('h1', (e) => {\n e.replaceChildren('Goodbye, World!');\n }),\n );\n let text = await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(text, expected);\n});\n\nroutes.set('/html-rewriter/insert', async () => {\n const toRewrite =\n 'Test

Hello, World!

';\n const expected =\n 'TestBefore -

Prefix - Hello, World! - Suffix

- After';\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('h1', (e) => {\n e.before('Before - ');\n e.prepend('Prefix - ');\n e.append(' - Suffix');\n e.after(' - After');\n }),\n );\n let text = await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(text, expected);\n});\n\nroutes.set('/html-rewriter/complex-selector', async () => {\n const toRewrite =\n \"Test

Hello, World!

Hello again, World!

\";\n const expected =\n \"Test

Hello, World!

Hello again, World!

\";\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('div.a-class > h1[id^=\"an\"]', (e) => {\n e.setAttribute('custom-attr', 'custom-value');\n }),\n );\n let text = await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(text, expected);\n});\n\nroutes.set('/html-rewriter/no-match-rewritten-content', async () => {\n const toRewrite =\n 'Test
';\n const expected =\n 'Test

Hello, World!

';\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream()\n .onElement('div', (e) => {\n e.setAttribute('class', 'a-class');\n e.append('

Hello, World!

');\n })\n .onElement('h1', (e) => {\n // should not be called, as h1 does not exist in original content\n e.setAttribute('custom-attr', 'custom-value');\n }),\n );\n let text = await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(text, expected);\n});\n\nroutes.set('/html-rewriter/multiple-handlers', async () => {\n const toRewrite =\n \"Test

Hello, World!

Hello again, World!

\";\n const expected =\n 'Test

Hello, World!

Hello again, World!

';\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream()\n .onElement('div.a-class', (e) => {\n e.setAttribute('custom-attr', 'custom-value');\n })\n .onElement('h1', (e) => {\n e.setAttribute('another-attr', 'another-value');\n }),\n );\n let text = await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(text, expected);\n});\n\nroutes.set('/html-rewriter/invalid-selector', async () => {\n assertThrows(() => {\n new HTMLRewritingStream().onElement('div..a-class', (e) => {\n e.setAttribute('custom-attr', 'custom-value');\n });\n }, Error);\n});\n\nroutes.set('/html-rewriter/invalid-handler', async () => {\n assertThrows(() => {\n new HTMLRewritingStream().onElement(\n 'div.a-class',\n 'this is not a function',\n );\n }, Error);\n});\n\nroutes.set('/html-rewriter/throw-in-handler', async () => {\n const toRewrite =\n \"Test

Hello, World!

\";\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('div.a-class', (e) => {\n throw new Error('This is an error from the handler');\n }),\n );\n assertRejects(async () => {\n await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n }, Error);\n});\n\nroutes.set('/html-rewriter/invalid-html', async () => {\n const toRewrite = 'This is not HTML content';\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('div.a-class', (e) => {\n e.setAttribute('custom-attr', 'custom-value');\n }),\n );\n assertRejects(async () => {\n await new Response(body, {\n headers: { 'Content-Type': 'text/plain' },\n }).text();\n }, Error);\n});\n\nroutes.set('/html-rewriter/insertion-order', async () => {\n const toRewrite =\n 'Test

Hello, World!

';\n const expected =\n 'TestFirst - Before -

Prefix - Other Prefix - Hello, World! - Suffix - Other Suffix

- After - Last';\n let body = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('h1', (e) => {\n e.before('First - ');\n e.before('Before - ');\n // The insertion position is maintained, so prepends are inserted in reverse order\n e.prepend('Other Prefix - ');\n e.prepend('Prefix - ');\n e.append(' - Suffix');\n e.append(' - Other Suffix');\n // The insertion position is maintained, so appends are inserted in reverse order\n e.after(' - Last');\n e.after(' - After');\n }),\n );\n let text = await new Response(body, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(text, expected);\n});\n\nroutes.set('/html-rewriter/escape-html', async () => {\n const toRewrite =\n 'Test

Hello, World!

';\n const expectedNoEscape =\n 'Test

Hello, Beautiful World!

';\n const expectedEscape =\n 'Test

Hello, <strong>Beautiful</strong> World!

';\n let bodyNoEscape = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('em', (e) => {\n e.before('Beautiful ', { escapeHTML: false });\n }),\n );\n let textNoEscape = await new Response(bodyNoEscape, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(textNoEscape, expectedNoEscape);\n\n let bodyEscape = new Response(toRewrite, {\n headers: { 'Content-Type': 'text/html' },\n }).body.pipeThrough(\n new HTMLRewritingStream().onElement('em', (e) => {\n e.before('Beautiful ', { escapeHTML: true });\n }),\n );\n let textEscape = await new Response(bodyEscape, {\n headers: { 'Content-Type': 'text/html' },\n }).text();\n strictEqual(textEscape, expectedEscape);\n});\n", "import { routes } from './routes.js';\nimport { assert } from './assertions.js';\nimport { includeBytes } from 'fastly:experimental';\n\nlet message;\ntry {\n message = includeBytes('message.txt');\n} catch {}\n\nconst expected = [\n 104, 101, 108, 108, 111, 32, 105, 110, 99, 108, 117, 100, 101, 66, 121, 116,\n 101, 115, 10,\n];\n\nroutes.set('/includeBytes', () => {\n assert(Array.from(message), expected, `message === expected`);\n});\n", "export const { \n Region, Auto, Format, BWAlgorithm, Disable, Enable, Fit, Metadata, \n Optimize, Orient, Profile, ResizeFilter, CropMode, optionsToQueryString\n } = globalThis.fastly.imageOptimizer;", "/* eslint-env serviceworker */\n\nimport { routes } from './routes.js';\nimport {\n Region,\n Auto,\n BWAlgorithm,\n CropMode,\n Disable,\n Enable,\n Fit,\n Metadata,\n Optimize,\n Orient,\n Profile,\n ResizeFilter,\n optionsToQueryString,\n} from 'fastly:image-optimizer';\nimport { assert, assertThrows } from './assertions.js';\n\n// Enums\nroutes.set('/image-optimizer/options/region', () => {\n assert(optionsToQueryString({ region: Region.UsEast }), 'region=us_east');\n assert(optionsToQueryString({ region: Region.Asia }), 'region=asia');\n assertThrows(() => optionsToQueryString({ region: 'invalid' }), TypeError);\n assertThrows(() => optionsToQueryString({}), TypeError);\n});\nroutes.set('/image-optimizer/options/auto', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, auto: Auto.AVIF }),\n 'region=asia&auto=avif',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, auto: Auto.WEBP }),\n 'region=asia&auto=webp',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, auto: 'invalid' }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/bw', () => {\n assert(\n optionsToQueryString({\n region: Region.EuCentral,\n bw: BWAlgorithm.Threshold,\n }),\n 'region=eu_central&bw=threshold',\n );\n assert(\n optionsToQueryString({\n region: Region.EuCentral,\n bw: BWAlgorithm.Atkinson,\n }),\n 'region=eu_central&bw=atkinson',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.EuCentral, bw: 'invalid' }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/crop-mode', () => {\n const qs = optionsToQueryString({\n region: Region.UsWest,\n crop: {\n size: { absolute: { width: 100, height: 100 } },\n mode: CropMode.Smart,\n },\n });\n assert(qs.includes('smart'), true);\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.UsWest,\n crop: { size: { absolute: { width: 100, height: 100 } }, mode: 'bad' },\n }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/disable', () => {\n assert(\n optionsToQueryString({\n region: Region.Australia,\n disable: Disable.Upscale,\n }),\n 'region=australia&disable=upscale',\n );\n assertThrows(() =>\n optionsToQueryString({ region: Region.Australia, disable: 'invalid' }),\n );\n});\nroutes.set('/image-optimizer/options/enable', () => {\n assert(\n optionsToQueryString({ region: Region.Australia, enable: Enable.Upscale }),\n 'region=australia&enable=upscale',\n );\n assertThrows(() =>\n optionsToQueryString({ region: Region.Australia, enable: 'invalid' }),\n );\n});\nroutes.set('/image-optimizer/options/fit', () => {\n assert(\n optionsToQueryString({ region: Region.Australia, fit: Fit.Crop }),\n 'region=australia&fit=crop',\n );\n assert(\n optionsToQueryString({ region: Region.Australia, fit: Fit.Cover }),\n 'region=australia&fit=cover',\n );\n assertThrows(() =>\n optionsToQueryString({ region: Region.Australia, fit: 'invalid' }),\n );\n});\nroutes.set('/image-optimizer/options/metadata', () => {\n assert(\n optionsToQueryString({ region: Region.Australia, metadata: Metadata.C2PA }),\n 'region=australia&metadata=c2pa',\n );\n assert(\n optionsToQueryString({\n region: Region.Australia,\n metadata: Metadata.CopyrightAndC2PA,\n }),\n 'region=australia&metadata=copyright,c2pa',\n );\n assertThrows(() =>\n optionsToQueryString({ region: Region.Australia, metadata: 'invalid' }),\n );\n});\nroutes.set('/image-optimizer/options/optimize', () => {\n assert(\n optionsToQueryString({ region: Region.Australia, optimize: Optimize.Low }),\n 'region=australia&optimize=low',\n );\n assert(\n optionsToQueryString({ region: Region.Australia, optimize: Optimize.High }),\n 'region=australia&optimize=high',\n );\n assertThrows(() =>\n optionsToQueryString({ region: Region.Australia, optimize: 'invalid' }),\n );\n});\nroutes.set('/image-optimizer/options/orient', () => {\n assert(\n optionsToQueryString({ region: Region.Australia, orient: Orient.Default }),\n 'region=australia&orient=1',\n );\n assert(\n optionsToQueryString({\n region: Region.Australia,\n orient: Orient.FlipHorizontalOrientRight,\n }),\n 'region=australia&orient=7',\n );\n assertThrows(() =>\n optionsToQueryString({ region: Region.Australia, orient: 'invalid' }),\n );\n});\nroutes.set('/image-optimizer/options/profile', () => {\n assert(\n optionsToQueryString({\n region: Region.Australia,\n profile: Profile.Baseline,\n }),\n 'region=australia&profile=baseline',\n );\n assert(\n optionsToQueryString({ region: Region.Australia, profile: Profile.Main }),\n 'region=australia&profile=main',\n );\n assertThrows(() =>\n optionsToQueryString({ region: Region.Australia, profile: 'invalid' }),\n );\n});\nroutes.set('/image-optimizer/options/resizeFilter', () => {\n assert(\n optionsToQueryString({\n region: Region.Australia,\n resizeFilter: ResizeFilter.Bicubic,\n }),\n 'region=australia&resize-filter=bicubic',\n );\n assert(\n optionsToQueryString({\n region: Region.Australia,\n resizeFilter: ResizeFilter.Lanczos2,\n }),\n 'region=australia&resize-filter=lanczos2',\n );\n assertThrows(() =>\n optionsToQueryString({ region: Region.Australia, resizeFilter: 'invalid' }),\n );\n});\n\n// Other options\nroutes.set('/image-optimizer/options/bgColor', () => {\n // Hex strings\n assert(\n optionsToQueryString({\n region: Region.Asia,\n bgColor: '123456',\n }),\n 'region=asia&bg-color=123456',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n bgColor: 'a2345e',\n }),\n 'region=asia&bg-color=a2345e',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n bgColor: '123',\n }),\n 'region=asia&bg-color=123',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n bgColor: 'a2e',\n }),\n 'region=asia&bg-color=a2e',\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n bgColor: '12',\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n bgColor: '12j',\n }),\n TypeError,\n );\n\n // RGB(A)\n assert(\n optionsToQueryString({\n region: Region.Asia,\n bgColor: {\n r: 255,\n g: 0,\n b: 128,\n },\n }),\n 'region=asia&bg-color=255,0,128',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n bgColor: {\n r: 255,\n g: 0,\n b: 128,\n a: 0.5,\n },\n }),\n 'region=asia&bg-color=255,0,128,0.500000',\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n bgColor: {\n r: 12,\n b: 12,\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n bgColor: {\n r: 12,\n b: 1212,\n g: 12,\n },\n }),\n TypeError,\n );\n\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n bgColor: 123123,\n }),\n TypeError,\n );\n});\n\nroutes.set('/image-optimizer/options/blur', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, blur: 0.5 }),\n 'region=asia&blur=0.500000',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, blur: 1000 }),\n 'region=asia&blur=1000.000000',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, blur: 1001 }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, blur: 0.4 }),\n TypeError,\n );\n\n assert(\n optionsToQueryString({ region: Region.Asia, blur: '10%' }),\n 'region=asia&blur=10.000000p',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, blur: '0.5%' }),\n 'region=asia&blur=0.500000p',\n );\n});\nroutes.set('/image-optimizer/options/brightness', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, brightness: 0.5 }),\n 'region=asia&brightness=0.500000',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, brightness: 100 }),\n 'region=asia&brightness=100.000000',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, brightness: -100 }),\n 'region=asia&brightness=-100.000000',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, brightness: 1001 }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, brightness: -101 }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/canvas', () => {\n assert(\n optionsToQueryString({\n region: Region.Asia,\n canvas: {\n size: {\n absolute: {\n width: 100,\n height: '10%',\n },\n },\n },\n }),\n 'region=asia&canvas=100,10.000000p',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n canvas: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n },\n }),\n 'region=asia&canvas=4.000000:3.000000',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n canvas: {\n size: {\n absolute: {\n width: 100,\n height: '10%',\n },\n },\n position: {\n x: 10,\n offsetY: 10,\n },\n },\n }),\n 'region=asia&canvas=100,10.000000p,x10,offset-y10.000000',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n canvas: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n },\n }),\n 'region=asia&canvas=4.000000:3.000000,offset-x10.000000,y10.000000p',\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n canvas: {\n position: {\n offsetX: 10,\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n canvas: {\n size: {\n ratio: {\n width: '4%',\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n canvas: {\n size: {\n ratio: {\n width: '4%',\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n x: 100,\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n canvas: {\n size: {\n ratio: {\n width: '4%',\n height: 3,\n },\n },\n position: {\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/crop', () => {\n assert(\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n size: {\n absolute: {\n width: 100,\n height: '10%',\n },\n },\n },\n }),\n 'region=asia&crop=100,10.000000p',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n },\n }),\n 'region=asia&crop=4.000000:3.000000',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n size: {\n absolute: {\n width: 100,\n height: '10%',\n },\n },\n position: {\n x: 10,\n offsetY: 10,\n },\n },\n }),\n 'region=asia&crop=100,10.000000p,x10,offset-y10.000000',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n },\n }),\n 'region=asia&crop=4.000000:3.000000,offset-x10.000000,y10.000000p',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n mode: CropMode.Safe,\n },\n }),\n 'region=asia&crop=4.000000:3.000000,offset-x10.000000,y10.000000p,safe',\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n position: {\n offsetX: 10,\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n size: {\n ratio: {\n width: '4%',\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n size: {\n ratio: {\n width: '4%',\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n x: 100,\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n size: {\n ratio: {\n width: '4%',\n height: 3,\n },\n },\n position: {\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n crop: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n mode: 'invalid',\n },\n }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/precrop', () => {\n assert(\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n size: {\n absolute: {\n width: 100,\n height: '10%',\n },\n },\n },\n }),\n 'region=asia&precrop=100,10.000000p',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n },\n }),\n 'region=asia&precrop=4.000000:3.000000',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n size: {\n absolute: {\n width: 100,\n height: '10%',\n },\n },\n position: {\n x: 10,\n offsetY: 10,\n },\n },\n }),\n 'region=asia&precrop=100,10.000000p,x10,offset-y10.000000',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n },\n }),\n 'region=asia&precrop=4.000000:3.000000,offset-x10.000000,y10.000000p',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n mode: CropMode.Safe,\n },\n }),\n 'region=asia&precrop=4.000000:3.000000,offset-x10.000000,y10.000000p,safe',\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n position: {\n offsetX: 10,\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n size: {\n ratio: {\n width: '4%',\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n size: {\n ratio: {\n width: '4%',\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n x: 100,\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n size: {\n ratio: {\n width: '4%',\n height: 3,\n },\n },\n position: {\n y: '10%',\n },\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n precrop: {\n size: {\n ratio: {\n width: 4,\n height: 3,\n },\n },\n position: {\n offsetX: 10,\n y: '10%',\n },\n mode: 'invalid',\n },\n }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/dpr', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, dpr: 1.5 }),\n 'region=asia&dpr=1.500000',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, dpr: 10 }),\n 'region=asia&dpr=10.000000',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, dpr: '1001' }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, dpr: 1001 }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, dpr: 0.5 }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/frame', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, frame: 1 }),\n 'region=asia&frame=1',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, frame: 2 }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/height', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, height: 1000 }),\n 'region=asia&height=1000',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, height: '10%' }),\n 'region=asia&height=10.000000p',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, height: '1001' }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, height: 100.5 }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/level', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, level: '1.1' }),\n 'region=asia&level=1.1',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, level: '5.1' }),\n 'region=asia&level=5.1',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, level: 5.1 }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, level: '7.1' }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/pad', () => {\n assert(\n optionsToQueryString({\n region: Region.Asia,\n pad: {\n top: 10,\n bottom: 20,\n left: '10%',\n right: '20%',\n },\n }),\n 'region=asia&pad=10,20.000000p,20,10.000000p',\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n pad: {\n top: 10,\n bottom: 20,\n left: '10',\n right: '20%',\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n pad: {\n top: 10,\n left: '10',\n right: '20%',\n },\n }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/quality', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, quality: 1 }),\n 'region=asia&quality=1',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, quality: 100 }),\n 'region=asia&quality=100',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, quality: 1001 }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, quality: 1.5 }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, quality: 0.4 }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/saturation', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, saturation: 1 }),\n 'region=asia&saturation=1.000000',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, saturation: 100 }),\n 'region=asia&saturation=100.000000',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, saturation: -100 }),\n 'region=asia&saturation=-100.000000',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, saturation: 1001 }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, saturation: -101 }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/sharpen', () => {\n assert(\n optionsToQueryString({\n region: Region.Asia,\n sharpen: {\n amount: 10,\n radius: 10,\n threshold: 1,\n },\n }),\n 'region=asia&sharpen=a10.000000,r10.000000,t1',\n );\n assert(\n optionsToQueryString({\n region: Region.Asia,\n sharpen: {\n amount: 0.1,\n radius: 0.5,\n threshold: 255,\n },\n }),\n 'region=asia&sharpen=a0.100000,r0.500000,t255',\n );\n assertThrows(() => {\n optionsToQueryString({\n region: Region.Asia,\n sharpen: {\n amount: 0.1,\n radius: 0.5,\n threshold: 256,\n },\n });\n }, TypeError);\n assertThrows(() => {\n optionsToQueryString({\n region: Region.Asia,\n sharpen: {\n amount: 0.1,\n radius: 0.4,\n threshold: 255,\n },\n });\n }, TypeError);\n assertThrows(() => {\n optionsToQueryString({\n region: Region.Asia,\n sharpen: {\n amount: -1,\n radius: 0.5,\n threshold: 255,\n },\n });\n }, TypeError);\n assertThrows(() => {\n optionsToQueryString({\n region: Region.Asia,\n sharpen: {\n amount: 1,\n radius: 1,\n },\n });\n }, TypeError);\n});\nroutes.set('/image-optimizer/options/trim', () => {\n assert(\n optionsToQueryString({\n region: Region.Asia,\n trim: {\n top: 10,\n bottom: 20,\n left: '10%',\n right: '20%',\n },\n }),\n 'region=asia&trim=10,20.000000p,20,10.000000p',\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n trim: {\n top: 10,\n bottom: 20,\n left: '10',\n right: '20%',\n },\n }),\n TypeError,\n );\n assertThrows(\n () =>\n optionsToQueryString({\n region: Region.Asia,\n trim: {\n top: 10,\n left: '10',\n right: '20%',\n },\n }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/viewbox', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, viewbox: 1 }),\n 'region=asia&viewbox=1',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, viewbox: 2 }),\n TypeError,\n );\n});\nroutes.set('/image-optimizer/options/width', () => {\n assert(\n optionsToQueryString({ region: Region.Asia, width: 1000 }),\n 'region=asia&width=1000',\n );\n assert(\n optionsToQueryString({ region: Region.Asia, width: '10%' }),\n 'region=asia&width=10.000000p',\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, width: '1001' }),\n TypeError,\n );\n assertThrows(\n () => optionsToQueryString({ region: Region.Asia, width: 100.5 }),\n TypeError,\n );\n});\n", "export const Logger = globalThis.Logger;\nexport const configureConsole = Logger.configureConsole;\ndelete globalThis.Logger.configureConsole;\n", "import { Logger, configureConsole } from 'fastly:logger';\nimport { routes, isRunningLocally } from './routes';\n\nconfigureConsole({ prefixing: false, stderr: true });\n\nconst earlyLogger = new Logger('AnotherLog');\n\nroutes.set('/logger', () => {\n if (isRunningLocally()) {\n let logger = new Logger('ComputeLog');\n logger.log('Hello!');\n earlyLogger.log('World!');\n }\n\n console.log('LOG');\n console.error('ERROR');\n\n return new Response();\n});\n", "/// \n/* eslint-env serviceworker */\n\nimport { CacheOverride } from 'fastly:cache-override';\nimport { assert, assertRejects } from './assertions.js';\nimport { routes, isRunningLocally } from './routes.js';\n\nasync function requestInitObjectLiteral(manualFramingHeaders) {\n let request = new Request('https://http-me.glitch.me/anything', {\n backend: 'httpme',\n method: 'POST',\n body: 'meow',\n manualFramingHeaders,\n headers: {\n 'content-length': '1',\n },\n });\n let response = await fetch(request);\n let body = await response.json();\n return body?.headers?.['content-length'];\n}\n\nasync function requestMethod(manualFramingHeaders) {\n let request = new Request('https://http-me.glitch.me/anything', {\n backend: 'httpme',\n method: 'POST',\n body: 'meow',\n headers: {\n 'content-length': '1',\n },\n });\n request.setManualFramingHeaders(manualFramingHeaders);\n let response = await fetch(request);\n let body = await response.json();\n return body?.headers?.['content-length'];\n}\n\nasync function requestClone(manualFramingHeaders) {\n let request = new Request('https://http-me.glitch.me/anything', {\n backend: 'httpme',\n method: 'POST',\n body: 'meow',\n manualFramingHeaders,\n headers: {\n 'content-length': '1',\n },\n });\n let response = await fetch(request.clone());\n let body = await response.json();\n return body?.headers?.['content-length'];\n}\n\nasync function fetchInitObjectLiteral(manualFramingHeaders) {\n let response = await fetch('https://http-me.glitch.me/anything', {\n backend: 'httpme',\n method: 'POST',\n body: 'meow',\n manualFramingHeaders,\n headers: {\n 'content-length': '1',\n },\n });\n let body = await response.json();\n return body?.headers?.['content-length'];\n}\n\nroutes.set(\n '/override-content-length/request/init/object-literal/true',\n async () => {\n if (isRunningLocally()) {\n await assertRejects(() => requestInitObjectLiteral(true));\n } else {\n let actual = await requestInitObjectLiteral(true);\n let expected = '1';\n assert(actual, expected, `await requestInitObjectLiteral(true)`);\n }\n },\n);\n\nroutes.set(\n '/override-content-length/request/init/object-literal/false',\n async () => {\n let actual = await requestInitObjectLiteral(false);\n let expected = '4';\n assert(actual, expected, `await requestInitObjectLiteral(false)`);\n },\n);\n\nroutes.set(\n '/override-content-length/request/method/object-literal/true',\n async () => {\n if (isRunningLocally()) {\n await assertRejects(() => requestMethod(true));\n } else {\n let actual = await requestMethod(true);\n let expected = '1';\n assert(actual, expected, `await requestMethod(true)`);\n }\n },\n);\n\nroutes.set(\n '/override-content-length/request/method/object-literal/false',\n async () => {\n let actual = await requestMethod(false);\n let expected = '4';\n assert(actual, expected, `await requestMethod(false)`);\n },\n);\n\nroutes.set('/override-content-length/request/clone/true', async () => {\n if (isRunningLocally()) {\n await assertRejects(() => requestClone(true));\n } else {\n let actual = await requestClone(true);\n let expected = '1';\n assert(actual, expected, `await requestClone(true)`);\n }\n});\n\nroutes.set('/override-content-length/request/clone/false', async () => {\n let actual = await requestClone(false);\n let expected = '4';\n assert(actual, expected, `await requestClone(false)`);\n});\n\nroutes.set(\n '/override-content-length/fetch/init/object-literal/true',\n async () => {\n if (isRunningLocally()) {\n await assertRejects(() => fetchInitObjectLiteral(true));\n } else {\n let actual = await fetchInitObjectLiteral(true);\n let expected = '1';\n assert(actual, expected, `await fetchInitObjectLiteral(true)`);\n }\n },\n);\n\nroutes.set(\n '/override-content-length/fetch/init/object-literal/false',\n async () => {\n let actual = await fetchInitObjectLiteral(false);\n let expected = '4';\n assert(actual, expected, `await fetchInitObjectLiteral(false)`);\n },\n);\n\nasync function responseInitObjectLiteral(manualFramingHeaders) {\n let response = new Response(\n new ReadableStream({\n start(controller) {\n controller.enqueue(new TextEncoder().encode('meow'));\n controller.close();\n },\n }),\n {\n manualFramingHeaders,\n headers: {\n 'content-length': '4',\n },\n },\n );\n return response;\n}\n\nasync function responseInitresponseInstance(manualFramingHeaders) {\n let response = new Response(\n new ReadableStream({\n start(controller) {\n controller.enqueue(new TextEncoder().encode('meow'));\n controller.close();\n },\n }),\n {\n manualFramingHeaders,\n headers: {\n 'content-length': '4',\n },\n },\n );\n response = new Response(\n new ReadableStream({\n start(controller) {\n controller.enqueue(new TextEncoder().encode('meow'));\n controller.close();\n },\n }),\n response,\n );\n return response;\n}\n\nroutes.set(\n '/override-content-length/response/init/object-literal/true',\n async () => {\n return responseInitObjectLiteral(true);\n },\n);\n\nroutes.set(\n '/override-content-length/response/init/object-literal/false',\n async () => {\n return responseInitObjectLiteral(false);\n },\n);\n\nroutes.set(\n '/override-content-length/response/init/response-instance/true',\n async () => {\n return responseInitresponseInstance(true);\n },\n);\n\nroutes.set(\n '/override-content-length/response/init/response-instance/false',\n async () => {\n return responseInitresponseInstance(false);\n },\n);\n\nasync function responseMethod(setManualFramingHeaders) {\n const response = await fetch('https://httpbin.org/stream-bytes/11', {\n backend: 'httpbin',\n cacheOverride: new CacheOverride('pass'),\n });\n response.setManualFramingHeaders(setManualFramingHeaders);\n response.headers.set('content-length', '11');\n response.headers.delete('transfer-encoding');\n return response;\n}\n\nroutes.set('/override-content-length/response/method/false', async () => {\n return responseMethod(false);\n});\n\nroutes.set('/override-content-length/response/method/true', async () => {\n return responseMethod(true);\n});\n\nasync function responseClone(setManualFramingHeaders) {\n let response = new Response('meow', {\n backend: 'httpme',\n method: 'POST',\n body: 'meow',\n setManualFramingHeaders,\n headers: {\n 'transfer-encoding': 'chunked',\n },\n });\n response = response.clone();\n return response;\n}\n\n// TODO: implement Response.clone\nroutes.set('/override-content-length/response/clone/true', async () => {\n return responseClone(true);\n});\n\n// TODO: implement Response.clone\nroutes.set('/override-content-length/response/clone/false', async () => {\n return responseClone(false);\n});\n", "import { assertRejects } from './assertions.js';\nimport { routes } from './routes.js';\n\nroutes.set('/missing-backend', async () => {\n await assertRejects(\n async () => fetch('https://example.com', { backend: 'missing' }),\n TypeError,\n `Requested backend named 'missing' does not exist`,\n );\n});\n", "import { routes } from './routes.js';\n\nroutes.set('/multiple-set-cookie/response-init', async () => {\n let h = new Headers();\n\n h.append(\n 'Set-Cookie',\n 'test=1; expires=Tue, 06-Dec-2022 12:34:56 GMT; Max-Age=60; Path=/; HttpOnly; Secure, test_id=1; Max-Age=60; Path=/; expires=Tue, 06-Dec-2022 12:34:56 GMT, test_id=1; Max-Age=60; Path=/; expires=Tue, 06-Dec-2022 12:34:56 GMT',\n );\n h.append('Set-Cookie', 'test2=2');\n h.append('Set-Cookie', 'test3=3');\n h.append('Set-Cookie', 'test4=4');\n h.append('Set-Cookie', 'test5=5');\n h.append('Set-Cookie', 'test6=6');\n h.append('Set-Cookie', 'test7=7');\n h.append('Set-Cookie', 'test8=8');\n h.append('Set-Cookie', 'test9=9');\n h.append('Set-Cookie', 'test10=10');\n h.append('Set-Cookie', 'test11=11');\n let r = new Response('Hello', {\n headers: h,\n });\n return r;\n});\nroutes.set('/multiple-set-cookie/response-direct', async () => {\n let r = new Response('Hello');\n\n r.headers.append(\n 'Set-Cookie',\n 'test=1; expires=Tue, 06-Dec-2022 12:34:56 GMT; Max-Age=60; Path=/; HttpOnly; Secure, test_id=1; Max-Age=60; Path=/; expires=Tue, 06-Dec-2022 12:34:56 GMT, test_id=1; Max-Age=60; Path=/; expires=Tue, 06-Dec-2022 12:34:56 GMT',\n );\n r.headers.append('Set-Cookie', 'test2=2');\n r.headers.append('Set-Cookie', 'test3=3');\n r.headers.append('Set-Cookie', 'test4=4');\n r.headers.append('Set-Cookie', 'test5=5');\n r.headers.append('Set-Cookie', 'test6=6');\n r.headers.append('Set-Cookie', 'test7=7');\n r.headers.append('Set-Cookie', 'test8=8');\n r.headers.append('Set-Cookie', 'test9=9');\n r.headers.append('Set-Cookie', 'test10=10');\n r.headers.append('Set-Cookie', 'test11=11');\n return r;\n});\nroutes.set('/multiple-set-cookie/downstream', async () => {\n let response = await fetch(\n 'https://httpbin.org/cookies/set?1=1&2=2&3=3&4=4&5=5&6=6&7=7&8=8&9=9&10=10&11=11',\n {\n backend: 'httpbin',\n },\n );\n\n return new Response('', response);\n});\n", "/* eslint-env serviceworker */\nimport { routes } from './routes.js';\nimport { assert, assertThrows } from './assertions.js';\n\nroutes.set('/Performance/interface', () => {\n let actual = Reflect.ownKeys(Performance);\n let expected = ['prototype', 'length', 'name'];\n assert(actual, expected, `Reflect.ownKeys(Performance)`);\n\n // Check the prototype descriptors are correct\n {\n actual = Reflect.getOwnPropertyDescriptor(Performance, 'prototype');\n expected = {\n value: Performance.prototype,\n writable: false,\n enumerable: false,\n configurable: false,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance, 'prototype')`,\n );\n }\n\n // Check the constructor function's defined parameter length is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(Performance, 'length');\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance, 'length')`,\n );\n }\n\n // Check the constructor function's name is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(Performance, 'name');\n expected = {\n value: 'Performance',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance, 'name')`,\n );\n }\n\n // Check the prototype has the correct keys\n {\n actual = Reflect.ownKeys(Performance.prototype);\n expected = ['constructor', 'timeOrigin', 'now', Symbol.toStringTag];\n assert(actual, expected, `Reflect.ownKeys(Performance.prototype)`);\n }\n\n // Check the constructor on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n Performance.prototype,\n 'constructor',\n );\n expected = {\n writable: true,\n enumerable: false,\n configurable: true,\n value: Performance.prototype.constructor,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance.prototype, 'constructor')`,\n );\n\n assert(\n typeof Performance.prototype.constructor,\n 'function',\n `typeof Performance.prototype.constructor`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n Performance.prototype.constructor,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance.prototype.constructor, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n Performance.prototype.constructor,\n 'name',\n );\n expected = {\n value: 'Performance',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance.prototype.constructor, 'name')`,\n );\n }\n\n // Check the Symbol.toStringTag on the prototype is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(\n Performance.prototype,\n Symbol.toStringTag,\n );\n expected = {\n value: 'performance',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance.prototype, [Symbol.toStringTag])`,\n );\n\n assert(\n typeof Performance.prototype[Symbol.toStringTag],\n 'string',\n `typeof Performance.prototype[Symbol.toStringTag]`,\n );\n }\n\n // Check the timeOrigin property is correct\n {\n const descriptors = Reflect.getOwnPropertyDescriptor(\n Performance.prototype,\n 'timeOrigin',\n );\n expected = { enumerable: true, configurable: true };\n assert(\n descriptors.enumerable,\n true,\n `Reflect.getOwnPropertyDescriptor(Performance, 'timeOrigin').enumerable`,\n );\n assert(\n descriptors.configurable,\n true,\n `Reflect.getOwnPropertyDescriptor(Performance, 'timeOrigin').configurable`,\n );\n assert(\n descriptors.value,\n undefined,\n `Reflect.getOwnPropertyDescriptor(Performance, 'timeOrigin').value`,\n );\n assert(\n descriptors.set,\n undefined,\n `Reflect.getOwnPropertyDescriptor(Performance, 'timeOrigin').set`,\n );\n assert(\n typeof descriptors.get,\n 'function',\n `typeof Reflect.getOwnPropertyDescriptor(Performance, 'timeOrigin').get`,\n );\n\n assert(\n typeof Performance.prototype.timeOrigin,\n 'number',\n `typeof Performance.prototype.timeOrigin`,\n );\n }\n\n // Check the now property is correct\n {\n actual = Reflect.getOwnPropertyDescriptor(Performance.prototype, 'now');\n expected = {\n writable: true,\n enumerable: true,\n configurable: true,\n value: Performance.prototype.now,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance, 'now')`,\n );\n\n assert(\n typeof Performance.prototype.now,\n 'function',\n `typeof Performance.prototype.now`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n Performance.prototype.now,\n 'length',\n );\n expected = {\n value: 0,\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance.prototype.now, 'length')`,\n );\n\n actual = Reflect.getOwnPropertyDescriptor(\n Performance.prototype.now,\n 'name',\n );\n expected = {\n value: 'now',\n writable: false,\n enumerable: false,\n configurable: true,\n };\n assert(\n actual,\n expected,\n `Reflect.getOwnPropertyDescriptor(Performance.prototype.now, 'name')`,\n );\n }\n});\n\nroutes.set('/globalThis.performance', () => {\n assert(\n globalThis.performance instanceof Performance,\n true,\n `globalThis.performance instanceof Performance`,\n );\n});\n\nroutes.set('/globalThis.performance/now', () => {\n assertThrows(() => new performance.now());\n\n assert(typeof performance.now(), 'number');\n\n assert(performance.now() > 0, true);\n\n assert(Number.isNaN(performance.now()), false);\n\n assert(Number.isFinite(performance.now()), true);\n\n assert(performance.now() < Date.now(), true);\n});\n\nroutes.set('/globalThis.performance/timeOrigin', () => {\n assert(typeof performance.timeOrigin, 'number');\n\n assert(performance.timeOrigin > 0, true);\n\n assert(Number.isNaN(performance.timeOrigin), false);\n\n assert(Number.isFinite(performance.timeOrigin), true);\n\n assert(performance.timeOrigin < Date.now(), true);\n});\n", "/* eslint-env serviceworker */\nimport { routes } from './routes.js';\nimport { assert } from './assertions.js';\n\nfunction random3Decimals() {\n return String(Math.random()).slice(0, 5);\n}\n\nlet a = random3Decimals();\nlet b = random3Decimals();\n\n// This tests can fail sporadically as it is testing randomness.\n// If it fails, rerun the tests and it should pass, if it does not\n// then we may have another bug with how we are seeding the random\n// number generator in SpiderMonkey.\nroutes.set('/random', () => {\n assert(\n a !== b,\n true,\n 'The first 4 digits were repeated in sequential calls to Math.random() during wizening\\n\\n' +\n JSON.stringify({ a, b }, undefined, 4),\n );\n\n let c = random3Decimals();\n let d = random3Decimals();\n assert(\n c !== d,\n true,\n 'The first 4 digits were repeated in sequential calls to Math.random() during request handling\\n\\n' +\n JSON.stringify({ c, d }, undefined, 4),\n );\n});\n", "/* eslint-env serviceworker */\nimport { routes } from './routes.js';\n// React\nvar React = {};\n{\n /**\n * @license React\n * react.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n let exports = React;\n let l = Symbol.for('react.element'),\n n = Symbol.for('react.portal'),\n p = Symbol.for('react.fragment'),\n q = Symbol.for('react.strict_mode'),\n r = Symbol.for('react.profiler'),\n t = Symbol.for('react.provider'),\n u = Symbol.for('react.context'),\n v = Symbol.for('react.forward_ref'),\n w = Symbol.for('react.suspense'),\n x = Symbol.for('react.memo'),\n y = Symbol.for('react.lazy'),\n z = Symbol.iterator;\n function A(a) {\n if (null === a || 'object' !== typeof a) return null;\n a = (z && a[z]) || a['@@iterator'];\n return 'function' === typeof a ? a : null;\n }\n let B = {\n isMounted: function () {\n return !1;\n },\n enqueueForceUpdate: function () {},\n enqueueReplaceState: function () {},\n enqueueSetState: function () {},\n },\n C = Object.assign,\n D = {};\n function E(a, b, e) {\n this.props = a;\n this.context = b;\n this.refs = D;\n this.updater = e || B;\n }\n E.prototype.isReactComponent = {};\n E.prototype.setState = function (a, b) {\n if ('object' !== typeof a && 'function' !== typeof a && null != a)\n throw Error(\n 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.',\n );\n this.updater.enqueueSetState(this, a, b, 'setState');\n };\n E.prototype.forceUpdate = function (a) {\n this.updater.enqueueForceUpdate(this, a, 'forceUpdate');\n };\n function F() {}\n F.prototype = E.prototype;\n function G(a, b, e) {\n this.props = a;\n this.context = b;\n this.refs = D;\n this.updater = e || B;\n }\n var H = (G.prototype = new F());\n H.constructor = G;\n C(H, E.prototype);\n H.isPureReactComponent = !0;\n var I = Array.isArray,\n J = Object.prototype.hasOwnProperty,\n K = { current: null },\n L = { key: !0, ref: !0, __self: !0, __source: !0 };\n function M(a, b, e) {\n var d,\n c = {},\n k = null,\n h = null;\n if (null != b)\n for (d in (void 0 !== b.ref && (h = b.ref),\n void 0 !== b.key && (k = '' + b.key),\n b))\n J.call(b, d) && !L.hasOwnProperty(d) && (c[d] = b[d]);\n var g = arguments.length - 2;\n if (1 === g) c.children = e;\n else if (1 < g) {\n for (var f = Array(g), m = 0; m < g; m++) f[m] = arguments[m + 2];\n c.children = f;\n }\n if (a && a.defaultProps)\n for (d in ((g = a.defaultProps), g)) void 0 === c[d] && (c[d] = g[d]);\n return {\n $$typeof: l,\n type: a,\n key: k,\n ref: h,\n props: c,\n _owner: K.current,\n };\n }\n function N(a, b) {\n return {\n $$typeof: l,\n type: a.type,\n key: b,\n ref: a.ref,\n props: a.props,\n _owner: a._owner,\n };\n }\n function O(a) {\n return 'object' === typeof a && null !== a && a.$$typeof === l;\n }\n function escape(a) {\n var b = { '=': '=0', ':': '=2' };\n return (\n '$' +\n a.replace(/[=:]/g, function (a) {\n return b[a];\n })\n );\n }\n var P = /\\/+/g;\n function Q(a, b) {\n return 'object' === typeof a && null !== a && null != a.key\n ? escape('' + a.key)\n : b.toString(36);\n }\n function R(a, b, e, d, c) {\n var k = typeof a;\n if ('undefined' === k || 'boolean' === k) a = null;\n var h = !1;\n if (null === a) h = !0;\n else\n switch (k) {\n case 'string':\n case 'number':\n h = !0;\n break;\n case 'object':\n switch (a.$$typeof) {\n case l:\n case n:\n h = !0;\n }\n }\n if (h)\n return (\n (h = a),\n (c = c(h)),\n (a = '' === d ? '.' + Q(h, 0) : d),\n I(c)\n ? ((e = ''),\n null != a && (e = a.replace(P, '$&/') + '/'),\n R(c, b, e, '', function (a) {\n return a;\n }))\n : null != c &&\n (O(c) &&\n (c = N(\n c,\n e +\n (!c.key || (h && h.key === c.key)\n ? ''\n : ('' + c.key).replace(P, '$&/') + '/') +\n a,\n )),\n b.push(c)),\n 1\n );\n h = 0;\n d = '' === d ? '.' : d + ':';\n if (I(a))\n for (var g = 0; g < a.length; g++) {\n k = a[g];\n var f = d + Q(k, g);\n h += R(k, b, e, f, c);\n }\n else if (((f = A(a)), 'function' === typeof f))\n for (a = f.call(a), g = 0; !(k = a.next()).done; )\n (k = k.value), (f = d + Q(k, g++)), (h += R(k, b, e, f, c));\n else if ('object' === k)\n throw (\n ((b = String(a)),\n Error(\n 'Objects are not valid as a React child (found: ' +\n ('[object Object]' === b\n ? 'object with keys {' + Object.keys(a).join(', ') + '}'\n : b) +\n '). If you meant to render a collection of children, use an array instead.',\n ))\n );\n return h;\n }\n function S(a, b, e) {\n if (null == a) return a;\n var d = [],\n c = 0;\n R(a, d, '', '', function (a) {\n return b.call(e, a, c++);\n });\n return d;\n }\n function T(a) {\n if (-1 === a._status) {\n var b = a._result;\n b = b();\n b.then(\n function (b) {\n if (0 === a._status || -1 === a._status)\n (a._status = 1), (a._result = b);\n },\n function (b) {\n if (0 === a._status || -1 === a._status)\n (a._status = 2), (a._result = b);\n },\n );\n -1 === a._status && ((a._status = 0), (a._result = b));\n }\n if (1 === a._status) return a._result.default;\n throw a._result;\n }\n var U = { current: null },\n V = { transition: null },\n W = {\n ReactCurrentDispatcher: U,\n ReactCurrentBatchConfig: V,\n ReactCurrentOwner: K,\n };\n exports.Children = {\n map: S,\n forEach: function (a, b, e) {\n S(\n a,\n function () {\n b.apply(this, arguments);\n },\n e,\n );\n },\n count: function (a) {\n var b = 0;\n S(a, function () {\n b++;\n });\n return b;\n },\n toArray: function (a) {\n return (\n S(a, function (a) {\n return a;\n }) || []\n );\n },\n only: function (a) {\n if (!O(a))\n throw Error(\n 'React.Children.only expected to receive a single React element child.',\n );\n return a;\n },\n };\n exports.Component = E;\n exports.Fragment = p;\n exports.Profiler = r;\n exports.PureComponent = G;\n exports.StrictMode = q;\n exports.Suspense = w;\n exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = W;\n exports.cloneElement = function (a, b, e) {\n if (null === a || void 0 === a)\n throw Error(\n 'React.cloneElement(...): The argument must be a React element, but you passed ' +\n a +\n '.',\n );\n var d = C({}, a.props),\n c = a.key,\n k = a.ref,\n h = a._owner;\n if (null != b) {\n void 0 !== b.ref && ((k = b.ref), (h = K.current));\n void 0 !== b.key && (c = '' + b.key);\n if (a.type && a.type.defaultProps) var g = a.type.defaultProps;\n for (f in b)\n J.call(b, f) &&\n !L.hasOwnProperty(f) &&\n (d[f] = void 0 === b[f] && void 0 !== g ? g[f] : b[f]);\n }\n var f = arguments.length - 2;\n if (1 === f) d.children = e;\n else if (1 < f) {\n g = Array(f);\n for (var m = 0; m < f; m++) g[m] = arguments[m + 2];\n d.children = g;\n }\n return { $$typeof: l, type: a.type, key: c, ref: k, props: d, _owner: h };\n };\n exports.createContext = function (a) {\n a = {\n $$typeof: u,\n _currentValue: a,\n _currentValue2: a,\n _threadCount: 0,\n Provider: null,\n Consumer: null,\n _defaultValue: null,\n _globalName: null,\n };\n a.Provider = { $$typeof: t, _context: a };\n return (a.Consumer = a);\n };\n exports.createElement = M;\n exports.createFactory = function (a) {\n var b = M.bind(null, a);\n b.type = a;\n return b;\n };\n exports.createRef = function () {\n return { current: null };\n };\n exports.forwardRef = function (a) {\n return { $$typeof: v, render: a };\n };\n exports.isValidElement = O;\n exports.lazy = function (a) {\n return { $$typeof: y, _payload: { _status: -1, _result: a }, _init: T };\n };\n exports.memo = function (a, b) {\n return { $$typeof: x, type: a, compare: void 0 === b ? null : b };\n };\n exports.startTransition = function (a) {\n var b = V.transition;\n V.transition = {};\n try {\n a();\n } finally {\n V.transition = b;\n }\n };\n exports.unstable_act = function () {\n throw Error('act(...) is not supported in production builds of React.');\n };\n exports.useCallback = function (a, b) {\n return U.current.useCallback(a, b);\n };\n exports.useContext = function (a) {\n return U.current.useContext(a);\n };\n exports.useDebugValue = function () {};\n exports.useDeferredValue = function (a) {\n return U.current.useDeferredValue(a);\n };\n exports.useEffect = function (a, b) {\n return U.current.useEffect(a, b);\n };\n exports.useId = function () {\n return U.current.useId();\n };\n exports.useImperativeHandle = function (a, b, e) {\n return U.current.useImperativeHandle(a, b, e);\n };\n exports.useInsertionEffect = function (a, b) {\n return U.current.useInsertionEffect(a, b);\n };\n exports.useLayoutEffect = function (a, b) {\n return U.current.useLayoutEffect(a, b);\n };\n exports.useMemo = function (a, b) {\n return U.current.useMemo(a, b);\n };\n exports.useReducer = function (a, b, e) {\n return U.current.useReducer(a, b, e);\n };\n exports.useRef = function (a) {\n return U.current.useRef(a);\n };\n exports.useState = function (a) {\n return U.current.useState(a);\n };\n exports.useSyncExternalStore = function (a, b, e) {\n return U.current.useSyncExternalStore(a, b, e);\n };\n exports.useTransition = function () {\n return U.current.useTransition();\n };\n exports.version = '18.2.0';\n}\n\n// react/jsx-runtime\nlet jsx, jsxs;\n{\n /**\n * @license React\n * react-jsx-runtime.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n let k = Symbol.for('react.element'),\n m = Object.prototype.hasOwnProperty,\n n =\n React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED\n .ReactCurrentOwner,\n p = { key: !0, ref: !0, __self: !0, __source: !0 };\n function q(c, a, g) {\n var b,\n d = {},\n e = null,\n h = null;\n void 0 !== g && (e = '' + g);\n void 0 !== a.key && (e = '' + a.key);\n void 0 !== a.ref && (h = a.ref);\n for (b in a) m.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);\n if (c && c.defaultProps)\n for (b in ((a = c.defaultProps), a)) void 0 === d[b] && (d[b] = a[b]);\n return {\n $$typeof: k,\n type: c,\n key: e,\n ref: h,\n props: d,\n _owner: n.current,\n };\n }\n jsx = jsxs = q;\n}\n\n// react-dom/server\nlet renderToReadableStream;\n{\n /**\n * @license React\n * react-dom-server.browser.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n var aa = React;\n function k(a) {\n for (\n var b = 'https://reactjs.org/docs/error-decoder.html?invariant=' + a,\n c = 1;\n c < arguments.length;\n c++\n )\n b += '&args[]=' + encodeURIComponent(arguments[c]);\n return (\n 'Minified React error #' +\n a +\n '; visit ' +\n b +\n ' for the full message or use the non-minified dev environment for full errors and additional helpful warnings.'\n );\n }\n var l = null,\n n = 0;\n function p(a, b) {\n if (0 !== b.length)\n if (512 < b.length)\n 0 < n &&\n (a.enqueue(new Uint8Array(l.buffer, 0, n)),\n (l = new Uint8Array(512)),\n (n = 0)),\n a.enqueue(b);\n else {\n var c = l.length - n;\n c < b.length &&\n (0 === c\n ? a.enqueue(l)\n : (l.set(b.subarray(0, c), n), a.enqueue(l), (b = b.subarray(c))),\n (l = new Uint8Array(512)),\n (n = 0));\n l.set(b, n);\n n += b.length;\n }\n }\n function t(a, b) {\n p(a, b);\n return !0;\n }\n function ba(a) {\n l &&\n 0 < n &&\n (a.enqueue(new Uint8Array(l.buffer, 0, n)), (l = null), (n = 0));\n }\n var ca = new TextEncoder();\n function u(a) {\n return ca.encode(a);\n }\n function w(a) {\n return ca.encode(a);\n }\n function da(a, b) {\n 'function' === typeof a.error ? a.error(b) : a.close();\n }\n var x = Object.prototype.hasOwnProperty,\n ea =\n /^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*$/,\n fa = {},\n ha = {};\n function ia(a) {\n if (x.call(ha, a)) return !0;\n if (x.call(fa, a)) return !1;\n if (ea.test(a)) return (ha[a] = !0);\n fa[a] = !0;\n return !1;\n }\n function y(a, b, c, d, f, e, g) {\n this.acceptsBooleans = 2 === b || 3 === b || 4 === b;\n this.attributeName = d;\n this.attributeNamespace = f;\n this.mustUseProperty = c;\n this.propertyName = a;\n this.type = b;\n this.sanitizeURL = e;\n this.removeEmptyString = g;\n }\n var z = {};\n 'children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style'\n .split(' ')\n .forEach(function (a) {\n z[a] = new y(a, 0, !1, a, null, !1, !1);\n });\n [\n ['acceptCharset', 'accept-charset'],\n ['className', 'class'],\n ['htmlFor', 'for'],\n ['httpEquiv', 'http-equiv'],\n ].forEach(function (a) {\n var b = a[0];\n z[b] = new y(b, 1, !1, a[1], null, !1, !1);\n });\n ['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(function (a) {\n z[a] = new y(a, 2, !1, a.toLowerCase(), null, !1, !1);\n });\n [\n 'autoReverse',\n 'externalResourcesRequired',\n 'focusable',\n 'preserveAlpha',\n ].forEach(function (a) {\n z[a] = new y(a, 2, !1, a, null, !1, !1);\n });\n 'allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope'\n .split(' ')\n .forEach(function (a) {\n z[a] = new y(a, 3, !1, a.toLowerCase(), null, !1, !1);\n });\n ['checked', 'multiple', 'muted', 'selected'].forEach(function (a) {\n z[a] = new y(a, 3, !0, a, null, !1, !1);\n });\n ['capture', 'download'].forEach(function (a) {\n z[a] = new y(a, 4, !1, a, null, !1, !1);\n });\n ['cols', 'rows', 'size', 'span'].forEach(function (a) {\n z[a] = new y(a, 6, !1, a, null, !1, !1);\n });\n ['rowSpan', 'start'].forEach(function (a) {\n z[a] = new y(a, 5, !1, a.toLowerCase(), null, !1, !1);\n });\n var ja = /[\\-:]([a-z])/g;\n function ka(a) {\n return a[1].toUpperCase();\n }\n 'accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height'\n .split(' ')\n .forEach(function (a) {\n var b = a.replace(ja, ka);\n z[b] = new y(b, 1, !1, a, null, !1, !1);\n });\n 'xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type'\n .split(' ')\n .forEach(function (a) {\n var b = a.replace(ja, ka);\n z[b] = new y(b, 1, !1, a, 'http://www.w3.org/1999/xlink', !1, !1);\n });\n ['xml:base', 'xml:lang', 'xml:space'].forEach(function (a) {\n var b = a.replace(ja, ka);\n z[b] = new y(b, 1, !1, a, 'http://www.w3.org/XML/1998/namespace', !1, !1);\n });\n ['tabIndex', 'crossOrigin'].forEach(function (a) {\n z[a] = new y(a, 1, !1, a.toLowerCase(), null, !1, !1);\n });\n z.xlinkHref = new y(\n 'xlinkHref',\n 1,\n !1,\n 'xlink:href',\n 'http://www.w3.org/1999/xlink',\n !0,\n !1,\n );\n ['src', 'href', 'action', 'formAction'].forEach(function (a) {\n z[a] = new y(a, 1, !1, a.toLowerCase(), null, !0, !0);\n });\n var B = {\n animationIterationCount: !0,\n aspectRatio: !0,\n borderImageOutset: !0,\n borderImageSlice: !0,\n borderImageWidth: !0,\n boxFlex: !0,\n boxFlexGroup: !0,\n boxOrdinalGroup: !0,\n columnCount: !0,\n columns: !0,\n flex: !0,\n flexGrow: !0,\n flexPositive: !0,\n flexShrink: !0,\n flexNegative: !0,\n flexOrder: !0,\n gridArea: !0,\n gridRow: !0,\n gridRowEnd: !0,\n gridRowSpan: !0,\n gridRowStart: !0,\n gridColumn: !0,\n gridColumnEnd: !0,\n gridColumnSpan: !0,\n gridColumnStart: !0,\n fontWeight: !0,\n lineClamp: !0,\n lineHeight: !0,\n opacity: !0,\n order: !0,\n orphans: !0,\n tabSize: !0,\n widows: !0,\n zIndex: !0,\n zoom: !0,\n fillOpacity: !0,\n floodOpacity: !0,\n stopOpacity: !0,\n strokeDasharray: !0,\n strokeDashoffset: !0,\n strokeMiterlimit: !0,\n strokeOpacity: !0,\n strokeWidth: !0,\n },\n la = ['Webkit', 'ms', 'Moz', 'O'];\n Object.keys(B).forEach(function (a) {\n la.forEach(function (b) {\n b = b + a.charAt(0).toUpperCase() + a.substring(1);\n B[b] = B[a];\n });\n });\n var oa = /[\"'&<>]/;\n function C(a) {\n if ('boolean' === typeof a || 'number' === typeof a) return '' + a;\n a = '' + a;\n var b = oa.exec(a);\n if (b) {\n var c = '',\n d,\n f = 0;\n for (d = b.index; d < a.length; d++) {\n switch (a.charCodeAt(d)) {\n case 34:\n b = '"';\n break;\n case 38:\n b = '&';\n break;\n case 39:\n b = ''';\n break;\n case 60:\n b = '<';\n break;\n case 62:\n b = '>';\n break;\n default:\n continue;\n }\n f !== d && (c += a.substring(f, d));\n f = d + 1;\n c += b;\n }\n a = f !== d ? c + a.substring(f, d) : c;\n }\n return a;\n }\n var pa = /([A-Z])/g,\n qa = /^ms-/,\n ra = Array.isArray,\n sa = w('