@lssm/example.analytics-dashboard 0.0.0-canary-20251216035145 → 0.0.0-canary-20251217023603
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/dist/dashboard/dashboard.contracts.js +119 -1
- package/dist/dashboard/dashboard.enum.js +42 -1
- package/dist/dashboard/dashboard.presentation.js +76 -0
- package/dist/dashboard/dashboard.schema.js +235 -1
- package/dist/dashboard/index.js +5 -1
- package/dist/dashboard.feature.js +164 -1
- package/dist/docs/analytics-dashboard.docblock.js +60 -5
- package/dist/docs/index.js +1 -1
- package/dist/events.js +112 -0
- package/dist/example.js +50 -1
- package/dist/index.js +8 -1
- package/dist/query/index.js +5 -1
- package/dist/query/query.contracts.js +65 -1
- package/dist/query/query.enum.js +15 -1
- package/dist/query/query.presentation.js +50 -0
- package/dist/query/query.schema.js +156 -1
- package/dist/query-engine/index.js +185 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -1
|
@@ -1 +1,185 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/query-engine/index.ts
|
|
2
|
+
var InMemoryQueryCache = class {
|
|
3
|
+
cache = /* @__PURE__ */ new Map();
|
|
4
|
+
async get(key) {
|
|
5
|
+
const entry = this.cache.get(key);
|
|
6
|
+
if (!entry) return null;
|
|
7
|
+
if (entry.expiresAt < /* @__PURE__ */ new Date()) {
|
|
8
|
+
this.cache.delete(key);
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
...entry.result,
|
|
13
|
+
cached: true,
|
|
14
|
+
cachedAt: entry.expiresAt
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async set(key, result, ttlSeconds) {
|
|
18
|
+
const expiresAt = new Date(Date.now() + ttlSeconds * 1e3);
|
|
19
|
+
this.cache.set(key, {
|
|
20
|
+
result,
|
|
21
|
+
expiresAt
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async invalidate(pattern) {
|
|
25
|
+
const regex = new RegExp(pattern);
|
|
26
|
+
for (const key of this.cache.keys()) if (regex.test(key)) this.cache.delete(key);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var BasicQueryEngine = class {
|
|
30
|
+
cache;
|
|
31
|
+
constructor(cache) {
|
|
32
|
+
this.cache = cache ?? new InMemoryQueryCache();
|
|
33
|
+
}
|
|
34
|
+
async execute(definition, params) {
|
|
35
|
+
const startTime = Date.now();
|
|
36
|
+
const validation = this.validateQuery(definition);
|
|
37
|
+
if (!validation.valid) return {
|
|
38
|
+
data: [],
|
|
39
|
+
columns: [],
|
|
40
|
+
rowCount: 0,
|
|
41
|
+
executionTimeMs: Date.now() - startTime,
|
|
42
|
+
cached: false,
|
|
43
|
+
error: validation.errors.join(", ")
|
|
44
|
+
};
|
|
45
|
+
const cacheKey = this.buildCacheKey(definition, params);
|
|
46
|
+
const cachedResult = await this.cache.get(cacheKey);
|
|
47
|
+
if (cachedResult) return cachedResult;
|
|
48
|
+
let result;
|
|
49
|
+
switch (definition.type) {
|
|
50
|
+
case "AGGREGATION":
|
|
51
|
+
result = await this.executeAggregation(definition.aggregation, params);
|
|
52
|
+
break;
|
|
53
|
+
case "METRIC":
|
|
54
|
+
result = await this.executeMetric(definition.metricIds, params);
|
|
55
|
+
break;
|
|
56
|
+
case "SQL":
|
|
57
|
+
result = await this.executeSql(definition.sql, params);
|
|
58
|
+
break;
|
|
59
|
+
default: result = {
|
|
60
|
+
data: [],
|
|
61
|
+
columns: [],
|
|
62
|
+
rowCount: 0,
|
|
63
|
+
executionTimeMs: Date.now() - startTime,
|
|
64
|
+
cached: false,
|
|
65
|
+
error: `Unknown query type: ${definition.type}`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
result.executionTimeMs = Date.now() - startTime;
|
|
69
|
+
result.cached = false;
|
|
70
|
+
await this.cache.set(cacheKey, result, 300);
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
validateQuery(definition) {
|
|
74
|
+
const errors = [];
|
|
75
|
+
if (!definition.type) errors.push("Query type is required");
|
|
76
|
+
switch (definition.type) {
|
|
77
|
+
case "SQL":
|
|
78
|
+
if (!definition.sql) errors.push("SQL query is required for SQL type");
|
|
79
|
+
break;
|
|
80
|
+
case "METRIC":
|
|
81
|
+
if (!definition.metricIds || definition.metricIds.length === 0) errors.push("Metric IDs are required for METRIC type");
|
|
82
|
+
break;
|
|
83
|
+
case "AGGREGATION":
|
|
84
|
+
if (!definition.aggregation) errors.push("Aggregation definition is required for AGGREGATION type");
|
|
85
|
+
else {
|
|
86
|
+
if (!definition.aggregation.source) errors.push("Aggregation source is required");
|
|
87
|
+
if (!definition.aggregation.measures || definition.aggregation.measures.length === 0) errors.push("At least one measure is required");
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
valid: errors.length === 0,
|
|
93
|
+
errors
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
buildCacheKey(definition, params) {
|
|
97
|
+
return JSON.stringify({
|
|
98
|
+
definition,
|
|
99
|
+
params
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async executeAggregation(aggregation, params) {
|
|
103
|
+
const columns = [...aggregation.dimensions.map((d) => ({
|
|
104
|
+
name: d.name,
|
|
105
|
+
type: d.type === "NUMBER" ? "NUMBER" : d.type === "TIME" ? "DATE" : "STRING",
|
|
106
|
+
label: d.name
|
|
107
|
+
})), ...aggregation.measures.map((m) => ({
|
|
108
|
+
name: m.name,
|
|
109
|
+
type: "NUMBER",
|
|
110
|
+
label: m.name,
|
|
111
|
+
format: m.format
|
|
112
|
+
}))];
|
|
113
|
+
const data = this.generateMockData(aggregation, params);
|
|
114
|
+
return {
|
|
115
|
+
data,
|
|
116
|
+
columns,
|
|
117
|
+
rowCount: data.length,
|
|
118
|
+
executionTimeMs: 0,
|
|
119
|
+
cached: false
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async executeMetric(metricIds, _params) {
|
|
123
|
+
const data = metricIds.map((id) => ({
|
|
124
|
+
metricId: id,
|
|
125
|
+
value: Math.random() * 1e3,
|
|
126
|
+
change: (Math.random() - .5) * 20
|
|
127
|
+
}));
|
|
128
|
+
return {
|
|
129
|
+
data,
|
|
130
|
+
columns: [
|
|
131
|
+
{
|
|
132
|
+
name: "metricId",
|
|
133
|
+
type: "STRING"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "value",
|
|
137
|
+
type: "NUMBER"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "change",
|
|
141
|
+
type: "NUMBER"
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
rowCount: data.length,
|
|
145
|
+
executionTimeMs: 0,
|
|
146
|
+
cached: false
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
async executeSql(_sql, _params) {
|
|
150
|
+
return {
|
|
151
|
+
data: [],
|
|
152
|
+
columns: [],
|
|
153
|
+
rowCount: 0,
|
|
154
|
+
executionTimeMs: 0,
|
|
155
|
+
cached: false,
|
|
156
|
+
error: "SQL execution not implemented in demo"
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
generateMockData(aggregation, params) {
|
|
160
|
+
const data = [];
|
|
161
|
+
const rowCount = 10;
|
|
162
|
+
const timeDimension = aggregation.dimensions.find((d) => d.type === "TIME");
|
|
163
|
+
for (let i = 0; i < rowCount; i++) {
|
|
164
|
+
const row = {};
|
|
165
|
+
for (const dim of aggregation.dimensions) if (dim.type === "TIME") {
|
|
166
|
+
const date = new Date(params.dateRange?.start ?? /* @__PURE__ */ new Date());
|
|
167
|
+
date.setDate(date.getDate() + i);
|
|
168
|
+
row[dim.name] = date.toISOString().split("T")[0];
|
|
169
|
+
} else row[dim.name] = `${dim.name}_${i % 5}`;
|
|
170
|
+
for (const measure of aggregation.measures) {
|
|
171
|
+
const baseValue = timeDimension ? 100 + i * 10 : Math.random() * 1e3;
|
|
172
|
+
const noise = (Math.random() - .5) * 20;
|
|
173
|
+
row[measure.name] = Math.round((baseValue + noise) * 100) / 100;
|
|
174
|
+
}
|
|
175
|
+
data.push(row);
|
|
176
|
+
}
|
|
177
|
+
return data;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
function createQueryEngine(cache) {
|
|
181
|
+
return new BasicQueryEngine(cache);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
//#endregion
|
|
185
|
+
export { BasicQueryEngine, InMemoryQueryCache, createQueryEngine };
|