diff --git a/Cargo.lock b/Cargo.lock index 81c15fbbd2..34ee5a81b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -792,6 +792,7 @@ dependencies = [ "deno_ast", "deno_bench_util", "deno_broadcast_channel", + "deno_cache", "deno_console", "deno_core", "deno_crypto", @@ -1139,6 +1140,7 @@ version = "0.70.0" dependencies = [ "atty", "deno_broadcast_channel", + "deno_cache", "deno_console", "deno_core", "deno_crypto", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f38a8e651d..f561272cd1 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -27,6 +27,7 @@ path = "./bench/lsp_bench_standalone.rs" [build-dependencies] deno_broadcast_channel = { version = "0.56.0", path = "../ext/broadcast_channel" } deno_console = { version = "0.62.0", path = "../ext/console" } +deno_cache = { version = "0.1.0", path = "../ext/cache" } deno_core = { version = "0.144.0", path = "../core" } deno_crypto = { version = "0.76.0", path = "../ext/crypto" } deno_fetch = { version = "0.85.0", path = "../ext/fetch" } diff --git a/cli/build.rs b/cli/build.rs index 1a4eaa4254..0c3630d514 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -81,6 +81,7 @@ fn create_compiler_snapshot( ) { // libs that are being provided by op crates. let mut op_crate_libs = HashMap::new(); + op_crate_libs.insert("deno.caches", deno_cache::get_declaration()); op_crate_libs.insert("deno.console", deno_console::get_declaration()); op_crate_libs.insert("deno.url", deno_url::get_declaration()); op_crate_libs.insert("deno.web", deno_web::get_declaration()); diff --git a/ext/cache/01_cache.js b/ext/cache/01_cache.js index 0ea93c3c0d..83ac1cdfeb 100644 --- a/ext/cache/01_cache.js +++ b/ext/cache/01_cache.js @@ -3,105 +3,195 @@ /// ((window) => { - const core = window.Deno.core; - const webidl = window.__bootstrap.webidl; - const { - SafeArrayIterator, - Symbol, - SymbolFor, - ObjectDefineProperty, - ObjectFromEntries, - ObjectEntries, - ReflectGet, - ReflectHas, - Proxy, - } = window.__bootstrap.primordials; + function queryCache(requestQuery, options = {}, targetStorage = new Map()) { + const resultList = new Map(); + // let storage = null; + // Ignore step 2-4; + for (const [request, response] of targetStorage.entries()) { + if (requestMatchesCatchedItem(requestQuery, request, response, options)) { + resultList.set(request, response); + } + } + return resultList; + } - const _persistent = Symbol("[[persistent]]"); + function requestMatchesCachedItem( + requestQuery, + request, + response = null, + options = {}, + ) { + // Step 1. + if (options["ignoreMethod"] === false && request.method !== "GET") { + return false; + } + + // Step 2. + let queryURL = requestQuery.url; + let cachedURL = request.url; + if (options["ignoreSearch"] === true) { + queryURL = ""; + cachedURL = ""; + } + + // Step 5. + { + const a = new URL(queryURL); + const b = new URL(cachedURL); + if ( + a.host !== b.host || a.pathname !== b.pathname || a.search !== b.search + ) { + // TODO(@satyarohith): think about exclude fragment flag + return false; + } + } + + // Step 6. + if ( + (response === null && options["ignoreVary"] === true) || + !response.headers.has("Vary") + ) { + return true; + } + + // Step 7. + const varyHeader = response.headers.get("Vary"); + // TODO(@satyarohith): do the parsing of the vary header. + const fieldValues = varyHeader.split(",").map((field) => field.trim()); + for (const fieldValue of fieldValues) { + if ( + fieldValue === "*" || + request.headers.get(fieldValue) !== requestQuery.headers.get(fieldValue) + ) { + return false; + } + } + + return true; + } class CacheStorage { #storage; constructor() { - webidl.illegalConstructor(); + this.#storage = new Map(); + return this; } - get length() { - webidl.assertBranded(this, StoragePrototype); - return core.opSync("op_webstorage_length", this[_persistent]); + // deno-lint-ignore require-await + async match(_request, _options) { + // TODO(@satyarohith): implement the algorithm. + return Promise.resolve(new Response("hello world")); } - - key(index) { - webidl.assertBranded(this, StoragePrototype); - const prefix = "Failed to execute 'key' on 'Storage'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - index = webidl.converters["unsigned long"](index, { - prefix, - context: "Argument 1", - }); - - return core.opSync("op_webstorage_key", index, this[_persistent]); + // deno-lint-ignore require-await + async open(cacheName) { + if (!this.#storage.has(cacheName)) { + this.#storage.set(cacheName, new Cache(cacheName)); + } + return Promise.resolve(this.#storage.get(cacheName)); } - - setItem(key, value) { - webidl.assertBranded(this, StoragePrototype); - const prefix = "Failed to execute 'setItem' on 'Storage'"; - webidl.requiredArguments(arguments.length, 2, { prefix }); - key = webidl.converters.DOMString(key, { - prefix, - context: "Argument 1", - }); - value = webidl.converters.DOMString(value, { - prefix, - context: "Argument 2", - }); - - core.opSync("op_webstorage_set", key, value, this[_persistent]); + // deno-lint-ignore require-await + async has(cacheName) { + return Promise.resolve(this.#storage.has(cacheName)); } - - getItem(key) { - webidl.assertBranded(this, StoragePrototype); - const prefix = "Failed to execute 'getItem' on 'Storage'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - key = webidl.converters.DOMString(key, { - prefix, - context: "Argument 1", - }); - - return core.opSync("op_webstorage_get", key, this[_persistent]); + // deno-lint-ignore require-await + async delete(cacheName) { + return Promise.resolve(this.#storage.delete(cacheName)); } - - removeItem(key) { - webidl.assertBranded(this, StoragePrototype); - const prefix = "Failed to execute 'removeItem' on 'Storage'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - key = webidl.converters.DOMString(key, { - prefix, - context: "Argument 1", - }); - - core.opSync("op_webstorage_remove", key, this[_persistent]); - } - - clear() { - webidl.assertBranded(this, StoragePrototype); - core.opSync("op_webstorage_clear", this[_persistent]); + // deno-lint-ignore require-await + async keys() { + return Promise.resolve(Array.from(this.#storage.keys())); } } - window.__bootstrap.webStorage = { - localStorage() { - if (!localStorage) { - localStorage = createStorage(true); + class Cache { + #storage; + #name; + constructor(cacheName) { + this.#name = cacheName; + this.#storage = new Map(); + return this; + } + // async match(request, options) {} + + // deno-lint-ignore require-await + async matchAll(request, options = {}) { + let r = null; + // Step 2. + if (request instanceof Request) { + if (request.method !== "GET" && !options?.ignoreMethod) { + return Promise.resolve([]); + } + r = request; + } else if (request instanceof string) { + try { + r = new Request(request); + } catch (error) { + return Promise.reject(error); + } } - return localStorage; - }, - sessionStorage() { - if (!sessionStorage) { - sessionStorage = createStorage(false); + + // Step 5. + const responses = []; + // Step 5.2 + if (r === null) { + for (const [_request, response] of this.#storage.entries()) { + responses.push(response); + } + // Step 5.3 + } else { + const requestResponses = queryCache(r, options, this.#storage); + for (const response of requestResponses.values()) { + responses.push(response); + } + // Skip 5.4. } - return sessionStorage; - }, - Storage, + // Step 5.5 + + return Promise.resolve(responses); + } + + // deno-lint-ignore require-await + async add(request) { + const requests = [request]; + return this.addAll(requests); + } + + // async addAll(requests) { + // const responsePromises = []; + // const requestList = []; + // for (const request of requests) { + // if ( + // request instanceof Request && + // request.scheme !== "http" && request.scheme !== "https" || + // request.method !== "GET" + // ) { + // return Promise.reject(new TypeError("type error")); + // } + // } + // } + + // put(request, response) { + // let innerRequest = null; + // if (request instanceof Request) { + // innerRequest = request; + // } else { + // try { + // innerRequest = new Request(request); + // } catch (error) { + // throw Promise.reject(error); + // } + // } + // } + + // async delete(request, options) {} + // deno-lint-ignore require-await + async keys() { + return Promise.resolve(Array.from(this.#storage.keys())); + } + } + + window.__bootstrap.caches = { + CacheStorage, }; })(this); diff --git a/ext/cache/lib.deno_cache.d.ts b/ext/cache/lib.deno_cache.d.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ext/cache/lib.rs b/ext/cache/lib.rs index bd1aacffff..68fc922550 100644 --- a/ext/cache/lib.rs +++ b/ext/cache/lib.rs @@ -14,5 +14,5 @@ pub fn init() -> Extension { } pub fn get_declaration() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_console.d.ts") + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_cache.d.ts") } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index c123a912d6..c7c8774afa 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -23,6 +23,7 @@ path = "examples/hello_runtime.rs" [build-dependencies] deno_broadcast_channel = { version = "0.56.0", path = "../ext/broadcast_channel" } +deno_cache = { version = "0.1.0", path = "../ext/cache" } deno_console = { version = "0.62.0", path = "../ext/console" } deno_core = { version = "0.144.0", path = "../core" } deno_crypto = { version = "0.76.0", path = "../ext/crypto" } @@ -46,6 +47,7 @@ winapi = "0.3.9" [dependencies] deno_broadcast_channel = { version = "0.56.0", path = "../ext/broadcast_channel" } +deno_cache = { version = "0.1.0", path = "../ext/cache" } deno_console = { version = "0.62.0", path = "../ext/console" } deno_core = { version = "0.144.0", path = "../core" } deno_crypto = { version = "0.76.0", path = "../ext/crypto" } diff --git a/runtime/build.rs b/runtime/build.rs index eea7a3602c..a15b2ec676 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -142,6 +142,7 @@ mod not_docs { fn create_runtime_snapshot(snapshot_path: &Path, files: Vec) { let extensions: Vec = vec![ deno_webidl::init(), + deno_cache::init(), deno_console::init(), deno_url::init(), deno_tls::init(), diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 3421528d20..b6d5f70914 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -49,6 +49,7 @@ delete Intl.v8BreakIterator; const encoding = window.__bootstrap.encoding; const colors = window.__bootstrap.colors; const Console = window.__bootstrap.console.Console; + const CacheStorage = window.__bootstrap.caches.CacheStorage; const inspectArgs = window.__bootstrap.console.inspectArgs; const quoteString = window.__bootstrap.console.quoteString; const compression = window.__bootstrap.compression; @@ -466,6 +467,7 @@ delete Intl.v8BreakIterator; btoa: util.writable(base64.btoa), clearInterval: util.writable(timers.clearInterval), clearTimeout: util.writable(timers.clearTimeout), + caches: util.nonEnumerable(new CacheStorage()), console: util.nonEnumerable( new Console((msg, level) => core.print(msg, level > 1)), ), diff --git a/runtime/lib.rs b/runtime/lib.rs index 543d3a0a21..50b1063ba6 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -1,6 +1,7 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. pub use deno_broadcast_channel; +pub use deno_cache; pub use deno_console; pub use deno_core; pub use deno_crypto; diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 83ba380a6f..5f6c16a917 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -374,6 +374,7 @@ impl WebWorker { let mut extensions: Vec = vec![ // Web APIs deno_webidl::init(), + deno_cache::init(), deno_console::init(), deno_url::init(), deno_web::init::( diff --git a/runtime/worker.rs b/runtime/worker.rs index 5100f42da1..770049f473 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -117,6 +117,7 @@ impl MainWorker { let mut extensions: Vec = vec![ // Web APIs deno_webidl::init(), + deno_cache::init(), deno_console::init(), deno_url::init(), deno_web::init::(