yamchart 0.9.5 → 0.10.1

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 (92) hide show
  1. package/dist/{advisor-SC64RTZO.js → advisor-5TJDNAY7.js} +24 -14
  2. package/dist/advisor-5TJDNAY7.js.map +1 -0
  3. package/dist/agent-TZZHNDPK.js +1 -0
  4. package/dist/{chunk-NXQ6ZO3V.js → chunk-5FHV22X2.js} +7 -6
  5. package/dist/chunk-5FHV22X2.js.map +1 -0
  6. package/dist/{dist-MNXSMGV6.js → chunk-64CI3HSY.js} +184 -15
  7. package/dist/chunk-64CI3HSY.js.map +1 -0
  8. package/dist/chunk-7D4SUZUM.js +1 -0
  9. package/dist/chunk-DZVT6PHW.js +1 -0
  10. package/dist/chunk-EAQXUGP6.js +1 -0
  11. package/dist/chunk-HJVVHYVN.js +1 -0
  12. package/dist/chunk-IVD4OP3K.js +1 -0
  13. package/dist/{chunk-S7YQXEKM.js → chunk-QUIDZO5G.js} +104 -275
  14. package/dist/chunk-QUIDZO5G.js.map +1 -0
  15. package/dist/{chunk-RMIDEBHD.js → chunk-S2CH4HUZ.js} +7 -6
  16. package/dist/chunk-TZKNU5TD.js +1 -0
  17. package/dist/chunk-UFDQ3C7Q.js +919 -0
  18. package/dist/chunk-UFDQ3C7Q.js.map +1 -0
  19. package/dist/chunk-VJC24RKT.js +1 -0
  20. package/dist/chunk-WYS4ULBE.js +1 -0
  21. package/dist/{chunk-H4L3FNLS.js → chunk-ZBCQNWVN.js} +3 -2
  22. package/dist/{chunk-RM6MNDVF.js → chunk-ZIY22VO7.js} +193 -11
  23. package/dist/chunk-ZIY22VO7.js.map +1 -0
  24. package/dist/chunk-ZMJPRNOA.js +1 -0
  25. package/dist/compare-RQFCEZIK.js +1 -0
  26. package/dist/connection-utils-FEUWER5I.js +20 -0
  27. package/dist/{describe-X75C2VDU.js → describe-MEP72B56.js} +7 -6
  28. package/dist/{dev-GWXHBBWB.js → dev-H6SJZ5UA.js} +1315 -73
  29. package/dist/dev-H6SJZ5UA.js.map +1 -0
  30. package/dist/dist-5DQO6L2S.js +48 -0
  31. package/dist/dist-MIKFZKSD.js +1 -0
  32. package/dist/dist-PPAD6KOM.js +99 -0
  33. package/dist/{dist-JMLAZUL7.js → dist-XNCED7JW.js} +30 -12
  34. package/dist/generate-B7FAWVIQ.js +1 -0
  35. package/dist/index.js +25 -24
  36. package/dist/init-SECXKD5G.js +1 -0
  37. package/dist/lineage-HYO4RKZT.js +1 -0
  38. package/dist/public/assets/DataView-DUCz_96y.js +9 -0
  39. package/dist/public/assets/{EventManagement-CTuAJ0eF.js → EventManagement-BnmeJDQl.js} +1 -1
  40. package/dist/public/assets/{ExplorePage-ZJ3zNjNQ.js → ExplorePage-kk4z9ldZ.js} +1 -1
  41. package/dist/public/assets/{LoginPage-wygea4BI.js → LoginPage-CzaFkkjg.js} +1 -1
  42. package/dist/public/assets/{PublicViewer-8YqGrVVB.js → PublicViewer-irjxqH6a.js} +1 -1
  43. package/dist/public/assets/{SetupWizard-DqDBwHrF.js → SetupWizard-ConWIcmy.js} +1 -1
  44. package/dist/public/assets/{ShareManagement-TAAdI_gY.js → ShareManagement-CP4wdwLR.js} +1 -1
  45. package/dist/public/assets/SourceDetailView-DZS5518E.js +1 -0
  46. package/dist/public/assets/{UserManagement-B4kZHyri.js → UserManagement-AubGd9hl.js} +1 -1
  47. package/dist/public/assets/data-3vtzSuAZ.js +1 -0
  48. package/dist/public/assets/{index-CfyF2Wf-.css → index-C1X8RW4Z.css} +1 -1
  49. package/dist/public/assets/{index-BgzSjgIu.js → index-jlfTO7f5.js} +44 -44
  50. package/dist/public/assets/{index.es-AB-GdGyc.js → index.es-CgnvEWi5.js} +1 -1
  51. package/dist/public/assets/{jspdf.es.min-ChRx2mOQ.js → jspdf.es.min-Cw5WefMt.js} +3 -3
  52. package/dist/public/index.html +2 -2
  53. package/dist/{query-QNRDS74I.js → query-2H3YOPI2.js} +6 -5
  54. package/dist/reset-password-YVCZKZPC.js +1 -0
  55. package/dist/rewrite-database-BOA4QPUR.js +1 -0
  56. package/dist/{sample-SKLHBZBU.js → sample-ODUGGSFA.js} +6 -5
  57. package/dist/{search-4KMETZVX.js → search-IPE4ISFB.js} +7 -6
  58. package/dist/{semantic-6WKELH5V.js → semantic-K3MYXXJI.js} +4 -2
  59. package/dist/{semantic-6WKELH5V.js.map → semantic-K3MYXXJI.js.map} +1 -1
  60. package/dist/source-resolver-HZQLOODU.js +19 -0
  61. package/dist/source-resolver-HZQLOODU.js.map +1 -0
  62. package/dist/sync-dbt-72GVO75P.js +1 -0
  63. package/dist/{sync-warehouse-UWRNUXE7.js → sync-warehouse-TUNULDUY.js} +6 -5
  64. package/dist/{tables-V65QUGHK.js → tables-K5NAN2WK.js} +7 -6
  65. package/dist/templates/default/docs/yamchart-reference.md +42 -0
  66. package/dist/{test-UE5OWG3E.js → test-SRHVOXZB.js} +9 -7
  67. package/dist/{test-UE5OWG3E.js.map → test-SRHVOXZB.js.map} +1 -1
  68. package/dist/update-HWCJNQRP.js +1 -0
  69. package/package.json +8 -6
  70. package/dist/advisor-SC64RTZO.js.map +0 -1
  71. package/dist/chunk-NXQ6ZO3V.js.map +0 -1
  72. package/dist/chunk-RM6MNDVF.js.map +0 -1
  73. package/dist/chunk-S7YQXEKM.js.map +0 -1
  74. package/dist/chunk-UND73EOB.js +0 -449
  75. package/dist/chunk-UND73EOB.js.map +0 -1
  76. package/dist/connection-utils-C4FQGBW6.js +0 -19
  77. package/dist/dev-GWXHBBWB.js.map +0 -1
  78. package/dist/dist-MNXSMGV6.js.map +0 -1
  79. package/dist/dist-MX5K2ABB.js +0 -56
  80. package/dist/source-resolver-R7WBIL7M.js +0 -18
  81. /package/dist/{chunk-RMIDEBHD.js.map → chunk-S2CH4HUZ.js.map} +0 -0
  82. /package/dist/{chunk-H4L3FNLS.js.map → chunk-ZBCQNWVN.js.map} +0 -0
  83. /package/dist/{connection-utils-C4FQGBW6.js.map → connection-utils-FEUWER5I.js.map} +0 -0
  84. /package/dist/{describe-X75C2VDU.js.map → describe-MEP72B56.js.map} +0 -0
  85. /package/dist/{dist-JMLAZUL7.js.map → dist-5DQO6L2S.js.map} +0 -0
  86. /package/dist/{dist-MX5K2ABB.js.map → dist-PPAD6KOM.js.map} +0 -0
  87. /package/dist/{source-resolver-R7WBIL7M.js.map → dist-XNCED7JW.js.map} +0 -0
  88. /package/dist/{query-QNRDS74I.js.map → query-2H3YOPI2.js.map} +0 -0
  89. /package/dist/{sample-SKLHBZBU.js.map → sample-ODUGGSFA.js.map} +0 -0
  90. /package/dist/{search-4KMETZVX.js.map → search-IPE4ISFB.js.map} +0 -0
  91. /package/dist/{sync-warehouse-UWRNUXE7.js.map → sync-warehouse-TUNULDUY.js.map} +0 -0
  92. /package/dist/{tables-V65QUGHK.js.map → tables-K5NAN2WK.js.map} +0 -0
@@ -0,0 +1,919 @@
1
+ import { createRequire as __yamchartCreateRequire } from 'module'; if (!globalThis.require) globalThis.require = __yamchartCreateRequire(import.meta.url);
2
+ import {
3
+ SemanticDefinitionSchema
4
+ } from "./chunk-ZIY22VO7.js";
5
+
6
+ // ../../packages/query/dist/presets.js
7
+ import { subDays, subWeeks, subMonths, subYears, subQuarters, addDays, addWeeks, addMonths, addYears, addQuarters, startOfDay, startOfWeek, startOfYear, startOfMonth, startOfQuarter, endOfWeek, endOfMonth, endOfQuarter, endOfYear, differenceInDays, parseISO, format } from "date-fns";
8
+ function isCustomDateRange(value) {
9
+ return typeof value === "object" && value !== null && "type" in value && value.type === "custom" && "start" in value && "end" in value;
10
+ }
11
+ function expandCustomDateRange(range) {
12
+ return {
13
+ start_date: range.start,
14
+ end_date: range.end
15
+ };
16
+ }
17
+ var DATE_PRESETS = [
18
+ "today",
19
+ "yesterday",
20
+ "last_7_days",
21
+ "last_30_days",
22
+ "last_90_days",
23
+ "last_12_months",
24
+ "year_to_date",
25
+ "month_to_date",
26
+ "quarter_to_date",
27
+ "previous_month",
28
+ "previous_quarter",
29
+ "previous_year"
30
+ ];
31
+ var DATE_FORMAT = "yyyy-MM-dd";
32
+ function formatDate(date) {
33
+ return format(date, DATE_FORMAT);
34
+ }
35
+ function expandDatePreset(preset) {
36
+ const now = /* @__PURE__ */ new Date();
37
+ const today = formatDate(now);
38
+ switch (preset) {
39
+ case "today":
40
+ return {
41
+ start_date: today,
42
+ end_date: today
43
+ };
44
+ case "yesterday": {
45
+ const yesterday = formatDate(subDays(now, 1));
46
+ return {
47
+ start_date: yesterday,
48
+ end_date: yesterday
49
+ };
50
+ }
51
+ case "last_7_days": {
52
+ const yesterday = formatDate(subDays(now, 1));
53
+ return {
54
+ start_date: formatDate(subDays(now, 7)),
55
+ end_date: yesterday
56
+ };
57
+ }
58
+ case "last_30_days": {
59
+ const yesterday = formatDate(subDays(now, 1));
60
+ return {
61
+ start_date: formatDate(subDays(now, 30)),
62
+ end_date: yesterday
63
+ };
64
+ }
65
+ case "last_90_days": {
66
+ const yesterday = formatDate(subDays(now, 1));
67
+ return {
68
+ start_date: formatDate(subDays(now, 90)),
69
+ end_date: yesterday
70
+ };
71
+ }
72
+ case "last_12_months": {
73
+ const yesterday = formatDate(subDays(now, 1));
74
+ return {
75
+ start_date: formatDate(subMonths(now, 12)),
76
+ end_date: yesterday
77
+ };
78
+ }
79
+ case "year_to_date":
80
+ return {
81
+ start_date: formatDate(startOfYear(now)),
82
+ end_date: today
83
+ };
84
+ case "month_to_date":
85
+ return {
86
+ start_date: formatDate(startOfMonth(now)),
87
+ end_date: today
88
+ };
89
+ case "quarter_to_date":
90
+ return {
91
+ start_date: formatDate(startOfQuarter(now)),
92
+ end_date: today
93
+ };
94
+ case "previous_month": {
95
+ const lastMonth = subMonths(now, 1);
96
+ return {
97
+ start_date: formatDate(startOfMonth(lastMonth)),
98
+ end_date: formatDate(endOfMonth(lastMonth))
99
+ };
100
+ }
101
+ case "previous_quarter": {
102
+ const lastQuarter = subMonths(now, 3);
103
+ return {
104
+ start_date: formatDate(startOfQuarter(lastQuarter)),
105
+ end_date: formatDate(endOfQuarter(lastQuarter))
106
+ };
107
+ }
108
+ case "previous_year": {
109
+ const lastYear = subYears(now, 1);
110
+ return {
111
+ start_date: formatDate(startOfYear(lastYear)),
112
+ end_date: formatDate(endOfYear(lastYear))
113
+ };
114
+ }
115
+ default:
116
+ return null;
117
+ }
118
+ }
119
+ function computePreviousPeriod(startDate, endDate, presetName) {
120
+ const start = parseISO(startDate);
121
+ const end = parseISO(endDate);
122
+ switch (presetName) {
123
+ case "today":
124
+ case "yesterday": {
125
+ return {
126
+ start_date: formatDate(subDays(start, 1)),
127
+ end_date: formatDate(subDays(end, 1))
128
+ };
129
+ }
130
+ case "last_7_days": {
131
+ return {
132
+ start_date: formatDate(subDays(start, 7)),
133
+ end_date: formatDate(subDays(end, 7))
134
+ };
135
+ }
136
+ case "last_30_days": {
137
+ return {
138
+ start_date: formatDate(subDays(start, 30)),
139
+ end_date: formatDate(subDays(end, 30))
140
+ };
141
+ }
142
+ case "last_90_days": {
143
+ return {
144
+ start_date: formatDate(subDays(start, 90)),
145
+ end_date: formatDate(subDays(end, 90))
146
+ };
147
+ }
148
+ case "last_12_months":
149
+ case "last_365_days": {
150
+ return {
151
+ start_date: formatDate(subMonths(start, 12)),
152
+ end_date: formatDate(subMonths(end, 12))
153
+ };
154
+ }
155
+ case "year_to_date": {
156
+ return {
157
+ start_date: formatDate(subYears(start, 1)),
158
+ end_date: formatDate(subYears(end, 1))
159
+ };
160
+ }
161
+ case "month_to_date": {
162
+ return {
163
+ start_date: formatDate(subMonths(start, 1)),
164
+ end_date: formatDate(subMonths(end, 1))
165
+ };
166
+ }
167
+ case "quarter_to_date": {
168
+ return {
169
+ start_date: formatDate(subQuarters(start, 1)),
170
+ end_date: formatDate(subQuarters(end, 1))
171
+ };
172
+ }
173
+ case "previous_month": {
174
+ const shifted = subMonths(start, 1);
175
+ return {
176
+ start_date: formatDate(startOfMonth(shifted)),
177
+ end_date: formatDate(endOfMonth(shifted))
178
+ };
179
+ }
180
+ case "previous_quarter": {
181
+ const shifted = subQuarters(start, 1);
182
+ return {
183
+ start_date: formatDate(startOfQuarter(shifted)),
184
+ end_date: formatDate(endOfQuarter(shifted))
185
+ };
186
+ }
187
+ case "previous_year": {
188
+ const shifted = subYears(start, 1);
189
+ return {
190
+ start_date: formatDate(startOfYear(shifted)),
191
+ end_date: formatDate(endOfYear(shifted))
192
+ };
193
+ }
194
+ default: {
195
+ const days = differenceInDays(end, start);
196
+ return {
197
+ start_date: formatDate(subDays(start, days)),
198
+ end_date: formatDate(subDays(end, days))
199
+ };
200
+ }
201
+ }
202
+ }
203
+ function formatPeriodLabel(startDate, endDate) {
204
+ const start = parseISO(startDate);
205
+ const end = parseISO(endDate);
206
+ if (startDate === endDate) {
207
+ return format(start, "MMM d");
208
+ }
209
+ const startYear = start.getFullYear();
210
+ const endYear = end.getFullYear();
211
+ if (startYear === endYear) {
212
+ return `${format(start, "MMM d")} \u2013 ${format(end, "MMM d")}`;
213
+ }
214
+ return `${format(start, "MMM d, yyyy")} \u2013 ${format(end, "MMM d, yyyy")}`;
215
+ }
216
+ function isDatePreset(value) {
217
+ return DATE_PRESETS.includes(value);
218
+ }
219
+ function isRelativeDateRange(value) {
220
+ return typeof value === "object" && value !== null && "type" in value && value.type === "relative";
221
+ }
222
+ function expandRelativeDateRange(rel) {
223
+ const now = /* @__PURE__ */ new Date();
224
+ const today = startOfDay(now);
225
+ if (rel.direction === "current") {
226
+ switch (rel.unit) {
227
+ case "day":
228
+ return { start_date: formatDate(today), end_date: formatDate(today) };
229
+ case "week":
230
+ return {
231
+ start_date: formatDate(startOfWeek(today, { weekStartsOn: 1 })),
232
+ end_date: formatDate(endOfWeek(today, { weekStartsOn: 1 }))
233
+ };
234
+ case "month":
235
+ return { start_date: formatDate(startOfMonth(today)), end_date: formatDate(endOfMonth(today)) };
236
+ case "quarter":
237
+ return { start_date: formatDate(startOfQuarter(today)), end_date: formatDate(endOfQuarter(today)) };
238
+ case "year":
239
+ return { start_date: formatDate(startOfYear(today)), end_date: formatDate(endOfYear(today)) };
240
+ }
241
+ }
242
+ const n = rel.value ?? 1;
243
+ if (rel.direction === "previous") {
244
+ const end2 = rel.includeToday ? today : subDays(today, 1);
245
+ const shiftFns2 = { day: subDays, week: subWeeks, month: subMonths, quarter: subQuarters, year: subYears };
246
+ const start2 = shiftFns2[rel.unit](rel.includeToday ? today : today, n);
247
+ return {
248
+ start_date: formatDate(start2 < end2 ? start2 : end2),
249
+ end_date: formatDate(start2 < end2 ? end2 : start2)
250
+ };
251
+ }
252
+ const start = rel.includeToday ? today : addDays(today, 1);
253
+ const shiftFns = { day: addDays, week: addWeeks, month: addMonths, quarter: addQuarters, year: addYears };
254
+ const end = shiftFns[rel.unit](start, n);
255
+ return {
256
+ start_date: formatDate(start < end ? start : end),
257
+ end_date: formatDate(start < end ? end : start)
258
+ };
259
+ }
260
+
261
+ // ../../packages/query/dist/semantic/field-classifier.js
262
+ var DIMENSION_ID_PATTERN = /(_id|_key|_code)$/i;
263
+ var DIMENSION_NAME_PATTERN = /(_name|_label|_title|_slug)$/i;
264
+ var DIMENSION_TYPE_PATTERN = /(_type|_status|_category|_class|_group|_tier|_level|_kind|_role)$/i;
265
+ var DIMENSION_EXACT = /* @__PURE__ */ new Set([
266
+ "id",
267
+ "name",
268
+ "category",
269
+ "channel",
270
+ "country",
271
+ "region",
272
+ "city",
273
+ "state",
274
+ "source",
275
+ "medium",
276
+ "campaign",
277
+ "status",
278
+ "type",
279
+ "tier",
280
+ "segment",
281
+ "department",
282
+ "team",
283
+ "brand",
284
+ "product",
285
+ "currency",
286
+ "email",
287
+ "url"
288
+ ]);
289
+ var TIME_PATTERN = /(_at|_date|_time|_timestamp|_ts|_day|_week|_month|_year|_quarter)$/i;
290
+ var TIME_EXACT = /* @__PURE__ */ new Set([
291
+ "date",
292
+ "period",
293
+ "timestamp",
294
+ "date_day",
295
+ "month",
296
+ "year",
297
+ "quarter",
298
+ "week",
299
+ "created",
300
+ "updated",
301
+ "deleted",
302
+ "started",
303
+ "ended"
304
+ ]);
305
+ var MEASURE_COUNT_PATTERN = /(_count$|^count_|^num_|^number_of_|_total_count$)/i;
306
+ var MEASURE_AMOUNT_PATTERN = /(_amount$|_total$|_revenue$|_cost$|_price$|_spend$|_profit$|_margin$|_fee$|_tax$|_discount$|_balance$|_qty$|_quantity$|_sum$|_volume$|_weight$|_size$|_value$|_rate$|_ratio$|_score$|_percent$|_pct$)/i;
307
+ var MEASURE_EXACT = /* @__PURE__ */ new Set([
308
+ "quantity",
309
+ "revenue",
310
+ "amount",
311
+ "total",
312
+ "cost",
313
+ "price",
314
+ "profit",
315
+ "margin",
316
+ "balance",
317
+ "score",
318
+ "rate",
319
+ "ratio",
320
+ "volume",
321
+ "weight",
322
+ "spend",
323
+ "fee"
324
+ ]);
325
+ var MEASURE_AVG_PATTERN = /(_avg$|_average$|_mean$|^avg_|^average_|^mean_)/i;
326
+ var MEASURE_MIN_PATTERN = /(^min_|_min$)/i;
327
+ var MEASURE_MAX_PATTERN = /(^max_|_max$)/i;
328
+ var NUMERIC_TYPES = /^(number|numeric|int|integer|bigint|smallint|tinyint|float|double|decimal|real|money)/i;
329
+ var STRING_TYPES = /^(varchar|text|string|char|nchar|nvarchar|clob|binary|varbinary)/i;
330
+ var TIME_TYPES = /^(date|time|timestamp|datetime|interval)/i;
331
+ var BOOLEAN_TYPES = /^(bool|boolean|bit)/i;
332
+ var MEASURE_DESCRIPTION_KEYWORDS = /\b(total|sum of|count of|number of|amount|revenue|average|aggregate)\b/i;
333
+ function classifyField(input) {
334
+ const name = input.name.toLowerCase();
335
+ if (TIME_PATTERN.test(name) || TIME_EXACT.has(name)) {
336
+ return { role: "dimension", dimensionType: "time" };
337
+ }
338
+ if (DIMENSION_ID_PATTERN.test(name) || DIMENSION_NAME_PATTERN.test(name) || DIMENSION_TYPE_PATTERN.test(name) || DIMENSION_EXACT.has(name)) {
339
+ return { role: "dimension", dimensionType: "string" };
340
+ }
341
+ if (MEASURE_COUNT_PATTERN.test(name)) {
342
+ return { role: "measure", measureType: "count" };
343
+ }
344
+ if (MEASURE_AVG_PATTERN.test(name)) {
345
+ return { role: "measure", measureType: "avg" };
346
+ }
347
+ if (MEASURE_MIN_PATTERN.test(name)) {
348
+ return { role: "measure", measureType: "min" };
349
+ }
350
+ if (MEASURE_MAX_PATTERN.test(name)) {
351
+ return { role: "measure", measureType: "max" };
352
+ }
353
+ if (MEASURE_AMOUNT_PATTERN.test(name) || MEASURE_EXACT.has(name)) {
354
+ return { role: "measure", measureType: "sum" };
355
+ }
356
+ if (input.data_type) {
357
+ if (TIME_TYPES.test(input.data_type)) {
358
+ return { role: "dimension", dimensionType: "time" };
359
+ }
360
+ if (BOOLEAN_TYPES.test(input.data_type)) {
361
+ return { role: "dimension", dimensionType: "boolean" };
362
+ }
363
+ if (NUMERIC_TYPES.test(input.data_type)) {
364
+ return { role: "measure", measureType: "sum" };
365
+ }
366
+ if (STRING_TYPES.test(input.data_type)) {
367
+ return { role: "dimension", dimensionType: "string" };
368
+ }
369
+ }
370
+ if (input.description && MEASURE_DESCRIPTION_KEYWORDS.test(input.description)) {
371
+ return { role: "measure", measureType: "sum" };
372
+ }
373
+ return { role: "dimension", dimensionType: "string" };
374
+ }
375
+ function inferAggregationType(name) {
376
+ const lower = name.toLowerCase();
377
+ if (MEASURE_COUNT_PATTERN.test(lower) || lower.startsWith("num_"))
378
+ return "count";
379
+ if (MEASURE_AVG_PATTERN.test(lower))
380
+ return "avg";
381
+ if (MEASURE_MIN_PATTERN.test(lower))
382
+ return "min";
383
+ if (MEASURE_MAX_PATTERN.test(lower))
384
+ return "max";
385
+ return "sum";
386
+ }
387
+ function classifyCatalogColumns(columns) {
388
+ const measures = [];
389
+ const dimensions = [];
390
+ for (const col of columns) {
391
+ const result = classifyField({
392
+ name: col.name,
393
+ data_type: col.data_type,
394
+ description: col.description
395
+ });
396
+ if (result.role === "measure") {
397
+ measures.push({
398
+ name: col.name,
399
+ type: result.measureType ?? "sum",
400
+ description: col.description || void 0
401
+ });
402
+ } else {
403
+ dimensions.push({
404
+ name: col.name,
405
+ type: result.dimensionType ?? "string",
406
+ description: col.description || void 0
407
+ });
408
+ }
409
+ }
410
+ return { measures, dimensions };
411
+ }
412
+
413
+ // ../../packages/query/dist/semantic/model-builder.js
414
+ var DEFAULT_FILTER = {
415
+ paths: ["gold/", "marts/", "core/"],
416
+ prefixes: ["dim_", "fct_", "fact_"]
417
+ };
418
+ var SemanticModelBuilder = class {
419
+ filter;
420
+ constructor(config) {
421
+ this.filter = config?.include ?? DEFAULT_FILTER;
422
+ }
423
+ build(input) {
424
+ const { catalog, models, chartHints } = input;
425
+ const catalogEntities = catalog ? this.buildCatalogEntities(catalog) : [];
426
+ const modelEntities = models ? this.buildModelEntities(models) : [];
427
+ const allEntities = [...catalogEntities, ...modelEntities];
428
+ if (chartHints) {
429
+ this.applyChartHints(allEntities, chartHints);
430
+ }
431
+ const merged = this.mergeEntities(catalogEntities, modelEntities);
432
+ const relationships = catalog ? this.extractRelationships(catalog, merged) : [];
433
+ return {
434
+ entities: merged,
435
+ relationships,
436
+ generated_at: (/* @__PURE__ */ new Date()).toISOString()
437
+ };
438
+ }
439
+ isGoldModel(entry) {
440
+ const path = entry.path?.toLowerCase() ?? "";
441
+ const name = entry.name.toLowerCase();
442
+ const pathMatch = this.filter.paths.some((p) => path.includes(p.toLowerCase()));
443
+ const prefixMatch = this.filter.prefixes.some((p) => name.startsWith(p.toLowerCase()));
444
+ return pathMatch || prefixMatch;
445
+ }
446
+ buildCatalogEntities(catalog) {
447
+ return catalog.models.filter((entry) => this.isGoldModel(entry)).filter((entry) => entry.table).map((entry) => {
448
+ const { measures, dimensions } = classifyCatalogColumns(entry.columns ?? []);
449
+ return {
450
+ name: entry.name,
451
+ description: entry.description && entry.description !== "No description" ? entry.description : void 0,
452
+ source: "catalog",
453
+ source_table: entry.table,
454
+ measures,
455
+ dimensions
456
+ };
457
+ });
458
+ }
459
+ buildModelEntities(models) {
460
+ return models.filter((m) => m.metadata.returns && m.metadata.returns.length > 0).map((m) => {
461
+ const measures = [];
462
+ const dimensions = [];
463
+ for (const col of m.metadata.returns) {
464
+ const colType = col.type?.toLowerCase();
465
+ if (colType === "number") {
466
+ measures.push({
467
+ name: col.name,
468
+ type: this.inferAggType(col.name)
469
+ });
470
+ } else if (colType === "date" || colType === "datetime" || colType === "timestamp") {
471
+ dimensions.push({ name: col.name, type: "time" });
472
+ } else {
473
+ dimensions.push({ name: col.name, type: "string" });
474
+ }
475
+ }
476
+ const parameters = m.metadata.params?.map((p) => ({
477
+ name: p.name,
478
+ type: p.type,
479
+ default: p.default,
480
+ options: p.options
481
+ }));
482
+ return {
483
+ name: m.name,
484
+ description: m.description ?? m.metadata.description,
485
+ source: "model",
486
+ source_table: m.sourceTable ?? m.name,
487
+ measures,
488
+ dimensions,
489
+ parameters: parameters?.length ? parameters : void 0
490
+ };
491
+ });
492
+ }
493
+ inferAggType(name) {
494
+ const lower = name.toLowerCase();
495
+ if (/(_count$|^count_|^num_)/.test(lower))
496
+ return "count";
497
+ if (/(_avg$|_average$|_mean$|^avg_)/.test(lower))
498
+ return "avg";
499
+ if (/(^min_|_min$)/.test(lower))
500
+ return "min";
501
+ if (/(^max_|_max$)/.test(lower))
502
+ return "max";
503
+ return "sum";
504
+ }
505
+ applyChartHints(entities, hints) {
506
+ for (const hint of hints) {
507
+ const entity = entities.find((e) => e.name === hint.modelName || e.source_table.toLowerCase().includes(hint.modelName.toLowerCase()));
508
+ if (!entity)
509
+ continue;
510
+ for (const measureName of hint.measures) {
511
+ const dimIdx = entity.dimensions.findIndex((d) => d.name === measureName);
512
+ if (dimIdx !== -1) {
513
+ const dim = entity.dimensions.splice(dimIdx, 1)[0];
514
+ entity.measures.push({ name: dim.name, type: this.inferAggType(dim.name) });
515
+ }
516
+ }
517
+ for (const timeDim of hint.timeDimensions) {
518
+ const dim = entity.dimensions.find((d) => d.name === timeDim);
519
+ if (dim)
520
+ dim.type = "time";
521
+ }
522
+ for (const [field, format2] of Object.entries(hint.formats)) {
523
+ const measure = entity.measures.find((m) => m.name === field);
524
+ if (measure)
525
+ measure.format = format2;
526
+ }
527
+ }
528
+ }
529
+ mergeEntities(catalogEntities, modelEntities) {
530
+ const modelTables = new Set(modelEntities.map((e) => e.source_table.toLowerCase()));
531
+ const filteredCatalog = catalogEntities.filter((e) => !modelTables.has(e.source_table.toLowerCase()));
532
+ return [...filteredCatalog, ...modelEntities];
533
+ }
534
+ extractRelationships(catalog, entities) {
535
+ const entityNames = new Set(entities.map((e) => e.name));
536
+ const relationships = [];
537
+ for (const entry of catalog.models) {
538
+ if (!entityNames.has(entry.name))
539
+ continue;
540
+ if (!entry.dependsOn)
541
+ continue;
542
+ for (const dep of entry.dependsOn) {
543
+ const parts = dep.split(".");
544
+ const depName = parts[parts.length - 1];
545
+ if (!entityNames.has(depName))
546
+ continue;
547
+ const fromEntity = entities.find((e) => e.name === entry.name);
548
+ const toEntity = entities.find((e) => e.name === depName);
549
+ if (!fromEntity || !toEntity)
550
+ continue;
551
+ const fromFields = [...fromEntity.measures, ...fromEntity.dimensions].map((f) => f.name.toLowerCase());
552
+ const toFields = [...toEntity.measures, ...toEntity.dimensions].map((f) => f.name.toLowerCase());
553
+ const shared = fromFields.filter((f) => toFields.includes(f) && f.endsWith("_id"));
554
+ if (shared.length > 0) {
555
+ relationships.push({
556
+ from: { entity: entry.name, field: shared[0] },
557
+ to: { entity: depName, field: shared[0] },
558
+ type: "many_to_one"
559
+ });
560
+ }
561
+ }
562
+ }
563
+ return relationships;
564
+ }
565
+ };
566
+
567
+ // ../../packages/query/dist/semantic/query-compiler.js
568
+ var SemanticQueryCompiler = class {
569
+ compile(model, query) {
570
+ const entity = model.entities.find((e) => e.name === query.model);
571
+ if (!entity)
572
+ throw new Error(`Entity '${query.model}' not found`);
573
+ const selectParts = [];
574
+ const groupByParts = [];
575
+ for (const dimName of query.dimensions) {
576
+ selectParts.push(`"${dimName}"`);
577
+ groupByParts.push(`"${dimName}"`);
578
+ }
579
+ for (const measureName of query.measures) {
580
+ const measure = entity.measures.find((m) => m.name === measureName);
581
+ if (!measure)
582
+ throw new Error(`Measure '${measureName}' not found`);
583
+ selectParts.push(`${this.formatMeasure(measure.type, measureName)} AS "${measureName}"`);
584
+ }
585
+ const parts = [];
586
+ parts.push(`SELECT ${selectParts.join(", ")}`);
587
+ parts.push(`FROM ${entity.source_table}`);
588
+ if (query.filters.length > 0) {
589
+ const conditions = query.filters.map((f) => this.compileFilter(f));
590
+ parts.push(`WHERE ${conditions.join(" AND ")}`);
591
+ }
592
+ if (groupByParts.length > 0) {
593
+ parts.push(`GROUP BY ${groupByParts.join(", ")}`);
594
+ }
595
+ if (query.order_by) {
596
+ parts.push(`ORDER BY "${query.order_by.field}" ${query.order_by.direction.toUpperCase()}`);
597
+ }
598
+ parts.push(`LIMIT ${query.limit}`);
599
+ return parts.join("\n");
600
+ }
601
+ formatMeasure(type, column) {
602
+ if (type === "count_distinct") {
603
+ return `COUNT(DISTINCT "${column}")`;
604
+ }
605
+ return `${this.aggFunction(type)}("${column}")`;
606
+ }
607
+ aggFunction(type) {
608
+ switch (type) {
609
+ case "sum":
610
+ return "SUM";
611
+ case "count":
612
+ return "COUNT";
613
+ case "avg":
614
+ return "AVG";
615
+ case "min":
616
+ return "MIN";
617
+ case "max":
618
+ return "MAX";
619
+ }
620
+ }
621
+ compileFilter(filter) {
622
+ const field = `"${filter.dimension}"`;
623
+ const value = filter.value;
624
+ switch (filter.operator) {
625
+ case "equals":
626
+ return `${field} = ${this.quoteValue(value)}`;
627
+ case "not_equals":
628
+ return `${field} != ${this.quoteValue(value)}`;
629
+ case "contains":
630
+ return `${field} LIKE '%${this.escapeString(String(value))}%'`;
631
+ case "gt":
632
+ return `${field} > ${this.quoteValue(value)}`;
633
+ case "gte":
634
+ return `${field} >= ${this.quoteValue(value)}`;
635
+ case "lt":
636
+ return `${field} < ${this.quoteValue(value)}`;
637
+ case "lte":
638
+ return `${field} <= ${this.quoteValue(value)}`;
639
+ case "in":
640
+ return `${field} IN (${value.map((v) => this.quoteValue(v)).join(", ")})`;
641
+ case "not_in":
642
+ return `${field} NOT IN (${value.map((v) => this.quoteValue(v)).join(", ")})`;
643
+ default:
644
+ throw new Error(`Unknown filter operator: ${filter.operator}`);
645
+ }
646
+ }
647
+ quoteValue(value) {
648
+ if (typeof value === "number")
649
+ return String(value);
650
+ if (typeof value === "boolean")
651
+ return String(value);
652
+ return `'${this.escapeString(String(value))}'`;
653
+ }
654
+ escapeString(value) {
655
+ return value.replace(/'/g, "''");
656
+ }
657
+ };
658
+
659
+ // ../../packages/query/dist/semantic/validators.js
660
+ var SemanticValidationError = class extends Error {
661
+ code;
662
+ available;
663
+ constructor(code, message, available) {
664
+ super(message);
665
+ this.name = "SemanticValidationError";
666
+ this.code = code;
667
+ this.available = available;
668
+ }
669
+ };
670
+ function validateSemanticQuery(model, query) {
671
+ const entity = model.entities.find((e) => e.name === query.model);
672
+ if (!entity) {
673
+ throw new SemanticValidationError("unknown_model", `Unknown model '${query.model}'. Available models: ${model.entities.map((e) => e.name).join(", ")}`, model.entities.map((e) => e.name));
674
+ }
675
+ const measureNames = entity.measures.map((m) => m.name);
676
+ const dimensionNames = entity.dimensions.map((d) => d.name);
677
+ const allFields = [...measureNames, ...dimensionNames];
678
+ for (const m of query.measures) {
679
+ if (!measureNames.includes(m)) {
680
+ throw new SemanticValidationError("unknown_measure", `Unknown measure '${m}' on entity '${query.model}'. Available measures: ${measureNames.join(", ")}`, measureNames);
681
+ }
682
+ }
683
+ for (const d of query.dimensions) {
684
+ if (!dimensionNames.includes(d)) {
685
+ throw new SemanticValidationError("unknown_dimension", `Unknown dimension '${d}' on entity '${query.model}'. Available dimensions: ${dimensionNames.join(", ")}`, dimensionNames);
686
+ }
687
+ }
688
+ for (const f of query.filters) {
689
+ if (!dimensionNames.includes(f.dimension)) {
690
+ throw new SemanticValidationError("unknown_filter_dimension", `Unknown filter dimension '${f.dimension}' on entity '${query.model}'. Available dimensions: ${dimensionNames.join(", ")}`, dimensionNames);
691
+ }
692
+ }
693
+ if (query.order_by) {
694
+ if (!allFields.includes(query.order_by.field)) {
695
+ throw new SemanticValidationError("unknown_order_field", `Unknown order_by field '${query.order_by.field}' on entity '${query.model}'. Available fields: ${allFields.join(", ")}`, allFields);
696
+ }
697
+ }
698
+ }
699
+
700
+ // ../../packages/query/dist/semantic/loader.js
701
+ import { readFileSync, existsSync, readdirSync } from "fs";
702
+ import { join } from "path";
703
+ import { parse as parseYaml } from "yaml";
704
+ function toResolvedEntity(def, resolveTable) {
705
+ const sourceRef = def.source.table ?? resolveTable(def.source.model);
706
+ return {
707
+ name: def.entity,
708
+ sourceRef,
709
+ timeDimension: def.time_dimension,
710
+ dimensions: def.dimensions,
711
+ filters: def.filters ?? {},
712
+ metrics: def.metrics
713
+ };
714
+ }
715
+ function buildSemanticLayer(defs, resolveTable = (m) => m) {
716
+ const entities = defs.map((d) => toResolvedEntity(d, resolveTable));
717
+ const byMetricKey = /* @__PURE__ */ new Map();
718
+ const list = [];
719
+ for (const entity of entities) {
720
+ for (const metric of entity.metrics) {
721
+ const resolved = { metric, entity };
722
+ byMetricKey.set(metric.name.toLowerCase(), resolved);
723
+ for (const syn of metric.synonyms ?? []) {
724
+ if (!byMetricKey.has(syn.toLowerCase()))
725
+ byMetricKey.set(syn.toLowerCase(), resolved);
726
+ }
727
+ list.push({
728
+ name: metric.name,
729
+ label: metric.label,
730
+ description: metric.description,
731
+ entity: entity.name,
732
+ agg: metric.agg,
733
+ synonyms: metric.synonyms ?? [],
734
+ format: metric.format,
735
+ dimensions: entity.dimensions.map((d) => d.name)
736
+ });
737
+ }
738
+ }
739
+ return {
740
+ listMetrics: () => list,
741
+ resolveMetric: (key) => byMetricKey.get(key.toLowerCase()),
742
+ getEntity: (name) => entities.find((e) => e.name === name)
743
+ };
744
+ }
745
+ function loadSemanticDefinitions(projectDir) {
746
+ const dir = join(projectDir, "semantic");
747
+ if (!existsSync(dir))
748
+ return [];
749
+ const defs = [];
750
+ for (const file of readdirSync(dir)) {
751
+ if (!/\.ya?ml$/.test(file))
752
+ continue;
753
+ const raw = parseYaml(readFileSync(join(dir, file), "utf8"));
754
+ const entries = Array.isArray(raw?.entities) ? raw.entities : [raw];
755
+ for (const entry of entries) {
756
+ defs.push(SemanticDefinitionSchema.parse(entry));
757
+ }
758
+ }
759
+ return defs;
760
+ }
761
+
762
+ // ../../packages/query/dist/semantic/dialect.js
763
+ var stringFirst = {
764
+ dateTrunc: (col, grain) => `date_trunc('${grain}', ${col})`,
765
+ quoteIdent: (id) => `"${id}"`
766
+ };
767
+ var bigquery = {
768
+ dateTrunc: (col, grain) => `DATE_TRUNC(${col}, ${grain.toUpperCase()})`,
769
+ quoteIdent: (id) => "`" + id + "`"
770
+ };
771
+ function getDialect(connectionType) {
772
+ switch ((connectionType || "").toLowerCase()) {
773
+ case "bigquery":
774
+ return bigquery;
775
+ case "duckdb":
776
+ case "postgres":
777
+ case "snowflake":
778
+ case "mysql":
779
+ return stringFirst;
780
+ default:
781
+ return stringFirst;
782
+ }
783
+ }
784
+
785
+ // ../../packages/query/dist/semantic/metric-compiler.js
786
+ var AGG_FN = {
787
+ sum: "SUM",
788
+ count: "COUNT",
789
+ avg: "AVG",
790
+ min: "MIN",
791
+ max: "MAX",
792
+ count_distinct: "COUNT(DISTINCT"
793
+ };
794
+ function aggExpr(metric, filterSql) {
795
+ if (metric.sql)
796
+ return `${metric.sql} AS ${metric.name}`;
797
+ const inner = filterSql ? `CASE WHEN ${filterSql} THEN ${metric.field} END` : metric.field;
798
+ if (metric.agg === "count_distinct")
799
+ return `COUNT(DISTINCT ${inner}) AS ${metric.name}`;
800
+ return `${AGG_FN[metric.agg]}(${inner}) AS ${metric.name}`;
801
+ }
802
+ function sqlLiteral(value) {
803
+ if (typeof value === "number" || typeof value === "boolean")
804
+ return String(value);
805
+ if (Array.isArray(value))
806
+ return `(${value.map(sqlLiteral).join(", ")})`;
807
+ return `'${String(value).replace(/'/g, "''")}'`;
808
+ }
809
+ var OPERATORS = {
810
+ equals: "=",
811
+ not_equals: "!=",
812
+ gt: ">",
813
+ gte: ">=",
814
+ lt: "<",
815
+ lte: "<=",
816
+ in: "IN",
817
+ not_in: "NOT IN"
818
+ };
819
+ var DATE_RE = /^\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2}(:\d{2}(\.\d+)?)?Z?)?$/;
820
+ function compileMetricQuery(entity, query, connectionType) {
821
+ const dialect = getDialect(connectionType);
822
+ const select = [];
823
+ const groupCols = [];
824
+ for (const dimName of query.dimensions ?? []) {
825
+ const dim = entity.dimensions.find((d) => d.name === dimName);
826
+ if (!dim)
827
+ throw new Error(`Unknown dimension "${dimName}" on entity "${entity.name}".`);
828
+ if (dim.type === "time" && query.grain) {
829
+ select.push(`${dialect.dateTrunc(dim.name, query.grain)} AS ${dim.name}`);
830
+ } else {
831
+ select.push(dim.name);
832
+ }
833
+ groupCols.push(dim.name);
834
+ }
835
+ if (query.grain && entity.timeDimension && !(query.dimensions ?? []).includes(entity.timeDimension)) {
836
+ select.unshift(`${dialect.dateTrunc(entity.timeDimension, query.grain)} AS ${entity.timeDimension}`);
837
+ groupCols.unshift(entity.timeDimension);
838
+ }
839
+ const resolvedMetrics = [];
840
+ for (const metricName of query.metrics) {
841
+ const metric = entity.metrics.find((m) => m.name === metricName);
842
+ if (!metric)
843
+ throw new Error(`Unknown metric "${metricName}" on entity "${entity.name}".`);
844
+ const filterSql = metric.filter ? entity.filters[metric.filter]?.sql : void 0;
845
+ if (metric.filter && !filterSql) {
846
+ throw new Error(`Metric "${metricName}" references unknown filter "${metric.filter}".`);
847
+ }
848
+ select.push(aggExpr(metric, filterSql));
849
+ resolvedMetrics.push(metric);
850
+ }
851
+ const where = [];
852
+ if (query.time_range && entity.timeDimension) {
853
+ let start_date;
854
+ let end_date;
855
+ if ("preset" in query.time_range) {
856
+ const expanded = expandDatePreset(query.time_range.preset);
857
+ if (!expanded) {
858
+ throw new Error(`Unrecognized date preset "${query.time_range.preset}".`);
859
+ }
860
+ start_date = expanded.start_date;
861
+ end_date = expanded.end_date;
862
+ } else {
863
+ start_date = query.time_range.start;
864
+ end_date = query.time_range.end;
865
+ if (!DATE_RE.test(start_date) || !DATE_RE.test(end_date)) {
866
+ throw new Error("time_range.start and time_range.end must be YYYY-MM-DD (optionally with a time).");
867
+ }
868
+ }
869
+ where.push(`${entity.timeDimension} >= '${start_date}'`);
870
+ where.push(`${entity.timeDimension} <= '${end_date}'`);
871
+ }
872
+ for (const f of query.filters ?? []) {
873
+ const op = OPERATORS[f.operator];
874
+ if (!op)
875
+ throw new Error(`Unsupported filter operator "${f.operator}".`);
876
+ if (!entity.dimensions.some((d) => d.name === f.dimension)) {
877
+ throw new Error(`Unknown filter dimension "${f.dimension}" on entity "${entity.name}".`);
878
+ }
879
+ where.push(`${dialect.quoteIdent(f.dimension)} ${op} ${sqlLiteral(f.value)}`);
880
+ }
881
+ const lines = [`SELECT ${select.join(", ")}`, `FROM ${entity.sourceRef}`];
882
+ if (where.length)
883
+ lines.push(`WHERE ${where.join(" AND ")}`);
884
+ if (groupCols.length)
885
+ lines.push(`GROUP BY ${groupCols.map((_, i) => i + 1).join(", ")}`);
886
+ if (query.order_by) {
887
+ const selectable = /* @__PURE__ */ new Set([...groupCols, ...resolvedMetrics.map((m) => m.name)]);
888
+ if (!selectable.has(query.order_by.field)) {
889
+ throw new Error(`order_by.field "${query.order_by.field}" is not a selected column. Choose one of: ${[...selectable].join(", ")}.`);
890
+ }
891
+ lines.push(`ORDER BY ${dialect.quoteIdent(query.order_by.field)} ${(query.order_by.direction ?? "asc").toUpperCase()}`);
892
+ }
893
+ lines.push(`LIMIT ${query.limit ?? 1e3}`);
894
+ return { sql: lines.join("\n"), metrics: resolvedMetrics, groupCols };
895
+ }
896
+
897
+ export {
898
+ isCustomDateRange,
899
+ expandCustomDateRange,
900
+ DATE_PRESETS,
901
+ expandDatePreset,
902
+ computePreviousPeriod,
903
+ formatPeriodLabel,
904
+ isDatePreset,
905
+ isRelativeDateRange,
906
+ expandRelativeDateRange,
907
+ classifyField,
908
+ inferAggregationType,
909
+ classifyCatalogColumns,
910
+ SemanticModelBuilder,
911
+ SemanticQueryCompiler,
912
+ SemanticValidationError,
913
+ validateSemanticQuery,
914
+ buildSemanticLayer,
915
+ loadSemanticDefinitions,
916
+ getDialect,
917
+ compileMetricQuery
918
+ };
919
+ //# sourceMappingURL=chunk-UFDQ3C7Q.js.map