@winkjs/composer 0.5.0 → 0.6.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.
Files changed (104) hide show
  1. package/package.json +8 -8
  2. package/src/core/batch/aoo-encoder.js +4 -4
  3. package/src/core/batch/sliding-window.js +2 -2
  4. package/src/core/codec/index.js +4 -4
  5. package/src/core/emitter-manager/mqtt/emitter.js +44 -14
  6. package/src/core/emitter-manager/mqtt/index.js +2 -1
  7. package/src/core/emitter-manager/terminal/assert-columns.js +1 -1
  8. package/src/core/emitter-manager/terminal/index.js +4 -4
  9. package/src/core/env-vars.js +44 -3
  10. package/src/core/logger/index.js +174 -0
  11. package/src/core/mqtt-protocol.js +2 -2
  12. package/src/core/partition-manager/get-stats.js +31 -0
  13. package/src/core/partition-manager/index.js +1 -0
  14. package/src/core/partition-manager/init.js +15 -0
  15. package/src/core/partition-manager/update.js +143 -87
  16. package/src/core/partition-manager/validate-flow.js +1 -1
  17. package/src/core/semantics/loader.js +1 -1
  18. package/src/core/semantics/warnings.js +5 -3
  19. package/src/core/shutdown-manager/index.js +71 -21
  20. package/src/core/source-manager/csv/start.js +109 -35
  21. package/src/core/source-manager/mqtt/client.js +95 -36
  22. package/src/core/source-manager/mqtt/dedup.js +3 -3
  23. package/src/core/source-manager/mqtt/index.js +1 -1
  24. package/src/core/source-manager/mqtt/status.js +67 -14
  25. package/src/core/source-manager/record-shape.js +55 -0
  26. package/src/core/source-manager/test-harness/field-generator.js +1 -1
  27. package/src/core/source-manager/test-harness/start.js +52 -10
  28. package/src/core/source-manager/test-harness/validate.js +1 -1
  29. package/src/core/storage-manager/questdb/assert-columns.js +2 -2
  30. package/src/core/storage-manager/questdb/ensure-tables.js +1 -1
  31. package/src/core/storage-manager/questdb/index.js +55 -17
  32. package/src/core/storage-manager/questdb/persist-plan.js +32 -8
  33. package/src/core/tunable/helpers.js +1 -1
  34. package/src/core/utils/callback-guard/index.js +155 -0
  35. package/src/core/utils/half-life/index.js +4 -4
  36. package/src/core/utils/validate/composer-validators.js +2 -2
  37. package/src/core/utils/validate/index.js +1 -1
  38. package/src/core/utils/validate/validate.js +1 -1
  39. package/src/core/wiring/assert-handle.js +5 -5
  40. package/src/core/wiring/assert-module.js +2 -2
  41. package/src/core/wiring/wire-emitters.js +4 -3
  42. package/src/core/wiring/wire-linear-graph.js +1 -1
  43. package/src/core/wiring/wire-node.js +6 -6
  44. package/src/core/wiring/wire-semantics.js +1 -1
  45. package/src/core/wiring/wire-storages.js +4 -3
  46. package/src/flow/driver.js +31 -6
  47. package/src/flow/expand-single-input-array.js +4 -4
  48. package/src/flow/flow-api-schemas.js +4 -4
  49. package/src/flow/flow.js +41 -41
  50. package/src/flow/for-each.js +12 -11
  51. package/src/flow/get-target-array.js +2 -2
  52. package/src/flow/load-node-module.js +1 -1
  53. package/src/flow/make-node-method.js +4 -4
  54. package/src/flow/resolve-pick-by-field.js +1 -1
  55. package/src/flow/run.js +176 -38
  56. package/src/flow/serialize.js +1 -1
  57. package/src/flow/spec-builder.js +1 -1
  58. package/src/flow/validate.js +1 -1
  59. package/src/nodes/butterworth-filter/init.js +5 -4
  60. package/src/nodes/categorize/update.js +3 -1
  61. package/src/nodes/controller/update.js +2 -1
  62. package/src/nodes/dwell-time-tracker/update.js +2 -1
  63. package/src/nodes/emit-if/helpers.js +57 -11
  64. package/src/nodes/emit-if/init.js +1 -1
  65. package/src/nodes/emit-if/update.js +8 -11
  66. package/src/nodes/page-hinkley/update.js +2 -1
  67. package/src/nodes/pass-if/update.js +3 -1
  68. package/src/nodes/persist-if/helpers.js +60 -4
  69. package/src/nodes/persist-if/init.js +1 -1
  70. package/src/nodes/persist-if/update.js +8 -16
  71. package/src/nodes/persistence-check/update.js +2 -1
  72. package/src/nodes/sanitize/update.js +4 -2
  73. package/src/nodes/swing-watch/update.js +3 -2
  74. package/src/nodes/threshold/update.js +2 -1
  75. package/src/nodes/trend/compute-confidence.js +4 -4
  76. package/src/nodes/trend/update.js +2 -1
  77. package/src/nodes/winnow/update.js +3 -1
  78. package/src/tools/training/features.js +13 -13
  79. package/src/tools/training/logistic.js +1 -1
  80. package/src/tools/training/metrics.js +2 -2
  81. package/src/tools/training/scale.js +2 -2
  82. package/src/tools/training/validate.js +7 -7
  83. package/src/core/emitter-manager/mqtt/mqtt-store.js +0 -538
  84. package/src/core/utils/console-formatter/index.js +0 -207
  85. package/src/nodes/archive/csv/index.js +0 -6
  86. package/src/nodes/archive/csv/init.js +0 -36
  87. package/src/nodes/archive/csv/load.js +0 -70
  88. package/src/nodes/archive/csv/reset.js +0 -14
  89. package/src/nodes/archive/csv/simple-parser.js +0 -216
  90. package/src/nodes/archive/csv/test.js +0 -173
  91. package/src/nodes/archive/csv/update.js +0 -63
  92. package/src/nodes/sse-emitter/index.js +0 -7
  93. package/src/nodes/sse-emitter/init.js +0 -105
  94. package/src/nodes/sse-emitter/introspect.js +0 -134
  95. package/src/nodes/sse-emitter/publish-to.js +0 -6
  96. package/src/nodes/sse-emitter/recompute.js +0 -16
  97. package/src/nodes/sse-emitter/reset.js +0 -19
  98. package/src/nodes/sse-emitter/update.js +0 -69
  99. package/src/nodes/sse-emitter/utils/broadcast-heartbeat.js +0 -36
  100. package/src/nodes/sse-emitter/utils/build-message.js +0 -31
  101. package/src/nodes/sse-emitter/utils/connection-manager.js +0 -45
  102. package/src/nodes/sse-emitter/utils/endpoint-initializer.js +0 -20
  103. package/src/nodes/sse-emitter/utils/health-monitor.js +0 -38
  104. package/src/nodes/sse-emitter/utils/qos-handler.js +0 -41
@@ -8,6 +8,8 @@
8
8
  * emit-if/helpers.js — the two output gates share one failure model.
9
9
  */
10
10
 
11
+ import { logger } from '../../core/logger/index.js';
12
+
11
13
  /**
12
14
  * Static fallback error the framework substitutes when an adapter breaks
13
15
  * the return contract — no result object at all, or `{ ok: false }` with
@@ -74,8 +76,8 @@ const sweepAnnotateKeys = function ( state, record, msg ) {
74
76
  }
75
77
 
76
78
  if ( invented.length > 0 ) {
77
- console.warn(
78
- `WinkComposer/persistIf: annotate for node '${state.name}' ` +
79
+ logger.warn(
80
+ `winkComposer/persistIf: annotate for node '${state.name}' ` +
79
81
  `(insightType '${state.insightType}') returned keys that are not declared columns: ` +
80
82
  `${invented.join( ', ' )}. These values are never stored, because the persist plan ` +
81
83
  'writes only declared columns. Check the key names against the asset class. ' +
@@ -104,8 +106,62 @@ const recordPersistFailure = function ( state, error ) {
104
106
  state.writeErrorLogged = true;
105
107
  state.firstPersistError = error.message;
106
108
  state.firstPersistErrorCode = error.code;
107
- console.error( state.writeErrorLogPrefix + error.code + '): ' + error.message );
109
+ logger.error( state.writeErrorLogPrefix + error.code + '): ' + error.message );
108
110
  }
109
111
  }; // recordPersistFailure()
110
112
 
111
- export { assertAnnotateReturn, sweepAnnotateKeys, recordPersistFailure, MALFORMED_RESULT_ERROR };
113
+ /**
114
+ * Substituted error for a storage adapter that THREW from write() —
115
+ * the other face of a broken return contract (ADR-018: the hot path
116
+ * never throws; a sink answers { ok }). Same code as the
117
+ * malformed-result face: the remediation is identical (fix the
118
+ * non-conforming adapter), and the message names the difference.
119
+ * Allocates on the failure path only. Mirrors emit-if.
120
+ *
121
+ * @param {Error} error - The thrown value
122
+ * @returns {Object} `{ code, message }` for the failure episode
123
+ */
124
+ const thrownSinkError = function ( error ) {
125
+ return {
126
+ code: 'MALFORMED_RESULT',
127
+ message: `storage write threw instead of returning { ok, error? }: ${error.message}`
128
+ };
129
+ }; // thrownSinkError()
130
+
131
+ /**
132
+ * Writes one record to the wired storage with the sink call fully
133
+ * guarded. A conforming adapter never throws; a throwing one is a
134
+ * broken adapter, recorded in the persist episode — never escaped
135
+ * into the pipeline where it would also cost the message's other
136
+ * outputs. Returns true only on `{ ok: true }`. Mirrors emit-if's
137
+ * deliverToEmitter.
138
+ *
139
+ * @param {Object} state - Node state containing storage, insightType,
140
+ * and partitionId
141
+ * @param {Object} record - Record to persist
142
+ * @returns {boolean} True when the storage accepted the record
143
+ */
144
+ const writeToStorage = function ( state, record ) {
145
+ try {
146
+ const result = state.storage.write(
147
+ state.insightType,
148
+ record,
149
+ state.partitionId
150
+ );
151
+ if ( result && result.ok ) {
152
+ return true;
153
+ }
154
+ recordPersistFailure( state, ( result && result.error ) || MALFORMED_RESULT_ERROR );
155
+ } catch ( sinkError ) {
156
+ recordPersistFailure( state, thrownSinkError( sinkError ) );
157
+ }
158
+ return false;
159
+ }; // writeToStorage()
160
+
161
+ export {
162
+ assertAnnotateReturn,
163
+ sweepAnnotateKeys,
164
+ recordPersistFailure,
165
+ writeToStorage,
166
+ MALFORMED_RESULT_ERROR
167
+ };
@@ -90,7 +90,7 @@ const init = function ( spec ) {
90
90
  state.firstPersistErrorCode = null;
91
91
  // Static log prefix pre-built here per ADR-004 (no string building in
92
92
  // update beyond the two runtime fields, and only once per episode).
93
- state.writeErrorLogPrefix = `WinkComposer/persistIf: storage write failed (node=${state.name}, insightType=${state.insightType}, code=`;
93
+ state.writeErrorLogPrefix = `winkComposer/persistIf: storage write failed (node=${state.name}, insightType=${state.insightType}, code=`;
94
94
 
95
95
  return state;
96
96
  }; // init()
@@ -10,7 +10,8 @@
10
10
  * it to the storage buffer if the predicate passed.
11
11
  */
12
12
 
13
- import { assertAnnotateReturn, sweepAnnotateKeys, recordPersistFailure, MALFORMED_RESULT_ERROR } from './helpers.js';
13
+ import { assertAnnotateReturn, sweepAnnotateKeys, writeToStorage } from './helpers.js';
14
+ import { logger } from '../../core/logger/index.js';
14
15
 
15
16
  /**
16
17
  * Process incoming message.
@@ -64,7 +65,7 @@ const update = function ( state, msg ) {
64
65
  // Log first error per episode; suppress subsequent until recovery
65
66
  if ( !state.predicateErrorLogged ) {
66
67
  state.predicateErrorLogged = true;
67
- console.error( `WinkComposer/persistIf: predicate threw exception: ${error.message}` );
68
+ logger.error( `winkComposer/persistIf: predicate threw exception: ${error.message}` );
68
69
  }
69
70
  state.persistErrors += 1;
70
71
  state.lastPersistError = error.message;
@@ -78,31 +79,22 @@ const update = function ( state, msg ) {
78
79
 
79
80
  // Write to storage if predicate returned true
80
81
  if ( shouldPersist ) {
81
- // Build record for storage (zero-alloc: storage.write handles it).
82
82
  // storage.write() is synchronous per ADR-013; the actual I/O happens
83
83
  // asynchronously via the storage adapter's background flush.
84
84
  // Per the ADR-018 sink return contract, the storage answers
85
85
  // { ok: true } on success or
86
86
  // { ok: false, error: { code, message } } on failure.
87
- const result = state.storage.write(
88
- state.insightType,
89
- record,
90
- state.partitionId
91
- );
92
-
93
- if ( result && result.ok ) {
87
+ // writeToStorage guards the call itself too: a THROWING adapter
88
+ // is the other face of a broken contract, recorded in the same
89
+ // adapter episode — a non-conforming adapter can neither fail
90
+ // silently nor throw out of the hot path.
91
+ if ( writeToStorage( state, record ) ) {
94
92
  state.persistCount += 1;
95
93
  // Wall-clock marker of the last successful write (post-mortem
96
94
  // reads only, like the first* error fields).
97
95
  state.lastPersistTime = Date.now();
98
96
  // Recovery: a successful write closes the failure episode.
99
97
  if ( state.writeErrorLogged ) state.writeErrorLogged = false;
100
- } else {
101
- // A malformed result (no object at all, or ok:false with no
102
- // error) counts as a failure with the static fallback error,
103
- // so a non-conforming adapter can neither fail silently
104
- // nor throw out of the hot path.
105
- recordPersistFailure( state, ( result && result.error ) || MALFORMED_RESULT_ERROR );
106
98
  }
107
99
  }
108
100
 
@@ -1,6 +1,7 @@
1
1
  // nodes/persistence-check/update.js
2
2
 
3
3
  import { executeTriggers } from '../../core/utils/node/index.js';
4
+ import { logger } from '../../core/logger/index.js';
4
5
 
5
6
  const update = function ( state, msg ) {
6
7
  if ( state.disable || state.pause ) return state;
@@ -16,7 +17,7 @@ const update = function ( state, msg ) {
16
17
  // Log first error per episode; suppress subsequent until recovery
17
18
  if ( !state.predicateErrorLogged ) {
18
19
  state.predicateErrorLogged = true;
19
- console.error( `WinkComposer/persistenceCheck: predicate threw exception: ${error.message}` );
20
+ logger.error( `winkComposer/persistenceCheck: predicate threw exception: ${error.message}` );
20
21
  }
21
22
  }
22
23
 
@@ -1,3 +1,5 @@
1
+ import { logger } from '../../core/logger/index.js';
2
+
1
3
  // Helper: Check value against range
2
4
  const checkRange = function ( state, value, rangeSpec ) {
3
5
  if ( !state.hasRange || !rangeSpec ) {
@@ -34,7 +36,7 @@ const checkPredicate = function ( state, value, msg ) {
34
36
  // Log first error per episode; suppress subsequent until recovery
35
37
  if ( !state.predicateErrorLogged ) {
36
38
  state.predicateErrorLogged = true;
37
- console.error( `WinkComposer/sanitize: predicate threw exception: ${error.message}` );
39
+ logger.error( `winkComposer/sanitize: predicate threw exception: ${error.message}` );
38
40
  }
39
41
  return false;
40
42
  }
@@ -64,7 +66,7 @@ const update = function ( state, msg ) {
64
66
  } catch ( error ) {
65
67
  if ( !state.tunableErrorLogged ) {
66
68
  state.tunableErrorLogged = true;
67
- console.error( `WinkComposer/${state.nodeType}: tunable threw: ${error.message}` );
69
+ logger.error( `winkComposer/${state.nodeType}: tunable threw: ${error.message}` );
68
70
  }
69
71
  }
70
72
  }
@@ -66,6 +66,7 @@
66
66
  // https://github.com/weinkauf/Persistence1D
67
67
 
68
68
  import { push, isNotFull } from '../../windowing/count-sliding/index.js';
69
+ import { logger } from '../../core/logger/index.js';
69
70
 
70
71
 
71
72
  // ═══════════════════════════════════════════════════════════════════════════
@@ -624,8 +625,8 @@ const resolveThreshold = function ( state, msg ) {
624
625
  } catch ( error ) {
625
626
  if ( !state.tunableErrorLogged ) {
626
627
  state.tunableErrorLogged = true;
627
- const errMsg = 'WinkComposer/' + state.nodeType + ': tunable threw: ' + error.message;
628
- console.error( errMsg );
628
+ const errMsg = 'winkComposer/' + state.nodeType + ': tunable threw: ' + error.message;
629
+ logger.error( errMsg );
629
630
  }
630
631
  }
631
632
  const epsBase = state.currentThreshold;
@@ -1,6 +1,7 @@
1
1
  // nodes/threshold/update.js
2
2
 
3
3
  import { executeTriggers } from '../../core/utils/node/index.js';
4
+ import { logger } from '../../core/logger/index.js';
4
5
 
5
6
  const update = function ( state, msg ) {
6
7
  if ( state.disable || state.pause ) return state;
@@ -27,7 +28,7 @@ const update = function ( state, msg ) {
27
28
  // Log first error per episode; suppress subsequent until recovery
28
29
  if ( !state.tunableErrorLogged ) {
29
30
  state.tunableErrorLogged = true;
30
- console.error( `WinkComposer/${state.nodeType}: tunable threw: ${error.message}` );
31
+ logger.error( `winkComposer/${state.nodeType}: tunable threw: ${error.message}` );
31
32
  }
32
33
  }
33
34
 
@@ -5,7 +5,7 @@ const computeConfidence = function ( state ) {
5
5
  // Provides gradual trust building as statistics stabilize
6
6
  if ( state.samples < state.warmupSamples ) {
7
7
  const ramp = state.samples / Math.max( state.warmupSamples, 1 );
8
- return ( ramp < 0 ) ? 0 : ( ramp > 1 ? 1 : ramp );
8
+ return Math.min( 1, Math.max( 0, ramp ) );
9
9
  }
10
10
 
11
11
  const epsilon = state.epsilon ?? 1e-12;
@@ -45,7 +45,7 @@ const computeConfidence = function ( state ) {
45
45
 
46
46
  // Combine factors: far from edge AND low noise AND persistent = high confidence
47
47
  const confStable = margin * quietness * persistenceFactor;
48
- return ( confStable < 0 ) ? 0 : ( confStable > 1 ? 1 : confStable );
48
+ return Math.min( 1, Math.max( 0, confStable ) );
49
49
  }
50
50
 
51
51
  // TRENDING CONFIDENCE: "How confident are we in this trend direction?"
@@ -105,8 +105,8 @@ const computeConfidence = function ( state ) {
105
105
  // Therefore, we omit the quietness factor for rising/falling states.
106
106
  const confTrend = signalClarity * boundaryClarity * persistenceFactor;
107
107
 
108
- // Ensure output is strictly in [0,1] despite numerical errors
109
- return ( confTrend < 0 ) ? 0 : ( confTrend > 1 ? 1 : confTrend );
108
+ // Clamp to [0,1] — the published confidence contract
109
+ return Math.min( 1, Math.max( 0, confTrend ) );
110
110
  }; // computeConfidence()
111
111
 
112
112
  export { computeConfidence };
@@ -1,6 +1,7 @@
1
1
  // nodes/trend/update.js
2
2
 
3
3
  import { computeConfidence } from './compute-confidence.js';
4
+ import { logger } from '../../core/logger/index.js';
4
5
 
5
6
  const update = function ( state, msg ) {
6
7
  if ( state.disable || state.pause ) return state;
@@ -47,7 +48,7 @@ const update = function ( state, msg ) {
47
48
  } catch ( error ) {
48
49
  if ( !state.tunableErrorLogged ) {
49
50
  state.tunableErrorLogged = true;
50
- console.error( `WinkComposer/${state.nodeType}: tunable threw: ${error.message}` );
51
+ logger.error( `winkComposer/${state.nodeType}: tunable threw: ${error.message}` );
51
52
  }
52
53
  }
53
54
 
@@ -22,6 +22,8 @@
22
22
  // Extracted helper: set anchor and mark significant.
23
23
  // Called from each of the five checks. Zero allocation — scalar
24
24
  // assignments only. Reduces update() cyclomatic complexity.
25
+ import { logger } from '../../core/logger/index.js';
26
+
25
27
  const setAnchor = function ( state, xVal, slope ) {
26
28
  state.anchor = xVal;
27
29
  state.anchorSlope = Number.isFinite( slope ) ? slope : 0;
@@ -80,7 +82,7 @@ const update = function ( state, msg ) {
80
82
  } catch ( error ) {
81
83
  if ( !state.tunableErrorLogged ) {
82
84
  state.tunableErrorLogged = true;
83
- console.error( `WinkComposer/${state.nodeType}: tunable threw: ${error.message}` );
85
+ logger.error( `winkComposer/${state.nodeType}: tunable threw: ${error.message}` );
84
86
  }
85
87
  }
86
88
 
@@ -38,13 +38,13 @@ const AVAILABLE_STATS = [ 'max', 'sum', 'countAbove' ];
38
38
  */
39
39
  const group = function ( name, ids, accessor, options ) {
40
40
  if ( typeof name !== 'string' || name.length === 0 ) {
41
- throw new Error( 'features.group: name must be a non-empty string' );
41
+ throw new Error( 'winkComposer/features.group: name must be a non-empty string' );
42
42
  }
43
43
  if ( !Array.isArray( ids ) || ids.length === 0 ) {
44
- throw new Error( 'features.group: ids must be a non-empty array' );
44
+ throw new Error( 'winkComposer/features.group: ids must be a non-empty array' );
45
45
  }
46
46
  if ( typeof accessor !== 'function' ) {
47
- throw new Error( 'features.group: accessor must be a function' );
47
+ throw new Error( 'winkComposer/features.group: accessor must be a function' );
48
48
  }
49
49
 
50
50
  const stats = ( options && Array.isArray( options.stats ) ) ?
@@ -54,7 +54,7 @@ const group = function ( name, ids, accessor, options ) {
54
54
  for ( let i = 0; i < stats.length; i += 1 ) {
55
55
  if ( AVAILABLE_STATS.indexOf( stats[ i ] ) === -1 ) {
56
56
  throw new Error(
57
- 'features.group: unknown stat "' + stats[ i ] +
57
+ 'winkComposer/features.group: unknown stat "' + stats[ i ] +
58
58
  '". Available: ' + AVAILABLE_STATS.join( ', ' )
59
59
  );
60
60
  }
@@ -65,7 +65,7 @@ const group = function ( name, ids, accessor, options ) {
65
65
  1.5;
66
66
 
67
67
  if ( typeof countAboveThreshold !== 'number' ) {
68
- throw new Error( 'features.group: countAboveThreshold must be a number' );
68
+ throw new Error( 'winkComposer/features.group: countAboveThreshold must be a number' );
69
69
  }
70
70
 
71
71
  return {
@@ -90,10 +90,10 @@ const group = function ( name, ids, accessor, options ) {
90
90
  */
91
91
  const scalar = function ( name, accessor ) {
92
92
  if ( typeof name !== 'string' || name.length === 0 ) {
93
- throw new Error( 'features.scalar: name must be a non-empty string' );
93
+ throw new Error( 'winkComposer/features.scalar: name must be a non-empty string' );
94
94
  }
95
95
  if ( typeof accessor !== 'function' ) {
96
- throw new Error( 'features.scalar: accessor must be a function' );
96
+ throw new Error( 'winkComposer/features.scalar: accessor must be a function' );
97
97
  }
98
98
  return { type: SCALAR, name: name, accessor: accessor };
99
99
  };
@@ -112,13 +112,13 @@ const scalar = function ( name, accessor ) {
112
112
  */
113
113
  const counter = function ( name, sourceAccessor, predicate ) {
114
114
  if ( typeof name !== 'string' || name.length === 0 ) {
115
- throw new Error( 'features.counter: name must be a non-empty string' );
115
+ throw new Error( 'winkComposer/features.counter: name must be a non-empty string' );
116
116
  }
117
117
  if ( typeof sourceAccessor !== 'function' ) {
118
- throw new Error( 'features.counter: sourceAccessor must be a function' );
118
+ throw new Error( 'winkComposer/features.counter: sourceAccessor must be a function' );
119
119
  }
120
120
  if ( typeof predicate !== 'function' ) {
121
- throw new Error( 'features.counter: predicate must be a function' );
121
+ throw new Error( 'winkComposer/features.counter: predicate must be a function' );
122
122
  }
123
123
  return { type: COUNTER, name: name, sourceAccessor: sourceAccessor, predicate: predicate };
124
124
  };
@@ -134,7 +134,7 @@ const counter = function ( name, sourceAccessor, predicate ) {
134
134
  */
135
135
  const buildExtractor = function ( schema ) {
136
136
  if ( !Array.isArray( schema ) || schema.length === 0 ) {
137
- throw new Error( 'features.buildExtractor: schema must be a non-empty array' );
137
+ throw new Error( 'winkComposer/features.buildExtractor: schema must be a non-empty array' );
138
138
  }
139
139
 
140
140
  // Build feature names from schema
@@ -149,7 +149,7 @@ const buildExtractor = function ( schema ) {
149
149
  names.push( col.name );
150
150
  } else {
151
151
  throw new Error(
152
- 'features.buildExtractor: unknown column type "' +
152
+ 'winkComposer/features.buildExtractor: unknown column type "' +
153
153
  String( col.type ) + '" at index ' + i
154
154
  );
155
155
  }
@@ -160,7 +160,7 @@ const buildExtractor = function ( schema ) {
160
160
  for ( let i = 0; i < names.length; i += 1 ) {
161
161
  if ( seen[ names[ i ] ] ) {
162
162
  throw new Error(
163
- 'features.buildExtractor: duplicate feature name "' + names[ i ] + '"'
163
+ 'winkComposer/features.buildExtractor: duplicate feature name "' + names[ i ] + '"'
164
164
  );
165
165
  }
166
166
  seen[ names[ i ] ] = true;
@@ -93,7 +93,7 @@ const train = function ( X, y, options ) {
93
93
  for ( let i = 0; i < n; i += 1 ) n1 += y[ i ];
94
94
  const n0 = n - n1;
95
95
  if ( n0 === 0 || n1 === 0 ) {
96
- throw new Error( 'train: y must contain both classes (0 and 1).' );
96
+ throw new Error( 'winkComposer/train: y must contain both classes (0 and 1).' );
97
97
  }
98
98
  w[ 0 ] = n / ( 2 * n0 );
99
99
  w[ 1 ] = n / ( 2 * n1 );
@@ -24,7 +24,7 @@ import { validateBinaryArray } from './validate.js';
24
24
  */
25
25
  const confusionMatrix = function ( predictions, labels ) {
26
26
  if ( predictions.length !== labels.length ) {
27
- throw new Error( 'confusionMatrix: predictions and labels must have the same length.' );
27
+ throw new Error( 'winkComposer/confusionMatrix: predictions and labels must have the same length.' );
28
28
  }
29
29
 
30
30
  validateBinaryArray( predictions, 'predictions', 'confusionMatrix' );
@@ -85,7 +85,7 @@ const classificationMetrics = function ( cm ) {
85
85
  */
86
86
  const sweepThresholds = function ( probabilities, labels, thresholds ) {
87
87
  if ( probabilities.length !== labels.length ) {
88
- throw new Error( 'sweepThresholds: probabilities and labels must have the same length.' );
88
+ throw new Error( 'winkComposer/sweepThresholds: probabilities and labels must have the same length.' );
89
89
  }
90
90
 
91
91
  validateBinaryArray( labels, 'labels', 'sweepThresholds' );
@@ -177,7 +177,7 @@ const computeRobustParams = function ( X ) {
177
177
  */
178
178
  const scale = function ( X, params ) {
179
179
  if ( !Array.isArray( X ) || X.length === 0 ) {
180
- throw new Error( 'scale: X must be a non-empty array of rows.' );
180
+ throw new Error( 'winkComposer/scale: X must be a non-empty array of rows.' );
181
181
  }
182
182
 
183
183
  const n = X.length;
@@ -221,7 +221,7 @@ const scale = function ( X, params ) {
221
221
  result[ i ] = out;
222
222
  }
223
223
  } else {
224
- throw new Error( 'scale: unknown method "' + method + '".' );
224
+ throw new Error( 'winkComposer/scale: unknown method "' + method + '".' );
225
225
  }
226
226
 
227
227
  return result;
@@ -24,21 +24,21 @@ import { validators } from '../../core/utils/validate/validators.js';
24
24
  */
25
25
  const validateMatrix = function ( X, caller ) {
26
26
  if ( !Array.isArray( X ) || X.length === 0 ) {
27
- throw new Error( caller + ': X must be a non-empty array of rows.' );
27
+ throw new Error( 'winkComposer/' + caller + ': X must be a non-empty array of rows.' );
28
28
  }
29
29
 
30
30
  const n = X.length;
31
31
  const p = X[ 0 ].length;
32
32
 
33
33
  if ( p === 0 ) {
34
- throw new Error( caller + ': rows must have at least one feature.' );
34
+ throw new Error( 'winkComposer/' + caller + ': rows must have at least one feature.' );
35
35
  }
36
36
 
37
37
  for ( let i = 0; i < n; i += 1 ) {
38
38
  const row = X[ i ];
39
39
  if ( !Array.isArray( row ) || row.length !== p ) {
40
40
  throw new Error(
41
- caller + ': row ' + i + ' has length ' +
41
+ 'winkComposer/' + caller + ': row ' + i + ' has length ' +
42
42
  ( Array.isArray( row ) ? row.length : 'N/A' ) +
43
43
  ', expected ' + p + '.'
44
44
  );
@@ -46,7 +46,7 @@ const validateMatrix = function ( X, caller ) {
46
46
  for ( let j = 0; j < p; j += 1 ) {
47
47
  if ( !validators.isFinite( row[ j ] ) ) {
48
48
  throw new Error(
49
- caller + ': non-finite value at row ' + i +
49
+ 'winkComposer/' + caller + ': non-finite value at row ' + i +
50
50
  ', column ' + j + '.'
51
51
  );
52
52
  }
@@ -67,7 +67,7 @@ const validateMatrix = function ( X, caller ) {
67
67
  const validateBinaryLabels = function ( y, n, caller ) {
68
68
  if ( !Array.isArray( y ) || y.length !== n ) {
69
69
  throw new Error(
70
- caller + ': y must be an array with length ' + n +
70
+ 'winkComposer/' + caller + ': y must be an array with length ' + n +
71
71
  ', got ' + ( Array.isArray( y ) ? y.length : typeof y ) + '.'
72
72
  );
73
73
  }
@@ -75,7 +75,7 @@ const validateBinaryLabels = function ( y, n, caller ) {
75
75
  for ( let i = 0; i < n; i += 1 ) {
76
76
  if ( y[ i ] !== 0 && y[ i ] !== 1 ) {
77
77
  throw new Error(
78
- caller + ': y[' + i + '] = ' + y[ i ] +
78
+ 'winkComposer/' + caller + ': y[' + i + '] = ' + y[ i ] +
79
79
  ', expected 0 or 1.'
80
80
  );
81
81
  }
@@ -93,7 +93,7 @@ const validateBinaryArray = function ( arr, name, caller ) {
93
93
  for ( let i = 0; i < arr.length; i += 1 ) {
94
94
  if ( arr[ i ] !== 0 && arr[ i ] !== 1 ) {
95
95
  throw new Error(
96
- caller + ': ' + name + '[' + i + '] = ' + arr[ i ] +
96
+ 'winkComposer/' + caller + ': ' + name + '[' + i + '] = ' + arr[ i ] +
97
97
  ', expected 0 or 1.'
98
98
  );
99
99
  }