@ohos-ports/layer0-prefetch 4.20.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +7 -0
  2. package/constants.d.ts +23 -0
  3. package/constants.js +25 -0
  4. package/index.js +24 -0
  5. package/package.json +50 -0
  6. package/sw/CustomCacheKeyPlugin.d.ts +10 -0
  7. package/sw/CustomCacheKeyPlugin.js +54 -0
  8. package/sw/CustomExpirationPlugin.d.ts +122 -0
  9. package/sw/CustomExpirationPlugin.js +280 -0
  10. package/sw/DeepFetchPlugin.d.ts +108 -0
  11. package/sw/DeepFetchPlugin.js +144 -0
  12. package/sw/DevtoolsPlugin.d.ts +64 -0
  13. package/sw/DevtoolsPlugin.js +131 -0
  14. package/sw/PrefetchPlugin.d.ts +32 -0
  15. package/sw/PrefetchPlugin.js +167 -0
  16. package/sw/Prefetcher.d.ts +209 -0
  17. package/sw/Prefetcher.js +367 -0
  18. package/sw/getServiceWorkerMaxAge.d.ts +22 -0
  19. package/sw/getServiceWorkerMaxAge.js +51 -0
  20. package/sw/index.d.ts +2 -0
  21. package/sw/index.js +6 -0
  22. package/sw/log.d.ts +1 -0
  23. package/sw/log.js +9 -0
  24. package/sw/messageBrowser.d.ts +7 -0
  25. package/sw/messageBrowser.js +18 -0
  26. package/sw/prefetch.d.ts +9 -0
  27. package/sw/prefetch.js +17 -0
  28. package/window/InstallOptions.d.ts +77 -0
  29. package/window/InstallOptions.js +2 -0
  30. package/window/PrefetchOptions.d.ts +19 -0
  31. package/window/PrefetchOptions.js +2 -0
  32. package/window/entry.d.ts +1 -0
  33. package/window/entry.js +8 -0
  34. package/window/getHostName.d.ts +1 -0
  35. package/window/getHostName.js +6 -0
  36. package/window/getUserAgent.d.ts +1 -0
  37. package/window/getUserAgent.js +6 -0
  38. package/window/index.d.ts +3 -0
  39. package/window/index.js +8 -0
  40. package/window/install.d.ts +13 -0
  41. package/window/install.js +72 -0
  42. package/window/isSafari.d.ts +6 -0
  43. package/window/isSafari.js +31 -0
  44. package/window/loadManifest.d.ts +3 -0
  45. package/window/loadManifest.js +20 -0
  46. package/window/prefetch.d.ts +71 -0
  47. package/window/prefetch.js +157 -0
  48. package/window/prefetchWhenVisible.d.ts +5 -0
  49. package/window/prefetchWhenVisible.js +20 -0
  50. package/window/registerServiceWorker.d.ts +2 -0
  51. package/window/registerServiceWorker.js +66 -0
  52. package/window/runWhenVisible.d.ts +6 -0
  53. package/window/runWhenVisible.js +28 -0
  54. package/window/waitForServiceWorker.d.ts +4 -0
  55. package/window/waitForServiceWorker.js +27 -0
  56. package/window/watchElements.d.ts +7 -0
  57. package/window/watchElements.js +63 -0
  58. package/window/watchLinks.d.ts +9 -0
  59. package/window/watchLinks.js +65 -0
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Sends a message to the last focused browser window.
5
+ * @param message
6
+ */
7
+ function messageBrowser(message, { broadcast = false } = {}) {
8
+ // @ts-ignore
9
+ return self.clients.matchAll({ type: 'window' }).then((clients) => {
10
+ let targetClients = clients;
11
+ if (!broadcast && clients.length) {
12
+ // clients array is ordered by last focused
13
+ targetClients = [clients[0]];
14
+ }
15
+ targetClients.forEach(client => client.postMessage(message));
16
+ });
17
+ }
18
+ exports.default = messageBrowser;
@@ -0,0 +1,9 @@
1
+ import { PrefetchConfiguration, PrefetchAs } from '../window/prefetch';
2
+ /**
3
+ * Prefetches an asset.
4
+ * @param url The asset URL
5
+ * @param as The value for the link element's `as` attribute.
6
+ * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link.
7
+ * Use "fetch" for html pages as Safari doesn't support "document".
8
+ */
9
+ export declare function prefetch(url: string, as?: PrefetchAs, options?: PrefetchConfiguration): any;
package/sw/prefetch.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const messageBrowser_1 = __importDefault(require("./messageBrowser"));
7
+ /**
8
+ * Prefetches an asset.
9
+ * @param url The asset URL
10
+ * @param as The value for the link element's `as` attribute.
11
+ * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link.
12
+ * Use "fetch" for html pages as Safari doesn't support "document".
13
+ */
14
+ function prefetch(url, as = 'fetch', options = { cors: 'anonymous' }) {
15
+ return messageBrowser_1.default({ action: 'prefetch', url, as, options });
16
+ }
17
+ exports.prefetch = prefetch;
@@ -0,0 +1,77 @@
1
+ import PrefetchOptions from './PrefetchOptions';
2
+ export interface PrefetchWatcherConfig {
3
+ /**
4
+ * The CSS selector to use when finding elements whose src or href should be deep fetched
5
+ */
6
+ selector: string;
7
+ /**
8
+ * A callback to allow using attributes of the discovered element to be used to prefetch
9
+ * resources as needed.
10
+ */
11
+ callback: (param: HTMLElement) => void;
12
+ }
13
+ export interface ObserveOptionsConfig {
14
+ /**
15
+ * `true` to disable observation of all DOM nodes for prefetching
16
+ */
17
+ disabled?: boolean;
18
+ /**
19
+ * `true` to observe changes to DOM attributes that may trigger the need to prefetch
20
+ * resources.
21
+ */
22
+ attributes?: boolean;
23
+ }
24
+ /**
25
+ * Options for the `install` function.
26
+ */
27
+ export default interface InstallOptions extends PrefetchOptions {
28
+ /**
29
+ * Any link whose href matches one of these patterns will have its
30
+ * href prefetched when the link becomes visible
31
+ */
32
+ prefetchPatterns?: RegExp[];
33
+ /**
34
+ * URLs to prefetch immediately
35
+ */
36
+ prefetchURLs?: string[];
37
+ /**
38
+ * The Layer0 hostname from which to prefetch cached content. This is only used when the origin
39
+ * site is not served from Layer0.
40
+ */
41
+ cacheHost?: string;
42
+ /**
43
+ * The URL path on which the service worker is served.
44
+ */
45
+ serviceWorkerPath?: string;
46
+ /**
47
+ * A list of config objects with selectors to watch for. When elements matching the
48
+ * selector are found, the specified callback is called.
49
+ *
50
+ * **Example**
51
+ *
52
+ * install({
53
+ * watch: [{
54
+ * selector: 'div.product-tile',
55
+ * callback: el => {
56
+ * const productId = el.getAttribute('data-product-id')
57
+ * const catId = document.getElementById('cat-listing').getAttribute('data-category-id')
58
+ * prefetch(`/api/${catId}/${productId}`, 'fetch')
59
+ * }
60
+ * }]
61
+ * })
62
+ */
63
+ watch?: PrefetchWatcherConfig[];
64
+ /**
65
+ * The prefetcher observes the DOM for new elements that are added for potential
66
+ * prefetching. Observations are made to the document's child nodes and subtree.
67
+ * Attributes changes are not observed by default, but can be enabled with
68
+ * `ObserveOptionsConfig.attributes`. Additionally, you can disable the observer
69
+ * by setting `ObserveOptionsConfig.disabled` to `true`.
70
+ */
71
+ observe?: ObserveOptionsConfig;
72
+ /**
73
+ * The probability that a prefetch request will be relayed to the origin even
74
+ * if a response is not in the cache. Defaults to 0. This should be a number between 0 and 1.
75
+ */
76
+ forcePrefetchRatio?: number;
77
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,19 @@
1
+ export default interface PrefetchOptions {
2
+ /**
3
+ * Set to true to include pages not in the edge cache when prefetching. By default
4
+ * pages will only be prefetched if they are available in Layer0 edge cache. Warning:
5
+ * setting this to `true` will cause a significant increase in traffic to the origin server.
6
+ */
7
+ includeCacheMisses: boolean;
8
+ spaRoutes: RegExp[];
9
+ /**
10
+ * The Layer0 hostname from which to prefetch cached content. This is only used when the origin
11
+ * site is not served from Layer0.
12
+ */
13
+ cacheHost?: string;
14
+ /**
15
+ * The probability that a prefetch request will be relayed to the origin even
16
+ * if a response is not in the cache. Defaults to 0. This should be a number between 0 and 1.
17
+ */
18
+ forcePrefetchRatio?: number;
19
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const install_1 = __importDefault(require("./install"));
7
+ // @ts-ignore prefetchOptions is not a standard property, but one we ask the user to expose
8
+ install_1.default(window.prefetchOptions);
@@ -0,0 +1 @@
1
+ export default function getHostName(): string;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function getHostName() {
4
+ return window.location.hostname;
5
+ }
6
+ exports.default = getHostName;
@@ -0,0 +1 @@
1
+ export default function getUserAgent(): string;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function getUserAgent() {
4
+ return navigator.userAgent;
5
+ }
6
+ exports.default = getUserAgent;
@@ -0,0 +1,3 @@
1
+ export { prefetch } from './prefetch';
2
+ export { default as waitForServiceWorker } from './waitForServiceWorker';
3
+ export { default as install } from './install';
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var prefetch_1 = require("./prefetch");
4
+ exports.prefetch = prefetch_1.prefetch;
5
+ var waitForServiceWorker_1 = require("./waitForServiceWorker");
6
+ exports.waitForServiceWorker = waitForServiceWorker_1.default;
7
+ var install_1 = require("./install");
8
+ exports.install = install_1.default;
@@ -0,0 +1,13 @@
1
+ import InstallOptions from './InstallOptions';
2
+ /**
3
+ * Installs the service worker, prefetches any URLs specified in `prefetchURLs` and watches
4
+ * all links whose `href` attribute matches one of the specified `prefetchPatterns`. When
5
+ * a matching link is visible in the viewport, the destination URL will be prefetched and
6
+ * added to the cache.
7
+ * @param options
8
+ */
9
+ export default function install(options?: Partial<InstallOptions>): Promise<void>;
10
+ /**
11
+ * Adds event listener for messages sent by service-worker if it wasn't added already
12
+ */
13
+ export declare function listenForSwMessages(): void;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const prefetch_1 = require("./prefetch");
7
+ const registerServiceWorker_1 = require("./registerServiceWorker");
8
+ const watchLinks_1 = __importDefault(require("./watchLinks"));
9
+ const loadManifest_1 = __importDefault(require("./loadManifest"));
10
+ const watchElements_1 = __importDefault(require("./watchElements"));
11
+ const CacheManifest_1 = __importDefault(require("@layer0/core/router/CacheManifest"));
12
+ const defaults = {
13
+ includeCacheMisses: false,
14
+ cacheHost: undefined,
15
+ forcePrefetchRatio: 0,
16
+ };
17
+ let swEventListenerAdded = false;
18
+ /**
19
+ * Installs the service worker, prefetches any URLs specified in `prefetchURLs` and watches
20
+ * all links whose `href` attribute matches one of the specified `prefetchPatterns`. When
21
+ * a matching link is visible in the viewport, the destination URL will be prefetched and
22
+ * added to the cache.
23
+ * @param options
24
+ */
25
+ async function install(options) {
26
+ options = { ...defaults, ...options };
27
+ const spaRoutes = [];
28
+ const { serviceWorker } = navigator;
29
+ if (typeof serviceWorker === 'undefined') {
30
+ return;
31
+ }
32
+ const { prefetchURLs, watch, serviceWorkerPath = '/service-worker.js', observe } = options;
33
+ await loadManifest_1.default({ cacheHost: options.cacheHost });
34
+ const cacheManifest = CacheManifest_1.default.fromEntries(
35
+ // @ts-ignore
36
+ window.__LAYER0_CACHE_MANIFEST__ || []);
37
+ const prefetchPatterns = [...(options.prefetchPatterns || [])].map(routeRegex => ({
38
+ criteriaPath: routeRegex.source,
39
+ route: routeRegex.source,
40
+ cacheOptions: { edge: { maxAgeSeconds: 1 }, browser: { serviceWorkerSeconds: 1 } },
41
+ returnsResponse: false,
42
+ }));
43
+ spaRoutes.push(...cacheManifest.spaRoutes);
44
+ prefetch_1.configure({ ...options, spaRoutes });
45
+ // install the service worker
46
+ registerServiceWorker_1.registerServiceWorker(serviceWorkerPath);
47
+ // prefetch requested URLs
48
+ prefetchURLs === null || prefetchURLs === void 0 ? void 0 : prefetchURLs.forEach(url => prefetch_1.prefetch(url));
49
+ // watch links currently in the DOM as well as any links that are added in the future
50
+ watchLinks_1.default(cacheManifest, observe); // observer options only needs supplied to the first call since this creates the MutationObserver
51
+ watchLinks_1.default(CacheManifest_1.default.fromEntries(prefetchPatterns));
52
+ watchElements_1.default(watch);
53
+ listenForSwMessages();
54
+ }
55
+ exports.default = install;
56
+ /**
57
+ * Adds event listener for messages sent by service-worker if it wasn't added already
58
+ */
59
+ function listenForSwMessages() {
60
+ const { serviceWorker } = navigator;
61
+ // In case service-worker was not registered or eventListener for messages was already added, do nothing
62
+ if (typeof serviceWorker === 'undefined' || swEventListenerAdded) {
63
+ return;
64
+ }
65
+ serviceWorker.addEventListener('message', event => {
66
+ if (event.data.action === 'prefetch') {
67
+ prefetch_1.prefetch(event.data.url, event.data.as, event.data.options);
68
+ }
69
+ });
70
+ swEventListenerAdded = true;
71
+ }
72
+ exports.listenForSwMessages = listenForSwMessages;
@@ -0,0 +1,6 @@
1
+ export declare function isSafari(): boolean;
2
+ /**
3
+ * Returns true if this is a private window in safari
4
+ * From https://stackoverflow.com/questions/12821127/how-to-check-if-ios-is-using-private-browsing/47642304#47642304
5
+ */
6
+ export declare function isSafariPrivateWindow(): boolean;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const getUserAgent_1 = __importDefault(require("./getUserAgent"));
7
+ let _isSafari = null;
8
+ function isSafari() {
9
+ if (_isSafari == null) {
10
+ _isSafari = /^((?!chrome|android).)*safari/i.test(getUserAgent_1.default());
11
+ }
12
+ return _isSafari;
13
+ }
14
+ exports.isSafari = isSafari;
15
+ /**
16
+ * Returns true if this is a private window in safari
17
+ * From https://stackoverflow.com/questions/12821127/how-to-check-if-ios-is-using-private-browsing/47642304#47642304
18
+ */
19
+ function isSafariPrivateWindow() {
20
+ if (!isSafari())
21
+ return false;
22
+ try {
23
+ // @ts-ignore
24
+ window.openDatabase(null, null, null, null);
25
+ return false;
26
+ }
27
+ catch (_) {
28
+ return true;
29
+ }
30
+ }
31
+ exports.isSafariPrivateWindow = isSafariPrivateWindow;
@@ -0,0 +1,3 @@
1
+ export default function loadManifest(options: {
2
+ cacheHost?: string;
3
+ }): Promise<unknown>;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ async function loadManifest(options) {
4
+ return new Promise(resolve => {
5
+ let script = document.createElement('script');
6
+ script.type = 'text/javascript';
7
+ script.async = true;
8
+ script.onload = resolve;
9
+ let manifestURL = '__layer0__/cache-manifest.js';
10
+ if (options === null || options === void 0 ? void 0 : options.cacheHost) {
11
+ manifestURL = new URL(`https://${options.cacheHost}`).toString() + manifestURL;
12
+ script.src = manifestURL;
13
+ }
14
+ else {
15
+ script.src = '/' + manifestURL;
16
+ }
17
+ document.getElementsByTagName('head')[0].appendChild(script);
18
+ });
19
+ }
20
+ exports.default = loadManifest;
@@ -0,0 +1,71 @@
1
+ import PrefetchOptions from './PrefetchOptions';
2
+ import { HTTPMethod } from '@layer0/core/router/CacheOptions';
3
+ /**
4
+ * Object with configuration for prefetch function
5
+ */
6
+ export declare type PrefetchConfiguration = {
7
+ /**
8
+ * Sets the body of prefetch request.
9
+ */
10
+ body?: string;
11
+ /**
12
+ * Allows to change default HTTP GET method of prefetch request.
13
+ */
14
+ method?: HTTPMethod;
15
+ /**
16
+ * Allows to change default CORS policy of prefetch request.
17
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-crossorigin
18
+ */
19
+ cors?: false | 'anonymous' | 'use-credentials';
20
+ /**
21
+ * Allows to override the default Prefetch TTL or serviceWorkerSeconds value defined in routes.js.
22
+ * The number represents the seconds for how long the item will be stored in the browser cache.
23
+ */
24
+ maxAgeSeconds?: number;
25
+ /**
26
+ * Allows to override the default includeCacheMisses value from install() function.
27
+ * Set to true to send all requests to origin even when they are not in the Edge cache.
28
+ */
29
+ includeCacheMisses?: boolean;
30
+ /**
31
+ * Allows to override the default forcePrefetchRatio value from install() function.
32
+ * The number represents ratio of requests that are sent to origin even when they are not in the Edge cache.
33
+ * Possible values: Number between 0 and 1. Number 0 is equal to 0% requests and 1 to 100% requests sent to origin.
34
+ */
35
+ forcePrefetchRatio?: number;
36
+ };
37
+ /**
38
+ * Possible values for the "as" param of prefetch function
39
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as
40
+ */
41
+ export declare type PrefetchAs = 'audio' | 'document' | 'embed' | 'fetch' | 'font' | 'image' | 'object' | 'script' | 'style' | 'track' | 'video' | 'worker' | undefined;
42
+ /**
43
+ * Configures prefetching options
44
+ * @param opts
45
+ */
46
+ export declare function configure(opts: Partial<PrefetchOptions>): void;
47
+ /**
48
+ * Prefetches and caches the specified URL.
49
+ *
50
+ * **Example**
51
+ *
52
+ * ```js
53
+ * import { prefetch } from '@layer0/prefetch/window
54
+ *
55
+ * // Prefetches the URL with default PrefetchConfiguration
56
+ * prefetch('/some/url')
57
+ *
58
+ * // Prefetches the URL as fetch with custom PrefetchConfiguration
59
+ * prefetch('/some/url', "fetch", {
60
+ * // Overrides the default Prefetch TTL or serviceWorkerSeconds value defined in routes.js
61
+ * maxAgeSeconds: 300, // 5 minutes
62
+ *
63
+ * // Allows to override the default forcePrefetchRatio value from install() function
64
+ * includeCacheMisses: true
65
+ * });
66
+ * ```
67
+ * @param {String} url The URL to prefetch
68
+ * @param {String} as Value to use for the "as" attribute of the <link> tag
69
+ * @param {PrefetchConfiguration} config Options to use for the prefetch
70
+ */
71
+ export declare function prefetch(url: string, as?: PrefetchAs, config?: PrefetchConfiguration): Promise<void>;
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const waitForServiceWorker_1 = __importDefault(require("./waitForServiceWorker"));
7
+ const constants_1 = require("@layer0/core/constants");
8
+ const getHostName_1 = __importDefault(require("./getHostName"));
9
+ const install_1 = require("./install");
10
+ // According to Fastly, 2^13 (8192) is the max number of bytes allowed for a URL:
11
+ const MAX_URL_BYTES = 2 ** 13;
12
+ const prefetched = new Set();
13
+ const defaults = {
14
+ includeCacheMisses: false,
15
+ cacheHost: undefined,
16
+ spaRoutes: [],
17
+ forcePrefetchRatio: 0,
18
+ };
19
+ let options = defaults;
20
+ /**
21
+ * Configures prefetching options
22
+ * @param opts
23
+ */
24
+ function configure(opts) {
25
+ options = { ...defaults, ...opts };
26
+ }
27
+ exports.configure = configure;
28
+ /**
29
+ * Prefetches and caches the specified URL.
30
+ *
31
+ * **Example**
32
+ *
33
+ * ```js
34
+ * import { prefetch } from '@layer0/prefetch/window
35
+ *
36
+ * // Prefetches the URL with default PrefetchConfiguration
37
+ * prefetch('/some/url')
38
+ *
39
+ * // Prefetches the URL as fetch with custom PrefetchConfiguration
40
+ * prefetch('/some/url', "fetch", {
41
+ * // Overrides the default Prefetch TTL or serviceWorkerSeconds value defined in routes.js
42
+ * maxAgeSeconds: 300, // 5 minutes
43
+ *
44
+ * // Allows to override the default forcePrefetchRatio value from install() function
45
+ * includeCacheMisses: true
46
+ * });
47
+ * ```
48
+ * @param {String} url The URL to prefetch
49
+ * @param {String} as Value to use for the "as" attribute of the <link> tag
50
+ * @param {PrefetchConfiguration} config Options to use for the prefetch
51
+ */
52
+ async function prefetch(url, as = 'fetch', config = {
53
+ cors: 'anonymous',
54
+ includeCacheMisses: options.includeCacheMisses,
55
+ forcePrefetchRatio: options.forcePrefetchRatio,
56
+ }) {
57
+ var _a, _b, _c, _d, _e;
58
+ // In case service-worker was not registered by our install() function,
59
+ // this function will add eventListener manually
60
+ install_1.listenForSwMessages();
61
+ if (typeof url === 'undefined') {
62
+ console.error(`prefetch() called with an undefined url`);
63
+ return;
64
+ }
65
+ // Set values from global config if any of config options is missing
66
+ // Set default value if global config value is undefined
67
+ config.cors = (_a = config.cors) !== null && _a !== void 0 ? _a : 'anonymous';
68
+ config.includeCacheMisses = (_c = (_b = config.includeCacheMisses) !== null && _b !== void 0 ? _b : options.includeCacheMisses) !== null && _c !== void 0 ? _c : false;
69
+ config.forcePrefetchRatio = (_e = (_d = config.forcePrefetchRatio) !== null && _d !== void 0 ? _d : options.forcePrefetchRatio) !== null && _e !== void 0 ? _e : 0;
70
+ // @ts-ignore
71
+ if (config.headers) {
72
+ console.warn('The prefetch `headers` configuration option has been removed as it is not compatible with the service-worker prefetch mechanism. Please set headers in your routes.js file with `setRequestHeader` if required.');
73
+ }
74
+ const cacheUrl = modifyUrl(url, config);
75
+ if (typeof TextEncoder !== 'undefined' &&
76
+ new TextEncoder().encode(cacheUrl).length > MAX_URL_BYTES) {
77
+ console.warn(`URL is too many characters to prefetch and cache: ${cacheUrl}`);
78
+ return;
79
+ }
80
+ // This causes all the prefetch request to be called even though they are already cached in SW,
81
+ // but they should be served from SW cache anyway. The impact to the performance with hmtl DOM changes is minimal,
82
+ // tested with 1k prefetch calls.
83
+ if (prefetched.has(cacheUrl)) {
84
+ let prefetchLink = document.querySelector(`link[href='${cacheUrl}']`);
85
+ prefetchLink === null || prefetchLink === void 0 ? void 0 : prefetchLink.remove();
86
+ }
87
+ prefetched.add(cacheUrl);
88
+ await waitForServiceWorker_1.default();
89
+ const link = document.createElement('link');
90
+ const { relList } = link;
91
+ link.setAttribute('href', cacheUrl);
92
+ if (config.cors) {
93
+ link.setAttribute('crossorigin', config.cors);
94
+ }
95
+ link.setAttribute('rel', relList.supports('preload') && /* istanbul ignore next */ !relList.supports('prefetch')
96
+ ? /* istanbul ignore next */ 'preload'
97
+ : 'prefetch' // Safari does not support prefetch so we use preload instead
98
+ );
99
+ link.setAttribute('as', as);
100
+ document.head.append(link);
101
+ }
102
+ exports.prefetch = prefetch;
103
+ function modifyUrl(url, config) {
104
+ const { body, method, maxAgeSeconds } = config;
105
+ const parsed = new URL(url, window.origin);
106
+ maybeAddHeadParam(parsed);
107
+ if (parsed.hostname === getHostName_1.default()) {
108
+ // If we are prefetching from Layer0 sidecar, rewrite the URL to use the sidecar's domain name
109
+ if (options.cacheHost) {
110
+ const cacheHostUrl = new URL(`https://${options.cacheHost}`);
111
+ parsed.hostname = cacheHostUrl.hostname;
112
+ parsed.protocol = cacheHostUrl.protocol;
113
+ parsed.port = cacheHostUrl.port;
114
+ }
115
+ appendSearchParam(parsed, constants_1.DEVTOOLS_PREFETCH_QUERY_PARAM, '1');
116
+ maybeAddThrottleParam(parsed, config);
117
+ }
118
+ if (body) {
119
+ appendSearchParam(parsed, constants_1.POST_BODY_QUERY_PARAM, body);
120
+ }
121
+ if (method) {
122
+ appendSearchParam(parsed, constants_1.METHOD_QUERY_PARAM, method);
123
+ }
124
+ if (maxAgeSeconds && maxAgeSeconds > 0) {
125
+ appendSearchParam(parsed, constants_1.PREFETCH_TTL_PARAM, maxAgeSeconds.toString());
126
+ }
127
+ return parsed.toString();
128
+ }
129
+ function appendSearchParam(url, name, value) {
130
+ const separator = url.search.length ? '&' : '?';
131
+ url.search += `${separator}${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
132
+ }
133
+ /**
134
+ * Adds the query param that indicates to the service worker that a request may be throttled
135
+ * @param url
136
+ * @param config
137
+ */
138
+ function maybeAddThrottleParam(url, config) {
139
+ if (!config.includeCacheMisses && shouldAcceptThrottling(config)) {
140
+ appendSearchParam(url, constants_1.THROTTLED_QUERY_PARAM, '1');
141
+ }
142
+ }
143
+ /**
144
+ * Returns true if the next prefetch request should only be served from the edge cache.
145
+ * This is done by comparing a random number between 0 and 1 to options.forcePrefetchRatio
146
+ */
147
+ function shouldAcceptThrottling(config) {
148
+ return !config.forcePrefetchRatio || Math.random() > config.forcePrefetchRatio;
149
+ }
150
+ function maybeAddHeadParam(url) {
151
+ options.spaRoutes.some(routePattern => {
152
+ if (routePattern.test(url.pathname)) {
153
+ appendSearchParam(url, constants_1.HEAD_QUERY_PARAM, '1');
154
+ return true;
155
+ }
156
+ });
157
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Prefetches a link's href when it is visible in the viewport
3
+ * @param {HTMLElement} el The link element
4
+ */
5
+ export default function prefetchWhenVisible(el: HTMLElement): void;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const prefetch_1 = require("./prefetch");
7
+ const runWhenVisible_1 = __importDefault(require("./runWhenVisible"));
8
+ /**
9
+ * Prefetches a link's href when it is visible in the viewport
10
+ * @param {HTMLElement} el The link element
11
+ */
12
+ function prefetchWhenVisible(el) {
13
+ runWhenVisible_1.default(el, innerEl => {
14
+ const url = innerEl.getAttribute('href');
15
+ if (url) {
16
+ prefetch_1.prefetch(url);
17
+ }
18
+ });
19
+ }
20
+ exports.default = prefetchWhenVisible;
@@ -0,0 +1,2 @@
1
+ export declare function registerServiceWorker(serviceWorkerPath: string): void;
2
+ export declare function unregisterServiceWorker(): void;