todos-contra-el-fuego-web/UPGRADE.md
vjrj 6e28bf3545 docs: record react-* modernization (helmet/i18n/bootstrap/router) + deferred leaflet & BS5
UPGRADE.md dependency-debt table and PENDIENTE.md updated to reflect the
5 shipped phases and the two deferred sub-projects (react-leaflet 1.8->4
and the Bootstrap 4->5 CSS/JS jump), with rationale for each deferral.
Final state: helmet/i18next/react-bootstrap/react-router warnings all
gone from the console; only react-leaflet (deferred) and our own
defaultProps/findDOMNode remain. REST smoke byte-identical.
2026-07-21 13:05:05 +02:00

372 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Meteor upgrade — 1.6.1.1 → 2.5 (Mongo 3.2) → 3.1 (Mongo 7, on `meteor3-wip`)
Incremental, verified upgrade of `todos-contra-el-fuego-web`. Branch:
`meteor3-upgrade` (base `tcef-master`). Local commits only — no push until
authorized.
## ⚠️ Hard blocker for the final 3.x jump: MongoDB 3.2
Production runs against the shared replica set **rsmain on MongoDB 3.2.11**.
Meteor 3 ships the modern `mongodb` Node driver, which requires a server
**≥ 4.2/4.4**. Meteor **2.x still works against Mongo 3.2**.
Therefore this branch lands on **Meteor 2.5** — empirically the highest release
whose bundled `mongodb` driver still connects to Mongo 3.2 (2.6 bumps the driver
to 4.x and fails; see "Escala ceiling" below) — with everything green. The
`meteor update --release 3.x` step (and even 2.6+) is **deliberately not taken**
until the Comunes infra team upgrades rsmain (shared infra — coordinated in a
separate phase, not unilaterally). Server code should still be migrated to
async/await before the final 3.x jump so it is mechanical (tracked as debt).
## ⚠️ Deploy-ordering dependency: notifications cutover
The old push/email notification code was removed from the web (see the
notifications commit) because it now lives in the **tcef-notifications**
microservice. **Production must not run this build until tcef-notifications is
emitting in prod** — otherwise users stop receiving notifications. The web
deploy and the notifications cutover must be coordinated.
## Safety net: REST smoke test
`smoke/` is a standalone Node harness that calls every REST endpoint the Flutter
app consumes, against a seeded local dev server, and diffs the responses against
committed snapshots (`smoke/snapshots/`). See `smoke/README.md`.
**Run after every escala — it must stay byte-identical:**
```bash
docker run -d --name tcef-mongo32 -p 27018:27017 mongo:3.2 # once
export PATH="$HOME/.meteor:$PATH" MONGO_URL="mongodb://localhost:27018/fuegos"
meteor --settings settings-development.json --port 3100 & # dev server
./smoke/smoke.sh # seed + compare
```
## Escalas
### Baseline — Meteor 1.6.1.1 (starting point)
- Boots against Mongo 3.2 with `settings-development.json`.
- Dev-enabling changes (do not affect production behavior):
- `IPGeocoder.js`: degrade gracefully when the MaxMind GeoLite2 DB is absent
(provisioned by cron in prod) instead of crashing at boot.
- `settings-development.json`: added dev-only `private.internalApiToken`
(`dev-smoke-token`) — the REST API only registers its routes when this is
set.
- **Notifications code removed** (migrated to tcef-notifications): deleted
`notificationsObserver.js`, `notificationsProcess.js`, the "Process pending
notif" SyncedCron job, and the `node-gcm` dependency. `subsUnion.js` kept (it
feeds `subs-public-union`, consumed by the app).
- REST smoke baseline captured here. ✅ green.
### Escala 1 — 1.6.1.1 → 1.8.3 ✅ green
```bash
meteor update --release 1.8.3
meteor npm install --save @babel/runtime@^7.26.0
```
Core packages bumped (highlights): `meteor` 1.8.6→1.9.3, `ecmascript`
0.10.7→0.13.2, `modules` 0.11.6→0.14.0, `mongo` 1.4.7→1.7.0, `npm-mongo`
2.2.34→**3.2.0** (mongodb driver 3.x — still fine against Mongo 3.2 server),
`webapp` 1.5.0→1.7.5, `standard-minifier-js` →2.5.2. `underscore` auto-added
(1.7 dropped it from meteor-base), `fetch` + `modern-browsers` added.
**Breaking change — Babel beta → stable.** Boot failed with
`Cannot find module '@babel/runtime/helpers/objectSpread2'`: the pinned
`@babel/runtime@7.0.0-beta.44` predates helpers that 1.8's `ecmascript` emits.
Fixed by upgrading to stable **7.x** (`@babel/runtime@^7.29.7`). Note: `@latest`
resolves to `8.x`, which is too new for Meteor's 7-style helper layout — pin to
`^7`.
REST smoke test: **byte-identical to baseline.**
### Escala 2 — 1.8.3 → 1.11 (version resolution only)
```bash
meteor update --release 1.11
```
Resolution reached 1.11 (`ecmascript` 0.14.3, `mongo` 1.10.0, `npm-mongo`
**3.8.0** — still Mongo-3.2-compatible, `accounts-*` 1.x, `email` 2.0). But this
release surfaced the two big blockers below. The app was ultimately driven to
**2.3** (see next section); 1.11 was a transit point, not a landing.
### Escala 3 — → Meteor 2.3 ✅ green (builds + runs against Mongo 3.2, smoke byte-identical)
`.meteor/release = METEOR@2.3`. `npm-mongo` **3.9.0** (mongodb driver 3.x) — the
last driver line that still speaks to a Mongo 3.2 server. Core bumps:
`accounts-*` 2.0, `ecmascript` 0.15.2, `mongo` 1.12.0, HMR added.
This escala was dominated by **dead Atmosphere packages** (the plan's #1 risk).
Root causes and fixes, in the order they were hit:
1. **`arkham:comments-ui` — no Meteor 2.x build.** The vendored local package
used `Npm.depends`, and rebuilding a *local* package's npm deps crashes
meteor-tool ≥1.11 on this host (`node_contextify.cc … Assertion
args[1]->IsString()` → SIGABRT; reproducible with any `Npm.depends`, even
`is-number`). Switching to the Atmosphere isopack avoided the local rebuild
but the package is abandoned: its newest 2.x-solvable version (0.2.15) pins
`accounts-password@1.0.1`, conflicting with Meteor 2.0's accounts-password 2.0.
**Decision (user-approved): reimplement comments as a small React feature**
(`imports/api/Comments/*`, `imports/ui/components/Comments/CommentsBox.js`),
dropping the package entirely. Fire-page comments only (the sole usage), with
likes/dislikes, image/YouTube embed, owner edit/remove, and the
"email other commenters" side-effect preserved. Comment text is now rendered
as **safe plain text** (+ media embed) instead of markdown-to-HTML — a small
scope reduction and a security improvement (no raw-HTML injection of user
content). The old local fork is preserved in its nested git repo
(commit `4761da9`).
2. **`coffeescript@1.0.17` build plugin — crashes the build.** The same
`node_contextify` assertion fired at *build start* on this host whenever the
coffeescript compiler plugin loaded. It was pulled transitively by the dead
**test stack** (`meteortesting:mocha`, `practicalmeteor:chai`,
`xolvio:cleaner`) and by the old auto-downgraded `nimble:restivus@0.6.6`
(which needs `iron:router`). Removing those removed coffeescript and
unblocked the build. A trivial `meteor create` app builds fine on the same
tool, confirming the crash is package-specific, not a broken tool.
3. **`nimble:restivus` — the REST API package — pins `accounts-password@1.3.3`**
(incompatible with 2.x) and is CoffeeScript (needs the crashing plugin).
**Vendored** as `packages/nimble-restivus`: the `.coffee` sources were
precompiled to plain JS (`lib/restivus-all.js`, single translation unit so
the `Auth`/`Route`/`Restivus` classes share scope; `Restivus` left
un-`var`'d so Meteor's `api.export` picks it up), and `package.js` loosens
`accounts-password` to 2.x and drops the coffeescript dependency. This also
let restivus resolve to 0.8.12 (json-routes based), which **removed the whole
`iron:router` stack**. REST behavior unchanged (smoke byte-identical).
4. **`maximum:server-transform` (unused) removal** broke
`Meteor.publishTransformed` in `FalsePositives/server/publications.js`; no
transform was actually configured, so replaced with plain `Meteor.publish`.
5. **`fourseven:scss` 4.5.4 → 4.14.1** — `node-sass@4.5.3` won't build on node 12
(Meteor ≥1.9); 4.14.1 uses a node-12-compatible node-sass with prebuilt
binaries. (Meteor 1.8 built it on node 8.) `4.15.0` needs ecmascript ≥ 0.15.1
→ too new for 1.11.
6. **`less`, `markdown` removed** — no `.less`/`.md` source files; their build
plugins were dead weight.
### ⚠️ Local build-host caveat
Meteor-tool's bundled node (12.x/14.x) SIGABRTs (`node_contextify` assertion) on
this machine (kernel 6.12) when certain old build plugins load — this is an
environment interaction, not a code defect (the deploy host built these fine
historically). It was fully worked around by removing the dead build plugins
above. If a future escala hits it again, build in a container matching the
deploy target (Debian) rather than on this host.
### Escalas 2.4 & 2.5 — ✅ green
`meteor update --release 2.4`, then `--release 2.5`. Both trivial; `npm-mongo`
stays at **3.9.1** (mongodb driver 3.x). REST smoke byte-identical against
`mongo:3.2` on both.
## Escala ceiling vs Mongo 3.2 — **landing on Meteor 2.5** (empirically verified)
Meteor bumps the bundled `mongodb` Node driver to **4.3.1 at Meteor 2.6**, which
requires server wire version ≥ 6 (**MongoDB ≥ 3.6**). Production's rsmain is
**Mongo 3.2** (wire version 4). Verified directly by booting 2.6 against a real
`mongo:3.2`:
```
MongoCompatibilityError: Server at localhost:27018 reports maximum wire
version 4, but this version of the Node.js Driver requires at least 6
(MongoDB 3.6)
=> Exited with code: 1
```
So **Meteor 2.5 is the highest release that runs against the production Mongo
3.2** (npm-mongo 3.9.1). That is where this branch lands: everything green, REST
smoke byte-identical. Going past 2.5 (to 2.16 or 3.x) is gated on upgrading the
shared rsmain replica set to Mongo ≥ 4.4 first — the same DB blocker as 3.x.
## ✅ Meteor 3.1 + MongoDB 7 — REST smoke GREEN (branch `meteor3-wip`)
**The 3.x jump works end-to-end for the REST/Flutter contract.** On branch
`meteor3-wip`, the web runs on **Meteor 3.1** (Node 22, async/await, no Fibers)
against a **dockerized MongoDB 7** (npm-mongo 6.10 / mongodb driver 6), and the
REST smoke test is **byte-identical to the 1.6.1.1 baseline** (all 12 endpoints).
This is the "mongo superior" decision realized: give fuegos its own Mongo 7
instead of the shared rsmain 3.2 — which is what unblocks Meteor 3's driver.
`meteor3-upgrade` stays at the deployable **Meteor 2.5** (Mongo 3.2). The 3.x
work lives on `meteor3-wip` until (a) production Mongo is migrated to 7 and
(b) the web-UI async migration below is finished.
### Reproduce the 3.1 dev stack
```bash
docker run -d --name tcef-mongo7 -p 27019:27019 mongo:7 --replSet rs0 --port 27019 --bind_ip_all
docker exec tcef-mongo7 mongosh --port 27019 --quiet --eval 'rs.initiate({_id:"rs0",members:[{_id:0,host:"localhost:27019"}]})'
# (migrations run from scratch on an empty DB — all up() bodies are async now,
# no need to pre-mark db.migrations at version 18)
export MONGO_URL="mongodb://localhost:27019/fuegos?replicaSet=rs0"
export NODE_OPTIONS="--dns-result-order=ipv4first --no-network-family-autoselection" # reach warehouse.meteor.com on Node 22
meteor --settings settings-development.json --port 3100
MONGO_CONTAINER=tcef-mongo7 MONGO_SHELL=mongosh MONGO_PORT=27019 ./smoke/smoke.sh
```
### Key build/runtime unblocks (Meteor 3.1)
- **underscore linker crash** (`Runtime is not available … underscore`): resolved
`underscore@1.6.2` had a runtime-less `web.browser` unibuild → pin
**`underscore@1.6.4`** (diagnosed by instrumenting the tool's `linker.js`).
- **collection2 v4** is fully lazy with no main module → import its eager entry
`meteor/aldeed:collection2/static.js` from `server|client/00-collection2-init.js`
so `attachSchema`/autoValues are patched before any collection loads.
- **Fibers gone**: removed `fibers.js`; server sync Mongo → `*Async` throughout
(Rest.js + helpers + methods + startup: oauth/subsUnion/migrations/email/facts).
- **vendored restivus** patched to `await` async endpoint handlers.
- **`$near` + `countAsync()`**: driver 6 runs `count` via aggregation where
`$near` is illegal → derive total from `fetchAsync().length`.
- **json-routes 3.0** matches routes in registration order → reordered the
`mobile/subscriptions/all/...` route before `.../:subsId` in Rest.js.
- **fixtures.js / sitemaps.js** disabled (sync-only `@cleverbeagle/seeder` /
pre-0.9 `gadicohen:sitemaps`) — not on the REST path. Debt.
### Web-UI async migration (beyond the REST contract)
-**Fires publications → async** (`fireFromId`, `fireFromAlertId`,
`fireFromActiveId`, `fireFromHash`): `findOne``findOneAsync`,
`count()``countAsync()`, `firesUnion` awaited, handlers `async` so the
try/catch actually catches rejections. Verified over raw DDP (all subs
`ready`, docs flow; fire + falsePositives docs delivered for the seeded
fire). `falsePositivesMyloc` / `industriesMyloc` / `comments.forReference`
needed **no change** — they return bare `find()` cursors (still sync-safe on
Meteor 3). `subscriptions.*` methods were already `*Async`.
- Note: the module-level `new Counter(...)` (natestrauser:publish-performant-counts)
behind `falsePositivesTotal` boots fine; the publication itself is unused by
the current UI — watch it if re-enabled.
-**Comments methods → async** (`comments.insert/edit/remove/like/dislike`):
`findOne/insert/update/remove``*Async`, helpers (`requireOwner`, `toggle`)
async, `users.findOneAsync` for the username. `onCommentAdd` mail fan-out:
`users.find().forEach``forEachAsync`. Still fire-and-forget from the
insert. Comments UI staging verification pending (pre-existing note below).
-**fixtures.js re-enabled**`@cleverbeagle/seeder` (sync Mongo, no Meteor
3 support) **removed from package.json** and replaced with a small in-repo
async seeder: idempotent, dev-only (staging opts in with `TCEF_SEED=1`);
same accounts as before (`admin@admin.com` / 5 test users / 5 Documents
each). Verified: `fixtures: 6 dev users ensured` on boot.
-**sitemaps.js re-enabled**`gadicohen:sitemaps` (pre-0.9 API, dead on
Meteor 3) **removed from .meteor/packages**; `/sitemap.xml` is now a small
`WebApp.connectHandlers` handler serving the same static page list (the
per-fire section of the old handler was behind `firesMapEnabled = false`,
i.e. dead code, and was dropped). Verified serving on dev.
-**Dead client packages purged — the web UI now RENDERS on Meteor 3.**
First-ever browser verification of this branch (the smoke only covers REST)
found a chain of ancient Blaze-era packages whose client code threw at
bundle load, killing the whole app (`meteorInstall is not defined`):
- **alanning:roles 1.2.10** (client crash, and its server
`Roles.userIsInRole` is sync) → package removed; the app barely used it:
`facts.js` now checks the plain `user.roles` field (admin set cached at
startup) and App.js takes `roles` from the user doc (prop was unused).
Upgrading to roles v4 was rejected: needs a prod data migration.
- **selaias:cookie-consent** (its `Cookies` global is gone) → removed;
replaced by in-repo React `imports/ui/components/CookieConsent` reusing
the same i18n keys; `CookieConsent.init` dropped from `i18n.js`.
- **natestrauser:publish-performant-counts** server `Counter._publishCursor`
uses sync `cursor.count()`**vendored** as
`packages/publish-performant-counts` (`tcef:publish-performant-counts`)
with `countAsync` and an async-aware interval; same public API.
- `Meteor.autorun` (removed in Meteor 3) → dropped (App.js debug) /
`Tracker.autorun` (FiresMap).
- Map/server publications not exercised by the REST smoke converted to
async: `activefiresmyloc`, `activefiresunionmyloc`, `fireAlerts`
(countAsync/fetchAsync/awaited firesUnion), and `oauth.verifyConfiguration`
(findOneAsync). `migrations.js`: all 18 historical `up()` bodies ported to
async (`fetchAsync`+`for..of`, `*Async` writes, `createIndexAsync`,
`Accounts.createUserAsync`; percolate:migrations 2.0.1 awaits async `up()`).
Verified: empty Mongo 7 DB migrates 0→18 with no errors (indexes +
industry registries created); pre-marking `version=18` is no longer needed.
Verified in a real browser: `/` and `/fires` render (map, search, layers,
cookie banner), zero uncaught exceptions; REST smoke byte-identical.
-**React 16 → 18.3**`react`/`react-dom` bumped to `^18.3.1`
(`--legacy-peer-deps`: the ancient react-* libs below declare 15/16 peers),
client entry migrated to `createRoot` (`imports/startup/client/index.js`).
The legacy UI libs (react-bootstrap 0.31, reactstrap 5-alpha, react-leaflet
1.8, react-router-dom 4, react-i18next 7) stay pinned and WORK on 18, but
spam the console with legacy-context/defaultProps deprecation warnings and
will die on React 19. **Debt**: own front-end refresh project.
⚠️ npm gotcha: running `meteor npm install/uninstall` while the dev server
watches node_modules can crash the meteor-tool watcher mid-prune (ENOENT on
a watched file). Stop the server (or restart it after) when touching deps.
### Still TODO before the web (not just REST) is fully deployable on 3.x
-**React 16 → 18** render root + ancient react-* libs modernized: react-helmet→
react-helmet-async, i18next 10→23 + react-i18next 7→14, react-bootstrap 0.31→2,
react-router-dom 4→6. Dead deps (reactstrap, react-leaflet-sidebarv2,
react-router-hash-link, react-addons-pure-render-mixin) dropped. All
browser-verified, REST smoke byte-identical. **Remaining front debt:**
react-leaflet 1.8→4 (deferred — core map, 4 v1-only plugins) and the
Bootstrap 4→5 CSS/JS jump (deferred — jQuery carousel/navbar). Both are what's
left before a React 19 jump.
- ✅ Re-enabled dev fixtures / sitemaps / email default template with async ports.
- ✅ SCSS `@import``@use`, `lighten`/`darken``color.adjust` (dart-sass deprecations).
- **Production cutover** still needs: Mongo data migrated 3.2→7 (mongodump/restore),
the notifications service emitting in prod, and the Docker deployment (fase 3).
## Dependency debt (tracked, to resolve in the noted escala)
| Dep | From | Target | Status |
|---|---|---|---|
| React / react-dom | 16.0 | 18 | ✅ done — 18.3.1 + createRoot. The peripheral react-* libs that only worked on 18 via legacy context are now all modernized (see rows below) **except react-leaflet** (deferred). What still blocks React 19: react-leaflet 1.8, plus our own `defaultProps` on function components and one `findDOMNode` (gadicc:blaze-react-component in Reconnect) — cosmetic, tracked. |
| Server async/await | Fibers | `*Async` APIs | ✅ done — REST, publications, methods, startup and migrations all on `*Async`. |
| bcrypt | 1.0.3 (broken binding) | 5.x | ✅ done — 1.0.3 didn't load on Node 22 (accounts-password silently fell back to pure JS) and wouldn't compile in the Docker build; 5.1.1 ships Node-22 prebuilds. |
| raven / flowkey:raven | 2.4 | @sentry/node + @sentry/browser 8.x | ✅ done — `flowkey:raven` (Sentry legacy SDK, dead on 3.x, spammed the boot log with 502s against the dead sentry.comunes.org) replaced by the modern SDKs behind the same `ravenLogger.log()` facade (6 call sites unchanged). Backend: **GlitchTip** self-hosted on `aaron` (Sentry-compatible DSN/API; Sentry proper needs 16 GB RAM, didn't fit), deployed via the Comunes ansible (`glitchtip.yml`, org "comunes" / project "tcef-web"), fronted at `https://sentry.comunes.org` (nginx on `assange``aaron:8000`). DSN set in `settings-development.json`; end-to-end verified (a test exception sent from the dev server showed up in GlitchTip within seconds). |
| nodemailer | 4 | 6.10 | ✅ done — drop-in for our usage: `email.js` only calls `nodemailer.createTransport(MAIL_URL)` and reads `transport.options.auth.user` (in the production-only `from()` branch); both verified unchanged in v6. Sending goes through `ostrio:mailer` (MailTime 2.5), which is v6-compatible. |
| Babel | 7 beta | 7 stable | ✅ done (escala 1) |
| react-meteor-data | 0.2.16 | 3.0.6 | ✅ done — 0.2.16 (React-15/16-era) looped `withTracker` on fire-detail pages under React 18 (never-ready → blank). `meteor add react-meteor-data@3.0.6`; no call-site changes (HOC API kept). Also dropped `tmeasday:check-npm-versions` (the constraint pinning 0.2.16). See section above. |
| i18next | 10.5 | 23.16 | ✅ done — with react-i18next 14. `whitelist``supportedLngs`, added `compatibilityJSON: 'v3'` (our locale JSON uses natural-language keys + v3 `_plural` layout, not the v4 `_one`/`_other` suffixes), custom separators `ß`/`ð`/`đ` kept. `sendMissing``saveMissing`, missingKeyHandler signature is `(lngs, ns, key, fallbackValue)` now (array first). See react-i18next row. |
| react-i18next | 7.4 | 14.1 | ✅ done — 48 `translate([], {wait:true})`/`translate()` HOCs → `withTranslation()`; `react.wait:true``react.useSuspense:false`. `<Trans>` unchanged (locale JSON already stores the indexed-tag format `<1><0>{{x}}</0></1>`). ReSendEmail: removed `<Interpolate>` (deleted in v10) → `<Trans i18nKey values={{email}}/>`, dropped the removed bare `t` export. This is what silenced the per-component `Translate`/`I18n` legacy-context console spam. Backends: xhr→`i18next-http-backend` (client), sync-fs→`i18next-fs-backend` (server); `i18next-localstorage-cache` dropped (was `enabled:false`); languagedetector 2→8. Browser-verified: es/en switch, `<Trans>` interpolation (`{{countTotal}}`+`<strong>`) renders on /fires. |
| react-leaflet + leaflet | 1.8 / 1.3.1 | 4.x / 1.9 | ⏸️ **deferred as debt** (the one lib not modernized this pass). react-leaflet v4 is a ground-up hooks rewrite of the app's core feature (the fire map), and the migration is deeply entangled: (a) **4 unmaintained v1-only plugins with no v4 equivalent**`react-leaflet-control` (custom map buttons, 3 maps), `react-leaflet-fullscreen`, `react-leaflet-google` (`GoogleLayer` base layers in DefMapLayers), `leaflet-sleep` (`sleep`/`wakeTime`… props on `<Map>`) — each needs a bespoke `createControlComponent`/`createLayerComponent`/googlemutant reimplementation; (b) the **controlled-viewport** pattern (`onViewportChanged` + `state.center/zoom` in FiresMap/SelectionMap) is gone in v4 (`MapContainer` center/zoom are initial-only; you drive it via a `useMap()` child); (c) `.leafletElement` refs in 6 files/13 sites (FiresMap, SelectionMap, SubscriptionsMap, Fires, SubsUnion) — v4 hands you the `L.Map`/layer directly with different timing; (d) `Leaflet.control.graphicScale().addTo(map)` + `subsUnion` reach into the raw map via the ref. **v1.8 works on React 18** (verified: all maps render), so this only blocks the eventual React 19 jump. Do it as its own focused project with heavy in-browser verification (drag markers, viewport-driven fire reload, fitBounds, layer switch, fullscreen, sleep, custom controls). Stays pinned at 1.8 until then. |
| arkham:comments-ui | 1.4.x | — | ✅ replaced with in-repo React feature |
| nimble:restivus | Atmosphere | vendored | ✅ local precompiled package, accounts-password 2.x |
| maximum:server-transform, meteorhacks:zones, less, markdown, test stack | — | — | ✅ removed (dead/unused) |
| fourseven:scss | 4.5.4 | 4.14.1 | ✅ node-12 compatible |
| node-gcm | 1.0.2 | — | ✅ removed (dead API, moved to microservice) |
| react-router-dom | 4.2.2 | 6.30 | ✅ done — with `history` 4→5 and `react-router-bootstrap` 0.24→0.26. Kills the `Router`/`Switch`/`Route`/`Link`/`LinkContainer` legacy-context warnings. Strategy: keep the shared `history` **singleton** (used outside React by NotificationsObserver and Utils/location) via `unstable_HistoryRouter`, and DON'T rewrite the ~20 class pages — a new `withRouterCompat` HOC bridges v6 hooks back to v4-shaped `history`/`match`/`location` props. `<Switch>``<Routes>`, `component=``element=`, `Authenticated`/`Public` become guard components rendering `children` or `<Navigate replace>`, `LocationListener` is now a `useLocation`+`useEffect` function. The regex route `/fire/:type(active\|archive\|alert)/:id``/fire/:type/:id` (type validated in the component; the only behavior delta is an invalid two-segment type now hits Fires instead of 404 — negligible). `history.listen` callback is `({location})` in v5. Browser-verified: SPA nav, `/subscriptions``/login` auth redirect, deep-link `/fire/archive/:id` (params flow), browser back/forward; REST smoke byte-identical. Gotcha hit & fixed: the rewritten guard files need `import React` for their `<Navigate>` JSX. |
| react-helmet | 5.2.0 | react-helmet-async 2.0.5 | ✅ done — react-helmet is unmaintained and misbehaves under React 18 StrictMode. Drop-in: same `<Helmet>` children API across the 15 pages; `<HelmetProvider>` wraps `<App/>` in `imports/startup/client/index.js`. Verified in browser: per-page titles, meta description and hreflang alternates injected (`data-rh`). Note: head updates are rAF-deferred (like react-helmet), so hidden/background tabs don't flush — irrelevant for real users. |
| react-bootstrap | 0.31.5 | 2.10 | ✅ done — kills the biggest batch of React-19-blocking warnings (Grid/FormGroup/ControlLabel/Navbar.Header/Navbar.Brand/Checkbox/SafeAnchor all used legacy context/defaultProps). 29 files: `Grid``Container`, `FormGroup/ControlLabel/FormControl/HelpBlock``Form.Group/Label/Control/Text`, `Checkbox``Form.Check` (label moves to a prop), `bsStyle``variant` (×23, `"default"``"secondary"`), `bsSize``size` (×2), `pull-right``float-end` (×7). Custom `Col.js` (used v0.31 `bootstrapUtils`/`StyleConfig` internals) now re-exports v2's `Col`; custom `NavItem.js` rewritten self-contained (dropped `SafeAnchor`+`createChainedFunction`); `Navigation.js` drops the react-bootstrap `Navbar` (was hand-rolled raw markup anyway). **CSS kept at Bootstrap 4** (see next row) — the only BS5-only class v2 emits that BS4 lacks is `.form-label`, shimmed in `forms.scss`. Browser-verified: home carousel+navbar, signup form + terms `Form.Check` (toggles submit), login form; REST smoke byte-identical. |
| Bootstrap CSS/JS | 4.1 (alexwine:bootstrap-4) | 5.x | ⏸️ **deferred as debt.** The app's CSS+JS still comes from the Atmosphere `alexwine:bootstrap-4` (Bootstrap 4 + jQuery). The BS4→5 jump is a separate invasive sub-project: the home **carousel** uses the jQuery `bootstrap-carousel-swipe` plugin (BS4-only), the **navbar collapse** and ~17 `data-toggle`/`data-slide` attributes across 6 files are BS4 jQuery behaviors, and BS5 is vanilla-JS with `data-bs-*`. react-bootstrap v2 runs fine on BS4 CSS for every component we use (Button/Alert/Row/Col/Table/ButtonGroup/Modal render identical classes; only `.form-label` needed a shim). Do this jump on its own: npm `bootstrap@5` SCSS+JS, rewrite carousel (BS5 has native swipe → drop the plugin), `data-toggle``data-bs-toggle`, `ml-auto``ms-auto`, etc. |
| reactstrap | 5.0.0-alpha.3 | — | ✅ removed — zero imports in the codebase (react-bootstrap covers all UI). |
| react-leaflet-sidebarv2 | 0.5.1 | — | ✅ removed — never imported. |
| react-router-hash-link | 1.1.1 | — | ✅ removed — only a commented-out import in Index.js. |
| react-addons-pure-render-mixin | 15.6.2 | — | ✅ removed — never imported (React-15 era leftover). |
| chimp | 0.51.1 | — | ✅ removed — dead e2e test runner (Selenium 2 / cucumber), sole remaining puller of native `fibers@1.x`, which cannot compile on Node 22 and broke the clean Docker build. Not imported anywhere. |
### Comments React feature — verification note
The server builds and the REST smoke test is green, and the client bundle
compiles, but the comments UI itself is **not exercised by the smoke test**
(it's not part of the Flutter REST contract). It needs manual staging
verification: on a fire archive page — logged-in post/edit/remove, like/dislike,
and image/YouTube embed.
### ✅ Fixed: fire-detail pages now render (react-meteor-data 0.2.16 → 3.0.6)
Browser-verifying the Comments feature surfaced a **pre-existing client bug**:
the fire-detail pages (`/fire/{active,archive,alert}/:id`, component
`imports/ui/pages/Fires/Fires.js`) stayed blank — the page's `withTracker`
subscription (`fireFromActiveId` / `fireFromId`) never reached `ready()`, so
`loading` stayed true and nothing rendered, pegging a CPU core in a
re-subscribe loop.
Diagnosis (isolated, not guessed):
- **The publications were fine.** Subscribing to `fireFromActiveId` /
`fireFromId` *standalone* from the browser console readied immediately and
delivered the doc — not a server/publication bug.
- **The bug was `react-meteor-data@0.2.16`** — a React-15/16-era version
(`componentWillMount` + legacy context, the source of the deprecation
warnings flooding the console) that misbehaves under React 18 for a component
gating its whole render on `subscription.ready()`.
Fix: **`meteor add react-meteor-data@3.0.6`** (the React-18-compatible line for
Meteor 3.1; 4.x targets Meteor 3.2+). No call-site changes were needed — 3.x
keeps the `withTracker` HOC API, and all 19 call sites work unchanged. The old
constraint that pinned 0.2.16 was **`tmeasday:check-npm-versions`**, pulled in
by 0.2.16 itself; the upgrade dropped it, which also silenced the
"npm peer requirements not installed (react@15-16)" boot/console warning.
Verified in a real browser at `/fire/archive/c0000000000000000000000c`: the
page renders fully (title, Leaflet + NASA/satellite map, the "not a wildfire"
FalsePositives widget, and the **Comments** box — all `withTracker`-driven),
zero console errors. REST smoke byte-identical (12/12).