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.
Files changed (40) hide show
  1. package/dist/bin.js +1 -1
  2. package/dist/{create-project-CP-h4Ygi.js → create-project-geQBZ0Ru.js} +5 -0
  3. package/dist/index.js +1 -1
  4. package/package.json +1 -1
  5. package/templates/base/.claude/skills/kuckit/SKILL.md +22 -2
  6. package/templates/base/.claude/skills/kuckit/references/MODULE-DEVELOPMENT.md +39 -28
  7. package/templates/base/.claude/skills/kuckit/references/PACKAGES.md +94 -74
  8. package/templates/base/AGENTS.md +44 -18
  9. package/templates/base/apps/server/AGENTS.md +35 -10
  10. package/templates/base/apps/server/package.json +7 -7
  11. package/templates/base/apps/server/src/auth.ts +1 -1
  12. package/templates/base/apps/server/src/container.ts +3 -1
  13. package/templates/base/apps/server/src/module-rest-routes.ts +47 -0
  14. package/templates/base/apps/server/src/rest-router-registry.ts +32 -0
  15. package/templates/base/apps/server/src/rpc.ts +1 -1
  16. package/templates/base/apps/server/src/server.ts +2 -0
  17. package/templates/base/apps/web/package.json +9 -9
  18. package/templates/base/apps/web/src/services/auth-client.ts +1 -1
  19. package/templates/base/{packages/db/src/migrations → drizzle}/0000_init.sql +31 -36
  20. package/templates/base/{packages/db/src/migrations → drizzle}/meta/_journal.json +1 -1
  21. package/templates/base/drizzle.config.ts +38 -0
  22. package/templates/base/package.json +14 -9
  23. package/templates/base/packages/items-module/package.json +7 -7
  24. package/templates/base/packages/items-module/src/api/items.router.ts +1 -1
  25. package/templates/base/packages/api/AGENTS.md +0 -66
  26. package/templates/base/packages/api/package.json +0 -35
  27. package/templates/base/packages/api/src/context.ts +0 -48
  28. package/templates/base/packages/api/src/index.ts +0 -22
  29. package/templates/base/packages/api/tsconfig.json +0 -8
  30. package/templates/base/packages/auth/AGENTS.md +0 -61
  31. package/templates/base/packages/auth/package.json +0 -27
  32. package/templates/base/packages/auth/src/index.ts +0 -22
  33. package/templates/base/packages/auth/tsconfig.json +0 -8
  34. package/templates/base/packages/db/AGENTS.md +0 -99
  35. package/templates/base/packages/db/drizzle.config.ts +0 -23
  36. package/templates/base/packages/db/package.json +0 -36
  37. package/templates/base/packages/db/src/connection.ts +0 -40
  38. package/templates/base/packages/db/src/index.ts +0 -4
  39. package/templates/base/packages/db/src/schema/auth.ts +0 -51
  40. 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-CP-h4Ygi.js";
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
@@ -1,3 +1,3 @@
1
- import { t as createProject } from "./create-project-CP-h4Ygi.js";
1
+ import { t as createProject } from "./create-project-geQBZ0Ru.js";
2
2
 
3
3
  export { createProject };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kuckit-app",
3
- "version": "0.3.5",
3
+ "version": "0.4.0",
4
4
  "description": "Create a new Kuckit application",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -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/module.js",
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
- │ ├── 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
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
- "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
- }
46
+ ".": "./src/index.ts",
47
+ "./client": "./src/client-module.ts"
50
48
  },
51
49
  "peerDependencies": {
52
- "@kuckit/sdk": ">=0.1.0"
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 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
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
- | 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 |
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
- | 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 |
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
- // CLI programmatic usage
89
- import { generateModule, addModule, discoverModules } from '@kuckit/cli'
104
+ // API imports (for modules)
105
+ import { protectedProcedure, publicProcedure } from '@kuckit/api'
90
106
 
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'
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 | 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 |
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 |
@@ -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:migrate # Run database migrations
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
- 1. Copy `packages/items-module` as a template
51
- 2. Rename and update `package.json`
52
- 3. Implement your domain entities in `domain/`
53
- 4. Define repository interfaces in `ports/`
54
- 5. Implement adapters in `adapters/`
55
- 6. Create use cases in `usecases/`
56
- 7. Expose API in `api/` via oRPC router
57
- 8. Register module in `apps/server/src/config/modules.ts`
58
- 9. Add client module to `apps/web/src/modules.client.ts`
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/` or `packages/db/src/schema/`
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 '@__APP_NAME_KEBAB__/api'
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 `packages/db/drizzle.config.ts`:
392
+ **Fix**: Add the module's schema path to `drizzle.config.ts` (in project root):
367
393
 
368
394
  ```typescript
369
395
  schema: [
370
- resolve(currentDirPath, './src/schema'),
371
- resolve(currentDirPath, '../your-module/src/adapters/schema.drizzle.ts'),
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 | 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
- | `auth.ts` | Better-Auth configuration |
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 routers to rootRpcRouter
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 listens
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.2.2",
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.28",
19
- "zod": "^4.1.11",
20
- "@kuckit/sdk": "^1.0.0",
21
- "@__APP_NAME_KEBAB__/api": "workspace:*",
22
- "@__APP_NAME_KEBAB__/auth": "workspace:*",
23
- "@__APP_NAME_KEBAB__/db": "workspace:*"
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",
@@ -1,4 +1,4 @@
1
- import { auth } from '@__APP_NAME_KEBAB__/auth'
1
+ import { auth } from '@kuckit/auth'
2
2
  import { toNodeHandler } from 'better-auth/node'
3
3
  import type { Express } from 'express'
4
4
 
@@ -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 '@__APP_NAME_KEBAB__/db/connection'
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
+ }