@hypequery/datasets 0.0.0-canary-20260520183507
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/LICENSE +201 -0
- package/dist/aggregations.d.ts +24 -0
- package/dist/aggregations.d.ts.map +1 -0
- package/dist/aggregations.js +41 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +13 -0
- package/dist/dataset.d.ts +33 -0
- package/dist/dataset.d.ts.map +1 -0
- package/dist/dataset.js +203 -0
- package/dist/executor.d.ts +39 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +244 -0
- package/dist/field.d.ts +25 -0
- package/dist/field.d.ts.map +1 -0
- package/dist/field.js +35 -0
- package/dist/formulas.d.ts +25 -0
- package/dist/formulas.d.ts.map +1 -0
- package/dist/formulas.js +60 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/measure.d.ts +10 -0
- package/dist/measure.d.ts.map +1 -0
- package/dist/measure.js +18 -0
- package/dist/query-builder-protocol.d.ts +36 -0
- package/dist/query-builder-protocol.d.ts.map +1 -0
- package/dist/query-builder-protocol.js +11 -0
- package/dist/query-helpers.d.ts +30 -0
- package/dist/query-helpers.d.ts.map +1 -0
- package/dist/query-helpers.js +55 -0
- package/dist/query-planner.d.ts +13 -0
- package/dist/query-planner.d.ts.map +1 -0
- package/dist/query-planner.js +87 -0
- package/dist/registry.d.ts +8 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +25 -0
- package/dist/relationships.d.ts +44 -0
- package/dist/relationships.d.ts.map +1 -0
- package/dist/relationships.js +39 -0
- package/dist/sql-utils.d.ts +28 -0
- package/dist/sql-utils.d.ts.map +1 -0
- package/dist/sql-utils.js +38 -0
- package/dist/types.d.ts +214 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/validation.d.ts +28 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +73 -0
- package/package.json +49 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { GRAIN_FUNCTIONS } from "./constants.js";
|
|
2
|
+
export function resolveDimensionExpression(ds, dimensionName) {
|
|
3
|
+
const definition = ds.dimensions[dimensionName];
|
|
4
|
+
return definition?.sql ?? definition?.column ?? dimensionName;
|
|
5
|
+
}
|
|
6
|
+
export function resolveFilterField(ds, filterField) {
|
|
7
|
+
const resolvedField = ds.filters[filterField]?.field ?? filterField;
|
|
8
|
+
return resolveDimensionExpression(ds, resolvedField);
|
|
9
|
+
}
|
|
10
|
+
export function buildDimensionSelectionPlan(ds, dimensions, grain) {
|
|
11
|
+
const selectParts = [];
|
|
12
|
+
const groupByParts = [];
|
|
13
|
+
if (grain) {
|
|
14
|
+
const fn = GRAIN_FUNCTIONS[grain];
|
|
15
|
+
selectParts.push(`${fn}(${ds.timeKey}) AS period`);
|
|
16
|
+
groupByParts.push("period");
|
|
17
|
+
}
|
|
18
|
+
for (const dimensionName of dimensions) {
|
|
19
|
+
const expression = resolveDimensionExpression(ds, dimensionName);
|
|
20
|
+
if (expression === dimensionName) {
|
|
21
|
+
selectParts.push(dimensionName);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
selectParts.push(`${expression} AS ${dimensionName}`);
|
|
25
|
+
}
|
|
26
|
+
groupByParts.push(dimensionName);
|
|
27
|
+
}
|
|
28
|
+
return { selectParts, groupByParts };
|
|
29
|
+
}
|
|
30
|
+
export function applyAggregationSpec(qb, ds, spec, alias) {
|
|
31
|
+
const fieldOrExpr = resolveDimensionExpression(ds, spec.field);
|
|
32
|
+
switch (spec.aggregation) {
|
|
33
|
+
case "sum":
|
|
34
|
+
return qb.sum(fieldOrExpr, alias);
|
|
35
|
+
case "count":
|
|
36
|
+
return qb.count(fieldOrExpr, alias);
|
|
37
|
+
case "countDistinct":
|
|
38
|
+
return qb.countDistinct(fieldOrExpr, alias);
|
|
39
|
+
case "avg":
|
|
40
|
+
return qb.avg(fieldOrExpr, alias);
|
|
41
|
+
case "min":
|
|
42
|
+
return qb.min(fieldOrExpr, alias);
|
|
43
|
+
case "max":
|
|
44
|
+
return qb.max(fieldOrExpr, alias);
|
|
45
|
+
default:
|
|
46
|
+
throw new Error(`Unknown aggregation type: ${spec.aggregation}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function applyMeasureDefinition(qb, ds, name, definition) {
|
|
50
|
+
const fieldOrExpr = definition.sql ?? resolveDimensionExpression(ds, definition.field);
|
|
51
|
+
switch (definition.aggregation) {
|
|
52
|
+
case "sum":
|
|
53
|
+
return qb.sum(fieldOrExpr, name);
|
|
54
|
+
case "count":
|
|
55
|
+
return qb.count(fieldOrExpr, name);
|
|
56
|
+
case "countDistinct":
|
|
57
|
+
return qb.countDistinct(fieldOrExpr, name);
|
|
58
|
+
case "avg":
|
|
59
|
+
return qb.avg(fieldOrExpr, name);
|
|
60
|
+
case "min":
|
|
61
|
+
return qb.min(fieldOrExpr, name);
|
|
62
|
+
case "max":
|
|
63
|
+
return qb.max(fieldOrExpr, name);
|
|
64
|
+
default:
|
|
65
|
+
throw new Error(`Unsupported measure aggregation: ${definition.aggregation}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function appendOrderLimitOffset(qb, orderBy, grain, limit, offset) {
|
|
69
|
+
if (orderBy && orderBy.length > 0) {
|
|
70
|
+
for (const order of orderBy) {
|
|
71
|
+
qb = qb.orderBy(order.field, order.direction.toUpperCase());
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (grain) {
|
|
75
|
+
qb = qb.orderBy("period", "ASC");
|
|
76
|
+
}
|
|
77
|
+
if (limit != null) {
|
|
78
|
+
qb = qb.limit(limit);
|
|
79
|
+
}
|
|
80
|
+
if (offset != null) {
|
|
81
|
+
qb = qb.offset(offset);
|
|
82
|
+
}
|
|
83
|
+
return qb;
|
|
84
|
+
}
|
|
85
|
+
export function resolveTenantFilterColumn(_ds, context) {
|
|
86
|
+
return context?.runtime?.tenant?.column;
|
|
87
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DatasetRegistry — runtime registry of defined datasets.
|
|
3
|
+
*
|
|
4
|
+
* Used by MetricExecutor and serve() to discover datasets at startup.
|
|
5
|
+
*/
|
|
6
|
+
import type { DatasetRegistryInstance } from './types.js';
|
|
7
|
+
export declare function createDatasetRegistry(): DatasetRegistryInstance;
|
|
8
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAmB,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE3E,wBAAgB,qBAAqB,IAAI,uBAAuB,CAyB/D"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DatasetRegistry — runtime registry of defined datasets.
|
|
3
|
+
*
|
|
4
|
+
* Used by MetricExecutor and serve() to discover datasets at startup.
|
|
5
|
+
*/
|
|
6
|
+
export function createDatasetRegistry() {
|
|
7
|
+
const datasets = new Map();
|
|
8
|
+
return {
|
|
9
|
+
register(ds) {
|
|
10
|
+
if (datasets.has(ds.name)) {
|
|
11
|
+
throw new Error(`Dataset "${ds.name}" is already registered. Dataset names must be unique.`);
|
|
12
|
+
}
|
|
13
|
+
datasets.set(ds.name, ds);
|
|
14
|
+
},
|
|
15
|
+
get(name) {
|
|
16
|
+
return datasets.get(name);
|
|
17
|
+
},
|
|
18
|
+
getAll() {
|
|
19
|
+
return Array.from(datasets.values());
|
|
20
|
+
},
|
|
21
|
+
has(name) {
|
|
22
|
+
return datasets.has(name);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relationship helpers for dataset definitions.
|
|
3
|
+
*
|
|
4
|
+
* These helpers currently define semantic model metadata only. The shipped
|
|
5
|
+
* executor does not yet resolve relationship paths into joined dataset queries
|
|
6
|
+
* or cross-dataset metrics.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const Orders = dataset("orders", {
|
|
11
|
+
* source: "orders",
|
|
12
|
+
* fields: { ... },
|
|
13
|
+
* relationships: {
|
|
14
|
+
* customer: belongsTo(() => Customers, { from: "customerId", to: "id" }),
|
|
15
|
+
* },
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import type { RelationshipDefinition } from './types.js';
|
|
20
|
+
/** Many-to-one relationship (FK on this table). */
|
|
21
|
+
export declare function belongsTo(target: () => {
|
|
22
|
+
__type: 'dataset';
|
|
23
|
+
name: string;
|
|
24
|
+
}, join: {
|
|
25
|
+
from: string;
|
|
26
|
+
to: string;
|
|
27
|
+
}): RelationshipDefinition;
|
|
28
|
+
/** One-to-many relationship (FK on target table). */
|
|
29
|
+
export declare function hasMany(target: () => {
|
|
30
|
+
__type: 'dataset';
|
|
31
|
+
name: string;
|
|
32
|
+
}, join: {
|
|
33
|
+
from: string;
|
|
34
|
+
to: string;
|
|
35
|
+
}): RelationshipDefinition;
|
|
36
|
+
/** One-to-one relationship (FK on target table). */
|
|
37
|
+
export declare function hasOne(target: () => {
|
|
38
|
+
__type: 'dataset';
|
|
39
|
+
name: string;
|
|
40
|
+
}, join: {
|
|
41
|
+
from: string;
|
|
42
|
+
to: string;
|
|
43
|
+
}): RelationshipDefinition;
|
|
44
|
+
//# sourceMappingURL=relationships.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../src/relationships.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAoB,MAAM,YAAY,CAAC;AAgB3E,mDAAmD;AACnD,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACjD,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACjC,sBAAsB,CAExB;AAED,qDAAqD;AACrD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACjD,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACjC,sBAAsB,CAExB;AAED,oDAAoD;AACpD,wBAAgB,MAAM,CACpB,MAAM,EAAE,MAAM;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACjD,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACjC,sBAAsB,CAExB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relationship helpers for dataset definitions.
|
|
3
|
+
*
|
|
4
|
+
* These helpers currently define semantic model metadata only. The shipped
|
|
5
|
+
* executor does not yet resolve relationship paths into joined dataset queries
|
|
6
|
+
* or cross-dataset metrics.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const Orders = dataset("orders", {
|
|
11
|
+
* source: "orders",
|
|
12
|
+
* fields: { ... },
|
|
13
|
+
* relationships: {
|
|
14
|
+
* customer: belongsTo(() => Customers, { from: "customerId", to: "id" }),
|
|
15
|
+
* },
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
function createRelationship(kind, target, join) {
|
|
20
|
+
return {
|
|
21
|
+
__type: 'relationship',
|
|
22
|
+
kind,
|
|
23
|
+
target,
|
|
24
|
+
from: join.from,
|
|
25
|
+
to: join.to,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Many-to-one relationship (FK on this table). */
|
|
29
|
+
export function belongsTo(target, join) {
|
|
30
|
+
return createRelationship('belongsTo', target, join);
|
|
31
|
+
}
|
|
32
|
+
/** One-to-many relationship (FK on target table). */
|
|
33
|
+
export function hasMany(target, join) {
|
|
34
|
+
return createRelationship('hasMany', target, join);
|
|
35
|
+
}
|
|
36
|
+
/** One-to-one relationship (FK on target table). */
|
|
37
|
+
export function hasOne(target, join) {
|
|
38
|
+
return createRelationship('hasOne', target, join);
|
|
39
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL utility functions for safe SQL generation.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validates that a string is a safe SQL identifier (column/table name).
|
|
6
|
+
* Allows only alphanumeric characters and underscores, starting with letter or underscore.
|
|
7
|
+
*
|
|
8
|
+
* @param identifier - The identifier to validate
|
|
9
|
+
* @returns true if valid, false otherwise
|
|
10
|
+
*/
|
|
11
|
+
export declare function isSafeSQLIdentifier(identifier: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Validates and throws if identifier is not safe for SQL.
|
|
14
|
+
*
|
|
15
|
+
* @param identifier - The identifier to validate
|
|
16
|
+
* @param context - Context for error message (e.g., "dimension name", "field name")
|
|
17
|
+
* @throws Error if identifier is not safe
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateSQLIdentifier(identifier: string, context: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Quotes a SQL identifier for safe use in queries.
|
|
22
|
+
* Uses double quotes which is standard SQL.
|
|
23
|
+
*
|
|
24
|
+
* @param identifier - The identifier to quote
|
|
25
|
+
* @returns Quoted identifier
|
|
26
|
+
*/
|
|
27
|
+
export declare function quoteSQLIdentifier(identifier: string): string;
|
|
28
|
+
//# sourceMappingURL=sql-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-utils.d.ts","sourceRoot":"","sources":["../src/sql-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAO/E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAI7D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL utility functions for safe SQL generation.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validates that a string is a safe SQL identifier (column/table name).
|
|
6
|
+
* Allows only alphanumeric characters and underscores, starting with letter or underscore.
|
|
7
|
+
*
|
|
8
|
+
* @param identifier - The identifier to validate
|
|
9
|
+
* @returns true if valid, false otherwise
|
|
10
|
+
*/
|
|
11
|
+
export function isSafeSQLIdentifier(identifier) {
|
|
12
|
+
return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Validates and throws if identifier is not safe for SQL.
|
|
16
|
+
*
|
|
17
|
+
* @param identifier - The identifier to validate
|
|
18
|
+
* @param context - Context for error message (e.g., "dimension name", "field name")
|
|
19
|
+
* @throws Error if identifier is not safe
|
|
20
|
+
*/
|
|
21
|
+
export function validateSQLIdentifier(identifier, context) {
|
|
22
|
+
if (!isSafeSQLIdentifier(identifier)) {
|
|
23
|
+
throw new Error(`Invalid ${context}: "${identifier}". Must contain only letters, numbers, and underscores, ` +
|
|
24
|
+
`and start with a letter or underscore.`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Quotes a SQL identifier for safe use in queries.
|
|
29
|
+
* Uses double quotes which is standard SQL.
|
|
30
|
+
*
|
|
31
|
+
* @param identifier - The identifier to quote
|
|
32
|
+
* @returns Quoted identifier
|
|
33
|
+
*/
|
|
34
|
+
export function quoteSQLIdentifier(identifier) {
|
|
35
|
+
// Escape any existing double quotes by doubling them
|
|
36
|
+
const escaped = identifier.replace(/"/g, '""');
|
|
37
|
+
return `"${escaped}"`;
|
|
38
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { QueryBuilderFactoryLike } from './query-builder-protocol.js';
|
|
2
|
+
export type FieldType = 'string' | 'number' | 'boolean' | 'timestamp';
|
|
3
|
+
export type DimensionType = FieldType;
|
|
4
|
+
export interface DimensionOptions {
|
|
5
|
+
label?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
column?: string;
|
|
8
|
+
sql?: string;
|
|
9
|
+
filterable?: boolean;
|
|
10
|
+
groupable?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface DimensionDefinition<TType extends DimensionType = DimensionType> {
|
|
13
|
+
__type: 'field_definition';
|
|
14
|
+
fieldType: TType;
|
|
15
|
+
label?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
column?: string;
|
|
18
|
+
sql?: string;
|
|
19
|
+
filterable?: boolean;
|
|
20
|
+
groupable?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export type InferDimensionType<T extends DimensionDefinition> = T['fieldType'] extends 'string' ? string : T['fieldType'] extends 'number' ? number : T['fieldType'] extends 'boolean' ? boolean : T['fieldType'] extends 'timestamp' ? string : never;
|
|
23
|
+
export type RelationshipKind = 'belongsTo' | 'hasMany' | 'hasOne';
|
|
24
|
+
export interface RelationshipDefinition {
|
|
25
|
+
__type: 'relationship';
|
|
26
|
+
kind: RelationshipKind;
|
|
27
|
+
target: () => {
|
|
28
|
+
__type: 'dataset';
|
|
29
|
+
name: string;
|
|
30
|
+
};
|
|
31
|
+
from: string;
|
|
32
|
+
to: string;
|
|
33
|
+
}
|
|
34
|
+
export type AggregationType = 'sum' | 'count' | 'countDistinct' | 'avg' | 'min' | 'max';
|
|
35
|
+
export type MeasureAggregation = AggregationType;
|
|
36
|
+
export interface AggregationSpec {
|
|
37
|
+
__type: 'aggregation_spec';
|
|
38
|
+
aggregation: AggregationType;
|
|
39
|
+
field: string;
|
|
40
|
+
}
|
|
41
|
+
export interface MeasureOptions {
|
|
42
|
+
sql?: string;
|
|
43
|
+
label?: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface MeasureDefinition {
|
|
47
|
+
__type: 'measure_definition';
|
|
48
|
+
aggregation: MeasureAggregation;
|
|
49
|
+
field: string;
|
|
50
|
+
sql?: string;
|
|
51
|
+
label?: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
}
|
|
54
|
+
export type FormulaExpr = {
|
|
55
|
+
__type: 'formula_expr';
|
|
56
|
+
toSQL: () => string;
|
|
57
|
+
};
|
|
58
|
+
export interface MetricRef<TDatasetName extends string = string, TMetricName extends string = string> {
|
|
59
|
+
__type: 'metric_ref';
|
|
60
|
+
datasetName: TDatasetName;
|
|
61
|
+
name: TMetricName;
|
|
62
|
+
spec: AggregationSpec | DerivedMetricSpec;
|
|
63
|
+
label?: string;
|
|
64
|
+
description?: string;
|
|
65
|
+
dataset: DatasetInstance<any, any, any>;
|
|
66
|
+
by(grain: TimeGrain): GrainedMetricRef<TDatasetName, TMetricName>;
|
|
67
|
+
contract(): MetricContract;
|
|
68
|
+
}
|
|
69
|
+
export interface DerivedMetricSpec {
|
|
70
|
+
__type: 'derived_metric_spec';
|
|
71
|
+
uses: Record<string, MetricRef>;
|
|
72
|
+
formula: (inputs: Record<string, string>) => FormulaExpr;
|
|
73
|
+
}
|
|
74
|
+
export interface GrainedMetricRef<TDatasetName extends string = string, TMetricName extends string = string> {
|
|
75
|
+
__type: 'grained_metric_ref';
|
|
76
|
+
metric: MetricRef<TDatasetName, TMetricName>;
|
|
77
|
+
grain: TimeGrain;
|
|
78
|
+
contract(): MetricContract;
|
|
79
|
+
}
|
|
80
|
+
export type MetricHandle<TDatasetName extends string = string, TMetricName extends string = string> = MetricRef<TDatasetName, TMetricName> | GrainedMetricRef<TDatasetName, TMetricName>;
|
|
81
|
+
export type TimeGrain = 'day' | 'week' | 'month' | 'quarter' | 'year';
|
|
82
|
+
export interface MetricContract {
|
|
83
|
+
kind: 'metric' | 'derived_metric' | 'grained_metric';
|
|
84
|
+
name: string;
|
|
85
|
+
dataset: string;
|
|
86
|
+
valueType: 'number';
|
|
87
|
+
label?: string;
|
|
88
|
+
description?: string;
|
|
89
|
+
dimensions: string[];
|
|
90
|
+
measures?: string[];
|
|
91
|
+
filters: string[];
|
|
92
|
+
grains: TimeGrain[];
|
|
93
|
+
grain?: TimeGrain;
|
|
94
|
+
requires?: string[];
|
|
95
|
+
tenantScoped: boolean;
|
|
96
|
+
}
|
|
97
|
+
export interface MetricFilter {
|
|
98
|
+
field: string;
|
|
99
|
+
operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'notIn' | 'between' | 'like';
|
|
100
|
+
value: unknown;
|
|
101
|
+
}
|
|
102
|
+
export interface MetricOrderBy {
|
|
103
|
+
field: string;
|
|
104
|
+
direction: 'asc' | 'desc';
|
|
105
|
+
}
|
|
106
|
+
export interface MetricQuery {
|
|
107
|
+
dimensions?: string[];
|
|
108
|
+
filters?: MetricFilter[];
|
|
109
|
+
orderBy?: MetricOrderBy[];
|
|
110
|
+
limit?: number;
|
|
111
|
+
offset?: number;
|
|
112
|
+
by?: TimeGrain;
|
|
113
|
+
}
|
|
114
|
+
export interface MetricResultMeta {
|
|
115
|
+
timingMs?: number;
|
|
116
|
+
sql?: string;
|
|
117
|
+
tenant?: string;
|
|
118
|
+
}
|
|
119
|
+
export interface MetricResult<T = Record<string, unknown>> {
|
|
120
|
+
data: T[];
|
|
121
|
+
meta?: MetricResultMeta;
|
|
122
|
+
}
|
|
123
|
+
export interface SemanticTenantRuntime {
|
|
124
|
+
id: string;
|
|
125
|
+
column: string;
|
|
126
|
+
handledByBuilder: boolean;
|
|
127
|
+
}
|
|
128
|
+
export interface SemanticExecutionRuntime {
|
|
129
|
+
builderFactory?: QueryBuilderFactoryLike;
|
|
130
|
+
tenant?: SemanticTenantRuntime;
|
|
131
|
+
}
|
|
132
|
+
export interface ExecutionContext {
|
|
133
|
+
runtime?: SemanticExecutionRuntime;
|
|
134
|
+
}
|
|
135
|
+
export interface SemanticFilterDefinition {
|
|
136
|
+
__type: 'filter_definition';
|
|
137
|
+
field: string;
|
|
138
|
+
label?: string;
|
|
139
|
+
description?: string;
|
|
140
|
+
operators?: MetricFilter['operator'][];
|
|
141
|
+
}
|
|
142
|
+
export type SemanticFiltersDefinition = Record<string, SemanticFilterDefinition>;
|
|
143
|
+
export interface DatasetLimits {
|
|
144
|
+
maxDimensions?: number;
|
|
145
|
+
maxMeasures?: number;
|
|
146
|
+
maxFilters?: number;
|
|
147
|
+
maxResultSize?: number;
|
|
148
|
+
}
|
|
149
|
+
export interface BaseMetricConfig<TDimensions extends Record<string, DimensionDefinition> = Record<string, DimensionDefinition>, TMeasures extends Record<string, MeasureDefinition> = Record<string, MeasureDefinition>> {
|
|
150
|
+
measure: keyof TMeasures & string;
|
|
151
|
+
label?: string;
|
|
152
|
+
description?: string;
|
|
153
|
+
}
|
|
154
|
+
export interface DerivedMetricConfig {
|
|
155
|
+
uses: Record<string, MetricRef>;
|
|
156
|
+
formula: (inputs: Record<string, string>) => FormulaExpr;
|
|
157
|
+
label?: string;
|
|
158
|
+
description?: string;
|
|
159
|
+
}
|
|
160
|
+
export interface DatasetQueryConfig<TDimensions extends Record<string, DimensionDefinition> = Record<string, DimensionDefinition>, TMeasures extends Record<string, MeasureDefinition> = Record<string, MeasureDefinition>> {
|
|
161
|
+
dimensions?: Array<keyof TDimensions & string>;
|
|
162
|
+
measures?: Array<keyof TMeasures & string>;
|
|
163
|
+
filters?: MetricFilter[];
|
|
164
|
+
orderBy?: MetricOrderBy[];
|
|
165
|
+
limit?: number;
|
|
166
|
+
offset?: number;
|
|
167
|
+
by?: TimeGrain;
|
|
168
|
+
}
|
|
169
|
+
export interface DatasetQueryContract {
|
|
170
|
+
dataset: string;
|
|
171
|
+
dimensions: string[];
|
|
172
|
+
measures: string[];
|
|
173
|
+
filters: string[];
|
|
174
|
+
grains: TimeGrain[];
|
|
175
|
+
tenantScoped: boolean;
|
|
176
|
+
}
|
|
177
|
+
export interface DatasetQueryRef<TDimensions extends Record<string, DimensionDefinition> = Record<string, DimensionDefinition>, TMeasures extends Record<string, MeasureDefinition> = Record<string, MeasureDefinition>> {
|
|
178
|
+
__type: 'dataset_query_ref';
|
|
179
|
+
dataset: DatasetInstance<TDimensions, TMeasures, any>;
|
|
180
|
+
config: DatasetQueryConfig<TDimensions, TMeasures>;
|
|
181
|
+
contract(): DatasetQueryContract;
|
|
182
|
+
}
|
|
183
|
+
export interface DatasetConfig<TDimensions extends Record<string, DimensionDefinition> = Record<string, DimensionDefinition>, TMeasures extends Record<string, MeasureDefinition> = Record<string, MeasureDefinition>, TRelationships extends Record<string, RelationshipDefinition> = Record<string, never>> {
|
|
184
|
+
source: string;
|
|
185
|
+
tenantKey?: string;
|
|
186
|
+
timeKey?: string;
|
|
187
|
+
dimensions: TDimensions;
|
|
188
|
+
measures?: TMeasures;
|
|
189
|
+
filters?: SemanticFiltersDefinition;
|
|
190
|
+
relationships?: TRelationships;
|
|
191
|
+
limits?: DatasetLimits;
|
|
192
|
+
}
|
|
193
|
+
export interface DatasetInstance<TDimensions extends Record<string, DimensionDefinition> = Record<string, DimensionDefinition>, TMeasures extends Record<string, MeasureDefinition> = Record<string, MeasureDefinition>, TRelationships extends Record<string, RelationshipDefinition> = Record<string, never>> {
|
|
194
|
+
__type: 'dataset';
|
|
195
|
+
name: string;
|
|
196
|
+
source: string;
|
|
197
|
+
tenantKey?: string;
|
|
198
|
+
timeKey?: string;
|
|
199
|
+
dimensions: TDimensions;
|
|
200
|
+
measures: TMeasures;
|
|
201
|
+
filters: SemanticFiltersDefinition;
|
|
202
|
+
relationships: TRelationships;
|
|
203
|
+
limits?: DatasetLimits;
|
|
204
|
+
metric<TName extends string>(metricName: TName, metricConfig: BaseMetricConfig<TDimensions, TMeasures> | DerivedMetricConfig): MetricRef<string, TName>;
|
|
205
|
+
query(config: DatasetQueryConfig<TDimensions, TMeasures>): DatasetQueryRef<TDimensions, TMeasures>;
|
|
206
|
+
}
|
|
207
|
+
export interface DatasetRegistryInstance {
|
|
208
|
+
register(ds: DatasetInstance): void;
|
|
209
|
+
get(name: string): DatasetInstance | undefined;
|
|
210
|
+
getAll(): DatasetInstance[];
|
|
211
|
+
has(name: string): boolean;
|
|
212
|
+
}
|
|
213
|
+
export type DatasetFieldNames<TDataset extends DatasetInstance> = keyof TDataset['dimensions'] & string;
|
|
214
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAE3E,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB,CAAC,KAAK,SAAS,aAAa,GAAG,aAAa;IAC9E,MAAM,EAAE,kBAAkB,CAAC;IAC3B,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,mBAAmB,IAC1D,CAAC,CAAC,WAAW,CAAC,SAAS,QAAQ,GAAG,MAAM,GACxC,CAAC,CAAC,WAAW,CAAC,SAAS,QAAQ,GAAG,MAAM,GACxC,CAAC,CAAC,WAAW,CAAC,SAAS,SAAS,GAAG,OAAO,GAC1C,CAAC,CAAC,WAAW,CAAC,SAAS,WAAW,GAAG,MAAM,GAC3C,KAAK,CAAC;AAER,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM;QAAE,MAAM,EAAE,SAAS,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,OAAO,GAAG,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AACxF,MAAM,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,WAAW,EAAE,eAAe,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,WAAW,EAAE,kBAAkB,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,SAAS,CACxB,YAAY,SAAS,MAAM,GAAG,MAAM,EACpC,WAAW,SAAS,MAAM,GAAG,MAAM;IAEnC,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,EAAE,YAAY,CAAC;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAAC;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACxC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,gBAAgB,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAClE,QAAQ,IAAI,cAAc,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAChC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,WAAW,CAAC;CAC1D;AAED,MAAM,WAAW,gBAAgB,CAC/B,YAAY,SAAS,MAAM,GAAG,MAAM,EACpC,WAAW,SAAS,MAAM,GAAG,MAAM;IAEnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,EAAE,SAAS,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAC7C,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,IAAI,cAAc,CAAC;CAC5B;AAED,MAAM,MAAM,YAAY,CACtB,YAAY,SAAS,MAAM,GAAG,MAAM,EACpC,WAAW,SAAS,MAAM,GAAG,MAAM,IACjC,SAAS,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,gBAAgB,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AAEvF,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEtE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IAC3F,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACvD,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACvC,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;CACxC;AAED,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;AAEjF,MAAM,WAAW,aAAa;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB,CAC/B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAC7F,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAEvF,OAAO,EAAE,MAAM,SAAS,GAAG,MAAM,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAChC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,WAAW,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB,CACjC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAC7F,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAEvF,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC;IAC/C,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,eAAe,CAC9B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAC7F,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAEvF,MAAM,EAAE,mBAAmB,CAAC;IAC5B,OAAO,EAAE,eAAe,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IACtD,MAAM,EAAE,kBAAkB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACnD,QAAQ,IAAI,oBAAoB,CAAC;CAClC;AAED,MAAM,WAAW,aAAa,CAC5B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAC7F,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACvF,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAErF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,WAAW,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,yBAAyB,CAAC;IACpC,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,eAAe,CAC9B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAC7F,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACvF,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAErF,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,WAAW,CAAC;IACxB,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,yBAAyB,CAAC;IACnC,aAAa,EAAE,cAAc,CAAC;IAC9B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,MAAM,CAAC,KAAK,SAAS,MAAM,EACzB,UAAU,EAAE,KAAK,EACjB,YAAY,EAAE,gBAAgB,CAAC,WAAW,EAAE,SAAS,CAAC,GAAG,mBAAmB,GAC3E,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5B,KAAK,CAAC,MAAM,EAAE,kBAAkB,CAAC,WAAW,EAAE,SAAS,CAAC,GAAG,eAAe,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;CACpG;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IAC/C,MAAM,IAAI,eAAe,EAAE,CAAC;IAC5B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,iBAAiB,CAAC,QAAQ,SAAS,eAAe,IAC5D,MAAM,QAAQ,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared validation utilities for semantic layer queries.
|
|
3
|
+
*/
|
|
4
|
+
import type { FieldType, MetricFilter } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Result of a validation operation.
|
|
7
|
+
*/
|
|
8
|
+
export interface ValidationResult {
|
|
9
|
+
valid: boolean;
|
|
10
|
+
errors: string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Checks if a value matches the expected field type.
|
|
14
|
+
*
|
|
15
|
+
* @param fieldType - The expected field type
|
|
16
|
+
* @param value - The value to check
|
|
17
|
+
* @returns true if the value matches the field type
|
|
18
|
+
*/
|
|
19
|
+
export declare function matchesFieldType(fieldType: FieldType, value: unknown): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Validates a filter value against the expected field type.
|
|
22
|
+
*
|
|
23
|
+
* @param filter - The filter to validate
|
|
24
|
+
* @param fieldType - The expected field type
|
|
25
|
+
* @returns null if valid, error message if invalid
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateFilterValue(filter: MetricFilter, fieldType: FieldType): string | null;
|
|
28
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAY9E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CA0C7F"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared validation utilities for semantic layer queries.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Checks if a value matches the expected field type.
|
|
6
|
+
*
|
|
7
|
+
* @param fieldType - The expected field type
|
|
8
|
+
* @param value - The value to check
|
|
9
|
+
* @returns true if the value matches the field type
|
|
10
|
+
*/
|
|
11
|
+
export function matchesFieldType(fieldType, value) {
|
|
12
|
+
switch (fieldType) {
|
|
13
|
+
case 'string':
|
|
14
|
+
case 'timestamp':
|
|
15
|
+
return typeof value === 'string';
|
|
16
|
+
case 'number':
|
|
17
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
18
|
+
case 'boolean':
|
|
19
|
+
return typeof value === 'boolean';
|
|
20
|
+
default:
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Validates a filter value against the expected field type.
|
|
26
|
+
*
|
|
27
|
+
* @param filter - The filter to validate
|
|
28
|
+
* @param fieldType - The expected field type
|
|
29
|
+
* @returns null if valid, error message if invalid
|
|
30
|
+
*/
|
|
31
|
+
export function validateFilterValue(filter, fieldType) {
|
|
32
|
+
switch (filter.operator) {
|
|
33
|
+
case 'eq':
|
|
34
|
+
case 'neq':
|
|
35
|
+
return matchesFieldType(fieldType, filter.value)
|
|
36
|
+
? null
|
|
37
|
+
: `"${filter.operator}" expects a ${fieldType} value for field "${filter.field}".`;
|
|
38
|
+
case 'gt':
|
|
39
|
+
case 'gte':
|
|
40
|
+
case 'lt':
|
|
41
|
+
case 'lte':
|
|
42
|
+
if (fieldType === 'boolean') {
|
|
43
|
+
return `"${filter.operator}" is not supported for boolean field "${filter.field}".`;
|
|
44
|
+
}
|
|
45
|
+
return matchesFieldType(fieldType, filter.value)
|
|
46
|
+
? null
|
|
47
|
+
: `"${filter.operator}" expects a ${fieldType} value for field "${filter.field}".`;
|
|
48
|
+
case 'like':
|
|
49
|
+
if (fieldType !== 'string' && fieldType !== 'timestamp') {
|
|
50
|
+
return `"like" is only supported for string or timestamp field "${filter.field}".`;
|
|
51
|
+
}
|
|
52
|
+
return typeof filter.value === 'string'
|
|
53
|
+
? null
|
|
54
|
+
: `"like" expects a string value for field "${filter.field}".`;
|
|
55
|
+
case 'in':
|
|
56
|
+
case 'notIn':
|
|
57
|
+
if (!Array.isArray(filter.value) || filter.value.length === 0) {
|
|
58
|
+
return `"${filter.operator}" expects a non-empty array for field "${filter.field}".`;
|
|
59
|
+
}
|
|
60
|
+
return filter.value.every(value => matchesFieldType(fieldType, value))
|
|
61
|
+
? null
|
|
62
|
+
: `"${filter.operator}" expects ${fieldType} values for field "${filter.field}".`;
|
|
63
|
+
case 'between':
|
|
64
|
+
if (!Array.isArray(filter.value) || filter.value.length !== 2) {
|
|
65
|
+
return `"between" expects a two-item array for field "${filter.field}".`;
|
|
66
|
+
}
|
|
67
|
+
return filter.value.every(value => matchesFieldType(fieldType, value))
|
|
68
|
+
? null
|
|
69
|
+
: `"between" expects ${fieldType} values for field "${filter.field}".`;
|
|
70
|
+
default:
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|