@bluecopa/core 0.1.58 → 0.1.59

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.
@@ -0,0 +1,78 @@
1
+ import { WhereOp, AggregateSpec, AggregateResult, Row } from './types';
2
+ export type WhereClause = {
3
+ field: string;
4
+ op: WhereOp;
5
+ value: unknown;
6
+ };
7
+ /**
8
+ * Validates an aggregate spec — function names must be in the allowed set,
9
+ * column names must pass validateIdentifier (safe for URL params).
10
+ */
11
+ export declare function validateAggregateSpec<TRow extends Row>(spec: AggregateSpec<TRow>): void;
12
+ /**
13
+ * Validates groupBy columns — each name must pass validateIdentifier() and must not
14
+ * overlap with aggregate spec keys (which would create a key collision in the result).
15
+ */
16
+ export declare function validateGroupBySpec<TRow extends Row>(groupBy: (keyof TRow & string)[], spec: AggregateSpec<TRow>): void;
17
+ /**
18
+ * Builds the PostgREST `select` query param string from an AggregateSpec.
19
+ *
20
+ * For spec `{ amount: ['sum', 'avg'], _count: true }` produces:
21
+ * `amount_sum:amount.sum(),amount_avg:amount.avg(),count()`
22
+ *
23
+ * When groupBy columns are provided, they are prepended as bare column names,
24
+ * which causes PostgREST to automatically group by those columns:
25
+ * `order_date,status,amount_sum:amount.sum(),count()`
26
+ *
27
+ * Aliases prevent collisions when multiple columns use the same function.
28
+ */
29
+ export declare function buildAggregateSelect<TRow extends Row>(spec: AggregateSpec<TRow>, groupBy?: (keyof TRow & string)[]): string;
30
+ /**
31
+ * Builds the PostgREST `order` query param string from an array of order clauses.
32
+ *
33
+ * For `[{ field: 'order_date', direction: 'asc' }, { field: 'status', direction: 'desc' }]`
34
+ * produces: `order_date.asc,status.desc`
35
+ */
36
+ export declare function buildPostgrestOrder(orders: {
37
+ field: string;
38
+ direction: 'asc' | 'desc';
39
+ }[]): string;
40
+ /**
41
+ * Builds PostgREST filter query params from a WhereClause array.
42
+ *
43
+ * For `[{ field: 'status', op: '==', value: 'active' }]` produces:
44
+ * `{ status: 'eq.active' }`
45
+ *
46
+ * The `in`/`not-in` operators wrap array values in parentheses:
47
+ * `{ status: 'in.(a,b,c)' }`
48
+ *
49
+ * Field names are validated via validateIdentifier and checked against
50
+ * PostgREST reserved param names to prevent query param injection.
51
+ *
52
+ * Values containing PostgREST reserved characters (`,` `.` `:` `(` `)`)
53
+ * are wrapped in double quotes per PostgREST escaping rules.
54
+ *
55
+ * Multiple filters on the same field (e.g., range queries) are preserved as
56
+ * an array, which Axios serializes as repeated query params:
57
+ * `amount=gt.10&amount=lt.100` — PostgREST ANDs them automatically.
58
+ */
59
+ export declare function buildPostgrestFilters(wheres: WhereClause[]): Record<string, string | string[]>;
60
+ /**
61
+ * Maps a flat PostgREST aggregate response back to the nested AggregateResult shape.
62
+ *
63
+ * Given spec `{ amount: ['sum', 'avg'], _count: true }` and flat row:
64
+ * `{ amount_sum: 1234, amount_avg: 123, count: 50 }`
65
+ * Returns:
66
+ * `{ amount: { sum: 1234, avg: 123 }, _count: 50 }`
67
+ */
68
+ export declare function parseAggregateResponse<TSpec extends AggregateSpec>(spec: TSpec, data: Record<string, number | null>[]): AggregateResult<TSpec>;
69
+ /**
70
+ * Maps a flat PostgREST grouped aggregate response to an array of nested result objects.
71
+ *
72
+ * For each row in data, includes the groupBy column values directly plus nested aggregate values.
73
+ * Given spec `{ amount: ['sum'] }`, groupBy `['status']`, and data:
74
+ * `[{ status: 'active', amount_sum: 500 }, { status: 'draft', amount_sum: 100 }]`
75
+ * Returns:
76
+ * `[{ status: 'active', amount: { sum: 500 } }, { status: 'draft', amount: { sum: 100 } }]`
77
+ */
78
+ export declare function parseGroupedAggregateResponse<TSpec extends AggregateSpec>(spec: TSpec, groupBy: string[], data: Record<string, unknown>[]): Record<string, unknown>[];
@@ -1,8 +1,9 @@
1
1
  import { RxCollection } from 'rxdb';
2
2
  import { RxReplicationState } from 'rxdb/plugins/replication';
3
3
  import { IWebsocketProvider } from '../config';
4
- import { Row, WhereOp, CollectionRef, QueryBuilder, InputTableColumn } from './types';
4
+ import { Row, WhereOp, CollectionRef, QueryBuilder, InputTableColumn, AggregateSpec, AggregateResult, GroupedAggregateResult } from './types';
5
5
  import { DocRefImpl } from './docRef';
6
+ import { WhereClause } from './aggregateUtils';
6
7
  export declare class CollectionRefImpl<TRow extends Row = Row> implements CollectionRef<TRow> {
7
8
  private collection;
8
9
  private solutionId;
@@ -12,6 +13,7 @@ export declare class CollectionRefImpl<TRow extends Row = Row> implements Collec
12
13
  private replicationState;
13
14
  private getWebsocketProvider;
14
15
  constructor(collection: RxCollection, solutionId: string, tableName: string, primaryKeyField: string, pkColumn: InputTableColumn | undefined, replicationState: RxReplicationState<any, any>, getWebsocketProvider: () => IWebsocketProvider | undefined);
16
+ private makeQueryBuilder;
15
17
  where(field: keyof TRow & string, op: WhereOp, value: unknown): QueryBuilder<TRow>;
16
18
  orderBy(field: keyof TRow & string, direction?: 'asc' | 'desc'): QueryBuilder<TRow>;
17
19
  limit(count: number): QueryBuilder<TRow>;
@@ -21,4 +23,24 @@ export declare class CollectionRefImpl<TRow extends Row = Row> implements Collec
21
23
  subscribe(callback: (rows: TRow[]) => void): () => void;
22
24
  get(): Promise<TRow[]>;
23
25
  count(callback: (count: number) => void): () => void;
26
+ /**
27
+ * Compute server-side aggregates via PostgREST. Bypasses local RxDB cache.
28
+ *
29
+ * - No `groupBy` → returns a single summary object.
30
+ * - `groupBy: ['col']` → returns an array of per-group objects, each containing the
31
+ * grouped column values plus nested aggregate results. Requires at least one aggregate
32
+ * function or `_count: true` in the spec.
33
+ * - `groupBy: []` (empty array) → treated as non-grouped; returns the single summary shape.
34
+ */
35
+ aggregate<TSpec extends AggregateSpec<TRow>>(spec: TSpec): Promise<AggregateResult<TSpec>>;
36
+ aggregate<TSpec extends AggregateSpec<TRow>>(spec: TSpec, options: {
37
+ groupBy: [];
38
+ }): Promise<AggregateResult<TSpec>>;
39
+ aggregate<TSpec extends AggregateSpec<TRow>, TKeys extends (keyof TRow & string)[]>(spec: TSpec, options: {
40
+ groupBy: TKeys;
41
+ }): Promise<GroupedAggregateResult<TSpec, TRow, TKeys>>;
42
+ executeAggregate<TSpec extends AggregateSpec<TRow>>(wheres: WhereClause[], orders: {
43
+ field: string;
44
+ direction: 'asc' | 'desc';
45
+ }[], limit: number | undefined, skip: number | undefined, spec: TSpec, groupBy?: (keyof TRow & string)[]): Promise<AggregateResult<TSpec> | GroupedAggregateResult<TSpec, TRow, (keyof TRow & string)[]>>;
24
46
  }
@@ -1,4 +1,4 @@
1
1
  export { copaInputTableDb } from './inputTableDb';
2
2
  export { InputTableColumnType } from './types';
3
3
  export { InputTableError } from './utils';
4
- export type { Row, WhereOp, QueryBuilder, DocRef, CollectionRef, InputTableDB, InputTableDefinition, InputTableColumn } from './types';
4
+ export type { Row, WhereOp, QueryBuilder, DocRef, CollectionRef, InputTableDB, InputTableDefinition, InputTableColumn, AggregateFunction, AggregateSpec, AggregateResult, GroupedAggregateResult } from './types';
@@ -1,27 +1,32 @@
1
1
  import { RxCollection } from 'rxdb';
2
- import { Row, WhereOp, QueryBuilder } from './types';
3
- type WhereClause = {
4
- field: string;
5
- op: WhereOp;
6
- value: unknown;
7
- };
2
+ import { Row, WhereOp, QueryBuilder, AggregateSpec, AggregateResult, GroupedAggregateResult } from './types';
3
+ import { WhereClause } from './aggregateUtils';
8
4
  type OrderClause = {
9
5
  field: string;
10
6
  direction: 'asc' | 'desc';
11
7
  };
8
+ type AggregateExecutor<TRow extends Row> = <TSpec extends AggregateSpec<TRow>>(wheres: WhereClause[], orders: OrderClause[], limit: number | undefined, skip: number | undefined, spec: TSpec, groupBy?: (keyof TRow & string)[]) => Promise<AggregateResult<TSpec> | GroupedAggregateResult<TSpec, TRow, (keyof TRow & string)[]>>;
12
9
  export declare class QueryBuilderImpl<TRow extends Row = Row> implements QueryBuilder<TRow> {
13
10
  private collection;
11
+ private aggregateExecutor?;
14
12
  private wheres;
15
13
  private orders;
16
14
  private limitCount;
17
15
  private skipCount;
18
- constructor(collection: RxCollection, initialWhere?: WhereClause, initialOrder?: OrderClause, initialLimit?: number, initialSkip?: number);
16
+ constructor(collection: RxCollection, initialWhere?: WhereClause, initialOrder?: OrderClause, initialLimit?: number, initialSkip?: number, aggregateExecutor?: AggregateExecutor<TRow>);
19
17
  where(field: keyof TRow & string, op: WhereOp, value: unknown): QueryBuilder<TRow>;
20
18
  orderBy(field: keyof TRow & string, direction?: 'asc' | 'desc'): QueryBuilder<TRow>;
21
19
  limit(count: number): QueryBuilder<TRow>;
22
20
  skip(count: number): QueryBuilder<TRow>;
23
21
  subscribe(callback: (rows: TRow[]) => void): () => void;
24
22
  get(): Promise<TRow[]>;
23
+ aggregate<TSpec extends AggregateSpec<TRow>>(spec: TSpec): Promise<AggregateResult<TSpec>>;
24
+ aggregate<TSpec extends AggregateSpec<TRow>>(spec: TSpec, options: {
25
+ groupBy: [];
26
+ }): Promise<AggregateResult<TSpec>>;
27
+ aggregate<TSpec extends AggregateSpec<TRow>, TKeys extends (keyof TRow & string)[]>(spec: TSpec, options: {
28
+ groupBy: TKeys;
29
+ }): Promise<GroupedAggregateResult<TSpec, TRow, TKeys>>;
25
30
  private buildQuery;
26
31
  }
27
32
  export {};
@@ -28,6 +28,26 @@ export type InputTableDefinition = {
28
28
  export type Row = Record<string, unknown>;
29
29
  /** Where clause operators */
30
30
  export type WhereOp = '==' | '!=' | '<' | '<=' | '>' | '>=' | 'in' | 'not-in';
31
+ /** Supported PostgREST aggregate functions */
32
+ export type AggregateFunction = 'sum' | 'avg' | 'count' | 'min' | 'max';
33
+ /** Aggregate specification — column-to-functions mapping + optional row count */
34
+ export type AggregateSpec<TRow extends Row = Row> = {
35
+ [K in keyof TRow & string]?: AggregateFunction[];
36
+ } & {
37
+ _count?: true;
38
+ };
39
+ /** Result shape — nested column.function values + optional row count */
40
+ export type AggregateResult<TSpec> = {
41
+ [K in keyof TSpec as K extends '_count' ? never : K]: K extends string ? TSpec[K] extends AggregateFunction[] ? {
42
+ [F in TSpec[K][number]]: number | null;
43
+ } : never : never;
44
+ } & (TSpec extends {
45
+ _count: true;
46
+ } ? {
47
+ _count: number;
48
+ } : object);
49
+ /** Result type for grouped aggregates — array of group rows */
50
+ export type GroupedAggregateResult<TSpec extends AggregateSpec, TRow extends Row, TKeys extends (keyof TRow & string)[]> = (AggregateResult<TSpec> & Pick<TRow, TKeys[number]>)[];
31
51
  export interface QueryBuilder<TRow extends Row = Row> {
32
52
  where(field: keyof TRow & string, op: WhereOp, value: unknown): QueryBuilder<TRow>;
33
53
  orderBy(field: keyof TRow & string, direction?: 'asc' | 'desc'): QueryBuilder<TRow>;
@@ -35,6 +55,13 @@ export interface QueryBuilder<TRow extends Row = Row> {
35
55
  skip(count: number): QueryBuilder<TRow>;
36
56
  subscribe(callback: (rows: TRow[]) => void): () => void;
37
57
  get(): Promise<TRow[]>;
58
+ aggregate<TSpec extends AggregateSpec<TRow>>(spec: TSpec): Promise<AggregateResult<TSpec>>;
59
+ aggregate<TSpec extends AggregateSpec<TRow>>(spec: TSpec, options: {
60
+ groupBy: [];
61
+ }): Promise<AggregateResult<TSpec>>;
62
+ aggregate<TSpec extends AggregateSpec<TRow>, TKeys extends (keyof TRow & string)[]>(spec: TSpec, options: {
63
+ groupBy: TKeys;
64
+ }): Promise<GroupedAggregateResult<TSpec, TRow, TKeys>>;
38
65
  }
39
66
  export interface DocRef<TRow extends Row = Row> {
40
67
  get(): Promise<TRow | null>;
@@ -52,6 +79,13 @@ export interface CollectionRef<TRow extends Row = Row> {
52
79
  subscribe(callback: (rows: TRow[]) => void): () => void;
53
80
  get(): Promise<TRow[]>;
54
81
  count(callback: (count: number) => void): () => void;
82
+ aggregate<TSpec extends AggregateSpec<TRow>>(spec: TSpec): Promise<AggregateResult<TSpec>>;
83
+ aggregate<TSpec extends AggregateSpec<TRow>>(spec: TSpec, options: {
84
+ groupBy: [];
85
+ }): Promise<AggregateResult<TSpec>>;
86
+ aggregate<TSpec extends AggregateSpec<TRow>, TKeys extends (keyof TRow & string)[]>(spec: TSpec, options: {
87
+ groupBy: TKeys;
88
+ }): Promise<GroupedAggregateResult<TSpec, TRow, TKeys>>;
55
89
  }
56
90
  export interface InputTableDB {
57
91
  collection<TRow extends Row = Row>(name: string): CollectionRef<TRow>;
@@ -1,6 +1,11 @@
1
1
  /**
2
2
  * Shared utilities for the input-table-db SDK.
3
3
  */
4
+ /**
5
+ * Builds request headers for solution branch context.
6
+ * Reads from SDK config first, falls back to cookies (browser only).
7
+ */
8
+ export declare function getSolutionBranchHeaders(): Record<string, string>;
4
9
  export declare class InputTableError extends Error {
5
10
  status: number;
6
11
  constructor(message: string, status: number);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bluecopa/core",
3
- "version": "0.1.58",
3
+ "version": "0.1.59",
4
4
  "type": "module",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",