@graphenedata/cli 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/cli.js CHANGED
@@ -69,6 +69,7 @@ async function pollFor(fn, timeoutMs, interval) {
69
69
  }
70
70
  var init_util = __esm({
71
71
  "../lang/util.ts"() {
72
+ "use strict";
72
73
  }
73
74
  });
74
75
 
@@ -135,6 +136,7 @@ function extractLeadingMetadata(node) {
135
136
  }
136
137
  var init_metadata = __esm({
137
138
  "../lang/metadata.ts"() {
139
+ "use strict";
138
140
  init_util();
139
141
  }
140
142
  });
@@ -143,8 +145,12 @@ var init_metadata = __esm({
143
145
  import * as fs from "fs";
144
146
  import path from "path";
145
147
  function setConfig(cfg) {
148
+ let dialect = cfg.dialect || "duckdb";
149
+ if (cfg.bigquery) dialect = "bigquery";
150
+ else if (cfg.snowflake) dialect = "snowflake";
151
+ else if (cfg.duckdb) dialect = "duckdb";
146
152
  Object.keys(config).forEach((key) => delete config[key]);
147
- Object.assign(config, cfg);
153
+ Object.assign(config, cfg, { dialect });
148
154
  }
149
155
  function loadConfig(dir) {
150
156
  if (config.root) return;
@@ -156,14 +162,12 @@ function loadConfig(dir) {
156
162
  } catch {
157
163
  console.warn("No package.json found in current directory");
158
164
  }
159
- let dialect = "duckdb";
160
- if (packageJsonObject.bigquery) dialect = "bigquery";
161
- else if (packageJsonObject.snowflake) dialect = "snowflake";
162
- setConfig({ ...packageJsonObject, dialect, root: packageJsonObject.root || process.cwd() });
165
+ setConfig({ ...packageJsonObject, root: packageJsonObject.root || process.cwd() });
163
166
  }
164
167
  var config;
165
168
  var init_config = __esm({
166
169
  "../lang/config.ts"() {
170
+ "use strict";
167
171
  config = { dialect: "duckdb", root: "" };
168
172
  }
169
173
  });
@@ -181,6 +185,7 @@ function findOverloads(name, dialect) {
181
185
  var globalNamespace, dialectNamespaces, BIGQUERY_DIALECT_FUNCTIONS;
182
186
  var init_functions = __esm({
183
187
  "../lang/functions.ts"() {
188
+ "use strict";
184
189
  globalNamespace = new GlobalNameSpace();
185
190
  dialectNamespaces = /* @__PURE__ */ new Map();
186
191
  Object.assign(DUCKDB_DIALECT_FUNCTIONS, {
@@ -310,6 +315,138 @@ var init_functions = __esm({
310
315
  }
311
316
  });
312
317
 
318
+ // ../lang/temporalLiterals.ts
319
+ function parseTemporalLiteral(value, expected) {
320
+ let raw = (value ?? "").trim();
321
+ if (!raw) return null;
322
+ let yearMatch = raw.match(/^([0-9]{4})$/);
323
+ if (yearMatch) {
324
+ let year = Number(yearMatch[1]);
325
+ return buildResult(year, 1, 1, 0, 0, 0, "year", expected);
326
+ }
327
+ let yearMonthMatch = raw.match(/^([0-9]{4})-([0-9]{2})$/);
328
+ if (yearMonthMatch) {
329
+ let year = Number(yearMonthMatch[1]);
330
+ let month = Number(yearMonthMatch[2]);
331
+ if (!inRange(month, 1, 12)) return null;
332
+ return buildResult(year, month, 1, 0, 0, 0, "month", expected);
333
+ }
334
+ let dateMatch = raw.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/);
335
+ if (dateMatch) {
336
+ let year = Number(dateMatch[1]);
337
+ let month = Number(dateMatch[2]);
338
+ let day = Number(dateMatch[3]);
339
+ if (!isValidDate(year, month, day)) return null;
340
+ return buildResult(year, month, day, 0, 0, 0, "day", expected);
341
+ }
342
+ if (expected === "timestamp") {
343
+ let dateTimeMatch = raw.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})[Tt\s]([0-9]{1,2})(?::([0-9]{2})(?::([0-9]{2}))?)?$/);
344
+ if (!dateTimeMatch) return null;
345
+ let year = Number(dateTimeMatch[1]);
346
+ let month = Number(dateTimeMatch[2]);
347
+ let day = Number(dateTimeMatch[3]);
348
+ if (!isValidDate(year, month, day)) return null;
349
+ let hour = Number(dateTimeMatch[4]);
350
+ let minute = dateTimeMatch[5] ? Number(dateTimeMatch[5]) : 0;
351
+ let second = dateTimeMatch[6] ? Number(dateTimeMatch[6]) : 0;
352
+ if (!inRange(hour, 0, 23) || !inRange(minute, 0, 59) || !inRange(second, 0, 59)) return null;
353
+ let timeframe = "hour";
354
+ if (dateTimeMatch[6]) {
355
+ timeframe = "second";
356
+ } else if (dateTimeMatch[5]) {
357
+ timeframe = "minute";
358
+ }
359
+ return buildResult(year, month, day, hour, minute, second, timeframe, expected);
360
+ }
361
+ return null;
362
+ }
363
+ function parseTemporal(node) {
364
+ if (node.node !== "stringLiteral") return null;
365
+ let rawValue = typeof node.literal === "string" ? node.literal.trim() : "";
366
+ if (!rawValue) return null;
367
+ let parsedDate = parseTemporalLiteral(rawValue, "date");
368
+ if (parsedDate) {
369
+ let typeDef = { type: parsedDate.type, timeframe: parsedDate.timeframe };
370
+ Object.assign(node, { node: "timeLiteral", literal: parsedDate.literal, type: parsedDate.type, typeDef });
371
+ return parsedDate.timeframe;
372
+ }
373
+ let parsedTimestamp = parseTemporalLiteral(rawValue, "timestamp");
374
+ if (parsedTimestamp) {
375
+ let typeDef = { type: parsedTimestamp.type, timeframe: parsedTimestamp.timeframe };
376
+ Object.assign(node, { node: "timeLiteral", literal: parsedTimestamp.literal, type: parsedTimestamp.type, typeDef });
377
+ return parsedTimestamp.timeframe;
378
+ }
379
+ let interval = parseIntervalLiteral(rawValue);
380
+ if (interval) {
381
+ Object.assign(node, { node: "numberLiteral", literal: interval.quantity.toString(), type: "interval", intervalUnit: interval.unit });
382
+ return interval.unit;
383
+ }
384
+ return null;
385
+ }
386
+ function parseIntervalLiteral(value) {
387
+ let raw = (value ?? "").trim().toLowerCase().replace(/\s+/g, " ");
388
+ if (!raw) return null;
389
+ let match = raw.match(/^(-?\d+(?:\.\d+)?)\s+([a-z]+)$/);
390
+ if (!match) return null;
391
+ let quantity = Number(match[1]);
392
+ if (!Number.isFinite(quantity)) return null;
393
+ let unit = INTERVAL_UNITS[match[2]];
394
+ if (!unit) return null;
395
+ return { quantity, unit };
396
+ }
397
+ function buildResult(year, month, day, hour, minute, second, timeframe, expected) {
398
+ if (expected === "date") {
399
+ return { literal: `${pad(year)}-${pad(month)}-${pad(day)}`, timeframe, type: "date" };
400
+ }
401
+ return {
402
+ literal: `${pad(year)}-${pad(month)}-${pad(day)} ${pad(hour)}:${pad(minute)}:${pad(second)}`,
403
+ timeframe,
404
+ type: "timestamp"
405
+ };
406
+ }
407
+ function inRange(value, min, max) {
408
+ return Number.isInteger(value) && value >= min && value <= max;
409
+ }
410
+ function isValidDate(year, month, day) {
411
+ if (!inRange(month, 1, 12)) return false;
412
+ if (!inRange(day, 1, 31)) return false;
413
+ let date = new Date(Date.UTC(year, month - 1, day));
414
+ return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
415
+ }
416
+ function pad(value) {
417
+ return value.toString().padStart(2, "0");
418
+ }
419
+ var INTERVAL_UNITS;
420
+ var init_temporalLiterals = __esm({
421
+ "../lang/temporalLiterals.ts"() {
422
+ "use strict";
423
+ INTERVAL_UNITS = {
424
+ second: "second",
425
+ seconds: "second",
426
+ sec: "second",
427
+ secs: "second",
428
+ minute: "minute",
429
+ minutes: "minute",
430
+ min: "minute",
431
+ mins: "minute",
432
+ hour: "hour",
433
+ hours: "hour",
434
+ hr: "hour",
435
+ hrs: "hour",
436
+ day: "day",
437
+ days: "day",
438
+ week: "week",
439
+ weeks: "week",
440
+ month: "month",
441
+ months: "month",
442
+ quarter: "quarter",
443
+ quarters: "quarter",
444
+ year: "year",
445
+ years: "year"
446
+ };
447
+ }
448
+ });
449
+
313
450
  // ../lang/params.ts
314
451
  function inferParamTypes(query) {
315
452
  let parentMap = /* @__PURE__ */ new WeakMap();
@@ -416,8 +553,7 @@ function sanitizeType(value) {
416
553
  return value;
417
554
  }
418
555
  function fillInParams(query, params) {
419
- let q = structuredClone(query);
420
- let filters = q.pipeline[0].filterList || [];
556
+ let filters = query.pipeline[0].filterList || [];
421
557
  for (let filter of filters) {
422
558
  walkExpression(filter.e, (e) => {
423
559
  if (e.node !== "parameter") return;
@@ -426,20 +562,31 @@ function fillInParams(query, params) {
426
562
  else if (value == null) Object.assign(e, { node: "null", type: "string" });
427
563
  else if (e.type == "string") Object.assign(e, { node: "stringLiteral", literal: value });
428
564
  else if (e.type == "number") Object.assign(e, { node: "numberLiteral", literal: value.toString() });
429
- else if (e.type == "boolean") Object.assign(e, { node: value ? "true" : "false" });
565
+ else if (e.type == "date" || e.type == "timestamp") {
566
+ if (typeof value !== "string") throw new Error(`Parameters of type ${e.type} must be provided as strings`);
567
+ let parsed = parseTemporalLiteral(value, e.type);
568
+ if (!parsed) throw new Error(`Could not parse ${e.type} literal for param $${e.path[0]}`);
569
+ Object.assign(e, { node: "timeLiteral", literal: parsed.literal, type: parsed.type, typeDef: { type: parsed.type, timeframe: parsed.timeframe } });
570
+ } else if (e.type == "interval") {
571
+ if (typeof value !== "string") throw new Error("Parameters of type interval must be provided as strings");
572
+ let parsed = parseIntervalLiteral(value);
573
+ if (!parsed) throw new Error(`Could not parse interval literal for param $${e.path[0]}`);
574
+ Object.assign(e, { node: "numberLiteral", literal: parsed.quantity.toString(), type: "interval", intervalUnit: parsed.unit });
575
+ } else if (e.type == "boolean") Object.assign(e, { node: value ? "true" : "false" });
430
576
  else throw new Error(`Unsupported param type ${e.type}`);
431
577
  });
432
578
  }
433
- return q;
434
579
  }
435
580
  var COMPARISON_OPS, BOOLEAN_OPS, NUMERIC_UNARY_OPS, FIELD_TYPES;
436
581
  var init_params = __esm({
437
582
  "../lang/params.ts"() {
583
+ "use strict";
438
584
  init_util();
585
+ init_temporalLiterals();
439
586
  COMPARISON_OPS = /* @__PURE__ */ new Set(["=", "!=", "<>", ">", ">=", "<", "<=", "like", "ilike"]);
440
587
  BOOLEAN_OPS = /* @__PURE__ */ new Set(["and", "or"]);
441
588
  NUMERIC_UNARY_OPS = /* @__PURE__ */ new Set(["unary-"]);
442
- FIELD_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "date", "timestamp", "json", "sql native", "error", "fieldref", "array", "record", "null"]);
589
+ FIELD_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "date", "timestamp", "json", "sql native", "error", "fieldref", "array", "record", "null", "interval"]);
443
590
  }
444
591
  });
445
592
 
@@ -457,34 +604,9 @@ function findTables(fi) {
457
604
  }
458
605
  let table2 = makeTable(name, syntaxNode.getChild("QueryStatement") ? "query_source" : "table");
459
606
  table2.metadata = extractLeadingMetadata(syntaxNode);
460
- for (let cn of syntaxNode.getChildren("ColumnDef")) {
461
- let name2 = txt(cn.getChild("Identifier"));
462
- if (cn.getChild("PrimaryKey")) {
463
- if (table2.primaryKey) diag(cn, `Table ${table2.name} has multiple primary keys`);
464
- table2.primaryKey = name2;
465
- }
466
- let type = convertDataType(txt(cn.getChild("DataType")));
467
- if (!type) diag(cn, `Unsupported data type: ${txt(cn.getChild("DataType"))}`);
468
- let field = { name: name2, type, metadata: extractLeadingMetadata(cn) };
469
- table2.fields.push(field);
470
- FIELD_NODE_MAP.set(field, cn);
471
- }
472
- for (let jn of syntaxNode.getChildren("JoinDef")) {
473
- let nameNode = jn.getChild("Alias") || jn.getChild("Identifier");
474
- let field = { name: txt(nameNode) };
475
- table2.fields.push(field);
476
- FIELD_NODE_MAP.set(field, jn);
477
- }
478
- for (let cn of syntaxNode.getChildren("ComputedDef")) {
479
- let field = { name: txt(cn.getChild("Alias")), metadata: extractLeadingMetadata(cn) };
480
- table2.fields.push(field);
481
- FIELD_NODE_MAP.set(field, cn);
482
- }
483
- table2.fields.reduce((set, f) => {
484
- if (!set[f.name]) set[f.name] = true;
485
- else diag(FIELD_NODE_MAP.get(f), `Table already has a field called "${f.name}"`);
486
- return set;
487
- }, {});
607
+ syntaxNode.getChildren("ColumnDef").forEach((cn) => addColumnField(table2, cn));
608
+ syntaxNode.getChildren("JoinDef").forEach((jn) => addJoinField(table2, jn));
609
+ syntaxNode.getChildren("ComputedDef").forEach((cn) => addComputedField(table2, cn));
488
610
  TABLE_NODE_MAP.set(table2, syntaxNode);
489
611
  fi.tables.push(table2);
490
612
  }
@@ -493,12 +615,56 @@ function makeTable(name, type) {
493
615
  let tablePath = config.namespace ? `${config.namespace}.${name}` : name;
494
616
  return { name, type, fields: [], connection: config.dialect, dialect: config.dialect, tableName: name, tablePath, metadata: {} };
495
617
  }
618
+ function addColumnField(table2, node) {
619
+ let name = txt(node.getChild("Identifier"));
620
+ if (node.getChild("PrimaryKey")) {
621
+ if (table2.primaryKey) diag(node, `Table ${table2.name} has multiple primary keys`);
622
+ table2.primaryKey = name;
623
+ }
624
+ let type = convertDataType(txt(node.getChild("DataType")));
625
+ if (!type) diag(node, `Unsupported data type: ${txt(node.getChild("DataType"))}`);
626
+ let field = { name, type, metadata: extractLeadingMetadata(node) };
627
+ return addFieldToTable(table2, field, node);
628
+ }
629
+ function addJoinField(table2, node) {
630
+ let nameNode = node.getChild("Alias") || node.getChild("Identifier");
631
+ return addFieldToTable(table2, { name: txt(nameNode) }, node);
632
+ }
633
+ function addComputedField(table2, node) {
634
+ let name = txt(node.getChild("Alias"));
635
+ addFieldToTable(table2, { name, metadata: extractLeadingMetadata(node) }, node);
636
+ }
637
+ function addFieldToTable(table2, field, node) {
638
+ if (table2.fields.find((f) => f.name == field.name)) {
639
+ diag(node, `Table already has a field called "${field.name}"`);
640
+ return null;
641
+ }
642
+ table2.fields.push(field);
643
+ FIELD_NODE_MAP.set(field, node);
644
+ return field;
645
+ }
646
+ function applyExtends(fi) {
647
+ fi.tree.topNode.getChildren("ExtendStatement").forEach((node) => {
648
+ let tableName = txt(node.getChild("Identifier"));
649
+ let target = lookupTable(tableName, node);
650
+ if (!target) {
651
+ return diag(node.getChild("Identifier") || node, `Cannot extend unknown table "${tableName}"`);
652
+ }
653
+ node.getChildren("JoinDef").forEach((jn) => addJoinField(target, jn));
654
+ node.getChildren("ComputedDef").forEach((cn) => addComputedField(target, cn));
655
+ });
656
+ }
496
657
  function analyzeTable(table2) {
497
- if (table2.type == "query_source") return analyzeQueryTable(table2);
498
- for (let f of table2.fields) {
499
- if (f.type) continue;
500
- analyzeField(f, table2);
658
+ if (table2.type == "query_source") {
659
+ if (table2.query) return;
660
+ let node = TABLE_NODE_MAP.get(table2);
661
+ let query = analyzeQuery(node.getChild("QueryStatement"));
662
+ if (!query) return;
663
+ let queryFields = query.fields.map((f) => ({ type: f.type, name: f.name, metadata: f.metadata }));
664
+ table2.fields.push(...queryFields);
665
+ table2.query = query;
501
666
  }
667
+ table2.fields.map((f) => analyzeField(f, table2));
502
668
  }
503
669
  function analyzeField(field, table2) {
504
670
  if (field.type) return;
@@ -511,8 +677,9 @@ function analyzeField(field, table2) {
511
677
  field = field;
512
678
  let target = lookupTable(txt(node.getChild("Identifier")), node);
513
679
  if (!target) return diag(node, "Unknown table to join");
514
- if (target.type == "query_source") analyzeQueryTable(target);
515
- let jt = { "join_many": "many", "join_one": "one" }[txt(node.getChild("JoinType"))];
680
+ if (target.type == "query_source") analyzeTable(target);
681
+ let joinTypeStr = txt(node.getChild("JoinType")).replace(/\s+/g, " ");
682
+ let jt = { "join many": "many", "join one": "one" }[joinTypeStr];
516
683
  if (!jt) return diag(node, "Unknown join type");
517
684
  Object.assign(field, target, { name: field.name, join: jt });
518
685
  field.onExpression = analyzeExpression(node.getChild("Expression"), { table: table2, outputFields: [] });
@@ -524,38 +691,29 @@ function analyzeField(field, table2) {
524
691
  }
525
692
  analysisQueue.delete(field);
526
693
  }
527
- function analyzeQueryTable(table2) {
528
- if (table2.query) return;
529
- let node = TABLE_NODE_MAP.get(table2);
530
- let query = analyzeQuery(node.getChild("QueryStatement"));
531
- if (!query) return;
532
- table2.fields = query.fields.map((f) => ({ type: f.type, name: f.name, metadata: f.metadata }));
533
- table2.query = query.malloyQuery;
534
- if (table2.query && typeof table2.query.structRef == "string") {
535
- table2.query.structRef = lookupTable(table2.query.structRef, node);
536
- }
537
- }
538
694
  function analyzeQuery(queryNode) {
539
- let structRef;
695
+ let baseTableName;
540
696
  let scope = { table: null, outputFields: [] };
541
697
  let isAgg = false;
542
- let subQuerySources = [];
543
698
  if (!txt(queryNode)) return;
544
- if (txt(queryNode).trim().toLowerCase() == "select 1") return { fields: [{ name: "col_0", type: "number", metadata: {}, e: { node: "numberLiteral", literal: "1", type: "number" } }], subQuerySources, rawSql: "select 1" };
699
+ if (txt(queryNode).trim().toLowerCase() == "select 1") {
700
+ let fields = [{ name: "col_0", type: "number", metadata: {}, e: { node: "numberLiteral", literal: "1", type: "number" } }];
701
+ return { fields, baseTableName: "", rawSql: "select 1", structRef: {}, pipeline: [] };
702
+ }
545
703
  let froms = queryNode.getChild("FromClause")?.getChildren("TablePrimary") || [];
546
704
  if (froms.find((f) => f.name == "JoinClause")) diag(froms[0], "Query joins not yet supported");
547
705
  if (froms.length == 0) return diag(queryNode, "No tables in FROM clause");
548
706
  if (froms.length > 1) diag(froms[0], "Multiple tables/joins in FROM clause not yet supported");
549
707
  if (froms[0].name == "Subquery") {
550
- structRef = txt(froms[0].getChild("Alias")) || "subquery";
551
- scope.table = makeTable(structRef, "query_source");
708
+ diag(froms[0], "Graphene doesn't yet support subqueries. Try chaining queries instead.");
709
+ baseTableName = txt(froms[0].getChild("Alias")) || "subquery";
710
+ scope.table = makeTable(baseTableName, "query_source");
552
711
  TABLE_NODE_MAP.set(scope.table, froms[0].getChild("SubqueryExpression"));
553
- analyzeQueryTable(scope.table);
554
- subQuerySources.push(scope.table);
712
+ analyzeTable(scope.table);
555
713
  } else {
556
- structRef = txt(froms[0].getChild("Identifier"));
557
- scope.table = lookupTable(structRef, froms[0]);
558
- if (!scope.table) return diag(froms[0], `could not find table "${structRef}"`);
714
+ baseTableName = txt(froms[0].getChild("Identifier"));
715
+ scope.table = lookupTable(baseTableName, froms[0]);
716
+ if (!scope.table) return diag(froms[0], `could not find table "${baseTableName}"`);
559
717
  NODE_ENTITY_MAP.set(froms[0], { entityType: "table", table: scope.table });
560
718
  }
561
719
  let selects = queryNode.getChild("SelectClause")?.getChildren("SelectItem") || [];
@@ -563,11 +721,12 @@ function analyzeQuery(queryNode) {
563
721
  isAgg ||= !!isSelectDistinct;
564
722
  selects.forEach((s) => {
565
723
  if (s.getChild("Wildcard")) {
566
- let path9 = s.getChild("Wildcard").getChildren("Identifier");
567
- let pathStrings = path9.map((p) => txt(p));
568
- let target = followJoins(path9, scope.table);
724
+ let path10 = s.getChild("Wildcard").getChildren("Identifier");
725
+ let pathStrings = path10.map((p) => txt(p));
726
+ let target = followJoins(path10, scope.table);
569
727
  if (!target) return;
570
728
  target.fields.forEach((f) => {
729
+ analyzeField(f, target);
571
730
  if (isJoin(f) || f.isAgg) return;
572
731
  scope.outputFields.push({ ...f, e: { node: "field", path: [...pathStrings, f.name], type: f.type } });
573
732
  });
@@ -621,22 +780,21 @@ function analyzeQuery(queryNode) {
621
780
  }
622
781
  let q = {
623
782
  fields: scope.outputFields,
624
- subQuerySources,
625
- malloyQuery: {
626
- type: "query",
627
- structRef,
628
- pipeline: [{
629
- type: isAgg ? "reduce" : "project",
630
- queryFields: scope.outputFields,
631
- filterList,
632
- outputStruct: null,
633
- isRepeated: false,
634
- orderBy: orderByList.length ? orderByList : void 0,
635
- limit: queryLimit
636
- }]
637
- }
783
+ baseTableName,
784
+ type: "query",
785
+ structRef: null,
786
+ // We fill this in as part of `toSql`
787
+ pipeline: [{
788
+ type: isAgg ? "reduce" : "project",
789
+ queryFields: scope.outputFields,
790
+ filterList,
791
+ outputStruct: null,
792
+ isRepeated: false,
793
+ orderBy: orderByList.length ? orderByList : void 0,
794
+ limit: queryLimit
795
+ }]
638
796
  };
639
- inferParamTypes(q.malloyQuery);
797
+ inferParamTypes(q);
640
798
  return q;
641
799
  }
642
800
  function analyzeExpression(expr, scope) {
@@ -663,11 +821,13 @@ function analyzeExpression(expr, scope) {
663
821
  if (scope.outputFields.includes(field) && field.isAgg) {
664
822
  return { node: "outputField", name: field.name, ...typeInfo, isAgg: field.isAgg };
665
823
  }
666
- let path9 = expr.getChildren("Identifier").map((i) => txt(i));
667
- return { node: "field", path: path9, ...typeInfo, isAgg: field.isAgg };
824
+ let path10 = expr.getChildren("Identifier").map((i) => txt(i));
825
+ return { node: "field", path: path10, ...typeInfo, isAgg: field.isAgg };
668
826
  }
669
827
  case "ExtractExpression": {
670
- let e = analyzeExpression(expr.getChild("Expression"), scope);
828
+ let extractExprNode = expr.getChild("Expression");
829
+ let e = analyzeExpression(extractExprNode, scope);
830
+ checkTypes(e, ["date", "timestamp"], extractExprNode);
671
831
  if (!isTemporalType(e.type) || !e.typeDef) return diag(expr, "Expression must be a date or timestamp", errExpr);
672
832
  let units = txt(expr.getChild("ExtractUnit")).replace(/^['"]|['"]$/g, "").toLowerCase();
673
833
  if (!isExtractUnit(units)) return diag(expr, "Not a valid unit to extract", errExpr);
@@ -690,7 +850,28 @@ function analyzeExpression(expr, scope) {
690
850
  let left2 = analyzeExpression(expr.firstChild, scope);
691
851
  let right2 = analyzeExpression(expr.lastChild, scope);
692
852
  let op = txt(expr.firstChild?.nextSibling).toLowerCase();
693
- return { node: op, kids: { left: left2, right: right2 }, type: left2.type, isAgg: left2.isAgg || right2.isAgg };
853
+ let type = left2.type;
854
+ if (op == "or" || op == "and") type = "boolean";
855
+ if (op == "+" || op == "-") {
856
+ if (["date", "timestamp", "interval"].find((t) => left2.type == t || right2.type == t)) {
857
+ return analyzeTimeExpression(op, left2, right2, expr);
858
+ }
859
+ ensureSameType(left2, expr.firstChild, right2, expr.lastChild);
860
+ }
861
+ if (op == "*" || op == "/" || op == "%") {
862
+ checkTypes(left2, ["number"], expr.firstChild);
863
+ checkTypes(right2, ["number"], expr.lastChild);
864
+ }
865
+ if (op == "<" || op == "<=" || op == ">" || op == ">=" || op == "=" || op == "!=" || op == "<>") {
866
+ ensureSameType(left2, expr.firstChild, right2, expr.lastChild);
867
+ type = "boolean";
868
+ }
869
+ if (op == "like" || op == "ilike") {
870
+ checkTypes(left2, ["string"], expr.firstChild);
871
+ checkTypes(right2, ["string"], expr.lastChild);
872
+ type = "boolean";
873
+ }
874
+ return { node: op, kids: { left: left2, right: right2 }, type, isAgg: left2.isAgg || right2.isAgg };
694
875
  }
695
876
  case "NullTestExpression": {
696
877
  let node = expr.getChildren("Kw").find((n) => txt(n).toLowerCase() == "not") ? "is-not-null" : "is-null";
@@ -724,8 +905,13 @@ function analyzeExpression(expr, scope) {
724
905
  let oneOf = [];
725
906
  let valueList = expr.getChild("InValueList");
726
907
  if (valueList) {
727
- oneOf = valueList.getChildren("Expression").map((v) => analyzeExpression(v, scope));
908
+ oneOf = valueList.getChildren("Expression").map((v) => {
909
+ let e = analyzeExpression(v, scope);
910
+ checkTypes(e, [eNode.type], v);
911
+ return e;
912
+ });
728
913
  } else {
914
+ diag(expr, "IN (<subquery>) is not yet supported");
729
915
  oneOf = [{ node: "genericSQLExpr", kids: { args: [] }, type: "array" }];
730
916
  }
731
917
  let isAgg = eNode.isAgg || oneOf.some((v) => v.isAgg);
@@ -743,11 +929,14 @@ function analyzeFunctionCall(expr, scope) {
743
929
  return o.params.length == argNodes.length || !!o.params.find((p) => p.isVariadic);
744
930
  });
745
931
  let args = argNodes.map((node, idx) => {
746
- let type2 = overload?.params[idx]?.allowedTypes[0];
747
- if (type2?.type === "sql native" && type2?.rawType === "kw") {
932
+ let firstType = overload?.params[idx]?.allowedTypes[0];
933
+ if (firstType?.type === "sql native" && firstType?.rawType === "kw") {
748
934
  return { node: "genericSQLExpr", kids: { args: [] }, type: "sql native", src: [txt(node)], isAgg: false };
749
935
  } else {
750
- return analyzeExpression(node, scope);
936
+ let argExpr = analyzeExpression(node, scope);
937
+ let allowed = overload?.params[idx]?.allowedTypes.map((at) => at.type);
938
+ if (allowed) checkTypes(argExpr, allowed, node);
939
+ return argExpr;
751
940
  }
752
941
  });
753
942
  let type = overload?.returnType.type;
@@ -762,7 +951,12 @@ function analyzeFunctionCall(expr, scope) {
762
951
  }));
763
952
  let ret;
764
953
  if (["count", "min", "max", "avg", "sum"].includes(name.toLowerCase())) {
765
- ret = { node: "aggregate", function: name, e: args[0], type: "number", isAgg: true };
954
+ let type2 = "number", typeDef;
955
+ if (["min", "max", "avg"].includes(name.toLowerCase())) {
956
+ type2 = args[0].type;
957
+ typeDef = args[0].typeDef;
958
+ }
959
+ ret = { node: "aggregate", function: name, e: args[0], type: type2, typeDef, isAgg: true };
766
960
  } else if (overload && type) {
767
961
  ret = {
768
962
  node: "function_call",
@@ -783,8 +977,52 @@ function analyzeFunctionCall(expr, scope) {
783
977
  if (foriegnPaths.length > 0) ret.structPath = foriegnPaths[0].split(".");
784
978
  return ret;
785
979
  }
980
+ function analyzeTimeExpression(op, left2, right2, node) {
981
+ if (left2.type !== "date" && left2.type !== "timestamp") return diag(node, "Expected left side to be a date or timestamp", errExpr);
982
+ let units = left2.type === "timestamp" ? "second" : "day";
983
+ if (right2.node == "stringLiteral") {
984
+ units = parseTemporal(right2);
985
+ if (right2.node == "stringLiteral") {
986
+ return diag(node, "Could not parse interval", errExpr);
987
+ }
988
+ }
989
+ if (right2.type == "date" || right2.type == "timestamp") {
990
+ if (op !== "-") return diag(node, "Only subtraction between dates is supported", errExpr);
991
+ if (right2.type !== left2.type) return diag(node, `Expected right side to be a ${left2.type}`, errExpr);
992
+ return { node: "timeDiff", kids: { left: left2, right: right2 }, units, type: "interval", isAgg: false };
993
+ }
994
+ if (right2.type == "interval") {
995
+ let typeDef = { type: left2.type };
996
+ return { node: "delta", kids: { base: left2, delta: right2 }, op, units, type: left2.type, typeDef, isAgg: false };
997
+ }
998
+ return diag(node, "Expected right side to be a date or interval", errExpr);
999
+ }
1000
+ function ensureSameType(left2, leftNode, right2, rightNode) {
1001
+ if (left2.type === "error" || right2.type === "error") return;
1002
+ if (left2.node === "parameter" || right2.node === "parameter") return;
1003
+ if (isTemporalType(left2.type)) checkTypes(right2, [left2.type], rightNode);
1004
+ if (isTemporalType(right2.type)) checkTypes(left2, [right2.type], leftNode);
1005
+ if (left2.type !== right2.type) diag(rightNode, `Expected ${left2.type}, got ${right2.type}`);
1006
+ }
1007
+ function checkTypes(expr, expected, node) {
1008
+ if (expr.type === "error") return;
1009
+ if (expr.node === "parameter") return;
1010
+ if (expected.includes(expr.type)) return;
1011
+ if (expected.includes("generic")) return;
1012
+ let dt = expected.find((t) => t == "date") || expected.find((t) => t == "timestamp");
1013
+ if (expr.node == "stringLiteral" && dt) {
1014
+ let parsed = parseTemporalLiteral(expr.literal, dt);
1015
+ if (!parsed) return diag(node, `Could not parse ${dt} literal: "${expr.literal}"`, void 0);
1016
+ let typeDef = { type: parsed.type, timeframe: parsed.timeframe };
1017
+ Object.assign(expr, { node: "timeLiteral", literal: parsed?.literal, type: parsed?.type, typeDef });
1018
+ } else if (expr.node == "stringLiteral" && expected.includes("interval")) {
1019
+ let parsed = parseIntervalLiteral(expr.literal);
1020
+ if (!parsed) return diag(node, `Could not parse interval literal: "${expr.literal}"`, void 0);
1021
+ return Object.assign(expr, { node: "numberLiteral", literal: parsed.quantity.toString(), type: "interval", intervalUnit: parsed.unit });
1022
+ } else diag(node, `Expected types: ${expected.join(", ")}`);
1023
+ }
786
1024
  function isSupportedType(value) {
787
- let supported = ["string", "number", "boolean", "date", "timestamp", "json", "sql native", "error", "array", "record", "null", "generic"];
1025
+ let supported = ["string", "number", "boolean", "date", "timestamp", "json", "sql native", "error", "array", "record", "null", "generic", "interval"];
788
1026
  return supported.includes(value);
789
1027
  }
790
1028
  function lookupField(expr, scope) {
@@ -921,11 +1159,13 @@ function convertDataType(dataType) {
921
1159
  var FILE_MAP, diagnostics, TABLE_NODE_MAP, FIELD_NODE_MAP, NODE_ENTITY_MAP, analysisQueue, errExpr;
922
1160
  var init_analyze = __esm({
923
1161
  "../lang/analyze.ts"() {
1162
+ "use strict";
924
1163
  init_util();
925
1164
  init_metadata();
926
1165
  init_config();
927
1166
  init_functions();
928
1167
  init_params();
1168
+ init_temporalLiterals();
929
1169
  FILE_MAP = {};
930
1170
  diagnostics = [];
931
1171
  TABLE_NODE_MAP = /* @__PURE__ */ new WeakMap();
@@ -937,39 +1177,40 @@ var init_analyze = __esm({
937
1177
  });
938
1178
 
939
1179
  // ../lang/parser.terms.js
940
- var table, primary_key, as, on, not, exists, from, inner, left, right, full, cross, join, select, where, group, by, order, asc, desc, limit, offset, _in, is, _null, _true, _false, or, and, like;
1180
+ var table, primary_key, join, as, on, or, and, like, not, _in, from, inner, left, right, full, cross, select, where, group, by, order, asc, desc, limit, offset, is, _null, exists, _true, _false;
941
1181
  var init_parser_terms = __esm({
942
1182
  "../lang/parser.terms.js"() {
1183
+ "use strict";
943
1184
  table = 6;
944
1185
  primary_key = 11;
945
- as = 19;
946
- on = 22;
947
- not = 31;
948
- exists = 49;
949
- from = 53;
950
- inner = 59;
951
- left = 61;
952
- right = 63;
953
- full = 65;
954
- cross = 67;
955
- join = 69;
956
- select = 72;
957
- where = 80;
958
- group = 83;
959
- by = 85;
960
- order = 91;
961
- asc = 95;
962
- desc = 97;
963
- limit = 100;
964
- offset = 102;
965
- _in = 105;
1186
+ join = 15;
1187
+ as = 21;
1188
+ on = 24;
1189
+ or = 31;
1190
+ and = 34;
1191
+ like = 43;
1192
+ not = 45;
1193
+ _in = 55;
1194
+ from = 60;
1195
+ inner = 66;
1196
+ left = 68;
1197
+ right = 70;
1198
+ full = 72;
1199
+ cross = 74;
1200
+ select = 77;
1201
+ where = 84;
1202
+ group = 87;
1203
+ by = 89;
1204
+ order = 95;
1205
+ asc = 99;
1206
+ desc = 101;
1207
+ limit = 104;
1208
+ offset = 106;
966
1209
  is = 109;
967
1210
  _null = 111;
968
- _true = 115;
969
- _false = 117;
970
- or = 130;
971
- and = 132;
972
- like = 141;
1211
+ exists = 129;
1212
+ _true = 133;
1213
+ _false = 135;
973
1214
  }
974
1215
  });
975
1216
 
@@ -981,6 +1222,7 @@ function specializeIdentifier(value) {
981
1222
  var keywords;
982
1223
  var init_tokens = __esm({
983
1224
  "../lang/tokens.js"() {
1225
+ "use strict";
984
1226
  init_parser_terms();
985
1227
  keywords = {
986
1228
  select,
@@ -1022,25 +1264,26 @@ import { LRParser } from "@lezer/lr";
1022
1264
  var spec_Identifier, parser;
1023
1265
  var init_parser = __esm({
1024
1266
  "../lang/parser.js"() {
1267
+ "use strict";
1025
1268
  init_tokens();
1026
- spec_Identifier = { __proto__: null, table: 12, primary_key: 22, join_one: 30, join_many: 34, as: 38, on: 44, not: 62, case: 72, when: 78, then: 82, else: 88, end: 92, exists: 98, from: 106, inner: 118, left: 122, right: 126, full: 130, cross: 134, join: 138, select: 144, distinct: 148, where: 160, group: 166, by: 170, having: 176, order: 182, asc: 190, desc: 194, limit: 200, offset: 204, in: 210, is: 218, null: 222, true: 230, false: 234, count: 244, extract: 250, or: 260, and: 264, like: 282 };
1269
+ spec_Identifier = { __proto__: null, table: 12, primary_key: 22, join: 30, one: 34, many: 38, as: 42, on: 48, or: 62, and: 68, like: 86, not: 90, in: 110, from: 120, inner: 132, left: 136, right: 140, full: 144, cross: 148, select: 154, distinct: 158, where: 168, group: 174, by: 178, having: 184, order: 190, asc: 198, desc: 202, limit: 208, offset: 212, is: 218, null: 222, case: 232, when: 238, then: 242, else: 248, end: 252, exists: 258, true: 266, false: 270, count: 280, extract: 286, extend: 306 };
1027
1270
  parser = LRParser.deserialize({
1028
1271
  version: 14,
1029
- states: "BbQYQPOOOOQO'#C`'#C`OOQO'#Db'#DbOtQPO'#DaOOQO'#Du'#DuO#QQPO'#DtOOQO'#D}'#D}O#XQPO'#D|OOQO'#EQ'#EQO$YQPO'#EPOOQO'#EV'#EVO#XQPO'#EUOOQO'#EY'#EYO$YQPO'#EXOOQO'#Ec'#EcO$_QPO'#EbOOQO'#Fi'#FiO$dQPO'#D`O$qQPO'#C_O$vQPO'#FeQYQPOOQ%hQPO'#FeO%mQPO'#DdO]QPO'#DfO&zQPO'#DeOOQO'#Fv'#FvO(mQPO,59{O*tQPO'#CtO,^QPO'#CtOOQO'#Cz'#CzOOQO'#Cy'#CyO#XQPO'#CxOOQO'#DP'#DPO,fQPO'#DOOOQO'#D^'#D^O,mQPO'#DfOOQO'#Dw'#DwOOQO'#Dz'#DzO.bQPO'#DyOOQO'#Dy'#DyO/_QPO,5:`O|QPO,5:`O0SQPO'#D]OOQO'#En'#EnOOQO'#Er'#ErOOQO'#Et'#EtOOQO'#Eq'#EqOOQO'#Ev'#EvOOQO'#Fy'#FyOOQO'#Ey'#EyO0XQPO'#ExOOQO'#E|'#E|O0^QPO'#E{OOQO'#Fu'#FuO0cQPO'#CtO0hQPO,5:hOOQO'#ES'#ESO|QPO,5:kO1[QPO,5:pO2OQPO,5:sO2WQPO,5:|OOQO-E9g-E9gO2{QPO,58yOOQO,5<P,5<POOQO-E9c-E9cOOQO'#Cn'#CnOOQO'#Cp'#CpOOQO,5:O,5:OO3TQPO,5:OO3YQPO,5:QOOQO,5:P,5:PO3TQPO,5:POOQO'#Dh'#DhOOQO'#Dj'#DjOOQO'#Dl'#DlOOQO'#Dn'#DnOOQO'#Dp'#DpOOQO'#Dr'#DrOtQPO'#DgO(hQPO'#DgOOQO'#Fj'#FjO3_QPO1G/gOOQO,5<R,5<RO4RQPO,5;kO4YQPO,59`OOQO-E9e-E9eOOQO,5:f,5:fOOQO,59d,59dOOQO'#DS'#DSO#XQPO'#DROOQO'#Fh'#FhO7wQPO,59jO8SQPO,59jO8ZQPO,5;lOOQO,5:e,5:eO3TQPO,5:eOOQO'#Eh'#EhO8bQPO,5;RO8gQPO,5;ROOQO'#El'#ElO8oQPO,5;VOOQO'#FR'#FROOQO'#FT'#FTOOQO'#F^'#F^OOQO'#FV'#FVOOQO'#F`'#F`OOQO'#Fa'#FaO#XQPO,59cO#XQPO,59cO#XQPO,59cO#XQPO,59cO#XQPO,59cO8wQPO1G/zO9kQPO1G/zO9kQPO1G/zO]QPO,59wO:`QPO,5;dO:mQPO,5;gO:uQPO1G0VO;jQPO'#E[O<eQPO1G0_OOQO'#Ee'#EeO=YQPO1G0hO=_QPO1G.eO>fQPO1G1jOOQO1G/j1G/jOOQO1G/l1G/lOOQO1G/k1G/kO>kQPO,5:ROtQPO,5:ROOQO-E9h-E9hO?rQPO1G1VOOQO1G1V1G1VP?|QPO'#FgO@RQPO,59mOOQO-E9f-E9fOOQO'#DX'#DXO#XQPO'#DWOOQO'#DZ'#DZOOQO1G/U1G/UO7}QPO1G/UO7wQPO1G/UOOQO1G1W1G1WOOQO1G0P1G0PO,mQPO1G0mO@YQPO1G0mOOQO,5;q,5;qOOQO1G0q1G0qO8rQPO1G0qOB}QPO1G.}OCUQPO1G.}OExQPO1G.}OFSQPO1G.}OOQO1G.}1G.}OOQO,5<V,5<VOF^QPO7+%fOOQO-E9i-E9iOGQQPO7+%fOGuQPO1G/cOGzQPO1G1OOOQO1G1O1G1OOHRQPO1G1OO#XQPO1G1OOOQO'#FO'#FOOHWQPO1G1ROH]QPO7+%qOIPQPO7+%qOOQO'#E^'#E^OOQO'#E`'#E`OOQO,5:v,5:vOJfQPO7+%yOJpQPO7+%yOOQO7+&S7+&SOJwQPO'#CtOOQO'#Cj'#CjOOQO'#Cl'#ClOOQO'#Ci'#CiOKOQPO'#ChO-WQPOOOL_QPO'#FuOLfQPO'#FuOOQO'#Ft'#FtOLmQPO'#CvOOQO'#Fr'#FrOLrQPO'#FfONPQPO7+$PO]QPO7+'UOOQO'#Cq'#CqO#XQPO1G/mONWQPO1G/mO! _QPO7+&qO! fQPO7+&qOOQO7+&q7+&qOOQO'#DU'#DUO#XQPO1G/XO! nQPO,59rOOQO7+$p7+$pO7}QPO7+$pO! uQPO'#EjO!!PQPO7+&XO,mQPO7+&XOOQO7+&]7+&]P|QPO'#FkO!!UQPO<<IQOOQO7+$}7+$}OOQO7+&j7+&jO!!xQPO7+&jO#XQPO7+&mO!#PQPO<<I]OOQO,5<W,5<WO!#sQPO<<IeOOQO-E9j-E9jOOQO'#Cd'#CdO!$kQPO,58}O!%{QPO,59SO3TQPO,59bOOQO,5<Q,5<QOOQO-E9d-E9dOOQO<<Gk<<GkO!&TQPO<<JpO!&YQPO7+%XO#XQPO7+%XO!'`QPO,5<XOOQO<<J]<<J]O!'jQPO<<J]OOQO-E9k-E9kO!'qQPO7+$sOOQO<<H[<<H[O!(OQPO,5;UO!(VQPO,5;UOOQO<<Is<<IsO!(_QPO<<IsOOQO<<JU<<JUO!(dQPO<<JXP2OQPO'#FlOOQO'#Cf'#CfOOQO'#Ce'#CeOOQO1G.i1G.iO!(kQPO1G.nO3TQPO1G.nOOQO1G.|1G.|OOQOAN@[AN@[O!(pQPO<<HsOOQOAN?wAN?wP#XQPO'#FmO!)vQPO1G0pOOQOAN?_AN?_OOQOAN?sAN?sO!)}QPO'#CtO!+bQPO'#CsOOQO7+$Y7+$YO!&OQPO7+$YO!(kQPO,59_O!(kQPO<<GtOOQO1G.y1G.yOOQOAN=`AN=`",
1030
- stateData: "!+l~O$dOSPOS~OUPO!VQO!jSO!rUO!uWO!zYO!}[O#W^O~OTfO$egO~OTkOomOpnOqnOtpO!RrO!ouO#P!QO#c{O#d!QO#g|O#i}O#k!VO#n!RO#q!TO$esO~O!ltO~P|OTkOomOpnOqnOtpO!RrO#P!QO#c{O#d!QO#g|O#i}O#k!VO#n!RO#q!TO$esO~O!w!YO~O#P!^O~O$b!SX$o!SX$k!SX~P]OT!`O~O$o!aOU$XX!V$XX!j$XX!r$XX!u$XX!z$XX!}$XX#W$XX$b$XX~O$o!aO~OT!dOc!cO!V!WX!]!WX!_!WX!a!WX!c!WX!e!WX!g!WX!j!WX!r!WX!u!WX!z!WX!}!WX#W!WX$b!WX$o!WX$k!WXf!WX~OT!dOc!cO!V!XX!]!XX!_!XX!a!XX!c!XX!e!XX!g!XX!j!XX!r!XX!u!XX!z!XX!}!XX#W!XX$b!XX$o!XX$k!XXf!XX~O!]!jO!_!kO!a!lO!c!mO!e!nO!g!oO~O!V!Ta!j!Ta!r!Ta!u!Ta!z!Ta!}!Ta#W!Ta$b!Ta$o!Ta$k!Ta~P(XO$e!uO$g!tOchXihXohXphXqhX!ohX#]hX#ahX#vhX#xhX#zhX#{hX#|hX#}hX$OhX$PhX$RhX$UhX$VhX~OThX!VhX!jhX!rhX!uhX!zhX!}hX#WhX$bhX$lhX$ohXwhX$khXyhX!OhX!]hX!_hX!ahX!chX!ehX!ghX|hX~P)aOT!vO!o!xO~Ow!zO~P#XO!VQO!jSO!rUO!uWO!zYO!}[O#W^O~P#XOi#[OomOp#]Oq#]O!o#^O#]#SO#a#VO#v#XO#x#YO#z#[O#{#[O#|#[O#}#[O$O#[O$P#[O$R#ZO$U#^O$V#^O~OT!dOc!cO!V!mX!j!mX!r!mX!u!mX!z!mX!}!mX#W!mX$b!mX$l!mX$o!mX$k!mX~P-WO$l#dO!V!ha!j!ha!r!ha!u!ha!z!ha!}!ha#W!ha$b!ha$o!ha$k!ha~O$e#gO~O$e#hO~O$e#iO~OT!vO~O!V!pa!j!pa!r!pa!u!pa!z!pa!}!pa#W!pa$b!pa$o!pa$k!pa~P-WO!V!xa!j!xa!r!xa!u!xa!z!xa!}!xa#W!xa$b!xa$o!xa$k!xa~P-WOT#kO#P#kO~O#Y#mO!V#Ua!j#Ua!r#Ua!u#Ua!z#Ua!}#Ua#W#Ua$b#Ua$o#Ua$k#Ua~Oc!cO$e#oO~OT!dO~O$k#rO~O!V!Ti!j!Ti!r!Ti!u!Ti!z!Ti!}!Ti#W!Ti$b!Ti$o!Ti$k!Ti~P(XO$k#xO~P#XO$g!tOThachaihaohaphaqha!Vha!jha!oha!rha!uha!zha!}ha#Wha#]ha#aha#vha#xha#zha#{ha#|ha#}ha$Oha$Pha$Rha$Uha$Vha$bha$lha$ohawha$khayha!Oha!]ha!_ha!aha!cha!eha!gha|ha_haahatha!Rha#Pha#cha#dha#gha#iha#kha#nha#qha$eha~Ow!zO|#|O!O$OO~Ow!zO~P-WO$k$SO~P-WO$e$UO~O#]#SO$R#ZO~OomO#c{O~O!V!hi!j!hi!r!hi!u!hi!z!hi!}!hi#W!hi$b!hi$o!hi$k!hi~P|O$l$aO!V!hi!j!hi!r!hi!u!hi!z!hi!}!hi#W!hi$b!hi$o!hi$k!hi~O!ltO!o$gO$k$fO~P#XOT$iO#d$iO~O$l$kO!V!si!j!si!r!si!u!si!z!si!}!si#W!si$b!si$o!si$k!si~O#R$mO#T$nO!V#OX!j#OX!r#OX!u#OX!z#OX!}#OX#W#OX$b#OX$l#OX$o#OX$k#OX~O$l$pO!V!{i!j!{i!r!{i!u!{i!z!{i!}!{i#W!{i$b!{i$o!{i$k!{i~O#P$rO~OT$sO_$tOa$uOomOpnOqnOtpO!RrO#P!QO#c{O#d!QO#g|O#i}O#k!VO#n!RO#q!TO$esO~O$e%QO~Of%RO!V!Za!]!Za!_!Za!a!Za!c!Za!e!Za!g!Za!j!Za!r!Za!u!Za!z!Za!}!Za#W!Za$b!Za$o!Za$k!Za~O$k%WO$l%UO~P-WO$g!tO~Oy%XO~P-WO$e%`O~Oi#[OomOp#]Oq#]O!o#^O#]#SO#z#[O#{#[O#|#[O#}#[O$O#[O$P#[O$R#ZO$U#^O$V#^OTkicki!Vki!jki!rki!uki!zki!}ki#Wki#aki#vki$bki$lki$okiwki$kkiyki!Oki!]ki!_ki!aki!cki!eki!gki|ki~O#x#YO~P@_O#xki~P@_O!o#^O$U#^O$V#^OTkickiikioki!Vki!jki!rki!uki!zki!}ki#Wki#]ki#aki#vki#xki#zki#{ki#|ki#}ki$Oki$Pki$Rki$bki$lki$okiwki$kkiyki!Oki!]ki!_ki!aki!cki!eki!gki|ki~Op#]Oq#]O~PC]Opkiqki~PC]O!V!hq!j!hq!r!hq!u!hq!z!hq!}!hq#W!hq$b!hq$o!hq$k!hq~P|O$l%cO!V!hq!j!hq!r!hq!u!hq!z!hq!}!hq#W!hq$b!hq$o!hq$k!hq~O$k%dO~O$k%eO~P-WO$k%eO~O!VQO~O!V!sq!j!sq!r!sq!u!sq!z!sq!}!sq#W!sq$b!sq$o!sq$k!sq~P|O$l%hO!V!sq!j!sq!r!sq!u!sq!z!sq!}!sq#W!sq$b!sq$o!sq$k!sq~O!V!{q!j!{q!r!{q!u!{q!z!{q!}!{q#W!{q$b!{q$o!{q$k!{q~OT#kO#P#kO~PItO$l%jO~PItOT%lO~P)aOT%nO~Oi$iXo$iXp$iXq$iX!o$iX#]$iX#a$iX#v$iX#x$iX#z$iX#{$iX#|$iX#}$iX$O$iX$P$iX$R$iX$U$iX$V$iX~Oc$hX~PKTOc$nX~PKTOc!cO~O$l%pOT$YX_$YXa$YXo$YXp$YXq$YXt$YX!R$YX#P$YX#c$YX#d$YX#g$YX#i$YX#k$YX#n$YX#q$YX$e$YX$k$YX~O$k%rO~P=_Of%RO!V!Zi!]!Zi!_!Zi!a!Zi!c!Zi!e!Zi!g!Zi!j!Zi!r!Zi!u!Zi!z!Zi!}!Zi#W!Zi$b!Zi$o!Zi$k!Zi~O$k%wO~P#XO$k%wO$l%xO~O!Oza~P-WO$l%|O$k#^X~P-WO$k&OO~O!V!hy!j!hy!r!hy!u!hy!z!hy!}!hy#W!hy$b!hy$o!hy$k!hy~P|O$k&QO~P-WO!V!sy!j!sy!r!sy!u!sy!z!sy!}!sy#W!sy$b!sy$o!sy$k!sy~P|OT#kO#P#kO!V!{y!j!{y!r!{y!u!{y!z!{y!}!{y#W!{y$b!{y$o!{y$k!{y~OZ&TOTVa_VaaVaoVapVaqVatVa!RVa#PVa#cVa#dVa#gVa#iVa#kVa#nVa#qVa$eVa$kVa$lVa~Oc!cOf%RO~O$k&ZO~O!V!Zq!]!Zq!_!Zq!a!Zq!c!Zq!e!Zq!g!Zq!j!Zq!r!Zq!u!Zq!z!Zq!}!Zq#W!Zq$b!Zq$o!Zq$k!Zq~P-WO$k$aa$l$aa~P-WO$k&]O~P#XOwuq|uq!Ouq~P-WO$k#^a~P#XO$l&_O$k#^a~O$k&`O~O$k&aO~P-WOT&bO~O!V!Zy!]!Zy!_!Zy!a!Zy!c!Zy!e!Zy!g!Zy!j!Zy!r!Zy!u!Zy!z!Zy!}!Zy#W!Zy$b!Zy$o!Zy$k!Zy~P-WO$k#^i~P#XO$g!tOihXThX_hXahXohXphXqhXthX!RhX#PhX#chX#dhX#ghX#ihX#khX#nhX#qhX$ehX$khX$lhX~Oi&fO~OP$U!op~",
1031
- goto: "@[$oPPP$p$tPP$x$|%P%SP$x%V%ZP%ZP%_P%q&ZP&g&mP$x'o'o(j)cPPP'o*tP+m+sP+yP+|,SP,WP'o,bP-Z-o-zP.Y.Y._/]/aP/aP/aP/aP/aP/eP-o/lP/wP/}0`P-o0kP-o0vP1RP-o1XP-o1dP1oP1wP1wP-o1zP2VP'o2YP2r.d2xP3]PP4[5TP5TP4[P5|6wP.d7pP8i5|.d8lP9PP9ePPPPPP9zP:d:zPP$p;c;i;o<q<{=Y=`=n=tPPPP>OP>S>W?}PP5|@WTcOdTbOdT$}#o%PR%m$sR&V%mR&U%mT$w#o%PT$v#o%PQ!ffQ!ihQ#RvQ#p!`Q%o$|R&X%nQ!efQ!hhQ#QvQ#q!fQ#s!iQ$T#RQ&Y%oR&e&XQ%S#tQ%u%TQ&W%nR&g&eQ&d&WR&i&g!h!VTVZoqsy!Z!u!{#_#`#a#b#c#d#h#}$U$a$h$k%S%U%Y%`%b%c%g%h%u%x%|&^&_S$z#o%PS&c&W&gR&h&f!h!VTVZoqsy!Z!u!{#_#`#a#b#c#d#h#}$U$a$h$k%S%U%Y%`%b%c%g%h%u%x%|&^&_T$y#o%P!moTVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_!lnTVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_v#Uv!X![#O#P#w#z$Z$[$e$x%Z%^%f%t%v%z&R&[R$Y#W!mqTVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_X!|q!}#O$RX!{q!}#O$RR%Y#zQ$Q!}R%]$RT#}!}$RQ$P!}S%[$Q$RR%{%]!mzTVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_SeOdS!ggsQ$d#gQ%_$UQ%s%QR&P%`c`Oadgs#g$U%Q%`bROadgs#g$U%Q%`R%g$jViR!p#uUhR!p#u!m!VTVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_T!rj!sT!qj!sS!pj!sR#u!qcTOadgs#g$U%Q%`QyTR$h#hQxTQ#fyQ#j!Z]$`#d$a$k%b%c%hcwTy!Z#d$a$k%b%c%hcVOadgs#g$U%Q%`cXOadgs#g$U%Q%`Q!ZXR!]]cZOadgs#g$U%Q%`c]Oadgs#g$U%Q%`Q#l!]V%i$p%j&SR$o#kc_Oadgs#g$U%Q%`R#n!^v#Tv!X![#O#P#w#z$Z$[$e$x%Z%^%f%t%v%z&R&[R$V#UQ%_$UR&P%`s#Wv!X![#O#P#w#z$e$x%Z%^%f%t%v%z&R&[!l!PTVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_Q$X#WR%a$Y!m!QTVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_!m!OTVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_!h!VTVZoqsy!Z!u!{#_#`#a#b#c#d#h#}$U$a$h$k%S%U%Y%`%b%c%g%h%u%x%|&^&_T$z#o%P!m!STVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_!m!UTVZoqsy!Z!u!{#_#`#a#b#c#d#h#o#}$U$a$h$k%P%S%U%Y%`%b%c%g%h%u%x%|&^&_R$j#is#_v!X![#O#P#w#z$e$x%Z%^%f%t%v%z&R&[u#`v!X![#O#P#w#z$Z$e$x%Z%^%f%t%v%z&R&[w#av!X![#O#P#w#z$Z$[$e$x%Z%^%f%t%v%z&R&[v#[v!X![#O#P#w#z$Z$[$e$x%Z%^%f%t%v%z&R&[R$W#Uy#bv!X![#O#P#w#z$Z$[$]$e$x%Z%^%f%t%v%z&R&[{#cv!X![#O#P#w#z$Z$[$]$^$e$x%Z%^%f%t%v%z&R&[QdOR!bdQ%P#oR%q%PblTy!Z#d$a$k%b%c%h!`!WVZoqs!u!{#_#`#a#b#c#h#o#}$U$h%P%S%U%Y%`%g%u%x%|&W&^&_&f&gT!wl!WQ!}qS#{!}$RR$R#O`aOdgs#g$U%Q%`R!_aQ!sjR#v!sQ#exU$b#e$c$lQ$c#fR$l#jQ$q#lR%k$qQ%V#wS%y%V%}R%}%^T%O#o%PT$|#o%PbvTy!Z#d$a$k%b%c%hQ!XVQ![ZQ!yoQ#OqQ#PsQ#w!uQ#z!{Q$Z#_Q$[#`Q$]#aQ$^#bQ$_#cQ$e#hS$x#o%PQ%Z#}S%^$U%`Q%f$hQ%t%SY%v%U%x%|&^&_Q%z%YQ&R%gR&[%uQjRQ#t!pR%T#uT${#o%P",
1032
- nodeNames: "\u26A0 Comment Program TableStatement Kw Identifier table ColumnDef DataType PrimaryKey Kw primary_key JoinDef JoinType Kw join_one Kw join_many Kw as Alias Kw on BinaryExpression Ref = ComputedDef BinaryExpression UnaryExpression UnaryOperator Kw not - + CaseExpression Kw case WhenClause Kw when Kw then ElseClause Kw else Kw end ExistsExpression Kw exists QueryStatement FromClause Kw from TableName Subquery SubqueryExpression JoinClause Kw inner Kw left Kw right Kw full Kw cross Kw join SelectClause Kw select Kw distinct SelectItem Wildcard * WhereClause Kw where GroupByClause Kw group Kw by HavingClause Kw having OrderByClause Kw order OrderItem Number Kw asc Kw desc LimitClause Kw limit Kw offset InExpression Kw in InValueList NullTestExpression Kw is Kw null String Boolean Kw true Kw false Null Param Count Kw count ExtractExpression Kw extract ExtractUnit FunctionCall Parenthetical Kw or Kw and ComparisonOp != <> < > <= >= Kw like AddOp MulOp / % ViewStatement",
1033
- maxTerm: 169,
1272
+ states: "G`QYQPOOOOQO'#C`'#C`OOQO'#Di'#DiOwQPO'#DhOOQO'#Dz'#DzO#TQPO'#DyOOQO'#ER'#EROOQO'#EU'#EUO#[QPO'#ETOOQO'#EZ'#EZOOQO'#E^'#E^O#[QPO'#E]OOQO'#Eg'#EgO#aQPO'#EfOOQO'#Fp'#FpO#}QPO'#DgO$[QPO'#C_OOQO'#Fj'#FjO$aQPO'#FiO$fQPO'#FlQYQPOOQ%ZQPO'#FlO%`QPO'#EQO%`QPO'#EYO&aQPO'#DkO#fQPO'#DmO'nQPO'#DlOOQO'#GP'#GPO)aQPO,5:SO+hQPO'#CvO-pQPO'#CvOOQO'#DY'#DYO-xQPO'#DmOOQO'#D|'#D|OOQO'#EP'#EPOOQO'#EO'#EOO.cQPO,5:eO!PQPO,5:eO0eQPO'#EOOOQO'#En'#EnOOQO'#Eq'#EqOOQO'#Es'#EsO1_QPO'#ErOOQO'#FQ'#FQO1fQPO'#FPOOQO'#FU'#FUOOQO'#FW'#FWOOQO'#FT'#FTOOQO'#FY'#FYOOQO'#GT'#GTOOQO'#F]'#F]O1kQPO'#F[OOQO'#GS'#GSOOQO'#F`'#F`OOQO'#GR'#GROOQO'#GU'#GUOOQO'#F}'#F}O%`QPO'#EpO1pQPO'#F_OOQO'#EW'#EWO!PQPO,5:oO1uQPO,5:wO1}QPO,5;QOOQO-E9n-E9nO2rQPO,58yO2zQPO,5<TOOQO,5<W,5<WOOQO-E9j-E9jO3PQPO'#CvO3UQPO,5:lO3xQPO,5:tOOQO'#Cp'#CpOOQO'#Cr'#CrOOQO,5:V,5:VO4lQPO,5:VO4qQPO,5:XOOQO,5:W,5:WO4lQPO,5:WOOQO'#Cj'#CjOOQO'#Do'#DoOOQO'#Dq'#DqOOQO'#Ds'#DsOOQO'#Du'#DuOOQO'#Dw'#DwOwQPO'#DnO4vQPO'#DnOOQO'#Fq'#FqO4{QPO1G/nOOQO,5<Y,5<YO5oQPO,5;uO5vQPO,59bOOQO-E9l-E9lOOQO,5:k,5:kO9[QPO,5<OO9cQPO1G0PO:VQPO1G0PO:VQPO1G0POOQO'#Cz'#CzOOQO'#C}'#C}OOQO'#DW'#DWOOQO'#DP'#DPO:zQPO,59}OOQO'#D['#D[OOQO'#D_'#D_OOQO'#Dd'#DdO;SQPO,59}OOQO'#El'#ElO;XQPO,5;VO%`QPO,59hO%`QPO,59hO%`QPO,59hO%`QPO,59hO%`QPO,59hOOQO,5:j,5:jO4lQPO,5:jO;aQPO,5;^OOQO'#Ev'#EvOOQO'#Ft'#FtO;hQPO,5;^O%`QPO'#EuO#fQPO,5;kO;sQPO,5;vOOQO,5;[,5;[O<QQPO,5;yO<YQPO1G0ZO<}QPO'#E`O=xQPO1G0cOOQO'#Ei'#EiO>mQPO1G0lO>rQPO1G.eO?sQPO1G1nO?xQPO1G1oOOQO1G/q1G/qOOQO1G/s1G/sOOQO1G/r1G/rO@yQPO,5:YOwQPO,5:YOOQO-E9o-E9oOBQQPO1G1aOOQO1G1a1G1aPB[QPO'#FnOOQO1G1j1G1jOOQO,5<^,5<^OBaQPO7+%kOOQO-E9p-E9pOCTQPO7+%kOOQO,59k,59kOCxQPO1G/iO-xQPO1G/iOOQO1G0q1G0qO;[QPO1G0qOG]QPO1G/SOGdQPO1G/SOJvQPO1G/SOKQQPO1G/SOOQO1G/S1G/SOOQO1G0U1G0UO;hQPO1G0xOOQO-E9r-E9rOOQO'#E{'#E{OOQO'#E}'#E}OOQO1G0x1G0xO;nQPO1G0xO%`QPO'#EzOK[QPO,5;aOKcQPO1G1VOKhQPO1G1bOOQO1G1b1G1bOKoQPO1G1bO%`QPO1G1bOOQO'#Fb'#FbOKtQPO1G1eOKyQPO7+%uOLmQPO7+%uOOQO'#Eb'#EbOOQO'#Ed'#EdOOQO,5:z,5:zONSQPO7+%}ON^QPO7+%}OOQO7+&W7+&WONeQPO'#CrONoQPO'#CiONwQPO'#ChO/WQPO'#CxOOQO'#F|'#F|OOQO'#Fz'#FzON|QPO'#FmO!!TQPO7+$PO!![QPO'#CxO#fQPO7+'YONhQPO'#CrOOQO'#GW'#GWO!!aQPO'#FuO!#hQPO7+'ZOOQO'#Cs'#CsO%`QPO1G/tO!#oQPO1G/tO!$vQPO7+&{O!%OQPO7+&{OOQO7+&{7+&{P!PQPO'#FrO!%VQPO<<IVO-xQPO7+%TO!%yQPO'#DfO!&TQPO7+%TOOQO7+&]7+&]OOQO7+&d7+&dO;nQPO7+&dO!&YQPO,5;fOOQO'#Ex'#ExO%`QPO1G0{OOQO7+&q7+&qOOQO7+&|7+&|O!&aQPO7+&|O%`QPO7+'PO!&hQPO<<IaOOQO,5<_,5<_O!'[QPO<<IiOOQO-E9q-E9qOOQO'#Cd'#CdO!(SQPO,58}OOQO'#Cl'#ClOOQO'#Cn'#CnOOQO,59T,59TO!)^QPO,59SO:zQPO,5<PO!)fQPO,5<PO;XQPO,5<QO%`QPO,59eO%`QPO,59eO%`QPO,59eO%`QPO,59eO%`QPO,59eO4lQPO,59dOOQO,5<X,5<XOOQO-E9k-E9kOOQO<<Gk<<GkO%`QPO,59dO!)kQPO<<JtOOQO,5<a,5<aOOQO-E9s-E9sOOQO<<Ju<<JuO!)pQPO7+%`O%`QPO7+%`OOQO-E9m-E9mO!*vQPO<<JgOOQO<<Jg<<JgO!*}QPO,5<ZO!+XQPO<<HoO!+^QPO,5:QO!+fQPO,5:QOOQO<<Ho<<HoOOQO<<JO<<JOO!+mQPO7+&gOOQO<<Jh<<JhO!+zQPO<<JkP1uQPO'#FsOOQO'#Cf'#CfOOQO'#Ce'#CeOOQO1G.i1G.iO!,RQPO1G.nO4lQPO1G.nO!,WQPO1G1kO-xQPO1G1kOOQO1G1l1G1lO;[QPO1G1lO!-gQPO1G/PO!-nQPO1G/PO!.|QPO1G/PO!/WQPO1G/POOQO1G/P1G/POOQO1G/O1G/OO!/bQPO1G/OOOQOAN@`AN@`O!0bQPO<<HzP%`QPO'#FoOOQOAN@RAN@ROOQOAN>ZAN>ZO!1hQPO1G/lOOQOAN@VAN@VO!1oQPO'#CvO!2|QPO'#CuOOQO7+$Y7+$YO!)aQPO7+$YO-xQPO7+'VO!3RQPO7+'VOOQO7+'W7+'WO!,RQPO,59aO!,RQPO<<GtO!3WQPO<<JqOOQO<<Jq<<JqOOQO1G.{1G.{OOQOAN=`AN=`OOQOAN@]AN@]",
1273
+ stateData: "!3b~O$lOSPOS~OUPO!^QO!oSO!vUO!yVO#OXO#RYO#[[O$_aO~OThO$miO~OTmO}oO!PxO!QxO!SrO#T!RO#cwO#hyO#u{O#v!RO#y}O#{!OO$Q!SO$T!VO$V!WO$mpO~O!qqO~P!PO!{!]O~O#T!`O~O!^QO!oSO!vUO!yVO#OXO#RYO#[[O~O$j!ZX$y!ZX$t!ZX~P#fOT!bO~OT!cO~O$y!dOU$`X!^$`X!o$`X!v$`X!y$`X#O$`X#R$`X#[$`X$_$`X$j$`X~O$y!dO~OTmO}oO!PxO!QxO#T!RO#cwO#hyO#u{O#v!RO#y}O#{!OO$Q!SO$T!VO$V!WO$mpO~OT!jOe!iO_!_X!^!_X!d!_X!f!_X!h!_X!j!_X!l!_X!o!_X!v!_X!y!_X#O!_X#R!_X#[!_X$j!_X$y!_X$t!_Xh!_X~OT!jOe!iO_!`X!^!`X!d!`X!f!`X!h!`X!j!`X!l!`X!o!`X!v!`X!y!`X#O!`X#R!`X#[!`X$j!`X$y!`X$t!`Xh!`X~O_!pO!d!qO!f!rO!h!sO!j!tO!l!uO~O!^![a!o![a!v![a!y![a#O![a#R![a#[![a$j![a$y![a$t![a~P({O$m!{O$o!zOejXkjXojXrjXtjXujXvjXwjXxjXyjX{jX}jX!PjX!QjX!SjX!TjX!UjX!XjX#ajX~OTjX!^jX!ojX!vjX!yjX#OjX#RjX#[jX$jjX$rjX$yjX$tjX#kjX#mjX#rjX_jX!djX!fjX!hjX!jjX!ljX#pjX#TjX#cjX#hjX#ujX#vjX#yjX#{jX$QjX$TjX$VjX~P*TOT!|O!S#OO~O!^QO!oSO!vUO!yVO#OXO#RYO#[[O~P%`O$r#QO!^!ma!o!ma!v!ma!y!ma#O!ma#R!ma#[!ma$j!ma$y!ma$t!ma~Oe!iOk#WOo#TOr#UOt#WOu#WOv#WOw#WOx#WOy#WO{#VO}oO!P#YO!Q#YO!S#ZO!T#ZO!U#ZO!X#[O#a#^O~OT!jO!^!rX!o!rX!v!rX!y!rX#O!rX#R!rX#[!rX$j!rX$r!rX$y!rX$t!rX~P/WO#k#hO~P%`O$m#lO~O$m#mO~O$m#oO~OT#qO#T#qO~O#^#sO!^#Ya!o#Ya!v#Ya!y#Ya#O#Ya#R#Ya#[#Ya$j#Ya$y#Ya$t#Ya~Oe!iO$m#uO~O$m#wO~OT!|O~O!^!ta!o!ta!v!ta!y!ta#O!ta#R!ta#[!ta$j!ta$y!ta$t!ta~P/ZO!^!|a!o!|a!v!|a!y!|a#O!|a#R!|a#[!|a$j!|a$y!|a$t!|a~P/ZOT!jO~O$t#yO~O_!pO~O!^![i!o![i!v![i!y![i#O![i#R![i#[![i$j![i$y![i$t![i~P({O$t$PO~P%`O$o!zOTjaejakjaojarjatjaujavjawjaxjayja{ja}ja!Pja!Qja!Sja!Tja!Uja!Xja!^ja!oja!vja!yja#Oja#Rja#[ja#aja$jja$rja$yja$tja#kja#mja#rja_ja!dja!fja!hja!jja!lja#pja#Tja#cja#hja#uja#vja#yja#{ja$Qja$Tja$Vja~O$t$RO~P/ZO!^!mi!o!mi!v!mi!y!mi#O!mi#R!mi#[!mi$j!mi$y!mi$t!mi~P!PO$r$TO!^!mi!o!mi!v!mi!y!mi#O!mi#R!mi#[!mi$j!mi$y!mi$t!mi~O{#VO!X#[O~O$m$YO~O}oO#cwO~O#k#hO~P/ZO#k#hO#p$eO#r$fO~O!S$nO!qqO$t$mO~P%`OT$pO#v$pO~O$r$rO!^!wi!o!wi!v!wi!y!wi#O!wi#R!wi#[!wi$j!wi$y!wi$t!wi~O#V$tO#X$uO!^#SX!o#SX!v#SX!y#SX#O#SX#R#SX#[#SX$j#SX$r#SX$y#SX$t#SX~O$r$wO!^#Pi!o#Pi!v#Pi!y#Pi#O#Pi#R#Pi#[#Pi$j#Pi$y#Pi$t#Pi~O#T$yO~OT$zO_!pO}oO!PxO!QxO#T!RO#cwO#hyO#u{O#v!RO#y}O#{!OO$Q!SO$T!VO$V!WO~O$m%TO~OT%UO_!pO}oO!PxO!QxO#T!RO#cwO#hyO#u{O#v!RO#y}O#{!OO$Q!SO$T!VO$V!WO~Oh%YO_!ba!^!ba!d!ba!f!ba!h!ba!j!ba!l!ba!o!ba!v!ba!y!ba#O!ba#R!ba#[!ba$j!ba$y!ba$t!ba~O$r%^O$t%_O~P/ZO$o!zO~O!^!mq!o!mq!v!mq!y!mq#O!mq#R!mq#[!mq$j!mq$y!mq$t!mq~P!PO$r%aO!^!mq!o!mq!v!mq!y!mq#O!mq#R!mq#[!mq$j!mq$y!mq$t!mq~O$m%bO~Ok#WOt#WOu#WOv#WOw#WOx#WOy#WO{#VO}oO!P#YO!Q#YO!S#ZO!T#ZO!U#ZO!X#[O#a#^OTpiepiopi!^pi!opi!vpi!ypi#Opi#Rpi#[pi$jpi$rpi$ypi$tpi#kpi#mpi#rpi_pi!dpi!fpi!hpi!jpi!lpi#ppi#Tpi#cpi#hpi#upi#vpi#ypi#{pi$Qpi$Tpi$Vpi~Or#UO~PC}Orpi~PC}O!S#ZO!T#ZO!U#ZOTpiepikpiopirpitpiupivpiwpixpiypi{pi}pi!Xpi!^pi!opi!vpi!ypi#Opi#Rpi#[pi#api$jpi$rpi$ypi$tpi#kpi#mpi#rpi_pi!dpi!fpi!hpi!jpi!lpi#ppi#Tpi#cpi#hpi#upi#vpi#ypi#{pi$Qpi$Tpi$Vpi~O!P#YO!Q#YO~PGkO!Ppi!Qpi~PGkO#m%iO~P/ZO$t%kO~O$t%lO~P/ZO$t%lO~O!^QO~O!^!wq!o!wq!v!wq!y!wq#O!wq#R!wq#[!wq$j!wq$y!wq$t!wq~P!PO$r%oO!^!wq!o!wq!v!wq!y!wq#O!wq#R!wq#[!wq$j!wq$y!wq$t!wq~O!^#Pq!o#Pq!v#Pq!y#Pq#O#Pq#R#Pq#[#Pq$j#Pq$y#Pq$t#Pq~OT#qO#T#qO~PMbO$r%qO~PMbOT%sO$ZfX~P*TOa%uOc%vO~OT%xO~O$r&SOT$aX_$aX}$aX!P$aX!Q$aX#T$aX#c$aX#h$aX#u$aX#v$aX#y$aX#{$aX$Q$aX$T$aX$V$aX$t$aX~O$t&UO~P>rO$Z&VO~O$r&XOT$iX_$iX}$iX!P$iX!Q$iX#T$iX#c$iX#h$iX#u$iX#v$iX#y$iX#{$iX$Q$iX$T$iX$V$iX$t$iX~O$t&ZO~P?xOh%YO_!bi!^!bi!d!bi!f!bi!h!bi!j!bi!l!bi!o!bi!v!bi!y!bi#O!bi#R!bi#[!bi$j!bi$y!bi$t!bi~O$r&_O$t&`O~O$t&`O~P%`O!^!my!o!my!v!my!y!my#O!my#R!my#[!my$j!my$y!my$t!my~P!PO$r&dO$t!YX~P/ZO$t&eO~O#r#na~P/ZO$t&hO~P/ZO!^!wy!o!wy!v!wy!y!wy#O!wy#R!wy#[!wy$j!wy$y!wy$t!wy~P!POT#qO#T#qO!^#Py!o#Py!v#Py!y#Py#O#Py#R#Py#[#Py$j#Py$y#Py$t#Py~OZ&kOTVa_Va}Va!PVa!QVa#TVa#cVa#hVa#uVa#vVa#yVa#{Va$QVa$TVa$VVa$rVa$tVa~Oe!iOh%YO~O$m&qO~O$t&{O~O_!bq!^!bq!d!bq!f!bq!h!bq!j!bq!l!bq!o!bq!v!bq!y!bq#O!bq#R!bq#[!bq$j!bq$y!bq$t!bq~P/ZO$t'OO~P%`O$r$ca$t$ca~P/ZO$t'PO~O$r'QO$t!Ya~O$t!Ya~P%`O#k#iq#p#iq#r#iq~P/ZO$t'RO~P/ZOT'SO~O$m'WO~Ok#WOt#WOu#WOv#WOw#WOx#WOy#WO{#VO}oO!P#YO!Q#YO!S#ZO!T#ZO!U#ZO!X#[O#a#^Oemiomi~Or#UO~P!,]Ormi~P!,]O!S#ZO!T#ZO!U#ZOemikmiomirmitmiumivmiwmixmiymi{mi}mi!Xmi#ami~O!P#YO!Q#YO~P!-uO!Pmi!Qmi~P!-uOTli_li#Tli#cli#hli#uli#vli#yli#{li$Qli$Tli$Vli$rli$tli~P/ZO_!by!^!by!d!by!f!by!h!by!j!by!l!by!o!by!v!by!y!by#O!by#R!by#[!by$j!by$y!by$t!by~P/ZO$t!Yi~P%`O$o!zOkjXTjX_jX}jX!PjX!QjX#TjX#cjX#hjX#ujX#vjX#yjX#{jX$QjX$TjX$VjX$rjX$tjX~Ok'ZO~O$t'^O~O$t'aO~OP!T!S!Q~",
1274
+ goto: "Hi${PPP$|%QPP%U%Y%]%`P%c%k%qP&OP&OP&RP&e'TP'a'gP%c(q(wP)_*^P*vPPPPPP+bP,OP-sPP.aPPP)_/PP/q/}0i0vP1W1W1]2a2eP2eP2eP2eP2eP0i2iP2vP2|3_0i3jP0i3wP4UP0i4[P0i4iP4vP5OP5OP0i5RP5`P)_5cP5}P7^8a7^9dP:g:mP:sP:v:|P;QP7^;[PP<_=bP=bP<_>e>e?hP7^@kPAnP1b(q(qP$|$|AqPAuA{BRC_CiCxDOD^DdDnPPPPDtPDxEOPGVPG`7^>e)_PHeTcOdT`OdT%P#u%RR%t$zR&m%tR&l%tS%P#u%RT%V#w%XX$|#u#w%R%XS!vl!yQ#|!wX${#u#w%R%XR%w${Q!lhQ!ojQ#fvQ#v!bQ&R$}R&o%xQ!khQ!njQ#evQ#x!lQ#z!oQ$b#fW%S#u#w%R%XQ&y&RR'V&oQ%Z#{Q&]%[Q&n%xR'['VQ'U&nR'`'[#Q!UTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WS'T&n'[R'_'ZX%O#u#w%R%Xr#`v!g!h#P#g$O$j$l%c%h%m&[&a&g&i&z&|R%|$}!y!YTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m$T$Y$i$o$r%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'Wv#av!g!h#P#g$O$]$j$l%c%h%m&[&a&g&i&t&z&|R%}$}z#bv!g!h#P#g$O$]$^$j$l%c%h%m&[&a&g&i&t&u&z&|R&O$}|#Wv!g!h#P#g$O$]$^$j$l$}%c%h%m&[&a&g&i&t&u&z&|T$W#X%y#QxTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'Wz#Xv!g!h#P#g$O$]$^$j$l%c%h%m&[&a&g&i&t&u&z&|Q$[#_Q%y$}R&s%{!O#cv!g!h#P#g$O$]$^$_$j$l%c%h%m&[&a&g&i&t&u&v&z&|R&P$}!S#dv!g!h#P#g$O$]$^$_$`$j$l%c%h%m&[&a&g&i&t&u&v&w&z&|R&Q$}z#]v!g!h#P#g$O$]$^$j$l%c%h%m&[&a&g&i&t&u&z&|Q$X#XQ%z$}R&p%yQ%d$YQ&b%bQ'X&qR']'WSeOdS!mipQ$k#lQ%d$YQ&W%TQ&b%bQ'X&qR']'Wg^O_dip#l$Y%T%b&q'WfRO_dip#l$Y%T%b&q'WR%n$qVkR!v#|UjR!v#|!y!XTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m$T$Y$i$o$r%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WT!xl!yT!wl!ygTO_dip#l$Y%T%b&q'WQuTR$o#mQtTQ#SuQ#p!^]$S#Q$T$r%`%a%ocsTu!^#Q$T$r%`%a%ogfO_dip#l$Y%T%b&q'WgWO_dip#l$Y%T%b&q'WQ!^WR!_ZggO_dip#l$Y%T%b&q'WgZO_dip#l$Y%T%b&q'WQ#r!_V%p$w%q&jR$v#qg]O_dip#l$Y%T%b&q'WR#t!`z#_v!g!h#P#g$O$]$^$j$l%c%h%m&[&a&g&i&t&u&z&|R%{$}#Q!QTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WQ$Z#_Q%e$[Q&r%{R'Y&s#R!WTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!ZTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#RzTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WX#iz#g#j$cX#kz#g#j$cR%j$jQ$h#jR%g$cT$i#j$cQ$g#jS%f$c$hR&f%g#R|Tfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!RTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!PTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!UTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!TTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R![Tfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WR$q#oTbOdQdOR!edQ%R#uR&T%RbnTu!^#Q$T$r%`%a%o!t!ffgpz!Z!{#`#a#b#c#d#k#m#u#w$Y$i$o%R%X%Z%^%b%j%n%|%}&O&P&Q&V&]&_&d&n&q&}'Q'W'Z'[T!}n!fQ%]$OS&^%]&cR&c%cd_Odip#l$Y%T%b&q'WR!a_Q!ylR#}!yQ#RtU$U#R$V$sQ$V#SR$s#pQ$x#rR%r$xQ#jzQ$c#gT$d#j$cQ%X#wR&Y%XT%Q#u%RX$}#u#w%R%XbvTu!^#Q$T$r%`%a%oQ!gfQ!hgQ#PpQ#gzQ#n!ZQ$O!{Q$]#`Q$^#aQ$_#bQ$`#cQ$a#dQ$j#kQ$l#mW%c$Y%b&q'WQ%h$iQ%m$oQ&[%ZY&a%^&_&d&}'QQ&g%jQ&i%nQ&t%|Q&u%}Q&v&OQ&w&PQ&x&QQ&z&VR&|&]QlRQ#{!vR%[#|!x!YTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m$T$Y$i$o$r%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WX%O#u#w%R%XT%W#w%X",
1275
+ nodeNames: "\u26A0 Comment Program TableStatement Kw Identifier table ColumnDef DataType PrimaryKey Kw primary_key JoinDef JoinType Kw join Kw one Kw many Kw as Alias Kw on BinaryExpression Ref = ComputedDef BinaryExpression Kw or BinaryExpression Kw and ComparisonOp != <> < > <= >= Kw like Kw not AddOp + - MulOp * / % InExpression Kw in InValueList QueryStatement FromClause Kw from TableName Subquery SubqueryExpression JoinClause Kw inner Kw left Kw right Kw full Kw cross SelectClause Kw select Kw distinct SelectItem Wildcard WhereClause Kw where GroupByClause Kw group Kw by HavingClause Kw having OrderByClause Kw order OrderItem Number Kw asc Kw desc LimitClause Kw limit Kw offset NullTestExpression Kw is Kw null UnaryExpression UnaryOperator CaseExpression Kw case WhenClause Kw when Kw then ElseClause Kw else Kw end ExistsExpression Kw exists String Boolean Kw true Kw false Null FunctionCall Count Kw count ExtractExpression Kw extract ExtractUnit Param Parenthetical InExpression NullTestExpression : ViewStatement ExtendStatement Kw extend",
1276
+ maxTerm: 180,
1034
1277
  nodeProps: [
1035
- ["group", -6, 23, 56, 107, 119, 123, 128, "Expression", -12, 24, 27, 28, 34, 47, 93, 103, 112, 113, 118, 120, 127, "Expression Expression", -2, 54, 55, "TablePrimary"]
1278
+ ["group", -9, 25, 29, 32, 53, 63, 107, 146, 147, 148, "Expression", -12, 26, 97, 112, 114, 127, 130, 131, 136, 137, 138, 141, 145, "Expression Expression", -2, 61, 62, "TablePrimary"]
1036
1279
  ],
1037
1280
  skippedNodes: [0, 1],
1038
- repeatNodeCount: 9,
1039
- tokenData: "*g~RnX^#Ppq#Pqr#trs$Ptu$nuv%`wx%exy%}yz&Sz{&X{|&^|}&c}!O&h!O!P'X!P!Q'^!Q![(m!]!^)W!^!_)]!_!`)r!`!a)w!c!}*U#T#o*U#y#z#P$f$g#P#BY#BZ#P$IS$I_#P$I|$JO#P$JT$JU#P$KV$KW#P&FU&FV#P~#UY$d~X^#Ppq#P#y#z#P$f$g#P#BY#BZ#P$IS$I_#P$I|$JO#P$JT$JU#P$KV$KW#P&FU&FV#P~#wP!_!`#z~$PO#z~~$STOr$Prs$cs;'S$P;'S;=`$h<%lO$P~$hO#d~~$kP;=`<%l$P~$qS!Q![$}!c!}$}#R#S$}#T#o$}~%SS#k~!Q![$}!c!}$}#R#S$}#T#o$}~%eO$V~~%hTOw%ewx$cx;'S%e;'S;=`%w<%lO%e~%zP;=`<%l%e~&SO$e~~&XO$k~~&^O!o~~&cOq~~&hO$l~~&mPp~}!O&p~&uSP~OY&pZ;'S&p;'S;=`'R<%lO&p~'UP;=`<%l&p~'^O$g~~'cP$U~z{'f~'iTOz'fz{'x{;'S'f;'S;=`(g<%lO'f~'{VOz'fz{'x{!P'f!P!Q(b!Q;'S'f;'S;=`(g<%lO'f~(gOP~~(jP;=`<%l'f~(rQ#P~!O!P(x!Q![(m~({P!Q![)O~)TP#P~!Q![)O~)]O$o~~)bQ#|~!_!`)h!`!a)m~)mO$O~~)rO#{~~)wOi~~)|P#}~!_!`*P~*UO$P~~*ZST~!Q![*U!c!}*U#R#S*U#T#o*U",
1281
+ repeatNodeCount: 10,
1282
+ tokenData: "*o~RoX^#Spq#Sqr#wrs$Stu$quv%cwx%hxy&Qyz&Vz{&[{|&a|}&f}!O&k!O!P'[!P!Q'a!Q![(p![!])Z!]!^)`!^!_)e!_!`)z!`!a*P!c!}*^#T#o*^#y#z#S$f$g#S#BY#BZ#S$IS$I_#S$I|$JO#S$JT$JU#S$KV$KW#S&FU&FV#S~#XY$l~X^#Spq#S#y#z#S$f$g#S#BY#BZ#S$IS$I_#S$I|$JO#S$JT$JU#S$KV$KW#S&FU&FV#S~#zP!_!`#}~$SOt~~$VTOr$Srs$fs;'S$S;'S;=`$k<%lO$S~$kO#v~~$nP;=`<%l$S~$tS!Q![%Q!c!}%Q#R#S%Q#T#o%Q~%VS$V~!Q![%Q!c!}%Q#R#S%Q#T#o%Q~%hO!U~~%kTOw%hwx$fx;'S%h;'S;=`%z<%lO%h~%}P;=`<%l%h~&VO$m~~&[O$t~~&aO!S~~&fO!P~~&kO$r~~&pP!Q~}!O&s~&xSP~OY&sZ;'S&s;'S;=`'U<%lO&s~'XP;=`<%l&s~'aO$o~~'fP!T~z{'i~'lTOz'iz{'{{;'S'i;'S;=`(j<%lO'i~(OVOz'iz{'{{!P'i!P!Q(e!Q;'S'i;'S;=`(j<%lO'i~(jOP~~(mP;=`<%l'i~(uQ#T~!O!P({!Q![(p~)OP!Q![)R~)WP#T~!Q![)R~)`O$Z~~)eO$y~~)jQv~!_!`)p!`!a)u~)uOx~~)zOu~~*POk~~*UPw~!_!`*X~*^Oy~~*cST~!Q![*^!c!}*^#R#S*^#T#o*^",
1040
1283
  tokenizers: [0],
1041
1284
  topRules: { "Program": [0, 2] },
1042
1285
  specialized: [{ term: 5, get: (value, stack) => specializeIdentifier(value, stack) << 1, external: specializeIdentifier }, { term: 5, get: (value) => spec_Identifier[value] || -1 }],
1043
- tokenPrec: 2599
1286
+ tokenPrec: 2957
1044
1287
  });
1045
1288
  }
1046
1289
  });
@@ -1203,6 +1446,7 @@ function isFence(event) {
1203
1446
  var COMPONENT_ATTRIBUTE_KEYS, GSQL_FENCE, COMPONENT_TAG, ATTRIBUTE;
1204
1447
  var init_markdown = __esm({
1205
1448
  "../lang/markdown.ts"() {
1449
+ "use strict";
1206
1450
  init_parser();
1207
1451
  COMPONENT_ATTRIBUTE_KEYS = ["x", "y", "y2", "series", "value", "category"];
1208
1452
  GSQL_FENCE = /^([ \t]*)(`{3,})g?sql[^\n]*\n([\s\S]*?)^\1\2[ \t]*$/gim;
@@ -1211,6 +1455,74 @@ var init_markdown = __esm({
1211
1455
  }
1212
1456
  });
1213
1457
 
1458
+ // ../lang/snowflake.ts
1459
+ function uppercaseMalloyQuery(query) {
1460
+ query.baseTableName = uppercaseIdentifier(query.baseTableName);
1461
+ for (let stage of query.pipeline || []) {
1462
+ let fields = stage.queryFields || [];
1463
+ fields.forEach((field) => uppercaseColumnField(field));
1464
+ let filters = stage.filterList || [];
1465
+ filters.forEach((filter) => uppercaseExpression(filter?.e));
1466
+ }
1467
+ }
1468
+ function uppercaseTable(table2) {
1469
+ if (table2.upperCased) return;
1470
+ table2.upperCased = true;
1471
+ table2.name = uppercaseIdentifier(table2.name);
1472
+ if (table2.primaryKey) table2.primaryKey = uppercaseIdentifier(table2.primaryKey);
1473
+ if (table2.tableName) table2.tableName = uppercaseQualified(table2.tableName);
1474
+ if (table2.tablePath) table2.tablePath = uppercaseQualified(table2.tablePath);
1475
+ table2.fields?.forEach((field) => uppercaseField(field));
1476
+ if (table2.query) uppercaseMalloyQuery(table2.query);
1477
+ }
1478
+ function uppercaseField(field) {
1479
+ if (!field) return;
1480
+ if (isJoinField(field)) {
1481
+ field.name = uppercaseIdentifier(field.name);
1482
+ if (field.tableName) field.tableName = uppercaseQualified(field.tableName);
1483
+ if (field.tablePath) field.tablePath = uppercaseQualified(field.tablePath);
1484
+ if (field.structPath) field.structPath = field.structPath.map(uppercaseIdentifier);
1485
+ if (field.path) field.path = field.path.map(uppercaseIdentifier);
1486
+ if (field.onExpression) uppercaseExpression(field.onExpression);
1487
+ uppercaseTable(field);
1488
+ } else {
1489
+ uppercaseColumnField(field);
1490
+ }
1491
+ }
1492
+ function uppercaseColumnField(field) {
1493
+ if (!field) return;
1494
+ field.name = uppercaseIdentifier(field.name);
1495
+ if (field.path) field.path = field.path.map(uppercaseIdentifier);
1496
+ if (field.structPath) field.structPath = field.structPath.map(uppercaseIdentifier);
1497
+ if (field.tableName) field.tableName = uppercaseQualified(field.tableName);
1498
+ if (field.tablePath) field.tablePath = uppercaseQualified(field.tablePath);
1499
+ if (field.e) uppercaseExpression(field.e);
1500
+ }
1501
+ function uppercaseExpression(expr) {
1502
+ if (!expr) return;
1503
+ walkExpression(expr, (node) => {
1504
+ if (Array.isArray(node.path)) node.path = node.path.map(uppercaseIdentifier);
1505
+ if (Array.isArray(node.structPath)) node.structPath = node.structPath.map(uppercaseIdentifier);
1506
+ });
1507
+ }
1508
+ function uppercaseIdentifier(value) {
1509
+ if (!value) return value || "";
1510
+ return value.toString().toUpperCase();
1511
+ }
1512
+ function uppercaseQualified(value) {
1513
+ if (!value) return value;
1514
+ return value.split(".").map((part) => part.startsWith('"') && part.endsWith('"') ? part : uppercaseIdentifier(part)).join(".");
1515
+ }
1516
+ function isJoinField(field) {
1517
+ return !!field?.join;
1518
+ }
1519
+ var init_snowflake = __esm({
1520
+ "../lang/snowflake.ts"() {
1521
+ "use strict";
1522
+ init_util();
1523
+ }
1524
+ });
1525
+
1214
1526
  // ../lang/core.ts
1215
1527
  import { registerDialect, StandardSQLDialect, QueryModel, expandBlueprintMap } from "@graphenedata/malloy";
1216
1528
  import { readFile } from "node:fs/promises";
@@ -1225,15 +1537,19 @@ function getDiagnostics() {
1225
1537
  async function loadWorkspace(dir, includeMd) {
1226
1538
  let files = await glob(includeMd ? "**/*.{gsql,md}" : "**/*.gsql", { cwd: dir, ignore: ["node_modules/**"] });
1227
1539
  for await (let file of files) {
1228
- let contents = await readFile(path2.join(dir, file), "utf-8");
1229
- updateFile(contents, file);
1540
+ try {
1541
+ let contents = await readFile(path2.join(dir, file), "utf-8");
1542
+ updateFile(contents, file);
1543
+ } catch (e) {
1544
+ console.error("Failed to read file", file, e.message);
1545
+ }
1230
1546
  }
1231
1547
  }
1232
- function updateFile(contents, path9) {
1233
- FILE_MAP[path9] ||= { path: path9, contents, tree: null, tables: [], queries: [] };
1234
- FILE_MAP[path9].contents = contents;
1235
- FILE_MAP[path9].tree = null;
1236
- return FILE_MAP[path9];
1548
+ function updateFile(contents, path10) {
1549
+ FILE_MAP[path10] ||= { path: path10, contents, tree: null, tables: [], queries: [] };
1550
+ FILE_MAP[path10].contents = contents;
1551
+ FILE_MAP[path10].tree = null;
1552
+ return FILE_MAP[path10];
1237
1553
  }
1238
1554
  function analyze(contents, type) {
1239
1555
  clearDiagnostics();
@@ -1246,6 +1562,7 @@ function analyze(contents, type) {
1246
1562
  recordSyntaxErrors(fi);
1247
1563
  findTables(fi);
1248
1564
  });
1565
+ Object.values(FILE_MAP).forEach(applyExtends);
1249
1566
  Object.values(FILE_MAP).flatMap((f) => f.tables).forEach(analyzeTable);
1250
1567
  if (contents) {
1251
1568
  let fi = FILE_MAP["input"];
@@ -1258,13 +1575,21 @@ function analyze(contents, type) {
1258
1575
  }
1259
1576
  function toSql(query, params = {}) {
1260
1577
  if (query.rawSql) return query.rawSql;
1261
- let contents = {};
1262
- let gsqlTables = Object.values(FILE_MAP).filter((f) => f.path !== "input").flatMap((f) => f.tables);
1263
- gsqlTables.forEach((t) => contents[t.name] = t);
1264
- let inputTables = [...FILE_MAP["input"]?.tables || [], ...query.subQuerySources];
1265
- inputTables.forEach((t) => contents[t.name] = { ...t, query: t.query && fillInParams(t.query, params) });
1266
- if (!query.malloyQuery) throw new Error("Cannot compile query without Malloy query");
1267
- let malloyQuery = fillInParams(query.malloyQuery, params);
1578
+ let contents = Object.fromEntries(Object.values(FILE_MAP).flatMap((fi) => {
1579
+ return fi.tables.map((t) => {
1580
+ t = structuredClone(t);
1581
+ if (fi.path == "input" && t.query) fillInParams(t.query, params);
1582
+ if (config.dialect == "snowflake") uppercaseTable(t);
1583
+ return [t.name, t];
1584
+ });
1585
+ }));
1586
+ query = structuredClone(query);
1587
+ fillInParams(query, params);
1588
+ if (config.dialect == "snowflake") uppercaseMalloyQuery(query);
1589
+ let tableQueries = Object.values(contents).map((t) => t.query);
1590
+ let joinQueries = Object.values(contents).flatMap((t) => t.fields.map((f) => f.query));
1591
+ let allQueries = [...tableQueries, ...joinQueries, query].filter((q) => !!q);
1592
+ allQueries.forEach((q) => q.structRef = contents[q.baseTableName]);
1268
1593
  let qm = new QueryModel({
1269
1594
  name: "generated_model",
1270
1595
  contents,
@@ -1272,11 +1597,12 @@ function toSql(query, params = {}) {
1272
1597
  dependencies: {},
1273
1598
  exports: []
1274
1599
  });
1275
- return qm.compileQuery(malloyQuery).sql;
1600
+ return qm.compileQuery(query).sql;
1276
1601
  }
1277
1602
  var BigQueryDialect;
1278
1603
  var init_core = __esm({
1279
1604
  "../lang/core.ts"() {
1605
+ "use strict";
1280
1606
  init_analyze();
1281
1607
  init_params();
1282
1608
  init_util();
@@ -1284,6 +1610,7 @@ var init_core = __esm({
1284
1610
  init_functions();
1285
1611
  init_parser();
1286
1612
  init_markdown();
1613
+ init_snowflake();
1287
1614
  BigQueryDialect = class extends StandardSQLDialect {
1288
1615
  constructor() {
1289
1616
  super();
@@ -1349,6 +1676,7 @@ function printTable(rows) {
1349
1676
  var styleText;
1350
1677
  var init_printer = __esm({
1351
1678
  "printer.ts"() {
1679
+ "use strict";
1352
1680
  init_core();
1353
1681
  styleText = (style, text) => {
1354
1682
  try {
@@ -1361,21 +1689,20 @@ var init_printer = __esm({
1361
1689
  });
1362
1690
 
1363
1691
  // background.ts
1364
- import { spawn } from "child_process";
1692
+ import { spawn, exec } from "child_process";
1693
+ import { promisify } from "util";
1365
1694
  import { fileURLToPath } from "url";
1366
1695
  import fs2 from "fs-extra";
1367
1696
  import path3 from "path";
1368
1697
  async function runServeInBackground() {
1369
- let root = process.cwd();
1370
- let grapheneCache = getGrapheneCache(root);
1698
+ let grapheneCache = getGrapheneCache(config.root);
1371
1699
  let logFile = path3.join(grapheneCache, "serve.log");
1372
1700
  await fs2.ensureDir(grapheneCache);
1373
- await stopGrapheneIfRunning(root);
1374
1701
  let log = fs2.openSync(logFile, "w");
1375
1702
  let entryPoint = process.argv[1] || fileURLToPath(import.meta.url);
1376
- let childArgs = [...process.execArgv, entryPoint, "serve", "--fg", ...process.argv.slice(3)];
1703
+ let childArgs = [...process.execArgv, entryPoint, "serve"];
1377
1704
  let child = spawn(process.execPath, childArgs, {
1378
- cwd: root,
1705
+ cwd: config.root,
1379
1706
  detached: true,
1380
1707
  env: { ...process.env },
1381
1708
  stdio: ["ignore", log, log]
@@ -1394,7 +1721,6 @@ async function runServeInBackground() {
1394
1721
  }
1395
1722
  });
1396
1723
  child.once("exit", () => {
1397
- process.stdout.write(fs2.readFileSync(logFile));
1398
1724
  reject(new Error("Exited before server started"));
1399
1725
  });
1400
1726
  child.once("error", (e) => reject(e));
@@ -1403,15 +1729,9 @@ async function runServeInBackground() {
1403
1729
  function getGrapheneCache(root) {
1404
1730
  return path3.join(root, "node_modules", ".graphene");
1405
1731
  }
1406
- function getPidFilePath(root) {
1407
- return path3.join(getGrapheneCache(root), process.env.NODE_ENV == "test" ? "test.pid" : "serve.pid");
1408
- }
1409
- function targetPids(pid) {
1410
- if (process.platform === "win32") return [pid];
1411
- return [pid, -pid];
1412
- }
1413
1732
  function sendSignal(pid, signal) {
1414
- for (let target of targetPids(pid)) {
1733
+ let pids = process.platform === "win32" ? [pid] : [pid, -pid];
1734
+ for (let target of pids) {
1415
1735
  try {
1416
1736
  process.kill(target, signal);
1417
1737
  } catch (err) {
@@ -1422,229 +1742,67 @@ function sendSignal(pid, signal) {
1422
1742
  }
1423
1743
  return true;
1424
1744
  }
1425
- async function stopGrapheneIfRunning(root) {
1426
- if (!await isServerRunning()) return;
1427
- let pidFile = getPidFilePath(root);
1428
- let pid = await readPid(pidFile);
1745
+ async function stopGrapheneIfRunning() {
1746
+ let port = Number(process.env.GRAPHENE_PORT) || 4e3;
1747
+ let pid = await getPidOnPort(port);
1429
1748
  if (!pid) return;
1430
1749
  console.log(`Stopping server (${pid})`);
1431
1750
  sendSignal(pid, "SIGTERM");
1432
1751
  let end = Date.now() + 5e3;
1433
- while (Date.now() < end && isServerRunning()) {
1752
+ while (Date.now() < end) {
1753
+ if (!await getPidOnPort(port)) break;
1434
1754
  await new Promise((resolve) => setTimeout(resolve, 100));
1435
1755
  }
1436
- if (!isServerRunning()) return;
1437
- sendSignal(pid, "SIGKILL");
1438
- await fs2.remove(pidFile);
1439
- }
1440
- async function readPid(pidFile) {
1441
- if (!await fs2.pathExists(pidFile)) return void 0;
1442
- let contents = (await fs2.readFile(pidFile, "utf8")).trim();
1443
- if (!contents) return void 0;
1444
- let pid = Number.parseInt(contents, 10);
1445
- if (Number.isNaN(pid)) return void 0;
1446
- return pid;
1447
- }
1448
- async function isServerRunning() {
1449
- let pidFile = getPidFilePath(config.root);
1450
- let pid = await readPid(pidFile);
1451
- if (!pid) return false;
1452
- try {
1453
- process.kill(pid, 0);
1454
- return true;
1455
- } catch {
1456
- fs2.removeSync(pidFile);
1457
- return false;
1756
+ if (await getPidOnPort(port)) {
1757
+ sendSignal(pid, "SIGKILL");
1458
1758
  }
1459
- }
1460
- var init_background = __esm({
1461
- "background.ts"() {
1462
- init_config();
1759
+ if (await getPidOnPort(port)) {
1760
+ console.error("Failed to stop previous Graphene server");
1463
1761
  }
1464
- });
1465
-
1466
- // connections/bigQuery.ts
1467
- var bigQuery_exports = {};
1468
- __export(bigQuery_exports, {
1469
- BigQueryConnection: () => BigQueryConnection
1470
- });
1471
- import { BigQuery, BigQueryDate, BigQueryTimestamp } from "@google-cloud/bigquery";
1472
- var BigQueryConnection;
1473
- var init_bigQuery = __esm({
1474
- "connections/bigQuery.ts"() {
1475
- init_config();
1476
- BigQueryConnection = class {
1477
- client;
1478
- constructor(options = {}) {
1479
- options.projectId ||= config.bigquery?.projectId;
1480
- if (process.env.GOOGLE_CREDENTIALS_CONTENT) {
1481
- let parsed = JSON.parse(process.env.GOOGLE_CREDENTIALS_CONTENT);
1482
- options.projectId = parsed.project_id;
1483
- options.credentials = parsed;
1484
- }
1485
- if (!options.projectId) throw new Error("projectId must be set in config or provided in service account credentials");
1486
- this.client = new BigQuery({ ...options, userAgent: "Graphene" });
1487
- }
1488
- async runQuery(sql) {
1489
- let [job] = await this.client.createQueryJob({ query: sql, useLegacySql: false });
1490
- let [rows] = await job.getQueryResults({ maxResults: 1e4 });
1491
- let metadata = job.metadata || (await job.getMetadata())[0];
1492
- let totalRows = Number(metadata?.statistics?.query?.totalRows ?? rows.length);
1493
- rows.forEach((r) => {
1494
- Object.entries(r).forEach(([k, v]) => {
1495
- if (v instanceof BigQueryTimestamp) r[k] = v.value;
1496
- if (v instanceof BigQueryDate) r[k] = v.value;
1497
- });
1498
- });
1499
- return { rows, totalRows };
1500
- }
1501
- };
1502
- }
1503
- });
1504
-
1505
- // connections/duckdb.ts
1506
- var duckdb_exports = {};
1507
- __export(duckdb_exports, {
1508
- DuckDBConnection: () => DuckDBConnection
1509
- });
1510
- import { promises as fs3 } from "fs";
1511
- import path4 from "path";
1512
- import { DuckDBTimestampValue, DuckDBInstance, DuckDBDateValue } from "@duckdb/node-api";
1513
- var DuckDBConnection;
1514
- var init_duckdb = __esm({
1515
- "connections/duckdb.ts"() {
1516
- init_config();
1517
- DuckDBConnection = class {
1518
- options;
1519
- ready;
1520
- connection = null;
1521
- constructor(options) {
1522
- this.options = options || {};
1523
- this.ready = this.initialize();
1524
- }
1525
- async initialize() {
1526
- let dbPath = this.options.path;
1527
- if (!dbPath) {
1528
- let files = await fs3.readdir(config.root);
1529
- dbPath = files.find((f) => f.endsWith(".duckdb"));
1530
- if (!dbPath) throw new Error("No .duckdb file found in current directory");
1531
- dbPath = path4.resolve(config.root, dbPath);
1532
- }
1533
- let db = await DuckDBInstance.create(":memory:");
1534
- this.connection = await db.connect();
1535
- let escapedPath = dbPath.replace(/'/g, "''");
1536
- await this.connection.run(`attach '${escapedPath}' as graphene_cli (READ_ONLY);`);
1537
- await this.connection.run("use graphene_cli;");
1538
- }
1539
- async runQuery(sql) {
1540
- await this.ready;
1541
- let reader = await this.connection.runAndReadAll(sql);
1542
- let rows = reader.getRowObjects().map((record) => {
1543
- let out = {};
1544
- for (let [k, v] of Object.entries(record)) {
1545
- if (typeof v === "bigint") out[k] = Number(v);
1546
- else if (v === null) out[k] = null;
1547
- else if (v instanceof DuckDBTimestampValue) out[k] = new Date(Number(v.micros / 1000n)).toUTCString();
1548
- else if (v instanceof DuckDBDateValue) out[k] = v.toString();
1549
- else if (typeof v === "object") throw new Error(`Unsupported datatype ${v.constructor?.name}`);
1550
- else out[k] = v;
1551
- }
1552
- return out;
1553
- });
1554
- return { rows };
1555
- }
1556
- };
1557
- }
1558
- });
1559
-
1560
- // connections/snowflake.ts
1561
- var snowflake_exports = {};
1562
- __export(snowflake_exports, {
1563
- SnowflakeConnection: () => SnowflakeConnection
1564
- });
1565
- import { createPrivateKey } from "node:crypto";
1566
- import snowflake from "snowflake-sdk";
1567
- var SnowflakeConnection;
1568
- var init_snowflake = __esm({
1569
- "connections/snowflake.ts"() {
1570
- init_config();
1571
- SnowflakeConnection = class {
1572
- ready;
1573
- connection;
1574
- constructor(opts) {
1575
- this.ready = this.initialize(opts || {});
1576
- }
1577
- async initialize(opts) {
1578
- let privateKeyPath = process.env.SNOWFLAKE_PRI_KEY_PATH || config.snowflake?.privateKeyPath;
1579
- let privateKeyPass = process.env.SNOWFLAKE_PRI_PASSPHRASE;
1580
- let authOptions = {};
1581
- if (privateKeyPath) {
1582
- authOptions = { privateKeyPath, privateKeyPass };
1583
- } else if (opts.privateKey) {
1584
- let privateKey = createPrivateKey({ key: opts.privateKey, format: "pem", passphrase: privateKeyPass });
1585
- authOptions = { privateKey: privateKey.export({ format: "pem", type: "pkcs8" }) };
1762
+ }
1763
+ async function isServerRunning(portOverride) {
1764
+ let port = portOverride || Number(process.env.GRAPHENE_PORT) || 4e3;
1765
+ return !!await getPidOnPort(port);
1766
+ }
1767
+ async function getPidOnPort(port) {
1768
+ try {
1769
+ if (process.platform === "win32") {
1770
+ let { stdout } = await execAsync(`netstat -ano | findstr :${port}`);
1771
+ let lines = stdout.trim().split("\n");
1772
+ for (let line of lines) {
1773
+ let parts = line.trim().split(/\s+/);
1774
+ if (parts.length < 5) continue;
1775
+ let localAddress = parts[1];
1776
+ let pid = parseInt(parts[parts.length - 1], 10);
1777
+ if (localAddress.endsWith(`:${port}`)) {
1778
+ return pid;
1586
1779
  }
1587
- snowflake.configure({ logLevel: process.env.SNOWFLAKE_LOG_LEVEL || "WARN", logFilePath: "/dev/null" });
1588
- this.connection = snowflake.createConnection({
1589
- ...opts,
1590
- ...config.snowflake || {},
1591
- ...authOptions,
1592
- authenticator: "SNOWFLAKE_JWT",
1593
- application: "Graphene"
1594
- });
1595
- await new Promise((resolve, reject) => {
1596
- this.connection.connect((err, conn) => err ? reject(err) : resolve(conn));
1597
- });
1598
1780
  }
1599
- async runQuery(sql) {
1600
- await this.ready;
1601
- return await new Promise((resolve, reject) => {
1602
- let rows = [];
1603
- this.connection.execute({
1604
- sqlText: sql,
1605
- streamResult: true,
1606
- complete: (error, statement) => {
1607
- if (error) {
1608
- reject(new Error(`Snowflake query failed: ${error.message || error}`));
1609
- return;
1610
- }
1611
- let stream = statement.streamRows();
1612
- stream.on("error", (err) => reject(err));
1613
- stream.on("readable", function(row) {
1614
- while ((row = this.read()) !== null) {
1615
- rows.push(row);
1616
- }
1617
- });
1618
- stream.on("end", () => {
1619
- let totalRows = Number(statement.getNumRows());
1620
- resolve({ rows, totalRows });
1621
- });
1622
- }
1623
- });
1781
+ } else {
1782
+ return new Promise((resolve) => {
1783
+ let child = spawn("lsof", ["-i", `:${port}`, "-t", "-sTCP:LISTEN"]);
1784
+ let stdout = "";
1785
+ child.stdout.on("data", (d) => stdout += d.toString());
1786
+ child.on("close", (code) => {
1787
+ if (code !== 0) return resolve(void 0);
1788
+ let pid = parseInt(stdout.trim(), 10);
1789
+ resolve(isNaN(pid) ? void 0 : pid);
1624
1790
  });
1625
- }
1626
- };
1627
- }
1628
- });
1629
-
1630
- // connections/index.ts
1631
- async function getConnection() {
1632
- if (config.dialect === "bigquery") {
1633
- let mod = await Promise.resolve().then(() => (init_bigQuery(), bigQuery_exports));
1634
- return new mod.BigQueryConnection();
1635
- } else if (config.dialect === "duckdb") {
1636
- let mod = await Promise.resolve().then(() => (init_duckdb(), duckdb_exports));
1637
- return new mod.DuckDBConnection({});
1638
- } else if (config.dialect === "snowflake") {
1639
- let mod = await Promise.resolve().then(() => (init_snowflake(), snowflake_exports));
1640
- return new mod.SnowflakeConnection({});
1641
- } else {
1642
- throw new Error(`Unsupported dialect: ${config.dialect}`);
1791
+ child.on("error", () => resolve(void 0));
1792
+ });
1793
+ }
1794
+ } catch (e) {
1795
+ console.warn("Failed to check for server:", e.message);
1796
+ return void 0;
1643
1797
  }
1798
+ return void 0;
1644
1799
  }
1645
- var init_connections = __esm({
1646
- "connections/index.ts"() {
1800
+ var execAsync;
1801
+ var init_background = __esm({
1802
+ "background.ts"() {
1803
+ "use strict";
1647
1804
  init_config();
1805
+ execAsync = promisify(exec);
1648
1806
  }
1649
1807
  });
1650
1808
 
@@ -1652,14 +1810,15 @@ var init_connections = __esm({
1652
1810
  var mockFileMap;
1653
1811
  var init_mockFiles = __esm({
1654
1812
  "mockFiles.ts"() {
1813
+ "use strict";
1655
1814
  mockFileMap = {};
1656
1815
  }
1657
1816
  });
1658
1817
 
1659
1818
  // check.ts
1660
- import fs4 from "fs-extra";
1819
+ import fs3 from "fs-extra";
1661
1820
  import os from "os";
1662
- import path5 from "path";
1821
+ import path4 from "path";
1663
1822
  import { spawn as spawn2 } from "child_process";
1664
1823
  import { WebSocketServer } from "ws";
1665
1824
  import { readFileSync as readFileSync2 } from "node:fs";
@@ -1676,7 +1835,7 @@ async function check(options) {
1676
1835
  if (process.env.NODE_ENV == "test" && mockFileMap[mdFile]) {
1677
1836
  updateFile(mockFileMap[mdFile], mdFile);
1678
1837
  } else {
1679
- let content = readFileSync2(path5.resolve(config.root, mdFile), "utf-8");
1838
+ let content = readFileSync2(path4.resolve(config.root, mdFile), "utf-8");
1680
1839
  updateFile(content, mdFile);
1681
1840
  }
1682
1841
  }
@@ -1731,12 +1890,12 @@ async function check(options) {
1731
1890
  }
1732
1891
  if (resp?.screenshot) {
1733
1892
  let filename = `graphene-screenshot-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.png`;
1734
- let screenshotPath = path5.join(os.tmpdir(), filename);
1893
+ let screenshotPath = path4.join(os.tmpdir(), filename);
1735
1894
  let base64Data = resp.screenshot.replace(/^data:image\/png;base64,/, "");
1736
- await fs4.writeFile(screenshotPath, base64Data, "base64");
1895
+ await fs3.writeFile(screenshotPath, base64Data, "base64");
1737
1896
  log("Screenshot saved to", screenshotPath);
1738
1897
  }
1739
- return !!errors.length;
1898
+ return errors.length == 0;
1740
1899
  }
1741
1900
  async function sendCheckRequest({ host, pageUrl, chart }) {
1742
1901
  let abort = new AbortController();
@@ -1770,11 +1929,11 @@ function normalizeMdFile(mdFile) {
1770
1929
  return clean;
1771
1930
  }
1772
1931
  let absolute = [
1773
- path5.resolve(process.cwd(), clean),
1774
- path5.resolve(config.root, clean)
1775
- ].find((p) => fs4.existsSync(p)) || null;
1932
+ path4.resolve(process.cwd(), clean),
1933
+ path4.resolve(config.root, clean)
1934
+ ].find((p) => fs3.existsSync(p)) || null;
1776
1935
  if (!absolute) return null;
1777
- let relative = path5.relative(config.root, absolute);
1936
+ let relative = path4.relative(config.root, absolute);
1778
1937
  return relative;
1779
1938
  }
1780
1939
  async function proxyCheckRequest(req, res) {
@@ -1833,6 +1992,7 @@ function checkVitePlugin() {
1833
1992
  var browserConnections, pendingRequests;
1834
1993
  var init_check = __esm({
1835
1994
  "check.ts"() {
1995
+ "use strict";
1836
1996
  init_core();
1837
1997
  init_printer();
1838
1998
  init_mockFiles();
@@ -1843,9 +2003,368 @@ var init_check = __esm({
1843
2003
  }
1844
2004
  });
1845
2005
 
1846
- // mdCompile.ts
1847
- import fs5 from "fs";
2006
+ // auth.ts
2007
+ import fs4 from "node:fs/promises";
2008
+ import path5 from "path";
2009
+ import os2 from "os";
2010
+ import { spawn as spawn3 } from "child_process";
2011
+ import http from "http";
2012
+ async function readStore() {
2013
+ try {
2014
+ let txt2 = await fs4.readFile(credsPath, "utf8");
2015
+ return JSON.parse(txt2) || {};
2016
+ } catch {
2017
+ return {};
2018
+ }
2019
+ }
2020
+ async function readEntry() {
2021
+ let store = await readStore();
2022
+ return store[config.root];
2023
+ }
2024
+ async function updateEntry(cred) {
2025
+ let store = await readStore();
2026
+ cred.refresh_token ||= store[config.root]?.refresh_token;
2027
+ cred.expires_at = Date.now() + cred.expires_in;
2028
+ store[config.root] = cred;
2029
+ await fs4.mkdir(path5.dirname(credsPath), { recursive: true, mode: 448 });
2030
+ await fs4.writeFile(credsPath, JSON.stringify(store, null, 2) + "\n", { mode: 384 });
2031
+ if (process.platform !== "win32") {
2032
+ await fs4.chmod(credsPath, 384).catch(() => {
2033
+ });
2034
+ }
2035
+ }
2036
+ function openInBrowser(url) {
2037
+ try {
2038
+ let plat = process.platform;
2039
+ let cmd = "xdg-open";
2040
+ if (plat == "darwin") cmd = "open";
2041
+ if (plat == "win32") cmd = "start";
2042
+ let p = spawn3(cmd, [url], { stdio: "ignore", shell: plat === "win32" });
2043
+ p.unref();
2044
+ } catch {
2045
+ console.log(`Open this URL to authenticate:
2046
+ ${url}`);
2047
+ }
2048
+ }
2049
+ function base64url(buf) {
2050
+ let b64 = Buffer.from(buf).toString("base64");
2051
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
2052
+ }
2053
+ function randomBytes(len = 32) {
2054
+ return crypto.getRandomValues(new Uint8Array(len));
2055
+ }
2056
+ async function startLoopback() {
2057
+ let server = http.createServer();
2058
+ await new Promise((r) => server.listen(0, "127.0.0.1", () => r()));
2059
+ let addr = server.address();
2060
+ if (!addr || typeof addr !== "object") throw new Error("Couldnt start oauth callback server");
2061
+ let redirectBase = `http://localhost:${addr.port}`;
2062
+ let waitForCode = new Promise((resolve) => {
2063
+ server.on("request", (req, res) => {
2064
+ let url = new URL(req.url || "/", redirectBase);
2065
+ if (url.pathname !== "/callback") {
2066
+ res.statusCode = 404;
2067
+ res.end("Not Found");
2068
+ return;
2069
+ }
2070
+ let code = url.searchParams.get("code") || "";
2071
+ let state = url.searchParams.get("state") || "";
2072
+ res.statusCode = 200;
2073
+ res.setHeader("content-type", "text/html; charset=utf-8");
2074
+ res.end("<html><body>Login complete. You may close this window.</body></html>");
2075
+ resolve({ code, state });
2076
+ });
2077
+ });
2078
+ return { url: redirectBase, waitForCode, close: () => server.close() };
2079
+ }
2080
+ async function loginPkce(opener) {
2081
+ let verifier = base64url(randomBytes(48));
2082
+ let data = new TextEncoder().encode(verifier);
2083
+ let digest = await crypto.subtle.digest("SHA-256", data);
2084
+ let code_challenge = base64url(digest);
2085
+ let state = base64url(randomBytes(16));
2086
+ let loop = await startLoopback();
2087
+ let redirect_uri = `${loop.url}/callback`;
2088
+ let authorizeUrl = new URL(`${config.host}/authenticate`);
2089
+ authorizeUrl.search = new URLSearchParams({
2090
+ redirect_uri,
2091
+ code_challenge,
2092
+ state,
2093
+ client_id: AUTH_CLIENT_ID,
2094
+ response_type: "code",
2095
+ code_challenge_method: "S256",
2096
+ scope: AUTH_SCOPES
2097
+ }).toString();
2098
+ if (opener) await opener(authorizeUrl.toString());
2099
+ else openInBrowser(authorizeUrl.toString());
2100
+ let result = await loop.waitForCode;
2101
+ if (!result.code) throw new Error("No authorization code received");
2102
+ if (result.state !== state) throw new Error("State mismatch");
2103
+ let res = await fetch(`${config.host}/_api/oauth2/token`, {
2104
+ method: "POST",
2105
+ headers: { "content-type": "application/json" },
2106
+ body: JSON.stringify({
2107
+ grant_type: "authorization_code",
2108
+ code: result.code,
2109
+ redirect_uri,
2110
+ client_id: AUTH_CLIENT_ID,
2111
+ code_verifier: verifier
2112
+ })
2113
+ });
2114
+ if (!res.ok) throw new Error(`token exchange failed: ${res.status}`);
2115
+ let tokenResp = await res.json();
2116
+ await updateEntry(tokenResp);
2117
+ }
2118
+ async function refreshAccessToken() {
2119
+ let refresh_token = (await readEntry())?.refresh_token;
2120
+ let res = await fetch(new URL("/_api/oauth2/token", config.host).toString(), {
2121
+ method: "POST",
2122
+ headers: { "content-type": "application/json" },
2123
+ body: JSON.stringify({ grant_type: "refresh_token", refresh_token, client_id: AUTH_CLIENT_ID })
2124
+ });
2125
+ if (!res.ok) throw new Error(`refresh failed: ${res.status}`);
2126
+ let json = await res.json();
2127
+ await updateEntry(json);
2128
+ }
2129
+ async function authenticatedFetch(pathOrUrl, init = {}) {
2130
+ let entry = await readEntry();
2131
+ if (!entry) throw new Error("Not logged in; run `graphene login`");
2132
+ if (!entry.access_token || entry.expires_at < Date.now()) {
2133
+ await refreshAccessToken();
2134
+ entry = await readEntry();
2135
+ }
2136
+ let token = entry?.access_token;
2137
+ if (!token) throw new Error("Failed to obtain access token");
2138
+ let url = new URL(pathOrUrl, config.host);
2139
+ let headers = new Headers(init.headers || {});
2140
+ headers.set("authorization", `Bearer ${token}`);
2141
+ let res = await fetch(url.toString(), { ...init, headers });
2142
+ if (res.status === 401 || res.status === 403) {
2143
+ await refreshAccessToken();
2144
+ token = (await readEntry())?.access_token;
2145
+ if (token) {
2146
+ headers.set("cookie", `access_token=${token}`);
2147
+ res = await fetch(url.toString(), { ...init, headers });
2148
+ }
2149
+ }
2150
+ return res;
2151
+ }
2152
+ var AUTH_CLIENT_ID, AUTH_SCOPES, cfgDir, credsPath;
2153
+ var init_auth = __esm({
2154
+ "auth.ts"() {
2155
+ "use strict";
2156
+ init_config();
2157
+ AUTH_CLIENT_ID = "connected-app-test-4685a2a0-1cb9-4a81-a6bf-01a3efe7b981";
2158
+ AUTH_SCOPES = "offline_access";
2159
+ cfgDir = process.env.XDG_CONFIG_HOME || path5.join(os2.homedir(), ".config");
2160
+ credsPath = path5.join(cfgDir, "graphene", "credentials.json");
2161
+ }
2162
+ });
2163
+
2164
+ // connections/bigQuery.ts
2165
+ var bigQuery_exports = {};
2166
+ __export(bigQuery_exports, {
2167
+ BigQueryConnection: () => BigQueryConnection
2168
+ });
2169
+ import { BigQuery, BigQueryDate, BigQueryTimestamp } from "@google-cloud/bigquery";
2170
+ var BigQueryConnection;
2171
+ var init_bigQuery = __esm({
2172
+ "connections/bigQuery.ts"() {
2173
+ "use strict";
2174
+ init_config();
2175
+ BigQueryConnection = class {
2176
+ client;
2177
+ constructor(options = {}) {
2178
+ options.projectId ||= config.bigquery?.projectId;
2179
+ if (process.env.GOOGLE_CREDENTIALS_CONTENT) {
2180
+ let parsed = JSON.parse(process.env.GOOGLE_CREDENTIALS_CONTENT);
2181
+ options.projectId = parsed.project_id;
2182
+ options.credentials = parsed;
2183
+ }
2184
+ if (!options.projectId) throw new Error("projectId must be set in config or provided in service account credentials");
2185
+ this.client = new BigQuery({ ...options, userAgent: "Graphene" });
2186
+ }
2187
+ async runQuery(sql) {
2188
+ let [job] = await this.client.createQueryJob({ query: sql, useLegacySql: false });
2189
+ let [rows] = await job.getQueryResults({ maxResults: 1e4 });
2190
+ let metadata = job.metadata || (await job.getMetadata())[0];
2191
+ let totalRows = Number(metadata?.statistics?.query?.totalRows ?? rows.length);
2192
+ rows.forEach((r) => {
2193
+ Object.entries(r).forEach(([k, v]) => {
2194
+ if (v instanceof BigQueryTimestamp) r[k] = v.value;
2195
+ if (v instanceof BigQueryDate) r[k] = v.value;
2196
+ });
2197
+ });
2198
+ return { rows, totalRows };
2199
+ }
2200
+ };
2201
+ }
2202
+ });
2203
+
2204
+ // connections/duckdb.ts
2205
+ var duckdb_exports = {};
2206
+ __export(duckdb_exports, {
2207
+ DuckDBConnection: () => DuckDBConnection
2208
+ });
2209
+ import { promises as fs5 } from "fs";
1848
2210
  import path6 from "path";
2211
+ import { DuckDBTimestampValue, DuckDBInstance, DuckDBDateValue } from "@duckdb/node-api";
2212
+ var DuckDBConnection;
2213
+ var init_duckdb = __esm({
2214
+ "connections/duckdb.ts"() {
2215
+ "use strict";
2216
+ init_config();
2217
+ DuckDBConnection = class {
2218
+ options;
2219
+ ready;
2220
+ connection = null;
2221
+ constructor(options) {
2222
+ this.options = options || {};
2223
+ this.ready = this.initialize();
2224
+ }
2225
+ async initialize() {
2226
+ let dbPath = this.options.path;
2227
+ if (!dbPath) {
2228
+ let files = await fs5.readdir(config.root);
2229
+ dbPath = files.find((f) => f.endsWith(".duckdb"));
2230
+ if (!dbPath) throw new Error("No .duckdb file found in current directory");
2231
+ dbPath = path6.resolve(config.root, dbPath);
2232
+ }
2233
+ let db = await DuckDBInstance.create(":memory:");
2234
+ this.connection = await db.connect();
2235
+ let escapedPath = dbPath.replace(/'/g, "''");
2236
+ await this.connection.run(`attach '${escapedPath}' as graphene_cli (READ_ONLY);`);
2237
+ await this.connection.run("use graphene_cli;");
2238
+ }
2239
+ async runQuery(sql) {
2240
+ await this.ready;
2241
+ let reader = await this.connection.runAndReadAll(sql);
2242
+ let rows = reader.getRowObjects().map((record) => {
2243
+ let out = {};
2244
+ for (let [k, v] of Object.entries(record)) {
2245
+ if (typeof v === "bigint") out[k] = Number(v);
2246
+ else if (v === null) out[k] = null;
2247
+ else if (v instanceof DuckDBTimestampValue) out[k] = new Date(Number(v.micros / 1000n)).toUTCString();
2248
+ else if (v instanceof DuckDBDateValue) out[k] = v.toString();
2249
+ else if (typeof v === "object") throw new Error(`Unsupported datatype ${v.constructor?.name}`);
2250
+ else out[k] = v;
2251
+ }
2252
+ return out;
2253
+ });
2254
+ return { rows };
2255
+ }
2256
+ };
2257
+ }
2258
+ });
2259
+
2260
+ // connections/snowflake.ts
2261
+ var snowflake_exports = {};
2262
+ __export(snowflake_exports, {
2263
+ SnowflakeConnection: () => SnowflakeConnection
2264
+ });
2265
+ import { createPrivateKey } from "node:crypto";
2266
+ import snowflake from "snowflake-sdk";
2267
+ var SnowflakeConnection;
2268
+ var init_snowflake2 = __esm({
2269
+ "connections/snowflake.ts"() {
2270
+ "use strict";
2271
+ init_config();
2272
+ SnowflakeConnection = class {
2273
+ ready;
2274
+ connection;
2275
+ constructor(opts) {
2276
+ this.ready = this.initialize(opts || {});
2277
+ }
2278
+ async initialize(opts) {
2279
+ let privateKeyPath = process.env.SNOWFLAKE_PRI_KEY_PATH || config.snowflake?.privateKeyPath;
2280
+ let privateKeyPass = process.env.SNOWFLAKE_PRI_PASSPHRASE;
2281
+ let authOptions = {};
2282
+ if (privateKeyPath) {
2283
+ authOptions = { privateKeyPath, privateKeyPass };
2284
+ } else if (opts.privateKey) {
2285
+ let privateKey = createPrivateKey({ key: opts.privateKey, format: "pem", passphrase: privateKeyPass });
2286
+ authOptions = { privateKey: privateKey.export({ format: "pem", type: "pkcs8" }) };
2287
+ }
2288
+ snowflake.configure({ logLevel: process.env.SNOWFLAKE_LOG_LEVEL || "WARN", logFilePath: "/dev/null" });
2289
+ this.connection = snowflake.createConnection({
2290
+ ...opts,
2291
+ ...config.snowflake || {},
2292
+ ...authOptions,
2293
+ authenticator: "SNOWFLAKE_JWT",
2294
+ application: "Graphene"
2295
+ });
2296
+ await new Promise((resolve, reject) => {
2297
+ this.connection.connect((err, conn) => err ? reject(err) : resolve(conn));
2298
+ });
2299
+ }
2300
+ async runQuery(sql) {
2301
+ await this.ready;
2302
+ return await new Promise((resolve, reject) => {
2303
+ let rows = [];
2304
+ this.connection.execute({
2305
+ sqlText: sql,
2306
+ streamResult: true,
2307
+ complete: (error, statement) => {
2308
+ if (error) {
2309
+ reject(new Error(`Snowflake query failed: ${error.message || error}`));
2310
+ return;
2311
+ }
2312
+ let stream = statement.streamRows();
2313
+ stream.on("error", (err) => reject(err));
2314
+ stream.on("readable", function(row) {
2315
+ while ((row = this.read()) !== null) {
2316
+ rows.push(row);
2317
+ }
2318
+ });
2319
+ stream.on("end", () => {
2320
+ let totalRows = Number(statement.getNumRows());
2321
+ resolve({ rows, totalRows });
2322
+ });
2323
+ }
2324
+ });
2325
+ });
2326
+ }
2327
+ };
2328
+ }
2329
+ });
2330
+
2331
+ // connections/index.ts
2332
+ async function runQuery(sql) {
2333
+ if (config.host) {
2334
+ let resp = await authenticatedFetch("/_api/query", {
2335
+ method: "POST",
2336
+ headers: { "Content-Type": "application/json" },
2337
+ body: JSON.stringify({ sql })
2338
+ });
2339
+ return await resp.json();
2340
+ }
2341
+ if (config.dialect === "bigquery") {
2342
+ let mod = await Promise.resolve().then(() => (init_bigQuery(), bigQuery_exports));
2343
+ let conn = new mod.BigQueryConnection();
2344
+ return await conn.runQuery(sql);
2345
+ } else if (config.dialect === "duckdb") {
2346
+ let mod = await Promise.resolve().then(() => (init_duckdb(), duckdb_exports));
2347
+ let conn = new mod.DuckDBConnection({});
2348
+ return await conn.runQuery(sql);
2349
+ } else if (config.dialect === "snowflake") {
2350
+ let mod = await Promise.resolve().then(() => (init_snowflake2(), snowflake_exports));
2351
+ let conn = new mod.SnowflakeConnection({});
2352
+ return await conn.runQuery(sql);
2353
+ } else {
2354
+ throw new Error(`Unsupported dialect: ${config.dialect}`);
2355
+ }
2356
+ }
2357
+ var init_connections = __esm({
2358
+ "connections/index.ts"() {
2359
+ "use strict";
2360
+ init_config();
2361
+ init_auth();
2362
+ }
2363
+ });
2364
+
2365
+ // mdCompile.ts
2366
+ import fs6 from "fs";
2367
+ import path7 from "path";
1849
2368
  import { visit } from "unist-util-visit";
1850
2369
  import sanitizeHtml from "sanitize-html";
1851
2370
  function extractQueries() {
@@ -1922,13 +2441,14 @@ ${content}`;
1922
2441
  }
1923
2442
  function componentNames() {
1924
2443
  if (cachedComponentNames) return cachedComponentNames;
1925
- let files = fs5.readdirSync(path6.join(import.meta.dirname, "../ui/components"));
1926
- cachedComponentNames = files.map((f) => path6.basename(f, ".svelte")).filter((f) => !f.startsWith("_"));
2444
+ let files = fs6.readdirSync(path7.join(import.meta.dirname, "../ui/components"));
2445
+ cachedComponentNames = files.map((f) => path7.basename(f, ".svelte")).filter((f) => !f.startsWith("_"));
1927
2446
  return cachedComponentNames || [];
1928
2447
  }
1929
2448
  var cachedComponentNames;
1930
2449
  var init_mdCompile = __esm({
1931
2450
  "mdCompile.ts"() {
2451
+ "use strict";
1932
2452
  cachedComponentNames = null;
1933
2453
  }
1934
2454
  });
@@ -1940,16 +2460,15 @@ __export(serve2_exports, {
1940
2460
  });
1941
2461
  import { createServer, optimizeDeps } from "vite";
1942
2462
  import { svelte, vitePreprocess } from "@sveltejs/vite-plugin-svelte";
1943
- import fs6 from "fs-extra";
1944
- import crypto from "crypto";
2463
+ import fs7 from "fs-extra";
2464
+ import crypto2 from "crypto";
1945
2465
  import { mdsvex } from "mdsvex";
1946
- import path7 from "path";
2466
+ import path8 from "path";
1947
2467
  import { fileURLToPath as fileURLToPath2 } from "url";
1948
2468
  async function serve2() {
1949
- uiRoot = path7.join(fileURLToPath2(import.meta.url), "../../ui");
2469
+ uiRoot = path8.join(fileURLToPath2(import.meta.url), "../../ui");
1950
2470
  let port = Number(process.env.GRAPHENE_PORT) || 4e3;
1951
- await fs6.ensureDir(path7.resolve(config.root, "node_modules/.graphene"));
1952
- await fs6.writeFile(path7.resolve(config.root, `node_modules/.graphene/${process.env.NODE_ENV == "test" ? "test" : "serve"}.pid`), String(process.pid));
2471
+ await fs7.ensureDir(path8.resolve(config.root, "node_modules/.graphene"));
1953
2472
  let server = await createServer({
1954
2473
  root: config.root,
1955
2474
  plugins: [
@@ -1970,7 +2489,7 @@ async function serve2() {
1970
2489
  updateWorkspacePlugin,
1971
2490
  mockFilesForTests()
1972
2491
  ],
1973
- publicDir: path7.resolve(uiRoot),
2492
+ publicDir: path8.resolve(uiRoot),
1974
2493
  server: {
1975
2494
  port,
1976
2495
  fs: { strict: false },
@@ -1978,7 +2497,7 @@ async function serve2() {
1978
2497
  },
1979
2498
  resolve: {
1980
2499
  alias: {
1981
- graphene: path7.resolve(uiRoot, "web.js")
2500
+ graphene: path8.resolve(uiRoot, "web.js")
1982
2501
  }
1983
2502
  }
1984
2503
  // optimizeDeps: { // this seems prudent in tests, but currently breaks because ssf needs to be optimized, even in tests
@@ -1988,9 +2507,7 @@ async function serve2() {
1988
2507
  });
1989
2508
  await optimizeDeps(server.config);
1990
2509
  await server.listen();
1991
- if (process.env.NODE_ENV !== "test") {
1992
- console.log(`Server running at http://localhost:${port}`);
1993
- }
2510
+ console.log(`Server running at http://localhost:${port}`);
1994
2511
  return server;
1995
2512
  }
1996
2513
  async function handleQuery(req, res) {
@@ -2007,14 +2524,13 @@ async function handleQuery(req, res) {
2007
2524
  }
2008
2525
  if (queries.length > 1) throw new Error("Found multiple queries, which could be a parsing error");
2009
2526
  let sql = toSql(queries[0], params);
2010
- let hash = crypto.createHash("SHA1").update(sql).digest("hex");
2527
+ let hash = crypto2.createHash("SHA1").update(sql).digest("hex");
2011
2528
  res.setHeader("ETag", hash);
2012
2529
  if (hashes.includes(hash) && req.headers["cache-control"] != "no-cache") {
2013
2530
  res.statusCode = 304;
2014
2531
  return res.end();
2015
2532
  }
2016
- let connection = await getConnection();
2017
- let queryResults = await connection.runQuery(sql);
2533
+ let queryResults = await runQuery(sql);
2018
2534
  let totalRows = queryResults.totalRows ?? queryResults.rows.length;
2019
2535
  if (totalRows > queryResults.rows.length) throw new Error("Query returns too many rows");
2020
2536
  let fields = queries[0].fields.map((f) => ({ name: f.name, type: f.type }));
@@ -2066,6 +2582,7 @@ function mockFilesForTests() {
2066
2582
  var uiRoot, workspaceLoadPromise, updateWorkspacePlugin, handleRequestPlugin;
2067
2583
  var init_serve2 = __esm({
2068
2584
  "serve2.ts"() {
2585
+ "use strict";
2069
2586
  init_core();
2070
2587
  init_connections();
2071
2588
  init_mdCompile();
@@ -2091,8 +2608,8 @@ var init_serve2 = __esm({
2091
2608
  if (pathName == "/_api/query") return await handleQuery(req, res);
2092
2609
  if (pathName == "/__ct") return await handlePage(s, res, "__ct", false);
2093
2610
  if (!pathName || pathName == "/") pathName = "index";
2094
- let mdPath = path7.join(config.root, pathName + ".md");
2095
- if (await fs6.exists(mdPath)) {
2611
+ let mdPath = path8.join(config.root, pathName + ".md");
2612
+ if (await fs7.exists(mdPath)) {
2096
2613
  await handlePage(s, res, mdPath, true);
2097
2614
  } else {
2098
2615
  next();
@@ -2113,11 +2630,12 @@ init_printer();
2113
2630
  init_core();
2114
2631
  init_config();
2115
2632
  init_background();
2116
- init_connections();
2117
2633
  init_check();
2634
+ init_connections();
2635
+ init_auth();
2118
2636
  import { Command } from "commander";
2119
- import fs7 from "fs-extra";
2120
- import path8 from "path";
2637
+ import fs8 from "fs-extra";
2638
+ import path9 from "path";
2121
2639
  var program = new Command();
2122
2640
  program.name("graphene").description("Graphene CLI").version("1.0.0");
2123
2641
  program.hook("preAction", async () => {
@@ -2139,26 +2657,31 @@ program.command("run").description("Run a query against your database").argument
2139
2657
  let queries = analyze(gsql);
2140
2658
  if (!validQuery(queries)) return;
2141
2659
  let sql = toSql(queries[0]);
2142
- let connection = await getConnection();
2143
- let res = await connection.runQuery(sql);
2660
+ let res = await runQuery(sql);
2144
2661
  printTable(res.rows);
2145
2662
  });
2146
- program.command("serve").description("Run the local server").option("--fg", "Run the server in the foreground").action(async (options) => {
2147
- if (options.fg || process.env.DEBUG) {
2148
- let mod = await Promise.resolve().then(() => (init_serve2(), serve2_exports));
2149
- await mod.serve2();
2150
- } else {
2663
+ program.command("serve").description("Run the local server").option("--bg", "Run the server in the background").action(async (options) => {
2664
+ await stopGrapheneIfRunning();
2665
+ if (options.bg) {
2151
2666
  await runServeInBackground();
2152
2667
  process.exit(0);
2668
+ } else {
2669
+ let mod = await Promise.resolve().then(() => (init_serve2(), serve2_exports));
2670
+ await mod.serve2();
2153
2671
  }
2154
2672
  });
2155
2673
  program.command("stop").description("Stop the local server").action(async () => {
2156
- await stopGrapheneIfRunning(process.cwd());
2674
+ await stopGrapheneIfRunning();
2157
2675
  });
2158
2676
  program.command("check").description("Check the project for errors, optionally capturing a page screenshot").argument("[mdFile]", "Markdown file to check (e.g., index.md)").option("-c, --chart <chartTitle>", "Title of a specific chart to capture").action(async (mdArg, options) => {
2159
2677
  let res = await check({ mdArg, chart: options.chart });
2160
2678
  process.exit(res ? 0 : 1);
2161
2679
  });
2680
+ program.command("login").description("Log in to Graphene Cloud").action(async () => {
2681
+ await loginPkce();
2682
+ console.log("Successfully logged in");
2683
+ process.exit(0);
2684
+ });
2162
2685
  program.parse(process.argv);
2163
2686
  async function readInput(arg) {
2164
2687
  if (!arg || arg === "-") {
@@ -2170,9 +2693,9 @@ async function readInput(arg) {
2170
2693
  process.stdin.resume();
2171
2694
  });
2172
2695
  }
2173
- let absolutePath = path8.resolve(arg);
2174
- if (fs7.existsSync(absolutePath)) {
2175
- return await fs7.promises.readFile(absolutePath, "utf-8");
2696
+ let absolutePath = path9.resolve(arg);
2697
+ if (fs8.existsSync(absolutePath)) {
2698
+ return await fs8.promises.readFile(absolutePath, "utf-8");
2176
2699
  }
2177
2700
  return arg;
2178
2701
  }