kitcn 0.0.1 → 0.12.1

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 +13264 -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 -34
  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 +761 -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
@@ -0,0 +1,501 @@
1
+ ## 6. Auth Core (Better Auth)
2
+
3
+ Feature gate: only apply this section if auth is enabled.
4
+
5
+ ### 6.1 Install auth with CLI
6
+
7
+ If kitcn is not bootstrapped yet, start there first:
8
+
9
+ ```bash
10
+ bunx kitcn init -t next --yes
11
+ ```
12
+
13
+ Use `bunx kitcn init --yes` instead for in-place adoption of the
14
+ current supported app.
15
+
16
+ Then install auth:
17
+
18
+ ```bash
19
+ bunx kitcn add auth --yes
20
+ ```
21
+
22
+ Local Convex rule:
23
+
24
+ 1. `add auth --yes` installs the auth scaffold and finishes the first local auth bootstrap in one pass.
25
+ 2. `kitcn dev` is the long-running local runtime; later edits to `convex/.env` auto-sync while it is running.
26
+ 3. `kitcn env push` stays for `--prod`, `--rotate`, or explicit repair against an already active deployment.
27
+
28
+ ### 6.2 Auth config provider
29
+
30
+ **Create:** `convex/functions/auth.config.ts`
31
+
32
+ ```ts
33
+ import { getAuthConfigProvider } from "kitcn/auth/config";
34
+ import type { AuthConfig } from "convex/server";
35
+ import { getEnv } from "../lib/get-env";
36
+
37
+ export default {
38
+ providers: [
39
+ getEnv().JWKS
40
+ ? getAuthConfigProvider({ jwks: getEnv().JWKS })
41
+ : getAuthConfigProvider(),
42
+ ],
43
+ } satisfies AuthConfig;
44
+ ```
45
+
46
+ Treat generated auth secrets as owned by the CLI flow. Do not manually set
47
+ `BETTER_AUTH_SECRET` in setup/simulation unless explicitly requested.
48
+ Malformed `JWKS` values can fail Convex module analysis during push/codegen.
49
+
50
+ ### 6.3 Define auth contract
51
+
52
+ **Create:** `<functionsDir>/auth.ts`
53
+
54
+ `functionsDir` comes from `convex.json.functions` (default: `convex`).
55
+ Scaffolded kitcn apps use `convex/functions/auth.ts`.
56
+
57
+ ```ts
58
+ import { convex } from "kitcn/auth";
59
+ import { getEnv } from "../lib/get-env";
60
+ import authConfig from "./auth.config";
61
+ import { defineAuth } from "./generated/auth";
62
+
63
+ export default defineAuth(() => ({
64
+ emailAndPassword: {
65
+ enabled: true,
66
+ },
67
+ baseURL: getEnv().SITE_URL,
68
+ plugins: [
69
+ convex({
70
+ authConfig,
71
+ jwks: getEnv().JWKS,
72
+ }),
73
+ ],
74
+ session: {
75
+ expiresIn: 60 * 60 * 24 * 30,
76
+ updateAge: 60 * 60 * 24 * 15,
77
+ },
78
+ telemetry: { enabled: false },
79
+ trustedOrigins: [getEnv().SITE_URL],
80
+ }));
81
+ ```
82
+
83
+ Canonical rule:
84
+
85
+ 1. `bunx kitcn init --yes`, `bunx kitcn dev`, and `bunx kitcn add auth --yes` all drive generation of `convex/functions/generated/` when they own the local Convex flow.
86
+ 2. `auth.ts` default-exports `defineAuth(() => ({ ...options, triggers }))` imported from `./generated/auth`.
87
+ 3. Import runtime auth contract (`getAuth`, `authClient`, CRUD/triggers, `auth`) from `<functionsDir>/generated/auth`.
88
+ 4. If `auth.ts` is missing or incomplete, codegen still succeeds and generated runtime exports `authEnabled = false` with setup guidance at call time.
89
+
90
+ Do not manually create `authClient`, `createApi` exports, or static `auth` in `auth.ts`.
91
+
92
+ ### 6.3.1 User session query module
93
+
94
+ Ordering note:
95
+
96
+ 1. This module intentionally uses `publicQuery` + `getAuth(ctx)` so it works before Section 6.9 upgrades cRPC auth builders.
97
+
98
+ **Create:** `convex/functions/user.ts`
99
+
100
+ ```ts
101
+ import { z } from "zod";
102
+ import { getHeaders } from "kitcn/auth";
103
+
104
+ import { getAuth } from "./generated/auth";
105
+ import { publicQuery } from "../lib/crpc";
106
+
107
+ export const getSessionUser = publicQuery
108
+ .output(
109
+ z.union([
110
+ z.object({
111
+ id: z.string(),
112
+ image: z.string().nullish(),
113
+ isAdmin: z.boolean(),
114
+ name: z.string().optional(),
115
+ plan: z.string().optional(),
116
+ }),
117
+ z.null(),
118
+ ])
119
+ )
120
+ .query(async ({ ctx }) => {
121
+ const auth = getAuth(ctx);
122
+ const session = await auth.api.getSession({
123
+ headers: await getHeaders(ctx),
124
+ });
125
+ const user = session?.user;
126
+ if (!user) {
127
+ return null;
128
+ }
129
+
130
+ return {
131
+ id: user.id,
132
+ image: user.image,
133
+ isAdmin: user.isAdmin ?? false,
134
+ name: user.name,
135
+ plan: user.plan,
136
+ };
137
+ });
138
+
139
+ export const getIsAuthenticated = publicQuery
140
+ .output(z.boolean())
141
+ .query(async ({ ctx }) => !!(await ctx.auth.getUserIdentity()));
142
+ ```
143
+
144
+ ### 6.3.2 Shared auth type contract
145
+
146
+ **Create:** `convex/shared/auth-shared.ts`
147
+
148
+ ```ts
149
+ import type { getAuth } from "../functions/generated/auth";
150
+ import type { Select } from "./api";
151
+
152
+ export type Auth = ReturnType<typeof getAuth>;
153
+
154
+ export type SessionUser = Select<"user"> & {
155
+ isAdmin: boolean;
156
+ session: Select<"session">;
157
+ impersonatedBy?: string | null;
158
+ plan?: "premium" | "team";
159
+ };
160
+ ```
161
+
162
+ ### 6.4 Define auth tables in schema
163
+
164
+ If you used the kitcn scaffold, install auth once with:
165
+
166
+ ```bash
167
+ bunx kitcn add auth --yes
168
+ ```
169
+
170
+ After changing plugins or auth fields in `<functionsDir>/auth.ts`, refresh only
171
+ the auth-owned schema blocks with:
172
+
173
+ ```bash
174
+ bunx kitcn add auth --schema --yes
175
+ ```
176
+
177
+ Use the raw Convex preset only when the app stays on the plain Convex auth
178
+ path:
179
+
180
+ ```bash
181
+ bunx kitcn add auth --preset convex --yes
182
+ ```
183
+
184
+ That raw Convex path refreshes `authSchema.ts` and `schema.ts` together. It
185
+ assumes the raw Convex app is already initialized and does not support
186
+ `--schema`.
187
+
188
+ If you used section 5.1's schema template, these tables already exist.
189
+ Otherwise add:
190
+
191
+ - `user`
192
+ - `session`
193
+ - `account`
194
+ - `verification`
195
+ - `jwks`
196
+
197
+ Keep all auth reads/writes on ORM table definitions in `convex/functions/schema.ts`.
198
+
199
+ ### 6.5 Register auth HTTP routes
200
+
201
+ Use `kitcn/auth/http` for `authMiddleware` or `registerRoutes`.
202
+ It auto-installs the Convex-safe `MessageChannel` polyfill, so no manual `http-polyfills.ts` file is needed.
203
+
204
+ **Create:** `convex/functions/http.ts`
205
+
206
+ Bootstrap note:
207
+
208
+ 1. `http.ts` is parsed during startup/codegen.
209
+ 2. Keep imports static (no lazy imports in Convex code).
210
+ 3. If `_generated/*` modules are missing, run `bunx kitcn dev` first, then continue.
211
+
212
+ cRPC + Hono route shape:
213
+
214
+ ```ts
215
+ import { authMiddleware } from "kitcn/auth/http";
216
+ import { createHttpRouter } from "kitcn/server";
217
+ import { Hono } from "hono";
218
+ import { cors } from "hono/cors";
219
+ import { getEnv } from "../lib/get-env";
220
+
221
+ import { router } from "../lib/crpc";
222
+ import { getAuth } from "./generated/auth";
223
+
224
+ const app = new Hono();
225
+
226
+ app.use(
227
+ "/api/*",
228
+ cors({
229
+ origin: getEnv().SITE_URL,
230
+ allowHeaders: ["Content-Type", "Authorization", "Better-Auth-Cookie"],
231
+ exposeHeaders: ["Set-Better-Auth-Cookie"],
232
+ credentials: true,
233
+ })
234
+ );
235
+
236
+ app.use(authMiddleware(getAuth));
237
+
238
+ export const httpRouter = router({
239
+ // register routers here
240
+ });
241
+
242
+ export default createHttpRouter(app, httpRouter);
243
+ ```
244
+
245
+ ### 6.6 Sync env and JWKS
246
+
247
+ `convex/.env` comes from base setup. Keep `SITE_URL` and any provider
248
+ credentials current there. For the normal local path, `SITE_URL` should stay on
249
+ `http://localhost:3000`.
250
+
251
+ Typical local values:
252
+
253
+ ```bash
254
+ SITE_URL=http://localhost:3000
255
+ GOOGLE_CLIENT_ID=...
256
+ GOOGLE_CLIENT_SECRET=...
257
+ ```
258
+
259
+ Local Convex:
260
+
261
+ ```bash
262
+ bunx kitcn dev
263
+ ```
264
+
265
+ `kitcn init --yes`, `kitcn dev`, and `kitcn add auth --yes`
266
+ already handle the first local auth bootstrap pass when they own the flow.
267
+ While `kitcn dev` is running, later edits to `convex/.env` auto-sync.
268
+
269
+ Repair / remote sync:
270
+
271
+ ```bash
272
+ bunx kitcn env push
273
+ ```
274
+
275
+ Use this for `--prod` or explicit repair against an already active deployment.
276
+
277
+ Rotate later:
278
+
279
+ ```bash
280
+ bunx kitcn env push --rotate
281
+ ```
282
+
283
+ ### 6.7 Production bootstrap notes
284
+
285
+ First prod deploy requires JWKS initialization:
286
+
287
+ ```bash
288
+ bunx convex deploy --prod
289
+ bunx kitcn env push --prod
290
+ ```
291
+
292
+ ### 6.9 Upgrade `convex/lib/crpc.ts` to auth-aware builders (only after Section 11.2 passes)
293
+
294
+ After non-auth baseline is green, replace `convex/lib/crpc.ts` with this auth-aware variant:
295
+
296
+ ```ts
297
+ import { getHeaders } from "kitcn/auth";
298
+ import { CRPCError } from "kitcn/server";
299
+
300
+ import { getAuth } from "../functions/generated/auth";
301
+ import { initCRPC } from "../functions/generated/server";
302
+
303
+ const c = initCRPC
304
+ .meta<{
305
+ auth?: "optional" | "required";
306
+ role?: "admin";
307
+ ratelimit?: string;
308
+ }>()
309
+ .create();
310
+
311
+ const roleMiddleware = c.middleware(({ meta, ctx, next }) => {
312
+ if (meta.role !== "admin") return next({ ctx });
313
+
314
+ const user = (ctx as { user?: { isAdmin?: boolean } }).user;
315
+ if (!user?.isAdmin) {
316
+ throw new CRPCError({
317
+ code: "FORBIDDEN",
318
+ message: "Admin access required",
319
+ });
320
+ }
321
+
322
+ return next({ ctx });
323
+ });
324
+
325
+ function requireAuth<T>(user: T | null): T {
326
+ if (!user) {
327
+ throw new CRPCError({ code: "UNAUTHORIZED", message: "Not authenticated" });
328
+ }
329
+ return user;
330
+ }
331
+
332
+ export const publicQuery = c.query.meta({ auth: "optional" });
333
+ export const publicAction = c.action;
334
+ export const publicMutation = c.mutation;
335
+
336
+ export const privateQuery = c.query.internal();
337
+ export const privateMutation = c.mutation.internal();
338
+ export const privateAction = c.action.internal();
339
+
340
+ export const optionalAuthQuery = c.query
341
+ .meta({ auth: "optional" })
342
+ .use(async ({ ctx, next }) => {
343
+ const auth = getAuth(ctx);
344
+ const session = await auth.api.getSession({
345
+ headers: await getHeaders(ctx),
346
+ });
347
+
348
+ return next({
349
+ ctx: {
350
+ ...ctx,
351
+ user: session?.user ?? null,
352
+ userId: session?.user?.id ?? null,
353
+ },
354
+ });
355
+ });
356
+
357
+ export const authQuery = c.query
358
+ .meta({ auth: "required" })
359
+ .use(async ({ ctx, next }) => {
360
+ const auth = getAuth(ctx);
361
+ const session = await auth.api.getSession({
362
+ headers: await getHeaders(ctx),
363
+ });
364
+ const user = requireAuth(session?.user ?? null);
365
+ return next({ ctx: { ...ctx, user, userId: user.id } });
366
+ })
367
+ .use(roleMiddleware);
368
+
369
+ export const optionalAuthMutation = c.mutation
370
+ .meta({ auth: "optional" })
371
+ .use(async ({ ctx, next }) => {
372
+ const auth = getAuth(ctx);
373
+ const session = await auth.api.getSession({
374
+ headers: await getHeaders(ctx),
375
+ });
376
+
377
+ return next({
378
+ ctx: {
379
+ ...ctx,
380
+ user: session?.user ?? null,
381
+ userId: session?.user?.id ?? null,
382
+ },
383
+ });
384
+ });
385
+
386
+ export const authMutation = c.mutation
387
+ .meta({ auth: "required" })
388
+ .use(async ({ ctx, next }) => {
389
+ const auth = getAuth(ctx);
390
+ const session = await auth.api.getSession({
391
+ headers: await getHeaders(ctx),
392
+ });
393
+ const user = requireAuth(session?.user ?? null);
394
+ return next({ ctx: { ...ctx, user, userId: user.id } });
395
+ })
396
+ .use(roleMiddleware);
397
+
398
+ export const authAction = c.action
399
+ .meta({ auth: "required" })
400
+ .use(async ({ ctx, next }) => {
401
+ const auth = getAuth(ctx);
402
+ const session = await auth.api.getSession({
403
+ headers: await getHeaders(ctx),
404
+ });
405
+ const user = requireAuth(session?.user ?? null);
406
+ return next({ ctx: { ...ctx, user, userId: user.id } });
407
+ });
408
+
409
+ export const publicRoute = c.httpAction;
410
+ export const authRoute = c.httpAction.use(async ({ ctx, next }) => {
411
+ const identity = await ctx.auth.getUserIdentity();
412
+ if (!identity) {
413
+ throw new CRPCError({ code: "UNAUTHORIZED", message: "Not authenticated" });
414
+ }
415
+ return next({
416
+ ctx: {
417
+ ...ctx,
418
+ userId: identity.subject,
419
+ user: {
420
+ id: identity.subject,
421
+ email: identity.email,
422
+ name: identity.name,
423
+ },
424
+ },
425
+ });
426
+ });
427
+ export const optionalAuthRoute = c.httpAction.use(async ({ ctx, next }) => {
428
+ const identity = await ctx.auth.getUserIdentity();
429
+ return next({
430
+ ctx: {
431
+ ...ctx,
432
+ userId: identity ? identity.subject : null,
433
+ user: identity
434
+ ? {
435
+ id: identity.subject,
436
+ email: identity.email,
437
+ name: identity.name,
438
+ }
439
+ : null,
440
+ },
441
+ });
442
+ });
443
+ export const router = c.router;
444
+ ```
445
+
446
+ ### 6.10 Auth sign-in gate (required before Section 7+ and all optional modules/plugins)
447
+
448
+ Do not continue until all checks below pass:
449
+
450
+ 1. Start local runtime with `bunx kitcn dev`
451
+ 2. `bun run typecheck || bunx tsc --noEmit`
452
+ 3. `bun test`
453
+ 4. `bun run build`
454
+ 5. Headed browser auth verification:
455
+ - Open `/auth`
456
+ - Complete sign-in with configured provider/credentials
457
+ - Confirm session is established (signed-in UI/state visible)
458
+ - Execute one protected query or mutation and confirm it succeeds (no `UNAUTHORIZED`)
459
+ 6. Signed-out enforcement check:
460
+ - In a signed-out context, call one protected path and confirm `UNAUTHORIZED` is returned.
461
+
462
+ Stop/go rule:
463
+
464
+ 1. If any sign-in gate check fails, fix auth wiring first.
465
+ 2. Do not continue to Section 7, 8, 9, or 10 until this gate is green.
466
+
467
+ ## 10. Plugin Setup Modules
468
+
469
+ Feature gate each plugin independently after auth core.
470
+
471
+ ### 10.1 Admin plugin
472
+
473
+ Server:
474
+
475
+ ```ts
476
+ import { admin } from "better-auth/plugins";
477
+
478
+ plugins: [
479
+ admin({
480
+ defaultRole: "user",
481
+ }),
482
+ ];
483
+ ```
484
+
485
+ Client:
486
+
487
+ ```ts
488
+ import { adminClient } from "better-auth/client/plugins";
489
+
490
+ plugins: [adminClient()];
491
+ ```
492
+
493
+ Schema needs admin fields on `user` + `impersonatedBy` on `session`.
494
+
495
+ ### 10.2 Organizations plugin
496
+
497
+ Server: add `organization({...})` plugin config.
498
+
499
+ Client: add `organizationClient({...})` plugin config.
500
+
501
+ Schema: add `organization`, `member`, `invitation` (+ optional `team`, `teamMember`), and session fields `activeOrganizationId`/`activeTeamId`.
@@ -0,0 +1,190 @@
1
+ ## Biome / Lint Setup
2
+
3
+ One-time config. Enforces import boundaries between `src/`, `convex/`, and `convex/shared/`.
4
+
5
+ ### Install
6
+
7
+ ```bash
8
+ bun add -D @biomejs/biome ultracite
9
+ ```
10
+
11
+ ### Architecture: 3-Layer Import Boundary
12
+
13
+ ```
14
+ src/ ──(alias @convex/*)──> convex/shared/ ──(type-only)──> convex/functions/generated/
15
+ ✗ convex/functions/ ✗ convex/lib/
16
+ ✗ convex/lib/ ✗ convex/routers/
17
+ ✗ convex/* packages ✗ convex/functions/*
18
+ ```
19
+
20
+ Rationale:
21
+
22
+ 1. **`src/` → `convex/shared/`**: Client code reads shared types via `@convex/*` alias. Cannot reach `convex/functions/` or `convex/lib/` (server-only).
23
+ 2. **`convex/` → `convex/`**: Backend files cannot import from `src/`.
24
+ 3. **`convex/shared/`**: Strictest — client-importable, so no lib/routers/functions imports. Only type-only imports from `generated/auth` (via biome-ignore).
25
+
26
+ ### File Exclusions
27
+
28
+ ```jsonc
29
+ "files": {
30
+ "includes": [
31
+ "!**/_generated", // Convex auto-generated (raw types)
32
+ "!**/generated", // kitcn codegen (ORM-wrapped types)
33
+ "!**/convex/shared/api.ts" // generated shared API
34
+ ]
35
+ }
36
+ ```
37
+
38
+ Both `_generated/` and `generated/` are excluded from all linting — rules only apply to user-written files.
39
+
40
+ ### Key Rules
41
+
42
+ #### `_generated/server` is forbidden
43
+
44
+ All convex files must import from `generated/server` (ORM-wrapped types) instead of `_generated/server` (raw Convex types):
45
+
46
+ - `QueryCtx`, `MutationCtx`, `ActionCtx` — wrapped with ORM context
47
+ - `initCRPC` — prewired with `DataModel` and ORM context
48
+ - `orm`, `withOrm` — ORM instance
49
+
50
+ Exception: `generated/server.ts` itself imports from `_generated/server` (excluded from linting).
51
+
52
+ #### `ConvexError` is forbidden
53
+
54
+ Use `CRPCError` from `kitcn/crpc` instead of `ConvexError` from `convex/values`.
55
+
56
+ #### `convex/react` and `convex/nextjs` are forbidden in `src/`
57
+
58
+ - Use `useCRPC` from `@/lib/convex/crpc` instead of `convex/react`.
59
+ - Use `caller` from `@/lib/convex/rsc` or `createContext().caller` from `@/lib/convex/server` instead of `convex/nextjs`.
60
+
61
+ #### `convex/shared/` cannot import from `convex/functions/`
62
+
63
+ Exception: type-only imports from `generated/auth` need `biome-ignore`:
64
+
65
+ ```ts
66
+ // biome-ignore lint/style/noRestrictedImports: types
67
+ import type { getAuth } from '../functions/generated/auth';
68
+ ```
69
+
70
+ ### Adding Exceptions
71
+
72
+ Use `biome-ignore` inline comments for legitimate rule violations:
73
+
74
+ ```ts
75
+ // biome-ignore lint/style/noRestrictedImports: types
76
+ import type { ActionCtx } from './_generated/server';
77
+ ```
78
+
79
+ ### Full Config
80
+
81
+ ```jsonc
82
+ {
83
+ "extends": ["ultracite/core", "ultracite/react", "ultracite/next"],
84
+ "files": {
85
+ "includes": [
86
+ "!**/_generated",
87
+ "!**/generated",
88
+ "!**/convex/shared/api.ts"
89
+ ]
90
+ },
91
+ "overrides": [
92
+ {
93
+ // src/ cannot import from convex/* packages directly
94
+ "includes": ["src/**/*.ts*"],
95
+ "linter": {
96
+ "rules": {
97
+ "style": {
98
+ "noRestrictedImports": {
99
+ "level": "error",
100
+ "options": {
101
+ "paths": {
102
+ "convex/values": {
103
+ "importNames": ["ConvexError"],
104
+ "message": "Use CRPCError from 'kitcn/crpc' instead."
105
+ },
106
+ "convex/react": "Use useCRPC from '@/lib/convex/crpc' instead.",
107
+ "convex/nextjs": "Use caller from '@/lib/convex/rsc' or createContext({ headers }).caller from '@/lib/convex/server' instead."
108
+ },
109
+ "patterns": [{
110
+ "group": ["**/../convex/**"],
111
+ "message": "Use @convex/* alias instead of relative convex imports."
112
+ }]
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+ },
119
+ {
120
+ // convex/ cannot import from src/ or _generated/server
121
+ "includes": ["convex/**/*.ts*"],
122
+ "linter": {
123
+ "rules": {
124
+ "style": {
125
+ "noRestrictedImports": {
126
+ "level": "error",
127
+ "options": {
128
+ "paths": {
129
+ "convex/values": {
130
+ "importNames": ["ConvexError"],
131
+ "message": "Use CRPCError from 'kitcn/crpc' instead."
132
+ }
133
+ },
134
+ "patterns": [
135
+ {
136
+ "group": ["@/*", "**/src/**"],
137
+ "message": "Convex files cannot import from src/."
138
+ },
139
+ {
140
+ "group": ["**/_generated/server*"],
141
+ "message": "Use convex/functions/generated/server instead of _generated/server."
142
+ }
143
+ ]
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ },
150
+ {
151
+ // convex/shared/ is client-importable, so restrict its imports
152
+ "includes": ["convex/shared/**/*.ts*"],
153
+ "linter": {
154
+ "rules": {
155
+ "style": {
156
+ "noRestrictedImports": {
157
+ "level": "error",
158
+ "options": {
159
+ "patterns": [
160
+ {
161
+ "group": ["@/*", "**/src/**"],
162
+ "message": "Convex files cannot import from src/."
163
+ },
164
+ {
165
+ "group": ["**/convex/lib/**", "../lib/**"],
166
+ "message": "convex/shared cannot import from convex/lib."
167
+ },
168
+ {
169
+ "group": ["**/convex/routers/**", "../routers/**"],
170
+ "message": "convex/shared cannot import from convex/routers."
171
+ },
172
+ {
173
+ "group": ["**/_generated/server*"],
174
+ "message": "Use convex/functions/generated/server instead of _generated/server."
175
+ },
176
+ {
177
+ "group": ["**/../functions/*"],
178
+ "message": "convex/shared cannot import from convex/functions."
179
+ }
180
+ ]
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ }
187
+ ]
188
+ }
189
+ ```
190
+