nuxt-convex-module 0.0.1 → 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 (73) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +40 -200
  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/{CgIEvOQU.js → 71bSMC-_.js} +1 -1
  7. package/dist/devtools-client/_nuxt/{C2KGYsR4.js → BhmjAr2Q.js} +1 -1
  8. package/dist/devtools-client/_nuxt/{D2ksFYuU.js → C6YdTgGl.js} +2 -2
  9. package/dist/devtools-client/_nuxt/{CtxksWnu.js → _Lf3mfPR.js} +1 -1
  10. package/dist/devtools-client/_nuxt/builds/latest.json +1 -1
  11. package/dist/devtools-client/_nuxt/builds/meta/7dfea6da-3eca-4a35-ba06-5e069ac7e8df.json +1 -0
  12. package/dist/devtools-client/_nuxt/error-404.CLlwiMp-.css +1 -0
  13. package/dist/devtools-client/_nuxt/error-500.Bxr6fpDU.css +1 -0
  14. package/dist/devtools-client/_nuxt/queries.DNBUjWk2.css +1 -0
  15. package/dist/devtools-client/auth/index.html +1 -1
  16. package/dist/devtools-client/index.html +1 -1
  17. package/dist/devtools-client/logs/index.html +1 -1
  18. package/dist/devtools-client/queries/index.html +1 -1
  19. package/dist/module.d.mts +29 -105
  20. package/dist/module.json +2 -2
  21. package/dist/module.mjs +329 -221
  22. package/dist/runtime/auth0/vue/index.d.ts +3 -3
  23. package/dist/runtime/better-auth/nuxt/middleware.d.ts +0 -6
  24. package/dist/runtime/better-auth/nuxt/middleware.js +4 -5
  25. package/dist/runtime/better-auth/nuxt/proxy.d.ts +18 -2
  26. package/dist/runtime/better-auth/nuxt/proxy.js +7 -7
  27. package/dist/runtime/better-auth/nuxt/server.d.ts +6 -6
  28. package/dist/runtime/better-auth/vue/auth-boundary.d.ts +1 -0
  29. package/dist/runtime/better-auth/vue/cross-domain.js +7 -2
  30. package/dist/runtime/better-auth/vue/hydration.d.ts +1 -1
  31. package/dist/runtime/better-auth/vue/index.d.ts +1 -1
  32. package/dist/runtime/better-auth/vue/index.js +1 -1
  33. package/dist/runtime/better-auth/vue/plugin.client.d.ts +1 -5
  34. package/dist/runtime/better-auth/vue/plugin.client.js +6 -10
  35. package/dist/runtime/better-auth/vue/plugin.server.d.ts +5 -16
  36. package/dist/runtime/better-auth/vue/plugin.server.js +3 -6
  37. package/dist/runtime/better-auth/vue/redirect.d.ts +2 -14
  38. package/dist/runtime/better-auth/vue/{use-auth.d.ts → use-better-auth.d.ts} +31 -11
  39. package/dist/runtime/better-auth/vue/{use-auth.js → use-better-auth.js} +2 -2
  40. package/dist/runtime/clerk/vue/index.d.ts +4 -4
  41. package/dist/runtime/nuxt/app.d.ts +48 -0
  42. package/dist/runtime/nuxt/app.js +8 -0
  43. package/dist/runtime/nuxt/composables/use-async-paginated-query.d.ts +88 -0
  44. package/dist/runtime/nuxt/composables/use-async-paginated-query.js +90 -0
  45. package/dist/runtime/nuxt/composables/use-async-query.d.ts +7 -16
  46. package/dist/runtime/nuxt/composables/use-async-query.js +14 -9
  47. package/dist/runtime/nuxt/config.d.ts +11 -0
  48. package/dist/runtime/nuxt/config.js +1 -0
  49. package/dist/runtime/nuxt/index.d.ts +2 -5
  50. package/dist/runtime/polar/vue/components.d.ts +8 -1
  51. package/dist/runtime/polar/vue/components.js +2 -2
  52. package/dist/runtime/vue/auth/index.d.ts +4 -4
  53. package/dist/runtime/vue/client.d.ts +5 -57
  54. package/dist/runtime/vue/client.js +2 -0
  55. package/dist/runtime/vue/components/convex-image.d.ts +50 -0
  56. package/dist/runtime/vue/components/convex-image.js +21 -0
  57. package/dist/runtime/vue/composables/use-connection-state.d.ts +1 -0
  58. package/dist/runtime/vue/composables/use-paginated-query.d.ts +5 -1
  59. package/dist/runtime/vue/composables/use-paginated-query.js +2 -2
  60. package/dist/runtime/vue/composables/use-query.d.ts +2 -0
  61. package/dist/runtime/vue/composables/use-subscription.d.ts +1 -15
  62. package/dist/runtime/vue/composables/use-upload-queue.d.ts +2 -3
  63. package/dist/runtime/vue/hydration.d.ts +3 -24
  64. package/dist/runtime/vue/index.d.ts +2 -2
  65. package/dist/runtime/vue/index.js +1 -0
  66. package/dist/runtime/vue/plugin.js +1 -1
  67. package/dist/runtime/vue/provide.d.ts +5 -5
  68. package/dist/types.d.mts +2 -2
  69. package/package.json +52 -16
  70. package/dist/devtools-client/_nuxt/builds/meta/23303226-2230-4d14-87ce-87b0f562de6c.json +0 -1
  71. package/dist/devtools-client/_nuxt/error-404.BpRUKU69.css +0 -1
  72. package/dist/devtools-client/_nuxt/error-500.DuDYJ5TI.css +0 -1
  73. package/dist/devtools-client/_nuxt/queries.BmISPDaV.css +0 -1
package/dist/module.mjs CHANGED
@@ -1,8 +1,128 @@
1
- import { useLogger, defineNuxtModule, createResolver, addPlugin, hasNuxtModule, addServerPlugin, addPluginTemplate, addTypeTemplate, updateTemplates, addImports, addComponent, addServerImports, addServerHandler, extendRouteRules, addRouteMiddleware } from '@nuxt/kit';
1
+ import { useLogger, updateTemplates, defineNuxtModule, createResolver, addPlugin, hasNuxtModule, addServerPlugin, addPluginTemplate, addTypeTemplate, addImports, addServerImports, addComponent, addServerHandler, extendRouteRules, addRouteMiddleware } from '@nuxt/kit';
2
2
  import { join, isAbsolute } from 'node:path';
3
- import { existsSync, readFileSync } from 'node:fs';
3
+ import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
5
 
6
+ function resolveIntegrationState(explicit, installed, declared = installed) {
7
+ if (explicit === false) return { enabled: false, missingPackage: false };
8
+ if (explicit === void 0) return { enabled: installed && declared, missingPackage: false };
9
+ return installed ? { enabled: true, missingPackage: false } : { enabled: false, missingPackage: true };
10
+ }
11
+ function isDeclaredDependency(id, rootDir) {
12
+ try {
13
+ const manifest = JSON.parse(readFileSync(join(rootDir, "package.json"), "utf8"));
14
+ return ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"].some((field) => typeof manifest[field] === "object" && manifest[field] !== null && id in manifest[field]);
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+ function stripTrailingSlashes(value) {
20
+ let end = value.length;
21
+ while (end > 0 && value[end - 1] === "/") end--;
22
+ return value.slice(0, end);
23
+ }
24
+ function normalizeRoutePath(value, option, warnings) {
25
+ let path = value;
26
+ if (!path.startsWith("/")) {
27
+ path = `/${path}`;
28
+ warnings.push(`\`${option}\` ("${value}") must start with "/" \u2014 using "${path}".`);
29
+ }
30
+ return path.length > 1 && path.endsWith("/") ? stripTrailingSlashes(path) : path;
31
+ }
32
+ function validateModuleOptions(input) {
33
+ const errors = [];
34
+ const warnings = [];
35
+ if (input.url && input.url.endsWith(".convex.site")) {
36
+ errors.push(
37
+ `\`convex.url\` ("${input.url}") ends with .convex.site, which is the HTTP Actions domain \u2014 deployment URLs end with .convex.cloud. Did you mean to set \`convex.siteUrl\`?`
38
+ );
39
+ } else if (input.url && !isHttpUrl(input.url)) {
40
+ warnings.push(
41
+ `\`convex.url\` ("${input.url}") does not look like a valid http(s) URL \u2014 Convex clients will fail to connect.`
42
+ );
43
+ }
44
+ if (input.siteUrl && input.siteUrl.endsWith(".convex.cloud")) {
45
+ errors.push(
46
+ `\`convex.siteUrl\` ("${input.siteUrl}") ends with .convex.cloud, which is the deployment domain \u2014 site URLs (HTTP Actions) end with .convex.site. Did you swap it with \`convex.url\`?`
47
+ );
48
+ } else if (input.siteUrl && !isHttpUrl(input.siteUrl)) {
49
+ warnings.push(
50
+ `\`convex.siteUrl\` ("${input.siteUrl}") does not look like a valid http(s) URL.`
51
+ );
52
+ }
53
+ const authRoute = normalizeRoutePath(input.authRoute, "convex.authRoute", warnings);
54
+ const loginPath = normalizeRoutePath(input.loginPath ?? "/login", "convex.betterAuth.loginPath", warnings);
55
+ if (authRoute !== "/api/auth" && !input.authClient) {
56
+ errors.push(
57
+ `\`convex.authRoute\` is "${authRoute}" but the bundled Better Auth client still calls /api/auth, so every auth request would 404. Keep the default route, or set \`convex.betterAuth.authClient\` to a client created with \`basePath: "${authRoute}"\`.`
58
+ );
59
+ }
60
+ if (input.authClient && !authClientModuleExists(input.authClient, input.rootDir)) {
61
+ errors.push(
62
+ `\`convex.betterAuth.authClient\` points at "${input.authClient}", which does not exist (resolved against \`${input.rootDir}\`). The build would fail with an opaque import error \u2014 fix the path or remove the option to use the bundled client.`
63
+ );
64
+ }
65
+ return { errors, warnings, authRoute, loginPath };
66
+ }
67
+ function isHttpUrl(value) {
68
+ if (!value.startsWith("http://") && !value.startsWith("https://")) {
69
+ return false;
70
+ }
71
+ try {
72
+ new URL(value);
73
+ return true;
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
78
+ function authClientModuleExists(authClient, rootDir) {
79
+ const base = isAbsolute(authClient) ? authClient : join(rootDir, authClient);
80
+ return ["", ".ts", ".js", ".mts", ".mjs", "/index.ts", "/index.js"].some((suffix) => {
81
+ const candidate = `${base}${suffix}`;
82
+ return existsSync(candidate) && statSync(candidate).isFile();
83
+ });
84
+ }
85
+ function integrationWarnings(flags, isInstalled) {
86
+ const warnings = [];
87
+ const rivals = [flags.clerk && "Clerk", flags.auth0 && "Auth0"].filter((name) => Boolean(name));
88
+ if (flags.betterAuth && rivals.length > 0) {
89
+ const options = rivals.map((name) => `\`convex.${name.toLowerCase()}: false\``).join(" / ");
90
+ warnings.push(
91
+ `Better Auth and ${rivals.join(" and ")} are both enabled. Better Auth's plugin owns the Convex client's auth, so the ${rivals.join(" / ")} adapter would fight it \u2014 set ${options}, or \`convex.betterAuth: false\`, to pick one.`
92
+ );
93
+ }
94
+ if (flags.polar && !isInstalled("@polar-sh/checkout")) {
95
+ warnings.push(
96
+ "`@convex-dev/polar` is installed but its peer `@polar-sh/checkout` is not \u2014 `<CheckoutLink>` imports it and the build fails the moment the component is used. Run `npm install @polar-sh/checkout`."
97
+ );
98
+ }
99
+ return warnings;
100
+ }
101
+ function formatStartupSummary(url, functionsDir, integrations) {
102
+ const names = {
103
+ betterAuth: "better-auth",
104
+ clerk: "clerk",
105
+ auth0: "auth0",
106
+ polar: "polar",
107
+ security: "nuxt-security"
108
+ };
109
+ const enabled = Object.keys(names).filter((key) => integrations[key]).map((key) => names[key]);
110
+ return `Convex ${url || "(no URL)"} \xB7 functions: ${functionsDir}/ \xB7 integrations: ${enabled.join(", ") || "none"}`;
111
+ }
112
+ function isPackageInstalled(id, rootDir) {
113
+ for (const base of [join(rootDir, "package.json"), import.meta.url]) {
114
+ const lookupPaths = createRequire(base).resolve.paths(id) ?? [];
115
+ if (lookupPaths.some((dir) => existsSync(join(dir, id, "package.json")))) return true;
116
+ }
117
+ return false;
118
+ }
119
+ function resolveDeploymentUrls(options, env) {
120
+ return {
121
+ url: stripTrailingSlashes(options.url || env.NUXT_PUBLIC_CONVEX_URL || env.CONVEX_URL || ""),
122
+ siteUrl: stripTrailingSlashes(options.siteUrl || env.NUXT_PUBLIC_CONVEX_SITE_URL || env.CONVEX_SITE_URL || "")
123
+ };
124
+ }
125
+
6
126
  const logger$1 = useLogger("nuxt-convex-module");
7
127
  const DEFAULT_FUNCTIONS_DIR = "convex";
8
128
  function resolveFunctionsDir(rootDir) {
@@ -27,18 +147,183 @@ function readFunctionsDirFromConvexJson(convexJsonPath) {
27
147
  }
28
148
  }
29
149
  function normalizeFunctionsDir(functionsDir) {
30
- const normalized = functionsDir.replace(/^\.?\//, "").replace(/\/+$/, "");
150
+ const normalized = stripTrailingSlashes(functionsDir.replace(/^\.?\//, ""));
31
151
  return normalized || void 0;
32
152
  }
33
153
 
154
+ function getConvexAliases(rootDir) {
155
+ const functionsDir = resolveFunctionsDir(rootDir);
156
+ const convexDir = join(rootDir, functionsDir);
157
+ const generatedDir = join(convexDir, "_generated");
158
+ return {
159
+ "#convex/api": join(generatedDir, "api"),
160
+ "#convex/server": join(generatedDir, "server"),
161
+ "#convex/dataModel": join(generatedDir, "dataModel"),
162
+ "#convex/_generated": generatedDir,
163
+ "#convex": convexDir
164
+ };
165
+ }
166
+
167
+ const CODEGEN_GUARDED_TEMPLATES = ["nuxt-convex-module-provide-api.mjs", "types/nuxt-convex-module-api-fallback.d.ts"];
168
+ function watchConvexCodegen(nuxt) {
169
+ if (!nuxt.options.dev) return;
170
+ const generatedDir = join(nuxt.options.rootDir, resolveFunctionsDir(nuxt.options.rootDir), "_generated");
171
+ if (!nuxt.options.watch.includes(generatedDir)) nuxt.options.watch.push(generatedDir);
172
+ nuxt.hook("builder:watch", async (_event, path) => {
173
+ if (!path.replace(/\\/g, "/").includes("_generated/api")) return;
174
+ await updateTemplates({ filter: (template) => CODEGEN_GUARDED_TEMPLATES.includes(template.filename) });
175
+ });
176
+ }
177
+
178
+ const PLAIN_NUXT_DEV = /^(?:npx )?(?:nuxt|nuxi) dev(?: [^&|;]*)?$/;
179
+ function combinedDevScript(nuxtCommand) {
180
+ return `convex dev --start '${nuxtCommand}'`;
181
+ }
182
+ function setupDevScript(rootDir) {
183
+ const path = join(rootDir, "package.json");
184
+ let raw;
185
+ try {
186
+ raw = readFileSync(path, "utf8");
187
+ } catch {
188
+ return { changed: false };
189
+ }
190
+ let manifest;
191
+ try {
192
+ manifest = JSON.parse(raw);
193
+ } catch {
194
+ return { changed: false };
195
+ }
196
+ const scripts = manifest.scripts;
197
+ if (typeof scripts !== "object" || scripts === null) return { changed: false };
198
+ const dev = scripts.dev;
199
+ if (typeof dev !== "string" || !PLAIN_NUXT_DEV.test(dev.trim())) return { changed: false };
200
+ if (Object.values(scripts).some((s) => typeof s === "string" && s.includes("convex dev"))) return { changed: false };
201
+ if (!isDeclaredDependency("convex", rootDir)) return { changed: false };
202
+ const to = combinedDevScript(dev.trim());
203
+ scripts.dev = to;
204
+ const indent = raw.match(/^[ \t]+(?=")/m)?.[0] ?? " ";
205
+ const newline = raw.endsWith("\n") ? "\n" : "";
206
+ writeFileSync(path, JSON.stringify(manifest, null, indent) + newline);
207
+ return { changed: true, from: dev, to };
208
+ }
209
+
210
+ const vue = (file) => `runtime/vue/${file}`;
211
+ const composable = (file) => vue(`composables/${file}`);
212
+ const asyncQuery = "runtime/nuxt/composables/use-async-query";
213
+ const asyncPaginatedQuery = "runtime/nuxt/composables/use-async-paginated-query";
214
+ const APP_IMPORTS = {
215
+ core: [
216
+ { name: "useConvex", from: vue("client") },
217
+ { name: "useQuery", from: composable("use-query") },
218
+ { name: "useQuery_experimental", from: composable("use-query") },
219
+ { name: "useConvexQuery", from: composable("use-query") },
220
+ { name: "useQueries", from: composable("use-queries") },
221
+ { name: "useConvexQueries", from: composable("use-queries") },
222
+ { name: "useMutation", from: composable("use-mutation") },
223
+ { name: "useConvexMutation", from: composable("use-mutation") },
224
+ { name: "useAction", from: composable("use-action") },
225
+ { name: "useConvexAction", from: composable("use-action") },
226
+ { name: "useConvexConnectionState", from: composable("use-connection-state") },
227
+ { name: "useUpload", from: composable("use-upload") },
228
+ { name: "useConvexUpload", from: composable("use-upload") },
229
+ { name: "uploadFile", from: composable("use-upload") },
230
+ { name: "useUploadQueue", from: composable("use-upload-queue") },
231
+ { name: "useConvexUploadQueue", from: composable("use-upload-queue") },
232
+ { name: "useStorageUrl", from: composable("use-storage-url") },
233
+ { name: "useConvexStorageUrl", from: composable("use-storage-url") },
234
+ { name: "useConvexAuth", from: vue("auth/index") },
235
+ { name: "provideConvexAuth", from: vue("auth/index") },
236
+ { name: "provideConvexApi", from: vue("provide") },
237
+ { name: "useConvexApi", from: vue("provide") },
238
+ { name: "useConvexNamespace", from: vue("provide") },
239
+ { name: "usePreloadedQuery", from: vue("hydration") },
240
+ // Nuxt-only (imports `#app`), hence under runtime/nuxt/ — see PARITY.md.
241
+ // Its types too, so `import type { AsyncQueryReturn } from '#imports'` works
242
+ // without spelling out the `nuxt-convex-module/app` subpath.
243
+ { name: "useAsyncQuery", from: asyncQuery },
244
+ { name: "useConvexAsyncQuery", from: asyncQuery },
245
+ { name: "AsyncQueryData", from: asyncQuery, type: true },
246
+ { name: "AsyncQueryOptions", from: asyncQuery, type: true },
247
+ { name: "AsyncQueryReturn", from: asyncQuery, type: true },
248
+ { name: "AsyncQueryStatus", from: asyncQuery, type: true },
249
+ { name: "usePaginatedQuery", from: composable("use-paginated-query") },
250
+ { name: "useConvexPaginatedQuery", from: composable("use-paginated-query") },
251
+ { name: "usePaginatedQuery_experimental", from: composable("use-paginated-query") },
252
+ { name: "useAsyncPaginatedQuery", from: asyncPaginatedQuery },
253
+ { name: "useConvexAsyncPaginatedQuery", from: asyncPaginatedQuery },
254
+ { name: "AsyncPaginatedQueryData", from: asyncPaginatedQuery, type: true },
255
+ { name: "AsyncPaginatedQueryOptions", from: asyncPaginatedQuery, type: true },
256
+ { name: "AsyncPaginatedQueryReturn", from: asyncPaginatedQuery, type: true }
257
+ ],
258
+ betterAuth: [
259
+ { name: "useBetterAuth", from: "runtime/better-auth/vue/use-better-auth" },
260
+ { name: "usePreloadedAuthQuery", from: "runtime/better-auth/vue/hydration" },
261
+ { name: "resolveAuthRedirect", from: "runtime/better-auth/vue/redirect" }
262
+ ],
263
+ clerk: [{ name: "provideConvexAuthFromClerk", from: "runtime/clerk/vue/index" }],
264
+ auth0: [{ name: "provideConvexAuthFromAuth0", from: "runtime/auth0/vue/index" }],
265
+ polar: []
266
+ };
267
+ const APP_COMPONENTS = {
268
+ core: [
269
+ ...["Authenticated", "Unauthenticated", "AuthLoading", "AuthRefreshing"].map((name) => ({ name, from: vue("auth/helpers") })),
270
+ { name: "ConvexImage", from: vue("components/convex-image") }
271
+ ],
272
+ betterAuth: [{ name: "AuthBoundary", from: "runtime/better-auth/vue/auth-boundary" }],
273
+ clerk: [{ name: "ConvexProviderWithClerk", from: "runtime/clerk/vue/index" }],
274
+ auth0: [{ name: "ConvexProviderWithAuth0", from: "runtime/auth0/vue/index" }],
275
+ polar: ["CheckoutLink", "CustomerPortalLink"].map((name) => ({ name, from: "runtime/polar/vue/components" }))
276
+ };
277
+ const SERVER_IMPORTS = {
278
+ core: ["fetchQuery", "fetchMutation", "fetchAction", "preloadQuery", "preloadedQueryResult"].map((name) => ({ name, from: "runtime/nuxt/index" })),
279
+ betterAuth: ["convexAuth", "convexBetterAuthNuxt"].map((name) => ({ name, from: "runtime/better-auth/nuxt/server" })),
280
+ clerk: [],
281
+ auth0: [],
282
+ polar: []
283
+ };
284
+
285
+ function convexTypeFallbackContents(hasApi, functionsDir) {
286
+ if (hasApi) {
287
+ return "export {}\n";
288
+ }
289
+ return [
290
+ `// Placeholder until \`npx convex dev\` generates ${functionsDir}/_generated.`,
291
+ "declare module '#convex/api' {",
292
+ " export const api: any",
293
+ " export const internal: any",
294
+ " export const components: any",
295
+ "}",
296
+ "declare module '#convex/server' {",
297
+ " export const query: any",
298
+ " export const internalQuery: any",
299
+ " export const mutation: any",
300
+ " export const internalMutation: any",
301
+ " export const action: any",
302
+ " export const internalAction: any",
303
+ " export const httpAction: any",
304
+ " export type QueryCtx = any",
305
+ " export type MutationCtx = any",
306
+ " export type ActionCtx = any",
307
+ " export type DatabaseReader = any",
308
+ " export type DatabaseWriter = any",
309
+ "}",
310
+ "declare module '#convex/dataModel' {",
311
+ " export type Doc<TableName extends string = string> = any",
312
+ " export type Id<TableName extends string = string> = string",
313
+ " export type DataModel = any",
314
+ " export type TableNames = string",
315
+ "}",
316
+ ""
317
+ ].join("\n");
318
+ }
319
+
34
320
  const logger = useLogger("nuxt-convex-module");
35
321
  const module$1 = defineNuxtModule({
36
322
  meta: {
37
323
  name: "nuxt-convex-module",
38
324
  configKey: "convex",
39
- // Surfaced by Nuxt DevTools and the nuxt/modules registry. Point at the
40
- // deployed docs site once it ships.
41
- docs: "https://github.com/qruto/nuxt-convex-module#readme",
325
+ // Surfaced by Nuxt DevTools and the nuxt/modules registry.
326
+ docs: "https://nuxt-convex-module.dev",
42
327
  // `moduleDependencies` (below) is a Nuxt >= 4.1 feature — fail fast with a
43
328
  // clear kit error on older Nuxt instead of silently skipping nuxt-security.
44
329
  compatibility: {
@@ -47,7 +332,8 @@ const module$1 = defineNuxtModule({
47
332
  },
48
333
  defaults: {
49
334
  authRoute: "/api/auth",
50
- devtools: true
335
+ devtools: true,
336
+ devScript: true
51
337
  },
52
338
  // nuxt-security is optional. When the app has it installed (and hasn't
53
339
  // switched it off), declare it as a module dependency so Nuxt's core loader
@@ -64,19 +350,20 @@ const module$1 = defineNuxtModule({
64
350
  url,
65
351
  siteUrl,
66
352
  authRoute: options.authRoute || "/api/auth",
353
+ loginPath: nuxt.options.runtimeConfig.public.convex.loginPath,
67
354
  authClient: typeof options.betterAuth === "object" ? options.betterAuth.authClient : void 0,
68
355
  rootDir: nuxt.options.rootDir
69
356
  });
70
357
  for (const message of diagnostics.errors) logger.error(message);
71
358
  for (const message of diagnostics.warnings) logger.warn(message);
72
359
  options.authRoute = diagnostics.authRoute;
360
+ nuxt.options.runtimeConfig.public.convex.loginPath = diagnostics.loginPath;
73
361
  registerAuthClientAlias(resolver, nuxt, options);
74
362
  registerConvexAliases(nuxt);
75
363
  registerConvexApiPlugin(resolver, nuxt);
76
364
  registerConvexTypeFallback(nuxt);
77
365
  registerVueComposables(resolver);
78
366
  registerAuthComponents(resolver);
79
- registerServerImports(resolver);
80
367
  const integrations = registerIntegrations(resolver, nuxt, options);
81
368
  watchConvexCodegen(nuxt);
82
369
  if (nuxt.options.dev && options.devtools !== false && isDevtoolsUiEnabled(nuxt)) {
@@ -93,88 +380,18 @@ const module$1 = defineNuxtModule({
93
380
  if (nuxt.options.dev && !nuxt.options._prepare) {
94
381
  logger.info(formatStartupSummary(url, resolveFunctionsDir(nuxt.options.rootDir), integrations));
95
382
  }
383
+ if (options.devScript !== false && (nuxt.options.dev || nuxt.options._prepare)) {
384
+ const result = setupDevScript(nuxt.options.rootDir);
385
+ if (result.changed) {
386
+ logger.info(`Set \`scripts.dev\` to \`${result.to}\` \u2014 one command now runs Convex beside Nuxt, no .env needed locally. Set \`convex.devScript: false\` to keep your own script.`);
387
+ }
388
+ }
96
389
  }
97
390
  });
98
391
  function isDevtoolsUiEnabled(nuxt) {
99
392
  const devtools = nuxt.options.devtools;
100
393
  return typeof devtools === "boolean" ? devtools : devtools?.enabled !== false;
101
394
  }
102
- function resolveIntegrationState(explicit, installed) {
103
- if (explicit === false) return { enabled: false, missingPackage: false };
104
- if (explicit === void 0) return { enabled: installed, missingPackage: false };
105
- return installed ? { enabled: true, missingPackage: false } : { enabled: false, missingPackage: true };
106
- }
107
- function stripTrailingSlashes(value) {
108
- let end = value.length;
109
- while (end > 0 && value[end - 1] === "/") end--;
110
- return value.slice(0, end);
111
- }
112
- function validateModuleOptions(input) {
113
- const errors = [];
114
- const warnings = [];
115
- if (input.url && input.url.endsWith(".convex.site")) {
116
- errors.push(
117
- `\`convex.url\` ("${input.url}") ends with .convex.site, which is the HTTP Actions domain \u2014 deployment URLs end with .convex.cloud. Did you mean to set \`convex.siteUrl\`?`
118
- );
119
- } else if (input.url && !isHttpUrl(input.url)) {
120
- warnings.push(
121
- `\`convex.url\` ("${input.url}") does not look like a valid http(s) URL \u2014 Convex clients will fail to connect.`
122
- );
123
- }
124
- if (input.siteUrl && input.siteUrl.endsWith(".convex.cloud")) {
125
- warnings.push(
126
- `\`convex.siteUrl\` ("${input.siteUrl}") ends with .convex.cloud, which is the deployment domain \u2014 site URLs (HTTP Actions) end with .convex.site. Did you swap it with \`convex.url\`?`
127
- );
128
- } else if (input.siteUrl && !isHttpUrl(input.siteUrl)) {
129
- warnings.push(
130
- `\`convex.siteUrl\` ("${input.siteUrl}") does not look like a valid http(s) URL.`
131
- );
132
- }
133
- let authRoute = input.authRoute;
134
- if (!authRoute.startsWith("/")) {
135
- authRoute = `/${authRoute}`;
136
- warnings.push(
137
- `\`convex.authRoute\` ("${input.authRoute}") must start with "/" \u2014 using "${authRoute}".`
138
- );
139
- }
140
- if (authRoute.length > 1 && authRoute.endsWith("/")) {
141
- authRoute = stripTrailingSlashes(authRoute);
142
- }
143
- if (input.authClient && !authClientModuleExists(input.authClient, input.rootDir)) {
144
- errors.push(
145
- `\`convex.betterAuth.authClient\` points at "${input.authClient}", which does not exist (resolved against \`${input.rootDir}\`). The build would fail with an opaque import error \u2014 fix the path or remove the option to use the bundled client.`
146
- );
147
- }
148
- return { errors, warnings, authRoute };
149
- }
150
- function isHttpUrl(value) {
151
- if (!value.startsWith("http://") && !value.startsWith("https://")) {
152
- return false;
153
- }
154
- try {
155
- new URL(value);
156
- return true;
157
- } catch {
158
- return false;
159
- }
160
- }
161
- function authClientModuleExists(authClient, rootDir) {
162
- const base = isAbsolute(authClient) ? authClient : join(rootDir, authClient);
163
- return ["", ".ts", ".js", ".mts", ".mjs", "/index.ts", "/index.js"].some(
164
- (suffix) => existsSync(`${base}${suffix}`)
165
- );
166
- }
167
- function formatStartupSummary(url, functionsDir, integrations) {
168
- const names = {
169
- betterAuth: "better-auth",
170
- clerk: "clerk",
171
- auth0: "auth0",
172
- polar: "polar",
173
- security: "nuxt-security"
174
- };
175
- const enabled = Object.keys(names).filter((key) => integrations[key]).map((key) => names[key]);
176
- return `Convex ${url || "(no URL)"} \xB7 functions: ${functionsDir}/ \xB7 integrations: ${enabled.join(", ") || "none"}`;
177
- }
178
395
  function registerIntegrations(resolver, nuxt, options) {
179
396
  const report = (key, pkg, state) => {
180
397
  if (state.missingPackage) {
@@ -182,7 +399,8 @@ function registerIntegrations(resolver, nuxt, options) {
182
399
  }
183
400
  return state.enabled;
184
401
  };
185
- const resolve = (key, pkg) => report(key, pkg, resolveIntegrationState(options[key], isPackageInstalled(pkg, nuxt.options.rootDir)));
402
+ const rootDir = nuxt.options.rootDir;
403
+ const resolve = (key, pkg) => report(key, pkg, resolveIntegrationState(options[key], isPackageInstalled(pkg, rootDir), isDeclaredDependency(pkg, rootDir)));
186
404
  const betterAuth = resolve("betterAuth", "@convex-dev/better-auth");
187
405
  if (betterAuth) {
188
406
  registerBetterAuth(resolver, options.authRoute || "/api/auth");
@@ -205,7 +423,9 @@ function registerIntegrations(resolver, nuxt, options) {
205
423
  if (security) {
206
424
  registerSecurity(resolver);
207
425
  }
208
- return { betterAuth, clerk, auth0, polar, security };
426
+ const flags = { betterAuth, clerk, auth0, polar, security };
427
+ for (const message of integrationWarnings(flags, (pkg) => isPackageInstalled(pkg, rootDir))) logger.warn(message);
428
+ return flags;
209
429
  }
210
430
  function readSecurityOption(nuxt) {
211
431
  const opts = nuxt.options;
@@ -214,25 +434,14 @@ function readSecurityOption(nuxt) {
214
434
  function resolveSecurityState(nuxt, explicit) {
215
435
  const opts = nuxt.options;
216
436
  if (opts.security === false) return { enabled: false, missingPackage: false };
217
- const installed = hasNuxtModule("nuxt-security", nuxt) || isPackageInstalled("nuxt-security", nuxt.options.rootDir);
218
- return resolveIntegrationState(explicit, installed);
437
+ const registered = hasNuxtModule("nuxt-security", nuxt);
438
+ const installed = registered || isPackageInstalled("nuxt-security", nuxt.options.rootDir);
439
+ const declared = registered || isDeclaredDependency("nuxt-security", nuxt.options.rootDir);
440
+ return resolveIntegrationState(explicit, installed, declared);
219
441
  }
220
442
  function registerSecurity(resolver) {
221
443
  addServerPlugin(resolver.resolve("./runtime/nuxt/security"));
222
444
  }
223
- function isPackageInstalled(id, rootDir) {
224
- for (const base of [join(rootDir, "package.json"), import.meta.url]) {
225
- const lookupPaths = createRequire(base).resolve.paths(id) ?? [];
226
- if (lookupPaths.some((dir) => existsSync(join(dir, id, "package.json")))) return true;
227
- }
228
- return false;
229
- }
230
- function resolveDeploymentUrls(options, env) {
231
- return {
232
- url: options.url || env.NUXT_PUBLIC_CONVEX_URL || env.CONVEX_URL || "",
233
- siteUrl: options.siteUrl || env.NUXT_PUBLIC_CONVEX_SITE_URL || env.CONVEX_SITE_URL || ""
234
- };
235
- }
236
445
  function applyRuntimeConfig(nuxt, options) {
237
446
  const resolved = resolveDeploymentUrls(options, process.env);
238
447
  const url = nuxt.options._prepare ? "" : resolved.url;
@@ -255,18 +464,6 @@ function applyRuntimeConfig(nuxt, options) {
255
464
  runtimeConfig.convex = { ...runtimeConfig.convex, siteUrl };
256
465
  return { url, siteUrl };
257
466
  }
258
- function getConvexAliases(rootDir) {
259
- const functionsDir = resolveFunctionsDir(rootDir);
260
- const convexDir = join(rootDir, functionsDir);
261
- const generatedDir = join(convexDir, "_generated");
262
- return {
263
- "#convex/api": join(generatedDir, "api"),
264
- "#convex/server": join(generatedDir, "server"),
265
- "#convex/dataModel": join(generatedDir, "dataModel"),
266
- "#convex/_generated": generatedDir,
267
- "#convex": convexDir
268
- };
269
- }
270
467
  function registerConvexAliases(nuxt) {
271
468
  const aliases = getConvexAliases(nuxt.options.rootDir);
272
469
  nuxt.options.nitro ||= {};
@@ -314,40 +511,6 @@ function registerConvexApiPlugin(resolver, nuxt) {
314
511
  }
315
512
  });
316
513
  }
317
- function convexTypeFallbackContents(hasApi, functionsDir) {
318
- if (hasApi) {
319
- return "export {}\n";
320
- }
321
- return [
322
- `// Placeholder until \`npx convex dev\` generates ${functionsDir}/_generated.`,
323
- "declare module '#convex/api' {",
324
- " export const api: any",
325
- " export const internal: any",
326
- " export const components: any",
327
- "}",
328
- "declare module '#convex/server' {",
329
- " export const query: any",
330
- " export const internalQuery: any",
331
- " export const mutation: any",
332
- " export const internalMutation: any",
333
- " export const action: any",
334
- " export const internalAction: any",
335
- " export const httpAction: any",
336
- " export type QueryCtx = any",
337
- " export type MutationCtx = any",
338
- " export type ActionCtx = any",
339
- " export type DatabaseReader = any",
340
- " export type DatabaseWriter = any",
341
- "}",
342
- "declare module '#convex/dataModel' {",
343
- " export type Doc<TableName extends string = string> = any",
344
- " export type Id<TableName extends string = string> = string",
345
- " export type DataModel = any",
346
- " export type TableNames = string",
347
- "}",
348
- ""
349
- ].join("\n");
350
- }
351
514
  function registerConvexTypeFallback(nuxt) {
352
515
  const functionsDir = resolveFunctionsDir(nuxt.options.rootDir);
353
516
  addTypeTemplate({
@@ -355,59 +518,21 @@ function registerConvexTypeFallback(nuxt) {
355
518
  getContents: () => convexTypeFallbackContents(hasGeneratedApi(nuxt.options.rootDir, functionsDir), functionsDir)
356
519
  }, { nuxt: true, nitro: true });
357
520
  }
358
- const CODEGEN_GUARDED_TEMPLATES = ["nuxt-convex-module-provide-api.mjs", "types/nuxt-convex-module-api-fallback.d.ts"];
359
- function watchConvexCodegen(nuxt) {
360
- if (!nuxt.options.dev) return;
361
- nuxt.hook("builder:watch", async (_event, path) => {
362
- if (!path.replace(/\\/g, "/").includes("_generated/api")) return;
363
- await updateTemplates({ filter: (template) => CODEGEN_GUARDED_TEMPLATES.includes(template.filename) });
364
- });
365
- }
366
521
  function registerVueComposables(resolver) {
367
- const composables = [
368
- { name: "useConvex", from: resolver.resolve("./runtime/vue/client") },
369
- { name: "useQuery", from: resolver.resolve("./runtime/vue/composables/use-query") },
370
- { name: "useQuery_experimental", from: resolver.resolve("./runtime/vue/composables/use-query") },
371
- { name: "useConvexQuery", from: resolver.resolve("./runtime/vue/composables/use-query") },
372
- { name: "useQueries", from: resolver.resolve("./runtime/vue/composables/use-queries") },
373
- { name: "useConvexQueries", from: resolver.resolve("./runtime/vue/composables/use-queries") },
374
- { name: "useMutation", from: resolver.resolve("./runtime/vue/composables/use-mutation") },
375
- { name: "useConvexMutation", from: resolver.resolve("./runtime/vue/composables/use-mutation") },
376
- { name: "useAction", from: resolver.resolve("./runtime/vue/composables/use-action") },
377
- { name: "useConvexAction", from: resolver.resolve("./runtime/vue/composables/use-action") },
378
- { name: "useConvexConnectionState", from: resolver.resolve("./runtime/vue/composables/use-connection-state") },
379
- { name: "useUpload", from: resolver.resolve("./runtime/vue/composables/use-upload") },
380
- { name: "useConvexUpload", from: resolver.resolve("./runtime/vue/composables/use-upload") },
381
- { name: "uploadFile", from: resolver.resolve("./runtime/vue/composables/use-upload") },
382
- { name: "useUploadQueue", from: resolver.resolve("./runtime/vue/composables/use-upload-queue") },
383
- { name: "useConvexUploadQueue", from: resolver.resolve("./runtime/vue/composables/use-upload-queue") },
384
- { name: "useStorageUrl", from: resolver.resolve("./runtime/vue/composables/use-storage-url") },
385
- { name: "useConvexStorageUrl", from: resolver.resolve("./runtime/vue/composables/use-storage-url") },
386
- { name: "useConvexAuth", from: resolver.resolve("./runtime/vue/auth/index") },
387
- { name: "provideConvexAuth", from: resolver.resolve("./runtime/vue/auth/index") },
388
- { name: "provideConvexApi", from: resolver.resolve("./runtime/vue/provide") },
389
- { name: "useConvexApi", from: resolver.resolve("./runtime/vue/provide") },
390
- { name: "useConvexNamespace", from: resolver.resolve("./runtime/vue/provide") },
391
- { name: "usePreloadedQuery", from: resolver.resolve("./runtime/vue/hydration") },
392
- // Nuxt-only (imports `#app`), hence under runtime/nuxt/ — see PARITY.md.
393
- { name: "useAsyncQuery", from: resolver.resolve("./runtime/nuxt/composables/use-async-query") },
394
- { name: "useConvexAsyncQuery", from: resolver.resolve("./runtime/nuxt/composables/use-async-query") },
395
- { name: "usePaginatedQuery", from: resolver.resolve("./runtime/vue/composables/use-paginated-query") },
396
- { name: "useConvexPaginatedQuery", from: resolver.resolve("./runtime/vue/composables/use-paginated-query") },
397
- { name: "usePaginatedQuery_experimental", from: resolver.resolve("./runtime/vue/composables/use-paginated-query") }
398
- ];
399
- addImports(composables);
522
+ registerIntegrationImports(resolver, "core");
400
523
  }
401
524
  function registerAuthComponents(resolver) {
402
- const helpersFile = resolver.resolve("./runtime/vue/auth/helpers");
403
- for (const name of ["Authenticated", "Unauthenticated", "AuthLoading", "AuthRefreshing"]) {
404
- addComponent({ name, filePath: helpersFile, export: name });
405
- }
525
+ registerComponents(resolver, "core");
406
526
  }
407
- function registerServerImports(resolver) {
408
- const fromNuxt = resolver.resolve("./runtime/nuxt/index");
409
- const serverUtils = ["fetchQuery", "fetchMutation", "fetchAction", "preloadQuery", "preloadedQueryResult"];
410
- addServerImports(serverUtils.map((name) => ({ name, from: fromNuxt })));
527
+ function registerIntegrationImports(resolver, integration) {
528
+ const resolve = (entry) => ({ ...entry, from: resolver.resolve(`./${entry.from}`) });
529
+ if (APP_IMPORTS[integration].length > 0) addImports(APP_IMPORTS[integration].map(resolve));
530
+ if (SERVER_IMPORTS[integration].length > 0) addServerImports(SERVER_IMPORTS[integration].map(resolve));
531
+ }
532
+ function registerComponents(resolver, integration) {
533
+ for (const { name, from } of APP_COMPONENTS[integration]) {
534
+ addComponent({ name, filePath: resolver.resolve(`./${from}`), export: name });
535
+ }
411
536
  }
412
537
  const AUTH_PROXY_SECURITY_RULES = {
413
538
  xssValidator: false,
@@ -416,16 +541,8 @@ const AUTH_PROXY_SECURITY_RULES = {
416
541
  function registerBetterAuth(resolver, authRoute) {
417
542
  addPlugin(resolver.resolve("./runtime/better-auth/vue/plugin.client"));
418
543
  addPlugin(resolver.resolve("./runtime/better-auth/vue/plugin.server"));
419
- addImports([
420
- { name: "useAuth", from: resolver.resolve("./runtime/better-auth/vue/use-auth") },
421
- { name: "usePreloadedAuthQuery", from: resolver.resolve("./runtime/better-auth/vue/hydration") },
422
- { name: "resolveAuthRedirect", from: resolver.resolve("./runtime/better-auth/vue/redirect") }
423
- ]);
424
- addComponent({
425
- name: "AuthBoundary",
426
- filePath: resolver.resolve("./runtime/better-auth/vue/auth-boundary"),
427
- export: "AuthBoundary"
428
- });
544
+ registerIntegrationImports(resolver, "betterAuth");
545
+ registerComponents(resolver, "betterAuth");
429
546
  addServerHandler({
430
547
  route: `${authRoute}/**`,
431
548
  handler: resolver.resolve("./runtime/better-auth/nuxt/proxy")
@@ -440,29 +557,20 @@ function registerBetterAuth(resolver, authRoute) {
440
557
  path: resolver.resolve("./runtime/better-auth/nuxt/middleware"),
441
558
  global: false
442
559
  });
443
- addServerImports([
444
- { name: "convexAuth", from: resolver.resolve("./runtime/better-auth/nuxt/server") },
445
- { name: "convexBetterAuthNuxt", from: resolver.resolve("./runtime/better-auth/nuxt/server") }
446
- ]);
447
560
  }
448
561
  function registerBaseConvexClient(resolver) {
449
562
  addPlugin(resolver.resolve("./runtime/vue/plugin"));
450
563
  }
451
564
  function registerClerk(resolver) {
452
- const from = resolver.resolve("./runtime/clerk/vue/index");
453
- addImports({ name: "provideConvexAuthFromClerk", from });
454
- addComponent({ name: "ConvexProviderWithClerk", filePath: from, export: "ConvexProviderWithClerk" });
565
+ registerIntegrationImports(resolver, "clerk");
566
+ registerComponents(resolver, "clerk");
455
567
  }
456
568
  function registerAuth0(resolver) {
457
- const from = resolver.resolve("./runtime/auth0/vue/index");
458
- addImports({ name: "provideConvexAuthFromAuth0", from });
459
- addComponent({ name: "ConvexProviderWithAuth0", filePath: from, export: "ConvexProviderWithAuth0" });
569
+ registerIntegrationImports(resolver, "auth0");
570
+ registerComponents(resolver, "auth0");
460
571
  }
461
572
  function registerPolarComponents(resolver) {
462
- const componentsFile = resolver.resolve("./runtime/polar/vue/components");
463
- for (const name of ["CheckoutLink", "CustomerPortalLink"]) {
464
- addComponent({ name, filePath: componentsFile, export: name });
465
- }
573
+ registerComponents(resolver, "polar");
466
574
  }
467
575
 
468
- export { convexTypeFallbackContents, module$1 as default, formatStartupSummary, getConvexAliases, resolveDeploymentUrls, resolveIntegrationState, validateModuleOptions };
576
+ export { module$1 as default };