@flightdev/ui 2.0.1 → 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 +283 -68
- 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 +7 -183
- 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
package/docs/ADAPTERS.md
DELETED
|
@@ -1,946 +0,0 @@
|
|
|
1
|
-
# UI Adapter System
|
|
2
|
-
|
|
3
|
-
This document provides comprehensive documentation for the `@flightdev/ui` adapter system, including tier classifications, implementation patterns, and enterprise usage examples.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
1. [Overview](#overview)
|
|
8
|
-
2. [Tier Classification](#tier-classification)
|
|
9
|
-
3. [Tier 1: Full Support Adapters](#tier-1-full-support-adapters)
|
|
10
|
-
4. [Tier 2: Standard Support Adapters](#tier-2-standard-support-adapters)
|
|
11
|
-
5. [Tier 3: HTML-First Adapters](#tier-3-html-first-adapters)
|
|
12
|
-
6. [Advanced Patterns](#advanced-patterns)
|
|
13
|
-
7. [Creating Custom Adapters](#creating-custom-adapters)
|
|
14
|
-
8. [Performance Considerations](#performance-considerations)
|
|
15
|
-
9. [Migration Guide](#migration-guide)
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Overview
|
|
20
|
-
|
|
21
|
-
The Flight UI adapter system provides a unified interface for rendering components across 18+ frameworks. All adapters implement the `UIAdapterV2` interface, ensuring consistent behavior regardless of the underlying framework.
|
|
22
|
-
|
|
23
|
-
### Core Concepts
|
|
24
|
-
|
|
25
|
-
**Adapter**: A class that translates Flight's rendering API to a specific framework's SSR implementation.
|
|
26
|
-
|
|
27
|
-
**Capabilities**: Feature flags indicating what an adapter supports (streaming, islands, resumability, etc.).
|
|
28
|
-
|
|
29
|
-
**Tiers**: Classification levels indicating the depth of framework integration.
|
|
30
|
-
|
|
31
|
-
### Quick Start
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
import { defineUI } from '@flightdev/ui';
|
|
35
|
-
import { react } from '@flightdev/ui/react';
|
|
36
|
-
|
|
37
|
-
export default defineUI(react({
|
|
38
|
-
streaming: true,
|
|
39
|
-
}));
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Tier Classification
|
|
45
|
-
|
|
46
|
-
Adapters are organized into three tiers based on their feature set and integration depth.
|
|
47
|
-
|
|
48
|
-
| Tier | Support Level | Use Case |
|
|
49
|
-
|------|---------------|----------|
|
|
50
|
-
| Tier 1 | Full | Enterprise applications requiring streaming, islands, and full hydration |
|
|
51
|
-
| Tier 2 | Standard | Applications needing SSR with simpler hydration requirements |
|
|
52
|
-
| Tier 3 | HTML-First | Server-driven applications with minimal client-side JavaScript |
|
|
53
|
-
|
|
54
|
-
### Capability Matrix
|
|
55
|
-
|
|
56
|
-
| Capability | Tier 1 | Tier 2 | Tier 3 |
|
|
57
|
-
|------------|--------|--------|--------|
|
|
58
|
-
| SSR | Yes | Yes | Yes |
|
|
59
|
-
| Streaming | Yes | Some | No |
|
|
60
|
-
| Islands | Yes | Some | No |
|
|
61
|
-
| Resumable | Qwik only | No | No |
|
|
62
|
-
| CSR Fallback | Yes | Yes | Limited |
|
|
63
|
-
| Server Components | React only | No | No |
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## Tier 1: Full Support Adapters
|
|
68
|
-
|
|
69
|
-
Tier 1 adapters provide complete SSR, streaming, hydration, and islands architecture support.
|
|
70
|
-
|
|
71
|
-
### Available Adapters
|
|
72
|
-
|
|
73
|
-
- React 18+
|
|
74
|
-
- Vue 3
|
|
75
|
-
- Angular 17+
|
|
76
|
-
- Svelte 5
|
|
77
|
-
- Solid
|
|
78
|
-
- Qwik
|
|
79
|
-
|
|
80
|
-
### React Adapter
|
|
81
|
-
|
|
82
|
-
Full-featured React 18+ SSR adapter with streaming and hydration support.
|
|
83
|
-
|
|
84
|
-
**Capabilities**:
|
|
85
|
-
- Streaming SSR via `renderToReadableStream`
|
|
86
|
-
- Progressive hydration
|
|
87
|
-
- Islands architecture
|
|
88
|
-
- React Server Components
|
|
89
|
-
- SSG and CSR fallback
|
|
90
|
-
|
|
91
|
-
**Basic Usage**:
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
import { defineUI } from '@flightdev/ui';
|
|
95
|
-
import { react } from '@flightdev/ui/react';
|
|
96
|
-
|
|
97
|
-
export default defineUI(react());
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**With Options**:
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
import { react } from '@flightdev/ui/react';
|
|
104
|
-
|
|
105
|
-
const adapter = react({
|
|
106
|
-
streaming: true,
|
|
107
|
-
serverComponents: true,
|
|
108
|
-
onError: (error) => {
|
|
109
|
-
console.error('[SSR Error]', error.message);
|
|
110
|
-
},
|
|
111
|
-
onShellReady: () => {
|
|
112
|
-
console.log('Shell rendered');
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**Streaming SSR Example**:
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
import { react } from '@flightdev/ui/react';
|
|
121
|
-
|
|
122
|
-
const adapter = react();
|
|
123
|
-
|
|
124
|
-
// Component definition
|
|
125
|
-
function App({ user }) {
|
|
126
|
-
return (
|
|
127
|
-
<main>
|
|
128
|
-
<h1>Welcome, {user.name}</h1>
|
|
129
|
-
<Suspense fallback={<Spinner />}>
|
|
130
|
-
<UserDashboard userId={user.id} />
|
|
131
|
-
</Suspense>
|
|
132
|
-
</main>
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Streaming render
|
|
137
|
-
const { stream, done, abort } = adapter.renderToStream(
|
|
138
|
-
{ component: App, props: { user } },
|
|
139
|
-
{ url: request.url },
|
|
140
|
-
{
|
|
141
|
-
timeout: 10000,
|
|
142
|
-
onShellReady: () => {
|
|
143
|
-
// Send headers when shell is ready
|
|
144
|
-
response.writeHead(200, {
|
|
145
|
-
'Content-Type': 'text/html',
|
|
146
|
-
'Transfer-Encoding': 'chunked',
|
|
147
|
-
});
|
|
148
|
-
},
|
|
149
|
-
onError: (error) => {
|
|
150
|
-
console.error('[Stream Error]', error);
|
|
151
|
-
},
|
|
152
|
-
}
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
// Pipe stream to response
|
|
156
|
-
const reader = stream.getReader();
|
|
157
|
-
while (true) {
|
|
158
|
-
const { done: readerDone, value } = await reader.read();
|
|
159
|
-
if (readerDone) break;
|
|
160
|
-
response.write(value);
|
|
161
|
-
}
|
|
162
|
-
response.end();
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
**Islands Architecture**:
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
const adapter = react();
|
|
169
|
-
|
|
170
|
-
// Create an island for partial hydration
|
|
171
|
-
const profileIsland = adapter.createIsland(
|
|
172
|
-
'UserProfile',
|
|
173
|
-
{ userId: '123' },
|
|
174
|
-
{
|
|
175
|
-
hydrate: 'visible', // Hydrate when visible
|
|
176
|
-
priority: 10,
|
|
177
|
-
}
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// Render island placeholder
|
|
181
|
-
const html = `
|
|
182
|
-
<div class="page">
|
|
183
|
-
<header>Static content</header>
|
|
184
|
-
${profileIsland.placeholder}
|
|
185
|
-
<footer>Static content</footer>
|
|
186
|
-
</div>
|
|
187
|
-
`;
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Vue Adapter
|
|
191
|
-
|
|
192
|
-
Vue 3 SSR adapter with streaming and islands support.
|
|
193
|
-
|
|
194
|
-
**Basic Usage**:
|
|
195
|
-
|
|
196
|
-
```typescript
|
|
197
|
-
import { vue } from '@flightdev/ui/vue';
|
|
198
|
-
|
|
199
|
-
const adapter = vue({
|
|
200
|
-
streaming: true,
|
|
201
|
-
});
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
**SSR Example**:
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
import { vue } from '@flightdev/ui/vue';
|
|
208
|
-
import App from './App.vue';
|
|
209
|
-
|
|
210
|
-
const adapter = vue();
|
|
211
|
-
|
|
212
|
-
const result = await adapter.renderToString({
|
|
213
|
-
component: App,
|
|
214
|
-
props: { title: 'My Page' },
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
// result.html contains rendered HTML
|
|
218
|
-
// result.hydrationData contains state for client
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Solid Adapter
|
|
222
|
-
|
|
223
|
-
Solid SSR adapter with streaming support.
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
import { solid } from '@flightdev/ui/solid';
|
|
227
|
-
|
|
228
|
-
const adapter = solid({
|
|
229
|
-
streaming: true,
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
// Streaming render
|
|
233
|
-
const { stream, done } = adapter.renderToStream({
|
|
234
|
-
component: App,
|
|
235
|
-
props: { data },
|
|
236
|
-
});
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### Qwik Adapter
|
|
240
|
-
|
|
241
|
-
Qwik adapter with resumable hydration support.
|
|
242
|
-
|
|
243
|
-
**Resumability**: Qwik serializes component state to HTML, enabling zero-JS-until-interaction. This is the only Tier 1 adapter with `resumable: true`.
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
import { qwik } from '@flightdev/ui/qwik';
|
|
247
|
-
|
|
248
|
-
const adapter = qwik();
|
|
249
|
-
|
|
250
|
-
const result = await adapter.renderToString({
|
|
251
|
-
component: App,
|
|
252
|
-
props: { data },
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// Serialize state for resumability
|
|
256
|
-
const serialized = adapter.serializeState(result.hydrationData);
|
|
257
|
-
|
|
258
|
-
// Resume on client (no JS needed until interaction)
|
|
259
|
-
const state = adapter.resumeFromState(serialized);
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
---
|
|
263
|
-
|
|
264
|
-
## Tier 2: Standard Support Adapters
|
|
265
|
-
|
|
266
|
-
Tier 2 adapters provide SSR and hydration with framework-specific optimizations.
|
|
267
|
-
|
|
268
|
-
### Available Adapters
|
|
269
|
-
|
|
270
|
-
- Preact
|
|
271
|
-
- Lit
|
|
272
|
-
- Marko (with streaming)
|
|
273
|
-
- Stencil
|
|
274
|
-
- Mithril
|
|
275
|
-
- Inferno
|
|
276
|
-
|
|
277
|
-
### Preact Adapter
|
|
278
|
-
|
|
279
|
-
Lightweight React alternative with smaller bundle size.
|
|
280
|
-
|
|
281
|
-
```typescript
|
|
282
|
-
import { preact } from '@flightdev/ui/preact';
|
|
283
|
-
|
|
284
|
-
const adapter = preact();
|
|
285
|
-
|
|
286
|
-
const result = await adapter.renderToString({
|
|
287
|
-
component: App,
|
|
288
|
-
props: { count: 0 },
|
|
289
|
-
});
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### Lit Adapter
|
|
293
|
-
|
|
294
|
-
Web Components SSR with declarative hydration.
|
|
295
|
-
|
|
296
|
-
```typescript
|
|
297
|
-
import { lit } from '@flightdev/ui/lit';
|
|
298
|
-
|
|
299
|
-
const adapter = lit();
|
|
300
|
-
|
|
301
|
-
// Lit uses web components with shadow DOM
|
|
302
|
-
const result = await adapter.renderToString({
|
|
303
|
-
component: 'my-element',
|
|
304
|
-
props: { data: 'value' },
|
|
305
|
-
});
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Marko Adapter
|
|
309
|
-
|
|
310
|
-
Streaming SSR with progressive enhancement.
|
|
311
|
-
|
|
312
|
-
```typescript
|
|
313
|
-
import { marko } from '@flightdev/ui/marko';
|
|
314
|
-
|
|
315
|
-
const adapter = marko();
|
|
316
|
-
|
|
317
|
-
// Marko supports streaming out of the box
|
|
318
|
-
const { stream, done } = adapter.renderToStream({
|
|
319
|
-
component: template,
|
|
320
|
-
props: { items },
|
|
321
|
-
});
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
---
|
|
325
|
-
|
|
326
|
-
## Tier 3: HTML-First Adapters
|
|
327
|
-
|
|
328
|
-
Tier 3 adapters focus on server-rendered HTML with minimal JavaScript.
|
|
329
|
-
|
|
330
|
-
### Available Adapters
|
|
331
|
-
|
|
332
|
-
- HTMX
|
|
333
|
-
- Alpine.js
|
|
334
|
-
- Hotwire (Turbo + Stimulus)
|
|
335
|
-
- Stimulus
|
|
336
|
-
- Petite-vue
|
|
337
|
-
- Vanilla (Pure Web Components)
|
|
338
|
-
|
|
339
|
-
### HTMX Adapter
|
|
340
|
-
|
|
341
|
-
Server-driven UI with HTML over the wire.
|
|
342
|
-
|
|
343
|
-
**Philosophy**: HTMX keeps state on the server. Clicking a button sends a request, the server returns HTML, and HTMX swaps it into the DOM. No client-side JavaScript framework needed.
|
|
344
|
-
|
|
345
|
-
**Basic Usage**:
|
|
346
|
-
|
|
347
|
-
```typescript
|
|
348
|
-
import { htmx } from '@flightdev/ui/htmx';
|
|
349
|
-
|
|
350
|
-
const adapter = htmx({
|
|
351
|
-
version: '2.0.2',
|
|
352
|
-
extensions: ['json-enc', 'loading-states'],
|
|
353
|
-
});
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
**Template Helpers**:
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
import { htmx, hxGet, hxPost, hx, attrsToString } from '@flightdev/ui/htmx';
|
|
360
|
-
|
|
361
|
-
// Create hx-get attributes
|
|
362
|
-
const loadMoreAttrs = hxGet('/api/items?page=2', {
|
|
363
|
-
target: '#item-list',
|
|
364
|
-
swap: 'beforeend',
|
|
365
|
-
trigger: 'click',
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// Build HTML with helpers
|
|
369
|
-
const button = hx('button', loadMoreAttrs, 'Load More');
|
|
370
|
-
// <button hx-get="/api/items?page=2" hx-target="#item-list" hx-swap="beforeend" hx-trigger="click">Load More</button>
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
**Full Page Example**:
|
|
374
|
-
|
|
375
|
-
```typescript
|
|
376
|
-
const adapter = htmx({
|
|
377
|
-
extensions: ['loading-states'],
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
function renderPage(items) {
|
|
381
|
-
return `
|
|
382
|
-
<!DOCTYPE html>
|
|
383
|
-
<html>
|
|
384
|
-
<head>
|
|
385
|
-
<title>Items</title>
|
|
386
|
-
</head>
|
|
387
|
-
<body>
|
|
388
|
-
<main>
|
|
389
|
-
<h1>Items</h1>
|
|
390
|
-
|
|
391
|
-
<ul id="item-list">
|
|
392
|
-
${items.map(item => `
|
|
393
|
-
<li>
|
|
394
|
-
${item.name}
|
|
395
|
-
<button hx-delete="/api/items/${item.id}"
|
|
396
|
-
hx-target="closest li"
|
|
397
|
-
hx-swap="outerHTML"
|
|
398
|
-
hx-confirm="Delete this item?">
|
|
399
|
-
Delete
|
|
400
|
-
</button>
|
|
401
|
-
</li>
|
|
402
|
-
`).join('')}
|
|
403
|
-
</ul>
|
|
404
|
-
|
|
405
|
-
<form hx-post="/api/items" hx-target="#item-list" hx-swap="beforeend">
|
|
406
|
-
<input name="name" placeholder="New item" required />
|
|
407
|
-
<button type="submit">Add</button>
|
|
408
|
-
</form>
|
|
409
|
-
</main>
|
|
410
|
-
|
|
411
|
-
${adapter.getHydrationScript({ html: '' })}
|
|
412
|
-
</body>
|
|
413
|
-
</html>
|
|
414
|
-
`;
|
|
415
|
-
}
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
### Alpine.js Adapter
|
|
419
|
-
|
|
420
|
-
Declarative reactivity sprinkled on HTML.
|
|
421
|
-
|
|
422
|
-
```typescript
|
|
423
|
-
import { alpine, xData } from '@flightdev/ui/alpine';
|
|
424
|
-
|
|
425
|
-
const adapter = alpine({
|
|
426
|
-
plugins: ['intersect', 'persist'],
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
// x-data helper
|
|
430
|
-
const counter = xData({ count: 0 }, `
|
|
431
|
-
<div>
|
|
432
|
-
<button @click="count++">Increment</button>
|
|
433
|
-
<span x-text="count"></span>
|
|
434
|
-
</div>
|
|
435
|
-
`);
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
### Hotwire Adapter
|
|
439
|
-
|
|
440
|
-
Turbo + Stimulus for server-rendered applications.
|
|
441
|
-
|
|
442
|
-
```typescript
|
|
443
|
-
import { hotwire } from '@flightdev/ui/hotwire';
|
|
444
|
-
|
|
445
|
-
const adapter = hotwire({
|
|
446
|
-
turbo: true,
|
|
447
|
-
stimulus: true,
|
|
448
|
-
});
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
---
|
|
452
|
-
|
|
453
|
-
## Advanced Patterns
|
|
454
|
-
|
|
455
|
-
### Pattern 1: Multi-Framework Rendering
|
|
456
|
-
|
|
457
|
-
Render different parts of your application with different frameworks.
|
|
458
|
-
|
|
459
|
-
```typescript
|
|
460
|
-
import { adapterRegistry, registerBuiltinAdapters } from '@flightdev/ui';
|
|
461
|
-
|
|
462
|
-
registerBuiltinAdapters();
|
|
463
|
-
|
|
464
|
-
async function renderPage(request) {
|
|
465
|
-
// Use React for main content
|
|
466
|
-
const reactAdapter = await adapterRegistry.get('react');
|
|
467
|
-
const mainContent = await reactAdapter.renderToString({
|
|
468
|
-
component: MainApp,
|
|
469
|
-
props: { user },
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
// Use HTMX for dynamic sidebar
|
|
473
|
-
const htmxAdapter = await adapterRegistry.get('htmx');
|
|
474
|
-
const sidebar = await htmxAdapter.renderToString({
|
|
475
|
-
component: () => `
|
|
476
|
-
<aside hx-get="/api/notifications" hx-trigger="every 30s">
|
|
477
|
-
Loading...
|
|
478
|
-
</aside>
|
|
479
|
-
`,
|
|
480
|
-
props: {},
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
return `
|
|
484
|
-
<!DOCTYPE html>
|
|
485
|
-
<html>
|
|
486
|
-
<body>
|
|
487
|
-
<div id="main">${mainContent.html}</div>
|
|
488
|
-
${sidebar.html}
|
|
489
|
-
${reactAdapter.getHydrationScript(mainContent)}
|
|
490
|
-
${htmxAdapter.getHydrationScript(sidebar)}
|
|
491
|
-
</body>
|
|
492
|
-
</html>
|
|
493
|
-
`;
|
|
494
|
-
}
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
### Pattern 2: Progressive Enhancement
|
|
498
|
-
|
|
499
|
-
Start with HTML, enhance with JavaScript where needed.
|
|
500
|
-
|
|
501
|
-
```typescript
|
|
502
|
-
import { htmx } from '@flightdev/ui/htmx';
|
|
503
|
-
import { react } from '@flightdev/ui/react';
|
|
504
|
-
|
|
505
|
-
// Render base page with HTMX
|
|
506
|
-
const htmxAdapter = htmx();
|
|
507
|
-
const basePage = await htmxAdapter.renderToString({
|
|
508
|
-
component: () => `
|
|
509
|
-
<main>
|
|
510
|
-
<article>${articleContent}</article>
|
|
511
|
-
|
|
512
|
-
<!-- Island for interactive comments -->
|
|
513
|
-
<div id="comments-island"
|
|
514
|
-
data-flight-island="comments"
|
|
515
|
-
data-props='${JSON.stringify({ articleId })}'>
|
|
516
|
-
<noscript>
|
|
517
|
-
<!-- Fallback HTMX for no-JS -->
|
|
518
|
-
<div hx-get="/api/comments/${articleId}" hx-trigger="load">
|
|
519
|
-
Loading comments...
|
|
520
|
-
</div>
|
|
521
|
-
</noscript>
|
|
522
|
-
</div>
|
|
523
|
-
</main>
|
|
524
|
-
`,
|
|
525
|
-
props: {},
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
// Hydrate React island on client
|
|
529
|
-
// (handled by client-side code)
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
### Pattern 3: Capability-Based Adapter Selection
|
|
533
|
-
|
|
534
|
-
Select adapters based on required capabilities.
|
|
535
|
-
|
|
536
|
-
```typescript
|
|
537
|
-
import { adapterRegistry, registerBuiltinAdapters } from '@flightdev/ui';
|
|
538
|
-
|
|
539
|
-
registerBuiltinAdapters();
|
|
540
|
-
|
|
541
|
-
function getAdapterForFeatures(requirements) {
|
|
542
|
-
// List adapters with required capabilities
|
|
543
|
-
const streamingAdapters = adapterRegistry.listByCapability('streaming');
|
|
544
|
-
const islandAdapters = adapterRegistry.listByCapability('islands');
|
|
545
|
-
|
|
546
|
-
if (requirements.streaming && requirements.islands) {
|
|
547
|
-
// Need both - find intersection
|
|
548
|
-
const both = streamingAdapters.filter(a => islandAdapters.includes(a));
|
|
549
|
-
return both[0] || 'react'; // Default to React
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
if (requirements.streaming) {
|
|
553
|
-
return streamingAdapters[0] || 'react';
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
if (requirements.islands) {
|
|
557
|
-
return islandAdapters[0] || 'react';
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// Default to lightweight option
|
|
561
|
-
return 'htmx';
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const adapterId = getAdapterForFeatures({ streaming: true, islands: false });
|
|
565
|
-
const adapter = await adapterRegistry.get(adapterId);
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
### Pattern 4: Error Boundaries with Streaming
|
|
569
|
-
|
|
570
|
-
Handle errors gracefully during streaming SSR.
|
|
571
|
-
|
|
572
|
-
```typescript
|
|
573
|
-
import { react } from '@flightdev/ui/react';
|
|
574
|
-
|
|
575
|
-
const adapter = react();
|
|
576
|
-
|
|
577
|
-
const { stream, done, abort } = adapter.renderToStream(
|
|
578
|
-
{ component: App, props: {} },
|
|
579
|
-
{ url: '/page' },
|
|
580
|
-
{
|
|
581
|
-
onShellError: (error) => {
|
|
582
|
-
// Shell failed - return error page
|
|
583
|
-
console.error('Shell error:', error);
|
|
584
|
-
abort();
|
|
585
|
-
return renderErrorPage(500);
|
|
586
|
-
},
|
|
587
|
-
onError: (error) => {
|
|
588
|
-
// Error in Suspense boundary - log but continue
|
|
589
|
-
console.error('Render error:', error);
|
|
590
|
-
// HTMX fallback for failed section
|
|
591
|
-
},
|
|
592
|
-
onAllReady: () => {
|
|
593
|
-
console.log('Page fully rendered');
|
|
594
|
-
},
|
|
595
|
-
}
|
|
596
|
-
);
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
### Pattern 5: Caching SSR Results
|
|
600
|
-
|
|
601
|
-
Cache rendered HTML for frequently accessed pages.
|
|
602
|
-
|
|
603
|
-
```typescript
|
|
604
|
-
import { react } from '@flightdev/ui/react';
|
|
605
|
-
|
|
606
|
-
const adapter = react();
|
|
607
|
-
const cache = new Map();
|
|
608
|
-
|
|
609
|
-
async function renderWithCache(cacheKey, component, props, ttl = 60000) {
|
|
610
|
-
const cached = cache.get(cacheKey);
|
|
611
|
-
if (cached && Date.now() - cached.time < ttl) {
|
|
612
|
-
return cached.result;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
const result = await adapter.renderToString({
|
|
616
|
-
component,
|
|
617
|
-
props,
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
cache.set(cacheKey, {
|
|
621
|
-
result,
|
|
622
|
-
time: Date.now(),
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
return result;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// Usage
|
|
629
|
-
const html = await renderWithCache(
|
|
630
|
-
`product:${productId}`,
|
|
631
|
-
ProductPage,
|
|
632
|
-
{ productId },
|
|
633
|
-
5 * 60 * 1000 // 5 minute TTL
|
|
634
|
-
);
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
---
|
|
638
|
-
|
|
639
|
-
## Creating Custom Adapters
|
|
640
|
-
|
|
641
|
-
Extend `BaseUIAdapter` to create custom framework adapters.
|
|
642
|
-
|
|
643
|
-
### Minimal Implementation
|
|
644
|
-
|
|
645
|
-
```typescript
|
|
646
|
-
import { BaseUIAdapter } from '@flightdev/ui/core';
|
|
647
|
-
import type {
|
|
648
|
-
Component,
|
|
649
|
-
RenderResult,
|
|
650
|
-
RenderContext,
|
|
651
|
-
AdapterCapabilities
|
|
652
|
-
} from '@flightdev/ui/core/types';
|
|
653
|
-
|
|
654
|
-
export class MyFrameworkAdapter extends BaseUIAdapter {
|
|
655
|
-
readonly id = 'my-framework';
|
|
656
|
-
readonly name = 'My Framework';
|
|
657
|
-
readonly framework = 'my-framework';
|
|
658
|
-
readonly tier = 'tier-2' as const;
|
|
659
|
-
|
|
660
|
-
override readonly capabilities: AdapterCapabilities = {
|
|
661
|
-
streaming: false,
|
|
662
|
-
partialHydration: false,
|
|
663
|
-
islands: false,
|
|
664
|
-
resumable: false,
|
|
665
|
-
ssg: true,
|
|
666
|
-
csr: true,
|
|
667
|
-
serverComponents: false,
|
|
668
|
-
};
|
|
669
|
-
|
|
670
|
-
async renderToString(
|
|
671
|
-
component: Component,
|
|
672
|
-
context?: RenderContext
|
|
673
|
-
): Promise<RenderResult> {
|
|
674
|
-
const startTime = performance.now();
|
|
675
|
-
|
|
676
|
-
// Your framework's SSR logic
|
|
677
|
-
const html = await myFramework.render(
|
|
678
|
-
component.component,
|
|
679
|
-
component.props
|
|
680
|
-
);
|
|
681
|
-
|
|
682
|
-
return {
|
|
683
|
-
html,
|
|
684
|
-
hydrationData: {
|
|
685
|
-
props: component.props,
|
|
686
|
-
componentId: this.generateId(),
|
|
687
|
-
},
|
|
688
|
-
timing: this.createTiming(startTime),
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
getHydrationScript(result: RenderResult): string {
|
|
693
|
-
return `
|
|
694
|
-
<script>
|
|
695
|
-
window.__FLIGHT_DATA__ = ${this.serializeProps(result.hydrationData)};
|
|
696
|
-
// Your hydration initialization code
|
|
697
|
-
</script>
|
|
698
|
-
`.trim();
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
getClientEntry(): string {
|
|
702
|
-
return `
|
|
703
|
-
export function hydrate() {
|
|
704
|
-
const data = window.__FLIGHT_DATA__;
|
|
705
|
-
// Your hydration logic
|
|
706
|
-
}
|
|
707
|
-
`.trim();
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
export function myFramework(): MyFrameworkAdapter {
|
|
712
|
-
return new MyFrameworkAdapter();
|
|
713
|
-
}
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
### Adding Streaming Support
|
|
717
|
-
|
|
718
|
-
```typescript
|
|
719
|
-
import { StreamingRenderResult, StreamingOptions } from '@flightdev/ui/core/types';
|
|
720
|
-
|
|
721
|
-
export class MyStreamingAdapter extends BaseUIAdapter {
|
|
722
|
-
// ... base implementation ...
|
|
723
|
-
|
|
724
|
-
override readonly capabilities: AdapterCapabilities = {
|
|
725
|
-
streaming: true, // Enable streaming
|
|
726
|
-
// ... other capabilities
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
renderToStream(
|
|
730
|
-
component: Component,
|
|
731
|
-
context?: RenderContext,
|
|
732
|
-
options?: StreamingOptions
|
|
733
|
-
): StreamingRenderResult {
|
|
734
|
-
let resolvePromise: () => void;
|
|
735
|
-
let rejectPromise: (error: Error) => void;
|
|
736
|
-
|
|
737
|
-
const done = new Promise<void>((resolve, reject) => {
|
|
738
|
-
resolvePromise = resolve;
|
|
739
|
-
rejectPromise = reject;
|
|
740
|
-
});
|
|
741
|
-
|
|
742
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
743
|
-
start: async (controller) => {
|
|
744
|
-
try {
|
|
745
|
-
// Your streaming logic
|
|
746
|
-
const chunks = myFramework.renderStream(component);
|
|
747
|
-
|
|
748
|
-
for await (const chunk of chunks) {
|
|
749
|
-
controller.enqueue(new TextEncoder().encode(chunk));
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
controller.close();
|
|
753
|
-
resolvePromise();
|
|
754
|
-
} catch (error) {
|
|
755
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
756
|
-
options?.onError?.(err);
|
|
757
|
-
controller.error(err);
|
|
758
|
-
rejectPromise(err);
|
|
759
|
-
}
|
|
760
|
-
},
|
|
761
|
-
});
|
|
762
|
-
|
|
763
|
-
return {
|
|
764
|
-
stream,
|
|
765
|
-
done,
|
|
766
|
-
abort: () => {
|
|
767
|
-
resolvePromise();
|
|
768
|
-
},
|
|
769
|
-
};
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
### Registering Custom Adapters
|
|
775
|
-
|
|
776
|
-
```typescript
|
|
777
|
-
import { adapterRegistry } from '@flightdev/ui';
|
|
778
|
-
import { myFramework } from './my-framework-adapter';
|
|
779
|
-
|
|
780
|
-
// Register your adapter
|
|
781
|
-
adapterRegistry.register('my-framework', () => myFramework());
|
|
782
|
-
|
|
783
|
-
// Use it
|
|
784
|
-
const adapter = await adapterRegistry.get('my-framework');
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
---
|
|
788
|
-
|
|
789
|
-
## Performance Considerations
|
|
790
|
-
|
|
791
|
-
### Streaming vs String Rendering
|
|
792
|
-
|
|
793
|
-
| Approach | TTFB | Total Time | Memory | Use Case |
|
|
794
|
-
|----------|------|------------|--------|----------|
|
|
795
|
-
| renderToString | Slower | Faster | Higher | Small pages, caching |
|
|
796
|
-
| renderToStream | Faster | Slightly slower | Lower | Large pages, real-time |
|
|
797
|
-
|
|
798
|
-
**Recommendation**: Use streaming for pages with async data or Suspense boundaries.
|
|
799
|
-
|
|
800
|
-
### Hydration Strategies
|
|
801
|
-
|
|
802
|
-
| Strategy | Bundle Size | Interactivity | Best For |
|
|
803
|
-
|----------|-------------|---------------|----------|
|
|
804
|
-
| Full | Largest | Immediate | SPAs, complex UIs |
|
|
805
|
-
| Progressive | Medium | Gradual | Content sites |
|
|
806
|
-
| Islands | Smallest | Per-component | Mostly static sites |
|
|
807
|
-
| None (HTMX) | Minimal | Server-roundtrip | CRUD applications |
|
|
808
|
-
|
|
809
|
-
### Bundle Size by Tier
|
|
810
|
-
|
|
811
|
-
| Tier | Typical JS Bundle | Example |
|
|
812
|
-
|------|-------------------|---------|
|
|
813
|
-
| Tier 1 | 50-150 KB | React, Vue, Angular |
|
|
814
|
-
| Tier 2 | 10-50 KB | Preact, Lit, Inferno |
|
|
815
|
-
| Tier 3 | 0-15 KB | HTMX, Alpine |
|
|
816
|
-
|
|
817
|
-
---
|
|
818
|
-
|
|
819
|
-
## Migration Guide
|
|
820
|
-
|
|
821
|
-
### From React to Preact
|
|
822
|
-
|
|
823
|
-
```diff
|
|
824
|
-
- import { react } from '@flightdev/ui/react';
|
|
825
|
-
+ import { preact } from '@flightdev/ui/preact';
|
|
826
|
-
|
|
827
|
-
- export default defineUI(react());
|
|
828
|
-
+ export default defineUI(preact());
|
|
829
|
-
```
|
|
830
|
-
|
|
831
|
-
Component compatibility: Most React components work in Preact. Check for:
|
|
832
|
-
- Class components (prefer functional)
|
|
833
|
-
- React-specific hooks (useId, useSyncExternalStore)
|
|
834
|
-
- Concurrent features (Suspense for data)
|
|
835
|
-
|
|
836
|
-
### From Vue to Svelte
|
|
837
|
-
|
|
838
|
-
Both have similar reactivity models but different syntax.
|
|
839
|
-
|
|
840
|
-
```diff
|
|
841
|
-
- import { vue } from '@flightdev/ui/vue';
|
|
842
|
-
+ import { svelte } from '@flightdev/ui/svelte';
|
|
843
|
-
|
|
844
|
-
- export default defineUI(vue());
|
|
845
|
-
+ export default defineUI(svelte());
|
|
846
|
-
```
|
|
847
|
-
|
|
848
|
-
### From Any Framework to HTMX
|
|
849
|
-
|
|
850
|
-
For applications that can move state to the server:
|
|
851
|
-
|
|
852
|
-
1. Identify interactive sections
|
|
853
|
-
2. Replace with `hx-*` attributes
|
|
854
|
-
3. Create server endpoints for each interaction
|
|
855
|
-
4. Remove client-side state management
|
|
856
|
-
|
|
857
|
-
```diff
|
|
858
|
-
- // React component with client state
|
|
859
|
-
- function Counter() {
|
|
860
|
-
- const [count, setCount] = useState(0);
|
|
861
|
-
- return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
|
|
862
|
-
- }
|
|
863
|
-
|
|
864
|
-
+ // HTMX with server state
|
|
865
|
-
+ // Server endpoint: POST /api/counter increments session counter
|
|
866
|
-
+ function Counter({ count }) {
|
|
867
|
-
+ return `
|
|
868
|
-
+ <button hx-post="/api/counter" hx-swap="outerHTML">
|
|
869
|
-
+ ${count}
|
|
870
|
-
+ </button>
|
|
871
|
-
+ `;
|
|
872
|
-
+ }
|
|
873
|
-
```
|
|
874
|
-
|
|
875
|
-
---
|
|
876
|
-
|
|
877
|
-
## API Reference
|
|
878
|
-
|
|
879
|
-
### UIAdapterV2 Interface
|
|
880
|
-
|
|
881
|
-
All adapters implement this interface.
|
|
882
|
-
|
|
883
|
-
```typescript
|
|
884
|
-
interface UIAdapterV2 {
|
|
885
|
-
// Identification
|
|
886
|
-
readonly id: string;
|
|
887
|
-
readonly name: string;
|
|
888
|
-
readonly framework: string;
|
|
889
|
-
readonly frameworkVersion?: string;
|
|
890
|
-
readonly tier: AdapterTier;
|
|
891
|
-
readonly capabilities: AdapterCapabilities;
|
|
892
|
-
|
|
893
|
-
// Core rendering (required)
|
|
894
|
-
renderToString(component: Component, context?: RenderContext): Promise<RenderResult>;
|
|
895
|
-
getHydrationScript(result: RenderResult): string;
|
|
896
|
-
getClientEntry(): string;
|
|
897
|
-
|
|
898
|
-
// Streaming (optional)
|
|
899
|
-
renderToStream?(component: Component, context?: RenderContext, options?: StreamingOptions): StreamingRenderResult;
|
|
900
|
-
|
|
901
|
-
// Islands (optional)
|
|
902
|
-
createIsland?(component: unknown, props?: Record<string, unknown>, options?: IslandOptions): Island;
|
|
903
|
-
hydrateIsland?(element: Element, island: Island): void;
|
|
904
|
-
|
|
905
|
-
// Resumability (optional)
|
|
906
|
-
serializeState?(state: unknown): string;
|
|
907
|
-
resumeFromState?(serialized: string): unknown;
|
|
908
|
-
|
|
909
|
-
// Lifecycle (optional)
|
|
910
|
-
init?(): Promise<void>;
|
|
911
|
-
dispose?(): Promise<void>;
|
|
912
|
-
}
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
### AdapterCapabilities
|
|
916
|
-
|
|
917
|
-
```typescript
|
|
918
|
-
interface AdapterCapabilities {
|
|
919
|
-
streaming: boolean; // renderToStream support
|
|
920
|
-
partialHydration: boolean; // Selective hydration
|
|
921
|
-
islands: boolean; // Islands architecture
|
|
922
|
-
resumable: boolean; // Qwik-style resumability
|
|
923
|
-
ssg: boolean; // Static generation
|
|
924
|
-
csr: boolean; // Client-side rendering
|
|
925
|
-
serverComponents: boolean; // RSC support
|
|
926
|
-
}
|
|
927
|
-
```
|
|
928
|
-
|
|
929
|
-
### AdapterRegistry
|
|
930
|
-
|
|
931
|
-
```typescript
|
|
932
|
-
// Get adapter by ID
|
|
933
|
-
const adapter = await adapterRegistry.get('react');
|
|
934
|
-
|
|
935
|
-
// Check if adapter exists
|
|
936
|
-
const hasVue = adapterRegistry.has('vue');
|
|
937
|
-
|
|
938
|
-
// List adapters by tier
|
|
939
|
-
const tier1 = adapterRegistry.listByTier('tier-1');
|
|
940
|
-
|
|
941
|
-
// List adapters by capability
|
|
942
|
-
const streaming = adapterRegistry.listByCapability('streaming');
|
|
943
|
-
|
|
944
|
-
// Register custom adapter
|
|
945
|
-
adapterRegistry.register('custom', () => new CustomAdapter());
|
|
946
|
-
```
|