@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,244 @@
|
|
|
1
|
+
// core/source-manager/mqtt/dedup.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Time-bounded, count-capped dedup cache for MQTT QoS 1
|
|
5
|
+
* retransmissions (ADR-022).
|
|
6
|
+
*
|
|
7
|
+
* QoS 1 is "at least once": a re-send after a connection break carries
|
|
8
|
+
* the same `winkDedupId` as its original, and this cache drops the
|
|
9
|
+
* repeat. A duplicate arrives roughly one outage after its original,
|
|
10
|
+
* so the cache is specified in TIME (catch any duplicate within
|
|
11
|
+
* `windowMs`) and guaranteed in MEMORY (never hold more than
|
|
12
|
+
* `maxEntries` ids). Whichever bound binds first wins; at high message
|
|
13
|
+
* rates the effective window is `maxEntries ÷ rate` — the trade is
|
|
14
|
+
* documented in ADR-022, not hidden.
|
|
15
|
+
*
|
|
16
|
+
* One structure, no modes, no timers:
|
|
17
|
+
* - A `Set` gives O(1) duplicate lookup.
|
|
18
|
+
* - A pre-allocated ring (id slots + a Float64Array of arrival
|
|
19
|
+
* timestamps) gives O(1) insertion and eviction in arrival order.
|
|
20
|
+
* - Expiry is checked at the ring head on each call — amortized O(1),
|
|
21
|
+
* no background sweep.
|
|
22
|
+
*
|
|
23
|
+
* Hot-path allocation profile (ADR-018 zero-alloc — allocation only where
|
|
24
|
+
* unavoidable, each residual stated, no overclaiming):
|
|
25
|
+
* - Our code allocates nothing per message: the ring, the timestamp
|
|
26
|
+
* array, and the Set are created at initialization; ids are stored
|
|
27
|
+
* by reference; no objects, arrays, or strings are built.
|
|
28
|
+
* - Two unavoidable residuals, both bounded: (1) `Set.add`/`Set.delete`
|
|
29
|
+
* maintain V8's internal hash table — amortized O(1), memory bounded
|
|
30
|
+
* by `maxEntries`, and the identical profile the pre-ADR-022 cache
|
|
31
|
+
* already had (JS offers no pre-sized Set). (2) `Date.now()` exceeds
|
|
32
|
+
* the small-integer range, so the timestamp is transiently boxed; it
|
|
33
|
+
* unboxes on store into the Float64Array and dies in the nursery.
|
|
34
|
+
* - Eviction clears the ring slot so an evicted id string is not
|
|
35
|
+
* retained past its window — that release is deliberate.
|
|
36
|
+
*
|
|
37
|
+
* Opt-in by construction: a message without a dedup id (null or
|
|
38
|
+
* undefined) bypasses the cache entirely — identity is never guessed
|
|
39
|
+
* from content (ADR-022 rejected content hashing: byte-identical
|
|
40
|
+
* telemetry is routinely two real readings).
|
|
41
|
+
*
|
|
42
|
+
* Crash residue (accepted, per ADR-022): the cache is in-memory, so a
|
|
43
|
+
* crash between processing and acknowledging a message means its
|
|
44
|
+
* broker redelivery after restart is processed once more. At most one
|
|
45
|
+
* duplicate per source crash; disk-backed dedup state was rejected.
|
|
46
|
+
*
|
|
47
|
+
* ASSUMPTIONS
|
|
48
|
+
* -----------
|
|
49
|
+
* 1. A dedup id is unique per message and repeats only on a QoS-1
|
|
50
|
+
* retransmission (the emitter stamps UUIDs).
|
|
51
|
+
* 2. The clock moves forward at millisecond granularity. A backward
|
|
52
|
+
* clock jump can expire entries early or hold them one window too
|
|
53
|
+
* long — bounded either way, and self-healing within one window.
|
|
54
|
+
*
|
|
55
|
+
* LIMITATIONS
|
|
56
|
+
* -----------
|
|
57
|
+
* 1. In-memory: the cache dies with the process, so a duplicate
|
|
58
|
+
* arriving just after a restart may slip through. Deliberate
|
|
59
|
+
* (ADR-022 rejected disk-backed state); at most one per crash;
|
|
60
|
+
* QuestDB's at-rest dedup is the backstop.
|
|
61
|
+
* 2. At high rates the count cap binds before the time window: the
|
|
62
|
+
* effective window is maxEntries ÷ rate (the full 120 s up to
|
|
63
|
+
* about 550 msg/s at defaults; ~6.5 s at 10 k msg/s). Stated in
|
|
64
|
+
* ADR-022, not hidden.
|
|
65
|
+
* 3. Memory, measured (benchmark/mqtt-source/dedup-memory.js,
|
|
66
|
+
* 2026-07-09): 86 bytes per production-shaped id, ~6.7 MB worst
|
|
67
|
+
* case at the 65,536 cap including bounded V8 Set slack.
|
|
68
|
+
*
|
|
69
|
+
* @see ADR-022 - The full decision
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
import {
|
|
73
|
+
DEFAULT_DEDUP_WINDOW_MS,
|
|
74
|
+
DEFAULT_DEDUP_MAX_ENTRIES
|
|
75
|
+
} from './constants.js';
|
|
76
|
+
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// VALIDATION HELPER
|
|
79
|
+
// ============================================================================
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Throws a classified INVALID_CONFIG error (ADR-018 fail-fast setup)
|
|
83
|
+
* when `value` is not a positive integer.
|
|
84
|
+
*
|
|
85
|
+
* @param {*} value - Candidate value
|
|
86
|
+
* @param {string} name - Field name for the error message
|
|
87
|
+
*/
|
|
88
|
+
const assertPositiveInteger = function ( value, name ) {
|
|
89
|
+
if ( typeof value !== 'number' || value < 1 || !Number.isInteger( value ) ) {
|
|
90
|
+
const err = new Error( `WinkComposer/mqtt-source: ${name} must be a positive integer` );
|
|
91
|
+
err.code = 'INVALID_CONFIG';
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// DEDUP CACHE FACTORY
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create a time-bounded, count-capped dedup cache.
|
|
102
|
+
*
|
|
103
|
+
* All hot-path operations are O(1) (expiry amortized): Set lookup,
|
|
104
|
+
* ring-slot insertion, head eviction.
|
|
105
|
+
*
|
|
106
|
+
* @param {Object} [options={}] - Cache options
|
|
107
|
+
* @param {number} [options.windowMs=120000] - Time bound: an entry
|
|
108
|
+
* expires once its age reaches this many milliseconds
|
|
109
|
+
* @param {number} [options.maxEntries=65536] - Count cap: the memory
|
|
110
|
+
* guarantee; the oldest entry is evicted when the ring is full
|
|
111
|
+
* @param {function} [options.nowFn=Date.now] - Clock source. Injection
|
|
112
|
+
* point for deterministic tests; production uses the default
|
|
113
|
+
* @returns {Object} Cache instance with isDuplicate, size, clear, has
|
|
114
|
+
*/
|
|
115
|
+
const createDedupCache = function ( options = {} ) {
|
|
116
|
+
if ( typeof options !== 'object' || options === null ) {
|
|
117
|
+
const err = new Error( 'WinkComposer/mqtt-source: dedup options must be an object' );
|
|
118
|
+
err.code = 'INVALID_CONFIG';
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const {
|
|
123
|
+
windowMs = DEFAULT_DEDUP_WINDOW_MS,
|
|
124
|
+
maxEntries = DEFAULT_DEDUP_MAX_ENTRIES,
|
|
125
|
+
nowFn = Date.now
|
|
126
|
+
} = options;
|
|
127
|
+
|
|
128
|
+
assertPositiveInteger( windowMs, 'windowMs' );
|
|
129
|
+
assertPositiveInteger( maxEntries, 'maxEntries' );
|
|
130
|
+
if ( typeof nowFn !== 'function' ) {
|
|
131
|
+
const err = new Error( 'WinkComposer/mqtt-source: nowFn must be a function' );
|
|
132
|
+
err.code = 'INVALID_CONFIG';
|
|
133
|
+
throw err;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Pre-allocated structures (zero allocation on the hot path).
|
|
137
|
+
const seen = new Set(); // O(1) duplicate lookup
|
|
138
|
+
const ids = new Array( maxEntries ); // Ring: dedup ids in arrival order
|
|
139
|
+
const stamps = new Float64Array( maxEntries ); // Ring: arrival timestamps
|
|
140
|
+
let head = 0; // Oldest live entry
|
|
141
|
+
let tail = 0; // Next insert slot
|
|
142
|
+
let count = 0; // Live entries
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Evict the entry at the ring head. The slot is cleared so the id
|
|
146
|
+
* string is not retained after eviction.
|
|
147
|
+
*/
|
|
148
|
+
const evictHead = function () {
|
|
149
|
+
seen.delete( ids[ head ] );
|
|
150
|
+
ids[ head ] = undefined;
|
|
151
|
+
head = ( head + 1 ) % maxEntries;
|
|
152
|
+
count -= 1;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Check whether dedupId was seen within the window. New ids are
|
|
157
|
+
* cached; expired and cap-evicted ids count as new again.
|
|
158
|
+
*
|
|
159
|
+
* @param {string|null|undefined} dedupId - Deduplication identifier
|
|
160
|
+
* @returns {boolean} true if duplicate (skip), false if new (process)
|
|
161
|
+
*/
|
|
162
|
+
const isDuplicate = function ( dedupId ) {
|
|
163
|
+
// No dedup id → bypass: the message is processed verbatim and
|
|
164
|
+
// the cache is not touched (opt-in by construction, ADR-022).
|
|
165
|
+
if ( dedupId === null || dedupId === undefined ) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const now = nowFn();
|
|
170
|
+
|
|
171
|
+
// Sweep expired entries from the head. Bounded by the live
|
|
172
|
+
// count so the loop provably terminates; each entry is evicted
|
|
173
|
+
// at most once, so the sweep is amortized O(1) per call.
|
|
174
|
+
const live = count;
|
|
175
|
+
for ( let i = 0; i < live; i += 1 ) {
|
|
176
|
+
if ( ( now - stamps[ head ] ) < windowMs ) {
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
evictHead();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if ( seen.has( dedupId ) ) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Cap bound: a full ring evicts its oldest to admit the newest.
|
|
187
|
+
if ( count === maxEntries ) {
|
|
188
|
+
evictHead();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
ids[ tail ] = dedupId;
|
|
192
|
+
stamps[ tail ] = now;
|
|
193
|
+
seen.add( dedupId );
|
|
194
|
+
tail = ( tail + 1 ) % maxEntries;
|
|
195
|
+
count += 1;
|
|
196
|
+
|
|
197
|
+
return false;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get current number of cached entries.
|
|
202
|
+
*
|
|
203
|
+
* @returns {number} Number of live entries
|
|
204
|
+
*/
|
|
205
|
+
const size = function () {
|
|
206
|
+
return count;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Clear all cached entries. Used for testing or reset.
|
|
211
|
+
*/
|
|
212
|
+
const clear = function () {
|
|
213
|
+
seen.clear();
|
|
214
|
+
ids.fill( undefined );
|
|
215
|
+
stamps.fill( 0 );
|
|
216
|
+
head = 0;
|
|
217
|
+
tail = 0;
|
|
218
|
+
count = 0;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Check if dedupId is in cache without modifying it. Test helper —
|
|
223
|
+
* does not add, evict, or sweep.
|
|
224
|
+
*
|
|
225
|
+
* @param {string} dedupId - Deduplication identifier
|
|
226
|
+
* @returns {boolean} true if in cache, false otherwise
|
|
227
|
+
*/
|
|
228
|
+
const has = function ( dedupId ) {
|
|
229
|
+
return seen.has( dedupId );
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
isDuplicate,
|
|
234
|
+
size,
|
|
235
|
+
clear,
|
|
236
|
+
has
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// ============================================================================
|
|
241
|
+
// EXPORTS
|
|
242
|
+
// ============================================================================
|
|
243
|
+
|
|
244
|
+
export { createDedupCache };
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// core/source-manager/mqtt/index.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview MQTT source adapter for WinkComposer aggregator.
|
|
5
|
+
*
|
|
6
|
+
* Subscribes to edge composer MQTT outputs with deduplication for QoS 1.
|
|
7
|
+
* Extracts winkDedupId from MQTT v5 user properties to filter retransmissions.
|
|
8
|
+
*
|
|
9
|
+
* Adapter contract (ADR-018):
|
|
10
|
+
* - Module-level exports: `id`, `configSchema`, `start`,
|
|
11
|
+
* `durabilityClass`, and the default aggregate referencing the same
|
|
12
|
+
* constants.
|
|
13
|
+
* - `start( config )` returns a `stopFn({ timeout })`.
|
|
14
|
+
* - Lifecycle and error contract details: see `client.js` file header.
|
|
15
|
+
*
|
|
16
|
+
* Durability (ADR-018): `'broker-queue'` — the source connects with a
|
|
17
|
+
* persistent broker session (`cleanStart: false`), so while it is
|
|
18
|
+
* disconnected the broker holds every QoS-1 message published to its
|
|
19
|
+
* subscribed topics and replays them on reconnect. What the source has
|
|
20
|
+
* not yet acknowledged is not lost; the broker owns the queue.
|
|
21
|
+
*
|
|
22
|
+
* `configSchema` declares the user-supplied config fields. The flow
|
|
23
|
+
* runtime calls `validateWithSchema( mqtt.configSchema, sourceConfig )`
|
|
24
|
+
* at DSL time (in `flow.source()`), so typos and bad types are caught
|
|
25
|
+
* before the flow ever runs. Per ADR-018 the schema is authoritative
|
|
26
|
+
* for the fields it covers; `client.js` does not re-enforce them. The
|
|
27
|
+
* schema carries `_propertyNames`, so an unknown key (e.g. the typo
|
|
28
|
+
* `brokerURL`) is rejected at definition time.
|
|
29
|
+
*
|
|
30
|
+
* Runtime-injected callbacks (`onMessage`, `onShutdown`) are not part
|
|
31
|
+
* of `configSchema` — the wiring layer adds them to the config at start
|
|
32
|
+
* time, after DSL validation. A user-supplied `onMessage` would be
|
|
33
|
+
* silently overwritten; the unknown-key rejection turns that mistake
|
|
34
|
+
* into a fail-fast error instead.
|
|
35
|
+
*
|
|
36
|
+
* ASSUMPTIONS
|
|
37
|
+
* -----------
|
|
38
|
+
* 1. Inside a flow, the runtime injects `onMessage` / `onShutdown`
|
|
39
|
+
* and wraps `onStatus`; a direct caller of `start()` supplies
|
|
40
|
+
* `onMessage` itself and owns calling the returned stop function.
|
|
41
|
+
* 2. Config passed through the DSL was validated by this schema;
|
|
42
|
+
* `client.js` re-checks only the three fields a direct caller
|
|
43
|
+
* could omit (brokerUrl, topics, onMessage).
|
|
44
|
+
*
|
|
45
|
+
* LIMITATIONS
|
|
46
|
+
* -----------
|
|
47
|
+
* 1. This is an infinite source: it never emits `phase: 'complete'`,
|
|
48
|
+
* so a flow's `whenComplete()` resolves only via `shutdown()`.
|
|
49
|
+
* 2. Duplicate filtering is opt-in per message: only messages that
|
|
50
|
+
* carry `winkDedupId` are protected. Watch the `dedupBypassed`
|
|
51
|
+
* counter to spot publishers that are not stamping ids.
|
|
52
|
+
*
|
|
53
|
+
* Usage in flow:
|
|
54
|
+
* import mqtt from './core/source-manager/mqtt/index.js';
|
|
55
|
+
*
|
|
56
|
+
* flow( 'aggregator' )
|
|
57
|
+
* .source( mqtt, {
|
|
58
|
+
* brokerUrl: 'mqtt://broker:1883',
|
|
59
|
+
* topics: [ 'edge/+/enriched' ],
|
|
60
|
+
* codec: msgpackCodec,
|
|
61
|
+
* dedupWindowMs: 120000
|
|
62
|
+
* } )
|
|
63
|
+
*
|
|
64
|
+
* @see src/core/emitter-manager/mqtt/index.js - Emitter counterpart
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
import { validators } from '../../utils/validate/index.js';
|
|
68
|
+
import { createMQTTSourceClient } from './client.js';
|
|
69
|
+
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// SOURCE ADAPTER
|
|
72
|
+
// ============================================================================
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Source adapter identifier.
|
|
76
|
+
* Used by the flow runtime to reference this adapter.
|
|
77
|
+
*
|
|
78
|
+
* @type {string}
|
|
79
|
+
*/
|
|
80
|
+
const id = 'mqtt';
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Crash-survival class per ADR-018. For a source the value describes
|
|
84
|
+
* the INPUT it can recover after a disconnect: the persistent broker
|
|
85
|
+
* session holds subscribed QoS-1 messages across the gap and replays
|
|
86
|
+
* them on reconnect.
|
|
87
|
+
*
|
|
88
|
+
* @type {string}
|
|
89
|
+
*/
|
|
90
|
+
const durabilityClass = 'broker-queue';
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Validates that a value is a non-empty topic string or a non-empty
|
|
94
|
+
* array of non-empty topic strings.
|
|
95
|
+
*
|
|
96
|
+
* @param {*} value
|
|
97
|
+
* @returns {boolean}
|
|
98
|
+
*/
|
|
99
|
+
const isTopicOrTopicArray = function ( value ) {
|
|
100
|
+
if ( typeof value === 'string' && value.length > 0 ) return true;
|
|
101
|
+
if ( Array.isArray( value ) && value.length > 0 ) {
|
|
102
|
+
return value.every( ( t ) => typeof t === 'string' && t.length > 0 );
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Schema for the MQTT source's user-supplied config. Consumed by the
|
|
109
|
+
* flow runtime (`flow.source()` → `validateWithSchema`) at DSL time.
|
|
110
|
+
*
|
|
111
|
+
* Required:
|
|
112
|
+
* - `brokerUrl` — MQTT broker URL (e.g., 'mqtt://broker.local:1883').
|
|
113
|
+
* - `topics` — topic string or non-empty array of topic strings
|
|
114
|
+
* (wildcards supported).
|
|
115
|
+
*
|
|
116
|
+
* Optional:
|
|
117
|
+
* - `codec` — payload decoder with an `unpack( buffer )` function.
|
|
118
|
+
* Default: JSON.parse of the payload.
|
|
119
|
+
* - `transform` — function( msg ) → msg, applied before `onMessage`;
|
|
120
|
+
* returning null/undefined drops the message (counted in `skipped`).
|
|
121
|
+
* A throw skips that one message with a per-record CALLBACK_FAILED
|
|
122
|
+
* report and the stream continues. Uniform with the CSV source.
|
|
123
|
+
* - `dedupWindowMs` — dedup time bound: a duplicate arriving within
|
|
124
|
+
* this many milliseconds of its original is dropped. Default 120000
|
|
125
|
+
* (ADR-022).
|
|
126
|
+
* - `dedupMaxEntries` — dedup count cap: the memory guarantee (~8 MB
|
|
127
|
+
* worst case at the default 65536). At high rates the effective
|
|
128
|
+
* window is maxEntries ÷ rate — see ADR-022.
|
|
129
|
+
* - `clientId` — MQTT client ID. Auto-generated when omitted.
|
|
130
|
+
* - `cleanStart` — override the persistent-session default (false).
|
|
131
|
+
* - `onStatus` — status callback. Receives the structured ADR-018
|
|
132
|
+
* status payload on every transition and per decode failure — the full
|
|
133
|
+
* vocabulary is documented in `client.js`.
|
|
134
|
+
* - `onMetrics` — counter-snapshot callback, ~1 Hz plus on every
|
|
135
|
+
* health transition (delivered, skipped, decodeErrors, reconnects,
|
|
136
|
+
* dedupHits, dedupMisses, dedupBypassed, dedupCacheSize).
|
|
137
|
+
* - `expectedQuietPeriodMs` — opt-in quiet rule: health goes yellow
|
|
138
|
+
* when no packet arrives for longer than this many milliseconds.
|
|
139
|
+
* Off when omitted (silence is normal for many sources).
|
|
140
|
+
* - `mqttConnectFn` — MQTT connect function injection point (tests,
|
|
141
|
+
* benchmarks).
|
|
142
|
+
*
|
|
143
|
+
* `_propertyNames` lists every accepted key — unknown keys throw at
|
|
144
|
+
* DSL time (the validator's only unknown-key mechanism).
|
|
145
|
+
*
|
|
146
|
+
* @type {Object}
|
|
147
|
+
*/
|
|
148
|
+
const configSchema = {
|
|
149
|
+
_propertyNames: [
|
|
150
|
+
'brokerUrl',
|
|
151
|
+
'topics',
|
|
152
|
+
'codec',
|
|
153
|
+
'transform',
|
|
154
|
+
'dedupWindowMs',
|
|
155
|
+
'dedupMaxEntries',
|
|
156
|
+
'clientId',
|
|
157
|
+
'cleanStart',
|
|
158
|
+
'onStatus',
|
|
159
|
+
'onMetrics',
|
|
160
|
+
'expectedQuietPeriodMs',
|
|
161
|
+
'mqttConnectFn'
|
|
162
|
+
],
|
|
163
|
+
brokerUrl: {
|
|
164
|
+
type: 'string',
|
|
165
|
+
required: true,
|
|
166
|
+
validator: validators.nonEmptyString,
|
|
167
|
+
error: 'brokerUrl must be a non-empty string (e.g., mqtt://broker.local:1883)'
|
|
168
|
+
},
|
|
169
|
+
topics: {
|
|
170
|
+
required: true,
|
|
171
|
+
validator: isTopicOrTopicArray,
|
|
172
|
+
error: 'topics must be a non-empty string or a non-empty array of non-empty strings'
|
|
173
|
+
},
|
|
174
|
+
codec: {
|
|
175
|
+
type: 'object',
|
|
176
|
+
required: false,
|
|
177
|
+
validator: ( v ) => v !== null && typeof v.unpack === 'function',
|
|
178
|
+
error: 'codec must be an object with an unpack() function'
|
|
179
|
+
},
|
|
180
|
+
transform: {
|
|
181
|
+
type: 'function',
|
|
182
|
+
required: false,
|
|
183
|
+
error: 'transform must be a function( msg ) returning a msg (or null/undefined to drop)'
|
|
184
|
+
},
|
|
185
|
+
dedupWindowMs: {
|
|
186
|
+
type: 'number',
|
|
187
|
+
required: false,
|
|
188
|
+
validator: validators.positiveInteger,
|
|
189
|
+
error: 'dedupWindowMs must be a positive integer (milliseconds; default 120000)'
|
|
190
|
+
},
|
|
191
|
+
dedupMaxEntries: {
|
|
192
|
+
type: 'number',
|
|
193
|
+
required: false,
|
|
194
|
+
validator: validators.positiveInteger,
|
|
195
|
+
error: 'dedupMaxEntries must be a positive integer (default 65536)'
|
|
196
|
+
},
|
|
197
|
+
clientId: {
|
|
198
|
+
type: 'string',
|
|
199
|
+
required: false,
|
|
200
|
+
validator: validators.noSpaces,
|
|
201
|
+
error: 'clientId must be a string without spaces'
|
|
202
|
+
},
|
|
203
|
+
cleanStart: {
|
|
204
|
+
type: 'boolean',
|
|
205
|
+
required: false,
|
|
206
|
+
error: 'cleanStart must be a boolean'
|
|
207
|
+
},
|
|
208
|
+
onStatus: {
|
|
209
|
+
type: 'function',
|
|
210
|
+
required: false,
|
|
211
|
+
error: 'onStatus must be a function'
|
|
212
|
+
},
|
|
213
|
+
onMetrics: {
|
|
214
|
+
type: 'function',
|
|
215
|
+
required: false,
|
|
216
|
+
error: 'onMetrics must be a function'
|
|
217
|
+
},
|
|
218
|
+
expectedQuietPeriodMs: {
|
|
219
|
+
type: 'number',
|
|
220
|
+
required: false,
|
|
221
|
+
validator: validators.positiveInteger,
|
|
222
|
+
error: 'expectedQuietPeriodMs must be a positive integer (milliseconds)'
|
|
223
|
+
},
|
|
224
|
+
mqttConnectFn: {
|
|
225
|
+
type: 'function',
|
|
226
|
+
required: false,
|
|
227
|
+
error: 'mqttConnectFn must be a function (test/benchmark injection point)'
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Start the MQTT source.
|
|
233
|
+
*
|
|
234
|
+
* Called by the flow runtime (flow/run.js) with config from flow DSL.
|
|
235
|
+
* Returns a stop function for graceful shutdown.
|
|
236
|
+
*
|
|
237
|
+
* @param {Object} config - Source configuration
|
|
238
|
+
* @param {string} config.brokerUrl - MQTT broker URL
|
|
239
|
+
* @param {string|string[]} config.topics - Topic(s) to subscribe to
|
|
240
|
+
* @param {function} config.onMessage - Message handler (injected by the flow runtime)
|
|
241
|
+
* @param {Object} [config.codec] - Codec for payload decoding
|
|
242
|
+
* @param {function} [config.transform] - Optional message transform
|
|
243
|
+
* @param {number} [config.dedupWindowMs=120000] - Dedup time bound (ADR-022)
|
|
244
|
+
* @param {number} [config.dedupMaxEntries=65536] - Dedup count cap (ADR-022)
|
|
245
|
+
* @param {string} [config.clientId] - MQTT client ID
|
|
246
|
+
* @param {function} [config.onStatus] - Structured status callback (ADR-018)
|
|
247
|
+
* @param {function} [config.onMetrics] - Counter-snapshot callback (~1 Hz + transitions)
|
|
248
|
+
* @param {number} [config.expectedQuietPeriodMs] - Opt-in quiet rule threshold
|
|
249
|
+
* @returns {function} Stop function that returns a Promise
|
|
250
|
+
*/
|
|
251
|
+
const start = function ( config ) {
|
|
252
|
+
return createMQTTSourceClient( config );
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// EXPORTS
|
|
257
|
+
// ============================================================================
|
|
258
|
+
|
|
259
|
+
export { id, configSchema, durabilityClass, start };
|
|
260
|
+
export default { id, configSchema, durabilityClass, start };
|