diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..9e4d863 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + labels: + - dependencies + - security + + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + open-pull-requests-limit: 3 + labels: + - dependencies diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index a02ba3f..009fa2d 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -31,7 +31,8 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' + cache: npm # Windows-specific: Clean any existing node_modules to avoid EPERM errors - name: Clean node_modules (Windows) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7752b6a..7621cf5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,14 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' + cache: npm + + - name: Verify dependency lockfile + run: npm ci --ignore-scripts + + - name: Audit dependencies + run: npm audit --audit-level=high - name: Validate JSON files run: | @@ -23,16 +30,10 @@ jobs: node -e "JSON.parse(require('fs').readFileSync('$f', 'utf8'))" done - - name: Check inline script syntax in organization-network-map.html + - name: Check public map JavaScript syntax run: | - node -e " - const fs = require('fs'); - const html = fs.readFileSync('organization-network-map.html', 'utf8'); - const match = html.match(/ + diff --git a/admin/index.html b/admin/index.html index ed5e03e..660ba8a 100644 --- a/admin/index.html +++ b/admin/index.html @@ -3,6 +3,11 @@ + + Praxium - Allies and Partners: Admin Login diff --git a/api/_lib/githubGist.js b/api/_lib/githubGist.js index 69c8ec0..3cd8829 100644 --- a/api/_lib/githubGist.js +++ b/api/_lib/githubGist.js @@ -1,6 +1,7 @@ // Thin wrapper around the GitHub Gist REST API. Uses the global `fetch` available in the -// Node.js 18+ runtime — no dependency needed for two REST calls. +// Modern Node.js runtime — no dependency needed for two REST calls. const DATA_FILENAME = 'organization-network-map.json'; +const GITHUB_API_VERSION = '2022-11-28'; function getConfig() { const gistId = process.env.GIST_ID; @@ -15,11 +16,23 @@ function authHeaders(token) { Authorization: `Bearer ${token}`, 'User-Agent': 'praxium-interactive-map-admin', Accept: 'application/vnd.github+json', + 'X-GitHub-Api-Version': GITHUB_API_VERSION, }; } +function getGistVersion(gist) { + const revision = gist + && Array.isArray(gist.history) + && gist.history[0] + && gist.history[0].version; + if (typeof revision === 'string' && revision) return revision; + if (gist && typeof gist.updated_at === 'string' && gist.updated_at) return gist.updated_at; + throw new Error('GitHub Gist response did not include a version'); +} + // Fetches the Gist and returns { organizations, version, raw }. -// `version` is the Gist's `updated_at` timestamp, used for optimistic concurrency checks. +// `version` prefers GitHub's immutable revision id over the lower-resolution +// updated_at timestamp, making optimistic concurrency checks more reliable. async function fetchGist() { const { gistId, token } = getConfig(); const resp = await fetch(`https://api.github.com/gists/${gistId}`, { @@ -37,7 +50,12 @@ async function fetchGist() { // this dataset's scale, but fetch raw_url as a fallback so publish never silently truncates. let content = file.content; if (file.truncated && file.raw_url) { - const rawResp = await fetch(file.raw_url, { headers: { 'User-Agent': authHeaders(token)['User-Agent'] } }); + const rawResp = await fetch(file.raw_url, { + headers: { + Authorization: `Bearer ${token}`, + 'User-Agent': authHeaders(token)['User-Agent'], + }, + }); if (!rawResp.ok) throw new Error(`Failed to fetch truncated Gist file: ${rawResp.status}`); content = await rawResp.text(); } @@ -52,7 +70,7 @@ async function fetchGist() { throw new Error('Gist file content must be a JSON array of organizations'); } - return { organizations, version: gist.updated_at, raw: content }; + return { organizations, version: getGistVersion(gist), raw: content }; } // Overwrites the Gist's data file with a new array of organizations. @@ -72,7 +90,7 @@ async function updateGist(organizations) { throw new Error(`GitHub Gist API returned ${resp.status} on update`); } const gist = await resp.json(); - return { organizations, version: gist.updated_at }; + return { organizations, version: getGistVersion(gist) }; } module.exports = { fetchGist, updateGist, DATA_FILENAME }; diff --git a/api/_lib/http.js b/api/_lib/http.js index e22b805..b8e76cf 100644 --- a/api/_lib/http.js +++ b/api/_lib/http.js @@ -13,7 +13,12 @@ function readJsonBody(req) { } function sendJson(res, status, payload) { - res.status(status).setHeader('Content-Type', 'application/json').end(JSON.stringify(payload)); + res + .status(status) + .setHeader('Content-Type', 'application/json; charset=utf-8') + .setHeader('Cache-Control', 'no-store') + .setHeader('X-Content-Type-Options', 'nosniff') + .end(JSON.stringify(payload)); } function methodNotAllowed(res, allowed) { diff --git a/api/_lib/rateLimit.js b/api/_lib/rateLimit.js index ddd4253..d3580cb 100644 --- a/api/_lib/rateLimit.js +++ b/api/_lib/rateLimit.js @@ -3,18 +3,28 @@ // small trusted-board internal tool. Do not rely on this alone for a public-facing surface. const buckets = new Map(); -// Returns true if the caller (keyed by e.g. IP) is within the allowed rate. -function checkRateLimit(key, { max, windowMs }) { +function recentAttempts(key, windowMs) { const now = Date.now(); - const entry = buckets.get(key) || []; - const recent = entry.filter((ts) => now - ts < windowMs); - if (recent.length >= max) { - buckets.set(key, recent); - return false; - } - recent.push(now); + const recent = (buckets.get(key) || []).filter((ts) => now - ts < windowMs); + if (recent.length) buckets.set(key, recent); + else buckets.delete(key); + return recent; +} + +// Returns true if the caller (keyed by e.g. IP) may make another attempt. +// Checking does not consume the budget; only failed authentication should. +function checkRateLimit(key, { max, windowMs }) { + return recentAttempts(key, windowMs).length < max; +} + +function recordRateLimitFailure(key, { windowMs }) { + const recent = recentAttempts(key, windowMs); + recent.push(Date.now()); buckets.set(key, recent); - return true; +} + +function clearRateLimit(key) { + buckets.delete(key); } function getClientIp(req) { @@ -34,4 +44,10 @@ async function throttleGeocodeCall(minIntervalMs) { lastGeocodeCallAt = Date.now(); } -module.exports = { checkRateLimit, getClientIp, throttleGeocodeCall }; +module.exports = { + checkRateLimit, + recordRateLimitFailure, + clearRateLimit, + getClientIp, + throttleGeocodeCall, +}; diff --git a/api/_lib/session.js b/api/_lib/session.js index e777750..4303937 100644 --- a/api/_lib/session.js +++ b/api/_lib/session.js @@ -9,6 +9,9 @@ const SESSION_TTL_MS = 4 * 60 * 60 * 1000; // 4 hours function getSecret() { const secret = process.env.SESSION_SECRET; if (!secret) throw new Error('SESSION_SECRET is not configured'); + if (Buffer.byteLength(secret, 'utf8') < 32) { + throw new Error('SESSION_SECRET must be at least 32 bytes'); + } return secret; } diff --git a/api/auth.js b/api/auth.js index 4e84aa2..37da147 100644 --- a/api/auth.js +++ b/api/auth.js @@ -1,6 +1,11 @@ const crypto = require('crypto'); const { createSessionToken, buildSessionCookie } = require('./_lib/session'); -const { checkRateLimit, getClientIp } = require('./_lib/rateLimit'); +const { + checkRateLimit, + recordRateLimitFailure, + clearRateLimit, + getClientIp, +} = require('./_lib/rateLimit'); const { readJsonBody, sendJson, methodNotAllowed } = require('./_lib/http'); const MAX_ATTEMPTS = 5; @@ -18,7 +23,8 @@ module.exports = async (req, res) => { if (req.method !== 'POST') return methodNotAllowed(res, ['POST']); const ip = getClientIp(req); - if (!checkRateLimit(`auth:${ip}`, { max: MAX_ATTEMPTS, windowMs: WINDOW_MS })) { + const rateLimitKey = `auth:${ip}`; + if (!checkRateLimit(rateLimitKey, { max: MAX_ATTEMPTS, windowMs: WINDOW_MS })) { return sendJson(res, 429, { ok: false, error: 'too_many_attempts' }); } @@ -29,9 +35,11 @@ module.exports = async (req, res) => { } if (typeof passphrase !== 'string' || !passphrase || !constantTimeEquals(passphrase, expected)) { + recordRateLimitFailure(rateLimitKey, { windowMs: WINDOW_MS }); return sendJson(res, 401, { ok: false, error: 'invalid_passphrase' }); } + clearRateLimit(rateLimitKey); const token = createSessionToken(); res.setHeader('Set-Cookie', buildSessionCookie(token)); return sendJson(res, 200, { ok: true }); diff --git a/api/geocode.js b/api/geocode.js index b778a6f..f3a5056 100644 --- a/api/geocode.js +++ b/api/geocode.js @@ -6,7 +6,8 @@ const { readJsonBody, sendJson, methodNotAllowed } = require('./_lib/http'); // User-Agent. This function is the *only* place that ever talks to Nominatim — the browser // never calls it directly. const NOMINATIM_MIN_INTERVAL_MS = 1100; -const USER_AGENT = 'Praxium-Map-Admin/1.0 (contact: hezekiahj@praxiumfoundation.com)'; +const MAX_ADDRESS_LENGTH = 300; +const USER_AGENT = 'Praxium-Map-Admin/1.0 (contact: info@praxiumfoundation.com)'; module.exports = async (req, res) => { if (req.method !== 'POST') return methodNotAllowed(res, ['POST']); @@ -16,6 +17,9 @@ module.exports = async (req, res) => { if (typeof address !== 'string' || !address.trim()) { return sendJson(res, 400, { error: 'address_required' }); } + if (address.trim().length > MAX_ADDRESS_LENGTH) { + return sendJson(res, 400, { error: 'address_too_long' }); + } await throttleGeocodeCall(NOMINATIM_MIN_INTERVAL_MS); @@ -30,10 +34,22 @@ module.exports = async (req, res) => { return sendJson(res, 404, { error: 'not_found' }); } const best = results[0]; + const lat = Number(best.lat); + const lng = Number(best.lon); + if ( + !Number.isFinite(lat) + || !Number.isFinite(lng) + || lat < -90 + || lat > 90 + || lng < -180 + || lng > 180 + ) { + return sendJson(res, 502, { error: 'geocode_service_error' }); + } return sendJson(res, 200, { - lat: Number(best.lat), - lng: Number(best.lon), - displayName: best.display_name, + lat, + lng, + displayName: typeof best.display_name === 'string' ? best.display_name : address.trim(), }); } catch (err) { console.error('POST /api/geocode failed:', err); diff --git a/api/publish.js b/api/publish.js index c14f365..1b33754 100644 --- a/api/publish.js +++ b/api/publish.js @@ -4,6 +4,16 @@ const { readCategories } = require('./_lib/categories'); const { readJsonBody, sendJson, methodNotAllowed } = require('./_lib/http'); const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; +const FIELD_LIMITS = Object.freeze({ + name: 200, + location: 300, + country: 100, + description: 500, + relationship: 500, + website: 2048, + email: 254, + phone: 50, +}); function isValidUrl(value) { try { @@ -24,8 +34,30 @@ function validateOrgPayload(org, categories) { if (!org || typeof org !== 'object') return ['org payload is required']; if (typeof org.name !== 'string' || !org.name.trim()) errors.push('name is required'); + else if (org.name.trim().length > FIELD_LIMITS.name) { + errors.push(`name must be ${FIELD_LIMITS.name} characters or fewer`); + } if (typeof org.location !== 'string' || !org.location.trim()) errors.push('location is required'); + else if (org.location.trim().length > FIELD_LIMITS.location) { + errors.push(`location must be ${FIELD_LIMITS.location} characters or fewer`); + } if (typeof org.description !== 'string' || !org.description.trim()) errors.push('description is required'); + else if (org.description.trim().length > FIELD_LIMITS.description) { + errors.push(`description must be ${FIELD_LIMITS.description} characters or fewer`); + } + if (org.country !== undefined && typeof org.country !== 'string') { + errors.push('country must be a string'); + } else if (typeof org.country === 'string' && org.country.trim().length > FIELD_LIMITS.country) { + errors.push(`country must be ${FIELD_LIMITS.country} characters or fewer`); + } + if (org.relationship !== undefined && typeof org.relationship !== 'string') { + errors.push('relationship must be a string'); + } else if ( + typeof org.relationship === 'string' + && org.relationship.trim().length > FIELD_LIMITS.relationship + ) { + errors.push(`relationship must be ${FIELD_LIMITS.relationship} characters or fewer`); + } const coords = org.coordinates; if ( @@ -46,15 +78,36 @@ function validateOrgPayload(org, categories) { } const website = org.contact && org.contact.website; - if (typeof website !== 'string' || !website.trim() || !isValidUrl(website)) { + if ( + typeof website !== 'string' + || !website.trim() + || website.trim().length > FIELD_LIMITS.website + || !isValidUrl(website) + ) { errors.push('contact.website is required and must be a valid http(s) URL'); } const email = org.contact && org.contact.email; - if (email !== undefined && email !== '' && !EMAIL_RE.test(String(email))) { + if ( + email !== undefined + && email !== '' + && ( + String(email).trim().length > FIELD_LIMITS.email + || !EMAIL_RE.test(String(email).trim()) + ) + ) { errors.push('contact.email is not a valid email address'); } + const phone = org.contact && org.contact.phone; + if ( + phone !== undefined + && phone !== '' + && String(phone).trim().length > FIELD_LIMITS.phone + ) { + errors.push(`contact.phone must be ${FIELD_LIMITS.phone} characters or fewer`); + } + if (org.connections !== undefined) { if (!Array.isArray(org.connections) || !org.connections.every((id) => Number.isInteger(id))) { errors.push('connections must be an array of integer organization IDs'); @@ -65,13 +118,14 @@ function validateOrgPayload(org, categories) { } // Builds the finalized org record (only the fields we persist) from a validated payload. -function finalizeOrgFields(org, id) { +function finalizeOrgFields(org, id, existing = {}) { const contact = {}; if (org.contact && org.contact.website) contact.website = org.contact.website.trim(); if (org.contact && org.contact.email) contact.email = org.contact.email.trim(); if (org.contact && org.contact.phone) contact.phone = String(org.contact.phone).trim(); - return { + const finalized = { + ...existing, id, name: org.name.trim(), location: org.location.trim(), @@ -81,6 +135,20 @@ function finalizeOrgFields(org, id) { contact, connections: Array.isArray(org.connections) ? [...new Set(org.connections)].filter((cid) => cid !== id) : [], }; + + if (Object.prototype.hasOwnProperty.call(org, 'country')) { + const country = typeof org.country === 'string' ? org.country.trim() : ''; + if (country) finalized.country = country; + else delete finalized.country; + } + if (Object.prototype.hasOwnProperty.call(org, 'relationship')) { + const relationship = typeof org.relationship === 'string' ? org.relationship.trim() : ''; + if (relationship) finalized.relationship = relationship; + else delete finalized.relationship; + } + delete finalized.allowCustomCategory; + + return finalized; } // Keeps `connections` symmetric: if A links to B, B should link back to A. Given the prior @@ -96,15 +164,17 @@ function applyReciprocalConnections(organizations, orgId, previousConnections, n added.forEach((otherId) => { const other = byId.get(otherId); - if (other && !other.connections.includes(orgId)) { - other.connections = [...other.connections, orgId]; + if (other) { + const connections = Array.isArray(other.connections) ? other.connections : []; + if (!connections.includes(orgId)) other.connections = [...connections, orgId]; } }); removed.forEach((otherId) => { const other = byId.get(otherId); if (other) { - other.connections = other.connections.filter((cid) => cid !== orgId); + const connections = Array.isArray(other.connections) ? other.connections : []; + other.connections = connections.filter((cid) => cid !== orgId); } }); } @@ -151,18 +221,32 @@ module.exports = async (req, res) => { let resultOrgId = null; if (mode === 'delete') { + if (!Number.isInteger(orgId)) return sendJson(res, 400, { error: 'organization_id_required' }); const idx = organizations.findIndex((o) => o.id === orgId); if (idx === -1) return sendJson(res, 404, { error: 'organization_not_found' }); organizations.splice(idx, 1); stripConnectionsTo(organizations, orgId); } else { const errors = validateOrgPayload(org, categories); + const knownIds = new Set(organizations.map((organization) => organization.id)); + if (mode === 'update' && !Number.isInteger(orgId)) { + errors.push('organization ID is required for updates'); + } + if (Array.isArray(org && org.connections)) { + const unknownIds = [...new Set(org.connections)].filter((id) => !knownIds.has(id)); + if (unknownIds.length) { + errors.push(`connections reference unknown organization IDs: ${unknownIds.join(', ')}`); + } + } if (errors.length) { return sendJson(res, 400, { error: 'validation_failed', details: errors }); } if (mode === 'create') { - const nextId = organizations.length ? Math.max(...organizations.map((o) => o.id)) + 1 : 1; + const validIds = organizations + .map((organization) => organization.id) + .filter((id) => Number.isSafeInteger(id) && id > 0); + const nextId = validIds.length ? Math.max(...validIds) + 1 : 1; const finalized = finalizeOrgFields(org, nextId); organizations.push(finalized); applyReciprocalConnections(organizations, nextId, [], finalized.connections); @@ -172,7 +256,7 @@ module.exports = async (req, res) => { const idx = organizations.findIndex((o) => o.id === orgId); if (idx === -1) return sendJson(res, 404, { error: 'organization_not_found' }); const previousConnections = organizations[idx].connections || []; - const finalized = finalizeOrgFields(org, orgId); + const finalized = finalizeOrgFields(org, orgId, organizations[idx]); organizations[idx] = finalized; applyReciprocalConnections(organizations, orgId, previousConnections, finalized.connections); resultOrgId = orgId; diff --git a/docs/PROFESSIONALIZATION_ROADMAP.md b/docs/PROFESSIONALIZATION_ROADMAP.md new file mode 100644 index 0000000..3d5a0f8 --- /dev/null +++ b/docs/PROFESSIONALIZATION_ROADMAP.md @@ -0,0 +1,163 @@ +# Praxium Network Professionalization Roadmap + +Last reviewed: September 6, 2026 + +## Goal + +Present a credible, human-centered picture of Praxium's reach while keeping the +publishing workflow simple for board members and inexpensive to operate. + +The guiding rule is: **show only impact that the data can support**. The +interface may be visually ambitious, but organization counts, countries, +relationships, and stories must remain verifiable. + +## Current architecture + +| Surface | Current role | Recommended direction | +| --- | --- | --- | +| GitHub Pages | Public, embeddable map | Keep it. Static hosting is an appropriate low-cost boundary for public content. | +| GitHub Gist | Public organization dataset | Keep during the first Azure migration so the public data contract does not change. | +| Vercel deployment | Board admin and Node serverless handlers | Migrate the authenticated admin/API to Azure after staging tests pass. | +| Electron editor | Offline and bulk-edit fallback | Retain as a recovery path, not the primary board workflow. | + +## Improvements implemented on this branch + +### 1. Professional public experience + +- Reframed the map as an impact and partnership story rather than a technical + directory. +- Added a branded, responsive visual system that works as a full page or iframe. +- Aligned the visual system with the foundation website's shared paper, ink, + clay, Epilogue, Poppins, spacing, focus, button, and form-field tokens. +- Added data-derived organization, focus-area, place, and relationship metrics. +- Added a mobile-first map experience with an explorer drawer, legend, and + expand/fullscreen behavior. +- Improved organization cards and popups with clear visual hierarchy and + optional "How Praxium Connects" context. + +### 2. Trust and accessibility + +- Organization-provided values are rendered with DOM text nodes instead of + executable HTML. +- Unsafe website schemes and invalid organization records are ignored. +- Keyboard focus, labels, result announcements, reduced-motion handling, and + touch targets are included. +- Stale cached data is clearly identified instead of silently presented as live. +- Leaflet CDN files are version-pinned and protected with Subresource Integrity. +- A restrictive Content Security Policy limits executable and network resources. + +### 3. Data quality and relationships + +- A shared data utility normalizes categories, coordinates, IDs, contacts, and + relationships. +- Connection pairs are de-duplicated even when older data records only one side + of a relationship. +- Unknown, duplicate, and self-referential relationships are rejected or + safely ignored. +- The web and offline editors can now maintain `country`, `relationship`, + email, phone, and reciprocal relationship data. + +### 4. Delivery quality + +- Public CSS, JavaScript, and data utilities are separate assets instead of one + large inline HTML file. +- GitHub Pages deploys only the assets required by the public map. +- CI checks the new JavaScript files and runs public data and API tests. +- Local `npm run validate` runs syntax checks and all test suites. + +## Content work that unlocks the strongest impact story + +The live data should be enriched before adding more claims or dashboards. + +1. Add an explicit `country` to every organization. +2. Write a short, factual `relationship` statement describing the shared + project, referral, learning activity, or collaboration. +3. Add `connections` only where a relationship can be substantiated. +4. Standardize categories that currently describe the same sector + (`Non-Profit` and `Not-For-Profit`, for example). +5. Review public descriptions and links at least twice per year. +6. Establish a board owner for data quality and a lightweight review date. + +Once those fields are maintained, future enhancements can responsibly include: + +- country-level impact totals; +- a "featured collaboration" story mode; +- relationship density and cross-sector collaboration summaries; +- a timeline of new partners; +- accessible organization detail pages that are linkable from the map; +- privacy-respecting aggregate analytics for searches and organization clicks. + +## Azure migration plan + +Eligible nonprofits currently receive a **$2,000 USD annual Azure grant**. The +grant must be renewed and usage beyond the credit can become pay-as-you-go, so +cost controls are part of the migration rather than an afterthought. + +Official references: + +- [Microsoft nonprofit offerings](https://learn.microsoft.com/en-us/industry/nonprofit/microsoft-for-nonprofits/nonprofit-offerings-products) +- [Azure grant renewal guidance](https://learn.microsoft.com/en-us/industry/nonprofit/microsoft-for-nonprofits/renew-azure-grant) +- [Azure Static Web Apps authentication](https://learn.microsoft.com/en-us/azure/static-web-apps/authentication-authorization) +- [Azure Static Web Apps application settings](https://learn.microsoft.com/en-us/azure/static-web-apps/application-settings) + +### Phase A — prepare without changing production + +- Confirm the nonprofit Azure sponsorship is active and note its renewal date. +- Create separate resource groups for production and staging. +- Add a conservative Azure budget and alerts well below the grant limit. +- Inventory the four existing secrets: `GITHUB_PAT`, `GIST_ID`, + `ADMIN_PASSPHRASE`, and `SESSION_SECRET`. +- Use a test Gist in staging; never test write behavior against production data. + +### Phase B — port the admin/API + +- Host the `admin/` frontend in Azure Static Web Apps. +- Port the Vercel handler adapters to Azure Functions while preserving the + already-tested validation and Gist logic. +- Keep configuration in Azure application settings. These are exposed to the + backend as environment variables and are encrypted at rest. +- Protect admin routes and API routes with Microsoft Entra ID. Restrict sign-in + to the Praxium tenant or an explicit administrator role rather than allowing + any authenticated Microsoft account. +- Retain the shared passphrase only as a short-lived migration fallback, then + remove it after Entra access is verified. + +### Phase C — staging acceptance tests + +- Sign in and sign out with each intended board account. +- Verify unauthorized users cannot read or call admin API routes. +- Read the test Gist and geocode a sample address. +- Create, edit, connect, disconnect, and delete a test organization. +- Open the same record in two tabs and confirm the stale tab receives a + conflict instead of overwriting newer data. +- Confirm logs do not contain passphrases, tokens, full request bodies, or + private contact information. +- Confirm the GitHub Pages public map still reads the unchanged Gist contract. + +### Phase D — production cutover + +- Configure production settings and the production Gist only after staging + passes. +- Give the board the Azure admin URL and revoke or remove the Vercel deployment + once a rollback window has passed. +- Keep GitHub Pages as the public iframe host; moving a static map into Azure + provides little benefit by itself. +- Document one owner for Azure billing/renewal and one owner for map content. + +### Later data-platform option + +Moving from a Gist to Azure Storage, Cosmos DB, or another database should be a +separate project. It becomes worthwhile when Praxium needs approval workflows, +per-user audit history, structured reporting, media assets, or significantly +larger datasets. Do not combine that change with the initial hosting migration. + +## Definition of done for the next release + +- The feature branch passes `npm run validate`. +- The GitHub Pages artifact contains every referenced public asset and no admin + or secret-bearing files. +- Desktop and phone layouts are visually checked in a real browser. +- Board members can create a record with country, relationship context, and + named connections without editing JSON. +- The iframe uses a descriptive `title` and `allow="fullscreen"`. +- The live content owner has reviewed every public record for accuracy. diff --git a/electron/README.md b/electron/README.md index e04e1c4..6c9cc00 100644 --- a/electron/README.md +++ b/electron/README.md @@ -4,7 +4,7 @@ This folder contains the Electron configuration for packaging the admin editor a ## Development -1. Install dependencies: +1. Install Node.js 22.12 or newer, then install dependencies: ```bash npm install ``` @@ -39,10 +39,11 @@ Built packages will be output to the `dist/` folder. ## Icons -Place application icons in the `icons/` folder: -- `icon.png` - 512x512 PNG (used for Linux and as source) +Application icons live in the `icons/` folder: +- `icon.png` - 1024x1024 PNG (used for Linux and as source) - `icon.ico` - Windows icon - `icon.icns` - macOS icon +- `icon.svg` - editable high-resolution source artwork You can generate these from a PNG using tools like: - [electron-icon-maker](https://www.npmjs.com/package/electron-icon-maker) diff --git a/electron/icons/icon.icns b/electron/icons/icon.icns new file mode 100644 index 0000000..d238224 Binary files /dev/null and b/electron/icons/icon.icns differ diff --git a/electron/icons/icon.ico b/electron/icons/icon.ico new file mode 100644 index 0000000..d390906 Binary files /dev/null and b/electron/icons/icon.ico differ diff --git a/electron/icons/icon.png b/electron/icons/icon.png new file mode 100644 index 0000000..e1e2243 Binary files /dev/null and b/electron/icons/icon.png differ diff --git a/electron/icons/icon.svg b/electron/icons/icon.svg new file mode 100644 index 0000000..ac766e9 --- /dev/null +++ b/electron/icons/icon.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + P + + diff --git a/electron/main.js b/electron/main.js index 138bd61..76f43e3 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1,6 +1,7 @@ const { app, BrowserWindow, ipcMain, dialog, Menu } = require('electron'); const path = require('path'); const fs = require('fs'); +const { pathToFileURL } = require('url'); let mainWindow; @@ -13,13 +14,25 @@ function createWindow() { webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, - nodeIntegration: false + nodeIntegration: false, + sandbox: true, + webSecurity: true }, icon: path.join(__dirname, 'icons', 'icon.png') }); // Load the admin editor - mainWindow.loadFile(path.join(__dirname, '..', 'admin-editor', 'index.html')); + const editorPath = path.join(__dirname, '..', 'admin-editor', 'index.html'); + const editorUrl = pathToFileURL(editorPath).href; + mainWindow.loadFile(editorPath); + + // The editor is a local-only utility. Prevent a changed dataset or future + // markup from navigating the privileged desktop window to remote content. + mainWindow.webContents.setWindowOpenHandler(() => ({ action: 'deny' })); + mainWindow.webContents.on('will-navigate', (event, targetUrl) => { + if (targetUrl !== editorUrl) event.preventDefault(); + }); + mainWindow.webContents.on('will-attach-webview', (event) => event.preventDefault()); // Build application menu const menuTemplate = [ @@ -161,6 +174,9 @@ function loadSampleData() { // IPC handlers ipcMain.handle('save-file', async (event, { filePath, content }) => { + if (typeof filePath !== 'string' || !filePath || typeof content !== 'string') { + return { success: false, error: 'Invalid save request.' }; + } try { fs.writeFileSync(filePath, content, 'utf-8'); return { success: true }; @@ -196,6 +212,7 @@ ipcMain.handle('show-message', async (event, { type, title, message }) => { }); app.whenReady().then(() => { + if (process.platform === 'win32') app.setAppUserModelId('com.praxium.mapeditor'); createWindow(); app.on('activate', () => { diff --git a/map.css b/map.css new file mode 100644 index 0000000..682e5f5 --- /dev/null +++ b/map.css @@ -0,0 +1,1485 @@ +:root { + color-scheme: light; + --map-shadow-sm: 0 3px 12px rgba(62, 61, 61, 0.1); + --map-shadow-lg: 0 12px 36px rgba(62, 61, 61, 0.18); + --map-hero-surface: var(--surface-inverse); + --map-hero-text: var(--text-on-inverse); + --map-hero-muted: var(--ink-300); + --map-hero-accent: var(--clay-300); + --map-canvas: var(--paper-200); + --map-overlay: rgba(62, 61, 61, 0.58); + --sidebar-width: clamp(350px, 31vw, 430px); +} + +body.dark { + color-scheme: dark; + --surface: var(--ink-900); + --surface-sunken: #333232; + --surface-raised: var(--ink-700); + --surface-accent: var(--clay-800); + --surface-inverse: var(--paper-100); + + --text: var(--paper-100); + --text-muted: var(--ink-200); + --text-display: var(--paper-000); + --text-accent: var(--clay-200); + --text-on-accent: var(--paper-000); + --text-on-inverse: var(--ink-900); + + --interactive: var(--clay-200); + --interactive-hover: var(--paper-000); + --interactive-text: var(--ink-900); + --link: var(--interactive); + --link-hover: var(--interactive-hover); + + --border-subtle: var(--ink-500); + --border-strong: var(--ink-300); + --divider: var(--clay-300); + --focus-ring: var(--paper-100); + + --status-error: #f0aaa1; + --status-error-bg: #542b27; + --status-success: #b9d8c0; + --status-success-bg: #2f4033; + --status-warning: #ecd4a4; + --status-warning-bg: #4c4028; + + --map-shadow-sm: 0 3px 14px rgba(0, 0, 0, 0.28); + --map-shadow-lg: 0 14px 42px rgba(0, 0, 0, 0.42); + --map-hero-surface: #333232; + --map-hero-text: var(--paper-100); + --map-hero-muted: var(--ink-300); + --map-hero-accent: var(--clay-300); + --map-canvas: #2d2c2c; + --map-overlay: rgba(0, 0, 0, 0.58); +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html, +body { + width: 100%; + min-width: 0; + height: 100%; + margin: 0; +} + +body { + overflow: hidden; + background: var(--surface); + color: var(--text); + font-family: var(--font-text); + font-size: 16px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +button, +input, +select, +a { + font: inherit; +} + +button, +a { + -webkit-tap-highlight-color: transparent; +} + +button { + color: inherit; +} + +button:focus-visible, +a:focus-visible, +input:focus-visible, +select:focus-visible, +[tabindex]:focus-visible { + outline: var(--focus-width) solid var(--focus-ring); + outline-offset: var(--focus-offset); +} + +[hidden] { + display: none !important; +} + +.skip-link { + position: fixed; + top: 10px; + left: 10px; + z-index: 5000; + padding: 10px 14px; + border-radius: var(--radius-btn); + background: var(--surface-raised); + color: var(--text); + font-weight: 500; + text-decoration: none; + transform: translateY(-180%); + transition: transform 160ms ease; +} + +.skip-link:focus { + transform: translateY(0); +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.mobile-only { + display: none; +} + +#map-shell { + position: relative; + isolation: isolate; + display: grid; + grid-template-columns: var(--sidebar-width) minmax(0, 1fr); + width: 100%; + height: 100vh; + height: 100dvh; + min-height: 420px; + overflow: hidden; + background: var(--surface); +} + +/* Sidebar and narrative */ +#sidebar { + position: relative; + z-index: 1200; + display: flex; + min-width: 0; + min-height: 0; + flex-direction: column; + overflow: hidden; + background: var(--surface); + border-right: 1px solid var(--border-subtle); + box-shadow: 8px 0 24px rgba(62, 61, 61, 0.08); +} + +.network-hero { + position: relative; + flex: 0 0 auto; + padding: 24px 25px 22px; + overflow: hidden; + background: var(--map-hero-surface); + color: var(--map-hero-text); +} + +.network-hero::after { + content: ""; + position: absolute; + left: 25px; + bottom: 0; + width: 64px; + height: 4px; + background: var(--clay-500); + pointer-events: none; +} + +.brand-lockup { + position: relative; + z-index: 1; + display: flex; + align-items: center; + gap: 10px; + color: var(--map-hero-muted); + font-size: 0.75rem; + font-weight: 500; + letter-spacing: var(--tracking-text); +} + +.brand-mark { + display: inline-grid; + width: 31px; + height: 31px; + place-items: center; + border: 1px solid var(--map-hero-accent); + border-radius: var(--radius-btn); + background: transparent; + color: var(--map-hero-accent); + font-family: var(--font-display); + font-size: 1.05rem; + font-weight: var(--weight-display); +} + +.eyebrow, +.section-kicker { + margin: 0; + color: var(--text-accent); + font-size: 0.68rem; + font-weight: 500; + letter-spacing: 0.16em; + text-transform: uppercase; +} + +.network-hero .eyebrow { + position: relative; + z-index: 1; + margin-top: 22px; + color: var(--map-hero-accent); +} + +.network-hero h1 { + position: relative; + z-index: 1; + max-width: 360px; + margin: 5px 0 9px; + color: var(--map-hero-text); + font-family: var(--font-display); + font-size: clamp(1.65rem, 2.3vw, 2.25rem); + font-weight: var(--weight-display); + line-height: var(--leading-display); + letter-spacing: var(--tracking-display); +} + +.hero-copy { + position: relative; + z-index: 1; + max-width: 350px; + margin: 0; + color: var(--map-hero-muted); + font-size: 0.86rem; + line-height: 1.55; +} + +.impact-stats { + position: relative; + z-index: 1; + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0; + margin-top: 18px; + border-top: 1px solid color-mix(in srgb, var(--map-hero-muted) 35%, transparent); +} + +.impact-stats:has(.impact-stat:nth-child(4):not([hidden])) { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.impact-stat { + min-width: 0; + padding: 12px 9px 4px; + border-left: 1px solid color-mix(in srgb, var(--map-hero-muted) 30%, transparent); + background: transparent; +} + +.impact-stat:first-child { + border-left: 0; + padding-left: 0; +} + +.impact-stat strong, +.impact-stat span { + display: block; +} + +.impact-stat strong { + color: var(--map-hero-text); + font-family: var(--font-display); + font-weight: var(--weight-display); + font-size: 1.22rem; + font-variant-numeric: tabular-nums; + line-height: 1; +} + +.impact-stat span { + margin-top: 5px; + overflow: hidden; + color: var(--map-hero-muted); + font-size: 0.59rem; + font-weight: var(--weight-text); + letter-spacing: 0.02em; + text-overflow: ellipsis; + white-space: nowrap; +} + +.network-explorer { + display: flex; + min-height: 0; + flex: 1 1 auto; + flex-direction: column; + padding: 20px 20px 0; + overflow: hidden; +} + +.section-heading { + display: flex; + flex: 0 0 auto; + align-items: flex-end; + justify-content: space-between; + gap: 12px; + margin-bottom: 14px; +} + +.section-kicker { + color: var(--interactive); +} + +.section-heading h2 { + margin: 1px 0 0; + color: var(--text); + font-family: var(--font-display); + font-weight: var(--weight-display); + letter-spacing: var(--tracking-display); + font-size: 1.18rem; + line-height: var(--leading-display); +} + +.result-count { + flex: 0 0 auto; + padding: 3px 0 3px 8px; + border-left: 2px solid var(--divider); + border-radius: 0; + background: transparent; + color: var(--text-muted); + font-size: 0.72rem; + font-weight: 500; +} + +.filter-stack { + display: grid; + flex: 0 0 auto; + grid-template-columns: minmax(0, 1fr) minmax(0, 0.78fr); + gap: 5px 9px; + padding-bottom: 15px; + border-bottom: 1px solid var(--border-subtle); +} + +.field-label { + color: var(--text-muted); + font-size: 0.68rem; + font-weight: 500; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.search-field { + position: relative; + display: flex; + min-width: 0; + align-items: center; + grid-row: 2; +} + +.search-field > span { + position: absolute; + left: 11px; + z-index: 1; + color: var(--text-muted); + font-size: 1.05rem; + pointer-events: none; +} + +#search-input, +#category-filter { + width: 100%; + height: 40px; + min-width: 0; + border: 1px solid var(--border-strong); + border-radius: var(--radius-none) var(--radius-field) var(--radius-field) var(--radius-field); + background: var(--surface-raised); + color: var(--text); + font-size: 0.78rem; + transition: border-color 160ms ease, box-shadow 160ms ease, background 160ms ease; +} + +#search-input { + padding: 0 36px 0 34px; +} + +#search-input::placeholder { + color: color-mix(in srgb, var(--text-muted) 76%, transparent); +} + +#category-filter { + grid-column: 2; + grid-row: 2; + padding: 0 30px 0 10px; + cursor: pointer; +} + +#search-input:hover, +#category-filter:hover { + border-color: var(--interactive); +} + +#search-input:focus, +#category-filter:focus { + border-color: var(--interactive); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--interactive) 20%, transparent); + outline: none; +} + +#clear-search { + position: absolute; + right: 5px; + display: grid; + width: 30px; + height: 30px; + padding: 0; + place-items: center; + border: 0; + border-radius: var(--radius-btn); + background: transparent; + color: var(--text-muted); + cursor: pointer; + font-size: 1.2rem; +} + +#clear-search:hover { + background: var(--surface-sunken); + color: var(--text); +} + +.text-button { + width: max-content; + margin-top: 3px; + padding: 2px 0; + grid-column: 1 / -1; + border: 0; + background: transparent; + color: var(--link); + cursor: pointer; + font-size: 0.72rem; + font-weight: 500; +} + +.text-button:hover { + text-decoration: underline; + text-underline-offset: 3px; +} + +.organization-list { + min-height: 0; + flex: 1 1 auto; + margin-right: -8px; + padding: 14px 8px 24px 0; + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-color: color-mix(in srgb, var(--interactive) 38%, transparent) transparent; + scrollbar-width: thin; +} + +.org-item { + position: relative; + display: grid; + width: 100%; + grid-template-columns: 5px minmax(0, 1fr) auto; + gap: 0 11px; + align-items: start; + margin: 0 0 9px; + padding: 13px 13px 13px 10px; + overflow: hidden; + border: 1px solid var(--border-subtle); + border-radius: 0; + background: var(--surface-raised); + color: var(--text); + box-shadow: none; + cursor: pointer; + text-align: left; + transition: transform 170ms ease, border-color 170ms ease, box-shadow 170ms ease, background 170ms ease; +} + +.org-item:hover { + border-color: color-mix(in srgb, var(--org-color, var(--interactive)) 46%, var(--border-subtle)); + box-shadow: 3px 3px 0 color-mix(in srgb, var(--org-color, var(--interactive)) 16%, transparent); + transform: translate(-1px, -1px); +} + +.org-item.selected { + border-color: var(--org-color, var(--interactive)); + background: color-mix(in srgb, var(--org-color, var(--interactive)) 9%, var(--surface-raised)); + box-shadow: inset 4px 0 0 var(--org-color, var(--interactive)); +} + +.org-item.connected:not(.selected)::after { + content: "Connected"; + position: absolute; + right: 10px; + bottom: 8px; + color: var(--interactive); + font-size: 0.58rem; + font-weight: 500; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.org-color-bar { + width: 5px; + height: 100%; + min-height: 45px; + grid-row: 1 / span 2; + border-radius: 0; + background: var(--org-color, var(--interactive)); +} + +.org-card-copy { + min-width: 0; +} + +.org-name { + display: block; + overflow: hidden; + color: var(--text); + font-size: 0.85rem; + font-family: var(--font-display); + font-weight: var(--weight-display); + letter-spacing: var(--tracking-display); + line-height: 1.28; + text-overflow: ellipsis; +} + +.org-location { + display: -webkit-box; + margin-top: 4px; + overflow: hidden; + color: var(--text-muted); + font-size: 0.7rem; + line-height: 1.32; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.org-category { + display: block; + max-width: 96px; + padding: 3px 7px; + overflow: hidden; + border-radius: 3px; + background: color-mix(in srgb, var(--org-color, var(--interactive)) 12%, var(--surface-raised)); + color: color-mix(in srgb, var(--org-color, var(--interactive)) 76%, var(--text)); + font-size: 0.59rem; + font-weight: 500; + line-height: 1.25; + text-overflow: ellipsis; + white-space: nowrap; +} + +.org-connection-count { + display: flex; + align-items: center; + gap: 4px; + grid-column: 2 / -1; + margin-top: 8px; + color: var(--text-muted); + font-size: 0.63rem; + font-weight: 500; +} + +.org-connection-count::before { + content: ""; + width: 13px; + border-top: 1.5px dashed currentColor; +} + +.empty-state { + margin: 20px 0; + padding: 18px; + border: 1px dashed var(--border-subtle); + border-radius: 0; + color: var(--text-muted); + font-size: 0.82rem; + text-align: center; +} + +.network-footer { + display: flex; + flex: 0 0 auto; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 11px 20px max(11px, env(safe-area-inset-bottom)); + border-top: 1px solid var(--border-subtle); + background: var(--surface-raised); + color: var(--text-muted); + font-size: 0.66rem; + font-weight: 500; + letter-spacing: 0.025em; +} + +.network-footer i { + padding: 0 2px; + color: var(--text-accent); + font-style: normal; +} + +.network-footer a { + color: var(--link); + font-weight: 500; + text-decoration: none; +} + +.network-footer a:hover { + text-decoration: underline; + text-underline-offset: 3px; +} + +/* Map canvas and overlays */ +#map-region { + position: relative; + z-index: 1; + min-width: 0; + min-height: 0; + overflow: hidden; + background: var(--map-canvas); +} + +#map { + width: 100%; + height: 100%; + background: var(--map-canvas); +} + +body.dark #map, +body.dark #map-region { + background: var(--map-canvas); +} + +.leaflet-tile-pane { + background: var(--map-canvas); +} + +body.dark .leaflet-tile-pane { + background: var(--map-canvas); +} + +.praxium-light-tiles { + filter: saturate(0.55) sepia(0.12) brightness(1.03); +} + +/* The invert-based filter this used to carry was built to fake a dark theme + out of light OSM tiles; it now runs on Esri's actual World_Dark_Gray_Base + basemap (already dark), so it's replaced with a much lighter touch that + just nudges the tiles toward Praxium's palette instead of re-inverting them. */ +.praxium-dark-tiles { + filter: saturate(0.85) brightness(0.95); +} + +.map-toolbar { + position: absolute; + top: max(16px, env(safe-area-inset-top)); + right: max(16px, env(safe-area-inset-right)); + z-index: 1000; + display: flex; + gap: 7px; +} + +.map-toolbar button, +.map-toolbar a { + display: flex; + min-height: 40px; + align-items: center; + gap: 7px; + padding: 8px 11px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-btn); + background: color-mix(in srgb, var(--surface-raised) 96%, transparent); + color: var(--text); + box-shadow: var(--map-shadow-sm); + cursor: pointer; + font-size: 0.72rem; + font-weight: 500; + text-decoration: none; + transition: transform 150ms ease, background 150ms ease; +} + +.map-toolbar button:hover, +.map-toolbar a:hover { + background: var(--surface-raised); + transform: translateY(-1px); +} + +.map-toolbar button > span:first-child, +.map-toolbar a > span:first-child { + color: var(--interactive); + font-size: 1rem; + line-height: 1; +} + +.data-status { + position: absolute; + top: 70px; + left: 50%; + z-index: 1050; + display: flex; + width: min(620px, calc(100% - 34px)); + align-items: flex-start; + justify-content: space-between; + gap: 12px; + padding: 11px 13px 11px 15px; + border: 1px solid var(--status-warning); + border-radius: var(--radius-btn); + background: var(--status-warning-bg); + color: var(--status-warning); + box-shadow: var(--map-shadow-sm); + font-size: 0.76rem; + transform: translateX(-50%); +} + +.data-status.error { + border-color: var(--status-error); + background: var(--status-error-bg); + color: var(--status-error); +} + +.data-status button { + display: grid; + width: 26px; + height: 26px; + flex: 0 0 auto; + padding: 0; + place-items: center; + border: 0; + border-radius: var(--radius-btn); + background: transparent; + color: inherit; + cursor: pointer; + font-size: 1.1rem; +} + +.data-status button:hover { + background: rgba(0, 0, 0, 0.06); +} + +.map-message { + position: absolute; + top: 50%; + left: 50%; + z-index: 900; + display: flex; + width: min(310px, calc(100% - 40px)); + flex-direction: column; + align-items: center; + padding: 22px; + border: 1px solid var(--border-subtle); + border-radius: 0; + background: color-mix(in srgb, var(--surface-raised) 97%, transparent); + color: var(--text); + box-shadow: var(--map-shadow-lg); + text-align: center; + transform: translate(-50%, -50%); +} + +.map-message strong { + margin-top: 10px; + font-family: var(--font-display); + font-weight: var(--weight-display); + letter-spacing: var(--tracking-display); + font-size: 1.12rem; +} + +.map-message span:last-child { + margin-top: 3px; + color: var(--text-muted); + font-size: 0.74rem; +} + +.map-message--error { + z-index: 1300; +} + +.loading-mark { + position: relative; + display: block; + width: 34px; + height: 34px; + border: 2px solid color-mix(in srgb, var(--interactive) 28%, transparent); + border-radius: var(--radius-none) var(--radius-field) var(--radius-field) var(--radius-field); + animation: loading-pulse 1.2s ease-in-out infinite; +} + +.loading-mark::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--text-accent); + transform: translate(-50%, -50%); +} + +@keyframes loading-pulse { + 0%, 100% { transform: scale(0.92); opacity: 0.6; } + 50% { transform: scale(1.08); opacity: 1; } +} + +.legend { + position: absolute; + right: max(16px, env(safe-area-inset-right)); + bottom: max(24px, env(safe-area-inset-bottom)); + z-index: 1000; + width: min(230px, calc(100% - 32px)); + max-height: min(52vh, 430px); + padding: 14px; + overflow-y: auto; + border: 1px solid var(--border-strong); + border-radius: 0; + background: color-mix(in srgb, var(--surface-raised) 97%, transparent); + color: var(--text); + box-shadow: var(--map-shadow-sm); +} + +.legend-heading-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 10px; +} + +.legend-heading-row p, +.legend-heading-row h2 { + margin: 0; +} + +.legend-heading-row p { + color: var(--interactive); + font-size: 0.58rem; + font-weight: 500; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.legend-heading-row h2 { + font-family: var(--font-display); + font-weight: var(--weight-display); + letter-spacing: var(--tracking-display); + font-size: 0.86rem; +} + +.legend-items { + display: flex; + flex-direction: column; + gap: 3px; +} + +.legend-item { + display: flex; + width: 100%; + align-items: center; + gap: 8px; + padding: 5px 6px; + border: 0; + border-radius: var(--radius-btn); + background: transparent; + color: var(--text-muted); + cursor: pointer; + font-size: 0.66rem; + font-weight: 400; + text-align: left; +} + +.legend-item:hover, +.legend-item.active { + background: var(--surface-sunken); + color: var(--text); +} + +.legend-dot { + width: 10px; + height: 10px; + flex: 0 0 auto; + border: 2px solid rgba(255, 255, 255, 0.8); + border-radius: 50%; + background: var(--legend-color); + box-shadow: 0 0 0 1px color-mix(in srgb, var(--legend-color) 55%, transparent); +} + +.legend-count { + margin-left: auto; + color: var(--text-muted); + font-size: 0.6rem; + font-variant-numeric: tabular-nums; +} + +.connection-key { + display: flex; + align-items: center; + gap: 8px; + margin-top: 10px; + padding: 9px 6px 0; + border-top: 1px solid var(--border-subtle); + color: var(--text-muted); +} + +.connection-key > span { + width: 20px; + border-top: 2px dashed var(--interactive); +} + +.connection-key p { + margin: 0; + font-size: 0.62rem; + font-weight: 500; +} + +/* Leaflet custom presentation */ +.leaflet-container { + font-family: var(--font-text); +} + +.leaflet-control-zoom { + margin-bottom: 22px !important; + margin-left: 16px !important; + overflow: hidden; + border: 1px solid var(--border-strong) !important; + border-radius: var(--radius-btn) !important; + box-shadow: var(--map-shadow-sm) !important; +} + +.leaflet-control-zoom a { + width: 38px !important; + height: 38px !important; + border-color: var(--border-subtle) !important; + background: var(--surface-raised) !important; + color: var(--text) !important; + line-height: 38px !important; +} + +.leaflet-control-zoom a:hover { + background: var(--surface-sunken) !important; +} + +.leaflet-control-attribution { + background: color-mix(in srgb, var(--surface-raised) 82%, transparent) !important; + color: var(--text-muted) !important; + font-size: 9px !important; +} + +.leaflet-control-attribution a { + color: var(--interactive) !important; +} + +.custom-marker { + background: transparent; + border: 0; +} + +.marker-pin { + position: relative; + display: block; + width: 20px; + height: 20px; + border: 3px solid var(--paper-000); + border-radius: 50% 50% 50% 12%; + background: var(--marker-color); + box-shadow: 0 4px 12px rgba(62, 61, 61, 0.34); + transform: rotate(-45deg); + transition: filter 150ms ease, transform 150ms ease; +} + +.marker-pin::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 5px; + height: 5px; + border-radius: 50%; + background: var(--paper-000); + transform: translate(-50%, -50%); +} + +.custom-marker:hover .marker-pin, +.marker-pin.selected { + filter: brightness(1.08) saturate(1.18); + transform: rotate(-45deg) scale(1.24); +} + +.leaflet-popup-content-wrapper, +.leaflet-popup-tip { + background: var(--surface-raised) !important; + color: var(--text) !important; +} + +.leaflet-popup-content-wrapper { + overflow: hidden; + border: 1px solid var(--border-subtle); + border-radius: 0 !important; + box-shadow: var(--map-shadow-lg) !important; +} + +.leaflet-popup-content { + width: min(290px, 70vw) !important; + margin: 0 !important; +} + +.leaflet-popup-close-button { + top: 7px !important; + right: 7px !important; + z-index: 2; + width: 30px !important; + height: 30px !important; + border-radius: var(--radius-btn); + background: color-mix(in srgb, var(--surface-raised) 84%, transparent) !important; + color: var(--text) !important; + font-size: 21px !important; + line-height: 28px !important; +} + +.popup-content { + overflow: hidden; +} + +.popup-accent { + height: 5px; + background: var(--popup-color, var(--interactive)); +} + +.popup-body { + padding: 17px 18px 18px; +} + +.popup-kicker { + margin: 0 34px 3px 0; + color: var(--popup-color, var(--interactive)); + font-size: 0.62rem; + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.popup-title { + margin: 0; + color: var(--text); + font-family: var(--font-display); + font-weight: var(--weight-display); + letter-spacing: var(--tracking-display); + font-size: 1.18rem; + line-height: var(--leading-display); +} + +.popup-location { + margin: 7px 0 0; + color: var(--text-muted); + font-size: 0.7rem; + line-height: 1.4; +} + +.popup-description { + margin: 12px 0 0; + color: var(--text); + font-size: 0.76rem; + line-height: 1.55; +} + +.popup-relationship { + margin-top: 13px; + padding: 10px 11px; + border-left: 3px solid var(--popup-color, var(--interactive)); + border-radius: 0; + background: var(--surface-sunken); + color: var(--text); + font-size: 0.7rem; +} + +.popup-relationship strong { + display: block; + margin-bottom: 2px; + color: var(--popup-color, var(--interactive)); + font-size: 0.6rem; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.popup-connections { + margin-top: 12px; + color: var(--text-muted); + font-size: 0.66rem; +} + +.popup-connections strong { + color: var(--text); +} + +.popup-contact { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 14px; +} + +.popup-contact a { + display: inline-flex; + min-height: 34px; + align-items: center; + padding: 7px 10px; + border: 1px solid var(--border-subtle); + border-radius: var(--radius-btn); + background: var(--surface-sunken); + color: var(--interactive) !important; + font-size: 0.66rem; + font-weight: 500; + text-decoration: none !important; +} + +.popup-contact a.primary { + border-color: var(--interactive); + background: var(--interactive); + color: var(--interactive-text) !important; +} + +.popup-contact a:hover { + filter: brightness(0.96); +} + +/* Mobile / embedded layouts */ +.map-fab { + position: absolute; + z-index: 1500; + width: 44px; + height: 44px; + padding: 0; + place-items: center; + border: 1px solid var(--border-strong); + border-radius: var(--radius-btn); + background: color-mix(in srgb, var(--surface-raised) 97%, transparent); + color: var(--text); + box-shadow: var(--map-shadow-sm); + cursor: pointer; + font-size: 1.08rem; +} + +#sidebar-toggle { + top: max(12px, env(safe-area-inset-top)); + left: max(12px, env(safe-area-inset-left)); +} + +#map-expand-toggle { + top: max(12px, env(safe-area-inset-top)); + right: max(12px, env(safe-area-inset-right)); +} + +#legend-toggle { + right: max(12px, env(safe-area-inset-right)); + bottom: max(20px, env(safe-area-inset-bottom)); + color: var(--interactive); +} + +#sidebar-overlay { + display: none; +} + +@media (max-width: 980px) { + :root { + --sidebar-width: 340px; + } + + .network-hero { + padding: 20px 20px 18px; + } + + .network-hero h1 { + font-size: 1.65rem; + } + + .hero-copy { + font-size: 0.79rem; + } + + .impact-stat { + padding-inline: 6px; + } + + .impact-stat span { + font-size: 0.54rem; + } + + .network-explorer { + padding-inline: 16px; + } + + .filter-stack { + grid-template-columns: 1fr; + } + + .search-field, + #category-filter { + grid-column: 1; + grid-row: auto; + } + + .field-label:nth-of-type(2) { + margin-top: 5px; + } +} + +@media (max-width: 720px) { + .mobile-only { + display: grid; + } + + #map-shell { + display: block; + min-height: 360px; + } + + #map-region { + width: 100%; + height: 100%; + } + + #sidebar { + position: absolute; + top: 0; + bottom: 0; + left: 0; + z-index: 1450; + width: min(390px, 91vw); + height: 100%; + border-right: 0; + box-shadow: 12px 0 34px rgba(62, 61, 61, 0.28); + transform: translateX(-105%); + transition: transform 240ms cubic-bezier(0.2, 0.8, 0.2, 1); + } + + #sidebar.sidebar-open { + transform: translateX(0); + } + + #sidebar-overlay { + position: absolute; + inset: 0; + z-index: 1400; + background: var(--map-overlay); + opacity: 0; + transition: opacity 180ms ease; + } + + #sidebar-overlay.show { + display: block; + opacity: 1; + } + + .network-hero { + padding-top: max(22px, env(safe-area-inset-top)); + } + + .network-explorer { + overflow: hidden; + } + + .filter-stack { + grid-template-columns: minmax(0, 1fr) minmax(0, 0.78fr); + } + + .search-field { + grid-column: 1; + grid-row: 2; + } + + #category-filter { + grid-column: 2; + grid-row: 2; + } + + .field-label:nth-of-type(2) { + margin-top: 0; + } + + #search-input, + #category-filter { + height: 44px; + font-size: 16px; + } + + .org-item { + min-height: 62px; + } + + .map-toolbar { + top: max(12px, env(safe-area-inset-top)); + right: 64px; + } + + .map-toolbar button, + .map-toolbar a { + width: 44px; + height: 44px; + min-height: 44px; + justify-content: center; + padding: 0; + } + + .map-toolbar button > span:last-child, + .map-toolbar a > span:last-child { + display: none; + } + + #reset-view { + display: none; + } + + .legend { + right: max(12px, env(safe-area-inset-right)); + bottom: 72px; + display: none; + width: min(250px, calc(100% - 24px)); + max-height: min(58vh, 430px); + } + + .legend.legend-open { + display: block; + } + + #legend-close { + width: 32px; + height: 32px; + padding: 0; + place-items: center; + border: 0; + border-radius: var(--radius-btn); + background: var(--surface-sunken); + cursor: pointer; + font-size: 1.15rem; + } + + .legend-item { + min-height: 38px; + font-size: 0.72rem; + } + + .legend-dot { + width: 13px; + height: 13px; + } + + .leaflet-control-zoom { + margin-bottom: 16px !important; + margin-left: 12px !important; + } + + .leaflet-control-zoom a { + width: 42px !important; + height: 42px !important; + line-height: 42px !important; + } + + .data-status { + top: 66px; + } + + body.map-expanded-active #map-shell { + position: fixed; + inset: 0; + z-index: 4000; + width: 100vw; + height: 100vh; + height: 100dvh; + } +} + +@media (max-width: 430px) { + .network-hero { + padding: 18px 17px 16px; + } + + .brand-lockup { + font-size: 0.68rem; + } + + .network-hero .eyebrow { + margin-top: 16px; + } + + .network-hero h1 { + margin-top: 3px; + font-size: 1.45rem; + } + + .hero-copy { + display: none; + } + + .impact-stats { + margin-top: 13px; + } + + .impact-stat strong { + font-size: 1.05rem; + } + + .impact-stat span { + font-size: 0.5rem; + } + + .network-explorer { + padding: 15px 14px 0; + } + + .section-heading { + margin-bottom: 11px; + } + + .filter-stack { + grid-template-columns: 1fr; + gap: 4px; + padding-bottom: 11px; + } + + .search-field, + #category-filter { + grid-column: 1; + grid-row: auto; + } + + .field-label:nth-of-type(2) { + margin-top: 5px; + } + + .organization-list { + padding-top: 11px; + } + + .network-footer { + padding-inline: 14px; + } + + .map-toolbar { + right: 62px; + } + + #open-map-link { + display: none !important; + } + + .leaflet-popup-content { + width: min(270px, 74vw) !important; + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + +@media (forced-colors: active) { + .org-color-bar, + .legend-dot, + .marker-pin { + forced-color-adjust: none; + } +} diff --git a/map.js b/map.js new file mode 100644 index 0000000..65269c7 --- /dev/null +++ b/map.js @@ -0,0 +1,1098 @@ +(function initializePraxiumMap() { + 'use strict'; + + const DATA_CACHE_KEY = 'praxium-map-cache-v2'; + const LEGACY_DATA_CACHE_KEY = 'praxium-map-cache-v1'; + const THEME_STORAGE_KEY = 'praxium-dark-mode'; + const FETCH_TIMEOUT_MS = 12000; + // Centered on the Praxium Foundation's home location (Vancouver, BC), at the + // same zoom level used when focusing on any single organization below. + const INITIAL_VIEW = { center: [49.2827, -123.1207], zoom: 5 }; + const FOCUS_ZOOM = 5; + + // Hotlinking tile.openstreetmap.org directly from a production site embedded + // elsewhere (e.g. via iframe on the org's public website) violates OSM's tile + // usage policy (osm.wiki/tile_usage_policy) and gets the app blocked + // (osm.wiki/blocked) — that policy explicitly asks non-trivial-traffic apps to + // use a third-party tile provider instead of OSM's own volunteer-run servers. + // + // CARTO's basemaps were tried first, but as of this writing they now require a + // signed-up API key even for the "free" tier (a client-visible key we'd have to + // provision and maintain an account for). Esri's community Canvas basemaps + // (World_Light_Gray_Base / World_Dark_Gray_Base) are genuinely keyless, provide + // a real dark style (no CSS-filter fakery needed), and have been a standard + // free, no-signup Leaflet basemap option for years. + // Native tiles only go to zoom 16; maxZoom lets Leaflet upscale further rather + // than showing blank tiles if someone zooms in past that. + const ESRI_ATTRIBUTION = + 'Tiles © Esri — Esri, HERE, Garmin, © OpenStreetMap contributors, and the GIS User Community'; + const LIGHT_TILE = { + url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}', + attribution: ESRI_ATTRIBUTION, + }; + const DARK_TILE = { + url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}', + attribution: ESRI_ATTRIBUTION, + }; + + const COLOR_PALETTE = [ + '#85624b', '#6e6c6b', '#6b7964', '#7d6c7f', '#687c80', '#9a7259', '#5c5a5a', + '#7a7556', '#826568', '#65738a', '#8d6a52', '#6c7b70', '#776978', '#66757d', + '#785c48', '#75706e', '#687762', '#75657e', '#64787b', '#856b58', '#4a4948', + ]; + + const dataTools = window.PraxiumData; + const reducedMotion = window.matchMedia + ? window.matchMedia('(prefers-reduced-motion: reduce)') + : { matches: false }; + + const refs = { + shell: document.getElementById('map-shell'), + sidebar: document.getElementById('sidebar'), + sidebarToggle: document.getElementById('sidebar-toggle'), + sidebarOverlay: document.getElementById('sidebar-overlay'), + legend: document.getElementById('legend'), + legendToggle: document.getElementById('legend-toggle'), + legendClose: document.getElementById('legend-close'), + legendItems: document.getElementById('legend-items'), + connectionKey: document.getElementById('connection-key'), + expandToggle: document.getElementById('map-expand-toggle'), + themeToggle: document.getElementById('dark-mode-toggle'), + resetView: document.getElementById('reset-view'), + openMapLink: document.getElementById('open-map-link'), + search: document.getElementById('search-input'), + clearSearch: document.getElementById('clear-search'), + category: document.getElementById('category-filter'), + clearFilters: document.getElementById('clear-filters'), + list: document.getElementById('organization-list'), + emptyResults: document.getElementById('empty-results'), + resultCount: document.getElementById('result-count'), + loading: document.getElementById('map-loading'), + status: document.getElementById('data-status-banner'), + statusMessage: document.getElementById('data-status-message'), + statusDismiss: document.getElementById('data-status-dismiss'), + statOrganizations: document.getElementById('stat-organizations'), + statCategories: document.getElementById('stat-categories'), + statPlaces: document.getElementById('stat-places'), + statPlacesLabel: document.getElementById('stat-places-label'), + statConnections: document.getElementById('stat-connections'), + statConnectionsCard: document.getElementById('stat-connections-card'), + themeColor: document.querySelector('meta[name="theme-color"]'), + }; + + const state = { + organizations: [], + categories: [], + categoryColors: new Map(), + cards: new Map(), + markers: new Map(), + connectionLines: [], + visibleIds: new Set(), + selectedId: null, + metrics: { organizations: 0, categories: 0, locations: 0, countries: 0, connections: 0 }, + usingFallbackData: false, + }; + + let map = null; + let baseLayer = null; + let readyResolve; + const ready = new Promise((resolve) => { readyResolve = resolve; }); + + function storageGet(key) { + try { + return window.localStorage.getItem(key); + } catch (error) { + return null; + } + } + + function storageSet(key, value) { + try { + window.localStorage.setItem(key, value); + return true; + } catch (error) { + return false; + } + } + + function readDataUrl() { + const meta = document.querySelector('meta[name="praxium-data-url"]'); + return meta ? meta.content.trim() : ''; + } + + function isEmbedded() { + try { + return window.self !== window.top; + } catch (error) { + return true; + } + } + + function notifyParent(type, detail) { + if (!isEmbedded()) return; + window.parent.postMessage({ + source: 'praxium-network-map', + type, + detail: detail || {}, + }, '*'); + } + + function notifyHeight() { + notifyParent('resize', { + height: Math.ceil(refs.shell.getBoundingClientRect().height), + }); + } + + function showStatus(kind, message) { + refs.status.classList.remove('warning', 'error'); + refs.status.classList.add(kind === 'error' ? 'error' : 'warning'); + refs.statusMessage.textContent = message; + refs.status.hidden = false; + } + + function hideStatus() { + refs.status.hidden = true; + } + + function setLoadingMessage(title, message, isError) { + refs.loading.querySelector('strong').textContent = title; + refs.loading.querySelector('span:last-child').textContent = message; + refs.loading.classList.toggle('map-message--error', Boolean(isError)); + refs.loading.hidden = false; + } + + function hideLoading() { + refs.loading.hidden = true; + refs.loading.classList.remove('map-message--error'); + } + + function applyTheme(isDark, persist) { + document.body.classList.toggle('dark', isDark); + if (refs.themeColor) refs.themeColor.content = isDark ? '#3e3d3d' : '#efefee'; + refs.themeToggle.setAttribute('aria-pressed', isDark ? 'true' : 'false'); + refs.themeToggle.setAttribute('aria-label', isDark ? 'Switch to light map' : 'Switch to dark map'); + refs.themeToggle.querySelector('.theme-icon').textContent = isDark ? '☀' : '☾'; + refs.themeToggle.querySelector('.theme-label').textContent = isDark ? 'Light' : 'Dark'; + + if (persist) storageSet(THEME_STORAGE_KEY, isDark ? 'true' : 'false'); + if (map) { + setBaseLayer(isDark ? DARK_TILE : LIGHT_TILE); + updateMarkerIcons(); + styleConnectionLines(); + } + } + + function setBaseLayer(tile) { + if (!map) return; + if (baseLayer) map.removeLayer(baseLayer); + baseLayer = window.L.tileLayer(tile.url, { + attribution: tile.attribution, + maxZoom: 19, + maxNativeZoom: 16, + className: tile === DARK_TILE ? 'praxium-dark-tiles' : 'praxium-light-tiles', + }); + baseLayer.addTo(map); + } + + function initializeLeaflet() { + if (!window.L || !dataTools) { + setLoadingMessage( + 'The map could not start', + 'A required map resource did not load. Please refresh or try again later.', + true, + ); + refs.resultCount.textContent = 'Unavailable'; + return false; + } + + map = window.L.map('map', { + zoomControl: false, + minZoom: 2, + worldCopyJump: true, + preferCanvas: true, + }).setView(INITIAL_VIEW.center, INITIAL_VIEW.zoom); + + window.L.control.zoom({ position: 'bottomleft' }).addTo(map); + map.createPane('connections'); + map.getPane('connections').style.zIndex = '350'; + map.getPane('connections').style.pointerEvents = 'auto'; + + map.on('click', () => clearSelection(false)); + + const savedTheme = storageGet(THEME_STORAGE_KEY); + const useDarkTheme = savedTheme === 'true'; + applyTheme(useDarkTheme, false); + return true; + } + + function adjustColorForDark(hex) { + const normalized = String(hex).replace('#', ''); + if (!/^[0-9a-f]{6}$/i.test(normalized)) return hex; + const parts = [0, 2, 4].map((offset) => parseInt(normalized.slice(offset, offset + 2), 16)); + return `#${parts.map((value) => Math.min(255, Math.round(value * 1.18)).toString(16).padStart(2, '0')).join('')}`; + } + + function getCategoryColor(category) { + const base = state.categoryColors.get(category) || '#85624b'; + return document.body.classList.contains('dark') ? adjustColorForDark(base) : base; + } + + function buildCategoryColors() { + state.categoryColors.clear(); + const discovered = [...new Set(state.organizations.map((organization) => organization.category))]; + const ordered = [ + ...state.categories.filter((category) => discovered.includes(category)), + ...discovered.filter((category) => !state.categories.includes(category)).sort(), + ]; + ordered.forEach((category, index) => { + state.categoryColors.set(category, COLOR_PALETTE[index % COLOR_PALETTE.length]); + }); + return ordered; + } + + function createMarkerIcon(organization, selected) { + const color = getCategoryColor(organization.category); + const selectedClass = selected ? ' selected' : ''; + return window.L.divIcon({ + html: ``, + className: 'custom-marker', + iconSize: [24, 24], + iconAnchor: [12, 22], + popupAnchor: [0, -22], + }); + } + + function appendTextElement(parent, tagName, className, text) { + const element = document.createElement(tagName); + if (className) element.className = className; + element.textContent = text; + parent.appendChild(element); + return element; + } + + function getConnectedIds(organizationId) { + const connected = new Set(); + dataTools.getConnectionPairs(state.organizations).forEach(([first, second]) => { + if (first === organizationId) connected.add(second); + if (second === organizationId) connected.add(first); + }); + return connected; + } + + function createPopup(organization) { + const color = getCategoryColor(organization.category); + const wrapper = document.createElement('article'); + wrapper.className = 'popup-content'; + wrapper.style.setProperty('--popup-color', color); + + const accent = document.createElement('div'); + accent.className = 'popup-accent'; + wrapper.appendChild(accent); + + const body = document.createElement('div'); + body.className = 'popup-body'; + wrapper.appendChild(body); + + appendTextElement(body, 'p', 'popup-kicker', organization.category || 'Organization'); + appendTextElement(body, 'h2', 'popup-title', organization.name); + if (organization.location) appendTextElement(body, 'p', 'popup-location', organization.location); + if (organization.description) appendTextElement(body, 'p', 'popup-description', organization.description); + + if (organization.relationship) { + const relationship = document.createElement('div'); + relationship.className = 'popup-relationship'; + appendTextElement(relationship, 'strong', '', 'How we connect'); + relationship.appendChild(document.createTextNode(organization.relationship)); + body.appendChild(relationship); + } + + const connectedNames = [...getConnectedIds(organization.id)] + .map((id) => state.organizations.find((candidate) => candidate.id === id)) + .filter(Boolean) + .map((candidate) => candidate.name); + if (connectedNames.length) { + const connections = document.createElement('p'); + connections.className = 'popup-connections'; + const label = document.createElement('strong'); + label.textContent = `${connectedNames.length} mapped connection${connectedNames.length === 1 ? '' : 's'}: `; + connections.appendChild(label); + connections.appendChild(document.createTextNode(connectedNames.join(', '))); + body.appendChild(connections); + } + + const contact = document.createElement('div'); + contact.className = 'popup-contact'; + + if (organization.contact.website) { + const website = document.createElement('a'); + website.className = 'primary'; + website.href = organization.contact.website; + website.target = '_blank'; + website.rel = 'noopener noreferrer'; + website.textContent = 'Visit website ↗'; + website.setAttribute('aria-label', `Visit ${organization.name} website (opens in a new tab)`); + contact.appendChild(website); + } + + if (organization.contact.email) { + const email = document.createElement('a'); + email.href = `mailto:${organization.contact.email}`; + email.textContent = 'Email'; + email.setAttribute('aria-label', `Email ${organization.name}`); + contact.appendChild(email); + } + + if (organization.contact.phone) { + const phoneValue = organization.contact.phone.replace(/[^+\d]/g, ''); + if (phoneValue) { + const phone = document.createElement('a'); + phone.href = `tel:${phoneValue}`; + phone.textContent = 'Call'; + phone.setAttribute('aria-label', `Call ${organization.name}`); + contact.appendChild(phone); + } + } + + if (contact.childElementCount) body.appendChild(contact); + return wrapper; + } + + function createOrganizationCard(organization) { + const color = getCategoryColor(organization.category); + const button = document.createElement('button'); + button.type = 'button'; + button.className = 'org-item'; + button.dataset.id = String(organization.id); + button.style.setProperty('--org-color', color); + button.setAttribute('role', 'listitem'); + button.setAttribute('aria-label', `View ${organization.name} on the map`); + + const colorBar = document.createElement('span'); + colorBar.className = 'org-color-bar'; + colorBar.setAttribute('aria-hidden', 'true'); + button.appendChild(colorBar); + + const copy = document.createElement('span'); + copy.className = 'org-card-copy'; + appendTextElement(copy, 'span', 'org-name', organization.name); + if (organization.location) appendTextElement(copy, 'span', 'org-location', organization.location); + button.appendChild(copy); + + appendTextElement(button, 'span', 'org-category', organization.category); + + const connectionCount = getConnectedIds(organization.id).size; + if (connectionCount) { + appendTextElement( + button, + 'span', + 'org-connection-count', + `${connectionCount} connection${connectionCount === 1 ? '' : 's'}`, + ); + } + + button.addEventListener('click', () => selectOrganization(organization.id, { toggle: true })); + return button; + } + + function renderStats() { + state.metrics = dataTools.computeMetrics(state.organizations); + refs.statOrganizations.textContent = String(state.metrics.organizations); + refs.statCategories.textContent = String(state.metrics.categories); + + if (state.metrics.countries > 1) { + refs.statPlaces.textContent = String(state.metrics.countries); + refs.statPlacesLabel.textContent = 'Countries'; + } else { + refs.statPlaces.textContent = String(state.metrics.locations); + refs.statPlacesLabel.textContent = 'Locations'; + } + + refs.statConnections.textContent = String(state.metrics.connections); + refs.statConnectionsCard.hidden = state.metrics.connections === 0; + refs.connectionKey.hidden = state.metrics.connections === 0; + } + + function renderFiltersAndLegend() { + const orderedCategories = buildCategoryColors(); + const selected = refs.category.value; + refs.category.querySelectorAll('option:not([value="all"])').forEach((option) => option.remove()); + refs.legendItems.replaceChildren(); + + orderedCategories.forEach((category) => { + const count = state.organizations.filter((organization) => organization.category === category).length; + const option = document.createElement('option'); + option.value = category; + option.textContent = `${category} (${count})`; + refs.category.appendChild(option); + + const legendButton = document.createElement('button'); + legendButton.type = 'button'; + legendButton.className = 'legend-item'; + legendButton.dataset.category = category; + legendButton.setAttribute('aria-label', `Show ${category} organizations`); + + const dot = document.createElement('span'); + dot.className = 'legend-dot'; + dot.style.setProperty('--legend-color', getCategoryColor(category)); + dot.setAttribute('aria-hidden', 'true'); + legendButton.appendChild(dot); + appendTextElement(legendButton, 'span', '', category); + appendTextElement(legendButton, 'span', 'legend-count', String(count)); + + legendButton.addEventListener('click', () => { + refs.category.value = refs.category.value === category ? 'all' : category; + applyFilters({ fit: true }); + if (window.innerWidth <= 720) closeLegend(); + }); + refs.legendItems.appendChild(legendButton); + }); + + refs.category.value = [...refs.category.options].some((option) => option.value === selected) + ? selected + : 'all'; + } + + function clearRenderedMap() { + state.connectionLines.forEach((line) => line.remove()); + state.connectionLines = []; + state.markers.forEach((marker) => marker.remove()); + state.markers.clear(); + state.cards.clear(); + refs.list.replaceChildren(); + } + + function renderOrganizations() { + clearRenderedMap(); + + state.organizations.forEach((organization) => { + const marker = window.L.marker(organization.coordinates, { + icon: createMarkerIcon(organization, false), + keyboard: true, + title: organization.name, + alt: organization.name, + riseOnHover: true, + }); + marker.bindPopup(() => createPopup(organization), { + maxWidth: 320, + minWidth: 235, + autoPanPaddingTopLeft: [20, 80], + autoPanPaddingBottomRight: [20, 20], + }); + marker.on('click', () => selectOrganization(organization.id, { toggle: false, moveMap: false })); + marker.addTo(map); + state.markers.set(organization.id, marker); + + const card = createOrganizationCard(organization); + refs.list.appendChild(card); + state.cards.set(organization.id, card); + }); + } + + function lineBaseStyle() { + const dark = document.body.classList.contains('dark'); + return { + color: dark ? '#c7ac98' : '#85624b', + weight: 2, + opacity: dark ? 0.64 : 0.58, + dashArray: '5 7', + lineCap: 'round', + }; + } + + function lineActiveStyle() { + return { + color: document.body.classList.contains('dark') ? '#eee7e2' : '#654a38', + weight: 4, + opacity: 0.95, + dashArray: null, + lineCap: 'round', + }; + } + + function drawConnections() { + state.connectionLines.forEach((line) => line.remove()); + state.connectionLines = []; + + dataTools.getConnectionPairs(state.organizations).forEach(([firstId, secondId]) => { + if (!state.visibleIds.has(firstId) || !state.visibleIds.has(secondId)) return; + const first = state.organizations.find((organization) => organization.id === firstId); + const second = state.organizations.find((organization) => organization.id === secondId); + if (!first || !second) return; + + const line = window.L.polyline([first.coordinates, second.coordinates], { + ...lineBaseStyle(), + pane: 'connections', + interactive: true, + }); + line.orgIds = [firstId, secondId]; + + const tooltip = document.createElement('span'); + tooltip.textContent = `${first.name} ↔ ${second.name}`; + line.bindTooltip(tooltip, { sticky: true, direction: 'top' }); + line.on('click', (event) => { + if (event.originalEvent) window.L.DomEvent.stopPropagation(event.originalEvent); + const targetId = state.selectedId === firstId ? secondId : firstId; + selectOrganization(targetId, { toggle: false }); + }); + line.addTo(map); + state.connectionLines.push(line); + }); + + styleConnectionLines(); + } + + function styleConnectionLines() { + state.connectionLines.forEach((line) => { + const active = state.selectedId !== null && line.orgIds.includes(state.selectedId); + line.setStyle(active ? lineActiveStyle() : lineBaseStyle()); + if (active) line.bringToFront(); + }); + } + + function updateMarkerIcons() { + state.organizations.forEach((organization) => { + const marker = state.markers.get(organization.id); + if (marker) marker.setIcon(createMarkerIcon(organization, state.selectedId === organization.id)); + }); + + state.cards.forEach((card, id) => { + const organization = state.organizations.find((candidate) => candidate.id === id); + if (organization) card.style.setProperty('--org-color', getCategoryColor(organization.category)); + }); + + refs.legendItems.querySelectorAll('.legend-item').forEach((button) => { + const dot = button.querySelector('.legend-dot'); + if (dot) dot.style.setProperty('--legend-color', getCategoryColor(button.dataset.category)); + }); + } + + function updateSelectionStyles(options) { + const connectedIds = state.selectedId === null ? new Set() : getConnectedIds(state.selectedId); + + state.cards.forEach((card, id) => { + card.classList.toggle('selected', id === state.selectedId); + card.classList.toggle('connected', connectedIds.has(id)); + card.setAttribute('aria-pressed', id === state.selectedId ? 'true' : 'false'); + }); + updateMarkerIcons(); + styleConnectionLines(); + + if (state.selectedId !== null && options && options.scrollCard) { + const card = state.cards.get(state.selectedId); + if (card && !card.hidden) { + card.scrollIntoView({ + behavior: reducedMotion.matches ? 'auto' : 'smooth', + block: 'nearest', + }); + } + } + } + + function fitOrganizations(organizations, animate) { + if (!map) return; + const rows = (organizations || []).filter(Boolean); + if (rows.length === 0) { + map.setView(INITIAL_VIEW.center, INITIAL_VIEW.zoom, { animate: false }); + return; + } + if (rows.length === 1) { + const method = animate && !reducedMotion.matches ? 'flyTo' : 'setView'; + map[method](rows[0].coordinates, FOCUS_ZOOM, { duration: 0.65 }); + return; + } + + const bounds = window.L.latLngBounds(rows.map((organization) => organization.coordinates)); + map.fitBounds(bounds, { + animate: Boolean(animate && !reducedMotion.matches), + duration: 0.75, + paddingTopLeft: [46, 76], + paddingBottomRight: [46, 54], + maxZoom: FOCUS_ZOOM, + }); + } + + function clearSelection(resetView) { + if (state.selectedId === null && !resetView) return; + state.selectedId = null; + if (map) map.closePopup(); + updateSelectionStyles(); + if (resetView) { + fitOrganizations( + state.organizations.filter((organization) => state.visibleIds.has(organization.id)), + true, + ); + } + notifyParent('selection', { organizationId: null }); + } + + function selectOrganization(idValue, options) { + const settings = { toggle: false, moveMap: true, ...(options || {}) }; + const id = Number(idValue); + const organization = state.organizations.find((candidate) => candidate.id === id); + const marker = state.markers.get(id); + if (!organization || !marker) return false; + + if (!state.visibleIds.has(id)) { + refs.search.value = ''; + refs.category.value = 'all'; + applyFilters({ fit: false }); + } + + if (settings.toggle && state.selectedId === id) { + clearSelection(true); + closeSidebarOnMobile(); + return true; + } + + state.selectedId = id; + updateSelectionStyles({ scrollCard: true }); + + if (settings.moveMap) { + const method = reducedMotion.matches ? 'setView' : 'flyTo'; + map[method](organization.coordinates, Math.max(map.getZoom(), FOCUS_ZOOM), { duration: 0.65 }); + } + marker.openPopup(); + closeSidebarOnMobile(); + notifyParent('selection', { organizationId: id, organizationName: organization.name }); + return true; + } + + function normalizedSearchText(organization) { + return [ + organization.name, + organization.location, + organization.country, + organization.category, + organization.description, + organization.relationship, + ].join(' ').toLocaleLowerCase(); + } + + function updateLegendSelection() { + const selected = refs.category.value; + refs.legendItems.querySelectorAll('.legend-item').forEach((button) => { + const active = selected !== 'all' && button.dataset.category === selected; + button.classList.toggle('active', active); + button.setAttribute('aria-pressed', active ? 'true' : 'false'); + }); + } + + function applyFilters(options) { + const settings = { fit: false, ...(options || {}) }; + const searchTerm = refs.search.value.trim().toLocaleLowerCase(); + const selectedCategory = refs.category.value; + state.visibleIds.clear(); + + state.organizations.forEach((organization) => { + const searchMatches = !searchTerm || normalizedSearchText(organization).includes(searchTerm); + const categoryMatches = selectedCategory === 'all' || organization.category === selectedCategory; + const visible = searchMatches && categoryMatches; + const card = state.cards.get(organization.id); + const marker = state.markers.get(organization.id); + + if (visible) { + state.visibleIds.add(organization.id); + if (marker && !map.hasLayer(marker)) marker.addTo(map); + } else if (marker && map.hasLayer(marker)) { + marker.removeFrom(map); + } + if (card) card.hidden = !visible; + }); + + if (state.selectedId !== null && !state.visibleIds.has(state.selectedId)) { + clearSelection(false); + } + + const visibleCount = state.visibleIds.size; + refs.resultCount.textContent = `${visibleCount} of ${state.organizations.length}`; + refs.emptyResults.hidden = visibleCount !== 0 || state.organizations.length === 0; + refs.clearSearch.hidden = refs.search.value.length === 0; + refs.clearFilters.hidden = !searchTerm && selectedCategory === 'all'; + updateLegendSelection(); + drawConnections(); + + if (settings.fit) { + fitOrganizations( + state.organizations.filter((organization) => state.visibleIds.has(organization.id)), + true, + ); + } + } + + function renderAll(options) { + const settings = { resetFilters: true, ...(options || {}) }; + state.selectedId = null; + if (settings.resetFilters) { + refs.search.value = ''; + refs.category.value = 'all'; + } + + renderStats(); + renderFiltersAndLegend(); + renderOrganizations(); + applyFilters({ fit: false }); + + if (state.organizations.length) { + hideLoading(); + window.setTimeout(() => { + map.invalidateSize(); + fitOrganizations(state.organizations, false); + }, 60); + } else { + setLoadingMessage( + 'The network is being updated', + 'There are no organization locations to display right now.', + false, + ); + } + + notifyHeight(); + } + + async function fetchJson(url) { + const controller = typeof AbortController === 'function' ? new AbortController() : null; + const timer = controller ? window.setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS) : null; + try { + const response = await fetch(url, { + cache: 'no-store', + headers: { Accept: 'application/json' }, + signal: controller ? controller.signal : undefined, + }); + if (!response.ok) throw new Error(`Request failed with status ${response.status}`); + return await response.json(); + } finally { + if (timer) window.clearTimeout(timer); + } + } + + async function loadCategories() { + const fallback = Array.isArray(window.CANONICAL_CATEGORIES) + ? window.CANONICAL_CATEGORIES.map((category) => String(category).trim()).filter(Boolean) + : []; + try { + const categoriesUrl = new URL('categories.json', document.baseURI).href; + const categories = await fetchJson(categoriesUrl); + if (Array.isArray(categories) && categories.length) { + return [...new Set(categories.map((category) => String(category).trim()).filter(Boolean))]; + } + } catch (error) { + console.warn('Could not load categories.json; using the bundled fallback.', error); + } + return fallback; + } + + function readCachedData() { + const raw = storageGet(DATA_CACHE_KEY) || storageGet(LEGACY_DATA_CACHE_KEY); + if (!raw) return null; + try { + const cached = JSON.parse(raw); + return cached && Array.isArray(cached.data) ? cached : null; + } catch (error) { + return null; + } + } + + function formatCachedDate(timestamp) { + const date = new Date(timestamp); + return Number.isFinite(date.getTime()) ? date.toLocaleString() : 'an earlier visit'; + } + + function useDataset(rawData, sourceDescription) { + const result = dataTools.normalizeDataset(rawData, state.categories); + if (!result.valid) throw new Error('The data source did not contain an organization array.'); + if (rawData.length > 0 && result.organizations.length === 0) { + throw new Error('The data source did not contain any usable organization records.'); + } + + state.organizations = result.organizations; + if (result.warnings.length) { + console.warn('Praxium network data warnings:', result.warnings); + showStatus( + 'warning', + `${sourceDescription} ${result.warnings.length} data issue${result.warnings.length === 1 ? ' was' : 's were'} safely ignored.`, + ); + } + return result; + } + + async function loadNetworkData() { + state.categories = await loadCategories(); + const dataUrl = readDataUrl(); + + if (!dataUrl) { + state.organizations = []; + showStatus('error', 'The organization data source has not been configured.'); + renderAll(); + return; + } + + try { + const rawData = await fetchJson(dataUrl); + const result = useDataset(rawData, 'The network loaded, but'); + storageSet(DATA_CACHE_KEY, JSON.stringify({ data: rawData, fetchedAt: Date.now() })); + state.usingFallbackData = false; + if (result.warnings.length === 0) hideStatus(); + } catch (error) { + console.warn('Could not load the live Praxium network data.', error); + const cached = readCachedData(); + if (cached) { + try { + useDataset(cached.data, 'The saved network loaded, but'); + state.usingFallbackData = true; + showStatus( + 'warning', + `Live updates are temporarily unavailable. Showing the last saved network from ${formatCachedDate(cached.fetchedAt)}.`, + ); + } catch (cacheError) { + state.organizations = []; + showStatus('error', 'The organization network could not be loaded. Please try again later.'); + } + } else { + state.organizations = []; + showStatus('error', 'The organization network could not be loaded. Please try again later.'); + } + } + + renderAll(); + } + + function openSidebar() { + refs.sidebar.classList.add('sidebar-open'); + refs.sidebarOverlay.classList.add('show'); + refs.sidebarOverlay.setAttribute('aria-hidden', 'false'); + refs.sidebarToggle.setAttribute('aria-expanded', 'true'); + refs.sidebarToggle.setAttribute('aria-label', 'Close network explorer'); + refs.sidebarToggle.querySelector('span').textContent = '×'; + window.setTimeout(() => refs.search.focus(), reducedMotion.matches ? 0 : 240); + } + + function closeSidebar() { + refs.sidebar.classList.remove('sidebar-open'); + refs.sidebarOverlay.classList.remove('show'); + refs.sidebarOverlay.setAttribute('aria-hidden', 'true'); + refs.sidebarToggle.setAttribute('aria-expanded', 'false'); + refs.sidebarToggle.setAttribute('aria-label', 'Open network explorer'); + refs.sidebarToggle.querySelector('span').textContent = '☰'; + } + + function closeSidebarOnMobile() { + if (window.innerWidth <= 720) closeSidebar(); + } + + function openLegend() { + refs.legend.classList.add('legend-open'); + refs.legendToggle.setAttribute('aria-expanded', 'true'); + refs.legendToggle.setAttribute('aria-label', 'Hide map legend'); + } + + function closeLegend() { + refs.legend.classList.remove('legend-open'); + refs.legendToggle.setAttribute('aria-expanded', 'false'); + refs.legendToggle.setAttribute('aria-label', 'Show map legend'); + } + + function setFallbackExpanded(expanded) { + refs.shell.classList.toggle('map-expanded', expanded); + document.body.classList.toggle('map-expanded-active', expanded); + updateExpandButton(expanded); + window.setTimeout(() => { + if (map) map.invalidateSize(); + notifyHeight(); + }, reducedMotion.matches ? 0 : 260); + } + + function updateExpandButton(expanded) { + refs.expandToggle.setAttribute('aria-pressed', expanded ? 'true' : 'false'); + refs.expandToggle.setAttribute('aria-label', expanded ? 'Exit expanded map' : 'Expand map'); + refs.expandToggle.querySelector('span').textContent = expanded ? '×' : '⛶'; + } + + async function toggleExpandedMap() { + const fallbackExpanded = refs.shell.classList.contains('map-expanded'); + if (document.fullscreenElement) { + await document.exitFullscreen(); + return; + } + if (fallbackExpanded) { + setFallbackExpanded(false); + return; + } + + if (refs.shell.requestFullscreen) { + try { + await refs.shell.requestFullscreen(); + return; + } catch (error) { + // Cross-origin iframes need allow="fullscreen". Fall back to filling the iframe. + } + } + setFallbackExpanded(true); + } + + function bindUiEvents() { + refs.themeToggle.addEventListener('click', () => { + applyTheme(!document.body.classList.contains('dark'), true); + }); + refs.resetView.addEventListener('click', () => clearSelection(true)); + refs.statusDismiss.addEventListener('click', hideStatus); + + refs.search.addEventListener('input', () => applyFilters()); + refs.search.addEventListener('keydown', (event) => { + if (event.key === 'Escape' && refs.search.value) { + refs.search.value = ''; + applyFilters({ fit: true }); + } + }); + refs.category.addEventListener('change', () => applyFilters({ fit: true })); + refs.clearSearch.addEventListener('click', () => { + refs.search.value = ''; + refs.search.focus(); + applyFilters({ fit: true }); + }); + refs.clearFilters.addEventListener('click', () => { + refs.search.value = ''; + refs.category.value = 'all'; + applyFilters({ fit: true }); + }); + + refs.sidebarToggle.addEventListener('click', () => { + if (refs.sidebar.classList.contains('sidebar-open')) closeSidebar(); + else openSidebar(); + }); + refs.sidebarOverlay.addEventListener('click', closeSidebar); + refs.legendToggle.addEventListener('click', () => { + if (refs.legend.classList.contains('legend-open')) closeLegend(); + else openLegend(); + }); + refs.legendClose.addEventListener('click', closeLegend); + refs.expandToggle.addEventListener('click', toggleExpandedMap); + + document.addEventListener('fullscreenchange', () => { + const expanded = document.fullscreenElement === refs.shell; + if (expanded) setFallbackExpanded(false); + updateExpandButton(expanded); + window.setTimeout(() => { + if (map) map.invalidateSize(); + notifyHeight(); + }, 80); + }); + + document.addEventListener('keydown', (event) => { + if (event.key !== 'Escape') return; + if (refs.legend.classList.contains('legend-open')) closeLegend(); + else if (refs.sidebar.classList.contains('sidebar-open')) closeSidebar(); + else if (refs.shell.classList.contains('map-expanded')) setFallbackExpanded(false); + else if (state.selectedId !== null) clearSelection(false); + }); + + window.addEventListener('resize', () => { + window.requestAnimationFrame(() => { + if (map) map.invalidateSize(); + if (window.innerWidth > 720) { + closeSidebar(); + closeLegend(); + } + notifyHeight(); + }); + }); + + if (typeof ResizeObserver === 'function') { + const observer = new ResizeObserver(() => { + if (map) map.invalidateSize({ pan: false }); + notifyHeight(); + }); + observer.observe(refs.shell); + } + } + + function exportNetworkData() { + return JSON.stringify(state.organizations, null, 2); + } + + function importNetworkData(jsonData) { + try { + const parsed = typeof jsonData === 'string' ? JSON.parse(jsonData) : jsonData; + const result = dataTools.normalizeDataset(parsed, state.categories); + if (!result.valid || (parsed.length > 0 && result.organizations.length === 0)) return false; + state.organizations = result.organizations; + hideStatus(); + if (result.warnings.length) { + console.warn('Imported network data warnings:', result.warnings); + showStatus( + 'warning', + `The import completed with ${result.warnings.length} data issue${result.warnings.length === 1 ? '' : 's'} safely ignored.`, + ); + } + renderAll(); + return true; + } catch (error) { + console.error('Could not import Praxium network data.', error); + return false; + } + } + + function addOrganization(organizationData) { + if (!organizationData || typeof organizationData !== 'object' || Array.isArray(organizationData)) return null; + const highestId = state.organizations.reduce((highest, organization) => Math.max(highest, organization.id), 0); + const id = highestId + 1; + const result = dataTools.normalizeDataset( + [...state.organizations, { ...organizationData, id }], + state.categories, + ); + if (!result.valid || result.organizations.length !== state.organizations.length + 1) return null; + state.organizations = result.organizations; + renderAll({ resetFilters: false }); + return id; + } + + function installPublicApi() { + window.praxiumNetwork = { + exportData: exportNetworkData, + importData: importNetworkData, + addOrganization, + selectOrganization, + resetView: () => clearSelection(true), + getMetrics: () => ({ ...state.metrics }), + ready, + }; + } + + async function start() { + installPublicApi(); + bindUiEvents(); + + if (isEmbedded()) { + const url = new URL(window.location.href); + url.hash = ''; + refs.openMapLink.href = url.href; + refs.openMapLink.hidden = false; + } + + if (!initializeLeaflet()) { + readyResolve({ ok: false, organizations: 0 }); + return; + } + + await loadNetworkData(); + const detail = { + ok: state.organizations.length > 0, + organizations: state.organizations.length, + metrics: { ...state.metrics }, + cached: state.usingFallbackData, + }; + readyResolve(detail); + document.dispatchEvent(new CustomEvent('praxium:ready', { detail })); + notifyParent('ready', detail); + } + + start().catch((error) => { + console.error('Praxium network startup failed.', error); + setLoadingMessage( + 'The map could not start', + 'Please refresh the page. If the problem continues, contact Praxium.', + true, + ); + refs.resultCount.textContent = 'Unavailable'; + readyResolve({ ok: false, organizations: 0 }); + }); +}()); diff --git a/network-data.js b/network-data.js new file mode 100644 index 0000000..85abdb9 --- /dev/null +++ b/network-data.js @@ -0,0 +1,245 @@ +(function exposePraxiumData(root, factory) { + const api = factory(); + if (typeof module === 'object' && module.exports) { + module.exports = api; + } else { + root.PraxiumData = api; + } +}(typeof globalThis !== 'undefined' ? globalThis : this, function createPraxiumData() { + 'use strict'; + + const DEFAULT_CATEGORY = 'Other'; + + function asText(value) { + if (value === undefined || value === null) return ''; + return String(value).trim(); + } + + function simplifyCategory(value) { + return asText(value) + .toLowerCase() + .replace(/[-_]+/g, ' ') + .replace(/[^a-z0-9\s]/g, '') + .replace(/\s+/g, ' ') + .trim(); + } + + function normalizeCategory(value, canonicalCategories) { + const raw = asText(value); + if (!raw) return DEFAULT_CATEGORY; + + const categories = Array.isArray(canonicalCategories) ? canonicalCategories : []; + const direct = categories.find((category) => asText(category).toLowerCase() === raw.toLowerCase()); + if (direct) return asText(direct); + + const simplified = simplifyCategory(raw); + const equivalent = categories.find((category) => simplifyCategory(category) === simplified); + if (equivalent) return asText(equivalent); + + return raw + .split(/\s+/) + .map((word) => word ? word.charAt(0).toUpperCase() + word.slice(1) : '') + .join(' '); + } + + function safeHttpUrl(value) { + const raw = asText(value); + if (!raw) return ''; + try { + const parsed = new URL(raw); + return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? parsed.href : ''; + } catch (error) { + return ''; + } + } + + function normalizeId(value) { + const number = Number(value); + return Number.isSafeInteger(number) && number > 0 ? number : null; + } + + function normalizeCoordinates(value) { + if (!Array.isArray(value) || value.length !== 2) return null; + const latitude = Number(value[0]); + const longitude = Number(value[1]); + if ( + !Number.isFinite(latitude) + || !Number.isFinite(longitude) + || latitude < -90 + || latitude > 90 + || longitude < -180 + || longitude > 180 + ) { + return null; + } + return [latitude, longitude]; + } + + function nextAvailableId(usedIds) { + let id = 1; + while (usedIds.has(id)) id += 1; + return id; + } + + function normalizeDataset(input, canonicalCategories) { + if (!Array.isArray(input)) { + return { + organizations: [], + warnings: ['The data source must contain a JSON array.'], + valid: false, + }; + } + + const warnings = []; + const organizations = []; + const usedIds = new Set(); + + input.forEach((rawOrganization, index) => { + if (!rawOrganization || typeof rawOrganization !== 'object' || Array.isArray(rawOrganization)) { + warnings.push(`Entry ${index + 1} was skipped because it is not an organization object.`); + return; + } + + const name = asText(rawOrganization.name); + const coordinates = normalizeCoordinates( + rawOrganization.coordinates + || ( + rawOrganization.lat !== undefined && rawOrganization.lng !== undefined + ? [rawOrganization.lat, rawOrganization.lng] + : null + ), + ); + + if (!name) { + warnings.push(`Entry ${index + 1} was skipped because it has no organization name.`); + return; + } + if (!coordinates) { + warnings.push(`${name} was skipped because its coordinates are missing or invalid.`); + return; + } + + let id = normalizeId(rawOrganization.id); + if (id !== null && usedIds.has(id)) { + warnings.push(`${name} was skipped because organization ID ${id} is duplicated.`); + return; + } + if (id === null) { + id = nextAvailableId(usedIds); + warnings.push(`${name} was assigned organization ID ${id} because its ID was missing or invalid.`); + } + usedIds.add(id); + + const rawContact = ( + rawOrganization.contact + && typeof rawOrganization.contact === 'object' + && !Array.isArray(rawOrganization.contact) + ) ? rawOrganization.contact : {}; + + const contact = {}; + const website = safeHttpUrl(rawContact.website); + const email = asText(rawContact.email); + const phone = asText(rawContact.phone); + if (website) contact.website = website; + if (email) contact.email = email; + if (phone) contact.phone = phone; + + const rawConnections = Array.isArray(rawOrganization.connections) + ? rawOrganization.connections + : []; + const connections = [...new Set( + rawConnections + .map(normalizeId) + .filter((connectionId) => connectionId !== null && connectionId !== id), + )]; + + organizations.push({ + ...rawOrganization, + id, + name, + location: asText(rawOrganization.location), + country: asText(rawOrganization.country), + coordinates, + category: normalizeCategory( + rawOrganization.category || rawOrganization.type, + canonicalCategories, + ), + description: asText(rawOrganization.description), + relationship: asText(rawOrganization.relationship), + contact, + connections, + }); + }); + + const knownIds = new Set(organizations.map((organization) => organization.id)); + organizations.forEach((organization) => { + const removed = organization.connections.filter((connectionId) => !knownIds.has(connectionId)); + if (removed.length) { + warnings.push( + `${organization.name} referenced unknown organization ID${removed.length === 1 ? '' : 's'} ` + + `${removed.join(', ')}; ${removed.length === 1 ? 'it was' : 'they were'} ignored.`, + ); + } + organization.connections = organization.connections.filter((connectionId) => knownIds.has(connectionId)); + }); + + return { organizations, warnings, valid: true }; + } + + function getConnectionPairs(organizations) { + const rows = Array.isArray(organizations) ? organizations : []; + const knownIds = new Set(rows.map((organization) => normalizeId(organization.id)).filter(Boolean)); + const pairs = new Map(); + + rows.forEach((organization) => { + const sourceId = normalizeId(organization.id); + if (!sourceId || !Array.isArray(organization.connections)) return; + + organization.connections.forEach((connectionValue) => { + const targetId = normalizeId(connectionValue); + if (!targetId || targetId === sourceId || !knownIds.has(targetId)) return; + const low = Math.min(sourceId, targetId); + const high = Math.max(sourceId, targetId); + pairs.set(`${low}:${high}`, [low, high]); + }); + }); + + return [...pairs.values()]; + } + + function computeMetrics(organizations) { + const rows = Array.isArray(organizations) ? organizations : []; + const categories = new Set(); + const locations = new Set(); + const countries = new Set(); + + rows.forEach((organization) => { + const category = asText(organization.category); + const location = asText(organization.location); + const country = asText(organization.country); + if (category) categories.add(category.toLowerCase()); + if (location) locations.add(location.toLowerCase()); + if (country) countries.add(country.toLowerCase()); + }); + + return { + organizations: rows.length, + categories: categories.size, + locations: locations.size, + countries: countries.size, + connections: getConnectionPairs(rows).length, + }; + } + + return { + DEFAULT_CATEGORY, + asText, + computeMetrics, + getConnectionPairs, + normalizeCategory, + normalizeCoordinates, + normalizeDataset, + normalizeId, + safeHttpUrl, + }; +})); diff --git a/organization-network-map.html b/organization-network-map.html index ac284d6..5933646 100644 --- a/organization-network-map.html +++ b/organization-network-map.html @@ -1,1345 +1,218 @@ - + - - - Praxium Global Network - - - - - + + + + + + + + + + + Praxium Global Network — Allies & Partners + + + + + + + + + + + + - - -
- - - - - - - - - - - - - + + + + + diff --git a/package-lock.json b/package-lock.json index 4aa8d6e..6e6c335 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,25 +9,21 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "electron": "^28.0.0", - "electron-builder": "^24.9.1" + "electron": "^44.2.0", + "electron-builder": "^26.15.3" + }, + "engines": { + "node": ">=22.12.0" } }, - "node_modules/@develar/schema-utils": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "node_modules/@electron-internal/extract-zip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.5.tgz", + "integrity": "sha512-+bqFCP98pLI0Tt0XQo1TmlXtwjWchISndDOxCkEcIuUgXWpBnLyRI+2DU+mesvnMMX6L1XDqYNA0lXNDHd/yiA==", "dev": true, - "dependencies": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=22.12.0" } }, "node_modules/@electron/asar": { @@ -35,6 +31,7 @@ "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", "dev": true, + "license": "MIT", "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", @@ -47,21 +44,30 @@ "node": ">=10.12.0" } }, + "node_modules/@electron/asar/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/@electron/asar/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/@electron/asar/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -69,32 +75,64 @@ "node": "*" } }, + "node_modules/@electron/fuses": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", + "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" + }, + "bin": { + "electron-fuses": "dist/bin.js" + } + }, + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.1.0.tgz", + "integrity": "sha512-3kSBtG8ObcTVfXanm5vVJ6UnBLEVmVsRk1M+vGqCuMBV+XLCbJYuWQful+yIy0GQDsSlK0kHEriEHn7SPk4EnA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", + "env-paths": "^3.0.0", + "graceful-fs": "^4.2.11", "progress": "^2.0.3", - "semver": "^6.2.0", + "semver": "^7.6.3", "sumchecker": "^3.0.1" }, "engines": { - "node": ">=12" + "node": ">=22.12.0" }, "optionalDependencies": { - "global-agent": "^3.0.0" + "undici": "^7.24.4" } }, "node_modules/@electron/notarize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", - "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.1", @@ -109,6 +147,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -119,32 +158,12 @@ "node": ">=10" } }, - "node_modules/@electron/notarize/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/notarize/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@electron/osx-sign": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", - "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", + "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "compare-version": "^0.1.2", "debug": "^4.3.4", @@ -161,25 +180,12 @@ "node": ">=12.0.0" } }, - "node_modules/@electron/osx-sign/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8.0.0" }, @@ -187,203 +193,150 @@ "url": "https://github.com/sponsors/gjtorikian/" } }, - "node_modules/@electron/osx-sign/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "node_modules/@electron/rebuild": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.2.0.tgz", + "integrity": "sha512-RKL/O+jGoXJMxrx/5771y1n0xTKmFuOYGO3gMmwypBM6rsH0kou0mswwdXA2JrhIkE4xyC7v9vGk0n6NPzgOxQ==", "dev": true, + "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.1.1", + "node-abi": "^4.2.0", + "node-api-version": "^0.2.1", + "node-gyp": "^12.2.0", + "read-binary-file-arch": "^1.0.6" + }, + "bin": { + "electron-rebuild": "lib/cli.js" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/osx-sign/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, "engines": { - "node": ">= 10.0.0" + "node": ">=22.12.0" } }, "node_modules/@electron/universal": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", - "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", + "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", "dev": true, + "license": "MIT", "dependencies": { - "@electron/asar": "^3.2.1", - "@malept/cross-spawn-promise": "^1.1.0", + "@electron/asar": "^3.3.1", + "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.3.1", - "dir-compare": "^3.0.0", - "fs-extra": "^9.0.1", - "minimatch": "^3.0.4", - "plist": "^3.0.4" + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" }, "engines": { - "node": ">=8.6" + "node": ">=16.4" } }, + "node_modules/@electron/universal/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/@electron/universal/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/@electron/universal/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", "dev": true, + "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/universal/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "node": ">=14.14" } }, "node_modules/@electron/universal/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@electron/universal/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, + "license": "ISC", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "engines": { - "node": ">=12" + "brace-expansion": "^2.0.2" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, "engines": { - "node": ">=12" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" }, - "engines": { - "node": ">=12" + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=14.14" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", "dev": true, + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "ansi-regex": "^6.0.1" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=14.14" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "dev": true, + "license": "ISC", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "minipass": "^7.0.4" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=18.0.0" } }, "node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", "dev": true, "funding": [ { @@ -395,11 +348,12 @@ "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" } ], + "license": "Apache-2.0", "dependencies": { "cross-spawn": "^7.0.1" }, "engines": { - "node": ">= 10" + "node": ">= 12.13.0" } }, "node_modules/@malept/flatpak-bundler": { @@ -407,6 +361,7 @@ "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.0", @@ -422,6 +377,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -432,35 +388,72 @@ "node": ">=10" } }, - "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.9.4.tgz", + "integrity": "sha512-GjzePcT9Iw8NzeOPf73iNS9xM+TBhd/FilAfP+RQGkTMQJTVWtytN3JHJACCjf/ABNau5S7mS3g+DcuxmRgYEg==", "dev": true, + "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=14" } }, - "node_modules/@malept/flatpak-bundler/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=8.0.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", "dev": true, - "optional": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/webcrypto": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.7.1.tgz", + "integrity": "sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "tslib": "^2.8.1", + "webcrypto-core": "^1.9.2" + }, "engines": { - "node": ">=14" + "node": ">=14.18.0" } }, "node_modules/@sindresorhus/is": { @@ -468,6 +461,7 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -480,6 +474,7 @@ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dev": true, + "license": "MIT", "dependencies": { "defer-to-connect": "^2.0.0" }, @@ -487,20 +482,12 @@ "node": ">=10" } }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", @@ -509,10 +496,11 @@ } }, "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "dev": true, + "license": "MIT", "dependencies": { "@types/ms": "*" } @@ -522,6 +510,7 @@ "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -530,13 +519,15 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -545,26 +536,17 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } + "license": "MIT" }, - "node_modules/@types/plist": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", - "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "@types/node": "*", - "xmlbuilder": ">=11.0.1" + "undici-types": "~7.18.0" } }, "node_modules/@types/responselike": { @@ -572,79 +554,58 @@ "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/@types/verror": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", - "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", + "node_modules/@xmldom/xmldom": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.15.tgz", + "integrity": "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==", "dev": true, - "optional": true + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", - "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", + "node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "dev": true, + "license": "ISC", "engines": { - "node": ">=10.0.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/7zip-bin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", - "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", - "dev": true - }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, - "dependencies": { - "debug": "4" - }, + "license": "MIT", "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -669,212 +630,204 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/app-builder-bin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", - "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", - "dev": true - }, "node_modules/app-builder-lib": { - "version": "24.13.3", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", - "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.15.3.tgz", + "integrity": "sha512-2VnyWkqsP5v5XbBhL3tD5Syx8iNPBYsoU7kY4S2fz7wg8Rj/nztWKCUzGKaFRTv0Xwf3/H058CR1Kvtd/3lRow==", "dev": true, + "license": "MIT", "dependencies": { - "@develar/schema-utils": "~2.6.5", - "@electron/notarize": "2.2.1", - "@electron/osx-sign": "1.0.5", - "@electron/universal": "1.5.1", + "@electron/asar": "3.4.1", + "@electron/fuses": "^1.8.0", + "@electron/get": "^3.0.0", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.3", + "@electron/rebuild": "^4.0.4", + "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", + "@noble/hashes": "^2.2.0", + "@peculiar/webcrypto": "^1.7.1", "@types/fs-extra": "9.0.13", + "ajv": "^8.18.0", + "asn1js": "^3.0.10", "async-exit-hook": "^2.0.1", - "bluebird-lst": "^1.0.9", - "builder-util": "24.13.1", - "builder-util-runtime": "9.2.4", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", "chromium-pickle-js": "^0.2.0", + "ci-info": "4.3.1", "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", - "electron-publish": "24.13.1", - "form-data": "^4.0.0", + "electron-publish": "26.15.3", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", - "is-ci": "^3.0.0", "isbinaryfile": "^5.0.0", + "jiti": "^2.4.2", "js-yaml": "^4.1.0", + "json5": "^2.2.3", "lazy-val": "^1.0.5", - "minimatch": "^5.1.1", - "read-config-file": "6.3.2", - "sanitize-filename": "^1.6.3", - "semver": "^7.3.8", - "tar": "^6.1.12", - "temp-file": "^3.4.0" + "minimatch": "^10.2.5", + "pkijs": "^3.4.0", + "plist": "3.1.0", + "proper-lockfile": "^4.1.2", + "resedit": "^1.7.0", + "semver": "~7.7.3", + "tar": "^7.5.7", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0", + "unzipper": "^0.12.3", + "which": "^5.0.0" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { - "dmg-builder": "24.13.3", - "electron-builder-squirrel-windows": "24.13.3" + "dmg-builder": "26.15.3", + "electron-builder-squirrel-windows": "26.15.3" } }, - "node_modules/app-builder-lib/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/app-builder-lib/node_modules/@electron/get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", + "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" }, "engines": { - "node": ">=12" + "node": ">=14" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" } }, - "node_modules/app-builder-lib/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, + "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=6 <7 || >=8" } }, - "node_modules/app-builder-lib/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, - "node_modules/app-builder-lib/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/app-builder-lib/node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">=8" } }, - "node_modules/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "node_modules/app-builder-lib/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, - "peer": true, - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=6" } }, - "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "peer": true, - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/archiver-utils/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "peer": true - }, - "node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" + "license": "MIT", + "engines": { + "node": ">= 4.0.0" } }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, - "optional": true, - "engines": { - "node": ">=0.8" - } + "license": "Python-2.0" }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "node_modules/asn1js": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", "dev": true, - "optional": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.5", + "tslib": "^2.8.1" + }, "engines": { - "node": ">=8" + "node": ">=12.0.0" } }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/async-exit-hook": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -883,22 +836,35 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, + "license": "ISC", "engines": { "node": ">= 4.0.0" } }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/base64-js": { "version": "1.5.1", @@ -918,34 +884,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "peer": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } + ], + "license": "MIT" }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/bluebird-lst": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", - "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", "dev": true, - "dependencies": { - "bluebird": "^3.5.5" - } + "license": "MIT" }, "node_modules/boolean": { "version": "3.2.0", @@ -953,97 +900,61 @@ "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, + "license": "MIT", "optional": true }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/buffer-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", - "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", - "dev": true, - "engines": { - "node": ">=0.4" + "balanced-match": "^4.0.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "20 || >=22" } }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/builder-util": { - "version": "24.13.1", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", - "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.15.3.tgz", + "integrity": "sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw==", "dev": true, + "license": "MIT", "dependencies": { "@types/debug": "^4.1.6", - "7zip-bin": "~5.2.0", - "app-builder-bin": "4.0.0", - "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.2.4", + "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", + "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-ci": "^3.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", - "temp-file": "^3.4.0" + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/builder-util-runtime": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", - "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz", + "integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" @@ -1052,39 +963,14 @@ "node": ">=12.0.0" } }, - "node_modules/builder-util/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/builder-util/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/builder-util/node_modules/universalify": { + "node_modules/bytestreamjs": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">= 10.0.0" + "node": ">=6.0.0" } }, "node_modules/cacheable-lookup": { @@ -1092,6 +978,7 @@ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.6.0" } @@ -1101,6 +988,7 @@ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, + "license": "MIT", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -1119,6 +1007,7 @@ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -1132,6 +1021,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1144,24 +1034,26 @@ } }, "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "dev": true, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/chromium-pickle-js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -1169,27 +1061,11 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "optional": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1209,6 +1085,7 @@ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" }, @@ -1239,6 +1116,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1251,6 +1129,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } @@ -1260,134 +1139,40 @@ "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", - "dev": true, - "peer": true, - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/config-file-ts": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", - "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", - "dev": true, - "dependencies": { - "glob": "^10.3.10", - "typescript": "^5.3.3" - } - }, - "node_modules/config-file-ts/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/config-file-ts/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/config-file-ts/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } + "license": "MIT" }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "node_modules/crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "optional": true, - "dependencies": { - "buffer": "^5.1.0" - } - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, - "peer": true, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } + "license": "MIT" }, - "node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", - "dev": true, - "peer": true, - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1397,11 +1182,35 @@ "node": ">= 8" } }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -1419,6 +1228,7 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -1434,6 +1244,7 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1446,6 +1257,7 @@ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } @@ -1455,6 +1267,7 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "es-define-property": "^1.0.0", @@ -1473,6 +1286,7 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "define-data-property": "^1.0.1", @@ -1491,6 +1305,7 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -1500,33 +1315,44 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/dir-compare": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", - "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", "dev": true, + "license": "MIT", "dependencies": { - "buffer-equal": "^1.0.0", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " } }, + "node_modules/dir-compare/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/dir-compare/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/dir-compare/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1535,103 +1361,53 @@ } }, "node_modules/dmg-builder": { - "version": "24.13.3", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", - "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.15.3.tgz", + "integrity": "sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ==", "dev": true, + "license": "MIT", "dependencies": { - "app-builder-lib": "24.13.3", - "builder-util": "24.13.1", - "builder-util-runtime": "9.2.4", + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", "fs-extra": "^10.1.0", - "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" - }, - "optionalDependencies": { - "dmg-license": "^1.0.11" } }, - "node_modules/dmg-builder/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, + "license": "BSD-2-Clause", "engines": { "node": ">=12" - } - }, - "node_modules/dmg-builder/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/dmg-builder/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/dmg-license": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", - "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "license": "BSD-2-Clause", "dependencies": { - "@types/plist": "^3.0.1", - "@types/verror": "^1.10.3", - "ajv": "^6.10.0", - "crc": "^3.8.0", - "iconv-corefoundation": "^1.1.7", - "plist": "^3.0.4", - "smart-buffer": "^4.0.2", - "verror": "^1.10.0" - }, - "bin": { - "dmg-license": "bin/dmg-license.js" + "dotenv": "^16.4.5" }, "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", - "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", - "dev": true, - "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -1641,17 +1417,22 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" }, @@ -1663,38 +1444,39 @@ } }, "node_modules/electron": { - "version": "28.3.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-28.3.3.tgz", - "integrity": "sha512-ObKMLSPNhomtCOBAxFS8P2DW/4umkh72ouZUlUKzXGtYuPzgr1SYhskhFWgzAsPtUzhL2CzyV2sfbHcEW4CXqw==", + "version": "44.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-44.2.0.tgz", + "integrity": "sha512-oK1icjhapp3xsUZycO9WZaLmd3hJnA7ieobC4mpdtO1pEN+PPGWpA3m1Mgzzuc1wzSD2Wai4zcH4yfpGJqxFMA==", "dev": true, - "hasInstallScript": true, + "license": "MIT", "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^18.11.18", - "extract-zip": "^2.0.1" + "@electron-internal/extract-zip": "^1.0.1", + "@electron/get": "^5.0.0", + "@types/node": "^24.9.0" }, "bin": { - "electron": "cli.js" + "electron": "cli.js", + "install-electron": "install.js" }, "engines": { - "node": ">= 12.20.55" + "node": ">= 22.12.0" } }, "node_modules/electron-builder": { - "version": "24.13.3", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", - "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.15.3.tgz", + "integrity": "sha512-a1KM5heqS3gQCZzizXEI8RjJy3QVogULPdeSknt76uLDpBIW/HDGsMg/XgP0riP6PI9COsRvFITKKGDqA8fJxA==", "dev": true, + "license": "MIT", "dependencies": { - "app-builder-lib": "24.13.3", - "builder-util": "24.13.1", - "builder-util-runtime": "9.2.4", + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", - "dmg-builder": "24.13.3", + "ci-info": "^4.2.0", + "dmg-builder": "26.15.3", "fs-extra": "^10.1.0", - "is-ci": "^3.0.0", "lazy-val": "^1.0.5", - "read-config-file": "6.3.2", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, @@ -1707,139 +1489,94 @@ } }, "node_modules/electron-builder-squirrel-windows": { - "version": "24.13.3", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", - "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", - "dev": true, - "peer": true, - "dependencies": { - "app-builder-lib": "24.13.3", - "archiver": "^5.3.1", - "builder-util": "24.13.1", - "fs-extra": "^10.1.0" - } - }, - "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.15.3.tgz", + "integrity": "sha512-Jc19XPV9y9+2bAdZPkXuVNGNIEFBq9poHC61l8Kv6FdK7DRG3+Ic0rerC0DXOaeHNz8yW0fg/JnF8GQROOF5MA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "electron-winstaller": "5.4.0" } }, - "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "node_modules/electron-publish": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.15.3.tgz", + "integrity": "sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "@types/fs-extra": "^9.0.11", + "aws4": "^1.13.2", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "form-data": "^4.0.5", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" } }, - "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/electron-winstaller": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", + "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", "dev": true, + "hasInstallScript": true, + "license": "MIT", "peer": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/electron-builder/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@electron/asar": "^3.2.1", + "debug": "^4.1.1", + "fs-extra": "^7.0.1", + "lodash": "^4.17.21", + "temp": "^0.9.0" }, "engines": { - "node": ">=12" - } - }, - "node_modules/electron-builder/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" + "node": ">=8.0.0" }, "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-builder/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/electron-publish": { - "version": "24.13.1", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", - "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", - "dev": true, - "dependencies": { - "@types/fs-extra": "^9.0.11", - "builder-util": "24.13.1", - "builder-util-runtime": "9.2.4", - "chalk": "^4.1.2", - "fs-extra": "^10.1.0", - "lazy-val": "^1.0.5", - "mime": "^2.5.2" + "@electron/windows-sign": "^1.1.2" } }, - "node_modules/electron-publish/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=6 <7 || >=8" } }, - "node_modules/electron-publish/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "node_modules/electron-winstaller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, + "license": "MIT", + "peer": true, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, - "node_modules/electron-publish/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/electron-winstaller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, + "license": "MIT", + "peer": true, "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/emoji-regex": { @@ -1853,30 +1590,37 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "dev": true, + "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -1886,15 +1630,17 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -1907,6 +1653,7 @@ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -1922,6 +1669,7 @@ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/escalade": { @@ -1938,6 +1686,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=10" @@ -1946,154 +1695,140 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", "dev": true, - "engines": [ - "node >=0.6.0" - ], - "optional": true + "license": "Apache-2.0" }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "node_modules/fast-uri": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "pend": "~1.2.0" + "minimatch": "^5.0.1" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, + "license": "MIT", "dependencies": { - "minimatch": "^5.0.1" + "balanced-match": "^1.0.0" } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, + "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10" } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "peer": true - }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2112,6 +1847,7 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -2136,6 +1872,7 @@ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, + "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -2149,6 +1886,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -2163,8 +1901,9 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2180,21 +1919,30 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2207,6 +1955,7 @@ "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "dev": true, + "license": "BSD-3-Clause", "optional": true, "dependencies": { "boolean": "^3.0.1", @@ -2220,24 +1969,12 @@ "node": ">=10.0" } }, - "node_modules/global-agent/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "define-properties": "^1.2.1", @@ -2255,6 +1992,7 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -2267,6 +2005,7 @@ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", @@ -2298,6 +2037,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2307,6 +2047,7 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "es-define-property": "^1.0.0" @@ -2320,6 +2061,7 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -2332,6 +2074,7 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -2343,10 +2086,11 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -2359,6 +2103,7 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -2370,20 +2115,21 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, + "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/http2-wrapper": { @@ -2391,6 +2137,7 @@ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, + "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" @@ -2400,73 +2147,26 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, + "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-corefoundation": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "cli-truncate": "^2.1.0", - "node-addon-api": "^1.6.3" - }, - "engines": { - "node": "^8.11.2 || >=10" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "node": ">= 14" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2476,19 +2176,8 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } + "license": "ISC" }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", @@ -2504,13 +2193,14 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, - "peer": true + "license": "MIT" }, "node_modules/isbinaryfile": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz", "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 18.0.0" }, @@ -2519,24 +2209,13 @@ } }, "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" } }, "node_modules/jake": { @@ -2544,6 +2223,7 @@ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", @@ -2556,11 +2236,32 @@ "node": ">=10" } }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -2572,149 +2273,80 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, + "license": "ISC", "optional": true }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/lazy-val": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", - "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", - "dev": true - }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "dev": true, - "peer": true, - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "peer": true - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "peer": true + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "dev": true, - "peer": true + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "peer": true + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", "dev": true, - "peer": true + "license": "MIT" }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, - "peer": true + "license": "MIT" }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2724,6 +2356,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -2736,6 +2369,7 @@ "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "escape-string-regexp": "^4.0.0" @@ -2749,6 +2383,7 @@ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2758,6 +2393,7 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -2770,6 +2406,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -2779,6 +2416,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -2791,20 +2429,25 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.8" }, "engines": { - "node": ">=10" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -2812,77 +2455,170 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, + "license": "MIT", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "minipass": "^7.1.2" }, "engines": { - "node": ">= 8" + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.35.0.tgz", + "integrity": "sha512-ymk4aIzxdPopw2giv8Fs1Ec6vybGkjmyxUwVqhkI4MCy2tVfXdkOGGWieWVjL0THgH+7a8lRdevyupoYj3Js/Q==", + "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "semver": "^7.6.3" }, "engines": { - "node": ">=8" + "node": ">=22.12.0" } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-gyp": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" + }, "bin": { - "mkdirp": "bin/cmd.js" + "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": ">=10" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/node-gyp/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "dev": true, - "optional": true + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/node-gyp/node_modules/undici": { + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.1.tgz", + "integrity": "sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==", "dev": true, - "peer": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=18.17" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/normalize-url": { @@ -2890,6 +2626,7 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2902,6 +2639,7 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">= 0.4" @@ -2912,6 +2650,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -2921,21 +2660,33 @@ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2945,49 +2696,83 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=12", + "npm": ">=6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/jet2jet" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/pkijs/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, + "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -2997,18 +2782,59 @@ "node": ">=10.4.0" } }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, - "peer": true + "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -3018,6 +2844,7 @@ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, + "license": "MIT", "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" @@ -3026,23 +2853,47 @@ "node": ">=10" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", "dev": true, + "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.2.0.tgz", + "integrity": "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=16.0.0" } }, "node_modules/quick-lru": { @@ -3050,6 +2901,7 @@ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3057,46 +2909,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-config-file": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", - "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", "dev": true, + "license": "MIT", "dependencies": { - "config-file-ts": "^0.2.4", - "dotenv": "^9.0.2", - "dotenv-expand": "^5.1.0", - "js-yaml": "^4.1.0", - "json5": "^2.2.0", - "lazy-val": "^1.0.4" + "debug": "^4.3.4" }, - "engines": { - "node": ">=12.0.0" + "bin": { + "read-binary-file-arch": "cli.js" } }, "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "peer": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "minimatch": "^5.1.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "node_modules/require-directory": { @@ -3108,17 +2947,47 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, "node_modules/resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, + "license": "MIT", "dependencies": { "lowercase-keys": "^2.0.0" }, @@ -3131,15 +3000,32 @@ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, + "node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, + "license": "BSD-3-Clause", "optional": true, "dependencies": { "boolean": "^3.0.1", @@ -3154,57 +3040,43 @@ } }, "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "license": "MIT" }, "node_modules/sanitize-filename": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", - "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", "dev": true, + "license": "WTFPL OR ISC", "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "node_modules/sax": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", - "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", "dev": true, + "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-compare": { @@ -3212,6 +3084,7 @@ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/serialize-error": { @@ -3219,6 +3092,7 @@ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "type-fest": "^0.13.1" @@ -3235,6 +3109,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -3247,21 +3122,17 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "license": "ISC" }, "node_modules/simple-update-notifier": { "version": "2.0.0", @@ -3275,49 +3146,12 @@ "node": ">=10" } }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "optional": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -3327,6 +3161,7 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -3337,6 +3172,7 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true, + "license": "BSD-3-Clause", "optional": true }, "node_modules/stat-mode": { @@ -3344,18 +3180,19 @@ "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "safe-buffer": "~5.1.0" } }, "node_modules/string-width": { @@ -3372,21 +3209,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3399,24 +3221,12 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "debug": "^4.1.0" }, @@ -3429,6 +3239,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3437,38 +3248,45 @@ } }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" }, "engines": { - "node": ">=6" + "node": ">=6.0.0" } }, "node_modules/temp-file": { @@ -3476,51 +3294,55 @@ "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", "dev": true, + "license": "MIT", "dependencies": { "async-exit-hook": "^2.0.1", "fs-extra": "^10.0.0" } }, - "node_modules/temp-file/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", + "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" + "semver": "^5.5.0" } }, - "node_modules/temp-file/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "node_modules/temp-file/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.14" } @@ -3530,6 +3352,7 @@ "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "dev": true, + "license": "MIT", "dependencies": { "tmp": "^0.2.0" } @@ -3539,15 +3362,24 @@ "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", "dev": true, + "license": "WTFPL", "dependencies": { "utf8-byte-length": "^1.0.1" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-fest": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true, + "license": "(MIT OR CC0-1.0)", "optional": true, "engines": { "node": ">=10" @@ -3556,84 +3388,105 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "node_modules/undici": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.1.tgz", + "integrity": "sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "license": "MIT", + "optional": true, "engines": { - "node": ">=14.17" + "node": ">=20.18.1" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" + } + }, + "node_modules/unzipper": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.5.tgz", + "integrity": "sha512-tXYOi9R57Uj/2Z25SOs5RRSzq886MBQj2gY8dPL+xl/kv6s6SvByoKfAtvfVeEuhntWDgjd2o9p2lb4TVPAz0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "~3.7.2", + "duplexer2": "~0.1.4", + "fs-extra": "11.3.1", + "graceful-fs": "^4.2.2", + "node-int64": "^0.4.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/unzipper/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", "dev": true, + "license": "MIT", "dependencies": { - "punycode": "^2.1.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" } }, "node_modules/utf8-byte-length": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", - "dev": true + "dev": true, + "license": "(WTFPL OR MIT)" }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, - "peer": true + "license": "MIT" }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "node_modules/webcrypto-core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.9.2.tgz", + "integrity": "sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" } }, "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, + "license": "ISC", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "node-which": "bin/node-which" + "node-which": "bin/which.js" }, "engines": { - "node": ">= 8" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/wrap-ansi": { @@ -3653,35 +3506,19 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0" } @@ -3699,7 +3536,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yargs": { "version": "17.7.2", @@ -3728,51 +3566,17 @@ "node": ">=12" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "peer": true, - "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" - }, + "license": "MIT", "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", - "dev": true, - "peer": true, - "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" + "node": ">=10" }, - "engines": { - "node": ">= 10" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/package.json b/package.json index 6dd1ed8..7de2eb1 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,14 @@ { "name": "praxium-map-editor", "version": "1.0.0", + "desktopName": "praxium-map-editor.desktop", "description": "Desktop application for editing the Praxium Interactive Network Map data", "main": "electron/main.js", "scripts": { "start": "electron .", + "check": "node --check map.js && node --check network-data.js && node --check admin/admin.js && node --check admin/admin-auth.js && node --check admin-editor/app.js && node --check electron/main.js && node --check electron/preload.js", + "test": "node test/public-assets.test.js && node test/network-data.test.js && node test/api.test.js", + "validate": "npm run check && npm test", "pack": "electron-builder --dir", "dist": "electron-builder --publish never", "dist:win": "electron-builder --win --publish never", @@ -20,9 +24,12 @@ "email": "info@praxiumfoundation.com" }, "license": "MIT", + "engines": { + "node": ">=22.12.0" + }, "devDependencies": { - "electron": "^28.0.0", - "electron-builder": "^24.9.1" + "electron": "^44.2.0", + "electron-builder": "^26.15.3" }, "build": { "appId": "com.praxium.mapeditor", @@ -41,11 +48,15 @@ "target": [ { "target": "nsis", - "arch": ["x64"] + "arch": [ + "x64" + ] }, { "target": "portable", - "arch": ["x64"] + "arch": [ + "x64" + ] } ], "icon": "electron/icons/icon.ico", @@ -65,10 +76,14 @@ "artifactName": "${productName}-${version}-macOS.${ext}" }, "linux": { - "target": ["AppImage", "deb"], + "target": [ + "AppImage", + "deb" + ], "icon": "electron/icons", "artifactName": "${productName}-${version}-Linux.${ext}", - "category": "Utility" + "category": "Utility", + "syncDesktopName": true } } } diff --git a/praxium-contrast-comparison.html b/praxium-contrast-comparison.html new file mode 100644 index 0000000..5246638 --- /dev/null +++ b/praxium-contrast-comparison.html @@ -0,0 +1,254 @@ + + + + + +Praxium — clay contrast comparison + + + + + + + +
+

Three versions of clay

+

Same hue, three lightness values. The question is which one carries text and button labels. Headings are unaffected — at 2.2rem and 1.6rem they clear the large-text threshold in all three. Ratios below are measured against the live palette, not estimated.

+
+ +
+ +
+
+

The candidates

+

The middle value is the tempting one — a small nudge that looks like it should be enough. It isn't, and that's the useful thing to see.

+
+ +
+
+
+
+
As built
+
#A3826C · L 53%
+
The current brand value. Fine as a surface fill and for large headings. Fails as text or as a button ground.
+
+
+
+
+
+
Nudged
+
#8D6A52 · L 44%
+
Visibly darker, close to the original. Reaches 4.23:1 — still short of 4.5:1, so it fails while looking like it shouldn't.
+
+
+
+
+
+
Corrected
+
#85624B · L 41%
+
Clears AA in both directions — as text on paper, and as a fill under white labels. Same hue family, reads as the same brand.
+
+
+
+
+ +
+
+

Body link on paper

+

This is the failure that matters most, because paragraph links appear on every page at 16px.

+
+
+
+
As built#A3826C
+
+

Sessions are open to anyone. Read the Endeavor philosophy before you come.

+ 3.06 : 1 — fails AA +
+
+
+
Nudged#8D6A52
+
+

Sessions are open to anyone. Read the Endeavor philosophy before you come.

+ 4.23 : 1 — fails AA +
+
+
+
Corrected#85624B
+
+

Sessions are open to anyone. Read the Endeavor philosophy before you come.

+ 4.75 : 1 — passes AA +
+
+
+
+ +
+
+

Primary button

+

White labels at 16px on a clay fill. The current combination is the single most-used interactive element on the site.

+
+
+
+
As built#A3826C
+
+ Start your journey +
3.52 : 1 — fails AA
+
+
+
+
Nudged#8D6A52
+
+ Start your journey +
4.87 : 1 — passes AA
+
+
+
+
Corrected#85624B
+
+ Start your journey +
5.46 : 1 — passes AA
+
+
+
+
+ +
+
+

Headings — unchanged either way

+

Included so the tradeoff is visible. h3 and h4 already pass as large text at 3.06:1, so darkening clay costs you the lighter heading tone you currently have. This is the only place the correction takes something away.

+
+
+
+
As built#A3826C
+
+
Attend Shinrin Yoku
+
Reconnect with Nature
+
3.06 : 1 — passes for large text
+
+
+
+
Nudged#8D6A52
+
+
Attend Shinrin Yoku
+
Reconnect with Nature
+
4.23 : 1 — passes
+
+
+
+
Corrected#85624B
+
+
Attend Shinrin Yoku
+
Reconnect with Nature
+
4.75 : 1 — passes
+
+
+
+
+ +
+

All measurements

+ + + + + + + + + + + + + +
ForegroundBackgroundRatioWhere it's usedVerdict
#3E3D3D#EFEFEE9.41 : 1Body copy, h1, h2Passes AAA
#A3826C#EFEFEE3.06 : 1Links, h3, h4Large text only
#FFFFFF#A3826C3.52 : 1Primary button labelsFails AA
#8D6A52#EFEFEE4.23 : 1—Fails AA
#FFFFFF#8D6A524.87 : 1—Passes AA
#85624B#EFEFEE4.75 : 1—Passes AA
#FFFFFF#85624B5.46 : 1—Passes AA
#6E6C6B#EFEFEE4.54 : 1Proposed muted textPasses AA
#C7AC98#3E3D3D5.04 : 1Reserved for dark modePasses AA
+
+ +
+

What I'd pick

+
+

Split the role rather than replacing the color. Keep #A3826C as the display value — h3, h4, large numerals, decorative fills, the announcement bar. It's the color the foundation already recognises as theirs, and at those sizes it's compliant.

+

Introduce #85624B as a separate token used only where the text is small or a label sits on top: paragraph links, button fills, form accents, anything under 24px. Two tokens, one hue, no visible rebrand.

+

The reason not to take the nudged value: at 4.23:1 it fails the threshold while looking like a fix, which is worse than either extreme. You'd carry the same problem with less of the original color to show for it.

+

Whichever you choose, the token file ships with both defined and a single line that selects between them, so the decision stays reversible.

+
+
+ +
+ + + + + diff --git a/praxium-tokens-v2.css b/praxium-tokens-v2.css new file mode 100644 index 0000000..2056b67 --- /dev/null +++ b/praxium-tokens-v2.css @@ -0,0 +1,453 @@ +/* ========================================================================== + Praxium Learning Foundation — design tokens v2 + Three tiers: primitives -> semantic -> component. + + Light mode is populated. Dark mode is scaffolded but not enabled: the + structure is in place and the ramp steps exist, so turning it on later is + a matter of filling one block, not rewriting components. + + THE ONE RULE + Components never reference a primitive. If you write var(--clay-500) in a + button, a semantic token is missing. Add it instead. + ========================================================================== */ + +/* ========================================================================== + TIER 1 — PRIMITIVES + Raw values. No meaning attached. Never used directly outside this file. + ========================================================================== */ + +:root { + + /* --- Clay --------------------------------------------------------------- + The brand hue, ~24deg. 500 is the value the foundation already uses. + 700 exists because 500 fails contrast as small text; 300 exists so dark + mode has something to reach for later. */ + + --clay-050: #f8f4f2; + --clay-100: #eee7e2; + --clay-200: #ddcfc5; + --clay-300: #c7ac98; /* 5.04:1 on ink — reserved for dark mode */ + --clay-400: #b5977f; + --clay-500: #a3826c; /* brand value. 3.06:1 on paper — large text only */ + --clay-600: #8d6a52; /* 4.23:1 on paper — still fails AA, see comparison */ + --clay-700: #85624b; /* 4.75:1 on paper, 5.46:1 under white — passes AA */ + --clay-800: #654a38; + --clay-900: #45301f; + + /* --- Ink ---------------------------------------------------------------- + Neutral, warmed by a trace of red to sit with clay. 900 is the brand + value used for body copy. */ + + --ink-100: #e2e0de; + --ink-200: #c9c7c5; + --ink-300: #aba9a7; + --ink-400: #84817e; /* ~3:1 on paper — usable for functional borders */ + --ink-500: #6e6c6b; /* 4.54:1 on paper — passes AA for muted text */ + --ink-600: #5c5a5a; + --ink-700: #4a4948; + --ink-900: #3e3d3d; /* brand value. 9.41:1 on paper */ + + /* --- Paper -------------------------------------------------------------- + The warm neutral grounds. 100 is the brand page background. */ + + --paper-000: #ffffff; + --paper-050: #f7f7f6; + --paper-100: #efefee; /* brand value */ + --paper-200: #e4e4e2; + + --true-black: #000000; + + /* --- Type ---------------------------------------------------------------- */ + + --font-display: "Epilogue", ui-sans-serif, system-ui, sans-serif; + --font-text: "Poppins", ui-sans-serif, system-ui, sans-serif; + + --weight-display: 500; + --weight-text: 400; + + --tracking-display: -0.02em; + --tracking-text: 0em; + + --leading-display: 1.2; + --leading-text: 1.5; + --leading-ui: 1.2; + + /* Fluid steps. Squarespace does this with nested min()/max()/vw; clamp() + produces the same curve in one readable line. Floor is the mobile size, + ceiling is the value from the current site. */ + + --size-h1: clamp(2.5rem, 1.4rem + 4.4vw, 4rem); + --size-h2: clamp(1.9rem, 1.3rem + 2.6vw, 2.8rem); + --size-h3: clamp(1.6rem, 1.25rem + 1.6vw, 2.2rem); + --size-h4: clamp(1.3rem, 1.15rem + 0.7vw, 1.6rem); + --size-lead: clamp(1.15rem, 1.05rem + 0.5vw, 1.4rem); + --size-body: 1rem; + --size-fine: 0.9rem; + --size-site-title: 1.5rem; + + /* --- Space -------------------------------------------------------------- + The current site has no spacing scale, which is why the form field gap + (20px) and the control gap (16px) don't relate. This is a 4px base. */ + + --space-1: 0.25rem; /* 4px */ + --space-2: 0.5rem; /* 8px */ + --space-3: 0.75rem; /* 12px */ + --space-4: 1rem; /* 16px */ + --space-5: 1.25rem; /* 20px */ + --space-6: 1.5rem; /* 24px */ + --space-8: 2rem; /* 32px */ + --space-12: 3rem; /* 48px */ + --space-16: 4rem; /* 64px */ + --space-24: 6rem; /* 96px */ + + /* --- Shape --------------------------------------------------------------- */ + + --radius-btn: 6.4px; + --radius-field: 20px; + --radius-none: 0px; + + /* --- Layout -------------------------------------------------------------- */ + + --page-max: 1400px; + --gutter: 4vw; + + /* --- Motion -------------------------------------------------------------- */ + + --duration: 0.65s; + --delay: 0.6s; + --ease: cubic-bezier(0.4, 0, 0.2, 1); +} + +@media (max-width: 767px) { + :root { --gutter: 6vw; } +} + + +/* ========================================================================== + TIER 2 — SEMANTIC + Role, not appearance. This is the only tier that changes between light and + dark, and the only tier components are allowed to read from. + ========================================================================== */ + +:root { + + /* --- Surfaces ----------------------------------------------------------- */ + + --surface: var(--paper-100); /* the page */ + --surface-sunken: var(--paper-200); /* wells, inset bands */ + --surface-raised: var(--paper-000); /* cards, form fields, header */ + --surface-accent: var(--clay-500); /* the clay band */ + --surface-inverse: var(--ink-900); /* the dark band */ + + /* --- Text --------------------------------------------------------------- */ + + --text: var(--ink-900); + --text-muted: var(--ink-500); /* 4.54:1 — meta, captions */ + --text-display: var(--ink-900); /* h1, h2 */ + --text-accent: var(--clay-500); /* h3, h4 — large text only */ + --text-on-accent: var(--paper-000); + --text-on-inverse: var(--paper-000); + + /* --- Interactive -------------------------------------------------------- + CONTRAST SWITCH. This is the decision from the comparison page. + Set to --clay-500 to preserve the exact current appearance and accept + the AA failures; set to --clay-700 to pass. Nothing else needs editing. + */ + --interactive: var(--clay-700); + --interactive-hover: var(--clay-800); + --interactive-text: var(--paper-000); + + --link: var(--interactive); + --link-hover: var(--interactive-hover); + + /* --- Lines and edges ---------------------------------------------------- */ + + --border-subtle: var(--ink-200); /* decorative hairlines */ + --border-strong: var(--ink-400); /* form fields, functional edges */ + --divider: var(--clay-500); /* the line block on the live site */ + + /* --- Focus ------------------------------------------------------------- + The live site has no focus token; it borrows the nav link color. Naming + it means it survives a navigation restyle. */ + + --focus-ring: var(--ink-900); + --focus-width: 2px; + --focus-offset: 4px; + + /* --- Status ------------------------------------------------------------ + Absent from the live site — form errors currently render in Squarespace's + hardcoded #bd0000. These are proposals, tuned to sit with the palette + rather than fight it. Replace if the board has opinions. */ + + --status-error: #8f2f24; + --status-error-bg: #f4dedb; + --status-success: #2c4d33; + --status-success-bg:#e2ece4; + --status-warning: #7a5518; + --status-warning-bg:#f5e9d3; +} + + +/* -------------------------------------------------------------------------- + DARK MODE — SCAFFOLD ONLY. Not enabled. + + The ramp steps it needs already exist (clay-300 measures 5.04:1 on ink). + To turn it on: delete the comment markers, verify every pair, and add + to the document head. + + Note what does NOT change: no component rule below needs touching, because + components only read Tier 2. That is the whole point of the split. + +@media (prefers-color-scheme: dark) { + :root { + --surface: var(--ink-900); + --surface-sunken: #333232; + --surface-raised: var(--ink-700); + --surface-accent: var(--clay-800); + --surface-inverse: var(--paper-100); + + --text: var(--paper-100); + --text-muted: var(--ink-300); + --text-display: var(--paper-000); + --text-accent: var(--clay-300); + --text-on-accent: var(--paper-000); + --text-on-inverse: var(--ink-900); + + --interactive: var(--clay-300); + --interactive-hover: var(--clay-200); + --interactive-text: var(--ink-900); + + --border-subtle: var(--ink-700); + --border-strong: var(--ink-500); + --divider: var(--clay-400); + + --focus-ring: var(--paper-100); + } +} +-------------------------------------------------------------------------- */ + + +/* ========================================================================== + SECTION THEMES — relative, not absolute. + + The live site has ten absolute themes (white, black, bright...). Absolute + names break the moment dark mode arrives: a section named "white" has to + stay white or stop meaning anything. + + These four are relative to the current mode, so they survive it. Mapping + from the old names: + white, light -> default (no attribute needed) + white-bold -> emphasis + dark, black, + dark-bold, black-bold -> inverse + bright -> accent + bright-inverse -> default with accent text + ========================================================================== */ + +[data-theme="emphasis"] { + --surface: var(--surface-raised); + --text: var(--text-display); + --text-accent: var(--interactive); +} + +[data-theme="inverse"] { + --surface: var(--surface-inverse); + --text: var(--text-on-inverse); + --text-display: var(--text-on-inverse); + --text-muted: var(--ink-300); + --text-accent: var(--clay-300); + --interactive: var(--clay-300); + --interactive-text: var(--ink-900); + --border-subtle: var(--ink-700); + --focus-ring: var(--paper-100); +} + +[data-theme="accent"] { + --surface: var(--surface-accent); + --text: var(--text-on-accent); + --text-display: var(--text-on-accent); + --text-muted: var(--clay-100); + --text-accent: var(--text-on-accent); + --interactive: var(--paper-000); + --interactive-text: var(--clay-700); + --border-subtle: var(--clay-400); + --focus-ring: var(--paper-000); +} + +[data-theme] { + background-color: var(--surface); + color: var(--text); +} + + +/* ========================================================================== + TIER 3 — COMPONENTS + Thin. Only reads Tier 2. Add a token above rather than a value here. + ========================================================================== */ + +html { font-size: 16px; } + +body { + margin: 0; + background-color: var(--surface); + color: var(--text); + font-family: var(--font-text); + font-weight: var(--weight-text); + font-size: var(--size-body); + line-height: var(--leading-text); + letter-spacing: var(--tracking-text); + -webkit-font-smoothing: antialiased; +} + +h1, h2, h3, h4 { + font-family: var(--font-display); + font-weight: var(--weight-display); + line-height: var(--leading-display); + letter-spacing: var(--tracking-display); + margin: var(--space-8) 0 var(--space-4); +} + +h1 { font-size: var(--size-h1); color: var(--text-display); } +h2 { font-size: var(--size-h2); color: var(--text-display); } +h3 { font-size: var(--size-h3); color: var(--text-accent); } +h4 { font-size: var(--size-h4); color: var(--text-accent); } + +p { margin: 0 0 var(--space-4); max-width: 68ch; } +.lead { font-size: var(--size-lead); } +.fine { font-size: var(--size-fine); color: var(--text-muted); } + +a { color: var(--link); text-underline-offset: 0.2em; } +a:hover { color: var(--link-hover); } + +:focus-visible { + outline: var(--focus-width) solid var(--focus-ring); + outline-offset: var(--focus-offset); +} + +hr { + border: 0; + border-top: 1px solid var(--divider); + margin: var(--space-12) 0; +} + +/* --- Layout -------------------------------------------------------------- */ + +.page { + max-width: var(--page-max); + margin-inline: auto; + padding-inline: var(--gutter); +} + +.section { padding-block: var(--space-24); } + +/* --- Buttons ------------------------------------------------------------- + Rank is height and border, not color. All three use --interactive. */ + +.btn { + display: inline-block; + font-family: var(--font-text); + font-size: var(--size-body); + line-height: var(--leading-ui); + text-align: center; + text-decoration: none; + border-radius: var(--radius-btn); + border: 0; + cursor: pointer; +} + +.btn--primary { + background: var(--interactive); + color: var(--interactive-text); + padding: 1.5em 2em; +} +.btn--primary:hover { background: var(--interactive-hover); color: var(--interactive-text); } + +.btn--secondary { + background: var(--interactive); + color: var(--interactive-text); + padding: 1.2em 2em; + border: 1px solid var(--interactive-text); +} + +.btn--tertiary { + background: transparent; + color: var(--interactive); + padding: 1.2em 0; + text-decoration: underline; + text-underline-offset: 0.25em; +} + +/* --- Form controls ------------------------------------------------------- + The asymmetric corner is the system's one piece of signature geometry. + Inputs only. Putting it on cards turns a detail into a motif. */ + +.field { margin-bottom: var(--space-5); } + +.field__label { + display: block; + margin-bottom: var(--space-1); + font-size: var(--size-body); +} + +.field__input { + width: 100%; + font-family: var(--font-text); + font-size: var(--size-body); + color: var(--text); + background: var(--surface-raised); + border: 1px solid var(--border-strong); + border-radius: var(--radius-none) var(--radius-field) var(--radius-field) var(--radius-field); + padding: 10px var(--space-5); +} + +.field__input::placeholder { color: var(--text-muted); } + +.field--invalid .field__input { border-color: var(--status-error); } +.field__error { + margin-top: var(--space-1); + font-size: var(--size-fine); + color: var(--status-error); +} + +.control { + display: flex; + align-items: center; + gap: 11px; + margin-bottom: var(--space-4); +} + +.control__box { + inline-size: 25px; + block-size: 25px; + flex: 0 0 auto; + background: var(--surface-raised); + border: 3px solid var(--border-strong); + border-radius: var(--radius-none) var(--radius-field) var(--radius-field) var(--radius-field); +} + +.control input:checked + .control__box { + background: var(--interactive); + border-color: var(--interactive); +} + +/* --- Motion --------------------------------------------------------------- */ + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + + +/* ========================================================================== + STILL OPEN + + - Elevation. No shadow tokens. The system is flat, which is defensible, + but record it as a decision rather than an omission. + - Status colors above are proposals, not extracted values. + - Type scale floors are estimates. Measure a real page at 375px before + committing to the clamp() minimums. + ========================================================================== */ diff --git a/test/api.test.js b/test/api.test.js index 01b4395..2e40929 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,6 +1,6 @@ // Lightweight functional test for the /api handlers, using an in-memory fake Gist backend // (no real network calls to GitHub). Run with: node test/api.test.js -process.env.SESSION_SECRET = 'test-secret-value-not-real'; +process.env.SESSION_SECRET = 'test-secret-value-not-real-at-least-32-bytes'; process.env.ADMIN_PASSPHRASE = 'correct-horse-battery-staple'; process.env.GIST_ID = 'fake-gist-id'; process.env.GITHUB_PAT = 'fake-pat-not-real'; @@ -14,6 +14,7 @@ let fakeGistFile = JSON.stringify([ { id: 2, name: 'Org Two', location: 'Toronto, ON', coordinates: [43.65, -79.38], category: 'Non-Profit', description: 'desc2', contact: { website: 'https://two.example' }, connections: [1] }, ]); let fakeUpdatedAt = '2026-01-01T00:00:00Z'; +let fakeRevision = 'revision-0'; let versionCounter = 0; global.fetch = async (url, opts = {}) => { @@ -25,6 +26,7 @@ global.fetch = async (url, opts = {}) => { status: 200, json: async () => ({ updated_at: fakeUpdatedAt, + history: [{ version: fakeRevision }], files: { 'organization-network-map.json': { content: fakeGistFile, truncated: false } }, }), }; @@ -34,21 +36,25 @@ global.fetch = async (url, opts = {}) => { fakeGistFile = body.files['organization-network-map.json'].content; versionCounter += 1; fakeUpdatedAt = `2026-01-01T00:0${versionCounter}:00Z`; + fakeRevision = `revision-${versionCounter}`; return { ok: true, status: 200, - json: async () => ({ updated_at: fakeUpdatedAt }), + json: async () => ({ + updated_at: fakeUpdatedAt, + history: [{ version: fakeRevision }], + }), }; } } throw new Error('Unexpected fetch to ' + u); }; -function mockReqRes({ method = 'GET', body = {}, cookie = '' } = {}) { +function mockReqRes({ method = 'GET', body = {}, cookie = '', ip = '1.2.3.4' } = {}) { let statusCode = 200; let headers = {}; let ended = ''; - const req = { method, body, headers: { cookie }, socket: { remoteAddress: '1.2.3.4' } }; + const req = { method, body, headers: { cookie }, socket: { remoteAddress: ip } }; const res = { status(code) { statusCode = code; return res; }, setHeader(k, v) { headers[k] = v; return res; }, @@ -70,6 +76,7 @@ function assert(cond, message) { async function run() { const auth = require(path.join(REPO, 'api/auth.js')); const gist = require(path.join(REPO, 'api/gist.js')); + const geocode = require(path.join(REPO, 'api/geocode.js')); const publish = require(path.join(REPO, 'api/publish.js')); const logout = require(path.join(REPO, 'api/logout.js')); @@ -87,6 +94,11 @@ async function run() { await auth(req, res); const r = res._result(); assert(r.statusCode === 200, 'expected 200 for correct passphrase'); + assert(r.headers['Cache-Control'] === 'no-store', 'expected private API responses not to be cached'); + assert( + r.headers['X-Content-Type-Options'] === 'nosniff', + 'expected API responses to disable content-type sniffing', + ); sessionCookie = extractCookieValue(r.headers['Set-Cookie']); assert(sessionCookie, 'expected a session cookie to be set'); } @@ -95,11 +107,55 @@ async function run() { { let last; for (let i = 0; i < 5; i++) { - const { req, res } = mockReqRes({ method: 'POST', body: { passphrase: 'nope' + i } }); + const { req, res } = mockReqRes({ + method: 'POST', + body: { passphrase: 'nope' + i }, + ip: '5.6.7.8', + }); await auth(req, res); last = res._result(); + assert(last.statusCode === 401, `expected failed attempt ${i + 1} to return 401`); + } + const { req, res } = mockReqRes({ + method: 'POST', + body: { passphrase: 'one-too-many' }, + ip: '5.6.7.8', + }); + await auth(req, res); + last = res._result(); + assert(last.statusCode === 429, 'expected the request after 5 failures to be rate limited'); + } + + console.log('--- successful login clears prior failed attempts ---'); + { + const ip = '9.8.7.6'; + for (let i = 0; i < 4; i++) { + const { req, res } = mockReqRes({ + method: 'POST', + body: { passphrase: 'wrong-' + i }, + ip, + }); + await auth(req, res); + assert(res._result().statusCode === 401, 'expected pre-success failure to return 401'); + } + { + const { req, res } = mockReqRes({ + method: 'POST', + body: { passphrase: 'correct-horse-battery-staple' }, + ip, + }); + await auth(req, res); + assert(res._result().statusCode === 200, 'expected success before limit'); + } + { + const { req, res } = mockReqRes({ + method: 'POST', + body: { passphrase: 'wrong-after-success' }, + ip, + }); + await auth(req, res); + assert(res._result().statusCode === 401, 'expected cleared failure bucket after success'); } - assert(last.statusCode === 429, 'expected 429 once rate limited'); } console.log('--- GET /api/gist without session -> 401 ---'); @@ -118,9 +174,23 @@ async function run() { assert(r.statusCode === 200, 'expected 200 with session'); assert(Array.isArray(r.body.organizations) && r.body.organizations.length === 2, 'expected 2 orgs'); assert(Array.isArray(r.body.categories) && r.body.categories.length > 0, 'expected categories list'); + assert(r.body.version === 'revision-0', 'expected the immutable Gist revision as version'); currentVersion = r.body.version; } + console.log('--- geocode rejects overlong addresses before network access ---'); + { + const { req, res } = mockReqRes({ + method: 'POST', + cookie: `praxium_admin_session=${sessionCookie}`, + body: { address: 'x'.repeat(301) }, + }); + await geocode(req, res); + const r = res._result(); + assert(r.statusCode === 400, 'expected an overlong address to be rejected'); + assert(r.body.error === 'address_too_long', 'expected address_too_long error'); + } + console.log('--- publish create with stale version -> 409 ---'); { const { req, res } = mockReqRes({ @@ -131,7 +201,9 @@ async function run() { expectedVersion: 'stale-version-value', org: { name: 'Org Three', location: 'Calgary, AB', coordinates: [51.05, -114.07], - category: 'Health', description: 'new org', contact: { website: 'https://three.example' }, connections: [1], + country: 'Canada', category: 'Health', description: 'new org', + relationship: 'A shared learning and community initiative.', + contact: { website: 'https://three.example' }, connections: [1], }, }, }); @@ -150,7 +222,9 @@ async function run() { expectedVersion: currentVersion, org: { name: 'Org Three', location: 'Calgary, AB', coordinates: [51.05, -114.07], - category: 'Health', description: 'new org', contact: { website: 'https://three.example' }, connections: [1], + country: 'Canada', category: 'Health', description: 'new org', + relationship: 'A shared learning and community initiative.', + contact: { website: 'https://three.example' }, connections: [1], }, }, }); @@ -159,6 +233,11 @@ async function run() { assert(r.statusCode === 200, 'expected 200 for valid create'); newOrgId = r.body.org.id; assert(newOrgId === 3, 'expected assigned id 3, got ' + newOrgId); + assert(r.body.org.country === 'Canada', 'expected country to be persisted'); + assert( + r.body.org.relationship === 'A shared learning and community initiative.', + 'expected relationship context to be persisted', + ); currentVersion = r.body.version; const orgs = JSON.parse(fakeGistFile); @@ -181,6 +260,58 @@ async function run() { assert(res._result().statusCode === 400, 'expected 400 validation failure'); } + console.log('--- validation failure (overlong fields) -> 400 ---'); + { + const { req, res } = mockReqRes({ + method: 'POST', + cookie: `praxium_admin_session=${sessionCookie}`, + body: { + mode: 'create', + expectedVersion: currentVersion, + org: { + name: 'x'.repeat(201), + location: 'Nowhere', + coordinates: [0, 0], + category: 'Health', + description: 'x', + contact: { website: 'https://limits.example' }, + connections: [], + }, + }, + }); + await publish(req, res); + const r = res._result(); + assert(r.statusCode === 400, 'expected 400 for an overlong field'); + assert( + r.body.details.some((detail) => detail.includes('name must be 200')), + 'expected the name length limit in validation details', + ); + } + + console.log('--- validation failure (unknown connection) -> 400 ---'); + { + const { req, res } = mockReqRes({ + method: 'POST', + cookie: `praxium_admin_session=${sessionCookie}`, + body: { + mode: 'create', + expectedVersion: currentVersion, + org: { + name: 'Unknown Link Org', location: 'Nowhere', coordinates: [0, 0], + category: 'Health', description: 'x', + contact: { website: 'https://unknown.example' }, connections: [999], + }, + }, + }); + await publish(req, res); + const r = res._result(); + assert(r.statusCode === 400, 'expected 400 for an unknown connection ID'); + assert( + r.body.details.some((detail) => detail.includes('999')), + 'expected the unknown connection ID in the validation details', + ); + } + console.log('--- update removes a connection -> reciprocal removal applies ---'); { const { req, res } = mockReqRes({ @@ -199,6 +330,11 @@ async function run() { await publish(req, res); const r = res._result(); assert(r.statusCode === 200, 'expected 200 for update'); + assert(r.body.org.country === 'Canada', 'older update payloads should preserve country'); + assert( + r.body.org.relationship === 'A shared learning and community initiative.', + 'older update payloads should preserve relationship context', + ); currentVersion = r.body.version; const orgs = JSON.parse(fakeGistFile); diff --git a/test/network-data.test.js b/test/network-data.test.js new file mode 100644 index 0000000..a7b026f --- /dev/null +++ b/test/network-data.test.js @@ -0,0 +1,130 @@ +const { + computeMetrics, + getConnectionPairs, + normalizeCategory, + normalizeDataset, + safeHttpUrl, +} = require('../network-data'); + +function assert(condition, message) { + if (!condition) throw new Error(`Assertion failed: ${message}`); +} + +function test(name, callback) { + try { + callback(); + console.log(`✓ ${name}`); + } catch (error) { + console.error(`✗ ${name}`); + throw error; + } +} + +const categories = ['Arts & Culture', 'Non-Profit', 'Other']; + +test('normalizes equivalent category punctuation and casing', () => { + assert(normalizeCategory('non profit', categories) === 'Non-Profit', 'category should be canonical'); +}); + +test('accepts an empty organization array', () => { + const result = normalizeDataset([], categories); + assert(result.valid, 'empty arrays are valid datasets'); + assert(result.organizations.length === 0, 'empty arrays stay empty'); +}); + +test('rejects a non-array top-level value', () => { + const result = normalizeDataset({ organizations: [] }, categories); + assert(!result.valid, 'object payload should be invalid'); + assert(result.organizations.length === 0, 'invalid payload should not yield organizations'); +}); + +test('skips unsafe or unusable records without breaking valid records', () => { + const result = normalizeDataset([ + { id: 1, name: 'Valid', coordinates: [49, -123], category: 'non profit' }, + { id: 2, name: '', coordinates: [49, -123] }, + { id: 3, name: 'Bad coordinates', coordinates: [900, -123] }, + ], categories); + + assert(result.organizations.length === 1, 'only the valid record should remain'); + assert(result.organizations[0].category === 'Non-Profit', 'valid record should be normalized'); + assert(result.warnings.length === 2, 'each skipped record should produce a warning'); +}); + +test('skips later records with duplicate organization IDs', () => { + const result = normalizeDataset([ + { id: 7, name: 'Original', coordinates: [1, 1] }, + { id: 7, name: 'Duplicate', coordinates: [2, 2] }, + ], categories); + + assert(result.organizations.length === 1, 'only the first unique ID should be retained'); + assert(result.organizations[0].name === 'Original', 'the first organization should win'); + assert(result.warnings.some((warning) => warning.includes('duplicated')), 'duplicate should be reported'); +}); + +test('assigns a deterministic ID when one is missing', () => { + const result = normalizeDataset([ + { id: 1, name: 'One', coordinates: [1, 1] }, + { name: 'Two', coordinates: [2, 2] }, + ], categories); + + assert(result.organizations[1].id === 2, 'missing ID should use the next available positive integer'); +}); + +test('preserves impact-story fields and strips unsafe website links', () => { + const result = normalizeDataset([ + { + id: 1, + name: 'Story Org', + location: 'Vancouver, Canada', + country: 'Canada', + coordinates: [49.28, -123.12], + category: 'Non-Profit', + description: 'A partner.', + relationship: 'We share a community learning project.', + contact: { website: 'javascript:alert(1)', email: 'hello@example.org' }, + }, + ], categories); + + const organization = result.organizations[0]; + assert(organization.country === 'Canada', 'country should be retained'); + assert(organization.relationship.includes('community learning'), 'relationship should be retained'); + assert(!organization.contact.website, 'unsafe website should be removed'); + assert(organization.contact.email === 'hello@example.org', 'safe contact fields should be retained'); +}); + +test('removes self-links, duplicate links, and links to unknown IDs', () => { + const result = normalizeDataset([ + { id: 1, name: 'One', coordinates: [1, 1], connections: [1, 2, 2, 99] }, + { id: 2, name: 'Two', coordinates: [2, 2], connections: [] }, + ], categories); + + assert( + JSON.stringify(result.organizations[0].connections) === JSON.stringify([2]), + 'only one valid external connection should remain', + ); + assert(result.warnings.some((warning) => warning.includes('99')), 'unknown ID should be reported'); +}); + +test('counts one-way and reciprocal relationships only once', () => { + const rows = [ + { id: 1, connections: [2, 3], category: 'A', location: 'X', country: 'Canada' }, + { id: 2, connections: [1], category: 'B', location: 'Y', country: 'Canada' }, + { id: 3, connections: [], category: 'A', location: 'X', country: 'Peru' }, + ]; + + assert(getConnectionPairs(rows).length === 2, 'two unique pairs should be returned'); + const metrics = computeMetrics(rows); + assert(metrics.organizations === 3, 'organization count should be accurate'); + assert(metrics.categories === 2, 'category count should be unique'); + assert(metrics.locations === 2, 'location count should be unique'); + assert(metrics.countries === 2, 'country count should be unique'); + assert(metrics.connections === 2, 'relationship count should be unique'); +}); + +test('allows only http and https organization links', () => { + assert(safeHttpUrl('https://example.org') === 'https://example.org/', 'https should be accepted'); + assert(safeHttpUrl('javascript:alert(1)') === '', 'script URLs should be rejected'); + assert(safeHttpUrl('not a url') === '', 'malformed URLs should be rejected'); +}); + +console.log('\nNETWORK DATA TESTS PASSED'); diff --git a/test/public-assets.test.js b/test/public-assets.test.js new file mode 100644 index 0000000..a008910 --- /dev/null +++ b/test/public-assets.test.js @@ -0,0 +1,77 @@ +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.join(__dirname, '..'); +const HTML_FILE = 'organization-network-map.html'; +const WORKFLOW_FILE = '.github/workflows/static.yml'; + +function assert(condition, message) { + if (!condition) throw new Error(`Assertion failed: ${message}`); +} + +function read(relativePath) { + return fs.readFileSync(path.join(ROOT, relativePath), 'utf8'); +} + +const html = read(HTML_FILE); +const workflow = read(WORKFLOW_FILE); + +const references = [ + ...html.matchAll(/<(?:script|link)\b[^>]*\b(?:src|href)="([^"]+)"/g), +].map((match) => match[1]); + +const publicAssets = [...new Set(references.filter((reference) => ( + !reference.startsWith('http://') + && !reference.startsWith('https://') + && !reference.startsWith('data:') + && !reference.startsWith('#') + && reference !== 'organization-network-map.html' +)))]; + +publicAssets.forEach((asset) => { + assert(fs.existsSync(path.join(ROOT, asset)), `${asset} must exist`); + assert( + workflow.includes(`cp ${asset} _site/`), + `${asset} must be copied into the GitHub Pages artifact`, + ); +}); + +assert( + workflow.includes('cp organization-network-map.html _site/index.html'), + 'the GitHub Pages root must serve the map', +); +assert( + html.includes('http-equiv="Content-Security-Policy"'), + 'the public page must define a Content Security Policy', +); +assert( + !/]*\bsrc=)[^>]*>[\s\S]*?<\/script>/i.test(html), + 'the public page must not contain inline executable scripts', +); +assert( + !/\binnerHTML\s*=/.test(read('map.js')), + 'the public renderer must not inject organization data with innerHTML', +); +assert( + html.indexOf('praxium-tokens-v2.css') < html.indexOf('map.css'), + 'shared site tokens must load before map-specific component styles', +); +assert( + /]*)?>/.test(html) && !/]*\bclass="[^"]*\bdark\b/.test(html), + 'the public map must default to the website-aligned light theme', +); +assert( + html.includes('family=Epilogue') && html.includes('family=Poppins'), + 'the public map must load the website typefaces', +); + +const externalLeafletAssets = references.filter((reference) => ( + reference.includes('cdnjs.cloudflare.com/ajax/libs/leaflet/') +)); +assert(externalLeafletAssets.length === 2, 'Leaflet CSS and JavaScript should both be pinned'); +assert( + (html.match(/\bintegrity="sha384-/g) || []).length >= 2, + 'external Leaflet assets must use Subresource Integrity', +); + +console.log('PUBLIC ASSET TESTS PASSED'); diff --git a/vercel.json b/vercel.json index 803ed3c..7e5c6ae 100644 --- a/vercel.json +++ b/vercel.json @@ -3,5 +3,36 @@ "redirects": [ { "source": "/", "destination": "/admin", "permanent": false } ], + "headers": [ + { + "source": "/admin/(.*)", + "headers": [ + { "key": "X-Content-Type-Options", "value": "nosniff" }, + { "key": "X-Frame-Options", "value": "DENY" }, + { "key": "Referrer-Policy", "value": "no-referrer" }, + { + "key": "Permissions-Policy", + "value": "camera=(), microphone=(), geolocation=()" + }, + { + "key": "Content-Security-Policy", + "value": "default-src 'self'; script-src 'self' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; img-src 'self' data: blob: https://server.arcgisonline.com https://cdnjs.cloudflare.com; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests" + } + ] + }, + { + "source": "/api/(.*)", + "headers": [ + { "key": "Cache-Control", "value": "no-store" }, + { "key": "X-Content-Type-Options", "value": "nosniff" }, + { "key": "X-Frame-Options", "value": "DENY" }, + { "key": "Referrer-Policy", "value": "no-referrer" }, + { + "key": "Content-Security-Policy", + "value": "default-src 'none'; frame-ancestors 'none'" + } + ] + } + ], "cleanUrls": true }