@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,444 @@
|
|
|
1
|
+
// ── Next.js Project Template Generator ──
|
|
2
|
+
// Generates a Next.js project scaffold with Plumbus auth integration,
|
|
3
|
+
// generated client imports, and example pages wired to capabilities.
|
|
4
|
+
// ── Helpers ──
|
|
5
|
+
function toPascalCase(str) {
|
|
6
|
+
return str.replace(/(^|[-_ ])(\w)/g, (_, _sep, c) => c.toUpperCase());
|
|
7
|
+
}
|
|
8
|
+
function toKebabCase(str) {
|
|
9
|
+
return str
|
|
10
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
11
|
+
.replace(/[\s_]+/g, '-')
|
|
12
|
+
.toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
/** Generate package.json for the Next.js project */
|
|
15
|
+
export function generatePackageJson(config) {
|
|
16
|
+
const name = toKebabCase(config.appName);
|
|
17
|
+
const content = JSON.stringify({
|
|
18
|
+
name,
|
|
19
|
+
version: '0.1.0',
|
|
20
|
+
private: true,
|
|
21
|
+
scripts: {
|
|
22
|
+
dev: 'next dev',
|
|
23
|
+
build: 'next build',
|
|
24
|
+
start: 'next start',
|
|
25
|
+
},
|
|
26
|
+
dependencies: {
|
|
27
|
+
next: '^14.0.0',
|
|
28
|
+
react: '^18.2.0',
|
|
29
|
+
'react-dom': '^18.2.0',
|
|
30
|
+
tailwindcss: '^4.0.0',
|
|
31
|
+
'@tailwindcss/postcss': '^4.0.0',
|
|
32
|
+
},
|
|
33
|
+
devDependencies: {
|
|
34
|
+
typescript: '^5.0.0',
|
|
35
|
+
'@types/react': '^18.2.0',
|
|
36
|
+
'@types/react-dom': '^18.2.0',
|
|
37
|
+
},
|
|
38
|
+
}, null, 2);
|
|
39
|
+
return { path: 'package.json', content };
|
|
40
|
+
}
|
|
41
|
+
/** Generate tsconfig.json */
|
|
42
|
+
export function generateTsConfig() {
|
|
43
|
+
const content = JSON.stringify({
|
|
44
|
+
compilerOptions: {
|
|
45
|
+
target: 'ES2017',
|
|
46
|
+
lib: ['dom', 'dom.iterable', 'esnext'],
|
|
47
|
+
allowJs: true,
|
|
48
|
+
skipLibCheck: true,
|
|
49
|
+
strict: true,
|
|
50
|
+
noEmit: true,
|
|
51
|
+
esModuleInterop: true,
|
|
52
|
+
module: 'esnext',
|
|
53
|
+
moduleResolution: 'bundler',
|
|
54
|
+
resolveJsonModule: true,
|
|
55
|
+
isolatedModules: true,
|
|
56
|
+
jsx: 'preserve',
|
|
57
|
+
incremental: true,
|
|
58
|
+
paths: {
|
|
59
|
+
'@/*': ['./*'],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
include: ['next-env.d.ts', '**/*.ts', '**/*.tsx'],
|
|
63
|
+
exclude: ['node_modules'],
|
|
64
|
+
}, null, 2);
|
|
65
|
+
return { path: 'tsconfig.json', content };
|
|
66
|
+
}
|
|
67
|
+
/** Generate globals.css with Tailwind import and basic resets */
|
|
68
|
+
export function generateGlobalsCss() {
|
|
69
|
+
return {
|
|
70
|
+
path: 'app/globals.css',
|
|
71
|
+
content: `@import "tailwindcss";
|
|
72
|
+
|
|
73
|
+
/* ── Base resets ── */
|
|
74
|
+
*,
|
|
75
|
+
*::before,
|
|
76
|
+
*::after {
|
|
77
|
+
box-sizing: border-box;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
html {
|
|
81
|
+
min-height: 100%;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
body {
|
|
85
|
+
margin: 0;
|
|
86
|
+
min-height: 100vh;
|
|
87
|
+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
88
|
+
-webkit-font-smoothing: antialiased;
|
|
89
|
+
-moz-osx-font-smoothing: grayscale;
|
|
90
|
+
}
|
|
91
|
+
`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** Generate postcss.config.mjs for Tailwind CSS */
|
|
95
|
+
export function generatePostcssConfig() {
|
|
96
|
+
return {
|
|
97
|
+
path: 'postcss.config.mjs',
|
|
98
|
+
content: `export default {\n plugins: {\n "@tailwindcss/postcss": {},\n },\n};\n`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Generate the layout component */
|
|
102
|
+
export function generateLayout(config) {
|
|
103
|
+
const auth = config.auth !== false;
|
|
104
|
+
const authImport = auth ? `import { AuthProvider } from "../components/AuthProvider";\n` : '';
|
|
105
|
+
const authWrapOpen = auth ? ' <AuthProvider>\n' : '';
|
|
106
|
+
const authWrapClose = auth ? ' </AuthProvider>\n' : '';
|
|
107
|
+
return {
|
|
108
|
+
path: 'app/layout.tsx',
|
|
109
|
+
content: `import type { Metadata } from "next";
|
|
110
|
+
import "./globals.css";
|
|
111
|
+
${authImport}
|
|
112
|
+
export const metadata: Metadata = {
|
|
113
|
+
title: "${config.appName}",
|
|
114
|
+
description: "Generated by @plumbus/ui",
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
118
|
+
return (
|
|
119
|
+
<html lang="en">
|
|
120
|
+
<body>
|
|
121
|
+
${authWrapOpen} {children}
|
|
122
|
+
${authWrapClose} </body>
|
|
123
|
+
</html>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** Generate the home page */
|
|
130
|
+
export function generateHomePage(config) {
|
|
131
|
+
return {
|
|
132
|
+
path: 'app/page.tsx',
|
|
133
|
+
content: `export default function Home() {
|
|
134
|
+
return (
|
|
135
|
+
<main>
|
|
136
|
+
<h1>${config.appName}</h1>
|
|
137
|
+
<p>Welcome to your Plumbus application.</p>
|
|
138
|
+
</main>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Generate example page for a capability */
|
|
145
|
+
export function generateCapabilityPage(cap) {
|
|
146
|
+
const pascal = toPascalCase(cap.name);
|
|
147
|
+
const slug = toKebabCase(cap.name);
|
|
148
|
+
if (cap.kind === 'query') {
|
|
149
|
+
return {
|
|
150
|
+
path: `app/${slug}/page.tsx`,
|
|
151
|
+
content: `"use client";
|
|
152
|
+
|
|
153
|
+
import { use${pascal} } from "@/generated/hooks";
|
|
154
|
+
|
|
155
|
+
export default function ${pascal}Page() {
|
|
156
|
+
const { data, loading, error } = use${pascal}({});
|
|
157
|
+
|
|
158
|
+
if (loading) return <div>Loading...</div>;
|
|
159
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<main>
|
|
163
|
+
<h1>${cap.name}</h1>
|
|
164
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
165
|
+
</main>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
path: `app/${slug}/page.tsx`,
|
|
173
|
+
content: `"use client";
|
|
174
|
+
|
|
175
|
+
import { use${pascal} } from "@/generated/hooks";
|
|
176
|
+
|
|
177
|
+
export default function ${pascal}Page() {
|
|
178
|
+
const { mutate, data, loading, error, reset } = use${pascal}();
|
|
179
|
+
|
|
180
|
+
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
181
|
+
e.preventDefault();
|
|
182
|
+
const formData = new FormData(e.currentTarget);
|
|
183
|
+
const input = Object.fromEntries(formData.entries());
|
|
184
|
+
await mutate(input as Record<string, unknown>);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<main>
|
|
189
|
+
<h1>${cap.name}</h1>
|
|
190
|
+
<form onSubmit={handleSubmit}>
|
|
191
|
+
<button type="submit" disabled={loading}>
|
|
192
|
+
{loading ? "Processing..." : "Submit"}
|
|
193
|
+
</button>
|
|
194
|
+
</form>
|
|
195
|
+
{error && <p>Error: {error.message}</p>}
|
|
196
|
+
{data && (
|
|
197
|
+
<div>
|
|
198
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
199
|
+
<button onClick={reset}>Reset</button>
|
|
200
|
+
</div>
|
|
201
|
+
)}
|
|
202
|
+
</main>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
`,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/** Generate AuthProvider component */
|
|
209
|
+
export function generateAuthProvider() {
|
|
210
|
+
return {
|
|
211
|
+
path: 'components/AuthProvider.tsx',
|
|
212
|
+
content: `"use client";
|
|
213
|
+
|
|
214
|
+
import React, { createContext, useContext, useState, useEffect } from "react";
|
|
215
|
+
import { getStoredToken, isTokenExpired, refreshSession } from "@/generated/auth";
|
|
216
|
+
import type { AuthUser, AuthState } from "@/generated/auth";
|
|
217
|
+
|
|
218
|
+
const AuthContext = createContext<AuthState>({
|
|
219
|
+
user: null,
|
|
220
|
+
isAuthenticated: false,
|
|
221
|
+
isLoading: true,
|
|
222
|
+
error: null,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
export function useAuthContext() {
|
|
226
|
+
return useContext(AuthContext);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
230
|
+
const [state, setState] = useState<AuthState>({
|
|
231
|
+
user: null,
|
|
232
|
+
isAuthenticated: false,
|
|
233
|
+
isLoading: true,
|
|
234
|
+
error: null,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
const token = getStoredToken();
|
|
239
|
+
if (!token || isTokenExpired(token)) {
|
|
240
|
+
setState({ user: null, isAuthenticated: false, isLoading: false, error: null });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
refreshSession()
|
|
244
|
+
.then((user: AuthUser | null) => {
|
|
245
|
+
setState({ user, isAuthenticated: !!user, isLoading: false, error: null });
|
|
246
|
+
})
|
|
247
|
+
.catch((err: unknown) => {
|
|
248
|
+
setState({
|
|
249
|
+
user: null,
|
|
250
|
+
isAuthenticated: false,
|
|
251
|
+
isLoading: false,
|
|
252
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}, []);
|
|
256
|
+
|
|
257
|
+
return <AuthContext.Provider value={state}>{children}</AuthContext.Provider>;
|
|
258
|
+
}
|
|
259
|
+
`,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/** Generate the "generated" directory placeholder */
|
|
263
|
+
export function generatePlaceholderFiles() {
|
|
264
|
+
return [
|
|
265
|
+
{
|
|
266
|
+
path: 'generated/.gitkeep',
|
|
267
|
+
content: '',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
path: 'hooks/.gitkeep',
|
|
271
|
+
content: '',
|
|
272
|
+
},
|
|
273
|
+
];
|
|
274
|
+
}
|
|
275
|
+
/** Generate .env.local template with Plumbus-specific variables */
|
|
276
|
+
export function generateEnvLocal(config) {
|
|
277
|
+
const baseUrl = config.apiBaseUrl ?? 'http://localhost:3000';
|
|
278
|
+
return {
|
|
279
|
+
path: '.env.local',
|
|
280
|
+
content: `# Plumbus API
|
|
281
|
+
NEXT_PUBLIC_API_BASE_URL=${baseUrl}
|
|
282
|
+
|
|
283
|
+
# Auth
|
|
284
|
+
NEXT_PUBLIC_AUTH_ENABLED=${config.auth !== false ? 'true' : 'false'}
|
|
285
|
+
AUTH_SECRET=change-me-in-production
|
|
286
|
+
|
|
287
|
+
# Database (if using Next.js API routes with direct DB access)
|
|
288
|
+
# DATABASE_URL=postgresql://postgres:postgres@localhost:5432/plumbus
|
|
289
|
+
`,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/** Generate a global error boundary component */
|
|
293
|
+
export function generateErrorBoundary() {
|
|
294
|
+
return {
|
|
295
|
+
path: 'app/error.tsx',
|
|
296
|
+
content: `"use client";
|
|
297
|
+
|
|
298
|
+
import { useEffect } from "react";
|
|
299
|
+
|
|
300
|
+
export default function ErrorBoundary({
|
|
301
|
+
error,
|
|
302
|
+
reset,
|
|
303
|
+
}: {
|
|
304
|
+
error: Error & { digest?: string };
|
|
305
|
+
reset: () => void;
|
|
306
|
+
}) {
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
console.error("Application error:", error);
|
|
309
|
+
}, [error]);
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<main>
|
|
313
|
+
<h2>Something went wrong</h2>
|
|
314
|
+
<p>{error.message}</p>
|
|
315
|
+
<button onClick={reset}>Try again</button>
|
|
316
|
+
</main>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
`,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/** Generate a global loading component */
|
|
323
|
+
export function generateLoadingComponent() {
|
|
324
|
+
return {
|
|
325
|
+
path: 'app/loading.tsx',
|
|
326
|
+
content: `export default function Loading() {
|
|
327
|
+
return (
|
|
328
|
+
<main>
|
|
329
|
+
<div role="status" aria-label="Loading">
|
|
330
|
+
<p>Loading...</p>
|
|
331
|
+
</div>
|
|
332
|
+
</main>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
`,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/** Generate Next.js middleware for auth token forwarding */
|
|
339
|
+
export function generateMiddleware(config) {
|
|
340
|
+
const protectedPaths = config.auth !== false
|
|
341
|
+
? `
|
|
342
|
+
// Protect routes that require authentication
|
|
343
|
+
const protectedPaths = ["/dashboard", "/settings", "/api/protected"];
|
|
344
|
+
const isProtected = protectedPaths.some((p) => request.nextUrl.pathname.startsWith(p));
|
|
345
|
+
|
|
346
|
+
if (isProtected) {
|
|
347
|
+
const token = request.cookies.get("auth_token")?.value;
|
|
348
|
+
if (!token) {
|
|
349
|
+
return NextResponse.redirect(new URL("/login", request.url));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
`
|
|
353
|
+
: '';
|
|
354
|
+
return {
|
|
355
|
+
path: 'middleware.ts',
|
|
356
|
+
content: `import { NextResponse } from "next/server";
|
|
357
|
+
import type { NextRequest } from "next/server";
|
|
358
|
+
|
|
359
|
+
export function middleware(request: NextRequest) {${protectedPaths}
|
|
360
|
+
return NextResponse.next();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export const config = {
|
|
364
|
+
matcher: [
|
|
365
|
+
// Match all paths except static files and Next.js internals
|
|
366
|
+
"/((?!_next/static|_next/image|favicon.ico).*)",
|
|
367
|
+
],
|
|
368
|
+
};
|
|
369
|
+
`,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
/** Generate API route helper for proxying to the Plumbus backend */
|
|
373
|
+
export function generateApiRouteHelper(config) {
|
|
374
|
+
const baseUrl = config.apiBaseUrl ?? 'http://localhost:3000';
|
|
375
|
+
return {
|
|
376
|
+
path: 'app/api/plumbus/[...path]/route.ts',
|
|
377
|
+
content: `import { NextRequest, NextResponse } from "next/server";
|
|
378
|
+
|
|
379
|
+
const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL ?? "${baseUrl}";
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Proxy handler that forwards requests to the Plumbus API server.
|
|
383
|
+
* Forwards auth headers and returns the upstream response.
|
|
384
|
+
*/
|
|
385
|
+
async function handler(request: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
|
386
|
+
const { path } = await params;
|
|
387
|
+
const target = new URL(path.join("/"), API_BASE);
|
|
388
|
+
|
|
389
|
+
// Forward query parameters
|
|
390
|
+
request.nextUrl.searchParams.forEach((value, key) => {
|
|
391
|
+
target.searchParams.set(key, value);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const headers = new Headers(request.headers);
|
|
395
|
+
headers.delete("host");
|
|
396
|
+
|
|
397
|
+
const resp = await fetch(target.toString(), {
|
|
398
|
+
method: request.method,
|
|
399
|
+
headers,
|
|
400
|
+
body: request.method !== "GET" && request.method !== "HEAD" ? await request.text() : undefined,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const body = await resp.text();
|
|
404
|
+
return new NextResponse(body, {
|
|
405
|
+
status: resp.status,
|
|
406
|
+
headers: { "Content-Type": resp.headers.get("Content-Type") ?? "application/json" },
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export const GET = handler;
|
|
411
|
+
export const POST = handler;
|
|
412
|
+
export const PUT = handler;
|
|
413
|
+
export const PATCH = handler;
|
|
414
|
+
export const DELETE = handler;
|
|
415
|
+
`,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
/** Generate the complete Next.js template file set */
|
|
419
|
+
export function generateNextjsTemplate(config, capabilities) {
|
|
420
|
+
const files = [
|
|
421
|
+
generatePackageJson(config),
|
|
422
|
+
generateTsConfig(),
|
|
423
|
+
generateGlobalsCss(),
|
|
424
|
+
generatePostcssConfig(),
|
|
425
|
+
generateLayout(config),
|
|
426
|
+
generateHomePage(config),
|
|
427
|
+
generateEnvLocal(config),
|
|
428
|
+
generateErrorBoundary(),
|
|
429
|
+
generateLoadingComponent(),
|
|
430
|
+
generateMiddleware(config),
|
|
431
|
+
generateApiRouteHelper(config),
|
|
432
|
+
...generatePlaceholderFiles(),
|
|
433
|
+
];
|
|
434
|
+
if (config.auth !== false) {
|
|
435
|
+
files.push(generateAuthProvider());
|
|
436
|
+
}
|
|
437
|
+
if (capabilities) {
|
|
438
|
+
for (const cap of capabilities) {
|
|
439
|
+
files.push(generateCapabilityPage(cap));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return files;
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=nextjs-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextjs-template.js","sourceRoot":"","sources":["../../src/generators/nextjs-template.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { generateCapabilityTypes, generateClientModule, generateErrorTypes, generateFlowTrigger, generateHooksModule, generateMutationHook, generateQueryHook, generateReactHook, generateTypedClient, } from './generators/client-generator.js';
|
|
2
|
+
export type { ClientGeneratorConfig, FlowTriggerInput } from './generators/client-generator.js';
|
|
3
|
+
export { generateAuthFunctions, generateAuthModule, generateAuthTypes, generateRouteGuard, generateTenantContext, generateTokenUtils, generateUseAuthHook, generateUseCurrentUserHook, } from './generators/auth-generator.js';
|
|
4
|
+
export type { AuthHelperConfig } from './generators/auth-generator.js';
|
|
5
|
+
export { generateApiRouteHelper, generateAuthProvider, generateCapabilityPage, generateEnvLocal, generateErrorBoundary, generateGlobalsCss, generateHomePage, generateLayout, generateLoadingComponent, generateMiddleware, generateNextjsTemplate, generatePackageJson, generatePlaceholderFiles, generatePostcssConfig, generateTsConfig, } from './generators/nextjs-template.js';
|
|
6
|
+
export type { GeneratedFile, NextjsTemplateConfig } from './generators/nextjs-template.js';
|
|
7
|
+
export { extractFieldHint, extractFormHints, generateFormHintsCode, generateFormHintsModule, } from './generators/form-generator.js';
|
|
8
|
+
export type { FormFieldHint, FormFieldType, FormHints, FormValidation, } from './generators/form-generator.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAGhG,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,YAAY,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGvE,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,wBAAwB,EACxB,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAG3F,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,aAAa,EACb,aAAa,EACb,SAAS,EACT,cAAc,GACf,MAAM,gCAAgC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// ── @plumbus/ui ──
|
|
2
|
+
// UI layer generators for Plumbus framework
|
|
3
|
+
// Client & Hooks Generators
|
|
4
|
+
export { generateCapabilityTypes, generateClientModule, generateErrorTypes, generateFlowTrigger, generateHooksModule, generateMutationHook, generateQueryHook, generateReactHook, generateTypedClient, } from './generators/client-generator.js';
|
|
5
|
+
// Auth Helpers Generator
|
|
6
|
+
export { generateAuthFunctions, generateAuthModule, generateAuthTypes, generateRouteGuard, generateTenantContext, generateTokenUtils, generateUseAuthHook, generateUseCurrentUserHook, } from './generators/auth-generator.js';
|
|
7
|
+
// Next.js Template Generator
|
|
8
|
+
export { generateApiRouteHelper, generateAuthProvider, generateCapabilityPage, generateEnvLocal, generateErrorBoundary, generateGlobalsCss, generateHomePage, generateLayout, generateLoadingComponent, generateMiddleware, generateNextjsTemplate, generatePackageJson, generatePlaceholderFiles, generatePostcssConfig, generateTsConfig, } from './generators/nextjs-template.js';
|
|
9
|
+
// Form Generation Hints
|
|
10
|
+
export { extractFieldHint, extractFormHints, generateFormHintsCode, generateFormHintsModule, } from './generators/form-generator.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,4CAA4C;AAE5C,4BAA4B;AAC5B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC;AAG1C,yBAAyB;AACzB,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AAGxC,6BAA6B;AAC7B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,wBAAwB,EACxB,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,iCAAiC,CAAC;AAGzC,wBAAwB;AACxB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,gCAAgC,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Auth Generator
|
|
2
|
+
|
|
3
|
+
Generates frontend authentication utilities — types, token management, login/logout functions, React hooks, a route guard component, and optional multi-tenant support.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
interface AuthHelperConfig {
|
|
9
|
+
/** Auth provider type (e.g. "jwt", "oauth") */
|
|
10
|
+
provider: string;
|
|
11
|
+
/** localStorage key for token (default: "plumbus_auth_token") */
|
|
12
|
+
tokenKey?: string;
|
|
13
|
+
/** Login endpoint (default: "/api/auth/login") */
|
|
14
|
+
loginEndpoint?: string;
|
|
15
|
+
/** Logout endpoint (default: "/api/auth/logout") */
|
|
16
|
+
logoutEndpoint?: string;
|
|
17
|
+
/** Session refresh endpoint (default: "/api/auth/refresh") */
|
|
18
|
+
refreshEndpoint?: string;
|
|
19
|
+
/** Enable tenant context provider (default: false) */
|
|
20
|
+
multiTenant?: boolean;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Individual Generators
|
|
25
|
+
|
|
26
|
+
### `generateAuthTypes()`
|
|
27
|
+
|
|
28
|
+
Produces core TypeScript interfaces used across auth code:
|
|
29
|
+
|
|
30
|
+
| Type | Fields |
|
|
31
|
+
|------|--------|
|
|
32
|
+
| `AuthUser` | `userId`, `roles`, `scopes`, `tenantId?`, `provider`, `sessionId?` |
|
|
33
|
+
| `AuthState` | `user`, `isAuthenticated`, `isLoading`, `error` |
|
|
34
|
+
| `AuthActions` | `login()`, `logout()`, `refreshSession()`, `getToken()` |
|
|
35
|
+
| `LoginCredentials` | `email?`, `password?`, `token?`, `provider?` |
|
|
36
|
+
| `AuthConfig` | `loginEndpoint`, `logoutEndpoint`, `refreshEndpoint`, `tokenKey` |
|
|
37
|
+
|
|
38
|
+
### `generateTokenUtils(config?)`
|
|
39
|
+
|
|
40
|
+
Token storage utilities for browser environments:
|
|
41
|
+
|
|
42
|
+
| Function | Purpose |
|
|
43
|
+
|----------|---------|
|
|
44
|
+
| `getStoredToken()` | Read token from `localStorage` (returns `null` on server) |
|
|
45
|
+
| `setStoredToken(token)` | Persist token to `localStorage` |
|
|
46
|
+
| `clearStoredToken()` | Remove token from `localStorage` |
|
|
47
|
+
| `parseJwtPayload(token)` | Decode JWT payload (base64url → JSON) |
|
|
48
|
+
| `isTokenExpired(token)` | Check `exp` claim against `Date.now()` |
|
|
49
|
+
|
|
50
|
+
- Uses the configurable `tokenKey` (default: `"plumbus_auth_token"`).
|
|
51
|
+
- All functions guard against SSR with `typeof window === "undefined"`.
|
|
52
|
+
|
|
53
|
+
### `generateAuthFunctions(config?)`
|
|
54
|
+
|
|
55
|
+
HTTP-based auth operations:
|
|
56
|
+
|
|
57
|
+
| Function | HTTP | Endpoint |
|
|
58
|
+
|----------|------|----------|
|
|
59
|
+
| `login(credentials)` | POST | `{loginEndpoint}` |
|
|
60
|
+
| `logout()` | POST | `{logoutEndpoint}` |
|
|
61
|
+
| `refreshSession()` | POST | `{refreshEndpoint}` |
|
|
62
|
+
| `getAuthHeaders()` | — | Returns `{ Authorization: "Bearer ..." }` or `{}` |
|
|
63
|
+
|
|
64
|
+
- `login` stores the returned token and returns `AuthUser`.
|
|
65
|
+
- `logout` calls the endpoint (fire-and-forget) then clears the stored token.
|
|
66
|
+
- `refreshSession` returns `AuthUser | null` — clears token on failure.
|
|
67
|
+
|
|
68
|
+
### `generateUseAuthHook()`
|
|
69
|
+
|
|
70
|
+
Full React hook combining state and actions:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
function useAuth(): AuthState & AuthActions
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- On mount: checks stored token, refreshes session if valid, sets loading state.
|
|
77
|
+
- Returns `{ user, isAuthenticated, isLoading, error, login, logout, refreshSession, getToken }`.
|
|
78
|
+
- `login` and `logout` update state and re-render.
|
|
79
|
+
|
|
80
|
+
### `generateUseCurrentUserHook()`
|
|
81
|
+
|
|
82
|
+
Simplified read-only hook:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
function useCurrentUser(): { user: AuthUser | null; isAuthenticated: boolean; isLoading: boolean }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Delegates to `useAuth()` internally.
|
|
89
|
+
|
|
90
|
+
### `generateRouteGuard()`
|
|
91
|
+
|
|
92
|
+
Role/scope-based route protection component:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
interface RouteGuardProps {
|
|
96
|
+
children: React.ReactNode;
|
|
97
|
+
roles?: string[]; // User must have at least one of these roles
|
|
98
|
+
scopes?: string[]; // User must have at least one of these scopes
|
|
99
|
+
fallback?: React.ReactNode; // Shown while loading or when unauthorized
|
|
100
|
+
redirectTo?: string; // Redirect URL for unauthenticated users
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Behavior:
|
|
105
|
+
1. While loading → render `fallback` (or null).
|
|
106
|
+
2. Not authenticated → redirect if `redirectTo` set, else render `fallback`.
|
|
107
|
+
3. Roles check → `some` match (any role matches → pass).
|
|
108
|
+
4. Scopes check → `some` match (any scope matches → pass).
|
|
109
|
+
5. Authorized → render `children`.
|
|
110
|
+
|
|
111
|
+
### `generateTenantContext()`
|
|
112
|
+
|
|
113
|
+
Multi-tenant context hook (only included when `config.multiTenant` is true):
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
interface TenantContextValue {
|
|
117
|
+
tenantId: string | null;
|
|
118
|
+
setTenantId(id: string): void;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function useTenant(): TenantContextValue
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- Initializes `tenantId` from the authenticated user's `tenantId`.
|
|
125
|
+
- Syncs when user identity changes.
|
|
126
|
+
|
|
127
|
+
## Module Generator
|
|
128
|
+
|
|
129
|
+
### `generateAuthModule(config?)`
|
|
130
|
+
|
|
131
|
+
Combines all auth generators into a single file:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const code = generateAuthModule({
|
|
135
|
+
provider: "jwt",
|
|
136
|
+
tokenKey: "my_app_token",
|
|
137
|
+
loginEndpoint: "/api/v1/auth/login",
|
|
138
|
+
multiTenant: true,
|
|
139
|
+
});
|
|
140
|
+
// Write to: generated/auth.ts
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Output order: imports → types → token utils → auth functions → useAuth → useCurrentUser → RouteGuard → (tenant context if enabled).
|
|
144
|
+
|
|
145
|
+
Imports `React`, `useState`, `useEffect` from React.
|
|
146
|
+
|
|
147
|
+
## Auth Flow
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
Login: credentials → POST /api/auth/login → { token, user } → localStorage
|
|
151
|
+
Refresh: token → POST /api/auth/refresh → { token, user } → localStorage
|
|
152
|
+
Logout: token → POST /api/auth/logout → clear localStorage
|
|
153
|
+
Request: getAuthHeaders() → { Authorization: "Bearer <token>" }
|
|
154
|
+
```
|