nuxt-convex-module 0.0.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +51 -157
  3. package/dist/chunks/index.mjs +1 -1
  4. package/dist/devtools-client/200.html +1 -1
  5. package/dist/devtools-client/404.html +1 -1
  6. package/dist/devtools-client/_nuxt/71bSMC-_.js +2 -0
  7. package/dist/devtools-client/_nuxt/{CZbAGcNC.js → BAaUCl_m.js} +1 -1
  8. package/dist/devtools-client/_nuxt/BhmjAr2Q.js +1 -0
  9. package/dist/devtools-client/_nuxt/BmBQNDID.js +1 -0
  10. package/dist/devtools-client/_nuxt/C6YdTgGl.js +2 -0
  11. package/dist/devtools-client/_nuxt/{DPWXUmcI.js → CKakiU_h.js} +1 -1
  12. package/dist/devtools-client/_nuxt/Cm7YqZQ-.js +1 -0
  13. package/dist/devtools-client/_nuxt/{dy_xXcv9.js → DQmVr-EY.js} +1 -1
  14. package/dist/devtools-client/_nuxt/D_Rij7MD.js +1 -0
  15. package/dist/devtools-client/_nuxt/EZ4-8Xzm.js +3 -0
  16. package/dist/devtools-client/_nuxt/Kba0XpjH.js +1 -0
  17. package/dist/devtools-client/_nuxt/OVE7s6ty.js +1 -0
  18. package/dist/devtools-client/_nuxt/_Lf3mfPR.js +1 -0
  19. package/dist/devtools-client/_nuxt/builds/latest.json +1 -1
  20. package/dist/devtools-client/_nuxt/builds/meta/7dfea6da-3eca-4a35-ba06-5e069ac7e8df.json +1 -0
  21. package/dist/devtools-client/_nuxt/entry.HF8C6P11.css +1 -0
  22. package/dist/devtools-client/_nuxt/error-404.CLlwiMp-.css +1 -0
  23. package/dist/devtools-client/_nuxt/error-500.Bxr6fpDU.css +1 -0
  24. package/dist/devtools-client/_nuxt/queries.DNBUjWk2.css +1 -0
  25. package/dist/devtools-client/_nuxt/vZZlEX5A.js +1 -0
  26. package/dist/devtools-client/auth/index.html +1 -1
  27. package/dist/devtools-client/index.html +1 -1
  28. package/dist/devtools-client/logs/index.html +1 -1
  29. package/dist/devtools-client/queries/index.html +1 -1
  30. package/dist/module.d.mts +129 -0
  31. package/dist/module.json +2 -2
  32. package/dist/module.mjs +576 -0
  33. package/dist/runtime/auth0/vue/index.d.ts +3 -3
  34. package/dist/runtime/better-auth/nuxt/middleware.d.ts +4 -2
  35. package/dist/runtime/better-auth/nuxt/middleware.js +15 -12
  36. package/dist/runtime/better-auth/nuxt/proxy.d.ts +18 -2
  37. package/dist/runtime/better-auth/nuxt/proxy.js +7 -7
  38. package/dist/runtime/better-auth/nuxt/server.d.ts +6 -6
  39. package/dist/runtime/better-auth/nuxt/server.js +15 -0
  40. package/dist/runtime/better-auth/vue/auth-boundary.d.ts +3 -1
  41. package/dist/runtime/better-auth/vue/client.d.ts +1 -1
  42. package/dist/runtime/better-auth/vue/cross-domain.d.ts +4 -5
  43. package/dist/runtime/better-auth/vue/cross-domain.js +11 -4
  44. package/dist/runtime/better-auth/vue/hydration.d.ts +1 -1
  45. package/dist/runtime/better-auth/vue/index.d.ts +2 -1
  46. package/dist/runtime/better-auth/vue/index.js +2 -1
  47. package/dist/runtime/better-auth/vue/plugin.client.d.ts +1 -5
  48. package/dist/runtime/better-auth/vue/plugin.client.js +6 -10
  49. package/dist/runtime/better-auth/vue/plugin.server.d.ts +5 -16
  50. package/dist/runtime/better-auth/vue/plugin.server.js +3 -6
  51. package/dist/runtime/better-auth/vue/redirect.d.ts +33 -0
  52. package/dist/runtime/better-auth/vue/redirect.js +14 -0
  53. package/dist/runtime/better-auth/vue/{use-auth.d.ts → use-better-auth.d.ts} +31 -11
  54. package/dist/runtime/better-auth/vue/{use-auth.js → use-better-auth.js} +4 -3
  55. package/dist/runtime/clerk/vue/index.d.ts +4 -4
  56. package/dist/runtime/devtools/bridge.js +1 -1
  57. package/dist/runtime/nuxt/app.d.ts +48 -0
  58. package/dist/runtime/nuxt/app.js +8 -0
  59. package/dist/runtime/nuxt/composables/use-async-paginated-query.d.ts +88 -0
  60. package/dist/runtime/nuxt/composables/use-async-paginated-query.js +90 -0
  61. package/dist/runtime/nuxt/composables/use-async-query.d.ts +7 -16
  62. package/dist/runtime/nuxt/composables/use-async-query.js +14 -9
  63. package/dist/runtime/nuxt/config.d.ts +11 -0
  64. package/dist/runtime/nuxt/config.js +1 -0
  65. package/dist/runtime/nuxt/csp.d.ts +44 -0
  66. package/dist/runtime/nuxt/csp.js +46 -0
  67. package/dist/runtime/nuxt/index.d.ts +8 -8
  68. package/dist/runtime/nuxt/security.d.ts +33 -0
  69. package/dist/runtime/nuxt/security.js +14 -0
  70. package/dist/runtime/polar/vue/components.d.ts +8 -1
  71. package/dist/runtime/polar/vue/components.js +3 -3
  72. package/dist/runtime/vue/auth/index.d.ts +4 -4
  73. package/dist/runtime/vue/auth/index.js +6 -2
  74. package/dist/runtime/vue/client.d.ts +5 -55
  75. package/dist/runtime/vue/client.js +4 -0
  76. package/dist/runtime/vue/components/convex-image.d.ts +50 -0
  77. package/dist/runtime/vue/components/convex-image.js +21 -0
  78. package/dist/runtime/vue/composables/use-connection-state.d.ts +1 -0
  79. package/dist/runtime/vue/composables/use-paginated-query.d.ts +5 -1
  80. package/dist/runtime/vue/composables/use-paginated-query.js +2 -2
  81. package/dist/runtime/vue/composables/use-query.d.ts +2 -0
  82. package/dist/runtime/vue/composables/use-subscription.d.ts +1 -15
  83. package/dist/runtime/vue/composables/use-upload-queue.d.ts +2 -3
  84. package/dist/runtime/vue/hydration.d.ts +3 -24
  85. package/dist/runtime/vue/index.d.ts +2 -2
  86. package/dist/runtime/vue/index.js +1 -0
  87. package/dist/runtime/vue/plugin.js +1 -1
  88. package/dist/runtime/vue/provide.d.ts +5 -5
  89. package/dist/types.d.mts +3 -0
  90. package/package.json +103 -56
  91. package/dist/devtools-client/_nuxt/B29GGJ5n.js +0 -1
  92. package/dist/devtools-client/_nuxt/BSDEpQet.js +0 -1
  93. package/dist/devtools-client/_nuxt/BY23OQOo.js +0 -1
  94. package/dist/devtools-client/_nuxt/BZYRaL-G.js +0 -1
  95. package/dist/devtools-client/_nuxt/Byw6FxnC.js +0 -1
  96. package/dist/devtools-client/_nuxt/CJJ_hcZN.js +0 -1
  97. package/dist/devtools-client/_nuxt/CS6WMFOx.js +0 -2
  98. package/dist/devtools-client/_nuxt/Cr6_aklh.js +0 -2
  99. package/dist/devtools-client/_nuxt/DNfpyBVU.js +0 -3
  100. package/dist/devtools-client/_nuxt/builds/meta/a7708498-bd09-4de2-b5ab-19b380293c77.json +0 -1
  101. package/dist/devtools-client/_nuxt/entry.DbTJhkHT.css +0 -1
  102. package/dist/devtools-client/_nuxt/error-404.CwGGO2Kz.css +0 -1
  103. package/dist/devtools-client/_nuxt/error-500.8ZJqCQm8.css +0 -1
  104. package/dist/devtools-client/_nuxt/ncgEdbEt.js +0 -1
  105. package/dist/devtools-client/_nuxt/queries.CE_dtt9-.css +0 -1
  106. package/dist/module.d.ts +0 -189
  107. package/dist/module.js +0 -489
  108. package/dist/types.d.ts +0 -3
@@ -0,0 +1,88 @@
1
+ import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
2
+ import { type PaginatedQueryArgs, type PaginatedQueryItem, type PaginatedQueryReference, type PaginationStatus } from '../../vue/composables/use-paginated-query.js';
3
+ import { type AsyncQueryOptions } from './use-async-query.js';
4
+ /**
5
+ * Options for {@link useAsyncPaginatedQuery}: `usePaginatedQuery`'s
6
+ * `initialNumItems` plus the SSR options of {@link AsyncQueryOptions}.
7
+ *
8
+ * @public
9
+ */
10
+ export interface AsyncPaginatedQueryOptions extends Pick<AsyncQueryOptions, 'key' | 'server' | 'lazy' | 'token'> {
11
+ /** How many items the first page holds — on the server and on the client. */
12
+ initialNumItems: number;
13
+ }
14
+ /**
15
+ * The reactive half of {@link AsyncPaginatedQueryReturn}: `usePaginatedQuery`'s
16
+ * fields plus the `error` and `refresh` of the Nuxt data contract.
17
+ *
18
+ * @public
19
+ */
20
+ export interface AsyncPaginatedQueryData<Item> {
21
+ /**
22
+ * The rows: the server-rendered first page until the live subscription
23
+ * delivers its own, then every loaded page, live. The server page is
24
+ * shown for the initial args only — after an args change the list is
25
+ * empty until the new first page arrives.
26
+ */
27
+ results: ComputedRef<Item[]>;
28
+ /**
29
+ * `usePaginatedQuery`'s status. Before the live subscription has its first
30
+ * page it is derived from the server page: `'CanLoadMore'` or `'Exhausted'`.
31
+ */
32
+ status: ComputedRef<PaginationStatus>;
33
+ /** `true` while no page — server or live — is available yet, or more rows are loading. */
34
+ isLoading: ComputedRef<boolean>;
35
+ /** The SSR fetch error or the live subscription's, `null` when none. Never thrown. */
36
+ error: ComputedRef<Error | null>;
37
+ /**
38
+ * Fetch `numItems` more rows. Takes effect once the live subscription has
39
+ * its first page (`status` no longer derived from the server page).
40
+ */
41
+ loadMore: (numItems: number) => void;
42
+ /** Re-run the server-side first-page fetch (client-side it is a no-op — the subscription is live). */
43
+ refresh: (opts?: {
44
+ dedupe?: 'cancel' | 'defer';
45
+ }) => Promise<void>;
46
+ }
47
+ /**
48
+ * Result of {@link useAsyncPaginatedQuery}: awaitable like `useAsyncData`, and
49
+ * the refs of {@link AsyncPaginatedQueryData}.
50
+ *
51
+ * @public
52
+ */
53
+ export interface AsyncPaginatedQueryReturn<Item> extends PromiseLike<AsyncPaginatedQueryData<Item>>, AsyncPaginatedQueryData<Item> {
54
+ }
55
+ /**
56
+ * A paginated Convex query the Nuxt way: the first page is fetched during SSR
57
+ * and hydrated from the payload, then {@link usePaginatedQuery} takes over on
58
+ * the client — live rows and `loadMore`, no loading flash for the first page.
59
+ *
60
+ * @example
61
+ * ```vue
62
+ * <script setup lang="ts">
63
+ * import { api } from '#convex/api'
64
+ *
65
+ * const { results, status, loadMore } = useAsyncPaginatedQuery(
66
+ * api.messages.list,
67
+ * {},
68
+ * { initialNumItems: 20 },
69
+ * )
70
+ * </script>
71
+ * ```
72
+ *
73
+ * The payload entry is keyed on the query name, the *initial* args and
74
+ * `initialNumItems` (override with `options.key`). Client-side navigation does
75
+ * not fetch a page over HTTP: the subscription opens straight away, and the
76
+ * server page only matters for the first paint.
77
+ *
78
+ * @param query - a `FunctionReference` for the public paginated query.
79
+ * @param args - the query's arguments without `paginationOpts`, or `'skip'`.
80
+ * Accepts a ref, computed, or getter.
81
+ * @param options - {@link AsyncPaginatedQueryOptions}.
82
+ * @returns An awaitable {@link AsyncPaginatedQueryReturn}.
83
+ *
84
+ * @public
85
+ */
86
+ export declare function useAsyncPaginatedQuery<Query extends PaginatedQueryReference>(query: Query, args: MaybeRefOrGetter<PaginatedQueryArgs<Query> | 'skip'>, options: AsyncPaginatedQueryOptions): AsyncPaginatedQueryReturn<PaginatedQueryItem<Query>>;
87
+ /** @public */
88
+ export declare const useConvexAsyncPaginatedQuery: typeof useAsyncPaginatedQuery;
@@ -0,0 +1,90 @@
1
+ import { useAsyncData } from "#app";
2
+ import { computed, toValue } from "vue";
3
+ import { getFunctionName } from "convex/server";
4
+ import { convexToJson, jsonToConvex } from "convex/values";
5
+ import {
6
+ usePaginatedQuery
7
+ } from "../../vue/composables/use-paginated-query.js";
8
+ import { fetchQuery } from "../index.js";
9
+ import { useAsyncQueryContext } from "./use-async-query.js";
10
+ export function useAsyncPaginatedQuery(query, args, options) {
11
+ const { initialNumItems, server = true, lazy = false, token } = options;
12
+ const queryName = getFunctionName(query);
13
+ const initialArgs = toValue(args);
14
+ const key = options.key ?? defaultAsyncPaginatedQueryKey(queryName, initialArgs, initialNumItems);
15
+ const { client, deploymentUrl, resolveToken } = useAsyncQueryContext(token);
16
+ const asyncData = useAsyncData(
17
+ key,
18
+ async () => {
19
+ const currentArgs = toValue(args);
20
+ if (currentArgs === "skip" || !import.meta.server) {
21
+ return null;
22
+ }
23
+ if (!deploymentUrl) {
24
+ throw new Error(
25
+ "`useAsyncPaginatedQuery` could not fetch during SSR: no Convex deployment URL is configured. Set NUXT_PUBLIC_CONVEX_URL or `convex.url` in nuxt.config."
26
+ );
27
+ }
28
+ const result2 = await fetchQuery(
29
+ query,
30
+ { ...currentArgs, paginationOpts: { numItems: initialNumItems, cursor: null } },
31
+ { token: await resolveToken(), url: deploymentUrl }
32
+ );
33
+ return { page: convexToJson(result2.page), isDone: result2.isDone };
34
+ },
35
+ { server, lazy, deep: false, immediate: initialArgs !== "skip" }
36
+ );
37
+ const payloadPage = computed(() => {
38
+ const entry = asyncData.data.value;
39
+ return entry ? jsonToConvex(entry.page) : void 0;
40
+ });
41
+ const live = !import.meta.server && client ? usePaginatedQuery(query, args, { initialNumItems }) : void 0;
42
+ const liveState = computed(() => {
43
+ if (!live) return void 0;
44
+ try {
45
+ return { results: live.results.value, status: live.status.value, error: null };
46
+ } catch (thrown) {
47
+ return { results: [], status: "LoadingFirstPage", error: thrown };
48
+ }
49
+ });
50
+ const liveHasPage = computed(() => liveState.value !== void 0 && liveState.value.status !== "LoadingFirstPage");
51
+ const clientError = computed(() => {
52
+ if (import.meta.server || client || toValue(args) === "skip") return null;
53
+ return new Error(
54
+ "`useAsyncPaginatedQuery` could not start on the client: no Convex client is available. Set NUXT_PUBLIC_CONVEX_URL or `convex.url` in nuxt.config."
55
+ );
56
+ });
57
+ const initialArgsJson = argsJson(initialArgs);
58
+ let payloadRetired = false;
59
+ const payloadApplies = computed(() => {
60
+ if (liveHasPage.value || argsJson(toValue(args)) !== initialArgsJson) payloadRetired = true;
61
+ return !payloadRetired && payloadPage.value !== void 0;
62
+ });
63
+ const results = computed(() => {
64
+ if (liveHasPage.value) return liveState.value.results;
65
+ return payloadApplies.value ? payloadPage.value : [];
66
+ });
67
+ const status = computed(() => {
68
+ if (liveHasPage.value) return liveState.value.status;
69
+ if (payloadApplies.value) return asyncData.data.value.isDone ? "Exhausted" : "CanLoadMore";
70
+ return "LoadingFirstPage";
71
+ });
72
+ const isLoading = computed(() => status.value === "LoadingFirstPage" || status.value === "LoadingMore");
73
+ const error = computed(
74
+ () => liveState.value?.error ?? clientError.value ?? asyncData.error.value ?? null
75
+ );
76
+ const loadMore = (numItems) => {
77
+ live?.loadMore(numItems);
78
+ };
79
+ const refresh = (opts) => asyncData.refresh(opts);
80
+ const result = { results, status, isLoading, error, loadMore, refresh };
81
+ const promise = Promise.resolve(asyncData).then(() => result);
82
+ return Object.assign(promise, result);
83
+ }
84
+ function argsJson(args) {
85
+ return args === "skip" ? '"skip"' : JSON.stringify(convexToJson(args ?? {}));
86
+ }
87
+ export function defaultAsyncPaginatedQueryKey(queryName, initialArgs, initialNumItems) {
88
+ return `convex:paginated:${queryName}:${argsJson(initialArgs)}:${initialNumItems}`;
89
+ }
90
+ export const useConvexAsyncPaginatedQuery = useAsyncPaginatedQuery;
@@ -1,15 +1,3 @@
1
- /**
2
- * Nuxt-idiomatic data fetching for Convex queries.
3
- *
4
- * {@link useAsyncQuery} is a Vue/Nuxt-only addition (no `convex/react`
5
- * counterpart — see PARITY.md): it marries Nuxt's `useAsyncData` model with
6
- * Convex's live queries. During SSR the query runs over HTTP and the result is
7
- * embedded in the Nuxt payload; after hydration the composable upgrades to the
8
- * WebSocket subscription, so the page paints with server data and stays live —
9
- * no loading flash, no client refetch.
10
- *
11
- * @module
12
- */
13
1
  import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
14
2
  import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server';
15
3
  /**
@@ -94,7 +82,13 @@ export interface AsyncQueryReturn<T> extends PromiseLike<AsyncQueryData<T>> {
94
82
  dedupe?: 'cancel' | 'defer';
95
83
  }) => Promise<void>;
96
84
  }
97
- type AsyncQueryData<T> = Pick<AsyncQueryReturn<T>, 'data' | 'error' | 'status' | 'refresh'>;
85
+ /**
86
+ * The reactive half of {@link AsyncQueryReturn} — what `await useAsyncQuery(...)`
87
+ * resolves to, and the shape to annotate a value passed on from the call site.
88
+ *
89
+ * @public
90
+ */
91
+ export type AsyncQueryData<T> = Pick<AsyncQueryReturn<T>, 'data' | 'error' | 'status' | 'refresh'>;
98
92
  /**
99
93
  * Load a Convex query the Nuxt way: fetched on the server during SSR,
100
94
  * hydrated through the Nuxt payload, then seamlessly upgraded to a live
@@ -137,8 +131,5 @@ type AsyncQueryData<T> = Pick<AsyncQueryReturn<T>, 'data' | 'error' | 'status' |
137
131
  * @public
138
132
  */
139
133
  export declare function useAsyncQuery<Query extends FunctionReference<'query'>>(query: Query, args?: MaybeRefOrGetter<FunctionArgs<Query> | 'skip'>, options?: AsyncQueryOptions): AsyncQueryReturn<FunctionReturnType<Query>>;
140
- /** Payload key for a query + initial args. Exported for tests. @internal */
141
- export declare function defaultAsyncQueryKey(queryName: string, initialArgs: unknown): string;
142
134
  /** @public */
143
135
  export declare const useConvexAsyncQuery: typeof useAsyncQuery;
144
- export {};
@@ -4,6 +4,7 @@ import { getFunctionName, makeFunctionReference } from "convex/server";
4
4
  import { convexToJson, jsonToConvex } from "convex/values";
5
5
  import { useConvex } from "../../vue/client.js";
6
6
  import { useConvexQueries } from "../../vue/composables/use-queries.js";
7
+ import { CONVEX_INITIAL_TOKEN_KEY } from "../config.js";
7
8
  import { fetchQuery } from "../index.js";
8
9
  export function useAsyncQuery(query, args, options = {}) {
9
10
  const { server = true, lazy = false, live = true, token } = options;
@@ -11,13 +12,7 @@ export function useAsyncQuery(query, args, options = {}) {
11
12
  const queryName = getFunctionName(queryReference);
12
13
  const initialArgs = toValue(args) ?? {};
13
14
  const key = options.key ?? defaultAsyncQueryKey(queryName, initialArgs);
14
- const initialToken = useState("convex:initialToken", () => null);
15
- const client = useConvex();
16
- const deploymentUrl = import.meta.server ? useRuntimeConfig().public.convex.url : "";
17
- const resolveToken = async () => {
18
- const resolved = typeof token === "function" ? await token() : token ?? initialToken.value;
19
- return resolved ?? void 0;
20
- };
15
+ const { client, deploymentUrl, resolveToken } = useAsyncQueryContext(token);
21
16
  const asyncData = useAsyncData(
22
17
  key,
23
18
  async () => {
@@ -28,7 +23,7 @@ export function useAsyncQuery(query, args, options = {}) {
28
23
  if (import.meta.server) {
29
24
  if (!deploymentUrl) {
30
25
  throw new Error(
31
- "`useAsyncQuery` could not fetch during SSR: no Convex deployment URL is configured. Set the `convex.url` module option or NUXT_PUBLIC_CONVEX_URL."
26
+ "`useAsyncQuery` could not fetch during SSR: no Convex deployment URL is configured. Set NUXT_PUBLIC_CONVEX_URL or `convex.url` in nuxt.config."
32
27
  );
33
28
  }
34
29
  const value2 = await fetchQuery(queryReference, currentArgs, {
@@ -39,7 +34,7 @@ export function useAsyncQuery(query, args, options = {}) {
39
34
  }
40
35
  if (!client) {
41
36
  throw new Error(
42
- "`useAsyncQuery` could not fetch on the client: no Convex client is available. Is the `convex.url` module option (or NUXT_PUBLIC_CONVEX_URL) configured?"
37
+ "`useAsyncQuery` could not fetch on the client: no Convex client is available. Set NUXT_PUBLIC_CONVEX_URL or `convex.url` in nuxt.config."
43
38
  );
44
39
  }
45
40
  const value = await client.query(queryReference, currentArgs);
@@ -97,6 +92,16 @@ export function useAsyncQuery(query, args, options = {}) {
97
92
  const promise = Promise.resolve(asyncData).then(() => result);
98
93
  return Object.assign(promise, result);
99
94
  }
95
+ export function useAsyncQueryContext(token) {
96
+ const initialToken = useState(CONVEX_INITIAL_TOKEN_KEY, () => null);
97
+ const client = useConvex();
98
+ const deploymentUrl = import.meta.server ? useRuntimeConfig().public.convex.url : "";
99
+ const resolveToken = async () => {
100
+ const resolved = typeof token === "function" ? await token() : token ?? initialToken.value;
101
+ return resolved ?? void 0;
102
+ };
103
+ return { client, deploymentUrl, resolveToken };
104
+ }
100
105
  export function defaultAsyncQueryKey(queryName, initialArgs) {
101
106
  const argsJson = initialArgs === "skip" ? '"skip"' : JSON.stringify(convexToJson(initialArgs ?? {}));
102
107
  return `convex:${queryName}:${argsJson}`;
@@ -1,3 +1,14 @@
1
+ /**
2
+ * Nuxt `useState` key the Better Auth server plugin stashes the SSR-prefetched
3
+ * Convex JWT under, read back by the client plugin and `useAsyncQuery`. Lives
4
+ * here, dependency-free, so the core composable can read the token without
5
+ * importing the integration.
6
+ */
7
+ export declare const CONVEX_INITIAL_TOKEN_KEY = "convex:initialToken";
8
+ /**
9
+ * The module's runtime config, tolerating a missing Nuxt context (a plain
10
+ * server import outside a request) by returning an empty object.
11
+ */
1
12
  export declare function getConvexRuntimeConfig(): {
2
13
  url: string | undefined;
3
14
  siteUrl: string | undefined;
@@ -1,4 +1,5 @@
1
1
  import { useRuntimeConfig } from "#imports";
2
+ export const CONVEX_INITIAL_TOKEN_KEY = "convex:initialToken";
2
3
  export function getConvexRuntimeConfig() {
3
4
  try {
4
5
  const config = useRuntimeConfig();
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Pure helpers behind the Convex-aware Content Security Policy the module
3
+ * applies when `nuxt-security` is present — shared by the Nitro plugin
4
+ * (`./security`) and the unit tests. Import-free so it runs anywhere.
5
+ */
6
+ /**
7
+ * The slice of a nuxt-security rule set this module touches: the resolved
8
+ * options for one route (`rules['/**']` at runtime), where `headers` and
9
+ * `contentSecurityPolicy` may each be `false` when the app switched them off.
10
+ */
11
+ export interface SecurityRules {
12
+ headers?: false | {
13
+ contentSecurityPolicy?: false | Record<string, unknown>;
14
+ [header: string]: unknown;
15
+ };
16
+ [option: string]: unknown;
17
+ }
18
+ /**
19
+ * CSP `connect-src` entries the browser needs to reach a Convex deployment: the
20
+ * deployment URL over both HTTPS and WebSocket (the realtime sync channel) and,
21
+ * when configured, the `.site` URL that serves Convex HTTP actions. Returns
22
+ * `[]` for empty or unparseable input.
23
+ */
24
+ export declare function convexConnectSrc(url?: string, siteUrl?: string): string[];
25
+ /**
26
+ * CSP sources for Convex-served resources (`img-src` / `media-src`): files
27
+ * uploaded through `useStorageUrl()` are served from the deployment origin,
28
+ * and files streamed by HTTP actions from the `.site` origin.
29
+ */
30
+ export declare function convexResourceSrc(url?: string, siteUrl?: string): string[];
31
+ /**
32
+ * Apply nuxt-convex-module's secure-by-default, Convex-aware CSP onto a
33
+ * nuxt-security rule set (mutated in place). This tightens the directives we
34
+ * can safely pre-fill — locking network egress and Convex-served media to
35
+ * same-origin plus the Convex deployment — while leaving every other directive
36
+ * (script/style/font, etc.) to nuxt-security's own defaults.
37
+ *
38
+ * For each directive we keep any value the user already set and *append* the
39
+ * Convex origins, so consumers extend (add their own third parties) rather than
40
+ * fight the defaults, and the Convex origins are always present. A user who sets
41
+ * `contentSecurityPolicy: false` (CSP disabled) is left untouched, as is a rule
42
+ * set with nothing to add (no Convex URL).
43
+ */
44
+ export declare function applyConvexSecurityDefaults(rules: SecurityRules, connectSrc: string[], resourceSrc: string[]): void;
@@ -0,0 +1,46 @@
1
+ function toHttpOrigin(raw) {
2
+ if (!raw) return void 0;
3
+ try {
4
+ return new URL(raw).origin;
5
+ } catch {
6
+ return void 0;
7
+ }
8
+ }
9
+ function toWsOrigin(raw) {
10
+ if (!raw) return void 0;
11
+ try {
12
+ const u = new URL(raw);
13
+ return `${u.protocol === "https:" ? "wss:" : "ws:"}//${u.host}`;
14
+ } catch {
15
+ return void 0;
16
+ }
17
+ }
18
+ function uniq(values) {
19
+ return Array.from(new Set(values.filter((v) => Boolean(v))));
20
+ }
21
+ export function convexConnectSrc(url, siteUrl) {
22
+ return uniq([toHttpOrigin(url), toWsOrigin(url), toHttpOrigin(siteUrl), toWsOrigin(siteUrl)]);
23
+ }
24
+ export function convexResourceSrc(url, siteUrl) {
25
+ return uniq([toHttpOrigin(url), toHttpOrigin(siteUrl)]);
26
+ }
27
+ export function applyConvexSecurityDefaults(rules, connectSrc, resourceSrc) {
28
+ if (connectSrc.length === 0 && resourceSrc.length === 0) return;
29
+ if (rules.headers === false) return;
30
+ if (typeof rules.headers !== "object" || rules.headers === null) rules.headers = {};
31
+ const headers = rules.headers;
32
+ if (headers.contentSecurityPolicy === false) return;
33
+ if (typeof headers.contentSecurityPolicy !== "object" || headers.contentSecurityPolicy === null) {
34
+ headers.contentSecurityPolicy = {};
35
+ }
36
+ const csp = headers.contentSecurityPolicy;
37
+ tightenDirective(csp, "connect-src", ["'self'"], connectSrc);
38
+ tightenDirective(csp, "img-src", ["'self'", "data:"], resourceSrc);
39
+ tightenDirective(csp, "media-src", ["'self'"], resourceSrc);
40
+ }
41
+ function tightenDirective(csp, name, baseline, additions) {
42
+ if (additions.length === 0) return;
43
+ const current = csp[name];
44
+ const existing = Array.isArray(current) ? current : typeof current === "string" ? current.split(/\s+/).filter(Boolean) : baseline;
45
+ csp[name] = uniq([...existing, ...additions]);
46
+ }
@@ -8,9 +8,12 @@
8
8
  *
9
9
  * ## Usage
10
10
  *
11
- * All exported functions assume that a Convex deployment URL is set in the
12
- * `NUXT_PUBLIC_CONVEX_URL` environment variable. `npx convex dev` will
13
- * automatically set it during local development.
11
+ * All exported functions assume that a Convex deployment URL is configured —
12
+ * `convex.url` in `nuxt.config`, `NUXT_PUBLIC_CONVEX_URL`, or the `CONVEX_URL`
13
+ * that `npx convex dev` writes to `.env.local`. Unlike its Next counterpart,
14
+ * `npx convex dev` does *not* set the framework-prefixed name for Nuxt: the
15
+ * Convex CLI has no Nuxt case in its framework detection, so it writes the
16
+ * unprefixed `CONVEX_URL`, which the module reads too.
14
17
  *
15
18
  * ### Preloading data
16
19
  *
@@ -51,15 +54,12 @@ export type NuxtOptions = {
51
54
  token?: string;
52
55
  /**
53
56
  * The URL of the Convex deployment to use for the function call.
54
- * Defaults to `process.env.NUXT_PUBLIC_CONVEX_URL` if not provided.
57
+ * Defaults to `runtimeConfig.public.convex.url`, then to
58
+ * `process.env.NUXT_PUBLIC_CONVEX_URL`, if not provided.
55
59
  *
56
60
  * Explicitly passing undefined here (such as from missing ENV variables) will throw an error in the future.
57
61
  */
58
62
  url?: string;
59
- /**
60
- * @internal
61
- */
62
- adminToken?: string;
63
63
  /**
64
64
  * Skip validating that the Convex deployment URL looks like
65
65
  * `https://happy-animal-123.convex.cloud` or localhost.
@@ -0,0 +1,33 @@
1
+ import { type SecurityRules } from './csp.js';
2
+ /**
3
+ * The slice of the Nitro app this plugin touches, typed locally: the hook's
4
+ * real declaration lives in the types nuxt-security generates into the app,
5
+ * which only exist when the app has it installed — and this plugin is only
6
+ * registered then. Nitro itself is not imported, so the published types don't
7
+ * depend on how the app resolves `nitropack`.
8
+ */
9
+ export interface SecurityNitroApp {
10
+ hooks: {
11
+ hook: (name: 'nuxt-security:routeRules', fn: (rules: Record<string, SecurityRules>) => void) => unknown;
12
+ };
13
+ }
14
+ /**
15
+ * Nitro plugin: extend nuxt-security's Content Security Policy with the Convex
16
+ * origins. Registered by the module only when the nuxt-security integration is
17
+ * on (see `registerSecurity` in `module.ts`).
18
+ *
19
+ * Listens for `nuxt-security:routeRules`, which nuxt-security fires once its
20
+ * global config and every route rule are merged into one table — so the
21
+ * origins are appended to whatever the app configured, whatever the order of
22
+ * `modules`. nuxt-security sorts its own Nitro plugins last, so this listener
23
+ * is in place before the hook fires. The deployment URL comes from runtime
24
+ * config, so it need not be known at build time.
25
+ *
26
+ * `connect-src` is tightened only in production: creating it in dev would
27
+ * block the Vite HMR and devtools WebSockets, which run on ports `'self'`
28
+ * doesn't cover, while nuxt-security's defaults leave the directive open.
29
+ * `img-src` / `media-src` are widened in dev too — nuxt-security enforces its
30
+ * default `img-src 'self' data:` during `nuxt dev` as well, which would block
31
+ * files served from Convex storage.
32
+ */
33
+ export default function convexSecurityPlugin(nitroApp: SecurityNitroApp): void;
@@ -0,0 +1,14 @@
1
+ import { getConvexRuntimeConfig } from "./config.js";
2
+ import { applyConvexSecurityDefaults, convexConnectSrc, convexResourceSrc } from "./csp.js";
3
+ export default function convexSecurityPlugin(nitroApp) {
4
+ nitroApp.hooks.hook("nuxt-security:routeRules", (rules) => {
5
+ const global = rules["/**"];
6
+ if (!global) return;
7
+ const { url, siteUrl } = getConvexRuntimeConfig();
8
+ applyConvexSecurityDefaults(
9
+ global,
10
+ import.meta.dev ? [] : convexConnectSrc(url, siteUrl),
11
+ convexResourceSrc(url, siteUrl)
12
+ );
13
+ });
14
+ }
@@ -48,10 +48,15 @@ export interface PolarComponentApi {
48
48
  }>;
49
49
  }
50
50
  /**
51
+ * Renders a link to the Polar customer portal for the signed-in user, where
52
+ * they manage their subscription.
53
+ *
51
54
  * @example
52
55
  * ```vue
53
56
  * <CustomerPortalLink>Manage subscription</CustomerPortalLink>
54
57
  * ```
58
+ *
59
+ * @public
55
60
  */
56
61
  export declare const CustomerPortalLink: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
57
62
  polarApi: {
@@ -86,6 +91,8 @@ export declare const CustomerPortalLink: import("vue").DefineComponent<import("v
86
91
  * Start free trial
87
92
  * </CheckoutLink>
88
93
  * ```
94
+ *
95
+ * @public
89
96
  */
90
97
  export declare const CheckoutLink: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
91
98
  polarApi: {
@@ -172,9 +179,9 @@ export declare const CheckoutLink: import("vue").DefineComponent<import("vue").E
172
179
  default: boolean;
173
180
  };
174
181
  }>> & Readonly<{}>, {
182
+ embed: boolean;
175
183
  lazy: boolean;
176
184
  metadata: Record<string, string>;
177
- embed: boolean;
178
185
  subscriptionId: string;
179
186
  trialInterval: "day" | "week" | "month" | "year" | null;
180
187
  trialIntervalCount: number | null;
@@ -13,7 +13,7 @@ export const CustomerPortalLink = defineComponent({
13
13
  const polarApi = props.polarApi ?? useConvexNamespace("billing");
14
14
  const generateCustomerPortalUrl = polarApi?.generateCustomerPortalUrl ? useAction(polarApi.generateCustomerPortalUrl) : void 0;
15
15
  const portalUrl = ref();
16
- if (import.meta.client && generateCustomerPortalUrl) {
16
+ if (!import.meta.server && generateCustomerPortalUrl) {
17
17
  watch(
18
18
  () => props.returnUrl,
19
19
  (returnUrl) => {
@@ -55,7 +55,7 @@ export const CheckoutLink = defineComponent({
55
55
  const generateCheckoutLink = polarApi?.generateCheckoutLink ? useAction(polarApi.generateCheckoutLink) : void 0;
56
56
  const checkoutLink = ref();
57
57
  const isLoading = ref(false);
58
- if (import.meta.client && generateCheckoutLink) {
58
+ if (!import.meta.server && generateCheckoutLink) {
59
59
  watch(
60
60
  () => [props.lazy, props.productIds, props.subscriptionId, props.metadata, props.embed, props.trialInterval, props.trialIntervalCount, props.locale],
61
61
  async () => {
@@ -99,7 +99,7 @@ export const CheckoutLink = defineComponent({
99
99
  const { PolarEmbedCheckout } = await import("@polar-sh/checkout/embed");
100
100
  await PolarEmbedCheckout.create(url, { theme: props.theme });
101
101
  } else {
102
- window.open(url, "_blank");
102
+ window.open(url, "_blank", "noopener");
103
103
  }
104
104
  } finally {
105
105
  isLoading.value = false;
@@ -89,9 +89,9 @@ export interface ConvexAuthProviderOptions {
89
89
  * @example
90
90
  * ```vue
91
91
  * <script setup lang="ts">
92
- * import { useAuth } from '~/composables/useAuth' // your auth provider
92
+ * import { useAuth } from '~/composables/useMyAuthProvider' // your auth provider
93
93
  *
94
- * const { client } = useNuxtApp().$convex
94
+ * const client = useConvex()
95
95
  * const authState = provideConvexAuth({ client, useAuth })
96
96
  * </script>
97
97
  * ```
@@ -112,11 +112,11 @@ export declare function provideConvexAuth(options: ConvexAuthProviderOptions): C
112
112
  * (`nuxtApp.vueApp.provide`) rather than from a component's setup function.
113
113
  *
114
114
  * When `scope` is provided, all watchers are created inside that
115
- * {@link EffectScope} so callers can dispose them later.
115
+ * `EffectScope` so callers can dispose them later.
116
116
  */
117
117
  export declare function createConvexAuthState(options: ConvexAuthProviderOptions, scope?: EffectScope): ConvexAuthState;
118
118
  /**
119
- * Create a fresh {@link EffectScope} and build a Convex auth state inside it.
119
+ * Create a fresh `EffectScope` and build a Convex auth state inside it.
120
120
  * The scope is returned so the caller can `.stop()` it on teardown.
121
121
  */
122
122
  export declare function createScopedConvexAuthState(options: ConvexAuthProviderOptions): {
@@ -84,10 +84,14 @@ export function createConvexAuthState(options, scope) {
84
84
  const isAuthenticated = computed(
85
85
  () => toValue(authProviderAuthenticated) && (isConvexAuthenticated.value ?? false)
86
86
  );
87
+ const isLoading = computed(() => isConvexAuthenticated.value === null);
88
+ const isRefreshingWhileAuthenticated = computed(
89
+ () => isRefreshing.value && isAuthenticated.value
90
+ );
87
91
  return {
88
- isLoading: computed(() => isConvexAuthenticated.value === null),
92
+ isLoading,
89
93
  isAuthenticated,
90
- isRefreshing: computed(() => isRefreshing.value && isAuthenticated.value)
94
+ isRefreshing: isRefreshingWhileAuthenticated
91
95
  };
92
96
  }
93
97
  export function createScopedConvexAuthState(options) {