hadars 1.0.2 → 1.0.3

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-17 · 120s · 100 connections · Bun runtime
24
+ > Last run: 2026-04-19 · 120s · 100 connections · Bun runtime
25
25
  > hadars is **9.5x faster** in requests/sec
26
26
 
27
27
  **Throughput** (autocannon, 120s)
28
28
 
29
29
  | Metric | hadars | Next.js |
30
30
  |---|---:|---:|
31
- | Requests/sec | **218** | 23 |
32
- | Latency median | **446 ms** | 2421 ms |
33
- | Latency p99 | **838 ms** | 3244 ms |
34
- | Throughput | **62.07** MB/s | 12.6 MB/s |
35
- | Peak RSS | 1032.7 MB | **514.0 MB** |
36
- | Avg RSS | 842.2 MB | **462.8 MB** |
37
- | Build time | 1.1 s | 5.3 s |
31
+ | Requests/sec | **162** | 17 |
32
+ | Latency median | **607 ms** | 2754 ms |
33
+ | Latency p99 | **952 ms** | 4084 ms |
34
+ | Throughput | **46.04** MB/s | 9.49 MB/s |
35
+ | Peak RSS | 1032.4 MB | **482.3 MB** |
36
+ | Avg RSS | 797.0 MB | **427.0 MB** |
37
+ | Build time | 0.6 s | 6.0 s |
38
38
 
39
39
  **Page load** (Playwright · Chromium headless · median)
40
40
 
41
41
  | Metric | hadars | Next.js |
42
42
  |---|---:|---:|
43
- | TTFB | **15 ms** | 33 ms |
44
- | FCP | **76 ms** | 112 ms |
45
- | DOMContentLoaded | **31 ms** | 101 ms |
46
- | Load | **96 ms** | 139 ms |
47
- | Peak RSS | 508.0 MB | **299.5 MB** |
43
+ | TTFB | **18 ms** | 47 ms |
44
+ | FCP | **96 ms** | 140 ms |
45
+ | DOMContentLoaded | **39 ms** | 128 ms |
46
+ | Load | **120 ms** | 174 ms |
47
+ | Peak RSS | 443.5 MB | **309.9 MB** |
48
48
  <!-- BENCHMARK_END -->
49
49
 
50
50
  ## Quick start
@@ -179,14 +179,27 @@ var getReactResponse = async (req, opts) => {
179
179
  globalThis.__hadarsUnsuspend = unsuspend;
180
180
  globalThis.__hadarsContext = context;
181
181
  const element = createElement(App, props);
182
- try {
183
- await renderPreflight(element);
184
- } finally {
185
- globalThis.__hadarsUnsuspend = null;
186
- globalThis.__hadarsContext = null;
182
+ let bodyHtml = null;
183
+ if (opts.singlePass) {
184
+ globalThis.__hadarsUnsuspend = unsuspend;
185
+ globalThis.__hadarsContext = context;
186
+ try {
187
+ bodyHtml = await renderToString(element);
188
+ } finally {
189
+ globalThis.__hadarsUnsuspend = null;
190
+ globalThis.__hadarsContext = null;
191
+ }
192
+ } else {
193
+ try {
194
+ await renderPreflight(element);
195
+ } finally {
196
+ globalThis.__hadarsUnsuspend = null;
197
+ globalThis.__hadarsContext = null;
198
+ }
187
199
  }
188
200
  const status = context.head.status;
189
201
  const getAppBody = async () => {
202
+ if (bodyHtml !== null) return bodyHtml;
190
203
  globalThis.__hadarsUnsuspend = unsuspend;
191
204
  globalThis.__hadarsContext = context;
192
205
  try {
@@ -264,15 +277,23 @@ async function transformStream(data, stream) {
264
277
  }
265
278
  return out;
266
279
  }
280
+ var _zlibGzip = null;
281
+ var _zlibGunzip = null;
267
282
  async function zlibGzip(d) {
268
- const zlib = await import("zlib");
269
- const { promisify } = await import("util");
270
- return promisify(zlib.gzip)(d);
283
+ if (!_zlibGzip) {
284
+ const zlib = await import("zlib");
285
+ const { promisify } = await import("util");
286
+ _zlibGzip = promisify(zlib.gzip);
287
+ }
288
+ return _zlibGzip(d);
271
289
  }
272
290
  async function zlibGunzip(d) {
273
- const zlib = await import("zlib");
274
- const { promisify } = await import("util");
275
- return promisify(zlib.gunzip)(d);
291
+ if (!_zlibGunzip) {
292
+ const zlib = await import("zlib");
293
+ const { promisify } = await import("util");
294
+ _zlibGunzip = promisify(zlib.gunzip);
295
+ }
296
+ return _zlibGunzip(d);
276
297
  }
277
298
  var gzipCompress = (d) => globalThis.CompressionStream ? transformStream(d, new globalThis.CompressionStream("gzip")) : zlibGzip(d);
278
299
  var gzipDecompress = (d) => globalThis.DecompressionStream ? transformStream(d, new globalThis.DecompressionStream("gzip")) : zlibGunzip(d);
package/dist/cli.js CHANGED
@@ -1220,14 +1220,28 @@ var getReactResponse = async (req, opts) => {
1220
1220
  globalThis.__hadarsUnsuspend = unsuspend;
1221
1221
  globalThis.__hadarsContext = context;
1222
1222
  const element = createElement(App, props);
1223
- try {
1224
- await renderPreflight(element);
1225
- } finally {
1226
- globalThis.__hadarsUnsuspend = null;
1227
- globalThis.__hadarsContext = null;
1223
+ let bodyHtml = null;
1224
+ if (opts.singlePass) {
1225
+ globalThis.__hadarsUnsuspend = unsuspend;
1226
+ globalThis.__hadarsContext = context;
1227
+ try {
1228
+ bodyHtml = await renderToString(element);
1229
+ } finally {
1230
+ globalThis.__hadarsUnsuspend = null;
1231
+ globalThis.__hadarsContext = null;
1232
+ }
1233
+ } else {
1234
+ try {
1235
+ await renderPreflight(element);
1236
+ } finally {
1237
+ globalThis.__hadarsUnsuspend = null;
1238
+ globalThis.__hadarsContext = null;
1239
+ }
1228
1240
  }
1229
1241
  const status = context.head.status;
1230
1242
  const getAppBody = async () => {
1243
+ if (bodyHtml !== null)
1244
+ return bodyHtml;
1231
1245
  globalThis.__hadarsUnsuspend = unsuspend;
1232
1246
  globalThis.__hadarsContext = context;
1233
1247
  try {
@@ -1823,15 +1837,23 @@ async function transformStream(data, stream) {
1823
1837
  }
1824
1838
  return out;
1825
1839
  }
1840
+ var _zlibGzip = null;
1841
+ var _zlibGunzip = null;
1826
1842
  async function zlibGzip(d) {
1827
- const zlib = await import("node:zlib");
1828
- const { promisify } = await import("node:util");
1829
- return promisify(zlib.gzip)(d);
1843
+ if (!_zlibGzip) {
1844
+ const zlib = await import("node:zlib");
1845
+ const { promisify } = await import("node:util");
1846
+ _zlibGzip = promisify(zlib.gzip);
1847
+ }
1848
+ return _zlibGzip(d);
1830
1849
  }
1831
1850
  async function zlibGunzip(d) {
1832
- const zlib = await import("node:zlib");
1833
- const { promisify } = await import("node:util");
1834
- return promisify(zlib.gunzip)(d);
1851
+ if (!_zlibGunzip) {
1852
+ const zlib = await import("node:zlib");
1853
+ const { promisify } = await import("node:util");
1854
+ _zlibGunzip = promisify(zlib.gunzip);
1855
+ }
1856
+ return _zlibGunzip(d);
1835
1857
  }
1836
1858
  var gzipCompress = (d) => globalThis.CompressionStream ? transformStream(d, new globalThis.CompressionStream("gzip")) : zlibGzip(d);
1837
1859
  var gzipDecompress = (d) => globalThis.DecompressionStream ? transformStream(d, new globalThis.DecompressionStream("gzip")) : zlibGunzip(d);
@@ -2565,6 +2587,8 @@ var dev = async (options) => {
2565
2587
  const tmpFilePath = pathMod2.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
2566
2588
  await fs.writeFile(tmpFilePath, clientScript);
2567
2589
  let ssrBuildId = crypto.randomBytes(4).toString("hex");
2590
+ let cachedSsrModule = null;
2591
+ let cachedSsrBuildId = "";
2568
2592
  const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(pathMod2.resolve(__dirname3, options.htmlTemplate)) : undefined;
2569
2593
  const clientCompiler = createClientCompiler(tmpFilePath, {
2570
2594
  target: "web",
@@ -2739,13 +2763,17 @@ var dev = async (options) => {
2739
2763
  if (projectRes)
2740
2764
  return projectRes;
2741
2765
  const ssrComponentPath = pathMod2.join(__dirname3, HadarsFolder, SSR_FILENAME);
2742
- const importPath = pathToFileURL(ssrComponentPath).href + `?t=${ssrBuildId}`;
2743
2766
  try {
2767
+ if (ssrBuildId !== cachedSsrBuildId) {
2768
+ const importPath = pathToFileURL(ssrComponentPath).href + `?t=${ssrBuildId}`;
2769
+ cachedSsrModule = await import(importPath);
2770
+ cachedSsrBuildId = ssrBuildId;
2771
+ }
2744
2772
  const {
2745
2773
  default: Component,
2746
2774
  getInitProps,
2747
2775
  getFinalProps
2748
- } = await import(importPath);
2776
+ } = cachedSsrModule;
2749
2777
  globalThis.__hadarsGraphQL = devStaticCtx?.graphql;
2750
2778
  const { head, status, getAppBody, finalize } = await getReactResponse(request, {
2751
2779
  document: {
@@ -2754,7 +2782,8 @@ var dev = async (options) => {
2754
2782
  getInitProps,
2755
2783
  getFinalProps
2756
2784
  },
2757
- staticCtx: devStaticCtx
2785
+ staticCtx: devStaticCtx,
2786
+ singlePass: true
2758
2787
  });
2759
2788
  if (request.headers.get("Accept") === "application/json") {
2760
2789
  const { clientProps } = await finalize();
@@ -2913,7 +2942,8 @@ var run = async (options) => {
2913
2942
  lang: "en",
2914
2943
  getInitProps,
2915
2944
  getFinalProps
2916
- }
2945
+ },
2946
+ singlePass: true
2917
2947
  });
2918
2948
  if (request.headers.get("Accept") === "application/json") {
2919
2949
  const { clientProps } = await finalize();
@@ -1220,14 +1220,27 @@ var getReactResponse = async (req, opts) => {
1220
1220
  globalThis.__hadarsUnsuspend = unsuspend;
1221
1221
  globalThis.__hadarsContext = context;
1222
1222
  const element = createElement(App, props);
1223
- try {
1224
- await renderPreflight(element);
1225
- } finally {
1226
- globalThis.__hadarsUnsuspend = null;
1227
- globalThis.__hadarsContext = null;
1223
+ let bodyHtml = null;
1224
+ if (opts.singlePass) {
1225
+ globalThis.__hadarsUnsuspend = unsuspend;
1226
+ globalThis.__hadarsContext = context;
1227
+ try {
1228
+ bodyHtml = await renderToString(element);
1229
+ } finally {
1230
+ globalThis.__hadarsUnsuspend = null;
1231
+ globalThis.__hadarsContext = null;
1232
+ }
1233
+ } else {
1234
+ try {
1235
+ await renderPreflight(element);
1236
+ } finally {
1237
+ globalThis.__hadarsUnsuspend = null;
1238
+ globalThis.__hadarsContext = null;
1239
+ }
1228
1240
  }
1229
1241
  const status = context.head.status;
1230
1242
  const getAppBody = async () => {
1243
+ if (bodyHtml !== null) return bodyHtml;
1231
1244
  globalThis.__hadarsUnsuspend = unsuspend;
1232
1245
  globalThis.__hadarsContext = context;
1233
1246
  try {
@@ -1305,15 +1318,23 @@ async function transformStream(data, stream) {
1305
1318
  }
1306
1319
  return out;
1307
1320
  }
1321
+ var _zlibGzip = null;
1322
+ var _zlibGunzip = null;
1308
1323
  async function zlibGzip(d) {
1309
- const zlib = await import("zlib");
1310
- const { promisify } = await import("util");
1311
- return promisify(zlib.gzip)(d);
1324
+ if (!_zlibGzip) {
1325
+ const zlib = await import("zlib");
1326
+ const { promisify } = await import("util");
1327
+ _zlibGzip = promisify(zlib.gzip);
1328
+ }
1329
+ return _zlibGzip(d);
1312
1330
  }
1313
1331
  async function zlibGunzip(d) {
1314
- const zlib = await import("zlib");
1315
- const { promisify } = await import("util");
1316
- return promisify(zlib.gunzip)(d);
1332
+ if (!_zlibGunzip) {
1333
+ const zlib = await import("zlib");
1334
+ const { promisify } = await import("util");
1335
+ _zlibGunzip = promisify(zlib.gunzip);
1336
+ }
1337
+ return _zlibGunzip(d);
1317
1338
  }
1318
1339
  var gzipCompress = (d) => globalThis.CompressionStream ? transformStream(d, new globalThis.CompressionStream("gzip")) : zlibGzip(d);
1319
1340
  var gzipDecompress = (d) => globalThis.DecompressionStream ? transformStream(d, new globalThis.DecompressionStream("gzip")) : zlibGunzip(d);
@@ -6,7 +6,7 @@ import {
6
6
  getReactResponse,
7
7
  makePrecontentHtmlGetter,
8
8
  parseRequest
9
- } from "./chunk-LDVJ26Q3.js";
9
+ } from "./chunk-PQ5N5GOY.js";
10
10
  import "./chunk-QOTDCUE5.js";
11
11
  import "./chunk-OZUZS2PD.js";
12
12
 
package/dist/lambda.cjs CHANGED
@@ -1260,14 +1260,27 @@ var getReactResponse = async (req, opts) => {
1260
1260
  globalThis.__hadarsUnsuspend = unsuspend;
1261
1261
  globalThis.__hadarsContext = context;
1262
1262
  const element = createElement(App, props);
1263
- try {
1264
- await renderPreflight(element);
1265
- } finally {
1266
- globalThis.__hadarsUnsuspend = null;
1267
- globalThis.__hadarsContext = null;
1263
+ let bodyHtml = null;
1264
+ if (opts.singlePass) {
1265
+ globalThis.__hadarsUnsuspend = unsuspend;
1266
+ globalThis.__hadarsContext = context;
1267
+ try {
1268
+ bodyHtml = await renderToString(element);
1269
+ } finally {
1270
+ globalThis.__hadarsUnsuspend = null;
1271
+ globalThis.__hadarsContext = null;
1272
+ }
1273
+ } else {
1274
+ try {
1275
+ await renderPreflight(element);
1276
+ } finally {
1277
+ globalThis.__hadarsUnsuspend = null;
1278
+ globalThis.__hadarsContext = null;
1279
+ }
1268
1280
  }
1269
1281
  const status = context.head.status;
1270
1282
  const getAppBody = async () => {
1283
+ if (bodyHtml !== null) return bodyHtml;
1271
1284
  globalThis.__hadarsUnsuspend = unsuspend;
1272
1285
  globalThis.__hadarsContext = context;
1273
1286
  try {
@@ -1345,15 +1358,23 @@ async function transformStream(data, stream) {
1345
1358
  }
1346
1359
  return out;
1347
1360
  }
1361
+ var _zlibGzip = null;
1362
+ var _zlibGunzip = null;
1348
1363
  async function zlibGzip(d) {
1349
- const zlib = await import("zlib");
1350
- const { promisify } = await import("util");
1351
- return promisify(zlib.gzip)(d);
1364
+ if (!_zlibGzip) {
1365
+ const zlib = await import("zlib");
1366
+ const { promisify } = await import("util");
1367
+ _zlibGzip = promisify(zlib.gzip);
1368
+ }
1369
+ return _zlibGzip(d);
1352
1370
  }
1353
1371
  async function zlibGunzip(d) {
1354
- const zlib = await import("zlib");
1355
- const { promisify } = await import("util");
1356
- return promisify(zlib.gunzip)(d);
1372
+ if (!_zlibGunzip) {
1373
+ const zlib = await import("zlib");
1374
+ const { promisify } = await import("util");
1375
+ _zlibGunzip = promisify(zlib.gunzip);
1376
+ }
1377
+ return _zlibGunzip(d);
1357
1378
  }
1358
1379
  var gzipCompress = (d) => globalThis.CompressionStream ? transformStream(d, new globalThis.CompressionStream("gzip")) : zlibGzip(d);
1359
1380
  var gzipDecompress = (d) => globalThis.DecompressionStream ? transformStream(d, new globalThis.DecompressionStream("gzip")) : zlibGunzip(d);
package/dist/lambda.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  getReactResponse,
7
7
  makePrecontentHtmlGetter,
8
8
  parseRequest
9
- } from "./chunk-LDVJ26Q3.js";
9
+ } from "./chunk-PQ5N5GOY.js";
10
10
  import "./chunk-QOTDCUE5.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.2",
3
+ "version": "1.0.3",
4
4
  "description": "Minimal SSR framework for React — rspack, HMR, TypeScript, Bun/Node/Deno",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",