create-kuckit-app 0.3.5 → 0.4.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-CP-h4Ygi.js → create-project-geQBZ0Ru.js} +5 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/base/.claude/skills/kuckit/SKILL.md +22 -2
- package/templates/base/.claude/skills/kuckit/references/MODULE-DEVELOPMENT.md +39 -28
- package/templates/base/.claude/skills/kuckit/references/PACKAGES.md +94 -74
- package/templates/base/AGENTS.md +44 -18
- package/templates/base/apps/server/AGENTS.md +35 -10
- package/templates/base/apps/server/package.json +7 -7
- package/templates/base/apps/server/src/auth.ts +1 -1
- package/templates/base/apps/server/src/container.ts +3 -1
- package/templates/base/apps/server/src/module-rest-routes.ts +47 -0
- package/templates/base/apps/server/src/rest-router-registry.ts +32 -0
- package/templates/base/apps/server/src/rpc.ts +1 -1
- package/templates/base/apps/server/src/server.ts +2 -0
- package/templates/base/apps/web/package.json +9 -9
- package/templates/base/apps/web/src/services/auth-client.ts +1 -1
- package/templates/base/{packages/db/src/migrations → drizzle}/0000_init.sql +31 -36
- package/templates/base/{packages/db/src/migrations → drizzle}/meta/_journal.json +1 -1
- package/templates/base/drizzle.config.ts +38 -0
- package/templates/base/package.json +14 -9
- package/templates/base/packages/items-module/package.json +7 -7
- package/templates/base/packages/items-module/src/api/items.router.ts +1 -1
- package/templates/base/packages/api/AGENTS.md +0 -66
- package/templates/base/packages/api/package.json +0 -35
- package/templates/base/packages/api/src/context.ts +0 -48
- package/templates/base/packages/api/src/index.ts +0 -22
- package/templates/base/packages/api/tsconfig.json +0 -8
- package/templates/base/packages/auth/AGENTS.md +0 -61
- package/templates/base/packages/auth/package.json +0 -27
- package/templates/base/packages/auth/src/index.ts +0 -22
- package/templates/base/packages/auth/tsconfig.json +0 -8
- package/templates/base/packages/db/AGENTS.md +0 -99
- package/templates/base/packages/db/drizzle.config.ts +0 -23
- package/templates/base/packages/db/package.json +0 -36
- package/templates/base/packages/db/src/connection.ts +0 -40
- package/templates/base/packages/db/src/index.ts +0 -4
- package/templates/base/packages/db/src/schema/auth.ts +0 -51
- package/templates/base/packages/db/tsconfig.json +0 -8
package/dist/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as createProject } from "./create-project-
|
|
2
|
+
import { t as createProject } from "./create-project-geQBZ0Ru.js";
|
|
3
3
|
import { program } from "commander";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -73,6 +73,11 @@ Next steps:
|
|
|
73
73
|
bun run db:migrate # Run database migrations
|
|
74
74
|
bun run dev # Start development server
|
|
75
75
|
|
|
76
|
+
Deploy to GCP:
|
|
77
|
+
|
|
78
|
+
bun add -D @kuckit/infra-gcp
|
|
79
|
+
bunx kuckit infra up # One-command deploy
|
|
80
|
+
|
|
76
81
|
Inside that directory, you can run:
|
|
77
82
|
|
|
78
83
|
bun run dev Start the development server
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -193,14 +193,34 @@ export const kuckitClientModule = defineKuckitClientModule({
|
|
|
193
193
|
|
|
194
194
|
### Module package.json Metadata
|
|
195
195
|
|
|
196
|
+
Modules depend on `@kuckit/*` npm packages:
|
|
197
|
+
|
|
196
198
|
```json
|
|
197
199
|
{
|
|
198
200
|
"name": "@acme/billing-module",
|
|
201
|
+
"version": "0.1.0",
|
|
202
|
+
"type": "module",
|
|
203
|
+
"exports": {
|
|
204
|
+
".": "./src/index.ts",
|
|
205
|
+
"./client": "./src/client-module.ts"
|
|
206
|
+
},
|
|
207
|
+
"peerDependencies": {
|
|
208
|
+
"@kuckit/sdk": "^1.0.0",
|
|
209
|
+
"@kuckit/sdk-react": "^1.0.0",
|
|
210
|
+
"typescript": "^5"
|
|
211
|
+
},
|
|
212
|
+
"dependencies": {
|
|
213
|
+
"@kuckit/api": "^1.0.0",
|
|
214
|
+
"@orpc/server": "^1.10.0",
|
|
215
|
+
"@orpc/zod": "^1.10.0",
|
|
216
|
+
"drizzle-orm": "^0.44.0",
|
|
217
|
+
"zod": "^3.23.0",
|
|
218
|
+
"react": "^19.0.0"
|
|
219
|
+
},
|
|
199
220
|
"kuckit": {
|
|
200
221
|
"id": "acme.billing",
|
|
201
222
|
"server": "./dist/module.js",
|
|
202
|
-
"client": "./dist/client
|
|
203
|
-
"schema": "./src/server/schema/index.ts"
|
|
223
|
+
"client": "./dist/client-module.js"
|
|
204
224
|
}
|
|
205
225
|
}
|
|
206
226
|
```
|
|
@@ -6,50 +6,61 @@ Complete guide for developing Kuckit modules.
|
|
|
6
6
|
|
|
7
7
|
### Generated Module Layout
|
|
8
8
|
|
|
9
|
+
Modules follow Clean Architecture internally with all layers self-contained:
|
|
10
|
+
|
|
9
11
|
```
|
|
10
12
|
packages/billing-module/
|
|
11
13
|
├── src/
|
|
12
|
-
│ ├──
|
|
13
|
-
│
|
|
14
|
-
│ ├──
|
|
15
|
-
│ │
|
|
16
|
-
│
|
|
17
|
-
│ └──
|
|
18
|
-
│
|
|
19
|
-
│
|
|
20
|
-
│
|
|
21
|
-
│
|
|
22
|
-
│
|
|
23
|
-
│
|
|
24
|
-
|
|
14
|
+
│ ├── domain/
|
|
15
|
+
│ │ └── billing.entity.ts # Zod schema for entity
|
|
16
|
+
│ ├── ports/
|
|
17
|
+
│ │ └── billing.repository.ts # Repository interface
|
|
18
|
+
│ ├── adapters/
|
|
19
|
+
│ │ └── billing.drizzle.ts # Drizzle implementation
|
|
20
|
+
│ ├── usecases/
|
|
21
|
+
│ │ ├── list-billings.ts
|
|
22
|
+
│ │ ├── get-billing.ts
|
|
23
|
+
│ │ ├── create-billing.ts
|
|
24
|
+
│ │ └── delete-billing.ts
|
|
25
|
+
│ ├── api/
|
|
26
|
+
│ │ └── billings.router.ts # oRPC router
|
|
27
|
+
│ ├── ui/
|
|
28
|
+
│ │ └── BillingsPage.tsx # React component
|
|
29
|
+
│ ├── module.ts # Server module
|
|
30
|
+
│ ├── client-module.ts # Client module
|
|
31
|
+
│ └── index.ts
|
|
32
|
+
├── package.json # Uses @kuckit/* from npm
|
|
25
33
|
└── tsconfig.json
|
|
26
34
|
```
|
|
27
35
|
|
|
28
36
|
### package.json Metadata
|
|
29
37
|
|
|
38
|
+
Modules depend on `@kuckit/*` npm packages:
|
|
39
|
+
|
|
30
40
|
```json
|
|
31
41
|
{
|
|
32
42
|
"name": "@acme/billing-module",
|
|
33
43
|
"version": "1.0.0",
|
|
34
44
|
"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
45
|
"exports": {
|
|
42
|
-
".":
|
|
43
|
-
|
|
44
|
-
"types": "./dist/index.d.ts"
|
|
45
|
-
},
|
|
46
|
-
"./client": {
|
|
47
|
-
"import": "./dist/client/index.js",
|
|
48
|
-
"types": "./dist/client/index.d.ts"
|
|
49
|
-
}
|
|
46
|
+
".": "./src/index.ts",
|
|
47
|
+
"./client": "./src/client-module.ts"
|
|
50
48
|
},
|
|
51
49
|
"peerDependencies": {
|
|
52
|
-
"@kuckit/sdk": "
|
|
50
|
+
"@kuckit/sdk": "^1.0.0",
|
|
51
|
+
"@kuckit/sdk-react": "^1.0.0",
|
|
52
|
+
"typescript": "^5"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@kuckit/api": "^1.0.0",
|
|
56
|
+
"@orpc/server": "^1.10.0",
|
|
57
|
+
"drizzle-orm": "^0.44.0",
|
|
58
|
+
"zod": "^3.23.0"
|
|
59
|
+
},
|
|
60
|
+
"kuckit": {
|
|
61
|
+
"id": "acme.billing",
|
|
62
|
+
"server": "./dist/module.js",
|
|
63
|
+
"client": "./dist/client-module.js"
|
|
53
64
|
}
|
|
54
65
|
}
|
|
55
66
|
```
|
|
@@ -1,67 +1,82 @@
|
|
|
1
1
|
# Kuckit Package Reference
|
|
2
2
|
|
|
3
|
-
Quick reference for all packages
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
3
|
+
Quick reference for all `@kuckit/*` packages from npm.
|
|
4
|
+
|
|
5
|
+
## Framework Packages
|
|
6
|
+
|
|
7
|
+
These are installed from npm as dependencies, not copied into your project:
|
|
8
|
+
|
|
9
|
+
| Package | Description |
|
|
10
|
+
| ------------------- | ------------------------------------------------------ |
|
|
11
|
+
| `@kuckit/sdk` | Server-side module system, DI container, core services |
|
|
12
|
+
| `@kuckit/sdk-react` | Client-side module system, hooks, providers |
|
|
13
|
+
| `@kuckit/api` | oRPC procedures (publicProcedure, protectedProcedure) |
|
|
14
|
+
| `@kuckit/db` | Drizzle ORM utilities and connection |
|
|
15
|
+
| `@kuckit/auth` | Better-Auth configuration |
|
|
16
|
+
| `@kuckit/domain` | Base types, Logger, Clock interfaces |
|
|
17
|
+
| `@kuckit/contracts` | Shared DTOs and Zod schemas |
|
|
18
|
+
|
|
19
|
+
## Your Project Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
your-app/
|
|
23
|
+
├── apps/
|
|
24
|
+
│ ├── server/ # Express bootstrap (imports @kuckit/* from npm)
|
|
25
|
+
│ └── web/ # React bootstrap (imports @kuckit/* from npm)
|
|
26
|
+
├── packages/
|
|
27
|
+
│ └── your-module/ # Your modules with Clean Architecture
|
|
28
|
+
├── node_modules/
|
|
29
|
+
│ └── @kuckit/ # Framework packages from npm
|
|
30
|
+
├── package.json # Lists @kuckit/* deps
|
|
31
|
+
└── drizzle.config.ts # Points to @kuckit/db + module schemas
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Key Files Per Framework Package
|
|
26
35
|
|
|
27
36
|
### @kuckit/sdk
|
|
28
37
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
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 |
|
|
38
|
+
| Export | Purpose |
|
|
39
|
+
| ----------------------- | ------------------------ |
|
|
40
|
+
| `createKuckitContainer` | DI container creation |
|
|
41
|
+
| `loadKuckitModules` | Module loading |
|
|
42
|
+
| `defineKuckitModule` | Server module definition |
|
|
43
|
+
| `disposeContainer` | Container cleanup |
|
|
44
|
+
| `asClass`, `asFunction` | DI registration helpers |
|
|
45
|
+
| `asValue` | DI value registration |
|
|
54
46
|
|
|
55
47
|
### @kuckit/sdk-react
|
|
56
48
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
|
|
49
|
+
| Export | Purpose |
|
|
50
|
+
| -------------------------- | ------------------------ |
|
|
51
|
+
| `defineKuckitClientModule` | Client module definition |
|
|
52
|
+
| `loadKuckitClientModules` | Client module loading |
|
|
53
|
+
| `KuckitNavProvider` | Navigation provider |
|
|
54
|
+
| `useKuckitNav` | Navigation hook |
|
|
55
|
+
| `useRpc` | RPC client hook |
|
|
56
|
+
|
|
57
|
+
### @kuckit/api
|
|
58
|
+
|
|
59
|
+
| Export | Purpose |
|
|
60
|
+
| -------------------- | ----------------------- |
|
|
61
|
+
| `publicProcedure` | Public oRPC procedure |
|
|
62
|
+
| `protectedProcedure` | Auth-required procedure |
|
|
63
|
+
| `createContext` | oRPC context factory |
|
|
64
|
+
| `appRouter` | Base app router |
|
|
65
|
+
|
|
66
|
+
### @kuckit/db
|
|
67
|
+
|
|
68
|
+
| Export | Purpose |
|
|
69
|
+
| ------------ | -------------------------- |
|
|
70
|
+
| `createDb` | Drizzle instance factory |
|
|
71
|
+
| `createPool` | PostgreSQL pool factory |
|
|
72
|
+
| `authSchema` | Better-Auth schema exports |
|
|
73
|
+
|
|
74
|
+
### @kuckit/auth
|
|
75
|
+
|
|
76
|
+
| Export | Purpose |
|
|
77
|
+
| ------------ | --------------------- |
|
|
78
|
+
| `auth` | Better-Auth instance |
|
|
79
|
+
| `authClient` | Client auth utilities |
|
|
65
80
|
|
|
66
81
|
## Import Patterns
|
|
67
82
|
|
|
@@ -83,30 +98,35 @@ import {
|
|
|
83
98
|
loadKuckitClientModules,
|
|
84
99
|
KuckitNavProvider,
|
|
85
100
|
useKuckitNav,
|
|
101
|
+
useRpc,
|
|
86
102
|
} from '@kuckit/sdk-react'
|
|
87
103
|
|
|
88
|
-
//
|
|
89
|
-
import {
|
|
104
|
+
// API imports (for modules)
|
|
105
|
+
import { protectedProcedure, publicProcedure } from '@kuckit/api'
|
|
90
106
|
|
|
91
|
-
//
|
|
92
|
-
import {
|
|
93
|
-
|
|
94
|
-
// Contracts imports
|
|
95
|
-
import { CreateUserInput, UserDTO, createUserSchema } from '@kuckit/contracts'
|
|
107
|
+
// Database imports
|
|
108
|
+
import { createDb, createPool } from '@kuckit/db/connection'
|
|
96
109
|
```
|
|
97
110
|
|
|
98
111
|
## Package Dependencies
|
|
99
112
|
|
|
100
113
|
### What Each Package Can Import
|
|
101
114
|
|
|
102
|
-
| Package
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
|
115
|
+
| Package | Allowed Imports |
|
|
116
|
+
| ------------ | ------------------------------- |
|
|
117
|
+
| Your modules | @kuckit/\* packages, zod |
|
|
118
|
+
| apps/server | @kuckit/\*, your modules |
|
|
119
|
+
| apps/web | @kuckit/sdk-react, your modules |
|
|
120
|
+
|
|
121
|
+
### Module Internal Layers
|
|
122
|
+
|
|
123
|
+
Within your modules, follow Clean Architecture:
|
|
124
|
+
|
|
125
|
+
| Layer | Can Import |
|
|
126
|
+
| -------- | ----------------------------- |
|
|
127
|
+
| domain | (none - pure Zod schemas) |
|
|
128
|
+
| ports | domain |
|
|
129
|
+
| usecases | domain, ports |
|
|
130
|
+
| adapters | domain, drizzle-orm |
|
|
131
|
+
| api | domain, usecases, @kuckit/api |
|
|
132
|
+
| ui | @kuckit/sdk-react, react |
|
package/templates/base/AGENTS.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
```bash
|
|
6
6
|
bun install # Install dependencies
|
|
7
7
|
docker compose up -d # Start PostgreSQL
|
|
8
|
-
bun run db:
|
|
8
|
+
bun run db:push # Sync schema to database (dev) or db:migrate (prod)
|
|
9
9
|
bun run dev # Start dev servers (web + server)
|
|
10
10
|
```
|
|
11
11
|
|
|
@@ -17,15 +17,15 @@ __APP_NAME_KEBAB__/
|
|
|
17
17
|
│ ├── server/ # Express + oRPC backend (port 3000)
|
|
18
18
|
│ └── web/ # React + TanStack Router frontend (port 3001)
|
|
19
19
|
├── packages/
|
|
20
|
-
│ ├── api/ # Shared API context and types
|
|
21
|
-
│ ├── auth/ # Better-Auth configuration
|
|
22
|
-
│ ├── db/ # Drizzle ORM, migrations, schema
|
|
23
20
|
│ └── items-module/ # Example Kuckit module (reference implementation)
|
|
24
21
|
├── .env.example # Environment template
|
|
25
22
|
├── docker-compose.yml # PostgreSQL container
|
|
23
|
+
├── drizzle.config.ts # Drizzle pointing to @kuckit/db + module schemas
|
|
26
24
|
└── turbo.json # Turborepo configuration
|
|
27
25
|
```
|
|
28
26
|
|
|
27
|
+
**Note:** Core packages (`api`, `auth`, `db`, `domain`, `contracts`) are installed from npm as `@kuckit/*` dependencies, not copied into your project.
|
|
28
|
+
|
|
29
29
|
## Module Development
|
|
30
30
|
|
|
31
31
|
Kuckit uses a **module-based architecture** where each module contains its own Clean Architecture layers internally.
|
|
@@ -47,15 +47,21 @@ packages/your-module/
|
|
|
47
47
|
|
|
48
48
|
### Creating a New Module
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
```bash
|
|
51
|
+
bunx kuckit generate module your-module
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This scaffolds a full Clean Architecture module. Then:
|
|
55
|
+
|
|
56
|
+
1. Implement your domain entities in `domain/`
|
|
57
|
+
2. Define repository interfaces in `ports/`
|
|
58
|
+
3. Implement adapters in `adapters/`
|
|
59
|
+
4. Create use cases in `usecases/`
|
|
60
|
+
5. Expose API in `api/` via oRPC router
|
|
61
|
+
6. Register module in `apps/server/src/config/modules.ts`
|
|
62
|
+
7. Add client module to `apps/web/src/modules.client.ts`
|
|
63
|
+
8. Add schema path to `drizzle.config.ts` in project root
|
|
64
|
+
9. Run `bun run db:push` to create the table
|
|
59
65
|
|
|
60
66
|
### Dependency Rules
|
|
61
67
|
|
|
@@ -256,6 +262,26 @@ export const wireModuleRpcRouters = (registrations: ApiRegistration[]) => {
|
|
|
256
262
|
}
|
|
257
263
|
```
|
|
258
264
|
|
|
265
|
+
### REST Router Pattern
|
|
266
|
+
|
|
267
|
+
For modules that need direct Express response access (e.g., AI streaming with `pipeUIMessageStreamToResponse`), use REST routers instead of oRPC:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// In your module's registerApi()
|
|
271
|
+
ctx.addApiRegistration({
|
|
272
|
+
type: 'rest-router',
|
|
273
|
+
name: 'ai',
|
|
274
|
+
router: createAiRouter(),
|
|
275
|
+
prefix: '/ai', // Optional, defaults to /<name>
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
REST routers are mounted at `/api<prefix>` (e.g., `/api/ai/chat`). They receive:
|
|
280
|
+
|
|
281
|
+
- Per-request scoped container via `req.scope`
|
|
282
|
+
- Session via `req.scope.cradle.session`
|
|
283
|
+
- Full Express `Request` and `Response` objects
|
|
284
|
+
|
|
259
285
|
### Typing DI in Routers
|
|
260
286
|
|
|
261
287
|
Use a module-local interface to type `context.di.cradle`:
|
|
@@ -284,14 +310,14 @@ export const invoicesRouter = {
|
|
|
284
310
|
|
|
285
311
|
### Add a new database table
|
|
286
312
|
|
|
287
|
-
1. Define schema in your module's `adapters/`
|
|
313
|
+
1. Define schema in your module's `adapters/` directory
|
|
288
314
|
2. Run `bun run db:generate`
|
|
289
315
|
3. Run `bun run db:migrate`
|
|
290
316
|
|
|
291
317
|
### Add authentication to an endpoint
|
|
292
318
|
|
|
293
319
|
```typescript
|
|
294
|
-
import { protectedProcedure } from '@
|
|
320
|
+
import { protectedProcedure } from '@kuckit/api'
|
|
295
321
|
|
|
296
322
|
export const myRouter = {
|
|
297
323
|
protectedRoute: protectedProcedure.input(mySchema).handler(async ({ input, context }) => {
|
|
@@ -363,12 +389,12 @@ See `.env.example` for required variables:
|
|
|
363
389
|
|
|
364
390
|
**Cause**: Module schema not registered in `drizzle.config.ts`.
|
|
365
391
|
|
|
366
|
-
**Fix**: Add the module's schema path to `
|
|
392
|
+
**Fix**: Add the module's schema path to `drizzle.config.ts` (in project root):
|
|
367
393
|
|
|
368
394
|
```typescript
|
|
369
395
|
schema: [
|
|
370
|
-
|
|
371
|
-
|
|
396
|
+
'./node_modules/@kuckit/db/dist/schema/auth.js',
|
|
397
|
+
'./packages/your-module/src/adapters',
|
|
372
398
|
],
|
|
373
399
|
```
|
|
374
400
|
|
|
@@ -8,14 +8,16 @@ Express backend hosting oRPC API, Better-Auth authentication, and the Kuckit mod
|
|
|
8
8
|
|
|
9
9
|
## Key Files
|
|
10
10
|
|
|
11
|
-
| File
|
|
12
|
-
|
|
|
13
|
-
| `server.ts`
|
|
14
|
-
| `container.ts`
|
|
15
|
-
| `config/modules.ts`
|
|
16
|
-
| `rpc.ts`
|
|
17
|
-
| `rpc-router-registry.ts`
|
|
18
|
-
| `
|
|
11
|
+
| File | Purpose |
|
|
12
|
+
| ------------------------- | -------------------------- |
|
|
13
|
+
| `server.ts` | Entry point, bootstrap |
|
|
14
|
+
| `container.ts` | DI container setup |
|
|
15
|
+
| `config/modules.ts` | Module registration |
|
|
16
|
+
| `rpc.ts` | oRPC handler setup |
|
|
17
|
+
| `rpc-router-registry.ts` | Mutable router for modules |
|
|
18
|
+
| `rest-router-registry.ts` | REST router collection |
|
|
19
|
+
| `module-rest-routes.ts` | REST router wiring |
|
|
20
|
+
| `auth.ts` | Better-Auth configuration |
|
|
19
21
|
|
|
20
22
|
## Module Loading Sequence
|
|
21
23
|
|
|
@@ -26,10 +28,11 @@ The server bootstraps in this order:
|
|
|
26
28
|
2. loadKuckitModules() with onApiRegistrations callback:
|
|
27
29
|
├─► register() hooks run (DI bindings)
|
|
28
30
|
├─► registerApi() hooks run (routers collected)
|
|
29
|
-
├─► onApiRegistrations() - Wire
|
|
31
|
+
├─► onApiRegistrations() - Wire RPC and REST routers
|
|
30
32
|
└─► onBootstrap() hooks run (startup logic)
|
|
31
33
|
3. setupRPC() - Create RPCHandler AFTER modules loaded
|
|
32
|
-
4. Express
|
|
34
|
+
4. setupModuleRestRouters() - Mount REST routers to Express
|
|
35
|
+
5. Express listens
|
|
33
36
|
```
|
|
34
37
|
|
|
35
38
|
## Router Registry Pattern
|
|
@@ -56,12 +59,34 @@ await loadKuckitModules({
|
|
|
56
59
|
modules: getModuleSpecs(),
|
|
57
60
|
onApiRegistrations: (registrations) => {
|
|
58
61
|
wireModuleRpcRouters(registrations) // Wire BEFORE setupRPC()
|
|
62
|
+
collectModuleRestRouters(registrations) // Collect REST routers
|
|
59
63
|
},
|
|
60
64
|
})
|
|
61
65
|
|
|
62
66
|
setupRPC(app) // Now create handler with fully-wired router
|
|
67
|
+
setupModuleRestRouters(app) // Mount REST routers at /api/<name>
|
|
63
68
|
```
|
|
64
69
|
|
|
70
|
+
## REST Router Pattern
|
|
71
|
+
|
|
72
|
+
For modules that need direct Express response access (e.g., AI streaming), use REST routers:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// In your module's registerApi()
|
|
76
|
+
ctx.addApiRegistration({
|
|
77
|
+
type: 'rest-router',
|
|
78
|
+
name: 'ai',
|
|
79
|
+
router: createAiRouter(),
|
|
80
|
+
prefix: '/ai', // Optional, defaults to /<name>
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
REST routers are mounted at `/api<prefix>` (e.g., `/api/ai/chat`). They receive:
|
|
85
|
+
|
|
86
|
+
- Per-request scoped container via `req.scope`
|
|
87
|
+
- Session via `req.scope.cradle.session`
|
|
88
|
+
- Full Express `Request` and `Response` objects
|
|
89
|
+
|
|
65
90
|
## Per-Request Scoping
|
|
66
91
|
|
|
67
92
|
Each HTTP request gets a scoped container with:
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"express": "^5.1.0",
|
|
12
12
|
"cors": "^2.8.5",
|
|
13
|
-
"dotenv": "^17.
|
|
13
|
+
"dotenv": "^17.0.0",
|
|
14
14
|
"awilix": "^12.0.5",
|
|
15
15
|
"pg": "^8.14.1",
|
|
16
16
|
"@orpc/server": "^1.10.0",
|
|
17
17
|
"@orpc/zod": "^1.10.0",
|
|
18
|
-
"better-auth": "^1.3.
|
|
19
|
-
"zod": "^
|
|
20
|
-
"@kuckit/sdk": "^
|
|
21
|
-
"@
|
|
22
|
-
"@
|
|
23
|
-
"@
|
|
18
|
+
"better-auth": "^1.3.0",
|
|
19
|
+
"zod": "^3.23.0",
|
|
20
|
+
"@kuckit/sdk": "^2.0.0",
|
|
21
|
+
"@kuckit/api": "^2.0.0",
|
|
22
|
+
"@kuckit/auth": "^2.0.0",
|
|
23
|
+
"@kuckit/db": "^2.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/express": "^5.0.1",
|
|
@@ -6,9 +6,10 @@ import {
|
|
|
6
6
|
type CoreCradle,
|
|
7
7
|
} from '@kuckit/sdk'
|
|
8
8
|
import type { Pool } from 'pg'
|
|
9
|
-
import { ensureDatabaseUrl } from '@
|
|
9
|
+
import { ensureDatabaseUrl } from '@kuckit/db/connection'
|
|
10
10
|
import { getModuleSpecs } from './config/modules'
|
|
11
11
|
import { wireModuleRpcRouters } from './rpc-router-registry'
|
|
12
|
+
import { collectModuleRestRouters } from './rest-router-registry'
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Server configuration
|
|
@@ -60,6 +61,7 @@ export const buildRootContainer = async (): Promise<AppContainer> => {
|
|
|
60
61
|
modules: getModuleSpecs(),
|
|
61
62
|
onApiRegistrations: (registrations) => {
|
|
62
63
|
wireModuleRpcRouters(registrations)
|
|
64
|
+
collectModuleRestRouters(registrations)
|
|
63
65
|
console.log(`Loaded ${registrations.length} API registrations from modules`)
|
|
64
66
|
},
|
|
65
67
|
onComplete: () => {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Express, Request, Response, NextFunction } from 'express'
|
|
2
|
+
import express from 'express'
|
|
3
|
+
import { asValue } from 'awilix'
|
|
4
|
+
import { fromNodeHeaders } from 'better-auth/node'
|
|
5
|
+
import { auth } from '@kuckit/auth'
|
|
6
|
+
import { getRestRouters } from './rest-router-registry'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Middleware to add session to request scope for REST routes.
|
|
10
|
+
* Similar to createContext for RPC, but for Express routes.
|
|
11
|
+
*/
|
|
12
|
+
const sessionMiddleware = async (req: Request, _res: Response, next: NextFunction) => {
|
|
13
|
+
if (!req.scope) {
|
|
14
|
+
return next(new Error('Request scope not initialized'))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const session = await auth.api.getSession({
|
|
19
|
+
headers: fromNodeHeaders(req.headers),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
req.scope.register({
|
|
23
|
+
session: asValue(session),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
next()
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('[REST Auth] Failed to get session:', error)
|
|
29
|
+
req.scope.register({
|
|
30
|
+
session: asValue(null),
|
|
31
|
+
})
|
|
32
|
+
next()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Setup REST routers from modules.
|
|
38
|
+
* Must be called after container middleware is initialized.
|
|
39
|
+
*/
|
|
40
|
+
export const setupModuleRestRouters = (app: Express): void => {
|
|
41
|
+
const routers = getRestRouters()
|
|
42
|
+
|
|
43
|
+
for (const { name, router, basePath } of routers) {
|
|
44
|
+
app.use(`/api${basePath}`, express.json(), sessionMiddleware, router)
|
|
45
|
+
console.log(`[REST] Mounted module router: /api${basePath} (${name})`)
|
|
46
|
+
}
|
|
47
|
+
}
|