@winkjs/composer 0.0.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +106 -80
- package/package.json +56 -28
- package/src/composer.js +75 -0
- package/src/core/batch/aoo-encoder.js +148 -0
- package/src/core/batch/index.js +12 -0
- package/src/core/batch/sliding-window.js +91 -0
- package/src/core/codec/index.js +137 -0
- package/src/core/emitter-manager/mqtt/constants.js +102 -0
- package/src/core/emitter-manager/mqtt/emitter.js +780 -0
- package/src/core/emitter-manager/mqtt/index.js +190 -0
- package/src/core/emitter-manager/mqtt/mqtt-store.js +538 -0
- package/src/core/emitter-manager/terminal/assert-columns.js +98 -0
- package/src/core/emitter-manager/terminal/formatters.js +160 -0
- package/src/core/emitter-manager/terminal/index.js +341 -0
- package/src/core/env-vars.js +245 -0
- package/src/core/mqtt-protocol.js +47 -0
- package/src/core/partition-manager/index.js +7 -0
- package/src/core/partition-manager/init.js +44 -0
- package/src/core/partition-manager/introspect.js +15 -0
- package/src/core/partition-manager/publish-to.js +6 -0
- package/src/core/partition-manager/recompute.js +5 -0
- package/src/core/partition-manager/reset.js +5 -0
- package/src/core/partition-manager/update.js +264 -0
- package/src/core/partition-manager/validate-flow.js +157 -0
- package/src/core/semantics/digest.js +128 -0
- package/src/core/semantics/index.js +83 -0
- package/src/core/semantics/loader.js +571 -0
- package/src/core/semantics/schemas/asset-class-schema.js +218 -0
- package/src/core/semantics/schemas/column-schema.js +651 -0
- package/src/core/semantics/schemas/enum-schema.js +153 -0
- package/src/core/semantics/schemas/index.js +51 -0
- package/src/core/semantics/warnings.js +141 -0
- package/src/core/shutdown-manager/index.js +289 -0
- package/src/core/source-manager/csv/index.js +171 -0
- package/src/core/source-manager/csv/start.js +456 -0
- package/src/core/source-manager/mqtt/client.js +406 -0
- package/src/core/source-manager/mqtt/constants.js +147 -0
- package/src/core/source-manager/mqtt/dedup.js +244 -0
- package/src/core/source-manager/mqtt/index.js +260 -0
- package/src/core/source-manager/mqtt/status.js +546 -0
- package/src/core/source-manager/test-harness/comparator.js +226 -0
- package/src/core/source-manager/test-harness/field-generator.js +85 -0
- package/src/core/source-manager/test-harness/fuzz.js +87 -0
- package/src/core/source-manager/test-harness/index.js +115 -0
- package/src/core/source-manager/test-harness/prng.js +57 -0
- package/src/core/source-manager/test-harness/start.js +232 -0
- package/src/core/source-manager/test-harness/validate.js +213 -0
- package/src/core/storage-manager/questdb/assert-columns.js +120 -0
- package/src/core/storage-manager/questdb/ensure-tables.js +187 -0
- package/src/core/storage-manager/questdb/index.js +1251 -0
- package/src/core/storage-manager/questdb/persist-plan.js +488 -0
- package/src/core/storage-manager/questdb/writers.js +142 -0
- package/src/core/test-utils/sealed-record.js +80 -0
- package/src/core/test-utils/soak-signature.js +149 -0
- package/src/core/test-utils/tcp-proxy.js +106 -0
- package/src/core/tunable/helpers.js +254 -0
- package/src/core/tunable/index.js +87 -0
- package/src/core/utils/console-formatter/index.js +207 -0
- package/src/core/utils/create-smart-store-as/index.js +98 -0
- package/src/core/utils/create-smart-store-as/sanitize-field.js +46 -0
- package/src/core/utils/create-smart-store-as/smart-truncate.js +93 -0
- package/src/core/utils/flow/index.js +5 -0
- package/src/core/utils/flow/make-collision-checker.js +23 -0
- package/src/core/utils/flow/to-kebab.js +20 -0
- package/src/core/utils/half-life/index.js +111 -0
- package/src/core/utils/node/disable.js +11 -0
- package/src/core/utils/node/enable.js +11 -0
- package/src/core/utils/node/execute-triggers.js +40 -0
- package/src/core/utils/node/index.js +10 -0
- package/src/core/utils/node/pause.js +11 -0
- package/src/core/utils/node/populate-predicate-inputs.js +14 -0
- package/src/core/utils/node/publish-nan.js +19 -0
- package/src/core/utils/node/reset-predicate-inputs.js +11 -0
- package/src/core/utils/node/unpause.js +11 -0
- package/src/core/utils/node/validate-spec.js +22 -0
- package/src/core/utils/options/resolve-field-keyed.js +115 -0
- package/src/core/utils/quantize/index.js +75 -0
- package/src/core/utils/stats/formulas.js +67 -0
- package/src/core/utils/stats/index.js +3 -0
- package/src/core/utils/validate/composer-validators.js +141 -0
- package/src/core/utils/validate/helpers.js +206 -0
- package/src/core/utils/validate/index.js +12 -0
- package/src/core/utils/validate/type-validators.js +538 -0
- package/src/core/utils/validate/validate.js +107 -0
- package/src/core/utils/validate/validators.js +177 -0
- package/src/core/wiring/assert-handle.js +57 -0
- package/src/core/wiring/assert-module.js +63 -0
- package/src/core/wiring/index.js +5 -0
- package/src/core/wiring/node-type-to-module.js +17 -0
- package/src/core/wiring/wire-emitters.js +256 -0
- package/src/core/wiring/wire-linear-graph.js +25 -0
- package/src/core/wiring/wire-node.js +157 -0
- package/src/core/wiring/wire-semantics.js +173 -0
- package/src/core/wiring/wire-storages.js +344 -0
- package/src/flow/check-output-collisions.js +93 -0
- package/src/flow/consts.js +9 -0
- package/src/flow/deep-clone-spec.js +64 -0
- package/src/flow/driver.js +171 -0
- package/src/flow/expand-single-input-array.js +76 -0
- package/src/flow/expand-template-spec.js +93 -0
- package/src/flow/flow-api-schemas.js +300 -0
- package/src/flow/flow.js +658 -0
- package/src/flow/for-each.js +225 -0
- package/src/flow/get-signature-pattern.js +31 -0
- package/src/flow/get-target-array.js +46 -0
- package/src/flow/inspect.js +67 -0
- package/src/flow/load-node-module.js +81 -0
- package/src/flow/make-node-method.js +112 -0
- package/src/flow/resolve-pick-by-field.js +77 -0
- package/src/flow/resolve-tunables.js +76 -0
- package/src/flow/run.js +392 -0
- package/src/flow/serialize.js +161 -0
- package/src/flow/signatures-schema.js +222 -0
- package/src/flow/spec-builder.js +65 -0
- package/src/flow/validate-triggers.js +250 -0
- package/src/flow/validate.js +261 -0
- package/src/nodes/accumulate/index.js +21 -0
- package/src/nodes/accumulate/init.js +48 -0
- package/src/nodes/accumulate/introspect.js +123 -0
- package/src/nodes/accumulate/publish-to.js +34 -0
- package/src/nodes/accumulate/recompute.js +27 -0
- package/src/nodes/accumulate/reset.js +25 -0
- package/src/nodes/accumulate/update.js +42 -0
- package/src/nodes/appraise/calibrate.js +64 -0
- package/src/nodes/appraise/decision.js +66 -0
- package/src/nodes/appraise/deviation.js +140 -0
- package/src/nodes/appraise/index.js +23 -0
- package/src/nodes/appraise/init.js +165 -0
- package/src/nodes/appraise/integrate.js +45 -0
- package/src/nodes/appraise/introspect.js +352 -0
- package/src/nodes/appraise/publish-to.js +93 -0
- package/src/nodes/appraise/receptor.js +69 -0
- package/src/nodes/appraise/recompute.js +79 -0
- package/src/nodes/appraise/reset.js +53 -0
- package/src/nodes/appraise/update.js +130 -0
- package/src/nodes/archive/csv/index.js +6 -0
- package/src/nodes/archive/csv/init.js +36 -0
- package/src/nodes/archive/csv/load.js +70 -0
- package/src/nodes/archive/csv/reset.js +14 -0
- package/src/nodes/archive/csv/simple-parser.js +216 -0
- package/src/nodes/archive/csv/test.js +173 -0
- package/src/nodes/archive/csv/update.js +63 -0
- package/src/nodes/butterworth-filter/index.js +18 -0
- package/src/nodes/butterworth-filter/init.js +184 -0
- package/src/nodes/butterworth-filter/introspect.js +228 -0
- package/src/nodes/butterworth-filter/publish-to.js +21 -0
- package/src/nodes/butterworth-filter/recompute.js +13 -0
- package/src/nodes/butterworth-filter/reset.js +30 -0
- package/src/nodes/butterworth-filter/update.js +38 -0
- package/src/nodes/categorize/index.js +19 -0
- package/src/nodes/categorize/init.js +58 -0
- package/src/nodes/categorize/introspect.js +177 -0
- package/src/nodes/categorize/publish-to.js +29 -0
- package/src/nodes/categorize/recompute.js +14 -0
- package/src/nodes/categorize/reset.js +16 -0
- package/src/nodes/categorize/update.js +53 -0
- package/src/nodes/controller/index.js +9 -0
- package/src/nodes/controller/init.js +55 -0
- package/src/nodes/controller/introspect.js +91 -0
- package/src/nodes/controller/publish-to.js +8 -0
- package/src/nodes/controller/recompute.js +9 -0
- package/src/nodes/controller/reset.js +17 -0
- package/src/nodes/controller/update.js +63 -0
- package/src/nodes/diff/index.js +19 -0
- package/src/nodes/diff/init.js +37 -0
- package/src/nodes/diff/introspect.js +132 -0
- package/src/nodes/diff/publish-to.js +21 -0
- package/src/nodes/diff/recompute.js +12 -0
- package/src/nodes/diff/reset.js +12 -0
- package/src/nodes/diff/update.js +37 -0
- package/src/nodes/digest-moments/formulas.js +12 -0
- package/src/nodes/digest-moments/index.js +14 -0
- package/src/nodes/digest-moments/init.js +67 -0
- package/src/nodes/digest-moments/introspect.js +154 -0
- package/src/nodes/digest-moments/publish-to.js +34 -0
- package/src/nodes/digest-moments/recompute.js +14 -0
- package/src/nodes/digest-moments/reset.js +14 -0
- package/src/nodes/digest-moments/update.js +120 -0
- package/src/nodes/dwell-time-tracker/index.js +14 -0
- package/src/nodes/dwell-time-tracker/init.js +59 -0
- package/src/nodes/dwell-time-tracker/introspect.js +137 -0
- package/src/nodes/dwell-time-tracker/publish-to.js +22 -0
- package/src/nodes/dwell-time-tracker/recompute.js +8 -0
- package/src/nodes/dwell-time-tracker/reset.js +23 -0
- package/src/nodes/dwell-time-tracker/update.js +103 -0
- package/src/nodes/emit-if/helpers.js +94 -0
- package/src/nodes/emit-if/index.js +16 -0
- package/src/nodes/emit-if/init.js +67 -0
- package/src/nodes/emit-if/introspect.js +102 -0
- package/src/nodes/emit-if/publish-to.js +13 -0
- package/src/nodes/emit-if/recompute.js +12 -0
- package/src/nodes/emit-if/reset.js +27 -0
- package/src/nodes/emit-if/update.js +93 -0
- package/src/nodes/es-correlation/index.js +19 -0
- package/src/nodes/es-correlation/init.js +73 -0
- package/src/nodes/es-correlation/introspect.js +176 -0
- package/src/nodes/es-correlation/publish-to.js +32 -0
- package/src/nodes/es-correlation/recompute.js +56 -0
- package/src/nodes/es-correlation/reset.js +27 -0
- package/src/nodes/es-correlation/update.js +110 -0
- package/src/nodes/es-mean/index.js +20 -0
- package/src/nodes/es-mean/init.js +58 -0
- package/src/nodes/es-mean/introspect.js +129 -0
- package/src/nodes/es-mean/publish-to.js +19 -0
- package/src/nodes/es-mean/recompute.js +16 -0
- package/src/nodes/es-mean/reset.js +24 -0
- package/src/nodes/es-mean/update.js +69 -0
- package/src/nodes/es-pairwise-correlation/index.js +19 -0
- package/src/nodes/es-pairwise-correlation/init.js +98 -0
- package/src/nodes/es-pairwise-correlation/introspect.js +187 -0
- package/src/nodes/es-pairwise-correlation/publish-to.js +31 -0
- package/src/nodes/es-pairwise-correlation/recompute.js +69 -0
- package/src/nodes/es-pairwise-correlation/reset.js +29 -0
- package/src/nodes/es-pairwise-correlation/update.js +89 -0
- package/src/nodes/es-stats/index.js +21 -0
- package/src/nodes/es-stats/init.js +105 -0
- package/src/nodes/es-stats/introspect.js +157 -0
- package/src/nodes/es-stats/publish-to.js +42 -0
- package/src/nodes/es-stats/recompute.js +74 -0
- package/src/nodes/es-stats/reset.js +49 -0
- package/src/nodes/es-stats/update-envelope.js +44 -0
- package/src/nodes/es-stats/update-welford.js +64 -0
- package/src/nodes/es-stats/update.js +59 -0
- package/src/nodes/index.js +83 -0
- package/src/nodes/invert-flag/index.js +20 -0
- package/src/nodes/invert-flag/init.js +36 -0
- package/src/nodes/invert-flag/introspect.js +115 -0
- package/src/nodes/invert-flag/publish-to.js +23 -0
- package/src/nodes/invert-flag/recompute.js +12 -0
- package/src/nodes/invert-flag/reset.js +12 -0
- package/src/nodes/invert-flag/update.js +32 -0
- package/src/nodes/kalman1d/index.js +20 -0
- package/src/nodes/kalman1d/init.js +103 -0
- package/src/nodes/kalman1d/introspect.js +250 -0
- package/src/nodes/kalman1d/publish-to.js +34 -0
- package/src/nodes/kalman1d/recompute.js +23 -0
- package/src/nodes/kalman1d/reset.js +29 -0
- package/src/nodes/kalman1d/update.js +144 -0
- package/src/nodes/kernel/index.js +18 -0
- package/src/nodes/kernel/init.js +66 -0
- package/src/nodes/kernel/introspect.js +163 -0
- package/src/nodes/kernel/presets.js +74 -0
- package/src/nodes/kernel/publish-to.js +25 -0
- package/src/nodes/kernel/recompute.js +14 -0
- package/src/nodes/kernel/reset.js +20 -0
- package/src/nodes/kernel/update.js +69 -0
- package/src/nodes/lag/index.js +21 -0
- package/src/nodes/lag/init.js +87 -0
- package/src/nodes/lag/introspect.js +202 -0
- package/src/nodes/lag/publish-to.js +64 -0
- package/src/nodes/lag/recompute.js +33 -0
- package/src/nodes/lag/reset.js +49 -0
- package/src/nodes/lag/update.js +133 -0
- package/src/nodes/median3/index.js +19 -0
- package/src/nodes/median3/init.js +40 -0
- package/src/nodes/median3/introspect.js +116 -0
- package/src/nodes/median3/publish-to.js +21 -0
- package/src/nodes/median3/recompute.js +12 -0
- package/src/nodes/median3/reset.js +15 -0
- package/src/nodes/median3/update.js +58 -0
- package/src/nodes/moments-digest/copy-from.js +12 -0
- package/src/nodes/moments-digest/flush.js +17 -0
- package/src/nodes/moments-digest/index.js +132 -0
- package/src/nodes/moments-digest/init.js +84 -0
- package/src/nodes/moments-digest/introspect.js +122 -0
- package/src/nodes/moments-digest/prelude.js +28 -0
- package/src/nodes/moments-digest/publish-to.js +31 -0
- package/src/nodes/moments-digest/recompute.js +9 -0
- package/src/nodes/moments-digest/reset.js +20 -0
- package/src/nodes/moments-digest/update.js +182 -0
- package/src/nodes/node-names.js +67 -0
- package/src/nodes/page-hinkley/index.js +20 -0
- package/src/nodes/page-hinkley/init.js +66 -0
- package/src/nodes/page-hinkley/introspect.js +193 -0
- package/src/nodes/page-hinkley/publish-to.js +28 -0
- package/src/nodes/page-hinkley/recompute.js +14 -0
- package/src/nodes/page-hinkley/reset.js +23 -0
- package/src/nodes/page-hinkley/update.js +105 -0
- package/src/nodes/pass-if/index.js +21 -0
- package/src/nodes/pass-if/init.js +37 -0
- package/src/nodes/pass-if/introspect.js +93 -0
- package/src/nodes/pass-if/publish-to.js +15 -0
- package/src/nodes/pass-if/recompute.js +15 -0
- package/src/nodes/pass-if/reset.js +18 -0
- package/src/nodes/pass-if/update.js +37 -0
- package/src/nodes/persist-if/helpers.js +111 -0
- package/src/nodes/persist-if/index.js +9 -0
- package/src/nodes/persist-if/init.js +98 -0
- package/src/nodes/persist-if/introspect.js +106 -0
- package/src/nodes/persist-if/publish-to.js +21 -0
- package/src/nodes/persist-if/recompute.js +20 -0
- package/src/nodes/persist-if/reset.js +35 -0
- package/src/nodes/persist-if/update.js +112 -0
- package/src/nodes/persistence-check/index.js +14 -0
- package/src/nodes/persistence-check/init.js +38 -0
- package/src/nodes/persistence-check/introspect.js +144 -0
- package/src/nodes/persistence-check/publish-to.js +11 -0
- package/src/nodes/persistence-check/recompute.js +7 -0
- package/src/nodes/persistence-check/reset.js +12 -0
- package/src/nodes/persistence-check/update.js +57 -0
- package/src/nodes/process-index/index.js +15 -0
- package/src/nodes/process-index/init.js +48 -0
- package/src/nodes/process-index/introspect.js +182 -0
- package/src/nodes/process-index/publish-to.js +29 -0
- package/src/nodes/process-index/recompute.js +8 -0
- package/src/nodes/process-index/reset.js +8 -0
- package/src/nodes/process-index/update.js +75 -0
- package/src/nodes/ratio/index.js +14 -0
- package/src/nodes/ratio/init.js +37 -0
- package/src/nodes/ratio/introspect.js +155 -0
- package/src/nodes/ratio/publish-to.js +15 -0
- package/src/nodes/ratio/recompute.js +7 -0
- package/src/nodes/ratio/reset.js +7 -0
- package/src/nodes/ratio/update.js +47 -0
- package/src/nodes/sanitize/index.js +82 -0
- package/src/nodes/sanitize/init.js +82 -0
- package/src/nodes/sanitize/introspect.js +194 -0
- package/src/nodes/sanitize/publish-to.js +15 -0
- package/src/nodes/sanitize/recompute.js +8 -0
- package/src/nodes/sanitize/reset.js +14 -0
- package/src/nodes/sanitize/update.js +102 -0
- package/src/nodes/spike-guard/index.js +14 -0
- package/src/nodes/spike-guard/init.js +47 -0
- package/src/nodes/spike-guard/introspect.js +130 -0
- package/src/nodes/spike-guard/publish-to.js +35 -0
- package/src/nodes/spike-guard/recompute.js +14 -0
- package/src/nodes/spike-guard/reset.js +19 -0
- package/src/nodes/spike-guard/update.js +89 -0
- package/src/nodes/sse-emitter/index.js +7 -0
- package/src/nodes/sse-emitter/init.js +105 -0
- package/src/nodes/sse-emitter/introspect.js +134 -0
- package/src/nodes/sse-emitter/publish-to.js +6 -0
- package/src/nodes/sse-emitter/recompute.js +16 -0
- package/src/nodes/sse-emitter/reset.js +19 -0
- package/src/nodes/sse-emitter/update.js +69 -0
- package/src/nodes/sse-emitter/utils/broadcast-heartbeat.js +36 -0
- package/src/nodes/sse-emitter/utils/build-message.js +31 -0
- package/src/nodes/sse-emitter/utils/connection-manager.js +45 -0
- package/src/nodes/sse-emitter/utils/endpoint-initializer.js +20 -0
- package/src/nodes/sse-emitter/utils/health-monitor.js +38 -0
- package/src/nodes/sse-emitter/utils/qos-handler.js +41 -0
- package/src/nodes/state-change-detector/helpers.js +35 -0
- package/src/nodes/state-change-detector/index.js +12 -0
- package/src/nodes/state-change-detector/init.js +56 -0
- package/src/nodes/state-change-detector/introspect.js +146 -0
- package/src/nodes/state-change-detector/publish-to.js +20 -0
- package/src/nodes/state-change-detector/recompute.js +6 -0
- package/src/nodes/state-change-detector/reset.js +20 -0
- package/src/nodes/state-change-detector/update.js +97 -0
- package/src/nodes/sw-stats/index.js +14 -0
- package/src/nodes/sw-stats/init.js +51 -0
- package/src/nodes/sw-stats/introspect.js +131 -0
- package/src/nodes/sw-stats/publish-to.js +45 -0
- package/src/nodes/sw-stats/recompute.js +33 -0
- package/src/nodes/sw-stats/reset.js +15 -0
- package/src/nodes/sw-stats/update.js +39 -0
- package/src/nodes/swing-watch/index.js +17 -0
- package/src/nodes/swing-watch/init.js +144 -0
- package/src/nodes/swing-watch/introspect.js +188 -0
- package/src/nodes/swing-watch/publish-to.js +53 -0
- package/src/nodes/swing-watch/recompute.js +22 -0
- package/src/nodes/swing-watch/reset.js +55 -0
- package/src/nodes/swing-watch/update.js +767 -0
- package/src/nodes/tally/index.js +15 -0
- package/src/nodes/tally/init.js +32 -0
- package/src/nodes/tally/introspect.js +116 -0
- package/src/nodes/tally/publish-to.js +28 -0
- package/src/nodes/tally/recompute.js +10 -0
- package/src/nodes/tally/reset.js +10 -0
- package/src/nodes/tally/update.js +44 -0
- package/src/nodes/threshold/index.js +14 -0
- package/src/nodes/threshold/init.js +121 -0
- package/src/nodes/threshold/introspect.js +192 -0
- package/src/nodes/threshold/publish-to.js +11 -0
- package/src/nodes/threshold/recompute.js +8 -0
- package/src/nodes/threshold/reset.js +14 -0
- package/src/nodes/threshold/update.js +54 -0
- package/src/nodes/transform/helpers.js +39 -0
- package/src/nodes/transform/index.js +15 -0
- package/src/nodes/transform/init.js +35 -0
- package/src/nodes/transform/introspect.js +118 -0
- package/src/nodes/transform/publish-to.js +23 -0
- package/src/nodes/transform/recompute.js +12 -0
- package/src/nodes/transform/reset.js +11 -0
- package/src/nodes/transform/update.js +32 -0
- package/src/nodes/trend/compute-confidence.js +112 -0
- package/src/nodes/trend/index.js +14 -0
- package/src/nodes/trend/init.js +81 -0
- package/src/nodes/trend/introspect.js +151 -0
- package/src/nodes/trend/publish-to.js +22 -0
- package/src/nodes/trend/recompute.js +15 -0
- package/src/nodes/trend/reset.js +26 -0
- package/src/nodes/trend/update.js +93 -0
- package/src/nodes/tw-stats/copy-from.js +16 -0
- package/src/nodes/tw-stats/flush.js +12 -0
- package/src/nodes/tw-stats/index.js +41 -0
- package/src/nodes/tw-stats/init.js +92 -0
- package/src/nodes/tw-stats/introspect.js +171 -0
- package/src/nodes/tw-stats/publish-to.js +108 -0
- package/src/nodes/tw-stats/recompute.js +9 -0
- package/src/nodes/tw-stats/reset.js +16 -0
- package/src/nodes/tw-stats/update.js +113 -0
- package/src/nodes/unbalance/index.js +15 -0
- package/src/nodes/unbalance/init.js +59 -0
- package/src/nodes/unbalance/introspect.js +167 -0
- package/src/nodes/unbalance/publish-to.js +52 -0
- package/src/nodes/unbalance/recompute.js +11 -0
- package/src/nodes/unbalance/reset.js +10 -0
- package/src/nodes/unbalance/update.js +83 -0
- package/src/nodes/vector-distance/index.js +14 -0
- package/src/nodes/vector-distance/init.js +54 -0
- package/src/nodes/vector-distance/introspect.js +149 -0
- package/src/nodes/vector-distance/publish-to.js +35 -0
- package/src/nodes/vector-distance/recompute.js +11 -0
- package/src/nodes/vector-distance/reset.js +20 -0
- package/src/nodes/vector-distance/update.js +139 -0
- package/src/nodes/winnow/index.js +12 -0
- package/src/nodes/winnow/init.js +82 -0
- package/src/nodes/winnow/introspect.js +253 -0
- package/src/nodes/winnow/publish-to.js +51 -0
- package/src/nodes/winnow/recompute.js +14 -0
- package/src/nodes/winnow/reset.js +34 -0
- package/src/nodes/winnow/update.js +203 -0
- package/src/tools/training/features.js +227 -0
- package/src/tools/training/index.js +21 -0
- package/src/tools/training/logistic.js +238 -0
- package/src/tools/training/metrics.js +126 -0
- package/src/tools/training/scale.js +239 -0
- package/src/tools/training/validate.js +103 -0
- package/src/windowing/count-sliding/create.js +16 -0
- package/src/windowing/count-sliding/get-reverse-chronological-index.js +19 -0
- package/src/windowing/count-sliding/index.js +6 -0
- package/src/windowing/count-sliding/is-not-full.js +5 -0
- package/src/windowing/count-sliding/push.js +15 -0
- package/src/windowing/count-sliding/reset.js +11 -0
- package/src/windowing/count-sliding/size.js +5 -0
- package/src/windowing/time-sliding/create.js +25 -0
- package/src/windowing/time-sliding/push.js +42 -0
- package/CODE_OF_CONDUCT.md +0 -46
- package/CONTRIBUTING.md +0 -132
- package/docs/.nojekyll +0 -0
- package/docs/CONTRIBUTING.md +0 -134
- package/docs/README.md +0 -132
- package/docs/_coverpage.md +0 -26
- package/docs/_media/bg.png +0 -0
- package/docs/_media/wink-logo.png +0 -0
- package/docs/_navbar.md +0 -14
- package/docs/_sidebar.md +0 -2
- package/docs/index.html +0 -44
- package/eslint.config.js +0 -283
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// nodes/appraise/update.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Hot path for two-layer SNN appraise node message processing.
|
|
5
|
+
*
|
|
6
|
+
* Pipeline per message:
|
|
7
|
+
* 1. Guard / validate timestamp
|
|
8
|
+
* 2. Compute decay factors (L1 uniform or per-source, L2 single)
|
|
9
|
+
* 3. L1 receptor loop: deviation -> normalise -> processReceptor (LIF+BLI+Rate)
|
|
10
|
+
* 4. L2 decision: synaptic current -> membrane update
|
|
11
|
+
* 5. Calibration check (burn-in Theta derivation)
|
|
12
|
+
* 6. MM readout -> conviction
|
|
13
|
+
* 7. Threshold classification
|
|
14
|
+
*
|
|
15
|
+
* Zero allocation. All arrays and objects are pre-allocated in init.
|
|
16
|
+
* Missing source fields cause pure decay (no injection) on that source.
|
|
17
|
+
*
|
|
18
|
+
* @see ADR-004
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { computeDeviation } from './deviation.js';
|
|
22
|
+
import { normalise } from './integrate.js';
|
|
23
|
+
import { processReceptor, decayReceptor } from './receptor.js';
|
|
24
|
+
import { computeSynapticCurrent, updateMembrane, readout } from './decision.js';
|
|
25
|
+
import { checkCalibration } from './calibrate.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Processes a message through the two-layer SNN pipeline.
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} state - Node state from init()
|
|
31
|
+
* @param {Object} msg - Incoming message with field values and timestamp
|
|
32
|
+
* @returns {Object} Updated state
|
|
33
|
+
*/
|
|
34
|
+
const update = function ( state, msg ) {
|
|
35
|
+
// Guard: skip if disabled
|
|
36
|
+
if ( state.disable || state.pause ) return state;
|
|
37
|
+
|
|
38
|
+
// Extract timestamp
|
|
39
|
+
const timestamp = msg.timestamp;
|
|
40
|
+
|
|
41
|
+
// Reset health flag
|
|
42
|
+
state.inputValidationFailed = false;
|
|
43
|
+
|
|
44
|
+
// Validate timestamp
|
|
45
|
+
if ( !Number.isFinite( timestamp ) ) {
|
|
46
|
+
state.inputValidationFailed = true;
|
|
47
|
+
return state;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Compute Decay ───────────────────────────────────────────────────────
|
|
51
|
+
const isFirst = !state.hasReceivedMessage;
|
|
52
|
+
const dt = timestamp - state.lastTimestamp;
|
|
53
|
+
state.lastTimestamp = timestamp;
|
|
54
|
+
state.hasReceivedMessage = true;
|
|
55
|
+
state.messageCount += 1;
|
|
56
|
+
|
|
57
|
+
// ── L1 Per-Source Receptor Loop ─────────────────────────────────────────
|
|
58
|
+
const thetas = state.thetas;
|
|
59
|
+
const fields = state.sourceFields;
|
|
60
|
+
const types = state.deviationTypes;
|
|
61
|
+
const dp1 = state.deviationP1;
|
|
62
|
+
const dp2 = state.deviationP2;
|
|
63
|
+
const n = state.sourceCount;
|
|
64
|
+
|
|
65
|
+
if ( state.uniformDecay ) {
|
|
66
|
+
// Fast path: single decay factor for all L1 sources
|
|
67
|
+
const decayFactor = isFirst ? 1 : Math.exp( -dt / state.taus[ 0 ] );
|
|
68
|
+
|
|
69
|
+
for ( let i = 0; i < n; i += 1 ) {
|
|
70
|
+
const raw = msg[ fields[ i ] ];
|
|
71
|
+
if ( Number.isFinite( raw ) ) {
|
|
72
|
+
const norm = normalise( computeDeviation( types[ i ], raw, dp1[ i ], dp2[ i ] ), thetas[ i ] );
|
|
73
|
+
processReceptor( state, i, norm, decayFactor );
|
|
74
|
+
} else {
|
|
75
|
+
decayReceptor( state, i, decayFactor );
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
// Per-source path: each source decays at its own rate
|
|
80
|
+
const taus = state.taus;
|
|
81
|
+
const decayFactors = state.decayFactors;
|
|
82
|
+
|
|
83
|
+
if ( isFirst ) {
|
|
84
|
+
decayFactors.fill( 1 );
|
|
85
|
+
} else {
|
|
86
|
+
for ( let i = 0; i < n; i += 1 ) {
|
|
87
|
+
decayFactors[ i ] = Math.exp( -dt / taus[ i ] );
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for ( let i = 0; i < n; i += 1 ) {
|
|
92
|
+
const raw = msg[ fields[ i ] ];
|
|
93
|
+
if ( Number.isFinite( raw ) ) {
|
|
94
|
+
const norm = normalise( computeDeviation( types[ i ], raw, dp1[ i ], dp2[ i ] ), thetas[ i ] );
|
|
95
|
+
processReceptor( state, i, norm, decayFactors[ i ] );
|
|
96
|
+
} else {
|
|
97
|
+
decayReceptor( state, i, decayFactors[ i ] );
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── L2 Decision Neuron ──────────────────────────────────────────────────
|
|
103
|
+
const l2DecayFactor = isFirst ? 1 : Math.exp( -dt / state.l2Tau );
|
|
104
|
+
const current = computeSynapticCurrent(
|
|
105
|
+
state.spikes, state.weights, state.totalAbsWeight, n
|
|
106
|
+
);
|
|
107
|
+
state.l2Membrane = updateMembrane( state.l2Membrane, current, l2DecayFactor );
|
|
108
|
+
|
|
109
|
+
// ── Calibration Check ───────────────────────────────────────────────────
|
|
110
|
+
if ( state.calibrating ) {
|
|
111
|
+
checkCalibration( state );
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── Conviction Readout ──────────────────────────────────────────────────
|
|
115
|
+
state.combined = readout( state.l2Membrane, state.l2Theta );
|
|
116
|
+
|
|
117
|
+
// ── Threshold Classification ────────────────────────────────────────────
|
|
118
|
+
const levels = state.thresholdLevels;
|
|
119
|
+
state.stateName = 'Normal';
|
|
120
|
+
for ( let i = levels.length - 1; i >= 0; i -= 1 ) {
|
|
121
|
+
if ( state.combined >= levels[ i ].at ) {
|
|
122
|
+
state.stateName = levels[ i ].name;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return state;
|
|
128
|
+
}; // update()
|
|
129
|
+
|
|
130
|
+
export default update;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as init } from './init.js';
|
|
2
|
+
export { default as update } from './update.js';
|
|
3
|
+
export { default as reset } from './reset.js';
|
|
4
|
+
// export { default as recompute } from './recompute.js';
|
|
5
|
+
// export { default as publishTo } from './publish-to.js';
|
|
6
|
+
// export * from './introspect.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// import { getNodeType } from './introspect.js';
|
|
2
|
+
|
|
3
|
+
const init = function ( spec ) {
|
|
4
|
+
const state = Object.create( null );
|
|
5
|
+
|
|
6
|
+
// CSV source configuration
|
|
7
|
+
state.source = spec.source; // URL or file path
|
|
8
|
+
state.mode = spec.mode || 'stream'; // 'stream' or 'batch'
|
|
9
|
+
state.rate = spec.rate || 100; // Messages per second (for stream mode)
|
|
10
|
+
state.loop = spec.loop !== false; // Loop when reaching end
|
|
11
|
+
state.columns = spec.columns || 'auto'; // Column mapping
|
|
12
|
+
|
|
13
|
+
// Parsing options for Papaparse
|
|
14
|
+
state.parseOptions = {
|
|
15
|
+
header: true,
|
|
16
|
+
dynamicTyping: true,
|
|
17
|
+
skipEmptyLines: true,
|
|
18
|
+
...( spec.parseOptions || {} )
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Runtime state
|
|
22
|
+
state.data = null; // Parsed CSV data
|
|
23
|
+
state.currentIndex = 0;
|
|
24
|
+
state.lastEmitTime = 0;
|
|
25
|
+
state.isLoaded = false;
|
|
26
|
+
state.isPaused = false;
|
|
27
|
+
|
|
28
|
+
// For browser environment - store fetch promise
|
|
29
|
+
state.loadPromise = null;
|
|
30
|
+
|
|
31
|
+
state.nodeType = 'CSV'; // getNodeType();
|
|
32
|
+
|
|
33
|
+
return state;
|
|
34
|
+
}; // init()
|
|
35
|
+
|
|
36
|
+
export default init;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// nodes/csv-source/load.js
|
|
2
|
+
import SimpleCSV from './simple-parser.js';
|
|
3
|
+
|
|
4
|
+
const loadCSV = async function ( state ) {
|
|
5
|
+
try {
|
|
6
|
+
let csvText;
|
|
7
|
+
|
|
8
|
+
// Check if source looks like CSV data (has newlines or commas)
|
|
9
|
+
const isEmbeddedData = state.source.includes('\n') ||
|
|
10
|
+
(state.source.includes(',') && !state.source.endsWith('.csv'));
|
|
11
|
+
|
|
12
|
+
// Determine environment and load CSV text
|
|
13
|
+
if ( typeof window === 'object' ) {
|
|
14
|
+
// Browser environment
|
|
15
|
+
if ( isEmbeddedData ) {
|
|
16
|
+
// Embedded data
|
|
17
|
+
csvText = state.source;
|
|
18
|
+
} else if ( state.source.startsWith( 'http' ) || state.source.startsWith( '/' ) ) {
|
|
19
|
+
// Fetch from URL
|
|
20
|
+
const response = await fetch( state.source );
|
|
21
|
+
csvText = await response.text();
|
|
22
|
+
} else {
|
|
23
|
+
// Assume it's embedded data if not a URL
|
|
24
|
+
csvText = state.source;
|
|
25
|
+
}
|
|
26
|
+
} else if ( typeof process === 'object' && process.versions && process.versions.node ) {
|
|
27
|
+
// Node.js environment
|
|
28
|
+
if ( isEmbeddedData ) {
|
|
29
|
+
// Embedded data
|
|
30
|
+
csvText = state.source;
|
|
31
|
+
} else if ( state.source.startsWith( 'http' ) ) {
|
|
32
|
+
// Fetch from URL
|
|
33
|
+
const response = await fetch( state.source );
|
|
34
|
+
csvText = await response.text();
|
|
35
|
+
} else {
|
|
36
|
+
// Read from file
|
|
37
|
+
const fs = await import( 'fs/promises' );
|
|
38
|
+
csvText = await fs.readFile( state.source, 'utf8' );
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
throw new Error( 'Unable to determine environment for CSV loading' );
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Parse CSV using SimpleCSV
|
|
45
|
+
const parsed = SimpleCSV.parse( csvText, state.parseOptions );
|
|
46
|
+
|
|
47
|
+
// Handle any parsing errors
|
|
48
|
+
if ( parsed.errors && parsed.errors.length > 0 ) {
|
|
49
|
+
console.warn( 'CSV parsing warnings:', parsed.errors );
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Store parsed data
|
|
53
|
+
state.data = parsed.data;
|
|
54
|
+
state.isLoaded = true;
|
|
55
|
+
state.currentIndex = 0;
|
|
56
|
+
|
|
57
|
+
// Log summary
|
|
58
|
+
console.log( `Loaded ${state.data.length} rows from CSV` );
|
|
59
|
+
if ( parsed.meta && parsed.meta.fields ) {
|
|
60
|
+
console.log( `Columns: ${parsed.meta.fields.join( ', ' )}` );
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
} catch ( error ) {
|
|
64
|
+
console.error( 'Failed to load CSV:', error );
|
|
65
|
+
state.data = [];
|
|
66
|
+
state.isLoaded = false;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default loadCSV;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// nodes/csv-source/reset.js
|
|
2
|
+
const reset = function ( state ) {
|
|
3
|
+
// Reset to initial state
|
|
4
|
+
state.currentIndex = 0;
|
|
5
|
+
state.lastEmitTime = 0;
|
|
6
|
+
state.isPaused = false;
|
|
7
|
+
|
|
8
|
+
// Don't clear the data - just reset the reading position
|
|
9
|
+
// This allows resuming from the beginning without reloading
|
|
10
|
+
|
|
11
|
+
return true;
|
|
12
|
+
}; // reset()
|
|
13
|
+
|
|
14
|
+
export default reset;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// nodes/csv-source/simple-csv-parser.js
|
|
2
|
+
|
|
3
|
+
const SimpleCSV = {
|
|
4
|
+
parse: function ( csvText, config = {} ) {
|
|
5
|
+
// Default configuration
|
|
6
|
+
const options = {
|
|
7
|
+
delimiter: config.delimiter || ',',
|
|
8
|
+
header: config.header !== false,
|
|
9
|
+
dynamicTyping: config.dynamicTyping !== false,
|
|
10
|
+
skipEmptyLines: config.skipEmptyLines !== false,
|
|
11
|
+
transformHeader: config.transformHeader || null,
|
|
12
|
+
transform: config.transform || null,
|
|
13
|
+
comments: config.comments || false,
|
|
14
|
+
quoteChar: config.quoteChar || '"',
|
|
15
|
+
parseDates: config.parseDates || false
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const result = {
|
|
19
|
+
data: [],
|
|
20
|
+
errors: [],
|
|
21
|
+
meta: {
|
|
22
|
+
delimiter: options.delimiter,
|
|
23
|
+
linebreak: '\n',
|
|
24
|
+
aborted: false,
|
|
25
|
+
fields: []
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// Normalize line breaks
|
|
31
|
+
csvText = csvText.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
32
|
+
|
|
33
|
+
// Split into lines (handling quoted newlines)
|
|
34
|
+
const lines = this.splitLines( csvText, options.quoteChar );
|
|
35
|
+
|
|
36
|
+
// Filter empty lines and comments
|
|
37
|
+
const processedLines = lines.filter( (line, index) => {
|
|
38
|
+
if ( options.skipEmptyLines && line.trim() === '' ) return false;
|
|
39
|
+
if ( options.comments && line.trim().startsWith( options.comments ) ) return false;
|
|
40
|
+
return true;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if ( processedLines.length === 0 ) {
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Parse header if present
|
|
48
|
+
let headers = [];
|
|
49
|
+
let dataStartIndex = 0;
|
|
50
|
+
|
|
51
|
+
if ( options.header ) {
|
|
52
|
+
headers = this.parseLine( processedLines[0], options );
|
|
53
|
+
|
|
54
|
+
// Transform headers if function provided
|
|
55
|
+
if ( options.transformHeader ) {
|
|
56
|
+
headers = headers.map( (h) => options.transformHeader( h ) );
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
result.meta.fields = headers;
|
|
60
|
+
dataStartIndex = 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Parse data rows
|
|
64
|
+
for ( let i = dataStartIndex; i < processedLines.length; i += 1 ) {
|
|
65
|
+
const values = this.parseLine( processedLines[i], options );
|
|
66
|
+
|
|
67
|
+
if ( values.length === 0 ) continue;
|
|
68
|
+
|
|
69
|
+
// Create row object or array
|
|
70
|
+
let row;
|
|
71
|
+
if ( options.header && headers.length > 0 ) {
|
|
72
|
+
row = {};
|
|
73
|
+
headers.forEach( (header, index) => {
|
|
74
|
+
let value = values[index];
|
|
75
|
+
|
|
76
|
+
// Apply dynamic typing
|
|
77
|
+
if ( options.dynamicTyping ) {
|
|
78
|
+
value = this.castValue( value, options );
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Apply transform if provided
|
|
82
|
+
if ( options.transform ) {
|
|
83
|
+
value = options.transform( value, header );
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
row[header] = value;
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
row = values.map( (value) => {
|
|
90
|
+
if ( options.dynamicTyping ) {
|
|
91
|
+
return this.castValue( value );
|
|
92
|
+
}
|
|
93
|
+
return value;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
result.data.push( row );
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
} catch ( error ) {
|
|
101
|
+
result.errors.push({
|
|
102
|
+
type: 'ParseError',
|
|
103
|
+
code: 'ParseError',
|
|
104
|
+
message: error.message,
|
|
105
|
+
row: result.data.length
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// Split CSV into lines, respecting quoted newlines
|
|
113
|
+
splitLines: function ( text, quoteChar ) {
|
|
114
|
+
const lines = [];
|
|
115
|
+
let currentLine = '';
|
|
116
|
+
let inQuotes = false;
|
|
117
|
+
|
|
118
|
+
for ( let i = 0; i < text.length; i += 1 ) {
|
|
119
|
+
const char = text[i];
|
|
120
|
+
const nextChar = text[i + 1];
|
|
121
|
+
|
|
122
|
+
if ( char === quoteChar ) {
|
|
123
|
+
// Check if it's an escaped quote
|
|
124
|
+
if ( nextChar === quoteChar && inQuotes ) {
|
|
125
|
+
currentLine += quoteChar;
|
|
126
|
+
i += 1; // Skip next quote
|
|
127
|
+
} else {
|
|
128
|
+
inQuotes = !inQuotes;
|
|
129
|
+
currentLine += char;
|
|
130
|
+
}
|
|
131
|
+
} else if ( char === '\n' && !inQuotes ) {
|
|
132
|
+
lines.push( currentLine );
|
|
133
|
+
currentLine = '';
|
|
134
|
+
} else {
|
|
135
|
+
currentLine += char;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Don't forget the last line
|
|
140
|
+
if ( currentLine !== '' ) {
|
|
141
|
+
lines.push( currentLine );
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return lines;
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// Parse a single line into values
|
|
148
|
+
parseLine: function ( line, options ) {
|
|
149
|
+
const values = [];
|
|
150
|
+
let currentValue = '';
|
|
151
|
+
let inQuotes = false;
|
|
152
|
+
const delimiter = options.delimiter;
|
|
153
|
+
const quoteChar = options.quoteChar;
|
|
154
|
+
|
|
155
|
+
for ( let i = 0; i < line.length; i += 1 ) {
|
|
156
|
+
const char = line[i];
|
|
157
|
+
const nextChar = line[i + 1];
|
|
158
|
+
|
|
159
|
+
if ( char === quoteChar ) {
|
|
160
|
+
if ( inQuotes && nextChar === quoteChar ) {
|
|
161
|
+
// Escaped quote
|
|
162
|
+
currentValue += quoteChar;
|
|
163
|
+
i += 1; // Skip next quote
|
|
164
|
+
} else if ( inQuotes && ( nextChar === delimiter || nextChar === undefined || i === line.length - 1 ) ) {
|
|
165
|
+
// End of quoted value
|
|
166
|
+
inQuotes = false;
|
|
167
|
+
} else if ( !inQuotes && ( currentValue === '' || currentValue === ' '.repeat( currentValue.length ) ) ) {
|
|
168
|
+
// Start of quoted value
|
|
169
|
+
inQuotes = true;
|
|
170
|
+
} else {
|
|
171
|
+
currentValue += char;
|
|
172
|
+
}
|
|
173
|
+
} else if ( char === delimiter && !inQuotes ) {
|
|
174
|
+
// End of value
|
|
175
|
+
values.push( currentValue.trim() );
|
|
176
|
+
currentValue = '';
|
|
177
|
+
} else {
|
|
178
|
+
currentValue += char;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Don't forget the last value
|
|
183
|
+
values.push( currentValue.trim() );
|
|
184
|
+
|
|
185
|
+
return values;
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
// Convert string values to appropriate types
|
|
189
|
+
castValue: function ( value, options ) {
|
|
190
|
+
if ( value === '' || value === null || value === undefined ) {
|
|
191
|
+
return value;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Boolean
|
|
195
|
+
if ( value.toLowerCase() === 'true' ) return true;
|
|
196
|
+
if ( value.toLowerCase() === 'false' ) return false;
|
|
197
|
+
|
|
198
|
+
// Number
|
|
199
|
+
if ( !isNaN( value ) && !isNaN( parseFloat( value ) ) ) {
|
|
200
|
+
return value.indexOf( '.' ) > -1 ? parseFloat( value ) : parseInt( value, 10 );
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Date (only if parseDates is true)
|
|
204
|
+
if ( options.parseDates && (/^\d{4}-\d{2}-\d{2}/).test( value ) ) {
|
|
205
|
+
const date = new Date( value );
|
|
206
|
+
if ( !isNaN( date.getTime() ) ) {
|
|
207
|
+
return date;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// String (default)
|
|
212
|
+
return value;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export default SimpleCSV;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// nodes/csv-source/test.js
|
|
2
|
+
import * as csvSource from './index.js';
|
|
3
|
+
import loadCSV from './load.js';
|
|
4
|
+
|
|
5
|
+
// Sample CSV data for testing
|
|
6
|
+
const sampleCSV = `timestamp,temperature,pressure,humidity,location
|
|
7
|
+
2024-01-01T00:00:00,23.5,1013.25,65,sensor_01
|
|
8
|
+
2024-01-01T00:00:10,23.7,1013.20,64,sensor_01
|
|
9
|
+
2024-01-01T00:00:20,24.1,1013.15,63,sensor_01
|
|
10
|
+
2024-01-01T00:00:30,24.5,1013.10,62,sensor_01
|
|
11
|
+
2024-01-01T00:00:40,25.2,1013.05,61,sensor_01`;
|
|
12
|
+
|
|
13
|
+
// Test 1: Basic initialization and loading
|
|
14
|
+
console.log('=== Test 1: Basic CSV Loading ===');
|
|
15
|
+
const basicSpec = {
|
|
16
|
+
source: sampleCSV, // Embedded CSV data
|
|
17
|
+
mode: 'batch', // Get all at once
|
|
18
|
+
parseOptions: {
|
|
19
|
+
header: true,
|
|
20
|
+
dynamicTyping: true,
|
|
21
|
+
skipEmptyLines: true
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const basicState = csvSource.init( basicSpec );
|
|
26
|
+
console.log( 'Initial state:', {
|
|
27
|
+
mode: basicState.mode,
|
|
28
|
+
isLoaded: basicState.isLoaded,
|
|
29
|
+
currentIndex: basicState.currentIndex
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Load the CSV data
|
|
33
|
+
await loadCSV( basicState );
|
|
34
|
+
console.log( `Loaded ${basicState.data.length} rows` );
|
|
35
|
+
console.log( 'First row:', basicState.data[0] );
|
|
36
|
+
console.log( 'Data types:', Object.entries( basicState.data[0] ).map(
|
|
37
|
+
([ k, v ]) => `${k}: ${typeof v}`
|
|
38
|
+
));
|
|
39
|
+
|
|
40
|
+
// Test 2: Message generation
|
|
41
|
+
console.log( '\n=== Test 2: Message Generation ===');
|
|
42
|
+
const msg1 = csvSource.update( basicState, {} );
|
|
43
|
+
console.log( 'Message 1:', msg1 );
|
|
44
|
+
|
|
45
|
+
const msg2 = csvSource.update( basicState, {} );
|
|
46
|
+
console.log( 'Message 2:', msg2 );
|
|
47
|
+
|
|
48
|
+
// Test 3: Stream mode with rate limiting
|
|
49
|
+
console.log( '\n=== Test 3: Stream Mode ===');
|
|
50
|
+
const streamSpec = {
|
|
51
|
+
source: sampleCSV,
|
|
52
|
+
mode: 'stream',
|
|
53
|
+
rate: 100, // 100 messages per second
|
|
54
|
+
loop: true,
|
|
55
|
+
parseOptions: {
|
|
56
|
+
header: true,
|
|
57
|
+
dynamicTyping: true
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const streamState = csvSource.init( streamSpec );
|
|
62
|
+
await loadCSV( streamState );
|
|
63
|
+
|
|
64
|
+
// Simulate rapid updates - should be rate limited
|
|
65
|
+
const startTime = Date.now();
|
|
66
|
+
let messageCount = 0;
|
|
67
|
+
|
|
68
|
+
for ( let i = 0; i < 10; i += 1 ) {
|
|
69
|
+
const msg = csvSource.update( streamState, {} );
|
|
70
|
+
if ( msg ) {
|
|
71
|
+
messageCount += 1;
|
|
72
|
+
console.log( `Stream message ${messageCount}:`, msg._source );
|
|
73
|
+
}
|
|
74
|
+
// Small delay to show rate limiting
|
|
75
|
+
await new Promise( (resolve) => setTimeout( resolve, 5 ) );
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const elapsed = Date.now() - startTime;
|
|
79
|
+
console.log( `Generated ${messageCount} messages in ${elapsed}ms` );
|
|
80
|
+
console.log( `Effective rate: ${(messageCount / elapsed * 1000).toFixed(1)} msg/sec` );
|
|
81
|
+
|
|
82
|
+
// Test 4: Loop behavior
|
|
83
|
+
console.log( '\n=== Test 4: Loop Behavior ===');
|
|
84
|
+
const loopState = csvSource.init({
|
|
85
|
+
source: sampleCSV,
|
|
86
|
+
mode: 'batch',
|
|
87
|
+
loop: true
|
|
88
|
+
});
|
|
89
|
+
await loadCSV( loopState );
|
|
90
|
+
|
|
91
|
+
// Read all messages twice to test looping
|
|
92
|
+
console.log( 'First pass:' );
|
|
93
|
+
for ( let i = 0; i < 5; i += 1 ) {
|
|
94
|
+
const msg = csvSource.update( loopState, {} );
|
|
95
|
+
console.log( ` Row ${i + 1}: temp=${msg.temperature}°C` );
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log( 'Second pass (should loop):' );
|
|
99
|
+
for ( let i = 0; i < 3; i += 1 ) {
|
|
100
|
+
const msg = csvSource.update( loopState, {} );
|
|
101
|
+
console.log( ` Row ${i + 1}: temp=${msg.temperature}°C` );
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Test 5: Column mapping
|
|
105
|
+
console.log( '\n=== Test 5: Column Mapping ===');
|
|
106
|
+
const mappedState = csvSource.init({
|
|
107
|
+
source: sampleCSV,
|
|
108
|
+
columns: {
|
|
109
|
+
'temperature': 'temp',
|
|
110
|
+
'pressure': 'press',
|
|
111
|
+
'location': 'id'
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
await loadCSV( mappedState );
|
|
115
|
+
|
|
116
|
+
const mappedMsg = csvSource.update( mappedState, {} );
|
|
117
|
+
console.log( 'Mapped message:', mappedMsg );
|
|
118
|
+
console.log( 'Should have: temp, press, id fields' );
|
|
119
|
+
|
|
120
|
+
// Test 6: CSV with different delimiter
|
|
121
|
+
console.log( '\n=== Test 6: Tab-Delimited CSV ===');
|
|
122
|
+
const tabCSV = `name age score
|
|
123
|
+
John Doe 30 95.5
|
|
124
|
+
Jane Smith 25 87.3`;
|
|
125
|
+
|
|
126
|
+
const tabState = csvSource.init({
|
|
127
|
+
source: tabCSV,
|
|
128
|
+
parseOptions: {
|
|
129
|
+
delimiter: '\t',
|
|
130
|
+
header: true,
|
|
131
|
+
dynamicTyping: true
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
await loadCSV( tabState );
|
|
135
|
+
|
|
136
|
+
const tabMsg = csvSource.update( tabState, {} );
|
|
137
|
+
console.log( 'Tab-delimited data:', tabMsg );
|
|
138
|
+
|
|
139
|
+
// Test 7: File loading simulation (Node.js only)
|
|
140
|
+
if ( typeof process === 'object' ) {
|
|
141
|
+
console.log( '\n=== Test 7: File Loading (Node.js) ===');
|
|
142
|
+
|
|
143
|
+
// Create a temporary CSV file
|
|
144
|
+
const fs = await import( 'fs/promises' );
|
|
145
|
+
const tempFile = './test-data.csv';
|
|
146
|
+
await fs.writeFile( tempFile, sampleCSV );
|
|
147
|
+
|
|
148
|
+
const fileState = csvSource.init({
|
|
149
|
+
source: tempFile,
|
|
150
|
+
mode: 'batch'
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await loadCSV( fileState );
|
|
154
|
+
console.log( `Loaded ${fileState.data.length} rows from file` );
|
|
155
|
+
|
|
156
|
+
// Clean up
|
|
157
|
+
await fs.unlink( tempFile );
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Test 8: Reset functionality
|
|
161
|
+
console.log( '\n=== Test 8: Reset ===');
|
|
162
|
+
console.log( 'Before reset:', {
|
|
163
|
+
currentIndex: basicState.currentIndex,
|
|
164
|
+
isLoaded: basicState.isLoaded
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
csvSource.reset( basicState );
|
|
168
|
+
console.log( 'After reset:', {
|
|
169
|
+
currentIndex: basicState.currentIndex,
|
|
170
|
+
isLoaded: basicState.isLoaded
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
console.log( '\n✅ All tests completed!' );
|