nuxt-convex 0.0.1-alpha.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 +508 -0
- package/dist/module.d.mts +17 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +181 -0
- package/dist/runtime/composables/useConvexStorage.d.ts +28 -0
- package/dist/runtime/composables/useConvexStorage.js +34 -0
- package/dist/runtime/composables/useConvexUpload.d.ts +32 -0
- package/dist/runtime/composables/useConvexUpload.js +45 -0
- package/dist/runtime/plugin.client.d.ts +2 -0
- package/dist/runtime/plugin.client.js +13 -0
- package/dist/types.d.mts +9 -0
- package/package.json +86 -0
package/README.md
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/onmax/nuxt-convex/main/.github/og.webp" alt="Nuxt Convex" width="100%">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">nuxt-convex</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">Nuxt module for <a href="https://convex.dev">Convex</a> - reactive backend with real-time sync, file storage, and auto-imports.</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://npmjs.com/package/nuxt-convex"><img src="https://img.shields.io/npm/v/nuxt-convex/latest.svg?style=flat&colorA=020420&colorB=00DC82" alt="npm version"></a>
|
|
11
|
+
<a href="https://npm.chart.dev/nuxt-convex"><img src="https://img.shields.io/npm/dm/nuxt-convex.svg?style=flat&colorA=020420&colorB=00DC82" alt="npm downloads"></a>
|
|
12
|
+
<a href="https://npmjs.com/package/nuxt-convex"><img src="https://img.shields.io/npm/l/nuxt-convex.svg?style=flat&colorA=020420&colorB=00DC82" alt="License"></a>
|
|
13
|
+
<a href="https://nuxt.com"><img src="https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js" alt="Nuxt"></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
> **[Live Demo](https://nuxt-convex-playground.workers.dev)** · [Convex Docs](https://docs.convex.dev)
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Real-time by default** - Queries auto-update when data changes
|
|
21
|
+
- **Virtual modules** - Import from `#convex`, `#convex/api`, and `#convex/storage`
|
|
22
|
+
- **Auto-imports** - Composables available without manual imports
|
|
23
|
+
- **File storage** - Upload API with auto-scaffolded Convex functions
|
|
24
|
+
- **DevTools** - Convex dashboard tab in Nuxt DevTools
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
- Nuxt 3.0.0 or higher
|
|
29
|
+
- A Convex project (run `npx convex dev` to create one)
|
|
30
|
+
|
|
31
|
+
## Setup
|
|
32
|
+
|
|
33
|
+
Install the module and its peer dependencies:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pnpm add nuxt-convex convex @convex-vue/core
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Add the module to your Nuxt configuration:
|
|
40
|
+
|
|
41
|
+
```ts [nuxt.config.ts]
|
|
42
|
+
export default defineNuxtConfig({
|
|
43
|
+
modules: ['nuxt-convex'],
|
|
44
|
+
convex: {
|
|
45
|
+
storage: true, // Enable file storage (optional)
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The module reads `CONVEX_URL` or `NUXT_PUBLIC_CONVEX_URL` environment variables automatically. Run `npx convex dev` to start the Convex development server.
|
|
51
|
+
|
|
52
|
+
## Creating Your Backend
|
|
53
|
+
|
|
54
|
+
Convex functions live in the `convex/` directory at your project root. See [Convex docs](https://docs.convex.dev/functions) for full reference.
|
|
55
|
+
|
|
56
|
+
### Schema
|
|
57
|
+
|
|
58
|
+
Define your database tables with [schemas](https://docs.convex.dev/database/schemas). Tables are created automatically when you push to Convex.
|
|
59
|
+
|
|
60
|
+
```ts [convex/schema.ts]
|
|
61
|
+
import { defineSchema, defineTable } from 'convex/server'
|
|
62
|
+
import { v } from 'convex/values'
|
|
63
|
+
|
|
64
|
+
export default defineSchema({
|
|
65
|
+
tasks: defineTable({
|
|
66
|
+
title: v.string(),
|
|
67
|
+
userId: v.optional(v.string()),
|
|
68
|
+
isCompleted: v.boolean(),
|
|
69
|
+
createdAt: v.number(),
|
|
70
|
+
}).index('by_user', ['userId']),
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The `v` object provides [validators](https://docs.convex.dev/database/schemas#validators) for all Convex types: `v.string()`, `v.number()`, `v.boolean()`, `v.id('tableName')`, `v.array()`, `v.object()`, `v.optional()`, `v.union()`, etc.
|
|
75
|
+
|
|
76
|
+
### Queries
|
|
77
|
+
|
|
78
|
+
[Queries](https://docs.convex.dev/functions/query-functions) read data from the database. They are reactive - the UI updates automatically when underlying data changes.
|
|
79
|
+
|
|
80
|
+
```ts [convex/tasks.ts]
|
|
81
|
+
import { v } from 'convex/values'
|
|
82
|
+
import { query } from './_generated/server'
|
|
83
|
+
|
|
84
|
+
export const list = query({
|
|
85
|
+
args: { userId: v.string() },
|
|
86
|
+
handler: async (ctx, { userId }) => {
|
|
87
|
+
return await ctx.db
|
|
88
|
+
.query('tasks')
|
|
89
|
+
.withIndex('by_user', q => q.eq('userId', userId))
|
|
90
|
+
.order('desc')
|
|
91
|
+
.collect()
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
export const get = query({
|
|
96
|
+
args: { id: v.id('tasks') },
|
|
97
|
+
handler: async (ctx, { id }) => {
|
|
98
|
+
return await ctx.db.get(id)
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Mutations
|
|
104
|
+
|
|
105
|
+
[Mutations](https://docs.convex.dev/functions/mutation-functions) write data to the database. They are transactional and run on the server.
|
|
106
|
+
|
|
107
|
+
```ts [convex/tasks.ts]
|
|
108
|
+
import { v } from 'convex/values'
|
|
109
|
+
import { mutation } from './_generated/server'
|
|
110
|
+
|
|
111
|
+
export const add = mutation({
|
|
112
|
+
args: { title: v.string(), userId: v.string() },
|
|
113
|
+
handler: async (ctx, { title, userId }) => {
|
|
114
|
+
return await ctx.db.insert('tasks', {
|
|
115
|
+
title,
|
|
116
|
+
userId,
|
|
117
|
+
isCompleted: false,
|
|
118
|
+
createdAt: Date.now(),
|
|
119
|
+
})
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
export const toggle = mutation({
|
|
124
|
+
args: { id: v.id('tasks') },
|
|
125
|
+
handler: async (ctx, { id }) => {
|
|
126
|
+
const task = await ctx.db.get(id)
|
|
127
|
+
if (task) {
|
|
128
|
+
await ctx.db.patch(id, { isCompleted: !task.isCompleted })
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
export const remove = mutation({
|
|
134
|
+
args: { id: v.id('tasks') },
|
|
135
|
+
handler: async (ctx, { id }) => {
|
|
136
|
+
await ctx.db.delete(id)
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Actions
|
|
142
|
+
|
|
143
|
+
[Actions](https://docs.convex.dev/functions/actions) run arbitrary code including external API calls, but cannot directly access the database. Use them for third-party integrations.
|
|
144
|
+
|
|
145
|
+
```ts [convex/ai.ts]
|
|
146
|
+
import { v } from 'convex/values'
|
|
147
|
+
import { action } from './_generated/server'
|
|
148
|
+
|
|
149
|
+
export const summarize = action({
|
|
150
|
+
args: { text: v.string() },
|
|
151
|
+
handler: async (ctx, { text }) => {
|
|
152
|
+
const response = await fetch('https://api.openai.com/v1/...', {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
|
|
155
|
+
body: JSON.stringify({ prompt: text }),
|
|
156
|
+
})
|
|
157
|
+
return await response.json()
|
|
158
|
+
},
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Actions can call mutations/queries via `ctx.runMutation()` and `ctx.runQuery()`.
|
|
163
|
+
|
|
164
|
+
## Usage
|
|
165
|
+
|
|
166
|
+
Composables are auto-imported. They wrap [@convex-vue/core](https://github.com/niconiahi/convex-vue).
|
|
167
|
+
|
|
168
|
+
### useConvexQuery
|
|
169
|
+
|
|
170
|
+
Subscribe to a [query](https://docs.convex.dev/functions/query-functions). Returns reactive data that auto-updates when the database changes.
|
|
171
|
+
|
|
172
|
+
```vue [app/pages/tasks.vue]
|
|
173
|
+
<script setup lang="ts">
|
|
174
|
+
import { api } from '#convex/api'
|
|
175
|
+
|
|
176
|
+
const { data: tasks, isLoading, error } = useConvexQuery(api.tasks.list, { userId: 'user_123' })
|
|
177
|
+
</script>
|
|
178
|
+
|
|
179
|
+
<template>
|
|
180
|
+
<div v-if="isLoading">
|
|
181
|
+
Loading...
|
|
182
|
+
</div>
|
|
183
|
+
<div v-else-if="error">
|
|
184
|
+
Error: {{ error.message }}
|
|
185
|
+
</div>
|
|
186
|
+
<ul v-else>
|
|
187
|
+
<li v-for="task in tasks" :key="task._id">
|
|
188
|
+
{{ task.title }}
|
|
189
|
+
</li>
|
|
190
|
+
</ul>
|
|
191
|
+
</template>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Return values:**
|
|
195
|
+
|
|
196
|
+
| Property | Type | Description |
|
|
197
|
+
| ----------- | --------------------- | ----------------------- |
|
|
198
|
+
| `data` | `Ref<T \| undefined>` | Query result, reactive |
|
|
199
|
+
| `isLoading` | `Ref<boolean>` | True while initial load |
|
|
200
|
+
| `error` | `Ref<Error \| null>` | Error if query failed |
|
|
201
|
+
|
|
202
|
+
### useConvexMutation
|
|
203
|
+
|
|
204
|
+
Call a [mutation](https://docs.convex.dev/functions/mutation-functions) to write data.
|
|
205
|
+
|
|
206
|
+
```vue [app/components/AddTask.vue]
|
|
207
|
+
<script setup lang="ts">
|
|
208
|
+
import { api } from '#convex/api'
|
|
209
|
+
|
|
210
|
+
const { mutate: addTask, isLoading, error } = useConvexMutation(api.tasks.add)
|
|
211
|
+
|
|
212
|
+
const title = ref('')
|
|
213
|
+
|
|
214
|
+
async function handleSubmit() {
|
|
215
|
+
await addTask({ title: title.value, userId: 'user_123' })
|
|
216
|
+
title.value = ''
|
|
217
|
+
}
|
|
218
|
+
</script>
|
|
219
|
+
|
|
220
|
+
<template>
|
|
221
|
+
<form @submit.prevent="handleSubmit">
|
|
222
|
+
<input v-model="title" :disabled="isLoading" placeholder="New task...">
|
|
223
|
+
<button :disabled="isLoading">
|
|
224
|
+
Add
|
|
225
|
+
</button>
|
|
226
|
+
<p v-if="error">
|
|
227
|
+
{{ error.message }}
|
|
228
|
+
</p>
|
|
229
|
+
</form>
|
|
230
|
+
</template>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Return values:**
|
|
234
|
+
|
|
235
|
+
| Property | Type | Description |
|
|
236
|
+
| ----------- | ---------------------- | ------------------------ |
|
|
237
|
+
| `mutate` | `(args) => Promise<T>` | Call the mutation |
|
|
238
|
+
| `isLoading` | `Ref<boolean>` | True while running |
|
|
239
|
+
| `error` | `Ref<Error \| null>` | Error if mutation failed |
|
|
240
|
+
|
|
241
|
+
### useConvexAction
|
|
242
|
+
|
|
243
|
+
Call an [action](https://docs.convex.dev/functions/actions) for external API calls or long-running tasks.
|
|
244
|
+
|
|
245
|
+
```vue [app/components/Summarize.vue]
|
|
246
|
+
<script setup lang="ts">
|
|
247
|
+
import { api } from '#convex/api'
|
|
248
|
+
|
|
249
|
+
const { execute: summarize, isLoading, error } = useConvexAction(api.ai.summarize)
|
|
250
|
+
|
|
251
|
+
const result = ref('')
|
|
252
|
+
|
|
253
|
+
async function handleSummarize(text: string) {
|
|
254
|
+
result.value = await summarize({ text })
|
|
255
|
+
}
|
|
256
|
+
</script>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Return values:**
|
|
260
|
+
|
|
261
|
+
| Property | Type | Description |
|
|
262
|
+
| ----------- | ---------------------- | ---------------------- |
|
|
263
|
+
| `execute` | `(args) => Promise<T>` | Call the action |
|
|
264
|
+
| `isLoading` | `Ref<boolean>` | True while running |
|
|
265
|
+
| `error` | `Ref<Error \| null>` | Error if action failed |
|
|
266
|
+
|
|
267
|
+
### useConvex
|
|
268
|
+
|
|
269
|
+
Get the raw [ConvexClient](https://docs.convex.dev/api/classes/browser.ConvexClient) for advanced usage.
|
|
270
|
+
|
|
271
|
+
```vue [app/components/Advanced.vue]
|
|
272
|
+
<script setup lang="ts">
|
|
273
|
+
import { api } from '#convex/api'
|
|
274
|
+
|
|
275
|
+
const client = useConvex()
|
|
276
|
+
|
|
277
|
+
// Direct client access for advanced patterns
|
|
278
|
+
const result = await client.query(api.tasks.get, { id: 'abc123' })
|
|
279
|
+
</script>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## File Storage
|
|
283
|
+
|
|
284
|
+
Convex provides built-in [file storage](https://docs.convex.dev/file-storage). Enable it in your config to auto-scaffold the required functions.
|
|
285
|
+
|
|
286
|
+
### Setup
|
|
287
|
+
|
|
288
|
+
```ts [nuxt.config.ts]
|
|
289
|
+
export default defineNuxtConfig({
|
|
290
|
+
modules: ['nuxt-convex'],
|
|
291
|
+
convex: {
|
|
292
|
+
storage: true,
|
|
293
|
+
},
|
|
294
|
+
})
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
This creates `convex/_hub/storage.ts` with `generateUploadUrl`, `getUrl`, and `remove` functions. Customize as needed.
|
|
298
|
+
|
|
299
|
+
Add the `uploads` table to your schema:
|
|
300
|
+
|
|
301
|
+
```ts [convex/schema.ts]
|
|
302
|
+
import { defineSchema, defineTable } from 'convex/server'
|
|
303
|
+
import { v } from 'convex/values'
|
|
304
|
+
|
|
305
|
+
export default defineSchema({
|
|
306
|
+
uploads: defineTable({
|
|
307
|
+
storageId: v.id('_storage'),
|
|
308
|
+
name: v.string(),
|
|
309
|
+
type: v.string(),
|
|
310
|
+
url: v.optional(v.string()),
|
|
311
|
+
userId: v.string(),
|
|
312
|
+
createdAt: v.number(),
|
|
313
|
+
}).index('by_user', ['userId']),
|
|
314
|
+
})
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### useConvexStorage
|
|
318
|
+
|
|
319
|
+
Low-level composable for storage operations. Auto-imported when storage is enabled.
|
|
320
|
+
|
|
321
|
+
```vue [app/components/Storage.vue]
|
|
322
|
+
<script setup lang="ts">
|
|
323
|
+
import { api } from '#convex/api'
|
|
324
|
+
|
|
325
|
+
const { generateUploadUrl, getUrl, remove } = useConvexStorage(api)
|
|
326
|
+
|
|
327
|
+
// Get a reactive URL for a stored file
|
|
328
|
+
const fileUrl = getUrl('storage_id_here')
|
|
329
|
+
|
|
330
|
+
// Generate upload URL for direct upload
|
|
331
|
+
const uploadUrl = await generateUploadUrl.mutate()
|
|
332
|
+
|
|
333
|
+
// Delete a file
|
|
334
|
+
await remove.mutate({ storageId: 'storage_id_here' })
|
|
335
|
+
</script>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Return values:**
|
|
339
|
+
|
|
340
|
+
| Property | Type | Description |
|
|
341
|
+
| ------------------- | -------------------------------------------- | --------------------- |
|
|
342
|
+
| `generateUploadUrl` | `{ mutate: () => Promise<string> }` | Get URL for uploading |
|
|
343
|
+
| `getUrl` | `(storageId: string) => Ref<string \| null>` | Reactive file URL |
|
|
344
|
+
| `remove` | `{ mutate: (args) => Promise<void> }` | Delete a file |
|
|
345
|
+
|
|
346
|
+
### useConvexUpload
|
|
347
|
+
|
|
348
|
+
High-level composable for file uploads with progress tracking. Auto-imported.
|
|
349
|
+
|
|
350
|
+
```vue [app/pages/upload.vue]
|
|
351
|
+
<script setup lang="ts">
|
|
352
|
+
import { api } from '#convex/api'
|
|
353
|
+
|
|
354
|
+
const { generateUploadUrl } = useConvexStorage(api)
|
|
355
|
+
const saveFile = useConvexMutation(api._hub.storage.saveFile)
|
|
356
|
+
|
|
357
|
+
const { upload, isUploading, progress, error } = useConvexUpload({
|
|
358
|
+
generateUploadUrl,
|
|
359
|
+
onSuccess: async (storageId, file) => {
|
|
360
|
+
await saveFile.mutate({
|
|
361
|
+
storageId,
|
|
362
|
+
name: file.name,
|
|
363
|
+
type: file.type,
|
|
364
|
+
userId: 'user_123',
|
|
365
|
+
})
|
|
366
|
+
},
|
|
367
|
+
onError: err => console.error('Upload failed:', err),
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
async function handleFileChange(event: Event) {
|
|
371
|
+
const file = (event.target as HTMLInputElement).files?.[0]
|
|
372
|
+
if (file) {
|
|
373
|
+
const storageId = await upload(file)
|
|
374
|
+
console.log('Uploaded:', storageId)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
</script>
|
|
378
|
+
|
|
379
|
+
<template>
|
|
380
|
+
<div>
|
|
381
|
+
<input type="file" :disabled="isUploading" @change="handleFileChange">
|
|
382
|
+
<p v-if="isUploading">
|
|
383
|
+
Uploading... {{ progress }}%
|
|
384
|
+
</p>
|
|
385
|
+
<p v-if="error">
|
|
386
|
+
{{ error.message }}
|
|
387
|
+
</p>
|
|
388
|
+
</div>
|
|
389
|
+
</template>
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Options:**
|
|
393
|
+
|
|
394
|
+
| Option | Type | Description |
|
|
395
|
+
| ------------------- | ----------------------------------------- | --------------------------------- |
|
|
396
|
+
| `generateUploadUrl` | `{ mutate: () => Promise<string> }` | Required. From `useConvexStorage` |
|
|
397
|
+
| `onSuccess` | `(storageId: string, file: File) => void` | Called after successful upload |
|
|
398
|
+
| `onError` | `(error: Error) => void` | Called on upload error |
|
|
399
|
+
|
|
400
|
+
**Return values:**
|
|
401
|
+
|
|
402
|
+
| Property | Type | Description |
|
|
403
|
+
| ------------- | ----------------------------------------- | -------------------------------- |
|
|
404
|
+
| `upload` | `(file: File) => Promise<string \| null>` | Upload a file, returns storageId |
|
|
405
|
+
| `isUploading` | `Ref<boolean>` | True while uploading |
|
|
406
|
+
| `progress` | `Ref<number>` | Upload progress 0-100 |
|
|
407
|
+
| `error` | `Ref<Error \| null>` | Error if upload failed |
|
|
408
|
+
|
|
409
|
+
## Configuration
|
|
410
|
+
|
|
411
|
+
| Option | Type | Default | Description |
|
|
412
|
+
| --------- | --------- | ------------------------ | ------------------------------- |
|
|
413
|
+
| `url` | `string` | `process.env.CONVEX_URL` | Convex deployment URL |
|
|
414
|
+
| `storage` | `boolean` | `false` | Enable file storage integration |
|
|
415
|
+
|
|
416
|
+
**Environment variables:**
|
|
417
|
+
|
|
418
|
+
| Variable | Description |
|
|
419
|
+
| ------------------------ | ----------------------------------------------- |
|
|
420
|
+
| `CONVEX_URL` | Convex deployment URL (set by `npx convex dev`) |
|
|
421
|
+
| `NUXT_PUBLIC_CONVEX_URL` | Alternative, follows Nuxt convention |
|
|
422
|
+
|
|
423
|
+
## DevTools
|
|
424
|
+
|
|
425
|
+
When running in development, the module adds a **Convex** tab to Nuxt DevTools with an embedded Convex dashboard. Access your data, run functions, and view logs directly from DevTools.
|
|
426
|
+
|
|
427
|
+
## Better Auth Integration
|
|
428
|
+
|
|
429
|
+
Use [nuxt-better-auth](https://nuxt-better-auth.onmax.me/) for authentication alongside Convex for your app data.
|
|
430
|
+
|
|
431
|
+
### 1. Install dependencies
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
pnpm add @onmax/nuxt-better-auth
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### 2. Configure nuxt-better-auth
|
|
438
|
+
|
|
439
|
+
```ts [nuxt.config.ts]
|
|
440
|
+
export default defineNuxtConfig({
|
|
441
|
+
modules: ['nuxt-convex', '@onmax/nuxt-better-auth'],
|
|
442
|
+
convex: { storage: true },
|
|
443
|
+
})
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### 3. Create server auth config
|
|
447
|
+
|
|
448
|
+
```ts [server/auth.config.ts]
|
|
449
|
+
import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
|
|
450
|
+
|
|
451
|
+
export default defineServerAuth(() => ({
|
|
452
|
+
emailAndPassword: { enabled: true },
|
|
453
|
+
}))
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
See [nuxt-better-auth docs](https://nuxt-better-auth.onmax.me/) for database adapters, social providers, and plugins.
|
|
457
|
+
|
|
458
|
+
### Using Convex as Auth Database (Advanced)
|
|
459
|
+
|
|
460
|
+
To store auth data directly in Convex instead of a SQL database:
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
pnpm add @convex-dev/better-auth better-auth --save-exact
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
```ts [convex/convex.config.ts]
|
|
467
|
+
import betterAuth from '@convex-dev/better-auth/convex.config'
|
|
468
|
+
import { defineApp } from 'convex/server'
|
|
469
|
+
|
|
470
|
+
const app = defineApp()
|
|
471
|
+
app.use(betterAuth)
|
|
472
|
+
export default app
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
```ts [server/auth.config.ts]
|
|
476
|
+
import { components } from '#convex/api'
|
|
477
|
+
import { createClient } from '@convex-dev/better-auth'
|
|
478
|
+
import { convex } from '@convex-dev/better-auth/server/plugins'
|
|
479
|
+
import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
|
|
480
|
+
|
|
481
|
+
const authComponent = createClient(components.betterAuth)
|
|
482
|
+
|
|
483
|
+
export default defineServerAuth(() => ({
|
|
484
|
+
database: authComponent.adapter(),
|
|
485
|
+
plugins: [convex()],
|
|
486
|
+
emailAndPassword: { enabled: true },
|
|
487
|
+
}))
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
This stores users, sessions, and accounts in Convex. See [@convex-dev/better-auth](https://github.com/get-convex/convex-better-auth) for details.
|
|
491
|
+
|
|
492
|
+
## Development
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
pnpm install
|
|
496
|
+
pnpm dev:prepare
|
|
497
|
+
pnpm dev
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Run tests:
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
pnpm test
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
## License
|
|
507
|
+
|
|
508
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
|
|
3
|
+
interface ConvexConfig {
|
|
4
|
+
/** Convex deployment URL. Auto-detected from CONVEX_URL or NUXT_PUBLIC_CONVEX_URL env vars */
|
|
5
|
+
url?: string;
|
|
6
|
+
/** Enable file storage integration. When true, scaffolds convex/_hub/storage.ts */
|
|
7
|
+
storage?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface ResolvedConvexConfig {
|
|
10
|
+
url: string;
|
|
11
|
+
storage: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare const _default: _nuxt_schema.NuxtModule<ConvexConfig, ConvexConfig, false>;
|
|
15
|
+
|
|
16
|
+
export { _default as default };
|
|
17
|
+
export type { ConvexConfig, ResolvedConvexConfig };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
import { defineNuxtModule, createResolver, addPlugin, addImports, addTemplate, addTypeTemplate } from '@nuxt/kit';
|
|
5
|
+
import { consola } from 'consola';
|
|
6
|
+
import { defu } from 'defu';
|
|
7
|
+
import { join } from 'pathe';
|
|
8
|
+
|
|
9
|
+
const version = "0.0.1-alpha.0";
|
|
10
|
+
|
|
11
|
+
const log = consola.withTag("nuxt:convex");
|
|
12
|
+
const module$1 = defineNuxtModule({
|
|
13
|
+
meta: {
|
|
14
|
+
name: "nuxt-convex",
|
|
15
|
+
version,
|
|
16
|
+
configKey: "convex",
|
|
17
|
+
compatibility: { nuxt: ">=3.0.0" }
|
|
18
|
+
},
|
|
19
|
+
defaults: {
|
|
20
|
+
storage: false
|
|
21
|
+
},
|
|
22
|
+
async onInstall(nuxt) {
|
|
23
|
+
const options = nuxt.options.convex;
|
|
24
|
+
if (options?.storage) {
|
|
25
|
+
await scaffoldStorageFunctions(nuxt);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
async setup(options, nuxt) {
|
|
29
|
+
const { resolve } = createResolver(import.meta.url);
|
|
30
|
+
const config = resolveConfig(options);
|
|
31
|
+
nuxt.options.runtimeConfig.public.convex = defu(
|
|
32
|
+
nuxt.options.runtimeConfig.public.convex,
|
|
33
|
+
{ url: config.url }
|
|
34
|
+
);
|
|
35
|
+
addPlugin({ src: resolve("./runtime/plugin.client"), mode: "client" });
|
|
36
|
+
addImports({ name: "useConvexUpload", from: resolve("./runtime/composables/useConvexUpload") });
|
|
37
|
+
setupConvexApiAlias(nuxt);
|
|
38
|
+
setupConvexClient(nuxt);
|
|
39
|
+
if (config.storage) {
|
|
40
|
+
await setupConvexStorage(nuxt, resolve);
|
|
41
|
+
}
|
|
42
|
+
if (nuxt.options.dev) {
|
|
43
|
+
addConvexDevTools(nuxt, config);
|
|
44
|
+
}
|
|
45
|
+
nuxt.hook("ready", () => {
|
|
46
|
+
if (config.url) {
|
|
47
|
+
log.info(`Convex connected: ${config.url}`);
|
|
48
|
+
} else {
|
|
49
|
+
log.warn("Convex URL not configured. Set CONVEX_URL or NUXT_PUBLIC_CONVEX_URL");
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
function resolveConfig(options) {
|
|
55
|
+
const url = options.url || process.env.CONVEX_URL || process.env.NUXT_PUBLIC_CONVEX_URL || "";
|
|
56
|
+
return defu(options, { url, storage: false });
|
|
57
|
+
}
|
|
58
|
+
function setupConvexClient(nuxt, _resolve) {
|
|
59
|
+
const template = addTemplate({
|
|
60
|
+
filename: "convex/client.mjs",
|
|
61
|
+
getContents: () => `// Auto-generated by nuxt-convex
|
|
62
|
+
import { inject } from 'vue'
|
|
63
|
+
import { CONVEX_INJECTION_KEY } from '@convex-vue/core'
|
|
64
|
+
export { useConvexQuery, useConvexMutation, useConvexAction } from '@convex-vue/core'
|
|
65
|
+
|
|
66
|
+
// Get the Convex client - MUST be called during component setup
|
|
67
|
+
export function useConvex() {
|
|
68
|
+
const client = inject(CONVEX_INJECTION_KEY)
|
|
69
|
+
if (!client) throw new Error('[nuxt-convex] Convex client not found. Is the plugin installed?')
|
|
70
|
+
return client
|
|
71
|
+
}
|
|
72
|
+
`,
|
|
73
|
+
write: true
|
|
74
|
+
});
|
|
75
|
+
addTypeTemplate({
|
|
76
|
+
filename: "types/convex.d.ts",
|
|
77
|
+
getContents: () => `// Auto-generated by nuxt-convex
|
|
78
|
+
import type { ConvexClient } from 'convex/browser'
|
|
79
|
+
declare module '#convex' {
|
|
80
|
+
export { useConvexQuery, useConvexMutation, useConvexAction } from '@convex-vue/core'
|
|
81
|
+
export function useConvex(): ConvexClient
|
|
82
|
+
}
|
|
83
|
+
`
|
|
84
|
+
}, { nitro: true, nuxt: true });
|
|
85
|
+
nuxt.options.alias["#convex"] = template.dst;
|
|
86
|
+
addImports([
|
|
87
|
+
{ name: "useConvexQuery", from: "#convex" },
|
|
88
|
+
{ name: "useConvexMutation", from: "#convex" },
|
|
89
|
+
{ name: "useConvexAction", from: "#convex" },
|
|
90
|
+
{ name: "useConvex", from: "#convex" }
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
function setupConvexApiAlias(nuxt) {
|
|
94
|
+
const generatedDir = join(nuxt.options.rootDir, "convex/_generated");
|
|
95
|
+
if (!existsSync(generatedDir)) {
|
|
96
|
+
log.warn("convex/_generated not found. Run `npx convex dev` to generate types.");
|
|
97
|
+
}
|
|
98
|
+
nuxt.options.alias["#convex/api"] = join(generatedDir, "api");
|
|
99
|
+
addTypeTemplate({
|
|
100
|
+
filename: "types/convex-api.d.ts",
|
|
101
|
+
getContents: () => `// Auto-generated by nuxt-convex
|
|
102
|
+
declare module '#convex/api' {
|
|
103
|
+
export * from '../../convex/_generated/api'
|
|
104
|
+
}
|
|
105
|
+
`
|
|
106
|
+
}, { nitro: true, nuxt: true });
|
|
107
|
+
}
|
|
108
|
+
async function setupConvexStorage(nuxt, resolve) {
|
|
109
|
+
const template = addTemplate({
|
|
110
|
+
filename: "convex/storage.mjs",
|
|
111
|
+
getContents: () => `// Auto-generated by nuxt-convex
|
|
112
|
+
export { useConvexStorage } from '${resolve("./runtime/composables/useConvexStorage")}'
|
|
113
|
+
`,
|
|
114
|
+
write: true
|
|
115
|
+
});
|
|
116
|
+
addTypeTemplate({
|
|
117
|
+
filename: "types/convex-storage.d.ts",
|
|
118
|
+
getContents: () => `// Auto-generated by nuxt-convex
|
|
119
|
+
declare module '#convex/storage' {
|
|
120
|
+
import type { Ref } from 'vue'
|
|
121
|
+
|
|
122
|
+
export interface ConvexStorageReturn {
|
|
123
|
+
generateUploadUrl: () => Promise<string>
|
|
124
|
+
getUrl: (storageId: string) => Ref<string | null>
|
|
125
|
+
remove: (storageId: string) => Promise<void>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function useConvexStorage(): ConvexStorageReturn
|
|
129
|
+
}
|
|
130
|
+
`
|
|
131
|
+
}, { nitro: true, nuxt: true });
|
|
132
|
+
nuxt.options.alias["#convex/storage"] = template.dst;
|
|
133
|
+
addImports({ name: "useConvexStorage", from: "#convex/storage" });
|
|
134
|
+
await scaffoldStorageFunctions(nuxt);
|
|
135
|
+
}
|
|
136
|
+
async function scaffoldStorageFunctions(nuxt) {
|
|
137
|
+
const storageDir = join(nuxt.options.rootDir, "convex", "_hub");
|
|
138
|
+
const storagePath = join(storageDir, "storage.ts");
|
|
139
|
+
if (existsSync(storagePath))
|
|
140
|
+
return;
|
|
141
|
+
await mkdir(storageDir, { recursive: true });
|
|
142
|
+
await writeFile(storagePath, `// Auto-generated by nuxt-convex - feel free to customize
|
|
143
|
+
import { mutation, query } from '../_generated/server'
|
|
144
|
+
import { v } from 'convex/values'
|
|
145
|
+
|
|
146
|
+
export const generateUploadUrl = mutation(async (ctx) => {
|
|
147
|
+
return await ctx.storage.generateUploadUrl()
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
export const getUrl = query({
|
|
151
|
+
args: { storageId: v.id('_storage') },
|
|
152
|
+
handler: async (ctx, { storageId }) => {
|
|
153
|
+
return await ctx.storage.getUrl(storageId)
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
export const remove = mutation({
|
|
158
|
+
args: { storageId: v.id('_storage') },
|
|
159
|
+
handler: async (ctx, { storageId }) => {
|
|
160
|
+
await ctx.storage.delete(storageId)
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
`);
|
|
164
|
+
log.success("Created convex/_hub/storage.ts");
|
|
165
|
+
}
|
|
166
|
+
function addConvexDevTools(nuxt, config) {
|
|
167
|
+
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
168
|
+
if (!config.url)
|
|
169
|
+
return;
|
|
170
|
+
const dashboardUrl = config.url.replace(".convex.cloud", ".convex.dev");
|
|
171
|
+
tabs.push({
|
|
172
|
+
category: "server",
|
|
173
|
+
name: "convex",
|
|
174
|
+
title: "Convex",
|
|
175
|
+
icon: "i-simple-icons-convex",
|
|
176
|
+
view: { type: "iframe", src: dashboardUrl }
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Ref } from '#imports';
|
|
2
|
+
import type { FunctionReference } from 'convex/server';
|
|
3
|
+
export interface ConvexStorageApi {
|
|
4
|
+
_hub?: {
|
|
5
|
+
storage?: {
|
|
6
|
+
generateUploadUrl: FunctionReference<'mutation'>;
|
|
7
|
+
getUrl: FunctionReference<'query'>;
|
|
8
|
+
remove: FunctionReference<'mutation'>;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface ConvexStorageReturn {
|
|
13
|
+
generateUploadUrl: {
|
|
14
|
+
mutate: () => Promise<string>;
|
|
15
|
+
};
|
|
16
|
+
getUrl: (storageId: string) => Ref<string | null>;
|
|
17
|
+
remove: {
|
|
18
|
+
mutate: (args: {
|
|
19
|
+
storageId: string;
|
|
20
|
+
}) => Promise<void>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Composable for Convex file storage operations.
|
|
25
|
+
* MUST be called during component setup (not in onMounted/callbacks).
|
|
26
|
+
* @param api - The Convex API from `#convex/api`
|
|
27
|
+
*/
|
|
28
|
+
export declare function useConvexStorage(api: ConvexStorageApi): ConvexStorageReturn;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { inject, readonly, ref } from "#imports";
|
|
2
|
+
import { CONVEX_INJECTION_KEY, useConvexQuery } from "@convex-vue/core";
|
|
3
|
+
export function useConvexStorage(api) {
|
|
4
|
+
const storage = api?._hub?.storage;
|
|
5
|
+
if (!storage) {
|
|
6
|
+
console.warn("[nuxt-convex] Storage API not found. Ensure convex/_hub/storage.ts exists and `npx convex dev` is running.");
|
|
7
|
+
}
|
|
8
|
+
const client = inject(CONVEX_INJECTION_KEY);
|
|
9
|
+
if (!client) {
|
|
10
|
+
console.warn("[nuxt-convex] Convex client not found. Is the plugin installed?");
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
generateUploadUrl: {
|
|
14
|
+
async mutate() {
|
|
15
|
+
if (!client || !storage?.generateUploadUrl)
|
|
16
|
+
throw new Error("[nuxt-convex] Storage API not configured");
|
|
17
|
+
return await client.mutation(storage.generateUploadUrl, {});
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
getUrl: (storageId) => {
|
|
21
|
+
if (!storage?.getUrl)
|
|
22
|
+
return readonly(ref(null));
|
|
23
|
+
const { data } = useConvexQuery(storage.getUrl, { storageId });
|
|
24
|
+
return readonly(data);
|
|
25
|
+
},
|
|
26
|
+
remove: {
|
|
27
|
+
async mutate(args) {
|
|
28
|
+
if (!client || !storage?.remove)
|
|
29
|
+
throw new Error("[nuxt-convex] Storage API not configured");
|
|
30
|
+
return await client.mutation(storage.remove, args);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { DeepReadonly, Ref } from 'vue';
|
|
2
|
+
export interface UseConvexUploadOptions {
|
|
3
|
+
/** Mutation to generate upload URL (from useConvexStorage or custom) */
|
|
4
|
+
generateUploadUrl: {
|
|
5
|
+
mutate: (args?: any) => Promise<string | undefined>;
|
|
6
|
+
};
|
|
7
|
+
/** Called on successful upload with storageId and file */
|
|
8
|
+
onSuccess?: (storageId: string, file: File) => void;
|
|
9
|
+
/** Called on upload error */
|
|
10
|
+
onError?: (error: Error) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface UseConvexUploadReturn {
|
|
13
|
+
upload: (file: File) => Promise<string | null>;
|
|
14
|
+
isUploading: DeepReadonly<Ref<boolean>>;
|
|
15
|
+
progress: DeepReadonly<Ref<number>>;
|
|
16
|
+
error: DeepReadonly<Ref<Error | null>>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Composable for uploading files to Convex storage.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const { generateUploadUrl } = useConvexStorage()
|
|
24
|
+
* const { upload, isUploading, error } = useConvexUpload({
|
|
25
|
+
* generateUploadUrl,
|
|
26
|
+
* onSuccess: (id) => console.log('Uploaded:', id)
|
|
27
|
+
* })
|
|
28
|
+
*
|
|
29
|
+
* const handleFile = (file: File) => upload(file)
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function useConvexUpload(options: UseConvexUploadOptions): UseConvexUploadReturn;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readonly, ref } from "#imports";
|
|
2
|
+
export function useConvexUpload(options) {
|
|
3
|
+
const _isUploading = ref(false);
|
|
4
|
+
const _progress = ref(0);
|
|
5
|
+
const _error = ref(null);
|
|
6
|
+
async function upload(file) {
|
|
7
|
+
if (import.meta.server) {
|
|
8
|
+
console.warn("[nuxt-convex] useConvexUpload can only be used on client-side");
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
_isUploading.value = true;
|
|
12
|
+
_progress.value = 0;
|
|
13
|
+
_error.value = null;
|
|
14
|
+
try {
|
|
15
|
+
const uploadUrl = await options.generateUploadUrl.mutate({});
|
|
16
|
+
if (!uploadUrl)
|
|
17
|
+
throw new Error("[nuxt-convex] Failed to generate upload URL");
|
|
18
|
+
const response = await fetch(uploadUrl, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: { "Content-Type": file.type },
|
|
21
|
+
body: file
|
|
22
|
+
});
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
throw new Error(`[nuxt-convex] Upload failed: ${response.statusText}`);
|
|
25
|
+
}
|
|
26
|
+
const { storageId } = await response.json();
|
|
27
|
+
_progress.value = 100;
|
|
28
|
+
options.onSuccess?.(storageId, file);
|
|
29
|
+
return storageId;
|
|
30
|
+
} catch (e) {
|
|
31
|
+
const error = e;
|
|
32
|
+
_error.value = error;
|
|
33
|
+
options.onError?.(error);
|
|
34
|
+
return null;
|
|
35
|
+
} finally {
|
|
36
|
+
_isUploading.value = false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
upload,
|
|
41
|
+
isUploading: readonly(_isUploading),
|
|
42
|
+
progress: readonly(_progress),
|
|
43
|
+
error: readonly(_error)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useRuntimeConfig } from "#imports";
|
|
2
|
+
import { createConvexVue } from "@convex-vue/core";
|
|
3
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
4
|
+
const config = useRuntimeConfig();
|
|
5
|
+
const convexUrl = config.public.convex?.url;
|
|
6
|
+
if (!convexUrl) {
|
|
7
|
+
console.warn("[nuxt-convex] No Convex URL configured. Set CONVEX_URL env var or run `npx convex dev`");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const convex = createConvexVue({ convexUrl });
|
|
11
|
+
nuxtApp.vueApp.use(convex);
|
|
12
|
+
return { provide: { convex } };
|
|
13
|
+
});
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NuxtModule } from '@nuxt/schema'
|
|
2
|
+
|
|
3
|
+
import type { default as Module } from './module.mjs'
|
|
4
|
+
|
|
5
|
+
export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
|
6
|
+
|
|
7
|
+
export { default } from './module.mjs'
|
|
8
|
+
|
|
9
|
+
export { type ConvexConfig, type ResolvedConvexConfig } from './module.mjs'
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nuxt-convex",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1-alpha.0",
|
|
5
|
+
"packageManager": "pnpm@10.15.1",
|
|
6
|
+
"description": "Nuxt module for Convex - reactive backend with real-time sync, file storage, and auto-imports",
|
|
7
|
+
"author": "onmax",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://github.com/onmax/nuxt-convex#readme",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/onmax/nuxt-convex.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/onmax/nuxt-convex/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"nuxt",
|
|
19
|
+
"nuxt-module",
|
|
20
|
+
"convex",
|
|
21
|
+
"realtime",
|
|
22
|
+
"database",
|
|
23
|
+
"storage",
|
|
24
|
+
"vue"
|
|
25
|
+
],
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/types.d.mts",
|
|
29
|
+
"import": "./dist/module.mjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/module.mjs",
|
|
33
|
+
"types": "./dist/types.d.mts",
|
|
34
|
+
"typesVersions": {
|
|
35
|
+
"*": {
|
|
36
|
+
".": [
|
|
37
|
+
"./dist/types.d.mts"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"prepack": "nuxt-module-build build",
|
|
46
|
+
"dev": "nuxi dev playground",
|
|
47
|
+
"dev:build": "nuxi build playground",
|
|
48
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare",
|
|
49
|
+
"playground:prepare": "nuxi prepare playground",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"typecheck": "nuxt-module-build build",
|
|
53
|
+
"lint": "eslint .",
|
|
54
|
+
"lint:fix": "eslint . --fix",
|
|
55
|
+
"release": "bumpp && git push --follow-tags"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@convex-vue/core": ">=0.0.4",
|
|
59
|
+
"convex": ">=1.0.0"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@nuxt/kit": "catalog:prod",
|
|
63
|
+
"consola": "catalog:prod",
|
|
64
|
+
"defu": "catalog:prod",
|
|
65
|
+
"pathe": "catalog:prod"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@antfu/eslint-config": "catalog:lint",
|
|
69
|
+
"@convex-vue/core": "catalog:convex",
|
|
70
|
+
"@nuxt/devtools": "catalog:nuxt",
|
|
71
|
+
"@nuxt/eslint-config": "catalog:lint",
|
|
72
|
+
"@nuxt/module-builder": "catalog:build",
|
|
73
|
+
"@nuxt/test-utils": "catalog:test",
|
|
74
|
+
"@types/node": "catalog:types",
|
|
75
|
+
"bumpp": "^10.3.2",
|
|
76
|
+
"convex": "catalog:convex",
|
|
77
|
+
"eslint": "catalog:lint",
|
|
78
|
+
"eslint-plugin-format": "catalog:lint",
|
|
79
|
+
"nuxt": "catalog:nuxt",
|
|
80
|
+
"typescript": "catalog:build",
|
|
81
|
+
"vitest": "catalog:test",
|
|
82
|
+
"vitest-package-exports": "catalog:test",
|
|
83
|
+
"vue": "catalog:nuxt",
|
|
84
|
+
"yaml": "catalog:"
|
|
85
|
+
}
|
|
86
|
+
}
|