@rip-lang/swarm 1.0.0 → 1.0.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/swarm.rip +37 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rip-lang/swarm",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
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
@@ -214,6 +214,7 @@ export swarm = (opts = {}) ->
214
214
  info = {}
215
215
  taskIdx = 0
216
216
  inflight = {} # slot → taskPath (track in-flight tasks for crash recovery)
217
+ lastTask = {} # slot → last completed task name (for display)
217
218
 
218
219
  # signal handlers
219
220
  process.on 'SIGINT', ->
@@ -239,7 +240,7 @@ export swarm = (opts = {}) ->
239
240
  if not finished and done + died >= jobs
240
241
  finished = true
241
242
  for wk in allWorkers
242
- try wk.postMessage { type: 'shutdown' }
243
+ try wk.unref()
243
244
  catch then null
244
245
  resolveAll()
245
246
 
@@ -269,6 +270,7 @@ export swarm = (opts = {}) ->
269
270
  dispatchNext(w, slot)
270
271
  when 'done'
271
272
  move(msg.taskPath, _done)
273
+ lastTask[slot] = msg.taskPath.split('/').pop()
272
274
  inflight[slot] = null
273
275
  live--
274
276
  done++
@@ -277,6 +279,7 @@ export swarm = (opts = {}) ->
277
279
  dispatchNext(w, slot)
278
280
  when 'failed'
279
281
  move(msg.taskPath, _died)
282
+ lastTask[slot] = msg.taskPath.split('/').pop()
280
283
  inflight[slot] = null
281
284
  live--
282
285
  died++
@@ -307,9 +310,17 @@ export swarm = (opts = {}) ->
307
310
  for slot in [1..count]
308
311
  spawnWorker(slot)
309
312
 
313
+ # final redraw — fill all worker bars and show per-worker stats
314
+ secs = (Date.now() - startTime) / 1000
315
+ for slot of info
316
+ s = parseInt(slot)
317
+ n = info[slot]
318
+ rate = if secs > 0 then (n / secs).toFixed(1) else '—'
319
+ write go(s + 1, _len + 5) + bg("5383ec") + _char.repeat(_wide) + bg() + " │ #{n} jobs @ #{rate}/sec" + clear(true)
320
+ draw({ live: 0, done, died, jobs, workers, info })
321
+
310
322
  # summary
311
323
  cursor(true)
312
- secs = (Date.now() - startTime) / 1000
313
324
  write go(workers + 5, 1)
314
325
  write "#{secs.toFixed(2)} secs"
315
326
  write " for #{jobs} jobs"
@@ -321,6 +332,11 @@ export swarm = (opts = {}) ->
321
332
  # CLI helpers
322
333
  # ==============================================================================
323
334
 
335
+ # flags that swarm consumes (with value)
336
+ _flagsWithValue = ['-w', '--workers', '-b', '--bar', '-c', '--char']
337
+ # flags that swarm consumes (standalone)
338
+ _flagsAlone = ['-r', '--reset', '-h', '--help', '-v', '--version']
339
+
324
340
  findArg = (args, short, long) ->
325
341
  for arg, i in args
326
342
  if arg is short or arg is long
@@ -330,3 +346,22 @@ findArg = (args, short, long) ->
330
346
  if arg.startsWith("#{short}=")
331
347
  return arg.split('=')[1]
332
348
  null
349
+
350
+ # return process.argv with swarm's flags stripped — only your args remain
351
+ export args = ->
352
+ result = []
353
+ list = process.argv.slice(2)
354
+ skip = false
355
+ for arg in list
356
+ if skip
357
+ skip = false
358
+ continue
359
+ if arg in _flagsWithValue
360
+ skip = true
361
+ continue
362
+ if arg in _flagsAlone
363
+ continue
364
+ if _flagsWithValue.some((f) -> arg.startsWith("#{f}="))
365
+ continue
366
+ result.push(arg)
367
+ result