slonik-trpc
Version:
Slonik tRPC loader
567 lines (566 loc) • 39.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeQueryLoader = void 0;
const slonik_1 = require("slonik");
const zod_1 = require("zod");
const zod_2 = require("../helpers/zod");
const cursors_1 = require("../helpers/cursors");
const buildView_1 = require("./buildView");
const orderDirection = zod_1.z.enum(["ASC", "DESC"]);
function isBasicSortFieldOption(field) {
var _a, _b;
return !((_b = (_a = field) === null || _a === void 0 ? void 0 : _a.field) === null || _b === void 0 ? void 0 : _b.sql);
}
function getOrderFieldNullsLast(field) {
return isBasicSortFieldOption(field) ? false : !!(field === null || field === void 0 ? void 0 : field.nullsLast);
}
function getOrderFieldNullable(field) {
return isBasicSortFieldOption(field) ? false : !!(field === null || field === void 0 ? void 0 : field.nullable);
}
function getOrderByDirection(field, reverse) {
if (getOrderFieldNullsLast(field[0])) {
switch (field[1]) {
case 'ASC': return reverse ? slonik_1.sql.fragment `DESC NULLS FIRST` : slonik_1.sql.fragment `ASC NULLS LAST`;
case 'DESC': return reverse ? slonik_1.sql.fragment `ASC NULLS FIRST` : slonik_1.sql.fragment `DESC NULLS LAST`;
}
}
switch (field[1]) {
case 'ASC': return reverse ? slonik_1.sql.fragment `DESC` : slonik_1.sql.fragment `ASC`;
case 'DESC': return reverse ? slonik_1.sql.fragment `ASC` : slonik_1.sql.fragment `DESC`;
}
}
function interpretFieldFragment(field) {
if (typeof field === 'string' || Array.isArray(field)) {
return slonik_1.sql.fragment `${slonik_1.sql.identifier(Array.isArray(field) ? field : [field])}`;
}
return isBasicSortFieldOption(field) ? field : field.field;
}
function interpretOrderBy(field, reverse) {
return slonik_1.sql.fragment `${interpretFieldFragment(field[0])} ${getOrderByDirection(field, reverse)}`;
}
const countQueryType = zod_1.z.object({
count: zod_1.z.number(),
});
function getSelectedKeys(allKeys, selected) {
const noneSelected = !(selected === null || selected === void 0 ? void 0 : selected.length);
if (!noneSelected) {
return allKeys.filter(key => selected.includes(key));
}
else {
return allKeys;
}
}
function makeQueryLoader(options) {
var _a, _b, _c, _d, _e, _f, _g;
const queryComponents = options.query;
const query = queryComponents.select;
let view = queryComponents.view;
const fromFragment = queryComponents.from || (view === null || view === void 0 ? void 0 : view.getFromFragment({}));
if (((_a = options.filters) === null || _a === void 0 ? void 0 : _a.interpreters) && !view && fromFragment) {
// backwards compatible if only filters are specified
view = (0, buildView_1.buildView) `${fromFragment}`
.addFilters(options.filters.interpreters);
}
else if (((_b = options.filters) === null || _b === void 0 ? void 0 : _b.interpreters) && view) {
// Add filters to existing view if both are specified
view = view.addFilters(options.filters.interpreters);
}
if (query.sql.match(/;\s*$/)) {
// TODO: Add more checks for invalid queries
console.warn("Your query includes semicolons at the end. Please refer to the documentation of slonik-trpc, and do not include semicolons in the query:\n " + query.sql);
}
if (!fromFragment) {
console.warn("Deprecation warning: Specify query.from and query.select separately in makeQueryLoader", query === null || query === void 0 ? void 0 : query.sql);
}
if (fromFragment && ((_c = fromFragment.sql) === null || _c === void 0 ? void 0 : _c.length) > 5 && !((_e = (_d = fromFragment === null || fromFragment === void 0 ? void 0 : fromFragment.sql) === null || _d === void 0 ? void 0 : _d.match) === null || _e === void 0 ? void 0 : _e.call(_d, /^\s*FROM/i))) {
throw new Error("query.from must begin with FROM");
}
if (!((_g = (_f = query === null || query === void 0 ? void 0 : query.sql) === null || _f === void 0 ? void 0 : _f.match) === null || _g === void 0 ? void 0 : _g.call(_f, /^\s*(SELECT|WITH)/i))) {
throw new Error("Your query must begin with SELECT or WITH");
}
const type = options.type || query.parser;
// @ts-expect-error accessing internal _any
const isAnyType = type._any === true;
if (!isAnyType && (!type || !type.keyof || !type.partial))
throw new Error('Invalid query type provided: ' + (type));
const interpretFilters = view ? view.getWhereConditions : null;
const sortableAliases = Object.keys((options === null || options === void 0 ? void 0 : options.sortableColumns) || {});
const sortFields = sortableAliases.length
? zod_1.z.enum(sortableAliases)
: // If unspecified, no field is allowed to be used for sorting
zod_1.z.never();
const orderByWithoutTransform = zod_1.z.tuple([sortFields, orderDirection]);
const cursorColumns = "cursorcolumns";
const sortFieldWithTransform = sortFields.transform(field => {
var _a;
return ((_a = options.sortableColumns) === null || _a === void 0 ? void 0 : _a[field]) || field;
});
const orderByType = zod_1.z.tuple([sortFieldWithTransform, orderDirection]);
const mapTransformRows = (rows, args) => __awaiter(this, void 0, void 0, function* () {
var _h;
if (!rows.length)
return rows;
if (options.virtualFields) {
const keys = Object.keys(options.virtualFields);
const selected = getSelectedKeys(keys, args.select);
if (selected.length) {
const { default: Nativebird } = yield Promise.resolve().then(() => __importStar(require('nativebird')));
yield Promise.all(selected.map((key) => __awaiter(this, void 0, void 0, function* () {
var _j, _k, _l, _m, _o;
const remoteLoad = yield ((_l = (_k = (_j = options.virtualFields) === null || _j === void 0 ? void 0 : _j[key]) === null || _k === void 0 ? void 0 : _k.load) === null || _l === void 0 ? void 0 : _l.call(_k, rows, args));
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const firstResolve = options.virtualFields[key].resolve(rows[0], Object.assign(Object.assign({}, args), { index: 0 }), remoteLoad);
if (typeof ((_m = firstResolve) === null || _m === void 0 ? void 0 : _m.then) === 'function') {
yield Nativebird.map(rows.slice(1), (row, index) => __awaiter(this, void 0, void 0, function* () {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
row[key] = yield options.virtualFields[key].resolve(row, Object.assign(Object.assign({}, args), { index }), remoteLoad);
}), { concurrency: ((_o = options === null || options === void 0 ? void 0 : options.options) === null || _o === void 0 ? void 0 : _o.runConcurrency) || 50 });
rows[0][key] = yield firstResolve;
}
else {
rows[0][key] = firstResolve;
let index = 0;
for (const row of rows.slice(1)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
row[key] = options.virtualFields[key].resolve(row, Object.assign(Object.assign({}, args), { index: ++index }), remoteLoad);
}
}
})));
}
}
if ((_h = options === null || options === void 0 ? void 0 : options.options) === null || _h === void 0 ? void 0 : _h.runtimeCheck) {
const zodType = type.partial();
return rows.map(row => zodType.parse(row));
}
else {
return rows;
}
});
const getQuery = (allArgs) => __awaiter(this, void 0, void 0, function* () {
var _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
let { take, select, orderBy, } = allArgs;
const { where, skip, distinctOn, ctx, cursor, takeCursors, searchAfter, selectGroups, } = allArgs;
const context = ctx && ((_p = options.contextParser) === null || _p === void 0 ? void 0 : _p.parse) ? options.contextParser.parse(ctx) : ctx;
const filtersCondition = yield (interpretFilters === null || interpretFilters === void 0 ? void 0 : interpretFilters({
where: where || {},
ctx: context,
options: {
orEnabled: !!((_q = options === null || options === void 0 ? void 0 : options.options) === null || _q === void 0 ? void 0 : _q.orFilterEnabled),
}
}));
// Allows easier usage of cursor pagination with default sorting
if (!(orderBy === null || orderBy === void 0 ? void 0 : orderBy.length) && ((_s = (_r = options.defaults) === null || _r === void 0 ? void 0 : _r.orderBy) === null || _s === void 0 ? void 0 : _s.length)) {
orderBy = options.defaults.orderBy;
}
const authConditions = yield ((_t = options === null || options === void 0 ? void 0 : options.constraints) === null || _t === void 0 ? void 0 : _t.call(options, context));
const auth = Array.isArray(authConditions) ? authConditions : [authConditions].filter(zod_2.notEmpty);
const allConditions = [...auth, ...(filtersCondition || [])].filter(zod_2.notEmpty);
const whereCondition = allConditions.length ? slonik_1.sql.fragment `(${slonik_1.sql.join(allConditions, slonik_1.sql.fragment `) AND (`)})` : slonik_1.sql.fragment `TRUE`;
let actualQuery = query;
if (selectGroups === null || selectGroups === void 0 ? void 0 : selectGroups.length) {
const groupFields = selectGroups.flatMap(group => { var _a; return ((_a = options.columnGroups) === null || _a === void 0 ? void 0 : _a[group]) || []; });
select = (select || []).concat(...groupFields);
}
const isPartial = (select === null || select === void 0 ? void 0 : select.length) || (selectGroups === null || selectGroups === void 0 ? void 0 : selectGroups.length);
const reverse = !!take && take < 0;
if (take && take < 0)
take = -take;
if (reverse && !(orderBy === null || orderBy === void 0 ? void 0 : orderBy.length)) {
throw new Error("orderBy must be specified when take parameter is negative!");
}
const noneSelected = !(select === null || select === void 0 ? void 0 : select.length);
const distinctFields = Array.isArray(distinctOn) ? distinctOn === null || distinctOn === void 0 ? void 0 : distinctOn.map(distinct => sortFields.parse(distinct)) :
(distinctOn === null || distinctOn === void 0 ? void 0 : distinctOn.length) ? [sortFields.parse(distinctOn)] : null;
const orderExpressions = Array.isArray(orderBy === null || orderBy === void 0 ? void 0 : orderBy[0]) ? orderBy === null || orderBy === void 0 ? void 0 : orderBy.map(order => orderByWithoutTransform.parse(order)) :
(orderBy === null || orderBy === void 0 ? void 0 : orderBy.length) ? [orderByWithoutTransform.parse(orderBy)] : (distinctFields === null || distinctFields === void 0 ? void 0 : distinctFields.length) ? [] : null;
if ((distinctFields === null || distinctFields === void 0 ? void 0 : distinctFields.length) && !((_u = options === null || options === void 0 ? void 0 : options.options) === null || _u === void 0 ? void 0 : _u.useSqlite)) {
const distinctExpressions = distinctFields.map(field => interpretFieldFragment(sortFieldWithTransform.parse(field)));
const distinctQuery = slonik_1.sql.fragment `SELECT DISTINCT ON (${slonik_1.sql.join(distinctExpressions, slonik_1.sql.fragment `, `)})`;
// Hacky way to add DISTINCT ON (must be done towards the end...)
actualQuery = Object.assign(Object.assign({}, actualQuery), { sql: query.sql.replace(/^\n*(\W\n?)*SELECT(\s*DISTINCT)?/i, distinctQuery.sql) });
for (const expression of distinctFields.reverse()) {
// Reverse to make sure unshift is done in the correct order
const orderPosition = orderExpressions === null || orderExpressions === void 0 ? void 0 : orderExpressions.findIndex(([field]) => field === expression);
if (typeof orderPosition === 'number' && orderPosition >= 0) {
orderExpressions === null || orderExpressions === void 0 ? void 0 : orderExpressions.unshift((_v = orderExpressions === null || orderExpressions === void 0 ? void 0 : orderExpressions.splice(orderPosition, 1)) === null || _v === void 0 ? void 0 : _v[0]);
}
else {
orderExpressions === null || orderExpressions === void 0 ? void 0 : orderExpressions.unshift([expression, 'ASC']);
}
}
}
const conditions = [whereCondition].filter(zod_2.notEmpty);
const cursorsEnabled = takeCursors && (orderExpressions === null || orderExpressions === void 0 ? void 0 : orderExpressions.length);
const zodType = isAnyType ? type : cursorsEnabled ? (isPartial ? type.partial() : type).merge(zod_1.z.object({
[cursorColumns]: zod_1.z.any(),
})) : (isPartial ? type.partial() : type);
const fields = Object.keys(((_x = (_w = type === null || type === void 0 ? void 0 : type.keyof) === null || _w === void 0 ? void 0 : _w.call(type)) === null || _x === void 0 ? void 0 : _x.Values) || {});
const selectable = options === null || options === void 0 ? void 0 : options.selectableColumns;
select = (select || [])
.filter(field => !(selectable === null || selectable === void 0 ? void 0 : selectable.length) || selectable.indexOf(field) >= 0)
// Add dependencies from selected fields.
.flatMap((field) => {
var _a, _b;
return [
field,
...(((_b = (_a = options === null || options === void 0 ? void 0 : options.virtualFields) === null || _a === void 0 ? void 0 : _a[field]) === null || _b === void 0 ? void 0 : _b.dependencies) || []),
];
})
.filter((field) => !(fields === null || fields === void 0 ? void 0 : fields.length) || (fields === null || fields === void 0 ? void 0 : fields.indexOf(field)) >= 0);
const hasTransformColumns = typeof ((_y = options === null || options === void 0 ? void 0 : options.options) === null || _y === void 0 ? void 0 : _y.transformColumns) === 'function';
if (hasTransformColumns) {
select = select.map((field) => { var _a, _b, _c; return (_c = (_b = (_a = options === null || options === void 0 ? void 0 : options.options) === null || _a === void 0 ? void 0 : _a.transformColumns) === null || _b === void 0 ? void 0 : _b.call(_a, field)) !== null && _c !== void 0 ? _c : field; });
}
const sqlFields = hasTransformColumns ? fields.map(field => { var _a, _b, _c; return (_c = (_b = (_a = options === null || options === void 0 ? void 0 : options.options) === null || _a === void 0 ? void 0 : _a.transformColumns) === null || _b === void 0 ? void 0 : _b.call(_a, field)) !== null && _c !== void 0 ? _c : field; }) : fields;
const finalKeys = Array.from(new Set(sqlFields
.filter(zod_2.notEmpty)
.filter((column) => noneSelected || (select === null || select === void 0 ? void 0 : select.includes(column)))).values()).map(a => slonik_1.sql.identifier([a]));
const lateralExpressions = [];
const extraSelects = [];
if (takeCursors && (orderExpressions === null || orderExpressions === void 0 ? void 0 : orderExpressions.length)) {
if ((_z = options.options) === null || _z === void 0 ? void 0 : _z.useSqlite) {
finalKeys.push(slonik_1.sql.fragment `json_object(${orderExpressions.length
? slonik_1.sql.join(orderExpressions.flatMap((expression) => [slonik_1.sql.literalValue(expression[0]), interpretFieldFragment(orderByType.parse(expression)[0])]), slonik_1.sql.fragment `,`)
: slonik_1.sql.fragment ``}) ${slonik_1.sql.identifier([cursorColumns])}`);
}
else {
if (!query.sql.includes("lateralcolumns.cursorjson")) {
// Hacky way to get access to internal FROM tables for sorting expressions...
extraSelects.push(slonik_1.sql.fragment `lateralcolumns.cursorjson ${slonik_1.sql.identifier([cursorColumns])}`);
}
lateralExpressions.push(slonik_1.sql.fragment `jsonb_build_object(${orderExpressions.length
? slonik_1.sql.join(orderExpressions.flatMap((expression) => [slonik_1.sql.literalValue(expression[0]), interpretFieldFragment(orderByType.parse(expression)[0])]), slonik_1.sql.fragment `,`)
: slonik_1.sql.fragment ``}) cursorjson`);
}
}
if ((searchAfter || cursor) && (orderExpressions === null || orderExpressions === void 0 ? void 0 : orderExpressions.length)) {
const orderByExpressions = orderExpressions.map(parsed => [interpretFieldFragment(orderByType.parse(parsed)[0]), parsed[1], parsed[0], parsed]);
const cursorValues = cursor ? (0, cursors_1.fromCursor)(cursor) : {};
conditions.push(slonik_1.sql.fragment `(${slonik_1.sql.join(orderByExpressions.map((_, outerIndex) => {
const expressions = orderByExpressions.slice(0, outerIndex + 1);
return slonik_1.sql.fragment `(${slonik_1.sql.join(expressions.map(([expression, direction, columnAlias], innerIndex) => {
var _a;
let operator = slonik_1.sql.fragment `=`;
let nullFragment = slonik_1.sql.fragment `TRUE`;
const orderField = (_a = options.sortableColumns) === null || _a === void 0 ? void 0 : _a[columnAlias];
const value = searchAfter ? searchAfter[columnAlias] : cursorValues[columnAlias];
const isNullable = getOrderFieldNullable(orderField) || value === null;
const nullsLast = getOrderFieldNullsLast(orderField);
let isNull = false;
const ascending = direction === (reverse ? "DESC" : "ASC");
if (innerIndex === expressions.length - 1) {
operator = ascending ? slonik_1.sql.fragment `>` : slonik_1.sql.fragment `<`;
if (isNullable) {
nullFragment = ascending || nullsLast ? slonik_1.sql.fragment `${expression} IS NULL`
: slonik_1.sql.fragment `${expression} IS NOT NULL`;
isNull = ascending || nullsLast;
}
}
else if (value === null) {
nullFragment = ascending !== nullsLast ? slonik_1.sql.fragment `${expression} IS NOT NULL`
: slonik_1.sql.fragment `${expression} IS NULL`;
isNull = ascending === nullsLast;
}
return value !== null && value !== undefined ?
isNullable && isNull ? slonik_1.sql.fragment `(${expression} ${operator} ${value} OR ${nullFragment})`
: slonik_1.sql.fragment `${expression} ${operator} ${value}`
: nullFragment;
}), slonik_1.sql.fragment ` AND `)})`;
}), slonik_1.sql.fragment ` OR `)})`);
}
const groupExpression = (queryComponents.groupBy) ? [
...(typeof queryComponents.groupBy === 'function' ? [queryComponents.groupBy(allArgs)] : []),
...(((_0 = queryComponents.groupBy) === null || _0 === void 0 ? void 0 : _0.sql) ? [queryComponents.groupBy] : []),
lateralExpressions[0] ? slonik_1.sql.fragment `lateralcolumns.cursorjson` : null
].filter(zod_2.notEmpty) : [];
const extraSelectFields = extraSelects.length ? slonik_1.sql.fragment `, ${slonik_1.sql.join(extraSelects, slonik_1.sql.fragment `, `)}` : slonik_1.sql.fragment ``;
const realFromFragment = view ? view.getFromFragment(context) : fromFragment;
const baseQuery = slonik_1.sql.type(zodType) `${actualQuery} ${extraSelectFields} ${realFromFragment !== null && realFromFragment !== void 0 ? realFromFragment : slonik_1.sql.fragment ``} ${lateralExpressions[0] ? slonik_1.sql.fragment `, LATERAL (SELECT ${slonik_1.sql.join(lateralExpressions, slonik_1.sql.fragment `, `)}) lateralcolumns` : slonik_1.sql.fragment ``}
${conditions.length ? slonik_1.sql.fragment `WHERE (${slonik_1.sql.join(conditions, slonik_1.sql.fragment `)\n AND (`)})` : slonik_1.sql.fragment ``}
${(groupExpression === null || groupExpression === void 0 ? void 0 : groupExpression.length) ? slonik_1.sql.fragment `GROUP BY ${slonik_1.sql.join(groupExpression, slonik_1.sql.fragment `, `)}` : slonik_1.sql.fragment ``}
${orderExpressions ? slonik_1.sql.fragment `ORDER BY ${slonik_1.sql.join(orderExpressions.map(parsed => interpretOrderBy(orderByType.parse(parsed), reverse)), slonik_1.sql.fragment `, `)}` : slonik_1.sql.fragment ``}
${typeof take === 'number' ? slonik_1.sql.fragment `LIMIT ${take}` : slonik_1.sql.fragment ``}
${typeof skip === 'number' ? slonik_1.sql.fragment `OFFSET ${skip}` : slonik_1.sql.fragment ``}
`;
if (!(select === null || select === void 0 ? void 0 : select.length) && !(selectGroups === null || selectGroups === void 0 ? void 0 : selectGroups.length) && !fields.length) {
finalKeys.push(slonik_1.sql.fragment `*`);
}
else if (takeCursors && (lateralExpressions === null || lateralExpressions === void 0 ? void 0 : lateralExpressions.length)) {
finalKeys.push(slonik_1.sql.identifier(["root_query", cursorColumns]));
}
// Run another root query, that only selects the column names that aren't excluded, or only ones that are included.
const finalQuery = slonik_1.sql.type(zodType) `WITH root_query AS (${baseQuery})
SELECT ${slonik_1.sql.join(finalKeys, slonik_1.sql.fragment `, `)} FROM root_query`;
for (const plugin of (options.plugins || [])) {
if (plugin.onGetQuery) {
plugin.onGetQuery({
args: Object.assign(Object.assign({}, allArgs), { take,
select }),
query: finalQuery,
});
}
}
return finalQuery;
});
const getSelectableFields = () => {
var _a, _b;
const selectable = options === null || options === void 0 ? void 0 : options.selectableColumns;
const columns = Object.keys((options === null || options === void 0 ? void 0 : options.virtualFields) || {}).concat(Object.keys(((_b = (_a = type === null || type === void 0 ? void 0 : type.keyof) === null || _a === void 0 ? void 0 : _a.call(type)) === null || _b === void 0 ? void 0 : _b.Values) || {}));
if (selectable === null || selectable === void 0 ? void 0 : selectable.length) {
return columns.filter(column => selectable.indexOf(column) >= 0);
}
return columns;
};
const getLoadArgs = ({ sortableColumns = Object.keys((options === null || options === void 0 ? void 0 : options.sortableColumns) || {}), selectableColumns = options === null || options === void 0 ? void 0 : options.selectableColumns, disabledFilters, transformSortColumns, } = {}) => {
var _a, _b, _c;
const sortFields = (sortableColumns === null || sortableColumns === void 0 ? void 0 : sortableColumns.length)
? zod_1.z.enum(sortableColumns)
: // If unspecified, no field is allowed to be used for sorting
zod_1.z.never();
const orderTuple = zod_1.z.tuple([sortFields, orderDirection]);
const selectColumns = (selectableColumns || options.selectableColumns);
const fields = (selectColumns === null || selectColumns === void 0 ? void 0 : selectColumns.length)
? zod_1.z.enum(selectColumns)
: // If unspecified, any field is allowed to be selected
zod_1.z.string();
const groups = Object.keys(options.columnGroups || {});
const groupsEnum = groups.length ? zod_1.z.enum(groups) : zod_1.z.never();
const orderUnion = zod_1.z.union([zod_1.z.array(orderTuple), orderTuple]).nullish();
const orderBy = zod_1.z.preprocess((typeof transformSortColumns === 'function' ? (columns => {
if (Array.isArray(columns)) {
if (Array.isArray(columns[0]) || columns[0] === undefined) {
return transformSortColumns(columns) || columns;
}
else {
return transformSortColumns([columns].filter(zod_2.notEmpty)) || columns;
}
}
}) : (a) => (Array.isArray(a) && (Array.isArray(a[0]) || a[0] === undefined) ? a : [a].filter(zod_2.notEmpty))), orderUnion);
const filterKeys = Object.keys(((_a = options.query.view) === null || _a === void 0 ? void 0 : _a.getFilters()) || {})
.concat(Object.keys(((_b = options === null || options === void 0 ? void 0 : options.filters) === null || _b === void 0 ? void 0 : _b.filters) || {}));
const hasFilters = !!filterKeys.length;
const filterType = zod_1.z.lazy(() => {
var _a;
return zod_1.z.object(Object.assign(Object.assign(Object.assign(Object.assign({}, (filterKeys.reduce((acc, key) => {
var _a, _b;
acc[key] = ((_b = (_a = options === null || options === void 0 ? void 0 : options.filters) === null || _a === void 0 ? void 0 : _a.filters) === null || _b === void 0 ? void 0 : _b[key]) || zod_1.z.any();
return acc;
}, {}))), (hasFilters && !(disabledFilters === null || disabledFilters === void 0 ? void 0 : disabledFilters.OR) && ((_a = options === null || options === void 0 ? void 0 : options.options) === null || _a === void 0 ? void 0 : _a.orFilterEnabled) && { OR: zod_1.z.array(filterType) })), (hasFilters && !(disabledFilters === null || disabledFilters === void 0 ? void 0 : disabledFilters.AND) && { AND: zod_1.z.array(filterType) })), (hasFilters && !(disabledFilters === null || disabledFilters === void 0 ? void 0 : disabledFilters.NOT) && { NOT: filterType }))).partial();
}).nullish();
return zod_1.z.object({
/** The fields that should be included. If unspecified, all fields are returned. */
select: zod_1.z.array(fields).optional(),
take: zod_1.z.number().optional(),
skip: zod_1.z.number().optional().default(0),
takeCount: zod_1.z.boolean().optional(),
takeCursors: zod_1.z.boolean().optional(),
cursor: zod_1.z.string().optional(),
takeNextPages: zod_1.z.number().optional(),
selectGroups: zod_1.z.array(groupsEnum).optional(),
searchAfter: sortableColumns.length ? zod_1.z.object(sortableColumns.reduce((acc, column) => {
acc[column] = zod_1.z.any();
return acc;
}, {})).partial().optional() : zod_1.z.null(),
orderBy: ((_c = options === null || options === void 0 ? void 0 : options.defaults) === null || _c === void 0 ? void 0 : _c.orderBy) ?
orderBy.default(options.defaults.orderBy) :
orderBy,
where: filterType,
}).partial();
};
const self = {
_columnGroups: options.columnGroups,
getSelectableFields,
getLoadArgs,
getQuery,
// By default, select all fields (string covers all), and don't exclude any fields
load(args, database) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
if ((_a = args.selectGroups) === null || _a === void 0 ? void 0 : _a.length) {
const groupFields = args.selectGroups.flatMap(group => { var _a; return ((_a = options.columnGroups) === null || _a === void 0 ? void 0 : _a[group]) || []; });
args.select = (args.select || []).concat(...groupFields);
}
if (typeof options.contextFactory === 'function') {
args.ctx = options.contextFactory(args.ctx);
}
const db = database || (options === null || options === void 0 ? void 0 : options.db);
const reverse = !!args.take && args.take < 0;
if (!(db === null || db === void 0 ? void 0 : db.any))
throw new Error("Database not provided");
const finalQuery = yield getQuery(Object.assign(Object.assign({}, args), { take: typeof args.take === 'number' ? args.take : ((_b = options === null || options === void 0 ? void 0 : options.defaults) === null || _b === void 0 ? void 0 : _b.take), takeCursors: false }));
let result = null;
const onLoadOptions = {
args,
query: finalQuery,
setResultAndStopExecution(newResult) {
result = newResult;
},
};
const afterCalls = [];
for (const plugin of options.plugins || []) {
if (plugin.onLoad) {
const done = plugin.onLoad(onLoadOptions);
if (done === null || done === void 0 ? void 0 : done.onLoadDone) {
afterCalls.push(done.onLoadDone);
}
if (result)
break;
}
}
const load = () => db.any(finalQuery).then((rows) => __awaiter(this, void 0, void 0, function* () {
return mapTransformRows(reverse ? rows.reverse() : rows, args);
})).then((rows) => __awaiter(this, void 0, void 0, function* () {
// Call the onLoadDone method of each plugin
for (const onLoadDone of afterCalls) {
onLoadDone({
result: rows,
setResult: (newResult) => { result = newResult; },
});
}
if (result)
return result;
return rows;
}));
if (result)
return result;
return load();
});
},
/**
* Returns the data in a pagination-convenient form.
* Specify takeCount: true to query the overall count as if no limit had been specified.
* Otherwise, count will be null.
* `take` is limited to 1000 items when using loadPagination, as it's meant to be used for loading only a few pages at a time.
* */
loadPagination(args, database) {
var _a, _b, _c, _d, _e, _f;
return __awaiter(this, void 0, void 0, function* () {
if ((_a = args.selectGroups) === null || _a === void 0 ? void 0 : _a.length) {
const groupFields = args.selectGroups.flatMap(group => { var _a; return ((_a = options.columnGroups) === null || _a === void 0 ? void 0 : _a[group]) || []; });
args.select = (args.select || []).concat(...groupFields);
}
if (typeof options.contextFactory === 'function') {
args.ctx = options.contextFactory(args.ctx);
}
if (typeof args.take !== 'number' && ((_b = options === null || options === void 0 ? void 0 : options.defaults) === null || _b === void 0 ? void 0 : _b.take)) {
args.take = options.defaults.take;
}
const allArgs = args;
const db = database || (options === null || options === void 0 ? void 0 : options.db);
if (!(db === null || db === void 0 ? void 0 : db.any))
throw new Error("Database not provided");
const reverse = !!args.take && args.take < 0 ? -1 : 1;
const take = (typeof args.take === 'number' && args.take < 0) ? -args.take : args.take;
if (reverse === -1 && !((_c = allArgs.orderBy) === null || _c === void 0 ? void 0 : _c.length) && !((_e = (_d = options === null || options === void 0 ? void 0 : options.defaults) === null || _d === void 0 ? void 0 : _d.orderBy) === null || _e === void 0 ? void 0 : _e.length)) {
throw new Error("orderBy must be specified when take parameter is negative!");
}
const extraItems = Math.max(Math.min(3, ((args === null || args === void 0 ? void 0 : args.takeNextPages) || 0) - 1), 0) * (take !== null && take !== void 0 ? take : 25) + 1;
const finalQuery = yield getQuery(Object.assign(Object.assign({}, args), { take: // Query an extra row to see if the next page exists
(Math.min(Math.max(0, take !== null && take !== void 0 ? take : 100), (((_f = options.options) === null || _f === void 0 ? void 0 : _f.maxLimit) || 1000)) + extraItems) * reverse }));
const countQuery = args.takeCount ? slonik_1.sql.type(countQueryType) `SELECT COUNT(*) FROM (${yield getQuery(Object.assign(Object.assign({}, args), { skip: undefined, searchAfter: undefined, cursor: undefined, takeCursors: false, take: undefined }))}) allrows` : null;
// Count is null by default
let countPromise = Promise.resolve(null);
let countSet = false;
let result = null;
const afterCalls = [];
for (const plugin of options.plugins || []) {
if (plugin.onLoadPagination) {
const done = plugin.onLoadPagination({
args,
query: finalQuery,
countQuery,
setCount(newCount) {
countPromise = Promise.resolve(newCount);
countSet = true;
},
setResultAndStopExecution(newResult) {
result = newResult;
},
});
if (done === null || done === void 0 ? void 0 : done.onLoadDone) {
afterCalls.push(done.onLoadDone);
}
if (result)
break;
}
}
const load = () => db
.any(finalQuery)
.then((nodes) => __awaiter(this, void 0, void 0, function* () {
var _g, _h;
const slicedNodes = nodes.slice(0, take !== null && take !== void 0 ? take : undefined);
const rows = reverse === -1 ? slicedNodes.reverse() : slicedNodes;
const cursors = allArgs.takeCursors && {
startCursor: (0, cursors_1.toCursor)((_g = rows[0]) === null || _g === void 0 ? void 0 : _g[cursorColumns]),
endCursor: (0, cursors_1.toCursor)((_h = rows[rows.length - 1]) === null || _h === void 0 ? void 0 : _h[cursorColumns]),
cursors: rows.map((row) => {
if (row[cursorColumns]) {
const cursorcolumns = row[cursorColumns];
delete row[cursorColumns];
return (0, cursors_1.toCursor)(cursorcolumns);
}
}),
};
const hasMore = nodes.length > slicedNodes.length;
const hasPrevious = !!args.skip || (!!allArgs.cursor || !!allArgs.searchAfter);
const allRows = yield mapTransformRows(rows, Object.assign(Object.assign({}, args.ctx), args));
return Object.assign(Object.assign({ nodes: allRows }, (cursors && {
cursors: cursors.cursors
})), { pageInfo: Object.assign(Object.assign({ hasPreviousPage: reverse === -1 ? hasMore : hasPrevious, hasNextPage: reverse === -1 ? hasPrevious : hasMore, minimumCount: (args.skip || 0) + nodes.length }, (cursors && {
startCursor: cursors.startCursor,
endCursor: cursors.endCursor,
})), { count: yield countPromise }) });
})).then((rows) => {
// Call the onLoadDone method of each plugin
for (const onLoadDone of afterCalls) {
onLoadDone({
result: rows,
setResult: (newResult) => { result = newResult; },
});
}
if (result)
return result;
return rows;
});
if (result)
return result;
if (args.takeCount && !countSet) {
countPromise = db
.any(countQuery)
.then((res) => { var _a; return (_a = res === null || res === void 0 ? void 0 : res[0]) === null || _a === void 0 ? void 0 : _a.count; });
}
return load();
});
},
};
return self;
}
exports.makeQueryLoader = makeQueryLoader;