/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/cache/01_cache.js
389 строк
12 KB
Kenta Moriuchi
chore: make prefer-primordials lint an internal plugin (#35978)
23 июл 2026, 11:53
Не верифицирован
23 июл 2026, 11:53
5b55d08
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. (function () { const { core, primordials } = __bootstrap; const { op_cache_delete, op_cache_keys, op_cache_match, op_cache_put, op_cache_storage_delete, op_cache_storage_has, op_cache_storage_keys, op_cache_storage_open, } = core.ops; const { ArrayPrototypePush, ObjectPrototypeIsPrototypeOf, StringPrototypeSplit, StringPrototypeTrim, Symbol, SymbolFor, TypeError, } = primordials; const webidl = core.loadExtScript("ext:deno_webidl/00_webidl.js"); const { Request, RequestPrototype, toInnerRequest, } = core.loadExtScript("ext:deno_fetch/23_request.js"); const { toInnerResponse, wireHeaderList } = core.loadExtScript( "ext:deno_fetch/23_response.js", ); const { URLPrototype } = core.loadExtScript("ext:deno_web/00_url.js"); const { getHeader } = core.loadExtScript("ext:deno_fetch/20_headers.js"); const { getReadableStreamResourceBacking, readableStreamForRid, resourceForReadableStream, } = core.loadExtScript("ext:deno_web/06_streams.js"); class CacheStorage { constructor() { webidl.illegalConstructor(); } async open(cacheName) { webidl.assertBranded(this, CacheStoragePrototype); const prefix = "Failed to execute 'open' on 'CacheStorage'"; webidl.requiredArguments(arguments.length, 1, prefix); cacheName = webidl.converters["DOMString"](cacheName, prefix, "Argument 1"); const cacheId = await op_cache_storage_open(cacheName); const cache = webidl.createBranded(Cache); cache[_id] = cacheId; return cache; } async has(cacheName) { webidl.assertBranded(this, CacheStoragePrototype); const prefix = "Failed to execute 'has' on 'CacheStorage'"; webidl.requiredArguments(arguments.length, 1, prefix); cacheName = webidl.converters["DOMString"](cacheName, prefix, "Argument 1"); return await op_cache_storage_has(cacheName); } async delete(cacheName) { webidl.assertBranded(this, CacheStoragePrototype); const prefix = "Failed to execute 'delete' on 'CacheStorage'"; webidl.requiredArguments(arguments.length, 1, prefix); cacheName = webidl.converters["DOMString"](cacheName, prefix, "Argument 1"); return await op_cache_storage_delete(cacheName); } async keys() { webidl.assertBranded(this, CacheStoragePrototype); return await op_cache_storage_keys(); } async match(request, options) { webidl.assertBranded(this, CacheStoragePrototype); const prefix = "Failed to execute 'match' on 'CacheStorage'"; webidl.requiredArguments(arguments.length, 1, prefix); request = webidl.converters["RequestInfo_DOMString"]( request, prefix, "Argument 1", ); const cacheName = options?.cacheName; if (cacheName !== undefined) { if (!(await op_cache_storage_has(cacheName))) { return undefined; } const cache = await this.open(cacheName); // false positive: cache is a local Cache instance, not a global intrinsic // deno-lint-ignore deno-internal/prefer-primordials return await cache.match(request, options); } const names = await op_cache_storage_keys(); for (let i = 0; i < names.length; ++i) { const cache = await this.open(names[i]); // false positive: cache is a local Cache instance, not a global intrinsic // deno-lint-ignore deno-internal/prefer-primordials const response = await cache.match(request, options); if (response !== undefined) { return response; } } return undefined; } [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { return `${this.constructor.name} ${inspect({}, inspectOptions)}`; } } const _matchAll = Symbol("[[matchAll]]"); const _id = Symbol("id"); class Cache { /** @type {number} */ [_id]; constructor() { webidl.illegalConstructor(); } /** See https://w3c.github.io/ServiceWorker/#dom-cache-put */ async put(request, response) { webidl.assertBranded(this, CachePrototype); const prefix = "Failed to execute 'put' on 'Cache'"; webidl.requiredArguments(arguments.length, 2, prefix); request = webidl.converters["RequestInfo_DOMString"]( request, prefix, "Argument 1", ); response = webidl.converters["Response"](response, prefix, "Argument 2"); // Step 1. let innerRequest = null; // Step 2. if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) { innerRequest = toInnerRequest(request); } else { // Step 3. innerRequest = toInnerRequest(new Request(request)); } // Step 4. const reqUrl = new URL(innerRequest.url()); if (reqUrl.protocol !== "http:" && reqUrl.protocol !== "https:") { throw new TypeError( `Request url protocol must be 'http:' or 'https:': received '${reqUrl.protocol}'`, ); } if (innerRequest.method !== "GET") { throw new TypeError("Request method must be GET"); } // Step 5. const innerResponse = toInnerResponse(response); // Step 6. if (innerResponse.status === 206) { throw new TypeError("Response status must not be 206"); } // Step 7. const varyHeader = getHeader(innerResponse.headerList, "vary"); if (varyHeader) { const fieldValues = StringPrototypeSplit(varyHeader, ","); for (let i = 0; i < fieldValues.length; ++i) { const field = fieldValues[i]; if (StringPrototypeTrim(field) === "*") { throw new TypeError("Vary header must not contain '*'"); } } } // Step 8. if (innerResponse.body !== null && innerResponse.body.unusable()) { throw new TypeError("Response body is already used"); } const stream = innerResponse.body?.stream; let rid = null; if (stream) { const resourceBacking = getReadableStreamResourceBacking( innerResponse.body?.stream, ); if (resourceBacking) { rid = resourceBacking.rid; } else { rid = resourceForReadableStream(stream, innerResponse.body?.length); } } // Remove fragment from request URL before put. reqUrl.hash = ""; // Step 9-11. // Step 12-19: TODO(@satyarohith): do the insertion in background. await op_cache_put( { cacheId: this[_id], // deno-lint-ignore deno-internal/prefer-primordials requestUrl: reqUrl.toString(), responseHeaders: wireHeaderList(innerResponse), requestHeaders: innerRequest.headerList, responseStatus: innerResponse.status, responseStatusText: innerResponse.statusMessage, responseRid: rid, }, ); } /** See https://w3c.github.io/ServiceWorker/#cache-match */ async match(request, options) { webidl.assertBranded(this, CachePrototype); const prefix = "Failed to execute 'match' on 'Cache'"; webidl.requiredArguments(arguments.length, 1, prefix); request = webidl.converters["RequestInfo_DOMString"]( request, prefix, "Argument 1", ); const p = await this[_matchAll](request, options); if (p.length > 0) { return p[0]; } else { return undefined; } } /** See https://w3c.github.io/ServiceWorker/#cache-delete */ async delete(request, _options) { webidl.assertBranded(this, CachePrototype); const prefix = "Failed to execute 'delete' on 'Cache'"; webidl.requiredArguments(arguments.length, 1, prefix); request = webidl.converters["RequestInfo_DOMString"]( request, prefix, "Argument 1", ); // Step 1. let r = null; // Step 2. if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) { r = request; if (request.method !== "GET") { return false; } } else if ( typeof request === "string" || ObjectPrototypeIsPrototypeOf(URLPrototype, request) ) { r = new Request(request); } return await op_cache_delete({ cacheId: this[_id], requestUrl: r.url, }); } /** See https://w3c.github.io/ServiceWorker/#cache-keys */ async keys(request, _options) { webidl.assertBranded(this, CachePrototype); const prefix = "Failed to execute 'keys' on 'Cache'"; // Step 1-2. let requestUrl = null; if (request !== undefined) { request = webidl.converters["RequestInfo_DOMString"]( request, prefix, "Argument 1", ); let r = null; if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) { r = request; // Only GET requests are stored in the cache. if (request.method !== "GET") { return []; } } else { r = new Request(request); } // Remove the fragment from the request URL before matching. const url = new URL(r.url); url.hash = ""; // deno-lint-ignore deno-internal/prefer-primordials requestUrl = url.toString(); } // Step 5: return the request keys in insertion order. When a request is // given, the backend filters by URL so we don't fetch the whole cache. const entries = await op_cache_keys({ cacheId: this[_id], requestUrl }); const requests = []; for (let i = 0; i < entries.length; ++i) { const entry = entries[i]; ArrayPrototypePush( requests, new Request(entry.requestUrl, { headers: entry.requestHeaders }), ); } return requests; } /** See https://w3c.github.io/ServiceWorker/#cache-matchall * * Note: the function is private as we don't want to expose * this API to the public yet. * * The function will return an array of responses. */ async [_matchAll](request, _options) { // Step 1. let r = null; // Step 2. if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) { r = request; if (request.method !== "GET") { return []; } } else if ( typeof request === "string" || ObjectPrototypeIsPrototypeOf(URLPrototype, request) ) { r = new Request(request); } // Step 5. const responses = []; // Step 5.2 if (r === null) { // Step 5.3 // Note: we have to return all responses in the cache when // the request is null. // We deviate from the spec here and return an empty array // as we don't expose matchAll() API. return responses; } else { // Remove the fragment from the request URL. const url = new URL(r.url); url.hash = ""; const innerRequest = toInnerRequest(r); const matchResult = await op_cache_match( { cacheId: this[_id], // deno-lint-ignore deno-internal/prefer-primordials requestUrl: url.toString(), requestHeaders: innerRequest.headerList, }, ); if (matchResult) { const { 0: meta, 1: responseBodyRid } = matchResult; let body = null; if (responseBodyRid !== null) { body = readableStreamForRid(responseBodyRid); } const response = new Response( body, { headers: meta.responseHeaders, status: meta.responseStatus, statusText: meta.responseStatusText, }, ); ArrayPrototypePush(responses, response); } } // Step 5.4-5.5: don't apply in this context. return responses; } [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { return `${this.constructor.name} ${inspect({}, inspectOptions)}`; } } webidl.configureInterface(CacheStorage); webidl.configureInterface(Cache); const CacheStoragePrototype = CacheStorage.prototype; const CachePrototype = Cache.prototype; let cacheStorageStorage; function cacheStorage() { if (!cacheStorageStorage) { cacheStorageStorage = webidl.createBranded(CacheStorage); } return cacheStorageStorage; } return { Cache, CacheStorage, cacheStorage }; })();