@cloudwerk/ui 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Squirrelsoft Dev Tools
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,270 @@
1
+ /**
2
+ * @cloudwerk/ui - Type Definitions
3
+ *
4
+ * Core types for the renderer abstraction layer.
5
+ */
6
+ /**
7
+ * Renderer implementation interface.
8
+ *
9
+ * Each renderer (hono-jsx, react, preact) implements this interface.
10
+ * The renderer is responsible for converting JSX elements to HTML responses.
11
+ */
12
+ interface Renderer {
13
+ /**
14
+ * Render a JSX element to an HTML Response.
15
+ *
16
+ * @param element - JSX element to render (type is unknown to support any JSX implementation)
17
+ * @param options - Render options
18
+ * @returns Response object (may be a Promise for async rendering)
19
+ */
20
+ render(element: unknown, options?: RenderOptions): Response | Promise<Response>;
21
+ /**
22
+ * Create an HTML Response from a raw string.
23
+ *
24
+ * Useful for static HTML, templates, or pre-rendered content.
25
+ *
26
+ * @param content - Raw HTML string
27
+ * @param options - HTML response options
28
+ * @returns Response object
29
+ */
30
+ html(content: string, options?: HtmlOptions): Response;
31
+ /**
32
+ * Hydrate a JSX element on the client.
33
+ *
34
+ * Only used for Client Components marked with 'use client'.
35
+ * This attaches event handlers to server-rendered HTML.
36
+ *
37
+ * @param element - JSX element to hydrate
38
+ * @param root - DOM element to hydrate into
39
+ */
40
+ hydrate(element: unknown, root: Element): void;
41
+ }
42
+ /**
43
+ * Options for rendering JSX elements.
44
+ */
45
+ interface RenderOptions {
46
+ /**
47
+ * HTTP status code for the response.
48
+ * @default 200
49
+ */
50
+ status?: number;
51
+ /**
52
+ * Additional response headers.
53
+ * These are merged with the default Content-Type header.
54
+ */
55
+ headers?: Record<string, string>;
56
+ /**
57
+ * Whether to include the <!DOCTYPE html> declaration.
58
+ * @default true
59
+ */
60
+ doctype?: boolean;
61
+ }
62
+ /**
63
+ * Options for creating HTML responses from raw strings.
64
+ */
65
+ interface HtmlOptions {
66
+ /**
67
+ * HTTP status code for the response.
68
+ * @default 200
69
+ */
70
+ status?: number;
71
+ /**
72
+ * Additional response headers.
73
+ * These are merged with the default Content-Type header.
74
+ */
75
+ headers?: Record<string, string>;
76
+ }
77
+ /**
78
+ * Props for components that receive children.
79
+ *
80
+ * Works with any JSX implementation (Hono JSX, React, Preact).
81
+ * The children type is `unknown` to allow any JSX element type.
82
+ *
83
+ * @example
84
+ * function Layout({ children }: PropsWithChildren) {
85
+ * return (
86
+ * <html>
87
+ * <body>{children}</body>
88
+ * </html>
89
+ * )
90
+ * }
91
+ */
92
+ interface PropsWithChildren<_P = unknown> {
93
+ children?: unknown;
94
+ }
95
+
96
+ /**
97
+ * @cloudwerk/ui - Renderer Selection
98
+ *
99
+ * Manages the active renderer and provides registration functionality.
100
+ */
101
+
102
+ /**
103
+ * Get the currently active renderer instance.
104
+ *
105
+ * @returns The active Renderer implementation
106
+ *
107
+ * @example
108
+ * const renderer = getActiveRenderer()
109
+ * const response = renderer.render(<App />)
110
+ */
111
+ declare function getActiveRenderer(): Renderer;
112
+ /**
113
+ * Get the name of the currently active renderer.
114
+ *
115
+ * @returns The renderer name (e.g., 'hono-jsx', 'react', 'preact')
116
+ *
117
+ * @example
118
+ * console.log(`Using ${getActiveRendererName()} renderer`)
119
+ */
120
+ declare function getActiveRendererName(): string;
121
+ /**
122
+ * Set the active renderer by name.
123
+ *
124
+ * Called during app initialization based on the `ui.renderer` config option.
125
+ * The renderer must be registered (either built-in or via registerRenderer).
126
+ *
127
+ * @param name - Renderer name from config (e.g., 'hono-jsx', 'react')
128
+ * @throws Error if renderer is not found
129
+ *
130
+ * @example
131
+ * // Initialize from config
132
+ * setActiveRenderer(config.ui?.renderer ?? 'hono-jsx')
133
+ */
134
+ declare function setActiveRenderer(name: string): void;
135
+ /**
136
+ * Register a custom renderer.
137
+ *
138
+ * Allows third-party renderer implementations to be used.
139
+ * The renderer must implement the Renderer interface.
140
+ *
141
+ * @param name - Unique name for the renderer
142
+ * @param renderer - Renderer implementation
143
+ * @throws Error if a renderer with that name is already registered
144
+ *
145
+ * @example
146
+ * // Register a custom renderer
147
+ * registerRenderer('solid', solidRenderer)
148
+ *
149
+ * // Then use it in config
150
+ * // ui: { renderer: 'solid' }
151
+ */
152
+ declare function registerRenderer(name: string, renderer: Renderer): void;
153
+ /**
154
+ * Get a list of all available renderer names.
155
+ *
156
+ * Useful for validation and error messages.
157
+ *
158
+ * @returns Array of registered renderer names
159
+ *
160
+ * @example
161
+ * const available = getAvailableRenderers()
162
+ * // ['hono-jsx']
163
+ */
164
+ declare function getAvailableRenderers(): string[];
165
+
166
+ /**
167
+ * @cloudwerk/ui - Hono JSX Renderer
168
+ *
169
+ * Default renderer implementation using Hono JSX.
170
+ * Hono JSX elements have a toString() method that renders them to HTML.
171
+ */
172
+
173
+ /**
174
+ * Hono JSX renderer implementation.
175
+ *
176
+ * Uses hono/jsx for server-side rendering. This is the default renderer
177
+ * for Cloudwerk applications.
178
+ *
179
+ * Features:
180
+ * - Synchronous rendering via toString()
181
+ * - Automatic doctype handling
182
+ * - Proper Content-Type headers
183
+ *
184
+ * Note: Streaming support via renderToReadableStream will be added in issue #38.
185
+ */
186
+ declare const honoJsxRenderer: Renderer;
187
+
188
+ /**
189
+ * @cloudwerk/ui
190
+ *
191
+ * UI rendering abstraction for Cloudwerk.
192
+ *
193
+ * This package provides a renderer-agnostic API for rendering JSX components
194
+ * to HTML responses. The default renderer uses Hono JSX, but custom renderers
195
+ * (React, Preact, etc.) can be registered and selected via configuration.
196
+ *
197
+ * @packageDocumentation
198
+ */
199
+
200
+ /**
201
+ * Render a JSX element to an HTML Response.
202
+ *
203
+ * Uses the currently active renderer (default: hono-jsx).
204
+ * Supports streaming for async components (via renderer implementation).
205
+ *
206
+ * @param element - JSX element to render
207
+ * @param options - Render options
208
+ * @returns Response object (may be Promise for async rendering)
209
+ *
210
+ * @example
211
+ * import { render } from '@cloudwerk/ui'
212
+ *
213
+ * // In a route handler
214
+ * app.get('/', (c) => {
215
+ * return render(<HomePage />)
216
+ * })
217
+ *
218
+ * @example
219
+ * // With options
220
+ * return render(<NotFoundPage />, { status: 404 })
221
+ *
222
+ * @example
223
+ * // With custom headers
224
+ * return render(<App />, {
225
+ * headers: { 'X-Custom-Header': 'value' }
226
+ * })
227
+ */
228
+ declare function render(element: unknown, options?: RenderOptions): Response | Promise<Response>;
229
+ /**
230
+ * Create an HTML Response from a raw string.
231
+ *
232
+ * Useful for static HTML, templates, or pre-rendered content
233
+ * that doesn't need to go through JSX rendering.
234
+ *
235
+ * @param content - Raw HTML string
236
+ * @param options - HTML response options
237
+ * @returns Response object
238
+ *
239
+ * @example
240
+ * import { html } from '@cloudwerk/ui'
241
+ *
242
+ * // Return static HTML
243
+ * return html('<!DOCTYPE html><html><body>Hello</body></html>')
244
+ *
245
+ * @example
246
+ * // With custom status
247
+ * return html('<html><body>Not Found</body></html>', { status: 404 })
248
+ */
249
+ declare function html(content: string, options?: HtmlOptions): Response;
250
+ /**
251
+ * Hydrate a JSX element on the client.
252
+ *
253
+ * Only used for Client Components marked with 'use client'.
254
+ * This attaches event handlers to server-rendered HTML.
255
+ *
256
+ * Note: This feature requires issue #39 to be implemented.
257
+ * Currently throws an informative error.
258
+ *
259
+ * @param element - JSX element to hydrate
260
+ * @param root - DOM element to hydrate into
261
+ *
262
+ * @example
263
+ * import { hydrate } from '@cloudwerk/ui'
264
+ *
265
+ * // Client-side entry point
266
+ * hydrate(<App />, document.getElementById('root')!)
267
+ */
268
+ declare function hydrate(element: unknown, root: Element): void;
269
+
270
+ export { type HtmlOptions, type PropsWithChildren, type RenderOptions, type Renderer, getActiveRenderer, getActiveRendererName, getAvailableRenderers, honoJsxRenderer, html, hydrate, registerRenderer, render, setActiveRenderer };
package/dist/index.js ADDED
@@ -0,0 +1,115 @@
1
+ // src/renderers/hono-jsx.ts
2
+ var honoJsxRenderer = {
3
+ /**
4
+ * Render a JSX element to an HTML Response.
5
+ *
6
+ * Hono JSX elements implement toString() for rendering to HTML strings.
7
+ * This method wraps the output in a proper Response object with headers.
8
+ *
9
+ * @param element - Hono JSX element (has toString() method)
10
+ * @param options - Render options
11
+ * @returns Response object with HTML content
12
+ */
13
+ render(element, options = {}) {
14
+ const { status = 200, headers = {}, doctype = true } = options;
15
+ const htmlString = String(element);
16
+ const body = doctype ? `<!DOCTYPE html>${htmlString}` : htmlString;
17
+ return new Response(body, {
18
+ status,
19
+ headers: {
20
+ "Content-Type": "text/html; charset=utf-8",
21
+ ...headers
22
+ }
23
+ });
24
+ },
25
+ /**
26
+ * Create an HTML Response from a raw string.
27
+ *
28
+ * Useful for static HTML, templates, or pre-rendered content
29
+ * that doesn't need to go through JSX rendering.
30
+ *
31
+ * @param content - Raw HTML string
32
+ * @param options - HTML response options
33
+ * @returns Response object with HTML content
34
+ */
35
+ html(content, options = {}) {
36
+ const { status = 200, headers = {} } = options;
37
+ return new Response(content, {
38
+ status,
39
+ headers: {
40
+ "Content-Type": "text/html; charset=utf-8",
41
+ ...headers
42
+ }
43
+ });
44
+ },
45
+ /**
46
+ * Hydrate a JSX element on the client.
47
+ *
48
+ * This is a placeholder that throws an informative error.
49
+ * Client-side hydration will be implemented in issue #39.
50
+ *
51
+ * @param _element - JSX element (unused)
52
+ * @param _root - DOM element (unused)
53
+ * @throws Error with information about when this feature will be available
54
+ */
55
+ hydrate(_element, _root) {
56
+ throw new Error(
57
+ "Client hydration requires hono/jsx/dom. This feature will be available after issue #39 is implemented."
58
+ );
59
+ }
60
+ };
61
+
62
+ // src/renderer.ts
63
+ var renderers = {
64
+ "hono-jsx": honoJsxRenderer
65
+ };
66
+ var activeRenderer = honoJsxRenderer;
67
+ var activeRendererName = "hono-jsx";
68
+ function getActiveRenderer() {
69
+ return activeRenderer;
70
+ }
71
+ function getActiveRendererName() {
72
+ return activeRendererName;
73
+ }
74
+ function setActiveRenderer(name) {
75
+ const renderer = renderers[name];
76
+ if (!renderer) {
77
+ const available = Object.keys(renderers).join(", ");
78
+ throw new Error(`Unknown renderer "${name}". Available renderers: ${available}`);
79
+ }
80
+ activeRenderer = renderer;
81
+ activeRendererName = name;
82
+ }
83
+ function registerRenderer(name, renderer) {
84
+ if (renderers[name]) {
85
+ throw new Error(
86
+ `Renderer "${name}" is already registered. Use a different name.`
87
+ );
88
+ }
89
+ renderers[name] = renderer;
90
+ }
91
+ function getAvailableRenderers() {
92
+ return Object.keys(renderers);
93
+ }
94
+
95
+ // src/index.ts
96
+ function render(element, options) {
97
+ return getActiveRenderer().render(element, options);
98
+ }
99
+ function html(content, options) {
100
+ return getActiveRenderer().html(content, options);
101
+ }
102
+ function hydrate(element, root) {
103
+ return getActiveRenderer().hydrate(element, root);
104
+ }
105
+ export {
106
+ getActiveRenderer,
107
+ getActiveRendererName,
108
+ getAvailableRenderers,
109
+ honoJsxRenderer,
110
+ html,
111
+ hydrate,
112
+ registerRenderer,
113
+ render,
114
+ setActiveRenderer
115
+ };
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@cloudwerk/ui",
3
+ "version": "0.1.0",
4
+ "description": "UI rendering abstraction for Cloudwerk",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/squirrelsoft-dev/cloudwerk.git",
8
+ "directory": "packages/ui"
9
+ },
10
+ "type": "module",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "dependencies": {},
21
+ "peerDependencies": {
22
+ "hono": "^4.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@vitest/coverage-v8": "^1.0.0",
26
+ "hono": "^4.7.4",
27
+ "tsup": "^8.0.0",
28
+ "typescript": "^5.0.0",
29
+ "vitest": "^1.0.0"
30
+ },
31
+ "scripts": {
32
+ "build": "tsup src/index.ts --format esm --dts",
33
+ "dev": "tsup src/index.ts --format esm --dts --watch",
34
+ "test": "vitest --run",
35
+ "test:watch": "vitest",
36
+ "test:coverage": "vitest --run --coverage",
37
+ "typecheck": "tsc --noEmit"
38
+ }
39
+ }