yamchart 0.3.6 → 0.3.9

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.
@@ -6,7 +6,7 @@ import {
6
6
  ScheduleSchema,
7
7
  loadEnvFile,
8
8
  validateProject
9
- } from "./chunk-7HKIUUVE.js";
9
+ } from "./chunk-ENOTMVPI.js";
10
10
  import {
11
11
  AuthDatabase,
12
12
  generateSessionToken,
@@ -289,6 +289,10 @@ var ConfigLoader = class {
289
289
  return this.models.get(name);
290
290
  }
291
291
  getDefaultConnection() {
292
+ const envConnection = process.env.YAMCHART_CONNECTION;
293
+ if (envConnection) {
294
+ return this.connections.get(envConnection);
295
+ }
292
296
  const defaultName = this.project?.defaults?.connection;
293
297
  if (defaultName) {
294
298
  return this.connections.get(defaultName);
@@ -529,6 +533,49 @@ function createConnectorFromConfig(connection, options) {
529
533
  throw new Error(`Unsupported connection type: ${connection.type}`);
530
534
  }
531
535
  }
536
+ function buildConnectionInfo(connection) {
537
+ const info2 = {
538
+ name: connection.name,
539
+ type: connection.type
540
+ };
541
+ switch (connection.type) {
542
+ case "duckdb": {
543
+ const c = connection;
544
+ info2.path = c.config.path;
545
+ break;
546
+ }
547
+ case "postgres": {
548
+ const c = connection;
549
+ info2.host = c.config.host;
550
+ info2.port = c.config.port;
551
+ info2.database = c.config.database;
552
+ info2.schema = c.config.schema;
553
+ break;
554
+ }
555
+ case "mysql": {
556
+ const c = connection;
557
+ info2.host = c.config.host;
558
+ info2.port = c.config.port;
559
+ info2.database = c.config.database;
560
+ break;
561
+ }
562
+ case "sqlite": {
563
+ const c = connection;
564
+ info2.path = c.config.path;
565
+ break;
566
+ }
567
+ case "snowflake": {
568
+ const c = connection;
569
+ info2.account = c.config.account;
570
+ info2.warehouse = c.config.warehouse;
571
+ info2.database = c.config.database;
572
+ info2.schema = c.config.schema;
573
+ info2.role = c.config.role;
574
+ break;
575
+ }
576
+ }
577
+ return info2;
578
+ }
532
579
  async function testConnection(connection, options) {
533
580
  const start = performance.now();
534
581
  let connector = null;
@@ -799,12 +846,30 @@ async function dashboardRoutes(fastify, options) {
799
846
  });
800
847
  fastify.post("/api/dashboards/:id", async (request, reply) => {
801
848
  const { id } = request.params;
802
- const { layout, message } = request.body;
849
+ const { layout, tab, tabLayout, message } = request.body;
803
850
  const dashboard = configLoader.getDashboardByName(id);
804
851
  if (!dashboard) {
805
852
  return reply.status(404).send({ error: `Dashboard not found: ${id}` });
806
853
  }
807
- const updated = { ...dashboard, layout };
854
+ let updated;
855
+ if (dashboard.tabs) {
856
+ if (!tab || !tabLayout) {
857
+ return reply.status(400).send({ error: 'Tabbed dashboard requires "tab" and "tabLayout" fields' });
858
+ }
859
+ const tabIndex = dashboard.tabs.findIndex((t) => t.name === tab);
860
+ if (tabIndex === -1) {
861
+ return reply.status(400).send({ error: `Tab not found: ${tab}` });
862
+ }
863
+ const existingTab = dashboard.tabs[tabIndex];
864
+ const newTabs = [...dashboard.tabs];
865
+ newTabs[tabIndex] = { ...existingTab, layout: tabLayout };
866
+ updated = { ...dashboard, tabs: newTabs };
867
+ } else {
868
+ if (!layout) {
869
+ return reply.status(400).send({ error: 'Non-tabbed dashboard requires "layout" field' });
870
+ }
871
+ updated = { ...dashboard, layout };
872
+ }
808
873
  const filePath = join3(projectDir, "dashboards", `${id}.yaml`);
809
874
  await writeFile(filePath, stringifyYaml(updated));
810
875
  await configLoader.load();
@@ -818,7 +883,7 @@ async function dashboardRoutes(fastify, options) {
818
883
  });
819
884
  fastify.post("/api/dashboards/:id/warm-cache", async (request, reply) => {
820
885
  const { id } = request.params;
821
- const { params = {} } = request.body;
886
+ const { params = {}, tab } = request.body;
822
887
  const dashboard = configLoader.getDashboardByName(id);
823
888
  if (!dashboard) {
824
889
  return reply.status(404).send({ error: `Dashboard not found: ${id}` });
@@ -827,12 +892,28 @@ async function dashboardRoutes(fastify, options) {
827
892
  return reply.status(500).send({ error: "Query service not available" });
828
893
  }
829
894
  const chartRefs = [];
830
- for (const row of dashboard.layout.rows) {
831
- for (const widget of row.widgets) {
832
- if (widget.type === "chart" && widget.ref) {
833
- chartRefs.push(widget.ref);
895
+ const extractFromLayout = (layout) => {
896
+ for (const row of layout.rows) {
897
+ for (const widget of row.widgets) {
898
+ if (widget.type === "chart" && widget.ref) {
899
+ chartRefs.push(widget.ref);
900
+ }
834
901
  }
835
902
  }
903
+ };
904
+ if (dashboard.tabs) {
905
+ if (tab) {
906
+ const matchingTab = dashboard.tabs.find((t) => t.name === tab);
907
+ if (matchingTab) {
908
+ extractFromLayout(matchingTab.layout);
909
+ }
910
+ } else {
911
+ for (const t of dashboard.tabs) {
912
+ extractFromLayout(t.layout);
913
+ }
914
+ }
915
+ } else if (dashboard.layout) {
916
+ extractFromLayout(dashboard.layout);
836
917
  }
837
918
  const results = await Promise.all(chartRefs.map(async (chartRef) => {
838
919
  const chart = configLoader.getChartByName(chartRef);
@@ -13326,32 +13407,8 @@ async function createServer(options) {
13326
13407
  if (!defaultConnection) {
13327
13408
  throw new Error("No connection configured");
13328
13409
  }
13329
- let connector;
13330
- if (defaultConnection.type === "duckdb") {
13331
- const duckdbConfig = defaultConnection;
13332
- const dbPath = duckdbConfig.config.path.startsWith("/") ? duckdbConfig.config.path : join4(projectDir, duckdbConfig.config.path);
13333
- connector = new DuckDBConnector({ path: dbPath });
13334
- await connector.connect();
13335
- } else if (defaultConnection.type === "postgres") {
13336
- const pgConnection = defaultConnection;
13337
- const credentials = resolvePostgresAuth(pgConnection);
13338
- connector = new PostgresConnector({
13339
- host: pgConnection.config.host,
13340
- port: pgConnection.config.port,
13341
- database: pgConnection.config.database,
13342
- schema: pgConnection.config.schema,
13343
- ssl: pgConnection.config.ssl,
13344
- user: credentials.user,
13345
- password: credentials.password,
13346
- min: pgConnection.pool?.min_connections,
13347
- max: pgConnection.pool?.max_connections,
13348
- idleTimeoutMillis: pgConnection.pool?.idle_timeout,
13349
- statementTimeout: pgConnection.query?.timeout
13350
- });
13351
- await connector.connect();
13352
- } else {
13353
- throw new Error(`Unsupported connection type: ${defaultConnection.type}`);
13354
- }
13410
+ const connector = createConnectorFromConfig(defaultConnection, { projectDir });
13411
+ await connector.connect();
13355
13412
  const project = configLoader.getProject();
13356
13413
  const cacheTtl = project.defaults?.cache_ttl ? parseTtl2(project.defaults.cache_ttl) : 5 * 60 * 1e3;
13357
13414
  const cache = new MemoryCache({
@@ -13383,26 +13440,16 @@ async function createServer(options) {
13383
13440
  if (schedules.length > 0) {
13384
13441
  schedulerService.start(schedules);
13385
13442
  }
13386
- const connectionInfo = {
13387
- name: defaultConnection.name,
13388
- type: defaultConnection.type,
13389
- ...defaultConnection.type === "duckdb" && {
13390
- path: defaultConnection.config.path
13391
- },
13392
- ...defaultConnection.type === "postgres" && {
13393
- host: defaultConnection.config.host,
13394
- port: defaultConnection.config.port,
13395
- database: defaultConnection.config.database,
13396
- schema: defaultConnection.config.schema
13397
- }
13398
- };
13399
- fastify.get("/api/health", async () => ({
13400
- status: "ok",
13401
- version: VERSION,
13402
- project: project.name,
13403
- environment: process.env.NODE_ENV || "development",
13404
- connection: connectionInfo
13405
- }));
13443
+ fastify.get("/api/health", async () => {
13444
+ const currentConnection = configLoader.getDefaultConnection();
13445
+ return {
13446
+ status: "ok",
13447
+ version: VERSION,
13448
+ project: configLoader.getProject().name,
13449
+ environment: process.env.NODE_ENV || "development",
13450
+ connection: currentConnection ? buildConnectionInfo(currentConnection) : void 0
13451
+ };
13452
+ });
13406
13453
  if (authDb) {
13407
13454
  const secureCookies = process.env.NODE_ENV === "production";
13408
13455
  const sessionTtlMs = options.localAuth.sessionTtlMs;
@@ -13554,24 +13601,41 @@ async function runDevServer(projectDir, options) {
13554
13601
  }
13555
13602
  success(`Validation passed (${validation.stats.passed} files)`);
13556
13603
  newline();
13557
- const spinner2 = spinner("Starting server...");
13558
- let server;
13604
+ let usesBrowserAuth = false;
13605
+ let localAuth;
13559
13606
  try {
13560
- let localAuth;
13561
- try {
13562
- const { readFileSync } = await import("fs");
13563
- const { parse } = await import("yaml");
13564
- const raw = readFileSync(resolve(projectDir, "yamchart.yaml"), "utf-8");
13565
- const projectConfig = parse(raw);
13566
- if (projectConfig?.auth?.enabled) {
13567
- localAuth = {
13568
- enabled: true,
13569
- dbPath: projectConfig.auth.db_path ? resolve(projectConfig.auth.db_path.replace(/^~/, homedir())) : resolve(homedir(), ".yamchart", "auth.db"),
13570
- sessionTtlMs: projectConfig.auth.session_ttl ? parseTtl(projectConfig.auth.session_ttl) : 30 * 24 * 60 * 60 * 1e3
13571
- };
13607
+ const { readFileSync } = await import("fs");
13608
+ const { parse } = await import("yaml");
13609
+ const raw = readFileSync(resolve(projectDir, "yamchart.yaml"), "utf-8");
13610
+ const projectConfig = parse(raw);
13611
+ if (projectConfig?.auth?.enabled) {
13612
+ localAuth = {
13613
+ enabled: true,
13614
+ dbPath: projectConfig.auth.db_path ? resolve(projectConfig.auth.db_path.replace(/^~/, homedir())) : resolve(homedir(), ".yamchart", "auth.db"),
13615
+ sessionTtlMs: projectConfig.auth.session_ttl ? parseTtl(projectConfig.auth.session_ttl) : 30 * 24 * 60 * 60 * 1e3
13616
+ };
13617
+ }
13618
+ const connectionDir = resolve(projectDir, "connections");
13619
+ const targetConn = process.env.YAMCHART_CONNECTION || projectConfig?.defaults?.connection;
13620
+ if (targetConn) {
13621
+ try {
13622
+ const connRaw = readFileSync(resolve(connectionDir, `${targetConn}.yaml`), "utf-8");
13623
+ const connConfig = parse(connRaw);
13624
+ if (connConfig?.auth?.type === "externalbrowser") {
13625
+ usesBrowserAuth = true;
13626
+ }
13627
+ } catch {
13572
13628
  }
13573
- } catch {
13574
13629
  }
13630
+ } catch {
13631
+ }
13632
+ if (usesBrowserAuth) {
13633
+ info("Snowflake SSO \u2014 opening browser for authentication...");
13634
+ newline();
13635
+ }
13636
+ const spinner2 = spinner("Starting server...");
13637
+ let server;
13638
+ try {
13575
13639
  server = await createServer({
13576
13640
  projectDir,
13577
13641
  port: options.port,
@@ -13621,4 +13685,4 @@ async function runDevServer(projectDir, options) {
13621
13685
  export {
13622
13686
  runDevServer
13623
13687
  };
13624
- //# sourceMappingURL=dev-PWY7UVIO.js.map
13688
+ //# sourceMappingURL=dev-CPZ3M5EE.js.map