kitcn 0.24.0 → 0.25.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 (78) hide show
  1. package/dist/aggregate/index.d.ts +11 -1
  2. package/dist/aggregate/index.js +1 -1
  3. package/dist/auth/generated/index.d.ts +1 -1
  4. package/dist/auth/generated/index.js +1 -1
  5. package/dist/auth/http/index.d.ts +1 -1
  6. package/dist/auth/index.d.ts +4 -4
  7. package/dist/auth/index.js +10 -9
  8. package/dist/auth/nextjs/index.d.ts +2 -2
  9. package/dist/auth/nextjs/index.js +3 -3
  10. package/dist/auth/start/server/index.d.ts +1 -1
  11. package/dist/auth/start/server/index.js +1 -1
  12. package/dist/{builder-CsxVc5xC.js → builder-1Vx-tEnS.js} +3 -3
  13. package/dist/{caller-factory-CWa0ELLD.js → caller-factory-Dd3H7j3V.js} +3 -3
  14. package/dist/{where-clause-compiler-DW6jy2er.d.ts → capabilities-Bem7xvGK.d.ts} +2958 -4145
  15. package/dist/cli.mjs +59 -40
  16. package/dist/{convex-plugin-D8B0oCFq.js → convex-plugin-DfOhBU9g.js} +1 -1
  17. package/dist/{create-schema-orm-B3f2Kc8O.js → create-schema-orm-Bu2JCiBS.js} +1 -1
  18. package/dist/crpc/index.d.ts +2 -2
  19. package/dist/crpc/index.js +3 -3
  20. package/dist/{customFunctions-DxEEO4Dq.js → customFunctions-BbhgdRGl.js} +2 -2
  21. package/dist/definitions-D9vhJ6OV.js +215 -0
  22. package/dist/extensions-DzjCJXYl.js +55 -0
  23. package/dist/filter-expression-Dydt8wS0.js +402 -0
  24. package/dist/{generated-contract-disabled-B-nmd7Ne.d.ts → generated-contract-disabled-BEc4d98x.d.ts} +2 -2
  25. package/dist/{http-types-DXOgaerG.d.ts → http-types-90mapMP9.d.ts} +2 -2
  26. package/dist/id-Bibg34Yb.js +37 -0
  27. package/dist/{local-env-yFKub75x.mjs → local-env-Du24tdbp.mjs} +2243 -2113
  28. package/dist/{middleware-Br5zubu7.d.ts → middleware-CE_MC6Gi.d.ts} +1 -1
  29. package/dist/{middleware-DUd1Sj39.js → middleware-cJDRHTRY.js} +1 -1
  30. package/dist/orm/aggregate-index/index.d.ts +20 -0
  31. package/dist/orm/aggregate-index/index.js +2136 -0
  32. package/dist/orm/index.d.ts +5 -4
  33. package/dist/orm/index.js +126 -4292
  34. package/dist/orm/migrations/index.d.ts +3 -0
  35. package/dist/orm/migrations/index.js +506 -0
  36. package/dist/plugins/index.d.ts +1 -1
  37. package/dist/plugins/index.js +1 -1
  38. package/dist/{procedure-caller-DQxnLBS_.js → procedure-caller-DIzXfX2E.js} +2 -2
  39. package/dist/{procedure-name-C20pFZnk.d.ts → procedure-name-B09-cf1-.d.ts} +3 -3
  40. package/dist/{query-context-BzihIpnM.js → query-context-DOm5Xm3H.js} +1 -402
  41. package/dist/ratelimit/index.d.ts +2 -2
  42. package/dist/ratelimit/index.js +3 -3
  43. package/dist/rsc/index.d.ts +4 -4
  44. package/dist/rsc/index.js +4 -4
  45. package/dist/runtime-DfGsZtx3.js +1074 -0
  46. package/dist/schema-Besl-rpv.js +1411 -0
  47. package/dist/server/index.d.ts +4 -4
  48. package/dist/server/index.js +4 -4
  49. package/dist/{runtime-6DJxpDfb.js → table-rmJm5Qio.js} +3 -1095
  50. package/dist/{token-xlpENMVn.js → token-DcV_0fkF.js} +1 -1
  51. package/dist/{types-BHR7ZKKD.d.ts → types-In-ZZOsd.d.ts} +1 -1
  52. package/dist/{validators-C7LelqTN.js → validators-Dmbhsd3U.js} +1 -1
  53. package/dist/watcher.mjs +1 -1
  54. package/dist/where-clause-compiler-mohWFiG0.d.ts +2042 -0
  55. package/package.json +3 -2
  56. package/skills/kitcn/SKILL.md +19 -18
  57. package/skills/kitcn/references/features/aggregates.md +21 -0
  58. package/skills/kitcn/references/features/auth-organizations.md +3 -3
  59. package/skills/kitcn/references/features/auth-polar.md +2 -1
  60. package/skills/kitcn/references/features/auth.md +8 -1
  61. package/skills/kitcn/references/setup/auth.md +52 -42
  62. package/skills/kitcn/references/setup/index.md +1 -1
  63. package/skills/kitcn/references/setup/server.md +2 -1
  64. /package/dist/{api-entry-N3nBOlI2.js → api-entry-CkDpGYVg.js} +0 -0
  65. /package/dist/{context-utils-BBUtBqjN.d.ts → context-utils-DwZ3Cam1.d.ts} +0 -0
  66. /package/dist/{create-schema-BXrKE2YY.js → create-schema-BRFjdgRr.js} +0 -0
  67. /package/dist/{error-Bvo7YEhk.js → error-CMLeCadS.js} +0 -0
  68. /package/dist/{generated-contract-disabled-BXaz7JCE.js → generated-contract-disabled-CZa0iyV0.js} +0 -0
  69. /package/dist/{meta-utils-D9K4fICl.js → meta-utils-D2lqN2Lg.js} +0 -0
  70. /package/dist/{query-context-CNo9ffvI.d.ts → query-context-DJONf8X5.d.ts} +0 -0
  71. /package/dist/{query-options-CzRV4G4N.js → query-options-DUiOj01a.js} +0 -0
  72. /package/dist/{token-B9Bjcqug.d.ts → token-kQaqFby4.d.ts} +0 -0
  73. /package/dist/{transformer-yZuBWo8v.js → transformer-D8wO-kEj.js} +0 -0
  74. /package/dist/{types-BCl8gfGy.d.ts → types-Bf3XQex5.d.ts} +0 -0
  75. /package/dist/{types-DF2cg_w0.d.ts → types-MHYXboyy.d.ts} +0 -0
  76. /package/dist/{types-CnTpHR1F.d.ts → types-eNzQmbqM.d.ts} +0 -0
  77. /package/dist/{upstream-BR6sBLg3.js → upstream-BCgGZX6q.js} +0 -0
  78. /package/dist/{validators-BhsByJeg.d.ts → validators-V4eM2-Jg.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitcn",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "description": "kitcn - React Query integration and CLI tools for Convex",
5
5
  "keywords": [
6
6
  "convex",
@@ -31,6 +31,8 @@
31
31
  "./auth/start/server": "./dist/auth/start/server/index.js",
32
32
  "./crpc": "./dist/crpc/index.js",
33
33
  "./orm": "./dist/orm/index.js",
34
+ "./orm/aggregate-index": "./dist/orm/aggregate-index/index.js",
35
+ "./orm/migrations": "./dist/orm/migrations/index.js",
34
36
  "./plugins": "./dist/plugins/index.js",
35
37
  "./ratelimit": "./dist/ratelimit/index.js",
36
38
  "./ratelimit/react": "./dist/ratelimit/react/index.js",
@@ -73,7 +75,6 @@
73
75
  "jose": "^6.1.3",
74
76
  "picocolors": "^1.1.1",
75
77
  "remeda": "^2.33.6",
76
- "svix": "^1.84.1",
77
78
  "typescript": "5.9.3",
78
79
  "type-fest": "^5.4.4"
79
80
  },
@@ -171,10 +171,9 @@ Schema rules that matter:
171
171
  ### 2) Procedure Builders + Middleware
172
172
 
173
173
  ```ts
174
- import { getHeaders } from "kitcn/auth";
174
+ import { getSession } from "kitcn/auth";
175
175
  import { CRPCError } from "kitcn/server";
176
- import { getAuth } from "../functions/generated/auth";
177
- import { initCRPC } from "../functions/generated/server";
176
+ import { initCRPC, type QueryCtx } from "../functions/generated/server";
178
177
 
179
178
  const c = initCRPC
180
179
  .meta<{
@@ -190,40 +189,42 @@ function requireAuth<T>(user: T | null): T {
190
189
  }
191
190
  return user;
192
191
  }
192
+ async function getSessionUser(ctx: QueryCtx) {
193
+ const session = await getSession(ctx);
194
+ if (!session) return null;
195
+ return await ctx.orm.query.user.findFirst({
196
+ where: { id: { eq: session.userId } },
197
+ });
198
+ }
199
+
193
200
  export const publicQuery = c.query.meta({ auth: "optional" });
194
201
  export const authQuery = c.query
195
202
  .meta({ auth: "required" })
196
203
  .use(async ({ ctx, next }) => {
197
- const auth = getAuth(ctx);
198
- const session = await auth.api.getSession({
199
- headers: await getHeaders(ctx),
200
- });
201
- const user = requireAuth(session?.user ?? null);
204
+ const user = requireAuth(await getSessionUser(ctx));
202
205
  return next({ ctx: { ...ctx, user, userId: user.id } });
203
206
  });
204
207
  export const authMutation = c.mutation
205
208
  .meta({ auth: "optional" })
206
209
  .use(async ({ ctx, next }) => {
207
- const auth = getAuth(ctx);
208
- const session = await auth.api.getSession({
209
- headers: await getHeaders(ctx),
210
- });
210
+ const user = await getSessionUser(ctx);
211
211
  return next({
212
- ctx: {
213
- ...ctx,
214
- user: session?.user ?? null,
215
- userId: session?.user?.id ?? null,
216
- },
212
+ ctx: { ...ctx, user, userId: user?.id ?? null },
217
213
  });
218
214
  });
219
215
  ```
220
216
 
221
217
  Builder rules that matter:
222
218
 
223
- 1. Build `public`, `optional`, `auth`, and `private` procedure families once in `convex/lib/crpc.ts`.
219
+ 1. Build `public`, `optional`, `auth`, and `private` procedure families once in `convex/lib/crpc.ts`. Authenticated action builders live in `convex/lib/crpc-action.ts`, the only builder module that imports `getAuth`.
224
220
  2. `.meta(...)` is client-visible via generated API metadata. Never put secrets there.
225
221
  3. Middleware receives server-only `procedure` info. When procedures are built from your app `generated/server` helper, standard `export const` queries, mutations, and actions infer `module:function` automatically from file path + export name. Use `.name("module:function")` only to override or cover unusual export shapes.
226
222
  4. Resolve session/user once in middleware. Do not re-fetch auth state in every procedure.
223
+ Query/mutation middleware uses `getSession(ctx)` from `kitcn/auth`, which reads the session row
224
+ directly. Keep `getAuth(ctx)` out of `convex/lib/crpc.ts`: it pulls the whole Better Auth
225
+ definition and every auth plugin into the static import closure of every procedure module, and
226
+ Convex has no dynamic `import()` to escape it. Import `getAuth` only in the modules that call
227
+ `auth.api.*` — `convex/lib/crpc-action.ts`, HTTP routes, and organization/admin mutations.
227
228
  5. Shared `c.middleware()` chains preserve mutation writer types on mutation procedures. If the middleware itself performs writes, type it as mutation-only with `c.middleware<MutationCtx>(...)`.
228
229
  6. Keep deeper auth/runtime edge cases in `references/setup/server.md` and `references/features/auth*.md`.
229
230
 
@@ -9,6 +9,27 @@ Canonical runtime rules:
9
9
  - Use `rankIndex` + `rank()` for rankings, random access, sorted pagination
10
10
  - `aggregateIndex` and `rankIndex` backfill automatically via `kitcn dev` — no manual trigger wiring needed
11
11
 
12
+ ## Setup
13
+
14
+ Aggregate and rank indexes are an optional ORM subsystem. Register it where the ORM is created:
15
+
16
+ ```ts
17
+ import { createOrm } from "kitcn/orm";
18
+ import { aggregateCapability } from "kitcn/orm/aggregate-index";
19
+
20
+ export const orm = createOrm({ schema, capabilities: [aggregateCapability()] });
21
+ ```
22
+
23
+ `kitcn codegen` writes this into `<functionsDir>/generated/server.ts` for schemas that declare an
24
+ `aggregateIndex` or `rankIndex`, and leaves it out otherwise. Convex bundles everything a module
25
+ statically imports and has no dynamic `import()`, and every procedure module imports
26
+ `generated/server.ts`, so an unused registration would put the aggregate btree in every Convex
27
+ function of the app. Declare the index first, then rerun `kitcn codegen`.
28
+
29
+ Without the capability, `aggregateIndex`/`rankIndex` schemas and filtered `count()` / `aggregate()`
30
+ / `groupBy()` / `rank()` / relation `_count` throw a setup error. Unfiltered `count()` and
31
+ `aggregate({ _count: true })` are served by the native Convex count syscall and need nothing.
32
+
12
33
  ## ORM Scalar Metrics
13
34
 
14
35
  ### `aggregateIndex` Schema Declaration
@@ -438,9 +438,9 @@ export const updateOrganization = authMutation
438
438
  });
439
439
  ```
440
440
 
441
- Use an `authAction` instead of an `authMutation` for any Better Auth endpoint
442
- that can run external plugin work such as Stripe, Polar, or email delivery.
443
- Convex mutations cannot call those SDKs.
441
+ Use an `authAction` (from `../lib/crpc-action`) instead of an `authMutation` for
442
+ any Better Auth endpoint that can run external plugin work such as Stripe,
443
+ Polar, or email delivery. Convex mutations cannot call those SDKs.
444
444
 
445
445
  ### Delete Organization
446
446
 
@@ -435,7 +435,8 @@ export const updateUserPolarCustomerId = privateMutation
435
435
  import '../lib/polar-polyfills';
436
436
  import { CRPCError } from 'kitcn/server';
437
437
  import { z } from 'zod';
438
- import { authAction, privateMutation, privateQuery } from '../lib/crpc';
438
+ import { privateMutation, privateQuery } from '../lib/crpc';
439
+ import { authAction } from '../lib/crpc-action';
439
440
  import { getPolarClient } from '../lib/polar-client';
440
441
  import { createPolarSubscriptionCaller } from './generated/polarSubscription.runtime';
441
442
 
@@ -15,7 +15,14 @@ Covers Better Auth integration with Convex: server setup, client hooks, triggers
15
15
  | Query/Mutation (`ctx.db`) | Direct DB | No `runQuery`/`runMutation` wrapper |
16
16
  | Action/HTTP | HTTP adapter | Uses `ctx.run*` APIs |
17
17
 
18
- **Entrypoint**: `getAuth(ctx)` everywhere (query, mutation, action, HTTP).
18
+ **Entrypoints**:
19
+
20
+ - `getSession(ctx)` from `kitcn/auth` in queries and mutations, including the shared cRPC auth
21
+ middleware. It reads the session row directly and keeps the Better Auth definition out of the
22
+ importing module's static closure.
23
+ - `getAuth(ctx)` only where `auth.api.*` is actually called: `convex/lib/crpc-action.ts`, HTTP
24
+ routes, and organization/admin mutations. Convex has no dynamic `import()`, so every module that
25
+ names `getAuth` carries the whole auth definition and its plugins into its evaluation closure.
19
26
 
20
27
  ## Auth Flow
21
28
 
@@ -331,11 +331,10 @@ Again: printed payload only. You still need to set the env manually.
331
331
  After non-auth baseline is green, replace `convex/lib/crpc.ts` with this auth-aware variant:
332
332
 
333
333
  ```ts
334
- import { getHeaders } from "kitcn/auth";
334
+ import { getSession } from "kitcn/auth";
335
335
  import { CRPCError } from "kitcn/server";
336
336
 
337
- import { getAuth } from "../functions/generated/auth";
338
- import { initCRPC } from "../functions/generated/server";
337
+ import { initCRPC, type QueryCtx } from "../functions/generated/server";
339
338
 
340
339
  const c = initCRPC
341
340
  .meta<{
@@ -366,6 +365,14 @@ function requireAuth<T>(user: T | null): T {
366
365
  return user;
367
366
  }
368
367
 
368
+ async function getSessionUser(ctx: QueryCtx) {
369
+ const session = await getSession(ctx);
370
+ if (!session) return null;
371
+ return await ctx.orm.query.user.findFirst({
372
+ where: { id: { eq: session.userId } },
373
+ });
374
+ }
375
+
369
376
  export const publicQuery = c.query.meta({ auth: "optional" });
370
377
  export const publicAction = c.action;
371
378
  export const publicMutation = c.mutation;
@@ -377,28 +384,17 @@ export const privateAction = c.action.internal();
377
384
  export const optionalAuthQuery = c.query
378
385
  .meta({ auth: "optional" })
379
386
  .use(async ({ ctx, next }) => {
380
- const auth = getAuth(ctx);
381
- const session = await auth.api.getSession({
382
- headers: await getHeaders(ctx),
383
- });
387
+ const user = await getSessionUser(ctx);
384
388
 
385
389
  return next({
386
- ctx: {
387
- ...ctx,
388
- user: session?.user ?? null,
389
- userId: session?.user?.id ?? null,
390
- },
390
+ ctx: { ...ctx, user, userId: user?.id ?? null },
391
391
  });
392
392
  });
393
393
 
394
394
  export const authQuery = c.query
395
395
  .meta({ auth: "required" })
396
396
  .use(async ({ ctx, next }) => {
397
- const auth = getAuth(ctx);
398
- const session = await auth.api.getSession({
399
- headers: await getHeaders(ctx),
400
- });
401
- const user = requireAuth(session?.user ?? null);
397
+ const user = requireAuth(await getSessionUser(ctx));
402
398
  return next({ ctx: { ...ctx, user, userId: user.id } });
403
399
  })
404
400
  .use(roleMiddleware);
@@ -406,43 +402,21 @@ export const authQuery = c.query
406
402
  export const optionalAuthMutation = c.mutation
407
403
  .meta({ auth: "optional" })
408
404
  .use(async ({ ctx, next }) => {
409
- const auth = getAuth(ctx);
410
- const session = await auth.api.getSession({
411
- headers: await getHeaders(ctx),
412
- });
405
+ const user = await getSessionUser(ctx);
413
406
 
414
407
  return next({
415
- ctx: {
416
- ...ctx,
417
- user: session?.user ?? null,
418
- userId: session?.user?.id ?? null,
419
- },
408
+ ctx: { ...ctx, user, userId: user?.id ?? null },
420
409
  });
421
410
  });
422
411
 
423
412
  export const authMutation = c.mutation
424
413
  .meta({ auth: "required" })
425
414
  .use(async ({ ctx, next }) => {
426
- const auth = getAuth(ctx);
427
- const session = await auth.api.getSession({
428
- headers: await getHeaders(ctx),
429
- });
430
- const user = requireAuth(session?.user ?? null);
415
+ const user = requireAuth(await getSessionUser(ctx));
431
416
  return next({ ctx: { ...ctx, user, userId: user.id } });
432
417
  })
433
418
  .use(roleMiddleware);
434
419
 
435
- export const authAction = c.action
436
- .meta({ auth: "required" })
437
- .use(async ({ ctx, next }) => {
438
- const auth = getAuth(ctx);
439
- const session = await auth.api.getSession({
440
- headers: await getHeaders(ctx),
441
- });
442
- const user = requireAuth(session?.user ?? null);
443
- return next({ ctx: { ...ctx, user, userId: user.id } });
444
- });
445
-
446
420
  export const publicRoute = c.httpAction;
447
421
  export const authRoute = c.httpAction.use(async ({ ctx, next }) => {
448
422
  const identity = await ctx.auth.getUserIdentity();
@@ -480,6 +454,42 @@ export const optionalAuthRoute = c.httpAction.use(async ({ ctx, next }) => {
480
454
  export const router = c.router;
481
455
  ```
482
456
 
457
+ ### 6.9.1 Authenticated action builders
458
+
459
+ Authenticated actions live in their own module. Actions have no `ctx.db`, so they resolve the
460
+ session through `auth.api.getSession` instead of `getSession(ctx)`. `getAuth` carries the whole
461
+ Better Auth definition and every plugin into the static import closure of whatever imports it, and
462
+ Convex has no dynamic `import()`, so it stays out of `convex/lib/crpc.ts` — the module every
463
+ procedure imports.
464
+
465
+ Skip this file if the app has no authenticated actions.
466
+
467
+ **Create:** `convex/lib/crpc-action.ts`
468
+
469
+ ```ts
470
+ import { getHeaders } from "kitcn/auth";
471
+ import { CRPCError } from "kitcn/server";
472
+
473
+ import { getAuth } from "../functions/generated/auth";
474
+ import { publicAction } from "./crpc";
475
+
476
+ export const authAction = publicAction
477
+ .meta({ auth: "required" })
478
+ .use(async ({ ctx, next }) => {
479
+ const auth = getAuth(ctx);
480
+ const session = await auth.api.getSession({
481
+ headers: await getHeaders(ctx),
482
+ });
483
+ const user = session?.user;
484
+ if (!user) {
485
+ throw new CRPCError({ code: "UNAUTHORIZED", message: "Not authenticated" });
486
+ }
487
+ return next({ ctx: { ...ctx, user, userId: user.id } });
488
+ });
489
+ ```
490
+
491
+ Import `authAction` from `../lib/crpc-action`. Every other builder comes from `../lib/crpc`.
492
+
483
493
  ### 6.10 Auth sign-in gate (required before Section 7+ and all optional modules/plugins)
484
494
 
485
495
  Do not continue until all checks below pass:
@@ -755,7 +755,7 @@ domain depth: `orm.md`, `http.md`, `react.md`, `aggregates.md`,
755
755
  | HTTP calls fail but queries work | `.site` URL missing or wrong | Set `NEXT_PUBLIC_CONVEX_SITE_URL` correctly |
756
756
  | Auth works locally but fails in prod | JWKS not pushed | Run `bunx kitcn env push --prod` |
757
757
  | Sign-in fails on `/auth` (loop, no session, or immediate sign-out) | Auth route/env/provider wiring mismatch | Recheck Sections 6.5-6.7 (`authMiddleware`, route registration, env push), verify provider credentials/URLs, then rerun Section 11.3 |
758
- | `UNAUTHORIZED` on protected procedures | auth middleware not attaching `userId` | Ensure `getAuth(ctx)` + `getHeaders(ctx)` session lookup is in middleware |
758
+ | `UNAUTHORIZED` on protected procedures | auth middleware not attaching `userId` | Ensure the `getSession(ctx)` session lookup is in middleware |
759
759
  | `ctx.orm` missing in handlers | Generated `initCRPC` not used | Use `initCRPC` from `../functions/generated/server` — ORM context is pre-wired |
760
760
  | `Property 'insert'/'update' does not exist on type 'OrmReader'` | Using query context for mutations | Ensure mutation handlers use `publicMutation` / `protectedMutation` builders |
761
761
  | `useCRPC must be used within CRPCProvider` | Provider chain not mounted around route tree | Wrap app with `AppConvexProvider` and verify `CRPCProvider` is inside QueryClientProvider (Section 7.4 / 8.A.4) |
@@ -175,7 +175,8 @@ export const router = c.router;
175
175
  Phase ordering rule:
176
176
 
177
177
  1. Keep this non-auth baseline until Section 11.2 fully passes.
178
- 2. Only then replace this file with the auth-aware variant in Section 6.9.
178
+ 2. Only then replace this file with the auth-aware variant in Section 6.9, and add
179
+ `convex/lib/crpc-action.ts` from Section 6.9.1 if the app has authenticated actions.
179
180
 
180
181
  ### 5.4 Shared API/type helpers (generated)
181
182
 
File without changes