hadars 1.0.5 → 1.0.7

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
@@ -21,30 +21,30 @@ Bring your own router (or none), keep your components as plain React, and get SS
21
21
  ## Benchmarks
22
22
 
23
23
  <!-- BENCHMARK_START -->
24
- > Last run: 2026-04-19 · 120s · 100 connections · Bun runtime
25
- > hadars is **9.7x faster** in requests/sec
24
+ > Last run: 2026-05-12 · 120s · 100 connections · Bun runtime
25
+ > hadars is **8.9x faster** in requests/sec
26
26
 
27
27
  **Throughput** (autocannon, 120s)
28
28
 
29
29
  | Metric | hadars | Next.js |
30
30
  |---|---:|---:|
31
- | Requests/sec | **165** | 17 |
32
- | Latency median | **605 ms** | 2716 ms |
33
- | Latency p99 | **944 ms** | 4004 ms |
34
- | Throughput | **47** MB/s | 9.6 MB/s |
35
- | Peak RSS | 1030.4 MB | **476.8 MB** |
36
- | Avg RSS | 802.6 MB | **424.8 MB** |
37
- | Build time | 0.8 s | 5.9 s |
31
+ | Requests/sec | **152** | 17 |
32
+ | Latency median | **603 ms** | 2762 ms |
33
+ | Latency p99 | **1307 ms** | 4162 ms |
34
+ | Throughput | **43.4** MB/s | 9.4 MB/s |
35
+ | Peak RSS | 788.6 MB | **477.3 MB** |
36
+ | Avg RSS | 617.5 MB | **425.6 MB** |
37
+ | Build time | 0.6 s | 6.1 s |
38
38
 
39
39
  **Page load** (Playwright · Chromium headless · median)
40
40
 
41
41
  | Metric | hadars | Next.js |
42
42
  |---|---:|---:|
43
- | TTFB | **17 ms** | 42 ms |
44
- | FCP | **92 ms** | 140 ms |
45
- | DOMContentLoaded | **36 ms** | 126 ms |
46
- | Load | **118 ms** | 173 ms |
47
- | Peak RSS | 455.4 MB | **272.8 MB** |
43
+ | TTFB | **18 ms** | 43 ms |
44
+ | FCP | **96 ms** | 144 ms |
45
+ | DOMContentLoaded | **39 ms** | 131 ms |
46
+ | Load | **121 ms** | 177 ms |
47
+ | Peak RSS | 457.3 MB | **288.8 MB** |
48
48
  <!-- BENCHMARK_END -->
49
49
 
50
50
  ## Quick start
@@ -180,7 +180,16 @@ var getReactResponse = async (req, opts) => {
180
180
  globalThis.__hadarsContext = context;
181
181
  const element = createElement(App, props);
182
182
  let bodyHtml = null;
183
- if (opts.singlePass) {
183
+ if (opts.dataOnly) {
184
+ globalThis.__hadarsUnsuspend = unsuspend;
185
+ globalThis.__hadarsContext = context;
186
+ try {
187
+ await renderPreflight(element);
188
+ } finally {
189
+ globalThis.__hadarsUnsuspend = null;
190
+ globalThis.__hadarsContext = null;
191
+ }
192
+ } else if (opts.singlePass) {
184
193
  globalThis.__hadarsUnsuspend = unsuspend;
185
194
  globalThis.__hadarsContext = context;
186
195
  try {
@@ -190,6 +199,8 @@ var getReactResponse = async (req, opts) => {
190
199
  globalThis.__hadarsContext = null;
191
200
  }
192
201
  } else {
202
+ globalThis.__hadarsUnsuspend = unsuspend;
203
+ globalThis.__hadarsContext = context;
193
204
  try {
194
205
  await renderPreflight(element);
195
206
  } finally {
package/dist/cli.js CHANGED
@@ -1228,7 +1228,16 @@ var getReactResponse = async (req, opts) => {
1228
1228
  globalThis.__hadarsContext = context;
1229
1229
  const element = createElement(App, props);
1230
1230
  let bodyHtml = null;
1231
- if (opts.singlePass) {
1231
+ if (opts.dataOnly) {
1232
+ globalThis.__hadarsUnsuspend = unsuspend;
1233
+ globalThis.__hadarsContext = context;
1234
+ try {
1235
+ await renderPreflight(element);
1236
+ } finally {
1237
+ globalThis.__hadarsUnsuspend = null;
1238
+ globalThis.__hadarsContext = null;
1239
+ }
1240
+ } else if (opts.singlePass) {
1232
1241
  globalThis.__hadarsUnsuspend = unsuspend;
1233
1242
  globalThis.__hadarsContext = context;
1234
1243
  try {
@@ -1238,6 +1247,8 @@ var getReactResponse = async (req, opts) => {
1238
1247
  globalThis.__hadarsContext = null;
1239
1248
  }
1240
1249
  } else {
1250
+ globalThis.__hadarsUnsuspend = unsuspend;
1251
+ globalThis.__hadarsContext = context;
1241
1252
  try {
1242
1253
  await renderPreflight(element);
1243
1254
  } finally {
@@ -1760,6 +1771,15 @@ async function tryServeFile(filePath) {
1760
1771
  return null;
1761
1772
  }
1762
1773
  }
1774
+ var _notFound = new Set;
1775
+ async function tryServeFileCached(filePath) {
1776
+ if (_notFound.has(filePath))
1777
+ return null;
1778
+ const res = await tryServeFile(filePath);
1779
+ if (!res)
1780
+ _notFound.add(filePath);
1781
+ return res;
1782
+ }
1763
1783
 
1764
1784
  // src/build.ts
1765
1785
  import { RspackDevServer } from "@rspack/dev-server";
@@ -1783,10 +1803,11 @@ function buildSsrResponse(head, status, getAppBody, finalize, getPrecontentHtml)
1783
1803
  async start(controller) {
1784
1804
  try {
1785
1805
  const [precontentHtml, postContent] = precontentResult instanceof Promise ? await precontentResult : precontentResult;
1806
+ const finalizePromise = finalize();
1786
1807
  controller.enqueue(encoder.encode(precontentHtml));
1787
1808
  const bodyHtml = await getAppBody();
1788
1809
  controller.enqueue(encoder.encode(`<div id="app">${bodyHtml}</div>`));
1789
- const { clientProps } = await finalize();
1810
+ const { clientProps } = await finalizePromise;
1790
1811
  const scriptContent = JSON.stringify({ hadars: { props: clientProps } }).replace(/</g, "\\u003c");
1791
1812
  controller.enqueue(encoder.encode(`<script id="hadars" type="application/json">${scriptContent}</script>` + postContent));
1792
1813
  controller.close();
@@ -2742,6 +2763,7 @@ var dev = async (options) => {
2742
2763
  } catch (e) {}
2743
2764
  })();
2744
2765
  const getPrecontentHtml = makePrecontentHtmlGetter(readyPromise.then(() => fs.readFile(pathMod2.join(__dirname3, StaticPath, "out.html"), "utf-8")));
2766
+ const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
2745
2767
  await serve(port, async (req, ctx) => {
2746
2768
  await readyPromise;
2747
2769
  const request = parseRequest(req);
@@ -2762,11 +2784,10 @@ var dev = async (options) => {
2762
2784
  return proxied;
2763
2785
  const url = new URL(request.url);
2764
2786
  const path2 = url.pathname;
2765
- const staticRes = await tryServeFile(pathMod2.join(__dirname3, StaticPath, path2));
2787
+ const staticRes = await tryServeFileCached(pathMod2.join(__dirname3, StaticPath, path2));
2766
2788
  if (staticRes)
2767
2789
  return staticRes;
2768
- const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
2769
- const projectRes = await tryServeFile(pathMod2.join(projectStaticPath, path2));
2790
+ const projectRes = await tryServeFileCached(pathMod2.join(projectStaticPath, path2));
2770
2791
  if (projectRes)
2771
2792
  return projectRes;
2772
2793
  const ssrComponentPath = pathMod2.join(__dirname3, HadarsFolder, SSR_FILENAME);
@@ -2782,6 +2803,7 @@ var dev = async (options) => {
2782
2803
  getFinalProps
2783
2804
  } = cachedSsrModule;
2784
2805
  globalThis.__hadarsGraphQL = devStaticCtx?.graphql;
2806
+ const isDataOnly = request.headers.get("Accept") === "application/json";
2785
2807
  const { head, status, getAppBody, finalize } = await getReactResponse(request, {
2786
2808
  document: {
2787
2809
  body: Component,
@@ -2790,9 +2812,10 @@ var dev = async (options) => {
2790
2812
  getFinalProps
2791
2813
  },
2792
2814
  staticCtx: devStaticCtx,
2793
- singlePass: true
2815
+ singlePass: !isDataOnly,
2816
+ dataOnly: isDataOnly
2794
2817
  });
2795
- if (request.headers.get("Accept") === "application/json") {
2818
+ if (isDataOnly) {
2796
2819
  const { clientProps } = await finalize();
2797
2820
  const serverData = clientProps.__serverData ?? {};
2798
2821
  return new Response(JSON.stringify({ serverData }), {
@@ -2899,6 +2922,7 @@ var run = async (options) => {
2899
2922
  console.log(`[hadars] SSR render pool: ${workers} worker threads`);
2900
2923
  }
2901
2924
  const getPrecontentHtml = makePrecontentHtmlGetter(fs.readFile(pathMod2.join(__dirname3, StaticPath, "out.html"), "utf-8"));
2925
+ const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
2902
2926
  const componentPath = pathToFileURL(pathMod2.resolve(__dirname3, HadarsFolder, SSR_FILENAME)).href;
2903
2927
  const ssrModulePromise = import(componentPath);
2904
2928
  const runHandler = async (req, ctx) => {
@@ -2915,16 +2939,15 @@ var run = async (options) => {
2915
2939
  return proxied;
2916
2940
  const url = new URL(request.url);
2917
2941
  const path2 = url.pathname;
2918
- const staticRes = await tryServeFile(pathMod2.join(__dirname3, StaticPath, path2));
2942
+ const staticRes = await tryServeFileCached(pathMod2.join(__dirname3, StaticPath, path2));
2919
2943
  if (staticRes)
2920
2944
  return staticRes;
2921
- const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
2922
- const projectRes = await tryServeFile(pathMod2.join(projectStaticPath, path2));
2945
+ const projectRes = await tryServeFileCached(pathMod2.join(projectStaticPath, path2));
2923
2946
  if (projectRes)
2924
2947
  return projectRes;
2925
2948
  const routeClean = path2.replace(/(^\/|\/$)/g, "");
2926
2949
  if (routeClean) {
2927
- const routeRes = await tryServeFile(pathMod2.join(__dirname3, StaticPath, routeClean, "index.html"));
2950
+ const routeRes = await tryServeFileCached(pathMod2.join(__dirname3, StaticPath, routeClean, "index.html"));
2928
2951
  if (routeRes)
2929
2952
  return routeRes;
2930
2953
  }
@@ -2943,6 +2966,7 @@ var run = async (options) => {
2943
2966
  status: wStatus
2944
2967
  });
2945
2968
  }
2969
+ const isDataOnly = request.headers.get("Accept") === "application/json";
2946
2970
  const { head, status, getAppBody, finalize } = await getReactResponse(request, {
2947
2971
  document: {
2948
2972
  body: Component,
@@ -2950,9 +2974,10 @@ var run = async (options) => {
2950
2974
  getInitProps,
2951
2975
  getFinalProps
2952
2976
  },
2953
- singlePass: true
2977
+ singlePass: !isDataOnly,
2978
+ dataOnly: isDataOnly
2954
2979
  });
2955
- if (request.headers.get("Accept") === "application/json") {
2980
+ if (isDataOnly) {
2956
2981
  const { clientProps } = await finalize();
2957
2982
  const serverData = clientProps.__serverData ?? {};
2958
2983
  return new Response(JSON.stringify({ serverData }), {
@@ -1231,7 +1231,16 @@ var getReactResponse = async (req, opts) => {
1231
1231
  globalThis.__hadarsContext = context;
1232
1232
  const element = createElement(App, props);
1233
1233
  let bodyHtml = null;
1234
- if (opts.singlePass) {
1234
+ if (opts.dataOnly) {
1235
+ globalThis.__hadarsUnsuspend = unsuspend;
1236
+ globalThis.__hadarsContext = context;
1237
+ try {
1238
+ await renderPreflight(element);
1239
+ } finally {
1240
+ globalThis.__hadarsUnsuspend = null;
1241
+ globalThis.__hadarsContext = null;
1242
+ }
1243
+ } else if (opts.singlePass) {
1235
1244
  globalThis.__hadarsUnsuspend = unsuspend;
1236
1245
  globalThis.__hadarsContext = context;
1237
1246
  try {
@@ -1241,6 +1250,8 @@ var getReactResponse = async (req, opts) => {
1241
1250
  globalThis.__hadarsContext = null;
1242
1251
  }
1243
1252
  } else {
1253
+ globalThis.__hadarsUnsuspend = unsuspend;
1254
+ globalThis.__hadarsContext = context;
1244
1255
  try {
1245
1256
  await renderPreflight(element);
1246
1257
  } finally {
@@ -6,7 +6,7 @@ import {
6
6
  getReactResponse,
7
7
  makePrecontentHtmlGetter,
8
8
  parseRequest
9
- } from "./chunk-EJUCAJXR.js";
9
+ } from "./chunk-VAR5KTG3.js";
10
10
  import "./chunk-6SOA2HTO.js";
11
11
  import "./chunk-OZUZS2PD.js";
12
12
 
package/dist/lambda.cjs CHANGED
@@ -1271,7 +1271,16 @@ var getReactResponse = async (req, opts) => {
1271
1271
  globalThis.__hadarsContext = context;
1272
1272
  const element = createElement(App, props);
1273
1273
  let bodyHtml = null;
1274
- if (opts.singlePass) {
1274
+ if (opts.dataOnly) {
1275
+ globalThis.__hadarsUnsuspend = unsuspend;
1276
+ globalThis.__hadarsContext = context;
1277
+ try {
1278
+ await renderPreflight(element);
1279
+ } finally {
1280
+ globalThis.__hadarsUnsuspend = null;
1281
+ globalThis.__hadarsContext = null;
1282
+ }
1283
+ } else if (opts.singlePass) {
1275
1284
  globalThis.__hadarsUnsuspend = unsuspend;
1276
1285
  globalThis.__hadarsContext = context;
1277
1286
  try {
@@ -1281,6 +1290,8 @@ var getReactResponse = async (req, opts) => {
1281
1290
  globalThis.__hadarsContext = null;
1282
1291
  }
1283
1292
  } else {
1293
+ globalThis.__hadarsUnsuspend = unsuspend;
1294
+ globalThis.__hadarsContext = context;
1284
1295
  try {
1285
1296
  await renderPreflight(element);
1286
1297
  } finally {
package/dist/lambda.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  getReactResponse,
7
7
  makePrecontentHtmlGetter,
8
8
  parseRequest
9
- } from "./chunk-EJUCAJXR.js";
9
+ } from "./chunk-VAR5KTG3.js";
10
10
  import "./chunk-6SOA2HTO.js";
11
11
  import "./chunk-OZUZS2PD.js";
12
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hadars",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Minimal SSR framework for React — rspack, HMR, TypeScript, Bun/Node/Deno",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",