kitcn 0.0.1 → 0.12.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 (93) hide show
  1. package/bin/intent.js +3 -0
  2. package/dist/aggregate/index.d.ts +388 -0
  3. package/dist/aggregate/index.js +37 -0
  4. package/dist/api-entry-BckXqaLb.js +66 -0
  5. package/dist/auth/client/index.d.ts +37 -0
  6. package/dist/auth/client/index.js +217 -0
  7. package/dist/auth/config/index.d.ts +45 -0
  8. package/dist/auth/config/index.js +24 -0
  9. package/dist/auth/generated/index.d.ts +2 -0
  10. package/dist/auth/generated/index.js +3 -0
  11. package/dist/auth/http/index.d.ts +64 -0
  12. package/dist/auth/http/index.js +461 -0
  13. package/dist/auth/index.d.ts +221 -0
  14. package/dist/auth/index.js +1398 -0
  15. package/dist/auth/nextjs/index.d.ts +50 -0
  16. package/dist/auth/nextjs/index.js +81 -0
  17. package/dist/auth-store-Cljlmdmi.js +197 -0
  18. package/dist/builder-CBdG5W6A.js +1974 -0
  19. package/dist/caller-factory-cTXNvYdz.js +216 -0
  20. package/dist/cli.mjs +13255 -0
  21. package/dist/codegen-lF80HSWu.mjs +3416 -0
  22. package/dist/context-utils-HPC5nXzx.d.ts +17 -0
  23. package/dist/create-schema-odyF4kCy.js +156 -0
  24. package/dist/create-schema-orm-DOyiNDCx.js +246 -0
  25. package/dist/crpc/index.d.ts +105 -0
  26. package/dist/crpc/index.js +169 -0
  27. package/dist/customFunctions-C0voKmtx.js +144 -0
  28. package/dist/error-BZEnI7Sq.js +41 -0
  29. package/dist/generated-contract-disabled-Cih4eITO.js +50 -0
  30. package/dist/generated-contract-disabled-D-sOFy92.d.ts +354 -0
  31. package/dist/http-types-DqJubRPJ.d.ts +292 -0
  32. package/dist/meta-utils-0Pu0Nrap.js +117 -0
  33. package/dist/middleware-BUybuv9n.d.ts +34 -0
  34. package/dist/middleware-C2qTZ3V7.js +84 -0
  35. package/dist/orm/index.d.ts +17 -0
  36. package/dist/orm/index.js +10713 -0
  37. package/dist/plugins/index.d.ts +2 -0
  38. package/dist/plugins/index.js +3 -0
  39. package/dist/procedure-caller-DtxLmGwA.d.ts +1467 -0
  40. package/dist/procedure-caller-MWcxhQDv.js +349 -0
  41. package/dist/query-context-B8o6-8kC.js +1518 -0
  42. package/dist/query-context-CFZqIvD7.d.ts +42 -0
  43. package/dist/query-options-Dw7cOyXl.js +121 -0
  44. package/dist/ratelimit/index.d.ts +269 -0
  45. package/dist/ratelimit/index.js +856 -0
  46. package/dist/ratelimit/react/index.d.ts +76 -0
  47. package/dist/ratelimit/react/index.js +183 -0
  48. package/dist/react/index.d.ts +1284 -0
  49. package/dist/react/index.js +2526 -0
  50. package/dist/rsc/index.d.ts +276 -0
  51. package/dist/rsc/index.js +233 -0
  52. package/dist/runtime-CtvJPkur.js +2453 -0
  53. package/dist/server/index.d.ts +5 -0
  54. package/dist/server/index.js +6 -0
  55. package/dist/solid/index.d.ts +1221 -0
  56. package/dist/solid/index.js +2940 -0
  57. package/dist/transformer-DtDhR3Lc.js +194 -0
  58. package/dist/types-BTb_4BaU.d.ts +42 -0
  59. package/dist/types-BiJE7qxR.d.ts +4 -0
  60. package/dist/types-DEJpkIhw.d.ts +88 -0
  61. package/dist/types-HhO_R6pd.d.ts +213 -0
  62. package/dist/validators-B7oIJCAp.js +279 -0
  63. package/dist/validators-vzRKjBJC.d.ts +88 -0
  64. package/dist/watcher.mjs +96 -0
  65. package/dist/where-clause-compiler-DdjN63Io.d.ts +4756 -0
  66. package/package.json +107 -35
  67. package/skills/convex/SKILL.md +486 -0
  68. package/skills/convex/references/features/aggregates.md +353 -0
  69. package/skills/convex/references/features/auth-admin.md +446 -0
  70. package/skills/convex/references/features/auth-organizations.md +1141 -0
  71. package/skills/convex/references/features/auth-polar.md +579 -0
  72. package/skills/convex/references/features/auth.md +470 -0
  73. package/skills/convex/references/features/create-plugins.md +153 -0
  74. package/skills/convex/references/features/http.md +676 -0
  75. package/skills/convex/references/features/migrations.md +162 -0
  76. package/skills/convex/references/features/orm.md +1166 -0
  77. package/skills/convex/references/features/react.md +657 -0
  78. package/skills/convex/references/features/scheduling.md +267 -0
  79. package/skills/convex/references/features/testing.md +209 -0
  80. package/skills/convex/references/setup/auth.md +501 -0
  81. package/skills/convex/references/setup/biome.md +190 -0
  82. package/skills/convex/references/setup/doc-guidelines.md +145 -0
  83. package/skills/convex/references/setup/index.md +759 -0
  84. package/skills/convex/references/setup/next.md +116 -0
  85. package/skills/convex/references/setup/react.md +175 -0
  86. package/skills/convex/references/setup/server.md +473 -0
  87. package/skills/convex/references/setup/start.md +67 -0
  88. package/LICENSE +0 -21
  89. package/README.md +0 -0
  90. package/dist/index.d.mts +0 -5
  91. package/dist/index.d.mts.map +0 -1
  92. package/dist/index.mjs +0 -6
  93. package/dist/index.mjs.map +0 -1
package/package.json CHANGED
@@ -1,56 +1,128 @@
1
1
  {
2
2
  "name": "kitcn",
3
- "version": "0.0.1",
4
- "description": "",
5
- "keywords": [],
6
- "bugs": {
7
- "url": "https://github.com/udecode/TEMPLATE/issues"
8
- },
3
+ "version": "0.12.0",
4
+ "description": "kitcn - React Query integration and CLI tools for Convex",
5
+ "keywords": [
6
+ "convex",
7
+ "react-query",
8
+ "tanstack-query",
9
+ "tanstack-intent",
10
+ "codegen",
11
+ "cli",
12
+ "agent-skills",
13
+ "intent"
14
+ ],
15
+ "homepage": "https://kitcn.dev",
9
16
  "repository": {
10
17
  "type": "git",
11
- "url": "https://github.com/udecode/TEMPLATE.git"
18
+ "url": "https://github.com/udecode/kitcn.git"
12
19
  },
13
- "license": "MIT",
14
20
  "sideEffects": false,
21
+ "type": "module",
15
22
  "exports": {
16
- ".": "./dist/index.mjs",
23
+ "./aggregate": "./dist/aggregate/index.js",
24
+ "./auth": "./dist/auth/index.js",
25
+ "./auth/client": "./dist/auth/client/index.js",
26
+ "./auth/config": "./dist/auth/config/index.js",
27
+ "./auth/generated": "./dist/auth/generated/index.js",
28
+ "./auth/http": "./dist/auth/http/index.js",
29
+ "./auth/nextjs": "./dist/auth/nextjs/index.js",
30
+ "./crpc": "./dist/crpc/index.js",
31
+ "./orm": "./dist/orm/index.js",
32
+ "./plugins": "./dist/plugins/index.js",
33
+ "./ratelimit": "./dist/ratelimit/index.js",
34
+ "./ratelimit/react": "./dist/ratelimit/react/index.js",
35
+ "./react": "./dist/react/index.js",
36
+ "./rsc": "./dist/rsc/index.js",
37
+ "./server": "./dist/server/index.js",
38
+ "./solid": "./dist/solid/index.js",
17
39
  "./package.json": "./package.json"
18
40
  },
19
- "main": "./dist/index.mjs",
20
- "module": "./dist/index.mjs",
21
- "types": "./dist/index.d.mts",
41
+ "bin": {
42
+ "kitcn": "./dist/cli.mjs",
43
+ "intent": "./bin/intent.js"
44
+ },
22
45
  "files": [
23
- "dist"
46
+ "dist",
47
+ "skills",
48
+ "bin",
49
+ "!skills/_artifacts"
24
50
  ],
25
51
  "scripts": {
26
- "brl": "sh tooling/brl.sh",
27
- "build": "tsdown --config tooling/tsdown.config.ts",
28
- "build:watch": "tsdown --config tooling/tsdown.config.ts --watch",
29
- "postinstall": "bunx skiller apply",
30
- "lint": "biome check && eslint",
31
- "lint:fix": "biome check --write",
32
- "release": "bun run build && bun changeset publish",
52
+ "build": "NODE_OPTIONS=\"--require ../../tooling/node-styletext-patch.cjs\" tsdown",
53
+ "build:watch": "NODE_OPTIONS=\"--require ../../tooling/node-styletext-patch.cjs\" tsdown --watch",
54
+ "prepublishOnly": "bun run build",
33
55
  "typecheck": "tsc --noEmit",
34
- "typecheck:watch": "tsc --noEmit --watch"
56
+ "typecheck:types": "tsc --project ../../convex/test-types/tsconfig.json"
57
+ },
58
+ "dependencies": {
59
+ "@babel/parser": "^7.28.4",
60
+ "@clack/prompts": "^0.11.0",
61
+ "@convex-dev/better-auth": "^0.11.1",
62
+ "chokidar": "^5.0.0",
63
+ "common-tags": "^1.8.2",
64
+ "diff": "^8.0.2",
65
+ "dotenv": "^17.3.1",
66
+ "esbuild": "^0.27.3",
67
+ "execa": "^9.6.1",
68
+ "jiti": "^2.6.1",
69
+ "jotai": "^2.18.0",
70
+ "jotai-x": "^2.3.3",
71
+ "picocolors": "^1.1.1",
72
+ "remeda": "^2.33.6",
73
+ "svix": "^1.84.1",
74
+ "type-fest": "^5.4.4"
35
75
  },
36
76
  "devDependencies": {
37
- "@biomejs/biome": "2.3.7",
38
- "@changesets/changelog-github": "^0.5.1",
39
- "@changesets/cli": "2.29.7",
40
77
  "@rollup/plugin-babel": "^6.1.0",
41
- "@types/node": "^24.7.2",
42
- "@typescript-eslint/parser": "^8.47.0",
78
+ "@tanstack/intent": "0.0.23",
79
+ "@tanstack/query-core": "5.90.20",
80
+ "@types/common-tags": "^1.8.4",
43
81
  "babel-plugin-react-compiler": "^1.0.0",
44
- "barrelsby": "^2.8.1",
45
- "eslint": "^9.39.1",
46
- "eslint-plugin-react-hooks": "7.0.1",
47
- "lefthook": "^2.0.0",
48
- "tsdown": "^0.16.6",
49
- "typescript": "5.9.3",
50
- "ultracite": "6.3.6"
51
- },
52
- "packageManager": "bun@1.3.3",
82
+ "rolldown-plugin-solid": "^0.2.1",
83
+ "tsdown": "^0.20.3"
84
+ },
85
+ "peerDependencies": {
86
+ "@tanstack/query-core": ">=5",
87
+ "@tanstack/react-query": ">=5",
88
+ "@tanstack/solid-query": ">=5",
89
+ "better-auth": "1.5.3",
90
+ "convex": ">=1.33",
91
+ "hono": "4.12.9",
92
+ "next": ">=14",
93
+ "react": ">=18",
94
+ "solid-js": ">=1.8",
95
+ "zod": ">=4"
96
+ },
97
+ "peerDependenciesMeta": {
98
+ "@tanstack/react-query": {
99
+ "optional": true
100
+ },
101
+ "@tanstack/solid-query": {
102
+ "optional": true
103
+ },
104
+ "better-auth": {
105
+ "optional": true
106
+ },
107
+ "hono": {
108
+ "optional": true
109
+ },
110
+ "next": {
111
+ "optional": true
112
+ },
113
+ "react": {
114
+ "optional": true
115
+ },
116
+ "solid-js": {
117
+ "optional": true
118
+ }
119
+ },
53
120
  "publishConfig": {
54
121
  "access": "public"
122
+ },
123
+ "intent": {
124
+ "version": 1,
125
+ "repo": "udecode/kitcn",
126
+ "docs": "https://kitcn.dev/docs"
55
127
  }
56
128
  }
@@ -0,0 +1,486 @@
1
+ ---
2
+ name: convex
3
+ description: ALWAYS use this skill when working with convex or kitcn. Covers the common end-to-end feature path using cRPC + ORM + auth + React, with setup/bootstrap and niche depth in references.
4
+ # biome-ignore format: keep sources compact so intent's 500-line validator passes
5
+ sources: [www/content/docs/concepts.mdx, www/content/docs/orm/index.mdx, www/content/docs/orm/schema/relations.mdx, www/content/docs/orm/schema/triggers.mdx, www/content/docs/orm/queries/aggregates.mdx, www/content/docs/orm/queries/pagination.mdx, www/content/docs/server/error-handling.mdx, www/content/docs/server/http.mdx, www/content/docs/server/middlewares.mdx, www/content/docs/server/procedures.mdx, www/content/docs/server/server-side-calls.mdx, www/content/docs/react/queries.mdx, www/content/docs/react/mutations.mdx, www/content/docs/react/infinite-queries.mdx, www/content/docs/auth/client.mdx, www/content/docs/auth/server.mdx]
6
+ # biome-ignore format: mirror blog/spec shape without burning 30 lines
7
+ metadata: { sources: [www/content/docs/concepts.mdx, www/content/docs/orm/index.mdx, www/content/docs/orm/schema/relations.mdx, www/content/docs/orm/schema/triggers.mdx, www/content/docs/orm/queries/aggregates.mdx, www/content/docs/orm/queries/pagination.mdx, www/content/docs/server/error-handling.mdx, www/content/docs/server/http.mdx, www/content/docs/server/middlewares.mdx, www/content/docs/server/procedures.mdx, www/content/docs/server/server-side-calls.mdx, www/content/docs/react/queries.mdx, www/content/docs/react/mutations.mdx, www/content/docs/react/infinite-queries.mdx, www/content/docs/auth/client.mdx, www/content/docs/auth/server.mdx] }
8
+ ---
9
+ # kitcn Core Skill (80% Path)
10
+ Use this file first for everyday feature delivery in an already configured kitcn app.
11
+ - If setup/bootstrap/env/auth wiring or project structure mirroring is missing, use `references/setup/index.md` (then the relevant setup file).
12
+ - If the task is advanced or niche, load only the specific feature reference listed at the end.
13
+ ## Scope
14
+ In scope:
15
+ - Add or update schema tables, indexes, relations, and triggers.
16
+ - Implement cRPC procedures (`query`, `mutation`, `action`, `httpAction`) with runtime auth + rate limits.
17
+ - Implement feature UI with `useCRPC()` + TanStack Query.
18
+ - Add minimal high-value tests for auth, errors, and side effects.
19
+ Out of scope:
20
+ - Greenfield setup/install/env/bootstrap.
21
+ - Full plugin deep-dives (admin/organizations/polar).
22
+ - Internal package-level parity testing.
23
+ ## Skill Contract
24
+ 1. Favor `ctx.orm` for app data access.
25
+ 2. Keep list/read paths bounded and index-aware.
26
+ 3. Use cRPC builders and middleware; avoid raw handler objects for new feature code.
27
+ 4. Use `CRPCError` for expected failures.
28
+ 5. Prefer schema triggers for cross-row invariants, but move invariant maintenance to explicit mutation helpers if trigger execution is unstable (for example init/seed hangs or recursive write paths).
29
+ 6. Keep auth/rate-limit checks server-side.
30
+ 7. **Inter-procedure calls**: `create<Module>Handler(ctx)` in queries/mutations (zero overhead) unless validation is relevant, `create<Module>Caller(ctx)` in actions/HTTP routes. In action context use `caller.actions.*` for action procedures and `caller.schedule.*` for scheduling. Import from `./generated/<module>.runtime`. Never call `ctx.runQuery`/`ctx.runMutation`/`ctx.runAction` directly for module procedures.
31
+ ## Shortcut Mode (tRPC + Drizzle Mental Model)
32
+ Default assumption:
33
+ - cRPC behavior is tRPC-like (builder chain + middleware + TanStack options).
34
+ - ORM behavior is Drizzle-like (schema, relations, `findMany/findFirst`, `insert/update/delete`).
35
+ Only remember these non-parity deltas:
36
+ 1. Procedure input root must be `z.object(...)` (no primitive root args).
37
+ 2. No `z.void()` outputs; omit `.output(...)` for no-value mutations.
38
+ 3. Stacked `.input(...)` calls merge input shapes.
39
+ 4. `.paginated({ limit, item })` must be before `.query()` and auto-adds `input.cursor` + `input.limit`, output `{ page, continueCursor, isDone }`.
40
+ 5. Metadata is codegen’d onto `@convex/api` leaves (`api.namespace.fn.meta`) so never put secrets in `.meta(...)`; chaining `.meta(...)` is shallow merge and supports `defaultMeta`.
41
+ 6. Auth metadata drives client behavior: `auth: "optional"` waits for auth load then runs, `auth: "required"` waits then skips when logged out.
42
+ 7. `ctx.orm` enforces constraints + RLS; `ctx.db` bypasses them.
43
+ 8. Non-paginated `findMany()` must be explicitly sized (`limit`, cursor mode, schema `defaultLimit`, or explicit `allowFullScan`).
44
+ 9. Predicate `where` requires explicit `.withIndex(...)`; no implicit full scan fallback.
45
+ 10. Cursor pagination uses the first `orderBy` field; index that field for stable paging.
46
+ 11. `maxScan` applies to cursor mode only; `allowFullScan` is for non-cursor full-scan opt-in.
47
+ 12. String operators / `columns` projection / many-relation subfilters can run post-fetch; bound result size early.
48
+ 13. Search mode is relevance-ordered and does not support `orderBy`; vector mode has stricter limits (no cursor/offset/top-level where/order).
49
+ 14. Update/delete without `where` throws unless `allowFullScan()`.
50
+ 15. `count()`, `aggregate()`, and `groupBy()` require a matching `aggregateIndex`. Use `groupBy({ by, _count, _sum })` instead of multiple `.count()` calls or `findMany` + manual JS grouping. Every `by` field must be finite-constrained (`eq`/`in`/`isNull`) in `where`. See `references/features/aggregates.md`.
51
+ 16. cRPC React queries are real-time by default (`subscribe: true`); never use `queryClient.invalidateQueries` for these subscribed paths.
52
+ 17. In RSC, `prefetch` hydrates client, `caller` is server-only and not hydrated, `preloadQuery` hydrates but can cause stale split ownership if also rendered client-side.
53
+ 18. Better Auth Next.js shortcut is `convexBetterAuth(...)`; generic server-only shortcut is `createCallerFactory(...)`.
54
+ 19. On the kitcn auth client path, use `createAuthMutations(authClient)` wrappers so logout unsubscribes auth queries before sign out. Raw Convex preset keeps a smaller plain `authClient`.
55
+ 20. **NEVER** use `ctx.runQuery`/`ctx.runMutation`/`ctx.runAction` directly for module-to-module calls. Use `create<Module>Handler(ctx)` or `create<Module>Caller(ctx)` from `convex/functions/generated/<module>.runtime` instead.
56
+ 21. **`create<Module>Handler(ctx)`** — default choice for queries/mutations. Bypasses input validation, middleware, output validation → zero overhead. Query/mutation ctx only. Import from `./generated/<module>.runtime`.
57
+ 22. **`create<Module>Caller(ctx)`** — use in actions and HTTP routes (where handler is unavailable). Goes through validation + middleware. Root caller exposes query+mutation procedures. In `ActionCtx`, action procedures are under `caller.actions.*`; scheduling is under `caller.schedule.now|after|at` with `caller.schedule.cancel(id)`. Import from `./generated/<module>.runtime`. Each caller/handler eagerly loads every procedure in its module (no lazy loading) — split large modules to keep bundles lean.
58
+ 23. API types (`Api`, `ApiInputs`, `ApiOutputs`, `Select`, `Insert`, `TableName`) import from `@convex/api` — no manual `inferApiInputs<typeof api>`.
59
+ 24. HTTP router must export as `httpRouter` (not `appRouter`) for codegen.
60
+ 25. Server wiring imports come from `convex/functions/generated/` directory: `getAuth`, `defineAuth` from `generated/auth`; `initCRPC`, `QueryCtx`, `MutationCtx`, `OrmCtx` from `generated/server`; `create<Module>Caller`, `create<Module>Handler` from `generated/<module>.runtime`. No manual `convex/lib/orm.ts`.
61
+ 26. `defineAuth(() => ({ ...options, triggers }))` replaces split `getAuthOptions` + `authTriggers`. Trigger callbacks are doc-first: `beforeCreate(data)`, `onCreate(doc)`, `onUpdate(newDoc, oldDoc)` — no `ctx` first param.
62
+ 27. Internal auth functions at `internal.generated.*` (not `internal.auth.*`).
63
+ 28. Async mutation batching is the default (codegen wires it). Customize per call: `execute({ batchSize, delayMs })`. Opt into sync: `execute({ mode: 'sync' })` or `defineSchema(..., { defaults: { mutationExecutionMode: 'sync' } })`. Relevant defaults: `mutationBatchSize`, `mutationLeafBatchSize`, `mutationMaxRows`, `mutationScheduleCallCap`.
64
+ 29. Polymorphic unions are schema-first: use `actionType: discriminator({ variants, as? })` in `convexTable(...)`. Query config does not include a `polymorphic` option. Writes stay flat; reads synthesize nested `details` (or custom alias). Use `withVariants: true` to auto-load all `one()` relations on discriminator tables.
65
+ 30. Do not add manual ORM mutation batching loops in app/plugin code by default. Convex runtime batching already handles mutation execution. Prefer set-based deletes/updates over per-row loops. Only add explicit chunking when batching external side effects (for example Resend API calls) or bounded cleanup sweeps.
66
+ ## Directory Boundary (Important)
67
+ This skill is directory-scoped. Do not depend on reading files outside `packages/kitcn/skills/convex/**`.
68
+ Use `references/setup/` when the task needs:
69
+ 1. Project/file structure setup → `setup/index.md` + `setup/server.md`
70
+ 2. Auth bootstrap → `setup/auth.md`
71
+ 3. Client/provider wiring → `setup/react.md`
72
+ 4. Framework-specific setup → `setup/next.md` or `setup/start.md`
73
+ For full template-level recreation: start with `setup/index.md`, then load relevant setup files, then load selected feature refs.
74
+ ## First-Pass Feature Intake (Do This Before Edits)
75
+ Lock these decisions first:
76
+ 1. Auth level per endpoint: `public` / `optionalAuth` / `auth` / `private`.
77
+ 2. Data invariants: what must always be true after writes?
78
+ 3. Query shape: list, detail, relation-loaded, search, or stream composition.
79
+ 4. Pagination mode: offset, cursor, infinite.
80
+ 5. Side effects: trigger vs scheduled function vs inline mutation.
81
+ 6. UI consumption: client hook only, RSC prefetch, or server-only caller.
82
+ 7. Risk paths: unauthorized, forbidden, not found, conflicts, rate limit.
83
+ ## Canonical File Targets
84
+ Typical feature touches:
85
+ - `convex/functions/schema.ts`
86
+ - `convex/functions/<feature>.ts`
87
+ - `convex/lib/crpc.ts` (only if middleware/procedure builder changes)
88
+ - `src/lib/convex/crpc.tsx` (only if cRPC context/meta wiring changes)
89
+ - `src/**` feature UI files
90
+ - `convex/functions/http.ts` or `convex/routers/**` for HTTP endpoints
91
+ - `convex/functions/crons.ts` or scheduled handlers if needed
92
+ ## E2E Build Order (Default)
93
+ 1. Schema + indexes + relations.
94
+ 2. Trigger hooks for cross-row invariants (or explicit mutation-side sync if trigger path is unstable).
95
+ 3. Procedures with strict input/output + auth + rate limits.
96
+ 4. React hooks (query/mutation/infinite) using cRPC options.
97
+ 5. Optional: HTTP route(s), scheduling hooks.
98
+ 6. Tests for auth/error/trigger behavior.
99
+
100
+ ## Core Patterns
101
+
102
+ ### 1) Schema + Relations + Trigger
103
+
104
+ ```ts
105
+ import {
106
+ convexTable,
107
+ defineSchema,
108
+ id,
109
+ integer,
110
+ index,
111
+ text,
112
+ timestamp,
113
+ } from "kitcn/orm";
114
+
115
+ export const project = convexTable(
116
+ "project",
117
+ {
118
+ name: text().notNull(),
119
+ ownerId: id("user").notNull(),
120
+ updatedAt: timestamp()
121
+ .notNull()
122
+ .defaultNow()
123
+ .$onUpdateFn(() => new Date()),
124
+ },
125
+ (t) => [index("ownerId_updatedAt").on(t.ownerId, t.updatedAt)]
126
+ );
127
+
128
+ export const task = convexTable(
129
+ "task",
130
+ {
131
+ projectId: id("project").notNull(),
132
+ title: text().notNull(),
133
+ status: text().notNull().default("open"),
134
+ updatedAt: timestamp()
135
+ .notNull()
136
+ .defaultNow()
137
+ .$onUpdateFn(() => new Date()),
138
+ },
139
+ (t) => [index("projectId_updatedAt").on(t.projectId, t.updatedAt)]
140
+ );
141
+
142
+ export default defineSchema({ project, task })
143
+ .relations((r) => ({
144
+ project: {
145
+ tasks: r.many.task(),
146
+ },
147
+ task: {
148
+ project: r.one.project({ from: r.task.projectId, to: r.project.id }),
149
+ },
150
+ }))
151
+ .triggers({
152
+ task: {
153
+ change: async (change, ctx) => {
154
+ const projectId = change.newDoc?.projectId ?? change.oldDoc?.projectId;
155
+ if (!projectId) return;
156
+ const open = await ctx.orm.query.task.findMany({
157
+ where: { projectId, status: "open" },
158
+ columns: { id: true },
159
+ limit: 500,
160
+ });
161
+ await ctx.orm.update(project).set({ openTaskCount: open.length });
162
+ },
163
+ },
164
+ });
165
+ ```
166
+
167
+ Schema rules that matter:
168
+
169
+ 1. Index fields that power filters/order/search.
170
+ 2. `many()` relation paths need child FK indexes.
171
+ 3. Trigger logic must be bounded and non-recursive.
172
+ 4. Use table defaults for consistent write behavior.
173
+ 5. Keep full ORM/query edge cases in `references/features/orm.md`.
174
+
175
+ ### 2) Procedure Builders + Middleware
176
+
177
+ ```ts
178
+ import { getHeaders } from "kitcn/auth";
179
+ import { CRPCError } from "kitcn/server";
180
+ import { getAuth } from "../functions/generated/auth";
181
+ import { initCRPC } from "../functions/generated/server";
182
+
183
+ const c = initCRPC
184
+ .meta<{
185
+ auth?: "optional" | "required";
186
+ role?: "admin";
187
+ ratelimit?: string;
188
+ }>()
189
+ .create();
190
+
191
+ function requireAuth<T>(user: T | null): T {
192
+ if (!user) {
193
+ throw new CRPCError({ code: "UNAUTHORIZED", message: "Not authenticated" });
194
+ }
195
+ return user;
196
+ }
197
+ export const publicQuery = c.query.meta({ auth: "optional" });
198
+ export const authQuery = c.query
199
+ .meta({ auth: "required" })
200
+ .use(async ({ ctx, next }) => {
201
+ const auth = getAuth(ctx);
202
+ const session = await auth.api.getSession({
203
+ headers: await getHeaders(ctx),
204
+ });
205
+ const user = requireAuth(session?.user ?? null);
206
+ return next({ ctx: { ...ctx, user, userId: user.id } });
207
+ });
208
+ export const authMutation = c.mutation
209
+ .meta({ auth: "optional" })
210
+ .use(async ({ ctx, next }) => {
211
+ const auth = getAuth(ctx);
212
+ const session = await auth.api.getSession({
213
+ headers: await getHeaders(ctx),
214
+ });
215
+ return next({
216
+ ctx: {
217
+ ...ctx,
218
+ user: session?.user ?? null,
219
+ userId: session?.user?.id ?? null,
220
+ },
221
+ });
222
+ });
223
+ ```
224
+
225
+ Builder rules that matter:
226
+
227
+ 1. Build `public`, `optional`, `auth`, and `private` procedure families once in `convex/lib/crpc.ts`.
228
+ 2. `.meta(...)` is client-visible via generated API metadata. Never put secrets there.
229
+ 3. Resolve session/user once in middleware. Do not re-fetch auth state in every procedure.
230
+ 4. Keep deeper auth/runtime edge cases in `references/setup/server.md` and `references/features/auth*.md`.
231
+
232
+ ### 3) Query + Mutation Procedure Template
233
+
234
+ ```ts
235
+ import { z } from "zod";
236
+ import { eq } from "kitcn/orm";
237
+ import { CRPCError } from "kitcn/server";
238
+ import { authMutation, authQuery } from "../lib/crpc";
239
+ import { project } from "./schema";
240
+
241
+ export const listProjects = authQuery
242
+ .paginated({ limit: z.number().min(1).max(50).default(20), item: project })
243
+ .query(async ({ ctx, input }) =>
244
+ ctx.orm.query.project.findMany({
245
+ where: { ownerId: ctx.userId },
246
+ orderBy: { updatedAt: "desc" },
247
+ cursor: input.cursor,
248
+ limit: input.limit,
249
+ })
250
+ );
251
+
252
+ export const renameProject = authMutation
253
+ .input(z.object({ id: z.string(), name: z.string().min(1).max(120) }))
254
+ .mutation(async ({ ctx, input }) => {
255
+ const current = await ctx.orm.query.project.findFirst({
256
+ where: { id: input.id, ownerId: ctx.userId },
257
+ columns: { id: true },
258
+ });
259
+ if (!current) {
260
+ throw new CRPCError({ code: "NOT_FOUND", message: "Project not found" });
261
+ }
262
+ await ctx.orm
263
+ .update(project)
264
+ .set({ name: input.name })
265
+ .where(eq(project.id, current.id));
266
+ return null;
267
+ });
268
+ ```
269
+
270
+ Procedure rules that matter:
271
+
272
+ 1. Root input must be `z.object(...)`.
273
+ 2. Use strict `.input(...)`; add `.output(...)` only when needed.
274
+ 3. Omit `.output(...)` for no-value mutations.
275
+ 4. Use the default mutation rate limit; add `.meta({ ratelimit: ... })` only for named bucket overrides.
276
+ 5. Throw `CRPCError` for expected outcomes.
277
+ 6. Bound every list with `limit`, cursor, or `.paginated(...)`.
278
+ 7. Move advanced query-builder shapes to `references/features/orm.md`.
279
+
280
+ ### 3b) Inter-Procedure Composition
281
+
282
+ Use:
283
+
284
+ 1. `create<Module>Handler(ctx)` in queries/mutations.
285
+ 2. `create<Module>Caller(ctx)` in actions/HTTP routes.
286
+ 3. `caller.actions.*` for action procedures.
287
+ 4. `caller.schedule.*` for scheduled procedures.
288
+ 5. Never `ctx.runQuery` / `ctx.runMutation` / `ctx.runAction` for module procedures.
289
+
290
+ ### 4) Query Modes (Use The Right One)
291
+
292
+ 1. Default to object `where`.
293
+ 2. Use callback `where` only when composition reads better than object form.
294
+ 3. Predicate/filter callbacks require `.withIndex(...)` first plus explicit `limit`/`maxScan`.
295
+ 4. Full-text search uses `search: { index, query, filters }` and does not support `orderBy`.
296
+ 5. Cursor paging is only stable when the `orderBy` field is indexed.
297
+ 6. Advanced modes (`pageByKey`, vector search, pipelines, aggregate indexes) live in `references/features/orm.md`.
298
+
299
+ ### 5) Mutation Patterns (Most Used)
300
+
301
+ 1. Use `.returning(...)` on inserts when caller needs created ids.
302
+ 2. Every update/delete path gets an explicit `where(...)`.
303
+ 3. Clear optional columns with `unsetToken`.
304
+ 4. Async mutation execution is the default; use `.execute({ mode: "sync" })` only when atomic all-at-once behavior is required.
305
+ 5. Prefer set-based deletes/updates. Add chunking only for external side effects or bounded cleanups.
306
+ 6. Upsert, conflict handling, mutation batching, and schema extension edge cases live in `references/features/orm.md`.
307
+
308
+ ### 6) Error Model
309
+
310
+ Use this map consistently:
311
+
312
+ 1. `BAD_REQUEST`: invalid input or business precondition.
313
+ 2. `UNAUTHORIZED`: no session.
314
+ 3. `FORBIDDEN`: session exists, permission missing.
315
+ 4. `NOT_FOUND`: missing or inaccessible resource.
316
+ 5. `CONFLICT`: duplicate or conflicting write.
317
+ 6. `TOO_MANY_REQUESTS`: rate limit.
318
+ 7. `INTERNAL_SERVER_ERROR`: unexpected failures only.
319
+
320
+ Required tests:
321
+
322
+ 1. unauthenticated rejection
323
+ 2. permission rejection when relevant
324
+ 3. missing resource path
325
+ 4. conflict path when relevant
326
+ 5. rate-limited write path when relevant
327
+
328
+ ### 7) React Query Integration
329
+
330
+ Preconditions (must be true before writing/using `useCRPC()` code paths):
331
+
332
+ 1. Generated imports exist (`@convex/api`) from setup bootstrap.
333
+ 2. Provider chain is mounted (`CRPCProvider` inside QueryClient + Convex provider flow).
334
+ 3. If bootstrap/provider prerequisites are missing, stop feature work and finish `references/setup/` first.
335
+ 4. Backend state is project-local in `.convex/`, not `~/.convex`.
336
+
337
+ `useCRPC()` pattern: `const crpc = useCRPC(); const projects = useQuery(crpc.project.listProjects.queryOptions({ cursor: null, limit: 20 })); const createProject = useMutation(crpc.project.createProject.mutationOptions());`
338
+
339
+ Key client defaults/deltas:
340
+
341
+ 1. Queries are real-time by default (`subscribe: true`).
342
+ 2. Never use `queryClient.invalidateQueries` for subscribed cRPC query paths.
343
+ 3. Use `{ subscribe: false }` only for one-time fetches; refresh those with explicit `refetch`/`fetchQuery`.
344
+ 4. Use `skipUnauth: true` to avoid unauthorized fetch churn.
345
+ 5. For pagination, use `useInfiniteQuery` from `kitcn/react`.
346
+ 6. Prefer typed `queryKey(...)` helpers for cache read/write/fetch ops instead of manual keys.
347
+ 7. For kitcn auth flows, prefer `createAuthMutations(...)` wrappers (not raw auth client calls) to avoid logout race errors. Raw Convex preset keeps the plain auth client path.
348
+ 8. Full client/RSC depth lives in `references/features/react.md`.
349
+
350
+ ### 8) RSC Patterns (Next.js)
351
+
352
+ Choose one per use case:
353
+
354
+ 1. `prefetch(...)` (preferred): non-blocking, hydrated, client owns data.
355
+ 2. `caller.*`: blocking server-only logic (redirects/auth checks), not hydrated.
356
+ 3. `preloadQuery(...)`: blocking + hydrated when server needs data immediately.
357
+
358
+ Do not render `preloadQuery` result on server and again on client for the same data path.
359
+
360
+ 1. `HydrateClient` must wrap all client components that consume prefetched queries.
361
+ 2. Next.js-specific setup and deeper hydration tradeoffs live in `references/setup/next.md` and `references/features/react.md`.
362
+
363
+ ### 9) HTTP Route Pattern (When Feature Needs REST/Webhooks)
364
+
365
+ ```ts
366
+ import { createTaskCaller } from "../functions/generated/task.runtime";
367
+
368
+ export const createTaskRoute = authRoute
369
+ .post("/api/projects/:projectId/tasks")
370
+ .params(z.object({ projectId: z.string() }))
371
+ .input(z.object({ title: z.string().min(1) }))
372
+ .output(z.object({ id: z.string() }))
373
+ .mutation(async ({ ctx, params, input }) => {
374
+ const caller = createTaskCaller(ctx);
375
+ const id = await caller.createFromHttp({
376
+ projectId: params.projectId,
377
+ title: input.title,
378
+ userId: ctx.userId,
379
+ });
380
+ return { id };
381
+ });
382
+ ```
383
+
384
+ HTTP-specific rules:
385
+
386
+ 1. Use `z.coerce.*` for search params.
387
+ 2. Keep auth and permission checks in middleware/procedure.
388
+ 3. Apply rate limits to public/heavy endpoints.
389
+ 4. Validate webhook signatures before any side effects.
390
+ 5. Use `publicRoute` / `authRoute` / `optionalAuthRoute` builders from `convex/lib/crpc.ts`.
391
+ 6. Compose endpoints with `router(...)` for feature-level HTTP grouping.
392
+ 7. Client calls must pass path/query args as `{ params, searchParams }`; query values are strings.
393
+ 8. Webhooks, streaming, and Hono-specific patterns live in `references/features/http.md`.
394
+
395
+ ### 10) Scheduling Pattern (If Needed)
396
+
397
+ Example: `const caller = createTaskCaller(ctx); await caller.schedule.now.sendTaskCreated({ taskId: created.id, userId: ctx.userId }); await caller.schedule.at(input.sendAt).sendReminder({ taskId: input.taskId, userId: ctx.userId });`
398
+
399
+ Scheduling rules:
400
+
401
+ 1. Auth context is not propagated; pass user/org IDs explicitly.
402
+ 2. Mutation scheduling is atomic with the mutation transaction.
403
+ 3. Store returned job IDs when cancellation is required.
404
+ 4. Scheduling inside actions is not atomic with action failure.
405
+ 5. Cron schedules run in UTC.
406
+ 6. Use `ctx.scheduler.*` directly only when you must schedule non-procedure `internal.*` functions.
407
+ 7. Cron expressions and operational details live in `references/features/scheduling.md`.
408
+
409
+ ### 11) Testing Baseline (High Signal)
410
+
411
+ Minimum feature test set:
412
+
413
+ 1. happy path query/mutation
414
+ 2. unauthenticated rejection (`UNAUTHORIZED`)
415
+ 3. permission/ownership rejection (`FORBIDDEN` where relevant)
416
+ 4. missing resource (`NOT_FOUND`)
417
+ 5. trigger side effect assertion
418
+ 6. scheduler assertion if feature schedules work
419
+ 7. not-found checks should use real IDs or non-ID lookup keys (slug/name/email), not synthetic IDs
420
+ 8. Full testing recipes live in `references/features/testing.md`.
421
+ 9. If Convex bootstrap blocks integration tests, extract pure guards/helpers and keep one smoke integration test once bootstrap works.
422
+
423
+ ## Performance + Safety Checklist
424
+
425
+ Before calling a feature done:
426
+
427
+ 1. Every list query is bounded (`limit`/cursor).
428
+ 2. Filters/order align with indexes.
429
+ 3. Expensive post-fetch logic uses pre-narrowed index path.
430
+ 4. Mutations use targeted `where` and avoid accidental full scans.
431
+ 5. Trigger logic is bounded, idempotent, and avoids ping-pong loops.
432
+ 6. Error codes are explicit and intentional.
433
+ 7. User-facing writes have rate-limit metadata.
434
+ 8. Tests cover auth + not-found + side effects.
435
+ 9. `ctx.db` is not used on paths that rely on ORM constraints/RLS.
436
+ 10. Paginated endpoints use `.paginated(...)` + ORM cursor flow (not ad-hoc wrappers).
437
+ 11. For any predicate/full-scan-like path, `.withIndex(...)` + bound (`limit`/`maxScan`) is explicit.
438
+ 12. NEVER use `@ts-nocheck`, no global lint-rule downgrades, no unresolved lint warnings in touched files.
439
+
440
+ ## Common Mistakes (And Fixes)
441
+
442
+ | Mistake | Correct pattern |
443
+ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
444
+ | Raw Convex handler for new feature procedures | cRPC builders (`publicQuery`, `authMutation`, etc.) |
445
+ | Write-time side effects duplicated across mutations | Schema trigger, or one centralized mutation-side sync helper when trigger path is unsafe |
446
+ | Missing bounds on list/search | Add `limit` + cursor/pagination |
447
+ | `orderBy` written as array objects | Use object form: `orderBy: { updatedAt: "desc" }` |
448
+ | Using `ctx.db` for policy-sensitive reads | Use `ctx.orm` (RLS/constraints path) |
449
+ | Throwing generic `Error` for expected outcomes | Throw `CRPCError` with explicit code |
450
+ | Infinite list with TanStack native hook directly | Use `useInfiniteQuery` from `kitcn/react` |
451
+ | Primitive root input (`z.string()`) | Use root `z.object(...)` input schema |
452
+ | Returning nothing with `z.void()` | Omit explicit output |
453
+ | Manual pagination wrappers for infinite endpoints | Use `.paginated({ limit, item })` |
454
+ | Synthetic Convex IDs in tests (`"missing-id"`) | Use inserted IDs or semantic lookup keys |
455
+ | Aggregates disabled but helper/config still present | Remove aggregate helper + `defineTriggers` handlers + app config together |
456
+ | Putting secrets in `.meta(...)` | Keep metadata non-sensitive (client-visible) |
457
+ | Using `ctx.runQuery`/`ctx.runMutation`/`ctx.runAction` directly | Use `create<Module>Handler(ctx)` in queries/mutations, `create<Module>Caller(ctx)` in actions/HTTP with `caller.actions.*` / `caller.schedule.*` (from `generated/<module>.runtime`) |
458
+ | Using `createCaller` in query/mutation context | Use `create<Module>Handler(ctx)` — zero overhead, bypasses redundant validation |
459
+ | Adding `// @ts-nocheck` to unblock compile | NEVER do this; fix the underlying types using canonical patterns in `references/setup/` |
460
+ | Relaxing lint rules to pass checks | Keep baseline lint config; fix code-level warnings/errors instead |
461
+
462
+ ## Reference Escalation Map (Load Only If Needed)
463
+
464
+ **Setup (once per project):**
465
+
466
+ - `references/setup/index.md`: bootstrap, env, decision intake, gates, checklist, troubleshooting
467
+ - `references/setup/server.md`: core backend (schema, ORM, cRPC) + optional module gates
468
+ - `references/setup/auth.md`: auth core bootstrap + plugin setup
469
+ - `references/setup/react.md`: client core (QueryClient, provider, cRPC context)
470
+ - `references/setup/next.md`: Next.js App Router setup
471
+ - `references/setup/start.md`: TanStack Start setup
472
+ - `references/setup/doc-guidelines.md`: skill/docs sync contract
473
+
474
+ **Features (per session, self-contained):**
475
+
476
+ - `references/features/orm.md`: full ORM API, constraints, RLS, advanced mutations, filtering/search/composition/pagination
477
+ - `references/features/react.md`: full client, RSC, hydration, error handling matrix
478
+ - `references/features/http.md`: typed REST routes, webhooks, streaming
479
+ - `references/features/scheduling.md`: cron + delayed job patterns
480
+ - `references/features/testing.md`: deeper testing scenarios
481
+ - `references/features/aggregates.md`: aggregate component patterns
482
+ - `references/features/migrations.md`: built-in online data migrations (defineMigration, CLI, deploy, drift). Load when: task involves data backfills, optional→required field hardening, field renames/removals, type narrowing, or `kitcn migrate` CLI commands. Skip for backward-compatible changes (new optional fields, new tables, code-level defaults).
483
+ - `references/features/create-plugins.md`: canonical plugin authoring patterns (split package entries, token config, scaffold/lockfile/CLI manifest rules). Load when: creating or refactoring plugins.
484
+ - `references/features/auth.md`: full Better Auth core flow
485
+ - `references/features/auth-admin.md`: admin plugin details
486
+ - `references/features/auth-organizations.md`: org/multi-tenant plugin details