@networkpro/web 1.4.3 → 1.5.1
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 +154 -62
- package/package.json +10 -9
- package/scripts/flattenHeaders.js +2 -2
- package/scripts/validateHeaders.js +1 -1
- package/src/app.html +43 -3
- 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/components/RedirectPage.svelte +62 -0
- package/src/lib/meta.js +1 -9
- package/src/lib/pages/TermsUseContent.svelte +5 -4
- 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/types/metadata.js +17 -0
- package/src/lib/unregisterServiceWorker.js +18 -0
- package/src/lib/utils/utm.js +19 -0
- package/src/routes/+layout.js +8 -0
- package/src/routes/+layout.svelte +2 -28
- package/src/routes/consultation/+page.svelte +34 -0
- package/src/routes/contact/+page.svelte +34 -0
- package/src/routes/privacy-rights/+page.svelte +34 -0
- package/src/service-worker.js +15 -11
- package/tests/unit/unregisterServiceWorker.test.js +40 -0
- package/tests/unit/utm.test.js +48 -0
- package/src/routes/contact/+page.server.js +0 -24
- package/src/routes/privacy-rights/+page.server.js +0 -24
- /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/README.md
CHANGED
|
@@ -1,116 +1,208 @@
|
|
|
1
|
-
|
|
2
|
-
README.md
|
|
1
|
+
# 🌐 Network Pro — Web Presence
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
========================================================================== -->
|
|
3
|
+
> **Locking Down Networks, Unlocking Confidence™**
|
|
4
|
+
> _Security, Networking, Privacy — Network Pro™_
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
Copyright © 2025 Network Pro Strategies (Network Pro)
|
|
6
|
+
|
|
10
7
|
|
|
11
|
-
|
|
8
|
+
## 🚀 Project Overview
|
|
9
|
+
|
|
10
|
+
This is the official web presence for **[Network Pro Strategies](https://netwk.pro/about)**, a privacy-forward consultancy specializing in network engineering, information security, and public advocacy focused on cybersecurity and digital privacy.
|
|
11
|
+
|
|
12
|
+
Built with [SvelteKit](https://kit.svelte.dev/) and deployed via [Netlify](https://www.netlify.com/).
|
|
13
|
+
Blog and documentation subsites built with [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) and deployed via [GitHub Pages](https://pages.github.com/).
|
|
14
|
+
All infrastructure and data flows are designed with **maximum transparency, self-hosting, and user privacy** in mind.
|
|
15
|
+
|
|
16
|
+
### 📁 Repository Structure
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
```bash
|
|
19
|
+
.
|
|
20
|
+
├── src/
|
|
21
|
+
│ ├── lib/ # Reusable components, styles, utilities
|
|
22
|
+
│ ├── routes/ # SvelteKit routes (+page.svelte, +page.server.js)
|
|
23
|
+
│ ├── hooks.client.ts # Client-only lifecycle hooks (e.g., SW control)
|
|
24
|
+
│ └── app.html # SvelteKit entry HTML with CSP/meta/bootstrap
|
|
25
|
+
├── tests/ # Vitest test suites
|
|
26
|
+
├── public/ # Static assets served at root
|
|
27
|
+
├── netlify.toml # Netlify configuration
|
|
28
|
+
├── .github/ # CI workflows and automation
|
|
29
|
+
└── ...
|
|
30
|
+
```
|
|
14
31
|
|
|
15
|
-
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## 🛠 Getting Started
|
|
35
|
+
|
|
36
|
+
### 📦 Environment Setup
|
|
16
37
|
|
|
17
|
-
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/netwk-pro/netwk-pro.github.io.git
|
|
40
|
+
cd netwk-pro.github.io
|
|
41
|
+
cp .env.template .env
|
|
42
|
+
npm install
|
|
43
|
+
```
|
|
18
44
|
|
|
19
|
-
|
|
45
|
+
Edit .env to configure your environment mode:
|
|
20
46
|
|
|
21
|
-
|
|
47
|
+
```env
|
|
48
|
+
ENV_MODE=dev # Options: dev, test, ci, preview, prod
|
|
49
|
+
```
|
|
22
50
|
|
|
23
|
-
|
|
24
|
-
|
|
51
|
+
> ENV*MODE is used for tooling and workflows — not by SvelteKit itself.
|
|
52
|
+
> Use VITE*-prefixed env variables for runtime values.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
### 📐 Node.js Version Management
|
|
57
|
+
|
|
58
|
+
This repo uses .nvmrc and .node-version for version consistency with tools like:
|
|
59
|
+
|
|
60
|
+
- nvm
|
|
61
|
+
- asdf
|
|
62
|
+
- Volta
|
|
63
|
+
- GitHub Actions
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node -v # Should match "engines" in package.json
|
|
67
|
+
npm -v
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
|
|
25
71
|
|
|
26
72
|
---
|
|
27
73
|
|
|
28
|
-
|
|
74
|
+
|
|
29
75
|
|
|
30
|
-
|
|
76
|
+
## 📜 Available Scripts
|
|
31
77
|
|
|
32
|
-
|
|
33
|
-
FITNESS FOR A PARTICULAR PURPOSE.
|
|
78
|
+
The following CLI commands are available via `npm run <script>` or `pnpm run <script>`.
|
|
34
79
|
|
|
35
|
-
|
|
80
|
+
### 🔄 Development
|
|
36
81
|
|
|
37
|
-
|
|
38
|
-
|
|
82
|
+
| Script | Description |
|
|
83
|
+
| --------------- | ---------------------------------- |
|
|
84
|
+
| `dev` | Start development server with Vite |
|
|
85
|
+
| `preview` | Preview production build locally |
|
|
86
|
+
| `build` | Build the project with Vite |
|
|
87
|
+
| `build:netlify` | Build using Netlify CLI |
|
|
88
|
+
| `css:bundle` | Bundle and minify CSS |
|
|
39
89
|
|
|
40
90
|
---
|
|
41
91
|
|
|
42
|
-
|
|
43
|
-
Email: <contact@neteng.pro>
|
|
44
|
-
Web: <https://bio.neteng.pro>
|
|
45
|
-
-->
|
|
92
|
+
### ✅ Pre-check / Sync
|
|
46
93
|
|
|
47
|
-
|
|
94
|
+
| Script | Description |
|
|
95
|
+
| ------------- | ------------------------------------------------------------ |
|
|
96
|
+
| `prepare` | Run SvelteKit sync |
|
|
97
|
+
| `check` | Run SvelteKit sync and type check with `svelte-check` |
|
|
98
|
+
| `check:watch` | Watch mode for type checks |
|
|
99
|
+
| `check:node` | Validate Node & NPM versions match package.json `engines` |
|
|
100
|
+
| `checkout` | Full local validation: check versions, test, lint, typecheck |
|
|
101
|
+
| `verify` | Alias for `checkout` |
|
|
48
102
|
|
|
49
|
-
|
|
103
|
+
---
|
|
50
104
|
|
|
51
|
-
|
|
105
|
+
### 🧹 Cleanup & Maintenance
|
|
52
106
|
|
|
53
|
-
|
|
107
|
+
| Script | Description |
|
|
108
|
+
| --------- | ----------------------------------------------- |
|
|
109
|
+
| `delete` | Remove build artifacts and `node_modules` |
|
|
110
|
+
| `clean` | Fully reset environment and reinstall |
|
|
111
|
+
| `upgrade` | Update all dependencies via `npm-check-updates` |
|
|
54
112
|
|
|
55
|
-
|
|
56
|
-
[](https://github.com/prettier/prettier) [](https://stylelint.io/)
|
|
57
|
-
[](https://github.com/netwk-pro/netwk-pro.github.io/blob/master/CODE_OF_CONDUCT.md)
|
|
113
|
+
---
|
|
58
114
|
|
|
59
|
-
|
|
115
|
+
### 🧪 Testing
|
|
60
116
|
|
|
61
|
-
|
|
117
|
+
| Script | Description |
|
|
118
|
+
| --------------- | -------------------------------------------- |
|
|
119
|
+
| `test` | Alias for `test:all` |
|
|
120
|
+
| `test:all` | Run both client and server test suites |
|
|
121
|
+
| `test:client` | Run client tests with Vitest |
|
|
122
|
+
| `test:server` | Run server-side tests with Vitest |
|
|
123
|
+
| `test:watch` | Watch mode for client tests |
|
|
124
|
+
| `test:coverage` | Collect coverage from both client and server |
|
|
62
125
|
|
|
63
|
-
|
|
126
|
+
---
|
|
64
127
|
|
|
65
|
-
###
|
|
128
|
+
### 🧼 Linting & Formatting
|
|
66
129
|
|
|
67
|
-
|
|
130
|
+
| Script | Description |
|
|
131
|
+
| ------------ | --------------------------------------- |
|
|
132
|
+
| `lint` | Run ESLint on JS, MJS, and Svelte files |
|
|
133
|
+
| `lint:fix` | Auto-fix ESLint issues |
|
|
134
|
+
| `lint:jsdoc` | Check JSDoc annotations |
|
|
135
|
+
| `lint:css` | Run Stylelint on CSS and Svelte styles |
|
|
136
|
+
| `lint:md` | Lint Markdown content |
|
|
137
|
+
| `lint:all` | Run all linters and formatting checks |
|
|
138
|
+
| `format` | Run Prettier formatting check |
|
|
139
|
+
| `format:fix` | Auto-format code using Prettier |
|
|
68
140
|
|
|
69
|
-
|
|
70
|
-
- **Firewall Architecture & Policy Optimization**
|
|
71
|
-
- **Cloud Security & Zero Trust Implementation**
|
|
72
|
-
- **Secure Infrastructure Design & Implementation**
|
|
73
|
-
- **Risk Reduction & Security Posture Assessment**
|
|
141
|
+
---
|
|
74
142
|
|
|
75
|
-
|
|
143
|
+
### 💡 Lighthouse / Performance
|
|
76
144
|
|
|
77
|
-
|
|
145
|
+
| Script | Description |
|
|
146
|
+
| ------------------ | ----------------------------------------------- |
|
|
147
|
+
| `lhci` | Alias for Lighthouse CI |
|
|
148
|
+
| `lighthouse` | Run local Lighthouse test and launch viewer |
|
|
149
|
+
| `lighthouse:local` | Build site, preview, and run Lighthouse locally |
|
|
150
|
+
| `lhci:run` | Run Lighthouse CI autorun |
|
|
78
151
|
|
|
79
|
-
|
|
152
|
+
---
|
|
80
153
|
|
|
81
|
-
|
|
154
|
+
### 📋 Audits / Validation
|
|
82
155
|
|
|
83
|
-
|
|
156
|
+
| Script | Description |
|
|
157
|
+
| --------------- | -------------------------------------------- |
|
|
158
|
+
| `audit:scripts` | Check for untested utility scripts |
|
|
159
|
+
| `head:flatten` | Flatten headers for Netlify |
|
|
160
|
+
| `head:validate` | Validate headers file against project config |
|
|
84
161
|
|
|
85
162
|
---
|
|
86
163
|
|
|
87
|
-
|
|
164
|
+
### 🔄 Lifecycle Hooks
|
|
88
165
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
| <img decoding="async" loading="lazy" src="https://netwk.pro/img/qr/vcard.png" width="125px" height="125px" alt="vCard"> | **vCard**<br /> <br /><a href="https://raw.githubusercontent.com/netwk-pro/netwk-pro.github.io/refs/heads/master/assets/bin/contact.vcf" download type="text/vcard">**vcf**</a> |
|
|
166
|
+
| Script | Description |
|
|
167
|
+
| ------------- | ----------------------------------- |
|
|
168
|
+
| `postinstall` | Ensures version check after install |
|
|
93
169
|
|
|
94
|
-
|
|
170
|
+
|
|
95
171
|
|
|
96
172
|
---
|
|
97
173
|
|
|
98
|
-
|
|
174
|
+
|
|
99
175
|
|
|
100
|
-
|
|
101
|
-
[Privacy Policy](https://netwk.pro/privacy-policy) | [Legal](https://netwk.pro/license)
|
|
176
|
+
## 🧾 License
|
|
102
177
|
|
|
103
|
-
|
|
178
|
+
This project is licensed under:
|
|
179
|
+
|
|
180
|
+
- [Creative Commons BY 4.0](https://netwk.pro/license#cc-by)
|
|
181
|
+
|
|
182
|
+
- Or optionally, [GNU GPL v3 or later](https://netwk.pro/license#gnu-gpl)
|
|
183
|
+
|
|
184
|
+
Source code, branding, and visual assets are subject to reuse and distribution terms specified on our [Legal, Copyright, and Licensing page](https://netwk.pro/license).
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
## 🙋♂️ Questions?
|
|
189
|
+
|
|
190
|
+
Reach out via [netwk.pro/contact](https://netwk.pro/contact), open an issue on this repo, or email us directly at `contact (at) s.neteng.pro`.
|
|
104
191
|
|
|
105
192
|
|
|
106
193
|
|
|
107
|
-
|
|
194
|
+
_Designed for professionals. Hardened for privacy. Built with intent._
|
|
195
|
+
— **Network Pro Strategies**
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
<div style="font-size: 12px; text-align: center;">
|
|
108
200
|
|
|
109
201
|
Copyright © 2025
|
|
110
|
-
**[Network Pro Strategies](https://netwk.pro
|
|
202
|
+
**[Network Pro Strategies](https://netwk.pro) (Network Pro™)**
|
|
111
203
|
|
|
112
204
|
Network Pro™, the shield logo, and the "Locking Down Networks™" slogan are [trademarks](https://netwk.pro/license#trademark) of Network Pro Strategies.
|
|
113
205
|
|
|
114
|
-
Licensed under **[CC BY 4.0](https://netwk.pro/license#cc-by)** and the **[GNU GPL](https://netwk.pro/license#gnu-gpl)**, as published by the [Free Software Foundation](https://fsf.org), either version 3 of the License, or (at your option) any later version.
|
|
206
|
+
Licensed under **[CC BY 4.0](https://netwk.pro/license#cc-by)** and the **[GNU GPL](https://netwk.pro/license#gnu-gpl)**, as published by the [Free Software Foundation](https://www.fsf.org), either version 3 of the License, or (at your option) any later version.
|
|
115
207
|
|
|
116
|
-
</
|
|
208
|
+
</div>
|
package/package.json
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
"name": "@networkpro/web",
|
|
3
3
|
"private": false,
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.5.1",
|
|
6
6
|
"description": "Locking Down Networks, Unlocking Confidence | Security, Networking, Privacy — Network Pro Strategies",
|
|
7
7
|
"keywords": [
|
|
8
|
-
"security",
|
|
9
|
-
"networking",
|
|
10
|
-
"privacy",
|
|
11
|
-
"cybersecurity",
|
|
12
8
|
"advisory",
|
|
13
9
|
"consulting",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
10
|
+
"cybersecurity",
|
|
11
|
+
"networking",
|
|
12
|
+
"privacy",
|
|
13
|
+
"pwa",
|
|
14
|
+
"security",
|
|
15
|
+
"svelte",
|
|
16
|
+
"sveltekit"
|
|
16
17
|
],
|
|
17
18
|
"homepage": "https://netwk.pro",
|
|
18
19
|
"bugs": {
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
"postinstall": "npm run check:node"
|
|
85
86
|
},
|
|
86
87
|
"dependencies": {
|
|
87
|
-
"svelte": "5.
|
|
88
|
+
"svelte": "5.32.1"
|
|
88
89
|
},
|
|
89
90
|
"devDependencies": {
|
|
90
91
|
"@eslint/compat": "^1.2.9",
|
|
@@ -102,7 +103,7 @@
|
|
|
102
103
|
"eslint": "^9.27.0",
|
|
103
104
|
"eslint-config-prettier": "^10.1.5",
|
|
104
105
|
"eslint-plugin-jsdoc": "^50.6.17",
|
|
105
|
-
"eslint-plugin-svelte": "^3.
|
|
106
|
+
"eslint-plugin-svelte": "^3.9.0",
|
|
106
107
|
"globals": "^16.1.0",
|
|
107
108
|
"jsdom": "^26.1.0",
|
|
108
109
|
"lightningcss": "^1.30.1",
|
|
@@ -16,8 +16,8 @@ This file is part of Network Pro.
|
|
|
16
16
|
|
|
17
17
|
import fs from "fs";
|
|
18
18
|
|
|
19
|
-
const HEADERS_PATH = "
|
|
20
|
-
const OUTPUT_PATH = "./
|
|
19
|
+
const HEADERS_PATH = "./.headers_new"; // update if needed
|
|
20
|
+
const OUTPUT_PATH = "./_headers.flattened";
|
|
21
21
|
|
|
22
22
|
const lines = fs.readFileSync(HEADERS_PATH, "utf-8").split("\n");
|
|
23
23
|
const output = [];
|
package/src/app.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
|
|
17
17
|
<meta name="author" content="Network Pro Strategies" />
|
|
18
18
|
|
|
19
|
-
<!--
|
|
19
|
+
<!-- CSP for Dev -->
|
|
20
20
|
<meta
|
|
21
21
|
http-equiv="Content-Security-Policy"
|
|
22
22
|
content="default-src 'self'; script-src 'self' 'unsafe-inline' https://snap.licdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://px.ads.linkedin.com; connect-src 'self' https://px.ads.linkedin.com;" />
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!-- ==========================================================================
|
|
2
|
+
src/lib/components/RedirectPage.svelte
|
|
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
|
+
<script>
|
|
9
|
+
import { onMount } from "svelte";
|
|
10
|
+
|
|
11
|
+
export let to;
|
|
12
|
+
export let delay = 3;
|
|
13
|
+
|
|
14
|
+
onMount(() => {
|
|
15
|
+
if (!to) {
|
|
16
|
+
console.warn("⛔ No redirect target provided");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log("✅ Starting redirect to:", to);
|
|
21
|
+
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
window.location.href = to;
|
|
24
|
+
}, delay * 1000);
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<svelte:head>
|
|
29
|
+
<title>Redirecting…</title>
|
|
30
|
+
<meta name="robots" content="noindex, nofollow" />
|
|
31
|
+
</svelte:head>
|
|
32
|
+
|
|
33
|
+
<div class="container">
|
|
34
|
+
<h1>Redirecting…</h1>
|
|
35
|
+
<p>You'll be taken to the destination in just a moment.</p>
|
|
36
|
+
<div class="loading-spinner" aria-hidden="true"></div>
|
|
37
|
+
<p>If nothing happens, <a href={to}>click here</a>.</p>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
.loading-spinner {
|
|
42
|
+
width: 48px;
|
|
43
|
+
height: 48px;
|
|
44
|
+
margin: 2rem auto;
|
|
45
|
+
border: 4px solid #ddd;
|
|
46
|
+
animation: spin 1s linear infinite;
|
|
47
|
+
border-radius: 50%;
|
|
48
|
+
border-top: 4px solid #ffc627;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@keyframes spin {
|
|
52
|
+
to {
|
|
53
|
+
transform: rotate(360deg);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.container {
|
|
58
|
+
text-align: center;
|
|
59
|
+
font-family: system-ui, sans-serif;
|
|
60
|
+
margin-top: 5rem;
|
|
61
|
+
}
|
|
62
|
+
</style>
|
package/src/lib/meta.js
CHANGED
|
@@ -50,15 +50,7 @@ const meta = {
|
|
|
50
50
|
description:
|
|
51
51
|
"FOSS Spotlight | Security, Networking, Privacy — Network Pro™",
|
|
52
52
|
},
|
|
53
|
-
|
|
54
|
-
title: "Contact Form — Network Pro™",
|
|
55
|
-
description: "Contact Form | Security, Networking, Privacy — Network Pro™",
|
|
56
|
-
},
|
|
57
|
-
"/privacy-rights": {
|
|
58
|
-
title: "Privacy Rights Request Form — Network Pro™",
|
|
59
|
-
description:
|
|
60
|
-
"Privacy Rights Request Form | Security, Networking, Privacy — Network Pro™",
|
|
61
|
-
},
|
|
53
|
+
// Excludes redirect-only routes like /contact, /consultation, /privacy-rights
|
|
62
54
|
};
|
|
63
55
|
|
|
64
56
|
/** @type {MetaData} */
|
|
@@ -58,7 +58,7 @@ This file is part of Network Pro.
|
|
|
58
58
|
*/
|
|
59
59
|
const constants = {
|
|
60
60
|
company: "Network Pro Strategies",
|
|
61
|
-
effectiveDate: "May
|
|
61
|
+
effectiveDate: "May 21, 2025",
|
|
62
62
|
classSmall: "small-text",
|
|
63
63
|
rel: "noopener noreferrer",
|
|
64
64
|
hrefTop: "#top",
|
|
@@ -141,9 +141,10 @@ This file is part of Network Pro.
|
|
|
141
141
|
These Terms of Use apply to all platforms associated with the Company,
|
|
142
142
|
including but not limited to:
|
|
143
143
|
<strong>
|
|
144
|
-
GitHub, our main website (hosted via GitHub Pages),
|
|
145
|
-
|
|
146
|
-
media presence (e.g., Facebook, Instagram, X, and similar
|
|
144
|
+
GitHub, our main website (hosted via Netlify and GitHub Pages), Stack
|
|
145
|
+
Overflow Teams, Nextcloud, communications on Discord and/or Slack, and
|
|
146
|
+
our social media presence (e.g., Facebook, Instagram, X, and similar
|
|
147
|
+
platforms).
|
|
147
148
|
</strong>
|
|
148
149
|
</p>
|
|
149
150
|
{:else if link.id === "acceptable-use"}
|
|
@@ -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,17 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
src/lib/types/metadata.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
|
+
* @typedef {object} MetaData
|
|
10
|
+
* @property {string} title - Page title
|
|
11
|
+
* @property {string} description - Meta description for SEO/social
|
|
12
|
+
* @property {string} [image] - Optional OG image URL
|
|
13
|
+
* @property {string} [url] - Canonical URL
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// Export types only for JSDoc
|
|
17
|
+
export /** @typedef {import('./metadata.js').MetaData} MetaData */ {};
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
src/lib/utils/utm.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
|
+
* Append UTM parameter from window.location to a given URL.
|
|
10
|
+
* Returns `null` if not in a browser context.
|
|
11
|
+
* @param {string} url - The base URL to append to
|
|
12
|
+
* @returns {string | null}
|
|
13
|
+
*/
|
|
14
|
+
export function appendUTM(url) {
|
|
15
|
+
if (typeof window === "undefined") return null;
|
|
16
|
+
|
|
17
|
+
const utm = new URLSearchParams(window.location.search).get("utm_source");
|
|
18
|
+
return utm ? `${url}?utm_source=${encodeURIComponent(utm)}` : url;
|
|
19
|
+
}
|
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 -->
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!-- ==========================================================================
|
|
2
|
+
src/routes/consultation/+page.svelte
|
|
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
|
+
<script>
|
|
9
|
+
import RedirectPage from "$lib/components/RedirectPage.svelte";
|
|
10
|
+
import { appendUTM } from "$lib/utils/utm.js";
|
|
11
|
+
import { onMount } from "svelte";
|
|
12
|
+
import { browser } from "$app/environment";
|
|
13
|
+
|
|
14
|
+
/** @type {string | null} */
|
|
15
|
+
let target = null;
|
|
16
|
+
|
|
17
|
+
/** @type {boolean} */
|
|
18
|
+
let show = false;
|
|
19
|
+
|
|
20
|
+
onMount(() => {
|
|
21
|
+
if (!browser) return;
|
|
22
|
+
|
|
23
|
+
target = appendUTM(
|
|
24
|
+
"https://cloud.neteng.pro/index.php/apps/appointments/pub/8clCqQrt3AtGbNrr/form",
|
|
25
|
+
);
|
|
26
|
+
show = true;
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
{#if show && target}
|
|
31
|
+
<RedirectPage to={target} />
|
|
32
|
+
{:else}
|
|
33
|
+
<p style="text-align: center; margin-top: 4rem;">Preparing to redirect…</p>
|
|
34
|
+
{/if}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!-- ==========================================================================
|
|
2
|
+
src/routes/contact/+page.svelte
|
|
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
|
+
<script>
|
|
9
|
+
import RedirectPage from "$lib/components/RedirectPage.svelte";
|
|
10
|
+
import { appendUTM } from "$lib/utils/utm.js";
|
|
11
|
+
import { onMount } from "svelte";
|
|
12
|
+
import { browser } from "$app/environment";
|
|
13
|
+
|
|
14
|
+
/** @type {string | null} */
|
|
15
|
+
let target = null;
|
|
16
|
+
|
|
17
|
+
/** @type {boolean} */
|
|
18
|
+
let show = false;
|
|
19
|
+
|
|
20
|
+
onMount(() => {
|
|
21
|
+
if (!browser) return;
|
|
22
|
+
|
|
23
|
+
target = appendUTM(
|
|
24
|
+
"https://cloud.neteng.pro/index.php/apps/forms/s/nyWEq9fdE7kWAjqMtMySLqJc",
|
|
25
|
+
);
|
|
26
|
+
show = true;
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
{#if show && target}
|
|
31
|
+
<RedirectPage to={target} />
|
|
32
|
+
{:else}
|
|
33
|
+
<p style="text-align: center; margin-top: 4rem;">Preparing to redirect…</p>
|
|
34
|
+
{/if}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!-- ==========================================================================
|
|
2
|
+
src/routes/privacy-rights/+page.svelte
|
|
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
|
+
<script>
|
|
9
|
+
import RedirectPage from "$lib/components/RedirectPage.svelte";
|
|
10
|
+
import { appendUTM } from "$lib/utils/utm.js";
|
|
11
|
+
import { onMount } from "svelte";
|
|
12
|
+
import { browser } from "$app/environment";
|
|
13
|
+
|
|
14
|
+
/** @type {string | null} */
|
|
15
|
+
let target = null;
|
|
16
|
+
|
|
17
|
+
/** @type {boolean} */
|
|
18
|
+
let show = false;
|
|
19
|
+
|
|
20
|
+
onMount(() => {
|
|
21
|
+
if (!browser) return;
|
|
22
|
+
|
|
23
|
+
target = appendUTM(
|
|
24
|
+
"https://cloud.neteng.pro/index.php/apps/forms/s/6HpZKZCaLwb6TXYL99nLQM8t",
|
|
25
|
+
);
|
|
26
|
+
show = true;
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
{#if show && target}
|
|
31
|
+
<RedirectPage to={target} />
|
|
32
|
+
{:else}
|
|
33
|
+
<p style="text-align: center; margin-top: 4rem;">Preparing to redirect…</p>
|
|
34
|
+
{/if}
|
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
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
tests/unit/utm.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 { appendUTM } from "$lib/utils/utm.js";
|
|
9
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
10
|
+
|
|
11
|
+
describe("appendUTM", () => {
|
|
12
|
+
const originalWindow = globalThis.window;
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
globalThis.window = originalWindow;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should return null when not in a browser environment", () => {
|
|
19
|
+
// @ts-expect-error – simulating SSR
|
|
20
|
+
delete globalThis.window;
|
|
21
|
+
|
|
22
|
+
const url = "https://example.com";
|
|
23
|
+
const result = appendUTM(url);
|
|
24
|
+
expect(result).toBe(null);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should return URL with utm_source appended", () => {
|
|
28
|
+
globalThis.window = {
|
|
29
|
+
// @ts-expect-error – mock minimal window for test
|
|
30
|
+
location: { search: "?utm_source=linkedin" },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const url = "https://example.com";
|
|
34
|
+
const result = appendUTM(url);
|
|
35
|
+
expect(result).toBe("https://example.com?utm_source=linkedin");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should return original URL if no utm_source is present", () => {
|
|
39
|
+
globalThis.window = {
|
|
40
|
+
// @ts-expect-error – mock minimal window for test
|
|
41
|
+
location: { search: "" },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const url = "https://example.com";
|
|
45
|
+
const result = appendUTM(url);
|
|
46
|
+
expect(result).toBe("https://example.com");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/* ==========================================================================
|
|
2
|
-
src/routes/contact/+page.server.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 { redirect } from "@sveltejs/kit";
|
|
9
|
-
|
|
10
|
-
export const prerender = false;
|
|
11
|
-
|
|
12
|
-
/** @type {import('./$types').PageServerLoad} */
|
|
13
|
-
export function load({ url }) {
|
|
14
|
-
const utmSource = url.searchParams.get("utm_source");
|
|
15
|
-
|
|
16
|
-
// Set target to your actual contact page path
|
|
17
|
-
const target =
|
|
18
|
-
"https://cloud.neteng.pro/index.php/apps/forms/s/nyWEq9fdE7kWAjqMtMySLqJc";
|
|
19
|
-
|
|
20
|
-
// Preserve the tracking parameter
|
|
21
|
-
const redirectTo = utmSource ? `${target}?utm_source=${utmSource}` : target;
|
|
22
|
-
|
|
23
|
-
throw redirect(302, redirectTo);
|
|
24
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/* ==========================================================================
|
|
2
|
-
src/routes/privacy-rights/+page.server.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 { redirect } from "@sveltejs/kit";
|
|
9
|
-
|
|
10
|
-
export const prerender = false;
|
|
11
|
-
|
|
12
|
-
/** @type {import('./$types').PageServerLoad} */
|
|
13
|
-
export function load({ url }) {
|
|
14
|
-
const utmSource = url.searchParams.get("utm_source");
|
|
15
|
-
|
|
16
|
-
// Set target to your actual privacy rights form path
|
|
17
|
-
const target =
|
|
18
|
-
"https://cloud.neteng.pro/index.php/apps/forms/s/6HpZKZCaLwb6TXYL99nLQM8t";
|
|
19
|
-
|
|
20
|
-
// Preserve the tracking parameter
|
|
21
|
-
const redirectTo = utmSource ? `${target}?utm_source=${utmSource}` : target;
|
|
22
|
-
|
|
23
|
-
throw redirect(302, redirectTo);
|
|
24
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|