@rip-lang/server 0.6.0 → 0.7.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.
package/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  ## Overview
8
8
 
9
- `@rip-lang/server` is a production-grade application server written entirely in Rip. It provides multi-worker process management, hot reloading, automatic HTTPS, and mDNS service discovery — all in a single ~1,100 line file.
9
+ `@rip-lang/server` is a production-grade application server written entirely in Rip. It provides multi-worker process management, hot reloading, automatic HTTPS, and mDNS service discovery — all in a single ~1,110 line file.
10
10
 
11
- - **`server.rip`** (~1,100 lines) — Complete server: CLI, workers, load balancing, TLS, mDNS
11
+ - **`server.rip`** (~1,110 lines) — Complete server: CLI, workers, load balancing, TLS, mDNS
12
12
 
13
13
  **Core Philosophy**: Application servers should be simple, fast, and reliable. No complex configuration files. No dependency on external process managers. Just run your app.
14
14
 
@@ -39,7 +39,7 @@ bun add -g rip-lang @rip-lang/server
39
39
  ### Running Your App
40
40
 
41
41
  ```bash
42
- # Basic usage (HTTPS on port 443 or fallback)
42
+ # Basic usage (HTTPS on port 443, or 5700 if unavailable)
43
43
  rip-server ./app.rip
44
44
 
45
45
  # HTTP only mode
@@ -104,18 +104,20 @@ rip-server [flags] <app-path>[@alias1,alias2,...]
104
104
  | Flag | Description | Default |
105
105
  |------|-------------|---------|
106
106
  | `-v`, `--version` | Show version and exit | — |
107
+ | `--env=<mode>` | Environment mode (`dev`, `prod`, `development`, `production`) | `development` |
108
+ | `--debug` | Enable debug logging | Disabled |
109
+ | `--static` | Disable hot reload (production) | Hot reload enabled |
107
110
  | `http` | HTTP-only mode (no HTTPS) | HTTPS enabled |
108
111
  | `https` | HTTPS mode (explicit) | Auto |
109
112
  | `http:<port>` | Set HTTP port | 80 or 5700 |
110
113
  | `https:<port>` | Set HTTPS port | 443 or 5700 |
111
114
  | `w:<n>` | Worker count (`auto`, `half`, `2x`, `3x`, or number) | `half` of cores |
112
- | `r:<policy>` | Restart policy (e.g., `5000,3600s`) | `10000,3600s` |
115
+ | `r:<reqs>,<secs>s` | Restart policy: requests, seconds (e.g., `5000,3600s`) | `10000,3600s` |
113
116
  | `--cert=<path>` | TLS certificate path | Auto-generated |
114
117
  | `--key=<path>` | TLS private key path | Auto-generated |
115
- | `--auto-tls` | Use mkcert for TLS | Fallback to self-signed |
118
+ | `--auto-tls` | Try mkcert first, then self-signed | Self-signed only |
116
119
  | `--hsts` | Enable HSTS headers | Disabled |
117
120
  | `--no-redirect-http` | Don't redirect HTTP to HTTPS | Redirects enabled |
118
- | `--static` | Disable hot reload (production) | Hot reload enabled |
119
121
  | `--json-logging` | Output JSON access logs | Human-readable |
120
122
  | `--no-access-log` | Disable access logging | Enabled |
121
123
 
@@ -141,8 +143,11 @@ rip-server http app.rip
141
143
  # Development: HTTPS with mkcert
142
144
  rip-server --auto-tls app.rip
143
145
 
144
- # Production: 8 workers, HTTPS
145
- rip-server w:8 https app.rip
146
+ # Production: 8 workers, HTTPS, no hot reload
147
+ rip-server --env=prod w:8 https app.rip
148
+
149
+ # Debug mode to troubleshoot issues
150
+ rip-server --debug http app.rip
146
151
 
147
152
  # Custom ports
148
153
  rip-server http:3000 app.rip
@@ -277,19 +282,39 @@ export default
277
282
 
278
283
  ## Environment Variables
279
284
 
280
- | Variable | Description |
281
- |----------|-------------|
282
- | `RIP_WORKER_MODE` | Set by server when spawning workers |
283
- | `RIP_DEBUG` | Enable debug logging |
284
- | `RIP_MAX_REQUESTS` | Default max requests per worker |
285
- | `RIP_MAX_SECONDS` | Default max seconds per worker |
286
- | `RIP_MAX_QUEUE` | Maximum request queue size |
287
- | `RIP_QUEUE_TIMEOUT_MS` | Queue timeout in milliseconds |
288
- | `RIP_STATIC` | Set to `1` to disable hot reload |
285
+ Most settings are configured via CLI flags, but environment variables provide an alternative for containers, CI/CD, or system-wide defaults.
286
+
287
+ **Essential:**
288
+
289
+ | Variable | CLI Equivalent | Default | Description |
290
+ |----------|----------------|---------|-------------|
291
+ | `NODE_ENV` | `--env=` | `development` | Environment mode (`development` or `production`) |
292
+ | `RIP_DEBUG` | `--debug` | | Enable debug logging |
293
+ | `RIP_STATIC` | `--static` | `0` | Set to `1` to disable hot reload |
294
+
295
+ **Advanced (rarely needed):**
296
+
297
+ | Variable | CLI Equivalent | Default | Description |
298
+ |----------|----------------|---------|-------------|
299
+ | `RIP_MAX_REQUESTS` | `r:N,...` | `10000` | Max requests before worker recycle |
300
+ | `RIP_MAX_SECONDS` | `r:...,Ns` | `3600` | Max seconds before worker recycle |
301
+ | `RIP_MAX_QUEUE` | `--max-queue=` | `512` | Request queue limit |
302
+ | `RIP_QUEUE_TIMEOUT_MS` | `--queue-timeout-ms=` | `2000` | Queue wait timeout (ms) |
303
+ | `RIP_CONNECT_TIMEOUT_MS` | `--connect-timeout-ms=` | `200` | Worker connect timeout (ms) |
304
+ | `RIP_READ_TIMEOUT_MS` | `--read-timeout-ms=` | `5000` | Worker read timeout (ms) |
289
305
 
290
306
  ## Dashboard
291
307
 
292
- The server includes a built-in dashboard accessible at `http://rip.local/` (when mDNS is active). The dashboard shows server status, worker count, and registered hosts.
308
+ The server includes a built-in dashboard accessible at `http://rip.local/` (when mDNS is active). This is a **meta-UI for the server itself**, not your application.
309
+
310
+ **Dashboard Features:**
311
+
312
+ - **Server Status** — Health status and uptime
313
+ - **Worker Overview** — Active worker count
314
+ - **Registered Hosts** — All mDNS aliases being advertised
315
+ - **Server Ports** — HTTP/HTTPS port configuration
316
+
317
+ The dashboard uses the same mDNS infrastructure as your app, so it's always available at `rip.local` when any rip-server instance is running.
293
318
 
294
319
  ## Troubleshooting
295
320
 
@@ -297,14 +322,14 @@ The server includes a built-in dashboard accessible at `http://rip.local/` (when
297
322
 
298
323
  **mDNS not working**: Ensure `dns-sd` is available (built into macOS). On Linux, install Avahi.
299
324
 
300
- **Workers keep restarting**: Check `RIP_DEBUG=1` for import errors in your app.
325
+ **Workers keep restarting**: Use `--debug` (or `RIP_DEBUG=1`) to see import errors in your app.
301
326
 
302
327
  ## Comparison with Other Servers
303
328
 
304
329
  | Feature | rip-server | PM2 | Nginx |
305
330
  |---------|------------|-----|-------|
306
331
  | Pure Rip | ✅ | ❌ | ❌ |
307
- | Single File | ✅ (~1,100 lines) | ❌ | ❌ |
332
+ | Single File | ✅ (~1,110 lines) | ❌ | ❌ |
308
333
  | Hot Reload | ✅ | ✅ | ❌ |
309
334
  | Multi-Worker | ✅ | ✅ | ✅ |
310
335
  | Auto HTTPS | ✅ | ❌ | ❌ |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rip-lang/server",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Pure Rip application server — multi-worker, hot reload, HTTPS, mDNS",
5
5
  "type": "module",
6
6
  "main": "server.rip",
@@ -37,14 +37,10 @@
37
37
  },
38
38
  "author": "Steve Shreeve <steve.shreeve@gmail.com>",
39
39
  "license": "MIT",
40
- "peerDependencies": {
41
- "rip-lang": "^2.0.0",
42
- "@rip-lang/api": "^0.5.0"
43
- },
44
40
  "files": [
45
41
  "bin/",
46
42
  "server.rip",
47
- "dashboard.html",
43
+ "server.html",
48
44
  "docs/",
49
45
  "README.md"
50
46
  ]
package/server.rip CHANGED
@@ -43,10 +43,16 @@ coerceInt = (value, fallback) ->
43
43
  n = parseInt(String(value))
44
44
  if Number.isFinite(n) then n else fallback
45
45
 
46
+ # Environment detection (can be overridden by --env flag)
47
+ _envOverride = null
48
+ _debugMode = false
49
+
46
50
  isDev = ->
47
- env = (process.env.NODE_ENV or '').toLowerCase()
51
+ env = (_envOverride or process.env.NODE_ENV or '').toLowerCase()
48
52
  env in ['development', 'dev', '']
49
53
 
54
+ isDebug = -> _debugMode or process.env.RIP_DEBUG?
55
+
50
56
  formatTimestamp = ->
51
57
  now = new Date()
52
58
  pad = (n, w = 2) -> String(n).padStart(w, '0')
@@ -263,6 +269,18 @@ parseFlags = (argv) ->
263
269
 
264
270
  reload = not (has('--static') or process.env.RIP_STATIC is '1')
265
271
 
272
+ # Environment mode (--env=production, --env=dev, etc.)
273
+ envValue = getKV('--env=')
274
+ if envValue
275
+ normalized = envValue.toLowerCase()
276
+ _envOverride = switch normalized
277
+ when 'prod', 'production' then 'production'
278
+ when 'dev', 'development' then 'development'
279
+ else normalized
280
+
281
+ # Debug mode
282
+ _debugMode = has('--debug')
283
+
266
284
  httpsPort = do ->
267
285
  kv = getKV('--https-port=')
268
286
  return coerceInt(kv, 443) if kv?
@@ -480,7 +498,7 @@ class Manager
480
498
  RIP_LOG_JSON: if @flags.jsonLogging then '1' else '0'
481
499
  RIP_VERSION: String(version or @currentVersion)
482
500
 
483
- proc = Bun.spawn ['rip', __filename],
501
+ proc = Bun.spawn ['rip', import.meta.path],
484
502
  stdout: 'inherit'
485
503
  stderr: 'inherit'
486
504
  stdin: 'ignore'
@@ -557,6 +575,15 @@ class Manager
557
575
 
558
576
  Promise.all!(pairs.map(({ old }) -> old.process.exited.catch(-> null)))
559
577
 
578
+ # Notify server to remove old workers' socket entries
579
+ ctl = getControlSocketPath(@flags.socketPrefix)
580
+ for { old } in pairs
581
+ try
582
+ body = JSON.stringify({ op: 'quit', workerId: old.id })
583
+ fetch!('http://localhost/worker', { method: 'POST', body, headers: { 'content-type': 'application/json' }, unix: ctl }).catch(-> null)
584
+ catch
585
+ null
586
+
560
587
  retiring = new Set(pairs.map((p) -> p.old.id))
561
588
  @workers = @workers.filter((w) -> not retiring.has(w.id))
562
589
  @retiringIds.delete(id) for id from retiring
@@ -668,9 +695,7 @@ class Server
668
695
 
669
696
  # Dashboard for rip.local
670
697
  if host is 'rip.local' and url.pathname in ['/', '']
671
- headers = new Headers({ 'content-type': 'text/html; charset=utf-8' })
672
- @maybeAddSecurityHeaders(headers)
673
- return new Response(@getDashboardHTML(), { headers })
698
+ return new Response Bun.file(import.meta.dir + '/server.html')
674
699
 
675
700
  return @status() if url.pathname is '/status'
676
701
 
@@ -771,7 +796,7 @@ class Server
771
796
  @releaseWorker(retry)
772
797
  return @buildResponse(res, req, start, workerSeconds)
773
798
  catch err
774
- console.error "[server] forwardToWorker error:", err.message or err if process.env.RIP_DEBUG
799
+ console.error "[server] forwardToWorker error:", err.message or err if isDebug()
775
800
  @sockets = @sockets.filter((x) -> x.socket isnt socket.socket)
776
801
  @availableWorkers = @availableWorkers.filter((x) -> x.socket isnt socket.socket)
777
802
  released = true
@@ -969,11 +994,6 @@ class Server
969
994
  catch e
970
995
  console.error "rip-server: failed to advertise #{host} via mDNS:", e.message
971
996
 
972
- getDashboardHTML: ->
973
- try
974
- readFileSync(join(__dirname, 'dashboard.html'), 'utf8')
975
- catch
976
- '<!DOCTYPE html><html><body><h1>Rip Server</h1><p>Dashboard not found</p></body></html>'
977
997
 
978
998
  # ==============================================================================
979
999
  # Main Entry
@@ -983,7 +1003,7 @@ main = ->
983
1003
  # Version flag
984
1004
  if '--version' in process.argv or '-v' in process.argv
985
1005
  try
986
- pkg = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'))
1006
+ pkg = JSON.parse(readFileSync(import.meta.dir + '/package.json', 'utf8'))
987
1007
  console.log "rip-server v#{pkg.version}"
988
1008
  catch
989
1009
  console.log 'rip-server (version unknown)'
@@ -1029,7 +1049,7 @@ main = ->
1029
1049
  console.log "rip-server: sent SIGTERM to process #{pid}"
1030
1050
  else
1031
1051
  console.log "rip-server: no PID file found at #{pidFile}, trying pkill..."
1032
- Bun.spawnSync(['pkill', '-f', __filename])
1052
+ Bun.spawnSync(['pkill', '-f', import.meta.path])
1033
1053
  catch e
1034
1054
  console.error "rip-server: stop failed: #{e.message}"
1035
1055
  return
File without changes