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.
@@ -0,0 +1,93 @@
1
+ import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-B3b_6sj2.cjs';
2
+ import 'react';
3
+
4
+ /**
5
+ * AWS Lambda adapter for hadars.
6
+ *
7
+ * After running `hadars build`, import this in your Lambda entry file:
8
+ *
9
+ * // lambda.ts
10
+ * import { createLambdaHandler } from 'hadars/lambda';
11
+ * import config from './hadars.config';
12
+ * export const handler = createLambdaHandler(config);
13
+ *
14
+ * The returned handler speaks the API Gateway HTTP API (v2) payload format.
15
+ * API Gateway REST API (v1) events are also accepted.
16
+ *
17
+ * Static assets (JS, CSS, fonts) are served directly from the bundled
18
+ * `.hadars/static/` directory. For production use, front the Lambda with
19
+ * CloudFront and route static paths to an S3 origin instead.
20
+ */
21
+
22
+ /** API Gateway HTTP API (v2) event. */
23
+ interface APIGatewayV2Event {
24
+ version: '2.0';
25
+ routeKey: string;
26
+ rawPath: string;
27
+ rawQueryString: string;
28
+ cookies?: string[];
29
+ headers: Record<string, string>;
30
+ requestContext: {
31
+ http: {
32
+ method: string;
33
+ };
34
+ domainName: string;
35
+ };
36
+ body?: string;
37
+ isBase64Encoded: boolean;
38
+ }
39
+ /** API Gateway REST API (v1) event. */
40
+ interface APIGatewayV1Event {
41
+ httpMethod: string;
42
+ path: string;
43
+ queryStringParameters?: Record<string, string> | null;
44
+ headers?: Record<string, string> | null;
45
+ body?: string | null;
46
+ isBase64Encoded: boolean;
47
+ }
48
+ type APIGatewayEvent = APIGatewayV2Event | APIGatewayV1Event;
49
+ interface LambdaResponse {
50
+ statusCode: number;
51
+ headers: Record<string, string>;
52
+ body: string;
53
+ isBase64Encoded: boolean;
54
+ }
55
+ type LambdaHandler = (event: APIGatewayEvent) => Promise<LambdaResponse>;
56
+ /**
57
+ * Pre-loaded module and HTML for single-file Lambda bundles created by
58
+ * `hadars export lambda`. Passing this bypasses all runtime file I/O so
59
+ * the handler works without a `.hadars/` directory on disk.
60
+ */
61
+ interface LambdaBundled {
62
+ /** The compiled SSR module — import it statically in your entry shim. */
63
+ ssrModule: HadarsEntryModule<any>;
64
+ /**
65
+ * The contents of `.hadars/static/out.html` — load it as a text string
66
+ * at build time (esbuild's `--loader:.html=text` does this automatically
67
+ * when `hadars export lambda` runs).
68
+ */
69
+ outHtml: string;
70
+ }
71
+ /**
72
+ * Creates an AWS Lambda handler from a hadars config.
73
+ *
74
+ * Must be called after `hadars build` has produced the `.hadars/` output
75
+ * directory. The handler is stateless — Lambda can reuse the same instance
76
+ * across invocations; the SSR module and HTML template are cached in memory
77
+ * after the first warm invocation.
78
+ *
79
+ * Pass a {@link LambdaBundled} object as the second argument when using
80
+ * `hadars export lambda` to produce a single-file bundle — in that mode all
81
+ * I/O is eliminated and the handler is fully self-contained.
82
+ *
83
+ * @example — standard (file-based) deployment
84
+ * export const handler = createLambdaHandler(config);
85
+ *
86
+ * @example — single-file bundle (generated entry shim)
87
+ * import * as ssrModule from './.hadars/index.ssr.js';
88
+ * import outHtml from './.hadars/static/out.html';
89
+ * export const handler = createLambdaHandler(config, { ssrModule, outHtml });
90
+ */
91
+ declare function createLambdaHandler(options: HadarsOptions, bundled?: LambdaBundled): LambdaHandler;
92
+
93
+ export { type APIGatewayEvent, type APIGatewayV1Event, type APIGatewayV2Event, type LambdaBundled, type LambdaHandler, type LambdaResponse, createLambdaHandler };
@@ -0,0 +1,93 @@
1
+ import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-B3b_6sj2.js';
2
+ import 'react';
3
+
4
+ /**
5
+ * AWS Lambda adapter for hadars.
6
+ *
7
+ * After running `hadars build`, import this in your Lambda entry file:
8
+ *
9
+ * // lambda.ts
10
+ * import { createLambdaHandler } from 'hadars/lambda';
11
+ * import config from './hadars.config';
12
+ * export const handler = createLambdaHandler(config);
13
+ *
14
+ * The returned handler speaks the API Gateway HTTP API (v2) payload format.
15
+ * API Gateway REST API (v1) events are also accepted.
16
+ *
17
+ * Static assets (JS, CSS, fonts) are served directly from the bundled
18
+ * `.hadars/static/` directory. For production use, front the Lambda with
19
+ * CloudFront and route static paths to an S3 origin instead.
20
+ */
21
+
22
+ /** API Gateway HTTP API (v2) event. */
23
+ interface APIGatewayV2Event {
24
+ version: '2.0';
25
+ routeKey: string;
26
+ rawPath: string;
27
+ rawQueryString: string;
28
+ cookies?: string[];
29
+ headers: Record<string, string>;
30
+ requestContext: {
31
+ http: {
32
+ method: string;
33
+ };
34
+ domainName: string;
35
+ };
36
+ body?: string;
37
+ isBase64Encoded: boolean;
38
+ }
39
+ /** API Gateway REST API (v1) event. */
40
+ interface APIGatewayV1Event {
41
+ httpMethod: string;
42
+ path: string;
43
+ queryStringParameters?: Record<string, string> | null;
44
+ headers?: Record<string, string> | null;
45
+ body?: string | null;
46
+ isBase64Encoded: boolean;
47
+ }
48
+ type APIGatewayEvent = APIGatewayV2Event | APIGatewayV1Event;
49
+ interface LambdaResponse {
50
+ statusCode: number;
51
+ headers: Record<string, string>;
52
+ body: string;
53
+ isBase64Encoded: boolean;
54
+ }
55
+ type LambdaHandler = (event: APIGatewayEvent) => Promise<LambdaResponse>;
56
+ /**
57
+ * Pre-loaded module and HTML for single-file Lambda bundles created by
58
+ * `hadars export lambda`. Passing this bypasses all runtime file I/O so
59
+ * the handler works without a `.hadars/` directory on disk.
60
+ */
61
+ interface LambdaBundled {
62
+ /** The compiled SSR module — import it statically in your entry shim. */
63
+ ssrModule: HadarsEntryModule<any>;
64
+ /**
65
+ * The contents of `.hadars/static/out.html` — load it as a text string
66
+ * at build time (esbuild's `--loader:.html=text` does this automatically
67
+ * when `hadars export lambda` runs).
68
+ */
69
+ outHtml: string;
70
+ }
71
+ /**
72
+ * Creates an AWS Lambda handler from a hadars config.
73
+ *
74
+ * Must be called after `hadars build` has produced the `.hadars/` output
75
+ * directory. The handler is stateless — Lambda can reuse the same instance
76
+ * across invocations; the SSR module and HTML template are cached in memory
77
+ * after the first warm invocation.
78
+ *
79
+ * Pass a {@link LambdaBundled} object as the second argument when using
80
+ * `hadars export lambda` to produce a single-file bundle — in that mode all
81
+ * I/O is eliminated and the handler is fully self-contained.
82
+ *
83
+ * @example — standard (file-based) deployment
84
+ * export const handler = createLambdaHandler(config);
85
+ *
86
+ * @example — single-file bundle (generated entry shim)
87
+ * import * as ssrModule from './.hadars/index.ssr.js';
88
+ * import outHtml from './.hadars/static/out.html';
89
+ * export const handler = createLambdaHandler(config, { ssrModule, outHtml });
90
+ */
91
+ declare function createLambdaHandler(options: HadarsOptions, bundled?: LambdaBundled): LambdaHandler;
92
+
93
+ export { type APIGatewayEvent, type APIGatewayV1Event, type APIGatewayV2Event, type LambdaBundled, type LambdaHandler, type LambdaResponse, createLambdaHandler };
package/dist/lambda.js ADDED
@@ -0,0 +1,499 @@
1
+ import {
2
+ processSegmentCache
3
+ } from "./chunk-UNQSQIOO.js";
4
+ import {
5
+ renderToString
6
+ } from "./chunk-F7IIMM3J.js";
7
+ import {
8
+ createElement
9
+ } from "./chunk-OS3V4CPN.js";
10
+
11
+ // src/lambda.ts
12
+ import "react";
13
+ import pathMod from "node:path";
14
+ import { pathToFileURL } from "node:url";
15
+ import fs from "node:fs/promises";
16
+
17
+ // src/utils/proxyHandler.tsx
18
+ var cloneHeaders = (headers) => {
19
+ return new Headers(headers);
20
+ };
21
+ var getCORSHeaders = (req) => {
22
+ const origin = req.headers.get("Origin") || "*";
23
+ return {
24
+ "Access-Control-Allow-Origin": origin,
25
+ "Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
26
+ "Access-Control-Allow-Headers": req.headers.get("Access-Control-Request-Headers") || "*",
27
+ "Access-Control-Allow-Credentials": "true"
28
+ };
29
+ };
30
+ var createProxyHandler = (options) => {
31
+ const { proxy, proxyCORS } = options;
32
+ if (!proxy) {
33
+ return () => void 0;
34
+ }
35
+ if (typeof proxy === "function") {
36
+ return async (req) => {
37
+ if (req.method === "OPTIONS" && options.proxyCORS) {
38
+ return new Response(null, {
39
+ status: 204,
40
+ headers: getCORSHeaders(req)
41
+ });
42
+ }
43
+ const res = await proxy(req);
44
+ if (res && proxyCORS) {
45
+ const modifiedHeaders = new Headers(res.headers);
46
+ Object.entries(getCORSHeaders(req)).forEach(([key, value]) => {
47
+ modifiedHeaders.set(key, value);
48
+ });
49
+ return new Response(res.body, {
50
+ status: res.status,
51
+ statusText: res.statusText,
52
+ headers: modifiedHeaders
53
+ });
54
+ }
55
+ return res || void 0;
56
+ };
57
+ }
58
+ const proxyRules = Object.entries(proxy).sort((a, b) => b[0].length - a[0].length);
59
+ return async (req) => {
60
+ for (const [path, target] of proxyRules) {
61
+ if (req.pathname.startsWith(path)) {
62
+ if (req.method === "OPTIONS" && proxyCORS) {
63
+ return new Response(null, {
64
+ status: 204,
65
+ headers: getCORSHeaders(req)
66
+ });
67
+ }
68
+ const targetURL = new URL(target);
69
+ targetURL.pathname = targetURL.pathname.replace(/\/$/, "") + req.pathname.slice(path.length);
70
+ targetURL.search = req.search;
71
+ const sendHeaders = cloneHeaders(req.headers);
72
+ sendHeaders.set("Host", targetURL.host);
73
+ const hasBody = !["GET", "HEAD"].includes(req.method);
74
+ const proxyReq = new Request(targetURL.toString(), {
75
+ method: req.method,
76
+ headers: sendHeaders,
77
+ body: hasBody ? req.body : void 0,
78
+ redirect: "follow",
79
+ // Node.js (undici) requires duplex:'half' when body is a ReadableStream
80
+ ...hasBody ? { duplex: "half" } : {}
81
+ });
82
+ const res = await fetch(proxyReq);
83
+ const body = await res.arrayBuffer();
84
+ const clonedRes = new Headers(res.headers);
85
+ clonedRes.delete("content-length");
86
+ clonedRes.delete("content-encoding");
87
+ if (proxyCORS) {
88
+ Object.entries(getCORSHeaders(req)).forEach(([key, value]) => {
89
+ clonedRes.set(key, value);
90
+ });
91
+ }
92
+ return new Response(body, {
93
+ status: res.status,
94
+ statusText: res.statusText,
95
+ headers: clonedRes
96
+ });
97
+ }
98
+ }
99
+ return void 0;
100
+ };
101
+ };
102
+
103
+ // src/utils/cookies.ts
104
+ var parseCookies = (cookieString) => {
105
+ const cookies = {};
106
+ if (!cookieString) {
107
+ return cookies;
108
+ }
109
+ const pairs = cookieString.split(";");
110
+ for (const pair of pairs) {
111
+ const index = pair.indexOf("=");
112
+ if (index > -1) {
113
+ const key = pair.slice(0, index).trim();
114
+ const value = pair.slice(index + 1).trim();
115
+ cookies[key] = decodeURIComponent(value);
116
+ }
117
+ }
118
+ return cookies;
119
+ };
120
+
121
+ // src/utils/request.tsx
122
+ var parseRequest = (request) => {
123
+ const url = new URL(request.url);
124
+ const cookies = request.headers.get("Cookie") || "";
125
+ const cookieRecord = parseCookies(cookies);
126
+ return Object.assign(request, { pathname: url.pathname, search: url.search, location: url.pathname + url.search, cookies: cookieRecord });
127
+ };
128
+
129
+ // src/utils/staticFile.ts
130
+ import { readFile, stat } from "node:fs/promises";
131
+ var MIME = {
132
+ html: "text/html; charset=utf-8",
133
+ htm: "text/html; charset=utf-8",
134
+ css: "text/css",
135
+ js: "application/javascript",
136
+ mjs: "application/javascript",
137
+ cjs: "application/javascript",
138
+ json: "application/json",
139
+ map: "application/json",
140
+ png: "image/png",
141
+ jpg: "image/jpeg",
142
+ jpeg: "image/jpeg",
143
+ gif: "image/gif",
144
+ webp: "image/webp",
145
+ svg: "image/svg+xml",
146
+ ico: "image/x-icon",
147
+ woff: "font/woff",
148
+ woff2: "font/woff2",
149
+ ttf: "font/ttf",
150
+ otf: "font/otf",
151
+ txt: "text/plain",
152
+ xml: "application/xml",
153
+ pdf: "application/pdf"
154
+ };
155
+ async function tryServeFile(filePath) {
156
+ try {
157
+ await stat(filePath);
158
+ } catch {
159
+ return null;
160
+ }
161
+ try {
162
+ const data = await readFile(filePath);
163
+ const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
164
+ const contentType = MIME[ext] ?? "application/octet-stream";
165
+ return new Response(data.buffer, { headers: { "Content-Type": contentType } });
166
+ } catch {
167
+ return null;
168
+ }
169
+ }
170
+
171
+ // src/utils/response.tsx
172
+ var ESC = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" };
173
+ var escAttr = (s) => s.replace(/[&<>"]/g, (c) => ESC[c] ?? c);
174
+ var escText = (s) => s.replace(/[&<>]/g, (c) => ESC[c] ?? c);
175
+ var ATTR = {
176
+ className: "class",
177
+ htmlFor: "for",
178
+ httpEquiv: "http-equiv",
179
+ charSet: "charset",
180
+ crossOrigin: "crossorigin",
181
+ noModule: "nomodule",
182
+ referrerPolicy: "referrerpolicy",
183
+ fetchPriority: "fetchpriority"
184
+ };
185
+ function renderHeadTag(tag, id, opts, selfClose = false) {
186
+ let attrs = ` id="${escAttr(id)}"`;
187
+ let inner = "";
188
+ for (const [k, v] of Object.entries(opts)) {
189
+ if (k === "key" || k === "children") continue;
190
+ if (k === "dangerouslySetInnerHTML") {
191
+ inner = v.__html ?? "";
192
+ continue;
193
+ }
194
+ const attr = ATTR[k] ?? k;
195
+ if (v === true) attrs += ` ${attr}`;
196
+ else if (v !== false && v != null) attrs += ` ${attr}="${escAttr(String(v))}"`;
197
+ }
198
+ return selfClose ? `<${tag}${attrs}>` : `<${tag}${attrs}>${inner}</${tag}>`;
199
+ }
200
+ var getHeadHtml = (seoData) => {
201
+ let html = `<title>${escText(seoData.title ?? "")}</title>`;
202
+ for (const [id, opts] of Object.entries(seoData.meta))
203
+ html += renderHeadTag("meta", id, opts, true);
204
+ for (const [id, opts] of Object.entries(seoData.link))
205
+ html += renderHeadTag("link", id, opts, true);
206
+ for (const [id, opts] of Object.entries(seoData.style))
207
+ html += renderHeadTag("style", id, opts);
208
+ for (const [id, opts] of Object.entries(seoData.script))
209
+ html += renderHeadTag("script", id, opts);
210
+ return html;
211
+ };
212
+ var getReactResponse = async (req, opts) => {
213
+ const App = opts.document.body;
214
+ const { getInitProps, getAfterRenderProps, getFinalProps } = opts.document;
215
+ const context = {
216
+ head: { title: "Hadars App", meta: {}, link: {}, style: {}, script: {}, status: 200 }
217
+ };
218
+ let props = {
219
+ ...getInitProps ? await getInitProps(req) : {},
220
+ location: req.location,
221
+ context
222
+ };
223
+ const unsuspend = { cache: /* @__PURE__ */ new Map() };
224
+ globalThis.__hadarsUnsuspend = unsuspend;
225
+ let bodyHtml;
226
+ try {
227
+ bodyHtml = await renderToString(createElement(App, props));
228
+ if (getAfterRenderProps) {
229
+ props = await getAfterRenderProps(props, bodyHtml);
230
+ bodyHtml = await renderToString(
231
+ createElement(App, { ...props, location: req.location, context })
232
+ );
233
+ }
234
+ } finally {
235
+ globalThis.__hadarsUnsuspend = null;
236
+ }
237
+ const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
238
+ const serverData = {};
239
+ for (const [key, entry] of unsuspend.cache) {
240
+ if (entry.status === "fulfilled") serverData[key] = entry.value;
241
+ }
242
+ const clientProps = {
243
+ ...restProps,
244
+ location: req.location,
245
+ ...Object.keys(serverData).length > 0 ? { __serverData: serverData } : {}
246
+ };
247
+ return {
248
+ bodyHtml: processSegmentCache(bodyHtml),
249
+ clientProps,
250
+ status: context.head.status,
251
+ headHtml: getHeadHtml(context.head)
252
+ };
253
+ };
254
+
255
+ // src/utils/ssrHandler.ts
256
+ var HEAD_MARKER = '<meta name="HADARS_HEAD">';
257
+ var BODY_MARKER = '<meta name="HADARS_BODY">';
258
+ var encoder = new TextEncoder();
259
+ async function buildSsrHtml(bodyHtml, clientProps, headHtml, getPrecontentHtml) {
260
+ const [precontentHtml, postContent] = await getPrecontentHtml(headHtml);
261
+ const scriptContent = JSON.stringify({ hadars: { props: clientProps } }).replace(/</g, "\\u003c");
262
+ return precontentHtml + `<div id="app">${bodyHtml}</div><script id="hadars" type="application/json">${scriptContent}</script>` + postContent;
263
+ }
264
+ var makePrecontentHtmlGetter = (htmlFilePromise) => {
265
+ let preHead = null;
266
+ let postHead = null;
267
+ let postContent = null;
268
+ return async (headHtml) => {
269
+ if (preHead === null || postHead === null || postContent === null) {
270
+ const html = await htmlFilePromise;
271
+ const headEnd = html.indexOf(HEAD_MARKER);
272
+ const contentStart = html.indexOf(BODY_MARKER);
273
+ preHead = html.slice(0, headEnd);
274
+ postHead = html.slice(headEnd + HEAD_MARKER.length, contentStart);
275
+ postContent = html.slice(contentStart + BODY_MARKER.length);
276
+ }
277
+ return [preHead + headHtml + postHead, postContent];
278
+ };
279
+ };
280
+ async function transformStream(data, stream) {
281
+ const writer = stream.writable.getWriter();
282
+ writer.write(data);
283
+ writer.close();
284
+ const chunks = [];
285
+ const reader = stream.readable.getReader();
286
+ while (true) {
287
+ const { done, value } = await reader.read();
288
+ if (done) break;
289
+ chunks.push(value);
290
+ }
291
+ const total = chunks.reduce((n, c) => n + c.length, 0);
292
+ const out = new Uint8Array(total);
293
+ let offset = 0;
294
+ for (const c of chunks) {
295
+ out.set(c, offset);
296
+ offset += c.length;
297
+ }
298
+ return out;
299
+ }
300
+ var gzipCompress = (d) => transformStream(d, new globalThis.CompressionStream("gzip"));
301
+ var gzipDecompress = (d) => transformStream(d, new globalThis.DecompressionStream("gzip"));
302
+ async function buildCacheEntry(res, ttl) {
303
+ const buf = await res.arrayBuffer();
304
+ const body = await gzipCompress(new Uint8Array(buf));
305
+ const headers = [];
306
+ res.headers.forEach((v, k) => {
307
+ if (k.toLowerCase() !== "content-encoding" && k.toLowerCase() !== "content-length") {
308
+ headers.push([k, v]);
309
+ }
310
+ });
311
+ headers.push(["content-encoding", "gzip"]);
312
+ return { body, status: res.status, headers, expiresAt: ttl != null ? Date.now() + ttl : null };
313
+ }
314
+ async function serveFromEntry(entry, req) {
315
+ const accept = req.headers.get("Accept-Encoding") ?? "";
316
+ if (accept.includes("gzip")) {
317
+ return new Response(entry.body.buffer, { status: entry.status, headers: entry.headers });
318
+ }
319
+ const plain = await gzipDecompress(entry.body);
320
+ const headers = entry.headers.filter(([k]) => k.toLowerCase() !== "content-encoding");
321
+ return new Response(plain.buffer, { status: entry.status, headers });
322
+ }
323
+ function createRenderCache(opts, handler) {
324
+ const store = /* @__PURE__ */ new Map();
325
+ const inFlight = /* @__PURE__ */ new Map();
326
+ return async (req, ctx) => {
327
+ const hadarsReq = parseRequest(req);
328
+ const cacheOpts = await opts(hadarsReq);
329
+ const key = cacheOpts?.key ?? null;
330
+ if (key != null) {
331
+ const entry = store.get(key);
332
+ if (entry) {
333
+ const expired = entry.expiresAt != null && Date.now() >= entry.expiresAt;
334
+ if (!expired) return serveFromEntry(entry, req);
335
+ store.delete(key);
336
+ }
337
+ let flight = inFlight.get(key);
338
+ if (!flight) {
339
+ const ttl = cacheOpts?.ttl;
340
+ flight = handler(new Request(req), ctx).then(async (res) => {
341
+ if (!res || res.status < 200 || res.status >= 300 || res.headers.has("set-cookie")) {
342
+ return null;
343
+ }
344
+ const newEntry2 = await buildCacheEntry(res, ttl);
345
+ store.set(key, newEntry2);
346
+ return newEntry2;
347
+ }).catch(() => null).finally(() => inFlight.delete(key));
348
+ inFlight.set(key, flight);
349
+ }
350
+ const newEntry = await flight;
351
+ if (newEntry) return serveFromEntry(newEntry, req);
352
+ }
353
+ return handler(req, ctx);
354
+ };
355
+ }
356
+
357
+ // src/lambda.ts
358
+ function eventToRequest(event) {
359
+ let method;
360
+ let path;
361
+ let queryString;
362
+ let headers;
363
+ let rawBody;
364
+ let isBase64;
365
+ let host;
366
+ if ("version" in event && event.version === "2.0") {
367
+ method = event.requestContext.http.method;
368
+ path = event.rawPath;
369
+ queryString = event.rawQueryString;
370
+ headers = { ...event.headers };
371
+ if (event.cookies?.length) headers["cookie"] = event.cookies.join("; ");
372
+ rawBody = event.body;
373
+ isBase64 = event.isBase64Encoded;
374
+ host = event.requestContext.domainName;
375
+ } else {
376
+ const e = event;
377
+ method = e.httpMethod;
378
+ path = e.path;
379
+ const qs = e.queryStringParameters;
380
+ queryString = qs ? new URLSearchParams(qs).toString() : "";
381
+ headers = e.headers ?? {};
382
+ rawBody = e.body;
383
+ isBase64 = e.isBase64Encoded;
384
+ host = headers["host"] ?? "lambda";
385
+ }
386
+ const url = `https://${host}${path}${queryString ? "?" + queryString : ""}`;
387
+ let body;
388
+ if (rawBody) body = isBase64 ? Buffer.from(rawBody, "base64") : rawBody;
389
+ return new Request(url, {
390
+ method,
391
+ headers,
392
+ body: ["GET", "HEAD"].includes(method) ? void 0 : body
393
+ });
394
+ }
395
+ var TEXT_RE = /\b(?:text\/|application\/(?:json|javascript|xml)|image\/svg\+xml)/;
396
+ async function responseToLambda(response) {
397
+ const headers = {};
398
+ response.headers.forEach((v, k) => {
399
+ headers[k] = v;
400
+ });
401
+ const contentType = response.headers.get("Content-Type") ?? "";
402
+ if (TEXT_RE.test(contentType)) {
403
+ return {
404
+ statusCode: response.status,
405
+ headers,
406
+ body: await response.text(),
407
+ isBase64Encoded: false
408
+ };
409
+ }
410
+ return {
411
+ statusCode: response.status,
412
+ headers,
413
+ body: Buffer.from(await response.arrayBuffer()).toString("base64"),
414
+ isBase64Encoded: true
415
+ };
416
+ }
417
+ var HadarsFolder = "./.hadars";
418
+ var StaticPath = `${HadarsFolder}/static`;
419
+ var SSR_FILENAME = "index.ssr.js";
420
+ var noopCtx = { upgrade: () => false };
421
+ function createLambdaHandler(options, bundled) {
422
+ const cwd = process.cwd();
423
+ const fetchHandler = options.fetch;
424
+ const handleProxy = createProxyHandler(options);
425
+ const getPrecontentHtml = bundled ? makePrecontentHtmlGetter(Promise.resolve(bundled.outHtml)) : makePrecontentHtmlGetter(fs.readFile(pathMod.join(cwd, StaticPath, "out.html"), "utf-8"));
426
+ let ssrModulePromise = null;
427
+ const getSsrModule = () => {
428
+ if (bundled) return Promise.resolve(bundled.ssrModule);
429
+ if (!ssrModulePromise) {
430
+ ssrModulePromise = import(pathToFileURL(pathMod.resolve(cwd, HadarsFolder, SSR_FILENAME)).href);
431
+ }
432
+ return ssrModulePromise;
433
+ };
434
+ const runHandler = async (req) => {
435
+ const request = parseRequest(req);
436
+ if (fetchHandler) {
437
+ const res = await fetchHandler(request);
438
+ if (res) return res;
439
+ }
440
+ const proxied = await handleProxy(request);
441
+ if (proxied) return proxied;
442
+ const urlPath = new URL(request.url).pathname;
443
+ if (!bundled) {
444
+ const staticRes = await tryServeFile(pathMod.join(cwd, StaticPath, urlPath));
445
+ if (staticRes) return staticRes;
446
+ const projectStaticPath = pathMod.resolve(cwd, "static");
447
+ const projectRes = await tryServeFile(pathMod.join(projectStaticPath, urlPath));
448
+ if (projectRes) return projectRes;
449
+ const routeClean = urlPath.replace(/(^\/|\/$)/g, "");
450
+ if (routeClean) {
451
+ const routeRes = await tryServeFile(
452
+ pathMod.join(cwd, StaticPath, routeClean, "index.html")
453
+ );
454
+ if (routeRes) return routeRes;
455
+ }
456
+ }
457
+ try {
458
+ const {
459
+ default: Component,
460
+ getInitProps,
461
+ getAfterRenderProps,
462
+ getFinalProps
463
+ } = await getSsrModule();
464
+ const { bodyHtml, clientProps, status, headHtml } = await getReactResponse(request, {
465
+ document: {
466
+ body: Component,
467
+ lang: "en",
468
+ getInitProps,
469
+ getAfterRenderProps,
470
+ getFinalProps
471
+ }
472
+ });
473
+ if (request.headers.get("Accept") === "application/json") {
474
+ const serverData = clientProps.__serverData ?? {};
475
+ return new Response(JSON.stringify({ serverData }), {
476
+ status,
477
+ headers: { "Content-Type": "application/json; charset=utf-8" }
478
+ });
479
+ }
480
+ const html = await buildSsrHtml(bodyHtml, clientProps, headHtml, getPrecontentHtml);
481
+ return new Response(html, {
482
+ status,
483
+ headers: { "Content-Type": "text/html; charset=utf-8" }
484
+ });
485
+ } catch (err) {
486
+ console.error("[hadars] SSR render error:", err);
487
+ return new Response("Internal Server Error", { status: 500 });
488
+ }
489
+ };
490
+ const finalHandler = options.cache ? createRenderCache(options.cache, (req) => runHandler(req)) : (req) => runHandler(req);
491
+ return async (event) => {
492
+ const req = eventToRequest(event);
493
+ const response = await finalHandler(req, noopCtx) ?? new Response("Not Found", { status: 404 });
494
+ return responseToLambda(response);
495
+ };
496
+ }
497
+ export {
498
+ createLambdaHandler
499
+ };