convex-verify 0.1.0 → 1.0.5
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 +123 -65
- package/dist/core/index.d.mts +39 -9
- package/dist/core/index.d.ts +39 -9
- package/dist/core/index.js +11 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +11 -2
- package/dist/core/index.mjs.map +1 -1
- package/dist/index.js +14 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14 -10
- package/dist/index.mjs.map +1 -1
- package/dist/plugins/index.js +3 -8
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/index.mjs +3 -8
- package/dist/plugins/index.mjs.map +1 -1
- package/dist/transforms/index.js.map +1 -1
- package/dist/transforms/index.mjs.map +1 -1
- package/dist/utils/index.js +3 -8
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +3 -8
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +86 -74
package/README.md
CHANGED
|
@@ -13,12 +13,12 @@ Type-safe verification and validation for Convex database operations.
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
|
|
16
|
+
pnpm install convex-verify
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
**Peer Dependencies:**
|
|
20
20
|
|
|
21
|
-
- `convex` >= 1.
|
|
21
|
+
- `convex` >= 1.31.3
|
|
22
22
|
|
|
23
23
|
## Quick Start
|
|
24
24
|
|
|
@@ -29,43 +29,46 @@ import {
|
|
|
29
29
|
uniqueColumnConfig,
|
|
30
30
|
uniqueRowConfig,
|
|
31
31
|
verifyConfig,
|
|
32
|
-
} from
|
|
32
|
+
} from "convex-verify";
|
|
33
33
|
|
|
34
|
-
import schema from
|
|
34
|
+
import schema from "./schema";
|
|
35
35
|
|
|
36
36
|
export const { insert, patch, dangerouslyPatch } = verifyConfig(schema, {
|
|
37
37
|
// Make fields optional with defaults
|
|
38
38
|
defaultValues: defaultValuesConfig(schema, () => ({
|
|
39
|
-
posts: { status:
|
|
39
|
+
posts: { status: "draft", views: 0 },
|
|
40
40
|
})),
|
|
41
41
|
|
|
42
42
|
// Prevent patching critical fields
|
|
43
43
|
protectedColumns: protectedColumnsConfig(schema, {
|
|
44
|
-
posts: [
|
|
44
|
+
posts: ["authorId"],
|
|
45
45
|
}),
|
|
46
46
|
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
// Enforce unique row combinations
|
|
48
|
+
uniqueRow: uniqueRowConfig(schema, {
|
|
49
|
+
posts: ["by_author_slug"],
|
|
50
|
+
}),
|
|
51
|
+
|
|
52
|
+
// Enforce unique column values
|
|
53
|
+
uniqueColumn: uniqueColumnConfig(schema, {
|
|
54
|
+
users: ["by_email", "by_username"],
|
|
55
|
+
}),
|
|
56
|
+
|
|
57
|
+
// Custom/third-party plugins (optional)
|
|
58
|
+
plugins: [],
|
|
56
59
|
});
|
|
57
60
|
```
|
|
58
61
|
|
|
59
62
|
Then use in your mutations:
|
|
60
63
|
|
|
61
64
|
```ts
|
|
62
|
-
import { insert, patch } from
|
|
65
|
+
import { insert, patch } from "./verify";
|
|
63
66
|
|
|
64
67
|
export const createPost = mutation({
|
|
65
68
|
args: { title: v.string(), content: v.string() },
|
|
66
69
|
handler: async (ctx, args) => {
|
|
67
70
|
// status and views are optional - defaults are applied
|
|
68
|
-
return await insert(ctx,
|
|
71
|
+
return await insert(ctx, "posts", {
|
|
69
72
|
title: args.title,
|
|
70
73
|
content: args.content,
|
|
71
74
|
authorId: ctx.auth.userId,
|
|
@@ -74,10 +77,10 @@ export const createPost = mutation({
|
|
|
74
77
|
});
|
|
75
78
|
|
|
76
79
|
export const updatePost = mutation({
|
|
77
|
-
args: { id: v.id(
|
|
80
|
+
args: { id: v.id("posts"), title: v.string() },
|
|
78
81
|
handler: async (ctx, args) => {
|
|
79
82
|
// authorId is protected - TypeScript won't allow it here
|
|
80
|
-
await patch(ctx,
|
|
83
|
+
await patch(ctx, "posts", args.id, {
|
|
81
84
|
title: args.title,
|
|
82
85
|
});
|
|
83
86
|
},
|
|
@@ -94,8 +97,15 @@ Main configuration function that returns typed `insert`, `patch`, and `dangerous
|
|
|
94
97
|
|
|
95
98
|
```ts
|
|
96
99
|
const { insert, patch, dangerouslyPatch, configs } = verifyConfig(schema, {
|
|
100
|
+
// Type-affecting configs
|
|
97
101
|
defaultValues?: DefaultValuesConfig,
|
|
98
102
|
protectedColumns?: ProtectedColumnsConfig,
|
|
103
|
+
|
|
104
|
+
// Built-in validation configs
|
|
105
|
+
uniqueRow?: UniqueRowConfig,
|
|
106
|
+
uniqueColumn?: UniqueColumnConfig,
|
|
107
|
+
|
|
108
|
+
// Custom/third-party plugins
|
|
99
109
|
plugins?: ValidatePlugin[],
|
|
100
110
|
});
|
|
101
111
|
```
|
|
@@ -120,16 +130,16 @@ Transforms modify the input type of `insert()`.
|
|
|
120
130
|
Makes specified fields optional in `insert()` by providing default values.
|
|
121
131
|
|
|
122
132
|
```ts
|
|
123
|
-
import { defaultValuesConfig } from
|
|
133
|
+
import { defaultValuesConfig } from "convex-verify";
|
|
124
134
|
// or
|
|
125
|
-
import { defaultValuesConfig } from
|
|
135
|
+
import { defaultValuesConfig } from "convex-verify/transforms";
|
|
126
136
|
```
|
|
127
137
|
|
|
128
138
|
#### Static Config
|
|
129
139
|
|
|
130
140
|
```ts
|
|
131
141
|
const defaults = defaultValuesConfig(schema, {
|
|
132
|
-
posts: { status:
|
|
142
|
+
posts: { status: "draft", views: 0 },
|
|
133
143
|
comments: { likes: 0 },
|
|
134
144
|
});
|
|
135
145
|
```
|
|
@@ -141,7 +151,7 @@ Use a function for values that should be generated fresh on each insert:
|
|
|
141
151
|
```ts
|
|
142
152
|
const defaults = defaultValuesConfig(schema, () => ({
|
|
143
153
|
posts: {
|
|
144
|
-
status:
|
|
154
|
+
status: "draft",
|
|
145
155
|
slug: generateRandomSlug(),
|
|
146
156
|
createdAt: Date.now(),
|
|
147
157
|
},
|
|
@@ -169,17 +179,17 @@ Configs modify the input type of `patch()`.
|
|
|
169
179
|
Removes specified columns from the `patch()` input type, preventing accidental updates.
|
|
170
180
|
|
|
171
181
|
```ts
|
|
172
|
-
import { protectedColumnsConfig } from
|
|
182
|
+
import { protectedColumnsConfig } from "convex-verify";
|
|
173
183
|
// or
|
|
174
|
-
import { protectedColumnsConfig } from
|
|
184
|
+
import { protectedColumnsConfig } from "convex-verify/configs";
|
|
175
185
|
```
|
|
176
186
|
|
|
177
187
|
#### Example
|
|
178
188
|
|
|
179
189
|
```ts
|
|
180
190
|
const protected = protectedColumnsConfig(schema, {
|
|
181
|
-
posts: [
|
|
182
|
-
comments: [
|
|
191
|
+
posts: ["authorId", "createdAt"],
|
|
192
|
+
comments: ["postId", "authorId"],
|
|
183
193
|
});
|
|
184
194
|
```
|
|
185
195
|
|
|
@@ -189,15 +199,15 @@ Use `dangerouslyPatch()` when you need to update protected columns:
|
|
|
189
199
|
|
|
190
200
|
```ts
|
|
191
201
|
// Regular patch - authorId not allowed
|
|
192
|
-
await patch(ctx,
|
|
202
|
+
await patch(ctx, "posts", id, {
|
|
193
203
|
authorId: newAuthorId, // ❌ TypeScript error
|
|
194
|
-
title:
|
|
204
|
+
title: "New Title", // ✅ OK
|
|
195
205
|
});
|
|
196
206
|
|
|
197
207
|
// Dangerous patch - full access
|
|
198
|
-
await dangerouslyPatch(ctx,
|
|
208
|
+
await dangerouslyPatch(ctx, "posts", id, {
|
|
199
209
|
authorId: newAuthorId, // ✅ OK (bypasses protection)
|
|
200
|
-
title:
|
|
210
|
+
title: "New Title",
|
|
201
211
|
});
|
|
202
212
|
```
|
|
203
213
|
|
|
@@ -205,26 +215,46 @@ await dangerouslyPatch(ctx, 'posts', id, {
|
|
|
205
215
|
|
|
206
216
|
---
|
|
207
217
|
|
|
208
|
-
##
|
|
218
|
+
## Validation
|
|
209
219
|
|
|
210
|
-
|
|
220
|
+
Validation configs check data during `insert()` and `patch()` operations. They run after transforms and can throw errors to prevent the operation.
|
|
211
221
|
|
|
212
222
|
### `uniqueRowConfig(schema, config)`
|
|
213
223
|
|
|
214
224
|
Enforces uniqueness across multiple columns using composite indexes.
|
|
215
225
|
|
|
216
226
|
```ts
|
|
217
|
-
import { uniqueRowConfig } from
|
|
227
|
+
import { uniqueRowConfig } from "convex-verify";
|
|
218
228
|
// or
|
|
219
|
-
import { uniqueRowConfig } from
|
|
229
|
+
import { uniqueRowConfig } from "convex-verify/plugins";
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### Usage
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
// As a named config key (recommended)
|
|
236
|
+
verifyConfig(schema, {
|
|
237
|
+
uniqueRow: uniqueRowConfig(schema, {
|
|
238
|
+
posts: ["by_author_slug"],
|
|
239
|
+
}),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Or in the plugins array
|
|
243
|
+
verifyConfig(schema, {
|
|
244
|
+
plugins: [
|
|
245
|
+
uniqueRowConfig(schema, {
|
|
246
|
+
posts: ["by_author_slug"],
|
|
247
|
+
}),
|
|
248
|
+
],
|
|
249
|
+
});
|
|
220
250
|
```
|
|
221
251
|
|
|
222
252
|
#### Shorthand (Index Names)
|
|
223
253
|
|
|
224
254
|
```ts
|
|
225
255
|
const uniqueRows = uniqueRowConfig(schema, {
|
|
226
|
-
posts: [
|
|
227
|
-
projects: [
|
|
256
|
+
posts: ["by_author_slug"], // Unique author + slug combo
|
|
257
|
+
projects: ["by_org_slug"], // Unique org + slug combo
|
|
228
258
|
});
|
|
229
259
|
```
|
|
230
260
|
|
|
@@ -234,8 +264,8 @@ const uniqueRows = uniqueRowConfig(schema, {
|
|
|
234
264
|
const uniqueRows = uniqueRowConfig(schema, {
|
|
235
265
|
posts: [
|
|
236
266
|
{
|
|
237
|
-
index:
|
|
238
|
-
identifiers: [
|
|
267
|
+
index: "by_author_slug",
|
|
268
|
+
identifiers: ["_id", "authorId"], // Fields to check for "same document"
|
|
239
269
|
},
|
|
240
270
|
],
|
|
241
271
|
});
|
|
@@ -246,9 +276,29 @@ const uniqueRows = uniqueRowConfig(schema, {
|
|
|
246
276
|
Enforces uniqueness on single columns using indexes.
|
|
247
277
|
|
|
248
278
|
```ts
|
|
249
|
-
import { uniqueColumnConfig } from
|
|
279
|
+
import { uniqueColumnConfig } from "convex-verify";
|
|
250
280
|
// or
|
|
251
|
-
import { uniqueColumnConfig } from
|
|
281
|
+
import { uniqueColumnConfig } from "convex-verify/plugins";
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Usage
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
// As a named config key (recommended)
|
|
288
|
+
verifyConfig(schema, {
|
|
289
|
+
uniqueColumn: uniqueColumnConfig(schema, {
|
|
290
|
+
users: ["by_email", "by_username"],
|
|
291
|
+
}),
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Or in the plugins array
|
|
295
|
+
verifyConfig(schema, {
|
|
296
|
+
plugins: [
|
|
297
|
+
uniqueColumnConfig(schema, {
|
|
298
|
+
users: ["by_email", "by_username"],
|
|
299
|
+
}),
|
|
300
|
+
],
|
|
301
|
+
});
|
|
252
302
|
```
|
|
253
303
|
|
|
254
304
|
The column name is derived from the index name by removing `by_` prefix:
|
|
@@ -256,12 +306,12 @@ The column name is derived from the index name by removing `by_` prefix:
|
|
|
256
306
|
- `by_username` → checks `username` column
|
|
257
307
|
- `by_email` → checks `email` column
|
|
258
308
|
|
|
259
|
-
####
|
|
309
|
+
#### Shorthand (Index Names)
|
|
260
310
|
|
|
261
311
|
```ts
|
|
262
312
|
const uniqueColumns = uniqueColumnConfig(schema, {
|
|
263
|
-
users: [
|
|
264
|
-
organizations: [
|
|
313
|
+
users: ["by_username", "by_email"],
|
|
314
|
+
organizations: ["by_slug"],
|
|
265
315
|
});
|
|
266
316
|
```
|
|
267
317
|
|
|
@@ -270,38 +320,44 @@ const uniqueColumns = uniqueColumnConfig(schema, {
|
|
|
270
320
|
```ts
|
|
271
321
|
const uniqueColumns = uniqueColumnConfig(schema, {
|
|
272
322
|
users: [
|
|
273
|
-
|
|
274
|
-
{ index:
|
|
323
|
+
"by_username", // shorthand
|
|
324
|
+
{ index: "by_email", identifiers: ["_id", "clerkId"] }, // with options
|
|
275
325
|
],
|
|
276
326
|
});
|
|
277
327
|
```
|
|
278
328
|
|
|
329
|
+
## Custom Plugins
|
|
330
|
+
|
|
331
|
+
The `plugins` array accepts custom validation plugins for extensibility.
|
|
332
|
+
|
|
279
333
|
### `createValidatePlugin(name, config, handlers)`
|
|
280
334
|
|
|
281
335
|
Create custom validation plugins.
|
|
282
336
|
|
|
283
337
|
```ts
|
|
284
|
-
import { createValidatePlugin } from
|
|
338
|
+
import { createValidatePlugin } from "convex-verify";
|
|
285
339
|
// or
|
|
286
|
-
import { createValidatePlugin } from
|
|
340
|
+
import { createValidatePlugin } from "convex-verify/core";
|
|
287
341
|
```
|
|
288
342
|
|
|
289
343
|
#### Example: Required Fields Plugin
|
|
290
344
|
|
|
291
345
|
```ts
|
|
292
346
|
const requiredFields = createValidatePlugin(
|
|
293
|
-
|
|
294
|
-
{ fields: [
|
|
347
|
+
"requiredFields",
|
|
348
|
+
{ fields: ["title", "content"] },
|
|
295
349
|
{
|
|
296
350
|
insert: (context, data) => {
|
|
297
351
|
for (const field of context.config.fields) {
|
|
298
352
|
if (!data[field]) {
|
|
299
|
-
throw new ConvexError({
|
|
353
|
+
throw new ConvexError({
|
|
354
|
+
message: `Missing required field: ${field}`,
|
|
355
|
+
});
|
|
300
356
|
}
|
|
301
357
|
}
|
|
302
358
|
return data;
|
|
303
359
|
},
|
|
304
|
-
}
|
|
360
|
+
},
|
|
305
361
|
);
|
|
306
362
|
```
|
|
307
363
|
|
|
@@ -309,7 +365,7 @@ const requiredFields = createValidatePlugin(
|
|
|
309
365
|
|
|
310
366
|
```ts
|
|
311
367
|
const checkOwnership = createValidatePlugin(
|
|
312
|
-
|
|
368
|
+
"checkOwnership",
|
|
313
369
|
{},
|
|
314
370
|
{
|
|
315
371
|
patch: async (context, data) => {
|
|
@@ -317,11 +373,13 @@ const checkOwnership = createValidatePlugin(
|
|
|
317
373
|
const user = await getCurrentUser(context.ctx);
|
|
318
374
|
|
|
319
375
|
if (existing?.authorId !== user._id) {
|
|
320
|
-
throw new ConvexError({
|
|
376
|
+
throw new ConvexError({
|
|
377
|
+
message: "Not authorized to edit this document",
|
|
378
|
+
});
|
|
321
379
|
}
|
|
322
380
|
return data;
|
|
323
381
|
},
|
|
324
|
-
}
|
|
382
|
+
},
|
|
325
383
|
);
|
|
326
384
|
```
|
|
327
385
|
|
|
@@ -333,7 +391,7 @@ Plugins receive a `ValidateContext` object:
|
|
|
333
391
|
type ValidateContext = {
|
|
334
392
|
ctx: GenericMutationCtx; // Convex mutation context
|
|
335
393
|
tableName: string; // Table being operated on
|
|
336
|
-
operation:
|
|
394
|
+
operation: "insert" | "patch";
|
|
337
395
|
patchId?: GenericId; // Document ID (patch only)
|
|
338
396
|
onFail?: OnFailCallback; // Callback for failure details
|
|
339
397
|
schema?: SchemaDefinition; // Schema reference
|
|
@@ -348,13 +406,13 @@ For smaller bundle sizes, you can import from specific subpaths:
|
|
|
348
406
|
|
|
349
407
|
```ts
|
|
350
408
|
// Import everything from root
|
|
351
|
-
import { uniqueRowConfig, verifyConfig } from
|
|
352
|
-
import { protectedColumnsConfig } from
|
|
409
|
+
import { uniqueRowConfig, verifyConfig } from "convex-verify";
|
|
410
|
+
import { protectedColumnsConfig } from "convex-verify/configs";
|
|
353
411
|
// Or import from specific subpaths
|
|
354
|
-
import { createValidatePlugin, verifyConfig } from
|
|
355
|
-
import { uniqueColumnConfig, uniqueRowConfig } from
|
|
356
|
-
import { defaultValuesConfig } from
|
|
357
|
-
import { getTableIndexes } from
|
|
412
|
+
import { createValidatePlugin, verifyConfig } from "convex-verify/core";
|
|
413
|
+
import { uniqueColumnConfig, uniqueRowConfig } from "convex-verify/plugins";
|
|
414
|
+
import { defaultValuesConfig } from "convex-verify/transforms";
|
|
415
|
+
import { getTableIndexes } from "convex-verify/utils";
|
|
358
416
|
```
|
|
359
417
|
|
|
360
418
|
---
|
|
@@ -366,13 +424,13 @@ import { getTableIndexes } from 'convex-verify/utils';
|
|
|
366
424
|
All operations accept an optional `onFail` callback for handling validation failures:
|
|
367
425
|
|
|
368
426
|
```ts
|
|
369
|
-
await insert(ctx,
|
|
427
|
+
await insert(ctx, "posts", data, {
|
|
370
428
|
onFail: (args) => {
|
|
371
429
|
if (args.uniqueRow) {
|
|
372
|
-
console.log(
|
|
430
|
+
console.log("Duplicate row:", args.uniqueRow.existingData);
|
|
373
431
|
}
|
|
374
432
|
if (args.uniqueColumn) {
|
|
375
|
-
console.log(
|
|
433
|
+
console.log("Duplicate column:", args.uniqueColumn.conflictingColumn);
|
|
376
434
|
}
|
|
377
435
|
},
|
|
378
436
|
});
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GenericSchema, DataModelFromSchemaDefinition, SchemaDefinition, TableNamesInDataModel, DocumentByName, GenericMutationCtx, WithoutSystemFields } from 'convex/server';
|
|
2
2
|
import { GenericId } from 'convex/values';
|
|
3
3
|
import { a as ValidatePlugin } from '../plugin-mHMV2-SG.mjs';
|
|
4
4
|
export { V as ValidateContext, b as ValidatePluginRecord, c as createValidatePlugin, i as isValidatePlugin, r as runValidatePlugins } from '../plugin-mHMV2-SG.mjs';
|
|
@@ -10,8 +10,25 @@ export { B as BaseConfigReturn, k as DMGeneric, D as DefaultValuesConfigData, l
|
|
|
10
10
|
*/
|
|
11
11
|
type VerifyConfigInputWithPlugins = VerifyConfigInput & {
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Unique row validation config.
|
|
14
|
+
* Enforces uniqueness across multiple columns using composite indexes.
|
|
15
|
+
*
|
|
16
|
+
* Can also be added to the `plugins` array.
|
|
17
|
+
*/
|
|
18
|
+
uniqueRow?: ValidatePlugin<"uniqueRow", any>;
|
|
19
|
+
/**
|
|
20
|
+
* Unique column validation config.
|
|
21
|
+
* Enforces uniqueness on single columns using indexes.
|
|
22
|
+
*
|
|
23
|
+
* Can also be added to the `plugins` array.
|
|
24
|
+
*/
|
|
25
|
+
uniqueColumn?: ValidatePlugin<"uniqueColumn", any>;
|
|
26
|
+
/**
|
|
27
|
+
* Additional validate plugins to run after transforms.
|
|
14
28
|
* These plugins can validate data but don't affect input types.
|
|
29
|
+
*
|
|
30
|
+
* Built-in plugins (uniqueRow, uniqueColumn) can be added here
|
|
31
|
+
* as an alternative to using their dedicated config keys.
|
|
15
32
|
*/
|
|
16
33
|
plugins?: ValidatePlugin[];
|
|
17
34
|
};
|
|
@@ -24,25 +41,38 @@ type VerifyConfigInputWithPlugins = VerifyConfigInput & {
|
|
|
24
41
|
*
|
|
25
42
|
* @example
|
|
26
43
|
* ```ts
|
|
27
|
-
* import {
|
|
44
|
+
* import {
|
|
45
|
+
* verifyConfig,
|
|
46
|
+
* defaultValuesConfig,
|
|
47
|
+
* protectedColumnsConfig,
|
|
48
|
+
* uniqueRowConfig,
|
|
49
|
+
* uniqueColumnConfig,
|
|
50
|
+
* } from 'convex-verify';
|
|
28
51
|
* import schema from './schema';
|
|
29
52
|
*
|
|
30
53
|
* export const { insert, patch, dangerouslyPatch } = verifyConfig(schema, {
|
|
54
|
+
* // Type-affecting configs
|
|
31
55
|
* defaultValues: defaultValuesConfig(schema, () => ({
|
|
32
56
|
* posts: { status: 'draft', views: 0 },
|
|
33
57
|
* })),
|
|
34
58
|
* protectedColumns: protectedColumnsConfig(schema, {
|
|
35
59
|
* posts: ['authorId'],
|
|
36
60
|
* }),
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
61
|
+
*
|
|
62
|
+
* // Built-in validation configs
|
|
63
|
+
* uniqueRow: uniqueRowConfig(schema, {
|
|
64
|
+
* posts: ['by_author_slug'],
|
|
65
|
+
* }),
|
|
66
|
+
* uniqueColumn: uniqueColumnConfig(schema, {
|
|
67
|
+
* users: ['by_email', 'by_username'],
|
|
68
|
+
* }),
|
|
69
|
+
*
|
|
70
|
+
* // Custom/third-party plugins
|
|
71
|
+
* plugins: [myCustomPlugin()],
|
|
42
72
|
* });
|
|
43
73
|
* ```
|
|
44
74
|
*/
|
|
45
|
-
declare const verifyConfig: <S extends
|
|
75
|
+
declare const verifyConfig: <S extends GenericSchema, DataModel extends DataModelFromSchemaDefinition<SchemaDefinition<S, boolean>>, const VC extends VerifyConfigInputWithPlugins>(_schema: SchemaDefinition<S, boolean>, configs: VC) => {
|
|
46
76
|
insert: <const TN extends TableNamesInDataModel<DataModel>, const D extends DocumentByName<DataModel, TN>>(ctx: Omit<GenericMutationCtx<DataModel>, never>, tableName: TN, data: HasKey<VC, "defaultValues"> extends true ? MakeOptional<WithoutSystemFields<D>, OptionalKeysForTable<VC, TN> & keyof WithoutSystemFields<D>> : WithoutSystemFields<D>, options?: {
|
|
47
77
|
onFail?: OnFailCallback<D>;
|
|
48
78
|
}) => Promise<GenericId<TN>>;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GenericSchema, DataModelFromSchemaDefinition, SchemaDefinition, TableNamesInDataModel, DocumentByName, GenericMutationCtx, WithoutSystemFields } from 'convex/server';
|
|
2
2
|
import { GenericId } from 'convex/values';
|
|
3
3
|
import { a as ValidatePlugin } from '../plugin-BjJ7yjrc.js';
|
|
4
4
|
export { V as ValidateContext, b as ValidatePluginRecord, c as createValidatePlugin, i as isValidatePlugin, r as runValidatePlugins } from '../plugin-BjJ7yjrc.js';
|
|
@@ -10,8 +10,25 @@ export { B as BaseConfigReturn, k as DMGeneric, D as DefaultValuesConfigData, l
|
|
|
10
10
|
*/
|
|
11
11
|
type VerifyConfigInputWithPlugins = VerifyConfigInput & {
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Unique row validation config.
|
|
14
|
+
* Enforces uniqueness across multiple columns using composite indexes.
|
|
15
|
+
*
|
|
16
|
+
* Can also be added to the `plugins` array.
|
|
17
|
+
*/
|
|
18
|
+
uniqueRow?: ValidatePlugin<"uniqueRow", any>;
|
|
19
|
+
/**
|
|
20
|
+
* Unique column validation config.
|
|
21
|
+
* Enforces uniqueness on single columns using indexes.
|
|
22
|
+
*
|
|
23
|
+
* Can also be added to the `plugins` array.
|
|
24
|
+
*/
|
|
25
|
+
uniqueColumn?: ValidatePlugin<"uniqueColumn", any>;
|
|
26
|
+
/**
|
|
27
|
+
* Additional validate plugins to run after transforms.
|
|
14
28
|
* These plugins can validate data but don't affect input types.
|
|
29
|
+
*
|
|
30
|
+
* Built-in plugins (uniqueRow, uniqueColumn) can be added here
|
|
31
|
+
* as an alternative to using their dedicated config keys.
|
|
15
32
|
*/
|
|
16
33
|
plugins?: ValidatePlugin[];
|
|
17
34
|
};
|
|
@@ -24,25 +41,38 @@ type VerifyConfigInputWithPlugins = VerifyConfigInput & {
|
|
|
24
41
|
*
|
|
25
42
|
* @example
|
|
26
43
|
* ```ts
|
|
27
|
-
* import {
|
|
44
|
+
* import {
|
|
45
|
+
* verifyConfig,
|
|
46
|
+
* defaultValuesConfig,
|
|
47
|
+
* protectedColumnsConfig,
|
|
48
|
+
* uniqueRowConfig,
|
|
49
|
+
* uniqueColumnConfig,
|
|
50
|
+
* } from 'convex-verify';
|
|
28
51
|
* import schema from './schema';
|
|
29
52
|
*
|
|
30
53
|
* export const { insert, patch, dangerouslyPatch } = verifyConfig(schema, {
|
|
54
|
+
* // Type-affecting configs
|
|
31
55
|
* defaultValues: defaultValuesConfig(schema, () => ({
|
|
32
56
|
* posts: { status: 'draft', views: 0 },
|
|
33
57
|
* })),
|
|
34
58
|
* protectedColumns: protectedColumnsConfig(schema, {
|
|
35
59
|
* posts: ['authorId'],
|
|
36
60
|
* }),
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
61
|
+
*
|
|
62
|
+
* // Built-in validation configs
|
|
63
|
+
* uniqueRow: uniqueRowConfig(schema, {
|
|
64
|
+
* posts: ['by_author_slug'],
|
|
65
|
+
* }),
|
|
66
|
+
* uniqueColumn: uniqueColumnConfig(schema, {
|
|
67
|
+
* users: ['by_email', 'by_username'],
|
|
68
|
+
* }),
|
|
69
|
+
*
|
|
70
|
+
* // Custom/third-party plugins
|
|
71
|
+
* plugins: [myCustomPlugin()],
|
|
42
72
|
* });
|
|
43
73
|
* ```
|
|
44
74
|
*/
|
|
45
|
-
declare const verifyConfig: <S extends
|
|
75
|
+
declare const verifyConfig: <S extends GenericSchema, DataModel extends DataModelFromSchemaDefinition<SchemaDefinition<S, boolean>>, const VC extends VerifyConfigInputWithPlugins>(_schema: SchemaDefinition<S, boolean>, configs: VC) => {
|
|
46
76
|
insert: <const TN extends TableNamesInDataModel<DataModel>, const D extends DocumentByName<DataModel, TN>>(ctx: Omit<GenericMutationCtx<DataModel>, never>, tableName: TN, data: HasKey<VC, "defaultValues"> extends true ? MakeOptional<WithoutSystemFields<D>, OptionalKeysForTable<VC, TN> & keyof WithoutSystemFields<D>> : WithoutSystemFields<D>, options?: {
|
|
47
77
|
onFail?: OnFailCallback<D>;
|
|
48
78
|
}) => Promise<GenericId<TN>>;
|
package/dist/core/index.js
CHANGED
|
@@ -52,11 +52,20 @@ function createValidatePlugin(type, config, verify) {
|
|
|
52
52
|
|
|
53
53
|
// src/core/verifyConfig.ts
|
|
54
54
|
var verifyConfig = (_schema, configs) => {
|
|
55
|
-
const validatePlugins =
|
|
55
|
+
const validatePlugins = [
|
|
56
|
+
// Built-in validation configs (if provided as named keys)
|
|
57
|
+
...configs.uniqueRow ? [configs.uniqueRow] : [],
|
|
58
|
+
...configs.uniqueColumn ? [configs.uniqueColumn] : [],
|
|
59
|
+
// Additional plugins from the plugins array
|
|
60
|
+
...configs.plugins ?? []
|
|
61
|
+
];
|
|
56
62
|
const insert = async (ctx, tableName, data, options) => {
|
|
57
63
|
let verifiedData = data;
|
|
58
64
|
if (configs.defaultValues) {
|
|
59
|
-
verifiedData = await configs.defaultValues.verify(
|
|
65
|
+
verifiedData = await configs.defaultValues.verify(
|
|
66
|
+
tableName,
|
|
67
|
+
verifiedData
|
|
68
|
+
);
|
|
60
69
|
}
|
|
61
70
|
if (validatePlugins.length > 0) {
|
|
62
71
|
verifiedData = await runValidatePlugins(
|