@flightdev/ui 2.0.0 → 4.0.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/README.md +285 -70
- package/dist/{chunk-XTDK7ME5.js → chunk-S4DTUQII.js} +246 -19
- package/dist/chunk-S4DTUQII.js.map +1 -0
- package/dist/core/index.d.ts +423 -3
- package/dist/core/index.js +23 -2
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.js +29 -5
- package/dist/index.js.map +1 -0
- package/package.json +11 -181
- package/.turbo/turbo-build.log +0 -81
- package/.turbo/turbo-lint.log +0 -40
- package/.turbo/turbo-typecheck.log +0 -4
- package/TESTING.md +0 -124
- package/dist/adapter-MMD-iHNx.d.ts +0 -424
- package/dist/adapters/tier-1/angular.d.ts +0 -60
- package/dist/adapters/tier-1/angular.js +0 -2
- package/dist/adapters/tier-1/index.d.ts +0 -7
- package/dist/adapters/tier-1/index.js +0 -7
- package/dist/adapters/tier-1/qwik.d.ts +0 -55
- package/dist/adapters/tier-1/qwik.js +0 -2
- package/dist/adapters/tier-1/react.d.ts +0 -67
- package/dist/adapters/tier-1/react.js +0 -2
- package/dist/adapters/tier-1/solid.d.ts +0 -45
- package/dist/adapters/tier-1/solid.js +0 -2
- package/dist/adapters/tier-1/svelte.d.ts +0 -48
- package/dist/adapters/tier-1/svelte.js +0 -2
- package/dist/adapters/tier-1/vue.d.ts +0 -47
- package/dist/adapters/tier-1/vue.js +0 -2
- package/dist/adapters/tier-2/index.d.ts +0 -7
- package/dist/adapters/tier-2/index.js +0 -7
- package/dist/adapters/tier-2/inferno.d.ts +0 -31
- package/dist/adapters/tier-2/inferno.js +0 -2
- package/dist/adapters/tier-2/lit.d.ts +0 -34
- package/dist/adapters/tier-2/lit.js +0 -2
- package/dist/adapters/tier-2/marko.d.ts +0 -59
- package/dist/adapters/tier-2/marko.js +0 -2
- package/dist/adapters/tier-2/mithril.d.ts +0 -31
- package/dist/adapters/tier-2/mithril.js +0 -2
- package/dist/adapters/tier-2/preact.d.ts +0 -33
- package/dist/adapters/tier-2/preact.js +0 -2
- package/dist/adapters/tier-2/stencil.d.ts +0 -52
- package/dist/adapters/tier-2/stencil.js +0 -2
- package/dist/adapters/tier-3/alpine.d.ts +0 -73
- package/dist/adapters/tier-3/alpine.js +0 -2
- package/dist/adapters/tier-3/hotwire.d.ts +0 -71
- package/dist/adapters/tier-3/hotwire.js +0 -2
- package/dist/adapters/tier-3/htmx.d.ts +0 -88
- package/dist/adapters/tier-3/htmx.js +0 -2
- package/dist/adapters/tier-3/index.d.ts +0 -7
- package/dist/adapters/tier-3/index.js +0 -7
- package/dist/adapters/tier-3/petite-vue.d.ts +0 -56
- package/dist/adapters/tier-3/petite-vue.js +0 -2
- package/dist/adapters/tier-3/stimulus.d.ts +0 -63
- package/dist/adapters/tier-3/stimulus.js +0 -2
- package/dist/adapters/tier-3/vanilla.d.ts +0 -63
- package/dist/adapters/tier-3/vanilla.js +0 -2
- package/dist/chunk-2SNQ6PTM.js +0 -217
- package/dist/chunk-3D4XMIZI.js +0 -136
- package/dist/chunk-3HU6GSQ4.js +0 -125
- package/dist/chunk-4PZDNFL7.js +0 -148
- package/dist/chunk-5IBLFTYL.js +0 -114
- package/dist/chunk-64JZJ7OK.js +0 -142
- package/dist/chunk-7ZJI3QU2.js +0 -132
- package/dist/chunk-CE4FJHQJ.js +0 -133
- package/dist/chunk-DTCAUBH5.js +0 -87
- package/dist/chunk-NTASPOHG.js +0 -106
- package/dist/chunk-OI2AMQLG.js +0 -152
- package/dist/chunk-Q7HUE44H.js +0 -106
- package/dist/chunk-QH3LOWXU.js +0 -155
- package/dist/chunk-QIVAK6BH.js +0 -103
- package/dist/chunk-V34XPVGK.js +0 -103
- package/dist/chunk-VK7ZPMO7.js +0 -221
- package/dist/chunk-X6CNUW6T.js +0 -136
- package/dist/chunk-YFGSHW5S.js +0 -121
- package/dist/chunk-ZAJVSE7J.js +0 -90
- package/docs/ADAPTERS.md +0 -946
- package/docs/PATTERNS.md +0 -836
- package/src/adapters/tier-1/angular.ts +0 -223
- package/src/adapters/tier-1/index.ts +0 -12
- package/src/adapters/tier-1/qwik.ts +0 -177
- package/src/adapters/tier-1/react.ts +0 -330
- package/src/adapters/tier-1/solid.ts +0 -222
- package/src/adapters/tier-1/svelte.ts +0 -211
- package/src/adapters/tier-1/vue.ts +0 -234
- package/src/adapters/tier-2/index.ts +0 -12
- package/src/adapters/tier-2/inferno.ts +0 -149
- package/src/adapters/tier-2/lit.ts +0 -191
- package/src/adapters/tier-2/marko.ts +0 -199
- package/src/adapters/tier-2/mithril.ts +0 -152
- package/src/adapters/tier-2/preact.ts +0 -133
- package/src/adapters/tier-2/stencil.ts +0 -214
- package/src/adapters/tier-3/alpine.ts +0 -218
- package/src/adapters/tier-3/hotwire.ts +0 -254
- package/src/adapters/tier-3/htmx.ts +0 -263
- package/src/adapters/tier-3/index.ts +0 -12
- package/src/adapters/tier-3/petite-vue.ts +0 -163
- package/src/adapters/tier-3/stimulus.ts +0 -233
- package/src/adapters/tier-3/vanilla.ts +0 -252
- package/src/ambient.d.ts +0 -310
- package/src/core/adapter.ts +0 -366
- package/src/core/index.ts +0 -56
- package/src/core/registry.ts +0 -518
- package/src/core/types.ts +0 -461
- package/src/htmx.ts +0 -134
- package/src/index.ts +0 -263
- package/test/__mocks__/stencil-core.ts +0 -19
- package/test/__mocks__/stencil-hydrate.ts +0 -15
- package/test/adapters/tier-1.test.ts +0 -206
- package/test/adapters/tier-2.test.ts +0 -175
- package/test/adapters/tier-3.test.ts +0 -284
- package/test/contracts/adapter.contract.ts +0 -293
- package/test/core/core.test.ts +0 -310
- package/test/errors/error-handling.test.ts +0 -454
- package/test/integration/htmx.integration.test.ts +0 -246
- package/test/integration/react.integration.test.ts +0 -271
- package/test/integration/registry.integration.test.ts +0 -308
- package/tsconfig.json +0 -22
- package/tsup.config.ts +0 -93
- package/vitest.config.ts +0 -101
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @flightdev/ui - React Adapter (Tier 1)
|
|
3
|
-
*
|
|
4
|
-
* Full-featured React 18+ SSR adapter with streaming and hydration support.
|
|
5
|
-
*
|
|
6
|
-
* @module @flightdev/ui/react
|
|
7
|
-
* @version 2.0.0
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { BaseUIAdapter } from '../../core/adapter.js';
|
|
11
|
-
import type {
|
|
12
|
-
AdapterCapabilities,
|
|
13
|
-
Component,
|
|
14
|
-
RenderContext,
|
|
15
|
-
RenderResult,
|
|
16
|
-
StreamingOptions,
|
|
17
|
-
StreamingRenderResult,
|
|
18
|
-
} from '../../core/types.js';
|
|
19
|
-
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// Types
|
|
22
|
-
// ============================================================================
|
|
23
|
-
|
|
24
|
-
export interface ReactAdapterOptions {
|
|
25
|
-
/** Enable streaming SSR with renderToPipeableStream */
|
|
26
|
-
streaming?: boolean;
|
|
27
|
-
|
|
28
|
-
/** Enable React Server Components support */
|
|
29
|
-
serverComponents?: boolean;
|
|
30
|
-
|
|
31
|
-
/** Custom error handler for streaming */
|
|
32
|
-
onError?: (error: Error) => void;
|
|
33
|
-
|
|
34
|
-
/** Custom shell ready handler */
|
|
35
|
-
onShellReady?: () => void;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ============================================================================
|
|
39
|
-
// React Adapter
|
|
40
|
-
// ============================================================================
|
|
41
|
-
|
|
42
|
-
export class ReactAdapter extends BaseUIAdapter {
|
|
43
|
-
readonly id = 'react';
|
|
44
|
-
readonly name = 'React';
|
|
45
|
-
readonly framework = 'react';
|
|
46
|
-
readonly frameworkVersion = '18+';
|
|
47
|
-
readonly tier = 'tier-1' as const;
|
|
48
|
-
|
|
49
|
-
override readonly capabilities: AdapterCapabilities = {
|
|
50
|
-
streaming: true,
|
|
51
|
-
partialHydration: true,
|
|
52
|
-
islands: true,
|
|
53
|
-
resumable: false, // React 19 has some resumability features
|
|
54
|
-
ssg: true,
|
|
55
|
-
csr: true,
|
|
56
|
-
serverComponents: true,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
constructor(private options: ReactAdapterOptions = {}) {
|
|
60
|
-
super();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async renderToString(
|
|
64
|
-
component: Component,
|
|
65
|
-
_context?: RenderContext
|
|
66
|
-
): Promise<RenderResult> {
|
|
67
|
-
const startTime = performance.now();
|
|
68
|
-
|
|
69
|
-
// Dynamic imports for React SSR
|
|
70
|
-
const { renderToString } = await import('react-dom/server');
|
|
71
|
-
const { createElement } = await import('react');
|
|
72
|
-
|
|
73
|
-
// Create React element
|
|
74
|
-
const element = createElement(
|
|
75
|
-
component.component as React.ComponentType,
|
|
76
|
-
(component.props ?? {}) as Record<string, unknown>,
|
|
77
|
-
component.children as React.ReactNode
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const html = renderToString(element);
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
html,
|
|
84
|
-
hydrationData: {
|
|
85
|
-
props: component.props,
|
|
86
|
-
componentId: component.id ?? this.generateId(),
|
|
87
|
-
},
|
|
88
|
-
timing: this.createTiming(startTime),
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Render to stream using React 18+ streaming SSR.
|
|
94
|
-
*
|
|
95
|
-
* Uses `renderToReadableStream` (Web Streams API) which works in:
|
|
96
|
-
* - Node.js 18+ (with Web Streams support)
|
|
97
|
-
* - Deno
|
|
98
|
-
* - Cloudflare Workers
|
|
99
|
-
* - Vercel Edge Runtime
|
|
100
|
-
* - Other edge runtimes
|
|
101
|
-
*
|
|
102
|
-
* Falls back to `renderToPipeableStream` for Node.js environments
|
|
103
|
-
* that require traditional streams.
|
|
104
|
-
*/
|
|
105
|
-
renderToStream(
|
|
106
|
-
component: Component,
|
|
107
|
-
_context?: RenderContext,
|
|
108
|
-
options?: StreamingOptions
|
|
109
|
-
): StreamingRenderResult {
|
|
110
|
-
let aborted = false;
|
|
111
|
-
let abortController: AbortController | null = null;
|
|
112
|
-
let resolvePromise: () => void;
|
|
113
|
-
let rejectPromise: (error: Error) => void;
|
|
114
|
-
|
|
115
|
-
const done = new Promise<void>((resolve, reject) => {
|
|
116
|
-
resolvePromise = resolve;
|
|
117
|
-
rejectPromise = reject;
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
121
|
-
start: async (controller) => {
|
|
122
|
-
try {
|
|
123
|
-
// Use renderToReadableStream - the Web Streams API version
|
|
124
|
-
// This is the recommended approach for modern Node.js and edge runtimes
|
|
125
|
-
const { renderToReadableStream } = await import('react-dom/server');
|
|
126
|
-
const { createElement } = await import('react');
|
|
127
|
-
|
|
128
|
-
// Create React element
|
|
129
|
-
const element = createElement(
|
|
130
|
-
component.component as React.ComponentType,
|
|
131
|
-
(component.props ?? {}) as Record<string, unknown>,
|
|
132
|
-
component.children as React.ReactNode
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
// Create abort controller for cancellation
|
|
136
|
-
abortController = new AbortController();
|
|
137
|
-
|
|
138
|
-
// Render to ReadableStream
|
|
139
|
-
const reactStream = await renderToReadableStream(element, {
|
|
140
|
-
signal: abortController.signal,
|
|
141
|
-
onError(error: unknown) {
|
|
142
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
143
|
-
options?.onError?.(err);
|
|
144
|
-
// Don't reject here - let the stream handle it
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Notify shell is ready
|
|
149
|
-
options?.onShellReady?.();
|
|
150
|
-
|
|
151
|
-
// Get reader from React's stream
|
|
152
|
-
const reader = reactStream.getReader();
|
|
153
|
-
|
|
154
|
-
// Read and forward chunks
|
|
155
|
-
const pump = async (): Promise<void> => {
|
|
156
|
-
try {
|
|
157
|
-
while (true) {
|
|
158
|
-
if (aborted) {
|
|
159
|
-
await reader.cancel();
|
|
160
|
-
controller.close();
|
|
161
|
-
resolvePromise();
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const { done: readerDone, value } = await reader.read();
|
|
166
|
-
|
|
167
|
-
if (readerDone) {
|
|
168
|
-
controller.close();
|
|
169
|
-
options?.onAllReady?.();
|
|
170
|
-
resolvePromise();
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (value) {
|
|
175
|
-
controller.enqueue(value);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
} catch (error) {
|
|
179
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
180
|
-
options?.onError?.(err);
|
|
181
|
-
controller.error(err);
|
|
182
|
-
rejectPromise(err);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
// Start pumping data
|
|
187
|
-
await pump();
|
|
188
|
-
|
|
189
|
-
} catch (error) {
|
|
190
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
191
|
-
|
|
192
|
-
// Check if renderToReadableStream is not available (older Node.js)
|
|
193
|
-
if (err.message.includes('renderToReadableStream')) {
|
|
194
|
-
// Fallback to synchronous render for environments without Web Streams
|
|
195
|
-
await this.fallbackStreamRender(component, controller, options, resolvePromise, rejectPromise);
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
options?.onShellError?.(err);
|
|
200
|
-
options?.onError?.(err);
|
|
201
|
-
controller.error(err);
|
|
202
|
-
rejectPromise(err);
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
stream,
|
|
209
|
-
done,
|
|
210
|
-
abort: () => {
|
|
211
|
-
aborted = true;
|
|
212
|
-
abortController?.abort();
|
|
213
|
-
resolvePromise();
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Fallback render for environments without Web Streams support.
|
|
220
|
-
* Uses renderToString and emits as a single chunk.
|
|
221
|
-
*/
|
|
222
|
-
private async fallbackStreamRender(
|
|
223
|
-
component: Component,
|
|
224
|
-
controller: ReadableStreamDefaultController<Uint8Array>,
|
|
225
|
-
options: StreamingOptions | undefined,
|
|
226
|
-
resolvePromise: () => void,
|
|
227
|
-
rejectPromise: (error: Error) => void
|
|
228
|
-
): Promise<void> {
|
|
229
|
-
try {
|
|
230
|
-
const { renderToString } = await import('react-dom/server');
|
|
231
|
-
const { createElement } = await import('react');
|
|
232
|
-
|
|
233
|
-
const element = createElement(
|
|
234
|
-
component.component as React.ComponentType,
|
|
235
|
-
(component.props ?? {}) as Record<string, unknown>,
|
|
236
|
-
component.children as React.ReactNode
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
options?.onShellReady?.();
|
|
240
|
-
|
|
241
|
-
const html = renderToString(element);
|
|
242
|
-
const encoded = new TextEncoder().encode(html);
|
|
243
|
-
|
|
244
|
-
controller.enqueue(encoded);
|
|
245
|
-
controller.close();
|
|
246
|
-
options?.onAllReady?.();
|
|
247
|
-
resolvePromise();
|
|
248
|
-
} catch (error) {
|
|
249
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
250
|
-
options?.onError?.(err);
|
|
251
|
-
controller.error(err);
|
|
252
|
-
rejectPromise(err);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
override getHydrationScript(result: RenderResult): string {
|
|
257
|
-
const data = this.serializeProps(result.hydrationData);
|
|
258
|
-
return `
|
|
259
|
-
<script type="module">
|
|
260
|
-
window.__FLIGHT_DATA__ = ${data};
|
|
261
|
-
window.__FLIGHT_ADAPTER__ = 'react';
|
|
262
|
-
|
|
263
|
-
import { hydrateRoot } from 'react-dom/client';
|
|
264
|
-
|
|
265
|
-
const container = document.getElementById('app');
|
|
266
|
-
const App = window.__FLIGHT_APP__;
|
|
267
|
-
|
|
268
|
-
if (container && App) {
|
|
269
|
-
const { props } = window.__FLIGHT_DATA__;
|
|
270
|
-
import('react').then(({ createElement }) => {
|
|
271
|
-
hydrateRoot(container, createElement(App, props));
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
</script>
|
|
275
|
-
`.trim();
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
override getClientEntry(): string {
|
|
279
|
-
return `
|
|
280
|
-
import { hydrateRoot, createRoot } from 'react-dom/client';
|
|
281
|
-
import { createElement } from 'react';
|
|
282
|
-
|
|
283
|
-
export function hydrate() {
|
|
284
|
-
const container = document.getElementById('app');
|
|
285
|
-
const App = window.__FLIGHT_APP__;
|
|
286
|
-
const data = window.__FLIGHT_DATA__ ?? {};
|
|
287
|
-
|
|
288
|
-
if (!container || !App) {
|
|
289
|
-
console.warn('[Flight/React] Missing container or App');
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Check if we have SSR content to hydrate
|
|
294
|
-
if (container.innerHTML.trim()) {
|
|
295
|
-
hydrateRoot(container, createElement(App, data.props));
|
|
296
|
-
} else {
|
|
297
|
-
// Fallback to client-side render
|
|
298
|
-
createRoot(container).render(createElement(App, data.props));
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export function render(App, container, props = {}) {
|
|
303
|
-
const root = createRoot(container);
|
|
304
|
-
root.render(createElement(App, props));
|
|
305
|
-
return root;
|
|
306
|
-
}
|
|
307
|
-
`.trim();
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// ============================================================================
|
|
312
|
-
// Factory Function
|
|
313
|
-
// ============================================================================
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Create a React UI adapter.
|
|
317
|
-
*
|
|
318
|
-
* @example
|
|
319
|
-
* ```typescript
|
|
320
|
-
* import { react } from '@flightdev/ui/react';
|
|
321
|
-
* import { defineUI } from '@flightdev/ui';
|
|
322
|
-
*
|
|
323
|
-
* export default defineUI(react({ streaming: true }));
|
|
324
|
-
* ```
|
|
325
|
-
*/
|
|
326
|
-
export function react(options?: ReactAdapterOptions): ReactAdapter {
|
|
327
|
-
return new ReactAdapter(options);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
export default react;
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @flightdev/ui - Solid Adapter (Tier 1)
|
|
3
|
-
*
|
|
4
|
-
* Full-featured SolidJS SSR adapter with streaming and hydration support.
|
|
5
|
-
*
|
|
6
|
-
* @module @flightdev/ui/solid
|
|
7
|
-
* @version 2.0.0
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { BaseUIAdapter } from '../../core/adapter.js';
|
|
11
|
-
import type {
|
|
12
|
-
AdapterCapabilities,
|
|
13
|
-
Component,
|
|
14
|
-
RenderContext,
|
|
15
|
-
RenderResult,
|
|
16
|
-
StreamingOptions,
|
|
17
|
-
StreamingRenderResult,
|
|
18
|
-
} from '../../core/types.js';
|
|
19
|
-
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// Types
|
|
22
|
-
// ============================================================================
|
|
23
|
-
|
|
24
|
-
export interface SolidAdapterOptions {
|
|
25
|
-
/** Enable streaming SSR */
|
|
26
|
-
streaming?: boolean;
|
|
27
|
-
|
|
28
|
-
/** Render timeout in milliseconds */
|
|
29
|
-
timeout?: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// ============================================================================
|
|
33
|
-
// Solid Adapter
|
|
34
|
-
// ============================================================================
|
|
35
|
-
|
|
36
|
-
export class SolidAdapter extends BaseUIAdapter {
|
|
37
|
-
readonly id = 'solid';
|
|
38
|
-
readonly name = 'Solid';
|
|
39
|
-
readonly framework = 'solid';
|
|
40
|
-
readonly frameworkVersion = '1.8+';
|
|
41
|
-
readonly tier = 'tier-1' as const;
|
|
42
|
-
|
|
43
|
-
override readonly capabilities: AdapterCapabilities = {
|
|
44
|
-
streaming: true,
|
|
45
|
-
partialHydration: true,
|
|
46
|
-
islands: true,
|
|
47
|
-
resumable: false,
|
|
48
|
-
ssg: true,
|
|
49
|
-
csr: true,
|
|
50
|
-
serverComponents: false,
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
constructor(private options: SolidAdapterOptions = {}) {
|
|
54
|
-
super();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async renderToString(
|
|
58
|
-
component: Component,
|
|
59
|
-
_context?: RenderContext
|
|
60
|
-
): Promise<RenderResult> {
|
|
61
|
-
const startTime = performance.now();
|
|
62
|
-
|
|
63
|
-
const { renderToString } = await import('solid-js/web');
|
|
64
|
-
|
|
65
|
-
const SolidComponent = component.component as () => unknown;
|
|
66
|
-
const props = component.props ?? {};
|
|
67
|
-
|
|
68
|
-
// Create a function that returns the component with props
|
|
69
|
-
const App = () => SolidComponent() as object;
|
|
70
|
-
|
|
71
|
-
const html = await renderToString(() => SolidComponent() as object);
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
html,
|
|
75
|
-
hydrationData: {
|
|
76
|
-
props,
|
|
77
|
-
componentId: component.id ?? this.generateId(),
|
|
78
|
-
},
|
|
79
|
-
timing: this.createTiming(startTime),
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
renderToStream(
|
|
84
|
-
component: Component,
|
|
85
|
-
_context?: RenderContext,
|
|
86
|
-
options?: StreamingOptions
|
|
87
|
-
): StreamingRenderResult {
|
|
88
|
-
let aborted = false;
|
|
89
|
-
let resolvePromise: () => void;
|
|
90
|
-
let rejectPromise: (error: Error) => void;
|
|
91
|
-
|
|
92
|
-
const done = new Promise<void>((resolve, reject) => {
|
|
93
|
-
resolvePromise = resolve;
|
|
94
|
-
rejectPromise = reject;
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
98
|
-
async start(controller) {
|
|
99
|
-
try {
|
|
100
|
-
const { renderToStream: solidRenderToStream } = await import('solid-js/web');
|
|
101
|
-
|
|
102
|
-
const SolidComponent = component.component as () => object;
|
|
103
|
-
const App = () => SolidComponent();
|
|
104
|
-
|
|
105
|
-
const { readable } = solidRenderToStream(() => SolidComponent());
|
|
106
|
-
const reader = readable.getReader();
|
|
107
|
-
|
|
108
|
-
options?.onShellReady?.();
|
|
109
|
-
|
|
110
|
-
while (true) {
|
|
111
|
-
const { done: readerDone, value } = await reader.read();
|
|
112
|
-
|
|
113
|
-
if (aborted) {
|
|
114
|
-
reader.cancel();
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (readerDone) {
|
|
119
|
-
controller.close();
|
|
120
|
-
resolvePromise();
|
|
121
|
-
options?.onAllReady?.();
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Convert string to Uint8Array if needed
|
|
126
|
-
const chunk = typeof value === 'string'
|
|
127
|
-
? new TextEncoder().encode(value)
|
|
128
|
-
: value;
|
|
129
|
-
|
|
130
|
-
controller.enqueue(chunk);
|
|
131
|
-
}
|
|
132
|
-
} catch (error) {
|
|
133
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
134
|
-
options?.onError?.(err);
|
|
135
|
-
controller.error(err);
|
|
136
|
-
rejectPromise(err);
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
stream,
|
|
143
|
-
done,
|
|
144
|
-
abort() {
|
|
145
|
-
aborted = true;
|
|
146
|
-
resolvePromise();
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
override getHydrationScript(result: RenderResult): string {
|
|
152
|
-
const data = this.serializeProps(result.hydrationData);
|
|
153
|
-
return `
|
|
154
|
-
<script type="module">
|
|
155
|
-
window.__FLIGHT_DATA__ = ${data};
|
|
156
|
-
window.__FLIGHT_ADAPTER__ = 'solid';
|
|
157
|
-
|
|
158
|
-
import { hydrate } from 'solid-js/web';
|
|
159
|
-
|
|
160
|
-
const App = window.__FLIGHT_APP__;
|
|
161
|
-
const container = document.getElementById('app');
|
|
162
|
-
|
|
163
|
-
if (App && container) {
|
|
164
|
-
hydrate(() => App(window.__FLIGHT_DATA__.props), container);
|
|
165
|
-
}
|
|
166
|
-
</script>
|
|
167
|
-
`.trim();
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
override getClientEntry(): string {
|
|
171
|
-
return `
|
|
172
|
-
import { hydrate, render } from 'solid-js/web';
|
|
173
|
-
|
|
174
|
-
export function hydrateApp() {
|
|
175
|
-
const container = document.getElementById('app');
|
|
176
|
-
const App = window.__FLIGHT_APP__;
|
|
177
|
-
const data = window.__FLIGHT_DATA__ ?? {};
|
|
178
|
-
|
|
179
|
-
if (!container || !App) {
|
|
180
|
-
console.warn('[Flight/Solid] Missing container or App');
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Check if we have SSR content to hydrate
|
|
185
|
-
if (container.innerHTML.trim()) {
|
|
186
|
-
return hydrate(() => App(data.props), container);
|
|
187
|
-
} else {
|
|
188
|
-
// Fallback to client-side render
|
|
189
|
-
return render(() => App(data.props), container);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export function renderApp(App, container, props = {}) {
|
|
194
|
-
return render(() => App(props), container);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Export for Flight
|
|
198
|
-
export { hydrateApp as hydrate };
|
|
199
|
-
`.trim();
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// ============================================================================
|
|
204
|
-
// Factory Function
|
|
205
|
-
// ============================================================================
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Create a Solid UI adapter.
|
|
209
|
-
*
|
|
210
|
-
* @example
|
|
211
|
-
* ```typescript
|
|
212
|
-
* import { solid } from '@flightdev/ui/solid';
|
|
213
|
-
* import { defineUI } from '@flightdev/ui';
|
|
214
|
-
*
|
|
215
|
-
* export default defineUI(solid({ streaming: true }));
|
|
216
|
-
* ```
|
|
217
|
-
*/
|
|
218
|
-
export function solid(options?: SolidAdapterOptions): SolidAdapter {
|
|
219
|
-
return new SolidAdapter(options);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export default solid;
|