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/index.d.cts CHANGED
@@ -1,7 +1,5 @@
1
- import * as React$1 from 'react';
2
- import React__default from 'react';
3
- import { A as AppContext } from './hadars-Bh-V5YXg.cjs';
4
- export { b as HadarsApp, H as HadarsEntryModule, c as HadarsGetClientProps, d as HadarsGetFinalProps, e as HadarsGetInitialProps, a as HadarsOptions, f as HadarsProps, g as HadarsRequest } from './hadars-Bh-V5YXg.cjs';
1
+ export { b as HadarsApp, H as HadarsEntryModule, c as HadarsGetClientProps, d as HadarsGetFinalProps, e as HadarsGetInitialProps, a as HadarsOptions, f as HadarsProps, g as HadarsRequest } from './hadars-DEBSYAQl.cjs';
2
+ import React from 'react';
5
3
 
6
4
  /** Call this before hydrating to seed the client cache from the server's data.
7
5
  * Invoked automatically by the hadars client bootstrap.
@@ -34,15 +32,11 @@ declare function initServerDataCache(data: Record<string, unknown>): void;
34
32
  * if (!user) return null; // undefined while pending on the first SSR pass
35
33
  */
36
34
  declare function useServerData<T>(key: string | string[], fn: () => Promise<T> | T): T | undefined;
37
- declare const Head: React__default.FC<{
38
- children?: React__default.ReactNode;
35
+ declare const Head: React.FC<{
36
+ children?: React.ReactNode;
39
37
  status?: number;
40
38
  }>;
41
39
 
42
- declare const HadarsContext: React$1.FC<{
43
- children: React.ReactNode;
44
- context: AppContext;
45
- }>;
46
40
  /**
47
41
  * Dynamically loads a module with target-aware behaviour:
48
42
  *
@@ -63,4 +57,4 @@ declare const HadarsContext: React$1.FC<{
63
57
  */
64
58
  declare function loadModule<T = any>(path: string): Promise<T>;
65
59
 
66
- export { HadarsContext, Head as HadarsHead, initServerDataCache, loadModule, useServerData };
60
+ export { Head as HadarsHead, initServerDataCache, loadModule, useServerData };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,5 @@
1
- import * as React$1 from 'react';
2
- import React__default from 'react';
3
- import { A as AppContext } from './hadars-Bh-V5YXg.js';
4
- export { b as HadarsApp, H as HadarsEntryModule, c as HadarsGetClientProps, d as HadarsGetFinalProps, e as HadarsGetInitialProps, a as HadarsOptions, f as HadarsProps, g as HadarsRequest } from './hadars-Bh-V5YXg.js';
1
+ export { b as HadarsApp, H as HadarsEntryModule, c as HadarsGetClientProps, d as HadarsGetFinalProps, e as HadarsGetInitialProps, a as HadarsOptions, f as HadarsProps, g as HadarsRequest } from './hadars-DEBSYAQl.js';
2
+ import React from 'react';
5
3
 
6
4
  /** Call this before hydrating to seed the client cache from the server's data.
7
5
  * Invoked automatically by the hadars client bootstrap.
@@ -34,15 +32,11 @@ declare function initServerDataCache(data: Record<string, unknown>): void;
34
32
  * if (!user) return null; // undefined while pending on the first SSR pass
35
33
  */
36
34
  declare function useServerData<T>(key: string | string[], fn: () => Promise<T> | T): T | undefined;
37
- declare const Head: React__default.FC<{
38
- children?: React__default.ReactNode;
35
+ declare const Head: React.FC<{
36
+ children?: React.ReactNode;
39
37
  status?: number;
40
38
  }>;
41
39
 
42
- declare const HadarsContext: React$1.FC<{
43
- children: React.ReactNode;
44
- context: AppContext;
45
- }>;
46
40
  /**
47
41
  * Dynamically loads a module with target-aware behaviour:
48
42
  *
@@ -63,4 +57,4 @@ declare const HadarsContext: React$1.FC<{
63
57
  */
64
58
  declare function loadModule<T = any>(path: string): Promise<T>;
65
59
 
66
- export { HadarsContext, Head as HadarsHead, initServerDataCache, loadModule, useServerData };
60
+ export { Head as HadarsHead, initServerDataCache, loadModule, useServerData };
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  // src/utils/Head.tsx
2
2
  import React from "react";
3
- import { jsx } from "react/jsx-runtime";
4
3
  function deriveKey(tag, props) {
5
4
  switch (tag) {
6
5
  case "meta": {
@@ -30,134 +29,116 @@ function deriveKey(tag, props) {
30
29
  return `${tag}:${JSON.stringify(props)}`;
31
30
  }
32
31
  }
33
- var AppContext = React.createContext({
34
- setTitle: () => {
35
- console.warn("AppContext: setTitle called outside of provider");
36
- },
37
- addMeta: () => {
38
- console.warn("AppContext: addMeta called outside of provider");
39
- },
40
- addLink: () => {
41
- console.warn("AppContext: addLink called outside of provider");
42
- },
43
- addStyle: () => {
44
- console.warn("AppContext: addStyle called outside of provider");
45
- },
46
- addScript: () => {
47
- console.warn("AppContext: addScript called outside of provider");
48
- },
49
- setStatus: () => {
50
- }
51
- });
52
- var AppProviderSSR = React.memo(({ children, context }) => {
53
- const { head } = context;
54
- const setTitle = React.useCallback((title) => {
55
- head.title = title;
56
- }, [head]);
57
- const addMeta = React.useCallback((props) => {
58
- head.meta[deriveKey("meta", props)] = props;
59
- }, [head]);
60
- const addLink = React.useCallback((props) => {
61
- head.link[deriveKey("link", props)] = props;
62
- }, [head]);
63
- const addStyle = React.useCallback((props) => {
64
- head.style[deriveKey("style", props)] = props;
65
- }, [head]);
66
- const addScript = React.useCallback((props) => {
67
- head.script[deriveKey("script", props)] = props;
68
- }, [head]);
69
- const setStatus = React.useCallback((status) => {
70
- head.status = status;
71
- }, [head]);
72
- const contextValue = React.useMemo(() => ({
73
- setTitle,
74
- addMeta,
75
- addLink,
76
- addStyle,
77
- addScript,
78
- setStatus
79
- }), [setTitle, addMeta, addLink, addStyle, addScript, setStatus]);
80
- return /* @__PURE__ */ jsx(AppContext.Provider, { value: contextValue, children });
81
- });
82
- var AppProviderCSR = React.memo(({ children }) => {
83
- const setTitle = React.useCallback((title) => {
84
- document.title = title;
85
- }, []);
86
- const addMeta = React.useCallback((props) => {
87
- const p = props;
88
- let meta = null;
89
- if (p.name) meta = document.querySelector(`meta[name="${CSS.escape(p.name)}"]`);
90
- else if (p.property) meta = document.querySelector(`meta[property="${CSS.escape(p.property)}"]`);
91
- else if (p.httpEquiv ?? p["http-equiv"]) meta = document.querySelector(`meta[http-equiv="${CSS.escape(p.httpEquiv ?? p["http-equiv"])}"]`);
92
- else if ("charSet" in p || "charset" in p) meta = document.querySelector("meta[charset]");
93
- if (!meta) {
94
- meta = document.createElement("meta");
95
- document.head.appendChild(meta);
96
- }
97
- for (const [k, v] of Object.entries(p)) {
98
- if (v != null && v !== false) meta.setAttribute(k === "charSet" ? "charset" : k === "httpEquiv" ? "http-equiv" : k, String(v));
99
- }
100
- }, []);
101
- const addLink = React.useCallback((props) => {
102
- const p = props;
103
- let link = null;
104
- const asSel = p.as ? `[as="${CSS.escape(p.as)}"]` : "";
105
- if (p.rel && p.href) link = document.querySelector(`link[rel="${CSS.escape(p.rel)}"][href="${CSS.escape(p.href)}"]${asSel}`);
106
- else if (p.rel) link = document.querySelector(`link[rel="${CSS.escape(p.rel)}"]${asSel}`);
107
- if (!link) {
108
- link = document.createElement("link");
109
- document.head.appendChild(link);
110
- }
111
- const LINK_ATTR = { crossOrigin: "crossorigin", referrerPolicy: "referrerpolicy", fetchPriority: "fetchpriority", hrefLang: "hreflang" };
112
- for (const [k, v] of Object.entries(p)) {
113
- if (v != null && v !== false) link.setAttribute(LINK_ATTR[k] ?? k, String(v));
114
- }
115
- }, []);
116
- const addStyle = React.useCallback((props) => {
117
- const p = props;
118
- let style = null;
119
- if (p["data-id"]) style = document.querySelector(`style[data-id="${CSS.escape(p["data-id"])}"]`);
120
- if (!style) {
121
- style = document.createElement("style");
122
- document.head.appendChild(style);
32
+ var LINK_ATTR = {
33
+ crossOrigin: "crossorigin",
34
+ referrerPolicy: "referrerpolicy",
35
+ fetchPriority: "fetchpriority",
36
+ hrefLang: "hreflang"
37
+ };
38
+ function makeServerCtx(head) {
39
+ return {
40
+ setTitle: (t) => {
41
+ head.title = t;
42
+ },
43
+ addMeta: (p) => {
44
+ head.meta[deriveKey("meta", p)] = p;
45
+ },
46
+ addLink: (p) => {
47
+ head.link[deriveKey("link", p)] = p;
48
+ },
49
+ addStyle: (p) => {
50
+ head.style[deriveKey("style", p)] = p;
51
+ },
52
+ addScript: (p) => {
53
+ head.script[deriveKey("script", p)] = p;
54
+ },
55
+ setStatus: (s) => {
56
+ head.status = s;
123
57
  }
124
- for (const [k, v] of Object.entries(p)) {
125
- if (k === "dangerouslySetInnerHTML") {
126
- style.innerHTML = v.__html ?? "";
127
- continue;
58
+ };
59
+ }
60
+ var _cliCtx = null;
61
+ function makeClientCtx() {
62
+ if (_cliCtx) return _cliCtx;
63
+ _cliCtx = {
64
+ setTitle: (title) => {
65
+ document.title = title;
66
+ },
67
+ setStatus: () => {
68
+ },
69
+ addMeta: (props) => {
70
+ const p = props;
71
+ let meta = null;
72
+ if (p.name) meta = document.querySelector(`meta[name="${CSS.escape(p.name)}"]`);
73
+ else if (p.property) meta = document.querySelector(`meta[property="${CSS.escape(p.property)}"]`);
74
+ else if (p.httpEquiv ?? p["http-equiv"]) meta = document.querySelector(`meta[http-equiv="${CSS.escape(p.httpEquiv ?? p["http-equiv"])}"]`);
75
+ else if ("charSet" in p || "charset" in p) meta = document.querySelector("meta[charset]");
76
+ if (!meta) {
77
+ meta = document.createElement("meta");
78
+ document.head.appendChild(meta);
128
79
  }
129
- if (v != null && v !== false) style.setAttribute(k, String(v));
130
- }
131
- }, []);
132
- const addScript = React.useCallback((props) => {
133
- const p = props;
134
- let script = null;
135
- if (p.src) script = document.querySelector(`script[src="${CSS.escape(p.src)}"]`);
136
- else if (p["data-id"]) script = document.querySelector(`script[data-id="${CSS.escape(p["data-id"])}"]`);
137
- if (!script) {
138
- script = document.createElement("script");
139
- document.body.appendChild(script);
140
- }
141
- for (const [k, v] of Object.entries(p)) {
142
- if (k === "dangerouslySetInnerHTML") {
143
- script.innerHTML = v.__html ?? "";
144
- continue;
80
+ for (const [k, v] of Object.entries(p)) {
81
+ if (v != null && v !== false) meta.setAttribute(k === "charSet" ? "charset" : k === "httpEquiv" ? "http-equiv" : k, String(v));
82
+ }
83
+ },
84
+ addLink: (props) => {
85
+ const p = props;
86
+ let link = null;
87
+ const asSel = p.as ? `[as="${CSS.escape(p.as)}"]` : "";
88
+ if (p.rel && p.href) link = document.querySelector(`link[rel="${CSS.escape(p.rel)}"][href="${CSS.escape(p.href)}"]${asSel}`);
89
+ else if (p.rel) link = document.querySelector(`link[rel="${CSS.escape(p.rel)}"]${asSel}`);
90
+ if (!link) {
91
+ link = document.createElement("link");
92
+ document.head.appendChild(link);
93
+ }
94
+ for (const [k, v] of Object.entries(p)) {
95
+ if (v != null && v !== false) link.setAttribute(LINK_ATTR[k] ?? k, String(v));
96
+ }
97
+ },
98
+ addStyle: (props) => {
99
+ const p = props;
100
+ let style = null;
101
+ if (p["data-id"]) style = document.querySelector(`style[data-id="${CSS.escape(p["data-id"])}"]`);
102
+ if (!style) {
103
+ style = document.createElement("style");
104
+ document.head.appendChild(style);
105
+ }
106
+ for (const [k, v] of Object.entries(p)) {
107
+ if (k === "dangerouslySetInnerHTML") {
108
+ style.innerHTML = v.__html ?? "";
109
+ continue;
110
+ }
111
+ if (v != null && v !== false) style.setAttribute(k, String(v));
112
+ }
113
+ },
114
+ addScript: (props) => {
115
+ const p = props;
116
+ let script = null;
117
+ if (p.src) script = document.querySelector(`script[src="${CSS.escape(p.src)}"]`);
118
+ else if (p["data-id"]) script = document.querySelector(`script[data-id="${CSS.escape(p["data-id"])}"]`);
119
+ if (!script) {
120
+ script = document.createElement("script");
121
+ document.body.appendChild(script);
122
+ }
123
+ for (const [k, v] of Object.entries(p)) {
124
+ if (k === "dangerouslySetInnerHTML") {
125
+ script.innerHTML = v.__html ?? "";
126
+ continue;
127
+ }
128
+ if (v != null && v !== false) script.setAttribute(k, String(v));
145
129
  }
146
- if (v != null && v !== false) script.setAttribute(k, String(v));
147
- }
148
- }, []);
149
- const contextValue = React.useMemo(() => ({
150
- setTitle,
151
- addMeta,
152
- addLink,
153
- addStyle,
154
- addScript,
155
- setStatus: () => {
156
130
  }
157
- }), [setTitle, addMeta, addLink, addStyle, addScript]);
158
- return /* @__PURE__ */ jsx(AppContext.Provider, { value: contextValue, children });
159
- });
160
- var useApp = () => React.useContext(AppContext);
131
+ };
132
+ return _cliCtx;
133
+ }
134
+ function getCtx() {
135
+ if (typeof window === "undefined") {
136
+ const head = globalThis.__hadarsContext?.head;
137
+ if (!head) return null;
138
+ return makeServerCtx(head);
139
+ }
140
+ return makeClientCtx();
141
+ }
161
142
  var clientServerDataCache = /* @__PURE__ */ new Map();
162
143
  var pendingDataFetch = /* @__PURE__ */ new Map();
163
144
  var fetchedPaths = /* @__PURE__ */ new Set();
@@ -227,26 +208,12 @@ function useServerData(key, fn) {
227
208
  const unsuspend = globalThis.__hadarsUnsuspend;
228
209
  if (!unsuspend) return void 0;
229
210
  const _u = unsuspend;
230
- if (!_u.seenThisPass) _u.seenThisPass = /* @__PURE__ */ new Set();
231
- if (!_u.seenLastPass) _u.seenLastPass = /* @__PURE__ */ new Set();
232
- if (_u.newPassStarting) {
233
- _u.seenLastPass = new Set(_u.seenThisPass);
234
- _u.seenThisPass.clear();
235
- _u.newPassStarting = false;
236
- }
237
- _u.seenThisPass.add(cacheKey);
211
+ if (!_u.pendingCreated) _u.pendingCreated = 0;
238
212
  const existing = unsuspend.cache.get(cacheKey);
213
+ if (existing?.status === "fulfilled" && _u.lastPendingKey === cacheKey) {
214
+ _u.lastPendingKeyAccessed = true;
215
+ }
239
216
  if (!existing) {
240
- if (_u.seenLastPass.size > 0) {
241
- const hasVanishedKey = [..._u.seenLastPass].some(
242
- (k) => !_u.seenThisPass.has(k)
243
- );
244
- if (hasVanishedKey) {
245
- throw new Error(
246
- `[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.`
247
- );
248
- }
249
- }
250
217
  const result = fn();
251
218
  const isThenable = result !== null && typeof result === "object" && typeof result.then === "function";
252
219
  if (!isThenable) {
@@ -254,6 +221,22 @@ function useServerData(key, fn) {
254
221
  unsuspend.cache.set(cacheKey, { status: "fulfilled", value });
255
222
  return value;
256
223
  }
224
+ if (_u.lastPendingKey != null && !_u.lastPendingKeyAccessed) {
225
+ const prev = unsuspend.cache.get(_u.lastPendingKey);
226
+ if (prev?.status === "fulfilled") {
227
+ throw new Error(
228
+ `[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.`
229
+ );
230
+ }
231
+ }
232
+ _u.pendingCreated++;
233
+ if (_u.pendingCreated > 100) {
234
+ throw new Error(
235
+ `[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)}.`
236
+ );
237
+ }
238
+ _u.lastPendingKey = cacheKey;
239
+ _u.lastPendingKeyAccessed = false;
257
240
  const promise = result.then(
258
241
  (value) => {
259
242
  unsuspend.cache.set(cacheKey, { status: "fulfilled", value });
@@ -263,25 +246,18 @@ function useServerData(key, fn) {
263
246
  }
264
247
  );
265
248
  unsuspend.cache.set(cacheKey, { status: "pending", promise });
266
- _u.newPassStarting = true;
267
249
  throw promise;
268
250
  }
269
251
  if (existing.status === "pending") {
270
- _u.newPassStarting = true;
271
252
  throw existing.promise;
272
253
  }
273
254
  if (existing.status === "rejected") throw existing.reason;
274
255
  return existing.value;
275
256
  }
276
257
  var Head = React.memo(({ children, status }) => {
277
- const {
278
- setStatus,
279
- setTitle,
280
- addMeta,
281
- addLink,
282
- addStyle,
283
- addScript
284
- } = useApp();
258
+ const ctx = getCtx();
259
+ if (!ctx) return null;
260
+ const { setStatus, setTitle, addMeta, addLink, addStyle, addScript } = ctx;
285
261
  if (status) {
286
262
  setStatus(status);
287
263
  }
@@ -326,7 +302,6 @@ var Head = React.memo(({ children, status }) => {
326
302
  });
327
303
 
328
304
  // src/index.tsx
329
- var HadarsContext = typeof window === "undefined" ? AppProviderSSR : AppProviderCSR;
330
305
  function loadModule(path) {
331
306
  return import(
332
307
  /* webpackIgnore: true */
@@ -334,7 +309,6 @@ function loadModule(path) {
334
309
  );
335
310
  }
336
311
  export {
337
- HadarsContext,
338
312
  Head as HadarsHead,
339
313
  initServerDataCache,
340
314
  loadModule,