@rip-lang/swarm 1.2.3 → 1.2.5

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 +60 -57
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rip-lang/swarm",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
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
@@ -94,19 +94,18 @@ bg = (rgb) -> if rgb then "\x1b[48;2;#{hex(rgb)}m" else "\x1b[49m"
94
94
  # Progress display
95
95
  # ==============================================================================
96
96
 
97
- _char = _wide = _len = null
97
+ char = wide = len = null
98
98
 
99
99
  drawFrame = (workers) ->
100
- _len = String(workers).length
100
+ len = String(workers).length
101
101
  write clear()
102
- write go(2 + workers, _len + 3) + "╰" + "─".repeat(_wide + 2) + "╯"
103
- write go(1, _len + 3) + "╭" + "─".repeat(_wide + 2) + "╮"
102
+ write go(2 + workers, len + 3) + "╰" + "─".repeat(wide + 2) + "╯"
103
+ write go(1, len + 3) + "╭" + "─".repeat(wide + 2) + "╮"
104
104
  for i in [0...workers]
105
105
  write go(i + 2, 1)
106
- label = String(i + 1).padStart(_len)
107
- write " #{label} │ " + " ".repeat(_wide) + " │"
108
- # summary bar row
109
- write go(workers + 3, _len + 3) + "│ " + " ".repeat(_wide) + " │"
106
+ label = String(i + 1).padStart(len)
107
+ write " #{label} │ " + " ".repeat(wide) + " │"
108
+ write go(workers + 3, len + 3) + "│ " + " ".repeat(wide) + " │"
110
109
 
111
110
  draw = (state) ->
112
111
  { live, done, died, jobs, workers, info } = state
@@ -117,27 +116,29 @@ draw = (state) ->
117
116
  # worker bars
118
117
  for slot, count of info
119
118
  tpct = count / most
120
- cols = Math.floor(ppct * tpct * _wide)
121
- write go(parseInt(slot) + 1, _len + 5) + bg("5383ec") + _char.repeat(cols) + bg()
119
+ cols = Math.floor(ppct * tpct * wide)
120
+ write go(parseInt(slot) + 1, len + 5) + fg("fff") + bg("5383ec") + char.repeat(cols) + fg() + bg()
122
121
 
123
122
  # summary bar
124
123
  dpct = done / jobs
125
124
  lpct = live / jobs
126
- gcol = Math.floor(dpct * _wide)
127
- ycol = Math.floor(lpct * _wide)
128
- rcol = Math.max(0, _wide - gcol - ycol)
125
+ gcol = Math.floor(dpct * wide)
126
+ ycol = Math.floor(lpct * wide)
127
+ rcol = Math.max(0, wide - gcol - ycol)
129
128
  row = workers + 3
130
129
 
131
- write go(row, _len + 5)
132
- write fg("fff")
133
- write bg("58a65c") + _char.repeat(gcol) # green (done)
134
- write bg("f1bf42") + _char.repeat(ycol) # yellow (live)
135
- write bg("d85140") + " ".repeat(rcol) # red (rest)
136
- write go(row, _len + 5 + _wide + 3)
137
- write bg("5383ec") + " #{(ppct * 100).toFixed(1)}% "
138
- write bg() + " " + bg("58a65c") + " #{done}/#{jobs} done " if done > 0
139
- write bg() + " " + bg("d85140") + " #{died} died " if died > 0
140
- write fg() + bg()
130
+ out = [
131
+ 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)
136
+ go(row, len + 5 + wide + 3)
137
+ bg("5383ec") + " #{(ppct * 100).toFixed(1)}% " # blue (pct)
138
+ if done > 0 then bg() + " " + bg("58a65c") + " #{done}/#{jobs} done "
139
+ if died > 0 then bg() + " " + bg("d85140") + " #{died} died "
140
+ ].filter(Boolean).join('') + fg() + bg()
141
+ write out
141
142
 
142
143
  # ==============================================================================
143
144
  # Worker orchestration
@@ -157,18 +158,16 @@ export swarm = (opts = {}) ->
157
158
  # parse CLI options
158
159
  args = process.argv.slice(2)
159
160
  workers = parseInt(findArg(args, '-w', '--workers')) or opts.workers or cpus().length
160
- barw = parseInt(findArg(args, '-b', '--bar')) or opts.bar or 20
161
- char = findArg(args, '-c', '--char') or opts.char or '•'
161
+ wide = parseInt(findArg(args, '-b', '--bar')) or opts.bar or 20
162
+ char = (findArg(args, '-c', '--char') or opts.char or '•')[0]
162
163
  doreset = args.includes('-r') or args.includes('--reset')
163
164
  dosafe = args.includes('-s') or args.includes('--safe')
165
+ quiet = args.includes('-q') or args.includes('--quiet')
164
166
 
165
167
  if workers < 1
166
168
  console.error 'error: workers must be at least 1'
167
169
  process.exit(1)
168
170
 
169
- _wide = barw
170
- _char = char[0]
171
-
172
171
  if doreset
173
172
  rmSync(_dir, { recursive: true, force: true })
174
173
  console.log 'removed .swarm directory'
@@ -225,17 +224,19 @@ export swarm = (opts = {}) ->
225
224
 
226
225
  # signal handlers
227
226
  process.on 'SIGINT', ->
228
- cursor(true)
229
- write go(workers + 5, 1) + "\n"
227
+ cursor(true) unless quiet
228
+ write go(workers + 5, 1) + "\n" unless quiet
230
229
  process.exit(1)
231
- process.on 'SIGWINCH', ->
232
- drawFrame(workers)
233
- draw({ live, done, died, jobs, workers, info })
230
+ unless quiet
231
+ process.on 'SIGWINCH', ->
232
+ drawFrame(workers)
233
+ draw({ live, done, died, jobs, workers, info })
234
234
 
235
235
  # draw initial frame
236
236
  startTime = Date.now()
237
- cursor(false)
238
- drawFrame(workers)
237
+ unless quiet
238
+ cursor(false)
239
+ drawFrame(workers)
239
240
 
240
241
  # create workers and dispatch tasks
241
242
  allWorkers = []
@@ -256,8 +257,9 @@ export swarm = (opts = {}) ->
256
257
  taskPath = tasks[taskIdx++]
257
258
  inflight[slot] = taskPath
258
259
  live++
259
- write go(slot + 1, _len + 5 + _wide + 3) + " " + taskPath.split('/').pop() + clear(true)
260
- draw({ live, done, died, jobs, workers, info })
260
+ unless quiet
261
+ write go(slot + 1, len + 5 + wide + 3) + " " + taskPath.split('/').pop() + clear(true)
262
+ draw({ live, done, died, jobs, workers, info })
261
263
  worker.postMessage { type: 'task', taskPath }
262
264
  else
263
265
  inflight[slot] = null
@@ -284,7 +286,7 @@ export swarm = (opts = {}) ->
284
286
  live--
285
287
  done++
286
288
  info[slot]++
287
- draw({ live, done, died, jobs, workers, info })
289
+ draw({ live, done, died, jobs, workers, info }) unless quiet
288
290
  dispatchNext(w, slot)
289
291
  when 'failed'
290
292
  move(msg.taskPath, _died)
@@ -294,7 +296,7 @@ export swarm = (opts = {}) ->
294
296
  live--
295
297
  died++
296
298
  info[slot]++
297
- draw({ live, done, died, jobs, workers, info })
299
+ draw({ live, done, died, jobs, workers, info }) unless quiet
298
300
  dispatchNext(w, slot)
299
301
 
300
302
  w.on 'error', (err) ->
@@ -309,7 +311,7 @@ export swarm = (opts = {}) ->
309
311
  live--
310
312
  died++
311
313
  info[slot]++
312
- draw({ live, done, died, jobs, workers, info })
314
+ draw({ live, done, died, jobs, workers, info }) unless quiet
313
315
  # respawn if there's still work to do
314
316
  if done + died < jobs
315
317
  spawnWorker(slot)
@@ -321,23 +323,24 @@ export swarm = (opts = {}) ->
321
323
  for slot in [1..count]
322
324
  spawnWorker(slot)
323
325
 
324
- # final redraw — fill all worker bars and show per-worker stats
325
- secs = (Date.now() - startTime) / 1000
326
- for slot of info
327
- s = parseInt(slot)
328
- n = info[slot]
329
- rate = if secs > 0 then (n / secs).toFixed(1) else '—'
330
- write go(s + 1, _len + 5) + bg("5383ec") + _char.repeat(_wide) + bg() + " │ #{n} jobs @ #{rate}/sec" + clear(true)
331
- draw({ live: 0, done, died, jobs, workers, info })
332
-
333
326
  # summary
334
- cursor(true)
335
- write go(workers + 5, 1)
336
- write "#{secs.toFixed(2)} secs"
337
- write " for #{jobs} jobs"
338
- write " by #{workers} workers"
339
- write " @ #{(jobs / secs).toFixed(2)} jobs/sec" if secs > 0
340
- write "\n\n"
327
+ secs = (Date.now() - startTime) / 1000
328
+ if quiet
329
+ p "#{secs.toFixed(2)} secs for #{jobs} jobs by #{workers} workers" + (if secs > 0 then " @ #{(jobs / secs).toFixed(2)} jobs/sec" else "")
330
+ else
331
+ for slot of info
332
+ s = parseInt(slot)
333
+ n = info[slot]
334
+ rate = if secs > 0 then (n / secs).toFixed(1) else '—'
335
+ write go(s + 1, len + 5) + fg("fff") + bg("5383ec") + char.repeat(wide) + fg() + bg() + " │ #{n} jobs @ #{rate}/sec" + clear(true)
336
+ draw({ live: 0, done, died, jobs, workers, info })
337
+ cursor(true)
338
+ write go(workers + 5, 1)
339
+ write "#{secs.toFixed(2)} secs"
340
+ write " for #{jobs} jobs"
341
+ write " by #{workers} workers"
342
+ write " @ #{(jobs / secs).toFixed(2)} jobs/sec" if secs > 0
343
+ write "\n\n"
341
344
 
342
345
  # ==============================================================================
343
346
  # CLI helpers
@@ -346,7 +349,7 @@ export swarm = (opts = {}) ->
346
349
  # flags that swarm consumes (with value)
347
350
  _flagsWithValue = ['-w', '--workers', '-b', '--bar', '-c', '--char']
348
351
  # flags that swarm consumes (standalone)
349
- _flagsAlone = ['-r', '--reset', '-s', '--safe', '-h', '--help', '-v', '--version']
352
+ _flagsAlone = ['-r', '--reset', '-s', '--safe', '-q', '--quiet', '-h', '--help', '-v', '--version']
350
353
 
351
354
  findArg = (args, short, long) ->
352
355
  for arg, i in args