use-convex 0.0.2

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 (87) hide show
  1. package/LICENSE +2 -0
  2. package/README.md +321 -0
  3. package/dist/module.d.mts +97 -0
  4. package/dist/module.json +12 -0
  5. package/dist/module.mjs +232 -0
  6. package/dist/runtime/components/AuthLoading.d.vue.ts +13 -0
  7. package/dist/runtime/components/AuthLoading.vue +8 -0
  8. package/dist/runtime/components/AuthLoading.vue.d.ts +13 -0
  9. package/dist/runtime/components/AuthRefreshing.d.vue.ts +13 -0
  10. package/dist/runtime/components/AuthRefreshing.vue +8 -0
  11. package/dist/runtime/components/AuthRefreshing.vue.d.ts +13 -0
  12. package/dist/runtime/components/Authenticated.d.vue.ts +13 -0
  13. package/dist/runtime/components/Authenticated.vue +8 -0
  14. package/dist/runtime/components/Authenticated.vue.d.ts +13 -0
  15. package/dist/runtime/components/Unauthenticated.d.vue.ts +13 -0
  16. package/dist/runtime/components/Unauthenticated.vue +8 -0
  17. package/dist/runtime/components/Unauthenticated.vue.d.ts +13 -0
  18. package/dist/runtime/composables/prewarmQuery.d.ts +9 -0
  19. package/dist/runtime/composables/prewarmQuery.js +14 -0
  20. package/dist/runtime/composables/useAuth.d.ts +60 -0
  21. package/dist/runtime/composables/useAuth.js +426 -0
  22. package/dist/runtime/composables/useAuthToken.d.ts +24 -0
  23. package/dist/runtime/composables/useAuthToken.js +71 -0
  24. package/dist/runtime/composables/useConvex.d.ts +5 -0
  25. package/dist/runtime/composables/useConvex.js +10 -0
  26. package/dist/runtime/composables/useConvexAction.d.ts +13 -0
  27. package/dist/runtime/composables/useConvexAction.js +30 -0
  28. package/dist/runtime/composables/useConvexAuth.d.ts +51 -0
  29. package/dist/runtime/composables/useConvexAuth.js +96 -0
  30. package/dist/runtime/composables/useConvexConnectionState.d.ts +8 -0
  31. package/dist/runtime/composables/useConvexConnectionState.js +21 -0
  32. package/dist/runtime/composables/useConvexGate.d.ts +20 -0
  33. package/dist/runtime/composables/useConvexGate.js +21 -0
  34. package/dist/runtime/composables/useConvexMutation.d.ts +25 -0
  35. package/dist/runtime/composables/useConvexMutation.js +35 -0
  36. package/dist/runtime/composables/useConvexPaginatedQuery.d.ts +43 -0
  37. package/dist/runtime/composables/useConvexPaginatedQuery.js +188 -0
  38. package/dist/runtime/composables/useConvexQueries.d.ts +27 -0
  39. package/dist/runtime/composables/useConvexQueries.js +84 -0
  40. package/dist/runtime/composables/useConvexQuery.d.ts +56 -0
  41. package/dist/runtime/composables/useConvexQuery.js +147 -0
  42. package/dist/runtime/plugin.auth.client.d.ts +8 -0
  43. package/dist/runtime/plugin.auth.client.js +41 -0
  44. package/dist/runtime/plugin.client.d.ts +7 -0
  45. package/dist/runtime/plugin.client.js +40 -0
  46. package/dist/runtime/plugin.devtools.client.d.ts +6 -0
  47. package/dist/runtime/plugin.devtools.client.js +16 -0
  48. package/dist/runtime/plugin.server.d.ts +7 -0
  49. package/dist/runtime/plugin.server.js +44 -0
  50. package/dist/runtime/server/api/convex/auth/session.d.ts +36 -0
  51. package/dist/runtime/server/api/convex/auth/session.js +95 -0
  52. package/dist/runtime/server/auth.d.ts +19 -0
  53. package/dist/runtime/server/auth.js +32 -0
  54. package/dist/runtime/server/authCookies.d.ts +9 -0
  55. package/dist/runtime/server/authCookies.js +86 -0
  56. package/dist/runtime/server/fetch.d.ts +43 -0
  57. package/dist/runtime/server/fetch.js +63 -0
  58. package/dist/runtime/server/routes/__convex_devtools.get.d.ts +6 -0
  59. package/dist/runtime/server/routes/__convex_devtools.get.js +32 -0
  60. package/dist/runtime/server/sameOrigin.d.ts +14 -0
  61. package/dist/runtime/server/sameOrigin.js +33 -0
  62. package/dist/runtime/utils/authCookie.d.ts +21 -0
  63. package/dist/runtime/utils/authCookie.js +47 -0
  64. package/dist/runtime/utils/authGate.d.ts +11 -0
  65. package/dist/runtime/utils/authGate.js +9 -0
  66. package/dist/runtime/utils/authMutex.d.ts +15 -0
  67. package/dist/runtime/utils/authMutex.js +47 -0
  68. package/dist/runtime/utils/authState.d.ts +31 -0
  69. package/dist/runtime/utils/authState.js +35 -0
  70. package/dist/runtime/utils/authStorage.d.ts +55 -0
  71. package/dist/runtime/utils/authStorage.js +85 -0
  72. package/dist/runtime/utils/context.d.ts +53 -0
  73. package/dist/runtime/utils/context.js +41 -0
  74. package/dist/runtime/utils/fetchToken.d.ts +9 -0
  75. package/dist/runtime/utils/fetchToken.js +6 -0
  76. package/dist/runtime/utils/http.d.ts +10 -0
  77. package/dist/runtime/utils/http.js +13 -0
  78. package/dist/runtime/utils/oauthRedirect.d.ts +5 -0
  79. package/dist/runtime/utils/oauthRedirect.js +12 -0
  80. package/dist/runtime/utils/overlay.d.ts +18 -0
  81. package/dist/runtime/utils/overlay.js +48 -0
  82. package/dist/runtime/utils/paginatedOptimistic.d.ts +37 -0
  83. package/dist/runtime/utils/paginatedOptimistic.js +178 -0
  84. package/dist/runtime/utils/queryKey.d.ts +8 -0
  85. package/dist/runtime/utils/queryKey.js +11 -0
  86. package/dist/types.d.mts +29 -0
  87. package/package.json +66 -0
@@ -0,0 +1,86 @@
1
+ import {
2
+ deleteCookie,
3
+ getCookie,
4
+ getRequestHeader,
5
+ setCookie
6
+ } from "h3";
7
+ import { useRuntimeConfig } from "nitropack/runtime";
8
+ import {
9
+ AUTH_JWT_COOKIE_MAX_AGE,
10
+ isHttpOnlyAuth,
11
+ resolveAuthCookieName,
12
+ resolveAuthPresentCookieName,
13
+ resolveAuthRefreshCookieName
14
+ } from "../utils/authStorage.js";
15
+ function readAuthConfig(event) {
16
+ try {
17
+ const config = useRuntimeConfig(event);
18
+ return config.public?.convex?.auth;
19
+ } catch {
20
+ return void 0;
21
+ }
22
+ }
23
+ function isLocalHost(event) {
24
+ const host = getRequestHeader(event, "host") ?? "";
25
+ return host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]");
26
+ }
27
+ function httpOnlyCookieOptions(event) {
28
+ const local = isLocalHost(event);
29
+ return {
30
+ httpOnly: true,
31
+ secure: !local,
32
+ sameSite: "lax",
33
+ path: "/",
34
+ maxAge: AUTH_JWT_COOKIE_MAX_AGE
35
+ };
36
+ }
37
+ function readableCookieOptions(event) {
38
+ const local = isLocalHost(event);
39
+ return {
40
+ httpOnly: false,
41
+ secure: !local,
42
+ sameSite: "lax",
43
+ path: "/",
44
+ maxAge: AUTH_JWT_COOKIE_MAX_AGE
45
+ };
46
+ }
47
+ export function setAuthCookies(event, tokens) {
48
+ const auth = readAuthConfig(event);
49
+ if (!isHttpOnlyAuth(auth)) {
50
+ throw new Error(
51
+ "[convex-nuxt] setAuthCookies requires convex.auth.httpOnly: true"
52
+ );
53
+ }
54
+ const jwtName = resolveAuthCookieName(auth);
55
+ const refreshName = resolveAuthRefreshCookieName(auth);
56
+ const presentName = resolveAuthPresentCookieName(auth);
57
+ const httpOpts = httpOnlyCookieOptions(event);
58
+ const presentOpts = readableCookieOptions(event);
59
+ if (!tokens.token) {
60
+ deleteCookie(event, jwtName, httpOpts);
61
+ deleteCookie(event, refreshName, httpOpts);
62
+ deleteCookie(event, presentName, presentOpts);
63
+ return;
64
+ }
65
+ setCookie(event, jwtName, tokens.token, httpOpts);
66
+ if (tokens.refreshToken) {
67
+ setCookie(event, refreshName, tokens.refreshToken, httpOpts);
68
+ }
69
+ setCookie(event, presentName, "1", presentOpts);
70
+ }
71
+ export function readHttpOnlyJwt(event) {
72
+ const auth = readAuthConfig(event);
73
+ const name = resolveAuthCookieName(auth);
74
+ if (!name) {
75
+ return void 0;
76
+ }
77
+ return getCookie(event, name) || void 0;
78
+ }
79
+ export function readHttpOnlyRefresh(event) {
80
+ const auth = readAuthConfig(event);
81
+ const name = resolveAuthRefreshCookieName(auth);
82
+ if (!name) {
83
+ return void 0;
84
+ }
85
+ return getCookie(event, name) || void 0;
86
+ }
@@ -0,0 +1,43 @@
1
+ import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server';
2
+ import type { H3Event } from 'h3';
3
+ import { resolveFetchToken } from '../utils/fetchToken.js';
4
+ export { resolveFetchToken };
5
+ export interface ConvexFetchOptions {
6
+ /**
7
+ * Deployment URL. Defaults to `NUXT_PUBLIC_CONVEX_URL` / runtimeConfig.
8
+ */
9
+ url?: string;
10
+ /**
11
+ * JWT for this call. Prefer per-request tokens — never put JWTs in public config.
12
+ * When omitted and `event` is passed, falls back to `convex.auth.cookie` if set.
13
+ */
14
+ token?: string;
15
+ /**
16
+ * Nitro/H3 event. Used to resolve runtimeConfig and the optional auth cookie.
17
+ */
18
+ event?: H3Event;
19
+ skipConvexDeploymentUrlCheck?: boolean;
20
+ }
21
+ /** @internal Exported for unit tests. */
22
+ export declare function resolveUrl(options: ConvexFetchOptions): string;
23
+ /** @internal Exported for unit tests. */
24
+ export declare function resolveToken(options: ConvexFetchOptions): string | undefined;
25
+ /**
26
+ * One-shot query via ConvexHttpClient. Use in Nitro routes / server middleware.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * export default defineEventHandler(async (event) => {
31
+ * return await fetchQuery(api.tasks.list, {}, { event })
32
+ * })
33
+ * ```
34
+ */
35
+ export declare function fetchQuery<Query extends FunctionReference<'query'>>(query: Query, args?: FunctionArgs<Query>, options?: ConvexFetchOptions): Promise<FunctionReturnType<Query>>;
36
+ /**
37
+ * One-shot mutation via ConvexHttpClient. Use in Nitro routes / server middleware.
38
+ */
39
+ export declare function fetchMutation<Mutation extends FunctionReference<'mutation'>>(mutation: Mutation, args?: FunctionArgs<Mutation>, options?: ConvexFetchOptions): Promise<FunctionReturnType<Mutation>>;
40
+ /**
41
+ * One-shot action via ConvexHttpClient. Use in Nitro routes / server middleware.
42
+ */
43
+ export declare function fetchAction<Action extends FunctionReference<'action'>>(action: Action, args?: FunctionArgs<Action>, options?: ConvexFetchOptions): Promise<FunctionReturnType<Action>>;
@@ -0,0 +1,63 @@
1
+ import { getCookie } from "h3";
2
+ import { useRuntimeConfig } from "nitropack/runtime";
3
+ import { createHttpClient } from "../utils/http.js";
4
+ import { resolveAuthCookieName } from "../utils/authStorage.js";
5
+ import { resolveFetchToken } from "../utils/fetchToken.js";
6
+ export { resolveFetchToken };
7
+ function readConvexConfig(event) {
8
+ try {
9
+ const config = useRuntimeConfig(event);
10
+ return config.public?.convex;
11
+ } catch {
12
+ return void 0;
13
+ }
14
+ }
15
+ export function resolveUrl(options) {
16
+ if (options.url) {
17
+ return options.url;
18
+ }
19
+ const fromConfig = readConvexConfig(options.event)?.url;
20
+ if (fromConfig) {
21
+ return fromConfig;
22
+ }
23
+ const fromEnv = process.env.NUXT_PUBLIC_CONVEX_URL;
24
+ if (fromEnv) {
25
+ return fromEnv;
26
+ }
27
+ throw new Error(
28
+ "[convex-nuxt] No Convex URL. Pass { url } or set NUXT_PUBLIC_CONVEX_URL."
29
+ );
30
+ }
31
+ export function resolveToken(options) {
32
+ if (options.token !== void 0) {
33
+ return resolveFetchToken({ token: options.token });
34
+ }
35
+ const cookieName = resolveAuthCookieName(readConvexConfig(options.event)?.auth);
36
+ if (!cookieName || !options.event) {
37
+ return void 0;
38
+ }
39
+ return resolveFetchToken({
40
+ cookieName,
41
+ cookieValue: getCookie(options.event, cookieName)
42
+ });
43
+ }
44
+ function setupClient(options = {}) {
45
+ return createHttpClient(resolveUrl(options), {
46
+ token: resolveToken(options),
47
+ skipConvexDeploymentUrlCheck: options.skipConvexDeploymentUrlCheck
48
+ });
49
+ }
50
+ export async function fetchQuery(query, args, options) {
51
+ const client = setupClient(options);
52
+ return await client.query(query, args ?? {});
53
+ }
54
+ export async function fetchMutation(mutation, args, options) {
55
+ const client = setupClient(options);
56
+ return await client.mutation(mutation, args ?? {}, {
57
+ skipQueue: true
58
+ });
59
+ }
60
+ export async function fetchAction(action, args, options) {
61
+ const client = setupClient(options);
62
+ return await client.action(action, args ?? {});
63
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Minimal DevTools iframe — shows public Convex runtime config.
3
+ * Connection state is available in-app via `useConvexConnectionState()`.
4
+ */
5
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, string>;
6
+ export default _default;
@@ -0,0 +1,32 @@
1
+ import { defineEventHandler } from "h3";
2
+ import { useRuntimeConfig } from "nitropack/runtime";
3
+ export default defineEventHandler((event) => {
4
+ const config = useRuntimeConfig(event);
5
+ const convex = config.public?.convex ?? {};
6
+ const body = JSON.stringify(
7
+ {
8
+ url: convex.url ?? null,
9
+ server: convex.server ?? true,
10
+ auth: convex.auth ?? null,
11
+ tip: "useConvexConnectionState() for live WebSocket status in the app"
12
+ },
13
+ null,
14
+ 2
15
+ );
16
+ return `<!doctype html>
17
+ <html lang="en">
18
+ <head>
19
+ <meta charset="utf-8" />
20
+ <title>Convex \xB7 Nuxt DevTools</title>
21
+ <style>
22
+ body { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; margin: 1rem; color: #e5e7eb; background: #0b1220; }
23
+ h1 { font-size: 0.95rem; font-weight: 600; margin: 0 0 0.75rem; color: #93c5fd; }
24
+ pre { margin: 0; white-space: pre-wrap; word-break: break-word; font-size: 0.8rem; line-height: 1.45; }
25
+ </style>
26
+ </head>
27
+ <body>
28
+ <h1>use-convex</h1>
29
+ <pre>${body.replace(/</g, "&lt;")}</pre>
30
+ </body>
31
+ </html>`;
32
+ });
@@ -0,0 +1,14 @@
1
+ import { type H3Event } from 'h3';
2
+ /**
3
+ * Fail-closed same-origin guard for cookie-bearing auth routes.
4
+ *
5
+ * Accepts the request when:
6
+ * - `Sec-Fetch-Site` is `same-origin` or `none` (user-initiated / same origin), or
7
+ * - `Origin` matches the trusted request origin (scheme + host), using
8
+ * `Host` and `getRequestProtocol` (honors `X-Forwarded-Proto` behind proxies).
9
+ *
10
+ * Rejects when `Sec-Fetch-Site` is `cross-site` / `same-site`, or when neither
11
+ * signal can confirm same-origin. Missing both headers is rejected.
12
+ * Scheme-mismatched Origins (e.g. `http://` vs HTTPS request) are rejected.
13
+ */
14
+ export declare function assertSameOrigin(event: H3Event): void;
@@ -0,0 +1,33 @@
1
+ import {
2
+ createError,
3
+ getRequestHeader,
4
+ getRequestProtocol
5
+ } from "h3";
6
+ export function assertSameOrigin(event) {
7
+ const secFetchSite = getRequestHeader(event, "sec-fetch-site");
8
+ if (secFetchSite === "same-origin" || secFetchSite === "none") {
9
+ return;
10
+ }
11
+ if (secFetchSite === "cross-site" || secFetchSite === "same-site") {
12
+ throw createError({
13
+ statusCode: 403,
14
+ message: "Forbidden"
15
+ });
16
+ }
17
+ const origin = getRequestHeader(event, "origin");
18
+ const host = getRequestHeader(event, "host");
19
+ if (origin && host) {
20
+ try {
21
+ const originUrl = new URL(origin);
22
+ const protocol = getRequestProtocol(event, { xForwardedProto: true });
23
+ if (originUrl.protocol === `${protocol}:` && originUrl.host === host) {
24
+ return;
25
+ }
26
+ } catch {
27
+ }
28
+ }
29
+ throw createError({
30
+ statusCode: 403,
31
+ message: "Forbidden"
32
+ });
33
+ }
@@ -0,0 +1,21 @@
1
+ import { type ComputedRef } from 'vue';
2
+ import { AUTH_JWT_COOKIE_MAX_AGE } from './authStorage.js';
3
+ export { AUTH_JWT_COOKIE_MAX_AGE };
4
+ /**
5
+ * Shared JWT cookie for SSR snapshots. Same name + options as persistTokens
6
+ * so plugin reads and sign-in/out writes share one Nuxt cookie ref.
7
+ *
8
+ * When `auth.httpOnly` is set, this cookie is HttpOnly (server-readable only).
9
+ */
10
+ export declare function useAuthJwtCookie(): import("nuxt/app").CookieRef<string | null> | undefined;
11
+ /**
12
+ * Readable presence marker used when JWT is HttpOnly so the client can still
13
+ * compute `hasSsrSession` / `showAuthedUi` without reading the token.
14
+ */
15
+ export declare function useAuthPresentCookie(): import("nuxt/app").CookieRef<string | null> | undefined;
16
+ /** Reactive JWT for HttpClient + SSR. Empty on the client when HttpOnly. */
17
+ export declare function useSsrTokenRef(): ComputedRef<string | undefined>;
18
+ /**
19
+ * Session hint for UI gating: JWT readable, or HttpOnly presence cookie.
20
+ */
21
+ export declare function useHasSsrSessionRef(): ComputedRef<boolean>;
@@ -0,0 +1,47 @@
1
+ import { computed } from "vue";
2
+ import { useCookie, useRuntimeConfig } from "nuxt/app";
3
+ import {
4
+ AUTH_JWT_COOKIE_MAX_AGE,
5
+ cookieValueToSsrToken,
6
+ isHttpOnlyAuth,
7
+ resolveAuthCookieName,
8
+ resolveAuthPresentCookieName
9
+ } from "./authStorage.js";
10
+ export { AUTH_JWT_COOKIE_MAX_AGE };
11
+ function cookieOptions(httpOnly) {
12
+ return {
13
+ sameSite: "lax",
14
+ secure: process.env.NODE_ENV === "production",
15
+ maxAge: AUTH_JWT_COOKIE_MAX_AGE,
16
+ path: "/",
17
+ httpOnly
18
+ };
19
+ }
20
+ export function useAuthJwtCookie() {
21
+ const config = useRuntimeConfig();
22
+ const convexConfig = config.public.convex;
23
+ const name = resolveAuthCookieName(convexConfig?.auth);
24
+ if (!name) {
25
+ return void 0;
26
+ }
27
+ const httpOnly = isHttpOnlyAuth(convexConfig?.auth);
28
+ return useCookie(name, cookieOptions(httpOnly));
29
+ }
30
+ export function useAuthPresentCookie() {
31
+ const config = useRuntimeConfig();
32
+ const convexConfig = config.public.convex;
33
+ const name = resolveAuthPresentCookieName(convexConfig?.auth);
34
+ if (!name) {
35
+ return void 0;
36
+ }
37
+ return useCookie(name, cookieOptions(false));
38
+ }
39
+ export function useSsrTokenRef() {
40
+ const cookie = useAuthJwtCookie();
41
+ return computed(() => cookieValueToSsrToken(cookie?.value));
42
+ }
43
+ export function useHasSsrSessionRef() {
44
+ const token = useSsrTokenRef();
45
+ const present = useAuthPresentCookie();
46
+ return computed(() => !!token.value || present?.value === "1");
47
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Apply `{ authenticated: true }` on top of caller args.
3
+ *
4
+ * Caller `'skip'` always wins (unrelated gates). Auth skip is applied after,
5
+ * so payload keys can still be derived from the raw args (non-empty args must
6
+ * not remap to `{}` while internally skipped).
7
+ */
8
+ export declare function resolveAuthGatedArgs<T>(raw: T | 'skip', options: {
9
+ authenticated?: boolean;
10
+ isAuthenticated: boolean;
11
+ }): T | 'skip';
@@ -0,0 +1,9 @@
1
+ export function resolveAuthGatedArgs(raw, options) {
2
+ if (raw === "skip") {
3
+ return "skip";
4
+ }
5
+ if (options.authenticated && !options.isAuthenticated) {
6
+ return "skip";
7
+ }
8
+ return raw;
9
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Single-flight mutex for token refresh (mirrors @convex-dev/auth/react).
3
+ * Prefers `navigator.locks` when available; falls back to an in-memory queue.
4
+ */
5
+ type MutexState = {
6
+ currentlyRunning: Promise<void> | null;
7
+ waiting: Array<() => Promise<void>>;
8
+ };
9
+ declare global {
10
+ var __convexNuxtAuthMutexes: Record<string, MutexState> | undefined;
11
+ }
12
+ export declare function withRefreshMutex<T>(key: string, callback: () => Promise<T>): Promise<T>;
13
+ /** Test helper — clear in-memory mutex state between tests. */
14
+ export declare function resetAuthMutexesForTests(): void;
15
+ export {};
@@ -0,0 +1,47 @@
1
+ export async function withRefreshMutex(key, callback) {
2
+ const lockManager = typeof navigator !== "undefined" ? navigator.locks : void 0;
3
+ if (lockManager !== void 0) {
4
+ return await lockManager.request(key, callback);
5
+ }
6
+ return await manualMutex(key, callback);
7
+ }
8
+ async function manualMutex(key, callback) {
9
+ return await new Promise((resolve, reject) => {
10
+ const wrapped = () => callback().then(resolve).catch(reject).then(() => void 0);
11
+ void enqueueCallbackForMutex(key, wrapped);
12
+ });
13
+ }
14
+ function getMutexValue(key) {
15
+ if (globalThis.__convexNuxtAuthMutexes === void 0) {
16
+ globalThis.__convexNuxtAuthMutexes = {};
17
+ }
18
+ let mutex = globalThis.__convexNuxtAuthMutexes[key];
19
+ if (mutex === void 0) {
20
+ mutex = { currentlyRunning: null, waiting: [] };
21
+ globalThis.__convexNuxtAuthMutexes[key] = mutex;
22
+ }
23
+ return mutex;
24
+ }
25
+ async function enqueueCallbackForMutex(key, callback) {
26
+ const mutex = getMutexValue(key);
27
+ if (mutex.currentlyRunning === null) {
28
+ globalThis.__convexNuxtAuthMutexes[key] = {
29
+ currentlyRunning: callback().finally(() => {
30
+ const next = getMutexValue(key).waiting.shift();
31
+ getMutexValue(key).currentlyRunning = null;
32
+ if (next !== void 0) {
33
+ void enqueueCallbackForMutex(key, next);
34
+ }
35
+ }),
36
+ waiting: []
37
+ };
38
+ } else {
39
+ globalThis.__convexNuxtAuthMutexes[key] = {
40
+ ...mutex,
41
+ waiting: [...mutex.waiting, callback]
42
+ };
43
+ }
44
+ }
45
+ export function resetAuthMutexesForTests() {
46
+ globalThis.__convexNuxtAuthMutexes = void 0;
47
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Derive Convex auth UI state from provider + backend confirmation.
3
+ * Mirrors convex/react ConvexAuthState.
4
+ */
5
+ export interface ConvexAuthFlags {
6
+ /**
7
+ * Whether the auth provider is still resolving its initial session.
8
+ * When true, Convex confirmation is ignored and we stay loading.
9
+ */
10
+ authProviderLoading: boolean;
11
+ /**
12
+ * Whether the auth provider believes a user is signed in.
13
+ */
14
+ authProviderAuthenticated: boolean;
15
+ /**
16
+ * Backend confirmation from ConvexClient.setAuth onChange.
17
+ * `null` means we have not heard from Convex yet.
18
+ */
19
+ isConvexAuthenticated: boolean | null;
20
+ /**
21
+ * Whether the socket is paused while fetching a replacement token
22
+ * after a server rejection. Routine background rotation does not set this.
23
+ */
24
+ isRefreshing: boolean;
25
+ }
26
+ export interface ConvexAuthState {
27
+ isLoading: boolean;
28
+ isAuthenticated: boolean;
29
+ isRefreshing: boolean;
30
+ }
31
+ export declare function resolveConvexAuthState(flags: ConvexAuthFlags): ConvexAuthState;
@@ -0,0 +1,35 @@
1
+ export function resolveConvexAuthState(flags) {
2
+ const {
3
+ authProviderLoading,
4
+ authProviderAuthenticated,
5
+ isConvexAuthenticated,
6
+ isRefreshing
7
+ } = flags;
8
+ if (authProviderLoading) {
9
+ return {
10
+ isLoading: true,
11
+ isAuthenticated: false,
12
+ isRefreshing: false
13
+ };
14
+ }
15
+ if (!authProviderAuthenticated) {
16
+ return {
17
+ isLoading: false,
18
+ isAuthenticated: false,
19
+ isRefreshing: false
20
+ };
21
+ }
22
+ if (isConvexAuthenticated === null) {
23
+ return {
24
+ isLoading: true,
25
+ isAuthenticated: false,
26
+ isRefreshing: false
27
+ };
28
+ }
29
+ const isAuthenticated = isConvexAuthenticated;
30
+ return {
31
+ isLoading: false,
32
+ isAuthenticated,
33
+ isRefreshing: isRefreshing && isAuthenticated
34
+ };
35
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Convex Auth client storage helpers (mirrors @convex-dev/auth/react).
3
+ */
4
+ export declare const JWT_STORAGE_KEY = "__convexAuthJWT";
5
+ export declare const REFRESH_TOKEN_STORAGE_KEY = "__convexAuthRefreshToken";
6
+ export declare const VERIFIER_STORAGE_KEY = "__convexAuthOAuthVerifier";
7
+ export declare const AUTH_JWT_COOKIE_MAX_AGE: number;
8
+ export declare const DEFAULT_CONVEX_AUTH_COOKIE = "convex_jwt";
9
+ /** Readable marker so the client knows an HttpOnly session exists. */
10
+ export declare const DEFAULT_AUTH_PRESENT_COOKIE = "convex_auth_present";
11
+ /** HttpOnly JWT cookie (Next.js Convex Auth naming). */
12
+ export declare const HTTPONLY_JWT_COOKIE = "__convexAuthJWT";
13
+ /** HttpOnly refresh cookie. */
14
+ export declare const HTTPONLY_REFRESH_COOKIE = "__convexAuthRefreshToken";
15
+ export type ConvexAuthConfig = {
16
+ provider?: string;
17
+ cookie?: string;
18
+ /**
19
+ * Store JWT + refresh in HttpOnly cookies via Nitro (Next.js parity).
20
+ * When true, JS cannot read tokens from `document.cookie`; `fetchToken`
21
+ * uses same-origin POST `/api/convex/auth/session` with `{ getToken: true }`.
22
+ * Prefer `true` in production. Does not make sessions XSS-proof.
23
+ */
24
+ httpOnly?: boolean;
25
+ /** Readable presence cookie when `httpOnly` is set (UI only). */
26
+ presentCookie?: string;
27
+ };
28
+ export declare function storageNamespace(url: string): string;
29
+ export declare function storageKey(base: string, url: string): string;
30
+ /**
31
+ * Flatten FormData (or pass through a plain record) the same way the React
32
+ * Convex Auth client does before calling `auth:signIn`.
33
+ */
34
+ export declare function flattenSignInParams(params?: FormData | Record<string, string>): Record<string, string>;
35
+ /**
36
+ * Resolve the auth cookie name for runtime config / SSR JWT reads.
37
+ * When `provider === 'convex-auth'` and cookie is omitted, default to `convex_jwt`
38
+ * (or the HttpOnly JWT name when `httpOnly` is enabled).
39
+ */
40
+ export declare function resolveAuthCookieName(auth?: ConvexAuthConfig): string | undefined;
41
+ export declare function resolveAuthPresentCookieName(auth?: ConvexAuthConfig): string | undefined;
42
+ export declare function resolveAuthRefreshCookieName(auth?: ConvexAuthConfig): string | undefined;
43
+ export declare function isHttpOnlyAuth(auth?: ConvexAuthConfig): boolean;
44
+ /** Empty / missing cookies are not a session. */
45
+ export declare function cookieValueToSsrToken(value: string | null | undefined): string | undefined;
46
+ export declare function readLocal(key: string): string | null;
47
+ export declare function writeLocal(key: string, value: string | null): void;
48
+ /**
49
+ * Whether the current URL's `?code=` should be treated as an OAuth callback.
50
+ * Only true when we previously stored a verifier (we started the flow).
51
+ */
52
+ export declare function shouldConsumeOAuthCode(options: {
53
+ code: string | null;
54
+ verifier: string | null;
55
+ }): boolean;
@@ -0,0 +1,85 @@
1
+ export const JWT_STORAGE_KEY = "__convexAuthJWT";
2
+ export const REFRESH_TOKEN_STORAGE_KEY = "__convexAuthRefreshToken";
3
+ export const VERIFIER_STORAGE_KEY = "__convexAuthOAuthVerifier";
4
+ export const AUTH_JWT_COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
5
+ export const DEFAULT_CONVEX_AUTH_COOKIE = "convex_jwt";
6
+ export const DEFAULT_AUTH_PRESENT_COOKIE = "convex_auth_present";
7
+ export const HTTPONLY_JWT_COOKIE = "__convexAuthJWT";
8
+ export const HTTPONLY_REFRESH_COOKIE = "__convexAuthRefreshToken";
9
+ export function storageNamespace(url) {
10
+ return url.replace(/[^a-zA-Z0-9]/g, "");
11
+ }
12
+ export function storageKey(base, url) {
13
+ return `${base}_${storageNamespace(url)}`;
14
+ }
15
+ export function flattenSignInParams(params) {
16
+ if (params === void 0) {
17
+ return {};
18
+ }
19
+ if (typeof FormData !== "undefined" && params instanceof FormData) {
20
+ const out = {};
21
+ params.forEach((value, key) => {
22
+ if (typeof value === "string") {
23
+ out[key] = value;
24
+ }
25
+ });
26
+ return out;
27
+ }
28
+ return { ...params };
29
+ }
30
+ export function resolveAuthCookieName(auth) {
31
+ if (auth?.httpOnly) {
32
+ return auth.cookie ?? HTTPONLY_JWT_COOKIE;
33
+ }
34
+ if (auth?.cookie) {
35
+ return auth.cookie;
36
+ }
37
+ if (auth?.provider === "convex-auth") {
38
+ return DEFAULT_CONVEX_AUTH_COOKIE;
39
+ }
40
+ return void 0;
41
+ }
42
+ export function resolveAuthPresentCookieName(auth) {
43
+ if (!auth?.httpOnly) {
44
+ return void 0;
45
+ }
46
+ return auth.presentCookie ?? DEFAULT_AUTH_PRESENT_COOKIE;
47
+ }
48
+ export function resolveAuthRefreshCookieName(auth) {
49
+ if (!auth?.httpOnly) {
50
+ return void 0;
51
+ }
52
+ return HTTPONLY_REFRESH_COOKIE;
53
+ }
54
+ export function isHttpOnlyAuth(auth) {
55
+ return auth?.httpOnly === true;
56
+ }
57
+ export function cookieValueToSsrToken(value) {
58
+ return value || void 0;
59
+ }
60
+ export function readLocal(key) {
61
+ if (typeof window === "undefined") {
62
+ return null;
63
+ }
64
+ try {
65
+ return window.localStorage.getItem(key);
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+ export function writeLocal(key, value) {
71
+ if (typeof window === "undefined") {
72
+ return;
73
+ }
74
+ try {
75
+ if (value === null) {
76
+ window.localStorage.removeItem(key);
77
+ } else {
78
+ window.localStorage.setItem(key, value);
79
+ }
80
+ } catch {
81
+ }
82
+ }
83
+ export function shouldConsumeOAuthCode(options) {
84
+ return options.code !== null && options.verifier !== null;
85
+ }