@squadbase/vite-server 0.1.17-dev.24af54e → 0.1.17-dev.3b633bb

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 (73) hide show
  1. package/dist/cli/index.js +1681 -449
  2. package/dist/connectors/airtable-oauth.js +28 -3
  3. package/dist/connectors/airtable.js +28 -3
  4. package/dist/connectors/amplitude.js +28 -3
  5. package/dist/connectors/asana.js +28 -3
  6. package/dist/connectors/attio.js +28 -3
  7. package/dist/connectors/aws-billing.js +28 -3
  8. package/dist/connectors/azure-sql.js +31 -6
  9. package/dist/connectors/backlog-api-key.js +28 -3
  10. package/dist/connectors/clickup.js +28 -3
  11. package/dist/connectors/cosmosdb.js +28 -3
  12. package/dist/connectors/customerio.js +29 -4
  13. package/dist/connectors/dbt.js +28 -3
  14. package/dist/connectors/freshdesk.js +28 -3
  15. package/dist/connectors/freshsales.js +28 -3
  16. package/dist/connectors/freshservice.js +28 -3
  17. package/dist/connectors/gamma.js +30 -5
  18. package/dist/connectors/github.js +28 -3
  19. package/dist/connectors/gmail-oauth.js +28 -3
  20. package/dist/connectors/gmail.js +28 -3
  21. package/dist/connectors/google-ads.js +28 -3
  22. package/dist/connectors/google-analytics-oauth.js +28 -3
  23. package/dist/connectors/google-analytics.js +227 -105
  24. package/dist/connectors/google-audit-log.js +28 -3
  25. package/dist/connectors/google-calendar-oauth.js +28 -3
  26. package/dist/connectors/google-calendar.js +28 -3
  27. package/dist/connectors/google-docs.js +28 -3
  28. package/dist/connectors/google-drive.js +28 -3
  29. package/dist/connectors/google-search-console-oauth.js +28 -3
  30. package/dist/connectors/google-sheets.js +28 -3
  31. package/dist/connectors/google-slides.js +28 -3
  32. package/dist/connectors/grafana.js +28 -3
  33. package/dist/connectors/hubspot-oauth.js +28 -3
  34. package/dist/connectors/hubspot.js +28 -3
  35. package/dist/connectors/influxdb.js +28 -3
  36. package/dist/connectors/intercom-oauth.js +28 -3
  37. package/dist/connectors/intercom.js +28 -3
  38. package/dist/connectors/jdbc.js +28 -3
  39. package/dist/connectors/jira-api-key.js +28 -3
  40. package/dist/connectors/kintone-api-token.js +28 -3
  41. package/dist/connectors/kintone.js +28 -3
  42. package/dist/connectors/linear.js +28 -3
  43. package/dist/connectors/linkedin-ads.js +28 -3
  44. package/dist/connectors/mailchimp-oauth.js +28 -3
  45. package/dist/connectors/mailchimp.js +28 -3
  46. package/dist/connectors/meta-ads-oauth.js +28 -3
  47. package/dist/connectors/meta-ads.js +28 -3
  48. package/dist/connectors/mixpanel.js +28 -3
  49. package/dist/connectors/monday.js +28 -3
  50. package/dist/connectors/mongodb.js +28 -3
  51. package/dist/connectors/notion-oauth.js +28 -3
  52. package/dist/connectors/notion.js +28 -3
  53. package/dist/connectors/oracle.js +54 -14
  54. package/dist/connectors/outlook-oauth.js +28 -3
  55. package/dist/connectors/powerbi-oauth.js +309 -37
  56. package/dist/connectors/salesforce.js +28 -3
  57. package/dist/connectors/semrush.js +366 -46
  58. package/dist/connectors/sentry.js +28 -3
  59. package/dist/connectors/shopify-oauth.js +28 -3
  60. package/dist/connectors/shopify.js +28 -3
  61. package/dist/connectors/sqlserver.js +31 -6
  62. package/dist/connectors/stripe-api-key.js +28 -3
  63. package/dist/connectors/stripe-oauth.js +28 -3
  64. package/dist/connectors/supabase.js +31 -6
  65. package/dist/connectors/tableau.js +246 -78
  66. package/dist/connectors/tiktok-ads.js +28 -3
  67. package/dist/connectors/wix-store.js +28 -3
  68. package/dist/connectors/zendesk-oauth.js +28 -3
  69. package/dist/connectors/zendesk.js +28 -3
  70. package/dist/index.js +1681 -449
  71. package/dist/main.js +1681 -449
  72. package/dist/vite-plugin.js +1681 -449
  73. package/package.json +1 -1
@@ -369,19 +369,34 @@ async function runSetupFlow(flow, params, ctx, config) {
369
369
  };
370
370
  let state = flow.initialState();
371
371
  let answerIdx = 0;
372
+ const pendingParameterUpdates = [];
372
373
  for (const step of flow.steps) {
373
374
  const ans = ctx.answers[answerIdx];
374
375
  if (ans && ans.questionSlug === step.slug) {
375
376
  state = step.applyAnswer(state, ans.answer);
377
+ if (step.toParameterUpdates) {
378
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
379
+ }
376
380
  answerIdx += 1;
377
381
  continue;
378
382
  }
383
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
379
384
  if (step.type === "text") {
385
+ if (step.fetchOptions) {
386
+ const options2 = await step.fetchOptions(state, runtime);
387
+ if (options2.length === 0) {
388
+ continue;
389
+ }
390
+ }
380
391
  return {
381
392
  type: "nextQuestion",
382
393
  questionSlug: step.slug,
383
394
  question: step.question[ctx.language],
384
- questionType: "text"
395
+ questionType: "text",
396
+ allowFreeText: resolvedAllowFreeText,
397
+ ...pendingParameterUpdates.length > 0 && {
398
+ parameterUpdates: pendingParameterUpdates
399
+ }
385
400
  };
386
401
  }
387
402
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -393,11 +408,21 @@ async function runSetupFlow(flow, params, ctx, config) {
393
408
  questionSlug: step.slug,
394
409
  question: step.question[ctx.language],
395
410
  questionType: step.type,
396
- options
411
+ options,
412
+ allowFreeText: resolvedAllowFreeText,
413
+ ...pendingParameterUpdates.length > 0 && {
414
+ parameterUpdates: pendingParameterUpdates
415
+ }
397
416
  };
398
417
  }
399
418
  const dataInvestigationResult = await flow.finalize(state, runtime);
400
- return { type: "fulfilled", dataInvestigationResult };
419
+ return {
420
+ type: "fulfilled",
421
+ dataInvestigationResult,
422
+ ...pendingParameterUpdates.length > 0 && {
423
+ parameterUpdates: pendingParameterUpdates
424
+ }
425
+ };
401
426
  }
402
427
  async function resolveSetupSelection(params) {
403
428
  const { selected, allSentinel, fetchAll, limit } = params;
@@ -467,7 +492,11 @@ async function tableauProxyApiFetch(proxyFetch, params, path2, init) {
467
492
 
468
493
  // ../connectors/src/connectors/tableau/setup-flow.ts
469
494
  var ALL_PROJECTS = "__ALL_PROJECTS__";
495
+ var ALL_WORKBOOKS = "__ALL_WORKBOOKS__";
470
496
  var TABLEAU_SETUP_MAX_PROJECTS = 10;
497
+ var MAX_WORKBOOKS_DETAIL = 10;
498
+ var MAX_VIEWS_PER_WORKBOOK = 5;
499
+ var MAX_SAMPLE_ROWS = 5;
471
500
  var PAGE_SIZE = 100;
472
501
  async function listAllProjects(rt) {
473
502
  const all = [];
@@ -516,6 +545,57 @@ async function listProjectResources(rt, resource) {
516
545
  }
517
546
  return all;
518
547
  }
548
+ async function listViewsForWorkbook(rt, workbookId) {
549
+ const res = await tableauProxyApiFetch(
550
+ rt.config.proxyFetch,
551
+ rt.params,
552
+ `/sites/{siteId}/workbooks/${encodeURIComponent(workbookId)}/views`
553
+ );
554
+ if (!res.ok) return [];
555
+ const data = await res.json();
556
+ return data.views?.view ?? [];
557
+ }
558
+ async function fetchViewDataSample(rt, viewId) {
559
+ try {
560
+ const res = await tableauProxyApiFetch(
561
+ rt.config.proxyFetch,
562
+ rt.params,
563
+ `/sites/{siteId}/views/${encodeURIComponent(viewId)}/data`
564
+ );
565
+ if (!res.ok) return null;
566
+ const csv = await res.text();
567
+ const lines = csv.trim().split("\n").filter(Boolean);
568
+ if (lines.length === 0) return null;
569
+ const columns = parseCsvLine(lines[0]);
570
+ const rows = lines.slice(1, 1 + MAX_SAMPLE_ROWS).map(parseCsvLine);
571
+ return { columns, rows };
572
+ } catch {
573
+ return null;
574
+ }
575
+ }
576
+ function parseCsvLine(line) {
577
+ const result = [];
578
+ let current = "";
579
+ let inQuotes = false;
580
+ for (let i = 0; i < line.length; i++) {
581
+ const ch = line[i];
582
+ if (ch === '"') {
583
+ if (inQuotes && line[i + 1] === '"') {
584
+ current += '"';
585
+ i++;
586
+ } else {
587
+ inQuotes = !inQuotes;
588
+ }
589
+ } else if (ch === "," && !inQuotes) {
590
+ result.push(current.trim());
591
+ current = "";
592
+ } else {
593
+ current += ch;
594
+ }
595
+ }
596
+ result.push(current.trim());
597
+ return result;
598
+ }
519
599
  var tableauSetupFlow = {
520
600
  initialState: () => ({}),
521
601
  steps: [
@@ -538,6 +618,39 @@ var tableauSetupFlow = {
538
618
  ];
539
619
  },
540
620
  applyAnswer: (state, answer) => ({ ...state, projects: answer })
621
+ },
622
+ {
623
+ slug: "workbooks",
624
+ type: "multiSelect",
625
+ question: {
626
+ ja: "\u5206\u6790\u5BFE\u8C61\u306E\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
627
+ en: "Select the workbooks to analyze (multi-select allowed)"
628
+ },
629
+ async fetchOptions(state, rt) {
630
+ if (!state.projects?.length) return [];
631
+ const allProjects = await listAllProjects(rt);
632
+ const targetIds = await resolveSetupSelection({
633
+ selected: state.projects,
634
+ allSentinel: ALL_PROJECTS,
635
+ fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
636
+ limit: TABLEAU_SETUP_MAX_PROJECTS
637
+ });
638
+ const targetSet = new Set(targetIds);
639
+ const workbooks = await listProjectResources(rt, "workbooks");
640
+ const options = workbooks.filter((wb) => wb.id && wb.project?.id && targetSet.has(wb.project.id)).map((wb) => ({
641
+ value: wb.id,
642
+ label: wb.name ?? wb.id ?? "(unknown)"
643
+ }));
644
+ if (options.length === 0) return [];
645
+ return [
646
+ {
647
+ value: ALL_WORKBOOKS,
648
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF" : "All workbooks"
649
+ },
650
+ ...options
651
+ ];
652
+ },
653
+ applyAnswer: (state, answer) => ({ ...state, workbooks: answer })
541
654
  }
542
655
  ],
543
656
  async finalize(state, rt) {
@@ -546,25 +659,33 @@ var tableauSetupFlow = {
546
659
  }
547
660
  const allProjects = await listAllProjects(rt);
548
661
  const projectById = new Map(allProjects.map((p) => [p.id, p]));
549
- const targetIds = await resolveSetupSelection({
662
+ const targetProjectIds = await resolveSetupSelection({
550
663
  selected: state.projects,
551
664
  allSentinel: ALL_PROJECTS,
552
665
  fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
553
666
  limit: TABLEAU_SETUP_MAX_PROJECTS
554
667
  });
555
668
  const sections = ["## Tableau", ""];
556
- if (!targetIds.length) {
669
+ if (!targetProjectIds.length) {
557
670
  sections.push("_No projects selected._", "");
558
671
  return sections.join("\n");
559
672
  }
560
- const [workbooks, datasources] = await Promise.all([
673
+ const [allWorkbooks, datasources] = await Promise.all([
561
674
  listProjectResources(rt, "workbooks"),
562
675
  listProjectResources(rt, "datasources")
563
676
  ]);
677
+ const projectIdSet = new Set(targetProjectIds);
678
+ const targetWorkbooks = await resolveSetupSelection({
679
+ selected: state.workbooks ?? [],
680
+ allSentinel: ALL_WORKBOOKS,
681
+ fetchAll: async () => allWorkbooks.filter((wb) => wb.id && wb.project?.id && projectIdSet.has(wb.project.id)).map((wb) => wb.id).filter(Boolean),
682
+ limit: MAX_WORKBOOKS_DETAIL
683
+ });
684
+ const targetWorkbookSet = new Set(targetWorkbooks);
564
685
  const workbooksByProject = /* @__PURE__ */ new Map();
565
- for (const wb of workbooks) {
686
+ for (const wb of allWorkbooks) {
566
687
  const pid = wb.project?.id;
567
- if (!pid) continue;
688
+ if (!pid || !projectIdSet.has(pid)) continue;
568
689
  const bucket = workbooksByProject.get(pid) ?? [];
569
690
  bucket.push(wb);
570
691
  workbooksByProject.set(pid, bucket);
@@ -572,32 +693,78 @@ var tableauSetupFlow = {
572
693
  const datasourcesByProject = /* @__PURE__ */ new Map();
573
694
  for (const ds of datasources) {
574
695
  const pid = ds.project?.id;
575
- if (!pid) continue;
696
+ if (!pid || !projectIdSet.has(pid)) continue;
576
697
  const bucket = datasourcesByProject.get(pid) ?? [];
577
698
  bucket.push(ds);
578
699
  datasourcesByProject.set(pid, bucket);
579
700
  }
580
- for (const id of targetIds) {
581
- const project = projectById.get(id);
582
- const name = project?.name ?? id;
583
- sections.push(`### Project: ${name}`, "", `- id: \`${id}\``);
584
- const projectWorkbooks = workbooksByProject.get(id) ?? [];
585
- sections.push(`- Workbooks (${projectWorkbooks.length}):`);
586
- for (const wb of projectWorkbooks.slice(0, 25)) {
587
- sections.push(` - ${wb.name ?? wb.id ?? "(unknown)"}`);
588
- }
589
- if (projectWorkbooks.length > 25) {
590
- sections.push(` - \u2026and ${projectWorkbooks.length - 25} more`);
701
+ const allDiscoveredColumns = [];
702
+ for (const pid of targetProjectIds) {
703
+ const project = projectById.get(pid);
704
+ sections.push(`### Project: ${project?.name ?? pid}`, "");
705
+ const projectWorkbooks = workbooksByProject.get(pid) ?? [];
706
+ for (const wb of projectWorkbooks) {
707
+ if (!wb.id) continue;
708
+ const isDetailed = targetWorkbookSet.has(wb.id);
709
+ sections.push(`#### Workbook: ${wb.name ?? wb.id}`, "");
710
+ if (!isDetailed) continue;
711
+ const views = await listViewsForWorkbook(rt, wb.id);
712
+ if (views.length === 0) {
713
+ sections.push("_No views found._", "");
714
+ continue;
715
+ }
716
+ for (const view of views.slice(0, MAX_VIEWS_PER_WORKBOOK)) {
717
+ if (!view.id) continue;
718
+ sections.push(`##### View: ${view.name ?? view.id}`, "");
719
+ const sample = await fetchViewDataSample(rt, view.id);
720
+ if (!sample || sample.columns.length === 0) {
721
+ sections.push("_Data not available._", "");
722
+ continue;
723
+ }
724
+ allDiscoveredColumns.push(...sample.columns);
725
+ sections.push(`| ${sample.columns.join(" | ")} |`);
726
+ sections.push(`|${sample.columns.map(() => "---").join("|")}|`);
727
+ for (const row of sample.rows) {
728
+ const cells = row.map((c) => c.replace(/\|/g, "\\|"));
729
+ sections.push(`| ${cells.join(" | ")} |`);
730
+ }
731
+ sections.push("");
732
+ }
591
733
  }
592
- const projectDatasources = datasourcesByProject.get(id) ?? [];
593
- sections.push(`- Datasources (${projectDatasources.length}):`);
594
- for (const ds of projectDatasources.slice(0, 25)) {
595
- sections.push(` - ${ds.name ?? ds.id ?? "(unknown)"}`);
734
+ const projectDatasources = datasourcesByProject.get(pid) ?? [];
735
+ if (projectDatasources.length > 0) {
736
+ sections.push(`#### Datasources (${projectDatasources.length})`, "");
737
+ for (const ds of projectDatasources.slice(0, 25)) {
738
+ sections.push(`- ${ds.name ?? ds.id ?? "(unknown)"}`);
739
+ }
740
+ sections.push("");
596
741
  }
597
- if (projectDatasources.length > 25) {
598
- sections.push(` - \u2026and ${projectDatasources.length - 25} more`);
742
+ }
743
+ if (allDiscoveredColumns.length > 0) {
744
+ const unique = [...new Set(allDiscoveredColumns.map((c) => c.toLowerCase()))];
745
+ const themes = [];
746
+ const hasPattern = (keywords) => unique.some((c) => keywords.some((k) => c.includes(k)));
747
+ if (hasPattern(["revenue", "sales", "amount", "price", "cost", "profit", "\u58F2\u4E0A", "\u91D1\u984D", "\u5229\u76CA"]))
748
+ themes.push("Revenue & profitability analysis (trends, breakdown by segment)");
749
+ if (hasPattern(["date", "month", "year", "quarter", "day", "\u65E5\u4ED8", "\u6708", "\u5E74", "\u671F"]))
750
+ themes.push("Time-series trend analysis (YoY, MoM comparisons)");
751
+ if (hasPattern(["region", "country", "city", "state", "\u5730\u57DF", "\u56FD", "\u90FD\u5E02", "\u5E02"]))
752
+ themes.push("Geographic/regional performance comparison");
753
+ if (hasPattern(["product", "category", "item", "sku", "\u5546\u54C1", "\u30AB\u30C6\u30B4\u30EA"]))
754
+ themes.push("Product/category mix analysis");
755
+ if (hasPattern(["customer", "user", "account", "\u9867\u5BA2", "\u30E6\u30FC\u30B6\u30FC"]))
756
+ themes.push("Customer segmentation and behavior analysis");
757
+ if (hasPattern(["quantity", "count", "volume", "\u6570\u91CF", "\u4EF6\u6570"]))
758
+ themes.push("Volume and demand pattern analysis");
759
+ if (hasPattern(["rate", "ratio", "percentage", "%", "\u7387"]))
760
+ themes.push("KPI ratio tracking and benchmarking");
761
+ if (themes.length > 0) {
762
+ sections.push("### Suggested Analysis Themes", "");
763
+ for (const theme of themes) {
764
+ sections.push(`- ${theme}`);
765
+ }
766
+ sections.push("");
599
767
  }
600
- sections.push("");
601
768
  }
602
769
  return sections.join("\n");
603
770
  }
@@ -607,27 +774,36 @@ var tableauSetupFlow = {
607
774
  import { z } from "zod";
608
775
  var DEFAULT_API_VERSION3 = "3.28";
609
776
  var REQUEST_TIMEOUT_MS = 6e4;
610
- async function fetchTableauSession(connectionId) {
611
- const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
612
- const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
613
- if (!token || !sandboxId) {
614
- throw new Error(
615
- "Tableau session manager is not configured. Missing INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL or INTERNAL_SQUADBASE_SANDBOX_ID."
616
- );
777
+ var cachedToken = null;
778
+ async function getProxyToken(config) {
779
+ if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
780
+ return cachedToken.token;
617
781
  }
618
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
619
- const url = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
782
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
620
783
  const res = await fetch(url, {
621
784
  method: "POST",
622
- headers: { Authorization: `Bearer ${token}` }
785
+ headers: {
786
+ "Content-Type": "application/json",
787
+ "x-api-key": config.appApiKey,
788
+ "project-id": config.projectId
789
+ },
790
+ body: JSON.stringify({
791
+ sandboxId: config.sandboxId,
792
+ issuedBy: "coding-agent"
793
+ })
623
794
  });
624
795
  if (!res.ok) {
625
- const errBody = await res.text().catch(() => res.statusText);
796
+ const errorText = await res.text().catch(() => res.statusText);
626
797
  throw new Error(
627
- `Failed to fetch Tableau session from backend-api (HTTP ${res.status}): ${errBody}`
798
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
628
799
  );
629
800
  }
630
- return await res.json();
801
+ const data = await res.json();
802
+ cachedToken = {
803
+ token: data.token,
804
+ expiresAt: new Date(data.expiresAt).getTime()
805
+ };
806
+ return data.token;
631
807
  }
632
808
  function buildBaseUrl(serverUrl, apiVersion) {
633
809
  return `${serverUrl.replace(/\/$/, "")}/api/${apiVersion}`;
@@ -669,7 +845,7 @@ All paths are relative to {serverUrl}/api/{apiVersion}. Use the literal placehol
669
845
  Accept and Content-Type headers default to application/json so list responses come back as JSON instead of Tableau's default XML.`,
670
846
  inputSchema,
671
847
  outputSchema,
672
- async execute({ connectionId, method, path: path2, queryParams, body }, connections) {
848
+ async execute({ connectionId, method, path: path2, queryParams, body }, connections, config) {
673
849
  const connection2 = connections.find((c) => c.id === connectionId);
674
850
  if (!connection2) {
675
851
  return {
@@ -684,50 +860,42 @@ Accept and Content-Type headers default to application/json so list responses co
684
860
  const serverUrl = parameters.serverUrl.getValue(connection2);
685
861
  const apiVersion = parameters.apiVersion.tryGetValue(connection2) || DEFAULT_API_VERSION3;
686
862
  const trimmedPath = path2.trim().replace(/^([^/])/, "/$1");
687
- const queryString = queryParams ? `?${new URLSearchParams(queryParams).toString()}` : "";
688
863
  const baseUrl = buildBaseUrl(serverUrl, apiVersion);
864
+ let url = `${baseUrl}${trimmedPath}`;
865
+ if (queryParams) {
866
+ url += `?${new URLSearchParams(queryParams).toString()}`;
867
+ }
868
+ const token = await getProxyToken(config.oauthProxy);
869
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
689
870
  const controller = new AbortController();
690
871
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
691
872
  try {
692
- let attempt = 0;
693
- while (true) {
694
- const session = await fetchTableauSession(connectionId);
695
- const resolvedPath = trimmedPath.replace(
696
- /\{siteId\}/g,
697
- session.siteId
698
- );
699
- const url = `${baseUrl}${resolvedPath}${queryString}`;
700
- const init = {
873
+ const response = await fetch(proxyUrl, {
874
+ method: "POST",
875
+ headers: {
876
+ "Content-Type": "application/json",
877
+ Authorization: `Bearer ${token}`
878
+ },
879
+ body: JSON.stringify({
880
+ url,
701
881
  method,
702
- headers: {
703
- "X-Tableau-Auth": session.authToken,
704
- Accept: "application/json",
705
- "Content-Type": "application/json"
706
- },
707
- signal: controller.signal
708
- };
709
- if (body !== void 0) {
710
- init.body = JSON.stringify(body);
711
- }
712
- const response = await fetch(url, init);
713
- const text = await response.text();
714
- const data = text ? (() => {
715
- try {
716
- return JSON.parse(text);
717
- } catch {
718
- return text;
719
- }
720
- })() : null;
721
- if (response.status === 401 && attempt === 0) {
722
- attempt++;
723
- continue;
724
- }
725
- if (!response.ok) {
726
- const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
727
- return { success: false, error: errorMessage };
882
+ ...body !== void 0 ? { body } : {}
883
+ }),
884
+ signal: controller.signal
885
+ });
886
+ const text = await response.text();
887
+ const data = text ? (() => {
888
+ try {
889
+ return JSON.parse(text);
890
+ } catch {
891
+ return text;
728
892
  }
729
- return { success: true, status: response.status, data };
893
+ })() : null;
894
+ if (!response.ok) {
895
+ const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
896
+ return { success: false, error: errorMessage };
730
897
  }
898
+ return { success: true, status: response.status, data };
731
899
  } finally {
732
900
  clearTimeout(timeout);
733
901
  }
@@ -235,19 +235,34 @@ async function runSetupFlow(flow, params, ctx, config) {
235
235
  };
236
236
  let state = flow.initialState();
237
237
  let answerIdx = 0;
238
+ const pendingParameterUpdates = [];
238
239
  for (const step of flow.steps) {
239
240
  const ans = ctx.answers[answerIdx];
240
241
  if (ans && ans.questionSlug === step.slug) {
241
242
  state = step.applyAnswer(state, ans.answer);
243
+ if (step.toParameterUpdates) {
244
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
245
+ }
242
246
  answerIdx += 1;
243
247
  continue;
244
248
  }
249
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
245
250
  if (step.type === "text") {
251
+ if (step.fetchOptions) {
252
+ const options2 = await step.fetchOptions(state, runtime);
253
+ if (options2.length === 0) {
254
+ continue;
255
+ }
256
+ }
246
257
  return {
247
258
  type: "nextQuestion",
248
259
  questionSlug: step.slug,
249
260
  question: step.question[ctx.language],
250
- questionType: "text"
261
+ questionType: "text",
262
+ allowFreeText: resolvedAllowFreeText,
263
+ ...pendingParameterUpdates.length > 0 && {
264
+ parameterUpdates: pendingParameterUpdates
265
+ }
251
266
  };
252
267
  }
253
268
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -259,11 +274,21 @@ async function runSetupFlow(flow, params, ctx, config) {
259
274
  questionSlug: step.slug,
260
275
  question: step.question[ctx.language],
261
276
  questionType: step.type,
262
- options
277
+ options,
278
+ allowFreeText: resolvedAllowFreeText,
279
+ ...pendingParameterUpdates.length > 0 && {
280
+ parameterUpdates: pendingParameterUpdates
281
+ }
263
282
  };
264
283
  }
265
284
  const dataInvestigationResult = await flow.finalize(state, runtime);
266
- return { type: "fulfilled", dataInvestigationResult };
285
+ return {
286
+ type: "fulfilled",
287
+ dataInvestigationResult,
288
+ ...pendingParameterUpdates.length > 0 && {
289
+ parameterUpdates: pendingParameterUpdates
290
+ }
291
+ };
267
292
  }
268
293
  async function resolveSetupSelection(params) {
269
294
  const { selected, allSentinel, fetchAll, limit } = params;
@@ -383,19 +383,34 @@ async function runSetupFlow(flow, params, ctx, config) {
383
383
  };
384
384
  let state = flow.initialState();
385
385
  let answerIdx = 0;
386
+ const pendingParameterUpdates = [];
386
387
  for (const step of flow.steps) {
387
388
  const ans = ctx.answers[answerIdx];
388
389
  if (ans && ans.questionSlug === step.slug) {
389
390
  state = step.applyAnswer(state, ans.answer);
391
+ if (step.toParameterUpdates) {
392
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
393
+ }
390
394
  answerIdx += 1;
391
395
  continue;
392
396
  }
397
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
393
398
  if (step.type === "text") {
399
+ if (step.fetchOptions) {
400
+ const options2 = await step.fetchOptions(state, runtime);
401
+ if (options2.length === 0) {
402
+ continue;
403
+ }
404
+ }
394
405
  return {
395
406
  type: "nextQuestion",
396
407
  questionSlug: step.slug,
397
408
  question: step.question[ctx.language],
398
- questionType: "text"
409
+ questionType: "text",
410
+ allowFreeText: resolvedAllowFreeText,
411
+ ...pendingParameterUpdates.length > 0 && {
412
+ parameterUpdates: pendingParameterUpdates
413
+ }
399
414
  };
400
415
  }
401
416
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -407,11 +422,21 @@ async function runSetupFlow(flow, params, ctx, config) {
407
422
  questionSlug: step.slug,
408
423
  question: step.question[ctx.language],
409
424
  questionType: step.type,
410
- options
425
+ options,
426
+ allowFreeText: resolvedAllowFreeText,
427
+ ...pendingParameterUpdates.length > 0 && {
428
+ parameterUpdates: pendingParameterUpdates
429
+ }
411
430
  };
412
431
  }
413
432
  const dataInvestigationResult = await flow.finalize(state, runtime);
414
- return { type: "fulfilled", dataInvestigationResult };
433
+ return {
434
+ type: "fulfilled",
435
+ dataInvestigationResult,
436
+ ...pendingParameterUpdates.length > 0 && {
437
+ parameterUpdates: pendingParameterUpdates
438
+ }
439
+ };
415
440
  }
416
441
 
417
442
  // ../connectors/src/auth-types.ts
@@ -178,19 +178,34 @@ async function runSetupFlow(flow, params, ctx, config) {
178
178
  };
179
179
  let state = flow.initialState();
180
180
  let answerIdx = 0;
181
+ const pendingParameterUpdates = [];
181
182
  for (const step of flow.steps) {
182
183
  const ans = ctx.answers[answerIdx];
183
184
  if (ans && ans.questionSlug === step.slug) {
184
185
  state = step.applyAnswer(state, ans.answer);
186
+ if (step.toParameterUpdates) {
187
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
188
+ }
185
189
  answerIdx += 1;
186
190
  continue;
187
191
  }
192
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
188
193
  if (step.type === "text") {
194
+ if (step.fetchOptions) {
195
+ const options2 = await step.fetchOptions(state, runtime);
196
+ if (options2.length === 0) {
197
+ continue;
198
+ }
199
+ }
189
200
  return {
190
201
  type: "nextQuestion",
191
202
  questionSlug: step.slug,
192
203
  question: step.question[ctx.language],
193
- questionType: "text"
204
+ questionType: "text",
205
+ allowFreeText: resolvedAllowFreeText,
206
+ ...pendingParameterUpdates.length > 0 && {
207
+ parameterUpdates: pendingParameterUpdates
208
+ }
194
209
  };
195
210
  }
196
211
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -202,11 +217,21 @@ async function runSetupFlow(flow, params, ctx, config) {
202
217
  questionSlug: step.slug,
203
218
  question: step.question[ctx.language],
204
219
  questionType: step.type,
205
- options
220
+ options,
221
+ allowFreeText: resolvedAllowFreeText,
222
+ ...pendingParameterUpdates.length > 0 && {
223
+ parameterUpdates: pendingParameterUpdates
224
+ }
206
225
  };
207
226
  }
208
227
  const dataInvestigationResult = await flow.finalize(state, runtime);
209
- return { type: "fulfilled", dataInvestigationResult };
228
+ return {
229
+ type: "fulfilled",
230
+ dataInvestigationResult,
231
+ ...pendingParameterUpdates.length > 0 && {
232
+ parameterUpdates: pendingParameterUpdates
233
+ }
234
+ };
210
235
  }
211
236
  async function resolveSetupSelection(params) {
212
237
  const { selected, allSentinel, fetchAll, limit } = params;