@networkpro/web 1.1.3 → 1.4.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.
@@ -12,8 +12,8 @@ This file is part of Network Pro.
12
12
  import Footer from "$lib/components/layout/Footer.svelte";
13
13
  import HeaderDefault from "$lib/components/layout/HeaderDefault.svelte";
14
14
  import HeaderHome from "$lib/components/layout/HeaderHome.svelte";
15
+ import PWAInstallButton from "$lib/components/PWAInstallButton.svelte";
15
16
  import { browser } from "$app/environment";
16
- // TODO: Testing in progress
17
17
  import { registerServiceWorker } from "$lib/registerServiceWorker.js";
18
18
  import "$lib/styles";
19
19
 
@@ -47,7 +47,6 @@ This file is part of Network Pro.
47
47
  // Preload Apple Touch icon
48
48
  touchImg.src = appleTouchIcon;
49
49
 
50
- // TODO: Testing in progress
51
50
  // Register the service worker
52
51
  registerServiceWorker();
53
52
  }
@@ -92,9 +91,11 @@ This file is part of Network Pro.
92
91
  {#if data.pathname === "/"}
93
92
  <!-- Render the Home Header for the root route -->
94
93
  <HeaderHome />
94
+ <PWAInstallButton />
95
95
  {:else}
96
96
  <!-- Render the Default Header for all other routes -->
97
97
  <HeaderDefault />
98
+ <PWAInstallButton />
98
99
  {/if}
99
100
  </ContainerSection>
100
101
  </header>
@@ -8,25 +8,71 @@ This file is part of Network Pro.
8
8
  /** @type {ServiceWorkerGlobalScope} */
9
9
  const sw = self;
10
10
 
11
+ const disallowedHosts = ["licdn.com", "googletagmanager.com"];
12
+
11
13
  import { build, files, version } from "$service-worker";
12
14
 
13
15
  /** @type {string} */
14
16
  const CACHE = `cache-${version}`;
15
17
 
16
18
  /** @type {string[]} */
17
- const ASSETS = Array.from(new Set([...build, ...files, "/offline.html"]));
19
+ const excludedAssets = [];
20
+
21
+ /** @type {string[]} */
22
+ const ASSETS = Array.from(
23
+ new Set(
24
+ [...build, ...files, "/offline.html"].filter((path) => {
25
+ try {
26
+ const url = new URL(path, location.origin);
27
+ const hostname = url.hostname;
28
+
29
+ const shouldExclude =
30
+ path.startsWith("http") ||
31
+ disallowedHosts.some(
32
+ (host) => hostname === host || hostname.endsWith(`.${host}`),
33
+ ) ||
34
+ [
35
+ "/img/banner-1280x640.png",
36
+ "/img/banner-og-1200x630.png",
37
+ "/img/logo-transparent.png",
38
+ "/img/logo.png",
39
+ "/img/svelte.png",
40
+ "/robots.txt",
41
+ "/screenshots/desktop-foss.png",
42
+ "/sitemap.xml",
43
+ "/CNAME",
44
+ ].includes(path);
45
+
46
+ if (shouldExclude) excludedAssets.push(path);
47
+ return !shouldExclude;
48
+ } catch (err) {
49
+ console.warn("[SW] URL parse failed, skipping path:", path, err);
50
+ excludedAssets.push(path);
51
+ return true;
52
+ }
53
+ }),
54
+ ),
55
+ );
18
56
 
19
- console.log("[SW] Assets to cache:", ASSETS); // Helps debug duplicates
57
+ const uniqueExcludedAssets = [...new Set(excludedAssets)].sort();
58
+
59
+ console.log("[SW] Assets to precache:", ASSETS);
60
+ console.log("[SW] Excluded assets:", uniqueExcludedAssets);
20
61
 
21
62
  /**
22
63
  * @param {ExtendableEvent} event
23
64
  */
24
65
  sw.addEventListener("install", (event) => {
25
- /** @type {ExtendableEvent} */ (event).waitUntil(
26
- caches
27
- .open(CACHE)
28
- .then((cache) => cache.addAll(ASSETS))
29
- .then(() => sw.skipWaiting()),
66
+ event.waitUntil(
67
+ (async () => {
68
+ const cache = await caches.open(CACHE);
69
+ try {
70
+ await cache.addAll(ASSETS);
71
+ sw.skipWaiting();
72
+ } catch (err) {
73
+ console.warn("[SW] Failed to precache some assets:", err);
74
+ }
75
+ })(),
30
76
  );
31
77
  });
32
78
 
@@ -64,8 +110,10 @@ sw.addEventListener("activate", (event) => {
64
110
  sw.addEventListener("fetch", (event) => {
65
111
  /** @type {FetchEvent} */ (event).respondWith(
66
112
  (async () => {
67
- const cached = await caches.match(event.request);
68
- if (cached) return cached;
113
+ if (new URL(event.request.url).origin === location.origin) {
114
+ const cached = await caches.match(event.request);
115
+ if (cached) return cached;
116
+ }
69
117
 
70
118
  try {
71
119
  if (event.request.mode === "navigate") {
@@ -1,10 +1,15 @@
1
1
  {
2
+ "id": "/",
3
+ "start_url": "/?utm_source=homescreen",
4
+ "scope": "/",
2
5
  "name": "Network Pro Strategies",
3
6
  "short_name": "Network Pro",
4
7
  "description": "Access our expert cybersecurity services anytime, anywhere with our streamlined Progressive Web App. Optimized for speed, security, and mobile use, this app offers a seamless experience to explore our solutions, view project highlights, and get in touch—all in one place.",
5
- "start_url": "/?utm_source=homescreen",
8
+ "lang": "en-US",
9
+ "dir": "ltr",
6
10
  "display": "standalone",
7
11
  "display_override": ["window-controls-overlay", "minimal-ui"],
12
+ "orientation": "any",
8
13
  "background_color": "#191919",
9
14
  "theme_color": "#ffc627",
10
15
  "icons": [
@@ -22,7 +27,7 @@
22
27
  "src": "/icon-512x512-maskable.png",
23
28
  "type": "image/png",
24
29
  "sizes": "512x512",
25
- "purpose": "maskable"
30
+ "purpose": "any maskable"
26
31
  },
27
32
  {
28
33
  "src": "/icon-splash.png",
@@ -30,6 +35,8 @@
30
35
  "sizes": "512x512"
31
36
  }
32
37
  ],
38
+ "categories": ["business", "security", "technology", "network", "privacy"],
39
+ "prefer_related_applications": false,
33
40
  "screenshots": [
34
41
  {
35
42
  "src": "/screenshots/desktop-home.png",
@@ -60,9 +67,6 @@
60
67
  "label": "FOSS Spotlight on mobile"
61
68
  }
62
69
  ],
63
- "orientation": "any",
64
- "scope": "/",
65
- "categories": ["business", "security", "technology", "network", "privacy"],
66
70
  "shortcuts": [
67
71
  {
68
72
  "name": "Consulting Services",
package/static/robots.txt CHANGED
@@ -6,7 +6,22 @@
6
6
  # www.robotstxt.org/
7
7
 
8
8
  User-agent: *
9
- Disallow: *.jsonc$
10
- Disallow: *.json$
11
- Disallow: *.mjs$
12
- Disallow: *.js$
9
+
10
+ # Disallow dev and CI/CD artifacts
11
+ Disallow: /tests/
12
+ Disallow: /scripts/
13
+ Disallow: /playwright-report/
14
+ Disallow: /reports/
15
+ Disallow: /coverage/
16
+ Disallow: /build/
17
+ Disallow: /.lighthouseci/
18
+
19
+ # Disallow stub routes that redirect externally
20
+ Disallow: /contact
21
+ Disallow: /privacy-rights
22
+
23
+ # Allow everything else
24
+ Allow: /
25
+
26
+ # Inform bots where to find the sitemap
27
+ Sitemap: https://netwk.pro/sitemap.xml
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- src/app.spec.js
2
+ tests/e2e/app.spec.js
3
3
 
4
4
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
5
  This file is part of Network Pro.
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- src/mobile.spec.js
2
+ tests/e2e/mobile.spec.js
3
3
 
4
4
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
5
  This file is part of Network Pro.
@@ -0,0 +1,42 @@
1
+ /* ==========================================================================
2
+ tests/unit/auditScripts.test.js
3
+
4
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
+ This file is part of Network Pro.
6
+ ========================================================================== */
7
+
8
+ /**
9
+ * Unit test for scripts/auditScripts.js
10
+ */
11
+
12
+ import fs from "fs";
13
+ import path from "path";
14
+ import { describe, expect, it } from "vitest";
15
+
16
+ describe("auditScripts.js", () => {
17
+ it("should identify untested scripts correctly", () => {
18
+ const scriptsDir = path.resolve("./scripts");
19
+ const testsDir = path.resolve("./tests");
20
+
21
+ const allowList = new Set(["checkNode.js", "auditScripts.js"]);
22
+
23
+ const scriptFiles = fs
24
+ .readdirSync(scriptsDir)
25
+ .filter((file) => file.endsWith(".js"));
26
+
27
+ const testFiles = fs
28
+ .readdirSync(testsDir)
29
+ .filter((file) => file.endsWith(".test.js") || file.endsWith(".spec.js"));
30
+
31
+ const testedModules = new Set(
32
+ testFiles.map((f) => f.replace(/\.test\.js$|\.spec\.js$/, "")),
33
+ );
34
+
35
+ const untested = scriptFiles.filter((file) => {
36
+ const base = file.replace(/\.js$/, "");
37
+ return !allowList.has(file) && !testedModules.has(base);
38
+ });
39
+
40
+ expect(untested).not.toContain("auditScripts.js");
41
+ });
42
+ });
@@ -0,0 +1,48 @@
1
+ /* ==========================================================================
2
+ tests/unit/checkEnv.test.js
3
+
4
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
+ This file is part of Network Pro.
6
+ ========================================================================== */
7
+
8
+ /**
9
+ * Unit test for scripts/checkEnv.js
10
+ */
11
+
12
+ import { afterEach, describe, expect, it } from "vitest";
13
+ import { checkEnv } from "../../scripts/checkEnv.js";
14
+
15
+ describe("checkEnv()", () => {
16
+ const originalEnv = process.env.ENV_MODE;
17
+
18
+ afterEach(() => {
19
+ if (originalEnv === undefined) {
20
+ delete process.env.ENV_MODE;
21
+ } else {
22
+ process.env.ENV_MODE = originalEnv;
23
+ }
24
+ });
25
+
26
+ it("should default to 'dev' if ENV_MODE is not set", () => {
27
+ delete process.env.ENV_MODE;
28
+ const result = checkEnv();
29
+ expect(result.mode).toBe("dev");
30
+ expect(result.valid).toBe(true);
31
+ expect(result.wasDefaulted).toBe(true);
32
+ });
33
+
34
+ it("should validate a correct ENV_MODE", () => {
35
+ process.env.ENV_MODE = "ci";
36
+ const result = checkEnv();
37
+ expect(result.mode).toBe("ci");
38
+ expect(result.valid).toBe(true);
39
+ expect(result.wasDefaulted).toBe(false);
40
+ });
41
+
42
+ it("should return invalid for an unknown ENV_MODE", () => {
43
+ process.env.ENV_MODE = "banana";
44
+ const result = checkEnv();
45
+ expect(result.valid).toBe(false);
46
+ expect(result.allowed).toContain("dev");
47
+ });
48
+ });
@@ -0,0 +1,24 @@
1
+ /* ==========================================================================
2
+ tests/unit/checkVersions.test.js
3
+
4
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
+ This file is part of Network Pro.
6
+ ========================================================================== */
7
+
8
+ /**
9
+ * Unit test for scripts/checkVersions.js
10
+ */
11
+
12
+ import { describe, expect, it } from "vitest";
13
+ import { checkVersions } from "../../scripts/checkVersions.js";
14
+
15
+ describe("checkVersions()", () => {
16
+ it("should match current Node and NPM versions to engine ranges", () => {
17
+ const result = checkVersions();
18
+
19
+ expect(result.nodeVersion).toMatch(/^v\d+\.\d+\.\d+$/);
20
+ expect(result.npmVersion).toMatch(/^\d+\.\d+\.\d+$/);
21
+ expect(result.nodeValid).toBe(true);
22
+ expect(result.npmValid).toBe(true);
23
+ });
24
+ });
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- src/demo.spec.js
2
+ tests/unit/demo.spec.js
3
3
 
4
4
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
5
  This file is part of Network Pro.
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- src/routes/page.svelte.test.js
2
+ tests/unit/routes/page.svelte.test.js
3
3
 
4
4
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
5
  This file is part of Network Pro.
@@ -8,7 +8,7 @@ This file is part of Network Pro.
8
8
  import "@testing-library/jest-dom/vitest";
9
9
  import { render, screen } from "@testing-library/svelte";
10
10
  import { describe, expect, test } from "vitest";
11
- import Page from "./+page.svelte";
11
+ import Page from "../../../src/routes/+page.svelte";
12
12
 
13
13
  describe("/+page.svelte", () => {
14
14
  test("should render the home page section", () => {
package/vite.config.js CHANGED
@@ -1,3 +1,10 @@
1
+ /* =========================================================================
2
+ vite.config.js
3
+
4
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
+ This file is part of Network Pro.
6
+ ========================================================================= */
7
+
1
8
  import { sveltekit } from "@sveltejs/kit/vite";
2
9
  import { defineConfig } from "vite";
3
10
  import lightningcssPlugin from "vite-plugin-lightningcss";
@@ -1,3 +1,10 @@
1
+ /* =========================================================================
2
+ vitest.config.client.js
3
+
4
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
+ This file is part of Network Pro.
6
+ ========================================================================= */
7
+
1
8
  import { sveltekit } from "@sveltejs/kit/vite";
2
9
  import { svelteTesting } from "@testing-library/svelte/vite";
3
10
  import lightningcssPlugin from "vite-plugin-lightningcss";
@@ -18,8 +25,8 @@ export default defineConfig({
18
25
  name: "client",
19
26
  environment: "jsdom",
20
27
  clearMocks: true,
21
- include: ["src/**/*.svelte.{test,spec}.{js,mjs}"],
22
- exclude: ["src/lib/server/**"],
28
+ include: ["tests/unit/**/*.svelte.test.{js,mjs}"],
29
+ exclude: [],
23
30
  setupFiles: ["./vitest-setup-client.js"],
24
31
  reporters: ["default", "json"],
25
32
  outputFile: {
@@ -1,3 +1,10 @@
1
+ /* =========================================================================
2
+ vitest.config.server.js
3
+
4
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
5
+ This file is part of Network Pro.
6
+ ========================================================================= */
7
+
1
8
  import { sveltekit } from "@sveltejs/kit/vite";
2
9
  import lightningcssPlugin from "vite-plugin-lightningcss";
3
10
  import { defineConfig } from "vitest/config";
@@ -15,8 +22,8 @@ export default defineConfig({
15
22
  test: {
16
23
  name: "server",
17
24
  environment: "node",
18
- include: ["src/**/*.{test,spec}.{js,mjs}"],
19
- exclude: ["src/**/*.svelte.{test,spec}.{js,mjs}"],
25
+ include: ["tests/unit/**/*.test.{js,mjs}"],
26
+ exclude: ["tests/unit/**/*.svelte.test.{js,mjs}"],
20
27
  reporters: ["default", "json"],
21
28
  outputFile: {
22
29
  json: "./reports/server/results.json",