@winkjs/composer 0.5.1 → 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 (102) hide show
  1. package/package.json +2 -2
  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/terminal/assert-columns.js +1 -1
  7. package/src/core/emitter-manager/terminal/index.js +4 -4
  8. package/src/core/env-vars.js +44 -3
  9. package/src/core/logger/index.js +174 -0
  10. package/src/core/mqtt-protocol.js +2 -2
  11. package/src/core/partition-manager/get-stats.js +31 -0
  12. package/src/core/partition-manager/index.js +1 -0
  13. package/src/core/partition-manager/init.js +15 -0
  14. package/src/core/partition-manager/update.js +143 -87
  15. package/src/core/partition-manager/validate-flow.js +1 -1
  16. package/src/core/semantics/loader.js +1 -1
  17. package/src/core/semantics/warnings.js +5 -3
  18. package/src/core/shutdown-manager/index.js +71 -21
  19. package/src/core/source-manager/csv/start.js +109 -35
  20. package/src/core/source-manager/mqtt/client.js +95 -36
  21. package/src/core/source-manager/mqtt/dedup.js +3 -3
  22. package/src/core/source-manager/mqtt/index.js +1 -1
  23. package/src/core/source-manager/mqtt/status.js +67 -14
  24. package/src/core/source-manager/record-shape.js +55 -0
  25. package/src/core/source-manager/test-harness/field-generator.js +1 -1
  26. package/src/core/source-manager/test-harness/start.js +52 -10
  27. package/src/core/source-manager/test-harness/validate.js +1 -1
  28. package/src/core/storage-manager/questdb/assert-columns.js +2 -2
  29. package/src/core/storage-manager/questdb/ensure-tables.js +1 -1
  30. package/src/core/storage-manager/questdb/index.js +55 -17
  31. package/src/core/storage-manager/questdb/persist-plan.js +32 -8
  32. package/src/core/tunable/helpers.js +1 -1
  33. package/src/core/utils/callback-guard/index.js +155 -0
  34. package/src/core/utils/half-life/index.js +4 -4
  35. package/src/core/utils/validate/composer-validators.js +2 -2
  36. package/src/core/utils/validate/index.js +1 -1
  37. package/src/core/utils/validate/validate.js +1 -1
  38. package/src/core/wiring/assert-handle.js +5 -5
  39. package/src/core/wiring/assert-module.js +2 -2
  40. package/src/core/wiring/wire-emitters.js +4 -3
  41. package/src/core/wiring/wire-linear-graph.js +1 -1
  42. package/src/core/wiring/wire-node.js +6 -6
  43. package/src/core/wiring/wire-semantics.js +1 -1
  44. package/src/core/wiring/wire-storages.js +4 -3
  45. package/src/flow/driver.js +31 -6
  46. package/src/flow/expand-single-input-array.js +4 -4
  47. package/src/flow/flow-api-schemas.js +4 -4
  48. package/src/flow/flow.js +41 -41
  49. package/src/flow/for-each.js +12 -11
  50. package/src/flow/get-target-array.js +2 -2
  51. package/src/flow/load-node-module.js +1 -1
  52. package/src/flow/make-node-method.js +4 -4
  53. package/src/flow/resolve-pick-by-field.js +1 -1
  54. package/src/flow/run.js +176 -38
  55. package/src/flow/serialize.js +1 -1
  56. package/src/flow/spec-builder.js +1 -1
  57. package/src/flow/validate.js +1 -1
  58. package/src/nodes/butterworth-filter/init.js +5 -4
  59. package/src/nodes/categorize/update.js +3 -1
  60. package/src/nodes/controller/update.js +2 -1
  61. package/src/nodes/dwell-time-tracker/update.js +2 -1
  62. package/src/nodes/emit-if/helpers.js +57 -11
  63. package/src/nodes/emit-if/init.js +1 -1
  64. package/src/nodes/emit-if/update.js +8 -11
  65. package/src/nodes/page-hinkley/update.js +2 -1
  66. package/src/nodes/pass-if/update.js +3 -1
  67. package/src/nodes/persist-if/helpers.js +60 -4
  68. package/src/nodes/persist-if/init.js +1 -1
  69. package/src/nodes/persist-if/update.js +8 -16
  70. package/src/nodes/persistence-check/update.js +2 -1
  71. package/src/nodes/sanitize/update.js +4 -2
  72. package/src/nodes/swing-watch/update.js +3 -2
  73. package/src/nodes/threshold/update.js +2 -1
  74. package/src/nodes/trend/compute-confidence.js +4 -4
  75. package/src/nodes/trend/update.js +2 -1
  76. package/src/nodes/winnow/update.js +3 -1
  77. package/src/tools/training/features.js +13 -13
  78. package/src/tools/training/logistic.js +1 -1
  79. package/src/tools/training/metrics.js +2 -2
  80. package/src/tools/training/scale.js +2 -2
  81. package/src/tools/training/validate.js +7 -7
  82. package/src/core/utils/console-formatter/index.js +0 -207
  83. package/src/nodes/archive/csv/index.js +0 -6
  84. package/src/nodes/archive/csv/init.js +0 -36
  85. package/src/nodes/archive/csv/load.js +0 -70
  86. package/src/nodes/archive/csv/reset.js +0 -14
  87. package/src/nodes/archive/csv/simple-parser.js +0 -216
  88. package/src/nodes/archive/csv/test.js +0 -173
  89. package/src/nodes/archive/csv/update.js +0 -63
  90. package/src/nodes/sse-emitter/index.js +0 -7
  91. package/src/nodes/sse-emitter/init.js +0 -105
  92. package/src/nodes/sse-emitter/introspect.js +0 -134
  93. package/src/nodes/sse-emitter/publish-to.js +0 -6
  94. package/src/nodes/sse-emitter/recompute.js +0 -16
  95. package/src/nodes/sse-emitter/reset.js +0 -19
  96. package/src/nodes/sse-emitter/update.js +0 -69
  97. package/src/nodes/sse-emitter/utils/broadcast-heartbeat.js +0 -36
  98. package/src/nodes/sse-emitter/utils/build-message.js +0 -31
  99. package/src/nodes/sse-emitter/utils/connection-manager.js +0 -45
  100. package/src/nodes/sse-emitter/utils/endpoint-initializer.js +0 -20
  101. package/src/nodes/sse-emitter/utils/health-monitor.js +0 -38
  102. package/src/nodes/sse-emitter/utils/qos-handler.js +0 -41
package/src/flow/flow.js CHANGED
@@ -91,7 +91,7 @@ const flow = function ( flowName = 'flow1' ) {
91
91
  const enforceConfigFirst = function ( methodName ) {
92
92
  if ( pipelineStarted ) {
93
93
  throw new Error(
94
- `WinkComposer/flow: .${methodName}() must be called before any nodes or .switch()/.groupBy()`
94
+ `winkComposer/flow: .${methodName}() must be called before any nodes or .switch()/.groupBy()`
95
95
  );
96
96
  }
97
97
  };
@@ -100,11 +100,11 @@ const flow = function ( flowName = 'flow1' ) {
100
100
  const validateSwitchComplete = function () {
101
101
  if ( switchState.active ) {
102
102
  if ( switchState.caseOrder.length === 0 ) {
103
- throw new Error( 'WinkComposer/flow: .switch() requires at least one .case()' );
103
+ throw new Error( 'winkComposer/flow: .switch() requires at least one .case()' );
104
104
  }
105
105
  if ( !switchState.caseEnded ) {
106
106
  throw new Error(
107
- `WinkComposer/flow: case '${switchState.currentCase}' must end with .break()`
107
+ `winkComposer/flow: case '${switchState.currentCase}' must end with .break()`
108
108
  );
109
109
  }
110
110
  }
@@ -118,7 +118,7 @@ const flow = function ( flowName = 'flow1' ) {
118
118
  const collisions = collectFlowOutputCollisions( switchState, flowDefinition );
119
119
  if ( collisions.length > 0 ) {
120
120
  throw new Error(
121
- 'WinkComposer/flow: output field collision(s) - two nodes on one path ' +
121
+ 'winkComposer/flow: output field collision(s) - two nodes on one path ' +
122
122
  'write the same message field:\n - ' + collisions.join( '\n - ' )
123
123
  );
124
124
  }
@@ -160,10 +160,10 @@ const flow = function ( flowName = 'flow1' ) {
160
160
  api.source = function ( adapter, sourceConfig = {} ) {
161
161
  enforceConfigFirst( 'source' );
162
162
  if ( typeof adapter !== 'object' || adapter === null ) {
163
- throw new Error( 'WinkComposer/flow: source adapter must be an imported module' );
163
+ throw new Error( 'winkComposer/flow: source adapter must be an imported module' );
164
164
  }
165
165
  if ( typeof adapter.start !== 'function' ) {
166
- throw new Error( 'WinkComposer/flow: source adapter must have a start() function' );
166
+ throw new Error( 'winkComposer/flow: source adapter must have a start() function' );
167
167
  }
168
168
 
169
169
  // If the adapter provides a configSchema, we validate config here.
@@ -171,7 +171,7 @@ const flow = function ( flowName = 'flow1' ) {
171
171
  // not later inside adapter.start().
172
172
  if ( adapter.configSchema ) {
173
173
  const validation = validateWithSchema( adapter.configSchema, sourceConfig, 'source' );
174
- validation.throwIfInvalid( `flow/source:${adapter.id || 'unknown'}` );
174
+ validation.throwIfInvalid( `flow.source.${adapter.id || 'unknown'}` );
175
175
  }
176
176
 
177
177
  runtime.source = { adapter, config: sourceConfig };
@@ -181,13 +181,13 @@ const flow = function ( flowName = 'flow1' ) {
181
181
  api.emitter = function ( adapter, emitterConfig = {} ) {
182
182
  enforceConfigFirst( 'emitter' );
183
183
  if ( typeof adapter !== 'object' || adapter === null ) {
184
- throw new Error( 'WinkComposer/flow: emitter adapter must be an imported module' );
184
+ throw new Error( 'winkComposer/flow: emitter adapter must be an imported module' );
185
185
  }
186
186
  if ( typeof adapter.id !== 'string' || !adapter.id ) {
187
- throw new Error( 'WinkComposer/flow: emitter adapter must have an id' );
187
+ throw new Error( 'winkComposer/flow: emitter adapter must have an id' );
188
188
  }
189
189
  if ( typeof adapter.createEmitter !== 'function' ) {
190
- throw new Error( 'WinkComposer/flow: emitter adapter must have a createEmitter() function' );
190
+ throw new Error( 'winkComposer/flow: emitter adapter must have a createEmitter() function' );
191
191
  }
192
192
 
193
193
  // If the adapter provides a configSchema, we validate config here.
@@ -195,7 +195,7 @@ const flow = function ( flowName = 'flow1' ) {
195
195
  // not later inside adapter.createEmitter().
196
196
  if ( adapter.configSchema ) {
197
197
  const validation = validateWithSchema( adapter.configSchema, emitterConfig, 'emitter' );
198
- validation.throwIfInvalid( `flow/emitter:${adapter.id}` );
198
+ validation.throwIfInvalid( `flow.emitter.${adapter.id}` );
199
199
  }
200
200
 
201
201
  runtime.emitters[ adapter.id ] = { adapter, config: emitterConfig };
@@ -205,13 +205,13 @@ const flow = function ( flowName = 'flow1' ) {
205
205
  api.storage = function ( adapter, storageConfig = {} ) {
206
206
  enforceConfigFirst( 'storage' );
207
207
  if ( typeof adapter !== 'object' || adapter === null ) {
208
- throw new Error( 'WinkComposer/flow: storage adapter must be an imported module' );
208
+ throw new Error( 'winkComposer/flow: storage adapter must be an imported module' );
209
209
  }
210
210
  if ( typeof adapter.id !== 'string' || !adapter.id ) {
211
- throw new Error( 'WinkComposer/flow: storage adapter must have an id' );
211
+ throw new Error( 'winkComposer/flow: storage adapter must have an id' );
212
212
  }
213
213
  if ( typeof adapter.createStorage !== 'function' ) {
214
- throw new Error( 'WinkComposer/flow: storage adapter must have a createStorage() function' );
214
+ throw new Error( 'winkComposer/flow: storage adapter must have a createStorage() function' );
215
215
  }
216
216
 
217
217
  // If the adapter provides a configSchema, we validate config here.
@@ -219,7 +219,7 @@ const flow = function ( flowName = 'flow1' ) {
219
219
  // not later inside adapter.createStorage().
220
220
  if ( adapter.configSchema ) {
221
221
  const validation = validateWithSchema( adapter.configSchema, storageConfig, 'storage' );
222
- validation.throwIfInvalid( `flow/storage:${adapter.id}` );
222
+ validation.throwIfInvalid( `flow.storage.${adapter.id}` );
223
223
  }
224
224
 
225
225
  runtime.storages[ adapter.id ] = { adapter, config: storageConfig };
@@ -230,7 +230,7 @@ const flow = function ( flowName = 'flow1' ) {
230
230
  enforceConfigFirst( 'assetId' );
231
231
 
232
232
  if ( typeof field !== 'string' || field.length === 0 ) {
233
- throw new Error( 'WinkComposer/flow: .assetId() requires a non-empty string field name' );
233
+ throw new Error( 'winkComposer/flow: .assetId() requires a non-empty string field name' );
234
234
  }
235
235
 
236
236
  // Schema-based validation for robust error handling
@@ -251,7 +251,7 @@ const flow = function ( flowName = 'flow1' ) {
251
251
  if ( assetClassDef === null || assetClassDef === undefined || typeof assetClassDef !== 'object' ) {
252
252
  const actualType = assetClassDef === null ? 'null' : typeof assetClassDef;
253
253
  throw new Error(
254
- `WinkComposer/flow.assetClass: Expected object, got ${actualType}`
254
+ `winkComposer/flow.assetClass: Expected object, got ${actualType}`
255
255
  );
256
256
  }
257
257
 
@@ -266,7 +266,7 @@ const flow = function ( flowName = 'flow1' ) {
266
266
  const validation = validateWithSchema( assetClassSchema, assetClassDef, 'assetClass' );
267
267
  if ( !validation.valid ) {
268
268
  throw new Error(
269
- `WinkComposer/flow.assetClass: validation failed:\n - ${validation.errors.join( '\n - ' )}`
269
+ `winkComposer/flow.assetClass: validation failed:\n - ${validation.errors.join( '\n - ' )}`
270
270
  );
271
271
  }
272
272
 
@@ -298,7 +298,7 @@ const flow = function ( flowName = 'flow1' ) {
298
298
  */
299
299
  api.switch = function ( field ) {
300
300
  if ( typeof field !== 'string' || field.length === 0 ) {
301
- throw new Error( 'WinkComposer/flow: .switch() requires a non-empty string field name' );
301
+ throw new Error( 'winkComposer/flow: .switch() requires a non-empty string field name' );
302
302
  }
303
303
 
304
304
  // Schema-based validation: field must be a valid identifier (mirrors .assetId())
@@ -307,21 +307,21 @@ const flow = function ( flowName = 'flow1' ) {
307
307
 
308
308
  // Check groupBy before switchState (endGroup sets switchState.active)
309
309
  if ( groupByState.active || groupByState.expanded ) {
310
- throw new Error( 'WinkComposer/flow: .switch() cannot be used with .groupBy()' );
310
+ throw new Error( 'winkComposer/flow: .switch() cannot be used with .groupBy()' );
311
311
  }
312
312
  if ( switchState.active ) {
313
- throw new Error( 'WinkComposer/flow: .switch() can only be called once per flow' );
313
+ throw new Error( 'winkComposer/flow: .switch() can only be called once per flow' );
314
314
  }
315
315
  if ( flowDefinition.length > 0 ) {
316
316
  throw new Error(
317
- 'WinkComposer/flow: .switch() cannot be called after nodes - ' +
317
+ 'winkComposer/flow: .switch() cannot be called after nodes - ' +
318
318
  'use .switch() before defining any nodes, or omit .switch() for single-pipeline flows'
319
319
  );
320
320
  }
321
321
 
322
322
  // Validate field conflict with partition
323
323
  if ( runtime.partitionField === field ) {
324
- throw new Error( 'WinkComposer/flow: partition field and switch field must be different' );
324
+ throw new Error( 'winkComposer/flow: partition field and switch field must be different' );
325
325
  }
326
326
 
327
327
  runtime.specializationField = field;
@@ -340,15 +340,15 @@ const flow = function ( flowName = 'flow1' ) {
340
340
  */
341
341
  api.case = function ( key ) {
342
342
  if ( !switchState.active ) {
343
- throw new Error( 'WinkComposer/flow: .case() requires an active .switch()' );
343
+ throw new Error( 'winkComposer/flow: .case() requires an active .switch()' );
344
344
  }
345
345
  if ( switchState.currentCase !== null && !switchState.caseEnded ) {
346
346
  throw new Error(
347
- `WinkComposer/flow: previous case '${switchState.currentCase}' must end with .break() before starting a new case`
347
+ `winkComposer/flow: previous case '${switchState.currentCase}' must end with .break() before starting a new case`
348
348
  );
349
349
  }
350
350
  if ( switchState.caseSpecs[ key ] !== undefined ) {
351
- throw new Error( `WinkComposer/flow: duplicate case key '${key}'` );
351
+ throw new Error( `winkComposer/flow: duplicate case key '${key}'` );
352
352
  }
353
353
 
354
354
  switchState.currentCase = key;
@@ -367,14 +367,14 @@ const flow = function ( flowName = 'flow1' ) {
367
367
  */
368
368
  api.break = function () {
369
369
  if ( !switchState.active ) {
370
- throw new Error( 'WinkComposer/flow: .break() requires an active .switch()' );
370
+ throw new Error( 'winkComposer/flow: .break() requires an active .switch()' );
371
371
  }
372
372
  if ( switchState.currentCase === null || switchState.caseEnded ) {
373
- throw new Error( 'WinkComposer/flow: .break() must follow a .case() with at least one node' );
373
+ throw new Error( 'winkComposer/flow: .break() must follow a .case() with at least one node' );
374
374
  }
375
375
  if ( switchState.caseSpecs[ switchState.currentCase ].length === 0 ) {
376
376
  throw new Error(
377
- `WinkComposer/flow: case '${switchState.currentCase}' is empty - add at least one node before .break()`
377
+ `winkComposer/flow: case '${switchState.currentCase}' is empty - add at least one node before .break()`
378
378
  );
379
379
  }
380
380
 
@@ -408,7 +408,7 @@ const flow = function ( flowName = 'flow1' ) {
408
408
  api.groupBy = function ( field, values ) {
409
409
  // Validation: field must be non-empty string
410
410
  if ( typeof field !== 'string' || field.length === 0 ) {
411
- throw new Error( 'WinkComposer/flow: .groupBy() requires a non-empty string field name' );
411
+ throw new Error( 'winkComposer/flow: .groupBy() requires a non-empty string field name' );
412
412
  }
413
413
 
414
414
  // Schema-based validation: field must be a valid identifier (mirrors .assetId())
@@ -417,14 +417,14 @@ const flow = function ( flowName = 'flow1' ) {
417
417
 
418
418
  // Validation: values must be array with at least 2 elements
419
419
  if ( !Array.isArray( values ) || values.length < 2 ) {
420
- throw new Error( 'WinkComposer/flow: .groupBy() requires at least 2 group values' );
420
+ throw new Error( 'winkComposer/flow: .groupBy() requires at least 2 group values' );
421
421
  }
422
422
 
423
423
  // Validation: each value must be string or number
424
424
  for ( let i = 0; i < values.length; i += 1 ) {
425
425
  const v = values[ i ];
426
426
  if ( typeof v !== 'string' && typeof v !== 'number' ) {
427
- throw new Error( `WinkComposer/flow: group value at index ${i} must be a string or number` );
427
+ throw new Error( `winkComposer/flow: group value at index ${i} must be a string or number` );
428
428
  }
429
429
  }
430
430
 
@@ -433,32 +433,32 @@ const flow = function ( flowName = 'flow1' ) {
433
433
  for ( let i = 0; i < values.length; i += 1 ) {
434
434
  const v = values[ i ];
435
435
  if ( seen[ v ] ) {
436
- throw new Error( `WinkComposer/flow: duplicate group value '${v}' in .groupBy()` );
436
+ throw new Error( `winkComposer/flow: duplicate group value '${v}' in .groupBy()` );
437
437
  }
438
438
  seen[ v ] = 1;
439
439
  }
440
440
 
441
441
  // Validation: cannot call groupBy twice (check this first - user intent is to reuse groupBy)
442
442
  if ( groupByState.active || groupByState.expanded ) {
443
- throw new Error( 'WinkComposer/flow: .groupBy() can only be called once per flow' );
443
+ throw new Error( 'winkComposer/flow: .groupBy() can only be called once per flow' );
444
444
  }
445
445
 
446
446
  // Validation: cannot use groupBy if switchState is active (manual switch, not from endGroup)
447
447
  if ( switchState.active ) {
448
- throw new Error( 'WinkComposer/flow: .groupBy() cannot be used inside .switch()' );
448
+ throw new Error( 'winkComposer/flow: .groupBy() cannot be used inside .switch()' );
449
449
  }
450
450
 
451
451
  // Validation: must come before any nodes
452
452
  if ( flowDefinition.length > 0 ) {
453
453
  throw new Error(
454
- 'WinkComposer/flow: .groupBy() cannot be called after nodes - ' +
454
+ 'winkComposer/flow: .groupBy() cannot be called after nodes - ' +
455
455
  'use .groupBy() before defining any nodes'
456
456
  );
457
457
  }
458
458
 
459
459
  // Validation: groupBy field cannot equal partition field
460
460
  if ( runtime.partitionField === field ) {
461
- throw new Error( 'WinkComposer/flow: partition field and groupBy field must be different' );
461
+ throw new Error( 'winkComposer/flow: partition field and groupBy field must be different' );
462
462
  }
463
463
 
464
464
  // Initialize groupBy state
@@ -487,12 +487,12 @@ const flow = function ( flowName = 'flow1' ) {
487
487
  api.endGroup = function () {
488
488
  // Validation: must be in active groupBy state
489
489
  if ( !groupByState.active ) {
490
- throw new Error( 'WinkComposer/flow: .endGroup() requires an active .groupBy()' );
490
+ throw new Error( 'winkComposer/flow: .endGroup() requires an active .groupBy()' );
491
491
  }
492
492
 
493
493
  // Validation: must have at least one template node
494
494
  if ( groupByState.templateSpecs.length === 0 ) {
495
- throw new Error( 'WinkComposer/flow: .groupBy() must contain at least one node before .endGroup()' );
495
+ throw new Error( 'winkComposer/flow: .groupBy() must contain at least one node before .endGroup()' );
496
496
  }
497
497
 
498
498
  // Set specialization field (same as switch does)
@@ -553,7 +553,7 @@ const flow = function ( flowName = 'flow1' ) {
553
553
 
554
554
  // Single-pipeline mode (backward compatible)
555
555
  if ( flowDefinition.length === 0 ) {
556
- throw new Error( 'WinkComposer/flow: Cannot build empty flow - add at least one node' );
556
+ throw new Error( 'winkComposer/flow: Cannot build empty flow - add at least one node' );
557
557
  }
558
558
  const imports = Array.from( importSet ).sort();
559
559
  return serializeModule( { imports, flowName, specs: flowDefinition } );
@@ -646,7 +646,7 @@ const flow = function ( flowName = 'flow1' ) {
646
646
 
647
647
  // Single-pipeline mode (backward compatible)
648
648
  if ( flowDefinition.length === 0 ) {
649
- throw new Error( 'WinkComposer/flow: Cannot run empty flow - add at least one node' );
649
+ throw new Error( 'winkComposer/flow: Cannot run empty flow - add at least one node' );
650
650
  }
651
651
  const handle = await runFlow( flowName, flowDefinition, importSet, runtime );
652
652
  return handle;
@@ -31,6 +31,7 @@ import { getSignaturePattern } from './get-signature-pattern.js';
31
31
  import { specBuilder } from './spec-builder.js';
32
32
  import { getTargetArray } from './get-target-array.js';
33
33
  import { resolvePickByField } from './resolve-pick-by-field.js';
34
+ import { logger } from '../core/logger/index.js';
34
35
 
35
36
  /**
36
37
  * Locate the index of a named argument slot in a signature schema (the source of
@@ -90,7 +91,7 @@ export const makeForEach = function ( api, flowState ) {
90
91
  // rather than emit a silently-wrong per-field controller.
91
92
  if ( pattern === SIGNATURE_PATTERNS.nameLogic ) {
92
93
  throw new Error(
93
- `WinkComposer/flow: forEach cannot fan a controller ('${args[ 0 ]}'); ` +
94
+ `winkComposer/flow: forEach cannot fan a controller ('${args[ 0 ]}'); ` +
94
95
  'fan data-processing nodes only.'
95
96
  );
96
97
  }
@@ -109,7 +110,7 @@ export const makeForEach = function ( api, flowState ) {
109
110
  // Node-name collision check before building stats (matches make-node-method),
110
111
  // so a rejected node never registers its output params in the global checker.
111
112
  if ( isNodeNameDuplicate( t[ 0 ] ) ) {
112
- throw new Error( `WinkComposer/flow: duplicate node ${t[ 0 ]} found.` );
113
+ throw new Error( `winkComposer/flow: duplicate node ${t[ 0 ]} found.` );
113
114
  }
114
115
 
115
116
  // Outputs: store each labelled output as `${field}_${label}`, and record the
@@ -121,8 +122,8 @@ export const makeForEach = function ( api, flowState ) {
121
122
  for ( let i = 0; i < outKeys.length; i += 1 ) {
122
123
  const label = rawOutputs[ outKeys[ i ] ];
123
124
  if ( Object.prototype.hasOwnProperty.call( recorded, label ) ) {
124
- console.warn(
125
- `WinkComposer/flow: forEach output label '${label}' written by ` +
125
+ logger.warn(
126
+ `winkComposer/flow: forEach output label '${label}' written by ` +
126
127
  `more than one step on field '${field}'; each.out will return the last.`
127
128
  );
128
129
  }
@@ -146,7 +147,7 @@ export const makeForEach = function ( api, flowState ) {
146
147
  targetArray.push( spec );
147
148
  } catch ( error ) {
148
149
  throw new Error(
149
- `WinkComposer/flow: Failed to process "${callContext}"\n` +
150
+ `winkComposer/flow: Failed to process "${callContext}"\n` +
150
151
  ` Reason: ${error.message}\n`
151
152
  );
152
153
  }
@@ -170,7 +171,7 @@ export const makeForEach = function ( api, flowState ) {
170
171
  each.out = function ( label ) {
171
172
  if ( !Object.prototype.hasOwnProperty.call( recorded, label ) ) {
172
173
  throw new Error(
173
- `WinkComposer/flow: each.out( '${label}' ) on field '${field}': ` +
174
+ `winkComposer/flow: each.out( '${label}' ) on field '${field}': ` +
174
175
  `no earlier step in this chain wrote an output labelled '${label}'.`
175
176
  );
176
177
  }
@@ -191,25 +192,25 @@ export const makeForEach = function ( api, flowState ) {
191
192
  // that only resolves at .endGroup(), so the callback cannot run where written.
192
193
  if ( groupByState.active ) {
193
194
  throw new Error(
194
- 'WinkComposer/flow: forEach inside groupBy is not supported yet; ' +
195
+ 'winkComposer/flow: forEach inside groupBy is not supported yet; ' +
195
196
  'the field list must be a concrete array at the call site.'
196
197
  );
197
198
  }
198
199
  if ( !Array.isArray( fields ) || fields.length === 0 ) {
199
- throw new Error( 'WinkComposer/flow: forEach requires a non-empty array of field names.' );
200
+ throw new Error( 'winkComposer/flow: forEach requires a non-empty array of field names.' );
200
201
  }
201
202
  if ( typeof callback !== 'function' ) {
202
- throw new Error( 'WinkComposer/flow: forEach requires a callback function.' );
203
+ throw new Error( 'winkComposer/flow: forEach requires a callback function.' );
203
204
  }
204
205
 
205
206
  const seen = Object.create( null );
206
207
  for ( let i = 0; i < fields.length; i += 1 ) {
207
208
  const field = fields[ i ];
208
209
  if ( typeof field !== 'string' || field.length === 0 ) {
209
- throw new Error( 'WinkComposer/flow: forEach field names must be non-empty strings.' );
210
+ throw new Error( 'winkComposer/flow: forEach field names must be non-empty strings.' );
210
211
  }
211
212
  if ( Object.prototype.hasOwnProperty.call( seen, field ) ) {
212
- throw new Error( `WinkComposer/flow: forEach duplicate field '${field}' in field list.` );
213
+ throw new Error( `winkComposer/flow: forEach duplicate field '${field}' in field list.` );
213
214
  }
214
215
  seen[ field ] = 1;
215
216
  }
@@ -29,13 +29,13 @@ export const getTargetArray = function ( switchState, groupByState, flowDefiniti
29
29
  if ( switchState && switchState.active ) {
30
30
  if ( switchState.currentCase === null ) {
31
31
  throw new Error(
32
- 'WinkComposer/flow: node called outside of a .case() block - ' +
32
+ 'winkComposer/flow: node called outside of a .case() block - ' +
33
33
  'after .switch(), you must call .case() before adding nodes'
34
34
  );
35
35
  }
36
36
  if ( switchState.caseEnded ) {
37
37
  throw new Error(
38
- 'WinkComposer/flow: node called after .break() - ' +
38
+ 'winkComposer/flow: node called after .break() - ' +
39
39
  'start a new .case() before adding more nodes'
40
40
  );
41
41
  }
@@ -31,7 +31,7 @@ const moduleCache = new Map();
31
31
  export const loadNodeModule = async function ( nodeName ) {
32
32
  // Validate node name
33
33
  if ( !isValidNodeName( nodeName ) ) {
34
- throw new Error( `Unknown node type: "${nodeName}"` );
34
+ throw new Error( `winkComposer/flow: Unknown node type: "${nodeName}"` );
35
35
  }
36
36
 
37
37
  // Return cached module if available
@@ -8,7 +8,7 @@ import { specBuilder } from './spec-builder.js';
8
8
  import { getTargetArray } from './get-target-array.js';
9
9
 
10
10
  /**
11
- * Creates a chainable DSL method for a WinkComposer node.
11
+ * Creates a chainable DSL method for a winkComposer node.
12
12
  *
13
13
  * @param {Object} api - The fluent API object to extend
14
14
  * @param {string} node - Node type name (e.g., 'esMean')
@@ -79,9 +79,9 @@ export const makeNodeMethod = function ( api, node, meta, flowState ) {
79
79
  } else {
80
80
  // Canonical builder path
81
81
  const instName = args[ 0 ];
82
- if ( effectiveNodeChecker( instName ) ) throw Error( `WinkComposer/flow: duplicate node ${instName} found.` );
82
+ if ( effectiveNodeChecker( instName ) ) throw Error( `winkComposer/flow: duplicate node ${instName} found.` );
83
83
  const pattern = getSignaturePattern( schema );
84
- if ( pattern === SIGNATURE_PATTERNS.unknown ) throw Error( `WinkComposer/flow: unknown signature pattern found for ${instName}.` );
84
+ if ( pattern === SIGNATURE_PATTERNS.unknown ) throw Error( `winkComposer/flow: unknown signature pattern found for ${instName}.` );
85
85
  const status = validateWithSchema( schemas[ pattern ], [ ...args ], 'Arg' );
86
86
  status.throwIfInvalid( 'argument' );
87
87
  built = buildSpecFor[ pattern ]( [ ...args ] );
@@ -101,7 +101,7 @@ export const makeNodeMethod = function ( api, node, meta, flowState ) {
101
101
  }
102
102
  } catch ( error ) {
103
103
  const enhancedError = new Error(
104
- `WinkComposer/flow: Failed to process "${callContext}"\n` +
104
+ `winkComposer/flow: Failed to process "${callContext}"\n` +
105
105
  ` Reason: ${error.message}\n`
106
106
  );
107
107
 
@@ -35,7 +35,7 @@ export const resolvePickByField = function ( value, field ) {
35
35
  const map = value.semantics.map;
36
36
  if ( !Object.prototype.hasOwnProperty.call( map, field ) ) {
37
37
  throw new Error(
38
- `WinkComposer/flow: pickByField has no entry for field '${field}' ` +
38
+ `winkComposer/flow: pickByField has no entry for field '${field}' ` +
39
39
  `(map keys: ${Object.keys( map ).join( ', ' )}).`
40
40
  );
41
41
  }