@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,546 @@
|
|
|
1
|
+
// core/source-manager/mqtt/status.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Health and metrics reporter for the MQTT source.
|
|
5
|
+
*
|
|
6
|
+
* One small state machine owns everything the source tells the
|
|
7
|
+
* outside world. client.js maps mqtt.js events onto reporter calls
|
|
8
|
+
* 1:1; the reporter decides what is worth emitting and in what shape.
|
|
9
|
+
* Splitting it out keeps the transport wiring and the reporting rules
|
|
10
|
+
* separately testable — the specs drive this module with an injected
|
|
11
|
+
* clock and no MQTT client at all.
|
|
12
|
+
*
|
|
13
|
+
* What it emits, on which channel:
|
|
14
|
+
* - `onStatus` — the ADR-018 core payload `{status, connected,
|
|
15
|
+
* phase}` with `error: {code, message}` on the error path, plus
|
|
16
|
+
* this adapter's additions: `msSinceLastMsg`, and `note` on a
|
|
17
|
+
* forced stop.
|
|
18
|
+
* Emitted on every state TRANSITION (a change of the status /
|
|
19
|
+
* phase / error-code tuple — repeats are suppressed, so a retry
|
|
20
|
+
* storm cannot flood the channel), and once PER RECORD for every
|
|
21
|
+
* decode failure and every transform throw (ADR-018: a skipped
|
|
22
|
+
* record is never silent).
|
|
23
|
+
* - `onMetrics` (optional) — a fresh snapshot of the monotonic
|
|
24
|
+
* counters `{delivered, skipped, decodeErrors, reconnects,
|
|
25
|
+
* dedupHits, dedupMisses, dedupBypassed, dedupCacheSize}`, emitted
|
|
26
|
+
* on every tick() (the client's 1 Hz cadence) and on every health
|
|
27
|
+
* transition. Consumers diff snapshots; nothing is ever reset.
|
|
28
|
+
*
|
|
29
|
+
* Health rules (evaluated on events and on tick):
|
|
30
|
+
* - RED — a subscribe failure (connected but deaf — no retry
|
|
31
|
+
* happens until the next reconnect, so waiting for
|
|
32
|
+
* repetition could wait forever); or disconnected for
|
|
33
|
+
* more than DISCONNECT_RED_MS (strictly greater).
|
|
34
|
+
* - YELLOW — offline / reconnecting (the ADR-018 two-tier rule: the
|
|
35
|
+
* phase says the source is still alive); or a decode-error
|
|
36
|
+
* ratio above 1 % over the last DECODE_RING_SIZE messages
|
|
37
|
+
* (strictly greater); or, when `expectedQuietPeriodMs` is
|
|
38
|
+
* configured, no packet for longer than that.
|
|
39
|
+
* - GREEN — otherwise.
|
|
40
|
+
* Precedence is the order above; the first rule that matches names
|
|
41
|
+
* the code.
|
|
42
|
+
*
|
|
43
|
+
* Hot-path allocation profile (ADR-018 zero-alloc — allocation only where
|
|
44
|
+
* unavoidable, each residual stated):
|
|
45
|
+
* - Counter bumps, ring writes, and the health evaluation are plain
|
|
46
|
+
* integer work on pre-allocated state; no per-message allocation
|
|
47
|
+
* in our code.
|
|
48
|
+
* - Payloads ARE allocated at emission — transitions (rare by
|
|
49
|
+
* construction), per-record decode reports (mandated by ADR-018), and
|
|
50
|
+
* metrics snapshots (1 Hz). Snapshots are fresh objects because a
|
|
51
|
+
* consumer may retain them; mutating a shared one under the
|
|
52
|
+
* caller's feet is a footgun we refuse.
|
|
53
|
+
* - `nowFn()` (Date.now) runs once per received message to feed the
|
|
54
|
+
* quiet-period clock — the same transient boxing the dedup cache
|
|
55
|
+
* already documents.
|
|
56
|
+
*
|
|
57
|
+
* Callers without an `onStatus` handler still see error-path
|
|
58
|
+
* payloads via a classified `console.error` line (the CSV source's
|
|
59
|
+
* fallback pattern); lifecycle payloads stay quiet.
|
|
60
|
+
*
|
|
61
|
+
* ASSUMPTIONS
|
|
62
|
+
* -----------
|
|
63
|
+
* 1. All reporter calls come from one event loop (mqtt.js event
|
|
64
|
+
* handlers plus one interval) — there is no locking.
|
|
65
|
+
* 2. The clock (`nowFn`) moves forward; the time rules use plain
|
|
66
|
+
* subtraction.
|
|
67
|
+
*
|
|
68
|
+
* LIMITATIONS
|
|
69
|
+
* -----------
|
|
70
|
+
* 1. Transition suppression keys on (status, phase, error code) —
|
|
71
|
+
* a repeat of the same tuple with a DIFFERENT message text is
|
|
72
|
+
* not re-emitted. Deliberate: the tuple is what a consumer acts
|
|
73
|
+
* on, and re-emitting per text would let a retry storm flood
|
|
74
|
+
* the channel.
|
|
75
|
+
* 2. Counters are plain JS numbers: exact to 2^53. At one million
|
|
76
|
+
* messages per second that is ~285 years of uptime — not the
|
|
77
|
+
* binding constraint.
|
|
78
|
+
*
|
|
79
|
+
* @see src/core/source-manager/mqtt/client.js - The transport wiring
|
|
80
|
+
* @see ADR-018 - lifecycle phases, status shapes, error codes
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
import {
|
|
84
|
+
DISCONNECT_RED_MS,
|
|
85
|
+
DECODE_RING_SIZE
|
|
86
|
+
} from './constants.js';
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// VALIDATION HELPERS
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Throws a classified INVALID_CONFIG error (ADR-018 error vocabulary)
|
|
94
|
+
* when `value` is present but not a function.
|
|
95
|
+
*
|
|
96
|
+
* @param {*} value - Candidate value
|
|
97
|
+
* @param {string} name - Field name for the error message
|
|
98
|
+
*/
|
|
99
|
+
const assertOptionalFunction = function ( value, name ) {
|
|
100
|
+
if ( value !== undefined && value !== null && typeof value !== 'function' ) {
|
|
101
|
+
const err = new Error( `WinkComposer/mqtt-source: ${name} must be a function` );
|
|
102
|
+
err.code = 'INVALID_CONFIG';
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// STATUS REPORTER FACTORY
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Create the source's status/metrics reporter.
|
|
113
|
+
*
|
|
114
|
+
* @param {Object} [options={}] - Reporter options
|
|
115
|
+
* @param {function} [options.onStatus] - Structured status callback
|
|
116
|
+
* @param {function} [options.onMetrics] - Counter-snapshot callback
|
|
117
|
+
* @param {number} [options.expectedQuietPeriodMs] - Opt-in quiet rule:
|
|
118
|
+
* yellow when no packet arrives for longer than this
|
|
119
|
+
* @param {function} [options.dedupSizeFn] - Live dedup-cache size read
|
|
120
|
+
* @param {function} [options.nowFn=Date.now] - Clock source. Injection
|
|
121
|
+
* point for deterministic tests; production uses the default
|
|
122
|
+
* @returns {Object} Reporter with lifecycle notes, hot-path counters,
|
|
123
|
+
* tick and snapshot
|
|
124
|
+
*/
|
|
125
|
+
const createStatusReporter = function ( options = {} ) {
|
|
126
|
+
if ( typeof options !== 'object' || options === null ) {
|
|
127
|
+
const err = new Error( 'WinkComposer/mqtt-source: status reporter options must be an object' );
|
|
128
|
+
err.code = 'INVALID_CONFIG';
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const {
|
|
133
|
+
onStatus = null,
|
|
134
|
+
onMetrics = null,
|
|
135
|
+
expectedQuietPeriodMs = null,
|
|
136
|
+
dedupSizeFn = null,
|
|
137
|
+
nowFn = Date.now
|
|
138
|
+
} = options;
|
|
139
|
+
|
|
140
|
+
assertOptionalFunction( onStatus, 'onStatus' );
|
|
141
|
+
assertOptionalFunction( onMetrics, 'onMetrics' );
|
|
142
|
+
assertOptionalFunction( dedupSizeFn, 'dedupSizeFn' );
|
|
143
|
+
if ( typeof nowFn !== 'function' ) {
|
|
144
|
+
const err = new Error( 'WinkComposer/mqtt-source: nowFn must be a function' );
|
|
145
|
+
err.code = 'INVALID_CONFIG';
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
if ( expectedQuietPeriodMs !== null &&
|
|
149
|
+
( typeof expectedQuietPeriodMs !== 'number' ||
|
|
150
|
+
expectedQuietPeriodMs < 1 ||
|
|
151
|
+
!Number.isInteger( expectedQuietPeriodMs ) ) ) {
|
|
152
|
+
const err = new Error( 'WinkComposer/mqtt-source: expectedQuietPeriodMs must be a positive integer' );
|
|
153
|
+
err.code = 'INVALID_CONFIG';
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── Connection & phase state ─────────────────────────────────────
|
|
158
|
+
const startedAt = nowFn();
|
|
159
|
+
let phase = 'starting';
|
|
160
|
+
let connected = false;
|
|
161
|
+
let disconnectedAt = startedAt; // Outage clock; reset on each fresh disconnect
|
|
162
|
+
let lastReceivedAt = startedAt; // Any packet counts — even a duplicate
|
|
163
|
+
let connectCount = 0;
|
|
164
|
+
let subscribeFailed = false;
|
|
165
|
+
let lastSubscribeError = null;
|
|
166
|
+
let lastConnectError = null;
|
|
167
|
+
|
|
168
|
+
// ── Monotonic counters (bumped inline, never reset) ──────────────
|
|
169
|
+
let delivered = 0;
|
|
170
|
+
let skipped = 0;
|
|
171
|
+
let decodeErrors = 0;
|
|
172
|
+
let reconnects = 0;
|
|
173
|
+
let dedupHits = 0;
|
|
174
|
+
let dedupMisses = 0;
|
|
175
|
+
let dedupBypassed = 0;
|
|
176
|
+
|
|
177
|
+
// ── Decode-outcome ring: last DECODE_RING_SIZE results ───────────
|
|
178
|
+
// Pre-allocated once; 1 = decode failure, 0 = success.
|
|
179
|
+
const ring = new Uint8Array( DECODE_RING_SIZE );
|
|
180
|
+
let ringSum = 0;
|
|
181
|
+
let ringIdx = 0;
|
|
182
|
+
let ringCount = 0;
|
|
183
|
+
|
|
184
|
+
// ── Emission de-duplication state ────────────────────────────────
|
|
185
|
+
let lastStatus = null;
|
|
186
|
+
let lastPhase = null;
|
|
187
|
+
let lastCode = null;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Record one decode outcome in the ring.
|
|
191
|
+
*
|
|
192
|
+
* @param {number} isError - 1 for a decode failure, 0 for success
|
|
193
|
+
*/
|
|
194
|
+
const ringPush = function ( isError ) {
|
|
195
|
+
ringSum -= ring[ ringIdx ];
|
|
196
|
+
ring[ ringIdx ] = isError;
|
|
197
|
+
ringSum += isError;
|
|
198
|
+
ringIdx = ( ringIdx + 1 ) % DECODE_RING_SIZE;
|
|
199
|
+
if ( ringCount < DECODE_RING_SIZE ) {
|
|
200
|
+
ringCount += 1;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Build a fresh counter snapshot. Fresh per call — consumers may
|
|
206
|
+
* retain it.
|
|
207
|
+
*
|
|
208
|
+
* @returns {Object} Monotonic counters + live dedup cache size
|
|
209
|
+
*/
|
|
210
|
+
const snapshot = function () {
|
|
211
|
+
return {
|
|
212
|
+
delivered,
|
|
213
|
+
skipped,
|
|
214
|
+
decodeErrors,
|
|
215
|
+
reconnects,
|
|
216
|
+
dedupHits,
|
|
217
|
+
dedupMisses,
|
|
218
|
+
dedupBypassed,
|
|
219
|
+
dedupCacheSize: dedupSizeFn ? dedupSizeFn() : 0
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Emit a metrics snapshot when the caller asked for one.
|
|
225
|
+
*/
|
|
226
|
+
const emitMetrics = function () {
|
|
227
|
+
if ( onMetrics ) {
|
|
228
|
+
onMetrics( snapshot() );
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Route a status payload: to the caller's handler when supplied;
|
|
234
|
+
* otherwise error-path payloads go to a classified console.error
|
|
235
|
+
* (never silent — ADR-018's two-party rule) and lifecycle
|
|
236
|
+
* payloads stay quiet.
|
|
237
|
+
*
|
|
238
|
+
* @param {Object} payload - The structured status payload
|
|
239
|
+
*/
|
|
240
|
+
const emitStatus = function ( payload ) {
|
|
241
|
+
if ( onStatus ) {
|
|
242
|
+
onStatus( payload );
|
|
243
|
+
} else if ( payload.error ) {
|
|
244
|
+
console.error( `MQTT source error [${payload.error.code}]: ${payload.error.message}` );
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* The health rules, in precedence order. Returns the status and
|
|
250
|
+
* leaves the matching code in `healthCode` (closure variable, so
|
|
251
|
+
* the no-emission path allocates nothing).
|
|
252
|
+
*
|
|
253
|
+
* @param {number} now - Current timestamp
|
|
254
|
+
* @returns {string} 'green' | 'yellow' | 'red'
|
|
255
|
+
*/
|
|
256
|
+
let healthCode = null;
|
|
257
|
+
const computeHealth = function ( now ) {
|
|
258
|
+
healthCode = null;
|
|
259
|
+
if ( phase === 'stopped' ) {
|
|
260
|
+
return 'green';
|
|
261
|
+
}
|
|
262
|
+
if ( subscribeFailed ) {
|
|
263
|
+
healthCode = 'SUBSCRIBE_FAILED';
|
|
264
|
+
return 'red';
|
|
265
|
+
}
|
|
266
|
+
if ( !connected && ( ( now - disconnectedAt ) > DISCONNECT_RED_MS ) ) {
|
|
267
|
+
healthCode = 'CONNECTION_LOST';
|
|
268
|
+
return 'red';
|
|
269
|
+
}
|
|
270
|
+
if ( phase === 'offline' || phase === 'reconnecting' ) {
|
|
271
|
+
healthCode = lastConnectError === null ? null : 'CONNECT_FAILED';
|
|
272
|
+
return 'yellow';
|
|
273
|
+
}
|
|
274
|
+
if ( lastConnectError !== null ) {
|
|
275
|
+
healthCode = 'CONNECT_FAILED';
|
|
276
|
+
return 'yellow';
|
|
277
|
+
}
|
|
278
|
+
if ( ( ringSum * 100 ) > ringCount ) {
|
|
279
|
+
healthCode = 'DECODE_ERROR';
|
|
280
|
+
return 'yellow';
|
|
281
|
+
}
|
|
282
|
+
if ( expectedQuietPeriodMs !== null &&
|
|
283
|
+
( ( now - lastReceivedAt ) > expectedQuietPeriodMs ) ) {
|
|
284
|
+
healthCode = 'QUIET_PERIOD_EXCEEDED';
|
|
285
|
+
return 'yellow';
|
|
286
|
+
}
|
|
287
|
+
return 'green';
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Human message for a health code, built only at emission time.
|
|
292
|
+
*
|
|
293
|
+
* @param {string} code - The health code being emitted
|
|
294
|
+
* @param {number} now - Current timestamp
|
|
295
|
+
* @returns {string} Operator-facing message
|
|
296
|
+
*/
|
|
297
|
+
const healthMessage = function ( code, now ) {
|
|
298
|
+
if ( code === 'SUBSCRIBE_FAILED' ) {
|
|
299
|
+
return lastSubscribeError;
|
|
300
|
+
}
|
|
301
|
+
if ( code === 'CONNECTION_LOST' ) {
|
|
302
|
+
return `not connected for ${now - disconnectedAt}ms (red threshold ${DISCONNECT_RED_MS}ms)`;
|
|
303
|
+
}
|
|
304
|
+
if ( code === 'CONNECT_FAILED' ) {
|
|
305
|
+
return lastConnectError;
|
|
306
|
+
}
|
|
307
|
+
if ( code === 'DECODE_ERROR' ) {
|
|
308
|
+
return `decode-error ratio above 1% over the last ${ringCount} messages`;
|
|
309
|
+
}
|
|
310
|
+
// QUIET_PERIOD_EXCEEDED — the only code left.
|
|
311
|
+
return `no message received for ${now - lastReceivedAt}ms (expected quiet period ${expectedQuietPeriodMs}ms)`;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Re-derive health and emit ONE status (plus a metrics snapshot)
|
|
316
|
+
* when the (status, phase, code) tuple changed. The no-change
|
|
317
|
+
* path is allocation-free.
|
|
318
|
+
*/
|
|
319
|
+
const evaluate = function () {
|
|
320
|
+
const now = nowFn();
|
|
321
|
+
const status = computeHealth( now );
|
|
322
|
+
if ( status === lastStatus && phase === lastPhase && healthCode === lastCode ) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
lastStatus = status;
|
|
326
|
+
lastPhase = phase;
|
|
327
|
+
lastCode = healthCode;
|
|
328
|
+
|
|
329
|
+
const payload = {
|
|
330
|
+
status,
|
|
331
|
+
connected,
|
|
332
|
+
phase,
|
|
333
|
+
msSinceLastMsg: now - lastReceivedAt
|
|
334
|
+
};
|
|
335
|
+
if ( healthCode !== null ) {
|
|
336
|
+
payload.error = { code: healthCode, message: healthMessage( healthCode, now ) };
|
|
337
|
+
}
|
|
338
|
+
emitStatus( payload );
|
|
339
|
+
emitMetrics();
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
// ── Lifecycle notes (each may emit one transition) ───────────
|
|
344
|
+
|
|
345
|
+
/** Factory finished; the transport is coming up. */
|
|
346
|
+
starting: function () {
|
|
347
|
+
evaluate();
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
/** The transport connected. Counts re-connections; running
|
|
351
|
+
* is declared by subscribed(), not here. */
|
|
352
|
+
connected: function () {
|
|
353
|
+
connected = true;
|
|
354
|
+
connectCount += 1;
|
|
355
|
+
if ( connectCount > 1 ) {
|
|
356
|
+
reconnects += 1;
|
|
357
|
+
}
|
|
358
|
+
lastConnectError = null;
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
/** Subscription acknowledged — the source is producing. */
|
|
362
|
+
subscribed: function () {
|
|
363
|
+
subscribeFailed = false;
|
|
364
|
+
lastSubscribeError = null;
|
|
365
|
+
phase = 'running';
|
|
366
|
+
evaluate();
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Subscription refused. Red immediately: there is no retry
|
|
371
|
+
* between reconnects, so a deaf-but-connected source would
|
|
372
|
+
* stay deaf forever while looking healthy.
|
|
373
|
+
*
|
|
374
|
+
* @param {Error} err - The subscribe error
|
|
375
|
+
*/
|
|
376
|
+
subscribeFailed: function ( err ) {
|
|
377
|
+
subscribeFailed = true;
|
|
378
|
+
lastSubscribeError = err.message;
|
|
379
|
+
evaluate();
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
/** The transport dropped. Starts the outage clock; a stale
|
|
383
|
+
* subscribe failure died with the connection. */
|
|
384
|
+
offline: function () {
|
|
385
|
+
if ( connected ) {
|
|
386
|
+
disconnectedAt = nowFn();
|
|
387
|
+
}
|
|
388
|
+
connected = false;
|
|
389
|
+
subscribeFailed = false;
|
|
390
|
+
lastSubscribeError = null;
|
|
391
|
+
phase = 'offline';
|
|
392
|
+
evaluate();
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
/** The library started a reconnect attempt. */
|
|
396
|
+
reconnecting: function () {
|
|
397
|
+
phase = 'reconnecting';
|
|
398
|
+
evaluate();
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* A transport-level error. Stored, not emitted directly —
|
|
403
|
+
* evaluate() attaches it as CONNECT_FAILED, so a retry storm
|
|
404
|
+
* of identical failures emits once.
|
|
405
|
+
*
|
|
406
|
+
* @param {Error} err - The transport error
|
|
407
|
+
*/
|
|
408
|
+
connectError: function ( err ) {
|
|
409
|
+
lastConnectError = err.message;
|
|
410
|
+
evaluate();
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
/** Clean stop finished. */
|
|
414
|
+
stopped: function () {
|
|
415
|
+
if ( phase === 'stopped' ) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
phase = 'stopped';
|
|
419
|
+
connected = false;
|
|
420
|
+
evaluate();
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* The stop's time budget ran out and the socket was forced
|
|
425
|
+
* closed. Reported with the sources' shared `note` convention
|
|
426
|
+
* (CSV and testHarness use the same wording).
|
|
427
|
+
*
|
|
428
|
+
* @param {number} timeoutMs - The exceeded budget
|
|
429
|
+
*/
|
|
430
|
+
stopForced: function ( timeoutMs ) {
|
|
431
|
+
phase = 'stopped';
|
|
432
|
+
connected = false;
|
|
433
|
+
const now = nowFn();
|
|
434
|
+
lastStatus = 'yellow';
|
|
435
|
+
lastPhase = 'stopped';
|
|
436
|
+
lastCode = null;
|
|
437
|
+
emitStatus( {
|
|
438
|
+
status: 'yellow',
|
|
439
|
+
connected: false,
|
|
440
|
+
phase: 'stopped',
|
|
441
|
+
msSinceLastMsg: now - lastReceivedAt,
|
|
442
|
+
note: `Stop took longer than ${timeoutMs}ms — forced.`
|
|
443
|
+
} );
|
|
444
|
+
emitMetrics();
|
|
445
|
+
},
|
|
446
|
+
|
|
447
|
+
// ── Hot-path counters (integer bumps; no allocation) ─────────
|
|
448
|
+
|
|
449
|
+
/** A duplicate was dropped. Still counts as arrival. */
|
|
450
|
+
dupSkipped: function () {
|
|
451
|
+
dedupHits += 1;
|
|
452
|
+
skipped += 1;
|
|
453
|
+
lastReceivedAt = nowFn();
|
|
454
|
+
evaluate();
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
/** A message arrived with no dedup id — the cache was bypassed. */
|
|
458
|
+
bypassed: function () {
|
|
459
|
+
dedupBypassed += 1;
|
|
460
|
+
},
|
|
461
|
+
|
|
462
|
+
/** A fresh dedup id was accepted into the cache. */
|
|
463
|
+
idAccepted: function () {
|
|
464
|
+
dedupMisses += 1;
|
|
465
|
+
},
|
|
466
|
+
|
|
467
|
+
/** A payload decoded cleanly. Heals a stored transport error —
|
|
468
|
+
* data flowing is proof the transport works. */
|
|
469
|
+
decodeOk: function () {
|
|
470
|
+
lastReceivedAt = nowFn();
|
|
471
|
+
lastConnectError = null;
|
|
472
|
+
ringPush( 0 );
|
|
473
|
+
evaluate();
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* A payload could not be decoded. Emits the mandated
|
|
478
|
+
* per-record report (ADR-018: skip, classify, continue — never
|
|
479
|
+
* silent), then re-evaluates the ratio rule.
|
|
480
|
+
*
|
|
481
|
+
* @param {string} detail - Operator-facing description
|
|
482
|
+
*/
|
|
483
|
+
decodeFailed: function ( detail ) {
|
|
484
|
+
decodeErrors += 1;
|
|
485
|
+
skipped += 1;
|
|
486
|
+
lastReceivedAt = nowFn();
|
|
487
|
+
ringPush( 1 );
|
|
488
|
+
emitStatus( {
|
|
489
|
+
status: 'yellow',
|
|
490
|
+
connected,
|
|
491
|
+
phase,
|
|
492
|
+
msSinceLastMsg: 0,
|
|
493
|
+
error: { code: 'DECODE_ERROR', message: detail }
|
|
494
|
+
} );
|
|
495
|
+
evaluate();
|
|
496
|
+
},
|
|
497
|
+
|
|
498
|
+
/** The user's transform returned null/undefined — dropped.
|
|
499
|
+
* An intentional drop, so it counts but does not report. */
|
|
500
|
+
transformDropped: function () {
|
|
501
|
+
skipped += 1;
|
|
502
|
+
},
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* The user's transform threw. Emits the mandated per-record
|
|
506
|
+
* report (skip, classify, continue — the transform contract,
|
|
507
|
+
* source-transform-uniformity 2026-07-11), classified as
|
|
508
|
+
* CALLBACK_FAILED: user code, never a transport failure. No
|
|
509
|
+
* evaluate() — a transform throw feeds no health rule, so
|
|
510
|
+
* there is no transition to re-derive.
|
|
511
|
+
*
|
|
512
|
+
* @param {string} detail - Operator-facing description
|
|
513
|
+
*/
|
|
514
|
+
transformFailed: function ( detail ) {
|
|
515
|
+
skipped += 1;
|
|
516
|
+
emitStatus( {
|
|
517
|
+
status: 'yellow',
|
|
518
|
+
connected,
|
|
519
|
+
phase,
|
|
520
|
+
msSinceLastMsg: 0,
|
|
521
|
+
error: { code: 'CALLBACK_FAILED', message: detail }
|
|
522
|
+
} );
|
|
523
|
+
},
|
|
524
|
+
|
|
525
|
+
/** A message reached the pipeline's onMessage. */
|
|
526
|
+
delivered: function () {
|
|
527
|
+
delivered += 1;
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
// ── Cadence & introspection ──────────────────────────────────
|
|
531
|
+
|
|
532
|
+
/** One heartbeat: re-check the time rules, emit metrics. */
|
|
533
|
+
tick: function () {
|
|
534
|
+
evaluate();
|
|
535
|
+
emitMetrics();
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
snapshot
|
|
539
|
+
};
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// ============================================================================
|
|
543
|
+
// EXPORTS
|
|
544
|
+
// ============================================================================
|
|
545
|
+
|
|
546
|
+
export { createStatusReporter };
|