@bonnard/sdk 0.2.1 → 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 +10 -13
- package/dist/client.d.ts +1 -4
- package/dist/client.js +5 -53
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/query.d.ts +3 -13
- package/dist/query.js +8 -40
- package/dist/types.d.ts +0 -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
|
@@ -5,10 +5,7 @@ import type { BonnardConfig, QueryOptions, QueryResult, SqlResult, CubeQuery, Ex
|
|
|
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
|
/**
|
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
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { createClient } from './client.js';
|
|
2
|
-
export {
|
|
2
|
+
export { toCubeQuery } from './query.js';
|
|
3
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 {
|
|
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