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,13 @@
1
+ declare var __VLS_1: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_1) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,13 @@
1
+ declare var __VLS_1: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_1) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,8 @@
1
+ <script setup>
2
+ import { useConvexAuth } from "../composables/useConvexAuth";
3
+ const { isAuthenticated, isRefreshing } = useConvexAuth();
4
+ </script>
5
+
6
+ <template>
7
+ <slot v-if="isAuthenticated && isRefreshing" />
8
+ </template>
@@ -0,0 +1,13 @@
1
+ declare var __VLS_1: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_1) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,13 @@
1
+ declare var __VLS_1: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_1) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,8 @@
1
+ <script setup>
2
+ import { useConvexAuth } from "../composables/useConvexAuth";
3
+ const { showAuthedUi } = useConvexAuth();
4
+ </script>
5
+
6
+ <template>
7
+ <slot v-if="showAuthedUi" />
8
+ </template>
@@ -0,0 +1,13 @@
1
+ declare var __VLS_1: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_1) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,13 @@
1
+ declare var __VLS_1: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_1) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,8 @@
1
+ <script setup>
2
+ import { useConvexAuth } from "../composables/useConvexAuth";
3
+ const { showAuthedUi, isLoading } = useConvexAuth();
4
+ </script>
5
+
6
+ <template>
7
+ <slot v-if="!showAuthedUi && !isLoading" />
8
+ </template>
@@ -0,0 +1,13 @@
1
+ declare var __VLS_1: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_1) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,9 @@
1
+ import type { FunctionArgs, FunctionReference } from 'convex/server';
2
+ /**
3
+ * Start a live subscription early so the first `useConvexQuery` / screen
4
+ * paint can reuse warm client state (React `prewarmQuery` parity).
5
+ *
6
+ * Returns an unsubscribe function. When called during Vue `setup`, the
7
+ * subscription is also cleaned up with the current effect scope.
8
+ */
9
+ export declare function prewarmQuery<Query extends FunctionReference<'query'>>(query: Query, args?: FunctionArgs<Query>): () => void;
@@ -0,0 +1,14 @@
1
+ import { onScopeDispose } from "vue";
2
+ import { useConvex } from "./useConvex.js";
3
+ export function prewarmQuery(query, args = {}) {
4
+ const client = useConvex();
5
+ const unsubscribe = client.onUpdate(query, args, () => {
6
+ });
7
+ try {
8
+ onScopeDispose(() => {
9
+ unsubscribe();
10
+ });
11
+ } catch {
12
+ }
13
+ return unsubscribe;
14
+ }
@@ -0,0 +1,60 @@
1
+ import { type ComputedRef, type Ref } from 'vue';
2
+ export type AuthTokens = {
3
+ token: string;
4
+ refreshToken: string;
5
+ };
6
+ export type SignInResult = {
7
+ signingIn: boolean;
8
+ redirect?: URL;
9
+ };
10
+ export interface UseAuthReturn {
11
+ isLoading: ComputedRef<boolean>;
12
+ isAuthenticated: ComputedRef<boolean>;
13
+ isRefreshing: ComputedRef<boolean>;
14
+ /**
15
+ * JWT cookie is present. Use with `isAuthenticated` to keep an SSR-gated
16
+ * shell mounted while Convex confirms — do not live-subscribe on this alone.
17
+ */
18
+ hasSsrSession: ComputedRef<boolean>;
19
+ /**
20
+ * Mount the signed-in shell when the SSR cookie is present **or** Convex
21
+ * has confirmed auth. Prefer this over `isAuthenticated` alone.
22
+ * Live subscriptions must still gate on `isAuthenticated`.
23
+ */
24
+ showAuthedUi: ComputedRef<boolean>;
25
+ error: Ref<string | null>;
26
+ pending: Ref<boolean>;
27
+ signIn: typeof signIn;
28
+ signOut: typeof signOut;
29
+ }
30
+ /**
31
+ * First-party Convex Auth session state + actions.
32
+ *
33
+ * Only auto-imported when `convex.auth.provider === 'convex-auth'`.
34
+ * Wire-up happens in `plugin.auth.client` — call `signIn` / `signOut` from forms.
35
+ */
36
+ export declare function useAuth(): UseAuthReturn;
37
+ /**
38
+ * Sign in with a Convex Auth provider (password, OAuth, OTP, …).
39
+ * Mirrors `@convex-dev/auth/react` `useAuthActions().signIn`.
40
+ */
41
+ export declare function signIn(provider?: string, params?: FormData | Record<string, string>): Promise<SignInResult>;
42
+ /**
43
+ * Sign out and clear local tokens + SSR JWT cookie.
44
+ */
45
+ export declare function signOut(): Promise<void>;
46
+ /** @internal Used by the Convex Auth client plugin. */
47
+ export declare function hydrateAuthFromStorage(): void;
48
+ /** @internal Token fetcher for `useConvexAuth({ fetchToken })`. */
49
+ export declare function getAuthToken({ forceRefreshToken, }: {
50
+ forceRefreshToken: boolean;
51
+ }): Promise<string | null>;
52
+ /** @internal Sync check for OAuth `?code=` + stored verifier (no await). */
53
+ export declare function hasPendingOAuthCallback(): boolean;
54
+ /** @internal Used by the Convex Auth client plugin. */
55
+ export declare function consumeOAuthCodeFromUrl(): Promise<boolean>;
56
+ /** @internal Provider session flags for `useConvexAuth`. */
57
+ export declare function useAuthProviderState(): {
58
+ isLoading: ComputedRef<boolean>;
59
+ hasSession: ComputedRef<boolean>;
60
+ };
@@ -0,0 +1,426 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ import { makeFunctionReference } from "convex/server";
3
+ import {
4
+ computed,
5
+ nextTick
6
+ } from "vue";
7
+ import {
8
+ useRuntimeConfig,
9
+ useState
10
+ } from "nuxt/app";
11
+ import { useConvexAuth } from "./useConvexAuth.js";
12
+ import { withRefreshMutex } from "../utils/authMutex.js";
13
+ import {
14
+ useAuthJwtCookie,
15
+ useAuthPresentCookie
16
+ } from "../utils/authCookie.js";
17
+ import {
18
+ flattenSignInParams,
19
+ isHttpOnlyAuth,
20
+ JWT_STORAGE_KEY,
21
+ readLocal,
22
+ REFRESH_TOKEN_STORAGE_KEY,
23
+ shouldConsumeOAuthCode,
24
+ storageKey,
25
+ VERIFIER_STORAGE_KEY,
26
+ writeLocal
27
+ } from "../utils/authStorage.js";
28
+ import { tryUseConvexContext } from "../utils/context.js";
29
+ import { parseOAuthRedirect } from "../utils/oauthRedirect.js";
30
+ const authSignIn = makeFunctionReference("auth:signIn");
31
+ const authSignOut = makeFunctionReference(
32
+ "auth:signOut"
33
+ );
34
+ const RETRY_BACKOFF = [500, 2e3];
35
+ const RETRY_JITTER = 100;
36
+ export function useAuth() {
37
+ const session = useAuthSession();
38
+ if (!tryUseConvexContext()) {
39
+ return {
40
+ isLoading: computed(() => session.isLoading.value),
41
+ isAuthenticated: computed(() => false),
42
+ isRefreshing: computed(() => false),
43
+ hasSsrSession: computed(() => false),
44
+ showAuthedUi: computed(() => false),
45
+ error: session.error,
46
+ pending: session.pending,
47
+ signIn,
48
+ signOut
49
+ };
50
+ }
51
+ const convexAuth = useConvexAuth();
52
+ return {
53
+ isLoading: convexAuth.isLoading,
54
+ isAuthenticated: convexAuth.isAuthenticated,
55
+ isRefreshing: convexAuth.isRefreshing,
56
+ hasSsrSession: convexAuth.hasSsrSession,
57
+ showAuthedUi: convexAuth.showAuthedUi,
58
+ error: session.error,
59
+ pending: session.pending,
60
+ signIn,
61
+ signOut
62
+ };
63
+ }
64
+ export async function signIn(provider, params) {
65
+ const session = useAuthSession();
66
+ const convexUrl = session.convexUrl;
67
+ if (!convexUrl) {
68
+ throw new Error("Convex URL is not configured");
69
+ }
70
+ session.pending.value = true;
71
+ session.error.value = null;
72
+ try {
73
+ const flatParams = flattenSignInParams(params);
74
+ const existingVerifier = readLocal(session.verifierKey.value) ?? void 0;
75
+ writeLocal(session.verifierKey.value, null);
76
+ const result = await callSignIn(convexUrl, {
77
+ provider,
78
+ params: flatParams,
79
+ verifier: existingVerifier
80
+ });
81
+ if (result.redirect !== void 0) {
82
+ const url = parseOAuthRedirect(result.redirect);
83
+ if (result.verifier) {
84
+ writeLocal(session.verifierKey.value, result.verifier);
85
+ }
86
+ if (import.meta.client) {
87
+ window.location.href = url.toString();
88
+ }
89
+ return { signingIn: false, redirect: url };
90
+ }
91
+ if (result.tokens !== void 0) {
92
+ await persistTokens(session, result.tokens);
93
+ return { signingIn: result.tokens !== null };
94
+ }
95
+ return { signingIn: false };
96
+ } catch (cause) {
97
+ const message = cause instanceof Error ? cause.message : "Authentication failed";
98
+ session.error.value = message;
99
+ throw cause;
100
+ } finally {
101
+ session.pending.value = false;
102
+ }
103
+ }
104
+ export async function signOut() {
105
+ const session = useAuthSession();
106
+ const convexUrl = session.convexUrl;
107
+ session.pending.value = true;
108
+ session.error.value = null;
109
+ try {
110
+ if (convexUrl) {
111
+ const token = readLocal(session.jwtKey.value);
112
+ const http = new ConvexHttpClient(convexUrl);
113
+ if (token) {
114
+ http.setAuth(token);
115
+ }
116
+ try {
117
+ await http.action(authSignOut, {});
118
+ } catch {
119
+ }
120
+ }
121
+ await persistTokens(session, null);
122
+ } finally {
123
+ session.pending.value = false;
124
+ }
125
+ }
126
+ export function hydrateAuthFromStorage() {
127
+ const session = useAuthSession();
128
+ if (!session.convexUrl) {
129
+ session.isLoading.value = false;
130
+ session.hasSession.value = false;
131
+ return;
132
+ }
133
+ if (session.httpOnly) {
134
+ const present = useAuthPresentCookie();
135
+ if (present?.value === "1") {
136
+ session.hasSession.value = true;
137
+ session.isLoading.value = false;
138
+ return;
139
+ }
140
+ session.isLoading.value = true;
141
+ void fetchAuthSession().then((result) => {
142
+ session.hasSession.value = result.hasSession;
143
+ session.isLoading.value = false;
144
+ if (result.hasSession) {
145
+ const marker = useAuthPresentCookie();
146
+ if (marker) {
147
+ marker.value = "1";
148
+ }
149
+ }
150
+ });
151
+ return;
152
+ }
153
+ const token = readLocal(session.jwtKey.value);
154
+ session.hasSession.value = token !== null;
155
+ session.isLoading.value = false;
156
+ if (token) {
157
+ setJwtCookie(token);
158
+ }
159
+ }
160
+ export async function getAuthToken({
161
+ forceRefreshToken
162
+ }) {
163
+ const session = useAuthSession();
164
+ const convexUrl = session.convexUrl;
165
+ if (!convexUrl) {
166
+ return null;
167
+ }
168
+ if (session.httpOnly) {
169
+ return withRefreshMutex(session.refreshKey.value, async () => {
170
+ if (!forceRefreshToken) {
171
+ const current = await fetchAuthSessionToken();
172
+ if (current.token) {
173
+ session.hasSession.value = true;
174
+ return current.token;
175
+ }
176
+ }
177
+ const refreshed = await refreshAuthSession();
178
+ if (!refreshed) {
179
+ await clearAuthSession();
180
+ session.hasSession.value = false;
181
+ return null;
182
+ }
183
+ session.hasSession.value = true;
184
+ return refreshed;
185
+ });
186
+ }
187
+ if (!forceRefreshToken) {
188
+ return readLocal(session.jwtKey.value);
189
+ }
190
+ return withRefreshMutex(session.refreshKey.value, async () => {
191
+ const refreshToken = readLocal(session.refreshKey.value);
192
+ if (!refreshToken) {
193
+ persistTokens(session, null);
194
+ return null;
195
+ }
196
+ try {
197
+ const result = await callSignInWithRetry(convexUrl, { refreshToken });
198
+ const tokens = result.tokens ?? null;
199
+ if (!tokens) {
200
+ persistTokens(session, null);
201
+ return null;
202
+ }
203
+ persistTokens(session, tokens);
204
+ return tokens.token;
205
+ } catch {
206
+ persistTokens(session, null);
207
+ return null;
208
+ }
209
+ });
210
+ }
211
+ export function hasPendingOAuthCallback() {
212
+ if (!import.meta.client) {
213
+ return false;
214
+ }
215
+ const session = useAuthSession();
216
+ if (!session.convexUrl) {
217
+ return false;
218
+ }
219
+ const code = new URLSearchParams(window.location.search).get("code");
220
+ const verifier = readLocal(session.verifierKey.value);
221
+ return shouldConsumeOAuthCode({ code, verifier });
222
+ }
223
+ export async function consumeOAuthCodeFromUrl() {
224
+ if (!import.meta.client) {
225
+ return false;
226
+ }
227
+ const session = useAuthSession();
228
+ if (!session.convexUrl) {
229
+ return false;
230
+ }
231
+ const code = new URLSearchParams(window.location.search).get("code");
232
+ const verifier = readLocal(session.verifierKey.value);
233
+ if (!shouldConsumeOAuthCode({ code, verifier })) {
234
+ return false;
235
+ }
236
+ const url = new URL(window.location.href);
237
+ url.searchParams.delete("code");
238
+ window.history.replaceState({}, "", url.pathname + url.search + url.hash);
239
+ session.pending.value = true;
240
+ session.error.value = null;
241
+ session.isLoading.value = true;
242
+ try {
243
+ writeLocal(session.verifierKey.value, null);
244
+ const result = await callSignInWithRetry(session.convexUrl, {
245
+ params: { code },
246
+ verifier
247
+ });
248
+ await persistTokens(session, result.tokens ?? null);
249
+ return result.tokens != null;
250
+ } catch (cause) {
251
+ const message = cause instanceof Error ? cause.message : "OAuth callback failed";
252
+ session.error.value = message;
253
+ await persistTokens(session, null);
254
+ return false;
255
+ } finally {
256
+ session.pending.value = false;
257
+ }
258
+ }
259
+ export function useAuthProviderState() {
260
+ const session = useAuthSession();
261
+ return {
262
+ isLoading: computed(() => session.isLoading.value),
263
+ hasSession: computed(() => session.hasSession.value)
264
+ };
265
+ }
266
+ function useAuthSession() {
267
+ const config = useRuntimeConfig();
268
+ const convexConfig = config.public.convex;
269
+ const convexUrl = convexConfig?.url;
270
+ const httpOnly = isHttpOnlyAuth(convexConfig?.auth);
271
+ const isLoading = useState("convex-auth-loading", () => true);
272
+ const hasSession = useState("convex-auth-has-session", () => false);
273
+ const error = useState("convex-auth-error", () => null);
274
+ const pending = useState("convex-auth-pending", () => false);
275
+ const jwtKey = computed(
276
+ () => convexUrl ? storageKey(JWT_STORAGE_KEY, convexUrl) : JWT_STORAGE_KEY
277
+ );
278
+ const refreshKey = computed(
279
+ () => convexUrl ? storageKey(REFRESH_TOKEN_STORAGE_KEY, convexUrl) : REFRESH_TOKEN_STORAGE_KEY
280
+ );
281
+ const verifierKey = computed(
282
+ () => convexUrl ? storageKey(VERIFIER_STORAGE_KEY, convexUrl) : VERIFIER_STORAGE_KEY
283
+ );
284
+ return {
285
+ convexUrl,
286
+ httpOnly,
287
+ jwtKey,
288
+ refreshKey,
289
+ verifierKey,
290
+ isLoading,
291
+ hasSession,
292
+ error,
293
+ pending
294
+ };
295
+ }
296
+ function setJwtCookie(token) {
297
+ const cookie = useAuthJwtCookie();
298
+ if (!cookie) {
299
+ return;
300
+ }
301
+ cookie.value = token;
302
+ }
303
+ function setPresentCookie(value) {
304
+ const cookie = useAuthPresentCookie();
305
+ if (!cookie) {
306
+ return;
307
+ }
308
+ cookie.value = value;
309
+ }
310
+ const AUTH_SESSION_PATH = "/api/convex/auth/session";
311
+ async function fetchAuthSession() {
312
+ try {
313
+ return await $fetch(AUTH_SESSION_PATH, {
314
+ method: "GET"
315
+ });
316
+ } catch {
317
+ return { hasSession: false };
318
+ }
319
+ }
320
+ async function fetchAuthSessionToken() {
321
+ try {
322
+ return await $fetch(
323
+ AUTH_SESSION_PATH,
324
+ {
325
+ method: "POST",
326
+ body: { getToken: true }
327
+ }
328
+ );
329
+ } catch {
330
+ return { hasSession: false, token: null };
331
+ }
332
+ }
333
+ async function refreshAuthSession() {
334
+ try {
335
+ const result = await $fetch(AUTH_SESSION_PATH, {
336
+ method: "POST",
337
+ body: { refresh: true }
338
+ });
339
+ return result.token;
340
+ } catch {
341
+ return null;
342
+ }
343
+ }
344
+ async function clearAuthSession() {
345
+ try {
346
+ await $fetch(AUTH_SESSION_PATH, { method: "DELETE" });
347
+ } catch {
348
+ }
349
+ setPresentCookie(null);
350
+ }
351
+ async function writeHttpOnlySession(tokens) {
352
+ if (tokens === null) {
353
+ await clearAuthSession();
354
+ return;
355
+ }
356
+ await $fetch(AUTH_SESSION_PATH, {
357
+ method: "POST",
358
+ body: {
359
+ token: tokens.token,
360
+ refreshToken: tokens.refreshToken
361
+ }
362
+ });
363
+ setPresentCookie("1");
364
+ }
365
+ async function persistTokens(session, tokens) {
366
+ if (session.httpOnly) {
367
+ if (tokens === null) {
368
+ await writeHttpOnlySession(null);
369
+ writeLocal(session.jwtKey.value, null);
370
+ writeLocal(session.refreshKey.value, null);
371
+ session.hasSession.value = false;
372
+ session.isLoading.value = false;
373
+ return;
374
+ }
375
+ session.hasSession.value = true;
376
+ session.isLoading.value = false;
377
+ await writeHttpOnlySession(tokens);
378
+ return;
379
+ }
380
+ if (tokens === null) {
381
+ writeLocal(session.jwtKey.value, null);
382
+ writeLocal(session.refreshKey.value, null);
383
+ setJwtCookie(null);
384
+ session.hasSession.value = false;
385
+ session.isLoading.value = false;
386
+ return;
387
+ }
388
+ writeLocal(session.jwtKey.value, tokens.token);
389
+ writeLocal(session.refreshKey.value, tokens.refreshToken);
390
+ session.hasSession.value = true;
391
+ session.isLoading.value = false;
392
+ nextTick(() => {
393
+ setJwtCookie(tokens.token);
394
+ });
395
+ }
396
+ async function callSignIn(convexUrl, args) {
397
+ const http = new ConvexHttpClient(convexUrl);
398
+ return await http.action(authSignIn, args);
399
+ }
400
+ async function callSignInWithRetry(convexUrl, args) {
401
+ let lastError;
402
+ let retry = 0;
403
+ while (retry < RETRY_BACKOFF.length) {
404
+ try {
405
+ return await callSignIn(convexUrl, args);
406
+ } catch (e) {
407
+ lastError = e;
408
+ if (!isNetworkError(e)) {
409
+ break;
410
+ }
411
+ const wait = RETRY_BACKOFF[retry] + RETRY_JITTER * Math.random();
412
+ retry++;
413
+ await new Promise((resolve) => setTimeout(resolve, wait));
414
+ }
415
+ }
416
+ throw lastError;
417
+ }
418
+ function isNetworkError(error) {
419
+ if (error instanceof TypeError) {
420
+ return true;
421
+ }
422
+ if (error instanceof Error && /network|fetch|Failed to fetch/i.test(error.message)) {
423
+ return true;
424
+ }
425
+ return false;
426
+ }
@@ -0,0 +1,24 @@
1
+ import { navigateTo } from 'nuxt/app';
2
+ import { type ComputedRef } from 'vue';
3
+ /**
4
+ * Current JWT for authenticated HTTP calls (React `@convex-dev/auth`
5
+ * `useAuthToken` parity).
6
+ *
7
+ * Prefers `ConvexClient.getAuth()`, then the configured `fetchToken`
8
+ * (Convex Auth / BYO). Returns `null` when signed out or unavailable.
9
+ */
10
+ export declare function useAuthToken(): ComputedRef<string | null>;
11
+ /**
12
+ * Nuxt route middleware helper — redirect when the user is signed out.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // middleware/auth.ts
17
+ * export default defineNuxtRouteMiddleware(() => {
18
+ * return requireConvexAuthMiddleware({ redirectTo: '/login' })
19
+ * })
20
+ * ```
21
+ */
22
+ export declare function requireConvexAuthMiddleware(options?: {
23
+ redirectTo?: string;
24
+ }): ReturnType<typeof navigateTo> | void;