@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 +45 -20
- package/package.json +2 -6
- package/server.rip +33 -13
- /package/{dashboard.html → server.html} +0 -0
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,
|
|
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,
|
|
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
|
|
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:<
|
|
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` |
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
|
285
|
-
|
|
286
|
-
| `
|
|
287
|
-
| `
|
|
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).
|
|
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**:
|
|
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,
|
|
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.
|
|
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
|
-
"
|
|
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',
|
|
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
|
-
|
|
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
|
|
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(
|
|
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',
|
|
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
|