@tanstack/redact 0.0.21 → 0.1.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 (254) hide show
  1. package/dist/core/attributes.js +2 -1
  2. package/dist/core/attributes.js.map +2 -2
  3. package/dist/core/browser.d.ts +9 -0
  4. package/dist/core/browser.js +35 -0
  5. package/dist/core/browser.js.map +7 -0
  6. package/dist/core/index.d.ts +2 -2
  7. package/dist/core/index.js +3 -3
  8. package/dist/core/internal.d.ts +31 -8
  9. package/dist/core/internal.js +3 -1
  10. package/dist/core/internal.js.map +2 -2
  11. package/dist/core/resource-hints.d.ts +7 -0
  12. package/dist/core/resource-hints.js +19 -0
  13. package/dist/core/resource-hints.js.map +7 -0
  14. package/dist/core/types.js +1 -1
  15. package/dist/dom/_all.d.ts +11 -16
  16. package/dist/dom/_all.js +9 -20
  17. package/dist/dom/_all.js.map +2 -2
  18. package/dist/dom/client.d.ts +4 -4
  19. package/dist/dom/client.js +4 -4
  20. package/dist/dom/commit.d.ts +23 -0
  21. package/dist/dom/commit.js +191 -0
  22. package/dist/dom/commit.js.map +7 -0
  23. package/dist/dom/dispatcher.d.ts +4 -22
  24. package/dist/dom/dispatcher.js +184 -126
  25. package/dist/dom/dispatcher.js.map +3 -3
  26. package/dist/dom/dom.d.ts +2 -0
  27. package/dist/dom/dom.js +72 -52
  28. package/dist/dom/dom.js.map +2 -2
  29. package/dist/dom/error-info.d.ts +2 -0
  30. package/dist/dom/error-info.js +16 -0
  31. package/dist/dom/error-info.js.map +7 -0
  32. package/dist/dom/event-replay.js +1 -1
  33. package/dist/dom/features/activity/full.d.ts +1 -0
  34. package/dist/dom/features/activity/full.js +162 -0
  35. package/dist/dom/features/activity/full.js.map +7 -0
  36. package/dist/dom/features/activity/index.d.ts +1 -0
  37. package/dist/dom/features/activity/index.js +3 -0
  38. package/dist/dom/features/activity/index.js.map +7 -0
  39. package/dist/dom/features/activity/stub.d.ts +1 -0
  40. package/dist/dom/features/activity/stub.js +12 -0
  41. package/dist/dom/features/activity/stub.js.map +7 -0
  42. package/dist/dom/features/class/full.js +87 -27
  43. package/dist/dom/features/class/full.js.map +2 -2
  44. package/dist/dom/features/class/index.d.ts +1 -1
  45. package/dist/dom/features/class/index.js +2 -2
  46. package/dist/dom/features/class/stub.js +3 -3
  47. package/dist/dom/features/context/full.d.ts +4 -1
  48. package/dist/dom/features/context/full.js +72 -15
  49. package/dist/dom/features/context/full.js.map +2 -2
  50. package/dist/dom/features/context/index.d.ts +1 -1
  51. package/dist/dom/features/context/index.js +2 -2
  52. package/dist/dom/features/context/stub.d.ts +4 -1
  53. package/dist/dom/features/context/stub.js +17 -8
  54. package/dist/dom/features/context/stub.js.map +2 -2
  55. package/dist/dom/features/forward-ref/full.js +12 -21
  56. package/dist/dom/features/forward-ref/full.js.map +2 -2
  57. package/dist/dom/features/forward-ref/index.d.ts +1 -1
  58. package/dist/dom/features/forward-ref/index.js +2 -2
  59. package/dist/dom/features/forward-ref/stub.js +4 -4
  60. package/dist/dom/features/fragment-refs/index.d.ts +1 -0
  61. package/dist/dom/features/fragment-refs/index.js +8 -0
  62. package/dist/dom/features/fragment-refs/index.js.map +7 -0
  63. package/dist/dom/features/fragment-refs/stub.d.ts +3 -0
  64. package/dist/dom/features/fragment-refs/stub.js +14 -0
  65. package/dist/dom/features/fragment-refs/stub.js.map +7 -0
  66. package/dist/dom/features/hydration/full.d.ts +11 -6
  67. package/dist/dom/features/hydration/full.js +76 -77
  68. package/dist/dom/features/hydration/full.js.map +2 -2
  69. package/dist/dom/features/hydration/index.d.ts +1 -1
  70. package/dist/dom/features/hydration/index.js +2 -2
  71. package/dist/dom/features/hydration/stub.d.ts +7 -2
  72. package/dist/dom/features/hydration/stub.js +1 -1
  73. package/dist/dom/features/hydration/stub.js.map +1 -1
  74. package/dist/dom/features/index.d.ts +8 -7
  75. package/dist/dom/features/index.js +9 -8
  76. package/dist/dom/features/index.js.map +2 -2
  77. package/dist/dom/features/lazy/full.js +4 -4
  78. package/dist/dom/features/lazy/index.d.ts +1 -1
  79. package/dist/dom/features/lazy/index.js +2 -2
  80. package/dist/dom/features/lazy/stub.js +4 -4
  81. package/dist/dom/features/memo/full.js +8 -6
  82. package/dist/dom/features/memo/full.js.map +2 -2
  83. package/dist/dom/features/memo/index.d.ts +1 -1
  84. package/dist/dom/features/memo/index.js +2 -2
  85. package/dist/dom/features/memo/stub.js +4 -4
  86. package/dist/dom/features/portal/full.js +4 -4
  87. package/dist/dom/features/portal/index.d.ts +1 -1
  88. package/dist/dom/features/portal/index.js +2 -2
  89. package/dist/dom/features/portal/stub.js +4 -4
  90. package/dist/dom/features/suspense/full.js +197 -52
  91. package/dist/dom/features/suspense/full.js.map +2 -2
  92. package/dist/dom/features/suspense/index.d.ts +1 -1
  93. package/dist/dom/features/suspense/index.js +2 -2
  94. package/dist/dom/features/suspense/stub.js +4 -4
  95. package/dist/dom/features/view-transition/full.d.ts +6 -0
  96. package/dist/dom/features/view-transition/full.js +280 -0
  97. package/dist/dom/features/view-transition/full.js.map +7 -0
  98. package/dist/dom/features/view-transition/index.d.ts +1 -0
  99. package/dist/dom/features/view-transition/index.js +3 -0
  100. package/dist/dom/features/view-transition/index.js.map +7 -0
  101. package/dist/dom/features/view-transition/resources.d.ts +11 -0
  102. package/dist/dom/features/view-transition/resources.js +108 -0
  103. package/dist/dom/features/view-transition/resources.js.map +7 -0
  104. package/dist/dom/features/view-transition/stub.d.ts +6 -0
  105. package/dist/dom/features/view-transition/stub.js +22 -0
  106. package/dist/dom/features/view-transition/stub.js.map +7 -0
  107. package/dist/dom/features/view-transition/visual.d.ts +14 -0
  108. package/dist/dom/features/view-transition/visual.js +356 -0
  109. package/dist/dom/features/view-transition/visual.js.map +7 -0
  110. package/dist/dom/fragment-instance.d.ts +40 -0
  111. package/dist/dom/fragment-instance.js +324 -0
  112. package/dist/dom/fragment-instance.js.map +7 -0
  113. package/dist/dom/index.d.ts +11 -11
  114. package/dist/dom/index.js +11 -18
  115. package/dist/dom/index.js.map +3 -3
  116. package/dist/dom/portal.d.ts +2 -2
  117. package/dist/dom/portal.js +2 -2
  118. package/dist/dom/portal.js.map +1 -1
  119. package/dist/dom/reconcile.d.ts +32 -3
  120. package/dist/dom/reconcile.js +689 -221
  121. package/dist/dom/reconcile.js.map +2 -2
  122. package/dist/dom/resource-hints.d.ts +6 -0
  123. package/dist/dom/resource-hints.js +115 -0
  124. package/dist/dom/resource-hints.js.map +7 -0
  125. package/dist/dom/resource-store.d.ts +13 -0
  126. package/dist/dom/resource-store.js +43 -0
  127. package/dist/dom/resource-store.js.map +7 -0
  128. package/dist/dom/resources.d.ts +1 -0
  129. package/dist/dom/resources.js +44 -0
  130. package/dist/dom/resources.js.map +7 -0
  131. package/dist/dom/retained-effects.d.ts +6 -0
  132. package/dist/dom/retained-effects.js +49 -0
  133. package/dist/dom/retained-effects.js.map +7 -0
  134. package/dist/dom/root-internal.d.ts +4 -4
  135. package/dist/dom/root-internal.js +2 -2
  136. package/dist/dom/root-internal.js.map +2 -2
  137. package/dist/dom/root.d.ts +5 -5
  138. package/dist/dom/root.js +11 -11
  139. package/dist/dom/root.js.map +2 -2
  140. package/dist/dom/test-utils.js +1 -1
  141. package/dist/react/activity.d.ts +7 -0
  142. package/dist/react/activity.js +8 -0
  143. package/dist/react/activity.js.map +7 -0
  144. package/dist/react/children.d.ts +1 -1
  145. package/dist/react/children.js +3 -3
  146. package/dist/react/children.js.map +1 -1
  147. package/dist/react/class.d.ts +1 -1
  148. package/dist/react/class.js +3 -3
  149. package/dist/react/class.js.map +1 -1
  150. package/dist/react/compiler-runtime.js +2 -2
  151. package/dist/react/context.d.ts +7 -2
  152. package/dist/react/context.js +3 -11
  153. package/dist/react/context.js.map +3 -3
  154. package/dist/react/element.d.ts +6 -3
  155. package/dist/react/element.js +7 -11
  156. package/dist/react/element.js.map +2 -2
  157. package/dist/react/hooks.d.ts +26 -7
  158. package/dist/react/hooks.js +13 -3
  159. package/dist/react/hooks.js.map +2 -2
  160. package/dist/react/index.d.ts +39 -24
  161. package/dist/react/index.js +39 -19
  162. package/dist/react/index.js.map +3 -3
  163. package/dist/react/jsx-runtime.d.ts +2 -2
  164. package/dist/react/jsx-runtime.js +3 -3
  165. package/dist/react/memo.d.ts +1 -1
  166. package/dist/react/memo.js +1 -1
  167. package/dist/react/portal.js +1 -1
  168. package/dist/react/shared-internals.d.ts +15 -5
  169. package/dist/react/shared-internals.js +2 -3
  170. package/dist/react/shared-internals.js.map +2 -2
  171. package/dist/react/suspense.js +1 -1
  172. package/dist/react/view-transition.d.ts +32 -0
  173. package/dist/react/view-transition.js +13 -0
  174. package/dist/react/view-transition.js.map +7 -0
  175. package/dist/scheduler/index.js +1 -1
  176. package/dist/server/bootstrap-script.d.ts +1 -23
  177. package/dist/server/bootstrap-script.js +7 -6
  178. package/dist/server/bootstrap-script.js.map +2 -2
  179. package/dist/server/dispatcher.d.ts +28 -6
  180. package/dist/server/dispatcher.js +33 -15
  181. package/dist/server/dispatcher.js.map +2 -2
  182. package/dist/server/escape.js +16 -15
  183. package/dist/server/escape.js.map +2 -2
  184. package/dist/server/index.d.ts +6 -6
  185. package/dist/server/index.js +7 -7
  186. package/dist/server/renderToString.d.ts +1 -1
  187. package/dist/server/renderToString.js +12 -8
  188. package/dist/server/renderToString.js.map +2 -2
  189. package/dist/server/resource-hints.d.ts +22 -0
  190. package/dist/server/resource-hints.js +148 -0
  191. package/dist/server/resource-hints.js.map +7 -0
  192. package/dist/server/stream.d.ts +15 -2
  193. package/dist/server/stream.js +155 -63
  194. package/dist/server/stream.js.map +2 -2
  195. package/dist/server/walk.d.ts +15 -2
  196. package/dist/server/walk.js +150 -110
  197. package/dist/server/walk.js.map +2 -2
  198. package/dist/vite/index.d.ts +6 -0
  199. package/dist/vite/index.js +22 -6
  200. package/dist/vite/index.js.map +2 -2
  201. package/package.json +1 -1
  202. package/src/core/attributes.ts +1 -0
  203. package/src/core/browser.ts +33 -0
  204. package/src/core/internal.ts +24 -6
  205. package/src/core/resource-hints.ts +36 -0
  206. package/src/dom/_all.ts +1 -7
  207. package/src/dom/commit.ts +186 -0
  208. package/src/dom/dispatcher.ts +190 -134
  209. package/src/dom/dom.ts +70 -54
  210. package/src/dom/error-info.ts +15 -0
  211. package/src/dom/features/activity/full.ts +166 -0
  212. package/src/dom/features/activity/index.ts +1 -0
  213. package/src/dom/features/activity/stub.ts +14 -0
  214. package/src/dom/features/class/full.ts +72 -28
  215. package/src/dom/features/context/full.ts +70 -13
  216. package/src/dom/features/context/stub.ts +10 -5
  217. package/src/dom/features/forward-ref/full.ts +9 -19
  218. package/src/dom/features/fragment-refs/index.ts +1 -0
  219. package/src/dom/features/fragment-refs/stub.ts +3 -0
  220. package/src/dom/features/hydration/full.ts +84 -85
  221. package/src/dom/features/hydration/stub.ts +1 -1
  222. package/src/dom/features/index.ts +1 -0
  223. package/src/dom/features/memo/full.ts +4 -3
  224. package/src/dom/features/suspense/full.ts +192 -55
  225. package/src/dom/features/view-transition/full.ts +288 -0
  226. package/src/dom/features/view-transition/index.ts +1 -0
  227. package/src/dom/features/view-transition/resources.ts +107 -0
  228. package/src/dom/features/view-transition/stub.ts +7 -0
  229. package/src/dom/features/view-transition/visual.ts +406 -0
  230. package/src/dom/fragment-instance.ts +337 -0
  231. package/src/dom/index.ts +6 -7
  232. package/src/dom/portal.ts +1 -1
  233. package/src/dom/reconcile.ts +695 -242
  234. package/src/dom/resource-hints.ts +115 -0
  235. package/src/dom/resource-store.ts +52 -0
  236. package/src/dom/resources.ts +39 -0
  237. package/src/dom/retained-effects.ts +38 -0
  238. package/src/dom/root-internal.ts +4 -4
  239. package/src/dom/root.ts +12 -14
  240. package/src/react/activity.ts +10 -0
  241. package/src/react/context.ts +4 -11
  242. package/src/react/element.ts +11 -12
  243. package/src/react/hooks.ts +12 -3
  244. package/src/react/index.ts +18 -0
  245. package/src/react/shared-internals.ts +3 -3
  246. package/src/react/view-transition.ts +41 -0
  247. package/src/server/bootstrap-script.ts +36 -5
  248. package/src/server/dispatcher.ts +35 -13
  249. package/src/server/escape.ts +15 -12
  250. package/src/server/renderToString.ts +7 -5
  251. package/src/server/resource-hints.ts +165 -0
  252. package/src/server/stream.ts +154 -62
  253. package/src/server/walk.ts +79 -26
  254. package/src/vite/index.ts +30 -13
@@ -0,0 +1,148 @@
1
+ // src/server/resource-hints.ts
2
+ import { resourceKind, resourceURL } from "../core/resource-hints.js";
3
+ import { attrToHtml, escapeAttr } from "./escape.js";
4
+ import { attributeName } from "../core/attributes.js";
5
+ function styleText(props) {
6
+ const html = props.dangerouslySetInnerHTML;
7
+ if (html != null) {
8
+ if (typeof html !== "object" || !("__html" in html)) throw new Error("dangerouslySetInnerHTML must be an object with an __html field.");
9
+ if (props.children != null) throw new Error("Can only set one of children or dangerouslySetInnerHTML.");
10
+ return html.__html == null ? "" : "" + html.__html;
11
+ }
12
+ const child = Array.isArray(props.children) ? props.children.length < 2 ? props.children[0] : null : props.children;
13
+ return child == null || typeof child === "function" || typeof child === "symbol" ? "" : ("" + child).replace(/(<\/?)(s)(tyle)/gi, (_, prefix, s, suffix) => prefix + "\\" + s.charCodeAt(0).toString(16) + " " + suffix);
14
+ }
15
+ function markup(resource, late) {
16
+ const { kind, href, options } = resource;
17
+ const script = kind === "script" || kind === "module-script";
18
+ const style = kind === "stylesheet";
19
+ const props = script ? { src: href, ...kind === "module-script" ? { type: "module" } : {}, async: true, ...options } : style ? late ? { rel: "preload", as: "style", href, crossOrigin: options.crossOrigin, fetchPriority: options.fetchPriority, integrity: options.integrity, media: options.media, hrefLang: options.hrefLang, referrerPolicy: options.referrerPolicy } : { rel: "stylesheet", href, "data-precedence": options.precedence, ...options } : { rel: kind, ...options.as === "image" && options.imageSrcSet ? {} : { href }, ...options };
20
+ let output = script ? "<script" : "<link";
21
+ for (const name in props) {
22
+ if (name === "precedence" || late && name === "data-precedence") continue;
23
+ const value = name === "href" || name === "src" ? resourceURL(props[name]) : props[name];
24
+ output += attrToHtml(name, value).replace(/'/g, "&#x27;");
25
+ }
26
+ return output + (script ? "><\/script>" : "/>");
27
+ }
28
+ function createResourceHints(shellFlushed = false, styleNonce) {
29
+ const resources = /* @__PURE__ */ new Map();
30
+ const groups = [[], [], [], [], []];
31
+ const styles = /* @__PURE__ */ new Map();
32
+ const dependencies = /* @__PURE__ */ new Map();
33
+ const visibleInline = /* @__PURE__ */ new Set();
34
+ const collector = {
35
+ add(kind, href, options) {
36
+ let type = kind;
37
+ let source = href;
38
+ const init = kind === "style" || kind === "stylesheet" || kind === "script" || kind === "module-script";
39
+ if (kind === "preload") {
40
+ type = options.as;
41
+ if (type === "image" && options.imageSrcSet) source = options.imageSrcSet + "\n" + (options.imageSizes || "");
42
+ } else if (kind === "modulepreload") type = "module-" + (options.as || "script");
43
+ else if (kind === "stylesheet") type = "style";
44
+ else if (kind === "preconnect") type += ":" + (options.crossOrigin ?? "default");
45
+ const key = JSON.stringify([type, source]);
46
+ const prior = resources.get(key);
47
+ if (prior && (!init || prior.kind !== "preload" && prior.kind !== "modulepreload")) return prior;
48
+ const resource = { kind, href, options: { ...options } };
49
+ if (kind === "style" && (!styleNonce || options.nonce === styleNonce)) resource.css = styleText(options);
50
+ if (prior) {
51
+ prior.cancelled = true;
52
+ resource.preloaded = !!prior.sent;
53
+ for (const name of ["crossOrigin", "integrity"]) {
54
+ if (resource.options[name] == null && prior.options[name] != null) resource.options[name] = prior.options[name];
55
+ }
56
+ }
57
+ resources.set(key, resource);
58
+ if (kind === "stylesheet" || kind === "style") {
59
+ const precedence = options.precedence;
60
+ let group = styles.get(precedence);
61
+ if (!group) styles.set(precedence, group = []);
62
+ group.push(resource);
63
+ } else {
64
+ const group = kind === "dns-prefetch" || kind === "preconnect" ? 0 : kind === "preload" && options.as === "font" ? 1 : kind === "preload" && options.as === "image" && options.fetchPriority === "high" ? 2 : init ? 3 : 4;
65
+ groups[group].push(resource);
66
+ }
67
+ return resource;
68
+ },
69
+ addHost(type, props, scope) {
70
+ const kind = resourceKind(type, props);
71
+ if (!kind) return false;
72
+ const resource = collector.add(kind === "script" && props.type === "module" ? "module-script" : kind, kind === "script" ? props.src : props.href, props);
73
+ if (shellFlushed && kind === "stylesheet") {
74
+ const descriptor = [resourceURL(resource.href), resource.options.precedence];
75
+ for (const name in resource.options) {
76
+ if (name === "href" || name === "rel" || name === "precedence" || name === "data-precedence" || name === "children" || name === "dangerouslySetInnerHTML" || name === "innerHTML" || name === "style" || name === "suppressHydrationWarning" || name === "suppressContentEditableWarning" || name === "ref" || name === "key" || /^on/i.test(name) || !/^[a-zA-Z_:][a-zA-Z0-9:_.-]*$/.test(name)) continue;
77
+ const value = resource.options[name];
78
+ if (value == null || typeof value === "function" || typeof value === "symbol" || name === "hidden" && value === false) continue;
79
+ descriptor.push(attributeName(name), name === "hidden" ? "" : name === "src" ? resourceURL("" + value) : "" + value);
80
+ }
81
+ ;
82
+ (scope ? scope.stylesheets ||= /* @__PURE__ */ new Map() : dependencies).set(resource.href, descriptor);
83
+ } else if (shellFlushed && kind === "style") {
84
+ ;
85
+ (scope ? scope.inline ||= /* @__PURE__ */ new Set() : visibleInline).add(resource.options.precedence);
86
+ }
87
+ return true;
88
+ },
89
+ mergeScope(scope, parent) {
90
+ if (scope.stylesheets) for (const [href, descriptor] of scope.stylesheets) {
91
+ ;
92
+ (parent ? parent.stylesheets ||= /* @__PURE__ */ new Map() : dependencies).set(href, descriptor);
93
+ }
94
+ if (scope.inline) for (const precedence of scope.inline) {
95
+ ;
96
+ (parent ? parent.inline ||= /* @__PURE__ */ new Set() : visibleInline).add(precedence);
97
+ }
98
+ },
99
+ takeStylesheets() {
100
+ const result = Array.from(dependencies.values());
101
+ dependencies.clear();
102
+ return result;
103
+ },
104
+ drain() {
105
+ let output = "";
106
+ function flush(group, precedence) {
107
+ let remaining = 0;
108
+ let inline = false, hasStylesheets = false;
109
+ let hrefs = "", rules = "";
110
+ for (const resource of group) {
111
+ if (!resource.sent && !resource.cancelled) {
112
+ if (resource.kind === "style") {
113
+ if (shellFlushed && !visibleInline.has(precedence)) {
114
+ group[remaining++] = resource;
115
+ continue;
116
+ }
117
+ inline = true;
118
+ if (resource.css !== void 0) {
119
+ hrefs += (hrefs ? " " : "") + resource.href;
120
+ rules += resource.css;
121
+ }
122
+ } else {
123
+ if (resource.kind === "stylesheet") hasStylesheets = true;
124
+ if (!(shellFlushed && resource.kind === "stylesheet" && resource.preloaded)) output += markup(resource, shellFlushed);
125
+ }
126
+ resource.sent = true;
127
+ }
128
+ }
129
+ group.length = remaining;
130
+ if (hrefs || inline && !shellFlushed && !hasStylesheets) {
131
+ output += `<style${styleNonce !== void 0 ? ` nonce="${escapeAttr(styleNonce)}"` : ""}${shellFlushed ? ' media="not all"' : ""} data-precedence="${escapeAttr(precedence)}"${hrefs ? ` data-href="${escapeAttr(hrefs)}"` : ""}>${rules}</style>`;
132
+ }
133
+ }
134
+ for (let index = 0; index < groups.length; index++) {
135
+ if (index === 3) for (const [precedence, group] of styles) flush(group, precedence);
136
+ flush(groups[index]);
137
+ }
138
+ shellFlushed = true;
139
+ visibleInline.clear();
140
+ return output;
141
+ }
142
+ };
143
+ return collector;
144
+ }
145
+ export {
146
+ createResourceHints
147
+ };
148
+ //# sourceMappingURL=resource-hints.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/server/resource-hints.ts"],
4
+ "sourcesContent": ["import { resourceKind, resourceURL, type ResourceHintKind, type ResourceHintOptions } from '../core/resource-hints'\nimport { attrToHtml, escapeAttr } from './escape'\nimport { attributeName } from '../core/attributes'\n\ninterface Resource {\n kind: ResourceHintKind | 'style'\n href: string\n options: Record<string, any>\n sent?: boolean\n cancelled?: boolean\n preloaded?: boolean\n css?: string\n}\n\nexport interface ResourceScope {\n stylesheets?: Map<string, string[]>\n inline?: Set<string>\n}\n\nfunction styleText(props: any): string {\n const html = props.dangerouslySetInnerHTML\n if (html != null) {\n if (typeof html !== 'object' || !('__html' in html)) throw new Error('dangerouslySetInnerHTML must be an object with an __html field.')\n if (props.children != null) throw new Error('Can only set one of children or dangerouslySetInnerHTML.')\n return html.__html == null ? '' : '' + html.__html\n }\n const child = Array.isArray(props.children) ? props.children.length < 2 ? props.children[0] : null : props.children\n return child == null || typeof child === 'function' || typeof child === 'symbol' ? ''\n : ('' + child).replace(/(<\\/?)(s)(tyle)/gi, (_, prefix: string, s: string, suffix: string) => prefix + '\\\\' + s.charCodeAt(0).toString(16) + ' ' + suffix)\n}\n\nfunction markup(resource: Resource, late: boolean): string {\n const { kind, href, options } = resource\n const script = kind === 'script' || kind === 'module-script'\n const style = kind === 'stylesheet'\n const props: Record<string, any> = script\n ? { src: href, ...(kind === 'module-script' ? { type: 'module' } : {}), async: true, ...options }\n : style\n ? late\n ? { rel: 'preload', as: 'style', href, crossOrigin: options.crossOrigin, fetchPriority: options.fetchPriority, integrity: options.integrity, media: options.media, hrefLang: options.hrefLang, referrerPolicy: options.referrerPolicy }\n : { rel: 'stylesheet', href, 'data-precedence': options.precedence!, ...options }\n : { rel: kind, ...(options.as === 'image' && options.imageSrcSet ? {} : { href }), ...options }\n let output = script ? '<script' : '<link'\n for (const name in props) {\n if (name === 'precedence' || late && name === 'data-precedence') continue\n const value = name === 'href' || name === 'src' ? resourceURL(props[name]!) : props[name]!\n output += attrToHtml(name, value).replace(/'/g, '&#x27;')\n }\n return output + (script ? '></script>' : '/>')\n}\n\nexport function createResourceHints(shellFlushed = false, styleNonce?: string) {\n const resources = new Map<string, Resource>()\n const groups: Resource[][] = [[], [], [], [], []]\n const styles = new Map<string, Resource[]>()\n const dependencies = new Map<string, string[]>()\n const visibleInline = new Set<string>()\n const collector = {\n add(kind: ResourceHintKind | 'style', href: string, options: ResourceHintOptions): Resource {\n let type: string = kind\n let source = href\n const init = kind === 'style' || kind === 'stylesheet' || kind === 'script' || kind === 'module-script'\n if (kind === 'preload') {\n type = options.as!\n if (type === 'image' && options.imageSrcSet) source = options.imageSrcSet + '\\n' + (options.imageSizes || '')\n } else if (kind === 'modulepreload') type = 'module-' + (options.as || 'script')\n else if (kind === 'stylesheet') type = 'style'\n else if (kind === 'preconnect') type += ':' + (options.crossOrigin ?? 'default')\n const key = JSON.stringify([type, source])\n const prior = resources.get(key)\n if (prior && (!init || prior.kind !== 'preload' && prior.kind !== 'modulepreload')) return prior\n\n const resource: Resource = { kind, href, options: { ...options } }\n if (kind === 'style' && (!styleNonce || options.nonce === styleNonce)) resource.css = styleText(options)\n if (prior) {\n prior.cancelled = true\n resource.preloaded = !!prior.sent\n for (const name of ['crossOrigin', 'integrity']) {\n if (resource.options[name] == null && prior.options[name] != null) resource.options[name] = prior.options[name]!\n }\n }\n resources.set(key, resource)\n if (kind === 'stylesheet' || kind === 'style') {\n const precedence = options.precedence!\n let group = styles.get(precedence)\n if (!group) styles.set(precedence, group = [])\n group.push(resource)\n } else {\n const group = kind === 'dns-prefetch' || kind === 'preconnect' ? 0\n : kind === 'preload' && options.as === 'font' ? 1\n : kind === 'preload' && options.as === 'image' && options.fetchPriority === 'high' ? 2\n : init ? 3 : 4\n groups[group]!.push(resource)\n }\n return resource\n },\n addHost(type: string, props: any, scope?: ResourceScope): boolean {\n const kind = resourceKind(type, props)\n if (!kind) return false\n const resource = collector.add(kind === 'script' && props.type === 'module' ? 'module-script' : kind, kind === 'script' ? props.src : props.href, props)\n if (shellFlushed && kind === 'stylesheet') {\n const descriptor = [resourceURL(resource.href), resource.options.precedence]\n for (const name in resource.options) {\n if (name === 'href' || name === 'rel' || name === 'precedence' || name === 'data-precedence' || name === 'children' || name === 'dangerouslySetInnerHTML' || name === 'innerHTML' || name === 'style' || name === 'suppressHydrationWarning' || name === 'suppressContentEditableWarning' || name === 'ref' || name === 'key' || /^on/i.test(name) || !/^[a-zA-Z_:][a-zA-Z0-9:_.-]*$/.test(name)) continue\n const value = resource.options[name]\n if (value == null || typeof value === 'function' || typeof value === 'symbol' || name === 'hidden' && value === false) continue\n descriptor.push(attributeName(name), name === 'hidden' ? '' : name === 'src' ? resourceURL('' + value) : '' + value)\n }\n ;(scope ? scope.stylesheets ||= new Map() : dependencies).set(resource.href, descriptor)\n } else if (shellFlushed && kind === 'style') {\n ;(scope ? scope.inline ||= new Set() : visibleInline).add(resource.options.precedence)\n }\n return true\n },\n mergeScope(scope: ResourceScope, parent?: ResourceScope): void {\n if (scope.stylesheets) for (const [href, descriptor] of scope.stylesheets) {\n ;(parent ? parent.stylesheets ||= new Map() : dependencies).set(href, descriptor)\n }\n if (scope.inline) for (const precedence of scope.inline) {\n ;(parent ? parent.inline ||= new Set() : visibleInline).add(precedence)\n }\n },\n takeStylesheets(): string[][] {\n const result = Array.from(dependencies.values())\n dependencies.clear()\n return result\n },\n drain(): string {\n let output = ''\n function flush(group: Resource[], precedence?: string) {\n let remaining = 0\n let inline = false, hasStylesheets = false\n let hrefs = '', rules = ''\n for (const resource of group) {\n if (!resource.sent && !resource.cancelled) {\n if (resource.kind === 'style') {\n if (shellFlushed && !visibleInline.has(precedence!)) { group[remaining++] = resource; continue }\n inline = true\n if (resource.css !== undefined) {\n hrefs += (hrefs ? ' ' : '') + resource.href\n rules += resource.css\n }\n } else {\n if (resource.kind === 'stylesheet') hasStylesheets = true\n if (!(shellFlushed && resource.kind === 'stylesheet' && resource.preloaded)) output += markup(resource, shellFlushed)\n }\n resource.sent = true\n }\n }\n group.length = remaining\n if (hrefs || inline && !shellFlushed && !hasStylesheets) {\n output += `<style${styleNonce !== undefined ? ` nonce=\"${escapeAttr(styleNonce)}\"` : ''}${shellFlushed ? ' media=\"not all\"' : ''} data-precedence=\"${escapeAttr(precedence!)}\"${hrefs ? ` data-href=\"${escapeAttr(hrefs)}\"` : ''}>${rules}</style>`\n }\n }\n for (let index = 0; index < groups.length; index++) {\n if (index === 3) for (const [precedence, group] of styles) flush(group, precedence)\n flush(groups[index]!)\n }\n shellFlushed = true\n visibleInline.clear()\n return output\n },\n }\n return collector\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,7 +1,12 @@
1
- import type { ReactNode } from '../core';
1
+ import type { ReactNode } from "../core/index.js";
2
+ import { type SSRFrame } from "./dispatcher.js";
3
+ import { type BrowserBailoutCallback } from "./walk.js";
2
4
  export interface StreamOptions {
3
5
  identifierPrefix?: string;
4
- nonce?: string;
6
+ nonce?: string | {
7
+ script?: string;
8
+ style?: string;
9
+ };
5
10
  bootstrapScriptContent?: string | ReadonlyArray<string>;
6
11
  bootstrapScripts?: ReadonlyArray<string | {
7
12
  src: string;
@@ -13,6 +18,7 @@ export interface StreamOptions {
13
18
  nonce?: string;
14
19
  }>;
15
20
  onError?: (error: unknown) => string | void;
21
+ onBrowserBailout?: BrowserBailoutCallback;
16
22
  signal?: AbortSignal;
17
23
  progressiveChunkSize?: number;
18
24
  }
@@ -22,12 +28,19 @@ export interface ReadableStreamResult extends ReadableStream<Uint8Array> {
22
28
  export interface PipeableWritable {
23
29
  write(chunk: string): unknown;
24
30
  end(): unknown;
31
+ destroy?(error?: unknown): unknown;
25
32
  }
26
33
  export interface OrchestratorState {
27
34
  nextId: number;
28
35
  pending: Set<Promise<void>>;
29
36
  closed: boolean;
30
37
  errored: unknown | null;
38
+ aborted: boolean;
39
+ reason: unknown;
40
+ abort: (reason?: unknown) => void;
41
+ cancelled: Promise<void>;
42
+ onShellReady?: () => void;
43
+ frame?: SSRFrame;
31
44
  }
32
45
  type Emit = (chunk: string) => void;
33
46
  export declare function streamHtml(children: ReactNode, emit: Emit, options: StreamOptions, state: OrchestratorState): Promise<void>;
@@ -1,37 +1,70 @@
1
- // packages/redact/src/server/stream.ts
1
+ // src/server/stream.ts
2
2
  import {
3
3
  beginSSR,
4
4
  endSSR,
5
- installSSRDispatcher,
6
- uninstallSSRDispatcher,
7
- applyContextSnapshot
8
- } from "./dispatcher";
9
- import { walk } from "./walk";
10
- import { BOUNDARY_REVEAL_RUNTIME, revealScript } from "./bootstrap-script";
11
- import { escapeScript } from "./escape";
5
+ applyContextSnapshot,
6
+ currentSSRFrame
7
+ } from "./dispatcher.js";
8
+ import { walk } from "./walk.js";
9
+ import { browserError, isBrowserToken, isBrowserError } from "../core/browser.js";
10
+ import { BOUNDARY_REVEAL_RUNTIME, revealScript } from "./bootstrap-script.js";
11
+ import { escapeAttr, escapeScript } from "./escape.js";
12
+ import { resourceURL } from "../core/resource-hints.js";
13
+ function createState() {
14
+ let wake;
15
+ const state = {
16
+ nextId: 0,
17
+ pending: /* @__PURE__ */ new Set(),
18
+ closed: false,
19
+ errored: null,
20
+ aborted: false,
21
+ reason: void 0,
22
+ cancelled: new Promise((resolve) => {
23
+ wake = resolve;
24
+ }),
25
+ abort(reason) {
26
+ if (state.closed || state.aborted) return;
27
+ state.aborted = true;
28
+ state.reason = isBrowserToken(reason) ? browserError(reason) : reason ?? new Error("The render was aborted by the server without a reason.");
29
+ wake();
30
+ }
31
+ };
32
+ return state;
33
+ }
12
34
  async function streamHtml(children, emit, options, state) {
13
- installSSRDispatcher();
14
- beginSSR(options.identifierPrefix);
15
- const nonce = options.nonce;
35
+ const nonce = scriptNonce(options);
16
36
  try {
17
37
  const boundaries = [];
18
38
  const shellChunks = [];
39
+ let head = 0;
19
40
  const bufferedEmit = (chunk) => {
20
41
  shellChunks.push(chunk);
21
42
  };
22
43
  let shellRendered = false;
23
44
  let rootRetries = 0;
45
+ let resources;
24
46
  while (!shellRendered) {
25
47
  const chunkCount = shellChunks.length;
26
48
  const boundaryCount = boundaries.length;
27
49
  const nextId = state.nextId;
50
+ head = 0;
51
+ const previous = beginSSR(options.identifierPrefix);
52
+ currentSSRFrame().styleNonce = typeof options.nonce === "object" ? options.nonce?.style : void 0;
53
+ if (resources) currentSSRFrame().resources = resources;
54
+ let suspended;
28
55
  try {
56
+ if (state.aborted) throw state.reason;
29
57
  walk(children, {
30
58
  emit: bufferedEmit,
31
59
  onSuspend: (b) => boundaries.push(b),
32
- nextBoundaryId: () => state.nextId++
60
+ nextBoundaryId: () => state.nextId++,
61
+ onBrowserBailout: options.onBrowserBailout,
62
+ onHead: () => {
63
+ head = shellChunks.length;
64
+ }
33
65
  });
34
66
  shellRendered = true;
67
+ state.frame = currentSSRFrame();
35
68
  } catch (err) {
36
69
  shellChunks.length = chunkCount;
37
70
  boundaries.length = boundaryCount;
@@ -40,12 +73,19 @@ async function streamHtml(children, emit, options, state) {
40
73
  if (++rootRetries > 50) {
41
74
  throw new Error("renderToReadableStream exceeded 50 root suspension retries.");
42
75
  }
43
- await err;
76
+ suspended = err;
77
+ } finally {
78
+ resources = currentSSRFrame().resources;
79
+ endSSR(previous);
44
80
  }
81
+ if (suspended) await Promise.race([suspended, state.cancelled]);
45
82
  }
83
+ const hints = state.frame?.resources?.drain();
84
+ if (hints) shellChunks.splice(head, 0, hints);
85
+ if (state.frame) state.frame.shellFlushed = true;
46
86
  const hasBootstrap = bootstrapScriptContentToArray(options.bootstrapScriptContent).length > 0 || (options.bootstrapScripts?.length ?? 0) > 0 || (options.bootstrapModules?.length ?? 0) > 0;
47
87
  if (boundaries.length > 0 || hasBootstrap) {
48
- shellChunks.push(`<script${nonce ? ` nonce="${nonce}"` : ""}>${BOUNDARY_REVEAL_RUNTIME}<\/script>`);
88
+ shellChunks.push(`<script${nonce ? ` nonce="${escapeAttr(nonce)}"` : ""}>${BOUNDARY_REVEAL_RUNTIME}<\/script>`);
49
89
  for (const content of bootstrapScriptContentToArray(options.bootstrapScriptContent)) {
50
90
  shellChunks.push(inlineBootstrapTag(content, nonce));
51
91
  }
@@ -57,46 +97,74 @@ async function streamHtml(children, emit, options, state) {
57
97
  }
58
98
  }
59
99
  if (shellChunks.length) emit(normalizeDocumentShell(shellChunks.join("")));
100
+ state.onShellReady?.();
60
101
  for (const b of boundaries) streamBoundary(b, emit, options, state);
61
102
  await drain(state);
62
103
  } catch (err) {
104
+ if (isBrowserError(err)) {
105
+ err = new Error(
106
+ "The server render could not complete because client rendering was requested outside a Suspense boundary.",
107
+ Object.hasOwn(err, "cause") ? { cause: err.cause } : void 0
108
+ );
109
+ }
63
110
  state.errored = err;
64
111
  if (options.onError) options.onError(err);
65
112
  throw err;
66
113
  } finally {
67
- endSSR();
68
- uninstallSSRDispatcher();
69
114
  state.closed = true;
70
115
  }
71
116
  }
72
117
  function streamBoundary(b, emit, options, state) {
118
+ const nonce = scriptNonce(options);
73
119
  const task = (async () => {
74
120
  try {
75
- await b.thenable;
76
- } catch (err) {
77
- if (options.onError) options.onError(err);
121
+ await Promise.race([b.thenable, state.cancelled]);
122
+ } catch {
78
123
  }
79
124
  if (state.closed) return;
125
+ const defer = (error) => {
126
+ if (isBrowserError(error)) options.onBrowserBailout?.(error, { componentStack: error.componentStack || b.componentStack });
127
+ else options.onError?.(error);
128
+ emit(`<script${nonce ? ` nonce="${escapeAttr(nonce)}"` : ""}>$RB(${b.id}${isBrowserError(error) ? "" : ",1"})<\/script>`);
129
+ };
130
+ if (state.aborted) {
131
+ defer(state.reason);
132
+ return;
133
+ }
80
134
  const parts = [];
81
135
  const sub = [];
136
+ const resourceScope = {};
137
+ let styles;
138
+ const previous = beginSSR(options.identifierPrefix, state.frame);
139
+ state.frame?.resources?.takeStylesheets();
82
140
  const restore = applyContextSnapshot(b.contextSnapshot);
83
141
  try {
84
142
  walk(b.children, {
85
143
  emit: (s) => parts.push(s),
86
144
  onSuspend: (n) => sub.push(n),
87
- nextBoundaryId: () => state.nextId++
145
+ nextBoundaryId: () => state.nextId++,
146
+ onBrowserBailout: options.onBrowserBailout,
147
+ componentStack: b.componentStack,
148
+ resourceScope
88
149
  });
150
+ state.frame?.resources?.mergeScope(resourceScope);
89
151
  } catch (err) {
90
- if (options.onError) options.onError(err);
91
- restore();
152
+ if (isThenable(err)) {
153
+ streamBoundary({ ...b, thenable: err }, emit, options, state);
154
+ } else defer(err);
92
155
  return;
156
+ } finally {
157
+ restore();
158
+ endSSR(previous);
159
+ const hints = state.frame?.resources?.drain();
160
+ styles = state.frame?.resources?.takeStylesheets();
161
+ if (hints) emit(hints);
93
162
  }
94
- restore();
95
- emit(`<div hidden id="S:${b.id}">${parts.join("")}</div>${revealScript(b.id, options.nonce)}`);
163
+ emit(`<div hidden id="S:${b.id}">${parts.join("")}</div>${revealScript(b.id, nonce, styles)}`);
96
164
  for (const s of sub) streamBoundary(s, emit, options, state);
97
165
  })();
98
166
  state.pending.add(task);
99
- task.finally(() => state.pending.delete(task));
167
+ task.then(() => state.pending.delete(task), () => state.pending.delete(task));
100
168
  }
101
169
  async function drain(state) {
102
170
  while (state.pending.size > 0) {
@@ -106,12 +174,15 @@ async function drain(state) {
106
174
  function isThenable(value) {
107
175
  return !!value && typeof value.then === "function";
108
176
  }
177
+ function scriptNonce(options) {
178
+ return typeof options.nonce === "string" ? options.nonce : options.nonce?.script;
179
+ }
109
180
  function bootstrapScriptContentToArray(content) {
110
181
  if (content === void 0) return [];
111
182
  return typeof content === "string" ? [content] : [...content];
112
183
  }
113
184
  function inlineBootstrapTag(content, defaultNonce) {
114
- const nAttr = defaultNonce ? ` nonce="${defaultNonce}"` : "";
185
+ const nAttr = defaultNonce ? ` nonce="${escapeAttr(defaultNonce)}"` : "";
115
186
  return `<script${nAttr}>${escapeScript(content)}<\/script>`;
116
187
  }
117
188
  function normalizeDocumentShell(html) {
@@ -146,26 +217,37 @@ function stripLeadingHeadTags(value) {
146
217
  return rest;
147
218
  }
148
219
  function bootstrapTag(entry, kind, defaultNonce) {
149
- const src = typeof entry === "string" ? entry : entry.src;
220
+ const src = escapeAttr(resourceURL(typeof entry === "string" ? entry : entry.src));
150
221
  const nonce = typeof entry === "string" ? defaultNonce : entry.nonce ?? defaultNonce;
151
- const nAttr = nonce ? ` nonce="${nonce}"` : "";
222
+ const nAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
152
223
  if (kind === "module") return `<script type="module"${nAttr} src="${src}"><\/script>`;
153
224
  return `<script async${nAttr} src="${src}"><\/script>`;
154
225
  }
155
226
  function renderToReadableStream(children, options = {}) {
156
- const state = {
157
- nextId: 0,
158
- pending: /* @__PURE__ */ new Set(),
159
- closed: false,
160
- errored: null
161
- };
227
+ const state = createState();
162
228
  const encoder = new TextEncoder();
229
+ let abortListener;
230
+ const detachAbortListener = () => {
231
+ if (abortListener) {
232
+ options.signal?.removeEventListener("abort", abortListener);
233
+ abortListener = void 0;
234
+ }
235
+ };
163
236
  let allReadyResolve;
164
237
  let allReadyReject;
165
238
  const allReady = new Promise((r, rej) => {
166
239
  allReadyResolve = r;
167
240
  allReadyReject = rej;
168
241
  });
242
+ allReady.catch(() => {
243
+ });
244
+ let shellResolve;
245
+ let shellReject;
246
+ const shell = new Promise((resolve, reject) => {
247
+ shellResolve = resolve;
248
+ shellReject = reject;
249
+ });
250
+ state.onShellReady = () => queueMicrotask(() => shellResolve(Object.assign(stream, { allReady })));
169
251
  const stream = new ReadableStream({
170
252
  start(controller) {
171
253
  const emit = (chunk) => {
@@ -174,8 +256,17 @@ function renderToReadableStream(children, options = {}) {
174
256
  } catch {
175
257
  }
176
258
  };
259
+ if (options.signal) {
260
+ abortListener = () => {
261
+ detachAbortListener();
262
+ state.abort(options.signal.reason);
263
+ };
264
+ options.signal.addEventListener("abort", abortListener);
265
+ if (options.signal.aborted) abortListener();
266
+ }
177
267
  streamHtml(children, emit, options, state).then(
178
268
  () => {
269
+ detachAbortListener();
179
270
  try {
180
271
  controller.close();
181
272
  } catch {
@@ -183,46 +274,51 @@ function renderToReadableStream(children, options = {}) {
183
274
  allReadyResolve();
184
275
  },
185
276
  (err) => {
277
+ detachAbortListener();
186
278
  try {
187
279
  controller.error(err);
188
280
  } catch {
189
281
  }
190
282
  allReadyReject(err);
283
+ shellReject(err);
191
284
  }
192
285
  );
193
- options.signal?.addEventListener("abort", () => {
194
- state.closed = true;
195
- try {
196
- controller.close();
197
- } catch {
198
- }
199
- });
286
+ },
287
+ cancel(reason) {
288
+ state.abort(reason);
289
+ detachAbortListener();
200
290
  }
201
291
  });
202
- return Promise.resolve(Object.assign(stream, { allReady }));
292
+ return shell;
203
293
  }
204
294
  function renderToPipeableStream(children, options = {}) {
205
- const state = {
206
- nextId: 0,
207
- pending: /* @__PURE__ */ new Set(),
208
- closed: false,
209
- errored: null
210
- };
295
+ const state = createState();
211
296
  const buffers = [];
212
297
  let dest = null;
213
298
  let shellReady = false;
299
+ let shellFailed = false;
214
300
  let finished = false;
215
- let aborted = false;
301
+ let failed = false;
302
+ let failure;
303
+ const finishDestination = (target) => {
304
+ if (failed && target.destroy) target.destroy(failure);
305
+ else target.end();
306
+ };
216
307
  const flushTo = (w) => {
217
308
  if (!buffers.length) return;
218
309
  for (const b of buffers) w.write(b);
219
310
  buffers.length = 0;
220
311
  };
221
312
  const emit = (chunk) => {
222
- if (aborted || finished) return;
313
+ if (finished) return;
223
314
  if (dest) dest.write(chunk);
224
315
  else buffers.push(chunk);
225
316
  };
317
+ state.onShellReady = () => queueMicrotask(() => {
318
+ if (shellFailed) return;
319
+ shellReady = true;
320
+ options.onShellReady?.();
321
+ });
226
322
  streamHtml(children, emit, options, state).then(
227
323
  () => {
228
324
  finished = true;
@@ -230,30 +326,26 @@ function renderToPipeableStream(children, options = {}) {
230
326
  options.onAllReady?.();
231
327
  },
232
328
  (err) => {
329
+ finished = true;
330
+ failed = true;
331
+ failure = err;
233
332
  if (!shellReady) {
333
+ shellFailed = true;
234
334
  options.onShellError?.(err);
235
335
  } else {
236
- options.onError?.(err);
237
- if (dest) dest.end();
336
+ if (dest) finishDestination(dest);
238
337
  }
239
338
  }
240
339
  );
241
- queueMicrotask(() => {
242
- if (aborted || finished) return;
243
- shellReady = true;
244
- options.onShellReady?.();
245
- });
246
340
  return {
247
341
  pipe(target) {
248
342
  dest = target;
249
- flushTo(target);
250
- if (finished) target.end();
343
+ if (!failed) flushTo(target);
344
+ if (finished) finishDestination(target);
251
345
  return target;
252
346
  },
253
- abort(_reason) {
254
- aborted = true;
255
- state.closed = true;
256
- if (dest) dest.end();
347
+ abort(reason) {
348
+ state.abort(reason);
257
349
  }
258
350
  };
259
351
  }