@rip-lang/server 1.3.77 → 1.3.78

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.
Files changed (3) hide show
  1. package/default.rip +137 -0
  2. package/package.json +2 -1
  3. package/server.rip +10 -6
package/default.rip ADDED
@@ -0,0 +1,137 @@
1
+ # ==============================================================================
2
+ # @rip-lang/server — Default Static File Server
3
+ # ==============================================================================
4
+ #
5
+ # Built-in fallback when no index.rip or index.ts exists in the target directory.
6
+ # Activated automatically by `rip server` when no app entry is found.
7
+ #
8
+ # Behavior:
9
+ # - Serves static files with auto-detected MIME types
10
+ # - Directories with index.html serve the index.html
11
+ # - Directories without index.html show a browsable file listing
12
+ # - Rip browser bundle available at /rip/rip.min.js
13
+ # - Hot-reload via SSE for .rip, .html, and .css changes
14
+ #
15
+ # ==============================================================================
16
+
17
+ import { use, start, notFound } from '@rip-lang/server'
18
+ import { serve } from '@rip-lang/server/middleware'
19
+ import { statSync, readdirSync } from 'node:fs'
20
+ import { join, resolve } from 'node:path'
21
+
22
+ root = resolve(process.env.APP_BASE_DIR or process.cwd())
23
+ rootSlash = root + '/'
24
+
25
+ use serve dir: root, bundle: [], watch: true
26
+
27
+ # --- Helpers ---
28
+
29
+ esc = (s) ->
30
+ s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;')
31
+
32
+ fmtSize = (n) ->
33
+ for u in ['B', 'KB', 'MB', 'GB']
34
+ if n < 1024
35
+ return if u is 'B' then "#{n} B" else "#{n.toFixed(1)} #{u}"
36
+ n /= 1024
37
+ "#{n.toFixed(1)} TB"
38
+
39
+ fmtDate = (ms) ->
40
+ new Date(ms).toLocaleString undefined,
41
+ year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit'
42
+
43
+ ICONS =
44
+ html: '🌐', htm: '🌐', css: '🎨', js: '📜', mjs: '📜', ts: '📜'
45
+ json: '📋', rip: '⚡', md: '📝', txt: '📝', pdf: '📕'
46
+ png: '🖼️', jpg: '🖼️', jpeg: '🖼️', gif: '🖼️', svg: '🖼️', webp: '🖼️', ico: '🖼️'
47
+ zip: '📦', gz: '📦', tar: '📦'
48
+ mp3: '🎵', ogg: '🎵', wav: '🎵', mp4: '🎬', webm: '🎬'
49
+ woff: '🔤', woff2: '🔤', ttf: '🔤', otf: '🔤'
50
+
51
+ icon = (name) ->
52
+ ICONS[name.slice(name.lastIndexOf('.') + 1).toLowerCase()] or '📄'
53
+
54
+ tr = (ic, href, label, size = '', date = '') ->
55
+ "<tr><td class=\"i\">#{ic}</td><td><a href=\"#{esc href}\">#{esc label}</a></td><td class=\"s\">#{size}</td><td class=\"d\">#{date}</td></tr>"
56
+
57
+ # --- Directory Index ---
58
+
59
+ renderIndex = (reqPath, fsPath) ->
60
+ entries = try readdirSync(fsPath, { withFileTypes: true }) catch then []
61
+ dirs = []
62
+ files = []
63
+ for e in entries
64
+ continue if e.name.startsWith('.')
65
+ if e.isDirectory() then dirs.push(e.name) else files.push(e.name)
66
+ dirs.sort()
67
+ files.sort()
68
+
69
+ rows = []
70
+ rows.push tr('📁', '../', 'Parent Directory') if reqPath isnt '/'
71
+ for d in dirs
72
+ rows.push tr('📁', "#{d}/", "#{d}/")
73
+ for f in files
74
+ try
75
+ s = statSync(join(fsPath, f))
76
+ rows.push tr(icon(f), f, f, fmtSize(s.size), fmtDate(s.mtimeMs))
77
+ catch
78
+ rows.push tr(icon(f), f, f)
79
+
80
+ title = esc(reqPath)
81
+ """
82
+ <!DOCTYPE html>
83
+ <html lang="en">
84
+ <head>
85
+ <meta charset="UTF-8">
86
+ <meta name="viewport" content="width=device-width, initial-scale=1">
87
+ <title>Index of #{title}</title>
88
+ <style>
89
+ *{margin:0;padding:0;box-sizing:border-box}
90
+ body{font-family:ui-monospace,'SF Mono',Menlo,Monaco,monospace;background:#fafafa;color:#333;padding:24px 40px}
91
+ h1{font-size:16px;font-weight:600;color:#111;padding:12px 0;border-bottom:2px solid #e1e4e8;margin-bottom:4px}
92
+ table{width:100%;border-collapse:collapse}
93
+ th{text-align:left;padding:8px 12px;font-size:11px;font-weight:600;color:#888;text-transform:uppercase;letter-spacing:.5px;border-bottom:1px solid #e1e4e8}
94
+ td{padding:5px 12px;border-bottom:1px solid #f0f0f0;font-size:13px}
95
+ a{color:#0969da;text-decoration:none}a:hover{text-decoration:underline}
96
+ tr:hover td{background:#f0f7ff}
97
+ .i{width:28px;text-align:center}.s{width:100px;text-align:right;color:#666}.d{width:200px;color:#666}th.s{text-align:right}
98
+ @media(max-width:600px){body{padding:16px}.d,th.d{display:none}}
99
+ </style>
100
+ </head>
101
+ <body>
102
+ <h1>Index of #{title}</h1>
103
+ <table>
104
+ <thead><tr><th></th><th>Name</th><th class="s">Size</th><th class="d">Modified</th></tr></thead>
105
+ <tbody>
106
+ #{rows.join('\n')}
107
+ </tbody>
108
+ </table>
109
+ </body>
110
+ </html>
111
+ """
112
+
113
+ # --- Request Handler ---
114
+
115
+ notFound ->
116
+ reqPath = @req.path
117
+ path = resolve(root, reqPath.slice(1))
118
+
119
+ unless path is root or path.startsWith(rootSlash)
120
+ return new Response 'Not Found', { status: 404 }
121
+
122
+ try
123
+ stat = statSync(path)
124
+ catch
125
+ return new Response 'Not Found', { status: 404 }
126
+
127
+ if stat.isFile()
128
+ return @send path
129
+
130
+ if stat.isDirectory()
131
+ return new Response(null, { status: 301, headers: { Location: "#{reqPath}/" } }) unless reqPath.endsWith('/')
132
+ try return @send join(path, 'index.html'), 'text/html; charset=UTF-8' if statSync(join(path, 'index.html')).isFile()
133
+ return new Response renderIndex(reqPath, path), { headers: { 'Content-Type': 'text/html; charset=UTF-8' } }
134
+
135
+ new Response 'Not Found', { status: 404 }
136
+
137
+ start()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rip-lang/server",
3
- "version": "1.3.77",
3
+ "version": "1.3.78",
4
4
  "description": "Pure Rip web framework and application server",
5
5
  "type": "module",
6
6
  "main": "api.rip",
@@ -49,6 +49,7 @@
49
49
  },
50
50
  "files": [
51
51
  "api.rip",
52
+ "default.rip",
52
53
  "middleware.rip",
53
54
  "server.rip",
54
55
  "server.html",
package/server.rip CHANGED
@@ -165,6 +165,8 @@ parseRestartPolicy = (token, defReqs, defSecs) ->
165
165
 
166
166
  { maxRequests, maxSeconds }
167
167
 
168
+ defaultEntry = join(import.meta.dir, 'default.rip')
169
+
168
170
  resolveAppEntry = (appPathInput) ->
169
171
  abs = if isAbsolute(appPathInput) then appPathInput else resolve(process.cwd(), appPathInput)
170
172
 
@@ -177,8 +179,8 @@ resolveAppEntry = (appPathInput) ->
177
179
  else if existsSync(two)
178
180
  entryPath = two
179
181
  else
180
- console.error "No app entry found. Probed: #{one}, #{two}"
181
- process.exit(2)
182
+ console.log "rip-server: no index.rip found, serving static files from #{abs}\n Create an index.rip to customize, or run 'rip server --help' for options."
183
+ entryPath = defaultEntry
182
184
  else
183
185
  unless existsSync(abs)
184
186
  console.error "App path not found: #{abs}"
@@ -247,10 +249,7 @@ parseFlags = (argv) ->
247
249
  else if existsSync(indexTs)
248
250
  appPathInput = indexTs
249
251
  else
250
- console.error 'No app entry found. Looked for index.rip or index.ts in current directory.'
251
- console.error 'Usage: rip-server [flags] [app-path] [app-name]'
252
- console.error ' rip-server [flags] [app-path]@<alias1>,<alias2>,...'
253
- process.exit(2)
252
+ appPathInput = cwd
254
253
 
255
254
  getKV = (prefix) ->
256
255
  for f as rawFlags
@@ -620,6 +619,7 @@ class Manager
620
619
  SOCKET_PATH: socketPath
621
620
  SOCKET_PREFIX: @flags.socketPrefix
622
621
  APP_ENTRY: @flags.appEntry
622
+ APP_BASE_DIR: @flags.appBaseDir
623
623
  MAX_REQUESTS: String(@flags.maxRequestsPerWorker)
624
624
  MAX_SECONDS: String(@flags.maxSecondsPerWorker)
625
625
  RIP_LOG_JSON: if @flags.jsonLogging then '1' else '0'
@@ -1212,6 +1212,10 @@ main = ->
1212
1212
  rip serve myapp Start with app name "myapp"
1213
1213
  rip serve http HTTP only (no TLS)
1214
1214
  rip serve --static w:8 Production: no reload, 8 workers
1215
+
1216
+ If no index.rip or index.ts is found, a built-in static file server
1217
+ activates with directory indexes, auto-detected MIME types, and
1218
+ hot-reload. Create an index.rip to customize server behavior.
1215
1219
  """
1216
1220
  return
1217
1221