hadars 0.1.35 → 0.1.37

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/dist/cli.js CHANGED
@@ -5,8 +5,9 @@ import { spawn as spawn2 } from "node:child_process";
5
5
 
6
6
  // cli-lib.ts
7
7
  import { existsSync as existsSync3 } from "node:fs";
8
- import { mkdir, writeFile } from "node:fs/promises";
9
- import { resolve, join } from "node:path";
8
+ import { mkdir, writeFile, unlink } from "node:fs/promises";
9
+ import { resolve, join, dirname } from "node:path";
10
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
10
11
 
11
12
  // src/utils/proxyHandler.tsx
12
13
  var cloneHeaders = (headers) => {
@@ -135,10 +136,10 @@ var upgradeHandler = (options) => {
135
136
  };
136
137
 
137
138
  // src/slim-react/types.ts
138
- var SLIM_ELEMENT = Symbol.for("react.element");
139
- var REACT19_ELEMENT = Symbol.for("react.transitional.element");
140
- var FRAGMENT_TYPE = Symbol.for("react.fragment");
141
- var SUSPENSE_TYPE = Symbol.for("react.suspense");
139
+ var SLIM_ELEMENT = /* @__PURE__ */ Symbol.for("react.element");
140
+ var REACT19_ELEMENT = /* @__PURE__ */ Symbol.for("react.transitional.element");
141
+ var FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment");
142
+ var SUSPENSE_TYPE = /* @__PURE__ */ Symbol.for("react.suspense");
142
143
 
143
144
  // src/slim-react/jsx.ts
144
145
  function createElement(type, props, ...children) {
@@ -161,8 +162,7 @@ function createElement(type, props, ...children) {
161
162
  // src/slim-react/renderContext.ts
162
163
  var MAP_KEY = "__slimReactContextMap";
163
164
  var _g = globalThis;
164
- if (!("__slimReactContextMap" in _g))
165
- _g[MAP_KEY] = null;
165
+ if (!("__slimReactContextMap" in _g)) _g[MAP_KEY] = null;
166
166
  function swapContextMap(map) {
167
167
  const prev = _g[MAP_KEY];
168
168
  _g[MAP_KEY] = map;
@@ -173,8 +173,7 @@ function captureMap() {
173
173
  }
174
174
  function getContextValue(context) {
175
175
  const map = _g[MAP_KEY];
176
- if (map && map.has(context))
177
- return map.get(context);
176
+ if (map && map.has(context)) return map.get(context);
178
177
  const c = context;
179
178
  return "_defaultValue" in c ? c._defaultValue : c._currentValue;
180
179
  }
@@ -256,8 +255,7 @@ function restoreContext(snap) {
256
255
  }
257
256
  function getTreeId() {
258
257
  const { id, overflow } = s().currentTreeContext;
259
- if (id === 1)
260
- return overflow;
258
+ if (id === 1) return overflow;
261
259
  const stripped = (id & ~(1 << 31 - Math.clz32(id))).toString(32);
262
260
  return stripped + overflow;
263
261
  }
@@ -266,8 +264,7 @@ function makeId() {
266
264
  const treeId = getTreeId();
267
265
  const n = st.localIdCounter++;
268
266
  let id = "_R_" + st.idPrefix + treeId;
269
- if (n > 0)
270
- id += "H" + n.toString(32);
267
+ if (n > 0) id += "H" + n.toString(32);
271
268
  return id + "_";
272
269
  }
273
270
 
@@ -322,10 +319,8 @@ function use(usable) {
322
319
  return getContextValue(usable);
323
320
  }
324
321
  const promise = usable;
325
- if (promise.status === "fulfilled")
326
- return promise.value;
327
- if (promise.status === "rejected")
328
- throw promise.reason;
322
+ if (promise.status === "fulfilled") return promise.value;
323
+ if (promise.status === "rejected") throw promise.reason;
329
324
  throw promise;
330
325
  }
331
326
 
@@ -359,15 +354,13 @@ var slimDispatcher = {
359
354
  useHostTransitionStatus: () => false
360
355
  };
361
356
  function installDispatcher() {
362
- if (!_internals)
363
- return null;
357
+ if (!_internals) return null;
364
358
  const prev = _internals.H;
365
359
  _internals.H = slimDispatcher;
366
360
  return prev;
367
361
  }
368
362
  function restoreDispatcher(prev) {
369
- if (_internals)
370
- _internals.H = prev;
363
+ if (_internals) _internals.H = prev;
371
364
  }
372
365
 
373
366
  // src/slim-react/render.ts
@@ -398,8 +391,7 @@ function escapeAttr(str) {
398
391
  function styleObjectToString(style) {
399
392
  let result = "";
400
393
  for (const key in style) {
401
- if (result)
402
- result += ";";
394
+ if (result) result += ";";
403
395
  const cssKey = key.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
404
396
  result += cssKey + ":" + style[key];
405
397
  }
@@ -566,8 +558,7 @@ function renderAttributes(props, isSvg) {
566
558
  }
567
559
  if (key === "style" && typeof value === "object") {
568
560
  const styleStr = styleObjectToString(value);
569
- if (styleStr)
570
- attrs += ` style="${escapeAttr(styleStr)}"`;
561
+ if (styleStr) attrs += ` style="${escapeAttr(styleStr)}"`;
571
562
  continue;
572
563
  }
573
564
  attrs += ` ${attrName}="${escapeAttr(String(value))}"`;
@@ -586,14 +577,12 @@ var BufferWriter = class {
586
577
  this.lastWasText = true;
587
578
  }
588
579
  flush(target) {
589
- for (const c of this.chunks)
590
- target.write(c);
580
+ for (const c of this.chunks) target.write(c);
591
581
  target.lastWasText = this.lastWasText;
592
582
  }
593
583
  };
594
584
  function renderNode(node, writer, isSvg = false) {
595
- if (node == null || typeof node === "boolean")
596
- return;
585
+ if (node == null || typeof node === "boolean") return;
597
586
  if (typeof node === "string") {
598
587
  writer.text(escapeHtml(node));
599
588
  return;
@@ -614,8 +603,7 @@ function renderNode(node, writer, isSvg = false) {
614
603
  }
615
604
  if (typeof node === "object" && node !== null && "$$typeof" in node) {
616
605
  const elType = node["$$typeof"];
617
- if (elType !== SLIM_ELEMENT && elType !== REACT19_ELEMENT)
618
- return;
606
+ if (elType !== SLIM_ELEMENT && elType !== REACT19_ELEMENT) return;
619
607
  const element = node;
620
608
  const { type, props } = element;
621
609
  if (type === FRAGMENT_TYPE) {
@@ -636,17 +624,14 @@ function renderNode(node, writer, isSvg = false) {
636
624
  }
637
625
  }
638
626
  function markSelectedOptionsMulti(children, selectedValues) {
639
- if (children == null || typeof children === "boolean")
640
- return children;
641
- if (typeof children === "string" || typeof children === "number")
642
- return children;
627
+ if (children == null || typeof children === "boolean") return children;
628
+ if (typeof children === "string" || typeof children === "number") return children;
643
629
  if (Array.isArray(children)) {
644
630
  return children.map((c) => markSelectedOptionsMulti(c, selectedValues));
645
631
  }
646
632
  if (typeof children === "object" && "$$typeof" in children) {
647
633
  const elType = children["$$typeof"];
648
- if (elType !== SLIM_ELEMENT && elType !== REACT19_ELEMENT)
649
- return children;
634
+ if (elType !== SLIM_ELEMENT && elType !== REACT19_ELEMENT) return children;
650
635
  const el = children;
651
636
  if (el.type === "option") {
652
637
  const optValue = el.props.value !== void 0 ? el.props.value : el.props.children;
@@ -667,8 +652,7 @@ function renderHostElement(tag, props, writer, isSvg) {
667
652
  const textContent = props.value ?? props.defaultValue ?? props.children ?? "";
668
653
  const filteredProps = {};
669
654
  for (const k of Object.keys(props)) {
670
- if (k !== "value" && k !== "defaultValue" && k !== "children")
671
- filteredProps[k] = props[k];
655
+ if (k !== "value" && k !== "defaultValue" && k !== "children") filteredProps[k] = props[k];
672
656
  }
673
657
  writer.write(`<textarea${renderAttributes(filteredProps, false)}>`);
674
658
  writer.text(escapeHtml(String(textContent)));
@@ -679,8 +663,7 @@ function renderHostElement(tag, props, writer, isSvg) {
679
663
  const selectedValue = props.value ?? props.defaultValue;
680
664
  const filteredProps = {};
681
665
  for (const k of Object.keys(props)) {
682
- if (k !== "value" && k !== "defaultValue")
683
- filteredProps[k] = props[k];
666
+ if (k !== "value" && k !== "defaultValue") filteredProps[k] = props[k];
684
667
  }
685
668
  writer.write(`<select${renderAttributes(filteredProps, false)}>`);
686
669
  const selectedSet = selectedValue == null ? null : Array.isArray(selectedValue) ? new Set(selectedValue.map(String)) : /* @__PURE__ */ new Set([String(selectedValue)]);
@@ -714,12 +697,12 @@ function renderHostElement(tag, props, writer, isSvg) {
714
697
  }
715
698
  writer.write(`</${tag}>`);
716
699
  }
717
- var REACT_MEMO = Symbol.for("react.memo");
718
- var REACT_FORWARD_REF = Symbol.for("react.forward_ref");
719
- var REACT_PROVIDER = Symbol.for("react.provider");
720
- var REACT_CONTEXT = Symbol.for("react.context");
721
- var REACT_CONSUMER = Symbol.for("react.consumer");
722
- var REACT_LAZY = Symbol.for("react.lazy");
700
+ var REACT_MEMO = /* @__PURE__ */ Symbol.for("react.memo");
701
+ var REACT_FORWARD_REF = /* @__PURE__ */ Symbol.for("react.forward_ref");
702
+ var REACT_PROVIDER = /* @__PURE__ */ Symbol.for("react.provider");
703
+ var REACT_CONTEXT = /* @__PURE__ */ Symbol.for("react.context");
704
+ var REACT_CONSUMER = /* @__PURE__ */ Symbol.for("react.consumer");
705
+ var REACT_LAZY = /* @__PURE__ */ Symbol.for("react.lazy");
723
706
  function renderComponent(type, props, writer, isSvg) {
724
707
  const typeOf = type?.$$typeof;
725
708
  if (typeOf === REACT_MEMO) {
@@ -788,8 +771,7 @@ function renderComponent(type, props, writer, isSvg) {
788
771
  const instance = new type(props);
789
772
  if (typeof type.getDerivedStateFromProps === "function") {
790
773
  const derived = type.getDerivedStateFromProps(props, instance.state ?? {});
791
- if (derived != null)
792
- instance.state = { ...instance.state ?? {}, ...derived };
774
+ if (derived != null) instance.state = { ...instance.state ?? {}, ...derived };
793
775
  }
794
776
  result = instance.render();
795
777
  } else {
@@ -798,8 +780,7 @@ function renderComponent(type, props, writer, isSvg) {
798
780
  } catch (e) {
799
781
  restoreDispatcher(prevDispatcher);
800
782
  popComponentScope(savedScope);
801
- if (isProvider)
802
- popContextValue(ctx, prevCtxValue);
783
+ if (isProvider) popContextValue(ctx, prevCtxValue);
803
784
  throw e;
804
785
  }
805
786
  restoreDispatcher(prevDispatcher);
@@ -808,11 +789,9 @@ function renderComponent(type, props, writer, isSvg) {
808
789
  savedIdTree = pushTreeContext(1, 0);
809
790
  }
810
791
  const finish = () => {
811
- if (savedIdTree !== void 0)
812
- popTreeContext(savedIdTree);
792
+ if (savedIdTree !== void 0) popTreeContext(savedIdTree);
813
793
  popComponentScope(savedScope);
814
- if (isProvider)
815
- popContextValue(ctx, prevCtxValue);
794
+ if (isProvider) popContextValue(ctx, prevCtxValue);
816
795
  };
817
796
  if (result instanceof Promise) {
818
797
  const m = captureMap();
@@ -823,11 +802,9 @@ function renderComponent(type, props, writer, isSvg) {
823
802
  asyncSavedIdTree = pushTreeContext(1, 0);
824
803
  }
825
804
  const asyncFinish = () => {
826
- if (asyncSavedIdTree !== void 0)
827
- popTreeContext(asyncSavedIdTree);
805
+ if (asyncSavedIdTree !== void 0) popTreeContext(asyncSavedIdTree);
828
806
  popComponentScope(savedScope);
829
- if (isProvider)
830
- popContextValue(ctx, prevCtxValue);
807
+ if (isProvider) popContextValue(ctx, prevCtxValue);
831
808
  };
832
809
  const r2 = renderNode(resolved, writer, isSvg);
833
810
  if (r2 && typeof r2.then === "function") {
@@ -910,8 +887,7 @@ function renderChildArrayFrom(children, startIndex, writer, isSvg) {
910
887
  }
911
888
  }
912
889
  function renderChildren(children, writer, isSvg = false) {
913
- if (children == null)
914
- return;
890
+ if (children == null) return;
915
891
  if (Array.isArray(children)) {
916
892
  return renderChildArray(children, writer, isSvg);
917
893
  }
@@ -1027,14 +1003,12 @@ function processSegmentCache(html) {
1027
1003
  const cacheM = /data-cache="([^"]+)"/.exec(attrs);
1028
1004
  const keyM = /data-key="([^"]+)"/.exec(attrs);
1029
1005
  const ttlM = /data-ttl="(\d+)"/.exec(attrs);
1030
- if (!cacheM || !keyM)
1031
- return match;
1006
+ if (!cacheM || !keyM) return match;
1032
1007
  if (cacheM[1] === "miss") {
1033
1008
  setSegment(keyM[1], content, ttlM ? Number(ttlM[1]) : void 0);
1034
1009
  return content;
1035
1010
  }
1036
- if (cacheM[1] === "hit")
1037
- return content;
1011
+ if (cacheM[1] === "hit") return content;
1038
1012
  return match;
1039
1013
  }
1040
1014
  );
@@ -1060,17 +1034,14 @@ function renderHeadTag(tag, id, opts, selfClose = false) {
1060
1034
  let attrs = ` id="${escAttr(id)}"`;
1061
1035
  let inner = "";
1062
1036
  for (const [k, v] of Object.entries(opts)) {
1063
- if (k === "key" || k === "children")
1064
- continue;
1037
+ if (k === "key" || k === "children") continue;
1065
1038
  if (k === "dangerouslySetInnerHTML") {
1066
1039
  inner = v.__html ?? "";
1067
1040
  continue;
1068
1041
  }
1069
1042
  const attr = ATTR[k] ?? k;
1070
- if (v === true)
1071
- attrs += ` ${attr}`;
1072
- else if (v !== false && v != null)
1073
- attrs += ` ${attr}="${escAttr(String(v))}"`;
1043
+ if (v === true) attrs += ` ${attr}`;
1044
+ else if (v !== false && v != null) attrs += ` ${attr}="${escAttr(String(v))}"`;
1074
1045
  }
1075
1046
  return selfClose ? `<${tag}${attrs}>` : `<${tag}${attrs}>${inner}</${tag}>`;
1076
1047
  }
@@ -1114,8 +1085,7 @@ var getReactResponse = async (req, opts) => {
1114
1085
  const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
1115
1086
  const serverData = {};
1116
1087
  for (const [key, entry] of unsuspend.cache) {
1117
- if (entry.status === "fulfilled")
1118
- serverData[key] = entry.value;
1088
+ if (entry.status === "fulfilled") serverData[key] = entry.value;
1119
1089
  }
1120
1090
  const clientProps = {
1121
1091
  ...restProps,
@@ -1323,8 +1293,7 @@ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
1323
1293
  if (!isServerBuild && opts.reactMode !== void 0) {
1324
1294
  const rules = localConfig.module?.rules ?? [];
1325
1295
  for (const rule of rules) {
1326
- if (!rule?.use || !Array.isArray(rule.use))
1327
- continue;
1296
+ if (!rule?.use || !Array.isArray(rule.use)) continue;
1328
1297
  for (const entry2 of rule.use) {
1329
1298
  if (entry2?.loader?.includes("swc-loader")) {
1330
1299
  entry2.options = entry2.options ?? {};
@@ -1338,6 +1307,25 @@ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
1338
1307
  }
1339
1308
  }
1340
1309
  const extraPlugins = [];
1310
+ if (!isServerBuild) {
1311
+ extraPlugins.push({
1312
+ apply(compiler) {
1313
+ compiler.hooks.compilation.tap("HadarsWorkerChunkLoading", (compilation) => {
1314
+ compilation.hooks.childCompiler.tap(
1315
+ "HadarsWorkerChunkLoading",
1316
+ (childCompiler) => {
1317
+ if (childCompiler.options?.output) {
1318
+ childCompiler.options.output.chunkLoading = "import-scripts";
1319
+ }
1320
+ if (childCompiler.options?.experiments) {
1321
+ childCompiler.options.experiments.outputModule = false;
1322
+ }
1323
+ }
1324
+ );
1325
+ });
1326
+ }
1327
+ });
1328
+ }
1341
1329
  const defineValues = { ...opts.define ?? {} };
1342
1330
  if (!isServerBuild && opts.reactMode !== void 0) {
1343
1331
  defineValues["process.env.NODE_ENV"] = JSON.stringify(opts.reactMode);
@@ -1414,7 +1402,8 @@ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
1414
1402
  },
1415
1403
  isDev && !isServerBuild && new ReactRefreshPlugin(),
1416
1404
  includeHotPlugin && isDev && !isServerBuild && new rspack.HotModuleReplacementPlugin(),
1417
- ...extraPlugins
1405
+ ...extraPlugins,
1406
+ ...opts.plugins ?? []
1418
1407
  ],
1419
1408
  ...localConfig,
1420
1409
  // Merge base resolve (modules, tsConfig, extensions) with per-build resolve
@@ -1517,16 +1506,12 @@ var COMPRESSIBLE_RE = /\b(?:text\/|application\/(?:json|javascript|xml)|image\/s
1517
1506
  function withCompression(handler) {
1518
1507
  return async (req, ctx) => {
1519
1508
  const res = await handler(req, ctx);
1520
- if (!res?.body)
1521
- return res;
1522
- if (!COMPRESSIBLE_RE.test(res.headers.get("Content-Type") ?? ""))
1523
- return res;
1524
- if (res.headers.has("Content-Encoding"))
1525
- return res;
1509
+ if (!res?.body) return res;
1510
+ if (!COMPRESSIBLE_RE.test(res.headers.get("Content-Type") ?? "")) return res;
1511
+ if (res.headers.has("Content-Encoding")) return res;
1526
1512
  const accept = req.headers.get("Accept-Encoding") ?? "";
1527
1513
  const encoding = accept.includes("br") ? "br" : accept.includes("gzip") ? "gzip" : null;
1528
- if (!encoding)
1529
- return res;
1514
+ if (!encoding) return res;
1530
1515
  try {
1531
1516
  const compressed = res.body.pipeThrough(new globalThis.CompressionStream(encoding));
1532
1517
  const headers = new Headers(res.headers);
@@ -1604,8 +1589,7 @@ async function serve(port, fetchHandler, websocket) {
1604
1589
  const reader = response.body.getReader();
1605
1590
  while (true) {
1606
1591
  const { done, value } = await reader.read();
1607
- if (done)
1608
- break;
1592
+ if (done) break;
1609
1593
  await new Promise(
1610
1594
  (resolve2, reject) => nodeRes.write(value, (err) => err ? reject(err) : resolve2())
1611
1595
  );
@@ -1613,8 +1597,7 @@ async function serve(port, fetchHandler, websocket) {
1613
1597
  }
1614
1598
  } catch (err) {
1615
1599
  console.error("[hadars] request error", err);
1616
- if (!nodeRes.headersSent)
1617
- nodeRes.writeHead(500);
1600
+ if (!nodeRes.headersSent) nodeRes.writeHead(500);
1618
1601
  } finally {
1619
1602
  nodeRes.end();
1620
1603
  }
@@ -1677,7 +1660,122 @@ import { existsSync as existsSync2 } from "node:fs";
1677
1660
  import os from "node:os";
1678
1661
  import { spawn } from "node:child_process";
1679
1662
  import cluster from "node:cluster";
1663
+
1664
+ // src/utils/ssrHandler.ts
1665
+ var HEAD_MARKER = '<meta name="HADARS_HEAD">';
1666
+ var BODY_MARKER = '<meta name="HADARS_BODY">';
1680
1667
  var encoder = new TextEncoder();
1668
+ async function buildSsrResponse(bodyHtml, clientProps, headHtml, status, getPrecontentHtml) {
1669
+ const responseStream = new ReadableStream({
1670
+ async start(controller) {
1671
+ const [precontentHtml, postContent] = await getPrecontentHtml(headHtml);
1672
+ controller.enqueue(encoder.encode(precontentHtml));
1673
+ const scriptContent = JSON.stringify({ hadars: { props: clientProps } }).replace(/</g, "\\u003c");
1674
+ controller.enqueue(encoder.encode(
1675
+ `<div id="app">${bodyHtml}</div><script id="hadars" type="application/json">${scriptContent}</script>` + postContent
1676
+ ));
1677
+ controller.close();
1678
+ }
1679
+ });
1680
+ return new Response(responseStream, {
1681
+ headers: { "Content-Type": "text/html; charset=utf-8" },
1682
+ status
1683
+ });
1684
+ }
1685
+ var makePrecontentHtmlGetter = (htmlFilePromise) => {
1686
+ let preHead = null;
1687
+ let postHead = null;
1688
+ let postContent = null;
1689
+ return async (headHtml) => {
1690
+ if (preHead === null || postHead === null || postContent === null) {
1691
+ const html = await htmlFilePromise;
1692
+ const headEnd = html.indexOf(HEAD_MARKER);
1693
+ const contentStart = html.indexOf(BODY_MARKER);
1694
+ preHead = html.slice(0, headEnd);
1695
+ postHead = html.slice(headEnd + HEAD_MARKER.length, contentStart);
1696
+ postContent = html.slice(contentStart + BODY_MARKER.length);
1697
+ }
1698
+ return [preHead + headHtml + postHead, postContent];
1699
+ };
1700
+ };
1701
+ async function transformStream(data, stream) {
1702
+ const writer = stream.writable.getWriter();
1703
+ writer.write(data);
1704
+ writer.close();
1705
+ const chunks = [];
1706
+ const reader = stream.readable.getReader();
1707
+ while (true) {
1708
+ const { done, value } = await reader.read();
1709
+ if (done) break;
1710
+ chunks.push(value);
1711
+ }
1712
+ const total = chunks.reduce((n, c) => n + c.length, 0);
1713
+ const out = new Uint8Array(total);
1714
+ let offset = 0;
1715
+ for (const c of chunks) {
1716
+ out.set(c, offset);
1717
+ offset += c.length;
1718
+ }
1719
+ return out;
1720
+ }
1721
+ var gzipCompress = (d) => transformStream(d, new globalThis.CompressionStream("gzip"));
1722
+ var gzipDecompress = (d) => transformStream(d, new globalThis.DecompressionStream("gzip"));
1723
+ async function buildCacheEntry(res, ttl) {
1724
+ const buf = await res.arrayBuffer();
1725
+ const body = await gzipCompress(new Uint8Array(buf));
1726
+ const headers = [];
1727
+ res.headers.forEach((v, k) => {
1728
+ if (k.toLowerCase() !== "content-encoding" && k.toLowerCase() !== "content-length") {
1729
+ headers.push([k, v]);
1730
+ }
1731
+ });
1732
+ headers.push(["content-encoding", "gzip"]);
1733
+ return { body, status: res.status, headers, expiresAt: ttl != null ? Date.now() + ttl : null };
1734
+ }
1735
+ async function serveFromEntry(entry, req) {
1736
+ const accept = req.headers.get("Accept-Encoding") ?? "";
1737
+ if (accept.includes("gzip")) {
1738
+ return new Response(entry.body.buffer, { status: entry.status, headers: entry.headers });
1739
+ }
1740
+ const plain = await gzipDecompress(entry.body);
1741
+ const headers = entry.headers.filter(([k]) => k.toLowerCase() !== "content-encoding");
1742
+ return new Response(plain.buffer, { status: entry.status, headers });
1743
+ }
1744
+ function createRenderCache(opts, handler) {
1745
+ const store = /* @__PURE__ */ new Map();
1746
+ const inFlight = /* @__PURE__ */ new Map();
1747
+ return async (req, ctx) => {
1748
+ const hadarsReq = parseRequest(req);
1749
+ const cacheOpts = await opts(hadarsReq);
1750
+ const key = cacheOpts?.key ?? null;
1751
+ if (key != null) {
1752
+ const entry = store.get(key);
1753
+ if (entry) {
1754
+ const expired = entry.expiresAt != null && Date.now() >= entry.expiresAt;
1755
+ if (!expired) return serveFromEntry(entry, req);
1756
+ store.delete(key);
1757
+ }
1758
+ let flight = inFlight.get(key);
1759
+ if (!flight) {
1760
+ const ttl = cacheOpts?.ttl;
1761
+ flight = handler(new Request(req), ctx).then(async (res) => {
1762
+ if (!res || res.status < 200 || res.status >= 300 || res.headers.has("set-cookie")) {
1763
+ return null;
1764
+ }
1765
+ const newEntry2 = await buildCacheEntry(res, ttl);
1766
+ store.set(key, newEntry2);
1767
+ return newEntry2;
1768
+ }).catch(() => null).finally(() => inFlight.delete(key));
1769
+ inFlight.set(key, flight);
1770
+ }
1771
+ const newEntry = await flight;
1772
+ if (newEntry) return serveFromEntry(newEntry, req);
1773
+ }
1774
+ return handler(req, ctx);
1775
+ };
1776
+ }
1777
+
1778
+ // src/build.ts
1681
1779
  async function processHtmlTemplate(templatePath) {
1682
1780
  const html = await fs.readFile(templatePath, "utf-8");
1683
1781
  const styleRegex = /<style([^>]*)>([\s\S]*?)<\/style>/gi;
@@ -1686,8 +1784,7 @@ async function processHtmlTemplate(templatePath) {
1686
1784
  while ((m = styleRegex.exec(html)) !== null) {
1687
1785
  matches.push({ full: m[0], attrs: m[1] ?? "", css: m[2] ?? "" });
1688
1786
  }
1689
- if (matches.length === 0)
1690
- return templatePath;
1787
+ if (matches.length === 0) return templatePath;
1691
1788
  await ensureHadarsTmpDir();
1692
1789
  const sourceHash = crypto.createHash("md5").update(html).digest("hex").slice(0, 8);
1693
1790
  const cachedPath = pathMod2.join(HADARS_TMP_DIR, `template-${sourceHash}.html`);
@@ -1716,8 +1813,6 @@ async function processHtmlTemplate(templatePath) {
1716
1813
  await fs.writeFile(cachedPath, processedHtml);
1717
1814
  return cachedPath;
1718
1815
  }
1719
- var HEAD_MARKER = '<meta name="HADARS_HEAD">';
1720
- var BODY_MARKER = '<meta name="HADARS_BODY">';
1721
1816
  var RenderWorkerPool = class {
1722
1817
  workers = [];
1723
1818
  pending = /* @__PURE__ */ new Map();
@@ -1737,28 +1832,23 @@ var RenderWorkerPool = class {
1737
1832
  this._ssrBundlePath = ssrBundlePath;
1738
1833
  import("node:worker_threads").then(({ Worker }) => {
1739
1834
  this._Worker = Worker;
1740
- for (let i = 0; i < size; i++)
1741
- this._spawnWorker();
1835
+ for (let i = 0; i < size; i++) this._spawnWorker();
1742
1836
  }).catch((err) => {
1743
1837
  console.error("[hadars] Failed to initialise render worker pool:", err);
1744
1838
  });
1745
1839
  }
1746
1840
  _spawnWorker() {
1747
- if (!this._Worker)
1748
- return;
1841
+ if (!this._Worker) return;
1749
1842
  const w = new this._Worker(this._workerPath, { workerData: { ssrBundlePath: this._ssrBundlePath } });
1750
1843
  this.workerPending.set(w, /* @__PURE__ */ new Set());
1751
1844
  w.on("message", (msg) => {
1752
1845
  const { id, html, headHtml, status, error } = msg;
1753
1846
  const p = this.pending.get(id);
1754
- if (!p)
1755
- return;
1847
+ if (!p) return;
1756
1848
  this.pending.delete(id);
1757
1849
  this.workerPending.get(w)?.delete(id);
1758
- if (error)
1759
- p.reject(new Error(error));
1760
- else
1761
- p.resolve({ html, headHtml, status });
1850
+ if (error) p.reject(new Error(error));
1851
+ else p.resolve({ html, headHtml, status });
1762
1852
  });
1763
1853
  w.on("error", (err) => {
1764
1854
  console.error("[hadars] Render worker error:", err);
@@ -1774,8 +1864,7 @@ var RenderWorkerPool = class {
1774
1864
  }
1775
1865
  _handleWorkerDeath(w, err) {
1776
1866
  const idx = this.workers.indexOf(w);
1777
- if (idx !== -1)
1778
- this.workers.splice(idx, 1);
1867
+ if (idx !== -1) this.workers.splice(idx, 1);
1779
1868
  const ids = this.workerPending.get(w);
1780
1869
  if (ids) {
1781
1870
  for (const id of ids) {
@@ -1791,8 +1880,7 @@ var RenderWorkerPool = class {
1791
1880
  this._spawnWorker();
1792
1881
  }
1793
1882
  nextWorker() {
1794
- if (this.workers.length === 0)
1795
- return void 0;
1883
+ if (this.workers.length === 0) return void 0;
1796
1884
  const w = this.workers[this.rrIndex % this.workers.length];
1797
1885
  this.rrIndex++;
1798
1886
  return w;
@@ -1821,23 +1909,6 @@ var RenderWorkerPool = class {
1821
1909
  await Promise.all(this.workers.map((w) => w.terminate()));
1822
1910
  }
1823
1911
  };
1824
- async function buildSsrResponse(bodyHtml, clientProps, headHtml, status, getPrecontentHtml) {
1825
- const responseStream = new ReadableStream({
1826
- async start(controller) {
1827
- const [precontentHtml, postContent] = await getPrecontentHtml(headHtml);
1828
- controller.enqueue(encoder.encode(precontentHtml));
1829
- const scriptContent = JSON.stringify({ hadars: { props: clientProps } }).replace(/</g, "\\u003c");
1830
- controller.enqueue(encoder.encode(
1831
- `<div id="app">${bodyHtml}</div><script id="hadars" type="application/json">${scriptContent}</script>` + postContent
1832
- ));
1833
- controller.close();
1834
- }
1835
- });
1836
- return new Response(responseStream, {
1837
- headers: { "Content-Type": "text/html; charset=utf-8" },
1838
- status
1839
- });
1840
- }
1841
1912
  async function serializeRequest(req) {
1842
1913
  const isGetOrHead = ["GET", "HEAD"].includes(req.method ?? "GET");
1843
1914
  const body = isGetOrHead ? null : new Uint8Array(await req.clone().arrayBuffer());
@@ -1856,102 +1927,6 @@ async function serializeRequest(req) {
1856
1927
  cookies: req.cookies
1857
1928
  };
1858
1929
  }
1859
- var makePrecontentHtmlGetter = (htmlFilePromise) => {
1860
- let preHead = null;
1861
- let postHead = null;
1862
- let postContent = null;
1863
- return async (headHtml) => {
1864
- if (preHead === null || postHead === null || postContent === null) {
1865
- const html = await htmlFilePromise;
1866
- const headEnd = html.indexOf(HEAD_MARKER);
1867
- const contentStart = html.indexOf(BODY_MARKER);
1868
- preHead = html.slice(0, headEnd);
1869
- postHead = html.slice(headEnd + HEAD_MARKER.length, contentStart);
1870
- postContent = html.slice(contentStart + BODY_MARKER.length);
1871
- }
1872
- return [preHead + headHtml + postHead, postContent];
1873
- };
1874
- };
1875
- async function transformStream(data, stream) {
1876
- const writer = stream.writable.getWriter();
1877
- writer.write(data);
1878
- writer.close();
1879
- const chunks = [];
1880
- const reader = stream.readable.getReader();
1881
- while (true) {
1882
- const { done, value } = await reader.read();
1883
- if (done)
1884
- break;
1885
- chunks.push(value);
1886
- }
1887
- const total = chunks.reduce((n, c) => n + c.length, 0);
1888
- const out = new Uint8Array(total);
1889
- let offset = 0;
1890
- for (const c of chunks) {
1891
- out.set(c, offset);
1892
- offset += c.length;
1893
- }
1894
- return out;
1895
- }
1896
- var gzipCompress = (d) => transformStream(d, new globalThis.CompressionStream("gzip"));
1897
- var gzipDecompress = (d) => transformStream(d, new globalThis.DecompressionStream("gzip"));
1898
- async function buildCacheEntry(res, ttl) {
1899
- const buf = await res.arrayBuffer();
1900
- const body = await gzipCompress(new Uint8Array(buf));
1901
- const headers = [];
1902
- res.headers.forEach((v, k) => {
1903
- if (k.toLowerCase() !== "content-encoding" && k.toLowerCase() !== "content-length") {
1904
- headers.push([k, v]);
1905
- }
1906
- });
1907
- headers.push(["content-encoding", "gzip"]);
1908
- return { body, status: res.status, headers, expiresAt: ttl != null ? Date.now() + ttl : null };
1909
- }
1910
- async function serveFromEntry(entry, req) {
1911
- const accept = req.headers.get("Accept-Encoding") ?? "";
1912
- if (accept.includes("gzip")) {
1913
- return new Response(entry.body.buffer, { status: entry.status, headers: entry.headers });
1914
- }
1915
- const plain = await gzipDecompress(entry.body);
1916
- const headers = entry.headers.filter(([k]) => k.toLowerCase() !== "content-encoding");
1917
- return new Response(plain.buffer, { status: entry.status, headers });
1918
- }
1919
- function createRenderCache(opts, handler) {
1920
- const store = /* @__PURE__ */ new Map();
1921
- const inFlight = /* @__PURE__ */ new Map();
1922
- return async (req, ctx) => {
1923
- const hadarsReq = parseRequest(req);
1924
- const cacheOpts = await opts(hadarsReq);
1925
- const key = cacheOpts?.key ?? null;
1926
- if (key != null) {
1927
- const entry = store.get(key);
1928
- if (entry) {
1929
- const expired = entry.expiresAt != null && Date.now() >= entry.expiresAt;
1930
- if (!expired) {
1931
- return serveFromEntry(entry, req);
1932
- }
1933
- store.delete(key);
1934
- }
1935
- let flight = inFlight.get(key);
1936
- if (!flight) {
1937
- const ttl = cacheOpts?.ttl;
1938
- flight = handler(new Request(req), ctx).then(async (res) => {
1939
- if (!res || res.status < 200 || res.status >= 300 || res.headers.has("set-cookie")) {
1940
- return null;
1941
- }
1942
- const newEntry2 = await buildCacheEntry(res, ttl);
1943
- store.set(key, newEntry2);
1944
- return newEntry2;
1945
- }).catch(() => null).finally(() => inFlight.delete(key));
1946
- inFlight.set(key, flight);
1947
- }
1948
- const newEntry = await flight;
1949
- if (newEntry)
1950
- return serveFromEntry(newEntry, req);
1951
- }
1952
- return handler(req, ctx);
1953
- };
1954
- }
1955
1930
  var SSR_FILENAME = "index.ssr.js";
1956
1931
  var __dirname2 = process.cwd();
1957
1932
  var getSuffix = (mode) => mode === "development" ? `?v=${Date.now()}` : "";
@@ -1980,10 +1955,8 @@ var resolveWorkerCmd = (packageDir2) => {
1980
1955
  const allArgs = [...process.execArgv, process.argv[1] ?? ""];
1981
1956
  const hasTsx = allArgs.some((a) => a.includes("tsx"));
1982
1957
  const hasTsNode = allArgs.some((a) => a.includes("ts-node"));
1983
- if (hasTsx)
1984
- return ["tsx", tsPath];
1985
- if (hasTsNode)
1986
- return ["ts-node", tsPath];
1958
+ if (hasTsx) return ["tsx", tsPath];
1959
+ if (hasTsNode) return ["ts-node", tsPath];
1987
1960
  }
1988
1961
  if (existsSync2(jsPath)) {
1989
1962
  return ["node", jsPath];
@@ -2030,6 +2003,7 @@ var dev = async (options) => {
2030
2003
  swcPlugins: options.swcPlugins,
2031
2004
  define: options.define,
2032
2005
  moduleRules: options.moduleRules,
2006
+ plugins: options.plugins,
2033
2007
  reactMode: options.reactMode,
2034
2008
  htmlTemplate: resolvedHtmlTemplate
2035
2009
  });
@@ -2073,8 +2047,7 @@ var dev = async (options) => {
2073
2047
  child.stdin?.end();
2074
2048
  const cleanupChild = () => {
2075
2049
  try {
2076
- if (!child.killed)
2077
- child.kill();
2050
+ if (!child.killed) child.kill();
2078
2051
  } catch {
2079
2052
  }
2080
2053
  };
@@ -2133,8 +2106,7 @@ var dev = async (options) => {
2133
2106
  try {
2134
2107
  while (true) {
2135
2108
  const { done, value } = await reader.read();
2136
- if (done)
2137
- break;
2109
+ if (done) break;
2138
2110
  const chunk = decoder.decode(value, { stream: true });
2139
2111
  try {
2140
2112
  process.stdout.write(chunk);
@@ -2155,8 +2127,7 @@ var dev = async (options) => {
2155
2127
  const r = stderrWebStream.getReader();
2156
2128
  while (true) {
2157
2129
  const { done, value } = await r.read();
2158
- if (done)
2159
- break;
2130
+ if (done) break;
2160
2131
  try {
2161
2132
  process.stderr.write(decoder.decode(value));
2162
2133
  } catch (e) {
@@ -2173,23 +2144,18 @@ var dev = async (options) => {
2173
2144
  const request = parseRequest(req);
2174
2145
  if (handler) {
2175
2146
  const res = await handler(request);
2176
- if (res)
2177
- return res;
2147
+ if (res) return res;
2178
2148
  }
2179
- if (handleWS && handleWS(request, ctx))
2180
- return void 0;
2149
+ if (handleWS && handleWS(request, ctx)) return void 0;
2181
2150
  const proxied = await handleProxy(request);
2182
- if (proxied)
2183
- return proxied;
2151
+ if (proxied) return proxied;
2184
2152
  const url = new URL(request.url);
2185
2153
  const path2 = url.pathname;
2186
2154
  const staticRes = await tryServeFile(pathMod2.join(__dirname2, StaticPath, path2));
2187
- if (staticRes)
2188
- return staticRes;
2155
+ if (staticRes) return staticRes;
2189
2156
  const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
2190
2157
  const projectRes = await tryServeFile(pathMod2.join(projectStaticPath, path2));
2191
- if (projectRes)
2192
- return projectRes;
2158
+ if (projectRes) return projectRes;
2193
2159
  const ssrComponentPath = pathMod2.join(__dirname2, HadarsFolder, SSR_FILENAME);
2194
2160
  const importPath = pathToFileURL(ssrComponentPath).href + `?t=${ssrBuildId}`;
2195
2161
  try {
@@ -2256,6 +2222,7 @@ var build = async (options) => {
2256
2222
  swcPlugins: options.swcPlugins,
2257
2223
  define: options.define,
2258
2224
  moduleRules: options.moduleRules,
2225
+ plugins: options.plugins,
2259
2226
  optimization: options.optimization,
2260
2227
  reactMode: options.reactMode,
2261
2228
  htmlTemplate: resolvedHtmlTemplate
@@ -2273,7 +2240,8 @@ var build = async (options) => {
2273
2240
  mode: "production",
2274
2241
  swcPlugins: options.swcPlugins,
2275
2242
  define: options.define,
2276
- moduleRules: options.moduleRules
2243
+ moduleRules: options.moduleRules,
2244
+ plugins: options.plugins
2277
2245
  })
2278
2246
  ]);
2279
2247
  await fs.rm(tmpFilePath);
@@ -2316,30 +2284,24 @@ var run = async (options) => {
2316
2284
  const request = parseRequest(req);
2317
2285
  if (handler) {
2318
2286
  const res = await handler(request);
2319
- if (res)
2320
- return res;
2287
+ if (res) return res;
2321
2288
  }
2322
- if (handleWS && handleWS(request, ctx))
2323
- return void 0;
2289
+ if (handleWS && handleWS(request, ctx)) return void 0;
2324
2290
  const proxied = await handleProxy(request);
2325
- if (proxied)
2326
- return proxied;
2291
+ if (proxied) return proxied;
2327
2292
  const url = new URL(request.url);
2328
2293
  const path2 = url.pathname;
2329
2294
  const staticRes = await tryServeFile(pathMod2.join(__dirname2, StaticPath, path2));
2330
- if (staticRes)
2331
- return staticRes;
2295
+ if (staticRes) return staticRes;
2332
2296
  const projectStaticPath = pathMod2.resolve(process.cwd(), "static");
2333
2297
  const projectRes = await tryServeFile(pathMod2.join(projectStaticPath, path2));
2334
- if (projectRes)
2335
- return projectRes;
2298
+ if (projectRes) return projectRes;
2336
2299
  const routeClean = path2.replace(/(^\/|\/$)/g, "");
2337
2300
  if (routeClean) {
2338
2301
  const routeRes = await tryServeFile(
2339
2302
  pathMod2.join(__dirname2, StaticPath, routeClean, "index.html")
2340
2303
  );
2341
- if (routeRes)
2342
- return routeRes;
2304
+ if (routeRes) return routeRes;
2343
2305
  }
2344
2306
  const componentPath = pathToFileURL(
2345
2307
  pathMod2.resolve(__dirname2, HadarsFolder, SSR_FILENAME)
@@ -2394,8 +2356,7 @@ var SUPPORTED = ["hadars.config.js", "hadars.config.mjs", "hadars.config.cjs", "
2394
2356
  function findConfig(cwd) {
2395
2357
  for (const name of SUPPORTED) {
2396
2358
  const p = resolve(cwd, name);
2397
- if (existsSync3(p))
2398
- return p;
2359
+ if (existsSync3(p)) return p;
2399
2360
  }
2400
2361
  return null;
2401
2362
  }
@@ -2423,6 +2384,61 @@ async function loadConfig(configPath) {
2423
2384
  const mod = await import(url);
2424
2385
  return mod && (mod.default ?? mod);
2425
2386
  }
2387
+ async function bundleLambda(config, configPath, outputFile, cwd) {
2388
+ console.log("Building hadars project...");
2389
+ await build({ ...config, mode: "production" });
2390
+ const ssrBundle = resolve(cwd, ".hadars", "index.ssr.js");
2391
+ const outHtml = resolve(cwd, ".hadars", "static", "out.html");
2392
+ if (!existsSync3(ssrBundle)) {
2393
+ console.error(`SSR bundle not found: ${ssrBundle}`);
2394
+ process.exit(1);
2395
+ }
2396
+ if (!existsSync3(outHtml)) {
2397
+ console.error(`HTML template not found: ${outHtml}`);
2398
+ process.exit(1);
2399
+ }
2400
+ const lambdaModule = resolve(dirname(fileURLToPath3(import.meta.url)), "lambda.js");
2401
+ const shimPath = join(cwd, `.hadars-lambda-shim-${Date.now()}.ts`);
2402
+ const shim = [
2403
+ `import * as ssrModule from ${JSON.stringify(ssrBundle)};`,
2404
+ `import outHtml from ${JSON.stringify(outHtml)};`,
2405
+ `import { createLambdaHandler } from ${JSON.stringify(lambdaModule)};`,
2406
+ `import config from ${JSON.stringify(configPath)};`,
2407
+ `export const handler = createLambdaHandler(config as any, { ssrModule: ssrModule as any, outHtml });`
2408
+ ].join("\n") + "\n";
2409
+ await writeFile(shimPath, shim, "utf-8");
2410
+ try {
2411
+ const { build: esbuild } = await import("esbuild");
2412
+ console.log(`Bundling Lambda handler \u2192 ${outputFile}`);
2413
+ await esbuild({
2414
+ entryPoints: [shimPath],
2415
+ bundle: true,
2416
+ platform: "node",
2417
+ format: "esm",
2418
+ target: ["node20"],
2419
+ outfile: outputFile,
2420
+ sourcemap: false,
2421
+ loader: { ".html": "text", ".tsx": "tsx", ".ts": "ts" },
2422
+ // @rspack/* contains native binaries and is build-time only —
2423
+ // it is never imported at Lambda runtime, so mark it external.
2424
+ // Everything else (React, hadars runtime, etc.) is bundled in to
2425
+ // produce a truly self-contained single-file deployment.
2426
+ external: ["@rspack/*"]
2427
+ });
2428
+ console.log(`Lambda bundle written to ${outputFile}`);
2429
+ console.log(`
2430
+ Deploy instructions:`);
2431
+ console.log(` 1. Create a staging directory with just this file:`);
2432
+ console.log(` mkdir -p lambda-deploy && cp ${outputFile} lambda-deploy/lambda.mjs`);
2433
+ console.log(` 2. Upload lambda-deploy/ as your Lambda function code`);
2434
+ console.log(` 3. Set handler to: lambda.handler (runtime: Node.js 20.x)`);
2435
+ console.log(` 4. Upload .hadars/static/ assets to S3 and serve via CloudFront`);
2436
+ console.log(` (the Lambda handler does not serve static JS/CSS \u2014 route those to S3)`);
2437
+ } finally {
2438
+ await unlink(shimPath).catch(() => {
2439
+ });
2440
+ }
2441
+ }
2426
2442
  var TEMPLATES = {
2427
2443
  "package.json": (name) => JSON.stringify({
2428
2444
  name,
@@ -2676,7 +2692,7 @@ Done! Next steps:
2676
2692
  `);
2677
2693
  }
2678
2694
  function usage() {
2679
- console.log("Usage: hadars <new <name> | dev | build | run>");
2695
+ console.log("Usage: hadars <new <name> | dev | build | run | export lambda [output.mjs]>");
2680
2696
  }
2681
2697
  async function runCli(argv, cwd = process.cwd()) {
2682
2698
  const cmd = argv[2];
@@ -2694,7 +2710,7 @@ async function runCli(argv, cwd = process.cwd()) {
2694
2710
  }
2695
2711
  return;
2696
2712
  }
2697
- if (!cmd || !["dev", "build", "run"].includes(cmd)) {
2713
+ if (!cmd || !["dev", "build", "run", "export"].includes(cmd)) {
2698
2714
  usage();
2699
2715
  process.exit(1);
2700
2716
  }
@@ -2717,6 +2733,16 @@ async function runCli(argv, cwd = process.cwd()) {
2717
2733
  await build2(cfg);
2718
2734
  console.log("Build complete");
2719
2735
  process.exit(0);
2736
+ case "export": {
2737
+ const subCmd = argv[3];
2738
+ if (subCmd !== "lambda") {
2739
+ console.error(`Unknown export target: ${subCmd ?? "(none)"}. Did you mean: hadars export lambda`);
2740
+ process.exit(1);
2741
+ }
2742
+ const outputFile = resolve(cwd, argv[4] ?? "lambda.mjs");
2743
+ await bundleLambda(cfg, configPath, outputFile, cwd);
2744
+ process.exit(0);
2745
+ }
2720
2746
  case "run":
2721
2747
  console.log("Running project...");
2722
2748
  await run2(cfg);
@@ -2741,11 +2767,9 @@ if (typeof globalThis.Bun === "undefined" && typeof globalThis.Deno === "undefin
2741
2767
  } catch {
2742
2768
  }
2743
2769
  };
2744
- for (const sig of sigs)
2745
- process.on(sig, fwd(sig));
2770
+ for (const sig of sigs) process.on(sig, fwd(sig));
2746
2771
  child.on("error", (err) => {
2747
- for (const sig of sigs)
2748
- process.removeAllListeners(sig);
2772
+ for (const sig of sigs) process.removeAllListeners(sig);
2749
2773
  if (err.code !== "ENOENT") {
2750
2774
  console.error(err);
2751
2775
  process.exit(1);