@onlook/capsule 0.1.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/src/types.ts ADDED
@@ -0,0 +1,268 @@
1
+ import { z } from 'zod';
2
+
3
+ // === Route Discovery ===
4
+
5
+ /** A react-router parent route that wraps child routes via <Outlet /> */
6
+ export type LayoutRoute = {
7
+ /** Route path segment, e.g. "/sign-in" */
8
+ path: string;
9
+ /** Relative path to the layout component */
10
+ component: string;
11
+ /** Named export (if omitted, assumes default export) */
12
+ exportName?: string;
13
+ };
14
+
15
+ /** A component that wraps <Routes> in the router (e.g. LocationManager, AuthGuard) */
16
+ export type AppWrapper = {
17
+ /** Component name, e.g. "LocationManager" */
18
+ componentName: string;
19
+ /** Relative path to component file */
20
+ component: string;
21
+ /** Named export (if omitted, assumes default export) */
22
+ exportName?: string;
23
+ };
24
+
25
+ export type RouteEntry = {
26
+ /** URL path, e.g. "/dashboard/settings" */
27
+ path: string;
28
+ /** Relative path to the page component, e.g. "app/dashboard/settings/page.tsx" */
29
+ pageComponent: string;
30
+ /** Layout files from outermost to innermost (Next.js) */
31
+ layouts: string[];
32
+ /** Parent route components that wrap this route via Outlet (react-router) */
33
+ layoutRoutes?: LayoutRoute[];
34
+ /** Mock values for dynamic segments, e.g. { id: "mock-123" } */
35
+ params?: Record<string, string>;
36
+ /** Named export to use (if omitted, assumes default export) */
37
+ exportName?: string;
38
+ };
39
+
40
+ export type RouteManifest = {
41
+ framework: 'nextjs-app' | 'vite-react-router';
42
+ routes: RouteEntry[];
43
+ /** Components that wrap <Routes> in the router file (react-router only) */
44
+ appWrappers?: AppWrapper[];
45
+ };
46
+
47
+ // === Boundary Analysis ===
48
+
49
+ export type ComponentBoundary = {
50
+ /** Import source strings, e.g. ["@/contexts/AuthContext", "@tanstack/react-query"] */
51
+ imports: string[];
52
+ /** Hook call names, e.g. ["useAuth", "useQuery", "useFeatureFlags"] */
53
+ hooks: string[];
54
+ /** Context names from useContext calls, e.g. ["AuthContext", "ThemeContext"] */
55
+ contexts: string[];
56
+ };
57
+
58
+ // === Component Profile (enriched analysis for LLM context) ===
59
+
60
+ export type PropInfo = {
61
+ /** Property name */
62
+ name: string;
63
+ /** TypeScript type text, e.g. "string", "User | null" */
64
+ type: string;
65
+ /** Whether the prop is optional (question token or `| undefined`) */
66
+ optional: boolean;
67
+ };
68
+
69
+ export type StateInfo = {
70
+ /** Variable name, e.g. "count" */
71
+ name: string;
72
+ /** Inferred type from generic or initial value */
73
+ type: string;
74
+ /** Setter name, e.g. "setCount" */
75
+ setter: string;
76
+ };
77
+
78
+ export type HookCallInfo = {
79
+ /** Hook name, e.g. "useAuth" */
80
+ name: string;
81
+ /** Import source module, e.g. "@/hooks/useAuth"; empty string if locally defined */
82
+ module: string;
83
+ };
84
+
85
+ export type ComponentProfile = {
86
+ props: PropInfo[];
87
+ state: StateInfo[];
88
+ hookCalls: HookCallInfo[];
89
+ };
90
+
91
+ // === JSON-safe value type ===
92
+
93
+ export type JsonValue =
94
+ | string
95
+ | number
96
+ | boolean
97
+ | null
98
+ | JsonValue[]
99
+ | { [key: string]: JsonValue };
100
+
101
+ // === Capsule Manifest (the decoupled patch) ===
102
+
103
+ export type ProviderSpec = {
104
+ /** Identifier for this provider, e.g. "auth" */
105
+ id: string;
106
+ /** Import path, e.g. "@/contexts/AuthContext" */
107
+ import: string;
108
+ /** Context variable name, e.g. "AuthContext" */
109
+ contextName: string;
110
+ /** Mock value to provide via Context.Provider */
111
+ mockValue: Record<string, unknown>;
112
+ };
113
+
114
+ export type HookStubSpec = {
115
+ /** Module path to intercept, e.g. "@/hooks/useFeatureFlags" */
116
+ module: string;
117
+ /** Named export to stub, e.g. "useFeatureFlags" */
118
+ export: string;
119
+ /** Value the stub returns */
120
+ returns: unknown;
121
+ /** If true, export as a value instead of a function */
122
+ isValue?: boolean;
123
+ };
124
+
125
+ export type NetworkMockSpec = {
126
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
127
+ /** URL path pattern, e.g. "/api/settings" */
128
+ path: string;
129
+ /** HTTP status code, defaults to 200 */
130
+ status?: number;
131
+ /** Response body */
132
+ response: unknown;
133
+ };
134
+
135
+ export type CapsuleManifest = {
136
+ providers: ProviderSpec[];
137
+ hookStubs: HookStubSpec[];
138
+ networkMocks: NetworkMockSpec[];
139
+ /** Global assignments, e.g. { "window.ENV": { API_URL: "..." } } */
140
+ globals: Record<string, JsonValue>;
141
+ };
142
+
143
+ // === Render Results ===
144
+
145
+ export type CapsuleResult =
146
+ | {
147
+ status: 'sealed';
148
+ html: string;
149
+ manifest: CapsuleManifest;
150
+ attempts: number;
151
+ }
152
+ | {
153
+ status: 'breached';
154
+ error: string;
155
+ componentStack: string;
156
+ manifest: CapsuleManifest;
157
+ };
158
+
159
+ // === Viewer ===
160
+
161
+ export type CapsuleStatus = {
162
+ route: RouteEntry;
163
+ status: 'sealed' | 'breached' | 'pending';
164
+ attempts: number;
165
+ error?: string;
166
+ };
167
+
168
+ // === Config ===
169
+
170
+ export type CapsuleConfig = {
171
+ /** Absolute path to the project root */
172
+ projectRoot: string;
173
+ /** Override route discovery glob */
174
+ routes?: string;
175
+ /** Max seal loop retries per route (default: 3) */
176
+ maxRetries: number;
177
+ /** Parallel seal operations (default: 4) */
178
+ concurrency: number;
179
+ /** Start viewer after sealing */
180
+ serve: boolean;
181
+ /** Viewer port (default: 5123) */
182
+ port: number;
183
+ /** Cache directory (default: .capsules/cache) */
184
+ cacheDir: string;
185
+ /** Show seal loop details */
186
+ verbose: boolean;
187
+ /** Run LLM fixes immediately on failure instead of after all routes render (default: false) */
188
+ eager: boolean;
189
+ /** Max parallel LLM calls in lazy mode (default: 1) */
190
+ llmConcurrency: number;
191
+ /** Path to the router file for Vite projects (auto-detected if not set) */
192
+ routerFile?: string;
193
+ /** Additional esbuild externals beyond the defaults */
194
+ external?: string[];
195
+ /** Workspace modules to allow through (bypass auto-proxy). Matched as prefixes. */
196
+ allowedModules?: string[];
197
+ };
198
+
199
+ // === Zod Schemas (runtime validation for LLM output, cache, API) ===
200
+
201
+ export const providerSpecSchema = z.object({
202
+ id: z.string(),
203
+ import: z.string(),
204
+ contextName: z.string(),
205
+ mockValue: z.record(z.string(), z.unknown()),
206
+ });
207
+
208
+ export const hookStubSchema = z.object({
209
+ module: z.string(),
210
+ export: z.string(),
211
+ returns: z.unknown().default(null),
212
+ isValue: z.boolean().optional(),
213
+ });
214
+
215
+ export const networkMockSchema = z.object({
216
+ method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
217
+ path: z.string(),
218
+ status: z.number().optional(),
219
+ response: z.unknown(),
220
+ });
221
+
222
+ export const capsuleManifestSchema = z.object({
223
+ providers: z.array(providerSpecSchema),
224
+ hookStubs: z.array(hookStubSchema),
225
+ networkMocks: z.array(networkMockSchema),
226
+ globals: z.record(z.string(), z.unknown()),
227
+ });
228
+
229
+ // === Default values ===
230
+
231
+ export const DEFAULTS = {
232
+ MAX_RETRIES: 3,
233
+ CONCURRENCY: 4,
234
+ VIEWER_PORT: 5123,
235
+ LLM_CONCURRENCY: 1,
236
+ CACHE_HASH_LENGTH: 16,
237
+ } as const;
238
+
239
+ /**
240
+ * Hooks covered by hardcoded framework stubs in the Vite plugin.
241
+ * These don't appear in the manifest's hookStubs but ARE stubbed at runtime.
242
+ */
243
+ export const FRAMEWORK_STUBBED_HOOKS = new Set([
244
+ // react-router
245
+ 'useParams',
246
+ 'useSearchParams',
247
+ 'useNavigate',
248
+ 'useLocation',
249
+ 'useMatch',
250
+ 'useRoutes',
251
+ 'useOutletContext',
252
+ 'useHref',
253
+ 'useInRouterContext',
254
+ 'useNavigationType',
255
+ 'useResolvedPath',
256
+ 'useRouteError',
257
+ 'useLoaderData',
258
+ 'useActionData',
259
+ // react-i18next
260
+ 'useTranslation',
261
+ // apollo/graphql
262
+ 'useQuery',
263
+ 'useLazyQuery',
264
+ 'useMutation',
265
+ 'useSubscription',
266
+ 'useApolloClient',
267
+ 'useClient',
268
+ ]);