use-convex 0.0.2 → 1.0.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 (52) hide show
  1. package/README.md +84 -27
  2. package/dist/module.d.mts +5 -3
  3. package/dist/module.mjs +35 -1
  4. package/dist/runtime/composables/useAuth.d.ts +5 -5
  5. package/dist/runtime/composables/useAuth.js +11 -24
  6. package/dist/runtime/composables/useAuthToken.d.ts +1 -1
  7. package/dist/runtime/composables/useAuthToken.js +2 -10
  8. package/dist/runtime/composables/useConvex.js +1 -1
  9. package/dist/runtime/composables/useConvexAction.js +6 -18
  10. package/dist/runtime/composables/useConvexAuth.js +4 -13
  11. package/dist/runtime/composables/useConvexFileUpload.d.ts +43 -0
  12. package/dist/runtime/composables/useConvexFileUpload.js +102 -0
  13. package/dist/runtime/composables/useConvexGate.js +3 -9
  14. package/dist/runtime/composables/useConvexMutation.js +7 -17
  15. package/dist/runtime/composables/useConvexPaginatedQuery.d.ts +1 -1
  16. package/dist/runtime/composables/useConvexPaginatedQuery.js +7 -29
  17. package/dist/runtime/composables/useConvexQueries.d.ts +12 -7
  18. package/dist/runtime/composables/useConvexQueries.js +1 -3
  19. package/dist/runtime/composables/useConvexQuery.js +6 -12
  20. package/dist/runtime/composables/useConvexR2Upload.d.ts +40 -0
  21. package/dist/runtime/composables/useConvexR2Upload.js +86 -0
  22. package/dist/runtime/plugin.client.js +3 -7
  23. package/dist/runtime/plugin.devtools.client.d.ts +15 -2
  24. package/dist/runtime/plugin.devtools.client.js +18 -4
  25. package/dist/runtime/plugin.server.js +3 -7
  26. package/dist/runtime/server/api/convex/auth/session.js +7 -12
  27. package/dist/runtime/server/auth.js +1 -9
  28. package/dist/runtime/server/authCookies.js +8 -30
  29. package/dist/runtime/server/convexConfig.d.ts +12 -0
  30. package/dist/runtime/server/convexConfig.js +9 -0
  31. package/dist/runtime/server/fetch.d.ts +2 -4
  32. package/dist/runtime/server/fetch.js +3 -13
  33. package/dist/runtime/server/routes/__convex_devtools.get.d.ts +6 -2
  34. package/dist/runtime/server/routes/__convex_devtools.get.js +184 -16
  35. package/dist/runtime/server/sameOrigin.js +1 -5
  36. package/dist/runtime/utils/authCookie.d.ts +0 -2
  37. package/dist/runtime/utils/authCookie.js +0 -1
  38. package/dist/runtime/utils/authState.js +1 -6
  39. package/dist/runtime/utils/authStorage.js +1 -1
  40. package/dist/runtime/utils/context.js +3 -2
  41. package/dist/runtime/utils/convexDashboard.d.ts +44 -0
  42. package/dist/runtime/utils/convexDashboard.js +77 -0
  43. package/dist/runtime/utils/errors.d.ts +14 -0
  44. package/dist/runtime/utils/errors.js +25 -0
  45. package/dist/runtime/utils/oauthRedirect.js +3 -2
  46. package/dist/runtime/utils/paginatedOptimistic.js +4 -22
  47. package/dist/runtime/utils/payloadCache.d.ts +6 -0
  48. package/dist/runtime/utils/payloadCache.js +7 -0
  49. package/dist/runtime/utils/pendingError.d.ts +9 -0
  50. package/dist/runtime/utils/pendingError.js +23 -0
  51. package/dist/types.d.mts +7 -3
  52. package/package.json +36 -20
@@ -1,35 +1,25 @@
1
- import { computed, ref, toValue } from "vue";
1
+ import { toValue } from "vue";
2
2
  import { useConvexContext } from "../utils/context.js";
3
+ import { createPendingErrorState } from "../utils/pendingError.js";
3
4
  export function useConvexMutation(mutation, options = {}) {
4
5
  const ctx = useConvexContext();
5
- const error = ref(null);
6
- const pendingCount = ref(0);
6
+ const { error, pending, withPending } = createPendingErrorState();
7
7
  const mutate = async (args = {}) => {
8
8
  if (import.meta.server || !ctx.client) {
9
- throw new Error(
10
- "[convex-nuxt] useConvexMutation can only run in the browser."
11
- );
9
+ throw new Error("[use-convex] useConvexMutation can only run in the browser.");
12
10
  }
13
- pendingCount.value++;
14
- error.value = null;
15
- try {
11
+ return await withPending(async () => {
16
12
  const resolved = toValue(args);
17
13
  return await ctx.client.mutation(
18
14
  mutation,
19
15
  resolved,
20
16
  options.optimisticUpdate ? { optimisticUpdate: options.optimisticUpdate } : void 0
21
17
  );
22
- } catch (cause) {
23
- const err = cause instanceof Error ? cause : new Error(String(cause));
24
- error.value = err;
25
- throw err;
26
- } finally {
27
- pendingCount.value--;
28
- }
18
+ });
29
19
  };
30
20
  return {
31
21
  mutate,
32
22
  error,
33
- pending: computed(() => pendingCount.value > 0)
23
+ pending
34
24
  };
35
25
  }
@@ -6,7 +6,7 @@ import { type ComputedRef, type MaybeRefOrGetter, type Ref } from 'vue';
6
6
  */
7
7
  export type PaginatedQueryReference = FunctionReference<'query', 'public', {
8
8
  paginationOpts: PaginationOptions;
9
- }, PaginationResult<any>>;
9
+ }, PaginationResult<unknown>>;
10
10
  export type PaginatedQueryArgs<Query extends PaginatedQueryReference> = Omit<FunctionArgs<Query>, 'paginationOpts'>;
11
11
  export type PaginatedQueryItem<Query extends PaginatedQueryReference> = FunctionReturnType<Query>['page'][number];
12
12
  export type PaginationStatus = 'LoadingFirstPage' | 'CanLoadMore' | 'LoadingMore' | 'Exhausted';
@@ -1,5 +1,5 @@
1
1
  import { convexToJson, jsonToConvex } from "convex/values";
2
- import { useAsyncData, useNuxtApp, useRuntimeConfig } from "nuxt/app";
2
+ import { useAsyncData, useRuntimeConfig } from "nuxt/app";
3
3
  import {
4
4
  computed,
5
5
  onScopeDispose,
@@ -10,24 +10,11 @@ import {
10
10
  } from "vue";
11
11
  import { resolveAuthGatedArgs } from "../utils/authGate.js";
12
12
  import { useConvexContext } from "../utils/context.js";
13
+ import { readHydratedPayloadCache } from "../utils/payloadCache.js";
13
14
  import { convexQueryKey } from "../utils/queryKey.js";
14
- function mapLiveStatus(status) {
15
- switch (status) {
16
- case "LoadingFirstPage":
17
- return "LoadingFirstPage";
18
- case "LoadingMore":
19
- return "LoadingMore";
20
- case "Exhausted":
21
- return "Exhausted";
22
- case "CanLoadMore":
23
- return "CanLoadMore";
24
- }
25
- }
26
15
  export async function useConvexPaginatedQuery(query, args = {}, options) {
27
16
  if (options.initialNumItems <= 0) {
28
- throw new Error(
29
- "[convex-nuxt] useConvexPaginatedQuery initialNumItems must be > 0"
30
- );
17
+ throw new Error("[use-convex] useConvexPaginatedQuery initialNumItems must be > 0");
31
18
  }
32
19
  const runtimeConfig = useRuntimeConfig();
33
20
  const defaultServer = runtimeConfig.public.convex?.server;
@@ -63,15 +50,10 @@ export async function useConvexPaginatedQuery(query, args = {}, options) {
63
50
  }
64
51
  const token = options.token ?? ctx.ssrToken.value;
65
52
  if (requireAuth && import.meta.client && !token) {
66
- const nuxtApp = useNuxtApp();
67
- const cached = nuxtApp.payload.data[toValue(key)];
68
- return cached ?? null;
53
+ return readHydratedPayloadCache(key);
69
54
  }
70
55
  const http = ctx.createHttpClient({ token });
71
- const result = await http.query(
72
- query,
73
- pageArgs
74
- );
56
+ const result = await http.query(query, pageArgs);
75
57
  return jsonToConvex(convexToJson(result));
76
58
  },
77
59
  {
@@ -104,7 +86,7 @@ export async function useConvexPaginatedQuery(query, args = {}, options) {
104
86
  return "Exhausted";
105
87
  }
106
88
  if (liveReady.value && live.value) {
107
- return mapLiveStatus(live.value.status);
89
+ return live.value.status;
108
90
  }
109
91
  if (asyncData.pending.value || import.meta.client && !liveReady.value) {
110
92
  if (asyncData.data.value) {
@@ -153,11 +135,7 @@ export async function useConvexPaginatedQuery(query, args = {}, options) {
153
135
  );
154
136
  };
155
137
  watch(
156
- () => [
157
- toValue(args),
158
- options.initialNumItems,
159
- ctx.auth.isAuthenticated.value
160
- ],
138
+ () => [toValue(args), options.initialNumItems, ctx.auth.isAuthenticated.value],
161
139
  subscribe,
162
140
  { immediate: true }
163
141
  );
@@ -1,14 +1,19 @@
1
- import type { FunctionReference, FunctionReturnType } from 'convex/server';
2
- import type { Value } from 'convex/values';
1
+ import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server';
3
2
  import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
4
- export type ConvexQueriesRequest = Record<string, {
5
- query: FunctionReference<'query'>;
6
- args: Record<string, Value>;
7
- } | 'skip'>;
3
+ /** One entry in a {@link useConvexQueries} request map. */
4
+ export type ConvexQueryRequestEntry<Query extends FunctionReference<'query'> = FunctionReference<'query'>> = {
5
+ query: Query;
6
+ args: FunctionArgs<Query>;
7
+ };
8
+ /**
9
+ * Request map for {@link useConvexQueries}.
10
+ * Values are `{ query, args }` or `'skip'`.
11
+ */
12
+ export type ConvexQueriesRequest = Record<string, ConvexQueryRequestEntry | 'skip'>;
8
13
  export type ConvexQueriesResult<Request extends ConvexQueriesRequest> = {
9
14
  [K in keyof Request]: Request[K] extends {
10
15
  query: infer Query;
11
- args: any;
16
+ args: infer _Args;
12
17
  } ? Query extends FunctionReference<'query'> ? FunctionReturnType<Query> | undefined | Error : undefined : undefined;
13
18
  };
14
19
  /**
@@ -15,9 +15,7 @@ export function useConvexQueries(queries) {
15
15
  const ctx = useConvexContext();
16
16
  const results = shallowRef({});
17
17
  if (import.meta.server || !ctx.client) {
18
- return computed(
19
- () => results.value
20
- );
18
+ return computed(() => results.value);
21
19
  }
22
20
  const client = ctx.client;
23
21
  const unsubscribers = /* @__PURE__ */ new Map();
@@ -1,5 +1,5 @@
1
1
  import { convexToJson, jsonToConvex } from "convex/values";
2
- import { useAsyncData, useNuxtApp, useRuntimeConfig } from "nuxt/app";
2
+ import { useAsyncData, useRuntimeConfig } from "nuxt/app";
3
3
  import {
4
4
  computed,
5
5
  onScopeDispose,
@@ -10,6 +10,7 @@ import {
10
10
  import { resolveAuthGatedArgs } from "../utils/authGate.js";
11
11
  import { useConvexContext } from "../utils/context.js";
12
12
  import { resolveQueryOverlay } from "../utils/overlay.js";
13
+ import { readHydratedPayloadCache } from "../utils/payloadCache.js";
13
14
  import { convexQueryKey } from "../utils/queryKey.js";
14
15
  export async function useConvexQuery(query, args = {}, options = {}) {
15
16
  const runtimeConfig = useRuntimeConfig();
@@ -23,9 +24,7 @@ export async function useConvexQuery(query, args = {}, options = {}) {
23
24
  authenticated: requireAuth,
24
25
  isAuthenticated: ctx.auth.isAuthenticated.value
25
26
  });
26
- const key = computed(
27
- () => convexQueryKey(query, resolveRawArgs(), options.key)
28
- );
27
+ const key = computed(() => convexQueryKey(query, resolveRawArgs(), options.key));
29
28
  const asyncData = await useAsyncData(
30
29
  key,
31
30
  async () => {
@@ -35,15 +34,10 @@ export async function useConvexQuery(query, args = {}, options = {}) {
35
34
  }
36
35
  const token = options.token ?? ctx.ssrToken.value;
37
36
  if (requireAuth && import.meta.client && !token) {
38
- const nuxtApp = useNuxtApp();
39
- const cached = nuxtApp.payload.data[toValue(key)];
40
- return cached ?? null;
37
+ return readHydratedPayloadCache(key);
41
38
  }
42
39
  const http = ctx.createHttpClient({ token });
43
- const result = await http.query(
44
- query,
45
- argsValue ?? {}
46
- );
40
+ const result = await http.query(query, argsValue ?? {});
47
41
  return jsonToConvex(convexToJson(result));
48
42
  },
49
43
  {
@@ -87,7 +81,7 @@ export async function useConvexQuery(query, args = {}, options = {}) {
87
81
  }
88
82
  const client = ctx.client;
89
83
  if (!client) {
90
- throw new Error("[convex-nuxt] ConvexClient is not available on the client.");
84
+ throw new Error("[use-convex] ConvexClient is not available on the client.");
91
85
  }
92
86
  const liveReady = ref(false);
93
87
  const liveData = ref();
@@ -0,0 +1,40 @@
1
+ import type { FunctionReference, FunctionReturnType } from 'convex/server';
2
+ /**
3
+ * Subset of `@convex-dev/r2` `clientApi()` exports needed for client uploads.
4
+ * Pass `api.example` (or any module that re-exports these two mutations).
5
+ */
6
+ export type ConvexR2UploadApi = {
7
+ generateUploadUrl: FunctionReference<'mutation'>;
8
+ syncMetadata: FunctionReference<'mutation'>;
9
+ };
10
+ export type ConvexR2UploadProgress = {
11
+ loaded: number;
12
+ total: number;
13
+ };
14
+ /**
15
+ * PUT a file to an R2 signed URL (matches `@convex-dev/r2` client upload).
16
+ */
17
+ export declare function putFileToR2UploadUrl(uploadUrl: string, file: File, onProgress?: (progress: ConvexR2UploadProgress) => void): Promise<void>;
18
+ /**
19
+ * Vue counterpart of `@convex-dev/r2/react`'s `useUploadFile`.
20
+ *
21
+ * Expects the object returned by `r2.clientApi()` (at least
22
+ * `generateUploadUrl` + `syncMetadata`). Flow:
23
+ * 1. `generateUploadUrl()` → `{ url, key }`
24
+ * 2. PUT file bytes to the signed URL
25
+ * 3. `syncMetadata({ key })`
26
+ * 4. return `key`
27
+ *
28
+ * Does **not** depend on `@convex-dev/r2` at runtime — only on Convex
29
+ * function references your app exports. Prefer this over built-in
30
+ * `useConvexFileUpload` for large / resumable-oriented object storage.
31
+ */
32
+ export declare function useConvexR2Upload(api: ConvexR2UploadApi): {
33
+ upload: (file: File, options?: {
34
+ onProgress?: (progress: ConvexR2UploadProgress) => void;
35
+ }) => Promise<string>;
36
+ error: import("vue").Ref<Error | null, Error | null>;
37
+ pending: import("vue").ComputedRef<boolean>;
38
+ progress: import("vue").ComputedRef<number | null>;
39
+ };
40
+ export type { FunctionReturnType };
@@ -0,0 +1,86 @@
1
+ import { computed, ref } from "vue";
2
+ import { useConvexContext } from "../utils/context.js";
3
+ function parseUploadUrlPayload(value) {
4
+ if (typeof value === "object" && value !== null && "url" in value && "key" in value && typeof value.url === "string" && typeof value.key === "string" && value.url.length > 0 && value.key.length > 0) {
5
+ return {
6
+ url: value.url,
7
+ key: value.key
8
+ };
9
+ }
10
+ throw new Error("[use-convex] generateUploadUrl must return `{ url, key }` strings.");
11
+ }
12
+ export function putFileToR2UploadUrl(uploadUrl, file, onProgress) {
13
+ return new Promise((resolve, reject) => {
14
+ const xhr = new XMLHttpRequest();
15
+ xhr.open("PUT", uploadUrl);
16
+ xhr.setRequestHeader("Content-Type", file.type || "application/octet-stream");
17
+ xhr.upload.onprogress = (event) => {
18
+ if (!onProgress) {
19
+ return;
20
+ }
21
+ onProgress({ loaded: event.loaded, total: event.total });
22
+ };
23
+ xhr.onload = () => {
24
+ if (xhr.status >= 200 && xhr.status < 300) {
25
+ resolve();
26
+ return;
27
+ }
28
+ reject(
29
+ new Error(`[use-convex] R2 upload failed (${xhr.status} ${xhr.statusText || "error"}).`)
30
+ );
31
+ };
32
+ xhr.onerror = () => {
33
+ reject(new Error("[use-convex] R2 upload network error."));
34
+ };
35
+ xhr.onabort = () => {
36
+ reject(new Error("[use-convex] R2 upload aborted."));
37
+ };
38
+ xhr.send(file);
39
+ });
40
+ }
41
+ export function useConvexR2Upload(api) {
42
+ const ctx = useConvexContext();
43
+ const error = ref(null);
44
+ const pendingCount = ref(0);
45
+ const progress = ref(null);
46
+ const upload = async (file, options) => {
47
+ if (import.meta.server || !ctx.client) {
48
+ throw new Error("[use-convex] useConvexR2Upload can only run in the browser.");
49
+ }
50
+ if (!(file instanceof File)) {
51
+ throw new TypeError("[use-convex] useConvexR2Upload expects a File.");
52
+ }
53
+ pendingCount.value++;
54
+ error.value = null;
55
+ progress.value = null;
56
+ try {
57
+ const raw = await ctx.client.mutation(
58
+ api.generateUploadUrl,
59
+ {}
60
+ );
61
+ const { url, key } = parseUploadUrlPayload(raw);
62
+ await putFileToR2UploadUrl(url, file, (event) => {
63
+ if (event.total > 0) {
64
+ progress.value = event.loaded / event.total;
65
+ }
66
+ options?.onProgress?.(event);
67
+ });
68
+ progress.value = 1;
69
+ await ctx.client.mutation(api.syncMetadata, { key });
70
+ return key;
71
+ } catch (cause) {
72
+ const err = cause instanceof Error ? cause : new Error(String(cause));
73
+ error.value = err;
74
+ throw err;
75
+ } finally {
76
+ pendingCount.value--;
77
+ progress.value = null;
78
+ }
79
+ };
80
+ return {
81
+ upload,
82
+ error,
83
+ pending: computed(() => pendingCount.value > 0),
84
+ progress: computed(() => progress.value)
85
+ };
86
+ }
@@ -2,10 +2,8 @@ import { ConvexClient } from "convex/browser";
2
2
  import { defineNuxtPlugin, useRuntimeConfig } from "nuxt/app";
3
3
  import { useSsrTokenRef } from "./utils/authCookie.js";
4
4
  import { createHttpClient } from "./utils/http.js";
5
- import {
6
- convexNuxtKey,
7
- createAuthContext
8
- } from "./utils/context.js";
5
+ import { convexNuxtKey, createAuthContext } from "./utils/context.js";
6
+ import { warnMissingConvexUrl } from "./utils/errors.js";
9
7
  export default defineNuxtPlugin({
10
8
  name: "convex-nuxt-client",
11
9
  setup(nuxtApp) {
@@ -14,9 +12,7 @@ export default defineNuxtPlugin({
14
12
  const url = convexConfig?.url;
15
13
  if (!url) {
16
14
  if (import.meta.dev) {
17
- console.warn(
18
- "[convex-nuxt] No Convex URL configured. Set convex.url or NUXT_PUBLIC_CONVEX_URL."
19
- );
15
+ warnMissingConvexUrl("client");
20
16
  }
21
17
  return;
22
18
  }
@@ -1,6 +1,19 @@
1
+ import { useConvexConnectionState } from './composables/useConvexConnectionState.js';
2
+ export interface ConvexDevtoolsBridge {
3
+ connection: ReturnType<typeof useConvexConnectionState>;
4
+ /** Auth UI / Convex confirmation flags (dev inspection only). */
5
+ auth: {
6
+ configured: boolean;
7
+ isLoading: boolean;
8
+ isAuthenticated: boolean;
9
+ isRefreshing: boolean;
10
+ isConvexAuthenticated: boolean | null;
11
+ hasSsrToken: boolean;
12
+ };
13
+ }
1
14
  /**
2
- * Expose connection state on `window.__CONVEX_NUXT__` in development for
3
- * quick inspection alongside the DevTools iframe tab.
15
+ * Expose connection + auth snapshot on `window.__CONVEX_NUXT__` in development
16
+ * for quick inspection alongside the DevTools iframe tab.
4
17
  */
5
18
  declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
6
19
  export default _default;
@@ -4,13 +4,27 @@ import { tryUseConvexContext } from "./utils/context.js";
4
4
  export default defineNuxtPlugin({
5
5
  name: "convex-nuxt-devtools",
6
6
  setup() {
7
- if (!import.meta.dev || !tryUseConvexContext()) {
7
+ const ctx = tryUseConvexContext();
8
+ if (!import.meta.dev || !ctx) {
8
9
  return;
9
10
  }
10
11
  const connection = useConvexConnectionState();
11
- if (typeof window !== "undefined") {
12
- ;
13
- window.__CONVEX_NUXT__ = { connection };
12
+ if (typeof window === "undefined") {
13
+ return;
14
14
  }
15
+ const bridge = {
16
+ connection,
17
+ get auth() {
18
+ return {
19
+ configured: ctx.auth.configured,
20
+ isLoading: ctx.auth.isLoading.value,
21
+ isAuthenticated: ctx.auth.isAuthenticated.value,
22
+ isRefreshing: ctx.auth.isRefreshing.value,
23
+ isConvexAuthenticated: ctx.auth.isConvexAuthenticated.value,
24
+ hasSsrToken: Boolean(ctx.ssrToken.value)
25
+ };
26
+ }
27
+ };
28
+ window.__CONVEX_NUXT__ = bridge;
15
29
  }
16
30
  });
@@ -1,10 +1,8 @@
1
1
  import { defineNuxtPlugin, useRuntimeConfig } from "nuxt/app";
2
2
  import { useSsrTokenRef } from "./utils/authCookie.js";
3
3
  import { createHttpClient } from "./utils/http.js";
4
- import {
5
- convexNuxtKey,
6
- createAuthContext
7
- } from "./utils/context.js";
4
+ import { convexNuxtKey, createAuthContext } from "./utils/context.js";
5
+ import { warnMissingConvexUrl } from "./utils/errors.js";
8
6
  export default defineNuxtPlugin({
9
7
  name: "convex-nuxt-server",
10
8
  setup(nuxtApp) {
@@ -13,9 +11,7 @@ export default defineNuxtPlugin({
13
11
  const url = convexConfig?.url;
14
12
  if (!url) {
15
13
  if (import.meta.dev) {
16
- console.warn(
17
- "[convex-nuxt] No Convex URL configured. Set convex.url or NUXT_PUBLIC_CONVEX_URL."
18
- );
14
+ warnMissingConvexUrl("server");
19
15
  }
20
16
  return;
21
17
  }
@@ -1,26 +1,21 @@
1
1
  import { ConvexHttpClient } from "convex/browser";
2
2
  import { makeFunctionReference } from "convex/server";
3
- import {
4
- createError,
5
- defineEventHandler,
6
- readBody
7
- } from "h3";
3
+ import { createError, defineEventHandler, readBody } from "h3";
8
4
  import { useRuntimeConfig } from "nitropack/runtime";
9
- import {
10
- readHttpOnlyJwt,
11
- readHttpOnlyRefresh,
12
- setAuthCookies
13
- } from "../../../authCookies.js";
5
+ import { readHttpOnlyJwt, readHttpOnlyRefresh, setAuthCookies } from "../../../authCookies.js";
14
6
  import { isHttpOnlyAuth } from "../../../../utils/authStorage.js";
15
7
  import { assertSameOrigin } from "../../../sameOrigin.js";
16
- const authSignIn = makeFunctionReference("auth:signIn");
8
+ import { MISSING_URL_HINT } from "../../../../utils/errors.js";
9
+ const authSignIn = makeFunctionReference(
10
+ "auth:signIn"
11
+ );
17
12
  function convexUrl(event) {
18
13
  const config = useRuntimeConfig(event);
19
14
  const url = config.public?.convex?.url ?? process.env.NUXT_PUBLIC_CONVEX_URL;
20
15
  if (!url) {
21
16
  throw createError({
22
17
  statusCode: 500,
23
- message: "Convex URL is not configured"
18
+ message: MISSING_URL_HINT
24
19
  });
25
20
  }
26
21
  return url;
@@ -1,15 +1,7 @@
1
1
  import { createError, getCookie } from "h3";
2
- import { useRuntimeConfig } from "nitropack/runtime";
3
2
  import { resolveAuthCookieName } from "../utils/authStorage.js";
4
3
  import { resolveFetchToken } from "../utils/fetchToken.js";
5
- function readConvexConfig(event) {
6
- try {
7
- const config = useRuntimeConfig(event);
8
- return config.public?.convex;
9
- } catch {
10
- return void 0;
11
- }
12
- }
4
+ import { readConvexConfig } from "./convexConfig.js";
13
5
  export function getConvexToken(event) {
14
6
  const cookieName = resolveAuthCookieName(readConvexConfig(event)?.auth);
15
7
  if (!cookieName) {
@@ -1,10 +1,4 @@
1
- import {
2
- deleteCookie,
3
- getCookie,
4
- getRequestHeader,
5
- setCookie
6
- } from "h3";
7
- import { useRuntimeConfig } from "nitropack/runtime";
1
+ import { deleteCookie, getCookie, getRequestHeader, setCookie } from "h3";
8
2
  import {
9
3
  AUTH_JWT_COOKIE_MAX_AGE,
10
4
  isHttpOnlyAuth,
@@ -12,32 +6,18 @@ import {
12
6
  resolveAuthPresentCookieName,
13
7
  resolveAuthRefreshCookieName
14
8
  } from "../utils/authStorage.js";
9
+ import { readConvexConfig } from "./convexConfig.js";
15
10
  function readAuthConfig(event) {
16
- try {
17
- const config = useRuntimeConfig(event);
18
- return config.public?.convex?.auth;
19
- } catch {
20
- return void 0;
21
- }
11
+ return readConvexConfig(event)?.auth;
22
12
  }
23
13
  function isLocalHost(event) {
24
14
  const host = getRequestHeader(event, "host") ?? "";
25
15
  return host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]");
26
16
  }
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) {
17
+ function cookieOptions(event, httpOnly) {
38
18
  const local = isLocalHost(event);
39
19
  return {
40
- httpOnly: false,
20
+ httpOnly,
41
21
  secure: !local,
42
22
  sameSite: "lax",
43
23
  path: "/",
@@ -47,15 +27,13 @@ function readableCookieOptions(event) {
47
27
  export function setAuthCookies(event, tokens) {
48
28
  const auth = readAuthConfig(event);
49
29
  if (!isHttpOnlyAuth(auth)) {
50
- throw new Error(
51
- "[convex-nuxt] setAuthCookies requires convex.auth.httpOnly: true"
52
- );
30
+ throw new Error("[use-convex] setAuthCookies requires convex.auth.httpOnly: true");
53
31
  }
54
32
  const jwtName = resolveAuthCookieName(auth);
55
33
  const refreshName = resolveAuthRefreshCookieName(auth);
56
34
  const presentName = resolveAuthPresentCookieName(auth);
57
- const httpOpts = httpOnlyCookieOptions(event);
58
- const presentOpts = readableCookieOptions(event);
35
+ const httpOpts = cookieOptions(event, true);
36
+ const presentOpts = cookieOptions(event, false);
59
37
  if (!tokens.token) {
60
38
  deleteCookie(event, jwtName, httpOpts);
61
39
  deleteCookie(event, refreshName, httpOpts);
@@ -0,0 +1,12 @@
1
+ import type { H3Event } from 'h3';
2
+ export interface ConvexPublicConfig {
3
+ url?: string;
4
+ auth?: {
5
+ provider?: string;
6
+ cookie?: string;
7
+ httpOnly?: boolean;
8
+ presentCookie?: string;
9
+ };
10
+ }
11
+ /** Read `runtimeConfig.public.convex`, or `undefined` outside a Nitro request. */
12
+ export declare function readConvexConfig(event?: H3Event): ConvexPublicConfig | undefined;
@@ -0,0 +1,9 @@
1
+ import { useRuntimeConfig } from "nitropack/runtime";
2
+ export function readConvexConfig(event) {
3
+ try {
4
+ const config = useRuntimeConfig(event);
5
+ return config.public?.convex;
6
+ } catch {
7
+ return void 0;
8
+ }
9
+ }
@@ -1,7 +1,5 @@
1
1
  import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server';
2
2
  import type { H3Event } from 'h3';
3
- import { resolveFetchToken } from '../utils/fetchToken.js';
4
- export { resolveFetchToken };
5
3
  export interface ConvexFetchOptions {
6
4
  /**
7
5
  * Deployment URL. Defaults to `NUXT_PUBLIC_CONVEX_URL` / runtimeConfig.
@@ -18,9 +16,9 @@ export interface ConvexFetchOptions {
18
16
  event?: H3Event;
19
17
  skipConvexDeploymentUrlCheck?: boolean;
20
18
  }
21
- /** @internal Exported for unit tests. */
19
+ /** @internal */
22
20
  export declare function resolveUrl(options: ConvexFetchOptions): string;
23
- /** @internal Exported for unit tests. */
21
+ /** @internal */
24
22
  export declare function resolveToken(options: ConvexFetchOptions): string | undefined;
25
23
  /**
26
24
  * One-shot query via ConvexHttpClient. Use in Nitro routes / server middleware.
@@ -1,17 +1,9 @@
1
1
  import { getCookie } from "h3";
2
- import { useRuntimeConfig } from "nitropack/runtime";
3
2
  import { createHttpClient } from "../utils/http.js";
4
3
  import { resolveAuthCookieName } from "../utils/authStorage.js";
5
4
  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
- }
5
+ import { missingConvexUrlError } from "../utils/errors.js";
6
+ import { readConvexConfig } from "./convexConfig.js";
15
7
  export function resolveUrl(options) {
16
8
  if (options.url) {
17
9
  return options.url;
@@ -24,9 +16,7 @@ export function resolveUrl(options) {
24
16
  if (fromEnv) {
25
17
  return fromEnv;
26
18
  }
27
- throw new Error(
28
- "[convex-nuxt] No Convex URL. Pass { url } or set NUXT_PUBLIC_CONVEX_URL."
29
- );
19
+ throw missingConvexUrlError("fetchQuery/Mutation/Action");
30
20
  }
31
21
  export function resolveToken(options) {
32
22
  if (options.token !== void 0) {
@@ -1,6 +1,10 @@
1
1
  /**
2
- * Minimal DevTools iframe — shows public Convex runtime config.
3
- * Connection state is available in-app via `useConvexConnectionState()`.
2
+ * DevTools iframe — embeds Convex dashboard (cloud) + public config tips.
3
+ * Live auth/connection: `window.__CONVEX_NUXT__` (devtools client plugin).
4
+ *
5
+ * Auto-login uses CONVEX_DEPLOY_KEY via postMessage to dashboard-embedded.
6
+ * Without a key, the embed shows Convex's credential form; Open dashboard
7
+ * still deep-links to dashboard.convex.dev (existing session).
4
8
  */
5
9
  declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, string>;
6
10
  export default _default;