@winkjs/composer 0.0.2 → 0.4.0
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 +42 -103
- package/package.json +53 -29
- 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,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Introspection metadata for kernel node.
|
|
3
|
+
*
|
|
4
|
+
* Defines supported stats, control methods, capabilities, DSL schema,
|
|
5
|
+
* preset validation, default options, and custom kernel validation.
|
|
6
|
+
* All getters return defensive copies.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
10
|
+
import PRESETS, { getPresetNames } from './presets.js';
|
|
11
|
+
|
|
12
|
+
/** Single source of truth for what this plugin can do */
|
|
13
|
+
const SUPPORTED_STATS = [
|
|
14
|
+
'filtered'
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/** Human-readable descriptions */
|
|
18
|
+
const STAT_DESCRIPTIONS = {
|
|
19
|
+
filtered: 'Result of applying the kernel (weighted sum over sliding window)'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Control methods available for triggers */
|
|
23
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
24
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
25
|
+
enable: 'Enables node processing',
|
|
26
|
+
disable: 'Disables node processing',
|
|
27
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
28
|
+
unpause: 'Resumes node processing after pause'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** The type of this node */
|
|
32
|
+
const NODE_TYPE = 'Kernel';
|
|
33
|
+
|
|
34
|
+
/** Node capabilities for documentation */
|
|
35
|
+
const CAPABILITIES = {
|
|
36
|
+
description: 'Apply weighted operations (kernels) over sliding windows in streaming data',
|
|
37
|
+
features: [
|
|
38
|
+
'Zero-allocation circular buffer',
|
|
39
|
+
'Scientifically validated presets (Savitzky-Golay, binomial, etc.)',
|
|
40
|
+
'Custom kernel support',
|
|
41
|
+
'Automatic time alignment'
|
|
42
|
+
],
|
|
43
|
+
categories: {
|
|
44
|
+
smoothing: [ 'smooth3', 'smooth5', 'sg5', 'sg7', 'binomial5', 'binomial7' ],
|
|
45
|
+
derivatives: [ 'rate', 'rate3', 'accel', 'sgRate5', 'shock', 'jerk' ],
|
|
46
|
+
detection: [ 'spike3', 'edge5', 'impulse' ],
|
|
47
|
+
slope: [ 'trend5', 'trend7', 'trend9', 'trend11', 'momentum5' ],
|
|
48
|
+
industrial: [ 'debounce5', 'envelope', 'volatility' ]
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Export functions for accessing metadata
|
|
53
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
54
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
55
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
56
|
+
export const getNodeType = () => NODE_TYPE;
|
|
57
|
+
export const getCapabilities = () => ( {
|
|
58
|
+
...CAPABILITIES,
|
|
59
|
+
features: CAPABILITIES.features.slice(),
|
|
60
|
+
categories: { ...CAPABILITIES.categories }
|
|
61
|
+
} );
|
|
62
|
+
export const getPresets = () => ( { ...PRESETS } );
|
|
63
|
+
|
|
64
|
+
/** Default options — kernel has no optional parameters. */
|
|
65
|
+
export const DEFAULT_OPTIONS = {};
|
|
66
|
+
|
|
67
|
+
// Custom validators
|
|
68
|
+
// Describes one kernel: an array of 2-100 finite numbers. The validation engine
|
|
69
|
+
// applies this per entry of a field-keyed map { field: [ ... ] }, so this stays a
|
|
70
|
+
// plain single-value check (see runCustomValidator in validate/helpers.js).
|
|
71
|
+
const kernelValidator = ( value ) => {
|
|
72
|
+
if ( !Array.isArray( value ) ) return false;
|
|
73
|
+
if ( value.length < 2 ) return false;
|
|
74
|
+
if ( value.length > 100 ) return false; // Reasonable limit
|
|
75
|
+
return value.every( ( v ) => Number.isFinite( v ) );
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// DSL Metadata for transpiler
|
|
79
|
+
const DSL_METADATA = {
|
|
80
|
+
// Validation schema
|
|
81
|
+
specSchema: {
|
|
82
|
+
nodeType: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
required: true,
|
|
85
|
+
value: NODE_TYPE
|
|
86
|
+
},
|
|
87
|
+
name: {
|
|
88
|
+
type: 'string',
|
|
89
|
+
required: true,
|
|
90
|
+
validator: validators.identifier,
|
|
91
|
+
error: 'Name must be a valid identifier'
|
|
92
|
+
},
|
|
93
|
+
from: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
required: true,
|
|
96
|
+
properties: {
|
|
97
|
+
x: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
required: true,
|
|
100
|
+
validator: validators.noSpaces,
|
|
101
|
+
error: 'Field name cannot contain spaces'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
// Either preset OR kernel must be provided
|
|
106
|
+
preset: {
|
|
107
|
+
type: 'stringOrFieldKeyed',
|
|
108
|
+
required: false,
|
|
109
|
+
validator: validators.oneOf( getPresetNames() ),
|
|
110
|
+
error: `Preset must be one of: ${getPresetNames().join(', ')}`
|
|
111
|
+
},
|
|
112
|
+
kernel: {
|
|
113
|
+
type: 'arrayOrFieldKeyed',
|
|
114
|
+
required: false,
|
|
115
|
+
validator: kernelValidator,
|
|
116
|
+
error: 'Kernel must be an array of 2-100 numbers'
|
|
117
|
+
},
|
|
118
|
+
stats: {
|
|
119
|
+
type: 'object',
|
|
120
|
+
required: true,
|
|
121
|
+
minProperties: 1,
|
|
122
|
+
propertyNames: SUPPORTED_STATS,
|
|
123
|
+
propertySchema: {
|
|
124
|
+
type: 'object',
|
|
125
|
+
properties: {
|
|
126
|
+
storeAs: {
|
|
127
|
+
type: 'string',
|
|
128
|
+
required: true,
|
|
129
|
+
validator: validators.identifier,
|
|
130
|
+
error: 'storeAs must be a valid identifier'
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
// Cross-field validators
|
|
138
|
+
crossFieldValidators: [
|
|
139
|
+
{
|
|
140
|
+
fields: [ 'preset', 'kernel' ],
|
|
141
|
+
validator: ( spec ) => {
|
|
142
|
+
// Exactly one must be provided
|
|
143
|
+
const hasPreset = spec.preset !== undefined;
|
|
144
|
+
const hasKernel = spec.kernel !== undefined;
|
|
145
|
+
return ( hasPreset && !hasKernel ) || ( !hasPreset && hasKernel );
|
|
146
|
+
},
|
|
147
|
+
error: 'Must provide either preset or kernel, but not both'
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
|
|
151
|
+
// Build spec from DSL
|
|
152
|
+
// Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
|
|
153
|
+
buildSpec: ( name, x, stats, options ) => ( {
|
|
154
|
+
nodeType: NODE_TYPE,
|
|
155
|
+
name,
|
|
156
|
+
from: { x },
|
|
157
|
+
stats,
|
|
158
|
+
...options
|
|
159
|
+
} ),
|
|
160
|
+
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Scientifically validated filter kernels for common streaming operations.
|
|
3
|
+
*
|
|
4
|
+
* Each preset is optimized for specific use cases in industrial, financial,
|
|
5
|
+
* and quality control applications. Kernels are defined in newest-to-oldest
|
|
6
|
+
* order (kernel[0] = weight for x[n]).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const PRESETS = Object.create( null );
|
|
10
|
+
// Basic smoothing operations
|
|
11
|
+
PRESETS.smooth3 = [ 0.25, 0.5, 0.25 ]; // 3-point Gaussian approximation
|
|
12
|
+
PRESETS.smooth5 = [ 0.1, 0.2, 0.4, 0.2, 0.1 ]; // 5-point Gaussian approximation
|
|
13
|
+
|
|
14
|
+
// Derivatives
|
|
15
|
+
PRESETS.rate = [ -1, 1 ]; // Simple difference (1st derivative)
|
|
16
|
+
PRESETS.rate3 = [ -1, 0, 1 ]; // Centered difference (less noise)
|
|
17
|
+
PRESETS.accel = [ 1, -2, 1 ]; // 2nd derivative (acceleration)
|
|
18
|
+
|
|
19
|
+
// Savitzky-Golay filters (preserves peaks while smoothing)
|
|
20
|
+
PRESETS.sg5 = [ -3 / 35, 12 / 35, 17 / 35, 12 / 35, -3 / 35 ]; // 5-point, 2nd order polynomial
|
|
21
|
+
PRESETS.sg7 = [ -2 / 21, 3 / 21, 6 / 21, 7 / 21, 6 / 21, 3 / 21, -2 / 21 ]; // 7-point, 2nd order
|
|
22
|
+
PRESETS.sgRate5 = [ -2 / 10, -1 / 10, 0, 1 / 10, 2 / 10 ]; // SG 1st derivative
|
|
23
|
+
|
|
24
|
+
// Trend kernels — Savitzky-Golay 1st-derivative (slope) filters, with
|
|
25
|
+
// the sign chosen so that a rising signal produces a POSITIVE slope and
|
|
26
|
+
// a falling signal produces a NEGATIVE slope. This matches the standard
|
|
27
|
+
// calculus/academic convention and reads naturally in thresholding code:
|
|
28
|
+
// `threshold({ below: -0.06 })` fires on a downward trend.
|
|
29
|
+
//
|
|
30
|
+
// Note: the older `sgRate5` preset uses the opposite sign (falling →
|
|
31
|
+
// positive) because kernel weights are user-defined newest-to-oldest
|
|
32
|
+
// and sgRate5 pre-dates this convention discussion. The trend family
|
|
33
|
+
// is independent — use these when the sign should read as a slope.
|
|
34
|
+
//
|
|
35
|
+
// weights_i = −i / Σi² for i = −N..N (newest-to-oldest user order).
|
|
36
|
+
// Wider windows trade latency for smoothness: trend5 is the most
|
|
37
|
+
// responsive, trend11 the smoothest. Pair with threshold +
|
|
38
|
+
// persistenceCheck to detect sustained rising or falling trends.
|
|
39
|
+
// Reference: Savitzky & Golay (1964), Anal. Chem. 36(8), 1627.
|
|
40
|
+
PRESETS.trend5 = [ 2 / 10, 1 / 10, 0, -1 / 10, -2 / 10 ]; // Σi² = 10
|
|
41
|
+
PRESETS.trend7 = [ 3 / 28, 2 / 28, 1 / 28, 0, -1 / 28, -2 / 28, -3 / 28 ]; // Σi² = 28
|
|
42
|
+
PRESETS.trend9 = [ 4 / 60, 3 / 60, 2 / 60, 1 / 60, 0,
|
|
43
|
+
-1 / 60, -2 / 60, -3 / 60, -4 / 60 ]; // Σi² = 60
|
|
44
|
+
PRESETS.trend11 = [ 5 / 110, 4 / 110, 3 / 110, 2 / 110, 1 / 110, 0,
|
|
45
|
+
-1 / 110, -2 / 110, -3 / 110, -4 / 110, -5 / 110 ]; // Σi² = 110
|
|
46
|
+
|
|
47
|
+
// Binomial filters (optimal noise reduction)
|
|
48
|
+
PRESETS.binomial5 = [ 1 / 16, 4 / 16, 6 / 16, 4 / 16, 1 / 16 ]; // Pascal's triangle row 4
|
|
49
|
+
PRESETS.binomial7 = [ 1 / 64, 6 / 64, 15 / 64, 20 / 64, 15 / 64, 6 / 64, 1 / 64 ]; // Row 6
|
|
50
|
+
|
|
51
|
+
// Industrial process control
|
|
52
|
+
PRESETS.debounce5 = [ 0.1, 0.2, 0.4, 0.2, 0.1 ]; // Digital signal debouncing
|
|
53
|
+
|
|
54
|
+
// Detection filters
|
|
55
|
+
PRESETS.spike3 = [ -1, 2, -1 ]; // Spike/transient detection
|
|
56
|
+
PRESETS.edge5 = [ -1, -1, 5, -1, -1 ]; // Edge enhancement (Laplacian)
|
|
57
|
+
PRESETS.impulse = [ 0.25, -1, 1.5, -1, 0.25 ]; // Impulse/event detection
|
|
58
|
+
|
|
59
|
+
// Mechanical/vibration analysis
|
|
60
|
+
PRESETS.shock = [ 1, -2, 1 ]; // Shock/impact detection
|
|
61
|
+
PRESETS.jerk = [ -1, 3, -3, 1 ]; // 3rd derivative (jerk)
|
|
62
|
+
PRESETS.envelope = [ 0.1, 0.15, 0.5, 0.15, 0.1 ]; // Envelope extraction
|
|
63
|
+
|
|
64
|
+
// Business/financial metrics
|
|
65
|
+
PRESETS.momentum5 = [ -0.3, -0.1, 0, 0.1, 0.3 ]; // Weighted momentum
|
|
66
|
+
PRESETS.volatility = [ -1, 2, -1 ]; // Local variance proxy
|
|
67
|
+
|
|
68
|
+
export default PRESETS;
|
|
69
|
+
|
|
70
|
+
// Helper to validate preset names
|
|
71
|
+
export const isValidPreset = ( name ) => Object.prototype.hasOwnProperty.call( PRESETS, name );
|
|
72
|
+
|
|
73
|
+
// Get list of all preset names for validation
|
|
74
|
+
export const getPresetNames = () => Object.keys( PRESETS );
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Publish function for kernel node.
|
|
3
|
+
*
|
|
4
|
+
* Copies the convolution result to the outgoing message. Gates on warmup
|
|
5
|
+
* (ring buffer not yet full) and input validation failure (publishes NaN).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { publishNaN } from '../../core/utils/node/index.js';
|
|
9
|
+
import { isNotFull } from '../../windowing/count-sliding/index.js';
|
|
10
|
+
|
|
11
|
+
const publishTo = function ( state, msg ) {
|
|
12
|
+
if ( state.disable ) return;
|
|
13
|
+
// Propagate NaN if unhealthy
|
|
14
|
+
if ( state.inputValidationFailed ) {
|
|
15
|
+
publishNaN( state, msg );
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Ring is full (warmup complete) — publish the result
|
|
20
|
+
if ( !isNotFull( state.ring ) ) {
|
|
21
|
+
msg[ state.stats.filtered.storeAs ] = state.result;
|
|
22
|
+
}
|
|
23
|
+
}; // publishTo()
|
|
24
|
+
|
|
25
|
+
export default publishTo;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Recompute for kernel node.
|
|
3
|
+
*
|
|
4
|
+
* No-op — returns true. The convolution result is computed fresh from ring
|
|
5
|
+
* buffer contents on every message, so there is no accumulator drift to
|
|
6
|
+
* correct. Unlike recursive filters (e.g., Kalman), FIR convolution is
|
|
7
|
+
* inherently stable.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const recompute = function () {
|
|
11
|
+
return true;
|
|
12
|
+
}; // recompute()
|
|
13
|
+
|
|
14
|
+
export default recompute;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Reset for kernel node.
|
|
3
|
+
*
|
|
4
|
+
* Clears the ring buffer and result. Configuration properties (kernel,
|
|
5
|
+
* field names, stats) are preserved across reset.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as ring from '../../windowing/count-sliding/index.js';
|
|
9
|
+
|
|
10
|
+
const reset = function ( state ) {
|
|
11
|
+
// Reset window state
|
|
12
|
+
ring.reset( state.ring );
|
|
13
|
+
|
|
14
|
+
// Clear result
|
|
15
|
+
state.result = 0;
|
|
16
|
+
|
|
17
|
+
return true;
|
|
18
|
+
}; // reset()
|
|
19
|
+
|
|
20
|
+
export default reset;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Core FIR convolution for kernel node (hot path).
|
|
3
|
+
*
|
|
4
|
+
* Pushes each valid input into a doubled ring buffer — each value is written
|
|
5
|
+
* at `buffer[ head ]` AND `buffer[ head + size ]` so the convolution window
|
|
6
|
+
* is always contiguous at `buffer[ head .. head + size - 1 ]`, eliminating
|
|
7
|
+
* the wrap branch inside the inner loop. Push and fullness check are
|
|
8
|
+
* inlined. The kernel array has been reversed at init (see init.js) so the
|
|
9
|
+
* buffer is read oldest-to-newest with the kernel indexed 0..L-1.
|
|
10
|
+
*
|
|
11
|
+
* Zero allocations in the hot path — all arithmetic on pre-allocated
|
|
12
|
+
* structures created in init(). Fault isolation via `inputValidationFailed`
|
|
13
|
+
* flag propagates NaN downstream through publishTo().
|
|
14
|
+
*
|
|
15
|
+
* References:
|
|
16
|
+
* - Oppenheim & Schafer, "Discrete-Time Signal Processing", Ch. 2.3
|
|
17
|
+
* (Convolution Sum).
|
|
18
|
+
* - "Duplicated head" ring-buffer pattern (FFTW small-transform codelets,
|
|
19
|
+
* https://www.fftw.org/fftw-paper-ieee.pdf Sec. III.B).
|
|
20
|
+
* - Benchmark: 2.2-2.6× speedup vs. the prior single-size ring
|
|
21
|
+
* implementation (measured; ADR-012).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const update = function ( state, msg ) {
|
|
25
|
+
if ( state.disable || state.pause ) return state;
|
|
26
|
+
const xVal = msg[ state.x ];
|
|
27
|
+
// Reset on each update
|
|
28
|
+
state.inputValidationFailed = false;
|
|
29
|
+
// Handle faults gracefully: flag and continue — downstream sees NaN
|
|
30
|
+
if ( !Number.isFinite( xVal ) ) {
|
|
31
|
+
state.inputValidationFailed = true;
|
|
32
|
+
return state;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const ring = state.ring;
|
|
36
|
+
const size = ring.size;
|
|
37
|
+
const buffer = ring.buffer;
|
|
38
|
+
let head = ring.head;
|
|
39
|
+
|
|
40
|
+
// Doubled-buffer push: write at head AND head + size.
|
|
41
|
+
// Head advances within [ 0, size ); the mirror half makes the read
|
|
42
|
+
// window contiguous without a modulus or wrap branch.
|
|
43
|
+
buffer[ head ] = xVal;
|
|
44
|
+
buffer[ head + size ] = xVal;
|
|
45
|
+
head = ( head + 1 < size ) ? head + 1 : 0;
|
|
46
|
+
ring.head = head;
|
|
47
|
+
|
|
48
|
+
// Warmup: grow `used` up to `size`, then skip convolution while still
|
|
49
|
+
// partial. Once `used === size` on the current push, fall through to
|
|
50
|
+
// compute the first convolution output on that same message — matches
|
|
51
|
+
// baseline semantics (fill-then-compute on the boundary push).
|
|
52
|
+
if ( ring.used < size ) {
|
|
53
|
+
ring.used += 1;
|
|
54
|
+
if ( ring.used < size ) return state;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Contiguous convolution — no wrap branch in the inner loop.
|
|
58
|
+
const kernel = state.kernel;
|
|
59
|
+
const kernelLength = state.kernelLength;
|
|
60
|
+
let sum = 0;
|
|
61
|
+
for ( let i = 0; i < kernelLength; i += 1 ) {
|
|
62
|
+
sum += buffer[ head + i ] * kernel[ i ];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
state.result = sum;
|
|
66
|
+
return state;
|
|
67
|
+
}; // update()
|
|
68
|
+
|
|
69
|
+
export default update;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// nodes/lag/index.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Exports for the lag node.
|
|
5
|
+
*
|
|
6
|
+
* The lag node computes five lag-based statistics: delta, ratio, roc,
|
|
7
|
+
* slope, and logReturn. This replaces and extends the former delta node.
|
|
8
|
+
*
|
|
9
|
+
* @see ADR-004 for patterns
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export { default as init } from './init.js';
|
|
13
|
+
export { default as update } from './update.js';
|
|
14
|
+
export { default as reset } from './reset.js';
|
|
15
|
+
export { default as recompute } from './recompute.js';
|
|
16
|
+
export { default as publishTo } from './publish-to.js';
|
|
17
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
18
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
19
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
20
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
21
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// nodes/lag/init.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Initializes the lag node state.
|
|
5
|
+
*
|
|
6
|
+
* Creates pre-allocated ring buffer(s) and pre-computes stat flags for
|
|
7
|
+
* zero-allocation hot path execution. Follows ADR-004 patterns.
|
|
8
|
+
*
|
|
9
|
+
* @see ADR-004
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { validateSpec } from '../../core/utils/node/index.js';
|
|
13
|
+
import * as introspect from './introspect.js';
|
|
14
|
+
import { create } from '../../windowing/count-sliding/index.js';
|
|
15
|
+
import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initializes the lag node state from a validated spec.
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} spec - Node specification
|
|
21
|
+
* @param {string} spec.name - Unique node identifier
|
|
22
|
+
* @param {Object} spec.from - Input field configuration
|
|
23
|
+
* @param {string} spec.from.x - Input field name for values
|
|
24
|
+
* @param {string} [spec.timestamp] - Input field name for timestamps (required for slope)
|
|
25
|
+
* @param {number|Object} [spec.lag=1] - Lag window size (supports field-keyed)
|
|
26
|
+
* @param {boolean} [spec.absolute=false] - Apply Math.abs to delta and slope
|
|
27
|
+
* @param {Object} spec.stats - Output statistics configuration
|
|
28
|
+
* @returns {Object} Initialized state object
|
|
29
|
+
*/
|
|
30
|
+
const init = function ( spec ) {
|
|
31
|
+
validateSpec( spec, introspect );
|
|
32
|
+
|
|
33
|
+
// Create state with null prototype (ADR-004: avoid prototype pollution)
|
|
34
|
+
const state = Object.create( null );
|
|
35
|
+
|
|
36
|
+
// ── Standard Flags ─────────────────────────────────────────────────────
|
|
37
|
+
state.inputValidationFailed = false;
|
|
38
|
+
state.disable = false;
|
|
39
|
+
state.pause = false;
|
|
40
|
+
|
|
41
|
+
// ── Configuration from Spec ────────────────────────────────────────────
|
|
42
|
+
state.x = spec.from.x;
|
|
43
|
+
state.timestamp = spec.timestamp ?? null;
|
|
44
|
+
state.stats = spec.stats;
|
|
45
|
+
|
|
46
|
+
// Resolve lag (supports field-keyed specification)
|
|
47
|
+
const lagSpec = resolveScalar( spec.lag, state.x );
|
|
48
|
+
state.lag = lagSpec ?? introspect.DEFAULT_OPTIONS.lag;
|
|
49
|
+
|
|
50
|
+
// Apply defaults for optional parameters
|
|
51
|
+
state.absolute = spec.absolute ?? introspect.DEFAULT_OPTIONS.absolute;
|
|
52
|
+
|
|
53
|
+
// ── Pre-allocated Ring Buffers ─────────────────────────────────────────
|
|
54
|
+
// Primary ring buffer for x values (always allocated)
|
|
55
|
+
state.ringX = create( state.lag );
|
|
56
|
+
|
|
57
|
+
// Secondary ring buffer for timestamps (only if slope is requested)
|
|
58
|
+
state.ringT = ( spec.stats.slope === undefined ) ? null : create( state.lag );
|
|
59
|
+
|
|
60
|
+
// ── Pre-computed Stat Flags (zero-branch hot path) ─────────────────────
|
|
61
|
+
// These flags avoid object property iteration in update()
|
|
62
|
+
state.hasDelta = spec.stats.delta !== undefined;
|
|
63
|
+
state.hasRatio = spec.stats.ratio !== undefined;
|
|
64
|
+
state.hasRoc = spec.stats.roc !== undefined;
|
|
65
|
+
state.hasSlope = spec.stats.slope !== undefined;
|
|
66
|
+
state.hasLogReturn = spec.stats.logReturn !== undefined;
|
|
67
|
+
state.hasCumDelta = spec.stats.cumDelta !== undefined;
|
|
68
|
+
state.hasXLag = spec.stats.xLag !== undefined;
|
|
69
|
+
|
|
70
|
+
// ── Computed Values (output storage) ───────────────────────────────────
|
|
71
|
+
// Initialize to NaN (startup state before buffer fills)
|
|
72
|
+
state.delta = NaN;
|
|
73
|
+
state.ratio = NaN;
|
|
74
|
+
state.roc = NaN;
|
|
75
|
+
state.slope = NaN;
|
|
76
|
+
state.logReturn = NaN;
|
|
77
|
+
state.xLag = NaN;
|
|
78
|
+
// cumDelta initializes to 0 (integral from a to a = 0)
|
|
79
|
+
state.cumDelta = 0;
|
|
80
|
+
|
|
81
|
+
// ── Metadata ───────────────────────────────────────────────────────────
|
|
82
|
+
state.nodeType = introspect.getNodeType();
|
|
83
|
+
|
|
84
|
+
return state;
|
|
85
|
+
}; // init()
|
|
86
|
+
|
|
87
|
+
export default init;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// nodes/lag/introspect.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Introspection metadata for the lag node.
|
|
5
|
+
*
|
|
6
|
+
* The lag node computes five lag-based statistics from a single ring buffer:
|
|
7
|
+
* delta, ratio, roc (rate of change), slope (time-normalized), and logReturn.
|
|
8
|
+
* This replaces and extends the former delta node.
|
|
9
|
+
*
|
|
10
|
+
* @see ADR-004 for patterns
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Supported output statistics.
|
|
17
|
+
* At least one must be specified in the spec.
|
|
18
|
+
* @type {string[]}
|
|
19
|
+
*/
|
|
20
|
+
const SUPPORTED_STATS = [
|
|
21
|
+
'delta',
|
|
22
|
+
'ratio',
|
|
23
|
+
'roc',
|
|
24
|
+
'slope',
|
|
25
|
+
'logReturn',
|
|
26
|
+
'cumDelta',
|
|
27
|
+
'xLag'
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Human-readable descriptions for each statistic.
|
|
32
|
+
* @type {Object<string, string>}
|
|
33
|
+
*/
|
|
34
|
+
const STAT_DESCRIPTIONS = {
|
|
35
|
+
delta: 'Absolute change: x - x_lag',
|
|
36
|
+
ratio: 'Relative value: x / x_lag',
|
|
37
|
+
roc: 'Rate of change (percentage): (x - x_lag) / x_lag',
|
|
38
|
+
slope: 'Time-normalized change: (x - x_lag) / (t - t_lag)',
|
|
39
|
+
logReturn: 'Log return (continuously compounded): ln(x / x_lag)',
|
|
40
|
+
cumDelta: 'Cumulative delta: running sum of (x - x_lag), reset sets lower limit',
|
|
41
|
+
xLag: 'Lagged input value: the value pushed lag samples ago. NaN during the warmup window (first lag samples).'
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Control methods available for triggers.
|
|
46
|
+
* @type {Object<string, string>}
|
|
47
|
+
*/
|
|
48
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
49
|
+
reset: 'Resets computed values; preserves ring buffer when cumDelta is configured (ADR-008)',
|
|
50
|
+
enable: 'Enables node processing',
|
|
51
|
+
disable: 'Disables node processing',
|
|
52
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
53
|
+
unpause: 'Resumes node processing after pause'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Node type identifier.
|
|
58
|
+
* @type {string}
|
|
59
|
+
*/
|
|
60
|
+
const NODE_TYPE = 'Lag';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Node capabilities for documentation and introspection.
|
|
64
|
+
* @type {Object}
|
|
65
|
+
*/
|
|
66
|
+
const CAPABILITIES = {
|
|
67
|
+
description: 'Computes lag-based statistics comparing current and historical values',
|
|
68
|
+
features: [
|
|
69
|
+
'Seven statistics: delta, ratio, roc, slope, logReturn, cumDelta, xLag',
|
|
70
|
+
'Configurable lag window for historical comparison',
|
|
71
|
+
'Optional absolute value transformation for delta and slope',
|
|
72
|
+
'Shared ring buffer for memory efficiency',
|
|
73
|
+
'Optional timestamp support for slope computation',
|
|
74
|
+
'Handles missing/invalid data gracefully with NaN propagation',
|
|
75
|
+
'Cumulative delta for integration-style computations with reset',
|
|
76
|
+
'Direct xLag publication for lookback-based downstream patterns'
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// ── Exported Getters (defensive copies) ────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
83
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
84
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
85
|
+
export const getNodeType = () => NODE_TYPE;
|
|
86
|
+
export const getCapabilities = () => ( {
|
|
87
|
+
...CAPABILITIES,
|
|
88
|
+
features: CAPABILITIES.features.slice()
|
|
89
|
+
} );
|
|
90
|
+
|
|
91
|
+
// ── Default Options ────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Default values for optional spec parameters.
|
|
95
|
+
* @type {Object}
|
|
96
|
+
*/
|
|
97
|
+
export const DEFAULT_OPTIONS = {
|
|
98
|
+
lag: 1,
|
|
99
|
+
absolute: false
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// ── DSL Metadata ───────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* DSL metadata for transpilation and validation.
|
|
106
|
+
*
|
|
107
|
+
* Signature pattern: NAME_X_OUTPUTS_OPTIONS
|
|
108
|
+
* DSL call: .lag( name, x, stats, options )
|
|
109
|
+
*/
|
|
110
|
+
const DSL_METADATA = {
|
|
111
|
+
specSchema: {
|
|
112
|
+
nodeType: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
required: true,
|
|
115
|
+
value: NODE_TYPE
|
|
116
|
+
},
|
|
117
|
+
name: {
|
|
118
|
+
type: 'string',
|
|
119
|
+
required: true,
|
|
120
|
+
validator: validators.identifier,
|
|
121
|
+
error: 'name must be a valid identifier'
|
|
122
|
+
},
|
|
123
|
+
from: {
|
|
124
|
+
type: 'object',
|
|
125
|
+
required: true,
|
|
126
|
+
properties: {
|
|
127
|
+
x: {
|
|
128
|
+
type: 'string',
|
|
129
|
+
required: true,
|
|
130
|
+
validator: validators.noSpaces,
|
|
131
|
+
error: 'from.x must not contain spaces'
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
lag: {
|
|
136
|
+
type: 'numberOrFieldKeyed',
|
|
137
|
+
required: false,
|
|
138
|
+
integer: true,
|
|
139
|
+
min: 1,
|
|
140
|
+
default: DEFAULT_OPTIONS.lag
|
|
141
|
+
},
|
|
142
|
+
timestamp: {
|
|
143
|
+
type: 'string',
|
|
144
|
+
required: false,
|
|
145
|
+
validator: validators.noSpaces,
|
|
146
|
+
error: 'timestamp must not contain spaces'
|
|
147
|
+
},
|
|
148
|
+
absolute: {
|
|
149
|
+
type: 'boolean',
|
|
150
|
+
required: false,
|
|
151
|
+
default: DEFAULT_OPTIONS.absolute
|
|
152
|
+
},
|
|
153
|
+
stats: {
|
|
154
|
+
type: 'object',
|
|
155
|
+
required: true,
|
|
156
|
+
minProperties: 1,
|
|
157
|
+
propertyNames: SUPPORTED_STATS,
|
|
158
|
+
propertySchema: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
required: true,
|
|
161
|
+
properties: {
|
|
162
|
+
storeAs: {
|
|
163
|
+
type: 'string',
|
|
164
|
+
required: true,
|
|
165
|
+
minLength: 1,
|
|
166
|
+
validator: validators.identifier,
|
|
167
|
+
error: 'storeAs must be a valid identifier'
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
// Cross-field validators
|
|
175
|
+
crossFieldValidators: [
|
|
176
|
+
{
|
|
177
|
+
fields: [ 'stats', 'timestamp' ],
|
|
178
|
+
validator: ( spec ) => {
|
|
179
|
+
// If slope stat is requested, timestamp field is required
|
|
180
|
+
if ( spec.stats?.slope !== undefined ) {
|
|
181
|
+
return spec.timestamp !== undefined;
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
184
|
+
},
|
|
185
|
+
error: 'timestamp is required when slope stat is requested'
|
|
186
|
+
}
|
|
187
|
+
],
|
|
188
|
+
|
|
189
|
+
// Build spec from DSL arguments
|
|
190
|
+
buildSpec: ( name, x, stats, options ) => {
|
|
191
|
+
const spec = {
|
|
192
|
+
nodeType: NODE_TYPE,
|
|
193
|
+
name,
|
|
194
|
+
from: { x },
|
|
195
|
+
stats,
|
|
196
|
+
...options
|
|
197
|
+
};
|
|
198
|
+
return spec;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|