@proteinjs/db 1.37.0 → 1.38.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proteinjs/db",
3
- "version": "1.37.0",
3
+ "version": "1.38.0",
4
4
  "main": "./dist/generated/index.js",
5
5
  "types": "./dist/generated/index.d.ts",
6
6
  "exports": {
@@ -41,7 +41,7 @@
41
41
  "test": "jest --passWithNoTests"
42
42
  },
43
43
  "dependencies": {
44
- "@proteinjs/db-query": "^1.8.0",
44
+ "@proteinjs/db-query": "^1.9.0",
45
45
  "@proteinjs/logger": "^1.0.21",
46
46
  "@proteinjs/reflection": "^1.2.0",
47
47
  "@proteinjs/serializer": "^1.1.10",
@@ -66,5 +66,5 @@
66
66
  "ts-jest": "29.1.1",
67
67
  "typescript": "5.2.2"
68
68
  },
69
- "gitHead": "cb0fda126d5d2bd5b17d1bc13e87c2373eb61f52"
69
+ "gitHead": "7c4ebb2ab218d48a276940140161979f0aa31730"
70
70
  }
package/src/Db.ts CHANGED
@@ -58,6 +58,8 @@ export type DbDriverQueryStatementConfig = ParameterizationConfig & {
58
58
  prefixTablesWithDb?: boolean;
59
59
  getDriverColumnType?: (tableName: string, columnName: string) => string;
60
60
  handleCaseSensitivity: (tableName: string, columnName: string, caseSensitive: boolean) => string;
61
+ /** Driver SQL for truncating a datetime column to a bucket unit (`QueryBuilder.timeBucket`). */
62
+ dateTruncExpression?: (resolvedColumnName: string, unit: 'day' | 'hour') => string;
61
63
  };
62
64
 
63
65
  export type DbDriverDmlStatementConfig = ParameterizationConfig & {
@@ -708,6 +710,29 @@ export class Db<R extends Record = Record> implements DbService<R> {
708
710
  return result[0]['count'];
709
711
  }
710
712
 
713
+ /**
714
+ * Run a grouped-aggregation query and return the RAW result rows — one object per group,
715
+ * carrying the selected group fields (keyed by their DRIVER COLUMN names), any
716
+ * `timeBucket` dimension (keyed by its `resultProp`), and the aggregate values (keyed by
717
+ * their `resultProp`s). This is the scalable read for reporting rollups (SUM/COUNT per
718
+ * model/day/owner AT the database) — the alternative, `query()` + summing in memory, loads
719
+ * every underlying row and does not survive real data volumes.
720
+ *
721
+ * Rides the same authority as `query()`: table read auth unless run as system, and column
722
+ * queries (e.g. scope pre-filtering) are applied — a scoped caller aggregates only rows it
723
+ * could read. No record deserialization happens: aggregate rows are not records.
724
+ */
725
+ async queryAggregates<T extends R>(table: Table<T>, qb: QueryBuilder<T>): Promise<{ [key: string]: unknown }[]> {
726
+ if (!this.runAsSystem) {
727
+ this.auth.canQuery(table);
728
+ }
729
+
730
+ await this.addColumnQueries(table, qb);
731
+ const generateQuery = (config: DbDriverQueryStatementConfig) =>
732
+ qb.toSql(this.statementConfigFactory.getStatementConfig(config));
733
+ return await this.dbDriver.runQuery(generateQuery, this.transactionForDriver());
734
+ }
735
+
711
736
  private async addColumnQueries<T extends R>(
712
737
  table: Table<T>,
713
738
  qb: QueryBuilder<T>,
@@ -34,7 +34,10 @@ export class StatementConfigFactory {
34
34
  useNamedParams: config.useNamedParams,
35
35
  getColumnType: this.getColumnType(),
36
36
  getDriverColumnType: config.getDriverColumnType,
37
- ...(this.isQueryConfig(config) && { handleCaseSensitivity: config.handleCaseSensitivity }),
37
+ ...(this.isQueryConfig(config) && {
38
+ handleCaseSensitivity: config.handleCaseSensitivity,
39
+ dateTruncExpression: config.dateTruncExpression,
40
+ }),
38
41
  };
39
42
  }
40
43
 
package/src/Table.ts CHANGED
@@ -311,6 +311,15 @@ export type ColumnOptions = {
311
311
  ) => Promise<void>;
312
312
  ui?: {
313
313
  hidden?: boolean;
314
+ /**
315
+ * Which section of the record form this column belongs to. The form derives a sane
316
+ * section from the column's type and name (identity strings up top, long text and
317
+ * structured values under Content, everything else under Details, server-managed meta
318
+ * under System); this hint overrides the derivation. Known values map to the canonical
319
+ * sections ('identity' | 'content' | 'details' | 'system'); any other string becomes its
320
+ * own titled section, ordered after Details.
321
+ */
322
+ formGroup?: 'identity' | 'content' | 'details' | 'system' | (string & {});
314
323
  };
315
324
  };
316
325