@rip-lang/server 1.1.5 → 1.1.7
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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/server.rip +42 -3
package/README.md
CHANGED
|
@@ -472,7 +472,7 @@ import { ripUI } from '@rip-lang/ui/serve'
|
|
|
472
472
|
|
|
473
473
|
dir = import.meta.dir
|
|
474
474
|
|
|
475
|
-
use ripUI dir: dir, components: '
|
|
475
|
+
use ripUI dir: dir, components: 'routes', includes: ['ui'], watch: true, title: 'My App'
|
|
476
476
|
|
|
477
477
|
get '/css/*', -> @send "#{dir}/css/#{@req.path.slice(5)}"
|
|
478
478
|
|
package/package.json
CHANGED
package/server.rip
CHANGED
|
@@ -485,6 +485,8 @@ class Manager
|
|
|
485
485
|
@nextWorkerId = -1
|
|
486
486
|
@retiringIds = new Set()
|
|
487
487
|
@currentVersion = 1
|
|
488
|
+
@onFileChange = null
|
|
489
|
+
@_broadcastTimer = null
|
|
488
490
|
|
|
489
491
|
process.on 'SIGTERM', => @shutdown!
|
|
490
492
|
process.on 'SIGINT', => @shutdown!
|
|
@@ -509,28 +511,36 @@ class Manager
|
|
|
509
511
|
@currentMtime = mt
|
|
510
512
|
@isRolling = true
|
|
511
513
|
@lastRollAt = now
|
|
512
|
-
@rollingRestart
|
|
514
|
+
@rollingRestart().finally => @isRolling = false
|
|
513
515
|
, 50
|
|
514
516
|
|
|
515
|
-
# Watch files in app directory
|
|
517
|
+
# Watch files in app directory (opt-in via -w/--watch)
|
|
516
518
|
if @flags.watchGlob
|
|
517
519
|
entryFile = @flags.appEntry
|
|
518
520
|
entryBase = basename(entryFile)
|
|
519
521
|
watchExt = if @flags.watchGlob.startsWith('*.') then @flags.watchGlob.slice(1) else null
|
|
522
|
+
debounceMs = @flags.debounce or 250
|
|
520
523
|
try
|
|
521
524
|
watch @flags.appBaseDir, { recursive: true }, (event, filename) =>
|
|
522
525
|
return unless filename
|
|
523
|
-
# Match by extension (e.g., *.rip) or exact glob
|
|
524
526
|
if watchExt
|
|
525
527
|
return unless filename.endsWith(watchExt)
|
|
526
528
|
else
|
|
527
529
|
return unless filename is @flags.watchGlob or filename.endsWith("/#{@flags.watchGlob}")
|
|
528
530
|
return if filename is entryBase or filename.endsWith("/#{entryBase}")
|
|
531
|
+
# Touch entry file to trigger rolling restart
|
|
529
532
|
try
|
|
530
533
|
now = new Date()
|
|
531
534
|
utimesSync(entryFile, now, now)
|
|
532
535
|
catch
|
|
533
536
|
null
|
|
537
|
+
# Debounced SSE broadcast to connected clients
|
|
538
|
+
if @onFileChange
|
|
539
|
+
clearTimeout(@_broadcastTimer) if @_broadcastTimer
|
|
540
|
+
@_broadcastTimer = setTimeout =>
|
|
541
|
+
@_broadcastTimer = null
|
|
542
|
+
@onFileChange()
|
|
543
|
+
, debounceMs
|
|
534
544
|
catch e
|
|
535
545
|
console.warn "rip-server: directory watch failed: #{e.message}"
|
|
536
546
|
|
|
@@ -673,6 +683,7 @@ class Server
|
|
|
673
683
|
@httpsActive = false
|
|
674
684
|
@hostRegistry = new Set(['localhost', '127.0.0.1', 'rip.local'])
|
|
675
685
|
@mdnsProcesses = new Map()
|
|
686
|
+
@sseClients = new Set()
|
|
676
687
|
try
|
|
677
688
|
pkg = JSON.parse(readFileSync(import.meta.dir + '/package.json', 'utf8'))
|
|
678
689
|
@serverVersion = pkg.version
|
|
@@ -762,6 +773,7 @@ class Server
|
|
|
762
773
|
return new Response Bun.file(import.meta.dir + '/server.html')
|
|
763
774
|
|
|
764
775
|
return @status() if url.pathname is '/status'
|
|
776
|
+
return @watch() if url.pathname is '/watch'
|
|
765
777
|
|
|
766
778
|
if url.pathname is '/server'
|
|
767
779
|
headers = new Headers({ 'content-type': 'text/plain' })
|
|
@@ -806,6 +818,32 @@ class Server
|
|
|
806
818
|
@maybeAddSecurityHeaders(headers)
|
|
807
819
|
new Response(body, { headers })
|
|
808
820
|
|
|
821
|
+
watch: ->
|
|
822
|
+
encoder = new TextEncoder()
|
|
823
|
+
client = null
|
|
824
|
+
new Response new ReadableStream(
|
|
825
|
+
start: (controller) =>
|
|
826
|
+
send = (event) ->
|
|
827
|
+
try controller.enqueue encoder.encode("event: #{event}\n\n")
|
|
828
|
+
catch then null
|
|
829
|
+
client = { send }
|
|
830
|
+
@sseClients.add(client)
|
|
831
|
+
send('connected')
|
|
832
|
+
cancel: =>
|
|
833
|
+
@sseClients.delete(client) if client
|
|
834
|
+
),
|
|
835
|
+
headers:
|
|
836
|
+
'Content-Type': 'text/event-stream'
|
|
837
|
+
'Cache-Control': 'no-cache'
|
|
838
|
+
'Connection': 'keep-alive'
|
|
839
|
+
|
|
840
|
+
broadcastChange: ->
|
|
841
|
+
dead = []
|
|
842
|
+
for client as @sseClients
|
|
843
|
+
try client.send('reload')
|
|
844
|
+
catch then dead.push(client)
|
|
845
|
+
@sseClients.delete(c) for c in dead
|
|
846
|
+
|
|
809
847
|
getNextAvailableSocket: ->
|
|
810
848
|
while @availableWorkers.length > 0
|
|
811
849
|
worker = @availableWorkers.pop()
|
|
@@ -1176,6 +1214,7 @@ main = ->
|
|
|
1176
1214
|
|
|
1177
1215
|
svr = new Server(flags)
|
|
1178
1216
|
mgr = new Manager(flags)
|
|
1217
|
+
mgr.onFileChange = -> svr.broadcastChange()
|
|
1179
1218
|
|
|
1180
1219
|
cleanup = ->
|
|
1181
1220
|
console.log 'rip-server: shutting down...'
|