@catmint/adapter-node 0.0.0-prealpha.6 → 0.0.0-prealpha.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server-template.d.ts","sourceRoot":"","sources":["../src/server-template.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,UAAU,GAAG,MAAM,CAAC;CACnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,aAAa,GACrB,MAAM,CA8YR"}
1
+ {"version":3,"file":"server-template.d.ts","sourceRoot":"","sources":["../src/server-template.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,UAAU,GAAG,MAAM,CAAC;CACnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,aAAa,GACrB,MAAM,CA+fR"}
@@ -19,6 +19,7 @@ import { fileURLToPath } from "node:url";
19
19
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
20
20
  const CLIENT_DIR = resolve(__dirname, "../client");
21
21
  const STATIC_DIR = resolve(__dirname, "../static");
22
+ const PRERENDERED_DIR = resolve(__dirname, "../prerendered");
22
23
 
23
24
  const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : ${options.port};
24
25
  const HOST = process.env.HOST ?? ${JSON.stringify(options.host)};
@@ -199,6 +200,28 @@ async function pipeWebStreamToResponse(webStream, res) {
199
200
  }
200
201
  }
201
202
 
203
+ function nodeReqToWebRequest(req) {
204
+ const protocol = "http";
205
+ const host = req.headers.host || \`\${HOST}:\${PORT}\`;
206
+ const reqUrl = new URL(req.url || "/", \`\${protocol}://\${host}\`);
207
+ const headers = new Headers();
208
+ for (const [key, value] of Object.entries(req.headers)) {
209
+ if (value) {
210
+ if (Array.isArray(value)) {
211
+ for (const v of value) {
212
+ headers.append(key, v);
213
+ }
214
+ } else {
215
+ headers.set(key, value);
216
+ }
217
+ }
218
+ }
219
+ return new Request(reqUrl.href, {
220
+ method: req.method || "GET",
221
+ headers,
222
+ });
223
+ }
224
+
202
225
  const server = createServer(async (req, res) => {
203
226
  ${options.headerPreset === "baseline"
204
227
  ? ` // Baseline security headers
@@ -212,8 +235,17 @@ ${options.headerPreset === "baseline"
212
235
  const method = (req.method || "GET").toUpperCase();
213
236
 
214
237
  try {
215
- // 1. Serve static assets from dist/static/ (no cache)
238
+ // 1. Serve client assets from dist/client/ (immutable for hashed assets)
239
+ // These have hashed filenames and never conflict with app routes.
216
240
  {
241
+ const filePath = join(CLIENT_DIR, pathname);
242
+ const served = await serveStaticFile(res, filePath, pathname.includes("/assets/"));
243
+ if (served) return;
244
+ }
245
+
246
+ // 2. Pre-rendered static pages WITHOUT middleware: serve directly from
247
+ // dist/static/ as a fast path — no middleware execution needed.
248
+ if (method === "GET") {
217
249
  let staticPath = pathname;
218
250
  if (!extname(staticPath)) {
219
251
  staticPath = staticPath.endsWith("/")
@@ -225,14 +257,54 @@ ${options.headerPreset === "baseline"
225
257
  if (served) return;
226
258
  }
227
259
 
228
- // 2. Serve client assets from dist/client/ (immutable for hashed assets)
229
- {
230
- const filePath = join(CLIENT_DIR, pathname);
231
- const served = await serveStaticFile(res, filePath, pathname.includes("/assets/"));
232
- if (served) return;
260
+ // 3. Middleware execution runs for all remaining routes (server functions,
261
+ // endpoints, prerendered pages with middleware, RSC-rendered pages).
262
+ // For RSC flight requests, run middleware against the TARGET page path
263
+ // (from ?path= query param), not the literal /__catmint/rsc pathname.
264
+ var middlewareHeaders = null;
265
+ if (ssrEntry.executeMiddleware) {
266
+ try {
267
+ var middlewarePath = pathname;
268
+ if (pathname === "/__catmint/rsc") {
269
+ var rscTargetPath = url.searchParams.get("path");
270
+ if (rscTargetPath) middlewarePath = rscTargetPath;
271
+ }
272
+ var webRequest = nodeReqToWebRequest(req);
273
+ var mwResult = await ssrEntry.executeMiddleware(middlewarePath, webRequest);
274
+
275
+ if (mwResult.shortCircuit) {
276
+ // Middleware short-circuited — return its response directly
277
+ res.writeHead(mwResult.shortCircuit.status,
278
+ Object.fromEntries(mwResult.shortCircuit.headers));
279
+ var scBody = await mwResult.shortCircuit.text();
280
+ res.end(scBody);
281
+ return;
282
+ }
283
+
284
+ // Middleware passed — capture headers to merge into final response
285
+ if (mwResult.headers && typeof mwResult.headers.forEach === "function") {
286
+ middlewareHeaders = mwResult.headers;
287
+ }
288
+ } catch (err) {
289
+ console.error("Middleware error:", err);
290
+ if (!res.headersSent) {
291
+ res.writeHead(500);
292
+ res.end("Internal Server Error");
293
+ }
294
+ return;
295
+ }
233
296
  }
234
297
 
235
- // 3. Handle server function RPC calls (/__catmint/fn/*)
298
+ // Helper: merge middleware headers into the response before sending.
299
+ function applyMiddlewareHeaders() {
300
+ if (middlewareHeaders) {
301
+ middlewareHeaders.forEach(function(value, key) {
302
+ res.setHeader(key, value);
303
+ });
304
+ }
305
+ }
306
+
307
+ // 4. Handle server function RPC calls (/__catmint/fn/*)
236
308
  if (pathname.startsWith("/__catmint/fn/") && ssrEntry.handleServerFn) {
237
309
  try {
238
310
  // Enforce POST method for server function calls
@@ -268,6 +340,7 @@ ${options.headerPreset === "baseline"
268
340
  }
269
341
  const result = await ssrEntry.handleServerFn(pathname, parsed);
270
342
  if (result) {
343
+ applyMiddlewareHeaders();
271
344
  sendJson(res, 200, result.result);
272
345
  return;
273
346
  }
@@ -279,7 +352,7 @@ ${options.headerPreset === "baseline"
279
352
  return;
280
353
  }
281
354
 
282
- // 4. RSC flight stream for client-side navigation (/__catmint/rsc?path=...)
355
+ // 5. RSC flight stream for client-side navigation (/__catmint/rsc?path=...)
283
356
  if (pathname === "/__catmint/rsc" && method === "GET" && rscEntry.render) {
284
357
  const targetPath = url.searchParams.get("path");
285
358
  if (!targetPath) {
@@ -291,6 +364,7 @@ ${options.headerPreset === "baseline"
291
364
  try {
292
365
  const rscResult = await rscEntry.render(targetPath);
293
366
  if (rscResult) {
367
+ applyMiddlewareHeaders();
294
368
  res.writeHead(200, {
295
369
  "Content-Type": "text/x-component; charset=utf-8",
296
370
  "Cache-Control": "no-cache, no-store, must-revalidate",
@@ -312,7 +386,7 @@ ${options.headerPreset === "baseline"
312
386
  return;
313
387
  }
314
388
 
315
- // 5. API endpoint handling
389
+ // 6. API endpoint handling
316
390
  if (ssrEntry.hasEndpoint && ssrEntry.hasEndpoint(pathname)) {
317
391
  try {
318
392
  const protocol = "http";
@@ -335,7 +409,10 @@ ${options.headerPreset === "baseline"
335
409
  const result = await ssrEntry.handleEndpoint(pathname, method, webRequest);
336
410
 
337
411
  if (result) {
338
- res.writeHead(result.response.status, Object.fromEntries(result.response.headers));
412
+ // Copy response headers then merge middleware headers
413
+ var epHeaders = Object.fromEntries(result.response.headers);
414
+ res.writeHead(result.response.status, epHeaders);
415
+ applyMiddlewareHeaders();
339
416
  if (result.response.body) {
340
417
  await pipeWebStreamToResponse(result.response.body, res);
341
418
  } else {
@@ -357,12 +434,41 @@ ${options.headerPreset === "baseline"
357
434
  }
358
435
  }
359
436
 
360
- // 6. RSC SSR page rendering pipeline
437
+ // 7. Pre-rendered pages WITH middleware: serve from dist/prerendered/
438
+ // These are static routes that have middleware ancestors — their HTML
439
+ // was pre-rendered but they must go through the middleware pipeline.
440
+ if (method === "GET") {
441
+ let prerenderedPath = pathname;
442
+ if (!extname(prerenderedPath)) {
443
+ prerenderedPath = prerenderedPath.endsWith("/")
444
+ ? prerenderedPath + "index.html"
445
+ : prerenderedPath + ".html";
446
+ }
447
+ try {
448
+ const prFilePath = join(PRERENDERED_DIR, prerenderedPath);
449
+ const prStats = await stat(prFilePath);
450
+ if (prStats.isFile()) {
451
+ const content = await readFile(prFilePath);
452
+ applyMiddlewareHeaders();
453
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
454
+ res.setHeader("Content-Length", content.byteLength);
455
+ res.setHeader("Cache-Control", "public, max-age=0, must-revalidate");
456
+ res.writeHead(200);
457
+ res.end(content);
458
+ return;
459
+ }
460
+ } catch {
461
+ // No prerendered file — continue to RSC rendering
462
+ }
463
+ }
464
+
465
+ // 8. RSC → SSR page rendering pipeline
361
466
  if (method === "GET" && rscEntry.render) {
362
467
  try {
363
468
  const rscResult = await rscEntry.render(pathname);
364
469
  if (rscResult) {
365
470
  const htmlStream = await ssrEntry.renderToHtml(rscResult.stream, rscResult.headConfig);
471
+ applyMiddlewareHeaders();
366
472
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
367
473
  await pipeWebStreamToResponse(htmlStream, res);
368
474
  return;
@@ -376,7 +482,14 @@ ${options.headerPreset === "baseline"
376
482
  }
377
483
  }
378
484
 
379
- // 7. 404
485
+ // 9. Static assets fallback (dist/static/ for public files like favicon, etc.)
486
+ {
487
+ const filePath = join(STATIC_DIR, pathname);
488
+ const served = await serveStaticFile(res, filePath, false);
489
+ if (served) return;
490
+ }
491
+
492
+ // 10. 404
380
493
  await sendStatusPage(res, 404, pathname);
381
494
  } catch (err) {
382
495
  console.error("Request error:", err);
@@ -1 +1 @@
1
- {"version":3,"file":"server-template.js","sourceRoot":"","sources":["../src/server-template.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAWhE;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAAsB,EACtB,OAAsB;IAEtB,OAAO;;;;;;;;;;mEAU0D,OAAO,CAAC,IAAI;mCAC5C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA4IrC,OAAO,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwC3C,OAAO,CAAC,YAAY,KAAK,UAAU;QACjC,CAAC,CAAC;;;;CAIL;QACG,CAAC,CAAC,EACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsMC,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"server-template.js","sourceRoot":"","sources":["../src/server-template.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAWhE;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAAsB,EACtB,OAAsB;IAEtB,OAAO;;;;;;;;;;;mEAW0D,OAAO,CAAC,IAAI;mCAC5C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA4IrC,OAAO,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8D3C,OAAO,CAAC,YAAY,KAAK,UAAU;QACjC,CAAC,CAAC;;;;CAIL;QACG,CAAC,CAAC,EACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgSC,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@catmint/adapter-node",
3
- "version": "0.0.0-prealpha.6",
3
+ "version": "0.0.0-prealpha.8",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -14,7 +14,7 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "catmint": "0.0.0-prealpha.6"
17
+ "catmint": "0.0.0-prealpha.8"
18
18
  },
19
19
  "devDependencies": {
20
20
  "typescript": "^5.7.0"