@zenstackhq/orm 3.5.1 → 3.5.3
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 +129 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +129 -6
- package/dist/index.js.map +1 -1
- package/package.json +19 -9
package/dist/index.d.cts
CHANGED
|
@@ -908,6 +908,10 @@ declare abstract class BaseCrudDialect<Schema extends SchemaDef> {
|
|
|
908
908
|
* Builds a VALUES table and select all fields from it.
|
|
909
909
|
*/
|
|
910
910
|
abstract buildValuesTableSelect(fields: FieldDef[], rows: unknown[][]): SelectQueryBuilder<any, any, any>;
|
|
911
|
+
/**
|
|
912
|
+
* Builds a binary comparison expression between two operands.
|
|
913
|
+
*/
|
|
914
|
+
buildComparison(left: Expression<unknown>, _leftFieldDef: FieldDef | undefined, op: string, right: Expression<unknown>, _rightFieldDef: FieldDef | undefined): Expression<SqlBool>;
|
|
911
915
|
/**
|
|
912
916
|
* Builds a JSON path selection expression.
|
|
913
917
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -908,6 +908,10 @@ declare abstract class BaseCrudDialect<Schema extends SchemaDef> {
|
|
|
908
908
|
* Builds a VALUES table and select all fields from it.
|
|
909
909
|
*/
|
|
910
910
|
abstract buildValuesTableSelect(fields: FieldDef[], rows: unknown[][]): SelectQueryBuilder<any, any, any>;
|
|
911
|
+
/**
|
|
912
|
+
* Builds a binary comparison expression between two operands.
|
|
913
|
+
*/
|
|
914
|
+
buildComparison(left: Expression<unknown>, _leftFieldDef: FieldDef | undefined, op: string, right: Expression<unknown>, _rightFieldDef: FieldDef | undefined): Expression<SqlBool>;
|
|
911
915
|
/**
|
|
912
916
|
* Builds a JSON path selection expression.
|
|
913
917
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1597,6 +1597,12 @@ var BaseCrudDialect = class {
|
|
|
1597
1597
|
buildExistsExpression(innerQuery) {
|
|
1598
1598
|
return this.eb.exists(innerQuery);
|
|
1599
1599
|
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Builds a binary comparison expression between two operands.
|
|
1602
|
+
*/
|
|
1603
|
+
buildComparison(left, _leftFieldDef, op, right, _rightFieldDef) {
|
|
1604
|
+
return this.eb(left, op, right);
|
|
1605
|
+
}
|
|
1600
1606
|
};
|
|
1601
1607
|
|
|
1602
1608
|
// src/client/crud/dialects/lateral-join-dialect-base.ts
|
|
@@ -1652,13 +1658,53 @@ var LateralJoinDialectBase = class extends BaseCrudDialect {
|
|
|
1652
1658
|
qb = qb.select((eb) => {
|
|
1653
1659
|
const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName);
|
|
1654
1660
|
if (relationFieldDef.array) {
|
|
1655
|
-
|
|
1661
|
+
const orderBy = this.buildRelationOrderByExpressions(relationModel, relationModelAlias, payload);
|
|
1662
|
+
return this.buildArrayAgg(this.buildJsonObject(objArgs), orderBy).as("$data");
|
|
1656
1663
|
} else {
|
|
1657
1664
|
return this.buildJsonObject(objArgs).as("$data");
|
|
1658
1665
|
}
|
|
1659
1666
|
});
|
|
1660
1667
|
return qb;
|
|
1661
1668
|
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Extracts scalar `orderBy` clauses from the relation payload and maps them to
|
|
1671
|
+
* the array-aggregation ordering format.
|
|
1672
|
+
*
|
|
1673
|
+
* For to-many relations aggregated into a JSON array (via lateral joins), this
|
|
1674
|
+
* lets us preserve a stable ordering by passing `{ expr, sort, nulls? }` into
|
|
1675
|
+
* the dialect's `buildArrayAgg` implementation.
|
|
1676
|
+
*/
|
|
1677
|
+
buildRelationOrderByExpressions(model, modelAlias, payload) {
|
|
1678
|
+
if (payload === true || !payload.orderBy) {
|
|
1679
|
+
return void 0;
|
|
1680
|
+
}
|
|
1681
|
+
const items = [];
|
|
1682
|
+
for (const orderBy of ensureArray(payload.orderBy)) {
|
|
1683
|
+
for (const [field, value] of Object.entries(orderBy)) {
|
|
1684
|
+
if (!value || requireField(this.schema, model, field).relation) {
|
|
1685
|
+
continue;
|
|
1686
|
+
}
|
|
1687
|
+
const expr = this.fieldRef(model, field, modelAlias);
|
|
1688
|
+
let sort = typeof value === "string" ? value : value.sort;
|
|
1689
|
+
if (payload.take !== void 0 && payload.take < 0) {
|
|
1690
|
+
sort = this.negateSort(sort, true);
|
|
1691
|
+
}
|
|
1692
|
+
if (typeof value === "string") {
|
|
1693
|
+
items.push({
|
|
1694
|
+
expr,
|
|
1695
|
+
sort
|
|
1696
|
+
});
|
|
1697
|
+
} else {
|
|
1698
|
+
items.push({
|
|
1699
|
+
expr,
|
|
1700
|
+
sort,
|
|
1701
|
+
nulls: value.nulls
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
return items.length > 0 ? items : void 0;
|
|
1707
|
+
}
|
|
1662
1708
|
buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName) {
|
|
1663
1709
|
const relationModelDef = requireModel(this.schema, relationModel);
|
|
1664
1710
|
const objArgs = {};
|
|
@@ -1844,7 +1890,7 @@ var MySqlCrudDialect = class extends LateralJoinDialectBase {
|
|
|
1844
1890
|
buildExistsExpression(innerQuery) {
|
|
1845
1891
|
return this.eb.exists(this.eb.selectFrom(innerQuery.as("$exists_sub")).select(this.eb.lit(1).as("_")));
|
|
1846
1892
|
}
|
|
1847
|
-
buildArrayAgg(arg) {
|
|
1893
|
+
buildArrayAgg(arg, _orderBy) {
|
|
1848
1894
|
return this.eb.fn.coalesce(sql2`JSON_ARRAYAGG(${arg})`, sql2`JSON_ARRAY()`);
|
|
1849
1895
|
}
|
|
1850
1896
|
buildSkipTake(query, skip, take) {
|
|
@@ -1991,6 +2037,33 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
|
|
|
1991
2037
|
Bytes: "bytea",
|
|
1992
2038
|
Json: "jsonb"
|
|
1993
2039
|
};
|
|
2040
|
+
// Maps @db.* attribute names to PostgreSQL SQL types for use in VALUES table casts
|
|
2041
|
+
static dbAttributeToSqlTypeMap = {
|
|
2042
|
+
"@db.Uuid": "uuid",
|
|
2043
|
+
"@db.Citext": "citext",
|
|
2044
|
+
"@db.Inet": "inet",
|
|
2045
|
+
"@db.Bit": "bit",
|
|
2046
|
+
"@db.VarBit": "varbit",
|
|
2047
|
+
"@db.Xml": "xml",
|
|
2048
|
+
"@db.Json": "json",
|
|
2049
|
+
"@db.JsonB": "jsonb",
|
|
2050
|
+
"@db.ByteA": "bytea",
|
|
2051
|
+
"@db.Text": "text",
|
|
2052
|
+
"@db.Char": "bpchar",
|
|
2053
|
+
"@db.VarChar": "varchar",
|
|
2054
|
+
"@db.Date": "date",
|
|
2055
|
+
"@db.Time": "time",
|
|
2056
|
+
"@db.Timetz": "timetz",
|
|
2057
|
+
"@db.Timestamp": "timestamp",
|
|
2058
|
+
"@db.Timestamptz": "timestamptz",
|
|
2059
|
+
"@db.SmallInt": "smallint",
|
|
2060
|
+
"@db.Integer": "integer",
|
|
2061
|
+
"@db.BigInt": "bigint",
|
|
2062
|
+
"@db.Real": "real",
|
|
2063
|
+
"@db.DoublePrecision": "double precision",
|
|
2064
|
+
"@db.Decimal": "decimal",
|
|
2065
|
+
"@db.Boolean": "boolean"
|
|
2066
|
+
};
|
|
1994
2067
|
constructor(schema, options) {
|
|
1995
2068
|
super(schema, options);
|
|
1996
2069
|
this.overrideTypeParsers();
|
|
@@ -2156,8 +2229,16 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
|
|
|
2156
2229
|
}
|
|
2157
2230
|
// #endregion
|
|
2158
2231
|
// #region other overrides
|
|
2159
|
-
buildArrayAgg(arg) {
|
|
2160
|
-
|
|
2232
|
+
buildArrayAgg(arg, orderBy) {
|
|
2233
|
+
if (!orderBy || orderBy.length === 0) {
|
|
2234
|
+
return this.eb.fn.coalesce(sql3`jsonb_agg(${arg})`, sql3`'[]'::jsonb`);
|
|
2235
|
+
}
|
|
2236
|
+
const orderBySql = sql3.join(orderBy.map(({ expr, sort, nulls }) => {
|
|
2237
|
+
const dir = sql3.raw(sort.toUpperCase());
|
|
2238
|
+
const nullsSql = nulls ? sql3` NULLS ${sql3.raw(nulls.toUpperCase())}` : sql3``;
|
|
2239
|
+
return sql3`${expr} ${dir}${nullsSql}`;
|
|
2240
|
+
}), sql3.raw(", "));
|
|
2241
|
+
return this.eb.fn.coalesce(sql3`jsonb_agg(${arg} ORDER BY ${orderBySql})`, sql3`'[]'::jsonb`);
|
|
2161
2242
|
}
|
|
2162
2243
|
buildSkipTake(query, skip, take) {
|
|
2163
2244
|
if (take !== void 0) {
|
|
@@ -2247,13 +2328,55 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
|
|
|
2247
2328
|
receiver
|
|
2248
2329
|
]).as("$items")).select(this.eb.lit(1).as("_")).where(buildFilter(this.eb.ref("$items.value"))));
|
|
2249
2330
|
}
|
|
2250
|
-
getSqlType(zmodelType) {
|
|
2331
|
+
getSqlType(zmodelType, attributes) {
|
|
2332
|
+
if (attributes) {
|
|
2333
|
+
for (const attr of attributes) {
|
|
2334
|
+
const mapped = _PostgresCrudDialect.dbAttributeToSqlTypeMap[attr.name];
|
|
2335
|
+
if (mapped) {
|
|
2336
|
+
return mapped;
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2251
2340
|
if (isEnum(this.schema, zmodelType)) {
|
|
2252
2341
|
return "text";
|
|
2253
2342
|
} else {
|
|
2254
2343
|
return this.zmodelToSqlTypeMap[zmodelType] ?? "text";
|
|
2255
2344
|
}
|
|
2256
2345
|
}
|
|
2346
|
+
// Resolves the effective SQL type for a field: the native type from any @db.* attribute,
|
|
2347
|
+
// or the base ZModel SQL type if no attribute is present, or undefined if the field is unknown.
|
|
2348
|
+
resolveFieldSqlType(fieldDef) {
|
|
2349
|
+
if (!fieldDef) {
|
|
2350
|
+
return {
|
|
2351
|
+
sqlType: void 0,
|
|
2352
|
+
hasDbOverride: false
|
|
2353
|
+
};
|
|
2354
|
+
}
|
|
2355
|
+
const dbAttr = fieldDef.attributes?.find((a) => a.name.startsWith("@db."));
|
|
2356
|
+
if (dbAttr) {
|
|
2357
|
+
return {
|
|
2358
|
+
sqlType: _PostgresCrudDialect.dbAttributeToSqlTypeMap[dbAttr.name],
|
|
2359
|
+
hasDbOverride: true
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
return {
|
|
2363
|
+
sqlType: this.getSqlType(fieldDef.type),
|
|
2364
|
+
hasDbOverride: false
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
buildComparison(left, leftFieldDef, op, right, rightFieldDef) {
|
|
2368
|
+
const leftResolved = this.resolveFieldSqlType(leftFieldDef);
|
|
2369
|
+
const rightResolved = this.resolveFieldSqlType(rightFieldDef);
|
|
2370
|
+
if (leftResolved.sqlType !== rightResolved.sqlType && (leftResolved.hasDbOverride || rightResolved.hasDbOverride)) {
|
|
2371
|
+
if (leftResolved.hasDbOverride) {
|
|
2372
|
+
left = this.eb.cast(left, sql3.raw(this.getSqlType(leftFieldDef.type)));
|
|
2373
|
+
}
|
|
2374
|
+
if (rightResolved.hasDbOverride) {
|
|
2375
|
+
right = this.eb.cast(right, sql3.raw(this.getSqlType(rightFieldDef.type)));
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
return super.buildComparison(left, leftFieldDef, op, right, rightFieldDef);
|
|
2379
|
+
}
|
|
2257
2380
|
getStringCasingBehavior() {
|
|
2258
2381
|
return {
|
|
2259
2382
|
supportsILike: true,
|
|
@@ -2275,7 +2398,7 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
|
|
|
2275
2398
|
}
|
|
2276
2399
|
const eb = expressionBuilder3();
|
|
2277
2400
|
return eb.selectFrom(sql3`(VALUES ${sql3.join(rows.map((row) => sql3`(${sql3.join(row.map((v) => sql3.val(v)))})`), sql3.raw(", "))})`.as("$values")).select(fields.map((f, i) => {
|
|
2278
|
-
const mappedType = this.getSqlType(f.type);
|
|
2401
|
+
const mappedType = this.getSqlType(f.type, f.attributes);
|
|
2279
2402
|
const castType = f.array ? sql3`${sql3.raw(mappedType)}[]` : sql3.raw(mappedType);
|
|
2280
2403
|
return this.eb.cast(sql3.ref(`$values.column${i + 1}`), castType).as(f.name);
|
|
2281
2404
|
}));
|