yamchart 0.1.4 → 0.3.3

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 (39) hide show
  1. package/dist/chunk-3CLMQNNR.js +64 -0
  2. package/dist/chunk-3CLMQNNR.js.map +1 -0
  3. package/dist/chunk-4P5UHWYK.js +247 -0
  4. package/dist/chunk-4P5UHWYK.js.map +1 -0
  5. package/dist/chunk-A24KVXJQ.js +922 -0
  6. package/dist/chunk-A24KVXJQ.js.map +1 -0
  7. package/dist/chunk-DGUM43GV.js +11 -0
  8. package/dist/chunk-DGUM43GV.js.map +1 -0
  9. package/dist/chunk-HJVVHYVN.js +59 -0
  10. package/dist/chunk-HJVVHYVN.js.map +1 -0
  11. package/dist/chunk-LL3VKMZM.js +1265 -0
  12. package/dist/chunk-LL3VKMZM.js.map +1 -0
  13. package/dist/dev-PTVNJI7G.js +13557 -0
  14. package/dist/dev-PTVNJI7G.js.map +1 -0
  15. package/dist/dist-JH7OL7U4.js +16 -0
  16. package/dist/dist-JH7OL7U4.js.map +1 -0
  17. package/dist/dist-JKJLH3F6.js +56 -0
  18. package/dist/dist-JKJLH3F6.js.map +1 -0
  19. package/dist/{generate-RD3LCS73.js → generate-KNER36CB.js} +3 -1
  20. package/dist/{generate-RD3LCS73.js.map → generate-KNER36CB.js.map} +1 -1
  21. package/dist/index.js +89 -10
  22. package/dist/index.js.map +1 -1
  23. package/dist/{init-6D5VNGSP.js → init-FTSEOTAD.js} +3 -1
  24. package/dist/{init-6D5VNGSP.js.map → init-FTSEOTAD.js.map} +1 -1
  25. package/dist/reset-password-IZQTDTU7.js +60 -0
  26. package/dist/reset-password-IZQTDTU7.js.map +1 -0
  27. package/dist/{sync-dbt-IDDD4X2Z.js → sync-dbt-6WY7HKP7.js} +3 -1
  28. package/dist/{sync-dbt-IDDD4X2Z.js.map → sync-dbt-6WY7HKP7.js.map} +1 -1
  29. package/dist/templates/default/CLAUDE.md +9 -0
  30. package/dist/templates/default/docs/yamchart-reference.md +315 -0
  31. package/dist/test-WYNX4RYZ.js +222 -0
  32. package/dist/test-WYNX4RYZ.js.map +1 -0
  33. package/dist/update-GWPF5AS6.js +76 -0
  34. package/dist/update-GWPF5AS6.js.map +1 -0
  35. package/package.json +25 -6
  36. package/dist/chunk-TBILHUB3.js +0 -365
  37. package/dist/chunk-TBILHUB3.js.map +0 -1
  38. package/dist/dev-UHYN2RXH.js +0 -85
  39. package/dist/dev-UHYN2RXH.js.map +0 -1
@@ -0,0 +1,1265 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-DGUM43GV.js";
4
+
5
+ // ../../packages/query/dist/parser.js
6
+ function parseModelMetadata(sql) {
7
+ const lines = sql.split("\n");
8
+ const metadataLines = [];
9
+ const sqlLines = [];
10
+ let inMetadata = true;
11
+ let inMultiline = null;
12
+ for (const line of lines) {
13
+ const trimmed = line.trim();
14
+ if (trimmed.startsWith("-- @") || inMultiline && trimmed.startsWith("--")) {
15
+ metadataLines.push(trimmed);
16
+ inMetadata = true;
17
+ } else if (trimmed.startsWith("--") && inMetadata && metadataLines.length > 0) {
18
+ metadataLines.push(trimmed);
19
+ } else if (trimmed === "" && inMetadata) {
20
+ continue;
21
+ } else {
22
+ inMetadata = false;
23
+ sqlLines.push(line);
24
+ }
25
+ }
26
+ const metadata = parseMetadataLines(metadataLines);
27
+ if (!metadata.name) {
28
+ throw new Error("Model must have a @name");
29
+ }
30
+ return {
31
+ ...metadata,
32
+ name: metadata.name,
33
+ sql: sqlLines.join("\n").trim()
34
+ };
35
+ }
36
+ function parseMetadataLines(lines) {
37
+ const result = {};
38
+ const params = [];
39
+ const returns = [];
40
+ const tests = [];
41
+ let currentMultiline = null;
42
+ for (const line of lines) {
43
+ const content = line.replace(/^--\s*/, "").trim();
44
+ if (currentMultiline && content.startsWith("- ")) {
45
+ const itemContent = content.slice(2).trim();
46
+ if (currentMultiline === "returns") {
47
+ const returnCol = parseReturnColumn(itemContent);
48
+ if (returnCol)
49
+ returns.push(returnCol);
50
+ } else if (currentMultiline === "tests") {
51
+ tests.push(itemContent);
52
+ }
53
+ continue;
54
+ }
55
+ if (content.startsWith("@")) {
56
+ currentMultiline = null;
57
+ if (content.startsWith("@name:")) {
58
+ result.name = content.slice(6).trim();
59
+ } else if (content.startsWith("@description:")) {
60
+ result.description = content.slice(13).trim();
61
+ } else if (content.startsWith("@owner:")) {
62
+ result.owner = content.slice(7).trim();
63
+ } else if (content.startsWith("@tags:")) {
64
+ result.tags = parseTags(content.slice(6).trim());
65
+ } else if (content.startsWith("@param")) {
66
+ const param = parseParam(content.slice(6).trim());
67
+ if (param)
68
+ params.push(param);
69
+ } else if (content.startsWith("@returns:")) {
70
+ currentMultiline = "returns";
71
+ } else if (content.startsWith("@tests:")) {
72
+ currentMultiline = "tests";
73
+ }
74
+ }
75
+ }
76
+ if (params.length > 0)
77
+ result.params = params;
78
+ if (returns.length > 0)
79
+ result.returns = returns;
80
+ if (tests.length > 0)
81
+ result.tests = tests;
82
+ return result;
83
+ }
84
+ function parseTags(input) {
85
+ const match = input.match(/\[(.*)\]/);
86
+ if (!match?.[1])
87
+ return [];
88
+ return match[1].split(",").map((t) => t.trim());
89
+ }
90
+ function parseParam(input) {
91
+ const match = input.match(/^(\w+):\s*(\w+(?:\[\])?)\s*(?:=\s*([^{]+))?\s*(?:\{([^}]+)\})?/);
92
+ if (!match)
93
+ return null;
94
+ const [, name, type, defaultValue, options] = match;
95
+ if (!name || !type)
96
+ return null;
97
+ const param = {
98
+ name: name.trim(),
99
+ type: type.trim()
100
+ };
101
+ if (defaultValue) {
102
+ param.default = defaultValue.trim();
103
+ }
104
+ if (options) {
105
+ param.options = options.split(",").map((o) => o.trim());
106
+ }
107
+ return param;
108
+ }
109
+ function parseReturnColumn(input) {
110
+ const match = input.match(/^(\w+):\s*(\w+)\s*(?:--\s*(.+))?/);
111
+ if (!match)
112
+ return null;
113
+ const [, name, type, description] = match;
114
+ if (!name || !type)
115
+ return null;
116
+ const col = {
117
+ name: name.trim(),
118
+ type: type.trim()
119
+ };
120
+ if (description) {
121
+ col.description = description.trim();
122
+ }
123
+ return col;
124
+ }
125
+
126
+ // ../../packages/query/dist/presets.js
127
+ import { subDays, subMonths, subYears, startOfYear, startOfMonth, startOfQuarter, endOfMonth, endOfQuarter, endOfYear, format } from "date-fns";
128
+ function isCustomDateRange(value) {
129
+ return typeof value === "object" && value !== null && "type" in value && value.type === "custom" && "start" in value && "end" in value;
130
+ }
131
+ function expandCustomDateRange(range) {
132
+ return {
133
+ start_date: range.start,
134
+ end_date: range.end
135
+ };
136
+ }
137
+ var DATE_PRESETS = [
138
+ "last_7_days",
139
+ "last_30_days",
140
+ "last_90_days",
141
+ "last_12_months",
142
+ "year_to_date",
143
+ "month_to_date",
144
+ "quarter_to_date",
145
+ "previous_month",
146
+ "previous_quarter",
147
+ "previous_year"
148
+ ];
149
+ var DATE_FORMAT = "yyyy-MM-dd";
150
+ function formatDate(date) {
151
+ return format(date, DATE_FORMAT);
152
+ }
153
+ function expandDatePreset(preset) {
154
+ const now = /* @__PURE__ */ new Date();
155
+ const today = formatDate(now);
156
+ switch (preset) {
157
+ case "last_7_days":
158
+ return {
159
+ start_date: formatDate(subDays(now, 7)),
160
+ end_date: today
161
+ };
162
+ case "last_30_days":
163
+ return {
164
+ start_date: formatDate(subDays(now, 30)),
165
+ end_date: today
166
+ };
167
+ case "last_90_days":
168
+ return {
169
+ start_date: formatDate(subDays(now, 90)),
170
+ end_date: today
171
+ };
172
+ case "last_12_months":
173
+ return {
174
+ start_date: formatDate(subMonths(now, 12)),
175
+ end_date: today
176
+ };
177
+ case "year_to_date":
178
+ return {
179
+ start_date: formatDate(startOfYear(now)),
180
+ end_date: today
181
+ };
182
+ case "month_to_date":
183
+ return {
184
+ start_date: formatDate(startOfMonth(now)),
185
+ end_date: today
186
+ };
187
+ case "quarter_to_date":
188
+ return {
189
+ start_date: formatDate(startOfQuarter(now)),
190
+ end_date: today
191
+ };
192
+ case "previous_month": {
193
+ const lastMonth = subMonths(now, 1);
194
+ return {
195
+ start_date: formatDate(startOfMonth(lastMonth)),
196
+ end_date: formatDate(endOfMonth(lastMonth))
197
+ };
198
+ }
199
+ case "previous_quarter": {
200
+ const lastQuarter = subMonths(now, 3);
201
+ return {
202
+ start_date: formatDate(startOfQuarter(lastQuarter)),
203
+ end_date: formatDate(endOfQuarter(lastQuarter))
204
+ };
205
+ }
206
+ case "previous_year": {
207
+ const lastYear = subYears(now, 1);
208
+ return {
209
+ start_date: formatDate(startOfYear(lastYear)),
210
+ end_date: formatDate(endOfYear(lastYear))
211
+ };
212
+ }
213
+ default:
214
+ return null;
215
+ }
216
+ }
217
+ function isDatePreset(value) {
218
+ return DATE_PRESETS.includes(value);
219
+ }
220
+
221
+ // ../../packages/query/dist/template.js
222
+ import nunjucks from "nunjucks";
223
+ var env = new nunjucks.Environment(null, {
224
+ autoescape: false,
225
+ // SQL doesn't need HTML escaping
226
+ throwOnUndefined: false
227
+ // user.x may be undefined for RLS
228
+ });
229
+ function createTemplateContext(params, refs = {}, userContext) {
230
+ return {
231
+ ...params,
232
+ user: userContext ?? {},
233
+ ref: (name) => {
234
+ const resolved = refs[name];
235
+ if (resolved === void 0) {
236
+ throw new Error(`Unknown model reference: ${name}`);
237
+ }
238
+ return resolved;
239
+ }
240
+ };
241
+ }
242
+ function renderTemplate(template, context) {
243
+ const rendered = env.renderString(template, context);
244
+ return rendered.split("\n").map((line) => line.trimEnd()).filter((line, i, arr) => {
245
+ if (line === "" && arr[i - 1] === "")
246
+ return false;
247
+ return true;
248
+ }).join("\n").trim();
249
+ }
250
+ function templateHasVariable(template, varName) {
251
+ const regex = new RegExp(`\\{\\{\\s*${varName}(?:\\s*\\|[^}]+)?\\s*\\}\\}`, "g");
252
+ return regex.test(template);
253
+ }
254
+ function extractTemplateVariables(template) {
255
+ const variables = /* @__PURE__ */ new Set();
256
+ const regex = /\{\{\s*(\w+)(?:\s*\|[^}]+)?\s*\}\}/g;
257
+ let match;
258
+ while ((match = regex.exec(template)) !== null) {
259
+ const varName = match[1];
260
+ if (varName && varName !== "ref" && varName !== "loop") {
261
+ variables.add(varName);
262
+ }
263
+ }
264
+ return Array.from(variables);
265
+ }
266
+
267
+ // ../../packages/query/dist/compiler.js
268
+ import { createHash } from "crypto";
269
+ import { format as format2 } from "date-fns";
270
+ function resolveDynamicDefault(value) {
271
+ if (typeof value !== "string")
272
+ return value;
273
+ const trimmed = value.trim().toLowerCase();
274
+ if (trimmed === "current_date()" || trimmed === "current_date" || trimmed === "now()") {
275
+ return format2(/* @__PURE__ */ new Date(), "yyyy-MM-dd");
276
+ }
277
+ if (trimmed.includes("current_date")) {
278
+ return format2(/* @__PURE__ */ new Date(), "yyyy-MM-dd");
279
+ }
280
+ return value;
281
+ }
282
+ var QueryCompiler = class {
283
+ models;
284
+ refs;
285
+ constructor(config) {
286
+ this.models = new Map(Object.entries(config.models));
287
+ this.refs = config.refs;
288
+ }
289
+ /**
290
+ * Compile a chart definition into an executable SQL query.
291
+ */
292
+ compile(chart, inputParams, userContext) {
293
+ const { sql, modelParams } = this.getSQL(chart);
294
+ const params = this.resolveParams(chart, modelParams, inputParams);
295
+ const expandedParams = this.expandPresets(params);
296
+ const context = createTemplateContext(expandedParams, this.refs, userContext);
297
+ const renderedSQL = renderTemplate(sql, context);
298
+ const cacheKey = this.generateCacheKey(chart.name, renderedSQL, expandedParams, userContext);
299
+ return {
300
+ sql: renderedSQL,
301
+ params: expandedParams,
302
+ cacheKey,
303
+ chartName: chart.name
304
+ };
305
+ }
306
+ getSQL(chart) {
307
+ if (chart.source.sql) {
308
+ return { sql: chart.source.sql, modelParams: void 0 };
309
+ }
310
+ if (chart.source.model) {
311
+ const model = this.models.get(chart.source.model);
312
+ if (!model) {
313
+ throw new Error(`Unknown model: ${chart.source.model}`);
314
+ }
315
+ return { sql: model.sql, modelParams: model.metadata.params };
316
+ }
317
+ throw new Error("Chart source must specify either model or sql");
318
+ }
319
+ resolveParams(chart, modelParams, inputParams) {
320
+ const params = {};
321
+ if (modelParams) {
322
+ for (const param of modelParams) {
323
+ if (param.default !== void 0) {
324
+ params[param.name] = resolveDynamicDefault(param.default);
325
+ }
326
+ }
327
+ }
328
+ if (chart.parameters) {
329
+ for (const param of chart.parameters) {
330
+ if (param.default !== void 0) {
331
+ params[param.name] = resolveDynamicDefault(param.default);
332
+ }
333
+ }
334
+ }
335
+ Object.assign(params, inputParams);
336
+ return params;
337
+ }
338
+ expandPresets(params) {
339
+ const expanded = { ...params };
340
+ if (isCustomDateRange(params.date_range)) {
341
+ const dateRange = expandCustomDateRange(params.date_range);
342
+ expanded.start_date = dateRange.start_date;
343
+ expanded.end_date = dateRange.end_date;
344
+ } else if (typeof params.date_range === "string" && isDatePreset(params.date_range)) {
345
+ const dateRange = expandDatePreset(params.date_range);
346
+ if (dateRange) {
347
+ expanded.start_date = dateRange.start_date;
348
+ expanded.end_date = dateRange.end_date;
349
+ }
350
+ }
351
+ return expanded;
352
+ }
353
+ generateCacheKey(chartName, sql, params, userContext) {
354
+ const sqlHash = createHash("sha256").update(sql).digest("hex").slice(0, 8);
355
+ const paramsHash = createHash("sha256").update(JSON.stringify(params, Object.keys(params).sort())).digest("hex").slice(0, 8);
356
+ let key = `${chartName}:${sqlHash}:${paramsHash}`;
357
+ if (userContext && Object.keys(userContext).length > 0) {
358
+ const userHash = createHash("sha256").update(JSON.stringify(userContext, Object.keys(userContext).sort())).digest("hex").slice(0, 8);
359
+ key += `:${userHash}`;
360
+ }
361
+ return key;
362
+ }
363
+ /**
364
+ * Add or update a model in the compiler.
365
+ */
366
+ addModel(name, metadata, sql) {
367
+ this.models.set(name, { metadata, sql });
368
+ }
369
+ /**
370
+ * Add or update a ref mapping.
371
+ */
372
+ addRef(name, target) {
373
+ this.refs[name] = target;
374
+ }
375
+ };
376
+
377
+ // ../../packages/query/dist/connectors/duckdb.js
378
+ import duckdb from "duckdb";
379
+ import { performance as performance2 } from "perf_hooks";
380
+ var DuckDBConnector = class {
381
+ config;
382
+ db = null;
383
+ connection = null;
384
+ constructor(config) {
385
+ this.config = config;
386
+ }
387
+ async connect() {
388
+ return new Promise((resolve, reject) => {
389
+ this.db = new duckdb.Database(this.config.path, (err) => {
390
+ if (err) {
391
+ reject(err);
392
+ return;
393
+ }
394
+ this.connection = this.db.connect();
395
+ resolve();
396
+ });
397
+ });
398
+ }
399
+ async disconnect() {
400
+ return new Promise((resolve) => {
401
+ if (this.connection) {
402
+ this.connection.close(() => {
403
+ this.connection = null;
404
+ });
405
+ }
406
+ if (this.db) {
407
+ this.db.close(() => {
408
+ this.db = null;
409
+ resolve();
410
+ });
411
+ } else {
412
+ resolve();
413
+ }
414
+ });
415
+ }
416
+ async execute(sql) {
417
+ if (!this.connection) {
418
+ throw new Error("Not connected to database");
419
+ }
420
+ const startTime = performance2.now();
421
+ return new Promise((resolve, reject) => {
422
+ const stmt = this.connection.prepare(sql, (prepErr) => {
423
+ if (prepErr) {
424
+ reject(prepErr);
425
+ return;
426
+ }
427
+ const columnInfo = stmt.columns();
428
+ stmt.all((err, rows) => {
429
+ const durationMs = performance2.now() - startTime;
430
+ if (err) {
431
+ reject(err);
432
+ return;
433
+ }
434
+ const typedRows = rows;
435
+ const serializedRows = typedRows.map((row) => this.serializeRow(row));
436
+ const columns = columnInfo.map((col) => ({
437
+ name: col.name,
438
+ type: col.type.sql_type
439
+ }));
440
+ stmt.finalize();
441
+ resolve({
442
+ columns,
443
+ rows: serializedRows,
444
+ rowCount: serializedRows.length,
445
+ durationMs
446
+ });
447
+ });
448
+ });
449
+ });
450
+ }
451
+ isConnected() {
452
+ return this.connection !== null;
453
+ }
454
+ async explain(sql) {
455
+ if (!this.connection) {
456
+ throw new Error("Not connected to database");
457
+ }
458
+ return new Promise((resolve) => {
459
+ this.connection.all(`EXPLAIN ${sql}`, (err) => {
460
+ if (err) {
461
+ resolve({ valid: false, error: err.message });
462
+ } else {
463
+ resolve({ valid: true });
464
+ }
465
+ });
466
+ });
467
+ }
468
+ serializeRow(row) {
469
+ const result = {};
470
+ for (const [key, value] of Object.entries(row)) {
471
+ if (typeof value === "bigint") {
472
+ result[key] = Number(value);
473
+ } else {
474
+ result[key] = value;
475
+ }
476
+ }
477
+ return result;
478
+ }
479
+ };
480
+
481
+ // ../../packages/query/dist/connectors/postgres.js
482
+ import { Pool, types } from "pg";
483
+ import { performance as performance3 } from "perf_hooks";
484
+ types.setTypeParser(20, (val) => {
485
+ if (val === null)
486
+ return null;
487
+ return BigInt(val);
488
+ });
489
+ function escapeIdentifier(identifier) {
490
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier)) {
491
+ throw new Error(`Invalid identifier: ${identifier}`);
492
+ }
493
+ return `"${identifier}"`;
494
+ }
495
+ var PostgresConnector = class {
496
+ config;
497
+ pool = null;
498
+ constructor(config) {
499
+ this.config = config;
500
+ }
501
+ async connect() {
502
+ const poolConfig = {
503
+ host: this.config.host,
504
+ port: this.config.port,
505
+ database: this.config.database,
506
+ user: this.config.user,
507
+ password: this.config.password,
508
+ ssl: this.config.ssl,
509
+ min: this.config.min ?? 2,
510
+ max: this.config.max ?? 10,
511
+ idleTimeoutMillis: this.config.idleTimeoutMillis ?? 3e4,
512
+ connectionTimeoutMillis: this.config.connectTimeoutMillis ?? 1e4
513
+ };
514
+ if (this.config.statementTimeout) {
515
+ poolConfig.statement_timeout = this.config.statementTimeout;
516
+ }
517
+ this.pool = new Pool(poolConfig);
518
+ this.pool.on("error", (err) => {
519
+ console.error("Unexpected postgres pool error:", err.message);
520
+ });
521
+ if (this.config.schema) {
522
+ const schema = escapeIdentifier(this.config.schema);
523
+ this.pool.on("connect", (client2) => {
524
+ client2.query(`SET search_path TO ${schema}`).catch((err) => {
525
+ console.error("Failed to set search_path:", err.message);
526
+ });
527
+ });
528
+ }
529
+ const client = await this.pool.connect();
530
+ client.release();
531
+ }
532
+ async disconnect() {
533
+ if (this.pool) {
534
+ await this.pool.end();
535
+ this.pool = null;
536
+ }
537
+ }
538
+ isConnected() {
539
+ return this.pool !== null;
540
+ }
541
+ async execute(sql) {
542
+ if (!this.pool) {
543
+ throw new Error("Not connected to database");
544
+ }
545
+ const start = performance3.now();
546
+ const result = await this.pool.query(sql);
547
+ const durationMs = performance3.now() - start;
548
+ return {
549
+ columns: this.extractColumns(result),
550
+ rows: result.rows.map((row) => this.serializeRow(row)),
551
+ rowCount: result.rowCount ?? result.rows.length,
552
+ durationMs
553
+ };
554
+ }
555
+ async explain(sql) {
556
+ if (!this.pool) {
557
+ throw new Error("Not connected to database");
558
+ }
559
+ try {
560
+ await this.pool.query(`EXPLAIN ${sql}`);
561
+ return { valid: true };
562
+ } catch (err) {
563
+ return {
564
+ valid: false,
565
+ error: err instanceof Error ? err.message : String(err)
566
+ };
567
+ }
568
+ }
569
+ extractColumns(result) {
570
+ return result.fields.map((field) => ({
571
+ name: field.name,
572
+ type: this.pgTypeToString(field.dataTypeID)
573
+ }));
574
+ }
575
+ pgTypeToString(oid) {
576
+ const typeMap = {
577
+ 16: "boolean",
578
+ // bool
579
+ 20: "integer",
580
+ // int8
581
+ 21: "integer",
582
+ // int2
583
+ 23: "integer",
584
+ // int4
585
+ 700: "number",
586
+ // float4
587
+ 701: "number",
588
+ // float8
589
+ 1700: "number",
590
+ // numeric
591
+ 25: "string",
592
+ // text
593
+ 1043: "string",
594
+ // varchar
595
+ 1082: "date",
596
+ // date
597
+ 1114: "date",
598
+ // timestamp
599
+ 1184: "date",
600
+ // timestamptz
601
+ 114: "unknown",
602
+ // json
603
+ 3802: "unknown"
604
+ // jsonb
605
+ };
606
+ return typeMap[oid] ?? "unknown";
607
+ }
608
+ serializeRow(row) {
609
+ const serialized = {};
610
+ for (const [key, value] of Object.entries(row)) {
611
+ serialized[key] = this.serializeValue(value);
612
+ }
613
+ return serialized;
614
+ }
615
+ serializeValue(value) {
616
+ if (value === null || value === void 0) {
617
+ return null;
618
+ }
619
+ if (typeof value === "bigint") {
620
+ if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) {
621
+ return value.toString();
622
+ }
623
+ return Number(value);
624
+ }
625
+ if (value instanceof Date) {
626
+ return value.toISOString();
627
+ }
628
+ return value;
629
+ }
630
+ };
631
+
632
+ // ../../packages/query/dist/connectors/mysql.js
633
+ import { createPool } from "mysql2/promise";
634
+ import { performance as performance4 } from "perf_hooks";
635
+ var MySQLConnector = class {
636
+ config;
637
+ pool = null;
638
+ constructor(config) {
639
+ this.config = config;
640
+ }
641
+ async connect() {
642
+ const poolConfig = {
643
+ host: this.config.host,
644
+ port: this.config.port,
645
+ database: this.config.database,
646
+ user: this.config.user,
647
+ password: this.config.password,
648
+ ssl: this.config.ssl ? {} : void 0,
649
+ waitForConnections: true,
650
+ connectionLimit: this.config.max ?? 10,
651
+ queueLimit: 0,
652
+ connectTimeout: this.config.connectTimeoutMillis ?? 1e4
653
+ };
654
+ this.pool = createPool(poolConfig);
655
+ const connection = await this.pool.getConnection();
656
+ connection.release();
657
+ }
658
+ async disconnect() {
659
+ if (this.pool) {
660
+ await this.pool.end();
661
+ this.pool = null;
662
+ }
663
+ }
664
+ isConnected() {
665
+ return this.pool !== null;
666
+ }
667
+ async execute(sql) {
668
+ if (!this.pool) {
669
+ throw new Error("Not connected to database");
670
+ }
671
+ const start = performance4.now();
672
+ const [rows, fields] = await this.pool.query(sql);
673
+ const durationMs = performance4.now() - start;
674
+ return {
675
+ columns: this.extractColumns(fields),
676
+ rows: rows.map((row) => this.serializeRow(row)),
677
+ rowCount: rows.length,
678
+ durationMs
679
+ };
680
+ }
681
+ async explain(sql) {
682
+ if (!this.pool) {
683
+ throw new Error("Not connected to database");
684
+ }
685
+ try {
686
+ await this.pool.query(`EXPLAIN ${sql}`);
687
+ return { valid: true };
688
+ } catch (err) {
689
+ return {
690
+ valid: false,
691
+ error: err instanceof Error ? err.message : String(err)
692
+ };
693
+ }
694
+ }
695
+ extractColumns(fields) {
696
+ return fields.map((field) => ({
697
+ name: field.name,
698
+ type: this.mysqlTypeToString(field.type)
699
+ }));
700
+ }
701
+ mysqlTypeToString(type) {
702
+ const typeMap = {
703
+ 0: "number",
704
+ // DECIMAL
705
+ 1: "integer",
706
+ // TINY
707
+ 2: "integer",
708
+ // SHORT
709
+ 3: "integer",
710
+ // LONG
711
+ 4: "number",
712
+ // FLOAT
713
+ 5: "number",
714
+ // DOUBLE
715
+ 7: "date",
716
+ // TIMESTAMP
717
+ 8: "integer",
718
+ // LONGLONG
719
+ 9: "integer",
720
+ // INT24
721
+ 10: "date",
722
+ // DATE
723
+ 11: "string",
724
+ // TIME
725
+ 12: "date",
726
+ // DATETIME
727
+ 13: "integer",
728
+ // YEAR
729
+ 15: "string",
730
+ // VARCHAR
731
+ 245: "unknown",
732
+ // JSON
733
+ 246: "number",
734
+ // NEWDECIMAL
735
+ 252: "string",
736
+ // BLOB
737
+ 253: "string",
738
+ // VAR_STRING
739
+ 254: "string"
740
+ // STRING
741
+ };
742
+ return typeMap[type ?? 0] ?? "unknown";
743
+ }
744
+ serializeRow(row) {
745
+ const serialized = {};
746
+ for (const [key, value] of Object.entries(row)) {
747
+ serialized[key] = this.serializeValue(value);
748
+ }
749
+ return serialized;
750
+ }
751
+ serializeValue(value) {
752
+ if (value === null || value === void 0) {
753
+ return null;
754
+ }
755
+ if (typeof value === "bigint") {
756
+ if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) {
757
+ return value.toString();
758
+ }
759
+ return Number(value);
760
+ }
761
+ if (value instanceof Date) {
762
+ return value.toISOString();
763
+ }
764
+ if (Buffer.isBuffer(value)) {
765
+ return value.toString("utf-8");
766
+ }
767
+ return value;
768
+ }
769
+ };
770
+
771
+ // ../../packages/query/dist/connectors/sqlite.js
772
+ import Database from "better-sqlite3";
773
+ import { performance as performance5 } from "perf_hooks";
774
+ var SQLiteConnector = class {
775
+ config;
776
+ db = null;
777
+ constructor(config) {
778
+ this.config = config;
779
+ }
780
+ async connect() {
781
+ this.db = new Database(this.config.path, {
782
+ readonly: this.config.readonly ?? false
783
+ });
784
+ this.db.pragma("journal_mode = WAL");
785
+ this.db.pragma("foreign_keys = ON");
786
+ }
787
+ async disconnect() {
788
+ if (this.db) {
789
+ this.db.close();
790
+ this.db = null;
791
+ }
792
+ }
793
+ isConnected() {
794
+ return this.db !== null;
795
+ }
796
+ async execute(sql) {
797
+ if (!this.db) {
798
+ throw new Error("Not connected to database");
799
+ }
800
+ const start = performance5.now();
801
+ const stmt = this.db.prepare(sql);
802
+ const rows = stmt.all();
803
+ const durationMs = performance5.now() - start;
804
+ const columns = stmt.columns().map((col) => ({
805
+ name: col.name,
806
+ type: this.sqliteTypeToString(col.type)
807
+ }));
808
+ return {
809
+ columns,
810
+ rows: rows.map((row) => this.serializeRow(row)),
811
+ rowCount: rows.length,
812
+ durationMs
813
+ };
814
+ }
815
+ async explain(sql) {
816
+ if (!this.db) {
817
+ throw new Error("Not connected to database");
818
+ }
819
+ try {
820
+ this.db.prepare(`EXPLAIN ${sql}`);
821
+ return { valid: true };
822
+ } catch (err) {
823
+ return {
824
+ valid: false,
825
+ error: err instanceof Error ? err.message : String(err)
826
+ };
827
+ }
828
+ }
829
+ sqliteTypeToString(type) {
830
+ if (!type)
831
+ return "unknown";
832
+ const upperType = type.toUpperCase();
833
+ if (upperType.includes("INT"))
834
+ return "integer";
835
+ if (upperType.includes("CHAR") || upperType.includes("TEXT") || upperType.includes("CLOB"))
836
+ return "string";
837
+ if (upperType.includes("BLOB"))
838
+ return "unknown";
839
+ if (upperType.includes("REAL") || upperType.includes("FLOA") || upperType.includes("DOUB"))
840
+ return "number";
841
+ if (upperType.includes("NUMERIC") || upperType.includes("DECIMAL"))
842
+ return "number";
843
+ if (upperType.includes("DATE") || upperType.includes("TIME"))
844
+ return "date";
845
+ if (upperType.includes("BOOL"))
846
+ return "boolean";
847
+ return "unknown";
848
+ }
849
+ serializeRow(row) {
850
+ const serialized = {};
851
+ for (const [key, value] of Object.entries(row)) {
852
+ serialized[key] = this.serializeValue(value);
853
+ }
854
+ return serialized;
855
+ }
856
+ serializeValue(value) {
857
+ if (value === null || value === void 0) {
858
+ return null;
859
+ }
860
+ if (typeof value === "bigint") {
861
+ if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) {
862
+ return value.toString();
863
+ }
864
+ return Number(value);
865
+ }
866
+ if (Buffer.isBuffer(value)) {
867
+ return value.toString("base64");
868
+ }
869
+ return value;
870
+ }
871
+ };
872
+
873
+ // ../../packages/query/dist/connectors/snowflake.js
874
+ import snowflake from "snowflake-sdk";
875
+ import { performance as performance6 } from "perf_hooks";
876
+ var SnowflakeConnector = class {
877
+ config;
878
+ connection = null;
879
+ constructor(config) {
880
+ this.config = config;
881
+ }
882
+ async connect() {
883
+ const connectionOptions = {
884
+ account: this.config.account,
885
+ username: this.config.username,
886
+ password: this.config.password,
887
+ privateKey: this.config.privateKey,
888
+ warehouse: this.config.warehouse,
889
+ database: this.config.database,
890
+ schema: this.config.schema,
891
+ role: this.config.role,
892
+ timeout: this.config.connectTimeoutMillis ?? 6e4
893
+ };
894
+ this.connection = snowflake.createConnection(connectionOptions);
895
+ return new Promise((resolve, reject) => {
896
+ this.connection.connect((err) => {
897
+ if (err) {
898
+ this.connection = null;
899
+ reject(err);
900
+ } else {
901
+ resolve();
902
+ }
903
+ });
904
+ });
905
+ }
906
+ async disconnect() {
907
+ if (this.connection) {
908
+ return new Promise((resolve, reject) => {
909
+ this.connection.destroy((err) => {
910
+ this.connection = null;
911
+ if (err) {
912
+ reject(err);
913
+ } else {
914
+ resolve();
915
+ }
916
+ });
917
+ });
918
+ }
919
+ }
920
+ isConnected() {
921
+ return this.connection !== null && this.connection.isUp();
922
+ }
923
+ async execute(sql) {
924
+ if (!this.connection) {
925
+ throw new Error("Not connected to database");
926
+ }
927
+ const start = performance6.now();
928
+ return new Promise((resolve, reject) => {
929
+ this.connection.execute({
930
+ sqlText: sql,
931
+ complete: (err, stmt, rows) => {
932
+ const durationMs = performance6.now() - start;
933
+ if (err) {
934
+ reject(err);
935
+ return;
936
+ }
937
+ const columns = stmt ? stmt.getColumns().map((col) => ({
938
+ name: col.getName(),
939
+ type: this.snowflakeTypeToString(col.getType())
940
+ })) : [];
941
+ resolve({
942
+ columns,
943
+ rows: (rows || []).map((row) => this.serializeRow(row)),
944
+ rowCount: rows?.length ?? 0,
945
+ durationMs
946
+ });
947
+ }
948
+ });
949
+ });
950
+ }
951
+ async explain(sql) {
952
+ if (!this.connection) {
953
+ throw new Error("Not connected to database");
954
+ }
955
+ try {
956
+ await this.execute(`EXPLAIN ${sql}`);
957
+ return { valid: true };
958
+ } catch (err) {
959
+ return {
960
+ valid: false,
961
+ error: err instanceof Error ? err.message : String(err)
962
+ };
963
+ }
964
+ }
965
+ snowflakeTypeToString(type) {
966
+ const typeMap = {
967
+ "NUMBER": "number",
968
+ "DECIMAL": "number",
969
+ "NUMERIC": "number",
970
+ "INT": "integer",
971
+ "INTEGER": "integer",
972
+ "BIGINT": "integer",
973
+ "SMALLINT": "integer",
974
+ "TINYINT": "integer",
975
+ "BYTEINT": "integer",
976
+ "FLOAT": "number",
977
+ "FLOAT4": "number",
978
+ "FLOAT8": "number",
979
+ "DOUBLE": "number",
980
+ "DOUBLE PRECISION": "number",
981
+ "REAL": "number",
982
+ "VARCHAR": "string",
983
+ "CHAR": "string",
984
+ "CHARACTER": "string",
985
+ "STRING": "string",
986
+ "TEXT": "string",
987
+ "BINARY": "unknown",
988
+ "VARBINARY": "unknown",
989
+ "BOOLEAN": "boolean",
990
+ "DATE": "date",
991
+ "DATETIME": "date",
992
+ "TIME": "string",
993
+ "TIMESTAMP": "date",
994
+ "TIMESTAMP_LTZ": "date",
995
+ "TIMESTAMP_NTZ": "date",
996
+ "TIMESTAMP_TZ": "date",
997
+ "VARIANT": "unknown",
998
+ "OBJECT": "unknown",
999
+ "ARRAY": "unknown"
1000
+ };
1001
+ const upperType = type.toUpperCase();
1002
+ return typeMap[upperType] ?? "unknown";
1003
+ }
1004
+ serializeRow(row) {
1005
+ const serialized = {};
1006
+ for (const [key, value] of Object.entries(row)) {
1007
+ serialized[key] = this.serializeValue(value);
1008
+ }
1009
+ return serialized;
1010
+ }
1011
+ serializeValue(value) {
1012
+ if (value === null || value === void 0) {
1013
+ return null;
1014
+ }
1015
+ if (typeof value === "bigint") {
1016
+ if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) {
1017
+ return value.toString();
1018
+ }
1019
+ return Number(value);
1020
+ }
1021
+ if (value instanceof Date) {
1022
+ return value.toISOString();
1023
+ }
1024
+ return value;
1025
+ }
1026
+ };
1027
+
1028
+ // ../../packages/query/dist/connectors/auth.js
1029
+ function resolvePostgresAuth(connection) {
1030
+ const auth = connection.auth;
1031
+ if (!auth) {
1032
+ return {
1033
+ user: process.env.PGUSER ?? "postgres",
1034
+ password: process.env.PGPASSWORD ?? ""
1035
+ };
1036
+ }
1037
+ if (auth.type === "env") {
1038
+ const user = process.env[auth.user_var];
1039
+ const password = process.env[auth.password_var];
1040
+ if (!user) {
1041
+ throw new Error(`Missing environment variable: ${auth.user_var}`);
1042
+ }
1043
+ return { user, password: password ?? "" };
1044
+ }
1045
+ throw new Error(`Auth type "${auth.type}" not yet implemented`);
1046
+ }
1047
+ function resolveMySQLAuth(connection) {
1048
+ const auth = connection.auth;
1049
+ if (!auth) {
1050
+ return {
1051
+ user: process.env.MYSQL_USER ?? "root",
1052
+ password: process.env.MYSQL_PASSWORD ?? ""
1053
+ };
1054
+ }
1055
+ if (auth.type === "env") {
1056
+ const user = process.env[auth.user_var];
1057
+ const password = process.env[auth.password_var];
1058
+ if (!user) {
1059
+ throw new Error(`Missing environment variable: ${auth.user_var}`);
1060
+ }
1061
+ return { user, password: password ?? "" };
1062
+ }
1063
+ throw new Error(`Auth type "${auth.type}" not yet implemented`);
1064
+ }
1065
+ function resolveSnowflakeAuth(connection) {
1066
+ const auth = connection.auth;
1067
+ if (auth.type === "env") {
1068
+ const username = process.env[auth.user_var];
1069
+ const password = process.env[auth.password_var];
1070
+ if (!username) {
1071
+ throw new Error(`Missing environment variable: ${auth.user_var}`);
1072
+ }
1073
+ return { username, password: password ?? void 0 };
1074
+ }
1075
+ if (auth.type === "key_pair") {
1076
+ const username = process.env[auth.user_var];
1077
+ if (!username) {
1078
+ throw new Error(`Missing environment variable: ${auth.user_var}`);
1079
+ }
1080
+ const fs = __require("fs");
1081
+ const privateKey = fs.readFileSync(auth.private_key_path, "utf-8");
1082
+ return { username, privateKey };
1083
+ }
1084
+ throw new Error(`Auth type "${auth.type}" not yet implemented for Snowflake`);
1085
+ }
1086
+
1087
+ // ../../packages/query/dist/test-runner.js
1088
+ var TYPE_MAP = {
1089
+ date: ["date", "timestamp", "datetime", "timestamptz", "timestamp with time zone"],
1090
+ number: ["integer", "int", "bigint", "float", "double", "decimal", "numeric", "real", "smallint", "tinyint", "hugeint", "int4", "int8", "float4", "float8"],
1091
+ integer: ["integer", "int", "bigint", "smallint", "tinyint", "hugeint", "int4", "int8"],
1092
+ string: ["varchar", "text", "char", "string", "character varying", "nvarchar"],
1093
+ boolean: ["boolean", "bool"]
1094
+ };
1095
+ function typesMatch(expectedType, actualType) {
1096
+ const expected = expectedType.toLowerCase();
1097
+ const actual = actualType.toLowerCase();
1098
+ if (expected === actual)
1099
+ return true;
1100
+ const acceptedTypes = TYPE_MAP[expected];
1101
+ if (acceptedTypes)
1102
+ return acceptedTypes.includes(actual);
1103
+ return false;
1104
+ }
1105
+ function expandThis(assertionSql, compiledSql) {
1106
+ return assertionSql.replace(/\{\{\s*this\s*\}\}/g, compiledSql);
1107
+ }
1108
+ var MAX_SAMPLE_VIOLATIONS = 5;
1109
+ async function runAssertion(compiledSql, assertionSql, connector) {
1110
+ const warning = !/\{\{\s*this\s*\}\}/.test(assertionSql) ? "Test assertion does not reference {{this}} \u2014 did you mean to include it?" : void 0;
1111
+ const expandedSql = expandThis(assertionSql, compiledSql);
1112
+ try {
1113
+ const result = await connector.execute(expandedSql);
1114
+ return {
1115
+ sql: assertionSql,
1116
+ passed: result.rowCount === 0,
1117
+ violationCount: result.rowCount,
1118
+ sampleViolations: result.rows.slice(0, MAX_SAMPLE_VIOLATIONS),
1119
+ warning
1120
+ };
1121
+ } catch (err) {
1122
+ return {
1123
+ sql: assertionSql,
1124
+ passed: false,
1125
+ error: err instanceof Error ? err.message : String(err),
1126
+ warning
1127
+ };
1128
+ }
1129
+ }
1130
+ async function runModel(compiledSql, metadata, connector) {
1131
+ const start = performance.now();
1132
+ const hasReturns = metadata.returns && metadata.returns.length > 0;
1133
+ const hasTests = metadata.tests && metadata.tests.length > 0;
1134
+ if (!hasReturns && !hasTests) {
1135
+ return {
1136
+ modelName: metadata.name,
1137
+ assertions: [],
1138
+ durationMs: performance.now() - start
1139
+ };
1140
+ }
1141
+ let schemaCheckResult;
1142
+ if (hasReturns) {
1143
+ try {
1144
+ schemaCheckResult = await checkSchema(compiledSql, connector, metadata.returns);
1145
+ } catch (err) {
1146
+ return {
1147
+ modelName: metadata.name,
1148
+ assertions: [],
1149
+ durationMs: performance.now() - start,
1150
+ error: err instanceof Error ? err.message : String(err)
1151
+ };
1152
+ }
1153
+ }
1154
+ const assertions = [];
1155
+ if (hasTests) {
1156
+ for (const testSql of metadata.tests) {
1157
+ const result = await runAssertion(compiledSql, testSql, connector);
1158
+ assertions.push(result);
1159
+ }
1160
+ }
1161
+ return {
1162
+ modelName: metadata.name,
1163
+ schemaCheck: schemaCheckResult,
1164
+ assertions,
1165
+ durationMs: performance.now() - start
1166
+ };
1167
+ }
1168
+ async function runAll(models, connector) {
1169
+ const start = performance.now();
1170
+ const results = [];
1171
+ let passed = 0;
1172
+ let failed = 0;
1173
+ let skipped = 0;
1174
+ for (const model of models) {
1175
+ const result = await runModel(model.compiledSql, model.metadata, connector);
1176
+ results.push(result);
1177
+ const hasAnyCheck = result.schemaCheck || result.assertions.length > 0;
1178
+ if (!hasAnyCheck && !result.error) {
1179
+ skipped++;
1180
+ continue;
1181
+ }
1182
+ if (result.error) {
1183
+ failed++;
1184
+ continue;
1185
+ }
1186
+ if (result.schemaCheck) {
1187
+ if (result.schemaCheck.passed)
1188
+ passed++;
1189
+ else
1190
+ failed++;
1191
+ }
1192
+ for (const assertion of result.assertions) {
1193
+ if (assertion.passed)
1194
+ passed++;
1195
+ else
1196
+ failed++;
1197
+ }
1198
+ }
1199
+ return {
1200
+ models: results,
1201
+ passed,
1202
+ failed,
1203
+ skipped,
1204
+ durationMs: performance.now() - start
1205
+ };
1206
+ }
1207
+ async function checkSchema(compiledSql, connector, expectedReturns) {
1208
+ const wrappedSql = `SELECT * FROM (${compiledSql}) AS _model LIMIT 0`;
1209
+ const result = await connector.execute(wrappedSql);
1210
+ const actualColumns = result.columns.map((c) => c.name);
1211
+ const expectedColumns = expectedReturns.map((c) => c.name);
1212
+ const missingColumns = expectedColumns.filter((name) => !actualColumns.includes(name));
1213
+ const extraColumns = actualColumns.filter((name) => !expectedColumns.includes(name));
1214
+ const typeMismatches = [];
1215
+ for (const expected of expectedReturns) {
1216
+ const actual = result.columns.find((c) => c.name === expected.name);
1217
+ if (actual && !typesMatch(expected.type, actual.type)) {
1218
+ typeMismatches.push({
1219
+ column: expected.name,
1220
+ expected: expected.type,
1221
+ actual: actual.type
1222
+ });
1223
+ }
1224
+ }
1225
+ return {
1226
+ passed: missingColumns.length === 0 && typeMismatches.length === 0,
1227
+ expectedColumns,
1228
+ actualColumns,
1229
+ missingColumns,
1230
+ extraColumns,
1231
+ typeMismatches
1232
+ };
1233
+ }
1234
+
1235
+ // ../../packages/query/dist/index.js
1236
+ var VERSION = "0.1.0";
1237
+
1238
+ export {
1239
+ parseModelMetadata,
1240
+ isCustomDateRange,
1241
+ expandCustomDateRange,
1242
+ DATE_PRESETS,
1243
+ expandDatePreset,
1244
+ isDatePreset,
1245
+ createTemplateContext,
1246
+ renderTemplate,
1247
+ templateHasVariable,
1248
+ extractTemplateVariables,
1249
+ QueryCompiler,
1250
+ DuckDBConnector,
1251
+ PostgresConnector,
1252
+ MySQLConnector,
1253
+ SQLiteConnector,
1254
+ SnowflakeConnector,
1255
+ resolvePostgresAuth,
1256
+ resolveMySQLAuth,
1257
+ resolveSnowflakeAuth,
1258
+ expandThis,
1259
+ runAssertion,
1260
+ runModel,
1261
+ runAll,
1262
+ checkSchema,
1263
+ VERSION
1264
+ };
1265
+ //# sourceMappingURL=chunk-LL3VKMZM.js.map