@rip-lang/server 1.3.98 → 1.3.100
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 +292 -25
- package/docs/edge/CONFIG_LIFECYCLE.md +73 -0
- package/docs/edge/CONTRACTS.md +146 -0
- package/docs/edge/EDGEFILE_CONTRACT.md +53 -0
- package/docs/edge/M0B_REVIEW_NOTES.md +102 -0
- package/docs/edge/SCHEDULER.md +46 -0
- package/middleware.rip +6 -4
- package/package.json +2 -2
- package/server.rip +469 -636
- package/tests/acme.rip +124 -0
- package/tests/helpers.rip +90 -0
- package/tests/metrics.rip +73 -0
- package/tests/proxy.rip +99 -0
- package/tests/{read.test.rip → read.rip} +10 -11
- package/tests/realtime.rip +147 -0
- package/tests/registry.rip +125 -0
- package/tests/security.rip +95 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# M0b Review Notes
|
|
2
|
+
|
|
3
|
+
Status: Active
|
|
4
|
+
|
|
5
|
+
## Accepted decisions
|
|
6
|
+
|
|
7
|
+
- Single project architecture with two runtime roles:
|
|
8
|
+
- EdgeControlPlane
|
|
9
|
+
- AppDataPlane
|
|
10
|
+
- Direct request path remains `client -> ingress -> worker` (no relay hop).
|
|
11
|
+
- v1 scheduler: least-inflight with deterministic tie-break.
|
|
12
|
+
- Config apply lifecycle is atomic (`parse -> validate -> normalize -> stage -> activate/rollback`).
|
|
13
|
+
- Deterministic config evaluation (no async/network I/O).
|
|
14
|
+
- v1 TLS posture reflects M0a findings:
|
|
15
|
+
- HTTP-01 prioritized for reliable v1 ACME
|
|
16
|
+
- TLS-ALPN-01 treated as beta/deferred
|
|
17
|
+
- graceful restart cert activation path
|
|
18
|
+
|
|
19
|
+
## Deferred / future decisions
|
|
20
|
+
|
|
21
|
+
- Dynamic SNI cert selection when Bun API supports it reliably
|
|
22
|
+
- In-process cert hot reload without restart
|
|
23
|
+
- DNS-01 provider integrations
|
|
24
|
+
- HTTP/2 and gRPC production posture
|
|
25
|
+
|
|
26
|
+
## Open items for next review pass
|
|
27
|
+
|
|
28
|
+
- Confirm `Edgefile.rip` syntax shape against current Rip parser
|
|
29
|
+
- Validate route precedence edge cases in conformance tests
|
|
30
|
+
- Confirm ops defaults for retention policy in deployment docs
|
|
31
|
+
|
|
32
|
+
## Refactor Guardrails
|
|
33
|
+
|
|
34
|
+
Group by theme, not by function. A file should cover a coherent area — not
|
|
35
|
+
necessarily one function or one class. Pragmatism over purity.
|
|
36
|
+
|
|
37
|
+
- Group related functions into one file by theme (e.g. all CLI logic in `cli.rip`).
|
|
38
|
+
- No generic `utils.rip` dumping ground — but themed helpers files are fine.
|
|
39
|
+
- If a module is under ~20 lines with only one importer, fold it into its neighbor.
|
|
40
|
+
- If a module grows past ~300 lines, consider splitting by sub-theme.
|
|
41
|
+
- Keep import fan-out low; if a module imports too many siblings, the boundary is wrong.
|
|
42
|
+
- `server.rip` should stay as orchestration/wiring, not business logic.
|
|
43
|
+
- Every extraction or consolidation must pass tests.
|
|
44
|
+
- Prefer vertical domains:
|
|
45
|
+
- `edge/*` for request-path behavior (forwarding, scheduling, status, registry)
|
|
46
|
+
- `control/*` for management (CLI, lifecycle, workers, watchers, mDNS)
|
|
47
|
+
- `acme/*` only when ACME implementation begins
|
|
48
|
+
|
|
49
|
+
## Coding Conventions
|
|
50
|
+
|
|
51
|
+
### Bare `try` over `try ... catch then null`
|
|
52
|
+
|
|
53
|
+
In Rip, a bare `try` compiles to `try {} catch {}` in JavaScript — functionally
|
|
54
|
+
identical to `try ... catch then null`, which compiles to `try {} catch { null; }`.
|
|
55
|
+
The extra `null;` is a no-op statement with no behavioral difference.
|
|
56
|
+
|
|
57
|
+
**Preferred style:** use bare `try` for fire-and-forget error suppression.
|
|
58
|
+
|
|
59
|
+
```coffee
|
|
60
|
+
# Good — clean, concise
|
|
61
|
+
try unlinkSync(path)
|
|
62
|
+
try server.stop()
|
|
63
|
+
|
|
64
|
+
# Good — multiline bare try
|
|
65
|
+
try
|
|
66
|
+
proc.kill()
|
|
67
|
+
console.log "stopped #{host}"
|
|
68
|
+
|
|
69
|
+
# Avoid — unnecessary catch block
|
|
70
|
+
try unlinkSync(path) catch then null
|
|
71
|
+
|
|
72
|
+
# Avoid — verbose no-op catch
|
|
73
|
+
try
|
|
74
|
+
proc.kill()
|
|
75
|
+
catch
|
|
76
|
+
null
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**When to use an explicit catch:**
|
|
80
|
+
- When the catch body does actual work (logging, fallback value, cleanup)
|
|
81
|
+
- When catching a specific error variable: `catch e`
|
|
82
|
+
- When fall-through after try needs a specific return value that differs from `undefined`
|
|
83
|
+
|
|
84
|
+
```coffee
|
|
85
|
+
# Good — catch does real work
|
|
86
|
+
try
|
|
87
|
+
pkg = JSON.parse(readFileSync(path, 'utf8'))
|
|
88
|
+
catch
|
|
89
|
+
console.log 'version unknown'
|
|
90
|
+
|
|
91
|
+
# Good — catch with error variable
|
|
92
|
+
catch e
|
|
93
|
+
console.error "failed: #{e.message}"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Evidence links
|
|
97
|
+
|
|
98
|
+
- TLS spikes and outputs:
|
|
99
|
+
- `packages/server/spikes/tls/README.md`
|
|
100
|
+
- `packages/server/spikes/tls/FINDINGS.md`
|
|
101
|
+
- Plan of record:
|
|
102
|
+
- `.cursor/plans/rip_unified_master_plan_v2_2892e891.plan.md`
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Scheduler Policy (M0b)
|
|
2
|
+
|
|
3
|
+
This document freezes v1 scheduler behavior.
|
|
4
|
+
|
|
5
|
+
## Algorithm
|
|
6
|
+
|
|
7
|
+
v1 uses **least-inflight** per app pool.
|
|
8
|
+
|
|
9
|
+
Selection order:
|
|
10
|
+
1. filter to workers in `ready` state
|
|
11
|
+
2. choose worker with lowest `inflight`
|
|
12
|
+
3. tie-break by lowest `workerId`
|
|
13
|
+
|
|
14
|
+
## Fallback behavior
|
|
15
|
+
|
|
16
|
+
- If all workers are saturated and queue depth `< maxQueue`: enqueue
|
|
17
|
+
- If queue depth `>= maxQueue`: return `503` with `Retry-After`
|
|
18
|
+
|
|
19
|
+
## Fairness expectations
|
|
20
|
+
|
|
21
|
+
- No worker starvation under steady load
|
|
22
|
+
- No worker starvation under burst load
|
|
23
|
+
- Workload spread should converge over time for equal worker capacity
|
|
24
|
+
|
|
25
|
+
## Retry policy interaction
|
|
26
|
+
|
|
27
|
+
- Retry only idempotent requests by default
|
|
28
|
+
- Never retry after partial upstream response
|
|
29
|
+
- Retry decisions occur after scheduler selection and only on retry-eligible failures
|
|
30
|
+
|
|
31
|
+
## Metrics emitted
|
|
32
|
+
|
|
33
|
+
- per-app scheduler decisions
|
|
34
|
+
- per-worker inflight counts
|
|
35
|
+
- queue depth and queue wait duration
|
|
36
|
+
- shed count (`503` due to capacity)
|
|
37
|
+
|
|
38
|
+
## Change criteria (v2)
|
|
39
|
+
|
|
40
|
+
Consider upgrading algorithm if:
|
|
41
|
+
- tail latency variance between workers > 3x for sustained periods
|
|
42
|
+
- workload variance causes persistent imbalance despite least-inflight
|
|
43
|
+
|
|
44
|
+
Candidate future algorithms:
|
|
45
|
+
- EWMA latency-weighted
|
|
46
|
+
- power-of-two choices
|
package/middleware.rip
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
import { get, enableCorsPreflight } from '@rip-lang/server'
|
|
37
37
|
import { fileURLToPath } from 'node:url'
|
|
38
38
|
import { dirname, resolve } from 'node:path'
|
|
39
|
-
import { existsSync } from 'node:fs'
|
|
39
|
+
import { existsSync, realpathSync } from 'node:fs'
|
|
40
40
|
|
|
41
41
|
export cors = (opts = {}) ->
|
|
42
42
|
origin = opts.origin or '*'
|
|
@@ -525,12 +525,14 @@ export serve = (opts = {}) ->
|
|
|
525
525
|
serve._registered = true
|
|
526
526
|
|
|
527
527
|
# Route: {prefix}/*.rip — serve .rip files from the app directory tree
|
|
528
|
-
appDirResolved = resolve(appDir)
|
|
528
|
+
appDirResolved = realpathSync(resolve(appDir))
|
|
529
529
|
get "#{prefix}/*.rip" ->
|
|
530
530
|
name = @req.path.slice("#{prefix}/".length)
|
|
531
531
|
path = resolve(appDir, name)
|
|
532
|
-
return unless path
|
|
533
|
-
|
|
532
|
+
return unless existsSync(path)
|
|
533
|
+
realPath = realpathSync(path)
|
|
534
|
+
return unless realPath.startsWith(appDirResolved)
|
|
535
|
+
@send realPath, 'text/x-rip; charset=UTF-8'
|
|
534
536
|
|
|
535
537
|
# Routes: {prefix}/{name} — per-bundle JSON endpoints (cached + Brotli)
|
|
536
538
|
# Built once per worker on first request, then served from memory with ETag/304.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rip-lang/server",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.100",
|
|
4
4
|
"description": "Pure Rip web framework and application server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "api.rip",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"author": "Steve Shreeve <steve.shreeve@gmail.com>",
|
|
46
46
|
"license": "MIT",
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"rip-lang": ">=3.13.
|
|
48
|
+
"rip-lang": ">=3.13.110"
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"api.rip",
|