@zenstackhq/orm 3.7.0-beta.1 → 3.7.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/index.cjs +379 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +334 -37
- package/dist/index.d.mts +334 -37
- package/dist/index.mjs +378 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -10
package/dist/index.cjs
CHANGED
|
@@ -568,6 +568,8 @@ const FILTER_PROPERTY_TO_KIND = {
|
|
|
568
568
|
array_contains: "Json",
|
|
569
569
|
array_starts_with: "Json",
|
|
570
570
|
array_ends_with: "Json",
|
|
571
|
+
fuzzy: "Fuzzy",
|
|
572
|
+
fts: "FullText",
|
|
571
573
|
has: "List",
|
|
572
574
|
hasEvery: "List",
|
|
573
575
|
hasSome: "List",
|
|
@@ -587,6 +589,19 @@ let TransactionIsolationLevel = /* @__PURE__ */ function(TransactionIsolationLev
|
|
|
587
589
|
return TransactionIsolationLevel;
|
|
588
590
|
}({});
|
|
589
591
|
/**
|
|
592
|
+
* Symbol used as a type-only key on `ClientContract` to brand the `ExtQueryArgs`
|
|
593
|
+
* generic slot. Hidden from member-access autocomplete since symbol keys are
|
|
594
|
+
* not surfaced. Consumed by `InferExtQueryArgs` to recover the slot.
|
|
595
|
+
* @internal
|
|
596
|
+
*/
|
|
597
|
+
const ExtQueryArgsMarker = Symbol("zenstack.client.extQueryArgs");
|
|
598
|
+
/**
|
|
599
|
+
* Symbol used as a type-only key on `ClientContract` to brand the `ExtResult`
|
|
600
|
+
* generic slot. Consumed by `InferExtResult` to recover the slot.
|
|
601
|
+
* @internal
|
|
602
|
+
*/
|
|
603
|
+
const ExtResultMarker = Symbol("zenstack.client.extResult");
|
|
604
|
+
/**
|
|
590
605
|
* CRUD operations.
|
|
591
606
|
*/
|
|
592
607
|
const CRUD = [
|
|
@@ -609,8 +624,13 @@ var BaseCrudDialect = class {
|
|
|
609
624
|
}
|
|
610
625
|
/**
|
|
611
626
|
* Transforms input value before sending to database.
|
|
627
|
+
*
|
|
628
|
+
* `fieldDef` is optional so existing callers that don't have it stay
|
|
629
|
+
* source-compatible. Dialects can use it to inspect `@db.*` native-type
|
|
630
|
+
* attributes (e.g. to format `@db.Time` values as `HH:MM:SS` rather than
|
|
631
|
+
* full ISO timestamps).
|
|
612
632
|
*/
|
|
613
|
-
transformInput(value, _type, _forArrayField) {
|
|
633
|
+
transformInput(value, _type, _forArrayField, _fieldDef) {
|
|
614
634
|
return value;
|
|
615
635
|
}
|
|
616
636
|
/**
|
|
@@ -653,7 +673,17 @@ var BaseCrudDialect = class {
|
|
|
653
673
|
if (existingOrderBy.length > 0 && !alreadySatisfied) effectiveOrderBy = [...distinctFields.map((f) => ({ [f]: "asc" })), ...existingOrderBy];
|
|
654
674
|
}
|
|
655
675
|
result = this.buildOrderBy(result, model, modelAlias, effectiveOrderBy, negateOrderBy, take);
|
|
656
|
-
if (args.cursor)
|
|
676
|
+
if (args.cursor) {
|
|
677
|
+
if (effectiveOrderBy) {
|
|
678
|
+
const offendingKey = (0, _zenstackhq_common_helpers.enumerate)(effectiveOrderBy).map((ob) => {
|
|
679
|
+
if (typeof ob !== "object" || ob === null) return void 0;
|
|
680
|
+
if ("_fuzzyRelevance" in ob) return "_fuzzyRelevance";
|
|
681
|
+
if ("_ftsRelevance" in ob) return "_ftsRelevance";
|
|
682
|
+
}).find((k) => k !== void 0);
|
|
683
|
+
if (offendingKey) throw createNotSupportedError(`cursor pagination cannot be combined with "${offendingKey}" ordering`);
|
|
684
|
+
}
|
|
685
|
+
result = this.buildCursorFilter(model, result, args.cursor, effectiveOrderBy, negateOrderBy, modelAlias);
|
|
686
|
+
}
|
|
657
687
|
return result;
|
|
658
688
|
}
|
|
659
689
|
buildFilter(model, modelAlias, where) {
|
|
@@ -793,7 +823,7 @@ var BaseCrudDialect = class {
|
|
|
793
823
|
for (const [key, _value] of Object.entries(payload)) {
|
|
794
824
|
if (_value === void 0) continue;
|
|
795
825
|
(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);
|
|
826
|
+
const value = this.transformInput(_value, fieldType, true, fieldDef);
|
|
797
827
|
let receiver = fieldRef;
|
|
798
828
|
if (isEnum(this.schema, fieldType)) receiver = this.eb.cast(fieldRef, kysely.sql.raw("text[]"));
|
|
799
829
|
const buildArray = (value) => {
|
|
@@ -828,7 +858,7 @@ var BaseCrudDialect = class {
|
|
|
828
858
|
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
859
|
return this.buildJsonFilter(fieldRef, payload, fieldDef);
|
|
830
860
|
}
|
|
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", () => {
|
|
861
|
+
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
862
|
throw createInvalidInputError(`Unsupported field cannot be used in filters`);
|
|
833
863
|
}).exhaustive();
|
|
834
864
|
}
|
|
@@ -996,13 +1026,23 @@ var BaseCrudDialect = class {
|
|
|
996
1026
|
consumedKeys
|
|
997
1027
|
};
|
|
998
1028
|
}
|
|
999
|
-
buildStringFilter(fieldRef, payload) {
|
|
1029
|
+
buildStringFilter(fieldRef, payload, fieldDef) {
|
|
1000
1030
|
let mode;
|
|
1001
1031
|
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));
|
|
1032
|
+
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
1033
|
if (payload && typeof payload === "object") for (const [key, value] of Object.entries(payload)) {
|
|
1004
1034
|
if (key === "mode" || consumedKeys.includes(key)) continue;
|
|
1005
1035
|
if (value === void 0) continue;
|
|
1036
|
+
if (key === "fuzzy") {
|
|
1037
|
+
(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`);
|
|
1038
|
+
conditions.push(this.buildFuzzyFilter(fieldRef, this.normalizeFuzzyOptions(value)));
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
if (key === "fts") {
|
|
1042
|
+
(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`);
|
|
1043
|
+
conditions.push(this.buildFullTextFilter(fieldRef, value));
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1006
1046
|
(0, _zenstackhq_common_helpers.invariant)(typeof value === "string", `${key} value must be a string`);
|
|
1007
1047
|
const escapedValue = this.escapeLikePattern(value);
|
|
1008
1048
|
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 +1111,14 @@ var BaseCrudDialect = class {
|
|
|
1071
1111
|
(0, _zenstackhq_common_helpers.enumerate)(orderBy).forEach((orderBy, index) => {
|
|
1072
1112
|
for (const [field, value] of Object.entries(orderBy)) {
|
|
1073
1113
|
if (!value) continue;
|
|
1114
|
+
if (field === "_fuzzyRelevance") {
|
|
1115
|
+
result = this.applyFuzzyRelevanceOrderBy(result, model, modelAlias, value, negated, buildFieldRef);
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
if (field === "_ftsRelevance") {
|
|
1119
|
+
result = this.applyFtsRelevanceOrderBy(result, model, modelAlias, value, negated, buildFieldRef);
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1074
1122
|
if ([
|
|
1075
1123
|
"_count",
|
|
1076
1124
|
"_avg",
|
|
@@ -1078,47 +1126,84 @@ var BaseCrudDialect = class {
|
|
|
1078
1126
|
"_min",
|
|
1079
1127
|
"_max"
|
|
1080
1128
|
].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
|
-
}
|
|
1129
|
+
result = this.applyAggregationOrderBy(result, model, modelAlias, field, value, negated, buildFieldRef);
|
|
1086
1130
|
continue;
|
|
1087
1131
|
}
|
|
1088
1132
|
const fieldDef = requireField(this.schema, model, field);
|
|
1089
|
-
if (!fieldDef.relation)
|
|
1090
|
-
|
|
1091
|
-
if (value === "asc" || value === "desc") result = result.orderBy(fieldRef, this.negateSort(value, negated));
|
|
1092
|
-
else if (typeof value === "object" && "nulls" in value && "sort" in value && (value.sort === "asc" || value.sort === "desc") && (value.nulls === "first" || value.nulls === "last")) result = this.buildOrderByField(result, fieldRef, this.negateSort(value.sort, negated), value.nulls);
|
|
1093
|
-
} else {
|
|
1094
|
-
const relationModel = fieldDef.type;
|
|
1095
|
-
if (fieldDef.array) {
|
|
1096
|
-
if (typeof value !== "object") throw createInvalidInputError(`invalid orderBy value for field "${field}"`);
|
|
1097
|
-
if ("_count" in value) {
|
|
1098
|
-
(0, _zenstackhq_common_helpers.invariant)(value._count === "asc" || value._count === "desc", "invalid orderBy value for field \"_count\"");
|
|
1099
|
-
const sort = this.negateSort(value._count, negated);
|
|
1100
|
-
result = result.orderBy((eb) => {
|
|
1101
|
-
const subQueryAlias = tmpAlias(`${modelAlias}$ob$${field}$ct`);
|
|
1102
|
-
let subQuery = this.buildSelectModel(relationModel, subQueryAlias);
|
|
1103
|
-
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
|
|
1104
|
-
subQuery = subQuery.where(() => this.and(...joinPairs.map(([left, right]) => eb(this.eb.ref(left), "=", this.eb.ref(right)))));
|
|
1105
|
-
subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
|
|
1106
|
-
return subQuery;
|
|
1107
|
-
}, sort);
|
|
1108
|
-
}
|
|
1109
|
-
} else {
|
|
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
|
-
}
|
|
1133
|
+
if (!fieldDef.relation) result = this.applyScalarOrderBy(result, model, modelAlias, field, value, negated, buildFieldRef);
|
|
1134
|
+
else result = this.applyRelationOrderBy(result, model, modelAlias, field, fieldDef, value, negated, take, index);
|
|
1118
1135
|
}
|
|
1119
1136
|
});
|
|
1120
1137
|
return result;
|
|
1121
1138
|
}
|
|
1139
|
+
applyRelationOrderBy(query, model, modelAlias, field, fieldDef, value, negated, take, index) {
|
|
1140
|
+
const relationModel = fieldDef.type;
|
|
1141
|
+
if (fieldDef.array) {
|
|
1142
|
+
if (typeof value !== "object") throw createInvalidInputError(`invalid orderBy value for field "${field}"`);
|
|
1143
|
+
if ("_count" in value) {
|
|
1144
|
+
(0, _zenstackhq_common_helpers.invariant)(value._count === "asc" || value._count === "desc", "invalid orderBy value for field \"_count\"");
|
|
1145
|
+
const sort = this.negateSort(value._count, negated);
|
|
1146
|
+
return query.orderBy((eb) => {
|
|
1147
|
+
const subQueryAlias = tmpAlias(`${modelAlias}$ob$${field}$ct`);
|
|
1148
|
+
let subQuery = this.buildSelectModel(relationModel, subQueryAlias);
|
|
1149
|
+
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
|
|
1150
|
+
subQuery = subQuery.where(() => this.and(...joinPairs.map(([left, right]) => eb(this.eb.ref(left), "=", this.eb.ref(right)))));
|
|
1151
|
+
subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
|
|
1152
|
+
return subQuery;
|
|
1153
|
+
}, sort);
|
|
1154
|
+
}
|
|
1155
|
+
return query;
|
|
1156
|
+
}
|
|
1157
|
+
const joinAlias = tmpAlias(`${modelAlias}$ob$${index}`);
|
|
1158
|
+
const joined = query.leftJoin(`${relationModel} as ${joinAlias}`, (join) => {
|
|
1159
|
+
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, joinAlias);
|
|
1160
|
+
return join.on((eb) => this.and(...joinPairs.map(([left, right]) => eb(this.eb.ref(left), "=", this.eb.ref(right)))));
|
|
1161
|
+
});
|
|
1162
|
+
return this.buildOrderBy(joined, relationModel, joinAlias, value, negated, take);
|
|
1163
|
+
}
|
|
1164
|
+
applyScalarOrderBy(query, model, modelAlias, field, value, negated, buildFieldRef) {
|
|
1165
|
+
const fieldRef = buildFieldRef(model, field, modelAlias);
|
|
1166
|
+
if (value === "asc" || value === "desc") return query.orderBy(fieldRef, this.negateSort(value, negated));
|
|
1167
|
+
if (typeof value === "object" && "sort" in value && (value.sort === "asc" || value.sort === "desc")) {
|
|
1168
|
+
const sort = this.negateSort(value.sort, negated);
|
|
1169
|
+
if (value.nulls === "first" || value.nulls === "last") return this.buildOrderByField(query, fieldRef, sort, value.nulls);
|
|
1170
|
+
else return query.orderBy(fieldRef, sort);
|
|
1171
|
+
}
|
|
1172
|
+
return query;
|
|
1173
|
+
}
|
|
1174
|
+
applyAggregationOrderBy(query, model, modelAlias, field, value, negated, buildFieldRef) {
|
|
1175
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value === "object", `invalid orderBy value for field "${field}"`);
|
|
1176
|
+
let result = query;
|
|
1177
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1178
|
+
(0, _zenstackhq_common_helpers.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
1179
|
+
result = result.orderBy((eb) => aggregate(eb, buildFieldRef(model, k, modelAlias), field), this.negateSort(v, negated));
|
|
1180
|
+
}
|
|
1181
|
+
return result;
|
|
1182
|
+
}
|
|
1183
|
+
applyFuzzyRelevanceOrderBy(query, model, modelAlias, value, negated, buildFieldRef) {
|
|
1184
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value === "object" && "fields" in value && "search" in value && "sort" in value, "invalid orderBy value for \"_fuzzyRelevance\"");
|
|
1185
|
+
(0, _zenstackhq_common_helpers.invariant)(Array.isArray(value.fields) && value.fields.length > 0, "_fuzzyRelevance.fields must be a non-empty array");
|
|
1186
|
+
(0, _zenstackhq_common_helpers.invariant)(value.sort === "asc" || value.sort === "desc", "invalid sort value for \"_fuzzyRelevance\"");
|
|
1187
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value.search === "string" && value.search.length > 0, "_fuzzyRelevance.search must be a non-empty string");
|
|
1188
|
+
const mode = value.mode ?? "simple";
|
|
1189
|
+
(0, _zenstackhq_common_helpers.invariant)(mode === "simple" || mode === "word" || mode === "strictWord", "_fuzzyRelevance.mode must be \"simple\", \"word\" or \"strictWord\"");
|
|
1190
|
+
const unaccent = value.unaccent ?? false;
|
|
1191
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof unaccent === "boolean", "_fuzzyRelevance.unaccent must be a boolean");
|
|
1192
|
+
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\``);
|
|
1193
|
+
const fieldRefs = value.fields.map((f) => buildFieldRef(model, f, modelAlias));
|
|
1194
|
+
return this.buildFuzzyRelevanceOrderBy(query, fieldRefs, value.search, this.negateSort(value.sort, negated), mode, unaccent);
|
|
1195
|
+
}
|
|
1196
|
+
applyFtsRelevanceOrderBy(query, model, modelAlias, value, negated, buildFieldRef) {
|
|
1197
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value === "object" && "fields" in value && "search" in value && "sort" in value, "invalid orderBy value for \"_ftsRelevance\"");
|
|
1198
|
+
(0, _zenstackhq_common_helpers.invariant)(Array.isArray(value.fields) && value.fields.length > 0, "_ftsRelevance.fields must be a non-empty array");
|
|
1199
|
+
(0, _zenstackhq_common_helpers.invariant)(value.sort === "asc" || value.sort === "desc", "invalid sort value for \"_ftsRelevance\"");
|
|
1200
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof value.search === "string" && value.search.length > 0, "_ftsRelevance.search must be a non-empty string");
|
|
1201
|
+
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");
|
|
1202
|
+
const config = value.config;
|
|
1203
|
+
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\``);
|
|
1204
|
+
const fieldRefs = value.fields.map((f) => buildFieldRef(model, f, modelAlias));
|
|
1205
|
+
return this.buildFtsRelevanceOrderBy(query, fieldRefs, value.search, config, this.negateSort(value.sort, negated));
|
|
1206
|
+
}
|
|
1122
1207
|
buildSelectAllFields(model, query, omit, modelAlias) {
|
|
1123
1208
|
let result = query;
|
|
1124
1209
|
for (const fieldDef of getModelFields(this.schema, model, {
|
|
@@ -1261,6 +1346,27 @@ var BaseCrudDialect = class {
|
|
|
1261
1346
|
buildComparison(left, _leftFieldDef, op, right, _rightFieldDef) {
|
|
1262
1347
|
return this.eb(left, op, right);
|
|
1263
1348
|
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Validate the user-provided fuzzy filter payload and apply defaults so dialects
|
|
1351
|
+
* always receive a fully-resolved {@link FuzzyFilterOptions} value.
|
|
1352
|
+
*/
|
|
1353
|
+
normalizeFuzzyOptions(value) {
|
|
1354
|
+
(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");
|
|
1355
|
+
const raw = value;
|
|
1356
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof raw["search"] === "string" && raw["search"].length > 0, "fuzzy.search must be a non-empty string");
|
|
1357
|
+
const mode = raw["mode"] ?? "simple";
|
|
1358
|
+
(0, _zenstackhq_common_helpers.invariant)(mode === "simple" || mode === "word" || mode === "strictWord", "fuzzy.mode must be \"simple\", \"word\" or \"strictWord\"");
|
|
1359
|
+
const threshold = raw["threshold"];
|
|
1360
|
+
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");
|
|
1361
|
+
const unaccent = raw["unaccent"] ?? false;
|
|
1362
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof unaccent === "boolean", "fuzzy.unaccent must be a boolean");
|
|
1363
|
+
return {
|
|
1364
|
+
search: raw["search"],
|
|
1365
|
+
mode,
|
|
1366
|
+
threshold,
|
|
1367
|
+
unaccent
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1264
1370
|
};
|
|
1265
1371
|
//#endregion
|
|
1266
1372
|
//#region src/client/crud/dialects/lateral-join-dialect-base.ts
|
|
@@ -1545,9 +1651,32 @@ var MySqlCrudDialect = class extends LateralJoinDialectBase {
|
|
|
1545
1651
|
}
|
|
1546
1652
|
return result;
|
|
1547
1653
|
}
|
|
1654
|
+
buildFuzzyFilter(_fieldRef, _options) {
|
|
1655
|
+
throw createNotSupportedError("\"fuzzy\" filter is not supported by the \"mysql\" provider");
|
|
1656
|
+
}
|
|
1657
|
+
buildFuzzyRelevanceOrderBy(_query, _fieldRefs, _search, _sort, _mode, _unaccent) {
|
|
1658
|
+
throw createNotSupportedError("\"_fuzzyRelevance\" ordering is not supported by the \"mysql\" provider");
|
|
1659
|
+
}
|
|
1660
|
+
buildFullTextFilter(_fieldRef, _payload) {
|
|
1661
|
+
throw createNotSupportedError("\"fts\" filter is not supported by the \"mysql\" provider");
|
|
1662
|
+
}
|
|
1663
|
+
buildFtsRelevanceOrderBy(_query, _fieldRefs, _search, _config, _sort) {
|
|
1664
|
+
throw createNotSupportedError("\"_ftsRelevance\" ordering is not supported by the \"mysql\" provider");
|
|
1665
|
+
}
|
|
1548
1666
|
};
|
|
1549
1667
|
//#endregion
|
|
1550
1668
|
//#region src/client/crud/dialects/postgresql.ts
|
|
1669
|
+
/**
|
|
1670
|
+
* Formats a JS `Date` as a Postgres TIME / TIMETZ literal (`HH:MM:SS.fff`,
|
|
1671
|
+
* optionally with `+ZZ:ZZ` for TIMETZ). Reads UTC components so the value
|
|
1672
|
+
* round-trips with ISO-input parsing — callers anchor time-only inputs to
|
|
1673
|
+
* the Unix epoch.
|
|
1674
|
+
*/
|
|
1675
|
+
function formatTimeOfDay(date, withTimezone) {
|
|
1676
|
+
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
1677
|
+
const time = `${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}.${pad(date.getUTCMilliseconds(), 3)}`;
|
|
1678
|
+
return withTimezone ? `${time}+00:00` : time;
|
|
1679
|
+
}
|
|
1551
1680
|
var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBase {
|
|
1552
1681
|
static typeParserOverrideApplied = false;
|
|
1553
1682
|
zmodelToSqlTypeMap = {
|
|
@@ -1641,7 +1770,7 @@ var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBa
|
|
|
1641
1770
|
get insertIgnoreMethod() {
|
|
1642
1771
|
return "onConflict";
|
|
1643
1772
|
}
|
|
1644
|
-
transformInput(value, type, forArrayField) {
|
|
1773
|
+
transformInput(value, type, forArrayField, fieldDef) {
|
|
1645
1774
|
if (value === void 0) return value;
|
|
1646
1775
|
if (value instanceof require_common_types.JsonNullClass) return "null";
|
|
1647
1776
|
else if (value instanceof require_common_types.DbNullClass) return null;
|
|
@@ -1649,9 +1778,15 @@ var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBa
|
|
|
1649
1778
|
if (isTypeDef(this.schema, type)) if (typeof value !== "string") return JSON.stringify(value);
|
|
1650
1779
|
else return value;
|
|
1651
1780
|
else if (Array.isArray(value)) if (type === "Json" && !forArrayField) return JSON.stringify(value);
|
|
1652
|
-
else return value.map((v) => this.transformInput(v, type, false));
|
|
1781
|
+
else return value.map((v) => this.transformInput(v, type, false, fieldDef));
|
|
1653
1782
|
else switch (type) {
|
|
1654
|
-
case "DateTime":
|
|
1783
|
+
case "DateTime": {
|
|
1784
|
+
const date = value instanceof Date ? value : typeof value === "string" ? new Date(value) : null;
|
|
1785
|
+
if (date === null || isNaN(date.getTime())) return value;
|
|
1786
|
+
const dbAttrName = fieldDef?.attributes?.find((a) => a.name.startsWith("@db."))?.name;
|
|
1787
|
+
if (dbAttrName === "@db.Time" || dbAttrName === "@db.Timetz") return formatTimeOfDay(date, dbAttrName === "@db.Timetz");
|
|
1788
|
+
return date.toISOString();
|
|
1789
|
+
}
|
|
1655
1790
|
case "Decimal": return value !== null ? value.toString() : value;
|
|
1656
1791
|
case "Json": if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return JSON.stringify(value);
|
|
1657
1792
|
else return value;
|
|
@@ -1823,6 +1958,72 @@ var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBa
|
|
|
1823
1958
|
return ob;
|
|
1824
1959
|
});
|
|
1825
1960
|
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Wraps an expression with `unaccent(lower(...))` or just `lower(...)` depending on
|
|
1963
|
+
* whether the user opted into accent-insensitive matching. The lowering is always
|
|
1964
|
+
* applied so trigram comparisons are case-insensitive on both sides.
|
|
1965
|
+
*/
|
|
1966
|
+
normalizeForTrigram(expr, applyUnaccent) {
|
|
1967
|
+
return applyUnaccent ? kysely.sql`unaccent(lower(${expr}))` : kysely.sql`lower(${expr})`;
|
|
1968
|
+
}
|
|
1969
|
+
buildFuzzyFilter(fieldRef, options) {
|
|
1970
|
+
const fieldExpr = this.normalizeForTrigram(fieldRef, options.unaccent);
|
|
1971
|
+
const valueExpr = this.normalizeForTrigram(kysely.sql.val(options.search), options.unaccent);
|
|
1972
|
+
if (options.threshold === void 0) switch (options.mode) {
|
|
1973
|
+
case "simple": return kysely.sql`${fieldExpr} % ${valueExpr}`;
|
|
1974
|
+
case "word": return kysely.sql`${valueExpr} <% ${fieldExpr}`;
|
|
1975
|
+
case "strictWord": return kysely.sql`${valueExpr} <<% ${fieldExpr}`;
|
|
1976
|
+
}
|
|
1977
|
+
const threshold = kysely.sql.val(options.threshold);
|
|
1978
|
+
switch (options.mode) {
|
|
1979
|
+
case "simple": return kysely.sql`similarity(${fieldExpr}, ${valueExpr}) > ${threshold}`;
|
|
1980
|
+
case "word": return kysely.sql`word_similarity(${valueExpr}, ${fieldExpr}) > ${threshold}`;
|
|
1981
|
+
case "strictWord": return kysely.sql`strict_word_similarity(${valueExpr}, ${fieldExpr}) > ${threshold}`;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
buildFuzzyRelevanceOrderBy(query, fieldRefs, search, sort, mode, unaccent) {
|
|
1985
|
+
const valueExpr = this.normalizeForTrigram(kysely.sql.val(search), unaccent);
|
|
1986
|
+
const buildSimilarity = (fieldRef) => {
|
|
1987
|
+
const fieldExpr = this.normalizeForTrigram(fieldRef, unaccent);
|
|
1988
|
+
switch (mode) {
|
|
1989
|
+
case "simple": return kysely.sql`similarity(${fieldExpr}, ${valueExpr})`;
|
|
1990
|
+
case "word": return kysely.sql`word_similarity(${valueExpr}, ${fieldExpr})`;
|
|
1991
|
+
case "strictWord": return kysely.sql`strict_word_similarity(${valueExpr}, ${fieldExpr})`;
|
|
1992
|
+
}
|
|
1993
|
+
};
|
|
1994
|
+
if (fieldRefs.length === 1) return query.orderBy(buildSimilarity(fieldRefs[0]), sort);
|
|
1995
|
+
const similarities = fieldRefs.map((ref) => buildSimilarity(ref));
|
|
1996
|
+
return query.orderBy(kysely.sql`GREATEST(${kysely.sql.join(similarities)})`, sort);
|
|
1997
|
+
}
|
|
1998
|
+
buildFullTextFilter(fieldRef, payload) {
|
|
1999
|
+
const options = this.normalizeFullTextOptions(payload);
|
|
2000
|
+
const query = kysely.sql.val(options.search);
|
|
2001
|
+
if (options.config === void 0) return kysely.sql`to_tsvector(${fieldRef}) @@ to_tsquery(${query})`;
|
|
2002
|
+
const cfg = kysely.sql.val(options.config);
|
|
2003
|
+
return kysely.sql`to_tsvector(${cfg}::regconfig, ${fieldRef}) @@ to_tsquery(${cfg}::regconfig, ${query})`;
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* Validate the user-provided `fts` filter payload. When `config` is omitted
|
|
2007
|
+
* it stays `undefined` so {@link buildFullTextFilter} can emit the no-regconfig
|
|
2008
|
+
* SQL form and let Postgres fall back to `default_text_search_config`.
|
|
2009
|
+
*/
|
|
2010
|
+
normalizeFullTextOptions(value) {
|
|
2011
|
+
(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");
|
|
2012
|
+
const raw = value;
|
|
2013
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof raw["search"] === "string" && raw["search"].length > 0, "fts.search must be a non-empty string");
|
|
2014
|
+
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");
|
|
2015
|
+
return {
|
|
2016
|
+
search: raw["search"],
|
|
2017
|
+
config: raw["config"]
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
buildFtsRelevanceOrderBy(query, fieldRefs, search, config, sort) {
|
|
2021
|
+
const q = kysely.sql.val(search);
|
|
2022
|
+
const document = fieldRefs.length === 1 ? kysely.sql`coalesce(${fieldRefs[0]}, '')` : kysely.sql`concat_ws(' ', ${kysely.sql.join(fieldRefs)})`;
|
|
2023
|
+
if (config === void 0) return query.orderBy(kysely.sql`ts_rank(to_tsvector(${document}), to_tsquery(${q}))`, sort);
|
|
2024
|
+
const cfg = kysely.sql.val(config);
|
|
2025
|
+
return query.orderBy(kysely.sql`ts_rank(to_tsvector(${cfg}::regconfig, ${document}), to_tsquery(${cfg}::regconfig, ${q}))`, sort);
|
|
2026
|
+
}
|
|
1826
2027
|
};
|
|
1827
2028
|
//#endregion
|
|
1828
2029
|
//#region src/client/crud/dialects/sqlite.ts
|
|
@@ -2040,6 +2241,18 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
2040
2241
|
return ob;
|
|
2041
2242
|
});
|
|
2042
2243
|
}
|
|
2244
|
+
buildFuzzyFilter(_fieldRef, _options) {
|
|
2245
|
+
throw createNotSupportedError("\"fuzzy\" filter is not supported by the \"sqlite\" provider");
|
|
2246
|
+
}
|
|
2247
|
+
buildFuzzyRelevanceOrderBy(_query, _fieldRefs, _search, _sort, _mode, _unaccent) {
|
|
2248
|
+
throw createNotSupportedError("\"_fuzzyRelevance\" ordering is not supported by the \"sqlite\" provider");
|
|
2249
|
+
}
|
|
2250
|
+
buildFullTextFilter(_fieldRef, _payload) {
|
|
2251
|
+
throw createNotSupportedError("\"fts\" filter is not supported by the \"sqlite\" provider");
|
|
2252
|
+
}
|
|
2253
|
+
buildFtsRelevanceOrderBy(_query, _fieldRefs, _search, _config, _sort) {
|
|
2254
|
+
throw createNotSupportedError("\"_ftsRelevance\" ordering is not supported by the \"sqlite\" provider");
|
|
2255
|
+
}
|
|
2043
2256
|
};
|
|
2044
2257
|
//#endregion
|
|
2045
2258
|
//#region src/client/crud/dialects/index.ts
|
|
@@ -2095,6 +2308,33 @@ function createQuerySchemaFactory(clientOrSchema, options) {
|
|
|
2095
2308
|
return new ZodSchemaFactory(clientOrSchema, options);
|
|
2096
2309
|
}
|
|
2097
2310
|
/**
|
|
2311
|
+
* Builds a `DateTime` value schema that accepts a `Date` object or any string
|
|
2312
|
+
* the JS `Date` constructor parses, and coerces it to a `Date`. ISO datetime,
|
|
2313
|
+
* ISO date, and time-only strings (e.g. `"09:00:00"` for `@db.Time` fields,
|
|
2314
|
+
* anchored to the Unix epoch) are the documented happy paths; other formats
|
|
2315
|
+
* accepted by `new Date(...)` also pass through, mirroring Prisma's pre-3.5
|
|
2316
|
+
* behaviour. Strings the engine can't parse fall through and are rejected by
|
|
2317
|
+
* `z.date()` with the standard error.
|
|
2318
|
+
*
|
|
2319
|
+
* @see https://github.com/zenstackhq/zenstack/issues/2631
|
|
2320
|
+
*/
|
|
2321
|
+
function coercedDateTimeSchema() {
|
|
2322
|
+
return zod.z.preprocess((val) => {
|
|
2323
|
+
if (typeof val !== "string") return val;
|
|
2324
|
+
if (/^\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d\d(?::\d\d)?)?$/.test(val)) {
|
|
2325
|
+
const hasTz = val.endsWith("Z") || /[+-]\d\d(?::\d\d)?$/.test(val);
|
|
2326
|
+
const d = /* @__PURE__ */ new Date(`1970-01-01T${val}${hasTz ? "" : "Z"}`);
|
|
2327
|
+
return isNaN(d.getTime()) ? val : d;
|
|
2328
|
+
}
|
|
2329
|
+
const d = new Date(val);
|
|
2330
|
+
return isNaN(d.getTime()) ? val : d;
|
|
2331
|
+
}, zod.z.union([
|
|
2332
|
+
zod.z.iso.datetime(),
|
|
2333
|
+
zod.z.iso.date(),
|
|
2334
|
+
zod.z.date()
|
|
2335
|
+
]));
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2098
2338
|
* Factory class responsible for creating and caching Zod schemas for ORM input validation.
|
|
2099
2339
|
*/
|
|
2100
2340
|
var ZodSchemaFactory = class {
|
|
@@ -2263,7 +2503,7 @@ var ZodSchemaFactory = class {
|
|
|
2263
2503
|
const typeDef = getTypeDef(this.schema, type);
|
|
2264
2504
|
(0, _zenstackhq_common_helpers.invariant)(typeDef, `Type definition "${type}" not found in schema`);
|
|
2265
2505
|
const schema = zod.z.looseObject(Object.fromEntries(Object.entries(typeDef.fields).map(([field, def]) => {
|
|
2266
|
-
let fieldSchema = this.makeScalarSchema(def.type);
|
|
2506
|
+
let fieldSchema = isTypeDef(this.schema, def.type) ? zod.z.lazy(() => this.makeTypeDefSchema(def.type)) : this.makeScalarSchema(def.type);
|
|
2267
2507
|
if (def.array) fieldSchema = fieldSchema.array();
|
|
2268
2508
|
if (def.optional) fieldSchema = fieldSchema.nullish();
|
|
2269
2509
|
return [field, fieldSchema];
|
|
@@ -2307,7 +2547,7 @@ var ZodSchemaFactory = class {
|
|
|
2307
2547
|
if (Object.keys(enumDef.values).length > 0) fieldSchema = this.makeEnumFilterSchema(fieldDef.type, !!fieldDef.optional, !!fieldDef.array, withAggregations, allowedFilterKinds);
|
|
2308
2548
|
} else if (fieldDef.array) fieldSchema = this.makeArrayFilterSchema(fieldDef.type, allowedFilterKinds);
|
|
2309
2549
|
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);
|
|
2550
|
+
else fieldSchema = this.makePrimitiveFilterSchema(fieldDef.type, !!fieldDef.optional, withAggregations, allowedFilterKinds, !!fieldDef.fuzzy, !!fieldDef.fullText);
|
|
2311
2551
|
}
|
|
2312
2552
|
if (fieldSchema) fields[field] = fieldSchema.optional();
|
|
2313
2553
|
}
|
|
@@ -2436,8 +2676,8 @@ var ZodSchemaFactory = class {
|
|
|
2436
2676
|
const filteredOperators = this.trimFilterOperators(operators, allowedFilterKinds);
|
|
2437
2677
|
return zod.z.strictObject(filteredOperators);
|
|
2438
2678
|
}
|
|
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();
|
|
2679
|
+
makePrimitiveFilterSchema(type, optional, withAggregations, allowedFilterKinds, withFuzzy = false, withFullText = false) {
|
|
2680
|
+
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
2681
|
}
|
|
2442
2682
|
makeJsonValueSchema() {
|
|
2443
2683
|
const schema = zod.z.union([
|
|
@@ -2479,11 +2719,7 @@ var ZodSchemaFactory = class {
|
|
|
2479
2719
|
return schema;
|
|
2480
2720
|
}
|
|
2481
2721
|
makeDateTimeValueSchema() {
|
|
2482
|
-
const schema =
|
|
2483
|
-
zod.z.iso.datetime(),
|
|
2484
|
-
zod.z.iso.date(),
|
|
2485
|
-
zod.z.date()
|
|
2486
|
-
]);
|
|
2722
|
+
const schema = coercedDateTimeSchema();
|
|
2487
2723
|
this.registerSchema("DateTime", schema);
|
|
2488
2724
|
return schema;
|
|
2489
2725
|
}
|
|
@@ -2579,8 +2815,8 @@ var ZodSchemaFactory = class {
|
|
|
2579
2815
|
})}`, schema);
|
|
2580
2816
|
return schema;
|
|
2581
2817
|
}
|
|
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 ? [
|
|
2818
|
+
makeStringFilterSchema(optional, withAggregations, allowedFilterKinds, withFuzzy = false, withFullText = false) {
|
|
2819
|
+
const baseComponents = this.makeCommonPrimitiveFilterComponents(zod.z.string(), optional, () => zod.z.lazy(() => this.makeStringFilterSchema(optional, withAggregations, allowedFilterKinds, withFuzzy, withFullText)), void 0, withAggregations ? [
|
|
2584
2820
|
"_count",
|
|
2585
2821
|
"_min",
|
|
2586
2822
|
"_max"
|
|
@@ -2589,6 +2825,8 @@ var ZodSchemaFactory = class {
|
|
|
2589
2825
|
startsWith: zod.z.string().optional(),
|
|
2590
2826
|
endsWith: zod.z.string().optional(),
|
|
2591
2827
|
contains: zod.z.string().optional(),
|
|
2828
|
+
...withFuzzy && this.providerSupportsFuzzySearch ? { fuzzy: this.makeFuzzyFilterSchema().optional() } : {},
|
|
2829
|
+
...withFullText && this.providerSupportsFullTextSearch ? { fts: this.makeFullTextFilterSchema().optional() } : {},
|
|
2592
2830
|
...this.providerSupportsCaseSensitivity ? { mode: this.makeStringModeSchema().optional() } : {}
|
|
2593
2831
|
};
|
|
2594
2832
|
const filteredStringOperators = this.trimFilterOperators(stringSpecificOperators, allowedFilterKinds);
|
|
@@ -2597,16 +2835,35 @@ var ZodSchemaFactory = class {
|
|
|
2597
2835
|
...filteredStringOperators
|
|
2598
2836
|
};
|
|
2599
2837
|
const schema = this.createUnionFilterSchema(zod.z.string(), optional, allComponents, allowedFilterKinds);
|
|
2838
|
+
const featureSuffix = `${withFuzzy ? "Fuzzy" : ""}${withFullText ? "FullText" : ""}`;
|
|
2600
2839
|
this.registerSchema(`StringFilter${this.filterSchemaSuffix({
|
|
2601
2840
|
optional,
|
|
2602
2841
|
allowedFilterKinds,
|
|
2603
2842
|
withAggregations
|
|
2604
|
-
})}`, schema);
|
|
2843
|
+
})}${featureSuffix}`, schema);
|
|
2605
2844
|
return schema;
|
|
2606
2845
|
}
|
|
2607
2846
|
makeStringModeSchema() {
|
|
2608
2847
|
return zod.z.union([zod.z.literal("default"), zod.z.literal("insensitive")]);
|
|
2609
2848
|
}
|
|
2849
|
+
makeFuzzyFilterSchema() {
|
|
2850
|
+
return zod.z.strictObject({
|
|
2851
|
+
search: zod.z.string().min(1),
|
|
2852
|
+
mode: zod.z.union([
|
|
2853
|
+
zod.z.literal("simple"),
|
|
2854
|
+
zod.z.literal("word"),
|
|
2855
|
+
zod.z.literal("strictWord")
|
|
2856
|
+
]).default("simple"),
|
|
2857
|
+
threshold: zod.z.number().min(0).max(1).optional(),
|
|
2858
|
+
unaccent: zod.z.boolean().default(false)
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2861
|
+
makeFullTextFilterSchema() {
|
|
2862
|
+
return zod.z.strictObject({
|
|
2863
|
+
search: zod.z.string().min(1),
|
|
2864
|
+
config: zod.z.string().min(1).optional()
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2610
2867
|
makeSelectSchema(model, options) {
|
|
2611
2868
|
const fields = {};
|
|
2612
2869
|
for (const [field, fieldDef] of this.getModelFields(model)) if (fieldDef.relation) {
|
|
@@ -2707,7 +2964,7 @@ var ZodSchemaFactory = class {
|
|
|
2707
2964
|
}).optional();
|
|
2708
2965
|
} else if (fieldDef.optional) fields[field] = zod.z.union([sort, zod.z.strictObject({
|
|
2709
2966
|
sort,
|
|
2710
|
-
nulls: zod.z.union([zod.z.literal("first"), zod.z.literal("last")])
|
|
2967
|
+
nulls: zod.z.union([zod.z.literal("first"), zod.z.literal("last")]).optional()
|
|
2711
2968
|
})]).optional();
|
|
2712
2969
|
else fields[field] = sort.optional();
|
|
2713
2970
|
if (WithAggregation) for (const agg of [
|
|
@@ -2717,6 +2974,29 @@ var ZodSchemaFactory = class {
|
|
|
2717
2974
|
"_min",
|
|
2718
2975
|
"_max"
|
|
2719
2976
|
]) fields[agg] = zod.z.lazy(() => this.makeOrderBySchema(model, true, false, options).optional());
|
|
2977
|
+
if (this.providerSupportsFuzzySearch) {
|
|
2978
|
+
const fuzzyFieldNames = this.getModelFields(model).filter(([, def]) => !def.relation && def.type === "String" && def.fuzzy === true).map(([name]) => name);
|
|
2979
|
+
if (fuzzyFieldNames.length > 0) fields["_fuzzyRelevance"] = zod.z.strictObject({
|
|
2980
|
+
fields: zod.z.array(zod.z.enum(fuzzyFieldNames)).min(1),
|
|
2981
|
+
search: zod.z.string(),
|
|
2982
|
+
mode: zod.z.union([
|
|
2983
|
+
zod.z.literal("simple"),
|
|
2984
|
+
zod.z.literal("word"),
|
|
2985
|
+
zod.z.literal("strictWord")
|
|
2986
|
+
]).default("simple"),
|
|
2987
|
+
unaccent: zod.z.boolean().default(false),
|
|
2988
|
+
sort
|
|
2989
|
+
}).optional();
|
|
2990
|
+
}
|
|
2991
|
+
if (this.providerSupportsFullTextSearch) {
|
|
2992
|
+
const fullTextFieldNames = this.getModelFields(model).filter(([, def]) => !def.relation && def.type === "String" && def.fullText === true).map(([name]) => name);
|
|
2993
|
+
if (fullTextFieldNames.length > 0) fields["_ftsRelevance"] = zod.z.strictObject({
|
|
2994
|
+
fields: zod.z.array(zod.z.enum(fullTextFieldNames)).min(1),
|
|
2995
|
+
search: zod.z.string().min(1),
|
|
2996
|
+
config: zod.z.string().min(1).optional(),
|
|
2997
|
+
sort
|
|
2998
|
+
}).optional();
|
|
2999
|
+
}
|
|
2720
3000
|
const schema = refineAtMostOneKey(zod.z.strictObject(fields));
|
|
2721
3001
|
let schemaId = `${model}OrderBy`;
|
|
2722
3002
|
if (withRelation) schemaId += "WithRelation";
|
|
@@ -3218,6 +3498,12 @@ var ZodSchemaFactory = class {
|
|
|
3218
3498
|
get providerSupportsCaseSensitivity() {
|
|
3219
3499
|
return this.schema.provider.type === "postgresql";
|
|
3220
3500
|
}
|
|
3501
|
+
get providerSupportsFullTextSearch() {
|
|
3502
|
+
return this.schema.provider.type === "postgresql";
|
|
3503
|
+
}
|
|
3504
|
+
get providerSupportsFuzzySearch() {
|
|
3505
|
+
return this.schema.provider.type === "postgresql";
|
|
3506
|
+
}
|
|
3221
3507
|
/**
|
|
3222
3508
|
* Gets the effective set of allowed FilterKind values for a specific model and field.
|
|
3223
3509
|
* Respects the precedence: model[field] > model.$all > $all[field] > $all.$all.
|
|
@@ -3379,6 +3665,8 @@ __decorate([
|
|
|
3379
3665
|
Object,
|
|
3380
3666
|
Boolean,
|
|
3381
3667
|
Boolean,
|
|
3668
|
+
Object,
|
|
3669
|
+
Object,
|
|
3382
3670
|
Object
|
|
3383
3671
|
]),
|
|
3384
3672
|
__decorateMetadata("design:returntype", void 0)
|
|
@@ -3448,6 +3736,8 @@ __decorate([
|
|
|
3448
3736
|
__decorateMetadata("design:paramtypes", [
|
|
3449
3737
|
Boolean,
|
|
3450
3738
|
Boolean,
|
|
3739
|
+
Object,
|
|
3740
|
+
Object,
|
|
3451
3741
|
Object
|
|
3452
3742
|
]),
|
|
3453
3743
|
__decorateMetadata("design:returntype", typeof (_ref10 = typeof zod.ZodType !== "undefined" && zod.ZodType) === "function" ? _ref10 : Object)
|
|
@@ -3937,7 +4227,7 @@ var BaseOperationHandler = class {
|
|
|
3937
4227
|
return new this.constructor(client, this.model, this.inputValidator);
|
|
3938
4228
|
}
|
|
3939
4229
|
get hasPolicyEnabled() {
|
|
3940
|
-
return this.options.plugins?.some((plugin) => plugin.
|
|
4230
|
+
return this.options.plugins?.some((plugin) => plugin.id === "policy") ?? false;
|
|
3941
4231
|
}
|
|
3942
4232
|
requireModel(model) {
|
|
3943
4233
|
return requireModel(this.schema, model);
|
|
@@ -4038,8 +4328,8 @@ var BaseOperationHandler = class {
|
|
|
4038
4328
|
const postCreateRelations = {};
|
|
4039
4329
|
for (const [field, value] of Object.entries(data)) {
|
|
4040
4330
|
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);
|
|
4331
|
+
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);
|
|
4332
|
+
else createFields[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4043
4333
|
else if (!getManyToManyRelation(this.schema, model, field) && fieldDef.relation?.fields && fieldDef.relation?.references) {
|
|
4044
4334
|
const fkValues = await this.processOwnedRelationForCreate(kysely$7, fieldDef, value);
|
|
4045
4335
|
for (let i = 0; i < fieldDef.relation.fields.length; i++) createFields[fieldDef.relation.fields[i]] = fkValues[fieldDef.relation.references[i]];
|
|
@@ -4239,7 +4529,7 @@ var BaseOperationHandler = class {
|
|
|
4239
4529
|
for (const [name, value] of Object.entries(item)) {
|
|
4240
4530
|
const fieldDef = this.requireField(model, name);
|
|
4241
4531
|
(0, _zenstackhq_common_helpers.invariant)(!fieldDef.relation, "createMany does not support relations");
|
|
4242
|
-
newItem[name] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array);
|
|
4532
|
+
newItem[name] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4243
4533
|
}
|
|
4244
4534
|
if (fromRelation) for (const { fk, pk } of relationKeyPairs) newItem[fk] = fromRelation.ids[pk];
|
|
4245
4535
|
return this.fillGeneratedAndDefaultValues(modelDef, newItem);
|
|
@@ -4255,7 +4545,7 @@ var BaseOperationHandler = class {
|
|
|
4255
4545
|
if (Object.keys(item).length === allPassedFields.length) continue;
|
|
4256
4546
|
for (const field of allPassedFields) if (!(field in item)) {
|
|
4257
4547
|
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);
|
|
4548
|
+
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
4549
|
}
|
|
4260
4550
|
}
|
|
4261
4551
|
}
|
|
@@ -4318,15 +4608,15 @@ var BaseOperationHandler = class {
|
|
|
4318
4608
|
if (!(field in data)) {
|
|
4319
4609
|
if (typeof fieldDef?.default === "object" && "kind" in fieldDef.default) {
|
|
4320
4610
|
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);
|
|
4611
|
+
if (generated !== void 0) values[field] = this.dialect.transformInput(generated, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4612
|
+
} else if (fieldDef?.updatedAt) values[field] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false, fieldDef);
|
|
4323
4613
|
else if (fieldDef?.default !== void 0) {
|
|
4324
4614
|
let value = fieldDef.default;
|
|
4325
4615
|
if (fieldDef.type === "Json") {
|
|
4326
4616
|
if (fieldDef.array && Array.isArray(value)) value = value.map((v) => typeof v === "string" ? JSON.parse(v) : v);
|
|
4327
4617
|
else if (typeof value === "string") value = JSON.parse(value);
|
|
4328
4618
|
}
|
|
4329
|
-
values[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array);
|
|
4619
|
+
values[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4330
4620
|
}
|
|
4331
4621
|
}
|
|
4332
4622
|
}
|
|
@@ -4376,7 +4666,7 @@ var BaseOperationHandler = class {
|
|
|
4376
4666
|
const ignoredFields = new Set(typeof fieldDef.updatedAt === "boolean" ? [] : fieldDef.updatedAt.ignore);
|
|
4377
4667
|
if (Object.keys(data).some((field) => (isScalarField(this.schema, modelDef.name, field) || isForeignKeyField(this.schema, modelDef.name, field)) && !ignoredFields.has(field))) {
|
|
4378
4668
|
if (finalData === data) finalData = (0, _zenstackhq_common_helpers.clone)(data);
|
|
4379
|
-
finalData[fieldName] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false);
|
|
4669
|
+
finalData[fieldName] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false, fieldDef);
|
|
4380
4670
|
autoUpdatedFields.push(fieldName);
|
|
4381
4671
|
}
|
|
4382
4672
|
}
|
|
@@ -4480,7 +4770,7 @@ var BaseOperationHandler = class {
|
|
|
4480
4770
|
const fieldDef = this.requireField(model, field);
|
|
4481
4771
|
if (this.isNumericIncrementalUpdate(fieldDef, data[field])) return this.transformIncrementalUpdate(model, field, fieldDef, data[field]);
|
|
4482
4772
|
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);
|
|
4773
|
+
return this.dialect.transformInput(data[field], fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4484
4774
|
}
|
|
4485
4775
|
isNumericIncrementalUpdate(fieldDef, value) {
|
|
4486
4776
|
if (!this.isNumericField(fieldDef)) return false;
|
|
@@ -4508,7 +4798,7 @@ var BaseOperationHandler = class {
|
|
|
4508
4798
|
transformIncrementalUpdate(model, field, fieldDef, payload) {
|
|
4509
4799
|
(0, _zenstackhq_common_helpers.invariant)(Object.keys(payload).length === 1, "Only one of \"set\", \"increment\", \"decrement\", \"multiply\", or \"divide\" can be provided");
|
|
4510
4800
|
const key = Object.keys(payload)[0];
|
|
4511
|
-
const value = this.dialect.transformInput(payload[key], fieldDef.type, false);
|
|
4801
|
+
const value = this.dialect.transformInput(payload[key], fieldDef.type, false, fieldDef);
|
|
4512
4802
|
const eb = (0, kysely.expressionBuilder)();
|
|
4513
4803
|
const fieldRef = this.dialect.fieldRef(model, field);
|
|
4514
4804
|
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 +4808,7 @@ var BaseOperationHandler = class {
|
|
|
4518
4808
|
transformScalarListUpdate(model, field, fieldDef, payload) {
|
|
4519
4809
|
(0, _zenstackhq_common_helpers.invariant)(Object.keys(payload).length === 1, "Only one of \"set\", \"push\" can be provided");
|
|
4520
4810
|
const key = Object.keys(payload)[0];
|
|
4521
|
-
const value = this.dialect.transformInput(payload[key], fieldDef.type, true);
|
|
4811
|
+
const value = this.dialect.transformInput(payload[key], fieldDef.type, true, fieldDef);
|
|
4522
4812
|
const eb = (0, kysely.expressionBuilder)();
|
|
4523
4813
|
const fieldRef = this.dialect.fieldRef(model, field);
|
|
4524
4814
|
return (0, ts_pattern.match)(key).with("set", () => value).with("push", () => {
|
|
@@ -6929,12 +7219,21 @@ var ClientImpl = class ClientImpl {
|
|
|
6929
7219
|
});
|
|
6930
7220
|
}
|
|
6931
7221
|
}
|
|
7222
|
+
getPromiseCallback(promise) {
|
|
7223
|
+
(0, _zenstackhq_common_helpers.invariant)(promise.cb, "Invalid ZenStackPromise, missing cb property");
|
|
7224
|
+
const cb = promise.cb;
|
|
7225
|
+
(0, _zenstackhq_common_helpers.invariant)(typeof cb === "function", "Invalid ZenStackPromise, cb property is not a function");
|
|
7226
|
+
return promise.cb;
|
|
7227
|
+
}
|
|
6932
7228
|
async sequentialTransaction(arg, options) {
|
|
6933
7229
|
const execute = async (tx) => {
|
|
6934
7230
|
const txClient = new ClientImpl(this.schema, this.$options, this);
|
|
6935
7231
|
txClient.kysely = tx;
|
|
6936
7232
|
const result = [];
|
|
6937
|
-
for (const promise of arg)
|
|
7233
|
+
for (const promise of arg) {
|
|
7234
|
+
const cb = this.getPromiseCallback(promise);
|
|
7235
|
+
result.push(await cb(txClient));
|
|
7236
|
+
}
|
|
6938
7237
|
return result;
|
|
6939
7238
|
};
|
|
6940
7239
|
if (this.kysely.isTransaction) return execute(this.kysely);
|
|
@@ -7721,6 +8020,15 @@ var DefaultOperationNodeVisitor = class extends kysely.OperationNodeVisitor {
|
|
|
7721
8020
|
visitCollate(node) {
|
|
7722
8021
|
this.defaultVisit(node);
|
|
7723
8022
|
}
|
|
8023
|
+
visitAlterType(node) {
|
|
8024
|
+
this.defaultVisit(node);
|
|
8025
|
+
}
|
|
8026
|
+
visitAddValue(node) {
|
|
8027
|
+
this.defaultVisit(node);
|
|
8028
|
+
}
|
|
8029
|
+
visitRenameValue(node) {
|
|
8030
|
+
this.defaultVisit(node);
|
|
8031
|
+
}
|
|
7724
8032
|
};
|
|
7725
8033
|
//#endregion
|
|
7726
8034
|
//#region src/utils/schema-utils.ts
|
|
@@ -7796,6 +8104,8 @@ exports.CoreUpdateOperations = CoreUpdateOperations;
|
|
|
7796
8104
|
exports.CoreWriteOperations = CoreWriteOperations;
|
|
7797
8105
|
exports.DbNull = require_common_types.DbNull;
|
|
7798
8106
|
exports.DbNullClass = require_common_types.DbNullClass;
|
|
8107
|
+
exports.ExtQueryArgsMarker = ExtQueryArgsMarker;
|
|
8108
|
+
exports.ExtResultMarker = ExtResultMarker;
|
|
7799
8109
|
exports.InputValidator = InputValidator;
|
|
7800
8110
|
exports.JsonNull = require_common_types.JsonNull;
|
|
7801
8111
|
exports.JsonNullClass = require_common_types.JsonNullClass;
|