@rip-lang/swarm 1.2.4 → 1.2.6

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 (2) hide show
  1. package/package.json +1 -1
  2. package/swarm.rip +46 -39
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rip-lang/swarm",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "Parallel job runner with worker threads — setup once, swarm many",
5
5
  "type": "module",
6
6
  "main": "swarm.rip",
package/swarm.rip CHANGED
@@ -87,8 +87,9 @@ hex = (str) ->
87
87
  [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)].join(';')
88
88
  _hex[str] = result
89
89
 
90
- fg = (rgb) -> if rgb then "\x1b[38;2;#{hex(rgb)}m" else "\x1b[39m"
91
- bg = (rgb) -> if rgb then "\x1b[48;2;#{hex(rgb)}m" else "\x1b[49m"
90
+ fg = (rgb) -> if rgb then "\x1b[38;2;#{hex(rgb)}m" else "\x1b[39m"
91
+ bg = (rgb) -> if rgb then "\x1b[48;2;#{hex(rgb)}m" else "\x1b[49m"
92
+ fgw =! "\x1b[97m" # bright white — more reliable than 24-bit fg("fff")
92
93
 
93
94
  # ==============================================================================
94
95
  # Progress display
@@ -113,13 +114,14 @@ draw = (state) ->
113
114
  ppct = (done + died) / jobs
114
115
  most = Math.max(...Object.values(info), 1)
115
116
 
116
- # worker bars
117
+ # worker bars — set colors once, just move cursor per row
118
+ write fgw + bg("5383ec")
117
119
  for slot, count of info
118
120
  tpct = count / most
119
121
  cols = Math.floor(ppct * tpct * wide)
120
- write go(parseInt(slot) + 1, len + 5) + bg("5383ec") + char.repeat(cols) + bg()
122
+ write go(parseInt(slot) + 1, len + 5) + char.repeat(cols)
121
123
 
122
- # summary bar
124
+ # summary bar — one atomic write
123
125
  dpct = done / jobs
124
126
  lpct = live / jobs
125
127
  gcol = Math.floor(dpct * wide)
@@ -129,12 +131,11 @@ draw = (state) ->
129
131
 
130
132
  out = [
131
133
  go(row, len + 5)
132
- fg("fff")
133
- bg("58a65c") + char.repeat(gcol) # green (done)
134
- bg("f1bf42") + char.repeat(ycol) # yellow (live)
135
- bg("d85140") + " ".repeat(rcol) # red (rest)
134
+ bg("58a65c") + char.repeat(gcol) # green (done)
135
+ bg("f1bf42") + char.repeat(ycol) # yellow (live)
136
+ bg("d85140") + " ".repeat(rcol) # red (rest)
136
137
  go(row, len + 5 + wide + 3)
137
- bg("5383ec") + " #{(ppct * 100).toFixed(1)}% " # blue (pct)
138
+ bg("5383ec") + " #{(ppct * 100).toFixed(1)}% " # blue (pct)
138
139
  if done > 0 then bg() + " " + bg("58a65c") + " #{done}/#{jobs} done "
139
140
  if died > 0 then bg() + " " + bg("d85140") + " #{died} died "
140
141
  ].filter(Boolean).join('') + fg() + bg()
@@ -162,6 +163,7 @@ export swarm = (opts = {}) ->
162
163
  char = (findArg(args, '-c', '--char') or opts.char or '•')[0]
163
164
  doreset = args.includes('-r') or args.includes('--reset')
164
165
  dosafe = args.includes('-s') or args.includes('--safe')
166
+ quiet = args.includes('-q') or args.includes('--quiet')
165
167
 
166
168
  if workers < 1
167
169
  console.error 'error: workers must be at least 1'
@@ -223,17 +225,19 @@ export swarm = (opts = {}) ->
223
225
 
224
226
  # signal handlers
225
227
  process.on 'SIGINT', ->
226
- cursor(true)
227
- write go(workers + 5, 1) + "\n"
228
+ cursor(true) unless quiet
229
+ write go(workers + 5, 1) + "\n" unless quiet
228
230
  process.exit(1)
229
- process.on 'SIGWINCH', ->
230
- drawFrame(workers)
231
- draw({ live, done, died, jobs, workers, info })
231
+ unless quiet
232
+ process.on 'SIGWINCH', ->
233
+ drawFrame(workers)
234
+ draw({ live, done, died, jobs, workers, info })
232
235
 
233
236
  # draw initial frame
234
237
  startTime = Date.now()
235
- cursor(false)
236
- drawFrame(workers)
238
+ unless quiet
239
+ cursor(false)
240
+ drawFrame(workers)
237
241
 
238
242
  # create workers and dispatch tasks
239
243
  allWorkers = []
@@ -254,8 +258,9 @@ export swarm = (opts = {}) ->
254
258
  taskPath = tasks[taskIdx++]
255
259
  inflight[slot] = taskPath
256
260
  live++
257
- write go(slot + 1, len + 5 + wide + 3) + " " + taskPath.split('/').pop() + clear(true)
258
- draw({ live, done, died, jobs, workers, info })
261
+ unless quiet
262
+ write go(slot + 1, len + 5 + wide + 3) + " " + taskPath.split('/').pop() + clear(true)
263
+ draw({ live, done, died, jobs, workers, info })
259
264
  worker.postMessage { type: 'task', taskPath }
260
265
  else
261
266
  inflight[slot] = null
@@ -272,6 +277,7 @@ export swarm = (opts = {}) ->
272
277
  w.on 'message', (msg) ->
273
278
  switch msg.type
274
279
  when 'error'
280
+ console.error "\nswarm: worker #{slot} failed to start: #{msg.error}"
275
281
  writeFileSync('.swarm/errors.log', "worker #{slot} startup: #{msg.error}\n", { flag: 'a' }) if existsSync(_dir)
276
282
  when 'ready'
277
283
  dispatchNext(w, slot)
@@ -282,7 +288,7 @@ export swarm = (opts = {}) ->
282
288
  live--
283
289
  done++
284
290
  info[slot]++
285
- draw({ live, done, died, jobs, workers, info })
291
+ draw({ live, done, died, jobs, workers, info }) unless quiet
286
292
  dispatchNext(w, slot)
287
293
  when 'failed'
288
294
  move(msg.taskPath, _died)
@@ -292,7 +298,7 @@ export swarm = (opts = {}) ->
292
298
  live--
293
299
  died++
294
300
  info[slot]++
295
- draw({ live, done, died, jobs, workers, info })
301
+ draw({ live, done, died, jobs, workers, info }) unless quiet
296
302
  dispatchNext(w, slot)
297
303
 
298
304
  w.on 'error', (err) ->
@@ -307,7 +313,7 @@ export swarm = (opts = {}) ->
307
313
  live--
308
314
  died++
309
315
  info[slot]++
310
- draw({ live, done, died, jobs, workers, info })
316
+ draw({ live, done, died, jobs, workers, info }) unless quiet
311
317
  # respawn if there's still work to do
312
318
  if done + died < jobs
313
319
  spawnWorker(slot)
@@ -319,23 +325,24 @@ export swarm = (opts = {}) ->
319
325
  for slot in [1..count]
320
326
  spawnWorker(slot)
321
327
 
322
- # final redraw — fill all worker bars and show per-worker stats
323
- secs = (Date.now() - startTime) / 1000
324
- for slot of info
325
- s = parseInt(slot)
326
- n = info[slot]
327
- rate = if secs > 0 then (n / secs).toFixed(1) else '—'
328
- write go(s + 1, len + 5) + bg("5383ec") + char.repeat(wide) + bg() + " │ #{n} jobs @ #{rate}/sec" + clear(true)
329
- draw({ live: 0, done, died, jobs, workers, info })
330
-
331
328
  # summary
332
- cursor(true)
333
- write go(workers + 5, 1)
334
- write "#{secs.toFixed(2)} secs"
335
- write " for #{jobs} jobs"
336
- write " by #{workers} workers"
337
- write " @ #{(jobs / secs).toFixed(2)} jobs/sec" if secs > 0
338
- write "\n\n"
329
+ secs = (Date.now() - startTime) / 1000
330
+ if quiet
331
+ p "#{secs.toFixed(2)} secs for #{jobs} jobs by #{workers} workers" + (if secs > 0 then " @ #{(jobs / secs).toFixed(2)} jobs/sec" else "")
332
+ else
333
+ for slot of info
334
+ s = parseInt(slot)
335
+ n = info[slot]
336
+ rate = if secs > 0 then (n / secs).toFixed(1) else '—'
337
+ write go(s + 1, len + 5) + fgw + bg("5383ec") + char.repeat(wide) + fg() + bg() + " │ #{n} jobs @ #{rate}/sec" + clear(true)
338
+ draw({ live: 0, done, died, jobs, workers, info })
339
+ cursor(true)
340
+ write go(workers + 5, 1)
341
+ write "#{secs.toFixed(2)} secs"
342
+ write " for #{jobs} jobs"
343
+ write " by #{workers} workers"
344
+ write " @ #{(jobs / secs).toFixed(2)} jobs/sec" if secs > 0
345
+ write "\n\n"
339
346
 
340
347
  # ==============================================================================
341
348
  # CLI helpers
@@ -344,7 +351,7 @@ export swarm = (opts = {}) ->
344
351
  # flags that swarm consumes (with value)
345
352
  _flagsWithValue = ['-w', '--workers', '-b', '--bar', '-c', '--char']
346
353
  # flags that swarm consumes (standalone)
347
- _flagsAlone = ['-r', '--reset', '-s', '--safe', '-h', '--help', '-v', '--version']
354
+ _flagsAlone = ['-r', '--reset', '-s', '--safe', '-q', '--quiet', '-h', '--help', '-v', '--version']
348
355
 
349
356
  findArg = (args, short, long) ->
350
357
  for arg, i in args