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
@@ -0,0 +1,151 @@
1
+ import { DataModelFromSchemaDefinition, SchemaDefinition, WithoutSystemFields, Indexes, NamedTableInfo } from 'convex/server';
2
+
3
+ type Prettify<T> = {
4
+ [K in keyof T]: T[K];
5
+ } & {};
6
+ type MakeOptional<T, K extends PropertyKey> = Prettify<Omit<T, K & keyof T> & Partial<Pick<T, K & keyof T>>>;
7
+ /**
8
+ * Base interface that all config functions should return.
9
+ * Each config type can have its own `verify` signature and additional properties.
10
+ */
11
+ type BaseConfigReturn = {
12
+ config: Record<string, any>;
13
+ };
14
+ type OnFailArgs<D> = {
15
+ uniqueColumn?: {
16
+ conflictingColumn: keyof D;
17
+ existingData: D;
18
+ };
19
+ uniqueRow?: {
20
+ existingData: D | null;
21
+ };
22
+ editableColumn?: {
23
+ removedColumns: string[];
24
+ filteredData: D;
25
+ };
26
+ requiredColumn?: {
27
+ missingColumn: keyof D;
28
+ };
29
+ };
30
+ type OnFailCallback<D> = (args: OnFailArgs<D>) => void;
31
+ type DMGeneric = DataModelFromSchemaDefinition<SchemaDefinition<any, boolean>>;
32
+ type DefaultValuesConfigData<DM extends DMGeneric> = {
33
+ [K in keyof DM]?: {
34
+ [column in keyof WithoutSystemFields<DM[K]['document']>]?: DM[K]['document'][column];
35
+ };
36
+ };
37
+ /**
38
+ * Base options shared by all index-based config entries.
39
+ * Individual plugins can extend this with their own options.
40
+ */
41
+ type IndexConfigBaseOptions = {
42
+ /** Additional identifiers to check if the existing row is the same document being updated */
43
+ identifiers?: string[];
44
+ };
45
+ /**
46
+ * A config entry that can be either:
47
+ * - A string (index name) for shorthand
48
+ * - An object with `index` and additional options
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * // These are equivalent:
53
+ * 'by_username'
54
+ * { index: 'by_username' }
55
+ *
56
+ * // With options:
57
+ * { index: 'by_username', identifiers: ['_id', 'userId'] }
58
+ * ```
59
+ */
60
+ type IndexConfigEntry<DM extends DMGeneric, K extends keyof DM, Options extends IndexConfigBaseOptions = IndexConfigBaseOptions> = keyof Indexes<NamedTableInfo<DM, K>> | ({
61
+ index: keyof Indexes<NamedTableInfo<DM, K>>;
62
+ identifiers?: (keyof NamedTableInfo<DM, K>['document'])[];
63
+ } & Omit<Options, 'identifiers'>);
64
+ /**
65
+ * Normalized form of an index config entry (always an object)
66
+ */
67
+ type NormalizedIndexConfig<Options extends IndexConfigBaseOptions = IndexConfigBaseOptions> = {
68
+ index: string;
69
+ identifiers: string[];
70
+ } & Omit<Options, 'identifiers'>;
71
+ /**
72
+ * Normalize a config entry to always have index and identifiers.
73
+ * Works for both string shorthand and full object configs.
74
+ */
75
+ declare function normalizeIndexConfigEntry<Options extends IndexConfigBaseOptions = IndexConfigBaseOptions>(entry: string | ({
76
+ index: string;
77
+ identifiers?: string[];
78
+ } & Omit<Options, 'identifiers'>), defaultIdentifiers?: string[]): NormalizedIndexConfig<Options>;
79
+ type UniqueRowConfigOptions = IndexConfigBaseOptions & {
80
+ queryExistingWithNullish?: boolean;
81
+ };
82
+ type UniqueRowConfigEntry<DM extends DMGeneric, K extends keyof DM> = IndexConfigEntry<DM, K, UniqueRowConfigOptions>;
83
+ type UniqueRowConfigData<DM extends DMGeneric> = {
84
+ [K in keyof DM]?: UniqueRowConfigEntry<DM, K>[];
85
+ };
86
+ type UniqueColumnConfigOptions = IndexConfigBaseOptions;
87
+ type UniqueColumnConfigEntry<DM extends DMGeneric, K extends keyof DM> = IndexConfigEntry<DM, K, UniqueColumnConfigOptions>;
88
+ type UniqueColumnConfigData<DM extends DMGeneric> = {
89
+ [K in keyof DM]?: UniqueColumnConfigEntry<DM, K>[];
90
+ };
91
+ /**
92
+ * Loose input types that accept any return from config functions.
93
+ * We use loose types here to avoid complex generic matching,
94
+ * then extract the specific config types using conditional types.
95
+ */
96
+ type DefaultValuesInput = {
97
+ _type: 'defaultValues';
98
+ verify: (tableName: any, data: any) => Promise<any>;
99
+ config: Record<string, Record<string, any>> | (() => Record<string, Record<string, any>> | Promise<Record<string, Record<string, any>>>);
100
+ };
101
+ /**
102
+ * Loose input type for protectedColumnsConfig return value.
103
+ */
104
+ type ProtectedColumnsInput = {
105
+ _type: 'protectedColumns';
106
+ config: Record<string, string[]>;
107
+ };
108
+ /**
109
+ * Config input for verifyConfig.
110
+ *
111
+ * - `defaultValues`: Transform plugin that makes fields optional (affects types)
112
+ * - `protectedColumns`: Columns that cannot be patched (affects patch() types)
113
+ * - `plugins`: Array of validate plugins (use for uniqueRow, uniqueColumn, custom plugins, etc.)
114
+ */
115
+ type VerifyConfigInput = {
116
+ defaultValues?: DefaultValuesInput;
117
+ protectedColumns?: ProtectedColumnsInput;
118
+ };
119
+ /**
120
+ * Extract the config type from defaultValues.config.
121
+ * Handles both direct object and function forms.
122
+ */
123
+ type ExtractDefaultValuesConfig<VC> = VC extends {
124
+ defaultValues: {
125
+ config: infer C;
126
+ };
127
+ } ? C extends () => infer R ? Awaited<R> : C : Record<string, never>;
128
+ /**
129
+ * Compute which keys should be optional for a given table based on all configs.
130
+ * Currently only defaultValues affects optionality.
131
+ */
132
+ type OptionalKeysForTable<VC, TN> = TN extends keyof ExtractDefaultValuesConfig<VC> ? keyof ExtractDefaultValuesConfig<VC>[TN] : never;
133
+ /**
134
+ * Helper to check if a key exists in a type
135
+ */
136
+ type HasKey<T, K extends PropertyKey> = K extends keyof T ? true : false;
137
+ /**
138
+ * Extract the config type from protectedColumns.config
139
+ */
140
+ type ExtractProtectedColumnsConfig<VC> = VC extends {
141
+ protectedColumns: {
142
+ config: infer C;
143
+ };
144
+ } ? C : Record<string, never>;
145
+ /**
146
+ * Get protected column keys for a specific table.
147
+ * Returns the column names that should be omitted from patch() input.
148
+ */
149
+ type ProtectedKeysForTable<VC, TN> = TN extends keyof ExtractProtectedColumnsConfig<VC> ? ExtractProtectedColumnsConfig<VC>[TN] extends readonly (infer K)[] ? K : never : never;
150
+
151
+ export { type BaseConfigReturn as B, type DefaultValuesConfigData as D, type ExtractDefaultValuesConfig as E, type HasKey as H, type IndexConfigBaseOptions as I, type MakeOptional as M, type NormalizedIndexConfig as N, type OnFailArgs as O, type Prettify as P, type UniqueRowConfigData as U, type VerifyConfigInput as V, type OnFailCallback as a, type OptionalKeysForTable as b, type ExtractProtectedColumnsConfig as c, type ProtectedKeysForTable as d, type UniqueRowConfigEntry as e, type UniqueRowConfigOptions as f, type UniqueColumnConfigData as g, type UniqueColumnConfigEntry as h, type UniqueColumnConfigOptions as i, type IndexConfigEntry as j, type DMGeneric as k, type DefaultValuesInput as l, type ProtectedColumnsInput as m, normalizeIndexConfigEntry as n };
@@ -0,0 +1,151 @@
1
+ import { DataModelFromSchemaDefinition, SchemaDefinition, WithoutSystemFields, Indexes, NamedTableInfo } from 'convex/server';
2
+
3
+ type Prettify<T> = {
4
+ [K in keyof T]: T[K];
5
+ } & {};
6
+ type MakeOptional<T, K extends PropertyKey> = Prettify<Omit<T, K & keyof T> & Partial<Pick<T, K & keyof T>>>;
7
+ /**
8
+ * Base interface that all config functions should return.
9
+ * Each config type can have its own `verify` signature and additional properties.
10
+ */
11
+ type BaseConfigReturn = {
12
+ config: Record<string, any>;
13
+ };
14
+ type OnFailArgs<D> = {
15
+ uniqueColumn?: {
16
+ conflictingColumn: keyof D;
17
+ existingData: D;
18
+ };
19
+ uniqueRow?: {
20
+ existingData: D | null;
21
+ };
22
+ editableColumn?: {
23
+ removedColumns: string[];
24
+ filteredData: D;
25
+ };
26
+ requiredColumn?: {
27
+ missingColumn: keyof D;
28
+ };
29
+ };
30
+ type OnFailCallback<D> = (args: OnFailArgs<D>) => void;
31
+ type DMGeneric = DataModelFromSchemaDefinition<SchemaDefinition<any, boolean>>;
32
+ type DefaultValuesConfigData<DM extends DMGeneric> = {
33
+ [K in keyof DM]?: {
34
+ [column in keyof WithoutSystemFields<DM[K]['document']>]?: DM[K]['document'][column];
35
+ };
36
+ };
37
+ /**
38
+ * Base options shared by all index-based config entries.
39
+ * Individual plugins can extend this with their own options.
40
+ */
41
+ type IndexConfigBaseOptions = {
42
+ /** Additional identifiers to check if the existing row is the same document being updated */
43
+ identifiers?: string[];
44
+ };
45
+ /**
46
+ * A config entry that can be either:
47
+ * - A string (index name) for shorthand
48
+ * - An object with `index` and additional options
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * // These are equivalent:
53
+ * 'by_username'
54
+ * { index: 'by_username' }
55
+ *
56
+ * // With options:
57
+ * { index: 'by_username', identifiers: ['_id', 'userId'] }
58
+ * ```
59
+ */
60
+ type IndexConfigEntry<DM extends DMGeneric, K extends keyof DM, Options extends IndexConfigBaseOptions = IndexConfigBaseOptions> = keyof Indexes<NamedTableInfo<DM, K>> | ({
61
+ index: keyof Indexes<NamedTableInfo<DM, K>>;
62
+ identifiers?: (keyof NamedTableInfo<DM, K>['document'])[];
63
+ } & Omit<Options, 'identifiers'>);
64
+ /**
65
+ * Normalized form of an index config entry (always an object)
66
+ */
67
+ type NormalizedIndexConfig<Options extends IndexConfigBaseOptions = IndexConfigBaseOptions> = {
68
+ index: string;
69
+ identifiers: string[];
70
+ } & Omit<Options, 'identifiers'>;
71
+ /**
72
+ * Normalize a config entry to always have index and identifiers.
73
+ * Works for both string shorthand and full object configs.
74
+ */
75
+ declare function normalizeIndexConfigEntry<Options extends IndexConfigBaseOptions = IndexConfigBaseOptions>(entry: string | ({
76
+ index: string;
77
+ identifiers?: string[];
78
+ } & Omit<Options, 'identifiers'>), defaultIdentifiers?: string[]): NormalizedIndexConfig<Options>;
79
+ type UniqueRowConfigOptions = IndexConfigBaseOptions & {
80
+ queryExistingWithNullish?: boolean;
81
+ };
82
+ type UniqueRowConfigEntry<DM extends DMGeneric, K extends keyof DM> = IndexConfigEntry<DM, K, UniqueRowConfigOptions>;
83
+ type UniqueRowConfigData<DM extends DMGeneric> = {
84
+ [K in keyof DM]?: UniqueRowConfigEntry<DM, K>[];
85
+ };
86
+ type UniqueColumnConfigOptions = IndexConfigBaseOptions;
87
+ type UniqueColumnConfigEntry<DM extends DMGeneric, K extends keyof DM> = IndexConfigEntry<DM, K, UniqueColumnConfigOptions>;
88
+ type UniqueColumnConfigData<DM extends DMGeneric> = {
89
+ [K in keyof DM]?: UniqueColumnConfigEntry<DM, K>[];
90
+ };
91
+ /**
92
+ * Loose input types that accept any return from config functions.
93
+ * We use loose types here to avoid complex generic matching,
94
+ * then extract the specific config types using conditional types.
95
+ */
96
+ type DefaultValuesInput = {
97
+ _type: 'defaultValues';
98
+ verify: (tableName: any, data: any) => Promise<any>;
99
+ config: Record<string, Record<string, any>> | (() => Record<string, Record<string, any>> | Promise<Record<string, Record<string, any>>>);
100
+ };
101
+ /**
102
+ * Loose input type for protectedColumnsConfig return value.
103
+ */
104
+ type ProtectedColumnsInput = {
105
+ _type: 'protectedColumns';
106
+ config: Record<string, string[]>;
107
+ };
108
+ /**
109
+ * Config input for verifyConfig.
110
+ *
111
+ * - `defaultValues`: Transform plugin that makes fields optional (affects types)
112
+ * - `protectedColumns`: Columns that cannot be patched (affects patch() types)
113
+ * - `plugins`: Array of validate plugins (use for uniqueRow, uniqueColumn, custom plugins, etc.)
114
+ */
115
+ type VerifyConfigInput = {
116
+ defaultValues?: DefaultValuesInput;
117
+ protectedColumns?: ProtectedColumnsInput;
118
+ };
119
+ /**
120
+ * Extract the config type from defaultValues.config.
121
+ * Handles both direct object and function forms.
122
+ */
123
+ type ExtractDefaultValuesConfig<VC> = VC extends {
124
+ defaultValues: {
125
+ config: infer C;
126
+ };
127
+ } ? C extends () => infer R ? Awaited<R> : C : Record<string, never>;
128
+ /**
129
+ * Compute which keys should be optional for a given table based on all configs.
130
+ * Currently only defaultValues affects optionality.
131
+ */
132
+ type OptionalKeysForTable<VC, TN> = TN extends keyof ExtractDefaultValuesConfig<VC> ? keyof ExtractDefaultValuesConfig<VC>[TN] : never;
133
+ /**
134
+ * Helper to check if a key exists in a type
135
+ */
136
+ type HasKey<T, K extends PropertyKey> = K extends keyof T ? true : false;
137
+ /**
138
+ * Extract the config type from protectedColumns.config
139
+ */
140
+ type ExtractProtectedColumnsConfig<VC> = VC extends {
141
+ protectedColumns: {
142
+ config: infer C;
143
+ };
144
+ } ? C : Record<string, never>;
145
+ /**
146
+ * Get protected column keys for a specific table.
147
+ * Returns the column names that should be omitted from patch() input.
148
+ */
149
+ type ProtectedKeysForTable<VC, TN> = TN extends keyof ExtractProtectedColumnsConfig<VC> ? ExtractProtectedColumnsConfig<VC>[TN] extends readonly (infer K)[] ? K : never : never;
150
+
151
+ export { type BaseConfigReturn as B, type DefaultValuesConfigData as D, type ExtractDefaultValuesConfig as E, type HasKey as H, type IndexConfigBaseOptions as I, type MakeOptional as M, type NormalizedIndexConfig as N, type OnFailArgs as O, type Prettify as P, type UniqueRowConfigData as U, type VerifyConfigInput as V, type OnFailCallback as a, type OptionalKeysForTable as b, type ExtractProtectedColumnsConfig as c, type ProtectedKeysForTable as d, type UniqueRowConfigEntry as e, type UniqueRowConfigOptions as f, type UniqueColumnConfigData as g, type UniqueColumnConfigEntry as h, type UniqueColumnConfigOptions as i, type IndexConfigEntry as j, type DMGeneric as k, type DefaultValuesInput as l, type ProtectedColumnsInput as m, normalizeIndexConfigEntry as n };
@@ -0,0 +1,36 @@
1
+ import { SchemaDefinition, GenericSchema, DataModelFromSchemaDefinition, TableNamesInDataModel, DocumentByName } from 'convex/server';
2
+ import { I as IndexConfigBaseOptions, U as UniqueRowConfigData, N as NormalizedIndexConfig } from '../types-_64SXyva.mjs';
3
+ export { j as IndexConfigEntry, n as normalizeIndexConfigEntry } from '../types-_64SXyva.mjs';
4
+
5
+ /**
6
+ * Get Table indexes helper
7
+ *
8
+ * Note: this is using an experimental API in convex-js
9
+ * https://github.com/get-convex/convex-js/commit/04c3b44cab54c4d2230cce9312bdff074d54ab04
10
+ */
11
+ declare const getTableIndexes: <S extends SchemaDefinition<GenericSchema, boolean>, DataModel extends DataModelFromSchemaDefinition<S>, TN extends TableNamesInDataModel<DataModel>>(schema: S, tableName: TN) => {
12
+ indexDescriptor: string;
13
+ fields: string[];
14
+ }[];
15
+ /**
16
+ * Generate column data from fields and data object
17
+ */
18
+ declare const constructColumnData: <S extends SchemaDefinition<GenericSchema, boolean>, DataModel extends DataModelFromSchemaDefinition<S>, TN extends TableNamesInDataModel<DataModel>, D extends Partial<DocumentByName<DataModel, TN>>>(fields: string[], data: D, { allowNullishValue, allOrNothing, }: {
19
+ allowNullishValue?: boolean;
20
+ allOrNothing?: boolean;
21
+ }) => {
22
+ column: string;
23
+ value: DocumentByName<DataModel, TN>[string] | undefined;
24
+ }[] | null;
25
+ /**
26
+ * Construct index data from schema and config.
27
+ * Handles both string shorthand and full object config entries.
28
+ *
29
+ * @returns Array of normalized index configs with resolved field names from schema
30
+ */
31
+ declare const constructIndexData: <S extends SchemaDefinition<GenericSchema, boolean>, DataModel extends DataModelFromSchemaDefinition<S>, TN extends TableNamesInDataModel<DataModel>, Options extends IndexConfigBaseOptions = IndexConfigBaseOptions>(schema: S, tableName: TN, indexConfig?: UniqueRowConfigData<DataModel>) => (NormalizedIndexConfig<Options> & {
32
+ name: string;
33
+ fields: string[];
34
+ })[] | undefined;
35
+
36
+ export { IndexConfigBaseOptions, NormalizedIndexConfig, constructColumnData, constructIndexData, getTableIndexes };
@@ -0,0 +1,36 @@
1
+ import { SchemaDefinition, GenericSchema, DataModelFromSchemaDefinition, TableNamesInDataModel, DocumentByName } from 'convex/server';
2
+ import { I as IndexConfigBaseOptions, U as UniqueRowConfigData, N as NormalizedIndexConfig } from '../types-_64SXyva.js';
3
+ export { j as IndexConfigEntry, n as normalizeIndexConfigEntry } from '../types-_64SXyva.js';
4
+
5
+ /**
6
+ * Get Table indexes helper
7
+ *
8
+ * Note: this is using an experimental API in convex-js
9
+ * https://github.com/get-convex/convex-js/commit/04c3b44cab54c4d2230cce9312bdff074d54ab04
10
+ */
11
+ declare const getTableIndexes: <S extends SchemaDefinition<GenericSchema, boolean>, DataModel extends DataModelFromSchemaDefinition<S>, TN extends TableNamesInDataModel<DataModel>>(schema: S, tableName: TN) => {
12
+ indexDescriptor: string;
13
+ fields: string[];
14
+ }[];
15
+ /**
16
+ * Generate column data from fields and data object
17
+ */
18
+ declare const constructColumnData: <S extends SchemaDefinition<GenericSchema, boolean>, DataModel extends DataModelFromSchemaDefinition<S>, TN extends TableNamesInDataModel<DataModel>, D extends Partial<DocumentByName<DataModel, TN>>>(fields: string[], data: D, { allowNullishValue, allOrNothing, }: {
19
+ allowNullishValue?: boolean;
20
+ allOrNothing?: boolean;
21
+ }) => {
22
+ column: string;
23
+ value: DocumentByName<DataModel, TN>[string] | undefined;
24
+ }[] | null;
25
+ /**
26
+ * Construct index data from schema and config.
27
+ * Handles both string shorthand and full object config entries.
28
+ *
29
+ * @returns Array of normalized index configs with resolved field names from schema
30
+ */
31
+ declare const constructIndexData: <S extends SchemaDefinition<GenericSchema, boolean>, DataModel extends DataModelFromSchemaDefinition<S>, TN extends TableNamesInDataModel<DataModel>, Options extends IndexConfigBaseOptions = IndexConfigBaseOptions>(schema: S, tableName: TN, indexConfig?: UniqueRowConfigData<DataModel>) => (NormalizedIndexConfig<Options> & {
32
+ name: string;
33
+ fields: string[];
34
+ })[] | undefined;
35
+
36
+ export { IndexConfigBaseOptions, NormalizedIndexConfig, constructColumnData, constructIndexData, getTableIndexes };
@@ -0,0 +1,113 @@
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/utils/index.ts
21
+ var utils_exports = {};
22
+ __export(utils_exports, {
23
+ constructColumnData: () => constructColumnData,
24
+ constructIndexData: () => constructIndexData,
25
+ getTableIndexes: () => getTableIndexes,
26
+ normalizeIndexConfigEntry: () => normalizeIndexConfigEntry
27
+ });
28
+ module.exports = __toCommonJS(utils_exports);
29
+
30
+ // src/core/types.ts
31
+ function normalizeIndexConfigEntry(entry, defaultIdentifiers = ["_id"]) {
32
+ if (typeof entry === "string") {
33
+ return {
34
+ index: entry,
35
+ identifiers: defaultIdentifiers
36
+ };
37
+ }
38
+ const { index, identifiers, ...rest } = entry;
39
+ return {
40
+ index: String(index),
41
+ identifiers: identifiers?.map(String) ?? defaultIdentifiers,
42
+ ...rest
43
+ };
44
+ }
45
+
46
+ // src/utils/helpers.ts
47
+ var getTableIndexes = (schema, tableName) => {
48
+ return schema.tables[tableName][" indexes"]();
49
+ };
50
+ var constructColumnData = (fields, data, {
51
+ allowNullishValue = false,
52
+ allOrNothing = true
53
+ }) => {
54
+ const lengthOfFields = fields.length;
55
+ const columnData = fields.map((_, index) => {
56
+ const column = fields?.[index];
57
+ const value = data?.[column];
58
+ if (!column || !allowNullishValue && !value) {
59
+ return;
60
+ }
61
+ return {
62
+ column,
63
+ value
64
+ };
65
+ }).filter((e) => !!e);
66
+ if (allOrNothing && columnData.length !== lengthOfFields) {
67
+ console.warn(
68
+ "The index was NOT supplied with the same amount data as there was fields. This warning only appears when setting `allOrNothing` to `true`.",
69
+ "`fields: `",
70
+ fields,
71
+ "`columnData: `",
72
+ columnData
73
+ );
74
+ return null;
75
+ }
76
+ return columnData.length > 0 ? columnData : null;
77
+ };
78
+ var constructIndexData = (schema, tableName, indexConfig) => {
79
+ if (!indexConfig) {
80
+ return;
81
+ }
82
+ const tableConfig = indexConfig?.[tableName];
83
+ if (!tableConfig) {
84
+ return;
85
+ }
86
+ return tableConfig.map((entry) => {
87
+ const normalized = normalizeIndexConfigEntry(entry);
88
+ const { index, identifiers, ...rest } = normalized;
89
+ const fields = getTableIndexes(schema, tableName).find(
90
+ (i) => i.indexDescriptor == index
91
+ )?.fields;
92
+ if (!fields) {
93
+ throw new Error(`Error in 'constructIndexData()'. No fields found for index: [${index}]`);
94
+ }
95
+ const identifierMap = new Map(
96
+ [...identifiers, "_id"].map((i) => [String(i), String(i)])
97
+ );
98
+ return {
99
+ name: index,
100
+ fields,
101
+ identifiers: Array.from(identifierMap.values()),
102
+ ...rest
103
+ };
104
+ });
105
+ };
106
+ // Annotate the CommonJS export names for ESM import in node:
107
+ 0 && (module.exports = {
108
+ constructColumnData,
109
+ constructIndexData,
110
+ getTableIndexes,
111
+ normalizeIndexConfigEntry
112
+ });
113
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/index.ts","../../src/core/types.ts","../../src/utils/helpers.ts"],"sourcesContent":["export { getTableIndexes, constructColumnData, constructIndexData } from './helpers';\n\n// Re-export types from core for convenience\nexport { normalizeIndexConfigEntry } from '../core/types';\nexport type {\n\tNormalizedIndexConfig,\n\tIndexConfigBaseOptions,\n\tIndexConfigEntry,\n} from '../core/types';\n","import {\n\tDataModelFromSchemaDefinition,\n\tIndexes,\n\tNamedTableInfo,\n\tSchemaDefinition,\n\tWithoutSystemFields,\n} from 'convex/server';\n\n// =============================================================================\n// Utility Types\n// =============================================================================\n\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n\nexport type MakeOptional<T, K extends PropertyKey> = Prettify<\n\tOmit<T, K & keyof T> & Partial<Pick<T, K & keyof T>>\n>;\n\n// =============================================================================\n// Base Types for Config Functions\n// =============================================================================\n\n/**\n * Base interface that all config functions should return.\n * Each config type can have its own `verify` signature and additional properties.\n */\nexport type BaseConfigReturn = {\n\tconfig: Record<string, any>;\n};\n\n// =============================================================================\n// OnFail Types\n// =============================================================================\n\nexport type OnFailArgs<D> = {\n\tuniqueColumn?: {\n\t\tconflictingColumn: keyof D;\n\t\texistingData: D;\n\t};\n\tuniqueRow?: {\n\t\texistingData: D | null;\n\t};\n\teditableColumn?: {\n\t\tremovedColumns: string[];\n\t\tfilteredData: D;\n\t};\n\trequiredColumn?: {\n\t\tmissingColumn: keyof D;\n\t};\n};\n\nexport type OnFailCallback<D> = (args: OnFailArgs<D>) => void;\n\n// =============================================================================\n// Config Data Types (what the user provides)\n// =============================================================================\n\nexport type DMGeneric = DataModelFromSchemaDefinition<SchemaDefinition<any, boolean>>;\n\nexport type DefaultValuesConfigData<DM extends DMGeneric> = {\n\t[K in keyof DM]?: {\n\t\t[column in keyof WithoutSystemFields<DM[K]['document']>]?: DM[K]['document'][column];\n\t};\n};\n\n// =============================================================================\n// Index-Based Config Types (shared between uniqueRow, uniqueColumn, etc.)\n// =============================================================================\n\n/**\n * Base options shared by all index-based config entries.\n * Individual plugins can extend this with their own options.\n */\nexport type IndexConfigBaseOptions = {\n\t/** Additional identifiers to check if the existing row is the same document being updated */\n\tidentifiers?: string[];\n};\n\n/**\n * A config entry that can be either:\n * - A string (index name) for shorthand\n * - An object with `index` and additional options\n *\n * @example\n * ```ts\n * // These are equivalent:\n * 'by_username'\n * { index: 'by_username' }\n *\n * // With options:\n * { index: 'by_username', identifiers: ['_id', 'userId'] }\n * ```\n */\nexport type IndexConfigEntry<\n\tDM extends DMGeneric,\n\tK extends keyof DM,\n\tOptions extends IndexConfigBaseOptions = IndexConfigBaseOptions,\n> =\n\t| keyof Indexes<NamedTableInfo<DM, K>>\n\t| ({\n\t\t\tindex: keyof Indexes<NamedTableInfo<DM, K>>;\n\t\t\tidentifiers?: (keyof NamedTableInfo<DM, K>['document'])[];\n\t } & Omit<Options, 'identifiers'>);\n\n/**\n * Normalized form of an index config entry (always an object)\n */\nexport type NormalizedIndexConfig<Options extends IndexConfigBaseOptions = IndexConfigBaseOptions> =\n\t{\n\t\tindex: string;\n\t\tidentifiers: string[];\n\t} & Omit<Options, 'identifiers'>;\n\n/**\n * Normalize a config entry to always have index and identifiers.\n * Works for both string shorthand and full object configs.\n */\nexport function normalizeIndexConfigEntry<\n\tOptions extends IndexConfigBaseOptions = IndexConfigBaseOptions,\n>(\n\tentry: string | ({ index: string; identifiers?: string[] } & Omit<Options, 'identifiers'>),\n\tdefaultIdentifiers: string[] = ['_id']\n): NormalizedIndexConfig<Options> {\n\tif (typeof entry === 'string') {\n\t\treturn {\n\t\t\tindex: entry,\n\t\t\tidentifiers: defaultIdentifiers,\n\t\t} as NormalizedIndexConfig<Options>;\n\t}\n\n\tconst { index, identifiers, ...rest } = entry;\n\treturn {\n\t\tindex: String(index),\n\t\tidentifiers: identifiers?.map(String) ?? defaultIdentifiers,\n\t\t...rest,\n\t} as NormalizedIndexConfig<Options>;\n}\n\n// =============================================================================\n// UniqueRow Config Types\n// =============================================================================\n\nexport type UniqueRowConfigOptions = IndexConfigBaseOptions & {\n\tqueryExistingWithNullish?: boolean;\n};\n\nexport type UniqueRowConfigEntry<DM extends DMGeneric, K extends keyof DM> = IndexConfigEntry<\n\tDM,\n\tK,\n\tUniqueRowConfigOptions\n>;\n\nexport type UniqueRowConfigData<DM extends DMGeneric> = {\n\t[K in keyof DM]?: UniqueRowConfigEntry<DM, K>[];\n};\n\n// =============================================================================\n// UniqueColumn Config Types\n// =============================================================================\n\nexport type UniqueColumnConfigOptions = IndexConfigBaseOptions;\n\nexport type UniqueColumnConfigEntry<DM extends DMGeneric, K extends keyof DM> = IndexConfigEntry<\n\tDM,\n\tK,\n\tUniqueColumnConfigOptions\n>;\n\nexport type UniqueColumnConfigData<DM extends DMGeneric> = {\n\t[K in keyof DM]?: UniqueColumnConfigEntry<DM, K>[];\n};\n\n// =============================================================================\n// Input Types (loose types for verifyConfig to accept)\n// =============================================================================\n\n/**\n * Loose input types that accept any return from config functions.\n * We use loose types here to avoid complex generic matching,\n * then extract the specific config types using conditional types.\n */\nexport type DefaultValuesInput = {\n\t_type: 'defaultValues';\n\tverify: (tableName: any, data: any) => Promise<any>;\n\tconfig:\n\t\t| Record<string, Record<string, any>>\n\t\t| (() => Record<string, Record<string, any>> | Promise<Record<string, Record<string, any>>>);\n};\n\n/**\n * Loose input type for protectedColumnsConfig return value.\n */\nexport type ProtectedColumnsInput = {\n\t_type: 'protectedColumns';\n\tconfig: Record<string, string[]>;\n};\n\n// =============================================================================\n// Object-Based Types (for verifyConfig)\n// =============================================================================\n\n/**\n * Config input for verifyConfig.\n *\n * - `defaultValues`: Transform plugin that makes fields optional (affects types)\n * - `protectedColumns`: Columns that cannot be patched (affects patch() types)\n * - `plugins`: Array of validate plugins (use for uniqueRow, uniqueColumn, custom plugins, etc.)\n */\nexport type VerifyConfigInput = {\n\tdefaultValues?: DefaultValuesInput;\n\tprotectedColumns?: ProtectedColumnsInput;\n};\n\n// =============================================================================\n// Type Extraction Helpers\n// =============================================================================\n\n/**\n * Extract the config type from defaultValues.config.\n * Handles both direct object and function forms.\n */\nexport type ExtractDefaultValuesConfig<VC> = VC extends {\n\tdefaultValues: { config: infer C };\n}\n\t? C extends () => infer R\n\t\t? Awaited<R>\n\t\t: C\n\t: Record<string, never>;\n\n/**\n * Compute which keys should be optional for a given table based on all configs.\n * Currently only defaultValues affects optionality.\n */\nexport type OptionalKeysForTable<VC, TN> = TN extends keyof ExtractDefaultValuesConfig<VC>\n\t? keyof ExtractDefaultValuesConfig<VC>[TN]\n\t: never;\n\n/**\n * Helper to check if a key exists in a type\n */\nexport type HasKey<T, K extends PropertyKey> = K extends keyof T ? true : false;\n\n// =============================================================================\n// Protected Columns Type Extraction\n// =============================================================================\n\n/**\n * Extract the config type from protectedColumns.config\n */\nexport type ExtractProtectedColumnsConfig<VC> = VC extends {\n\tprotectedColumns: { config: infer C };\n}\n\t? C\n\t: Record<string, never>;\n\n/**\n * Get protected column keys for a specific table.\n * Returns the column names that should be omitted from patch() input.\n */\nexport type ProtectedKeysForTable<VC, TN> = TN extends keyof ExtractProtectedColumnsConfig<VC>\n\t? ExtractProtectedColumnsConfig<VC>[TN] extends readonly (infer K)[]\n\t\t? K\n\t\t: never\n\t: never;\n","import {\n\tDataModelFromSchemaDefinition,\n\tDocumentByName,\n\tGenericSchema,\n\tSchemaDefinition,\n\tTableNamesInDataModel,\n} from 'convex/server';\n\nimport {\n\tIndexConfigBaseOptions,\n\tNormalizedIndexConfig,\n\tnormalizeIndexConfigEntry,\n\tUniqueRowConfigData,\n} from '../core/types';\n\n/**\n * Get Table indexes helper\n *\n * Note: this is using an experimental API in convex-js\n * https://github.com/get-convex/convex-js/commit/04c3b44cab54c4d2230cce9312bdff074d54ab04\n */\nexport const getTableIndexes = <\n\tS extends SchemaDefinition<GenericSchema, boolean>,\n\tDataModel extends DataModelFromSchemaDefinition<S>,\n\tTN extends TableNamesInDataModel<DataModel>,\n>(\n\tschema: S,\n\ttableName: TN\n) => {\n\treturn schema.tables[tableName][' indexes']();\n};\n\n/**\n * Generate column data from fields and data object\n */\nexport const constructColumnData = <\n\tS extends SchemaDefinition<GenericSchema, boolean>,\n\tDataModel extends DataModelFromSchemaDefinition<S>,\n\tTN extends TableNamesInDataModel<DataModel>,\n\tD extends Partial<DocumentByName<DataModel, TN>>,\n>(\n\tfields: string[],\n\tdata: D,\n\t{\n\t\tallowNullishValue = false,\n\t\tallOrNothing = true,\n\t}: {\n\t\tallowNullishValue?: boolean;\n\t\tallOrNothing?: boolean;\n\t}\n) => {\n\tconst lengthOfFields = fields.length;\n\n\tconst columnData = fields\n\t\t.map((_, index) => {\n\t\t\tconst column = fields?.[index];\n\t\t\tconst value = data?.[column];\n\n\t\t\tif (!column || (!allowNullishValue && !value)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcolumn,\n\t\t\t\tvalue,\n\t\t\t};\n\t\t})\n\t\t.filter((e) => !!e);\n\n\tif (allOrNothing && columnData.length !== lengthOfFields) {\n\t\tconsole.warn(\n\t\t\t'The index was NOT supplied with the same amount data as there was fields. This warning only appears when setting `allOrNothing` to `true`.',\n\t\t\t'`fields: `',\n\t\t\tfields,\n\t\t\t'`columnData: `',\n\t\t\tcolumnData\n\t\t);\n\t\treturn null;\n\t}\n\n\treturn columnData.length > 0 ? columnData : null;\n};\n\n/**\n * Construct index data from schema and config.\n * Handles both string shorthand and full object config entries.\n *\n * @returns Array of normalized index configs with resolved field names from schema\n */\nexport const constructIndexData = <\n\tS extends SchemaDefinition<GenericSchema, boolean>,\n\tDataModel extends DataModelFromSchemaDefinition<S>,\n\tTN extends TableNamesInDataModel<DataModel>,\n\tOptions extends IndexConfigBaseOptions = IndexConfigBaseOptions,\n>(\n\tschema: S,\n\ttableName: TN,\n\tindexConfig?: UniqueRowConfigData<DataModel>\n): (NormalizedIndexConfig<Options> & { name: string; fields: string[] })[] | undefined => {\n\tif (!indexConfig) {\n\t\treturn;\n\t}\n\n\tconst tableConfig = indexConfig?.[tableName];\n\tif (!tableConfig) {\n\t\treturn;\n\t}\n\n\treturn tableConfig.map((entry) => {\n\t\t// Normalize the entry (handles both string and object forms)\n\t\tconst normalized = normalizeIndexConfigEntry<Options>(entry as any);\n\t\tconst { index, identifiers, ...rest } = normalized;\n\n\t\tconst fields = getTableIndexes(schema, tableName).find(\n\t\t\t(i) => i.indexDescriptor == index\n\t\t)?.fields;\n\n\t\tif (!fields) {\n\t\t\tthrow new Error(`Error in 'constructIndexData()'. No fields found for index: [${index}]`);\n\t\t}\n\n\t\t// Create a unique map in case there is any overlap in identifiers\n\t\t// Always include '_id' as a fallback identifier\n\t\tconst identifierMap = new Map<string, string>(\n\t\t\t[...identifiers, '_id'].map((i) => [String(i), String(i)])\n\t\t);\n\n\t\treturn {\n\t\t\tname: index,\n\t\t\tfields,\n\t\t\tidentifiers: Array.from(identifierMap.values()),\n\t\t\t...rest,\n\t\t} as NormalizedIndexConfig<Options> & { name: string; fields: string[] };\n\t});\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACqHO,SAAS,0BAGf,OACA,qBAA+B,CAAC,KAAK,GACJ;AACjC,MAAI,OAAO,UAAU,UAAU;AAC9B,WAAO;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IACd;AAAA,EACD;AAEA,QAAM,EAAE,OAAO,aAAa,GAAG,KAAK,IAAI;AACxC,SAAO;AAAA,IACN,OAAO,OAAO,KAAK;AAAA,IACnB,aAAa,aAAa,IAAI,MAAM,KAAK;AAAA,IACzC,GAAG;AAAA,EACJ;AACD;;;ACnHO,IAAM,kBAAkB,CAK9B,QACA,cACI;AACJ,SAAO,OAAO,OAAO,SAAS,EAAE,UAAU,EAAE;AAC7C;AAKO,IAAM,sBAAsB,CAMlC,QACA,MACA;AAAA,EACC,oBAAoB;AAAA,EACpB,eAAe;AAChB,MAII;AACJ,QAAM,iBAAiB,OAAO;AAE9B,QAAM,aAAa,OACjB,IAAI,CAAC,GAAG,UAAU;AAClB,UAAM,SAAS,SAAS,KAAK;AAC7B,UAAM,QAAQ,OAAO,MAAM;AAE3B,QAAI,CAAC,UAAW,CAAC,qBAAqB,CAAC,OAAQ;AAC9C;AAAA,IACD;AAEA,WAAO;AAAA,MACN;AAAA,MACA;AAAA,IACD;AAAA,EACD,CAAC,EACA,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAEnB,MAAI,gBAAgB,WAAW,WAAW,gBAAgB;AACzD,YAAQ;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAEA,SAAO,WAAW,SAAS,IAAI,aAAa;AAC7C;AAQO,IAAM,qBAAqB,CAMjC,QACA,WACA,gBACyF;AACzF,MAAI,CAAC,aAAa;AACjB;AAAA,EACD;AAEA,QAAM,cAAc,cAAc,SAAS;AAC3C,MAAI,CAAC,aAAa;AACjB;AAAA,EACD;AAEA,SAAO,YAAY,IAAI,CAAC,UAAU;AAEjC,UAAM,aAAa,0BAAmC,KAAY;AAClE,UAAM,EAAE,OAAO,aAAa,GAAG,KAAK,IAAI;AAExC,UAAM,SAAS,gBAAgB,QAAQ,SAAS,EAAE;AAAA,MACjD,CAAC,MAAM,EAAE,mBAAmB;AAAA,IAC7B,GAAG;AAEH,QAAI,CAAC,QAAQ;AACZ,YAAM,IAAI,MAAM,gEAAgE,KAAK,GAAG;AAAA,IACzF;AAIA,UAAM,gBAAgB,IAAI;AAAA,MACzB,CAAC,GAAG,aAAa,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;AAAA,IAC1D;AAEA,WAAO;AAAA,MACN,MAAM;AAAA,MACN;AAAA,MACA,aAAa,MAAM,KAAK,cAAc,OAAO,CAAC;AAAA,MAC9C,GAAG;AAAA,IACJ;AAAA,EACD,CAAC;AACF;","names":[]}
@@ -0,0 +1,83 @@
1
+ // src/core/types.ts
2
+ function normalizeIndexConfigEntry(entry, defaultIdentifiers = ["_id"]) {
3
+ if (typeof entry === "string") {
4
+ return {
5
+ index: entry,
6
+ identifiers: defaultIdentifiers
7
+ };
8
+ }
9
+ const { index, identifiers, ...rest } = entry;
10
+ return {
11
+ index: String(index),
12
+ identifiers: identifiers?.map(String) ?? defaultIdentifiers,
13
+ ...rest
14
+ };
15
+ }
16
+
17
+ // src/utils/helpers.ts
18
+ var getTableIndexes = (schema, tableName) => {
19
+ return schema.tables[tableName][" indexes"]();
20
+ };
21
+ var constructColumnData = (fields, data, {
22
+ allowNullishValue = false,
23
+ allOrNothing = true
24
+ }) => {
25
+ const lengthOfFields = fields.length;
26
+ const columnData = fields.map((_, index) => {
27
+ const column = fields?.[index];
28
+ const value = data?.[column];
29
+ if (!column || !allowNullishValue && !value) {
30
+ return;
31
+ }
32
+ return {
33
+ column,
34
+ value
35
+ };
36
+ }).filter((e) => !!e);
37
+ if (allOrNothing && columnData.length !== lengthOfFields) {
38
+ console.warn(
39
+ "The index was NOT supplied with the same amount data as there was fields. This warning only appears when setting `allOrNothing` to `true`.",
40
+ "`fields: `",
41
+ fields,
42
+ "`columnData: `",
43
+ columnData
44
+ );
45
+ return null;
46
+ }
47
+ return columnData.length > 0 ? columnData : null;
48
+ };
49
+ var constructIndexData = (schema, tableName, indexConfig) => {
50
+ if (!indexConfig) {
51
+ return;
52
+ }
53
+ const tableConfig = indexConfig?.[tableName];
54
+ if (!tableConfig) {
55
+ return;
56
+ }
57
+ return tableConfig.map((entry) => {
58
+ const normalized = normalizeIndexConfigEntry(entry);
59
+ const { index, identifiers, ...rest } = normalized;
60
+ const fields = getTableIndexes(schema, tableName).find(
61
+ (i) => i.indexDescriptor == index
62
+ )?.fields;
63
+ if (!fields) {
64
+ throw new Error(`Error in 'constructIndexData()'. No fields found for index: [${index}]`);
65
+ }
66
+ const identifierMap = new Map(
67
+ [...identifiers, "_id"].map((i) => [String(i), String(i)])
68
+ );
69
+ return {
70
+ name: index,
71
+ fields,
72
+ identifiers: Array.from(identifierMap.values()),
73
+ ...rest
74
+ };
75
+ });
76
+ };
77
+ export {
78
+ constructColumnData,
79
+ constructIndexData,
80
+ getTableIndexes,
81
+ normalizeIndexConfigEntry
82
+ };
83
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/types.ts","../../src/utils/helpers.ts"],"sourcesContent":["import {\n\tDataModelFromSchemaDefinition,\n\tIndexes,\n\tNamedTableInfo,\n\tSchemaDefinition,\n\tWithoutSystemFields,\n} from 'convex/server';\n\n// =============================================================================\n// Utility Types\n// =============================================================================\n\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n\nexport type MakeOptional<T, K extends PropertyKey> = Prettify<\n\tOmit<T, K & keyof T> & Partial<Pick<T, K & keyof T>>\n>;\n\n// =============================================================================\n// Base Types for Config Functions\n// =============================================================================\n\n/**\n * Base interface that all config functions should return.\n * Each config type can have its own `verify` signature and additional properties.\n */\nexport type BaseConfigReturn = {\n\tconfig: Record<string, any>;\n};\n\n// =============================================================================\n// OnFail Types\n// =============================================================================\n\nexport type OnFailArgs<D> = {\n\tuniqueColumn?: {\n\t\tconflictingColumn: keyof D;\n\t\texistingData: D;\n\t};\n\tuniqueRow?: {\n\t\texistingData: D | null;\n\t};\n\teditableColumn?: {\n\t\tremovedColumns: string[];\n\t\tfilteredData: D;\n\t};\n\trequiredColumn?: {\n\t\tmissingColumn: keyof D;\n\t};\n};\n\nexport type OnFailCallback<D> = (args: OnFailArgs<D>) => void;\n\n// =============================================================================\n// Config Data Types (what the user provides)\n// =============================================================================\n\nexport type DMGeneric = DataModelFromSchemaDefinition<SchemaDefinition<any, boolean>>;\n\nexport type DefaultValuesConfigData<DM extends DMGeneric> = {\n\t[K in keyof DM]?: {\n\t\t[column in keyof WithoutSystemFields<DM[K]['document']>]?: DM[K]['document'][column];\n\t};\n};\n\n// =============================================================================\n// Index-Based Config Types (shared between uniqueRow, uniqueColumn, etc.)\n// =============================================================================\n\n/**\n * Base options shared by all index-based config entries.\n * Individual plugins can extend this with their own options.\n */\nexport type IndexConfigBaseOptions = {\n\t/** Additional identifiers to check if the existing row is the same document being updated */\n\tidentifiers?: string[];\n};\n\n/**\n * A config entry that can be either:\n * - A string (index name) for shorthand\n * - An object with `index` and additional options\n *\n * @example\n * ```ts\n * // These are equivalent:\n * 'by_username'\n * { index: 'by_username' }\n *\n * // With options:\n * { index: 'by_username', identifiers: ['_id', 'userId'] }\n * ```\n */\nexport type IndexConfigEntry<\n\tDM extends DMGeneric,\n\tK extends keyof DM,\n\tOptions extends IndexConfigBaseOptions = IndexConfigBaseOptions,\n> =\n\t| keyof Indexes<NamedTableInfo<DM, K>>\n\t| ({\n\t\t\tindex: keyof Indexes<NamedTableInfo<DM, K>>;\n\t\t\tidentifiers?: (keyof NamedTableInfo<DM, K>['document'])[];\n\t } & Omit<Options, 'identifiers'>);\n\n/**\n * Normalized form of an index config entry (always an object)\n */\nexport type NormalizedIndexConfig<Options extends IndexConfigBaseOptions = IndexConfigBaseOptions> =\n\t{\n\t\tindex: string;\n\t\tidentifiers: string[];\n\t} & Omit<Options, 'identifiers'>;\n\n/**\n * Normalize a config entry to always have index and identifiers.\n * Works for both string shorthand and full object configs.\n */\nexport function normalizeIndexConfigEntry<\n\tOptions extends IndexConfigBaseOptions = IndexConfigBaseOptions,\n>(\n\tentry: string | ({ index: string; identifiers?: string[] } & Omit<Options, 'identifiers'>),\n\tdefaultIdentifiers: string[] = ['_id']\n): NormalizedIndexConfig<Options> {\n\tif (typeof entry === 'string') {\n\t\treturn {\n\t\t\tindex: entry,\n\t\t\tidentifiers: defaultIdentifiers,\n\t\t} as NormalizedIndexConfig<Options>;\n\t}\n\n\tconst { index, identifiers, ...rest } = entry;\n\treturn {\n\t\tindex: String(index),\n\t\tidentifiers: identifiers?.map(String) ?? defaultIdentifiers,\n\t\t...rest,\n\t} as NormalizedIndexConfig<Options>;\n}\n\n// =============================================================================\n// UniqueRow Config Types\n// =============================================================================\n\nexport type UniqueRowConfigOptions = IndexConfigBaseOptions & {\n\tqueryExistingWithNullish?: boolean;\n};\n\nexport type UniqueRowConfigEntry<DM extends DMGeneric, K extends keyof DM> = IndexConfigEntry<\n\tDM,\n\tK,\n\tUniqueRowConfigOptions\n>;\n\nexport type UniqueRowConfigData<DM extends DMGeneric> = {\n\t[K in keyof DM]?: UniqueRowConfigEntry<DM, K>[];\n};\n\n// =============================================================================\n// UniqueColumn Config Types\n// =============================================================================\n\nexport type UniqueColumnConfigOptions = IndexConfigBaseOptions;\n\nexport type UniqueColumnConfigEntry<DM extends DMGeneric, K extends keyof DM> = IndexConfigEntry<\n\tDM,\n\tK,\n\tUniqueColumnConfigOptions\n>;\n\nexport type UniqueColumnConfigData<DM extends DMGeneric> = {\n\t[K in keyof DM]?: UniqueColumnConfigEntry<DM, K>[];\n};\n\n// =============================================================================\n// Input Types (loose types for verifyConfig to accept)\n// =============================================================================\n\n/**\n * Loose input types that accept any return from config functions.\n * We use loose types here to avoid complex generic matching,\n * then extract the specific config types using conditional types.\n */\nexport type DefaultValuesInput = {\n\t_type: 'defaultValues';\n\tverify: (tableName: any, data: any) => Promise<any>;\n\tconfig:\n\t\t| Record<string, Record<string, any>>\n\t\t| (() => Record<string, Record<string, any>> | Promise<Record<string, Record<string, any>>>);\n};\n\n/**\n * Loose input type for protectedColumnsConfig return value.\n */\nexport type ProtectedColumnsInput = {\n\t_type: 'protectedColumns';\n\tconfig: Record<string, string[]>;\n};\n\n// =============================================================================\n// Object-Based Types (for verifyConfig)\n// =============================================================================\n\n/**\n * Config input for verifyConfig.\n *\n * - `defaultValues`: Transform plugin that makes fields optional (affects types)\n * - `protectedColumns`: Columns that cannot be patched (affects patch() types)\n * - `plugins`: Array of validate plugins (use for uniqueRow, uniqueColumn, custom plugins, etc.)\n */\nexport type VerifyConfigInput = {\n\tdefaultValues?: DefaultValuesInput;\n\tprotectedColumns?: ProtectedColumnsInput;\n};\n\n// =============================================================================\n// Type Extraction Helpers\n// =============================================================================\n\n/**\n * Extract the config type from defaultValues.config.\n * Handles both direct object and function forms.\n */\nexport type ExtractDefaultValuesConfig<VC> = VC extends {\n\tdefaultValues: { config: infer C };\n}\n\t? C extends () => infer R\n\t\t? Awaited<R>\n\t\t: C\n\t: Record<string, never>;\n\n/**\n * Compute which keys should be optional for a given table based on all configs.\n * Currently only defaultValues affects optionality.\n */\nexport type OptionalKeysForTable<VC, TN> = TN extends keyof ExtractDefaultValuesConfig<VC>\n\t? keyof ExtractDefaultValuesConfig<VC>[TN]\n\t: never;\n\n/**\n * Helper to check if a key exists in a type\n */\nexport type HasKey<T, K extends PropertyKey> = K extends keyof T ? true : false;\n\n// =============================================================================\n// Protected Columns Type Extraction\n// =============================================================================\n\n/**\n * Extract the config type from protectedColumns.config\n */\nexport type ExtractProtectedColumnsConfig<VC> = VC extends {\n\tprotectedColumns: { config: infer C };\n}\n\t? C\n\t: Record<string, never>;\n\n/**\n * Get protected column keys for a specific table.\n * Returns the column names that should be omitted from patch() input.\n */\nexport type ProtectedKeysForTable<VC, TN> = TN extends keyof ExtractProtectedColumnsConfig<VC>\n\t? ExtractProtectedColumnsConfig<VC>[TN] extends readonly (infer K)[]\n\t\t? K\n\t\t: never\n\t: never;\n","import {\n\tDataModelFromSchemaDefinition,\n\tDocumentByName,\n\tGenericSchema,\n\tSchemaDefinition,\n\tTableNamesInDataModel,\n} from 'convex/server';\n\nimport {\n\tIndexConfigBaseOptions,\n\tNormalizedIndexConfig,\n\tnormalizeIndexConfigEntry,\n\tUniqueRowConfigData,\n} from '../core/types';\n\n/**\n * Get Table indexes helper\n *\n * Note: this is using an experimental API in convex-js\n * https://github.com/get-convex/convex-js/commit/04c3b44cab54c4d2230cce9312bdff074d54ab04\n */\nexport const getTableIndexes = <\n\tS extends SchemaDefinition<GenericSchema, boolean>,\n\tDataModel extends DataModelFromSchemaDefinition<S>,\n\tTN extends TableNamesInDataModel<DataModel>,\n>(\n\tschema: S,\n\ttableName: TN\n) => {\n\treturn schema.tables[tableName][' indexes']();\n};\n\n/**\n * Generate column data from fields and data object\n */\nexport const constructColumnData = <\n\tS extends SchemaDefinition<GenericSchema, boolean>,\n\tDataModel extends DataModelFromSchemaDefinition<S>,\n\tTN extends TableNamesInDataModel<DataModel>,\n\tD extends Partial<DocumentByName<DataModel, TN>>,\n>(\n\tfields: string[],\n\tdata: D,\n\t{\n\t\tallowNullishValue = false,\n\t\tallOrNothing = true,\n\t}: {\n\t\tallowNullishValue?: boolean;\n\t\tallOrNothing?: boolean;\n\t}\n) => {\n\tconst lengthOfFields = fields.length;\n\n\tconst columnData = fields\n\t\t.map((_, index) => {\n\t\t\tconst column = fields?.[index];\n\t\t\tconst value = data?.[column];\n\n\t\t\tif (!column || (!allowNullishValue && !value)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcolumn,\n\t\t\t\tvalue,\n\t\t\t};\n\t\t})\n\t\t.filter((e) => !!e);\n\n\tif (allOrNothing && columnData.length !== lengthOfFields) {\n\t\tconsole.warn(\n\t\t\t'The index was NOT supplied with the same amount data as there was fields. This warning only appears when setting `allOrNothing` to `true`.',\n\t\t\t'`fields: `',\n\t\t\tfields,\n\t\t\t'`columnData: `',\n\t\t\tcolumnData\n\t\t);\n\t\treturn null;\n\t}\n\n\treturn columnData.length > 0 ? columnData : null;\n};\n\n/**\n * Construct index data from schema and config.\n * Handles both string shorthand and full object config entries.\n *\n * @returns Array of normalized index configs with resolved field names from schema\n */\nexport const constructIndexData = <\n\tS extends SchemaDefinition<GenericSchema, boolean>,\n\tDataModel extends DataModelFromSchemaDefinition<S>,\n\tTN extends TableNamesInDataModel<DataModel>,\n\tOptions extends IndexConfigBaseOptions = IndexConfigBaseOptions,\n>(\n\tschema: S,\n\ttableName: TN,\n\tindexConfig?: UniqueRowConfigData<DataModel>\n): (NormalizedIndexConfig<Options> & { name: string; fields: string[] })[] | undefined => {\n\tif (!indexConfig) {\n\t\treturn;\n\t}\n\n\tconst tableConfig = indexConfig?.[tableName];\n\tif (!tableConfig) {\n\t\treturn;\n\t}\n\n\treturn tableConfig.map((entry) => {\n\t\t// Normalize the entry (handles both string and object forms)\n\t\tconst normalized = normalizeIndexConfigEntry<Options>(entry as any);\n\t\tconst { index, identifiers, ...rest } = normalized;\n\n\t\tconst fields = getTableIndexes(schema, tableName).find(\n\t\t\t(i) => i.indexDescriptor == index\n\t\t)?.fields;\n\n\t\tif (!fields) {\n\t\t\tthrow new Error(`Error in 'constructIndexData()'. No fields found for index: [${index}]`);\n\t\t}\n\n\t\t// Create a unique map in case there is any overlap in identifiers\n\t\t// Always include '_id' as a fallback identifier\n\t\tconst identifierMap = new Map<string, string>(\n\t\t\t[...identifiers, '_id'].map((i) => [String(i), String(i)])\n\t\t);\n\n\t\treturn {\n\t\t\tname: index,\n\t\t\tfields,\n\t\t\tidentifiers: Array.from(identifierMap.values()),\n\t\t\t...rest,\n\t\t} as NormalizedIndexConfig<Options> & { name: string; fields: string[] };\n\t});\n};\n"],"mappings":";AAqHO,SAAS,0BAGf,OACA,qBAA+B,CAAC,KAAK,GACJ;AACjC,MAAI,OAAO,UAAU,UAAU;AAC9B,WAAO;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IACd;AAAA,EACD;AAEA,QAAM,EAAE,OAAO,aAAa,GAAG,KAAK,IAAI;AACxC,SAAO;AAAA,IACN,OAAO,OAAO,KAAK;AAAA,IACnB,aAAa,aAAa,IAAI,MAAM,KAAK;AAAA,IACzC,GAAG;AAAA,EACJ;AACD;;;ACnHO,IAAM,kBAAkB,CAK9B,QACA,cACI;AACJ,SAAO,OAAO,OAAO,SAAS,EAAE,UAAU,EAAE;AAC7C;AAKO,IAAM,sBAAsB,CAMlC,QACA,MACA;AAAA,EACC,oBAAoB;AAAA,EACpB,eAAe;AAChB,MAII;AACJ,QAAM,iBAAiB,OAAO;AAE9B,QAAM,aAAa,OACjB,IAAI,CAAC,GAAG,UAAU;AAClB,UAAM,SAAS,SAAS,KAAK;AAC7B,UAAM,QAAQ,OAAO,MAAM;AAE3B,QAAI,CAAC,UAAW,CAAC,qBAAqB,CAAC,OAAQ;AAC9C;AAAA,IACD;AAEA,WAAO;AAAA,MACN;AAAA,MACA;AAAA,IACD;AAAA,EACD,CAAC,EACA,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAEnB,MAAI,gBAAgB,WAAW,WAAW,gBAAgB;AACzD,YAAQ;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAEA,SAAO,WAAW,SAAS,IAAI,aAAa;AAC7C;AAQO,IAAM,qBAAqB,CAMjC,QACA,WACA,gBACyF;AACzF,MAAI,CAAC,aAAa;AACjB;AAAA,EACD;AAEA,QAAM,cAAc,cAAc,SAAS;AAC3C,MAAI,CAAC,aAAa;AACjB;AAAA,EACD;AAEA,SAAO,YAAY,IAAI,CAAC,UAAU;AAEjC,UAAM,aAAa,0BAAmC,KAAY;AAClE,UAAM,EAAE,OAAO,aAAa,GAAG,KAAK,IAAI;AAExC,UAAM,SAAS,gBAAgB,QAAQ,SAAS,EAAE;AAAA,MACjD,CAAC,MAAM,EAAE,mBAAmB;AAAA,IAC7B,GAAG;AAEH,QAAI,CAAC,QAAQ;AACZ,YAAM,IAAI,MAAM,gEAAgE,KAAK,GAAG;AAAA,IACzF;AAIA,UAAM,gBAAgB,IAAI;AAAA,MACzB,CAAC,GAAG,aAAa,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;AAAA,IAC1D;AAEA,WAAO;AAAA,MACN,MAAM;AAAA,MACN;AAAA,MACA,aAAa,MAAM,KAAK,cAAc,OAAO,CAAC;AAAA,MAC9C,GAAG;AAAA,IACJ;AAAA,EACD,CAAC;AACF;","names":[]}