create-kuckit-app 0.1.1 → 0.2.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/bin.js +1 -1
- package/dist/{create-project-DTm05G7D.js → create-project-CP-h4Ygi.js} +7 -5
- package/dist/index.js +1 -1
- package/package.json +3 -2
- package/templates/base/.claude/CLAUDE.md +44 -0
- package/templates/base/.claude/agents/daidalos.md +76 -0
- package/templates/base/.claude/agents/episteme.md +79 -0
- package/templates/base/.claude/agents/librarian.md +132 -0
- package/templates/base/.claude/agents/oracle.md +210 -0
- package/templates/base/.claude/commands/create-plan.md +159 -0
- package/templates/base/.claude/commands/file-beads.md +98 -0
- package/templates/base/.claude/commands/review-beads.md +161 -0
- package/templates/base/.claude/settings.json +11 -0
- package/templates/base/.claude/skills/kuckit/SKILL.md +436 -0
- package/templates/base/.claude/skills/kuckit/references/ARCHITECTURE.md +388 -0
- package/templates/base/.claude/skills/kuckit/references/CLI-COMMANDS.md +365 -0
- package/templates/base/.claude/skills/kuckit/references/MODULE-DEVELOPMENT.md +581 -0
- package/templates/base/.claude/skills/kuckit/references/PACKAGES.md +112 -0
- package/templates/base/.claude/skills/kuckit/references/PUBLISHING.md +231 -0
- package/templates/base/.env.example +13 -0
- package/templates/base/.github/workflows/ci.yml +28 -0
- package/templates/base/.husky/pre-commit +1 -0
- package/templates/base/.prettierignore +5 -0
- package/templates/base/.prettierrc +8 -0
- package/templates/base/AGENTS.md +148 -0
- package/templates/base/apps/server/.env.example +18 -0
- package/templates/base/apps/server/AGENTS.md +37 -0
- package/templates/base/apps/server/package.json +13 -2
- package/templates/base/apps/server/src/app.ts +20 -0
- package/templates/base/apps/server/src/auth.ts +10 -0
- package/templates/base/apps/server/src/config/modules.ts +22 -0
- package/templates/base/apps/server/src/container.ts +81 -0
- package/templates/base/apps/server/src/health.ts +27 -0
- package/templates/base/apps/server/src/middleware/container.ts +41 -0
- package/templates/base/apps/server/src/rpc-router-registry.ts +26 -0
- package/templates/base/apps/server/src/rpc.ts +31 -0
- package/templates/base/apps/server/src/server.ts +42 -14
- package/templates/base/apps/web/.env.example +4 -0
- package/templates/base/apps/web/AGENTS.md +53 -0
- package/templates/base/apps/web/index.html +1 -1
- package/templates/base/apps/web/package.json +15 -2
- package/templates/base/apps/web/src/lib/kuckit-router.ts +42 -0
- package/templates/base/apps/web/src/main.tsx +26 -14
- package/templates/base/apps/web/src/providers/KuckitProvider.tsx +147 -0
- package/templates/base/apps/web/src/providers/ServicesProvider.tsx +47 -0
- package/templates/base/apps/web/src/routeTree.gen.ts +91 -0
- package/templates/base/apps/web/src/routes/__root.tsx +31 -0
- package/templates/base/apps/web/src/routes/index.tsx +46 -0
- package/templates/base/apps/web/src/routes/login.tsx +108 -0
- package/templates/base/apps/web/src/services/auth-client.ts +12 -0
- package/templates/base/apps/web/src/services/index.ts +3 -0
- package/templates/base/apps/web/src/services/rpc.ts +29 -0
- package/templates/base/apps/web/src/services/types.ts +14 -0
- package/templates/base/apps/web/vite.config.ts +2 -1
- package/templates/base/docker-compose.yml +23 -0
- package/templates/base/eslint.config.js +18 -0
- package/templates/base/package.json +32 -2
- package/templates/base/packages/api/AGENTS.md +27 -0
- package/templates/base/packages/api/package.json +35 -0
- package/templates/base/packages/api/src/context.ts +48 -0
- package/templates/base/packages/api/src/index.ts +22 -0
- package/templates/base/packages/api/tsconfig.json +8 -0
- package/templates/base/packages/auth/AGENTS.md +45 -0
- package/templates/base/packages/auth/package.json +27 -0
- package/templates/base/packages/auth/src/index.ts +22 -0
- package/templates/base/packages/auth/tsconfig.json +8 -0
- package/templates/base/packages/db/AGENTS.md +59 -0
- package/templates/base/packages/db/drizzle.config.ts +19 -0
- package/templates/base/packages/db/package.json +36 -0
- package/templates/base/packages/db/src/connection.ts +40 -0
- package/templates/base/packages/db/src/index.ts +4 -0
- package/templates/base/packages/db/src/migrations/0000_init.sql +54 -0
- package/templates/base/packages/db/src/migrations/meta/_journal.json +13 -0
- package/templates/base/packages/db/src/schema/auth.ts +51 -0
- package/templates/base/packages/db/tsconfig.json +8 -0
- package/templates/base/packages/items-module/AGENTS.md +112 -0
- package/templates/base/packages/items-module/package.json +32 -0
- package/templates/base/packages/items-module/src/adapters/item.drizzle.ts +66 -0
- package/templates/base/packages/items-module/src/api/items.router.ts +47 -0
- package/templates/base/packages/items-module/src/client-module.ts +39 -0
- package/templates/base/packages/items-module/src/domain/item.entity.ts +36 -0
- package/templates/base/packages/items-module/src/index.ts +15 -0
- package/templates/base/packages/items-module/src/module.ts +53 -0
- package/templates/base/packages/items-module/src/ports/item.repository.ts +13 -0
- package/templates/base/packages/items-module/src/ui/ItemsPage.tsx +162 -0
- package/templates/base/packages/items-module/src/usecases/create-item.ts +25 -0
- package/templates/base/packages/items-module/src/usecases/delete-item.ts +18 -0
- package/templates/base/packages/items-module/src/usecases/get-item.ts +19 -0
- package/templates/base/packages/items-module/src/usecases/list-items.ts +21 -0
- package/templates/base/packages/items-module/tsconfig.json +9 -0
- package/templates/base/turbo.json +13 -1
- package/templates/base/apps/web/src/App.tsx +0 -16
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
# Kuckit Module Development Reference
|
|
2
|
+
|
|
3
|
+
Complete guide for developing Kuckit modules.
|
|
4
|
+
|
|
5
|
+
## Module Structure
|
|
6
|
+
|
|
7
|
+
### Generated Module Layout
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
packages/billing-module/
|
|
11
|
+
├── src/
|
|
12
|
+
│ ├── module.ts # Server module definition
|
|
13
|
+
│ ├── index.ts # Public exports
|
|
14
|
+
│ ├── client/
|
|
15
|
+
│ │ ├── module.ts # Client module definition
|
|
16
|
+
│ │ └── index.ts # Client exports
|
|
17
|
+
│ └── server/
|
|
18
|
+
│ ├── schema/
|
|
19
|
+
│ │ └── index.ts # Drizzle schema
|
|
20
|
+
│ ├── repositories/
|
|
21
|
+
│ │ └── invoice.ts # Repository implementations
|
|
22
|
+
│ └── use-cases/
|
|
23
|
+
│ └── create-invoice.ts
|
|
24
|
+
├── package.json
|
|
25
|
+
└── tsconfig.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### package.json Metadata
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"name": "@acme/billing-module",
|
|
33
|
+
"version": "1.0.0",
|
|
34
|
+
"type": "module",
|
|
35
|
+
"kuckit": {
|
|
36
|
+
"id": "acme.billing",
|
|
37
|
+
"server": "./dist/module.js",
|
|
38
|
+
"client": "./dist/client/module.js",
|
|
39
|
+
"schema": "./src/server/schema/index.ts"
|
|
40
|
+
},
|
|
41
|
+
"exports": {
|
|
42
|
+
".": {
|
|
43
|
+
"import": "./dist/index.js",
|
|
44
|
+
"types": "./dist/index.d.ts"
|
|
45
|
+
},
|
|
46
|
+
"./client": {
|
|
47
|
+
"import": "./dist/client/index.js",
|
|
48
|
+
"types": "./dist/client/index.d.ts"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@kuckit/sdk": ">=0.1.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Server Module Definition
|
|
60
|
+
|
|
61
|
+
### Full Example
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// src/module.ts
|
|
65
|
+
import {
|
|
66
|
+
defineKuckitModule,
|
|
67
|
+
asClass,
|
|
68
|
+
asFunction,
|
|
69
|
+
asValue,
|
|
70
|
+
type RegisterContext,
|
|
71
|
+
type BootstrapContext,
|
|
72
|
+
} from '@kuckit/sdk'
|
|
73
|
+
import { DrizzleInvoiceRepository } from './server/repositories/invoice'
|
|
74
|
+
import { makeCreateInvoiceUseCase } from './server/use-cases/create-invoice'
|
|
75
|
+
import { createBillingRouter } from './server/api/router'
|
|
76
|
+
|
|
77
|
+
// Module configuration interface
|
|
78
|
+
interface BillingModuleConfig {
|
|
79
|
+
currency: string
|
|
80
|
+
taxRate: number
|
|
81
|
+
enableNotifications: boolean
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Default configuration
|
|
85
|
+
const defaultConfig: BillingModuleConfig = {
|
|
86
|
+
currency: 'USD',
|
|
87
|
+
taxRate: 0.1,
|
|
88
|
+
enableNotifications: true,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const kuckitModule = defineKuckitModule<BillingModuleConfig>({
|
|
92
|
+
// Required: Unique module identifier
|
|
93
|
+
id: 'acme.billing',
|
|
94
|
+
|
|
95
|
+
// Optional: Display name for UI
|
|
96
|
+
displayName: 'Billing',
|
|
97
|
+
|
|
98
|
+
// Optional: Module description
|
|
99
|
+
description: 'Invoice and payment processing',
|
|
100
|
+
|
|
101
|
+
// Optional: Semantic version
|
|
102
|
+
version: '1.0.0',
|
|
103
|
+
|
|
104
|
+
// Optional: Module dependencies (other module IDs)
|
|
105
|
+
dependencies: ['kuckit.users'],
|
|
106
|
+
|
|
107
|
+
// Register DI dependencies
|
|
108
|
+
register(ctx: RegisterContext<BillingModuleConfig>) {
|
|
109
|
+
const config = { ...defaultConfig, ...ctx.config }
|
|
110
|
+
|
|
111
|
+
ctx.container.register({
|
|
112
|
+
// Scoped = new instance per request
|
|
113
|
+
invoiceRepository: asClass(DrizzleInvoiceRepository).scoped(),
|
|
114
|
+
|
|
115
|
+
// Singleton = shared instance
|
|
116
|
+
billingConfig: asValue(config),
|
|
117
|
+
|
|
118
|
+
// Factory function
|
|
119
|
+
createInvoice: asFunction(makeCreateInvoiceUseCase).scoped(),
|
|
120
|
+
})
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// Register API routes
|
|
124
|
+
registerApi(ctx) {
|
|
125
|
+
ctx.addApiRegistration({
|
|
126
|
+
type: 'rpc-router',
|
|
127
|
+
name: 'billing',
|
|
128
|
+
router: createBillingRouter(ctx.container),
|
|
129
|
+
})
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// Startup logic (after all modules registered)
|
|
133
|
+
onBootstrap(ctx: BootstrapContext<BillingModuleConfig>) {
|
|
134
|
+
const logger = ctx.container.resolve('logger')
|
|
135
|
+
const config = { ...defaultConfig, ...ctx.config }
|
|
136
|
+
logger.info(`Billing module started with currency: ${config.currency}`)
|
|
137
|
+
|
|
138
|
+
// Subscribe to events
|
|
139
|
+
const eventBus = ctx.container.resolve('eventBus')
|
|
140
|
+
eventBus.subscribe('user.created', async (event) => {
|
|
141
|
+
logger.info(`New user created: ${event.userId}`)
|
|
142
|
+
})
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// Cleanup logic (on shutdown)
|
|
146
|
+
onShutdown(ctx) {
|
|
147
|
+
const logger = ctx.container.resolve('logger')
|
|
148
|
+
logger.info('Billing module shutting down')
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Lifecycle Hooks
|
|
154
|
+
|
|
155
|
+
| Hook | When Called | Purpose |
|
|
156
|
+
| ------------------ | ---------------------------- | ---------------------------------- |
|
|
157
|
+
| `register(ctx)` | Module loading | Register DI dependencies |
|
|
158
|
+
| `registerApi(ctx)` | After register | Register API routes |
|
|
159
|
+
| `onBootstrap(ctx)` | After all modules registered | Startup logic, event subscriptions |
|
|
160
|
+
| `onShutdown(ctx)` | Container disposal | Cleanup resources |
|
|
161
|
+
|
|
162
|
+
### Context Objects
|
|
163
|
+
|
|
164
|
+
**RegisterContext:**
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
interface RegisterContext<TConfig> {
|
|
168
|
+
container: AwilixContainer
|
|
169
|
+
config: TConfig
|
|
170
|
+
env: 'development' | 'production' | 'test'
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**BootstrapContext:**
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
interface BootstrapContext<TConfig> {
|
|
178
|
+
container: AwilixContainer
|
|
179
|
+
config: TConfig
|
|
180
|
+
env: string
|
|
181
|
+
logger: Logger
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Client Module Definition
|
|
188
|
+
|
|
189
|
+
### Full Example
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// src/client/module.ts
|
|
193
|
+
import { defineKuckitClientModule } from '@kuckit/sdk-react'
|
|
194
|
+
import { CreditCard, FileText, Settings } from 'lucide-react'
|
|
195
|
+
|
|
196
|
+
// Lazy load components
|
|
197
|
+
const BillingDashboard = React.lazy(() => import('./pages/Dashboard'))
|
|
198
|
+
const InvoicesPage = React.lazy(() => import('./pages/Invoices'))
|
|
199
|
+
const InvoiceDetail = React.lazy(() => import('./pages/InvoiceDetail'))
|
|
200
|
+
const BillingSettings = React.lazy(() => import('./pages/Settings'))
|
|
201
|
+
|
|
202
|
+
export const kuckitClientModule = defineKuckitClientModule({
|
|
203
|
+
// Must match server module ID
|
|
204
|
+
id: 'acme.billing',
|
|
205
|
+
|
|
206
|
+
// Route registrations
|
|
207
|
+
routes: [
|
|
208
|
+
{
|
|
209
|
+
path: '/billing',
|
|
210
|
+
component: BillingDashboard,
|
|
211
|
+
meta: { title: 'Billing Dashboard' },
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
path: '/billing/invoices',
|
|
215
|
+
component: InvoicesPage,
|
|
216
|
+
meta: { title: 'Invoices' },
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
path: '/billing/invoices/$invoiceId',
|
|
220
|
+
component: InvoiceDetail,
|
|
221
|
+
meta: { title: 'Invoice Details' },
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
path: '/billing/settings',
|
|
225
|
+
component: BillingSettings,
|
|
226
|
+
meta: { title: 'Billing Settings' },
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
|
|
230
|
+
// Navigation items
|
|
231
|
+
navItems: [
|
|
232
|
+
{
|
|
233
|
+
label: 'Billing',
|
|
234
|
+
href: '/billing',
|
|
235
|
+
icon: CreditCard,
|
|
236
|
+
children: [
|
|
237
|
+
{ label: 'Dashboard', href: '/billing' },
|
|
238
|
+
{ label: 'Invoices', href: '/billing/invoices', icon: FileText },
|
|
239
|
+
{ label: 'Settings', href: '/billing/settings', icon: Settings },
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
|
|
244
|
+
// UI slots (for shell integration)
|
|
245
|
+
slots: {
|
|
246
|
+
'header.actions': HeaderActions,
|
|
247
|
+
'sidebar.footer': BillingStatus,
|
|
248
|
+
},
|
|
249
|
+
})
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Route Definition
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
interface RouteDefinition {
|
|
256
|
+
path: string // TanStack Router path
|
|
257
|
+
component: React.ComponentType // Page component
|
|
258
|
+
meta?: {
|
|
259
|
+
title?: string // Page title
|
|
260
|
+
requiresAuth?: boolean // Auth required
|
|
261
|
+
permissions?: string[] // Required permissions
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Navigation Item Definition
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
interface NavItem {
|
|
270
|
+
label: string // Display text
|
|
271
|
+
href: string // Route path
|
|
272
|
+
icon?: React.ComponentType // Icon component
|
|
273
|
+
children?: NavItem[] // Nested items
|
|
274
|
+
badge?: string | number // Badge content
|
|
275
|
+
hidden?: boolean | (() => boolean)
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## DI Registration Patterns
|
|
282
|
+
|
|
283
|
+
### Awilix Helpers
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { asClass, asFunction, asValue } from '@kuckit/sdk'
|
|
287
|
+
|
|
288
|
+
// Class registration (scoped = new per request)
|
|
289
|
+
invoiceRepository: asClass(DrizzleInvoiceRepository).scoped()
|
|
290
|
+
|
|
291
|
+
// Class registration (singleton = shared)
|
|
292
|
+
emailService: asClass(EmailService).singleton()
|
|
293
|
+
|
|
294
|
+
// Factory function
|
|
295
|
+
createInvoice: asFunction(({ invoiceRepository, logger }) => {
|
|
296
|
+
return new CreateInvoiceUseCase(invoiceRepository, logger)
|
|
297
|
+
}).scoped()
|
|
298
|
+
|
|
299
|
+
// Plain value
|
|
300
|
+
billingConfig: asValue({ currency: 'USD', taxRate: 0.1 })
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Resolving Dependencies
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// In a use case factory
|
|
307
|
+
export function makeCreateInvoiceUseCase({ invoiceRepository, logger, eventBus }) {
|
|
308
|
+
return async function createInvoice(input: CreateInvoiceInput) {
|
|
309
|
+
const invoice = await invoiceRepository.create(input)
|
|
310
|
+
await eventBus.publish('invoice.created', { invoiceId: invoice.id })
|
|
311
|
+
logger.info(`Invoice created: ${invoice.id}`)
|
|
312
|
+
return invoice
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// In a class constructor
|
|
317
|
+
export class DrizzleInvoiceRepository {
|
|
318
|
+
constructor(private deps: { db: DrizzleInstance; logger: Logger }) {}
|
|
319
|
+
|
|
320
|
+
async findById(id: string) {
|
|
321
|
+
return this.deps.db.query.invoices.findFirst({
|
|
322
|
+
where: eq(invoices.id, id),
|
|
323
|
+
})
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## API Registration
|
|
331
|
+
|
|
332
|
+
### oRPC Router Example
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// src/server/api/router.ts
|
|
336
|
+
import { createORPCRouter, createProcedure } from '@kuckit/api'
|
|
337
|
+
import type { AwilixContainer } from 'awilix'
|
|
338
|
+
|
|
339
|
+
export function createBillingRouter(container: AwilixContainer) {
|
|
340
|
+
return createORPCRouter({
|
|
341
|
+
// List invoices
|
|
342
|
+
list: createProcedure()
|
|
343
|
+
.input(listInvoicesSchema)
|
|
344
|
+
.query(async ({ input, ctx }) => {
|
|
345
|
+
const invoiceRepository = container.resolve('invoiceRepository')
|
|
346
|
+
return invoiceRepository.findAll(input)
|
|
347
|
+
}),
|
|
348
|
+
|
|
349
|
+
// Get single invoice
|
|
350
|
+
get: createProcedure()
|
|
351
|
+
.input(z.object({ id: z.string() }))
|
|
352
|
+
.query(async ({ input }) => {
|
|
353
|
+
const invoiceRepository = container.resolve('invoiceRepository')
|
|
354
|
+
return invoiceRepository.findById(input.id)
|
|
355
|
+
}),
|
|
356
|
+
|
|
357
|
+
// Create invoice
|
|
358
|
+
create: createProcedure()
|
|
359
|
+
.input(createInvoiceSchema)
|
|
360
|
+
.mutation(async ({ input }) => {
|
|
361
|
+
const createInvoice = container.resolve('createInvoice')
|
|
362
|
+
return createInvoice(input)
|
|
363
|
+
}),
|
|
364
|
+
})
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Registration in Module
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
registerApi(ctx) {
|
|
372
|
+
ctx.addApiRegistration({
|
|
373
|
+
type: 'rpc-router',
|
|
374
|
+
name: 'billing', // Accessible at /api/billing/*
|
|
375
|
+
router: createBillingRouter(ctx.container),
|
|
376
|
+
})
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Database Schema
|
|
383
|
+
|
|
384
|
+
### Drizzle Schema Example
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
// src/server/schema/index.ts
|
|
388
|
+
import { pgTable, uuid, text, timestamp, numeric } from 'drizzle-orm/pg-core'
|
|
389
|
+
import { users } from '@kuckit/db'
|
|
390
|
+
|
|
391
|
+
export const invoices = pgTable('invoices', {
|
|
392
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
393
|
+
userId: uuid('user_id')
|
|
394
|
+
.references(() => users.id)
|
|
395
|
+
.notNull(),
|
|
396
|
+
amount: numeric('amount', { precision: 10, scale: 2 }).notNull(),
|
|
397
|
+
currency: text('currency').default('USD').notNull(),
|
|
398
|
+
status: text('status').default('pending').notNull(),
|
|
399
|
+
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
400
|
+
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
export const invoiceItems = pgTable('invoice_items', {
|
|
404
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
405
|
+
invoiceId: uuid('invoice_id')
|
|
406
|
+
.references(() => invoices.id)
|
|
407
|
+
.notNull(),
|
|
408
|
+
description: text('description').notNull(),
|
|
409
|
+
quantity: numeric('quantity', { precision: 10, scale: 2 }).notNull(),
|
|
410
|
+
unitPrice: numeric('unit_price', { precision: 10, scale: 2 }).notNull(),
|
|
411
|
+
})
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Schema Discovery
|
|
415
|
+
|
|
416
|
+
The CLI automatically discovers schemas from:
|
|
417
|
+
|
|
418
|
+
1. `kuckit.schema` field in package.json
|
|
419
|
+
2. `src/server/schema/index.ts` (convention)
|
|
420
|
+
3. `src/schema/index.ts` (convention)
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Event System
|
|
425
|
+
|
|
426
|
+
### Publishing Events
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
onBootstrap(ctx) {
|
|
430
|
+
const eventBus = ctx.container.resolve('eventBus')
|
|
431
|
+
|
|
432
|
+
// Publish an event
|
|
433
|
+
await eventBus.publish('invoice.created', {
|
|
434
|
+
invoiceId: '123',
|
|
435
|
+
userId: '456',
|
|
436
|
+
amount: 100.00,
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Subscribing to Events
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
onBootstrap(ctx) {
|
|
445
|
+
const eventBus = ctx.container.resolve('eventBus')
|
|
446
|
+
|
|
447
|
+
// Subscribe to events from other modules
|
|
448
|
+
eventBus.subscribe('user.created', async (event) => {
|
|
449
|
+
const logger = ctx.container.resolve('logger')
|
|
450
|
+
logger.info(`New user created, initializing billing: ${event.userId}`)
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
eventBus.subscribe('payment.received', async (event) => {
|
|
454
|
+
const invoiceRepository = ctx.container.resolve('invoiceRepository')
|
|
455
|
+
await invoiceRepository.markPaid(event.invoiceId)
|
|
456
|
+
})
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## Testing Modules
|
|
463
|
+
|
|
464
|
+
### Test Container Setup
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
import { createTestContainer } from '@kuckit/sdk/testing'
|
|
468
|
+
import { asValue } from '@kuckit/sdk'
|
|
469
|
+
import { kuckitModule } from '../src/module'
|
|
470
|
+
|
|
471
|
+
describe('BillingModule', () => {
|
|
472
|
+
let container: AwilixContainer
|
|
473
|
+
|
|
474
|
+
beforeEach(async () => {
|
|
475
|
+
container = await createTestContainer({
|
|
476
|
+
modules: [{ module: kuckitModule, config: { currency: 'USD' } }],
|
|
477
|
+
overrides: {
|
|
478
|
+
// Mock external dependencies
|
|
479
|
+
emailService: asValue({ send: vi.fn() }),
|
|
480
|
+
clock: asValue(new FakeClock()),
|
|
481
|
+
},
|
|
482
|
+
})
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
afterEach(async () => {
|
|
486
|
+
await disposeContainer(container)
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
it('creates an invoice', async () => {
|
|
490
|
+
const createInvoice = container.resolve('createInvoice')
|
|
491
|
+
const invoice = await createInvoice({
|
|
492
|
+
userId: 'user-123',
|
|
493
|
+
amount: 100.0,
|
|
494
|
+
})
|
|
495
|
+
expect(invoice.id).toBeDefined()
|
|
496
|
+
})
|
|
497
|
+
})
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Mocking Services
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
// Mock logger
|
|
504
|
+
const mockLogger = {
|
|
505
|
+
info: vi.fn(),
|
|
506
|
+
warn: vi.fn(),
|
|
507
|
+
error: vi.fn(),
|
|
508
|
+
debug: vi.fn(),
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Mock database
|
|
512
|
+
const mockDb = {
|
|
513
|
+
query: {
|
|
514
|
+
invoices: {
|
|
515
|
+
findFirst: vi.fn(),
|
|
516
|
+
findMany: vi.fn(),
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
insert: vi.fn(),
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
container = await createTestContainer({
|
|
523
|
+
overrides: {
|
|
524
|
+
logger: asValue(mockLogger),
|
|
525
|
+
db: asValue(mockDb),
|
|
526
|
+
},
|
|
527
|
+
})
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
## Best Practices
|
|
533
|
+
|
|
534
|
+
### Module ID Naming
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
organization.modulename
|
|
538
|
+
|
|
539
|
+
Examples:
|
|
540
|
+
- kuckit.users
|
|
541
|
+
- acme.billing
|
|
542
|
+
- mycompany.analytics
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Dependency Injection
|
|
546
|
+
|
|
547
|
+
- Use `scoped()` for request-specific services (repositories, use cases)
|
|
548
|
+
- Use `singleton()` for shared services (config, email client)
|
|
549
|
+
- Use `asValue()` for configuration objects
|
|
550
|
+
- Always inject dependencies via constructor/factory params
|
|
551
|
+
|
|
552
|
+
### Error Handling
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
import { ModuleError } from '@kuckit/sdk'
|
|
556
|
+
|
|
557
|
+
export class InvoiceNotFoundError extends ModuleError {
|
|
558
|
+
constructor(invoiceId: string) {
|
|
559
|
+
super(`Invoice not found: ${invoiceId}`, 'INVOICE_NOT_FOUND')
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### Logging
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
onBootstrap(ctx) {
|
|
568
|
+
const logger = ctx.container.resolve('logger')
|
|
569
|
+
|
|
570
|
+
// Use structured logging
|
|
571
|
+
logger.info('Module started', {
|
|
572
|
+
moduleId: 'acme.billing',
|
|
573
|
+
config: ctx.config,
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
// Log levels: debug, info, warn, error
|
|
577
|
+
logger.debug('Detailed debug info')
|
|
578
|
+
logger.warn('Warning message')
|
|
579
|
+
logger.error('Error occurred', { error: err })
|
|
580
|
+
}
|
|
581
|
+
```
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Kuckit Package Reference
|
|
2
|
+
|
|
3
|
+
Quick reference for all packages in the Kuckit monorepo.
|
|
4
|
+
|
|
5
|
+
## Package Locations
|
|
6
|
+
|
|
7
|
+
| Package | Path | Description |
|
|
8
|
+
| ----------------------- | -------------------------------------------------------------------- | ------------------------------------ |
|
|
9
|
+
| @kuckit/sdk | `/Users/themrb/Documents/personal/kuckit/packages/sdk` | Backend DI container & module system |
|
|
10
|
+
| @kuckit/sdk-react | `/Users/themrb/Documents/personal/kuckit/packages/sdk-react` | Frontend module system |
|
|
11
|
+
| @kuckit/cli | `/Users/themrb/Documents/personal/kuckit/packages/kuckit-cli` | CLI tooling |
|
|
12
|
+
| create-kuckit-app | `/Users/themrb/Documents/personal/kuckit/packages/create-kuckit-app` | Project scaffolding |
|
|
13
|
+
| @kuckit/domain | `/Users/themrb/Documents/personal/kuckit/packages/domain` | Business entities |
|
|
14
|
+
| @kuckit/application | `/Users/themrb/Documents/personal/kuckit/packages/application` | Use cases |
|
|
15
|
+
| @kuckit/contracts | `/Users/themrb/Documents/personal/kuckit/packages/contracts` | DTOs & Zod schemas |
|
|
16
|
+
| @kuckit/infrastructure | `/Users/themrb/Documents/personal/kuckit/packages/infrastructure` | Repository implementations |
|
|
17
|
+
| @kuckit/api | `/Users/themrb/Documents/personal/kuckit/packages/api` | oRPC procedures |
|
|
18
|
+
| @kuckit/db | `/Users/themrb/Documents/personal/kuckit/packages/db` | Drizzle ORM schemas |
|
|
19
|
+
| @kuckit/auth | `/Users/themrb/Documents/personal/kuckit/packages/auth` | Better-Auth config |
|
|
20
|
+
| @kuckit/infra | `/Users/themrb/Documents/personal/kuckit/packages/infra` | Pulumi infrastructure |
|
|
21
|
+
| @kuckit/users-module | `/Users/themrb/Documents/personal/kuckit/packages/users-module` | Reference module |
|
|
22
|
+
| @kuckit/landing-module | `/Users/themrb/Documents/personal/kuckit/packages/landing-module` | Landing page module |
|
|
23
|
+
| @kuckit/cli-auth-module | `/Users/themrb/Documents/personal/kuckit/packages/cli-auth-module` | CLI auth module |
|
|
24
|
+
|
|
25
|
+
## Key Files Per Package
|
|
26
|
+
|
|
27
|
+
### @kuckit/sdk
|
|
28
|
+
|
|
29
|
+
| File | Purpose |
|
|
30
|
+
| ------------------------------ | ----------------------------- |
|
|
31
|
+
| `src/index.ts` | Public exports |
|
|
32
|
+
| `src/core/container.ts` | DI container creation |
|
|
33
|
+
| `src/core/core.module.ts` | Core services registration |
|
|
34
|
+
| `src/modules/define-module.ts` | `defineKuckitModule()` helper |
|
|
35
|
+
| `src/modules/loader.ts` | Module loading logic |
|
|
36
|
+
| `src/modules/types.ts` | Module type definitions |
|
|
37
|
+
| `src/config/loader.ts` | Configuration loading |
|
|
38
|
+
| `src/schema/registry.ts` | Schema registry |
|
|
39
|
+
|
|
40
|
+
### @kuckit/cli
|
|
41
|
+
|
|
42
|
+
| File | Purpose |
|
|
43
|
+
| --------------------------------- | ------------------------------ |
|
|
44
|
+
| `src/bin.ts` | CLI entry point (Commander.js) |
|
|
45
|
+
| `src/commands/generate-module.ts` | Module scaffolding |
|
|
46
|
+
| `src/commands/add-module.ts` | Module installation |
|
|
47
|
+
| `src/commands/discover-module.ts` | Module discovery |
|
|
48
|
+
| `src/commands/doctor.ts` | Project validation |
|
|
49
|
+
| `src/commands/auth.ts` | Authentication commands |
|
|
50
|
+
| `src/commands/db.ts` | Database commands |
|
|
51
|
+
| `src/commands/infra/*.ts` | Infrastructure commands |
|
|
52
|
+
| `src/lib/credentials.ts` | Token storage |
|
|
53
|
+
| `src/lib/sdk-loader.ts` | Dynamic SDK loading |
|
|
54
|
+
|
|
55
|
+
### @kuckit/sdk-react
|
|
56
|
+
|
|
57
|
+
| File | Purpose |
|
|
58
|
+
| ------------------------------------- | ------------------------ |
|
|
59
|
+
| `src/modules/define-client-module.ts` | Client module definition |
|
|
60
|
+
| `src/modules/loader.ts` | Client module loading |
|
|
61
|
+
| `src/registries/route-registry.ts` | Route management |
|
|
62
|
+
| `src/registries/nav-registry.ts` | Navigation management |
|
|
63
|
+
| `src/registries/slot-registry.ts` | UI slot management |
|
|
64
|
+
| `src/context/*.ts` | React context providers |
|
|
65
|
+
|
|
66
|
+
## Import Patterns
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// SDK imports
|
|
70
|
+
import {
|
|
71
|
+
createKuckitContainer,
|
|
72
|
+
loadKuckitModules,
|
|
73
|
+
defineKuckitModule,
|
|
74
|
+
asClass,
|
|
75
|
+
asFunction,
|
|
76
|
+
asValue,
|
|
77
|
+
disposeContainer,
|
|
78
|
+
} from '@kuckit/sdk'
|
|
79
|
+
|
|
80
|
+
// React SDK imports
|
|
81
|
+
import {
|
|
82
|
+
defineKuckitClientModule,
|
|
83
|
+
loadKuckitClientModules,
|
|
84
|
+
KuckitNavProvider,
|
|
85
|
+
useKuckitNav,
|
|
86
|
+
} from '@kuckit/sdk-react'
|
|
87
|
+
|
|
88
|
+
// CLI programmatic usage
|
|
89
|
+
import { generateModule, addModule, discoverModules } from '@kuckit/cli'
|
|
90
|
+
|
|
91
|
+
// Domain imports (from within the monorepo)
|
|
92
|
+
import { User, type UserPort } from '@kuckit/domain'
|
|
93
|
+
|
|
94
|
+
// Contracts imports
|
|
95
|
+
import { CreateUserInput, UserDTO, createUserSchema } from '@kuckit/contracts'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Package Dependencies
|
|
99
|
+
|
|
100
|
+
### What Each Package Can Import
|
|
101
|
+
|
|
102
|
+
| Package | Allowed Imports |
|
|
103
|
+
| -------------- | ------------------------------ |
|
|
104
|
+
| domain | (none - pure business logic) |
|
|
105
|
+
| contracts | zod |
|
|
106
|
+
| application | domain |
|
|
107
|
+
| infrastructure | domain, external libs |
|
|
108
|
+
| api | domain, application, contracts |
|
|
109
|
+
| sdk | all core packages, awilix |
|
|
110
|
+
| sdk-react | sdk types |
|
|
111
|
+
| server | ALL packages |
|
|
112
|
+
| web | sdk-react, contracts |
|