hadars 0.2.0 → 0.2.2-rc.0

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/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();
@@ -206,14 +187,17 @@ function useServerData(key, fn) {
206
187
  pendingDataFetch.set(pathKey, p);
207
188
  queueMicrotask(async () => {
208
189
  try {
209
- const res = await fetch(pathKey, {
210
- headers: { "Accept": "application/json" }
211
- });
212
- if (res.ok) {
213
- const json = await res.json();
214
- for (const [k, v] of Object.entries(json.serverData ?? {})) {
215
- clientServerDataCache.set(k, v);
216
- }
190
+ let json = null;
191
+ if (globalThis.__hadarsStatic) {
192
+ const sidecarUrl = pathKey.replace(/\/$/, "") + "/index.json";
193
+ const res = await fetch(sidecarUrl).catch(() => null);
194
+ if (res?.ok) json = await res.json().catch(() => null);
195
+ } else {
196
+ const res = await fetch(pathKey, { headers: { "Accept": "application/json" } });
197
+ if (res.ok) json = await res.json();
198
+ }
199
+ for (const [k, v] of Object.entries(json?.serverData ?? {})) {
200
+ clientServerDataCache.set(k, v);
217
201
  }
218
202
  } finally {
219
203
  fetchedPaths.add(pathKey);
@@ -227,26 +211,12 @@ function useServerData(key, fn) {
227
211
  const unsuspend = globalThis.__hadarsUnsuspend;
228
212
  if (!unsuspend) return void 0;
229
213
  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);
214
+ if (!_u.pendingCreated) _u.pendingCreated = 0;
238
215
  const existing = unsuspend.cache.get(cacheKey);
216
+ if (existing?.status === "fulfilled" && _u.lastPendingKey === cacheKey) {
217
+ _u.lastPendingKeyAccessed = true;
218
+ }
239
219
  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
220
  const result = fn();
251
221
  const isThenable = result !== null && typeof result === "object" && typeof result.then === "function";
252
222
  if (!isThenable) {
@@ -254,6 +224,22 @@ function useServerData(key, fn) {
254
224
  unsuspend.cache.set(cacheKey, { status: "fulfilled", value });
255
225
  return value;
256
226
  }
227
+ if (_u.lastPendingKey != null && !_u.lastPendingKeyAccessed) {
228
+ const prev = unsuspend.cache.get(_u.lastPendingKey);
229
+ if (prev?.status === "fulfilled") {
230
+ throw new Error(
231
+ `[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.`
232
+ );
233
+ }
234
+ }
235
+ _u.pendingCreated++;
236
+ if (_u.pendingCreated > 100) {
237
+ throw new Error(
238
+ `[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)}.`
239
+ );
240
+ }
241
+ _u.lastPendingKey = cacheKey;
242
+ _u.lastPendingKeyAccessed = false;
257
243
  const promise = result.then(
258
244
  (value) => {
259
245
  unsuspend.cache.set(cacheKey, { status: "fulfilled", value });
@@ -263,25 +249,18 @@ function useServerData(key, fn) {
263
249
  }
264
250
  );
265
251
  unsuspend.cache.set(cacheKey, { status: "pending", promise });
266
- _u.newPassStarting = true;
267
252
  throw promise;
268
253
  }
269
254
  if (existing.status === "pending") {
270
- _u.newPassStarting = true;
271
255
  throw existing.promise;
272
256
  }
273
257
  if (existing.status === "rejected") throw existing.reason;
274
258
  return existing.value;
275
259
  }
276
260
  var Head = React.memo(({ children, status }) => {
277
- const {
278
- setStatus,
279
- setTitle,
280
- addMeta,
281
- addLink,
282
- addStyle,
283
- addScript
284
- } = useApp();
261
+ const ctx = getCtx();
262
+ if (!ctx) return null;
263
+ const { setStatus, setTitle, addMeta, addLink, addStyle, addScript } = ctx;
285
264
  if (status) {
286
265
  setStatus(status);
287
266
  }
@@ -326,7 +305,6 @@ var Head = React.memo(({ children, status }) => {
326
305
  });
327
306
 
328
307
  // src/index.tsx
329
- var HadarsContext = typeof window === "undefined" ? AppProviderSSR : AppProviderCSR;
330
308
  function loadModule(path) {
331
309
  return import(
332
310
  /* webpackIgnore: true */
@@ -334,7 +312,6 @@ function loadModule(path) {
334
312
  );
335
313
  }
336
314
  export {
337
- HadarsContext,
338
315
  Head as HadarsHead,
339
316
  initServerDataCache,
340
317
  loadModule,
package/dist/lambda.cjs CHANGED
@@ -1226,29 +1226,33 @@ var getReactResponse = async (req, opts) => {
1226
1226
  head: { title: "Hadars App", meta: {}, link: {}, style: {}, script: {}, status: 200 }
1227
1227
  };
1228
1228
  let props = {
1229
- ...getInitProps ? await getInitProps(req) : {},
1229
+ ...getInitProps ? await getInitProps(req, opts.staticCtx) : {},
1230
1230
  location: req.location,
1231
1231
  context
1232
1232
  };
1233
1233
  const unsuspend = { cache: /* @__PURE__ */ new Map() };
1234
1234
  globalThis.__hadarsUnsuspend = unsuspend;
1235
+ globalThis.__hadarsContext = context;
1235
1236
  const element = createElement(App, props);
1236
1237
  try {
1237
1238
  await renderPreflight(element);
1238
1239
  } finally {
1239
1240
  globalThis.__hadarsUnsuspend = null;
1241
+ globalThis.__hadarsContext = null;
1240
1242
  }
1241
1243
  const status = context.head.status;
1242
1244
  const getAppBody = async () => {
1243
1245
  globalThis.__hadarsUnsuspend = unsuspend;
1246
+ globalThis.__hadarsContext = context;
1244
1247
  try {
1245
1248
  return await renderToString(element);
1246
1249
  } finally {
1247
1250
  globalThis.__hadarsUnsuspend = null;
1251
+ globalThis.__hadarsContext = null;
1248
1252
  }
1249
1253
  };
1250
1254
  const finalize = async () => {
1251
- const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
1255
+ const restProps = getFinalProps ? await getFinalProps(props) : props;
1252
1256
  const serverData = {};
1253
1257
  let hasServerData = false;
1254
1258
  for (const [key, entry] of unsuspend.cache) {
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-mKu5txjW.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-mKu5txjW.js';
3
2
 
4
3
  /**
5
4
  * AWS Lambda adapter for hadars.