@networkpro/web 1.25.10 → 1.25.12

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/.env.template CHANGED
@@ -7,10 +7,11 @@
7
7
  # Custom environment mode for scripts and tooling
8
8
  # One of: dev, test, ci, audit, production
9
9
  ENV_MODE=dev
10
+ PUBLIC_ENV_MODE=dev
10
11
 
11
12
  # Optional: API keys or tokens for local dev (never commit real values)
12
- #VITE_PUBLIC_API_URL=https://api.example.com
13
- #VITE_PUBLIC_SITE_NAME=Network Pro
13
+ #PUBLIC_API_URL=https://api.example.com
14
+ #PUBLIC_SITE_NAME=Network Pro
14
15
 
15
- # ⚠️ DO NOT include sensitive credentials in VITE_ variables
16
+ # ⚠️ DO NOT include sensitive credentials in PUBLIC_ variables
16
17
  # These are exposed to client-side code at build time
package/.ncurc.cjs CHANGED
@@ -31,7 +31,13 @@ This file is part of Network Pro.
31
31
  /** @type {import('npm-check-updates').RunOptions} */
32
32
  module.exports = {
33
33
  // Ignore specific dependencies (prevent upgrades)
34
- reject: ['vitest', '@vitest/coverage-v8', 'prettier', 'jsdom'],
34
+ reject: [
35
+ 'vitest',
36
+ '@vitest/coverage-v8',
37
+ 'prettier',
38
+ 'jsdom',
39
+ 'markdownlint-cli2',
40
+ ],
35
41
 
36
42
  // Always upgrade devDependencies as well
37
43
  dep: 'prod,dev',
package/.node-version CHANGED
@@ -1 +1 @@
1
- 24.11.0
1
+ 24.11.1
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- 24.11.0
1
+ 24.11.1
package/CHANGELOG.md CHANGED
@@ -22,6 +22,48 @@ This project attempts to follow [Keep a Changelog](https://keepachangelog.com/en
22
22
 
23
23
  ---
24
24
 
25
+ ## [1.25.12] - 2025-11-14
26
+
27
+ ### Added
28
+
29
+ - Added revised **QR code** image assets for **Vcard** information:
30
+ - `src/lib/img/qr/vcard.png`
31
+ - `src/lib/img/qr/vcard.webp`
32
+
33
+ ### Changed
34
+
35
+ - Modified `.node-version` and `.nvmrc` to utilize **Node.js** `24.11.1` (LTS).
36
+ - Updated `.ncurc.cjs` to reject updates to `markdownlint-cli2`, due to discrepancies between in-editor and CLI linting errors.
37
+ - Updated environment template (`.env.template`) to include `PUBLIC_ENV_MODE`, which is now required to build the proper environment (e.g., `dev`, `audit`, `production`).
38
+ - Updated generator metadata in `src/app.html` to reflect **SvelteKit 2.48.5**.
39
+ - Bumped project version to `v1.25.12`.
40
+ - Updated dependencies:
41
+ - `@sveltejs/kit` `2.48.4` → `2.48.5`
42
+ - `eslint-plugin-jsdoc` `^61.2.0` → `^61.2.1`
43
+
44
+ ---
45
+
46
+ ## [1.25.11] - 2025-11-12
47
+
48
+ ### Added
49
+
50
+ - `gotoDesktop(page, path)` and `gotoMobile(page, path)` helper functions to streamline viewport + navigation setup.
51
+ - `clickAndWaitForNavigation(page, locator, options)` utility for safe SPA or full-page navigation detection with optional URL pattern matching.
52
+ - `DEBUG_LOGS` flag in `helpers.js` to allow toggling of console logs for test diagnostics.
53
+ - Navigation debug logs to `getVisibleNav()` to indicate which navigation region was detected (when debugging is enabled).
54
+
55
+ ### Changed
56
+
57
+ - Refactored all E2E tests to use `gotoDesktop()` and `gotoMobile()` for consistency and DRY principles.
58
+ - Replaced brittle direct `waitForNavigation()` usages with `clickAndWaitForNavigation()` helper.
59
+ - Updated mobile and desktop tests to improve consistency across specs and improve visibility assertions.
60
+
61
+ ### Removed
62
+
63
+ - Legacy direct `setViewportSize()` and `page.goto()` calls from individual test blocks (now handled via `goto*()` helpers).
64
+
65
+ ---
66
+
25
67
  ## [1.25.10] - 2025-11-12
26
68
 
27
69
  ### Changed
@@ -1855,7 +1897,9 @@ This enables analytics filtering and CSP hardening for the audit environment.
1855
1897
 
1856
1898
  <!-- Link references -->
1857
1899
 
1858
- [Unreleased]: https://github.com/netwk-pro/netwk-pro.github.io/compare/v1.25.10...HEAD
1900
+ [Unreleased]: https://github.com/netwk-pro/netwk-pro.github.io/compare/v1.25.12...HEAD
1901
+ [1.25.12]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.12
1902
+ [1.25.11]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.11
1859
1903
  [1.25.10]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.10
1860
1904
  [1.25.9]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.9
1861
1905
  [1.25.8]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.8
package/cspell.json CHANGED
@@ -14,6 +14,7 @@
14
14
  "CCPA",
15
15
  "CPRA",
16
16
  "cryptomator",
17
+ "domcontentloaded",
17
18
  "dont",
18
19
  "elif",
19
20
  "Embedder",
@@ -28,6 +29,7 @@
28
29
  "homescreen",
29
30
  "HREFTOP",
30
31
  "HSTS",
32
+ "interactable",
31
33
  "Izzy",
32
34
  "Keybase",
33
35
  "keypair",
@@ -40,6 +42,7 @@
40
42
  "lightningcss",
41
43
  "linksheet",
42
44
  "linktree",
45
+ "LOCALAPPDATA",
43
46
  "lsheet",
44
47
  "Mailvelope",
45
48
  "Maricopa",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@networkpro/web",
3
3
  "private": false,
4
- "version": "1.25.10",
4
+ "version": "1.25.12",
5
5
  "description": "Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro Strategies",
6
6
  "keywords": [
7
7
  "advisory",
@@ -54,6 +54,7 @@
54
54
  "verify": "npm run checkout",
55
55
  "delete": "rm -rf .svelte-kit node_modules package-lock.json",
56
56
  "clean": "npm run delete && npm cache clean --force && npm install",
57
+ "repair": "npm run delete && npx playwright uninstall && rm -rf %LOCALAPPDATA%/ms-playwright && npm install && npx playwright install",
57
58
  "upgrade": "ncu -u --format group --color",
58
59
  "check:updates": "ncu --format group --color",
59
60
  "test": "npm run test:all",
@@ -96,7 +97,7 @@
96
97
  "@playwright/test": "^1.56.1",
97
98
  "@sveltejs/adapter-netlify": "^5.2.4",
98
99
  "@sveltejs/adapter-vercel": "^6.1.1",
99
- "@sveltejs/kit": "2.48.4",
100
+ "@sveltejs/kit": "2.48.5",
100
101
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
101
102
  "@testing-library/jest-dom": "^6.9.1",
102
103
  "@testing-library/svelte": "^5.2.9",
@@ -105,13 +106,13 @@
105
106
  "browserslist": "^4.28.0",
106
107
  "eslint": "^9.39.1",
107
108
  "eslint-config-prettier": "^10.1.8",
108
- "eslint-plugin-jsdoc": "^61.2.0",
109
+ "eslint-plugin-jsdoc": "^61.2.1",
109
110
  "eslint-plugin-svelte": "^3.13.0",
110
111
  "globals": "^16.5.0",
111
112
  "jsdom": "26.1.0",
112
113
  "lightningcss": "^1.30.2",
113
114
  "markdownlint": "^0.39.0",
114
- "markdownlint-cli2": "^0.18.1",
115
+ "markdownlint-cli2": "0.18.1",
115
116
  "npm-run-all": "^4.1.5",
116
117
  "playwright": "^1.56.1",
117
118
  "postcss": "^8.5.6",
package/src/app.html CHANGED
@@ -53,7 +53,7 @@
53
53
  content="bx4ham0zkpvzztzu213bhpt76m9siq" />
54
54
  <!-- cspell:enable -->
55
55
 
56
- <meta name="generator" content="SvelteKit 2.48.4" />
56
+ <meta name="generator" content="SvelteKit 2.48.5" />
57
57
 
58
58
  <script src="/disableSw.js"></script>
59
59
 
Binary file
Binary file
@@ -11,15 +11,16 @@ This file is part of Network Pro.
11
11
  * @description Runs Playwright E2E tests with desktop and root route assertions.
12
12
  * @module tests/e2e
13
13
  * @author Scott Lopez
14
- * @updated 2025-10-21
14
+ * @updated 2025-11-12
15
15
  */
16
16
 
17
17
  import { expect, test } from '@playwright/test';
18
18
  import {
19
+ clickAndWaitForNavigation,
19
20
  getFooter,
20
21
  getVisibleNav,
21
- setDesktopView,
22
- setMobileView,
22
+ gotoDesktop,
23
+ gotoMobile,
23
24
  } from './shared/helpers.js';
24
25
 
25
26
  // Root route should display nav bar and about link
@@ -29,9 +30,7 @@ test.describe('Desktop Tests', () => {
29
30
  test("should display the navigation bar and 'about' link", async ({
30
31
  page,
31
32
  }) => {
32
- await setDesktopView(page);
33
- await page.goto('/');
34
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
33
+ await gotoDesktop(page);
35
34
 
36
35
  const nav = await getVisibleNav(page);
37
36
 
@@ -42,9 +41,7 @@ test.describe('Desktop Tests', () => {
42
41
 
43
42
  // Root route should display the footer properly
44
43
  test('should display the footer correctly', async ({ page }) => {
45
- await setDesktopView(page);
46
- await page.goto('/');
47
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
44
+ await gotoDesktop(page);
48
45
 
49
46
  const footer = getFooter(page);
50
47
  await expect(footer).toBeVisible();
@@ -54,9 +51,7 @@ test.describe('Desktop Tests', () => {
54
51
  test("should render the 'about' page with footer visible", async ({
55
52
  page,
56
53
  }) => {
57
- await setDesktopView(page);
58
- await page.goto('/about');
59
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
54
+ await gotoDesktop(page, '/about');
60
55
 
61
56
  const footer = getFooter(page);
62
57
  await expect(footer).toBeVisible();
@@ -64,20 +59,16 @@ test.describe('Desktop Tests', () => {
64
59
 
65
60
  // Root route should have a clickable "about" link
66
61
  test("should ensure the 'about' link is clickable", async ({ page }) => {
67
- await setDesktopView(page);
68
- await page.goto('/');
69
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
62
+ await gotoDesktop(page);
70
63
 
71
64
  const nav = await getVisibleNav(page);
72
65
  const aboutLink = nav.getByRole('link', { name: 'about' });
73
- await expect(aboutLink).toBeVisible();
74
- await aboutLink.click();
75
66
 
76
- // safer wait pattern with load state
77
- await Promise.all([
78
- page.waitForLoadState('load'),
79
- page.waitForURL('**/about', { timeout: 60000 }),
80
- ]);
67
+ await clickAndWaitForNavigation(page, aboutLink, {
68
+ urlPattern: /\/about/,
69
+ timeout: 60000,
70
+ });
71
+
81
72
  await expect(page).toHaveURL(/\/about/);
82
73
  });
83
74
  }); // End Desktop Tests
@@ -85,13 +76,10 @@ test.describe('Desktop Tests', () => {
85
76
  // Root route should display headings properly on mobile
86
77
  test.describe('Mobile Tests', () => {
87
78
  test('should display main content correctly on mobile', async ({ page }) => {
88
- await setMobileView(page);
89
- await page.goto('/');
90
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
79
+ await gotoMobile(page);
91
80
 
92
- const mainHeading = page.locator('h1, h2');
93
- await expect(mainHeading).toBeVisible();
81
+ // Ensure main headline is present on mobile
82
+ const h2 = page.locator('h2.index-title2');
83
+ await expect(h2).toContainText(/security/i);
94
84
  });
95
85
  });
96
-
97
- // cspell:ignore domcontentloaded
@@ -11,7 +11,7 @@ This file is part of Network Pro.
11
11
  * @description Runs Playwright E2E tests with mobile assertions.
12
12
  * @module tests/e2e
13
13
  * @author Scott Lopez
14
- * @updated 2025-10-21
14
+ * @updated 2025-11-12
15
15
  */
16
16
 
17
17
  import { expect, test } from '@playwright/test';
@@ -19,7 +19,7 @@ import {
19
19
  clickAndWaitForNavigation,
20
20
  getFooter,
21
21
  getVisibleNav,
22
- setMobileView,
22
+ gotoMobile,
23
23
  } from './shared/helpers.js';
24
24
 
25
25
  // ---------------------------------------------------------------------------
@@ -27,21 +27,20 @@ import {
27
27
  // ---------------------------------------------------------------------------
28
28
 
29
29
  test.describe('Mobile Tests', () => {
30
- // Increase timeout for all mobile tests
31
30
  test.setTimeout(90_000);
32
31
 
32
+ test.beforeEach(({ browserName }) => {
33
+ if (browserName === 'webkit')
34
+ test.skip('Skipping WebKit: manual validation only');
35
+ });
36
+
33
37
  // -------------------------------------------------------------------------
34
38
  // 🧱 Test 1: Main description text
35
39
  // -------------------------------------------------------------------------
36
40
  test('should display the main description text on mobile', async ({
37
41
  page,
38
- browserName,
39
42
  }) => {
40
- if (browserName === 'webkit') test.skip();
41
-
42
- await setMobileView(page);
43
- await page.goto('/');
44
- await page.waitForLoadState('domcontentloaded', { timeout: 60_000 });
43
+ await gotoMobile(page);
45
44
 
46
45
  const description = page.locator(
47
46
  'div.index-title1:has-text("Locking Down Networks")',
@@ -52,18 +51,11 @@ test.describe('Mobile Tests', () => {
52
51
  // -------------------------------------------------------------------------
53
52
  // 🧱 Test 2: Main content
54
53
  // -------------------------------------------------------------------------
55
- test('should display main content correctly on mobile', async ({
56
- page,
57
- browserName,
58
- }) => {
59
- if (browserName === 'webkit') test.skip();
60
-
61
- await setMobileView(page);
62
- await page.goto('/');
63
- await page.waitForLoadState('domcontentloaded', { timeout: 60_000 });
54
+ test('should display main content correctly on mobile', async ({ page }) => {
55
+ await gotoMobile(page);
64
56
 
65
- const mainHeading = page.locator('h1, h2');
66
- await expect(mainHeading).toBeVisible();
57
+ const mainHeading = page.locator('h2.index-title2');
58
+ await expect(mainHeading).toContainText(/security/i);
67
59
  });
68
60
 
69
61
  // -------------------------------------------------------------------------
@@ -71,41 +63,27 @@ test.describe('Mobile Tests', () => {
71
63
  // -------------------------------------------------------------------------
72
64
  test("should ensure the 'about' link is clickable on mobile", async ({
73
65
  page,
74
- browserName,
75
66
  }) => {
76
- if (browserName === 'webkit') test.skip();
77
-
78
- await setMobileView(page);
79
- await page.goto('/', { waitUntil: 'networkidle' });
67
+ await gotoMobile(page);
80
68
 
81
69
  const nav = await getVisibleNav(page);
82
-
83
- // Ensure the link exists before interacting
84
- await page.waitForSelector('a[href="/about"]', { timeout: 10_000 });
85
70
  const aboutLink = nav.getByRole('link', { name: 'about' });
86
71
 
87
- // Use the safe navigation helper
88
72
  await clickAndWaitForNavigation(page, aboutLink, {
89
73
  urlPattern: /\/about/,
90
74
  timeout: 60_000,
91
75
  });
76
+
77
+ await expect(page).toHaveURL(/\/about/);
92
78
  });
93
79
 
94
80
  // -------------------------------------------------------------------------
95
81
  // 🧱 Test 4: Footer presence on /about
96
82
  // -------------------------------------------------------------------------
97
- test('should display the footer on /about (mobile)', async ({
98
- page,
99
- browserName,
100
- }) => {
101
- if (browserName === 'webkit') test.skip();
102
-
103
- await setMobileView(page);
104
- await page.goto('/about');
83
+ test('should display the footer on /about (mobile)', async ({ page }) => {
84
+ await gotoMobile(page, '/about');
105
85
 
106
86
  const footer = getFooter(page);
107
87
  await expect(footer).toBeVisible();
108
88
  });
109
89
  });
110
-
111
- // cspell:ignore domcontentloaded networkidle
@@ -11,19 +11,19 @@ This file is part of Network Pro.
11
11
  * @description Stores commonly used functions for importing into E2E tests.
12
12
  * @module tests/e2e/shared
13
13
  * @author Scott Lopez
14
- * @updated 2025-10-29
14
+ * @updated 2025-11-12
15
15
  */
16
16
 
17
- import { expect } from '@playwright/test';
17
+ const DEBUG_LOGS = false; // set to true to enable console logs
18
18
 
19
19
  /**
20
- * @param {import('@playwright/test').Page} page - The Playwright page object.
20
+ * @param {import('@playwright/test').Page} page
21
21
  * @returns {Promise<void>}
22
- * @description Sets standard desktop viewport and waits for animations.
22
+ * @description Sets desktop viewport and allows layout to settle.
23
23
  */
24
24
  export async function setDesktopView(page) {
25
25
  await page.setViewportSize({ width: 1280, height: 720 });
26
- await page.waitForTimeout(1500);
26
+ await page.waitForTimeout(1500); // Known-stable stabilization delay
27
27
  }
28
28
 
29
29
  /**
@@ -36,18 +36,47 @@ export async function setMobileView(page) {
36
36
  }
37
37
 
38
38
  /**
39
- * @param {import('@playwright/test').Page} page - The Playwright page object.
40
- * @returns {Promise<import('@playwright/test').Locator>} - A visible navigation locator.
41
- * @throws {Error} If no visible navigation is found.
39
+ * Navigate with desktop viewport + initial DOM load.
40
+ * @param {import('@playwright/test').Page} page
41
+ * @param {string} path
42
+ */
43
+ export async function gotoDesktop(page, path = '/') {
44
+ await setDesktopView(page);
45
+ await page.goto(path, { waitUntil: 'domcontentloaded', timeout: 60000 });
46
+ }
47
+
48
+ /**
49
+ * Navigate with mobile viewport + initial DOM load.
50
+ * @param {import('@playwright/test').Page} page
51
+ * @param {string} path
52
+ */
53
+ export async function gotoMobile(page, path = '/') {
54
+ await setMobileView(page);
55
+ await page.goto(path, { waitUntil: 'domcontentloaded', timeout: 60000 });
56
+ }
57
+
58
+ /**
59
+ * Returns the visible navigation region.
60
+ * @param {import('@playwright/test').Page} page
61
+ * @returns {Promise<import('@playwright/test').Locator>}
42
62
  */
43
63
  export async function getVisibleNav(page) {
44
64
  const navHome = page.getByRole('navigation', { name: 'Homepage navigation' });
45
65
  const navMain = page.getByRole('navigation', { name: 'Main navigation' });
46
66
 
47
- if (await navHome.isVisible()) return navHome;
48
- if (await navMain.isVisible()) return navMain;
67
+ const timeout = 5000;
68
+
69
+ if (await navHome.isVisible({ timeout }).catch(() => false)) {
70
+ if (DEBUG_LOGS) console.log('✅ Detected visible nav: Homepage navigation');
71
+ return navHome;
72
+ }
49
73
 
50
- throw new Error('No visible navigation element found.');
74
+ if (await navMain.isVisible({ timeout }).catch(() => false)) {
75
+ if (DEBUG_LOGS) console.log('✅ Detected visible nav: Main navigation');
76
+ return navMain;
77
+ }
78
+
79
+ throw new Error('❌ No visible navigation element found within timeout.');
51
80
  }
52
81
 
53
82
  /**
@@ -59,37 +88,28 @@ export function getFooter(page) {
59
88
  }
60
89
 
61
90
  /**
62
- * @function clickAndWaitForNavigation
63
- * @description Clicks a link or button and waits for navigation or URL change.
64
- * Works for both SPA (client-side) and full-page navigations.
91
+ * Click + wait for SPA or full navigation event.
65
92
  *
66
93
  * @param {import('@playwright/test').Page} page
67
94
  * @param {import('@playwright/test').Locator} locator
68
- * @param {object} [options]
69
- * @param {string|RegExp} [options.urlPattern] - URL or regex to match
70
- * @param {number} [options.timeout=60000] - Max wait time
95
+ * @param {{ urlPattern?: string | RegExp, timeout?: number }} [options]
71
96
  */
72
97
  export async function clickAndWaitForNavigation(page, locator, options = {}) {
73
98
  const { urlPattern = /\/.*/, timeout = 60000 } = options;
74
99
 
75
- // Ensure the element is ready for interaction
76
100
  await locator.scrollIntoViewIfNeeded();
77
- await locator.waitFor({ state: 'visible', timeout: 10000 });
78
- await locator.click({ trial: true });
101
+ await locator.waitFor({ state: 'visible', timeout: 60000 });
79
102
 
80
- // Capture current URL to detect SPA navigation
81
- const currentUrl = page.url();
103
+ const previousURL = page.url();
82
104
 
83
- // Handle both SPA transitions and full page reloads
84
- await Promise.all([
85
- page.waitForURL(urlPattern, { timeout }).catch(() => {}),
86
- page.waitForLoadState('load', { timeout }).catch(() => {}),
87
- locator.click(),
105
+ const [, newURL] = await Promise.all([
106
+ page.waitForURL(
107
+ (url) =>
108
+ url.toString() !== previousURL && urlPattern.test(url.toString()),
109
+ { timeout },
110
+ ),
111
+ locator.click().then(() => page.url()),
88
112
  ]);
89
113
 
90
- // Confirm the URL changed successfully
91
- await expect(page).toHaveURL(urlPattern, { timeout });
92
-
93
- // Optional: log navigation success (helps in CI)
94
- console.log(`✅ Navigation from ${currentUrl} → ${page.url()}`);
114
+ console.log(`✅ Navigation from ${previousURL} ${newURL}`);
95
115
  }
package/vite.config.js CHANGED
@@ -12,7 +12,7 @@ import { fileURLToPath } from 'node:url';
12
12
  import { defineConfig } from 'vite';
13
13
  import devtoolsJson from 'vite-plugin-devtools-json';
14
14
  import lightningcssPlugin from 'vite-plugin-lightningcss';
15
- import tsconfigPaths from 'vite-tsconfig-paths'; // NEW: tsconfig/jsconfig alias support
15
+ import tsconfigPaths from 'vite-tsconfig-paths'; // tsconfig/jsconfig alias support
16
16
 
17
17
  // Compute absolute project root
18
18
  const projectRoot = fileURLToPath(new URL('.', import.meta.url));