@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.mjs
CHANGED
|
@@ -189,6 +189,7 @@ var query_utils_exports = /* @__PURE__ */ __exportAll({
|
|
|
189
189
|
fieldHasDefaultValue: () => fieldHasDefaultValue,
|
|
190
190
|
flattenCompoundUniqueFilters: () => flattenCompoundUniqueFilters,
|
|
191
191
|
getDelegateDescendantModels: () => getDelegateDescendantModels,
|
|
192
|
+
getDelegateDiscriminatorValue: () => getDelegateDiscriminatorValue,
|
|
192
193
|
getDiscriminatorField: () => getDiscriminatorField,
|
|
193
194
|
getEnum: () => getEnum,
|
|
194
195
|
getField: () => getField,
|
|
@@ -422,6 +423,10 @@ function getDiscriminatorField(schema, model) {
|
|
|
422
423
|
if (!discriminator || !ExpressionUtils.isField(discriminator.value)) throw createInternalError(`Discriminator field not defined for model "${model}"`, model);
|
|
423
424
|
return discriminator.value.field;
|
|
424
425
|
}
|
|
426
|
+
function getDelegateDiscriminatorValue(schema, model) {
|
|
427
|
+
const modelDef = requireModel(schema, model);
|
|
428
|
+
return modelDef.delegateMap ?? modelDef.name;
|
|
429
|
+
}
|
|
425
430
|
function getDelegateDescendantModels(schema, model, collected = /* @__PURE__ */ new Set()) {
|
|
426
431
|
Object.values(schema.models).filter((m) => m.baseModel === model).forEach((def) => {
|
|
427
432
|
if (!collected.has(def)) {
|
|
@@ -531,6 +536,8 @@ const FILTER_PROPERTY_TO_KIND = {
|
|
|
531
536
|
array_contains: "Json",
|
|
532
537
|
array_starts_with: "Json",
|
|
533
538
|
array_ends_with: "Json",
|
|
539
|
+
fuzzy: "Fuzzy",
|
|
540
|
+
fts: "FullText",
|
|
534
541
|
has: "List",
|
|
535
542
|
hasEvery: "List",
|
|
536
543
|
hasSome: "List",
|
|
@@ -550,6 +557,19 @@ let TransactionIsolationLevel = /* @__PURE__ */ function(TransactionIsolationLev
|
|
|
550
557
|
return TransactionIsolationLevel;
|
|
551
558
|
}({});
|
|
552
559
|
/**
|
|
560
|
+
* Symbol used as a type-only key on `ClientContract` to brand the `ExtQueryArgs`
|
|
561
|
+
* generic slot. Hidden from member-access autocomplete since symbol keys are
|
|
562
|
+
* not surfaced. Consumed by `InferExtQueryArgs` to recover the slot.
|
|
563
|
+
* @internal
|
|
564
|
+
*/
|
|
565
|
+
const ExtQueryArgsMarker = Symbol("zenstack.client.extQueryArgs");
|
|
566
|
+
/**
|
|
567
|
+
* Symbol used as a type-only key on `ClientContract` to brand the `ExtResult`
|
|
568
|
+
* generic slot. Consumed by `InferExtResult` to recover the slot.
|
|
569
|
+
* @internal
|
|
570
|
+
*/
|
|
571
|
+
const ExtResultMarker = Symbol("zenstack.client.extResult");
|
|
572
|
+
/**
|
|
553
573
|
* CRUD operations.
|
|
554
574
|
*/
|
|
555
575
|
const CRUD = [
|
|
@@ -572,8 +592,13 @@ var BaseCrudDialect = class {
|
|
|
572
592
|
}
|
|
573
593
|
/**
|
|
574
594
|
* Transforms input value before sending to database.
|
|
595
|
+
*
|
|
596
|
+
* `fieldDef` is optional so existing callers that don't have it stay
|
|
597
|
+
* source-compatible. Dialects can use it to inspect `@db.*` native-type
|
|
598
|
+
* attributes (e.g. to format `@db.Time` values as `HH:MM:SS` rather than
|
|
599
|
+
* full ISO timestamps).
|
|
575
600
|
*/
|
|
576
|
-
transformInput(value, _type, _forArrayField) {
|
|
601
|
+
transformInput(value, _type, _forArrayField, _fieldDef) {
|
|
577
602
|
return value;
|
|
578
603
|
}
|
|
579
604
|
/**
|
|
@@ -616,7 +641,17 @@ var BaseCrudDialect = class {
|
|
|
616
641
|
if (existingOrderBy.length > 0 && !alreadySatisfied) effectiveOrderBy = [...distinctFields.map((f) => ({ [f]: "asc" })), ...existingOrderBy];
|
|
617
642
|
}
|
|
618
643
|
result = this.buildOrderBy(result, model, modelAlias, effectiveOrderBy, negateOrderBy, take);
|
|
619
|
-
if (args.cursor)
|
|
644
|
+
if (args.cursor) {
|
|
645
|
+
if (effectiveOrderBy) {
|
|
646
|
+
const offendingKey = enumerate(effectiveOrderBy).map((ob) => {
|
|
647
|
+
if (typeof ob !== "object" || ob === null) return void 0;
|
|
648
|
+
if ("_fuzzyRelevance" in ob) return "_fuzzyRelevance";
|
|
649
|
+
if ("_ftsRelevance" in ob) return "_ftsRelevance";
|
|
650
|
+
}).find((k) => k !== void 0);
|
|
651
|
+
if (offendingKey) throw createNotSupportedError(`cursor pagination cannot be combined with "${offendingKey}" ordering`);
|
|
652
|
+
}
|
|
653
|
+
result = this.buildCursorFilter(model, result, args.cursor, effectiveOrderBy, negateOrderBy, modelAlias);
|
|
654
|
+
}
|
|
620
655
|
return result;
|
|
621
656
|
}
|
|
622
657
|
buildFilter(model, modelAlias, where) {
|
|
@@ -756,7 +791,7 @@ var BaseCrudDialect = class {
|
|
|
756
791
|
for (const [key, _value] of Object.entries(payload)) {
|
|
757
792
|
if (_value === void 0) continue;
|
|
758
793
|
invariant(fieldDef.array, "Field must be an array type to build array filter");
|
|
759
|
-
const value = this.transformInput(_value, fieldType, true);
|
|
794
|
+
const value = this.transformInput(_value, fieldType, true, fieldDef);
|
|
760
795
|
let receiver = fieldRef;
|
|
761
796
|
if (isEnum(this.schema, fieldType)) receiver = this.eb.cast(fieldRef, sql.raw("text[]"));
|
|
762
797
|
const buildArray = (value) => {
|
|
@@ -791,7 +826,7 @@ var BaseCrudDialect = class {
|
|
|
791
826
|
if (payload instanceof DbNullClass || payload instanceof JsonNullClass || payload instanceof AnyNullClass) return this.buildJsonValueFilterClause(fieldRef, payload);
|
|
792
827
|
return this.buildJsonFilter(fieldRef, payload, fieldDef);
|
|
793
828
|
}
|
|
794
|
-
return match(fieldDef.type).with("String", () => this.buildStringFilter(fieldRef, payload)).with(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", () => {
|
|
829
|
+
return match(fieldDef.type).with("String", () => this.buildStringFilter(fieldRef, payload, fieldDef)).with(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", () => {
|
|
795
830
|
throw createInvalidInputError(`Unsupported field cannot be used in filters`);
|
|
796
831
|
}).exhaustive();
|
|
797
832
|
}
|
|
@@ -959,13 +994,23 @@ var BaseCrudDialect = class {
|
|
|
959
994
|
consumedKeys
|
|
960
995
|
};
|
|
961
996
|
}
|
|
962
|
-
buildStringFilter(fieldRef, payload) {
|
|
997
|
+
buildStringFilter(fieldRef, payload, fieldDef) {
|
|
963
998
|
let mode;
|
|
964
999
|
if (payload && typeof payload === "object" && "mode" in payload) mode = payload.mode;
|
|
965
|
-
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));
|
|
1000
|
+
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));
|
|
966
1001
|
if (payload && typeof payload === "object") for (const [key, value] of Object.entries(payload)) {
|
|
967
1002
|
if (key === "mode" || consumedKeys.includes(key)) continue;
|
|
968
1003
|
if (value === void 0) continue;
|
|
1004
|
+
if (key === "fuzzy") {
|
|
1005
|
+
invariant(fieldDef?.fuzzy === true, `field "${fieldDef?.name ?? "<unknown>"}" is not fuzzy-searchable; add the \`@fuzzy\` attribute to use the \`fuzzy\` filter`);
|
|
1006
|
+
conditions.push(this.buildFuzzyFilter(fieldRef, this.normalizeFuzzyOptions(value)));
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
if (key === "fts") {
|
|
1010
|
+
invariant(fieldDef?.fullText === true, `field "${fieldDef?.name ?? "<unknown>"}" is not full-text-searchable; add the \`@fullText\` attribute to use the \`fts\` filter`);
|
|
1011
|
+
conditions.push(this.buildFullTextFilter(fieldRef, value));
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
969
1014
|
invariant(typeof value === "string", `${key} value must be a string`);
|
|
970
1015
|
const escapedValue = this.escapeLikePattern(value);
|
|
971
1016
|
const condition = 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(() => {
|
|
@@ -1034,6 +1079,14 @@ var BaseCrudDialect = class {
|
|
|
1034
1079
|
enumerate(orderBy).forEach((orderBy, index) => {
|
|
1035
1080
|
for (const [field, value] of Object.entries(orderBy)) {
|
|
1036
1081
|
if (!value) continue;
|
|
1082
|
+
if (field === "_fuzzyRelevance") {
|
|
1083
|
+
result = this.applyFuzzyRelevanceOrderBy(result, model, modelAlias, value, negated, buildFieldRef);
|
|
1084
|
+
continue;
|
|
1085
|
+
}
|
|
1086
|
+
if (field === "_ftsRelevance") {
|
|
1087
|
+
result = this.applyFtsRelevanceOrderBy(result, model, modelAlias, value, negated, buildFieldRef);
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1037
1090
|
if ([
|
|
1038
1091
|
"_count",
|
|
1039
1092
|
"_avg",
|
|
@@ -1041,47 +1094,84 @@ var BaseCrudDialect = class {
|
|
|
1041
1094
|
"_min",
|
|
1042
1095
|
"_max"
|
|
1043
1096
|
].includes(field)) {
|
|
1044
|
-
|
|
1045
|
-
for (const [k, v] of Object.entries(value)) {
|
|
1046
|
-
invariant(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
1047
|
-
result = result.orderBy((eb) => aggregate(eb, buildFieldRef(model, k, modelAlias), field), this.negateSort(v, negated));
|
|
1048
|
-
}
|
|
1097
|
+
result = this.applyAggregationOrderBy(result, model, modelAlias, field, value, negated, buildFieldRef);
|
|
1049
1098
|
continue;
|
|
1050
1099
|
}
|
|
1051
1100
|
const fieldDef = requireField(this.schema, model, field);
|
|
1052
|
-
if (!fieldDef.relation)
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
const joinAlias = tmpAlias(`${modelAlias}$ob$${index}`);
|
|
1074
|
-
result = result.leftJoin(`${relationModel} as ${joinAlias}`, (join) => {
|
|
1075
|
-
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, joinAlias);
|
|
1076
|
-
return join.on((eb) => this.and(...joinPairs.map(([left, right]) => eb(this.eb.ref(left), "=", this.eb.ref(right)))));
|
|
1077
|
-
});
|
|
1078
|
-
result = this.buildOrderBy(result, relationModel, joinAlias, value, negated, take);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1101
|
+
if (!fieldDef.relation) result = this.applyScalarOrderBy(result, model, modelAlias, field, value, negated, buildFieldRef);
|
|
1102
|
+
else result = this.applyRelationOrderBy(result, model, modelAlias, field, fieldDef, value, negated, take, index);
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
return result;
|
|
1106
|
+
}
|
|
1107
|
+
applyRelationOrderBy(query, model, modelAlias, field, fieldDef, value, negated, take, index) {
|
|
1108
|
+
const relationModel = fieldDef.type;
|
|
1109
|
+
if (fieldDef.array) {
|
|
1110
|
+
if (typeof value !== "object") throw createInvalidInputError(`invalid orderBy value for field "${field}"`);
|
|
1111
|
+
if ("_count" in value) {
|
|
1112
|
+
invariant(value._count === "asc" || value._count === "desc", "invalid orderBy value for field \"_count\"");
|
|
1113
|
+
const sort = this.negateSort(value._count, negated);
|
|
1114
|
+
return query.orderBy((eb) => {
|
|
1115
|
+
const subQueryAlias = tmpAlias(`${modelAlias}$ob$${field}$ct`);
|
|
1116
|
+
let subQuery = this.buildSelectModel(relationModel, subQueryAlias);
|
|
1117
|
+
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
|
|
1118
|
+
subQuery = subQuery.where(() => this.and(...joinPairs.map(([left, right]) => eb(this.eb.ref(left), "=", this.eb.ref(right)))));
|
|
1119
|
+
subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
|
|
1120
|
+
return subQuery;
|
|
1121
|
+
}, sort);
|
|
1081
1122
|
}
|
|
1123
|
+
return query;
|
|
1124
|
+
}
|
|
1125
|
+
const joinAlias = tmpAlias(`${modelAlias}$ob$${index}`);
|
|
1126
|
+
const joined = query.leftJoin(`${relationModel} as ${joinAlias}`, (join) => {
|
|
1127
|
+
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, joinAlias);
|
|
1128
|
+
return join.on((eb) => this.and(...joinPairs.map(([left, right]) => eb(this.eb.ref(left), "=", this.eb.ref(right)))));
|
|
1082
1129
|
});
|
|
1130
|
+
return this.buildOrderBy(joined, relationModel, joinAlias, value, negated, take);
|
|
1131
|
+
}
|
|
1132
|
+
applyScalarOrderBy(query, model, modelAlias, field, value, negated, buildFieldRef) {
|
|
1133
|
+
const fieldRef = buildFieldRef(model, field, modelAlias);
|
|
1134
|
+
if (value === "asc" || value === "desc") return query.orderBy(fieldRef, this.negateSort(value, negated));
|
|
1135
|
+
if (typeof value === "object" && "sort" in value && (value.sort === "asc" || value.sort === "desc")) {
|
|
1136
|
+
const sort = this.negateSort(value.sort, negated);
|
|
1137
|
+
if (value.nulls === "first" || value.nulls === "last") return this.buildOrderByField(query, fieldRef, sort, value.nulls);
|
|
1138
|
+
else return query.orderBy(fieldRef, sort);
|
|
1139
|
+
}
|
|
1140
|
+
return query;
|
|
1141
|
+
}
|
|
1142
|
+
applyAggregationOrderBy(query, model, modelAlias, field, value, negated, buildFieldRef) {
|
|
1143
|
+
invariant(typeof value === "object", `invalid orderBy value for field "${field}"`);
|
|
1144
|
+
let result = query;
|
|
1145
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1146
|
+
invariant(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
1147
|
+
result = result.orderBy((eb) => aggregate(eb, buildFieldRef(model, k, modelAlias), field), this.negateSort(v, negated));
|
|
1148
|
+
}
|
|
1083
1149
|
return result;
|
|
1084
1150
|
}
|
|
1151
|
+
applyFuzzyRelevanceOrderBy(query, model, modelAlias, value, negated, buildFieldRef) {
|
|
1152
|
+
invariant(typeof value === "object" && "fields" in value && "search" in value && "sort" in value, "invalid orderBy value for \"_fuzzyRelevance\"");
|
|
1153
|
+
invariant(Array.isArray(value.fields) && value.fields.length > 0, "_fuzzyRelevance.fields must be a non-empty array");
|
|
1154
|
+
invariant(value.sort === "asc" || value.sort === "desc", "invalid sort value for \"_fuzzyRelevance\"");
|
|
1155
|
+
invariant(typeof value.search === "string" && value.search.length > 0, "_fuzzyRelevance.search must be a non-empty string");
|
|
1156
|
+
const mode = value.mode ?? "simple";
|
|
1157
|
+
invariant(mode === "simple" || mode === "word" || mode === "strictWord", "_fuzzyRelevance.mode must be \"simple\", \"word\" or \"strictWord\"");
|
|
1158
|
+
const unaccent = value.unaccent ?? false;
|
|
1159
|
+
invariant(typeof unaccent === "boolean", "_fuzzyRelevance.unaccent must be a boolean");
|
|
1160
|
+
for (const fieldName of value.fields) invariant(requireField(this.schema, model, fieldName).fuzzy === true, `field "${fieldName}" is not fuzzy-searchable; add the \`@fuzzy\` attribute to use it in \`_fuzzyRelevance\``);
|
|
1161
|
+
const fieldRefs = value.fields.map((f) => buildFieldRef(model, f, modelAlias));
|
|
1162
|
+
return this.buildFuzzyRelevanceOrderBy(query, fieldRefs, value.search, this.negateSort(value.sort, negated), mode, unaccent);
|
|
1163
|
+
}
|
|
1164
|
+
applyFtsRelevanceOrderBy(query, model, modelAlias, value, negated, buildFieldRef) {
|
|
1165
|
+
invariant(typeof value === "object" && "fields" in value && "search" in value && "sort" in value, "invalid orderBy value for \"_ftsRelevance\"");
|
|
1166
|
+
invariant(Array.isArray(value.fields) && value.fields.length > 0, "_ftsRelevance.fields must be a non-empty array");
|
|
1167
|
+
invariant(value.sort === "asc" || value.sort === "desc", "invalid sort value for \"_ftsRelevance\"");
|
|
1168
|
+
invariant(typeof value.search === "string" && value.search.length > 0, "_ftsRelevance.search must be a non-empty string");
|
|
1169
|
+
if (value.config !== void 0) invariant(typeof value.config === "string" && value.config.length > 0, "_ftsRelevance.config must be a non-empty string");
|
|
1170
|
+
const config = value.config;
|
|
1171
|
+
for (const fieldName of value.fields) invariant(requireField(this.schema, model, fieldName).fullText === true, `field "${fieldName}" is not full-text-searchable; add the \`@fullText\` attribute to use it in \`_ftsRelevance\``);
|
|
1172
|
+
const fieldRefs = value.fields.map((f) => buildFieldRef(model, f, modelAlias));
|
|
1173
|
+
return this.buildFtsRelevanceOrderBy(query, fieldRefs, value.search, config, this.negateSort(value.sort, negated));
|
|
1174
|
+
}
|
|
1085
1175
|
buildSelectAllFields(model, query, omit, modelAlias) {
|
|
1086
1176
|
let result = query;
|
|
1087
1177
|
for (const fieldDef of getModelFields(this.schema, model, {
|
|
@@ -1224,6 +1314,27 @@ var BaseCrudDialect = class {
|
|
|
1224
1314
|
buildComparison(left, _leftFieldDef, op, right, _rightFieldDef) {
|
|
1225
1315
|
return this.eb(left, op, right);
|
|
1226
1316
|
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Validate the user-provided fuzzy filter payload and apply defaults so dialects
|
|
1319
|
+
* always receive a fully-resolved {@link FuzzyFilterOptions} value.
|
|
1320
|
+
*/
|
|
1321
|
+
normalizeFuzzyOptions(value) {
|
|
1322
|
+
invariant(value !== null && typeof value === "object" && !Array.isArray(value), "fuzzy filter must be an object with at least a \"search\" field");
|
|
1323
|
+
const raw = value;
|
|
1324
|
+
invariant(typeof raw["search"] === "string" && raw["search"].length > 0, "fuzzy.search must be a non-empty string");
|
|
1325
|
+
const mode = raw["mode"] ?? "simple";
|
|
1326
|
+
invariant(mode === "simple" || mode === "word" || mode === "strictWord", "fuzzy.mode must be \"simple\", \"word\" or \"strictWord\"");
|
|
1327
|
+
const threshold = raw["threshold"];
|
|
1328
|
+
if (threshold !== void 0) invariant(typeof threshold === "number" && threshold >= 0 && threshold <= 1, "fuzzy.threshold must be a number between 0 and 1");
|
|
1329
|
+
const unaccent = raw["unaccent"] ?? false;
|
|
1330
|
+
invariant(typeof unaccent === "boolean", "fuzzy.unaccent must be a boolean");
|
|
1331
|
+
return {
|
|
1332
|
+
search: raw["search"],
|
|
1333
|
+
mode,
|
|
1334
|
+
threshold,
|
|
1335
|
+
unaccent
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1227
1338
|
};
|
|
1228
1339
|
//#endregion
|
|
1229
1340
|
//#region src/client/crud/dialects/lateral-join-dialect-base.ts
|
|
@@ -1508,9 +1619,32 @@ var MySqlCrudDialect = class extends LateralJoinDialectBase {
|
|
|
1508
1619
|
}
|
|
1509
1620
|
return result;
|
|
1510
1621
|
}
|
|
1622
|
+
buildFuzzyFilter(_fieldRef, _options) {
|
|
1623
|
+
throw createNotSupportedError("\"fuzzy\" filter is not supported by the \"mysql\" provider");
|
|
1624
|
+
}
|
|
1625
|
+
buildFuzzyRelevanceOrderBy(_query, _fieldRefs, _search, _sort, _mode, _unaccent) {
|
|
1626
|
+
throw createNotSupportedError("\"_fuzzyRelevance\" ordering is not supported by the \"mysql\" provider");
|
|
1627
|
+
}
|
|
1628
|
+
buildFullTextFilter(_fieldRef, _payload) {
|
|
1629
|
+
throw createNotSupportedError("\"fts\" filter is not supported by the \"mysql\" provider");
|
|
1630
|
+
}
|
|
1631
|
+
buildFtsRelevanceOrderBy(_query, _fieldRefs, _search, _config, _sort) {
|
|
1632
|
+
throw createNotSupportedError("\"_ftsRelevance\" ordering is not supported by the \"mysql\" provider");
|
|
1633
|
+
}
|
|
1511
1634
|
};
|
|
1512
1635
|
//#endregion
|
|
1513
1636
|
//#region src/client/crud/dialects/postgresql.ts
|
|
1637
|
+
/**
|
|
1638
|
+
* Formats a JS `Date` as a Postgres TIME / TIMETZ literal (`HH:MM:SS.fff`,
|
|
1639
|
+
* optionally with `+ZZ:ZZ` for TIMETZ). Reads UTC components so the value
|
|
1640
|
+
* round-trips with ISO-input parsing — callers anchor time-only inputs to
|
|
1641
|
+
* the Unix epoch.
|
|
1642
|
+
*/
|
|
1643
|
+
function formatTimeOfDay(date, withTimezone) {
|
|
1644
|
+
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
1645
|
+
const time = `${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}.${pad(date.getUTCMilliseconds(), 3)}`;
|
|
1646
|
+
return withTimezone ? `${time}+00:00` : time;
|
|
1647
|
+
}
|
|
1514
1648
|
var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBase {
|
|
1515
1649
|
static typeParserOverrideApplied = false;
|
|
1516
1650
|
zmodelToSqlTypeMap = {
|
|
@@ -1604,7 +1738,7 @@ var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBa
|
|
|
1604
1738
|
get insertIgnoreMethod() {
|
|
1605
1739
|
return "onConflict";
|
|
1606
1740
|
}
|
|
1607
|
-
transformInput(value, type, forArrayField) {
|
|
1741
|
+
transformInput(value, type, forArrayField, fieldDef) {
|
|
1608
1742
|
if (value === void 0) return value;
|
|
1609
1743
|
if (value instanceof JsonNullClass) return "null";
|
|
1610
1744
|
else if (value instanceof DbNullClass) return null;
|
|
@@ -1612,9 +1746,15 @@ var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBa
|
|
|
1612
1746
|
if (isTypeDef(this.schema, type)) if (typeof value !== "string") return JSON.stringify(value);
|
|
1613
1747
|
else return value;
|
|
1614
1748
|
else if (Array.isArray(value)) if (type === "Json" && !forArrayField) return JSON.stringify(value);
|
|
1615
|
-
else return value.map((v) => this.transformInput(v, type, false));
|
|
1749
|
+
else return value.map((v) => this.transformInput(v, type, false, fieldDef));
|
|
1616
1750
|
else switch (type) {
|
|
1617
|
-
case "DateTime":
|
|
1751
|
+
case "DateTime": {
|
|
1752
|
+
const date = value instanceof Date ? value : typeof value === "string" ? new Date(value) : null;
|
|
1753
|
+
if (date === null || isNaN(date.getTime())) return value;
|
|
1754
|
+
const dbAttrName = fieldDef?.attributes?.find((a) => a.name.startsWith("@db."))?.name;
|
|
1755
|
+
if (dbAttrName === "@db.Time" || dbAttrName === "@db.Timetz") return formatTimeOfDay(date, dbAttrName === "@db.Timetz");
|
|
1756
|
+
return date.toISOString();
|
|
1757
|
+
}
|
|
1618
1758
|
case "Decimal": return value !== null ? value.toString() : value;
|
|
1619
1759
|
case "Json": if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return JSON.stringify(value);
|
|
1620
1760
|
else return value;
|
|
@@ -1786,6 +1926,72 @@ var PostgresCrudDialect = class PostgresCrudDialect extends LateralJoinDialectBa
|
|
|
1786
1926
|
return ob;
|
|
1787
1927
|
});
|
|
1788
1928
|
}
|
|
1929
|
+
/**
|
|
1930
|
+
* Wraps an expression with `unaccent(lower(...))` or just `lower(...)` depending on
|
|
1931
|
+
* whether the user opted into accent-insensitive matching. The lowering is always
|
|
1932
|
+
* applied so trigram comparisons are case-insensitive on both sides.
|
|
1933
|
+
*/
|
|
1934
|
+
normalizeForTrigram(expr, applyUnaccent) {
|
|
1935
|
+
return applyUnaccent ? sql`unaccent(lower(${expr}))` : sql`lower(${expr})`;
|
|
1936
|
+
}
|
|
1937
|
+
buildFuzzyFilter(fieldRef, options) {
|
|
1938
|
+
const fieldExpr = this.normalizeForTrigram(fieldRef, options.unaccent);
|
|
1939
|
+
const valueExpr = this.normalizeForTrigram(sql.val(options.search), options.unaccent);
|
|
1940
|
+
if (options.threshold === void 0) switch (options.mode) {
|
|
1941
|
+
case "simple": return sql`${fieldExpr} % ${valueExpr}`;
|
|
1942
|
+
case "word": return sql`${valueExpr} <% ${fieldExpr}`;
|
|
1943
|
+
case "strictWord": return sql`${valueExpr} <<% ${fieldExpr}`;
|
|
1944
|
+
}
|
|
1945
|
+
const threshold = sql.val(options.threshold);
|
|
1946
|
+
switch (options.mode) {
|
|
1947
|
+
case "simple": return sql`similarity(${fieldExpr}, ${valueExpr}) > ${threshold}`;
|
|
1948
|
+
case "word": return sql`word_similarity(${valueExpr}, ${fieldExpr}) > ${threshold}`;
|
|
1949
|
+
case "strictWord": return sql`strict_word_similarity(${valueExpr}, ${fieldExpr}) > ${threshold}`;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
buildFuzzyRelevanceOrderBy(query, fieldRefs, search, sort, mode, unaccent) {
|
|
1953
|
+
const valueExpr = this.normalizeForTrigram(sql.val(search), unaccent);
|
|
1954
|
+
const buildSimilarity = (fieldRef) => {
|
|
1955
|
+
const fieldExpr = this.normalizeForTrigram(fieldRef, unaccent);
|
|
1956
|
+
switch (mode) {
|
|
1957
|
+
case "simple": return sql`similarity(${fieldExpr}, ${valueExpr})`;
|
|
1958
|
+
case "word": return sql`word_similarity(${valueExpr}, ${fieldExpr})`;
|
|
1959
|
+
case "strictWord": return sql`strict_word_similarity(${valueExpr}, ${fieldExpr})`;
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
if (fieldRefs.length === 1) return query.orderBy(buildSimilarity(fieldRefs[0]), sort);
|
|
1963
|
+
const similarities = fieldRefs.map((ref) => buildSimilarity(ref));
|
|
1964
|
+
return query.orderBy(sql`GREATEST(${sql.join(similarities)})`, sort);
|
|
1965
|
+
}
|
|
1966
|
+
buildFullTextFilter(fieldRef, payload) {
|
|
1967
|
+
const options = this.normalizeFullTextOptions(payload);
|
|
1968
|
+
const query = sql.val(options.search);
|
|
1969
|
+
if (options.config === void 0) return sql`to_tsvector(${fieldRef}) @@ to_tsquery(${query})`;
|
|
1970
|
+
const cfg = sql.val(options.config);
|
|
1971
|
+
return sql`to_tsvector(${cfg}::regconfig, ${fieldRef}) @@ to_tsquery(${cfg}::regconfig, ${query})`;
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Validate the user-provided `fts` filter payload. When `config` is omitted
|
|
1975
|
+
* it stays `undefined` so {@link buildFullTextFilter} can emit the no-regconfig
|
|
1976
|
+
* SQL form and let Postgres fall back to `default_text_search_config`.
|
|
1977
|
+
*/
|
|
1978
|
+
normalizeFullTextOptions(value) {
|
|
1979
|
+
invariant(value !== null && typeof value === "object" && !Array.isArray(value), "fts filter must be an object with at least a \"search\" field");
|
|
1980
|
+
const raw = value;
|
|
1981
|
+
invariant(typeof raw["search"] === "string" && raw["search"].length > 0, "fts.search must be a non-empty string");
|
|
1982
|
+
if (raw["config"] !== void 0) invariant(typeof raw["config"] === "string" && raw["config"].length > 0, "fts.config must be a non-empty string");
|
|
1983
|
+
return {
|
|
1984
|
+
search: raw["search"],
|
|
1985
|
+
config: raw["config"]
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
buildFtsRelevanceOrderBy(query, fieldRefs, search, config, sort) {
|
|
1989
|
+
const q = sql.val(search);
|
|
1990
|
+
const document = fieldRefs.length === 1 ? sql`coalesce(${fieldRefs[0]}, '')` : sql`concat_ws(' ', ${sql.join(fieldRefs)})`;
|
|
1991
|
+
if (config === void 0) return query.orderBy(sql`ts_rank(to_tsvector(${document}), to_tsquery(${q}))`, sort);
|
|
1992
|
+
const cfg = sql.val(config);
|
|
1993
|
+
return query.orderBy(sql`ts_rank(to_tsvector(${cfg}::regconfig, ${document}), to_tsquery(${cfg}::regconfig, ${q}))`, sort);
|
|
1994
|
+
}
|
|
1789
1995
|
};
|
|
1790
1996
|
//#endregion
|
|
1791
1997
|
//#region src/client/crud/dialects/sqlite.ts
|
|
@@ -2003,6 +2209,18 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
2003
2209
|
return ob;
|
|
2004
2210
|
});
|
|
2005
2211
|
}
|
|
2212
|
+
buildFuzzyFilter(_fieldRef, _options) {
|
|
2213
|
+
throw createNotSupportedError("\"fuzzy\" filter is not supported by the \"sqlite\" provider");
|
|
2214
|
+
}
|
|
2215
|
+
buildFuzzyRelevanceOrderBy(_query, _fieldRefs, _search, _sort, _mode, _unaccent) {
|
|
2216
|
+
throw createNotSupportedError("\"_fuzzyRelevance\" ordering is not supported by the \"sqlite\" provider");
|
|
2217
|
+
}
|
|
2218
|
+
buildFullTextFilter(_fieldRef, _payload) {
|
|
2219
|
+
throw createNotSupportedError("\"fts\" filter is not supported by the \"sqlite\" provider");
|
|
2220
|
+
}
|
|
2221
|
+
buildFtsRelevanceOrderBy(_query, _fieldRefs, _search, _config, _sort) {
|
|
2222
|
+
throw createNotSupportedError("\"_ftsRelevance\" ordering is not supported by the \"sqlite\" provider");
|
|
2223
|
+
}
|
|
2006
2224
|
};
|
|
2007
2225
|
//#endregion
|
|
2008
2226
|
//#region src/client/crud/dialects/index.ts
|
|
@@ -2058,6 +2276,33 @@ function createQuerySchemaFactory(clientOrSchema, options) {
|
|
|
2058
2276
|
return new ZodSchemaFactory(clientOrSchema, options);
|
|
2059
2277
|
}
|
|
2060
2278
|
/**
|
|
2279
|
+
* Builds a `DateTime` value schema that accepts a `Date` object or any string
|
|
2280
|
+
* the JS `Date` constructor parses, and coerces it to a `Date`. ISO datetime,
|
|
2281
|
+
* ISO date, and time-only strings (e.g. `"09:00:00"` for `@db.Time` fields,
|
|
2282
|
+
* anchored to the Unix epoch) are the documented happy paths; other formats
|
|
2283
|
+
* accepted by `new Date(...)` also pass through, mirroring Prisma's pre-3.5
|
|
2284
|
+
* behaviour. Strings the engine can't parse fall through and are rejected by
|
|
2285
|
+
* `z.date()` with the standard error.
|
|
2286
|
+
*
|
|
2287
|
+
* @see https://github.com/zenstackhq/zenstack/issues/2631
|
|
2288
|
+
*/
|
|
2289
|
+
function coercedDateTimeSchema() {
|
|
2290
|
+
return z.preprocess((val) => {
|
|
2291
|
+
if (typeof val !== "string") return val;
|
|
2292
|
+
if (/^\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d\d(?::\d\d)?)?$/.test(val)) {
|
|
2293
|
+
const hasTz = val.endsWith("Z") || /[+-]\d\d(?::\d\d)?$/.test(val);
|
|
2294
|
+
const d = /* @__PURE__ */ new Date(`1970-01-01T${val}${hasTz ? "" : "Z"}`);
|
|
2295
|
+
return isNaN(d.getTime()) ? val : d;
|
|
2296
|
+
}
|
|
2297
|
+
const d = new Date(val);
|
|
2298
|
+
return isNaN(d.getTime()) ? val : d;
|
|
2299
|
+
}, z.union([
|
|
2300
|
+
z.iso.datetime(),
|
|
2301
|
+
z.iso.date(),
|
|
2302
|
+
z.date()
|
|
2303
|
+
]));
|
|
2304
|
+
}
|
|
2305
|
+
/**
|
|
2061
2306
|
* Factory class responsible for creating and caching Zod schemas for ORM input validation.
|
|
2062
2307
|
*/
|
|
2063
2308
|
var ZodSchemaFactory = class {
|
|
@@ -2226,7 +2471,7 @@ var ZodSchemaFactory = class {
|
|
|
2226
2471
|
const typeDef = getTypeDef(this.schema, type);
|
|
2227
2472
|
invariant(typeDef, `Type definition "${type}" not found in schema`);
|
|
2228
2473
|
const schema = z.looseObject(Object.fromEntries(Object.entries(typeDef.fields).map(([field, def]) => {
|
|
2229
|
-
let fieldSchema = this.makeScalarSchema(def.type);
|
|
2474
|
+
let fieldSchema = isTypeDef(this.schema, def.type) ? z.lazy(() => this.makeTypeDefSchema(def.type)) : this.makeScalarSchema(def.type);
|
|
2230
2475
|
if (def.array) fieldSchema = fieldSchema.array();
|
|
2231
2476
|
if (def.optional) fieldSchema = fieldSchema.nullish();
|
|
2232
2477
|
return [field, fieldSchema];
|
|
@@ -2270,7 +2515,7 @@ var ZodSchemaFactory = class {
|
|
|
2270
2515
|
if (Object.keys(enumDef.values).length > 0) fieldSchema = this.makeEnumFilterSchema(fieldDef.type, !!fieldDef.optional, !!fieldDef.array, withAggregations, allowedFilterKinds);
|
|
2271
2516
|
} else if (fieldDef.array) fieldSchema = this.makeArrayFilterSchema(fieldDef.type, allowedFilterKinds);
|
|
2272
2517
|
else if (this.isTypeDefType(fieldDef.type)) fieldSchema = this.makeTypedJsonFilterSchema(fieldDef.type, !!fieldDef.optional, !!fieldDef.array, allowedFilterKinds);
|
|
2273
|
-
else fieldSchema = this.makePrimitiveFilterSchema(fieldDef.type, !!fieldDef.optional, withAggregations, allowedFilterKinds);
|
|
2518
|
+
else fieldSchema = this.makePrimitiveFilterSchema(fieldDef.type, !!fieldDef.optional, withAggregations, allowedFilterKinds, !!fieldDef.fuzzy, !!fieldDef.fullText);
|
|
2274
2519
|
}
|
|
2275
2520
|
if (fieldSchema) fields[field] = fieldSchema.optional();
|
|
2276
2521
|
}
|
|
@@ -2399,8 +2644,8 @@ var ZodSchemaFactory = class {
|
|
|
2399
2644
|
const filteredOperators = this.trimFilterOperators(operators, allowedFilterKinds);
|
|
2400
2645
|
return z.strictObject(filteredOperators);
|
|
2401
2646
|
}
|
|
2402
|
-
makePrimitiveFilterSchema(type, optional, withAggregations, allowedFilterKinds) {
|
|
2403
|
-
return match(type).with("String", () => this.makeStringFilterSchema(optional, withAggregations, allowedFilterKinds)).with(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", () => z.never()).exhaustive();
|
|
2647
|
+
makePrimitiveFilterSchema(type, optional, withAggregations, allowedFilterKinds, withFuzzy = false, withFullText = false) {
|
|
2648
|
+
return match(type).with("String", () => this.makeStringFilterSchema(optional, withAggregations, allowedFilterKinds, withFuzzy, withFullText)).with(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", () => z.never()).exhaustive();
|
|
2404
2649
|
}
|
|
2405
2650
|
makeJsonValueSchema() {
|
|
2406
2651
|
const schema = z.union([
|
|
@@ -2442,11 +2687,7 @@ var ZodSchemaFactory = class {
|
|
|
2442
2687
|
return schema;
|
|
2443
2688
|
}
|
|
2444
2689
|
makeDateTimeValueSchema() {
|
|
2445
|
-
const schema =
|
|
2446
|
-
z.iso.datetime(),
|
|
2447
|
-
z.iso.date(),
|
|
2448
|
-
z.date()
|
|
2449
|
-
]);
|
|
2690
|
+
const schema = coercedDateTimeSchema();
|
|
2450
2691
|
this.registerSchema("DateTime", schema);
|
|
2451
2692
|
return schema;
|
|
2452
2693
|
}
|
|
@@ -2542,8 +2783,8 @@ var ZodSchemaFactory = class {
|
|
|
2542
2783
|
})}`, schema);
|
|
2543
2784
|
return schema;
|
|
2544
2785
|
}
|
|
2545
|
-
makeStringFilterSchema(optional, withAggregations, allowedFilterKinds) {
|
|
2546
|
-
const baseComponents = this.makeCommonPrimitiveFilterComponents(z.string(), optional, () => z.lazy(() => this.makeStringFilterSchema(optional, withAggregations, allowedFilterKinds)), void 0, withAggregations ? [
|
|
2786
|
+
makeStringFilterSchema(optional, withAggregations, allowedFilterKinds, withFuzzy = false, withFullText = false) {
|
|
2787
|
+
const baseComponents = this.makeCommonPrimitiveFilterComponents(z.string(), optional, () => z.lazy(() => this.makeStringFilterSchema(optional, withAggregations, allowedFilterKinds, withFuzzy, withFullText)), void 0, withAggregations ? [
|
|
2547
2788
|
"_count",
|
|
2548
2789
|
"_min",
|
|
2549
2790
|
"_max"
|
|
@@ -2552,6 +2793,8 @@ var ZodSchemaFactory = class {
|
|
|
2552
2793
|
startsWith: z.string().optional(),
|
|
2553
2794
|
endsWith: z.string().optional(),
|
|
2554
2795
|
contains: z.string().optional(),
|
|
2796
|
+
...withFuzzy && this.providerSupportsFuzzySearch ? { fuzzy: this.makeFuzzyFilterSchema().optional() } : {},
|
|
2797
|
+
...withFullText && this.providerSupportsFullTextSearch ? { fts: this.makeFullTextFilterSchema().optional() } : {},
|
|
2555
2798
|
...this.providerSupportsCaseSensitivity ? { mode: this.makeStringModeSchema().optional() } : {}
|
|
2556
2799
|
};
|
|
2557
2800
|
const filteredStringOperators = this.trimFilterOperators(stringSpecificOperators, allowedFilterKinds);
|
|
@@ -2560,16 +2803,35 @@ var ZodSchemaFactory = class {
|
|
|
2560
2803
|
...filteredStringOperators
|
|
2561
2804
|
};
|
|
2562
2805
|
const schema = this.createUnionFilterSchema(z.string(), optional, allComponents, allowedFilterKinds);
|
|
2806
|
+
const featureSuffix = `${withFuzzy ? "Fuzzy" : ""}${withFullText ? "FullText" : ""}`;
|
|
2563
2807
|
this.registerSchema(`StringFilter${this.filterSchemaSuffix({
|
|
2564
2808
|
optional,
|
|
2565
2809
|
allowedFilterKinds,
|
|
2566
2810
|
withAggregations
|
|
2567
|
-
})}`, schema);
|
|
2811
|
+
})}${featureSuffix}`, schema);
|
|
2568
2812
|
return schema;
|
|
2569
2813
|
}
|
|
2570
2814
|
makeStringModeSchema() {
|
|
2571
2815
|
return z.union([z.literal("default"), z.literal("insensitive")]);
|
|
2572
2816
|
}
|
|
2817
|
+
makeFuzzyFilterSchema() {
|
|
2818
|
+
return z.strictObject({
|
|
2819
|
+
search: z.string().min(1),
|
|
2820
|
+
mode: z.union([
|
|
2821
|
+
z.literal("simple"),
|
|
2822
|
+
z.literal("word"),
|
|
2823
|
+
z.literal("strictWord")
|
|
2824
|
+
]).default("simple"),
|
|
2825
|
+
threshold: z.number().min(0).max(1).optional(),
|
|
2826
|
+
unaccent: z.boolean().default(false)
|
|
2827
|
+
});
|
|
2828
|
+
}
|
|
2829
|
+
makeFullTextFilterSchema() {
|
|
2830
|
+
return z.strictObject({
|
|
2831
|
+
search: z.string().min(1),
|
|
2832
|
+
config: z.string().min(1).optional()
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2573
2835
|
makeSelectSchema(model, options) {
|
|
2574
2836
|
const fields = {};
|
|
2575
2837
|
for (const [field, fieldDef] of this.getModelFields(model)) if (fieldDef.relation) {
|
|
@@ -2670,7 +2932,7 @@ var ZodSchemaFactory = class {
|
|
|
2670
2932
|
}).optional();
|
|
2671
2933
|
} else if (fieldDef.optional) fields[field] = z.union([sort, z.strictObject({
|
|
2672
2934
|
sort,
|
|
2673
|
-
nulls: z.union([z.literal("first"), z.literal("last")])
|
|
2935
|
+
nulls: z.union([z.literal("first"), z.literal("last")]).optional()
|
|
2674
2936
|
})]).optional();
|
|
2675
2937
|
else fields[field] = sort.optional();
|
|
2676
2938
|
if (WithAggregation) for (const agg of [
|
|
@@ -2680,6 +2942,29 @@ var ZodSchemaFactory = class {
|
|
|
2680
2942
|
"_min",
|
|
2681
2943
|
"_max"
|
|
2682
2944
|
]) fields[agg] = z.lazy(() => this.makeOrderBySchema(model, true, false, options).optional());
|
|
2945
|
+
if (this.providerSupportsFuzzySearch) {
|
|
2946
|
+
const fuzzyFieldNames = this.getModelFields(model).filter(([, def]) => !def.relation && def.type === "String" && def.fuzzy === true).map(([name]) => name);
|
|
2947
|
+
if (fuzzyFieldNames.length > 0) fields["_fuzzyRelevance"] = z.strictObject({
|
|
2948
|
+
fields: z.array(z.enum(fuzzyFieldNames)).min(1),
|
|
2949
|
+
search: z.string(),
|
|
2950
|
+
mode: z.union([
|
|
2951
|
+
z.literal("simple"),
|
|
2952
|
+
z.literal("word"),
|
|
2953
|
+
z.literal("strictWord")
|
|
2954
|
+
]).default("simple"),
|
|
2955
|
+
unaccent: z.boolean().default(false),
|
|
2956
|
+
sort
|
|
2957
|
+
}).optional();
|
|
2958
|
+
}
|
|
2959
|
+
if (this.providerSupportsFullTextSearch) {
|
|
2960
|
+
const fullTextFieldNames = this.getModelFields(model).filter(([, def]) => !def.relation && def.type === "String" && def.fullText === true).map(([name]) => name);
|
|
2961
|
+
if (fullTextFieldNames.length > 0) fields["_ftsRelevance"] = z.strictObject({
|
|
2962
|
+
fields: z.array(z.enum(fullTextFieldNames)).min(1),
|
|
2963
|
+
search: z.string().min(1),
|
|
2964
|
+
config: z.string().min(1).optional(),
|
|
2965
|
+
sort
|
|
2966
|
+
}).optional();
|
|
2967
|
+
}
|
|
2683
2968
|
const schema = refineAtMostOneKey(z.strictObject(fields));
|
|
2684
2969
|
let schemaId = `${model}OrderBy`;
|
|
2685
2970
|
if (withRelation) schemaId += "WithRelation";
|
|
@@ -3181,6 +3466,12 @@ var ZodSchemaFactory = class {
|
|
|
3181
3466
|
get providerSupportsCaseSensitivity() {
|
|
3182
3467
|
return this.schema.provider.type === "postgresql";
|
|
3183
3468
|
}
|
|
3469
|
+
get providerSupportsFullTextSearch() {
|
|
3470
|
+
return this.schema.provider.type === "postgresql";
|
|
3471
|
+
}
|
|
3472
|
+
get providerSupportsFuzzySearch() {
|
|
3473
|
+
return this.schema.provider.type === "postgresql";
|
|
3474
|
+
}
|
|
3184
3475
|
/**
|
|
3185
3476
|
* Gets the effective set of allowed FilterKind values for a specific model and field.
|
|
3186
3477
|
* Respects the precedence: model[field] > model.$all > $all[field] > $all.$all.
|
|
@@ -3342,6 +3633,8 @@ __decorate([
|
|
|
3342
3633
|
Object,
|
|
3343
3634
|
Boolean,
|
|
3344
3635
|
Boolean,
|
|
3636
|
+
Object,
|
|
3637
|
+
Object,
|
|
3345
3638
|
Object
|
|
3346
3639
|
]),
|
|
3347
3640
|
__decorateMetadata("design:returntype", void 0)
|
|
@@ -3411,6 +3704,8 @@ __decorate([
|
|
|
3411
3704
|
__decorateMetadata("design:paramtypes", [
|
|
3412
3705
|
Boolean,
|
|
3413
3706
|
Boolean,
|
|
3707
|
+
Object,
|
|
3708
|
+
Object,
|
|
3414
3709
|
Object
|
|
3415
3710
|
]),
|
|
3416
3711
|
__decorateMetadata("design:returntype", typeof (_ref10 = typeof ZodType !== "undefined" && ZodType) === "function" ? _ref10 : Object)
|
|
@@ -3900,7 +4195,7 @@ var BaseOperationHandler = class {
|
|
|
3900
4195
|
return new this.constructor(client, this.model, this.inputValidator);
|
|
3901
4196
|
}
|
|
3902
4197
|
get hasPolicyEnabled() {
|
|
3903
|
-
return this.options.plugins?.some((plugin) => plugin.
|
|
4198
|
+
return this.options.plugins?.some((plugin) => plugin.id === "policy") ?? false;
|
|
3904
4199
|
}
|
|
3905
4200
|
requireModel(model) {
|
|
3906
4201
|
return requireModel(this.schema, model);
|
|
@@ -4001,8 +4296,8 @@ var BaseOperationHandler = class {
|
|
|
4001
4296
|
const postCreateRelations = {};
|
|
4002
4297
|
for (const [field, value] of Object.entries(data)) {
|
|
4003
4298
|
const fieldDef = this.requireField(model, field);
|
|
4004
|
-
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);
|
|
4005
|
-
else createFields[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array);
|
|
4299
|
+
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);
|
|
4300
|
+
else createFields[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4006
4301
|
else if (!getManyToManyRelation(this.schema, model, field) && fieldDef.relation?.fields && fieldDef.relation?.references) {
|
|
4007
4302
|
const fkValues = await this.processOwnedRelationForCreate(kysely, fieldDef, value);
|
|
4008
4303
|
for (let i = 0; i < fieldDef.relation.fields.length; i++) createFields[fieldDef.relation.fields[i]] = fkValues[fieldDef.relation.references[i]];
|
|
@@ -4059,7 +4354,7 @@ var BaseOperationHandler = class {
|
|
|
4059
4354
|
});
|
|
4060
4355
|
const discriminatorField = getDiscriminatorField(this.schema, model);
|
|
4061
4356
|
invariant(discriminatorField, `Base model "${model}" must have a discriminator field`);
|
|
4062
|
-
thisCreateFields[discriminatorField] = forModel;
|
|
4357
|
+
thisCreateFields[discriminatorField] = getDelegateDiscriminatorValue(this.schema, forModel);
|
|
4063
4358
|
const baseEntity = await this.create(kysely, model, thisCreateFields, void 0, true);
|
|
4064
4359
|
const idValues = extractIdFields(baseEntity, this.schema, model);
|
|
4065
4360
|
Object.assign(remainingFields, idValues);
|
|
@@ -4202,7 +4497,7 @@ var BaseOperationHandler = class {
|
|
|
4202
4497
|
for (const [name, value] of Object.entries(item)) {
|
|
4203
4498
|
const fieldDef = this.requireField(model, name);
|
|
4204
4499
|
invariant(!fieldDef.relation, "createMany does not support relations");
|
|
4205
|
-
newItem[name] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array);
|
|
4500
|
+
newItem[name] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4206
4501
|
}
|
|
4207
4502
|
if (fromRelation) for (const { fk, pk } of relationKeyPairs) newItem[fk] = fromRelation.ids[pk];
|
|
4208
4503
|
return this.fillGeneratedAndDefaultValues(modelDef, newItem);
|
|
@@ -4218,7 +4513,7 @@ var BaseOperationHandler = class {
|
|
|
4218
4513
|
if (Object.keys(item).length === allPassedFields.length) continue;
|
|
4219
4514
|
for (const field of allPassedFields) if (!(field in item)) {
|
|
4220
4515
|
const fieldDef = this.requireField(model, field);
|
|
4221
|
-
if (fieldDef.default !== void 0 && fieldDef.default !== null && typeof fieldDef.default !== "object") item[field] = this.dialect.transformInput(fieldDef.default, fieldDef.type, !!fieldDef.array);
|
|
4516
|
+
if (fieldDef.default !== void 0 && fieldDef.default !== null && typeof fieldDef.default !== "object") item[field] = this.dialect.transformInput(fieldDef.default, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4222
4517
|
}
|
|
4223
4518
|
}
|
|
4224
4519
|
}
|
|
@@ -4251,7 +4546,7 @@ var BaseOperationHandler = class {
|
|
|
4251
4546
|
if (this.getField(model, field)) thisCreateFields[field] = value;
|
|
4252
4547
|
else remainingFields[field] = value;
|
|
4253
4548
|
});
|
|
4254
|
-
thisCreateFields[discriminatorField] = forModel;
|
|
4549
|
+
thisCreateFields[discriminatorField] = getDelegateDiscriminatorValue(this.schema, forModel);
|
|
4255
4550
|
thisCreateRows.push(thisCreateFields);
|
|
4256
4551
|
remainingFieldRows.push(remainingFields);
|
|
4257
4552
|
}
|
|
@@ -4281,15 +4576,15 @@ var BaseOperationHandler = class {
|
|
|
4281
4576
|
if (!(field in data)) {
|
|
4282
4577
|
if (typeof fieldDef?.default === "object" && "kind" in fieldDef.default) {
|
|
4283
4578
|
const generated = this.evalGenerator(fieldDef.default);
|
|
4284
|
-
if (generated !== void 0) values[field] = this.dialect.transformInput(generated, fieldDef.type, !!fieldDef.array);
|
|
4285
|
-
} else if (fieldDef?.updatedAt) values[field] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false);
|
|
4579
|
+
if (generated !== void 0) values[field] = this.dialect.transformInput(generated, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4580
|
+
} else if (fieldDef?.updatedAt) values[field] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false, fieldDef);
|
|
4286
4581
|
else if (fieldDef?.default !== void 0) {
|
|
4287
4582
|
let value = fieldDef.default;
|
|
4288
4583
|
if (fieldDef.type === "Json") {
|
|
4289
4584
|
if (fieldDef.array && Array.isArray(value)) value = value.map((v) => typeof v === "string" ? JSON.parse(v) : v);
|
|
4290
4585
|
else if (typeof value === "string") value = JSON.parse(value);
|
|
4291
4586
|
}
|
|
4292
|
-
values[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array);
|
|
4587
|
+
values[field] = this.dialect.transformInput(value, fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4293
4588
|
}
|
|
4294
4589
|
}
|
|
4295
4590
|
}
|
|
@@ -4339,7 +4634,7 @@ var BaseOperationHandler = class {
|
|
|
4339
4634
|
const ignoredFields = new Set(typeof fieldDef.updatedAt === "boolean" ? [] : fieldDef.updatedAt.ignore);
|
|
4340
4635
|
if (Object.keys(data).some((field) => (isScalarField(this.schema, modelDef.name, field) || isForeignKeyField(this.schema, modelDef.name, field)) && !ignoredFields.has(field))) {
|
|
4341
4636
|
if (finalData === data) finalData = clone(data);
|
|
4342
|
-
finalData[fieldName] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false);
|
|
4637
|
+
finalData[fieldName] = this.dialect.transformInput(/* @__PURE__ */ new Date(), "DateTime", false, fieldDef);
|
|
4343
4638
|
autoUpdatedFields.push(fieldName);
|
|
4344
4639
|
}
|
|
4345
4640
|
}
|
|
@@ -4443,7 +4738,7 @@ var BaseOperationHandler = class {
|
|
|
4443
4738
|
const fieldDef = this.requireField(model, field);
|
|
4444
4739
|
if (this.isNumericIncrementalUpdate(fieldDef, data[field])) return this.transformIncrementalUpdate(model, field, fieldDef, data[field]);
|
|
4445
4740
|
if (fieldDef.array && typeof data[field] === "object" && !Array.isArray(data[field]) && data[field]) return this.transformScalarListUpdate(model, field, fieldDef, data[field]);
|
|
4446
|
-
return this.dialect.transformInput(data[field], fieldDef.type, !!fieldDef.array);
|
|
4741
|
+
return this.dialect.transformInput(data[field], fieldDef.type, !!fieldDef.array, fieldDef);
|
|
4447
4742
|
}
|
|
4448
4743
|
isNumericIncrementalUpdate(fieldDef, value) {
|
|
4449
4744
|
if (!this.isNumericField(fieldDef)) return false;
|
|
@@ -4471,7 +4766,7 @@ var BaseOperationHandler = class {
|
|
|
4471
4766
|
transformIncrementalUpdate(model, field, fieldDef, payload) {
|
|
4472
4767
|
invariant(Object.keys(payload).length === 1, "Only one of \"set\", \"increment\", \"decrement\", \"multiply\", or \"divide\" can be provided");
|
|
4473
4768
|
const key = Object.keys(payload)[0];
|
|
4474
|
-
const value = this.dialect.transformInput(payload[key], fieldDef.type, false);
|
|
4769
|
+
const value = this.dialect.transformInput(payload[key], fieldDef.type, false, fieldDef);
|
|
4475
4770
|
const eb = expressionBuilder();
|
|
4476
4771
|
const fieldRef = this.dialect.fieldRef(model, field);
|
|
4477
4772
|
return 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(() => {
|
|
@@ -4481,7 +4776,7 @@ var BaseOperationHandler = class {
|
|
|
4481
4776
|
transformScalarListUpdate(model, field, fieldDef, payload) {
|
|
4482
4777
|
invariant(Object.keys(payload).length === 1, "Only one of \"set\", \"push\" can be provided");
|
|
4483
4778
|
const key = Object.keys(payload)[0];
|
|
4484
|
-
const value = this.dialect.transformInput(payload[key], fieldDef.type, true);
|
|
4779
|
+
const value = this.dialect.transformInput(payload[key], fieldDef.type, true, fieldDef);
|
|
4485
4780
|
const eb = expressionBuilder();
|
|
4486
4781
|
const fieldRef = this.dialect.fieldRef(model, field);
|
|
4487
4782
|
return match(key).with("set", () => value).with("push", () => {
|
|
@@ -6892,12 +7187,21 @@ var ClientImpl = class ClientImpl {
|
|
|
6892
7187
|
});
|
|
6893
7188
|
}
|
|
6894
7189
|
}
|
|
7190
|
+
getPromiseCallback(promise) {
|
|
7191
|
+
invariant(promise.cb, "Invalid ZenStackPromise, missing cb property");
|
|
7192
|
+
const cb = promise.cb;
|
|
7193
|
+
invariant(typeof cb === "function", "Invalid ZenStackPromise, cb property is not a function");
|
|
7194
|
+
return promise.cb;
|
|
7195
|
+
}
|
|
6895
7196
|
async sequentialTransaction(arg, options) {
|
|
6896
7197
|
const execute = async (tx) => {
|
|
6897
7198
|
const txClient = new ClientImpl(this.schema, this.$options, this);
|
|
6898
7199
|
txClient.kysely = tx;
|
|
6899
7200
|
const result = [];
|
|
6900
|
-
for (const promise of arg)
|
|
7201
|
+
for (const promise of arg) {
|
|
7202
|
+
const cb = this.getPromiseCallback(promise);
|
|
7203
|
+
result.push(await cb(txClient));
|
|
7204
|
+
}
|
|
6901
7205
|
return result;
|
|
6902
7206
|
};
|
|
6903
7207
|
if (this.kysely.isTransaction) return execute(this.kysely);
|
|
@@ -7684,6 +7988,15 @@ var DefaultOperationNodeVisitor = class extends OperationNodeVisitor {
|
|
|
7684
7988
|
visitCollate(node) {
|
|
7685
7989
|
this.defaultVisit(node);
|
|
7686
7990
|
}
|
|
7991
|
+
visitAlterType(node) {
|
|
7992
|
+
this.defaultVisit(node);
|
|
7993
|
+
}
|
|
7994
|
+
visitAddValue(node) {
|
|
7995
|
+
this.defaultVisit(node);
|
|
7996
|
+
}
|
|
7997
|
+
visitRenameValue(node) {
|
|
7998
|
+
this.defaultVisit(node);
|
|
7999
|
+
}
|
|
7687
8000
|
};
|
|
7688
8001
|
//#endregion
|
|
7689
8002
|
//#region src/utils/schema-utils.ts
|
|
@@ -7743,6 +8056,6 @@ var MatchingExpressionVisitor = class extends ExpressionVisitor {
|
|
|
7743
8056
|
}
|
|
7744
8057
|
};
|
|
7745
8058
|
//#endregion
|
|
7746
|
-
export { AllCrudOperations, AllReadOperations, AllWriteOperations, AnyNull, AnyNullClass, BaseCrudDialect, CRUD, CRUD_EXT, CoreCreateOperations, CoreCrudOperations, CoreDeleteOperations, CoreReadOperations, CoreUpdateOperations, CoreWriteOperations, DbNull, DbNullClass, InputValidator, JsonNull, JsonNullClass, kysely_utils_exports as KyselyUtils, ORMError, ORMErrorReason, query_utils_exports as QueryUtils, RejectedByPolicyReason, schema_utils_exports as SchemaUtils, TransactionIsolationLevel, ZenStackClient, createQuerySchemaFactory, definePlugin, getCrudDialect };
|
|
8059
|
+
export { AllCrudOperations, AllReadOperations, AllWriteOperations, AnyNull, AnyNullClass, BaseCrudDialect, CRUD, CRUD_EXT, CoreCreateOperations, CoreCrudOperations, CoreDeleteOperations, CoreReadOperations, CoreUpdateOperations, CoreWriteOperations, DbNull, DbNullClass, ExtQueryArgsMarker, ExtResultMarker, InputValidator, JsonNull, JsonNullClass, kysely_utils_exports as KyselyUtils, ORMError, ORMErrorReason, query_utils_exports as QueryUtils, RejectedByPolicyReason, schema_utils_exports as SchemaUtils, TransactionIsolationLevel, ZenStackClient, createQuerySchemaFactory, definePlugin, getCrudDialect };
|
|
7747
8060
|
|
|
7748
8061
|
//# sourceMappingURL=index.mjs.map
|