hadars 0.2.0 → 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.
- package/README.md +84 -15
- package/cli-lib.ts +89 -12
- package/dist/chunk-HWOLYLPF.js +332 -0
- package/dist/cli.js +82 -12
- package/dist/cloudflare.cjs +1394 -0
- package/dist/cloudflare.d.cts +64 -0
- package/dist/cloudflare.d.ts +64 -0
- package/dist/cloudflare.js +68 -0
- package/dist/{hadars-Bh-V5YXg.d.cts → hadars-DEBSYAQl.d.cts} +1 -36
- package/dist/{hadars-Bh-V5YXg.d.ts → hadars-DEBSYAQl.d.ts} +1 -36
- package/dist/index.cjs +129 -156
- package/dist/index.d.cts +5 -11
- package/dist/index.d.ts +5 -11
- package/dist/index.js +129 -155
- package/dist/lambda.cjs +4 -0
- package/dist/lambda.d.cts +1 -2
- package/dist/lambda.d.ts +1 -2
- package/dist/lambda.js +10 -317
- package/dist/ssr-render-worker.js +3 -2
- package/dist/utils/Head.tsx +132 -187
- package/package.json +7 -2
- package/src/cloudflare.ts +139 -0
- package/src/index.tsx +0 -3
- package/src/ssr-render-worker.ts +4 -1
- package/src/types/hadars.ts +0 -1
- package/src/utils/Head.tsx +132 -187
- package/src/utils/response.tsx +6 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-DEBSYAQl.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cloudflare Workers adapter for hadars.
|
|
5
|
+
*
|
|
6
|
+
* After running `hadars build`, bundle your app with:
|
|
7
|
+
*
|
|
8
|
+
* hadars export cloudflare
|
|
9
|
+
*
|
|
10
|
+
* This produces a self-contained `cloudflare.mjs` that you deploy with:
|
|
11
|
+
*
|
|
12
|
+
* wrangler deploy
|
|
13
|
+
*
|
|
14
|
+
* Static assets (JS, CSS, fonts) under `.hadars/static/` must be served from
|
|
15
|
+
* R2 or another CDN — the Worker only handles HTML rendering. Route requests
|
|
16
|
+
* for static file extensions to R2 and everything else to the Worker.
|
|
17
|
+
*
|
|
18
|
+
* @example wrangler.toml
|
|
19
|
+
* name = "my-app"
|
|
20
|
+
* main = "cloudflare.mjs"
|
|
21
|
+
* compatibility_date = "2024-09-23"
|
|
22
|
+
* compatibility_flags = ["nodejs_compat"]
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Pre-loaded SSR module and HTML template for single-file Cloudflare bundles
|
|
27
|
+
* produced by `hadars export cloudflare`. All I/O is eliminated at runtime —
|
|
28
|
+
* the Worker is fully self-contained.
|
|
29
|
+
*/
|
|
30
|
+
interface CloudflareBundled {
|
|
31
|
+
/** The compiled SSR module — import it statically in your entry shim. */
|
|
32
|
+
ssrModule: HadarsEntryModule<any>;
|
|
33
|
+
/**
|
|
34
|
+
* The contents of `.hadars/static/out.html` — esbuild inlines this as a
|
|
35
|
+
* string when `hadars export cloudflare` runs.
|
|
36
|
+
*/
|
|
37
|
+
outHtml: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The shape of a Cloudflare Workers export object.
|
|
41
|
+
* Return this as the default export of your Worker entry file.
|
|
42
|
+
*/
|
|
43
|
+
interface CloudflareHandler {
|
|
44
|
+
fetch(request: Request, env: unknown, ctx: unknown): Promise<Response>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates a Cloudflare Workers handler from a hadars config and a pre-bundled
|
|
48
|
+
* SSR module. Use this as the default export of your Worker entry.
|
|
49
|
+
*
|
|
50
|
+
* Unlike the Lambda adapter, Cloudflare Workers receive a standard Web
|
|
51
|
+
* `Request` and return a standard `Response` — no event format conversion is
|
|
52
|
+
* required. Static assets must be routed to R2/CDN via wrangler rules; this
|
|
53
|
+
* Worker handles only HTML rendering and API routes.
|
|
54
|
+
*
|
|
55
|
+
* @example — generated entry shim (created by `hadars export cloudflare`)
|
|
56
|
+
* import * as ssrModule from './.hadars/index.ssr.js';
|
|
57
|
+
* import outHtml from './.hadars/static/out.html';
|
|
58
|
+
* import { createCloudflareHandler } from 'hadars/cloudflare';
|
|
59
|
+
* import config from './hadars.config';
|
|
60
|
+
* export default createCloudflareHandler(config, { ssrModule, outHtml });
|
|
61
|
+
*/
|
|
62
|
+
declare function createCloudflareHandler(options: HadarsOptions, bundled: CloudflareBundled): CloudflareHandler;
|
|
63
|
+
|
|
64
|
+
export { type CloudflareBundled, type CloudflareHandler, createCloudflareHandler };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-DEBSYAQl.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cloudflare Workers adapter for hadars.
|
|
5
|
+
*
|
|
6
|
+
* After running `hadars build`, bundle your app with:
|
|
7
|
+
*
|
|
8
|
+
* hadars export cloudflare
|
|
9
|
+
*
|
|
10
|
+
* This produces a self-contained `cloudflare.mjs` that you deploy with:
|
|
11
|
+
*
|
|
12
|
+
* wrangler deploy
|
|
13
|
+
*
|
|
14
|
+
* Static assets (JS, CSS, fonts) under `.hadars/static/` must be served from
|
|
15
|
+
* R2 or another CDN — the Worker only handles HTML rendering. Route requests
|
|
16
|
+
* for static file extensions to R2 and everything else to the Worker.
|
|
17
|
+
*
|
|
18
|
+
* @example wrangler.toml
|
|
19
|
+
* name = "my-app"
|
|
20
|
+
* main = "cloudflare.mjs"
|
|
21
|
+
* compatibility_date = "2024-09-23"
|
|
22
|
+
* compatibility_flags = ["nodejs_compat"]
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Pre-loaded SSR module and HTML template for single-file Cloudflare bundles
|
|
27
|
+
* produced by `hadars export cloudflare`. All I/O is eliminated at runtime —
|
|
28
|
+
* the Worker is fully self-contained.
|
|
29
|
+
*/
|
|
30
|
+
interface CloudflareBundled {
|
|
31
|
+
/** The compiled SSR module — import it statically in your entry shim. */
|
|
32
|
+
ssrModule: HadarsEntryModule<any>;
|
|
33
|
+
/**
|
|
34
|
+
* The contents of `.hadars/static/out.html` — esbuild inlines this as a
|
|
35
|
+
* string when `hadars export cloudflare` runs.
|
|
36
|
+
*/
|
|
37
|
+
outHtml: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The shape of a Cloudflare Workers export object.
|
|
41
|
+
* Return this as the default export of your Worker entry file.
|
|
42
|
+
*/
|
|
43
|
+
interface CloudflareHandler {
|
|
44
|
+
fetch(request: Request, env: unknown, ctx: unknown): Promise<Response>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates a Cloudflare Workers handler from a hadars config and a pre-bundled
|
|
48
|
+
* SSR module. Use this as the default export of your Worker entry.
|
|
49
|
+
*
|
|
50
|
+
* Unlike the Lambda adapter, Cloudflare Workers receive a standard Web
|
|
51
|
+
* `Request` and return a standard `Response` — no event format conversion is
|
|
52
|
+
* required. Static assets must be routed to R2/CDN via wrangler rules; this
|
|
53
|
+
* Worker handles only HTML rendering and API routes.
|
|
54
|
+
*
|
|
55
|
+
* @example — generated entry shim (created by `hadars export cloudflare`)
|
|
56
|
+
* import * as ssrModule from './.hadars/index.ssr.js';
|
|
57
|
+
* import outHtml from './.hadars/static/out.html';
|
|
58
|
+
* import { createCloudflareHandler } from 'hadars/cloudflare';
|
|
59
|
+
* import config from './hadars.config';
|
|
60
|
+
* export default createCloudflareHandler(config, { ssrModule, outHtml });
|
|
61
|
+
*/
|
|
62
|
+
declare function createCloudflareHandler(options: HadarsOptions, bundled: CloudflareBundled): CloudflareHandler;
|
|
63
|
+
|
|
64
|
+
export { type CloudflareBundled, type CloudflareHandler, createCloudflareHandler };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
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";
|
|
12
|
+
|
|
13
|
+
// src/cloudflare.ts
|
|
14
|
+
import "react";
|
|
15
|
+
function createCloudflareHandler(options, bundled) {
|
|
16
|
+
const fetchHandler = options.fetch;
|
|
17
|
+
const handleProxy = createProxyHandler(options);
|
|
18
|
+
const getPrecontentHtml = makePrecontentHtmlGetter(Promise.resolve(bundled.outHtml));
|
|
19
|
+
const { ssrModule } = bundled;
|
|
20
|
+
const runHandler = async (req) => {
|
|
21
|
+
const request = parseRequest(req);
|
|
22
|
+
if (fetchHandler) {
|
|
23
|
+
const res = await fetchHandler(request);
|
|
24
|
+
if (res) return res;
|
|
25
|
+
}
|
|
26
|
+
const proxied = await handleProxy(request);
|
|
27
|
+
if (proxied) return proxied;
|
|
28
|
+
try {
|
|
29
|
+
const { default: Component, getInitProps, getFinalProps } = ssrModule;
|
|
30
|
+
const { head, status, getAppBody, finalize } = await getReactResponse(request, {
|
|
31
|
+
document: {
|
|
32
|
+
body: Component,
|
|
33
|
+
lang: "en",
|
|
34
|
+
getInitProps,
|
|
35
|
+
getFinalProps
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (request.headers.get("Accept") === "application/json") {
|
|
39
|
+
const { clientProps: clientProps2 } = await finalize();
|
|
40
|
+
const serverData = clientProps2.__serverData ?? {};
|
|
41
|
+
return new Response(JSON.stringify({ serverData }), {
|
|
42
|
+
status,
|
|
43
|
+
headers: { "Content-Type": "application/json; charset=utf-8" }
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const bodyHtml = await getAppBody();
|
|
47
|
+
const { clientProps } = await finalize();
|
|
48
|
+
const headHtml = buildHeadHtml(head);
|
|
49
|
+
const html = await buildSsrHtml(bodyHtml, clientProps, headHtml, getPrecontentHtml);
|
|
50
|
+
return new Response(html, {
|
|
51
|
+
status,
|
|
52
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
53
|
+
});
|
|
54
|
+
} catch (err) {
|
|
55
|
+
console.error("[hadars] SSR render error:", err);
|
|
56
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const finalFetch = options.cache ? createRenderCache(options.cache, (req) => runHandler(req)) : (req) => runHandler(req);
|
|
60
|
+
return {
|
|
61
|
+
fetch: async (request, _env, ctx) => {
|
|
62
|
+
return await finalFetch(request, ctx) ?? new Response("Not Found", { status: 404 });
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
createCloudflareHandler
|
|
68
|
+
};
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { MetaHTMLAttributes, LinkHTMLAttributes, StyleHTMLAttributes, ScriptHTMLAttributes } from 'react';
|
|
2
|
-
|
|
3
1
|
type HadarsGetInitialProps<T extends {}> = (req: HadarsRequest) => Promise<T> | T;
|
|
4
2
|
type HadarsGetClientProps<T extends {}> = (props: T) => Promise<T> | T;
|
|
5
3
|
type HadarsGetFinalProps<T extends {}> = (props: HadarsProps<T>) => Promise<T> | T;
|
|
@@ -10,43 +8,10 @@ type HadarsEntryModule<T extends {}> = {
|
|
|
10
8
|
getFinalProps?: HadarsGetFinalProps<T>;
|
|
11
9
|
getClientProps?: HadarsGetClientProps<T>;
|
|
12
10
|
};
|
|
13
|
-
interface AppHead {
|
|
14
|
-
title: string;
|
|
15
|
-
status: number;
|
|
16
|
-
meta: Record<string, MetaProps>;
|
|
17
|
-
link: Record<string, LinkProps>;
|
|
18
|
-
style: Record<string, StyleProps>;
|
|
19
|
-
script: Record<string, ScriptProps>;
|
|
20
|
-
}
|
|
21
|
-
type UnsuspendEntry = {
|
|
22
|
-
status: 'pending';
|
|
23
|
-
promise: Promise<unknown>;
|
|
24
|
-
} | {
|
|
25
|
-
status: 'fulfilled';
|
|
26
|
-
value: unknown;
|
|
27
|
-
} | {
|
|
28
|
-
status: 'rejected';
|
|
29
|
-
reason: unknown;
|
|
30
|
-
};
|
|
31
|
-
/** @internal Populated by the framework's render loop — use useServerData() instead. */
|
|
32
|
-
interface AppUnsuspend {
|
|
33
|
-
cache: Map<string, UnsuspendEntry>;
|
|
34
|
-
}
|
|
35
|
-
interface AppContext {
|
|
36
|
-
path?: string;
|
|
37
|
-
head: AppHead;
|
|
38
|
-
/** @internal Framework use only — use the useServerData() hook instead. */
|
|
39
|
-
_unsuspend?: AppUnsuspend;
|
|
40
|
-
}
|
|
41
11
|
type HadarsEntryBase = {
|
|
42
12
|
location: string;
|
|
43
|
-
context: AppContext;
|
|
44
13
|
};
|
|
45
14
|
type HadarsProps<T extends {}> = T & HadarsEntryBase;
|
|
46
|
-
type MetaProps = MetaHTMLAttributes<HTMLMetaElement>;
|
|
47
|
-
type LinkProps = LinkHTMLAttributes<HTMLLinkElement>;
|
|
48
|
-
type StyleProps = StyleHTMLAttributes<HTMLStyleElement>;
|
|
49
|
-
type ScriptProps = ScriptHTMLAttributes<HTMLScriptElement>;
|
|
50
15
|
interface HadarsOptions {
|
|
51
16
|
port?: number;
|
|
52
17
|
entry: string;
|
|
@@ -196,4 +161,4 @@ interface HadarsRequest extends Request {
|
|
|
196
161
|
cookies: Record<string, string>;
|
|
197
162
|
}
|
|
198
163
|
|
|
199
|
-
export type {
|
|
164
|
+
export type { HadarsEntryModule as H, HadarsOptions as a, HadarsApp as b, HadarsGetClientProps as c, HadarsGetFinalProps as d, HadarsGetInitialProps as e, HadarsProps as f, HadarsRequest as g };
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { MetaHTMLAttributes, LinkHTMLAttributes, StyleHTMLAttributes, ScriptHTMLAttributes } from 'react';
|
|
2
|
-
|
|
3
1
|
type HadarsGetInitialProps<T extends {}> = (req: HadarsRequest) => Promise<T> | T;
|
|
4
2
|
type HadarsGetClientProps<T extends {}> = (props: T) => Promise<T> | T;
|
|
5
3
|
type HadarsGetFinalProps<T extends {}> = (props: HadarsProps<T>) => Promise<T> | T;
|
|
@@ -10,43 +8,10 @@ type HadarsEntryModule<T extends {}> = {
|
|
|
10
8
|
getFinalProps?: HadarsGetFinalProps<T>;
|
|
11
9
|
getClientProps?: HadarsGetClientProps<T>;
|
|
12
10
|
};
|
|
13
|
-
interface AppHead {
|
|
14
|
-
title: string;
|
|
15
|
-
status: number;
|
|
16
|
-
meta: Record<string, MetaProps>;
|
|
17
|
-
link: Record<string, LinkProps>;
|
|
18
|
-
style: Record<string, StyleProps>;
|
|
19
|
-
script: Record<string, ScriptProps>;
|
|
20
|
-
}
|
|
21
|
-
type UnsuspendEntry = {
|
|
22
|
-
status: 'pending';
|
|
23
|
-
promise: Promise<unknown>;
|
|
24
|
-
} | {
|
|
25
|
-
status: 'fulfilled';
|
|
26
|
-
value: unknown;
|
|
27
|
-
} | {
|
|
28
|
-
status: 'rejected';
|
|
29
|
-
reason: unknown;
|
|
30
|
-
};
|
|
31
|
-
/** @internal Populated by the framework's render loop — use useServerData() instead. */
|
|
32
|
-
interface AppUnsuspend {
|
|
33
|
-
cache: Map<string, UnsuspendEntry>;
|
|
34
|
-
}
|
|
35
|
-
interface AppContext {
|
|
36
|
-
path?: string;
|
|
37
|
-
head: AppHead;
|
|
38
|
-
/** @internal Framework use only — use the useServerData() hook instead. */
|
|
39
|
-
_unsuspend?: AppUnsuspend;
|
|
40
|
-
}
|
|
41
11
|
type HadarsEntryBase = {
|
|
42
12
|
location: string;
|
|
43
|
-
context: AppContext;
|
|
44
13
|
};
|
|
45
14
|
type HadarsProps<T extends {}> = T & HadarsEntryBase;
|
|
46
|
-
type MetaProps = MetaHTMLAttributes<HTMLMetaElement>;
|
|
47
|
-
type LinkProps = LinkHTMLAttributes<HTMLLinkElement>;
|
|
48
|
-
type StyleProps = StyleHTMLAttributes<HTMLStyleElement>;
|
|
49
|
-
type ScriptProps = ScriptHTMLAttributes<HTMLScriptElement>;
|
|
50
15
|
interface HadarsOptions {
|
|
51
16
|
port?: number;
|
|
52
17
|
entry: string;
|
|
@@ -196,4 +161,4 @@ interface HadarsRequest extends Request {
|
|
|
196
161
|
cookies: Record<string, string>;
|
|
197
162
|
}
|
|
198
163
|
|
|
199
|
-
export type {
|
|
164
|
+
export type { HadarsEntryModule as H, HadarsOptions as a, HadarsApp as b, HadarsGetClientProps as c, HadarsGetFinalProps as d, HadarsGetInitialProps as e, HadarsProps as f, HadarsRequest as g };
|
package/dist/index.cjs
CHANGED
|
@@ -30,7 +30,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.tsx
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
HadarsContext: () => HadarsContext,
|
|
34
33
|
HadarsHead: () => Head,
|
|
35
34
|
initServerDataCache: () => initServerDataCache,
|
|
36
35
|
loadModule: () => loadModule,
|
|
@@ -40,7 +39,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
40
39
|
|
|
41
40
|
// src/utils/Head.tsx
|
|
42
41
|
var import_react = __toESM(require("react"), 1);
|
|
43
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
44
42
|
function deriveKey(tag, props) {
|
|
45
43
|
switch (tag) {
|
|
46
44
|
case "meta": {
|
|
@@ -70,134 +68,116 @@ function deriveKey(tag, props) {
|
|
|
70
68
|
return `${tag}:${JSON.stringify(props)}`;
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
|
-
var
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
head.meta[deriveKey("meta", props)] = props;
|
|
99
|
-
}, [head]);
|
|
100
|
-
const addLink = import_react.default.useCallback((props) => {
|
|
101
|
-
head.link[deriveKey("link", props)] = props;
|
|
102
|
-
}, [head]);
|
|
103
|
-
const addStyle = import_react.default.useCallback((props) => {
|
|
104
|
-
head.style[deriveKey("style", props)] = props;
|
|
105
|
-
}, [head]);
|
|
106
|
-
const addScript = import_react.default.useCallback((props) => {
|
|
107
|
-
head.script[deriveKey("script", props)] = props;
|
|
108
|
-
}, [head]);
|
|
109
|
-
const setStatus = import_react.default.useCallback((status) => {
|
|
110
|
-
head.status = status;
|
|
111
|
-
}, [head]);
|
|
112
|
-
const contextValue = import_react.default.useMemo(() => ({
|
|
113
|
-
setTitle,
|
|
114
|
-
addMeta,
|
|
115
|
-
addLink,
|
|
116
|
-
addStyle,
|
|
117
|
-
addScript,
|
|
118
|
-
setStatus
|
|
119
|
-
}), [setTitle, addMeta, addLink, addStyle, addScript, setStatus]);
|
|
120
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppContext.Provider, { value: contextValue, children });
|
|
121
|
-
});
|
|
122
|
-
var AppProviderCSR = import_react.default.memo(({ children }) => {
|
|
123
|
-
const setTitle = import_react.default.useCallback((title) => {
|
|
124
|
-
document.title = title;
|
|
125
|
-
}, []);
|
|
126
|
-
const addMeta = import_react.default.useCallback((props) => {
|
|
127
|
-
const p = props;
|
|
128
|
-
let meta = null;
|
|
129
|
-
if (p.name) meta = document.querySelector(`meta[name="${CSS.escape(p.name)}"]`);
|
|
130
|
-
else if (p.property) meta = document.querySelector(`meta[property="${CSS.escape(p.property)}"]`);
|
|
131
|
-
else if (p.httpEquiv ?? p["http-equiv"]) meta = document.querySelector(`meta[http-equiv="${CSS.escape(p.httpEquiv ?? p["http-equiv"])}"]`);
|
|
132
|
-
else if ("charSet" in p || "charset" in p) meta = document.querySelector("meta[charset]");
|
|
133
|
-
if (!meta) {
|
|
134
|
-
meta = document.createElement("meta");
|
|
135
|
-
document.head.appendChild(meta);
|
|
136
|
-
}
|
|
137
|
-
for (const [k, v] of Object.entries(p)) {
|
|
138
|
-
if (v != null && v !== false) meta.setAttribute(k === "charSet" ? "charset" : k === "httpEquiv" ? "http-equiv" : k, String(v));
|
|
139
|
-
}
|
|
140
|
-
}, []);
|
|
141
|
-
const addLink = import_react.default.useCallback((props) => {
|
|
142
|
-
const p = props;
|
|
143
|
-
let link = null;
|
|
144
|
-
const asSel = p.as ? `[as="${CSS.escape(p.as)}"]` : "";
|
|
145
|
-
if (p.rel && p.href) link = document.querySelector(`link[rel="${CSS.escape(p.rel)}"][href="${CSS.escape(p.href)}"]${asSel}`);
|
|
146
|
-
else if (p.rel) link = document.querySelector(`link[rel="${CSS.escape(p.rel)}"]${asSel}`);
|
|
147
|
-
if (!link) {
|
|
148
|
-
link = document.createElement("link");
|
|
149
|
-
document.head.appendChild(link);
|
|
150
|
-
}
|
|
151
|
-
const LINK_ATTR = { crossOrigin: "crossorigin", referrerPolicy: "referrerpolicy", fetchPriority: "fetchpriority", hrefLang: "hreflang" };
|
|
152
|
-
for (const [k, v] of Object.entries(p)) {
|
|
153
|
-
if (v != null && v !== false) link.setAttribute(LINK_ATTR[k] ?? k, String(v));
|
|
154
|
-
}
|
|
155
|
-
}, []);
|
|
156
|
-
const addStyle = import_react.default.useCallback((props) => {
|
|
157
|
-
const p = props;
|
|
158
|
-
let style = null;
|
|
159
|
-
if (p["data-id"]) style = document.querySelector(`style[data-id="${CSS.escape(p["data-id"])}"]`);
|
|
160
|
-
if (!style) {
|
|
161
|
-
style = document.createElement("style");
|
|
162
|
-
document.head.appendChild(style);
|
|
71
|
+
var LINK_ATTR = {
|
|
72
|
+
crossOrigin: "crossorigin",
|
|
73
|
+
referrerPolicy: "referrerpolicy",
|
|
74
|
+
fetchPriority: "fetchpriority",
|
|
75
|
+
hrefLang: "hreflang"
|
|
76
|
+
};
|
|
77
|
+
function makeServerCtx(head) {
|
|
78
|
+
return {
|
|
79
|
+
setTitle: (t) => {
|
|
80
|
+
head.title = t;
|
|
81
|
+
},
|
|
82
|
+
addMeta: (p) => {
|
|
83
|
+
head.meta[deriveKey("meta", p)] = p;
|
|
84
|
+
},
|
|
85
|
+
addLink: (p) => {
|
|
86
|
+
head.link[deriveKey("link", p)] = p;
|
|
87
|
+
},
|
|
88
|
+
addStyle: (p) => {
|
|
89
|
+
head.style[deriveKey("style", p)] = p;
|
|
90
|
+
},
|
|
91
|
+
addScript: (p) => {
|
|
92
|
+
head.script[deriveKey("script", p)] = p;
|
|
93
|
+
},
|
|
94
|
+
setStatus: (s) => {
|
|
95
|
+
head.status = s;
|
|
163
96
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
var _cliCtx = null;
|
|
100
|
+
function makeClientCtx() {
|
|
101
|
+
if (_cliCtx) return _cliCtx;
|
|
102
|
+
_cliCtx = {
|
|
103
|
+
setTitle: (title) => {
|
|
104
|
+
document.title = title;
|
|
105
|
+
},
|
|
106
|
+
setStatus: () => {
|
|
107
|
+
},
|
|
108
|
+
addMeta: (props) => {
|
|
109
|
+
const p = props;
|
|
110
|
+
let meta = null;
|
|
111
|
+
if (p.name) meta = document.querySelector(`meta[name="${CSS.escape(p.name)}"]`);
|
|
112
|
+
else if (p.property) meta = document.querySelector(`meta[property="${CSS.escape(p.property)}"]`);
|
|
113
|
+
else if (p.httpEquiv ?? p["http-equiv"]) meta = document.querySelector(`meta[http-equiv="${CSS.escape(p.httpEquiv ?? p["http-equiv"])}"]`);
|
|
114
|
+
else if ("charSet" in p || "charset" in p) meta = document.querySelector("meta[charset]");
|
|
115
|
+
if (!meta) {
|
|
116
|
+
meta = document.createElement("meta");
|
|
117
|
+
document.head.appendChild(meta);
|
|
168
118
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
119
|
+
for (const [k, v] of Object.entries(p)) {
|
|
120
|
+
if (v != null && v !== false) meta.setAttribute(k === "charSet" ? "charset" : k === "httpEquiv" ? "http-equiv" : k, String(v));
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
addLink: (props) => {
|
|
124
|
+
const p = props;
|
|
125
|
+
let link = null;
|
|
126
|
+
const asSel = p.as ? `[as="${CSS.escape(p.as)}"]` : "";
|
|
127
|
+
if (p.rel && p.href) link = document.querySelector(`link[rel="${CSS.escape(p.rel)}"][href="${CSS.escape(p.href)}"]${asSel}`);
|
|
128
|
+
else if (p.rel) link = document.querySelector(`link[rel="${CSS.escape(p.rel)}"]${asSel}`);
|
|
129
|
+
if (!link) {
|
|
130
|
+
link = document.createElement("link");
|
|
131
|
+
document.head.appendChild(link);
|
|
132
|
+
}
|
|
133
|
+
for (const [k, v] of Object.entries(p)) {
|
|
134
|
+
if (v != null && v !== false) link.setAttribute(LINK_ATTR[k] ?? k, String(v));
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
addStyle: (props) => {
|
|
138
|
+
const p = props;
|
|
139
|
+
let style = null;
|
|
140
|
+
if (p["data-id"]) style = document.querySelector(`style[data-id="${CSS.escape(p["data-id"])}"]`);
|
|
141
|
+
if (!style) {
|
|
142
|
+
style = document.createElement("style");
|
|
143
|
+
document.head.appendChild(style);
|
|
144
|
+
}
|
|
145
|
+
for (const [k, v] of Object.entries(p)) {
|
|
146
|
+
if (k === "dangerouslySetInnerHTML") {
|
|
147
|
+
style.innerHTML = v.__html ?? "";
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (v != null && v !== false) style.setAttribute(k, String(v));
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
addScript: (props) => {
|
|
154
|
+
const p = props;
|
|
155
|
+
let script = null;
|
|
156
|
+
if (p.src) script = document.querySelector(`script[src="${CSS.escape(p.src)}"]`);
|
|
157
|
+
else if (p["data-id"]) script = document.querySelector(`script[data-id="${CSS.escape(p["data-id"])}"]`);
|
|
158
|
+
if (!script) {
|
|
159
|
+
script = document.createElement("script");
|
|
160
|
+
document.body.appendChild(script);
|
|
161
|
+
}
|
|
162
|
+
for (const [k, v] of Object.entries(p)) {
|
|
163
|
+
if (k === "dangerouslySetInnerHTML") {
|
|
164
|
+
script.innerHTML = v.__html ?? "";
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (v != null && v !== false) script.setAttribute(k, String(v));
|
|
185
168
|
}
|
|
186
|
-
if (v != null && v !== false) script.setAttribute(k, String(v));
|
|
187
|
-
}
|
|
188
|
-
}, []);
|
|
189
|
-
const contextValue = import_react.default.useMemo(() => ({
|
|
190
|
-
setTitle,
|
|
191
|
-
addMeta,
|
|
192
|
-
addLink,
|
|
193
|
-
addStyle,
|
|
194
|
-
addScript,
|
|
195
|
-
setStatus: () => {
|
|
196
169
|
}
|
|
197
|
-
}
|
|
198
|
-
return
|
|
199
|
-
}
|
|
200
|
-
|
|
170
|
+
};
|
|
171
|
+
return _cliCtx;
|
|
172
|
+
}
|
|
173
|
+
function getCtx() {
|
|
174
|
+
if (typeof window === "undefined") {
|
|
175
|
+
const head = globalThis.__hadarsContext?.head;
|
|
176
|
+
if (!head) return null;
|
|
177
|
+
return makeServerCtx(head);
|
|
178
|
+
}
|
|
179
|
+
return makeClientCtx();
|
|
180
|
+
}
|
|
201
181
|
var clientServerDataCache = /* @__PURE__ */ new Map();
|
|
202
182
|
var pendingDataFetch = /* @__PURE__ */ new Map();
|
|
203
183
|
var fetchedPaths = /* @__PURE__ */ new Set();
|
|
@@ -267,26 +247,12 @@ function useServerData(key, fn) {
|
|
|
267
247
|
const unsuspend = globalThis.__hadarsUnsuspend;
|
|
268
248
|
if (!unsuspend) return void 0;
|
|
269
249
|
const _u = unsuspend;
|
|
270
|
-
if (!_u.
|
|
271
|
-
if (!_u.seenLastPass) _u.seenLastPass = /* @__PURE__ */ new Set();
|
|
272
|
-
if (_u.newPassStarting) {
|
|
273
|
-
_u.seenLastPass = new Set(_u.seenThisPass);
|
|
274
|
-
_u.seenThisPass.clear();
|
|
275
|
-
_u.newPassStarting = false;
|
|
276
|
-
}
|
|
277
|
-
_u.seenThisPass.add(cacheKey);
|
|
250
|
+
if (!_u.pendingCreated) _u.pendingCreated = 0;
|
|
278
251
|
const existing = unsuspend.cache.get(cacheKey);
|
|
252
|
+
if (existing?.status === "fulfilled" && _u.lastPendingKey === cacheKey) {
|
|
253
|
+
_u.lastPendingKeyAccessed = true;
|
|
254
|
+
}
|
|
279
255
|
if (!existing) {
|
|
280
|
-
if (_u.seenLastPass.size > 0) {
|
|
281
|
-
const hasVanishedKey = [..._u.seenLastPass].some(
|
|
282
|
-
(k) => !_u.seenThisPass.has(k)
|
|
283
|
-
);
|
|
284
|
-
if (hasVanishedKey) {
|
|
285
|
-
throw new Error(
|
|
286
|
-
`[hadars] useServerData: key ${JSON.stringify(cacheKey)} appeared in this pass but a key that was present in the previous pass is now missing. This means the key is not stable across render passes (e.g. it contains Date.now(), Math.random(), or a value that changes on every render). Keys must be deterministic.`
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
256
|
const result = fn();
|
|
291
257
|
const isThenable = result !== null && typeof result === "object" && typeof result.then === "function";
|
|
292
258
|
if (!isThenable) {
|
|
@@ -294,6 +260,22 @@ function useServerData(key, fn) {
|
|
|
294
260
|
unsuspend.cache.set(cacheKey, { status: "fulfilled", value });
|
|
295
261
|
return value;
|
|
296
262
|
}
|
|
263
|
+
if (_u.lastPendingKey != null && !_u.lastPendingKeyAccessed) {
|
|
264
|
+
const prev = unsuspend.cache.get(_u.lastPendingKey);
|
|
265
|
+
if (prev?.status === "fulfilled") {
|
|
266
|
+
throw new Error(
|
|
267
|
+
`[hadars] useServerData: key ${JSON.stringify(cacheKey)} is not stable between render passes. The previous pass resolved ${JSON.stringify(_u.lastPendingKey)} but it was not requested in this pass \u2014 the key is changing between renders. Avoid dynamic values in keys (e.g. Date.now() or Math.random()); use stable, deterministic identifiers instead.`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
_u.pendingCreated++;
|
|
272
|
+
if (_u.pendingCreated > 100) {
|
|
273
|
+
throw new Error(
|
|
274
|
+
`[hadars] useServerData: more than 100 async keys created in a single render. This usually means a key is not stable between renders (e.g. it contains Date.now() or Math.random()). Currently offending key: ${JSON.stringify(cacheKey)}.`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
_u.lastPendingKey = cacheKey;
|
|
278
|
+
_u.lastPendingKeyAccessed = false;
|
|
297
279
|
const promise = result.then(
|
|
298
280
|
(value) => {
|
|
299
281
|
unsuspend.cache.set(cacheKey, { status: "fulfilled", value });
|
|
@@ -303,25 +285,18 @@ function useServerData(key, fn) {
|
|
|
303
285
|
}
|
|
304
286
|
);
|
|
305
287
|
unsuspend.cache.set(cacheKey, { status: "pending", promise });
|
|
306
|
-
_u.newPassStarting = true;
|
|
307
288
|
throw promise;
|
|
308
289
|
}
|
|
309
290
|
if (existing.status === "pending") {
|
|
310
|
-
_u.newPassStarting = true;
|
|
311
291
|
throw existing.promise;
|
|
312
292
|
}
|
|
313
293
|
if (existing.status === "rejected") throw existing.reason;
|
|
314
294
|
return existing.value;
|
|
315
295
|
}
|
|
316
296
|
var Head = import_react.default.memo(({ children, status }) => {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
addMeta,
|
|
321
|
-
addLink,
|
|
322
|
-
addStyle,
|
|
323
|
-
addScript
|
|
324
|
-
} = useApp();
|
|
297
|
+
const ctx = getCtx();
|
|
298
|
+
if (!ctx) return null;
|
|
299
|
+
const { setStatus, setTitle, addMeta, addLink, addStyle, addScript } = ctx;
|
|
325
300
|
if (status) {
|
|
326
301
|
setStatus(status);
|
|
327
302
|
}
|
|
@@ -366,7 +341,6 @@ var Head = import_react.default.memo(({ children, status }) => {
|
|
|
366
341
|
});
|
|
367
342
|
|
|
368
343
|
// src/index.tsx
|
|
369
|
-
var HadarsContext = typeof window === "undefined" ? AppProviderSSR : AppProviderCSR;
|
|
370
344
|
function loadModule(path) {
|
|
371
345
|
return import(
|
|
372
346
|
/* webpackIgnore: true */
|
|
@@ -375,7 +349,6 @@ function loadModule(path) {
|
|
|
375
349
|
}
|
|
376
350
|
// Annotate the CommonJS export names for ESM import in node:
|
|
377
351
|
0 && (module.exports = {
|
|
378
|
-
HadarsContext,
|
|
379
352
|
HadarsHead,
|
|
380
353
|
initServerDataCache,
|
|
381
354
|
loadModule,
|