@cloudwerk/ui 0.1.1 → 0.3.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.d.ts +43 -1
- package/dist/index.js +45 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -74,6 +74,21 @@ interface HtmlOptions {
|
|
|
74
74
|
*/
|
|
75
75
|
headers?: Record<string, string>;
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Options for streaming render.
|
|
79
|
+
*/
|
|
80
|
+
interface StreamRenderOptions {
|
|
81
|
+
/**
|
|
82
|
+
* HTTP status code for the response.
|
|
83
|
+
* @default 200
|
|
84
|
+
*/
|
|
85
|
+
status?: number;
|
|
86
|
+
/**
|
|
87
|
+
* Additional response headers.
|
|
88
|
+
* These are merged with the default Content-Type and Transfer-Encoding headers.
|
|
89
|
+
*/
|
|
90
|
+
headers?: Record<string, string>;
|
|
91
|
+
}
|
|
77
92
|
/**
|
|
78
93
|
* Props for components that receive children.
|
|
79
94
|
*
|
|
@@ -190,6 +205,33 @@ declare function _resetRenderers(): void;
|
|
|
190
205
|
* Note: Streaming support via renderToReadableStream will be added in issue #38.
|
|
191
206
|
*/
|
|
192
207
|
declare const honoJsxRenderer: Renderer;
|
|
208
|
+
/**
|
|
209
|
+
* Create a streaming HTML Response that sends loading UI immediately,
|
|
210
|
+
* then streams the final content when the content promise resolves.
|
|
211
|
+
*
|
|
212
|
+
* This uses a chunked transfer encoding to send HTML in two parts:
|
|
213
|
+
* 1. Loading UI (sent immediately)
|
|
214
|
+
* 2. Final content with script to replace loading UI (sent when ready)
|
|
215
|
+
*
|
|
216
|
+
* Note: The innerHTML assignment in the client script is safe because we only
|
|
217
|
+
* use server-rendered content that we control. No user input is directly
|
|
218
|
+
* inserted into the HTML.
|
|
219
|
+
*
|
|
220
|
+
* @param loadingElement - Loading UI to show immediately (JSX element)
|
|
221
|
+
* @param contentPromise - Promise that resolves to final content (JSX element)
|
|
222
|
+
* @param options - Streaming render options
|
|
223
|
+
* @returns Response object with streaming HTML content
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* const loadingElement = <Loading params={{}} searchParams={{}} pathname="/dashboard" />
|
|
227
|
+
* const contentPromise = (async () => {
|
|
228
|
+
* const data = await loader()
|
|
229
|
+
* return <Page {...data} />
|
|
230
|
+
* })()
|
|
231
|
+
*
|
|
232
|
+
* return renderStream(loadingElement, contentPromise)
|
|
233
|
+
*/
|
|
234
|
+
declare function renderStream(loadingElement: unknown, contentPromise: Promise<unknown>, options?: StreamRenderOptions): Response;
|
|
193
235
|
|
|
194
236
|
/**
|
|
195
237
|
* @cloudwerk/ui
|
|
@@ -273,4 +315,4 @@ declare function html(content: string, options?: HtmlOptions): Response;
|
|
|
273
315
|
*/
|
|
274
316
|
declare function hydrate(element: unknown, root: Element): void;
|
|
275
317
|
|
|
276
|
-
export { type HtmlOptions, type PropsWithChildren, type RenderOptions, type Renderer, _resetRenderers, getActiveRenderer, getActiveRendererName, getAvailableRenderers, honoJsxRenderer, html, hydrate, registerRenderer, render, setActiveRenderer };
|
|
318
|
+
export { type HtmlOptions, type PropsWithChildren, type RenderOptions, type Renderer, type StreamRenderOptions, _resetRenderers, getActiveRenderer, getActiveRendererName, getAvailableRenderers, honoJsxRenderer, html, hydrate, registerRenderer, render, renderStream, setActiveRenderer };
|
package/dist/index.js
CHANGED
|
@@ -58,6 +58,50 @@ var honoJsxRenderer = {
|
|
|
58
58
|
);
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
|
+
function renderStream(loadingElement, contentPromise, options = {}) {
|
|
62
|
+
const { status = 200, headers = {} } = options;
|
|
63
|
+
const stream = new ReadableStream({
|
|
64
|
+
async start(controller) {
|
|
65
|
+
const encoder = new TextEncoder();
|
|
66
|
+
try {
|
|
67
|
+
const loadingHtml = String(loadingElement);
|
|
68
|
+
const loadingWrapper = `<!DOCTYPE html><div id="__cloudwerk_loading">${loadingHtml}</div>`;
|
|
69
|
+
controller.enqueue(encoder.encode(loadingWrapper));
|
|
70
|
+
const finalElement = await contentPromise;
|
|
71
|
+
const finalHtml = String(finalElement);
|
|
72
|
+
const replacementScript = `
|
|
73
|
+
<script>
|
|
74
|
+
(function() {
|
|
75
|
+
var loading = document.getElementById('__cloudwerk_loading');
|
|
76
|
+
if (loading) {
|
|
77
|
+
var content = document.getElementById('__cloudwerk_content');
|
|
78
|
+
if (content) {
|
|
79
|
+
document.body.innerHTML = content.innerHTML;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
</script>
|
|
84
|
+
<div id="__cloudwerk_content" style="display:none">${finalHtml}</div>
|
|
85
|
+
`;
|
|
86
|
+
controller.enqueue(encoder.encode(replacementScript));
|
|
87
|
+
controller.close();
|
|
88
|
+
} catch (error) {
|
|
89
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
90
|
+
const errorHtml = `<div style="color:red;padding:20px;">Error loading content: ${errorMessage}</div>`;
|
|
91
|
+
controller.enqueue(encoder.encode(errorHtml));
|
|
92
|
+
controller.close();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return new Response(stream, {
|
|
97
|
+
status,
|
|
98
|
+
headers: {
|
|
99
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
100
|
+
"Transfer-Encoding": "chunked",
|
|
101
|
+
...headers
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
61
105
|
|
|
62
106
|
// src/renderer.ts
|
|
63
107
|
var renderers = {
|
|
@@ -121,5 +165,6 @@ export {
|
|
|
121
165
|
hydrate,
|
|
122
166
|
registerRenderer,
|
|
123
167
|
render,
|
|
168
|
+
renderStream,
|
|
124
169
|
setActiveRenderer
|
|
125
170
|
};
|