nuxt-convex 0.0.1 → 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.
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-convex",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "configKey": "convex",
5
5
  "compatibility": {
6
6
  "nuxt": ">=3.0.0"
package/dist/module.mjs CHANGED
@@ -6,7 +6,7 @@ import { consola } from 'consola';
6
6
  import { defu } from 'defu';
7
7
  import { join } from 'pathe';
8
8
 
9
- const version = "0.0.1";
9
+ const version = "0.0.2";
10
10
 
11
11
  const log = consola.withTag("nuxt:convex");
12
12
  const module$1 = defineNuxtModule({
@@ -33,9 +33,11 @@ const module$1 = defineNuxtModule({
33
33
  { url: config.url }
34
34
  );
35
35
  addPlugin({ src: resolve("./runtime/plugin.client"), mode: "client" });
36
- addImports({ name: "useConvexUpload", from: resolve("./runtime/composables/useConvexUpload") });
36
+ addImports([
37
+ { name: "useConvexUpload", from: resolve("./runtime/composables/useConvexUpload") }
38
+ ]);
37
39
  setupConvexApiAlias(nuxt);
38
- setupConvexClient(nuxt);
40
+ setupConvexClient(nuxt, resolve);
39
41
  if (config.storage) {
40
42
  await setupConvexStorage(nuxt, resolve);
41
43
  }
@@ -55,13 +57,14 @@ function resolveConfig(options) {
55
57
  const url = options.url || process.env.CONVEX_URL || process.env.NUXT_PUBLIC_CONVEX_URL || "";
56
58
  return defu(options, { url, storage: false });
57
59
  }
58
- function setupConvexClient(nuxt, _resolve) {
60
+ function setupConvexClient(nuxt, resolve) {
59
61
  const template = addTemplate({
60
62
  filename: "convex/client.mjs",
61
63
  getContents: () => `// Auto-generated by nuxt-convex
62
64
  import { inject } from 'vue'
63
65
  import { CONVEX_INJECTION_KEY } from '@convex-vue/core'
64
- export { useConvexQuery, useConvexMutation, useConvexAction } from '@convex-vue/core'
66
+ export { useConvexMutation, useConvexAction } from '@convex-vue/core'
67
+ export { useConvexQuery } from '${resolve("./runtime/composables/useConvexQuery")}'
65
68
 
66
69
  // Get the Convex client - MUST be called during component setup
67
70
  export function useConvex() {
@@ -76,8 +79,23 @@ export function useConvex() {
76
79
  filename: "types/convex.d.ts",
77
80
  getContents: () => `// Auto-generated by nuxt-convex
78
81
  import type { ConvexClient } from 'convex/browser'
82
+ import type { FunctionReference, FunctionArgs, FunctionReturnType } from 'convex/server'
83
+ import type { AsyncData, AsyncDataOptions } from 'nuxt/app'
84
+ import type { MaybeRefOrGetter } from 'vue'
85
+
79
86
  declare module '#convex' {
80
- export { useConvexQuery, useConvexMutation, useConvexAction } from '@convex-vue/core'
87
+ export { useConvexMutation, useConvexAction } from '@convex-vue/core'
88
+
89
+ export interface UseConvexQueryOptions<T> extends Omit<AsyncDataOptions<T>, 'transform' | 'default'> {
90
+ ssr?: boolean
91
+ }
92
+
93
+ export function useConvexQuery<Query extends FunctionReference<'query'>>(
94
+ query: Query,
95
+ args: MaybeRefOrGetter<FunctionArgs<Query>>,
96
+ options?: UseConvexQueryOptions<FunctionReturnType<Query>>
97
+ ): Promise<AsyncData<FunctionReturnType<Query> | null, Error | null>>
98
+
81
99
  export function useConvex(): ConvexClient
82
100
  }
83
101
  `
@@ -0,0 +1,30 @@
1
+ import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server';
2
+ import type { AsyncData, AsyncDataOptions } from 'nuxt/app';
3
+ import type { MaybeRefOrGetter } from 'vue';
4
+ type QueryReference = FunctionReference<'query'>;
5
+ export interface UseConvexQueryOptions<T> extends Omit<AsyncDataOptions<T>, 'transform' | 'default'> {
6
+ /**
7
+ * Enable server-side data fetching.
8
+ * @default true
9
+ */
10
+ ssr?: boolean;
11
+ }
12
+ /**
13
+ * Convex query composable with SSR support.
14
+ *
15
+ * By default, fetches data on the server (SSR) and subscribes to real-time updates on the client.
16
+ * Set `ssr: false` to disable server-side fetching and use client-only mode.
17
+ *
18
+ * @example
19
+ * ```vue
20
+ * <script setup>
21
+ * // SSR + real-time (default)
22
+ * const { data, pending, error } = await useConvexQuery(api.tasks.list, {})
23
+ *
24
+ * // Client-only mode
25
+ * const { data } = await useConvexQuery(api.tasks.list, {}, { ssr: false })
26
+ * </script>
27
+ * ```
28
+ */
29
+ export declare function useConvexQuery<Query extends QueryReference>(query: Query, args: MaybeRefOrGetter<FunctionArgs<Query>>, options?: UseConvexQueryOptions<FunctionReturnType<Query>>): Promise<AsyncData<FunctionReturnType<Query> | null, Error | null>>;
30
+ export {};
@@ -0,0 +1,119 @@
1
+ import { useAsyncData, useNuxtApp, useRuntimeConfig } from "#imports";
2
+ import { CONVEX_INJECTION_KEY } from "@convex-vue/core";
3
+ import { inject, onScopeDispose, ref, toValue, watch } from "vue";
4
+ export async function useConvexQuery(query, args, options = {}) {
5
+ const nuxtApp = useNuxtApp();
6
+ const config = useRuntimeConfig();
7
+ const convexUrl = config.public.convex?.url;
8
+ const client = import.meta.client ? inject(CONVEX_INJECTION_KEY) : null;
9
+ if (!convexUrl) {
10
+ console.warn("[nuxt-convex] No Convex URL configured");
11
+ return createErrorAsyncData(new Error("Convex URL not configured"));
12
+ }
13
+ const ssrEnabled = options.ssr ?? true;
14
+ const shouldUseSSR = ssrEnabled && (import.meta.server || nuxtApp.isHydrating);
15
+ if (shouldUseSSR) {
16
+ return useConvexQuerySSR(query, args, options, convexUrl, client);
17
+ }
18
+ return useConvexQueryClient(query, args, client);
19
+ }
20
+ async function useConvexQuerySSR(query, args, options, convexUrl, client) {
21
+ const queryName = query._name ?? "unknown";
22
+ const sortedArgs = sortObjectKeys(toValue(args));
23
+ const key = `convex:${queryName}:${JSON.stringify(sortedArgs)}`;
24
+ const asyncData = await useAsyncData(
25
+ key,
26
+ async () => {
27
+ const { fetchQuery } = await import("convex/nextjs");
28
+ return fetchQuery(query, toValue(args), { url: convexUrl });
29
+ },
30
+ { ...options, server: options.ssr ?? true }
31
+ );
32
+ if (import.meta.client) {
33
+ setupClientSubscription(query, args, {
34
+ data: asyncData.data,
35
+ error: asyncData.error,
36
+ pending: asyncData.pending,
37
+ status: asyncData.status
38
+ }, client);
39
+ }
40
+ return asyncData;
41
+ }
42
+ function useConvexQueryClient(query, args, client) {
43
+ const data = ref(null);
44
+ const error = ref(null);
45
+ const pending = ref(true);
46
+ const status = ref("pending");
47
+ const state = { data, error, pending, status };
48
+ const { refresh } = setupClientSubscription(query, args, state, client);
49
+ return {
50
+ data,
51
+ pending,
52
+ error,
53
+ status,
54
+ refresh,
55
+ execute: refresh,
56
+ clear: () => {
57
+ data.value = null;
58
+ error.value = null;
59
+ pending.value = false;
60
+ status.value = "idle";
61
+ }
62
+ };
63
+ }
64
+ function setupClientSubscription(query, args, state, client) {
65
+ if (!client) {
66
+ console.warn("[nuxt-convex] Convex client not found. Real-time updates disabled.");
67
+ state.pending.value = false;
68
+ state.status.value = "error";
69
+ state.error.value = new Error("Convex client not found");
70
+ return { refresh: async () => {
71
+ } };
72
+ }
73
+ let unsubscribe = null;
74
+ const subscribe = () => {
75
+ unsubscribe?.();
76
+ const currentArgs = toValue(args);
77
+ unsubscribe = client.onUpdate(query, currentArgs, (result) => {
78
+ if (result !== void 0) {
79
+ state.data.value = result;
80
+ state.pending.value = false;
81
+ state.status.value = "success";
82
+ }
83
+ });
84
+ };
85
+ subscribe();
86
+ watch(() => toValue(args), subscribe, { deep: true });
87
+ onScopeDispose(() => unsubscribe?.());
88
+ return {
89
+ refresh: async () => {
90
+ state.pending.value = true;
91
+ subscribe();
92
+ }
93
+ };
94
+ }
95
+ function createErrorAsyncData(err) {
96
+ return {
97
+ data: ref(null),
98
+ pending: ref(false),
99
+ error: ref(err),
100
+ status: ref("error"),
101
+ refresh: async () => {
102
+ },
103
+ execute: async () => {
104
+ },
105
+ clear: () => {
106
+ }
107
+ };
108
+ }
109
+ function sortObjectKeys(obj) {
110
+ if (obj === null || typeof obj !== "object")
111
+ return obj;
112
+ if (Array.isArray(obj))
113
+ return obj.map(sortObjectKeys);
114
+ const sorted = {};
115
+ for (const key of Object.keys(obj).sort()) {
116
+ sorted[key] = sortObjectKeys(obj[key]);
117
+ }
118
+ return sorted;
119
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-convex",
3
3
  "type": "module",
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "packageManager": "pnpm@10.15.1",
6
6
  "description": "Nuxt module for Convex - reactive backend with real-time sync, file storage, and auto-imports",
7
7
  "author": "onmax",
@@ -72,6 +72,7 @@
72
72
  "@nuxt/module-builder": "catalog:build",
73
73
  "@nuxt/test-utils": "catalog:test",
74
74
  "@types/node": "catalog:types",
75
+ "@vue/test-utils": "catalog:test",
75
76
  "bumpp": "catalog:",
76
77
  "convex": "catalog:convex",
77
78
  "eslint": "catalog:lint",