@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,160 @@
|
|
|
1
|
+
// core/emitter-manager/terminal/formatters.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Per-column value formatters for the terminal emitter.
|
|
5
|
+
*
|
|
6
|
+
* Same role for terminal as `questdb/writers.js` plays for QDB: takes
|
|
7
|
+
* an asset-class column slice at startup and returns a value-formatter
|
|
8
|
+
* closure pre-resolved to the per-column quantization. Hot path is a
|
|
9
|
+
* lookup + a quantizer call, no per-message allocation.
|
|
10
|
+
*
|
|
11
|
+
* Quantization is delegated to the shared `core/utils/quantize` helper
|
|
12
|
+
* — the same one QDB's `writers.js` consumes. Both sinks compute
|
|
13
|
+
* identical rounded values for the same input; cross-sink alignment is
|
|
14
|
+
* structural, not coincidental.
|
|
15
|
+
*
|
|
16
|
+
* What this file does NOT do: validate the column slice. Validation
|
|
17
|
+
* lives in `assert-columns.js` and runs once at startup before the
|
|
18
|
+
* formatter is built. By the time these helpers see the columns map,
|
|
19
|
+
* every `resolution` is known to be a positive finite number (or
|
|
20
|
+
* absent, in which case the column falls through to global precision).
|
|
21
|
+
*
|
|
22
|
+
* @see ./assert-columns.js (defensive validation that runs first)
|
|
23
|
+
* @see ../../utils/quantize/index.js (shared quantizer used here and by QDB)
|
|
24
|
+
* @see ../../storage-manager/questdb/writers.js (sister consumer)
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { buildResolutionQuantizer } from '../../utils/quantize/index.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Build a column-name → quantizer-closure map at startup from the asset
|
|
31
|
+
* class column slice. Only float64 columns with a defined positive
|
|
32
|
+
* resolution contribute; everything else falls through to the global
|
|
33
|
+
* precision at format time.
|
|
34
|
+
*
|
|
35
|
+
* Each quantizer applies the same formula QDB's `createFloat64Writer`
|
|
36
|
+
* uses, so a value declared with `resolution: R` produces the same
|
|
37
|
+
* rounded number in both sinks:
|
|
38
|
+
*
|
|
39
|
+
* quantized = Math.round( value * invResolution ) * resolution
|
|
40
|
+
* clean = +quantized.toFixed( decimalPlaces )
|
|
41
|
+
*
|
|
42
|
+
* `Math.round(v / r) * r` snaps the value to the nearest multiple of
|
|
43
|
+
* `r` (the resolution grid). Then `toFixed( decimalPlaces )` cleans up
|
|
44
|
+
* floating-point noise like `10.100000000001` → `10.1`. The closure
|
|
45
|
+
* pre-resolves `invResolution` and `decimalPlaces` at startup so the
|
|
46
|
+
* per-message hot path is one multiply, one round, one multiply, one
|
|
47
|
+
* `toFixed` — no allocations, no recomputation.
|
|
48
|
+
*
|
|
49
|
+
* The per-column quantizer takes precedence over the integer-passthrough
|
|
50
|
+
* check in the formatter — when a column declares `resolution: 5`, an
|
|
51
|
+
* input value of `1234` gets quantized to `1235` (the nearest multiple
|
|
52
|
+
* of 5), regardless of the value's `Number.isInteger` shape. This
|
|
53
|
+
* matches QDB's behaviour: the asset class declared the resolution,
|
|
54
|
+
* the data MUST be aligned to it, integer-shaped or not.
|
|
55
|
+
*
|
|
56
|
+
* Decimal places follow the same rule as QDB writers:
|
|
57
|
+
* - `resolution = 0.1` → 1 decimal place
|
|
58
|
+
* - `resolution = 0.01` → 2 decimal places
|
|
59
|
+
* - `resolution = 0.001` → 3 decimal places
|
|
60
|
+
* - `resolution = 5` → 0 decimal places (whole-number resolution)
|
|
61
|
+
* - `resolution = 1` → 0 decimal places (effectively passthrough)
|
|
62
|
+
*
|
|
63
|
+
* Returns `Object.create(null)` so per-column lookups never accidentally
|
|
64
|
+
* hit `Object.prototype` keys (e.g., a column literally named
|
|
65
|
+
* `'toString'` or `'hasOwnProperty'`). The keys are runtime-supplied
|
|
66
|
+
* column names — exactly the case the rule says use a prototype-less
|
|
67
|
+
* dictionary for.
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} columns - the columns map from the asset class slice
|
|
70
|
+
* (typically `config.assetClass.columns` post wire-semantics injection)
|
|
71
|
+
* @returns {Object} prototype-less map of column name → `(v) → number`
|
|
72
|
+
*/
|
|
73
|
+
const buildColumnQuantizers = function ( columns ) {
|
|
74
|
+
const map = Object.create( null );
|
|
75
|
+
|
|
76
|
+
if ( !columns || typeof columns !== 'object' ) {
|
|
77
|
+
return map;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const colNames = Object.keys( columns );
|
|
81
|
+
for ( let i = 0; i < colNames.length; i += 1 ) {
|
|
82
|
+
const colName = colNames[ i ];
|
|
83
|
+
const colSpec = columns[ colName ];
|
|
84
|
+
if ( colSpec && colSpec.type === 'float64' ) {
|
|
85
|
+
// Delegate to the shared quantizer. Returns null when the
|
|
86
|
+
// column does not need quantization (no resolution declared
|
|
87
|
+
// or resolution = 1); we simply skip those columns and
|
|
88
|
+
// they fall back to the global precision at format time.
|
|
89
|
+
const quantize = buildResolutionQuantizer( colSpec.resolution );
|
|
90
|
+
if ( quantize ) {
|
|
91
|
+
map[ colName ] = quantize;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return map;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Build a value formatter closure that uses each column's quantizer
|
|
101
|
+
* (when one was registered for the column name) and the global
|
|
102
|
+
* `precision` config otherwise. Receives `(key, v)` so it can look up
|
|
103
|
+
* the column-specific quantizer by name.
|
|
104
|
+
*
|
|
105
|
+
* Branch order in the returned formatter:
|
|
106
|
+
* 1. null → `'null'`
|
|
107
|
+
* 2. boolean → as-is
|
|
108
|
+
* 3. string → as-is
|
|
109
|
+
* 4. non-number (object, undefined, etc.) → `String( v )`
|
|
110
|
+
* 5. **per-column quantizer** if the column registered one (this fires
|
|
111
|
+
* even for integer values, because the column declared a resolution
|
|
112
|
+
* and the data MUST be on the resolution grid)
|
|
113
|
+
* 6. integer → as-is (only reached when no per-column quantizer
|
|
114
|
+
* applies; preserves the original behaviour for non-resolution columns)
|
|
115
|
+
* 7. very small non-zero (`|v| < 0.001`) → exponential notation
|
|
116
|
+
* using the **global** precision (intentionally; see note below)
|
|
117
|
+
* 8. normal decimal → `+v.toFixed( precision )` (global fallback)
|
|
118
|
+
*
|
|
119
|
+
* **Why the exponential branch keeps using global precision.** Resolution
|
|
120
|
+
* describes the meaningful step size on the absolute scale. For values
|
|
121
|
+
* below that step — say `resolution: 0.1` and `v = 0.0003` — the
|
|
122
|
+
* "correctly rounded" value is `0`, but printing `0` for a non-zero
|
|
123
|
+
* value loses diagnostic information. Terminal is a debugging tool;
|
|
124
|
+
* the exponential form (`3e-4`) is more useful to a human eye than
|
|
125
|
+
* `0`. This is an intentional carve-out, not a bug. Note that this
|
|
126
|
+
* branch only fires for columns WITHOUT a per-column quantizer; columns
|
|
127
|
+
* with one always go through the quantizer path (step 5).
|
|
128
|
+
*
|
|
129
|
+
* @param {number} precision - Default decimal places when no per-column
|
|
130
|
+
* quantizer is registered
|
|
131
|
+
* @param {Object} columnQuantizers - Map produced by
|
|
132
|
+
* `buildColumnQuantizers`. Pass an empty `Object.create(null)` if no
|
|
133
|
+
* asset class was supplied — the formatter then falls back to global
|
|
134
|
+
* precision for every column.
|
|
135
|
+
* @returns {function(string, *): *} Formatter function `(key, v) → out`
|
|
136
|
+
*/
|
|
137
|
+
const createValueFormatter = function ( precision, columnQuantizers ) {
|
|
138
|
+
return function ( key, v ) {
|
|
139
|
+
if ( v === null ) return 'null';
|
|
140
|
+
if ( typeof v === 'boolean' ) return v;
|
|
141
|
+
if ( typeof v === 'string' ) return v;
|
|
142
|
+
if ( typeof v !== 'number' ) return String( v );
|
|
143
|
+
|
|
144
|
+
// Per-column quantizer takes precedence over every other rule:
|
|
145
|
+
// the column declared a resolution, the value MUST snap to the
|
|
146
|
+
// grid (matching what QDB will store). This is the cross-sink
|
|
147
|
+
// alignment guarantee. Fires for integers too — e.g.,
|
|
148
|
+
// resolution=5 with value=1234 quantizes to 1235.
|
|
149
|
+
const quantizer = columnQuantizers[ key ];
|
|
150
|
+
if ( quantizer ) return quantizer( v );
|
|
151
|
+
|
|
152
|
+
// No per-column quantizer beyond this point — the original
|
|
153
|
+
// behaviour for non-resolution columns is preserved exactly.
|
|
154
|
+
if ( Number.isInteger( v ) ) return v;
|
|
155
|
+
if ( v !== 0 && Math.abs( v ) < 0.001 ) return v.toExponential( precision );
|
|
156
|
+
return Number( v.toFixed( precision ) );
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export { buildColumnQuantizers, createValueFormatter };
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
// core/emitter-manager/terminal/index.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Terminal Emitter for debugging and testing.
|
|
5
|
+
*
|
|
6
|
+
* Outputs emitted messages to stdout with smart formatting:
|
|
7
|
+
* - Shows all fields (no filtering — emitIf already gates what's emitted)
|
|
8
|
+
* - Formats numbers with configurable precision (default: 2 decimal places)
|
|
9
|
+
* - Compact key=value format, verbose mode for full JSON
|
|
10
|
+
*
|
|
11
|
+
* Adapter contract (ADR-018, stream sink):
|
|
12
|
+
* - Module-level exports per the ADR's module surface: `id`,
|
|
13
|
+
* `configSchema`, `createEmitter`, `durabilityClass`,
|
|
14
|
+
* `semanticsRequirement`, and the default aggregate
|
|
15
|
+
* referencing the same constants.
|
|
16
|
+
* - Durability class: `'best-effort'` — nothing is buffered beyond the
|
|
17
|
+
* stdout write; there is no queue for a crash to lose, and no recovery
|
|
18
|
+
* commitment either.
|
|
19
|
+
* - `publishNow(topic, msg)` returns `{ ok: true }` synchronously. Terminal cannot
|
|
20
|
+
* fail (stdout is always available); the contract still requires the return shape.
|
|
21
|
+
* - `getPressure()` returns `0` — Terminal has no observable buffer (stdout drains
|
|
22
|
+
* to the kernel); a trivial sink meets the pressure obligation with a
|
|
23
|
+
* constant 0 (ADR-018).
|
|
24
|
+
* - `getHealth()` returns `{ status: 'green', connected: true, pressure: 0 }` —
|
|
25
|
+
* Terminal is always healthy and always "connected" to stdout (there is no
|
|
26
|
+
* connection to lose). Uniform semantics with MQTT emitter / QuestDB.
|
|
27
|
+
* - `err.code` vocabulary:
|
|
28
|
+
* - Setup-time (per the ADR-018 error vocabulary):
|
|
29
|
+
* `INVALID_CONFIG` — a float64 column in the supplied asset class
|
|
30
|
+
* declares a malformed `resolution` (zero, negative, NaN, Infinity,
|
|
31
|
+
* non-number). Thrown by `assertColumnFacts` at `createEmitter`
|
|
32
|
+
* time; see the column-internal facts section below.
|
|
33
|
+
* - Runtime: none today. Terminal has no failure modes that surface
|
|
34
|
+
* through `publishNow`. Future runtime codes (e.g., `STDOUT_CLOSED`)
|
|
35
|
+
* will be documented here when introduced.
|
|
36
|
+
*
|
|
37
|
+
* Column-internal facts consumed (per ADR-018's adapter–semantics
|
|
38
|
+
* alignment):
|
|
39
|
+
*
|
|
40
|
+
* Terminal's top-level dependency on the asset class is declared in the
|
|
41
|
+
* `semanticsRequirement` export above: just `columns`. Inside `columns`,
|
|
42
|
+
* terminal additionally reads ONE field per column:
|
|
43
|
+
*
|
|
44
|
+
* - `columns.*.resolution` — read by `formatters.js` (passed to the
|
|
45
|
+
* shared `buildResolutionQuantizer` helper in `core/utils/quantize`).
|
|
46
|
+
* Asserted at startup by `assert-columns.js`: when present on a
|
|
47
|
+
* `float64` column, must be a positive finite number; absent is fine
|
|
48
|
+
* (column falls back to the global `precision` config). Throws
|
|
49
|
+
* `INVALID_CONFIG` otherwise.
|
|
50
|
+
*
|
|
51
|
+
* Terminal does NOT require columns to declare a `type` field — it
|
|
52
|
+
* handles every JS value at runtime via the formatter's branch chain.
|
|
53
|
+
* `type === 'float64'` is checked only as a precondition for honouring
|
|
54
|
+
* `resolution`; non-float64 columns ignore `resolution` as dead weight.
|
|
55
|
+
*
|
|
56
|
+
* Cross-sink alignment with QuestDB: both adapters consume `resolution`
|
|
57
|
+
* via the same shared quantizer (`core/utils/quantize`). For the same
|
|
58
|
+
* input value and the same declared resolution, terminal's formatted
|
|
59
|
+
* number and QDB's stored number are guaranteed identical. Verified
|
|
60
|
+
* by the cross-sink consistency tests in `terminal-emitter.specs.js`.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* import { flow, csv, terminal } from '@winkjs/composer';
|
|
64
|
+
*
|
|
65
|
+
* flow('debug')
|
|
66
|
+
* .sanitize(...)
|
|
67
|
+
* .emitIf('log', () => true, { target: 'terminal' })
|
|
68
|
+
* .source(csv, { path: './data.csv' })
|
|
69
|
+
* .emitter(terminal)
|
|
70
|
+
* .run();
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
import { validators } from '../../utils/validate/index.js';
|
|
74
|
+
import { assertColumnFacts } from './assert-columns.js';
|
|
75
|
+
import { buildColumnQuantizers, createValueFormatter } from './formatters.js';
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Emitter identifier - must match target in emitIf specs.
|
|
79
|
+
* @type {string}
|
|
80
|
+
*/
|
|
81
|
+
export const id = 'terminal';
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Crash-survival class per ADR-018. Terminal buffers nothing beyond
|
|
85
|
+
* the stdout write — no queue to lose, no recovery promised.
|
|
86
|
+
* @type {string}
|
|
87
|
+
*/
|
|
88
|
+
export const durabilityClass = 'best-effort';
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Capability declaration per ADR-018's adapter–semantics alignment.
|
|
92
|
+
*
|
|
93
|
+
* Terminal opts in *optionally* to receiving the asset class. When a flow
|
|
94
|
+
* supplies one via `.assetClass()`, the wiring layer slices it down to
|
|
95
|
+
* the requested fields and injects the result as `config.assetClass`.
|
|
96
|
+
* With `fields: [ 'columns' ]`, terminal receives `config.assetClass`
|
|
97
|
+
* containing just the `columns` map — the column metadata is always
|
|
98
|
+
* nested under the asset class container per the semantics module's
|
|
99
|
+
* structure (a column map cannot exist standalone; it lives inside an
|
|
100
|
+
* asset class).
|
|
101
|
+
*
|
|
102
|
+
* Terminal uses each column's declared `resolution` to format float
|
|
103
|
+
* values to the intended number of decimal places — so a value declared
|
|
104
|
+
* with `resolution: 0.001` prints as `23.456` instead of `23.46` (which
|
|
105
|
+
* is what the global `precision: 2` default would produce). Without an
|
|
106
|
+
* asset class, terminal falls back to the global `precision` config —
|
|
107
|
+
* exactly the original global-precision behaviour.
|
|
108
|
+
*
|
|
109
|
+
* `required: false` because terminal is the simplest sink and is useful
|
|
110
|
+
* for debugging flows that have no semantics (e.g., a quick csv →
|
|
111
|
+
* terminal sanity check). The capability is purely additive when
|
|
112
|
+
* supplied.
|
|
113
|
+
*
|
|
114
|
+
* `fields: [ 'columns' ]` because terminal only reads the columns map;
|
|
115
|
+
* the other top-level asset class fields (name, description,
|
|
116
|
+
* insightTypes) carry no formatting-relevant facts.
|
|
117
|
+
*/
|
|
118
|
+
export const semanticsRequirement = {
|
|
119
|
+
assetClass: {
|
|
120
|
+
required: false,
|
|
121
|
+
fields: [ 'columns' ]
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Singleton success result reused on every publish. Hot-path zero allocation
|
|
127
|
+
* per ADR-013 / ADR-004 — `{ ok: true }` would otherwise allocate per call.
|
|
128
|
+
* Not frozen: V8 hot paths handle plain objects more predictably, and no
|
|
129
|
+
* caller mutates this.
|
|
130
|
+
* @type {{ok: true}}
|
|
131
|
+
*/
|
|
132
|
+
const RESULT_OK = { ok: true };
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Singleton health snapshot reused on every getHealth() call. Terminal's
|
|
136
|
+
* health is constant (no transport state to track), so a single object
|
|
137
|
+
* suffices. Same rationale as RESULT_OK above. `pressure: 0` completes
|
|
138
|
+
* the ADR-018 health floor: stdout has no observable buffer, matching
|
|
139
|
+
* what getPressure() reports.
|
|
140
|
+
* @type {{status: 'green', connected: true, pressure: 0}}
|
|
141
|
+
*/
|
|
142
|
+
const HEALTH_GREEN = { status: 'green', connected: true, pressure: 0 };
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Configuration schema for terminal emitter validation.
|
|
146
|
+
* Used by flow.emitter() to validate config at DSL time.
|
|
147
|
+
*
|
|
148
|
+
* `_propertyNames` lists every accepted key — unknown keys throw at
|
|
149
|
+
* DSL time (the validator's only unknown-key mechanism).
|
|
150
|
+
* `assetClass` is deliberately absent: wire-emitters injects it from
|
|
151
|
+
* the flow's `.assetClass()` after DSL validation, so a user-supplied
|
|
152
|
+
* value would be silently overwritten — rejecting it loudly is the
|
|
153
|
+
* point.
|
|
154
|
+
*
|
|
155
|
+
* @type {Object}
|
|
156
|
+
*/
|
|
157
|
+
export const configSchema = {
|
|
158
|
+
_propertyNames: [ 'verbose', 'prefix', 'precision' ],
|
|
159
|
+
verbose: {
|
|
160
|
+
type: 'boolean',
|
|
161
|
+
required: false,
|
|
162
|
+
error: 'verbose must be a boolean'
|
|
163
|
+
},
|
|
164
|
+
prefix: {
|
|
165
|
+
type: 'string',
|
|
166
|
+
required: false,
|
|
167
|
+
error: 'prefix must be a string'
|
|
168
|
+
},
|
|
169
|
+
precision: {
|
|
170
|
+
type: 'number',
|
|
171
|
+
required: false,
|
|
172
|
+
validator: validators.nonNegative,
|
|
173
|
+
error: 'precision must be a non-negative number'
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Formats a message for compact display.
|
|
180
|
+
*
|
|
181
|
+
* Passes the field name to `formatValue` so the formatter can look up
|
|
182
|
+
* per-column decimal places when the column has a declared
|
|
183
|
+
* `resolution`. Backward compatible — formatters that ignore the key
|
|
184
|
+
* (none today) would still work.
|
|
185
|
+
*
|
|
186
|
+
* @param {Object} msg - Message to format
|
|
187
|
+
* @param {function(string, *): *} formatValue - Value formatter
|
|
188
|
+
* @returns {string} Formatted message string
|
|
189
|
+
*/
|
|
190
|
+
const formatCompact = function ( msg, formatValue ) {
|
|
191
|
+
const parts = [];
|
|
192
|
+
|
|
193
|
+
for ( const [ key, value ] of Object.entries( msg ) ) {
|
|
194
|
+
parts.push( `${key}=${formatValue( key, value )}` );
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return parts.join( ' ' );
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Formats a message as pretty JSON with number formatting.
|
|
202
|
+
*
|
|
203
|
+
* @param {Object} msg - Message to format
|
|
204
|
+
* @param {function(string, *): *} formatValue - Value formatter
|
|
205
|
+
* @returns {string} Pretty JSON string
|
|
206
|
+
*/
|
|
207
|
+
const formatVerbose = function ( msg, formatValue ) {
|
|
208
|
+
const formatted = Object.create( null );
|
|
209
|
+
|
|
210
|
+
for ( const [ key, value ] of Object.entries( msg ) ) {
|
|
211
|
+
// Always pass through the formatter so per-column quantizers
|
|
212
|
+
// get a chance to round integer-shaped values too — e.g., when
|
|
213
|
+
// a column declares `resolution: 5`, an integer input of 1234
|
|
214
|
+
// must quantize to 1235 (the nearest multiple of 5), matching
|
|
215
|
+
// QDB's storage. The formatter handles every JS type internally
|
|
216
|
+
// and returns non-numbers as-is, so this is safe to call
|
|
217
|
+
// unconditionally.
|
|
218
|
+
formatted[ key ] = formatValue( key, value );
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return JSON.stringify( formatted, null, 2 );
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Creates a terminal emitter instance.
|
|
226
|
+
*
|
|
227
|
+
* @param {Object} [config={}] - Emitter configuration
|
|
228
|
+
* @param {boolean} [config.verbose=false] - Use pretty JSON format
|
|
229
|
+
* @param {string} [config.prefix=''] - Optional prefix for output lines
|
|
230
|
+
* @param {number} [config.precision=2] - Default decimal places for floats
|
|
231
|
+
* that have no per-column resolution declared
|
|
232
|
+
* @param {Object} [config.assetClass] - Asset class slice injected by
|
|
233
|
+
* wire-emitters when the flow supplies one via `.assetClass()`. Per
|
|
234
|
+
* terminal's `semanticsRequirement`, this contains just the `columns`
|
|
235
|
+
* field. Each `float64` column with a declared `resolution` formats
|
|
236
|
+
* to its own decimal places; columns without resolution fall back to
|
|
237
|
+
* the global `precision` config. Without an asset class entirely,
|
|
238
|
+
* every column falls back — the original global-precision behaviour,
|
|
239
|
+
* unchanged.
|
|
240
|
+
* @returns {Object} Emitter instance with publishNow, getPressure, getHealth, shutdown
|
|
241
|
+
*/
|
|
242
|
+
export const createEmitter = function ( config = {} ) {
|
|
243
|
+
const {
|
|
244
|
+
verbose = false,
|
|
245
|
+
prefix = '',
|
|
246
|
+
precision = 2,
|
|
247
|
+
assetClass = null
|
|
248
|
+
} = config;
|
|
249
|
+
|
|
250
|
+
// Defensive validation per ADR-018's adapter–semantics alignment:
|
|
251
|
+
// any resolution declared on a float64 column must be a positive
|
|
252
|
+
// finite number. Throws INVALID_CONFIG with a diagnostic message
|
|
253
|
+
// before the emitter is built, so a misconfigured flow fails at
|
|
254
|
+
// startup rather than producing garbage formatting later.
|
|
255
|
+
const columns = assetClass && assetClass.columns;
|
|
256
|
+
assertColumnFacts( columns );
|
|
257
|
+
|
|
258
|
+
// Pre-compute the per-column quantizer map at startup. Each entry
|
|
259
|
+
// is a closure built by the shared `buildResolutionQuantizer` helper
|
|
260
|
+
// (in core/utils/quantize) — same closure shape QDB's writers use,
|
|
261
|
+
// so terminal and QDB compute identical rounded values for the same
|
|
262
|
+
// input. An empty prototype-less map is returned when no asset
|
|
263
|
+
// class was supplied; the formatter then falls back to the global
|
|
264
|
+
// `precision` for every column. Hot path is one O(1) lookup per
|
|
265
|
+
// value, no allocation.
|
|
266
|
+
const columnQuantizers = buildColumnQuantizers( columns );
|
|
267
|
+
|
|
268
|
+
const linePrefix = prefix ? `${prefix} ` : '';
|
|
269
|
+
const formatValue = createValueFormatter( precision, columnQuantizers );
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Publishes a message to terminal.
|
|
275
|
+
*
|
|
276
|
+
* @param {string} topic - Message topic
|
|
277
|
+
* @param {Object} msg - Message payload
|
|
278
|
+
* @returns {{ok: true}} Contract return per ADR-018.
|
|
279
|
+
*/
|
|
280
|
+
publishNow: function ( topic, msg ) {
|
|
281
|
+
const ts = new Date().toLocaleTimeString();
|
|
282
|
+
const header = `${linePrefix}── ${ts} ── ${topic} ──`;
|
|
283
|
+
|
|
284
|
+
console.log( header );
|
|
285
|
+
|
|
286
|
+
if ( verbose ) {
|
|
287
|
+
console.log( formatVerbose( msg, formatValue ) );
|
|
288
|
+
} else {
|
|
289
|
+
console.log( formatCompact( msg, formatValue ) );
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
console.log(); // Blank line for readability
|
|
293
|
+
|
|
294
|
+
return RESULT_OK;
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Backpressure metric for the partition manager. Terminal has no
|
|
299
|
+
* observable buffer (stdout drains to the kernel), so always 0.
|
|
300
|
+
* Sync, O(1), allocation-free per ADR-018.
|
|
301
|
+
*
|
|
302
|
+
* @returns {number} 0
|
|
303
|
+
*/
|
|
304
|
+
getPressure: function () {
|
|
305
|
+
return 0;
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Health snapshot for operator monitoring. Terminal is always healthy
|
|
310
|
+
* and always "connected" to stdout — there is no transport state to
|
|
311
|
+
* lose. Returns the singleton HEALTH_GREEN.
|
|
312
|
+
*
|
|
313
|
+
* Uniform semantics with MQTT emitter / QuestDB:
|
|
314
|
+
* - `status: 'green'` — operational
|
|
315
|
+
* - `connected: true` — transport active (Terminal exception: always true)
|
|
316
|
+
* - `pressure: 0` — no observable buffer (the ADR-018 health floor)
|
|
317
|
+
*
|
|
318
|
+
* @returns {{status: 'green', connected: true, pressure: 0}}
|
|
319
|
+
*/
|
|
320
|
+
getHealth: function () {
|
|
321
|
+
return HEALTH_GREEN;
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* No-op shutdown for terminal emitter.
|
|
326
|
+
*
|
|
327
|
+
* Accepts the ADR-018 shutdown-contract shape `{ timeout }`. Terminal has
|
|
328
|
+
* no body to time out (stdout drains to the kernel without our help),
|
|
329
|
+
* so the value is intentionally unused — the leading `_` on the
|
|
330
|
+
* destructured name signals that to readers and to ESLint.
|
|
331
|
+
*
|
|
332
|
+
* @param {{timeout?: number}} [_options]
|
|
333
|
+
* @returns {Promise<void>}
|
|
334
|
+
*/
|
|
335
|
+
shutdown: async function ( { timeout: _timeout = 0 } = {} ) {
|
|
336
|
+
// Nothing to clean up.
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
export default { id, configSchema, durabilityClass, semanticsRequirement, createEmitter };
|