@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.d.cts CHANGED
@@ -89,6 +89,31 @@ declare class QueryBuilder {
89
89
  delete(): Promise<any>;
90
90
  findOne(): Promise<any>;
91
91
  findMany(params: any): Promise<any>;
92
+ /**
93
+ * Execute query with pagination support.
94
+ * Returns results along with pageInfo metadata.
95
+ * @param {Object} params - Pagination parameters
96
+ * @param {number} [params.first] - Number of items for cursor pagination (forward)
97
+ * @param {string} [params.after] - Cursor for forward pagination
98
+ * @param {number} [params.last] - Number of items for cursor pagination (backward)
99
+ * @param {string} [params.before] - Cursor for backward pagination
100
+ * @param {number} [params.limit] - Page size for offset pagination
101
+ * @param {number} [params.offset] - Offset for offset pagination
102
+ * @param {Object} [params.orderBy] - Ordering specification
103
+ * @returns {Promise<{results: any[], pageInfo: Object}>}
104
+ */
105
+ findManyWithPageInfo(params?: {
106
+ first?: number | undefined;
107
+ after?: string | undefined;
108
+ last?: number | undefined;
109
+ before?: string | undefined;
110
+ limit?: number | undefined;
111
+ offset?: number | undefined;
112
+ orderBy?: Object | undefined;
113
+ }): Promise<{
114
+ results: any[];
115
+ pageInfo: Object;
116
+ }>;
92
117
  }
93
118
 
94
119
  /**
@@ -1464,6 +1489,31 @@ type PageInfo = {
1464
1489
  count: number;
1465
1490
  pageNumber?: number;
1466
1491
  };
1492
+ /**
1493
+ * Partial PageInfo for manual pagination - all fields optional with sensible defaults.
1494
+ * - count: defaults to results.length
1495
+ * - totalCount: defaults to results.length
1496
+ * - hasNextPage: defaults to false
1497
+ * - startCursor: defaults to first result's id or ""
1498
+ * - endCursor: defaults to last result's id or ""
1499
+ * - pageNumber: optional, no default
1500
+ */
1501
+ type PartialPageInfo = {
1502
+ startCursor?: string;
1503
+ endCursor?: string;
1504
+ totalCount?: number;
1505
+ hasNextPage?: boolean;
1506
+ count?: number;
1507
+ pageNumber?: number;
1508
+ };
1509
+ /**
1510
+ * Result type for list operations with pagination info.
1511
+ * Used when returning manually paginated results from beforeQuery or afterQuery hooks.
1512
+ */
1513
+ type ListResult<M> = {
1514
+ results: Array<M>;
1515
+ pageInfo: PartialPageInfo;
1516
+ };
1467
1517
  type SortDirection = "asc" | "desc" | "ASC" | "DESC";
1468
1518
  declare class NotFoundError extends Error {
1469
1519
  }
@@ -1553,4 +1603,4 @@ type FlowListResult = {
1553
1603
  };
1554
1604
  };
1555
1605
 
1556
- export { type BooleanArrayQueryWhereCondition, type BooleanArrayWhereCondition, type BooleanWhereCondition, type ContextAPI, type DateArrayQueryWhereCondition, type DateArrayWhereCondition, type DateQueryInput, type DateWhereCondition, Duration, type DurationString, type DurationWhereCondition, ErrorPresets, type Errors, type ExtractStageKeys, File, type FlowConfig, type FlowConfigAPI, type FlowContext, type FlowFunction, type FlowListOptions, type FlowListResult, type FlowRun, type FlowRunStatus, type FlowRunStep, FlowsAPI, type FuncWithConfig, type FunctionConfig, type Hardware, type IDWhereCondition, InlineFile, ModelAPI, NonRetriableError, type NullableHardware, type NumberArrayQueryWhereCondition, type NumberArrayWhereCondition, type NumberWhereCondition, PERMISSION_STATE, type PageInfo, Permissions, type Printer, type RelativeDateString, RequestHeaders, type Response, RetryBackoffExponential, RetryBackoffLinear, RetryConstant, STEP_STATUS, STEP_TYPE, type SortDirection, type Step, type StringArrayQueryWhereCondition, type StringArrayWhereCondition, type StringWhereCondition, type Task, TaskAPI, type TaskCreateOptions, type TaskFlowFunction, type TaskStatus, type TimestampQueryInput, type UI, type UIApiResponses, checkBuiltInPermissions, createFlowContext, handleFlow, handleJob, handleRequest, handleRoute, handleSubscriber, ksuid, tracing, useDatabase };
1606
+ export { type BooleanArrayQueryWhereCondition, type BooleanArrayWhereCondition, type BooleanWhereCondition, type ContextAPI, type DateArrayQueryWhereCondition, type DateArrayWhereCondition, type DateQueryInput, type DateWhereCondition, Duration, type DurationString, type DurationWhereCondition, ErrorPresets, type Errors, type ExtractStageKeys, File, type FlowConfig, type FlowConfigAPI, type FlowContext, type FlowFunction, type FlowListOptions, type FlowListResult, type FlowRun, type FlowRunStatus, type FlowRunStep, FlowsAPI, type FuncWithConfig, type FunctionConfig, type Hardware, type IDWhereCondition, InlineFile, type ListResult, ModelAPI, NonRetriableError, type NullableHardware, type NumberArrayQueryWhereCondition, type NumberArrayWhereCondition, type NumberWhereCondition, PERMISSION_STATE, type PageInfo, type PartialPageInfo, Permissions, type Printer, type RelativeDateString, RequestHeaders, type Response, RetryBackoffExponential, RetryBackoffLinear, RetryConstant, STEP_STATUS, STEP_TYPE, type SortDirection, type Step, type StringArrayQueryWhereCondition, type StringArrayWhereCondition, type StringWhereCondition, type Task, TaskAPI, type TaskCreateOptions, type TaskFlowFunction, type TaskStatus, type TimestampQueryInput, type UI, type UIApiResponses, checkBuiltInPermissions, createFlowContext, handleFlow, handleJob, handleRequest, handleRoute, handleSubscriber, ksuid, tracing, useDatabase };
package/dist/index.d.ts CHANGED
@@ -89,6 +89,31 @@ declare class QueryBuilder {
89
89
  delete(): Promise<any>;
90
90
  findOne(): Promise<any>;
91
91
  findMany(params: any): Promise<any>;
92
+ /**
93
+ * Execute query with pagination support.
94
+ * Returns results along with pageInfo metadata.
95
+ * @param {Object} params - Pagination parameters
96
+ * @param {number} [params.first] - Number of items for cursor pagination (forward)
97
+ * @param {string} [params.after] - Cursor for forward pagination
98
+ * @param {number} [params.last] - Number of items for cursor pagination (backward)
99
+ * @param {string} [params.before] - Cursor for backward pagination
100
+ * @param {number} [params.limit] - Page size for offset pagination
101
+ * @param {number} [params.offset] - Offset for offset pagination
102
+ * @param {Object} [params.orderBy] - Ordering specification
103
+ * @returns {Promise<{results: any[], pageInfo: Object}>}
104
+ */
105
+ findManyWithPageInfo(params?: {
106
+ first?: number | undefined;
107
+ after?: string | undefined;
108
+ last?: number | undefined;
109
+ before?: string | undefined;
110
+ limit?: number | undefined;
111
+ offset?: number | undefined;
112
+ orderBy?: Object | undefined;
113
+ }): Promise<{
114
+ results: any[];
115
+ pageInfo: Object;
116
+ }>;
92
117
  }
93
118
 
94
119
  /**
@@ -1464,6 +1489,31 @@ type PageInfo = {
1464
1489
  count: number;
1465
1490
  pageNumber?: number;
1466
1491
  };
1492
+ /**
1493
+ * Partial PageInfo for manual pagination - all fields optional with sensible defaults.
1494
+ * - count: defaults to results.length
1495
+ * - totalCount: defaults to results.length
1496
+ * - hasNextPage: defaults to false
1497
+ * - startCursor: defaults to first result's id or ""
1498
+ * - endCursor: defaults to last result's id or ""
1499
+ * - pageNumber: optional, no default
1500
+ */
1501
+ type PartialPageInfo = {
1502
+ startCursor?: string;
1503
+ endCursor?: string;
1504
+ totalCount?: number;
1505
+ hasNextPage?: boolean;
1506
+ count?: number;
1507
+ pageNumber?: number;
1508
+ };
1509
+ /**
1510
+ * Result type for list operations with pagination info.
1511
+ * Used when returning manually paginated results from beforeQuery or afterQuery hooks.
1512
+ */
1513
+ type ListResult<M> = {
1514
+ results: Array<M>;
1515
+ pageInfo: PartialPageInfo;
1516
+ };
1467
1517
  type SortDirection = "asc" | "desc" | "ASC" | "DESC";
1468
1518
  declare class NotFoundError extends Error {
1469
1519
  }
@@ -1553,4 +1603,4 @@ type FlowListResult = {
1553
1603
  };
1554
1604
  };
1555
1605
 
1556
- export { type BooleanArrayQueryWhereCondition, type BooleanArrayWhereCondition, type BooleanWhereCondition, type ContextAPI, type DateArrayQueryWhereCondition, type DateArrayWhereCondition, type DateQueryInput, type DateWhereCondition, Duration, type DurationString, type DurationWhereCondition, ErrorPresets, type Errors, type ExtractStageKeys, File, type FlowConfig, type FlowConfigAPI, type FlowContext, type FlowFunction, type FlowListOptions, type FlowListResult, type FlowRun, type FlowRunStatus, type FlowRunStep, FlowsAPI, type FuncWithConfig, type FunctionConfig, type Hardware, type IDWhereCondition, InlineFile, ModelAPI, NonRetriableError, type NullableHardware, type NumberArrayQueryWhereCondition, type NumberArrayWhereCondition, type NumberWhereCondition, PERMISSION_STATE, type PageInfo, Permissions, type Printer, type RelativeDateString, RequestHeaders, type Response, RetryBackoffExponential, RetryBackoffLinear, RetryConstant, STEP_STATUS, STEP_TYPE, type SortDirection, type Step, type StringArrayQueryWhereCondition, type StringArrayWhereCondition, type StringWhereCondition, type Task, TaskAPI, type TaskCreateOptions, type TaskFlowFunction, type TaskStatus, type TimestampQueryInput, type UI, type UIApiResponses, checkBuiltInPermissions, createFlowContext, handleFlow, handleJob, handleRequest, handleRoute, handleSubscriber, ksuid, tracing, useDatabase };
1606
+ export { type BooleanArrayQueryWhereCondition, type BooleanArrayWhereCondition, type BooleanWhereCondition, type ContextAPI, type DateArrayQueryWhereCondition, type DateArrayWhereCondition, type DateQueryInput, type DateWhereCondition, Duration, type DurationString, type DurationWhereCondition, ErrorPresets, type Errors, type ExtractStageKeys, File, type FlowConfig, type FlowConfigAPI, type FlowContext, type FlowFunction, type FlowListOptions, type FlowListResult, type FlowRun, type FlowRunStatus, type FlowRunStep, FlowsAPI, type FuncWithConfig, type FunctionConfig, type Hardware, type IDWhereCondition, InlineFile, type ListResult, ModelAPI, NonRetriableError, type NullableHardware, type NumberArrayQueryWhereCondition, type NumberArrayWhereCondition, type NumberWhereCondition, PERMISSION_STATE, type PageInfo, type PartialPageInfo, Permissions, type Printer, type RelativeDateString, RequestHeaders, type Response, RetryBackoffExponential, RetryBackoffLinear, RetryConstant, STEP_STATUS, STEP_TYPE, type SortDirection, type Step, type StringArrayQueryWhereCondition, type StringArrayWhereCondition, type StringWhereCondition, type Task, TaskAPI, type TaskCreateOptions, type TaskFlowFunction, type TaskStatus, type TimestampQueryInput, type UI, type UIApiResponses, checkBuiltInPermissions, createFlowContext, handleFlow, handleJob, handleRequest, handleRoute, handleSubscriber, ksuid, tracing, useDatabase };
package/dist/index.js CHANGED
@@ -1508,6 +1508,221 @@ var QueryBuilder = class _QueryBuilder {
1508
1508
  return rows.map((x) => transformRichDataTypes(camelCaseObject(x)));
1509
1509
  });
1510
1510
  }
1511
+ /**
1512
+ * Execute query with pagination support.
1513
+ * Returns results along with pageInfo metadata.
1514
+ * @param {Object} params - Pagination parameters
1515
+ * @param {number} [params.first] - Number of items for cursor pagination (forward)
1516
+ * @param {string} [params.after] - Cursor for forward pagination
1517
+ * @param {number} [params.last] - Number of items for cursor pagination (backward)
1518
+ * @param {string} [params.before] - Cursor for backward pagination
1519
+ * @param {number} [params.limit] - Page size for offset pagination
1520
+ * @param {number} [params.offset] - Offset for offset pagination
1521
+ * @param {Object} [params.orderBy] - Ordering specification
1522
+ * @returns {Promise<{results: any[], pageInfo: Object}>}
1523
+ */
1524
+ async findManyWithPageInfo(params = {}) {
1525
+ const name = spanNameForModelAPI(
1526
+ this._modelName,
1527
+ "findManyWithPageInfo"
1528
+ );
1529
+ const db = useDatabase();
1530
+ return withSpan(name, async (span) => {
1531
+ const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1532
+ const isOffsetPagination = params.limit != null && params.limit > 0;
1533
+ const isBackward = params.last != null && params.last > 0;
1534
+ const DEFAULT_PAGE_SIZE = 50;
1535
+ let limit;
1536
+ if (isOffsetPagination) {
1537
+ limit = params.limit;
1538
+ } else if (isBackward) {
1539
+ limit = params.last;
1540
+ } else {
1541
+ limit = params.first ?? DEFAULT_PAGE_SIZE;
1542
+ }
1543
+ const countQuery = db.selectFrom((qb) => this._db.as(this._tableName)).select(db.fn.countAll().as("total"));
1544
+ const countResult = await countQuery.executeTakeFirst();
1545
+ const totalCount = Number(countResult?.total ?? 0);
1546
+ let builder = db.selectFrom((qb) => this._db.as(this._tableName)).selectAll();
1547
+ let normalizedOrderBy = null;
1548
+ if (params.orderBy) {
1549
+ if (Array.isArray(params.orderBy)) {
1550
+ normalizedOrderBy = {};
1551
+ for (const item of params.orderBy) {
1552
+ for (const [key, dir] of Object.entries(item)) {
1553
+ normalizedOrderBy[key] = dir;
1554
+ }
1555
+ }
1556
+ } else {
1557
+ normalizedOrderBy = params.orderBy;
1558
+ }
1559
+ }
1560
+ const orderByFields = [];
1561
+ const hasCustomOrderBy = normalizedOrderBy !== null && Object.keys(normalizedOrderBy).length > 0;
1562
+ if (hasCustomOrderBy) {
1563
+ for (const [key, dir] of Object.entries(normalizedOrderBy)) {
1564
+ orderByFields.push({
1565
+ field: key,
1566
+ direction: String(dir).toLowerCase()
1567
+ });
1568
+ }
1569
+ }
1570
+ orderByFields.push({ field: "id", direction: "asc" });
1571
+ if (hasCustomOrderBy) {
1572
+ const effectiveOrderBy = isBackward ? Object.fromEntries(
1573
+ Object.entries(normalizedOrderBy).map(([key, dir]) => [
1574
+ key,
1575
+ String(dir).toLowerCase() === "asc" ? "desc" : "asc"
1576
+ ])
1577
+ ) : normalizedOrderBy;
1578
+ builder = applyOrderBy(
1579
+ context6,
1580
+ builder,
1581
+ this._tableName,
1582
+ effectiveOrderBy
1583
+ );
1584
+ const idDirection = isBackward ? "desc" : "asc";
1585
+ builder = builder.orderBy(`${this._tableName}.id`, idDirection);
1586
+ } else {
1587
+ const direction = isBackward ? "desc" : "asc";
1588
+ builder = builder.orderBy(`${this._tableName}.id`, direction);
1589
+ }
1590
+ const cursor = isBackward ? params.before : params.after;
1591
+ if (cursor) {
1592
+ const primaryField = orderByFields[0];
1593
+ const primaryFieldCol = snakeCase(primaryField.field);
1594
+ const cursorLookup = await db.selectFrom((qb) => this._db.as(this._tableName)).select([primaryFieldCol, "id"]).where(`${this._tableName}.id`, "=", cursor).limit(1).executeTakeFirst();
1595
+ if (cursorLookup) {
1596
+ const primaryValue = cursorLookup[primaryFieldCol];
1597
+ const cursorId = cursorLookup.id;
1598
+ let primaryOp;
1599
+ if (primaryField.direction === "asc") {
1600
+ primaryOp = isBackward ? "<" : ">";
1601
+ } else {
1602
+ primaryOp = isBackward ? ">" : "<";
1603
+ }
1604
+ const idOp = isBackward ? "<" : ">";
1605
+ if (primaryValue === null) {
1606
+ if (primaryOp === ">") {
1607
+ builder = builder.where(
1608
+ (eb) => eb.or([
1609
+ eb(`${this._tableName}.${primaryFieldCol}`, "is not", null),
1610
+ eb.and([
1611
+ eb(`${this._tableName}.${primaryFieldCol}`, "is", null),
1612
+ eb(`${this._tableName}.id`, idOp, cursorId)
1613
+ ])
1614
+ ])
1615
+ );
1616
+ } else {
1617
+ builder = builder.where(
1618
+ (eb) => eb.and([
1619
+ eb(`${this._tableName}.${primaryFieldCol}`, "is", null),
1620
+ eb(`${this._tableName}.id`, idOp, cursorId)
1621
+ ])
1622
+ );
1623
+ }
1624
+ } else {
1625
+ builder = builder.where(
1626
+ (eb) => eb.or([
1627
+ eb(
1628
+ `${this._tableName}.${primaryFieldCol}`,
1629
+ primaryOp,
1630
+ primaryValue
1631
+ ),
1632
+ eb.and([
1633
+ eb(
1634
+ `${this._tableName}.${primaryFieldCol}`,
1635
+ "=",
1636
+ primaryValue
1637
+ ),
1638
+ eb(`${this._tableName}.id`, idOp, cursorId)
1639
+ ])
1640
+ ])
1641
+ );
1642
+ }
1643
+ } else {
1644
+ builder = builder.where(
1645
+ `${this._tableName}.id`,
1646
+ "=",
1647
+ "___NONEXISTENT_ID___"
1648
+ );
1649
+ }
1650
+ }
1651
+ if (limit != null) {
1652
+ builder = applyLimit(context6, builder, limit);
1653
+ }
1654
+ if (isOffsetPagination && params.offset) {
1655
+ builder = applyOffset(context6, builder, params.offset);
1656
+ }
1657
+ span.setAttribute("sql", builder.compile().sql);
1658
+ let rows = await builder.execute();
1659
+ if (isBackward) {
1660
+ rows = rows.reverse();
1661
+ }
1662
+ const results = rows.map(
1663
+ (x) => transformRichDataTypes(camelCaseObject(x))
1664
+ );
1665
+ let hasNextPage = false;
1666
+ if (isOffsetPagination) {
1667
+ hasNextPage = totalCount > (params.offset || 0) + limit;
1668
+ } else if (limit != null && results.length === limit && results.length > 0) {
1669
+ const lastResult = results[results.length - 1];
1670
+ const primaryField = orderByFields[0];
1671
+ const primaryFieldCol = snakeCase(primaryField.field);
1672
+ const primaryValue = lastResult[primaryField.field];
1673
+ const lastId = lastResult.id;
1674
+ const primaryOp = primaryField.direction === "asc" ? ">" : "<";
1675
+ let nextCheckQuery = db.selectFrom((qb) => this._db.as(this._tableName)).select("id").limit(1);
1676
+ if (primaryValue === null) {
1677
+ if (primaryOp === ">") {
1678
+ nextCheckQuery = nextCheckQuery.where(
1679
+ (eb) => eb.or([
1680
+ eb(`${this._tableName}.${primaryFieldCol}`, "is not", null),
1681
+ eb.and([
1682
+ eb(`${this._tableName}.${primaryFieldCol}`, "is", null),
1683
+ eb(`${this._tableName}.id`, ">", lastId)
1684
+ ])
1685
+ ])
1686
+ );
1687
+ } else {
1688
+ nextCheckQuery = nextCheckQuery.where(
1689
+ (eb) => eb.and([
1690
+ eb(`${this._tableName}.${primaryFieldCol}`, "is", null),
1691
+ eb(`${this._tableName}.id`, ">", lastId)
1692
+ ])
1693
+ );
1694
+ }
1695
+ } else {
1696
+ nextCheckQuery = nextCheckQuery.where(
1697
+ (eb) => eb.or([
1698
+ eb(
1699
+ `${this._tableName}.${primaryFieldCol}`,
1700
+ primaryOp,
1701
+ primaryValue
1702
+ ),
1703
+ eb.and([
1704
+ eb(`${this._tableName}.${primaryFieldCol}`, "=", primaryValue),
1705
+ eb(`${this._tableName}.id`, ">", lastId)
1706
+ ])
1707
+ ])
1708
+ );
1709
+ }
1710
+ const nextRecord = await nextCheckQuery.executeTakeFirst();
1711
+ hasNextPage = nextRecord != null;
1712
+ }
1713
+ const pageInfo = {
1714
+ count: results.length,
1715
+ totalCount,
1716
+ hasNextPage,
1717
+ startCursor: results.length > 0 ? results[0].id : "",
1718
+ endCursor: results.length > 0 ? results[results.length - 1].id : ""
1719
+ };
1720
+ if (isOffsetPagination && limit > 0) {
1721
+ pageInfo.pageNumber = 1 + Math.floor((params.offset || 0) / limit);
1722
+ }
1723
+ return { results, pageInfo };
1724
+ });
1725
+ }
1511
1726
  };
1512
1727
 
1513
1728
  // src/ModelAPI.js
@@ -2523,7 +2738,11 @@ function tryExecuteFunction({ request, db, permitted, permissionFns, actionType,
2523
2738
  if (fnResult != null) {
2524
2739
  switch (actionType) {
2525
2740
  case PROTO_ACTION_TYPES.LIST:
2526
- rowsForPermissions = fnResult;
2741
+ if (Array.isArray(fnResult)) {
2742
+ rowsForPermissions = fnResult;
2743
+ } else if (fnResult.results != null && Array.isArray(fnResult.results)) {
2744
+ rowsForPermissions = fnResult.results;
2745
+ }
2527
2746
  break;
2528
2747
  case PROTO_ACTION_TYPES.DELETE:
2529
2748
  rowsForPermissions = [{ id: fnResult }];