better-convex 0.8.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aggregate/index.d.ts +1 -2
- package/dist/aggregate/index.js +1 -1
- package/dist/cli.mjs +16 -1
- package/dist/{id-DdAxiGby.js → id-BF_SaWhQ.js} +1 -1
- package/dist/orm/index.d.ts +2 -2
- package/dist/orm/index.js +98 -90
- package/dist/plugins/index.d.ts +4 -2
- package/dist/plugins/index.js +1 -1
- package/dist/plugins/ratelimit/index.d.ts +1 -2
- package/dist/plugins/ratelimit/index.js +2 -2
- package/dist/{runtime-oWZgeWOJ.js → runtime-B20PP4Qr.js} +2 -2
- package/dist/{schema-DVFZAlKx.js → schema-Cw5-LWTg.js} +2 -2
- package/dist/{table-B7yzBihE.js → table-Bxqm450r.js} +107 -3
- package/dist/{text-enum-CFdcLUuw.js → text-enum-KyijdQ8Q.js} +1 -1
- package/dist/{where-clause-compiler-_b1UHbYW.d.ts → where-clause-compiler-UavDdMUX.d.ts} +180 -163
- package/package.json +1 -1
|
@@ -529,6 +529,7 @@ const OrmContext = Symbol.for("better-convex:OrmContext");
|
|
|
529
529
|
const RlsPolicies = Symbol.for("better-convex:RlsPolicies");
|
|
530
530
|
const EnableRLS = Symbol.for("better-convex:EnableRLS");
|
|
531
531
|
const TableDeleteConfig = Symbol.for("better-convex:TableDeleteConfig");
|
|
532
|
+
const TablePolymorphic = Symbol.for("better-convex:TablePolymorphic");
|
|
532
533
|
const OrmSchemaOptions = Symbol.for("better-convex:OrmSchemaOptions");
|
|
533
534
|
const OrmSchemaDefinition = Symbol.for("better-convex:OrmSchemaDefinition");
|
|
534
535
|
const OrmSchemaPluginTables = Symbol.for("better-convex:OrmSchemaPluginTables");
|
|
@@ -544,6 +545,8 @@ const RESERVED_COLUMN_NAMES = new Set([
|
|
|
544
545
|
"_id",
|
|
545
546
|
"_creationTime"
|
|
546
547
|
]);
|
|
548
|
+
const DEFAULT_POLYMORPHIC_ALIAS = "details";
|
|
549
|
+
const CONVEX_TABLE_FIELD_LIMIT = 1024;
|
|
547
550
|
/**
|
|
548
551
|
* Valid table name pattern: starts with letter/underscore, contains only alphanumeric and underscore
|
|
549
552
|
*/
|
|
@@ -568,6 +571,21 @@ function createValidatorFromColumns(columns) {
|
|
|
568
571
|
const validatorFields = Object.fromEntries(Object.entries(columns).map(([key, builder]) => [key, builder.convexValidator]));
|
|
569
572
|
return v.object(validatorFields);
|
|
570
573
|
}
|
|
574
|
+
function discriminator(config) {
|
|
575
|
+
if (!config || typeof config !== "object") throw new Error("discriminator(...) requires a config object.");
|
|
576
|
+
if (!config.variants || typeof config.variants !== "object" || Object.keys(config.variants).length === 0) throw new Error("discriminator(...).variants must contain at least one case.");
|
|
577
|
+
if (config.as !== void 0 && (typeof config.as !== "string" || config.as.length === 0)) throw new Error("discriminator(...).as must be a non-empty string when set.");
|
|
578
|
+
const builder = text().notNull();
|
|
579
|
+
builder.__polymorphic = {
|
|
580
|
+
as: config.as ?? DEFAULT_POLYMORPHIC_ALIAS,
|
|
581
|
+
variants: config.variants
|
|
582
|
+
};
|
|
583
|
+
builder.config.discriminator = {
|
|
584
|
+
as: config.as,
|
|
585
|
+
variants: config.variants
|
|
586
|
+
};
|
|
587
|
+
return builder;
|
|
588
|
+
}
|
|
571
589
|
var ConvexDeletionBuilder = class {
|
|
572
590
|
static [entityKind] = "ConvexDeletionBuilder";
|
|
573
591
|
[entityKind] = "ConvexDeletionBuilder";
|
|
@@ -699,6 +717,86 @@ function assertRankOrderFieldType(column, indexName) {
|
|
|
699
717
|
].includes(columnType)) throw new Error(`rankIndex '${indexName}' orderBy() supports integer()/timestamp()/date() columns only. Field '${getColumnName(column)}' is type '${columnType}'.`);
|
|
700
718
|
}
|
|
701
719
|
const dedupeFieldNames = (fields) => [...new Set(fields)];
|
|
720
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
721
|
+
const isColumnBuilder = (value) => isRecord(value) && typeof value.build === "function";
|
|
722
|
+
const getDiscriminatorConfig = (value) => {
|
|
723
|
+
if (!isColumnBuilder(value)) return;
|
|
724
|
+
const discriminator = value.config?.discriminator;
|
|
725
|
+
if (!discriminator) return;
|
|
726
|
+
return discriminator;
|
|
727
|
+
};
|
|
728
|
+
const getPolymorphicFieldSignature = (column) => {
|
|
729
|
+
const validator = column.convexValidator ?? column.build();
|
|
730
|
+
return JSON.stringify({
|
|
731
|
+
columnType: column.config?.columnType,
|
|
732
|
+
validator: validator?.json
|
|
733
|
+
});
|
|
734
|
+
};
|
|
735
|
+
function resolveTableColumns(tableName, columns) {
|
|
736
|
+
const resolvedColumns = {};
|
|
737
|
+
const pendingPolymorphic = [];
|
|
738
|
+
for (const [columnName, rawBuilder] of Object.entries(columns)) {
|
|
739
|
+
if (!isColumnBuilder(rawBuilder)) throw new Error(`Column '${columnName}' on '${tableName}' must be a column builder.`);
|
|
740
|
+
resolvedColumns[columnName] = rawBuilder;
|
|
741
|
+
const discriminatorConfig = getDiscriminatorConfig(rawBuilder);
|
|
742
|
+
if (!discriminatorConfig) continue;
|
|
743
|
+
if (!isRecord(discriminatorConfig.variants) || Object.keys(discriminatorConfig.variants).length === 0) throw new Error(`discriminator('${tableName}.${columnName}') requires at least one variant.`);
|
|
744
|
+
const alias = discriminatorConfig.as === void 0 ? DEFAULT_POLYMORPHIC_ALIAS : discriminatorConfig.as;
|
|
745
|
+
if (typeof alias !== "string" || alias.length === 0) throw new Error(`discriminator('${tableName}.${columnName}').as must be a non-empty string.`);
|
|
746
|
+
pendingPolymorphic.push({
|
|
747
|
+
discriminator: columnName,
|
|
748
|
+
alias,
|
|
749
|
+
variants: discriminatorConfig.variants
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
if (pendingPolymorphic.length > 1) throw new Error(`Only one discriminator(...) column is currently supported on '${tableName}'.`);
|
|
753
|
+
const polymorphicConfigs = [];
|
|
754
|
+
for (const pending of pendingPolymorphic) {
|
|
755
|
+
if (pending.alias in resolvedColumns) throw new Error(`discriminator('${tableName}.${pending.discriminator}') alias '${pending.alias}' collides with an existing column.`);
|
|
756
|
+
const generatedFieldMap = /* @__PURE__ */ new Map();
|
|
757
|
+
const variantRuntime = {};
|
|
758
|
+
for (const [variantKey, rawVariantColumns] of Object.entries(pending.variants)) {
|
|
759
|
+
if (!isRecord(rawVariantColumns)) throw new Error(`discriminator('${tableName}.${pending.discriminator}') variant '${variantKey}' must be an object.`);
|
|
760
|
+
const fieldNames = [];
|
|
761
|
+
const requiredFieldNames = [];
|
|
762
|
+
for (const [fieldName, rawFieldBuilder] of Object.entries(rawVariantColumns)) {
|
|
763
|
+
if (!isColumnBuilder(rawFieldBuilder)) throw new Error(`discriminator('${tableName}.${pending.discriminator}').variants.${variantKey}.${fieldName} must be a column builder.`);
|
|
764
|
+
if (fieldName in resolvedColumns) throw new Error(`discriminator('${tableName}.${pending.discriminator}').variants.${variantKey}.${fieldName} collides with an existing table column.`);
|
|
765
|
+
const fieldBuilder = rawFieldBuilder;
|
|
766
|
+
const fieldConfig = fieldBuilder.config;
|
|
767
|
+
const isRequiredForVariant = fieldConfig?.notNull === true && fieldConfig.hasDefault !== true && typeof fieldConfig.defaultFn !== "function";
|
|
768
|
+
const signature = getPolymorphicFieldSignature(fieldBuilder);
|
|
769
|
+
const existing = generatedFieldMap.get(fieldName);
|
|
770
|
+
if (existing && existing.signature !== signature) throw new Error(`discriminator('${tableName}.${pending.discriminator}') field '${fieldName}' has conflicting builder signatures across variants.`);
|
|
771
|
+
if (!existing) {
|
|
772
|
+
if (fieldConfig) fieldConfig.notNull = false;
|
|
773
|
+
generatedFieldMap.set(fieldName, {
|
|
774
|
+
builder: fieldBuilder,
|
|
775
|
+
signature
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
fieldNames.push(fieldName);
|
|
779
|
+
if (isRequiredForVariant) requiredFieldNames.push(fieldName);
|
|
780
|
+
}
|
|
781
|
+
variantRuntime[variantKey] = {
|
|
782
|
+
fieldNames,
|
|
783
|
+
requiredFieldNames
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
for (const [fieldName, { builder }] of generatedFieldMap.entries()) resolvedColumns[fieldName] = builder;
|
|
787
|
+
polymorphicConfigs.push({
|
|
788
|
+
discriminator: pending.discriminator,
|
|
789
|
+
alias: pending.alias,
|
|
790
|
+
generatedFieldNames: Object.freeze([...generatedFieldMap.keys()]),
|
|
791
|
+
variants: Object.freeze(variantRuntime)
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
if (Object.keys(resolvedColumns).length > CONVEX_TABLE_FIELD_LIMIT) throw new Error(`Table '${tableName}' exceeds Convex field count limit (${CONVEX_TABLE_FIELD_LIMIT}) after discriminator expansion.`);
|
|
795
|
+
return {
|
|
796
|
+
columns: resolvedColumns,
|
|
797
|
+
polymorphicConfigs
|
|
798
|
+
};
|
|
799
|
+
}
|
|
702
800
|
function applyExtraConfig(table, config) {
|
|
703
801
|
if (!config) return;
|
|
704
802
|
const entries = Array.isArray(config) ? config : Object.values(config);
|
|
@@ -920,11 +1018,12 @@ var ConvexTableImpl = class {
|
|
|
920
1018
|
[EnableRLS] = false;
|
|
921
1019
|
[RlsPolicies] = [];
|
|
922
1020
|
[TableDeleteConfig];
|
|
1021
|
+
[TablePolymorphic];
|
|
923
1022
|
/**
|
|
924
1023
|
* Public tableName for convenience
|
|
925
1024
|
*/
|
|
926
1025
|
tableName;
|
|
927
|
-
constructor(name, columns) {
|
|
1026
|
+
constructor(name, columns, polymorphicConfigs) {
|
|
928
1027
|
validateTableName(name);
|
|
929
1028
|
for (const columnName of Object.keys(columns)) if (RESERVED_COLUMN_NAMES.has(columnName)) throw new Error(`Column name '${columnName}' is reserved. System fields are managed by Convex ORM.`);
|
|
930
1029
|
this[TableName] = name;
|
|
@@ -936,6 +1035,7 @@ var ConvexTableImpl = class {
|
|
|
936
1035
|
}));
|
|
937
1036
|
this[Columns] = namedColumns;
|
|
938
1037
|
this.tableName = name;
|
|
1038
|
+
if (polymorphicConfigs && polymorphicConfigs.length > 0) this[TablePolymorphic] = polymorphicConfigs;
|
|
939
1039
|
this.validator = createValidatorFromColumns(namedColumns);
|
|
940
1040
|
for (const [columnName, builder] of Object.entries(namedColumns)) {
|
|
941
1041
|
const config = builder.config;
|
|
@@ -958,6 +1058,9 @@ var ConvexTableImpl = class {
|
|
|
958
1058
|
});
|
|
959
1059
|
}
|
|
960
1060
|
}
|
|
1061
|
+
getPolymorphicConfigs() {
|
|
1062
|
+
return this[TablePolymorphic];
|
|
1063
|
+
}
|
|
961
1064
|
/**
|
|
962
1065
|
* Internal: add index to table from builder extraConfig
|
|
963
1066
|
*
|
|
@@ -1163,7 +1266,8 @@ var ConvexTableImpl = class {
|
|
|
1163
1266
|
}
|
|
1164
1267
|
};
|
|
1165
1268
|
const convexTableInternal = (name, columns, extraConfig) => {
|
|
1166
|
-
const
|
|
1269
|
+
const expanded = resolveTableColumns(name, columns);
|
|
1270
|
+
const rawTable = new ConvexTableImpl(name, expanded.columns, expanded.polymorphicConfigs);
|
|
1167
1271
|
const systemFields = createSystemFields(name);
|
|
1168
1272
|
for (const builder of Object.values(systemFields)) builder.config.table = rawTable;
|
|
1169
1273
|
const table = Object.assign(rawTable, systemFields, rawTable[Columns]);
|
|
@@ -1192,4 +1296,4 @@ const convexTableWithRLS = (name, columns, extraConfig) => {
|
|
|
1192
1296
|
const convexTable = Object.assign(convexTableInternal, { withRLS: convexTableWithRLS });
|
|
1193
1297
|
|
|
1194
1298
|
//#endregion
|
|
1195
|
-
export {
|
|
1299
|
+
export { text as C, entityKind as D, ConvexColumnBuilder as E, vectorIndex as S, integer as T, aggregateIndex as _, Columns as a, searchIndex as b, OrmSchemaDefinition as c, RlsPolicies as d, TableDeleteConfig as f, rlsPolicy as g, RlsPolicy as h, Brand as i, OrmSchemaOptions as l, TablePolymorphic as m, deletion as n, EnableRLS as o, TableName as p, discriminator as r, OrmContext as s, convexTable as t, OrmSchemaPluginTables as u, index as v, createSystemFields as w, uniqueIndex as x, rankIndex as y };
|