@pylonsync/functions 0.3.307 → 0.3.308

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pylonsync/functions",
3
- "version": "0.3.307",
3
+ "version": "0.3.308",
4
4
  "description": "TypeScript function runtime for pylon — defines server-side queries, mutations, and actions.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1220,9 +1220,11 @@ async function _doBuildInner(
1220
1220
  // Tailwind v4 compile. Optional — only fires if the project has
1221
1221
  // `app/globals.css`. Adds the stylesheet to every route's css
1222
1222
  // array so SSR head injection emits `<link rel="stylesheet">`.
1223
+ let stylesRel: string | null = null;
1223
1224
  try {
1224
1225
  const styles = await buildTailwind(fs, path, cwd, outdir, appDirRel);
1225
1226
  if (styles) {
1227
+ stylesRel = styles;
1226
1228
  for (const r of Object.values(manifest.routes)) {
1227
1229
  r.css = [styles];
1228
1230
  }
@@ -1252,6 +1254,46 @@ async function _doBuildInner(
1252
1254
  console.warn(`[pylon ssr] font build failed: ${fErr?.message ?? fErr}`);
1253
1255
  }
1254
1256
 
1257
+ // App-overridable rate-limit page. The rate limiter short-circuits in the
1258
+ // Rust layer BEFORE SSR, so it can't render a route per (shed) request —
1259
+ // instead pre-render `app/rate-limit.tsx` ONCE here to a self-contained
1260
+ // static HTML (compiled CSS inlined, so it needs no asset fetch) that the
1261
+ // runtime serves on 429 for browser navigations. FULLY GATED: any failure
1262
+ // just skips the override and the framework's built-in default 429 page is
1263
+ // used — a broken/absent rate-limit.tsx never affects the build.
1264
+ try {
1265
+ const rlPath = path.join(cwd, appDirRel, "rate-limit.tsx");
1266
+ if (fs.existsSync(rlPath)) {
1267
+ const ReactMod: any = await import("react");
1268
+ const React = ReactMod.default ?? ReactMod;
1269
+ const { renderToStaticMarkup }: any = await import("react-dom/server");
1270
+ const mod: any = await import(rlPath);
1271
+ const Comp = mod.default;
1272
+ if (typeof Comp === "function") {
1273
+ const inner = renderToStaticMarkup(React.createElement(Comp));
1274
+ const css = stylesRel
1275
+ ? fs.readFileSync(path.join(outdir, stylesRel), "utf8")
1276
+ : "";
1277
+ const html =
1278
+ `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">` +
1279
+ `<meta name="viewport" content="width=device-width, initial-scale=1">` +
1280
+ `<title>Too many requests</title>` +
1281
+ (css ? `<style>${css}</style>` : "") +
1282
+ `</head><body>${inner}</body></html>`;
1283
+ fs.writeFileSync(path.join(outdir, "rate-limit.html"), html, "utf8");
1284
+ // eslint-disable-next-line no-console
1285
+ console.log(
1286
+ "[pylon ssr] pre-rendered app/rate-limit.tsx → rate-limit.html",
1287
+ );
1288
+ }
1289
+ }
1290
+ } catch (rlErr: any) {
1291
+ // eslint-disable-next-line no-console
1292
+ console.warn(
1293
+ `[pylon ssr] rate-limit.tsx pre-render skipped: ${rlErr?.message ?? rlErr}`,
1294
+ );
1295
+ }
1296
+
1255
1297
  const manifestPath = path.join(outdir, "manifest.json");
1256
1298
  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
1257
1299