@bonnard/sdk 0.2.1 → 0.4.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 +10 -13
- package/dist/client.d.ts +9 -5
- package/dist/client.js +20 -53
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/query.d.ts +3 -13
- package/dist/query.js +8 -40
- package/dist/types.d.ts +20 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,13 +20,12 @@ const bon = createClient({
|
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
const { data } = await bon.query({
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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, DashboardResult, DashboardListResult } from './types.js';
|
|
4
|
+
import type { BonnardConfig, QueryOptions, QueryResult, SqlResult, CubeQuery, ExploreMeta, ExploreOptions, DashboardResult, DashboardListResult, DocsOptions, DocsTopicListResult, DocsTopicResult } 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
|
/**
|
|
@@ -33,4 +30,11 @@ export declare function createClient(config: BonnardConfig): {
|
|
|
33
30
|
* List all dashboards for the current organization.
|
|
34
31
|
*/
|
|
35
32
|
listDashboards(): Promise<DashboardListResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Access Bonnard documentation.
|
|
35
|
+
* - `docs()` — list all topics
|
|
36
|
+
* - `docs({ category: 'dashboards' })` — list topics in a category
|
|
37
|
+
* - `docs({ topic: 'dashboards.components' })` — get full markdown content
|
|
38
|
+
*/
|
|
39
|
+
docs(options?: DocsOptions): Promise<DocsTopicListResult | DocsTopicResult>;
|
|
36
40
|
};
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bonnard SDK — Client for querying semantic layer
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -170,5 +122,20 @@ export function createClient(config) {
|
|
|
170
122
|
async listDashboards() {
|
|
171
123
|
return requestGet('/api/dashboards');
|
|
172
124
|
},
|
|
125
|
+
/**
|
|
126
|
+
* Access Bonnard documentation.
|
|
127
|
+
* - `docs()` — list all topics
|
|
128
|
+
* - `docs({ category: 'dashboards' })` — list topics in a category
|
|
129
|
+
* - `docs({ topic: 'dashboards.components' })` — get full markdown content
|
|
130
|
+
*/
|
|
131
|
+
async docs(options) {
|
|
132
|
+
const params = new URLSearchParams();
|
|
133
|
+
if (options?.topic)
|
|
134
|
+
params.set('topic', options.topic);
|
|
135
|
+
else if (options?.category)
|
|
136
|
+
params.set('category', options.category);
|
|
137
|
+
const qs = params.toString();
|
|
138
|
+
return requestGet(`/api/docs${qs ? `?${qs}` : ''}`);
|
|
139
|
+
},
|
|
173
140
|
};
|
|
174
141
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { createClient } from './client.js';
|
|
2
|
-
export {
|
|
3
|
-
export type { BonnardConfig, QueryOptions, QueryResult, SqlResult, Filter, TimeDimension, InferQueryResult, CubeQuery, ExploreMeta, CubeMetaItem, CubeFieldMeta, CubeSegmentMeta, ExploreOptions, DashboardResult, DashboardListResult, } 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, DocsOptions, DocsTopicSummary, DocsTopicListResult, DocsTopicResult, } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { createClient } from './client.js';
|
|
2
|
-
export {
|
|
2
|
+
export { toCubeQuery } from './query.js';
|
package/dist/query.d.ts
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bonnard SDK —
|
|
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
|
-
*
|
|
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
|
|
9
|
+
export declare function toCubeQuery(options: QueryOptions): Record<string, unknown>;
|
package/dist/query.js
CHANGED
|
@@ -1,69 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bonnard SDK —
|
|
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
|
-
*
|
|
6
|
+
* All field names must be fully qualified (e.g. "orders.revenue").
|
|
20
7
|
*/
|
|
21
|
-
export function
|
|
8
|
+
export function toCubeQuery(options) {
|
|
22
9
|
const cubeQuery = {};
|
|
23
10
|
if (options.measures) {
|
|
24
|
-
cubeQuery.measures = options.measures
|
|
11
|
+
cubeQuery.measures = options.measures;
|
|
25
12
|
}
|
|
26
13
|
if (options.dimensions) {
|
|
27
|
-
cubeQuery.dimensions = options.dimensions
|
|
14
|
+
cubeQuery.dimensions = options.dimensions;
|
|
28
15
|
}
|
|
29
16
|
if (options.filters) {
|
|
30
17
|
cubeQuery.filters = options.filters.map(f => ({
|
|
31
|
-
|
|
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
|
|
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[];
|
|
@@ -115,3 +114,23 @@ export interface DashboardListResult {
|
|
|
115
114
|
updated_at: string;
|
|
116
115
|
}>;
|
|
117
116
|
}
|
|
117
|
+
export interface DocsOptions {
|
|
118
|
+
topic?: string;
|
|
119
|
+
category?: string;
|
|
120
|
+
}
|
|
121
|
+
export interface DocsTopicSummary {
|
|
122
|
+
id: string;
|
|
123
|
+
title: string;
|
|
124
|
+
description: string | null;
|
|
125
|
+
category: string;
|
|
126
|
+
}
|
|
127
|
+
export interface DocsTopicListResult {
|
|
128
|
+
topics: DocsTopicSummary[];
|
|
129
|
+
}
|
|
130
|
+
export interface DocsTopicResult {
|
|
131
|
+
topic: {
|
|
132
|
+
id: string;
|
|
133
|
+
title: string;
|
|
134
|
+
content: string;
|
|
135
|
+
};
|
|
136
|
+
}
|