@velox0/cerver 0.4.1 → 0.4.2

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.
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ tests:
9
+ name: Tests
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v6
15
+
16
+ - name: Setup pnpm
17
+ uses: pnpm/action-setup@v6
18
+ with:
19
+ version: 10
20
+ run_install: false
21
+
22
+ - name: Setup Node.js
23
+ uses: actions/setup-node@v6
24
+ with:
25
+ node-version: 24
26
+ cache: pnpm
27
+
28
+ - name: Install dependencies
29
+ run: pnpm install --frozen-lockfile
30
+
31
+ - name: Run runtime tests
32
+ run: make test-runtime
33
+
34
+ - name: Run JS tests
35
+ run: pnpm test
@@ -35,6 +35,9 @@ jobs:
35
35
  - name: Install dependencies
36
36
  run: pnpm install --frozen-lockfile
37
37
 
38
+ - name: Run runtime tests
39
+ run: make test-runtime
40
+
38
41
  - name: Run tests
39
42
  run: pnpm test
40
43
 
package/Makefile ADDED
@@ -0,0 +1,26 @@
1
+ CC ?= cc
2
+ CFLAGS ?= -std=c11 -Wall -Wextra -O2
3
+
4
+ RUNTIME_SRCS = \
5
+ runtime/http_parser.c \
6
+ runtime/http_writer.c \
7
+ runtime/router.c \
8
+ runtime/static.c \
9
+ runtime/mime.c \
10
+ runtime/server.c
11
+
12
+ TEST_SRCS = runtime/tests/runtime_tests.c \
13
+ runtime/tests/minunit.c
14
+ TEST_BIN = build/runtime_tests
15
+
16
+ .PHONY: test-runtime clean
17
+
18
+ test-runtime: $(TEST_BIN)
19
+ ./$(TEST_BIN)
20
+
21
+ $(TEST_BIN): $(RUNTIME_SRCS) $(TEST_SRCS) runtime/cerver.h
22
+ mkdir -p build
23
+ $(CC) $(CFLAGS) -Iruntime -o $(TEST_BIN) $(RUNTIME_SRCS) $(TEST_SRCS) -pthread
24
+
25
+ clean:
26
+ rm -rf build
package/bin/cerver.js CHANGED
@@ -7,6 +7,17 @@ const pkg = require("../package.json");
7
7
 
8
8
  const program = new Command();
9
9
 
10
+ function restoreTty() {
11
+ const stdin = process.stdin;
12
+ if (!stdin || !stdin.isTTY || typeof stdin.setRawMode !== "function") return;
13
+ if (!stdin.isRaw) return;
14
+ try {
15
+ stdin.setRawMode(false);
16
+ } catch (_) {}
17
+ }
18
+
19
+ process.on("exit", restoreTty);
20
+
10
21
  program
11
22
  .name("cerver")
12
23
  .description("Compile restricted JavaScript into native C server binaries")
@@ -16,6 +16,7 @@ async function dev(opts) {
16
16
  let serverProcess = null;
17
17
  let building = false;
18
18
  let pendingRebuild = false;
19
+ let shuttingDown = false;
19
20
 
20
21
  const port = opts.port || null;
21
22
 
@@ -40,7 +41,7 @@ async function dev(opts) {
40
41
  try {
41
42
  await build({
42
43
  embed: opts.embed !== undefined ? opts.embed : true,
43
- minify: false, /* Skip minification in dev for speed */
44
+ minify: false /* Skip minification in dev for speed */,
44
45
  static: false,
45
46
  });
46
47
 
@@ -84,6 +85,16 @@ async function dev(opts) {
84
85
  });
85
86
  }
86
87
 
88
+ function waitForExit(proc) {
89
+ if (!proc) return Promise.resolve();
90
+ return new Promise((resolve) => {
91
+ const done = () => resolve();
92
+ proc.once("exit", done);
93
+ proc.once("close", done);
94
+ proc.once("error", done);
95
+ });
96
+ }
97
+
87
98
  /* ---- Watch for changes ---- */
88
99
  const watchPaths = [
89
100
  path.join(projectDir, "app"),
@@ -92,11 +103,7 @@ async function dev(opts) {
92
103
  ].filter((p) => fs.existsSync(p));
93
104
 
94
105
  const watcher = chokidar.watch(watchPaths, {
95
- ignored: [
96
- /(^|[\/\\])\./, /* dotfiles */
97
- /node_modules/,
98
- /dist/,
99
- ],
106
+ ignored: [/(^|[\/\\])\./ /* dotfiles */, /node_modules/, /dist/],
100
107
  persistent: true,
101
108
  ignoreInitial: true,
102
109
  awaitWriteFinish: {
@@ -125,18 +132,25 @@ async function dev(opts) {
125
132
  .on("unlink", scheduleRebuild);
126
133
 
127
134
  /* ---- Graceful shutdown ---- */
128
- function shutdown() {
135
+ async function shutdown() {
136
+ if (shuttingDown) return;
137
+ shuttingDown = true;
129
138
  console.log("\n cerver dev: shutting down...");
130
139
  watcher.close();
131
140
  if (debounceTimer) clearTimeout(debounceTimer);
132
141
  if (serverProcess) {
133
142
  serverProcess.kill("SIGTERM");
143
+ await waitForExit(serverProcess);
134
144
  }
135
145
  process.exit(0);
136
146
  }
137
147
 
138
- process.on("SIGINT", shutdown);
139
- process.on("SIGTERM", shutdown);
148
+ process.on("SIGINT", () => {
149
+ void shutdown();
150
+ });
151
+ process.on("SIGTERM", () => {
152
+ void shutdown();
153
+ });
140
154
 
141
155
  /* ---- Start ---- */
142
156
  console.log("\n cerver dev — watching for changes\n");
@@ -17,13 +17,7 @@ function newProject(name) {
17
17
  console.log(`\n Creating cerver project: ${name}\n`);
18
18
 
19
19
  // Create directory structure
20
- const dirs = [
21
- "",
22
- "app",
23
- "app/routes",
24
- "public",
25
- "dist",
26
- ];
20
+ const dirs = ["", "app", "app/routes", "public", "dist"];
27
21
 
28
22
  for (const dir of dirs) {
29
23
  fs.mkdirSync(path.join(projectDir, dir), { recursive: true });
@@ -35,13 +29,13 @@ function newProject(name) {
35
29
  // cerver.config.js
36
30
  fs.copyFileSync(
37
31
  path.join(templatesDir, "cerver.config.js"),
38
- path.join(projectDir, "cerver.config.js")
32
+ path.join(projectDir, "cerver.config.js"),
39
33
  );
40
34
 
41
35
  // Default route
42
36
  fs.copyFileSync(
43
37
  path.join(templatesDir, "index.route.js"),
44
- path.join(projectDir, "app", "routes", "index.js")
38
+ path.join(projectDir, "app", "routes", "index.js"),
45
39
  );
46
40
 
47
41
  // Default public/index.html
@@ -54,100 +48,300 @@ function newProject(name) {
54
48
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
55
49
  <title>${name}</title>
56
50
  <link rel="icon" href="/favicon.ico" type="image/x-icon">
51
+ <link rel="preconnect" href="https://fonts.googleapis.com">
52
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
53
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Unbounded:wght@500;700&display=swap" rel="stylesheet">
57
54
  <style>
58
55
  :root {
59
- --bg-color: #0f172a;
60
- --text-color: #f8fafc;
61
- --accent-color: #38bdf8;
62
- --card-bg: rgba(30, 41, 59, 0.7);
56
+ --ink: #121316;
57
+ --muted: #4a4f57;
58
+ --paper: #f7f4f1;
59
+ --glass: rgba(255, 255, 255, 0.7);
60
+ --edge: rgba(255, 255, 255, 0.6);
61
+ --pink: #ff7abf;
62
+ --peach: #ffb380;
63
+ --mint: #7de3c9;
64
+ --blue: #6aa9ff;
65
+ --shadow: rgba(18, 19, 22, 0.18);
66
+ }
67
+ * {
68
+ box-sizing: border-box;
63
69
  }
64
70
  body {
65
71
  margin: 0;
66
- padding: 0;
67
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
68
- background-color: var(--bg-color);
69
- color: var(--text-color);
70
- display: flex;
71
- flex-direction: column;
72
- align-items: center;
73
- justify-content: center;
72
+ font-family: "Space Grotesk", "Segoe UI", sans-serif;
73
+ color: var(--ink);
74
74
  min-height: 100vh;
75
- background: radial-gradient(circle at top right, #1e293b, #0f172a);
76
- }
77
- .container {
78
- background: var(--card-bg);
79
- backdrop-filter: blur(12px);
80
- -webkit-backdrop-filter: blur(12px);
81
- border: 1px solid rgba(255, 255, 255, 0.1);
82
- padding: 3rem;
83
- border-radius: 16px;
84
- text-align: center;
85
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
86
- max-width: 500px;
87
- width: 90%;
88
- animation: fadeUp 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
89
- opacity: 0;
90
- transform: translateY(20px);
91
- }
92
- @keyframes fadeUp {
93
- to { opacity: 1; transform: translateY(0); }
94
- }
95
- .logo {
96
- width: 120px;
97
- height: 120px;
98
- margin-bottom: 1.5rem;
99
- border-radius: 24%;
100
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
101
- transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
102
- }
103
- .logo:hover {
104
- transform: scale(1.08) rotate(-3deg);
75
+ display: grid;
76
+ place-items: center;
77
+ background-color: var(--paper);
78
+ background-image:
79
+ radial-gradient(circle at 15% 10%, rgba(255, 122, 191, 0.35), transparent 45%),
80
+ radial-gradient(circle at 85% 12%, rgba(255, 179, 128, 0.4), transparent 50%),
81
+ radial-gradient(circle at 82% 82%, rgba(122, 214, 255, 0.35), transparent 55%),
82
+ radial-gradient(circle at 20% 80%, rgba(125, 227, 201, 0.35), transparent 55%),
83
+ linear-gradient(120deg, #f7f4f1 0%, #f2f7ff 100%);
84
+ }
85
+ body::before,
86
+ body::after {
87
+ content: "";
88
+ position: fixed;
89
+ inset: -20% -10%;
90
+ pointer-events: none;
91
+ }
92
+ body::before {
93
+ background:
94
+ conic-gradient(from 200deg at 50% 50%, rgba(255, 122, 191, 0.08), rgba(122, 214, 255, 0.08), rgba(125, 227, 201, 0.08), rgba(255, 179, 128, 0.08));
95
+ filter: blur(60px);
96
+ opacity: 0.6;
97
+ }
98
+ body::after {
99
+ background-image: radial-gradient(circle, rgba(18, 19, 22, 0.04) 1px, transparent 1px);
100
+ background-size: 24px 24px;
101
+ opacity: 0.6;
102
+ }
103
+ .stage {
104
+ width: min(1100px, 92vw);
105
+ padding: 4rem 0;
106
+ }
107
+ .frame {
108
+ position: relative;
109
+ display: grid;
110
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
111
+ gap: 2.5rem;
112
+ align-items: center;
113
+ padding: clamp(2.5rem, 4vw, 4rem);
114
+ border-radius: 32px;
115
+ background: var(--glass);
116
+ border: 1px solid var(--edge);
117
+ box-shadow:
118
+ 0 40px 90px -40px var(--shadow),
119
+ inset 0 1px 0 rgba(255, 255, 255, 0.7);
120
+ backdrop-filter: blur(18px);
121
+ -webkit-backdrop-filter: blur(18px);
122
+ animation: rise 0.8s cubic-bezier(0.16, 1, 0.3, 1) both;
105
123
  }
106
124
  h1 {
107
- margin: 0 0 1rem 0;
108
- font-size: 2.5rem;
109
- font-weight: 700;
110
- letter-spacing: -0.025em;
125
+ margin: 0.6rem 0 1rem;
126
+ font-family: "Unbounded", "Space Grotesk", sans-serif;
127
+ font-size: clamp(2.2rem, 4vw, 4.4rem);
128
+ letter-spacing: 0.08em;
129
+ text-transform: uppercase;
111
130
  }
112
131
  p {
132
+ margin: 0 0 1.5rem;
133
+ font-size: 1.1rem;
134
+ line-height: 1.7;
135
+ color: var(--muted);
136
+ max-width: 32rem;
137
+ }
138
+ .steps {
139
+ margin: 0 0 1.8rem;
140
+ }
141
+ .steps-box {
142
+ padding: 1rem 1.2rem;
143
+ border-radius: 18px;
144
+ background: rgba(255, 255, 255, 0.35);
145
+ border: 1px solid rgba(18, 19, 22, 0.08);
146
+ backdrop-filter: blur(8px);
147
+ -webkit-backdrop-filter: blur(8px);
148
+ }
149
+ .steps-title {
150
+ margin: 0 0 0.7rem;
151
+ font-size: 0.75rem;
152
+ text-transform: uppercase;
153
+ letter-spacing: 0.18em;
154
+ color: var(--muted);
155
+ }
156
+ .steps-list {
113
157
  margin: 0;
114
- color: #94a3b8;
115
- font-size: 1.125rem;
116
- line-height: 1.6;
117
- }
118
- .badge {
119
- display: inline-block;
120
- margin-top: 2rem;
121
- padding: 0.5rem 1rem;
122
- background: rgba(56, 189, 248, 0.1);
123
- color: var(--accent-color);
124
- border-radius: 9999px;
125
- font-size: 0.875rem;
158
+ padding-left: 1.1rem;
159
+ display: grid;
160
+ gap: 0.55rem;
161
+ color: var(--muted);
162
+ }
163
+ .steps-list li::marker {
164
+ color: var(--muted);
165
+ }
166
+ .steps-list strong {
167
+ color: #2c3138;
168
+ font-weight: 600;
169
+ }
170
+ .meta {
171
+ display: inline-flex;
172
+ align-items: center;
173
+ gap: 0.6rem;
174
+ font-weight: 600;
175
+ color: #2c3138;
176
+ }
177
+ .dot {
178
+ width: 10px;
179
+ height: 10px;
180
+ border-radius: 50%;
181
+ background: linear-gradient(135deg, var(--pink), var(--blue));
182
+ box-shadow: 0 0 12px rgba(122, 214, 255, 0.8);
183
+ }
184
+ .footer {
185
+ margin-top: 1.8rem;
186
+ text-align: center;
187
+ font-size: 0.9rem;
188
+ color: var(--muted);
189
+ }
190
+ .footer a {
191
+ color: inherit;
192
+ text-decoration: none;
126
193
  font-weight: 600;
127
- border: 1px solid rgba(56, 189, 248, 0.2);
194
+ }
195
+ .footer a:hover {
196
+ text-decoration: underline;
197
+ }
198
+ .art {
199
+ position: relative;
200
+ display: grid;
201
+ place-items: center;
202
+ min-height: 280px;
203
+ }
204
+ .art::before {
205
+ content: "";
206
+ position: absolute;
207
+ width: min(320px, 70vw);
208
+ aspect-ratio: 1;
209
+ border-radius: 28%;
210
+ background: linear-gradient(140deg, rgba(255, 122, 191, 0.25), rgba(122, 214, 255, 0.2), rgba(125, 227, 201, 0.25));
211
+ filter: blur(10px);
212
+ transform: rotate(18deg);
213
+ }
214
+ .art img {
215
+ width: min(300px, 65vw);
216
+ height: auto;
217
+ border-radius: 18%;
218
+ filter: drop-shadow(0 25px 40px rgba(18, 19, 22, 0.25));
219
+ animation: float 6s ease-in-out infinite;
220
+ }
221
+ @keyframes float {
222
+ 0%, 100% { transform: translateY(0px) rotate(-2deg); }
223
+ 50% { transform: translateY(-12px) rotate(2deg); }
224
+ }
225
+ @keyframes rise {
226
+ from { opacity: 0; transform: translateY(18px) scale(0.98); }
227
+ to { opacity: 1; transform: translateY(0) scale(1); }
228
+ }
229
+ @media (max-width: 760px) {
230
+ .stage {
231
+ padding: 2.5rem 0;
232
+ }
233
+ .frame {
234
+ padding: 2.2rem;
235
+ }
236
+ h1 {
237
+ letter-spacing: 0.05em;
238
+ }
239
+ .art {
240
+ order: -1;
241
+ }
242
+ }
243
+ @media (prefers-color-scheme: dark) {
244
+ :root {
245
+ --ink: #f7f8fb;
246
+ --muted: #c2c8d0;
247
+ --paper: #0b0e13;
248
+ --glass: rgba(15, 19, 28, 0.78);
249
+ --edge: rgba(255, 255, 255, 0.12);
250
+ --shadow: rgba(0, 0, 0, 0.65);
251
+ }
252
+ body {
253
+ background-image:
254
+ radial-gradient(circle at 15% 10%, rgba(255, 122, 191, 0.45), transparent 45%),
255
+ radial-gradient(circle at 85% 12%, rgba(255, 179, 128, 0.45), transparent 50%),
256
+ radial-gradient(circle at 82% 82%, rgba(106, 169, 255, 0.45), transparent 55%),
257
+ radial-gradient(circle at 20% 80%, rgba(125, 227, 201, 0.4), transparent 55%),
258
+ linear-gradient(120deg, #0b0e13 0%, #121826 100%);
259
+ }
260
+ body::before {
261
+ opacity: 0.95;
262
+ filter: blur(90px);
263
+ }
264
+ body::after {
265
+ opacity: 0.2;
266
+ }
267
+ .frame {
268
+ box-shadow:
269
+ 0 50px 120px -40px rgba(0, 0, 0, 0.75),
270
+ 0 0 120px rgba(106, 169, 255, 0.25),
271
+ inset 0 1px 0 rgba(255, 255, 255, 0.08);
272
+ }
273
+ .steps-box {
274
+ background: rgba(6, 8, 14, 0.45);
275
+ border: 1px solid rgba(255, 255, 255, 0.12);
276
+ }
277
+ .steps-list strong {
278
+ color: #eef1f6;
279
+ }
280
+ .meta {
281
+ color: #e6e9ef;
282
+ }
283
+ .dot {
284
+ box-shadow: 0 0 18px rgba(255, 122, 191, 0.9);
285
+ }
286
+ .art::before {
287
+ filter: blur(18px);
288
+ }
289
+ .art img {
290
+ filter: drop-shadow(0 30px 60px rgba(106, 169, 255, 0.45));
291
+ }
292
+ .footer {
293
+ color: #c2c8d0;
294
+ }
295
+ }
296
+ @media (prefers-reduced-motion: reduce) {
297
+ .frame,
298
+ .art img {
299
+ animation: none;
300
+ }
128
301
  }
129
302
  </style>
130
303
  </head>
131
304
  <body>
132
- <div class="container">
133
- <img src="/cerver.png" alt="cerver logo" class="logo">
134
- <h1>${name}</h1>
135
- <p>Your ultra-fast, native web application is running.</p>
136
- <div class="badge">Powered by cerver</div>
137
- </div>
305
+ <main class="stage">
306
+ <section class="frame">
307
+ <div class="copy">
308
+ <h1>${name}</h1>
309
+ <p>Native-speed web apps with a glassy sheen. Your new cerver project is wired, built, and ready to ship.</p>
310
+ <div class="steps">
311
+ <div class="steps-box">
312
+ <div class="steps-title">Next steps</div>
313
+ <ul class="steps-list">
314
+ <li>Edit <strong>public/index.html</strong> to change this page.</li>
315
+ <li>Config lives in <strong>cerver.config.js</strong> at the project root.</li>
316
+ </ul>
317
+ </div>
318
+ </div>
319
+ <div class="meta">
320
+ <span class="dot"></span>
321
+ <span>Powered by cerver</span>
322
+ </div>
323
+ </div>
324
+ <div class="art">
325
+ <img src="/cerver.png" alt="cerver logo">
326
+ </div>
327
+ </section>
328
+ <footer class="footer">
329
+ <a href="https://github.com/velox0/cerver" target="_blank" rel="noreferrer">GitHub: github.com/velox0/cerver</a>
330
+ </footer>
331
+ </main>
138
332
  </body>
139
333
  </html>
140
- `
334
+ `,
141
335
  );
142
336
 
143
337
  // Copy standard static assets
144
338
  fs.copyFileSync(
145
339
  path.join(templatesDir, "cerver.png"),
146
- path.join(projectDir, "public", "cerver.png")
340
+ path.join(projectDir, "public", "cerver.png"),
147
341
  );
148
342
  fs.copyFileSync(
149
343
  path.join(templatesDir, "favicon.ico"),
150
- path.join(projectDir, "public", "favicon.ico")
344
+ path.join(projectDir, "public", "favicon.ico"),
151
345
  );
152
346
 
153
347
  // package.json
@@ -164,8 +358,8 @@ function newProject(name) {
164
358
  },
165
359
  },
166
360
  null,
167
- 2
168
- ) + "\n"
361
+ 2,
362
+ ) + "\n",
169
363
  );
170
364
 
171
365
  console.log(" Created:");
@@ -6,7 +6,7 @@ const IR = require("./types");
6
6
  * Transform a validated AST into an IR route descriptor.
7
7
  *
8
8
  * @param {object} ast - ESTree AST (validated)
9
- * @param {string} urlPath - The route path (e.g. "/art/:key")
9
+ * @param {string} urlPath - The route path (e.g. "/groups/:group_id")
10
10
  * @returns {IRRoute[]} — one IRRoute per exported handler (GET, POST)
11
11
  */
12
12
  function transformFile(ast, urlPath) {
@@ -135,11 +135,7 @@ function transformReturn(node, ctx) {
135
135
  }
136
136
 
137
137
  /* Plain expression return — treat as text */
138
- return IR.IRReturn(
139
- "text",
140
- 200,
141
- transformExpression(arg, ctx)
142
- );
138
+ return IR.IRReturn("text", 200, transformExpression(arg, ctx));
143
139
  }
144
140
 
145
141
  /**
@@ -148,9 +144,13 @@ function transformReturn(node, ctx) {
148
144
  function transformIf(node, ctx) {
149
145
  const condition = transformExpression(node.test, ctx);
150
146
 
151
- const thenBlock = node.consequent.type === "BlockStatement"
152
- ? transformBlock(node.consequent, ctx)
153
- : { variables: [], body: [transformStatement(node.consequent, ctx)].filter(Boolean) };
147
+ const thenBlock =
148
+ node.consequent.type === "BlockStatement"
149
+ ? transformBlock(node.consequent, ctx)
150
+ : {
151
+ variables: [],
152
+ body: [transformStatement(node.consequent, ctx)].filter(Boolean),
153
+ };
154
154
 
155
155
  let elseBody = null;
156
156
  if (node.alternate) {
@@ -208,28 +208,25 @@ function transformExpression(node, ctx) {
208
208
  return IR.IRComparison(
209
209
  node.operator,
210
210
  transformExpression(node.left, ctx),
211
- transformExpression(node.right, ctx)
211
+ transformExpression(node.right, ctx),
212
212
  );
213
213
 
214
214
  case "LogicalExpression":
215
215
  return IR.IRLogical(
216
216
  node.operator,
217
217
  transformExpression(node.left, ctx),
218
- transformExpression(node.right, ctx)
218
+ transformExpression(node.right, ctx),
219
219
  );
220
220
 
221
221
  case "UnaryExpression":
222
- return IR.IRUnary(
223
- node.operator,
224
- transformExpression(node.argument, ctx)
225
- );
222
+ return IR.IRUnary(node.operator, transformExpression(node.argument, ctx));
226
223
 
227
224
  case "ConditionalExpression":
228
225
  /* a ? b : c → IR.IRIf as expression — simplify to if/else for now */
229
226
  return IR.IRComparison(
230
227
  "?:",
231
228
  transformExpression(node.test, ctx),
232
- transformExpression(node.consequent, ctx)
229
+ transformExpression(node.consequent, ctx),
233
230
  );
234
231
 
235
232
  case "MemberExpression":
@@ -274,10 +271,7 @@ function transformMemberExpr(node, ctx) {
274
271
  }
275
272
 
276
273
  /* req.method, req.path */
277
- if (
278
- node.object.type === "Identifier" &&
279
- node.object.name === ctx.reqName
280
- ) {
274
+ if (node.object.type === "Identifier" && node.object.name === ctx.reqName) {
281
275
  const prop = node.property.name || node.property.value;
282
276
  if (prop === "method") return IR.IRIdentifier("req->method");
283
277
  if (prop === "path") return IR.IRIdentifier("req->path");
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@velox0/cerver",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Compile restricted JavaScript server logic into optimized native C binaries",
5
- "main": "lib/index.js",
5
+ "main": "bin/cerver.js",
6
6
  "bin": {
7
7
  "cerver": "bin/cerver.js"
8
8
  },