@winkjs/composer 0.0.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +106 -80
- package/package.json +56 -28
- package/src/composer.js +75 -0
- package/src/core/batch/aoo-encoder.js +148 -0
- package/src/core/batch/index.js +12 -0
- package/src/core/batch/sliding-window.js +91 -0
- package/src/core/codec/index.js +137 -0
- package/src/core/emitter-manager/mqtt/constants.js +102 -0
- package/src/core/emitter-manager/mqtt/emitter.js +780 -0
- package/src/core/emitter-manager/mqtt/index.js +190 -0
- package/src/core/emitter-manager/mqtt/mqtt-store.js +538 -0
- package/src/core/emitter-manager/terminal/assert-columns.js +98 -0
- package/src/core/emitter-manager/terminal/formatters.js +160 -0
- package/src/core/emitter-manager/terminal/index.js +341 -0
- package/src/core/env-vars.js +245 -0
- package/src/core/mqtt-protocol.js +47 -0
- package/src/core/partition-manager/index.js +7 -0
- package/src/core/partition-manager/init.js +44 -0
- package/src/core/partition-manager/introspect.js +15 -0
- package/src/core/partition-manager/publish-to.js +6 -0
- package/src/core/partition-manager/recompute.js +5 -0
- package/src/core/partition-manager/reset.js +5 -0
- package/src/core/partition-manager/update.js +264 -0
- package/src/core/partition-manager/validate-flow.js +157 -0
- package/src/core/semantics/digest.js +128 -0
- package/src/core/semantics/index.js +83 -0
- package/src/core/semantics/loader.js +571 -0
- package/src/core/semantics/schemas/asset-class-schema.js +218 -0
- package/src/core/semantics/schemas/column-schema.js +651 -0
- package/src/core/semantics/schemas/enum-schema.js +153 -0
- package/src/core/semantics/schemas/index.js +51 -0
- package/src/core/semantics/warnings.js +141 -0
- package/src/core/shutdown-manager/index.js +289 -0
- package/src/core/source-manager/csv/index.js +171 -0
- package/src/core/source-manager/csv/start.js +456 -0
- package/src/core/source-manager/mqtt/client.js +406 -0
- package/src/core/source-manager/mqtt/constants.js +147 -0
- package/src/core/source-manager/mqtt/dedup.js +244 -0
- package/src/core/source-manager/mqtt/index.js +260 -0
- package/src/core/source-manager/mqtt/status.js +546 -0
- package/src/core/source-manager/test-harness/comparator.js +226 -0
- package/src/core/source-manager/test-harness/field-generator.js +85 -0
- package/src/core/source-manager/test-harness/fuzz.js +87 -0
- package/src/core/source-manager/test-harness/index.js +115 -0
- package/src/core/source-manager/test-harness/prng.js +57 -0
- package/src/core/source-manager/test-harness/start.js +232 -0
- package/src/core/source-manager/test-harness/validate.js +213 -0
- package/src/core/storage-manager/questdb/assert-columns.js +120 -0
- package/src/core/storage-manager/questdb/ensure-tables.js +187 -0
- package/src/core/storage-manager/questdb/index.js +1251 -0
- package/src/core/storage-manager/questdb/persist-plan.js +488 -0
- package/src/core/storage-manager/questdb/writers.js +142 -0
- package/src/core/test-utils/sealed-record.js +80 -0
- package/src/core/test-utils/soak-signature.js +149 -0
- package/src/core/test-utils/tcp-proxy.js +106 -0
- package/src/core/tunable/helpers.js +254 -0
- package/src/core/tunable/index.js +87 -0
- package/src/core/utils/console-formatter/index.js +207 -0
- package/src/core/utils/create-smart-store-as/index.js +98 -0
- package/src/core/utils/create-smart-store-as/sanitize-field.js +46 -0
- package/src/core/utils/create-smart-store-as/smart-truncate.js +93 -0
- package/src/core/utils/flow/index.js +5 -0
- package/src/core/utils/flow/make-collision-checker.js +23 -0
- package/src/core/utils/flow/to-kebab.js +20 -0
- package/src/core/utils/half-life/index.js +111 -0
- package/src/core/utils/node/disable.js +11 -0
- package/src/core/utils/node/enable.js +11 -0
- package/src/core/utils/node/execute-triggers.js +40 -0
- package/src/core/utils/node/index.js +10 -0
- package/src/core/utils/node/pause.js +11 -0
- package/src/core/utils/node/populate-predicate-inputs.js +14 -0
- package/src/core/utils/node/publish-nan.js +19 -0
- package/src/core/utils/node/reset-predicate-inputs.js +11 -0
- package/src/core/utils/node/unpause.js +11 -0
- package/src/core/utils/node/validate-spec.js +22 -0
- package/src/core/utils/options/resolve-field-keyed.js +115 -0
- package/src/core/utils/quantize/index.js +75 -0
- package/src/core/utils/stats/formulas.js +67 -0
- package/src/core/utils/stats/index.js +3 -0
- package/src/core/utils/validate/composer-validators.js +141 -0
- package/src/core/utils/validate/helpers.js +206 -0
- package/src/core/utils/validate/index.js +12 -0
- package/src/core/utils/validate/type-validators.js +538 -0
- package/src/core/utils/validate/validate.js +107 -0
- package/src/core/utils/validate/validators.js +177 -0
- package/src/core/wiring/assert-handle.js +57 -0
- package/src/core/wiring/assert-module.js +63 -0
- package/src/core/wiring/index.js +5 -0
- package/src/core/wiring/node-type-to-module.js +17 -0
- package/src/core/wiring/wire-emitters.js +256 -0
- package/src/core/wiring/wire-linear-graph.js +25 -0
- package/src/core/wiring/wire-node.js +157 -0
- package/src/core/wiring/wire-semantics.js +173 -0
- package/src/core/wiring/wire-storages.js +344 -0
- package/src/flow/check-output-collisions.js +93 -0
- package/src/flow/consts.js +9 -0
- package/src/flow/deep-clone-spec.js +64 -0
- package/src/flow/driver.js +171 -0
- package/src/flow/expand-single-input-array.js +76 -0
- package/src/flow/expand-template-spec.js +93 -0
- package/src/flow/flow-api-schemas.js +300 -0
- package/src/flow/flow.js +658 -0
- package/src/flow/for-each.js +225 -0
- package/src/flow/get-signature-pattern.js +31 -0
- package/src/flow/get-target-array.js +46 -0
- package/src/flow/inspect.js +67 -0
- package/src/flow/load-node-module.js +81 -0
- package/src/flow/make-node-method.js +112 -0
- package/src/flow/resolve-pick-by-field.js +77 -0
- package/src/flow/resolve-tunables.js +76 -0
- package/src/flow/run.js +392 -0
- package/src/flow/serialize.js +161 -0
- package/src/flow/signatures-schema.js +222 -0
- package/src/flow/spec-builder.js +65 -0
- package/src/flow/validate-triggers.js +250 -0
- package/src/flow/validate.js +261 -0
- package/src/nodes/accumulate/index.js +21 -0
- package/src/nodes/accumulate/init.js +48 -0
- package/src/nodes/accumulate/introspect.js +123 -0
- package/src/nodes/accumulate/publish-to.js +34 -0
- package/src/nodes/accumulate/recompute.js +27 -0
- package/src/nodes/accumulate/reset.js +25 -0
- package/src/nodes/accumulate/update.js +42 -0
- package/src/nodes/appraise/calibrate.js +64 -0
- package/src/nodes/appraise/decision.js +66 -0
- package/src/nodes/appraise/deviation.js +140 -0
- package/src/nodes/appraise/index.js +23 -0
- package/src/nodes/appraise/init.js +165 -0
- package/src/nodes/appraise/integrate.js +45 -0
- package/src/nodes/appraise/introspect.js +352 -0
- package/src/nodes/appraise/publish-to.js +93 -0
- package/src/nodes/appraise/receptor.js +69 -0
- package/src/nodes/appraise/recompute.js +79 -0
- package/src/nodes/appraise/reset.js +53 -0
- package/src/nodes/appraise/update.js +130 -0
- package/src/nodes/archive/csv/index.js +6 -0
- package/src/nodes/archive/csv/init.js +36 -0
- package/src/nodes/archive/csv/load.js +70 -0
- package/src/nodes/archive/csv/reset.js +14 -0
- package/src/nodes/archive/csv/simple-parser.js +216 -0
- package/src/nodes/archive/csv/test.js +173 -0
- package/src/nodes/archive/csv/update.js +63 -0
- package/src/nodes/butterworth-filter/index.js +18 -0
- package/src/nodes/butterworth-filter/init.js +184 -0
- package/src/nodes/butterworth-filter/introspect.js +228 -0
- package/src/nodes/butterworth-filter/publish-to.js +21 -0
- package/src/nodes/butterworth-filter/recompute.js +13 -0
- package/src/nodes/butterworth-filter/reset.js +30 -0
- package/src/nodes/butterworth-filter/update.js +38 -0
- package/src/nodes/categorize/index.js +19 -0
- package/src/nodes/categorize/init.js +58 -0
- package/src/nodes/categorize/introspect.js +177 -0
- package/src/nodes/categorize/publish-to.js +29 -0
- package/src/nodes/categorize/recompute.js +14 -0
- package/src/nodes/categorize/reset.js +16 -0
- package/src/nodes/categorize/update.js +53 -0
- package/src/nodes/controller/index.js +9 -0
- package/src/nodes/controller/init.js +55 -0
- package/src/nodes/controller/introspect.js +91 -0
- package/src/nodes/controller/publish-to.js +8 -0
- package/src/nodes/controller/recompute.js +9 -0
- package/src/nodes/controller/reset.js +17 -0
- package/src/nodes/controller/update.js +63 -0
- package/src/nodes/diff/index.js +19 -0
- package/src/nodes/diff/init.js +37 -0
- package/src/nodes/diff/introspect.js +132 -0
- package/src/nodes/diff/publish-to.js +21 -0
- package/src/nodes/diff/recompute.js +12 -0
- package/src/nodes/diff/reset.js +12 -0
- package/src/nodes/diff/update.js +37 -0
- package/src/nodes/digest-moments/formulas.js +12 -0
- package/src/nodes/digest-moments/index.js +14 -0
- package/src/nodes/digest-moments/init.js +67 -0
- package/src/nodes/digest-moments/introspect.js +154 -0
- package/src/nodes/digest-moments/publish-to.js +34 -0
- package/src/nodes/digest-moments/recompute.js +14 -0
- package/src/nodes/digest-moments/reset.js +14 -0
- package/src/nodes/digest-moments/update.js +120 -0
- package/src/nodes/dwell-time-tracker/index.js +14 -0
- package/src/nodes/dwell-time-tracker/init.js +59 -0
- package/src/nodes/dwell-time-tracker/introspect.js +137 -0
- package/src/nodes/dwell-time-tracker/publish-to.js +22 -0
- package/src/nodes/dwell-time-tracker/recompute.js +8 -0
- package/src/nodes/dwell-time-tracker/reset.js +23 -0
- package/src/nodes/dwell-time-tracker/update.js +103 -0
- package/src/nodes/emit-if/helpers.js +94 -0
- package/src/nodes/emit-if/index.js +16 -0
- package/src/nodes/emit-if/init.js +67 -0
- package/src/nodes/emit-if/introspect.js +102 -0
- package/src/nodes/emit-if/publish-to.js +13 -0
- package/src/nodes/emit-if/recompute.js +12 -0
- package/src/nodes/emit-if/reset.js +27 -0
- package/src/nodes/emit-if/update.js +93 -0
- package/src/nodes/es-correlation/index.js +19 -0
- package/src/nodes/es-correlation/init.js +73 -0
- package/src/nodes/es-correlation/introspect.js +176 -0
- package/src/nodes/es-correlation/publish-to.js +32 -0
- package/src/nodes/es-correlation/recompute.js +56 -0
- package/src/nodes/es-correlation/reset.js +27 -0
- package/src/nodes/es-correlation/update.js +110 -0
- package/src/nodes/es-mean/index.js +20 -0
- package/src/nodes/es-mean/init.js +58 -0
- package/src/nodes/es-mean/introspect.js +129 -0
- package/src/nodes/es-mean/publish-to.js +19 -0
- package/src/nodes/es-mean/recompute.js +16 -0
- package/src/nodes/es-mean/reset.js +24 -0
- package/src/nodes/es-mean/update.js +69 -0
- package/src/nodes/es-pairwise-correlation/index.js +19 -0
- package/src/nodes/es-pairwise-correlation/init.js +98 -0
- package/src/nodes/es-pairwise-correlation/introspect.js +187 -0
- package/src/nodes/es-pairwise-correlation/publish-to.js +31 -0
- package/src/nodes/es-pairwise-correlation/recompute.js +69 -0
- package/src/nodes/es-pairwise-correlation/reset.js +29 -0
- package/src/nodes/es-pairwise-correlation/update.js +89 -0
- package/src/nodes/es-stats/index.js +21 -0
- package/src/nodes/es-stats/init.js +105 -0
- package/src/nodes/es-stats/introspect.js +157 -0
- package/src/nodes/es-stats/publish-to.js +42 -0
- package/src/nodes/es-stats/recompute.js +74 -0
- package/src/nodes/es-stats/reset.js +49 -0
- package/src/nodes/es-stats/update-envelope.js +44 -0
- package/src/nodes/es-stats/update-welford.js +64 -0
- package/src/nodes/es-stats/update.js +59 -0
- package/src/nodes/index.js +83 -0
- package/src/nodes/invert-flag/index.js +20 -0
- package/src/nodes/invert-flag/init.js +36 -0
- package/src/nodes/invert-flag/introspect.js +115 -0
- package/src/nodes/invert-flag/publish-to.js +23 -0
- package/src/nodes/invert-flag/recompute.js +12 -0
- package/src/nodes/invert-flag/reset.js +12 -0
- package/src/nodes/invert-flag/update.js +32 -0
- package/src/nodes/kalman1d/index.js +20 -0
- package/src/nodes/kalman1d/init.js +103 -0
- package/src/nodes/kalman1d/introspect.js +250 -0
- package/src/nodes/kalman1d/publish-to.js +34 -0
- package/src/nodes/kalman1d/recompute.js +23 -0
- package/src/nodes/kalman1d/reset.js +29 -0
- package/src/nodes/kalman1d/update.js +144 -0
- package/src/nodes/kernel/index.js +18 -0
- package/src/nodes/kernel/init.js +66 -0
- package/src/nodes/kernel/introspect.js +163 -0
- package/src/nodes/kernel/presets.js +74 -0
- package/src/nodes/kernel/publish-to.js +25 -0
- package/src/nodes/kernel/recompute.js +14 -0
- package/src/nodes/kernel/reset.js +20 -0
- package/src/nodes/kernel/update.js +69 -0
- package/src/nodes/lag/index.js +21 -0
- package/src/nodes/lag/init.js +87 -0
- package/src/nodes/lag/introspect.js +202 -0
- package/src/nodes/lag/publish-to.js +64 -0
- package/src/nodes/lag/recompute.js +33 -0
- package/src/nodes/lag/reset.js +49 -0
- package/src/nodes/lag/update.js +133 -0
- package/src/nodes/median3/index.js +19 -0
- package/src/nodes/median3/init.js +40 -0
- package/src/nodes/median3/introspect.js +116 -0
- package/src/nodes/median3/publish-to.js +21 -0
- package/src/nodes/median3/recompute.js +12 -0
- package/src/nodes/median3/reset.js +15 -0
- package/src/nodes/median3/update.js +58 -0
- package/src/nodes/moments-digest/copy-from.js +12 -0
- package/src/nodes/moments-digest/flush.js +17 -0
- package/src/nodes/moments-digest/index.js +132 -0
- package/src/nodes/moments-digest/init.js +84 -0
- package/src/nodes/moments-digest/introspect.js +122 -0
- package/src/nodes/moments-digest/prelude.js +28 -0
- package/src/nodes/moments-digest/publish-to.js +31 -0
- package/src/nodes/moments-digest/recompute.js +9 -0
- package/src/nodes/moments-digest/reset.js +20 -0
- package/src/nodes/moments-digest/update.js +182 -0
- package/src/nodes/node-names.js +67 -0
- package/src/nodes/page-hinkley/index.js +20 -0
- package/src/nodes/page-hinkley/init.js +66 -0
- package/src/nodes/page-hinkley/introspect.js +193 -0
- package/src/nodes/page-hinkley/publish-to.js +28 -0
- package/src/nodes/page-hinkley/recompute.js +14 -0
- package/src/nodes/page-hinkley/reset.js +23 -0
- package/src/nodes/page-hinkley/update.js +105 -0
- package/src/nodes/pass-if/index.js +21 -0
- package/src/nodes/pass-if/init.js +37 -0
- package/src/nodes/pass-if/introspect.js +93 -0
- package/src/nodes/pass-if/publish-to.js +15 -0
- package/src/nodes/pass-if/recompute.js +15 -0
- package/src/nodes/pass-if/reset.js +18 -0
- package/src/nodes/pass-if/update.js +37 -0
- package/src/nodes/persist-if/helpers.js +111 -0
- package/src/nodes/persist-if/index.js +9 -0
- package/src/nodes/persist-if/init.js +98 -0
- package/src/nodes/persist-if/introspect.js +106 -0
- package/src/nodes/persist-if/publish-to.js +21 -0
- package/src/nodes/persist-if/recompute.js +20 -0
- package/src/nodes/persist-if/reset.js +35 -0
- package/src/nodes/persist-if/update.js +112 -0
- package/src/nodes/persistence-check/index.js +14 -0
- package/src/nodes/persistence-check/init.js +38 -0
- package/src/nodes/persistence-check/introspect.js +144 -0
- package/src/nodes/persistence-check/publish-to.js +11 -0
- package/src/nodes/persistence-check/recompute.js +7 -0
- package/src/nodes/persistence-check/reset.js +12 -0
- package/src/nodes/persistence-check/update.js +57 -0
- package/src/nodes/process-index/index.js +15 -0
- package/src/nodes/process-index/init.js +48 -0
- package/src/nodes/process-index/introspect.js +182 -0
- package/src/nodes/process-index/publish-to.js +29 -0
- package/src/nodes/process-index/recompute.js +8 -0
- package/src/nodes/process-index/reset.js +8 -0
- package/src/nodes/process-index/update.js +75 -0
- package/src/nodes/ratio/index.js +14 -0
- package/src/nodes/ratio/init.js +37 -0
- package/src/nodes/ratio/introspect.js +155 -0
- package/src/nodes/ratio/publish-to.js +15 -0
- package/src/nodes/ratio/recompute.js +7 -0
- package/src/nodes/ratio/reset.js +7 -0
- package/src/nodes/ratio/update.js +47 -0
- package/src/nodes/sanitize/index.js +82 -0
- package/src/nodes/sanitize/init.js +82 -0
- package/src/nodes/sanitize/introspect.js +194 -0
- package/src/nodes/sanitize/publish-to.js +15 -0
- package/src/nodes/sanitize/recompute.js +8 -0
- package/src/nodes/sanitize/reset.js +14 -0
- package/src/nodes/sanitize/update.js +102 -0
- package/src/nodes/spike-guard/index.js +14 -0
- package/src/nodes/spike-guard/init.js +47 -0
- package/src/nodes/spike-guard/introspect.js +130 -0
- package/src/nodes/spike-guard/publish-to.js +35 -0
- package/src/nodes/spike-guard/recompute.js +14 -0
- package/src/nodes/spike-guard/reset.js +19 -0
- package/src/nodes/spike-guard/update.js +89 -0
- package/src/nodes/sse-emitter/index.js +7 -0
- package/src/nodes/sse-emitter/init.js +105 -0
- package/src/nodes/sse-emitter/introspect.js +134 -0
- package/src/nodes/sse-emitter/publish-to.js +6 -0
- package/src/nodes/sse-emitter/recompute.js +16 -0
- package/src/nodes/sse-emitter/reset.js +19 -0
- package/src/nodes/sse-emitter/update.js +69 -0
- package/src/nodes/sse-emitter/utils/broadcast-heartbeat.js +36 -0
- package/src/nodes/sse-emitter/utils/build-message.js +31 -0
- package/src/nodes/sse-emitter/utils/connection-manager.js +45 -0
- package/src/nodes/sse-emitter/utils/endpoint-initializer.js +20 -0
- package/src/nodes/sse-emitter/utils/health-monitor.js +38 -0
- package/src/nodes/sse-emitter/utils/qos-handler.js +41 -0
- package/src/nodes/state-change-detector/helpers.js +35 -0
- package/src/nodes/state-change-detector/index.js +12 -0
- package/src/nodes/state-change-detector/init.js +56 -0
- package/src/nodes/state-change-detector/introspect.js +146 -0
- package/src/nodes/state-change-detector/publish-to.js +20 -0
- package/src/nodes/state-change-detector/recompute.js +6 -0
- package/src/nodes/state-change-detector/reset.js +20 -0
- package/src/nodes/state-change-detector/update.js +97 -0
- package/src/nodes/sw-stats/index.js +14 -0
- package/src/nodes/sw-stats/init.js +51 -0
- package/src/nodes/sw-stats/introspect.js +131 -0
- package/src/nodes/sw-stats/publish-to.js +45 -0
- package/src/nodes/sw-stats/recompute.js +33 -0
- package/src/nodes/sw-stats/reset.js +15 -0
- package/src/nodes/sw-stats/update.js +39 -0
- package/src/nodes/swing-watch/index.js +17 -0
- package/src/nodes/swing-watch/init.js +144 -0
- package/src/nodes/swing-watch/introspect.js +188 -0
- package/src/nodes/swing-watch/publish-to.js +53 -0
- package/src/nodes/swing-watch/recompute.js +22 -0
- package/src/nodes/swing-watch/reset.js +55 -0
- package/src/nodes/swing-watch/update.js +767 -0
- package/src/nodes/tally/index.js +15 -0
- package/src/nodes/tally/init.js +32 -0
- package/src/nodes/tally/introspect.js +116 -0
- package/src/nodes/tally/publish-to.js +28 -0
- package/src/nodes/tally/recompute.js +10 -0
- package/src/nodes/tally/reset.js +10 -0
- package/src/nodes/tally/update.js +44 -0
- package/src/nodes/threshold/index.js +14 -0
- package/src/nodes/threshold/init.js +121 -0
- package/src/nodes/threshold/introspect.js +192 -0
- package/src/nodes/threshold/publish-to.js +11 -0
- package/src/nodes/threshold/recompute.js +8 -0
- package/src/nodes/threshold/reset.js +14 -0
- package/src/nodes/threshold/update.js +54 -0
- package/src/nodes/transform/helpers.js +39 -0
- package/src/nodes/transform/index.js +15 -0
- package/src/nodes/transform/init.js +35 -0
- package/src/nodes/transform/introspect.js +118 -0
- package/src/nodes/transform/publish-to.js +23 -0
- package/src/nodes/transform/recompute.js +12 -0
- package/src/nodes/transform/reset.js +11 -0
- package/src/nodes/transform/update.js +32 -0
- package/src/nodes/trend/compute-confidence.js +112 -0
- package/src/nodes/trend/index.js +14 -0
- package/src/nodes/trend/init.js +81 -0
- package/src/nodes/trend/introspect.js +151 -0
- package/src/nodes/trend/publish-to.js +22 -0
- package/src/nodes/trend/recompute.js +15 -0
- package/src/nodes/trend/reset.js +26 -0
- package/src/nodes/trend/update.js +93 -0
- package/src/nodes/tw-stats/copy-from.js +16 -0
- package/src/nodes/tw-stats/flush.js +12 -0
- package/src/nodes/tw-stats/index.js +41 -0
- package/src/nodes/tw-stats/init.js +92 -0
- package/src/nodes/tw-stats/introspect.js +171 -0
- package/src/nodes/tw-stats/publish-to.js +108 -0
- package/src/nodes/tw-stats/recompute.js +9 -0
- package/src/nodes/tw-stats/reset.js +16 -0
- package/src/nodes/tw-stats/update.js +113 -0
- package/src/nodes/unbalance/index.js +15 -0
- package/src/nodes/unbalance/init.js +59 -0
- package/src/nodes/unbalance/introspect.js +167 -0
- package/src/nodes/unbalance/publish-to.js +52 -0
- package/src/nodes/unbalance/recompute.js +11 -0
- package/src/nodes/unbalance/reset.js +10 -0
- package/src/nodes/unbalance/update.js +83 -0
- package/src/nodes/vector-distance/index.js +14 -0
- package/src/nodes/vector-distance/init.js +54 -0
- package/src/nodes/vector-distance/introspect.js +149 -0
- package/src/nodes/vector-distance/publish-to.js +35 -0
- package/src/nodes/vector-distance/recompute.js +11 -0
- package/src/nodes/vector-distance/reset.js +20 -0
- package/src/nodes/vector-distance/update.js +139 -0
- package/src/nodes/winnow/index.js +12 -0
- package/src/nodes/winnow/init.js +82 -0
- package/src/nodes/winnow/introspect.js +253 -0
- package/src/nodes/winnow/publish-to.js +51 -0
- package/src/nodes/winnow/recompute.js +14 -0
- package/src/nodes/winnow/reset.js +34 -0
- package/src/nodes/winnow/update.js +203 -0
- package/src/tools/training/features.js +227 -0
- package/src/tools/training/index.js +21 -0
- package/src/tools/training/logistic.js +238 -0
- package/src/tools/training/metrics.js +126 -0
- package/src/tools/training/scale.js +239 -0
- package/src/tools/training/validate.js +103 -0
- package/src/windowing/count-sliding/create.js +16 -0
- package/src/windowing/count-sliding/get-reverse-chronological-index.js +19 -0
- package/src/windowing/count-sliding/index.js +6 -0
- package/src/windowing/count-sliding/is-not-full.js +5 -0
- package/src/windowing/count-sliding/push.js +15 -0
- package/src/windowing/count-sliding/reset.js +11 -0
- package/src/windowing/count-sliding/size.js +5 -0
- package/src/windowing/time-sliding/create.js +25 -0
- package/src/windowing/time-sliding/push.js +42 -0
- package/CODE_OF_CONDUCT.md +0 -46
- package/CONTRIBUTING.md +0 -132
- package/docs/.nojekyll +0 -0
- package/docs/CONTRIBUTING.md +0 -134
- package/docs/README.md +0 -132
- package/docs/_coverpage.md +0 -26
- package/docs/_media/bg.png +0 -0
- package/docs/_media/wink-logo.png +0 -0
- package/docs/_navbar.md +0 -14
- package/docs/_sidebar.md +0 -2
- package/docs/index.html +0 -44
- package/eslint.config.js +0 -283
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pre-built transform helpers — pure math primitives.
|
|
3
|
+
*
|
|
4
|
+
* Each helper is a single-argument pure function suitable for the
|
|
5
|
+
* transform node's `using` option. Helpers carry a `.semantics`
|
|
6
|
+
* property for introspection, consistent with tunable helpers.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { square, abs } from '@winkjs/composer';
|
|
10
|
+
* .transform( 'sq', 'ecgDeriv', { result: 'ecgSquared' }, { using: square } )
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Squares the input: x * x */
|
|
14
|
+
export const square = ( x ) => ( x * x );
|
|
15
|
+
square.semantics = { type: 'transform', name: 'square', formula: 'x * x' };
|
|
16
|
+
|
|
17
|
+
/** Absolute value: |x| */
|
|
18
|
+
export const abs = ( x ) => Math.abs( x );
|
|
19
|
+
abs.semantics = { type: 'transform', name: 'abs', formula: '|x|' };
|
|
20
|
+
|
|
21
|
+
/** Square root: √x (NaN if x < 0) */
|
|
22
|
+
export const sqrt = ( x ) => Math.sqrt( x );
|
|
23
|
+
sqrt.semantics = { type: 'transform', name: 'sqrt', formula: '√x' };
|
|
24
|
+
|
|
25
|
+
/** Natural logarithm: ln(x) (-Infinity if x = 0, NaN if x < 0) */
|
|
26
|
+
export const log = ( x ) => Math.log( x );
|
|
27
|
+
log.semantics = { type: 'transform', name: 'log', formula: 'ln(x)' };
|
|
28
|
+
|
|
29
|
+
/** Base-10 logarithm: log₁₀(x) (-Infinity if x = 0, NaN if x < 0) */
|
|
30
|
+
export const log10 = ( x ) => Math.log10( x );
|
|
31
|
+
log10.semantics = { type: 'transform', name: 'log10', formula: 'log₁₀(x)' };
|
|
32
|
+
|
|
33
|
+
/** Reciprocal: 1/x (Infinity if x = 0) */
|
|
34
|
+
export const reciprocal = ( x ) => ( 1 / x );
|
|
35
|
+
reciprocal.semantics = { type: 'transform', name: 'reciprocal', formula: '1/x' };
|
|
36
|
+
|
|
37
|
+
/** Negation: -x */
|
|
38
|
+
export const negate = ( x ) => ( -x );
|
|
39
|
+
negate.semantics = { type: 'transform', name: 'negate', formula: '-x' };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Transform node — applies a user-supplied pure function
|
|
3
|
+
* to each sample: result = using( x ).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { default as init } from './init.js';
|
|
7
|
+
export { default as update } from './update.js';
|
|
8
|
+
export { default as reset } from './reset.js';
|
|
9
|
+
export { default as recompute } from './recompute.js';
|
|
10
|
+
export { default as publishTo } from './publish-to.js';
|
|
11
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
12
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
13
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
14
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
15
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Initialization for the transform node.
|
|
3
|
+
*
|
|
4
|
+
* Validates the spec and creates a minimal state object. The user-supplied
|
|
5
|
+
* function (`using`) is stored as a direct reference — it is fixed at init
|
|
6
|
+
* time and not wrapped as a tunable.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { validateSpec } from '../../core/utils/node/index.js';
|
|
10
|
+
import * as introspect from './introspect.js';
|
|
11
|
+
|
|
12
|
+
const init = function ( spec ) {
|
|
13
|
+
validateSpec( spec, introspect );
|
|
14
|
+
|
|
15
|
+
const state = Object.create( null );
|
|
16
|
+
|
|
17
|
+
// Standard flags
|
|
18
|
+
state.inputValidationFailed = false;
|
|
19
|
+
state.disable = false;
|
|
20
|
+
state.pause = false;
|
|
21
|
+
|
|
22
|
+
// Configuration from spec
|
|
23
|
+
state.x = spec.from.x;
|
|
24
|
+
state.stats = spec.stats;
|
|
25
|
+
state.using = spec.using;
|
|
26
|
+
|
|
27
|
+
// Pre-allocate output slot
|
|
28
|
+
state.result = 0;
|
|
29
|
+
|
|
30
|
+
state.nodeType = introspect.getNodeType();
|
|
31
|
+
|
|
32
|
+
return state;
|
|
33
|
+
}; // init()
|
|
34
|
+
|
|
35
|
+
export default init;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Introspection metadata for the transform node.
|
|
3
|
+
*
|
|
4
|
+
* Applies a user-supplied pure function to each sample: result = using( x ).
|
|
5
|
+
* The function is fixed at init time and receives a single numeric value.
|
|
6
|
+
* Transform-produced NaN (e.g. sqrt of negative) flows naturally downstream;
|
|
7
|
+
* only genuine input validation failures set the health flag.
|
|
8
|
+
*
|
|
9
|
+
* DSL: .transform( 'sq', 'ecgDeriv', { result: 'ecgSquared' }, { using: square } )
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
13
|
+
|
|
14
|
+
// ── Supported Features ──────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const SUPPORTED_STATS = [ 'result' ];
|
|
17
|
+
|
|
18
|
+
const STAT_DESCRIPTIONS = {
|
|
19
|
+
result: 'Transformed value: using(x)'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
23
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
24
|
+
enable: 'Enables node processing',
|
|
25
|
+
disable: 'Disables node processing',
|
|
26
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
27
|
+
unpause: 'Resumes node processing after pause'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const NODE_TYPE = 'Transform';
|
|
31
|
+
|
|
32
|
+
const CAPABILITIES = {
|
|
33
|
+
description: 'Applies a user-supplied pure function to each sample',
|
|
34
|
+
features: [
|
|
35
|
+
'Applies a pure function to each sample: result = using(x)',
|
|
36
|
+
'Validates input for NaN/Infinity; transform-produced NaN flows naturally downstream',
|
|
37
|
+
'Ships with pre-built helpers: square, abs, sqrt, log, log10, reciprocal, negate'
|
|
38
|
+
]
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ── Default Options ─────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
export const DEFAULT_OPTIONS = Object.create( null );
|
|
44
|
+
|
|
45
|
+
// ── Getter Functions (defensive copies) ─────────────────────────
|
|
46
|
+
|
|
47
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
48
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
49
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
50
|
+
export const getNodeType = () => NODE_TYPE;
|
|
51
|
+
export const getCapabilities = () => ( {
|
|
52
|
+
...CAPABILITIES,
|
|
53
|
+
features: CAPABILITIES.features.slice()
|
|
54
|
+
} );
|
|
55
|
+
|
|
56
|
+
// ── DSL Metadata ────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
const DSL_METADATA = {
|
|
59
|
+
specSchema: {
|
|
60
|
+
nodeType: {
|
|
61
|
+
type: 'string',
|
|
62
|
+
required: true,
|
|
63
|
+
value: NODE_TYPE
|
|
64
|
+
},
|
|
65
|
+
name: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
required: true,
|
|
68
|
+
validator: validators.identifier,
|
|
69
|
+
error: 'Name must be a valid identifier'
|
|
70
|
+
},
|
|
71
|
+
from: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
required: true,
|
|
74
|
+
properties: {
|
|
75
|
+
x: {
|
|
76
|
+
type: 'string',
|
|
77
|
+
required: true,
|
|
78
|
+
validator: validators.noSpaces,
|
|
79
|
+
error: 'Field name cannot contain spaces'
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
stats: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
required: true,
|
|
86
|
+
propertyNames: SUPPORTED_STATS,
|
|
87
|
+
propertySchema: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
required: true,
|
|
90
|
+
properties: {
|
|
91
|
+
storeAs: {
|
|
92
|
+
type: 'string',
|
|
93
|
+
required: true,
|
|
94
|
+
minLength: 1,
|
|
95
|
+
validator: validators.identifier,
|
|
96
|
+
error: 'storeAs must be a valid identifier'
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
using: {
|
|
102
|
+
type: 'function',
|
|
103
|
+
required: true,
|
|
104
|
+
arity: 1,
|
|
105
|
+
error: '"using" must be a pure function with exactly one parameter'
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
buildSpec: ( name, x, stats, options ) => ( {
|
|
110
|
+
nodeType: NODE_TYPE,
|
|
111
|
+
name,
|
|
112
|
+
from: { x },
|
|
113
|
+
stats,
|
|
114
|
+
...options
|
|
115
|
+
} )
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Publish-to function for the transform node.
|
|
3
|
+
*
|
|
4
|
+
* Copies the computed result to the output message. When input
|
|
5
|
+
* validation failed, publishes NaN via the standard helper.
|
|
6
|
+
* When the transform produced NaN (e.g. sqrt of negative),
|
|
7
|
+
* the NaN flows through naturally — no flag, no special handling.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { publishNaN } from '../../core/utils/node/index.js';
|
|
11
|
+
|
|
12
|
+
const publishTo = function ( state, msg ) {
|
|
13
|
+
if ( state.disable ) return;
|
|
14
|
+
|
|
15
|
+
if ( state.inputValidationFailed ) {
|
|
16
|
+
publishNaN( state, msg );
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
msg[ state.stats.result.storeAs ] = state.result;
|
|
21
|
+
}; // publishTo()
|
|
22
|
+
|
|
23
|
+
export default publishTo;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Recompute function for the transform node.
|
|
3
|
+
*
|
|
4
|
+
* No numerical stability concerns — the node is stateless with
|
|
5
|
+
* no accumulators, running sums, or recursive filters.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const recompute = function () {
|
|
9
|
+
return true;
|
|
10
|
+
}; // recompute()
|
|
11
|
+
|
|
12
|
+
export default recompute;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Update function for the transform node.
|
|
3
|
+
*
|
|
4
|
+
* Applies the user-supplied pure function to the input value.
|
|
5
|
+
* Input validation uses the standard `inputValidationFailed` flag.
|
|
6
|
+
* Transform-produced NaN (e.g. sqrt of negative) is NOT flagged —
|
|
7
|
+
* the node is healthy; the math produced NaN. Downstream nodes
|
|
8
|
+
* catch it via their own input validation.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const update = function ( state, msg ) {
|
|
12
|
+
if ( state.disable || state.pause ) return state;
|
|
13
|
+
|
|
14
|
+
state.inputValidationFailed = false;
|
|
15
|
+
|
|
16
|
+
const xVal = msg[ state.x ];
|
|
17
|
+
|
|
18
|
+
if ( !Number.isFinite( xVal ) ) {
|
|
19
|
+
state.inputValidationFailed = true;
|
|
20
|
+
return state;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
state.result = state.using( xVal );
|
|
25
|
+
} catch {
|
|
26
|
+
state.result = NaN;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return state;
|
|
30
|
+
}; // update()
|
|
31
|
+
|
|
32
|
+
export default update;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// nodes/trend/compute-confidence.js
|
|
2
|
+
|
|
3
|
+
const computeConfidence = function ( state ) {
|
|
4
|
+
// During warmup: linear ramp from 0 to 1 based on sample count
|
|
5
|
+
// Provides gradual trust building as statistics stabilize
|
|
6
|
+
if ( state.samples < state.warmupSamples ) {
|
|
7
|
+
const ramp = state.samples / Math.max( state.warmupSamples, 1 );
|
|
8
|
+
return ( ramp < 0 ) ? 0 : ( ramp > 1 ? 1 : ramp );
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const epsilon = state.epsilon ?? 1e-12;
|
|
12
|
+
|
|
13
|
+
// Prevent division by zero while preserving user's threshold intent
|
|
14
|
+
// If threshold is 0, user wants to detect ANY change, so use tiny epsilon
|
|
15
|
+
const safeThreshold = ( state.rocThreshold > 0 ) ? state.rocThreshold : epsilon;
|
|
16
|
+
|
|
17
|
+
// Compute noise level (standard deviation) with numerical safety
|
|
18
|
+
// Variance can be 0 for perfectly stable signals or negative due to numerical errors
|
|
19
|
+
const rawStd = Math.sqrt( Math.max( state.rocVariance, 0 ) );
|
|
20
|
+
const stddev = ( rawStd > epsilon ) ? rawStd : epsilon;
|
|
21
|
+
|
|
22
|
+
// Persistence: rewards consistent state classification
|
|
23
|
+
// tanh(x/5) gives: 0.2 at 1 sample, 0.76 at 5, 0.999 at 20
|
|
24
|
+
const persistence = Math.tanh( state.consistentSamples / 5 );
|
|
25
|
+
const persistenceFactor = ( 0.5 + ( 0.5 * persistence ) );
|
|
26
|
+
const absRoCMean = Math.abs( state.rocMean );
|
|
27
|
+
|
|
28
|
+
if ( state.trend === 'stable' ) {
|
|
29
|
+
// STABLE CONFIDENCE: "How confident are we that this is truly stable?"
|
|
30
|
+
|
|
31
|
+
// Quietness factor: confidence that noise won't cause false trends
|
|
32
|
+
// - When stddev << threshold: q ≈ 0, quietness ≈ 1 (high confidence)
|
|
33
|
+
// - When stddev ≈ threshold: q ≈ 1, quietness ≈ 0.5 (uncertain)
|
|
34
|
+
// - When stddev >> threshold: q >> 1, quietness ≈ 0 (no confidence)
|
|
35
|
+
// Quadratic denominator provides smooth, intuitive decay
|
|
36
|
+
const q = stddev / safeThreshold;
|
|
37
|
+
const quietness = 1 / ( 1 + ( q * q ) );
|
|
38
|
+
|
|
39
|
+
// Margin factor: confidence we're not near the threshold boundary
|
|
40
|
+
// - At roc = 0: margin = 1 (center of stable zone)
|
|
41
|
+
// - At roc = threshold: margin = 0 (edge of stability)
|
|
42
|
+
// This prevents high confidence when hovering near transition
|
|
43
|
+
const r = absRoCMean / safeThreshold;
|
|
44
|
+
const margin = 1 - Math.min( 1, r );
|
|
45
|
+
|
|
46
|
+
// Combine factors: far from edge AND low noise AND persistent = high confidence
|
|
47
|
+
const confStable = margin * quietness * persistenceFactor;
|
|
48
|
+
return ( confStable < 0 ) ? 0 : ( confStable > 1 ? 1 : confStable );
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// TRENDING CONFIDENCE: "How confident are we in this trend direction?"
|
|
52
|
+
|
|
53
|
+
// Signal clarity: signal-to-noise ratio of the *mean roc estimate*.
|
|
54
|
+
//
|
|
55
|
+
// The per-sample SNR (|rocMean| / stddev) answers: "is each individual
|
|
56
|
+
// roc sample clearly positive?" But the relevant question for trend
|
|
57
|
+
// confidence is: "given all the samples I've seen, is the *average*
|
|
58
|
+
// roc clearly positive?"
|
|
59
|
+
//
|
|
60
|
+
// The standard error of the EWMA mean is stddev / sqrt(n_eff), where
|
|
61
|
+
// n_eff ≈ (2 / alpha) - 1 is the effective sample size of the EWMA.
|
|
62
|
+
// Scaling by sqrt(n_eff) converts per-sample SNR to mean-estimate SNR.
|
|
63
|
+
//
|
|
64
|
+
// Example: per-sample SNR = 0.5 with n_eff = 35 gives
|
|
65
|
+
// SNR_mean = 0.5 × sqrt(35) ≈ 2.96 → signalClarity ≈ 0.76
|
|
66
|
+
// vs. signalClarity ≈ 0.16 without the correction.
|
|
67
|
+
//
|
|
68
|
+
// Cap n_eff at actual post-warmup samples to avoid over-confidence early.
|
|
69
|
+
const nEff = Math.min( ( 2 / state.rocAlpha ) - 1, state.samples - state.warmupSamples );
|
|
70
|
+
const safeNEff = ( nEff > 1 ) ? nEff : 1;
|
|
71
|
+
state.snr = ( absRoCMean / stddev ) * Math.sqrt( safeNEff );
|
|
72
|
+
const signalClarity = Math.tanh( state.snr / 3 );
|
|
73
|
+
|
|
74
|
+
// Boundary clarity: how far above the classification threshold?
|
|
75
|
+
// - Just above (roc ≈ 1.1×threshold): overshoot ≈ 0.1, clarity ≈ 0.1
|
|
76
|
+
// - Well above (roc = 2×threshold): overshoot = 1, clarity ≈ 0.76
|
|
77
|
+
// - Far above (roc = 5×threshold): overshoot = 4, clarity ≈ 0.999
|
|
78
|
+
// Using threshold-normalized ratio makes this parameter-agnostic
|
|
79
|
+
const overshoot = Math.max( 0, ( absRoCMean / safeThreshold ) - 1 );
|
|
80
|
+
const boundaryClarity = Math.tanh( overshoot );
|
|
81
|
+
|
|
82
|
+
// For trending states, we intentionally DO NOT apply the quietness penalty.
|
|
83
|
+
// Here's why:
|
|
84
|
+
//
|
|
85
|
+
// The quietness factor (1/(1+q²) where q = stddev/threshold) was designed to
|
|
86
|
+
// penalize high noise relative to the threshold. This makes sense for STABLE
|
|
87
|
+
// states where we want confidence that noise won't cause false trend detection.
|
|
88
|
+
//
|
|
89
|
+
// However, for TRENDING states, applying quietness creates redundant penalties:
|
|
90
|
+
// 1. signalClarity already accounts for noise via SNR (signal/noise ratio)
|
|
91
|
+
// 2. boundaryClarity ensures we're sufficiently above the threshold
|
|
92
|
+
//
|
|
93
|
+
// Adding quietness on top would triple-penalize for noise, causing even strong
|
|
94
|
+
// trends (SNR > 20) to have low confidence if stddev happens to be comparable
|
|
95
|
+
// to the threshold. This is overly conservative.
|
|
96
|
+
//
|
|
97
|
+
// Example: A perfect linear trend with roc=1.0, threshold=0.1, stddev=0.15
|
|
98
|
+
// - SNR = 6.7 (excellent signal!)
|
|
99
|
+
// - boundaryClarity = tanh(9) ≈ 1.0 (far above threshold!)
|
|
100
|
+
// - But quietness = 1/(1+1.5²) = 0.31 (harsh penalty)
|
|
101
|
+
// - Result: confidence = 0.31 instead of ~0.9
|
|
102
|
+
//
|
|
103
|
+
// For trending states, if the SNR is high and we're well above threshold,
|
|
104
|
+
// the absolute noise level is less relevant - the trend is clearly real.
|
|
105
|
+
// Therefore, we omit the quietness factor for rising/falling states.
|
|
106
|
+
const confTrend = signalClarity * boundaryClarity * persistenceFactor;
|
|
107
|
+
|
|
108
|
+
// Ensure output is strictly in [0,1] despite numerical errors
|
|
109
|
+
return ( confTrend < 0 ) ? 0 : ( confTrend > 1 ? 1 : confTrend );
|
|
110
|
+
}; // computeConfidence()
|
|
111
|
+
|
|
112
|
+
export { computeConfidence };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// nodes/trend/index.js
|
|
2
|
+
|
|
3
|
+
export { default as init } from './init.js';
|
|
4
|
+
export { default as update } from './update.js';
|
|
5
|
+
export { default as reset } from './reset.js';
|
|
6
|
+
export { default as recompute } from './recompute.js';
|
|
7
|
+
export { default as publishTo } from './publish-to.js';
|
|
8
|
+
// Direct imports for common control functions
|
|
9
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
10
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
11
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
12
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
13
|
+
// Re-export everything from introspection.js
|
|
14
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// nodes/trend/init.js
|
|
2
|
+
|
|
3
|
+
import { validateWithSchema } from '../../core/utils/validate/index.js';
|
|
4
|
+
import { getDSLMetadata, getNodeType, DEFAULT_OPTIONS } from './introspect.js';
|
|
5
|
+
import { halfLifeToAlpha, halfLifeToWarmupSamples } from '../../core/utils/half-life/index.js';
|
|
6
|
+
import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
|
|
7
|
+
import { asTunable } from '../../core/tunable/index.js';
|
|
8
|
+
|
|
9
|
+
const init = function ( spec ) {
|
|
10
|
+
// Validate specification
|
|
11
|
+
const metadata = getDSLMetadata();
|
|
12
|
+
const validation = validateWithSchema(
|
|
13
|
+
{
|
|
14
|
+
...metadata.specSchema,
|
|
15
|
+
_crossFieldValidators: metadata.crossFieldValidators
|
|
16
|
+
},
|
|
17
|
+
spec,
|
|
18
|
+
'spec'
|
|
19
|
+
);
|
|
20
|
+
validation.throwIfInvalid( getNodeType() );
|
|
21
|
+
|
|
22
|
+
// Create state after validation passes
|
|
23
|
+
const state = Object.create( null );
|
|
24
|
+
// Standard input parameter health flag during update: false means healthy
|
|
25
|
+
state.inputValidationFailed = false;
|
|
26
|
+
// Ensure node is enabled by default
|
|
27
|
+
state.disable = false;
|
|
28
|
+
state.pause = false;
|
|
29
|
+
|
|
30
|
+
// Core configuration
|
|
31
|
+
state.x = spec.from.x;
|
|
32
|
+
state.stats = spec.stats;
|
|
33
|
+
|
|
34
|
+
// Apply parameters with defaults (validation doesn't enforce them)
|
|
35
|
+
// Supports direct, field-keyed, and tunable specification for tunable params
|
|
36
|
+
const rocStatsHalfLifeSpec = resolveScalar( spec.rocStatsHalfLife, state.x );
|
|
37
|
+
const rocThresholdSpec = resolveScalar( spec.rocThreshold, state.x );
|
|
38
|
+
const speedUpSpec = resolveScalar( spec.speedUp, state.x );
|
|
39
|
+
const warmupSamplesSpec = resolveScalar( spec.warmupSamples, state.x );
|
|
40
|
+
|
|
41
|
+
// Structural params (affect derived values at init time)
|
|
42
|
+
state.rocStatsHalfLife = rocStatsHalfLifeSpec ?? DEFAULT_OPTIONS.rocStatsHalfLife;
|
|
43
|
+
state.rocAlpha = halfLifeToAlpha( state.rocStatsHalfLife );
|
|
44
|
+
state.speedUp = speedUpSpec ?? DEFAULT_OPTIONS.speedUp;
|
|
45
|
+
state.warmupSamples = warmupSamplesSpec ?? halfLifeToWarmupSamples( state.rocStatsHalfLife, 0.8 );
|
|
46
|
+
state.warmupSamplesForAccel = Math.ceil( state.warmupSamples * 1.5 );
|
|
47
|
+
|
|
48
|
+
// rocThreshold supports tunable for phase-dependent sensitivity
|
|
49
|
+
state.rocThresholdFn = asTunable( rocThresholdSpec ?? DEFAULT_OPTIONS.rocThreshold );
|
|
50
|
+
state.rocThreshold = DEFAULT_OPTIONS.rocThreshold;
|
|
51
|
+
state.tunableErrorLogged = false;
|
|
52
|
+
|
|
53
|
+
state.previousValue = null;
|
|
54
|
+
|
|
55
|
+
// roc statistics
|
|
56
|
+
state.rocMean = 0;
|
|
57
|
+
state.rocVariance = 0;
|
|
58
|
+
|
|
59
|
+
// Acceleration hint configuration
|
|
60
|
+
if ( state.stats.accelerationHint ) {
|
|
61
|
+
state.rocSmoothedFast = 0;
|
|
62
|
+
state.accelAlpha = halfLifeToAlpha( state.rocStatsHalfLife / state.speedUp );
|
|
63
|
+
state.accelerationHint = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Tracking
|
|
67
|
+
state.samples = 0;
|
|
68
|
+
state.consistentSamples = 0;
|
|
69
|
+
state.trend = 'learning';
|
|
70
|
+
state.previousTrend = 'learning';
|
|
71
|
+
state.confidence = 0;
|
|
72
|
+
state.snr = 0;
|
|
73
|
+
// accelThreshold is derived from rocThreshold at runtime (rocThreshold may be dynamic)
|
|
74
|
+
|
|
75
|
+
// Node metadata
|
|
76
|
+
state.nodeType = getNodeType();
|
|
77
|
+
|
|
78
|
+
return state;
|
|
79
|
+
}; // init()
|
|
80
|
+
|
|
81
|
+
export default init;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// nodes/trend/introspect.js
|
|
2
|
+
|
|
3
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
4
|
+
|
|
5
|
+
/** Single source of truth for what this plugin can do */
|
|
6
|
+
const SUPPORTED_STATS = [
|
|
7
|
+
'trend',
|
|
8
|
+
'confidence',
|
|
9
|
+
'rocMean',
|
|
10
|
+
'accelerationHint'
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
/** Human-readable descriptions (for introspection) */
|
|
14
|
+
const STAT_DESCRIPTIONS = {
|
|
15
|
+
trend: 'Trend direction: learning, stable, rising, or falling',
|
|
16
|
+
confidence: 'Confidence score between 0 and 1',
|
|
17
|
+
rocMean: 'Mean Rate of change (first derivative)',
|
|
18
|
+
accelerationHint: 'Acceleration hint: likely_accelerating, likely_decelerating, or null'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Control methods available for triggers */
|
|
22
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
23
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
24
|
+
enable: 'Enables node processing',
|
|
25
|
+
disable: 'Disables node processing',
|
|
26
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
27
|
+
unpause: 'Resumes node processing after pause'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** The type of this node */
|
|
31
|
+
const NODE_TYPE = 'Trend';
|
|
32
|
+
|
|
33
|
+
/** Node capabilities for documentation */
|
|
34
|
+
const CAPABILITIES = {
|
|
35
|
+
description: 'Rate Of Change-domain (first-difference) trend detection with exponentially smoothed roc statistics; no internal smoothing for clean composability.',
|
|
36
|
+
features: [
|
|
37
|
+
'States: learning, stable, rising, falling',
|
|
38
|
+
'Confidence: SNR × persistence (tanh mapping)',
|
|
39
|
+
'Domain knob: rocThreshold (set ≳ noise of the roc signal)',
|
|
40
|
+
'Optional acceleration hint (fast/slow EWMAs on roc, gated)',
|
|
41
|
+
'Warm-up derived from roc half-life'
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Export functions for accessing metadata
|
|
46
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
47
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
48
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
49
|
+
export const getNodeType = () => NODE_TYPE;
|
|
50
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
51
|
+
|
|
52
|
+
// Spec default values
|
|
53
|
+
export const DEFAULT_OPTIONS = {
|
|
54
|
+
rocStatsHalfLife: 9,
|
|
55
|
+
rocThreshold: 0.1,
|
|
56
|
+
speedUp: 2
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Parameters that support tunable (dynamic) values */
|
|
60
|
+
export const TUNABLE_PARAMS = [ 'rocThreshold' ];
|
|
61
|
+
|
|
62
|
+
// DSL Metadata for transpilation
|
|
63
|
+
const DSL_METADATA = {
|
|
64
|
+
specSchema: {
|
|
65
|
+
nodeType: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
required: true,
|
|
68
|
+
value: NODE_TYPE
|
|
69
|
+
},
|
|
70
|
+
name: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
required: true,
|
|
73
|
+
validator: validators.identifier,
|
|
74
|
+
error: 'Name must be a valid identifier'
|
|
75
|
+
},
|
|
76
|
+
from: {
|
|
77
|
+
type: 'object',
|
|
78
|
+
required: true,
|
|
79
|
+
properties: {
|
|
80
|
+
x: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
required: true,
|
|
83
|
+
validator: validators.noSpaces,
|
|
84
|
+
error: 'Field name cannot contain spaces'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
stats: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
required: true,
|
|
91
|
+
minProperties: 1,
|
|
92
|
+
propertyNames: SUPPORTED_STATS,
|
|
93
|
+
propertySchema: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
storeAs: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
required: true,
|
|
99
|
+
validator: validators.identifier,
|
|
100
|
+
error: 'storeAs must be a valid identifier'
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
rocStatsHalfLife: {
|
|
106
|
+
type: 'numberOrFieldKeyed',
|
|
107
|
+
required: false,
|
|
108
|
+
default: DEFAULT_OPTIONS.rocStatsHalfLife,
|
|
109
|
+
min: 2,
|
|
110
|
+
validator: validators.positive,
|
|
111
|
+
error: 'roc stats half-life must be minimum 2'
|
|
112
|
+
},
|
|
113
|
+
rocThreshold: {
|
|
114
|
+
type: 'numberOrFunctionOrFieldKeyed',
|
|
115
|
+
required: false,
|
|
116
|
+
default: DEFAULT_OPTIONS.rocThreshold,
|
|
117
|
+
// Supports tunable for phase-dependent sensitivity (warmup vs steady-state)
|
|
118
|
+
validator: validators.nonNegativeOrFunction,
|
|
119
|
+
error: 'Threshold must be non-negative or function'
|
|
120
|
+
},
|
|
121
|
+
warmupSamples: {
|
|
122
|
+
type: 'numberOrFieldKeyed',
|
|
123
|
+
required: false,
|
|
124
|
+
integer: true,
|
|
125
|
+
min: 3,
|
|
126
|
+
validator: validators.positiveInteger,
|
|
127
|
+
error: 'Warmup samples must be at least 3'
|
|
128
|
+
},
|
|
129
|
+
speedUp: {
|
|
130
|
+
type: 'numberOrFieldKeyed',
|
|
131
|
+
required: false,
|
|
132
|
+
default: DEFAULT_OPTIONS.speedUp,
|
|
133
|
+
validator: validators.inRange( 1.5, 3 ),
|
|
134
|
+
error: 'speedUp must be between 1.5 and 3'
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
crossFieldValidators: [],
|
|
139
|
+
|
|
140
|
+
// Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
|
|
141
|
+
buildSpec: ( name, x, stats, options ) => ( {
|
|
142
|
+
nodeType: NODE_TYPE,
|
|
143
|
+
name,
|
|
144
|
+
from: { x },
|
|
145
|
+
stats,
|
|
146
|
+
...options
|
|
147
|
+
} ),
|
|
148
|
+
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// nodes/trend/publish-to.js
|
|
2
|
+
|
|
3
|
+
import { publishNaN } from '../../core/utils/node/index.js';
|
|
4
|
+
|
|
5
|
+
const publishTo = function ( state, msg ) {
|
|
6
|
+
if ( state.disable ) return;
|
|
7
|
+
// Propagate NaN if unhealthy
|
|
8
|
+
if ( state.inputValidationFailed ) {
|
|
9
|
+
publishNaN( state, msg );
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Iterate only over configured stats
|
|
14
|
+
const stats = state.stats;
|
|
15
|
+
for ( const statName in stats ) {
|
|
16
|
+
if ( Object.prototype.hasOwnProperty.call(stats, statName ) ) {
|
|
17
|
+
msg[ stats[ statName ].storeAs ] = state[ statName ];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}; // publishTo()
|
|
21
|
+
|
|
22
|
+
export default publishTo;
|