better-convex-nuxt 0.1.8 → 0.2.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.
- package/README.md +13 -2
- package/dist/module.d.mts +70 -10
- package/dist/module.json +1 -1
- package/dist/module.mjs +73 -14
- package/dist/runtime/components/ConvexAuthError.d.vue.ts +19 -0
- package/dist/runtime/components/ConvexAuthError.vue +18 -0
- package/dist/runtime/components/ConvexAuthError.vue.d.ts +19 -0
- package/dist/runtime/composables/index.d.ts +6 -5
- package/dist/runtime/composables/index.js +6 -2
- package/dist/runtime/composables/useConvexAction.d.ts +1 -12
- package/dist/runtime/composables/useConvexAction.js +80 -13
- package/dist/runtime/composables/useConvexAuth.d.ts +1 -0
- package/dist/runtime/composables/useConvexAuth.js +1 -13
- package/dist/runtime/composables/useConvexConnectionState.d.ts +1 -12
- package/dist/runtime/composables/useConvexConnectionState.js +25 -15
- package/dist/runtime/composables/useConvexFileUpload.d.ts +191 -0
- package/dist/runtime/composables/useConvexFileUpload.js +175 -0
- package/dist/runtime/composables/useConvexMutation.d.ts +0 -6
- package/dist/runtime/composables/useConvexMutation.js +84 -22
- package/dist/runtime/composables/useConvexPaginatedQuery.d.ts +2 -7
- package/dist/runtime/composables/useConvexPaginatedQuery.js +60 -59
- package/dist/runtime/composables/useConvexQuery.d.ts +14 -105
- package/dist/runtime/composables/useConvexQuery.js +226 -267
- package/dist/runtime/composables/useConvexStorageUrl.d.ts +82 -0
- package/dist/runtime/composables/useConvexStorageUrl.js +12 -0
- package/dist/runtime/composables/usePermissions.d.ts +2 -2
- package/dist/runtime/composables/usePermissions.js +10 -2
- package/dist/runtime/devtools/event-buffer.d.ts +29 -0
- package/dist/runtime/devtools/event-buffer.js +30 -0
- package/dist/runtime/devtools/index.d.ts +14 -0
- package/dist/runtime/devtools/index.js +20 -0
- package/dist/runtime/devtools/mutation-registry.d.ts +39 -0
- package/dist/runtime/devtools/mutation-registry.js +59 -0
- package/dist/runtime/devtools/query-registry.d.ts +77 -0
- package/dist/runtime/devtools/query-registry.js +55 -0
- package/dist/runtime/devtools/server.d.ts +2 -0
- package/dist/runtime/devtools/server.js +1205 -0
- package/dist/runtime/devtools/types.d.ts +107 -0
- package/dist/runtime/devtools/types.js +1 -0
- package/dist/runtime/plugin.client.d.ts +0 -13
- package/dist/runtime/plugin.client.js +187 -52
- package/dist/runtime/plugin.server.d.ts +1 -0
- package/dist/runtime/plugin.server.js +113 -50
- package/dist/runtime/server/api/auth/[...].d.ts +1 -1
- package/dist/runtime/server/api/auth/[...].js +45 -66
- package/dist/runtime/server/utils/auth-cache.d.ts +38 -0
- package/dist/runtime/server/utils/auth-cache.js +21 -0
- package/dist/runtime/server/utils/convex.d.ts +0 -6
- package/dist/runtime/server/utils/convex.js +63 -77
- package/dist/runtime/types.d.ts +0 -17
- package/dist/runtime/utils/convex-cache.d.ts +82 -27
- package/dist/runtime/utils/convex-cache.js +57 -41
- package/dist/runtime/utils/convex-shared.d.ts +48 -0
- package/dist/runtime/utils/convex-shared.js +42 -0
- package/dist/runtime/utils/logger.d.ts +101 -100
- package/dist/runtime/utils/logger.js +240 -39
- package/dist/runtime/utils/mime-type.d.ts +33 -0
- package/dist/runtime/utils/mime-type.js +10 -0
- package/dist/runtime/utils/shared-helpers.d.ts +116 -0
- package/dist/runtime/utils/shared-helpers.js +134 -0
- package/dist/runtime/utils/types.d.ts +44 -0
- package/dist/runtime/utils/types.js +1 -0
- package/dist/types.d.mts +1 -1
- package/package.json +6 -3
- package/dist/runtime/composables/useConvexCached.d.ts +0 -32
- package/dist/runtime/composables/useConvexCached.js +0 -7
- package/dist/runtime/composables/useConvexData.d.ts +0 -37
- package/dist/runtime/composables/useConvexData.js +0 -6
- package/dist/runtime/composables/useLazyConvexQuery.d.ts +0 -26
- package/dist/runtime/composables/useLazyConvexQuery.js +0 -7
- package/dist/runtime/utils/query-helpers.d.ts +0 -118
- package/dist/runtime/utils/query-helpers.js +0 -67
package/README.md
CHANGED
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
Full-featured [Convex](https://convex.dev) integration for [Nuxt](https://nuxt.com) with SSR, real-time subscriptions, authentication, and permissions.
|
|
9
9
|
|
|
10
10
|
- [Documentation](https://better-convex-nuxt.vercel.app)
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
> [!NOTE]
|
|
13
|
+
> **Work In Progress**
|
|
14
|
+
> This module is rapidly evolving. While we are using it in our own apps, features and APIs are subject to change as we refine the best patterns for Nuxt + Convex.
|
|
15
|
+
|
|
16
|
+
Contributions and PRs to help improve the library, playground or docs are highly appreciated! 🙏
|
|
12
17
|
|
|
13
18
|
## Features
|
|
14
19
|
|
|
@@ -24,7 +29,7 @@ Full-featured [Convex](https://convex.dev) integration for [Nuxt](https://nuxt.c
|
|
|
24
29
|
Install the module:
|
|
25
30
|
|
|
26
31
|
```bash
|
|
27
|
-
|
|
32
|
+
pnpm add better-convex-nuxt
|
|
28
33
|
```
|
|
29
34
|
|
|
30
35
|
Add your Convex URL to `.env`:
|
|
@@ -113,6 +118,8 @@ async function handleLogin() {
|
|
|
113
118
|
| `useConvexMutation` | Execute mutations with optimistic updates |
|
|
114
119
|
| `useConvexAction` | Execute Convex actions |
|
|
115
120
|
| `useConvexPaginatedQuery` | Paginated queries with `loadMore()` |
|
|
121
|
+
| `useConvexFileUpload` | Upload files to Convex storage with progress |
|
|
122
|
+
| `useConvexStorageUrl` | Get reactive URLs for stored files |
|
|
116
123
|
| `useConvexAuth` | Authentication state (user, token, isAuthenticated) |
|
|
117
124
|
| `useConvexConnectionState` | WebSocket connection status |
|
|
118
125
|
| `useConvexCached` | Read cached query data |
|
|
@@ -155,6 +162,10 @@ pnpm test
|
|
|
155
162
|
pnpm lint
|
|
156
163
|
```
|
|
157
164
|
|
|
165
|
+
## Acknowledgements
|
|
166
|
+
|
|
167
|
+
- File upload composables inspired by [nuxt-convex](https://github.com/onmax/nuxt-convex) by [@onmax](https://github.com/onmax)
|
|
168
|
+
|
|
158
169
|
## License
|
|
159
170
|
|
|
160
171
|
[MIT](./LICENSE)
|
package/dist/module.d.mts
CHANGED
|
@@ -1,16 +1,50 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
|
|
3
|
-
interface
|
|
4
|
-
/**
|
|
5
|
-
|
|
3
|
+
interface LoggingOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Enable module logging.
|
|
6
|
+
* - false: No logs (production default)
|
|
7
|
+
* - true: Info-level logs (canonical events only)
|
|
8
|
+
* - 'debug': Include debug-level details
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
enabled?: boolean | 'debug';
|
|
12
|
+
/**
|
|
13
|
+
* Output format for logs.
|
|
14
|
+
* - 'pretty': Human-readable with icons (default)
|
|
15
|
+
* - 'json': Structured JSON for log aggregation
|
|
16
|
+
* @default 'pretty'
|
|
17
|
+
*/
|
|
18
|
+
format?: 'pretty' | 'json';
|
|
19
|
+
}
|
|
20
|
+
interface AuthCacheOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Enable SSR auth token caching.
|
|
23
|
+
* When enabled, Convex JWT tokens are cached to reduce TTFB on subsequent SSR requests.
|
|
24
|
+
* Uses Nitro Storage (memory by default, configurable to Redis for multi-instance deployments).
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Cache TTL in seconds.
|
|
30
|
+
* Determines how long tokens are cached before requiring a fresh fetch.
|
|
31
|
+
* Shorter TTL = more security, longer TTL = better performance.
|
|
32
|
+
* @default 900 (15 minutes)
|
|
33
|
+
*/
|
|
34
|
+
ttl?: number;
|
|
6
35
|
}
|
|
7
36
|
interface ModuleOptions {
|
|
8
37
|
/** Convex deployment URL (WebSocket) - e.g., https://your-app.convex.cloud */
|
|
9
38
|
url?: string;
|
|
10
39
|
/** Convex site URL (HTTP/Auth) - e.g., https://your-app.convex.site. Auto-derived from url if not set. */
|
|
11
40
|
siteUrl?: string;
|
|
12
|
-
/**
|
|
13
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Additional trusted origins for CORS validation on the auth proxy.
|
|
43
|
+
* By default, only requests from the origin matching siteUrl are allowed.
|
|
44
|
+
* Supports wildcards for preview deployments (e.g., 'https://preview-*.vercel.app').
|
|
45
|
+
* @default []
|
|
46
|
+
*/
|
|
47
|
+
trustedOrigins?: string[];
|
|
14
48
|
/**
|
|
15
49
|
* Enable permission composables (createPermissions factory).
|
|
16
50
|
* When true, auto-imports createPermissions for building usePermissions.
|
|
@@ -18,13 +52,39 @@ interface ModuleOptions {
|
|
|
18
52
|
*/
|
|
19
53
|
permissions?: boolean;
|
|
20
54
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
|
|
55
|
+
* Configure module logging behavior.
|
|
56
|
+
* Emits canonical log events for debugging SSR, auth, queries, and mutations.
|
|
57
|
+
*/
|
|
58
|
+
logging?: LoggingOptions;
|
|
59
|
+
/**
|
|
60
|
+
* SSR auth token caching configuration (opt-in).
|
|
61
|
+
* Caches Convex JWT tokens server-side to reduce TTFB on subsequent requests.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* // nuxt.config.ts
|
|
66
|
+
* export default defineNuxtConfig({
|
|
67
|
+
* convex: {
|
|
68
|
+
* authCache: {
|
|
69
|
+
* enabled: true,
|
|
70
|
+
* ttl: 900 // 15 minutes
|
|
71
|
+
* }
|
|
72
|
+
* },
|
|
73
|
+
* // For multi-instance deployments, configure Redis:
|
|
74
|
+
* nitro: {
|
|
75
|
+
* storage: {
|
|
76
|
+
* 'cache:convex:auth': {
|
|
77
|
+
* driver: 'redis',
|
|
78
|
+
* url: process.env.REDIS_URL
|
|
79
|
+
* }
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
* })
|
|
83
|
+
* ```
|
|
24
84
|
*/
|
|
25
|
-
|
|
85
|
+
authCache?: AuthCacheOptions;
|
|
26
86
|
}
|
|
27
87
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
28
88
|
|
|
29
89
|
export { _default as default };
|
|
30
|
-
export type {
|
|
90
|
+
export type { AuthCacheOptions, LoggingOptions, ModuleOptions };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, addPlugin, addServerHandler, addTemplate, addImports, addServerImports, addComponentsDir } from '@nuxt/kit';
|
|
1
|
+
import { useLogger, defineNuxtModule, createResolver, addPlugin, addServerHandler, addTemplate, addImports, addServerImports, addComponentsDir } from '@nuxt/kit';
|
|
2
2
|
import { defu } from 'defu';
|
|
3
3
|
|
|
4
|
+
const logger = useLogger("better-convex-nuxt");
|
|
5
|
+
function isValidUrl(url) {
|
|
6
|
+
try {
|
|
7
|
+
new URL(url);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
4
13
|
const module$1 = defineNuxtModule({
|
|
5
14
|
meta: {
|
|
6
15
|
name: "better-convex-nuxt",
|
|
@@ -12,21 +21,40 @@ const module$1 = defineNuxtModule({
|
|
|
12
21
|
defaults: {
|
|
13
22
|
url: process.env.CONVEX_URL,
|
|
14
23
|
siteUrl: process.env.CONVEX_SITE_URL,
|
|
24
|
+
trustedOrigins: [],
|
|
15
25
|
permissions: false,
|
|
16
|
-
|
|
26
|
+
logging: {
|
|
27
|
+
enabled: false,
|
|
28
|
+
format: "pretty"
|
|
29
|
+
},
|
|
30
|
+
authCache: {
|
|
31
|
+
enabled: false,
|
|
32
|
+
ttl: 900
|
|
33
|
+
// 15 minutes
|
|
34
|
+
}
|
|
17
35
|
},
|
|
18
36
|
setup(options, nuxt) {
|
|
19
37
|
const resolver = createResolver(import.meta.url);
|
|
20
|
-
|
|
38
|
+
if (options.url && !isValidUrl(options.url)) {
|
|
39
|
+
logger.warn(`Invalid Convex URL format: "${options.url}". Expected a valid URL like "https://your-app.convex.cloud"`);
|
|
40
|
+
}
|
|
41
|
+
if (options.siteUrl && !isValidUrl(options.siteUrl)) {
|
|
42
|
+
logger.warn(`Invalid Convex site URL format: "${options.siteUrl}". Expected a valid URL like "https://your-app.convex.site"`);
|
|
43
|
+
}
|
|
44
|
+
const derivedSiteUrl = options.siteUrl || (options.url?.replace(".convex.cloud", ".convex.site") ?? "");
|
|
21
45
|
const convexConfig = defu(
|
|
22
46
|
nuxt.options.runtimeConfig.public.convex,
|
|
23
47
|
{
|
|
24
48
|
url: options.url || "",
|
|
25
49
|
siteUrl: derivedSiteUrl,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
trustedOrigins: options.trustedOrigins ?? [],
|
|
51
|
+
logging: {
|
|
52
|
+
enabled: options.logging?.enabled ?? false,
|
|
53
|
+
format: options.logging?.format ?? "pretty"
|
|
54
|
+
},
|
|
55
|
+
authCache: {
|
|
56
|
+
enabled: options.authCache?.enabled ?? false,
|
|
57
|
+
ttl: options.authCache?.ttl ?? 900
|
|
30
58
|
}
|
|
31
59
|
}
|
|
32
60
|
);
|
|
@@ -75,12 +103,7 @@ export {}
|
|
|
75
103
|
{ name: "useConvexAction", from: resolver.resolve("./runtime/composables/useConvexAction") },
|
|
76
104
|
{ name: "useAuthClient", from: resolver.resolve("./runtime/composables/useAuthClient") },
|
|
77
105
|
{ name: "useConvexQuery", from: resolver.resolve("./runtime/composables/useConvexQuery") },
|
|
78
|
-
{ name: "
|
|
79
|
-
{ name: "useConvexData", from: resolver.resolve("./runtime/composables/useConvexData") },
|
|
80
|
-
{
|
|
81
|
-
name: "useLazyConvexQuery",
|
|
82
|
-
from: resolver.resolve("./runtime/composables/useLazyConvexQuery")
|
|
83
|
-
},
|
|
106
|
+
{ name: "getQueryKey", from: resolver.resolve("./runtime/composables/useConvexQuery") },
|
|
84
107
|
{
|
|
85
108
|
name: "useConvexPaginatedQuery",
|
|
86
109
|
from: resolver.resolve("./runtime/composables/useConvexPaginatedQuery")
|
|
@@ -120,6 +143,15 @@ export {}
|
|
|
120
143
|
{
|
|
121
144
|
name: "deleteFromPaginatedQuery",
|
|
122
145
|
from: resolver.resolve("./runtime/composables/useConvexPaginatedQuery")
|
|
146
|
+
},
|
|
147
|
+
// File upload composables
|
|
148
|
+
{
|
|
149
|
+
name: "useConvexFileUpload",
|
|
150
|
+
from: resolver.resolve("./runtime/composables/useConvexFileUpload")
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: "useConvexStorageUrl",
|
|
154
|
+
from: resolver.resolve("./runtime/composables/useConvexStorageUrl")
|
|
123
155
|
}
|
|
124
156
|
]);
|
|
125
157
|
if (options.permissions) {
|
|
@@ -133,7 +165,8 @@ export {}
|
|
|
133
165
|
addServerImports([
|
|
134
166
|
{ name: "fetchQuery", from: resolver.resolve("./runtime/server/utils/convex") },
|
|
135
167
|
{ name: "fetchMutation", from: resolver.resolve("./runtime/server/utils/convex") },
|
|
136
|
-
{ name: "fetchAction", from: resolver.resolve("./runtime/server/utils/convex") }
|
|
168
|
+
{ name: "fetchAction", from: resolver.resolve("./runtime/server/utils/convex") },
|
|
169
|
+
{ name: "clearAuthCache", from: resolver.resolve("./runtime/server/utils/auth-cache") }
|
|
137
170
|
]);
|
|
138
171
|
addComponentsDir({
|
|
139
172
|
path: resolver.resolve("./runtime/components"),
|
|
@@ -144,7 +177,33 @@ export {}
|
|
|
144
177
|
path: resolver.resolve(nuxt.options.buildDir, "types/better-convex-nuxt.d.ts")
|
|
145
178
|
});
|
|
146
179
|
});
|
|
180
|
+
if (nuxt.options.dev) {
|
|
181
|
+
setupDevTools(nuxt, resolver);
|
|
182
|
+
}
|
|
147
183
|
}
|
|
148
184
|
});
|
|
185
|
+
async function setupDevTools(nuxt, resolver) {
|
|
186
|
+
try {
|
|
187
|
+
const { addCustomTab } = await import('@nuxt/devtools-kit');
|
|
188
|
+
addCustomTab({
|
|
189
|
+
name: "convex",
|
|
190
|
+
title: "Convex",
|
|
191
|
+
icon: "carbon:data-base",
|
|
192
|
+
category: "app",
|
|
193
|
+
view: {
|
|
194
|
+
type: "iframe",
|
|
195
|
+
src: "/__convex_devtools__",
|
|
196
|
+
persistent: true
|
|
197
|
+
}
|
|
198
|
+
}, nuxt);
|
|
199
|
+
addServerHandler({
|
|
200
|
+
route: "/__convex_devtools__",
|
|
201
|
+
handler: resolver.resolve("./runtime/devtools/server")
|
|
202
|
+
});
|
|
203
|
+
logger.info("Nuxt DevTools integration enabled");
|
|
204
|
+
} catch {
|
|
205
|
+
logger.debug("Nuxt DevTools integration skipped (devtools-kit not available)");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
149
208
|
|
|
150
209
|
export { module$1 as default };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry authentication by reloading the page
|
|
3
|
+
*/
|
|
4
|
+
declare function retry(): void;
|
|
5
|
+
declare var __VLS_1: {
|
|
6
|
+
retry: typeof retry;
|
|
7
|
+
};
|
|
8
|
+
type __VLS_Slots = {} & {
|
|
9
|
+
default?: (props: typeof __VLS_1) => any;
|
|
10
|
+
};
|
|
11
|
+
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>;
|
|
12
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
13
|
+
declare const _default: typeof __VLS_export;
|
|
14
|
+
export default _default;
|
|
15
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
16
|
+
new (): {
|
|
17
|
+
$slots: S;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useConvexAuth } from "../composables/useConvexAuth";
|
|
4
|
+
const { isAuthenticated, isPending, token, user } = useConvexAuth();
|
|
5
|
+
const hasError = computed(() => {
|
|
6
|
+
if (isPending.value) return false;
|
|
7
|
+
if (isAuthenticated.value) return false;
|
|
8
|
+
if (token.value && !user.value) return true;
|
|
9
|
+
return false;
|
|
10
|
+
});
|
|
11
|
+
function retry() {
|
|
12
|
+
window.location.reload();
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<slot v-if="hasError" :retry="retry" />
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry authentication by reloading the page
|
|
3
|
+
*/
|
|
4
|
+
declare function retry(): void;
|
|
5
|
+
declare var __VLS_1: {
|
|
6
|
+
retry: typeof retry;
|
|
7
|
+
};
|
|
8
|
+
type __VLS_Slots = {} & {
|
|
9
|
+
default?: (props: typeof __VLS_1) => any;
|
|
10
|
+
};
|
|
11
|
+
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>;
|
|
12
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
13
|
+
declare const _default: typeof __VLS_export;
|
|
14
|
+
export default _default;
|
|
15
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
16
|
+
new (): {
|
|
17
|
+
$slots: S;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export { useConvexAuth } from './useConvexAuth.js';
|
|
1
|
+
export { useConvexAuth, type ConvexUser } from './useConvexAuth.js';
|
|
2
2
|
export { useConvex } from './useConvex.js';
|
|
3
|
-
export { useConvexConnectionState, type ConnectionState,
|
|
3
|
+
export { useConvexConnectionState, type ConnectionState, } from './useConvexConnectionState.js';
|
|
4
4
|
export { useConvexMutation, type MutationStatus, type UseConvexMutationReturn, type UseConvexMutationOptions, updateQuery, setQueryData, updateAllQueries, deleteFromQuery, type UpdateQueryOptions, type SetQueryDataOptions, type UpdateAllQueriesOptions, type DeleteFromQueryOptions, } from './useConvexMutation.js';
|
|
5
5
|
export type { OptimisticLocalStore } from 'convex/browser';
|
|
6
|
-
export { useConvexAction, type ActionStatus, type UseConvexActionReturn,
|
|
6
|
+
export { useConvexAction, type ActionStatus, type UseConvexActionReturn, } from './useConvexAction.js';
|
|
7
7
|
export { useAuthClient } from './useAuthClient.js';
|
|
8
|
-
export { useConvexQuery,
|
|
9
|
-
export { useConvexCached } from './useConvexCached.js';
|
|
8
|
+
export { useConvexQuery, getQueryKey, type QueryStatus, type UseConvexQueryOptions, } from './useConvexQuery.js';
|
|
10
9
|
export { useConvexPaginatedQuery, type PaginationStatus, type UseConvexPaginatedQueryOptions, type UseConvexPaginatedQueryReturn, type PaginatedQueryReference, type PaginatedQueryArgs, type PaginatedQueryItem, insertAtTop, insertAtPosition, insertAtBottomIfLoaded, optimisticallyUpdateValueInPaginatedQuery, deleteFromPaginatedQuery, type InsertAtTopOptions, type InsertAtPositionOptions, type InsertAtBottomIfLoadedOptions, type UpdateInPaginatedQueryOptions, type DeleteFromPaginatedQueryOptions, } from './useConvexPaginatedQuery.js';
|
|
10
|
+
export { useConvexFileUpload, type UploadStatus, type UseConvexFileUploadReturn, type UseConvexFileUploadOptions, } from './useConvexFileUpload.js';
|
|
11
|
+
export { useConvexStorageUrl } from './useConvexStorageUrl.js';
|
|
11
12
|
export { createPermissions, type PermissionContext, type Resource, type CheckPermissionFn, type CreatePermissionsOptions, type UsePermissionsReturn, type UsePermissionGuardOptions, } from './usePermissions.js';
|
|
@@ -15,9 +15,9 @@ export {
|
|
|
15
15
|
} from "./useConvexAction.js";
|
|
16
16
|
export { useAuthClient } from "./useAuthClient.js";
|
|
17
17
|
export {
|
|
18
|
-
useConvexQuery
|
|
18
|
+
useConvexQuery,
|
|
19
|
+
getQueryKey
|
|
19
20
|
} from "./useConvexQuery.js";
|
|
20
|
-
export { useConvexCached } from "./useConvexCached.js";
|
|
21
21
|
export {
|
|
22
22
|
useConvexPaginatedQuery,
|
|
23
23
|
insertAtTop,
|
|
@@ -26,6 +26,10 @@ export {
|
|
|
26
26
|
optimisticallyUpdateValueInPaginatedQuery,
|
|
27
27
|
deleteFromPaginatedQuery
|
|
28
28
|
} from "./useConvexPaginatedQuery.js";
|
|
29
|
+
export {
|
|
30
|
+
useConvexFileUpload
|
|
31
|
+
} from "./useConvexFileUpload.js";
|
|
32
|
+
export { useConvexStorageUrl } from "./useConvexStorageUrl.js";
|
|
29
33
|
export {
|
|
30
34
|
createPermissions
|
|
31
35
|
} from "./usePermissions.js";
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server';
|
|
2
2
|
import { type Ref, type ComputedRef } from 'vue';
|
|
3
|
-
/**
|
|
4
|
-
* Options for useConvexAction
|
|
5
|
-
*/
|
|
6
|
-
export interface UseConvexActionOptions {
|
|
7
|
-
/**
|
|
8
|
-
* Enable verbose logging for debugging.
|
|
9
|
-
* Logs action lifecycle events: start, success, error.
|
|
10
|
-
* @default false
|
|
11
|
-
*/
|
|
12
|
-
verbose?: boolean;
|
|
13
|
-
}
|
|
14
3
|
/**
|
|
15
4
|
* Action status representing the current state of the action
|
|
16
5
|
* - 'idle': not yet called or reset
|
|
@@ -102,4 +91,4 @@ export interface UseConvexActionReturn<Args, Result> {
|
|
|
102
91
|
* </template>
|
|
103
92
|
* ```
|
|
104
93
|
*/
|
|
105
|
-
export declare function useConvexAction<Action extends FunctionReference<'action'>>(action: Action
|
|
94
|
+
export declare function useConvexAction<Action extends FunctionReference<'action'>>(action: Action): UseConvexActionReturn<FunctionArgs<Action>, FunctionReturnType<Action>>;
|
|
@@ -1,47 +1,114 @@
|
|
|
1
1
|
import { ref, computed } from "vue";
|
|
2
|
-
import {
|
|
2
|
+
import { useRuntimeConfig } from "#imports";
|
|
3
|
+
import { getFunctionName } from "../utils/convex-cache.js";
|
|
4
|
+
import { createModuleLogger, getLoggingOptions, createTimer, formatArgsPreview } from "../utils/logger.js";
|
|
3
5
|
import { useConvex } from "./useConvex.js";
|
|
4
|
-
|
|
6
|
+
let devToolsMutationRegistryPromise = null;
|
|
7
|
+
let devToolsMutationRegistry = null;
|
|
8
|
+
if (import.meta.client && import.meta.dev) {
|
|
9
|
+
devToolsMutationRegistryPromise = import("../devtools/mutation-registry.js");
|
|
10
|
+
devToolsMutationRegistryPromise.then((module) => {
|
|
11
|
+
devToolsMutationRegistry = module;
|
|
12
|
+
}).catch(() => {
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function useConvexAction(action) {
|
|
5
16
|
const config = useRuntimeConfig();
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
17
|
+
const loggingOptions = getLoggingOptions(config.public.convex ?? {});
|
|
18
|
+
const logger = createModuleLogger(loggingOptions);
|
|
19
|
+
const fnName = getFunctionName(action);
|
|
9
20
|
const _status = ref("idle");
|
|
10
21
|
const error = ref(null);
|
|
11
22
|
const data = ref(void 0);
|
|
12
23
|
const status = computed(() => _status.value);
|
|
13
24
|
const pending = computed(() => _status.value === "pending");
|
|
14
25
|
const reset = () => {
|
|
15
|
-
log("Reset");
|
|
16
26
|
_status.value = "idle";
|
|
17
27
|
error.value = null;
|
|
18
28
|
data.value = void 0;
|
|
19
29
|
};
|
|
20
30
|
const execute = async (args) => {
|
|
21
31
|
const client = useConvex();
|
|
32
|
+
const timer = createTimer();
|
|
22
33
|
if (!client) {
|
|
23
|
-
const err = new Error(
|
|
24
|
-
"[bcn] ConvexClient not available - actions only work on client side"
|
|
25
|
-
);
|
|
26
|
-
log("Error: Client not available");
|
|
34
|
+
const err = new Error("ConvexClient not available - actions only work on client side");
|
|
27
35
|
_status.value = "error";
|
|
28
36
|
error.value = err;
|
|
37
|
+
logger.event({
|
|
38
|
+
event: "operation:complete",
|
|
39
|
+
env: "client",
|
|
40
|
+
type: "action",
|
|
41
|
+
name: fnName,
|
|
42
|
+
duration_ms: timer(),
|
|
43
|
+
outcome: "error",
|
|
44
|
+
error: { type: "ClientError", message: err.message }
|
|
45
|
+
});
|
|
29
46
|
throw err;
|
|
30
47
|
}
|
|
31
|
-
log("Starting action", args);
|
|
32
48
|
_status.value = "pending";
|
|
33
49
|
error.value = null;
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
let actionId = null;
|
|
52
|
+
if (devToolsMutationRegistry) {
|
|
53
|
+
actionId = devToolsMutationRegistry.registerMutation({
|
|
54
|
+
name: fnName,
|
|
55
|
+
type: "action",
|
|
56
|
+
args,
|
|
57
|
+
state: "pending",
|
|
58
|
+
hasOptimisticUpdate: false,
|
|
59
|
+
startedAt: startTime
|
|
60
|
+
});
|
|
61
|
+
}
|
|
34
62
|
try {
|
|
35
63
|
const result = await client.action(action, args);
|
|
36
|
-
log("Success", result);
|
|
37
64
|
_status.value = "success";
|
|
38
65
|
data.value = result;
|
|
66
|
+
const settledAt = Date.now();
|
|
67
|
+
if (devToolsMutationRegistry && actionId) {
|
|
68
|
+
devToolsMutationRegistry.updateMutationState(actionId, {
|
|
69
|
+
state: "success",
|
|
70
|
+
result,
|
|
71
|
+
settledAt,
|
|
72
|
+
duration: settledAt - startTime
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
logger.event({
|
|
76
|
+
event: "operation:complete",
|
|
77
|
+
env: "client",
|
|
78
|
+
type: "action",
|
|
79
|
+
name: fnName,
|
|
80
|
+
duration_ms: timer(),
|
|
81
|
+
outcome: "success",
|
|
82
|
+
args_preview: formatArgsPreview(args)
|
|
83
|
+
});
|
|
39
84
|
return result;
|
|
40
85
|
} catch (e) {
|
|
41
86
|
const err = e instanceof Error ? e : new Error(String(e));
|
|
42
|
-
log("Error", err.message);
|
|
43
87
|
_status.value = "error";
|
|
44
88
|
error.value = err;
|
|
89
|
+
const settledAt = Date.now();
|
|
90
|
+
if (devToolsMutationRegistry && actionId) {
|
|
91
|
+
devToolsMutationRegistry.updateMutationState(actionId, {
|
|
92
|
+
state: "error",
|
|
93
|
+
error: err.message,
|
|
94
|
+
settledAt,
|
|
95
|
+
duration: settledAt - startTime
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
logger.event({
|
|
99
|
+
event: "operation:complete",
|
|
100
|
+
env: "client",
|
|
101
|
+
type: "action",
|
|
102
|
+
name: fnName,
|
|
103
|
+
duration_ms: timer(),
|
|
104
|
+
outcome: "error",
|
|
105
|
+
args_preview: formatArgsPreview(args),
|
|
106
|
+
error: {
|
|
107
|
+
type: err.name,
|
|
108
|
+
message: err.message,
|
|
109
|
+
retriable: false
|
|
110
|
+
}
|
|
111
|
+
});
|
|
45
112
|
throw err;
|
|
46
113
|
}
|
|
47
114
|
};
|
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
import { useState, computed, readonly
|
|
2
|
-
import { createLogger, getVerboseFlag } from "../utils/logger.js";
|
|
1
|
+
import { useState, computed, readonly } from "#imports";
|
|
3
2
|
export function useConvexAuth() {
|
|
4
3
|
const token = useState("convex:token", () => null);
|
|
5
4
|
const user = useState("convex:user", () => null);
|
|
6
5
|
const isPending = useState("convex:pending", () => false);
|
|
7
6
|
const isAuthenticated = computed(() => !!token.value && !!user.value);
|
|
8
|
-
const config = useRuntimeConfig();
|
|
9
|
-
const log = createLogger({
|
|
10
|
-
verbose: getVerboseFlag(config),
|
|
11
|
-
prefix: "[bcn:auth]"
|
|
12
|
-
});
|
|
13
|
-
log("useConvexAuth called", {
|
|
14
|
-
hasToken: !!token.value,
|
|
15
|
-
hasUser: !!user.value,
|
|
16
|
-
isAuthenticated: isAuthenticated.value,
|
|
17
|
-
isPending: isPending.value
|
|
18
|
-
});
|
|
19
7
|
return {
|
|
20
8
|
/** The JWT token for Convex authentication (readonly) */
|
|
21
9
|
token: readonly(token),
|
|
@@ -19,17 +19,6 @@ export interface ConnectionState {
|
|
|
19
19
|
/** Number of pending actions */
|
|
20
20
|
inflightActions: number;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Options for useConvexConnectionState
|
|
24
|
-
*/
|
|
25
|
-
export interface UseConvexConnectionStateOptions {
|
|
26
|
-
/**
|
|
27
|
-
* Enable verbose logging for debugging.
|
|
28
|
-
* Logs connection state changes.
|
|
29
|
-
* @default false
|
|
30
|
-
*/
|
|
31
|
-
verbose?: boolean;
|
|
32
|
-
}
|
|
33
22
|
/**
|
|
34
23
|
* Monitor the Convex WebSocket connection state.
|
|
35
24
|
* Useful for showing offline/reconnecting UI.
|
|
@@ -47,7 +36,7 @@ export interface UseConvexConnectionStateOptions {
|
|
|
47
36
|
* </template>
|
|
48
37
|
* ```
|
|
49
38
|
*/
|
|
50
|
-
export declare function useConvexConnectionState(
|
|
39
|
+
export declare function useConvexConnectionState(): {
|
|
51
40
|
/** Full connection state object */
|
|
52
41
|
state: Readonly<import("vue").Ref<{
|
|
53
42
|
readonly hasInflightRequests: boolean;
|