convex-verify 0.1.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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +403 -0
  3. package/dist/configs/index.d.mts +51 -0
  4. package/dist/configs/index.d.ts +51 -0
  5. package/dist/configs/index.js +38 -0
  6. package/dist/configs/index.js.map +1 -0
  7. package/dist/configs/index.mjs +11 -0
  8. package/dist/configs/index.mjs.map +1 -0
  9. package/dist/core/index.d.mts +58 -0
  10. package/dist/core/index.d.ts +58 -0
  11. package/dist/core/index.js +144 -0
  12. package/dist/core/index.js.map +1 -0
  13. package/dist/core/index.mjs +113 -0
  14. package/dist/core/index.mjs.map +1 -0
  15. package/dist/index.d.mts +9 -0
  16. package/dist/index.d.ts +9 -0
  17. package/dist/index.js +442 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/index.mjs +404 -0
  20. package/dist/index.mjs.map +1 -0
  21. package/dist/plugin-BjJ7yjrc.d.ts +141 -0
  22. package/dist/plugin-mHMV2-SG.d.mts +141 -0
  23. package/dist/plugins/index.d.mts +85 -0
  24. package/dist/plugins/index.d.ts +85 -0
  25. package/dist/plugins/index.js +317 -0
  26. package/dist/plugins/index.js.map +1 -0
  27. package/dist/plugins/index.mjs +289 -0
  28. package/dist/plugins/index.mjs.map +1 -0
  29. package/dist/transforms/index.d.mts +38 -0
  30. package/dist/transforms/index.d.ts +38 -0
  31. package/dist/transforms/index.js +46 -0
  32. package/dist/transforms/index.js.map +1 -0
  33. package/dist/transforms/index.mjs +19 -0
  34. package/dist/transforms/index.mjs.map +1 -0
  35. package/dist/types-_64SXyva.d.mts +151 -0
  36. package/dist/types-_64SXyva.d.ts +151 -0
  37. package/dist/utils/index.d.mts +36 -0
  38. package/dist/utils/index.d.ts +36 -0
  39. package/dist/utils/index.js +113 -0
  40. package/dist/utils/index.js.map +1 -0
  41. package/dist/utils/index.mjs +83 -0
  42. package/dist/utils/index.mjs.map +1 -0
  43. package/package.json +75 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,403 @@
1
+ # convex-verify
2
+
3
+ Type-safe verification and validation for Convex database operations.
4
+
5
+ ## Features
6
+
7
+ - **Type-safe insert/patch** - Full TypeScript inference for your schema
8
+ - **Default values** - Make fields optional in `insert()` with automatic defaults
9
+ - **Protected columns** - Prevent accidental updates to critical fields in `patch()`
10
+ - **Validation plugins** - Unique row/column enforcement, custom validators
11
+ - **Extensible** - Create your own validation plugins
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install convex-verify
17
+ ```
18
+
19
+ **Peer Dependencies:**
20
+
21
+ - `convex` >= 1.17.4
22
+
23
+ ## Quick Start
24
+
25
+ ```ts
26
+ import {
27
+ defaultValuesConfig,
28
+ protectedColumnsConfig,
29
+ uniqueColumnConfig,
30
+ uniqueRowConfig,
31
+ verifyConfig,
32
+ } from 'convex-verify';
33
+
34
+ import schema from './schema';
35
+
36
+ export const { insert, patch, dangerouslyPatch } = verifyConfig(schema, {
37
+ // Make fields optional with defaults
38
+ defaultValues: defaultValuesConfig(schema, () => ({
39
+ posts: { status: 'draft', views: 0 },
40
+ })),
41
+
42
+ // Prevent patching critical fields
43
+ protectedColumns: protectedColumnsConfig(schema, {
44
+ posts: ['authorId'],
45
+ }),
46
+
47
+ // Validation plugins
48
+ plugins: [
49
+ uniqueRowConfig(schema, {
50
+ posts: ['by_author_slug'],
51
+ }),
52
+ uniqueColumnConfig(schema, {
53
+ users: ['by_email', 'by_username'],
54
+ }),
55
+ ],
56
+ });
57
+ ```
58
+
59
+ Then use in your mutations:
60
+
61
+ ```ts
62
+ import { insert, patch } from './verify';
63
+
64
+ export const createPost = mutation({
65
+ args: { title: v.string(), content: v.string() },
66
+ handler: async (ctx, args) => {
67
+ // status and views are optional - defaults are applied
68
+ return await insert(ctx, 'posts', {
69
+ title: args.title,
70
+ content: args.content,
71
+ authorId: ctx.auth.userId,
72
+ });
73
+ },
74
+ });
75
+
76
+ export const updatePost = mutation({
77
+ args: { id: v.id('posts'), title: v.string() },
78
+ handler: async (ctx, args) => {
79
+ // authorId is protected - TypeScript won't allow it here
80
+ await patch(ctx, 'posts', args.id, {
81
+ title: args.title,
82
+ });
83
+ },
84
+ });
85
+ ```
86
+
87
+ ---
88
+
89
+ ## API Reference
90
+
91
+ ### `verifyConfig(schema, config)`
92
+
93
+ Main configuration function that returns typed `insert`, `patch`, and `dangerouslyPatch` functions.
94
+
95
+ ```ts
96
+ const { insert, patch, dangerouslyPatch, configs } = verifyConfig(schema, {
97
+ defaultValues?: DefaultValuesConfig,
98
+ protectedColumns?: ProtectedColumnsConfig,
99
+ plugins?: ValidatePlugin[],
100
+ });
101
+ ```
102
+
103
+ #### Returns
104
+
105
+ | Function | Description |
106
+ | ------------------ | ----------------------------------------------------------------------------------- |
107
+ | `insert` | Insert with default values applied and validation plugins run |
108
+ | `patch` | Patch with protected columns removed from type and validation plugins run |
109
+ | `dangerouslyPatch` | Patch with full access to all columns (bypasses protected columns type restriction) |
110
+ | `configs` | The original config object (for debugging) |
111
+
112
+ ---
113
+
114
+ ## Transforms
115
+
116
+ Transforms modify the input type of `insert()`.
117
+
118
+ ### `defaultValuesConfig(schema, config)`
119
+
120
+ Makes specified fields optional in `insert()` by providing default values.
121
+
122
+ ```ts
123
+ import { defaultValuesConfig } from 'convex-verify';
124
+ // or
125
+ import { defaultValuesConfig } from 'convex-verify/transforms';
126
+ ```
127
+
128
+ #### Static Config
129
+
130
+ ```ts
131
+ const defaults = defaultValuesConfig(schema, {
132
+ posts: { status: 'draft', views: 0 },
133
+ comments: { likes: 0 },
134
+ });
135
+ ```
136
+
137
+ #### Dynamic Config (Fresh Values)
138
+
139
+ Use a function for values that should be generated fresh on each insert:
140
+
141
+ ```ts
142
+ const defaults = defaultValuesConfig(schema, () => ({
143
+ posts: {
144
+ status: 'draft',
145
+ slug: generateRandomSlug(),
146
+ createdAt: Date.now(),
147
+ },
148
+ }));
149
+ ```
150
+
151
+ #### Async Config
152
+
153
+ ```ts
154
+ const defaults = defaultValuesConfig(schema, async () => ({
155
+ posts: {
156
+ category: await fetchDefaultCategory(),
157
+ },
158
+ }));
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Configs
164
+
165
+ Configs modify the input type of `patch()`.
166
+
167
+ ### `protectedColumnsConfig(schema, config)`
168
+
169
+ Removes specified columns from the `patch()` input type, preventing accidental updates.
170
+
171
+ ```ts
172
+ import { protectedColumnsConfig } from 'convex-verify';
173
+ // or
174
+ import { protectedColumnsConfig } from 'convex-verify/configs';
175
+ ```
176
+
177
+ #### Example
178
+
179
+ ```ts
180
+ const protected = protectedColumnsConfig(schema, {
181
+ posts: ['authorId', 'createdAt'],
182
+ comments: ['postId', 'authorId'],
183
+ });
184
+ ```
185
+
186
+ #### Bypassing Protection
187
+
188
+ Use `dangerouslyPatch()` when you need to update protected columns:
189
+
190
+ ```ts
191
+ // Regular patch - authorId not allowed
192
+ await patch(ctx, 'posts', id, {
193
+ authorId: newAuthorId, // ❌ TypeScript error
194
+ title: 'New Title', // ✅ OK
195
+ });
196
+
197
+ // Dangerous patch - full access
198
+ await dangerouslyPatch(ctx, 'posts', id, {
199
+ authorId: newAuthorId, // ✅ OK (bypasses protection)
200
+ title: 'New Title',
201
+ });
202
+ ```
203
+
204
+ **Note:** `dangerouslyPatch()` still runs validation plugins - only the type restriction is bypassed.
205
+
206
+ ---
207
+
208
+ ## Plugins
209
+
210
+ Plugins validate data during `insert()` and `patch()` operations. They run after transforms and can throw errors to prevent the operation.
211
+
212
+ ### `uniqueRowConfig(schema, config)`
213
+
214
+ Enforces uniqueness across multiple columns using composite indexes.
215
+
216
+ ```ts
217
+ import { uniqueRowConfig } from 'convex-verify';
218
+ // or
219
+ import { uniqueRowConfig } from 'convex-verify/plugins';
220
+ ```
221
+
222
+ #### Shorthand (Index Names)
223
+
224
+ ```ts
225
+ const uniqueRows = uniqueRowConfig(schema, {
226
+ posts: ['by_author_slug'], // Unique author + slug combo
227
+ projects: ['by_org_slug'], // Unique org + slug combo
228
+ });
229
+ ```
230
+
231
+ #### With Options
232
+
233
+ ```ts
234
+ const uniqueRows = uniqueRowConfig(schema, {
235
+ posts: [
236
+ {
237
+ index: 'by_author_slug',
238
+ identifiers: ['_id', 'authorId'], // Fields to check for "same document"
239
+ },
240
+ ],
241
+ });
242
+ ```
243
+
244
+ ### `uniqueColumnConfig(schema, config)`
245
+
246
+ Enforces uniqueness on single columns using indexes.
247
+
248
+ ```ts
249
+ import { uniqueColumnConfig } from 'convex-verify';
250
+ // or
251
+ import { uniqueColumnConfig } from 'convex-verify/plugins';
252
+ ```
253
+
254
+ The column name is derived from the index name by removing `by_` prefix:
255
+
256
+ - `by_username` → checks `username` column
257
+ - `by_email` → checks `email` column
258
+
259
+ #### Example
260
+
261
+ ```ts
262
+ const uniqueColumns = uniqueColumnConfig(schema, {
263
+ users: ['by_username', 'by_email'],
264
+ organizations: ['by_slug'],
265
+ });
266
+ ```
267
+
268
+ #### With Options
269
+
270
+ ```ts
271
+ const uniqueColumns = uniqueColumnConfig(schema, {
272
+ users: [
273
+ 'by_username', // shorthand
274
+ { index: 'by_email', identifiers: ['_id', 'clerkId'] }, // with options
275
+ ],
276
+ });
277
+ ```
278
+
279
+ ### `createValidatePlugin(name, config, handlers)`
280
+
281
+ Create custom validation plugins.
282
+
283
+ ```ts
284
+ import { createValidatePlugin } from 'convex-verify';
285
+ // or
286
+ import { createValidatePlugin } from 'convex-verify/core';
287
+ ```
288
+
289
+ #### Example: Required Fields Plugin
290
+
291
+ ```ts
292
+ const requiredFields = createValidatePlugin(
293
+ 'requiredFields',
294
+ { fields: ['title', 'content'] },
295
+ {
296
+ insert: (context, data) => {
297
+ for (const field of context.config.fields) {
298
+ if (!data[field]) {
299
+ throw new ConvexError({ message: `Missing required field: ${field}` });
300
+ }
301
+ }
302
+ return data;
303
+ },
304
+ }
305
+ );
306
+ ```
307
+
308
+ #### Example: Async Validation
309
+
310
+ ```ts
311
+ const checkOwnership = createValidatePlugin(
312
+ 'checkOwnership',
313
+ {},
314
+ {
315
+ patch: async (context, data) => {
316
+ const existing = await context.ctx.db.get(context.patchId);
317
+ const user = await getCurrentUser(context.ctx);
318
+
319
+ if (existing?.authorId !== user._id) {
320
+ throw new ConvexError({ message: 'Not authorized to edit this document' });
321
+ }
322
+ return data;
323
+ },
324
+ }
325
+ );
326
+ ```
327
+
328
+ #### Plugin Context
329
+
330
+ Plugins receive a `ValidateContext` object:
331
+
332
+ ```ts
333
+ type ValidateContext = {
334
+ ctx: GenericMutationCtx; // Convex mutation context
335
+ tableName: string; // Table being operated on
336
+ operation: 'insert' | 'patch';
337
+ patchId?: GenericId; // Document ID (patch only)
338
+ onFail?: OnFailCallback; // Callback for failure details
339
+ schema?: SchemaDefinition; // Schema reference
340
+ };
341
+ ```
342
+
343
+ ---
344
+
345
+ ## Subpath Imports
346
+
347
+ For smaller bundle sizes, you can import from specific subpaths:
348
+
349
+ ```ts
350
+ // Import everything from root
351
+ import { uniqueRowConfig, verifyConfig } from 'convex-verify';
352
+ import { protectedColumnsConfig } from 'convex-verify/configs';
353
+ // Or import from specific subpaths
354
+ import { createValidatePlugin, verifyConfig } from 'convex-verify/core';
355
+ import { uniqueColumnConfig, uniqueRowConfig } from 'convex-verify/plugins';
356
+ import { defaultValuesConfig } from 'convex-verify/transforms';
357
+ import { getTableIndexes } from 'convex-verify/utils';
358
+ ```
359
+
360
+ ---
361
+
362
+ ## Error Handling
363
+
364
+ ### `onFail` Callback
365
+
366
+ All operations accept an optional `onFail` callback for handling validation failures:
367
+
368
+ ```ts
369
+ await insert(ctx, 'posts', data, {
370
+ onFail: (args) => {
371
+ if (args.uniqueRow) {
372
+ console.log('Duplicate row:', args.uniqueRow.existingData);
373
+ }
374
+ if (args.uniqueColumn) {
375
+ console.log('Duplicate column:', args.uniqueColumn.conflictingColumn);
376
+ }
377
+ },
378
+ });
379
+ ```
380
+
381
+ ### Error Types
382
+
383
+ Validation plugins throw `ConvexError` with specific codes:
384
+
385
+ - `UNIQUE_ROW_VERIFICATION_ERROR` - Duplicate row detected
386
+ - `UNIQUE_COLUMN_VERIFICATION_ERROR` - Duplicate column value detected
387
+
388
+ ---
389
+
390
+ ## TypeScript
391
+
392
+ This library is written in TypeScript and provides full type inference:
393
+
394
+ - `insert()` types reflect optional fields from `defaultValues`
395
+ - `patch()` types exclude protected columns
396
+ - Plugin configs are type-checked against your schema
397
+ - Index names are validated against your schema's indexes
398
+
399
+ ---
400
+
401
+ ## License
402
+
403
+ MIT
@@ -0,0 +1,51 @@
1
+ import { SchemaDefinition, GenericSchema, DataModelFromSchemaDefinition, WithoutSystemFields } from 'convex/server';
2
+ import { k as DMGeneric } from '../types-_64SXyva.mjs';
3
+
4
+ /**
5
+ * Config data type for protected columns.
6
+ * Maps table names to arrays of column names that should be protected from patching.
7
+ */
8
+ type ProtectedColumnsConfigData<DM extends DMGeneric> = {
9
+ [K in keyof DM]?: (keyof WithoutSystemFields<DM[K]['document']>)[];
10
+ };
11
+ /**
12
+ * Creates a protected columns config.
13
+ *
14
+ * Protected columns are removed from the patch() input type,
15
+ * preventing accidental updates to critical fields like foreign keys.
16
+ * Use dangerouslyPatch() to bypass this protection when needed.
17
+ *
18
+ * @param schema - Your Convex schema definition
19
+ * @param config - Object mapping table names to arrays of protected column names
20
+ * @returns Config object for use with verifyConfig
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const protectedColumns = protectedColumnsConfig(schema, {
25
+ * posts: ['authorId', 'createdAt'],
26
+ * comments: ['postId', 'authorId'],
27
+ * });
28
+ *
29
+ * // In verifyConfig:
30
+ * const { patch, dangerouslyPatch } = verifyConfig(schema, {
31
+ * protectedColumns,
32
+ * });
33
+ *
34
+ * // patch() won't allow authorId
35
+ * await patch(ctx, 'posts', id, {
36
+ * authorId: '...', // TS Error - property doesn't exist
37
+ * title: 'new', // OK
38
+ * });
39
+ *
40
+ * // dangerouslyPatch() allows all columns
41
+ * await dangerouslyPatch(ctx, 'posts', id, {
42
+ * authorId: '...', // OK - bypasses protection
43
+ * });
44
+ * ```
45
+ */
46
+ declare const protectedColumnsConfig: <S extends SchemaDefinition<GenericSchema, boolean>, DataModel extends DataModelFromSchemaDefinition<S>, const C extends ProtectedColumnsConfigData<DataModel>>(_schema: S, config: C) => {
47
+ _type: "protectedColumns";
48
+ config: C;
49
+ };
50
+
51
+ export { type ProtectedColumnsConfigData, protectedColumnsConfig };
@@ -0,0 +1,51 @@
1
+ import { SchemaDefinition, GenericSchema, DataModelFromSchemaDefinition, WithoutSystemFields } from 'convex/server';
2
+ import { k as DMGeneric } from '../types-_64SXyva.js';
3
+
4
+ /**
5
+ * Config data type for protected columns.
6
+ * Maps table names to arrays of column names that should be protected from patching.
7
+ */
8
+ type ProtectedColumnsConfigData<DM extends DMGeneric> = {
9
+ [K in keyof DM]?: (keyof WithoutSystemFields<DM[K]['document']>)[];
10
+ };
11
+ /**
12
+ * Creates a protected columns config.
13
+ *
14
+ * Protected columns are removed from the patch() input type,
15
+ * preventing accidental updates to critical fields like foreign keys.
16
+ * Use dangerouslyPatch() to bypass this protection when needed.
17
+ *
18
+ * @param schema - Your Convex schema definition
19
+ * @param config - Object mapping table names to arrays of protected column names
20
+ * @returns Config object for use with verifyConfig
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const protectedColumns = protectedColumnsConfig(schema, {
25
+ * posts: ['authorId', 'createdAt'],
26
+ * comments: ['postId', 'authorId'],
27
+ * });
28
+ *
29
+ * // In verifyConfig:
30
+ * const { patch, dangerouslyPatch } = verifyConfig(schema, {
31
+ * protectedColumns,
32
+ * });
33
+ *
34
+ * // patch() won't allow authorId
35
+ * await patch(ctx, 'posts', id, {
36
+ * authorId: '...', // TS Error - property doesn't exist
37
+ * title: 'new', // OK
38
+ * });
39
+ *
40
+ * // dangerouslyPatch() allows all columns
41
+ * await dangerouslyPatch(ctx, 'posts', id, {
42
+ * authorId: '...', // OK - bypasses protection
43
+ * });
44
+ * ```
45
+ */
46
+ declare const protectedColumnsConfig: <S extends SchemaDefinition<GenericSchema, boolean>, DataModel extends DataModelFromSchemaDefinition<S>, const C extends ProtectedColumnsConfigData<DataModel>>(_schema: S, config: C) => {
47
+ _type: "protectedColumns";
48
+ config: C;
49
+ };
50
+
51
+ export { type ProtectedColumnsConfigData, protectedColumnsConfig };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/configs/index.ts
21
+ var configs_exports = {};
22
+ __export(configs_exports, {
23
+ protectedColumnsConfig: () => protectedColumnsConfig
24
+ });
25
+ module.exports = __toCommonJS(configs_exports);
26
+
27
+ // src/configs/protectedColumnsConfig.ts
28
+ var protectedColumnsConfig = (_schema, config) => {
29
+ return {
30
+ _type: "protectedColumns",
31
+ config
32
+ };
33
+ };
34
+ // Annotate the CommonJS export names for ESM import in node:
35
+ 0 && (module.exports = {
36
+ protectedColumnsConfig
37
+ });
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/configs/index.ts","../../src/configs/protectedColumnsConfig.ts"],"sourcesContent":["export { protectedColumnsConfig } from './protectedColumnsConfig';\nexport type { ProtectedColumnsConfigData } from './protectedColumnsConfig';\n","import {\n\tDataModelFromSchemaDefinition,\n\tGenericSchema,\n\tSchemaDefinition,\n\tWithoutSystemFields,\n} from 'convex/server';\n\nimport { DMGeneric } from '../core/types';\n\n/**\n * Config data type for protected columns.\n * Maps table names to arrays of column names that should be protected from patching.\n */\nexport type ProtectedColumnsConfigData<DM extends DMGeneric> = {\n\t[K in keyof DM]?: (keyof WithoutSystemFields<DM[K]['document']>)[];\n};\n\n/**\n * Creates a protected columns config.\n *\n * Protected columns are removed from the patch() input type,\n * preventing accidental updates to critical fields like foreign keys.\n * Use dangerouslyPatch() to bypass this protection when needed.\n *\n * @param schema - Your Convex schema definition\n * @param config - Object mapping table names to arrays of protected column names\n * @returns Config object for use with verifyConfig\n *\n * @example\n * ```ts\n * const protectedColumns = protectedColumnsConfig(schema, {\n * posts: ['authorId', 'createdAt'],\n * comments: ['postId', 'authorId'],\n * });\n *\n * // In verifyConfig:\n * const { patch, dangerouslyPatch } = verifyConfig(schema, {\n * protectedColumns,\n * });\n *\n * // patch() won't allow authorId\n * await patch(ctx, 'posts', id, {\n * authorId: '...', // TS Error - property doesn't exist\n * title: 'new', // OK\n * });\n *\n * // dangerouslyPatch() allows all columns\n * await dangerouslyPatch(ctx, 'posts', id, {\n * authorId: '...', // OK - bypasses protection\n * });\n * ```\n */\nexport const protectedColumnsConfig = <\n\tS extends SchemaDefinition<GenericSchema, boolean>,\n\tDataModel extends DataModelFromSchemaDefinition<S>,\n\tconst C extends ProtectedColumnsConfigData<DataModel>,\n>(\n\t_schema: S,\n\tconfig: C\n) => {\n\treturn {\n\t\t_type: 'protectedColumns' as const,\n\t\tconfig,\n\t};\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACoDO,IAAM,yBAAyB,CAKrC,SACA,WACI;AACJ,SAAO;AAAA,IACN,OAAO;AAAA,IACP;AAAA,EACD;AACD;","names":[]}
@@ -0,0 +1,11 @@
1
+ // src/configs/protectedColumnsConfig.ts
2
+ var protectedColumnsConfig = (_schema, config) => {
3
+ return {
4
+ _type: "protectedColumns",
5
+ config
6
+ };
7
+ };
8
+ export {
9
+ protectedColumnsConfig
10
+ };
11
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/configs/protectedColumnsConfig.ts"],"sourcesContent":["import {\n\tDataModelFromSchemaDefinition,\n\tGenericSchema,\n\tSchemaDefinition,\n\tWithoutSystemFields,\n} from 'convex/server';\n\nimport { DMGeneric } from '../core/types';\n\n/**\n * Config data type for protected columns.\n * Maps table names to arrays of column names that should be protected from patching.\n */\nexport type ProtectedColumnsConfigData<DM extends DMGeneric> = {\n\t[K in keyof DM]?: (keyof WithoutSystemFields<DM[K]['document']>)[];\n};\n\n/**\n * Creates a protected columns config.\n *\n * Protected columns are removed from the patch() input type,\n * preventing accidental updates to critical fields like foreign keys.\n * Use dangerouslyPatch() to bypass this protection when needed.\n *\n * @param schema - Your Convex schema definition\n * @param config - Object mapping table names to arrays of protected column names\n * @returns Config object for use with verifyConfig\n *\n * @example\n * ```ts\n * const protectedColumns = protectedColumnsConfig(schema, {\n * posts: ['authorId', 'createdAt'],\n * comments: ['postId', 'authorId'],\n * });\n *\n * // In verifyConfig:\n * const { patch, dangerouslyPatch } = verifyConfig(schema, {\n * protectedColumns,\n * });\n *\n * // patch() won't allow authorId\n * await patch(ctx, 'posts', id, {\n * authorId: '...', // TS Error - property doesn't exist\n * title: 'new', // OK\n * });\n *\n * // dangerouslyPatch() allows all columns\n * await dangerouslyPatch(ctx, 'posts', id, {\n * authorId: '...', // OK - bypasses protection\n * });\n * ```\n */\nexport const protectedColumnsConfig = <\n\tS extends SchemaDefinition<GenericSchema, boolean>,\n\tDataModel extends DataModelFromSchemaDefinition<S>,\n\tconst C extends ProtectedColumnsConfigData<DataModel>,\n>(\n\t_schema: S,\n\tconfig: C\n) => {\n\treturn {\n\t\t_type: 'protectedColumns' as const,\n\t\tconfig,\n\t};\n};\n"],"mappings":";AAoDO,IAAM,yBAAyB,CAKrC,SACA,WACI;AACJ,SAAO;AAAA,IACN,OAAO;AAAA,IACP;AAAA,EACD;AACD;","names":[]}
@@ -0,0 +1,58 @@
1
+ import { SchemaDefinition, GenericSchema, DataModelFromSchemaDefinition, TableNamesInDataModel, DocumentByName, GenericMutationCtx, WithoutSystemFields } from 'convex/server';
2
+ import { GenericId } from 'convex/values';
3
+ import { a as ValidatePlugin } from '../plugin-mHMV2-SG.mjs';
4
+ export { V as ValidateContext, b as ValidatePluginRecord, c as createValidatePlugin, i as isValidatePlugin, r as runValidatePlugins } from '../plugin-mHMV2-SG.mjs';
5
+ import { V as VerifyConfigInput, H as HasKey, M as MakeOptional, b as OptionalKeysForTable, a as OnFailCallback, d as ProtectedKeysForTable } from '../types-_64SXyva.mjs';
6
+ export { B as BaseConfigReturn, k as DMGeneric, D as DefaultValuesConfigData, l as DefaultValuesInput, E as ExtractDefaultValuesConfig, c as ExtractProtectedColumnsConfig, I as IndexConfigBaseOptions, j as IndexConfigEntry, N as NormalizedIndexConfig, O as OnFailArgs, P as Prettify, m as ProtectedColumnsInput, g as UniqueColumnConfigData, h as UniqueColumnConfigEntry, i as UniqueColumnConfigOptions, U as UniqueRowConfigData, e as UniqueRowConfigEntry, f as UniqueRowConfigOptions, n as normalizeIndexConfigEntry } from '../types-_64SXyva.mjs';
7
+
8
+ /**
9
+ * Extended config input that includes optional validate plugins
10
+ */
11
+ type VerifyConfigInputWithPlugins = VerifyConfigInput & {
12
+ /**
13
+ * Validate plugins to run after transforms.
14
+ * These plugins can validate data but don't affect input types.
15
+ */
16
+ plugins?: ValidatePlugin[];
17
+ };
18
+ /**
19
+ * Configure type-safe insert and patch functions with validation and transforms.
20
+ *
21
+ * @param schema - Your Convex schema definition
22
+ * @param configs - Configuration object with transforms, configs, and plugins
23
+ * @returns Object with `insert`, `patch`, and `dangerouslyPatch` functions
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { verifyConfig, defaultValuesConfig, protectedColumnsConfig, uniqueRowConfig } from 'convex-verify';
28
+ * import schema from './schema';
29
+ *
30
+ * export const { insert, patch, dangerouslyPatch } = verifyConfig(schema, {
31
+ * defaultValues: defaultValuesConfig(schema, () => ({
32
+ * posts: { status: 'draft', views: 0 },
33
+ * })),
34
+ * protectedColumns: protectedColumnsConfig(schema, {
35
+ * posts: ['authorId'],
36
+ * }),
37
+ * plugins: [
38
+ * uniqueRowConfig(schema, {
39
+ * posts: ['by_slug'],
40
+ * }),
41
+ * ],
42
+ * });
43
+ * ```
44
+ */
45
+ declare const verifyConfig: <S extends SchemaDefinition<GenericSchema, boolean>, DataModel extends DataModelFromSchemaDefinition<S>, const VC extends VerifyConfigInputWithPlugins>(_schema: S, configs: VC) => {
46
+ 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
+ onFail?: OnFailCallback<D>;
48
+ }) => Promise<GenericId<TN>>;
49
+ patch: <const TN extends TableNamesInDataModel<DataModel>, const D_1 extends DocumentByName<DataModel, TN>>(ctx: Omit<GenericMutationCtx<DataModel>, never>, tableName: TN, id: GenericId<TN>, data: HasKey<VC, "protectedColumns"> extends true ? Omit<Partial<WithoutSystemFields<D_1>>, ProtectedKeysForTable<VC, TN> & keyof WithoutSystemFields<D_1>> : Partial<WithoutSystemFields<D_1>>, options?: {
50
+ onFail?: OnFailCallback<D_1>;
51
+ }) => Promise<void>;
52
+ dangerouslyPatch: <const TN extends TableNamesInDataModel<DataModel>, const D_2 extends DocumentByName<DataModel, TN>>(ctx: Omit<GenericMutationCtx<DataModel>, never>, tableName: TN, id: GenericId<TN>, data: Partial<WithoutSystemFields<D_2>>, options?: {
53
+ onFail?: OnFailCallback<D_2>;
54
+ }) => Promise<void>;
55
+ configs: VC;
56
+ };
57
+
58
+ export { HasKey, MakeOptional, OnFailCallback, OptionalKeysForTable, ProtectedKeysForTable, ValidatePlugin, VerifyConfigInput, verifyConfig };