create-kuckit-app 0.1.0 → 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.
Files changed (92) hide show
  1. package/dist/bin.js +11 -5
  2. package/dist/{create-project-DTm05G7D.js → create-project-CP-h4Ygi.js} +7 -5
  3. package/dist/index.js +1 -1
  4. package/package.json +3 -2
  5. package/templates/base/.claude/CLAUDE.md +44 -0
  6. package/templates/base/.claude/agents/daidalos.md +76 -0
  7. package/templates/base/.claude/agents/episteme.md +79 -0
  8. package/templates/base/.claude/agents/librarian.md +132 -0
  9. package/templates/base/.claude/agents/oracle.md +210 -0
  10. package/templates/base/.claude/commands/create-plan.md +159 -0
  11. package/templates/base/.claude/commands/file-beads.md +98 -0
  12. package/templates/base/.claude/commands/review-beads.md +161 -0
  13. package/templates/base/.claude/settings.json +11 -0
  14. package/templates/base/.claude/skills/kuckit/SKILL.md +436 -0
  15. package/templates/base/.claude/skills/kuckit/references/ARCHITECTURE.md +388 -0
  16. package/templates/base/.claude/skills/kuckit/references/CLI-COMMANDS.md +365 -0
  17. package/templates/base/.claude/skills/kuckit/references/MODULE-DEVELOPMENT.md +581 -0
  18. package/templates/base/.claude/skills/kuckit/references/PACKAGES.md +112 -0
  19. package/templates/base/.claude/skills/kuckit/references/PUBLISHING.md +231 -0
  20. package/templates/base/.env.example +13 -0
  21. package/templates/base/.github/workflows/ci.yml +28 -0
  22. package/templates/base/.husky/pre-commit +1 -0
  23. package/templates/base/.prettierignore +5 -0
  24. package/templates/base/.prettierrc +8 -0
  25. package/templates/base/AGENTS.md +148 -0
  26. package/templates/base/apps/server/.env.example +18 -0
  27. package/templates/base/apps/server/AGENTS.md +37 -0
  28. package/templates/base/apps/server/package.json +13 -4
  29. package/templates/base/apps/server/src/app.ts +20 -0
  30. package/templates/base/apps/server/src/auth.ts +10 -0
  31. package/templates/base/apps/server/src/config/modules.ts +14 -6
  32. package/templates/base/apps/server/src/container.ts +81 -0
  33. package/templates/base/apps/server/src/health.ts +27 -0
  34. package/templates/base/apps/server/src/middleware/container.ts +41 -0
  35. package/templates/base/apps/server/src/rpc-router-registry.ts +26 -0
  36. package/templates/base/apps/server/src/rpc.ts +31 -0
  37. package/templates/base/apps/server/src/server.ts +39 -29
  38. package/templates/base/apps/web/.env.example +4 -0
  39. package/templates/base/apps/web/AGENTS.md +53 -0
  40. package/templates/base/apps/web/index.html +1 -1
  41. package/templates/base/apps/web/package.json +15 -3
  42. package/templates/base/apps/web/src/lib/kuckit-router.ts +42 -0
  43. package/templates/base/apps/web/src/main.tsx +26 -14
  44. package/templates/base/apps/web/src/providers/KuckitProvider.tsx +147 -0
  45. package/templates/base/apps/web/src/providers/ServicesProvider.tsx +47 -0
  46. package/templates/base/apps/web/src/routeTree.gen.ts +91 -0
  47. package/templates/base/apps/web/src/routes/__root.tsx +31 -0
  48. package/templates/base/apps/web/src/routes/index.tsx +46 -0
  49. package/templates/base/apps/web/src/routes/login.tsx +108 -0
  50. package/templates/base/apps/web/src/services/auth-client.ts +12 -0
  51. package/templates/base/apps/web/src/services/index.ts +3 -0
  52. package/templates/base/apps/web/src/services/rpc.ts +29 -0
  53. package/templates/base/apps/web/src/services/types.ts +14 -0
  54. package/templates/base/apps/web/vite.config.ts +2 -1
  55. package/templates/base/docker-compose.yml +23 -0
  56. package/templates/base/eslint.config.js +18 -0
  57. package/templates/base/package.json +32 -2
  58. package/templates/base/packages/api/AGENTS.md +27 -0
  59. package/templates/base/packages/api/package.json +35 -0
  60. package/templates/base/packages/api/src/context.ts +48 -0
  61. package/templates/base/packages/api/src/index.ts +22 -0
  62. package/templates/base/packages/api/tsconfig.json +8 -0
  63. package/templates/base/packages/auth/AGENTS.md +45 -0
  64. package/templates/base/packages/auth/package.json +27 -0
  65. package/templates/base/packages/auth/src/index.ts +22 -0
  66. package/templates/base/packages/auth/tsconfig.json +8 -0
  67. package/templates/base/packages/db/AGENTS.md +59 -0
  68. package/templates/base/packages/db/drizzle.config.ts +19 -0
  69. package/templates/base/packages/db/package.json +36 -0
  70. package/templates/base/packages/db/src/connection.ts +40 -0
  71. package/templates/base/packages/db/src/index.ts +4 -0
  72. package/templates/base/packages/db/src/migrations/0000_init.sql +54 -0
  73. package/templates/base/packages/db/src/migrations/meta/_journal.json +13 -0
  74. package/templates/base/packages/db/src/schema/auth.ts +51 -0
  75. package/templates/base/packages/db/tsconfig.json +8 -0
  76. package/templates/base/packages/items-module/AGENTS.md +112 -0
  77. package/templates/base/packages/items-module/package.json +32 -0
  78. package/templates/base/packages/items-module/src/adapters/item.drizzle.ts +66 -0
  79. package/templates/base/packages/items-module/src/api/items.router.ts +47 -0
  80. package/templates/base/packages/items-module/src/client-module.ts +39 -0
  81. package/templates/base/packages/items-module/src/domain/item.entity.ts +36 -0
  82. package/templates/base/packages/items-module/src/index.ts +15 -0
  83. package/templates/base/packages/items-module/src/module.ts +53 -0
  84. package/templates/base/packages/items-module/src/ports/item.repository.ts +13 -0
  85. package/templates/base/packages/items-module/src/ui/ItemsPage.tsx +162 -0
  86. package/templates/base/packages/items-module/src/usecases/create-item.ts +25 -0
  87. package/templates/base/packages/items-module/src/usecases/delete-item.ts +18 -0
  88. package/templates/base/packages/items-module/src/usecases/get-item.ts +19 -0
  89. package/templates/base/packages/items-module/src/usecases/list-items.ts +21 -0
  90. package/templates/base/packages/items-module/tsconfig.json +9 -0
  91. package/templates/base/turbo.json +13 -1
  92. package/templates/base/apps/web/src/App.tsx +0 -16
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@__APP_NAME_KEBAB__/db",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.ts",
11
+ "default": "./src/index.ts"
12
+ },
13
+ "./*": {
14
+ "types": "./src/*.ts",
15
+ "default": "./src/*.ts"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "db:generate": "drizzle-kit generate",
20
+ "db:migrate": "drizzle-kit migrate",
21
+ "db:studio": "drizzle-kit studio"
22
+ },
23
+ "devDependencies": {
24
+ "drizzle-kit": "^0.31.2",
25
+ "@types/pg": "^8.11.11"
26
+ },
27
+ "peerDependencies": {
28
+ "typescript": "^5"
29
+ },
30
+ "dependencies": {
31
+ "drizzle-orm": "^0.44.2",
32
+ "pg": "^8.14.1",
33
+ "dotenv": "^17.2.2",
34
+ "zod": "^4.1.11"
35
+ }
36
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Database connection utilities
3
+ * Single source of truth for DATABASE_URL construction
4
+ */
5
+
6
+ /**
7
+ * Build DATABASE_URL from environment variables
8
+ *
9
+ * Supports two modes:
10
+ * 1. Direct DATABASE_URL - used in local development
11
+ * 2. Individual DB_* vars - used in Cloud Run with Cloud SQL Auth Proxy
12
+ *
13
+ * @throws Error if neither DATABASE_URL nor complete DB_* vars are provided
14
+ */
15
+ export function buildDatabaseUrl(): string {
16
+ // Prefer DATABASE_URL if provided directly
17
+ if (process.env.DATABASE_URL) {
18
+ return process.env.DATABASE_URL
19
+ }
20
+
21
+ // Construct from individual components (Cloud Run with Cloud SQL)
22
+ const { DB_HOST, DB_USER, DB_PASSWORD, DB_NAME } = process.env
23
+ if (DB_HOST && DB_USER && DB_PASSWORD && DB_NAME) {
24
+ return `postgresql://${DB_USER}:${encodeURIComponent(DB_PASSWORD)}@/${DB_NAME}?host=${DB_HOST}`
25
+ }
26
+
27
+ throw new Error(
28
+ 'Missing database configuration: provide DATABASE_URL or DB_HOST, DB_USER, DB_PASSWORD, DB_NAME'
29
+ )
30
+ }
31
+
32
+ /**
33
+ * Ensure DATABASE_URL is set in process.env
34
+ * Call this early in application startup to set up the environment
35
+ */
36
+ export function ensureDatabaseUrl(): string {
37
+ const url = buildDatabaseUrl()
38
+ process.env.DATABASE_URL = url
39
+ return url
40
+ }
@@ -0,0 +1,4 @@
1
+ import { drizzle } from 'drizzle-orm/node-postgres'
2
+ import { buildDatabaseUrl } from './connection'
3
+
4
+ export const db = drizzle(buildDatabaseUrl())
@@ -0,0 +1,54 @@
1
+ -- Initial migration: Auth tables
2
+ -- This migration is generated by drizzle-kit
3
+ -- Run: bun run db:migrate
4
+
5
+ CREATE TABLE IF NOT EXISTS "user" (
6
+ "id" text PRIMARY KEY NOT NULL,
7
+ "name" text NOT NULL,
8
+ "email" text NOT NULL,
9
+ "email_verified" boolean NOT NULL,
10
+ "image" text,
11
+ "created_at" timestamp NOT NULL,
12
+ "updated_at" timestamp NOT NULL,
13
+ CONSTRAINT "user_email_unique" UNIQUE("email")
14
+ );
15
+
16
+ CREATE TABLE IF NOT EXISTS "session" (
17
+ "id" text PRIMARY KEY NOT NULL,
18
+ "expires_at" timestamp NOT NULL,
19
+ "token" text NOT NULL,
20
+ "created_at" timestamp NOT NULL,
21
+ "updated_at" timestamp NOT NULL,
22
+ "ip_address" text,
23
+ "user_agent" text,
24
+ "user_id" text NOT NULL,
25
+ CONSTRAINT "session_token_unique" UNIQUE("token")
26
+ );
27
+
28
+ CREATE TABLE IF NOT EXISTS "account" (
29
+ "id" text PRIMARY KEY NOT NULL,
30
+ "account_id" text NOT NULL,
31
+ "provider_id" text NOT NULL,
32
+ "user_id" text NOT NULL,
33
+ "access_token" text,
34
+ "refresh_token" text,
35
+ "id_token" text,
36
+ "access_token_expires_at" timestamp,
37
+ "refresh_token_expires_at" timestamp,
38
+ "scope" text,
39
+ "password" text,
40
+ "created_at" timestamp NOT NULL,
41
+ "updated_at" timestamp NOT NULL
42
+ );
43
+
44
+ CREATE TABLE IF NOT EXISTS "verification" (
45
+ "id" text PRIMARY KEY NOT NULL,
46
+ "identifier" text NOT NULL,
47
+ "value" text NOT NULL,
48
+ "expires_at" timestamp NOT NULL,
49
+ "created_at" timestamp,
50
+ "updated_at" timestamp
51
+ );
52
+
53
+ ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
54
+ ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "7",
8
+ "when": 1734000000000,
9
+ "tag": "0000_init",
10
+ "breakpoints": true
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,51 @@
1
+ import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core'
2
+
3
+ export const user = pgTable('user', {
4
+ id: text('id').primaryKey(),
5
+ name: text('name').notNull(),
6
+ email: text('email').notNull().unique(),
7
+ emailVerified: boolean('email_verified').notNull(),
8
+ image: text('image'),
9
+ createdAt: timestamp('created_at').notNull(),
10
+ updatedAt: timestamp('updated_at').notNull(),
11
+ })
12
+
13
+ export const session = pgTable('session', {
14
+ id: text('id').primaryKey(),
15
+ expiresAt: timestamp('expires_at').notNull(),
16
+ token: text('token').notNull().unique(),
17
+ createdAt: timestamp('created_at').notNull(),
18
+ updatedAt: timestamp('updated_at').notNull(),
19
+ ipAddress: text('ip_address'),
20
+ userAgent: text('user_agent'),
21
+ userId: text('user_id')
22
+ .notNull()
23
+ .references(() => user.id, { onDelete: 'cascade' }),
24
+ })
25
+
26
+ export const account = pgTable('account', {
27
+ id: text('id').primaryKey(),
28
+ accountId: text('account_id').notNull(),
29
+ providerId: text('provider_id').notNull(),
30
+ userId: text('user_id')
31
+ .notNull()
32
+ .references(() => user.id, { onDelete: 'cascade' }),
33
+ accessToken: text('access_token'),
34
+ refreshToken: text('refresh_token'),
35
+ idToken: text('id_token'),
36
+ accessTokenExpiresAt: timestamp('access_token_expires_at'),
37
+ refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
38
+ scope: text('scope'),
39
+ password: text('password'),
40
+ createdAt: timestamp('created_at').notNull(),
41
+ updatedAt: timestamp('updated_at').notNull(),
42
+ })
43
+
44
+ export const verification = pgTable('verification', {
45
+ id: text('id').primaryKey(),
46
+ identifier: text('identifier').notNull(),
47
+ value: text('value').notNull(),
48
+ expiresAt: timestamp('expires_at').notNull(),
49
+ createdAt: timestamp('created_at'),
50
+ updatedAt: timestamp('updated_at'),
51
+ })
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }
@@ -0,0 +1,112 @@
1
+ # AGENTS.md - Items Module
2
+
3
+ > See root [AGENTS.md](../../AGENTS.md) for project overview
4
+
5
+ ## Purpose
6
+
7
+ **Reference implementation** of a Kuckit module demonstrating the full pattern.
8
+
9
+ Use this module as a template when creating new modules.
10
+
11
+ ## Structure
12
+
13
+ ```
14
+ src/
15
+ ├── domain/
16
+ │ └── item.entity.ts # Entity schema (Zod)
17
+ ├── ports/
18
+ │ └── item.repository.ts # Repository interface
19
+ ├── adapters/
20
+ │ └── item.drizzle.ts # Drizzle implementation
21
+ ├── usecases/
22
+ │ ├── create-item.ts # Create item use case
23
+ │ ├── get-item.ts # Get single item
24
+ │ ├── list-items.ts # List all items
25
+ │ └── delete-item.ts # Delete item
26
+ ├── api/
27
+ │ └── items.router.ts # oRPC router
28
+ ├── ui/
29
+ │ └── ItemsPage.tsx # React component
30
+ ├── module.ts # Server module definition
31
+ ├── client-module.ts # Client module definition
32
+ └── index.ts # Public exports
33
+ ```
34
+
35
+ ## Key Patterns
36
+
37
+ ### Domain Layer
38
+
39
+ Pure Zod schemas, no dependencies:
40
+
41
+ ```typescript
42
+ export const ItemSchema = z.object({
43
+ id: z.string().uuid(),
44
+ name: z.string().min(1),
45
+ })
46
+ ```
47
+
48
+ ### Ports Layer
49
+
50
+ Interfaces only, depend on domain:
51
+
52
+ ```typescript
53
+ export interface ItemRepository {
54
+ findAll(): Promise<Item[]>
55
+ findById(id: string): Promise<Item | null>
56
+ create(item: CreateItem): Promise<Item>
57
+ delete(id: string): Promise<void>
58
+ }
59
+ ```
60
+
61
+ ### Adapters Layer
62
+
63
+ Implement ports using Drizzle:
64
+
65
+ ```typescript
66
+ export const makeItemRepository = (db: Database): ItemRepository => ({
67
+ findAll: () => db.select().from(itemsTable),
68
+ // ...
69
+ })
70
+ ```
71
+
72
+ ### Use Cases
73
+
74
+ Business logic, depend on ports:
75
+
76
+ ```typescript
77
+ export const createItem =
78
+ (repo: ItemRepository) =>
79
+ async (input: CreateItem): Promise<Item> => {
80
+ return repo.create(input)
81
+ }
82
+ ```
83
+
84
+ ### Module Definition
85
+
86
+ Lifecycle hooks for registration:
87
+
88
+ ```typescript
89
+ export const kuckitModule = defineKuckitModule({
90
+ id: 'items',
91
+ register(ctx) {
92
+ /* DI registration */
93
+ },
94
+ registerApi(ctx) {
95
+ /* API routes */
96
+ },
97
+ onBootstrap(ctx) {
98
+ /* Startup logic */
99
+ },
100
+ })
101
+ ```
102
+
103
+ ## Creating a New Module
104
+
105
+ 1. Copy this entire `items-module` folder
106
+ 2. Rename to `your-module`
107
+ 3. Update `package.json` name
108
+ 4. Replace domain entities
109
+ 5. Update ports and adapters
110
+ 6. Implement use cases
111
+ 7. Create API router
112
+ 8. Register in server's `config/modules.ts`
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@__APP_NAME_KEBAB__/items-module",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.ts",
11
+ "default": "./src/index.ts"
12
+ },
13
+ "./client": {
14
+ "types": "./src/client-module.ts",
15
+ "default": "./src/client-module.ts"
16
+ }
17
+ },
18
+ "peerDependencies": {
19
+ "typescript": "^5"
20
+ },
21
+ "dependencies": {
22
+ "@kuckit/sdk": "^1.0.0",
23
+ "@kuckit/sdk-react": "^1.0.0",
24
+ "@orpc/server": "^1.10.0",
25
+ "@orpc/zod": "^1.10.0",
26
+ "drizzle-orm": "^0.44.2",
27
+ "zod": "^4.1.11",
28
+ "react": "^19.1.0",
29
+ "@__APP_NAME_KEBAB__/api": "workspace:*",
30
+ "@__APP_NAME_KEBAB__/db": "workspace:*"
31
+ }
32
+ }
@@ -0,0 +1,66 @@
1
+ import { pgTable, text, timestamp } from 'drizzle-orm/pg-core'
2
+ import { eq } from 'drizzle-orm'
3
+ import type { ItemRepository } from '../ports/item.repository'
4
+ import type { Item, CreateItemInput, UpdateItemInput } from '../domain/item.entity'
5
+
6
+ /**
7
+ * Items table schema for Drizzle ORM
8
+ */
9
+ export const itemsTable = pgTable('items', {
10
+ id: text('id').primaryKey(),
11
+ name: text('name').notNull(),
12
+ description: text('description'),
13
+ createdAt: timestamp('created_at').notNull().defaultNow(),
14
+ updatedAt: timestamp('updated_at').notNull().defaultNow(),
15
+ userId: text('user_id').notNull(),
16
+ })
17
+
18
+ /**
19
+ * Create a Drizzle-based item repository
20
+ */
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ export function makeItemRepository(db: any): ItemRepository {
23
+ return {
24
+ async findById(id: string): Promise<Item | null> {
25
+ const results = await db.select().from(itemsTable).where(eq(itemsTable.id, id))
26
+ return results[0] ?? null
27
+ },
28
+
29
+ async findByUserId(userId: string): Promise<Item[]> {
30
+ return db.select().from(itemsTable).where(eq(itemsTable.userId, userId))
31
+ },
32
+
33
+ async create(input: CreateItemInput & { id: string; userId: string }): Promise<Item> {
34
+ const now = new Date()
35
+ const item = {
36
+ id: input.id,
37
+ name: input.name,
38
+ description: input.description ?? null,
39
+ createdAt: now,
40
+ updatedAt: now,
41
+ userId: input.userId,
42
+ }
43
+ await db.insert(itemsTable).values(item)
44
+ return item as Item
45
+ },
46
+
47
+ async update(input: UpdateItemInput): Promise<Item | null> {
48
+ const existing = await this.findById(input.id)
49
+ if (!existing) return null
50
+
51
+ const updated = {
52
+ ...existing,
53
+ ...(input.name !== undefined && { name: input.name }),
54
+ ...(input.description !== undefined && { description: input.description }),
55
+ updatedAt: new Date(),
56
+ }
57
+ await db.update(itemsTable).set(updated).where(eq(itemsTable.id, input.id))
58
+ return updated
59
+ },
60
+
61
+ async delete(id: string): Promise<boolean> {
62
+ const result = await db.delete(itemsTable).where(eq(itemsTable.id, id))
63
+ return result.rowCount > 0
64
+ },
65
+ }
66
+ }
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod'
2
+ import { protectedProcedure } from '@__APP_NAME_KEBAB__/api'
3
+ import { CreateItemInputSchema } from '../domain/item.entity'
4
+ import type { ItemRepository } from '../ports/item.repository'
5
+
6
+ /**
7
+ * Items oRPC router
8
+ * Provides CRUD operations for items
9
+ */
10
+ export const itemsRouter = {
11
+ list: protectedProcedure.input(z.object({})).handler(async ({ context }) => {
12
+ const userId = context.session?.user?.id
13
+ if (!userId) throw new Error('User not authenticated')
14
+
15
+ // In a real app, you'd resolve this from the DI container
16
+ const items = await context.di.resolve<ItemRepository>('itemRepository').findByUserId(userId)
17
+ return items
18
+ }),
19
+
20
+ get: protectedProcedure
21
+ .input(z.object({ id: z.string() }))
22
+ .handler(async ({ input, context }) => {
23
+ const item = await context.di.resolve<ItemRepository>('itemRepository').findById(input.id)
24
+ return item
25
+ }),
26
+
27
+ create: protectedProcedure.input(CreateItemInputSchema).handler(async ({ input, context }) => {
28
+ const userId = context.session?.user?.id
29
+ if (!userId) throw new Error('User not authenticated')
30
+
31
+ const id = crypto.randomUUID()
32
+ const item = await context.di.resolve<ItemRepository>('itemRepository').create({
33
+ id,
34
+ name: input.name,
35
+ description: input.description,
36
+ userId,
37
+ })
38
+ return item
39
+ }),
40
+
41
+ delete: protectedProcedure
42
+ .input(z.object({ id: z.string() }))
43
+ .handler(async ({ input, context }) => {
44
+ const success = await context.di.resolve<ItemRepository>('itemRepository').delete(input.id)
45
+ return { success }
46
+ }),
47
+ }
@@ -0,0 +1,39 @@
1
+ import { defineKuckitClientModule, type KuckitClientModuleContext } from '@kuckit/sdk-react'
2
+ import { ItemsPage } from './ui/ItemsPage'
3
+
4
+ /**
5
+ * Items client module
6
+ * Registers routes and components for the web app
7
+ */
8
+ export const kuckitClientModule = defineKuckitClientModule({
9
+ id: 'items',
10
+ displayName: 'Items',
11
+ version: '0.1.0',
12
+
13
+ register(ctx: KuckitClientModuleContext) {
14
+ // Register the items page component
15
+ ctx.registerComponent('ItemsPage', ItemsPage)
16
+
17
+ // Add route for items page
18
+ ctx.addRoute({
19
+ id: 'items',
20
+ path: '/items',
21
+ component: ItemsPage,
22
+ meta: {
23
+ title: 'Items',
24
+ requiresAuth: true,
25
+ },
26
+ })
27
+
28
+ // Add navigation item
29
+ ctx.addNavItem({
30
+ id: 'items-nav',
31
+ label: 'Items',
32
+ path: '/items',
33
+ icon: 'list',
34
+ order: 10,
35
+ })
36
+ },
37
+ })
38
+
39
+ export { ItemsPage } from './ui/ItemsPage'
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod'
2
+
3
+ /**
4
+ * Item entity schema
5
+ */
6
+ export const ItemSchema = z.object({
7
+ id: z.string(),
8
+ name: z.string().min(1, 'Name is required'),
9
+ description: z.string().optional(),
10
+ createdAt: z.date(),
11
+ updatedAt: z.date(),
12
+ userId: z.string(),
13
+ })
14
+
15
+ export type Item = z.infer<typeof ItemSchema>
16
+
17
+ /**
18
+ * Create item input schema
19
+ */
20
+ export const CreateItemInputSchema = z.object({
21
+ name: z.string().min(1, 'Name is required'),
22
+ description: z.string().optional(),
23
+ })
24
+
25
+ export type CreateItemInput = z.infer<typeof CreateItemInputSchema>
26
+
27
+ /**
28
+ * Update item input schema
29
+ */
30
+ export const UpdateItemInputSchema = z.object({
31
+ id: z.string(),
32
+ name: z.string().min(1, 'Name is required').optional(),
33
+ description: z.string().optional(),
34
+ })
35
+
36
+ export type UpdateItemInput = z.infer<typeof UpdateItemInputSchema>
@@ -0,0 +1,15 @@
1
+ // Server module exports
2
+ export { kuckitModule } from './module'
3
+ export type { ItemsModuleConfig } from './module'
4
+
5
+ // Domain exports
6
+ export * from './domain/item.entity'
7
+
8
+ // Port exports
9
+ export type { ItemRepository } from './ports/item.repository'
10
+
11
+ // Use case exports
12
+ export { makeListItems } from './usecases/list-items'
13
+ export { makeCreateItem } from './usecases/create-item'
14
+ export { makeGetItem } from './usecases/get-item'
15
+ export { makeDeleteItem } from './usecases/delete-item'
@@ -0,0 +1,53 @@
1
+ import { defineKuckitModule, asFunction, type KuckitModuleContext } from '@kuckit/sdk'
2
+ import { itemsTable, makeItemRepository } from './adapters/item.drizzle'
3
+ import { itemsRouter } from './api/items.router'
4
+
5
+ export type ItemsModuleConfig = Record<string, never>
6
+
7
+ /**
8
+ * Items module - example Kuckit module demonstrating the full pattern
9
+ *
10
+ * This module shows:
11
+ * - Domain entity with Zod validation
12
+ * - Repository port/adapter pattern
13
+ * - Use cases for business logic
14
+ * - oRPC router for API endpoints
15
+ */
16
+ export const kuckitModule = defineKuckitModule<ItemsModuleConfig>({
17
+ id: 'items',
18
+ displayName: 'Items',
19
+ description: 'Example items module for CRUD operations',
20
+ version: '0.1.0',
21
+
22
+ async register(ctx: KuckitModuleContext<ItemsModuleConfig>) {
23
+ const { container } = ctx
24
+
25
+ // Register schema for migrations
26
+ ctx.registerSchema('items', itemsTable)
27
+
28
+ // Register repository
29
+ container.register({
30
+ itemRepository: asFunction(({ db }) => makeItemRepository(db)).scoped(),
31
+ })
32
+ },
33
+
34
+ registerApi(ctx) {
35
+ ctx.addApiRegistration({
36
+ type: 'rpc-router',
37
+ name: 'items',
38
+ router: itemsRouter,
39
+ })
40
+ },
41
+
42
+ async onBootstrap(ctx: KuckitModuleContext<ItemsModuleConfig>) {
43
+ const { container } = ctx
44
+ const logger = container.resolve('logger')
45
+ logger.info('Items module initialized')
46
+ },
47
+
48
+ async onShutdown(ctx: KuckitModuleContext<ItemsModuleConfig>) {
49
+ const { container } = ctx
50
+ const logger = container.resolve('logger')
51
+ logger.info('Items module shutting down')
52
+ },
53
+ })
@@ -0,0 +1,13 @@
1
+ import type { Item, CreateItemInput, UpdateItemInput } from '../domain/item.entity'
2
+
3
+ /**
4
+ * Item repository port interface
5
+ * Defines the contract for item persistence operations
6
+ */
7
+ export interface ItemRepository {
8
+ findById(id: string): Promise<Item | null>
9
+ findByUserId(userId: string): Promise<Item[]>
10
+ create(input: CreateItemInput & { id: string; userId: string }): Promise<Item>
11
+ update(input: UpdateItemInput): Promise<Item | null>
12
+ delete(id: string): Promise<boolean>
13
+ }