@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.cjs
CHANGED
|
@@ -1643,6 +1643,12 @@ var BaseCrudDialect = class {
|
|
|
1643
1643
|
buildExistsExpression(innerQuery) {
|
|
1644
1644
|
return this.eb.exists(innerQuery);
|
|
1645
1645
|
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Builds a binary comparison expression between two operands.
|
|
1648
|
+
*/
|
|
1649
|
+
buildComparison(left, _leftFieldDef, op, right, _rightFieldDef) {
|
|
1650
|
+
return this.eb(left, op, right);
|
|
1651
|
+
}
|
|
1646
1652
|
};
|
|
1647
1653
|
|
|
1648
1654
|
// src/client/crud/dialects/lateral-join-dialect-base.ts
|
|
@@ -1698,13 +1704,53 @@ var LateralJoinDialectBase = class extends BaseCrudDialect {
|
|
|
1698
1704
|
qb = qb.select((eb) => {
|
|
1699
1705
|
const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName);
|
|
1700
1706
|
if (relationFieldDef.array) {
|
|
1701
|
-
|
|
1707
|
+
const orderBy = this.buildRelationOrderByExpressions(relationModel, relationModelAlias, payload);
|
|
1708
|
+
return this.buildArrayAgg(this.buildJsonObject(objArgs), orderBy).as("$data");
|
|
1702
1709
|
} else {
|
|
1703
1710
|
return this.buildJsonObject(objArgs).as("$data");
|
|
1704
1711
|
}
|
|
1705
1712
|
});
|
|
1706
1713
|
return qb;
|
|
1707
1714
|
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Extracts scalar `orderBy` clauses from the relation payload and maps them to
|
|
1717
|
+
* the array-aggregation ordering format.
|
|
1718
|
+
*
|
|
1719
|
+
* For to-many relations aggregated into a JSON array (via lateral joins), this
|
|
1720
|
+
* lets us preserve a stable ordering by passing `{ expr, sort, nulls? }` into
|
|
1721
|
+
* the dialect's `buildArrayAgg` implementation.
|
|
1722
|
+
*/
|
|
1723
|
+
buildRelationOrderByExpressions(model, modelAlias, payload) {
|
|
1724
|
+
if (payload === true || !payload.orderBy) {
|
|
1725
|
+
return void 0;
|
|
1726
|
+
}
|
|
1727
|
+
const items = [];
|
|
1728
|
+
for (const orderBy of ensureArray(payload.orderBy)) {
|
|
1729
|
+
for (const [field, value] of Object.entries(orderBy)) {
|
|
1730
|
+
if (!value || requireField(this.schema, model, field).relation) {
|
|
1731
|
+
continue;
|
|
1732
|
+
}
|
|
1733
|
+
const expr = this.fieldRef(model, field, modelAlias);
|
|
1734
|
+
let sort = typeof value === "string" ? value : value.sort;
|
|
1735
|
+
if (payload.take !== void 0 && payload.take < 0) {
|
|
1736
|
+
sort = this.negateSort(sort, true);
|
|
1737
|
+
}
|
|
1738
|
+
if (typeof value === "string") {
|
|
1739
|
+
items.push({
|
|
1740
|
+
expr,
|
|
1741
|
+
sort
|
|
1742
|
+
});
|
|
1743
|
+
} else {
|
|
1744
|
+
items.push({
|
|
1745
|
+
expr,
|
|
1746
|
+
sort,
|
|
1747
|
+
nulls: value.nulls
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
return items.length > 0 ? items : void 0;
|
|
1753
|
+
}
|
|
1708
1754
|
buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName) {
|
|
1709
1755
|
const relationModelDef = requireModel(this.schema, relationModel);
|
|
1710
1756
|
const objArgs = {};
|
|
@@ -1890,7 +1936,7 @@ var MySqlCrudDialect = class extends LateralJoinDialectBase {
|
|
|
1890
1936
|
buildExistsExpression(innerQuery) {
|
|
1891
1937
|
return this.eb.exists(this.eb.selectFrom(innerQuery.as("$exists_sub")).select(this.eb.lit(1).as("_")));
|
|
1892
1938
|
}
|
|
1893
|
-
buildArrayAgg(arg) {
|
|
1939
|
+
buildArrayAgg(arg, _orderBy) {
|
|
1894
1940
|
return this.eb.fn.coalesce(import_kysely3.sql`JSON_ARRAYAGG(${arg})`, import_kysely3.sql`JSON_ARRAY()`);
|
|
1895
1941
|
}
|
|
1896
1942
|
buildSkipTake(query, skip, take) {
|
|
@@ -2037,6 +2083,33 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
|
|
|
2037
2083
|
Bytes: "bytea",
|
|
2038
2084
|
Json: "jsonb"
|
|
2039
2085
|
};
|
|
2086
|
+
// Maps @db.* attribute names to PostgreSQL SQL types for use in VALUES table casts
|
|
2087
|
+
static dbAttributeToSqlTypeMap = {
|
|
2088
|
+
"@db.Uuid": "uuid",
|
|
2089
|
+
"@db.Citext": "citext",
|
|
2090
|
+
"@db.Inet": "inet",
|
|
2091
|
+
"@db.Bit": "bit",
|
|
2092
|
+
"@db.VarBit": "varbit",
|
|
2093
|
+
"@db.Xml": "xml",
|
|
2094
|
+
"@db.Json": "json",
|
|
2095
|
+
"@db.JsonB": "jsonb",
|
|
2096
|
+
"@db.ByteA": "bytea",
|
|
2097
|
+
"@db.Text": "text",
|
|
2098
|
+
"@db.Char": "bpchar",
|
|
2099
|
+
"@db.VarChar": "varchar",
|
|
2100
|
+
"@db.Date": "date",
|
|
2101
|
+
"@db.Time": "time",
|
|
2102
|
+
"@db.Timetz": "timetz",
|
|
2103
|
+
"@db.Timestamp": "timestamp",
|
|
2104
|
+
"@db.Timestamptz": "timestamptz",
|
|
2105
|
+
"@db.SmallInt": "smallint",
|
|
2106
|
+
"@db.Integer": "integer",
|
|
2107
|
+
"@db.BigInt": "bigint",
|
|
2108
|
+
"@db.Real": "real",
|
|
2109
|
+
"@db.DoublePrecision": "double precision",
|
|
2110
|
+
"@db.Decimal": "decimal",
|
|
2111
|
+
"@db.Boolean": "boolean"
|
|
2112
|
+
};
|
|
2040
2113
|
constructor(schema, options) {
|
|
2041
2114
|
super(schema, options);
|
|
2042
2115
|
this.overrideTypeParsers();
|
|
@@ -2202,8 +2275,16 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
|
|
|
2202
2275
|
}
|
|
2203
2276
|
// #endregion
|
|
2204
2277
|
// #region other overrides
|
|
2205
|
-
buildArrayAgg(arg) {
|
|
2206
|
-
|
|
2278
|
+
buildArrayAgg(arg, orderBy) {
|
|
2279
|
+
if (!orderBy || orderBy.length === 0) {
|
|
2280
|
+
return this.eb.fn.coalesce(import_kysely4.sql`jsonb_agg(${arg})`, import_kysely4.sql`'[]'::jsonb`);
|
|
2281
|
+
}
|
|
2282
|
+
const orderBySql = import_kysely4.sql.join(orderBy.map(({ expr, sort, nulls }) => {
|
|
2283
|
+
const dir = import_kysely4.sql.raw(sort.toUpperCase());
|
|
2284
|
+
const nullsSql = nulls ? import_kysely4.sql` NULLS ${import_kysely4.sql.raw(nulls.toUpperCase())}` : import_kysely4.sql``;
|
|
2285
|
+
return import_kysely4.sql`${expr} ${dir}${nullsSql}`;
|
|
2286
|
+
}), import_kysely4.sql.raw(", "));
|
|
2287
|
+
return this.eb.fn.coalesce(import_kysely4.sql`jsonb_agg(${arg} ORDER BY ${orderBySql})`, import_kysely4.sql`'[]'::jsonb`);
|
|
2207
2288
|
}
|
|
2208
2289
|
buildSkipTake(query, skip, take) {
|
|
2209
2290
|
if (take !== void 0) {
|
|
@@ -2293,13 +2374,55 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
|
|
|
2293
2374
|
receiver
|
|
2294
2375
|
]).as("$items")).select(this.eb.lit(1).as("_")).where(buildFilter(this.eb.ref("$items.value"))));
|
|
2295
2376
|
}
|
|
2296
|
-
getSqlType(zmodelType) {
|
|
2377
|
+
getSqlType(zmodelType, attributes) {
|
|
2378
|
+
if (attributes) {
|
|
2379
|
+
for (const attr of attributes) {
|
|
2380
|
+
const mapped = _PostgresCrudDialect.dbAttributeToSqlTypeMap[attr.name];
|
|
2381
|
+
if (mapped) {
|
|
2382
|
+
return mapped;
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2297
2386
|
if (isEnum(this.schema, zmodelType)) {
|
|
2298
2387
|
return "text";
|
|
2299
2388
|
} else {
|
|
2300
2389
|
return this.zmodelToSqlTypeMap[zmodelType] ?? "text";
|
|
2301
2390
|
}
|
|
2302
2391
|
}
|
|
2392
|
+
// Resolves the effective SQL type for a field: the native type from any @db.* attribute,
|
|
2393
|
+
// or the base ZModel SQL type if no attribute is present, or undefined if the field is unknown.
|
|
2394
|
+
resolveFieldSqlType(fieldDef) {
|
|
2395
|
+
if (!fieldDef) {
|
|
2396
|
+
return {
|
|
2397
|
+
sqlType: void 0,
|
|
2398
|
+
hasDbOverride: false
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
const dbAttr = fieldDef.attributes?.find((a) => a.name.startsWith("@db."));
|
|
2402
|
+
if (dbAttr) {
|
|
2403
|
+
return {
|
|
2404
|
+
sqlType: _PostgresCrudDialect.dbAttributeToSqlTypeMap[dbAttr.name],
|
|
2405
|
+
hasDbOverride: true
|
|
2406
|
+
};
|
|
2407
|
+
}
|
|
2408
|
+
return {
|
|
2409
|
+
sqlType: this.getSqlType(fieldDef.type),
|
|
2410
|
+
hasDbOverride: false
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
buildComparison(left, leftFieldDef, op, right, rightFieldDef) {
|
|
2414
|
+
const leftResolved = this.resolveFieldSqlType(leftFieldDef);
|
|
2415
|
+
const rightResolved = this.resolveFieldSqlType(rightFieldDef);
|
|
2416
|
+
if (leftResolved.sqlType !== rightResolved.sqlType && (leftResolved.hasDbOverride || rightResolved.hasDbOverride)) {
|
|
2417
|
+
if (leftResolved.hasDbOverride) {
|
|
2418
|
+
left = this.eb.cast(left, import_kysely4.sql.raw(this.getSqlType(leftFieldDef.type)));
|
|
2419
|
+
}
|
|
2420
|
+
if (rightResolved.hasDbOverride) {
|
|
2421
|
+
right = this.eb.cast(right, import_kysely4.sql.raw(this.getSqlType(rightFieldDef.type)));
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
return super.buildComparison(left, leftFieldDef, op, right, rightFieldDef);
|
|
2425
|
+
}
|
|
2303
2426
|
getStringCasingBehavior() {
|
|
2304
2427
|
return {
|
|
2305
2428
|
supportsILike: true,
|
|
@@ -2321,7 +2444,7 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
|
|
|
2321
2444
|
}
|
|
2322
2445
|
const eb = (0, import_kysely4.expressionBuilder)();
|
|
2323
2446
|
return eb.selectFrom(import_kysely4.sql`(VALUES ${import_kysely4.sql.join(rows.map((row) => import_kysely4.sql`(${import_kysely4.sql.join(row.map((v) => import_kysely4.sql.val(v)))})`), import_kysely4.sql.raw(", "))})`.as("$values")).select(fields.map((f, i) => {
|
|
2324
|
-
const mappedType = this.getSqlType(f.type);
|
|
2447
|
+
const mappedType = this.getSqlType(f.type, f.attributes);
|
|
2325
2448
|
const castType = f.array ? import_kysely4.sql`${import_kysely4.sql.raw(mappedType)}[]` : import_kysely4.sql.raw(mappedType);
|
|
2326
2449
|
return this.eb.cast(import_kysely4.sql.ref(`$values.column${i + 1}`), castType).as(f.name);
|
|
2327
2450
|
}));
|