Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,14 +234,39 @@ npm run deploy # publish dist/ with gh-pages

CI runs on the current Node LTS release. Verify its reported Node version when
reproducing a failure; a passing run on a different runtime is not a CI result.
Local development requires Node 22.13+ on the 22.x line, or Node 24+, matching
the accessibility tooling's supported runtimes.
Run `npm ci`, `npm run lint -- --max-warnings=0`, `npm run typecheck`,
`npm run test:ci`, and `npm run build`. The coverage command enforces the existing
80% line and statement thresholds; a plain `vitest run` does not.

Vitest workers disable Node's native Web Storage with
`--no-experimental-webstorage` so happy-dom and jsdom provide isolated browser
storage. This avoids the Node 25+ / Vitest 3 global collision without a shared
storage file or changing application behavior.
storage. Vitest 4 configures this through `test.execArgv`, without a shared storage
file or changing application behavior.

### Dependency security updates

Use `npm ci` to reproduce the committed dependency tree and `npm audit` to check
current advisories. Keep Vitest, its UI, and its coverage provider on matching
versions. Security overrides in `package.json` keep transitive dependencies on
patched releases where their consumers still request vulnerable versions:

- `fflate` 0.7 and PostCSS selector parser 6 stay on their existing API lines.
- Modern `glob` and `minimatch` consumers use patched releases; legacy consumers
retain their existing major versions and calling conventions.
- Luma's glTF loader uses the matching Loaders.gl 4.5 packages, which remove the
required `texture-compressor` / vulnerable `image-size` dependency chain.
- Pa11y CI 5 uses newer Puppeteer tooling without the unpatched `extract-zip`
dependency.

As of November 11, 2026, all 180 open repository Dependabot alerts were checked
against the updated lockfile's package versions and vulnerable version ranges.
The separate low-severity
[deepmerge advisory (GHSA-gcqx-w48m-w2h4)](https://github.com/advisories/GHSA-gcqx-w48m-w2h4)
still has no published fix and is reported by `npm audit` through Mirador and
build tooling. It is not suppressed or worked around by downgrading to an older
release. Revisit it when upstream publishes a fix or a compatible replacement.

## Creating A New Institution Theme

Expand Down
9,198 changes: 3,977 additions & 5,221 deletions package-lock.json

Large diffs are not rendered by default.

65 changes: 39 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,32 @@
"generate-pwa-icons": "pwa-assets-generator"
},
"dependencies": {
"@appsignal/nodejs": "^3.0.0",
"@appsignal/nodejs": "^3.9.5",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@geoblacklight/frontend": "5.1.0",
"@hotwired/stimulus": "^3.2.2",
"@mui/material": "^7.3.11",
"@mui/system": "^7.3.11",
"@react-router/node": "^7.11.0",
"@react-router/serve": "^7.11.0",
"@react-router/node": "^7.18.5",
"@react-router/serve": "^7.18.5",
"@supabase/supabase-js": "^2.91.0",
"@tailwindcss/typography": "^0.5.10",
"@terraformer/wkt": "^2.2.2",
"@types/leaflet": "^1.9.20",
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"express": "^4.22.1",
"h3-js": "^4.1.0",
"isbot": "^5.1.11",
"js-cookie": "^3.0.5",
"js-yaml": "^4.1.1",
"js-cookie": "^3.0.8",
"js-yaml": "^4.3.2",
"leaflet": "^1.9.4",
"maplibre-gl": "^6.11.2",
"leaflet-fullscreen": "^1.0.2",
"leaflet-gesture-handling": "^1.2.2",
"leaflet-iiif": "^3.0.0",
"lucide-react": "^0.544.0",
"maplibre-gl": "^6.11.2",
"mirador": "4.0.0",
"mirador-dl-plugin": "1.0.0",
"ogm-viewer": "^1.5.0",
Expand All @@ -65,37 +66,46 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet-async": "^2.0.5",
"react-router": "^7.11.0",
"react-router": "^7.18.5",
"recharts": "^3.6.0",
"source-map-support": "^0.5.21",
"wkt": "^0.1.1",
"@terraformer/wkt": "^2.2.2"
"wkt": "^0.1.1"
},
"overrides": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router": "^7.11.0",
"react-router-dom": "npm:react-router@^7.11.0",
"vite": "^7.3.2",
"happy-dom": "^20.8.9",
"react-router": "$react-router",
"react-router-dom": "npm:react-router@^7.18.5",
"vite": "$vite",
"happy-dom": "$happy-dom",
"picomatch": "^4.0.4",
"lodash": "^4.18.0",
"lodash-es": "^4.18.0",
"path-to-regexp": "^0.1.13",
"serialize-javascript": "^7.0.5",
"undici": "^6.24.0",
"serialize-javascript": "^7.1.3",
"undici": "^6.29.1",
"flatted": "^3.4.2",
"defu": "^6.1.5",
"brace-expansion": "^5.0.5",
"brace-expansion": "^5.0.12",
"minimatch@3.1.5": {
"brace-expansion": "^1.1.12"
"brace-expansion": "^1.1.21"
},
"tar": "^7.5.11"
"tar": "^7.5.21",
"fflate@~0.7.0": "^0.7.5",
"postcss-selector-parser@^6.0.0": "^6.1.6",
"qs": "^6.17.1",
"glob@>=9": "^14.0.5",
"minimatch@>=7": "^11.0.8",
"@luma.gl/gltf": {
"@loaders.gl/core": "~4.5.4",
"@loaders.gl/gltf": "~4.5.4",
"@loaders.gl/textures": "~4.5.4"
}
},
"devDependencies": {
"@axe-core/react": "^4.11.1",
"@eslint/js": "^9.9.1",
"@react-router/dev": "^7.11.0",
"@react-router/dev": "^7.18.5",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
Expand All @@ -111,8 +121,8 @@
"@typescript-eslint/parser": "^8.24.1",
"@vite-pwa/assets-generator": "^1.0.2",
"@vitejs/plugin-react": "^5.0.0",
"@vitest/coverage-v8": "^3.2.4",
"@vitest/ui": "^3.2.4",
"@vitest/coverage-v8": "^4.1.12",
"@vitest/ui": "^4.1.12",
"autoprefixer": "^10.4.18",
"axe-core": "^4.11.1",
"concurrently": "^8.0.1",
Expand All @@ -128,20 +138,23 @@
"eslint-plugin-unused-imports": "^4.1.4",
"gh-pages": "^6.3.0",
"globals": "^15.9.0",
"happy-dom": "^20.8.9",
"happy-dom": "^21.0.4",
"jsdom": "^27.0.0",
"pa11y-ci": "^4.0.1",
"pa11y-ci": "^5.0.0",
"pa11y-reporter-html": "^2.0.0",
"postcss": "^8.4.35",
"postcss": "^8.5.32",
"prettier": "^3.5.1",
"start-server-and-test": "^2.1.5",
"tailwindcss": "^3.4.1",
"ts-node": "^10.9.1",
"typescript": "^5.5.3",
"typescript-eslint": "^8.3.0",
"vite": "^7.3.2",
"vite": "^7.3.8",
"vite-plugin-pwa": "^1.2.0",
"vitest": "^3.2.4",
"vitest": "^4.1.12",
"vitest-axe": "^0.1.0"
},
"engines": {
"node": "^22.13.0 || >=24"
}
}
1 change: 0 additions & 1 deletion src/__tests__/components/ResourceViewComponent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ const mockLocation: import('react-router').Location<{
searchUrl: string;
currentPage: number;
} | null> = {
unstable_mask: undefined,
state: null,
pathname: '/resources/mit-001145244',
search: '',
Expand Down
17 changes: 12 additions & 5 deletions src/__tests__/components/search/GalleryView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@ vi.mock('../../../context/BookmarkContext', () => ({
}));

beforeAll(() => {
global.IntersectionObserver = vi.fn().mockImplementation(() => ({
observe: observeMock,
unobserve: unobserveMock,
disconnect: vi.fn(),
}));
global.IntersectionObserver = vi.fn().mockImplementation(function () {
return {
observe: observeMock,
unobserve: unobserveMock,
disconnect: vi.fn(),
};
});
});

afterEach(() => {
vi.restoreAllMocks();
vi.clearAllMocks();
});

Expand Down Expand Up @@ -60,6 +63,10 @@ const withResourceClassIcon = (result: GeoDocument): GeoDocument => ({
describe('GalleryView', () => {
beforeEach(() => {
vi.useRealTimers();
// Model pending image requests explicitly; Happy DOM does not load assets.
vi.spyOn(HTMLImageElement.prototype, 'complete', 'get').mockReturnValue(
false
);
});

const renderGallery = (
Expand Down
Loading
Loading