hono 4.13.6 → 4.13.7

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.
@@ -33,11 +33,14 @@ __export(base_exports, {
33
33
  booleanAttributes: () => booleanAttributes,
34
34
  cloneElement: () => cloneElement,
35
35
  getNameSpaceContext: () => getNameSpaceContext,
36
+ isUntrustedObject: () => isUntrustedObject,
36
37
  isValidElement: () => isValidElement,
37
38
  jsx: () => jsx,
38
39
  jsxFn: () => jsxFn,
39
40
  memo: () => memo,
40
41
  reactAPICompatVersion: () => reactAPICompatVersion,
42
+ renderChildren: () => renderChildren,
43
+ renderUntrustedObject: () => renderUntrustedObject,
41
44
  shallowEqual: () => shallowEqual
42
45
  });
43
46
  module.exports = __toCommonJS(base_exports);
@@ -101,7 +104,7 @@ const booleanAttributes = [
101
104
  "selected"
102
105
  ];
103
106
  const resolveFunctionComponentResult = (result, suspendedContext) => result.then((resolved) => {
104
- if (!Array.isArray(resolved) && !(resolved instanceof JSXNode)) {
107
+ if (typeof resolved !== "string" && !Array.isArray(resolved) && !(resolved instanceof JSXNode)) {
105
108
  return resolved;
106
109
  }
107
110
  const children = Array.isArray(resolved) ? resolved : [resolved];
@@ -139,6 +142,17 @@ const childrenToStringToBuffer = (children, buffer) => {
139
142
  }
140
143
  }
141
144
  };
145
+ const renderChildren = (children) => (0, import_context.runWithRenderContext)(() => {
146
+ const buffer = [""];
147
+ childrenToStringToBuffer(children, buffer);
148
+ return buffer.length === 1 ? (0, import_html.raw)(buffer[0], buffer.callbacks) : (0, import_html2.stringBufferToString)(buffer, buffer.callbacks);
149
+ });
150
+ const isUntrustedObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof JSXNode) && !(value instanceof Promise) && !value.isEscaped && typeof value.toString === "function";
151
+ const renderUntrustedObject = (value) => {
152
+ const stringified = value.toString();
153
+ const escape = (result) => renderChildren([String(result)]);
154
+ return stringified instanceof Promise ? stringified.then(escape) : escape(stringified);
155
+ };
142
156
  class JSXNode {
143
157
  tag;
144
158
  props;
@@ -369,10 +383,13 @@ const reactAPICompatVersion = "19.0.0-hono-jsx";
369
383
  booleanAttributes,
370
384
  cloneElement,
371
385
  getNameSpaceContext,
386
+ isUntrustedObject,
372
387
  isValidElement,
373
388
  jsx,
374
389
  jsxFn,
375
390
  memo,
376
391
  reactAPICompatVersion,
392
+ renderChildren,
393
+ renderUntrustedObject,
377
394
  shallowEqual
378
395
  });
@@ -31,7 +31,7 @@ var import_streaming = require("./streaming");
31
31
  let errorBoundaryCounter = 0;
32
32
  const childrenToString = async (children) => {
33
33
  try {
34
- return children.flat().map((c) => c == null || typeof c === "boolean" ? "" : c.toString());
34
+ return children.flat().map(resolveChildEarly);
35
35
  } catch (e) {
36
36
  if (e instanceof Promise) {
37
37
  const resume = (0, import_context.captureRenderContext)();
@@ -42,18 +42,16 @@ const childrenToString = async (children) => {
42
42
  }
43
43
  }
44
44
  };
45
- const resolveChildEarly = (c) => {
46
- if (c == null || typeof c === "boolean") {
45
+ const resolveChildEarly = (child) => {
46
+ if (child == null || typeof child === "boolean") {
47
47
  return "";
48
- } else if (typeof c === "string") {
49
- return c;
48
+ } else if (typeof child === "string" || Array.isArray(child)) {
49
+ return (0, import_base.renderChildren)([child]);
50
+ } else if ((0, import_base.isUntrustedObject)(child)) {
51
+ return (0, import_base.renderUntrustedObject)(child);
50
52
  } else {
51
- const str = c.toString();
52
- if (!(str instanceof Promise)) {
53
- return (0, import_html.raw)(str);
54
- } else {
55
- return str;
56
- }
53
+ const str = child.toString();
54
+ return str instanceof Promise ? str : (0, import_html.raw)(str);
57
55
  }
58
56
  };
59
57
  const ErrorBoundary = async ({ children, fallback, fallbackRender, onError }) => {
@@ -69,26 +67,27 @@ const ErrorBoundary = async ({ children, fallback, fallbackRender, onError }) =>
69
67
  let fallbackStrPromise;
70
68
  const resolveFallbackStr = () => fallbackStrPromise ||= (async () => {
71
69
  const awaitedFallback = await fallback;
72
- if (typeof awaitedFallback === "string") {
73
- return awaitedFallback;
74
- } else {
75
- const fallbackResult = await getResume()(() => awaitedFallback?.toString());
76
- if (typeof fallbackResult === "string") {
77
- return (0, import_html.raw)(
78
- fallbackResult,
79
- fallbackResult.callbacks || awaitedFallback?.callbacks
80
- );
81
- }
70
+ if (awaitedFallback === null || awaitedFallback === void 0) {
71
+ return;
72
+ }
73
+ if (typeof awaitedFallback === "string" || Array.isArray(awaitedFallback)) {
74
+ return getResume()(() => (0, import_base.renderChildren)([awaitedFallback]));
75
+ }
76
+ if ((0, import_base.isUntrustedObject)(awaitedFallback)) {
77
+ return getResume()(() => (0, import_base.renderUntrustedObject)(awaitedFallback));
82
78
  }
79
+ const fallbackResult = await getResume()(() => awaitedFallback.toString());
80
+ return (0, import_html.raw)(
81
+ fallbackResult,
82
+ fallbackResult.callbacks || awaitedFallback.callbacks
83
+ );
83
84
  })();
84
85
  const renderFallback = async (error) => {
85
86
  const fallbackStr = await resolveFallbackStr();
86
87
  return getResume()(async () => {
87
88
  onError?.(error);
88
89
  const fallbackRes = fallbackStr !== void 0 ? fallbackStr : fallbackRender && (0, import_base.jsx)(import_base.Fragment, {}, fallbackRender(error)) || "";
89
- const fallbackResString = await (0, import_base.Fragment)({
90
- children: fallbackRes
91
- }).toString();
90
+ const fallbackResString = await (0, import_base.Fragment)({ children: fallbackRes }).toString();
92
91
  return (0, import_html.raw)(
93
92
  fallbackResString,
94
93
  fallbackResString.callbacks || fallbackRes.callbacks
@@ -121,7 +120,7 @@ const ErrorBoundary = async ({ children, fallback, fallbackRender, onError }) =>
121
120
  const fallbackResString = await renderFallback(error2);
122
121
  const fallbackCallbacks = fallbackResString.callbacks;
123
122
  if (buffer) {
124
- buffer[0] = buffer[0].replace(replaceRe, fallbackResString);
123
+ buffer[0] = buffer[0].replace(replaceRe, () => fallbackResString);
125
124
  return fallbackCallbacks?.length ? (0, import_html.raw)("", fallbackCallbacks) : "";
126
125
  }
127
126
  return (0, import_html.raw)(
@@ -160,7 +159,7 @@ d.parentElement.insertBefore(c.content,d.nextSibling)
160
159
  </script>`;
161
160
  if (htmlArray.every((html2) => !html2.callbacks?.length)) {
162
161
  if (buffer) {
163
- buffer[0] = buffer[0].replace(replaceRe, content);
162
+ buffer[0] = buffer[0].replace(replaceRe, () => content);
164
163
  }
165
164
  return html;
166
165
  }
@@ -155,18 +155,18 @@ const createContext = (defaultValue) => {
155
155
  const context = ((props) => {
156
156
  const contextValues = getContextValuesIn(getCurrentStore(), context);
157
157
  contextValues.push(props.value);
158
- let string;
158
+ let rendered;
159
159
  try {
160
- string = props.children ? (Array.isArray(props.children) ? new import_base.JSXFragmentNode("", {}, props.children) : props.children).toString() : "";
160
+ rendered = typeof props.children === "string" ? (0, import_base.renderChildren)([props.children]) : (0, import_base.isUntrustedObject)(props.children) ? (0, import_base.renderUntrustedObject)(props.children) : props.children ? (Array.isArray(props.children) ? new import_base.JSXFragmentNode("", {}, props.children) : props.children).toString() : (0, import_html.raw)("");
161
161
  } catch (e) {
162
162
  contextValues.pop();
163
163
  throw e;
164
164
  }
165
- if (string instanceof Promise) {
166
- return string.finally(() => contextValues.pop()).then((resString) => (0, import_html.raw)(resString, resString.callbacks));
165
+ if (rendered instanceof Promise) {
166
+ return rendered.finally(() => contextValues.pop()).then((resString) => (0, import_html.raw)(resString, resString.callbacks));
167
167
  } else {
168
168
  contextValues.pop();
169
- return (0, import_html.raw)(string);
169
+ return (0, import_html.raw)(rendered);
170
170
  }
171
171
  });
172
172
  context.values = values;
@@ -33,13 +33,16 @@ __export(server_exports, {
33
33
  version: () => import__.default
34
34
  });
35
35
  module.exports = __toCommonJS(server_exports);
36
+ var import_base = require("../base");
36
37
  var import_streaming = require("../streaming");
37
38
  var import__ = __toESM(require("./"), 1);
39
+ const prepareRoot = (element) => typeof element === "string" || Array.isArray(element) ? (0, import_base.renderChildren)([element]) : element;
38
40
  const renderToString = (element, options = {}) => {
39
41
  if (Object.keys(options).length > 0) {
40
42
  console.warn("options are not supported yet");
41
43
  }
42
- const res = element?.toString() ?? "";
44
+ element = prepareRoot(element);
45
+ const res = element instanceof Promise ? element : element?.toString() ?? "";
43
46
  if (typeof res !== "string") {
44
47
  throw new Error("Async component is not supported in renderToString");
45
48
  }
@@ -49,6 +52,7 @@ const renderToReadableStream = async (element, options = {}) => {
49
52
  if (Object.keys(options).some((key) => key !== "onError")) {
50
53
  console.warn("options are not supported yet, except onError");
51
54
  }
55
+ element = prepareRoot(element);
52
56
  if (!element || typeof element !== "object") {
53
57
  element = element?.toString() ?? "";
54
58
  }
@@ -85,10 +85,10 @@ const insertIntoHead = (tagName, tag, props, precedence) => ({ buffer, context }
85
85
  insertTags.forEach((tag2) => {
86
86
  buffer[0] = buffer[0].replaceAll(tag2, "");
87
87
  });
88
- buffer[0] = buffer[0].replace(/(?=<\/head>)/, insertTags.join(""));
88
+ buffer[0] = buffer[0].replace(/(?=<\/head>)/, () => insertTags.join(""));
89
89
  }
90
90
  };
91
- const returnWithoutSpecialBehavior = (tag, children, props) => (0, import_html.raw)(new import_base.JSXNode(tag, props, (0, import_children.toArray)(children ?? [])).toString());
91
+ const returnWithoutSpecialBehavior = (tag, children, props) => (0, import_base.renderChildren)([new import_base.JSXNode(tag, props, (0, import_children.toArray)(children ?? []))]);
92
92
  const documentMetadataTag = (tag, children, props, sort) => {
93
93
  if ("itemProp" in props) {
94
94
  return returnWithoutSpecialBehavior(tag, children, props);
@@ -32,6 +32,7 @@ var import_components2 = require("./dom/components");
32
32
  var import_render = require("./dom/render");
33
33
  const StreamingContext = (0, import_context.createContext)(null);
34
34
  let suspenseCounter = 0;
35
+ const serializeBoundaryChild = (child) => typeof child === "string" || Array.isArray(child) ? (0, import_base.renderChildren)([child]) : child == null || typeof child === "boolean" ? "" : (0, import_base.isUntrustedObject)(child) ? (0, import_base.renderUntrustedObject)(child) : child.toString();
35
36
  const Suspense = async ({
36
37
  children,
37
38
  fallback
@@ -48,9 +49,7 @@ const Suspense = async ({
48
49
  try {
49
50
  stackNode[import_constants.DOM_STASH][0] = 0;
50
51
  import_render.buildDataStack.push([[], stackNode]);
51
- resArray = children.map(
52
- (c) => c == null || typeof c === "boolean" ? "" : c.toString()
53
- );
52
+ resArray = children.map(serializeBoundaryChild);
54
53
  } catch (e) {
55
54
  if (e instanceof Promise) {
56
55
  const resume = (0, import_context.captureRenderContext)();
@@ -71,7 +70,7 @@ const Suspense = async ({
71
70
  }
72
71
  if (resArray.some((res) => res instanceof Promise)) {
73
72
  const index = suspenseCounter++;
74
- const fallbackStr = await fallback.toString();
73
+ const fallbackStr = await (typeof fallback === "string" || Array.isArray(fallback) ? (0, import_base.renderChildren)([fallback]) : (0, import_base.isUntrustedObject)(fallback) ? (0, import_base.renderUntrustedObject)(fallback) : fallback.toString());
75
74
  return (0, import_html.raw)(`<template id="H:${index}"></template>${fallbackStr}<!--/$-->`, [
76
75
  ...fallbackStr.callbacks || [],
77
76
  ({ phase, buffer, context }) => {
@@ -84,7 +83,7 @@ const Suspense = async ({
84
83
  if (buffer) {
85
84
  buffer[0] = buffer[0].replace(
86
85
  new RegExp(`<template id="H:${index}"></template>.*?<!--/\\$-->`),
87
- content
86
+ () => content
88
87
  );
89
88
  }
90
89
  let html = buffer ? "" : `<template data-hono-target="H:${index}">${content}</template><script${nonce ? ` nonce="${nonce}"` : ""}>
package/dist/jsx/base.js CHANGED
@@ -70,7 +70,7 @@ var booleanAttributes = [
70
70
  "selected"
71
71
  ];
72
72
  var resolveFunctionComponentResult = (result, suspendedContext) => result.then((resolved) => {
73
- if (!Array.isArray(resolved) && !(resolved instanceof JSXNode)) {
73
+ if (typeof resolved !== "string" && !Array.isArray(resolved) && !(resolved instanceof JSXNode)) {
74
74
  return resolved;
75
75
  }
76
76
  const children = Array.isArray(resolved) ? resolved : [resolved];
@@ -108,6 +108,17 @@ var childrenToStringToBuffer = (children, buffer) => {
108
108
  }
109
109
  }
110
110
  };
111
+ var renderChildren = (children) => runWithRenderContext(() => {
112
+ const buffer = [""];
113
+ childrenToStringToBuffer(children, buffer);
114
+ return buffer.length === 1 ? raw(buffer[0], buffer.callbacks) : stringBufferToString(buffer, buffer.callbacks);
115
+ });
116
+ var isUntrustedObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof JSXNode) && !(value instanceof Promise) && !value.isEscaped && typeof value.toString === "function";
117
+ var renderUntrustedObject = (value) => {
118
+ const stringified = value.toString();
119
+ const escape = (result) => renderChildren([String(result)]);
120
+ return stringified instanceof Promise ? stringified.then(escape) : escape(stringified);
121
+ };
111
122
  var JSXNode = class {
112
123
  tag;
113
124
  props;
@@ -337,10 +348,13 @@ export {
337
348
  booleanAttributes,
338
349
  cloneElement,
339
350
  getNameSpaceContext,
351
+ isUntrustedObject,
340
352
  isValidElement,
341
353
  jsx,
342
354
  jsxFn,
343
355
  memo,
344
356
  reactAPICompatVersion,
357
+ renderChildren,
358
+ renderUntrustedObject,
345
359
  shallowEqual
346
360
  };
@@ -1,7 +1,7 @@
1
1
  // src/jsx/components.ts
2
2
  import { raw } from "../helper/html/index.js";
3
3
  import { HtmlEscapedCallbackPhase, resolveCallback } from "../utils/html.js";
4
- import { jsx, Fragment } from "./base.js";
4
+ import { jsx, Fragment, isUntrustedObject, renderChildren, renderUntrustedObject } from "./base.js";
5
5
  import { DOM_RENDERER } from "./constants.js";
6
6
  import { captureRenderContext, useContext } from "./context.js";
7
7
  import { ErrorBoundary as ErrorBoundaryDomRenderer } from "./dom/components.js";
@@ -9,7 +9,7 @@ import { StreamingContext } from "./streaming.js";
9
9
  var errorBoundaryCounter = 0;
10
10
  var childrenToString = async (children) => {
11
11
  try {
12
- return children.flat().map((c) => c == null || typeof c === "boolean" ? "" : c.toString());
12
+ return children.flat().map(resolveChildEarly);
13
13
  } catch (e) {
14
14
  if (e instanceof Promise) {
15
15
  const resume = captureRenderContext();
@@ -20,18 +20,16 @@ var childrenToString = async (children) => {
20
20
  }
21
21
  }
22
22
  };
23
- var resolveChildEarly = (c) => {
24
- if (c == null || typeof c === "boolean") {
23
+ var resolveChildEarly = (child) => {
24
+ if (child == null || typeof child === "boolean") {
25
25
  return "";
26
- } else if (typeof c === "string") {
27
- return c;
26
+ } else if (typeof child === "string" || Array.isArray(child)) {
27
+ return renderChildren([child]);
28
+ } else if (isUntrustedObject(child)) {
29
+ return renderUntrustedObject(child);
28
30
  } else {
29
- const str = c.toString();
30
- if (!(str instanceof Promise)) {
31
- return raw(str);
32
- } else {
33
- return str;
34
- }
31
+ const str = child.toString();
32
+ return str instanceof Promise ? str : raw(str);
35
33
  }
36
34
  };
37
35
  var ErrorBoundary = async ({ children, fallback, fallbackRender, onError }) => {
@@ -47,26 +45,27 @@ var ErrorBoundary = async ({ children, fallback, fallbackRender, onError }) => {
47
45
  let fallbackStrPromise;
48
46
  const resolveFallbackStr = () => fallbackStrPromise ||= (async () => {
49
47
  const awaitedFallback = await fallback;
50
- if (typeof awaitedFallback === "string") {
51
- return awaitedFallback;
52
- } else {
53
- const fallbackResult = await getResume()(() => awaitedFallback?.toString());
54
- if (typeof fallbackResult === "string") {
55
- return raw(
56
- fallbackResult,
57
- fallbackResult.callbacks || awaitedFallback?.callbacks
58
- );
59
- }
48
+ if (awaitedFallback === null || awaitedFallback === void 0) {
49
+ return;
50
+ }
51
+ if (typeof awaitedFallback === "string" || Array.isArray(awaitedFallback)) {
52
+ return getResume()(() => renderChildren([awaitedFallback]));
53
+ }
54
+ if (isUntrustedObject(awaitedFallback)) {
55
+ return getResume()(() => renderUntrustedObject(awaitedFallback));
60
56
  }
57
+ const fallbackResult = await getResume()(() => awaitedFallback.toString());
58
+ return raw(
59
+ fallbackResult,
60
+ fallbackResult.callbacks || awaitedFallback.callbacks
61
+ );
61
62
  })();
62
63
  const renderFallback = async (error) => {
63
64
  const fallbackStr = await resolveFallbackStr();
64
65
  return getResume()(async () => {
65
66
  onError?.(error);
66
67
  const fallbackRes = fallbackStr !== void 0 ? fallbackStr : fallbackRender && jsx(Fragment, {}, fallbackRender(error)) || "";
67
- const fallbackResString = await Fragment({
68
- children: fallbackRes
69
- }).toString();
68
+ const fallbackResString = await Fragment({ children: fallbackRes }).toString();
70
69
  return raw(
71
70
  fallbackResString,
72
71
  fallbackResString.callbacks || fallbackRes.callbacks
@@ -99,7 +98,7 @@ var ErrorBoundary = async ({ children, fallback, fallbackRender, onError }) => {
99
98
  const fallbackResString = await renderFallback(error2);
100
99
  const fallbackCallbacks = fallbackResString.callbacks;
101
100
  if (buffer) {
102
- buffer[0] = buffer[0].replace(replaceRe, fallbackResString);
101
+ buffer[0] = buffer[0].replace(replaceRe, () => fallbackResString);
103
102
  return fallbackCallbacks?.length ? raw("", fallbackCallbacks) : "";
104
103
  }
105
104
  return raw(
@@ -138,7 +137,7 @@ d.parentElement.insertBefore(c.content,d.nextSibling)
138
137
  </script>`;
139
138
  if (htmlArray.every((html2) => !html2.callbacks?.length)) {
140
139
  if (buffer) {
141
- buffer[0] = buffer[0].replace(replaceRe, content);
140
+ buffer[0] = buffer[0].replace(replaceRe, () => content);
142
141
  }
143
142
  return html;
144
143
  }
@@ -1,6 +1,6 @@
1
1
  // src/jsx/context.ts
2
2
  import { raw } from "../helper/html/index.js";
3
- import { JSXFragmentNode } from "./base.js";
3
+ import { isUntrustedObject, JSXFragmentNode, renderChildren, renderUntrustedObject } from "./base.js";
4
4
  import { DOM_RENDERER } from "./constants.js";
5
5
  import { createContextProviderFunction } from "./dom/context.js";
6
6
  var globalContexts = [];
@@ -130,18 +130,18 @@ var createContext = (defaultValue) => {
130
130
  const context = ((props) => {
131
131
  const contextValues = getContextValuesIn(getCurrentStore(), context);
132
132
  contextValues.push(props.value);
133
- let string;
133
+ let rendered;
134
134
  try {
135
- string = props.children ? (Array.isArray(props.children) ? new JSXFragmentNode("", {}, props.children) : props.children).toString() : "";
135
+ rendered = typeof props.children === "string" ? renderChildren([props.children]) : isUntrustedObject(props.children) ? renderUntrustedObject(props.children) : props.children ? (Array.isArray(props.children) ? new JSXFragmentNode("", {}, props.children) : props.children).toString() : raw("");
136
136
  } catch (e) {
137
137
  contextValues.pop();
138
138
  throw e;
139
139
  }
140
- if (string instanceof Promise) {
141
- return string.finally(() => contextValues.pop()).then((resString) => raw(resString, resString.callbacks));
140
+ if (rendered instanceof Promise) {
141
+ return rendered.finally(() => contextValues.pop()).then((resString) => raw(resString, resString.callbacks));
142
142
  } else {
143
143
  contextValues.pop();
144
- return raw(string);
144
+ return raw(rendered);
145
145
  }
146
146
  });
147
147
  context.values = values;
@@ -1,11 +1,14 @@
1
1
  // src/jsx/dom/server.ts
2
+ import { renderChildren } from "../base.js";
2
3
  import { renderToReadableStream as renderToReadableStreamHono } from "../streaming.js";
3
4
  import version from "./index.js";
5
+ var prepareRoot = (element) => typeof element === "string" || Array.isArray(element) ? renderChildren([element]) : element;
4
6
  var renderToString = (element, options = {}) => {
5
7
  if (Object.keys(options).length > 0) {
6
8
  console.warn("options are not supported yet");
7
9
  }
8
- const res = element?.toString() ?? "";
10
+ element = prepareRoot(element);
11
+ const res = element instanceof Promise ? element : element?.toString() ?? "";
9
12
  if (typeof res !== "string") {
10
13
  throw new Error("Async component is not supported in renderToString");
11
14
  }
@@ -15,6 +18,7 @@ var renderToReadableStream = async (element, options = {}) => {
15
18
  if (Object.keys(options).some((key) => key !== "onError")) {
16
19
  console.warn("options are not supported yet, except onError");
17
20
  }
21
+ element = prepareRoot(element);
18
22
  if (!element || typeof element !== "object") {
19
23
  element = element?.toString() ?? "";
20
24
  }
@@ -1,6 +1,6 @@
1
1
  // src/jsx/intrinsic-element/components.ts
2
2
  import { raw } from "../../helper/html/index.js";
3
- import { JSXNode, getNameSpaceContext } from "../base.js";
3
+ import { JSXNode, getNameSpaceContext, renderChildren } from "../base.js";
4
4
  import { toArray } from "../children.js";
5
5
  import { PERMALINK } from "../constants.js";
6
6
  import { useContext } from "../context.js";
@@ -62,10 +62,10 @@ var insertIntoHead = (tagName, tag, props, precedence) => ({ buffer, context })
62
62
  insertTags.forEach((tag2) => {
63
63
  buffer[0] = buffer[0].replaceAll(tag2, "");
64
64
  });
65
- buffer[0] = buffer[0].replace(/(?=<\/head>)/, insertTags.join(""));
65
+ buffer[0] = buffer[0].replace(/(?=<\/head>)/, () => insertTags.join(""));
66
66
  }
67
67
  };
68
- var returnWithoutSpecialBehavior = (tag, children, props) => raw(new JSXNode(tag, props, toArray(children ?? [])).toString());
68
+ var returnWithoutSpecialBehavior = (tag, children, props) => renderChildren([new JSXNode(tag, props, toArray(children ?? []))]);
69
69
  var documentMetadataTag = (tag, children, props, sort) => {
70
70
  if ("itemProp" in props) {
71
71
  return returnWithoutSpecialBehavior(tag, children, props);
@@ -1,7 +1,7 @@
1
1
  // src/jsx/streaming.ts
2
2
  import { raw } from "../helper/html/index.js";
3
3
  import { HtmlEscapedCallbackPhase, resolveCallback } from "../utils/html.js";
4
- import { JSXNode } from "./base.js";
4
+ import { isUntrustedObject, JSXNode, renderChildren, renderUntrustedObject } from "./base.js";
5
5
  import { childrenToString } from "./components.js";
6
6
  import { DOM_RENDERER, DOM_STASH } from "./constants.js";
7
7
  import { captureRenderContext, createContext, useContext } from "./context.js";
@@ -9,6 +9,7 @@ import { Suspense as SuspenseDomRenderer } from "./dom/components.js";
9
9
  import { buildDataStack } from "./dom/render.js";
10
10
  var StreamingContext = createContext(null);
11
11
  var suspenseCounter = 0;
12
+ var serializeBoundaryChild = (child) => typeof child === "string" || Array.isArray(child) ? renderChildren([child]) : child == null || typeof child === "boolean" ? "" : isUntrustedObject(child) ? renderUntrustedObject(child) : child.toString();
12
13
  var Suspense = async ({
13
14
  children,
14
15
  fallback
@@ -25,9 +26,7 @@ var Suspense = async ({
25
26
  try {
26
27
  stackNode[DOM_STASH][0] = 0;
27
28
  buildDataStack.push([[], stackNode]);
28
- resArray = children.map(
29
- (c) => c == null || typeof c === "boolean" ? "" : c.toString()
30
- );
29
+ resArray = children.map(serializeBoundaryChild);
31
30
  } catch (e) {
32
31
  if (e instanceof Promise) {
33
32
  const resume = captureRenderContext();
@@ -48,7 +47,7 @@ var Suspense = async ({
48
47
  }
49
48
  if (resArray.some((res) => res instanceof Promise)) {
50
49
  const index = suspenseCounter++;
51
- const fallbackStr = await fallback.toString();
50
+ const fallbackStr = await (typeof fallback === "string" || Array.isArray(fallback) ? renderChildren([fallback]) : isUntrustedObject(fallback) ? renderUntrustedObject(fallback) : fallback.toString());
52
51
  return raw(`<template id="H:${index}"></template>${fallbackStr}<!--/$-->`, [
53
52
  ...fallbackStr.callbacks || [],
54
53
  ({ phase, buffer, context }) => {
@@ -61,7 +60,7 @@ var Suspense = async ({
61
60
  if (buffer) {
62
61
  buffer[0] = buffer[0].replace(
63
62
  new RegExp(`<template id="H:${index}"></template>.*?<!--/\\$-->`),
64
- content
63
+ () => content
65
64
  );
66
65
  }
67
66
  let html = buffer ? "" : `<template data-hono-target="H:${index}">${content}</template><script${nonce ? ` nonce="${nonce}"` : ""}>
@@ -26,6 +26,9 @@ export declare namespace JSX {
26
26
  export declare const getNameSpaceContext: () => Context<string> | undefined;
27
27
  export declare const booleanAttributes: string[];
28
28
  export type Child = string | Promise<string> | number | JSXNode | null | undefined | boolean | Child[];
29
+ export declare const renderChildren: (children: Child[]) => HtmlEscapedString | Promise<HtmlEscapedString>;
30
+ export declare const isUntrustedObject: (value: unknown) => boolean;
31
+ export declare const renderUntrustedObject: (value: unknown) => HtmlEscapedString | Promise<HtmlEscapedString>;
29
32
  export declare class JSXNode implements HtmlEscaped {
30
33
  tag: string | Function;
31
34
  props: Props;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "4.13.6",
3
+ "version": "4.13.7",
4
4
  "description": "Web framework built on Web Standards",
5
5
  "main": "dist/cjs/index.js",
6
6
  "type": "module",