@rip-lang/server 0.5.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 +336 -0
- package/bin/rip-server +17 -0
- package/dashboard.html +410 -0
- package/docs/logo.png +0 -0
- package/docs/logo.svg +13 -0
- package/docs/social.png +0 -0
- package/package.json +51 -0
- package/server.rip +1082 -0
package/README.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
<img src="https://raw.githubusercontent.com/shreeve/rip-lang/main/docs/rip.svg" style="width:50px" /> <br>
|
|
2
|
+
|
|
3
|
+
# Rip Server - @rip-lang/server
|
|
4
|
+
|
|
5
|
+
> **Pure Rip application server — multi-worker, hot reload, HTTPS, mDNS**
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
`@rip-lang/server` is a production-grade application server written entirely in Rip. It provides multi-worker process management, hot module reloading, automatic HTTPS, and mDNS service discovery — all in a single ~1,100 line file.
|
|
10
|
+
|
|
11
|
+
- **`server.rip`** (~1,082 lines) — Complete server: CLI, workers, load balancing, TLS, mDNS
|
|
12
|
+
|
|
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
|
+
|
|
15
|
+
### Key Features
|
|
16
|
+
|
|
17
|
+
- **Multi-Worker Architecture** — Automatic worker spawning based on CPU cores
|
|
18
|
+
- **Hot Module Reloading** — File-watch based reloading in development
|
|
19
|
+
- **Rolling Restarts** — Zero-downtime deployments
|
|
20
|
+
- **Automatic HTTPS** — TLS with mkcert or self-signed certificates
|
|
21
|
+
- **mDNS Discovery** — `.local` hostname advertisement
|
|
22
|
+
- **Request Queue** — Built-in request buffering and load balancing
|
|
23
|
+
- **CLI Interface** — Simple command-line operation
|
|
24
|
+
|
|
25
|
+
> **See Also**: For the API framework, see [@rip-lang/api](../api/README.md).
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Local (per-project)
|
|
33
|
+
bun add @rip-lang/server
|
|
34
|
+
|
|
35
|
+
# Global (use rip-server from anywhere)
|
|
36
|
+
bun add -g rip-lang @rip-lang/server
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Running Your App
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Basic usage (HTTPS on port 443 or fallback)
|
|
43
|
+
rip-server ./app.rip
|
|
44
|
+
|
|
45
|
+
# HTTP only mode
|
|
46
|
+
rip-server http ./app.rip
|
|
47
|
+
|
|
48
|
+
# With mDNS alias
|
|
49
|
+
rip-server ./app.rip@myapp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Example App
|
|
53
|
+
|
|
54
|
+
Create `app.rip`:
|
|
55
|
+
|
|
56
|
+
```coffee
|
|
57
|
+
import { get, post, read, startHandler } from '@rip-lang/api'
|
|
58
|
+
|
|
59
|
+
get '/', ->
|
|
60
|
+
'Hello from Rip Server!'
|
|
61
|
+
|
|
62
|
+
get '/json', ->
|
|
63
|
+
{ message: 'It works!', timestamp: Date.now() }
|
|
64
|
+
|
|
65
|
+
get '/users/:id', ->
|
|
66
|
+
id = read 'id', 'id!'
|
|
67
|
+
{ user: { id, name: "User #{id}" } }
|
|
68
|
+
|
|
69
|
+
# Export the handler for rip-server
|
|
70
|
+
export default startHandler()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Run it:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
rip-server http app.rip
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Test it:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
curl http://localhost/
|
|
83
|
+
# Hello from Rip Server!
|
|
84
|
+
|
|
85
|
+
curl http://localhost/json
|
|
86
|
+
# {"message":"It works!","timestamp":1234567890}
|
|
87
|
+
|
|
88
|
+
curl http://localhost/users/42
|
|
89
|
+
# {"user":{"id":42,"name":"User 42"}}
|
|
90
|
+
|
|
91
|
+
curl http://localhost/status
|
|
92
|
+
# {"status":"healthy","app":"myapp","workers":5,"ports":{"http":80}}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## CLI Reference
|
|
96
|
+
|
|
97
|
+
### Basic Syntax
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
rip-server [flags] <app-path>[@alias1,alias2,...]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Flags
|
|
104
|
+
|
|
105
|
+
| Flag | Description | Default |
|
|
106
|
+
|------|-------------|---------|
|
|
107
|
+
| `http` | HTTP-only mode (no HTTPS) | HTTPS enabled |
|
|
108
|
+
| `https` | HTTPS mode (explicit) | Auto |
|
|
109
|
+
| `http:<port>` | Set HTTP port | 80 or 5700 |
|
|
110
|
+
| `https:<port>` | Set HTTPS port | 443 or 5700 |
|
|
111
|
+
| `w:<n>` | Worker count (`auto`, `half`, `2x`, `3x`, or number) | `half` of cores |
|
|
112
|
+
| `r:<policy>` | Restart policy (e.g., `1000,3600s,10r`) | `10000,3600s,10r` |
|
|
113
|
+
| `--cert=<path>` | TLS certificate path | Auto-generated |
|
|
114
|
+
| `--key=<path>` | TLS private key path | Auto-generated |
|
|
115
|
+
| `--auto-tls` | Use mkcert for TLS | Fallback to self-signed |
|
|
116
|
+
| `--hsts` | Enable HSTS headers | Disabled |
|
|
117
|
+
| `--no-redirect-http` | Don't redirect HTTP to HTTPS | Redirects enabled |
|
|
118
|
+
| `--reload=<mode>` | Reload mode: `none`, `process`, `module` | `process` |
|
|
119
|
+
| `--json-logging` | Output JSON access logs | Human-readable |
|
|
120
|
+
| `--no-access-log` | Disable access logging | Enabled |
|
|
121
|
+
|
|
122
|
+
### Subcommands
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Stop running server
|
|
126
|
+
rip-server stop
|
|
127
|
+
|
|
128
|
+
# List registered hosts
|
|
129
|
+
rip-server list
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Examples
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Development: HTTP on any available port
|
|
136
|
+
rip-server http app.rip
|
|
137
|
+
|
|
138
|
+
# Development: HTTPS with mkcert
|
|
139
|
+
rip-server --auto-tls app.rip
|
|
140
|
+
|
|
141
|
+
# Production: 8 workers, HTTPS
|
|
142
|
+
rip-server w:8 https app.rip
|
|
143
|
+
|
|
144
|
+
# Custom ports
|
|
145
|
+
rip-server http:3000 app.rip
|
|
146
|
+
|
|
147
|
+
# With mDNS aliases (accessible as myapp.local and api.local)
|
|
148
|
+
rip-server app.rip@myapp,api
|
|
149
|
+
|
|
150
|
+
# Restart after 5000 requests or 1 hour
|
|
151
|
+
rip-server r:5000,3600s app.rip
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Architecture
|
|
155
|
+
|
|
156
|
+
### Self-Spawning Design
|
|
157
|
+
|
|
158
|
+
The server uses a single-file, self-spawning architecture:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
┌─────────────────────────────────────────────────────────┐
|
|
162
|
+
│ Main Process │
|
|
163
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
|
|
164
|
+
│ │ Server │ │ Manager │ │ Control Socket │ │
|
|
165
|
+
│ │ (HTTP/HTTPS)│ │ (Workers) │ │ (Commands) │ │
|
|
166
|
+
│ └──────┬──────┘ └──────┬──────┘ └────────┬────────┘ │
|
|
167
|
+
└─────────┼────────────────┼──────────────────┼───────────┘
|
|
168
|
+
│ │ │
|
|
169
|
+
▼ ▼ │
|
|
170
|
+
┌──────────┐ ┌──────────────┐ │
|
|
171
|
+
│ Requests │ │ Spawn/Monitor│ │
|
|
172
|
+
└────┬─────┘ └──────┬───────┘ │
|
|
173
|
+
│ │ │
|
|
174
|
+
▼ ▼ │
|
|
175
|
+
┌─────────────────────────────────────────────│───┐
|
|
176
|
+
│ Worker Processes │ │
|
|
177
|
+
│ ┌────────┐ ┌────────┐ ┌────────┐ │ │
|
|
178
|
+
│ │Worker 0│ │Worker 1│ │Worker N│ ◄────────┘ │
|
|
179
|
+
│ │(Unix) │ │(Unix) │ │(Unix) │ │
|
|
180
|
+
│ └────────┘ └────────┘ └────────┘ │
|
|
181
|
+
└─────────────────────────────────────────────────┘
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
When `RIP_WORKER_MODE=1` is set, the same `server.rip` file runs as a worker instead of the main server.
|
|
185
|
+
|
|
186
|
+
### Request Flow
|
|
187
|
+
|
|
188
|
+
1. **Main Process** receives HTTP/HTTPS request
|
|
189
|
+
2. **Server** selects available worker from pool
|
|
190
|
+
3. **Request** forwarded via Unix socket
|
|
191
|
+
4. **Worker** processes request, returns response
|
|
192
|
+
5. **Server** forwards response to client
|
|
193
|
+
|
|
194
|
+
### Hot Reloading
|
|
195
|
+
|
|
196
|
+
Two modes available:
|
|
197
|
+
|
|
198
|
+
- **`process` mode** (default): File changes trigger rolling restart of all workers
|
|
199
|
+
- **`module` mode**: Each worker reloads the app module on file change (faster, but uses more memory)
|
|
200
|
+
|
|
201
|
+
### Worker Lifecycle
|
|
202
|
+
|
|
203
|
+
Workers are recycled based on configurable limits:
|
|
204
|
+
|
|
205
|
+
- **maxRequests**: Restart after N requests (default: 10,000)
|
|
206
|
+
- **maxSeconds**: Restart after N seconds (default: 3,600)
|
|
207
|
+
- **maxReloads**: Maximum hot reloads before restart (default: 10)
|
|
208
|
+
|
|
209
|
+
## Built-in Endpoints
|
|
210
|
+
|
|
211
|
+
The server provides these endpoints automatically:
|
|
212
|
+
|
|
213
|
+
| Endpoint | Description |
|
|
214
|
+
|----------|-------------|
|
|
215
|
+
| `/status` | Health check with worker count and uptime |
|
|
216
|
+
| `/server` | Simple "ok" response for load balancer probes |
|
|
217
|
+
|
|
218
|
+
## TLS Certificates
|
|
219
|
+
|
|
220
|
+
### Automatic Certificate Generation
|
|
221
|
+
|
|
222
|
+
When HTTPS is enabled without explicit certificates, the server will:
|
|
223
|
+
|
|
224
|
+
1. Try **mkcert** (if installed and `--auto-tls` flag used)
|
|
225
|
+
2. Fall back to **self-signed** certificate via OpenSSL
|
|
226
|
+
|
|
227
|
+
Certificates are stored in `~/.rip/certs/`.
|
|
228
|
+
|
|
229
|
+
### Custom Certificates
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
rip-server --cert=/path/to/cert.pem --key=/path/to/key.pem app.rip
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## mDNS Service Discovery
|
|
236
|
+
|
|
237
|
+
The server automatically advertises itself via mDNS (Bonjour/Zeroconf):
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# App accessible at myapp.local
|
|
241
|
+
rip-server app.rip@myapp
|
|
242
|
+
|
|
243
|
+
# Multiple aliases
|
|
244
|
+
rip-server app.rip@myapp,api,backend
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Requires `dns-sd` (available on macOS by default).
|
|
248
|
+
|
|
249
|
+
## App Requirements
|
|
250
|
+
|
|
251
|
+
Your app must export a fetch handler. Two patterns are supported:
|
|
252
|
+
|
|
253
|
+
### Pattern 1: Export `startHandler()` (Recommended)
|
|
254
|
+
|
|
255
|
+
```coffee
|
|
256
|
+
import { get, startHandler } from '@rip-lang/api'
|
|
257
|
+
|
|
258
|
+
get '/', -> 'Hello!'
|
|
259
|
+
|
|
260
|
+
export default startHandler()
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Pattern 2: Export fetch function directly
|
|
264
|
+
|
|
265
|
+
```coffee
|
|
266
|
+
export default (req) ->
|
|
267
|
+
new Response('Hello!')
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Pattern 3: Export object with fetch method
|
|
271
|
+
|
|
272
|
+
```coffee
|
|
273
|
+
export default
|
|
274
|
+
fetch: (req) -> new Response('Hello!')
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Environment Variables
|
|
278
|
+
|
|
279
|
+
| Variable | Description |
|
|
280
|
+
|----------|-------------|
|
|
281
|
+
| `RIP_WORKER_MODE` | Set by server when spawning workers |
|
|
282
|
+
| `RIP_DEBUG` | Enable debug logging |
|
|
283
|
+
| `RIP_MAX_REQUESTS` | Default max requests per worker |
|
|
284
|
+
| `RIP_MAX_SECONDS` | Default max seconds per worker |
|
|
285
|
+
| `RIP_MAX_RELOADS` | Default max reloads per worker |
|
|
286
|
+
| `RIP_MAX_QUEUE` | Maximum request queue size |
|
|
287
|
+
| `RIP_QUEUE_TIMEOUT_MS` | Queue timeout in milliseconds |
|
|
288
|
+
| `RIP_RELOAD` | Reload mode override |
|
|
289
|
+
|
|
290
|
+
## Dashboard
|
|
291
|
+
|
|
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.
|
|
293
|
+
|
|
294
|
+
## Troubleshooting
|
|
295
|
+
|
|
296
|
+
**Port 80 requires sudo**: Use `http:3000` or another high port, or run with sudo.
|
|
297
|
+
|
|
298
|
+
**mDNS not working**: Ensure `dns-sd` is available (built into macOS). On Linux, install Avahi.
|
|
299
|
+
|
|
300
|
+
**Workers keep restarting**: Check `RIP_DEBUG=1` for import errors in your app.
|
|
301
|
+
|
|
302
|
+
## Comparison with Other Servers
|
|
303
|
+
|
|
304
|
+
| Feature | rip-server | PM2 | Nginx |
|
|
305
|
+
|---------|------------|-----|-------|
|
|
306
|
+
| Pure Rip | ✅ | ❌ | ❌ |
|
|
307
|
+
| Single File | ✅ (~1,076 lines) | ❌ | ❌ |
|
|
308
|
+
| Hot Reload | ✅ | ✅ | ❌ |
|
|
309
|
+
| Multi-Worker | ✅ | ✅ | ✅ |
|
|
310
|
+
| Auto HTTPS | ✅ | ❌ | ❌ |
|
|
311
|
+
| mDNS | ✅ | ❌ | ❌ |
|
|
312
|
+
| Zero Config | ✅ | ❌ | ❌ |
|
|
313
|
+
| Built-in LB | ✅ | ❌ | ✅ |
|
|
314
|
+
|
|
315
|
+
## TODO
|
|
316
|
+
|
|
317
|
+
> *Planned improvements for future releases:*
|
|
318
|
+
|
|
319
|
+
- [ ] "Try it Now" section with clone-and-run example
|
|
320
|
+
- [ ] Dashboard screenshots and feature description
|
|
321
|
+
- [ ] Performance benchmarks (req/s, latency)
|
|
322
|
+
- [ ] Scaling guidelines
|
|
323
|
+
- [ ] More troubleshooting scenarios
|
|
324
|
+
- [ ] Static file serving
|
|
325
|
+
- [ ] Rate limiting
|
|
326
|
+
- [ ] Graceful shutdown improvements
|
|
327
|
+
|
|
328
|
+
## License
|
|
329
|
+
|
|
330
|
+
MIT
|
|
331
|
+
|
|
332
|
+
## Links
|
|
333
|
+
|
|
334
|
+
- [Rip Language](https://github.com/shreeve/rip-lang)
|
|
335
|
+
- [@rip-lang/api](../api/README.md)
|
|
336
|
+
- [Report Issues](https://github.com/shreeve/rip-lang/issues)
|
package/bin/rip-server
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
const serverRip = join(__dirname, '..', 'server.rip');
|
|
11
|
+
const args = process.argv.slice(2).join(' ');
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
execSync(`rip ${serverRip} ${args}`, { stdio: 'inherit' });
|
|
15
|
+
} catch (error) {
|
|
16
|
+
process.exit(error.status || 1);
|
|
17
|
+
}
|