storage-fetch.js 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { request } from '@/common';
  2. import storage from '@/common/storage';
  3. /** @type { function(url, options, check): Promise<void> } or throws on error */
  4. storage.cache.fetch = cacheOrFetch({
  5. init(options) {
  6. return { ...options, responseType: 'blob' };
  7. },
  8. async transform(response, url, options, check) {
  9. const [type, body] = await storage.cache.makeRaw(response, true);
  10. await check?.(url, response.data, type);
  11. return `${type},${body}`;
  12. },
  13. });
  14. /** @type { function(url, options): Promise<void> } or throws on error */
  15. storage.require.fetch = cacheOrFetch({
  16. transform: ({ data }, url) => (
  17. /^\s*</.test(data)
  18. ? Promise.reject(`NOT_JS: ${url} "${data.slice(0, 100).trim().replace(/\s{2,}/g, ' ')}"`)
  19. : data
  20. ),
  21. });
  22. function cacheOrFetch(handlers = {}) {
  23. const requests = {};
  24. const { init, transform } = handlers;
  25. /** @this VMStorageBase */
  26. return function cacheOrFetchHandler(...args) {
  27. const [url] = args;
  28. const promise = requests[url] || (requests[url] = this::doFetch(...args));
  29. return promise;
  30. };
  31. /** @this VMStorageBase */
  32. async function doFetch(...args) {
  33. const [url, options] = args;
  34. try {
  35. const res = !url.startsWith('data:')
  36. && await requestNewer(url, init ? init(options) : options);
  37. if (res) {
  38. const result = transform ? await transform(res, ...args) : res.data;
  39. await this.set(url, result);
  40. }
  41. } finally {
  42. delete requests[url];
  43. }
  44. }
  45. }
  46. export async function requestNewer(url, opts) {
  47. const modOld = await storage.mod.getOne(url);
  48. for (const get of [0, 1]) {
  49. if (modOld || get) {
  50. const req = await request(url, !get ? { ...opts, method: 'HEAD' } : opts);
  51. const { headers } = req;
  52. const mod = headers.get('etag')
  53. || +new Date(headers.get('last-modified'))
  54. || +new Date(headers.get('date'));
  55. if (mod && mod === modOld) {
  56. return;
  57. }
  58. if (get) {
  59. if (mod) storage.mod.set(url, mod);
  60. else if (modOld) storage.mod.remove(url);
  61. return req;
  62. }
  63. }
  64. }
  65. }