signalk-server
Version:
An implementation of a [Signal K](http://signalk.org) server for boats.
119 lines • 4.02 kB
JavaScript
;
//
// Filters delta updates targeting the self vessel context. Static identity
// data (name, mmsi, design dimensions, GPS antenna offsets, callsignVhf) is
// configured at the server level and must not be overwritten by deltas from
// other providers; the only exception is the special `defaults` $source that
// carries the server-level base data itself.
//
// The hot path is `filterStaticSelfData` -> `filterSelfDataKP`, called for
// every value of every self-context delta.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.filterStaticSelfData = filterStaticSelfData;
const ROOT_VESSEL_FILTERED_KEYS = new Set(['name', 'mmsi']);
const SELF_DATA_FILTERED_PATHS = new Set([
'design.aisShipType',
'design.beam',
'design.length',
'design.draft',
'sensors.gps.fromBow',
'sensors.gps.fromCenter'
]);
const COMMUNICATION_FILTERED_KEYS = new Set([
'callsignVhf'
]);
function isRecord(v) {
return v !== null && typeof v === 'object';
}
function omitKeys(obj, exclude) {
const res = {};
for (const k of Object.keys(obj)) {
if (!exclude.has(k)) {
res[k] = obj[k];
}
}
return res;
}
// Probe the small filter Set against the (typically larger) input object
// rather than enumerating the input's keys.
function hasAnyKey(obj, keys) {
for (const k of keys) {
if (k in obj) {
return true;
}
}
return false;
}
// Accepts Partial<Delta> because the upstream input pipeline produces deltas
// with optional fields and patches them in place; see handleMessage in
// src/index.ts.
function filterStaticSelfData(delta, selfContext) {
if (delta.context !== selfContext || !delta.updates) {
return delta;
}
const keptUpdates = [];
for (const update of delta.updates) {
if (!('values' in update) || update['$source'] === 'defaults') {
keptUpdates.push(update);
continue;
}
const kept = [];
for (const pathValue of update.values) {
const nvp = filterSelfDataKP(pathValue);
if (nvp) {
kept.push(nvp);
}
}
if (kept.length === 0) {
// Every value was filtered out. Keep the update only if it still
// carries a meta payload; otherwise drop it so empty husk updates
// don't flow downstream.
if ('meta' in update) {
delete update.values;
keptUpdates.push(update);
}
}
else {
update.values = kept;
keptUpdates.push(update);
}
}
delta.updates = keptUpdates;
return delta;
}
function filterSelfDataKP(pathValue) {
// Path '' carries the vessel root: filter top-level identity keys plus the
// communication subobject's callsignVhf.
if (pathValue.path === '') {
if (!isRecord(pathValue.value)) {
// Malformed self-root payload (null, primitive). Pass through unchanged
// rather than throwing so co-batched valid updates are not lost.
return pathValue;
}
let value = pathValue.value;
if (hasAnyKey(value, ROOT_VESSEL_FILTERED_KEYS)) {
value = omitKeys(value, ROOT_VESSEL_FILTERED_KEYS);
pathValue.value = value;
}
const comm = value.communication;
if (isRecord(comm) && hasAnyKey(comm, COMMUNICATION_FILTERED_KEYS)) {
const filtered = omitKeys(comm, COMMUNICATION_FILTERED_KEYS);
if (Object.keys(filtered).length === 0) {
delete value.communication;
}
else {
value.communication = filtered;
}
}
if (Object.keys(value).length === 0) {
return null;
}
return pathValue;
}
if (SELF_DATA_FILTERED_PATHS.has(pathValue.path)) {
return null;
}
return pathValue;
}
//# sourceMappingURL=staticDataFilter.js.map