@networkpro/web 1.12.9 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -1
- package/README.md +26 -18
- package/cspell.json +1 -0
- package/eslint.config.mjs +48 -48
- package/netlify/edge-functions/csp-report.js +31 -31
- package/package.json +3 -3
- package/playwright.config.js +14 -14
- package/postcss.config.mjs +1 -1
- package/scripts/auditScripts.js +16 -16
- package/scripts/bundleCss.js +5 -5
- package/scripts/checkEnv.js +6 -6
- package/scripts/checkNode.js +10 -10
- package/scripts/checkVersions.js +6 -6
- package/scripts/flattenHeaders.js +13 -13
- package/scripts/generateTest.js +5 -5
- package/scripts/openReport.js +3 -3
- package/scripts/validateHeaders.js +13 -13
- package/src/app.html +0 -9
- package/src/hooks.client.ts +1 -1
- package/src/hooks.server.js +31 -32
- package/src/lib/components/Badges.svelte +10 -10
- package/src/lib/components/CodeBlock.svelte +1 -1
- package/src/lib/components/ContainerSection.svelte +1 -1
- package/src/lib/components/FullWidthSection.svelte +3 -3
- package/src/lib/components/LegalNav.svelte +7 -7
- package/src/lib/components/Logo.svelte +9 -9
- package/src/lib/components/MetaTags.svelte +4 -4
- package/src/lib/components/PWAInstallButton.svelte +4 -4
- package/src/lib/components/RedirectPage.svelte +4 -4
- package/src/lib/components/SocialMedia.svelte +16 -16
- package/src/lib/components/foss/FossItemContent.svelte +27 -58
- package/src/lib/components/foss/ObtainiumBlock.svelte +64 -0
- package/src/lib/components/layout/Footer.svelte +18 -18
- package/src/lib/components/layout/HeaderDefault.svelte +16 -16
- package/src/lib/components/layout/HeaderHome.svelte +14 -14
- package/src/lib/data/fossData.js +22 -10
- package/src/lib/images.js +34 -34
- package/src/lib/img/obtainium.png +0 -0
- package/src/lib/img/obtainium.webp +0 -0
- package/src/lib/index.js +15 -15
- package/src/lib/meta.js +29 -29
- package/src/lib/pages/AboutContent.svelte +24 -24
- package/src/lib/pages/FossContent.svelte +13 -13
- package/src/lib/pages/HomeContent.svelte +7 -7
- package/src/lib/pages/LicenseContent.svelte +39 -39
- package/src/lib/pages/PGPContent.svelte +23 -23
- package/src/lib/pages/PrivacyContent.svelte +39 -39
- package/src/lib/pages/PrivacyDashboard.svelte +12 -12
- package/src/lib/pages/TermsConditionsContent.svelte +29 -29
- package/src/lib/pages/TermsUseContent.svelte +26 -26
- package/src/lib/registerServiceWorker.js +25 -25
- package/src/lib/stores/posthog.js +13 -13
- package/src/lib/stores/trackingPreferences.js +19 -19
- package/src/lib/styles/css/default.css +30 -0
- package/src/lib/styles/global.min.css +1 -1
- package/src/lib/types/fossTypes.js +9 -2
- package/src/lib/unregisterServiceWorker.js +1 -1
- package/src/lib/utils/purify.js +4 -4
- package/src/lib/utils/utm.js +2 -2
- package/src/routes/+error.svelte +4 -4
- package/src/routes/+layout.js +6 -6
- package/src/routes/+layout.svelte +29 -29
- package/src/routes/+page.server.js +2 -2
- package/src/routes/+page.svelte +9 -9
- package/src/routes/about/+page.server.js +2 -2
- package/src/routes/about/+page.svelte +7 -7
- package/src/routes/api/mock-csp/+server.js +3 -3
- package/src/routes/consultation/+page.svelte +5 -5
- package/src/routes/contact/+page.svelte +5 -5
- package/src/routes/foss-spotlight/+page.server.js +2 -2
- package/src/routes/foss-spotlight/+page.svelte +7 -7
- package/src/routes/license/+page.server.js +2 -2
- package/src/routes/license/+page.svelte +7 -7
- package/src/routes/pgp/+page.server.js +2 -2
- package/src/routes/pgp/+page.svelte +7 -7
- package/src/routes/pgp/[key]/+server.js +9 -9
- package/src/routes/privacy/+page.server.js +2 -2
- package/src/routes/privacy/+page.svelte +7 -7
- package/src/routes/privacy-dashboard/+page.server.js +2 -2
- package/src/routes/privacy-dashboard/+page.svelte +8 -8
- package/src/routes/privacy-rights/+page.svelte +5 -5
- package/src/routes/status/+page.server.js +2 -2
- package/src/routes/terms-conditions/+page.server.js +2 -2
- package/src/routes/terms-conditions/+page.svelte +7 -7
- package/src/routes/terms-of-use/+page.server.js +2 -2
- package/src/routes/terms-of-use/+page.svelte +7 -7
- package/src/service-worker.js +86 -86
- package/static/bin/heliboard.json +8 -0
- package/static/disableSw.js +2 -2
- package/static/offline.html +7 -7
- package/stylelint.config.js +56 -56
- package/svelte.config.js +6 -6
- package/tests/e2e/app.spec.js +27 -27
- package/tests/e2e/mobile.spec.js +18 -18
- package/tests/e2e/shared/helpers.js +4 -4
- package/tests/internal/auditCoverage.test.js +24 -24
- package/tests/unit/checkEnv.test.js +10 -10
- package/tests/unit/checkVersions.test.js +4 -4
- package/tests/unit/csp-report.test.js +24 -24
- package/tests/unit/demo.test.js +3 -3
- package/tests/unit/lib/utils/purify.test.js +12 -12
- package/tests/unit/routes/page.svelte.test.js +10 -10
- package/tests/unit/unregisterServiceWorker.test.js +5 -5
- package/tests/unit/utm.test.js +13 -13
- package/vite.config.js +5 -5
- package/vitest-setup-client.js +4 -4
- package/vitest.config.client.js +15 -15
- package/vitest.config.server.js +13 -13
package/scripts/auditScripts.js
CHANGED
|
@@ -15,16 +15,16 @@ This file is part of Network Pro.
|
|
|
15
15
|
* @updated 2025-05-21
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import fs from
|
|
19
|
-
import path from
|
|
18
|
+
import fs from 'fs';
|
|
19
|
+
import path from 'path';
|
|
20
20
|
|
|
21
|
-
const scriptsDir = path.resolve(
|
|
22
|
-
const testsDir = path.resolve(
|
|
21
|
+
const scriptsDir = path.resolve('./scripts');
|
|
22
|
+
const testsDir = path.resolve('./tests');
|
|
23
23
|
|
|
24
24
|
// Scripts intentionally excluded from test coverage
|
|
25
25
|
const allowList = new Set([
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
'checkNode.js',
|
|
27
|
+
'auditScripts.js', // itself
|
|
28
28
|
]);
|
|
29
29
|
|
|
30
30
|
// Recursively gather all test files
|
|
@@ -38,10 +38,10 @@ function getAllTestFiles(dir) {
|
|
|
38
38
|
files.push(...getAllTestFiles(fullPath));
|
|
39
39
|
} else if (
|
|
40
40
|
entry.isFile() &&
|
|
41
|
-
(entry.name.endsWith(
|
|
42
|
-
entry.name.endsWith(
|
|
43
|
-
entry.name.endsWith(
|
|
44
|
-
entry.name.endsWith(
|
|
41
|
+
(entry.name.endsWith('.test.js') ||
|
|
42
|
+
entry.name.endsWith('.spec.js') ||
|
|
43
|
+
entry.name.endsWith('.test.mjs') ||
|
|
44
|
+
entry.name.endsWith('.spec.mjs'))
|
|
45
45
|
) {
|
|
46
46
|
files.push(fullPath);
|
|
47
47
|
}
|
|
@@ -56,17 +56,17 @@ const testedModules = new Set(
|
|
|
56
56
|
testFiles.map((filePath) =>
|
|
57
57
|
path
|
|
58
58
|
.basename(filePath)
|
|
59
|
-
.replace(/\.test\.js$|\.spec\.js$|\.test\.mjs$|\.spec\.mjs$/,
|
|
59
|
+
.replace(/\.test\.js$|\.spec\.js$|\.test\.mjs$|\.spec\.mjs$/, ''),
|
|
60
60
|
),
|
|
61
61
|
);
|
|
62
62
|
|
|
63
63
|
// Gather all scripts (.js and .mjs)
|
|
64
64
|
const scriptFiles = fs
|
|
65
65
|
.readdirSync(scriptsDir)
|
|
66
|
-
.filter((file) => file.endsWith(
|
|
66
|
+
.filter((file) => file.endsWith('.js') || file.endsWith('.mjs'));
|
|
67
67
|
|
|
68
68
|
const untested = scriptFiles.filter((file) => {
|
|
69
|
-
const base = file.replace(/\.(js|mjs)$/,
|
|
69
|
+
const base = file.replace(/\.(js|mjs)$/, '');
|
|
70
70
|
return !allowList.has(file) && !testedModules.has(base);
|
|
71
71
|
});
|
|
72
72
|
|
|
@@ -75,7 +75,7 @@ const pathRelative = (file) =>
|
|
|
75
75
|
|
|
76
76
|
// Output results
|
|
77
77
|
if (untested.length) {
|
|
78
|
-
console.warn(
|
|
78
|
+
console.warn('\n⚠ Untested script files detected:\n');
|
|
79
79
|
|
|
80
80
|
untested.forEach((file) => {
|
|
81
81
|
const filePath = pathRelative(file);
|
|
@@ -87,9 +87,9 @@ if (untested.length) {
|
|
|
87
87
|
console.warn(
|
|
88
88
|
`\nAdd a corresponding test file in /tests (e.g., ${untested[0].replace(
|
|
89
89
|
/\.(js|mjs)$/,
|
|
90
|
-
|
|
90
|
+
'.test.js',
|
|
91
91
|
)})`,
|
|
92
92
|
);
|
|
93
93
|
} else {
|
|
94
|
-
console.log(
|
|
94
|
+
console.log('✅ All script files have corresponding tests.');
|
|
95
95
|
}
|
package/scripts/bundleCss.js
CHANGED
|
@@ -15,15 +15,15 @@ This file is part of Network Pro.
|
|
|
15
15
|
* @updated 2025-05-16
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import fs from
|
|
19
|
-
import { bundle } from
|
|
20
|
-
import path from
|
|
18
|
+
import fs from 'fs';
|
|
19
|
+
import { bundle } from 'lightningcss';
|
|
20
|
+
import path from 'path';
|
|
21
21
|
|
|
22
22
|
// Define the path to your input CSS file
|
|
23
|
-
const inputFilePath = path.resolve(
|
|
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/scripts/checkEnv.js
CHANGED
|
@@ -15,10 +15,10 @@ This file is part of Network Pro.
|
|
|
15
15
|
* @updated 2025-05-21
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { basename } from
|
|
19
|
-
import { fileURLToPath } from
|
|
18
|
+
import { basename } from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
20
|
|
|
21
|
-
const validEnvs = new Set([
|
|
21
|
+
const validEnvs = new Set(['dev', 'test', 'ci', 'prod', 'preview']);
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Checks and returns validation for ENV_MODE
|
|
@@ -36,7 +36,7 @@ export function checkEnv() {
|
|
|
36
36
|
let wasDefaulted = false;
|
|
37
37
|
|
|
38
38
|
if (!mode) {
|
|
39
|
-
mode =
|
|
39
|
+
mode = 'dev';
|
|
40
40
|
process.env.ENV_MODE = mode;
|
|
41
41
|
wasDefaulted = true;
|
|
42
42
|
console.warn("⚠️ ENV_MODE not set. Defaulting to 'dev'.");
|
|
@@ -45,11 +45,11 @@ export function checkEnv() {
|
|
|
45
45
|
valid = validEnvs.has(mode);
|
|
46
46
|
|
|
47
47
|
if (valid) {
|
|
48
|
-
const tag = wasDefaulted ?
|
|
48
|
+
const tag = wasDefaulted ? '[info]' : '[ok]';
|
|
49
49
|
console.log(`${tag} ENV_MODE is set to: "${mode}"`);
|
|
50
50
|
} else {
|
|
51
51
|
console.error(
|
|
52
|
-
`❌ Invalid ENV_MODE "${mode}". Must be one of: ${[...validEnvs].join(
|
|
52
|
+
`❌ Invalid ENV_MODE "${mode}". Must be one of: ${[...validEnvs].join(', ')}`,
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
55
|
|
package/scripts/checkNode.js
CHANGED
|
@@ -19,23 +19,23 @@ This file is part of Network Pro.
|
|
|
19
19
|
* @updated 2025-05-23
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { execSync } from
|
|
23
|
-
import fs from
|
|
24
|
-
import path from
|
|
25
|
-
import semver from
|
|
26
|
-
import { fileURLToPath } from
|
|
22
|
+
import { execSync } from 'child_process';
|
|
23
|
+
import fs from 'fs';
|
|
24
|
+
import path from 'path';
|
|
25
|
+
import semver from 'semver';
|
|
26
|
+
import { fileURLToPath } from 'url';
|
|
27
27
|
|
|
28
28
|
const __filename = fileURLToPath(import.meta.url);
|
|
29
29
|
const __dirname = path.dirname(__filename);
|
|
30
30
|
|
|
31
31
|
// Load engines from package.json
|
|
32
32
|
const pkg = JSON.parse(
|
|
33
|
-
fs.readFileSync(path.resolve(__dirname,
|
|
33
|
+
fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf8'),
|
|
34
34
|
);
|
|
35
35
|
const { node: nodeRange, npm: npmRange } = pkg.engines;
|
|
36
36
|
|
|
37
37
|
// Determine if this is running as part of npm's postinstall hook
|
|
38
|
-
const isPostInstall = process.env.npm_lifecycle_event ===
|
|
38
|
+
const isPostInstall = process.env.npm_lifecycle_event === 'postinstall';
|
|
39
39
|
|
|
40
40
|
let hasError = false;
|
|
41
41
|
|
|
@@ -54,7 +54,7 @@ if (!semver.satisfies(process.version, nodeRange)) {
|
|
|
54
54
|
let npmVersion = null;
|
|
55
55
|
|
|
56
56
|
try {
|
|
57
|
-
npmVersion = execSync(
|
|
57
|
+
npmVersion = execSync('npm --version').toString().trim();
|
|
58
58
|
if (!semver.satisfies(npmVersion, npmRange)) {
|
|
59
59
|
const msg = `npm ${npmVersion} does not satisfy required range: ${npmRange}`;
|
|
60
60
|
if (isPostInstall) {
|
|
@@ -65,13 +65,13 @@ try {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
} catch (err) {
|
|
68
|
-
console.error(
|
|
68
|
+
console.error('❌ Failed to check npm version:', err.message);
|
|
69
69
|
process.exit(1);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// Final output
|
|
73
73
|
if (!hasError) {
|
|
74
|
-
if (!process.env.CI || process.env.VERBOSE ===
|
|
74
|
+
if (!process.env.CI || process.env.VERBOSE === 'true') {
|
|
75
75
|
console.log(
|
|
76
76
|
`✅ Node (${process.version}) matches ${nodeRange}, and npm (${npmVersion}) matches ${npmRange}`,
|
|
77
77
|
);
|
package/scripts/checkVersions.js
CHANGED
|
@@ -16,10 +16,10 @@ This file is part of Network Pro.
|
|
|
16
16
|
* @updated 2025-05-20
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { execSync } from
|
|
20
|
-
import fs from
|
|
21
|
-
import path from
|
|
22
|
-
import semver from
|
|
19
|
+
import { execSync } from 'child_process';
|
|
20
|
+
import fs from 'fs';
|
|
21
|
+
import path from 'path';
|
|
22
|
+
import semver from 'semver';
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* @typedef {object} VersionCheckResult
|
|
@@ -38,12 +38,12 @@ import semver from "semver";
|
|
|
38
38
|
*/
|
|
39
39
|
export function checkVersions() {
|
|
40
40
|
const pkg = JSON.parse(
|
|
41
|
-
fs.readFileSync(path.resolve(
|
|
41
|
+
fs.readFileSync(path.resolve('./package.json'), 'utf8'),
|
|
42
42
|
);
|
|
43
43
|
const { node: nodeRange, npm: npmRange } = pkg.engines;
|
|
44
44
|
|
|
45
45
|
const nodeVersion = process.version;
|
|
46
|
-
const npmVersion = execSync(
|
|
46
|
+
const npmVersion = execSync('npm --version').toString().trim();
|
|
47
47
|
|
|
48
48
|
const nodeValid = semver.satisfies(nodeVersion, nodeRange);
|
|
49
49
|
const npmValid = semver.satisfies(npmVersion, npmRange);
|
|
@@ -15,30 +15,30 @@ This file is part of Network Pro.
|
|
|
15
15
|
* @updated 2025-05-18
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import fs from
|
|
18
|
+
import fs from 'fs';
|
|
19
19
|
|
|
20
|
-
const HEADERS_PATH =
|
|
21
|
-
const OUTPUT_PATH =
|
|
20
|
+
const HEADERS_PATH = './.headers_new'; // update if needed
|
|
21
|
+
const OUTPUT_PATH = './_headers.flattened';
|
|
22
22
|
|
|
23
|
-
const lines = fs.readFileSync(HEADERS_PATH,
|
|
23
|
+
const lines = fs.readFileSync(HEADERS_PATH, 'utf-8').split('\n');
|
|
24
24
|
const output = [];
|
|
25
25
|
|
|
26
26
|
let cspLines = [];
|
|
27
27
|
let inCSP = false;
|
|
28
28
|
|
|
29
29
|
for (const line of lines) {
|
|
30
|
-
if (line.trim().startsWith(
|
|
30
|
+
if (line.trim().startsWith('Content-Security-Policy:')) {
|
|
31
31
|
inCSP = true;
|
|
32
32
|
cspLines.push(line.trim());
|
|
33
|
-
} else if (inCSP && line.startsWith(
|
|
33
|
+
} else if (inCSP && line.startsWith(' ')) {
|
|
34
34
|
cspLines.push(line.trim());
|
|
35
35
|
} else {
|
|
36
36
|
if (inCSP) {
|
|
37
37
|
// Output flattened CSP
|
|
38
38
|
const flattened = cspLines
|
|
39
|
-
.join(
|
|
40
|
-
.replace(/\s*;\s*/g,
|
|
41
|
-
.replace(/\s+/g,
|
|
39
|
+
.join(' ')
|
|
40
|
+
.replace(/\s*;\s*/g, '; ') // Normalize spacing
|
|
41
|
+
.replace(/\s+/g, ' ')
|
|
42
42
|
.trim();
|
|
43
43
|
output.push(` ${flattened}`);
|
|
44
44
|
cspLines = [];
|
|
@@ -51,12 +51,12 @@ for (const line of lines) {
|
|
|
51
51
|
// Handle edge case: CSP is last in file
|
|
52
52
|
if (cspLines.length > 0) {
|
|
53
53
|
const flattened = cspLines
|
|
54
|
-
.join(
|
|
55
|
-
.replace(/\s*;\s*/g,
|
|
56
|
-
.replace(/\s+/g,
|
|
54
|
+
.join(' ')
|
|
55
|
+
.replace(/\s*;\s*/g, '; ')
|
|
56
|
+
.replace(/\s+/g, ' ')
|
|
57
57
|
.trim();
|
|
58
58
|
output.push(` ${flattened}`);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
fs.writeFileSync(OUTPUT_PATH, output.join(
|
|
61
|
+
fs.writeFileSync(OUTPUT_PATH, output.join('\n'));
|
|
62
62
|
console.log(`Flattened headers written to ${OUTPUT_PATH}`);
|
package/scripts/generateTest.js
CHANGED
|
@@ -17,13 +17,13 @@ This file is part of Network Pro.
|
|
|
17
17
|
* @updated 2025-06-01
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import fs from
|
|
21
|
-
import path from
|
|
20
|
+
import fs from 'fs';
|
|
21
|
+
import path from 'path';
|
|
22
22
|
|
|
23
23
|
const [, , targetFile] = process.argv;
|
|
24
24
|
|
|
25
25
|
if (!targetFile) {
|
|
26
|
-
console.error(
|
|
26
|
+
console.error('Usage: node generateTest.js <path/to/yourFile.js>');
|
|
27
27
|
process.exit(1);
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -32,7 +32,7 @@ const parsed = path.parse(absolutePath);
|
|
|
32
32
|
|
|
33
33
|
const testFileName = `${parsed.name}.test.js`;
|
|
34
34
|
const testFilePath = path.join(
|
|
35
|
-
parsed.dir.replace(
|
|
35
|
+
parsed.dir.replace('src', 'tests/unit'),
|
|
36
36
|
testFileName,
|
|
37
37
|
);
|
|
38
38
|
|
|
@@ -42,7 +42,7 @@ const scaffold = `/**
|
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
44
|
import { describe, it, expect } from "vitest";
|
|
45
|
-
import * as Module from "${path.relative(path.dirname(testFilePath), absolutePath).replace(/\\/g,
|
|
45
|
+
import * as Module from "${path.relative(path.dirname(testFilePath), absolutePath).replace(/\\/g, '/')}";
|
|
46
46
|
|
|
47
47
|
describe("${parsed.name}", () => {
|
|
48
48
|
it("should have tests", () => {
|
package/scripts/openReport.js
CHANGED
|
@@ -16,9 +16,9 @@ This file is part of Network Pro.
|
|
|
16
16
|
* @updated 2025-06-09
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { exec } from
|
|
20
|
-
import { dirname, join } from
|
|
21
|
-
import { fileURLToPath } from
|
|
19
|
+
import { exec } from 'child_process';
|
|
20
|
+
import { dirname, join } from 'path';
|
|
21
|
+
import { fileURLToPath } from 'url';
|
|
22
22
|
|
|
23
23
|
// ESM replacement for __dirname
|
|
24
24
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -16,10 +16,10 @@ This file is part of Network Pro.
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
// validate-headers.js
|
|
19
|
-
import fs from
|
|
19
|
+
import fs from 'fs';
|
|
20
20
|
|
|
21
|
-
const file =
|
|
22
|
-
const lines = fs.readFileSync(file,
|
|
21
|
+
const file = './_headers';
|
|
22
|
+
const lines = fs.readFileSync(file, 'utf-8').split('\n');
|
|
23
23
|
|
|
24
24
|
let currentPath = null;
|
|
25
25
|
let errors = [];
|
|
@@ -30,15 +30,15 @@ let headerValueBuffer = [];
|
|
|
30
30
|
function flushBufferedHeader(lineNum) {
|
|
31
31
|
if (!currentHeader) return;
|
|
32
32
|
|
|
33
|
-
const value = headerValueBuffer.join(
|
|
33
|
+
const value = headerValueBuffer.join(' ').trim();
|
|
34
34
|
|
|
35
|
-
if (currentHeader !==
|
|
35
|
+
if (currentHeader !== 'Content-Security-Policy' && value.endsWith(';')) {
|
|
36
36
|
errors.push(
|
|
37
37
|
`Line ${lineNum}: Header '${currentHeader}' should not end with a semicolon`,
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
if (currentHeader ===
|
|
41
|
+
if (currentHeader === 'Content-Security-Policy' && !value.endsWith(';')) {
|
|
42
42
|
errors.push(
|
|
43
43
|
`Line ${lineNum}: 'Content-Security-Policy' must end with a semicolon`,
|
|
44
44
|
);
|
|
@@ -52,7 +52,7 @@ for (let i = 0; i < lines.length; i++) {
|
|
|
52
52
|
const line = lines[i];
|
|
53
53
|
const trimmed = line.trim();
|
|
54
54
|
|
|
55
|
-
if (trimmed.startsWith(
|
|
55
|
+
if (trimmed.startsWith('/*')) {
|
|
56
56
|
if (currentPath) {
|
|
57
57
|
errors.push(
|
|
58
58
|
`Line ${i + 1}: Multiple '/*' blocks detected. Only one is allowed in Netlify _headers`,
|
|
@@ -62,10 +62,10 @@ for (let i = 0; i < lines.length; i++) {
|
|
|
62
62
|
continue;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
if (trimmed ===
|
|
65
|
+
if (trimmed === '') continue;
|
|
66
66
|
|
|
67
|
-
if (line.startsWith(
|
|
68
|
-
const colonIndex = line.indexOf(
|
|
67
|
+
if (line.startsWith(' ')) {
|
|
68
|
+
const colonIndex = line.indexOf(':');
|
|
69
69
|
|
|
70
70
|
if (colonIndex !== -1) {
|
|
71
71
|
// New header
|
|
@@ -88,9 +88,9 @@ for (let i = 0; i < lines.length; i++) {
|
|
|
88
88
|
flushBufferedHeader(lines.length);
|
|
89
89
|
|
|
90
90
|
if (errors.length) {
|
|
91
|
-
console.error(
|
|
92
|
-
errors.forEach((e) => console.error(
|
|
91
|
+
console.error('❌ Header validation failed:');
|
|
92
|
+
errors.forEach((e) => console.error(' -', e));
|
|
93
93
|
process.exit(1);
|
|
94
94
|
} else {
|
|
95
|
-
console.log(
|
|
95
|
+
console.log('✅ _headers file passed validation!');
|
|
96
96
|
}
|
package/src/app.html
CHANGED
|
@@ -34,15 +34,6 @@
|
|
|
34
34
|
href="https://us-assets.i.posthog.com"
|
|
35
35
|
crossorigin="anonymous" />
|
|
36
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>
|
|
45
|
-
|
|
46
37
|
<!-- Preload FontAwesome webfonts -->
|
|
47
38
|
<link
|
|
48
39
|
rel="preload"
|
package/src/hooks.client.ts
CHANGED
|
@@ -18,7 +18,7 @@ export const init = undefined;
|
|
|
18
18
|
* @param error - The uncaught error object
|
|
19
19
|
*/
|
|
20
20
|
export function handleError(error: Error) {
|
|
21
|
-
console.error(
|
|
21
|
+
console.error('[CLIENT] Unhandled error:', error);
|
|
22
22
|
|
|
23
23
|
// Future: send to error reporting service
|
|
24
24
|
// e.g., Sentry.captureException(error);
|
package/src/hooks.server.js
CHANGED
|
@@ -17,21 +17,20 @@ export async function handle({ event, resolve }) {
|
|
|
17
17
|
// Determine environment flags
|
|
18
18
|
// Default to development policy if neither test nor prod
|
|
19
19
|
const isTestEnvironment =
|
|
20
|
-
process.env.NODE_ENV ===
|
|
20
|
+
process.env.NODE_ENV === 'test' || process.env.ENV_MODE === 'ci';
|
|
21
21
|
const isProdEnvironment =
|
|
22
|
-
process.env.NODE_ENV ===
|
|
22
|
+
process.env.NODE_ENV === 'production' || process.env.ENV_MODE === 'prod';
|
|
23
23
|
|
|
24
|
-
console.log(
|
|
25
|
-
console.log(
|
|
24
|
+
console.log('[CSP Debug] NODE_ENV:', process.env.NODE_ENV);
|
|
25
|
+
console.log('[CSP Debug] ENV_MODE:', process.env.ENV_MODE);
|
|
26
26
|
|
|
27
27
|
// Determine report URI
|
|
28
|
-
const reportUri = isProdEnvironment ?
|
|
28
|
+
const reportUri = isProdEnvironment ? '/api/csp-report' : '/api/mock-csp';
|
|
29
29
|
|
|
30
30
|
// Construct base policy
|
|
31
31
|
const cspDirectives = [
|
|
32
32
|
"default-src 'self';",
|
|
33
33
|
"script-src 'self' 'unsafe-inline' https://us.i.posthog.com https://us-assets.i.posthog.com;",
|
|
34
|
-
"script-src-elem 'self' 'unsafe-inline' https://us.i.posthog.com https://us-assets.i.posthog.com;",
|
|
35
34
|
"style-src 'self' 'unsafe-inline';",
|
|
36
35
|
"img-src 'self' data:;",
|
|
37
36
|
"connect-src 'self' https://us.i.posthog.com https://us-assets.i.posthog.com;",
|
|
@@ -40,7 +39,7 @@ export async function handle({ event, resolve }) {
|
|
|
40
39
|
"base-uri 'self';",
|
|
41
40
|
"object-src 'none';",
|
|
42
41
|
"frame-ancestors 'none';",
|
|
43
|
-
|
|
42
|
+
'upgrade-insecure-requests;',
|
|
44
43
|
`report-uri ${reportUri};`,
|
|
45
44
|
];
|
|
46
45
|
|
|
@@ -55,37 +54,37 @@ export async function handle({ event, resolve }) {
|
|
|
55
54
|
cspDirectives[5] = "connect-src 'self';";
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
response.headers.set(
|
|
57
|
+
response.headers.set('Content-Security-Policy', cspDirectives.join(' '));
|
|
59
58
|
|
|
60
59
|
// Set other security headers
|
|
61
60
|
response.headers.set(
|
|
62
|
-
|
|
61
|
+
'Permissions-Policy',
|
|
63
62
|
[
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
].join(
|
|
63
|
+
'fullscreen=(self)',
|
|
64
|
+
'sync-xhr=()',
|
|
65
|
+
'camera=()',
|
|
66
|
+
'microphone=()',
|
|
67
|
+
'geolocation=()',
|
|
68
|
+
'clipboard-read=()',
|
|
69
|
+
'clipboard-write=(self)',
|
|
70
|
+
'payment=()',
|
|
71
|
+
'usb=()',
|
|
72
|
+
'hid=()',
|
|
73
|
+
'gamepad=()',
|
|
74
|
+
'serial=()',
|
|
75
|
+
'publickey-credentials-get=()',
|
|
76
|
+
'browsing-topics=()',
|
|
77
|
+
].join(', '),
|
|
79
78
|
);
|
|
80
79
|
|
|
81
|
-
response.headers.set(
|
|
82
|
-
response.headers.set(
|
|
83
|
-
response.headers.set(
|
|
80
|
+
response.headers.set('X-Content-Type-Options', 'nosniff');
|
|
81
|
+
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
82
|
+
response.headers.set('X-Frame-Options', 'DENY');
|
|
84
83
|
|
|
85
|
-
if (process.env.ENV_MODE !==
|
|
84
|
+
if (process.env.ENV_MODE !== 'test' && process.env.ENV_MODE !== 'ci') {
|
|
86
85
|
response.headers.set(
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
'Strict-Transport-Security',
|
|
87
|
+
'max-age=31536000; includeSubDomains;', // No preload here
|
|
89
88
|
);
|
|
90
89
|
}
|
|
91
90
|
|
|
@@ -97,10 +96,10 @@ export async function handle({ event, resolve }) {
|
|
|
97
96
|
* @type {import('@sveltejs/kit').HandleServerError}
|
|
98
97
|
*/
|
|
99
98
|
export function handleError({ error, event }) {
|
|
100
|
-
console.error(
|
|
99
|
+
console.error('🔴 SSR Error in route:', event.url.pathname);
|
|
101
100
|
console.error(error);
|
|
102
101
|
|
|
103
102
|
return {
|
|
104
|
-
message:
|
|
103
|
+
message: 'A server-side error occurred',
|
|
105
104
|
};
|
|
106
105
|
}
|
|
@@ -7,15 +7,15 @@ This file is part of Network Pro.
|
|
|
7
7
|
========================================================================== -->
|
|
8
8
|
|
|
9
9
|
<script>
|
|
10
|
-
import { base } from
|
|
10
|
+
import { base } from '$app/paths';
|
|
11
11
|
// Import badges for licenses
|
|
12
|
-
import { ccBadge, gplBadge } from
|
|
13
|
-
import { CONSTANTS } from
|
|
12
|
+
import { ccBadge, gplBadge } from '$lib';
|
|
13
|
+
import { CONSTANTS } from '$lib';
|
|
14
14
|
|
|
15
15
|
// Log the base path to verify its value
|
|
16
16
|
//console.log("Base path:", base);
|
|
17
17
|
|
|
18
|
-
console.log(CONSTANTS.COMPANY_INFO.APP_NAME);
|
|
18
|
+
//console.log(CONSTANTS.COMPANY_INFO.APP_NAME);
|
|
19
19
|
|
|
20
20
|
const { PAGE } = CONSTANTS;
|
|
21
21
|
|
|
@@ -41,16 +41,16 @@ This file is part of Network Pro.
|
|
|
41
41
|
{
|
|
42
42
|
href: ccbyLink,
|
|
43
43
|
src: ccBadge,
|
|
44
|
-
alt:
|
|
45
|
-
width:
|
|
46
|
-
height:
|
|
44
|
+
alt: 'Creative Commons BY',
|
|
45
|
+
width: '160px',
|
|
46
|
+
height: '24px',
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
href: gplLink,
|
|
50
50
|
src: gplBadge,
|
|
51
|
-
alt:
|
|
52
|
-
width:
|
|
53
|
-
height:
|
|
51
|
+
alt: 'GPL 3.0 or Later',
|
|
52
|
+
width: '120px',
|
|
53
|
+
height: '24px',
|
|
54
54
|
},
|
|
55
55
|
];
|
|
56
56
|
</script>
|
|
@@ -17,16 +17,16 @@ This file is part of Network Pro.
|
|
|
17
17
|
* Additional classes for the outer full-width wrapper
|
|
18
18
|
* @type {string}
|
|
19
19
|
*/
|
|
20
|
-
export let outerClass =
|
|
20
|
+
export let outerClass = '';
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Additional classes for the inner container
|
|
24
24
|
* @type {string}
|
|
25
25
|
*/
|
|
26
|
-
export let containerClass =
|
|
26
|
+
export let containerClass = '';
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
|
-
<div class={`full-width-section ${centered ?
|
|
29
|
+
<div class={`full-width-section ${centered ? 'centered' : ''} ${outerClass}`}>
|
|
30
30
|
<div class={`container ${containerClass}`}>
|
|
31
31
|
<slot></slot>
|
|
32
32
|
</div>
|