@networkpro/web 1.4.3 → 1.5.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/package.json +1 -1
- package/src/app.html +42 -2
- package/src/global.d.ts +16 -7
- package/src/hooks.client.ts +16 -0
- package/src/lib/components/PWAInstallButton.svelte +13 -22
- package/src/lib/registerServiceWorker.js +7 -0
- package/src/lib/styles/css/brands.css +1 -1
- package/src/lib/styles/css/brands.min.css +1 -1
- package/src/lib/styles/css/solid.min.css +1 -1
- package/src/lib/unregisterServiceWorker.js +18 -0
- package/src/routes/+layout.js +8 -0
- package/src/routes/+layout.svelte +2 -28
- package/src/service-worker.js +15 -11
- package/tests/unit/unregisterServiceWorker.test.js +40 -0
- /package/{src/lib/styles → static}/webfonts/fa-brands-400.ttf +0 -0
- /package/{src/lib/styles → static}/webfonts/fa-brands-400.woff2 +0 -0
- /package/{src/lib/styles → static}/webfonts/fa-solid-900.ttf +0 -0
- /package/{src/lib/styles → static}/webfonts/fa-solid-900.woff2 +0 -0
package/package.json
CHANGED
package/src/app.html
CHANGED
|
@@ -32,12 +32,52 @@
|
|
|
32
32
|
<link rel="preconnect" href="https://snap.licdn.com" crossorigin />
|
|
33
33
|
<link rel="preconnect" href="https://px.ads.linkedin.com" crossorigin />
|
|
34
34
|
|
|
35
|
-
<!--
|
|
36
|
-
<link
|
|
35
|
+
<!-- Preload FontAwesome webfonts -->
|
|
36
|
+
<link
|
|
37
|
+
rel="preload"
|
|
38
|
+
href="/webfonts/fa-solid-900.woff2"
|
|
39
|
+
as="font"
|
|
40
|
+
type="font/woff2"
|
|
41
|
+
crossorigin="anonymous" />
|
|
42
|
+
<link
|
|
43
|
+
rel="preload"
|
|
44
|
+
href="/webfonts/fa-brands-400.woff2"
|
|
45
|
+
as="font"
|
|
46
|
+
type="font/woff2"
|
|
47
|
+
crossorigin="anonymous" />
|
|
48
|
+
|
|
49
|
+
<!-- PWA -->
|
|
37
50
|
<meta name="theme-color" content="#ffc627" />
|
|
51
|
+
<link rel="manifest" href="/manifest.json" />
|
|
52
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
53
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
54
|
+
<meta
|
|
55
|
+
name="apple-mobile-web-app-status-bar-style"
|
|
56
|
+
content="black-translucent" />
|
|
57
|
+
|
|
58
|
+
<meta
|
|
59
|
+
name="facebook-domain-verification"
|
|
60
|
+
content="bx4ham0zkpvzztzu213bhpt76m9siq" />
|
|
38
61
|
|
|
39
62
|
<meta name="generator" content="SvelteKit 2.21.1" />
|
|
40
63
|
|
|
64
|
+
<script>
|
|
65
|
+
if (location.search.includes("nosw")) {
|
|
66
|
+
window.__DISABLE_SW__ = true;
|
|
67
|
+
console.warn("🧪 Service worker disabled via ?nosw flag in URL.");
|
|
68
|
+
|
|
69
|
+
if ("serviceWorker" in navigator) {
|
|
70
|
+
navigator.serviceWorker.getRegistrations().then((registrations) => {
|
|
71
|
+
for (const registration of registrations) {
|
|
72
|
+
registration.unregister().then((success) => {
|
|
73
|
+
console.log("🧹 SW unregistered:", success);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
|
|
41
81
|
%sveltekit.head%
|
|
42
82
|
</head>
|
|
43
83
|
<body>
|
package/src/global.d.ts
CHANGED
|
@@ -5,11 +5,20 @@ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
|
5
5
|
This file is part of Network Pro.
|
|
6
6
|
========================================================================== */
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
declare global {
|
|
9
|
+
interface BeforeInstallPromptEvent extends Event {
|
|
10
|
+
readonly platforms: string[];
|
|
11
|
+
readonly userChoice: Promise<{
|
|
12
|
+
outcome: 'accepted' | 'dismissed';
|
|
13
|
+
platform: string;
|
|
14
|
+
}>;
|
|
15
|
+
prompt(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Window {
|
|
19
|
+
__DISABLE_SW__?: boolean;
|
|
20
|
+
}
|
|
15
21
|
}
|
|
22
|
+
|
|
23
|
+
export { };
|
|
24
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
src/hooks.client.ts
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== */
|
|
7
|
+
|
|
8
|
+
window.addEventListener("beforeinstallprompt", (e) => {
|
|
9
|
+
// Prevent default install prompt
|
|
10
|
+
e.preventDefault();
|
|
11
|
+
|
|
12
|
+
// Re-dispatch as a custom event so your component can respond
|
|
13
|
+
window.dispatchEvent(new CustomEvent("pwa-install-available", { detail: e }));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export {};
|
|
@@ -14,40 +14,31 @@ This file is part of Network Pro.
|
|
|
14
14
|
/** @type {BeforeInstallPromptEvent | null} */
|
|
15
15
|
let deferredPrompt = null;
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {CustomEvent<BeforeInstallPromptEvent>} PWAInstallAvailableEvent
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
17
|
onMount(() => {
|
|
22
18
|
/**
|
|
23
|
-
*
|
|
24
|
-
* to enable a custom install experience.
|
|
25
|
-
*
|
|
26
|
-
* TypeScript / svelte-check does not recognize custom events by default,
|
|
27
|
-
* so we cast the base Event to CustomEvent manually.
|
|
19
|
+
* @param {Event} e
|
|
28
20
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
);
|
|
21
|
+
function handleInstallPrompt(e) {
|
|
22
|
+
/** @type {CustomEvent<BeforeInstallPromptEvent>} */
|
|
23
|
+
const customEvent = /** @type {CustomEvent} */ (e);
|
|
24
|
+
deferredPrompt = customEvent.detail;
|
|
25
|
+
show = true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
window.addEventListener("pwa-install-available", handleInstallPrompt);
|
|
29
|
+
|
|
30
|
+
return () => {
|
|
31
|
+
window.removeEventListener("pwa-install-available", handleInstallPrompt);
|
|
32
|
+
};
|
|
37
33
|
});
|
|
38
34
|
|
|
39
|
-
/**
|
|
40
|
-
* Trigger the native install prompt and handle user response
|
|
41
|
-
*/
|
|
42
35
|
async function promptInstall() {
|
|
43
36
|
if (!deferredPrompt) return;
|
|
44
37
|
|
|
45
38
|
deferredPrompt.prompt();
|
|
46
|
-
|
|
47
39
|
const { outcome } = await deferredPrompt.userChoice;
|
|
48
40
|
console.log(`User response to PWA install prompt: ${outcome}`);
|
|
49
41
|
|
|
50
|
-
// Always hide the button after interaction
|
|
51
42
|
show = false;
|
|
52
43
|
deferredPrompt = null;
|
|
53
44
|
}
|
|
@@ -10,6 +10,13 @@ This file is part of Network Pro.
|
|
|
10
10
|
* browser/environment compatibility checks. This supports offline usage and PWA behavior.
|
|
11
11
|
*/
|
|
12
12
|
export function registerServiceWorker() {
|
|
13
|
+
const disableSW = window?.__DISABLE_SW__ || false;
|
|
14
|
+
|
|
15
|
+
if (disableSW) {
|
|
16
|
+
console.warn("⚠️ Service Worker registration disabled via diagnostic mode.");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
if ('serviceWorker' in navigator) {
|
|
14
21
|
// Skip registration in Firefox during development
|
|
15
22
|
const isFirefox = navigator.userAgent.includes('Firefox');
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
font-style: normal;
|
|
13
13
|
font-weight: 400;
|
|
14
14
|
font-display: block;
|
|
15
|
-
src: url("
|
|
15
|
+
src: url("/webfonts/fa-brands-400.woff2") format("woff2"), url("/webfonts/fa-brands-400.ttf") format("truetype"); }
|
|
16
16
|
|
|
17
17
|
.fab,
|
|
18
18
|
.fa-brands {
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
|
4
4
|
* Copyright 2024 Fonticons, Inc.
|
|
5
5
|
*/
|
|
6
|
-
:root,:host{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(
|
|
6
|
+
:root,:host{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(/webfonts/fa-brands-400.woff2)format("woff2"),url(/webfonts/fa-brands-400.ttf)format("truetype")}.fab,.fa-brands{font-weight:400}.fa-square-instagram,.fa-instagram-square{--fa:""}.fa-threads{--fa:""}.fa-linkedin-in{--fa:""}.fa-square-twitter,.fa-twitter-square{--fa:""}.fa-500px{--fa:""}.fa-mastodon{--fa:""}.fa-square-github,.fa-github-square{--fa:""}.fa-x-twitter{--fa:""}.fa-square-facebook,.fa-facebook-square{--fa:""}.fa-linkedin{--fa:""}.fa-instagram{--fa:""}.fa-facebook{--fa:""}.fa-github{--fa:""}.fa-twitter{--fa:""}.fa-square-x-twitter{--fa:""}
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
|
4
4
|
* Copyright 2024 Fonticons, Inc.
|
|
5
5
|
*/
|
|
6
|
-
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(
|
|
6
|
+
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(/webfonts/fa-solid-900.woff2) format("woff2"),url(/webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
src/lib/unregisterServiceWorker.js
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Allows for manual toggling of the service worker
|
|
10
|
+
*/
|
|
11
|
+
export function unregisterServiceWorker() {
|
|
12
|
+
if ('serviceWorker' in navigator) {
|
|
13
|
+
navigator.serviceWorker.getRegistrations().then((registrations) => {
|
|
14
|
+
registrations.forEach((reg) => reg.unregister());
|
|
15
|
+
console.log("🧹 All service workers unregistered.");
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/routes/+layout.js
CHANGED
|
@@ -21,6 +21,9 @@ const fallbackMeta = {
|
|
|
21
21
|
"Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro™",
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
import { browser } from "$app/environment";
|
|
25
|
+
import { registerServiceWorker } from "$lib/registerServiceWorker.js";
|
|
26
|
+
|
|
24
27
|
export const prerender = "auto";
|
|
25
28
|
export const trailingSlash = "never";
|
|
26
29
|
|
|
@@ -31,6 +34,11 @@ export const trailingSlash = "never";
|
|
|
31
34
|
export function load({ url }) {
|
|
32
35
|
const normalizedPathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
33
36
|
|
|
37
|
+
if (browser) {
|
|
38
|
+
// Move service worker registration here
|
|
39
|
+
registerServiceWorker();
|
|
40
|
+
}
|
|
41
|
+
|
|
34
42
|
return {
|
|
35
43
|
pathname: normalizedPathname,
|
|
36
44
|
meta: fallbackMeta, // Required to ensure meta always exists for typing
|
|
@@ -14,7 +14,6 @@ This file is part of Network Pro.
|
|
|
14
14
|
import HeaderHome from "$lib/components/layout/HeaderHome.svelte";
|
|
15
15
|
import PWAInstallButton from "$lib/components/PWAInstallButton.svelte";
|
|
16
16
|
import { browser } from "$app/environment";
|
|
17
|
-
import { registerServiceWorker } from "$lib/registerServiceWorker.js";
|
|
18
17
|
import "$lib/styles";
|
|
19
18
|
|
|
20
19
|
// Import favicon images
|
|
@@ -23,18 +22,6 @@ This file is part of Network Pro.
|
|
|
23
22
|
import faviconSvg from "$lib/img/favicon.svg";
|
|
24
23
|
import appleTouchIcon from "$lib/img/icon-180x180.png";
|
|
25
24
|
|
|
26
|
-
/**
|
|
27
|
-
* @type {string}
|
|
28
|
-
* Style class for the mobile-web-app-capable meta tag.
|
|
29
|
-
* OpenGraph URL for the website.
|
|
30
|
-
* Company name for the website.
|
|
31
|
-
* Twitter account for the website.
|
|
32
|
-
*/
|
|
33
|
-
const webApp = "mobile-web-app-capable";
|
|
34
|
-
const ogUrl = "https://netwk.pro";
|
|
35
|
-
const companyName = "Network Pro Strategies";
|
|
36
|
-
const twitterAct = "@NetEng_Pro";
|
|
37
|
-
|
|
38
25
|
if (browser) {
|
|
39
26
|
// Preload logo images
|
|
40
27
|
[logoPng, logoWbp].forEach((src) => {
|
|
@@ -46,9 +33,6 @@ This file is part of Network Pro.
|
|
|
46
33
|
const touchImg = new Image();
|
|
47
34
|
// Preload Apple Touch icon
|
|
48
35
|
touchImg.src = appleTouchIcon;
|
|
49
|
-
|
|
50
|
-
// Register the service worker
|
|
51
|
-
registerServiceWorker();
|
|
52
36
|
}
|
|
53
37
|
|
|
54
38
|
// fallback values if data.meta not set
|
|
@@ -62,7 +46,7 @@ This file is part of Network Pro.
|
|
|
62
46
|
</script>
|
|
63
47
|
|
|
64
48
|
<svelte:head>
|
|
65
|
-
<!--
|
|
49
|
+
<!-- Dynamic preloads only, meta moved to $lib/components/MetaTags.svelte -->
|
|
66
50
|
<link rel="preload" href={logoWbp} as="image" type="image/webp" />
|
|
67
51
|
<link rel="preload" href={logoPng} as="image" type="image/png" />
|
|
68
52
|
<link rel="preload" href={faviconSvg} as="image" type="image/svg+xml" />
|
|
@@ -71,18 +55,8 @@ This file is part of Network Pro.
|
|
|
71
55
|
<link rel="icon" href={faviconSvg} type="image/svg+xml" />
|
|
72
56
|
<link rel="apple-touch-icon" href={appleTouchIcon} />
|
|
73
57
|
|
|
74
|
-
<!--
|
|
75
|
-
<link rel="manifest" href="/manifest.json" />
|
|
76
|
-
<meta name={webApp} content="yes" />
|
|
77
|
-
<meta name={"apple-" + webApp} content="yes" />
|
|
78
|
-
<meta
|
|
79
|
-
name="apple-mobile-web-app-status-bar-style"
|
|
80
|
-
content="black-translucent" />
|
|
58
|
+
<!-- Static moved to app.html 2025-05-21 -->
|
|
81
59
|
<meta name="theme-color" content="#ffc627" />
|
|
82
|
-
|
|
83
|
-
<meta
|
|
84
|
-
name="facebook-domain-verification"
|
|
85
|
-
content="bx4ham0zkpvzztzu213bhpt76m9siq" />
|
|
86
60
|
</svelte:head>
|
|
87
61
|
|
|
88
62
|
<!-- BEGIN HEADER -->
|
package/src/service-worker.js
CHANGED
|
@@ -26,22 +26,26 @@ const ASSETS = Array.from(
|
|
|
26
26
|
const url = new URL(path, location.origin);
|
|
27
27
|
const hostname = url.hostname;
|
|
28
28
|
|
|
29
|
+
const IGNORE_PATHS = new Set([
|
|
30
|
+
"/img/banner-1280x640.png",
|
|
31
|
+
"/img/banner-og-1200x630.png",
|
|
32
|
+
"/img/logo-transparent.png",
|
|
33
|
+
"/img/logo.png",
|
|
34
|
+
"/img/svelte.png",
|
|
35
|
+
"/webfonts/fa-brands-400.ttf",
|
|
36
|
+
"/webfonts/fa-solid-900.ttf",
|
|
37
|
+
"/robots.txt",
|
|
38
|
+
"/screenshots/desktop-foss.png",
|
|
39
|
+
"/sitemap.xml",
|
|
40
|
+
"/CNAME",
|
|
41
|
+
]);
|
|
42
|
+
|
|
29
43
|
const shouldExclude =
|
|
30
44
|
path.startsWith("http") ||
|
|
31
45
|
disallowedHosts.some(
|
|
32
46
|
(host) => hostname === host || hostname.endsWith(`.${host}`),
|
|
33
47
|
) ||
|
|
34
|
-
|
|
35
|
-
"/img/banner-1280x640.png",
|
|
36
|
-
"/img/banner-og-1200x630.png",
|
|
37
|
-
"/img/logo-transparent.png",
|
|
38
|
-
"/img/logo.png",
|
|
39
|
-
"/img/svelte.png",
|
|
40
|
-
"/robots.txt",
|
|
41
|
-
"/screenshots/desktop-foss.png",
|
|
42
|
-
"/sitemap.xml",
|
|
43
|
-
"/CNAME",
|
|
44
|
-
].includes(path);
|
|
48
|
+
IGNORE_PATHS.has(path);
|
|
45
49
|
|
|
46
50
|
if (shouldExclude) excludedAssets.push(path);
|
|
47
51
|
return !shouldExclude;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
tests/unit/unregisterServiceWorker.test.js
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== */
|
|
7
|
+
|
|
8
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
9
|
+
import { unregisterServiceWorker } from "../../src/lib/unregisterServiceWorker.js";
|
|
10
|
+
|
|
11
|
+
describe("unregisterServiceWorker()", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
// Clean up any mocks from previous runs
|
|
14
|
+
vi.restoreAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should call unregister on all registered service workers", async () => {
|
|
18
|
+
const mockUnregister1 = vi.fn();
|
|
19
|
+
const mockUnregister2 = vi.fn();
|
|
20
|
+
|
|
21
|
+
// Minimal mock objects
|
|
22
|
+
const mockRegistration1 = { unregister: mockUnregister1 };
|
|
23
|
+
const mockRegistration2 = { unregister: mockUnregister2 };
|
|
24
|
+
|
|
25
|
+
// Stub getRegistrations to return mock service workers
|
|
26
|
+
Object.defineProperty(navigator, "serviceWorker", {
|
|
27
|
+
configurable: true,
|
|
28
|
+
value: {
|
|
29
|
+
getRegistrations: vi
|
|
30
|
+
.fn()
|
|
31
|
+
.mockResolvedValue([mockRegistration1, mockRegistration2]),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await unregisterServiceWorker();
|
|
36
|
+
|
|
37
|
+
expect(mockUnregister1).toHaveBeenCalled();
|
|
38
|
+
expect(mockUnregister2).toHaveBeenCalled();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|