@rip-lang/server 1.3.124 → 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 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
- appAliases = aliasesPart.split(',').map((a) -> a.trim()).filter((a) -> a)
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
- appAliases = [pathPart].concat(aliasesPart.split(',').map((a) -> a.trim()).filter((a) -> a))
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
- appAliases.push(tok)
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 serve [options] [app-path] [app-name]
264
- rip serve [options] [app-path]@<alias1>,<alias2>,...
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
- --edgefile=<path> Load Edgefile.rip from an explicit path
270
- --check-config Validate Edgefile.rip or config.rip and exit
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 serve Start with ./index.rip (watches *.rip)
301
- rip serve myapp Start with app name "myapp"
302
- rip serve http HTTP only (no TLS)
303
- rip serve --static w:8 Production: no reload, 8 workers
304
- rip serve --check-config
305
- rip serve --check-config --edgefile=./Edgefile.rip
306
- rip serve --edgefile=./Edgefile.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 runStopSubcommand = (argv, prefix, getPidFilePathFn, existsSyncFn, readFileSyncFn, killFn, spawnSyncFn, importMetaPath) ->
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)
@@ -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
- proc = Bun.spawn ['rip', importMetaPath],
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.124",
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.129"
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 { computeSocketPrefix as cliComputeSocketPrefix, runVersionOutput, runHelpOutput, runStopSubcommand, runListSubcommand, resolveAppEntry, parseFlags, coerceInt } from './control/cli.rip'
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')