meshai/work/dashboard-frontend/node_modules/internmap
malice 2c46c9104d
feat(region-routing): unified per-family routing cards + region-scoped family→channel routing (#87)
* feat(region-routing): P1 tagging + region_routes primitive + read/write API + preview launcher

- config.py: add Coverage.region_tagging (bool=False); add RegionRouteMatrix
  dataclass (enabled, cells) above NotificationsConfig; add region_routes field
  to NotificationsConfig; add explicit hydration branch for region_routes in
  _dict_to_dataclass mirroring destinations pattern.

- coverage_area.py: add MonitoringArea.name (str|None=None, frozen); update
  areas_from_config to preserve name; refactor inline geom extraction from
  classify_event_areas into shared _event_geom_json helper; add
  matching_area_names(geom_json, areas)->list[str] (additive, all named
  matches, config-order, deduped; gate unchanged); add event_region_names
  convenience wrapper.

- coverage_filter.py: add region_tagging ctor kwarg; stamp event.region/
  regions before the gate when region_tagging=True and areas non-empty and
  not event.regions (never clobbers satpass preset).

- pipeline/__init__.py: wire region_tagging into CoverageFilter construction.

- notification_routes.py: add GET /notifications/regions (named coverage area
  names, config-order, deduped); GET /notifications/region-routing (matrix as
  JSON); POST /notifications/region-routing (explicit RMW — only region_routes
  changes, toggles/rules/destinations survive).

- scripts/preview_dashboard.py: mesh-free launcher — dashboard API only, no
  mesh connector, no broadcast loop; vite runs separately.

All 87 coverage tests pass; 300 total pass; 6 pre-existing failures unchanged
(adapter config count mismatch + MeshCore EventType.NEW_CONTACT).

* feat(region-routing): manual region x family matrix editor page

Adds RegionRoutingMatrix.tsx — a plain editor over the region_routes
config primitive. Rows = families (via useFamilies()), cols = regions
(from GET /api/notifications/regions). Each cell exposes MT channel
(ChannelPicker single + includeDisabled), MC channel name (text input),
min_severity select (routine/priority/critical/immediate), and an enabled
checkbox. Only cells where MT or MC is set are included in the sparse
POST payload. Master enable toggle maps to top-level enabled. MT budget
guard warns when more than 7 distinct MT indices are in use. Sticky
family column; horizontal scroll for wide region sets.

Registers route /region-routing in App.tsx and adds "Region Routing"
nav entry (Map icon) under the Meshtastic section in Layout.tsx,
immediately after Routing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(region-routing): regions endpoint reads saved (disk) coverage so routing columns are dynamic without a bot restart; preview reloads config after writes

* feat(routing): unify MT/MC routing into per-family cards; region routing as an in-card expand; remove rules/destinations UI + standalone page

* refactor(routing): move Meshtastic Routing from /notifications to /meshtastic/routing (mirror /meshcore/routing); redirect legacy path

* feat(region-routing): dispatcher honors region_routes matrix (authoritative-on-match, per-region cooldown, per-channel dedup); non-matrix path unchanged

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(region-routing): matrix dedup key must match boot-restore 2-tuple form (prevents restart re-broadcast flood); regression test

---------

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 16:52:03 -06:00
..
src feat(region-routing): unified per-family routing cards + region-scoped family→channel routing (#87) 2026-07-07 16:52:03 -06:00
LICENSE feat(region-routing): unified per-family routing cards + region-scoped family→channel routing (#87) 2026-07-07 16:52:03 -06:00
package.json feat(region-routing): unified per-family routing cards + region-scoped family→channel routing (#87) 2026-07-07 16:52:03 -06:00
README.md feat(region-routing): unified per-family routing cards + region-scoped family→channel routing (#87) 2026-07-07 16:52:03 -06:00

InternMap

For live examples, see https://observablehq.com/@mbostock/internmap.

If you use dates as keys in a JavaScript Map (or as values in a Set), you may be surprised that it wont work as you expect.

dateMap = new Map([
  [new Date(Date.UTC(2001, 0, 1)), "red"],
  [new Date(Date.UTC(2001, 0, 1)), "green"] // distinct key!
])
dateMap.get(new Date(Date.UTC(2001, 0, 1))) // undefined!

Thats because Map uses the SameValueZero algorithm to determine key equality: for two dates to be considered the same, they must be the same instance (the same object), not just the same moment in time. This is true of the equality operator, too.

{
  const date1 = new Date(Date.UTC(2001, 0, 1));
  const date2 = new Date(Date.UTC(2001, 0, 1));
  return date1 === date2; // false!
}

You can avoid this issue by using primitive values such as numbers or strings as keys instead. But its tedious and easy to forget to coerce types. (Youll also need to do the inverse type conversion when pulling keys out of the map, say when using map.keys or map.entries, or when iterating over the map. The inverse above is new Date(key). Also, if you forget to coerce your key to a number when using map.get, its easy not to notice because the map wont throw an error; itll simply return undefined.)

numberMap = new Map([[978307200000, "red"]])
numberMap.get(978307200000) // "red"
numberMap.get(new Date(978307200000)) // undefined; oops!

Wouldnt it be easier if Map and Set “just worked” with dates? Or with any object that supports object.valueOf?

Enter InternMap. Interning refers to storing only one copy of each distinct key. An InternMap considers two Date instances representing the same moment to be equal, storing only the first instance.

map = new InternMap([
  [new Date(Date.UTC(2001, 0, 1)), "red"],
  [new Date(Date.UTC(2001, 0, 1)), "green"] // replaces previous entry
])
map.get(new Date(Date.UTC(2001, 0, 1))) // "green"
[...map.keys()] // [2001-01-01]

InternMap extends Map, so you can simply drop it in whenever youd prefer this behavior to the SameValueZero algorithm. Because InternMap calls object.valueOf only for non-primitive keys, note that you can pass primitive keys, too.

map.get(978307200000) // "green"; this works too!

InternMap keeps only the first distinct key according to its associated primitive value. Avoid adding keys to the map with inconsistent types.

map2 = new InternMap([
  [978307200000, "red"], // danger!
  [new Date(Date.UTC(2001, 0, 1)), "blue"]
])
map2.get(new Date(Date.UTC(2001, 0, 1))) // "blue"; this still works…
[...map2.keys()] // [978307200000]; but the key isnt a Date

While InternMap uses object.valueOf by default to compute the intern key, you can pass a key function as a second argument to the constructor to change the behavior. For example, if you use JSON.stringify, you can use arrays as compound keys (assuming that the array elements can be serialized to JSON).

map3 = new InternMap([
  [["foo", "bar"], 1],
  [["foo", "baz"], 2],
  [["goo", "bee"], 3]
], JSON.stringify)
map3.get(["foo", "baz"]) // 2

Theres an InternSet class, too.

set = new InternSet([
  new Date(Date.UTC(2000, 0, 1)),
  new Date(Date.UTC(2001, 0, 1)),
  new Date(Date.UTC(2001, 0, 1))
])