@networkpro/web 1.10.1 → 1.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/CHANGELOG.template.md +63 -0
- package/LICENSE.md +19 -8
- package/cspell.json +3 -0
- package/jsconfig.json +1 -15
- package/jsconfig.template.jsonc +32 -0
- package/netlify.toml +0 -1
- package/package.json +9 -9
- package/src/app.html +1 -1
- package/src/hooks.server.js +13 -0
- package/src/lib/components/Badges.svelte +6 -2
- package/src/lib/components/LegalNav.svelte +6 -1
- package/src/lib/components/MetaTags.svelte +8 -2
- package/src/lib/components/layout/Footer.svelte +3 -4
- package/src/lib/index.js +7 -33
- package/src/lib/pages/LicenseContent.svelte +22 -3
- package/src/lib/pages/PrivacyContent.svelte +81 -74
- package/src/lib/pages/PrivacyDashboard.svelte +78 -70
- package/src/lib/stores/posthog.js +115 -0
- package/src/lib/stores/trackingPreferences.js +222 -0
- package/src/lib/styles/css/default.css +12 -0
- package/src/lib/styles/global.min.css +1 -1
- package/src/lib/types/appConstants.js +60 -0
- package/src/lib/types/fossTypes.js +17 -0
- package/src/routes/+layout.js +0 -2
- package/src/routes/+layout.svelte +34 -40
- package/static/docs/tracking.md +63 -0
- package/static/img/fb-banner.png +0 -0
- package/static/offline.min.css +2 -3
- package/svelte.config.js +4 -1
- package/vite.config.js +2 -4
- package/CODE_OF_CONDUCT.md +0 -173
- package/src/lib/components/PostHog.svelte +0 -36
- package/src/lib/utils/privacy.js +0 -38
- package/src/lib/utils/trackingCookies.js +0 -40
- package/src/lib/utils/trackingStatus.js +0 -46
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
src/lib/stores/trackingPreferences.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
|
+
* NOTE: Legacy logic from `trackingCookies.js` was merged here in June 2025.
|
|
11
|
+
* That file has been removed to consolidate stateful tracking logic into a
|
|
12
|
+
* reactive store with SSR compatibility.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @file trackingPreferences.js
|
|
17
|
+
* @description Reactive store for tracking preferences derived from
|
|
18
|
+
* cookies and browser signals (DNT / GPC). Safe for SSR.
|
|
19
|
+
* @module src/lib/stores
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { browser } from "$app/environment";
|
|
23
|
+
import { derived, writable } from "svelte/store";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {object} TrackingState
|
|
27
|
+
* @property {boolean} optedIn - User explicitly opted in via cookie
|
|
28
|
+
* @property {boolean} optedOut - User explicitly opted out via cookie
|
|
29
|
+
* @property {boolean} dnt - Do Not Track browser signal
|
|
30
|
+
* @property {boolean} gpc - Global Privacy Control browser signal
|
|
31
|
+
* @property {boolean} enabled - Whether tracking is permitted
|
|
32
|
+
* @property {string} status - Human-readable description of tracking state
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @returns {string} Raw document.cookie or empty string (SSR-safe)
|
|
37
|
+
*/
|
|
38
|
+
function readCookies() {
|
|
39
|
+
return browser ? document.cookie || "" : "";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if a specific cookie exists
|
|
44
|
+
* @param {string} name
|
|
45
|
+
* @returns {boolean}
|
|
46
|
+
*/
|
|
47
|
+
function cookieExists(name) {
|
|
48
|
+
return readCookies().includes(`${name}=true`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Set a cookie with boolean true, 1-year duration
|
|
53
|
+
* @param {string} name
|
|
54
|
+
* @returns {void}
|
|
55
|
+
*/
|
|
56
|
+
function setCookie(name) {
|
|
57
|
+
if (browser) {
|
|
58
|
+
document.cookie = `${name}=true; path=/; max-age=31536000; samesite=lax`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Remove a cookie by setting zero max-age
|
|
64
|
+
* @param {string} name
|
|
65
|
+
* @returns {void}
|
|
66
|
+
*/
|
|
67
|
+
function removeCookie(name) {
|
|
68
|
+
if (browser) {
|
|
69
|
+
document.cookie = `${name}=; Max-Age=0; path=/; samesite=lax`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Write tracking_consent_timestamp cookie
|
|
75
|
+
* @returns {void}
|
|
76
|
+
*/
|
|
77
|
+
function setConsentTimestamp() {
|
|
78
|
+
if (browser) {
|
|
79
|
+
document.cookie = `tracking_consent_timestamp=${Date.now()}; path=/; max-age=31536000; samesite=lax`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Remove tracking_consent_timestamp cookie
|
|
85
|
+
* @returns {void}
|
|
86
|
+
*/
|
|
87
|
+
function removeConsentTimestamp() {
|
|
88
|
+
if (browser) {
|
|
89
|
+
document.cookie = `tracking_consent_timestamp=; Max-Age=0; path=/; samesite=lax`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @returns {{ dnt: boolean, gpc: boolean }}
|
|
95
|
+
*/
|
|
96
|
+
function getPrivacySignals() {
|
|
97
|
+
if (!browser) return { dnt: false, gpc: false };
|
|
98
|
+
|
|
99
|
+
const dnt = navigator.doNotTrack === "1";
|
|
100
|
+
// @ts-expect-error: Non-standard GPC property
|
|
101
|
+
const gpc = navigator.globalPrivacyControl === true;
|
|
102
|
+
|
|
103
|
+
return { dnt, gpc };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {object} args
|
|
108
|
+
* @param {boolean} args.optedOut
|
|
109
|
+
* @param {boolean} args.optedIn
|
|
110
|
+
* @param {boolean} args.dnt
|
|
111
|
+
* @param {boolean} args.gpc
|
|
112
|
+
* @returns {string}
|
|
113
|
+
*/
|
|
114
|
+
function deriveStatus({ optedOut, optedIn, dnt, gpc }) {
|
|
115
|
+
if (optedOut) return "🔒 Tracking disabled (manual opt-out)";
|
|
116
|
+
if (optedIn) return "✅ Tracking enabled (manual opt-in)";
|
|
117
|
+
if (dnt || gpc) return "🛑 Tracking disabled (via browser signal)";
|
|
118
|
+
return "⚙️ Using default settings (tracking enabled)";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @returns {TrackingState}
|
|
123
|
+
*/
|
|
124
|
+
function computePreferences() {
|
|
125
|
+
const optedOut = cookieExists("disable_tracking");
|
|
126
|
+
const optedIn = cookieExists("enable_tracking");
|
|
127
|
+
const { dnt, gpc } = getPrivacySignals();
|
|
128
|
+
|
|
129
|
+
const enabled = optedIn || (!optedOut && !dnt && !gpc);
|
|
130
|
+
const status = deriveStatus({ optedOut, optedIn, dnt, gpc });
|
|
131
|
+
|
|
132
|
+
return { optedIn, optedOut, dnt, gpc, enabled, status };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// --- Writable store ---
|
|
136
|
+
/** @type {import('svelte/store').Writable<TrackingState>} */
|
|
137
|
+
export const trackingPreferences = writable(
|
|
138
|
+
browser
|
|
139
|
+
? computePreferences()
|
|
140
|
+
: {
|
|
141
|
+
optedIn: false,
|
|
142
|
+
optedOut: false,
|
|
143
|
+
dnt: false,
|
|
144
|
+
gpc: false,
|
|
145
|
+
enabled: false,
|
|
146
|
+
status: "⏳ Checking tracking preferences...",
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Returns true if the user manually set a tracking preference cookie.
|
|
152
|
+
* @returns {boolean}
|
|
153
|
+
*/
|
|
154
|
+
function hasUserManuallySetTrackingPreference() {
|
|
155
|
+
const cookies = readCookies();
|
|
156
|
+
return (
|
|
157
|
+
cookies.includes("enable_tracking=true") ||
|
|
158
|
+
cookies.includes("disable_tracking=true")
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Determines if user should be reminded to reconsent (after 6 months).
|
|
164
|
+
*
|
|
165
|
+
* @param {TrackingState} $prefs - The current tracking preferences.
|
|
166
|
+
* @returns {boolean}
|
|
167
|
+
* @type {import("svelte/store").Readable<boolean>}
|
|
168
|
+
*/
|
|
169
|
+
export const remindUserToReconsent = derived(trackingPreferences, (_prefs) => {
|
|
170
|
+
if (!browser) return false;
|
|
171
|
+
if (!hasUserManuallySetTrackingPreference()) return false;
|
|
172
|
+
|
|
173
|
+
const match = readCookies().match(/tracking_consent_timestamp=(\d+)/);
|
|
174
|
+
if (!match) return true;
|
|
175
|
+
|
|
176
|
+
const timestamp = Number(match[1]);
|
|
177
|
+
if (isNaN(timestamp)) return true;
|
|
178
|
+
|
|
179
|
+
const age = Date.now() - timestamp;
|
|
180
|
+
return age > 1000 * 60 * 60 * 24 * 180; // 6 months
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Force-refresh current preferences
|
|
185
|
+
* @returns {void}
|
|
186
|
+
*/
|
|
187
|
+
export function refreshTrackingPreferences() {
|
|
188
|
+
if (browser) trackingPreferences.set(computePreferences());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Enable tracking by setting opt-in cookie
|
|
193
|
+
* @returns {void}
|
|
194
|
+
*/
|
|
195
|
+
export function setOptIn() {
|
|
196
|
+
setCookie("enable_tracking");
|
|
197
|
+
removeCookie("disable_tracking");
|
|
198
|
+
setConsentTimestamp();
|
|
199
|
+
refreshTrackingPreferences();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Disable tracking by setting opt-out cookie
|
|
204
|
+
* @returns {void}
|
|
205
|
+
*/
|
|
206
|
+
export function setOptOut() {
|
|
207
|
+
setCookie("disable_tracking");
|
|
208
|
+
removeCookie("enable_tracking");
|
|
209
|
+
setConsentTimestamp();
|
|
210
|
+
refreshTrackingPreferences();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Clear all tracking preference cookies
|
|
215
|
+
* @returns {void}
|
|
216
|
+
*/
|
|
217
|
+
export function clearPrefs() {
|
|
218
|
+
removeCookie("enable_tracking");
|
|
219
|
+
removeCookie("disable_tracking");
|
|
220
|
+
removeConsentTimestamp();
|
|
221
|
+
refreshTrackingPreferences();
|
|
222
|
+
}
|
|
@@ -506,3 +506,15 @@ footer .container {
|
|
|
506
506
|
font-family: inherit; /* Ensure it uses the same font-family as normal text */
|
|
507
507
|
font-style: normal; /* Remove italic for the description */
|
|
508
508
|
}
|
|
509
|
+
|
|
510
|
+
.sr-only {
|
|
511
|
+
position: absolute;
|
|
512
|
+
width: 1px;
|
|
513
|
+
height: 1px;
|
|
514
|
+
padding: 0;
|
|
515
|
+
margin: -1px;
|
|
516
|
+
border: 0;
|
|
517
|
+
clip: rect(0, 0, 0, 0);
|
|
518
|
+
overflow: hidden;
|
|
519
|
+
white-space: nowrap;
|
|
520
|
+
}
|
|
@@ -3,4 +3,4 @@ Copyright © 2025 Network Pro Strategies (Network Pro™)
|
|
|
3
3
|
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
4
4
|
This file is part of Network Pro.
|
|
5
5
|
========================================================================== */
|
|
6
|
-
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}.readable{max-width:900px;margin:0 auto}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{text-align:center;border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav td,.bnav th{text-align:center;vertical-align:middle;word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;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}.bold{font-weight:700}.emphasis{font-style:italic}.uline{text-decoration:underline}.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}.gold{color:#ffc627}.visited{color:#7f6227}.goldseparator{color:#ffc627;margin:0 .5rem}.center-nav{text-align:center;padding:5px;font-size:1rem;line-height:1.5rem}.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:150px;height:150px}.spacer{margin:2rem 0}.separator{margin:0 .5rem}.emoji{margin-right:8px}.headline{margin-bottom:4px;font-style:italic;font-weight:700;display:block}.label{font-family:inherit;font-weight:700}.description{font-family:inherit;font-style:normal;font-weight:400;display:inline}
|
|
6
|
+
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}.readable{max-width:900px;margin:0 auto}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{text-align:center;border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav td,.bnav th{text-align:center;vertical-align:middle;word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;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}.bold{font-weight:700}.emphasis{font-style:italic}.uline{text-decoration:underline}.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}.gold{color:#ffc627}.visited{color:#7f6227}.goldseparator{color:#ffc627;margin:0 .5rem}.center-nav{text-align:center;padding:5px;font-size:1rem;line-height:1.5rem}.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:150px;height:150px}.spacer{margin:2rem 0}.separator{margin:0 .5rem}.emoji{margin-right:8px}.headline{margin-bottom:4px;font-style:italic;font-weight:700;display:block}.label{font-family:inherit;font-weight:700}.description{font-family:inherit;font-style:normal;font-weight:400;display:inline}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}
|
|
@@ -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]
|
package/src/routes/+layout.js
CHANGED
|
@@ -9,61 +9,59 @@ 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
|
+
|
|
12
18
|
import ContainerSection from "$lib/components/ContainerSection.svelte";
|
|
13
19
|
import Footer from "$lib/components/layout/Footer.svelte";
|
|
14
20
|
import HeaderDefault from "$lib/components/layout/HeaderDefault.svelte";
|
|
15
21
|
import HeaderHome from "$lib/components/layout/HeaderHome.svelte";
|
|
16
22
|
import PWAInstallButton from "$lib/components/PWAInstallButton.svelte";
|
|
17
|
-
|
|
18
|
-
import { onMount } from "svelte";
|
|
19
|
-
import { registerServiceWorker } from "$lib/registerServiceWorker.js";
|
|
20
|
-
import { browser } from "$app/environment";
|
|
23
|
+
|
|
21
24
|
import "$lib/styles/global.min.css";
|
|
22
25
|
import "$lib/styles/fa-global.css";
|
|
23
26
|
|
|
24
|
-
// Import favicon images
|
|
25
27
|
import logoPng from "$lib/img/logo-web.png";
|
|
26
28
|
import logoWbp from "$lib/img/logo-web.webp";
|
|
27
29
|
import faviconSvg from "$lib/img/favicon.svg";
|
|
28
30
|
import appleTouchIcon from "$lib/img/icon-180x180.png";
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
/** @type {typeof import('$lib/components/PostHog.svelte').default | null} */
|
|
32
|
-
let PostHog = null;
|
|
32
|
+
$: shouldShowReminder = $showReminder;
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
34
|
+
onMount(() => {
|
|
35
|
+
console.log("[APP] onMount triggered in +layout.svelte");
|
|
36
|
+
|
|
37
|
+
registerServiceWorker();
|
|
38
|
+
initPostHog();
|
|
40
39
|
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
// Register navigation tracking only on client
|
|
41
|
+
afterNavigate(() => {
|
|
42
|
+
capture("$pageview");
|
|
43
|
+
});
|
|
45
44
|
|
|
45
|
+
if (browser) {
|
|
46
46
|
const isDev = import.meta.env.MODE === "development";
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (isDev ||
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
);
|
|
48
|
+
// Check for ?debug=true in URL (no persistence)
|
|
49
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
50
|
+
const debug = urlParams.get("debug") === "true";
|
|
51
|
+
|
|
52
|
+
if (isDev || debug) {
|
|
53
|
+
console.log("ENV MODE =", import.meta.env.MODE);
|
|
54
|
+
console.log("isDev =", isDev);
|
|
55
|
+
console.log("debug param =", debug);
|
|
64
56
|
}
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
|
|
58
|
+
// Preload logo assets
|
|
59
|
+
[logoPng, logoWbp, appleTouchIcon].forEach((src) => {
|
|
60
|
+
const img = new Image();
|
|
61
|
+
img.src = src;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
67
65
|
|
|
68
66
|
// fallback values if data.meta not set
|
|
69
67
|
const metaTitle =
|
|
@@ -103,10 +101,6 @@ This file is part of Network Pro.
|
|
|
103
101
|
</header>
|
|
104
102
|
<!-- END HEADER -->
|
|
105
103
|
|
|
106
|
-
{#if PostHog}
|
|
107
|
-
<PostHog /> <!-- Add PostHog component when it's loaded -->
|
|
108
|
-
{/if}
|
|
109
|
-
|
|
110
104
|
<main>
|
|
111
105
|
<slot />
|
|
112
106
|
</main>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Tracking Preferences & Privacy Signals
|
|
2
|
+
|
|
3
|
+
<!-- markdownlint-disable MD018 -->
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This document explains how Network Pro handles analytics tracking in a privacy-aware, user-consented, and standards-compliant manner. It covers:
|
|
8
|
+
|
|
9
|
+
- Tracking preference storage (cookies)
|
|
10
|
+
- Browser signals (DNT and GPC)
|
|
11
|
+
- Reconsent logic
|
|
12
|
+
- Reactive store architecture
|
|
13
|
+
|
|
14
|
+
### 🔐 Principles
|
|
15
|
+
|
|
16
|
+
- **Privacy by default**: Tracking is disabled when browser signals indicate user preference (DNT/GPC).
|
|
17
|
+
- **Explicit consent**: Users may opt-in or opt-out manually, overriding signals.
|
|
18
|
+
- **Persistent choice**: Consent state is remembered via first-party cookies.
|
|
19
|
+
- **Transparency**: The tracking status is shown to users in the UI.
|
|
20
|
+
|
|
21
|
+
### 🧠 Architecture Summary
|
|
22
|
+
|
|
23
|
+
- **Store**: `src/lib/stores/trackingPreferences.js`
|
|
24
|
+
- Consolidates logic for cookie preferences, browser signals, and consent state.
|
|
25
|
+
- SSR-safe, reactive, and fully declarative.
|
|
26
|
+
- **Deprecated**:
|
|
27
|
+
- `utils/privacy.js` → replaced by derived store logic.
|
|
28
|
+
- `utils/trackingCookies.js` → merged into the store with SSR-safe cookie APIs.
|
|
29
|
+
|
|
30
|
+
### Reactive State
|
|
31
|
+
|
|
32
|
+
| Store | Type | Description |
|
|
33
|
+
| ----------------------- | ------------------------- | ----------------------------------------------------------------------- |
|
|
34
|
+
| `trackingPreferences` | `Readable<TrackingState>` | Contains current tracking metadata (opt-in/out, DNT, GPC, status, etc). |
|
|
35
|
+
| `trackingEnabled` | `Writable<boolean>` | Exposed to toggle or query PostHog tracking state reactively. |
|
|
36
|
+
| `remindUserToReconsent` | `Readable<boolean>` | Indicates whether a consent renewal prompt should be shown. |
|
|
37
|
+
| `showReminder` | `Writable<boolean>` | Used by PostHog to conditionally display a reminder or banner. |
|
|
38
|
+
|
|
39
|
+
### ⏳ Reconsent Logic
|
|
40
|
+
|
|
41
|
+
The derived store `remindUserToReconsent` evaluates whether a user should be reminded to re-consent to tracking.
|
|
42
|
+
|
|
43
|
+
It checks for:
|
|
44
|
+
|
|
45
|
+
- Manual opt-in or opt-out
|
|
46
|
+
- A valid `tracking_consent_timestamp` cookie
|
|
47
|
+
- Whether 6+ months have elapsed since that timestamp
|
|
48
|
+
|
|
49
|
+
### ⚙️ Developer Notes
|
|
50
|
+
|
|
51
|
+
- Changes to tracking preferences update cookies and reactive state
|
|
52
|
+
- Reconsent timestamp is written/cleared via store utility functions
|
|
53
|
+
- Use `$trackingPreferences` and `remindUserToReconsent` wherever reactive values are needed
|
|
54
|
+
|
|
55
|
+
### 💡 Related Components
|
|
56
|
+
|
|
57
|
+
| File | Purpose |
|
|
58
|
+
| ------------------------------- | -------------------------------------------------------------------------------- |
|
|
59
|
+
| `+layout.svelte` | Initializes PostHog client and service worker; references `trackingPreferences`. |
|
|
60
|
+
| `PrivacyDashboard.svelte` | UI control panel for opt-in/out toggles and consent status display. |
|
|
61
|
+
| `PrivacyContent.svelte` | Informational content rendered in modals, footers, and standalone pages. |
|
|
62
|
+
| `stores/trackingPreferences.js` | Primary source of truth; tracks and derives tracking state. |
|
|
63
|
+
| `stores/posthog.js` | Encapsulates privacy-safe analytics setup and event capture logic. |
|
|
Binary file
|
package/static/offline.min.css
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/*! ==========================================================================
|
|
2
|
-
|
|
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
|
@@ -24,12 +24,14 @@ const config = {
|
|
|
24
24
|
// Netlify adapter configuration
|
|
25
25
|
adapter: adapter({
|
|
26
26
|
edge: false, // Disable edge functions (optional, enable if needed)
|
|
27
|
-
split: false, // Disable splitting function files (optional, enable if needed)
|
|
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
|
});
|