@networkpro/web 1.5.0 β†’ 1.5.2

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 CHANGED
@@ -1,116 +1,208 @@
1
- <!-- =========================================================================
2
- README.md
1
+ # 🌐 Network Pro β€” Web Presence
3
2
 
4
- SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
- This file is part of Network Pro.
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
+ &nbsp;
10
7
 
11
- ---
8
+ [![Netlify Status](https://api.netlify.com/api/v1/badges/93910633-3fdb-4bb3-a9bf-5d91ccfeebf9/deploy-status)](https://app.netlify.com/projects/networkpro-web/deploys) [![NPM Version](https://img.shields.io/npm/v/%40networkpro%2Fweb?registry_uri=https%3A%2F%2Fregistry.npmjs.com&style=flat&logo=npm&logoSize=auto&color=%23CB3837)](https://www.npmjs.com/package/@networkpro/web) [![Build and Publish to Registries](https://github.com/netwk-pro/netwk-pro.github.io/actions/workflows/build-and-publish.yml/badge.svg)](https://github.com/netwk-pro/netwk-pro.github.io/actions/workflows/build-and-publish.yml)
9
+ [![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat)](https://github.com/prettier/prettier) [![stylelint](https://img.shields.io/badge/stylelint-%23747474?style=flat&logo=stylelint&logoSize=auto&labelColor=%23263238)](https://stylelint.io/)
10
+ [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/netwk-pro/netwk-pro.github.io/blob/master/CODE_OF_CONDUCT.md)
12
11
 
13
- I. Creative Commons Attribution 4.0 International
12
+ ## πŸš€ Project Overview
14
13
 
15
- Network Pro (the "Licensed Material") is licensed under Creative Commons Attribution 4.0 International ("CC BY 4.0"). To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/.
14
+ 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.
16
15
 
17
- Per the terms of the License, you are free to distribute, remix, adapt, and build upon the Licensed Material for any purpose, even commercially. You must give appropriate credit, provide a link to the License, and indicate if changes were made.
16
+ Built with [SvelteKit](https://kit.svelte.dev/) and deployed via [Netlify](https://www.netlify.com/).
17
+ Blog and documentation subsites built with [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) and deployed via [GitHub Pages](https://pages.github.com/).
18
+ All infrastructure and data flows are designed with **maximum transparency, self-hosting, and user privacy** in mind.
18
19
 
19
- The Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable.
20
+ ### πŸ“ Repository Structure
20
21
 
21
- Permissions beyond the scope of this Licenseβ€”or instead of those permitted by this Licenseβ€”may be available as further defined within this document.
22
+ ```bash
23
+ .
24
+ β”œβ”€β”€ src/
25
+ β”‚ β”œβ”€β”€ lib/ # Reusable components, styles, utilities
26
+ β”‚ β”œβ”€β”€ routes/ # SvelteKit routes (+page.svelte, +page.server.js)
27
+ β”‚ β”œβ”€β”€ hooks.client.ts # Client-only lifecycle hooks (e.g., SW control)
28
+ β”‚ └── app.html # SvelteKit entry HTML with CSP/meta/bootstrap
29
+ β”œβ”€β”€ tests/ # Vitest test suites
30
+ β”œβ”€β”€ public/ # Static assets served at root
31
+ β”œβ”€β”€ netlify.toml # Netlify configuration
32
+ β”œβ”€β”€ .github/ # CI workflows and automation
33
+ └── ...
34
+ ```
22
35
 
23
- SPDX Reference: https://spdx.org/licenses/CC-BY-4.0.html
24
- Canonical URL: https://creativecommons.org/licenses/by/4.0/
36
+ &nbsp;
25
37
 
26
- ---
38
+ ## πŸ›  Getting Started
39
+
40
+ ### πŸ“¦ Environment Setup
41
+
42
+ ```bash
43
+ git clone https://github.com/netwk-pro/netwk-pro.github.io.git
44
+ cd netwk-pro.github.io
45
+ cp .env.template .env
46
+ npm install
47
+ ```
27
48
 
28
- II. GNU General Public License
49
+ Edit .env to configure your environment mode:
29
50
 
30
- Network Pro is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License ("GNU GPL") as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
51
+ ```env
52
+ ENV_MODE=dev # Options: dev, test, ci, preview, prod
53
+ ```
31
54
 
32
- This material is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
33
- FITNESS FOR A PARTICULAR PURPOSE.
55
+ > ENV*MODE is used for tooling and workflows β€” not by SvelteKit itself.
56
+ > Use VITE*-prefixed env variables for runtime values.
57
+
58
+ &nbsp;
34
59
 
35
- See the GNU General Public License for more details.
60
+ ### πŸ“ Node.js Version Management
36
61
 
37
- SPDX Reference: https://spdx.org/licenses/GPL-3.0-or-later.html
38
- Canonical URL: https://www.gnu.org/licenses/gpl-3.0.html
62
+ This repo uses .nvmrc and .node-version for version consistency with tools like:
63
+
64
+ - nvm
65
+ - asdf
66
+ - Volta
67
+ - GitHub Actions
68
+
69
+ ```bash
70
+ node -v # Should match "engines" in package.json
71
+ npm -v
72
+ ```
73
+
74
+ &nbsp;
39
75
 
40
76
  ---
41
77
 
42
- Author: Scott Lopez
43
- Email: <contact@neteng.pro>
44
- Web: <https://bio.neteng.pro>
45
- -->
78
+ ## πŸ“œ Available Scripts
79
+
80
+ The following CLI commands are available via `npm run <script>` or `pnpm run <script>`.
46
81
 
47
- <section id="top">
82
+ ### πŸ”„ Development
48
83
 
49
- <sup>[SPDX-License-Identifier](https://spdx.dev/learn/handling-license-info/): `CC-BY-4.0 OR GPL-3.0-or-later`</sup>
84
+ | Script | Description |
85
+ | --------------- | ---------------------------------- |
86
+ | `dev` | Start development server with Vite |
87
+ | `preview` | Preview production build locally |
88
+ | `build` | Build the project with Vite |
89
+ | `build:netlify` | Build using Netlify CLI |
90
+ | `css:bundle` | Bundle and minify CSS |
50
91
 
51
- # Network Pro Strategies
92
+ ---
52
93
 
53
- </section>
94
+ ### βœ… Pre-check / Sync
54
95
 
55
- [![Netlify Status](https://api.netlify.com/api/v1/badges/93910633-3fdb-4bb3-a9bf-5d91ccfeebf9/deploy-status)](https://app.netlify.com/projects/networkpro-web/deploys) [![NPM Version](https://img.shields.io/npm/v/%40networkpro%2Fweb?registry_uri=https%3A%2F%2Fregistry.npmjs.com&style=flat&logo=npm&logoSize=auto&color=%23CB3837)](https://www.npmjs.com/package/@networkpro/web) [![Build and Publish to Registries](https://github.com/netwk-pro/netwk-pro.github.io/actions/workflows/build-and-publish.yml/badge.svg)](https://github.com/netwk-pro/netwk-pro.github.io/actions/workflows/build-and-publish.yml)
56
- [![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat)](https://github.com/prettier/prettier) [![stylelint](https://img.shields.io/badge/stylelint-%23747474?style=flat&logo=stylelint&logoSize=auto&labelColor=%23263238)](https://stylelint.io/)
57
- [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/netwk-pro/netwk-pro.github.io/blob/master/CODE_OF_CONDUCT.md)
96
+ | Script | Description |
97
+ | ------------- | ------------------------------------------------------------ |
98
+ | `prepare` | Run SvelteKit sync |
99
+ | `check` | Run SvelteKit sync and type check with `svelte-check` |
100
+ | `check:watch` | Watch mode for type checks |
101
+ | `check:node` | Validate Node & NPM versions match package.json `engines` |
102
+ | `checkout` | Full local validation: check versions, test, lint, typecheck |
103
+ | `verify` | Alias for `checkout` |
104
+
105
+ ---
58
106
 
59
- ## Security That Respects You
107
+ ### 🧹 Cleanup & Maintenance
60
108
 
61
- At **Network Pro Strategies (Network Pro&trade;)**, we deliver network security, cybersecurity, and digital privacy consulting with clarity, credibility, and care. We believe that real security doesn’t have to come at the cost of user autonomy, and that privacy-minded solutions can be both practical and powerful.
109
+ | Script | Description |
110
+ | --------- | ----------------------------------------------- |
111
+ | `delete` | Remove build artifacts and `node_modules` |
112
+ | `clean` | Fully reset environment and reinstall |
113
+ | `upgrade` | Update all dependencies via `npm-check-updates` |
62
114
 
63
- Our approach is built on a simple principle: **the best security is the one that fits.** That means we don’t push ideologiesβ€”we apply what works. We advocate for and implement **free and open source solutions** where they offer competitive functionality, control, and visibility. When proprietary platforms are better suited, we deploy them responsibly and securelyβ€”ensuring every recommendation is grounded in **technical merit**, **scalability**, and **client goals**.
115
+ ---
64
116
 
65
- ### **What We Do**
117
+ ### πŸ§ͺ Testing
66
118
 
67
- As a remote-first consultancy, we support clients across industries and geographies with a focus on:
119
+ | Script | Description |
120
+ | --------------- | -------------------------------------------- |
121
+ | `test` | Alias for `test:all` |
122
+ | `test:all` | Run both client and server test suites |
123
+ | `test:client` | Run client tests with Vitest |
124
+ | `test:server` | Run server-side tests with Vitest |
125
+ | `test:watch` | Watch mode for client tests |
126
+ | `test:coverage` | Collect coverage from both client and server |
68
127
 
69
- - **Network Hardening & Perimeter Defense**
70
- - **Firewall Architecture & Policy Optimization**
71
- - **Cloud Security & Zero Trust Implementation**
72
- - **Secure Infrastructure Design & Implementation**
73
- - **Risk Reduction & Security Posture Assessment**
128
+ ---
74
129
 
75
- Our consulting engagements range from tactical one-off solutions to strategic, long-term partnerships. Whether it’s helping a business segment its internal network, lock down its cloud footprint, or plan scalable defensesβ€”we deliver clear value, with zero fluff.
130
+ ### 🧼 Linting & Formatting
76
131
 
77
- We also believe education is a core pillar of real-world security. That’s why we invest in raising awarenessβ€”across both technical and general audiencesβ€”on best practices in digital privacy, secure design, and threat mitigation.
132
+ | Script | Description |
133
+ | ------------ | --------------------------------------- |
134
+ | `lint` | Run ESLint on JS, MJS, and Svelte files |
135
+ | `lint:fix` | Auto-fix ESLint issues |
136
+ | `lint:jsdoc` | Check JSDoc annotations |
137
+ | `lint:css` | Run Stylelint on CSS and Svelte styles |
138
+ | `lint:md` | Lint Markdown content |
139
+ | `lint:all` | Run all linters and formatting checks |
140
+ | `format` | Run Prettier formatting check |
141
+ | `format:fix` | Auto-format code using Prettier |
78
142
 
79
- **Network Pro&trade; exists to bring strong, thoughtful security to organizations that value integrityβ€”without sacrificing agility or trust.** We don’t just secure infrastructure. We secure confidence.
143
+ ---
80
144
 
81
- &nbsp;
145
+ ### πŸ’‘ Lighthouse / Performance
82
146
 
83
- πŸ”Ή [Let’s connect](https://netwk.pro/contact) to discuss how we can help secure and strengthen your business today.
147
+ | Script | Description |
148
+ | ------------------ | ----------------------------------------------- |
149
+ | `lhci` | Alias for Lighthouse CI |
150
+ | `lighthouse` | Run local Lighthouse test and launch viewer |
151
+ | `lighthouse:local` | Build site, preview, and run Lighthouse locally |
152
+ | `lhci:run` | Run Lighthouse CI autorun |
84
153
 
85
154
  ---
86
155
 
87
- You can find our PGP keys and a vCard containing our contact information for your convenience below.
156
+ ### πŸ“‹ Audits / Validation
88
157
 
89
- | <img decoding="sync" loading="eager" src="https://netwk.pro/img/qr/pgp-support.png" width="125px" height="125px" alt="PGP Key - support@neteng.pro"> | **[support@neteng.pro](https://keys.openpgp.org/search?q=support%40neteng.pro)**<br />**PGP Key (ed25519)**<br />&nbsp;<br /><a href="https://raw.githubusercontent.com/netwk-pro/netwk-pro.github.io/refs/heads/master/assets/bin/support@neteng.pro.aexpk" download type="application/pgp-keys">**aexpk**</a>&nbsp; **&#47;** &nbsp;<a href="https://raw.githubusercontent.com/netwk-pro/netwk-pro.github.io/refs/heads/master/assets/bin/support@neteng.pro.asc" download type="application/pgp-keys">**asc**</a><br />&nbsp;<br />6590b992e2e3eff12738<br />7bce2af093e9dec61ba0 |
90
- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
91
- | **[contact@s.neteng.pro](https://keys.openpgp.org/search?q=contact%40s.neteng.pro)**<br />**PGP Key (ed25519)**<br />&nbsp;<br /><a href="https://raw.githubusercontent.com/netwk-pro/netwk-pro.github.io/refs/heads/master/assets/bin/contact@s.neteng.pro.aexpk" download type="application/pgp-keys">**aexpk**</a>&nbsp; **&#47;** &nbsp;<a href="https://raw.githubusercontent.com/netwk-pro/netwk-pro.github.io/refs/heads/master/assets/bin/contact@s.neteng.pro.asc" download type="application/pgp-keys">**asc**</a><br />&nbsp;<br />**df118baa6c2d9dcdebdc**<br />**2ddcf99373499495f957** | <img decoding="async" loading="lazy" src="https://netwk.pro/img/qr/pgp-contact.png" width="125px" height="125px" alt="PGP Key - contact@s.neteng.pro"> |
92
- | <img decoding="async" loading="lazy" src="https://netwk.pro/img/qr/vcard.png" width="125px" height="125px" alt="vCard"> | **vCard**<br />&nbsp;<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> |
158
+ | Script | Description |
159
+ | --------------- | -------------------------------------------- |
160
+ | `audit:scripts` | Check for untested utility scripts |
161
+ | `head:flatten` | Flatten headers for Netlify |
162
+ | `head:validate` | Validate headers file against project config |
93
163
 
94
- <sub>[Back to top](#top)</sub>
164
+ ---
165
+
166
+ ### πŸ”„ Lifecycle Hooks
167
+
168
+ | Script | Description |
169
+ | ------------- | ----------------------------------- |
170
+ | `postinstall` | Ensures version check after install |
171
+
172
+ &nbsp;
95
173
 
96
174
  ---
97
175
 
98
- <div style="font-size: 12px; font-weight: bold; text-align: center;">
176
+ ## 🧾 License
99
177
 
100
- [Home](https://netwk.pro) &nbsp; | &nbsp; [Terms of Use](https://netwk.pro/terms-of-use)
101
- [Privacy Policy](https://netwk.pro/privacy-policy) &nbsp; | &nbsp; [Legal](https://netwk.pro/license)
178
+ This project is licensed under:
102
179
 
103
- </div>
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
+ &nbsp;
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
  &nbsp;
106
193
 
107
- <span style="font-size: 12px; text-align: center;">
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 &copy; 2025
110
- **[Network Pro Strategies](https://netwk.pro/)** (Network Pro&trade;)
202
+ **[Network Pro Strategies](https://netwk.pro) (Network Pro&trade;)**
111
203
 
112
204
  Network Pro&trade;, the shield logo, and the "Locking Down Networks&trade;" 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
- </span>
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.0",
5
+ "version": "1.5.2",
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
- "android",
15
- "linux"
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.31.1"
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.8.2",
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 = "./static/_headers"; // update if needed
20
- const OUTPUT_PATH = "./static/_headers.flattened";
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 = [];
@@ -17,7 +17,7 @@ This file is part of Network Pro.
17
17
  // validate-headers.js
18
18
  import fs from "fs";
19
19
 
20
- const file = "./static/_headers";
20
+ const file = "./_headers";
21
21
  const lines = fs.readFileSync(file, "utf-8").split("\n");
22
22
 
23
23
  let currentPath = null;
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
- <!-- Optional CSP for Dev -->
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;" />
@@ -65,16 +65,6 @@
65
65
  if (location.search.includes("nosw")) {
66
66
  window.__DISABLE_SW__ = true;
67
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
68
  }
79
69
  </script>
80
70
 
@@ -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
- "/contact": {
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 8, 2025",
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), communications on
145
- Discord and/or Slack, Stack Overflow Teams, Nextcloud, and our social
146
- media presence (e.g., Facebook, Instagram, X, and similar platforms).
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,50 +10,60 @@ 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;
13
+ const disableSW =
14
+ window?.__DISABLE_SW__ || location.search.includes("nosw");
14
15
 
15
16
  if (disableSW) {
16
17
  console.warn("⚠️ Service Worker registration disabled via diagnostic mode.");
18
+
19
+ if ('serviceWorker' in navigator) {
20
+ navigator.serviceWorker.getRegistrations().then((registrations) => {
21
+ for (const registration of registrations) {
22
+ registration.unregister().then((success) => {
23
+ console.log("🧹 SW unregistered from registerServiceWorker.js:", success);
24
+ });
25
+ }
26
+ });
27
+ }
28
+
17
29
  return;
18
30
  }
19
31
 
20
32
  if ('serviceWorker' in navigator) {
21
- // Skip registration in Firefox during development
33
+ // Optional: skip for Firefox in dev
22
34
  const isFirefox = navigator.userAgent.includes('Firefox');
23
35
  const isDevelopment =
24
- window.location.hostname === 'localhost' ||
25
- window.location.hostname === '127.0.0.1';
36
+ location.hostname === 'localhost' || location.hostname === '127.0.0.1';
26
37
 
27
38
  if (isFirefox && isDevelopment) {
28
- console.log('Service Worker registration skipped in Firefox development mode');
39
+ console.log('πŸ›‘ SW registration skipped in Firefox development mode');
29
40
  return;
30
41
  }
31
42
 
32
- // Wait until after full page load for performance optimization
33
43
  window.addEventListener('load', () => {
34
44
  navigator.serviceWorker
35
45
  .register('service-worker.js')
36
46
  .then((registration) => {
37
- console.log('Service Worker registered with scope:', registration.scope);
47
+ console.log('βœ… Service Worker registered with scope:', registration.scope);
38
48
 
39
- // Track installation of a new service worker
40
49
  registration.addEventListener('updatefound', () => {
41
50
  const newWorker = registration.installing;
42
- console.log('New service worker installing...');
51
+ console.log('[SW-CLIENT] New service worker installing...');
43
52
 
44
53
  if (!newWorker) return;
45
54
 
46
55
  let updatePrompted = false;
47
56
 
48
57
  newWorker.addEventListener('statechange', () => {
58
+ console.log('[SW-CLIENT] New worker state:', newWorker.state);
59
+
49
60
  if (
50
61
  newWorker.state === 'installed' &&
51
62
  navigator.serviceWorker.controller &&
52
63
  !updatePrompted
53
64
  ) {
54
65
  updatePrompted = true;
55
-
56
- // Custom prompt: ask user to reload for latest content
66
+ console.log('[SW-CLIENT] New SW installed. Prompting user to reload.');
57
67
  if (confirm('New content is available. Reload to update?')) {
58
68
  window.location.reload();
59
69
  }
@@ -62,19 +72,18 @@ export function registerServiceWorker() {
62
72
  });
63
73
  })
64
74
  .catch((error) => {
65
- console.error('Service Worker registration failed:', error);
75
+ console.error('❌ Service Worker registration failed:', error);
66
76
  });
67
77
 
68
- // Ensure the page reloads automatically when the new service worker takes control
69
78
  let refreshing = false;
70
79
  navigator.serviceWorker.addEventListener('controllerchange', () => {
80
+ console.log('[SW-CLIENT] Controller changed.');
71
81
  if (!refreshing) {
72
82
  refreshing = true;
73
83
  window.location.reload();
74
84
  }
75
85
  });
76
86
 
77
- // Optional PWA install prompt logic
78
87
  window.addEventListener('beforeinstallprompt', (e) => {
79
88
  e.preventDefault();
80
89
  window.dispatchEvent(new CustomEvent('pwa-install-available', {
@@ -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,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
+ }
@@ -21,9 +21,6 @@ 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
-
27
24
  export const prerender = "auto";
28
25
  export const trailingSlash = "never";
29
26
 
@@ -34,11 +31,6 @@ export const trailingSlash = "never";
34
31
  export function load({ url }) {
35
32
  const normalizedPathname = url.pathname.replace(/\/+$/, "") || "/";
36
33
 
37
- if (browser) {
38
- // Move service worker registration here
39
- registerServiceWorker();
40
- }
41
-
42
34
  return {
43
35
  pathname: normalizedPathname,
44
36
  meta: fallbackMeta, // Required to ensure meta always exists for typing
@@ -13,6 +13,8 @@ This file is part of Network Pro.
13
13
  import HeaderDefault from "$lib/components/layout/HeaderDefault.svelte";
14
14
  import HeaderHome from "$lib/components/layout/HeaderHome.svelte";
15
15
  import PWAInstallButton from "$lib/components/PWAInstallButton.svelte";
16
+ import { onMount } from "svelte";
17
+ import { registerServiceWorker } from "$lib/registerServiceWorker.js";
16
18
  import { browser } from "$app/environment";
17
19
  import "$lib/styles";
18
20
 
@@ -22,6 +24,10 @@ This file is part of Network Pro.
22
24
  import faviconSvg from "$lib/img/favicon.svg";
23
25
  import appleTouchIcon from "$lib/img/icon-180x180.png";
24
26
 
27
+ onMount(() => {
28
+ registerServiceWorker();
29
+ });
30
+
25
31
  if (browser) {
26
32
  // Preload logo images
27
33
  [logoPng, logoWbp].forEach((src) => {
@@ -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}
@@ -18,28 +18,28 @@ const CACHE = `cache-${version}`;
18
18
  /** @type {string[]} */
19
19
  const excludedAssets = [];
20
20
 
21
+ const IGNORE_PATHS = new Set([
22
+ "/img/banner-1280x640.png",
23
+ "/img/banner-og-1200x630.png",
24
+ "/img/logo-transparent.png",
25
+ "/img/logo.png",
26
+ "/img/svelte.png",
27
+ "/webfonts/fa-brands-400.ttf",
28
+ "/webfonts/fa-solid-900.ttf",
29
+ "/robots.txt",
30
+ "/screenshots/desktop-foss.png",
31
+ "/sitemap.xml",
32
+ "/CNAME",
33
+ ]);
34
+
21
35
  /** @type {string[]} */
22
- const ASSETS = Array.from(
23
- new Set(
36
+ const ASSETS = [
37
+ ...new Set(
24
38
  [...build, ...files, "/offline.html"].filter((path) => {
25
39
  try {
26
40
  const url = new URL(path, location.origin);
27
41
  const hostname = url.hostname;
28
42
 
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
-
43
43
  const shouldExclude =
44
44
  path.startsWith("http") ||
45
45
  disallowedHosts.some(
@@ -56,23 +56,24 @@ const ASSETS = Array.from(
56
56
  }
57
57
  }),
58
58
  ),
59
- );
59
+ ];
60
60
 
61
61
  const uniqueExcludedAssets = [...new Set(excludedAssets)].sort();
62
62
 
63
63
  console.log("[SW] Assets to precache:", ASSETS);
64
64
  console.log("[SW] Excluded assets:", uniqueExcludedAssets);
65
65
 
66
- /**
67
- * @param {ExtendableEvent} event
68
- */
66
+ // πŸ”Ή Install event
69
67
  sw.addEventListener("install", (event) => {
68
+ console.log("[SW] Install event");
70
69
  event.waitUntil(
71
70
  (async () => {
72
71
  const cache = await caches.open(CACHE);
73
72
  try {
74
73
  await cache.addAll(ASSETS);
74
+ console.log("[SW] Precaching complete");
75
75
  sw.skipWaiting();
76
+ console.log("[SW] skipWaiting() called");
76
77
  } catch (err) {
77
78
  console.warn("[SW] Failed to precache some assets:", err);
78
79
  }
@@ -80,23 +81,26 @@ sw.addEventListener("install", (event) => {
80
81
  );
81
82
  });
82
83
 
83
- /**
84
- * @param {ExtendableEvent} event
85
- */
84
+ // πŸ”Ή Activate event
86
85
  sw.addEventListener("activate", (event) => {
87
- /** @type {ExtendableEvent} */ (event).waitUntil(
86
+ console.log("[SW] Activate event");
87
+ event.waitUntil(
88
88
  (async () => {
89
89
  const tasks = [];
90
90
 
91
91
  if (sw.registration.navigationPreload) {
92
92
  tasks.push(sw.registration.navigationPreload.enable());
93
+ console.log("[SW] Navigation preload enabled");
93
94
  }
94
95
 
95
96
  tasks.push(
96
97
  caches.keys().then((keys) =>
97
98
  Promise.all(
98
99
  keys.map((key) => {
99
- if (key !== CACHE) return caches.delete(key);
100
+ if (key !== CACHE) {
101
+ console.log("[SW] Deleting old cache:", key);
102
+ return caches.delete(key);
103
+ }
100
104
  }),
101
105
  ),
102
106
  ),
@@ -104,29 +108,43 @@ sw.addEventListener("activate", (event) => {
104
108
 
105
109
  await Promise.all(tasks);
106
110
  await sw.clients.claim();
111
+ console.log("[SW] clients.claim() called");
112
+ console.log("[SW] Scope:", sw.registration.scope);
107
113
  })(),
108
114
  );
109
115
  });
110
116
 
111
- /**
112
- * @param {FetchEvent} event
113
- */
117
+ // πŸ”Ή Fetch event
114
118
  sw.addEventListener("fetch", (event) => {
115
- /** @type {FetchEvent} */ (event).respondWith(
119
+ console.log("[SW] Fetch intercepted:", event.request.url);
120
+ event.respondWith(
116
121
  (async () => {
117
122
  if (new URL(event.request.url).origin === location.origin) {
118
123
  const cached = await caches.match(event.request);
119
- if (cached) return cached;
124
+ if (cached) {
125
+ console.log("[SW] Serving from cache:", event.request.url);
126
+ return cached;
127
+ }
120
128
  }
121
129
 
122
130
  try {
123
131
  if (event.request.mode === "navigate") {
124
132
  const preloadResponse = await event.preloadResponse;
125
- if (preloadResponse) return preloadResponse;
133
+ if (preloadResponse) {
134
+ console.log("[SW] Using preload response for:", event.request.url);
135
+ return preloadResponse;
136
+ }
126
137
  }
127
138
 
139
+ console.log("[SW] Fetching from network:", event.request.url);
128
140
  return await fetch(event.request);
129
- } catch {
141
+ } catch (err) {
142
+ console.warn(
143
+ "[SW] Fetch failed; offline fallback used:",
144
+ event.request.url,
145
+ err,
146
+ );
147
+
130
148
  if (event.request.mode === "navigate") {
131
149
  const offline = await caches.match("/offline.html");
132
150
  if (offline) return offline;
@@ -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
- }