@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.
Files changed (49) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/generators/__tests__/auth-generator.test.d.ts +2 -0
  3. package/dist/generators/__tests__/auth-generator.test.d.ts.map +1 -0
  4. package/dist/generators/__tests__/auth-generator.test.js +222 -0
  5. package/dist/generators/__tests__/auth-generator.test.js.map +1 -0
  6. package/dist/generators/__tests__/client-generator.test.d.ts +2 -0
  7. package/dist/generators/__tests__/client-generator.test.d.ts.map +1 -0
  8. package/dist/generators/__tests__/client-generator.test.js +224 -0
  9. package/dist/generators/__tests__/client-generator.test.js.map +1 -0
  10. package/dist/generators/__tests__/form-generator.test.d.ts +2 -0
  11. package/dist/generators/__tests__/form-generator.test.d.ts.map +1 -0
  12. package/dist/generators/__tests__/form-generator.test.js +192 -0
  13. package/dist/generators/__tests__/form-generator.test.js.map +1 -0
  14. package/dist/generators/__tests__/nextjs-template.test.d.ts +2 -0
  15. package/dist/generators/__tests__/nextjs-template.test.d.ts.map +1 -0
  16. package/dist/generators/__tests__/nextjs-template.test.js +214 -0
  17. package/dist/generators/__tests__/nextjs-template.test.js.map +1 -0
  18. package/dist/generators/auth-generator.d.ts +31 -0
  19. package/dist/generators/auth-generator.d.ts.map +1 -0
  20. package/dist/generators/auth-generator.js +292 -0
  21. package/dist/generators/auth-generator.js.map +1 -0
  22. package/dist/generators/client-generator.d.ts +31 -0
  23. package/dist/generators/client-generator.d.ts.map +1 -0
  24. package/dist/generators/client-generator.js +336 -0
  25. package/dist/generators/client-generator.js.map +1 -0
  26. package/dist/generators/form-generator.d.ts +47 -0
  27. package/dist/generators/form-generator.d.ts.map +1 -0
  28. package/dist/generators/form-generator.js +189 -0
  29. package/dist/generators/form-generator.js.map +1 -0
  30. package/dist/generators/index.d.ts +9 -0
  31. package/dist/generators/index.d.ts.map +1 -0
  32. package/dist/generators/index.js +6 -0
  33. package/dist/generators/index.js.map +1 -0
  34. package/dist/generators/nextjs-template.d.ts +44 -0
  35. package/dist/generators/nextjs-template.d.ts.map +1 -0
  36. package/dist/generators/nextjs-template.js +444 -0
  37. package/dist/generators/nextjs-template.js.map +1 -0
  38. package/dist/index.d.ts +9 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +11 -0
  41. package/dist/index.js.map +1 -0
  42. package/instructions/auth-generator.md +154 -0
  43. package/instructions/client-generator.md +149 -0
  44. package/instructions/form-generator.md +157 -0
  45. package/instructions/framework.md +108 -0
  46. package/instructions/nextjs-template.md +160 -0
  47. package/instructions/patterns.md +109 -0
  48. package/instructions/testing.md +211 -0
  49. package/package.json +52 -0
@@ -0,0 +1,149 @@
1
+ # Client Generator
2
+
3
+ Generates typed fetch-based API clients and React hooks from capability contracts and flow definitions.
4
+
5
+ ## Configuration
6
+
7
+ ```ts
8
+ interface ClientGeneratorConfig {
9
+ /** Base URL for API requests (default: "") */
10
+ baseUrl?: string;
11
+ /** Include JSDoc comments in generated code */
12
+ includeJsDoc?: boolean;
13
+ }
14
+ ```
15
+
16
+ ## Individual Generators
17
+
18
+ ### `generateCapabilityTypes(cap)`
19
+
20
+ Produces TypeScript type aliases for a capability's input and output:
21
+
22
+ ```ts
23
+ generateCapabilityTypes({ name: "getUser", kind: "query", domain: "users", ... })
24
+ // → export type GetUserInput = Record<string, unknown>;
25
+ // export type GetUserOutput = Record<string, unknown>;
26
+ ```
27
+
28
+ ### `generateTypedClient(cap, config?)`
29
+
30
+ Produces an async fetch function for a capability. Route path follows `GET /api/{domain}/{kebab-name}` for queries, `POST` for actions/jobs:
31
+
32
+ ```ts
33
+ generateTypedClient({ name: "getUser", kind: "query", domain: "users", ... })
34
+ // → export async function getUser(input: GetUserInput, options?): Promise<GetUserOutput> { ... }
35
+ ```
36
+
37
+ - **GET** capabilities serialize input as query parameters via `URLSearchParams`.
38
+ - **POST** capabilities send input as `JSON.stringify(input)` in the request body.
39
+ - All functions accept `options?: { headers?: Record<string, string>; signal?: AbortSignal }`.
40
+ - Non-OK responses throw an error with `status`, `code`, and `metadata` properties.
41
+
42
+ ### `generateQueryHook(cap, config?)`
43
+
44
+ Produces a React hook that auto-fetches on mount/input change:
45
+
46
+ ```ts
47
+ generateQueryHook({ name: "getUser", kind: "query", domain: "users", ... })
48
+ // → export function useGetUser(input: GetUserInput) {
49
+ // const [data, setData] = useState<GetUserOutput | null>(null);
50
+ // ...
51
+ // return { data, loading, error };
52
+ // }
53
+ ```
54
+
55
+ - Uses `useState` + `useEffect` with cancellation.
56
+ - Re-fetches when `JSON.stringify(input)` changes.
57
+
58
+ ### `generateMutationHook(cap, config?)`
59
+
60
+ Produces a React hook for manual invocation (actions, jobs):
61
+
62
+ ```ts
63
+ generateMutationHook({ name: "createUser", kind: "action", domain: "users", ... })
64
+ // → export function useCreateUser() {
65
+ // ...
66
+ // return { mutate, data, loading, error, reset };
67
+ // }
68
+ ```
69
+
70
+ - `mutate(input)` triggers the request and returns the result.
71
+ - `reset()` clears data and error state.
72
+
73
+ ### `generateReactHook(cap, config?)`
74
+
75
+ Dispatches to `generateQueryHook` or `generateMutationHook` based on `cap.kind`:
76
+ - `kind === "query"` → query hook
77
+ - Anything else → mutation hook
78
+
79
+ ### `generateFlowTrigger(flow, config?)`
80
+
81
+ Produces a function to start a flow execution:
82
+
83
+ ```ts
84
+ interface FlowTriggerInput {
85
+ name: string;
86
+ domain?: string;
87
+ description?: string;
88
+ }
89
+
90
+ generateFlowTrigger({ name: "orderFulfillment", domain: "orders" })
91
+ // → export async function startOrderFulfillment(input, options?)
92
+ // : Promise<{ executionId: string; status: string }> { ... }
93
+ ```
94
+
95
+ - POSTs to `/api/{domain}/{kebab-name}/start`.
96
+
97
+ ### `generateErrorTypes()`
98
+
99
+ Produces `PlumbusApiError` interface and `isPlumbusApiError()` type guard — always included in module output.
100
+
101
+ ## Module Generators
102
+
103
+ ### `generateClientModule(capabilities, flows, config?)`
104
+
105
+ Combines all generators into a single client file:
106
+
107
+ ```ts
108
+ const code = generateClientModule(
109
+ [getUser, createUser, deleteUser],
110
+ [{ name: "orderFulfillment", domain: "orders" }],
111
+ { baseUrl: "/api", includeJsDoc: true },
112
+ );
113
+ // Output: .plumbus/generated/ui/client.ts (written by CLI)
114
+ ```
115
+
116
+ Output order: error types → capability types → flow types → client functions → flow triggers.
117
+
118
+ ### `generateHooksModule(capabilities, config?)`
119
+
120
+ Produces a React hooks file that imports from the client module:
121
+
122
+ ```ts
123
+ const code = generateHooksModule([getUser, createUser]);
124
+ // Output: .plumbus/generated/ui/hooks.ts (written by CLI)
125
+ ```
126
+
127
+ - Auto-imports `useState`, `useEffect` from React.
128
+ - Auto-imports types and functions from `./client`.
129
+
130
+ ## URL Routing Convention
131
+
132
+ | Capability Kind | HTTP Method | URL Pattern |
133
+ |----------------|-------------|-------------|
134
+ | query | GET | `/api/{domain}/{kebab-name}` |
135
+ | action | POST | `/api/{domain}/{kebab-name}` |
136
+ | job | POST | `/api/{domain}/{kebab-name}` |
137
+ | eventHandler | POST | `/api/{domain}/{kebab-name}` |
138
+
139
+ Flow triggers always POST to `/api/{domain}/{kebab-name}/start`.
140
+
141
+ ## Internal Helpers
142
+
143
+ | Helper | Purpose |
144
+ |--------|---------|
145
+ | `toCamelCase(str)` | Capability function names — `getUser` |
146
+ | `toPascalCase(str)` | Type names — `GetUser` |
147
+ | `toKebabCase(str)` | URL path segments — `get-user` |
148
+ | `httpMethod(kind)` | `"query" → "GET"`, everything else → `"POST"` |
149
+ | `capabilityPath(domain, name)` | `/api/{domain}/{kebab-name}` |
@@ -0,0 +1,157 @@
1
+ # Form Generator
2
+
3
+ Extracts form field metadata from Zod schemas attached to capability contracts. Produces structured hints that UI components can consume for rendering form fields with labels, input types, validation, and options.
4
+
5
+ ## Types
6
+
7
+ ```ts
8
+ type FormFieldType = "text" | "number" | "boolean" | "select" | "textarea" | "date" | "hidden";
9
+
10
+ interface FormFieldHint {
11
+ name: string; // Field key from the schema
12
+ label: string; // Human-readable label (auto-derived)
13
+ fieldType: FormFieldType; // Suggested HTML input type
14
+ required: boolean; // true unless ZodOptional or ZodDefault
15
+ defaultValue?: unknown; // From .default() if set
16
+ zodType: string; // Underlying Zod type name (e.g. "ZodString")
17
+ options?: string[]; // For ZodEnum — the allowed values
18
+ validation: FormValidation;
19
+ description?: string; // From .describe() if set
20
+ }
21
+
22
+ interface FormValidation {
23
+ min?: number; // ZodNumber .min()
24
+ max?: number; // ZodNumber .max()
25
+ minLength?: number; // ZodString .min()
26
+ maxLength?: number; // ZodString .max()
27
+ pattern?: string; // ZodString .regex() source, or "email"/"url"
28
+ nullable?: boolean; // ZodNullable wrapper
29
+ }
30
+
31
+ interface FormHints {
32
+ capabilityName: string;
33
+ kind: string;
34
+ fields: FormFieldHint[];
35
+ }
36
+ ```
37
+
38
+ ## How It Works
39
+
40
+ The form generator introspects Zod schemas at runtime via their `_def` property. It does **not** use `z.infer` or code generation — it reads the schema tree directly.
41
+
42
+ ### Schema Unwrapping
43
+
44
+ Wrapper types are unwrapped to find the underlying type:
45
+ - `ZodOptional` → unwrap `innerType`, mark `required: false`
46
+ - `ZodNullable` → unwrap `innerType`, set `validation.nullable: true`
47
+ - `ZodDefault` → unwrap `innerType`, mark `required: false`, extract default value
48
+
49
+ ### Zod Type → Form Field Type Mapping
50
+
51
+ | Zod Type | Form Field Type |
52
+ |----------|----------------|
53
+ | ZodString | `"text"` |
54
+ | ZodNumber, ZodBigInt | `"number"` |
55
+ | ZodBoolean | `"boolean"` |
56
+ | ZodDate | `"date"` |
57
+ | ZodEnum, ZodNativeEnum | `"select"` |
58
+ | ZodObject, ZodArray, ZodRecord | `"textarea"` |
59
+ | Other | `"text"` |
60
+
61
+ ### Validation Extraction
62
+
63
+ Checks are read from `_def.checks` array:
64
+
65
+ | Check Kind | Extracted As |
66
+ |------------|-------------|
67
+ | `min` (ZodString) | `minLength` |
68
+ | `max` (ZodString) | `maxLength` |
69
+ | `min` (ZodNumber) | `min` |
70
+ | `max` (ZodNumber) | `max` |
71
+ | `regex` | `pattern` (regex source) |
72
+ | `email` | `pattern: "email"` |
73
+ | `url` | `pattern: "url"` |
74
+
75
+ ### Label Generation
76
+
77
+ Field names are converted to title case: `firstName` → `"First Name"`, `user_email` → `"User Email"`.
78
+
79
+ ## Functions
80
+
81
+ ### `extractFieldHint(name, schema)`
82
+
83
+ Extract metadata for a single Zod field:
84
+
85
+ ```ts
86
+ import { z } from "zod";
87
+ import { extractFieldHint } from "@plumbus/ui";
88
+
89
+ const hint = extractFieldHint("email", z.string().email().describe("User's email"));
90
+ // → { name: "email", label: "Email", fieldType: "text", required: true,
91
+ // zodType: "ZodString", validation: { pattern: "email" }, description: "User's email" }
92
+ ```
93
+
94
+ ### `extractFormHints(cap)`
95
+
96
+ Extract hints for all fields in a capability's `input` schema (must be `ZodObject`):
97
+
98
+ ```ts
99
+ const hints = extractFormHints(createUserCapability);
100
+ // → { capabilityName: "createUser", kind: "action", fields: [...] }
101
+ ```
102
+
103
+ Iterates over the `ZodObject` shape via `_def.shape()`.
104
+
105
+ ### `generateFormHintsCode(cap)`
106
+
107
+ Produce a TypeScript constant with the extracted hints:
108
+
109
+ ```ts
110
+ generateFormHintsCode(cap)
111
+ // → export const CreateUserFormHints = { ... } as const;
112
+ ```
113
+
114
+ ### `generateFormHintsModule(capabilities)`
115
+
116
+ Produce a module exporting form hints for all capabilities:
117
+
118
+ ```ts
119
+ const code = generateFormHintsModule([createUser, updateUser]);
120
+ // Write to: generated/form-hints.ts
121
+ ```
122
+
123
+ ## Usage Pattern
124
+
125
+ ```ts
126
+ // 1. Extract hints at build time
127
+ const hints = extractFormHints(createUserCapability);
128
+
129
+ // 2. Use hints in a React component
130
+ function DynamicForm({ hints }: { hints: FormHints }) {
131
+ return (
132
+ <form>
133
+ {hints.fields.map((field) => (
134
+ <div key={field.name}>
135
+ <label>{field.label}</label>
136
+ {field.fieldType === "select" ? (
137
+ <select name={field.name} required={field.required}>
138
+ {field.options?.map((opt) => <option key={opt}>{opt}</option>)}
139
+ </select>
140
+ ) : (
141
+ <input
142
+ name={field.name}
143
+ type={field.fieldType}
144
+ required={field.required}
145
+ minLength={field.validation.minLength}
146
+ maxLength={field.validation.maxLength}
147
+ min={field.validation.min}
148
+ max={field.validation.max}
149
+ defaultValue={field.defaultValue as string}
150
+ />
151
+ )}
152
+ </div>
153
+ ))}
154
+ </form>
155
+ );
156
+ }
157
+ ```
@@ -0,0 +1,108 @@
1
+ # @plumbus/ui — UI Code Generation Framework
2
+
3
+ `@plumbus/ui` is the frontend code generation layer for the Plumbus framework. It reads capability contracts, flow definitions, and auth configuration from `@plumbus/core` and produces ready-to-use TypeScript/React source files — typed API clients, React hooks, auth modules, form metadata, and full Next.js project scaffolds.
4
+
5
+ ## Purpose
6
+
7
+ All generated output is **source code as strings** — the package does not ship React components or a runtime. Instead, AI agents and CLI tooling call generator functions to produce `.ts`/`.tsx` files that applications import directly.
8
+
9
+ ## Package Layout
10
+
11
+ ```
12
+ packages/ui/
13
+ src/
14
+ index.ts # Barrel re-exports
15
+ generators/
16
+ index.ts # Generator barrel
17
+ client-generator.ts # Typed fetch clients + React hooks
18
+ auth-generator.ts # Auth types, token utils, hooks, route guard
19
+ form-generator.ts # Zod schema → form field metadata
20
+ nextjs-template.ts # Full Next.js project scaffold
21
+ __tests__/
22
+ client-generator.test.ts
23
+ auth-generator.test.ts
24
+ form-generator.test.ts
25
+ nextjs-template.test.ts
26
+ instructions/ # AI agent instructions (this directory)
27
+ package.json
28
+ tsconfig.json
29
+ vitest.config.browser.ts
30
+ ```
31
+
32
+ ## Core Concepts
33
+
34
+ | Concept | Description |
35
+ |---------|-------------|
36
+ | **Generator** | A function that takes a contract/config and returns a string of TypeScript/TSX source code |
37
+ | **CapabilityContract** | Imported from `@plumbus/core` — defines name, domain, kind, input/output schemas, access |
38
+ | **Module generator** | Combines multiple generators into a single file with proper imports |
39
+ | **GeneratedFile** | `{ path: string; content: string }` — represents a file to write to disk |
40
+
41
+ ## Generator Categories
42
+
43
+ | Generator | Input | Output |
44
+ |-----------|-------|--------|
45
+ | **Client** | `CapabilityContract[]`, `FlowTriggerInput[]` | Typed fetch functions, React query/mutation hooks |
46
+ | **Auth** | `AuthHelperConfig` | Login/logout, token utils, useAuth hook, RouteGuard, tenant context |
47
+ | **Form** | `CapabilityContract` (with Zod input schema) | Field metadata (type, label, validation, options) |
48
+ | **Next.js** | `NextjsTemplateConfig`, capabilities | Full project: package.json, layout, pages, middleware, API routes |
49
+
50
+ ## Relationship to @plumbus/core
51
+
52
+ `@plumbus/ui` depends on `@plumbus/core` for:
53
+ - `CapabilityContract` type — the shape of capability definitions
54
+ - Zod schemas — introspected at runtime for form hint extraction
55
+ - No runtime coupling — generators produce standalone code
56
+
57
+ ## Key Imports
58
+
59
+ ```ts
60
+ import {
61
+ // Client generators
62
+ generateClientModule,
63
+ generateHooksModule,
64
+ generateTypedClient,
65
+ generateReactHook,
66
+ generateCapabilityTypes,
67
+ generateFlowTrigger,
68
+ generateErrorTypes,
69
+
70
+ // Auth generators
71
+ generateAuthModule,
72
+ generateAuthTypes,
73
+ generateTokenUtils,
74
+ generateAuthFunctions,
75
+ generateUseAuthHook,
76
+ generateUseCurrentUserHook,
77
+ generateRouteGuard,
78
+ generateTenantContext,
79
+
80
+ // Form generators
81
+ extractFormHints,
82
+ extractFieldHint,
83
+ generateFormHintsCode,
84
+ generateFormHintsModule,
85
+
86
+ // Next.js generators
87
+ generateNextjsTemplate,
88
+ generateLayout,
89
+ generateHomePage,
90
+ generateCapabilityPage,
91
+ generateMiddleware,
92
+ generateApiRouteHelper,
93
+ generateAuthProvider,
94
+ generateErrorBoundary,
95
+ generateLoadingComponent,
96
+ generatePackageJson,
97
+ generateTsConfig,
98
+ generateEnvLocal,
99
+ generatePlaceholderFiles,
100
+ } from "@plumbus/ui";
101
+ ```
102
+
103
+ ## How Agents Should Use This Package
104
+
105
+ 1. **Run `plumbus ui generate`** — auto-detects the frontend directory (e.g., `frontend/`) and writes typed client, hooks, auth, and form-hints modules to `{frontend}/generated/`. Also writes to `.plumbus/generated/ui/` as a contract artifact cache.
106
+ 2. **Run `plumbus ui nextjs <dir>`** — scaffolds a Next.js project that includes the generated modules in `{dir}/generated/`. The frontend imports them via `@/generated/hooks`, `@/generated/client`, etc.
107
+ 3. **Never copy generated files manually.** Re-running `plumbus ui generate` updates `{frontend}/generated/` in place. The CLI auto-detects the frontend by looking for `tsconfig.json` in `frontend/`, `web/`, `client/`, or `app/`.
108
+ 4. To customize the output location: `plumbus ui generate --out-dir path/to/generated`.
@@ -0,0 +1,160 @@
1
+ # Next.js Template Generator
2
+
3
+ Scaffolds a complete Next.js 14 project wired to a Plumbus backend — layout, pages, auth, middleware, API proxy, error boundary, and environment config.
4
+
5
+ ## Configuration
6
+
7
+ ```ts
8
+ interface NextjsTemplateConfig {
9
+ appName: string; // Application display name
10
+ auth?: boolean; // Include auth wiring (default: true)
11
+ apiBaseUrl?: string; // Backend URL (default: "http://localhost:3000")
12
+ }
13
+
14
+ interface GeneratedFile {
15
+ path: string; // Relative file path within the project
16
+ content: string; // File contents
17
+ }
18
+ ```
19
+
20
+ ## Full Scaffold
21
+
22
+ ### `generateNextjsTemplate(config, capabilities?)`
23
+
24
+ Returns `GeneratedFile[]` — a complete Next.js project. Write each file to disk:
25
+
26
+ ```ts
27
+ const files = generateNextjsTemplate(
28
+ { appName: "My App", auth: true, apiBaseUrl: "http://localhost:3000" },
29
+ [getUser, createUser],
30
+ );
31
+
32
+ for (const file of files) {
33
+ writeFileSync(join(outputDir, file.path), file.content);
34
+ }
35
+ ```
36
+
37
+ ### Generated Project Structure
38
+
39
+ ```
40
+ package.json # Next.js 14, React 18, TypeScript 5, Tailwind CSS 4
41
+ tsconfig.json # Strict, bundler module resolution
42
+ postcss.config.mjs # PostCSS config with @tailwindcss/postcss
43
+ .env.local # API base URL, auth flag, secrets
44
+ middleware.ts # Auth token check, protected paths
45
+ app/
46
+ globals.css # Tailwind import + base resets
47
+ layout.tsx # Root layout (with AuthProvider if auth)
48
+ page.tsx # Home page
49
+ loading.tsx # Global loading skeleton
50
+ error.tsx # Global error boundary
51
+ {capability-slug}/page.tsx # Per-capability pages (query or action)
52
+ api/plumbus/[...path]/route.ts # API proxy to Plumbus backend
53
+ components/
54
+ AuthProvider.tsx # Context-based auth provider (if auth)
55
+ generated/
56
+ .gitkeep # Placeholder for generated client files
57
+ hooks/
58
+ .gitkeep # Placeholder for custom hooks
59
+ ```
60
+
61
+ ## Individual File Generators
62
+
63
+ ### `generatePackageJson(config)`
64
+
65
+ ```json
66
+ {
67
+ "name": "{kebab-case-app-name}",
68
+ "dependencies": { "next": "^14", "react": "^18", "react-dom": "^18", "tailwindcss": "^4", "@tailwindcss/postcss": "^4" },
69
+ "devDependencies": { "typescript": "^5", "@types/react": "^18", "@types/react-dom": "^18" }
70
+ }
71
+ ```
72
+
73
+ ### `generateTsConfig()`
74
+
75
+ Strict TypeScript config for Next.js: `target: "ES2017"`, `module: "esnext"`, `moduleResolution: "bundler"`, `jsx: "preserve"`, path alias `@/*`.
76
+
77
+ ### `generateGlobalsCss()`
78
+
79
+ Minimal `app/globals.css` with Tailwind CSS import and base resets (box-sizing, margin, antialiased text). Apps customize by extending this file with their own theme tokens.
80
+
81
+ ### `generatePostcssConfig()`
82
+
83
+ PostCSS config at `postcss.config.mjs` with `@tailwindcss/postcss` plugin. Required for Tailwind CSS 4 processing.
84
+
85
+ ### `generateLayout(config)`
86
+
87
+ Root layout with `<html>` + `<body>`. Imports `./globals.css` for Tailwind styles. If `auth !== false`, wraps children in `<AuthProvider>`.
88
+
89
+ ### `generateHomePage(config)`
90
+
91
+ Simple welcome page with app name and description text.
92
+
93
+ ### `generateCapabilityPage(cap)`
94
+
95
+ Route: `app/{kebab-name}/page.tsx`
96
+
97
+ Generated page depends on capability kind:
98
+
99
+ | Kind | UI Pattern |
100
+ |------|-----------|
101
+ | `query` | Auto-fetches with `use{Name}({})`, shows loading/error/data states |
102
+ | `action`/`job` | Form with `handleSubmit`, uses `use{Name}()` mutation hook, shows submit/loading/error/result |
103
+
104
+ All pages use `"use client"` directive and import hooks from `@/generated/hooks`.
105
+
106
+ ### `generateAuthProvider()`
107
+
108
+ Context-based provider at `components/AuthProvider.tsx`:
109
+ - Creates `AuthContext` with `AuthState`.
110
+ - On mount: checks stored token, refreshes session.
111
+ - Exports `useAuthContext()` hook.
112
+ - Imports from `@/generated/auth`.
113
+
114
+ ### `generateMiddleware(config)`
115
+
116
+ Next.js middleware at `middleware.ts`:
117
+ - When auth enabled: checks `auth_token` cookie for protected paths (`/dashboard`, `/settings`, `/api/protected`).
118
+ - Redirects to `/login` if no token.
119
+ - Matcher excludes `_next/static`, `_next/image`, and `favicon.ico`.
120
+
121
+ ### `generateApiRouteHelper(config)`
122
+
123
+ Catch-all API proxy at `app/api/plumbus/[...path]/route.ts`:
124
+ - Forwards requests to `{apiBaseUrl}/{path}`.
125
+ - Preserves auth headers, query parameters, and request method.
126
+ - Supports GET, POST, PUT, PATCH, DELETE.
127
+
128
+ ### `generateEnvLocal(config)`
129
+
130
+ Environment variables template:
131
+ ```env
132
+ NEXT_PUBLIC_API_BASE_URL=http://localhost:3000
133
+ NEXT_PUBLIC_AUTH_ENABLED=true
134
+ AUTH_SECRET=change-me-in-production
135
+ ```
136
+
137
+ ### `generateErrorBoundary()`
138
+
139
+ Client error component at `app/error.tsx` — logs error, shows message, provides retry button.
140
+
141
+ ### `generateLoadingComponent()`
142
+
143
+ Loading skeleton at `app/loading.tsx` with `role="status"` and `aria-label="Loading"`.
144
+
145
+ ### `generatePlaceholderFiles()`
146
+
147
+ Returns `generated/.gitkeep` and `hooks/.gitkeep`.
148
+
149
+ ## Auth Integration
150
+
151
+ When `auth: true` (default):
152
+ 1. `AuthProvider` wraps the app layout.
153
+ 2. Middleware protects configured paths.
154
+ 3. Generated pages can use `useAuthContext()`.
155
+ 4. API proxy forwards `Authorization` headers.
156
+
157
+ When `auth: false`:
158
+ - No `AuthProvider` import in layout.
159
+ - Middleware runs but skips auth checks.
160
+ - Pages still work for public capabilities.
@@ -0,0 +1,109 @@
1
+ # Patterns & Conventions
2
+
3
+ ## Naming Conventions
4
+
5
+ | Element | Convention | Example |
6
+ |---------|-----------|---------|
7
+ | Generator function | `generate{Thing}` | `generateClientModule`, `generateAuthModule` |
8
+ | Extractor function | `extract{Thing}` | `extractFormHints`, `extractFieldHint` |
9
+ | Generated React hook | `use{PascalName}` | `useGetUser`, `useCreateOrder` |
10
+ | Generated client function | `{camelName}` | `getUser`, `createOrder` |
11
+ | Generated type | `{PascalName}Input` / `{PascalName}Output` | `GetUserInput`, `GetUserOutput` |
12
+ | Generated flow trigger | `start{PascalName}` | `startOrderFulfillment` |
13
+ | Generated file | `kebab-case.ts` / `.tsx` | `client.ts`, `auth.ts`, `form-hints.ts` |
14
+ | Test file | `{generator-name}.test.ts` | `client-generator.test.ts` |
15
+
16
+ ## File Structure
17
+
18
+ ```
19
+ packages/ui/
20
+ src/
21
+ index.ts # Barrel re-exports (types + functions)
22
+ generators/
23
+ index.ts # Generator barrel
24
+ client-generator.ts # Typed clients + React hooks
25
+ auth-generator.ts # Auth types, functions, hooks
26
+ form-generator.ts # Zod introspection + form hints
27
+ nextjs-template.ts # Full Next.js scaffolding
28
+ __tests__/
29
+ client-generator.test.ts
30
+ auth-generator.test.ts
31
+ form-generator.test.ts
32
+ nextjs-template.test.ts
33
+ instructions/ # AI agent instructions
34
+ package.json
35
+ tsconfig.json
36
+ vitest.config.browser.ts
37
+ ```
38
+
39
+ ## Do's
40
+
41
+ - **Do** use the `CapabilityContract` type from `@plumbus/core` as the input for all generators that work with capabilities.
42
+ - **Do** return plain strings from generators — they produce source code, not runtime objects.
43
+ - **Do** use `GeneratedFile[]` for multi-file generators (Next.js template) so callers can write files to disk.
44
+ - **Do** add the `// Auto-generated by @plumbus/ui — do not edit` comment at the top of module generators.
45
+ - **Do** handle SSR safety in generated auth code — guard `localStorage` with `typeof window === "undefined"`.
46
+ - **Do** import React hooks (`useState`, `useEffect`) in generated hook modules.
47
+ - **Do** use the same naming helpers (`toCamelCase`, `toPascalCase`, `toKebabCase`) consistently across generators.
48
+ - **Do** keep generated code self-contained — each module generator produces a file that works with only its stated imports.
49
+ - **Do** support optional `ClientGeneratorConfig` / `AuthHelperConfig` / `NextjsTemplateConfig` for customization.
50
+ - **Do** unwrap Zod wrappers (`ZodOptional`, `ZodNullable`, `ZodDefault`) before checking the underlying type.
51
+
52
+ ## Don'ts
53
+
54
+ - **Don't** generate code that depends on `@plumbus/ui` at runtime — generated code imports from React, a client module, or `@/generated/*` paths only.
55
+ - **Don't** use `z.infer` in generators — introspect schemas via `_def` for form hints.
56
+ - **Don't** mutate `CapabilityContract` objects — generators are pure functions.
57
+ - **Don't** hardcode API paths — always derive from `domain` + `name` using `capabilityPath()`.
58
+ - **Don't** embed secrets in generated code — use environment variables (`process.env`).
59
+ - **Don't** add framework-specific runtime dependencies to generated Next.js projects (no `@plumbus/core` import in generated frontend code).
60
+ - **Don't** edit files in `generated/` directories manually — they are regenerated.
61
+
62
+ ## Common Workflows
63
+
64
+ ### Generate a Full Frontend from Capabilities
65
+
66
+ Use the CLI — never copy files manually:
67
+
68
+ ```bash
69
+ # Scaffold a Next.js app (includes generated modules in frontend/generated/)
70
+ plumbus ui nextjs frontend
71
+
72
+ # Regenerate UI modules after changing capabilities
73
+ # Auto-detects frontend/ and writes to frontend/generated/
74
+ plumbus ui generate
75
+ ```
76
+
77
+ The `plumbus ui generate` command auto-detects the frontend directory by looking for `tsconfig.json` in `frontend/`, `web/`, `client/`, or `app/`. Override with `--out-dir`.
78
+
79
+ ### Scaffold a New Next.js Application
80
+
81
+ ```ts
82
+ import { generateNextjsTemplate } from "@plumbus/ui";
83
+
84
+ const files = generateNextjsTemplate({ appName: "My App", auth: true }, capabilities);
85
+ for (const file of files) {
86
+ mkdirSync(dirname(join(outDir, file.path)), { recursive: true });
87
+ writeFileSync(join(outDir, file.path), file.content);
88
+ }
89
+ ```
90
+
91
+ ### Add a New Generator
92
+
93
+ 1. Create `src/generators/{name}-generator.ts`.
94
+ 2. Export individual generator functions + a module-level `generate{Name}Module()`.
95
+ 3. Re-export from `src/generators/index.ts` and `src/index.ts`.
96
+ 4. Add tests at `src/generators/__tests__/{name}-generator.test.ts`.
97
+ 5. Add an instruction file at `instructions/{name}-generator.md`.
98
+
99
+ ## Generated Code Output Conventions
100
+
101
+ | Convention | Rule |
102
+ |-----------|------|
103
+ | Module header | `// Auto-generated by @plumbus/ui — do not edit` |
104
+ | React imports | `import { useState, useEffect } from "react"` |
105
+ | Client imports | `import { functionName } from "./client"` |
106
+ | Auth imports | `import { getStoredToken, ... } from "@/generated/auth"` |
107
+ | Type imports | `import type { ... } from "./client"` |
108
+ | Error handling | Throw enriched `Error` with `status`, `code`, `metadata` |
109
+ | Cancellation | Query hooks track `cancelled` flag for cleanup |