@flightdev/ui 2.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/.turbo/turbo-build.log +81 -0
- package/.turbo/turbo-lint.log +40 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/TESTING.md +124 -0
- package/dist/adapter-MMD-iHNx.d.ts +424 -0
- package/dist/adapters/tier-1/angular.d.ts +60 -0
- package/dist/adapters/tier-1/angular.js +2 -0
- package/dist/adapters/tier-1/index.d.ts +7 -0
- package/dist/adapters/tier-1/index.js +7 -0
- package/dist/adapters/tier-1/qwik.d.ts +55 -0
- package/dist/adapters/tier-1/qwik.js +2 -0
- package/dist/adapters/tier-1/react.d.ts +67 -0
- package/dist/adapters/tier-1/react.js +2 -0
- package/dist/adapters/tier-1/solid.d.ts +45 -0
- package/dist/adapters/tier-1/solid.js +2 -0
- package/dist/adapters/tier-1/svelte.d.ts +48 -0
- package/dist/adapters/tier-1/svelte.js +2 -0
- package/dist/adapters/tier-1/vue.d.ts +47 -0
- package/dist/adapters/tier-1/vue.js +2 -0
- package/dist/adapters/tier-2/index.d.ts +7 -0
- package/dist/adapters/tier-2/index.js +7 -0
- package/dist/adapters/tier-2/inferno.d.ts +31 -0
- package/dist/adapters/tier-2/inferno.js +2 -0
- package/dist/adapters/tier-2/lit.d.ts +34 -0
- package/dist/adapters/tier-2/lit.js +2 -0
- package/dist/adapters/tier-2/marko.d.ts +59 -0
- package/dist/adapters/tier-2/marko.js +2 -0
- package/dist/adapters/tier-2/mithril.d.ts +31 -0
- package/dist/adapters/tier-2/mithril.js +2 -0
- package/dist/adapters/tier-2/preact.d.ts +33 -0
- package/dist/adapters/tier-2/preact.js +2 -0
- package/dist/adapters/tier-2/stencil.d.ts +52 -0
- package/dist/adapters/tier-2/stencil.js +2 -0
- package/dist/adapters/tier-3/alpine.d.ts +73 -0
- package/dist/adapters/tier-3/alpine.js +2 -0
- package/dist/adapters/tier-3/hotwire.d.ts +71 -0
- package/dist/adapters/tier-3/hotwire.js +2 -0
- package/dist/adapters/tier-3/htmx.d.ts +88 -0
- package/dist/adapters/tier-3/htmx.js +2 -0
- package/dist/adapters/tier-3/index.d.ts +7 -0
- package/dist/adapters/tier-3/index.js +7 -0
- package/dist/adapters/tier-3/petite-vue.d.ts +56 -0
- package/dist/adapters/tier-3/petite-vue.js +2 -0
- package/dist/adapters/tier-3/stimulus.d.ts +63 -0
- package/dist/adapters/tier-3/stimulus.js +2 -0
- package/dist/adapters/tier-3/vanilla.d.ts +63 -0
- package/dist/adapters/tier-3/vanilla.js +2 -0
- package/dist/chunk-2SNQ6PTM.js +217 -0
- package/dist/chunk-3D4XMIZI.js +136 -0
- package/dist/chunk-3HU6GSQ4.js +125 -0
- package/dist/chunk-4PZDNFL7.js +148 -0
- package/dist/chunk-5IBLFTYL.js +114 -0
- package/dist/chunk-64JZJ7OK.js +142 -0
- package/dist/chunk-7ZJI3QU2.js +132 -0
- package/dist/chunk-CE4FJHQJ.js +133 -0
- package/dist/chunk-DTCAUBH5.js +87 -0
- package/dist/chunk-NTASPOHG.js +106 -0
- package/dist/chunk-OI2AMQLG.js +152 -0
- package/dist/chunk-Q7HUE44H.js +106 -0
- package/dist/chunk-QH3LOWXU.js +155 -0
- package/dist/chunk-QIVAK6BH.js +103 -0
- package/dist/chunk-V34XPVGK.js +103 -0
- package/dist/chunk-VK7ZPMO7.js +221 -0
- package/dist/chunk-X6CNUW6T.js +136 -0
- package/dist/chunk-XTDK7ME5.js +382 -0
- package/dist/chunk-YFGSHW5S.js +121 -0
- package/dist/chunk-ZAJVSE7J.js +90 -0
- package/dist/core/index.d.ts +161 -0
- package/dist/core/index.js +2 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +71 -0
- package/docs/ADAPTERS.md +946 -0
- package/docs/PATTERNS.md +836 -0
- package/package.json +229 -0
- package/src/adapters/tier-1/angular.ts +223 -0
- package/src/adapters/tier-1/index.ts +12 -0
- package/src/adapters/tier-1/qwik.ts +177 -0
- package/src/adapters/tier-1/react.ts +330 -0
- package/src/adapters/tier-1/solid.ts +222 -0
- package/src/adapters/tier-1/svelte.ts +211 -0
- package/src/adapters/tier-1/vue.ts +234 -0
- package/src/adapters/tier-2/index.ts +12 -0
- package/src/adapters/tier-2/inferno.ts +149 -0
- package/src/adapters/tier-2/lit.ts +191 -0
- package/src/adapters/tier-2/marko.ts +199 -0
- package/src/adapters/tier-2/mithril.ts +152 -0
- package/src/adapters/tier-2/preact.ts +133 -0
- package/src/adapters/tier-2/stencil.ts +214 -0
- package/src/adapters/tier-3/alpine.ts +218 -0
- package/src/adapters/tier-3/hotwire.ts +254 -0
- package/src/adapters/tier-3/htmx.ts +263 -0
- package/src/adapters/tier-3/index.ts +12 -0
- package/src/adapters/tier-3/petite-vue.ts +163 -0
- package/src/adapters/tier-3/stimulus.ts +233 -0
- package/src/adapters/tier-3/vanilla.ts +252 -0
- package/src/ambient.d.ts +310 -0
- package/src/core/adapter.ts +366 -0
- package/src/core/index.ts +56 -0
- package/src/core/registry.ts +518 -0
- package/src/core/types.ts +461 -0
- package/src/htmx.ts +134 -0
- package/src/index.ts +263 -0
- package/test/__mocks__/stencil-core.ts +19 -0
- package/test/__mocks__/stencil-hydrate.ts +15 -0
- package/test/adapters/tier-1.test.ts +206 -0
- package/test/adapters/tier-2.test.ts +175 -0
- package/test/adapters/tier-3.test.ts +284 -0
- package/test/contracts/adapter.contract.ts +293 -0
- package/test/core/core.test.ts +310 -0
- package/test/errors/error-handling.test.ts +454 -0
- package/test/integration/htmx.integration.test.ts +246 -0
- package/test/integration/react.integration.test.ts +271 -0
- package/test/integration/registry.integration.test.ts +308 -0
- package/tsconfig.json +22 -0
- package/tsup.config.ts +93 -0
- package/vitest.config.ts +101 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/ui - Core Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Universal type system for all UI framework adapters.
|
|
5
|
+
* Supports SSR, streaming, islands architecture, and resumable hydration.
|
|
6
|
+
*
|
|
7
|
+
* @module @flightdev/ui/core/types
|
|
8
|
+
* @version 2.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Capability System
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Adapter capabilities flags.
|
|
17
|
+
* Used for feature detection and optimization decisions.
|
|
18
|
+
*/
|
|
19
|
+
export interface AdapterCapabilities {
|
|
20
|
+
/** Supports streaming SSR via renderToStream */
|
|
21
|
+
streaming: boolean;
|
|
22
|
+
|
|
23
|
+
/** Supports partial/selective hydration */
|
|
24
|
+
partialHydration: boolean;
|
|
25
|
+
|
|
26
|
+
/** Supports islands architecture */
|
|
27
|
+
islands: boolean;
|
|
28
|
+
|
|
29
|
+
/** Supports resumable hydration (Qwik-style serialization) */
|
|
30
|
+
resumable: boolean;
|
|
31
|
+
|
|
32
|
+
/** Supports Static Site Generation */
|
|
33
|
+
ssg: boolean;
|
|
34
|
+
|
|
35
|
+
/** Supports Client-Side Rendering only */
|
|
36
|
+
csr: boolean;
|
|
37
|
+
|
|
38
|
+
/** Supports Server Components (React Server Components, etc) */
|
|
39
|
+
serverComponents: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Default capabilities for adapters that don't specify their own
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_CAPABILITIES: AdapterCapabilities = {
|
|
46
|
+
streaming: false,
|
|
47
|
+
partialHydration: false,
|
|
48
|
+
islands: false,
|
|
49
|
+
resumable: false,
|
|
50
|
+
ssg: true,
|
|
51
|
+
csr: true,
|
|
52
|
+
serverComponents: false,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Tier Classification
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Adapter tier classification.
|
|
61
|
+
*
|
|
62
|
+
* - tier-1: Full support, first-class citizens (React, Vue, Angular, Svelte, Solid, Qwik)
|
|
63
|
+
* - tier-2: SSR + Hydration support (Preact, Lit, Marko, Stencil, Mithril, Inferno)
|
|
64
|
+
* - tier-3: HTML-first, minimal JS (HTMX, Alpine, Hotwire, Stimulus, Web Components)
|
|
65
|
+
*/
|
|
66
|
+
export type AdapterTier = 'tier-1' | 'tier-2' | 'tier-3';
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Tier metadata for documentation and tooling
|
|
70
|
+
*/
|
|
71
|
+
export interface TierInfo {
|
|
72
|
+
tier: AdapterTier;
|
|
73
|
+
name: string;
|
|
74
|
+
description: string;
|
|
75
|
+
features: string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const TIER_INFO: Record<AdapterTier, TierInfo> = {
|
|
79
|
+
'tier-1': {
|
|
80
|
+
tier: 'tier-1',
|
|
81
|
+
name: 'Full Support',
|
|
82
|
+
description: 'Complete SSR, streaming, hydration, and advanced features',
|
|
83
|
+
features: ['SSR', 'Streaming', 'Hydration', 'Islands', 'DevTools'],
|
|
84
|
+
},
|
|
85
|
+
'tier-2': {
|
|
86
|
+
tier: 'tier-2',
|
|
87
|
+
name: 'Standard Support',
|
|
88
|
+
description: 'SSR and hydration with framework-specific optimizations',
|
|
89
|
+
features: ['SSR', 'Hydration', 'Partial Islands'],
|
|
90
|
+
},
|
|
91
|
+
'tier-3': {
|
|
92
|
+
tier: 'tier-3',
|
|
93
|
+
name: 'HTML-First',
|
|
94
|
+
description: 'Server-rendered HTML with minimal JavaScript enhancement',
|
|
95
|
+
features: ['SSR', 'Progressive Enhancement'],
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// Component & Rendering Types
|
|
101
|
+
// ============================================================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Universal component wrapper type
|
|
105
|
+
*/
|
|
106
|
+
export interface Component<P = Record<string, unknown>> {
|
|
107
|
+
/** The actual component (React element, Vue component, HTML string, etc) */
|
|
108
|
+
component: unknown;
|
|
109
|
+
|
|
110
|
+
/** Props to pass to the component */
|
|
111
|
+
props?: P;
|
|
112
|
+
|
|
113
|
+
/** Slots/children content */
|
|
114
|
+
children?: unknown;
|
|
115
|
+
|
|
116
|
+
/** Component identifier for hydration */
|
|
117
|
+
id?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Render context passed to adapters
|
|
122
|
+
*/
|
|
123
|
+
export interface RenderContext {
|
|
124
|
+
/** Current request URL */
|
|
125
|
+
url?: string;
|
|
126
|
+
|
|
127
|
+
/** Request headers */
|
|
128
|
+
headers?: Headers;
|
|
129
|
+
|
|
130
|
+
/** Server-side cookies */
|
|
131
|
+
cookies?: Map<string, string>;
|
|
132
|
+
|
|
133
|
+
/** Locale for i18n */
|
|
134
|
+
locale?: string;
|
|
135
|
+
|
|
136
|
+
/** Custom context data */
|
|
137
|
+
data?: Record<string, unknown>;
|
|
138
|
+
|
|
139
|
+
/** AbortSignal for cancellation */
|
|
140
|
+
signal?: AbortSignal;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Result of a render operation
|
|
145
|
+
*/
|
|
146
|
+
export interface RenderResult {
|
|
147
|
+
/** The rendered HTML string */
|
|
148
|
+
html: string;
|
|
149
|
+
|
|
150
|
+
/** Head elements (meta, title, link, etc) */
|
|
151
|
+
head?: string;
|
|
152
|
+
|
|
153
|
+
/** Hydration data to pass to client */
|
|
154
|
+
hydrationData?: Record<string, unknown>;
|
|
155
|
+
|
|
156
|
+
/** CSS to inline in the document */
|
|
157
|
+
css?: string;
|
|
158
|
+
|
|
159
|
+
/** Scripts to inject */
|
|
160
|
+
scripts?: string[];
|
|
161
|
+
|
|
162
|
+
/** HTTP status code (for error pages, redirects) */
|
|
163
|
+
status?: number;
|
|
164
|
+
|
|
165
|
+
/** HTTP headers to set */
|
|
166
|
+
headers?: Record<string, string>;
|
|
167
|
+
|
|
168
|
+
/** Timing information for performance monitoring */
|
|
169
|
+
timing?: RenderTiming;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Render timing information
|
|
174
|
+
*/
|
|
175
|
+
export interface RenderTiming {
|
|
176
|
+
/** Total render time in milliseconds */
|
|
177
|
+
total: number;
|
|
178
|
+
|
|
179
|
+
/** Time to first byte */
|
|
180
|
+
ttfb?: number;
|
|
181
|
+
|
|
182
|
+
/** Component render times */
|
|
183
|
+
components?: Map<string, number>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Streaming Types
|
|
188
|
+
// ============================================================================
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Streaming render options
|
|
192
|
+
*/
|
|
193
|
+
export interface StreamingOptions {
|
|
194
|
+
/** Emit shell HTML before async content resolves */
|
|
195
|
+
emitShellEarly?: boolean;
|
|
196
|
+
|
|
197
|
+
/** Timeout for streaming (ms) */
|
|
198
|
+
timeout?: number;
|
|
199
|
+
|
|
200
|
+
/** Callback when shell is ready */
|
|
201
|
+
onShellReady?: () => void;
|
|
202
|
+
|
|
203
|
+
/** Callback when shell errors */
|
|
204
|
+
onShellError?: (error: Error) => void;
|
|
205
|
+
|
|
206
|
+
/** Callback when all content is ready */
|
|
207
|
+
onAllReady?: () => void;
|
|
208
|
+
|
|
209
|
+
/** Callback for each error during streaming */
|
|
210
|
+
onError?: (error: Error, errorInfo?: unknown) => void;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Streaming render result
|
|
215
|
+
*/
|
|
216
|
+
export interface StreamingRenderResult {
|
|
217
|
+
/** The readable stream of HTML chunks */
|
|
218
|
+
stream: ReadableStream<Uint8Array>;
|
|
219
|
+
|
|
220
|
+
/** Promise that resolves when streaming is complete */
|
|
221
|
+
done: Promise<void>;
|
|
222
|
+
|
|
223
|
+
/** Abort the stream */
|
|
224
|
+
abort: () => void;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Islands Architecture Types
|
|
229
|
+
// ============================================================================
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Island hydration strategy
|
|
233
|
+
*/
|
|
234
|
+
export type IslandHydrationStrategy =
|
|
235
|
+
| 'load' // Hydrate immediately on page load
|
|
236
|
+
| 'idle' // Hydrate when browser is idle
|
|
237
|
+
| 'visible' // Hydrate when island becomes visible
|
|
238
|
+
| 'media' // Hydrate based on media query
|
|
239
|
+
| 'interaction' // Hydrate on first interaction
|
|
240
|
+
| 'none'; // Never hydrate (static island)
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Island configuration options
|
|
244
|
+
*/
|
|
245
|
+
export interface IslandOptions {
|
|
246
|
+
/** Hydration strategy */
|
|
247
|
+
hydrate?: IslandHydrationStrategy;
|
|
248
|
+
|
|
249
|
+
/** For 'media' strategy: the media query */
|
|
250
|
+
media?: string;
|
|
251
|
+
|
|
252
|
+
/** For 'interaction' strategy: events to listen for */
|
|
253
|
+
on?: string[];
|
|
254
|
+
|
|
255
|
+
/** Island priority (higher = hydrate first) */
|
|
256
|
+
priority?: number;
|
|
257
|
+
|
|
258
|
+
/** Fallback content while loading */
|
|
259
|
+
fallback?: string;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Island definition
|
|
264
|
+
*/
|
|
265
|
+
export interface Island {
|
|
266
|
+
/** Unique island identifier */
|
|
267
|
+
id: string;
|
|
268
|
+
|
|
269
|
+
/** The component to render */
|
|
270
|
+
component: unknown;
|
|
271
|
+
|
|
272
|
+
/** Component props */
|
|
273
|
+
props?: Record<string, unknown>;
|
|
274
|
+
|
|
275
|
+
/** Island options */
|
|
276
|
+
options?: IslandOptions;
|
|
277
|
+
|
|
278
|
+
/** Rendered HTML placeholder */
|
|
279
|
+
placeholder?: string;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ============================================================================
|
|
283
|
+
// Adapter Interface (V2)
|
|
284
|
+
// ============================================================================
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Universal UI Adapter Interface (Version 2)
|
|
288
|
+
*
|
|
289
|
+
* All UI framework adapters must implement this interface.
|
|
290
|
+
* Optional methods are for advanced features (streaming, islands, etc).
|
|
291
|
+
*/
|
|
292
|
+
export interface UIAdapterV2 {
|
|
293
|
+
// === Identification ===
|
|
294
|
+
|
|
295
|
+
/** Unique adapter identifier (e.g., 'react', 'vue', 'angular') */
|
|
296
|
+
readonly id: string;
|
|
297
|
+
|
|
298
|
+
/** Human-readable adapter name */
|
|
299
|
+
readonly name: string;
|
|
300
|
+
|
|
301
|
+
/** Framework name */
|
|
302
|
+
readonly framework: string;
|
|
303
|
+
|
|
304
|
+
/** Framework version compatibility */
|
|
305
|
+
readonly frameworkVersion?: string;
|
|
306
|
+
|
|
307
|
+
/** Adapter tier classification */
|
|
308
|
+
readonly tier: AdapterTier;
|
|
309
|
+
|
|
310
|
+
/** Supported capabilities */
|
|
311
|
+
readonly capabilities: AdapterCapabilities;
|
|
312
|
+
|
|
313
|
+
// === Core Rendering (Required) ===
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Render a component to an HTML string.
|
|
317
|
+
* This is the primary SSR method.
|
|
318
|
+
*/
|
|
319
|
+
renderToString(
|
|
320
|
+
component: Component,
|
|
321
|
+
context?: RenderContext
|
|
322
|
+
): Promise<RenderResult>;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Generate the hydration script for client-side hydration.
|
|
326
|
+
*/
|
|
327
|
+
getHydrationScript(result: RenderResult): string;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get the client-side entry point code.
|
|
331
|
+
*/
|
|
332
|
+
getClientEntry(): string;
|
|
333
|
+
|
|
334
|
+
// === Streaming SSR (Optional) ===
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Render a component as a stream (for streaming SSR).
|
|
338
|
+
* Only available if capabilities.streaming is true.
|
|
339
|
+
*/
|
|
340
|
+
renderToStream?(
|
|
341
|
+
component: Component,
|
|
342
|
+
context?: RenderContext,
|
|
343
|
+
options?: StreamingOptions
|
|
344
|
+
): StreamingRenderResult;
|
|
345
|
+
|
|
346
|
+
// === Islands Architecture (Optional) ===
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Create an island wrapper for partial hydration.
|
|
350
|
+
* Only available if capabilities.islands is true.
|
|
351
|
+
*/
|
|
352
|
+
createIsland?(
|
|
353
|
+
component: unknown,
|
|
354
|
+
props?: Record<string, unknown>,
|
|
355
|
+
options?: IslandOptions
|
|
356
|
+
): Island;
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Hydrate an island on the client.
|
|
360
|
+
*/
|
|
361
|
+
hydrateIsland?(
|
|
362
|
+
element: Element,
|
|
363
|
+
island: Island
|
|
364
|
+
): void;
|
|
365
|
+
|
|
366
|
+
// === Resumability (Optional) ===
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Serialize component state for resumable hydration.
|
|
370
|
+
* Only available if capabilities.resumable is true.
|
|
371
|
+
*/
|
|
372
|
+
serializeState?(state: unknown): string;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Resume from serialized state.
|
|
376
|
+
*/
|
|
377
|
+
resumeFromState?(serialized: string): unknown;
|
|
378
|
+
|
|
379
|
+
// === Lifecycle (Optional) ===
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Initialize the adapter (load dependencies, warm up caches, etc).
|
|
383
|
+
*/
|
|
384
|
+
init?(): Promise<void>;
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Clean up resources.
|
|
388
|
+
*/
|
|
389
|
+
dispose?(): Promise<void>;
|
|
390
|
+
|
|
391
|
+
// === Transform (Optional) ===
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Transform a component for SSR if needed.
|
|
395
|
+
*/
|
|
396
|
+
transformForSSR?(component: unknown): unknown;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ============================================================================
|
|
400
|
+
// Legacy Compatibility (V1 Adapter)
|
|
401
|
+
// ============================================================================
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Legacy V1 adapter interface for backward compatibility.
|
|
405
|
+
* @deprecated Use UIAdapterV2 instead
|
|
406
|
+
*/
|
|
407
|
+
export interface UIAdapterV1 {
|
|
408
|
+
readonly name: string;
|
|
409
|
+
readonly framework: string;
|
|
410
|
+
renderToString(component: Component): Promise<RenderResult>;
|
|
411
|
+
getHydrationScript(result: RenderResult): string;
|
|
412
|
+
getClientEntry(): string;
|
|
413
|
+
transformForSSR?(component: unknown): unknown;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Type alias for backward compatibility
|
|
418
|
+
* @deprecated Use UIAdapterV2 instead
|
|
419
|
+
*/
|
|
420
|
+
export type UIAdapter = UIAdapterV1;
|
|
421
|
+
|
|
422
|
+
// ============================================================================
|
|
423
|
+
// Configuration Types
|
|
424
|
+
// ============================================================================
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* UI configuration for Flight
|
|
428
|
+
*/
|
|
429
|
+
export interface UIConfig {
|
|
430
|
+
/** The UI adapter to use */
|
|
431
|
+
adapter: UIAdapterV2 | UIAdapterV1;
|
|
432
|
+
|
|
433
|
+
/** Enable streaming SSR */
|
|
434
|
+
streaming?: boolean;
|
|
435
|
+
|
|
436
|
+
/** Hydration strategy */
|
|
437
|
+
hydration?: 'full' | 'partial' | 'progressive' | 'none';
|
|
438
|
+
|
|
439
|
+
/** Islands mode */
|
|
440
|
+
islands?: boolean;
|
|
441
|
+
|
|
442
|
+
/** Default island hydration strategy */
|
|
443
|
+
defaultIslandStrategy?: IslandHydrationStrategy;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// ============================================================================
|
|
447
|
+
// Utility Types
|
|
448
|
+
// ============================================================================
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Extract props type from a component
|
|
452
|
+
*/
|
|
453
|
+
export type ComponentProps<C> = C extends Component<infer P> ? P : never;
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Check if adapter supports a capability
|
|
457
|
+
*/
|
|
458
|
+
export type HasCapability<
|
|
459
|
+
A extends UIAdapterV2,
|
|
460
|
+
C extends keyof AdapterCapabilities
|
|
461
|
+
> = A['capabilities'][C] extends true ? true : false;
|
package/src/htmx.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/ui/htmx - HTMX Integration Adapter
|
|
3
|
+
*
|
|
4
|
+
* Server-driven UI with HTMX - no JavaScript framework needed.
|
|
5
|
+
* Perfect for hypermedia-driven applications.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { htmx } from '@flightdev/ui/htmx';
|
|
10
|
+
* import { defineUI } from '@flightdev/ui';
|
|
11
|
+
*
|
|
12
|
+
* export default defineUI(htmx({
|
|
13
|
+
* extensions: ['json-enc', 'loading-states'],
|
|
14
|
+
* }));
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { UIAdapter, RenderResult, Component } from './index.js';
|
|
19
|
+
|
|
20
|
+
export interface HTMXAdapterOptions {
|
|
21
|
+
/** HTMX version to use */
|
|
22
|
+
version?: string;
|
|
23
|
+
/** HTMX extensions to include */
|
|
24
|
+
extensions?: string[];
|
|
25
|
+
/** Enable WebSocket support */
|
|
26
|
+
websocket?: boolean;
|
|
27
|
+
/** Enable SSE support */
|
|
28
|
+
sse?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create an HTMX UI adapter
|
|
33
|
+
*/
|
|
34
|
+
export function htmx(options: HTMXAdapterOptions = {}): UIAdapter {
|
|
35
|
+
const {
|
|
36
|
+
version = '2.0.0',
|
|
37
|
+
extensions = [],
|
|
38
|
+
websocket = false,
|
|
39
|
+
sse = false,
|
|
40
|
+
} = options;
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
name: 'htmx',
|
|
44
|
+
framework: 'htmx',
|
|
45
|
+
|
|
46
|
+
async renderToString(component: Component): Promise<RenderResult> {
|
|
47
|
+
// HTMX components are just HTML strings or template functions
|
|
48
|
+
let html: string;
|
|
49
|
+
|
|
50
|
+
if (typeof component.component === 'function') {
|
|
51
|
+
html = (component.component as (props: any) => string)(component.props ?? {});
|
|
52
|
+
} else if (typeof component.component === 'string') {
|
|
53
|
+
html = component.component;
|
|
54
|
+
} else {
|
|
55
|
+
throw new Error('HTMX components must be strings or template functions');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
html,
|
|
60
|
+
// No hydration data needed - HTMX is server-driven
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
getHydrationScript(_result: RenderResult): string {
|
|
65
|
+
const extScripts = extensions
|
|
66
|
+
.map(ext => `<script src="https://unpkg.com/htmx.org@${version}/dist/ext/${ext}.js"></script>`)
|
|
67
|
+
.join('\n');
|
|
68
|
+
|
|
69
|
+
const wsScript = websocket
|
|
70
|
+
? `<script src="https://unpkg.com/htmx.org@${version}/dist/ext/ws.js"></script>`
|
|
71
|
+
: '';
|
|
72
|
+
|
|
73
|
+
const sseScript = sse
|
|
74
|
+
? `<script src="https://unpkg.com/htmx.org@${version}/dist/ext/sse.js"></script>`
|
|
75
|
+
: '';
|
|
76
|
+
|
|
77
|
+
return `
|
|
78
|
+
<script src="https://unpkg.com/htmx.org@${version}"></script>
|
|
79
|
+
${extScripts}
|
|
80
|
+
${wsScript}
|
|
81
|
+
${sseScript}
|
|
82
|
+
`;
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
getClientEntry(): string {
|
|
86
|
+
// HTMX doesn't need a client entry - it's declarative
|
|
87
|
+
return '// HTMX - No client-side JS needed';
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// HTMX Template Helpers
|
|
94
|
+
// ============================================================================
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Create an HTMX-enhanced element
|
|
98
|
+
*/
|
|
99
|
+
export function hx(
|
|
100
|
+
tag: string,
|
|
101
|
+
attrs: Record<string, string>,
|
|
102
|
+
children?: string
|
|
103
|
+
): string {
|
|
104
|
+
const attrStr = Object.entries(attrs)
|
|
105
|
+
.map(([k, v]) => `${k}="${v}"`)
|
|
106
|
+
.join(' ');
|
|
107
|
+
|
|
108
|
+
if (children !== undefined) {
|
|
109
|
+
return `<${tag} ${attrStr}>${children}</${tag}>`;
|
|
110
|
+
}
|
|
111
|
+
return `<${tag} ${attrStr} />`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* HTMX GET trigger
|
|
116
|
+
*/
|
|
117
|
+
export function hxGet(url: string, target?: string, swap?: string): Record<string, string> {
|
|
118
|
+
const attrs: Record<string, string> = { 'hx-get': url };
|
|
119
|
+
if (target) attrs['hx-target'] = target;
|
|
120
|
+
if (swap) attrs['hx-swap'] = swap;
|
|
121
|
+
return attrs;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* HTMX POST trigger
|
|
126
|
+
*/
|
|
127
|
+
export function hxPost(url: string, target?: string, swap?: string): Record<string, string> {
|
|
128
|
+
const attrs: Record<string, string> = { 'hx-post': url };
|
|
129
|
+
if (target) attrs['hx-target'] = target;
|
|
130
|
+
if (swap) attrs['hx-swap'] = swap;
|
|
131
|
+
return attrs;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export default htmx;
|