rip-lang 3.13.13 → 3.13.14

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 CHANGED
@@ -9,9 +9,9 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.13-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.14-blue.svg" alt="Version"></a>
13
13
  <a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
14
- <a href="#"><img src="https://img.shields.io/badge/tests-1%2C251%2F1%2C251-brightgreen.svg" alt="Tests"></a>
14
+ <a href="#"><img src="https://img.shields.io/badge/tests-1%2C255%2F1%2C255-brightgreen.svg" alt="Tests"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
16
16
  </p>
17
17
 
@@ -417,18 +417,17 @@ Rip includes optional packages for full-stack development:
417
417
 
418
418
  | Package | Version | Purpose |
419
419
  |---------|---------|---------|
420
- | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.10.10 | Core language compiler |
421
- | [@rip-lang/api](packages/api/) | 1.1.10 | HTTP framework (Sinatra-style routing, 37 validators) |
422
- | [@rip-lang/server](packages/server/) | 1.1.19 | Multi-worker app server (hot reload, HTTPS, mDNS) |
423
- | [@rip-lang/db](packages/db/) | 1.2.0 | DuckDB server with official UI + ActiveRecord-style client |
424
- | [@rip-lang/grid](packages/grid/) | 0.2.0 | Reactive data grid |
425
- | [@rip-lang/swarm](packages/swarm/) | 1.1.4 | Parallel job runner with worker pool |
426
- | [@rip-lang/csv](packages/csv/) | 1.1.4 | CSV parser + writer |
427
- | [@rip-lang/schema](packages/schema/) | 0.2.1 | Unified schema → TypeScript types, SQL DDL, validation, ORM |
420
+ | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.13.13 | Core language compiler |
421
+ | [@rip-lang/server](packages/server/) | 1.2.11 | Multi-worker app server (web framework, hot reload, HTTPS, mDNS) |
422
+ | [@rip-lang/db](packages/db/) | 1.3.13 | DuckDB server with official UI + ActiveRecord-style client |
423
+ | [@rip-lang/grid](packages/grid/) | 0.2.8 | Reactive data grid |
424
+ | [@rip-lang/swarm](packages/swarm/) | 1.2.16 | Parallel job runner with worker pool |
425
+ | [@rip-lang/csv](packages/csv/) | 1.3.4 | CSV parser + writer |
426
+ | [@rip-lang/schema](packages/schema/) | 0.3.6 | Unified schema TypeScript types, SQL DDL, validation, ORM |
428
427
  | [VS Code Extension](packages/vscode/) | 0.5.0 | Syntax highlighting, type intelligence, source maps |
429
428
 
430
429
  ```bash
431
- bun add -g @rip-lang/db # Installs everything (rip-lang + api + db)
430
+ bun add -g @rip-lang/db # Installs everything (rip-lang + server + db)
432
431
  ```
433
432
 
434
433
  ---
@@ -463,7 +462,7 @@ rip file.rip # Run
463
462
  rip -c file.rip # Compile
464
463
  rip -t file.rip # Tokens
465
464
  rip -s file.rip # S-expressions
466
- bun run test # 1251 tests
465
+ bun run test # 1255 tests
467
466
  bun run parser # Rebuild parser
468
467
  bun run build # Build browser bundle
469
468
  ```
package/bin/rip CHANGED
@@ -1,26 +1,26 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { readFileSync, writeFileSync, existsSync, statSync, unlinkSync } from 'fs';
3
+ import { readFileSync, readdirSync, writeFileSync, existsSync, statSync, unlinkSync } from 'fs';
4
4
  import { execSync, spawnSync, spawn } from 'child_process';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { dirname, basename, join } from 'path';
7
7
  import { Compiler } from '../src/compiler.js';
8
- import { startREPL } from '../src/repl.js';
9
8
  import packageJson from '../package.json' with { type: 'json' };
10
9
 
11
- // Get the directory where this script lives
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = dirname(__filename);
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const loaderPath = join(__dirname, '../rip-loader.js');
14
12
 
15
- // Set NODE_PATH so spawned child processes can resolve @rip-lang/* packages
16
- const _nodeModules = join(__dirname, '..', '..');
17
- if (!process.env.NODE_PATH?.split(':').includes(_nodeModules)) {
18
- process.env.NODE_PATH = [_nodeModules, process.env.NODE_PATH].filter(Boolean).join(':');
13
+ // Ensure spawned processes can resolve @rip-lang/* packages
14
+ const nodeModules = join(__dirname, '..', '..');
15
+ if (!process.env.NODE_PATH?.split(':').includes(nodeModules)) {
16
+ process.env.NODE_PATH = [nodeModules, process.env.NODE_PATH].filter(Boolean).join(':');
19
17
  }
20
18
 
21
19
  const VERSION = packageJson.version;
22
20
  const SUMMARY = packageJson.description;
23
21
 
22
+ const isFile = (path) => existsSync(path) && statSync(path).isFile();
23
+
24
24
  function printHelp() {
25
25
  console.log(`
26
26
  Rip ${VERSION} - ${SUMMARY}
@@ -40,15 +40,25 @@ Options:
40
40
  -s, --sexpr Show s-expressions (only, unless -c also specified)
41
41
  -t, --tokens Show token stream (only, unless -c also specified)
42
42
  -v, --version Show version number
43
- -w, --web Launch browser REPL (auto-opens + auto-shutdown)
44
43
 
45
44
  Subcommands:
45
+ rip serve [flags] [app] Start server (watches *.rip, HTTPS, mDNS)
46
46
  rip check [dir] Type-check all .rip files in directory
47
47
 
48
+ Serve flags:
49
+ --watch=<glob> Watch glob pattern (default: *.rip)
50
+ --static Disable hot reload and file watching
51
+ http HTTP-only mode (no HTTPS)
52
+ http:<port> Set HTTP port
53
+ w:<n> Worker count (auto, half, 2x, 3x, or number)
54
+
48
55
  Examples:
49
56
  rip # Interactive REPL (terminal)
50
57
  rip script.rip # Execute script directly
51
58
  rip script.rip arg1 arg2 # Execute with arguments
59
+ rip serve # Serve ./index.rip (watches *.rip)
60
+ rip serve http # Serve HTTP-only
61
+ rip serve myapp # Serve with mDNS name
52
62
  rip -c example.rip # Compile and show JavaScript
53
63
  rip -o output.js example.rip # Compile and save to file
54
64
  rip -s example.rip # Show ONLY s-expressions
@@ -59,7 +69,6 @@ Examples:
59
69
  rip -m example.rip # Compile with inline source map
60
70
  rip -cd example.rip # Show compiled JS and type declarations
61
71
  rip -q -c example.rip # Just the JS, no headers (for piping)
62
- rip -w # Launch browser REPL (auto-opens)
63
72
  echo 'x = 1 + 2' | rip -c # Compile from stdin
64
73
 
65
74
  Shebang support:
@@ -71,22 +80,14 @@ Shebang support:
71
80
  async function main() {
72
81
  const args = process.argv.slice(2);
73
82
 
74
- // Find the script file position (first non-option argument)
75
- // Everything BEFORE it is rip options, everything AFTER it is script arguments
83
+ // Find script file (first non-option argument)
84
+ // Everything before it is rip options, everything after is script arguments
76
85
  let scriptFileIndex = -1;
77
86
  for (let i = 0; i < args.length; i++) {
78
- // Skip option values (like -o filename)
79
- if (i > 0 && (args[i - 1] === '-o' || args[i - 1] === '--output')) {
80
- continue;
81
- }
82
- // First non-option arg is the script file
83
- if (!args[i].startsWith('-')) {
84
- scriptFileIndex = i;
85
- break;
86
- }
87
+ if (i > 0 && (args[i - 1] === '-o' || args[i - 1] === '--output')) continue;
88
+ if (!args[i].startsWith('-')) { scriptFileIndex = i; break; }
87
89
  }
88
90
 
89
- // Split into rip options and script arguments
90
91
  const rawOptions = scriptFileIndex === -1 ? args : args.slice(0, scriptFileIndex);
91
92
  const scriptArgs = scriptFileIndex === -1 ? [] : args.slice(scriptFileIndex + 1);
92
93
 
@@ -95,7 +96,6 @@ async function main() {
95
96
  /^-[a-zA-Z]{2,}$/.test(arg) ? [...arg.slice(1)].map(ch => `-${ch}`) : [arg]
96
97
  );
97
98
 
98
- // Only check ripOptions for rip's flags (not script args!)
99
99
  if (ripOptions.includes('-h') || ripOptions.includes('--help')) {
100
100
  printHelp();
101
101
  process.exit(0);
@@ -104,13 +104,11 @@ async function main() {
104
104
  if (ripOptions.includes('-v') || ripOptions.includes('--version')) {
105
105
  console.log(`Rip ${VERSION} - ${SUMMARY}`);
106
106
  try {
107
- const { readdirSync } = await import('fs');
108
107
  const home = process.env.HOME || process.env.USERPROFILE;
109
108
  const scopeDir = join(home, '.bun', 'install', 'global', 'node_modules', '@rip-lang');
110
109
  if (existsSync(scopeDir) && statSync(scopeDir).isDirectory()) {
111
- const pkgs = readdirSync(scopeDir).sort();
112
110
  console.log();
113
- for (const name of pkgs) {
111
+ for (const name of readdirSync(scopeDir).sort()) {
114
112
  try {
115
113
  const pkg = JSON.parse(readFileSync(join(scopeDir, name, 'package.json'), 'utf-8'));
116
114
  console.log(` ${pkg.name.padEnd(24)} ${pkg.version}`);
@@ -121,58 +119,32 @@ async function main() {
121
119
  process.exit(0);
122
120
  }
123
121
 
124
- // Launch local browser REPL (starts server + opens browser)
122
+ // --- Subcommands ---
123
+
125
124
  if (args[0] === 'check') {
126
- const targetDir = args[1] || '.';
127
125
  const { runCheck } = await import('../src/typecheck.js');
128
- const exitCode = await runCheck(targetDir, { quiet: args.includes('-q') || args.includes('--quiet') });
126
+ const exitCode = await runCheck(args[1] || '.', { quiet: args.includes('-q') || args.includes('--quiet') });
129
127
  process.exit(exitCode);
130
128
  }
131
129
 
132
- if (ripOptions.includes('-w') || ripOptions.includes('--web')) {
133
- console.log('🚀 Starting Rip browser REPL...\n');
134
-
135
- // Start the server and capture output to get the actual port
136
- const servePath = join(__dirname, '../scripts/serve.js');
137
- const serverProcess = spawn('bun', [servePath], {
138
- stdio: ['inherit', 'pipe', 'inherit'],
139
- detached: false,
130
+ if (args[0] === 'serve') {
131
+ let serverPath;
132
+ try {
133
+ serverPath = fileURLToPath(import.meta.resolve('@rip-lang/server/server'));
134
+ } catch {
135
+ console.error('Error: @rip-lang/server is not installed.\n\n bun add @rip-lang/server\n');
136
+ process.exit(1);
137
+ }
138
+ const result = spawnSync('bun', ['--preload', loaderPath, serverPath, ...args.slice(1)], {
139
+ stdio: 'inherit',
140
140
  env: process.env
141
141
  });
142
-
143
- let actualPort = null;
144
-
145
- // Capture server output and extract port
146
- serverProcess.stdout.on('data', (data) => {
147
- const output = data.toString();
148
- process.stdout.write(output);
149
-
150
- // Parse port from "Server running at http://localhost:PORT"
151
- if (!actualPort) {
152
- const match = output.match(/localhost:(\d+)/);
153
- if (match) {
154
- actualPort = match[1];
155
-
156
- // Open browser once we know the port
157
- setTimeout(() => {
158
- const openCmd = process.platform === 'darwin' ? 'open' :
159
- process.platform === 'win32' ? 'start' : 'xdg-open';
160
- execSync(`${openCmd} http://localhost:${actualPort}/`);
161
- console.log('\n📱 Press Ctrl+C to stop server\n');
162
- }, 300);
163
- }
164
- }
165
- });
166
-
167
- // Keep server running (don't exit)
168
- return;
142
+ process.exit(result.status ?? 1);
169
143
  }
170
144
 
171
- // Check if REPL should be started
172
- // Launch REPL if: no args AND stdin is a TTY (not piped), OR explicit -r flag
173
- const isTTY = process.stdin.isTTY;
174
- if ((args.length === 0 && isTTY) || ripOptions.includes('-r') || ripOptions.includes('--repl')) {
175
- // Spawn REPL with --experimental-vm-modules flag for proper ES module support
145
+ // --- REPL ---
146
+
147
+ if ((args.length === 0 && process.stdin.isTTY) || ripOptions.includes('-r') || ripOptions.includes('--repl')) {
176
148
  const replModule = join(__dirname, '../src/repl.js');
177
149
  const replProcess = spawn('bun', ['--experimental-vm-modules', '-e', `import('${replModule}').then(m => m.startREPL())`], {
178
150
  stdio: 'inherit',
@@ -182,6 +154,8 @@ async function main() {
182
154
  return;
183
155
  }
184
156
 
157
+ // --- Execute or compile ---
158
+
185
159
  const showTokens = ripOptions.includes('-t') || ripOptions.includes('--tokens');
186
160
  const showSExpr = ripOptions.includes('-s') || ripOptions.includes('--sexpr');
187
161
  const showCompiled = ripOptions.includes('-c') || ripOptions.includes('--compile');
@@ -189,133 +163,96 @@ async function main() {
189
163
  const generateMap = ripOptions.includes('-m') || ripOptions.includes('--map');
190
164
  const quiet = ripOptions.includes('-q') || ripOptions.includes('--quiet');
191
165
 
192
- const options = {
193
- showTokens,
194
- showSExpr,
195
- quiet,
196
- types: generateDts ? 'emit' : undefined,
197
- sourceMap: generateMap ? 'inline' : undefined,
198
- };
199
-
200
- // Find input file and output file from ripOptions only
201
166
  let inputFile = scriptFileIndex === -1 ? null : args[scriptFileIndex];
202
167
  let outputFile = null;
203
168
 
204
169
  for (let i = 0; i < ripOptions.length; i++) {
205
170
  if (ripOptions[i] === '-o' || ripOptions[i] === '--output') {
206
- outputFile = ripOptions[i + 1];
207
- i++;
171
+ outputFile = ripOptions[++i];
208
172
  }
209
173
  }
210
174
 
211
- // Helper to check if path is a regular file (not a directory)
212
- const isFile = (path) => existsSync(path) && statSync(path).isFile();
213
-
214
- // If no compile flags and file exists → execute the script instead of compiling it
175
+ // Execute script directly (no compile flags)
215
176
  const hasCompileFlag = showCompiled || showTokens || showSExpr || generateDts || generateMap || outputFile;
216
177
  if (inputFile && !hasCompileFlag && isFile(inputFile)) {
217
- const loaderPath = join(__dirname, '../rip-loader.js');
218
-
219
178
  if (inputFile.endsWith('.rip')) {
220
179
  const result = spawnSync('bun', ['--preload', loaderPath, inputFile, ...scriptArgs], {
221
180
  stdio: 'inherit',
222
181
  env: process.env
223
182
  });
224
183
  process.exit(result.status ?? 1);
225
- } else {
226
- // Non-.rip files (e.g. shebang scripts): compile, write temp file, execute
227
- const source = readFileSync(inputFile, 'utf-8');
228
- const compiler = new Compiler();
229
- const result = compiler.compile(source);
230
- const tmp = join(dirname(inputFile), `.${basename(inputFile)}.__rip__.mjs`);
231
- let exitCode = 0;
232
- try {
233
- writeFileSync(tmp, result.code);
234
- const r = spawnSync('bun', ['--preload', loaderPath, tmp, ...scriptArgs], { stdio: 'inherit', env: process.env });
235
- exitCode = r.status ?? 1;
236
- } catch (error) {
237
- exitCode = error.status || 1;
238
- }
239
- try { unlinkSync(tmp); } catch {}
240
- process.exit(exitCode);
241
184
  }
185
+
186
+ // Non-.rip files (e.g. shebang scripts): compile to temp file, execute, clean up
187
+ const source = readFileSync(inputFile, 'utf-8');
188
+ const result = new Compiler().compile(source);
189
+ const tmp = join(dirname(inputFile), `.${basename(inputFile)}.__rip__.mjs`);
190
+ let exitCode = 0;
191
+ try {
192
+ writeFileSync(tmp, result.code);
193
+ const r = spawnSync('bun', ['--preload', loaderPath, tmp, ...scriptArgs], { stdio: 'inherit', env: process.env });
194
+ exitCode = r.status ?? 1;
195
+ } catch (error) {
196
+ exitCode = error.status || 1;
197
+ }
198
+ try { unlinkSync(tmp); } catch {}
199
+ process.exit(exitCode);
242
200
  }
243
201
 
244
- // Fallback: Check for bin/ script in git repo root
202
+ // Fallback: look for bin/{command} in git repo root
245
203
  // Allows `rip migrate --status` to find and run {repo}/bin/migrate
246
- // Also triggers if inputFile is a directory (not a compilable file)
247
204
  if (inputFile && !inputFile.startsWith('-') && !isFile(inputFile)) {
248
205
  try {
249
- // Check if we're in a git repo
250
206
  const repoRoot = execSync('git rev-parse --show-toplevel', {
251
207
  encoding: 'utf-8',
252
208
  stdio: ['pipe', 'pipe', 'pipe']
253
209
  }).trim();
254
-
255
- // Look for bin/{command} in repo root
256
210
  const binScript = join(repoRoot, 'bin', inputFile);
257
-
258
211
  if (existsSync(binScript)) {
259
212
  const r = spawnSync(binScript, scriptArgs, { stdio: 'inherit', env: process.env });
260
213
  process.exit(r.status ?? 1);
261
214
  }
262
- } catch {
263
- // Not in a git repo, or git not available - fall through to normal error
264
- }
215
+ } catch {}
265
216
  }
266
217
 
218
+ // --- Compile ---
219
+
267
220
  let source;
268
221
 
269
222
  try {
270
- if (!inputFile) {
271
- // Read from stdin if no file specified (file descriptor 0)
272
- source = readFileSync(0, 'utf-8');
273
- } else {
274
- // Check if file exists and is a regular file
223
+ if (inputFile) {
275
224
  if (!isFile(inputFile)) {
276
225
  console.error(`Error: File not found: ${inputFile}`);
277
226
  process.exit(1);
278
227
  }
279
- // Read source file
280
228
  source = readFileSync(inputFile, 'utf-8');
229
+ } else {
230
+ source = readFileSync(0, 'utf-8');
281
231
  }
282
232
 
283
- // Compile
284
- const compiler = new Compiler(options);
233
+ const compiler = new Compiler({ showTokens, showSExpr, quiet,
234
+ types: generateDts ? 'emit' : undefined,
235
+ sourceMap: generateMap ? 'inline' : undefined,
236
+ });
285
237
  const result = compiler.compile(source);
286
238
 
287
- // Determine if we should show compiled output
288
- // Show compiled JS if: -c flag, OR no debug flags (default mode), OR saving to file
289
239
  const shouldShowCompiled = showCompiled || (!showTokens && !showSExpr && !generateDts) || outputFile;
290
240
 
291
- // Output
292
241
  if (outputFile) {
293
- // Save to file
294
242
  writeFileSync(outputFile, result.code, 'utf-8');
295
- if (!options.quiet) {
296
- console.log(`Compiled to ${outputFile}`);
297
- }
243
+ if (!quiet) console.log(`Compiled to ${outputFile}`);
298
244
  } else if (shouldShowCompiled) {
299
- // Show compiled output to stdout
300
- if (!options.quiet) {
301
- console.log(`// == JavaScript output by Rip ${VERSION} == //\n`);
302
- }
245
+ if (!quiet) console.log(`// == JavaScript output by Rip ${VERSION} == //\n`);
303
246
  console.log(result.code);
304
247
  }
305
248
 
306
- // Show .d.ts type declarations to stdout
307
249
  if (generateDts && result.dts) {
308
- if (!options.quiet) {
309
- console.log(`// == Type declarations == //\n`);
310
- }
250
+ if (!quiet) console.log(`// == Type declarations == //\n`);
311
251
  console.log(result.dts);
312
252
  }
313
-
314
253
  } catch (error) {
315
254
  console.error('Compilation Error:', error.message);
316
- if (error.stack) {
317
- console.error(error.stack);
318
- }
255
+ if (error.stack) console.error(error.stack);
319
256
  process.exit(1);
320
257
  }
321
258
  }
@@ -494,7 +494,7 @@ The `Compiler` class's lexer adapter reconstructs `new String()` wrapping from t
494
494
 
495
495
  **Solar** is a complete SLR(1) parser generator included with Rip — written in Rip, compiled by Rip, zero external dependencies.
496
496
 
497
- **Location:** `src/grammar/solar.rip` (916 lines)
497
+ **Location:** `src/grammar/solar.rip` (929 lines)
498
498
 
499
499
  ## Grammar Syntax
500
500
 
@@ -533,7 +533,7 @@ Parenthetical: [
533
533
  | Parse time | 12,500ms | ~50ms |
534
534
  | Dependencies | Many | Zero |
535
535
  | Self-hosting | No | Yes |
536
- | Code size | 2,285 LOC | 916 LOC |
536
+ | Code size | 2,285 LOC | 929 LOC |
537
537
 
538
538
  After modifying `src/grammar/grammar.rip`:
539
539
 
package/docs/RIP-LANG.md CHANGED
@@ -1232,8 +1232,7 @@ text =~ /line2/m # Works with /m flag
1232
1232
  Rip includes optional packages for full-stack development. All are written in Rip, have zero dependencies, and run on Bun.
1233
1233
 
1234
1234
  ```bash
1235
- bun add @rip-lang/api # Web framework
1236
- bun add @rip-lang/server # Production server
1235
+ bun add @rip-lang/server # Web framework + production server
1237
1236
  bun add @rip-lang/grid # Reactive data grid
1238
1237
  bun add @rip-lang/db # DuckDB server + client
1239
1238
  bun add @rip-lang/schema # ORM + validation
@@ -1241,12 +1240,12 @@ bun add @rip-lang/swarm # Parallel job runner
1241
1240
  bun add @rip-lang/csv # CSV parser + writer
1242
1241
  ```
1243
1242
 
1244
- ## @rip-lang/api — Web Framework
1243
+ ## @rip-lang/server — Web Framework & Production Server
1245
1244
 
1246
- Sinatra-style routing with `@` context magic and built-in validators.
1245
+ Sinatra-style routing with `@` context magic and built-in validators. Run with `rip serve` for multi-worker production deployment with hot reload, HTTPS, and mDNS.
1247
1246
 
1248
1247
  ```coffee
1249
- import { get, post, use, read, start, notFound } from '@rip-lang/api'
1248
+ import { get, post, use, read, start, notFound } from '@rip-lang/server'
1250
1249
 
1251
1250
  # Routes — return data directly
1252
1251
  get '/' -> { message: 'Hello!' }
@@ -1264,7 +1263,7 @@ get '/css/*' -> @send "public/#{@req.path.slice(5)}"
1264
1263
  notFound -> @send 'index.html', 'text/html; charset=UTF-8'
1265
1264
 
1266
1265
  # Middleware
1267
- import { cors, logger, sessions } from '@rip-lang/api/middleware'
1266
+ import { cors, logger, sessions } from '@rip-lang/server/middleware'
1268
1267
 
1269
1268
  use logger()
1270
1269
  use cors origin: '*'
@@ -1277,6 +1276,15 @@ after -> console.log "#{@req.method} #{@req.path} - #{Date.now() - @start}ms"
1277
1276
  start port: 3000
1278
1277
  ```
1279
1278
 
1279
+ ### Serving
1280
+
1281
+ ```bash
1282
+ rip serve # Start (uses ./index.rip)
1283
+ rip serve --static # No watching, no hot reload (production)
1284
+ rip serve myapp # Named (accessible at myapp.local)
1285
+ rip serve http:3000 # HTTP on specific port
1286
+ ```
1287
+
1280
1288
  ### read() Validators
1281
1289
 
1282
1290
  ```coffee
@@ -1298,25 +1306,14 @@ ids = read 'ids', 'ids' # "1,2,3" → [1, 2, 3]
1298
1306
  slug = read 'slug', 'slug' # URL-safe slug
1299
1307
  ```
1300
1308
 
1301
- ## @rip-lang/server — Production Server
1302
-
1303
- Multi-worker process manager with hot reload, automatic HTTPS, and mDNS.
1304
-
1305
- ```bash
1306
- rip-server # Start (uses ./index.rip)
1307
- rip-server -w # With file watching + hot-reload
1308
- rip-server myapp # Named (accessible at myapp.local)
1309
- rip-server http:3000 # HTTP on specific port
1310
- ```
1311
-
1312
1309
  ## Rip UI — Reactive Web Framework (built into rip-lang)
1313
1310
 
1314
1311
  Zero-build reactive framework. Ships the compiler to the browser and compiles `.rip` components on demand. File-based routing, unified reactive stash, and SSE hot reload.
1315
1312
 
1316
1313
  ```coffee
1317
1314
  # Server setup (index.rip)
1318
- import { get, use, start, notFound } from '@rip-lang/api'
1319
- import { serve } from '@rip-lang/api/serve'
1315
+ import { get, use, start, notFound } from '@rip-lang/server'
1316
+ import { serve } from '@rip-lang/server/middleware'
1320
1317
 
1321
1318
  dir = import.meta.dir
1322
1319
  use serve dir: dir, watch: true
@@ -1412,8 +1409,8 @@ user.save!()
1412
1409
  A complete API server in Rip:
1413
1410
 
1414
1411
  ```coffee
1415
- import { get, post, use, read, start, notFound } from '@rip-lang/api'
1416
- import { cors, logger } from '@rip-lang/api/middleware'
1412
+ import { get, post, use, read, start, notFound } from '@rip-lang/server'
1413
+ import { cors, logger } from '@rip-lang/server/middleware'
1417
1414
 
1418
1415
  use logger()
1419
1416
  use cors origin: '*'
package/docs/dist/rip.js CHANGED
@@ -8353,7 +8353,7 @@ globalThis.zip ??= (...a) => a[0].map((_, i) => a.map(b => b[i]));
8353
8353
  }
8354
8354
  // src/browser.js
8355
8355
  var VERSION = "3.13.13";
8356
- var BUILD_DATE = "2026-02-25@00:01:59GMT";
8356
+ var BUILD_DATE = "2026-02-25@05:34:54GMT";
8357
8357
  if (typeof globalThis !== "undefined") {
8358
8358
  if (!globalThis.__rip)
8359
8359
  new Function(getReactiveRuntime())();
@@ -534,7 +534,7 @@ globalThis.sleep ??= (ms) => new Promise(r => setTimeout(r, ms));
534
534
  globalThis.todo ??= (msg) => { throw new Error(msg || "Not implemented"); };
535
535
  globalThis.warn ??= console.warn;
536
536
  globalThis.zip ??= (...a) => a[0].map((_, i) => a.map(b => b[i]));
537
- `}function _1(){return new C({}).getReactiveRuntime()}function Z1(){return new C({}).getComponentRuntime()}var H2="3.13.13",z2="2026-02-25@00:01:59GMT";if(typeof globalThis<"u"){if(!globalThis.__rip)Function(_1())();if(!globalThis.__ripComponent)Function(Z1())()}var g3=($)=>{let W=$.match(/^[ \t]*(?=\S)/gm),F=Math.min(...(W||[]).map((U)=>U.length));return $.replace(RegExp(`^[ ]{${F}}`,"gm"),"").trim()};async function O2(){let $=[],W=document.querySelector('script[src$="rip.min.js"], script[src$="rip.js"]'),F=W?.getAttribute("data-src");if(F){for(let u of F.trim().split(/\s+/))if(u)$.push({url:u})}for(let u of document.querySelectorAll('script[type="text/rip"]'))if(u.src)$.push({url:u.src});else{let f=g3(u.textContent);if(f)$.push({code:f})}if($.length>0){await Promise.all($.map(async(Y)=>{if(!Y.url)return;try{let A=await fetch(Y.url);if(!A.ok){console.error(`Rip: failed to fetch ${Y.url} (${A.status})`);return}Y.code=await A.text()}catch(A){console.error(`Rip: failed to fetch ${Y.url}:`,A.message)}}));let u={skipRuntimes:!0,skipExports:!0},f=[];for(let Y of $){if(!Y.code)continue;try{f.push(n(Y.code,u))}catch(A){console.error("Rip compile error:",A.message)}}if(f.length>0){let Y=f.join(`
537
+ `}function _1(){return new C({}).getReactiveRuntime()}function Z1(){return new C({}).getComponentRuntime()}var H2="3.13.13",z2="2026-02-25@05:34:54GMT";if(typeof globalThis<"u"){if(!globalThis.__rip)Function(_1())();if(!globalThis.__ripComponent)Function(Z1())()}var g3=($)=>{let W=$.match(/^[ \t]*(?=\S)/gm),F=Math.min(...(W||[]).map((U)=>U.length));return $.replace(RegExp(`^[ ]{${F}}`,"gm"),"").trim()};async function O2(){let $=[],W=document.querySelector('script[src$="rip.min.js"], script[src$="rip.js"]'),F=W?.getAttribute("data-src");if(F){for(let u of F.trim().split(/\s+/))if(u)$.push({url:u})}for(let u of document.querySelectorAll('script[type="text/rip"]'))if(u.src)$.push({url:u.src});else{let f=g3(u.textContent);if(f)$.push({code:f})}if($.length>0){await Promise.all($.map(async(Y)=>{if(!Y.url)return;try{let A=await fetch(Y.url);if(!A.ok){console.error(`Rip: failed to fetch ${Y.url} (${A.status})`);return}Y.code=await A.text()}catch(A){console.error(`Rip: failed to fetch ${Y.url}:`,A.message)}}));let u={skipRuntimes:!0,skipExports:!0},f=[];for(let Y of $){if(!Y.code)continue;try{f.push(n(Y.code,u))}catch(A){console.error("Rip compile error:",A.message)}}if(f.length>0){let Y=f.join(`
538
538
  `),A=W?.getAttribute("data-mount");if(A){let _=W.getAttribute("data-target")||"body";Y+=`
539
539
  ${A}.mount(${JSON.stringify(_)});`}try{await(0,eval)(`(async()=>{
540
540
  ${Y}
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.13",
3
+ "version": "3.13.14",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
@@ -20,7 +20,7 @@ independent implementations derived from the same grammar specification.
20
20
  | File | Lines | Purpose |
21
21
  |------|-------|---------|
22
22
  | `grammar.rip` | 944 | Grammar specification — defines all syntax rules |
23
- | `solar.rip` | 926 | SLR(1) parser generator — produces table-driven parsers |
23
+ | `solar.rip` | 929 | SLR(1) parser generator — produces table-driven parsers |
24
24
  | `lunar.rip` | 2,412 | Predictive recursive descent generator — produces PRD parsers |
25
25
 
26
26
  ---