* 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> |
||
|---|---|---|
| .. | ||
| lib/rw | ||
| test | ||
| .eslintrc | ||
| .npmignore | ||
| index.js | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
rw - Now stdin and stdout are files.
How do you read a file from stdin? If you thought,
var contents = fs.readFileSync("/dev/stdin", "utf8");
you’d be wrong, because Node only reads up to the size of the file reported by fs.stat rather than reading until it receives an EOF. So, if you redirect a file to your program (cat file | program), you’ll only read the first 65,536 bytes of your file. Oops.
What about writing a file to stdout? If you thought,
fs.writeFileSync("/dev/stdout", contents, "utf8");
you’d also be wrong, because this tries to close stdout, so you get this error:
Error: UNKNOWN, unknown error
at Object.fs.writeSync (fs.js:528:18)
at Object.fs.writeFileSync (fs.js:975:21)
(Also, this doesn’t work on Windows, because Windows doesn’t support /dev/stdout, /dev/stdin and /dev/stderr!)
Shucks. So what should you do?
You could use a different pattern for reading from stdin:
var chunks = [];
process.stdin
.on("data", function(chunk) { chunks.push(chunk); })
.on("end", function() { console.log(chunks.join("").length); })
.setEncoding("utf8");
But that’s a pain, since now your code has two different code paths for reading inputs depending on whether you’re reading a real file or stdin. And the code gets even more complex if you want to read that file synchronously.
You could also try a different pattern for writing to stdout:
process.stdout.write(contents);
Or even:
console.log(contents);
But if you try to pipe your output to head, you’ll get this error:
Error: write EPIPE
at errnoException (net.js:904:11)
at Object.afterWrite (net.js:720:19)
Huh.
The rw module fixes these problems. It provides an interface just like readFile, readFileSync, writeFile and writeFileSync, but with implementations that work the way you expect on stdin and stdout. If you use these methods on files other than /dev/stdin or /dev/stdout, they simply delegate to the fs methods, so you can trust that they behave identically to the methods you’re used to.
For example, now you can read stdin synchronously like so:
var contents = rw.readFileSync("/dev/stdin", "utf8");
Or to write to stdout:
rw.writeFileSync("/dev/stdout", contents, "utf8");
And rw automatically squashes EPIPE errors, so you can pipe the output of your program to head and you won’t get a spurious stack trace.
To install, npm install rw.
Note
If you want to read synchronously from stdin using readFileSync, you cannot also use process.stdin in the same program. Likewise, if you want to write synchronously to stdout or stderr using writeFileSync, you cannot use process.stdout or process.stderr, respectively. (This includes using console.log and the like!) Failure to heed this warning may result in error: EAGAIN, resource temporarily unavailable. Unfortunately, it does not appear possible for this library to fix this issue automatically, so please use caution.
Only the asynchronous methods readFile and writeFile are supported on Windows. Node has no synchronous API for reading from process.stdin or writing to process.stdout or process.stderr, so you’re out of luck!
API Reference
# rw.readFile(path[, options], callback)
Reads the file at the specified path completely into memory, invoking the specified callback once the data is available and the file is closed. The callback is invoked with two arguments: the error that occurred during read (hopefully null), and the read data. If options is a string, it specifies the encoding to use, in which case the read data will be a string; otherwise options is an object, and may specify encoding and flag properties. This method is a drop-in replacement for fs.readFile and fixes the behavior of special files such as /dev/stdin.
# rw.readFileSync(path[, options])
Reads the file at the specified path completely into memory, synchronously, returning the data. If an error occurred during read, this function throws an error instead. If options is a string, it specifies the encoding to use, in which case the read data will be a string; otherwise options is an object, and may specify encoding and flag properties. This method is a drop-in replacement for fs.readFileSync and fixes the behavior of special files such as /dev/stdin.
# rw.writeFile(path, data[, options], callback)
Writes the specified data (completely in memory) to a file at the specified path, invoking the specified callback once the data is completely written and the file is closed. The callback is invoked with a single argument: the error that occurred during write (hopefully null). If options is a string, it specifies the encoding to use, in which case the data must be a string; otherwise options is an object, and may specify encoding, mode and flag properties. This method is a drop-in replacement for fs.writeFile and fixes the behavior of special files such as /dev/stdout.
# rw.writeFileSync(path, data[, options])
Writes the specified data (completely in memory) to a file at the specified path, synchronously, returning once the data is completely written and the file is closed. Throws an error if one occurs during write. If options is a string, it specifies the encoding to use, in which case the data must be a string; otherwise options is an object, and may specify encoding, mode and flag properties. This method is a drop-in replacement for fs.writeFileSync and fixes the behavior of special files such as /dev/stdout.
# rw.dash.readFile(path[, options], callback)
Equivalent to rw.readFile, except treats a path of - as /dev/stdin. Useful for command-line arguments.
# rw.dash.readFileSync(path[, options])
Equivalent to rw.readFileSync, except treats a path of - as /dev/stdin. Useful for command-line arguments.
# rw.dash.writeFile(path, data[, options], callback)
Equivalent to rw.writeFile, except treats a path of - as /dev/stdout. Useful for command-line arguments.
# rw.dash.writeFileSync(path, data[, options])
Equivalent to rw.writeFileSync, except treats a path of - as /dev/stdout. Useful for command-line arguments.