@graphenedata/cli 0.0.4 → 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.
Files changed (74) hide show
  1. package/cli.ts +25 -55
  2. package/dist/cli/cli.js +1194 -435
  3. package/dist/docs/graphene.md +1074 -166
  4. package/dist/ui/component-utilities/echarts.js +3 -1
  5. package/dist/ui/component-utilities/inputUtils.ts +11 -0
  6. package/dist/ui/component-utilities/themeStores.ts +35 -7
  7. package/dist/ui/components/Area.svelte +6 -3
  8. package/dist/ui/components/AreaChart.svelte +3 -1
  9. package/dist/ui/components/Bar.svelte +14 -8
  10. package/dist/ui/components/BarChart.svelte +3 -1
  11. package/dist/ui/components/BigValue.svelte +1 -1
  12. package/dist/ui/components/Chart.svelte +57 -101
  13. package/dist/ui/components/Column.svelte +2 -0
  14. package/dist/ui/components/ECharts.svelte +2 -0
  15. package/dist/ui/components/Line.svelte +8 -5
  16. package/dist/ui/components/LineChart.svelte +3 -2
  17. package/dist/ui/components/PieChart.svelte +1 -1
  18. package/dist/ui/components/QueryLoad.svelte +5 -6
  19. package/dist/ui/components/TableRow.svelte +1 -1
  20. package/dist/ui/components/_Table.svelte +2 -0
  21. package/dist/ui/internal/queryEngine.ts +39 -15
  22. package/dist/ui/internal/telemetry.ts +5 -3
  23. package/dist/ui/web.js +28 -12
  24. package/package.json +3 -2
  25. package/dist/docs/data_apps/components/charts/annotations.md +0 -673
  26. package/dist/docs/data_apps/components/charts/area-chart.md +0 -202
  27. package/dist/docs/data_apps/components/charts/bar-chart.md +0 -317
  28. package/dist/docs/data_apps/components/charts/box-plot.md +0 -190
  29. package/dist/docs/data_apps/components/charts/bubble-chart.md +0 -151
  30. package/dist/docs/data_apps/components/charts/calendar-heatmap.md +0 -112
  31. package/dist/docs/data_apps/components/charts/custom-echarts.md +0 -308
  32. package/dist/docs/data_apps/components/charts/echarts-options.md +0 -217
  33. package/dist/docs/data_apps/components/charts/funnel-chart.md +0 -106
  34. package/dist/docs/data_apps/components/charts/heatmap.md +0 -180
  35. package/dist/docs/data_apps/components/charts/histogram.md +0 -107
  36. package/dist/docs/data_apps/components/charts/line-chart.md +0 -265
  37. package/dist/docs/data_apps/components/charts/mixed-type-charts.md +0 -240
  38. package/dist/docs/data_apps/components/charts/sankey-diagram.md +0 -301
  39. package/dist/docs/data_apps/components/charts/scatter-plot.md +0 -134
  40. package/dist/docs/data_apps/components/charts/sparkline.md +0 -68
  41. package/dist/docs/data_apps/components/data/big-value.md +0 -153
  42. package/dist/docs/data_apps/components/data/delta.md +0 -89
  43. package/dist/docs/data_apps/components/data/table.md +0 -470
  44. package/dist/docs/data_apps/components/data/value.md +0 -97
  45. package/dist/docs/data_apps/components/inputs/button-group.md +0 -154
  46. package/dist/docs/data_apps/components/inputs/checkbox.md +0 -52
  47. package/dist/docs/data_apps/components/inputs/date-input.md +0 -131
  48. package/dist/docs/data_apps/components/inputs/date-range.md +0 -124
  49. package/dist/docs/data_apps/components/inputs/dimension-grid.md +0 -67
  50. package/dist/docs/data_apps/components/inputs/dropdown.md +0 -199
  51. package/dist/docs/data_apps/components/inputs/index.md +0 -3
  52. package/dist/docs/data_apps/components/inputs/slider.md +0 -126
  53. package/dist/docs/data_apps/components/inputs/text-input.md +0 -86
  54. package/dist/docs/data_apps/components/maps/area-map.md +0 -397
  55. package/dist/docs/data_apps/components/maps/base-map.md +0 -269
  56. package/dist/docs/data_apps/components/maps/bubble-map.md +0 -361
  57. package/dist/docs/data_apps/components/maps/point-map.md +0 -326
  58. package/dist/docs/data_apps/components/maps/us-map.md +0 -167
  59. package/dist/docs/data_apps/components/ui/accordion.md +0 -116
  60. package/dist/docs/data_apps/components/ui/alert.md +0 -37
  61. package/dist/docs/data_apps/components/ui/big-link.md +0 -19
  62. package/dist/docs/data_apps/components/ui/details.md +0 -58
  63. package/dist/docs/data_apps/components/ui/download-data.md +0 -41
  64. package/dist/docs/data_apps/components/ui/embed.md +0 -47
  65. package/dist/docs/data_apps/components/ui/grid.md +0 -45
  66. package/dist/docs/data_apps/components/ui/image.md +0 -61
  67. package/dist/docs/data_apps/components/ui/info.md +0 -47
  68. package/dist/docs/data_apps/components/ui/last-refreshed.md +0 -28
  69. package/dist/docs/data_apps/components/ui/link-button.md +0 -20
  70. package/dist/docs/data_apps/components/ui/link.md +0 -40
  71. package/dist/docs/data_apps/components/ui/modal.md +0 -57
  72. package/dist/docs/data_apps/components/ui/note.md +0 -32
  73. package/dist/docs/data_apps/components/ui/print-format-components.md +0 -85
  74. package/dist/docs/data_apps/components/ui/tabs.md +0 -122
package/dist/cli/cli.js CHANGED
@@ -58,8 +58,18 @@ function walkExpression(root, fn, parent = null) {
58
58
  });
59
59
  }
60
60
  }
61
+ async function pollFor(fn, timeoutMs, interval) {
62
+ let end = Date.now() + timeoutMs;
63
+ while (Date.now() < end) {
64
+ let res = fn();
65
+ if (res) return res;
66
+ await new Promise((r) => setTimeout(r, interval));
67
+ }
68
+ return null;
69
+ }
61
70
  var init_util = __esm({
62
71
  "../lang/util.ts"() {
72
+ "use strict";
63
73
  }
64
74
  });
65
75
 
@@ -126,6 +136,7 @@ function extractLeadingMetadata(node) {
126
136
  }
127
137
  var init_metadata = __esm({
128
138
  "../lang/metadata.ts"() {
139
+ "use strict";
129
140
  init_util();
130
141
  }
131
142
  });
@@ -134,8 +145,12 @@ var init_metadata = __esm({
134
145
  import * as fs from "fs";
135
146
  import path from "path";
136
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";
137
152
  Object.keys(config).forEach((key) => delete config[key]);
138
- Object.assign(config, cfg);
153
+ Object.assign(config, cfg, { dialect });
139
154
  }
140
155
  function loadConfig(dir) {
141
156
  if (config.root) return;
@@ -147,17 +162,13 @@ function loadConfig(dir) {
147
162
  } catch {
148
163
  console.warn("No package.json found in current directory");
149
164
  }
150
- setConfig({
151
- ...packageJsonObject,
152
- dialect: packageJsonObject.dialect || "duckdb",
153
- port: process.env.GRAPHENE_PORT || packageJsonObject.port || 4e3,
154
- root: packageJsonObject.root || process.cwd()
155
- });
165
+ setConfig({ ...packageJsonObject, root: packageJsonObject.root || process.cwd() });
156
166
  }
157
167
  var config;
158
168
  var init_config = __esm({
159
169
  "../lang/config.ts"() {
160
- config = { dialect: "duckdb" };
170
+ "use strict";
171
+ config = { dialect: "duckdb", root: "" };
161
172
  }
162
173
  });
163
174
 
@@ -174,6 +185,7 @@ function findOverloads(name, dialect) {
174
185
  var globalNamespace, dialectNamespaces, BIGQUERY_DIALECT_FUNCTIONS;
175
186
  var init_functions = __esm({
176
187
  "../lang/functions.ts"() {
188
+ "use strict";
177
189
  globalNamespace = new GlobalNameSpace();
178
190
  dialectNamespaces = /* @__PURE__ */ new Map();
179
191
  Object.assign(DUCKDB_DIALECT_FUNCTIONS, {
@@ -303,6 +315,138 @@ var init_functions = __esm({
303
315
  }
304
316
  });
305
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
+
306
450
  // ../lang/params.ts
307
451
  function inferParamTypes(query) {
308
452
  let parentMap = /* @__PURE__ */ new WeakMap();
@@ -409,8 +553,7 @@ function sanitizeType(value) {
409
553
  return value;
410
554
  }
411
555
  function fillInParams(query, params) {
412
- let q = structuredClone(query);
413
- let filters = q.pipeline[0].filterList || [];
556
+ let filters = query.pipeline[0].filterList || [];
414
557
  for (let filter of filters) {
415
558
  walkExpression(filter.e, (e) => {
416
559
  if (e.node !== "parameter") return;
@@ -419,20 +562,31 @@ function fillInParams(query, params) {
419
562
  else if (value == null) Object.assign(e, { node: "null", type: "string" });
420
563
  else if (e.type == "string") Object.assign(e, { node: "stringLiteral", literal: value });
421
564
  else if (e.type == "number") Object.assign(e, { node: "numberLiteral", literal: value.toString() });
422
- 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" });
423
576
  else throw new Error(`Unsupported param type ${e.type}`);
424
577
  });
425
578
  }
426
- return q;
427
579
  }
428
580
  var COMPARISON_OPS, BOOLEAN_OPS, NUMERIC_UNARY_OPS, FIELD_TYPES;
429
581
  var init_params = __esm({
430
582
  "../lang/params.ts"() {
583
+ "use strict";
431
584
  init_util();
585
+ init_temporalLiterals();
432
586
  COMPARISON_OPS = /* @__PURE__ */ new Set(["=", "!=", "<>", ">", ">=", "<", "<=", "like", "ilike"]);
433
587
  BOOLEAN_OPS = /* @__PURE__ */ new Set(["and", "or"]);
434
588
  NUMERIC_UNARY_OPS = /* @__PURE__ */ new Set(["unary-"]);
435
- 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"]);
436
590
  }
437
591
  });
438
592
 
@@ -450,34 +604,9 @@ function findTables(fi) {
450
604
  }
451
605
  let table2 = makeTable(name, syntaxNode.getChild("QueryStatement") ? "query_source" : "table");
452
606
  table2.metadata = extractLeadingMetadata(syntaxNode);
453
- for (let cn of syntaxNode.getChildren("ColumnDef")) {
454
- let name2 = txt(cn.getChild("Identifier"));
455
- if (cn.getChild("PrimaryKey")) {
456
- if (table2.primaryKey) diag(cn, `Table ${table2.name} has multiple primary keys`);
457
- table2.primaryKey = name2;
458
- }
459
- let type = convertDataType(txt(cn.getChild("DataType")));
460
- if (!type) diag(cn, `Unsupported data type: ${txt(cn.getChild("DataType"))}`);
461
- let field = { name: name2, type, metadata: extractLeadingMetadata(cn) };
462
- table2.fields.push(field);
463
- FIELD_NODE_MAP.set(field, cn);
464
- }
465
- for (let jn of syntaxNode.getChildren("JoinDef")) {
466
- let nameNode = jn.getChild("Alias") || jn.getChild("Identifier");
467
- let field = { name: txt(nameNode) };
468
- table2.fields.push(field);
469
- FIELD_NODE_MAP.set(field, jn);
470
- }
471
- for (let cn of syntaxNode.getChildren("ComputedDef")) {
472
- let field = { name: txt(cn.getChild("Alias")), metadata: extractLeadingMetadata(cn) };
473
- table2.fields.push(field);
474
- FIELD_NODE_MAP.set(field, cn);
475
- }
476
- table2.fields.reduce((set, f) => {
477
- if (!set[f.name]) set[f.name] = true;
478
- else diag(FIELD_NODE_MAP.get(f), `Table already has a field called "${f.name}"`);
479
- return set;
480
- }, {});
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));
481
610
  TABLE_NODE_MAP.set(table2, syntaxNode);
482
611
  fi.tables.push(table2);
483
612
  }
@@ -486,12 +615,56 @@ function makeTable(name, type) {
486
615
  let tablePath = config.namespace ? `${config.namespace}.${name}` : name;
487
616
  return { name, type, fields: [], connection: config.dialect, dialect: config.dialect, tableName: name, tablePath, metadata: {} };
488
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
+ }
489
657
  function analyzeTable(table2) {
490
- if (table2.type == "query_source") return analyzeQueryTable(table2);
491
- for (let f of table2.fields) {
492
- if (f.type) continue;
493
- 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;
494
666
  }
667
+ table2.fields.map((f) => analyzeField(f, table2));
495
668
  }
496
669
  function analyzeField(field, table2) {
497
670
  if (field.type) return;
@@ -504,8 +677,9 @@ function analyzeField(field, table2) {
504
677
  field = field;
505
678
  let target = lookupTable(txt(node.getChild("Identifier")), node);
506
679
  if (!target) return diag(node, "Unknown table to join");
507
- if (target.type == "query_source") analyzeQueryTable(target);
508
- 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];
509
683
  if (!jt) return diag(node, "Unknown join type");
510
684
  Object.assign(field, target, { name: field.name, join: jt });
511
685
  field.onExpression = analyzeExpression(node.getChild("Expression"), { table: table2, outputFields: [] });
@@ -517,37 +691,29 @@ function analyzeField(field, table2) {
517
691
  }
518
692
  analysisQueue.delete(field);
519
693
  }
520
- function analyzeQueryTable(table2) {
521
- if (table2.query) return;
522
- let node = TABLE_NODE_MAP.get(table2);
523
- let query = analyzeQuery(node.getChild("QueryStatement"));
524
- if (!query) throw new Error("Couldnt find query in QueryStatement");
525
- table2.fields = query.fields.map((f) => ({ type: f.type, name: f.name, metadata: f.metadata }));
526
- table2.query = query.malloyQuery;
527
- if (typeof table2.query.structRef == "string") {
528
- table2.query.structRef = lookupTable(table2.query.structRef, node);
529
- }
530
- }
531
694
  function analyzeQuery(queryNode) {
532
- let structRef;
695
+ let baseTableName;
533
696
  let scope = { table: null, outputFields: [] };
534
697
  let isAgg = false;
535
- let subQuerySources = [];
536
698
  if (!txt(queryNode)) return;
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
+ }
537
703
  let froms = queryNode.getChild("FromClause")?.getChildren("TablePrimary") || [];
538
704
  if (froms.find((f) => f.name == "JoinClause")) diag(froms[0], "Query joins not yet supported");
539
- if (froms.length == 0) diag(queryNode, "No tables in FROM clause");
705
+ if (froms.length == 0) return diag(queryNode, "No tables in FROM clause");
540
706
  if (froms.length > 1) diag(froms[0], "Multiple tables/joins in FROM clause not yet supported");
541
707
  if (froms[0].name == "Subquery") {
542
- structRef = txt(froms[0].getChild("Alias")) || "subquery";
543
- 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");
544
711
  TABLE_NODE_MAP.set(scope.table, froms[0].getChild("SubqueryExpression"));
545
- analyzeQueryTable(scope.table);
546
- subQuerySources.push(scope.table);
712
+ analyzeTable(scope.table);
547
713
  } else {
548
- structRef = txt(froms[0].getChild("Identifier"));
549
- scope.table = lookupTable(structRef, froms[0]);
550
- 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}"`);
551
717
  NODE_ENTITY_MAP.set(froms[0], { entityType: "table", table: scope.table });
552
718
  }
553
719
  let selects = queryNode.getChild("SelectClause")?.getChildren("SelectItem") || [];
@@ -555,11 +721,12 @@ function analyzeQuery(queryNode) {
555
721
  isAgg ||= !!isSelectDistinct;
556
722
  selects.forEach((s) => {
557
723
  if (s.getChild("Wildcard")) {
558
- let path8 = s.getChild("Wildcard").getChildren("Identifier");
559
- let pathStrings = path8.map((p) => txt(p));
560
- let target = followJoins(path8, 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);
561
727
  if (!target) return;
562
728
  target.fields.forEach((f) => {
729
+ analyzeField(f, target);
563
730
  if (isJoin(f) || f.isAgg) return;
564
731
  scope.outputFields.push({ ...f, e: { node: "field", path: [...pathStrings, f.name], type: f.type } });
565
732
  });
@@ -613,22 +780,21 @@ function analyzeQuery(queryNode) {
613
780
  }
614
781
  let q = {
615
782
  fields: scope.outputFields,
616
- subQuerySources,
617
- malloyQuery: {
618
- type: "query",
619
- structRef,
620
- pipeline: [{
621
- type: isAgg ? "reduce" : "project",
622
- queryFields: scope.outputFields,
623
- filterList,
624
- outputStruct: null,
625
- isRepeated: false,
626
- orderBy: orderByList.length ? orderByList : void 0,
627
- limit: queryLimit
628
- }]
629
- }
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
+ }]
630
796
  };
631
- inferParamTypes(q.malloyQuery);
797
+ inferParamTypes(q);
632
798
  return q;
633
799
  }
634
800
  function analyzeExpression(expr, scope) {
@@ -655,11 +821,13 @@ function analyzeExpression(expr, scope) {
655
821
  if (scope.outputFields.includes(field) && field.isAgg) {
656
822
  return { node: "outputField", name: field.name, ...typeInfo, isAgg: field.isAgg };
657
823
  }
658
- let path8 = expr.getChildren("Identifier").map((i) => txt(i));
659
- return { node: "field", path: path8, ...typeInfo, isAgg: field.isAgg };
824
+ let path10 = expr.getChildren("Identifier").map((i) => txt(i));
825
+ return { node: "field", path: path10, ...typeInfo, isAgg: field.isAgg };
660
826
  }
661
827
  case "ExtractExpression": {
662
- 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);
663
831
  if (!isTemporalType(e.type) || !e.typeDef) return diag(expr, "Expression must be a date or timestamp", errExpr);
664
832
  let units = txt(expr.getChild("ExtractUnit")).replace(/^['"]|['"]$/g, "").toLowerCase();
665
833
  if (!isExtractUnit(units)) return diag(expr, "Not a valid unit to extract", errExpr);
@@ -682,7 +850,28 @@ function analyzeExpression(expr, scope) {
682
850
  let left2 = analyzeExpression(expr.firstChild, scope);
683
851
  let right2 = analyzeExpression(expr.lastChild, scope);
684
852
  let op = txt(expr.firstChild?.nextSibling).toLowerCase();
685
- 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 };
686
875
  }
687
876
  case "NullTestExpression": {
688
877
  let node = expr.getChildren("Kw").find((n) => txt(n).toLowerCase() == "not") ? "is-not-null" : "is-null";
@@ -716,8 +905,13 @@ function analyzeExpression(expr, scope) {
716
905
  let oneOf = [];
717
906
  let valueList = expr.getChild("InValueList");
718
907
  if (valueList) {
719
- 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
+ });
720
913
  } else {
914
+ diag(expr, "IN (<subquery>) is not yet supported");
721
915
  oneOf = [{ node: "genericSQLExpr", kids: { args: [] }, type: "array" }];
722
916
  }
723
917
  let isAgg = eNode.isAgg || oneOf.some((v) => v.isAgg);
@@ -735,11 +929,14 @@ function analyzeFunctionCall(expr, scope) {
735
929
  return o.params.length == argNodes.length || !!o.params.find((p) => p.isVariadic);
736
930
  });
737
931
  let args = argNodes.map((node, idx) => {
738
- let type2 = overload?.params[idx]?.allowedTypes[0];
739
- 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") {
740
934
  return { node: "genericSQLExpr", kids: { args: [] }, type: "sql native", src: [txt(node)], isAgg: false };
741
935
  } else {
742
- 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;
743
940
  }
744
941
  });
745
942
  let type = overload?.returnType.type;
@@ -754,7 +951,12 @@ function analyzeFunctionCall(expr, scope) {
754
951
  }));
755
952
  let ret;
756
953
  if (["count", "min", "max", "avg", "sum"].includes(name.toLowerCase())) {
757
- 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 };
758
960
  } else if (overload && type) {
759
961
  ret = {
760
962
  node: "function_call",
@@ -775,8 +977,52 @@ function analyzeFunctionCall(expr, scope) {
775
977
  if (foriegnPaths.length > 0) ret.structPath = foriegnPaths[0].split(".");
776
978
  return ret;
777
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
+ }
778
1024
  function isSupportedType(value) {
779
- 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"];
780
1026
  return supported.includes(value);
781
1027
  }
782
1028
  function lookupField(expr, scope) {
@@ -862,6 +1108,10 @@ function convertDataType(dataType) {
862
1108
  return "number";
863
1109
  case "INT64":
864
1110
  return "number";
1111
+ case "NUMBER":
1112
+ return "number";
1113
+ case "VARIANT":
1114
+ return "string";
865
1115
  case "TEXT":
866
1116
  return "string";
867
1117
  case "STRING":
@@ -884,6 +1134,8 @@ function convertDataType(dataType) {
884
1134
  return "timestamp";
885
1135
  case "TIMESTAMP":
886
1136
  return "timestamp";
1137
+ case "TIMESTAMP_NTZ":
1138
+ return "timestamp";
887
1139
  case "DECIMAL":
888
1140
  return "number";
889
1141
  case "DOUBLE":
@@ -907,11 +1159,13 @@ function convertDataType(dataType) {
907
1159
  var FILE_MAP, diagnostics, TABLE_NODE_MAP, FIELD_NODE_MAP, NODE_ENTITY_MAP, analysisQueue, errExpr;
908
1160
  var init_analyze = __esm({
909
1161
  "../lang/analyze.ts"() {
1162
+ "use strict";
910
1163
  init_util();
911
1164
  init_metadata();
912
1165
  init_config();
913
1166
  init_functions();
914
1167
  init_params();
1168
+ init_temporalLiterals();
915
1169
  FILE_MAP = {};
916
1170
  diagnostics = [];
917
1171
  TABLE_NODE_MAP = /* @__PURE__ */ new WeakMap();
@@ -923,39 +1177,40 @@ var init_analyze = __esm({
923
1177
  });
924
1178
 
925
1179
  // ../lang/parser.terms.js
926
- 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;
927
1181
  var init_parser_terms = __esm({
928
1182
  "../lang/parser.terms.js"() {
1183
+ "use strict";
929
1184
  table = 6;
930
1185
  primary_key = 11;
931
- as = 19;
932
- on = 22;
933
- not = 31;
934
- exists = 49;
935
- from = 53;
936
- inner = 59;
937
- left = 61;
938
- right = 63;
939
- full = 65;
940
- cross = 67;
941
- join = 69;
942
- select = 72;
943
- where = 80;
944
- group = 83;
945
- by = 85;
946
- order = 91;
947
- asc = 95;
948
- desc = 97;
949
- limit = 100;
950
- offset = 102;
951
- _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;
952
1209
  is = 109;
953
1210
  _null = 111;
954
- _true = 115;
955
- _false = 117;
956
- or = 130;
957
- and = 132;
958
- like = 141;
1211
+ exists = 129;
1212
+ _true = 133;
1213
+ _false = 135;
959
1214
  }
960
1215
  });
961
1216
 
@@ -967,6 +1222,7 @@ function specializeIdentifier(value) {
967
1222
  var keywords;
968
1223
  var init_tokens = __esm({
969
1224
  "../lang/tokens.js"() {
1225
+ "use strict";
970
1226
  init_parser_terms();
971
1227
  keywords = {
972
1228
  select,
@@ -1008,25 +1264,26 @@ import { LRParser } from "@lezer/lr";
1008
1264
  var spec_Identifier, parser;
1009
1265
  var init_parser = __esm({
1010
1266
  "../lang/parser.js"() {
1267
+ "use strict";
1011
1268
  init_tokens();
1012
- 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 };
1013
1270
  parser = LRParser.deserialize({
1014
1271
  version: 14,
1015
- 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=`",
1016
- 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~",
1017
- 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",
1018
- 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",
1019
- 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,
1020
1277
  nodeProps: [
1021
- ["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"]
1022
1279
  ],
1023
1280
  skippedNodes: [0, 1],
1024
- repeatNodeCount: 9,
1025
- 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*^",
1026
1283
  tokenizers: [0],
1027
1284
  topRules: { "Program": [0, 2] },
1028
1285
  specialized: [{ term: 5, get: (value, stack) => specializeIdentifier(value, stack) << 1, external: specializeIdentifier }, { term: 5, get: (value) => spec_Identifier[value] || -1 }],
1029
- tokenPrec: 2599
1286
+ tokenPrec: 2957
1030
1287
  });
1031
1288
  }
1032
1289
  });
@@ -1189,6 +1446,7 @@ function isFence(event) {
1189
1446
  var COMPONENT_ATTRIBUTE_KEYS, GSQL_FENCE, COMPONENT_TAG, ATTRIBUTE;
1190
1447
  var init_markdown = __esm({
1191
1448
  "../lang/markdown.ts"() {
1449
+ "use strict";
1192
1450
  init_parser();
1193
1451
  COMPONENT_ATTRIBUTE_KEYS = ["x", "y", "y2", "series", "value", "category"];
1194
1452
  GSQL_FENCE = /^([ \t]*)(`{3,})g?sql[^\n]*\n([\s\S]*?)^\1\2[ \t]*$/gim;
@@ -1197,6 +1455,74 @@ var init_markdown = __esm({
1197
1455
  }
1198
1456
  });
1199
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
+
1200
1526
  // ../lang/core.ts
1201
1527
  import { registerDialect, StandardSQLDialect, QueryModel, expandBlueprintMap } from "@graphenedata/malloy";
1202
1528
  import { readFile } from "node:fs/promises";
@@ -1211,15 +1537,19 @@ function getDiagnostics() {
1211
1537
  async function loadWorkspace(dir, includeMd) {
1212
1538
  let files = await glob(includeMd ? "**/*.{gsql,md}" : "**/*.gsql", { cwd: dir, ignore: ["node_modules/**"] });
1213
1539
  for await (let file of files) {
1214
- let contents = await readFile(path2.join(dir, file), "utf-8");
1215
- 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
+ }
1216
1546
  }
1217
1547
  }
1218
- function updateFile(contents, path8) {
1219
- FILE_MAP[path8] ||= { path: path8, contents, tree: null, tables: [], queries: [] };
1220
- FILE_MAP[path8].contents = contents;
1221
- FILE_MAP[path8].tree = null;
1222
- return FILE_MAP[path8];
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];
1223
1553
  }
1224
1554
  function analyze(contents, type) {
1225
1555
  clearDiagnostics();
@@ -1232,6 +1562,7 @@ function analyze(contents, type) {
1232
1562
  recordSyntaxErrors(fi);
1233
1563
  findTables(fi);
1234
1564
  });
1565
+ Object.values(FILE_MAP).forEach(applyExtends);
1235
1566
  Object.values(FILE_MAP).flatMap((f) => f.tables).forEach(analyzeTable);
1236
1567
  if (contents) {
1237
1568
  let fi = FILE_MAP["input"];
@@ -1243,12 +1574,22 @@ function analyze(contents, type) {
1243
1574
  }
1244
1575
  }
1245
1576
  function toSql(query, params = {}) {
1246
- let contents = {};
1247
- let gsqlTables = Object.values(FILE_MAP).filter((f) => f.path !== "input").flatMap((f) => f.tables);
1248
- gsqlTables.forEach((t) => contents[t.name] = t);
1249
- let inputTables = [...FILE_MAP["input"]?.tables || [], ...query.subQuerySources];
1250
- inputTables.forEach((t) => contents[t.name] = { ...t, query: t.query && fillInParams(t.query, params) });
1251
- let malloyQuery = fillInParams(query.malloyQuery, params);
1577
+ if (query.rawSql) return query.rawSql;
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]);
1252
1593
  let qm = new QueryModel({
1253
1594
  name: "generated_model",
1254
1595
  contents,
@@ -1256,11 +1597,12 @@ function toSql(query, params = {}) {
1256
1597
  dependencies: {},
1257
1598
  exports: []
1258
1599
  });
1259
- return qm.compileQuery(malloyQuery).sql;
1600
+ return qm.compileQuery(query).sql;
1260
1601
  }
1261
1602
  var BigQueryDialect;
1262
1603
  var init_core = __esm({
1263
1604
  "../lang/core.ts"() {
1605
+ "use strict";
1264
1606
  init_analyze();
1265
1607
  init_params();
1266
1608
  init_util();
@@ -1268,6 +1610,7 @@ var init_core = __esm({
1268
1610
  init_functions();
1269
1611
  init_parser();
1270
1612
  init_markdown();
1613
+ init_snowflake();
1271
1614
  BigQueryDialect = class extends StandardSQLDialect {
1272
1615
  constructor() {
1273
1616
  super();
@@ -1281,6 +1624,543 @@ var init_core = __esm({
1281
1624
  }
1282
1625
  });
1283
1626
 
1627
+ // printer.ts
1628
+ import { styleText as nodeStyleText } from "node:util";
1629
+ import Table from "cli-table3";
1630
+ import chalk from "chalk";
1631
+ function offsetToLineCol(src, offset2) {
1632
+ let lines = src.split(/\r?\n/);
1633
+ let acc = 0;
1634
+ for (let i = 0; i < lines.length; i++) {
1635
+ let lineText = lines[i];
1636
+ let nextAcc = acc + lineText.length + 1;
1637
+ if (offset2 < nextAcc || i === lines.length - 1) {
1638
+ let col = Math.max(0, offset2 - acc);
1639
+ return { line: i + 1, col, lineStart: acc, lineText };
1640
+ }
1641
+ acc = nextAcc;
1642
+ }
1643
+ return { line: 1, col: 0, lineStart: 0, lineText: lines[0] || "" };
1644
+ }
1645
+ function printDiagnostics(diags, log) {
1646
+ log ||= console.log;
1647
+ let parts = [];
1648
+ for (let d of diags) {
1649
+ let src = getFile2(d.file)?.contents || "";
1650
+ let { line, col, lineStart, lineText } = offsetToLineCol(src, d.from.offset);
1651
+ let endCol = Math.max(col + 1, Math.min(lineText.length, d.to.offset - lineStart));
1652
+ let caretLen = Math.max(1, endCol - col);
1653
+ let sev = d.severity === "error" ? "red" : "yellow";
1654
+ let header = `${styleText(sev, d.severity.toUpperCase())}: ${d.file} line ${line}: ${d.message}`;
1655
+ let gutter = " | ";
1656
+ let caretLine = `${" ".repeat(col)}${styleText(sev, "^".repeat(caretLen))}`;
1657
+ parts.push([header, `${gutter}${lineText}`, `${gutter}${caretLine}`].join("\n"));
1658
+ }
1659
+ if (parts.length) log(parts.join("\n"));
1660
+ }
1661
+ function printTable(rows) {
1662
+ if (!rows || rows.length === 0) {
1663
+ console.log(chalk.yellow("No results returned"));
1664
+ return;
1665
+ }
1666
+ let headers = Object.keys(rows[0]);
1667
+ let table2 = new Table({ head: headers.map((h) => chalk.blue(h)) });
1668
+ let MAX_DISPLAY_ROWS = 200;
1669
+ let displayRows = rows.slice(0, MAX_DISPLAY_ROWS);
1670
+ displayRows.forEach((row) => table2.push(headers.map((h) => row[h]?.toString() || "")));
1671
+ console.log(table2.toString());
1672
+ if (rows.length > MAX_DISPLAY_ROWS) {
1673
+ console.log(chalk.yellow(`Displayed first ${MAX_DISPLAY_ROWS} rows (of ${rows.length} total).`));
1674
+ }
1675
+ }
1676
+ var styleText;
1677
+ var init_printer = __esm({
1678
+ "printer.ts"() {
1679
+ "use strict";
1680
+ init_core();
1681
+ styleText = (style, text) => {
1682
+ try {
1683
+ return nodeStyleText ? nodeStyleText(style, text) : text;
1684
+ } catch {
1685
+ return text;
1686
+ }
1687
+ };
1688
+ }
1689
+ });
1690
+
1691
+ // background.ts
1692
+ import { spawn, exec } from "child_process";
1693
+ import { promisify } from "util";
1694
+ import { fileURLToPath } from "url";
1695
+ import fs2 from "fs-extra";
1696
+ import path3 from "path";
1697
+ async function runServeInBackground() {
1698
+ let grapheneCache = getGrapheneCache(config.root);
1699
+ let logFile = path3.join(grapheneCache, "serve.log");
1700
+ await fs2.ensureDir(grapheneCache);
1701
+ let log = fs2.openSync(logFile, "w");
1702
+ let entryPoint = process.argv[1] || fileURLToPath(import.meta.url);
1703
+ let childArgs = [...process.execArgv, entryPoint, "serve"];
1704
+ let child = spawn(process.execPath, childArgs, {
1705
+ cwd: config.root,
1706
+ detached: true,
1707
+ env: { ...process.env },
1708
+ stdio: ["ignore", log, log]
1709
+ });
1710
+ if (!child.pid) throw new Error("Failed to start server process");
1711
+ await new Promise((resolve, reject) => {
1712
+ let buffer = "";
1713
+ fs2.watchFile(logFile, { interval: 200 }, (curr, prev) => {
1714
+ if (curr.size > prev.size) {
1715
+ let stream = fs2.createReadStream(logFile, { start: 0, end: curr.size - 1 });
1716
+ stream.on("data", (d) => {
1717
+ process.stdout.write(d);
1718
+ buffer = (buffer + d.toString()).slice(-200);
1719
+ if (buffer.includes("Server running at http://localhost:")) resolve();
1720
+ });
1721
+ }
1722
+ });
1723
+ child.once("exit", () => {
1724
+ reject(new Error("Exited before server started"));
1725
+ });
1726
+ child.once("error", (e) => reject(e));
1727
+ });
1728
+ }
1729
+ function getGrapheneCache(root) {
1730
+ return path3.join(root, "node_modules", ".graphene");
1731
+ }
1732
+ function sendSignal(pid, signal) {
1733
+ let pids = process.platform === "win32" ? [pid] : [pid, -pid];
1734
+ for (let target of pids) {
1735
+ try {
1736
+ process.kill(target, signal);
1737
+ } catch (err) {
1738
+ let code = err.code;
1739
+ if (code === "ESRCH" || code === "EINVAL") continue;
1740
+ return false;
1741
+ }
1742
+ }
1743
+ return true;
1744
+ }
1745
+ async function stopGrapheneIfRunning() {
1746
+ let port = Number(process.env.GRAPHENE_PORT) || 4e3;
1747
+ let pid = await getPidOnPort(port);
1748
+ if (!pid) return;
1749
+ console.log(`Stopping server (${pid})`);
1750
+ sendSignal(pid, "SIGTERM");
1751
+ let end = Date.now() + 5e3;
1752
+ while (Date.now() < end) {
1753
+ if (!await getPidOnPort(port)) break;
1754
+ await new Promise((resolve) => setTimeout(resolve, 100));
1755
+ }
1756
+ if (await getPidOnPort(port)) {
1757
+ sendSignal(pid, "SIGKILL");
1758
+ }
1759
+ if (await getPidOnPort(port)) {
1760
+ console.error("Failed to stop previous Graphene server");
1761
+ }
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;
1779
+ }
1780
+ }
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);
1790
+ });
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;
1797
+ }
1798
+ return void 0;
1799
+ }
1800
+ var execAsync;
1801
+ var init_background = __esm({
1802
+ "background.ts"() {
1803
+ "use strict";
1804
+ init_config();
1805
+ execAsync = promisify(exec);
1806
+ }
1807
+ });
1808
+
1809
+ // mockFiles.ts
1810
+ var mockFileMap;
1811
+ var init_mockFiles = __esm({
1812
+ "mockFiles.ts"() {
1813
+ "use strict";
1814
+ mockFileMap = {};
1815
+ }
1816
+ });
1817
+
1818
+ // check.ts
1819
+ import fs3 from "fs-extra";
1820
+ import os from "os";
1821
+ import path4 from "path";
1822
+ import { spawn as spawn2 } from "child_process";
1823
+ import { WebSocketServer } from "ws";
1824
+ import { readFileSync as readFileSync2 } from "node:fs";
1825
+ import { styleText as styleText2 } from "node:util";
1826
+ async function check(options) {
1827
+ let log = options.log || console.log;
1828
+ let mdFile = options.mdArg && normalizeMdFile(options.mdArg);
1829
+ if (options.mdArg && !mdFile) {
1830
+ log(`Couldn't find ${options.mdArg}`);
1831
+ return false;
1832
+ }
1833
+ await loadWorkspace(config.root, !mdFile);
1834
+ if (mdFile) {
1835
+ if (process.env.NODE_ENV == "test" && mockFileMap[mdFile]) {
1836
+ updateFile(mockFileMap[mdFile], mdFile);
1837
+ } else {
1838
+ let content = readFileSync2(path4.resolve(config.root, mdFile), "utf-8");
1839
+ updateFile(content, mdFile);
1840
+ }
1841
+ }
1842
+ analyze();
1843
+ if (getDiagnostics().length > 0) {
1844
+ printDiagnostics(getDiagnostics(), log);
1845
+ return false;
1846
+ }
1847
+ if (!mdFile) {
1848
+ log("No errors found \u{1F48E}");
1849
+ return true;
1850
+ }
1851
+ let host = `http://localhost:${config.port || Number(process.env.GRAPHENE_PORT) || 4e3}`;
1852
+ let pageUrl = "/" + mdFile.replace(/\.md$/, "").replace(/^\//, "").replace(/\\/g, "/");
1853
+ if (pageUrl === "/index") pageUrl = "/";
1854
+ if (process.env.NODE_ENV !== "test" && !await isServerRunning()) {
1855
+ log("Starting Graphene server...");
1856
+ await runServeInBackground();
1857
+ }
1858
+ let resp = await sendCheckRequest({ host, pageUrl, chart: options.chart });
1859
+ if (resp.checkError == "no_server") {
1860
+ log("Failed to start Graphene server");
1861
+ return false;
1862
+ }
1863
+ if (resp.checkError == "no_tab" && process.env.NODE_ENV !== "test") {
1864
+ log(`Opening page ${host}${pageUrl}`);
1865
+ spawn2("open", [host + pageUrl]);
1866
+ await new Promise((resolve) => setTimeout(resolve, 500));
1867
+ resp = await sendCheckRequest({ host, pageUrl, chart: options.chart });
1868
+ }
1869
+ if (resp.checkError == "no_tab") {
1870
+ log("Failed to open a new tab");
1871
+ return false;
1872
+ }
1873
+ if (resp.checkError) {
1874
+ log("Failed to run check: " + resp.checkError);
1875
+ return false;
1876
+ }
1877
+ let errors = Array.from(resp.errors || []);
1878
+ if (errors.length) {
1879
+ log(styleText2("red", "Runtime errors") + ` in ${mdFile}:`);
1880
+ } else {
1881
+ log("No errors found \u{1F48E}");
1882
+ }
1883
+ errors.forEach((e) => {
1884
+ if (e.file && e.line) printDiagnostics([e], log);
1885
+ else if (e.id) log(`${e.id}: ${e.message}`);
1886
+ else log(e.message);
1887
+ });
1888
+ if (resp?.stillLoading) {
1889
+ log("Warning: Queries were still loading when the screenshot was taken");
1890
+ }
1891
+ if (resp?.screenshot) {
1892
+ let filename = `graphene-screenshot-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.png`;
1893
+ let screenshotPath = path4.join(os.tmpdir(), filename);
1894
+ let base64Data = resp.screenshot.replace(/^data:image\/png;base64,/, "");
1895
+ await fs3.writeFile(screenshotPath, base64Data, "base64");
1896
+ log("Screenshot saved to", screenshotPath);
1897
+ }
1898
+ return errors.length == 0;
1899
+ }
1900
+ async function sendCheckRequest({ host, pageUrl, chart }) {
1901
+ let abort = new AbortController();
1902
+ let timeout = setTimeout(() => abort.abort(), 3e4);
1903
+ try {
1904
+ let response = await fetch(`${host}/_api/check`, {
1905
+ method: "POST",
1906
+ headers: { "Content-Type": "application/json" },
1907
+ body: JSON.stringify({ pageUrl: host + pageUrl, chart }),
1908
+ signal: abort.signal
1909
+ });
1910
+ clearTimeout(timeout);
1911
+ let body = response.headers.get("content-type") == "application/json" ? await response.json() : { error: await response.text() };
1912
+ if (!response.ok) {
1913
+ if (body.error) return { checkError: body.error };
1914
+ console.error(`Unexpected response: ${JSON.stringify(body)}`);
1915
+ return { checkError: "Unexpected response from Graphene server" };
1916
+ }
1917
+ return body;
1918
+ } catch (err) {
1919
+ clearTimeout(timeout);
1920
+ if (err.name === "AbortError") return { checkError: "timeout" };
1921
+ return { checkError: "no_server" };
1922
+ }
1923
+ }
1924
+ function normalizeMdFile(mdFile) {
1925
+ let clean = mdFile.trim();
1926
+ if (!clean) return null;
1927
+ if (!clean.endsWith(".md")) clean = clean + ".md";
1928
+ if (process.env.NODE_ENV == "test" && mockFileMap[clean]) {
1929
+ return clean;
1930
+ }
1931
+ let absolute = [
1932
+ path4.resolve(process.cwd(), clean),
1933
+ path4.resolve(config.root, clean)
1934
+ ].find((p) => fs3.existsSync(p)) || null;
1935
+ if (!absolute) return null;
1936
+ let relative = path4.relative(config.root, absolute);
1937
+ return relative;
1938
+ }
1939
+ async function proxyCheckRequest(req, res) {
1940
+ let chunks = [];
1941
+ for await (let chunk of req) chunks.push(chunk);
1942
+ let { pageUrl, chart } = JSON.parse(Buffer.concat(chunks).toString());
1943
+ let id = Math.random().toString(36).slice(2);
1944
+ res.setHeader("Content-Type", "application/json");
1945
+ let normalizedPageUrl = pageUrl.replace(/\/$/, "");
1946
+ let conn = await pollFor(() => browserConnections.find((conn2) => conn2.url === normalizedPageUrl), 5e3, 100);
1947
+ if (!conn) {
1948
+ res.statusCode = 400;
1949
+ res.end(JSON.stringify({ error: "no_tab" }));
1950
+ return;
1951
+ } else {
1952
+ conn.socket.send(JSON.stringify({ type: "check", chart, requestId: id }));
1953
+ pendingRequests[id] = { response: res };
1954
+ }
1955
+ }
1956
+ function checkVitePlugin() {
1957
+ return {
1958
+ name: "graphene-check-plugin",
1959
+ configureServer(server) {
1960
+ let wss = new WebSocketServer({ noServer: true });
1961
+ server.httpServer?.on("upgrade", (req, socket, head) => {
1962
+ if (!req.url || !req.url.includes("/_api/ws") && !req.url.includes("graphene-ws")) return;
1963
+ wss.handleUpgrade(req, socket, head, (ws) => {
1964
+ wss.emit("connection", ws, req);
1965
+ });
1966
+ });
1967
+ wss.on("connection", (socket) => {
1968
+ socket.on("message", (data) => {
1969
+ let message = JSON.parse(data.toString());
1970
+ if (message.type === "register") {
1971
+ let normalizedUrl = message.url.replace(/\/$/, "");
1972
+ browserConnections.push({ url: normalizedUrl, socket });
1973
+ }
1974
+ if (message.type === "checkResponse") {
1975
+ pendingRequests[message.requestId].response.end(JSON.stringify(message));
1976
+ delete pendingRequests[message.requestId];
1977
+ }
1978
+ });
1979
+ socket.on("close", () => {
1980
+ browserConnections = browserConnections.filter((conn) => conn.socket !== socket);
1981
+ });
1982
+ });
1983
+ server.httpServer?.on("close", () => wss.close());
1984
+ server.middlewares.use(async (req, res, next) => {
1985
+ let [pathName] = (req.url || "").split("?");
1986
+ if (pathName === "/_api/check") await proxyCheckRequest(req, res);
1987
+ else next();
1988
+ });
1989
+ }
1990
+ };
1991
+ }
1992
+ var browserConnections, pendingRequests;
1993
+ var init_check = __esm({
1994
+ "check.ts"() {
1995
+ "use strict";
1996
+ init_core();
1997
+ init_printer();
1998
+ init_mockFiles();
1999
+ init_background();
2000
+ init_util();
2001
+ browserConnections = [];
2002
+ pendingRequests = {};
2003
+ }
2004
+ });
2005
+
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
+
1284
2164
  // connections/bigQuery.ts
1285
2165
  var bigQuery_exports = {};
1286
2166
  __export(bigQuery_exports, {
@@ -1290,20 +2170,19 @@ import { BigQuery, BigQueryDate, BigQueryTimestamp } from "@google-cloud/bigquer
1290
2170
  var BigQueryConnection;
1291
2171
  var init_bigQuery = __esm({
1292
2172
  "connections/bigQuery.ts"() {
2173
+ "use strict";
1293
2174
  init_config();
1294
2175
  BigQueryConnection = class {
1295
2176
  client;
1296
2177
  constructor(options = {}) {
2178
+ options.projectId ||= config.bigquery?.projectId;
1297
2179
  if (process.env.GOOGLE_CREDENTIALS_CONTENT) {
1298
2180
  let parsed = JSON.parse(process.env.GOOGLE_CREDENTIALS_CONTENT);
1299
2181
  options.projectId = parsed.project_id;
1300
2182
  options.credentials = parsed;
1301
2183
  }
1302
- options.projectId ||= config.googleProjectId;
1303
- options.maxRetries ||= 3;
1304
- options.userAgent ||= "Graphene";
1305
- if (!options.projectId) throw new Error("googleProjectId must be set in config or provided in service account credentials");
1306
- this.client = new BigQuery(options);
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" });
1307
2186
  }
1308
2187
  async runQuery(sql) {
1309
2188
  let [job] = await this.client.createQueryJob({ query: sql, useLegacySql: false });
@@ -1327,27 +2206,33 @@ var duckdb_exports = {};
1327
2206
  __export(duckdb_exports, {
1328
2207
  DuckDBConnection: () => DuckDBConnection
1329
2208
  });
1330
- import { promises as fs3 } from "fs";
1331
- import path4 from "path";
2209
+ import { promises as fs5 } from "fs";
2210
+ import path6 from "path";
1332
2211
  import { DuckDBTimestampValue, DuckDBInstance, DuckDBDateValue } from "@duckdb/node-api";
1333
2212
  var DuckDBConnection;
1334
2213
  var init_duckdb = __esm({
1335
2214
  "connections/duckdb.ts"() {
2215
+ "use strict";
1336
2216
  init_config();
1337
2217
  DuckDBConnection = class {
2218
+ options;
1338
2219
  ready;
1339
2220
  connection = null;
1340
- constructor() {
2221
+ constructor(options) {
2222
+ this.options = options || {};
1341
2223
  this.ready = this.initialize();
1342
2224
  }
1343
2225
  async initialize() {
1344
- let files = await fs3.readdir(config.root);
1345
- let databasePath = files.find((f) => f.endsWith(".duckdb"));
1346
- if (!databasePath) throw new Error("No .duckdb file found in current directory");
1347
- databasePath = path4.resolve(config.root, databasePath);
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
+ }
1348
2233
  let db = await DuckDBInstance.create(":memory:");
1349
2234
  this.connection = await db.connect();
1350
- let escapedPath = databasePath.replace(/'/g, "''");
2235
+ let escapedPath = dbPath.replace(/'/g, "''");
1351
2236
  await this.connection.run(`attach '${escapedPath}' as graphene_cli (READ_ONLY);`);
1352
2237
  await this.connection.run("use graphene_cli;");
1353
2238
  }
@@ -1372,27 +2257,114 @@ var init_duckdb = __esm({
1372
2257
  }
1373
2258
  });
1374
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
+
1375
2331
  // connections/index.ts
1376
- async function getConnection() {
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
+ }
1377
2341
  if (config.dialect === "bigquery") {
1378
2342
  let mod = await Promise.resolve().then(() => (init_bigQuery(), bigQuery_exports));
1379
- return new mod.BigQueryConnection();
2343
+ let conn = new mod.BigQueryConnection();
2344
+ return await conn.runQuery(sql);
1380
2345
  } else if (config.dialect === "duckdb") {
1381
2346
  let mod = await Promise.resolve().then(() => (init_duckdb(), duckdb_exports));
1382
- return new mod.DuckDBConnection();
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);
1383
2353
  } else {
1384
2354
  throw new Error(`Unsupported dialect: ${config.dialect}`);
1385
2355
  }
1386
2356
  }
1387
2357
  var init_connections = __esm({
1388
2358
  "connections/index.ts"() {
2359
+ "use strict";
1389
2360
  init_config();
2361
+ init_auth();
1390
2362
  }
1391
2363
  });
1392
2364
 
1393
2365
  // mdCompile.ts
1394
- import fs4 from "fs";
1395
- import path5 from "path";
2366
+ import fs6 from "fs";
2367
+ import path7 from "path";
1396
2368
  import { visit } from "unist-util-visit";
1397
2369
  import sanitizeHtml from "sanitize-html";
1398
2370
  function extractQueries() {
@@ -1469,13 +2441,14 @@ ${content}`;
1469
2441
  }
1470
2442
  function componentNames() {
1471
2443
  if (cachedComponentNames) return cachedComponentNames;
1472
- let files = fs4.readdirSync(path5.join(import.meta.dirname, "../ui/components"));
1473
- cachedComponentNames = files.map((f) => path5.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("_"));
1474
2446
  return cachedComponentNames || [];
1475
2447
  }
1476
2448
  var cachedComponentNames;
1477
2449
  var init_mdCompile = __esm({
1478
2450
  "mdCompile.ts"() {
2451
+ "use strict";
1479
2452
  cachedComponentNames = null;
1480
2453
  }
1481
2454
  });
@@ -1483,23 +2456,19 @@ var init_mdCompile = __esm({
1483
2456
  // serve2.ts
1484
2457
  var serve2_exports = {};
1485
2458
  __export(serve2_exports, {
1486
- mockFileMap: () => mockFileMap,
1487
2459
  serve2: () => serve2
1488
2460
  });
1489
2461
  import { createServer, optimizeDeps } from "vite";
1490
2462
  import { svelte, vitePreprocess } from "@sveltejs/vite-plugin-svelte";
1491
- import fs5 from "fs-extra";
1492
- import crypto from "crypto";
2463
+ import fs7 from "fs-extra";
2464
+ import crypto2 from "crypto";
1493
2465
  import { mdsvex } from "mdsvex";
1494
- import path6 from "path";
2466
+ import path8 from "path";
1495
2467
  import { fileURLToPath as fileURLToPath2 } from "url";
1496
- import { WebSocketServer } from "ws";
1497
- import { spawn as spawn2 } from "child_process";
1498
2468
  async function serve2() {
1499
- grapheneRoot = config.root;
1500
- uiRoot = path6.join(fileURLToPath2(import.meta.url), "../../ui");
1501
- await fs5.ensureDir(path6.resolve(grapheneRoot, "node_modules/.graphene"));
1502
- await fs5.writeFile(path6.resolve(grapheneRoot, `node_modules/.graphene/${process.env.NODE_ENV == "test" ? "test" : "serve"}.pid`), String(process.pid));
2469
+ uiRoot = path8.join(fileURLToPath2(import.meta.url), "../../ui");
2470
+ let port = Number(process.env.GRAPHENE_PORT) || 4e3;
2471
+ await fs7.ensureDir(path8.resolve(config.root, "node_modules/.graphene"));
1503
2472
  let server = await createServer({
1504
2473
  root: config.root,
1505
2474
  plugins: [
@@ -1515,24 +2484,30 @@ async function serve2() {
1515
2484
  injectComponentImports()
1516
2485
  ]
1517
2486
  }),
2487
+ checkVitePlugin(),
1518
2488
  handleRequestPlugin,
1519
2489
  updateWorkspacePlugin,
1520
2490
  mockFilesForTests()
1521
2491
  ],
2492
+ publicDir: path8.resolve(uiRoot),
1522
2493
  server: {
1523
- port: config.port,
2494
+ port,
1524
2495
  fs: { strict: false },
1525
2496
  strictPort: true
1526
2497
  },
1527
2498
  resolve: {
1528
2499
  alias: {
1529
- graphene: path6.resolve(uiRoot, "web.js")
2500
+ graphene: path8.resolve(uiRoot, "web.js")
1530
2501
  }
1531
2502
  }
2503
+ // optimizeDeps: { // this seems prudent in tests, but currently breaks because ssf needs to be optimized, even in tests
2504
+ // noDiscovery: process.env.NODE_ENV == 'test',
2505
+ // include: process.env.NODE_ENV == 'test' ? [] : undefined,
2506
+ // },
1532
2507
  });
1533
2508
  await optimizeDeps(server.config);
1534
2509
  await server.listen();
1535
- console.log(`Server running at http://localhost:${config.port}`);
2510
+ console.log(`Server running at http://localhost:${port}`);
1536
2511
  return server;
1537
2512
  }
1538
2513
  async function handleQuery(req, res) {
@@ -1549,44 +2524,18 @@ async function handleQuery(req, res) {
1549
2524
  }
1550
2525
  if (queries.length > 1) throw new Error("Found multiple queries, which could be a parsing error");
1551
2526
  let sql = toSql(queries[0], params);
1552
- let hash = crypto.createHash("SHA1").update(sql).digest("hex");
2527
+ let hash = crypto2.createHash("SHA1").update(sql).digest("hex");
1553
2528
  res.setHeader("ETag", hash);
1554
2529
  if (hashes.includes(hash) && req.headers["cache-control"] != "no-cache") {
1555
2530
  res.statusCode = 304;
1556
2531
  return res.end();
1557
2532
  }
1558
- let connection = await getConnection();
1559
- let queryResults = await connection.runQuery(sql);
2533
+ let queryResults = await runQuery(sql);
1560
2534
  let totalRows = queryResults.totalRows ?? queryResults.rows.length;
1561
2535
  if (totalRows > queryResults.rows.length) throw new Error("Query returns too many rows");
1562
2536
  let fields = queries[0].fields.map((f) => ({ name: f.name, type: f.type }));
1563
2537
  res.end(JSON.stringify({ rows: queryResults.rows, hash, fields, sql }));
1564
2538
  }
1565
- async function handleView(req, res) {
1566
- let chunks = [];
1567
- for await (let chunk of req) chunks.push(chunk);
1568
- let { mdFile, chart } = JSON.parse(Buffer.concat(chunks).toString());
1569
- let id = Math.random().toString(36).slice(2);
1570
- res.setHeader("Content-Type", "application/json");
1571
- viewRequests[id] = { response: res };
1572
- let pageUrl = "/" + mdFile.replace(/\.md$/, "").replace(/^\//, "");
1573
- if (pageUrl === "/index") pageUrl = "/";
1574
- pageUrl = `http://localhost:${config.port || 4e3}${pageUrl}`;
1575
- let conn = browserConnections.find((conn2) => conn2.url === pageUrl);
1576
- if (!conn) {
1577
- spawn2("open", [pageUrl]);
1578
- let end = Date.now() + 5e3;
1579
- while (Date.now() < end && !conn) {
1580
- conn = browserConnections.find((conn2) => conn2.url === pageUrl);
1581
- await new Promise((resolve) => setTimeout(resolve, 100));
1582
- }
1583
- if (!conn) {
1584
- res.statusCode = 500;
1585
- return res.end(JSON.stringify({ error: "No browser tab available and failed to open one" }));
1586
- }
1587
- }
1588
- conn.socket.send(JSON.stringify({ type: "view", chart, requestId: id }));
1589
- }
1590
2539
  async function handlePage(server, res, filePath, mount) {
1591
2540
  res.setHeader("Content-Type", "text/html");
1592
2541
  let mdMount = mount ? `
@@ -1599,7 +2548,7 @@ async function handlePage(server, res, filePath, mount) {
1599
2548
  <meta charset="UTF-8" />
1600
2549
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1601
2550
  <title>Graphene</title>
1602
- <link rel="icon" href="@graphenedata/ui/assets/favicon.ico" />
2551
+ <link rel="icon" href="/assets/favicon.ico" />
1603
2552
  <link rel="preconnect" href="https://fonts.googleapis.com">
1604
2553
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
1605
2554
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet">
@@ -1609,10 +2558,7 @@ async function handlePage(server, res, filePath, mount) {
1609
2558
  <div id="content"></div>
1610
2559
  </main>
1611
2560
  <script type="module">
1612
- // do this first so we can track errors caused by importing the md file
1613
- import 'graphene'
1614
- </script>
1615
- <script type="module">
2561
+ import 'graphene' // do this first so we can track errors caused by importing the md file
1616
2562
  ${mdMount}
1617
2563
  </script>
1618
2564
  </body>
@@ -1625,63 +2571,45 @@ function mockFilesForTests() {
1625
2571
  name: "mock-files-for-tests",
1626
2572
  enforce: "pre",
1627
2573
  resolveId(id) {
1628
- if (mockFileMap[id.replace(grapheneRoot, "")]) return id + "?mock";
2574
+ if (mockFileMap[id.replace(config.root + "/", "")]) return id + "?mock";
1629
2575
  },
1630
2576
  load(id) {
1631
2577
  if (!id.endsWith("?mock")) return null;
1632
- return mockFileMap[id.replace(grapheneRoot, "").replace(/\?mock$/, "")];
2578
+ return mockFileMap[id.replace(config.root + "/", "").replace(/\?mock$/, "")];
1633
2579
  }
1634
2580
  };
1635
2581
  }
1636
- var grapheneRoot, uiRoot, workspaceLoadPromise, updateWorkspacePlugin, handleRequestPlugin, browserConnections, viewRequests, mockFileMap;
2582
+ var uiRoot, workspaceLoadPromise, updateWorkspacePlugin, handleRequestPlugin;
1637
2583
  var init_serve2 = __esm({
1638
2584
  "serve2.ts"() {
2585
+ "use strict";
1639
2586
  init_core();
1640
2587
  init_connections();
1641
2588
  init_mdCompile();
2589
+ init_check();
2590
+ init_mockFiles();
1642
2591
  updateWorkspacePlugin = {
1643
2592
  name: "updateWorkspace",
1644
2593
  configureServer: (s) => {
1645
2594
  s.watcher.add("**/*.gsql");
1646
2595
  s.watcher.on("change", () => {
1647
2596
  clearWorkspace();
1648
- workspaceLoadPromise = loadWorkspace(grapheneRoot, false);
2597
+ workspaceLoadPromise = loadWorkspace(config.root, false);
1649
2598
  });
1650
- workspaceLoadPromise = loadWorkspace(grapheneRoot, false);
2599
+ workspaceLoadPromise = loadWorkspace(config.root, false);
1651
2600
  }
1652
2601
  };
1653
2602
  handleRequestPlugin = {
1654
2603
  name: "handleRequest",
1655
2604
  configureServer: (s) => {
1656
- let wss = new WebSocketServer({ noServer: true });
1657
- s.httpServer.on("upgrade", (req, socket, head) => {
1658
- if (!req.url?.endsWith("/graphene-ws")) return;
1659
- wss.handleUpgrade(req, socket, head, (ws) => {
1660
- wss.emit("connection", ws, req);
1661
- });
1662
- });
1663
- wss.on("connection", (socket) => {
1664
- socket.on("message", (data) => {
1665
- let message = JSON.parse(data.toString());
1666
- if (message.type === "register") {
1667
- browserConnections.push({ url: message.url, socket });
1668
- }
1669
- if (message.type === "viewResponse") {
1670
- viewRequests[message.requestId].response.end(JSON.stringify(message));
1671
- delete viewRequests[message.requestId];
1672
- }
1673
- });
1674
- socket.on("close", () => browserConnections = browserConnections.filter((conn) => conn.socket !== socket));
1675
- });
1676
2605
  s.middlewares.use(async function handleRequest(req, res, next) {
1677
2606
  try {
1678
2607
  let [pathName] = (req.url || "").split("?");
1679
2608
  if (pathName == "/_api/query") return await handleQuery(req, res);
1680
- if (pathName == "/graphene/view") return await handleView(req, res);
1681
2609
  if (pathName == "/__ct") return await handlePage(s, res, "__ct", false);
1682
2610
  if (!pathName || pathName == "/") pathName = "index";
1683
- let mdPath = path6.join(grapheneRoot, pathName + ".md");
1684
- if (await fs5.exists(mdPath)) {
2611
+ let mdPath = path8.join(config.root, pathName + ".md");
2612
+ if (await fs7.exists(mdPath)) {
1685
2613
  await handlePage(s, res, mdPath, true);
1686
2614
  } else {
1687
2615
  next();
@@ -1694,166 +2622,20 @@ var init_serve2 = __esm({
1694
2622
  });
1695
2623
  }
1696
2624
  };
1697
- browserConnections = [];
1698
- viewRequests = {};
1699
- mockFileMap = {};
1700
2625
  }
1701
2626
  });
1702
2627
 
1703
2628
  // cli.ts
1704
- import { Command } from "commander";
1705
-
1706
- // printer.ts
1707
- init_core();
1708
- import { styleText as nodeStyleText } from "node:util";
1709
- import Table from "cli-table3";
1710
- import chalk from "chalk";
1711
- var styleText = (style, text) => {
1712
- try {
1713
- return nodeStyleText ? nodeStyleText(style, text) : text;
1714
- } catch {
1715
- return text;
1716
- }
1717
- };
1718
- function offsetToLineCol(src, offset2) {
1719
- let lines = src.split(/\r?\n/);
1720
- let acc = 0;
1721
- for (let i = 0; i < lines.length; i++) {
1722
- let lineText = lines[i];
1723
- let nextAcc = acc + lineText.length + 1;
1724
- if (offset2 < nextAcc || i === lines.length - 1) {
1725
- let col = Math.max(0, offset2 - acc);
1726
- return { line: i + 1, col, lineStart: acc, lineText };
1727
- }
1728
- acc = nextAcc;
1729
- }
1730
- return { line: 1, col: 0, lineStart: 0, lineText: lines[0] || "" };
1731
- }
1732
- function printDiagnostics(diags) {
1733
- let parts = [];
1734
- for (let d of diags) {
1735
- let src = getFile2(d.file)?.contents || "";
1736
- let { line, col, lineStart, lineText } = offsetToLineCol(src, d.from.offset);
1737
- let endCol = Math.max(col + 1, Math.min(lineText.length, d.to.offset - lineStart));
1738
- let caretLen = Math.max(1, endCol - col);
1739
- let sev = d.severity === "error" ? "red" : "yellow";
1740
- let header = `${styleText(sev, d.severity.toUpperCase())}: ${d.file} line ${line}: ${d.message}`;
1741
- let gutter = " | ";
1742
- let caretLine = `${" ".repeat(col)}${styleText(sev, "^".repeat(caretLen))}`;
1743
- parts.push([header, `${gutter}${lineText}`, `${gutter}${caretLine}`].join("\n"));
1744
- }
1745
- if (parts.length) console.error(parts.join("\n"));
1746
- }
1747
- function printTable(rows) {
1748
- if (!rows || rows.length === 0) {
1749
- console.log(chalk.yellow("No results returned"));
1750
- return;
1751
- }
1752
- let headers = Object.keys(rows[0]);
1753
- let table2 = new Table({ head: headers.map((h) => chalk.blue(h)) });
1754
- let MAX_DISPLAY_ROWS = 200;
1755
- let displayRows = rows.slice(0, MAX_DISPLAY_ROWS);
1756
- displayRows.forEach((row) => table2.push(headers.map((h) => row[h]?.toString() || "")));
1757
- console.log(table2.toString());
1758
- if (rows.length > MAX_DISPLAY_ROWS) {
1759
- console.log(chalk.yellow(`Displayed first ${MAX_DISPLAY_ROWS} rows (of ${rows.length} total).`));
1760
- }
1761
- }
1762
-
1763
- // cli.ts
2629
+ init_printer();
1764
2630
  init_core();
1765
2631
  init_config();
1766
- import fs6 from "fs-extra";
1767
- import path7 from "path";
1768
- import os from "os";
1769
-
1770
- // background.ts
1771
- import { spawn } from "child_process";
1772
- import { fileURLToPath } from "url";
1773
- import fs2 from "fs-extra";
1774
- import path3 from "path";
1775
- async function runServeInBackground() {
1776
- let root = process.cwd();
1777
- let grapheneCache = getGrapheneCache(root);
1778
- let logFile = path3.join(grapheneCache, "serve.log");
1779
- await fs2.ensureDir(grapheneCache);
1780
- await stopGrapheneIfRunning(root);
1781
- let log = fs2.openSync(logFile, "w");
1782
- let entryPoint = process.argv[1] || fileURLToPath(import.meta.url);
1783
- let childArgs = [...process.execArgv, entryPoint, "serve", "--fg", ...process.argv.slice(3)];
1784
- let child = spawn(process.execPath, childArgs, {
1785
- cwd: root,
1786
- detached: true,
1787
- env: { ...process.env },
1788
- stdio: ["ignore", log, log]
1789
- });
1790
- if (!child.pid) throw new Error("Failed to start server process");
1791
- await new Promise((resolve, reject) => {
1792
- let buffer = "";
1793
- fs2.watchFile(logFile, { interval: 200 }, (curr, prev) => {
1794
- if (curr.size > prev.size) {
1795
- let stream = fs2.createReadStream(logFile, { start: 0, end: curr.size - 1 });
1796
- stream.on("data", (d) => {
1797
- process.stdout.write(d);
1798
- buffer = (buffer + d.toString()).slice(-200);
1799
- if (buffer.includes("Server running at http://localhost:")) resolve();
1800
- });
1801
- }
1802
- });
1803
- child.once("exit", () => {
1804
- process.stdout.write(fs2.readFileSync(logFile));
1805
- reject(new Error("Exited before server started"));
1806
- });
1807
- child.once("error", (e) => reject(e));
1808
- });
1809
- }
1810
- function getGrapheneCache(root) {
1811
- return path3.join(root, "node_modules", ".graphene");
1812
- }
1813
- function getPidFilePath(root) {
1814
- return path3.join(getGrapheneCache(root), process.env.NODE_ENV == "test" ? "test.pid" : "serve.pid");
1815
- }
1816
- async function stopGrapheneIfRunning(root) {
1817
- let pidFile = getPidFilePath(root);
1818
- let pid = await readPid(pidFile);
1819
- if (!pid) return true;
1820
- if (!isProcessRunning(pid)) {
1821
- await fs2.remove(pidFile);
1822
- return true;
1823
- }
1824
- try {
1825
- console.log(`Stopping server (${pid})`);
1826
- process.kill(pid, "SIGTERM");
1827
- } catch (err) {
1828
- if (err.code === "ESRCH") return true;
1829
- return false;
1830
- }
1831
- let end = Date.now() + 5e3;
1832
- while (Date.now() < end && isProcessRunning(pid)) {
1833
- await new Promise((resolve) => setTimeout(resolve, 100));
1834
- }
1835
- await fs2.remove(pidFile);
1836
- return !isProcessRunning(pid);
1837
- }
1838
- async function readPid(pidFile) {
1839
- if (!await fs2.pathExists(pidFile)) return void 0;
1840
- let contents = (await fs2.readFile(pidFile, "utf8")).trim();
1841
- if (!contents) return void 0;
1842
- let pid = Number.parseInt(contents, 10);
1843
- if (Number.isNaN(pid)) return void 0;
1844
- return pid;
1845
- }
1846
- function isProcessRunning(pid) {
1847
- try {
1848
- process.kill(pid, 0);
1849
- return true;
1850
- } catch {
1851
- return false;
1852
- }
1853
- }
1854
-
1855
- // cli.ts
2632
+ init_background();
2633
+ init_check();
1856
2634
  init_connections();
2635
+ init_auth();
2636
+ import { Command } from "commander";
2637
+ import fs8 from "fs-extra";
2638
+ import path9 from "path";
1857
2639
  var program = new Command();
1858
2640
  program.name("graphene").description("Graphene CLI").version("1.0.0");
1859
2641
  program.hook("preAction", async () => {
@@ -1875,53 +2657,30 @@ program.command("run").description("Run a query against your database").argument
1875
2657
  let queries = analyze(gsql);
1876
2658
  if (!validQuery(queries)) return;
1877
2659
  let sql = toSql(queries[0]);
1878
- let connection = await getConnection();
1879
- let res = await connection.runQuery(sql);
2660
+ let res = await runQuery(sql);
1880
2661
  printTable(res.rows);
1881
2662
  });
1882
- program.command("serve").description("Run the local server").option("--fg", "Run the server in the foreground").action(async (options) => {
1883
- if (options.fg || process.env.DEBUG) {
1884
- let mod = await Promise.resolve().then(() => (init_serve2(), serve2_exports));
1885
- await mod.serve2();
1886
- } 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) {
1887
2666
  await runServeInBackground();
1888
2667
  process.exit(0);
2668
+ } else {
2669
+ let mod = await Promise.resolve().then(() => (init_serve2(), serve2_exports));
2670
+ await mod.serve2();
1889
2671
  }
1890
2672
  });
1891
2673
  program.command("stop").description("Stop the local server").action(async () => {
1892
- await stopGrapheneIfRunning(process.cwd());
2674
+ await stopGrapheneIfRunning();
1893
2675
  });
1894
- program.command("check").description("Check the project for errors").action(async () => {
1895
- await loadWorkspace(process.cwd(), true);
1896
- analyze();
1897
- if (getDiagnostics().length) {
1898
- printDiagnostics(getDiagnostics());
1899
- process.exit(1);
1900
- }
1901
- console.log("No errors found \u{1F48E}");
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) => {
2677
+ let res = await check({ mdArg, chart: options.chart });
2678
+ process.exit(res ? 0 : 1);
1902
2679
  });
1903
- program.command("view").description("Capture a screenshot of a rendered markdown file").argument("<mdFile>", "Markdown file to view (e.g., index.md)").option("-c, --chart <chartName>", "Name of specific chart to capture").action(async (mdFile, options) => {
1904
- let response = await fetch("http://localhost:4000/graphene/view", {
1905
- method: "POST",
1906
- headers: { "Content-Type": "application/json" },
1907
- body: JSON.stringify({ mdFile, chart: options.chart })
1908
- });
1909
- if (!response.ok) throw new Error(`View request failed: ${await response.text()}`);
1910
- let result = await response.json();
1911
- if (result.errors && result.errors.length > 0) {
1912
- console.error("Errors found:");
1913
- result.errors.forEach((error) => console.error(JSON.stringify(error)));
1914
- }
1915
- if (result.stillLoading) {
1916
- console.error("Warning: Queries were still loading when the screenshot was taken");
1917
- }
1918
- if (result.screenshot) {
1919
- let filename = `graphene-screenshot-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.png`;
1920
- let screenshotPath = path7.join(os.tmpdir(), filename);
1921
- let base64Data = result.screenshot.replace(/^data:image\/png;base64,/, "");
1922
- await fs6.writeFile(screenshotPath, base64Data, "base64");
1923
- console.log("Screenshot saved to", screenshotPath);
1924
- }
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);
1925
2684
  });
1926
2685
  program.parse(process.argv);
1927
2686
  async function readInput(arg) {
@@ -1934,9 +2693,9 @@ async function readInput(arg) {
1934
2693
  process.stdin.resume();
1935
2694
  });
1936
2695
  }
1937
- let absolutePath = path7.resolve(arg);
1938
- if (fs6.existsSync(absolutePath)) {
1939
- return await fs6.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");
1940
2699
  }
1941
2700
  return arg;
1942
2701
  }