convex-zen 0.0.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 (142) hide show
  1. package/dist/cli/generate.d.ts +14 -0
  2. package/dist/cli/generate.d.ts.map +1 -0
  3. package/dist/cli/generate.js +297 -0
  4. package/dist/cli/generate.js.map +1 -0
  5. package/dist/cli/index.d.ts +3 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +111 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/client/index.d.ts +300 -0
  10. package/dist/client/index.d.ts.map +1 -0
  11. package/dist/client/index.js +434 -0
  12. package/dist/client/index.js.map +1 -0
  13. package/dist/client/plugins/admin.d.ts +92 -0
  14. package/dist/client/plugins/admin.d.ts.map +1 -0
  15. package/dist/client/plugins/admin.js +165 -0
  16. package/dist/client/plugins/admin.js.map +1 -0
  17. package/dist/client/primitives.d.ts +57 -0
  18. package/dist/client/primitives.d.ts.map +1 -0
  19. package/dist/client/primitives.js +64 -0
  20. package/dist/client/primitives.js.map +1 -0
  21. package/dist/client/providers.d.ts +14 -0
  22. package/dist/client/providers.d.ts.map +1 -0
  23. package/dist/client/providers.js +25 -0
  24. package/dist/client/providers.js.map +1 -0
  25. package/dist/client/react.d.ts +23 -0
  26. package/dist/client/react.d.ts.map +1 -0
  27. package/dist/client/react.js +48 -0
  28. package/dist/client/react.js.map +1 -0
  29. package/dist/client/tanstack-start-client-plugins.d.ts +34 -0
  30. package/dist/client/tanstack-start-client-plugins.d.ts.map +1 -0
  31. package/dist/client/tanstack-start-client-plugins.js +32 -0
  32. package/dist/client/tanstack-start-client-plugins.js.map +1 -0
  33. package/dist/client/tanstack-start-client.d.ts +52 -0
  34. package/dist/client/tanstack-start-client.d.ts.map +1 -0
  35. package/dist/client/tanstack-start-client.js +130 -0
  36. package/dist/client/tanstack-start-client.js.map +1 -0
  37. package/dist/client/tanstack-start-plugins.d.ts +27 -0
  38. package/dist/client/tanstack-start-plugins.d.ts.map +1 -0
  39. package/dist/client/tanstack-start-plugins.js +145 -0
  40. package/dist/client/tanstack-start-plugins.js.map +1 -0
  41. package/dist/client/tanstack-start.d.ts +130 -0
  42. package/dist/client/tanstack-start.d.ts.map +1 -0
  43. package/dist/client/tanstack-start.js +331 -0
  44. package/dist/client/tanstack-start.js.map +1 -0
  45. package/dist/component/_generated/api.d.ts +50 -0
  46. package/dist/component/_generated/api.d.ts.map +1 -0
  47. package/dist/component/_generated/api.js +31 -0
  48. package/dist/component/_generated/api.js.map +1 -0
  49. package/dist/component/_generated/component.d.ts +92 -0
  50. package/dist/component/_generated/component.d.ts.map +1 -0
  51. package/dist/component/_generated/component.js +11 -0
  52. package/dist/component/_generated/component.js.map +1 -0
  53. package/dist/component/_generated/dataModel.d.ts +46 -0
  54. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  55. package/dist/component/_generated/dataModel.js +11 -0
  56. package/dist/component/_generated/dataModel.js.map +1 -0
  57. package/dist/component/_generated/server.d.ts +121 -0
  58. package/dist/component/_generated/server.d.ts.map +1 -0
  59. package/dist/component/_generated/server.js +78 -0
  60. package/dist/component/_generated/server.js.map +1 -0
  61. package/dist/component/convex.config.d.ts +3 -0
  62. package/dist/component/convex.config.d.ts.map +1 -0
  63. package/dist/component/convex.config.js +4 -0
  64. package/dist/component/convex.config.js.map +1 -0
  65. package/dist/component/core/sessions.d.ts +33 -0
  66. package/dist/component/core/sessions.d.ts.map +1 -0
  67. package/dist/component/core/sessions.js +186 -0
  68. package/dist/component/core/sessions.js.map +1 -0
  69. package/dist/component/core/users.d.ts +19 -0
  70. package/dist/component/core/users.d.ts.map +1 -0
  71. package/dist/component/core/users.js +154 -0
  72. package/dist/component/core/users.js.map +1 -0
  73. package/dist/component/core/verifications.d.ts +34 -0
  74. package/dist/component/core/verifications.d.ts.map +1 -0
  75. package/dist/component/core/verifications.js +135 -0
  76. package/dist/component/core/verifications.js.map +1 -0
  77. package/dist/component/gateway.d.ts +16 -0
  78. package/dist/component/gateway.d.ts.map +1 -0
  79. package/dist/component/gateway.js +229 -0
  80. package/dist/component/gateway.js.map +1 -0
  81. package/dist/component/lib/crypto.d.ts +24 -0
  82. package/dist/component/lib/crypto.d.ts.map +1 -0
  83. package/dist/component/lib/crypto.js +57 -0
  84. package/dist/component/lib/crypto.js.map +1 -0
  85. package/dist/component/lib/rateLimit.d.ts +26 -0
  86. package/dist/component/lib/rateLimit.d.ts.map +1 -0
  87. package/dist/component/lib/rateLimit.js +96 -0
  88. package/dist/component/lib/rateLimit.js.map +1 -0
  89. package/dist/component/lib/validators.d.ts +19 -0
  90. package/dist/component/lib/validators.d.ts.map +1 -0
  91. package/dist/component/lib/validators.js +12 -0
  92. package/dist/component/lib/validators.js.map +1 -0
  93. package/dist/component/plugins/admin.d.ts +72 -0
  94. package/dist/component/plugins/admin.d.ts.map +1 -0
  95. package/dist/component/plugins/admin.js +152 -0
  96. package/dist/component/plugins/admin.js.map +1 -0
  97. package/dist/component/providers/emailPassword.d.ts +49 -0
  98. package/dist/component/providers/emailPassword.d.ts.map +1 -0
  99. package/dist/component/providers/emailPassword.js +316 -0
  100. package/dist/component/providers/emailPassword.js.map +1 -0
  101. package/dist/component/providers/oauth.d.ts +33 -0
  102. package/dist/component/providers/oauth.d.ts.map +1 -0
  103. package/dist/component/providers/oauth.js +256 -0
  104. package/dist/component/providers/oauth.js.map +1 -0
  105. package/dist/component/schema.d.ts +132 -0
  106. package/dist/component/schema.d.ts.map +1 -0
  107. package/dist/component/schema.js +82 -0
  108. package/dist/component/schema.js.map +1 -0
  109. package/dist/types.d.ts +67 -0
  110. package/dist/types.d.ts.map +1 -0
  111. package/dist/types.js +5 -0
  112. package/dist/types.js.map +1 -0
  113. package/package.json +121 -0
  114. package/src/cli/generate.ts +360 -0
  115. package/src/cli/index.ts +133 -0
  116. package/src/client/index.ts +707 -0
  117. package/src/client/plugins/admin.ts +205 -0
  118. package/src/client/primitives.ts +100 -0
  119. package/src/client/providers.ts +35 -0
  120. package/src/client/react.ts +97 -0
  121. package/src/client/tanstack-start-client-plugins.ts +113 -0
  122. package/src/client/tanstack-start-client.ts +259 -0
  123. package/src/client/tanstack-start-plugins.ts +203 -0
  124. package/src/client/tanstack-start.ts +535 -0
  125. package/src/component/_generated/api.ts +70 -0
  126. package/src/component/_generated/component.ts +184 -0
  127. package/src/component/_generated/dataModel.ts +60 -0
  128. package/src/component/_generated/server.ts +156 -0
  129. package/src/component/convex.config.ts +5 -0
  130. package/src/component/core/sessions.ts +228 -0
  131. package/src/component/core/users.ts +199 -0
  132. package/src/component/core/verifications.ts +173 -0
  133. package/src/component/gateway.ts +321 -0
  134. package/src/component/lib/crypto.ts +63 -0
  135. package/src/component/lib/internalApi.ts +66 -0
  136. package/src/component/lib/rateLimit.ts +111 -0
  137. package/src/component/lib/validators.ts +12 -0
  138. package/src/component/plugins/admin.ts +178 -0
  139. package/src/component/providers/emailPassword.ts +374 -0
  140. package/src/component/providers/oauth.ts +324 -0
  141. package/src/component/schema.ts +88 -0
  142. package/src/types.ts +68 -0
@@ -0,0 +1,535 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ import type {
3
+ FunctionArgs,
4
+ FunctionReference,
5
+ FunctionReturnType,
6
+ } from "convex/server";
7
+ import type {
8
+ deleteCookie as deleteCookieFn,
9
+ setCookie as setCookieFn,
10
+ } from "@tanstack/react-start/server";
11
+ import {
12
+ createSessionPrimitives,
13
+ type SessionInfo,
14
+ type SessionPrimitives,
15
+ type SignInInput,
16
+ type SignInOutput,
17
+ } from "./primitives";
18
+
19
+ type SetCookieOptions = Exclude<Parameters<typeof setCookieFn>[2], undefined>;
20
+ type DeleteCookieOptions = Exclude<
21
+ Parameters<typeof deleteCookieFn>[1],
22
+ undefined
23
+ >;
24
+
25
+ export interface TanStackStartAuthOptions {
26
+ primitives: SessionPrimitives;
27
+ cookieName?: string;
28
+ cookieOptions?: Partial<SetCookieOptions>;
29
+ }
30
+
31
+ export interface TanStackStartConvexActions {
32
+ /** Email/password sign-in action. */
33
+ signInWithEmail: FunctionReference<"action", "public">;
34
+ validateSession: FunctionReference<"action", "public">;
35
+ signOut: FunctionReference<"action", "public">;
36
+ }
37
+
38
+ export interface TanStackStartConvexAuthOptions {
39
+ convexUrl: string;
40
+ actions: TanStackStartConvexActions;
41
+ cookieName?: string;
42
+ cookieOptions?: Partial<SetCookieOptions>;
43
+ }
44
+
45
+ export interface TanStackStartConvexReactStartOptions
46
+ extends TanStackStartConvexAuthOptions {
47
+ authApiBasePath?: string;
48
+ plugins?: readonly TanStackStartAuthApiPluginFactory[];
49
+ }
50
+
51
+ export interface AuthenticatedSession {
52
+ token: string;
53
+ session: SessionInfo;
54
+ }
55
+
56
+ export interface TanStackStartAuth {
57
+ getSession: () => Promise<SessionInfo | null>;
58
+ getToken: () => Promise<string | null>;
59
+ signIn: (input: SignInInput) => Promise<SessionInfo>;
60
+ signOut: () => Promise<void>;
61
+ requireSession: () => Promise<AuthenticatedSession>;
62
+ withSession: <T>(
63
+ handler: (auth: AuthenticatedSession) => Promise<T>
64
+ ) => Promise<T>;
65
+ }
66
+
67
+ export interface TanStackStartSessionHandlers {
68
+ getSession: () => Promise<SessionInfo | null>;
69
+ getToken: () => Promise<string | null>;
70
+ requireSession: () => Promise<AuthenticatedSession>;
71
+ withSession: <T>(
72
+ handler: (auth: AuthenticatedSession) => Promise<T>
73
+ ) => Promise<T>;
74
+ }
75
+
76
+ export interface TanStackStartAuthHandlers {
77
+ signInWithEmail: (input: SignInInput) => Promise<SessionInfo>;
78
+ signOut: () => Promise<void>;
79
+ }
80
+
81
+ export interface TanStackStartAuthApiHandlerOptions {
82
+ tanstackAuth: Pick<TanStackStartAuth, "getSession" | "signIn" | "signOut">;
83
+ basePath?: string;
84
+ plugins?: readonly TanStackStartAuthApiPlugin[];
85
+ }
86
+
87
+ export interface TanStackStartAuthApiPluginContext {
88
+ request: Request;
89
+ method: string;
90
+ action: string;
91
+ readJson: () => Promise<unknown>;
92
+ json: (data: unknown, status?: number) => Response;
93
+ }
94
+
95
+ export interface TanStackStartAuthApiPlugin {
96
+ id: string;
97
+ handle: (
98
+ context: TanStackStartAuthApiPluginContext
99
+ ) => Promise<Response | null> | Response | null;
100
+ }
101
+
102
+ export interface TanStackStartAuthApiPluginFactoryContext {
103
+ tanstackAuth: TanStackAuthForPluginFactory;
104
+ fetchers: TanStackStartConvexFetchers;
105
+ }
106
+
107
+ type TanStackAuthForPluginFactory = Pick<
108
+ TanStackStartAuth,
109
+ "getSession" | "getToken" | "signIn" | "signOut" | "requireSession" | "withSession"
110
+ >;
111
+
112
+ export interface TanStackStartAuthApiPluginFactory {
113
+ id: string;
114
+ create: (
115
+ context: TanStackStartAuthApiPluginFactoryContext
116
+ ) => TanStackStartAuthApiPlugin;
117
+ }
118
+
119
+ export interface TanStackStartConvexFetchers {
120
+ fetchAuthQuery: <Query extends FunctionReference<"query", "public">>(
121
+ fn: Query,
122
+ args: FunctionArgs<Query>
123
+ ) => Promise<FunctionReturnType<Query>>;
124
+ fetchAuthMutation: <Mutation extends FunctionReference<"mutation", "public">>(
125
+ fn: Mutation,
126
+ args: FunctionArgs<Mutation>
127
+ ) => Promise<FunctionReturnType<Mutation>>;
128
+ fetchAuthAction: <Action extends FunctionReference<"action", "public">>(
129
+ fn: Action,
130
+ args: FunctionArgs<Action>
131
+ ) => Promise<FunctionReturnType<Action>>;
132
+ }
133
+
134
+ export interface TanStackStartConvexFetchersOptions {
135
+ tanstackAuth: Pick<TanStackStartAuth, "requireSession">;
136
+ convexUrl: string;
137
+ }
138
+
139
+ export interface TanStackStartConvexReactStart
140
+ extends TanStackStartAuth,
141
+ TanStackStartConvexFetchers {
142
+ handler: (request: Request) => Promise<Response>;
143
+ }
144
+
145
+ function resolveCookieOptions(
146
+ options?: Partial<SetCookieOptions>
147
+ ): SetCookieOptions {
148
+ return {
149
+ path: "/",
150
+ httpOnly: true,
151
+ sameSite: "lax",
152
+ secure: false,
153
+ ...options,
154
+ };
155
+ }
156
+
157
+ type AuthApiErrorPayload = { error?: string };
158
+
159
+ function json(data: unknown, status = 200): Response {
160
+ return new Response(JSON.stringify(data), {
161
+ status,
162
+ headers: { "content-type": "application/json; charset=utf-8" },
163
+ });
164
+ }
165
+
166
+ function normalizeBasePath(path: string): string {
167
+ const normalized = path.trim();
168
+ if (normalized === "") {
169
+ return "/api/auth";
170
+ }
171
+ const withLeadingSlash = normalized.startsWith("/")
172
+ ? normalized
173
+ : `/${normalized}`;
174
+ if (withLeadingSlash.length > 1 && withLeadingSlash.endsWith("/")) {
175
+ return withLeadingSlash.slice(0, -1);
176
+ }
177
+ return withLeadingSlash;
178
+ }
179
+
180
+ function resolveActionFromPath(pathname: string, basePath: string): string | null {
181
+ if (pathname === basePath) {
182
+ return null;
183
+ }
184
+ const expectedPrefix = `${basePath}/`;
185
+ if (!pathname.startsWith(expectedPrefix)) {
186
+ return null;
187
+ }
188
+ const action = pathname.slice(expectedPrefix.length);
189
+ return action.length > 0 ? action : null;
190
+ }
191
+
192
+ function readClientIp(request: Request): string | undefined {
193
+ const forwarded = request.headers.get("x-forwarded-for");
194
+ if (typeof forwarded === "string" && forwarded.length > 0) {
195
+ const [first] = forwarded.split(",");
196
+ const ip = first?.trim();
197
+ if (ip) {
198
+ return ip;
199
+ }
200
+ }
201
+ const realIp = request.headers.get("x-real-ip");
202
+ return realIp && realIp.length > 0 ? realIp : undefined;
203
+ }
204
+
205
+ function toErrorMessage(error: unknown): string {
206
+ if (error instanceof Error) {
207
+ return error.message;
208
+ }
209
+ if (
210
+ error &&
211
+ typeof error === "object" &&
212
+ "error" in error &&
213
+ typeof (error as AuthApiErrorPayload).error === "string"
214
+ ) {
215
+ return (error as AuthApiErrorPayload).error as string;
216
+ }
217
+ return "Authentication request failed";
218
+ }
219
+
220
+ /**
221
+ * TanStack Start adapter.
222
+ *
223
+ * Exposes server-side auth handlers that read/write an HttpOnly session cookie
224
+ * and delegate auth logic to framework-agnostic SessionPrimitives.
225
+ *
226
+ * These handlers are meant to be wrapped by route-local `createServerFn(...)`
227
+ * so TanStack's server function transform runs in application code.
228
+ */
229
+ export function createTanStackStartAuth(
230
+ options: TanStackStartAuthOptions
231
+ ): TanStackStartAuth {
232
+ const cookieName = options.cookieName ?? "cz_session";
233
+ const cookieOptions = resolveCookieOptions(options.cookieOptions);
234
+ const clearCookieOptions: DeleteCookieOptions = {
235
+ path: cookieOptions.path ?? "/",
236
+ };
237
+ const unauthorizedError = () => new Error("Unauthorized");
238
+ const getCookieToken = async () => {
239
+ const { getCookie } = await import("@tanstack/react-start/server");
240
+ return getCookie(cookieName);
241
+ };
242
+
243
+ const resolveAuthenticatedSession = async (): Promise<AuthenticatedSession | null> => {
244
+ const { deleteCookie } = await import("@tanstack/react-start/server");
245
+ const token = await getCookieToken();
246
+ if (!token) {
247
+ return null;
248
+ }
249
+
250
+ const session = await options.primitives.getSessionFromToken(token);
251
+ if (!session) {
252
+ deleteCookie(cookieName, clearCookieOptions);
253
+ return null;
254
+ }
255
+
256
+ return { token, session };
257
+ };
258
+
259
+ const getSession = async () => {
260
+ return (await resolveAuthenticatedSession())?.session ?? null;
261
+ };
262
+
263
+ const getToken = async () => {
264
+ return (await resolveAuthenticatedSession())?.token ?? null;
265
+ };
266
+
267
+ const signIn = async (input: SignInInput) => {
268
+ const { setCookie } = await import("@tanstack/react-start/server");
269
+ const established = await options.primitives.signInAndResolveSession(input);
270
+ setCookie(cookieName, established.sessionToken, cookieOptions);
271
+ return established.session;
272
+ };
273
+
274
+ const signOut = async () => {
275
+ const { deleteCookie } = await import("@tanstack/react-start/server");
276
+ const token = await getCookieToken();
277
+ try {
278
+ await options.primitives.signOutByToken(token);
279
+ } finally {
280
+ deleteCookie(cookieName, clearCookieOptions);
281
+ }
282
+ };
283
+
284
+ const requireSession = async (): Promise<AuthenticatedSession> => {
285
+ const authenticated = await resolveAuthenticatedSession();
286
+ if (!authenticated) {
287
+ throw unauthorizedError();
288
+ }
289
+ return authenticated;
290
+ };
291
+
292
+ const withSession = async <T>(
293
+ handler: (auth: AuthenticatedSession) => Promise<T>
294
+ ): Promise<T> => {
295
+ return handler(await requireSession());
296
+ };
297
+
298
+ return {
299
+ getSession,
300
+ getToken,
301
+ signIn,
302
+ signOut,
303
+ requireSession,
304
+ withSession,
305
+ };
306
+ }
307
+
308
+ /** Build route-friendly session handlers from a TanStack auth instance. */
309
+ export function createTanStackStartSessionHandlers(
310
+ tanstackAuth: TanStackStartAuth
311
+ ): TanStackStartSessionHandlers {
312
+ return {
313
+ getSession: async () => tanstackAuth.getSession(),
314
+ getToken: async () => tanstackAuth.getToken(),
315
+ requireSession: async () => tanstackAuth.requireSession(),
316
+ withSession: async <T>(handler: (auth: AuthenticatedSession) => Promise<T>) =>
317
+ tanstackAuth.withSession(handler),
318
+ };
319
+ }
320
+
321
+ /** Build route-friendly auth mutation handlers from a TanStack auth instance. */
322
+ export function createTanStackStartAuthHandlers(
323
+ tanstackAuth: TanStackStartAuth
324
+ ): TanStackStartAuthHandlers {
325
+ return {
326
+ signInWithEmail: async (input) => tanstackAuth.signIn(input),
327
+ signOut: async () => tanstackAuth.signOut(),
328
+ };
329
+ }
330
+
331
+ /**
332
+ * Build a Request handler for `/api/auth/*` routes in TanStack Start.
333
+ *
334
+ * Supported actions:
335
+ * - `GET /api/auth/session`
336
+ * - `POST /api/auth/sign-in-with-email`
337
+ * - `POST /api/auth/sign-out`
338
+ */
339
+ export function createTanStackStartAuthApiHandler(
340
+ options: TanStackStartAuthApiHandlerOptions
341
+ ): (request: Request) => Promise<Response> {
342
+ const basePath = normalizeBasePath(options.basePath ?? "/api/auth");
343
+ const plugins = options.plugins ?? [];
344
+
345
+ return async (request: Request): Promise<Response> => {
346
+ const action = resolveActionFromPath(new URL(request.url).pathname, basePath);
347
+ if (!action) {
348
+ return json({ error: "Not found" }, 404);
349
+ }
350
+ let parsedBody: Promise<unknown> | null = null;
351
+ const readRequestBody = async (): Promise<unknown> => {
352
+ if (!parsedBody) {
353
+ parsedBody = request.json().catch(() => null);
354
+ }
355
+ return parsedBody;
356
+ };
357
+
358
+ try {
359
+ if (request.method === "GET" && action === "session") {
360
+ const session = await options.tanstackAuth.getSession();
361
+ return json({ session });
362
+ }
363
+
364
+ if (request.method === "POST" && action === "sign-in-with-email") {
365
+ const body = (await readRequestBody()) as {
366
+ email?: unknown;
367
+ password?: unknown;
368
+ ipAddress?: unknown;
369
+ userAgent?: unknown;
370
+ };
371
+ if (
372
+ typeof body?.email !== "string" ||
373
+ typeof body?.password !== "string"
374
+ ) {
375
+ return json({ error: "Invalid request body" }, 400);
376
+ }
377
+
378
+ const signInInput: SignInInput = {
379
+ email: body.email,
380
+ password: body.password,
381
+ };
382
+ if (typeof body.ipAddress === "string" && body.ipAddress.length > 0) {
383
+ signInInput.ipAddress = body.ipAddress;
384
+ } else {
385
+ const requestIp = readClientIp(request);
386
+ if (requestIp) {
387
+ signInInput.ipAddress = requestIp;
388
+ }
389
+ }
390
+ if (typeof body.userAgent === "string" && body.userAgent.length > 0) {
391
+ signInInput.userAgent = body.userAgent;
392
+ } else {
393
+ const headerUserAgent = request.headers.get("user-agent");
394
+ if (headerUserAgent) {
395
+ signInInput.userAgent = headerUserAgent;
396
+ }
397
+ }
398
+
399
+ const session = await options.tanstackAuth.signIn(signInInput);
400
+ return json({ session });
401
+ }
402
+
403
+ if (request.method === "POST" && action === "sign-out") {
404
+ await options.tanstackAuth.signOut();
405
+ return json({ ok: true });
406
+ }
407
+
408
+ const pluginContext: TanStackStartAuthApiPluginContext = {
409
+ request,
410
+ method: request.method,
411
+ action,
412
+ readJson: readRequestBody,
413
+ json,
414
+ };
415
+ for (const plugin of plugins) {
416
+ const response = await plugin.handle(pluginContext);
417
+ if (response) {
418
+ return response;
419
+ }
420
+ }
421
+
422
+ return json({ error: "Not found" }, 404);
423
+ } catch (error) {
424
+ return json({ error: toErrorMessage(error) }, 400);
425
+ }
426
+ };
427
+ }
428
+
429
+ /**
430
+ * Build authenticated Convex fetch helpers for TanStack Start server functions.
431
+ * Each call resolves auth from cookies via `requireSession`.
432
+ */
433
+ export function createTanStackStartConvexFetchers(
434
+ options: TanStackStartConvexFetchersOptions
435
+ ): TanStackStartConvexFetchers {
436
+ const withAuthenticatedConvexClient = async <T>(
437
+ runner: (client: ConvexHttpClient) => Promise<T>
438
+ ): Promise<T> => {
439
+ const { token } = await options.tanstackAuth.requireSession();
440
+ const convex = new ConvexHttpClient(options.convexUrl);
441
+ convex.setAuth(token);
442
+ return runner(convex);
443
+ };
444
+
445
+ return {
446
+ fetchAuthQuery: async (fn, args) => {
447
+ return withAuthenticatedConvexClient((convex) => convex.query(fn, args));
448
+ },
449
+ fetchAuthMutation: async (fn, args) => {
450
+ return withAuthenticatedConvexClient((convex) => convex.mutation(fn, args));
451
+ },
452
+ fetchAuthAction: async (fn, args) => {
453
+ return withAuthenticatedConvexClient((convex) => convex.action(fn, args));
454
+ },
455
+ };
456
+ }
457
+
458
+ /**
459
+ * Build TanStack Start auth handlers directly from Convex public action refs.
460
+ *
461
+ * This removes transport boilerplate in app code while keeping session logic
462
+ * fully based on Convex functions.
463
+ */
464
+ export function createTanStackStartConvexAuth(
465
+ options: TanStackStartConvexAuthOptions
466
+ ): TanStackStartAuth {
467
+ const convex = new ConvexHttpClient(options.convexUrl);
468
+ const primitives = createSessionPrimitives({
469
+ signIn: async (input) => {
470
+ return (await convex.action(options.actions.signInWithEmail, input)) as SignInOutput;
471
+ },
472
+ validateSession: async (token) => {
473
+ return (await convex.action(options.actions.validateSession, {
474
+ token,
475
+ })) as SessionInfo | null;
476
+ },
477
+ signOut: async (token) => {
478
+ await convex.action(options.actions.signOut, { token });
479
+ },
480
+ });
481
+ const adapterOptions: TanStackStartAuthOptions = { primitives };
482
+ if (options.cookieName !== undefined) {
483
+ adapterOptions.cookieName = options.cookieName;
484
+ }
485
+ if (options.cookieOptions !== undefined) {
486
+ adapterOptions.cookieOptions = options.cookieOptions;
487
+ }
488
+ return createTanStackStartAuth(adapterOptions);
489
+ }
490
+
491
+ /**
492
+ * Better-auth-style TanStack Start helper for convex-zen.
493
+ *
494
+ * Returns a single object containing:
495
+ * - auth route `handler` (`/api/auth/*`)
496
+ * - token/session helpers (`getSession`, `getToken`, ...)
497
+ * - authenticated Convex fetchers (`fetchAuthQuery`, `fetchAuthMutation`, `fetchAuthAction`)
498
+ */
499
+ export function convexZenReactStart(
500
+ options: TanStackStartConvexReactStartOptions
501
+ ): TanStackStartConvexReactStart {
502
+ const tanstackAuth = createTanStackStartConvexAuth(options);
503
+ const fetchers = createTanStackStartConvexFetchers({
504
+ tanstackAuth,
505
+ convexUrl: options.convexUrl,
506
+ });
507
+ const authApiPlugins =
508
+ options.plugins?.map((plugin) =>
509
+ plugin.create({
510
+ tanstackAuth,
511
+ fetchers,
512
+ })
513
+ ) ?? [];
514
+ const authApiHandlerOptions: TanStackStartAuthApiHandlerOptions = {
515
+ tanstackAuth,
516
+ plugins: authApiPlugins,
517
+ };
518
+ if (options.authApiBasePath !== undefined) {
519
+ authApiHandlerOptions.basePath = options.authApiBasePath;
520
+ }
521
+ const handler = createTanStackStartAuthApiHandler(authApiHandlerOptions);
522
+
523
+ return {
524
+ getSession: tanstackAuth.getSession,
525
+ getToken: tanstackAuth.getToken,
526
+ signIn: tanstackAuth.signIn,
527
+ signOut: tanstackAuth.signOut,
528
+ requireSession: tanstackAuth.requireSession,
529
+ withSession: tanstackAuth.withSession,
530
+ fetchAuthQuery: fetchers.fetchAuthQuery,
531
+ fetchAuthMutation: fetchers.fetchAuthMutation,
532
+ fetchAuthAction: fetchers.fetchAuthAction,
533
+ handler,
534
+ };
535
+ }
@@ -0,0 +1,70 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * Generated `api` utility.
4
+ *
5
+ * THIS CODE IS AUTOMATICALLY GENERATED.
6
+ *
7
+ * To regenerate, run `npx convex dev`.
8
+ * @module
9
+ */
10
+
11
+ import type * as core_sessions from "../core/sessions.js";
12
+ import type * as core_users from "../core/users.js";
13
+ import type * as core_verifications from "../core/verifications.js";
14
+ import type * as gateway from "../gateway.js";
15
+ import type * as lib_crypto from "../lib/crypto.js";
16
+ import type * as lib_internalApi from "../lib/internalApi.js";
17
+ import type * as lib_rateLimit from "../lib/rateLimit.js";
18
+ import type * as lib_validators from "../lib/validators.js";
19
+ import type * as plugins_admin from "../plugins/admin.js";
20
+ import type * as providers_emailPassword from "../providers/emailPassword.js";
21
+ import type * as providers_oauth from "../providers/oauth.js";
22
+
23
+ import type {
24
+ ApiFromModules,
25
+ FilterApi,
26
+ FunctionReference,
27
+ } from "convex/server";
28
+ import { anyApi, componentsGeneric } from "convex/server";
29
+
30
+ const fullApi: ApiFromModules<{
31
+ "core/sessions": typeof core_sessions;
32
+ "core/users": typeof core_users;
33
+ "core/verifications": typeof core_verifications;
34
+ gateway: typeof gateway;
35
+ "lib/crypto": typeof lib_crypto;
36
+ "lib/internalApi": typeof lib_internalApi;
37
+ "lib/rateLimit": typeof lib_rateLimit;
38
+ "lib/validators": typeof lib_validators;
39
+ "plugins/admin": typeof plugins_admin;
40
+ "providers/emailPassword": typeof providers_emailPassword;
41
+ "providers/oauth": typeof providers_oauth;
42
+ }> = anyApi as any;
43
+
44
+ /**
45
+ * A utility for referencing Convex functions in your app's public API.
46
+ *
47
+ * Usage:
48
+ * ```js
49
+ * const myFunctionReference = api.myModule.myFunction;
50
+ * ```
51
+ */
52
+ export const api: FilterApi<
53
+ typeof fullApi,
54
+ FunctionReference<any, "public">
55
+ > = anyApi as any;
56
+
57
+ /**
58
+ * A utility for referencing Convex functions in your app's internal API.
59
+ *
60
+ * Usage:
61
+ * ```js
62
+ * const myFunctionReference = internal.myModule.myFunction;
63
+ * ```
64
+ */
65
+ export const internal: FilterApi<
66
+ typeof fullApi,
67
+ FunctionReference<any, "internal">
68
+ > = anyApi as any;
69
+
70
+ export const components = componentsGeneric() as unknown as {};