@networkpro/web 1.6.2 → 1.6.5
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 +21 -19
- package/cspell.json +2 -1
- package/package.json +4 -4
- package/scripts/bundleCss.js +2 -2
- package/src/app.html +18 -1
- package/src/hooks.server.js +42 -46
- package/src/lib/components/PostHog.svelte +24 -0
- package/src/lib/img/logo-web.png +0 -0
- package/src/lib/img/logo-web.webp +0 -0
- package/src/lib/pages/AboutContent.svelte +4 -5
- package/src/lib/pages/HomeContent.svelte +1 -1
- package/src/lib/pages/LicenseContent.svelte +2 -3
- package/src/lib/pages/PrivacyContent.svelte +36 -3
- package/src/lib/pages/TermsConditionsContent.svelte +1 -1
- package/src/lib/pages/TermsUseContent.svelte +2 -3
- package/src/lib/styles/css/default.css +9 -1
- package/src/lib/styles/css/normalize.css +2 -2
- package/src/lib/styles/css/style.css +2 -2
- package/src/lib/styles/global.min.css +3 -5
- package/src/lib/utils/privacy.js +23 -0
- package/src/routes/+layout.js +2 -0
- package/src/routes/+layout.svelte +34 -10
- package/src/service-worker.js +5 -2
- package/tests/e2e/app.spec.js +24 -0
- package/tests/e2e/mobile.spec.js +14 -0
package/README.md
CHANGED
|
@@ -30,23 +30,23 @@ All infrastructure and data flows are designed with **maximum transparency, self
|
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
.
|
|
33
|
-
├── .github/workflows # CI workflows and automation
|
|
34
|
-
├── .vscode/
|
|
33
|
+
├── .github/workflows/ # CI workflows and automation
|
|
34
|
+
├── .vscode/ # Recommended VS Code settings, extensions
|
|
35
35
|
├── netlify-functions/
|
|
36
|
-
│ └── cspReport.js
|
|
37
|
-
├── scripts/
|
|
36
|
+
│ └── cspReport.js # Serverless function to receive and log CSP violation reports
|
|
37
|
+
├── scripts/ # Utility scripts
|
|
38
38
|
├── src/
|
|
39
|
-
│ ├── lib/
|
|
40
|
-
│ ├── routes/
|
|
41
|
-
│ ├── hooks.client.ts
|
|
42
|
-
│ ├── hooks.server.js
|
|
43
|
-
│ ├── app.html
|
|
44
|
-
│ └── service-worker.js
|
|
45
|
-
├── static/
|
|
39
|
+
│ ├── lib/ # Reusable components, styles, utilities
|
|
40
|
+
│ ├── routes/ # SvelteKit routes (+page.svelte, +page.server.js)
|
|
41
|
+
│ ├── hooks.client.ts # Handles PWA install prompt and logs client errors
|
|
42
|
+
│ ├── hooks.server.js # Injects CSP headers and permissions policy
|
|
43
|
+
│ ├── app.html # SvelteKit entry HTML with CSP/meta/bootentry
|
|
44
|
+
│ └── service-worker.js # Custom Service Worker
|
|
45
|
+
├── static/ # Static assets served at root
|
|
46
46
|
├── tests/
|
|
47
|
-
│ ├── e2e/
|
|
48
|
-
│ └── unit/
|
|
49
|
-
├── netlify.toml
|
|
47
|
+
│ ├── e2e/ # End-to-end Playwright tests
|
|
48
|
+
│ └── unit/ # Vite unit tests
|
|
49
|
+
├── netlify.toml # Netlify configuration
|
|
50
50
|
└── ...
|
|
51
51
|
```
|
|
52
52
|
|
|
@@ -169,18 +169,18 @@ This project includes custom runtime configuration files for enhancing security,
|
|
|
169
169
|
|
|
170
170
|
### 🔐 `hooks.server.js`
|
|
171
171
|
|
|
172
|
-
Located at src/hooks.server.js
|
|
172
|
+
Located at `src/hooks.server.js`, this file is responsible for injecting dynamic security headers. It includes:
|
|
173
173
|
|
|
174
174
|
- Content Security Policy (CSP) with support for relaxed directives (inline scripts allowed)
|
|
175
175
|
- Permissions Policy to explicitly disable unnecessary browser APIs
|
|
176
176
|
- X-Content-Type-Options, X-Frame-Options, and Referrer-Policy headers
|
|
177
177
|
|
|
178
|
-
> 💡 The CSP nonce feature has been disabled. Inline scripts are now allowed through the policy using the "script-src 'self' 'unsafe-inline'" directive. If you wish to use nonces in the future, you can re-enable them by uncommenting the relevant sections in hooks.server.js and modifying your inline
|
|
178
|
+
> 💡 The CSP nonce feature has been disabled. Inline scripts are now allowed through the policy using the `"script-src 'self' 'unsafe-inline'"` directive. If you wish to use nonces in the future, you can re-enable them by uncommenting the relevant sections in `hooks.server.js` and modifying your inline `<script>` tags.
|
|
179
179
|
|
|
180
180
|
To re-enable nonce generation for inline scripts in the future:
|
|
181
181
|
|
|
182
|
-
1. Uncomment the nonce generation and injection logic in hooks.server.js
|
|
183
|
-
2. Add nonce="**cspNonce**" to inline
|
|
182
|
+
1. Uncomment the nonce generation and injection logic in `hooks.server.js`.
|
|
183
|
+
2. Add `nonce="**cspNonce**"` to inline `<script>` blocks in `app.html` or route templates.
|
|
184
184
|
|
|
185
185
|
> 💡 The `[headers]` block in `netlify.toml` has been deprecated — all headers are now set dynamically from within SvelteKit.
|
|
186
186
|
|
|
@@ -246,7 +246,7 @@ This project uses a mix of automated performance, accessibility, and end-to-end
|
|
|
246
246
|
|
|
247
247
|
### Running Tests
|
|
248
248
|
|
|
249
|
-
Local testing via Playwright:
|
|
249
|
+
Local testing via Vitest and Playwright:
|
|
250
250
|
|
|
251
251
|
```bash
|
|
252
252
|
npm run test:client # Run client-side unit tests with Vitest
|
|
@@ -254,6 +254,7 @@ npm run test:server # Run server-side unit tests with Vitest
|
|
|
254
254
|
npm run test:all # Run full test suite
|
|
255
255
|
npm run test:watch # Watch mode for client tests
|
|
256
256
|
npm run test:coverage # Collect code coverage reports
|
|
257
|
+
npm run test:e2e # Runs Playwright E2E tests
|
|
257
258
|
```
|
|
258
259
|
|
|
259
260
|
Audit your app using Lighthouse:
|
|
@@ -406,6 +407,7 @@ The following CLI commands are available via `npm run <script>` or `pnpm run <sc
|
|
|
406
407
|
| `test:server` | Run server-side tests with Vitest |
|
|
407
408
|
| `test:watch` | Watch mode for client tests |
|
|
408
409
|
| `test:coverage` | Collect coverage from both client and server |
|
|
410
|
+
| `test:e2e` | Run Playwright E2E tests |
|
|
409
411
|
|
|
410
412
|
---
|
|
411
413
|
|
package/cspell.json
CHANGED
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sideEffects": [
|
|
5
5
|
"./.netlify/shims.js"
|
|
6
6
|
],
|
|
7
|
-
"version": "1.6.
|
|
7
|
+
"version": "1.6.5",
|
|
8
8
|
"description": "Locking Down Networks, Unlocking Confidence | Security, Networking, Privacy — Network Pro Strategies",
|
|
9
9
|
"keywords": [
|
|
10
10
|
"advisory",
|
|
@@ -36,7 +36,6 @@
|
|
|
36
36
|
"node": ">=22.0.0 <25",
|
|
37
37
|
"npm": ">=11.0.0 <12"
|
|
38
38
|
},
|
|
39
|
-
"style": "src/lib/styles/index.js",
|
|
40
39
|
"scripts": {
|
|
41
40
|
"dev": "vite dev",
|
|
42
41
|
"start": "npm run dev",
|
|
@@ -61,6 +60,7 @@
|
|
|
61
60
|
"test:server": "vitest --config vitest.config.server.js",
|
|
62
61
|
"test:watch": "vitest --config vitest.config.client.js --watch",
|
|
63
62
|
"test:coverage": "npm run test:client -- --run --coverage && npm run test:server -- --run --coverage",
|
|
63
|
+
"test:e2e": "npx playwright test",
|
|
64
64
|
"lint": "eslint . --ext .mjs,.js,.svelte",
|
|
65
65
|
"lint:fix": "eslint . --ext .mjs,.js,.svelte --fix",
|
|
66
66
|
"lint:jsdoc": "eslint . --ext .js,.mjs,.svelte --max-warnings=0",
|
|
@@ -78,8 +78,9 @@
|
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"nodemailer": "^7.0.3",
|
|
81
|
+
"posthog-js": "^1.246.0",
|
|
81
82
|
"semver": "^7.7.2",
|
|
82
|
-
"svelte": "5.33.
|
|
83
|
+
"svelte": "5.33.4"
|
|
83
84
|
},
|
|
84
85
|
"devDependencies": {
|
|
85
86
|
"@eslint/compat": "^1.2.9",
|
|
@@ -104,7 +105,6 @@
|
|
|
104
105
|
"markdownlint": "^0.38.0",
|
|
105
106
|
"markdownlint-cli2": "^0.18.1",
|
|
106
107
|
"mdsvex": "^0.12.6",
|
|
107
|
-
"normalize.css": "^8.0.1",
|
|
108
108
|
"playwright": "^1.52.0",
|
|
109
109
|
"postcss": "^8.5.3",
|
|
110
110
|
"prettier": "^3.5.3",
|
package/scripts/bundleCss.js
CHANGED
|
@@ -20,10 +20,10 @@ import { bundle } from "lightningcss";
|
|
|
20
20
|
import path from "path";
|
|
21
21
|
|
|
22
22
|
// Define the path to your input CSS file
|
|
23
|
-
const inputFilePath = path.resolve("src/lib/styles/css/
|
|
23
|
+
const inputFilePath = path.resolve("src/lib/styles/css/global.css");
|
|
24
24
|
|
|
25
25
|
// Define the path for the output CSS file
|
|
26
|
-
const outputFilePath = path.resolve("
|
|
26
|
+
const outputFilePath = path.resolve("src/lib/styles/css/global.min.css");
|
|
27
27
|
|
|
28
28
|
// Bundle and minify the CSS
|
|
29
29
|
const { code, map } = bundle({
|
package/src/app.html
CHANGED
|
@@ -24,7 +24,24 @@
|
|
|
24
24
|
sizes="any"
|
|
25
25
|
type="image/x-icon" />
|
|
26
26
|
|
|
27
|
-
<!--
|
|
27
|
+
<!-- Preconnect to PostHog domains -->
|
|
28
|
+
<link
|
|
29
|
+
rel="preconnect"
|
|
30
|
+
href="https://us.i.posthog.com"
|
|
31
|
+
crossorigin="anonymous" />
|
|
32
|
+
<link
|
|
33
|
+
rel="preconnect"
|
|
34
|
+
href="https://us-assets.i.posthog.com"
|
|
35
|
+
crossorigin="anonymous" />
|
|
36
|
+
|
|
37
|
+
<!-- Preload the PostHog script -->
|
|
38
|
+
<link
|
|
39
|
+
rel="preload"
|
|
40
|
+
href="https://us-assets.i.posthog.com/array/phc_Qshfo6AXzh4pS7aPigfqyeo4qj1qlyh7gDuHDeVMSR0/config.js"
|
|
41
|
+
as="script" />
|
|
42
|
+
<script
|
|
43
|
+
src="https://us-assets.i.posthog.com/array/phc_Qshfo6AXzh4pS7aPigfqyeo4qj1qlyh7gDuHDeVMSR0/config.js"
|
|
44
|
+
defer></script>
|
|
28
45
|
|
|
29
46
|
<!-- Preload FontAwesome webfonts -->
|
|
30
47
|
<link
|
package/src/hooks.server.js
CHANGED
|
@@ -14,53 +14,52 @@ export async function handle({ event, resolve }) {
|
|
|
14
14
|
// Create the response
|
|
15
15
|
const response = await resolve(event);
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// Check if the environment is for testing
|
|
17
|
+
// Determine environment flags
|
|
18
|
+
// Default to development policy if neither test nor prod
|
|
20
19
|
const isTestEnvironment =
|
|
21
20
|
process.env.NODE_ENV === "test" || process.env.ENV_MODE === "ci";
|
|
21
|
+
const isProdEnvironment =
|
|
22
|
+
process.env.NODE_ENV === "production" || process.env.ENV_MODE === "prod";
|
|
23
|
+
|
|
24
|
+
if (!isProdEnvironment) {
|
|
25
|
+
console.log("ENV_MODE:", process.env.ENV_MODE);
|
|
26
|
+
}
|
|
22
27
|
|
|
23
|
-
//
|
|
28
|
+
// Determine report URI
|
|
29
|
+
const reportUri = isProdEnvironment
|
|
30
|
+
? "/.netlify/functions/cspReport"
|
|
31
|
+
: "/api/mock-csp";
|
|
32
|
+
|
|
33
|
+
// Construct base policy
|
|
34
|
+
const cspDirectives = [
|
|
35
|
+
"default-src 'self';",
|
|
36
|
+
"script-src 'self' 'unsafe-inline' https://us.i.posthog.com https://us-assets.i.posthog.com;",
|
|
37
|
+
"script-src-elem 'self' 'unsafe-inline' https://us.i.posthog.com https://us-assets.i.posthog.com;",
|
|
38
|
+
"style-src 'self' 'unsafe-inline';",
|
|
39
|
+
"img-src 'self' data:;",
|
|
40
|
+
"connect-src 'self' https://us.i.posthog.com https://us-assets.i.posthog.com;",
|
|
41
|
+
"font-src 'self' data:;",
|
|
42
|
+
"form-action 'self';",
|
|
43
|
+
"base-uri 'self';",
|
|
44
|
+
"object-src 'none';",
|
|
45
|
+
"frame-ancestors 'none';",
|
|
46
|
+
"upgrade-insecure-requests;",
|
|
47
|
+
`report-uri ${reportUri};`,
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// Loosen up CSP for test environments
|
|
24
51
|
if (isTestEnvironment) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"img-src 'self' data:;", // Allow images from same origin and data URIs
|
|
33
|
-
"connect-src 'self';", // Allow connections only to same origin
|
|
34
|
-
"font-src 'self' data:;", // Allow fonts from same origin and data URIs
|
|
35
|
-
"form-action 'self';", // Allow forms to post to same origin
|
|
36
|
-
"base-uri 'self';", // Restrict base URIs to same origin
|
|
37
|
-
"object-src 'none';", // Block all object sources
|
|
38
|
-
"frame-ancestors 'none';", // Prevent framing of the site
|
|
39
|
-
"upgrade-insecure-requests;", // Automatically upgrade HTTP to HTTPS
|
|
40
|
-
"report-uri /api/mock-csp;", // Mock CSP reports for testing
|
|
41
|
-
].join(" "),
|
|
42
|
-
);
|
|
43
|
-
} else {
|
|
44
|
-
// Production or development environment: use a more restrictive CSP
|
|
45
|
-
response.headers.set(
|
|
46
|
-
"Content-Security-Policy",
|
|
47
|
-
[
|
|
48
|
-
"default-src 'self';", // Allow resources from same origin
|
|
49
|
-
"script-src 'self' 'unsafe-inline';", // Allow inline scripts
|
|
50
|
-
"style-src 'self' 'unsafe-inline';", // Allow inline styles
|
|
51
|
-
"img-src 'self' data:;", // Allow images from same origin and data URIs
|
|
52
|
-
"connect-src 'self';", // Allow connections only to same origin
|
|
53
|
-
"font-src 'self' data:;", // Allow fonts from same origin and data URIs
|
|
54
|
-
"form-action 'self';", // Allow forms to post to same origin
|
|
55
|
-
"base-uri 'self';", // Restrict base URIs to same origin
|
|
56
|
-
"object-src 'none';", // Block all object sources
|
|
57
|
-
"frame-ancestors 'none';", // Prevent framing of the site
|
|
58
|
-
"upgrade-insecure-requests;", // Automatically upgrade HTTP to HTTPS
|
|
59
|
-
`report-uri ${process.env.ENV_MODE === "prod" ? "/.netlify/functions/cspReport" : "/api/mock-csp"};`, // Add CSP report URI for violations
|
|
60
|
-
].join(" "),
|
|
61
|
-
);
|
|
52
|
+
cspDirectives[1] =
|
|
53
|
+
"script-src 'self' 'unsafe-inline' 'unsafe-eval' ws://localhost:*;";
|
|
54
|
+
cspDirectives[2] =
|
|
55
|
+
"script-src-elem 'self' 'unsafe-inline' 'unsafe-eval' ws://localhost:*;";
|
|
56
|
+
cspDirectives[3] = "style-src 'self' 'unsafe-inline';";
|
|
57
|
+
cspDirectives[4] = "img-src 'self' data:;";
|
|
58
|
+
cspDirectives[5] = "connect-src 'self';";
|
|
62
59
|
}
|
|
63
60
|
|
|
61
|
+
response.headers.set("Content-Security-Policy", cspDirectives.join(" "));
|
|
62
|
+
|
|
64
63
|
// Set other security headers
|
|
65
64
|
response.headers.set(
|
|
66
65
|
"Permissions-Policy",
|
|
@@ -78,8 +77,7 @@ export async function handle({ event, resolve }) {
|
|
|
78
77
|
"gamepad=()",
|
|
79
78
|
"serial=()",
|
|
80
79
|
"publickey-credentials-get=()",
|
|
81
|
-
"
|
|
82
|
-
"topics=()",
|
|
80
|
+
"browsing-topics=()",
|
|
83
81
|
].join(", "),
|
|
84
82
|
);
|
|
85
83
|
|
|
@@ -90,11 +88,9 @@ export async function handle({ event, resolve }) {
|
|
|
90
88
|
if (process.env.ENV_MODE !== "test" && process.env.ENV_MODE !== "ci") {
|
|
91
89
|
response.headers.set(
|
|
92
90
|
"Strict-Transport-Security",
|
|
93
|
-
"max-age=
|
|
91
|
+
"max-age=31536000; includeSubDomains;", // No preload here
|
|
94
92
|
);
|
|
95
93
|
}
|
|
96
94
|
|
|
97
95
|
return response;
|
|
98
96
|
}
|
|
99
|
-
|
|
100
|
-
// cspell:ignore licdn
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!-- ==========================================================================
|
|
2
|
+
src/lib/components/PostHog.svelte
|
|
3
|
+
|
|
4
|
+
Copyright © 2025 Network Pro Strategies (Network Pro™)
|
|
5
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
6
|
+
This file is part of Network Pro.
|
|
7
|
+
========================================================================== -->
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
import posthog from "posthog-js";
|
|
11
|
+
import { onMount } from "svelte";
|
|
12
|
+
import { browser } from "$app/environment";
|
|
13
|
+
|
|
14
|
+
// cspell:disable
|
|
15
|
+
onMount(() => {
|
|
16
|
+
if (browser) {
|
|
17
|
+
posthog.init("phc_Qshfo6AXzh4pS7aPigfqyeo4qj1qlyh7gDuHDeVMSR0", {
|
|
18
|
+
api_host: "https://us.i.posthog.com",
|
|
19
|
+
person_profiles: "identified_only",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
//cspell:enable
|
|
24
|
+
</script>
|
package/src/lib/img/logo-web.png
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -56,7 +56,7 @@ This file is part of Network Pro.
|
|
|
56
56
|
text: "CC-BY-4.0 OR GPL-3.0-or-later",
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
|
-
label: "
|
|
59
|
+
label: "Docs",
|
|
60
60
|
href: "https://docs.netwk.pro",
|
|
61
61
|
target: "_self",
|
|
62
62
|
},
|
|
@@ -103,9 +103,8 @@ This file is part of Network Pro.
|
|
|
103
103
|
<section id="top">
|
|
104
104
|
<span class="small-text">
|
|
105
105
|
<a {rel} href={navLinks[0].href} target={navLinks[0].target}>
|
|
106
|
-
{navLinks[0].label}
|
|
107
|
-
</
|
|
108
|
-
: <code>{navLinks[0].text}</code>
|
|
106
|
+
{navLinks[0].label}</a
|
|
107
|
+
>: <code>{navLinks[0].text}</code>
|
|
109
108
|
</span>
|
|
110
109
|
</section>
|
|
111
110
|
|
|
@@ -193,7 +192,7 @@ This file is part of Network Pro.
|
|
|
193
192
|
<div class="spacer"></div>
|
|
194
193
|
|
|
195
194
|
<p>
|
|
196
|
-
<a {rel} href="https://
|
|
195
|
+
<a {rel} href="https://netwk.pro/contact" target="_blank">Let's connect</a>
|
|
197
196
|
to discuss how we can help secure and strengthen your business today.
|
|
198
197
|
</p>
|
|
199
198
|
|
|
@@ -79,8 +79,7 @@ This file is part of Network Pro.
|
|
|
79
79
|
href="https://spdx.dev/learn/handling-license-info"
|
|
80
80
|
target={constants.targetBlank}>
|
|
81
81
|
SPDX License Identifier
|
|
82
|
-
</a>
|
|
83
|
-
: <code>CC-BY-4.0 OR GPL-3.0-or-later</code>
|
|
82
|
+
</a>: <code>CC-BY-4.0 OR GPL-3.0-or-later</code>
|
|
84
83
|
</span>
|
|
85
84
|
</section>
|
|
86
85
|
|
|
@@ -114,7 +113,7 @@ This file is part of Network Pro.
|
|
|
114
113
|
<strong>Formats Available:</strong> <span class="visited"
|
|
115
114
|
>HTML</span>
|
|
116
115
|
|
|
|
117
|
-
<a href={legalLink} target={constants.targetSelf}>
|
|
116
|
+
<a href={legalLink} target={constants.targetSelf}>Docs</a>
|
|
118
117
|
</sup>
|
|
119
118
|
</p>
|
|
120
119
|
</section>
|
|
@@ -35,6 +35,7 @@ This file is part of Network Pro.
|
|
|
35
35
|
const tocLinks = [
|
|
36
36
|
{ id: "intro", text: "Introduction" },
|
|
37
37
|
{ id: "collect", text: "Information We Collect" },
|
|
38
|
+
{ id: "tracking", text: "Web Analytics and Tracking" },
|
|
38
39
|
{ id: "payment", text: "Payment Information" },
|
|
39
40
|
{ id: "use", text: "Use of Information" },
|
|
40
41
|
{ id: "sharing", text: "Data Sharing" },
|
|
@@ -87,8 +88,7 @@ This file is part of Network Pro.
|
|
|
87
88
|
href="https://spdx.dev/learn/handling-license-info"
|
|
88
89
|
target={constants.targetBlank}>
|
|
89
90
|
SPDX License Identifier
|
|
90
|
-
</a>
|
|
91
|
-
: <code>CC-BY-4.0 OR GPL-3.0-or-later</code>
|
|
91
|
+
</a>: <code>CC-BY-4.0 OR GPL-3.0-or-later</code>
|
|
92
92
|
</span>
|
|
93
93
|
</section>
|
|
94
94
|
|
|
@@ -122,7 +122,7 @@ This file is part of Network Pro.
|
|
|
122
122
|
<strong>Formats Available:</strong> <span class="visited"
|
|
123
123
|
>HTML</span>
|
|
124
124
|
|
|
|
125
|
-
<a href={privacyLink} target={constants.targetSelf}>
|
|
125
|
+
<a href={privacyLink} target={constants.targetSelf}>Docs</a>
|
|
126
126
|
</sup>
|
|
127
127
|
</p>
|
|
128
128
|
</section>
|
|
@@ -161,6 +161,39 @@ This file is part of Network Pro.
|
|
|
161
161
|
<li
|
|
162
162
|
><strong>Client-Submitted Content</strong> related to our services</li>
|
|
163
163
|
</ul>
|
|
164
|
+
{:else if link.id === "tracking"}
|
|
165
|
+
<p>
|
|
166
|
+
To better understand visitor behavior and optimize website
|
|
167
|
+
functionality, we use <strong>PostHog Cloud</strong>, a hosted version
|
|
168
|
+
of the open-source PostHog analytics platform. This tool helps us
|
|
169
|
+
evaluate site performance and user engagement through the collection of
|
|
170
|
+
non-personally identifiable technical data.
|
|
171
|
+
</p>
|
|
172
|
+
<p> PostHog Cloud may collect and process information such as: </p>
|
|
173
|
+
<ul>
|
|
174
|
+
<li>Pages visited and navigation behavior</li>
|
|
175
|
+
<li>Device type, browser version, and operating system</li>
|
|
176
|
+
<li
|
|
177
|
+
>Time spent on pages and interaction events (e.g., clicks, scrolls)</li>
|
|
178
|
+
<li>Referral URLs and outbound link activity</li>
|
|
179
|
+
<li>General geolocation (approximate, based on IP address)</li>
|
|
180
|
+
</ul>
|
|
181
|
+
<p>
|
|
182
|
+
We configure PostHog to prioritize user privacy. <strong
|
|
183
|
+
>Analytics tracking is automatically disabled when a user's browser
|
|
184
|
+
sends a “Do Not Track” (DNT) or “Global Privacy Control” (GPC /
|
|
185
|
+
Sec-GPC) signal.</strong> No further action is required—your browser settings
|
|
186
|
+
are honored by default.
|
|
187
|
+
</p>
|
|
188
|
+
<p>
|
|
189
|
+
PostHog Cloud is a third-party service, but we deploy it in a
|
|
190
|
+
privacy-conscious manner that avoids intrusive profiling and aligns with
|
|
191
|
+
data protection best practices. For more information, please refer to <a
|
|
192
|
+
rel={constants.rel}
|
|
193
|
+
href="https://posthog.com/privacy"
|
|
194
|
+
target={constants.targetBlank}>PostHog's Privacy Policy</a
|
|
195
|
+
>.
|
|
196
|
+
</p>
|
|
164
197
|
{:else if link.id === "payment"}
|
|
165
198
|
<p>
|
|
166
199
|
When processing payments, we may collect credit card details and billing
|
|
@@ -112,7 +112,7 @@ This file is part of Network Pro.
|
|
|
112
112
|
<strong>Formats Available:</strong> <span class="visited"
|
|
113
113
|
>HTML</span>
|
|
114
114
|
|
|
|
115
|
-
<a href={tandcLink} target={constants.targetSelf}>
|
|
115
|
+
<a href={tandcLink} target={constants.targetSelf}>Docs</a>
|
|
116
116
|
|
|
|
117
117
|
<a
|
|
118
118
|
rel={constants.rel}
|
|
@@ -78,8 +78,7 @@ This file is part of Network Pro.
|
|
|
78
78
|
href="https://spdx.dev/learn/handling-license-info"
|
|
79
79
|
target={constants.targetBlank}>
|
|
80
80
|
SPDX License Identifier
|
|
81
|
-
</a>
|
|
82
|
-
: <code>CC-BY-4.0 OR GPL-3.0-or-later</code>
|
|
81
|
+
</a>: <code>CC-BY-4.0 OR GPL-3.0-or-later</code>
|
|
83
82
|
</span>
|
|
84
83
|
</section>
|
|
85
84
|
|
|
@@ -122,7 +121,7 @@ This file is part of Network Pro.
|
|
|
122
121
|
>HTML</span>
|
|
123
122
|
|
|
|
124
123
|
<a rel={constants.rel} href={termsLink} target={constants.targetSelf}
|
|
125
|
-
>
|
|
124
|
+
>Docs</a>
|
|
126
125
|
</sup>
|
|
127
126
|
</p>
|
|
128
127
|
</section>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/* ==========================================================================
|
|
2
2
|
src/lib/styles/default.css
|
|
3
3
|
|
|
4
4
|
Copyright © 2025 Network Pro Strategies (Network Pro™)
|
|
@@ -362,6 +362,14 @@ footer .container {
|
|
|
362
362
|
text-align: center;
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
.index4 {
|
|
366
|
+
font-size: 1.5rem;
|
|
367
|
+
line-height: 1.75rem;
|
|
368
|
+
letter-spacing: -0.035em;
|
|
369
|
+
text-align: center;
|
|
370
|
+
text-decoration: underline;
|
|
371
|
+
}
|
|
372
|
+
|
|
365
373
|
.subhead {
|
|
366
374
|
font-size: 1.5rem;
|
|
367
375
|
line-height: 1.75rem;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/* ==========================================================================
|
|
2
2
|
src/lib/styles/normalize.css
|
|
3
3
|
|
|
4
4
|
Copyright © 2025 Network Pro Strategies (Network Pro™)
|
|
5
5
|
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
6
6
|
This file is part of Network Pro.
|
|
7
|
-
|
|
7
|
+
========================================================================== */
|
|
8
8
|
|
|
9
9
|
/*! Modified from normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
|
10
10
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/* ==========================================================================
|
|
2
2
|
src/lib/styles/style.css
|
|
3
3
|
|
|
4
4
|
Copyright © 2025 Network Pro Strategies (Network Pro™)
|
|
5
5
|
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
6
6
|
This file is part of Network Pro.
|
|
7
|
-
|
|
7
|
+
========================================================================== */
|
|
8
8
|
|
|
9
9
|
/*! Modified from HTML5 Boilerplate v9.0.1 | MIT License | https://html5boilerplate.com/ */
|
|
10
10
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/*! ==========================================================================
|
|
2
|
-
src/lib/styles/global.min.css
|
|
2
|
+
src/lib/styles/css/global.min.css
|
|
3
3
|
|
|
4
4
|
Copyright © 2025 Network Pro Strategies (Network Pro™)
|
|
5
5
|
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
6
6
|
This file is part of Network Pro.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/*! Modified from HTML5 Boilerplate v9.0.1 | MIT License | https://html5boilerplate.com/ */
|
|
10
|
-
html{-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{margin:.67em 0;font-size:2em}hr{box-sizing:content-box}pre{font-family:monospace;font-size:1em}a{background-color:#0000}abbr[title]{border-bottom:none;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted buttontext}fieldset{padding:.35em .75em .625em}legend{color:inherit;box-sizing:border-box;white-space:normal;max-width:100%;padding:0;display:table}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}details{display:block}summary{display:list-item}template{display:none}html{color:#222;scroll-behavior:smooth;font-size:1em;line-height:1.4}::-moz-selection{text-shadow:none;background:#191919}::selection{text-shadow:none;background:#191919}hr{border:0;border-top:1px solid #ccc;height:1px;margin:1em 0;padding:0;display:block;overflow:visible}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}body{color:#fafafa;background-color:#191919;margin:10px;font-family:Arial,Helvetica,sans-serif}a{text-decoration:none}a:link{color:#ffc627}a:hover,a:active{color:#ffc627;text-decoration:underline}a:focus{color:#191919;background-color:#ffc627}a:visited,a:visited:hover{color:#7f6227}.hidden,[hidden]{display:none!important}.visually-hidden{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.visually-hidden.focusable:active,.visually-hidden.focusable:focus{clip:auto;width:auto;height:auto;white-space:inherit;margin:0;position:static;overflow:visible}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:"";display:table}.clearfix:after{clear:both}@media print{*,:before,:after{color:#000!important;box-shadow:none!important;text-shadow:none!important;background:#fff!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href)")"}abbr[title]:after{content:" (" attr(title)")"}a[href^=\#]:after,a[href^=javascript\:]:after{content:""}pre{white-space:pre-wrap!important}pre,blockquote{page-break-inside:avoid;border:1px solid #999}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.full-width-section{background-position:50%;background-size:cover;width:100%;max-width:1920px;margin:0 auto}.container{max-width:1200px;margin:0 auto;padding:0 12px}header,footer{width:100%}header .container,footer .container{max-width:1200px;margin:0 auto;padding:20px 12px}.gh{border-collapse:collapse;border-spacing:0;margin:0 auto}.gh td,.gh th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.gh .gh-tcell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.gh,.gh col{width:auto!important}.gh-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.soc{border-collapse:collapse;border-spacing:0;margin:0 auto}.soc td,.soc th{border-collapse:collapse;word-break:normal;padding:8px;overflow:hidden}.soc .soc-fa{text-align:center;vertical-align:middle}@media screen and (width<=767px){.soc,.soc col{width:auto!important}.soc-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.foss{border-collapse:collapse;border-spacing:0}.foss td,.foss th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.foss .foss-cell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.foss,.foss col{width:auto!important}.foss-wrap{-webkit-overflow-scrolling:touch;overflow-x:auto}}.bnav{border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav th{word-break:normal;border-style:none;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.bnav .bnav-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav,.bnav col{width:auto!important}.bnav-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.bnav2{border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav2 td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav2 th{word-break:normal;border-style:none;padding:12px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.bnav2 .bnav2-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav2,.bnav2 col{width:auto!important}.bnav2-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.pgp{border-collapse:collapse;border-spacing:0;margin:0 auto}.pgp td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp th{word-break:normal;border:1px solid #000;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp .pgp-col1{text-align:right;vertical-align:middle;padding-right:1rem}.pgp .pgp-col2{text-align:left;vertical-align:middle;padding-left:1rem}@media screen and (width<=767px){.pgp,.pgp col{width:auto!important}.pgp-wrap{-webkit-overflow-scrolling:touch;margin:2rem 0 auto;overflow-x:auto}}.logo{margin-left:auto;margin-right:auto;display:block}.index-title1{text-align:center;font-style:italic;font-weight:700}.index-title2{letter-spacing:-.015em;text-align:center;font-variant:small-caps;font-size:1.25rem;line-height:1.625rem}.index1{letter-spacing:-.035em;text-align:center;font-style:italic;font-weight:700;line-height:2.125rem}.index2{letter-spacing:-.035em;text-align:center;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.index3{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem}.subhead{letter-spacing:-.035em;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.bolditalic{font-style:italic;font-weight:700}.bquote{border-left:3px solid #9e9e9e;margin-left:30px;padding-left:10px;font-style:italic}.small-text{font-size:.75rem;line-height:1.125rem}.large-text-center{text-align:center;font-size:1.25rem;line-height:1.75rem}.prewrap{white-space:pre-wrap;display:block}.hr-styled{width:75%;margin:auto}.center-text{text-align:center}.copyright{text-align:center;font-size:.75rem;line-height:1.125rem}.visited{color:#7f6227}.center-nav{text-align:center;padding:5px;font-size:.875rem;line-height:1.125rem}.block{resize:none;background:0 0;border:none;border-radius:0;outline:none;width:100%;font-size:.75rem;line-height:1.125rem}.fingerprint{white-space:pre-line;font-weight:700;display:block}.pgp-image{width:125px;height:125px}.spacer{margin:2rem 0}.separator{margin:0 .5rem}
|
|
7
|
+
========================================================================== */
|
|
8
|
+
html{-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{margin:.67em 0;font-size:2em}hr{box-sizing:content-box}pre{font-family:monospace;font-size:1em}a{background-color:#0000}abbr[title]{border-bottom:none;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted buttontext}fieldset{padding:.35em .75em .625em}legend{color:inherit;box-sizing:border-box;white-space:normal;max-width:100%;padding:0;display:table}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}details{display:block}summary{display:list-item}template{display:none}html{color:#222;scroll-behavior:smooth;font-size:1em;line-height:1.4}::-moz-selection{text-shadow:none;background:#191919}::selection{text-shadow:none;background:#191919}hr{border:0;border-top:1px solid #ccc;height:1px;margin:1em 0;padding:0;display:block;overflow:visible}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}body{color:#fafafa;background-color:#191919;margin:10px;font-family:Arial,Helvetica,sans-serif}a{text-decoration:none}a:link{color:#ffc627}a:hover,a:active{color:#ffc627;text-decoration:underline}a:focus{color:#191919;background-color:#ffc627}a:visited,a:visited:hover{color:#7f6227}.hidden,[hidden]{display:none!important}.visually-hidden{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.visually-hidden.focusable:active,.visually-hidden.focusable:focus{clip:auto;width:auto;height:auto;white-space:inherit;margin:0;position:static;overflow:visible}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:"";display:table}.clearfix:after{clear:both}@media print{*,:before,:after{color:#000!important;box-shadow:none!important;text-shadow:none!important;background:#fff!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href)")"}abbr[title]:after{content:" (" attr(title)")"}a[href^=\#]:after,a[href^=javascript\:]:after{content:""}pre{white-space:pre-wrap!important}pre,blockquote{page-break-inside:avoid;border:1px solid #999}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.full-width-section{background-position:50%;background-size:cover;width:100%;max-width:1920px;margin:0 auto}.container{max-width:1200px;margin:0 auto;padding:0 12px}header,footer{width:100%}header .container,footer .container{max-width:1200px;margin:0 auto;padding:20px 12px}.gh{border-collapse:collapse;border-spacing:0;margin:0 auto}.gh td,.gh th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.gh .gh-tcell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.gh,.gh col{width:auto!important}.gh-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.soc{border-collapse:collapse;border-spacing:0;margin:0 auto}.soc td,.soc th{border-collapse:collapse;word-break:normal;padding:8px;overflow:hidden}.soc .soc-fa{text-align:center;vertical-align:middle}@media screen and (width<=767px){.soc,.soc col{width:auto!important}.soc-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.foss{border-collapse:collapse;border-spacing:0}.foss td,.foss th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.foss .foss-cell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.foss,.foss col{width:auto!important}.foss-wrap{-webkit-overflow-scrolling:touch;overflow-x:auto}}.bnav{border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav th{word-break:normal;border-style:none;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.bnav .bnav-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav,.bnav col{width:auto!important}.bnav-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.bnav2{border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav2 td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav2 th{word-break:normal;border-style:none;padding:12px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.bnav2 .bnav2-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav2,.bnav2 col{width:auto!important}.bnav2-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.pgp{border-collapse:collapse;border-spacing:0;margin:0 auto}.pgp td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp th{word-break:normal;border:1px solid #000;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp .pgp-col1{text-align:right;vertical-align:middle;padding-right:1rem}.pgp .pgp-col2{text-align:left;vertical-align:middle;padding-left:1rem}@media screen and (width<=767px){.pgp,.pgp col{width:auto!important}.pgp-wrap{-webkit-overflow-scrolling:touch;margin:2rem 0 auto;overflow-x:auto}}.logo{margin-left:auto;margin-right:auto;display:block}.index-title1{text-align:center;font-style:italic;font-weight:700}.index-title2{letter-spacing:-.015em;text-align:center;font-variant:small-caps;font-size:1.25rem;line-height:1.625rem}.index1{letter-spacing:-.035em;text-align:center;font-style:italic;font-weight:700;line-height:2.125rem}.index2{letter-spacing:-.035em;text-align:center;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.index3{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem}.index4{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem;text-decoration:underline}.subhead{letter-spacing:-.035em;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.bolditalic{font-style:italic;font-weight:700}.bquote{border-left:3px solid #9e9e9e;margin-left:30px;padding-left:10px;font-style:italic}.small-text{font-size:.75rem;line-height:1.125rem}.large-text-center{text-align:center;font-size:1.25rem;line-height:1.75rem}.prewrap{white-space:pre-wrap;display:block}.hr-styled{width:75%;margin:auto}.center-text{text-align:center}.copyright{text-align:center;font-size:.75rem;line-height:1.125rem}.visited{color:#7f6227}.center-nav{text-align:center;padding:5px;font-size:.875rem;line-height:1.125rem}.block{resize:none;background:0 0;border:none;border-radius:0;outline:none;width:100%;font-size:.75rem;line-height:1.125rem}.fingerprint{white-space:pre-line;font-weight:700;display:block}.pgp-image{width:125px;height:125px}.spacer{margin:2rem 0}.separator{margin:0 .5rem}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
src/lib/utils/privacy.js
|
|
3
|
+
|
|
4
|
+
Copyright © 2025 Network Pro Strategies (Network Pro™)
|
|
5
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
6
|
+
This file is part of Network Pro.
|
|
7
|
+
========================================================================== */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Determines whether the user allows tracking based on DNT or GPC signals.
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
export function shouldTrackUser() {
|
|
14
|
+
/** @type {string | undefined} */
|
|
15
|
+
const windowDNT = /** @type {any} */ (window).doNotTrack;
|
|
16
|
+
/** @type {boolean | undefined} */
|
|
17
|
+
const navigatorGPC = /** @type {any} */ (navigator).globalPrivacyControl;
|
|
18
|
+
|
|
19
|
+
const dnt = navigator.doNotTrack === "1" || windowDNT === "1";
|
|
20
|
+
const gpc = navigatorGPC === true;
|
|
21
|
+
|
|
22
|
+
return !dnt && !gpc;
|
|
23
|
+
}
|
package/src/routes/+layout.js
CHANGED
|
@@ -14,10 +14,12 @@ This file is part of Network Pro.
|
|
|
14
14
|
import HeaderDefault from "$lib/components/layout/HeaderDefault.svelte";
|
|
15
15
|
import HeaderHome from "$lib/components/layout/HeaderHome.svelte";
|
|
16
16
|
import PWAInstallButton from "$lib/components/PWAInstallButton.svelte";
|
|
17
|
+
import { shouldTrackUser } from "$lib/utils/privacy.js";
|
|
17
18
|
import { onMount } from "svelte";
|
|
18
19
|
import { registerServiceWorker } from "$lib/registerServiceWorker.js";
|
|
19
20
|
import { browser } from "$app/environment";
|
|
20
|
-
import "$lib/styles";
|
|
21
|
+
import "$lib/styles/global.min.css";
|
|
22
|
+
import "$lib/styles/fa-global.css";
|
|
21
23
|
|
|
22
24
|
// Import favicon images
|
|
23
25
|
import logoPng from "$lib/img/logo-web.png";
|
|
@@ -25,21 +27,41 @@ This file is part of Network Pro.
|
|
|
25
27
|
import faviconSvg from "$lib/img/favicon.svg";
|
|
26
28
|
import appleTouchIcon from "$lib/img/icon-180x180.png";
|
|
27
29
|
|
|
30
|
+
// Declare PostHog as null initially
|
|
31
|
+
/** @type {typeof import('$lib/components/PostHog.svelte').default | null} */
|
|
32
|
+
let PostHog = null;
|
|
33
|
+
|
|
28
34
|
if (browser) {
|
|
29
|
-
// Preload
|
|
30
|
-
[logoPng, logoWbp].forEach((src) => {
|
|
35
|
+
// Preload all core images (logos + apple touch)
|
|
36
|
+
[logoPng, logoWbp, appleTouchIcon].forEach((src) => {
|
|
31
37
|
const img = new Image();
|
|
32
38
|
img.src = src;
|
|
33
39
|
});
|
|
34
40
|
|
|
35
|
-
//
|
|
36
|
-
const touchImg = new Image();
|
|
37
|
-
touchImg.src = appleTouchIcon;
|
|
38
|
-
|
|
39
|
-
// Register the service worker only in the browser
|
|
41
|
+
// Run setup when component mounts (only in browser)
|
|
40
42
|
onMount(() => {
|
|
41
43
|
console.log("[APP] onMount triggered in +layout.svelte");
|
|
42
44
|
registerServiceWorker();
|
|
45
|
+
|
|
46
|
+
const isDev = import.meta.env.MODE === "development";
|
|
47
|
+
|
|
48
|
+
console.log("ENV MODE =", import.meta.env.MODE); // Should be "development"
|
|
49
|
+
console.log("isDev =", isDev);
|
|
50
|
+
console.log("shouldTrackUser =", shouldTrackUser());
|
|
51
|
+
|
|
52
|
+
if (isDev || shouldTrackUser()) {
|
|
53
|
+
import("$lib/components/PostHog.svelte").then((module) => {
|
|
54
|
+
PostHog = module.default;
|
|
55
|
+
|
|
56
|
+
if (isDev) {
|
|
57
|
+
console.log("[Dev] ✅ PostHog component loaded (tracking enabled)");
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
} else {
|
|
61
|
+
console.log(
|
|
62
|
+
"[Privacy] ⛔ Skipping PostHog component due to DNT or GPC signal.",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
43
65
|
});
|
|
44
66
|
}
|
|
45
67
|
|
|
@@ -49,8 +71,6 @@ This file is part of Network Pro.
|
|
|
49
71
|
const metaDescription =
|
|
50
72
|
data?.meta?.description ||
|
|
51
73
|
"Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro™";
|
|
52
|
-
|
|
53
|
-
// Pathname normalization takes place in +layout.js
|
|
54
74
|
</script>
|
|
55
75
|
|
|
56
76
|
<svelte:head>
|
|
@@ -83,6 +103,10 @@ This file is part of Network Pro.
|
|
|
83
103
|
</header>
|
|
84
104
|
<!-- END HEADER -->
|
|
85
105
|
|
|
106
|
+
{#if PostHog}
|
|
107
|
+
<PostHog /> <!-- Add PostHog component when it's loaded -->
|
|
108
|
+
{/if}
|
|
109
|
+
|
|
86
110
|
<main>
|
|
87
111
|
<slot />
|
|
88
112
|
</main>
|
package/src/service-worker.js
CHANGED
|
@@ -12,7 +12,10 @@ const sw = /** @type {ServiceWorkerGlobalScope} */ (
|
|
|
12
12
|
);
|
|
13
13
|
|
|
14
14
|
const isDev = location.hostname === "localhost";
|
|
15
|
-
const disallowedHosts = [
|
|
15
|
+
const disallowedHosts = [
|
|
16
|
+
"us.i.posthog.com", // Add PostHog to disallowed hosts
|
|
17
|
+
"posthog.com", // Add PostHog to disallowed hosts
|
|
18
|
+
];
|
|
16
19
|
|
|
17
20
|
import { build, files, version } from "$service-worker";
|
|
18
21
|
|
|
@@ -25,8 +28,8 @@ const excludedAssets = [];
|
|
|
25
28
|
//TODO: Remove files in docs once migrated to documentation subsite
|
|
26
29
|
|
|
27
30
|
const IGNORE_PATHS = new Set([
|
|
28
|
-
"/docs/Home.md",
|
|
29
31
|
"/docs/extensions.md",
|
|
32
|
+
"/docs/Home.md",
|
|
30
33
|
"/img/banner-1280x640.png",
|
|
31
34
|
"/img/banner-og-1200x630.png",
|
|
32
35
|
"/img/logo-transparent.png",
|
package/tests/e2e/app.spec.js
CHANGED
|
@@ -19,6 +19,10 @@ test.describe("Desktop Tests", () => {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
22
|
+
|
|
23
|
+
// Add a small timeout before navigating to the page
|
|
24
|
+
await page.waitForTimeout(1500); // Wait for 1.5 seconds
|
|
25
|
+
|
|
22
26
|
await page.goto("/");
|
|
23
27
|
|
|
24
28
|
// Wait for the page to fully load
|
|
@@ -33,6 +37,10 @@ test.describe("Desktop Tests", () => {
|
|
|
33
37
|
page,
|
|
34
38
|
}) => {
|
|
35
39
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
40
|
+
|
|
41
|
+
// Add a small timeout before navigating to the page
|
|
42
|
+
await page.waitForTimeout(1500); // Wait for 1.5 seconds
|
|
43
|
+
|
|
36
44
|
await page.goto("/");
|
|
37
45
|
|
|
38
46
|
// Ensure the navigation bar is visible
|
|
@@ -48,6 +56,10 @@ test.describe("Desktop Tests", () => {
|
|
|
48
56
|
// Simplified Footer Visibility Test
|
|
49
57
|
test("should display the footer correctly", async ({ page }) => {
|
|
50
58
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
59
|
+
|
|
60
|
+
// Add a small timeout before navigating to the page
|
|
61
|
+
await page.waitForTimeout(1500); // Wait for 1.5 seconds
|
|
62
|
+
|
|
51
63
|
await page.goto("/");
|
|
52
64
|
|
|
53
65
|
// Check that the footer is visible
|
|
@@ -58,6 +70,10 @@ test.describe("Desktop Tests", () => {
|
|
|
58
70
|
// Simplified Test for Clickable Links (e.g., 'about' link)
|
|
59
71
|
test("should ensure the 'about' link is clickable", async ({ page }) => {
|
|
60
72
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
73
|
+
|
|
74
|
+
// Add a small timeout before navigating to the page
|
|
75
|
+
await page.waitForTimeout(1500); // Wait for 1.5 seconds
|
|
76
|
+
|
|
61
77
|
await page.goto("/");
|
|
62
78
|
|
|
63
79
|
// Ensure the "about" link is visible and clickable
|
|
@@ -84,6 +100,10 @@ test.describe("Mobile Tests", () => {
|
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
await page.setViewportSize({ width: 375, height: 667 }); // Mobile size (e.g., iPhone 6)
|
|
103
|
+
|
|
104
|
+
// Add a small timeout before navigating to the page
|
|
105
|
+
await page.waitForTimeout(1500); // Wait for 1.5 seconds
|
|
106
|
+
|
|
87
107
|
await page.goto("/");
|
|
88
108
|
|
|
89
109
|
// Wait for the page to fully load
|
|
@@ -96,6 +116,10 @@ test.describe("Mobile Tests", () => {
|
|
|
96
116
|
// Simplified Test for mobile content visibility
|
|
97
117
|
test("should display main content correctly on mobile", async ({ page }) => {
|
|
98
118
|
await page.setViewportSize({ width: 375, height: 667 }); // Mobile size
|
|
119
|
+
|
|
120
|
+
// Add a small timeout before navigating to the page
|
|
121
|
+
await page.waitForTimeout(1500); // Wait for 1.5 seconds
|
|
122
|
+
|
|
99
123
|
await page.goto("/");
|
|
100
124
|
|
|
101
125
|
// Check that the main heading is visible on mobile
|
package/tests/e2e/mobile.spec.js
CHANGED
|
@@ -19,6 +19,10 @@ test.describe("Mobile Tests", () => {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
await page.setViewportSize({ width: 375, height: 667 }); // Mobile size (e.g., iPhone 6)
|
|
22
|
+
|
|
23
|
+
// Add a small timeout before navigating to the page
|
|
24
|
+
await page.waitForTimeout(1500); // Wait for 1.5 seconds
|
|
25
|
+
|
|
22
26
|
await page.goto("/");
|
|
23
27
|
|
|
24
28
|
// Wait for the page to load and for the title element to be available
|
|
@@ -44,6 +48,10 @@ test.describe("Mobile Tests", () => {
|
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
await page.setViewportSize({ width: 375, height: 667 }); // Mobile size
|
|
51
|
+
|
|
52
|
+
// Add a small timeout before navigating to the page
|
|
53
|
+
await page.waitForTimeout(1500); // Wait for 1.5 seconds
|
|
54
|
+
|
|
47
55
|
await page.goto("/");
|
|
48
56
|
|
|
49
57
|
// Wait for the page to load
|
|
@@ -63,6 +71,10 @@ test.describe("Mobile Tests", () => {
|
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
await page.setViewportSize({ width: 375, height: 667 }); // Mobile size
|
|
74
|
+
|
|
75
|
+
// Add a small timeout before navigating to the page
|
|
76
|
+
await page.waitForTimeout(1500); // Wait for 1.5 seconds
|
|
77
|
+
|
|
66
78
|
await page.goto("/");
|
|
67
79
|
|
|
68
80
|
// Wait for the page to load
|
|
@@ -77,3 +89,5 @@ test.describe("Mobile Tests", () => {
|
|
|
77
89
|
await expect(page).toHaveURL(/\/about/);
|
|
78
90
|
});
|
|
79
91
|
});
|
|
92
|
+
|
|
93
|
+
// cspell:ignore domcontentloaded
|