@teamkeel/functions-runtime 0.434.1 → 0.435.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1560,6 +1560,221 @@ var QueryBuilder = class _QueryBuilder {
1560
1560
  return rows.map((x) => transformRichDataTypes(camelCaseObject(x)));
1561
1561
  });
1562
1562
  }
1563
+ /**
1564
+ * Execute query with pagination support.
1565
+ * Returns results along with pageInfo metadata.
1566
+ * @param {Object} params - Pagination parameters
1567
+ * @param {number} [params.first] - Number of items for cursor pagination (forward)
1568
+ * @param {string} [params.after] - Cursor for forward pagination
1569
+ * @param {number} [params.last] - Number of items for cursor pagination (backward)
1570
+ * @param {string} [params.before] - Cursor for backward pagination
1571
+ * @param {number} [params.limit] - Page size for offset pagination
1572
+ * @param {number} [params.offset] - Offset for offset pagination
1573
+ * @param {Object} [params.orderBy] - Ordering specification
1574
+ * @returns {Promise<{results: any[], pageInfo: Object}>}
1575
+ */
1576
+ async findManyWithPageInfo(params = {}) {
1577
+ const name = spanNameForModelAPI(
1578
+ this._modelName,
1579
+ "findManyWithPageInfo"
1580
+ );
1581
+ const db = useDatabase();
1582
+ return withSpan(name, async (span) => {
1583
+ const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1584
+ const isOffsetPagination = params.limit != null && params.limit > 0;
1585
+ const isBackward = params.last != null && params.last > 0;
1586
+ const DEFAULT_PAGE_SIZE = 50;
1587
+ let limit;
1588
+ if (isOffsetPagination) {
1589
+ limit = params.limit;
1590
+ } else if (isBackward) {
1591
+ limit = params.last;
1592
+ } else {
1593
+ limit = params.first ?? DEFAULT_PAGE_SIZE;
1594
+ }
1595
+ const countQuery = db.selectFrom((qb) => this._db.as(this._tableName)).select(db.fn.countAll().as("total"));
1596
+ const countResult = await countQuery.executeTakeFirst();
1597
+ const totalCount = Number(countResult?.total ?? 0);
1598
+ let builder = db.selectFrom((qb) => this._db.as(this._tableName)).selectAll();
1599
+ let normalizedOrderBy = null;
1600
+ if (params.orderBy) {
1601
+ if (Array.isArray(params.orderBy)) {
1602
+ normalizedOrderBy = {};
1603
+ for (const item of params.orderBy) {
1604
+ for (const [key, dir] of Object.entries(item)) {
1605
+ normalizedOrderBy[key] = dir;
1606
+ }
1607
+ }
1608
+ } else {
1609
+ normalizedOrderBy = params.orderBy;
1610
+ }
1611
+ }
1612
+ const orderByFields = [];
1613
+ const hasCustomOrderBy = normalizedOrderBy !== null && Object.keys(normalizedOrderBy).length > 0;
1614
+ if (hasCustomOrderBy) {
1615
+ for (const [key, dir] of Object.entries(normalizedOrderBy)) {
1616
+ orderByFields.push({
1617
+ field: key,
1618
+ direction: String(dir).toLowerCase()
1619
+ });
1620
+ }
1621
+ }
1622
+ orderByFields.push({ field: "id", direction: "asc" });
1623
+ if (hasCustomOrderBy) {
1624
+ const effectiveOrderBy = isBackward ? Object.fromEntries(
1625
+ Object.entries(normalizedOrderBy).map(([key, dir]) => [
1626
+ key,
1627
+ String(dir).toLowerCase() === "asc" ? "desc" : "asc"
1628
+ ])
1629
+ ) : normalizedOrderBy;
1630
+ builder = applyOrderBy(
1631
+ context6,
1632
+ builder,
1633
+ this._tableName,
1634
+ effectiveOrderBy
1635
+ );
1636
+ const idDirection = isBackward ? "desc" : "asc";
1637
+ builder = builder.orderBy(`${this._tableName}.id`, idDirection);
1638
+ } else {
1639
+ const direction = isBackward ? "desc" : "asc";
1640
+ builder = builder.orderBy(`${this._tableName}.id`, direction);
1641
+ }
1642
+ const cursor = isBackward ? params.before : params.after;
1643
+ if (cursor) {
1644
+ const primaryField = orderByFields[0];
1645
+ const primaryFieldCol = (0, import_change_case.snakeCase)(primaryField.field);
1646
+ const cursorLookup = await db.selectFrom((qb) => this._db.as(this._tableName)).select([primaryFieldCol, "id"]).where(`${this._tableName}.id`, "=", cursor).limit(1).executeTakeFirst();
1647
+ if (cursorLookup) {
1648
+ const primaryValue = cursorLookup[primaryFieldCol];
1649
+ const cursorId = cursorLookup.id;
1650
+ let primaryOp;
1651
+ if (primaryField.direction === "asc") {
1652
+ primaryOp = isBackward ? "<" : ">";
1653
+ } else {
1654
+ primaryOp = isBackward ? ">" : "<";
1655
+ }
1656
+ const idOp = isBackward ? "<" : ">";
1657
+ if (primaryValue === null) {
1658
+ if (primaryOp === ">") {
1659
+ builder = builder.where(
1660
+ (eb) => eb.or([
1661
+ eb(`${this._tableName}.${primaryFieldCol}`, "is not", null),
1662
+ eb.and([
1663
+ eb(`${this._tableName}.${primaryFieldCol}`, "is", null),
1664
+ eb(`${this._tableName}.id`, idOp, cursorId)
1665
+ ])
1666
+ ])
1667
+ );
1668
+ } else {
1669
+ builder = builder.where(
1670
+ (eb) => eb.and([
1671
+ eb(`${this._tableName}.${primaryFieldCol}`, "is", null),
1672
+ eb(`${this._tableName}.id`, idOp, cursorId)
1673
+ ])
1674
+ );
1675
+ }
1676
+ } else {
1677
+ builder = builder.where(
1678
+ (eb) => eb.or([
1679
+ eb(
1680
+ `${this._tableName}.${primaryFieldCol}`,
1681
+ primaryOp,
1682
+ primaryValue
1683
+ ),
1684
+ eb.and([
1685
+ eb(
1686
+ `${this._tableName}.${primaryFieldCol}`,
1687
+ "=",
1688
+ primaryValue
1689
+ ),
1690
+ eb(`${this._tableName}.id`, idOp, cursorId)
1691
+ ])
1692
+ ])
1693
+ );
1694
+ }
1695
+ } else {
1696
+ builder = builder.where(
1697
+ `${this._tableName}.id`,
1698
+ "=",
1699
+ "___NONEXISTENT_ID___"
1700
+ );
1701
+ }
1702
+ }
1703
+ if (limit != null) {
1704
+ builder = applyLimit(context6, builder, limit);
1705
+ }
1706
+ if (isOffsetPagination && params.offset) {
1707
+ builder = applyOffset(context6, builder, params.offset);
1708
+ }
1709
+ span.setAttribute("sql", builder.compile().sql);
1710
+ let rows = await builder.execute();
1711
+ if (isBackward) {
1712
+ rows = rows.reverse();
1713
+ }
1714
+ const results = rows.map(
1715
+ (x) => transformRichDataTypes(camelCaseObject(x))
1716
+ );
1717
+ let hasNextPage = false;
1718
+ if (isOffsetPagination) {
1719
+ hasNextPage = totalCount > (params.offset || 0) + limit;
1720
+ } else if (limit != null && results.length === limit && results.length > 0) {
1721
+ const lastResult = results[results.length - 1];
1722
+ const primaryField = orderByFields[0];
1723
+ const primaryFieldCol = (0, import_change_case.snakeCase)(primaryField.field);
1724
+ const primaryValue = lastResult[primaryField.field];
1725
+ const lastId = lastResult.id;
1726
+ const primaryOp = primaryField.direction === "asc" ? ">" : "<";
1727
+ let nextCheckQuery = db.selectFrom((qb) => this._db.as(this._tableName)).select("id").limit(1);
1728
+ if (primaryValue === null) {
1729
+ if (primaryOp === ">") {
1730
+ nextCheckQuery = nextCheckQuery.where(
1731
+ (eb) => eb.or([
1732
+ eb(`${this._tableName}.${primaryFieldCol}`, "is not", null),
1733
+ eb.and([
1734
+ eb(`${this._tableName}.${primaryFieldCol}`, "is", null),
1735
+ eb(`${this._tableName}.id`, ">", lastId)
1736
+ ])
1737
+ ])
1738
+ );
1739
+ } else {
1740
+ nextCheckQuery = nextCheckQuery.where(
1741
+ (eb) => eb.and([
1742
+ eb(`${this._tableName}.${primaryFieldCol}`, "is", null),
1743
+ eb(`${this._tableName}.id`, ">", lastId)
1744
+ ])
1745
+ );
1746
+ }
1747
+ } else {
1748
+ nextCheckQuery = nextCheckQuery.where(
1749
+ (eb) => eb.or([
1750
+ eb(
1751
+ `${this._tableName}.${primaryFieldCol}`,
1752
+ primaryOp,
1753
+ primaryValue
1754
+ ),
1755
+ eb.and([
1756
+ eb(`${this._tableName}.${primaryFieldCol}`, "=", primaryValue),
1757
+ eb(`${this._tableName}.id`, ">", lastId)
1758
+ ])
1759
+ ])
1760
+ );
1761
+ }
1762
+ const nextRecord = await nextCheckQuery.executeTakeFirst();
1763
+ hasNextPage = nextRecord != null;
1764
+ }
1765
+ const pageInfo = {
1766
+ count: results.length,
1767
+ totalCount,
1768
+ hasNextPage,
1769
+ startCursor: results.length > 0 ? results[0].id : "",
1770
+ endCursor: results.length > 0 ? results[results.length - 1].id : ""
1771
+ };
1772
+ if (isOffsetPagination && limit > 0) {
1773
+ pageInfo.pageNumber = 1 + Math.floor((params.offset || 0) / limit);
1774
+ }
1775
+ return { results, pageInfo };
1776
+ });
1777
+ }
1563
1778
  };
1564
1779
 
1565
1780
  // src/ModelAPI.js
@@ -2571,7 +2786,11 @@ function tryExecuteFunction({ request, db, permitted, permissionFns, actionType,
2571
2786
  if (fnResult != null) {
2572
2787
  switch (actionType) {
2573
2788
  case PROTO_ACTION_TYPES.LIST:
2574
- rowsForPermissions = fnResult;
2789
+ if (Array.isArray(fnResult)) {
2790
+ rowsForPermissions = fnResult;
2791
+ } else if (fnResult.results != null && Array.isArray(fnResult.results)) {
2792
+ rowsForPermissions = fnResult.results;
2793
+ }
2575
2794
  break;
2576
2795
  case PROTO_ACTION_TYPES.DELETE:
2577
2796
  rowsForPermissions = [{ id: fnResult }];