@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.
- package/README.md +7 -0
- package/constants.d.ts +23 -0
- package/constants.js +25 -0
- package/index.js +24 -0
- package/package.json +50 -0
- package/sw/CustomCacheKeyPlugin.d.ts +10 -0
- package/sw/CustomCacheKeyPlugin.js +54 -0
- package/sw/CustomExpirationPlugin.d.ts +122 -0
- package/sw/CustomExpirationPlugin.js +280 -0
- package/sw/DeepFetchPlugin.d.ts +108 -0
- package/sw/DeepFetchPlugin.js +144 -0
- package/sw/DevtoolsPlugin.d.ts +64 -0
- package/sw/DevtoolsPlugin.js +131 -0
- package/sw/PrefetchPlugin.d.ts +32 -0
- package/sw/PrefetchPlugin.js +167 -0
- package/sw/Prefetcher.d.ts +209 -0
- package/sw/Prefetcher.js +367 -0
- package/sw/getServiceWorkerMaxAge.d.ts +22 -0
- package/sw/getServiceWorkerMaxAge.js +51 -0
- package/sw/index.d.ts +2 -0
- package/sw/index.js +6 -0
- package/sw/log.d.ts +1 -0
- package/sw/log.js +9 -0
- package/sw/messageBrowser.d.ts +7 -0
- package/sw/messageBrowser.js +18 -0
- package/sw/prefetch.d.ts +9 -0
- package/sw/prefetch.js +17 -0
- package/window/InstallOptions.d.ts +77 -0
- package/window/InstallOptions.js +2 -0
- package/window/PrefetchOptions.d.ts +19 -0
- package/window/PrefetchOptions.js +2 -0
- package/window/entry.d.ts +1 -0
- package/window/entry.js +8 -0
- package/window/getHostName.d.ts +1 -0
- package/window/getHostName.js +6 -0
- package/window/getUserAgent.d.ts +1 -0
- package/window/getUserAgent.js +6 -0
- package/window/index.d.ts +3 -0
- package/window/index.js +8 -0
- package/window/install.d.ts +13 -0
- package/window/install.js +72 -0
- package/window/isSafari.d.ts +6 -0
- package/window/isSafari.js +31 -0
- package/window/loadManifest.d.ts +3 -0
- package/window/loadManifest.js +20 -0
- package/window/prefetch.d.ts +71 -0
- package/window/prefetch.js +157 -0
- package/window/prefetchWhenVisible.d.ts +5 -0
- package/window/prefetchWhenVisible.js +20 -0
- package/window/registerServiceWorker.d.ts +2 -0
- package/window/registerServiceWorker.js +66 -0
- package/window/runWhenVisible.d.ts +6 -0
- package/window/runWhenVisible.js +28 -0
- package/window/waitForServiceWorker.d.ts +4 -0
- package/window/waitForServiceWorker.js +27 -0
- package/window/watchElements.d.ts +7 -0
- package/window/watchElements.js +63 -0
- package/window/watchLinks.d.ts +9 -0
- 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;
|
package/sw/prefetch.d.ts
ADDED
|
@@ -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,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 @@
|
|
|
1
|
+
export {};
|
package/window/entry.js
ADDED
|
@@ -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 @@
|
|
|
1
|
+
export default function getUserAgent(): string;
|
package/window/index.js
ADDED
|
@@ -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,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,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;
|