@duckcodeailabs/dql-runtime 0.1.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.
Files changed (50) hide show
  1. package/LICENSE +123 -0
  2. package/dist/breadcrumb.d.ts +2 -0
  3. package/dist/breadcrumb.d.ts.map +1 -0
  4. package/dist/breadcrumb.js +17 -0
  5. package/dist/breadcrumb.js.map +1 -0
  6. package/dist/dashboard-layout.d.ts +6 -0
  7. package/dist/dashboard-layout.d.ts.map +1 -0
  8. package/dist/dashboard-layout.js +14 -0
  9. package/dist/dashboard-layout.js.map +1 -0
  10. package/dist/data-fetcher.d.ts +16 -0
  11. package/dist/data-fetcher.d.ts.map +1 -0
  12. package/dist/data-fetcher.js +18 -0
  13. package/dist/data-fetcher.js.map +1 -0
  14. package/dist/dql-runtime.browser.js +571 -0
  15. package/dist/dql-runtime.browser.min.js +1 -0
  16. package/dist/dql-runtime.esm.js +549 -0
  17. package/dist/dql-runtime.esm.js.map +7 -0
  18. package/dist/filter-manager.d.ts +25 -0
  19. package/dist/filter-manager.d.ts.map +1 -0
  20. package/dist/filter-manager.js +140 -0
  21. package/dist/filter-manager.js.map +1 -0
  22. package/dist/hot-reload-client.d.ts +2 -0
  23. package/dist/hot-reload-client.d.ts.map +1 -0
  24. package/dist/hot-reload-client.js +27 -0
  25. package/dist/hot-reload-client.js.map +1 -0
  26. package/dist/index.d.ts +13 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +13 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/interactions.d.ts +49 -0
  31. package/dist/interactions.d.ts.map +1 -0
  32. package/dist/interactions.js +251 -0
  33. package/dist/interactions.js.map +1 -0
  34. package/dist/kpi-renderer.d.ts +4 -0
  35. package/dist/kpi-renderer.d.ts.map +1 -0
  36. package/dist/kpi-renderer.js +17 -0
  37. package/dist/kpi-renderer.js.map +1 -0
  38. package/dist/table-renderer.d.ts +4 -0
  39. package/dist/table-renderer.d.ts.map +1 -0
  40. package/dist/table-renderer.js +39 -0
  41. package/dist/table-renderer.js.map +1 -0
  42. package/dist/utils.d.ts +5 -0
  43. package/dist/utils.d.ts.map +1 -0
  44. package/dist/utils.js +29 -0
  45. package/dist/utils.js.map +1 -0
  46. package/dist/vega-renderer.d.ts +4 -0
  47. package/dist/vega-renderer.d.ts.map +1 -0
  48. package/dist/vega-renderer.js +15 -0
  49. package/dist/vega-renderer.js.map +1 -0
  50. package/package.json +45 -0
@@ -0,0 +1,549 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/data-fetcher.ts
6
+ var DataFetcher = class {
7
+ constructor(apiEndpoint = "/api/query") {
8
+ __publicField(this, "apiEndpoint");
9
+ this.apiEndpoint = apiEndpoint;
10
+ }
11
+ async fetch(_chartId, sql, sqlParams, variables, connectionId) {
12
+ const response = await globalThis.fetch(this.apiEndpoint, {
13
+ method: "POST",
14
+ headers: { "Content-Type": "application/json" },
15
+ body: JSON.stringify({ sql, sqlParams, variables, connectionId })
16
+ });
17
+ if (!response.ok) {
18
+ throw new Error(`Query failed: ${response.statusText}`);
19
+ }
20
+ return response.json();
21
+ }
22
+ };
23
+
24
+ // src/vega-renderer.ts
25
+ var VegaRenderer = class {
26
+ async render(containerId, spec, data) {
27
+ const fullSpec = { ...spec, data: { values: data } };
28
+ const vegaEmbed = globalThis.vegaEmbed;
29
+ if (!vegaEmbed) {
30
+ throw new Error("vega-embed not loaded. Include the Vega-Lite CDN scripts.");
31
+ }
32
+ await vegaEmbed(`#${containerId}`, fullSpec, {
33
+ actions: false,
34
+ renderer: "svg"
35
+ });
36
+ }
37
+ };
38
+
39
+ // src/dashboard-layout.ts
40
+ var DashboardLayout = class {
41
+ constructor() {
42
+ __publicField(this, "container", null);
43
+ }
44
+ mount(elementId) {
45
+ this.container = document.getElementById(elementId);
46
+ }
47
+ setGrid(columns, gap = "16px") {
48
+ if (!this.container) return;
49
+ this.container.style.display = "grid";
50
+ this.container.style.gridTemplateColumns = `repeat(${columns}, 1fr)`;
51
+ this.container.style.gap = gap;
52
+ }
53
+ };
54
+
55
+ // src/kpi-renderer.ts
56
+ var KPIRenderer = class {
57
+ render(containerId, data, metrics) {
58
+ const container = document.getElementById(containerId);
59
+ if (!container || data.length === 0) return;
60
+ const row = data[0];
61
+ const cards = metrics.map((metric) => {
62
+ const value = row[metric];
63
+ const label = metric.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
64
+ return `<div class="dql-kpi-card"><div class="dql-kpi-label">${label}</div><div class="dql-kpi-value">${value ?? "-"}</div></div>`;
65
+ }).join("");
66
+ container.innerHTML = `<div class="dql-kpi-grid">${cards}</div>`;
67
+ }
68
+ };
69
+
70
+ // src/utils.ts
71
+ function escapeHTML(str) {
72
+ if (str === null || str === void 0) return "";
73
+ return String(str).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
74
+ }
75
+ function interpolateDatum(template, datum) {
76
+ return template.replace(/\{(\w+)\}/g, (_match, key) => {
77
+ const val = datum[key];
78
+ return val === null || val === void 0 ? "" : String(val);
79
+ });
80
+ }
81
+ function cleanDatum(datum) {
82
+ const clean = {};
83
+ for (const [k, v] of Object.entries(datum)) {
84
+ if (k !== "_vgsid_" && !k.startsWith("__")) {
85
+ clean[k] = v;
86
+ }
87
+ }
88
+ return clean;
89
+ }
90
+ function getClickedLabel(datum) {
91
+ const keys = Object.keys(datum).filter(
92
+ (k) => k !== "_vgsid_" && !k.startsWith("__") && typeof datum[k] === "string"
93
+ );
94
+ return keys.length > 0 ? String(datum[keys[0]]) : null;
95
+ }
96
+
97
+ // src/table-renderer.ts
98
+ var TableRenderer = class {
99
+ render(containerId, data, pageSize = 25) {
100
+ const container = document.getElementById(containerId);
101
+ if (!container || !data || data.length === 0) {
102
+ if (container) container.innerHTML = '<div class="dql-loading">No data</div>';
103
+ return;
104
+ }
105
+ const colNames = Object.keys(data[0]);
106
+ let html = '<table class="dql-drill-table" style="width:100%"><thead><tr>';
107
+ for (const col of colNames) {
108
+ const label = col.replace(/_/g, " ");
109
+ html += `<th style="cursor:pointer;" data-col="${col}">${escapeHTML(label)}</th>`;
110
+ }
111
+ html += "</tr></thead><tbody>";
112
+ const maxRows = Math.min(data.length, pageSize);
113
+ for (let r = 0; r < maxRows; r++) {
114
+ html += "<tr>";
115
+ for (const col of colNames) {
116
+ let val = data[r][col];
117
+ if (val === null || val === void 0) val = "";
118
+ else if (typeof val === "number") val = val.toLocaleString();
119
+ else val = String(val);
120
+ html += `<td>${escapeHTML(String(val))}</td>`;
121
+ }
122
+ html += "</tr>";
123
+ }
124
+ html += "</tbody></table>";
125
+ if (data.length > pageSize) {
126
+ html += `<p style="margin-top:12px;font-size:13px;opacity:0.7;">Showing ${maxRows} of ${data.length} rows</p>`;
127
+ }
128
+ container.innerHTML = html;
129
+ }
130
+ };
131
+
132
+ // src/interactions.ts
133
+ var InteractionManager = class {
134
+ constructor(fetcher, options = {}) {
135
+ __publicField(this, "vegaViews", {});
136
+ __publicField(this, "fetcher");
137
+ __publicField(this, "crossFilterState", {});
138
+ __publicField(this, "drillStack", []);
139
+ __publicField(this, "onStateChange");
140
+ this.fetcher = fetcher;
141
+ this.onStateChange = options.onStateChange;
142
+ }
143
+ registerView(chartId, view) {
144
+ this.vegaViews[chartId] = view;
145
+ }
146
+ getView(chartId) {
147
+ return this.vegaViews[chartId];
148
+ }
149
+ getState() {
150
+ return {
151
+ crossFilters: this.getCrossFilters(),
152
+ drillStack: this.getDrillStack()
153
+ };
154
+ }
155
+ getCrossFilters() {
156
+ return JSON.parse(JSON.stringify(this.crossFilterState));
157
+ }
158
+ getDrillStack() {
159
+ return JSON.parse(JSON.stringify(this.drillStack));
160
+ }
161
+ setupChartInteractions(chartId, view, interaction, _data) {
162
+ view.addEventListener("click", (_event, item) => {
163
+ if (!item || !item.datum) return;
164
+ const datum = item.datum;
165
+ if (interaction.drillDown) {
166
+ this.handleDrillDown(chartId, interaction.drillDown, datum);
167
+ }
168
+ if (interaction.linkTo) {
169
+ this.handleLinkTo(interaction.linkTo, datum);
170
+ }
171
+ if (interaction.onClick) {
172
+ this.handleOnClick(interaction.onClick, datum, chartId);
173
+ }
174
+ });
175
+ if (interaction.filterBy) {
176
+ this.setupFilterByInteraction(chartId, view, interaction.filterBy);
177
+ }
178
+ }
179
+ async drillUp() {
180
+ if (this.drillStack.length === 0) return;
181
+ this.drillStack.pop();
182
+ this.emitStateChange();
183
+ const modal = document.getElementById("dql-drill-modal");
184
+ const bodyEl = document.getElementById("dql-drill-body");
185
+ if (!modal || !bodyEl) return;
186
+ if (this.drillStack.length === 0) {
187
+ modal.classList.remove("active");
188
+ return;
189
+ }
190
+ const top = this.drillStack[this.drillStack.length - 1];
191
+ bodyEl.innerHTML = '<div class="dql-loading">Loading details...</div>';
192
+ const result = await this.fetchDrillDown(top);
193
+ if (result.error) {
194
+ bodyEl.innerHTML = `<div class="dql-error">${escapeHTML(result.error)}</div>`;
195
+ return;
196
+ }
197
+ if (!result.rows || result.rows.length === 0) {
198
+ bodyEl.innerHTML = '<div class="dql-loading">No data found</div>';
199
+ return;
200
+ }
201
+ bodyEl.innerHTML = this.renderDrillTable(result.columns, result.rows);
202
+ }
203
+ async handleDrillDown(chartId, drillDownSQL, datum) {
204
+ const clean = cleanDatum(datum);
205
+ const frame = {
206
+ chartId,
207
+ label: String(getClickedLabel(clean) || chartId),
208
+ templateSQL: drillDownSQL,
209
+ datum: clean,
210
+ predicates: this.buildPredicates(clean),
211
+ connectionId: this.resolveChartConnection(chartId)
212
+ };
213
+ this.drillStack.push(frame);
214
+ this.emitStateChange();
215
+ const modal = document.getElementById("dql-drill-modal");
216
+ const titleEl = document.getElementById("dql-drill-title");
217
+ const stackEl = document.getElementById("dql-drill-stack");
218
+ const bodyEl = document.getElementById("dql-drill-body");
219
+ if (!modal || !bodyEl) return;
220
+ if (titleEl) titleEl.textContent = "Detail View - " + frame.label;
221
+ if (stackEl) {
222
+ stackEl.textContent = this.drillStack.map((entry) => entry.label).join(" > ");
223
+ }
224
+ bodyEl.innerHTML = '<div class="dql-loading">Loading details...</div>';
225
+ modal.classList.add("active");
226
+ const result = await this.fetchDrillDown(frame);
227
+ if (result.error) {
228
+ bodyEl.innerHTML = `<div class="dql-error">${escapeHTML(result.error)}</div>`;
229
+ return;
230
+ }
231
+ if (!result.rows || result.rows.length === 0) {
232
+ bodyEl.innerHTML = '<div class="dql-loading">No data found</div>';
233
+ return;
234
+ }
235
+ bodyEl.innerHTML = this.renderDrillTable(result.columns, result.rows);
236
+ }
237
+ async fetchDrillDown(frame) {
238
+ try {
239
+ const apiEndpoint = globalThis.DQL_CONFIG?.apiEndpoint ?? "/api/query";
240
+ const response = await globalThis.fetch(apiEndpoint.replace("/query", "/drill"), {
241
+ method: "POST",
242
+ headers: { "Content-Type": "application/json" },
243
+ body: JSON.stringify({
244
+ chartId: frame.chartId,
245
+ templateSQL: frame.templateSQL,
246
+ datum: frame.datum,
247
+ connectionId: frame.connectionId
248
+ })
249
+ });
250
+ if (!response.ok) throw new Error("Drill-down query failed: " + response.statusText);
251
+ return response.json();
252
+ } catch (error) {
253
+ return { columns: [], rows: [], error: error.message };
254
+ }
255
+ }
256
+ renderDrillTable(columns, rows) {
257
+ const colNames = columns ? columns.map((c) => c.name) : Object.keys(rows[0] || {});
258
+ let html = '<table class="dql-drill-table"><thead><tr>';
259
+ for (const col of colNames) {
260
+ html += `<th>${escapeHTML(col)}</th>`;
261
+ }
262
+ html += "</tr></thead><tbody>";
263
+ const maxRows = Math.min(rows.length, 100);
264
+ for (let r = 0; r < maxRows; r++) {
265
+ html += "<tr>";
266
+ for (const col of colNames) {
267
+ const val = rows[r][col];
268
+ html += `<td>${escapeHTML(val === null || val === void 0 ? "" : String(val))}</td>`;
269
+ }
270
+ html += "</tr>";
271
+ }
272
+ html += "</tbody></table>";
273
+ if (rows.length > 100) {
274
+ html += `<p style="margin-top:12px;color:#888;">Showing first 100 of ${rows.length} rows</p>`;
275
+ }
276
+ return html;
277
+ }
278
+ handleLinkTo(linkTo, datum) {
279
+ let url = linkTo.replace(/\{(\w+)\}/g, (_m, key) => {
280
+ const val = datum[key];
281
+ return val === null || val === void 0 ? "" : String(val);
282
+ });
283
+ const config = globalThis.DQL_CONFIG;
284
+ const sep = url.indexOf("?") >= 0 ? "&" : "?";
285
+ url += sep + "_from=" + encodeURIComponent(location.pathname) + "&_fromTitle=" + encodeURIComponent(config?.title ?? "");
286
+ window.location.href = url;
287
+ }
288
+ handleOnClick(action, datum, chartId) {
289
+ const event = new CustomEvent("dql:click", {
290
+ detail: { chartId, action, datum },
291
+ bubbles: true
292
+ });
293
+ document.dispatchEvent(event);
294
+ const config = globalThis.DQL_CONFIG;
295
+ if (action === "drill_down") {
296
+ const interaction = config?.interactions?.[chartId];
297
+ if (interaction?.drillDown) {
298
+ this.handleDrillDown(chartId, interaction.drillDown, datum);
299
+ }
300
+ } else if (action === "navigate" || action === "link") {
301
+ const interaction = config?.interactions?.[chartId];
302
+ if (interaction?.linkTo) {
303
+ this.handleLinkTo(interaction.linkTo, datum);
304
+ }
305
+ } else if (action.startsWith("set_param:") || action.startsWith("set_params:")) {
306
+ document.dispatchEvent(new CustomEvent("dql:param-action", {
307
+ detail: { chartId, action, datum },
308
+ bubbles: true
309
+ }));
310
+ }
311
+ }
312
+ setupFilterByInteraction(chartId, view, filterBy) {
313
+ const fields = Array.isArray(filterBy) ? filterBy : [filterBy];
314
+ view.addSignalListener("dql_filter", (_name, value) => {
315
+ const selectedValues = {};
316
+ if (value && value.vlPoint && value.vlPoint.or) {
317
+ for (const point of value.vlPoint.or) {
318
+ for (const field of fields) {
319
+ if (point[field] !== void 0 && point[field] !== null && point[field] !== "") {
320
+ if (!selectedValues[field]) selectedValues[field] = [];
321
+ if (!selectedValues[field].includes(point[field])) {
322
+ selectedValues[field].push(point[field]);
323
+ }
324
+ }
325
+ }
326
+ }
327
+ }
328
+ this.applyCrossChartFilter(chartId, selectedValues);
329
+ });
330
+ }
331
+ applyCrossChartFilter(sourceChartId, selectedValues) {
332
+ if (Object.keys(selectedValues).length === 0) {
333
+ delete this.crossFilterState[sourceChartId];
334
+ } else {
335
+ this.crossFilterState[sourceChartId] = selectedValues;
336
+ }
337
+ this.emitStateChange();
338
+ document.dispatchEvent(new CustomEvent("dql:cross-filter", {
339
+ detail: {
340
+ sourceChartId,
341
+ selectedValues,
342
+ state: this.getCrossFilters()
343
+ },
344
+ bubbles: true
345
+ }));
346
+ }
347
+ buildPredicates(datum) {
348
+ const predicates = {};
349
+ for (const [key, value] of Object.entries(datum)) {
350
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
351
+ if (value === null || value === void 0 || value === "") continue;
352
+ predicates[key] = [value];
353
+ }
354
+ return predicates;
355
+ }
356
+ resolveChartConnection(chartId) {
357
+ const config = globalThis.DQL_CONFIG;
358
+ const charts = Array.isArray(config?.charts) ? config.charts : [];
359
+ const chart = charts.find((entry) => entry.id === chartId);
360
+ return chart?.connection;
361
+ }
362
+ emitStateChange() {
363
+ if (this.onStateChange) {
364
+ this.onStateChange(this.getState());
365
+ }
366
+ }
367
+ };
368
+
369
+ // src/filter-manager.ts
370
+ var FilterManager = class {
371
+ constructor(fetcher, onApply) {
372
+ __publicField(this, "filterState", {});
373
+ __publicField(this, "fetcher");
374
+ __publicField(this, "onApply");
375
+ this.fetcher = fetcher;
376
+ this.onApply = onApply;
377
+ }
378
+ getState() {
379
+ return { ...this.filterState };
380
+ }
381
+ async init(filters) {
382
+ if (!filters || filters.length === 0) return;
383
+ for (const filter of filters) {
384
+ if (filter.sql) {
385
+ await this.loadFilterOptions(filter);
386
+ }
387
+ if (filter.defaultValue) {
388
+ this.filterState[filter.param] = filter.defaultValue;
389
+ this.setUIValue(filter.id, filter.defaultValue);
390
+ }
391
+ }
392
+ this.setupEventHandlers(filters);
393
+ }
394
+ async loadFilterOptions(filter) {
395
+ try {
396
+ const result = await this.fetcher.fetch(filter.id, filter.sql, []);
397
+ if (!result.rows || result.rows.length === 0) return;
398
+ const selectEl = document.getElementById(filter.id);
399
+ if (!selectEl || selectEl.tagName !== "SELECT") return;
400
+ const colName = result.columns && result.columns.length > 0 ? result.columns[0].name : Object.keys(result.rows[0] || {})[0];
401
+ if (!colName) return;
402
+ for (const row of result.rows) {
403
+ const val = row[colName];
404
+ if (val === null || val === void 0) continue;
405
+ const option = document.createElement("option");
406
+ option.value = String(val);
407
+ option.textContent = String(val);
408
+ selectEl.appendChild(option);
409
+ }
410
+ } catch (err) {
411
+ console.warn("[DQL] Failed to load filter options for " + filter.id, err);
412
+ }
413
+ }
414
+ setUIValue(filterId, value) {
415
+ const el = document.getElementById(filterId);
416
+ if (!el) return;
417
+ el.value = String(value);
418
+ }
419
+ setupEventHandlers(filters) {
420
+ const applyBtn = document.getElementById("dql-filter-apply");
421
+ const resetBtn = document.getElementById("dql-filter-reset");
422
+ if (applyBtn) {
423
+ applyBtn.addEventListener("click", () => {
424
+ this.collectState(filters);
425
+ this.onApply();
426
+ });
427
+ }
428
+ if (resetBtn) {
429
+ resetBtn.addEventListener("click", () => {
430
+ this.resetState(filters);
431
+ this.onApply();
432
+ });
433
+ }
434
+ const autoApplyEls = document.querySelectorAll(".dql-filter-select, .dql-filter-date");
435
+ autoApplyEls.forEach((el) => {
436
+ el.addEventListener("change", () => {
437
+ this.collectState(filters);
438
+ this.onApply();
439
+ });
440
+ });
441
+ const debounceTimers = {};
442
+ const textInputs = document.querySelectorAll('.dql-filter-input[type="text"]');
443
+ textInputs.forEach((el) => {
444
+ el.addEventListener("input", () => {
445
+ const id = el.id;
446
+ if (debounceTimers[id]) clearTimeout(debounceTimers[id]);
447
+ debounceTimers[id] = setTimeout(() => {
448
+ this.collectState(filters);
449
+ this.onApply();
450
+ }, 300);
451
+ });
452
+ });
453
+ }
454
+ collectState(filters) {
455
+ this.filterState = {};
456
+ for (const filter of filters) {
457
+ const el = document.getElementById(filter.id);
458
+ if (!el) continue;
459
+ const value = el.value;
460
+ if (value && value !== "") {
461
+ this.filterState[filter.param] = value;
462
+ }
463
+ }
464
+ }
465
+ resetState(filters) {
466
+ this.filterState = {};
467
+ for (const filter of filters) {
468
+ const el = document.getElementById(filter.id);
469
+ if (!el) continue;
470
+ if (filter.defaultValue) {
471
+ el.value = String(filter.defaultValue);
472
+ this.filterState[filter.param] = filter.defaultValue;
473
+ } else {
474
+ el.value = "";
475
+ }
476
+ }
477
+ }
478
+ applyToSQL(sql) {
479
+ const urlParams = new URLSearchParams(window.location.search);
480
+ const merged = { ...this.filterState };
481
+ urlParams.forEach((value, key) => {
482
+ if (key !== "_from" && key !== "_fromTitle" && !merged[key]) {
483
+ merged[key] = value;
484
+ }
485
+ });
486
+ let result = sql;
487
+ for (const [paramName, paramValue] of Object.entries(merged)) {
488
+ const placeholder = "{" + paramName + "}";
489
+ result = result.split(placeholder).join(String(paramValue).replace(/'/g, "''"));
490
+ }
491
+ return result;
492
+ }
493
+ };
494
+
495
+ // src/breadcrumb.ts
496
+ function initBreadcrumb() {
497
+ const breadcrumb = document.getElementById("dql-breadcrumb");
498
+ if (!breadcrumb) return;
499
+ const urlParams = new URLSearchParams(window.location.search);
500
+ const fromPath = urlParams.get("_from");
501
+ const fromTitle = urlParams.get("_fromTitle");
502
+ const config = globalThis.DQL_CONFIG;
503
+ if (fromPath && fromTitle) {
504
+ breadcrumb.innerHTML = `<a href="${escapeHTML(fromPath)}">${escapeHTML(fromTitle)}</a><span class="dql-breadcrumb-sep">/</span><span>${escapeHTML(config?.title ?? "")}</span>`;
505
+ }
506
+ }
507
+
508
+ // src/hot-reload-client.ts
509
+ function initHotReload(wsUrl) {
510
+ if (typeof WebSocket === "undefined") return;
511
+ const url = wsUrl ?? `${location.protocol === "https:" ? "wss:" : "ws:"}//${location.host}/__dql_hmr`;
512
+ let reconnectAttempts = 0;
513
+ function connect() {
514
+ const ws = new WebSocket(url);
515
+ ws.onopen = () => {
516
+ console.log("[DQL] Hot-reload connected");
517
+ reconnectAttempts = 0;
518
+ };
519
+ ws.onmessage = (event) => {
520
+ const msg = JSON.parse(event.data);
521
+ if (msg.type === "reload") {
522
+ location.reload();
523
+ }
524
+ };
525
+ ws.onclose = () => {
526
+ if (reconnectAttempts < 10) {
527
+ reconnectAttempts++;
528
+ setTimeout(connect, Math.min(1e3 * 2 ** reconnectAttempts, 1e4));
529
+ }
530
+ };
531
+ }
532
+ connect();
533
+ }
534
+ export {
535
+ DashboardLayout,
536
+ DataFetcher,
537
+ FilterManager,
538
+ InteractionManager,
539
+ KPIRenderer,
540
+ TableRenderer,
541
+ VegaRenderer,
542
+ cleanDatum,
543
+ escapeHTML,
544
+ getClickedLabel,
545
+ initBreadcrumb,
546
+ initHotReload,
547
+ interpolateDatum
548
+ };
549
+ //# sourceMappingURL=dql-runtime.esm.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/data-fetcher.ts", "../src/vega-renderer.ts", "../src/dashboard-layout.ts", "../src/kpi-renderer.ts", "../src/utils.ts", "../src/table-renderer.ts", "../src/interactions.ts", "../src/filter-manager.ts", "../src/breadcrumb.ts", "../src/hot-reload-client.ts"],
4
+ "sourcesContent": ["export class DataFetcher {\n private apiEndpoint: string;\n\n constructor(apiEndpoint: string = '/api/query') {\n this.apiEndpoint = apiEndpoint;\n }\n\n async fetch(\n _chartId: string,\n sql: string,\n sqlParams: Array<{ name: string; position: number; literalValue?: unknown }>,\n variables?: Record<string, unknown>,\n connectionId?: string,\n ): Promise<{ columns: Array<{ name: string; type: string }>; rows: Record<string, unknown>[] }> {\n const response = await globalThis.fetch(this.apiEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sql, sqlParams, variables, connectionId }),\n });\n\n if (!response.ok) {\n throw new Error(`Query failed: ${response.statusText}`);\n }\n\n return response.json();\n }\n}\n", "export class VegaRenderer {\n async render(containerId: string, spec: Record<string, unknown>, data: unknown[]): Promise<void> {\n const fullSpec = { ...spec, data: { values: data } };\n // vegaEmbed is loaded globally from CDN\n const vegaEmbed = (globalThis as unknown as Record<string, unknown>).vegaEmbed as\n | ((selector: string, spec: unknown, opts?: unknown) => Promise<unknown>)\n | undefined;\n\n if (!vegaEmbed) {\n throw new Error('vega-embed not loaded. Include the Vega-Lite CDN scripts.');\n }\n\n await vegaEmbed(`#${containerId}`, fullSpec, {\n actions: false,\n renderer: 'svg',\n });\n }\n}\n", "export class DashboardLayout {\n private container: HTMLElement | null = null;\n\n mount(elementId: string): void {\n this.container = document.getElementById(elementId);\n }\n\n setGrid(columns: number, gap: string = '16px'): void {\n if (!this.container) return;\n this.container.style.display = 'grid';\n this.container.style.gridTemplateColumns = `repeat(${columns}, 1fr)`;\n this.container.style.gap = gap;\n }\n}\n", "export class KPIRenderer {\n render(containerId: string, data: Record<string, unknown>[], metrics: string[]): void {\n const container = document.getElementById(containerId);\n if (!container || data.length === 0) return;\n\n const row = data[0];\n const cards = metrics\n .map((metric) => {\n const value = row[metric];\n const label = metric.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n return `<div class=\"dql-kpi-card\"><div class=\"dql-kpi-label\">${label}</div><div class=\"dql-kpi-value\">${value ?? '-'}</div></div>`;\n })\n .join('');\n\n container.innerHTML = `<div class=\"dql-kpi-grid\">${cards}</div>`;\n }\n}\n", "export function escapeHTML(str: unknown): string {\n if (str === null || str === undefined) return '';\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nexport function interpolateDatum(template: string, datum: Record<string, unknown>): string {\n return template.replace(/\\{(\\w+)\\}/g, (_match, key) => {\n const val = datum[key];\n return val === null || val === undefined ? '' : String(val);\n });\n}\n\nexport function cleanDatum(datum: Record<string, unknown>): Record<string, unknown> {\n const clean: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(datum)) {\n if (k !== '_vgsid_' && !k.startsWith('__')) {\n clean[k] = v;\n }\n }\n return clean;\n}\n\nexport function getClickedLabel(datum: Record<string, unknown>): string | null {\n const keys = Object.keys(datum).filter(\n (k) => k !== '_vgsid_' && !k.startsWith('__') && typeof datum[k] === 'string',\n );\n return keys.length > 0 ? String(datum[keys[0]]) : null;\n}\n", "import { escapeHTML } from './utils.js';\n\nexport class TableRenderer {\n render(containerId: string, data: Record<string, unknown>[], pageSize: number = 25): void {\n const container = document.getElementById(containerId);\n if (!container || !data || data.length === 0) {\n if (container) container.innerHTML = '<div class=\"dql-loading\">No data</div>';\n return;\n }\n\n const colNames = Object.keys(data[0]);\n\n let html = '<table class=\"dql-drill-table\" style=\"width:100%\"><thead><tr>';\n for (const col of colNames) {\n const label = col.replace(/_/g, ' ');\n html += `<th style=\"cursor:pointer;\" data-col=\"${col}\">${escapeHTML(label)}</th>`;\n }\n html += '</tr></thead><tbody>';\n\n const maxRows = Math.min(data.length, pageSize);\n for (let r = 0; r < maxRows; r++) {\n html += '<tr>';\n for (const col of colNames) {\n let val = data[r][col];\n if (val === null || val === undefined) val = '';\n else if (typeof val === 'number') val = val.toLocaleString();\n else val = String(val);\n html += `<td>${escapeHTML(String(val))}</td>`;\n }\n html += '</tr>';\n }\n\n html += '</tbody></table>';\n if (data.length > pageSize) {\n html += `<p style=\"margin-top:12px;font-size:13px;opacity:0.7;\">Showing ${maxRows} of ${data.length} rows</p>`;\n }\n container.innerHTML = html;\n }\n}\n", "import { escapeHTML, cleanDatum, getClickedLabel } from './utils.js';\nimport { DataFetcher } from './data-fetcher.js';\n\nexport interface InteractionConfig {\n drillDown?: string;\n linkTo?: string;\n onClick?: string;\n filterBy?: string | string[];\n}\n\ninterface DrillFrame {\n chartId: string;\n label: string;\n templateSQL: string;\n datum: Record<string, unknown>;\n predicates: Record<string, unknown[]>;\n connectionId?: string;\n}\n\nexport interface InteractionState {\n crossFilters: Record<string, Record<string, unknown[]>>;\n drillStack: DrillFrame[];\n}\n\nexport interface InteractionManagerOptions {\n onStateChange?: (state: InteractionState) => void;\n}\n\nexport class InteractionManager {\n private vegaViews: Record<string, any> = {};\n private fetcher: DataFetcher;\n private crossFilterState: Record<string, Record<string, unknown[]>> = {};\n private drillStack: DrillFrame[] = [];\n private onStateChange?: (state: InteractionState) => void;\n\n constructor(fetcher: DataFetcher, options: InteractionManagerOptions = {}) {\n this.fetcher = fetcher;\n this.onStateChange = options.onStateChange;\n }\n\n registerView(chartId: string, view: any): void {\n this.vegaViews[chartId] = view;\n }\n\n getView(chartId: string): any {\n return this.vegaViews[chartId];\n }\n\n getState(): InteractionState {\n return {\n crossFilters: this.getCrossFilters(),\n drillStack: this.getDrillStack(),\n };\n }\n\n getCrossFilters(): Record<string, Record<string, unknown[]>> {\n return JSON.parse(JSON.stringify(this.crossFilterState));\n }\n\n getDrillStack(): DrillFrame[] {\n return JSON.parse(JSON.stringify(this.drillStack));\n }\n\n setupChartInteractions(\n chartId: string,\n view: any,\n interaction: InteractionConfig,\n _data: unknown[],\n ): void {\n view.addEventListener('click', (_event: unknown, item: any) => {\n if (!item || !item.datum) return;\n const datum = item.datum;\n\n if (interaction.drillDown) {\n this.handleDrillDown(chartId, interaction.drillDown, datum);\n }\n if (interaction.linkTo) {\n this.handleLinkTo(interaction.linkTo, datum);\n }\n if (interaction.onClick) {\n this.handleOnClick(interaction.onClick, datum, chartId);\n }\n });\n\n if (interaction.filterBy) {\n this.setupFilterByInteraction(chartId, view, interaction.filterBy);\n }\n }\n\n async drillUp(): Promise<void> {\n if (this.drillStack.length === 0) return;\n this.drillStack.pop();\n this.emitStateChange();\n\n const modal = document.getElementById('dql-drill-modal');\n const bodyEl = document.getElementById('dql-drill-body');\n if (!modal || !bodyEl) return;\n\n if (this.drillStack.length === 0) {\n modal.classList.remove('active');\n return;\n }\n\n const top = this.drillStack[this.drillStack.length - 1];\n bodyEl.innerHTML = '<div class=\"dql-loading\">Loading details...</div>';\n const result = await this.fetchDrillDown(top);\n if (result.error) {\n bodyEl.innerHTML = `<div class=\"dql-error\">${escapeHTML(result.error)}</div>`;\n return;\n }\n if (!result.rows || result.rows.length === 0) {\n bodyEl.innerHTML = '<div class=\"dql-loading\">No data found</div>';\n return;\n }\n\n bodyEl.innerHTML = this.renderDrillTable(result.columns, result.rows);\n }\n\n private async handleDrillDown(\n chartId: string,\n drillDownSQL: string,\n datum: Record<string, unknown>,\n ): Promise<void> {\n const clean = cleanDatum(datum);\n const frame: DrillFrame = {\n chartId,\n label: String(getClickedLabel(clean) || chartId),\n templateSQL: drillDownSQL,\n datum: clean,\n predicates: this.buildPredicates(clean),\n connectionId: this.resolveChartConnection(chartId),\n };\n this.drillStack.push(frame);\n this.emitStateChange();\n\n const modal = document.getElementById('dql-drill-modal');\n const titleEl = document.getElementById('dql-drill-title');\n const stackEl = document.getElementById('dql-drill-stack');\n const bodyEl = document.getElementById('dql-drill-body');\n if (!modal || !bodyEl) return;\n\n if (titleEl) titleEl.textContent = 'Detail View - ' + frame.label;\n if (stackEl) {\n stackEl.textContent = this.drillStack.map((entry) => entry.label).join(' > ');\n }\n bodyEl.innerHTML = '<div class=\"dql-loading\">Loading details...</div>';\n modal.classList.add('active');\n\n const result = await this.fetchDrillDown(frame);\n if (result.error) {\n bodyEl.innerHTML = `<div class=\"dql-error\">${escapeHTML(result.error)}</div>`;\n return;\n }\n if (!result.rows || result.rows.length === 0) {\n bodyEl.innerHTML = '<div class=\"dql-loading\">No data found</div>';\n return;\n }\n\n bodyEl.innerHTML = this.renderDrillTable(result.columns, result.rows);\n }\n\n private async fetchDrillDown(frame: DrillFrame): Promise<any> {\n try {\n const apiEndpoint = (globalThis as any).DQL_CONFIG?.apiEndpoint ?? '/api/query';\n const response = await globalThis.fetch(apiEndpoint.replace('/query', '/drill'), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n chartId: frame.chartId,\n templateSQL: frame.templateSQL,\n datum: frame.datum,\n connectionId: frame.connectionId,\n }),\n });\n\n if (!response.ok) throw new Error('Drill-down query failed: ' + response.statusText);\n return response.json();\n } catch (error: any) {\n return { columns: [], rows: [], error: error.message };\n }\n }\n\n private renderDrillTable(\n columns: Array<{ name: string }> | undefined,\n rows: Record<string, unknown>[],\n ): string {\n const colNames = columns ? columns.map((c) => c.name) : Object.keys(rows[0] || {});\n\n let html = '<table class=\"dql-drill-table\"><thead><tr>';\n for (const col of colNames) {\n html += `<th>${escapeHTML(col)}</th>`;\n }\n html += '</tr></thead><tbody>';\n\n const maxRows = Math.min(rows.length, 100);\n for (let r = 0; r < maxRows; r++) {\n html += '<tr>';\n for (const col of colNames) {\n const val = rows[r][col];\n html += `<td>${escapeHTML(val === null || val === undefined ? '' : String(val))}</td>`;\n }\n html += '</tr>';\n }\n\n html += '</tbody></table>';\n if (rows.length > 100) {\n html += `<p style=\"margin-top:12px;color:#888;\">Showing first 100 of ${rows.length} rows</p>`;\n }\n return html;\n }\n\n private handleLinkTo(linkTo: string, datum: Record<string, unknown>): void {\n let url = linkTo.replace(/\\{(\\w+)\\}/g, (_m, key) => {\n const val = datum[key];\n return val === null || val === undefined ? '' : String(val);\n });\n\n const config = (globalThis as any).DQL_CONFIG;\n const sep = url.indexOf('?') >= 0 ? '&' : '?';\n url += sep + '_from=' + encodeURIComponent(location.pathname) +\n '&_fromTitle=' + encodeURIComponent(config?.title ?? '');\n\n window.location.href = url;\n }\n\n private handleOnClick(action: string, datum: Record<string, unknown>, chartId: string): void {\n const event = new CustomEvent('dql:click', {\n detail: { chartId, action, datum },\n bubbles: true,\n });\n document.dispatchEvent(event);\n\n const config = (globalThis as any).DQL_CONFIG;\n if (action === 'drill_down') {\n const interaction = config?.interactions?.[chartId];\n if (interaction?.drillDown) {\n this.handleDrillDown(chartId, interaction.drillDown, datum);\n }\n } else if (action === 'navigate' || action === 'link') {\n const interaction = config?.interactions?.[chartId];\n if (interaction?.linkTo) {\n this.handleLinkTo(interaction.linkTo, datum);\n }\n } else if (action.startsWith('set_param:') || action.startsWith('set_params:')) {\n document.dispatchEvent(new CustomEvent('dql:param-action', {\n detail: { chartId, action, datum },\n bubbles: true,\n }));\n }\n }\n\n private setupFilterByInteraction(chartId: string, view: any, filterBy: string | string[]): void {\n const fields = Array.isArray(filterBy) ? filterBy : [filterBy];\n\n view.addSignalListener('dql_filter', (_name: string, value: any) => {\n const selectedValues: Record<string, unknown[]> = {};\n if (value && value.vlPoint && value.vlPoint.or) {\n for (const point of value.vlPoint.or) {\n for (const field of fields) {\n if (point[field] !== undefined && point[field] !== null && point[field] !== '') {\n if (!selectedValues[field]) selectedValues[field] = [];\n if (!selectedValues[field].includes(point[field])) {\n selectedValues[field].push(point[field]);\n }\n }\n }\n }\n }\n\n this.applyCrossChartFilter(chartId, selectedValues);\n });\n }\n\n private applyCrossChartFilter(sourceChartId: string, selectedValues: Record<string, unknown[]>): void {\n if (Object.keys(selectedValues).length === 0) {\n delete this.crossFilterState[sourceChartId];\n } else {\n this.crossFilterState[sourceChartId] = selectedValues;\n }\n\n this.emitStateChange();\n document.dispatchEvent(new CustomEvent('dql:cross-filter', {\n detail: {\n sourceChartId,\n selectedValues,\n state: this.getCrossFilters(),\n },\n bubbles: true,\n }));\n }\n\n private buildPredicates(datum: Record<string, unknown>): Record<string, unknown[]> {\n const predicates: Record<string, unknown[]> = {};\n for (const [key, value] of Object.entries(datum)) {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;\n if (value === null || value === undefined || value === '') continue;\n predicates[key] = [value];\n }\n return predicates;\n }\n\n private resolveChartConnection(chartId: string): string | undefined {\n const config = (globalThis as any).DQL_CONFIG;\n const charts = Array.isArray(config?.charts) ? config.charts : [];\n const chart = charts.find((entry: { id: string; connection?: string }) => entry.id === chartId);\n return chart?.connection;\n }\n\n private emitStateChange(): void {\n if (this.onStateChange) {\n this.onStateChange(this.getState());\n }\n }\n}\n", "import { DataFetcher } from './data-fetcher.js';\nimport { escapeHTML } from './utils.js';\n\nexport interface FilterConfig {\n id: string;\n type: string;\n sql: string;\n params: unknown[];\n param: string;\n label: string;\n defaultValue?: unknown;\n}\n\nexport class FilterManager {\n private filterState: Record<string, unknown> = {};\n private fetcher: DataFetcher;\n private onApply: () => void;\n\n constructor(fetcher: DataFetcher, onApply: () => void) {\n this.fetcher = fetcher;\n this.onApply = onApply;\n }\n\n getState(): Record<string, unknown> {\n return { ...this.filterState };\n }\n\n async init(filters: FilterConfig[]): Promise<void> {\n if (!filters || filters.length === 0) return;\n\n for (const filter of filters) {\n if (filter.sql) {\n await this.loadFilterOptions(filter);\n }\n if (filter.defaultValue) {\n this.filterState[filter.param] = filter.defaultValue;\n this.setUIValue(filter.id, filter.defaultValue);\n }\n }\n\n this.setupEventHandlers(filters);\n }\n\n private async loadFilterOptions(filter: FilterConfig): Promise<void> {\n try {\n const result = await this.fetcher.fetch(filter.id, filter.sql, []);\n if (!result.rows || result.rows.length === 0) return;\n\n const selectEl = document.getElementById(filter.id);\n if (!selectEl || selectEl.tagName !== 'SELECT') return;\n\n const colName =\n result.columns && result.columns.length > 0\n ? result.columns[0].name\n : Object.keys(result.rows[0] || {})[0];\n if (!colName) return;\n\n for (const row of result.rows) {\n const val = row[colName];\n if (val === null || val === undefined) continue;\n const option = document.createElement('option');\n option.value = String(val);\n option.textContent = String(val);\n selectEl.appendChild(option);\n }\n } catch (err) {\n console.warn('[DQL] Failed to load filter options for ' + filter.id, err);\n }\n }\n\n private setUIValue(filterId: string, value: unknown): void {\n const el = document.getElementById(filterId) as HTMLInputElement | null;\n if (!el) return;\n el.value = String(value);\n }\n\n private setupEventHandlers(filters: FilterConfig[]): void {\n const applyBtn = document.getElementById('dql-filter-apply');\n const resetBtn = document.getElementById('dql-filter-reset');\n\n if (applyBtn) {\n applyBtn.addEventListener('click', () => {\n this.collectState(filters);\n this.onApply();\n });\n }\n\n if (resetBtn) {\n resetBtn.addEventListener('click', () => {\n this.resetState(filters);\n this.onApply();\n });\n }\n\n // Auto-apply for select/date filters\n const autoApplyEls = document.querySelectorAll('.dql-filter-select, .dql-filter-date');\n autoApplyEls.forEach((el) => {\n el.addEventListener('change', () => {\n this.collectState(filters);\n this.onApply();\n });\n });\n\n // Debounced auto-apply for text inputs\n const debounceTimers: Record<string, ReturnType<typeof setTimeout>> = {};\n const textInputs = document.querySelectorAll('.dql-filter-input[type=\"text\"]');\n textInputs.forEach((el) => {\n el.addEventListener('input', () => {\n const id = (el as HTMLElement).id;\n if (debounceTimers[id]) clearTimeout(debounceTimers[id]);\n debounceTimers[id] = setTimeout(() => {\n this.collectState(filters);\n this.onApply();\n }, 300);\n });\n });\n }\n\n private collectState(filters: FilterConfig[]): void {\n this.filterState = {};\n for (const filter of filters) {\n const el = document.getElementById(filter.id) as HTMLInputElement | null;\n if (!el) continue;\n const value = el.value;\n if (value && value !== '') {\n this.filterState[filter.param] = value;\n }\n }\n }\n\n private resetState(filters: FilterConfig[]): void {\n this.filterState = {};\n for (const filter of filters) {\n const el = document.getElementById(filter.id) as HTMLInputElement | null;\n if (!el) continue;\n if (filter.defaultValue) {\n el.value = String(filter.defaultValue);\n this.filterState[filter.param] = filter.defaultValue;\n } else {\n el.value = '';\n }\n }\n }\n\n applyToSQL(sql: string): string {\n const urlParams = new URLSearchParams(window.location.search);\n const merged: Record<string, unknown> = { ...this.filterState };\n urlParams.forEach((value, key) => {\n if (key !== '_from' && key !== '_fromTitle' && !merged[key]) {\n merged[key] = value;\n }\n });\n\n let result = sql;\n for (const [paramName, paramValue] of Object.entries(merged)) {\n const placeholder = '{' + paramName + '}';\n result = result.split(placeholder).join(String(paramValue).replace(/'/g, \"''\"));\n }\n return result;\n }\n}\n", "import { escapeHTML } from './utils.js';\n\nexport function initBreadcrumb(): void {\n const breadcrumb = document.getElementById('dql-breadcrumb');\n if (!breadcrumb) return;\n\n const urlParams = new URLSearchParams(window.location.search);\n const fromPath = urlParams.get('_from');\n const fromTitle = urlParams.get('_fromTitle');\n const config = (globalThis as any).DQL_CONFIG;\n\n if (fromPath && fromTitle) {\n breadcrumb.innerHTML =\n `<a href=\"${escapeHTML(fromPath)}\">${escapeHTML(fromTitle)}</a>` +\n '<span class=\"dql-breadcrumb-sep\">/</span>' +\n `<span>${escapeHTML(config?.title ?? '')}</span>`;\n }\n}\n", "export function initHotReload(wsUrl?: string): void {\n if (typeof WebSocket === 'undefined') return;\n\n const url = wsUrl ?? `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}/__dql_hmr`;\n let reconnectAttempts = 0;\n\n function connect(): void {\n const ws = new WebSocket(url);\n\n ws.onopen = () => {\n console.log('[DQL] Hot-reload connected');\n reconnectAttempts = 0;\n };\n\n ws.onmessage = (event) => {\n const msg = JSON.parse(event.data);\n if (msg.type === 'reload') {\n location.reload();\n }\n };\n\n ws.onclose = () => {\n if (reconnectAttempts < 10) {\n reconnectAttempts++;\n setTimeout(connect, Math.min(1000 * 2 ** reconnectAttempts, 10000));\n }\n };\n }\n\n connect();\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,25 @@
1
+ import { DataFetcher } from './data-fetcher.js';
2
+ export interface FilterConfig {
3
+ id: string;
4
+ type: string;
5
+ sql: string;
6
+ params: unknown[];
7
+ param: string;
8
+ label: string;
9
+ defaultValue?: unknown;
10
+ }
11
+ export declare class FilterManager {
12
+ private filterState;
13
+ private fetcher;
14
+ private onApply;
15
+ constructor(fetcher: DataFetcher, onApply: () => void);
16
+ getState(): Record<string, unknown>;
17
+ init(filters: FilterConfig[]): Promise<void>;
18
+ private loadFilterOptions;
19
+ private setUIValue;
20
+ private setupEventHandlers;
21
+ private collectState;
22
+ private resetState;
23
+ applyToSQL(sql: string): string;
24
+ }
25
+ //# sourceMappingURL=filter-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-manager.d.ts","sourceRoot":"","sources":["../src/filter-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,OAAO,CAAa;gBAEhB,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,IAAI;IAKrD,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI7B,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAgBpC,iBAAiB;IA2B/B,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,kBAAkB;IA0C1B,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,UAAU;IAclB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;CAgBhC"}