@zenstackhq/orm 3.7.0-beta.1 → 3.7.1
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/index.cjs +386 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +340 -42
- package/dist/index.d.mts +340 -42
- package/dist/index.mjs +385 -72
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -10
package/dist/index.cjs
CHANGED
|
@@ -226,6 +226,7 @@ var query_utils_exports = /* @__PURE__ */ __exportAll({
|
|
|
226
226
|
fieldHasDefaultValue: () => fieldHasDefaultValue,
|
|
227
227
|
flattenCompoundUniqueFilters: () => flattenCompoundUniqueFilters,
|
|
228
228
|
getDelegateDescendantModels: () => getDelegateDescendantModels,
|
|
229
|
+
getDelegateDiscriminatorValue: () => getDelegateDiscriminatorValue,
|
|
229
230
|
getDiscriminatorField: () => getDiscriminatorField,
|
|
230
231
|
getEnum: () => getEnum,
|
|
231
232
|
getField: () => getField,
|
|
@@ -459,6 +460,10 @@ function getDiscriminatorField(schema, model) {
|
|
|
459
460
|
if (!discriminator || !_zenstackhq_schema.ExpressionUtils.isField(discriminator.value)) throw createInternalError(`Discriminator field not defined for model "${model}"`, model);
|
|
460
461
|
return discriminator.value.field;
|
|
461
462
|
}
|
|
463
|
+
function getDelegateDiscriminatorValue(schema, model) {
|
|
464
|
+
const modelDef = requireModel(schema, model);
|
|
465
|
+
return modelDef.delegateMap ?? modelDef.name;
|
|
466
|
+
}
|
|
462
467
|
function getDelegateDescendantModels(schema, model, collected = /* @__PURE__ */ new Set()) {
|
|
463
468
|
Object.values(schema.models).filter((m) => m.baseModel === model).forEach((def) => {
|
|
464
469
|
if (!collected.has(def)) {
|
|
@@ -568,6 +573,8 @@ const FILTER_PROPERTY_TO_KIND = {
|
|
|
568
573
|
array_contains: "Json",
|
|
569
574
|
array_starts_with: "Json",
|
|
570
575
|
array_ends_with: "Json",
|
|
576
|
+
fuzzy: "Fuzzy",
|
|
577
|
+
fts: "FullText",
|
|
571
578
|
has: "List",
|
|
572
579
|
hasEvery: "List",
|
|
573
580
|
hasSome: "List",
|
|
@@ -587,6 +594,19 @@ let TransactionIsolationLevel = /* @__PURE__ */ function(TransactionIsolationLev
|
|
|
587
594
|
return TransactionIsolationLevel;
|
|
588
595
|
}({});
|
|
589
596
|
/**
|
|
597
|
+
* Symbol used as a type-only key on `ClientContract` to brand the `ExtQueryArgs`
|
|
598
|
+
* generic slot. Hidden from member-access autocomplete since symbol keys are
|
|
599
|
+
* not surfaced. Consumed by `InferExtQueryArgs` to recover the slot.
|
|
600
|
+
* @internal
|
|
601
|
+
*/
|
|
602
|
+
const ExtQueryArgsMarker = Symbol("zenstack.client.extQueryArgs");
|
|
603
|
+
/**
|
|
604
|
+
* Symbol used as a type-only key on `ClientContract` to brand the `ExtResult`
|
|
605
|
+
* generic slot. Consumed by `InferExtResult` to recover the slot.
|
|
606
|
+
* @internal
|
|
607
|
+
*/
|
|
608
|
+
const ExtResultMarker = Symbol("zenstack.client.extResult");
|
|
609
|
+
/**
|
|
590
610
|
* CRUD operations.
|
|
591
611
|
*/
|
|
592
612
|
const CRUD = [
|
|
@@ -609,8 +629,13 @@ var BaseCrudDialect = class {
|
|
|
609
629
|
}
|
|
610
630
|
/**
|
|
611
631
|
* Transforms input value before sending to database.
|
|
632
|
+
*
|
|
633
|
+
* `fieldDef` is optional so existing callers that don't have it stay
|
|
634
|
+
* source-compatible. Dialects can use it to inspect `@db.*` native-type
|
|
635
|
+
* attributes (e.g. to format `@db.Time` values as `HH:MM:SS` rather than
|
|
636
|
+
* full ISO timestamps).
|
|
612
637
|
*/
|
|
613
|
-
transformInput(value, _type, _forArrayField) {
|
|
638
|
+
transformInput(value, _type, _forArrayField, _fieldDef) {
|
|
614
639
|
return value;
|
|
615
640
|
}
|
|
616
641
|
/**
|
|
@@ -653,7 +678,17 @@ var BaseCrudDialect = class {
|
|
|
653
678
|
if (existingOrderBy.length > 0 && !alreadySatisfied) effectiveOrderBy = [...distinctFields.map((f) => ({ [f]: "asc" })), ...existingOrderBy];
|
|
654
679
|
}
|
|
655
680
|
result = this.buildOrderBy(result, model, modelAlias, effectiveOrderBy, negateOrderBy, take);
|
|
656
|
-
if (args.cursor)
|
|
681
|
+
if (args.cursor) {
|
|
682
|
+
if (effectiveOrderBy) {
|
|
683
|
+
const offendingKey = (0, _zenstackhq_common_helpers.enumerate)(effectiveOrderBy).map((ob) => {
|
|
684
|
+
if (typeof ob !== "object" || ob === null) return void 0;
|
|
685
|
+
if ("_fuzzyRelevance" in ob) return "_fuzzyRelevance";
|
|
686
|
+
if ("_ftsRelevance" in ob) return "_ftsRelevance";
|
|
687
|
+
}).find((k) => k !== void 0);
|
|
688
|
+
if (offendingKey) throw createNotSupportedError(`cursor pagination cannot be combined with "${offendingKey}" ordering`);
|
|
689
|
+
}
|
|
690
|
+
result = this.buildCursorFilter(model, result, args.cursor, effectiveOrderBy, negateOrderBy, modelAlias);
|
|
691
|
+
}
|
|
657
692
|
return result;
|
|
658
693
|
}
|
|
659
694
|
buildFilter(model, modelAlias, where) {
|
|
@@ -793,7 +828,7 @@ var BaseCrudDialect = class {
|
|
|
793
828
|
for (const [key, _value] of Object.entries(payload)) {
|
|
794
829
|
if (_value === void 0) continue;
|
|
795
830
|
(0, _zenstackhq_common_helpers.invariant)(fieldDef.array, "Field must be an array type to build array filter");
|
|
796
|
-
const value = this.transformInput(_value, fieldType, true);
|
|
831
|
+
const value = this.transformInput(_value, fieldType, true, fieldDef);
|
|
797
832
|
let receiver = fieldRef;
|
|
798
833
|
if (isEnum(this.schema, fieldType)) receiver = this.eb.cast(fieldRef, kysely.sql.raw("text[]"));
|
|
799
834
|
const buildArray = (value) => {
|
|
@@ -828,7 +863,7 @@ var BaseCrudDialect = class {
|
|
|
828
863
|
if (payload instanceof require_common_types.DbNullClass || payload instanceof require_common_types.JsonNullClass || payload instanceof require_common_types.AnyNullClass) return this.buildJsonValueFilterClause(fieldRef, payload);
|
|
829
864
|
return this.buildJsonFilter(fieldRef, payload, fieldDef);
|
|
830
865
|
}
|
|
831
|
-
return (0, ts_pattern.match)(fieldDef.type).with("String", () => this.buildStringFilter(fieldRef, payload)).with(ts_pattern.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.buildNumberFilter(fieldRef, type, payload)).with("Boolean", () => this.buildBooleanFilter(fieldRef, payload)).with("DateTime", () => this.buildDateTimeFilter(fieldRef, payload)).with("Bytes", () => this.buildBytesFilter(fieldRef, payload)).with("Json", () => this.buildJsonFilter(fieldRef, payload, fieldDef)).with("Unsupported", () => {
|
|
866
|
+
return (0, ts_pattern.match)(fieldDef.type).with("String", () => this.buildStringFilter(fieldRef, payload, fieldDef)).with(ts_pattern.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.buildNumberFilter(fieldRef, type, payload)).with("Boolean", () => this.buildBooleanFilter(fieldRef, payload)).with("DateTime", () => this.buildDateTimeFilter(fieldRef, payload)).with("Bytes", () => this.buildBytesFilter(fieldRef, payload)).with("Json", () => this.buildJsonFilter(fieldRef, payload, fieldDef)).with("Unsupported", () => {
|
|
832
867
|
throw createInvalidInputError(`Unsupported field cannot be used in filters`);
|
|
833
868
|
}).exhaustive();
|
|
834
869
|
}
|
|
@@ -996,13 +1031,23 @@ var BaseCrudDialect = class {
|
|
|
996
1031
|
consumedKeys
|
|
997
1032
|
};
|
|
998
1033
|
}
|
|
999
|
-
buildStringFilter(fieldRef, payload) {
|
|
1034
|
+
buildStringFilter(fieldRef, payload, fieldDef) {
|
|
1000
1035
|
let mode;
|
|
1001
1036
|
if (payload && typeof payload === "object" && "mode" in payload) mode = payload.mode;
|
|
1002
|
-
const { conditions, consumedKeys } = this.buildStandardFilter("String", payload, mode === "insensitive" ? this.eb.fn("lower", [fieldRef]) : fieldRef, (value) => this.prepStringCasing(this.eb, value, mode), (value) => this.buildStringFilter(fieldRef, value));
|
|
1037
|
+
const { conditions, consumedKeys } = this.buildStandardFilter("String", payload, mode === "insensitive" ? this.eb.fn("lower", [fieldRef]) : fieldRef, (value) => this.prepStringCasing(this.eb, value, mode), (value) => this.buildStringFilter(fieldRef, value, fieldDef));
|
|
1003
1038
|
if (payload && typeof payload === "object") for (const [key, value] of Object.entries(payload)) {
|
|
1004
1039
|
if (key === "mode" || consumedKeys.includes(key)) continue;
|
|
1005
1040
|
if (value === void 0) continue;
|
|
1041
|
+
if (key === "fuzzy") {
|
|
1042
|
+
(0, _zenstackhq_common_helpers.invariant)(fieldDef?.fuzzy === true, `field "${fieldDef?.name ?? "<unknown>"}" is not fuzzy-searchable; add the \`@fuzzy\` attribute to use the \`fuzzy\` filter`);
|
|
1043
|
+
conditions.push(this.buildFuzzyFilter(fieldRef, this.normalizeFuzzyOptions(value)));
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
if (key === "fts") {
|
|
1047
|
+
(0, _zenstackhq_common_helpers.invariant)(fieldDef?.fullText === true, `field "${fieldDef?.name ?? "<unknown>"}" is not full-text-searchable; add the \`@fullText\` attribute to use the \`fts\` filter`);
|
|
1048
|
+
conditions.push(this.buildFullTextFilter(fieldRef, value));
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1006
1051
|
(0, _zenstackhq_common_helpers.invariant)(typeof value === "string", `${key} value must be a string`);
|
|
1007
1052
|
const escapedValue = this.escapeLikePattern(value);
|
|
1008
1053
|
const condition = (0, ts_pattern.match)(key).with("contains", () => this.buildStringLike(fieldRef, `%${escapedValue}%`, mode === "insensitive")).with("startsWith", () => this.buildStringLike(fieldRef, `${escapedValue}%`, mode === "insensitive")).with("endsWith", () => this.buildStringLike(fieldRef, `%${escapedValue}`, mode === "insensitive")).otherwise(() => {
|
|
@@ -1071,6 +1116,14 @@ var BaseCrudDialect = class {
|
|
|
1071
1116
|
(0, _zenstackhq_common_helpers.enumerate)(orderBy).forEach((orderBy, index) => {
|
|
1072
1117
|
for (const [field, value] of Object.entries(orderBy)) {
|
|
1073
1118
|
if (!value) continue;
|
|
1119
|
+
if (field === "_fuzzyRelevance") {
|
|
1120
|
+
result = this.applyFuzzyRelevanceOrderBy(result, model, modelAlias, value, negated, buildFieldRef);
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
if (field === "_ftsRelevance") {
|
|
1124
|
+
result = this.applyFtsRelevanceOrderBy(result, model, modelAlias, value, negated, buildFieldRef);
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
1074
1127
|
if ([
|
|
1075
1128
|
"_count",
|
|
1076
1129
|
"_avg",
|
|
@@ -1078,47 +1131,84 @@ var BaseCrudDialect = class {
|
|
|
1078
1131
|
"_min",
|
|
1079
1132
|
"_max"
|
|
1080
1133
|
].includes(field)) {
|
|
1081
|
-
|
|
1082
|
-
for (const [k, v] of Object.entries(value)) {
|
|
1083
|
-
(0, _zenstackhq_common_helpers.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
1084
|
-
result = result.orderBy((eb) => aggregate(eb, buildFieldRef(model, k, modelAlias), field), this.negateSort(v, negated));
|
|
1085
|
-
}
|
|
1134
|
+
result = this.applyAggregationOrderBy(result, model, modelAlias, field, value, negated, buildFieldRef);
|
|
1086
1135
|
continue;
|
|
1087
1136
|
}
|
|
1088
1137
|
const fieldDef = requireField(this.schema, model, field);
|
|
1089
|
-
if (!fieldDef.relation)
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
const joinAlias = tmpAlias(`${modelAlias}$ob$${index}`);
|
|
1111
|
-
result = result.leftJoin(`${relationModel} as ${joinAlias}`, (join) => {
|
|
1112
|
-
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, joinAlias);
|
|
1113
|
-
return join.on((eb) => this.and(...joinPairs.map(([left, right]) => eb(this.eb.ref(left), "=", this.eb.ref(right)))));
|
|
1114
|
-
});
|
|
1115
|
-
result = this.buildOrderBy(result, relationModel, joinAlias, value, negated, take);
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1138
|
+
if (!fieldDef.relation) result = this.applyScalarOrderBy(result, model, modelAlias, field, value, negated, buildFieldRef);
|
|
1139
|
+
else result = this.applyRelationOrderBy(result, model, modelAlias, field, fieldDef, value, negated, take, index);
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
return result;
|
|
1143
|
+
}
|
|
1144
|
+
applyRelationOrderBy(query, model, modelAlias, field, fieldDef, value, negated, take, index) {
|
|
1145
|
+
const relationModel = fieldDef.type;
|
|
1146
|
+
if (fieldDef.array) {
|
|
1147
|
+
if (typeof value !== "object") throw createInvalidInputError(`invalid orderBy value for field "${field}"`);
|
|
1148
|
+
if ("_count" in value) {
|
|
1149
|
+
(0, _zenstackhq_common_helpers.invariant)(value._count === "asc" || value._count === "desc", "invalid orderBy value for field \"_count\"");
|
|
1150
|
+
const sort = this.negateSort(value._count, negated);
|
|
1151
|
+
return query.orderBy((eb) => {
|
|
1152
|
+
const subQueryAlias = tmpAlias(`${modelAlias}$ob$${field}$ct`);
|
|
1153
|
+
let subQuery = this.buildSelectModel(relationModel, subQueryAlias);
|
|
1154
|
+
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
|
|
1155
|
+
subQuery = subQuery.where(() => this.and(...joinPairs.map(([left, right]) => eb(this.eb.ref(left), "=", this.eb.ref(right)))));
|
|
1156
|
+
subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
|
|
1157
|
+
return subQuery;
|
|
1158
|
+
}, sort);
|
|
1118
1159
|
}
|
|
1160
|
+
return query;
|
|
1161
|
+
}
|
|
1162
|
+
const joinAlias = tmpAlias(`${modelAlias}$ob$${index}`);
|
|
1163
|
+
const joined = query.leftJoin(`${relationModel} as ${joinAlias}`, (join) => {
|
|
1164
|
+
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, joinAlias);
|
|
1165
|
+
return join.on((eb) => this.and(...joinPairs.map(([left, right]) => eb(this.eb.ref(left), "=", this.eb.ref(right)))));
|
|
1119
1166
|
});
|
|
1167
|
+
return this.buildOrderBy(joined, relationModel, joinAlias, value, negated, take);
|
|
1168
|
+
}
|
|
1169
|
+
applyScalarOrderBy(query, model, modelAlias, field, value, negated, buildFieldRef) {
|
|
1170
|
+
const fieldRef = buildFieldRef(model, field, modelAlias);
|
|
1171
|
+
if (value === "asc" || value === "desc") return query.orderBy(fieldRef, this.negateSort(value, negated));
|
|
1172
|
+
if (typeof value === "object" && "sort" in value && (value.sort === "asc" || value.sort === "desc")) {
|
|
1173
|
+
const sort = this.negateSort(value.sort, negated);
|
|
1174
|
+
if (value.nulls === "first" || value.nulls === "last") return this.buildOrderByField(query, fieldRef, sort, value.nulls);
|
|
1175
|
+
else return query.orderBy(fieldRef, sort);
|
|
1176
|
+
}
|
|
1177
|
+
return query;
|
|
1178
|
+
}
|
|
1179
|
+
applyAggregationOrderBy(query, model, modelAlias, field, value, negated, buildFieldRef) {
|
|
1180
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value === "object", `invalid orderBy value for field "${field}"`);
|
|
1181
|
+
let result = query;
|
|
1182
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1183
|
+
(0, _zenstackhq_common_helpers.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
1184
|
+
result = result.orderBy((eb) => aggregate(eb, buildFieldRef(model, k, modelAlias), field), this.negateSort(v, negated));
|
|
1185
|
+
}
|
|
1120
1186
|
return result;
|
|
1121
1187
|
}
|
|
1188
|
+
applyFuzzyRelevanceOrderBy(query, model, modelAlias, value, negated, buildFieldRef) {
|
|
1189
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value === "object" && "fields" in value && "search" in value && "sort" in value, "invalid orderBy value for \"_fuzzyRelevance\"");
|
|
1190
|
+
(0, _zenstackhq_common_helpers.invariant)(Array.isArray(value.fields) && value.fields.length > 0, "_fuzzyRelevance.fields must be a non-empty array");
|
|
1191
|
+
(0, _zenstackhq_common_helpers.invariant)(value.sort === "asc" || value.sort === "desc", "invalid sort value for \"_fuzzyRelevance\"");
|
|
1192
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value.search === "string" && value.search.length > 0, "_fuzzyRelevance.search must be a non-empty string");
|
|
1193
|
+
const mode = value.mode ?? "simple";
|
|
1194
|
+
(0, _zenstackhq_common_helpers.invariant)(mode === "simple" || mode === "word" || mode === "strictWord", "_fuzzyRelevance.mode must be \"simple\", \"word\" or \"strictWord\"");
|
|
1195
|
+
const unaccent = value.unaccent ?? false;
|
|
1196
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof unaccent === "boolean", "_fuzzyRelevance.unaccent must be a boolean");
|
|
1197
|
+
for (const fieldName of value.fields) (0, _zenstackhq_common_helpers.invariant)(requireField(this.schema, model, fieldName).fuzzy === true, `field "${fieldName}" is not fuzzy-searchable; add the \`@fuzzy\` attribute to use it in \`_fuzzyRelevance\``);
|
|
1198
|
+
const fieldRefs = value.fields.map((f) => buildFieldRef(model, f, modelAlias));
|
|
1199
|
+
return this.buildFuzzyRelevanceOrderBy(query, fieldRefs, value.search, this.negateSort(value.sort, negated), mode, unaccent);
|
|
1200
|
+
}
|
|
1201
|
+
applyFtsRelevanceOrderBy(query, model, modelAlias, value, negated, buildFieldRef) {
|
|
1202
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value === "object" && "fields" in value && "search" in value && "sort" in value, "invalid orderBy value for \"_ftsRelevance\"");
|
|
1203
|
+
(0, _zenstackhq_common_helpers.invariant)(Array.isArray(value.fields) && value.fields.length > 0, "_ftsRelevance.fields must be a non-empty array");
|
|
1204
|
+
(0, _zenstackhq_common_helpers.invariant)(value.sort === "asc" || value.sort === "desc", "invalid sort value for \"_ftsRelevance\"");
|
|
1205
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value.search === "string" && value.search.length > 0, "_ftsRelevance.search must be a non-empty string");
|
|
1206
|
+
if (value.config !== void 0) (0, _zenstackhq_common_helpers.invariant)(typeof value.config === "string" && value.config.length > 0, "_ftsRelevance.config must be a non-empty string");
|
|
1207
|
+
const config = value.config;
|
|
1208
|
+
for (const fieldName of value.fields) (0, _zenstackhq_common_helpers.invariant)(requireField(this.schema, model, fieldName).fullText === true, `field "${fieldName}" is not full-text-searchable; add the \`@fullText\` attribute to use it in \`_ftsRelevance\``);
|
|
1209
|
+
const fieldRefs = value.fields.map((f) => buildFieldRef(model, f, modelAlias));
|
|
1210
|
+
return this.buildFtsRelevanceOrderBy(query, fieldRefs, value.search, config, this.negateSort(value.sort, negated));
|
|
1211
|
+
}
|
|
1122
1212
|
buildSelectAllFields(model, query, omit, modelAlias) {
|
|
1123
1213
|
let result = query;
|
|
1124
1214
|
for (const fieldDef of getModelFields(this.schema, model, {
|
|
@@ -1261,6 +1351,27 @@ var BaseCrudDialect = class {
|
|
|
1261
1351
|
buildComparison(left, _leftFieldDef, op, right, _rightFieldDef) {
|
|
1262
1352
|
return this.eb(left, op, right);
|
|
1263
1353
|
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Validate the user-provided fuzzy filter payload and apply defaults so dialects
|
|
1356
|
+
* always receive a fully-resolved {@link FuzzyFilterOptions} value.
|
|
1357
|
+
*/
|
|
1358
|
+
normalizeFuzzyOptions(value) {
|
|
1359
|
+
(0, _zenstackhq_common_helpers.invariant)(value !== null && typeof value === "object" && !Array.isArray(value), "fuzzy filter must be an object with at least a \"search\" field");
|
|
1360
|
+
const raw = value;
|
|
1361
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof raw["search"] === "string" && raw["search"].length > 0, "fuzzy.search must be a non-empty string");
|
|
1362
|
+
const mode = raw["mode"] ?? "simple";
|
|
1363
|
+
(0, _zenstackhq_common_helpers.invariant)(mode === "simple" || mode === "word" || mode === "strictWord", "fuzzy.mode must be \"simple\", \"word\" or \"strictWord\"");
|
|
1364
|
+
const threshold = raw["threshold"];
|
|
1365
|
+
if (threshold !== void 0) (0, _zenstackhq_common_helpers.invariant)(typeof threshold === "number" && threshold >= 0 && threshold <= 1, "fuzzy.threshold must be a number between 0 and 1");
|
|
1366
|
+
const unaccent = raw["unaccent"] ?? false;
|
|
1367
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof unaccent === "boolean", "fuzzy.unaccent must be a boolean");
|
|
1368
|
+
return {
|
|
1369
|
+
search: raw["search"],
|
|
1370
|
+
mode,
|
|
1371
|
+
threshold,
|
|
1372
|
+
unaccent
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1264
1375
|
};
|
|
1265
1376
|
//#endregion
|
|
1266
1377
|
//#region src/client/crud/dialects/lateral-join-dialect-base.ts
|
|
@@ -1545,9 +1656,32 @@ var MySqlCrudDialect = class extends LateralJoinDialectBase {
|
|
|
1545
1656
|
}
|
|
1546
1657
|
return result;
|
|
1547
1658
|
}
|
|
1659
|
+
buildFuzzyFilter(_fieldRef, _options) {
|
|
1660
|
+
throw createNotSupportedError("\"fuzzy\" filter is not supported by the \"mysql\" provider");
|
|
1661
|
+
}
|
|
1662
|
+
buildFuzzyRelevanceOrderBy(_query, _fieldRefs, _search, _sort, _mode, _unaccent) {
|
|
1663
|
+
throw createNotSupportedError("\"_fuzzyRelevance\" ordering is not supported by the \"mysql\" provider");
|
|
1664
|
+
}
|
|
1665
|
+
buildFullTextFilter(_fieldRef, _payload) {
|
|
1666
|
+
throw createNotSupportedError("\"fts\" filter is not supported by the \"mysql\" provider");
|
|
1667
|
+
}
|
|
1668
|
+
buildFtsRelevanceOrderBy(_query, _fieldRefs, _search, _config, _sort) {
|
|
1669
|
+
throw createNotSupportedError("\"_ftsRelevance\" ordering is not supported by the \"mysql\" provider");
|
|
1670
|
+
}
|
|
1548
1671
|
};
|
|
1549
1672
|
//#endregion
|
|
1550
1673
|
//#region src/client/crud/dialects/postgresql.ts
|
|
1674
|
+
/**
|
|
1675
|
+
* Formats a JS `Date` as a Postgres TIME / TIMETZ literal (`HH:MM:SS.fff`,
|
|
1676
|
+
* optionally with `+ZZ:ZZ` for TIMETZ). Reads UTC components so the value
|
|
1677
|
+
* round-trips with ISO-input parsing — callers anchor time-only inputs to
|
|
1678
|
+
* the Unix epoch.
|
|
1679
|
+
*/
|
|
1680
|
+
function formatTimeOfDay(date, withTimezone) {
|
|
1681
|
+
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
1682
|
+
const time = `${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}.${pad(date.getUTCMilliseconds(), 3)}`;
|
|
1683
|
+
return withTimezone ? `${time}+00:00` : time;
|
|
1684
|
+
}
|
|
1551
1685
|
var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBase {
|
|
1552
1686
|
static typeParserOverrideApplied = false;
|
|
1553
1687
|
zmodelToSqlTypeMap = {
|
|
@@ -1641,7 +1775,7 @@ var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBa
|
|
|
1641
1775
|
get insertIgnoreMethod() {
|
|
1642
1776
|
return "onConflict";
|
|
1643
1777
|
}
|
|
1644
|
-
transformInput(value, type, forArrayField) {
|
|
1778
|
+
transformInput(value, type, forArrayField, fieldDef) {
|
|
1645
1779
|
if (value === void 0) return value;
|
|
1646
1780
|
if (value instanceof require_common_types.JsonNullClass) return "null";
|
|
1647
1781
|
else if (value instanceof require_common_types.DbNullClass) return null;
|
|
@@ -1649,9 +1783,15 @@ var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBa
|
|
|
1649
1783
|
if (isTypeDef(this.schema, type)) if (typeof value !== "string") return JSON.stringify(value);
|
|
1650
1784
|
else return value;
|
|
1651
1785
|
else if (Array.isArray(value)) if (type === "Json" && !forArrayField) return JSON.stringify(value);
|
|
1652
|
-
else return value.map((v) => this.transformInput(v, type, false));
|
|
1786
|
+
else return value.map((v) => this.transformInput(v, type, false, fieldDef));
|
|
1653
1787
|
else switch (type) {
|
|
1654
|
-
case "DateTime":
|
|
1788
|
+
case "DateTime": {
|
|
1789
|
+
const date = value instanceof Date ? value : typeof value === "string" ? new Date(value) : null;
|
|
1790
|
+
if (date === null || isNaN(date.getTime())) return value;
|
|
1791
|
+
const dbAttrName = fieldDef?.attributes?.find((a) => a.name.startsWith("@db."))?.name;
|
|
1792
|
+
if (dbAttrName === "@db.Time" || dbAttrName === "@db.Timetz") return formatTimeOfDay(date, dbAttrName === "@db.Timetz");
|
|
1793
|
+
return date.toISOString();
|
|
1794
|
+
}
|
|
1655
1795
|
case "Decimal": return value !== null ? value.toString() : value;
|
|
1656
1796
|
case "Json": if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return JSON.stringify(value);
|
|
1657
1797
|
else return value;
|
|
@@ -1823,6 +1963,72 @@ var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBa
|
|
|
1823
1963
|
return ob;
|
|
1824
1964
|
});
|
|
1825
1965
|
}
|
|
1966
|
+
/**
|
|
1967
|
+
* Wraps an expression with `unaccent(lower(...))` or just `lower(...)` depending on
|
|
1968
|
+
* whether the user opted into accent-insensitive matching. The lowering is always
|
|
1969
|
+
* applied so trigram comparisons are case-insensitive on both sides.
|
|
1970
|
+
*/
|
|
1971
|
+
normalizeForTrigram(expr, applyUnaccent) {
|
|
1972
|
+
return applyUnaccent ? kysely.sql`unaccent(lower(${expr}))` : kysely.sql`lower(${expr})`;
|
|
1973
|
+
}
|
|
1974
|
+
buildFuzzyFilter(fieldRef, options) {
|
|
1975
|
+
const fieldExpr = this.normalizeForTrigram(fieldRef, options.unaccent);
|
|
1976
|
+
const valueExpr = this.normalizeForTrigram(kysely.sql.val(options.search), options.unaccent);
|
|
1977
|
+
if (options.threshold === void 0) switch (options.mode) {
|
|
1978
|
+
case "simple": return kysely.sql`${fieldExpr} % ${valueExpr}`;
|
|
1979
|
+
case "word": return kysely.sql`${valueExpr} <% ${fieldExpr}`;
|
|
1980
|
+
case "strictWord": return kysely.sql`${valueExpr} <<% ${fieldExpr}`;
|
|
1981
|
+
}
|
|
1982
|
+
const threshold = kysely.sql.val(options.threshold);
|
|
1983
|
+
switch (options.mode) {
|
|
1984
|
+
case "simple": return kysely.sql`similarity(${fieldExpr}, ${valueExpr}) > ${threshold}`;
|
|
1985
|
+
case "word": return kysely.sql`word_similarity(${valueExpr}, ${fieldExpr}) > ${threshold}`;
|
|
1986
|
+
case "strictWord": return kysely.sql`strict_word_similarity(${valueExpr}, ${fieldExpr}) > ${threshold}`;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
buildFuzzyRelevanceOrderBy(query, fieldRefs, search, sort, mode, unaccent) {
|
|
1990
|
+
const valueExpr = this.normalizeForTrigram(kysely.sql.val(search), unaccent);
|
|
1991
|
+
const buildSimilarity = (fieldRef) => {
|
|
1992
|
+
const fieldExpr = this.normalizeForTrigram(fieldRef, unaccent);
|
|
1993
|
+
switch (mode) {
|
|
1994
|
+
case "simple": return kysely.sql`similarity(${fieldExpr}, ${valueExpr})`;
|
|
1995
|
+
case "word": return kysely.sql`word_similarity(${valueExpr}, ${fieldExpr})`;
|
|
1996
|
+
case "strictWord": return kysely.sql`strict_word_similarity(${valueExpr}, ${fieldExpr})`;
|
|
1997
|
+
}
|
|
1998
|
+
};
|
|
1999
|
+
if (fieldRefs.length === 1) return query.orderBy(buildSimilarity(fieldRefs[0]), sort);
|
|
2000
|
+
const similarities = fieldRefs.map((ref) => buildSimilarity(ref));
|
|
2001
|
+
return query.orderBy(kysely.sql`GREATEST(${kysely.sql.join(similarities)})`, sort);
|
|
2002
|
+
}
|
|
2003
|
+
buildFullTextFilter(fieldRef, payload) {
|
|
2004
|
+
const options = this.normalizeFullTextOptions(payload);
|
|
2005
|
+
const query = kysely.sql.val(options.search);
|
|
2006
|
+
if (options.config === void 0) return kysely.sql`to_tsvector(${fieldRef}) @@ to_tsquery(${query})`;
|
|
2007
|
+
const cfg = kysely.sql.val(options.config);
|
|
2008
|
+
return kysely.sql`to_tsvector(${cfg}::regconfig, ${fieldRef}) @@ to_tsquery(${cfg}::regconfig, ${query})`;
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Validate the user-provided `fts` filter payload. When `config` is omitted
|
|
2012
|
+
* it stays `undefined` so {@link buildFullTextFilter} can emit the no-regconfig
|
|
2013
|
+
* SQL form and let Postgres fall back to `default_text_search_config`.
|
|
2014
|
+
*/
|
|
2015
|
+
normalizeFullTextOptions(value) {
|
|
2016
|
+
(0, _zenstackhq_common_helpers.invariant)(value !== null && typeof value === "object" && !Array.isArray(value), "fts filter must be an object with at least a \"search\" field");
|
|
2017
|
+
const raw = value;
|
|
2018
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof raw["search"] === "string" && raw["search"].length > 0, "fts.search must be a non-empty string");
|
|
2019
|
+
if (raw["config"] !== void 0) (0, _zenstackhq_common_helpers.invariant)(typeof raw["config"] === "string" && raw["config"].length > 0, "fts.config must be a non-empty string");
|
|
2020
|
+
return {
|
|
2021
|
+
search: raw["search"],
|
|
2022
|
+
config: raw["config"]
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
buildFtsRelevanceOrderBy(query, fieldRefs, search, config, sort) {
|
|
2026
|
+
const q = kysely.sql.val(search);
|
|
2027
|
+
const document = fieldRefs.length === 1 ? kysely.sql`coalesce(${fieldRefs[0]}, '')` : kysely.sql`concat_ws(' ', ${kysely.sql.join(fieldRefs)})`;
|
|
2028
|
+
if (config === void 0) return query.orderBy(kysely.sql`ts_rank(to_tsvector(${document}), to_tsquery(${q}))`, sort);
|
|
2029
|
+
const cfg = kysely.sql.val(config);
|
|
2030
|
+
return query.orderBy(kysely.sql`ts_rank(to_tsvector(${cfg}::regconfig, ${document}), to_tsquery(${cfg}::regconfig, ${q}))`, sort);
|
|
2031
|
+
}
|
|
1826
2032
|
};
|
|
1827
2033
|
//#endregion
|
|
1828
2034
|
//#region src/client/crud/dialects/sqlite.ts
|
|
@@ -2040,6 +2246,18 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
2040
2246
|
return ob;
|
|
2041
2247
|
});
|
|
2042
2248
|
}
|
|
2249
|
+
buildFuzzyFilter(_fieldRef, _options) {
|
|
2250
|
+
throw createNotSupportedError("\"fuzzy\" filter is not supported by the \"sqlite\" provider");
|
|
2251
|
+
}
|
|
2252
|
+
buildFuzzyRelevanceOrderBy(_query, _fieldRefs, _search, _sort, _mode, _unaccent) {
|
|
2253
|
+
throw createNotSupportedError("\"_fuzzyRelevance\" ordering is not supported by the \"sqlite\" provider");
|
|
2254
|
+
}
|
|
2255
|
+
buildFullTextFilter(_fieldRef, _payload) {
|
|
2256
|
+
throw createNotSupportedError("\"fts\" filter is not supported by the \"sqlite\" provider");
|
|
2257
|
+
}
|
|
2258
|
+
buildFtsRelevanceOrderBy(_query, _fieldRefs, _search, _config, _sort) {
|
|
2259
|
+
throw createNotSupportedError("\"_ftsRelevance\" ordering is not supported by the \"sqlite\" provider");
|
|
2260
|
+
}
|
|
2043
2261
|
};
|
|
2044
2262
|
//#endregion
|
|
2045
2263
|
//#region src/client/crud/dialects/index.ts
|
|
@@ -2095,6 +2313,33 @@ function createQuerySchemaFactory(clientOrSchema, options) {
|
|
|
2095
2313
|
return new ZodSchemaFactory(clientOrSchema, options);
|
|
2096
2314
|
}
|
|
2097
2315
|
/**
|
|
2316
|
+
* Builds a `DateTime` value schema that accepts a `Date` object or any string
|
|
2317
|
+
* the JS `Date` constructor parses, and coerces it to a `Date`. ISO datetime,
|
|
2318
|
+
* ISO date, and time-only strings (e.g. `"09:00:00"` for `@db.Time` fields,
|
|
2319
|
+
* anchored to the Unix epoch) are the documented happy paths; other formats
|
|
2320
|
+
* accepted by `new Date(...)` also pass through, mirroring Prisma's pre-3.5
|
|
2321
|
+
* behaviour. Strings the engine can't parse fall through and are rejected by
|
|
2322
|
+
* `z.date()` with the standard error.
|
|
2323
|
+
*
|
|
2324
|
+
* @see https://github.com/zenstackhq/zenstack/issues/2631
|
|
2325
|
+
*/
|
|
2326
|
+
function coercedDateTimeSchema() {
|
|
2327
|
+
return zod.z.preprocess((val) => {
|
|
2328
|
+
if (typeof val !== "string") return val;
|
|
2329
|
+
if (/^\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d\d(?::\d\d)?)?$/.test(val)) {
|
|
2330
|
+
const hasTz = val.endsWith("Z") || /[+-]\d\d(?::\d\d)?$/.test(val);
|
|
2331
|
+
const d = /* @__PURE__ */ new Date(`1970-01-01T${val}${hasTz ? "" : "Z"}`);
|
|
2332
|
+
return isNaN(d.getTime()) ? val : d;
|
|
2333
|
+
}
|
|
2334
|
+
const d = new Date(val);
|
|
2335
|
+
return isNaN(d.getTime()) ? val : d;
|
|
2336
|
+
}, zod.z.union([
|
|
2337
|
+
zod.z.iso.datetime(),
|
|
2338
|
+
zod.z.iso.date(),
|
|
2339
|
+
zod.z.date()
|
|
2340
|
+
]));
|
|
2341
|
+
}
|
|
2342
|
+
/**
|
|
2098
2343
|
* Factory class responsible for creating and caching Zod schemas for ORM input validation.
|
|
2099
2344
|
*/
|
|
2100
2345
|
var ZodSchemaFactory = class {
|
|
@@ -2263,7 +2508,7 @@ var ZodSchemaFactory = class {
|
|
|
2263
2508
|
const typeDef = getTypeDef(this.schema, type);
|
|
2264
2509
|
(0, _zenstackhq_common_helpers.invariant)(typeDef, `Type definition "${type}" not found in schema`);
|
|
2265
2510
|
const schema = zod.z.looseObject(Object.fromEntries(Object.entries(typeDef.fields).map(([field, def]) => {
|
|
2266
|
-
let fieldSchema = this.makeScalarSchema(def.type);
|
|
2511
|
+
let fieldSchema = isTypeDef(this.schema, def.type) ? zod.z.lazy(() => this.makeTypeDefSchema(def.type)) : this.makeScalarSchema(def.type);
|
|
2267
2512
|
if (def.array) fieldSchema = fieldSchema.array();
|
|
2268
2513
|
if (def.optional) fieldSchema = fieldSchema.nullish();
|
|
2269
2514
|
return [field, fieldSchema];
|
|
@@ -2307,7 +2552,7 @@ var ZodSchemaFactory = class {
|
|
|
2307
2552
|
if (Object.keys(enumDef.values).length > 0) fieldSchema = this.makeEnumFilterSchema(fieldDef.type, !!fieldDef.optional, !!fieldDef.array, withAggregations, allowedFilterKinds);
|
|
2308
2553
|
} else if (fieldDef.array) fieldSchema = this.makeArrayFilterSchema(fieldDef.type, allowedFilterKinds);
|
|
2309
2554
|
else if (this.isTypeDefType(fieldDef.type)) fieldSchema = this.makeTypedJsonFilterSchema(fieldDef.type, !!fieldDef.optional, !!fieldDef.array, allowedFilterKinds);
|
|
2310
|
-
else fieldSchema = this.makePrimitiveFilterSchema(fieldDef.type, !!fieldDef.optional, withAggregations, allowedFilterKinds);
|
|
2555
|
+
else fieldSchema = this.makePrimitiveFilterSchema(fieldDef.type, !!fieldDef.optional, withAggregations, allowedFilterKinds, !!fieldDef.fuzzy, !!fieldDef.fullText);
|
|
2311
2556
|
}
|
|
2312
2557
|
if (fieldSchema) fields[field] = fieldSchema.optional();
|
|
2313
2558
|
}
|
|
@@ -2436,8 +2681,8 @@ var ZodSchemaFactory = class {
|
|
|
2436
2681
|
const filteredOperators = this.trimFilterOperators(operators, allowedFilterKinds);
|
|
2437
2682
|
return zod.z.strictObject(filteredOperators);
|
|
2438
2683
|
}
|
|
2439
|
-
makePrimitiveFilterSchema(type, optional, withAggregations, allowedFilterKinds) {
|
|
2440
|
-
return (0, ts_pattern.match)(type).with("String", () => this.makeStringFilterSchema(optional, withAggregations, allowedFilterKinds)).with(ts_pattern.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.makeNumberFilterSchema(type, optional, withAggregations, allowedFilterKinds)).with("Boolean", () => this.makeBooleanFilterSchema(optional, withAggregations, allowedFilterKinds)).with("DateTime", () => this.makeDateTimeFilterSchema(optional, withAggregations, allowedFilterKinds)).with("Bytes", () => this.makeBytesFilterSchema(optional, withAggregations, allowedFilterKinds)).with("Json", () => this.makeJsonFilterSchema(optional, allowedFilterKinds)).with("Unsupported", () => zod.z.never()).exhaustive();
|
|
2684
|
+
makePrimitiveFilterSchema(type, optional, withAggregations, allowedFilterKinds, withFuzzy = false, withFullText = false) {
|
|
2685
|
+
return (0, ts_pattern.match)(type).with("String", () => this.makeStringFilterSchema(optional, withAggregations, allowedFilterKinds, withFuzzy, withFullText)).with(ts_pattern.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.makeNumberFilterSchema(type, optional, withAggregations, allowedFilterKinds)).with("Boolean", () => this.makeBooleanFilterSchema(optional, withAggregations, allowedFilterKinds)).with("DateTime", () => this.makeDateTimeFilterSchema(optional, withAggregations, allowedFilterKinds)).with("Bytes", () => this.makeBytesFilterSchema(optional, withAggregations, allowedFilterKinds)).with("Json", () => this.makeJsonFilterSchema(optional, allowedFilterKinds)).with("Unsupported", () => zod.z.never()).exhaustive();
|
|
2441
2686
|
}
|
|
2442
2687
|
makeJsonValueSchema() {
|
|
2443
2688
|
const schema = zod.z.union([
|
|
@@ -2479,11 +2724,7 @@ var ZodSchemaFactory = class {
|
|
|
2479
2724
|
return schema;
|
|
2480
2725
|
}
|
|
2481
2726
|
makeDateTimeValueSchema() {
|
|
2482
|
-
const schema =
|
|
2483
|
-
zod.z.iso.datetime(),
|
|
2484
|
-
zod.z.iso.date(),
|
|
2485
|
-
zod.z.date()
|
|
2486
|
-
]);
|
|
2727
|
+
const schema = coercedDateTimeSchema();
|
|
2487
2728
|
this.registerSchema("DateTime", schema);
|
|
2488
2729
|
return schema;
|
|
2489
2730
|
}
|
|
@@ -2579,8 +2820,8 @@ var ZodSchemaFactory = class {
|
|
|
2579
2820
|
})}`, schema);
|
|
2580
2821
|
return schema;
|
|
2581
2822
|
}
|
|
2582
|
-
makeStringFilterSchema(optional, withAggregations, allowedFilterKinds) {
|
|
2583
|
-
const baseComponents = this.makeCommonPrimitiveFilterComponents(zod.z.string(), optional, () => zod.z.lazy(() => this.makeStringFilterSchema(optional, withAggregations, allowedFilterKinds)), void 0, withAggregations ? [
|
|
2823
|
+
makeStringFilterSchema(optional, withAggregations, allowedFilterKinds, withFuzzy = false, withFullText = false) {
|
|
2824
|
+
const baseComponents = this.makeCommonPrimitiveFilterComponents(zod.z.string(), optional, () => zod.z.lazy(() => this.makeStringFilterSchema(optional, withAggregations, allowedFilterKinds, withFuzzy, withFullText)), void 0, withAggregations ? [
|
|
2584
2825
|
"_count",
|
|
2585
2826
|
"_min",
|
|
2586
2827
|
"_max"
|
|
@@ -2589,6 +2830,8 @@ var ZodSchemaFactory = class {
|
|
|
2589
2830
|
startsWith: zod.z.string().optional(),
|
|
2590
2831
|
endsWith: zod.z.string().optional(),
|
|
2591
2832
|
contains: zod.z.string().optional(),
|
|
2833
|
+
...withFuzzy && this.providerSupportsFuzzySearch ? { fuzzy: this.makeFuzzyFilterSchema().optional() } : {},
|
|
2834
|
+
...withFullText && this.providerSupportsFullTextSearch ? { fts: this.makeFullTextFilterSchema().optional() } : {},
|
|
2592
2835
|
...this.providerSupportsCaseSensitivity ? { mode: this.makeStringModeSchema().optional() } : {}
|
|
2593
2836
|
};
|
|
2594
2837
|
const filteredStringOperators = this.trimFilterOperators(stringSpecificOperators, allowedFilterKinds);
|
|
@@ -2597,16 +2840,35 @@ var ZodSchemaFactory = class {
|
|
|
2597
2840
|
...filteredStringOperators
|
|
2598
2841
|
};
|
|
2599
2842
|
const schema = this.createUnionFilterSchema(zod.z.string(), optional, allComponents, allowedFilterKinds);
|
|
2843
|
+
const featureSuffix = `${withFuzzy ? "Fuzzy" : ""}${withFullText ? "FullText" : ""}`;
|
|
2600
2844
|
this.registerSchema(`StringFilter${this.filterSchemaSuffix({
|
|
2601
2845
|
optional,
|
|
2602
2846
|
allowedFilterKinds,
|
|
2603
2847
|
withAggregations
|
|
2604
|
-
})}`, schema);
|
|
2848
|
+
})}${featureSuffix}`, schema);
|
|
2605
2849
|
return schema;
|
|
2606
2850
|
}
|
|
2607
2851
|
makeStringModeSchema() {
|
|
2608
2852
|
return zod.z.union([zod.z.literal("default"), zod.z.literal("insensitive")]);
|
|
2609
2853
|
}
|
|
2854
|
+
makeFuzzyFilterSchema() {
|
|
2855
|
+
return zod.z.strictObject({
|
|
2856
|
+
search: zod.z.string().min(1),
|
|
2857
|
+
mode: zod.z.union([
|
|
2858
|
+
zod.z.literal("simple"),
|
|
2859
|
+
zod.z.literal("word"),
|
|
2860
|
+
zod.z.literal("strictWord")
|
|
2861
|
+
]).default("simple"),
|
|
2862
|
+
threshold: zod.z.number().min(0).max(1).optional(),
|
|
2863
|
+
unaccent: zod.z.boolean().default(false)
|
|
2864
|
+
});
|
|
2865
|
+
}
|
|
2866
|
+
makeFullTextFilterSchema() {
|
|
2867
|
+
return zod.z.strictObject({
|
|
2868
|
+
search: zod.z.string().min(1),
|
|
2869
|
+
config: zod.z.string().min(1).optional()
|
|
2870
|
+
});
|
|
2871
|
+
}
|
|
2610
2872
|
makeSelectSchema(model, options) {
|
|
2611
2873
|
const fields = {};
|
|
2612
2874
|
for (const [field, fieldDef] of this.getModelFields(model)) if (fieldDef.relation) {
|
|
@@ -2707,7 +2969,7 @@ var ZodSchemaFactory = class {
|
|
|
2707
2969
|
}).optional();
|
|
2708
2970
|
} else if (fieldDef.optional) fields[field] = zod.z.union([sort, zod.z.strictObject({
|
|
2709
2971
|
sort,
|
|
2710
|
-
nulls: zod.z.union([zod.z.literal("first"), zod.z.literal("last")])
|
|
2972
|
+
nulls: zod.z.union([zod.z.literal("first"), zod.z.literal("last")]).optional()
|
|
2711
2973
|
})]).optional();
|
|
2712
2974
|
else fields[field] = sort.optional();
|
|
2713
2975
|
if (WithAggregation) for (const agg of [
|
|
@@ -2717,6 +2979,29 @@ var ZodSchemaFactory = class {
|
|
|
2717
2979
|
"_min",
|
|
2718
2980
|
"_max"
|
|
2719
2981
|
]) fields[agg] = zod.z.lazy(() => this.makeOrderBySchema(model, true, false, options).optional());
|
|
2982
|
+
if (this.providerSupportsFuzzySearch) {
|
|
2983
|
+
const fuzzyFieldNames = this.getModelFields(model).filter(([, def]) => !def.relation && def.type === "String" && def.fuzzy === true).map(([name]) => name);
|
|
2984
|
+
if (fuzzyFieldNames.length > 0) fields["_fuzzyRelevance"] = zod.z.strictObject({
|
|
2985
|
+
fields: zod.z.array(zod.z.enum(fuzzyFieldNames)).min(1),
|
|
2986
|
+
search: zod.z.string(),
|
|
2987
|
+
mode: zod.z.union([
|
|
2988
|
+
zod.z.literal("simple"),
|
|
2989
|
+
zod.z.literal("word"),
|
|
2990
|
+
zod.z.literal("strictWord")
|
|
2991
|
+
]).default("simple"),
|
|
2992
|
+
unaccent: zod.z.boolean().default(false),
|
|
2993
|
+
sort
|
|
2994
|
+
}).optional();
|
|
2995
|
+
}
|
|
2996
|
+
if (this.providerSupportsFullTextSearch) {
|
|
2997
|
+
const fullTextFieldNames = this.getModelFields(model).filter(([, def]) => !def.relation && def.type === "String" && def.fullText === true).map(([name]) => name);
|
|
2998
|
+
if (fullTextFieldNames.length > 0) fields["_ftsRelevance"] = zod.z.strictObject({
|
|
2999
|
+
fields: zod.z.array(zod.z.enum(fullTextFieldNames)).min(1),
|
|
3000
|
+
search: zod.z.string().min(1),
|
|
3001
|
+
config: zod.z.string().min(1).optional(),
|
|
3002
|
+
sort
|
|
3003
|
+
}).optional();
|
|
3004
|
+
}
|
|
2720
3005
|
const schema = refineAtMostOneKey(zod.z.strictObject(fields));
|
|
2721
3006
|
let schemaId = `${model}OrderBy`;
|
|
2722
3007
|
if (withRelation) schemaId += "WithRelation";
|
|
@@ -3218,6 +3503,12 @@ var ZodSchemaFactory = class {
|
|
|
3218
3503
|
get providerSupportsCaseSensitivity() {
|
|
3219
3504
|
return this.schema.provider.type === "postgresql";
|
|
3220
3505
|
}
|
|
3506
|
+
get providerSupportsFullTextSearch() {
|
|
3507
|
+
return this.schema.provider.type === "postgresql";
|
|
3508
|
+
}
|
|
3509
|
+
get providerSupportsFuzzySearch() {
|
|
3510
|
+
return this.schema.provider.type === "postgresql";
|
|
3511
|
+
}
|
|
3221
3512
|
/**
|
|
3222
3513
|
* Gets the effective set of allowed FilterKind values for a specific model and field.
|
|
3223
3514
|
* Respects the precedence: model[field] > model.$all > $all[field] > $all.$all.
|
|
@@ -3379,6 +3670,8 @@ __decorate([
|
|
|
3379
3670
|
Object,
|
|
3380
3671
|
Boolean,
|
|
3381
3672
|
Boolean,
|
|
3673
|
+
Object,
|
|
3674
|
+
Object,
|
|
3382
3675
|
Object
|
|
3383
3676
|
]),
|
|
3384
3677
|
__decorateMetadata("design:returntype", void 0)
|
|
@@ -3448,6 +3741,8 @@ __decorate([
|
|
|
3448
3741
|
__decorateMetadata("design:paramtypes", [
|
|
3449
3742
|
Boolean,
|
|
3450
3743
|
Boolean,
|
|
3744
|
+
Object,
|
|
3745
|
+
Object,
|
|
3451
3746
|
Object
|
|
3452
3747
|
]),
|
|
3453
3748
|
__decorateMetadata("design:returntype", typeof (_ref10 = typeof zod.ZodType !== "undefined" && zod.ZodType) === "function" ? _ref10 : Object)
|
|
@@ -3937,7 +4232,7 @@ var BaseOperationHandler = class {
|
|
|
3937
4232
|
return new this.constructor(client, this.model, this.inputValidator);
|
|
3938
4233
|
}
|
|
3939
4234
|
get hasPolicyEnabled() {
|
|
3940
|
-
return this.options.plugins?.some((plugin) => plugin.
|
|
4235
|
+
return this.options.plugins?.some((plugin) => plugin.id === "policy") ?? false;
|
|
3941
4236
|
}
|
|
3942
4237
|
requireModel(model) {
|
|
3943
4238
|
return requireModel(this.schema, model);
|
|
@@ -4038,8 +4333,8 @@ var BaseOperationHandler = class {
|
|
|
4038
4333
|
const postCreateRelations = {};
|
|
4039
4334
|
for (const [field, value] of Object.entries(data)) {
|
|
4040
4335
|
const fieldDef = this.requireField(model, field);
|
|
4041
|
-
if (isScalarField(this.schema, model, field) || isForeignKeyField(this.schema, model, field)) if (fieldDef.array && value && typeof value === "object" && "set" in value && Array.isArray(value.set)) createFields[field] = this.dialect.transformInput(value.set, fieldDef.type, true);
|
|
4042
|
-
else createFields[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array);
|
|
4336
|
+
if (isScalarField(this.schema, model, field) || isForeignKeyField(this.schema, model, field)) if (fieldDef.array && value && typeof value === "object" && "set" in value && Array.isArray(value.set)) createFields[field] = this.dialect.transformInput(value.set, fieldDef.type, true, fieldDef);
|
|
4337
|
+
else createFields[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4043
4338
|
else if (!getManyToManyRelation(this.schema, model, field) && fieldDef.relation?.fields && fieldDef.relation?.references) {
|
|
4044
4339
|
const fkValues = await this.processOwnedRelationForCreate(kysely$7, fieldDef, value);
|
|
4045
4340
|
for (let i = 0; i < fieldDef.relation.fields.length; i++) createFields[fieldDef.relation.fields[i]] = fkValues[fieldDef.relation.references[i]];
|
|
@@ -4096,7 +4391,7 @@ var BaseOperationHandler = class {
|
|
|
4096
4391
|
});
|
|
4097
4392
|
const discriminatorField = getDiscriminatorField(this.schema, model);
|
|
4098
4393
|
(0, _zenstackhq_common_helpers.invariant)(discriminatorField, `Base model "${model}" must have a discriminator field`);
|
|
4099
|
-
thisCreateFields[discriminatorField] = forModel;
|
|
4394
|
+
thisCreateFields[discriminatorField] = getDelegateDiscriminatorValue(this.schema, forModel);
|
|
4100
4395
|
const baseEntity = await this.create(kysely$8, model, thisCreateFields, void 0, true);
|
|
4101
4396
|
const idValues = extractIdFields(baseEntity, this.schema, model);
|
|
4102
4397
|
Object.assign(remainingFields, idValues);
|
|
@@ -4239,7 +4534,7 @@ var BaseOperationHandler = class {
|
|
|
4239
4534
|
for (const [name, value] of Object.entries(item)) {
|
|
4240
4535
|
const fieldDef = this.requireField(model, name);
|
|
4241
4536
|
(0, _zenstackhq_common_helpers.invariant)(!fieldDef.relation, "createMany does not support relations");
|
|
4242
|
-
newItem[name] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array);
|
|
4537
|
+
newItem[name] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4243
4538
|
}
|
|
4244
4539
|
if (fromRelation) for (const { fk, pk } of relationKeyPairs) newItem[fk] = fromRelation.ids[pk];
|
|
4245
4540
|
return this.fillGeneratedAndDefaultValues(modelDef, newItem);
|
|
@@ -4255,7 +4550,7 @@ var BaseOperationHandler = class {
|
|
|
4255
4550
|
if (Object.keys(item).length === allPassedFields.length) continue;
|
|
4256
4551
|
for (const field of allPassedFields) if (!(field in item)) {
|
|
4257
4552
|
const fieldDef = this.requireField(model, field);
|
|
4258
|
-
if (fieldDef.default !== void 0 && fieldDef.default !== null && typeof fieldDef.default !== "object") item[field] = this.dialect.transformInput(fieldDef.default, fieldDef.type, !!fieldDef.array);
|
|
4553
|
+
if (fieldDef.default !== void 0 && fieldDef.default !== null && typeof fieldDef.default !== "object") item[field] = this.dialect.transformInput(fieldDef.default, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4259
4554
|
}
|
|
4260
4555
|
}
|
|
4261
4556
|
}
|
|
@@ -4288,7 +4583,7 @@ var BaseOperationHandler = class {
|
|
|
4288
4583
|
if (this.getField(model, field)) thisCreateFields[field] = value;
|
|
4289
4584
|
else remainingFields[field] = value;
|
|
4290
4585
|
});
|
|
4291
|
-
thisCreateFields[discriminatorField] = forModel;
|
|
4586
|
+
thisCreateFields[discriminatorField] = getDelegateDiscriminatorValue(this.schema, forModel);
|
|
4292
4587
|
thisCreateRows.push(thisCreateFields);
|
|
4293
4588
|
remainingFieldRows.push(remainingFields);
|
|
4294
4589
|
}
|
|
@@ -4318,15 +4613,15 @@ var BaseOperationHandler = class {
|
|
|
4318
4613
|
if (!(field in data)) {
|
|
4319
4614
|
if (typeof fieldDef?.default === "object" && "kind" in fieldDef.default) {
|
|
4320
4615
|
const generated = this.evalGenerator(fieldDef.default);
|
|
4321
|
-
if (generated !== void 0) values[field] = this.dialect.transformInput(generated, fieldDef.type, !!fieldDef.array);
|
|
4322
|
-
} else if (fieldDef?.updatedAt) values[field] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false);
|
|
4616
|
+
if (generated !== void 0) values[field] = this.dialect.transformInput(generated, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4617
|
+
} else if (fieldDef?.updatedAt) values[field] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false, fieldDef);
|
|
4323
4618
|
else if (fieldDef?.default !== void 0) {
|
|
4324
4619
|
let value = fieldDef.default;
|
|
4325
4620
|
if (fieldDef.type === "Json") {
|
|
4326
4621
|
if (fieldDef.array && Array.isArray(value)) value = value.map((v) => typeof v === "string" ? JSON.parse(v) : v);
|
|
4327
4622
|
else if (typeof value === "string") value = JSON.parse(value);
|
|
4328
4623
|
}
|
|
4329
|
-
values[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array);
|
|
4624
|
+
values[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4330
4625
|
}
|
|
4331
4626
|
}
|
|
4332
4627
|
}
|
|
@@ -4376,7 +4671,7 @@ var BaseOperationHandler = class {
|
|
|
4376
4671
|
const ignoredFields = new Set(typeof fieldDef.updatedAt === "boolean" ? [] : fieldDef.updatedAt.ignore);
|
|
4377
4672
|
if (Object.keys(data).some((field) => (isScalarField(this.schema, modelDef.name, field) || isForeignKeyField(this.schema, modelDef.name, field)) && !ignoredFields.has(field))) {
|
|
4378
4673
|
if (finalData === data) finalData = (0, _zenstackhq_common_helpers.clone)(data);
|
|
4379
|
-
finalData[fieldName] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false);
|
|
4674
|
+
finalData[fieldName] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false, fieldDef);
|
|
4380
4675
|
autoUpdatedFields.push(fieldName);
|
|
4381
4676
|
}
|
|
4382
4677
|
}
|
|
@@ -4480,7 +4775,7 @@ var BaseOperationHandler = class {
|
|
|
4480
4775
|
const fieldDef = this.requireField(model, field);
|
|
4481
4776
|
if (this.isNumericIncrementalUpdate(fieldDef, data[field])) return this.transformIncrementalUpdate(model, field, fieldDef, data[field]);
|
|
4482
4777
|
if (fieldDef.array && typeof data[field] === "object" && !Array.isArray(data[field]) && data[field]) return this.transformScalarListUpdate(model, field, fieldDef, data[field]);
|
|
4483
|
-
return this.dialect.transformInput(data[field], fieldDef.type, !!fieldDef.array);
|
|
4778
|
+
return this.dialect.transformInput(data[field], fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4484
4779
|
}
|
|
4485
4780
|
isNumericIncrementalUpdate(fieldDef, value) {
|
|
4486
4781
|
if (!this.isNumericField(fieldDef)) return false;
|
|
@@ -4508,7 +4803,7 @@ var BaseOperationHandler = class {
|
|
|
4508
4803
|
transformIncrementalUpdate(model, field, fieldDef, payload) {
|
|
4509
4804
|
(0, _zenstackhq_common_helpers.invariant)(Object.keys(payload).length === 1, "Only one of \"set\", \"increment\", \"decrement\", \"multiply\", or \"divide\" can be provided");
|
|
4510
4805
|
const key = Object.keys(payload)[0];
|
|
4511
|
-
const value = this.dialect.transformInput(payload[key], fieldDef.type, false);
|
|
4806
|
+
const value = this.dialect.transformInput(payload[key], fieldDef.type, false, fieldDef);
|
|
4512
4807
|
const eb = (0, kysely.expressionBuilder)();
|
|
4513
4808
|
const fieldRef = this.dialect.fieldRef(model, field);
|
|
4514
4809
|
return (0, ts_pattern.match)(key).with("set", () => value).with("increment", () => eb(fieldRef, "+", value)).with("decrement", () => eb(fieldRef, "-", value)).with("multiply", () => eb(fieldRef, "*", value)).with("divide", () => eb(fieldRef, "/", value)).otherwise(() => {
|
|
@@ -4518,7 +4813,7 @@ var BaseOperationHandler = class {
|
|
|
4518
4813
|
transformScalarListUpdate(model, field, fieldDef, payload) {
|
|
4519
4814
|
(0, _zenstackhq_common_helpers.invariant)(Object.keys(payload).length === 1, "Only one of \"set\", \"push\" can be provided");
|
|
4520
4815
|
const key = Object.keys(payload)[0];
|
|
4521
|
-
const value = this.dialect.transformInput(payload[key], fieldDef.type, true);
|
|
4816
|
+
const value = this.dialect.transformInput(payload[key], fieldDef.type, true, fieldDef);
|
|
4522
4817
|
const eb = (0, kysely.expressionBuilder)();
|
|
4523
4818
|
const fieldRef = this.dialect.fieldRef(model, field);
|
|
4524
4819
|
return (0, ts_pattern.match)(key).with("set", () => value).with("push", () => {
|
|
@@ -6929,12 +7224,21 @@ var ClientImpl = class ClientImpl {
|
|
|
6929
7224
|
});
|
|
6930
7225
|
}
|
|
6931
7226
|
}
|
|
7227
|
+
getPromiseCallback(promise) {
|
|
7228
|
+
(0, _zenstackhq_common_helpers.invariant)(promise.cb, "Invalid ZenStackPromise, missing cb property");
|
|
7229
|
+
const cb = promise.cb;
|
|
7230
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof cb === "function", "Invalid ZenStackPromise, cb property is not a function");
|
|
7231
|
+
return promise.cb;
|
|
7232
|
+
}
|
|
6932
7233
|
async sequentialTransaction(arg, options) {
|
|
6933
7234
|
const execute = async (tx) => {
|
|
6934
7235
|
const txClient = new ClientImpl(this.schema, this.$options, this);
|
|
6935
7236
|
txClient.kysely = tx;
|
|
6936
7237
|
const result = [];
|
|
6937
|
-
for (const promise of arg)
|
|
7238
|
+
for (const promise of arg) {
|
|
7239
|
+
const cb = this.getPromiseCallback(promise);
|
|
7240
|
+
result.push(await cb(txClient));
|
|
7241
|
+
}
|
|
6938
7242
|
return result;
|
|
6939
7243
|
};
|
|
6940
7244
|
if (this.kysely.isTransaction) return execute(this.kysely);
|
|
@@ -7721,6 +8025,15 @@ var DefaultOperationNodeVisitor = class extends kysely.OperationNodeVisitor {
|
|
|
7721
8025
|
visitCollate(node) {
|
|
7722
8026
|
this.defaultVisit(node);
|
|
7723
8027
|
}
|
|
8028
|
+
visitAlterType(node) {
|
|
8029
|
+
this.defaultVisit(node);
|
|
8030
|
+
}
|
|
8031
|
+
visitAddValue(node) {
|
|
8032
|
+
this.defaultVisit(node);
|
|
8033
|
+
}
|
|
8034
|
+
visitRenameValue(node) {
|
|
8035
|
+
this.defaultVisit(node);
|
|
8036
|
+
}
|
|
7724
8037
|
};
|
|
7725
8038
|
//#endregion
|
|
7726
8039
|
//#region src/utils/schema-utils.ts
|
|
@@ -7796,6 +8109,8 @@ exports.CoreUpdateOperations = CoreUpdateOperations;
|
|
|
7796
8109
|
exports.CoreWriteOperations = CoreWriteOperations;
|
|
7797
8110
|
exports.DbNull = require_common_types.DbNull;
|
|
7798
8111
|
exports.DbNullClass = require_common_types.DbNullClass;
|
|
8112
|
+
exports.ExtQueryArgsMarker = ExtQueryArgsMarker;
|
|
8113
|
+
exports.ExtResultMarker = ExtResultMarker;
|
|
7799
8114
|
exports.InputValidator = InputValidator;
|
|
7800
8115
|
exports.JsonNull = require_common_types.JsonNull;
|
|
7801
8116
|
exports.JsonNullClass = require_common_types.JsonNullClass;
|