# Meteor upgrade — 1.6.1.1 → 2.x (→ 3.x blocked) 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. ## 🚧 3.x — blocked (do not run `meteor update --release 3.x`) Two independent blockers, both external/coordination: 1. **MongoDB 3.2 → ≥ 4.4.** Meteor 3's driver requires it. Shared Comunes infra (rsmain) — coordinate the DB upgrade in a separate phase. This *also* gates Meteor 2.5+. 2. **Notifications cutover.** The old notif code is already removed from this branch; production must not run this build until `tcef-notifications` is emitting in prod. When Mongo is ≥ 4.4 and the notifications service is live in prod, the remaining 3.x work is: `meteor update --release 3.x`, finish the async/await migration of server code (collections/methods/publications `*Async`, no Fibers), React 18 render root, and re-run the smoke test. ## 3.x groundwork — done on branch `meteor3-wip` (in progress, not deployable) To de-risk the eventual 3.x jump, the migration was driven as far as it goes against a **modern dockerized MongoDB** (the "mongo superior" decision: give fuegos its own Mongo 7 instead of the shared rsmain 3.2, which is what actually unblocks Meteor 3's driver). This lives on `meteor3-wip`; `meteor3-upgrade` stays at the deployable Meteor 2.5. **Modern Mongo (local, reproducible):** ```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"}]})' export MONGO_URL="mongodb://localhost:27019/fuegos?replicaSet=rs0" # meteor-tool node 22 needs this to reach warehouse.meteor.com (else Happy-Eyeballs # picks an unreachable IPv6): export NODE_OPTIONS="--dns-result-order=ipv4first --no-network-family-autoselection" ``` **Reached `METEOR@3.1` with all package-version conflicts resolved.** Dead packages swapped for Meteor-3 equivalents: | Old (mongo@1.x / dead) | New | |---|---| | `mongo-livedata` | removed (deprecated alias) | | `facts` | `facts-base` | | `saucecode:timezoned-synced-cron` | `quave:synced-cron` | | `aldeed:collection2-core` | `aldeed:collection2@4.0.4` | | `mys:fonts` (pinned caching-compiler@1.0.0) | removed | | `fourseven:scss@4.14.1` (node-sass, fails on node 22) | `@5.0.0` (Dart Sass) + `sass` npm | | `nimble:restivus` (vendored) | accounts-password loosened to `2.0.0 \|\| 3.0.0` | SCSS then compiles (Dart Sass; only `@import`/`lighten`/`darken` deprecation warnings — cosmetic debt: migrate to `@use` + `color.adjust`). **Blocked at the client-bundle linker:** `Runtime is not available, but it uses features needing the runtime: underscore` — one of the old client atmosphere packages (bert / blaze-react / cookie-consent / meteor-status / bootstrap-4 / etc.) needs replacing for Meteor 3's linker. Beyond that, the **server async/await migration is the main remaining effort** (unavoidable for 3.x): - `Rest.js` + its helpers (`countRealFires`, `firesUnion`, `whichAreFalsePositives`, `fireFromHash`, `subscriptionsInsert`, `upsertFalsePositive`) use sync Mongo (`findOne`, `find().fetch()/.count()`, `insert/update/remove/upsert`) → must become `*Async`. - The vendored **restivus must be patched to `await`** async endpoint handlers (its `_callEndpoint` uses the return value synchronously). - `methods.js`/`publications.js` across the app, `subsUnion.js`, `migrations.js`, the Comments feature, and startup files → async. - `cron.js` `SyncedCron` import (quave export) and `facts.js` `Facts` import (facts-base export). - React 16 → 18 render root (`createRoot`) in `startup/client/index.js`. This is a multi-hour migration; the smoke test (now `mongosh`-capable, see `smoke/smoke.sh` on that branch) is the gate. Resume on `meteor3-wip`. ## Dependency debt (tracked, to resolve in the noted escala) | Dep | From | Target | Status | |---|---|---|---| | React / react-dom | 16.0 | 18 | **debt** — deferred; ancient react-* libs (react-bootstrap 0.31, reactstrap 5-alpha, react-leaflet 1.8) break on 18. React 16 works through Meteor 2.x/3.x. Own front-end project. | | Server async/await | Fibers | `*Async` APIs | **pending** — prep for 3.x; 2.x still runs on Fibers so not yet required. | | raven / flowkey:raven | 2.4 | @sentry/node + @sentry/react | debt — DSN off until GlitchTip/Sentry back (sentry.comunes.org dead → harmless boot-log timeout noise) | | nodemailer | 4 | 6+ | pending | | Babel | 7 beta | 7 stable | ✅ done (escala 1) | | i18next | 10 | current | debt — pin; not on the critical path | | Leaflet | 1.3.1 | current | debt — pin; not on the critical path | | 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) | ### 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.