@winkjs/composer 0.0.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +106 -80
- package/package.json +56 -28
- package/src/composer.js +75 -0
- package/src/core/batch/aoo-encoder.js +148 -0
- package/src/core/batch/index.js +12 -0
- package/src/core/batch/sliding-window.js +91 -0
- package/src/core/codec/index.js +137 -0
- package/src/core/emitter-manager/mqtt/constants.js +102 -0
- package/src/core/emitter-manager/mqtt/emitter.js +780 -0
- package/src/core/emitter-manager/mqtt/index.js +190 -0
- package/src/core/emitter-manager/mqtt/mqtt-store.js +538 -0
- package/src/core/emitter-manager/terminal/assert-columns.js +98 -0
- package/src/core/emitter-manager/terminal/formatters.js +160 -0
- package/src/core/emitter-manager/terminal/index.js +341 -0
- package/src/core/env-vars.js +245 -0
- package/src/core/mqtt-protocol.js +47 -0
- package/src/core/partition-manager/index.js +7 -0
- package/src/core/partition-manager/init.js +44 -0
- package/src/core/partition-manager/introspect.js +15 -0
- package/src/core/partition-manager/publish-to.js +6 -0
- package/src/core/partition-manager/recompute.js +5 -0
- package/src/core/partition-manager/reset.js +5 -0
- package/src/core/partition-manager/update.js +264 -0
- package/src/core/partition-manager/validate-flow.js +157 -0
- package/src/core/semantics/digest.js +128 -0
- package/src/core/semantics/index.js +83 -0
- package/src/core/semantics/loader.js +571 -0
- package/src/core/semantics/schemas/asset-class-schema.js +218 -0
- package/src/core/semantics/schemas/column-schema.js +651 -0
- package/src/core/semantics/schemas/enum-schema.js +153 -0
- package/src/core/semantics/schemas/index.js +51 -0
- package/src/core/semantics/warnings.js +141 -0
- package/src/core/shutdown-manager/index.js +289 -0
- package/src/core/source-manager/csv/index.js +171 -0
- package/src/core/source-manager/csv/start.js +456 -0
- package/src/core/source-manager/mqtt/client.js +406 -0
- package/src/core/source-manager/mqtt/constants.js +147 -0
- package/src/core/source-manager/mqtt/dedup.js +244 -0
- package/src/core/source-manager/mqtt/index.js +260 -0
- package/src/core/source-manager/mqtt/status.js +546 -0
- package/src/core/source-manager/test-harness/comparator.js +226 -0
- package/src/core/source-manager/test-harness/field-generator.js +85 -0
- package/src/core/source-manager/test-harness/fuzz.js +87 -0
- package/src/core/source-manager/test-harness/index.js +115 -0
- package/src/core/source-manager/test-harness/prng.js +57 -0
- package/src/core/source-manager/test-harness/start.js +232 -0
- package/src/core/source-manager/test-harness/validate.js +213 -0
- package/src/core/storage-manager/questdb/assert-columns.js +120 -0
- package/src/core/storage-manager/questdb/ensure-tables.js +187 -0
- package/src/core/storage-manager/questdb/index.js +1251 -0
- package/src/core/storage-manager/questdb/persist-plan.js +488 -0
- package/src/core/storage-manager/questdb/writers.js +142 -0
- package/src/core/test-utils/sealed-record.js +80 -0
- package/src/core/test-utils/soak-signature.js +149 -0
- package/src/core/test-utils/tcp-proxy.js +106 -0
- package/src/core/tunable/helpers.js +254 -0
- package/src/core/tunable/index.js +87 -0
- package/src/core/utils/console-formatter/index.js +207 -0
- package/src/core/utils/create-smart-store-as/index.js +98 -0
- package/src/core/utils/create-smart-store-as/sanitize-field.js +46 -0
- package/src/core/utils/create-smart-store-as/smart-truncate.js +93 -0
- package/src/core/utils/flow/index.js +5 -0
- package/src/core/utils/flow/make-collision-checker.js +23 -0
- package/src/core/utils/flow/to-kebab.js +20 -0
- package/src/core/utils/half-life/index.js +111 -0
- package/src/core/utils/node/disable.js +11 -0
- package/src/core/utils/node/enable.js +11 -0
- package/src/core/utils/node/execute-triggers.js +40 -0
- package/src/core/utils/node/index.js +10 -0
- package/src/core/utils/node/pause.js +11 -0
- package/src/core/utils/node/populate-predicate-inputs.js +14 -0
- package/src/core/utils/node/publish-nan.js +19 -0
- package/src/core/utils/node/reset-predicate-inputs.js +11 -0
- package/src/core/utils/node/unpause.js +11 -0
- package/src/core/utils/node/validate-spec.js +22 -0
- package/src/core/utils/options/resolve-field-keyed.js +115 -0
- package/src/core/utils/quantize/index.js +75 -0
- package/src/core/utils/stats/formulas.js +67 -0
- package/src/core/utils/stats/index.js +3 -0
- package/src/core/utils/validate/composer-validators.js +141 -0
- package/src/core/utils/validate/helpers.js +206 -0
- package/src/core/utils/validate/index.js +12 -0
- package/src/core/utils/validate/type-validators.js +538 -0
- package/src/core/utils/validate/validate.js +107 -0
- package/src/core/utils/validate/validators.js +177 -0
- package/src/core/wiring/assert-handle.js +57 -0
- package/src/core/wiring/assert-module.js +63 -0
- package/src/core/wiring/index.js +5 -0
- package/src/core/wiring/node-type-to-module.js +17 -0
- package/src/core/wiring/wire-emitters.js +256 -0
- package/src/core/wiring/wire-linear-graph.js +25 -0
- package/src/core/wiring/wire-node.js +157 -0
- package/src/core/wiring/wire-semantics.js +173 -0
- package/src/core/wiring/wire-storages.js +344 -0
- package/src/flow/check-output-collisions.js +93 -0
- package/src/flow/consts.js +9 -0
- package/src/flow/deep-clone-spec.js +64 -0
- package/src/flow/driver.js +171 -0
- package/src/flow/expand-single-input-array.js +76 -0
- package/src/flow/expand-template-spec.js +93 -0
- package/src/flow/flow-api-schemas.js +300 -0
- package/src/flow/flow.js +658 -0
- package/src/flow/for-each.js +225 -0
- package/src/flow/get-signature-pattern.js +31 -0
- package/src/flow/get-target-array.js +46 -0
- package/src/flow/inspect.js +67 -0
- package/src/flow/load-node-module.js +81 -0
- package/src/flow/make-node-method.js +112 -0
- package/src/flow/resolve-pick-by-field.js +77 -0
- package/src/flow/resolve-tunables.js +76 -0
- package/src/flow/run.js +392 -0
- package/src/flow/serialize.js +161 -0
- package/src/flow/signatures-schema.js +222 -0
- package/src/flow/spec-builder.js +65 -0
- package/src/flow/validate-triggers.js +250 -0
- package/src/flow/validate.js +261 -0
- package/src/nodes/accumulate/index.js +21 -0
- package/src/nodes/accumulate/init.js +48 -0
- package/src/nodes/accumulate/introspect.js +123 -0
- package/src/nodes/accumulate/publish-to.js +34 -0
- package/src/nodes/accumulate/recompute.js +27 -0
- package/src/nodes/accumulate/reset.js +25 -0
- package/src/nodes/accumulate/update.js +42 -0
- package/src/nodes/appraise/calibrate.js +64 -0
- package/src/nodes/appraise/decision.js +66 -0
- package/src/nodes/appraise/deviation.js +140 -0
- package/src/nodes/appraise/index.js +23 -0
- package/src/nodes/appraise/init.js +165 -0
- package/src/nodes/appraise/integrate.js +45 -0
- package/src/nodes/appraise/introspect.js +352 -0
- package/src/nodes/appraise/publish-to.js +93 -0
- package/src/nodes/appraise/receptor.js +69 -0
- package/src/nodes/appraise/recompute.js +79 -0
- package/src/nodes/appraise/reset.js +53 -0
- package/src/nodes/appraise/update.js +130 -0
- package/src/nodes/archive/csv/index.js +6 -0
- package/src/nodes/archive/csv/init.js +36 -0
- package/src/nodes/archive/csv/load.js +70 -0
- package/src/nodes/archive/csv/reset.js +14 -0
- package/src/nodes/archive/csv/simple-parser.js +216 -0
- package/src/nodes/archive/csv/test.js +173 -0
- package/src/nodes/archive/csv/update.js +63 -0
- package/src/nodes/butterworth-filter/index.js +18 -0
- package/src/nodes/butterworth-filter/init.js +184 -0
- package/src/nodes/butterworth-filter/introspect.js +228 -0
- package/src/nodes/butterworth-filter/publish-to.js +21 -0
- package/src/nodes/butterworth-filter/recompute.js +13 -0
- package/src/nodes/butterworth-filter/reset.js +30 -0
- package/src/nodes/butterworth-filter/update.js +38 -0
- package/src/nodes/categorize/index.js +19 -0
- package/src/nodes/categorize/init.js +58 -0
- package/src/nodes/categorize/introspect.js +177 -0
- package/src/nodes/categorize/publish-to.js +29 -0
- package/src/nodes/categorize/recompute.js +14 -0
- package/src/nodes/categorize/reset.js +16 -0
- package/src/nodes/categorize/update.js +53 -0
- package/src/nodes/controller/index.js +9 -0
- package/src/nodes/controller/init.js +55 -0
- package/src/nodes/controller/introspect.js +91 -0
- package/src/nodes/controller/publish-to.js +8 -0
- package/src/nodes/controller/recompute.js +9 -0
- package/src/nodes/controller/reset.js +17 -0
- package/src/nodes/controller/update.js +63 -0
- package/src/nodes/diff/index.js +19 -0
- package/src/nodes/diff/init.js +37 -0
- package/src/nodes/diff/introspect.js +132 -0
- package/src/nodes/diff/publish-to.js +21 -0
- package/src/nodes/diff/recompute.js +12 -0
- package/src/nodes/diff/reset.js +12 -0
- package/src/nodes/diff/update.js +37 -0
- package/src/nodes/digest-moments/formulas.js +12 -0
- package/src/nodes/digest-moments/index.js +14 -0
- package/src/nodes/digest-moments/init.js +67 -0
- package/src/nodes/digest-moments/introspect.js +154 -0
- package/src/nodes/digest-moments/publish-to.js +34 -0
- package/src/nodes/digest-moments/recompute.js +14 -0
- package/src/nodes/digest-moments/reset.js +14 -0
- package/src/nodes/digest-moments/update.js +120 -0
- package/src/nodes/dwell-time-tracker/index.js +14 -0
- package/src/nodes/dwell-time-tracker/init.js +59 -0
- package/src/nodes/dwell-time-tracker/introspect.js +137 -0
- package/src/nodes/dwell-time-tracker/publish-to.js +22 -0
- package/src/nodes/dwell-time-tracker/recompute.js +8 -0
- package/src/nodes/dwell-time-tracker/reset.js +23 -0
- package/src/nodes/dwell-time-tracker/update.js +103 -0
- package/src/nodes/emit-if/helpers.js +94 -0
- package/src/nodes/emit-if/index.js +16 -0
- package/src/nodes/emit-if/init.js +67 -0
- package/src/nodes/emit-if/introspect.js +102 -0
- package/src/nodes/emit-if/publish-to.js +13 -0
- package/src/nodes/emit-if/recompute.js +12 -0
- package/src/nodes/emit-if/reset.js +27 -0
- package/src/nodes/emit-if/update.js +93 -0
- package/src/nodes/es-correlation/index.js +19 -0
- package/src/nodes/es-correlation/init.js +73 -0
- package/src/nodes/es-correlation/introspect.js +176 -0
- package/src/nodes/es-correlation/publish-to.js +32 -0
- package/src/nodes/es-correlation/recompute.js +56 -0
- package/src/nodes/es-correlation/reset.js +27 -0
- package/src/nodes/es-correlation/update.js +110 -0
- package/src/nodes/es-mean/index.js +20 -0
- package/src/nodes/es-mean/init.js +58 -0
- package/src/nodes/es-mean/introspect.js +129 -0
- package/src/nodes/es-mean/publish-to.js +19 -0
- package/src/nodes/es-mean/recompute.js +16 -0
- package/src/nodes/es-mean/reset.js +24 -0
- package/src/nodes/es-mean/update.js +69 -0
- package/src/nodes/es-pairwise-correlation/index.js +19 -0
- package/src/nodes/es-pairwise-correlation/init.js +98 -0
- package/src/nodes/es-pairwise-correlation/introspect.js +187 -0
- package/src/nodes/es-pairwise-correlation/publish-to.js +31 -0
- package/src/nodes/es-pairwise-correlation/recompute.js +69 -0
- package/src/nodes/es-pairwise-correlation/reset.js +29 -0
- package/src/nodes/es-pairwise-correlation/update.js +89 -0
- package/src/nodes/es-stats/index.js +21 -0
- package/src/nodes/es-stats/init.js +105 -0
- package/src/nodes/es-stats/introspect.js +157 -0
- package/src/nodes/es-stats/publish-to.js +42 -0
- package/src/nodes/es-stats/recompute.js +74 -0
- package/src/nodes/es-stats/reset.js +49 -0
- package/src/nodes/es-stats/update-envelope.js +44 -0
- package/src/nodes/es-stats/update-welford.js +64 -0
- package/src/nodes/es-stats/update.js +59 -0
- package/src/nodes/index.js +83 -0
- package/src/nodes/invert-flag/index.js +20 -0
- package/src/nodes/invert-flag/init.js +36 -0
- package/src/nodes/invert-flag/introspect.js +115 -0
- package/src/nodes/invert-flag/publish-to.js +23 -0
- package/src/nodes/invert-flag/recompute.js +12 -0
- package/src/nodes/invert-flag/reset.js +12 -0
- package/src/nodes/invert-flag/update.js +32 -0
- package/src/nodes/kalman1d/index.js +20 -0
- package/src/nodes/kalman1d/init.js +103 -0
- package/src/nodes/kalman1d/introspect.js +250 -0
- package/src/nodes/kalman1d/publish-to.js +34 -0
- package/src/nodes/kalman1d/recompute.js +23 -0
- package/src/nodes/kalman1d/reset.js +29 -0
- package/src/nodes/kalman1d/update.js +144 -0
- package/src/nodes/kernel/index.js +18 -0
- package/src/nodes/kernel/init.js +66 -0
- package/src/nodes/kernel/introspect.js +163 -0
- package/src/nodes/kernel/presets.js +74 -0
- package/src/nodes/kernel/publish-to.js +25 -0
- package/src/nodes/kernel/recompute.js +14 -0
- package/src/nodes/kernel/reset.js +20 -0
- package/src/nodes/kernel/update.js +69 -0
- package/src/nodes/lag/index.js +21 -0
- package/src/nodes/lag/init.js +87 -0
- package/src/nodes/lag/introspect.js +202 -0
- package/src/nodes/lag/publish-to.js +64 -0
- package/src/nodes/lag/recompute.js +33 -0
- package/src/nodes/lag/reset.js +49 -0
- package/src/nodes/lag/update.js +133 -0
- package/src/nodes/median3/index.js +19 -0
- package/src/nodes/median3/init.js +40 -0
- package/src/nodes/median3/introspect.js +116 -0
- package/src/nodes/median3/publish-to.js +21 -0
- package/src/nodes/median3/recompute.js +12 -0
- package/src/nodes/median3/reset.js +15 -0
- package/src/nodes/median3/update.js +58 -0
- package/src/nodes/moments-digest/copy-from.js +12 -0
- package/src/nodes/moments-digest/flush.js +17 -0
- package/src/nodes/moments-digest/index.js +132 -0
- package/src/nodes/moments-digest/init.js +84 -0
- package/src/nodes/moments-digest/introspect.js +122 -0
- package/src/nodes/moments-digest/prelude.js +28 -0
- package/src/nodes/moments-digest/publish-to.js +31 -0
- package/src/nodes/moments-digest/recompute.js +9 -0
- package/src/nodes/moments-digest/reset.js +20 -0
- package/src/nodes/moments-digest/update.js +182 -0
- package/src/nodes/node-names.js +67 -0
- package/src/nodes/page-hinkley/index.js +20 -0
- package/src/nodes/page-hinkley/init.js +66 -0
- package/src/nodes/page-hinkley/introspect.js +193 -0
- package/src/nodes/page-hinkley/publish-to.js +28 -0
- package/src/nodes/page-hinkley/recompute.js +14 -0
- package/src/nodes/page-hinkley/reset.js +23 -0
- package/src/nodes/page-hinkley/update.js +105 -0
- package/src/nodes/pass-if/index.js +21 -0
- package/src/nodes/pass-if/init.js +37 -0
- package/src/nodes/pass-if/introspect.js +93 -0
- package/src/nodes/pass-if/publish-to.js +15 -0
- package/src/nodes/pass-if/recompute.js +15 -0
- package/src/nodes/pass-if/reset.js +18 -0
- package/src/nodes/pass-if/update.js +37 -0
- package/src/nodes/persist-if/helpers.js +111 -0
- package/src/nodes/persist-if/index.js +9 -0
- package/src/nodes/persist-if/init.js +98 -0
- package/src/nodes/persist-if/introspect.js +106 -0
- package/src/nodes/persist-if/publish-to.js +21 -0
- package/src/nodes/persist-if/recompute.js +20 -0
- package/src/nodes/persist-if/reset.js +35 -0
- package/src/nodes/persist-if/update.js +112 -0
- package/src/nodes/persistence-check/index.js +14 -0
- package/src/nodes/persistence-check/init.js +38 -0
- package/src/nodes/persistence-check/introspect.js +144 -0
- package/src/nodes/persistence-check/publish-to.js +11 -0
- package/src/nodes/persistence-check/recompute.js +7 -0
- package/src/nodes/persistence-check/reset.js +12 -0
- package/src/nodes/persistence-check/update.js +57 -0
- package/src/nodes/process-index/index.js +15 -0
- package/src/nodes/process-index/init.js +48 -0
- package/src/nodes/process-index/introspect.js +182 -0
- package/src/nodes/process-index/publish-to.js +29 -0
- package/src/nodes/process-index/recompute.js +8 -0
- package/src/nodes/process-index/reset.js +8 -0
- package/src/nodes/process-index/update.js +75 -0
- package/src/nodes/ratio/index.js +14 -0
- package/src/nodes/ratio/init.js +37 -0
- package/src/nodes/ratio/introspect.js +155 -0
- package/src/nodes/ratio/publish-to.js +15 -0
- package/src/nodes/ratio/recompute.js +7 -0
- package/src/nodes/ratio/reset.js +7 -0
- package/src/nodes/ratio/update.js +47 -0
- package/src/nodes/sanitize/index.js +82 -0
- package/src/nodes/sanitize/init.js +82 -0
- package/src/nodes/sanitize/introspect.js +194 -0
- package/src/nodes/sanitize/publish-to.js +15 -0
- package/src/nodes/sanitize/recompute.js +8 -0
- package/src/nodes/sanitize/reset.js +14 -0
- package/src/nodes/sanitize/update.js +102 -0
- package/src/nodes/spike-guard/index.js +14 -0
- package/src/nodes/spike-guard/init.js +47 -0
- package/src/nodes/spike-guard/introspect.js +130 -0
- package/src/nodes/spike-guard/publish-to.js +35 -0
- package/src/nodes/spike-guard/recompute.js +14 -0
- package/src/nodes/spike-guard/reset.js +19 -0
- package/src/nodes/spike-guard/update.js +89 -0
- package/src/nodes/sse-emitter/index.js +7 -0
- package/src/nodes/sse-emitter/init.js +105 -0
- package/src/nodes/sse-emitter/introspect.js +134 -0
- package/src/nodes/sse-emitter/publish-to.js +6 -0
- package/src/nodes/sse-emitter/recompute.js +16 -0
- package/src/nodes/sse-emitter/reset.js +19 -0
- package/src/nodes/sse-emitter/update.js +69 -0
- package/src/nodes/sse-emitter/utils/broadcast-heartbeat.js +36 -0
- package/src/nodes/sse-emitter/utils/build-message.js +31 -0
- package/src/nodes/sse-emitter/utils/connection-manager.js +45 -0
- package/src/nodes/sse-emitter/utils/endpoint-initializer.js +20 -0
- package/src/nodes/sse-emitter/utils/health-monitor.js +38 -0
- package/src/nodes/sse-emitter/utils/qos-handler.js +41 -0
- package/src/nodes/state-change-detector/helpers.js +35 -0
- package/src/nodes/state-change-detector/index.js +12 -0
- package/src/nodes/state-change-detector/init.js +56 -0
- package/src/nodes/state-change-detector/introspect.js +146 -0
- package/src/nodes/state-change-detector/publish-to.js +20 -0
- package/src/nodes/state-change-detector/recompute.js +6 -0
- package/src/nodes/state-change-detector/reset.js +20 -0
- package/src/nodes/state-change-detector/update.js +97 -0
- package/src/nodes/sw-stats/index.js +14 -0
- package/src/nodes/sw-stats/init.js +51 -0
- package/src/nodes/sw-stats/introspect.js +131 -0
- package/src/nodes/sw-stats/publish-to.js +45 -0
- package/src/nodes/sw-stats/recompute.js +33 -0
- package/src/nodes/sw-stats/reset.js +15 -0
- package/src/nodes/sw-stats/update.js +39 -0
- package/src/nodes/swing-watch/index.js +17 -0
- package/src/nodes/swing-watch/init.js +144 -0
- package/src/nodes/swing-watch/introspect.js +188 -0
- package/src/nodes/swing-watch/publish-to.js +53 -0
- package/src/nodes/swing-watch/recompute.js +22 -0
- package/src/nodes/swing-watch/reset.js +55 -0
- package/src/nodes/swing-watch/update.js +767 -0
- package/src/nodes/tally/index.js +15 -0
- package/src/nodes/tally/init.js +32 -0
- package/src/nodes/tally/introspect.js +116 -0
- package/src/nodes/tally/publish-to.js +28 -0
- package/src/nodes/tally/recompute.js +10 -0
- package/src/nodes/tally/reset.js +10 -0
- package/src/nodes/tally/update.js +44 -0
- package/src/nodes/threshold/index.js +14 -0
- package/src/nodes/threshold/init.js +121 -0
- package/src/nodes/threshold/introspect.js +192 -0
- package/src/nodes/threshold/publish-to.js +11 -0
- package/src/nodes/threshold/recompute.js +8 -0
- package/src/nodes/threshold/reset.js +14 -0
- package/src/nodes/threshold/update.js +54 -0
- package/src/nodes/transform/helpers.js +39 -0
- package/src/nodes/transform/index.js +15 -0
- package/src/nodes/transform/init.js +35 -0
- package/src/nodes/transform/introspect.js +118 -0
- package/src/nodes/transform/publish-to.js +23 -0
- package/src/nodes/transform/recompute.js +12 -0
- package/src/nodes/transform/reset.js +11 -0
- package/src/nodes/transform/update.js +32 -0
- package/src/nodes/trend/compute-confidence.js +112 -0
- package/src/nodes/trend/index.js +14 -0
- package/src/nodes/trend/init.js +81 -0
- package/src/nodes/trend/introspect.js +151 -0
- package/src/nodes/trend/publish-to.js +22 -0
- package/src/nodes/trend/recompute.js +15 -0
- package/src/nodes/trend/reset.js +26 -0
- package/src/nodes/trend/update.js +93 -0
- package/src/nodes/tw-stats/copy-from.js +16 -0
- package/src/nodes/tw-stats/flush.js +12 -0
- package/src/nodes/tw-stats/index.js +41 -0
- package/src/nodes/tw-stats/init.js +92 -0
- package/src/nodes/tw-stats/introspect.js +171 -0
- package/src/nodes/tw-stats/publish-to.js +108 -0
- package/src/nodes/tw-stats/recompute.js +9 -0
- package/src/nodes/tw-stats/reset.js +16 -0
- package/src/nodes/tw-stats/update.js +113 -0
- package/src/nodes/unbalance/index.js +15 -0
- package/src/nodes/unbalance/init.js +59 -0
- package/src/nodes/unbalance/introspect.js +167 -0
- package/src/nodes/unbalance/publish-to.js +52 -0
- package/src/nodes/unbalance/recompute.js +11 -0
- package/src/nodes/unbalance/reset.js +10 -0
- package/src/nodes/unbalance/update.js +83 -0
- package/src/nodes/vector-distance/index.js +14 -0
- package/src/nodes/vector-distance/init.js +54 -0
- package/src/nodes/vector-distance/introspect.js +149 -0
- package/src/nodes/vector-distance/publish-to.js +35 -0
- package/src/nodes/vector-distance/recompute.js +11 -0
- package/src/nodes/vector-distance/reset.js +20 -0
- package/src/nodes/vector-distance/update.js +139 -0
- package/src/nodes/winnow/index.js +12 -0
- package/src/nodes/winnow/init.js +82 -0
- package/src/nodes/winnow/introspect.js +253 -0
- package/src/nodes/winnow/publish-to.js +51 -0
- package/src/nodes/winnow/recompute.js +14 -0
- package/src/nodes/winnow/reset.js +34 -0
- package/src/nodes/winnow/update.js +203 -0
- package/src/tools/training/features.js +227 -0
- package/src/tools/training/index.js +21 -0
- package/src/tools/training/logistic.js +238 -0
- package/src/tools/training/metrics.js +126 -0
- package/src/tools/training/scale.js +239 -0
- package/src/tools/training/validate.js +103 -0
- package/src/windowing/count-sliding/create.js +16 -0
- package/src/windowing/count-sliding/get-reverse-chronological-index.js +19 -0
- package/src/windowing/count-sliding/index.js +6 -0
- package/src/windowing/count-sliding/is-not-full.js +5 -0
- package/src/windowing/count-sliding/push.js +15 -0
- package/src/windowing/count-sliding/reset.js +11 -0
- package/src/windowing/count-sliding/size.js +5 -0
- package/src/windowing/time-sliding/create.js +25 -0
- package/src/windowing/time-sliding/push.js +42 -0
- package/CODE_OF_CONDUCT.md +0 -46
- package/CONTRIBUTING.md +0 -132
- package/docs/.nojekyll +0 -0
- package/docs/CONTRIBUTING.md +0 -134
- package/docs/README.md +0 -132
- package/docs/_coverpage.md +0 -26
- package/docs/_media/bg.png +0 -0
- package/docs/_media/wink-logo.png +0 -0
- package/docs/_navbar.md +0 -14
- package/docs/_sidebar.md +0 -2
- package/docs/index.html +0 -44
- package/eslint.config.js +0 -283
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// nodes/moments-digest/index.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview
|
|
5
|
+
* Moments Digest — numerically stable, windowed M1..M4 with min/max, designed for
|
|
6
|
+
* arbitrary-depth cascades (fan-in = 1 per chain) using a single shared field space.
|
|
7
|
+
*
|
|
8
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
9
|
+
* TL;DR
|
|
10
|
+
* • Consumes raw samples `msg[ x ]` and produces per-window statistics:
|
|
11
|
+
* `x_n, x_M1, x_M2, x_M3, x_M4, x_min, x_max` plus a boolean `msg[ name ] = true`.
|
|
12
|
+
* • Central moments are carried as unnormalized sums (Pébay 2008):
|
|
13
|
+
* M2 = Σ (x−μ)², M3 = Σ (x−μ)³, M4 = Σ (x−μ)⁴. Compute variance/kurtosis downstream.
|
|
14
|
+
* • Supports arbitrary cascade depth: root and all downstream levels read/write the SAME `x_*` keys.
|
|
15
|
+
* • Publishes only on **window completion** or **flush**; on all other ticks it **scrubs** `x_*`
|
|
16
|
+
* to `undefined` so downstream nodes cannot accidentally ingest partials.
|
|
17
|
+
* • Uniform flush key: `x_flush`. **Only the root sets it;** cascades react to it.
|
|
18
|
+
*
|
|
19
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
* Execution model (per message)
|
|
21
|
+
* The node is split into three conceptual phases. All state mutation happens in
|
|
22
|
+
* `update()`; `publishTo()` is a pure renderer of what `update()` planned.
|
|
23
|
+
*
|
|
24
|
+
* 1) Prelude (top of update)
|
|
25
|
+
* • Clear last tick's publish plan: `planPublish = false`, `propagateFlush = false`.
|
|
26
|
+
* • ROOT ONLY: If flush is latched:
|
|
27
|
+
* – If `n > 0`: **copy → reset → planPublish** (publish the snapshot this tick).
|
|
28
|
+
* Root sets `propagateFlush = true` so `publishTo()` will emit `x_flush`.
|
|
29
|
+
* – If `n === 0`: **signal-only flush**. Root sets `propagateFlush = true`,
|
|
30
|
+
* but does not plan a stats publish (no data to emit).
|
|
31
|
+
* • CASCADE: Does NOT handle flush in prelude (deferred to after ingestion).
|
|
32
|
+
*
|
|
33
|
+
* 2) Core update
|
|
34
|
+
* • Raw level: read `msg[ x ]`; if non-finite → mark input invalid and return (no plan).
|
|
35
|
+
* Otherwise, update moments and counters with the new sample.
|
|
36
|
+
* • Cascade level:
|
|
37
|
+
* – **Always merge when** `msg[ x_n ] !== undefined` (ingest parent's moments).
|
|
38
|
+
* – **After ingestion**, if `msg[ x_flush ] === true` and `n > 0`:
|
|
39
|
+
* **copy → reset → planPublish** (ensures cascade includes parent's flush data).
|
|
40
|
+
*
|
|
41
|
+
* 3) Epilogue (end of update)
|
|
42
|
+
* • If the window just completed (and no flush snapshot was already planned, and input is valid):
|
|
43
|
+
* – **copy → reset → planPublish** (publish the completed window this tick).
|
|
44
|
+
*
|
|
45
|
+
* 4) publishTo (pure render; no state mutation)
|
|
46
|
+
* • If `propagateFlush === true` and this is the root: set `msg[ x_flush ] = true`.
|
|
47
|
+
* • If `planPublish === true`: write all `x_*` fields **from the snapshot** and set `msg[ name ] = true`.
|
|
48
|
+
* • Otherwise (non-publish tick): scrub `x_*` to `undefined`. Never touch `x_flush`.
|
|
49
|
+
*
|
|
50
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
51
|
+
* "Before/After" semantics (UX via placement)
|
|
52
|
+
* • Controller **upstream of the root** ("BEFORE"):
|
|
53
|
+
* – Flush latch is seen in Prelude on the **same** message → snapshot/reset now.
|
|
54
|
+
* The current message becomes the **first sample of the next window**.
|
|
55
|
+
* Root publishes the snapshot and emits `x_flush`; cascades see `x_flush`,
|
|
56
|
+
* ingest parent's moments FIRST, then publish their accumulated state and reset.
|
|
57
|
+
* • Controller **downstream of the root** ("AFTER"):
|
|
58
|
+
* – Flush latch is seen on the **next** message. The previous message is included
|
|
59
|
+
* in the snapshot that publishes next tick. (If no next message arrives, nothing
|
|
60
|
+
* triggers the deferred publish — controller may emit a final synthetic tick if needed.)
|
|
61
|
+
*
|
|
62
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
63
|
+
* Inputs
|
|
64
|
+
* • from.x : string (required) → base field name, e.g., 'temperature'.
|
|
65
|
+
* • windowSize : number (required) → samples per window at this level (≥ 4).
|
|
66
|
+
* • name : string (optional) → node identifier; sets `msg[ name ] = true` on publish.
|
|
67
|
+
* • cascade : boolean (optional) → false for raw level, true for cascade level.
|
|
68
|
+
*
|
|
69
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
70
|
+
* Outputs (on publish)
|
|
71
|
+
* • msg[ x + '_n' ] : number
|
|
72
|
+
* • msg[ x + '_M1' ] : number (mean)
|
|
73
|
+
* • msg[ x + '_M2' ] : number (Σ centered squares)
|
|
74
|
+
* • msg[ x + '_M3' ] : number (Σ centered cubes)
|
|
75
|
+
* • msg[ x + '_M4' ] : number (Σ centered fourth powers)
|
|
76
|
+
* • msg[ x + '_min' ] : number
|
|
77
|
+
* • msg[ x + '_max' ] : number
|
|
78
|
+
* • msg[ name ] : true (flag marking "this node published now")
|
|
79
|
+
* • msg[ x + '_flush' ] : true (root only, on flush ticks)
|
|
80
|
+
*
|
|
81
|
+
* On non-publish cycles, **all** `x_*` fields are set to `undefined` (keys kept, never deleted).
|
|
82
|
+
*
|
|
83
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
84
|
+
* Flush semantics (action + state)
|
|
85
|
+
* • Root:
|
|
86
|
+
* – A flush request latches a boundary. In Prelude the node **copies → resets → plans**
|
|
87
|
+
* a publish from the snapshot (if `n > 0`) and will **emit `x_flush`** at publish time.
|
|
88
|
+
* – If `n === 0`, it is a **signal-only** flush: emit `x_flush` without stats.
|
|
89
|
+
* • Cascades:
|
|
90
|
+
* – Never set/clear `x_flush`. They **observe** `x_flush` and publish accumulated state.
|
|
91
|
+
* – Critical: cascades ingest parent's moments BEFORE handling flush, ensuring
|
|
92
|
+
* parent's flush data is included in cascade's flush output (no data loss).
|
|
93
|
+
*
|
|
94
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
95
|
+
* NaN / invalid input handling
|
|
96
|
+
* • Raw level:
|
|
97
|
+
* – A non-finite current input suppresses planning of a **window** publish.
|
|
98
|
+
* – A **flush snapshot planned in Prelude** still publishes this tick; `publishTo()`
|
|
99
|
+
* does not early-bail on invalid input. This preserves the boundary and keeps
|
|
100
|
+
* cascades aligned (root still emits `x_flush`).
|
|
101
|
+
* • Cascade level:
|
|
102
|
+
* – No raw input is read. The node merges only when `x_*` fields are present.
|
|
103
|
+
*
|
|
104
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
* Invariants
|
|
106
|
+
* • Uniform flush key across the chain: `x_flush` (root only writes).
|
|
107
|
+
* • No data loss: cascades always ingest before responding to flush.
|
|
108
|
+
* • Scrub on every non-publish tick: `x_*` → `undefined`.
|
|
109
|
+
* • Publish is **snapshot-only**; `publishTo()` never mutates node state.
|
|
110
|
+
* • O(1) time and O(1) memory per message; no hot-path allocations (snapshot object is reused).
|
|
111
|
+
*
|
|
112
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
113
|
+
* References
|
|
114
|
+
* [1] Pébay, P. (2008). Formulas for robust, one-pass parallel computation of covariances
|
|
115
|
+
* and arbitrary-order statistical moments. Sandia Report SAND2008-6212.
|
|
116
|
+
* [2] Chan, T. F., Golub, G. H., & LeVeque, R. J. (1983). Algorithms for computing the
|
|
117
|
+
* sample variance: Analysis and recommendation. The American Statistician, 37(3), 242-247.
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
export { default as init } from './init.js';
|
|
121
|
+
export { default as update } from './update.js';
|
|
122
|
+
export { default as reset } from './reset.js';
|
|
123
|
+
export { default as recompute } from './recompute.js';
|
|
124
|
+
export { default as publishTo } from './publish-to.js';
|
|
125
|
+
export { default as flush } from './flush.js';
|
|
126
|
+
// Direct imports for common control functions
|
|
127
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
128
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
129
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
130
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
131
|
+
// Re-export everything from introspection.js
|
|
132
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// nodes/moments-digest/init.js
|
|
2
|
+
|
|
3
|
+
import { validateSpec } from '../../core/utils/node/index.js';
|
|
4
|
+
import * as introspect from './introspect.js';
|
|
5
|
+
import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
|
|
6
|
+
|
|
7
|
+
const init = function ( spec ) {
|
|
8
|
+
validateSpec( spec, introspect );
|
|
9
|
+
|
|
10
|
+
// Create state after validation passes
|
|
11
|
+
const state = Object.create( null );
|
|
12
|
+
// Standard input parameter health flag during update: false means healthy
|
|
13
|
+
state.inputValidationFailed = false;
|
|
14
|
+
// Ensure node is enabled by default
|
|
15
|
+
state.disable = false;
|
|
16
|
+
state.pause = false;
|
|
17
|
+
// Preserve node's name
|
|
18
|
+
state.name = spec.name;
|
|
19
|
+
|
|
20
|
+
// Extract configuration
|
|
21
|
+
state.x = spec.from.x;
|
|
22
|
+
|
|
23
|
+
// Apply defaults from introspect (validation doesn't enforce them)
|
|
24
|
+
// Supports both direct and field-keyed specification
|
|
25
|
+
const windowSizeSpec = resolveScalar( spec.windowSize, state.x );
|
|
26
|
+
state.windowSize = windowSizeSpec ?? introspect.DEFAULT_OPTIONS.windowSize;
|
|
27
|
+
|
|
28
|
+
// Determine mode: raw or cascade
|
|
29
|
+
state.isCascading = spec.cascade || introspect.DEFAULT_OPTIONS.cascade;
|
|
30
|
+
|
|
31
|
+
// Extract base field name
|
|
32
|
+
state.baseField = state.x;
|
|
33
|
+
|
|
34
|
+
// Pre-compute ALL field names for hot path (no string concat in update!)
|
|
35
|
+
// Input names are same output names in the case of cascade
|
|
36
|
+
state.fieldMap = Object.create( null );
|
|
37
|
+
state.fieldMap.n = state.baseField + '_n';
|
|
38
|
+
state.fieldMap.M1 = state.baseField + '_M1';
|
|
39
|
+
state.fieldMap.M2 = state.baseField + '_M2';
|
|
40
|
+
state.fieldMap.M3 = state.baseField + '_M3';
|
|
41
|
+
state.fieldMap.M4 = state.baseField + '_M4';
|
|
42
|
+
state.fieldMap.min = state.baseField + '_min';
|
|
43
|
+
state.fieldMap.max = state.baseField + '_max';
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// Pébay algorithm state
|
|
47
|
+
state.n = 0;
|
|
48
|
+
state.M1 = 0;
|
|
49
|
+
state.M2 = 0;
|
|
50
|
+
state.M3 = 0;
|
|
51
|
+
state.M4 = 0;
|
|
52
|
+
state.min = Infinity;
|
|
53
|
+
state.max = -Infinity;
|
|
54
|
+
// Signal for digest completion.
|
|
55
|
+
state[ state.name ] = false;
|
|
56
|
+
// This is used to signal downstream nodes to flush as the primary control
|
|
57
|
+
// path works only for the root (i.e. the first or the non-cascade one) node
|
|
58
|
+
// in the chain. Control signalling for cascaded nodes is disabled as the
|
|
59
|
+
// chain sequence for flush operation needs to be respected by the downstream
|
|
60
|
+
// nodes. This flag is set to true by the root node on flush event and is used
|
|
61
|
+
// subsequently by the cascaded downstream nodes to flush accordingly. BUT
|
|
62
|
+
// we need to create the **key** irrespective of being root or cascade.
|
|
63
|
+
state.flushSignalKey = state.x + '_flush'; // e.g. 'temperature_flush'
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
// Window management
|
|
67
|
+
state.currentCount = 0;
|
|
68
|
+
state.windowComplete = false;
|
|
69
|
+
|
|
70
|
+
// Set by flush method
|
|
71
|
+
state.flushLatched = false;
|
|
72
|
+
// Publish stats (from snapshot) this tick
|
|
73
|
+
state.planPublish = false;
|
|
74
|
+
// Root should emit x_flush this tick
|
|
75
|
+
state.propagateFlush = false;
|
|
76
|
+
// Always copy moments here on window completion or flush event
|
|
77
|
+
state.snapshot = Object.create( null );
|
|
78
|
+
|
|
79
|
+
state.nodeType = introspect.getNodeType();
|
|
80
|
+
|
|
81
|
+
return state;
|
|
82
|
+
}; // init()
|
|
83
|
+
|
|
84
|
+
export default init;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// nodes/moments-digest/introspect.js
|
|
2
|
+
|
|
3
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
4
|
+
|
|
5
|
+
/** Single source of truth for what this plugin can do */
|
|
6
|
+
const SUPPORTED_STATS = [
|
|
7
|
+
'n', // Sample count
|
|
8
|
+
'M1', // Mean (first moment)
|
|
9
|
+
'M2', // Second central moment
|
|
10
|
+
'M3', // Third central moment
|
|
11
|
+
'M4', // Fourth central moment
|
|
12
|
+
'min', // Minimum value
|
|
13
|
+
'max' // Maximum value
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/** Human-readable descriptions (for introspection) */
|
|
17
|
+
const STAT_DESCRIPTIONS = {
|
|
18
|
+
n: 'Number of samples in the aggregated window',
|
|
19
|
+
M1: 'Arithmetic mean of values in the window',
|
|
20
|
+
M2: 'Second central moment (sum of squared deviations from mean)',
|
|
21
|
+
M3: 'Third central moment (sum of cubed deviations from mean)',
|
|
22
|
+
M4: 'Fourth central moment (sum of quartic deviations from mean)',
|
|
23
|
+
min: 'Minimum value across all samples',
|
|
24
|
+
max: 'Maximum value across all samples'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Control methods available for triggers */
|
|
28
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
29
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
30
|
+
enable: 'Enables node processing',
|
|
31
|
+
disable: 'Disables node processing',
|
|
32
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
33
|
+
unpause: 'Resumes node processing after pause',
|
|
34
|
+
flush: 'Forces immediate output of partial window'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Node capabilities for documentation */
|
|
38
|
+
const CAPABILITIES = {
|
|
39
|
+
description: 'High-performance streaming aggregation using Pébay\'s numerically stable algorithm for statistical moments',
|
|
40
|
+
features: [
|
|
41
|
+
'Configurable window-based aggregation (4-1024 samples)',
|
|
42
|
+
'Numerically stable incremental mean and moment computation',
|
|
43
|
+
'Cascade mode for multi-level aggregation (seconds → minutes → hours)',
|
|
44
|
+
'Automatic mean and moment combining for cascaded windows',
|
|
45
|
+
'Window completion signaling via msg[nodeName] = true',
|
|
46
|
+
'Intelligent message filtering in cascade mode (processes only moment messages)',
|
|
47
|
+
'NaN resilience - skips invalid samples without corrupting statistics',
|
|
48
|
+
'Zero allocations in hot path for maximum throughput',
|
|
49
|
+
'Flush support for graceful shutdown with incomplete windows'
|
|
50
|
+
]
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** The type of this node */
|
|
54
|
+
const NODE_TYPE = 'Moments Digest';
|
|
55
|
+
|
|
56
|
+
// Attach static metadata once, from our constants by making a shallow copy.
|
|
57
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
58
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
59
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
60
|
+
export const getNodeType = () => NODE_TYPE;
|
|
61
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
62
|
+
|
|
63
|
+
export const DEFAULT_OPTIONS = {
|
|
64
|
+
windowSize: 100,
|
|
65
|
+
cascade: false
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// DSL Metadata for transpilation
|
|
69
|
+
const DSL_METADATA = {
|
|
70
|
+
// Spec schema for validation
|
|
71
|
+
specSchema: {
|
|
72
|
+
nodeType: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
required: true,
|
|
75
|
+
value: NODE_TYPE
|
|
76
|
+
},
|
|
77
|
+
name: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
required: true,
|
|
80
|
+
validator: validators.identifier,
|
|
81
|
+
error: 'Name must be a valid identifier'
|
|
82
|
+
},
|
|
83
|
+
from: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
required: true,
|
|
86
|
+
properties: {
|
|
87
|
+
x: {
|
|
88
|
+
type: 'string',
|
|
89
|
+
required: true,
|
|
90
|
+
validator: validators.noSpaces,
|
|
91
|
+
error: 'Field name cannot contain spaces'
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
windowSize: {
|
|
96
|
+
type: 'numberOrFieldKeyed',
|
|
97
|
+
required: false,
|
|
98
|
+
default: DEFAULT_OPTIONS.windowSize,
|
|
99
|
+
integer: true,
|
|
100
|
+
min: 4,
|
|
101
|
+
max: 1024,
|
|
102
|
+
validator: validators.positiveInteger,
|
|
103
|
+
error: 'Window size must be a positive integer'
|
|
104
|
+
},
|
|
105
|
+
cascade: {
|
|
106
|
+
type: 'boolean',
|
|
107
|
+
required: false,
|
|
108
|
+
default: DEFAULT_OPTIONS.cascade
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// How to build the spec from DSL params
|
|
113
|
+
buildSpec: ( name, x, options ) => ( {
|
|
114
|
+
nodeType: NODE_TYPE,
|
|
115
|
+
name,
|
|
116
|
+
from: { x },
|
|
117
|
+
...options
|
|
118
|
+
} ),
|
|
119
|
+
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// nodes/moments-digest/prelude.js
|
|
2
|
+
|
|
3
|
+
import { copyFrom } from './copy-from.js';
|
|
4
|
+
import reset from './reset.js';
|
|
5
|
+
|
|
6
|
+
export const prelude = function ( state, msg ) {
|
|
7
|
+
// Clear last tick’s plan
|
|
8
|
+
state.planPublish = false;
|
|
9
|
+
state.propagateFlush = false;
|
|
10
|
+
|
|
11
|
+
if ( !state.flushLatched && !msg[ state.flushSignalKey ] ) return;
|
|
12
|
+
// Either `state.flushLatched` or `msg[ state.flushSignalKey ]` is true
|
|
13
|
+
// implies that it is time to copy and plan a publish.
|
|
14
|
+
|
|
15
|
+
// Only ROOT nodes handle flush in prelude
|
|
16
|
+
if ( !state.isCascading ) {
|
|
17
|
+
state.propagateFlush = true;
|
|
18
|
+
|
|
19
|
+
if ( state.n > 0 ) {
|
|
20
|
+
copyFrom( state, state.snapshot ); // copy previous window
|
|
21
|
+
state.planPublish = true; // we will publish stats this tick
|
|
22
|
+
reset( state ); // exclude current message
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Otherwise: signal-only flush (no stats) via planPublish === false
|
|
26
|
+
|
|
27
|
+
state.flushLatched = false; // consume latch
|
|
28
|
+
}; // prelude()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// nodes/moments-digest/publish-to.js
|
|
2
|
+
|
|
3
|
+
const publishTo = function ( state, msg ) {
|
|
4
|
+
if ( state.disable ) return;
|
|
5
|
+
// Do not Skip publishing if we encountered NaN in this update cycle
|
|
6
|
+
// `update()` already suppresses planning on invalid samples;
|
|
7
|
+
// a flush snapshot planned in `prelude()` must still render.
|
|
8
|
+
const f = state.fieldMap;
|
|
9
|
+
|
|
10
|
+
if ( state.propagateFlush && ( !state.isCascading ) ) {
|
|
11
|
+
msg[ state.flushSignalKey ] = true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if ( state.planPublish ) {
|
|
15
|
+
const s = state.snapshot;
|
|
16
|
+
msg[ f.n ] = s.n;
|
|
17
|
+
msg[ f.M1 ] = s.M1;
|
|
18
|
+
msg[ f.M2 ] = s.M2;
|
|
19
|
+
msg[ f.M3 ] = s.M3;
|
|
20
|
+
msg[ f.M4 ] = s.M4;
|
|
21
|
+
msg[ f.min ] = s.min;
|
|
22
|
+
msg[ f.max ] = s.max;
|
|
23
|
+
msg[ state.name ] = true;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// signal-only or no publish → scrub
|
|
28
|
+
msg[ f.n ] = msg[ f.M1 ] = msg[ f.M2 ] = msg[ f.M3 ] = msg[ f.M4 ] = msg[ f.min ] = msg[ f.max ] = undefined;
|
|
29
|
+
}; // publishTo()
|
|
30
|
+
|
|
31
|
+
export default publishTo;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// nodes/moments-digest/reset.js
|
|
2
|
+
|
|
3
|
+
const reset = function ( state ) {
|
|
4
|
+
// Reset all statistical state
|
|
5
|
+
state.n = 0;
|
|
6
|
+
state.M1 = 0;
|
|
7
|
+
state.M2 = 0;
|
|
8
|
+
state.M3 = 0;
|
|
9
|
+
state.M4 = 0;
|
|
10
|
+
state.min = Infinity;
|
|
11
|
+
state.max = -Infinity;
|
|
12
|
+
|
|
13
|
+
// Reset window management
|
|
14
|
+
state.currentCount = 0;
|
|
15
|
+
state.windowComplete = false;
|
|
16
|
+
|
|
17
|
+
return true;
|
|
18
|
+
}; // reset()
|
|
19
|
+
|
|
20
|
+
export default reset;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// nodes/moments-digest/update.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview
|
|
5
|
+
* Moments Digest Update — Incremental moment accumulation with flush synchronization
|
|
6
|
+
*
|
|
7
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
* Core Processing Logic
|
|
9
|
+
* • Raw nodes: Process individual samples using Pébay's algorithm
|
|
10
|
+
* • Cascade nodes: Merge moment sets from parent digests
|
|
11
|
+
* • Window completion: Both node types publish on window completion
|
|
12
|
+
* • Flush override: Immediate publish of partial windows
|
|
13
|
+
*
|
|
14
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
* Flush Synchronization (Critical for Data Integrity)
|
|
16
|
+
*
|
|
17
|
+
* ROOT NODES:
|
|
18
|
+
* • Handle flush in prelude (snapshot → reset → plan)
|
|
19
|
+
* • Controller placement determines current message inclusion:
|
|
20
|
+
* - BEFORE: Exclude current (it starts next window)
|
|
21
|
+
* - AFTER: Include current (it's in the snapshot)
|
|
22
|
+
* • Propagate x_flush signal to downstream cascades
|
|
23
|
+
*
|
|
24
|
+
* CASCADE NODES:
|
|
25
|
+
* • Never snapshot/reset in prelude (only root does)
|
|
26
|
+
* • Process parent's moments FIRST (if present)
|
|
27
|
+
* • THEN check for flush signal to publish accumulated state
|
|
28
|
+
* • This sequence ensures cascade includes exactly what root decided
|
|
29
|
+
*
|
|
30
|
+
* Key invariant: Cascades ingest whatever root published before responding
|
|
31
|
+
* to flush. This prevents data loss regardless of controller placement.
|
|
32
|
+
*
|
|
33
|
+
* ────────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
* Execution Flow (per message)
|
|
35
|
+
* 1. Process input (raw sample or parent moments)
|
|
36
|
+
* 2. For cascades with flush signal: snapshot → reset after ingestion
|
|
37
|
+
* 3. Check window completion (both node types)
|
|
38
|
+
* 4. Plan publish if window complete or flush triggered
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/* eslint-disable camelcase */
|
|
42
|
+
import { copyFrom } from './copy-from.js';
|
|
43
|
+
import { prelude } from './prelude.js';
|
|
44
|
+
import reset from './reset.js';
|
|
45
|
+
|
|
46
|
+
// Pébay's algorithm for single value update
|
|
47
|
+
const updateFromRaw = function ( state, value ) {
|
|
48
|
+
const n1 = state.n;
|
|
49
|
+
state.n += 1;
|
|
50
|
+
|
|
51
|
+
const delta = value - state.M1;
|
|
52
|
+
const delta_n = delta / state.n;
|
|
53
|
+
const delta_n2 = delta_n * delta_n;
|
|
54
|
+
const term1 = delta * delta_n * n1;
|
|
55
|
+
|
|
56
|
+
// Update moments
|
|
57
|
+
state.M1 += delta_n;
|
|
58
|
+
state.M4 += ( term1 * delta_n2 * ( ( state.n * state.n ) - ( 3 * state.n ) + 3 ) ) +
|
|
59
|
+
( 6 * delta_n2 * state.M2 ) - ( 4 * delta_n * state.M3 );
|
|
60
|
+
state.M3 += ( term1 * delta_n * ( state.n - 2 ) ) - ( 3 * delta_n * state.M2 );
|
|
61
|
+
state.M2 += term1;
|
|
62
|
+
|
|
63
|
+
// Update min/max
|
|
64
|
+
if ( value < state.min ) state.min = value;
|
|
65
|
+
if ( value > state.max ) state.max = value;
|
|
66
|
+
}; // updateFromRaw()
|
|
67
|
+
|
|
68
|
+
// Combine moments from cascaded input
|
|
69
|
+
const updateFromCascade = function ( state, n, M1, M2, M3, M4, min, max ) {
|
|
70
|
+
// Initialize combined state if first cascade
|
|
71
|
+
if ( state.n === 0 ) {
|
|
72
|
+
state.n = n;
|
|
73
|
+
state.M1 = M1;
|
|
74
|
+
state.M2 = M2;
|
|
75
|
+
state.M3 = M3;
|
|
76
|
+
state.M4 = M4;
|
|
77
|
+
state.min = min;
|
|
78
|
+
state.max = max;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Combine using parallel moment algorithm
|
|
83
|
+
const a_n = state.n;
|
|
84
|
+
const a_M1 = state.M1;
|
|
85
|
+
const a_M2 = state.M2;
|
|
86
|
+
const a_M3 = state.M3;
|
|
87
|
+
const a_M4 = state.M4;
|
|
88
|
+
|
|
89
|
+
const combined_n = a_n + n;
|
|
90
|
+
const delta = M1 - a_M1;
|
|
91
|
+
const delta2 = delta * delta;
|
|
92
|
+
const delta3 = delta * delta2;
|
|
93
|
+
const delta4 = delta2 * delta2;
|
|
94
|
+
|
|
95
|
+
state.M1 = ( ( a_n * a_M1 ) + ( n * M1 ) ) / combined_n;
|
|
96
|
+
|
|
97
|
+
state.M2 = a_M2 + M2 +
|
|
98
|
+
( delta2 * a_n * n / combined_n );
|
|
99
|
+
|
|
100
|
+
state.M3 = a_M3 + M3 +
|
|
101
|
+
( ( delta3 * a_n * n * ( a_n - n ) ) / ( combined_n * combined_n ) ) +
|
|
102
|
+
( 3 * delta * ( ( a_n * M2 ) - ( n * a_M2 ) ) / combined_n );
|
|
103
|
+
|
|
104
|
+
state.M4 = a_M4 + M4 +
|
|
105
|
+
( delta4 * a_n * n * ( ( a_n * a_n ) - ( a_n * n ) + ( n * n ) ) / ( combined_n * combined_n * combined_n ) ) +
|
|
106
|
+
( 6 * delta2 * ( ( a_n * a_n * M2 ) + ( n * n * a_M2 ) ) / ( combined_n * combined_n ) ) +
|
|
107
|
+
( 4 * delta * ( ( a_n * M3 ) - ( n * a_M3 ) ) / combined_n );
|
|
108
|
+
|
|
109
|
+
state.n = combined_n;
|
|
110
|
+
|
|
111
|
+
// Update min/max
|
|
112
|
+
if ( min < state.min ) state.min = min;
|
|
113
|
+
if ( max > state.max ) state.max = max;
|
|
114
|
+
}; // updateFromCascade()
|
|
115
|
+
|
|
116
|
+
const update = function ( state, msg ) {
|
|
117
|
+
if ( state.disable || state.pause ) return state;
|
|
118
|
+
|
|
119
|
+
prelude( state, msg );
|
|
120
|
+
|
|
121
|
+
// Reset on each update
|
|
122
|
+
state.inputValidationFailed = false;
|
|
123
|
+
|
|
124
|
+
if ( state.isCascading ) {
|
|
125
|
+
// Extract ALL moments using pre-computed field names
|
|
126
|
+
const fmap = state.fieldMap;
|
|
127
|
+
|
|
128
|
+
// Always process parent's moments if available
|
|
129
|
+
if ( msg[ fmap.n ] !== undefined ) {
|
|
130
|
+
updateFromCascade(
|
|
131
|
+
state,
|
|
132
|
+
msg[ fmap.n ],
|
|
133
|
+
msg[ fmap.M1 ],
|
|
134
|
+
msg[ fmap.M2 ],
|
|
135
|
+
msg[ fmap.M3 ],
|
|
136
|
+
msg[ fmap.M4 ],
|
|
137
|
+
msg[ fmap.min ],
|
|
138
|
+
msg[ fmap.max ]
|
|
139
|
+
);
|
|
140
|
+
state.currentCount += 1;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Handle flush signal (overrides window completion)
|
|
144
|
+
if ( msg[ state.flushSignalKey ] === true && state.n > 0 ) {
|
|
145
|
+
copyFrom( state, state.snapshot );
|
|
146
|
+
state.planPublish = true;
|
|
147
|
+
reset( state );
|
|
148
|
+
return state; // Skip window completion check
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Fall through to shared window completion logic
|
|
152
|
+
} else {
|
|
153
|
+
const xVal = msg[ state.x ];
|
|
154
|
+
// Handle faults gracefully: ensure their isolation: applies to RAW updates only
|
|
155
|
+
if ( !Number.isFinite( xVal ) ) {
|
|
156
|
+
// Signals publishing NaN for all demanded `stats` in publishTo.
|
|
157
|
+
state.inputValidationFailed = true;
|
|
158
|
+
return state;
|
|
159
|
+
}
|
|
160
|
+
// Update using Pébay's algorithm
|
|
161
|
+
updateFromRaw( state, xVal );
|
|
162
|
+
// Increment window counter
|
|
163
|
+
state.currentCount += 1;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Epilogue: shared window completion logic
|
|
167
|
+
state.windowComplete = state.currentCount >= state.windowSize;
|
|
168
|
+
// End-of-window: schedule a publish (unless a flush snapshot is already planned)
|
|
169
|
+
if ( state.windowComplete && ( !state.planPublish ) && ( !state.inputValidationFailed ) ) {
|
|
170
|
+
// copy completed window
|
|
171
|
+
copyFrom( state, state.snapshot );
|
|
172
|
+
// Will publish snapshot this tick
|
|
173
|
+
state.planPublish = true;
|
|
174
|
+
// No flush propagation for plain window completion
|
|
175
|
+
// Start next window cleanly
|
|
176
|
+
reset( state );
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return state;
|
|
180
|
+
}; // update()
|
|
181
|
+
|
|
182
|
+
export default update;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// nodes/node-names.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Single source of truth for valid node names.
|
|
5
|
+
*
|
|
6
|
+
* Used for validation when nodes appear in DSL specs. Modules are
|
|
7
|
+
* loaded lazily at runtime via load-node-module.js.
|
|
8
|
+
*
|
|
9
|
+
* Names are camelCase, matching DSL method names. Directory paths
|
|
10
|
+
* are derived via toKebab() (e.g., 'esMean' -> 'es-mean').
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Set of valid node names for O(1) lookup.
|
|
15
|
+
* @type {Set<string>}
|
|
16
|
+
*/
|
|
17
|
+
export const NODE_NAMES = new Set( [
|
|
18
|
+
'accumulate',
|
|
19
|
+
'appraise',
|
|
20
|
+
'butterworthFilter',
|
|
21
|
+
'categorize',
|
|
22
|
+
'controller',
|
|
23
|
+
'diff',
|
|
24
|
+
'digestMoments',
|
|
25
|
+
'dwellTimeTracker',
|
|
26
|
+
'emitIf',
|
|
27
|
+
'esCorrelation',
|
|
28
|
+
'esMean',
|
|
29
|
+
'esPairwiseCorrelation',
|
|
30
|
+
'esStats',
|
|
31
|
+
'kalman1d',
|
|
32
|
+
'invertFlag',
|
|
33
|
+
'kernel',
|
|
34
|
+
'lag',
|
|
35
|
+
'median3',
|
|
36
|
+
'pageHinkley',
|
|
37
|
+
'passIf',
|
|
38
|
+
'persistIf',
|
|
39
|
+
'persistenceCheck',
|
|
40
|
+
'processIndex',
|
|
41
|
+
'ratio',
|
|
42
|
+
'sanitize',
|
|
43
|
+
'spikeGuard',
|
|
44
|
+
'stateChangeDetector',
|
|
45
|
+
'momentsDigest',
|
|
46
|
+
'swStats',
|
|
47
|
+
'swingWatch',
|
|
48
|
+
'tally',
|
|
49
|
+
'threshold',
|
|
50
|
+
'transform',
|
|
51
|
+
'trend',
|
|
52
|
+
'twStats',
|
|
53
|
+
'unbalance',
|
|
54
|
+
'vectorDistance',
|
|
55
|
+
'winnow'
|
|
56
|
+
] );
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checks if a node name is valid.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} name - camelCase node name
|
|
62
|
+
* @returns {boolean} true if valid
|
|
63
|
+
*/
|
|
64
|
+
export const isValidNodeName = function ( name ) {
|
|
65
|
+
return NODE_NAMES.has( name );
|
|
66
|
+
};
|
|
67
|
+
|