@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,153 @@
|
|
|
1
|
+
// core/semantics/schemas/enum-schema.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Enum Schema Definition
|
|
5
|
+
*
|
|
6
|
+
* Defines the validation schema for enum files used in semantics.
|
|
7
|
+
* Enums provide human-readable labels for column values of any type.
|
|
8
|
+
*
|
|
9
|
+
* Supported key types (as strings, no whitespace):
|
|
10
|
+
* - Numeric: "0", "1", "-1", "3.14" (for int64/float64 columns)
|
|
11
|
+
* - Boolean: "true", "false" (for bool columns like isWashing)
|
|
12
|
+
* - Text codes: "R", "G", "B", "idle" (for string columns)
|
|
13
|
+
*
|
|
14
|
+
* Example enum files:
|
|
15
|
+
*
|
|
16
|
+
* Numeric states:
|
|
17
|
+
* {
|
|
18
|
+
* "name": "machineState",
|
|
19
|
+
* "values": { "0": "Idle", "1": "Running", "2": "Error" }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* Boolean states (e.g., for isWashing column):
|
|
23
|
+
* {
|
|
24
|
+
* "name": "washingState",
|
|
25
|
+
* "values": { "true": "Washing", "false": "Idle" }
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* Text codes:
|
|
29
|
+
* {
|
|
30
|
+
* "name": "colorCodes",
|
|
31
|
+
* "values": { "R": "Red", "G": "Green", "B": "Blue" }
|
|
32
|
+
* }
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { validators } from '../../utils/validate/index.js';
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// CUSTOM VALIDATORS
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validates a single enum key.
|
|
43
|
+
* Keys must be one of:
|
|
44
|
+
* - Numeric string: Valid finite number in canonical form ("0", "-1", "3.14")
|
|
45
|
+
* - Boolean string: Exactly "true" or "false"
|
|
46
|
+
* - Identifier: Valid JS identifier for text codes ("R", "idle", "state_1")
|
|
47
|
+
*
|
|
48
|
+
* @param {string} key - Single enum key to validate
|
|
49
|
+
* @returns {boolean} True if key is valid
|
|
50
|
+
*/
|
|
51
|
+
const validEnumKey = function ( key ) {
|
|
52
|
+
// Try numeric (most common for machine states)
|
|
53
|
+
if ( validators.numericString( key ) ) return true;
|
|
54
|
+
// Try boolean
|
|
55
|
+
if ( validators.booleanString( key ) ) return true;
|
|
56
|
+
// Try identifier (text codes)
|
|
57
|
+
return validators.identifier( key );
|
|
58
|
+
}; // validEnumKey()
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Validates that enum values object has valid keys.
|
|
62
|
+
* Keys must be numeric strings, boolean strings, or valid identifiers.
|
|
63
|
+
*
|
|
64
|
+
* @param {Object} values - Enum values object
|
|
65
|
+
* @returns {boolean} True if all keys are valid
|
|
66
|
+
*/
|
|
67
|
+
const validEnumKeys = function ( values ) {
|
|
68
|
+
if ( typeof values !== 'object' || values === null ) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const keys = Object.keys( values );
|
|
73
|
+
if ( keys.length === 0 ) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for ( let i = 0; i < keys.length; i += 1 ) {
|
|
78
|
+
if ( !validEnumKey( keys[ i ] ) ) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return true;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validates that enum values are all non-empty strings.
|
|
88
|
+
* Values can contain spaces (e.g., "Error State", "In Progress").
|
|
89
|
+
*
|
|
90
|
+
* @param {Object} values - Enum values object
|
|
91
|
+
* @returns {boolean} True if all values are non-empty strings
|
|
92
|
+
*/
|
|
93
|
+
const validEnumValues = function ( values ) {
|
|
94
|
+
if ( typeof values !== 'object' || values === null ) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const entries = Object.values( values );
|
|
99
|
+
for ( let i = 0; i < entries.length; i += 1 ) {
|
|
100
|
+
if ( !validators.nonEmptyString( entries[ i ] ) ) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return true;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// SCHEMA DEFINITION
|
|
110
|
+
// ============================================================================
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Schema for enum JSON files.
|
|
114
|
+
*
|
|
115
|
+
* Uses validate module's `keyValidator` and `propertySchema` features
|
|
116
|
+
* for detailed error messages on individual key/value failures.
|
|
117
|
+
*
|
|
118
|
+
* @type {Object}
|
|
119
|
+
*/
|
|
120
|
+
const enumSchema = {
|
|
121
|
+
_propertyNames: [ 'name', 'description', 'values' ],
|
|
122
|
+
name: {
|
|
123
|
+
type: 'string',
|
|
124
|
+
required: true,
|
|
125
|
+
validator: validators.identifier,
|
|
126
|
+
error: 'Enum name must be a valid identifier'
|
|
127
|
+
},
|
|
128
|
+
description: {
|
|
129
|
+
type: 'string',
|
|
130
|
+
required: false,
|
|
131
|
+
default: ''
|
|
132
|
+
},
|
|
133
|
+
values: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
required: true,
|
|
136
|
+
minProperties: 1,
|
|
137
|
+
keyValidator: validEnumKey,
|
|
138
|
+
propertySchema: {
|
|
139
|
+
type: 'string',
|
|
140
|
+
validator: validators.nonEmptyString,
|
|
141
|
+
error: 'Enum values must be non-empty strings'
|
|
142
|
+
},
|
|
143
|
+
error: 'Enum values must be an object with valid keys (numeric, boolean, or identifier) and non-empty string values'
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// ============================================================================
|
|
148
|
+
// EXPORTS
|
|
149
|
+
// ============================================================================
|
|
150
|
+
|
|
151
|
+
export { enumSchema, validEnumKey, validEnumKeys, validEnumValues };
|
|
152
|
+
|
|
153
|
+
export default enumSchema;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// core/semantics/schemas/index.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Semantics Schemas Index
|
|
5
|
+
*
|
|
6
|
+
* Exports all schema definitions and validators for semantics JSON files.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// ENUM SCHEMA
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
enumSchema,
|
|
15
|
+
validEnumKey,
|
|
16
|
+
validEnumKeys,
|
|
17
|
+
validEnumValues
|
|
18
|
+
} from './enum-schema.js';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// COLUMN SCHEMA
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
columnSchema,
|
|
26
|
+
validPhysicalRange,
|
|
27
|
+
validOperational,
|
|
28
|
+
validSpecification,
|
|
29
|
+
validWhen,
|
|
30
|
+
validContext,
|
|
31
|
+
validContexts,
|
|
32
|
+
validColumnType,
|
|
33
|
+
validInterpretation,
|
|
34
|
+
validLimitsMutualExclusivity,
|
|
35
|
+
validLimitsHierarchy,
|
|
36
|
+
validContextLimitsHierarchy,
|
|
37
|
+
COLUMN_TYPES,
|
|
38
|
+
COLUMN_DEFAULTS
|
|
39
|
+
} from './column-schema.js';
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// ASSET CLASS SCHEMA
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
assetClassSchema,
|
|
47
|
+
insightTypeSchema,
|
|
48
|
+
validColumnsStructure,
|
|
49
|
+
validInsightType,
|
|
50
|
+
validInsightTypes
|
|
51
|
+
} from './asset-class-schema.js';
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// core/semantics/warnings.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Warning Infrastructure for Semantic Loading
|
|
5
|
+
*
|
|
6
|
+
* Provides warning accumulation and emission utilities for semantic validation.
|
|
7
|
+
* Warnings are advisory (don't fail validation) but highlight potential issues
|
|
8
|
+
* in semantic definitions.
|
|
9
|
+
*
|
|
10
|
+
* Follows the accumulation pattern used in flow/validate.js:
|
|
11
|
+
* - Collect all warnings during validation phases
|
|
12
|
+
* - Emit all accumulated warnings at the end
|
|
13
|
+
* - Support suppressWarnings option and custom onWarning handler
|
|
14
|
+
*
|
|
15
|
+
* @see flow/validate.js for the accumulation pattern
|
|
16
|
+
* @see core/storage-manager/questdb/persist-plan.js for onWarning callback pattern
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// CONSTANTS
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Standard warning prefix for semantic validation warnings.
|
|
25
|
+
* @type {string}
|
|
26
|
+
*/
|
|
27
|
+
const WARNING_PREFIX = 'WinkComposer/semantics';
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// DEFAULT WARNING HANDLER
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Default warning handler for semantic validation issues.
|
|
35
|
+
* Logs to console in WinkComposer format.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} message - Warning message describing the issue
|
|
38
|
+
*/
|
|
39
|
+
const defaultOnWarning = function ( message ) {
|
|
40
|
+
console.warn( `${WARNING_PREFIX}: ${message}` );
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// WARNING COLLECTOR
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates a warning collector for semantic validation.
|
|
49
|
+
*
|
|
50
|
+
* Follows the flow/validate.js accumulation pattern:
|
|
51
|
+
* - add() accumulates warnings to internal array
|
|
52
|
+
* - emit() outputs all accumulated warnings
|
|
53
|
+
* - getWarnings() returns copy of accumulated warnings
|
|
54
|
+
* - count() returns number of accumulated warnings
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} options - Warning options
|
|
57
|
+
* @param {boolean} [options.suppressWarnings=false] - Suppress all warnings
|
|
58
|
+
* @param {Function} [options.onWarning] - Custom warning handler
|
|
59
|
+
* @returns {Object} Warning collector with add, emit, getWarnings, count methods
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // Default: logs to console on emit()
|
|
63
|
+
* const collector = createWarningCollector( {} );
|
|
64
|
+
* collector.add( 'Missing interpretation' );
|
|
65
|
+
* collector.add( 'Missing physicalRange' );
|
|
66
|
+
* collector.emit(); // Outputs both warnings
|
|
67
|
+
*
|
|
68
|
+
* // Suppress all warnings (useful for tests)
|
|
69
|
+
* const collector = createWarningCollector( { suppressWarnings: true } );
|
|
70
|
+
*
|
|
71
|
+
* // Custom handler
|
|
72
|
+
* const collector = createWarningCollector( {
|
|
73
|
+
* onWarning: ( msg ) => logger.warn( msg )
|
|
74
|
+
* } );
|
|
75
|
+
*/
|
|
76
|
+
const createWarningCollector = function ( options = {} ) {
|
|
77
|
+
const { suppressWarnings = false, onWarning } = options;
|
|
78
|
+
const warnings = [];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Add a warning message to the collector.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} message - Warning message
|
|
84
|
+
*/
|
|
85
|
+
const add = function ( message ) {
|
|
86
|
+
warnings.push( message );
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Emit all accumulated warnings.
|
|
91
|
+
* Does nothing if suppressWarnings is true.
|
|
92
|
+
*/
|
|
93
|
+
const emit = function () {
|
|
94
|
+
if ( suppressWarnings ) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const handler = onWarning || defaultOnWarning;
|
|
99
|
+
|
|
100
|
+
for ( let i = 0; i < warnings.length; i += 1 ) {
|
|
101
|
+
handler( warnings[ i ] );
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get copy of accumulated warnings.
|
|
107
|
+
*
|
|
108
|
+
* @returns {Array<string>} Copy of warnings array
|
|
109
|
+
*/
|
|
110
|
+
const getWarnings = function () {
|
|
111
|
+
return [ ...warnings ];
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get count of accumulated warnings.
|
|
116
|
+
*
|
|
117
|
+
* @returns {number} Number of accumulated warnings
|
|
118
|
+
*/
|
|
119
|
+
const count = function () {
|
|
120
|
+
return warnings.length;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
add,
|
|
125
|
+
emit,
|
|
126
|
+
getWarnings,
|
|
127
|
+
count
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// ============================================================================
|
|
132
|
+
// EXPORTS
|
|
133
|
+
// ============================================================================
|
|
134
|
+
|
|
135
|
+
export {
|
|
136
|
+
WARNING_PREFIX,
|
|
137
|
+
defaultOnWarning,
|
|
138
|
+
createWarningCollector
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export default createWarningCollector;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// core/shutdown-manager/index.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Process-layer shutdown manager.
|
|
5
|
+
*
|
|
6
|
+
* Per ADR-018, the framework has three layers — adapter, flow, and
|
|
7
|
+
* process — each owning a distinct concern in the lifecycle. This file
|
|
8
|
+
* is the process layer.
|
|
9
|
+
*
|
|
10
|
+
* What this layer owns:
|
|
11
|
+
* - **OS signal handlers (SIGINT/SIGTERM)** as the SOLE owner. The flow
|
|
12
|
+
* layer (`flow/run.js`) does NOT attach signal handlers; it auto-
|
|
13
|
+
* registers each running flow's `handle` here, and this file routes
|
|
14
|
+
* signals to `handle.shutdown()` for every registered handle.
|
|
15
|
+
* - **Top-level forced-shutdown timeout** that bounds the entire
|
|
16
|
+
* `handle.shutdown()` drain. If a flow takes longer than
|
|
17
|
+
* `ENV_VARS.shutdownForceTimeoutMs` (default 30000ms, matching
|
|
18
|
+
* Kubernetes' `terminationGracePeriodSeconds`), this layer logs and
|
|
19
|
+
* force-exits with code 1; otherwise exits with code 0 on graceful
|
|
20
|
+
* completion. The ADR documents three nested timeouts, each on a
|
|
21
|
+
* wider scope: the per-adapter `{ timeout }` (one adapter's drain),
|
|
22
|
+
* the per-flow stage budget, and this process-level ceiling on the
|
|
23
|
+
* whole exit.
|
|
24
|
+
*
|
|
25
|
+
* This layer owns NO storage. The storage-directory cleanup it used to
|
|
26
|
+
* run (an `fs.rm` of STORAGE_DIR at dev/test shutdown) was removed
|
|
27
|
+
* 2026-07-09: the emitter's LevelDB store — removed by ADR-021 — was
|
|
28
|
+
* the only thing that ever wrote there, and a recursive delete with no
|
|
29
|
+
* producer behind it is a hazard, not a feature.
|
|
30
|
+
*
|
|
31
|
+
* What this layer does NOT own:
|
|
32
|
+
* - Per-flow shutdown orchestration. Each registered handle's
|
|
33
|
+
* `handle.shutdown()` runs its own ordered teardown
|
|
34
|
+
* (source → emitters → storages) via `flow/run.js`'s closure.
|
|
35
|
+
*
|
|
36
|
+
* Idempotent attach. `attachHandlers()` is safe to call multiple times;
|
|
37
|
+
* the second call is a no-op via the internal `attached` flag. This
|
|
38
|
+
* closes the `MaxListenersExceededWarning` that earlier accumulated
|
|
39
|
+
* across test runs (each `flow.run()` had registered its own handlers
|
|
40
|
+
* without detach).
|
|
41
|
+
*
|
|
42
|
+
* Legacy path. The block below labelled "LEGACY PATH — scheduled for
|
|
43
|
+
* retirement" exists to serve the pre-DSL example flows
|
|
44
|
+
* (`cpd-run.js`, `rwm-csv-run.js` via their flow files) that wire
|
|
45
|
+
* emitters via `emitters.wire()` and rely on this manager's
|
|
46
|
+
* `emitters.shutdown()`. Modern `flow().run()` pipelines do not use
|
|
47
|
+
* that path — their registries are already drained by
|
|
48
|
+
* `handle.shutdown()` (called from the registered-handle iteration
|
|
49
|
+
* above), so the legacy block finds an empty registry and is a benign
|
|
50
|
+
* no-op for them. The sources half of this path (`wire-sources.js`,
|
|
51
|
+
* the OPC-UA example runners) was removed 2026-07-07 — see ADR-019.
|
|
52
|
+
* The emitters half retires when the legacy examples migrate to the
|
|
53
|
+
* modern flow API.
|
|
54
|
+
*
|
|
55
|
+
* @see ADR-018 (flow lifecycle and signal handling)
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
import { emitters } from '../wiring/index.js';
|
|
59
|
+
import { ENV_VARS } from '../env-vars.js';
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Factory that produces a fresh shutdown-manager instance with isolated
|
|
63
|
+
* internal state. The default export below calls this once for the
|
|
64
|
+
* production singleton; tests call it per spec to get an isolated
|
|
65
|
+
* instance and avoid state bleed across cases. Same shape as the
|
|
66
|
+
* earlier in-file IIFE; the factoring is purely for testability.
|
|
67
|
+
*
|
|
68
|
+
* @returns {{shutdown, attachHandlers, register}} the manager API
|
|
69
|
+
*/
|
|
70
|
+
const createShutdownManager = function () {
|
|
71
|
+
// ========================================================================
|
|
72
|
+
// PRIVATE STATE
|
|
73
|
+
// ========================================================================
|
|
74
|
+
|
|
75
|
+
// Guards against re-entry if a signal arrives mid-shutdown.
|
|
76
|
+
let isShuttingDown = false;
|
|
77
|
+
|
|
78
|
+
// `attachHandlers` idempotency flag. Multiple calls (e.g., from
|
|
79
|
+
// `flow().run()` auto-attach AND from a legacy example's explicit
|
|
80
|
+
// call) are no-ops after the first.
|
|
81
|
+
let attached = false;
|
|
82
|
+
|
|
83
|
+
// Map of registered flow handles. Keyed by the handle object itself,
|
|
84
|
+
// so registering the same handle twice is idempotent (a single entry)
|
|
85
|
+
// and a single unregister fully removes it. Map is purpose-built for
|
|
86
|
+
// this add/remove-over-time pattern; iteration via `.keys()` is
|
|
87
|
+
// clean and never deoptimises.
|
|
88
|
+
const handles = new Map();
|
|
89
|
+
|
|
90
|
+
// ========================================================================
|
|
91
|
+
// REGISTER: Add a flow handle to the shutdown roster
|
|
92
|
+
// ========================================================================
|
|
93
|
+
/**
|
|
94
|
+
* Register a flow `handle` so this manager calls `handle.shutdown()`
|
|
95
|
+
* on signal. `flow().run()` auto-registers its returned handle so
|
|
96
|
+
* the caller experience is unchanged from earlier versions.
|
|
97
|
+
*
|
|
98
|
+
* Idempotent: registering the same handle twice is a single entry,
|
|
99
|
+
* and a single unregister fully removes it. The Map is keyed by
|
|
100
|
+
* the handle object itself — no synthetic IDs needed.
|
|
101
|
+
*
|
|
102
|
+
* Returns an unregister function so a flow that has already shut
|
|
103
|
+
* down (via direct `handle.shutdown()` invocation by the caller)
|
|
104
|
+
* can detach itself, preventing this manager from re-invoking
|
|
105
|
+
* `handle.shutdown()` on a torn-down flow.
|
|
106
|
+
*
|
|
107
|
+
* @param {{shutdown: function}} handle - flow handle exposing shutdown
|
|
108
|
+
* @returns {function} unregister
|
|
109
|
+
*/
|
|
110
|
+
const register = function ( handle ) {
|
|
111
|
+
handles.set( handle, true );
|
|
112
|
+
return function () {
|
|
113
|
+
handles.delete( handle );
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// ========================================================================
|
|
118
|
+
// DRAIN HANDLES: Race per-flow shutdown against the forced timeout
|
|
119
|
+
// ========================================================================
|
|
120
|
+
/**
|
|
121
|
+
* Iterate every registered flow handle, calling `handle.shutdown()`
|
|
122
|
+
* on each in parallel via `Promise.allSettled`. Race the whole drain
|
|
123
|
+
* against `timeoutMs`; if the timeout wins, return false so the
|
|
124
|
+
* caller can log + force-exit with code 1.
|
|
125
|
+
*
|
|
126
|
+
* Returns true when no handles are registered (e.g., the legacy
|
|
127
|
+
* code path with no modern flows in process).
|
|
128
|
+
*/
|
|
129
|
+
const drainHandles = async function ( timeoutMs ) {
|
|
130
|
+
if ( handles.size === 0 ) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const drainAll = Promise.allSettled(
|
|
135
|
+
Array.from( handles.keys() ).map(
|
|
136
|
+
( handle ) => Promise.resolve().then( () => handle.shutdown() )
|
|
137
|
+
)
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Distinct sentinel string so we can tell race winner apart from
|
|
141
|
+
// a Promise.allSettled result array.
|
|
142
|
+
const TIMED_OUT = '__SHUTDOWN_FORCE_TIMEOUT__';
|
|
143
|
+
let timerHandle;
|
|
144
|
+
const timeout = new Promise( ( resolve ) => {
|
|
145
|
+
timerHandle = setTimeout( () => resolve( TIMED_OUT ), timeoutMs );
|
|
146
|
+
} );
|
|
147
|
+
|
|
148
|
+
const winner = await Promise.race( [ drainAll, timeout ] );
|
|
149
|
+
// Cancel the loser. Without this, when drainAll wins the
|
|
150
|
+
// race, the unfired setTimeout still keeps the event loop
|
|
151
|
+
// alive until it fires (up to `timeoutMs` later) — a real
|
|
152
|
+
// tail-latency at end of test suites and in any process
|
|
153
|
+
// that runs the manager and then expects to exit cleanly.
|
|
154
|
+
// Same canonical pattern used by every source `stopFn`.
|
|
155
|
+
clearTimeout( timerHandle );
|
|
156
|
+
return winner !== TIMED_OUT;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// ========================================================================
|
|
160
|
+
// SHUTDOWN: Orchestrate the layered teardown
|
|
161
|
+
// ========================================================================
|
|
162
|
+
/**
|
|
163
|
+
* Run the full process-layer shutdown sequence. Re-entry is guarded
|
|
164
|
+
* via `isShuttingDown` (a second call returns immediately).
|
|
165
|
+
*
|
|
166
|
+
* Order:
|
|
167
|
+
* 1. Drain registered flow handles (modern path).
|
|
168
|
+
* 2. Run the legacy path block (clustered for retirement; warns once
|
|
169
|
+
* when actually doing work).
|
|
170
|
+
* 3. Return graceful (true) / forced (false) — the caller decides
|
|
171
|
+
* the exit code.
|
|
172
|
+
*
|
|
173
|
+
* @returns {Promise<boolean>} true on graceful completion, false on
|
|
174
|
+
* forced timeout
|
|
175
|
+
*/
|
|
176
|
+
const shutdown = async function () {
|
|
177
|
+
if ( isShuttingDown ) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
isShuttingDown = true;
|
|
181
|
+
|
|
182
|
+
const timestamp = new Date().toISOString();
|
|
183
|
+
console.log( `\n[${timestamp}] Shutdown initiated` );
|
|
184
|
+
|
|
185
|
+
// Phase 1 (modern path): drain registered flow handles, racing
|
|
186
|
+
// against the forced-shutdown timeout.
|
|
187
|
+
const graceful = await drainHandles( ENV_VARS.shutdownForceTimeoutMs );
|
|
188
|
+
if ( !graceful ) {
|
|
189
|
+
console.warn(
|
|
190
|
+
` ⚠ Forced shutdown — flow drain exceeded ${ENV_VARS.shutdownForceTimeoutMs}ms; ` +
|
|
191
|
+
'some adapters may not have completed cleanly'
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
// ================================================================
|
|
197
|
+
// LEGACY PATH — scheduled for retirement
|
|
198
|
+
// ================================================================
|
|
199
|
+
//
|
|
200
|
+
// The emitters.shutdown() call below serves the pre-DSL
|
|
201
|
+
// example flows (cpd-run.js, rwm-csv-run.js via their flow
|
|
202
|
+
// files) that wire emitters manually via emitters.wire()
|
|
203
|
+
// instead of using the modern flow().run() API.
|
|
204
|
+
//
|
|
205
|
+
// Modern flow().run() pipelines drain everything via
|
|
206
|
+
// handle.shutdown() (called from the registered-handle
|
|
207
|
+
// iteration above); by the time control reaches this block,
|
|
208
|
+
// the wire-emitters / wire-storages registries are already
|
|
209
|
+
// empty for those flows — the call finds nothing to do
|
|
210
|
+
// (benign no-op).
|
|
211
|
+
//
|
|
212
|
+
// The sources half of this path (wire-sources.js and the
|
|
213
|
+
// OPC-UA example runners) was removed 2026-07-07 (ADR-019).
|
|
214
|
+
// This remainder retires when the legacy examples migrate
|
|
215
|
+
// to the modern flow API.
|
|
216
|
+
// ================================================================
|
|
217
|
+
|
|
218
|
+
const emitterResults = await emitters.shutdown();
|
|
219
|
+
const failedEmitters = emitterResults.filter( ( r ) => r.status === 'rejected' );
|
|
220
|
+
if ( emitterResults.length > 0 ) {
|
|
221
|
+
if ( failedEmitters.length > 0 ) {
|
|
222
|
+
console.warn( ` ⚠ Warning: ${failedEmitters.length} legacy emitter(s) failed to shutdown cleanly` );
|
|
223
|
+
} else {
|
|
224
|
+
console.log( ' ✓ Legacy message queues flushed' );
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ================================================================
|
|
229
|
+
// END LEGACY PATH
|
|
230
|
+
// ================================================================
|
|
231
|
+
|
|
232
|
+
console.log( `[${new Date().toISOString()}] Shutdown complete\n` );
|
|
233
|
+
|
|
234
|
+
return graceful;
|
|
235
|
+
|
|
236
|
+
} catch ( error ) {
|
|
237
|
+
console.error( '✗ Shutdown error:', error );
|
|
238
|
+
process.exitCode = 1;
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}; // shutdown()
|
|
242
|
+
|
|
243
|
+
// ========================================================================
|
|
244
|
+
// ATTACH HANDLERS: Wire up SIGINT/SIGTERM (idempotent)
|
|
245
|
+
// ========================================================================
|
|
246
|
+
/**
|
|
247
|
+
* Register process-level SIGINT and SIGTERM handlers. Idempotent —
|
|
248
|
+
* subsequent calls are no-ops via the internal `attached` flag.
|
|
249
|
+
* Both `flow().run()` (auto-attach) and legacy examples' explicit
|
|
250
|
+
* calls converge here, so handlers are registered exactly once per
|
|
251
|
+
* process regardless of caller.
|
|
252
|
+
*
|
|
253
|
+
* On signal: runs `shutdown()`, then `process.exit(0)` on graceful
|
|
254
|
+
* or `process.exit(1)` on forced timeout.
|
|
255
|
+
*/
|
|
256
|
+
const attachHandlers = function () {
|
|
257
|
+
if ( attached ) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
attached = true;
|
|
261
|
+
|
|
262
|
+
const signalHandler = async function ( signalName ) {
|
|
263
|
+
console.log( `\ncomposer: Received ${signalName}` );
|
|
264
|
+
const graceful = await shutdown();
|
|
265
|
+
|
|
266
|
+
/* c8 ignore next 2 -- fundamentally untestable: invoking the signal handler from a test would call process.exit and kill the test runner. The graceful/forced exit-code mapping is verified by the shutdown() return-value tests above. */
|
|
267
|
+
// eslint-disable-next-line no-process-exit
|
|
268
|
+
process.exit( graceful ? 0 : 1 );
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
process.on( 'SIGINT', () => signalHandler( 'SIGINT (Ctrl-C)' ) );
|
|
272
|
+
process.on( 'SIGTERM', () => signalHandler( 'SIGTERM' ) );
|
|
273
|
+
}; // attachHandlers()
|
|
274
|
+
|
|
275
|
+
// ========================================================================
|
|
276
|
+
// PUBLIC API
|
|
277
|
+
// ========================================================================
|
|
278
|
+
return {
|
|
279
|
+
shutdown,
|
|
280
|
+
attachHandlers,
|
|
281
|
+
register
|
|
282
|
+
};
|
|
283
|
+
}; // createShutdownManager()
|
|
284
|
+
|
|
285
|
+
// Production singleton — one instance per process, exported as default.
|
|
286
|
+
const shutdownManager = createShutdownManager();
|
|
287
|
+
|
|
288
|
+
export default shutdownManager;
|
|
289
|
+
export { createShutdownManager };
|