@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,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Page-Hinkley change detection node — public API barrel file.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the standard node interface (init, update, publishTo, reset,
|
|
5
|
+
* recompute) plus shared control functions (disable, enable, pause, unpause)
|
|
6
|
+
* and all introspection metadata.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { default as init } from './init.js';
|
|
10
|
+
export { default as update } from './update.js';
|
|
11
|
+
export { default as reset } from './reset.js';
|
|
12
|
+
export { default as recompute } from './recompute.js';
|
|
13
|
+
export { default as publishTo } from './publish-to.js';
|
|
14
|
+
// Direct imports for common control functions
|
|
15
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
16
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
17
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
18
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
19
|
+
// Re-export everything from introspection.js
|
|
20
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Initializes Page-Hinkley node state from a validated spec.
|
|
3
|
+
*
|
|
4
|
+
* Creates the complete state object: input mapping, tunable parameters
|
|
5
|
+
* (delta, lambda), structural options (halfLife, detectDrop, minWarmUpSamples),
|
|
6
|
+
* and all accumulator/output fields at their initial values. No allocations
|
|
7
|
+
* occur after init — the hot path (update/publishTo) is allocation-free.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { validateSpec } from '../../core/utils/node/index.js';
|
|
11
|
+
import * as introspect from './introspect.js';
|
|
12
|
+
import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
|
|
13
|
+
import { asTunable } from '../../core/tunable/index.js';
|
|
14
|
+
import { halfLifeToAlpha } from '../../core/utils/half-life/index.js';
|
|
15
|
+
|
|
16
|
+
const init = function ( spec ) {
|
|
17
|
+
validateSpec( spec, introspect );
|
|
18
|
+
|
|
19
|
+
// Create state after validation passes
|
|
20
|
+
const state = Object.create( null );
|
|
21
|
+
// Standard input parameter health flag during update: false means healthy
|
|
22
|
+
state.inputValidationFailed = false;
|
|
23
|
+
// Ensure node is enabled by default
|
|
24
|
+
state.disable = false;
|
|
25
|
+
state.pause = false;
|
|
26
|
+
|
|
27
|
+
// Extract field name
|
|
28
|
+
state.x = spec.from.x;
|
|
29
|
+
|
|
30
|
+
// Config copy
|
|
31
|
+
state.stats = spec.stats;
|
|
32
|
+
|
|
33
|
+
// Apply defaults from introspect (validation doesn't enforce them)
|
|
34
|
+
// Supports direct, field-keyed, and tunable specification for tunable params
|
|
35
|
+
const deltaSpec = resolveScalar( spec.delta, state.x );
|
|
36
|
+
const lambdaSpec = resolveScalar( spec.lambda, state.x );
|
|
37
|
+
// delta and lambda support tunables for adaptive change detection
|
|
38
|
+
state.deltaFn = asTunable( deltaSpec ?? introspect.DEFAULT_OPTIONS.delta );
|
|
39
|
+
state.lambdaFn = asTunable( lambdaSpec ?? introspect.DEFAULT_OPTIONS.lambda );
|
|
40
|
+
// halfLife is structural: selects exponentially smoothed vs running mean baseline.
|
|
41
|
+
// When absent → running mean (alpha=0). When present → alpha derived from halfLife.
|
|
42
|
+
const halfLifeSpec = resolveScalar( spec.halfLife, state.x );
|
|
43
|
+
state.alpha = ( typeof halfLifeSpec === 'number' ) ? halfLifeToAlpha( halfLifeSpec ) : 0;
|
|
44
|
+
state.detectDrop = spec.detectDrop ?? introspect.DEFAULT_OPTIONS.detectDrop;
|
|
45
|
+
state.minWarmUpSamples = spec.minWarmUpSamples ?? introspect.DEFAULT_OPTIONS.minWarmUpSamples;
|
|
46
|
+
|
|
47
|
+
// Seed tunable state fields with defaults — overwritten on every successful update()
|
|
48
|
+
state.delta = introspect.DEFAULT_OPTIONS.delta;
|
|
49
|
+
state.lambda = introspect.DEFAULT_OPTIONS.lambda;
|
|
50
|
+
state.tunableErrorLogged = false;
|
|
51
|
+
|
|
52
|
+
// State variables
|
|
53
|
+
state.cumSum = 0; // Cumulative sum
|
|
54
|
+
state.minCumSum = 0; // Running minimum of cumulative sum
|
|
55
|
+
state.mean = 0; // Adaptive mean estimate
|
|
56
|
+
state.count = 0; // Sample count for mean estimation
|
|
57
|
+
state.shiftDetected = false; // Previous shift detection state
|
|
58
|
+
state.testStatistic = 0; // Page-Hinkley test statistic
|
|
59
|
+
|
|
60
|
+
state.nodeType = introspect.getNodeType();
|
|
61
|
+
state.inControlPhase = false;
|
|
62
|
+
|
|
63
|
+
return state;
|
|
64
|
+
}; // init()
|
|
65
|
+
|
|
66
|
+
export default init;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Page-Hinkley node introspection metadata.
|
|
3
|
+
*
|
|
4
|
+
* Defines supported stats (phShift, phTestStatistic, phMean), control methods,
|
|
5
|
+
* default options, DSL spec schema with validators, and the buildSpec function
|
|
6
|
+
* for DSL transpilation. Single source of truth for the node's public contract.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { validators, composerValidators } from '../../core/utils/validate/index.js';
|
|
10
|
+
import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
|
|
11
|
+
|
|
12
|
+
/** Single source of truth for what this plugin can do */
|
|
13
|
+
const SUPPORTED_STATS = [
|
|
14
|
+
'phTestStatistic',
|
|
15
|
+
'phMean',
|
|
16
|
+
'phShift'
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
/** Human-readable descriptions (for introspection) */
|
|
20
|
+
const STAT_DESCRIPTIONS = {
|
|
21
|
+
phTestStatistic: 'Page-Hinkley change detection statistic',
|
|
22
|
+
phMean: 'Running mean or EWMA based on value of alpha',
|
|
23
|
+
phShift: 'Boolean indicator of detected shift'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Control methods available for triggers */
|
|
27
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
28
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
29
|
+
enable: 'Enables node processing',
|
|
30
|
+
disable: 'Disables node processing',
|
|
31
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
32
|
+
unpause: 'Resumes node processing after pause'
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** The type of this node */
|
|
36
|
+
const NODE_TYPE = 'Page Hinkley';
|
|
37
|
+
|
|
38
|
+
/** Node capabilities for documentation */
|
|
39
|
+
const CAPABILITIES = {
|
|
40
|
+
description: 'Detects changes in data distribution using the Page-Hinkley test',
|
|
41
|
+
features: [
|
|
42
|
+
'Sequential change point detection algorithm',
|
|
43
|
+
'Configurable sensitivity via lambda threshold',
|
|
44
|
+
'Adaptive baseline with running mean or EWMA',
|
|
45
|
+
'Can detect both increases and decreases in mean'
|
|
46
|
+
]
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Export functions for accessing metadata
|
|
50
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
51
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
52
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
53
|
+
export const getNodeType = () => NODE_TYPE;
|
|
54
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
55
|
+
|
|
56
|
+
export const DEFAULT_OPTIONS = {
|
|
57
|
+
delta: 0.005,
|
|
58
|
+
lambda: 45,
|
|
59
|
+
// halfLife: not specified → running mean (alpha=0). When specified,
|
|
60
|
+
// init.js converts halfLife → alpha via halfLifeToAlpha().
|
|
61
|
+
detectDrop: false,
|
|
62
|
+
minWarmUpSamples: 10
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/** Parameters that support tunable (dynamic) values.
|
|
66
|
+
* halfLife is structural (selects running mean vs exponentially smoothed
|
|
67
|
+
* baseline at init time), not tunable per-message. */
|
|
68
|
+
export const TUNABLE_PARAMS = [ 'delta', 'lambda' ];
|
|
69
|
+
|
|
70
|
+
// DSL Metadata for transpilation
|
|
71
|
+
const DSL_METADATA = {
|
|
72
|
+
// Spec schema for validation
|
|
73
|
+
specSchema: {
|
|
74
|
+
nodeType: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
required: true,
|
|
77
|
+
value: NODE_TYPE
|
|
78
|
+
},
|
|
79
|
+
name: {
|
|
80
|
+
type: 'string',
|
|
81
|
+
required: true,
|
|
82
|
+
validator: validators.identifier,
|
|
83
|
+
error: 'Name must be a valid identifier'
|
|
84
|
+
},
|
|
85
|
+
from: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
required: true,
|
|
88
|
+
properties: {
|
|
89
|
+
x: {
|
|
90
|
+
type: 'string',
|
|
91
|
+
required: true,
|
|
92
|
+
validator: validators.noSpaces,
|
|
93
|
+
error: 'Field name cannot contain spaces'
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
delta: {
|
|
98
|
+
type: 'numberOrFunctionOrFieldKeyed',
|
|
99
|
+
required: false,
|
|
100
|
+
default: 0.005,
|
|
101
|
+
validator: validators.positiveOrFunction,
|
|
102
|
+
error: 'Delta must be a positive number or function'
|
|
103
|
+
},
|
|
104
|
+
lambda: {
|
|
105
|
+
type: 'numberOrFunctionOrFieldKeyed',
|
|
106
|
+
required: false,
|
|
107
|
+
default: 45,
|
|
108
|
+
validator: validators.positiveOrFunction,
|
|
109
|
+
error: 'Lambda must be a positive number or function'
|
|
110
|
+
},
|
|
111
|
+
halfLife: {
|
|
112
|
+
type: 'numberOrFieldKeyed',
|
|
113
|
+
required: false,
|
|
114
|
+
validator: composerValidators.halfLife,
|
|
115
|
+
error: 'halfLife must be > 0 and < 999999'
|
|
116
|
+
},
|
|
117
|
+
detectDrop: {
|
|
118
|
+
type: 'boolean',
|
|
119
|
+
required: false,
|
|
120
|
+
default: false
|
|
121
|
+
},
|
|
122
|
+
minWarmUpSamples: {
|
|
123
|
+
type: 'number',
|
|
124
|
+
required: false,
|
|
125
|
+
default: 10,
|
|
126
|
+
integer: true,
|
|
127
|
+
validator: validators.positiveInteger,
|
|
128
|
+
error: 'Minimum warm-up samples must be a positive integer'
|
|
129
|
+
},
|
|
130
|
+
stats: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
required: true,
|
|
133
|
+
minProperties: 1,
|
|
134
|
+
propertyNames: SUPPORTED_STATS,
|
|
135
|
+
propertySchema: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
properties: {
|
|
138
|
+
storeAs: {
|
|
139
|
+
type: 'string',
|
|
140
|
+
required: true,
|
|
141
|
+
validator: validators.identifier,
|
|
142
|
+
error: 'storeAs must be a valid identifier'
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
triggers: {
|
|
148
|
+
type: 'array',
|
|
149
|
+
required: false,
|
|
150
|
+
itemSchema: {
|
|
151
|
+
type: 'object',
|
|
152
|
+
validator: composerValidators.trigger,
|
|
153
|
+
error: 'Invalid trigger specification'
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
// Cross-field validators
|
|
159
|
+
crossFieldValidators: [
|
|
160
|
+
{
|
|
161
|
+
fields: [ 'delta', 'lambda' ],
|
|
162
|
+
validator: ( spec ) => {
|
|
163
|
+
// Resolve each for the node's field so a direct value and a
|
|
164
|
+
// field-keyed map are compared the same way (each resolves to a
|
|
165
|
+
// number, a function, or undefined).
|
|
166
|
+
const delta = resolveScalar( spec.delta, spec.from?.x );
|
|
167
|
+
const lambda = resolveScalar( spec.lambda, spec.from?.x );
|
|
168
|
+
// If either is a function (tunable), defer validation to runtime
|
|
169
|
+
if ( typeof delta === 'function' || typeof lambda === 'function' ) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
// Delta should be small relative to lambda for proper sensitivity
|
|
173
|
+
if ( !delta || !lambda ) return true; // Use defaults
|
|
174
|
+
return delta < lambda * 0.1;
|
|
175
|
+
},
|
|
176
|
+
error: 'Delta should be less than 10% of lambda for proper sensitivity'
|
|
177
|
+
},
|
|
178
|
+
composerValidators.noSelfTriggers
|
|
179
|
+
],
|
|
180
|
+
|
|
181
|
+
// Build spec from DSL parameters
|
|
182
|
+
// Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
|
|
183
|
+
buildSpec: ( name, x, stats, options ) => ( {
|
|
184
|
+
nodeType: NODE_TYPE,
|
|
185
|
+
name,
|
|
186
|
+
from: { x },
|
|
187
|
+
stats,
|
|
188
|
+
...options
|
|
189
|
+
} ),
|
|
190
|
+
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Publishes Page-Hinkley results to the downstream message.
|
|
3
|
+
*
|
|
4
|
+
* Copies requested stats (phShift, phTestStatistic, phMean) from internal
|
|
5
|
+
* state to the message. Gated by minWarmUpSamples — no output until the
|
|
6
|
+
* baseline has accumulated enough observations. Propagates NaN downstream
|
|
7
|
+
* when the input was invalid.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { publishNaN } from '../../core/utils/node/index.js';
|
|
11
|
+
|
|
12
|
+
const publishTo = function ( state, msg ) {
|
|
13
|
+
if ( state.disable ) return;
|
|
14
|
+
// Propagate NaN if unhealthy
|
|
15
|
+
if ( state.inputValidationFailed ) {
|
|
16
|
+
publishNaN( state, msg );
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Require minimum samples before publishing results.
|
|
21
|
+
if ( state.count < state.minWarmUpSamples ) return;
|
|
22
|
+
// Publish whatever is asked for.
|
|
23
|
+
if ( state.stats.phShift ) msg[ state.stats.phShift.storeAs ] = state.shiftDetected;
|
|
24
|
+
if ( state.stats.phTestStatistic ) msg[ state.stats.phTestStatistic.storeAs ] = state.testStatistic;
|
|
25
|
+
if ( state.stats.phMean ) msg[ state.stats.phMean.storeAs ] = state.mean;
|
|
26
|
+
}; // publishTo()
|
|
27
|
+
|
|
28
|
+
export default publishTo;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Recompute handler for Page-Hinkley — intentional no-op.
|
|
3
|
+
*
|
|
4
|
+
* The PHT cumulative sum depends on the entire history of observations.
|
|
5
|
+
* Unlike ring-buffer nodes (es-stats, kernel), there is no stored window
|
|
6
|
+
* to recompute from. The no-op is the correct implementation.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const recompute = function () {
|
|
10
|
+
// Page-Hinkley is inherently sequential — cannot recompute from stored values
|
|
11
|
+
return true;
|
|
12
|
+
}; // recompute()
|
|
13
|
+
|
|
14
|
+
export default recompute;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Resets Page-Hinkley state to initial values.
|
|
3
|
+
*
|
|
4
|
+
* Clears all accumulated state (cumulative sum, running minimum, baseline
|
|
5
|
+
* mean, sample count) and detection outputs (shiftDetected, testStatistic).
|
|
6
|
+
* Configuration (alpha, detectDrop, tunables, stats) is preserved.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const reset = function ( state ) {
|
|
10
|
+
// Reset accumulated computation state
|
|
11
|
+
state.cumSum = 0;
|
|
12
|
+
state.minCumSum = 0;
|
|
13
|
+
state.mean = 0;
|
|
14
|
+
state.count = 0;
|
|
15
|
+
// Reset detection outputs — avoids publishing stale results after reset
|
|
16
|
+
state.shiftDetected = false;
|
|
17
|
+
state.testStatistic = 0;
|
|
18
|
+
// Clear error suppression so next error episode is logged
|
|
19
|
+
state.tunableErrorLogged = false;
|
|
20
|
+
return true;
|
|
21
|
+
}; // reset()
|
|
22
|
+
|
|
23
|
+
export default reset;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Page-Hinkley Test update function — processes one observation.
|
|
3
|
+
*
|
|
4
|
+
* Implements the standard PHT sequential recurrence (Page 1954, Hinkley 1971):
|
|
5
|
+
* 1. Update baseline estimate (running mean, or exponentially smoothed when
|
|
6
|
+
* halfLife is configured)
|
|
7
|
+
* 2. Accumulate cumulative sum of deviations from baseline minus drift allowance
|
|
8
|
+
* 3. Track running minimum of cumulative sum
|
|
9
|
+
* 4. Detect shift when test statistic (cumSum - minCumSum) exceeds lambda
|
|
10
|
+
*
|
|
11
|
+
* On detection, cumSum and minCumSum reset to 0; mean and count continue
|
|
12
|
+
* tracking the new regime. Triggers (if any) fire on detection.
|
|
13
|
+
*
|
|
14
|
+
* Design rationale — exponentially smoothed baseline vs exponentially smoothed
|
|
15
|
+
* cumsum (as in river.drift.PageHinkley):
|
|
16
|
+
*
|
|
17
|
+
* The composer applies exponential smoothing to the baseline mean, not the
|
|
18
|
+
* cumulative sum. This is deliberate for industrial streaming:
|
|
19
|
+
*
|
|
20
|
+
* 1. Non-stationary signals: Industrial sensors drift with operating
|
|
21
|
+
* conditions, ambient temperature, and equipment wear. A running mean
|
|
22
|
+
* of 10,000 samples is effectively a constant — useless as "current
|
|
23
|
+
* normal." An exponentially smoothed baseline tracks the actual
|
|
24
|
+
* operating point.
|
|
25
|
+
*
|
|
26
|
+
* 2. Undecayed evidence accumulation: The raw cumsum grows monotonically under
|
|
27
|
+
* a sustained shift — the mathematical foundation of the PHT.
|
|
28
|
+
* Exponentially smoothing the cumsum (river's approach) decays evidence
|
|
29
|
+
* over time; a gradual shift can decay away before reaching threshold.
|
|
30
|
+
*
|
|
31
|
+
* 3. Orthogonal tuning: halfLife controls baseline responsiveness;
|
|
32
|
+
* delta/lambda control detection sensitivity — independent knobs.
|
|
33
|
+
* river's alpha couples evidence decay with the effective detection
|
|
34
|
+
* window.
|
|
35
|
+
*
|
|
36
|
+
* 4. Architectural recovery: The control plane (controller node) explicitly
|
|
37
|
+
* resets baselines on confirmed change — clean, debuggable, no loss of
|
|
38
|
+
* detection power. river needs cumsum forgetting as a workaround.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import { executeTriggers } from '../../core/utils/node/index.js';
|
|
42
|
+
|
|
43
|
+
const update = function ( state, msg ) {
|
|
44
|
+
if ( state.disable || state.pause ) return state;
|
|
45
|
+
let xVal = msg[ state.x ];
|
|
46
|
+
// Reset on each update
|
|
47
|
+
state.inputValidationFailed = false;
|
|
48
|
+
// Handle faults gracefully: ensure their isolation
|
|
49
|
+
if ( !Number.isFinite( xVal ) ) {
|
|
50
|
+
// Signals publishing NaN for all demanded `stats` in publishTo.
|
|
51
|
+
state.inputValidationFailed = true;
|
|
52
|
+
return state;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if ( state.detectDrop ) {
|
|
56
|
+
xVal = -xVal;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Resolve tunable parameters for this message
|
|
60
|
+
try {
|
|
61
|
+
state.delta = state.deltaFn( msg );
|
|
62
|
+
state.lambda = state.lambdaFn( msg );
|
|
63
|
+
if ( state.tunableErrorLogged ) state.tunableErrorLogged = false;
|
|
64
|
+
} catch ( error ) {
|
|
65
|
+
if ( !state.tunableErrorLogged ) {
|
|
66
|
+
state.tunableErrorLogged = true;
|
|
67
|
+
console.error( `WinkComposer/${state.nodeType}: tunable threw: ${error.message}` );
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
state.count += 1;
|
|
72
|
+
// Choose baseline update rule.
|
|
73
|
+
if ( state.alpha === 0 ) { // running mean
|
|
74
|
+
state.mean += ( xVal - state.mean ) / state.count;
|
|
75
|
+
} else if ( state.count === 1 ) { // ES baseline: seed with first observation
|
|
76
|
+
state.mean = xVal;
|
|
77
|
+
} else { // ES baseline: exponential smoothing
|
|
78
|
+
state.mean += state.alpha * ( xVal - state.mean );
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Page-Hinkley cumulative sum calculation.
|
|
82
|
+
// Detects positive changes (increase in mean).
|
|
83
|
+
state.cumSum += xVal - state.mean - state.delta;
|
|
84
|
+
|
|
85
|
+
// Update running minimum.
|
|
86
|
+
if ( state.cumSum < state.minCumSum ) {
|
|
87
|
+
state.minCumSum = state.cumSum;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Change detection: test statistic exceeds threshold.
|
|
91
|
+
state.testStatistic = state.cumSum - state.minCumSum;
|
|
92
|
+
state.shiftDetected = state.testStatistic > state.lambda;
|
|
93
|
+
if ( state.shiftDetected ) {
|
|
94
|
+
state.minCumSum = 0;
|
|
95
|
+
state.cumSum = 0;
|
|
96
|
+
|
|
97
|
+
// Trigger controls if present.
|
|
98
|
+
executeTriggers( state );
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return state;
|
|
102
|
+
}; // update()
|
|
103
|
+
|
|
104
|
+
export default update;
|
|
105
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// nodes/pass-if/index.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Public API for passIf node
|
|
5
|
+
*
|
|
6
|
+
* Re-exports lifecycle functions, shared control utilities,
|
|
7
|
+
* and introspection metadata.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { default as init } from './init.js';
|
|
11
|
+
export { default as update } from './update.js';
|
|
12
|
+
export { default as reset } from './reset.js';
|
|
13
|
+
export { default as recompute } from './recompute.js';
|
|
14
|
+
export { default as publishTo } from './publish-to.js';
|
|
15
|
+
// Direct imports for common control functions
|
|
16
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
17
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
18
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
19
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
20
|
+
// Re-export everything from introspection.js
|
|
21
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// nodes/pass-if/init.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Initialization for passIf node
|
|
5
|
+
*
|
|
6
|
+
* Validates spec via standard pipeline, creates state with
|
|
7
|
+
* counter, predicate reference, and control flags.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { validateSpec } from '../../core/utils/node/index.js';
|
|
11
|
+
import * as introspect from './introspect.js';
|
|
12
|
+
|
|
13
|
+
const init = function ( spec ) {
|
|
14
|
+
validateSpec( spec, introspect );
|
|
15
|
+
|
|
16
|
+
// Create state after validation passes
|
|
17
|
+
const state = Object.create( null );
|
|
18
|
+
// Ensure node is enabled by default
|
|
19
|
+
state.disable = false;
|
|
20
|
+
state.pause = false;
|
|
21
|
+
|
|
22
|
+
// Extract validated predicate
|
|
23
|
+
state.predicate = spec.predicate;
|
|
24
|
+
|
|
25
|
+
// Initialize counter
|
|
26
|
+
state.counter = 0;
|
|
27
|
+
|
|
28
|
+
// Store node type for debugging
|
|
29
|
+
state.nodeType = introspect.getNodeType();
|
|
30
|
+
|
|
31
|
+
// Log suppression: one log per error episode; reset on recovery.
|
|
32
|
+
state.predicateErrorLogged = false;
|
|
33
|
+
|
|
34
|
+
return state;
|
|
35
|
+
}; // init()
|
|
36
|
+
|
|
37
|
+
export default init;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// nodes/pass-if/introspect.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Introspection metadata for passIf node
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth for DSL metadata, validation schema,
|
|
7
|
+
* default options, and node capabilities. Used by flow transpiler
|
|
8
|
+
* and documentation.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
12
|
+
|
|
13
|
+
/** Single source of truth for what this plugin can do */
|
|
14
|
+
const SUPPORTED_STATS = []; // passIf doesn't publish stats - it's a pure gate
|
|
15
|
+
|
|
16
|
+
/** Human-readable descriptions (for introspection) */
|
|
17
|
+
const STAT_DESCRIPTIONS = {};
|
|
18
|
+
|
|
19
|
+
/** Control methods available for triggers */
|
|
20
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
21
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
22
|
+
enable: 'Enables node processing',
|
|
23
|
+
disable: 'Disables node processing',
|
|
24
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
25
|
+
unpause: 'Resumes node processing after pause'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** The type of this node */
|
|
29
|
+
const NODE_TYPE = 'Pass If';
|
|
30
|
+
|
|
31
|
+
/** Node capabilities for documentation */
|
|
32
|
+
const CAPABILITIES = {
|
|
33
|
+
description: 'Binary gate that passes or stops messages based on a predicate function',
|
|
34
|
+
features: [
|
|
35
|
+
'Evaluates predicate with message and counter arguments',
|
|
36
|
+
'Stops downstream flow when predicate returns false',
|
|
37
|
+
'Maintains message counter for deterministic sampling patterns',
|
|
38
|
+
'Pure predicate functions with no side effects'
|
|
39
|
+
]
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Export functions for accessing metadata
|
|
43
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
44
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
45
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
46
|
+
export const getNodeType = () => NODE_TYPE;
|
|
47
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
48
|
+
export const DEFAULT_OPTIONS = {};
|
|
49
|
+
|
|
50
|
+
// DSL Metadata for transpilation
|
|
51
|
+
const DSL_METADATA = {
|
|
52
|
+
// Spec schema for validation
|
|
53
|
+
specSchema: {
|
|
54
|
+
nodeType: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
required: true,
|
|
57
|
+
value: NODE_TYPE
|
|
58
|
+
},
|
|
59
|
+
name: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
required: true,
|
|
62
|
+
validator: validators.identifier,
|
|
63
|
+
error: 'Name must be a valid identifier'
|
|
64
|
+
},
|
|
65
|
+
predicate: {
|
|
66
|
+
type: 'function',
|
|
67
|
+
required: true,
|
|
68
|
+
arity: 2,
|
|
69
|
+
error: 'Predicate must be a function with exactly two parameters (msg, counter)'
|
|
70
|
+
},
|
|
71
|
+
stats: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
required: false,
|
|
74
|
+
propertyNames: [], // No stats for pass-through node
|
|
75
|
+
error: 'PassIf is a gating node and does not support stats'
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// No cross-field validators needed
|
|
80
|
+
crossFieldValidators: [],
|
|
81
|
+
|
|
82
|
+
// How to build the spec from DSL params
|
|
83
|
+
// Pattern: NAME_PREDICATE_OPTIONS -> (name, predicate, options)
|
|
84
|
+
buildSpec: ( name, predicate, options ) => ( {
|
|
85
|
+
nodeType: NODE_TYPE,
|
|
86
|
+
name,
|
|
87
|
+
predicate,
|
|
88
|
+
...options
|
|
89
|
+
} ),
|
|
90
|
+
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// nodes/pass-if/publish-to.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Publish-to function for passIf node
|
|
5
|
+
*
|
|
6
|
+
* No-op — pure gate node does not add any fields to the
|
|
7
|
+
* message. Messages that pass the predicate continue unchanged.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const publishTo = function ( _state, _msg ) {
|
|
11
|
+
// PassIf is a pure gate - it doesn't publish any data to the message
|
|
12
|
+
// Messages that pass through are unchanged
|
|
13
|
+
}; // publishTo()
|
|
14
|
+
|
|
15
|
+
export default publishTo;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// nodes/pass-if/recompute.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Recompute function for passIf node
|
|
5
|
+
*
|
|
6
|
+
* No-op — counter is a simple integer with no numerical
|
|
7
|
+
* stability concerns.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const recompute = function () {
|
|
11
|
+
// Nothing to recompute - counter is a simple integer
|
|
12
|
+
return true;
|
|
13
|
+
}; // recompute()
|
|
14
|
+
|
|
15
|
+
export default recompute;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// nodes/pass-if/reset.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Reset function for passIf node
|
|
5
|
+
*
|
|
6
|
+
* Clears message counter and error suppression flag,
|
|
7
|
+
* allowing counter-based predicates to restart.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const reset = function ( state ) {
|
|
11
|
+
// Reset counter to initial state
|
|
12
|
+
state.counter = 0;
|
|
13
|
+
// Clear error suppression so next error episode is logged
|
|
14
|
+
state.predicateErrorLogged = false;
|
|
15
|
+
return true;
|
|
16
|
+
}; // reset()
|
|
17
|
+
|
|
18
|
+
export default reset;
|