nuxt-convex-module 0.12.2 → 0.12.3

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 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><script type="importmap">{"imports":{"#entry":"/__nuxt-convex-module/_nuxt/COXexnyN.js"}}</script><link rel="stylesheet" href="/__nuxt-convex-module/_nuxt/entry.DsZySRVW.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-convex-module/_nuxt/COXexnyN.js"><script type="module" src="/__nuxt-convex-module/_nuxt/COXexnyN.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/__nuxt-convex-module/_nuxt/BPtiIkWa.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-convex-module",buildId:"d3335991-c747-41b3-a9e3-6d5c636b70f0",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793365964988,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><script type="importmap">{"imports":{"#entry":"/__nuxt-convex-module/_nuxt/COXexnyN.js"}}</script><link rel="stylesheet" href="/__nuxt-convex-module/_nuxt/entry.DsZySRVW.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-convex-module/_nuxt/COXexnyN.js"><script type="module" src="/__nuxt-convex-module/_nuxt/COXexnyN.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/__nuxt-convex-module/_nuxt/BPtiIkWa.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-convex-module",buildId:"bfa6df52-caf6-46de-a611-bdf9d975bd3f",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793468157767,false]</script></body></html>
package/dist/module.d.mts CHANGED
@@ -133,6 +133,7 @@ declare module '@nuxt/schema' {
133
133
  siteUrl: string;
134
134
  crossDomainCallbackRoute: string;
135
135
  loginPath: string;
136
+ authRoute: string;
136
137
  };
137
138
  }
138
139
  }
package/dist/module.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "compatibility": {
6
6
  "nuxt": ">=4.6.0"
7
7
  },
8
- "version": "0.12.2",
8
+ "version": "0.12.3",
9
9
  "builder": {
10
10
  "@nuxt/module-builder": "1.0.3",
11
11
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -402,6 +402,7 @@ const module$1 = defineNuxtModule({
402
402
  for (const message of diagnostics.errors) logger.error(message);
403
403
  for (const message of diagnostics.warnings) logger.warn(message);
404
404
  nuxt.options.runtimeConfig.public.convex.loginPath = diagnostics.loginPath;
405
+ nuxt.options.runtimeConfig.public.convex.authRoute = diagnostics.authRoute;
405
406
  registerAuthClientAlias(resolver, nuxt, options);
406
407
  registerConvexAliases(nuxt);
407
408
  registerConvexTypeFallback(nuxt);
@@ -82,10 +82,12 @@ const getArgsAndOptions = (args, token) => {
82
82
  return [args[0], { token }];
83
83
  };
84
84
  export function convexAuth(event, opts) {
85
+ const config = getConvexRuntimeConfig(event);
85
86
  const siteUrl = parseConvexSiteUrl(
86
- opts?.convexSiteUrl ?? getConvexRuntimeConfig().siteUrl ?? process.env.NUXT_PUBLIC_CONVEX_SITE_URL
87
+ opts?.convexSiteUrl || config.siteUrl || process.env.NUXT_PUBLIC_CONVEX_SITE_URL
87
88
  );
88
- const cacheKey = `__nuxtConvexToken:${siteUrl}|${opts?.basePath ?? ""}`;
89
+ const basePath = opts?.basePath ?? config.authRoute;
90
+ const cacheKey = `__nuxtConvexToken:${siteUrl}|${basePath ?? ""}`;
89
91
  const tokenCache = event.context;
90
92
  const cachedGetToken = ({ forceRefresh } = {}) => {
91
93
  const cached = tokenCache[cacheKey];
@@ -94,7 +96,7 @@ export function convexAuth(event, opts) {
94
96
  mutableHeaders.delete("content-length");
95
97
  mutableHeaders.delete("transfer-encoding");
96
98
  mutableHeaders.set("accept-encoding", "identity");
97
- const promise = getToken(siteUrl, mutableHeaders, { ...opts, forceRefresh });
99
+ const promise = getToken(siteUrl, mutableHeaders, { ...opts, basePath, forceRefresh });
98
100
  tokenCache[cacheKey] = promise;
99
101
  return promise;
100
102
  };
@@ -1,9 +1,10 @@
1
1
  import { computed } from "vue";
2
2
  import { defineNuxtPlugin, useRuntimeConfig, useState, useRequestEvent, useResponseHeader } from "#app";
3
3
  import { ConvexVueClient, ConvexClientKey } from "../../vue/client.js";
4
- import { CONVEX_INITIAL_TOKEN_KEY } from "../../nuxt/config.js";
4
+ import { CONVEX_INITIAL_TOKEN_KEY, closeAfterRender } from "../../nuxt/config.js";
5
5
  import { convexAuth } from "../nuxt/server.js";
6
6
  import { ConvexAuthStateKey } from "../../vue/auth/index.js";
7
+ import { BetterAuthTokenStoreKey, createBetterAuthTokenStore, seedInitialToken } from "./token-store.js";
7
8
  export async function prefetchAuthToken(event, initialToken, cacheControl) {
8
9
  try {
9
10
  const token = await convexAuth(event).getToken();
@@ -19,10 +20,13 @@ export default defineNuxtPlugin({
19
20
  name: "nuxt-convex-module:better-auth:server",
20
21
  async setup(nuxtApp) {
21
22
  const initialToken = useState(CONVEX_INITIAL_TOKEN_KEY, () => null);
23
+ const tokenStore = createBetterAuthTokenStore();
24
+ nuxtApp.vueApp.provide(BetterAuthTokenStoreKey, tokenStore);
22
25
  const url = useRuntimeConfig().public.convex.url;
23
26
  const ssrClient = url ? new ConvexVueClient(url) : void 0;
24
27
  if (ssrClient) {
25
28
  nuxtApp.vueApp.provide(ConvexClientKey, ssrClient);
29
+ closeAfterRender(nuxtApp, ssrClient);
26
30
  }
27
31
  const ssrAuthState = {
28
32
  isLoading: computed(() => true),
@@ -33,6 +37,7 @@ export default defineNuxtPlugin({
33
37
  const event = useRequestEvent();
34
38
  if (event && !import.meta.prerender) {
35
39
  await prefetchAuthToken(event, initialToken, useResponseHeader("Cache-Control"));
40
+ seedInitialToken(tokenStore, initialToken.value);
36
41
  }
37
42
  }
38
43
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { ref } from "vue";
2
+ export function createBetterAuthTokenStore() {
3
+ return {
4
+ cachedToken: ref(null),
5
+ cachedTokenVersion: null,
6
+ pendingToken: null,
7
+ pendingTokenVersion: null,
8
+ initialTokenUsed: false
9
+ };
10
+ }
11
+ export const BetterAuthTokenStoreKey = Symbol("BetterAuthTokenStore");
12
+ export const PENDING_AUTH_VERSION = "__pending__";
13
+ export function seedInitialToken(store, initialToken) {
14
+ if (store.initialTokenUsed || !initialToken) return;
15
+ store.cachedToken.value = initialToken;
16
+ store.cachedTokenVersion = PENDING_AUTH_VERSION;
17
+ store.initialTokenUsed = true;
18
+ }
@@ -1,18 +1,30 @@
1
- import { computed, ref } from "vue";
1
+ import { computed, effectScope, hasInjectionContext, inject, shallowRef } from "vue";
2
2
  import { authClient } from "#convex/auth-client";
3
- const cachedToken = ref(null);
4
- let cachedTokenVersion = null;
5
- let pendingToken = null;
6
- let pendingTokenVersion = null;
7
- let initialTokenUsed = false;
8
- const PENDING_AUTH_VERSION = "__pending__";
9
- const useClientSession = () => authClient.useSession();
10
- export function useBetterAuth(initialToken) {
11
- if (!initialTokenUsed && initialToken) {
12
- cachedToken.value = initialToken;
13
- cachedTokenVersion = PENDING_AUTH_VERSION;
14
- initialTokenUsed = true;
3
+ import { BetterAuthTokenStoreKey, createBetterAuthTokenStore, PENDING_AUTH_VERSION, seedInitialToken } from "./token-store.js";
4
+ const browserStore = createBetterAuthTokenStore();
5
+ function useTokenStore() {
6
+ if (!import.meta.server) return browserStore;
7
+ return (hasInjectionContext() ? inject(BetterAuthTokenStoreKey, null) : null) ?? createBetterAuthTokenStore();
8
+ }
9
+ const serverSession = () => shallowRef({
10
+ data: null,
11
+ error: null,
12
+ isPending: true,
13
+ isRefetching: false,
14
+ refetch: async () => {
15
15
  }
16
+ });
17
+ let browserSession;
18
+ let browserSessionScope;
19
+ const useClientSession = () => {
20
+ if (import.meta.server) return serverSession();
21
+ browserSessionScope ??= effectScope(true);
22
+ return browserSession ??= browserSessionScope.run(() => authClient.useSession());
23
+ };
24
+ export function useBetterAuth(initialToken) {
25
+ const store = useTokenStore();
26
+ seedInitialToken(store, initialToken);
27
+ const { cachedToken } = store;
16
28
  const session = useClientSession();
17
29
  const client = authClient;
18
30
  const authVersion = computed(() => {
@@ -26,32 +38,32 @@ export function useBetterAuth(initialToken) {
26
38
  const version = currentAuthVersion();
27
39
  if (version === null) {
28
40
  cachedToken.value = null;
29
- cachedTokenVersion = null;
30
- pendingToken = null;
31
- pendingTokenVersion = null;
41
+ store.cachedTokenVersion = null;
42
+ store.pendingToken = null;
43
+ store.pendingTokenVersion = null;
32
44
  return null;
33
45
  }
34
- if (cachedToken.value && !forceRefreshToken && cachedTokenVersion === version) {
46
+ if (cachedToken.value && !forceRefreshToken && store.cachedTokenVersion === version) {
35
47
  return cachedToken.value;
36
48
  }
37
- if (!forceRefreshToken && pendingToken && pendingTokenVersion === version) {
38
- return pendingToken;
49
+ if (!forceRefreshToken && store.pendingToken && store.pendingTokenVersion === version) {
50
+ return store.pendingToken;
39
51
  }
40
- pendingTokenVersion = version;
41
- pendingToken = client.convex.token({ fetchOptions: { throw: false } }).then(({ data }) => {
52
+ store.pendingTokenVersion = version;
53
+ const pending = store.pendingToken = client.convex.token({ fetchOptions: { throw: false } }).then(({ data }) => {
42
54
  const token = data?.token || null;
43
55
  cachedToken.value = token;
44
- cachedTokenVersion = version;
56
+ store.cachedTokenVersion = version;
45
57
  return token;
46
58
  }).catch(() => {
47
59
  cachedToken.value = null;
48
- cachedTokenVersion = version;
60
+ store.cachedTokenVersion = version;
49
61
  return null;
50
62
  }).finally(() => {
51
- pendingToken = null;
52
- pendingTokenVersion = null;
63
+ store.pendingToken = null;
64
+ store.pendingTokenVersion = null;
53
65
  });
54
- return pendingToken;
66
+ return pending;
55
67
  };
56
68
  return {
57
69
  isLoading: computed(() => session.value.isPending && !cachedToken.value),
@@ -75,9 +87,9 @@ export function useBetterAuth(initialToken) {
75
87
  };
76
88
  }
77
89
  export function __resetUseBetterAuthForTests() {
78
- cachedToken.value = null;
79
- cachedTokenVersion = null;
80
- pendingToken = null;
81
- pendingTokenVersion = null;
82
- initialTokenUsed = false;
90
+ Object.assign(browserStore, createBetterAuthTokenStore(), { cachedToken: browserStore.cachedToken });
91
+ browserStore.cachedToken.value = null;
92
+ browserSessionScope?.stop();
93
+ browserSessionScope = void 0;
94
+ browserSession = void 0;
83
95
  }
@@ -8,11 +8,17 @@ export declare const CONVEX_INITIAL_TOKEN_KEY = "convex:initialToken";
8
8
  /**
9
9
  * The module's runtime config, tolerating a missing Nuxt context (a plain
10
10
  * server import outside a request) by returning an empty object.
11
+ *
12
+ * Given a server route's event, it reads that request's config: an
13
+ * environment variable set after the server started (by a Nitro plugin, say)
14
+ * reaches it, where the config read without one was fixed at startup.
11
15
  */
12
- export declare function getConvexRuntimeConfig(): {
16
+ export declare function getConvexRuntimeConfig(event?: unknown): {
13
17
  url: string | undefined;
14
18
  siteUrl: string | undefined;
19
+ authRoute: string | undefined;
15
20
  } | {
16
21
  url?: undefined;
17
22
  siteUrl?: undefined;
23
+ authRoute?: undefined;
18
24
  };
@@ -1,11 +1,17 @@
1
1
  import { useRuntimeConfig } from "#imports";
2
2
  export const CONVEX_INITIAL_TOKEN_KEY = "convex:initialToken";
3
- export function getConvexRuntimeConfig() {
3
+ export function closeAfterRender(nuxtApp, client) {
4
+ const close = () => void client.close();
5
+ nuxtApp.hook("app:rendered", close);
6
+ nuxtApp.hook("app:error", close);
7
+ }
8
+ export function getConvexRuntimeConfig(event) {
4
9
  try {
5
- const config = useRuntimeConfig();
10
+ const config = useRuntimeConfig(event);
6
11
  return {
7
12
  url: config.public?.convex?.url,
8
- siteUrl: config.public?.convex?.siteUrl
13
+ siteUrl: config.public?.convex?.siteUrl,
14
+ authRoute: config.public?.convex?.authRoute
9
15
  };
10
16
  } catch {
11
17
  return {};
@@ -24,6 +24,18 @@ function instantiateDefaultLogger({ verbose }) {
24
24
  error: (...args) => console.error(...args)
25
25
  };
26
26
  }
27
+ function notConnected() {
28
+ return {
29
+ hasInflightRequests: false,
30
+ isWebSocketConnected: false,
31
+ timeOfOldestInflightRequest: null,
32
+ hasEverConnected: false,
33
+ connectionCount: 0,
34
+ connectionRetries: 0,
35
+ inflightMutations: 0,
36
+ inflightActions: 0
37
+ };
38
+ }
27
39
  export class ConvexVueClient {
28
40
  address;
29
41
  cachedSync;
@@ -317,6 +329,7 @@ export class ConvexVueClient {
317
329
  * @experimental May change in a minor release — see STABILITY.md.
318
330
  */
319
331
  connectionState() {
332
+ if (import.meta.server && !this.cachedSync) return notConnected();
320
333
  return this.sync.connectionState();
321
334
  }
322
335
  /**
@@ -12,7 +12,8 @@
12
12
  * those instead of this one — see `registerIntegrations` in `src/module.ts`.
13
13
  *
14
14
  * The WebSocket opens lazily on the first subscription; on the server the
15
- * preloaded-query helpers short-circuit, so no socket is opened during SSR.
15
+ * preloaded-query helpers short-circuit, so no socket is opened during SSR,
16
+ * and a server render's client is closed when its request is done.
16
17
  */
17
18
  declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
18
19
  export default _default;
@@ -1,5 +1,6 @@
1
1
  import { defineNuxtPlugin, useRuntimeConfig } from "#app";
2
2
  import { ConvexVueClient, ConvexClientKey } from "./client.js";
3
+ import { closeAfterRender } from "../nuxt/config.js";
3
4
  export default defineNuxtPlugin({
4
5
  name: "nuxt-convex-module:client",
5
6
  setup(nuxtApp) {
@@ -12,5 +13,6 @@ export default defineNuxtPlugin({
12
13
  }
13
14
  const client = new ConvexVueClient(url);
14
15
  nuxtApp.vueApp.provide(ConvexClientKey, client);
16
+ if (import.meta.server) closeAfterRender(nuxtApp, client);
15
17
  }
16
18
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "nuxt-convex-module",
4
- "version": "0.12.2",
4
+ "version": "0.12.3",
5
5
  "description": "Convex backend integration with live queries, mutations, actions, pagination, file storage and SSR, auto-imported and typed against your deployment. The same Vue client runs without Nuxt; Better Auth, Clerk, Auth0 and Polar are opt-in.",
6
6
  "repository": {
7
7
  "type": "git",