@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.
Files changed (50) hide show
  1. package/LICENSE +201 -0
  2. package/dist/aggregations.d.ts +24 -0
  3. package/dist/aggregations.d.ts.map +1 -0
  4. package/dist/aggregations.js +41 -0
  5. package/dist/constants.d.ts +9 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/constants.js +13 -0
  8. package/dist/dataset.d.ts +33 -0
  9. package/dist/dataset.d.ts.map +1 -0
  10. package/dist/dataset.js +203 -0
  11. package/dist/executor.d.ts +39 -0
  12. package/dist/executor.d.ts.map +1 -0
  13. package/dist/executor.js +244 -0
  14. package/dist/field.d.ts +25 -0
  15. package/dist/field.d.ts.map +1 -0
  16. package/dist/field.js +35 -0
  17. package/dist/formulas.d.ts +25 -0
  18. package/dist/formulas.d.ts.map +1 -0
  19. package/dist/formulas.js +60 -0
  20. package/dist/index.d.ts +18 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +25 -0
  23. package/dist/measure.d.ts +10 -0
  24. package/dist/measure.d.ts.map +1 -0
  25. package/dist/measure.js +18 -0
  26. package/dist/query-builder-protocol.d.ts +36 -0
  27. package/dist/query-builder-protocol.d.ts.map +1 -0
  28. package/dist/query-builder-protocol.js +11 -0
  29. package/dist/query-helpers.d.ts +30 -0
  30. package/dist/query-helpers.d.ts.map +1 -0
  31. package/dist/query-helpers.js +55 -0
  32. package/dist/query-planner.d.ts +13 -0
  33. package/dist/query-planner.d.ts.map +1 -0
  34. package/dist/query-planner.js +87 -0
  35. package/dist/registry.d.ts +8 -0
  36. package/dist/registry.d.ts.map +1 -0
  37. package/dist/registry.js +25 -0
  38. package/dist/relationships.d.ts +44 -0
  39. package/dist/relationships.d.ts.map +1 -0
  40. package/dist/relationships.js +39 -0
  41. package/dist/sql-utils.d.ts +28 -0
  42. package/dist/sql-utils.d.ts.map +1 -0
  43. package/dist/sql-utils.js +38 -0
  44. package/dist/types.d.ts +214 -0
  45. package/dist/types.d.ts.map +1 -0
  46. package/dist/types.js +1 -0
  47. package/dist/validation.d.ts +28 -0
  48. package/dist/validation.d.ts.map +1 -0
  49. package/dist/validation.js +73 -0
  50. package/package.json +49 -0
@@ -0,0 +1,244 @@
1
+ /**
2
+ * MetricExecutor — resolves metrics to SQL and executes them.
3
+ *
4
+ * Supports two modes:
5
+ * 1. **Query builder** (recommended) — pass a `QueryBuilderFactoryLike` and the
6
+ * executor builds queries via the builder's fluent API, then calls `.execute()`.
7
+ * 2. **Raw adapter** (deprecated) — pass a `MetricAdapter` with a `rawQuery` function
8
+ * and the executor generates SQL strings manually.
9
+ *
10
+ * The executor stays DB-agnostic via duck-typed protocol interfaces.
11
+ */
12
+ import { validateSQLIdentifier } from './sql-utils.js';
13
+ import { validateFilterValue } from './validation.js';
14
+ import { applyAggregationSpec, appendOrderLimitOffset, buildDimensionSelectionPlan, resolveFilterField, resolveTenantFilterColumn, } from './query-planner.js';
15
+ // =============================================================================
16
+ // VALIDATION
17
+ // =============================================================================
18
+ function validateQuery(metric, query) {
19
+ const errors = [];
20
+ const ref = metric.__type === 'grained_metric_ref' ? metric.metric : metric;
21
+ const ds = ref.dataset;
22
+ const dimensionNames = Object.keys(ds.dimensions);
23
+ const filterNames = Object.keys(ds.filters).length > 0
24
+ ? Object.keys(ds.filters)
25
+ : dimensionNames;
26
+ const grain = metric.__type === 'grained_metric_ref' ? metric.grain : query.by;
27
+ const orderableFields = new Set([
28
+ ...(query.dimensions ?? []),
29
+ ref.name,
30
+ ...(grain ? ['period'] : []),
31
+ ]);
32
+ if (metric.__type === 'grained_metric_ref' && query.by && query.by !== metric.grain) {
33
+ errors.push(`Metric "${ref.name}" is already grained by "${metric.grain}" and cannot be queried with by="${query.by}".`);
34
+ }
35
+ // Validate dimensions
36
+ for (const dim of query.dimensions ?? []) {
37
+ if (!dimensionNames.includes(dim)) {
38
+ errors.push(`Unknown dimension "${dim}". Available: ${dimensionNames.join(', ')}`);
39
+ }
40
+ }
41
+ // Validate filters
42
+ for (const filter of query.filters ?? []) {
43
+ if (!filterNames.includes(filter.field)) {
44
+ errors.push(`Unknown filter field "${filter.field}". Available: ${filterNames.join(', ')}`);
45
+ continue;
46
+ }
47
+ const filterDefinition = ds.filters[filter.field];
48
+ if (filterDefinition?.operators && !filterDefinition.operators.includes(filter.operator)) {
49
+ errors.push(`Filter "${filter.field}" does not allow operator "${filter.operator}". Allowed: ${filterDefinition.operators.join(', ')}`);
50
+ continue;
51
+ }
52
+ const resolvedField = ds.filters[filter.field]?.field ?? filter.field;
53
+ const fieldType = ds.dimensions[resolvedField]?.fieldType;
54
+ if (fieldType) {
55
+ const filterError = validateFilterValue(filter, fieldType);
56
+ if (filterError) {
57
+ errors.push(filterError);
58
+ }
59
+ }
60
+ }
61
+ // Validate order by fields against the metric output shape
62
+ for (const order of query.orderBy ?? []) {
63
+ if (!orderableFields.has(order.field)) {
64
+ errors.push(`Unknown orderBy field "${order.field}". Available: ${Array.from(orderableFields).join(', ')}`);
65
+ }
66
+ }
67
+ // Validate grain requires timeKey
68
+ if (query.by && !ds.timeKey) {
69
+ errors.push(`Cannot use "by" grain — dataset "${ds.name}" has no timeKey.`);
70
+ }
71
+ // Validate limits
72
+ if (ds.limits) {
73
+ if (ds.limits.maxDimensions && (query.dimensions?.length ?? 0) > ds.limits.maxDimensions) {
74
+ errors.push(`Too many dimensions (${query.dimensions?.length}). Max: ${ds.limits.maxDimensions}`);
75
+ }
76
+ if (ds.limits.maxMeasures && 1 > ds.limits.maxMeasures) {
77
+ errors.push(`Too many measures (1). Max: ${ds.limits.maxMeasures}`);
78
+ }
79
+ if (ds.limits.maxFilters && (query.filters?.length ?? 0) > ds.limits.maxFilters) {
80
+ errors.push(`Too many filters (${query.filters?.length}). Max: ${ds.limits.maxFilters}`);
81
+ }
82
+ }
83
+ return { valid: errors.length === 0, errors };
84
+ }
85
+ export class MetricExecutor {
86
+ builderFactory;
87
+ constructor(options) {
88
+ this.builderFactory = options.builderFactory;
89
+ }
90
+ getBuilderFactory() {
91
+ return this.builderFactory;
92
+ }
93
+ /**
94
+ * Execute a metric query. Generates SQL, applies tenant/filter context, executes.
95
+ */
96
+ async run(metric, query = {}, context) {
97
+ const validation = this.validate(metric, query);
98
+ if (!validation.valid) {
99
+ throw new Error(`Invalid metric query: ${validation.errors.join('; ')}`);
100
+ }
101
+ const start = Date.now();
102
+ return this.runViaBuilder(metric, query, context, start);
103
+ }
104
+ /**
105
+ * Generate SQL without executing.
106
+ */
107
+ toSQL(metric, query = {}, context) {
108
+ const ref = metric.__type === 'grained_metric_ref' ? metric.metric : metric;
109
+ const grain = metric.__type === 'grained_metric_ref' ? metric.grain : query.by ?? undefined;
110
+ const spec = ref.spec;
111
+ if (spec.__type === 'derived_metric_spec') {
112
+ return this.buildDerivedSQLViaBuilder(ref, spec, query, grain, context).sql;
113
+ }
114
+ const builder = this.buildBaseQuery(ref, spec, ref.dataset, query, grain, context);
115
+ return builder.toSQLWithParams().sql;
116
+ }
117
+ /**
118
+ * Validate a metric query against the metric's contract.
119
+ */
120
+ validate(metric, query) {
121
+ return validateQuery(metric, query);
122
+ }
123
+ // ---------------------------------------------------------------------------
124
+ // Query builder path
125
+ // ---------------------------------------------------------------------------
126
+ async runViaBuilder(metric, query, context, start) {
127
+ const ref = metric.__type === 'grained_metric_ref' ? metric.metric : metric;
128
+ const grain = metric.__type === 'grained_metric_ref' ? metric.grain : query.by ?? undefined;
129
+ const spec = ref.spec;
130
+ const activeBuilderFactory = context?.runtime?.builderFactory ?? this.builderFactory;
131
+ if (spec.__type === 'derived_metric_spec') {
132
+ // Derived metrics: build CTE via builder, outer query via string, execute via rawQuery
133
+ const { sql, params } = this.buildDerivedSQLViaBuilder(ref, spec, query, grain, context);
134
+ const data = await activeBuilderFactory.rawQuery(sql, params);
135
+ const timingMs = Date.now() - start;
136
+ return { data, meta: { sql, timingMs, tenant: context?.runtime?.tenant?.id } };
137
+ }
138
+ // Base metrics: fully use the builder's execute()
139
+ const builder = this.buildBaseQuery(ref, spec, ref.dataset, query, grain, context);
140
+ const { sql } = builder.toSQLWithParams();
141
+ const data = await builder.execute();
142
+ const timingMs = Date.now() - start;
143
+ return { data, meta: { sql, timingMs, tenant: context?.runtime?.tenant?.id } };
144
+ }
145
+ buildBaseQuery(ref, spec, ds, query, grain, context) {
146
+ const activeBuilderFactory = context?.runtime?.builderFactory ?? this.builderFactory;
147
+ let qb = activeBuilderFactory.table(ds.source);
148
+ const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], grain);
149
+ if (selectParts.length > 0) {
150
+ qb = qb.select(selectParts);
151
+ }
152
+ // Aggregation (appends to select, auto-sets groupBy on non-agg columns)
153
+ qb = applyAggregationSpec(qb, ds, spec, ref.name);
154
+ // Explicit groupBy (ensures period + dims are grouped even if aggregation auto-groupBy misses them)
155
+ if (groupByParts.length > 0) {
156
+ qb = qb.groupBy(groupByParts);
157
+ }
158
+ // Tenant auto-injection
159
+ const tenantColumn = resolveTenantFilterColumn(ds, context);
160
+ const tenantId = context?.runtime?.tenant?.id;
161
+ const tenantHandledByBuilder = context?.runtime?.tenant?.handledByBuilder === true;
162
+ if (tenantId && tenantColumn && !tenantHandledByBuilder) {
163
+ qb = qb.where(tenantColumn, 'eq', tenantId);
164
+ }
165
+ // User filters
166
+ for (const filter of query.filters ?? []) {
167
+ const resolvedField = resolveFilterField(ds, filter.field);
168
+ qb = qb.where(resolvedField, filter.operator, filter.value);
169
+ }
170
+ // Order, limit, offset
171
+ qb = appendOrderLimitOffset(qb, query.orderBy, grain, query.limit, query.offset);
172
+ return qb;
173
+ }
174
+ buildDerivedSQLViaBuilder(ref, spec, query, grain, context) {
175
+ const activeBuilderFactory = context?.runtime?.builderFactory ?? this.builderFactory;
176
+ const ds = ref.dataset;
177
+ // Build the CTE inner query using the builder
178
+ let cteBuilder = activeBuilderFactory.table(ds.source);
179
+ const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], grain);
180
+ if (selectParts.length > 0) {
181
+ cteBuilder = cteBuilder.select(selectParts);
182
+ }
183
+ // Base aggregations
184
+ const refAliases = {};
185
+ for (const [alias, baseMetric] of Object.entries(spec.uses)) {
186
+ const baseSpec = baseMetric.spec;
187
+ if (baseSpec.__type !== 'aggregation_spec') {
188
+ throw new Error(`Derived metric "${ref.name}" references non-base metric "${alias}".`);
189
+ }
190
+ cteBuilder = applyAggregationSpec(cteBuilder, ds, baseSpec, alias);
191
+ refAliases[alias] = alias;
192
+ }
193
+ if (groupByParts.length > 0) {
194
+ cteBuilder = cteBuilder.groupBy(groupByParts);
195
+ }
196
+ // Filters on CTE
197
+ const tenantColumn = resolveTenantFilterColumn(ds, context);
198
+ const tenantId = context?.runtime?.tenant?.id;
199
+ const tenantHandledByBuilder = context?.runtime?.tenant?.handledByBuilder === true;
200
+ if (tenantId && tenantColumn && !tenantHandledByBuilder) {
201
+ cteBuilder = cteBuilder.where(tenantColumn, 'eq', tenantId);
202
+ }
203
+ for (const filter of query.filters ?? []) {
204
+ const resolvedField = resolveFilterField(ds, filter.field);
205
+ cteBuilder = cteBuilder.where(resolvedField, filter.operator, filter.value);
206
+ }
207
+ const { sql: cteSql, parameters: cteParams } = cteBuilder.toSQLWithParams();
208
+ // Outer query: trivial SELECT from the CTE — stays as string concat
209
+ // because table('base') would fail schema typing
210
+ const outerSelectParts = [];
211
+ if (grain)
212
+ outerSelectParts.push('period');
213
+ for (const dim of query.dimensions ?? []) {
214
+ validateSQLIdentifier(dim, 'dimension name');
215
+ outerSelectParts.push(dim);
216
+ }
217
+ const formulaExpr = spec.formula(refAliases);
218
+ validateSQLIdentifier(ref.name, 'metric name');
219
+ outerSelectParts.push(`${formulaExpr.toSQL()} AS ${ref.name}`);
220
+ let sql = `WITH base AS (${cteSql}) SELECT ${outerSelectParts.join(', ')} FROM base`;
221
+ // ORDER BY
222
+ if (query.orderBy && query.orderBy.length > 0) {
223
+ const orderParts = query.orderBy.map(o => {
224
+ validateSQLIdentifier(o.field, 'order by field');
225
+ return `${o.field} ${o.direction.toUpperCase()}`;
226
+ });
227
+ sql += ` ORDER BY ${orderParts.join(', ')}`;
228
+ }
229
+ else if (grain) {
230
+ sql += ' ORDER BY period';
231
+ }
232
+ if (query.limit != null) {
233
+ // Ensure limit is a safe integer
234
+ const limit = Math.floor(Math.abs(query.limit));
235
+ sql += ` LIMIT ${limit}`;
236
+ }
237
+ if (query.offset != null) {
238
+ // Ensure offset is a safe integer
239
+ const offset = Math.floor(Math.abs(query.offset));
240
+ sql += ` OFFSET ${offset}`;
241
+ }
242
+ return { sql, params: cteParams };
243
+ }
244
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Dimension helpers — lightweight semantic markers for dataset dimensions.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { dimension } from '@hypequery/serve';
7
+ *
8
+ * const Orders = dataset("orders", {
9
+ * source: "orders",
10
+ * dimensions: {
11
+ * id: dimension.string(),
12
+ * amount: dimension.number({ label: "Amount" }),
13
+ * createdAt: dimension.timestamp({ label: "Created At" }),
14
+ * },
15
+ * });
16
+ * ```
17
+ */
18
+ import type { DimensionDefinition, DimensionOptions } from './types.js';
19
+ export declare const dimension: {
20
+ readonly string: (opts?: DimensionOptions) => DimensionDefinition<"string">;
21
+ readonly number: (opts?: DimensionOptions) => DimensionDefinition<"number">;
22
+ readonly boolean: (opts?: DimensionOptions) => DimensionDefinition<"boolean">;
23
+ readonly timestamp: (opts?: DimensionOptions) => DimensionDefinition<"timestamp">;
24
+ };
25
+ //# sourceMappingURL=field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAa,MAAM,YAAY,CAAC;AAenF,eAAO,MAAM,SAAS;6BAZL,gBAAgB;6BAAhB,gBAAgB;8BAAhB,gBAAgB;gCAAhB,gBAAgB;CAiBvB,CAAC"}
package/dist/field.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Dimension helpers — lightweight semantic markers for dataset dimensions.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { dimension } from '@hypequery/serve';
7
+ *
8
+ * const Orders = dataset("orders", {
9
+ * source: "orders",
10
+ * dimensions: {
11
+ * id: dimension.string(),
12
+ * amount: dimension.number({ label: "Amount" }),
13
+ * createdAt: dimension.timestamp({ label: "Created At" }),
14
+ * },
15
+ * });
16
+ * ```
17
+ */
18
+ function createFieldHelper(fieldType) {
19
+ return (opts) => ({
20
+ __type: 'field_definition',
21
+ fieldType,
22
+ label: opts?.label,
23
+ description: opts?.description,
24
+ column: opts?.column,
25
+ sql: opts?.sql,
26
+ filterable: opts?.filterable,
27
+ groupable: opts?.groupable,
28
+ });
29
+ }
30
+ export const dimension = {
31
+ string: createFieldHelper('string'),
32
+ number: createFieldHelper('number'),
33
+ boolean: createFieldHelper('boolean'),
34
+ timestamp: createFieldHelper('timestamp'),
35
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Formula helpers for derived metrics.
3
+ *
4
+ * These are symbolic — they build FormulaExpr objects that get compiled to SQL
5
+ * by the MetricExecutor. They do not produce raw SQL strings directly.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const avgOrderValue = Orders.metric("avgOrderValue", {
10
+ * uses: { revenue: totalRevenue, orders: orderCount },
11
+ * formula: ({ revenue, orders }) => divide(revenue, nullIfZero(orders)),
12
+ * });
13
+ * ```
14
+ */
15
+ import type { FormulaExpr } from './types.js';
16
+ export declare function divide(a: string | FormulaExpr, b: string | FormulaExpr): FormulaExpr;
17
+ export declare function multiply(a: string | FormulaExpr, b: string | FormulaExpr): FormulaExpr;
18
+ export declare function subtract(a: string | FormulaExpr, b: string | FormulaExpr): FormulaExpr;
19
+ export declare function add(a: string | FormulaExpr, b: string | FormulaExpr): FormulaExpr;
20
+ export declare function nullIfZero(a: string | FormulaExpr): FormulaExpr;
21
+ export declare function coalesce(a: string | FormulaExpr, fallback: number | string | FormulaExpr): FormulaExpr;
22
+ export declare function round(a: string | FormulaExpr, decimals?: number): FormulaExpr;
23
+ export declare function floor(a: string | FormulaExpr): FormulaExpr;
24
+ export declare function ceil(a: string | FormulaExpr): FormulaExpr;
25
+ //# sourceMappingURL=formulas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formulas.d.ts","sourceRoot":"","sources":["../src/formulas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAgB9C,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAEpF;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAEtF;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAEtF;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAEjF;AAMD,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAE/D;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,CAGtG;AAMD,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,QAAQ,GAAE,MAAU,GAAG,WAAW,CAEhF;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAE1D;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAEzD"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Formula helpers for derived metrics.
3
+ *
4
+ * These are symbolic — they build FormulaExpr objects that get compiled to SQL
5
+ * by the MetricExecutor. They do not produce raw SQL strings directly.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const avgOrderValue = Orders.metric("avgOrderValue", {
10
+ * uses: { revenue: totalRevenue, orders: orderCount },
11
+ * formula: ({ revenue, orders }) => divide(revenue, nullIfZero(orders)),
12
+ * });
13
+ * ```
14
+ */
15
+ function expr(sqlFn) {
16
+ return { __type: 'formula_expr', toSQL: sqlFn };
17
+ }
18
+ /** Check if a value is a FormulaExpr. */
19
+ function resolveArg(a) {
20
+ if (typeof a === 'string')
21
+ return a;
22
+ return a.toSQL();
23
+ }
24
+ // ---------------------------------------------------------------------------
25
+ // Arithmetic
26
+ // ---------------------------------------------------------------------------
27
+ export function divide(a, b) {
28
+ return expr(() => `(${resolveArg(a)}) / (${resolveArg(b)})`);
29
+ }
30
+ export function multiply(a, b) {
31
+ return expr(() => `(${resolveArg(a)}) * (${resolveArg(b)})`);
32
+ }
33
+ export function subtract(a, b) {
34
+ return expr(() => `(${resolveArg(a)}) - (${resolveArg(b)})`);
35
+ }
36
+ export function add(a, b) {
37
+ return expr(() => `(${resolveArg(a)}) + (${resolveArg(b)})`);
38
+ }
39
+ // ---------------------------------------------------------------------------
40
+ // Null handling
41
+ // ---------------------------------------------------------------------------
42
+ export function nullIfZero(a) {
43
+ return expr(() => `NULLIF(${resolveArg(a)}, 0)`);
44
+ }
45
+ export function coalesce(a, fallback) {
46
+ const fb = typeof fallback === 'number' ? String(fallback) : resolveArg(fallback);
47
+ return expr(() => `COALESCE(${resolveArg(a)}, ${fb})`);
48
+ }
49
+ // ---------------------------------------------------------------------------
50
+ // Rounding
51
+ // ---------------------------------------------------------------------------
52
+ export function round(a, decimals = 0) {
53
+ return expr(() => `ROUND(${resolveArg(a)}, ${decimals})`);
54
+ }
55
+ export function floor(a) {
56
+ return expr(() => `FLOOR(${resolveArg(a)})`);
57
+ }
58
+ export function ceil(a) {
59
+ return expr(() => `CEIL(${resolveArg(a)})`);
60
+ }
@@ -0,0 +1,18 @@
1
+ export { dataset } from './dataset.js';
2
+ export { dimension } from './field.js';
3
+ export { measure } from './measure.js';
4
+ export { belongsTo, hasMany, hasOne } from './relationships.js';
5
+ export { sum, count, countDistinct, avg, min, max } from './aggregations.js';
6
+ export { divide, multiply, subtract, add, nullIfZero, coalesce, round, floor, ceil, } from './formulas.js';
7
+ export { eq, neq, gt, gte, lt, lte, inList, notInList, between, like, asc, desc, filter, order, } from './query-helpers.js';
8
+ export { createDatasetRegistry } from './registry.js';
9
+ export { MetricExecutor } from './executor.js';
10
+ export type { MetricExecutorOptions } from './executor.js';
11
+ export type { ValidationResult } from './validation.js';
12
+ export { validateFilterValue, matchesFieldType } from './validation.js';
13
+ export type { QueryBuilderLike, QueryBuilderFactoryLike } from './query-builder-protocol.js';
14
+ export { validateSQLIdentifier, isSafeSQLIdentifier, quoteSQLIdentifier } from './sql-utils.js';
15
+ export { GRAIN_FUNCTIONS } from './constants.js';
16
+ export { resolveDimensionExpression, resolveFilterField, buildDimensionSelectionPlan, applyAggregationSpec, applyMeasureDefinition, appendOrderLimitOffset, resolveTenantFilterColumn, } from './query-planner.js';
17
+ export type { FieldType, DimensionType, DimensionOptions, DimensionDefinition, MeasureOptions, MeasureDefinition, InferDimensionType, RelationshipKind, RelationshipDefinition, AggregationType, MeasureAggregation, AggregationSpec, FormulaExpr, DerivedMetricSpec, TimeGrain, MetricRef, GrainedMetricRef, MetricContract, MetricFilter, MetricOrderBy, MetricQuery, MetricResultMeta, MetricResult, MetricHandle, ExecutionContext, SemanticExecutionRuntime, SemanticTenantRuntime, SemanticFilterDefinition, SemanticFiltersDefinition, DatasetConfig, DatasetQueryConfig, DatasetQueryContract, DatasetQueryRef, DatasetLimits, DatasetInstance, BaseMetricConfig, DerivedMetricConfig, DatasetRegistryInstance, DatasetFieldNames, } from './types.js';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGhE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAG7E,OAAO,EACL,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAC/B,UAAU,EAAE,QAAQ,EACpB,KAAK,EAAE,KAAK,EAAE,IAAI,GACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EACzB,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAChC,GAAG,EAAE,IAAI,EACT,MAAM,EAAE,KAAK,GACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAG3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxE,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAG7F,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGhG,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAClB,2BAA2B,EAC3B,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACV,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ // Dataset definition
2
+ export { dataset } from './dataset.js';
3
+ // Dimension helpers
4
+ export { dimension } from './field.js';
5
+ // Measure helpers
6
+ export { measure } from './measure.js';
7
+ // Relationship helpers
8
+ export { belongsTo, hasMany, hasOne } from './relationships.js';
9
+ // Aggregation helpers
10
+ export { sum, count, countDistinct, avg, min, max } from './aggregations.js';
11
+ // Formula helpers
12
+ export { divide, multiply, subtract, add, nullIfZero, coalesce, round, floor, ceil, } from './formulas.js';
13
+ // Semantic query helpers
14
+ export { eq, neq, gt, gte, lt, lte, inList, notInList, between, like, asc, desc, filter, order, } from './query-helpers.js';
15
+ // Registry
16
+ export { createDatasetRegistry } from './registry.js';
17
+ // Executor
18
+ export { MetricExecutor } from './executor.js';
19
+ export { validateFilterValue, matchesFieldType } from './validation.js';
20
+ // SQL utilities
21
+ export { validateSQLIdentifier, isSafeSQLIdentifier, quoteSQLIdentifier } from './sql-utils.js';
22
+ // Constants
23
+ export { GRAIN_FUNCTIONS } from './constants.js';
24
+ // Query planning helpers
25
+ export { resolveDimensionExpression, resolveFilterField, buildDimensionSelectionPlan, applyAggregationSpec, applyMeasureDefinition, appendOrderLimitOffset, resolveTenantFilterColumn, } from './query-planner.js';
@@ -0,0 +1,10 @@
1
+ import type { MeasureDefinition, MeasureOptions } from './types.js';
2
+ export declare const measure: {
3
+ readonly sum: (field: string, opts?: MeasureOptions) => MeasureDefinition;
4
+ readonly count: (field: string, opts?: MeasureOptions) => MeasureDefinition;
5
+ readonly countDistinct: (field: string, opts?: MeasureOptions) => MeasureDefinition;
6
+ readonly avg: (field: string, opts?: MeasureOptions) => MeasureDefinition;
7
+ readonly min: (field: string, opts?: MeasureOptions) => MeasureDefinition;
8
+ readonly max: (field: string, opts?: MeasureOptions) => MeasureDefinition;
9
+ };
10
+ //# sourceMappingURL=measure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../src/measure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAsB,MAAM,YAAY,CAAC;AAaxF,eAAO,MAAM,OAAO;0BAVH,MAAM,SAAS,cAAc,KAAG,iBAAiB;4BAAjD,MAAM,SAAS,cAAc,KAAG,iBAAiB;oCAAjD,MAAM,SAAS,cAAc,KAAG,iBAAiB;0BAAjD,MAAM,SAAS,cAAc,KAAG,iBAAiB;0BAAjD,MAAM,SAAS,cAAc,KAAG,iBAAiB;0BAAjD,MAAM,SAAS,cAAc,KAAG,iBAAiB;CAiBxD,CAAC"}
@@ -0,0 +1,18 @@
1
+ function createMeasureHelper(aggregation) {
2
+ return (field, opts) => ({
3
+ __type: 'measure_definition',
4
+ aggregation,
5
+ field,
6
+ sql: opts?.sql,
7
+ label: opts?.label,
8
+ description: opts?.description,
9
+ });
10
+ }
11
+ export const measure = {
12
+ sum: createMeasureHelper('sum'),
13
+ count: createMeasureHelper('count'),
14
+ countDistinct: createMeasureHelper('countDistinct'),
15
+ avg: createMeasureHelper('avg'),
16
+ min: createMeasureHelper('min'),
17
+ max: createMeasureHelper('max'),
18
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Duck-typed protocol interfaces for query builders.
3
+ *
4
+ * These interfaces define the minimal contract that a query builder must satisfy
5
+ * to work with the MetricExecutor. The @hypequery/clickhouse `createQueryBuilder`
6
+ * return value satisfies `QueryBuilderFactoryLike` structurally — no explicit
7
+ * `implements` is needed.
8
+ *
9
+ * This keeps @hypequery/serve DB-agnostic while enabling first-class builder usage.
10
+ */
11
+ /** A chainable query builder instance (what `.table(name)` returns). */
12
+ export interface QueryBuilderLike {
13
+ select(columns: string[] | string): QueryBuilderLike;
14
+ sum(column: string, alias?: string): QueryBuilderLike;
15
+ count(column: string, alias?: string): QueryBuilderLike;
16
+ countDistinct(column: string, alias?: string): QueryBuilderLike;
17
+ avg(column: string, alias?: string): QueryBuilderLike;
18
+ min(column: string, alias?: string): QueryBuilderLike;
19
+ max(column: string, alias?: string): QueryBuilderLike;
20
+ where(column: string, operator: string, value: unknown): QueryBuilderLike;
21
+ groupBy(columns: string | string[]): QueryBuilderLike;
22
+ orderBy(column: string, direction?: 'ASC' | 'DESC'): QueryBuilderLike;
23
+ limit(count: number): QueryBuilderLike;
24
+ offset(count: number): QueryBuilderLike;
25
+ toSQLWithParams(): {
26
+ sql: string;
27
+ parameters: unknown[];
28
+ };
29
+ execute(): Promise<Record<string, unknown>[]>;
30
+ }
31
+ /** A query builder factory (what `createQueryBuilder(config)` returns). */
32
+ export interface QueryBuilderFactoryLike {
33
+ table(name: string): QueryBuilderLike;
34
+ rawQuery<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
35
+ }
36
+ //# sourceMappingURL=query-builder-protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-builder-protocol.d.ts","sourceRoot":"","sources":["../src/query-builder-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,gBAAgB,CAAC;IAGrD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACtD,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACxD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAChE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACtD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACtD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAGtD,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAAC;IAG1E,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,gBAAgB,CAAC;IAGtD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,gBAAgB,CAAC;IACtE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACvC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAGxC,eAAe,IAAI;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;IAC1D,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;CAC/C;AAED,2EAA2E;AAC3E,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACtC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;CACtF"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Duck-typed protocol interfaces for query builders.
3
+ *
4
+ * These interfaces define the minimal contract that a query builder must satisfy
5
+ * to work with the MetricExecutor. The @hypequery/clickhouse `createQueryBuilder`
6
+ * return value satisfies `QueryBuilderFactoryLike` structurally — no explicit
7
+ * `implements` is needed.
8
+ *
9
+ * This keeps @hypequery/serve DB-agnostic while enabling first-class builder usage.
10
+ */
11
+ export {};
@@ -0,0 +1,30 @@
1
+ import type { MetricFilter, MetricOrderBy } from './types.js';
2
+ export declare function eq<TField extends string>(field: TField, value: unknown): MetricFilter;
3
+ export declare function neq<TField extends string>(field: TField, value: unknown): MetricFilter;
4
+ export declare function gt<TField extends string>(field: TField, value: unknown): MetricFilter;
5
+ export declare function gte<TField extends string>(field: TField, value: unknown): MetricFilter;
6
+ export declare function lt<TField extends string>(field: TField, value: unknown): MetricFilter;
7
+ export declare function lte<TField extends string>(field: TField, value: unknown): MetricFilter;
8
+ export declare function inList<TField extends string>(field: TField, value: unknown[]): MetricFilter;
9
+ export declare function notInList<TField extends string>(field: TField, value: unknown[]): MetricFilter;
10
+ export declare function between<TField extends string>(field: TField, lower: unknown, upper: unknown): MetricFilter;
11
+ export declare function like<TField extends string>(field: TField, value: string): MetricFilter;
12
+ export declare function asc<TField extends string>(field: TField): MetricOrderBy;
13
+ export declare function desc<TField extends string>(field: TField): MetricOrderBy;
14
+ export declare const filter: {
15
+ readonly eq: typeof eq;
16
+ readonly neq: typeof neq;
17
+ readonly gt: typeof gt;
18
+ readonly gte: typeof gte;
19
+ readonly lt: typeof lt;
20
+ readonly lte: typeof lte;
21
+ readonly inList: typeof inList;
22
+ readonly notInList: typeof notInList;
23
+ readonly between: typeof between;
24
+ readonly like: typeof like;
25
+ };
26
+ export declare const order: {
27
+ readonly asc: typeof asc;
28
+ readonly desc: typeof desc;
29
+ };
30
+ //# sourceMappingURL=query-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-helpers.d.ts","sourceRoot":"","sources":["../src/query-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAU9D,wBAAgB,EAAE,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAErF;AAED,wBAAgB,GAAG,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAEtF;AAED,wBAAgB,EAAE,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAErF;AAED,wBAAgB,GAAG,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAEtF;AAED,wBAAgB,EAAE,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAErF;AAED,wBAAgB,GAAG,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAEtF;AAED,wBAAgB,MAAM,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,YAAY,CAE3F;AAED,wBAAgB,SAAS,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,YAAY,CAE9F;AAED,wBAAgB,OAAO,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAE1G;AAED,wBAAgB,IAAI,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAEtF;AAED,wBAAgB,GAAG,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAEvE;AAED,wBAAgB,IAAI,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAExE;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;CAWT,CAAC;AAEX,eAAO,MAAM,KAAK;;;CAGR,CAAC"}
@@ -0,0 +1,55 @@
1
+ function createFilter(field, operator, value) {
2
+ return { field, operator, value };
3
+ }
4
+ export function eq(field, value) {
5
+ return createFilter(field, 'eq', value);
6
+ }
7
+ export function neq(field, value) {
8
+ return createFilter(field, 'neq', value);
9
+ }
10
+ export function gt(field, value) {
11
+ return createFilter(field, 'gt', value);
12
+ }
13
+ export function gte(field, value) {
14
+ return createFilter(field, 'gte', value);
15
+ }
16
+ export function lt(field, value) {
17
+ return createFilter(field, 'lt', value);
18
+ }
19
+ export function lte(field, value) {
20
+ return createFilter(field, 'lte', value);
21
+ }
22
+ export function inList(field, value) {
23
+ return createFilter(field, 'in', value);
24
+ }
25
+ export function notInList(field, value) {
26
+ return createFilter(field, 'notIn', value);
27
+ }
28
+ export function between(field, lower, upper) {
29
+ return createFilter(field, 'between', [lower, upper]);
30
+ }
31
+ export function like(field, value) {
32
+ return createFilter(field, 'like', value);
33
+ }
34
+ export function asc(field) {
35
+ return { field, direction: 'asc' };
36
+ }
37
+ export function desc(field) {
38
+ return { field, direction: 'desc' };
39
+ }
40
+ export const filter = {
41
+ eq,
42
+ neq,
43
+ gt,
44
+ gte,
45
+ lt,
46
+ lte,
47
+ inList,
48
+ notInList,
49
+ between,
50
+ like,
51
+ };
52
+ export const order = {
53
+ asc,
54
+ desc,
55
+ };
@@ -0,0 +1,13 @@
1
+ import type { AggregationSpec, DatasetInstance, ExecutionContext, MeasureDefinition, MetricOrderBy, TimeGrain } from "./types.js";
2
+ import type { QueryBuilderLike } from "./query-builder-protocol.js";
3
+ export declare function resolveDimensionExpression(ds: DatasetInstance, dimensionName: string): string;
4
+ export declare function resolveFilterField(ds: DatasetInstance, filterField: string): string;
5
+ export declare function buildDimensionSelectionPlan(ds: DatasetInstance, dimensions: string[], grain: TimeGrain | undefined): {
6
+ selectParts: string[];
7
+ groupByParts: string[];
8
+ };
9
+ export declare function applyAggregationSpec(qb: QueryBuilderLike, ds: DatasetInstance, spec: AggregationSpec, alias: string): QueryBuilderLike;
10
+ export declare function applyMeasureDefinition(qb: QueryBuilderLike, ds: DatasetInstance, name: string, definition: MeasureDefinition): QueryBuilderLike;
11
+ export declare function appendOrderLimitOffset(qb: QueryBuilderLike, orderBy: MetricOrderBy[] | undefined, grain: TimeGrain | undefined, limit?: number, offset?: number): QueryBuilderLike;
12
+ export declare function resolveTenantFilterColumn(_ds: DatasetInstance, context?: ExecutionContext): string | undefined;
13
+ //# sourceMappingURL=query-planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-planner.d.ts","sourceRoot":"","sources":["../src/query-planner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,SAAS,EACV,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGpE,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,eAAe,EACnB,aAAa,EAAE,MAAM,GACpB,MAAM,CAGR;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,eAAe,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAGR;AAED,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,eAAe,EACnB,UAAU,EAAE,MAAM,EAAE,EACpB,KAAK,EAAE,SAAS,GAAG,SAAS,GAC3B;IAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,CAqBnD;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,gBAAgB,EACpB,EAAE,EAAE,eAAe,EACnB,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,MAAM,GACZ,gBAAgB,CAmBlB;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,gBAAgB,EACpB,EAAE,EAAE,eAAe,EACnB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,iBAAiB,GAC5B,gBAAgB,CAmBlB;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,gBAAgB,EACpB,OAAO,EAAE,aAAa,EAAE,GAAG,SAAS,EACpC,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,gBAAgB,CAiBlB;AAED,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EACpB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,SAAS,CAEpB"}