kitcn 0.19.0 → 0.21.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.
@@ -1,4 +1,4 @@
1
- import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial, dn as ConvexTableWithColumns, tr as ConvexTextBuilderInitial } from "../where-clause-compiler-CmBz9CWA.js";
1
+ import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial, dn as ConvexTableWithColumns, tr as ConvexTextBuilderInitial } from "../where-clause-compiler-B_H3oio5.js";
2
2
  import * as convex_values0 from "convex/values";
3
3
  import { GenericId, Infer, Value } from "convex/values";
4
4
  import { DocumentByName, GenericDataModel, GenericDatabaseReader, GenericDatabaseWriter, TableNamesInDataModel } from "convex/server";
@@ -229,6 +229,12 @@ declare class Aggregate<K extends Key, ID extends string, TNamespace extends Val
229
229
  maxNodeSize?: number;
230
230
  rootLazy?: boolean;
231
231
  }, TNamespace>): Promise<void>;
232
+ /**
233
+ * Deletes up to `limit` namespace trees without recreating them. Returns true
234
+ * once this aggregate owns no trees, so callers can drain every namespace
235
+ * across several mutations instead of walking them all in one.
236
+ */
237
+ deleteTrees(ctx: RunMutationCtx, limit: number): Promise<boolean>;
232
238
  makeRootLazy(ctx: RunMutationCtx, namespace: TNamespace): Promise<void>;
233
239
  paginateNamespaces(ctx: RunQueryCtx, cursor?: string, pageSize?: number): Promise<{
234
240
  cursor: string;
@@ -1,4 +1,4 @@
1
- import { n as TableAggregate$1, r as aggregateStorageTables, t as DirectAggregate$1 } from "../runtime-DmVSOe24.js";
1
+ import { n as TableAggregate$1, r as aggregateStorageTables, t as DirectAggregate$1 } from "../runtime-6DJxpDfb.js";
2
2
 
3
3
  //#region src/aggregate/index.ts
4
4
  const wrapTriggerFactory = (methodName, factory) => ((...args) => {
@@ -24,28 +24,84 @@ const hasActiveSessionData = (session) => {
24
24
  if (!session || typeof session !== "object") return false;
25
25
  return Boolean(session.session);
26
26
  };
27
+ const getSessionId = (sessionData) => {
28
+ if (!sessionData || typeof sessionData !== "object") return;
29
+ const session = sessionData.session;
30
+ if (!session || typeof session !== "object") return;
31
+ const id = session.id;
32
+ return typeof id === "string" ? id : void 0;
33
+ };
34
+ const isSameSession = (left, right) => {
35
+ if (left === right) return true;
36
+ const leftId = getSessionId(left);
37
+ return leftId !== void 0 && leftId === getSessionId(right);
38
+ };
27
39
  const wait = (ms) => new Promise((resolve) => {
28
40
  setTimeout(resolve, ms);
29
41
  });
30
- const readAuthResultData = (result) => {
31
- if (!result || typeof result !== "object") return;
32
- return result.data;
42
+ const PERSISTED_TOKEN_RETRY_BASE_MS = 100;
43
+ const PERSISTED_TOKEN_RETRY_MAX_MS = 2e3;
44
+ const readAuthResult = (result) => {
45
+ if (!result || typeof result !== "object") return {
46
+ data: void 0,
47
+ errored: true
48
+ };
49
+ const { data, error } = result;
50
+ return {
51
+ data,
52
+ errored: Boolean(error)
53
+ };
33
54
  };
34
- const getSessionFromPersistedToken = async (authClient, token) => {
35
- await wait(250);
55
+ const fetchPersistedSession = async (authClient, token) => {
36
56
  const getSession = authClient.getSession;
37
- for (let attempt = 0; attempt < 10; attempt += 1) {
38
- const data = readAuthResultData(authClient.$fetch ? await authClient.$fetch("/get-session", {
57
+ try {
58
+ return await (authClient.$fetch ? authClient.$fetch("/get-session", {
39
59
  credentials: "omit",
40
60
  headers: { Authorization: `Bearer ${token}` }
41
- }) : await getSession?.({ fetchOptions: {
61
+ }) : getSession?.({ fetchOptions: {
42
62
  credentials: "omit",
43
63
  headers: { Authorization: `Bearer ${token}` }
44
64
  } }));
45
- if (data) return data;
46
- if (attempt < 9) await wait(100);
65
+ } catch (error) {
66
+ return {
67
+ data: void 0,
68
+ error
69
+ };
70
+ }
71
+ };
72
+ const fetchPersistedSessionBeforeDeadline = async (authClient, token, deadline) => {
73
+ const remaining = deadline - Date.now();
74
+ if (remaining <= 0) return { status: "deadline" };
75
+ let timeoutId;
76
+ const deadlineResult = new Promise((resolve) => {
77
+ timeoutId = setTimeout(() => resolve({ status: "deadline" }), remaining);
78
+ });
79
+ try {
80
+ return await Promise.race([fetchPersistedSession(authClient, token).then((result) => ({
81
+ result,
82
+ status: "result"
83
+ })), deadlineResult]);
84
+ } finally {
85
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
86
+ }
87
+ };
88
+ const getSessionFromPersistedToken = async (authClient, token, { deadline, shouldStop }) => {
89
+ for (let attempt = 0;; attempt += 1) {
90
+ if (attempt > 0) {
91
+ const remaining = deadline - Date.now();
92
+ if (remaining <= 0) return { status: "unknown" };
93
+ await wait(Math.min(PERSISTED_TOKEN_RETRY_BASE_MS * 3 ** (attempt - 1), PERSISTED_TOKEN_RETRY_MAX_MS, remaining));
94
+ }
95
+ if (shouldStop()) return { status: "unknown" };
96
+ const request = await fetchPersistedSessionBeforeDeadline(authClient, token, deadline);
97
+ if (request.status === "deadline") return { status: "unknown" };
98
+ const { data, errored } = readAuthResult(request.result);
99
+ if (data) return {
100
+ data,
101
+ status: "session"
102
+ };
103
+ if (!errored) return { status: "none" };
47
104
  }
48
- return null;
49
105
  };
50
106
  const syncSessionAtom = (authClient, sessionData) => {
51
107
  const sessionAtom = authClient.$store?.atoms?.session;
@@ -107,6 +163,8 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
107
163
  const sessionRef = useRef(session);
108
164
  const isPendingRef = useRef(isPending);
109
165
  const pendingTokenRef = useRef(null);
166
+ const restoredTokenRef = useRef(null);
167
+ const isMountedRef = useRef(false);
110
168
  sessionRef.current = session;
111
169
  isPendingRef.current = isPending;
112
170
  const getCachedJwt = useCallback((minTimeRemainingMs = 0) => {
@@ -132,21 +190,41 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
132
190
  isPending,
133
191
  authStore
134
192
  ]);
193
+ useEffect(() => {
194
+ isMountedRef.current = true;
195
+ return () => {
196
+ isMountedRef.current = false;
197
+ };
198
+ }, []);
135
199
  useEffect(() => {
136
200
  if (hasActiveSessionData(session) || isPending || authStore.get("token")) return;
137
201
  const persistedToken = readAuthSessionFallbackToken();
202
+ if (!persistedToken || restoredTokenRef.current === persistedToken || typeof authClient.getSession !== "function" && typeof authClient.$fetch !== "function") return;
203
+ restoredTokenRef.current = persistedToken;
138
204
  const persistedSessionData = readAuthSessionFallbackData();
139
- if (!persistedToken || typeof authClient.getSession !== "function" && typeof authClient.$fetch !== "function") return;
140
- let cancelled = false;
205
+ const graceUntil = Date.now() + AUTH_SESSION_SYNC_GRACE_MS;
141
206
  authStore.set("token", persistedToken);
142
207
  authStore.set("expiresAt", decodeJwtExp(persistedToken));
143
- authStore.set("sessionSyncGraceUntil", Date.now() + AUTH_SESSION_SYNC_GRACE_MS);
208
+ authStore.set("sessionSyncGraceUntil", graceUntil);
144
209
  if (persistedSessionData) syncSessionAtom(authClient, persistedSessionData);
145
- getSessionFromPersistedToken(authClient, persistedToken).then((result) => {
146
- if (cancelled) return;
147
- if (result) {
148
- syncSessionAtom(authClient, result);
149
- writeAuthSessionFallbackData(result);
210
+ const ownsToken = () => authStore.get("token") === persistedToken;
211
+ const shouldStop = () => !isMountedRef.current || !ownsToken();
212
+ getSessionFromPersistedToken(authClient, persistedToken, {
213
+ deadline: graceUntil,
214
+ shouldStop
215
+ }).then((outcome) => {
216
+ const hasCompetingSession = hasActiveSessionData(sessionRef.current) && !isSameSession(sessionRef.current, persistedSessionData);
217
+ if (!isMountedRef.current || !ownsToken() || hasCompetingSession) return;
218
+ if (outcome.status === "session") {
219
+ syncSessionAtom(authClient, outcome.data);
220
+ writeAuthSessionFallbackData(outcome.data);
221
+ return;
222
+ }
223
+ if (outcome.status === "unknown") {
224
+ if (persistedSessionData) clearSessionAtom(authClient);
225
+ authStore.set("token", null);
226
+ authStore.set("expiresAt", null);
227
+ authStore.set("sessionSyncGraceUntil", null);
150
228
  return;
151
229
  }
152
230
  clearAuthSessionFallback();
@@ -154,17 +232,7 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
154
232
  authStore.set("token", null);
155
233
  authStore.set("expiresAt", null);
156
234
  authStore.set("sessionSyncGraceUntil", null);
157
- }).catch(() => {
158
- if (cancelled) return;
159
- clearAuthSessionFallback();
160
- clearSessionAtom(authClient);
161
- authStore.set("token", null);
162
- authStore.set("expiresAt", null);
163
- authStore.set("sessionSyncGraceUntil", null);
164
- });
165
- return () => {
166
- cancelled = true;
167
- };
235
+ }).catch(() => {});
168
236
  }, [
169
237
  session,
170
238
  isPending,
@@ -1,2 +1,2 @@
1
- import { S as defineAuth, _ as GenericAuthBeforeResult, b as GenericAuthTriggerHandlers, g as BetterAuthOptionsWithoutDatabase, i as getGeneratedAuthDisabledReason, n as GeneratedAuthDisabledReasonKind, r as createDisabledAuthRuntime, t as AuthRuntime, v as GenericAuthDefinition, x as GenericAuthTriggers, y as GenericAuthTriggerChange } from "../../generated-contract-disabled-LOAfma7_.js";
1
+ import { S as defineAuth, _ as GenericAuthBeforeResult, b as GenericAuthTriggerHandlers, g as BetterAuthOptionsWithoutDatabase, i as getGeneratedAuthDisabledReason, n as GeneratedAuthDisabledReasonKind, r as createDisabledAuthRuntime, t as AuthRuntime, v as GenericAuthDefinition, x as GenericAuthTriggers, y as GenericAuthTriggerChange } from "../../generated-contract-disabled-B-nmd7Ne.js";
2
2
  export { type AuthRuntime, BetterAuthOptionsWithoutDatabase, type GeneratedAuthDisabledReasonKind, GenericAuthBeforeResult, GenericAuthDefinition, GenericAuthTriggerChange, GenericAuthTriggerHandlers, GenericAuthTriggers, createDisabledAuthRuntime, defineAuth, getGeneratedAuthDisabledReason };
@@ -1,7 +1,7 @@
1
1
  import { a as QueryCtxWithPreferredOrmQueryTable, n as LookupByIdResultByCtx, t as DocByCtx } from "../query-context-CNo9ffvI.js";
2
2
  import { t as GetAuth } from "../types-BCl8gfGy.js";
3
3
  import { t as GenericCtx } from "../context-utils-BBUtBqjN.js";
4
- import { S as defineAuth, _ as GenericAuthBeforeResult, a as AuthFunctions, b as GenericAuthTriggerHandlers, c as createApi, d as deleteOneHandler, f as findManyHandler, g as BetterAuthOptionsWithoutDatabase, h as updateOneHandler, i as getGeneratedAuthDisabledReason, l as createHandler, m as updateManyHandler, n as GeneratedAuthDisabledReasonKind, o as Triggers, p as findOneHandler, r as createDisabledAuthRuntime, s as createClient, t as AuthRuntime, u as deleteManyHandler, v as GenericAuthDefinition, x as GenericAuthTriggers, y as GenericAuthTriggerChange } from "../generated-contract-disabled-LOAfma7_.js";
4
+ import { S as defineAuth, _ as GenericAuthBeforeResult, a as AuthFunctions, b as GenericAuthTriggerHandlers, c as createApi, d as deleteOneHandler, f as findManyHandler, g as BetterAuthOptionsWithoutDatabase, h as updateOneHandler, i as getGeneratedAuthDisabledReason, l as createHandler, m as updateManyHandler, n as GeneratedAuthDisabledReasonKind, o as Triggers, p as findOneHandler, r as createDisabledAuthRuntime, s as createClient, t as AuthRuntime, u as deleteManyHandler, v as GenericAuthDefinition, x as GenericAuthTriggers, y as GenericAuthTriggerChange } from "../generated-contract-disabled-B-nmd7Ne.js";
5
5
  import * as convex_values0 from "convex/values";
6
6
  import { Infer } from "convex/values";
7
7
  import { AuthConfig, DocumentByName, GenericDataModel, GenericMutationCtx, GenericQueryCtx, GenericSchema, PaginationOptions, PaginationResult, SchemaDefinition, TableNamesInDataModel } from "convex/server";
@@ -25,9 +25,11 @@ declare const handlePagination: (next: ({
25
25
  }) => Promise<SetOptional<PaginationResult<any>, "page"> & {
26
26
  count?: number;
27
27
  }>, {
28
+ countOnly,
28
29
  limit,
29
30
  numItems
30
31
  }?: {
32
+ countOnly?: boolean;
31
33
  limit?: number;
32
34
  numItems?: number;
33
35
  }) => Promise<{
@@ -66,7 +68,7 @@ declare const adapterConfig: {
66
68
  action: "create" | "update" | "findOne" | "findMany" | "updateMany" | "delete" | "deleteMany" | "consumeOne" | "incrementOne" | "count";
67
69
  model: string;
68
70
  schema: BetterAuthDBSchema;
69
- options: BetterAuthOptions;
71
+ options: better_auth0.BetterAuthOptions;
70
72
  }) => any;
71
73
  customTransformOutput: ({
72
74
  data,
@@ -78,7 +80,7 @@ declare const adapterConfig: {
78
80
  select: string[];
79
81
  model: string;
80
82
  schema: BetterAuthDBSchema;
81
- options: BetterAuthOptions;
83
+ options: better_auth0.BetterAuthOptions;
82
84
  }) => any;
83
85
  };
84
86
  declare const httpAdapter: <DataModel extends GenericDataModel, Schema extends SchemaDefinition<any, any>>(ctx: GenericCtx<DataModel>, {
@@ -89,16 +91,18 @@ declare const httpAdapter: <DataModel extends GenericDataModel, Schema extends S
89
91
  authFunctions: AuthFunctions;
90
92
  debugLogs?: DBAdapterDebugLogOption;
91
93
  schema?: Schema;
92
- }) => better_auth_adapters0.AdapterFactory<BetterAuthOptions>;
93
- declare const dbAdapter: <DataModel extends GenericDataModel, Schema extends SchemaDefinition<any, any>>(ctx: GenericCtx<DataModel>, getAuthOptions: (ctx: any) => BetterAuthOptions, {
94
+ }) => better_auth_adapters0.AdapterFactory<better_auth0.BetterAuthOptions>;
95
+ declare const dbAdapter: <DataModel extends GenericDataModel, Schema extends SchemaDefinition<any, any>>(ctx: GenericCtx<DataModel>, {
94
96
  authFunctions,
95
97
  debugLogs,
98
+ getBetterAuthSchema,
96
99
  schema
97
100
  }: {
98
- authFunctions: AuthFunctions;
101
+ authFunctions: AuthFunctions; /** Ctx-free Better Auth table schema, memoized by the caller. */
102
+ getBetterAuthSchema: () => BetterAuthDBSchema;
99
103
  schema: Schema;
100
104
  debugLogs?: DBAdapterDebugLogOption;
101
- }) => better_auth_adapters0.AdapterFactory<BetterAuthOptions>;
105
+ }) => better_auth_adapters0.AdapterFactory<better_auth0.BetterAuthOptions>;
102
106
  //#endregion
103
107
  //#region src/auth/adapter-utils.d.ts
104
108
  type AdapterPaginationOptions = PaginationOptions & {
@@ -4,12 +4,12 @@ import { i as defineAuth, n as createDisabledAuthRuntime, r as getGeneratedAuthD
4
4
  import { n as createGeneratedFunctionReference, o as isQueryCtx, s as isRunMutationCtx } from "../api-entry-N3nBOlI2.js";
5
5
  import { n as customCtx, r as customMutation } from "../customFunctions-DxEEO4Dq.js";
6
6
  import { o as mergedStream, s as stream, t as getByIdWithOrmQueryFallback, u as unsetToken, y as eq } from "../query-context-BzihIpnM.js";
7
- import { n as convex } from "../convex-plugin-BHVCqWTH.js";
7
+ import { n as convex } from "../convex-plugin-D8B0oCFq.js";
8
8
  import { v } from "convex/values";
9
9
  import { internalActionGeneric, internalMutationGeneric, internalQueryGeneric, paginationOptsValidator } from "convex/server";
10
10
  import { createAdapterFactory } from "better-auth/adapters";
11
- import { getAuthTables } from "better-auth/db";
12
11
  import { prop, sortBy } from "remeda";
12
+ import { getAuthTables } from "better-auth/db";
13
13
  import { stripIndent } from "common-tags";
14
14
  import { betterAuth } from "better-auth/minimal";
15
15
 
@@ -32,10 +32,34 @@ const adapterArgsValidator = v.object({
32
32
  })),
33
33
  where: v.optional(v.array(adapterWhereValidator))
34
34
  });
35
+ const schemaViewsCache = /* @__PURE__ */ new WeakMap();
36
+ const EMPTY_SCHEMA_VIEWS = {
37
+ modelNameToKey: /* @__PURE__ */ new Map(),
38
+ uniqueFields: /* @__PURE__ */ new Map()
39
+ };
40
+ const getSchemaViews = (betterAuthSchema) => {
41
+ if (!betterAuthSchema || typeof betterAuthSchema !== "object") return EMPTY_SCHEMA_VIEWS;
42
+ const cached = schemaViewsCache.get(betterAuthSchema);
43
+ if (cached) return cached;
44
+ const modelNameToKey = /* @__PURE__ */ new Map();
45
+ const uniqueFields = /* @__PURE__ */ new Map();
46
+ for (const [key, model] of Object.entries(betterAuthSchema)) {
47
+ if (model?.modelName && !modelNameToKey.has(model.modelName)) modelNameToKey.set(model.modelName, key);
48
+ const unique = /* @__PURE__ */ new Set();
49
+ for (const [field, attrs] of Object.entries(model?.fields ?? {})) if (attrs?.unique) unique.add(field);
50
+ uniqueFields.set(key, unique);
51
+ }
52
+ const views = {
53
+ modelNameToKey,
54
+ uniqueFields
55
+ };
56
+ schemaViewsCache.set(betterAuthSchema, views);
57
+ return views;
58
+ };
35
59
  const isUniqueField = (betterAuthSchema, model, field) => {
36
- const modelSchema = betterAuthSchema[Object.keys(betterAuthSchema).find((key) => betterAuthSchema[key].modelName === model) || model];
37
- if (!modelSchema?.fields) return false;
38
- return Object.entries(modelSchema.fields).filter(([, value]) => value.unique).map(([key]) => key).includes(field);
60
+ const { modelNameToKey, uniqueFields } = getSchemaViews(betterAuthSchema);
61
+ const betterAuthModel = modelNameToKey.get(model) ?? model;
62
+ return uniqueFields.get(betterAuthModel)?.has(field) ?? false;
39
63
  };
40
64
  const hasUniqueFields = (betterAuthSchema, model, input) => {
41
65
  for (const field of Object.keys(input)) if (isUniqueField(betterAuthSchema, model, field)) return true;
@@ -482,8 +506,25 @@ const findManyHandler = async (ctx, args, schema, betterAuthSchema) => toConvexS
482
506
  const updateOneHandler = async (ctx, args, schema, betterAuthSchema) => {
483
507
  const triggerCtx = args.triggerCtx ?? ctx;
484
508
  const tableTriggers = args.tableTriggers;
485
- const doc = await listOne(ctx, schema, betterAuthSchema, args.input);
509
+ const matches = [];
510
+ let cursor = null;
511
+ let isDone = false;
512
+ while (!isDone && matches.length < 2) {
513
+ const result = await paginate(ctx, schema, betterAuthSchema, {
514
+ ...args.input,
515
+ paginationOpts: {
516
+ cursor,
517
+ numItems: 2 - matches.length
518
+ }
519
+ });
520
+ matches.push(...result.page);
521
+ isDone = result.isDone;
522
+ if (!isDone && result.continueCursor === cursor) throw new Error("Pagination made no forward progress");
523
+ cursor = result.continueCursor;
524
+ }
525
+ const doc = matches[0];
486
526
  if (!doc) throw new Error(`Failed to update ${args.input.model}`);
527
+ if (matches.length > 1) throw new Error(`Multiple ${args.input.model} found matching criteria. Expected exactly 1.`);
487
528
  const normalizedDoc = withBothIdFields(doc);
488
529
  const update = stripUnsupportedAuthTimestamps(serializeDatesForConvex(await applyBeforeHook(args.input.model, "update", args.input.update, tableTriggers?.update?.before, triggerCtx)), schema, betterAuthSchema, args.input.model);
489
530
  await checkUniqueFields(ctx, schema, betterAuthSchema, args.input.model, update, normalizedDoc);
@@ -590,9 +631,10 @@ const deleteManyHandler = async (ctx, args, schema, betterAuthSchema) => {
590
631
  });
591
632
  };
592
633
  const createApi = (schema, getAuth, options) => {
593
- const { internalMutation, validateInput = false, context, triggers } = options ?? {};
634
+ const { internalMutation, validateInput = false, context, getBetterAuthSchema: injectedBetterAuthSchema, triggers } = options ?? {};
594
635
  let betterAuthSchema;
595
636
  const getBetterAuthSchema = () => {
637
+ if (injectedBetterAuthSchema) return injectedBetterAuthSchema();
596
638
  betterAuthSchema ??= getAuthTables(getAuth({}).options);
597
639
  return betterAuthSchema;
598
640
  };
@@ -749,7 +791,7 @@ const createApi = (schema, getAuth, options) => {
749
791
  //#endregion
750
792
  //#region src/auth/adapter.ts
751
793
  let didWarnExperimentalJoinsUnsupported = false;
752
- const handlePagination = async (next, { limit, numItems } = {}) => {
794
+ const handlePagination = async (next, { countOnly, limit, numItems } = {}) => {
753
795
  const state = {
754
796
  count: 0,
755
797
  cursor: null,
@@ -759,6 +801,11 @@ const handlePagination = async (next, { limit, numItems } = {}) => {
759
801
  const onResult = (result) => {
760
802
  state.cursor = result.continueCursor;
761
803
  if (result.page) {
804
+ if (countOnly) {
805
+ state.count += result.page.length;
806
+ state.isDone = limit && state.count >= limit || result.isDone;
807
+ return;
808
+ }
762
809
  state.docs.push(...result.page);
763
810
  state.isDone = limit && state.docs.length >= limit || result.isDone;
764
811
  return;
@@ -772,9 +819,10 @@ const handlePagination = async (next, { limit, numItems } = {}) => {
772
819
  };
773
820
  do {
774
821
  const cursorBeforePage = state.cursor;
822
+ const consumed = countOnly ? state.count : state.docs.length;
775
823
  const result = await next({ paginationOpts: {
776
824
  cursor: state.cursor,
777
- numItems: Math.min(numItems ?? 200, limit === void 0 ? Number.POSITIVE_INFINITY : limit - state.docs.length, 200)
825
+ numItems: Math.min(numItems ?? 200, limit === void 0 ? Number.POSITIVE_INFINITY : limit - consumed, 200)
778
826
  } });
779
827
  onResult(result);
780
828
  const advanced = state.cursor !== cursorBeforePage;
@@ -901,7 +949,7 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, schema }) => {
901
949
  ...data,
902
950
  paginationOpts,
903
951
  where: parseWhere(data.where)
904
- }))).docs.length;
952
+ }), { countOnly: true })).count;
905
953
  },
906
954
  create: async ({ data, model, select }) => {
907
955
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
@@ -990,20 +1038,11 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, schema }) => {
990
1038
  update: async (data) => {
991
1039
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
992
1040
  if (!data.where?.length) return null;
993
- if (data.where.every((w) => (w.operator === "eq" || w.operator === void 0) && w.connector !== "OR")) {
994
- const countResult = await handlePagination(async ({ paginationOpts }) => await ctx.runQuery(authFunctions.findMany, {
995
- model: data.model,
996
- paginationOpts,
997
- where: parseWhere(data.where)
998
- }), { limit: 2 });
999
- if (countResult.docs.length === 0) throw new Error(`No ${data.model} found matching criteria`);
1000
- if (countResult.docs.length > 1) throw new Error(`Multiple ${data.model} found matching criteria. Expected exactly 1.`);
1001
- return await ctx.runMutation(authFunctions.updateOne, { input: {
1002
- model: data.model,
1003
- update: data.update,
1004
- where: parseWhere(data.where)
1005
- } });
1006
- }
1041
+ if (data.where.every((w) => (w.operator === "eq" || w.operator === void 0) && w.connector !== "OR")) return await ctx.runMutation(authFunctions.updateOne, { input: {
1042
+ model: data.model,
1043
+ update: data.update,
1044
+ where: parseWhere(data.where)
1045
+ } });
1007
1046
  throw new Error("where clause not supported");
1008
1047
  },
1009
1048
  updateMany: async (data) => {
@@ -1039,8 +1078,8 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, schema }) => {
1039
1078
  }
1040
1079
  });
1041
1080
  };
1042
- const dbAdapter = (ctx, getAuthOptions, { authFunctions, debugLogs, schema }) => {
1043
- const betterAuthSchema = getAuthTables(getAuthOptions({}));
1081
+ const dbAdapter = (ctx, { authFunctions, debugLogs, getBetterAuthSchema, schema }) => {
1082
+ const betterAuthSchema = getBetterAuthSchema();
1044
1083
  return createAdapterFactory({
1045
1084
  config: {
1046
1085
  ...adapterConfig,
@@ -1079,7 +1118,7 @@ const dbAdapter = (ctx, getAuthOptions, { authFunctions, debugLogs, schema }) =>
1079
1118
  ...data,
1080
1119
  paginationOpts,
1081
1120
  where: parseWhere(data.where)
1082
- }, schema, betterAuthSchema))).docs.length;
1121
+ }, schema, betterAuthSchema), { countOnly: true })).count;
1083
1122
  },
1084
1123
  create: async ({ data, model, select }) => {
1085
1124
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
@@ -1167,13 +1206,6 @@ const dbAdapter = (ctx, getAuthOptions, { authFunctions, debugLogs, schema }) =>
1167
1206
  update: async (data) => {
1168
1207
  if (!data.where?.length) return null;
1169
1208
  if (data.where.every((w) => (w.operator === "eq" || w.operator === void 0) && w.connector !== "OR")) {
1170
- const countResult = await handlePagination(async ({ paginationOpts }) => await findManyHandler(ctx, {
1171
- model: data.model,
1172
- paginationOpts,
1173
- where: parseWhere(data.where)
1174
- }, schema, betterAuthSchema), { limit: 2 });
1175
- if (countResult.docs.length === 0) throw new Error(`No ${data.model} found matching criteria`);
1176
- if (countResult.docs.length > 1) throw new Error(`Multiple ${data.model} found matching criteria. Expected exactly 1.`);
1177
1209
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
1178
1210
  return await ctx.runMutation(authFunctions.updateOne, { input: {
1179
1211
  model: data.model,
@@ -1222,7 +1254,7 @@ const dbAdapter = (ctx, getAuthOptions, { authFunctions, debugLogs, schema }) =>
1222
1254
  const createClient = (config) => ({
1223
1255
  authFunctions: config.authFunctions,
1224
1256
  triggers: config.triggers,
1225
- adapter: (ctx, getAuthOptions) => isQueryCtx(ctx) ? dbAdapter(ctx, getAuthOptions, config) : httpAdapter(ctx, config)
1257
+ adapter: (ctx) => isQueryCtx(ctx) ? dbAdapter(ctx, config) : httpAdapter(ctx, config)
1226
1258
  });
1227
1259
 
1228
1260
  //#endregion
@@ -1333,17 +1365,23 @@ const createAuthRuntime = (config) => {
1333
1365
  const authDefinition = resolveGeneratedAuthDefinition(config.auth, getInvalidAuthDefinitionExportReason());
1334
1366
  const authFunctions = resolveAuthFunctions(config.internal, config.moduleName);
1335
1367
  const resolveRuntimeTriggers = (ctx) => authDefinition(ctx).triggers;
1368
+ let betterAuthSchema;
1369
+ const getBetterAuthSchema = () => {
1370
+ betterAuthSchema ??= getAuthTables(withoutTriggers(authDefinition({})));
1371
+ return betterAuthSchema;
1372
+ };
1336
1373
  const authClient = createClient({
1337
1374
  authFunctions,
1375
+ getBetterAuthSchema,
1338
1376
  schema: config.schema,
1339
1377
  ...config.context ? { context: config.context } : {},
1340
1378
  triggers: resolveRuntimeTriggers
1341
1379
  });
1342
- const adapterGetAuthOptions = ((ctx) => withoutTriggers(authDefinition(ctx)));
1343
- const resolveAuthOptions = (ctx) => withDatabase(withAuthDefaults(withoutTriggers(authDefinition(ctx))), ctx, (_ctx) => authClient.adapter(_ctx, adapterGetAuthOptions));
1380
+ const resolveAuthOptions = (ctx) => withDatabase(withAuthDefaults(withoutTriggers(authDefinition(ctx))), ctx, (_ctx) => authClient.adapter(_ctx));
1344
1381
  const getAuth = (ctx) => betterAuth(resolveAuthOptions(ctx));
1345
1382
  const decoratedAuthApi = decorateAuthRuntimeProcedures(createApi(config.schema, getAuth, {
1346
1383
  ...config.context ? { context: config.context } : {},
1384
+ getBetterAuthSchema,
1347
1385
  triggers: resolveRuntimeTriggers
1348
1386
  }));
1349
1387
  let staticAuth;
@@ -1,4 +1,4 @@
1
- import { t as getToken } from "../../token-oA2EMtPA.js";
1
+ import { t as getToken } from "../../token-xlpENMVn.js";
2
2
  import { n as defaultIsUnauthorized } from "../../error-Bvo7YEhk.js";
3
3
  import { t as createCallerFactory } from "../../caller-factory-DHywSoGZ.js";
4
4
 
@@ -1,4 +1,4 @@
1
- import { t as getToken } from "../../../token-oA2EMtPA.js";
1
+ import { t as getToken } from "../../../token-xlpENMVn.js";
2
2
  import { stripIndent } from "common-tags";
3
3
  import { getRequestHeaders } from "@tanstack/react-start/server";
4
4
  import { ConvexHttpClient } from "convex/browser";
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as createSystemFields, S as TableName, _ as getSchemaRelations, a as generateMeta, b as OrmSchemaExtensions, d as loadBabelParser, f as loadClackPrompts, g as isColorEnabled, h as highlighter, i as resolveConfiguredBackend, l as createProjectJiti, m as loadEsbuild, n as withLocalCodegenEnv, o as getConvexConfig, p as loadDotenv, r as loadCliConfig, s as logger, t as getLocalBackendEnvVars, u as CRPC_BUILDER_STUB_SOURCE, v as Columns, x as RlsPolicies, y as EnableRLS } from "./local-env-DykABjCt.mjs";
2
+ import { C as createSystemFields, S as TableName, _ as getSchemaRelations, a as generateMeta, b as OrmSchemaExtensions, d as loadBabelParser, f as loadClackPrompts, g as isColorEnabled, h as highlighter, i as resolveConfiguredBackend, l as createProjectJiti, m as loadEsbuild, n as withLocalCodegenEnv, o as getConvexConfig, p as loadDotenv, r as loadCliConfig, s as logger, t as getLocalBackendEnvVars, u as CRPC_BUILDER_STUB_SOURCE, v as Columns, x as RlsPolicies, y as EnableRLS } from "./local-env-yFKub75x.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import fs, { existsSync, readFileSync } from "node:fs";
5
5
  import path, { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve } from "node:path";
@@ -12998,8 +12998,10 @@ async function runAggregatePruneFlow(params) {
12998
12998
  if (result.exitCode !== 0) return result.exitCode;
12999
12999
  const payload = parseBackendRunJson(result.stdout);
13000
13000
  const pruned = typeof payload === "object" && payload !== null && !Array.isArray(payload) && typeof payload.pruned === "number" ? payload.pruned : 0;
13001
+ const pruning = typeof payload === "object" && payload !== null && !Array.isArray(payload) && typeof payload.pruning === "number" ? payload.pruning : 0;
13001
13002
  if (pruned > 0) logger.info(`aggregateBackfill pruned ${pruned} removed indexes`);
13002
- else logger.info("aggregateBackfill prune no-op");
13003
+ else if (pruning === 0) logger.info("aggregateBackfill prune no-op");
13004
+ if (pruning > 0) logger.info(`aggregateBackfill is clearing ${pruning} removed indexes in scheduled batches`);
13003
13005
  return 0;
13004
13006
  }
13005
13007
  function slugifyMigrationName(name) {
@@ -28,16 +28,26 @@ const parseAuthConfig = (authConfig, opts) => {
28
28
  if (!isDataUriJwks && opts.jwks) console.warn("Static JWKS provided to Convex plugin, but not to auth config. This adds an unnecessary network request for token verification.");
29
29
  return providerConfig;
30
30
  };
31
- const convex = (opts) => {
32
- const jwtExpirationSeconds = opts.jwt?.expirationSeconds ?? opts.jwtExpirationSeconds ?? 900;
31
+ const oidcProviderCache = /* @__PURE__ */ new Map();
32
+ const getOidcProvider = (basePath) => {
33
+ const siteUrl = `${process.env.CONVEX_SITE_URL}`;
34
+ const key = `${siteUrl}|${basePath}`;
35
+ const cached = oidcProviderCache.get(key);
36
+ if (cached) return cached;
33
37
  const oidcProvider$1 = oidcProvider({
34
38
  loginPage: "/not-used",
35
39
  metadata: {
36
- issuer: `${process.env.CONVEX_SITE_URL}`,
37
- jwks_uri: `${process.env.CONVEX_SITE_URL}${opts.options?.basePath ?? "/api/auth"}/convex/jwks`
40
+ issuer: siteUrl,
41
+ jwks_uri: `${siteUrl}${basePath}/convex/jwks`
38
42
  },
39
43
  __skipDeprecationWarning: true
40
44
  });
45
+ oidcProviderCache.set(key, oidcProvider$1);
46
+ return oidcProvider$1;
47
+ };
48
+ const convex = (opts) => {
49
+ const jwtExpirationSeconds = opts.jwt?.expirationSeconds ?? opts.jwtExpirationSeconds ?? 900;
50
+ const oidcProvider = getOidcProvider(opts.options?.basePath ?? "/api/auth");
41
51
  const providerConfig = parseAuthConfig(opts.authConfig, opts);
42
52
  const jwtOptions = {
43
53
  jwt: {
@@ -128,7 +138,7 @@ const convex = (opts) => {
128
138
  })
129
139
  }],
130
140
  after: [
131
- ...normalizeAfterHooks(oidcProvider$1.hooks.after),
141
+ ...normalizeAfterHooks(oidcProvider.hooks.after),
132
142
  {
133
143
  matcher: (ctx) => {
134
144
  return Boolean(ctx.path?.startsWith("/sign-in") || ctx.path?.startsWith("/sign-up") || ctx.path?.startsWith("/callback") || ctx.path?.startsWith("/oauth2/callback") || ctx.path?.startsWith("/magic-link/verify") || ctx.path?.startsWith("/email-otp/verify-email") || ctx.path?.startsWith("/phone-number/verify") || ctx.path?.startsWith("/siwe/verify") || ctx.path?.startsWith("/update-session") || ctx.path?.startsWith("/get-session") && ctx.context.session);
@@ -167,7 +177,7 @@ const convex = (opts) => {
167
177
  method: "GET",
168
178
  metadata: { isAction: false }
169
179
  }, async (ctx) => {
170
- return await oidcProvider$1.endpoints.getOpenIdConfig({
180
+ return await oidcProvider.endpoints.getOpenIdConfig({
171
181
  ...ctx,
172
182
  asResponse: false,
173
183
  returnHeaders: false,
@@ -1,9 +1,11 @@
1
1
  import { t as GetAuth } from "./types-BCl8gfGy.js";
2
2
  import { t as GenericCtx } from "./context-utils-BBUtBqjN.js";
3
3
  import * as convex_server0 from "convex/server";
4
- import { DocumentByName, FunctionReference, GenericDataModel, GenericMutationCtx, GenericSchema, SchemaDefinition, TableNamesInDataModel, internalMutationGeneric } from "convex/server";
4
+ import { DocumentByName, FunctionReference, GenericDataModel, GenericMutationCtx, GenericSchema, PaginationResult, SchemaDefinition, TableNamesInDataModel, internalMutationGeneric } from "convex/server";
5
5
  import * as better_auth_adapters0 from "better-auth/adapters";
6
+ import { BetterAuthDBSchema, getAuthTables } from "better-auth/db";
6
7
  import { BetterAuthOptions } from "better-auth/minimal";
8
+ import * as better_auth0 from "better-auth";
7
9
  import { Auth } from "better-auth";
8
10
 
9
11
  //#region src/auth/define-auth.d.ts
@@ -101,7 +103,7 @@ declare const findManyHandler: (ctx: any, args: {
101
103
  field: string;
102
104
  };
103
105
  where?: any[];
104
- }, schema: Schema, betterAuthSchema: any) => Promise<convex_server0.PaginationResult<convex_server0.GenericDocument>>;
106
+ }, schema: Schema, betterAuthSchema: any) => Promise<PaginationResult<convex_server0.GenericDocument>>;
105
107
  declare const updateOneHandler: (ctx: any, args: {
106
108
  input: {
107
109
  model: string;
@@ -155,6 +157,12 @@ declare const deleteManyHandler: (ctx: any, args: {
155
157
  declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel extends GenericDataModel = GenericDataModel, Ctx = unknown, TriggerCtx = Ctx, Auth = unknown>(schema: Schema, getAuth: GetAuth<Ctx, Auth>, options?: {
156
158
  internalMutation?: typeof internalMutationGeneric;
157
159
  context?: (ctx: any) => TriggerCtx | Promise<TriggerCtx>;
160
+ /**
161
+ * Ctx-free Better Auth table schema, memoized by the caller. Supplying it
162
+ * lets the runtime share one derivation with the db adapter instead of
163
+ * building a throwaway auth instance just to read `.options`.
164
+ */
165
+ getBetterAuthSchema?: () => ReturnType<typeof getAuthTables>;
158
166
  triggers?: GenericAuthTriggers<DataModel, Schema, TriggerCtx> | ((ctx: TriggerCtx) => GenericAuthTriggers<DataModel, Schema, TriggerCtx> | undefined); /** Validate input validators against auth table schemas. Defaults to false for smaller generated types. */
159
167
  validateInput?: boolean;
160
168
  }) => {
@@ -239,7 +247,7 @@ declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel e
239
247
  numItems: number;
240
248
  cursor: string | null;
241
249
  };
242
- }, Promise<convex_server0.PaginationResult<convex_server0.GenericDocument>>>;
250
+ }, Promise<PaginationResult<convex_server0.GenericDocument>>>;
243
251
  findOne: convex_server0.RegisteredQuery<"internal", {
244
252
  join?: any;
245
253
  select?: string[] | undefined;
@@ -325,13 +333,18 @@ type Triggers<DataModel extends GenericDataModel, Schema extends SchemaDefinitio
325
333
  type TriggerResolver<DataModel extends GenericDataModel, Schema extends SchemaDefinition<any, any>, TriggerCtx extends GenericMutationCtx<DataModel> = GenericMutationCtx<DataModel>> = Triggers<DataModel, Schema, TriggerCtx> | ((ctx: TriggerCtx) => Triggers<DataModel, Schema, TriggerCtx> | undefined);
326
334
  declare const createClient: <DataModel extends GenericDataModel, Schema extends SchemaDefinition<GenericSchema, true>, TriggerCtx extends GenericMutationCtx<DataModel> = GenericMutationCtx<DataModel>>(config: {
327
335
  authFunctions: AuthFunctions;
336
+ /**
337
+ * Ctx-free Better Auth table schema, memoized by the caller. Resolved lazily
338
+ * on the first db-adapter construction, never at module scope.
339
+ */
340
+ getBetterAuthSchema: () => BetterAuthDBSchema;
328
341
  schema: Schema;
329
342
  context?: (ctx: GenericMutationCtx<DataModel>) => TriggerCtx | Promise<TriggerCtx>;
330
343
  triggers?: TriggerResolver<DataModel, Schema, TriggerCtx>;
331
344
  }) => {
332
345
  authFunctions: AuthFunctions;
333
346
  triggers: TriggerResolver<DataModel, Schema, TriggerCtx> | undefined;
334
- adapter: (ctx: GenericCtx<DataModel>, getAuthOptions: (ctx: any) => BetterAuthOptions) => better_auth_adapters0.AdapterFactory<BetterAuthOptions>;
347
+ adapter: (ctx: GenericCtx<DataModel>) => better_auth_adapters0.AdapterFactory<better_auth0.BetterAuthOptions>;
335
348
  };
336
349
  //#endregion
337
350
  //#region src/auth/generated-contract-disabled.d.ts