hadars 0.1.40 → 0.2.1

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.
Files changed (42) hide show
  1. package/README.md +85 -70
  2. package/cli-lib.ts +89 -12
  3. package/dist/chunk-HWOLYLPF.js +332 -0
  4. package/dist/{chunk-2ENP7IAW.js → chunk-LY5MTHFV.js} +360 -203
  5. package/dist/cli.js +506 -274
  6. package/dist/cloudflare.cjs +1394 -0
  7. package/dist/cloudflare.d.cts +64 -0
  8. package/dist/cloudflare.d.ts +64 -0
  9. package/dist/cloudflare.js +68 -0
  10. package/dist/{hadars-Bh-V5YXg.d.cts → hadars-DEBSYAQl.d.cts} +1 -36
  11. package/dist/{hadars-Bh-V5YXg.d.ts → hadars-DEBSYAQl.d.ts} +1 -36
  12. package/dist/index.cjs +129 -156
  13. package/dist/index.d.cts +5 -11
  14. package/dist/index.d.ts +5 -11
  15. package/dist/index.js +129 -155
  16. package/dist/lambda.cjs +391 -229
  17. package/dist/lambda.d.cts +1 -2
  18. package/dist/lambda.d.ts +1 -2
  19. package/dist/lambda.js +18 -307
  20. package/dist/slim-react/index.cjs +361 -203
  21. package/dist/slim-react/index.d.cts +24 -8
  22. package/dist/slim-react/index.d.ts +24 -8
  23. package/dist/slim-react/index.js +3 -1
  24. package/dist/ssr-render-worker.js +352 -221
  25. package/dist/utils/Head.tsx +132 -187
  26. package/package.json +7 -2
  27. package/src/build.ts +7 -6
  28. package/src/cloudflare.ts +139 -0
  29. package/src/index.tsx +0 -3
  30. package/src/lambda.ts +6 -2
  31. package/src/slim-react/context.ts +2 -1
  32. package/src/slim-react/index.ts +21 -18
  33. package/src/slim-react/render.ts +379 -240
  34. package/src/slim-react/renderContext.ts +105 -45
  35. package/src/ssr-render-worker.ts +14 -44
  36. package/src/types/hadars.ts +0 -1
  37. package/src/utils/Head.tsx +132 -187
  38. package/src/utils/cookies.ts +1 -1
  39. package/src/utils/response.tsx +68 -33
  40. package/src/utils/serve.ts +29 -27
  41. package/src/utils/ssrHandler.ts +54 -25
  42. package/src/utils/staticFile.ts +2 -7
package/dist/lambda.d.cts CHANGED
@@ -1,5 +1,4 @@
1
- import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-Bh-V5YXg.cjs';
2
- import 'react';
1
+ import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-DEBSYAQl.cjs';
3
2
 
4
3
  /**
5
4
  * AWS Lambda adapter for hadars.
package/dist/lambda.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-Bh-V5YXg.js';
2
- import 'react';
1
+ import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-DEBSYAQl.js';
3
2
 
4
3
  /**
5
4
  * AWS Lambda adapter for hadars.
package/dist/lambda.js CHANGED
@@ -1,9 +1,14 @@
1
1
  import {
2
- renderToString
3
- } from "./chunk-2ENP7IAW.js";
4
- import {
5
- createElement
6
- } from "./chunk-OS3V4CPN.js";
2
+ buildHeadHtml,
3
+ buildSsrHtml,
4
+ createProxyHandler,
5
+ createRenderCache,
6
+ getReactResponse,
7
+ makePrecontentHtmlGetter,
8
+ parseRequest
9
+ } from "./chunk-HWOLYLPF.js";
10
+ import "./chunk-LY5MTHFV.js";
11
+ import "./chunk-OS3V4CPN.js";
7
12
 
8
13
  // src/lambda.ts
9
14
  import "react";
@@ -11,120 +16,8 @@ import pathMod from "node:path";
11
16
  import { pathToFileURL } from "node:url";
12
17
  import fs from "node:fs/promises";
13
18
 
14
- // src/utils/proxyHandler.tsx
15
- var cloneHeaders = (headers) => {
16
- return new Headers(headers);
17
- };
18
- var getCORSHeaders = (req) => {
19
- const origin = req.headers.get("Origin") || "*";
20
- return {
21
- "Access-Control-Allow-Origin": origin,
22
- "Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
23
- "Access-Control-Allow-Headers": req.headers.get("Access-Control-Request-Headers") || "*",
24
- "Access-Control-Allow-Credentials": "true"
25
- };
26
- };
27
- var createProxyHandler = (options) => {
28
- const { proxy, proxyCORS } = options;
29
- if (!proxy) {
30
- return () => void 0;
31
- }
32
- if (typeof proxy === "function") {
33
- return async (req) => {
34
- if (req.method === "OPTIONS" && options.proxyCORS) {
35
- return new Response(null, {
36
- status: 204,
37
- headers: getCORSHeaders(req)
38
- });
39
- }
40
- const res = await proxy(req);
41
- if (res && proxyCORS) {
42
- const modifiedHeaders = new Headers(res.headers);
43
- Object.entries(getCORSHeaders(req)).forEach(([key, value]) => {
44
- modifiedHeaders.set(key, value);
45
- });
46
- return new Response(res.body, {
47
- status: res.status,
48
- statusText: res.statusText,
49
- headers: modifiedHeaders
50
- });
51
- }
52
- return res || void 0;
53
- };
54
- }
55
- const proxyRules = Object.entries(proxy).sort((a, b) => b[0].length - a[0].length);
56
- return async (req) => {
57
- for (const [path, target] of proxyRules) {
58
- if (req.pathname.startsWith(path)) {
59
- if (req.method === "OPTIONS" && proxyCORS) {
60
- return new Response(null, {
61
- status: 204,
62
- headers: getCORSHeaders(req)
63
- });
64
- }
65
- const targetURL = new URL(target);
66
- targetURL.pathname = targetURL.pathname.replace(/\/$/, "") + req.pathname.slice(path.length);
67
- targetURL.search = req.search;
68
- const sendHeaders = cloneHeaders(req.headers);
69
- sendHeaders.set("Host", targetURL.host);
70
- const hasBody = !["GET", "HEAD"].includes(req.method);
71
- const proxyReq = new Request(targetURL.toString(), {
72
- method: req.method,
73
- headers: sendHeaders,
74
- body: hasBody ? req.body : void 0,
75
- redirect: "follow",
76
- // Node.js (undici) requires duplex:'half' when body is a ReadableStream
77
- ...hasBody ? { duplex: "half" } : {}
78
- });
79
- const res = await fetch(proxyReq);
80
- const body = await res.arrayBuffer();
81
- const clonedRes = new Headers(res.headers);
82
- clonedRes.delete("content-length");
83
- clonedRes.delete("content-encoding");
84
- if (proxyCORS) {
85
- Object.entries(getCORSHeaders(req)).forEach(([key, value]) => {
86
- clonedRes.set(key, value);
87
- });
88
- }
89
- return new Response(body, {
90
- status: res.status,
91
- statusText: res.statusText,
92
- headers: clonedRes
93
- });
94
- }
95
- }
96
- return void 0;
97
- };
98
- };
99
-
100
- // src/utils/cookies.ts
101
- var parseCookies = (cookieString) => {
102
- const cookies = {};
103
- if (!cookieString) {
104
- return cookies;
105
- }
106
- const pairs = cookieString.split(";");
107
- for (const pair of pairs) {
108
- const index = pair.indexOf("=");
109
- if (index > -1) {
110
- const key = pair.slice(0, index).trim();
111
- const value = pair.slice(index + 1).trim();
112
- cookies[key] = decodeURIComponent(value);
113
- }
114
- }
115
- return cookies;
116
- };
117
-
118
- // src/utils/request.tsx
119
- var parseRequest = (request) => {
120
- const url = new URL(request.url);
121
- const cookies = request.headers.get("Cookie") || "";
122
- const cookieRecord = parseCookies(cookies);
123
- return Object.assign(request, { pathname: url.pathname, search: url.search, location: url.pathname + url.search, cookies: cookieRecord });
124
- };
125
-
126
19
  // src/utils/staticFile.ts
127
- import { readFile, stat } from "node:fs/promises";
20
+ import { readFile } from "node:fs/promises";
128
21
  var MIME = {
129
22
  html: "text/html; charset=utf-8",
130
23
  htm: "text/html; charset=utf-8",
@@ -150,202 +43,16 @@ var MIME = {
150
43
  pdf: "application/pdf"
151
44
  };
152
45
  async function tryServeFile(filePath) {
153
- try {
154
- await stat(filePath);
155
- } catch {
156
- return null;
157
- }
158
46
  try {
159
47
  const data = await readFile(filePath);
160
48
  const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
161
49
  const contentType = MIME[ext] ?? "application/octet-stream";
162
- return new Response(data.buffer, { headers: { "Content-Type": contentType } });
50
+ return new Response(data, { headers: { "Content-Type": contentType } });
163
51
  } catch {
164
52
  return null;
165
53
  }
166
54
  }
167
55
 
168
- // src/utils/response.tsx
169
- var ESC = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" };
170
- var escAttr = (s) => s.replace(/[&<>"]/g, (c) => ESC[c] ?? c);
171
- var escText = (s) => s.replace(/[&<>]/g, (c) => ESC[c] ?? c);
172
- var ATTR = {
173
- className: "class",
174
- htmlFor: "for",
175
- httpEquiv: "http-equiv",
176
- charSet: "charset",
177
- crossOrigin: "crossorigin",
178
- noModule: "nomodule",
179
- referrerPolicy: "referrerpolicy",
180
- fetchPriority: "fetchpriority",
181
- hrefLang: "hreflang"
182
- };
183
- function renderHeadTag(tag, opts, selfClose = false) {
184
- let attrs = "";
185
- let inner = "";
186
- for (const [k, v] of Object.entries(opts)) {
187
- if (k === "key" || k === "children") continue;
188
- if (k === "dangerouslySetInnerHTML") {
189
- inner = v.__html ?? "";
190
- continue;
191
- }
192
- const attr = ATTR[k] ?? k;
193
- if (v === true) attrs += ` ${attr}`;
194
- else if (v !== false && v != null) attrs += ` ${attr}="${escAttr(String(v))}"`;
195
- }
196
- return selfClose ? `<${tag}${attrs}>` : `<${tag}${attrs}>${inner}</${tag}>`;
197
- }
198
- var getHeadHtml = (seoData) => {
199
- let html = `<title>${escText(seoData.title ?? "")}</title>`;
200
- for (const opts of Object.values(seoData.meta))
201
- html += renderHeadTag("meta", opts, true);
202
- for (const opts of Object.values(seoData.link))
203
- html += renderHeadTag("link", opts, true);
204
- for (const opts of Object.values(seoData.style))
205
- html += renderHeadTag("style", opts);
206
- for (const opts of Object.values(seoData.script))
207
- html += renderHeadTag("script", opts);
208
- return html;
209
- };
210
- var getReactResponse = async (req, opts) => {
211
- const App = opts.document.body;
212
- const { getInitProps, getFinalProps } = opts.document;
213
- const context = {
214
- head: { title: "Hadars App", meta: {}, link: {}, style: {}, script: {}, status: 200 }
215
- };
216
- let props = {
217
- ...getInitProps ? await getInitProps(req) : {},
218
- location: req.location,
219
- context
220
- };
221
- const unsuspend = { cache: /* @__PURE__ */ new Map() };
222
- globalThis.__hadarsUnsuspend = unsuspend;
223
- let bodyHtml;
224
- try {
225
- bodyHtml = await renderToString(createElement(App, props));
226
- } finally {
227
- globalThis.__hadarsUnsuspend = null;
228
- }
229
- const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
230
- const serverData = {};
231
- for (const [key, entry] of unsuspend.cache) {
232
- if (entry.status === "fulfilled") serverData[key] = entry.value;
233
- }
234
- const clientProps = {
235
- ...restProps,
236
- location: req.location,
237
- ...Object.keys(serverData).length > 0 ? { __serverData: serverData } : {}
238
- };
239
- return {
240
- bodyHtml,
241
- clientProps,
242
- status: context.head.status,
243
- headHtml: getHeadHtml(context.head)
244
- };
245
- };
246
-
247
- // src/utils/ssrHandler.ts
248
- var HEAD_MARKER = '<meta name="HADARS_HEAD">';
249
- var BODY_MARKER = '<meta name="HADARS_BODY">';
250
- var encoder = new TextEncoder();
251
- async function buildSsrHtml(bodyHtml, clientProps, headHtml, getPrecontentHtml) {
252
- const [precontentHtml, postContent] = await getPrecontentHtml(headHtml);
253
- const scriptContent = JSON.stringify({ hadars: { props: clientProps } }).replace(/</g, "\\u003c");
254
- return precontentHtml + `<div id="app">${bodyHtml}</div><script id="hadars" type="application/json">${scriptContent}</script>` + postContent;
255
- }
256
- var makePrecontentHtmlGetter = (htmlFilePromise) => {
257
- let preHead = null;
258
- let postHead = null;
259
- let postContent = null;
260
- return async (headHtml) => {
261
- if (preHead === null || postHead === null || postContent === null) {
262
- const html = await htmlFilePromise;
263
- const headEnd = html.indexOf(HEAD_MARKER);
264
- const contentStart = html.indexOf(BODY_MARKER);
265
- preHead = html.slice(0, headEnd);
266
- postHead = html.slice(headEnd + HEAD_MARKER.length, contentStart);
267
- postContent = html.slice(contentStart + BODY_MARKER.length);
268
- }
269
- return [preHead + headHtml + postHead, postContent];
270
- };
271
- };
272
- async function transformStream(data, stream) {
273
- const writer = stream.writable.getWriter();
274
- writer.write(data);
275
- writer.close();
276
- const chunks = [];
277
- const reader = stream.readable.getReader();
278
- while (true) {
279
- const { done, value } = await reader.read();
280
- if (done) break;
281
- chunks.push(value);
282
- }
283
- const total = chunks.reduce((n, c) => n + c.length, 0);
284
- const out = new Uint8Array(total);
285
- let offset = 0;
286
- for (const c of chunks) {
287
- out.set(c, offset);
288
- offset += c.length;
289
- }
290
- return out;
291
- }
292
- var gzipCompress = (d) => transformStream(d, new globalThis.CompressionStream("gzip"));
293
- var gzipDecompress = (d) => transformStream(d, new globalThis.DecompressionStream("gzip"));
294
- async function buildCacheEntry(res, ttl) {
295
- const buf = await res.arrayBuffer();
296
- const body = await gzipCompress(new Uint8Array(buf));
297
- const headers = [];
298
- res.headers.forEach((v, k) => {
299
- if (k.toLowerCase() !== "content-encoding" && k.toLowerCase() !== "content-length") {
300
- headers.push([k, v]);
301
- }
302
- });
303
- headers.push(["content-encoding", "gzip"]);
304
- return { body, status: res.status, headers, expiresAt: ttl != null ? Date.now() + ttl : null };
305
- }
306
- async function serveFromEntry(entry, req) {
307
- const accept = req.headers.get("Accept-Encoding") ?? "";
308
- if (accept.includes("gzip")) {
309
- return new Response(entry.body.buffer, { status: entry.status, headers: entry.headers });
310
- }
311
- const plain = await gzipDecompress(entry.body);
312
- const headers = entry.headers.filter(([k]) => k.toLowerCase() !== "content-encoding");
313
- return new Response(plain.buffer, { status: entry.status, headers });
314
- }
315
- function createRenderCache(opts, handler) {
316
- const store = /* @__PURE__ */ new Map();
317
- const inFlight = /* @__PURE__ */ new Map();
318
- return async (req, ctx) => {
319
- const hadarsReq = parseRequest(req);
320
- const cacheOpts = await opts(hadarsReq);
321
- const key = cacheOpts?.key ?? null;
322
- if (key != null) {
323
- const entry = store.get(key);
324
- if (entry) {
325
- const expired = entry.expiresAt != null && Date.now() >= entry.expiresAt;
326
- if (!expired) return serveFromEntry(entry, req);
327
- store.delete(key);
328
- }
329
- let flight = inFlight.get(key);
330
- if (!flight) {
331
- const ttl = cacheOpts?.ttl;
332
- flight = handler(new Request(req), ctx).then(async (res) => {
333
- if (!res || res.status < 200 || res.status >= 300 || res.headers.has("set-cookie")) {
334
- return null;
335
- }
336
- const newEntry2 = await buildCacheEntry(res, ttl);
337
- store.set(key, newEntry2);
338
- return newEntry2;
339
- }).catch(() => null).finally(() => inFlight.delete(key));
340
- inFlight.set(key, flight);
341
- }
342
- const newEntry = await flight;
343
- if (newEntry) return serveFromEntry(newEntry, req);
344
- }
345
- return handler(req, ctx);
346
- };
347
- }
348
-
349
56
  // src/lambda.ts
350
57
  function eventToRequest(event) {
351
58
  let method;
@@ -451,7 +158,7 @@ function createLambdaHandler(options, bundled) {
451
158
  getInitProps,
452
159
  getFinalProps
453
160
  } = await getSsrModule();
454
- const { bodyHtml, clientProps, status, headHtml } = await getReactResponse(request, {
161
+ const { head, status, getAppBody, finalize } = await getReactResponse(request, {
455
162
  document: {
456
163
  body: Component,
457
164
  lang: "en",
@@ -460,12 +167,16 @@ function createLambdaHandler(options, bundled) {
460
167
  }
461
168
  });
462
169
  if (request.headers.get("Accept") === "application/json") {
463
- const serverData = clientProps.__serverData ?? {};
170
+ const { clientProps: clientProps2 } = await finalize();
171
+ const serverData = clientProps2.__serverData ?? {};
464
172
  return new Response(JSON.stringify({ serverData }), {
465
173
  status,
466
174
  headers: { "Content-Type": "application/json; charset=utf-8" }
467
175
  });
468
176
  }
177
+ const bodyHtml = await getAppBody();
178
+ const { clientProps } = await finalize();
179
+ const headHtml = buildHeadHtml(head);
469
180
  const html = await buildSsrHtml(bodyHtml, clientProps, headHtml, getPrecontentHtml);
470
181
  return new Response(html, {
471
182
  status,