@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
@@ -361,19 +361,28 @@ async function runSetupFlow(flow, params, ctx, config) {
361
361
  };
362
362
  let state = flow.initialState();
363
363
  let answerIdx = 0;
364
+ const pendingParameterUpdates = [];
364
365
  for (const step of flow.steps) {
365
366
  const ans = ctx.answers[answerIdx];
366
367
  if (ans && ans.questionSlug === step.slug) {
367
368
  state = step.applyAnswer(state, ans.answer);
369
+ if (step.toParameterUpdates) {
370
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
371
+ }
368
372
  answerIdx += 1;
369
373
  continue;
370
374
  }
375
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
371
376
  if (step.type === "text") {
372
377
  return {
373
378
  type: "nextQuestion",
374
379
  questionSlug: step.slug,
375
380
  question: step.question[ctx.language],
376
- questionType: "text"
381
+ questionType: "text",
382
+ allowFreeText: resolvedAllowFreeText,
383
+ ...pendingParameterUpdates.length > 0 && {
384
+ parameterUpdates: pendingParameterUpdates
385
+ }
377
386
  };
378
387
  }
379
388
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -385,11 +394,21 @@ async function runSetupFlow(flow, params, ctx, config) {
385
394
  questionSlug: step.slug,
386
395
  question: step.question[ctx.language],
387
396
  questionType: step.type,
388
- options
397
+ options,
398
+ allowFreeText: resolvedAllowFreeText,
399
+ ...pendingParameterUpdates.length > 0 && {
400
+ parameterUpdates: pendingParameterUpdates
401
+ }
389
402
  };
390
403
  }
391
404
  const dataInvestigationResult = await flow.finalize(state, runtime);
392
- return { type: "fulfilled", dataInvestigationResult };
405
+ return {
406
+ type: "fulfilled",
407
+ dataInvestigationResult,
408
+ ...pendingParameterUpdates.length > 0 && {
409
+ parameterUpdates: pendingParameterUpdates
410
+ }
411
+ };
393
412
  }
394
413
  async function resolveSetupSelection(params) {
395
414
  const { selected, allSentinel, fetchAll, limit } = params;
@@ -593,7 +612,8 @@ var semrushOnboarding = new ConnectorOnboarding({
593
612
  });
594
613
 
595
614
  // ../connectors/src/connectors/semrush/utils.ts
596
- var PROJECTS_BASE_URL = "https://api.semrush.com/management/v1";
615
+ var BASE_URL3 = "https://api.semrush.com";
616
+ var PROJECTS_BASE_URL = `${BASE_URL3}/management/v1`;
597
617
  function projectsApiFetch(params, path2, init) {
598
618
  const apiKey = params[parameters.apiKey.slug];
599
619
  if (!apiKey) {
@@ -610,10 +630,103 @@ function projectsApiFetch(params, path2, init) {
610
630
  if (!headers.has("Accept")) headers.set("Accept", "application/json");
611
631
  return fetch(url.toString(), { ...init, headers });
612
632
  }
633
+ async function reportFetch(params, query) {
634
+ const apiKey = params[parameters.apiKey.slug];
635
+ if (!apiKey) {
636
+ throw new Error(
637
+ `semrush: missing required parameter: ${parameters.apiKey.slug}`
638
+ );
639
+ }
640
+ const url = new URL(`${BASE_URL3}/`);
641
+ for (const [k, v] of Object.entries(query)) {
642
+ url.searchParams.set(k, v);
643
+ }
644
+ url.searchParams.set("key", apiKey);
645
+ const res = await fetch(url.toString());
646
+ const text = await res.text();
647
+ if (text.startsWith("ERROR ")) throw new Error(text.trim());
648
+ return parseSemicolonCsv(text);
649
+ }
650
+ async function backlinksFetch(params, query) {
651
+ const apiKey = params[parameters.apiKey.slug];
652
+ if (!apiKey) {
653
+ throw new Error(
654
+ `semrush: missing required parameter: ${parameters.apiKey.slug}`
655
+ );
656
+ }
657
+ const url = new URL(`${BASE_URL3}/analytics/v1/`);
658
+ for (const [k, v] of Object.entries(query)) {
659
+ url.searchParams.set(k, v);
660
+ }
661
+ url.searchParams.set("key", apiKey);
662
+ const res = await fetch(url.toString());
663
+ const text = await res.text();
664
+ if (text.startsWith("ERROR ")) throw new Error(text.trim());
665
+ return parseSemicolonCsv(text);
666
+ }
667
+ function parseSemicolonCsv(raw) {
668
+ const lines = raw.trim().split("\n").filter(Boolean);
669
+ if (lines.length === 0) return { columns: [], rows: [] };
670
+ const columns = lines[0].split(";");
671
+ const rows = lines.slice(1).map((line) => {
672
+ const values = line.split(";");
673
+ const row = {};
674
+ for (let i = 0; i < columns.length; i++) {
675
+ row[columns[i]] = values[i] ?? "";
676
+ }
677
+ return row;
678
+ });
679
+ return { columns, rows };
680
+ }
613
681
 
614
682
  // ../connectors/src/connectors/semrush/setup-flow.ts
615
683
  var ALL_PROJECTS = "__ALL_PROJECTS__";
616
684
  var SEMRUSH_SETUP_MAX_PROJECTS = 10;
685
+ var SEMRUSH_DATABASES = [
686
+ { value: "us", label: "United States" },
687
+ { value: "uk", label: "United Kingdom" },
688
+ { value: "ca", label: "Canada" },
689
+ { value: "au", label: "Australia" },
690
+ { value: "de", label: "Germany" },
691
+ { value: "fr", label: "France" },
692
+ { value: "es", label: "Spain" },
693
+ { value: "it", label: "Italy" },
694
+ { value: "br", label: "Brazil" },
695
+ { value: "jp", label: "Japan" },
696
+ { value: "in", label: "India" },
697
+ { value: "ru", label: "Russia" },
698
+ { value: "nl", label: "Netherlands" },
699
+ { value: "se", label: "Sweden" },
700
+ { value: "mx", label: "Mexico" },
701
+ { value: "kr", label: "South Korea" },
702
+ { value: "sg", label: "Singapore" },
703
+ { value: "hk", label: "Hong Kong" },
704
+ { value: "tw", label: "Taiwan" }
705
+ ];
706
+ var REPORT_TYPE_LABELS = {
707
+ domain_overview: {
708
+ en: "Domain Overview (rank, traffic, keyword count)",
709
+ ja: "Domain Overview (\u30E9\u30F3\u30AF\u30FB\u30C8\u30E9\u30D5\u30A3\u30C3\u30AF\u30FB\u30AD\u30FC\u30EF\u30FC\u30C9\u6570)"
710
+ },
711
+ organic_search: {
712
+ en: "Organic Search (top organic keywords)",
713
+ ja: "Organic Search (\u4E0A\u4F4D\u30AA\u30FC\u30AC\u30CB\u30C3\u30AF\u30AD\u30FC\u30EF\u30FC\u30C9)"
714
+ },
715
+ paid_search: {
716
+ en: "Paid Search (top paid keywords)",
717
+ ja: "Paid Search (\u4E0A\u4F4D\u6709\u6599\u30AD\u30FC\u30EF\u30FC\u30C9)"
718
+ },
719
+ backlinks: {
720
+ en: "Backlinks (referring domains, backlink count)",
721
+ ja: "Backlinks (\u53C2\u7167\u30C9\u30E1\u30A4\u30F3\u30FB\u88AB\u30EA\u30F3\u30AF\u6570)"
722
+ }
723
+ };
724
+ var REPORT_TYPE_VALUES = [
725
+ "domain_overview",
726
+ "organic_search",
727
+ "paid_search",
728
+ "backlinks"
729
+ ];
617
730
  function projectId(p) {
618
731
  const raw = p.project_id ?? p.id;
619
732
  return raw == null ? "" : String(raw);
@@ -622,7 +735,7 @@ function projectName(p) {
622
735
  return p.project_name ?? p.name ?? p.domain ?? p.url ?? projectId(p);
623
736
  }
624
737
  function projectDomain(p) {
625
- return p.domain ?? p.url ?? "";
738
+ return p.domain ?? p.domain_unicode ?? p.url ?? "";
626
739
  }
627
740
  function projectCreatedAt(p) {
628
741
  return p.created_at ?? p.date_created ?? "";
@@ -630,13 +743,19 @@ function projectCreatedAt(p) {
630
743
  async function fetchProjects(params) {
631
744
  let res;
632
745
  try {
633
- res = await projectsApiFetch(params, "/projects/");
746
+ res = await projectsApiFetch(params, "/projects");
634
747
  } catch (err) {
635
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
748
+ return {
749
+ ok: false,
750
+ error: err instanceof Error ? err.message : String(err)
751
+ };
636
752
  }
637
753
  if (!res.ok) {
638
754
  const body2 = await res.text().catch(() => res.statusText);
639
- return { ok: false, error: `HTTP ${res.status} ${body2 || res.statusText}` };
755
+ return {
756
+ ok: false,
757
+ error: `HTTP ${res.status} ${body2 || res.statusText}`
758
+ };
640
759
  }
641
760
  let body;
642
761
  try {
@@ -650,9 +769,171 @@ async function fetchProjects(params) {
650
769
  const projects = Array.isArray(body) ? body : Array.isArray(body?.projects) ? body.projects : Array.isArray(body?.data) ? body.data : [];
651
770
  return { ok: true, projects };
652
771
  }
772
+ function formatNumber(n) {
773
+ return n.toLocaleString("en-US");
774
+ }
775
+ async function fetchDomainOverview(params, domain, database) {
776
+ const sections = [];
777
+ try {
778
+ const report = await reportFetch(params, {
779
+ type: "domain_ranks",
780
+ domain,
781
+ database
782
+ });
783
+ if (report.rows.length === 0) {
784
+ sections.push("_No data found for this domain._", "");
785
+ return sections;
786
+ }
787
+ const row = report.rows[0];
788
+ sections.push("| Metric | Value |");
789
+ sections.push("|--------|-------|");
790
+ sections.push(
791
+ `| Rank | ${row["Rank"] ?? "-"} |`,
792
+ `| Organic Keywords | ${formatNumber(Number(row["Organic Keywords"]) || 0)} |`,
793
+ `| Organic Traffic | ${formatNumber(Number(row["Organic Traffic"]) || 0)} |`,
794
+ `| Organic Cost | $${formatNumber(Number(row["Organic Cost"]) || 0)} |`,
795
+ `| Adwords Keywords | ${formatNumber(Number(row["Adwords Keywords"]) || 0)} |`,
796
+ `| Adwords Traffic | ${formatNumber(Number(row["Adwords Traffic"]) || 0)} |`,
797
+ `| Adwords Cost | $${formatNumber(Number(row["Adwords Cost"]) || 0)} |`
798
+ );
799
+ sections.push("");
800
+ } catch (err) {
801
+ sections.push(
802
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
803
+ ""
804
+ );
805
+ }
806
+ return sections;
807
+ }
808
+ async function fetchOrganicSearch(params, domain, database) {
809
+ return fetchKeywordReport(params, "domain_organic", domain, database);
810
+ }
811
+ async function fetchPaidSearch(params, domain, database) {
812
+ return fetchKeywordReport(params, "domain_adwords", domain, database);
813
+ }
814
+ async function fetchKeywordReport(params, type, domain, database) {
815
+ const sections = [];
816
+ try {
817
+ const report = await reportFetch(params, {
818
+ type,
819
+ domain,
820
+ database,
821
+ display_limit: "5"
822
+ });
823
+ if (report.rows.length === 0) {
824
+ sections.push("_No data found._", "");
825
+ return sections;
826
+ }
827
+ sections.push(...renderCsvTable(report, 5));
828
+ sections.push("");
829
+ } catch (err) {
830
+ sections.push(
831
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
832
+ ""
833
+ );
834
+ }
835
+ return sections;
836
+ }
837
+ async function fetchBacklinks(params, domain) {
838
+ const sections = [];
839
+ try {
840
+ const report = await backlinksFetch(params, {
841
+ type: "backlinks_overview",
842
+ target: domain,
843
+ target_type: "root_domain"
844
+ });
845
+ if (report.rows.length === 0) {
846
+ sections.push("_No backlink data found._", "");
847
+ return sections;
848
+ }
849
+ const row = report.rows[0];
850
+ sections.push("| Metric | Value |");
851
+ sections.push("|--------|-------|");
852
+ for (const col of report.columns) {
853
+ sections.push(`| ${col} | ${formatNumber(Number(row[col]) || 0)} |`);
854
+ }
855
+ sections.push("");
856
+ } catch (err) {
857
+ sections.push(
858
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
859
+ ""
860
+ );
861
+ }
862
+ return sections;
863
+ }
864
+ function renderCsvTable(report, maxRows) {
865
+ const cols = report.columns;
866
+ const rows = report.rows.slice(0, maxRows);
867
+ const lines = [];
868
+ lines.push(`| ${cols.join(" | ")} |`);
869
+ lines.push(`|${cols.map(() => "---").join("|")}|`);
870
+ for (const row of rows) {
871
+ const cells = cols.map((c) => (row[c] ?? "").replace(/\|/g, "\\|"));
872
+ lines.push(`| ${cells.join(" | ")} |`);
873
+ }
874
+ return lines;
875
+ }
653
876
  var semrushSetupFlow = {
654
877
  initialState: () => ({}),
655
878
  steps: [
879
+ {
880
+ slug: "domain",
881
+ type: "select",
882
+ allowFreeText: true,
883
+ question: {
884
+ ja: "\u5206\u6790\u5BFE\u8C61\u306E\u30C9\u30E1\u30A4\u30F3\u3092\u9078\u629E\u307E\u305F\u306F\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
885
+ en: "Select or enter the domain to analyze"
886
+ },
887
+ async fetchOptions(_state, rt) {
888
+ const result = await fetchProjects(rt.params);
889
+ if (!result.ok || result.projects.length === 0) return [];
890
+ const seen = /* @__PURE__ */ new Set();
891
+ const options = [];
892
+ for (const p of result.projects) {
893
+ const d = projectDomain(p);
894
+ if (d && !seen.has(d)) {
895
+ seen.add(d);
896
+ options.push({
897
+ value: d,
898
+ label: `${d} (${projectName(p)})`
899
+ });
900
+ }
901
+ }
902
+ return options;
903
+ },
904
+ applyAnswer: (state, answer) => ({ ...state, domain: answer[0] })
905
+ },
906
+ {
907
+ slug: "database",
908
+ type: "select",
909
+ allowFreeText: false,
910
+ question: {
911
+ ja: "\u30EA\u30FC\u30B8\u30E7\u30F3\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u5BFE\u8C61\u30C9\u30E1\u30A4\u30F3\u306E\u4E3B\u8981\u5E02\u5834\uFF09",
912
+ en: "Select the region (primary market for the target domain)"
913
+ },
914
+ async fetchOptions() {
915
+ return SEMRUSH_DATABASES.map((db) => ({
916
+ value: db.value,
917
+ label: `${db.label} (${db.value})`
918
+ }));
919
+ },
920
+ applyAnswer: (state, answer) => ({ ...state, database: answer[0] })
921
+ },
922
+ {
923
+ slug: "reportTypes",
924
+ type: "multiSelect",
925
+ question: {
926
+ ja: "\u63A2\u7D22\u3059\u308B\u30EC\u30DD\u30FC\u30C8\u7A2E\u5225\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
927
+ en: "Select report types to explore (multi-select allowed)"
928
+ },
929
+ async fetchOptions(_state, rt) {
930
+ return REPORT_TYPE_VALUES.map((value) => ({
931
+ value,
932
+ label: REPORT_TYPE_LABELS[value][rt.language]
933
+ }));
934
+ },
935
+ applyAnswer: (state, answer) => ({ ...state, reportTypes: answer })
936
+ },
656
937
  {
657
938
  slug: "projects",
658
939
  type: "multiSelect",
@@ -669,7 +950,9 @@ var semrushSetupFlow = {
669
950
  const id = projectId(p);
670
951
  if (!id) return null;
671
952
  return { value: id, label: projectName(p) };
672
- }).filter((opt) => opt !== null);
953
+ }).filter(
954
+ (opt) => opt !== null
955
+ );
673
956
  if (options.length === 0) return [];
674
957
  return [
675
958
  {
@@ -683,49 +966,80 @@ var semrushSetupFlow = {
683
966
  }
684
967
  ],
685
968
  async finalize(state, rt) {
686
- const sections = ["## Semrush", ""];
687
- if (!state.projects?.length) {
969
+ if (!state.domain || !state.database || !state.reportTypes) {
970
+ throw new Error("Semrush setup: incomplete state on finalize");
971
+ }
972
+ const domain = state.domain;
973
+ const database = state.database;
974
+ const selected = state.reportTypes.filter(
975
+ (r) => REPORT_TYPE_VALUES.includes(r)
976
+ );
977
+ const sections = [
978
+ "## Semrush",
979
+ "",
980
+ `**Domain:** ${domain}`,
981
+ `**Region:** ${database}`,
982
+ ""
983
+ ];
984
+ for (const reportType of selected) {
688
985
  sections.push(
689
- "_No Semrush Projects API access detected; Standard Analytics reports run by domain/keyword and don't require pre-selection._",
986
+ `### ${REPORT_TYPE_LABELS[reportType].en}`,
690
987
  ""
691
988
  );
692
- return sections.join("\n");
693
- }
694
- const result = await fetchProjects(rt.params);
695
- if (!result.ok) {
696
- throw new Error(
697
- `semrush: listProjects failed on finalize: ${result.error}`
698
- );
699
- }
700
- const projectByIdMap = /* @__PURE__ */ new Map();
701
- for (const p of result.projects) {
702
- const id = projectId(p);
703
- if (id) projectByIdMap.set(id, p);
704
- }
705
- const targetIds = await resolveSetupSelection({
706
- selected: state.projects,
707
- allSentinel: ALL_PROJECTS,
708
- fetchAll: async () => result.projects.map((p) => projectId(p)).filter((id) => id),
709
- limit: SEMRUSH_SETUP_MAX_PROJECTS
710
- });
711
- if (!targetIds.length) {
712
- sections.push("_No projects selected._", "");
713
- return sections.join("\n");
989
+ switch (reportType) {
990
+ case "domain_overview":
991
+ sections.push(
992
+ ...await fetchDomainOverview(rt.params, domain, database)
993
+ );
994
+ break;
995
+ case "organic_search":
996
+ sections.push(
997
+ ...await fetchOrganicSearch(rt.params, domain, database)
998
+ );
999
+ break;
1000
+ case "paid_search":
1001
+ sections.push(
1002
+ ...await fetchPaidSearch(rt.params, domain, database)
1003
+ );
1004
+ break;
1005
+ case "backlinks":
1006
+ sections.push(...await fetchBacklinks(rt.params, domain));
1007
+ break;
1008
+ }
714
1009
  }
715
- sections.push("| Project | Domain | Created |");
716
- sections.push("|---------|--------|---------|");
717
- for (const id of targetIds) {
718
- const p = projectByIdMap.get(id);
719
- if (!p) {
720
- sections.push(`| ${id} | - | - |`);
721
- continue;
1010
+ if (state.projects?.length) {
1011
+ const result = await fetchProjects(rt.params);
1012
+ if (result.ok) {
1013
+ const projectByIdMap = /* @__PURE__ */ new Map();
1014
+ for (const p of result.projects) {
1015
+ const id = projectId(p);
1016
+ if (id) projectByIdMap.set(id, p);
1017
+ }
1018
+ const targetIds = await resolveSetupSelection({
1019
+ selected: state.projects,
1020
+ allSentinel: ALL_PROJECTS,
1021
+ fetchAll: async () => result.projects.map((p) => projectId(p)).filter((id) => id),
1022
+ limit: SEMRUSH_SETUP_MAX_PROJECTS
1023
+ });
1024
+ if (targetIds.length > 0) {
1025
+ sections.push("### Projects", "");
1026
+ sections.push("| Project | Domain | Created |");
1027
+ sections.push("|---------|--------|---------|");
1028
+ for (const id of targetIds) {
1029
+ const p = projectByIdMap.get(id);
1030
+ if (!p) {
1031
+ sections.push(`| ${id} | - | - |`);
1032
+ continue;
1033
+ }
1034
+ const name = projectName(p).replace(/\|/g, "\\|");
1035
+ const dom = projectDomain(p).replace(/\|/g, "\\|") || "-";
1036
+ const created = projectCreatedAt(p) || "-";
1037
+ sections.push(`| ${name} | ${dom} | ${created} |`);
1038
+ }
1039
+ sections.push("");
1040
+ }
722
1041
  }
723
- const name = projectName(p).replace(/\|/g, "\\|");
724
- const domain = projectDomain(p).replace(/\|/g, "\\|") || "-";
725
- const created = projectCreatedAt(p) || "-";
726
- sections.push(`| ${name} | ${domain} | ${created} |`);
727
1042
  }
728
- sections.push("");
729
1043
  return sections.join("\n");
730
1044
  }
731
1045
  };
@@ -352,19 +352,28 @@ async function runSetupFlow(flow, params, ctx, config) {
352
352
  };
353
353
  let state = flow.initialState();
354
354
  let answerIdx = 0;
355
+ const pendingParameterUpdates = [];
355
356
  for (const step of flow.steps) {
356
357
  const ans = ctx.answers[answerIdx];
357
358
  if (ans && ans.questionSlug === step.slug) {
358
359
  state = step.applyAnswer(state, ans.answer);
360
+ if (step.toParameterUpdates) {
361
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
362
+ }
359
363
  answerIdx += 1;
360
364
  continue;
361
365
  }
366
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
362
367
  if (step.type === "text") {
363
368
  return {
364
369
  type: "nextQuestion",
365
370
  questionSlug: step.slug,
366
371
  question: step.question[ctx.language],
367
- questionType: "text"
372
+ questionType: "text",
373
+ allowFreeText: resolvedAllowFreeText,
374
+ ...pendingParameterUpdates.length > 0 && {
375
+ parameterUpdates: pendingParameterUpdates
376
+ }
368
377
  };
369
378
  }
370
379
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -376,11 +385,21 @@ async function runSetupFlow(flow, params, ctx, config) {
376
385
  questionSlug: step.slug,
377
386
  question: step.question[ctx.language],
378
387
  questionType: step.type,
379
- options
388
+ options,
389
+ allowFreeText: resolvedAllowFreeText,
390
+ ...pendingParameterUpdates.length > 0 && {
391
+ parameterUpdates: pendingParameterUpdates
392
+ }
380
393
  };
381
394
  }
382
395
  const dataInvestigationResult = await flow.finalize(state, runtime);
383
- return { type: "fulfilled", dataInvestigationResult };
396
+ return {
397
+ type: "fulfilled",
398
+ dataInvestigationResult,
399
+ ...pendingParameterUpdates.length > 0 && {
400
+ parameterUpdates: pendingParameterUpdates
401
+ }
402
+ };
384
403
  }
385
404
  async function resolveSetupSelection(params) {
386
405
  const { selected, allSentinel, fetchAll, limit } = params;
@@ -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
 
212
231
  // ../connectors/src/auth-types.ts
@@ -465,19 +465,28 @@ async function runSetupFlow(flow, params, ctx, config) {
465
465
  };
466
466
  let state = flow.initialState();
467
467
  let answerIdx = 0;
468
+ const pendingParameterUpdates = [];
468
469
  for (const step of flow.steps) {
469
470
  const ans = ctx.answers[answerIdx];
470
471
  if (ans && ans.questionSlug === step.slug) {
471
472
  state = step.applyAnswer(state, ans.answer);
473
+ if (step.toParameterUpdates) {
474
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
475
+ }
472
476
  answerIdx += 1;
473
477
  continue;
474
478
  }
479
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
475
480
  if (step.type === "text") {
476
481
  return {
477
482
  type: "nextQuestion",
478
483
  questionSlug: step.slug,
479
484
  question: step.question[ctx.language],
480
- questionType: "text"
485
+ questionType: "text",
486
+ allowFreeText: resolvedAllowFreeText,
487
+ ...pendingParameterUpdates.length > 0 && {
488
+ parameterUpdates: pendingParameterUpdates
489
+ }
481
490
  };
482
491
  }
483
492
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -489,11 +498,21 @@ async function runSetupFlow(flow, params, ctx, config) {
489
498
  questionSlug: step.slug,
490
499
  question: step.question[ctx.language],
491
500
  questionType: step.type,
492
- options
501
+ options,
502
+ allowFreeText: resolvedAllowFreeText,
503
+ ...pendingParameterUpdates.length > 0 && {
504
+ parameterUpdates: pendingParameterUpdates
505
+ }
493
506
  };
494
507
  }
495
508
  const dataInvestigationResult = await flow.finalize(state, runtime);
496
- return { type: "fulfilled", dataInvestigationResult };
509
+ return {
510
+ type: "fulfilled",
511
+ dataInvestigationResult,
512
+ ...pendingParameterUpdates.length > 0 && {
513
+ parameterUpdates: pendingParameterUpdates
514
+ }
515
+ };
497
516
  }
498
517
 
499
518
  // ../connectors/src/auth-types.ts