ai-props 2.3.0 → 2.4.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/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +9 -0
- package/dist/ai.d.ts +125 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +199 -0
- package/dist/ai.js.map +1 -0
- package/dist/cache.d.ts +66 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +183 -0
- package/dist/cache.js.map +1 -0
- package/dist/cascade.d.ts +329 -0
- package/dist/cascade.d.ts.map +1 -0
- package/dist/cascade.js +522 -0
- package/dist/cascade.js.map +1 -0
- package/dist/client.d.ts +233 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +191 -0
- package/dist/client.js.map +1 -0
- package/dist/durable-cascade.d.ts +280 -0
- package/dist/durable-cascade.d.ts.map +1 -0
- package/dist/durable-cascade.js +469 -0
- package/dist/durable-cascade.js.map +1 -0
- package/dist/event-bridge.d.ts +257 -0
- package/dist/event-bridge.d.ts.map +1 -0
- package/dist/event-bridge.js +317 -0
- package/dist/event-bridge.js.map +1 -0
- package/dist/generate.d.ts +69 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +227 -0
- package/dist/generate.js.map +1 -0
- package/dist/hoc.d.ts +164 -0
- package/dist/hoc.d.ts.map +1 -0
- package/dist/hoc.js +236 -0
- package/dist/hoc.js.map +1 -0
- package/dist/hono-jsx.d.ts +208 -0
- package/dist/hono-jsx.d.ts.map +1 -0
- package/dist/hono-jsx.js +459 -0
- package/dist/hono-jsx.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/mdx-types.d.ts +152 -0
- package/dist/mdx-types.d.ts.map +1 -0
- package/dist/mdx-types.js +9 -0
- package/dist/mdx-types.js.map +1 -0
- package/dist/mdx-utils.d.ts +106 -0
- package/dist/mdx-utils.d.ts.map +1 -0
- package/dist/mdx-utils.js +384 -0
- package/dist/mdx-utils.js.map +1 -0
- package/dist/mdx.d.ts +230 -0
- package/dist/mdx.d.ts.map +1 -0
- package/dist/mdx.js +820 -0
- package/dist/mdx.js.map +1 -0
- package/dist/rpc.d.ts +313 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +359 -0
- package/dist/rpc.js.map +1 -0
- package/dist/streaming.d.ts +199 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +402 -0
- package/dist/streaming.js.map +1 -0
- package/dist/types.d.ts +152 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +58 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +251 -0
- package/dist/validate.js.map +1 -0
- package/dist/worker.d.ts +270 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +405 -0
- package/dist/worker.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hono/jsx integration for AI-powered props with hydration and streaming
|
|
3
|
+
*
|
|
4
|
+
* This module provides:
|
|
5
|
+
* - Hydration data collection during server render
|
|
6
|
+
* - Streaming render support with hono/jsx
|
|
7
|
+
* - AI-powered component prop generation
|
|
8
|
+
* - Context-aware rendering with Suspense support
|
|
9
|
+
*
|
|
10
|
+
* Bead: aip-fxpy (tests), aip-z57t (implementation)
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Hydration data structure collected during render
|
|
16
|
+
*/
|
|
17
|
+
export interface HydrationData {
|
|
18
|
+
/** Map of component ID to props used during render */
|
|
19
|
+
components: Map<string, Record<string, unknown>>;
|
|
20
|
+
/** Component hierarchy tree */
|
|
21
|
+
tree: HydrationNode[];
|
|
22
|
+
/** Serialize to JSON string */
|
|
23
|
+
toJSON(): string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Node in the component hydration tree
|
|
27
|
+
*/
|
|
28
|
+
export interface HydrationNode {
|
|
29
|
+
id: string;
|
|
30
|
+
component: string;
|
|
31
|
+
props: Record<string, unknown>;
|
|
32
|
+
children: HydrationNode[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Context for hydration tracking
|
|
36
|
+
*/
|
|
37
|
+
export interface HydrationContext {
|
|
38
|
+
/** Register a component render with props */
|
|
39
|
+
register(componentName: string, props: Record<string, unknown>): string;
|
|
40
|
+
/** Get collected hydration data */
|
|
41
|
+
getData(): HydrationData;
|
|
42
|
+
/** Clear collected data */
|
|
43
|
+
clear(): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Options for streaming render
|
|
47
|
+
*/
|
|
48
|
+
export interface StreamingOptions {
|
|
49
|
+
/** Include hydration data in stream */
|
|
50
|
+
includeHydration?: boolean;
|
|
51
|
+
/** Suspense configuration */
|
|
52
|
+
suspense?: {
|
|
53
|
+
fallback: string;
|
|
54
|
+
};
|
|
55
|
+
/** Error handler */
|
|
56
|
+
onError?: (error: Error) => string;
|
|
57
|
+
/** Enable progressive enhancement */
|
|
58
|
+
progressive?: boolean;
|
|
59
|
+
/** Custom headers for response */
|
|
60
|
+
headers?: Record<string, string>;
|
|
61
|
+
/** Enable streaming mode */
|
|
62
|
+
streaming?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Options for streaming renderer
|
|
66
|
+
*/
|
|
67
|
+
export interface StreamingRendererOptions {
|
|
68
|
+
/** DOCTYPE to prepend */
|
|
69
|
+
doctype?: string;
|
|
70
|
+
/** Shell wrapper function */
|
|
71
|
+
shell?: (content: string, hydration?: string) => string;
|
|
72
|
+
/** Include hydration data */
|
|
73
|
+
includeHydration?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Props for AI component creation
|
|
77
|
+
*/
|
|
78
|
+
export interface AIComponentProps<P = Record<string, unknown>> {
|
|
79
|
+
/** Component name */
|
|
80
|
+
name: string;
|
|
81
|
+
/** Schema for AI prop generation */
|
|
82
|
+
schema: Record<string, string>;
|
|
83
|
+
/** Render function */
|
|
84
|
+
render: (props: P) => string | Promise<string>;
|
|
85
|
+
/** Fallback props on error */
|
|
86
|
+
fallback?: Partial<P>;
|
|
87
|
+
/** Enable progressive enhancement */
|
|
88
|
+
progressive?: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Props for withAIProps wrapper
|
|
92
|
+
*/
|
|
93
|
+
export interface WithAIPropsOptions {
|
|
94
|
+
/** Schema for AI prop generation */
|
|
95
|
+
schema: Record<string, string>;
|
|
96
|
+
/** Fallback props on error */
|
|
97
|
+
fallback?: Record<string, unknown>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Props for AIPropsProvider
|
|
101
|
+
*/
|
|
102
|
+
export interface AIPropsProviderProps {
|
|
103
|
+
/** AI props configuration */
|
|
104
|
+
config: {
|
|
105
|
+
model?: string;
|
|
106
|
+
cache?: boolean;
|
|
107
|
+
system?: string;
|
|
108
|
+
};
|
|
109
|
+
/** Children to render */
|
|
110
|
+
children: unknown;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Component type with schema attached
|
|
114
|
+
*/
|
|
115
|
+
export interface AIComponentFunction<P = Record<string, unknown>> {
|
|
116
|
+
(props: Partial<P> & {
|
|
117
|
+
context?: Record<string, unknown>;
|
|
118
|
+
}): Promise<string>;
|
|
119
|
+
schema: Record<string, string>;
|
|
120
|
+
displayName?: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Collect hydration data from a component render
|
|
124
|
+
*
|
|
125
|
+
* @param component - Component function to render
|
|
126
|
+
* @param props - Props to pass to component
|
|
127
|
+
* @returns Hydration data collected during render
|
|
128
|
+
*/
|
|
129
|
+
export declare function collectHydrationData<P>(component: (props: P) => string | Promise<string>, props: P): Promise<HydrationData>;
|
|
130
|
+
/**
|
|
131
|
+
* Create a hydration context for tracking component renders
|
|
132
|
+
*
|
|
133
|
+
* @returns New hydration context
|
|
134
|
+
*/
|
|
135
|
+
export declare function createHydrationContext(): HydrationContext;
|
|
136
|
+
/**
|
|
137
|
+
* Serialize hydration data to JSON string
|
|
138
|
+
*
|
|
139
|
+
* @param data - Hydration data to serialize
|
|
140
|
+
* @returns JSON string safe for script embedding
|
|
141
|
+
*/
|
|
142
|
+
export declare function serializeHydrationData(data: HydrationData): string;
|
|
143
|
+
/**
|
|
144
|
+
* Provider component that enables hydration tracking for children
|
|
145
|
+
*
|
|
146
|
+
* @param props - Provider props with context and children
|
|
147
|
+
* @returns Rendered output with hydration tracking
|
|
148
|
+
*/
|
|
149
|
+
export declare function HydrationProvider(props: {
|
|
150
|
+
context: HydrationContext;
|
|
151
|
+
children: unknown;
|
|
152
|
+
}): unknown;
|
|
153
|
+
/**
|
|
154
|
+
* Hook to access hydration context from within a component
|
|
155
|
+
*
|
|
156
|
+
* @returns Current hydration context
|
|
157
|
+
*/
|
|
158
|
+
export declare function useHydration(): HydrationContext;
|
|
159
|
+
/**
|
|
160
|
+
* Render a component to a ReadableStream
|
|
161
|
+
*
|
|
162
|
+
* @param component - Component function to render
|
|
163
|
+
* @param props - Props to pass to component
|
|
164
|
+
* @param options - Streaming options
|
|
165
|
+
* @returns ReadableStream of rendered HTML
|
|
166
|
+
*/
|
|
167
|
+
export declare function renderToReadableStream<P>(component: (props: P) => string | Promise<string>, props: P, options?: StreamingOptions): Promise<ReadableStream<Uint8Array>>;
|
|
168
|
+
/**
|
|
169
|
+
* Create a streaming Response from a component
|
|
170
|
+
*
|
|
171
|
+
* @param component - Component function to render
|
|
172
|
+
* @param props - Props to pass to component
|
|
173
|
+
* @param options - Streaming options
|
|
174
|
+
* @returns Response with streaming body
|
|
175
|
+
*/
|
|
176
|
+
export declare function streamJSXResponse<P>(component: (props: P) => string | Promise<string>, props: P, options?: StreamingOptions): Promise<Response>;
|
|
177
|
+
/**
|
|
178
|
+
* Create a reusable streaming renderer with configuration
|
|
179
|
+
*
|
|
180
|
+
* @param options - Renderer options
|
|
181
|
+
* @returns Streaming renderer instance
|
|
182
|
+
*/
|
|
183
|
+
export declare function createStreamingRenderer(options: StreamingRendererOptions): {
|
|
184
|
+
render: <P>(component: (props: P) => string | Promise<string>, props: P) => Promise<ReadableStream<Uint8Array>>;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Create a component with AI-powered prop generation
|
|
188
|
+
*
|
|
189
|
+
* @param options - Component options including schema and render function
|
|
190
|
+
* @returns AI-enhanced component function
|
|
191
|
+
*/
|
|
192
|
+
export declare function createAIComponent<P extends Record<string, unknown>>(options: AIComponentProps<P>): AIComponentFunction<P>;
|
|
193
|
+
/**
|
|
194
|
+
* Wrap an existing component with AI prop generation
|
|
195
|
+
*
|
|
196
|
+
* @param component - Component function to wrap
|
|
197
|
+
* @param options - AI props options
|
|
198
|
+
* @returns Wrapped component with AI props
|
|
199
|
+
*/
|
|
200
|
+
export declare function withAIProps<P extends Record<string, unknown>>(component: (props: P) => string | Promise<string>, options: WithAIPropsOptions): AIComponentFunction<P>;
|
|
201
|
+
/**
|
|
202
|
+
* Provider component for AI props configuration
|
|
203
|
+
*
|
|
204
|
+
* @param props - Provider props with config and children
|
|
205
|
+
* @returns Rendered output with AI props context
|
|
206
|
+
*/
|
|
207
|
+
export declare function AIPropsProvider(props: AIPropsProviderProps): unknown;
|
|
208
|
+
//# sourceMappingURL=hono-jsx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono-jsx.d.ts","sourceRoot":"","sources":["../src/hono-jsx.ts"],"names":[],"mappings":"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"}
|
package/dist/hono-jsx.js
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hono/jsx integration for AI-powered props with hydration and streaming
|
|
3
|
+
*
|
|
4
|
+
* This module provides:
|
|
5
|
+
* - Hydration data collection during server render
|
|
6
|
+
* - Streaming render support with hono/jsx
|
|
7
|
+
* - AI-powered component prop generation
|
|
8
|
+
* - Context-aware rendering with Suspense support
|
|
9
|
+
*
|
|
10
|
+
* Bead: aip-fxpy (tests), aip-z57t (implementation)
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
import { generateProps, mergeWithGenerated } from './generate.js';
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Global hydration context (for useHydration hook simulation)
|
|
17
|
+
// ============================================================================
|
|
18
|
+
let globalHydrationContext = null;
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Hydration Data Collection
|
|
21
|
+
// ============================================================================
|
|
22
|
+
/**
|
|
23
|
+
* Create hydration data with the toJSON method
|
|
24
|
+
*/
|
|
25
|
+
function createHydrationData(components, tree) {
|
|
26
|
+
return {
|
|
27
|
+
components,
|
|
28
|
+
tree,
|
|
29
|
+
toJSON() {
|
|
30
|
+
return serializeHydrationData(this);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Collect hydration data from a component render
|
|
36
|
+
*
|
|
37
|
+
* @param component - Component function to render
|
|
38
|
+
* @param props - Props to pass to component
|
|
39
|
+
* @returns Hydration data collected during render
|
|
40
|
+
*/
|
|
41
|
+
export async function collectHydrationData(component, props) {
|
|
42
|
+
// Create a hydration context
|
|
43
|
+
const ctx = createHydrationContext();
|
|
44
|
+
// Store global context for nested components
|
|
45
|
+
const prevContext = globalHydrationContext;
|
|
46
|
+
globalHydrationContext = ctx;
|
|
47
|
+
try {
|
|
48
|
+
// Get component name
|
|
49
|
+
const componentName = component.name || 'Anonymous';
|
|
50
|
+
// Register the component
|
|
51
|
+
ctx.register(componentName, props);
|
|
52
|
+
// Render the component (this might trigger nested registrations)
|
|
53
|
+
await component(props);
|
|
54
|
+
// Return collected data
|
|
55
|
+
return ctx.getData();
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
// Restore previous context
|
|
59
|
+
globalHydrationContext = prevContext;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Create a hydration context for tracking component renders
|
|
64
|
+
*
|
|
65
|
+
* @returns New hydration context
|
|
66
|
+
*/
|
|
67
|
+
export function createHydrationContext() {
|
|
68
|
+
const components = new Map();
|
|
69
|
+
const tree = [];
|
|
70
|
+
let idCounter = 0;
|
|
71
|
+
return {
|
|
72
|
+
register(componentName, props) {
|
|
73
|
+
const id = `${componentName}-${idCounter++}`;
|
|
74
|
+
components.set(componentName, props);
|
|
75
|
+
const node = {
|
|
76
|
+
id,
|
|
77
|
+
component: componentName,
|
|
78
|
+
props,
|
|
79
|
+
children: [],
|
|
80
|
+
};
|
|
81
|
+
tree.push(node);
|
|
82
|
+
return id;
|
|
83
|
+
},
|
|
84
|
+
getData() {
|
|
85
|
+
return createHydrationData(new Map(components), [...tree]);
|
|
86
|
+
},
|
|
87
|
+
clear() {
|
|
88
|
+
components.clear();
|
|
89
|
+
tree.length = 0;
|
|
90
|
+
idCounter = 0;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Serialize hydration data to JSON string
|
|
96
|
+
*
|
|
97
|
+
* @param data - Hydration data to serialize
|
|
98
|
+
* @returns JSON string safe for script embedding
|
|
99
|
+
*/
|
|
100
|
+
export function serializeHydrationData(data) {
|
|
101
|
+
// Convert Map to object for serialization
|
|
102
|
+
const componentsObj = {};
|
|
103
|
+
// Use a WeakSet to detect circular references
|
|
104
|
+
const seen = new WeakSet();
|
|
105
|
+
function sanitizeValue(value) {
|
|
106
|
+
if (value === null || value === undefined) {
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
if (typeof value === 'object') {
|
|
110
|
+
if (seen.has(value)) {
|
|
111
|
+
return '[Circular]';
|
|
112
|
+
}
|
|
113
|
+
seen.add(value);
|
|
114
|
+
if (Array.isArray(value)) {
|
|
115
|
+
return value.map(sanitizeValue);
|
|
116
|
+
}
|
|
117
|
+
const result = {};
|
|
118
|
+
for (const [k, v] of Object.entries(value)) {
|
|
119
|
+
result[k] = sanitizeValue(v);
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
for (const [key, value] of data.components) {
|
|
126
|
+
componentsObj[key] = sanitizeValue(value);
|
|
127
|
+
}
|
|
128
|
+
const serializable = {
|
|
129
|
+
components: componentsObj,
|
|
130
|
+
tree: data.tree.map((node) => ({
|
|
131
|
+
id: node.id,
|
|
132
|
+
component: node.component,
|
|
133
|
+
props: sanitizeValue(node.props),
|
|
134
|
+
children: node.children,
|
|
135
|
+
})),
|
|
136
|
+
};
|
|
137
|
+
let json = JSON.stringify(serializable);
|
|
138
|
+
// Escape script tags to prevent XSS
|
|
139
|
+
json = json.replace(/<script/gi, '\\u003cscript');
|
|
140
|
+
json = json.replace(/<\/script/gi, '\\u003c/script');
|
|
141
|
+
return json;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Provider component that enables hydration tracking for children
|
|
145
|
+
*
|
|
146
|
+
* @param props - Provider props with context and children
|
|
147
|
+
* @returns Rendered output with hydration tracking
|
|
148
|
+
*/
|
|
149
|
+
export function HydrationProvider(props) {
|
|
150
|
+
// Store context globally for nested components
|
|
151
|
+
const prevContext = globalHydrationContext;
|
|
152
|
+
globalHydrationContext = props.context;
|
|
153
|
+
try {
|
|
154
|
+
// Return children - in a real JSX environment this would render them
|
|
155
|
+
return props.children;
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
// Note: In real JSX, we'd restore on unmount, not here
|
|
159
|
+
// For our purposes, we keep it set for the duration
|
|
160
|
+
globalHydrationContext = prevContext;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Hook to access hydration context from within a component
|
|
165
|
+
*
|
|
166
|
+
* @returns Current hydration context
|
|
167
|
+
*/
|
|
168
|
+
export function useHydration() {
|
|
169
|
+
if (!globalHydrationContext) {
|
|
170
|
+
// Return a no-op context if not in a HydrationProvider
|
|
171
|
+
return createHydrationContext();
|
|
172
|
+
}
|
|
173
|
+
return globalHydrationContext;
|
|
174
|
+
}
|
|
175
|
+
// ============================================================================
|
|
176
|
+
// Streaming Render
|
|
177
|
+
// ============================================================================
|
|
178
|
+
/**
|
|
179
|
+
* Render a component to a ReadableStream
|
|
180
|
+
*
|
|
181
|
+
* @param component - Component function to render
|
|
182
|
+
* @param props - Props to pass to component
|
|
183
|
+
* @param options - Streaming options
|
|
184
|
+
* @returns ReadableStream of rendered HTML
|
|
185
|
+
*/
|
|
186
|
+
export async function renderToReadableStream(component, props, options) {
|
|
187
|
+
const encoder = new TextEncoder();
|
|
188
|
+
return new ReadableStream({
|
|
189
|
+
async start(controller) {
|
|
190
|
+
try {
|
|
191
|
+
// Create hydration context if needed
|
|
192
|
+
const ctx = options?.includeHydration ? createHydrationContext() : null;
|
|
193
|
+
if (ctx) {
|
|
194
|
+
globalHydrationContext = ctx;
|
|
195
|
+
}
|
|
196
|
+
// Render the component
|
|
197
|
+
let content;
|
|
198
|
+
try {
|
|
199
|
+
content = await component(props);
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
if (options?.onError && error instanceof Error) {
|
|
203
|
+
content = options.onError(error);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Enqueue the content in chunks for streaming behavior
|
|
210
|
+
const chunkSize = 1024;
|
|
211
|
+
for (let i = 0; i < content.length; i += chunkSize) {
|
|
212
|
+
const chunk = content.slice(i, i + chunkSize);
|
|
213
|
+
controller.enqueue(encoder.encode(chunk));
|
|
214
|
+
}
|
|
215
|
+
// Add hydration script if requested
|
|
216
|
+
if (options?.includeHydration && ctx) {
|
|
217
|
+
const componentName = component.name || 'Anonymous';
|
|
218
|
+
ctx.register(componentName, props);
|
|
219
|
+
const hydrationData = ctx.getData();
|
|
220
|
+
const hydrationScript = `<script>window.__HYDRATION_DATA__=${serializeHydrationData(hydrationData)}</script>`;
|
|
221
|
+
controller.enqueue(encoder.encode(hydrationScript));
|
|
222
|
+
}
|
|
223
|
+
controller.close();
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
controller.error(error);
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
globalHydrationContext = null;
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Create a streaming Response from a component
|
|
236
|
+
*
|
|
237
|
+
* @param component - Component function to render
|
|
238
|
+
* @param props - Props to pass to component
|
|
239
|
+
* @param options - Streaming options
|
|
240
|
+
* @returns Response with streaming body
|
|
241
|
+
*/
|
|
242
|
+
export async function streamJSXResponse(component, props, options) {
|
|
243
|
+
const stream = await renderToReadableStream(component, props, options);
|
|
244
|
+
const headers = new Headers({
|
|
245
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
246
|
+
...options?.headers,
|
|
247
|
+
});
|
|
248
|
+
// Don't set Content-Length for streaming responses
|
|
249
|
+
if (options?.streaming) {
|
|
250
|
+
// Transfer-Encoding is typically set automatically by the runtime
|
|
251
|
+
}
|
|
252
|
+
return new Response(stream, {
|
|
253
|
+
status: 200,
|
|
254
|
+
headers,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Create a reusable streaming renderer with configuration
|
|
259
|
+
*
|
|
260
|
+
* @param options - Renderer options
|
|
261
|
+
* @returns Streaming renderer instance
|
|
262
|
+
*/
|
|
263
|
+
export function createStreamingRenderer(options) {
|
|
264
|
+
return {
|
|
265
|
+
async render(component, props) {
|
|
266
|
+
const encoder = new TextEncoder();
|
|
267
|
+
return new ReadableStream({
|
|
268
|
+
async start(controller) {
|
|
269
|
+
try {
|
|
270
|
+
// Create hydration context
|
|
271
|
+
const ctx = options.includeHydration ? createHydrationContext() : null;
|
|
272
|
+
if (ctx) {
|
|
273
|
+
globalHydrationContext = ctx;
|
|
274
|
+
}
|
|
275
|
+
// Render the component
|
|
276
|
+
const content = await component(props);
|
|
277
|
+
// Register for hydration data
|
|
278
|
+
if (ctx) {
|
|
279
|
+
const componentName = component.name || 'Anonymous';
|
|
280
|
+
ctx.register(componentName, props);
|
|
281
|
+
}
|
|
282
|
+
// Get hydration data
|
|
283
|
+
let hydrationJson = '';
|
|
284
|
+
if (options.includeHydration && ctx) {
|
|
285
|
+
hydrationJson = serializeHydrationData(ctx.getData());
|
|
286
|
+
}
|
|
287
|
+
// Apply shell wrapper if provided
|
|
288
|
+
let output;
|
|
289
|
+
if (options.shell) {
|
|
290
|
+
// When using shell, pass hydration data with __HYDRATION_DATA__ prefix
|
|
291
|
+
// so the shell can embed it directly in a script tag
|
|
292
|
+
const hydrationScript = hydrationJson
|
|
293
|
+
? `window.__HYDRATION_DATA__=${hydrationJson}`
|
|
294
|
+
: '';
|
|
295
|
+
output = options.shell(content, hydrationScript);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
output = content;
|
|
299
|
+
if (options.includeHydration && hydrationJson) {
|
|
300
|
+
output += `<script>window.__HYDRATION_DATA__=${hydrationJson}</script>`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Prepend doctype if provided
|
|
304
|
+
if (options.doctype) {
|
|
305
|
+
output = options.doctype + '\n' + output;
|
|
306
|
+
}
|
|
307
|
+
// Stream in chunks
|
|
308
|
+
const chunkSize = 1024;
|
|
309
|
+
for (let i = 0; i < output.length; i += chunkSize) {
|
|
310
|
+
const chunk = output.slice(i, i + chunkSize);
|
|
311
|
+
controller.enqueue(encoder.encode(chunk));
|
|
312
|
+
}
|
|
313
|
+
controller.close();
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
controller.error(error);
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
globalHydrationContext = null;
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
// ============================================================================
|
|
327
|
+
// AI Component Creation
|
|
328
|
+
// ============================================================================
|
|
329
|
+
/**
|
|
330
|
+
* Create a component with AI-powered prop generation
|
|
331
|
+
*
|
|
332
|
+
* @param options - Component options including schema and render function
|
|
333
|
+
* @returns AI-enhanced component function
|
|
334
|
+
*/
|
|
335
|
+
export function createAIComponent(options) {
|
|
336
|
+
const { name, schema, render, fallback, progressive } = options;
|
|
337
|
+
const aiComponent = async (props) => {
|
|
338
|
+
const { context, ...partialProps } = props;
|
|
339
|
+
// Check which props are missing
|
|
340
|
+
const schemaKeys = Object.keys(schema);
|
|
341
|
+
const providedKeys = Object.keys(partialProps);
|
|
342
|
+
const missingKeys = schemaKeys.filter((k) => !providedKeys.includes(k));
|
|
343
|
+
let finalProps;
|
|
344
|
+
if (missingKeys.length === 0) {
|
|
345
|
+
// All props provided, no generation needed
|
|
346
|
+
finalProps = partialProps;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
// Generate missing props
|
|
350
|
+
try {
|
|
351
|
+
// Build schema for only missing props
|
|
352
|
+
const missingSchema = {};
|
|
353
|
+
for (const key of missingKeys) {
|
|
354
|
+
const schemaValue = schema[key];
|
|
355
|
+
if (schemaValue !== undefined) {
|
|
356
|
+
missingSchema[key] = schemaValue;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const result = await generateProps({
|
|
360
|
+
schema: missingSchema,
|
|
361
|
+
context: context || partialProps,
|
|
362
|
+
});
|
|
363
|
+
finalProps = {
|
|
364
|
+
...result.props,
|
|
365
|
+
...partialProps,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
// Use fallback on error
|
|
370
|
+
if (fallback) {
|
|
371
|
+
finalProps = {
|
|
372
|
+
...fallback,
|
|
373
|
+
...partialProps,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// Render the component
|
|
382
|
+
return render(finalProps);
|
|
383
|
+
};
|
|
384
|
+
// Attach schema and metadata
|
|
385
|
+
aiComponent.schema = schema;
|
|
386
|
+
aiComponent.displayName = `AI(${name})`;
|
|
387
|
+
return aiComponent;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Wrap an existing component with AI prop generation
|
|
391
|
+
*
|
|
392
|
+
* @param component - Component function to wrap
|
|
393
|
+
* @param options - AI props options
|
|
394
|
+
* @returns Wrapped component with AI props
|
|
395
|
+
*/
|
|
396
|
+
export function withAIProps(component, options) {
|
|
397
|
+
const { schema, fallback } = options;
|
|
398
|
+
const wrappedComponent = async (props) => {
|
|
399
|
+
const { context, ...partialProps } = props;
|
|
400
|
+
// Check which props are missing
|
|
401
|
+
const schemaKeys = Object.keys(schema);
|
|
402
|
+
const providedKeys = Object.keys(partialProps);
|
|
403
|
+
const missingKeys = schemaKeys.filter((k) => !providedKeys.includes(k));
|
|
404
|
+
let finalProps;
|
|
405
|
+
if (missingKeys.length === 0) {
|
|
406
|
+
// All props provided
|
|
407
|
+
finalProps = partialProps;
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
// Generate missing props
|
|
411
|
+
try {
|
|
412
|
+
const missingSchema = {};
|
|
413
|
+
for (const key of missingKeys) {
|
|
414
|
+
const schemaValue = schema[key];
|
|
415
|
+
if (schemaValue !== undefined) {
|
|
416
|
+
missingSchema[key] = schemaValue;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const result = await generateProps({
|
|
420
|
+
schema: missingSchema,
|
|
421
|
+
context: context || partialProps,
|
|
422
|
+
});
|
|
423
|
+
finalProps = {
|
|
424
|
+
...result.props,
|
|
425
|
+
...partialProps,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
if (fallback) {
|
|
430
|
+
finalProps = {
|
|
431
|
+
...fallback,
|
|
432
|
+
...partialProps,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
throw error;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return component(finalProps);
|
|
441
|
+
};
|
|
442
|
+
// Preserve displayName
|
|
443
|
+
const originalName = component.displayName || component.name || 'Component';
|
|
444
|
+
wrappedComponent.schema = schema;
|
|
445
|
+
wrappedComponent.displayName = `withAIProps(${originalName})`;
|
|
446
|
+
return wrappedComponent;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Provider component for AI props configuration
|
|
450
|
+
*
|
|
451
|
+
* @param props - Provider props with config and children
|
|
452
|
+
* @returns Rendered output with AI props context
|
|
453
|
+
*/
|
|
454
|
+
export function AIPropsProvider(props) {
|
|
455
|
+
// Store configuration globally (in a real implementation, use React Context)
|
|
456
|
+
// For now, we just pass through children
|
|
457
|
+
return props.children;
|
|
458
|
+
}
|
|
459
|
+
//# sourceMappingURL=hono-jsx.js.map
|