@squadbase/vite-server 0.1.17-dev.24af54e → 0.1.17-dev.7408ec4

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