Replace the old Lisp-like array expression representation
(["AND", ["PARAM", ...], ...]) with a class hierarchy rooted in
an abstract Expression class (Literal, Parameter, Binary, Unary,
FunctionCall, Conditional). This gives compile-time type safety
to expression construction, evaluation, and pattern matching.
Key changes:
- New lib/common/expression.ts defines the class hierarchy with
map/evaluate/toString methods
- Rewrite parser as hand-rolled recursive descent, removing the
parsimmon dependency
- Extend Path with a colon separator to unify parameter identity
and attributes into a single address space (e.g.
"Device.WiFi.SSID.1.Name:writable"). The colon field tracks
how many trailing segments are attribute qualifiers. PathSet
rewritten with separate param/attr segment indexes
- Flatten device attribute representation in the UI to
colon-delimited keys mirroring the new path syntax
The previous approach was simplistic and could result in inefficient queries
in certain edge cases, notably causing MongoDB to not utilize existing indexes
when querying by last inform timestamps given upper and lower bounds. The new
implementation leverages our existing expression minimizer code and can
therefore generate more efficient queries or at least queries that can take
advantage of existing indexes.
Let's try this again. The implementation of canRaise callback was
overly complicated and was causing the minimizer to ocassionally
get stuck in a local optima.
This also fixes a memory leak issue when the client disconnects before
completing the download. I suspect the leak had something to do with
using pipe() but I'm not sure if it's a bug in MongoDB driver or that
we were lacking proper error handling. Anyway, the pipe is no longer
leaking :)
Tiny in size so we don't need to fetch the 80KB yaml package for simple
YAML serializaiton needs in various pages. It's also 8x faster than
yaml package so that's a big plus for debug logging.
This also modifies the behavior of AND/OR operators to strictly return
true/false/null whereas previously they could return the value of the
first truthy/false operand. The purpose of this change is to maintain
expression equivalency before and after minimization.