@rip-lang/server 1.3.124 → 1.3.126
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/control/cli.rip +64 -21
- package/control/workers.rip +3 -1
- package/package.json +2 -2
- package/server.rip +8 -18
- package/tests/runtime_entrypoints.rip +23 -1
package/control/cli.rip
CHANGED
|
@@ -63,6 +63,7 @@ export resolveAppEntry = (appPathInput, defaultEntryPath = null) ->
|
|
|
63
63
|
export parseFlags = (argv, defaultEntryPath = null) ->
|
|
64
64
|
rawFlags = new Set()
|
|
65
65
|
appPathInput = null
|
|
66
|
+
appNameOverride = null
|
|
66
67
|
appAliases = []
|
|
67
68
|
|
|
68
69
|
isFlag = (tok) ->
|
|
@@ -82,13 +83,17 @@ export parseFlags = (argv, defaultEntryPath = null) ->
|
|
|
82
83
|
unless appPathInput
|
|
83
84
|
if tok.includes('@') and not tok.startsWith('-')
|
|
84
85
|
[pathPart, aliasesPart] = tok.split('@')
|
|
86
|
+
aliases = aliasesPart.split(',').map((a) -> a.trim()).filter((a) -> a)
|
|
85
87
|
maybe = tryResolveApp(pathPart)
|
|
86
88
|
if maybe
|
|
87
89
|
appPathInput = maybe
|
|
88
|
-
|
|
90
|
+
if aliases.length > 0
|
|
91
|
+
appNameOverride = aliases[0]
|
|
92
|
+
appAliases = aliases
|
|
89
93
|
continue
|
|
90
94
|
else if not pathPart.includes('/') and not pathPart.startsWith('.')
|
|
91
|
-
|
|
95
|
+
appNameOverride = pathPart
|
|
96
|
+
appAliases = [pathPart].concat(aliases)
|
|
92
97
|
continue
|
|
93
98
|
|
|
94
99
|
maybe = tryResolveApp(tok)
|
|
@@ -97,7 +102,11 @@ export parseFlags = (argv, defaultEntryPath = null) ->
|
|
|
97
102
|
continue
|
|
98
103
|
|
|
99
104
|
unless isFlag(tok)
|
|
100
|
-
|
|
105
|
+
if appNameOverride
|
|
106
|
+
appAliases.push(tok)
|
|
107
|
+
else
|
|
108
|
+
appNameOverride = tok
|
|
109
|
+
appAliases = [tok]
|
|
101
110
|
continue
|
|
102
111
|
|
|
103
112
|
rawFlags.add(tok)
|
|
@@ -114,13 +123,18 @@ export parseFlags = (argv, defaultEntryPath = null) ->
|
|
|
114
123
|
appPathInput = cwd
|
|
115
124
|
|
|
116
125
|
getKV = (prefix) ->
|
|
117
|
-
|
|
118
|
-
|
|
126
|
+
tokens = Array.from(rawFlags)
|
|
127
|
+
bare = prefix.slice(0, -1)
|
|
128
|
+
for i in [0...tokens.length]
|
|
129
|
+
return tokens[i].slice(prefix.length) if tokens[i].startsWith(prefix)
|
|
130
|
+
if tokens[i] is bare and tokens[i + 1] and not tokens[i + 1].startsWith('-')
|
|
131
|
+
return tokens[i + 1]
|
|
119
132
|
undefined
|
|
120
133
|
|
|
121
134
|
has = (name) -> rawFlags.has(name)
|
|
122
135
|
|
|
123
136
|
{ baseDir, entryPath, appName } = resolveAppEntry(appPathInput, defaultEntryPath)
|
|
137
|
+
appName = appNameOverride if appNameOverride
|
|
124
138
|
appAliases = [appName] if appAliases.length is 0
|
|
125
139
|
|
|
126
140
|
tokens = Array.from(rawFlags)
|
|
@@ -211,8 +225,10 @@ export parseFlags = (argv, defaultEntryPath = null) ->
|
|
|
211
225
|
rateLimit: coerceInt(getKV('--rate-limit='), 0)
|
|
212
226
|
rateLimitWindow: coerceInt(getKV('--rate-limit-window='), 60000)
|
|
213
227
|
watch: watch
|
|
214
|
-
edgefilePath: getKV('--edgefile=')
|
|
215
|
-
checkConfig: has('--check-config')
|
|
228
|
+
edgefilePath: getKV('--edgefile=') or getKV('--file=') or getKV('-f=')
|
|
229
|
+
checkConfig: has('--check-config') or has('--check') or has('-c')
|
|
230
|
+
reloadConfig: has('--reload') or has('-r')
|
|
231
|
+
stopServer: has('--stop') or has('-s')
|
|
216
232
|
envOverride: envOverride
|
|
217
233
|
debug: has('--debug')
|
|
218
234
|
}
|
|
@@ -236,6 +252,16 @@ export findAppPathToken = (argv, existsSyncFn, isAbsoluteFn, resolveFn, cwdFn) -
|
|
|
236
252
|
export computeSocketPrefix = (argv, resolveAppEntryFn, existsSyncFn, isAbsoluteFn, resolveFn, cwdFn) ->
|
|
237
253
|
override = getKV(argv, '--socket-prefix=')
|
|
238
254
|
return override if override
|
|
255
|
+
for i in [2...argv.length]
|
|
256
|
+
tok = argv[i]
|
|
257
|
+
continue if tok.startsWith('-') or /^\d+$/.test(tok) or tok in ['http', 'https']
|
|
258
|
+
[pathPart, aliasesPart] = if tok.includes('@') then tok.split('@') else [tok, null]
|
|
259
|
+
aliases = aliasesPart?.split(',').map((a) -> a.trim()).filter((a) -> a) or []
|
|
260
|
+
looksLikePath = pathPart.includes('/') or pathPart.startsWith('.') or pathPart.endsWith('.rip') or pathPart.endsWith('.ts')
|
|
261
|
+
unless looksLikePath
|
|
262
|
+
return "rip_#{pathPart}"
|
|
263
|
+
return "rip_#{aliases[0]}" if aliases.length > 0
|
|
264
|
+
break
|
|
239
265
|
appTok = findAppPathToken(argv, existsSyncFn, isAbsoluteFn, resolveFn, cwdFn)
|
|
240
266
|
if appTok
|
|
241
267
|
try
|
|
@@ -260,14 +286,16 @@ export runHelpOutput = (argv) ->
|
|
|
260
286
|
rip-server - Pure Rip application server
|
|
261
287
|
|
|
262
288
|
Usage:
|
|
263
|
-
rip
|
|
264
|
-
rip
|
|
289
|
+
rip server [options] [app-path] [app-name]
|
|
290
|
+
rip server [options] [app-path]@<alias1>,<alias2>,...
|
|
265
291
|
|
|
266
292
|
Options:
|
|
267
293
|
-h, --help Show this help
|
|
268
294
|
-v, --version Show version
|
|
269
|
-
--
|
|
270
|
-
--
|
|
295
|
+
-c, --check Validate Edgefile.rip or config.rip and exit
|
|
296
|
+
-r, --reload Reload config on a running server
|
|
297
|
+
-s, --stop Stop a running server
|
|
298
|
+
-f, --file=<path> Load Edgefile.rip from an explicit path
|
|
271
299
|
--watch=<glob> Watch glob pattern (default: *.rip)
|
|
272
300
|
--static Disable hot reload and file watching
|
|
273
301
|
--env=<mode> Set environment (dev, production)
|
|
@@ -297,13 +325,15 @@ export runHelpOutput = (argv) ->
|
|
|
297
325
|
r:<n>,<s>s Restart policy: max requests, max seconds
|
|
298
326
|
|
|
299
327
|
Examples:
|
|
300
|
-
rip
|
|
301
|
-
rip
|
|
302
|
-
rip
|
|
303
|
-
rip
|
|
304
|
-
rip
|
|
305
|
-
rip
|
|
306
|
-
rip
|
|
328
|
+
rip server Start with ./index.rip (watches *.rip)
|
|
329
|
+
rip server myapp Start with app name "myapp"
|
|
330
|
+
rip server http HTTP only (no TLS)
|
|
331
|
+
rip server --static w:8 Production: no reload, 8 workers
|
|
332
|
+
rip server -c Validate config and exit
|
|
333
|
+
rip server -c -f=./Edgefile.rip
|
|
334
|
+
rip server -r Reload running server config
|
|
335
|
+
rip server -s Stop running server
|
|
336
|
+
rip server -f=./Edgefile.rip
|
|
307
337
|
|
|
308
338
|
If no index.rip or index.ts is found, a built-in static file server
|
|
309
339
|
activates with directory indexes, auto-detected MIME types, and
|
|
@@ -313,8 +343,7 @@ export runHelpOutput = (argv) ->
|
|
|
313
343
|
|
|
314
344
|
# --- Subcommands ---
|
|
315
345
|
|
|
316
|
-
export
|
|
317
|
-
return false unless 'stop' in argv
|
|
346
|
+
export stopServer = (prefix, getPidFilePathFn, existsSyncFn, readFileSyncFn, killFn, spawnSyncFn, importMetaPath) ->
|
|
318
347
|
pidFile = getPidFilePathFn(prefix)
|
|
319
348
|
try
|
|
320
349
|
if existsSyncFn(pidFile)
|
|
@@ -326,7 +355,6 @@ export runStopSubcommand = (argv, prefix, getPidFilePathFn, existsSyncFn, readFi
|
|
|
326
355
|
spawnSyncFn(['pkill', '-f', importMetaPath])
|
|
327
356
|
catch e
|
|
328
357
|
console.error "rip-server: stop failed: #{e.message}"
|
|
329
|
-
true
|
|
330
358
|
|
|
331
359
|
export runListSubcommand = (argv, prefix, getControlSocketPathFn, fetchFn, exitFn) ->
|
|
332
360
|
return false unless 'list' in argv
|
|
@@ -341,3 +369,18 @@ export runListSubcommand = (argv, prefix, getControlSocketPathFn, fetchFn, exitF
|
|
|
341
369
|
console.error "list command failed: #{e?.message or e}"
|
|
342
370
|
exitFn(1)
|
|
343
371
|
true
|
|
372
|
+
|
|
373
|
+
export runReloadSubcommand = (prefix, getControlSocketPathFn, fetchFn, exitFn) ->
|
|
374
|
+
controlUnix = getControlSocketPathFn(prefix)
|
|
375
|
+
try
|
|
376
|
+
res = fetchFn!('http://localhost/reload', { unix: controlUnix, method: 'POST' })
|
|
377
|
+
j = res.json!
|
|
378
|
+
if j?.ok
|
|
379
|
+
p "rip-server: config reload applied"
|
|
380
|
+
else
|
|
381
|
+
reason = j?.reason or 'unknown'
|
|
382
|
+
console.error "rip-server: config reload rejected: #{reason}"
|
|
383
|
+
exitFn(1)
|
|
384
|
+
catch e
|
|
385
|
+
console.error "rip-server: reload failed: #{e?.message or e}"
|
|
386
|
+
exitFn(1)
|
package/control/workers.rip
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# ==============================================================================
|
|
4
4
|
|
|
5
5
|
import { unlinkSync } from 'node:fs'
|
|
6
|
+
import { dirname, join } from 'node:path'
|
|
6
7
|
import { getWorkerSocketPath, getControlSocketPath } from './control.rip'
|
|
7
8
|
|
|
8
9
|
# --- Spawn ---
|
|
@@ -28,7 +29,8 @@ export spawnTrackedWorker = (flags, workerId, version, nowMsFn, importMetaPath,
|
|
|
28
29
|
RIP_LOG_JSON: if flags.jsonLogging then '1' else '0'
|
|
29
30
|
RIP_VERSION: String(version)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
loaderPath = join(dirname(importMetaPath), '..', '..', 'rip-loader.js')
|
|
33
|
+
proc = Bun.spawn ['bun', '--preload', loaderPath, importMetaPath],
|
|
32
34
|
stdout: 'inherit'
|
|
33
35
|
stderr: 'inherit'
|
|
34
36
|
stdin: 'ignore'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rip-lang/server",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.126",
|
|
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.131"
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"api.rip",
|
package/server.rip
CHANGED
|
@@ -26,7 +26,7 @@ import { handleWorkerControl, handleWatchControl, handleRegistryControl, handleR
|
|
|
26
26
|
import { getLanIP as controlGetLanIP, startMdnsAdvertisement as controlStartMdnsAdvertisement, stopMdnsAdvertisements as controlStopMdnsAdvertisements } from './control/mdns.rip'
|
|
27
27
|
import { registerWatchGroup, handleWatchGroup, broadcastWatchChange, registerAppWatchDirs, shouldTriggerCodeReload } from './control/watchers.rip'
|
|
28
28
|
import { computeHideUrls, logStartupSummary, createCleanup, installShutdownHandlers } from './control/lifecycle.rip'
|
|
29
|
-
import {
|
|
29
|
+
import { runVersionOutput, runHelpOutput, stopServer, runReloadSubcommand, resolveAppEntry, parseFlags, coerceInt } from './control/cli.rip'
|
|
30
30
|
import { runSetupMode, runWorkerMode } from './control/worker.rip'
|
|
31
31
|
import { spawnTrackedWorker, postWorkerQuit, waitForWorkerReady } from './control/workers.rip'
|
|
32
32
|
import { createChallengeStore, handleChallengeRequest } from './acme/store.rip'
|
|
@@ -1476,21 +1476,17 @@ main = ->
|
|
|
1476
1476
|
if runHelpOutput(process.argv)
|
|
1477
1477
|
return
|
|
1478
1478
|
|
|
1479
|
-
# Subcommands: stop/list
|
|
1480
|
-
if 'stop' in process.argv or 'list' in process.argv
|
|
1481
|
-
prefix = cliComputeSocketPrefix(process.argv, resolveAppEntry, existsSync, isAbsolute, resolve, (-> process.cwd()))
|
|
1482
|
-
if runStopSubcommand(process.argv, prefix, getPidFilePath, existsSync, readFileSync, process.kill.bind(process), Bun.spawnSync, import.meta.path)
|
|
1483
|
-
return
|
|
1484
|
-
|
|
1485
|
-
if runListSubcommand(process.argv, prefix, getControlSocketPath, fetch, exit)
|
|
1486
|
-
return
|
|
1487
|
-
|
|
1488
|
-
# Normal startup
|
|
1489
1479
|
flags = parseFlags(process.argv, defaultEntry)
|
|
1490
1480
|
applyFlagSideEffects(flags)
|
|
1491
1481
|
if flags.checkConfig
|
|
1492
1482
|
runCheckConfig!(flags)
|
|
1493
1483
|
return
|
|
1484
|
+
if flags.reloadConfig
|
|
1485
|
+
runReloadSubcommand!(flags.socketPrefix, getControlSocketPath, fetch, exit)
|
|
1486
|
+
return
|
|
1487
|
+
if flags.stopServer
|
|
1488
|
+
stopServer(flags.socketPrefix, getPidFilePath, existsSync, readFileSync, process.kill.bind(process), Bun.spawnSync, import.meta.path)
|
|
1489
|
+
return
|
|
1494
1490
|
setEventJsonMode(flags.jsonLogging)
|
|
1495
1491
|
pidFile = getPidFilePath(flags.socketPrefix)
|
|
1496
1492
|
writeFileSync(pidFile, String(process.pid))
|
|
@@ -1504,13 +1500,7 @@ main = ->
|
|
|
1504
1500
|
exit 1 unless startupReload.ok
|
|
1505
1501
|
|
|
1506
1502
|
cleanup = createCleanup(pidFile, svr, mgr, unlinkSync, fetch, process)
|
|
1507
|
-
installShutdownHandlers(cleanup, process
|
|
1508
|
-
p 'rip-server: received SIGHUP, reloading config...'
|
|
1509
|
-
if svr.reloadRuntimeConfig!(flags, 'sighup', true).ok
|
|
1510
|
-
p 'rip-server: config reload applied'
|
|
1511
|
-
else
|
|
1512
|
-
p 'rip-server: config reload rejected, keeping previous config'
|
|
1513
|
-
)
|
|
1503
|
+
installShutdownHandlers(cleanup, process)
|
|
1514
1504
|
|
|
1515
1505
|
# Suppress top URL lines if setup.rip will print them at the bottom
|
|
1516
1506
|
flags.hideUrls = computeHideUrls(flags.appEntry, join, dirname, existsSync)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { test, eq, ok } from '../test.rip'
|
|
2
|
-
import { coerceInt, parseFlags } from '../control/cli.rip'
|
|
2
|
+
import { coerceInt, computeSocketPrefix, parseFlags } from '../control/cli.rip'
|
|
3
3
|
import { createEdgeRuntime, createReloadHistoryEntry } from '../edge/runtime.rip'
|
|
4
4
|
|
|
5
5
|
test "control cli helpers load and parse basic flags", ->
|
|
@@ -8,6 +8,28 @@ test "control cli helpers load and parse basic flags", ->
|
|
|
8
8
|
eq flags.httpsPort, 9445
|
|
9
9
|
ok flags.edgefilePath.endsWith('Edgefile.rip')
|
|
10
10
|
|
|
11
|
+
test "control cli uses bare app token as true app name override", ->
|
|
12
|
+
flags = parseFlags(['bun', 'server', 'ola'])
|
|
13
|
+
eq flags.appName, 'ola'
|
|
14
|
+
eq flags.appAliases[0], 'ola'
|
|
15
|
+
eq flags.socketPrefix, 'rip_ola'
|
|
16
|
+
|
|
17
|
+
socketPrefix = computeSocketPrefix(
|
|
18
|
+
['bun', 'server', 'ola'],
|
|
19
|
+
(appTok) -> { appName: appTok }
|
|
20
|
+
-> false
|
|
21
|
+
-> false
|
|
22
|
+
(_, tok) -> tok
|
|
23
|
+
-> '/tmp'
|
|
24
|
+
)
|
|
25
|
+
eq socketPrefix, 'rip_ola'
|
|
26
|
+
|
|
27
|
+
test "control cli supports explicit path with custom app name", ->
|
|
28
|
+
flags = parseFlags(['bun', 'server', 'packages/server/default.rip@ola'])
|
|
29
|
+
eq flags.appName, 'ola'
|
|
30
|
+
eq flags.appAliases[0], 'ola'
|
|
31
|
+
eq flags.socketPrefix, 'rip_ola'
|
|
32
|
+
|
|
11
33
|
test "edge runtime helpers load and create summary entries", ->
|
|
12
34
|
runtime = createEdgeRuntime()
|
|
13
35
|
entry = createReloadHistoryEntry('reload-1', 'test', null, 1, 'applied')
|