nuxt-convex-module 0.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 (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +242 -0
  3. package/dist/chunks/index.mjs +64 -0
  4. package/dist/devtools-client/200.html +1 -0
  5. package/dist/devtools-client/404.html +1 -0
  6. package/dist/devtools-client/_nuxt/B29GGJ5n.js +1 -0
  7. package/dist/devtools-client/_nuxt/BSDEpQet.js +1 -0
  8. package/dist/devtools-client/_nuxt/BY23OQOo.js +1 -0
  9. package/dist/devtools-client/_nuxt/BZYRaL-G.js +1 -0
  10. package/dist/devtools-client/_nuxt/Byw6FxnC.js +1 -0
  11. package/dist/devtools-client/_nuxt/CJJ_hcZN.js +1 -0
  12. package/dist/devtools-client/_nuxt/CS6WMFOx.js +2 -0
  13. package/dist/devtools-client/_nuxt/CZbAGcNC.js +1 -0
  14. package/dist/devtools-client/_nuxt/Cr6_aklh.js +2 -0
  15. package/dist/devtools-client/_nuxt/DNfpyBVU.js +3 -0
  16. package/dist/devtools-client/_nuxt/DPWXUmcI.js +1 -0
  17. package/dist/devtools-client/_nuxt/builds/latest.json +1 -0
  18. package/dist/devtools-client/_nuxt/builds/meta/a7708498-bd09-4de2-b5ab-19b380293c77.json +1 -0
  19. package/dist/devtools-client/_nuxt/dy_xXcv9.js +1 -0
  20. package/dist/devtools-client/_nuxt/entry.DbTJhkHT.css +1 -0
  21. package/dist/devtools-client/_nuxt/error-404.CwGGO2Kz.css +1 -0
  22. package/dist/devtools-client/_nuxt/error-500.8ZJqCQm8.css +1 -0
  23. package/dist/devtools-client/_nuxt/ncgEdbEt.js +1 -0
  24. package/dist/devtools-client/_nuxt/queries.CE_dtt9-.css +1 -0
  25. package/dist/devtools-client/auth/index.html +1 -0
  26. package/dist/devtools-client/icon.svg +6 -0
  27. package/dist/devtools-client/index.html +1 -0
  28. package/dist/devtools-client/logs/index.html +1 -0
  29. package/dist/devtools-client/queries/index.html +1 -0
  30. package/dist/module.d.ts +189 -0
  31. package/dist/module.js +489 -0
  32. package/dist/module.json +13 -0
  33. package/dist/runtime/auth0/vue/index.d.ts +58 -0
  34. package/dist/runtime/auth0/vue/index.js +33 -0
  35. package/dist/runtime/better-auth/nuxt/middleware.d.ts +14 -0
  36. package/dist/runtime/better-auth/nuxt/middleware.js +39 -0
  37. package/dist/runtime/better-auth/nuxt/proxy.d.ts +6 -0
  38. package/dist/runtime/better-auth/nuxt/proxy.js +11 -0
  39. package/dist/runtime/better-auth/nuxt/server.d.ts +106 -0
  40. package/dist/runtime/better-auth/nuxt/server.js +123 -0
  41. package/dist/runtime/better-auth/vue/auth-boundary.d.ts +131 -0
  42. package/dist/runtime/better-auth/vue/auth-boundary.js +78 -0
  43. package/dist/runtime/better-auth/vue/client.d.ts +358 -0
  44. package/dist/runtime/better-auth/vue/client.js +5 -0
  45. package/dist/runtime/better-auth/vue/cross-domain.d.ts +39 -0
  46. package/dist/runtime/better-auth/vue/cross-domain.js +45 -0
  47. package/dist/runtime/better-auth/vue/hydration.d.ts +20 -0
  48. package/dist/runtime/better-auth/vue/hydration.js +52 -0
  49. package/dist/runtime/better-auth/vue/index.d.ts +12 -0
  50. package/dist/runtime/better-auth/vue/index.js +6 -0
  51. package/dist/runtime/better-auth/vue/plugin.client.d.ts +6 -0
  52. package/dist/runtime/better-auth/vue/plugin.client.js +32 -0
  53. package/dist/runtime/better-auth/vue/plugin.server.d.ts +24 -0
  54. package/dist/runtime/better-auth/vue/plugin.server.js +42 -0
  55. package/dist/runtime/better-auth/vue/use-auth.d.ts +92 -0
  56. package/dist/runtime/better-auth/vue/use-auth.js +82 -0
  57. package/dist/runtime/clerk/vue/index.d.ts +81 -0
  58. package/dist/runtime/clerk/vue/index.js +49 -0
  59. package/dist/runtime/devtools/bridge.d.ts +12 -0
  60. package/dist/runtime/devtools/bridge.js +191 -0
  61. package/dist/runtime/devtools/plugin.client.d.ts +8 -0
  62. package/dist/runtime/devtools/plugin.client.js +34 -0
  63. package/dist/runtime/devtools/types.d.ts +78 -0
  64. package/dist/runtime/devtools/types.js +0 -0
  65. package/dist/runtime/nuxt/composables/use-async-query.d.ts +144 -0
  66. package/dist/runtime/nuxt/composables/use-async-query.js +104 -0
  67. package/dist/runtime/nuxt/config.d.ts +7 -0
  68. package/dist/runtime/nuxt/config.js +12 -0
  69. package/dist/runtime/nuxt/index.d.ts +136 -0
  70. package/dist/runtime/nuxt/index.js +91 -0
  71. package/dist/runtime/polar/vue/components.d.ts +184 -0
  72. package/dist/runtime/polar/vue/components.js +115 -0
  73. package/dist/runtime/vue/auth/helpers.d.ts +67 -0
  74. package/dist/runtime/vue/auth/helpers.js +50 -0
  75. package/dist/runtime/vue/auth/index.d.ts +125 -0
  76. package/dist/runtime/vue/auth/index.js +97 -0
  77. package/dist/runtime/vue/client.d.ts +305 -0
  78. package/dist/runtime/vue/client.js +382 -0
  79. package/dist/runtime/vue/composables/use-action.d.ts +62 -0
  80. package/dist/runtime/vue/composables/use-action.js +13 -0
  81. package/dist/runtime/vue/composables/use-connection-state.d.ts +30 -0
  82. package/dist/runtime/vue/composables/use-connection-state.js +14 -0
  83. package/dist/runtime/vue/composables/use-mutation.d.ts +76 -0
  84. package/dist/runtime/vue/composables/use-mutation.js +34 -0
  85. package/dist/runtime/vue/composables/use-paginated-query.d.ts +132 -0
  86. package/dist/runtime/vue/composables/use-paginated-query.js +513 -0
  87. package/dist/runtime/vue/composables/use-queries.d.ts +62 -0
  88. package/dist/runtime/vue/composables/use-queries.js +37 -0
  89. package/dist/runtime/vue/composables/use-query.d.ts +87 -0
  90. package/dist/runtime/vue/composables/use-query.js +53 -0
  91. package/dist/runtime/vue/composables/use-storage-url.d.ts +46 -0
  92. package/dist/runtime/vue/composables/use-storage-url.js +9 -0
  93. package/dist/runtime/vue/composables/use-subscription.d.ts +15 -0
  94. package/dist/runtime/vue/composables/use-subscription.js +33 -0
  95. package/dist/runtime/vue/composables/use-upload-queue.d.ts +117 -0
  96. package/dist/runtime/vue/composables/use-upload-queue.js +115 -0
  97. package/dist/runtime/vue/composables/use-upload.d.ts +134 -0
  98. package/dist/runtime/vue/composables/use-upload.js +114 -0
  99. package/dist/runtime/vue/hydration.d.ts +66 -0
  100. package/dist/runtime/vue/hydration.js +46 -0
  101. package/dist/runtime/vue/index.d.ts +94 -0
  102. package/dist/runtime/vue/index.js +42 -0
  103. package/dist/runtime/vue/plugin.d.ts +18 -0
  104. package/dist/runtime/vue/plugin.js +16 -0
  105. package/dist/runtime/vue/provide.d.ts +39 -0
  106. package/dist/runtime/vue/provide.js +15 -0
  107. package/dist/runtime/vue/queries-observer.d.ts +41 -0
  108. package/dist/runtime/vue/queries-observer.js +126 -0
  109. package/dist/types.d.ts +3 -0
  110. package/package.json +233 -0
@@ -0,0 +1,189 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ /**
4
+ * Configuration for the opt-in Better Auth integration.
5
+ *
6
+ * @public
7
+ */
8
+ interface BetterAuthModuleOptions {
9
+ /**
10
+ * Path (relative to the Nuxt `rootDir`, or absolute) to a module that exports
11
+ * your configured Better Auth client as `authClient` — and, ideally, its type
12
+ * as `AuthClient`. Mirrors `convex/react`'s "you own the `authClient`" model:
13
+ * include whatever client plugins you need, e.g. `emailOTPClient()`,
14
+ * `passkeyClient()`, or `crossDomainClient()` from
15
+ * `@convex-dev/better-auth/client/plugins` for cross-domain auth. Defaults to
16
+ * a minimal bundled client carrying only `convexClient()` — the one plugin
17
+ * the integration itself requires.
18
+ *
19
+ * @example './app/convex-auth-client'
20
+ */
21
+ authClient?: string;
22
+ /**
23
+ * Restrict cross-domain one-time-token (`?ott=`) sign-in completion to one
24
+ * app route, e.g. `'/auth/callback'`. Only meaningful when your auth client
25
+ * installs `crossDomainClient()`. Upstream consumes the token on whatever
26
+ * page it lands on, and the protocol cannot bind it to the browser that
27
+ * started the flow — so a crafted `?ott=` link to any route silently signs
28
+ * the visitor into the link author's session (login CSRF). With this set,
29
+ * tokens are exchanged only on the given route and scrubbed everywhere
30
+ * else; point every sign-in `callbackURL` at it. Off by default, mirroring
31
+ * upstream `ConvexBetterAuthProvider`.
32
+ */
33
+ crossDomainCallbackRoute?: string;
34
+ }
35
+ interface ModuleOptions {
36
+ /** Convex deployment URL (defaults to `NUXT_PUBLIC_CONVEX_URL`). */
37
+ url?: string;
38
+ /** Convex `.site` URL (defaults to `NUXT_PUBLIC_CONVEX_SITE_URL`). */
39
+ siteUrl?: string;
40
+ /**
41
+ * Better Auth integration. Auto-enabled when `@convex-dev/better-auth` is
42
+ * installed; set `false` to force it off, `true` to require it, or a
43
+ * {@link BetterAuthModuleOptions} object to point at a custom auth client.
44
+ */
45
+ betterAuth?: boolean | BetterAuthModuleOptions;
46
+ /**
47
+ * Polar billing components. Auto-enabled when `@convex-dev/polar` is
48
+ * installed; set `false` to force it off (or `true` to require it).
49
+ */
50
+ polar?: boolean;
51
+ /**
52
+ * Clerk auth adapter (`provideConvexAuthFromClerk` / `<ConvexProviderWithClerk>`).
53
+ * Auto-enabled when `@clerk/vue` is installed; set `false` to force it off.
54
+ */
55
+ clerk?: boolean;
56
+ /**
57
+ * Auth0 auth adapter (`provideConvexAuthFromAuth0` / `<ConvexProviderWithAuth0>`).
58
+ * Auto-enabled when `@auth0/auth0-vue` is installed; set `false` to force it off.
59
+ */
60
+ auth0?: boolean;
61
+ /** Route the Better Auth same-origin proxy is mounted at. Defaults to `/api/auth`. */
62
+ authRoute?: string;
63
+ /**
64
+ * Convex panel in Nuxt DevTools (dev only): connection state, live query
65
+ * subscriptions, auth state, and client logs. Enabled by default whenever
66
+ * Nuxt DevTools is; set `false` to disable just the Convex tab.
67
+ */
68
+ devtools?: boolean;
69
+ }
70
+ declare module '@nuxt/schema' {
71
+ interface RuntimeConfig {
72
+ convex: {
73
+ siteUrl: string;
74
+ };
75
+ }
76
+ interface PublicRuntimeConfig {
77
+ convex: {
78
+ url: string;
79
+ siteUrl: string;
80
+ crossDomainCallbackRoute: string;
81
+ };
82
+ }
83
+ }
84
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
85
+
86
+ /**
87
+ * Which opt-in integrations ended up enabled — returned by
88
+ * {@link registerIntegrations} for the dev startup summary (and the DevTools
89
+ * panel, which reports the active adapter).
90
+ */
91
+ interface IntegrationFlags {
92
+ betterAuth: boolean;
93
+ clerk: boolean;
94
+ auth0: boolean;
95
+ polar: boolean;
96
+ }
97
+ /**
98
+ * Decide whether an opt-in integration is enabled, distinguishing the
99
+ * misconfiguration case: explicitly enabled but the backing package is not
100
+ * installed (`missingPackage`), where silently registering the runtime would
101
+ * surface as an opaque Vite import error instead of an actionable message.
102
+ * Auto-detection (option unset) treats package absence as the normal case.
103
+ */
104
+ declare function resolveIntegrationState(explicit: boolean | object | undefined, installed: boolean): {
105
+ enabled: boolean;
106
+ missingPackage: boolean;
107
+ };
108
+ /** Result of {@link validateModuleOptions}: findings plus normalized values. */
109
+ interface ModuleOptionDiagnostics {
110
+ errors: string[];
111
+ warnings: string[];
112
+ /** `authRoute` with a leading slash ensured and any trailing slash stripped. */
113
+ authRoute: string;
114
+ }
115
+ /**
116
+ * Validate the resolved module configuration, turning silent misconfiguration
117
+ * (swapped `.convex.cloud`/`.convex.site` URLs, malformed URLs, an `authRoute`
118
+ * that would produce a broken server-handler route, a `betterAuth.authClient`
119
+ * path that doesn't exist) into actionable messages. Pure — the caller logs
120
+ * the findings and applies the normalized `authRoute`. Exported for tests.
121
+ */
122
+ declare function validateModuleOptions(input: {
123
+ url: string;
124
+ siteUrl: string;
125
+ authRoute: string;
126
+ authClient?: string;
127
+ rootDir: string;
128
+ }): ModuleOptionDiagnostics;
129
+ /**
130
+ * The dev-mode one-line startup summary: resolved deployment URL, functions
131
+ * directory, and which opt-in integrations are active.
132
+ */
133
+ declare function formatStartupSummary(url: string, functionsDir: string, integrations: IntegrationFlags): string;
134
+ /**
135
+ * Build the ordered import-alias map for the Convex functions folder and its
136
+ * generated modules, so user code and server routes can `import from
137
+ * '#convex/...'` without spelling out `_generated`.
138
+ *
139
+ * - `#convex/api` -> _generated/api (`api`, `internal`, `components`)
140
+ * - `#convex/server` -> _generated/server (`query`, `mutation`, `action`, `*Ctx`, ...)
141
+ * - `#convex/dataModel` -> _generated/dataModel (`DataModel`, `Doc`, `Id`, `TableNames`)
142
+ * - `#convex/_generated` -> _generated (long form, covers every generated file)
143
+ * - `#convex` -> <rootDir>/<functionsDir>
144
+ *
145
+ * Order is significant: both Vite and Nitro resolve aliases with
146
+ * `@rollup/plugin-alias`, which is first-match-wins and treats `#convex` as a
147
+ * prefix of `#convex/api`. The specific generated-module aliases must come
148
+ * before the catch-all `#convex`, otherwise `#convex/api` would resolve to
149
+ * `<functionsDir>/api` instead of `<functionsDir>/_generated/api` (and would
150
+ * shadow any user function file literally named `api.ts` / `server.ts`).
151
+ */
152
+ declare function getConvexAliases(rootDir: string): Record<string, string>;
153
+ /**
154
+ * Contents of the fallback type template: placeholder (`any`-typed) ambient
155
+ * declarations for the generated `#convex/*` modules while `convex dev`
156
+ * hasn't emitted codegen yet, so a fresh project typechecks instead of failing
157
+ * on every `#convex/api` import. Once codegen exists the template goes empty
158
+ * and the real generated types win via the tsconfig `paths` the aliases
159
+ * already produce.
160
+ */
161
+ declare function convexTypeFallbackContents(hasApi: boolean, functionsDir: string): string;
162
+ /**
163
+ * CSP `connect-src` entries the browser needs to reach a Convex deployment: the
164
+ * deployment URL over both HTTPS and WebSocket (the realtime sync channel) and,
165
+ * when configured, the `.site` URL that serves Convex HTTP actions. Returns
166
+ * `[]` for empty or unparseable input.
167
+ */
168
+ declare function convexConnectSrc(url?: string, siteUrl?: string): string[];
169
+ /**
170
+ * CSP source for Convex-served resources (`img-src` / `media-src`): files
171
+ * uploaded through `useStorageUrl()` are served from the deployment origin.
172
+ */
173
+ declare function convexResourceSrc(url?: string): string[];
174
+ /**
175
+ * Apply nuxt-convex-module's secure-by-default, Convex-aware CSP onto a security config
176
+ * object (mutated in place). This tightens the directives we can safely
177
+ * pre-fill — locking network egress and Convex-served media to same-origin plus
178
+ * the Convex deployment — while leaving every other directive (script/style/
179
+ * font, etc.) to nuxt-security's own defaults.
180
+ *
181
+ * For each directive we keep any value the user already set and *append* the
182
+ * Convex origins, so consumers extend (add their own third parties) rather than
183
+ * fight the defaults, and the Convex origins are always present. A user who sets
184
+ * `contentSecurityPolicy: false` (CSP disabled) is left untouched.
185
+ */
186
+ declare function applyConvexSecurityDefaults(security: Record<string, unknown>, connectSrc: string[], resourceSrc: string[]): void;
187
+
188
+ export { applyConvexSecurityDefaults, convexConnectSrc, convexResourceSrc, convexTypeFallbackContents, _default as default, formatStartupSummary, getConvexAliases, resolveIntegrationState, validateModuleOptions };
189
+ export type { BetterAuthModuleOptions, IntegrationFlags, ModuleOptionDiagnostics, ModuleOptions };
package/dist/module.js ADDED
@@ -0,0 +1,489 @@
1
+ import { useLogger, defineNuxtModule, createResolver, addPlugin, addPluginTemplate, addTypeTemplate, updateTemplates, addImports, addComponent, addServerImports, addServerHandler, extendRouteRules, addRouteMiddleware } from '@nuxt/kit';
2
+ import { join, isAbsolute } from 'node:path';
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { createRequire } from 'node:module';
5
+
6
+ const logger$1 = useLogger("nuxt-convex-module");
7
+ const DEFAULT_FUNCTIONS_DIR = "convex";
8
+ function resolveFunctionsDir(rootDir) {
9
+ return readFunctionsDirFromConvexJson(join(rootDir, "convex.json")) ?? DEFAULT_FUNCTIONS_DIR;
10
+ }
11
+ function hasGeneratedApi(rootDir, functionsDir = resolveFunctionsDir(rootDir)) {
12
+ const generatedApi = join(rootDir, functionsDir, "_generated", "api");
13
+ return existsSync(`${generatedApi}.d.ts`) || existsSync(`${generatedApi}.js`);
14
+ }
15
+ function readFunctionsDirFromConvexJson(convexJsonPath) {
16
+ if (!existsSync(convexJsonPath)) {
17
+ return;
18
+ }
19
+ try {
20
+ const convexJson = JSON.parse(readFileSync(convexJsonPath, "utf-8"));
21
+ if (typeof convexJson.functions !== "string") {
22
+ return;
23
+ }
24
+ return normalizeFunctionsDir(convexJson.functions);
25
+ } catch (error) {
26
+ logger$1.warn(`Failed to parse convex.json: ${error instanceof Error ? error.message : String(error)}`);
27
+ }
28
+ }
29
+ function normalizeFunctionsDir(functionsDir) {
30
+ const normalized = functionsDir.replace(/^\.?\//, "").replace(/\/+$/, "");
31
+ return normalized || void 0;
32
+ }
33
+
34
+ const logger = useLogger("nuxt-convex-module");
35
+ const module$1 = defineNuxtModule({
36
+ meta: {
37
+ name: "nuxt-convex-module",
38
+ 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",
42
+ // `moduleDependencies` (below) is a Nuxt >= 4.1 feature — fail fast with a
43
+ // clear kit error on older Nuxt instead of silently skipping nuxt-security.
44
+ compatibility: {
45
+ nuxt: ">=4.1.0"
46
+ }
47
+ },
48
+ defaults: {
49
+ authRoute: "/api/auth",
50
+ devtools: true
51
+ },
52
+ // nuxt-security is an integral part of the integration: declaring it as a
53
+ // module dependency makes Nuxt's core loader install it (hoisting its types
54
+ // and deduping if you also register it yourself), and guarantees our setup
55
+ // runs *before* it — so the Convex-aware CSP defaults below land before
56
+ // nuxt-security reads its config. Configure it via the `security` key.
57
+ moduleDependencies: {
58
+ "nuxt-security": {}
59
+ },
60
+ async setup(options, nuxt) {
61
+ const resolver = createResolver(import.meta.url);
62
+ nuxt.options.build.transpile.push(resolver.resolve("./runtime"));
63
+ const { url, siteUrl } = applyRuntimeConfig(nuxt, options);
64
+ const diagnostics = validateModuleOptions({
65
+ url,
66
+ siteUrl,
67
+ authRoute: options.authRoute || "/api/auth",
68
+ authClient: typeof options.betterAuth === "object" ? options.betterAuth.authClient : void 0,
69
+ rootDir: nuxt.options.rootDir
70
+ });
71
+ for (const message of diagnostics.errors) logger.error(message);
72
+ for (const message of diagnostics.warnings) logger.warn(message);
73
+ options.authRoute = diagnostics.authRoute;
74
+ registerAuthClientAlias(resolver, nuxt, options);
75
+ registerConvexAliases(nuxt);
76
+ registerConvexApiPlugin(resolver, nuxt);
77
+ registerConvexTypeFallback(nuxt);
78
+ registerVueComposables(resolver);
79
+ registerAuthComponents(resolver);
80
+ registerServerImports(resolver);
81
+ const integrations = registerIntegrations(resolver, nuxt, options);
82
+ applyConvexCsp(nuxt, url, siteUrl);
83
+ watchConvexCodegen(nuxt);
84
+ if (nuxt.options.dev && options.devtools !== false && isDevtoolsUiEnabled(nuxt)) {
85
+ const { setupDevtools } = await import('./chunks/index.mjs');
86
+ setupDevtools(resolver, nuxt, {
87
+ url,
88
+ siteUrl,
89
+ rootDir: nuxt.options.rootDir,
90
+ functionsDir: resolveFunctionsDir(nuxt.options.rootDir),
91
+ integrations
92
+ });
93
+ addPlugin({ src: resolver.resolve("./runtime/devtools/plugin.client"), mode: "client" }, { append: true });
94
+ }
95
+ if (nuxt.options.dev && !nuxt.options._prepare) {
96
+ logger.info(formatStartupSummary(url, resolveFunctionsDir(nuxt.options.rootDir), integrations));
97
+ }
98
+ }
99
+ });
100
+ function isDevtoolsUiEnabled(nuxt) {
101
+ const devtools = nuxt.options.devtools;
102
+ return typeof devtools === "boolean" ? devtools : devtools?.enabled !== false;
103
+ }
104
+ function resolveIntegrationState(explicit, installed) {
105
+ if (explicit === false) return { enabled: false, missingPackage: false };
106
+ if (explicit === void 0) return { enabled: installed, missingPackage: false };
107
+ return installed ? { enabled: true, missingPackage: false } : { enabled: false, missingPackage: true };
108
+ }
109
+ function validateModuleOptions(input) {
110
+ const errors = [];
111
+ const warnings = [];
112
+ if (input.url && input.url.endsWith(".convex.site")) {
113
+ errors.push(
114
+ `\`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\`?`
115
+ );
116
+ } else if (input.url && !isHttpUrl(input.url)) {
117
+ warnings.push(
118
+ `\`convex.url\` ("${input.url}") does not look like a valid http(s) URL \u2014 Convex clients will fail to connect.`
119
+ );
120
+ }
121
+ if (input.siteUrl && input.siteUrl.endsWith(".convex.cloud")) {
122
+ warnings.push(
123
+ `\`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\`?`
124
+ );
125
+ } else if (input.siteUrl && !isHttpUrl(input.siteUrl)) {
126
+ warnings.push(
127
+ `\`convex.siteUrl\` ("${input.siteUrl}") does not look like a valid http(s) URL.`
128
+ );
129
+ }
130
+ let authRoute = input.authRoute;
131
+ if (!authRoute.startsWith("/")) {
132
+ authRoute = `/${authRoute}`;
133
+ warnings.push(
134
+ `\`convex.authRoute\` ("${input.authRoute}") must start with "/" \u2014 using "${authRoute}".`
135
+ );
136
+ }
137
+ if (authRoute.length > 1 && authRoute.endsWith("/")) {
138
+ authRoute = authRoute.replace(/\/+$/, "");
139
+ }
140
+ if (input.authClient && !authClientModuleExists(input.authClient, input.rootDir)) {
141
+ errors.push(
142
+ `\`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.`
143
+ );
144
+ }
145
+ return { errors, warnings, authRoute };
146
+ }
147
+ function isHttpUrl(value) {
148
+ if (!value.startsWith("http://") && !value.startsWith("https://")) {
149
+ return false;
150
+ }
151
+ try {
152
+ new URL(value);
153
+ return true;
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+ function authClientModuleExists(authClient, rootDir) {
159
+ const base = isAbsolute(authClient) ? authClient : join(rootDir, authClient);
160
+ return ["", ".ts", ".js", ".mts", ".mjs", "/index.ts", "/index.js"].some(
161
+ (suffix) => existsSync(`${base}${suffix}`)
162
+ );
163
+ }
164
+ function formatStartupSummary(url, functionsDir, integrations) {
165
+ const names = {
166
+ betterAuth: "better-auth",
167
+ clerk: "clerk",
168
+ auth0: "auth0",
169
+ polar: "polar"
170
+ };
171
+ const enabled = Object.keys(names).filter((key) => integrations[key]).map((key) => names[key]);
172
+ return `Convex ${url || "(no URL)"} \xB7 functions: ${functionsDir}/ \xB7 integrations: ${enabled.join(", ") || "none"}`;
173
+ }
174
+ function registerIntegrations(resolver, nuxt, options) {
175
+ const resolve = (key, pkg) => {
176
+ const state = resolveIntegrationState(options[key], isPackageInstalled(pkg, nuxt.options.rootDir));
177
+ if (state.missingPackage) {
178
+ logger.error(`\`convex.${key}\` is enabled but \`${pkg}\` is not installed. Run \`npm install ${pkg}\` or remove the option.`);
179
+ }
180
+ return state.enabled;
181
+ };
182
+ const betterAuth = resolve("betterAuth", "@convex-dev/better-auth");
183
+ if (betterAuth) {
184
+ registerBetterAuth(resolver, options.authRoute || "/api/auth");
185
+ } else {
186
+ registerBaseConvexClient(resolver);
187
+ }
188
+ const clerk = resolve("clerk", "@clerk/vue");
189
+ if (clerk) {
190
+ registerClerk(resolver);
191
+ }
192
+ const auth0 = resolve("auth0", "@auth0/auth0-vue");
193
+ if (auth0) {
194
+ registerAuth0(resolver);
195
+ }
196
+ const polar = resolve("polar", "@convex-dev/polar");
197
+ if (polar) {
198
+ registerPolarComponents(resolver);
199
+ }
200
+ return { betterAuth, clerk, auth0, polar };
201
+ }
202
+ function applyConvexCsp(nuxt, url, siteUrl) {
203
+ const opts = nuxt.options;
204
+ if (!url || opts.security === false) return;
205
+ const security = opts.security ??= {};
206
+ applyConvexSecurityDefaults(
207
+ security,
208
+ nuxt.options.dev ? [] : convexConnectSrc(url, siteUrl),
209
+ convexResourceSrc(url)
210
+ );
211
+ }
212
+ function isPackageInstalled(id, rootDir) {
213
+ for (const base of [join(rootDir, "package.json"), import.meta.url]) {
214
+ const require = createRequire(base);
215
+ for (const target of [id, `${id}/package.json`]) {
216
+ try {
217
+ require.resolve(target);
218
+ return true;
219
+ } catch {
220
+ }
221
+ }
222
+ }
223
+ return false;
224
+ }
225
+ function applyRuntimeConfig(nuxt, options) {
226
+ const url = nuxt.options._prepare ? void 0 : options.url || process.env.NUXT_PUBLIC_CONVEX_URL;
227
+ if (!url && !nuxt.options._prepare) {
228
+ logger.warn(
229
+ "No Convex deployment URL configured. Set NUXT_PUBLIC_CONVEX_URL or `convex.url` in nuxt.config. Note: `npx convex dev` writes CONVEX_URL to .env.local, which Nuxt does not load."
230
+ );
231
+ }
232
+ const siteUrl = options.siteUrl || process.env.NUXT_PUBLIC_CONVEX_SITE_URL || "";
233
+ const crossDomainCallbackRoute = typeof options.betterAuth === "object" && options.betterAuth.crossDomainCallbackRoute || "";
234
+ const runtimeConfig = nuxt.options.runtimeConfig;
235
+ runtimeConfig.public.convex = {
236
+ ...runtimeConfig.public.convex,
237
+ url: url || "",
238
+ siteUrl,
239
+ crossDomainCallbackRoute
240
+ };
241
+ runtimeConfig.convex = { ...runtimeConfig.convex, siteUrl };
242
+ return { url: url || "", siteUrl };
243
+ }
244
+ function getConvexAliases(rootDir) {
245
+ const functionsDir = resolveFunctionsDir(rootDir);
246
+ const convexDir = join(rootDir, functionsDir);
247
+ const generatedDir = join(convexDir, "_generated");
248
+ return {
249
+ "#convex/api": join(generatedDir, "api"),
250
+ "#convex/server": join(generatedDir, "server"),
251
+ "#convex/dataModel": join(generatedDir, "dataModel"),
252
+ "#convex/_generated": generatedDir,
253
+ "#convex": convexDir
254
+ };
255
+ }
256
+ function registerConvexAliases(nuxt) {
257
+ const aliases = getConvexAliases(nuxt.options.rootDir);
258
+ nuxt.options.nitro ||= {};
259
+ nuxt.options.nitro.alias ||= {};
260
+ for (const [alias, target] of Object.entries(aliases)) {
261
+ nuxt.options.alias[alias] = target;
262
+ nuxt.options.nitro.alias[alias] = target;
263
+ }
264
+ }
265
+ function registerAuthClientAlias(resolver, nuxt, options) {
266
+ const custom = typeof options.betterAuth === "object" ? options.betterAuth.authClient : void 0;
267
+ const target = custom ? isAbsolute(custom) ? custom : join(nuxt.options.rootDir, custom) : resolver.resolve("./runtime/better-auth/vue/client");
268
+ nuxt.options.nitro ||= {};
269
+ nuxt.options.nitro.alias ||= {};
270
+ nuxt.options.alias["#convex/auth-client"] = target;
271
+ nuxt.options.nitro.alias["#convex/auth-client"] = target;
272
+ }
273
+ function registerConvexApiPlugin(resolver, nuxt) {
274
+ const functionsDir = resolveFunctionsDir(nuxt.options.rootDir);
275
+ const provideModule = resolver.resolve("./runtime/vue/provide");
276
+ let notifiedMissing = false;
277
+ addPluginTemplate({
278
+ filename: "nuxt-convex-module-provide-api.mjs",
279
+ getContents: () => {
280
+ if (!hasGeneratedApi(nuxt.options.rootDir, functionsDir)) {
281
+ if (nuxt.options.dev && !nuxt.options._prepare && !notifiedMissing) {
282
+ notifiedMissing = true;
283
+ logger.info(`Convex codegen not found in \`${functionsDir}/_generated\` \u2014 run \`npx convex dev\`. Convex features no-op until it exists.`);
284
+ }
285
+ return "import { defineNuxtPlugin } from '#app'\nexport default defineNuxtPlugin(() => {})\n";
286
+ }
287
+ if (notifiedMissing) {
288
+ notifiedMissing = false;
289
+ logger.success("Convex codegen detected \u2014 generated `api` wired app-wide.");
290
+ }
291
+ return [
292
+ "import { defineNuxtPlugin } from '#app'",
293
+ "import { api } from '#convex/api'",
294
+ `import { provideConvexApi } from ${JSON.stringify(provideModule)}`,
295
+ "export default defineNuxtPlugin((nuxtApp) => {",
296
+ " provideConvexApi(api, nuxtApp.vueApp)",
297
+ "})",
298
+ ""
299
+ ].join("\n");
300
+ }
301
+ });
302
+ }
303
+ function convexTypeFallbackContents(hasApi, functionsDir) {
304
+ if (hasApi) {
305
+ return "export {}\n";
306
+ }
307
+ return [
308
+ `// Placeholder until \`npx convex dev\` generates ${functionsDir}/_generated.`,
309
+ "declare module '#convex/api' {",
310
+ " export const api: any",
311
+ " export const internal: any",
312
+ " export const components: any",
313
+ "}",
314
+ "declare module '#convex/server' {",
315
+ " export const query: any",
316
+ " export const internalQuery: any",
317
+ " export const mutation: any",
318
+ " export const internalMutation: any",
319
+ " export const action: any",
320
+ " export const internalAction: any",
321
+ " export const httpAction: any",
322
+ " export type QueryCtx = any",
323
+ " export type MutationCtx = any",
324
+ " export type ActionCtx = any",
325
+ " export type DatabaseReader = any",
326
+ " export type DatabaseWriter = any",
327
+ "}",
328
+ "declare module '#convex/dataModel' {",
329
+ " export type Doc<TableName extends string = string> = any",
330
+ " export type Id<TableName extends string = string> = string",
331
+ " export type DataModel = any",
332
+ " export type TableNames = string",
333
+ "}",
334
+ ""
335
+ ].join("\n");
336
+ }
337
+ function registerConvexTypeFallback(nuxt) {
338
+ const functionsDir = resolveFunctionsDir(nuxt.options.rootDir);
339
+ addTypeTemplate({
340
+ filename: "types/nuxt-convex-module-api-fallback.d.ts",
341
+ getContents: () => convexTypeFallbackContents(hasGeneratedApi(nuxt.options.rootDir, functionsDir), functionsDir)
342
+ }, { nuxt: true, nitro: true });
343
+ }
344
+ const CODEGEN_GUARDED_TEMPLATES = ["nuxt-convex-module-provide-api.mjs", "types/nuxt-convex-module-api-fallback.d.ts"];
345
+ function watchConvexCodegen(nuxt) {
346
+ if (!nuxt.options.dev) return;
347
+ nuxt.hook("builder:watch", async (_event, path) => {
348
+ if (!path.replace(/\\/g, "/").includes("_generated/api")) return;
349
+ await updateTemplates({ filter: (template) => CODEGEN_GUARDED_TEMPLATES.includes(template.filename) });
350
+ });
351
+ }
352
+ function registerVueComposables(resolver) {
353
+ const composables = [
354
+ { name: "useConvex", from: resolver.resolve("./runtime/vue/client") },
355
+ { name: "useQuery", from: resolver.resolve("./runtime/vue/composables/use-query") },
356
+ { name: "useQuery_experimental", from: resolver.resolve("./runtime/vue/composables/use-query") },
357
+ { name: "useConvexQuery", from: resolver.resolve("./runtime/vue/composables/use-query") },
358
+ { name: "useQueries", from: resolver.resolve("./runtime/vue/composables/use-queries") },
359
+ { name: "useConvexQueries", from: resolver.resolve("./runtime/vue/composables/use-queries") },
360
+ { name: "useMutation", from: resolver.resolve("./runtime/vue/composables/use-mutation") },
361
+ { name: "useConvexMutation", from: resolver.resolve("./runtime/vue/composables/use-mutation") },
362
+ { name: "useAction", from: resolver.resolve("./runtime/vue/composables/use-action") },
363
+ { name: "useConvexAction", from: resolver.resolve("./runtime/vue/composables/use-action") },
364
+ { name: "useConvexConnectionState", from: resolver.resolve("./runtime/vue/composables/use-connection-state") },
365
+ { name: "useUpload", from: resolver.resolve("./runtime/vue/composables/use-upload") },
366
+ { name: "useConvexUpload", from: resolver.resolve("./runtime/vue/composables/use-upload") },
367
+ { name: "uploadFile", from: resolver.resolve("./runtime/vue/composables/use-upload") },
368
+ { name: "useUploadQueue", from: resolver.resolve("./runtime/vue/composables/use-upload-queue") },
369
+ { name: "useConvexUploadQueue", from: resolver.resolve("./runtime/vue/composables/use-upload-queue") },
370
+ { name: "useStorageUrl", from: resolver.resolve("./runtime/vue/composables/use-storage-url") },
371
+ { name: "useConvexStorageUrl", from: resolver.resolve("./runtime/vue/composables/use-storage-url") },
372
+ { name: "useConvexAuth", from: resolver.resolve("./runtime/vue/auth/index") },
373
+ { name: "provideConvexAuth", from: resolver.resolve("./runtime/vue/auth/index") },
374
+ { name: "provideConvexApi", from: resolver.resolve("./runtime/vue/provide") },
375
+ { name: "useConvexApi", from: resolver.resolve("./runtime/vue/provide") },
376
+ { name: "useConvexNamespace", from: resolver.resolve("./runtime/vue/provide") },
377
+ { name: "usePreloadedQuery", from: resolver.resolve("./runtime/vue/hydration") },
378
+ // Nuxt-only (imports `#app`), hence under runtime/nuxt/ — see PARITY.md.
379
+ { name: "useAsyncQuery", from: resolver.resolve("./runtime/nuxt/composables/use-async-query") },
380
+ { name: "useConvexAsyncQuery", from: resolver.resolve("./runtime/nuxt/composables/use-async-query") },
381
+ { name: "usePaginatedQuery", from: resolver.resolve("./runtime/vue/composables/use-paginated-query") },
382
+ { name: "useConvexPaginatedQuery", from: resolver.resolve("./runtime/vue/composables/use-paginated-query") },
383
+ { name: "usePaginatedQuery_experimental", from: resolver.resolve("./runtime/vue/composables/use-paginated-query") }
384
+ ];
385
+ addImports(composables);
386
+ }
387
+ function registerAuthComponents(resolver) {
388
+ const helpersFile = resolver.resolve("./runtime/vue/auth/helpers");
389
+ for (const name of ["Authenticated", "Unauthenticated", "AuthLoading", "AuthRefreshing"]) {
390
+ addComponent({ name, filePath: helpersFile, export: name });
391
+ }
392
+ }
393
+ function registerServerImports(resolver) {
394
+ const fromNuxt = resolver.resolve("./runtime/nuxt/index");
395
+ const serverUtils = ["fetchQuery", "fetchMutation", "fetchAction", "preloadQuery", "preloadedQueryResult"];
396
+ addServerImports(serverUtils.map((name) => ({ name, from: fromNuxt })));
397
+ }
398
+ function registerBetterAuth(resolver, authRoute) {
399
+ addPlugin(resolver.resolve("./runtime/better-auth/vue/plugin.client"));
400
+ addPlugin(resolver.resolve("./runtime/better-auth/vue/plugin.server"));
401
+ addImports([
402
+ { name: "useAuth", from: resolver.resolve("./runtime/better-auth/vue/use-auth") },
403
+ { name: "usePreloadedAuthQuery", from: resolver.resolve("./runtime/better-auth/vue/hydration") }
404
+ ]);
405
+ addComponent({
406
+ name: "AuthBoundary",
407
+ filePath: resolver.resolve("./runtime/better-auth/vue/auth-boundary"),
408
+ export: "AuthBoundary"
409
+ });
410
+ addServerHandler({
411
+ route: `${authRoute}/**`,
412
+ handler: resolver.resolve("./runtime/better-auth/nuxt/proxy")
413
+ });
414
+ extendRouteRules(`${authRoute}/**`, { cache: false, prerender: false });
415
+ addRouteMiddleware({
416
+ name: "auth",
417
+ path: resolver.resolve("./runtime/better-auth/nuxt/middleware"),
418
+ global: false
419
+ });
420
+ addServerImports([
421
+ { name: "convexAuth", from: resolver.resolve("./runtime/better-auth/nuxt/server") },
422
+ { name: "convexBetterAuthNuxt", from: resolver.resolve("./runtime/better-auth/nuxt/server") }
423
+ ]);
424
+ }
425
+ function registerBaseConvexClient(resolver) {
426
+ addPlugin(resolver.resolve("./runtime/vue/plugin"));
427
+ }
428
+ function registerClerk(resolver) {
429
+ const from = resolver.resolve("./runtime/clerk/vue/index");
430
+ addImports({ name: "provideConvexAuthFromClerk", from });
431
+ addComponent({ name: "ConvexProviderWithClerk", filePath: from, export: "ConvexProviderWithClerk" });
432
+ }
433
+ function registerAuth0(resolver) {
434
+ const from = resolver.resolve("./runtime/auth0/vue/index");
435
+ addImports({ name: "provideConvexAuthFromAuth0", from });
436
+ addComponent({ name: "ConvexProviderWithAuth0", filePath: from, export: "ConvexProviderWithAuth0" });
437
+ }
438
+ function registerPolarComponents(resolver) {
439
+ const componentsFile = resolver.resolve("./runtime/polar/vue/components");
440
+ for (const name of ["CheckoutLink", "CustomerPortalLink"]) {
441
+ addComponent({ name, filePath: componentsFile, export: name });
442
+ }
443
+ }
444
+ function toHttpOrigin(raw) {
445
+ if (!raw) return void 0;
446
+ try {
447
+ return new URL(raw).origin;
448
+ } catch {
449
+ return void 0;
450
+ }
451
+ }
452
+ function toWsOrigin(raw) {
453
+ if (!raw) return void 0;
454
+ try {
455
+ const u = new URL(raw);
456
+ return `${u.protocol === "https:" ? "wss:" : "ws:"}//${u.host}`;
457
+ } catch {
458
+ return void 0;
459
+ }
460
+ }
461
+ function uniq(values) {
462
+ return Array.from(new Set(values.filter((v) => Boolean(v))));
463
+ }
464
+ function convexConnectSrc(url, siteUrl) {
465
+ return uniq([toHttpOrigin(url), toWsOrigin(url), toHttpOrigin(siteUrl), toWsOrigin(siteUrl)]);
466
+ }
467
+ function convexResourceSrc(url) {
468
+ return uniq([toHttpOrigin(url)]);
469
+ }
470
+ function applyConvexSecurityDefaults(security, connectSrc, resourceSrc) {
471
+ if (security.headers === false) return;
472
+ if (typeof security.headers !== "object" || security.headers === null) security.headers = {};
473
+ const headers = security.headers;
474
+ if (headers.contentSecurityPolicy === false) return;
475
+ if (typeof headers.contentSecurityPolicy !== "object" || headers.contentSecurityPolicy === null) {
476
+ headers.contentSecurityPolicy = {};
477
+ }
478
+ const csp = headers.contentSecurityPolicy;
479
+ tightenDirective(csp, "connect-src", ["'self'"], connectSrc);
480
+ tightenDirective(csp, "img-src", ["'self'", "data:"], resourceSrc);
481
+ tightenDirective(csp, "media-src", ["'self'"], resourceSrc);
482
+ }
483
+ function tightenDirective(csp, name, baseline, additions) {
484
+ if (additions.length === 0) return;
485
+ const existing = Array.isArray(csp[name]) ? csp[name] : baseline;
486
+ csp[name] = uniq([...existing, ...additions]);
487
+ }
488
+
489
+ export { applyConvexSecurityDefaults, convexConnectSrc, convexResourceSrc, convexTypeFallbackContents, module$1 as default, formatStartupSummary, getConvexAliases, resolveIntegrationState, validateModuleOptions };