The 4 class components on UNSAFE_componentWillReceiveProps converted:
- FromNow, FireStats: state is a pure mirror of props -> getDerivedStateFromProps
- Fires: split the derived state (getDerivedStateFromProps) from the URL-
canonicalization side effect (componentDidUpdate, guarded by prevProps) so
the side effect never runs inside the pure gDSFP
- SelectionMap: marker is also locally draggable, so gDSFP would clobber a
drag -> componentDidUpdate guarded on a real center/distance value change
(no clobber, no setState loop), merged with the existing fit()
Browser-verified: /fires count, fire detail + FromNow, active-fire URL
canonicalizes to /fire/archive/:id, home SelectionMap renders stably (no
render loop). No UNSAFE_ warnings remain from our code. REST smoke
byte-identical.
React 19 drops defaultProps on function components. Converted the 9 of
ours that used it (App, Navigation, ReSendEmail, Reconnect, OAuthLoginButton,
PageHeader, Page, EditDocument, EditSubscription) to destructured default
params; App defaults userId/emailAddress in its withTracker instead (it
spreads {...props} widely). Class components keep defaultProps (still
supported). The only defaultProps warnings left are from the react-share
npm package (CreatedButton/Icon), not our code. REST smoke byte-identical.
react-i18next 14 reserves `count` as the pluralization variable and no
longer interpolates it from a <Trans> object child, so <strong>{{count:N}}</strong>
left {{count,number}} unresolved -> the number formatter ran on undefined ->
"NaN". (t() and the countTotal var were unaffected; regression from the
i18next 10->23 / react-i18next 7->14 bump.)
Renamed the variable count -> inMap in FiresMap.js and in the
activeFireInMapCount value of the es/en/gl locales (the string has no plural
forms, so count was only an unlucky name). Browser-verified: /fires now shows
the real count; home/zones/fire-detail render with no real errors. REST smoke
byte-identical.
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.
Removes the Router/Switch/Route/Link/LinkContainer legacy-context
warnings. Keeps the shared history singleton (used outside React by
NotificationsObserver + Utils/location) via unstable_HistoryRouter, and
bridges v6 hooks back to v4-shaped history/match/location props with a
new withRouterCompat HOC so the ~20 class pages stay untouched.
- <Switch> -> <Routes>, component= -> element=
- Authenticated/Public -> guard components (children or <Navigate replace>)
- LocationListener -> useLocation + useEffect function
- regex route /fire/:type(active|archive|alert)/:id -> /fire/:type/:id
- history.listen callback is ({ location }) in history v5
Browser-verified: SPA nav, /subscriptions->/login auth redirect,
deep-link /fire/archive/:id (params), browser back/forward. REST smoke
byte-identical.
Removes the largest batch of React-19-blocking warnings: the 0.31
components (Grid, FormGroup, ControlLabel, Navbar.Header/Brand, Checkbox,
SafeAnchor) all leaned on legacy context / defaultProps.
29 files converted: Grid->Container, FormGroup/ControlLabel/FormControl/
HelpBlock -> Form.Group/Label/Control/Text, Checkbox -> Form.Check (label
as prop), bsStyle->variant (default->secondary), bsSize->size,
pull-right->float-end. Custom Col.js now re-exports v2's Col; custom
NavItem.js rewritten self-contained (no SafeAnchor/createChainedFunction);
Navigation.js drops react-bootstrap Navbar (raw markup anyway).
CSS stays on Bootstrap 4 (alexwine:bootstrap-4) for now: the BS4->5 jump
is entangled with the jQuery carousel/swipe + navbar-collapse and is
tracked as separate debt in UPGRADE.md. Only .form-label needed a shim
(forms.scss). Browser-verified home carousel+navbar, signup form + terms
checkbox, login form; REST smoke byte-identical.
react-helmet 5 is unmaintained and breaks under React 18 StrictMode.
Same <Helmet> API in the 15 pages; HelmetProvider wraps <App/> in the
client entry. Browser-verified: per-page titles, meta description and
hreflang alternates all inject. REST smoke byte-identical.
None of them is imported anywhere: reactstrap 5.0.0-alpha.3 was never
adopted (react-bootstrap covers the UI), react-leaflet-sidebarv2 and
react-addons-pure-render-mixin were never used, and react-router-hash-link
only survived as a commented-out import in Index.js.
UNSAFE_ rename of our 4 componentWillReceiveProps (FireStats, Fires, FromNow,
SelectionMap) and Authenticated/Public component propType func -> elementType.
Rest of the console noise is legacy react-* dev-mode warnings, absent in prod.
Fire collections use idGeneration:'MONGO', so _id is a Mongo.ObjectID whose
toString() is ObjectID("<hex>"), not the bare hex. Interpolating _id into a
URL produced /fire/archive/ObjectID("c0..."), which no route matches -> the
fire-detail page 404'd. New hexId() helper returns the 24-char hex for an
ObjectID (and passes plain strings through); applied at the three string-context
sites: the map-marker click URL (MarkListeners), the active->archive redirect,
and the comments referenceId (Fires.js). Left falsePositives.insert untouched —
its check() expects a Meteor.Collection.ObjectID, so it takes the object.
Verified in browser: /fire/archive/<hex> and /fire/active/<hex> render the
detail page (map + comments), URL stays clean hex, no ObjectID( anywhere.
Silences the dart-sass deprecation warnings that flooded every build:
- All @import of local partials (colors, mixins, and the CSS-emitting
partials) -> @use '...' as * (partials are pure defs, so 'as *' keeps the
global names and the ~25 call sites unchanged). Added explicit @use './colors'
to bootstrap-overrides.scss and cookies-eu.scss, which used -palette*
transitively (broke under @use's scoping).
- lighten(c, x%) -> color.adjust(c, $lightness: x%) and darken -> negative,
with @use 'sass:color' in the 6 affected files.
Compiles clean (byte-for-byte CSS behavior preserved).
GlitchTip is behind Cloudflare, which answers the browser's cross-site ingest
POSTs with 503 (no CORS headers -> 'Failed to fetch'); server-side requests
pass. The browser SDK now posts envelopes same-origin to /sentry-tunnel, and
the server forwards them to GlitchTip:
- imports/startup/server/sentryTunnel.js: WebApp handler that relays the raw
envelope to <dsn>/api/<project>/envelope/?sentry_key=<key> over IPv4
(family:4 avoids Happy-Eyeballs picking Cloudflare's unroutable IPv6 on
IPv4-only hosts). GlitchTip authenticates by the sentry_key query, so the
key is derived from the DSN and passed explicitly.
- client ravenLogger: tunnel: '/sentry-tunnel'.
Verified: POST /sentry-tunnel -> 200 (envelope reaches GlitchTip).
0.2.16 (React-15/16-era withTracker) looped forever on fire-detail pages
under React 18: the withTracker subscription never reached ready() so the
page stayed blank and pegged a CPU core. Publications were already correct
(subscribing standalone from the console readied instantly). 3.0.6 (the
React-18 line for Meteor 3.1) fixes it with zero call-site changes. Also
drops tmeasday:check-npm-versions (the constraint that pinned 0.2.16).
Browser-verified at /fire/archive/<id>: full render (map, NASA/satellite,
FalsePositives widget, Comments box), zero console errors. REST smoke
byte-identical (12/12).
Browser verification of the Comments feature surfaced a pre-existing client
bug: fire-detail pages (/fire/*/:id) stay blank because the page's withTracker
subscription never reaches ready() and re-subscribes in a loop. Isolated it:
the publications ready fine when subscribed standalone, so it's client-side —
react-meteor-data@0.2.16 (React-15/16 era) misbehaving under React 18 for a
component that gates render on subscription.ready(). Fix = upgrade
react-meteor-data to 2.x (useTracker). Documented in UPGRADE.md; not yet done.
Drop-in for our usage: email.js only does createTransport(MAIL_URL) and reads
transport.options.auth.user (production-only from() branch) — both verified
identical in v6 (createTransport from a URL still populates options.auth.user).
Actual sending goes through ostrio:mailer (MailTime 2.5), v6-compatible. Mail
server inits clean ('I'm the mail server'); REST smoke byte-identical (12/12).
flowkey:raven (Sentry legacy SDK) is dead on Meteor 3 and, with the old
sentry.comunes.org DSN unreachable, spammed the boot log with 502s on every
exception. Replaced by the modern SDKs (8.x) behind the same ravenLogger.log()
facade so the 6 call sites are unchanged. Empty DSN -> plain console logger
(same behavior as before).
End-to-end verified against a real backend: GlitchTip deployed on aaron
(Sentry-compatible, fits in ~1.5GB vs Sentry's 16GB), fronted at
sentry.comunes.org. A test exception sent from the dev server appeared in
GlitchTip within seconds. REST smoke byte-identical (12/12).
Compose con los 5 servicios: mongo:7 (replica set rs0 + mongo-init one-shot),
redis (AOF), web Meteor 3.1 (Dockerfile multi-stage: builder debian con
meteor-tool 3.1 -> server-deps alpine -> runtime node:22-alpine), notifications
(dry-run) y node-red 4. Healthchecks (127.0.0.1, no localhost: busybox wget
prefiere IPv6 y Meteor escucha IPv4), restart unless-stopped, logging rotado
3x10MB, secretos montados desde ./secrets (gitignored, solo .example versionado).
.npmrc legacy-peer-deps para el ERESOLVE de las libs react viejas.
Validado: los 5 servicios healthy y smoke REST byte-identico (12/12) contra la
web dockerizada en :3200. RUNBOOK.md documenta arranque, smoke, backups y
rollback. No toca infra de Comunes.
chimp 0.51.1 (Selenium 2 / cucumber e2e runner) was the last dependency
pulling native fibers@1.x, which cannot compile on Node 22 and failed the
clean Docker image build (node-gyp: no prebuilt, no python). Not imported
anywhere. Also add python-is-python3 to the builder stage so node-gyp can
find python for any other native dep. Boot + REST smoke green.
The 1.x native binding was built for another Node ABI and failed to load
(accounts-password silently used the pure-JS fallback) and 1.x does not
compile on Node 22, which would break the Docker image build. 5.1.1 ships
prebuilt Node-22 binaries and loads natively. Boot + REST smoke green.
react/react-dom ^18.3.1 (--legacy-peer-deps for the pinned ancient react-*
libs, which still work on 18 via legacy context but gate React 19). Client
entry uses createRoot. Browser-verified on / and /fires; REST smoke
byte-identical (12/12).
alanning:roles removed (client crashed the whole bundle; plain user.roles
checks instead), selaias:cookie-consent replaced by in-repo React banner,
publish-performant-counts vendored with countAsync, Meteor.autorun dropped
(App)/Tracker.autorun (FiresMap), and the map publications not covered by
the REST smoke (activefiresmyloc, activefiresunionmyloc, fireAlerts,
oauth.verifyConfiguration) converted to async APIs.
Verified in browser: / and /fires render with map + cookie banner, zero
uncaught exceptions. REST smoke byte-identical (12/12).
fixtures: drop @cleverbeagle/seeder (sync Mongo, unmaintained) for a small
idempotent async seeder (same dev accounts). sitemaps: drop dead
gadicohen:sitemaps for a WebApp.connectHandlers /sitemap.xml with the same
static page list (the per-fire section was already disabled). Verified on
dev boot: 6 users ensured, sitemap serving; REST smoke byte-identical.
fireFromId, fireFromAlertId, fireFromActiveId and fireFromHash now use the
async collection APIs and async publish handlers (so try/catch catches
rejections). falsePositivesMyloc/industriesMyloc/comments.forReference are
cursor-only and needed no change. Verified over raw DDP against the seeded
dev Mongo 7; REST smoke stays byte-identical (12/12).
All 12 Flutter REST endpoints byte-identical to the 1.6.1.1 baseline, running on
Meteor 3.1 against dockerized MongoDB 7. The 3.x jump works end-to-end.
- Rest.js: every endpoint action -> async; all Mongo calls -> *Async
(findOneAsync/countAsync/fetchAsync/upsertAsync/removeAsync).
- Helpers to async: countRealFires (accepts array|cursor, forEachAsync->for-of),
firesUnion (fetchAsync), fireFromHash/findOrCreateFire (await, dropped Fibers
Promise.await), subscriptionsInsert/subscriptionsRemove, upsertFalsePositive,
falsePositives.insert, subscriptions.update method, countFiresInRegions.
- getFires: countAsync() on a $near cursor throws on the mongodb 6 driver
($near not allowed in the aggregation countDocuments uses) -> fetch once and
use array length for total.
- Route order: json-routes 3.0 matches in registration order, so
mobile/subscriptions/all/:token/:mobileToken is now registered BEFORE
:token/:mobileToken/:subsId (else GET .../all/x/y hit the delete-only route).
Major milestone: the Meteor 3.1 server boots and runs against dockerized
MongoDB 7, and the REST framework responds (status/uptime OK).
- collection2 v4: import its eager entry 'meteor/aldeed:collection2/static.js'
from new eager files server|client/00-collection2-init.js so main.js runs and
patches attachSchema before collections load (v4 is fully lazy, no main module).
- fixtures.js disabled: @cleverbeagle/seeder uses sync Mongo, no Meteor 3 support
(dev fixtures, not needed for smoke). Debt.
- sitemaps.js disabled: gadicohen:sitemaps@0.0.17 pre-0.9 API, no 'sitemaps'
global on Meteor 3. Debt (SEO).
- Vendored restivus patched for async endpoints: route handler is async and
awaits _callEndpoint; _callEndpoint awaits the action + auth/role checks.
NEXT: Rest.js endpoints + helpers (countRealFires/firesUnion/whichAreFalsePositives/
fireFromHash/subscriptionsInsert/upsertFalsePositive) still use sync Mongo
(.count()/.findOne()/.fetch()/.insert()/.upsert()) -> convert to *Async, then
smoke test against Mongo 7.
Empirically verified: 2.4 and 2.5 green against mongo:3.2 (npm-mongo 3.9.1);
2.6 bumps npm-mongo to 4.3.1 which refuses MongoDB < 3.6
(MongoCompatibilityError, wire version 4). Branch lands on Meteor 2.5.
Reaches Meteor 2.3 building + running against Mongo 3.2, REST smoke test
byte-identical to the 1.6.1.1 baseline (all 12 Flutter endpoints green).
Dead/abandoned Atmosphere packages removed or replaced (the upgrade blockers):
- arkham:comments-ui (no Meteor 2.x build; pinned accounts-password@1.x):
reimplemented the fire-page comments as a React feature:
imports/api/Comments/ (collection, server methods, publication, media
analyzers, new-fire-comment email) + imports/ui/components/Comments/CommentsBox.
Comment text now rendered as safe plain text + image/youtube embed (was
markdown-to-HTML). Wired into Fires.js; sitemaps.js + migration v11 updated
to the new collection. Old Blaze/startup comments files deleted.
- nimble:restivus (REST API; pinned accounts-password@1.3.3, CoffeeScript source
that crashes this build host): vendored as a local package
packages/nimble-restivus with the .coffee precompiled to plain JS and the
accounts-password constraint loosened to 2.x. REST behavior unchanged
(verified by smoke test). This also dropped the entire iron:router stack.
- maximum:server-transform (+ peerlibrary:*, meteorhacks:zones/inject-initial):
unused; removal broke Meteor.publishTransformed in FalsePositives publications
-> replaced with plain Meteor.publish (no transform was configured).
- less, markdown (no source files), and the dead test stack
(meteortesting:mocha, practicalmeteor:chai, xolvio:cleaner) which transitively
pulled coffeescript@1.0.17 — the build plugin that crashed meteor-tool
(node_contextify assertion) on this machine. Removing it unblocked the build.
- fourseven:scss 4.5.4 -> 4.14.1 (node-sass 4.5.3 doesn't build on node 12).
npm-mongo driver at 2.3 is 3.9.x — still compatible with the production Mongo
3.2 replica set.
The push/email notification processing now lives in the tcef-notifications
service (fases 1a-1c). Removed from the web:
- imports/startup/server/notificationsObserver.js (observe Notifications -> processNotif)
- imports/modules/server/notificationsProcess.js (node-gcm push + notif emails)
- the 'Process pending notif' SyncedCron job in cron.js
- node-gcm dependency (dead Google API since jun-2024)
subsUnion.js is KEPT: it feeds the subs-public-union SiteSettings that the REST
API (status/subs-public-union) serves to the Flutter app; it is not part of the
notifications pipeline.
DEPLOY ORDERING (see UPGRADE.md): production must not run this build until the
tcef-notifications service is emitting in prod — otherwise notifications stop.
REST smoke test green (byte-identical).
Adds a standalone Node smoke test (smoke/) that exercises every REST endpoint
consumed by the Flutter app against a seeded local dev server and compares
responses to committed snapshots. This is the safety net for the Meteor upgrade:
it must stay byte-identical after every escala.
- smoke/seed.js: deterministic Mongo seed (fixed ObjectIds, geo indexes)
- smoke/run.js: calls endpoints, normalizes volatile fields, diffs snapshots
- smoke/smoke.sh: seed + run wrapper
- smoke/snapshots/: baseline captured on Meteor 1.6.1.1
- IPGeocoder.js: degrade gracefully when MaxMind DB absent (dev boot)
- settings-development.json: dev-only internalApiToken to enable the REST API
- .meteorignore: exclude smoke/ from the Meteor server build
Guard reversible al inicio de processNotif: los canales listados en
settings.private.notifDisabledChannels (p.ej. ['mobile','web']) los gestiona el
microservicio tcef-notifications, y el observer/cron viejos los ignoran.
Exclusion mutua por canal sin desplegar codigo (solo editar settings).
ES5-compatible (Meteor 1.6 / Node 8).
Retira import './segfaults' (workaround de crash obsoleto) y añade
leaflet-workaround.js (shims global window/document/navigator para
render de Leaflet en servidor). Ajustes menores en publications de
FalsePositives y subsUnion.