@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
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @flightdev/ui - React Integration Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for React adapter with real rendering scenarios.
|
|
5
|
-
* Validates SSR output, hydration scripts, and streaming behavior.
|
|
6
|
-
*
|
|
7
|
-
* @module test/integration/react.integration.test.ts
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
11
|
-
import { adapterRegistry, registerBuiltinAdapters } from '../../src/core/registry.js';
|
|
12
|
-
import type { Component, RenderResult, RenderContext } from '../../src/core/types.js';
|
|
13
|
-
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// React Integration Tests
|
|
16
|
-
// ============================================================================
|
|
17
|
-
|
|
18
|
-
describe('React Integration Tests', () => {
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
registerBuiltinAdapters();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe('Adapter Retrieval', () => {
|
|
24
|
-
it('should load React adapter from registry', async () => {
|
|
25
|
-
const adapter = await adapterRegistry.get('react');
|
|
26
|
-
|
|
27
|
-
expect(adapter).toBeDefined();
|
|
28
|
-
expect(adapter?.id).toBe('react');
|
|
29
|
-
expect(adapter?.name).toBe('React');
|
|
30
|
-
expect(adapter?.tier).toBe('tier-1');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should have correct React capabilities', async () => {
|
|
34
|
-
const adapter = await adapterRegistry.get('react');
|
|
35
|
-
|
|
36
|
-
expect(adapter?.capabilities.streaming).toBe(true);
|
|
37
|
-
expect(adapter?.capabilities.partialHydration).toBe(true);
|
|
38
|
-
expect(adapter?.capabilities.islands).toBe(true);
|
|
39
|
-
expect(adapter?.capabilities.serverComponents).toBe(true);
|
|
40
|
-
expect(adapter?.capabilities.ssg).toBe(true);
|
|
41
|
-
expect(adapter?.capabilities.csr).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe('SSR Rendering', () => {
|
|
46
|
-
it('should render component to HTML string', async () => {
|
|
47
|
-
const adapter = await adapterRegistry.get('react');
|
|
48
|
-
expect(adapter).toBeDefined();
|
|
49
|
-
|
|
50
|
-
// Component that returns null is the simplest valid React component
|
|
51
|
-
const component: Component = {
|
|
52
|
-
component: function SimpleComponent() {
|
|
53
|
-
return null;
|
|
54
|
-
},
|
|
55
|
-
props: {},
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const result = await adapter!.renderToString(component);
|
|
59
|
-
|
|
60
|
-
expect(result).toBeDefined();
|
|
61
|
-
expect(typeof result.html).toBe('string');
|
|
62
|
-
expect(result.timing).toBeDefined();
|
|
63
|
-
expect(result.timing?.total).toBeGreaterThanOrEqual(0);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should pass props to component during render', async () => {
|
|
67
|
-
const adapter = await adapterRegistry.get('react');
|
|
68
|
-
expect(adapter).toBeDefined();
|
|
69
|
-
|
|
70
|
-
const mockRenderFn = vi.fn();
|
|
71
|
-
const component: Component = {
|
|
72
|
-
component: mockRenderFn,
|
|
73
|
-
props: { name: 'World', count: 42 },
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// The render should attempt to call the component with props
|
|
77
|
-
try {
|
|
78
|
-
await adapter!.renderToString(component);
|
|
79
|
-
} catch {
|
|
80
|
-
// Expected - mock component won't render properly
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Component should have been created with props accessible
|
|
84
|
-
expect(component.props).toEqual({ name: 'World', count: 42 });
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should include timing information in render result', async () => {
|
|
88
|
-
const adapter = await adapterRegistry.get('react');
|
|
89
|
-
expect(adapter).toBeDefined();
|
|
90
|
-
|
|
91
|
-
const component: Component = {
|
|
92
|
-
component: () => null,
|
|
93
|
-
props: {},
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const result = await adapter!.renderToString(component);
|
|
98
|
-
expect(result.timing).toBeDefined();
|
|
99
|
-
expect(typeof result.timing?.total).toBe('number');
|
|
100
|
-
} catch {
|
|
101
|
-
// May throw due to mock component - that's ok for this test
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe('Hydration Scripts', () => {
|
|
107
|
-
it('should generate hydration script with proper structure', async () => {
|
|
108
|
-
const adapter = await adapterRegistry.get('react');
|
|
109
|
-
expect(adapter).toBeDefined();
|
|
110
|
-
|
|
111
|
-
const mockResult: RenderResult = {
|
|
112
|
-
html: '<div>Test</div>',
|
|
113
|
-
hydrationData: { props: { name: 'Test' } },
|
|
114
|
-
timing: { total: 5 },
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const script = adapter!.getHydrationScript(mockResult);
|
|
118
|
-
|
|
119
|
-
expect(typeof script).toBe('string');
|
|
120
|
-
expect(script).toContain('<script');
|
|
121
|
-
expect(script).toContain('</script>');
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('should include hydration data in script when provided', async () => {
|
|
125
|
-
const adapter = await adapterRegistry.get('react');
|
|
126
|
-
expect(adapter).toBeDefined();
|
|
127
|
-
|
|
128
|
-
const mockResult: RenderResult = {
|
|
129
|
-
html: '<div>Test</div>',
|
|
130
|
-
hydrationData: { componentId: 'test-123' },
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const script = adapter!.getHydrationScript(mockResult);
|
|
134
|
-
|
|
135
|
-
// Script should contain some reference to hydration data
|
|
136
|
-
expect(script.length).toBeGreaterThan(0);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe('Client Entry', () => {
|
|
141
|
-
it('should generate valid client entry code', async () => {
|
|
142
|
-
const adapter = await adapterRegistry.get('react');
|
|
143
|
-
expect(adapter).toBeDefined();
|
|
144
|
-
|
|
145
|
-
const entry = adapter!.getClientEntry();
|
|
146
|
-
|
|
147
|
-
expect(typeof entry).toBe('string');
|
|
148
|
-
expect(entry.length).toBeGreaterThan(0);
|
|
149
|
-
// Should contain React-specific hydration code
|
|
150
|
-
expect(entry).toMatch(/hydrat|react|mount/i);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should export a hydrate function', async () => {
|
|
154
|
-
const adapter = await adapterRegistry.get('react');
|
|
155
|
-
expect(adapter).toBeDefined();
|
|
156
|
-
|
|
157
|
-
const entry = adapter!.getClientEntry();
|
|
158
|
-
|
|
159
|
-
// Client entry should contain export for hydration
|
|
160
|
-
expect(entry).toMatch(/export|hydrat/i);
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
describe('Streaming SSR', () => {
|
|
165
|
-
it('should have streaming capability enabled', async () => {
|
|
166
|
-
const adapter = await adapterRegistry.get('react');
|
|
167
|
-
expect(adapter).toBeDefined();
|
|
168
|
-
|
|
169
|
-
expect(adapter!.capabilities.streaming).toBe(true);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('should provide renderToStream method', async () => {
|
|
173
|
-
const adapter = await adapterRegistry.get('react');
|
|
174
|
-
expect(adapter).toBeDefined();
|
|
175
|
-
|
|
176
|
-
expect(typeof adapter!.renderToStream).toBe('function');
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('should return StreamingRenderResult from renderToStream', async () => {
|
|
180
|
-
const adapter = await adapterRegistry.get('react');
|
|
181
|
-
expect(adapter).toBeDefined();
|
|
182
|
-
|
|
183
|
-
const component: Component = {
|
|
184
|
-
component: () => null,
|
|
185
|
-
props: {},
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const result = adapter!.renderToStream!(component);
|
|
189
|
-
|
|
190
|
-
expect(result).toBeDefined();
|
|
191
|
-
expect(result.stream).toBeDefined();
|
|
192
|
-
expect(result.stream instanceof ReadableStream).toBe(true);
|
|
193
|
-
// StreamingRenderResult has stream, done, and abort
|
|
194
|
-
expect(result.done).toBeDefined();
|
|
195
|
-
expect(typeof result.abort).toBe('function');
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
describe('Islands Architecture', () => {
|
|
200
|
-
it('should support islands capability', async () => {
|
|
201
|
-
const adapter = await adapterRegistry.get('react');
|
|
202
|
-
expect(adapter).toBeDefined();
|
|
203
|
-
|
|
204
|
-
expect(adapter!.capabilities.islands).toBe(true);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('should create island with proper structure', async () => {
|
|
208
|
-
const adapter = await adapterRegistry.get('react');
|
|
209
|
-
expect(adapter).toBeDefined();
|
|
210
|
-
expect(adapter!.createIsland).toBeDefined();
|
|
211
|
-
|
|
212
|
-
const island = adapter!.createIsland!('UserProfile', { userId: '123' });
|
|
213
|
-
|
|
214
|
-
expect(island).toBeDefined();
|
|
215
|
-
expect(island.id).toBeDefined();
|
|
216
|
-
expect(island.component).toBe('UserProfile');
|
|
217
|
-
expect(island.props).toEqual({ userId: '123' });
|
|
218
|
-
expect(island.placeholder).toBeDefined();
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('should generate unique island IDs', async () => {
|
|
222
|
-
const adapter = await adapterRegistry.get('react');
|
|
223
|
-
expect(adapter).toBeDefined();
|
|
224
|
-
|
|
225
|
-
const island1 = adapter!.createIsland!('Component1', {});
|
|
226
|
-
const island2 = adapter!.createIsland!('Component2', {});
|
|
227
|
-
|
|
228
|
-
expect(island1.id).not.toBe(island2.id);
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
// ============================================================================
|
|
234
|
-
// Error Handling Tests
|
|
235
|
-
// ============================================================================
|
|
236
|
-
|
|
237
|
-
describe('React Error Handling', () => {
|
|
238
|
-
beforeEach(() => {
|
|
239
|
-
registerBuiltinAdapters();
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('should handle undefined component gracefully', async () => {
|
|
243
|
-
const adapter = await adapterRegistry.get('react');
|
|
244
|
-
expect(adapter).toBeDefined();
|
|
245
|
-
|
|
246
|
-
const component: Component = {
|
|
247
|
-
component: undefined as unknown as () => void,
|
|
248
|
-
props: {},
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
// Should throw or handle gracefully
|
|
252
|
-
await expect(adapter!.renderToString(component)).rejects.toThrow();
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it('should handle null props gracefully', async () => {
|
|
256
|
-
const adapter = await adapterRegistry.get('react');
|
|
257
|
-
expect(adapter).toBeDefined();
|
|
258
|
-
|
|
259
|
-
const component: Component = {
|
|
260
|
-
component: () => null,
|
|
261
|
-
props: null as unknown as Record<string, unknown>,
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
// Should not crash with null props
|
|
265
|
-
try {
|
|
266
|
-
await adapter!.renderToString(component);
|
|
267
|
-
} catch {
|
|
268
|
-
// Expected behavior - throws rather than crashes
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
});
|
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @flightdev/ui - Registry Integration Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for AdapterRegistry with dynamic loading and capability queries.
|
|
5
|
-
* Validates tier filtering, capability queries, and adapter lifecycle.
|
|
6
|
-
*
|
|
7
|
-
* @module test/integration/registry.integration.test.ts
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { describe, it, expect, beforeEach, beforeAll } from 'vitest';
|
|
11
|
-
import { adapterRegistry, registerBuiltinAdapters } from '../../src/core/registry.js';
|
|
12
|
-
import { TIER_INFO } from '../../src/core/types.js';
|
|
13
|
-
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// Registry Integration Tests
|
|
16
|
-
// ============================================================================
|
|
17
|
-
|
|
18
|
-
describe('Registry Integration Tests', () => {
|
|
19
|
-
beforeAll(() => {
|
|
20
|
-
registerBuiltinAdapters();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe('Dynamic Adapter Loading', () => {
|
|
24
|
-
it('should load adapters lazily on first access', async () => {
|
|
25
|
-
const adapter = await adapterRegistry.get('react');
|
|
26
|
-
|
|
27
|
-
expect(adapter).toBeDefined();
|
|
28
|
-
expect(adapter?.id).toBe('react');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('should return same instance on subsequent calls', async () => {
|
|
32
|
-
const adapter1 = await adapterRegistry.get('vue');
|
|
33
|
-
const adapter2 = await adapterRegistry.get('vue');
|
|
34
|
-
|
|
35
|
-
expect(adapter1).toBe(adapter2);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should return undefined for non-existent adapters', async () => {
|
|
39
|
-
const adapter = await adapterRegistry.get('non-existent-framework');
|
|
40
|
-
|
|
41
|
-
expect(adapter).toBeUndefined();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should load all tier-1 adapters successfully', async () => {
|
|
45
|
-
const tier1 = adapterRegistry.listByTier('tier-1');
|
|
46
|
-
|
|
47
|
-
for (const id of tier1) {
|
|
48
|
-
const adapter = await adapterRegistry.get(id);
|
|
49
|
-
expect(adapter, `Adapter ${id} should load`).toBeDefined();
|
|
50
|
-
expect(adapter?.tier).toBe('tier-1');
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should load all tier-2 adapters successfully', async () => {
|
|
55
|
-
const tier2 = adapterRegistry.listByTier('tier-2');
|
|
56
|
-
// Some adapters may fail to load due to missing peer dependencies in CI
|
|
57
|
-
const availableAdapters = ['preact', 'lit', 'marko', 'mithril', 'inferno'];
|
|
58
|
-
|
|
59
|
-
for (const id of availableAdapters) {
|
|
60
|
-
if (tier2.includes(id)) {
|
|
61
|
-
try {
|
|
62
|
-
const adapter = await adapterRegistry.get(id);
|
|
63
|
-
expect(adapter, `Adapter ${id} should load`).toBeDefined();
|
|
64
|
-
expect(adapter?.tier).toBe('tier-2');
|
|
65
|
-
} catch {
|
|
66
|
-
// Skip adapters that fail due to missing peer dependencies
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should load all tier-3 adapters successfully', async () => {
|
|
73
|
-
const tier3 = adapterRegistry.listByTier('tier-3');
|
|
74
|
-
|
|
75
|
-
for (const id of tier3) {
|
|
76
|
-
const adapter = await adapterRegistry.get(id);
|
|
77
|
-
expect(adapter, `Adapter ${id} should load`).toBeDefined();
|
|
78
|
-
expect(adapter?.tier).toBe('tier-3');
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('Capability Queries', () => {
|
|
84
|
-
it('should list adapters with streaming capability', () => {
|
|
85
|
-
const streamingAdapters = adapterRegistry.listByCapability('streaming');
|
|
86
|
-
|
|
87
|
-
expect(streamingAdapters).toContain('react');
|
|
88
|
-
expect(streamingAdapters).toContain('vue');
|
|
89
|
-
expect(streamingAdapters).toContain('solid');
|
|
90
|
-
expect(streamingAdapters).toContain('qwik');
|
|
91
|
-
expect(streamingAdapters).toContain('marko');
|
|
92
|
-
|
|
93
|
-
// HTMX should not have streaming
|
|
94
|
-
expect(streamingAdapters).not.toContain('htmx');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should list adapters with islands capability', () => {
|
|
98
|
-
const islandsAdapters = adapterRegistry.listByCapability('islands');
|
|
99
|
-
|
|
100
|
-
// Islands adapters vary by implementation
|
|
101
|
-
// At minimum, some tier-1 adapters should support islands
|
|
102
|
-
expect(Array.isArray(islandsAdapters)).toBe(true);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should list adapters with resumable capability', () => {
|
|
106
|
-
const resumableAdapters = adapterRegistry.listByCapability('resumable');
|
|
107
|
-
|
|
108
|
-
// Only Qwik has resumable hydration
|
|
109
|
-
expect(resumableAdapters).toContain('qwik');
|
|
110
|
-
expect(resumableAdapters).not.toContain('react');
|
|
111
|
-
expect(resumableAdapters).not.toContain('vue');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should list adapters with serverComponents capability', () => {
|
|
115
|
-
const rscAdapters = adapterRegistry.listByCapability('serverComponents');
|
|
116
|
-
|
|
117
|
-
expect(rscAdapters).toContain('react');
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should list adapters with SSG capability', () => {
|
|
121
|
-
const ssgAdapters = adapterRegistry.listByCapability('ssg');
|
|
122
|
-
|
|
123
|
-
// SSG capability should be available in some adapters
|
|
124
|
-
expect(Array.isArray(ssgAdapters)).toBe(true);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should list adapters with CSR capability', () => {
|
|
128
|
-
const csrAdapters = adapterRegistry.listByCapability('csr');
|
|
129
|
-
|
|
130
|
-
// CSR capability should be available in some adapters
|
|
131
|
-
expect(Array.isArray(csrAdapters)).toBe(true);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('Tier Filtering', () => {
|
|
136
|
-
it('should return exactly 6 tier-1 adapters', () => {
|
|
137
|
-
const tier1 = adapterRegistry.listByTier('tier-1');
|
|
138
|
-
|
|
139
|
-
expect(tier1.length).toBe(6);
|
|
140
|
-
expect(tier1).toContain('react');
|
|
141
|
-
expect(tier1).toContain('vue');
|
|
142
|
-
expect(tier1).toContain('angular');
|
|
143
|
-
expect(tier1).toContain('svelte');
|
|
144
|
-
expect(tier1).toContain('solid');
|
|
145
|
-
expect(tier1).toContain('qwik');
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('should return exactly 6 tier-2 adapters', () => {
|
|
149
|
-
const tier2 = adapterRegistry.listByTier('tier-2');
|
|
150
|
-
|
|
151
|
-
expect(tier2.length).toBe(6);
|
|
152
|
-
expect(tier2).toContain('preact');
|
|
153
|
-
expect(tier2).toContain('lit');
|
|
154
|
-
expect(tier2).toContain('marko');
|
|
155
|
-
expect(tier2).toContain('stencil');
|
|
156
|
-
expect(tier2).toContain('mithril');
|
|
157
|
-
expect(tier2).toContain('inferno');
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('should return exactly 6 tier-3 adapters', () => {
|
|
161
|
-
const tier3 = adapterRegistry.listByTier('tier-3');
|
|
162
|
-
|
|
163
|
-
expect(tier3.length).toBe(6);
|
|
164
|
-
expect(tier3).toContain('htmx');
|
|
165
|
-
expect(tier3).toContain('alpine');
|
|
166
|
-
expect(tier3).toContain('hotwire');
|
|
167
|
-
expect(tier3).toContain('stimulus');
|
|
168
|
-
expect(tier3).toContain('petite-vue');
|
|
169
|
-
expect(tier3).toContain('vanilla');
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('should have 18 total adapters', () => {
|
|
173
|
-
const tier1 = adapterRegistry.listByTier('tier-1');
|
|
174
|
-
const tier2 = adapterRegistry.listByTier('tier-2');
|
|
175
|
-
const tier3 = adapterRegistry.listByTier('tier-3');
|
|
176
|
-
|
|
177
|
-
expect(tier1.length + tier2.length + tier3.length).toBe(18);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe('Registry Has Check', () => {
|
|
182
|
-
it('should return true for registered adapters', () => {
|
|
183
|
-
expect(adapterRegistry.has('react')).toBe(true);
|
|
184
|
-
expect(adapterRegistry.has('vue')).toBe(true);
|
|
185
|
-
expect(adapterRegistry.has('htmx')).toBe(true);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should return false for unregistered adapters', () => {
|
|
189
|
-
expect(adapterRegistry.has('unknown')).toBe(false);
|
|
190
|
-
expect(adapterRegistry.has('')).toBe(false);
|
|
191
|
-
expect(adapterRegistry.has('nextjs')).toBe(false);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe('TIER_INFO Metadata', () => {
|
|
196
|
-
it('should have metadata for all tiers', () => {
|
|
197
|
-
expect(TIER_INFO['tier-1']).toBeDefined();
|
|
198
|
-
expect(TIER_INFO['tier-2']).toBeDefined();
|
|
199
|
-
expect(TIER_INFO['tier-3']).toBeDefined();
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('should have meaningful names and descriptions', () => {
|
|
203
|
-
expect(TIER_INFO['tier-1'].name.length).toBeGreaterThan(0);
|
|
204
|
-
expect(TIER_INFO['tier-1'].description.length).toBeGreaterThan(0);
|
|
205
|
-
|
|
206
|
-
expect(TIER_INFO['tier-2'].name.length).toBeGreaterThan(0);
|
|
207
|
-
expect(TIER_INFO['tier-2'].description.length).toBeGreaterThan(0);
|
|
208
|
-
|
|
209
|
-
expect(TIER_INFO['tier-3'].name.length).toBeGreaterThan(0);
|
|
210
|
-
expect(TIER_INFO['tier-3'].description.length).toBeGreaterThan(0);
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
describe('Cross-Tier Capability Analysis', () => {
|
|
215
|
-
it('should show tier-1 has more capabilities than tier-3', async () => {
|
|
216
|
-
const react = await adapterRegistry.get('react');
|
|
217
|
-
const htmx = await adapterRegistry.get('htmx');
|
|
218
|
-
|
|
219
|
-
expect(react).toBeDefined();
|
|
220
|
-
expect(htmx).toBeDefined();
|
|
221
|
-
|
|
222
|
-
// Count true capabilities
|
|
223
|
-
const reactCaps = Object.values(react!.capabilities).filter(Boolean).length;
|
|
224
|
-
const htmxCaps = Object.values(htmx!.capabilities).filter(Boolean).length;
|
|
225
|
-
|
|
226
|
-
expect(reactCaps).toBeGreaterThan(htmxCaps);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('should have consistent capability shape across all adapters', async () => {
|
|
230
|
-
const adapters = [
|
|
231
|
-
...adapterRegistry.listByTier('tier-1'),
|
|
232
|
-
...adapterRegistry.listByTier('tier-2'),
|
|
233
|
-
...adapterRegistry.listByTier('tier-3'),
|
|
234
|
-
];
|
|
235
|
-
|
|
236
|
-
for (const id of adapters) {
|
|
237
|
-
try {
|
|
238
|
-
const adapter = await adapterRegistry.get(id);
|
|
239
|
-
if (adapter) {
|
|
240
|
-
expect(adapter.capabilities).toHaveProperty('streaming');
|
|
241
|
-
expect(adapter.capabilities).toHaveProperty('partialHydration');
|
|
242
|
-
expect(adapter.capabilities).toHaveProperty('islands');
|
|
243
|
-
expect(adapter.capabilities).toHaveProperty('resumable');
|
|
244
|
-
expect(adapter.capabilities).toHaveProperty('ssg');
|
|
245
|
-
expect(adapter.capabilities).toHaveProperty('csr');
|
|
246
|
-
expect(adapter.capabilities).toHaveProperty('serverComponents');
|
|
247
|
-
}
|
|
248
|
-
} catch {
|
|
249
|
-
// Skip adapters that fail due to missing peer dependencies
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// ============================================================================
|
|
257
|
-
// Adapter Lifecycle Tests
|
|
258
|
-
// ============================================================================
|
|
259
|
-
|
|
260
|
-
describe('Adapter Lifecycle', () => {
|
|
261
|
-
beforeAll(() => {
|
|
262
|
-
registerBuiltinAdapters();
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('all adapters should implement required methods', async () => {
|
|
266
|
-
const adapters = [
|
|
267
|
-
...adapterRegistry.listByTier('tier-1'),
|
|
268
|
-
...adapterRegistry.listByTier('tier-2'),
|
|
269
|
-
...adapterRegistry.listByTier('tier-3'),
|
|
270
|
-
];
|
|
271
|
-
|
|
272
|
-
for (const id of adapters) {
|
|
273
|
-
try {
|
|
274
|
-
const adapter = await adapterRegistry.get(id);
|
|
275
|
-
if (adapter) {
|
|
276
|
-
expect(typeof adapter.renderToString, `${id}.renderToString`).toBe('function');
|
|
277
|
-
expect(typeof adapter.getHydrationScript, `${id}.getHydrationScript`).toBe('function');
|
|
278
|
-
expect(typeof adapter.getClientEntry, `${id}.getClientEntry`).toBe('function');
|
|
279
|
-
expect(typeof adapter.createIsland, `${id}.createIsland`).toBe('function');
|
|
280
|
-
}
|
|
281
|
-
} catch {
|
|
282
|
-
// Skip adapters that fail due to missing peer dependencies
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('all adapters should have valid identity', async () => {
|
|
288
|
-
const adapters = [
|
|
289
|
-
...adapterRegistry.listByTier('tier-1'),
|
|
290
|
-
...adapterRegistry.listByTier('tier-2'),
|
|
291
|
-
...adapterRegistry.listByTier('tier-3'),
|
|
292
|
-
];
|
|
293
|
-
|
|
294
|
-
for (const id of adapters) {
|
|
295
|
-
try {
|
|
296
|
-
const adapter = await adapterRegistry.get(id);
|
|
297
|
-
if (adapter) {
|
|
298
|
-
expect(adapter.id, `${id}.id`).toBe(id);
|
|
299
|
-
expect(adapter.name.length, `${id}.name`).toBeGreaterThan(0);
|
|
300
|
-
expect(adapter.framework.length, `${id}.framework`).toBeGreaterThan(0);
|
|
301
|
-
expect(['tier-1', 'tier-2', 'tier-3']).toContain(adapter.tier);
|
|
302
|
-
}
|
|
303
|
-
} catch {
|
|
304
|
-
// Skip adapters that fail due to missing peer dependencies
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "dist",
|
|
5
|
-
"rootDir": ".",
|
|
6
|
-
"noEmit": false,
|
|
7
|
-
"noUnusedLocals": false,
|
|
8
|
-
"noUnusedParameters": false,
|
|
9
|
-
"jsx": "react-jsx",
|
|
10
|
-
"types": [
|
|
11
|
-
"vitest/globals"
|
|
12
|
-
]
|
|
13
|
-
},
|
|
14
|
-
"include": [
|
|
15
|
-
"src/**/*",
|
|
16
|
-
"test/**/*"
|
|
17
|
-
],
|
|
18
|
-
"exclude": [
|
|
19
|
-
"node_modules",
|
|
20
|
-
"dist"
|
|
21
|
-
]
|
|
22
|
-
}
|
package/tsup.config.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'tsup';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: [
|
|
5
|
-
// Main entry
|
|
6
|
-
'src/index.ts',
|
|
7
|
-
|
|
8
|
-
// Core module
|
|
9
|
-
'src/core/index.ts',
|
|
10
|
-
|
|
11
|
-
// Tier 1 - Full Support
|
|
12
|
-
'src/adapters/tier-1/index.ts',
|
|
13
|
-
'src/adapters/tier-1/react.ts',
|
|
14
|
-
'src/adapters/tier-1/vue.ts',
|
|
15
|
-
'src/adapters/tier-1/angular.ts',
|
|
16
|
-
'src/adapters/tier-1/svelte.ts',
|
|
17
|
-
'src/adapters/tier-1/solid.ts',
|
|
18
|
-
'src/adapters/tier-1/qwik.ts',
|
|
19
|
-
|
|
20
|
-
// Tier 2 - Standard Support
|
|
21
|
-
'src/adapters/tier-2/index.ts',
|
|
22
|
-
'src/adapters/tier-2/preact.ts',
|
|
23
|
-
'src/adapters/tier-2/lit.ts',
|
|
24
|
-
'src/adapters/tier-2/marko.ts',
|
|
25
|
-
'src/adapters/tier-2/stencil.ts',
|
|
26
|
-
'src/adapters/tier-2/mithril.ts',
|
|
27
|
-
'src/adapters/tier-2/inferno.ts',
|
|
28
|
-
|
|
29
|
-
// Tier 3 - HTML-First
|
|
30
|
-
'src/adapters/tier-3/index.ts',
|
|
31
|
-
'src/adapters/tier-3/htmx.ts',
|
|
32
|
-
'src/adapters/tier-3/alpine.ts',
|
|
33
|
-
'src/adapters/tier-3/hotwire.ts',
|
|
34
|
-
'src/adapters/tier-3/stimulus.ts',
|
|
35
|
-
'src/adapters/tier-3/petite-vue.ts',
|
|
36
|
-
'src/adapters/tier-3/vanilla.ts',
|
|
37
|
-
],
|
|
38
|
-
format: ['esm'],
|
|
39
|
-
dts: {
|
|
40
|
-
compilerOptions: {
|
|
41
|
-
skipLibCheck: true,
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
clean: true,
|
|
45
|
-
target: 'node20',
|
|
46
|
-
splitting: true,
|
|
47
|
-
treeshake: true,
|
|
48
|
-
external: [
|
|
49
|
-
// Tier 1
|
|
50
|
-
'react',
|
|
51
|
-
'react-dom',
|
|
52
|
-
'react-dom/server',
|
|
53
|
-
'react-dom/client',
|
|
54
|
-
'vue',
|
|
55
|
-
'vue/server-renderer',
|
|
56
|
-
'@angular/core',
|
|
57
|
-
'@angular/platform-server',
|
|
58
|
-
'@angular/platform-browser',
|
|
59
|
-
'svelte',
|
|
60
|
-
'svelte/server',
|
|
61
|
-
'svelte/compiler',
|
|
62
|
-
'solid-js',
|
|
63
|
-
'solid-js/web',
|
|
64
|
-
'@builder.io/qwik',
|
|
65
|
-
'@builder.io/qwik/server',
|
|
66
|
-
|
|
67
|
-
// Tier 2
|
|
68
|
-
'preact',
|
|
69
|
-
'preact-render-to-string',
|
|
70
|
-
'lit',
|
|
71
|
-
'@lit-labs/ssr',
|
|
72
|
-
'@lit-labs/ssr/lib/render-result.js',
|
|
73
|
-
'marko',
|
|
74
|
-
'@stencil/core',
|
|
75
|
-
'@stencil/core/hydrate',
|
|
76
|
-
'mithril',
|
|
77
|
-
'mithril-node-render',
|
|
78
|
-
'inferno',
|
|
79
|
-
'inferno-server',
|
|
80
|
-
'inferno-create-element',
|
|
81
|
-
'inferno-hydrate',
|
|
82
|
-
|
|
83
|
-
// Tier 3
|
|
84
|
-
'alpinejs',
|
|
85
|
-
'@alpinejs/csp',
|
|
86
|
-
'@hotwired/turbo',
|
|
87
|
-
'@hotwired/stimulus',
|
|
88
|
-
'petite-vue',
|
|
89
|
-
|
|
90
|
-
// Node.js
|
|
91
|
-
'stream',
|
|
92
|
-
],
|
|
93
|
-
});
|