@networkpro/web 1.10.1 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cspell.json CHANGED
@@ -30,6 +30,7 @@
30
30
  "linksheet",
31
31
  "Maricopa",
32
32
  "mdsvex",
33
+ "navigations",
33
34
  "neteng",
34
35
  "netpro",
35
36
  "netwk",
@@ -43,6 +44,8 @@
43
44
  "ntfy",
44
45
  "obtainium",
45
46
  "posthog",
47
+ "prefs",
48
+ "reconsent",
46
49
  "SIEM",
47
50
  "SPDY",
48
51
  "stylelintignore",
package/jsconfig.json CHANGED
@@ -1,11 +1,3 @@
1
- /* =========================================================================
2
- jsconfig.json
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
1
  {
10
2
  "extends": "./.svelte-kit/tsconfig.json",
11
3
  "compilerOptions": {
@@ -19,12 +11,6 @@ This file is part of Network Pro.
19
11
  "strict": true,
20
12
  "moduleResolution": "bundler"
21
13
  },
22
- "exclude": ["vite.config.js"], // Exclude the config file if needed
14
+ "exclude": ["vite.config.js"],
23
15
  "include": ["src", "src/global.d.ts", "src/service-worker.js"]
24
-
25
- // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
26
- // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
27
-
28
- // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
29
- // from the referenced tsconfig.json - TypeScript does not merge them in
30
16
  }
@@ -0,0 +1,32 @@
1
+ /* =========================================================================
2
+ jsconfig.template.jsonc
3
+
4
+ NOTE: This file is for reference only and is not actively used by SvelteKit or tooling. SvelteKit uses the jsconfig.json file without comments for actual configuration.
5
+
6
+ Copyright © 2025 Network Pro Strategies (Network Pro™)
7
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
8
+ This file is part of Network Pro.
9
+ ========================================================================= */
10
+
11
+ {
12
+ "extends": "./.svelte-kit/tsconfig.json",
13
+ "compilerOptions": {
14
+ "allowJs": true,
15
+ "checkJs": true,
16
+ "esModuleInterop": true,
17
+ "forceConsistentCasingInFileNames": true,
18
+ "resolveJsonModule": true,
19
+ "skipLibCheck": true,
20
+ "sourceMap": true,
21
+ "strict": true,
22
+ "moduleResolution": "bundler"
23
+ },
24
+ "exclude": ["vite.config.js"], // Exclude the config file if needed
25
+ "include": ["src", "src/global.d.ts", "src/service-worker.js"]
26
+
27
+ // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
28
+ // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
29
+
30
+ // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
31
+ // from the referenced tsconfig.json - TypeScript does not merge them in
32
+ }
package/netlify.toml CHANGED
@@ -1,6 +1,5 @@
1
1
  [build]
2
2
  command = "npm run build"
3
- publish = "build"
4
3
 
5
4
  [build.environment]
6
5
  NODE_VERSION = "22"
package/package.json CHANGED
@@ -1,10 +1,7 @@
1
1
  {
2
2
  "name": "@networkpro/web",
3
3
  "private": false,
4
- "sideEffects": [
5
- "./.netlify/shims.js"
6
- ],
7
- "version": "1.10.1",
4
+ "version": "1.11.0",
8
5
  "description": "Locking Down Networks, Unlocking Confidence | Security, Networking, Privacy — Network Pro Strategies",
9
6
  "keywords": [
10
7
  "advisory",
@@ -47,6 +44,8 @@
47
44
  "prepare": "svelte-kit sync || echo ''",
48
45
  "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
49
46
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
47
+ "type-check": "svelte-check --tsconfig ./jsconfig.json",
48
+ "lint:types": "npm run type-check",
50
49
  "check:env": "node scripts/checkEnv.js",
51
50
  "check:node": "node scripts/checkNode.js",
52
51
  "checkout": "npm run check:node && npm run test:all && npm run lint:all && npm run check",
@@ -78,7 +77,7 @@
78
77
  },
79
78
  "dependencies": {
80
79
  "dompurify": "^3.2.6",
81
- "posthog-js": "^1.249.1",
80
+ "posthog-js": "^1.249.2",
82
81
  "semver": "^7.7.2",
83
82
  "svelte": "5.33.14"
84
83
  },
@@ -88,11 +87,11 @@
88
87
  "@lhci/cli": "^0.14.0",
89
88
  "@playwright/test": "^1.52.0",
90
89
  "@sveltejs/adapter-netlify": "^5.0.2",
91
- "@sveltejs/kit": "2.21.1",
92
- "@sveltejs/vite-plugin-svelte": "5.0.3",
90
+ "@sveltejs/kit": "2.21.2",
91
+ "@sveltejs/vite-plugin-svelte": "5.1.0",
93
92
  "@testing-library/jest-dom": "^6.6.3",
94
93
  "@testing-library/svelte": "^5.2.8",
95
- "@vitest/coverage-v8": "^3.2.0",
94
+ "@vitest/coverage-v8": "^3.2.1",
96
95
  "autoprefixer": "^10.4.21",
97
96
  "browserslist": "^4.25.0",
98
97
  "eslint": "^9.28.0",
@@ -119,7 +118,8 @@
119
118
  "typescript": "^5.8.3",
120
119
  "vite": "^6.3.5",
121
120
  "vite-plugin-lightningcss": "^0.0.5",
122
- "vitest": "^3.2.0"
121
+ "vite-tsconfig-paths": "^5.1.4",
122
+ "vitest": "^3.2.1"
123
123
  },
124
124
  "overrides": {
125
125
  "@sveltejs/kit": {
package/src/app.html CHANGED
@@ -72,7 +72,7 @@
72
72
  content="bx4ham0zkpvzztzu213bhpt76m9siq" />
73
73
  <!-- cspell:enable -->
74
74
 
75
- <meta name="generator" content="SvelteKit 2.21.1" />
75
+ <meta name="generator" content="SvelteKit 2.21.2" />
76
76
 
77
77
  <script src="/disableSw.js"></script>
78
78
 
@@ -91,3 +91,16 @@ export async function handle({ event, resolve }) {
91
91
 
92
92
  return response;
93
93
  }
94
+
95
+ /**
96
+ * SvelteKit server-side error handler to log SSR errors.
97
+ * @type {import('@sveltejs/kit').HandleServerError}
98
+ */
99
+ export function handleError({ error, event }) {
100
+ console.error("🔴 SSR Error in route:", event.url.pathname);
101
+ console.error(error);
102
+
103
+ return {
104
+ message: "A server-side error occurred",
105
+ };
106
+ }
@@ -10,10 +10,15 @@ This file is part of Network Pro.
10
10
  import { base } from "$app/paths";
11
11
  // Import badges for licenses
12
12
  import { ccBadge, gplBadge } from "$lib";
13
+ import { CONSTANTS } from "$lib";
13
14
 
14
15
  // Log the base path to verify its value
15
16
  //console.log("Base path:", base);
16
17
 
18
+ console.log(CONSTANTS.COMPANY_INFO.APP_NAME);
19
+
20
+ const { PAGE } = CONSTANTS;
21
+
17
22
  const ccbyLink = `${base}/license#cc-by`;
18
23
  const gplLink = `${base}/license#gnu-gpl`;
19
24
 
@@ -51,14 +56,13 @@ This file is part of Network Pro.
51
56
  </script>
52
57
 
53
58
  <!-- BEGIN BADGES -->
54
- <!-- Updated 2025-05-15 02:12:35 by SunDevil311 -->
55
59
  <div class="bnav2-wrap">
56
60
  <table class="bnav2">
57
61
  <tbody>
58
62
  <tr>
59
63
  {#each badges as badge}
60
64
  <td class="bnav2-nav">
61
- <a href={badge.href} target="_self">
65
+ <a href={badge.href} target={PAGE.SELF}>
62
66
  <img
63
67
  decoding="async"
64
68
  loading="lazy"
@@ -8,10 +8,15 @@ This file is part of Network Pro.
8
8
 
9
9
  <script>
10
10
  import { base } from "$app/paths";
11
+ import { CONSTANTS } from "$lib";
11
12
 
12
13
  // Log the base path to verify its value
13
14
  //console.log("Base path:", base);
14
15
 
16
+ console.log(CONSTANTS.COMPANY_INFO.APP_NAME);
17
+
18
+ const { PAGE } = CONSTANTS;
19
+
15
20
  const termsLink = `${base}/terms-of-use`;
16
21
  const privacyLink = `${base}/privacy`;
17
22
  const licenseLink = `${base}/license`;
@@ -62,7 +67,7 @@ This file is part of Network Pro.
62
67
  <tr>
63
68
  {#each row as link}
64
69
  <td class="bnav-cell" colspan={link.colspan || 1}>
65
- <a href={link.href} target="_self">
70
+ <a href={link.href} target={PAGE.SELF}>
66
71
  {link.text}
67
72
  </a>
68
73
  </td>
@@ -10,10 +10,16 @@ This file is part of Network Pro.
10
10
  export let title;
11
11
  export let description;
12
12
 
13
+ import { CONSTANTS } from "$lib";
14
+
15
+ console.log(CONSTANTS.COMPANY_INFO.APP_NAME);
16
+
17
+ const { COMPANY_INFO, LINKS } = CONSTANTS;
18
+
13
19
  // Static shared values
14
- const ogUrl = "https://netwk.pro";
20
+ const ogUrl = LINKS.HOME;
15
21
  const ogImg = "/img/banner-og-1200x630.png";
16
- const companyName = "Network Pro Strategies";
22
+ const companyName = COMPANY_INFO.NAME;
17
23
  const twitterAct = "@NetEng_Pro";
18
24
  </script>
19
25
 
@@ -149,14 +149,13 @@ This file is part of Network Pro.
149
149
  {:else}
150
150
  <a href={license.url} target={PAGE.SELF} rel={relLicense}>
151
151
  <strong>{license.type}</strong>
152
- </a>
153
- , as published by the
152
+ </a>, as published by the
154
153
  <a rel={PAGE.REL} href={license.externalUrl} target={PAGE.BLANK}
155
154
  >{license.description}</a
156
- >, either version 3 of the License, or (at your option) any later
155
+ >, either version 3 of the License or (at your option) any later
157
156
  version.
158
157
  {/if}
159
- {index < licenses.length - 1 ? ", and the " : ""}
158
+ {index < licenses.length - 1 ? " and the " : ""}
160
159
  {/each}
161
160
  </p>
162
161
  </div>
package/src/lib/index.js CHANGED
@@ -13,7 +13,7 @@ This file is part of Network Pro.
13
13
  * @description Main export point for library components, utilities, and assets
14
14
  * @module src/lib
15
15
  * @author SunDevil311
16
- * @updated 2025-06-02
16
+ * @updated 2025-06-03
17
17
  */
18
18
 
19
19
  // Re-export images from dedicated images.js file
@@ -32,38 +32,8 @@ export * from "./images.js";
32
32
  // export { default as Button } from './components/Button.svelte';
33
33
  // export { default as Card } from './components/Card.svelte';
34
34
 
35
- /**
36
- * @typedef {{
37
- * NAME: string,
38
- * APP_NAME: string,
39
- * YEAR: string
40
- * }} CompanyInfo
41
- *
42
- * @typedef {{
43
- * EMAIL: string,
44
- * SECURE: string,
45
- * PRIVACY: string,
46
- * PHONE: string
47
- * }} ContactInfo
48
- *
49
- * @typedef {{
50
- * BLANK: string,
51
- * SELF: string,
52
- * REL: string
53
- * }} PageTargets
54
- *
55
- * @typedef {{
56
- * BACKTOP: string,
57
- * HREFTOP: string
58
- * }} NavigationLabels
59
- *
60
- * @typedef {{
61
- * COMPANY_INFO: CompanyInfo,
62
- * CONTACT: ContactInfo,
63
- * PAGE: PageTargets,
64
- * NAV: NavigationLabels
65
- * }} AppConstants
66
- */
35
+ /** @typedef {import('./types/appConstants.js').AppConstants} AppConstants */
36
+
67
37
 
68
38
  /** @type {AppConstants} */
69
39
  export const CONSTANTS = {
@@ -87,4 +57,8 @@ export const CONSTANTS = {
87
57
  BACKTOP: "Back to top",
88
58
  HREFTOP: "#top",
89
59
  },
60
+ LINKS: {
61
+ HOME: "https://netwk.pro",
62
+ BLOG: "https://blog.netwk.pro",
63
+ },
90
64
  };
@@ -9,43 +9,45 @@ This file is part of Network Pro.
9
9
  <script>
10
10
  import { base } from "$app/paths";
11
11
  import { onMount } from "svelte";
12
- import { getTrackingPreferences } from "$lib/utils/trackingStatus.js";
13
- /** @type {(type: 'enable' | 'disable') => void} */
12
+ import { trackingStatus } from "$lib/stores/trackingStatus.js";
14
13
  import {
14
+ /** @type {(type: 'enable' | 'disable') => void} */
15
15
  setTrackingPreference,
16
+ /** @type {() => void} */
16
17
  clearTrackingPreferences,
17
18
  } from "$lib/utils/trackingCookies.js";
18
19
  import { CONSTANTS } from "$lib";
19
20
 
20
- // Log the base path to verify its value
21
- //console.log("Base path:", base);
22
-
23
21
  console.log(CONSTANTS.COMPANY_INFO.APP_NAME);
24
22
 
25
- const { COMPANY_INFO, CONTACT, PAGE, NAV } = CONSTANTS;
23
+ /** @type {typeof CONSTANTS.COMPANY_INFO} */
24
+ const COMPANY_INFO = CONSTANTS.COMPANY_INFO;
26
25
 
27
- /**
28
- * URL to the Privacy Rights Request Form redirect route, using the base path
29
- * URL to the Contact Form redirect route, using the base path
30
- * URL to the Privacy Dashboard using the base path
31
- * @type {string}
32
- */
26
+ /** @type {typeof CONSTANTS.CONTACT} */
27
+ const CONTACT = CONSTANTS.CONTACT;
28
+
29
+ /** @type {typeof CONSTANTS.PAGE} */
30
+ const PAGE = CONSTANTS.PAGE;
31
+
32
+ /** @type {typeof CONSTANTS.NAV} */
33
+ const NAV = CONSTANTS.NAV;
34
+
35
+ /** @type {string} */
33
36
  const prightsLink = `${base}/privacy-rights`;
37
+
38
+ /** @type {string} */
34
39
  const contactLink = `${base}/contact`;
40
+
41
+ /** @type {string} */
35
42
  const pdashLink = `${base}/privacy-dashboard`;
36
43
 
37
- /**
38
- * URL to the privacy policy in Markdown format
39
- * External URL to the GPC website
40
- * @type {string}
41
- */
44
+ /** @type {string} */
42
45
  const privacyLink = "https://docs.netwk.pro/privacy";
46
+
47
+ /** @type {string} */
43
48
  const gpcLink = "https://globalprivacycontrol.org/";
44
49
 
45
- /**
46
- * Table of Contents Links
47
- * @type {{ id: string, text: string }[]}
48
- */
50
+ /** @type {{ id: string, text: string }[]} */
49
51
  const tocLinks = [
50
52
  { id: "intro", text: "Introduction" },
51
53
  { id: "collect", text: "Information We Collect" },
@@ -67,21 +69,41 @@ This file is part of Network Pro.
67
69
  /** @type {string} */
68
70
  const classSmall = "small-text";
69
71
 
72
+ /** @type {boolean} */
70
73
  let optedOut = false;
74
+
75
+ /** @type {boolean} */
71
76
  let optedIn = false;
72
- let trackingStatus = "";
73
77
 
74
- onMount(() => {
75
- const prefs = getTrackingPreferences();
78
+ /**
79
+ * Refreshes tracking preferences state and updates the reactive store.
80
+ * This function uses dynamic import to avoid SSR evaluation of browser-only code.
81
+ *
82
+ * @returns {Promise<void>}
83
+ */
84
+ async function refreshTrackingStatus() {
85
+ /** @type {typeof import("$lib/utils/trackingStatus.js")} */
86
+ const tracking = await import("$lib/utils/trackingStatus.js");
87
+
88
+ const prefs = tracking.getTrackingPreferences();
76
89
  optedOut = prefs.optedOut;
77
90
  optedIn = prefs.optedIn;
78
- trackingStatus = prefs.status;
79
- console.log("[Tracking] Status:", trackingStatus);
91
+ trackingStatus.set(prefs.status);
92
+ }
93
+
94
+ /**
95
+ * Runs tracking preference detection on client mount.
96
+ */
97
+ onMount(() => {
98
+ refreshTrackingStatus();
99
+ console.log("[Tracking] Status:", $trackingStatus);
80
100
  });
81
101
 
82
102
  /**
83
- * Toggle tracking opt-out.
84
- * @param {boolean} value
103
+ * Toggles user tracking opt-out setting and updates cookies + store.
104
+ *
105
+ * @param {boolean} value - Whether the user is opting out
106
+ * @returns {void}
85
107
  */
86
108
  function toggleTracking(value) {
87
109
  optedOut = value;
@@ -92,11 +114,14 @@ This file is part of Network Pro.
92
114
  console.log("[Tracking] User cleared opt-out");
93
115
  clearTrackingPreferences();
94
116
  }
117
+ refreshTrackingStatus();
95
118
  }
96
119
 
97
120
  /**
98
- * Toggle tracking opt-in.
99
- * @param {boolean} value
121
+ * Toggles user tracking opt-in setting and updates cookies + store.
122
+ *
123
+ * @param {boolean} value - Whether the user is opting in
124
+ * @returns {void}
100
125
  */
101
126
  function toggleOptIn(value) {
102
127
  optedIn = value;
@@ -107,6 +132,7 @@ This file is part of Network Pro.
107
132
  console.log("[Tracking] User cleared opt-in");
108
133
  clearTrackingPreferences();
109
134
  }
135
+ refreshTrackingStatus();
110
136
  }
111
137
  </script>
112
138
 
@@ -241,10 +267,17 @@ This file is part of Network Pro.
241
267
  <div class="spacer"></div>
242
268
 
243
269
  <h3>Tracking Preferences</h3>
244
- <p id="tracking-status" aria-live="polite">
245
- <strong>Tracking Status:</strong>
246
- {trackingStatus}
247
- </p>
270
+ {#if $trackingStatus !== "⏳ Checking tracking preferences..."}
271
+ <p id="tracking-status" aria-live="polite">
272
+ <strong>Tracking Status:</strong>
273
+ {$trackingStatus}
274
+ </p>
275
+ {:else}
276
+ <p id="tracking-status" aria-live="polite">
277
+ <strong>Tracking Status:</strong>
278
+ <em>Loading…</em>
279
+ </p>
280
+ {/if}
248
281
 
249
282
  <!-- Opt-out checkbox -->
250
283
  <label>
@@ -405,7 +438,7 @@ This file is part of Network Pro.
405
438
  For questions, please utilize our <a
406
439
  rel={PAGE.REL}
407
440
  href={contactLink}
408
- target={PAGE.SELF}>Contact Form</a> or contact us directly:
441
+ target={PAGE.BLANK}>Contact Form</a> or contact us directly:
409
442
  </p>
410
443
  <p>
411
444
  <strong>{COMPANY_INFO.NAME}</strong><br />
@@ -9,52 +9,72 @@ This file is part of Network Pro.
9
9
  <script>
10
10
  import { base } from "$app/paths";
11
11
  import { onMount } from "svelte";
12
- import { getTrackingPreferences } from "$lib/utils/trackingStatus.js";
13
- /** @type {(type: 'enable' | 'disable') => void} */
12
+ import { trackingStatus } from "$lib/stores/trackingStatus.js";
14
13
  import {
14
+ /** @type {(type: 'enable' | 'disable') => void} */
15
15
  setTrackingPreference,
16
+ /** @type {() => void} */
16
17
  clearTrackingPreferences,
17
18
  } from "$lib/utils/trackingCookies.js";
18
19
  import { CONSTANTS } from "$lib";
19
20
 
20
- // Log the base path to verify its value
21
- //console.log("Base path:", base);
22
-
23
21
  console.log(CONSTANTS.COMPANY_INFO.APP_NAME);
24
22
 
25
- const { CONTACT, PAGE, NAV } = CONSTANTS;
23
+ /** @type {typeof CONSTANTS.CONTACT} */
24
+ const CONTACT = CONSTANTS.CONTACT;
26
25
 
27
- /**
28
- * @type {string}
29
- * Style class for the div element.
30
- */
26
+ /** @type {typeof CONSTANTS.PAGE} */
27
+ const PAGE = CONSTANTS.PAGE;
28
+
29
+ /** @type {typeof CONSTANTS.NAV} */
30
+ const NAV = CONSTANTS.NAV;
31
+
32
+ /** @type {string} */
31
33
  const spaceStyle = "spacer";
32
34
 
33
- /**
34
- * URL to the full Privacy Policy using the base path
35
- * @type {string}
36
- */
35
+ /** @type {string} */
37
36
  const privacyPolicy = `${base}/privacy`;
37
+
38
+ /** @type {string} */
38
39
  const prightsLink = `${base}/privacy-rights`;
39
40
 
40
41
  /** @type {string} */
41
42
  const classSmall = "small-text";
42
43
 
44
+ /** @type {boolean} */
43
45
  let optedOut = false;
46
+
47
+ /** @type {boolean} */
44
48
  let optedIn = false;
45
- let trackingStatus = "";
46
49
 
47
- onMount(() => {
48
- const prefs = getTrackingPreferences();
50
+ /**
51
+ * Refreshes tracking preferences state and updates the reactive store.
52
+ * Uses dynamic import to prevent SSR from loading browser-only dependencies.
53
+ *
54
+ * @returns {Promise<void>}
55
+ */
56
+ async function refreshTrackingStatus() {
57
+ /** @type {typeof import("$lib/utils/trackingStatus.js")} */
58
+ const tracking = await import("$lib/utils/trackingStatus.js");
59
+
60
+ const prefs = tracking.getTrackingPreferences();
49
61
  optedOut = prefs.optedOut;
50
62
  optedIn = prefs.optedIn;
51
- trackingStatus = prefs.status;
52
- console.log("[Tracking] Status:", trackingStatus);
63
+ trackingStatus.set(prefs.status);
64
+ }
65
+
66
+ /**
67
+ * Initializes tracking state on component mount (runs only in browser).
68
+ */
69
+ onMount(() => {
70
+ refreshTrackingStatus();
53
71
  });
54
72
 
55
73
  /**
56
- * Toggle tracking opt-out.
57
- * @param {boolean} value
74
+ * Toggles opt-out tracking state, updates cookie and tracking store.
75
+ *
76
+ * @param {boolean} value - Whether the user is opting out
77
+ * @returns {void}
58
78
  */
59
79
  function toggleTracking(value) {
60
80
  optedOut = value;
@@ -65,11 +85,14 @@ This file is part of Network Pro.
65
85
  console.log("[Tracking] User cleared opt-out");
66
86
  clearTrackingPreferences();
67
87
  }
88
+ refreshTrackingStatus();
68
89
  }
69
90
 
70
91
  /**
71
- * Toggle tracking opt-in.
72
- * @param {boolean} value
92
+ * Toggles opt-in tracking state, updates cookie and tracking store.
93
+ *
94
+ * @param {boolean} value - Whether the user is opting in
95
+ * @returns {void}
73
96
  */
74
97
  function toggleOptIn(value) {
75
98
  optedIn = value;
@@ -80,6 +103,7 @@ This file is part of Network Pro.
80
103
  console.log("[Tracking] User cleared opt-in");
81
104
  clearTrackingPreferences();
82
105
  }
106
+ refreshTrackingStatus();
83
107
  }
84
108
  </script>
85
109
 
@@ -143,10 +167,17 @@ This file is part of Network Pro.
143
167
 
144
168
  &nbsp;
145
169
 
146
- <p id="tracking-status" aria-live="polite">
147
- <strong>Tracking Status:</strong>
148
- {trackingStatus}
149
- </p>
170
+ {#if $trackingStatus !== "⏳ Checking tracking preferences..."}
171
+ <p id="tracking-status" aria-live="polite">
172
+ <strong>Tracking Status:</strong>
173
+ {$trackingStatus}
174
+ </p>
175
+ {:else}
176
+ <p id="tracking-status" aria-live="polite">
177
+ <strong>Tracking Status:</strong>
178
+ <em>Loading…</em>
179
+ </p>
180
+ {/if}
150
181
 
151
182
  <!-- Opt-out checkbox -->
152
183
  <label>
@@ -0,0 +1,115 @@
1
+ /* ==========================================================================
2
+ src/lib/stores/posthog.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
+ ========================================================================== */
7
+
8
+ /**
9
+ * @file posthog.js
10
+ * @description Privacy-aware PostHog tracking store with reactive state and safe API surface.
11
+ * @module src/lib/stores
12
+ */
13
+
14
+ import {
15
+ shouldRemindUserToReconsent,
16
+ shouldTrackUser,
17
+ } from "$lib/utils/privacy.js";
18
+ import { get, writable } from "svelte/store";
19
+
20
+ /**
21
+ * Tracks whether tracking is allowed based on cookies, DNT/GPC, and user preference.
22
+ * @type {import("svelte/store").Writable<boolean>}
23
+ */
24
+ export const trackingEnabled = writable(false);
25
+
26
+ /**
27
+ * Determines if the user should be reminded to re-consent (after 6 months).
28
+ * @type {import("svelte/store").Writable<boolean>}
29
+ */
30
+ export const showReminder = writable(false);
31
+
32
+ /** @type {boolean} Internal one-time init guard */
33
+ let initialized = false;
34
+
35
+ /** @type {import("posthog-js").PostHog | null} Loaded PostHog instance */
36
+ let ph = null;
37
+
38
+ /**
39
+ * Initializes the PostHog analytics client if tracking is permitted.
40
+ * Uses dynamic import to avoid SSR failures.
41
+ * @returns {Promise<void>}
42
+ */
43
+ export async function initPostHog() {
44
+ if (initialized || typeof window === "undefined") return;
45
+ initialized = true;
46
+
47
+ const allowTracking = shouldTrackUser();
48
+ trackingEnabled.set(allowTracking);
49
+ showReminder.set(shouldRemindUserToReconsent());
50
+
51
+ if (!allowTracking) {
52
+ console.log("[PostHog] Tracking is disabled — skipping init.");
53
+ return;
54
+ }
55
+
56
+ const posthogModule = await import("posthog-js");
57
+ ph = posthogModule.default;
58
+
59
+ // cspell:disable-next-line
60
+ ph.init("phc_Qshfo6AXzh4pS7aPigfqyeo4qj1qlyh7gDuHDeVMSR0", {
61
+ api_host: "https://us.i.posthog.com",
62
+ autocapture: true,
63
+ capture_pageview: false,
64
+ person_profiles: "identified_only",
65
+ loaded: (phInstance) => {
66
+ if (!allowTracking) {
67
+ console.log(
68
+ "[PostHog] ⛔ User opted out — calling opt_out_capturing()",
69
+ );
70
+ phInstance.opt_out_capturing();
71
+ } else {
72
+ console.log("[PostHog] ✅ Tracking enabled");
73
+ }
74
+ },
75
+ });
76
+
77
+ ph.capture("$pageview");
78
+ }
79
+
80
+ /**
81
+ * Conditionally captures an event if tracking is enabled.
82
+ * @param {string} event - The event name to track
83
+ * @param {Record<string, any>} [properties={}] - Optional event properties
84
+ */
85
+ export function capture(event, properties = {}) {
86
+ if (ph !== null && get(trackingEnabled)) {
87
+ ph.capture(event, properties);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Conditionally identifies a user if tracking is enabled.
93
+ * @param {string} id - Unique user identifier
94
+ * @param {Record<string, any>} [properties={}] - Optional user traits
95
+ */
96
+ export function identify(id, properties = {}) {
97
+ if (ph !== null && get(trackingEnabled)) {
98
+ ph.identify(id, properties);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * For test cleanup only — resets internal state.
104
+ * No-op in production.
105
+ * @returns {void}
106
+ */
107
+ export function _resetPostHog() {
108
+ if (import.meta.env.MODE === "production") {
109
+ console.warn("[PostHog] _resetPostHog() called in production — no-op");
110
+ return;
111
+ }
112
+
113
+ initialized = false;
114
+ ph = null;
115
+ }
@@ -0,0 +1,30 @@
1
+ /* ==========================================================================
2
+ src/lib/stores/trackingStatus.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
+ ========================================================================== */
7
+
8
+ /**
9
+ * @file trackingStatus.js
10
+ * @description Tracks state of PostHog tracking status for instant updates
11
+ * in Privacy Policy and Privacy Dashboard.
12
+ * @module src/lib/stores
13
+ */
14
+
15
+ import { writable } from "svelte/store";
16
+
17
+ /**
18
+ * Writable tracking status store.
19
+ * Initialized with fallback value and updated in browser context.
20
+ * @type {import("svelte/store").Writable<string>}
21
+ */
22
+ export const trackingStatus = writable("⏳ Checking tracking preferences...");
23
+
24
+ // Dynamically import browser-only logic after mount
25
+ if (typeof window !== "undefined") {
26
+ import("$lib/utils/trackingStatus.js").then(({ getTrackingPreferences }) => {
27
+ const prefs = getTrackingPreferences();
28
+ trackingStatus.set(prefs.status);
29
+ });
30
+ }
@@ -0,0 +1,60 @@
1
+ /* ==========================================================================
2
+ src/lib/types/appConstants.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
+ * @file appConstants.js
11
+ * @description Type definitions for app constants in src/lib/index.js
12
+ * @module src/lib/types
13
+ * @author SunDevil311
14
+ * @updated 2025-06-03
15
+ */
16
+
17
+ /**
18
+ * @typedef {object} CompanyInfo
19
+ * @property {string} NAME - Full company name
20
+ * @property {string} APP_NAME - Application name
21
+ * @property {string} YEAR - Current copyright year
22
+ */
23
+
24
+ /**
25
+ * @typedef {object} ContactInfo
26
+ * @property {string} EMAIL - Primary contact email
27
+ * @property {string} SECURE - Secure contact email
28
+ * @property {string} PRIVACY - Privacy policy email
29
+ * @property {string} PHONE - Support phone number
30
+ */
31
+
32
+ /**
33
+ * @typedef {object} PageTargets
34
+ * @property {string} BLANK - Value for `target="_blank"`
35
+ * @property {string} SELF - Value for `target="_self"`
36
+ * @property {string} REL - Value for `rel="noopener noreferrer"`
37
+ */
38
+
39
+ /**
40
+ * @typedef {object} NavigationLabels
41
+ * @property {string} BACKTOP
42
+ * @property {string} HREFTOP
43
+ */
44
+
45
+ /**
46
+ * @typedef {object} Links
47
+ * @property {string} HOME - Main website URL
48
+ * @property {string} BLOG - External blog URL
49
+ */
50
+
51
+ /**
52
+ * @typedef {object} AppConstants
53
+ * @property {CompanyInfo} COMPANY_INFO
54
+ * @property {ContactInfo} CONTACT
55
+ * @property {PageTargets} PAGE
56
+ * @property {NavigationLabels} NAV
57
+ * @property {Links} LINKS
58
+ */
59
+
60
+ export {};
@@ -1,3 +1,20 @@
1
+ /* ==========================================================================
2
+ src/lib/types/fossTypes.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
+ * @file fossTypes.js
11
+ * @description Type definitions for `fossItem` in
12
+ * src/lib/components/foss/FossItemContent.svelte
13
+ * @module src/lib/types
14
+ * @author SunDevil311
15
+ * @updated 2025-06-03
16
+ */
17
+
1
18
  /**
2
19
  * @typedef {object} FossLink
3
20
  * @property {string} [label]
@@ -11,28 +11,71 @@ This file is part of Network Pro.
11
11
  * @description Determines whether the user allows tracking based on DNT, GPC, or manual opt-out.
12
12
  * @module src/lib/utils/
13
13
  * @author SunDevil311
14
- * @updated 2025-05-28
14
+ * @updated 2025-06-03
15
15
  */
16
16
 
17
17
  /**
18
+ * Check if user has manually set tracking preference.
19
+ * @returns {boolean}
20
+ */
21
+ export function hasUserManuallySetTrackingPreference() {
22
+ if (typeof document === "undefined") return false;
23
+
24
+ const cookies = document.cookie;
25
+ return (
26
+ cookies.includes("enable_tracking=true") ||
27
+ cookies.includes("disable_tracking=true")
28
+ );
29
+ }
30
+
31
+ /**
32
+ * Determine if the user allows tracking based on cookies, DNT, and GPC.
18
33
  * @returns {boolean}
19
34
  */
20
35
  export function shouldTrackUser() {
36
+ if (
37
+ typeof window === "undefined" ||
38
+ typeof navigator === "undefined" ||
39
+ typeof document === "undefined"
40
+ ) {
41
+ return false;
42
+ }
43
+
44
+ const cookies = document.cookie;
21
45
  const windowDNT = /** @type {any} */ (window).doNotTrack;
22
46
  const navigatorGPC = /** @type {any} */ (navigator).globalPrivacyControl;
23
47
 
24
48
  const dnt = navigator.doNotTrack === "1" || windowDNT === "1";
25
49
  const gpc = navigatorGPC === true;
26
50
 
27
- const manualOptOut = document.cookie.includes("disable_tracking=true");
28
- const manualOptIn = document.cookie.includes("enable_tracking=true");
51
+ const manualOptOut = cookies.includes("disable_tracking=true");
52
+ const manualOptIn = cookies.includes("enable_tracking=true");
29
53
 
30
54
  console.log("[Privacy] Opt-in cookie present:", manualOptIn);
31
55
  console.log("[Privacy] Opt-out cookie present:", manualOptOut);
32
56
 
33
- // Opt-in overrides everything; opt-out disables regardless of DNT/GPC
34
57
  if (manualOptIn) return true;
35
58
  if (manualOptOut) return false;
36
59
 
37
60
  return !dnt && !gpc;
38
61
  }
62
+
63
+ /**
64
+ * Determines if user should be reminded to reconsent (after 6 months).
65
+ * @returns {boolean}
66
+ */
67
+ export function shouldRemindUserToReconsent() {
68
+ if (typeof document === "undefined") return false;
69
+
70
+ if (!hasUserManuallySetTrackingPreference()) return false;
71
+
72
+ const match = document.cookie.match(/tracking_consent_timestamp=(\d+)/);
73
+ if (!match) return true;
74
+
75
+ const timestamp = Number(match[1]);
76
+ if (isNaN(timestamp)) return true;
77
+
78
+ const age = Date.now() - timestamp;
79
+
80
+ return age > 1000 * 60 * 60 * 24 * 180; // 6 months
81
+ }
@@ -11,30 +11,60 @@ This file is part of Network Pro.
11
11
  * @description Handles setting, clearing, and toggling tracking preference cookies.
12
12
  * @module src/lib/utils/
13
13
  * @author SunDevil311
14
- * @updated 2025-05-28
14
+ * @updated 2025-06-04
15
15
  */
16
16
 
17
+ // 6 months (in seconds). Will be centralized later.
18
+ const DEFAULT_COOKIE_MAX_AGE = 60 * 60 * 24 * 180;
19
+
20
+ /**
21
+ * Builds a standard cookie string for use in all tracking cookies.
22
+ * @param {number} maxAge
23
+ * @returns {string}
24
+ */
25
+ function buildCookieSettings(maxAge) {
26
+ return `path=/; max-age=${maxAge}; expires=${new Date(Date.now() + maxAge * 1000).toUTCString()}; SameSite=Lax; Secure`;
27
+ }
28
+
17
29
  /**
18
- * Set a tracking preference cookie.
30
+ * Sets tracking preference cookies based on type.
19
31
  * @param {"enable" | "disable"} type
32
+ * @param {number} [maxAge=DEFAULT_COOKIE_MAX_AGE]
20
33
  */
21
- export function setTrackingPreference(type) {
22
- const maxAge = 60 * 60 * 24 * 365 * 10; // 10 years
23
- const cookieSettings = `path=/; max-age=${maxAge}; SameSite=Lax`;
34
+ export function setTrackingPreference(type, maxAge = DEFAULT_COOKIE_MAX_AGE) {
35
+ if (typeof document === "undefined") return; // SSR guard
36
+
37
+ const cookieSettings = buildCookieSettings(maxAge);
38
+ const now = Date.now();
24
39
 
25
40
  if (type === "enable") {
26
41
  document.cookie = `enable_tracking=true; ${cookieSettings}`;
27
- document.cookie = `disable_tracking=; path=/; max-age=0; SameSite=Lax`;
42
+ document.cookie = `tracking_consent_timestamp=${now}; ${cookieSettings}`;
43
+ clearCookie("disable_tracking");
28
44
  } else if (type === "disable") {
29
45
  document.cookie = `disable_tracking=true; ${cookieSettings}`;
30
- document.cookie = `enable_tracking=; path=/; max-age=0; SameSite=Lax`;
46
+ document.cookie = `tracking_consent_timestamp=${now}; ${cookieSettings}`;
47
+ clearCookie("enable_tracking");
31
48
  }
32
49
  }
33
50
 
34
51
  /**
35
- * Clear both tracking cookies.
52
+ * Clears all tracking-related cookies.
36
53
  */
37
54
  export function clearTrackingPreferences() {
38
- document.cookie = `enable_tracking=; path=/; max-age=0; SameSite=Lax`;
39
- document.cookie = `disable_tracking=; path=/; max-age=0; SameSite=Lax`;
55
+ if (typeof document === "undefined") return; // SSR guard
56
+
57
+ clearCookie("enable_tracking");
58
+ clearCookie("disable_tracking");
59
+ clearCookie("tracking_consent_timestamp");
60
+ }
61
+
62
+ /**
63
+ * Clears an individual cookie.
64
+ * @param {string} name
65
+ */
66
+ function clearCookie(name) {
67
+ if (typeof document === "undefined") return; // SSR guard
68
+
69
+ document.cookie = `${name}=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax; Secure`;
40
70
  }
@@ -9,12 +9,16 @@ This file is part of Network Pro.
9
9
  /**
10
10
  * @file trackingStatus.js
11
11
  * @description Get tracking preferences based on cookies and browser privacy signals.
12
- * @module src/lib/utils/
12
+ * @module src/lib/utils
13
13
  * @author SunDevil311
14
14
  * @updated 2025-05-28
15
15
  */
16
16
 
17
+ import { browser } from "$app/environment";
18
+
17
19
  /**
20
+ * Gets the current tracking preferences based on browser cookies and signals.
21
+ *
18
22
  * @returns {{
19
23
  * optedOut: boolean,
20
24
  * optedIn: boolean,
@@ -24,12 +28,23 @@ This file is part of Network Pro.
24
28
  * }}
25
29
  */
26
30
  export function getTrackingPreferences() {
31
+ // Prevent errors during SSR (no document or navigator)
32
+ if (!browser) {
33
+ return {
34
+ optedOut: false,
35
+ optedIn: false,
36
+ dnt: false,
37
+ gpc: false,
38
+ status: "⏳ Checking tracking preferences...",
39
+ };
40
+ }
41
+
27
42
  const cookies = document.cookie;
28
43
  const optedOut = cookies.includes("disable_tracking=true");
29
44
  const optedIn = cookies.includes("enable_tracking=true");
30
- const dnt = navigator.doNotTrack === "1";
31
45
 
32
- // @ts-expect-error: 'globalPrivacyControl' is a non-standard property
46
+ const dnt = navigator.doNotTrack === "1";
47
+ // @ts-expect-error: 'globalPrivacyControl' is non-standard
33
48
  const gpc = navigator.globalPrivacyControl === true;
34
49
 
35
50
  let status = "⚙️ Using default settings (tracking enabled)";
@@ -42,5 +42,3 @@ export function load({ url }) {
42
42
  meta: currentMeta, // Return the meta data (either from the route or the fallback)
43
43
  };
44
44
  }
45
-
46
- // cspell:ignore posthog
@@ -9,61 +9,61 @@ This file is part of Network Pro.
9
9
  <script>
10
10
  export let data;
11
11
 
12
+ import { onMount } from "svelte";
13
+ import { afterNavigate } from "$app/navigation";
14
+ import { initPostHog, showReminder, capture } from "$lib/stores/posthog";
15
+ import { registerServiceWorker } from "$lib/registerServiceWorker.js";
16
+ import { browser } from "$app/environment";
17
+ import { shouldTrackUser } from "$lib/utils/privacy.js";
18
+
12
19
  import ContainerSection from "$lib/components/ContainerSection.svelte";
13
20
  import Footer from "$lib/components/layout/Footer.svelte";
14
21
  import HeaderDefault from "$lib/components/layout/HeaderDefault.svelte";
15
22
  import HeaderHome from "$lib/components/layout/HeaderHome.svelte";
16
23
  import PWAInstallButton from "$lib/components/PWAInstallButton.svelte";
17
- import { shouldTrackUser } from "$lib/utils/privacy.js";
18
- import { onMount } from "svelte";
19
- import { registerServiceWorker } from "$lib/registerServiceWorker.js";
20
- import { browser } from "$app/environment";
24
+
21
25
  import "$lib/styles/global.min.css";
22
26
  import "$lib/styles/fa-global.css";
23
27
 
24
- // Import favicon images
25
28
  import logoPng from "$lib/img/logo-web.png";
26
29
  import logoWbp from "$lib/img/logo-web.webp";
27
30
  import faviconSvg from "$lib/img/favicon.svg";
28
31
  import appleTouchIcon from "$lib/img/icon-180x180.png";
29
32
 
30
- // Declare PostHog as null initially
31
- /** @type {typeof import('$lib/components/PostHog.svelte').default | null} */
32
- let PostHog = null;
33
+ $: shouldShowReminder = $showReminder;
33
34
 
34
- if (browser) {
35
- // Preload all core images (logos + apple touch)
36
- [logoPng, logoWbp, appleTouchIcon].forEach((src) => {
37
- const img = new Image();
38
- img.src = src;
39
- });
35
+ onMount(() => {
36
+ console.log("[APP] onMount triggered in +layout.svelte");
40
37
 
41
- // Run setup when component mounts (only in browser)
42
- onMount(() => {
43
- console.log("[APP] onMount triggered in +layout.svelte");
44
- registerServiceWorker();
38
+ registerServiceWorker();
39
+ initPostHog();
40
+
41
+ // Register navigation tracking only on client
42
+ afterNavigate(() => {
43
+ capture("$pageview");
44
+ });
45
45
 
46
+ if (browser) {
46
47
  const isDev = import.meta.env.MODE === "development";
47
48
 
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
- );
49
+ // Check for ?debug=true in URL (no persistence)
50
+ const urlParams = new URLSearchParams(window.location.search);
51
+ const debug = urlParams.get("debug") === "true";
52
+
53
+ if (isDev || debug) {
54
+ console.log("ENV MODE =", import.meta.env.MODE);
55
+ console.log("isDev =", isDev);
56
+ console.log("debug param =", debug);
57
+ console.log("shouldTrackUser =", shouldTrackUser()); // Now called statically
64
58
  }
65
- });
66
- }
59
+
60
+ // Preload logo assets
61
+ [logoPng, logoWbp, appleTouchIcon].forEach((src) => {
62
+ const img = new Image();
63
+ img.src = src;
64
+ });
65
+ }
66
+ });
67
67
 
68
68
  // fallback values if data.meta not set
69
69
  const metaTitle =
@@ -103,10 +103,6 @@ This file is part of Network Pro.
103
103
  </header>
104
104
  <!-- END HEADER -->
105
105
 
106
- {#if PostHog}
107
- <PostHog /> <!-- Add PostHog component when it's loaded -->
108
- {/if}
109
-
110
106
  <main>
111
107
  <slot />
112
108
  </main>
@@ -1,7 +1,6 @@
1
1
  /*! ==========================================================================
2
- src/lib/styles/css/offline.css
3
-
2
+ Copyright © 2025 Network Pro Strategies (Network Pro™)
4
3
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
4
  This file is part of Network Pro.
6
5
  =========================================================================== */
7
- html,body{height:100%;margin:0;padding:0}body{color:#fafafa;text-align:center;background-color:#191919;flex-direction:column;justify-content:center;align-items:center;margin:10px;padding:0 1rem;font-family:Arial,Helvetica,sans-serif;display:flex}.container{max-width:600px;margin:0 auto}h1{color:#fafafa;margin-bottom:1rem;font-size:2rem}p{margin-bottom:1.5rem;font-size:1.1rem;line-height:1.5}.icon{color:#ff5252;margin-bottom:1rem;font-size:4rem}.retry-button{color:#fafafa;cursor:pointer;background-color:#2e7d32;border:none;border-radius:4px;margin-top:1rem;padding:12px 24px;font-family:Arial,Helvetica,sans-serif;font-size:1rem;transition:background-color .2s}.retry-button:hover{background-color:#388e3c}.status{color:#bdbdbd;background-color:#ffffff0d;border-radius:4px;margin:1rem 0;padding:1rem;font-size:.9rem}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}.help-text{color:#bdbdbd;margin-top:2rem;font-size:.9rem}
6
+ html,body{height:100%;margin:0;padding:0}body{color:#fafafa;text-align:center;background-color:#191919;flex-direction:column;justify-content:center;align-items:center;margin:10px;padding:0 1rem;font-family:Arial,Helvetica,sans-serif;display:flex}.container{max-width:600px;margin:0 auto}h1{color:#fafafa;margin-bottom:1rem;font-size:2rem}p{margin-bottom:1.5rem;font-size:1.1rem;line-height:1.5}.icon{color:#ff5252;margin-bottom:1rem;font-size:4rem}.retry-button{color:#fafafa;cursor:pointer;background-color:#2e7d32;border:none;border-radius:4px;margin-top:1rem;padding:12px 24px;font-family:Arial,Helvetica,sans-serif;font-size:1rem;transition:background-color .2s}.retry-button:hover{background-color:#388e3c}.status{color:#bdbdbd;background-color:#ffffff0d;border-radius:4px;margin:1rem 0;padding:1rem;font-size:.9rem}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}.help-text{color:#bdbdbd;margin-top:2rem;font-size:.9rem}
package/svelte.config.js CHANGED
@@ -23,13 +23,15 @@ const config = {
23
23
  kit: {
24
24
  // Netlify adapter configuration
25
25
  adapter: adapter({
26
- edge: false, // Disable edge functions (optional, enable if needed)
27
- split: false, // Disable splitting function files (optional, enable if needed),
26
+ edge: false, // Disable edge functions for site
27
+ split: false, // Disable splitting function files (optional, enable if needed)
28
28
  }),
29
+
29
30
  // Paths configuration for deployment
30
31
  paths: {
31
32
  base: "", // Always deploy to the root of the domain
32
33
  },
34
+
33
35
  prerender: {
34
36
  // Handle HTTP errors during prerendering
35
37
  handleHttpError: ({ path, _referrer, message }) => {
@@ -46,6 +48,7 @@ const config = {
46
48
  },
47
49
  },
48
50
  },
51
+
49
52
  // File extensions for Svelte and mdsvex
50
53
  extensions: [".svelte", ".svx", ".md"], // Added .md for Markdown support
51
54
  };
package/vite.config.js CHANGED
@@ -9,19 +9,17 @@ This file is part of Network Pro.
9
9
  import { sveltekit } from "@sveltejs/kit/vite";
10
10
  import { defineConfig } from "vite";
11
11
  import lightningcssPlugin from "vite-plugin-lightningcss";
12
+ import tsconfigPaths from "vite-tsconfig-paths"; // NEW: tsconfig/jsconfig alias support
12
13
 
13
14
  export default defineConfig({
14
15
  plugins: [
16
+ tsconfigPaths(), // Insert before sveltekit()
15
17
  sveltekit(),
16
18
  lightningcssPlugin({
17
19
  minify: process.env.NODE_ENV === "production",
18
20
  pruneUnusedFontFaceRules: true,
19
21
  pruneUnusedKeyframes: true,
20
22
  removeUnusedFontFaces: true,
21
- // Enables nesting support in Lightning CSS
22
- //drafts: {
23
- // nesting: true
24
- //}
25
23
  }),
26
24
  ],
27
25
  });
@@ -1,36 +0,0 @@
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
- import { shouldTrackUser } from "$lib/utils/privacy.js";
14
-
15
- onMount(() => {
16
- if (!browser) return;
17
-
18
- const allowTracking = shouldTrackUser();
19
-
20
- // cspell:disable-next-line
21
- posthog.init("phc_Qshfo6AXzh4pS7aPigfqyeo4qj1qlyh7gDuHDeVMSR0", {
22
- api_host: "https://us.i.posthog.com",
23
- loaded: (ph) => {
24
- if (!allowTracking) {
25
- console.log("[PostHog] ⛔ User opted out — disabling tracking");
26
- ph.opt_out_capturing(); // Fully disable any tracking
27
- } else {
28
- console.log("[PostHog] ✅ Tracking enabled");
29
- }
30
- },
31
- autocapture: allowTracking, // Optional: Disable autocapture
32
- capture_pageview: allowTracking, // Optional: Disable initial pageview
33
- person_profiles: "identified_only",
34
- });
35
- });
36
- </script>