@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,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `forEach` - the chain fan. It runs a callback once per field in a
|
|
3
|
+
* concrete list, giving each run an `each` handle, and adds one copy of the chain
|
|
4
|
+
* the callback builds for that field. It is the multi-node sibling of array sugar:
|
|
5
|
+
* array sugar fans a single node across a field list; `forEach` fans a whole chain,
|
|
6
|
+
* including predicate-input nodes (e.g. `persistenceCheck`) and two-input nodes
|
|
7
|
+
* (e.g. `ratio`) that array sugar cannot reach.
|
|
8
|
+
*
|
|
9
|
+
* Everything happens at build time. The fan expands into ordinary node specs through
|
|
10
|
+
* the same machinery the per-node DSL methods use - the signature-pattern dispatch
|
|
11
|
+
* (`spec-builder.js`), the shared router (`get-target-array.js`), the collision
|
|
12
|
+
* checkers, and `init` validation - so nothing new runs per message and the wiring,
|
|
13
|
+
* partition, and run layers are untouched.
|
|
14
|
+
*
|
|
15
|
+
* Naming is the fan-owned scheme (ADR-006/ADR-007): a node's
|
|
16
|
+
* name takes the field as a suffix (`clean` -> `clean_scb1`); a node's output takes
|
|
17
|
+
* the field as a prefix (the output labelled `delta` is stored as `scb1_delta`). The
|
|
18
|
+
* scheme is fixed, not the configurable naming policy, so the names are predictable -
|
|
19
|
+
* which `each.out` and the reduce nodes (`tally`, `unbalance`) rely on.
|
|
20
|
+
*
|
|
21
|
+
* @see get-target-array.js - the shared spec router
|
|
22
|
+
* @see resolve-pick-by-field.js - the build-time pickByField resolver
|
|
23
|
+
* @see expand-single-input-array.js - the single-node fan (array sugar)
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import * as nodes from '../nodes/index.js';
|
|
27
|
+
import { validateWithSchema } from '../core/utils/validate/index.js';
|
|
28
|
+
import { schemas } from './signatures-schema.js';
|
|
29
|
+
import { SIGNATURE_PATTERNS } from './consts.js';
|
|
30
|
+
import { getSignaturePattern } from './get-signature-pattern.js';
|
|
31
|
+
import { specBuilder } from './spec-builder.js';
|
|
32
|
+
import { getTargetArray } from './get-target-array.js';
|
|
33
|
+
import { resolvePickByField } from './resolve-pick-by-field.js';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Locate the index of a named argument slot in a signature schema (the source of
|
|
37
|
+
* truth for argument positions). Returns -1 when the pattern has no such slot.
|
|
38
|
+
*
|
|
39
|
+
* @param {Array} slots - The positional schema for a signature pattern
|
|
40
|
+
* @param {string} name - The slot name to find ('outputs' or 'options')
|
|
41
|
+
* @returns {number} The slot index, or -1
|
|
42
|
+
*/
|
|
43
|
+
const slotIndex = function ( slots, name ) {
|
|
44
|
+
for ( let i = 0; i < slots.length; i += 1 ) {
|
|
45
|
+
if ( slots[ i ].name === name ) {
|
|
46
|
+
return i;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return -1;
|
|
50
|
+
}; // slotIndex()
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Build `api.forEach`. Precomputes node metadata once, then returns the chainable
|
|
54
|
+
* `forEach( fields, callback )` method.
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} api - The fluent API object (returned by forEach for chaining)
|
|
57
|
+
* @param {Object} flowState - Shared flow build state (definitions, checkers, etc.)
|
|
58
|
+
* @returns {Function} The forEach method
|
|
59
|
+
*/
|
|
60
|
+
export const makeForEach = function ( api, flowState ) {
|
|
61
|
+
const {
|
|
62
|
+
flowDefinition,
|
|
63
|
+
importSet,
|
|
64
|
+
isNodeNameDuplicate,
|
|
65
|
+
switchState,
|
|
66
|
+
groupByState,
|
|
67
|
+
markPipelineStarted
|
|
68
|
+
} = flowState;
|
|
69
|
+
|
|
70
|
+
// Precompute metadata once, over the same DSL-eligible node filter flow.js uses.
|
|
71
|
+
const metaByNode = Object.create( null );
|
|
72
|
+
const nodeNames = Object.keys( nodes ).filter( ( n ) => ( typeof nodes[ n ]?.getDSLMetadata === 'function' ) );
|
|
73
|
+
for ( let i = 0; i < nodeNames.length; i += 1 ) {
|
|
74
|
+
metaByNode[ nodeNames[ i ] ] = nodes[ nodeNames[ i ] ].getDSLMetadata();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Expand one fanned node call for one field: transform the args (fan naming +
|
|
78
|
+
// pickByField resolution), then build, validate, and route the spec through the
|
|
79
|
+
// shared machinery. `recorded` maps each output label to its stored field, for
|
|
80
|
+
// this channel's `each.out`.
|
|
81
|
+
const fanBuild = function ( node, meta, field, recorded, args ) {
|
|
82
|
+
importSet.add( node );
|
|
83
|
+
|
|
84
|
+
const pattern = getSignaturePattern( meta.specSchema );
|
|
85
|
+
const callContext = `.forEach[ ${field} ].${node}( ${args[ 0 ]} )`;
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
// Controllers orchestrate other nodes by name; fanning one would leave its
|
|
89
|
+
// trigger targets unfanned and pointing at the wrong nodes. Reject it loudly
|
|
90
|
+
// rather than emit a silently-wrong per-field controller.
|
|
91
|
+
if ( pattern === SIGNATURE_PATTERNS.nameLogic ) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`WinkComposer/flow: forEach cannot fan a controller ('${args[ 0 ]}'); ` +
|
|
94
|
+
'fan data-processing nodes only.'
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Validate the original args, so any message names what the author wrote.
|
|
99
|
+
const status = validateWithSchema( schemas[ pattern ], [ ...args ], 'Arg' );
|
|
100
|
+
status.throwIfInvalid( 'argument' );
|
|
101
|
+
|
|
102
|
+
const slots = schemas[ pattern ];
|
|
103
|
+
const outIdx = slotIndex( slots, 'outputs' );
|
|
104
|
+
const optIdx = slotIndex( slots, 'options' );
|
|
105
|
+
|
|
106
|
+
const t = [ ...args ];
|
|
107
|
+
t[ 0 ] = `${args[ 0 ]}_${field}`;
|
|
108
|
+
|
|
109
|
+
// Node-name collision check before building stats (matches make-node-method),
|
|
110
|
+
// so a rejected node never registers its output params in the global checker.
|
|
111
|
+
if ( isNodeNameDuplicate( t[ 0 ] ) ) {
|
|
112
|
+
throw new Error( `WinkComposer/flow: duplicate node ${t[ 0 ]} found.` );
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Outputs: store each labelled output as `${field}_${label}`, and record the
|
|
116
|
+
// label -> stored-field map so a later step can read it via each.out.
|
|
117
|
+
if ( outIdx >= 0 ) {
|
|
118
|
+
const rawOutputs = args[ outIdx ];
|
|
119
|
+
const fanOutputs = Object.create( null );
|
|
120
|
+
const outKeys = Object.keys( rawOutputs );
|
|
121
|
+
for ( let i = 0; i < outKeys.length; i += 1 ) {
|
|
122
|
+
const label = rawOutputs[ outKeys[ i ] ];
|
|
123
|
+
if ( Object.prototype.hasOwnProperty.call( recorded, label ) ) {
|
|
124
|
+
console.warn(
|
|
125
|
+
`WinkComposer/flow: forEach output label '${label}' written by ` +
|
|
126
|
+
`more than one step on field '${field}'; each.out will return the last.`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
recorded[ label ] = `${field}_${label}`;
|
|
130
|
+
fanOutputs[ outKeys[ i ] ] = `${field}_${label}`;
|
|
131
|
+
}
|
|
132
|
+
t[ outIdx ] = fanOutputs;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Options: resolve any pickByField marker to this channel's value. Every
|
|
136
|
+
// fannable pattern has an options slot, so optIdx is always >= 0 here; the
|
|
137
|
+
// guard is only for an omitted (optional) options argument.
|
|
138
|
+
if ( optIdx < t.length ) {
|
|
139
|
+
t[ optIdx ] = resolvePickByField( args[ optIdx ], field );
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const buildSpecFor = specBuilder( meta );
|
|
143
|
+
const spec = buildSpecFor[ pattern ]( t );
|
|
144
|
+
const targetArray = getTargetArray( switchState, groupByState, flowDefinition );
|
|
145
|
+
nodes[ node ].init( spec );
|
|
146
|
+
targetArray.push( spec );
|
|
147
|
+
} catch ( error ) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`WinkComposer/flow: Failed to process "${callContext}"\n` +
|
|
150
|
+
` Reason: ${error.message}\n`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}; // fanBuild()
|
|
154
|
+
|
|
155
|
+
// Build the per-channel `each` handle: the node methods (fan-aware, chainable),
|
|
156
|
+
// the current field, and each.out for reading an earlier step's stored field.
|
|
157
|
+
const makeEachHandle = function ( field ) {
|
|
158
|
+
const recorded = Object.create( null );
|
|
159
|
+
const each = Object.create( null );
|
|
160
|
+
|
|
161
|
+
for ( let i = 0; i < nodeNames.length; i += 1 ) {
|
|
162
|
+
const node = nodeNames[ i ];
|
|
163
|
+
each[ node ] = function ( ...args ) {
|
|
164
|
+
fanBuild( node, metaByNode[ node ], field, recorded, args );
|
|
165
|
+
return each;
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
each.field = field;
|
|
170
|
+
each.out = function ( label ) {
|
|
171
|
+
if ( !Object.prototype.hasOwnProperty.call( recorded, label ) ) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
`WinkComposer/flow: each.out( '${label}' ) on field '${field}': ` +
|
|
174
|
+
`no earlier step in this chain wrote an output labelled '${label}'.`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return recorded[ label ];
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return each;
|
|
181
|
+
}; // makeEachHandle()
|
|
182
|
+
|
|
183
|
+
// The shared per-field routine. The plain-array driver below calls it in a loop;
|
|
184
|
+
// a future groupBy-deferred path would call the same routine per resolved field.
|
|
185
|
+
const expandForField = function ( field, callback ) {
|
|
186
|
+
callback( makeEachHandle( field ) );
|
|
187
|
+
}; // expandForField()
|
|
188
|
+
|
|
189
|
+
return function ( fields, callback ) {
|
|
190
|
+
// forEach inside a groupBy is deferred: there the field list is a pickByGroup
|
|
191
|
+
// that only resolves at .endGroup(), so the callback cannot run where written.
|
|
192
|
+
if ( groupByState.active ) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
'WinkComposer/flow: forEach inside groupBy is not supported yet; ' +
|
|
195
|
+
'the field list must be a concrete array at the call site.'
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if ( !Array.isArray( fields ) || fields.length === 0 ) {
|
|
199
|
+
throw new Error( 'WinkComposer/flow: forEach requires a non-empty array of field names.' );
|
|
200
|
+
}
|
|
201
|
+
if ( typeof callback !== 'function' ) {
|
|
202
|
+
throw new Error( 'WinkComposer/flow: forEach requires a callback function.' );
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const seen = Object.create( null );
|
|
206
|
+
for ( let i = 0; i < fields.length; i += 1 ) {
|
|
207
|
+
const field = fields[ i ];
|
|
208
|
+
if ( typeof field !== 'string' || field.length === 0 ) {
|
|
209
|
+
throw new Error( 'WinkComposer/flow: forEach field names must be non-empty strings.' );
|
|
210
|
+
}
|
|
211
|
+
if ( Object.prototype.hasOwnProperty.call( seen, field ) ) {
|
|
212
|
+
throw new Error( `WinkComposer/flow: forEach duplicate field '${field}' in field list.` );
|
|
213
|
+
}
|
|
214
|
+
seen[ field ] = 1;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
markPipelineStarted();
|
|
218
|
+
|
|
219
|
+
for ( let i = 0; i < fields.length; i += 1 ) {
|
|
220
|
+
expandForField( fields[ i ], callback );
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return api;
|
|
224
|
+
};
|
|
225
|
+
}; // makeForEach()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SIGNATURE_PATTERNS } from './consts.js';
|
|
2
|
+
|
|
3
|
+
export const getSignaturePattern = function ( schema ) {
|
|
4
|
+
// Controller node
|
|
5
|
+
if ( !schema.from && schema.logic ) return SIGNATURE_PATTERNS.nameLogic;
|
|
6
|
+
|
|
7
|
+
// Pure predicate based nodes (no inputs)
|
|
8
|
+
if ( !schema.from && schema.predicate ) {
|
|
9
|
+
return schema.stats?.required ?
|
|
10
|
+
// e.g. presistenceCheck
|
|
11
|
+
SIGNATURE_PATTERNS.namePredicateOutputsOptions :
|
|
12
|
+
// e.f. PassIf
|
|
13
|
+
SIGNATURE_PATTERNS.namePredicateOptions;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Input-based nodes
|
|
17
|
+
const props = schema.from?.properties;
|
|
18
|
+
// This schema has no `from`, `logic`, or `predicate`, so its shape is not one we
|
|
19
|
+
// recognize. Return `unknown` so make-node-method.js can raise its clear "unknown
|
|
20
|
+
// signature pattern" error, instead of crashing on `props.x` below with a raw
|
|
21
|
+
// TypeError.
|
|
22
|
+
if ( !props ) return SIGNATURE_PATTERNS.unknown;
|
|
23
|
+
if ( props.x && !props.y && !schema.stats ) return SIGNATURE_PATTERNS.nameXOptions; // momentsDigest: outputs are implied
|
|
24
|
+
// Single input node i.e. only `x` e.g. delta
|
|
25
|
+
if ( props.x && !props.y ) return SIGNATURE_PATTERNS.nameXOutputsOptions;
|
|
26
|
+
// Dual input node i.e. `x` and `y` e.g. diff
|
|
27
|
+
if ( props.x && props.y ) return SIGNATURE_PATTERNS.nameXYOutputsOptions;
|
|
28
|
+
|
|
29
|
+
// Unable to determine signature pattern
|
|
30
|
+
return SIGNATURE_PATTERNS.unknown;
|
|
31
|
+
}; // getSignaturePattern()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Routes built node specs to the array they belong in, based on the
|
|
3
|
+
* flow's current switch/case/groupBy state. This is the single router used by every
|
|
4
|
+
* spec-producing path in the flow DSL: the per-node method (`make-node-method.js`)
|
|
5
|
+
* and the `forEach` fan (`for-each.js`). Keeping it in one leaf module means both
|
|
6
|
+
* paths land specs identically and there is no second copy to drift.
|
|
7
|
+
*
|
|
8
|
+
* Why a function and not a field: the target depends on transient build state
|
|
9
|
+
* (are we collecting a groupBy template? inside a `.case()`? in the linear flow?),
|
|
10
|
+
* which is only known at the moment a spec is produced.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Determines the target array for spec accumulation based on switch/case/groupBy state.
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} switchState - The switch/case state machine
|
|
17
|
+
* @param {Object} groupByState - The groupBy state machine
|
|
18
|
+
* @param {Array} flowDefinition - Default target for single-pipeline mode
|
|
19
|
+
* @returns {Array} Target array to push specs into
|
|
20
|
+
* @throws {Error} If node is called in invalid state (outside case block, after break)
|
|
21
|
+
*/
|
|
22
|
+
export const getTargetArray = function ( switchState, groupByState, flowDefinition ) {
|
|
23
|
+
// GroupBy template collection mode - route to template array
|
|
24
|
+
if ( groupByState && groupByState.active ) {
|
|
25
|
+
return groupByState.templateSpecs;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Switch/case routing
|
|
29
|
+
if ( switchState && switchState.active ) {
|
|
30
|
+
if ( switchState.currentCase === null ) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
'WinkComposer/flow: node called outside of a .case() block - ' +
|
|
33
|
+
'after .switch(), you must call .case() before adding nodes'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
if ( switchState.caseEnded ) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
'WinkComposer/flow: node called after .break() - ' +
|
|
39
|
+
'start a new .case() before adding more nodes'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return switchState.caseSpecs[ switchState.currentCase ];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return flowDefinition;
|
|
46
|
+
}; // getTargetArray()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// flow/inspect.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Flow inspection for debugging.
|
|
5
|
+
*
|
|
6
|
+
* Returns structured information about the flow configuration
|
|
7
|
+
* for debugging and documentation purposes.
|
|
8
|
+
*
|
|
9
|
+
* Supports both single-pipeline and multi-specialization (switch/case) modes.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns debug information about the flow.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} flowName - Name of the flow
|
|
16
|
+
* @param {Array} flowDefinition - Node specifications (for single-pipeline mode)
|
|
17
|
+
* @param {Set} importSet - Set of node names used
|
|
18
|
+
* @param {Object} runtime - Runtime configuration
|
|
19
|
+
* @param {Object} [switchState] - Switch/case state machine (for multi-specialization)
|
|
20
|
+
* @returns {Object} Flow inspection data
|
|
21
|
+
*/
|
|
22
|
+
export const inspectFlow = function ( flowName, flowDefinition, importSet, runtime, switchState ) {
|
|
23
|
+
const result = {
|
|
24
|
+
flowName,
|
|
25
|
+
imports: Array.from( importSet ).sort(),
|
|
26
|
+
runtime: {
|
|
27
|
+
hasSource: runtime.source !== null,
|
|
28
|
+
emitterCount: Object.keys( runtime.emitters ).length,
|
|
29
|
+
storageCount: Object.keys( runtime.storages || {} ).length,
|
|
30
|
+
partitionField: runtime.partitionField,
|
|
31
|
+
specializationField: runtime.specializationField,
|
|
32
|
+
yieldThreshold: runtime.yieldThreshold,
|
|
33
|
+
assetClass: runtime.assetClass ? {
|
|
34
|
+
name: runtime.assetClass.name,
|
|
35
|
+
insightTypes: Object.keys( runtime.assetClass.insightTypes || {} )
|
|
36
|
+
} : null
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if ( switchState && switchState.active ) {
|
|
41
|
+
// Multi-specialization mode
|
|
42
|
+
result.mode = 'multi-specialization';
|
|
43
|
+
result.specializations = Object.create( null );
|
|
44
|
+
|
|
45
|
+
let totalNodes = 0;
|
|
46
|
+
for ( let i = 0; i < switchState.caseOrder.length; i += 1 ) {
|
|
47
|
+
const key = switchState.caseOrder[ i ];
|
|
48
|
+
const specs = switchState.caseSpecs[ key ];
|
|
49
|
+
result.specializations[ key ] = {
|
|
50
|
+
nodeCount: specs.length,
|
|
51
|
+
nodes: specs.map( ( s ) => ( { name: s.name, type: s.nodeType } ) )
|
|
52
|
+
};
|
|
53
|
+
totalNodes += specs.length;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
result.nodeCount = totalNodes;
|
|
57
|
+
result.caseOrder = switchState.caseOrder.slice();
|
|
58
|
+
} else {
|
|
59
|
+
// Single-pipeline mode (backward compatible)
|
|
60
|
+
result.mode = 'single-pipeline';
|
|
61
|
+
result.nodeCount = flowDefinition.length;
|
|
62
|
+
result.nodes = flowDefinition.map( ( s ) => ( { name: s.name, type: s.nodeType } ) );
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
};
|
|
67
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// flow/load-node-module.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Dynamic loader for node modules at runtime.
|
|
5
|
+
*
|
|
6
|
+
* Provides lazy loading of node modules using dynamic imports. This keeps
|
|
7
|
+
* the DSL build phase lightweight by deferring module loading until .run()
|
|
8
|
+
* is called. Validates node names against the canonical list before loading.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { toKebab } from '../core/utils/flow/index.js';
|
|
12
|
+
import { isValidNodeName } from '../nodes/node-names.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Cache for loaded node modules to avoid redundant dynamic imports.
|
|
16
|
+
* @type {Map<string, object>}
|
|
17
|
+
*/
|
|
18
|
+
const moduleCache = new Map();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Dynamically loads a node module by its camelCase name.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} nodeName - camelCase node name (e.g., 'esMean', 'pageHinkley')
|
|
24
|
+
* @returns {Promise<object>} The node module exports (init, update, publishTo, etc.)
|
|
25
|
+
* @throws {Error} If nodeName is not a valid node
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const esMean = await loadNodeModule('esMean');
|
|
29
|
+
* // esMean.init, esMean.update, esMean.publishTo, etc.
|
|
30
|
+
*/
|
|
31
|
+
export const loadNodeModule = async function ( nodeName ) {
|
|
32
|
+
// Validate node name
|
|
33
|
+
if ( !isValidNodeName( nodeName ) ) {
|
|
34
|
+
throw new Error( `Unknown node type: "${nodeName}"` );
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Return cached module if available
|
|
38
|
+
if ( moduleCache.has( nodeName ) ) {
|
|
39
|
+
return moduleCache.get( nodeName );
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const kebabName = toKebab( nodeName );
|
|
43
|
+
const nodeModule = await import( `../nodes/${kebabName}/index.js` );
|
|
44
|
+
|
|
45
|
+
// Cache for subsequent calls
|
|
46
|
+
moduleCache.set( nodeName, nodeModule );
|
|
47
|
+
|
|
48
|
+
return nodeModule;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Loads multiple node modules in parallel.
|
|
53
|
+
*
|
|
54
|
+
* @param {string[]} nodeNames - Array of camelCase node names
|
|
55
|
+
* @returns {Promise<Map<string, object>>} Map of nodeName -> module
|
|
56
|
+
* @throws {Error} If any nodeName is invalid
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* const modules = await loadNodeModules(['esMean', 'threshold', 'emitIf']);
|
|
60
|
+
* modules.get('esMean').update(state, msg);
|
|
61
|
+
*/
|
|
62
|
+
export const loadNodeModules = async function ( nodeNames ) {
|
|
63
|
+
const uniqueNames = [ ...new Set( nodeNames ) ];
|
|
64
|
+
|
|
65
|
+
const loadPromises = uniqueNames.map( async ( name ) => {
|
|
66
|
+
const mod = await loadNodeModule( name );
|
|
67
|
+
return [ name, mod ];
|
|
68
|
+
} );
|
|
69
|
+
|
|
70
|
+
const entries = await Promise.all( loadPromises );
|
|
71
|
+
|
|
72
|
+
return new Map( entries );
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Clears the module cache. Useful for testing or hot-reloading scenarios.
|
|
77
|
+
*/
|
|
78
|
+
export const clearModuleCache = function () {
|
|
79
|
+
moduleCache.clear();
|
|
80
|
+
};
|
|
81
|
+
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as nodes from '../nodes/index.js';
|
|
2
|
+
import { validateWithSchema } from '../core/utils/validate/index.js';
|
|
3
|
+
import { schemas } from './signatures-schema.js';
|
|
4
|
+
import { expandSingleInputArraySugar } from './expand-single-input-array.js';
|
|
5
|
+
import { SIGNATURE_PATTERNS } from './consts.js';
|
|
6
|
+
import { getSignaturePattern } from './get-signature-pattern.js';
|
|
7
|
+
import { specBuilder } from './spec-builder.js';
|
|
8
|
+
import { getTargetArray } from './get-target-array.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a chainable DSL method for a WinkComposer node.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} api - The fluent API object to extend
|
|
14
|
+
* @param {string} node - Node type name (e.g., 'esMean')
|
|
15
|
+
* @param {Object} meta - Node metadata from getDSLMetadata()
|
|
16
|
+
* @param {Object} flowState - Shared state including definitions and collision checkers
|
|
17
|
+
* @returns {Function} Chainable method that validates and adds node specs
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const method = makeNodeMethod(api, 'esMean', meta, flowState);
|
|
21
|
+
* method('smooth', 'temperature', { esMean: 'smoothTemp' }, { alpha: 0.3 });
|
|
22
|
+
*/
|
|
23
|
+
export const makeNodeMethod = function ( api, node, meta, flowState ) {
|
|
24
|
+
const {
|
|
25
|
+
flowDefinition,
|
|
26
|
+
importSet,
|
|
27
|
+
isNodeNameDuplicate,
|
|
28
|
+
switchState,
|
|
29
|
+
groupByState,
|
|
30
|
+
markPipelineStarted
|
|
31
|
+
} = flowState;
|
|
32
|
+
|
|
33
|
+
return function ( ...args ) {
|
|
34
|
+
// Mark pipeline as started (blocks subsequent config method calls)
|
|
35
|
+
if ( markPipelineStarted ) {
|
|
36
|
+
markPipelineStarted();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
importSet.add( node );
|
|
40
|
+
|
|
41
|
+
let built = null;
|
|
42
|
+
const schema = meta.specSchema;
|
|
43
|
+
|
|
44
|
+
// During groupBy, use the template-local node-name checker. Node names are
|
|
45
|
+
// prefixed per group at expansion (idle_corr, cruise_corr), so the global
|
|
46
|
+
// checker would false-positive on the shared template names.
|
|
47
|
+
const isGroupByActive = groupByState && groupByState.active;
|
|
48
|
+
const effectiveNodeChecker = isGroupByActive ?
|
|
49
|
+
groupByState.templateNodeChecker :
|
|
50
|
+
isNodeNameDuplicate;
|
|
51
|
+
|
|
52
|
+
const buildSpecFor = specBuilder( meta );
|
|
53
|
+
|
|
54
|
+
// Create effective flowState for sugar expansion
|
|
55
|
+
const effectiveFlowState = isGroupByActive ?
|
|
56
|
+
{ ...flowState, isNodeNameDuplicate: effectiveNodeChecker } :
|
|
57
|
+
flowState;
|
|
58
|
+
// The eligible nodes must have `x` defined as `string` and there is no `y`
|
|
59
|
+
// property. Note on logic:
|
|
60
|
+
// Safe because of short-circuit:
|
|
61
|
+
// - If the left check fails, the right check is not evaluated.
|
|
62
|
+
// - If the left check passes, then `schema.from.properties` must exist.
|
|
63
|
+
const isEligible = schema.from?.properties?.x?.type === 'string' && !schema.from.properties.y;
|
|
64
|
+
const isSugar = isEligible && Array.isArray( args[ 1 ] );
|
|
65
|
+
const callContext = `.${node}( ${args[ 0 ]}${args.length > 1 ? `, +${args.length - 1}` : ''} )${isSugar ? ` → ${args[ 1 ].join( ', ' )}` : ''}`;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
// Array syntactic sugar for single-input nodes only: ( name, [x...], options )—this
|
|
69
|
+
// signature generates the `storeAs` names automatically using the fixed
|
|
70
|
+
// `${field}_${label}` rule. If explicit `storeAs` naming is required then
|
|
71
|
+
// use the alternate signature: ( name, x, output, options? ).
|
|
72
|
+
//
|
|
73
|
+
if ( isSugar ) {
|
|
74
|
+
// Use our standard validators to verify the input signature before building.
|
|
75
|
+
const status = validateWithSchema( schemas[ SIGNATURE_PATTERNS.nameSugarOutputsOptions ], [ ...args ], 'Arg' );
|
|
76
|
+
const pattern = getSignaturePattern( schema );
|
|
77
|
+
status.throwIfInvalid( 'argument' );
|
|
78
|
+
built = expandSingleInputArraySugar( meta, [ ...args ], effectiveFlowState, pattern );
|
|
79
|
+
} else {
|
|
80
|
+
// Canonical builder path
|
|
81
|
+
const instName = args[ 0 ];
|
|
82
|
+
if ( effectiveNodeChecker( instName ) ) throw Error( `WinkComposer/flow: duplicate node ${instName} found.` );
|
|
83
|
+
const pattern = getSignaturePattern( schema );
|
|
84
|
+
if ( pattern === SIGNATURE_PATTERNS.unknown ) throw Error( `WinkComposer/flow: unknown signature pattern found for ${instName}.` );
|
|
85
|
+
const status = validateWithSchema( schemas[ pattern ], [ ...args ], 'Arg' );
|
|
86
|
+
status.throwIfInvalid( 'argument' );
|
|
87
|
+
built = buildSpecFor[ pattern ]( [ ...args ] );
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Normalize to array: allows downstream code to handle both single specs
|
|
91
|
+
// and spec arrays uniformly without conditional logic.
|
|
92
|
+
const specs = Array.isArray( built ) ? built : [ built ];
|
|
93
|
+
|
|
94
|
+
// Determine target array based on switch/case/groupBy state
|
|
95
|
+
const targetArray = getTargetArray( switchState, groupByState, flowDefinition );
|
|
96
|
+
|
|
97
|
+
// Apply flow-level naming and use initialize for spec to check any error.
|
|
98
|
+
for ( let j = 0; j < specs.length; j += 1 ) {
|
|
99
|
+
nodes[ node ].init( specs[ j ] );
|
|
100
|
+
targetArray.push( specs[ j ] );
|
|
101
|
+
}
|
|
102
|
+
} catch ( error ) {
|
|
103
|
+
const enhancedError = new Error(
|
|
104
|
+
`WinkComposer/flow: Failed to process "${callContext}"\n` +
|
|
105
|
+
` Reason: ${error.message}\n`
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
throw enhancedError;
|
|
109
|
+
}
|
|
110
|
+
return api;
|
|
111
|
+
};
|
|
112
|
+
}; // makeNodeMethod()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Build-time resolver for `pickByField` markers inside a `forEach`.
|
|
3
|
+
*
|
|
4
|
+
* A `pickByField( map )` marker is a function carrying
|
|
5
|
+
* `.semantics = { type: 'pickByField', map }`; it stands for "the value for the
|
|
6
|
+
* current fan channel". When a `forEach` expands a chain for one field, this walker
|
|
7
|
+
* replaces every such marker in that node's options with `map[ field ]` — a plain
|
|
8
|
+
* value baked into the spec before the node is initialized. After it runs, nothing
|
|
9
|
+
* of `pickByField` survives into the wired pipeline, so it costs nothing per message.
|
|
10
|
+
*
|
|
11
|
+
* It mirrors `resolve-tunables.js` (the groupBy resolver) on every pass-through:
|
|
12
|
+
* other tunables and plain predicates are preserved for runtime, primitives pass
|
|
13
|
+
* straight through, and arrays and plain objects are rebuilt with their entries
|
|
14
|
+
* resolved. It differs only at the leaf — it resolves `pickByField` by the current
|
|
15
|
+
* field (throwing on a missing key), where the groupBy resolver resolves
|
|
16
|
+
* `lookupByField` by the group value (with a default). The two leaf rules diverge,
|
|
17
|
+
* which is why this is a separate module rather than a shared walker.
|
|
18
|
+
*
|
|
19
|
+
* @see resolve-tunables.js - the groupBy sibling resolver
|
|
20
|
+
* @see ADR-006 - Tunable Pattern for Dynamic Parameters
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Recursively resolves `pickByField` markers in an options value for one fan field.
|
|
25
|
+
*
|
|
26
|
+
* @param {*} value - The value to process (an option, or a nested part of one)
|
|
27
|
+
* @param {string} field - The current fan channel's field name (each.field)
|
|
28
|
+
* @returns {*} The value with every pickByField marker replaced by map[ field ]
|
|
29
|
+
* @throws {Error} If a pickByField map has no entry for `field`
|
|
30
|
+
*/
|
|
31
|
+
export const resolvePickByField = function ( value, field ) {
|
|
32
|
+
// A pickByField marker: resolve to map[ field ]. A missing key is a build error,
|
|
33
|
+
// not a silent undefined that would slip through to the first message.
|
|
34
|
+
if ( typeof value === 'function' && value.semantics && value.semantics.type === 'pickByField' ) {
|
|
35
|
+
const map = value.semantics.map;
|
|
36
|
+
if ( !Object.prototype.hasOwnProperty.call( map, field ) ) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`WinkComposer/flow: pickByField has no entry for field '${field}' ` +
|
|
39
|
+
`(map keys: ${Object.keys( map ).join( ', ' )}).`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
// Return the resolved value as-is; do not recurse into it, so a resolved
|
|
43
|
+
// field-keyed object (e.g. a sanitize range) reaches the node's own resolver
|
|
44
|
+
// intact.
|
|
45
|
+
return map[ field ];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Any other function - a plain predicate, or a different tunable like
|
|
49
|
+
// lookupByField - is preserved for runtime. pickByField is the only marker this
|
|
50
|
+
// resolver owns.
|
|
51
|
+
if ( typeof value === 'function' ) {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Null/undefined/primitives pass through unchanged.
|
|
56
|
+
if ( value === null || value === undefined || typeof value !== 'object' ) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Arrays - resolve each element into a new array.
|
|
61
|
+
if ( Array.isArray( value ) ) {
|
|
62
|
+
const result = [];
|
|
63
|
+
for ( let i = 0; i < value.length; i += 1 ) {
|
|
64
|
+
result.push( resolvePickByField( value[ i ], field ) );
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Objects - resolve each property into a clean object.
|
|
70
|
+
const result = Object.create( null );
|
|
71
|
+
const keys = Object.keys( value );
|
|
72
|
+
for ( let i = 0; i < keys.length; i += 1 ) {
|
|
73
|
+
result[ keys[ i ] ] = resolvePickByField( value[ keys[ i ] ], field );
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return result;
|
|
77
|
+
}; // resolvePickByField()
|