@cloudwerk/ui 0.15.3 → 0.15.5

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.
@@ -0,0 +1,246 @@
1
+ // src/renderers/hono-jsx.ts
2
+ import { renderToReadableStream } from "hono/jsx/streaming";
3
+ import { jsx } from "hono/jsx/jsx-runtime";
4
+ var honoJsxRenderer = {
5
+ /**
6
+ * Render a JSX element to an HTML Response.
7
+ *
8
+ * Hono JSX elements implement toString() for rendering to HTML strings.
9
+ * This method wraps the output in a proper Response object with headers.
10
+ *
11
+ * @param element - Hono JSX element (has toString() method)
12
+ * @param options - Render options
13
+ * @returns Response object with HTML content
14
+ */
15
+ render(element, options = {}) {
16
+ const { status = 200, headers = {}, doctype = true } = options;
17
+ const htmlString = String(element);
18
+ const body = doctype ? `<!DOCTYPE html>${htmlString}` : htmlString;
19
+ return new Response(body, {
20
+ status,
21
+ headers: {
22
+ "Content-Type": "text/html; charset=utf-8",
23
+ ...headers
24
+ }
25
+ });
26
+ },
27
+ /**
28
+ * Create an HTML Response from a raw string.
29
+ *
30
+ * Useful for static HTML, templates, or pre-rendered content
31
+ * that doesn't need to go through JSX rendering.
32
+ *
33
+ * @param content - Raw HTML string
34
+ * @param options - HTML response options
35
+ * @returns Response object with HTML content
36
+ */
37
+ html(content, options = {}) {
38
+ const { status = 200, headers = {} } = options;
39
+ return new Response(content, {
40
+ status,
41
+ headers: {
42
+ "Content-Type": "text/html; charset=utf-8",
43
+ ...headers
44
+ }
45
+ });
46
+ },
47
+ /**
48
+ * Hydrate a JSX element on the client.
49
+ *
50
+ * Uses hono/jsx/dom render function to attach event handlers and state
51
+ * to server-rendered HTML. This is called by the client-side hydration
52
+ * bootstrap script for each Client Component.
53
+ *
54
+ * Note: This method is primarily used by the client-side hydration runtime.
55
+ * In server-side code, it will throw an error since the DOM is not available.
56
+ *
57
+ * @param element - JSX element to hydrate
58
+ * @param root - DOM element to hydrate into
59
+ * @throws Error if called in a non-browser environment
60
+ */
61
+ hydrate(element, root) {
62
+ if (typeof window === "undefined" || typeof document === "undefined") {
63
+ throw new Error(
64
+ "hydrate() can only be called in a browser environment. For server-side rendering, use render() instead."
65
+ );
66
+ }
67
+ import("hono/jsx/dom").then(({ render }) => {
68
+ render(element, root);
69
+ }).catch((error) => {
70
+ console.error("[Cloudwerk] Failed to hydrate component:", error);
71
+ });
72
+ },
73
+ createElement(type, props, ...children) {
74
+ return jsx(type, { ...props, children: children.length === 1 ? children[0] : children });
75
+ }
76
+ };
77
+ function renderStream(loadingElement, contentPromise, options = {}) {
78
+ const { status = 200, headers = {} } = options;
79
+ const stream = new ReadableStream({
80
+ async start(controller) {
81
+ const encoder = new TextEncoder();
82
+ try {
83
+ const loadingHtml = String(loadingElement);
84
+ const loadingWrapper = `<!DOCTYPE html><div id="__cloudwerk_loading">${loadingHtml}</div>`;
85
+ controller.enqueue(encoder.encode(loadingWrapper));
86
+ const finalElement = await contentPromise;
87
+ const finalHtml = String(finalElement);
88
+ const replacementScript = `
89
+ <script>
90
+ (function() {
91
+ var loading = document.getElementById('__cloudwerk_loading');
92
+ if (loading) {
93
+ var content = document.getElementById('__cloudwerk_content');
94
+ if (content) {
95
+ document.body.innerHTML = content.innerHTML;
96
+ }
97
+ }
98
+ })();
99
+ </script>
100
+ <div id="__cloudwerk_content" style="display:none">${finalHtml}</div>
101
+ `;
102
+ controller.enqueue(encoder.encode(replacementScript));
103
+ controller.close();
104
+ } catch (error) {
105
+ const errorMessage = error instanceof Error ? error.message : String(error);
106
+ const errorHtml = `<div style="color:red;padding:20px;">Error loading content: ${errorMessage}</div>`;
107
+ controller.enqueue(encoder.encode(errorHtml));
108
+ controller.close();
109
+ }
110
+ }
111
+ });
112
+ return new Response(stream, {
113
+ status,
114
+ headers: {
115
+ "Content-Type": "text/html; charset=utf-8",
116
+ "Transfer-Encoding": "chunked",
117
+ ...headers
118
+ }
119
+ });
120
+ }
121
+ function prependDoctype(stream) {
122
+ const encoder = new TextEncoder();
123
+ const doctypeBytes = encoder.encode("<!DOCTYPE html>");
124
+ let doctypeSent = false;
125
+ return stream.pipeThrough(
126
+ new TransformStream({
127
+ transform(chunk, controller) {
128
+ if (!doctypeSent) {
129
+ controller.enqueue(doctypeBytes);
130
+ doctypeSent = true;
131
+ }
132
+ controller.enqueue(chunk);
133
+ }
134
+ })
135
+ );
136
+ }
137
+ async function renderToStream(element, options = {}) {
138
+ const { status = 200, headers = {}, doctype = true } = options;
139
+ const contentStream = renderToReadableStream(element);
140
+ const stream = doctype ? prependDoctype(contentStream) : contentStream;
141
+ return new Response(stream, {
142
+ status,
143
+ headers: {
144
+ "Content-Type": "text/html; charset=utf-8",
145
+ ...headers
146
+ }
147
+ });
148
+ }
149
+
150
+ // src/renderer.ts
151
+ var renderers = {
152
+ "hono-jsx": honoJsxRenderer
153
+ };
154
+ var activeRenderer = honoJsxRenderer;
155
+ var activeRendererName = "hono-jsx";
156
+ function getActiveRenderer() {
157
+ return activeRenderer;
158
+ }
159
+ function getActiveRendererName() {
160
+ return activeRendererName;
161
+ }
162
+ async function initReactRenderer() {
163
+ if (renderers["react"]) {
164
+ return;
165
+ }
166
+ try {
167
+ const { reactRenderer } = await import("./react-FCDDATY5.js");
168
+ renderers["react"] = reactRenderer;
169
+ } catch (error) {
170
+ throw new Error(
171
+ `Failed to initialize React renderer. Make sure react and react-dom are installed: npm install react react-dom
172
+ Original error: ${error instanceof Error ? error.message : String(error)}`
173
+ );
174
+ }
175
+ }
176
+ function setActiveRenderer(name) {
177
+ const renderer = renderers[name];
178
+ if (!renderer) {
179
+ const available = Object.keys(renderers).join(", ");
180
+ if (name === "react") {
181
+ throw new Error(
182
+ `React renderer is not initialized. Call initReactRenderer() first, or install react and react-dom packages.`
183
+ );
184
+ }
185
+ throw new Error(`Unknown renderer "${name}". Available renderers: ${available}`);
186
+ }
187
+ activeRenderer = renderer;
188
+ activeRendererName = name;
189
+ }
190
+ function registerRenderer(name, renderer) {
191
+ if (renderers[name]) {
192
+ throw new Error(
193
+ `Renderer "${name}" is already registered. Use a different name.`
194
+ );
195
+ }
196
+ renderers[name] = renderer;
197
+ }
198
+ function getAvailableRenderers() {
199
+ return Object.keys(renderers);
200
+ }
201
+ function _resetRenderers() {
202
+ for (const name of Object.keys(renderers)) {
203
+ if (name !== "hono-jsx") {
204
+ delete renderers[name];
205
+ }
206
+ }
207
+ activeRenderer = honoJsxRenderer;
208
+ activeRendererName = "hono-jsx";
209
+ }
210
+
211
+ // src/clientWrapper.tsx
212
+ import { serializeProps } from "@cloudwerk/utils";
213
+ function createClientComponentWrapper(Component, meta) {
214
+ if (typeof window !== "undefined") {
215
+ return Component;
216
+ }
217
+ const { componentId, bundlePath } = meta;
218
+ return function WrappedClientComponent(props) {
219
+ const rendered = Component(props);
220
+ const serializedProps = serializeProps(props);
221
+ const renderer = getActiveRenderer();
222
+ return renderer.createElement(
223
+ "div",
224
+ {
225
+ "data-hydrate-id": componentId,
226
+ "data-hydrate-props": serializedProps,
227
+ "data-hydrate-bundle": bundlePath
228
+ },
229
+ rendered
230
+ );
231
+ };
232
+ }
233
+
234
+ export {
235
+ honoJsxRenderer,
236
+ renderStream,
237
+ renderToStream,
238
+ getActiveRenderer,
239
+ getActiveRendererName,
240
+ initReactRenderer,
241
+ setActiveRenderer,
242
+ registerRenderer,
243
+ getAvailableRenderers,
244
+ _resetRenderers,
245
+ createClientComponentWrapper
246
+ };
package/dist/client.d.ts CHANGED
@@ -3,6 +3,14 @@
3
3
  *
4
4
  * Wraps client components with hydration metadata for server-side rendering.
5
5
  * This wrapper is used by the esbuild plugin to transform imports of client components.
6
+ *
7
+ * IMPORTANT: This file must NOT use JSX syntax. The @cloudwerk/ui package is
8
+ * compiled with jsxImportSource: "hono/jsx", so any JSX here would produce
9
+ * Hono JSX elements. When the active renderer is React, those Hono elements
10
+ * would be nested inside a React element tree, causing "Objects are not valid
11
+ * as a React child" errors. Instead, we use the active renderer's createElement
12
+ * method at runtime to produce elements compatible with whichever JSX runtime
13
+ * is in use.
6
14
  */
7
15
  /**
8
16
  * Metadata for a wrapped client component.
package/dist/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createClientComponentWrapper
3
- } from "./chunk-A22YCEJM.js";
3
+ } from "./chunk-6324EM6Z.js";
4
4
  export {
5
5
  createClientComponentWrapper
6
6
  };
package/dist/index.d.ts CHANGED
@@ -41,6 +41,18 @@ interface Renderer {
41
41
  * @param root - DOM element to hydrate into
42
42
  */
43
43
  hydrate(element: unknown, root: Element): void;
44
+ /**
45
+ * Create a JSX element using the renderer's element factory.
46
+ *
47
+ * This is used by the client component wrapper to create hydration wrapper
48
+ * elements at runtime without baking in a specific JSX runtime at compile time.
49
+ *
50
+ * @param type - Element type (e.g., 'div', 'span')
51
+ * @param props - Element properties
52
+ * @param children - Child elements
53
+ * @returns A JSX element compatible with this renderer
54
+ */
55
+ createElement(type: string, props: Record<string, unknown>, ...children: unknown[]): unknown;
44
56
  }
45
57
  /**
46
58
  * Options for rendering JSX elements.
package/dist/index.js CHANGED
@@ -1,212 +1,16 @@
1
1
  import {
2
- createClientComponentWrapper
3
- } from "./chunk-A22YCEJM.js";
4
-
5
- // src/renderers/hono-jsx.ts
6
- import { renderToReadableStream } from "hono/jsx/streaming";
7
- var honoJsxRenderer = {
8
- /**
9
- * Render a JSX element to an HTML Response.
10
- *
11
- * Hono JSX elements implement toString() for rendering to HTML strings.
12
- * This method wraps the output in a proper Response object with headers.
13
- *
14
- * @param element - Hono JSX element (has toString() method)
15
- * @param options - Render options
16
- * @returns Response object with HTML content
17
- */
18
- render(element, options = {}) {
19
- const { status = 200, headers = {}, doctype = true } = options;
20
- const htmlString = String(element);
21
- const body = doctype ? `<!DOCTYPE html>${htmlString}` : htmlString;
22
- return new Response(body, {
23
- status,
24
- headers: {
25
- "Content-Type": "text/html; charset=utf-8",
26
- ...headers
27
- }
28
- });
29
- },
30
- /**
31
- * Create an HTML Response from a raw string.
32
- *
33
- * Useful for static HTML, templates, or pre-rendered content
34
- * that doesn't need to go through JSX rendering.
35
- *
36
- * @param content - Raw HTML string
37
- * @param options - HTML response options
38
- * @returns Response object with HTML content
39
- */
40
- html(content, options = {}) {
41
- const { status = 200, headers = {} } = options;
42
- return new Response(content, {
43
- status,
44
- headers: {
45
- "Content-Type": "text/html; charset=utf-8",
46
- ...headers
47
- }
48
- });
49
- },
50
- /**
51
- * Hydrate a JSX element on the client.
52
- *
53
- * Uses hono/jsx/dom render function to attach event handlers and state
54
- * to server-rendered HTML. This is called by the client-side hydration
55
- * bootstrap script for each Client Component.
56
- *
57
- * Note: This method is primarily used by the client-side hydration runtime.
58
- * In server-side code, it will throw an error since the DOM is not available.
59
- *
60
- * @param element - JSX element to hydrate
61
- * @param root - DOM element to hydrate into
62
- * @throws Error if called in a non-browser environment
63
- */
64
- hydrate(element, root) {
65
- if (typeof window === "undefined" || typeof document === "undefined") {
66
- throw new Error(
67
- "hydrate() can only be called in a browser environment. For server-side rendering, use render() instead."
68
- );
69
- }
70
- import("hono/jsx/dom").then(({ render: render2 }) => {
71
- render2(element, root);
72
- }).catch((error) => {
73
- console.error("[Cloudwerk] Failed to hydrate component:", error);
74
- });
75
- }
76
- };
77
- function renderStream(loadingElement, contentPromise, options = {}) {
78
- const { status = 200, headers = {} } = options;
79
- const stream = new ReadableStream({
80
- async start(controller) {
81
- const encoder = new TextEncoder();
82
- try {
83
- const loadingHtml = String(loadingElement);
84
- const loadingWrapper = `<!DOCTYPE html><div id="__cloudwerk_loading">${loadingHtml}</div>`;
85
- controller.enqueue(encoder.encode(loadingWrapper));
86
- const finalElement = await contentPromise;
87
- const finalHtml = String(finalElement);
88
- const replacementScript = `
89
- <script>
90
- (function() {
91
- var loading = document.getElementById('__cloudwerk_loading');
92
- if (loading) {
93
- var content = document.getElementById('__cloudwerk_content');
94
- if (content) {
95
- document.body.innerHTML = content.innerHTML;
96
- }
97
- }
98
- })();
99
- </script>
100
- <div id="__cloudwerk_content" style="display:none">${finalHtml}</div>
101
- `;
102
- controller.enqueue(encoder.encode(replacementScript));
103
- controller.close();
104
- } catch (error) {
105
- const errorMessage = error instanceof Error ? error.message : String(error);
106
- const errorHtml = `<div style="color:red;padding:20px;">Error loading content: ${errorMessage}</div>`;
107
- controller.enqueue(encoder.encode(errorHtml));
108
- controller.close();
109
- }
110
- }
111
- });
112
- return new Response(stream, {
113
- status,
114
- headers: {
115
- "Content-Type": "text/html; charset=utf-8",
116
- "Transfer-Encoding": "chunked",
117
- ...headers
118
- }
119
- });
120
- }
121
- function prependDoctype(stream) {
122
- const encoder = new TextEncoder();
123
- const doctypeBytes = encoder.encode("<!DOCTYPE html>");
124
- let doctypeSent = false;
125
- return stream.pipeThrough(
126
- new TransformStream({
127
- transform(chunk, controller) {
128
- if (!doctypeSent) {
129
- controller.enqueue(doctypeBytes);
130
- doctypeSent = true;
131
- }
132
- controller.enqueue(chunk);
133
- }
134
- })
135
- );
136
- }
137
- async function renderToStream(element, options = {}) {
138
- const { status = 200, headers = {}, doctype = true } = options;
139
- const contentStream = renderToReadableStream(element);
140
- const stream = doctype ? prependDoctype(contentStream) : contentStream;
141
- return new Response(stream, {
142
- status,
143
- headers: {
144
- "Content-Type": "text/html; charset=utf-8",
145
- ...headers
146
- }
147
- });
148
- }
149
-
150
- // src/renderer.ts
151
- var renderers = {
152
- "hono-jsx": honoJsxRenderer
153
- };
154
- var activeRenderer = honoJsxRenderer;
155
- var activeRendererName = "hono-jsx";
156
- function getActiveRenderer() {
157
- return activeRenderer;
158
- }
159
- function getActiveRendererName() {
160
- return activeRendererName;
161
- }
162
- async function initReactRenderer() {
163
- if (renderers["react"]) {
164
- return;
165
- }
166
- try {
167
- const { reactRenderer } = await import("./react-PVIKZSJC.js");
168
- renderers["react"] = reactRenderer;
169
- } catch (error) {
170
- throw new Error(
171
- `Failed to initialize React renderer. Make sure react and react-dom are installed: npm install react react-dom
172
- Original error: ${error instanceof Error ? error.message : String(error)}`
173
- );
174
- }
175
- }
176
- function setActiveRenderer(name) {
177
- const renderer = renderers[name];
178
- if (!renderer) {
179
- const available = Object.keys(renderers).join(", ");
180
- if (name === "react") {
181
- throw new Error(
182
- `React renderer is not initialized. Call initReactRenderer() first, or install react and react-dom packages.`
183
- );
184
- }
185
- throw new Error(`Unknown renderer "${name}". Available renderers: ${available}`);
186
- }
187
- activeRenderer = renderer;
188
- activeRendererName = name;
189
- }
190
- function registerRenderer(name, renderer) {
191
- if (renderers[name]) {
192
- throw new Error(
193
- `Renderer "${name}" is already registered. Use a different name.`
194
- );
195
- }
196
- renderers[name] = renderer;
197
- }
198
- function getAvailableRenderers() {
199
- return Object.keys(renderers);
200
- }
201
- function _resetRenderers() {
202
- for (const name of Object.keys(renderers)) {
203
- if (name !== "hono-jsx") {
204
- delete renderers[name];
205
- }
206
- }
207
- activeRenderer = honoJsxRenderer;
208
- activeRendererName = "hono-jsx";
209
- }
2
+ _resetRenderers,
3
+ createClientComponentWrapper,
4
+ getActiveRenderer,
5
+ getActiveRendererName,
6
+ getAvailableRenderers,
7
+ honoJsxRenderer,
8
+ initReactRenderer,
9
+ registerRenderer,
10
+ renderStream,
11
+ renderToStream,
12
+ setActiveRenderer
13
+ } from "./chunk-6324EM6Z.js";
210
14
 
211
15
  // src/hydration.ts
212
16
  import { serializeProps } from "@cloudwerk/core/build";
@@ -1,5 +1,6 @@
1
1
  // src/renderers/react.ts
2
2
  import { renderToString, renderToReadableStream } from "react-dom/server";
3
+ import { createElement } from "react";
3
4
  var reactRenderer = {
4
5
  /**
5
6
  * Render a React element to an HTML Response.
@@ -68,6 +69,9 @@ var reactRenderer = {
68
69
  }).catch((error) => {
69
70
  console.error("[Cloudwerk] Failed to hydrate React component:", error);
70
71
  });
72
+ },
73
+ createElement(type, props, ...children) {
74
+ return createElement(type, props, ...children);
71
75
  }
72
76
  };
73
77
  function prependDoctype(stream) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudwerk/ui",
3
- "version": "0.15.3",
3
+ "version": "0.15.5",
4
4
  "description": "UI rendering abstraction for Cloudwerk",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,26 +0,0 @@
1
- // src/clientWrapper.tsx
2
- import { serializeProps } from "@cloudwerk/utils";
3
- import { jsx } from "hono/jsx/jsx-runtime";
4
- function createClientComponentWrapper(Component, meta) {
5
- if (typeof window !== "undefined") {
6
- return Component;
7
- }
8
- const { componentId, bundlePath } = meta;
9
- return function WrappedClientComponent(props) {
10
- const rendered = Component(props);
11
- const serializedProps = serializeProps(props);
12
- return /* @__PURE__ */ jsx(
13
- "div",
14
- {
15
- "data-hydrate-id": componentId,
16
- "data-hydrate-props": serializedProps,
17
- "data-hydrate-bundle": bundlePath,
18
- children: rendered
19
- }
20
- );
21
- };
22
- }
23
-
24
- export {
25
- createClientComponentWrapper
26
- };