@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,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Build-time guard against output-field overwrites.
|
|
3
|
+
*
|
|
4
|
+
* Two nodes on the same runtime path must not write the same message field. If they
|
|
5
|
+
* did, the second node's `publishTo` would overwrite the first node's value silently.
|
|
6
|
+
* This module finds such collisions at build time so the flow fails loudly instead.
|
|
7
|
+
*
|
|
8
|
+
* A flow has these runtime paths: a flow with no `.switch()`/`.groupBy()` is one
|
|
9
|
+
* linear path (every node runs on every message); a switched flow is one path per
|
|
10
|
+
* case (only one case runs per message), and `.switch()`/`.groupBy()` forbid any node
|
|
11
|
+
* before the switch, so a case has no shared prefix to merge in. So uniqueness is
|
|
12
|
+
* checked within each path, never across paths: the same field written by two sibling
|
|
13
|
+
* cases is fine, because only one of those cases runs for a given message.
|
|
14
|
+
*
|
|
15
|
+
* Scope: this checks declared outputs - the `storeAs` names in a spec's `stats`. That
|
|
16
|
+
* is exactly what the fan naming rule (`${field}_${label}`) produces. It does not
|
|
17
|
+
* cover a node that derives its output field names from its input field at init time
|
|
18
|
+
* (for example `momentsDigest`, which writes `${field}_M1`, `${field}_mean`, and so
|
|
19
|
+
* on, with no `stats` in its spec). Catching those needs each node to declare the
|
|
20
|
+
* fields it writes; that is tracked as a separate follow-up.
|
|
21
|
+
*
|
|
22
|
+
* @see flow.js - build()/run()/validate() call these before producing or running a flow
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Record one spec's declared outputs against the path's owner map, appending an error
|
|
27
|
+
* for any field a previous node on this path already writes.
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} spec - A built spec with a `stats` object
|
|
30
|
+
* @param {Object} owner - Map of storeAs -> first node name that writes it (mutated)
|
|
31
|
+
* @param {string[]} errors - Collision messages (mutated)
|
|
32
|
+
* @param {string} scopeLabel - Text appended to the field name in the error
|
|
33
|
+
*/
|
|
34
|
+
const recordSpecOutputs = function ( spec, owner, errors, scopeLabel ) {
|
|
35
|
+
// eslint-disable-next-line guard-for-in
|
|
36
|
+
for ( const stat in spec.stats ) {
|
|
37
|
+
const storeAs = spec.stats[ stat ].storeAs;
|
|
38
|
+
if ( owner[ storeAs ] === undefined ) {
|
|
39
|
+
owner[ storeAs ] = spec.name;
|
|
40
|
+
} else {
|
|
41
|
+
errors.push(
|
|
42
|
+
`output field '${storeAs}'${scopeLabel} is written by two nodes on the ` +
|
|
43
|
+
`same path: '${owner[ storeAs ]}' and '${spec.name}'. The second write would ` +
|
|
44
|
+
'overwrite the first. Give them distinct output labels.'
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}; // recordSpecOutputs()
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Find duplicate output fields within one path's specs.
|
|
52
|
+
*
|
|
53
|
+
* @param {Array} specs - The specs on one runtime path (linear flow or one case)
|
|
54
|
+
* @param {string} scopeLabel - Text appended to the field name in the error (for
|
|
55
|
+
* example ` in case 'idle'`); empty for a linear flow
|
|
56
|
+
* @returns {string[]} One message per collision; empty when the path is clean
|
|
57
|
+
*/
|
|
58
|
+
export const findOutputCollisions = function ( specs, scopeLabel ) {
|
|
59
|
+
const owner = Object.create( null ); // storeAs -> name of the node that writes it
|
|
60
|
+
const errors = [];
|
|
61
|
+
|
|
62
|
+
for ( let i = 0; i < specs.length; i += 1 ) {
|
|
63
|
+
const spec = specs[ i ];
|
|
64
|
+
if ( spec.stats ) {
|
|
65
|
+
recordSpecOutputs( spec, owner, errors, scopeLabel );
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return errors;
|
|
70
|
+
}; // findOutputCollisions()
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Find output-field collisions across a whole flow, one path at a time.
|
|
74
|
+
*
|
|
75
|
+
* @param {Object} switchState - The flow's switch/case state
|
|
76
|
+
* @param {Array} flowDefinition - The linear flow's specs (used when not switched)
|
|
77
|
+
* @returns {string[]} One message per collision across all paths; empty when clean
|
|
78
|
+
*/
|
|
79
|
+
export const collectFlowOutputCollisions = function ( switchState, flowDefinition ) {
|
|
80
|
+
if ( switchState.active ) {
|
|
81
|
+
const errors = [];
|
|
82
|
+
for ( let i = 0; i < switchState.caseOrder.length; i += 1 ) {
|
|
83
|
+
const key = switchState.caseOrder[ i ];
|
|
84
|
+
const caseErrors = findOutputCollisions( switchState.caseSpecs[ key ], ` in case '${key}'` );
|
|
85
|
+
for ( let j = 0; j < caseErrors.length; j += 1 ) {
|
|
86
|
+
errors.push( caseErrors[ j ] );
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return errors;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return findOutputCollisions( flowDefinition, '' );
|
|
93
|
+
}; // collectFlowOutputCollisions()
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const SIGNATURE_PATTERNS = Object.create( null );
|
|
2
|
+
SIGNATURE_PATTERNS.nameLogic = 'NAME_LOGIC';
|
|
3
|
+
SIGNATURE_PATTERNS.namePredicateOutputsOptions = 'NAME_PREDICATE_OUTPUTS_OPTIONS';
|
|
4
|
+
SIGNATURE_PATTERNS.namePredicateOptions = 'NAME_PREDICATE_OPTIONS';
|
|
5
|
+
SIGNATURE_PATTERNS.nameXOutputsOptions = 'NAME_X_OUTPUTS_OPTIONS';
|
|
6
|
+
SIGNATURE_PATTERNS.nameXYOutputsOptions = 'NAME_X_Y_OUTPUTS_OPTIONS';
|
|
7
|
+
SIGNATURE_PATTERNS.nameSugarOutputsOptions = 'NAME_SUGAR_OUTPUTS_OPTIONS';
|
|
8
|
+
SIGNATURE_PATTERNS.nameXOptions = 'NAME_X_OPTIONS';
|
|
9
|
+
SIGNATURE_PATTERNS.unknown = 'UNK_PATTERN';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Deep clone utility for groupBy template expansion.
|
|
3
|
+
*
|
|
4
|
+
* Clones spec objects while preserving:
|
|
5
|
+
* - Functions (predicates, tunables) - kept by reference
|
|
6
|
+
* - RegExp objects - properly cloned
|
|
7
|
+
* - undefined values - preserved
|
|
8
|
+
* - Nested objects/arrays - recursively cloned
|
|
9
|
+
*
|
|
10
|
+
* Used during `.endGroup()` to create independent spec copies for each
|
|
11
|
+
* group value without sharing mutable state.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Deep clones a value, preserving functions by reference.
|
|
16
|
+
*
|
|
17
|
+
* @param {*} value - Value to clone
|
|
18
|
+
* @returns {*} Cloned value (functions returned by reference)
|
|
19
|
+
*/
|
|
20
|
+
const deepCloneValue = function ( value ) {
|
|
21
|
+
// Null and undefined pass through
|
|
22
|
+
if ( value === null || value === undefined ) {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const type = typeof value;
|
|
27
|
+
|
|
28
|
+
// Functions - keep reference (tunables, predicates)
|
|
29
|
+
if ( type === 'function' ) {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Primitives (string, number, boolean, symbol, bigint)
|
|
34
|
+
if ( type !== 'object' ) {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// RegExp - clone properly
|
|
39
|
+
if ( value instanceof RegExp ) {
|
|
40
|
+
// eslint-disable-next-line security/detect-non-literal-regexp -- cloning an existing RegExp; source/flags come from the object being cloned, not user input
|
|
41
|
+
return new RegExp( value.source, value.flags );
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Arrays - recurse
|
|
45
|
+
if ( Array.isArray( value ) ) {
|
|
46
|
+
const result = [];
|
|
47
|
+
for ( let i = 0; i < value.length; i += 1 ) {
|
|
48
|
+
result.push( deepCloneValue( value[ i ] ) );
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Objects - use null prototype for safety
|
|
54
|
+
const result = Object.create( null );
|
|
55
|
+
const keys = Object.keys( value );
|
|
56
|
+
for ( let i = 0; i < keys.length; i += 1 ) {
|
|
57
|
+
const key = keys[ i ];
|
|
58
|
+
result[ key ] = deepCloneValue( value[ key ] );
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export { deepCloneValue };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// flow/driver.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Headless-flow driver.
|
|
5
|
+
*
|
|
6
|
+
* A driver feeds a running flow that has no source. It plays the role a source
|
|
7
|
+
* adapter plays: it calls `handle.processMessage( msg )` once per message. It
|
|
8
|
+
* exists so callers get that contract right in one place — the sync-first call
|
|
9
|
+
* (ADR-013), awaiting only on the rare yield tick, and routing a node fault to a
|
|
10
|
+
* handler so one bad message cannot stop the loop.
|
|
11
|
+
*
|
|
12
|
+
* Hot-path allocation: none. A successful message allocates nothing. `feedOne`
|
|
13
|
+
* returns `undefined` (it is not an async function, so no Promise wrapper).
|
|
14
|
+
* `feedAll` keeps primitive counters and iterates a sync source with `for...of`
|
|
15
|
+
* (no per-item await). Faults route to a callback rather than a per-message
|
|
16
|
+
* result object. Promises are allocated only on the rare yield tick (ADR-013,
|
|
17
|
+
* ADR-024) and on faults — never on the success path (ADR-004 zero-allocation
|
|
18
|
+
* hot path). The yield-tick Promise arrives AFTER the message is fully
|
|
19
|
+
* processed (ADR-024 process-then-breathe), so ignoring it never delays or
|
|
20
|
+
* reorders a message; awaiting it is purely the event-loop breath.
|
|
21
|
+
*
|
|
22
|
+
* What it does NOT do: detect a silently dropped message. A message dropped for
|
|
23
|
+
* an over-cap asset id, or a missing assetId field, returns nothing from
|
|
24
|
+
* `processMessage`, exactly as a success does — so `feedAll` counts it as
|
|
25
|
+
* processed. See `docs/handbook/headless-flow.md`.
|
|
26
|
+
*
|
|
27
|
+
* @see docs/handbook/headless-flow.md
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Default fault handler. Surfaces every fault so none is silent. Allocates only
|
|
32
|
+
* when a fault occurs (rare), never on the success path. Provide your own
|
|
33
|
+
* `onError` to route or throttle in production.
|
|
34
|
+
*
|
|
35
|
+
* @param {Error} error - The node fault (wrapped: `error.nodeModule`,
|
|
36
|
+
* `error.cause`).
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
const logFault = function ( error ) {
|
|
40
|
+
console.error( `composer/headlessDriver: a message failed — ${error.message}` );
|
|
41
|
+
}; // logFault()
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create a driver bound to one headless flow handle.
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} handle - Handle returned by `flow(...).run()`. Must expose a
|
|
47
|
+
* `processMessage` function.
|
|
48
|
+
* @param {Object} [opts={}] - Options.
|
|
49
|
+
* @param {function(Error, Object): void} [opts.onError] - Called as
|
|
50
|
+
* `onError( error, msg )` for every message that faults, in both `feedOne` and
|
|
51
|
+
* `feedAll`. Defaults to logging the fault to the console.
|
|
52
|
+
* @returns {{feedOne: function, feedAll: function}} The driver.
|
|
53
|
+
* @throws {TypeError} When `handle` has no `processMessage`, or `onError` is
|
|
54
|
+
* given but is not a function.
|
|
55
|
+
*/
|
|
56
|
+
const headlessDriver = function ( handle, opts = {} ) {
|
|
57
|
+
if ( !handle || typeof handle.processMessage !== 'function' ) {
|
|
58
|
+
throw new TypeError(
|
|
59
|
+
'composer/headlessDriver: handle must be the object returned by ' +
|
|
60
|
+
'flow(...).run() — it has no processMessage() function'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const onError = opts.onError === undefined ? logFault : opts.onError;
|
|
65
|
+
if ( typeof onError !== 'function' ) {
|
|
66
|
+
throw new TypeError(
|
|
67
|
+
'composer/headlessDriver: onError must be a function'
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Feed one message. Use this for push sources, where an event hands you a
|
|
73
|
+
* message and there is no loop to own.
|
|
74
|
+
*
|
|
75
|
+
* Zero allocation on the success path: returns `undefined` synchronously. A
|
|
76
|
+
* fault is routed to `onError`, never thrown, so the caller's event handler
|
|
77
|
+
* is never interrupted. On the rare yield tick it returns a Promise you MAY
|
|
78
|
+
* await for backpressure; that Promise never rejects (a yield-path fault
|
|
79
|
+
* still routes to `onError`).
|
|
80
|
+
*
|
|
81
|
+
* @param {Object} msg - The message to feed.
|
|
82
|
+
* @returns {(undefined|Promise<void>)} `undefined` on the hot path; a Promise
|
|
83
|
+
* on the yield tick.
|
|
84
|
+
*/
|
|
85
|
+
const feedOne = function ( msg ) {
|
|
86
|
+
let pending;
|
|
87
|
+
try {
|
|
88
|
+
pending = handle.processMessage( msg );
|
|
89
|
+
} catch ( error ) {
|
|
90
|
+
onError( error, msg );
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
if ( pending instanceof Promise ) {
|
|
94
|
+
return pending.catch( function ( error ) {
|
|
95
|
+
onError( error, msg );
|
|
96
|
+
} );
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}; // feedOne()
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Feed every message from a source, in order. Use this for pull sources,
|
|
103
|
+
* where you iterate the data.
|
|
104
|
+
*
|
|
105
|
+
* Consumes any sync or async iterable — an array, a generator, an async
|
|
106
|
+
* generator, or a Node object-mode stream. A sync source is iterated with
|
|
107
|
+
* `for...of`, so it allocates nothing per message; an async source is pulled
|
|
108
|
+
* with `for await`, where a Promise per item is inherent. Each message is
|
|
109
|
+
* processed and awaited before the next begins, so the flow sees one message
|
|
110
|
+
* at a time and an async source throttles itself. A faulting message does
|
|
111
|
+
* not stop the stream: it is counted in `failed` and routed to `onError`.
|
|
112
|
+
*
|
|
113
|
+
* A failure of the source iterator itself (a stream that errors mid-read) is
|
|
114
|
+
* not caught here — it rejects the returned Promise for the caller to handle.
|
|
115
|
+
*
|
|
116
|
+
* @param {Iterable|AsyncIterable} source - Messages to feed, in order.
|
|
117
|
+
* @returns {Promise<{processed: number, failed: number}>} Counts taken after
|
|
118
|
+
* the source is exhausted.
|
|
119
|
+
* @throws {TypeError} When `source` is not a sync or async iterable.
|
|
120
|
+
*/
|
|
121
|
+
const feedAll = async function ( source ) {
|
|
122
|
+
const isAsync = typeof source?.[ Symbol.asyncIterator ] === 'function';
|
|
123
|
+
const isSync = typeof source?.[ Symbol.iterator ] === 'function';
|
|
124
|
+
if ( !isAsync && !isSync ) {
|
|
125
|
+
throw new TypeError(
|
|
126
|
+
'composer/headlessDriver.feedAll: source must be a sync or async ' +
|
|
127
|
+
'iterable (an array, a generator, or a stream)'
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let processed = 0;
|
|
132
|
+
let failed = 0;
|
|
133
|
+
|
|
134
|
+
// Two loops on purpose: `for await` over a sync source would allocate a
|
|
135
|
+
// Promise per item (it awaits each value). The sync branch uses `for...of`
|
|
136
|
+
// and awaits only on the rare yield tick, keeping the hot path alloc-free.
|
|
137
|
+
if ( isAsync ) {
|
|
138
|
+
for await ( const msg of source ) {
|
|
139
|
+
try {
|
|
140
|
+
const pending = handle.processMessage( msg );
|
|
141
|
+
if ( pending instanceof Promise ) {
|
|
142
|
+
await pending;
|
|
143
|
+
}
|
|
144
|
+
processed += 1;
|
|
145
|
+
} catch ( error ) {
|
|
146
|
+
failed += 1;
|
|
147
|
+
onError( error, msg );
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
for ( const msg of source ) {
|
|
152
|
+
try {
|
|
153
|
+
const pending = handle.processMessage( msg );
|
|
154
|
+
if ( pending instanceof Promise ) {
|
|
155
|
+
await pending; // eslint-disable-line no-await-in-loop
|
|
156
|
+
}
|
|
157
|
+
processed += 1;
|
|
158
|
+
} catch ( error ) {
|
|
159
|
+
failed += 1;
|
|
160
|
+
onError( error, msg );
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { processed, failed };
|
|
166
|
+
}; // feedAll()
|
|
167
|
+
|
|
168
|
+
return { feedOne, feedAll };
|
|
169
|
+
}; // headlessDriver()
|
|
170
|
+
|
|
171
|
+
export { headlessDriver };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { SIGNATURE_PATTERNS } from './consts.js';
|
|
2
|
+
|
|
3
|
+
const expandXOutputsOptions = function ( meta, nodeArgs, flowState ) {
|
|
4
|
+
const { isNodeNameDuplicate } = flowState;
|
|
5
|
+
const specs = [];
|
|
6
|
+
const seenX = Object.create( null );
|
|
7
|
+
const [ baseName, inputs, outputs, ...balanceArgs ] = nodeArgs;
|
|
8
|
+
// const inputs = args[ 1 ];
|
|
9
|
+
// const outputs = args[ 2 ]
|
|
10
|
+
|
|
11
|
+
for ( let i = 0; i < inputs.length; i += 1 ) {
|
|
12
|
+
const param = inputs[ i ];
|
|
13
|
+
|
|
14
|
+
if ( seenX[ param ] === 1 ) {
|
|
15
|
+
throw new Error( `WinkComposer/flow: duplicate input found: '${param}'.` );
|
|
16
|
+
}
|
|
17
|
+
seenX[ param ] = 1;
|
|
18
|
+
|
|
19
|
+
const instName = `${baseName}_${param}`;
|
|
20
|
+
if ( isNodeNameDuplicate( instName ) ) throw Error( `WinkComposer/flow: duplicate node ${instName} found.` );
|
|
21
|
+
const stats = Object.create( null );
|
|
22
|
+
for ( const st in outputs ) {
|
|
23
|
+
if (Object.prototype.hasOwnProperty.call( outputs, st ) ) {
|
|
24
|
+
const stName = outputs[ st ];
|
|
25
|
+
stats[ st ] = Object.create( null );
|
|
26
|
+
// Fixed fan naming: ${field}_${label}. The same rule forEach uses, so
|
|
27
|
+
// array sugar and forEach name outputs identically. (Overwrites on one
|
|
28
|
+
// path are caught by the build-time guard, check-output-collisions.js.)
|
|
29
|
+
stats[ st ].storeAs = `${param}_${stName}`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const spec = meta.buildSpec( instName, param, stats, ...balanceArgs );
|
|
34
|
+
specs.push( spec );
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return specs;
|
|
38
|
+
}; // expandXOutputsOptions()
|
|
39
|
+
|
|
40
|
+
const expandXOptions = function ( meta, nodeArgs, flowState ) {
|
|
41
|
+
const { isNodeNameDuplicate } = flowState;
|
|
42
|
+
const specs = [];
|
|
43
|
+
const seenX = Object.create( null );
|
|
44
|
+
const [ baseName, inputs, ...balanceArgs ] = nodeArgs;
|
|
45
|
+
|
|
46
|
+
for ( let i = 0; i < inputs.length; i += 1 ) {
|
|
47
|
+
const param = inputs[ i ];
|
|
48
|
+
|
|
49
|
+
if ( seenX[ param ] === 1 ) {
|
|
50
|
+
throw new Error( `WinkComposer/flow: duplicate input found: '${param}'.` );
|
|
51
|
+
}
|
|
52
|
+
seenX[ param ] = 1;
|
|
53
|
+
|
|
54
|
+
const instName = `${baseName}_${param}`;
|
|
55
|
+
if ( isNodeNameDuplicate( instName ) ) throw Error( `WinkComposer/flow: duplicate node ${instName} found.` );
|
|
56
|
+
|
|
57
|
+
const spec = meta.buildSpec( instName, param, ...balanceArgs );
|
|
58
|
+
specs.push( spec );
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return specs;
|
|
62
|
+
}; // expandXOptions()
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Expand array sugar for single-input nodes into multiple canonical calls.
|
|
66
|
+
* Assumes canonical signature: buildSpec( name, x, storeAs, options )
|
|
67
|
+
* - name is made unique per input as `${baseName}_${param}`
|
|
68
|
+
* - storeAs follows the fixed fan rule `${param}_${stat}` (same as forEach)
|
|
69
|
+
* Returns an array of per-input specs (throws on invalid inputs).
|
|
70
|
+
*
|
|
71
|
+
* nodeArgs: baseName, inputs, outputs, balance stuff
|
|
72
|
+
*/
|
|
73
|
+
export const expandSingleInputArraySugar = function ( meta, nodeArgs, flowState, pattern ) {
|
|
74
|
+
if ( pattern === SIGNATURE_PATTERNS.nameXOptions ) return expandXOptions( meta, nodeArgs, flowState );
|
|
75
|
+
return expandXOutputsOptions( meta, nodeArgs, flowState );
|
|
76
|
+
}; // expandSingleInputArraySugar()
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Template spec expansion for groupBy construct.
|
|
3
|
+
*
|
|
4
|
+
* Expands a template spec for a specific group value by:
|
|
5
|
+
* 1. Deep cloning the template (preserving functions)
|
|
6
|
+
* 2. Prefixing the node name with group value
|
|
7
|
+
* 3. Resolving matching tunables to concrete values
|
|
8
|
+
* 4. Prefixing trigger targets with group value
|
|
9
|
+
*
|
|
10
|
+
* Note: storeAs values are NOT prefixed. Output field names remain the same
|
|
11
|
+
* across all groups since each case is an isolated pipeline path.
|
|
12
|
+
*
|
|
13
|
+
* @see ADR-007: groupBy DSL Construct
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { deepCloneValue } from './deep-clone-spec.js';
|
|
17
|
+
import { resolveTunablesInValue } from './resolve-tunables.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Prefixes trigger target names with the group value.
|
|
21
|
+
*
|
|
22
|
+
* Handles:
|
|
23
|
+
* - Direct triggers: spec.triggers[].targets
|
|
24
|
+
* - Controller logic triggers: spec.logic[].triggers[].targets
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} spec - The spec to process (mutated in place)
|
|
27
|
+
* @param {string|number} groupValue - Prefix to add
|
|
28
|
+
*/
|
|
29
|
+
const prefixTriggerTargets = function ( spec, groupValue ) {
|
|
30
|
+
const prefixTargets = function ( triggers ) {
|
|
31
|
+
if ( !Array.isArray( triggers ) ) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for ( let i = 0; i < triggers.length; i += 1 ) {
|
|
36
|
+
const trigger = triggers[ i ];
|
|
37
|
+
if ( Array.isArray( trigger.targets ) ) {
|
|
38
|
+
trigger.targets = trigger.targets.map(
|
|
39
|
+
( target ) => `${groupValue}_${target}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Direct triggers on the spec
|
|
46
|
+
prefixTargets( spec.triggers );
|
|
47
|
+
|
|
48
|
+
// Controller node logic triggers
|
|
49
|
+
if ( Array.isArray( spec.logic ) ) {
|
|
50
|
+
for ( let j = 0; j < spec.logic.length; j += 1 ) {
|
|
51
|
+
prefixTargets( spec.logic[ j ].triggers );
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Expands a template spec for a specific group value.
|
|
58
|
+
*
|
|
59
|
+
* Performs:
|
|
60
|
+
* 1. Deep clone (preserves functions by reference)
|
|
61
|
+
* 2. Prefix node name: 'corr' → 'idle_corr'
|
|
62
|
+
* 3. Resolve matching tunables: lookupByField('rpmBand', {idle: 3.4}) → 3.4
|
|
63
|
+
* 4. Prefix trigger targets: ['corr', 'ph'] → ['idle_corr', 'idle_ph']
|
|
64
|
+
*
|
|
65
|
+
* Note: storeAs values are NOT prefixed. Each group writes to the same
|
|
66
|
+
* output field names since they run in isolated pipeline paths.
|
|
67
|
+
*
|
|
68
|
+
* @param {Object} templateSpec - Original template spec
|
|
69
|
+
* @param {string|number} groupValue - Group value for this expansion
|
|
70
|
+
* @param {string} groupByField - The field used for grouping
|
|
71
|
+
* @returns {Object} Expanded spec ready for the group's case
|
|
72
|
+
*/
|
|
73
|
+
const expandTemplateSpec = function ( templateSpec, groupValue, groupByField ) {
|
|
74
|
+
// Step 1: Deep clone (preserves functions by reference)
|
|
75
|
+
const spec = deepCloneValue( templateSpec );
|
|
76
|
+
|
|
77
|
+
// Step 2: Prefix node name
|
|
78
|
+
spec.name = `${groupValue}_${spec.name}`;
|
|
79
|
+
|
|
80
|
+
// Step 3: Resolve tunables (returns new object with resolved values)
|
|
81
|
+
const resolved = resolveTunablesInValue( spec, groupByField, groupValue );
|
|
82
|
+
|
|
83
|
+
// Step 4: Prefix trigger targets (mutates in place)
|
|
84
|
+
prefixTriggerTargets( resolved, groupValue );
|
|
85
|
+
|
|
86
|
+
// Note: storeAs values are intentionally NOT prefixed
|
|
87
|
+
// Each group outputs to the same field names (e.g., 'r2', 'shiftDetected')
|
|
88
|
+
// This is correct because each case is an isolated pipeline path
|
|
89
|
+
|
|
90
|
+
return resolved;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export { expandTemplateSpec, prefixTriggerTargets };
|