@plumbus/ui 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/dist/.tsbuildinfo +1 -0
- package/dist/generators/__tests__/auth-generator.test.d.ts +2 -0
- package/dist/generators/__tests__/auth-generator.test.d.ts.map +1 -0
- package/dist/generators/__tests__/auth-generator.test.js +222 -0
- package/dist/generators/__tests__/auth-generator.test.js.map +1 -0
- package/dist/generators/__tests__/client-generator.test.d.ts +2 -0
- package/dist/generators/__tests__/client-generator.test.d.ts.map +1 -0
- package/dist/generators/__tests__/client-generator.test.js +224 -0
- package/dist/generators/__tests__/client-generator.test.js.map +1 -0
- package/dist/generators/__tests__/form-generator.test.d.ts +2 -0
- package/dist/generators/__tests__/form-generator.test.d.ts.map +1 -0
- package/dist/generators/__tests__/form-generator.test.js +192 -0
- package/dist/generators/__tests__/form-generator.test.js.map +1 -0
- package/dist/generators/__tests__/nextjs-template.test.d.ts +2 -0
- package/dist/generators/__tests__/nextjs-template.test.d.ts.map +1 -0
- package/dist/generators/__tests__/nextjs-template.test.js +214 -0
- package/dist/generators/__tests__/nextjs-template.test.js.map +1 -0
- package/dist/generators/auth-generator.d.ts +31 -0
- package/dist/generators/auth-generator.d.ts.map +1 -0
- package/dist/generators/auth-generator.js +292 -0
- package/dist/generators/auth-generator.js.map +1 -0
- package/dist/generators/client-generator.d.ts +31 -0
- package/dist/generators/client-generator.d.ts.map +1 -0
- package/dist/generators/client-generator.js +336 -0
- package/dist/generators/client-generator.js.map +1 -0
- package/dist/generators/form-generator.d.ts +47 -0
- package/dist/generators/form-generator.d.ts.map +1 -0
- package/dist/generators/form-generator.js +189 -0
- package/dist/generators/form-generator.js.map +1 -0
- package/dist/generators/index.d.ts +9 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +6 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/nextjs-template.d.ts +44 -0
- package/dist/generators/nextjs-template.d.ts.map +1 -0
- package/dist/generators/nextjs-template.js +444 -0
- package/dist/generators/nextjs-template.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/instructions/auth-generator.md +154 -0
- package/instructions/client-generator.md +149 -0
- package/instructions/form-generator.md +157 -0
- package/instructions/framework.md +108 -0
- package/instructions/nextjs-template.md +160 -0
- package/instructions/patterns.md +109 -0
- package/instructions/testing.md +211 -0
- package/package.json +52 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// ── Frontend Auth Helpers Generator ──
|
|
2
|
+
// Generates auth utilities from auth adapter config:
|
|
3
|
+
// login/logout, session hooks, route guards, tenant context, user identity hooks.
|
|
4
|
+
// ── Auth Types Generator ──
|
|
5
|
+
/** Generate TypeScript types for auth state */
|
|
6
|
+
export function generateAuthTypes() {
|
|
7
|
+
return `export interface AuthUser {
|
|
8
|
+
userId: string;
|
|
9
|
+
roles: string[];
|
|
10
|
+
scopes: string[];
|
|
11
|
+
tenantId?: string;
|
|
12
|
+
provider: string;
|
|
13
|
+
sessionId?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AuthState {
|
|
17
|
+
user: AuthUser | null;
|
|
18
|
+
isAuthenticated: boolean;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
error: Error | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AuthActions {
|
|
24
|
+
login(credentials: LoginCredentials): Promise<void>;
|
|
25
|
+
logout(): Promise<void>;
|
|
26
|
+
refreshSession(): Promise<void>;
|
|
27
|
+
getToken(): string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface LoginCredentials {
|
|
31
|
+
email?: string;
|
|
32
|
+
password?: string;
|
|
33
|
+
token?: string;
|
|
34
|
+
provider?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AuthConfig {
|
|
38
|
+
loginEndpoint: string;
|
|
39
|
+
logoutEndpoint: string;
|
|
40
|
+
refreshEndpoint: string;
|
|
41
|
+
tokenKey: string;
|
|
42
|
+
}`;
|
|
43
|
+
}
|
|
44
|
+
// ── Token Management ──
|
|
45
|
+
/** Generate token storage utilities */
|
|
46
|
+
export function generateTokenUtils(config) {
|
|
47
|
+
const key = config?.tokenKey ?? 'plumbus_auth_token';
|
|
48
|
+
return `const TOKEN_KEY = "${key}";
|
|
49
|
+
|
|
50
|
+
export function getStoredToken(): string | null {
|
|
51
|
+
if (typeof window === "undefined") return null;
|
|
52
|
+
return localStorage.getItem(TOKEN_KEY);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function setStoredToken(token: string): void {
|
|
56
|
+
if (typeof window === "undefined") return;
|
|
57
|
+
localStorage.setItem(TOKEN_KEY, token);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function clearStoredToken(): void {
|
|
61
|
+
if (typeof window === "undefined") return;
|
|
62
|
+
localStorage.removeItem(TOKEN_KEY);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function parseJwtPayload(token: string): Record<string, unknown> | null {
|
|
66
|
+
try {
|
|
67
|
+
const parts = token.split(".");
|
|
68
|
+
if (parts.length !== 3) return null;
|
|
69
|
+
const payload = parts[1]!;
|
|
70
|
+
const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
71
|
+
return JSON.parse(decoded) as Record<string, unknown>;
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function isTokenExpired(token: string): boolean {
|
|
78
|
+
const payload = parseJwtPayload(token);
|
|
79
|
+
if (!payload || typeof payload.exp !== "number") return true;
|
|
80
|
+
return payload.exp * 1000 < Date.now();
|
|
81
|
+
}`;
|
|
82
|
+
}
|
|
83
|
+
// ── Login/Logout Functions ──
|
|
84
|
+
/** Generate login/logout functions */
|
|
85
|
+
export function generateAuthFunctions(config) {
|
|
86
|
+
const loginUrl = config?.loginEndpoint ?? '/api/auth/login';
|
|
87
|
+
const logoutUrl = config?.logoutEndpoint ?? '/api/auth/logout';
|
|
88
|
+
const refreshUrl = config?.refreshEndpoint ?? '/api/auth/refresh';
|
|
89
|
+
return `export async function login(credentials: LoginCredentials): Promise<AuthUser> {
|
|
90
|
+
const response = await fetch("${loginUrl}", {
|
|
91
|
+
method: "POST",
|
|
92
|
+
headers: { "Content-Type": "application/json" },
|
|
93
|
+
body: JSON.stringify(credentials),
|
|
94
|
+
});
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
const body = await response.json().catch(() => ({}));
|
|
97
|
+
throw new Error(body.message ?? "Login failed");
|
|
98
|
+
}
|
|
99
|
+
const data = await response.json() as { token: string; user: AuthUser };
|
|
100
|
+
setStoredToken(data.token);
|
|
101
|
+
return data.user;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function logout(): Promise<void> {
|
|
105
|
+
const token = getStoredToken();
|
|
106
|
+
if (token) {
|
|
107
|
+
await fetch("${logoutUrl}", {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: { Authorization: \`Bearer \${token}\` },
|
|
110
|
+
}).catch(() => {});
|
|
111
|
+
}
|
|
112
|
+
clearStoredToken();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function refreshSession(): Promise<AuthUser | null> {
|
|
116
|
+
const token = getStoredToken();
|
|
117
|
+
if (!token) return null;
|
|
118
|
+
const response = await fetch("${refreshUrl}", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: { Authorization: \`Bearer \${token}\` },
|
|
121
|
+
});
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
clearStoredToken();
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const data = await response.json() as { token: string; user: AuthUser };
|
|
127
|
+
setStoredToken(data.token);
|
|
128
|
+
return data.user;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function getAuthHeaders(): Record<string, string> {
|
|
132
|
+
const token = getStoredToken();
|
|
133
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
134
|
+
}`;
|
|
135
|
+
}
|
|
136
|
+
// ── React Hooks ──
|
|
137
|
+
/** Generate useAuth React hook */
|
|
138
|
+
export function generateUseAuthHook() {
|
|
139
|
+
return `export function useAuth(): AuthState & AuthActions {
|
|
140
|
+
const [state, setState] = useState<AuthState>({
|
|
141
|
+
user: null,
|
|
142
|
+
isAuthenticated: false,
|
|
143
|
+
isLoading: true,
|
|
144
|
+
error: null,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
const token = getStoredToken();
|
|
149
|
+
if (!token || isTokenExpired(token)) {
|
|
150
|
+
setState({ user: null, isAuthenticated: false, isLoading: false, error: null });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
refreshSession()
|
|
154
|
+
.then((user) => {
|
|
155
|
+
setState({
|
|
156
|
+
user,
|
|
157
|
+
isAuthenticated: !!user,
|
|
158
|
+
isLoading: false,
|
|
159
|
+
error: null,
|
|
160
|
+
});
|
|
161
|
+
})
|
|
162
|
+
.catch((err) => {
|
|
163
|
+
setState({
|
|
164
|
+
user: null,
|
|
165
|
+
isAuthenticated: false,
|
|
166
|
+
isLoading: false,
|
|
167
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}, []);
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
...state,
|
|
174
|
+
login: async (credentials) => {
|
|
175
|
+
setState((s) => ({ ...s, isLoading: true, error: null }));
|
|
176
|
+
try {
|
|
177
|
+
const user = await login(credentials);
|
|
178
|
+
setState({ user, isAuthenticated: true, isLoading: false, error: null });
|
|
179
|
+
} catch (err) {
|
|
180
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
181
|
+
setState((s) => ({ ...s, isLoading: false, error }));
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
logout: async () => {
|
|
186
|
+
await logout();
|
|
187
|
+
setState({ user: null, isAuthenticated: false, isLoading: false, error: null });
|
|
188
|
+
},
|
|
189
|
+
refreshSession: async () => {
|
|
190
|
+
const user = await refreshSession();
|
|
191
|
+
setState({
|
|
192
|
+
user,
|
|
193
|
+
isAuthenticated: !!user,
|
|
194
|
+
isLoading: false,
|
|
195
|
+
error: null,
|
|
196
|
+
});
|
|
197
|
+
},
|
|
198
|
+
getToken: getStoredToken,
|
|
199
|
+
};
|
|
200
|
+
}`;
|
|
201
|
+
}
|
|
202
|
+
/** Generate useCurrentUser hook */
|
|
203
|
+
export function generateUseCurrentUserHook() {
|
|
204
|
+
return `export function useCurrentUser() {
|
|
205
|
+
const { user, isAuthenticated, isLoading } = useAuth();
|
|
206
|
+
return { user, isAuthenticated, isLoading };
|
|
207
|
+
}`;
|
|
208
|
+
}
|
|
209
|
+
// ── Route Guard ──
|
|
210
|
+
/** Generate route guard component code */
|
|
211
|
+
export function generateRouteGuard() {
|
|
212
|
+
return `export interface RouteGuardProps {
|
|
213
|
+
children: React.ReactNode;
|
|
214
|
+
roles?: string[];
|
|
215
|
+
scopes?: string[];
|
|
216
|
+
fallback?: React.ReactNode;
|
|
217
|
+
redirectTo?: string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function RouteGuard({ children, roles, scopes, fallback, redirectTo }: RouteGuardProps) {
|
|
221
|
+
const { user, isAuthenticated, isLoading } = useAuth();
|
|
222
|
+
|
|
223
|
+
if (isLoading) return fallback ?? null;
|
|
224
|
+
|
|
225
|
+
if (!isAuthenticated) {
|
|
226
|
+
if (redirectTo && typeof window !== "undefined") {
|
|
227
|
+
window.location.href = redirectTo;
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
return fallback ?? null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (roles && roles.length > 0 && user) {
|
|
234
|
+
const hasRole = roles.some((r) => user.roles.includes(r));
|
|
235
|
+
if (!hasRole) return fallback ?? null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (scopes && scopes.length > 0 && user) {
|
|
239
|
+
const hasScope = scopes.some((s) => user.scopes.includes(s));
|
|
240
|
+
if (!hasScope) return fallback ?? null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return children;
|
|
244
|
+
}`;
|
|
245
|
+
}
|
|
246
|
+
// ── Tenant Context ──
|
|
247
|
+
/** Generate tenant context provider */
|
|
248
|
+
export function generateTenantContext() {
|
|
249
|
+
return `export interface TenantContextValue {
|
|
250
|
+
tenantId: string | null;
|
|
251
|
+
setTenantId(id: string): void;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function useTenant(): TenantContextValue {
|
|
255
|
+
const { user } = useAuth();
|
|
256
|
+
const [tenantId, setTenantId] = useState<string | null>(user?.tenantId ?? null);
|
|
257
|
+
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
if (user?.tenantId) setTenantId(user.tenantId);
|
|
260
|
+
}, [user?.tenantId]);
|
|
261
|
+
|
|
262
|
+
return { tenantId, setTenantId };
|
|
263
|
+
}`;
|
|
264
|
+
}
|
|
265
|
+
// ── Full Auth Module Generator ──
|
|
266
|
+
/** Generate a complete auth helpers module */
|
|
267
|
+
export function generateAuthModule(config) {
|
|
268
|
+
const lines = [
|
|
269
|
+
'// Auto-generated by @plumbus/ui — do not edit',
|
|
270
|
+
'// eslint-disable-next-line @typescript-eslint/no-unused-vars',
|
|
271
|
+
'import React, { useState, useEffect } from "react";',
|
|
272
|
+
'',
|
|
273
|
+
generateAuthTypes(),
|
|
274
|
+
'',
|
|
275
|
+
generateTokenUtils(config),
|
|
276
|
+
'',
|
|
277
|
+
generateAuthFunctions(config),
|
|
278
|
+
'',
|
|
279
|
+
generateUseAuthHook(),
|
|
280
|
+
'',
|
|
281
|
+
generateUseCurrentUserHook(),
|
|
282
|
+
'',
|
|
283
|
+
generateRouteGuard(),
|
|
284
|
+
'',
|
|
285
|
+
];
|
|
286
|
+
if (config?.multiTenant) {
|
|
287
|
+
lines.push(generateTenantContext());
|
|
288
|
+
lines.push('');
|
|
289
|
+
}
|
|
290
|
+
return lines.join('\n');
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=auth-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-generator.js","sourceRoot":"","sources":["../../src/generators/auth-generator.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,qDAAqD;AACrD,kFAAkF;AAiBlF,6BAA6B;AAE7B,+CAA+C;AAC/C,MAAM,UAAU,iBAAiB;IAC/B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmCP,CAAC;AACH,CAAC;AAED,yBAAyB;AAEzB,uCAAuC;AACvC,MAAM,UAAU,kBAAkB,CAAC,MAAyB;IAC1D,MAAM,GAAG,GAAG,MAAM,EAAE,QAAQ,IAAI,oBAAoB,CAAC;IAErD,OAAO,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiChC,CAAC;AACH,CAAC;AAED,+BAA+B;AAE/B,sCAAsC;AACtC,MAAM,UAAU,qBAAqB,CAAC,MAAyB;IAC7D,MAAM,QAAQ,GAAG,MAAM,EAAE,aAAa,IAAI,iBAAiB,CAAC;IAC5D,MAAM,SAAS,GAAG,MAAM,EAAE,cAAc,IAAI,kBAAkB,CAAC;IAC/D,MAAM,UAAU,GAAG,MAAM,EAAE,eAAe,IAAI,mBAAmB,CAAC;IAElE,OAAO;kCACyB,QAAQ;;;;;;;;;;;;;;;;;mBAiBvB,SAAS;;;;;;;;;;;kCAWM,UAAU;;;;;;;;;;;;;;;;EAgB1C,CAAC;AACH,CAAC;AAED,oBAAoB;AAEpB,kCAAkC;AAClC,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6DP,CAAC;AACH,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,0BAA0B;IACxC,OAAO;;;EAGP,CAAC;AACH,CAAC;AAED,oBAAoB;AAEpB,0CAA0C;AAC1C,MAAM,UAAU,kBAAkB;IAChC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCP,CAAC;AACH,CAAC;AAED,uBAAuB;AAEvB,uCAAuC;AACvC,MAAM,UAAU,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;EAcP,CAAC;AACH,CAAC;AAED,mCAAmC;AAEnC,8CAA8C;AAC9C,MAAM,UAAU,kBAAkB,CAAC,MAAyB;IAC1D,MAAM,KAAK,GAAa;QACtB,gDAAgD;QAChD,+DAA+D;QAC/D,qDAAqD;QACrD,EAAE;QACF,iBAAiB,EAAE;QACnB,EAAE;QACF,kBAAkB,CAAC,MAAM,CAAC;QAC1B,EAAE;QACF,qBAAqB,CAAC,MAAM,CAAC;QAC7B,EAAE;QACF,mBAAmB,EAAE;QACrB,EAAE;QACF,0BAA0B,EAAE;QAC5B,EAAE;QACF,kBAAkB,EAAE;QACpB,EAAE;KACH,CAAC;IAEF,IAAI,MAAM,EAAE,WAAW,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CapabilityContract } from '@plumbus/core';
|
|
2
|
+
export interface ClientGeneratorConfig {
|
|
3
|
+
/** Base URL for API requests (default: "") */
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
/** Include JSDoc comments in generated code */
|
|
6
|
+
includeJsDoc?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Generate TypeScript types from a capability's Zod input/output schemas */
|
|
9
|
+
export declare function generateCapabilityTypes(cap: CapabilityContract): string;
|
|
10
|
+
/** Generate a typed fetch-based API client function */
|
|
11
|
+
export declare function generateTypedClient(cap: CapabilityContract, config?: ClientGeneratorConfig): string;
|
|
12
|
+
/** Generate a React hook for a query capability */
|
|
13
|
+
export declare function generateQueryHook(cap: CapabilityContract, config?: ClientGeneratorConfig): string;
|
|
14
|
+
/** Generate a React hook for a mutation capability (action/job) */
|
|
15
|
+
export declare function generateMutationHook(cap: CapabilityContract, config?: ClientGeneratorConfig): string;
|
|
16
|
+
/** Generate the appropriate hook based on capability kind */
|
|
17
|
+
export declare function generateReactHook(cap: CapabilityContract, config?: ClientGeneratorConfig): string;
|
|
18
|
+
export interface FlowTriggerInput {
|
|
19
|
+
name: string;
|
|
20
|
+
domain?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Generate a flow trigger function */
|
|
24
|
+
export declare function generateFlowTrigger(flow: FlowTriggerInput, config?: ClientGeneratorConfig): string;
|
|
25
|
+
/** Generate common response and error types */
|
|
26
|
+
export declare function generateErrorTypes(): string;
|
|
27
|
+
/** Generate a complete typed client module from capabilities and flows */
|
|
28
|
+
export declare function generateClientModule(capabilities: CapabilityContract[], flows: FlowTriggerInput[], config?: ClientGeneratorConfig): string;
|
|
29
|
+
/** Generate a React hooks module from capabilities */
|
|
30
|
+
export declare function generateHooksModule(capabilities: CapabilityContract[], config?: ClientGeneratorConfig): string;
|
|
31
|
+
//# sourceMappingURL=client-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-generator.d.ts","sourceRoot":"","sources":["../../src/generators/client-generator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAIxD,MAAM,WAAW,qBAAqB;IACpC,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAyID,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,CAMvE;AAID,uDAAuD;AACvD,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,kBAAkB,EACvB,MAAM,CAAC,EAAE,qBAAqB,GAC7B,MAAM,CA2CR;AAID,mDAAmD;AACnD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAyBjG;AAED,mEAAmE;AACnE,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,kBAAkB,EACvB,MAAM,CAAC,EAAE,qBAAqB,GAC7B,MAAM,CAiCR;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAEjG;AAID,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,uCAAuC;AACvC,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,EACtB,MAAM,CAAC,EAAE,qBAAqB,GAC7B,MAAM,CA2BR;AAID,+CAA+C;AAC/C,wBAAgB,kBAAkB,IAAI,MAAM,CAW3C;AAID,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,kBAAkB,EAAE,EAClC,KAAK,EAAE,gBAAgB,EAAE,EACzB,MAAM,CAAC,EAAE,qBAAqB,GAC7B,MAAM,CA+BR;AAED,sDAAsD;AACtD,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,kBAAkB,EAAE,EAClC,MAAM,CAAC,EAAE,qBAAqB,GAC7B,MAAM,CAqBR"}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// ── Typed API Client Generator ──
|
|
2
|
+
// Generates typed fetch-based API clients, React hooks, and flow trigger functions
|
|
3
|
+
// from capability contracts and flow definitions.
|
|
4
|
+
// ── Helpers ──
|
|
5
|
+
function toKebabCase(str) {
|
|
6
|
+
return str
|
|
7
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
8
|
+
.replace(/[\s_]+/g, '-')
|
|
9
|
+
.toLowerCase();
|
|
10
|
+
}
|
|
11
|
+
function toPascalCase(str) {
|
|
12
|
+
return str.replace(/(^|[-_ ])(\w)/g, (_, _sep, c) => c.toUpperCase());
|
|
13
|
+
}
|
|
14
|
+
function toCamelCase(str) {
|
|
15
|
+
const pc = toPascalCase(str);
|
|
16
|
+
return pc.charAt(0).toLowerCase() + pc.slice(1);
|
|
17
|
+
}
|
|
18
|
+
function httpMethod(kind) {
|
|
19
|
+
return kind === 'query' ? 'GET' : 'POST';
|
|
20
|
+
}
|
|
21
|
+
function capabilityPath(domain, name) {
|
|
22
|
+
return `/api/${domain}/${toKebabCase(name)}`;
|
|
23
|
+
}
|
|
24
|
+
// ── Zod Schema → TypeScript Type String ──
|
|
25
|
+
function getZodTypeName(schema) {
|
|
26
|
+
if (schema && typeof schema === 'object' && '_def' in schema) {
|
|
27
|
+
const def = schema._def;
|
|
28
|
+
if (typeof def.typeName === 'string')
|
|
29
|
+
return def.typeName;
|
|
30
|
+
}
|
|
31
|
+
return 'ZodUnknown';
|
|
32
|
+
}
|
|
33
|
+
function getZodDef(schema) {
|
|
34
|
+
if (schema && typeof schema === 'object' && '_def' in schema) {
|
|
35
|
+
return schema._def;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function zodSchemaToTypeString(schema, indent = '') {
|
|
40
|
+
const def = getZodDef(schema);
|
|
41
|
+
if (!def)
|
|
42
|
+
return 'unknown';
|
|
43
|
+
const typeName = typeof def.typeName === 'string' ? def.typeName : 'ZodUnknown';
|
|
44
|
+
switch (typeName) {
|
|
45
|
+
case 'ZodString':
|
|
46
|
+
return 'string';
|
|
47
|
+
case 'ZodNumber':
|
|
48
|
+
case 'ZodBigInt':
|
|
49
|
+
return 'number';
|
|
50
|
+
case 'ZodBoolean':
|
|
51
|
+
return 'boolean';
|
|
52
|
+
case 'ZodDate':
|
|
53
|
+
return 'string';
|
|
54
|
+
case 'ZodLiteral':
|
|
55
|
+
return typeof def.value === 'string' ? `"${def.value}"` : String(def.value);
|
|
56
|
+
case 'ZodEnum':
|
|
57
|
+
if (Array.isArray(def.values)) {
|
|
58
|
+
return def.values.map((v) => `"${v}"`).join(' | ');
|
|
59
|
+
}
|
|
60
|
+
return 'string';
|
|
61
|
+
case 'ZodNativeEnum':
|
|
62
|
+
return 'string';
|
|
63
|
+
case 'ZodArray': {
|
|
64
|
+
const inner = zodSchemaToTypeString(def.type, indent);
|
|
65
|
+
return inner.includes('|') || inner.includes('{') ? `Array<${inner}>` : `${inner}[]`;
|
|
66
|
+
}
|
|
67
|
+
case 'ZodObject': {
|
|
68
|
+
if (typeof def.shape === 'function') {
|
|
69
|
+
const shape = def.shape();
|
|
70
|
+
const entries = Object.entries(shape);
|
|
71
|
+
if (entries.length === 0)
|
|
72
|
+
return 'Record<string, unknown>';
|
|
73
|
+
const innerIndent = `${indent} `;
|
|
74
|
+
const fields = entries.map(([key, fieldSchema]) => {
|
|
75
|
+
const optional = isFieldOptional(fieldSchema);
|
|
76
|
+
const innerType = zodSchemaToTypeString(unwrapWrappers(fieldSchema), innerIndent);
|
|
77
|
+
const nullable = getZodTypeName(fieldSchema) === 'ZodNullable';
|
|
78
|
+
const typeStr = nullable ? `${innerType} | null` : innerType;
|
|
79
|
+
return `${innerIndent}${key}${optional ? '?' : ''}: ${typeStr};`;
|
|
80
|
+
});
|
|
81
|
+
return `{\n${fields.join('\n')}\n${indent}}`;
|
|
82
|
+
}
|
|
83
|
+
return 'Record<string, unknown>';
|
|
84
|
+
}
|
|
85
|
+
case 'ZodRecord': {
|
|
86
|
+
const valueType = def.valueType ? zodSchemaToTypeString(def.valueType, indent) : 'unknown';
|
|
87
|
+
return `Record<string, ${valueType}>`;
|
|
88
|
+
}
|
|
89
|
+
case 'ZodOptional':
|
|
90
|
+
return zodSchemaToTypeString(def.innerType, indent);
|
|
91
|
+
case 'ZodNullable': {
|
|
92
|
+
const inner = zodSchemaToTypeString(def.innerType, indent);
|
|
93
|
+
return `${inner} | null`;
|
|
94
|
+
}
|
|
95
|
+
case 'ZodDefault':
|
|
96
|
+
return zodSchemaToTypeString(def.innerType, indent);
|
|
97
|
+
case 'ZodUnion': {
|
|
98
|
+
if (Array.isArray(def.options)) {
|
|
99
|
+
return def.options.map((o) => zodSchemaToTypeString(o, indent)).join(' | ');
|
|
100
|
+
}
|
|
101
|
+
return 'unknown';
|
|
102
|
+
}
|
|
103
|
+
case 'ZodAny':
|
|
104
|
+
return 'unknown';
|
|
105
|
+
default:
|
|
106
|
+
return 'unknown';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function isFieldOptional(schema) {
|
|
110
|
+
const def = getZodDef(schema);
|
|
111
|
+
if (!def)
|
|
112
|
+
return false;
|
|
113
|
+
const tn = typeof def.typeName === 'string' ? def.typeName : '';
|
|
114
|
+
if (tn === 'ZodOptional' || tn === 'ZodDefault')
|
|
115
|
+
return true;
|
|
116
|
+
if (tn === 'ZodNullable' && def.innerType)
|
|
117
|
+
return isFieldOptional(def.innerType);
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
function unwrapWrappers(schema) {
|
|
121
|
+
const def = getZodDef(schema);
|
|
122
|
+
if (!def)
|
|
123
|
+
return schema;
|
|
124
|
+
const tn = typeof def.typeName === 'string' ? def.typeName : '';
|
|
125
|
+
if (tn === 'ZodOptional' || tn === 'ZodDefault' || tn === 'ZodNullable') {
|
|
126
|
+
return unwrapWrappers(def.innerType);
|
|
127
|
+
}
|
|
128
|
+
return schema;
|
|
129
|
+
}
|
|
130
|
+
// ── Type Generators ──
|
|
131
|
+
/** Generate TypeScript types from a capability's Zod input/output schemas */
|
|
132
|
+
export function generateCapabilityTypes(cap) {
|
|
133
|
+
const pascal = toPascalCase(cap.name);
|
|
134
|
+
const inputType = zodSchemaToTypeString(cap.input);
|
|
135
|
+
const outputType = zodSchemaToTypeString(cap.output);
|
|
136
|
+
return `export type ${pascal}Input = ${inputType};
|
|
137
|
+
export type ${pascal}Output = ${outputType};`;
|
|
138
|
+
}
|
|
139
|
+
// ── Client Function Generator ──
|
|
140
|
+
/** Generate a typed fetch-based API client function */
|
|
141
|
+
export function generateTypedClient(cap, config) {
|
|
142
|
+
const fnName = toCamelCase(cap.name);
|
|
143
|
+
const pascal = toPascalCase(cap.name);
|
|
144
|
+
const method = httpMethod(cap.kind);
|
|
145
|
+
const urlPath = capabilityPath(cap.domain, cap.name);
|
|
146
|
+
const base = config?.baseUrl ?? '';
|
|
147
|
+
const jsdoc = config?.includeJsDoc
|
|
148
|
+
? `/** ${cap.description ?? `${cap.kind} — ${cap.name}`} */\n`
|
|
149
|
+
: '';
|
|
150
|
+
const queryParams = method === 'GET'
|
|
151
|
+
? `\n const params = new URLSearchParams();
|
|
152
|
+
for (const [k, v] of Object.entries(input)) {
|
|
153
|
+
if (v !== undefined && v !== null) params.set(k, String(v));
|
|
154
|
+
}
|
|
155
|
+
const qs = params.toString();
|
|
156
|
+
const url = qs ? \`${base}${urlPath}?\${qs}\` : "${base}${urlPath}";`
|
|
157
|
+
: '';
|
|
158
|
+
const fetchUrl = method === 'GET' ? 'url' : `"${base}${urlPath}"`;
|
|
159
|
+
const fetchBody = method === 'GET' ? '' : `\n body: JSON.stringify(input),`;
|
|
160
|
+
return `${jsdoc}export async function ${fnName}(
|
|
161
|
+
input: ${pascal}Input,
|
|
162
|
+
options?: { headers?: Record<string, string>; signal?: AbortSignal },
|
|
163
|
+
): Promise<${pascal}Output> {${queryParams}
|
|
164
|
+
const response = await fetch(${fetchUrl}, {
|
|
165
|
+
method: "${method}",
|
|
166
|
+
headers: { "Content-Type": "application/json", ...options?.headers },${fetchBody}
|
|
167
|
+
signal: options?.signal,
|
|
168
|
+
});
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
const body = await response.json().catch(() => ({}));
|
|
171
|
+
throw Object.assign(new Error(body.message ?? \`Request failed: \${response.status}\`), {
|
|
172
|
+
status: response.status,
|
|
173
|
+
code: body.code,
|
|
174
|
+
metadata: body.metadata,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return response.json() as Promise<${pascal}Output>;
|
|
178
|
+
}`;
|
|
179
|
+
}
|
|
180
|
+
// ── React Hook Generator ──
|
|
181
|
+
/** Generate a React hook for a query capability */
|
|
182
|
+
export function generateQueryHook(cap, config) {
|
|
183
|
+
const hookName = `use${toPascalCase(cap.name)}`;
|
|
184
|
+
const fnName = toCamelCase(cap.name);
|
|
185
|
+
const pascal = toPascalCase(cap.name);
|
|
186
|
+
const jsdoc = config?.includeJsDoc
|
|
187
|
+
? `/** Hook for ${cap.description ?? cap.name} (query) */\n`
|
|
188
|
+
: '';
|
|
189
|
+
return `${jsdoc}export function ${hookName}(input: ${pascal}Input) {
|
|
190
|
+
const [data, setData] = useState<${pascal}Output | null>(null);
|
|
191
|
+
const [loading, setLoading] = useState(false);
|
|
192
|
+
const [error, setError] = useState<Error | null>(null);
|
|
193
|
+
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
let cancelled = false;
|
|
196
|
+
setLoading(true);
|
|
197
|
+
${fnName}(input)
|
|
198
|
+
.then((result) => { if (!cancelled) setData(result); })
|
|
199
|
+
.catch((err) => { if (!cancelled) setError(err instanceof Error ? err : new Error(String(err))); })
|
|
200
|
+
.finally(() => { if (!cancelled) setLoading(false); });
|
|
201
|
+
return () => { cancelled = true; };
|
|
202
|
+
}, [JSON.stringify(input)]);
|
|
203
|
+
|
|
204
|
+
return { data, loading, error };
|
|
205
|
+
}`;
|
|
206
|
+
}
|
|
207
|
+
/** Generate a React hook for a mutation capability (action/job) */
|
|
208
|
+
export function generateMutationHook(cap, config) {
|
|
209
|
+
const hookName = `use${toPascalCase(cap.name)}`;
|
|
210
|
+
const fnName = toCamelCase(cap.name);
|
|
211
|
+
const pascal = toPascalCase(cap.name);
|
|
212
|
+
const jsdoc = config?.includeJsDoc
|
|
213
|
+
? `/** Hook for ${cap.description ?? cap.name} (${cap.kind}) */\n`
|
|
214
|
+
: '';
|
|
215
|
+
return `${jsdoc}export function ${hookName}() {
|
|
216
|
+
const [loading, setLoading] = useState(false);
|
|
217
|
+
const [error, setError] = useState<Error | null>(null);
|
|
218
|
+
const [data, setData] = useState<${pascal}Output | null>(null);
|
|
219
|
+
|
|
220
|
+
const mutate = async (input: ${pascal}Input) => {
|
|
221
|
+
setLoading(true);
|
|
222
|
+
setError(null);
|
|
223
|
+
try {
|
|
224
|
+
const result = await ${fnName}(input);
|
|
225
|
+
setData(result);
|
|
226
|
+
return result;
|
|
227
|
+
} catch (e) {
|
|
228
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
229
|
+
setError(err);
|
|
230
|
+
throw err;
|
|
231
|
+
} finally {
|
|
232
|
+
setLoading(false);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const reset = () => { setData(null); setError(null); };
|
|
237
|
+
|
|
238
|
+
return { mutate, data, loading, error, reset };
|
|
239
|
+
}`;
|
|
240
|
+
}
|
|
241
|
+
/** Generate the appropriate hook based on capability kind */
|
|
242
|
+
export function generateReactHook(cap, config) {
|
|
243
|
+
return cap.kind === 'query' ? generateQueryHook(cap, config) : generateMutationHook(cap, config);
|
|
244
|
+
}
|
|
245
|
+
/** Generate a flow trigger function */
|
|
246
|
+
export function generateFlowTrigger(flow, config) {
|
|
247
|
+
const fnName = `start${toPascalCase(flow.name)}`;
|
|
248
|
+
const pascal = toPascalCase(flow.name);
|
|
249
|
+
const base = config?.baseUrl ?? '';
|
|
250
|
+
const domain = flow.domain ?? 'flows';
|
|
251
|
+
const urlPath = `/api/${domain}/${toKebabCase(flow.name)}/start`;
|
|
252
|
+
const jsdoc = config?.includeJsDoc ? `/** Start flow: ${flow.description ?? flow.name} */\n` : '';
|
|
253
|
+
return `${jsdoc}export async function ${fnName}(
|
|
254
|
+
input: ${pascal}FlowInput,
|
|
255
|
+
options?: { headers?: Record<string, string> },
|
|
256
|
+
): Promise<{ executionId: string; status: string }> {
|
|
257
|
+
const response = await fetch("${base}${urlPath}", {
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers: { "Content-Type": "application/json", ...options?.headers },
|
|
260
|
+
body: JSON.stringify(input),
|
|
261
|
+
});
|
|
262
|
+
if (!response.ok) {
|
|
263
|
+
const body = await response.json().catch(() => ({}));
|
|
264
|
+
throw Object.assign(new Error(body.message ?? \`Flow start failed: \${response.status}\`), {
|
|
265
|
+
status: response.status,
|
|
266
|
+
code: body.code,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return response.json() as Promise<{ executionId: string; status: string }>;
|
|
270
|
+
}`;
|
|
271
|
+
}
|
|
272
|
+
// ── Response/Error Types ──
|
|
273
|
+
/** Generate common response and error types */
|
|
274
|
+
export function generateErrorTypes() {
|
|
275
|
+
return `export interface PlumbusApiError {
|
|
276
|
+
status: number;
|
|
277
|
+
code?: string;
|
|
278
|
+
message: string;
|
|
279
|
+
metadata?: Record<string, unknown>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function isPlumbusApiError(error: unknown): error is PlumbusApiError {
|
|
283
|
+
return typeof error === "object" && error !== null && "status" in error && "message" in error;
|
|
284
|
+
}`;
|
|
285
|
+
}
|
|
286
|
+
// ── Full Client Module Generator ──
|
|
287
|
+
/** Generate a complete typed client module from capabilities and flows */
|
|
288
|
+
export function generateClientModule(capabilities, flows, config) {
|
|
289
|
+
const lines = [
|
|
290
|
+
'// Auto-generated by @plumbus/ui — do not edit',
|
|
291
|
+
'',
|
|
292
|
+
generateErrorTypes(),
|
|
293
|
+
'',
|
|
294
|
+
];
|
|
295
|
+
// Type definitions
|
|
296
|
+
for (const cap of capabilities) {
|
|
297
|
+
lines.push(generateCapabilityTypes(cap));
|
|
298
|
+
lines.push('');
|
|
299
|
+
}
|
|
300
|
+
for (const flow of flows) {
|
|
301
|
+
lines.push(`export type ${toPascalCase(flow.name)}FlowInput = Record<string, unknown>;`);
|
|
302
|
+
lines.push('');
|
|
303
|
+
}
|
|
304
|
+
// Client functions
|
|
305
|
+
for (const cap of capabilities) {
|
|
306
|
+
lines.push(generateTypedClient(cap, config));
|
|
307
|
+
lines.push('');
|
|
308
|
+
}
|
|
309
|
+
// Flow triggers
|
|
310
|
+
for (const flow of flows) {
|
|
311
|
+
lines.push(generateFlowTrigger(flow, config));
|
|
312
|
+
lines.push('');
|
|
313
|
+
}
|
|
314
|
+
return lines.join('\n');
|
|
315
|
+
}
|
|
316
|
+
/** Generate a React hooks module from capabilities */
|
|
317
|
+
export function generateHooksModule(capabilities, config) {
|
|
318
|
+
const lines = [
|
|
319
|
+
'// Auto-generated by @plumbus/ui — do not edit',
|
|
320
|
+
'import { useState, useEffect } from "react";',
|
|
321
|
+
'',
|
|
322
|
+
];
|
|
323
|
+
// Import types from client
|
|
324
|
+
for (const cap of capabilities) {
|
|
325
|
+
const pascal = toPascalCase(cap.name);
|
|
326
|
+
lines.push(`import type { ${pascal}Input, ${pascal}Output } from "./client";`);
|
|
327
|
+
lines.push(`import { ${toCamelCase(cap.name)} } from "./client";`);
|
|
328
|
+
}
|
|
329
|
+
lines.push('');
|
|
330
|
+
for (const cap of capabilities) {
|
|
331
|
+
lines.push(generateReactHook(cap, config));
|
|
332
|
+
lines.push('');
|
|
333
|
+
}
|
|
334
|
+
return lines.join('\n');
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=client-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-generator.js","sourceRoot":"","sources":["../../src/generators/client-generator.ts"],"names":[],"mappings":"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"}
|