@rip-lang/server 1.3.123 → 1.3.125
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 +58 -19
- 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)
|
|
@@ -121,6 +130,7 @@ export parseFlags = (argv, defaultEntryPath = null) ->
|
|
|
121
130
|
has = (name) -> rawFlags.has(name)
|
|
122
131
|
|
|
123
132
|
{ baseDir, entryPath, appName } = resolveAppEntry(appPathInput, defaultEntryPath)
|
|
133
|
+
appName = appNameOverride if appNameOverride
|
|
124
134
|
appAliases = [appName] if appAliases.length is 0
|
|
125
135
|
|
|
126
136
|
tokens = Array.from(rawFlags)
|
|
@@ -211,8 +221,10 @@ export parseFlags = (argv, defaultEntryPath = null) ->
|
|
|
211
221
|
rateLimit: coerceInt(getKV('--rate-limit='), 0)
|
|
212
222
|
rateLimitWindow: coerceInt(getKV('--rate-limit-window='), 60000)
|
|
213
223
|
watch: watch
|
|
214
|
-
edgefilePath: getKV('--edgefile=')
|
|
215
|
-
checkConfig: has('--check-config')
|
|
224
|
+
edgefilePath: getKV('--edgefile=') or getKV('--file=') or getKV('-f=')
|
|
225
|
+
checkConfig: has('--check-config') or has('--check') or has('-c')
|
|
226
|
+
reloadConfig: has('--reload') or has('-r')
|
|
227
|
+
stopServer: has('--stop') or has('-s')
|
|
216
228
|
envOverride: envOverride
|
|
217
229
|
debug: has('--debug')
|
|
218
230
|
}
|
|
@@ -236,6 +248,16 @@ export findAppPathToken = (argv, existsSyncFn, isAbsoluteFn, resolveFn, cwdFn) -
|
|
|
236
248
|
export computeSocketPrefix = (argv, resolveAppEntryFn, existsSyncFn, isAbsoluteFn, resolveFn, cwdFn) ->
|
|
237
249
|
override = getKV(argv, '--socket-prefix=')
|
|
238
250
|
return override if override
|
|
251
|
+
for i in [2...argv.length]
|
|
252
|
+
tok = argv[i]
|
|
253
|
+
continue if tok.startsWith('-') or /^\d+$/.test(tok) or tok in ['http', 'https']
|
|
254
|
+
[pathPart, aliasesPart] = if tok.includes('@') then tok.split('@') else [tok, null]
|
|
255
|
+
aliases = aliasesPart?.split(',').map((a) -> a.trim()).filter((a) -> a) or []
|
|
256
|
+
looksLikePath = pathPart.includes('/') or pathPart.startsWith('.') or pathPart.endsWith('.rip') or pathPart.endsWith('.ts')
|
|
257
|
+
unless looksLikePath
|
|
258
|
+
return "rip_#{pathPart}"
|
|
259
|
+
return "rip_#{aliases[0]}" if aliases.length > 0
|
|
260
|
+
break
|
|
239
261
|
appTok = findAppPathToken(argv, existsSyncFn, isAbsoluteFn, resolveFn, cwdFn)
|
|
240
262
|
if appTok
|
|
241
263
|
try
|
|
@@ -260,14 +282,16 @@ export runHelpOutput = (argv) ->
|
|
|
260
282
|
rip-server - Pure Rip application server
|
|
261
283
|
|
|
262
284
|
Usage:
|
|
263
|
-
rip
|
|
264
|
-
rip
|
|
285
|
+
rip server [options] [app-path] [app-name]
|
|
286
|
+
rip server [options] [app-path]@<alias1>,<alias2>,...
|
|
265
287
|
|
|
266
288
|
Options:
|
|
267
289
|
-h, --help Show this help
|
|
268
290
|
-v, --version Show version
|
|
269
|
-
--
|
|
270
|
-
--
|
|
291
|
+
-c, --check Validate Edgefile.rip or config.rip and exit
|
|
292
|
+
-r, --reload Reload config on a running server
|
|
293
|
+
-s, --stop Stop a running server
|
|
294
|
+
-f, --file=<path> Load Edgefile.rip from an explicit path
|
|
271
295
|
--watch=<glob> Watch glob pattern (default: *.rip)
|
|
272
296
|
--static Disable hot reload and file watching
|
|
273
297
|
--env=<mode> Set environment (dev, production)
|
|
@@ -297,13 +321,15 @@ export runHelpOutput = (argv) ->
|
|
|
297
321
|
r:<n>,<s>s Restart policy: max requests, max seconds
|
|
298
322
|
|
|
299
323
|
Examples:
|
|
300
|
-
rip
|
|
301
|
-
rip
|
|
302
|
-
rip
|
|
303
|
-
rip
|
|
304
|
-
rip
|
|
305
|
-
rip
|
|
306
|
-
rip
|
|
324
|
+
rip server Start with ./index.rip (watches *.rip)
|
|
325
|
+
rip server myapp Start with app name "myapp"
|
|
326
|
+
rip server http HTTP only (no TLS)
|
|
327
|
+
rip server --static w:8 Production: no reload, 8 workers
|
|
328
|
+
rip server -c Validate config and exit
|
|
329
|
+
rip server -c -f=./Edgefile.rip
|
|
330
|
+
rip server -r Reload running server config
|
|
331
|
+
rip server -s Stop running server
|
|
332
|
+
rip server -f=./Edgefile.rip
|
|
307
333
|
|
|
308
334
|
If no index.rip or index.ts is found, a built-in static file server
|
|
309
335
|
activates with directory indexes, auto-detected MIME types, and
|
|
@@ -313,8 +339,7 @@ export runHelpOutput = (argv) ->
|
|
|
313
339
|
|
|
314
340
|
# --- Subcommands ---
|
|
315
341
|
|
|
316
|
-
export
|
|
317
|
-
return false unless 'stop' in argv
|
|
342
|
+
export stopServer = (prefix, getPidFilePathFn, existsSyncFn, readFileSyncFn, killFn, spawnSyncFn, importMetaPath) ->
|
|
318
343
|
pidFile = getPidFilePathFn(prefix)
|
|
319
344
|
try
|
|
320
345
|
if existsSyncFn(pidFile)
|
|
@@ -326,7 +351,6 @@ export runStopSubcommand = (argv, prefix, getPidFilePathFn, existsSyncFn, readFi
|
|
|
326
351
|
spawnSyncFn(['pkill', '-f', importMetaPath])
|
|
327
352
|
catch e
|
|
328
353
|
console.error "rip-server: stop failed: #{e.message}"
|
|
329
|
-
true
|
|
330
354
|
|
|
331
355
|
export runListSubcommand = (argv, prefix, getControlSocketPathFn, fetchFn, exitFn) ->
|
|
332
356
|
return false unless 'list' in argv
|
|
@@ -341,3 +365,18 @@ export runListSubcommand = (argv, prefix, getControlSocketPathFn, fetchFn, exitF
|
|
|
341
365
|
console.error "list command failed: #{e?.message or e}"
|
|
342
366
|
exitFn(1)
|
|
343
367
|
true
|
|
368
|
+
|
|
369
|
+
export runReloadSubcommand = (prefix, getControlSocketPathFn, fetchFn, exitFn) ->
|
|
370
|
+
controlUnix = getControlSocketPathFn(prefix)
|
|
371
|
+
try
|
|
372
|
+
res = fetchFn!('http://localhost/reload', { unix: controlUnix, method: 'POST' })
|
|
373
|
+
j = res.json!
|
|
374
|
+
if j?.ok
|
|
375
|
+
p "rip-server: config reload applied"
|
|
376
|
+
else
|
|
377
|
+
reason = j?.reason or 'unknown'
|
|
378
|
+
console.error "rip-server: config reload rejected: #{reason}"
|
|
379
|
+
exitFn(1)
|
|
380
|
+
catch e
|
|
381
|
+
console.error "rip-server: reload failed: #{e?.message or e}"
|
|
382
|
+
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.125",
|
|
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.130"
|
|
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')
|