@bonnard/sdk 0.1.3 → 0.3.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/README.md CHANGED
@@ -20,13 +20,12 @@ const bon = createClient({
20
20
  });
21
21
 
22
22
  const { data } = await bon.query({
23
- cube: 'orders',
24
- measures: ['revenue', 'count'],
25
- dimensions: ['status'],
23
+ measures: ['orders.revenue', 'orders.count'],
24
+ dimensions: ['orders.status'],
26
25
  });
27
26
 
28
27
  console.log(data);
29
- // [{ revenue: 45000, count: 120, status: "completed" }, ...]
28
+ // [{ "orders.revenue": 45000, "orders.count": 120, "orders.status": "completed" }, ...]
30
29
  ```
31
30
 
32
31
  ### With token exchange (multi-tenant)
@@ -59,10 +58,9 @@ const bon = createClient({
59
58
  });
60
59
 
61
60
  const { data } = await bon.query({
62
- cube: 'orders',
63
- measures: ['revenue'],
61
+ measures: ['orders.revenue'],
64
62
  timeDimension: {
65
- dimension: 'created_at',
63
+ dimension: 'orders.created_at',
66
64
  granularity: 'month',
67
65
  dateRange: ['2025-01-01', '2025-12-31'],
68
66
  },
@@ -87,18 +85,17 @@ JSON query against the semantic layer.
87
85
 
88
86
  ```typescript
89
87
  const { data } = await bon.query({
90
- cube: 'orders',
91
- measures: ['revenue', 'count'],
92
- dimensions: ['product_category'],
88
+ measures: ['orders.revenue', 'orders.count'],
89
+ dimensions: ['orders.product_category'],
93
90
  filters: [
94
- { dimension: 'status', operator: 'equals', values: ['completed'] },
91
+ { dimension: 'orders.status', operator: 'equals', values: ['completed'] },
95
92
  ],
96
93
  timeDimension: {
97
- dimension: 'created_at',
94
+ dimension: 'orders.created_at',
98
95
  granularity: 'month',
99
96
  dateRange: ['2025-01-01', '2025-12-31'],
100
97
  },
101
- orderBy: { revenue: 'desc' },
98
+ orderBy: { 'orders.revenue': 'desc' },
102
99
  limit: 100,
103
100
  });
104
101
  ```
package/dist/client.d.ts CHANGED
@@ -1,14 +1,11 @@
1
1
  /**
2
2
  * Bonnard SDK — Client for querying semantic layer
3
3
  */
4
- import type { BonnardConfig, QueryOptions, QueryResult, SqlResult, CubeQuery, ExploreMeta, ExploreOptions } from './types.js';
4
+ import type { BonnardConfig, QueryOptions, QueryResult, SqlResult, CubeQuery, ExploreMeta, ExploreOptions, DashboardResult, DashboardListResult } from './types.js';
5
5
  export declare function createClient(config: BonnardConfig): {
6
6
  /**
7
7
  * Execute a JSON query against the semantic layer.
8
- *
9
- * Supports two modes:
10
- * - **Short names**: provide `cube` and use unqualified field names (e.g. `"revenue"`)
11
- * - **Fully-qualified names**: omit `cube` and use dot notation (e.g. `"orders.revenue"`)
8
+ * All field names must be fully qualified (e.g. "orders.revenue").
12
9
  */
13
10
  query<T = Record<string, unknown>>(options: QueryOptions): Promise<QueryResult<T>>;
14
11
  /**
@@ -25,4 +22,12 @@ export declare function createClient(config: BonnardConfig): {
25
22
  * By default returns only views (viewsOnly: true).
26
23
  */
27
24
  explore(options?: ExploreOptions): Promise<ExploreMeta>;
25
+ /**
26
+ * Fetch a single dashboard by slug.
27
+ */
28
+ getDashboard(slug: string): Promise<DashboardResult>;
29
+ /**
30
+ * List all dashboards for the current organization.
31
+ */
32
+ listDashboards(): Promise<DashboardListResult>;
28
33
  };
package/dist/client.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Bonnard SDK — Client for querying semantic layer
3
3
  */
4
- import { buildCubeQuery, simplifyResult, isCubeNativeFormat } from './query.js';
4
+ import { toCubeQuery } from './query.js';
5
5
  /**
6
6
  * Parse JWT expiry from the payload (base64url-decoded middle segment).
7
7
  * Returns the `exp` claim as a millisecond timestamp, or 0 if unparseable.
@@ -74,62 +74,15 @@ export function createClient(config) {
74
74
  }
75
75
  return res.json();
76
76
  }
77
- /**
78
- * Build a Cube-native query from QueryOptions that already use fully-qualified names.
79
- */
80
- function buildNativeQuery(options) {
81
- const cubeQuery = {};
82
- if (options.measures) {
83
- cubeQuery.measures = options.measures;
84
- }
85
- if (options.dimensions) {
86
- cubeQuery.dimensions = options.dimensions;
87
- }
88
- if (options.filters) {
89
- cubeQuery.filters = options.filters.map(f => ({
90
- member: f.dimension,
91
- operator: f.operator,
92
- values: f.values,
93
- }));
94
- }
95
- if (options.timeDimension) {
96
- cubeQuery.timeDimensions = [{
97
- dimension: options.timeDimension.dimension,
98
- granularity: options.timeDimension.granularity,
99
- dateRange: options.timeDimension.dateRange,
100
- }];
101
- }
102
- if (options.orderBy) {
103
- cubeQuery.order = Object.entries(options.orderBy).map(([key, dir]) => [key, dir]);
104
- }
105
- if (options.limit) {
106
- cubeQuery.limit = options.limit;
107
- }
108
- return cubeQuery;
109
- }
110
77
  return {
111
78
  /**
112
79
  * Execute a JSON query against the semantic layer.
113
- *
114
- * Supports two modes:
115
- * - **Short names**: provide `cube` and use unqualified field names (e.g. `"revenue"`)
116
- * - **Fully-qualified names**: omit `cube` and use dot notation (e.g. `"orders.revenue"`)
80
+ * All field names must be fully qualified (e.g. "orders.revenue").
117
81
  */
118
82
  async query(options) {
119
- let cubeQuery;
120
- if (isCubeNativeFormat(options)) {
121
- cubeQuery = buildNativeQuery(options);
122
- }
123
- else {
124
- if (!options.cube) {
125
- throw new Error('QueryOptions requires "cube" when using short field names. ' +
126
- 'Either set "cube" or use fully-qualified names (e.g. "orders.revenue").');
127
- }
128
- cubeQuery = buildCubeQuery(options);
129
- }
83
+ const cubeQuery = toCubeQuery(options);
130
84
  const result = await request('/api/cube/query', { query: cubeQuery });
131
- const simplifiedData = simplifyResult(result.data);
132
- return { data: simplifiedData, annotation: result.annotation };
85
+ return { data: result.data, annotation: result.annotation };
133
86
  },
134
87
  /**
135
88
  * Execute a raw Cube-native JSON query against the semantic layer.
@@ -137,8 +90,7 @@ export function createClient(config) {
137
90
  */
138
91
  async rawQuery(cubeQuery) {
139
92
  const result = await request('/api/cube/query', { query: cubeQuery });
140
- const simplifiedData = simplifyResult(result.data);
141
- return { data: simplifiedData, annotation: result.annotation };
93
+ return { data: result.data, annotation: result.annotation };
142
94
  },
143
95
  /**
144
96
  * Execute a SQL query against the semantic layer
@@ -158,5 +110,17 @@ export function createClient(config) {
158
110
  }
159
111
  return meta;
160
112
  },
113
+ /**
114
+ * Fetch a single dashboard by slug.
115
+ */
116
+ async getDashboard(slug) {
117
+ return requestGet(`/api/dashboards/${encodeURIComponent(slug)}`);
118
+ },
119
+ /**
120
+ * List all dashboards for the current organization.
121
+ */
122
+ async listDashboards() {
123
+ return requestGet('/api/dashboards');
124
+ },
161
125
  };
162
126
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { createClient } from './client.js';
2
- export { buildCubeQuery, simplifyResult, isCubeNativeFormat } from './query.js';
3
- export type { BonnardConfig, QueryOptions, QueryResult, SqlResult, Filter, TimeDimension, InferQueryResult, CubeQuery, ExploreMeta, CubeMetaItem, CubeFieldMeta, CubeSegmentMeta, ExploreOptions, } from './types.js';
2
+ export { toCubeQuery } from './query.js';
3
+ export type { BonnardConfig, QueryOptions, QueryResult, SqlResult, Filter, TimeDimension, InferQueryResult, CubeQuery, ExploreMeta, CubeMetaItem, CubeFieldMeta, CubeSegmentMeta, ExploreOptions, DashboardResult, DashboardListResult, } from './types.js';
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export { createClient } from './client.js';
2
- export { buildCubeQuery, simplifyResult, isCubeNativeFormat } from './query.js';
2
+ export { toCubeQuery } from './query.js';
package/dist/query.d.ts CHANGED
@@ -1,19 +1,9 @@
1
1
  /**
2
- * Bonnard SDK — Pure query-building functions (zero IO)
2
+ * Bonnard SDK — Query format conversion (zero IO)
3
3
  */
4
4
  import type { QueryOptions } from './types.js';
5
- /**
6
- * Detect whether a QueryOptions object uses Cube-native fully-qualified names
7
- * (i.e. "view.field" dot notation) rather than short names that need a cube prefix.
8
- */
9
- export declare function isCubeNativeFormat(options: QueryOptions): boolean;
10
5
  /**
11
6
  * Convert SDK QueryOptions into a Cube-native query object.
12
- * Handles cube-name prefixing for all field references.
13
- */
14
- export declare function buildCubeQuery(options: QueryOptions): Record<string, unknown>;
15
- /**
16
- * Simplify Cube response keys by removing the cube prefix.
17
- * e.g. { "orders.revenue": 100 } → { "revenue": 100 }
7
+ * All field names must be fully qualified (e.g. "orders.revenue").
18
8
  */
19
- export declare function simplifyResult<T = Record<string, unknown>>(data: Record<string, unknown>[]): T[];
9
+ export declare function toCubeQuery(options: QueryOptions): Record<string, unknown>;
package/dist/query.js CHANGED
@@ -1,69 +1,37 @@
1
1
  /**
2
- * Bonnard SDK — Pure query-building functions (zero IO)
2
+ * Bonnard SDK — Query format conversion (zero IO)
3
3
  */
4
- /**
5
- * Detect whether a QueryOptions object uses Cube-native fully-qualified names
6
- * (i.e. "view.field" dot notation) rather than short names that need a cube prefix.
7
- */
8
- export function isCubeNativeFormat(options) {
9
- const fields = [
10
- ...(options.measures ?? []),
11
- ...(options.dimensions ?? []),
12
- ...(options.filters?.map(f => f.dimension) ?? []),
13
- ...(options.timeDimension ? [options.timeDimension.dimension] : []),
14
- ];
15
- return fields.some(f => f.includes('.'));
16
- }
17
4
  /**
18
5
  * Convert SDK QueryOptions into a Cube-native query object.
19
- * Handles cube-name prefixing for all field references.
6
+ * All field names must be fully qualified (e.g. "orders.revenue").
20
7
  */
21
- export function buildCubeQuery(options) {
8
+ export function toCubeQuery(options) {
22
9
  const cubeQuery = {};
23
10
  if (options.measures) {
24
- cubeQuery.measures = options.measures.map(m => m.includes('.') ? m : `${options.cube}.${m}`);
11
+ cubeQuery.measures = options.measures;
25
12
  }
26
13
  if (options.dimensions) {
27
- cubeQuery.dimensions = options.dimensions.map(d => d.includes('.') ? d : `${options.cube}.${d}`);
14
+ cubeQuery.dimensions = options.dimensions;
28
15
  }
29
16
  if (options.filters) {
30
17
  cubeQuery.filters = options.filters.map(f => ({
31
- dimension: f.dimension.includes('.') ? f.dimension : `${options.cube}.${f.dimension}`,
18
+ member: f.dimension,
32
19
  operator: f.operator,
33
20
  values: f.values,
34
21
  }));
35
22
  }
36
23
  if (options.timeDimension) {
37
24
  cubeQuery.timeDimensions = [{
38
- dimension: options.timeDimension.dimension.includes('.')
39
- ? options.timeDimension.dimension
40
- : `${options.cube}.${options.timeDimension.dimension}`,
25
+ dimension: options.timeDimension.dimension,
41
26
  granularity: options.timeDimension.granularity,
42
27
  dateRange: options.timeDimension.dateRange,
43
28
  }];
44
29
  }
45
30
  if (options.orderBy) {
46
- cubeQuery.order = Object.entries(options.orderBy).map(([key, dir]) => [
47
- key.includes('.') ? key : `${options.cube}.${key}`,
48
- dir,
49
- ]);
31
+ cubeQuery.order = Object.entries(options.orderBy).map(([key, dir]) => [key, dir]);
50
32
  }
51
33
  if (options.limit) {
52
34
  cubeQuery.limit = options.limit;
53
35
  }
54
36
  return cubeQuery;
55
37
  }
56
- /**
57
- * Simplify Cube response keys by removing the cube prefix.
58
- * e.g. { "orders.revenue": 100 } → { "revenue": 100 }
59
- */
60
- export function simplifyResult(data) {
61
- return data.map(row => {
62
- const simplified = {};
63
- for (const [key, value] of Object.entries(row)) {
64
- const simpleName = key.includes('.') ? key.split('.').pop() : key;
65
- simplified[simpleName] = value;
66
- }
67
- return simplified;
68
- });
69
- }
package/dist/types.d.ts CHANGED
@@ -7,7 +7,6 @@ export interface BonnardConfig {
7
7
  baseUrl?: string;
8
8
  }
9
9
  export interface QueryOptions {
10
- cube?: string;
11
10
  measures?: string[];
12
11
  dimensions?: string[];
13
12
  filters?: Filter[];
@@ -95,3 +94,23 @@ export interface ExploreOptions {
95
94
  export type InferQueryResult<C extends string, M extends string[], D extends string[]> = {
96
95
  [K in M[number] | D[number]]: K extends M[number] ? number : string;
97
96
  };
97
+ export interface DashboardResult {
98
+ dashboard: {
99
+ slug: string;
100
+ title: string;
101
+ description: string | null;
102
+ content: string;
103
+ version: number;
104
+ created_at: string;
105
+ updated_at: string;
106
+ };
107
+ }
108
+ export interface DashboardListResult {
109
+ dashboards: Array<{
110
+ slug: string;
111
+ title: string;
112
+ description: string | null;
113
+ version: number;
114
+ updated_at: string;
115
+ }>;
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bonnard/sdk",
3
- "version": "0.1.3",
3
+ "version": "0.3.0",
4
4
  "description": "Bonnard SDK - query your semantic layer from any JavaScript or TypeScript app",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",