@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
@@ -792,19 +792,28 @@ async function runSetupFlow(flow, params, ctx, config) {
792
792
  };
793
793
  let state = flow.initialState();
794
794
  let answerIdx = 0;
795
+ const pendingParameterUpdates = [];
795
796
  for (const step of flow.steps) {
796
797
  const ans = ctx.answers[answerIdx];
797
798
  if (ans && ans.questionSlug === step.slug) {
798
799
  state = step.applyAnswer(state, ans.answer);
800
+ if (step.toParameterUpdates) {
801
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
802
+ }
799
803
  answerIdx += 1;
800
804
  continue;
801
805
  }
806
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
802
807
  if (step.type === "text") {
803
808
  return {
804
809
  type: "nextQuestion",
805
810
  questionSlug: step.slug,
806
811
  question: step.question[ctx.language],
807
- questionType: "text"
812
+ questionType: "text",
813
+ allowFreeText: resolvedAllowFreeText,
814
+ ...pendingParameterUpdates.length > 0 && {
815
+ parameterUpdates: pendingParameterUpdates
816
+ }
808
817
  };
809
818
  }
810
819
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -816,11 +825,21 @@ async function runSetupFlow(flow, params, ctx, config) {
816
825
  questionSlug: step.slug,
817
826
  question: step.question[ctx.language],
818
827
  questionType: step.type,
819
- options
828
+ options,
829
+ allowFreeText: resolvedAllowFreeText,
830
+ ...pendingParameterUpdates.length > 0 && {
831
+ parameterUpdates: pendingParameterUpdates
832
+ }
820
833
  };
821
834
  }
822
835
  const dataInvestigationResult = await flow.finalize(state, runtime);
823
- return { type: "fulfilled", dataInvestigationResult };
836
+ return {
837
+ type: "fulfilled",
838
+ dataInvestigationResult,
839
+ ...pendingParameterUpdates.length > 0 && {
840
+ parameterUpdates: pendingParameterUpdates
841
+ }
842
+ };
824
843
  }
825
844
  async function resolveSetupSelection(params) {
826
845
  const { selected, allSentinel, fetchAll, limit } = params;
@@ -999,7 +1018,46 @@ var INTERNAL_SCHEMAS = /* @__PURE__ */ new Set([
999
1018
  "READER_ACCOUNT_USAGE",
1000
1019
  "DATA_SHARING_USAGE"
1001
1020
  ]);
1021
+ var OBJECT_TYPE_LABELS = {
1022
+ table: { ja: "\u30C6\u30FC\u30D6\u30EB", en: "Table" },
1023
+ view: { ja: "\u30D3\u30E5\u30FC", en: "View" },
1024
+ stream: { ja: "\u30B9\u30C8\u30EA\u30FC\u30E0", en: "Stream" },
1025
+ dynamic_table: { ja: "\u30C0\u30A4\u30CA\u30DF\u30C3\u30AF\u30C6\u30FC\u30D6\u30EB", en: "Dynamic Table" },
1026
+ pipe: { ja: "\u30D1\u30A4\u30D7", en: "Pipe" },
1027
+ task: { ja: "\u30BF\u30B9\u30AF", en: "Task" }
1028
+ };
1029
+ var HEADING_LABELS = {
1030
+ table: "Table",
1031
+ view: "View",
1032
+ stream: "Stream",
1033
+ dynamic_table: "Dynamic Table",
1034
+ pipe: "Pipe",
1035
+ task: "Task"
1036
+ };
1002
1037
  function createSnowflakeSetupFlow(runQuery11) {
1038
+ const objectTypes = /* @__PURE__ */ new Map();
1039
+ async function fetchAllObjects(params, db, schema) {
1040
+ objectTypes.clear();
1041
+ const typeQueries = [
1042
+ ["table", `SHOW TABLES IN SCHEMA "${db}"."${schema}"`],
1043
+ ["view", `SHOW VIEWS IN SCHEMA "${db}"."${schema}"`],
1044
+ ["stream", `SHOW STREAMS IN SCHEMA "${db}"."${schema}"`],
1045
+ ["dynamic_table", `SHOW DYNAMIC TABLES IN SCHEMA "${db}"."${schema}"`],
1046
+ ["pipe", `SHOW PIPES IN SCHEMA "${db}"."${schema}"`],
1047
+ ["task", `SHOW TASKS IN SCHEMA "${db}"."${schema}"`]
1048
+ ];
1049
+ const results = await Promise.all(
1050
+ typeQueries.map(([, sql]) => runQuery11(params, sql).catch(() => []))
1051
+ );
1052
+ for (let i = 0; i < results.length; i++) {
1053
+ const type = typeQueries[i][0];
1054
+ for (const r of results[i]) {
1055
+ const name = String(r["name"] ?? "");
1056
+ if (name) objectTypes.set(name, type);
1057
+ }
1058
+ }
1059
+ return Array.from(objectTypes.keys());
1060
+ }
1003
1061
  return {
1004
1062
  initialState: () => ({}),
1005
1063
  steps: [
@@ -1039,22 +1097,27 @@ function createSnowflakeSetupFlow(runQuery11) {
1039
1097
  slug: "tables",
1040
1098
  type: "multiSelect",
1041
1099
  question: {
1042
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
1043
- en: "Select target tables (multi-select allowed)"
1100
+ ja: "\u5BFE\u8C61\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u30FB\u30B9\u30C8\u30EA\u30FC\u30E0\u7B49\u3001\u8907\u6570\u9078\u629E\u53EF\uFF09",
1101
+ en: "Select target objects (tables, views, streams, etc. \u2014 multi-select allowed)"
1044
1102
  },
1045
1103
  async fetchOptions(state, rt) {
1046
1104
  if (!state.database || !state.schema) return [];
1047
- const rows = await runQuery11(
1105
+ const allNames = await fetchAllObjects(
1048
1106
  rt.params,
1049
- `SHOW TABLES IN SCHEMA "${state.database}"."${state.schema}"`
1107
+ state.database,
1108
+ state.schema
1050
1109
  );
1051
- const tableOptions = rows.map((r) => String(r["name"] ?? "")).filter((name) => name).map((value) => ({ value }));
1110
+ const options = allNames.map((name) => {
1111
+ const type = objectTypes.get(name) ?? "table";
1112
+ const typeLabel = OBJECT_TYPE_LABELS[type][rt.language];
1113
+ return { value: name, label: `${name} (${typeLabel})` };
1114
+ });
1052
1115
  return [
1053
1116
  {
1054
1117
  value: ALL_TABLES,
1055
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
1118
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8" : "All objects"
1056
1119
  },
1057
- ...tableOptions
1120
+ ...options
1058
1121
  ];
1059
1122
  },
1060
1123
  applyAnswer: (state, answer) => ({ ...state, tables: answer })
@@ -1066,16 +1129,10 @@ function createSnowflakeSetupFlow(runQuery11) {
1066
1129
  }
1067
1130
  const db = state.database;
1068
1131
  const schema = state.schema;
1069
- const targetTables = await resolveSetupSelection({
1132
+ const targetObjects = await resolveSetupSelection({
1070
1133
  selected: state.tables,
1071
1134
  allSentinel: ALL_TABLES,
1072
- fetchAll: async () => {
1073
- const rows = await runQuery11(
1074
- rt.params,
1075
- `SHOW TABLES IN SCHEMA "${db}"."${schema}"`
1076
- );
1077
- return rows.map((r) => String(r["name"] ?? "")).filter((name) => name);
1078
- },
1135
+ fetchAll: () => fetchAllObjects(rt.params, db, schema),
1079
1136
  limit: SNOWFLAKE_SETUP_MAX_TABLES
1080
1137
  });
1081
1138
  const sections = [
@@ -1086,24 +1143,66 @@ function createSnowflakeSetupFlow(runQuery11) {
1086
1143
  `#### Schema: ${schema}`,
1087
1144
  ""
1088
1145
  ];
1089
- for (const table of targetTables) {
1090
- const cols = await runQuery11(
1091
- rt.params,
1092
- `DESCRIBE TABLE "${db}"."${schema}"."${table}"`
1093
- );
1094
- sections.push(`##### Table: ${table}`, "");
1095
- sections.push("| Column | Type | Nullable | Default |");
1096
- sections.push("|--------|------|----------|---------|");
1097
- for (const c of cols) {
1098
- const name = String(c["name"] ?? "");
1099
- const type = String(c["type"] ?? "");
1100
- const nullable = String(c["null?"] ?? "");
1101
- const defaultValue = c["default"] == null ? "-" : String(c["default"]);
1102
- sections.push(
1103
- `| ${name} | ${type} | ${nullable} | ${defaultValue} |`
1104
- );
1146
+ for (const name of targetObjects) {
1147
+ const type = objectTypes.get(name) ?? "table";
1148
+ const heading = HEADING_LABELS[type];
1149
+ switch (type) {
1150
+ case "table":
1151
+ case "view":
1152
+ case "dynamic_table":
1153
+ case "stream": {
1154
+ const cols = await runQuery11(
1155
+ rt.params,
1156
+ `DESCRIBE TABLE "${db}"."${schema}"."${name}"`
1157
+ ).catch(() => []);
1158
+ sections.push(`##### ${heading}: ${name}`, "");
1159
+ if (cols.length > 0) {
1160
+ sections.push("| Column | Type | Nullable | Default |");
1161
+ sections.push("|--------|------|----------|---------|");
1162
+ for (const c of cols) {
1163
+ const colName = String(c["name"] ?? "");
1164
+ const colType = String(c["type"] ?? "");
1165
+ const nullable = String(c["null?"] ?? "");
1166
+ const defaultValue = c["default"] == null ? "-" : String(c["default"]);
1167
+ sections.push(
1168
+ `| ${colName} | ${colType} | ${nullable} | ${defaultValue} |`
1169
+ );
1170
+ }
1171
+ }
1172
+ sections.push("");
1173
+ break;
1174
+ }
1175
+ case "pipe": {
1176
+ const rows = await runQuery11(
1177
+ rt.params,
1178
+ `SHOW PIPES LIKE '${name.replace(/'/g, "''")}' IN SCHEMA "${db}"."${schema}"`
1179
+ ).catch(() => []);
1180
+ sections.push(`##### Pipe: ${name}`, "");
1181
+ if (rows.length > 0) {
1182
+ const definition = String(rows[0]["definition"] ?? "-");
1183
+ sections.push(`Definition: \`${definition}\``, "");
1184
+ }
1185
+ sections.push("");
1186
+ break;
1187
+ }
1188
+ case "task": {
1189
+ const rows = await runQuery11(
1190
+ rt.params,
1191
+ `SHOW TASKS LIKE '${name.replace(/'/g, "''")}' IN SCHEMA "${db}"."${schema}"`
1192
+ ).catch(() => []);
1193
+ sections.push(`##### Task: ${name}`, "");
1194
+ if (rows.length > 0) {
1195
+ const schedule = String(rows[0]["schedule"] ?? "-");
1196
+ const taskState = String(rows[0]["state"] ?? "-");
1197
+ const definition = String(rows[0]["definition"] ?? "-");
1198
+ sections.push(`Schedule: ${schedule}`, "");
1199
+ sections.push(`State: ${taskState}`, "");
1200
+ sections.push(`Definition: \`${definition}\``, "");
1201
+ }
1202
+ sections.push("");
1203
+ break;
1204
+ }
1105
1205
  }
1106
- sections.push("");
1107
1206
  }
1108
1207
  return sections.join("\n");
1109
1208
  }
@@ -1916,8 +2015,8 @@ var postgresqlSetupFlow = {
1916
2015
  slug: "tables",
1917
2016
  type: "multiSelect",
1918
2017
  question: {
1919
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
1920
- en: "Select target tables (multi-select allowed)"
2018
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
2019
+ en: "Select target tables and views (multi-select allowed)"
1921
2020
  },
1922
2021
  async fetchOptions(state, rt) {
1923
2022
  if (!state.schema) return [];
@@ -1926,7 +2025,7 @@ var postgresqlSetupFlow = {
1926
2025
  return [
1927
2026
  {
1928
2027
  value: ALL_TABLES2,
1929
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
2028
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
1930
2029
  },
1931
2030
  ...tableOptions
1932
2031
  ];
@@ -2263,8 +2362,8 @@ var mysqlSetupFlow = {
2263
2362
  slug: "tables",
2264
2363
  type: "multiSelect",
2265
2364
  question: {
2266
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
2267
- en: "Select target tables (multi-select allowed)"
2365
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
2366
+ en: "Select target tables and views (multi-select allowed)"
2268
2367
  },
2269
2368
  async fetchOptions(state, rt) {
2270
2369
  if (!state.database) return [];
@@ -2273,7 +2372,7 @@ var mysqlSetupFlow = {
2273
2372
  return [
2274
2373
  {
2275
2374
  value: ALL_TABLES3,
2276
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
2375
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
2277
2376
  },
2278
2377
  ...tableOptions
2279
2378
  ];
@@ -2724,6 +2823,72 @@ var bigqueryOnboarding = new ConnectorOnboarding({
2724
2823
  }
2725
2824
  });
2726
2825
 
2826
+ // ../connectors/src/table-grouping.ts
2827
+ var SHARD_GROUP_PREFIX = "__SHARD__";
2828
+ var DEFAULT_MIN_SHARD_GROUP_SIZE = 3;
2829
+ function extractDateShardSuffix(name) {
2830
+ const m = name.match(/^(.+)_(\d{8})$/);
2831
+ if (!m) return null;
2832
+ const [, prefix, suffix] = m;
2833
+ const y = +suffix.slice(0, 4);
2834
+ const mo = +suffix.slice(4, 6);
2835
+ const d = +suffix.slice(6, 8);
2836
+ if (y < 2e3 || y > 2099 || mo < 1 || mo > 12 || d < 1 || d > 31) {
2837
+ return null;
2838
+ }
2839
+ return { prefix, suffix };
2840
+ }
2841
+ function detectDateShardCandidates(tableNames, minCount = DEFAULT_MIN_SHARD_GROUP_SIZE) {
2842
+ const prefixMap = /* @__PURE__ */ new Map();
2843
+ const nonSharded = [];
2844
+ for (const name of tableNames) {
2845
+ const parsed = extractDateShardSuffix(name);
2846
+ if (parsed) {
2847
+ const existing = prefixMap.get(parsed.prefix);
2848
+ if (existing) {
2849
+ existing.push(name);
2850
+ } else {
2851
+ prefixMap.set(parsed.prefix, [name]);
2852
+ }
2853
+ } else {
2854
+ nonSharded.push(name);
2855
+ }
2856
+ }
2857
+ const candidates = [];
2858
+ const singles = [...nonSharded];
2859
+ for (const [prefix, members] of prefixMap) {
2860
+ if (members.length >= minCount) {
2861
+ candidates.push({ prefix, count: members.length, members });
2862
+ } else {
2863
+ singles.push(...members);
2864
+ }
2865
+ }
2866
+ return { singles, candidates };
2867
+ }
2868
+ function schemasMatch(columnsA, columnsB) {
2869
+ if (columnsA.length !== columnsB.length) return false;
2870
+ return columnsA.every(
2871
+ (a, i) => String(a["column_name"] ?? "") === String(columnsB[i]["column_name"] ?? "") && String(a["data_type"] ?? "") === String(columnsB[i]["data_type"] ?? "")
2872
+ );
2873
+ }
2874
+ function isShardGroupValue(value) {
2875
+ return value.startsWith(SHARD_GROUP_PREFIX);
2876
+ }
2877
+ function getShardGroupPrefix(value) {
2878
+ return value.slice(SHARD_GROUP_PREFIX.length);
2879
+ }
2880
+ function shardGroupValue(prefix) {
2881
+ return `${SHARD_GROUP_PREFIX}${prefix}`;
2882
+ }
2883
+ function buildGroupedTableOptions(singles, confirmedGroups, language) {
2884
+ const groupOptions = confirmedGroups.sort((a, b) => a.prefix.localeCompare(b.prefix)).map((g) => ({
2885
+ value: shardGroupValue(g.prefix),
2886
+ label: language === "ja" ? `${g.prefix}_* (\u65E5\u4ED8\u30B7\u30E3\u30FC\u30C9: ${g.count} \u30C6\u30FC\u30D6\u30EB)` : `${g.prefix}_* (date-sharded: ${g.count} tables)`
2887
+ }));
2888
+ const singleOptions = singles.sort().map((name) => ({ value: name }));
2889
+ return [...groupOptions, ...singleOptions];
2890
+ }
2891
+
2727
2892
  // ../connectors/src/connectors/bigquery/utils.ts
2728
2893
  async function runQuery5(params, sql) {
2729
2894
  const { BigQuery } = await import("@google-cloud/bigquery");
@@ -2754,13 +2919,23 @@ async function listProjects(params) {
2754
2919
  scopes: ["https://www.googleapis.com/auth/bigquery"]
2755
2920
  });
2756
2921
  const client = await auth.getClient();
2757
- const res = await client.request({
2758
- url: "https://bigquery.googleapis.com/bigquery/v2/projects"
2759
- });
2760
- return (res.data.projects ?? []).map((p) => ({
2761
- projectId: p.projectReference?.projectId ?? "",
2762
- friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
2763
- })).filter((p) => p.projectId !== "");
2922
+ const projects = [];
2923
+ let pageToken;
2924
+ do {
2925
+ const url = pageToken ? `https://bigquery.googleapis.com/bigquery/v2/projects?pageToken=${encodeURIComponent(pageToken)}` : "https://bigquery.googleapis.com/bigquery/v2/projects";
2926
+ const res = await client.request({ url });
2927
+ for (const p of res.data.projects ?? []) {
2928
+ const id = p.projectReference?.projectId ?? "";
2929
+ if (id) {
2930
+ projects.push({
2931
+ projectId: id,
2932
+ friendlyName: p.friendlyName ?? id
2933
+ });
2934
+ }
2935
+ }
2936
+ pageToken = res.data.nextPageToken;
2937
+ } while (pageToken);
2938
+ return projects;
2764
2939
  }
2765
2940
  async function listDatasets(params, projectId2) {
2766
2941
  const { BigQuery } = await import("@google-cloud/bigquery");
@@ -2798,23 +2973,25 @@ var PUBLIC_DATASETS = [
2798
2973
  labelEn: "bigquery-public-data.austin_311 (public dataset / customer service requests)"
2799
2974
  }
2800
2975
  ];
2976
+ var SAME_AS_BILLING = "__SAME_AS_BILLING__";
2801
2977
  function resolveDatasetRef(state) {
2802
2978
  const dataset = state.dataset ?? "";
2803
2979
  if (dataset.includes(".")) {
2804
2980
  const [datasetProject, ...rest] = dataset.split(".");
2805
2981
  return { datasetProject, datasetId: rest.join(".") };
2806
2982
  }
2807
- return { datasetProject: state.project ?? "", datasetId: dataset };
2983
+ return { datasetProject: state.databaseProject ?? "", datasetId: dataset };
2808
2984
  }
2809
2985
  var bigquerySetupFlow = {
2810
2986
  initialState: () => ({}),
2811
2987
  steps: [
2812
2988
  {
2813
- slug: "project",
2989
+ slug: "billingProject",
2814
2990
  type: "select",
2991
+ allowFreeText: false,
2815
2992
  question: {
2816
- ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u306B\u4F7F\u3046 GCP \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u30D1\u30D6\u30EA\u30C3\u30AF\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u3092\u4F7F\u3046\u5834\u5408\u3082\u8AB2\u91D1\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3068\u3057\u3066\u5FC5\u8981\u3067\u3059\uFF09",
2817
- en: "Select the GCP project to use for setup (also required as the billing project when querying public datasets)"
2993
+ ja: "BigQuery \u3092\u5B9F\u884C\u3059\u308B Google Cloud \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08BigQuery \u306E\u8AB2\u91D1\u306B\u7D10\u3065\u304F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3067\u3059\uFF09",
2994
+ en: "Select the Google Cloud project to run BigQuery queries (this project will be billed for BigQuery usage)"
2818
2995
  },
2819
2996
  async fetchOptions(_state, rt) {
2820
2997
  const projects = await listProjects(rt.params);
@@ -2823,7 +3000,40 @@ var bigquerySetupFlow = {
2823
3000
  label: p.friendlyName && p.friendlyName !== p.projectId ? `${p.friendlyName} (${p.projectId})` : p.projectId
2824
3001
  }));
2825
3002
  },
2826
- applyAnswer: (state, answer) => ({ ...state, project: answer[0] })
3003
+ applyAnswer: (state, answer) => ({
3004
+ ...state,
3005
+ billingProject: answer[0]
3006
+ }),
3007
+ toParameterUpdates: (state) => state.billingProject ? [{ slug: "project-id", value: state.billingProject }] : []
3008
+ },
3009
+ {
3010
+ slug: "databaseProject",
3011
+ type: "select",
3012
+ question: {
3013
+ ja: "\u30C7\u30FC\u30BF\u304C\u683C\u7D0D\u3055\u308C\u3066\u3044\u308B GCP \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044",
3014
+ en: "Select the GCP project where your data resides"
3015
+ },
3016
+ async fetchOptions(state, rt) {
3017
+ if (!state.billingProject) return [];
3018
+ const sameAsBillingOption = {
3019
+ value: SAME_AS_BILLING,
3020
+ label: rt.language === "ja" ? `\u8AB2\u91D1\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3068\u540C\u3058 (${state.billingProject})` : `Same as billing project (${state.billingProject})`
3021
+ };
3022
+ const projects = await listProjects(rt.params);
3023
+ const projectOptions = projects.filter((p) => p.projectId !== state.billingProject).map((p) => ({
3024
+ value: p.projectId,
3025
+ label: p.friendlyName && p.friendlyName !== p.projectId ? `${p.friendlyName} (${p.projectId})` : p.projectId
3026
+ }));
3027
+ const publicProjectOption = {
3028
+ value: "bigquery-public-data",
3029
+ label: rt.language === "ja" ? "bigquery-public-data (Google \u30D1\u30D6\u30EA\u30C3\u30AF\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8)" : "bigquery-public-data (Google public datasets)"
3030
+ };
3031
+ return [sameAsBillingOption, ...projectOptions, publicProjectOption];
3032
+ },
3033
+ applyAnswer: (state, answer) => ({
3034
+ ...state,
3035
+ databaseProject: answer[0] === SAME_AS_BILLING ? state.billingProject : answer[0]
3036
+ })
2827
3037
  },
2828
3038
  {
2829
3039
  slug: "dataset",
@@ -2833,8 +3043,8 @@ var bigquerySetupFlow = {
2833
3043
  en: "Select the dataset to use for setup"
2834
3044
  },
2835
3045
  async fetchOptions(state, rt) {
2836
- if (!state.project) return [];
2837
- const datasets = await listDatasets(rt.params, state.project);
3046
+ if (!state.databaseProject) return [];
3047
+ const datasets = await listDatasets(rt.params, state.databaseProject);
2838
3048
  const projectDatasetOptions = datasets.map((d) => ({
2839
3049
  value: d.datasetId,
2840
3050
  label: d.location ? `${d.datasetId} (${d.location})` : d.datasetId
@@ -2851,66 +3061,140 @@ var bigquerySetupFlow = {
2851
3061
  slug: "tables",
2852
3062
  type: "multiSelect",
2853
3063
  question: {
2854
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
2855
- en: "Select target tables (multi-select allowed)"
3064
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
3065
+ en: "Select target tables and views (multi-select allowed)"
2856
3066
  },
2857
3067
  async fetchOptions(state, rt) {
2858
- if (!state.project || !state.dataset) return [];
3068
+ if (!state.billingProject || !state.dataset) return [];
2859
3069
  const { datasetProject, datasetId } = resolveDatasetRef(state);
2860
3070
  const rows = await runQuery5(
2861
3071
  rt.params,
2862
3072
  `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
2863
3073
  );
2864
- const tableOptions = rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name).map((value) => ({ value }));
3074
+ const tableNames = rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3075
+ const { singles, candidates } = detectDateShardCandidates(tableNames);
3076
+ const confirmedGroups = [];
3077
+ const rejectedSingles = [];
3078
+ for (const candidate of candidates) {
3079
+ const [first, second] = candidate.members;
3080
+ const colRows = await runQuery5(
3081
+ rt.params,
3082
+ `SELECT table_name, column_name, data_type FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name IN ('${first.replaceAll("'", "''")}', '${second.replaceAll("'", "''")}') ORDER BY table_name, ordinal_position`
3083
+ );
3084
+ const colsA = colRows.filter(
3085
+ (r) => String(r["table_name"]) === first
3086
+ );
3087
+ const colsB = colRows.filter(
3088
+ (r) => String(r["table_name"]) === second
3089
+ );
3090
+ if (schemasMatch(colsA, colsB)) {
3091
+ confirmedGroups.push(candidate);
3092
+ } else {
3093
+ rejectedSingles.push(...candidate.members);
3094
+ }
3095
+ }
3096
+ const allSingles = [...singles, ...rejectedSingles];
2865
3097
  return [
2866
3098
  {
2867
3099
  value: ALL_TABLES4,
2868
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
3100
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
2869
3101
  },
2870
- ...tableOptions
3102
+ ...buildGroupedTableOptions(allSingles, confirmedGroups, rt.language)
2871
3103
  ];
2872
3104
  },
2873
3105
  applyAnswer: (state, answer) => ({ ...state, tables: answer })
2874
3106
  }
2875
3107
  ],
2876
3108
  async finalize(state, rt) {
2877
- if (!state.project || !state.dataset || !state.tables) {
3109
+ if (!state.billingProject || !state.databaseProject || !state.dataset || !state.tables) {
2878
3110
  throw new Error("BigQuery setup: incomplete state on finalize");
2879
3111
  }
2880
3112
  const { datasetProject, datasetId } = resolveDatasetRef(state);
2881
- const targetTables = await resolveSetupSelection({
2882
- selected: state.tables,
2883
- allSentinel: ALL_TABLES4,
2884
- fetchAll: async () => {
2885
- const rows = await runQuery5(
2886
- rt.params,
2887
- `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
2888
- );
2889
- return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
2890
- },
2891
- limit: BIGQUERY_SETUP_MAX_TABLES
2892
- });
3113
+ const fetchAllTableNames = async () => {
3114
+ const rows = await runQuery5(
3115
+ rt.params,
3116
+ `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
3117
+ );
3118
+ return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3119
+ };
3120
+ let entries;
3121
+ if (state.tables.includes(ALL_TABLES4)) {
3122
+ const allNames = await fetchAllTableNames();
3123
+ const { singles, candidates } = detectDateShardCandidates(allNames);
3124
+ entries = [
3125
+ ...candidates.map(
3126
+ (g) => ({
3127
+ type: "shardGroup",
3128
+ prefix: g.prefix,
3129
+ count: g.count,
3130
+ representative: g.members[0]
3131
+ })
3132
+ ),
3133
+ ...singles.map((name) => ({ type: "table", name }))
3134
+ ];
3135
+ } else {
3136
+ const shardSelections = state.tables.filter(isShardGroupValue);
3137
+ const tableSelections = state.tables.filter(
3138
+ (v) => !isShardGroupValue(v)
3139
+ );
3140
+ entries = tableSelections.map(
3141
+ (name) => ({ type: "table", name })
3142
+ );
3143
+ if (shardSelections.length > 0) {
3144
+ const allNames = await fetchAllTableNames();
3145
+ for (const value of shardSelections) {
3146
+ const prefix = getShardGroupPrefix(value);
3147
+ const members = allNames.filter((n) => {
3148
+ const parsed = extractDateShardSuffix(n);
3149
+ return parsed != null && parsed.prefix === prefix;
3150
+ });
3151
+ if (members.length > 0) {
3152
+ entries.push({
3153
+ type: "shardGroup",
3154
+ prefix,
3155
+ count: members.length,
3156
+ representative: members[0]
3157
+ });
3158
+ }
3159
+ }
3160
+ }
3161
+ }
3162
+ entries = entries.slice(0, BIGQUERY_SETUP_MAX_TABLES);
3163
+ const queryColumns = async (tableName) => runQuery5(
3164
+ rt.params,
3165
+ `SELECT column_name, data_type, is_nullable FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${tableName.replaceAll("'", "''")}' ORDER BY ordinal_position`
3166
+ );
2893
3167
  const sections = [
2894
3168
  "## BigQuery",
2895
3169
  "",
2896
- `### Billing project: ${state.project}`,
3170
+ `### Billing project: ${state.billingProject}`,
3171
+ "",
3172
+ `### Database project: ${state.databaseProject}`,
2897
3173
  "",
2898
3174
  `#### Dataset: ${datasetProject}.${datasetId}`,
2899
3175
  ""
2900
3176
  ];
2901
- for (const table of targetTables) {
2902
- const cols = await runQuery5(
2903
- rt.params,
2904
- `SELECT column_name, data_type, is_nullable FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${table.replaceAll("'", "''")}' ORDER BY ordinal_position`
2905
- );
2906
- sections.push(`##### Table: ${table}`, "");
3177
+ for (const entry of entries) {
3178
+ const tableName = entry.type === "shardGroup" ? entry.representative : entry.name;
3179
+ const cols = await queryColumns(tableName);
3180
+ if (entry.type === "shardGroup") {
3181
+ sections.push(
3182
+ `##### Table: ${entry.prefix}_* (date-sharded, ${entry.count} tables)`,
3183
+ ""
3184
+ );
3185
+ sections.push(
3186
+ `_Query all shards: \`SELECT * FROM \`${datasetProject}.${datasetId}.${entry.prefix}_*\` WHERE _TABLE_SUFFIX BETWEEN 'YYYYMMDD' AND 'YYYYMMDD'\`_`,
3187
+ ""
3188
+ );
3189
+ } else {
3190
+ sections.push(`##### Table: ${entry.name}`, "");
3191
+ }
2907
3192
  sections.push("| Column | Type | Nullable |");
2908
3193
  sections.push("|--------|------|----------|");
2909
3194
  for (const c of cols) {
2910
- const name = String(c["column_name"] ?? "");
2911
- const type = String(c["data_type"] ?? "");
2912
- const nullable = String(c["is_nullable"] ?? "");
2913
- sections.push(`| ${name} | ${type} | ${nullable} |`);
3195
+ sections.push(
3196
+ `| ${String(c["column_name"] ?? "")} | ${String(c["data_type"] ?? "")} | ${String(c["is_nullable"] ?? "")} |`
3197
+ );
2914
3198
  }
2915
3199
  sections.push("");
2916
3200
  }
@@ -3408,21 +3692,30 @@ async function runQuery6(proxyFetch, projectId2, sql) {
3408
3692
  return parseQueryResponse(data);
3409
3693
  }
3410
3694
  async function listProjects2(proxyFetch) {
3411
- const res = await proxyFetch(
3412
- "https://bigquery.googleapis.com/bigquery/v2/projects",
3413
- { method: "GET" }
3414
- );
3415
- if (!res.ok) {
3416
- const errorText = await res.text().catch(() => res.statusText);
3417
- throw new Error(
3418
- `BigQuery listProjects failed: HTTP ${res.status} ${errorText}`
3419
- );
3420
- }
3421
- const data = await res.json();
3422
- return (data.projects ?? []).map((p) => ({
3423
- projectId: p.projectReference?.projectId ?? "",
3424
- friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
3425
- })).filter((p) => p.projectId !== "");
3695
+ const projects = [];
3696
+ let pageToken;
3697
+ do {
3698
+ const url = pageToken ? `https://bigquery.googleapis.com/bigquery/v2/projects?pageToken=${encodeURIComponent(pageToken)}` : "https://bigquery.googleapis.com/bigquery/v2/projects";
3699
+ const res = await proxyFetch(url, { method: "GET" });
3700
+ if (!res.ok) {
3701
+ const errorText = await res.text().catch(() => res.statusText);
3702
+ throw new Error(
3703
+ `BigQuery listProjects failed: HTTP ${res.status} ${errorText}`
3704
+ );
3705
+ }
3706
+ const data = await res.json();
3707
+ for (const p of data.projects ?? []) {
3708
+ const id = p.projectReference?.projectId ?? "";
3709
+ if (id) {
3710
+ projects.push({
3711
+ projectId: id,
3712
+ friendlyName: p.friendlyName ?? id
3713
+ });
3714
+ }
3715
+ }
3716
+ pageToken = data.nextPageToken;
3717
+ } while (pageToken);
3718
+ return projects;
3426
3719
  }
3427
3720
  async function listDatasets2(proxyFetch, projectId2) {
3428
3721
  const res = await proxyFetch(
@@ -3462,23 +3755,25 @@ var PUBLIC_DATASETS2 = [
3462
3755
  labelEn: "bigquery-public-data.austin_311 (public dataset / customer service requests)"
3463
3756
  }
3464
3757
  ];
3758
+ var SAME_AS_BILLING2 = "__SAME_AS_BILLING__";
3465
3759
  function resolveDatasetRef2(state) {
3466
3760
  const dataset = state.dataset ?? "";
3467
3761
  if (dataset.includes(".")) {
3468
3762
  const [datasetProject, ...rest] = dataset.split(".");
3469
3763
  return { datasetProject, datasetId: rest.join(".") };
3470
3764
  }
3471
- return { datasetProject: state.project ?? "", datasetId: dataset };
3765
+ return { datasetProject: state.databaseProject ?? "", datasetId: dataset };
3472
3766
  }
3473
3767
  var bigqueryOauthSetupFlow = {
3474
3768
  initialState: () => ({}),
3475
3769
  steps: [
3476
3770
  {
3477
- slug: "project",
3771
+ slug: "billingProject",
3478
3772
  type: "select",
3773
+ allowFreeText: false,
3479
3774
  question: {
3480
- ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u306B\u4F7F\u3046 GCP \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u30D1\u30D6\u30EA\u30C3\u30AF\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u3092\u4F7F\u3046\u5834\u5408\u3082\u8AB2\u91D1\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3068\u3057\u3066\u5FC5\u8981\u3067\u3059\uFF09",
3481
- en: "Select the GCP project to use for setup (also required as the billing project when querying public datasets)"
3775
+ ja: "BigQuery \u3092\u5B9F\u884C\u3059\u308B Google Cloud \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08BigQuery \u306E\u8AB2\u91D1\u306B\u7D10\u3065\u304F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3067\u3059\uFF09",
3776
+ en: "Select the Google Cloud project to run BigQuery queries (this project will be billed for BigQuery usage)"
3482
3777
  },
3483
3778
  async fetchOptions(_state, rt) {
3484
3779
  const projects = await listProjects2(rt.config.proxyFetch);
@@ -3487,7 +3782,40 @@ var bigqueryOauthSetupFlow = {
3487
3782
  label: p.friendlyName && p.friendlyName !== p.projectId ? `${p.friendlyName} (${p.projectId})` : p.projectId
3488
3783
  }));
3489
3784
  },
3490
- applyAnswer: (state, answer) => ({ ...state, project: answer[0] })
3785
+ applyAnswer: (state, answer) => ({
3786
+ ...state,
3787
+ billingProject: answer[0]
3788
+ }),
3789
+ toParameterUpdates: (state) => state.billingProject ? [{ slug: "project-id", value: state.billingProject }] : []
3790
+ },
3791
+ {
3792
+ slug: "databaseProject",
3793
+ type: "select",
3794
+ question: {
3795
+ ja: "\u30C7\u30FC\u30BF\u304C\u683C\u7D0D\u3055\u308C\u3066\u3044\u308B GCP \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044",
3796
+ en: "Select the GCP project where your data resides"
3797
+ },
3798
+ async fetchOptions(state, rt) {
3799
+ if (!state.billingProject) return [];
3800
+ const sameAsBillingOption = {
3801
+ value: SAME_AS_BILLING2,
3802
+ label: rt.language === "ja" ? `\u8AB2\u91D1\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3068\u540C\u3058 (${state.billingProject})` : `Same as billing project (${state.billingProject})`
3803
+ };
3804
+ const projects = await listProjects2(rt.config.proxyFetch);
3805
+ const projectOptions = projects.filter((p) => p.projectId !== state.billingProject).map((p) => ({
3806
+ value: p.projectId,
3807
+ label: p.friendlyName && p.friendlyName !== p.projectId ? `${p.friendlyName} (${p.projectId})` : p.projectId
3808
+ }));
3809
+ const publicProjectOption = {
3810
+ value: "bigquery-public-data",
3811
+ label: rt.language === "ja" ? "bigquery-public-data (Google \u30D1\u30D6\u30EA\u30C3\u30AF\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8)" : "bigquery-public-data (Google public datasets)"
3812
+ };
3813
+ return [sameAsBillingOption, ...projectOptions, publicProjectOption];
3814
+ },
3815
+ applyAnswer: (state, answer) => ({
3816
+ ...state,
3817
+ databaseProject: answer[0] === SAME_AS_BILLING2 ? state.billingProject : answer[0]
3818
+ })
3491
3819
  },
3492
3820
  {
3493
3821
  slug: "dataset",
@@ -3497,8 +3825,11 @@ var bigqueryOauthSetupFlow = {
3497
3825
  en: "Select the dataset to use for setup"
3498
3826
  },
3499
3827
  async fetchOptions(state, rt) {
3500
- if (!state.project) return [];
3501
- const datasets = await listDatasets2(rt.config.proxyFetch, state.project);
3828
+ if (!state.databaseProject) return [];
3829
+ const datasets = await listDatasets2(
3830
+ rt.config.proxyFetch,
3831
+ state.databaseProject
3832
+ );
3502
3833
  const projectDatasetOptions = datasets.map((d) => ({
3503
3834
  value: d.datasetId,
3504
3835
  label: d.location ? `${d.datasetId} (${d.location})` : d.datasetId
@@ -3515,70 +3846,145 @@ var bigqueryOauthSetupFlow = {
3515
3846
  slug: "tables",
3516
3847
  type: "multiSelect",
3517
3848
  question: {
3518
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
3519
- en: "Select target tables (multi-select allowed)"
3849
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
3850
+ en: "Select target tables and views (multi-select allowed)"
3520
3851
  },
3521
3852
  async fetchOptions(state, rt) {
3522
- if (!state.project || !state.dataset) return [];
3853
+ if (!state.billingProject || !state.dataset) return [];
3523
3854
  const { datasetProject, datasetId } = resolveDatasetRef2(state);
3524
3855
  const rows = await runQuery6(
3525
3856
  rt.config.proxyFetch,
3526
- state.project,
3857
+ state.billingProject,
3527
3858
  `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
3528
3859
  );
3529
- const tableOptions = rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name).map((value) => ({ value }));
3860
+ const tableNames = rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3861
+ const { singles, candidates } = detectDateShardCandidates(tableNames);
3862
+ const confirmedGroups = [];
3863
+ const rejectedSingles = [];
3864
+ for (const candidate of candidates) {
3865
+ const [first, second] = candidate.members;
3866
+ const colRows = await runQuery6(
3867
+ rt.config.proxyFetch,
3868
+ state.billingProject,
3869
+ `SELECT table_name, column_name, data_type FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name IN ('${first.replaceAll("'", "''")}', '${second.replaceAll("'", "''")}') ORDER BY table_name, ordinal_position`
3870
+ );
3871
+ const colsA = colRows.filter(
3872
+ (r) => String(r["table_name"]) === first
3873
+ );
3874
+ const colsB = colRows.filter(
3875
+ (r) => String(r["table_name"]) === second
3876
+ );
3877
+ if (schemasMatch(colsA, colsB)) {
3878
+ confirmedGroups.push(candidate);
3879
+ } else {
3880
+ rejectedSingles.push(...candidate.members);
3881
+ }
3882
+ }
3883
+ const allSingles = [...singles, ...rejectedSingles];
3530
3884
  return [
3531
3885
  {
3532
3886
  value: ALL_TABLES5,
3533
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
3887
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
3534
3888
  },
3535
- ...tableOptions
3889
+ ...buildGroupedTableOptions(allSingles, confirmedGroups, rt.language)
3536
3890
  ];
3537
3891
  },
3538
3892
  applyAnswer: (state, answer) => ({ ...state, tables: answer })
3539
3893
  }
3540
3894
  ],
3541
3895
  async finalize(state, rt) {
3542
- if (!state.project || !state.dataset || !state.tables) {
3896
+ if (!state.billingProject || !state.databaseProject || !state.dataset || !state.tables) {
3543
3897
  throw new Error("BigQuery OAuth setup: incomplete state on finalize");
3544
3898
  }
3545
- const billingProject = state.project;
3899
+ const billingProject = state.billingProject;
3546
3900
  const { datasetProject, datasetId } = resolveDatasetRef2(state);
3547
- const targetTables = await resolveSetupSelection({
3548
- selected: state.tables,
3549
- allSentinel: ALL_TABLES5,
3550
- fetchAll: async () => {
3551
- const rows = await runQuery6(
3552
- rt.config.proxyFetch,
3553
- billingProject,
3554
- `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
3555
- );
3556
- return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3557
- },
3558
- limit: BIGQUERY_OAUTH_SETUP_MAX_TABLES
3559
- });
3901
+ const fetchAllTableNames = async () => {
3902
+ const rows = await runQuery6(
3903
+ rt.config.proxyFetch,
3904
+ billingProject,
3905
+ `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
3906
+ );
3907
+ return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3908
+ };
3909
+ let entries;
3910
+ if (state.tables.includes(ALL_TABLES5)) {
3911
+ const allNames = await fetchAllTableNames();
3912
+ const { singles, candidates } = detectDateShardCandidates(allNames);
3913
+ entries = [
3914
+ ...candidates.map(
3915
+ (g) => ({
3916
+ type: "shardGroup",
3917
+ prefix: g.prefix,
3918
+ count: g.count,
3919
+ representative: g.members[0]
3920
+ })
3921
+ ),
3922
+ ...singles.map((name) => ({ type: "table", name }))
3923
+ ];
3924
+ } else {
3925
+ const shardSelections = state.tables.filter(isShardGroupValue);
3926
+ const tableSelections = state.tables.filter(
3927
+ (v) => !isShardGroupValue(v)
3928
+ );
3929
+ entries = tableSelections.map(
3930
+ (name) => ({ type: "table", name })
3931
+ );
3932
+ if (shardSelections.length > 0) {
3933
+ const allNames = await fetchAllTableNames();
3934
+ for (const value of shardSelections) {
3935
+ const prefix = getShardGroupPrefix(value);
3936
+ const members = allNames.filter((n) => {
3937
+ const parsed = extractDateShardSuffix(n);
3938
+ return parsed != null && parsed.prefix === prefix;
3939
+ });
3940
+ if (members.length > 0) {
3941
+ entries.push({
3942
+ type: "shardGroup",
3943
+ prefix,
3944
+ count: members.length,
3945
+ representative: members[0]
3946
+ });
3947
+ }
3948
+ }
3949
+ }
3950
+ }
3951
+ entries = entries.slice(0, BIGQUERY_OAUTH_SETUP_MAX_TABLES);
3952
+ const queryColumns = async (tableName) => runQuery6(
3953
+ rt.config.proxyFetch,
3954
+ billingProject,
3955
+ `SELECT column_name, data_type, is_nullable FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${tableName.replaceAll("'", "''")}' ORDER BY ordinal_position`
3956
+ );
3560
3957
  const sections = [
3561
3958
  "## BigQuery (OAuth)",
3562
3959
  "",
3563
3960
  `### Billing project: ${billingProject}`,
3564
3961
  "",
3962
+ `### Database project: ${state.databaseProject}`,
3963
+ "",
3565
3964
  `#### Dataset: ${datasetProject}.${datasetId}`,
3566
3965
  ""
3567
3966
  ];
3568
- for (const table of targetTables) {
3569
- const cols = await runQuery6(
3570
- rt.config.proxyFetch,
3571
- billingProject,
3572
- `SELECT column_name, data_type, is_nullable FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${table.replaceAll("'", "''")}' ORDER BY ordinal_position`
3573
- );
3574
- sections.push(`##### Table: ${table}`, "");
3967
+ for (const entry of entries) {
3968
+ const tableName = entry.type === "shardGroup" ? entry.representative : entry.name;
3969
+ const cols = await queryColumns(tableName);
3970
+ if (entry.type === "shardGroup") {
3971
+ sections.push(
3972
+ `##### Table: ${entry.prefix}_* (date-sharded, ${entry.count} tables)`,
3973
+ ""
3974
+ );
3975
+ sections.push(
3976
+ `_Query all shards: \`SELECT * FROM \`${datasetProject}.${datasetId}.${entry.prefix}_*\` WHERE _TABLE_SUFFIX BETWEEN 'YYYYMMDD' AND 'YYYYMMDD'\`_`,
3977
+ ""
3978
+ );
3979
+ } else {
3980
+ sections.push(`##### Table: ${entry.name}`, "");
3981
+ }
3575
3982
  sections.push("| Column | Type | Nullable |");
3576
3983
  sections.push("|--------|------|----------|");
3577
3984
  for (const c of cols) {
3578
- const name = String(c["column_name"] ?? "");
3579
- const type = String(c["data_type"] ?? "");
3580
- const nullable = String(c["is_nullable"] ?? "");
3581
- sections.push(`| ${name} | ${type} | ${nullable} |`);
3985
+ sections.push(
3986
+ `| ${String(c["column_name"] ?? "")} | ${String(c["data_type"] ?? "")} | ${String(c["is_nullable"] ?? "")} |`
3987
+ );
3582
3988
  }
3583
3989
  sections.push("");
3584
3990
  }
@@ -5287,14 +5693,15 @@ function isInternalSchema2(name) {
5287
5693
  function quoteLiteral(value) {
5288
5694
  return "'" + value.replace(/'/g, "''") + "'";
5289
5695
  }
5290
- async function fetchTableNames3(params, schema) {
5696
+ async function fetchTableAndViewNames(params, schema) {
5291
5697
  const rows = await runQuery7(
5292
5698
  params,
5293
- `SELECT tablename FROM pg_tables
5294
- WHERE schemaname = ${quoteLiteral(schema)}
5295
- ORDER BY tablename`
5699
+ `SELECT table_name FROM information_schema.tables
5700
+ WHERE table_schema = ${quoteLiteral(schema)}
5701
+ AND table_type IN ('BASE TABLE', 'VIEW')
5702
+ ORDER BY table_name`
5296
5703
  );
5297
- return rows.map((r) => String(r["tablename"] ?? "")).filter((name) => name);
5704
+ return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
5298
5705
  }
5299
5706
  var redshiftSetupFlow = {
5300
5707
  initialState: () => ({}),
@@ -5309,9 +5716,11 @@ var redshiftSetupFlow = {
5309
5716
  async fetchOptions(_state, rt) {
5310
5717
  const rows = await runQuery7(
5311
5718
  rt.params,
5312
- `SELECT DISTINCT schemaname FROM pg_tables ORDER BY schemaname`
5719
+ `SELECT DISTINCT table_schema FROM information_schema.tables
5720
+ WHERE table_type IN ('BASE TABLE', 'VIEW')
5721
+ ORDER BY table_schema`
5313
5722
  );
5314
- return rows.map((r) => String(r["schemaname"] ?? "")).filter((name) => name && !isInternalSchema2(name)).map((value) => ({ value }));
5723
+ return rows.map((r) => String(r["table_schema"] ?? "")).filter((name) => name && !isInternalSchema2(name)).map((value) => ({ value }));
5315
5724
  },
5316
5725
  applyAnswer: (state, answer) => ({ ...state, schema: answer[0] })
5317
5726
  },
@@ -5319,17 +5728,17 @@ var redshiftSetupFlow = {
5319
5728
  slug: "tables",
5320
5729
  type: "multiSelect",
5321
5730
  question: {
5322
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
5323
- en: "Select target tables (multi-select allowed)"
5731
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
5732
+ en: "Select target tables and views (multi-select allowed)"
5324
5733
  },
5325
5734
  async fetchOptions(state, rt) {
5326
5735
  if (!state.schema) return [];
5327
- const names = await fetchTableNames3(rt.params, state.schema);
5736
+ const names = await fetchTableAndViewNames(rt.params, state.schema);
5328
5737
  const tableOptions = names.map((value) => ({ value }));
5329
5738
  return [
5330
5739
  {
5331
5740
  value: ALL_TABLES7,
5332
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
5741
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
5333
5742
  },
5334
5743
  ...tableOptions
5335
5744
  ];
@@ -5345,9 +5754,21 @@ var redshiftSetupFlow = {
5345
5754
  const targetTables = await resolveSetupSelection({
5346
5755
  selected: state.tables,
5347
5756
  allSentinel: ALL_TABLES7,
5348
- fetchAll: () => fetchTableNames3(rt.params, schema),
5757
+ fetchAll: () => fetchTableAndViewNames(rt.params, schema),
5349
5758
  limit: REDSHIFT_SETUP_MAX_TABLES
5350
5759
  });
5760
+ const typeRows = targetTables.length > 0 ? await runQuery7(
5761
+ rt.params,
5762
+ `SELECT table_name, table_type FROM information_schema.tables
5763
+ WHERE table_schema = ${quoteLiteral(schema)}
5764
+ AND table_name IN (${targetTables.map(quoteLiteral).join(", ")})`
5765
+ ) : [];
5766
+ const typeMap = new Map(
5767
+ typeRows.map((r) => [
5768
+ String(r["table_name"] ?? ""),
5769
+ String(r["table_type"] ?? "BASE TABLE")
5770
+ ])
5771
+ );
5351
5772
  const sections = [
5352
5773
  "## Redshift",
5353
5774
  "",
@@ -5355,6 +5776,7 @@ var redshiftSetupFlow = {
5355
5776
  ""
5356
5777
  ];
5357
5778
  for (const table of targetTables) {
5779
+ const heading = typeMap.get(table) === "VIEW" ? "View" : "Table";
5358
5780
  const cols = await runQuery7(
5359
5781
  rt.params,
5360
5782
  `SELECT column_name, data_type, is_nullable, column_default
@@ -5363,7 +5785,7 @@ var redshiftSetupFlow = {
5363
5785
  AND table_name = ${quoteLiteral(table)}
5364
5786
  ORDER BY ordinal_position`
5365
5787
  );
5366
- sections.push(`#### Table: ${table}`, "");
5788
+ sections.push(`#### ${heading}: ${table}`, "");
5367
5789
  sections.push("| Column | Type | Nullable | Default |");
5368
5790
  sections.push("|--------|------|----------|---------|");
5369
5791
  for (const c of cols) {
@@ -5791,8 +6213,8 @@ var databricksSetupFlow = {
5791
6213
  slug: "tables",
5792
6214
  type: "multiSelect",
5793
6215
  question: {
5794
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
5795
- en: "Select target tables (multi-select allowed)"
6216
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
6217
+ en: "Select target tables and views (multi-select allowed)"
5796
6218
  },
5797
6219
  async fetchOptions(state, rt) {
5798
6220
  if (!state.catalog || !state.schema) return [];
@@ -5804,7 +6226,7 @@ var databricksSetupFlow = {
5804
6226
  return [
5805
6227
  {
5806
6228
  value: ALL_TABLES8,
5807
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
6229
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
5808
6230
  },
5809
6231
  ...tableOptions
5810
6232
  ];
@@ -7184,11 +7606,11 @@ var parameters13 = {
7184
7606
  propertyId: new ParameterDefinition({
7185
7607
  slug: "property-id",
7186
7608
  name: "Google Analytics Property ID",
7187
- description: "The Google Analytics 4 property ID (e.g., 123456789).",
7609
+ description: "The Google Analytics 4 property ID (e.g., 123456789). Automatically set during the setup flow.",
7188
7610
  envVarBaseKey: "GA_PROPERTY_ID",
7189
7611
  type: "text",
7190
7612
  secret: false,
7191
- required: true
7613
+ required: false
7192
7614
  })
7193
7615
  };
7194
7616
 
@@ -7196,6 +7618,7 @@ var parameters13 = {
7196
7618
  import * as crypto2 from "crypto";
7197
7619
  var TOKEN_URL = "https://oauth2.googleapis.com/token";
7198
7620
  var ADMIN_BASE_URL = "https://analyticsadmin.googleapis.com/v1beta";
7621
+ var DATA_BASE_URL = "https://analyticsdata.googleapis.com/v1beta";
7199
7622
  var SCOPE = "https://www.googleapis.com/auth/analytics.readonly";
7200
7623
  function base64url(input) {
7201
7624
  const buf = typeof input === "string" ? Buffer.from(input) : input;
@@ -7277,10 +7700,25 @@ async function adminFetch(params, path4, init) {
7277
7700
  headers.set("Authorization", `Bearer ${accessToken}`);
7278
7701
  return fetch(url, { ...init, headers });
7279
7702
  }
7703
+ async function dataFetch(params, path4, init) {
7704
+ const keyJsonBase64 = params[parameters13.serviceAccountKeyJsonBase64.slug];
7705
+ if (!keyJsonBase64) {
7706
+ throw new Error(
7707
+ "google-analytics: missing required parameter: service-account-key-json-base64"
7708
+ );
7709
+ }
7710
+ const serviceAccountKey = decodeServiceAccount(keyJsonBase64);
7711
+ const accessToken = await getAccessToken(serviceAccountKey);
7712
+ const url = `${DATA_BASE_URL}${path4.startsWith("/") ? "" : "/"}${path4}`;
7713
+ const headers = new Headers(init?.headers);
7714
+ headers.set("Authorization", `Bearer ${accessToken}`);
7715
+ return fetch(url, { ...init, headers });
7716
+ }
7280
7717
 
7281
7718
  // ../connectors/src/connectors/google-analytics/setup-flow.ts
7282
7719
  var ALL_PROPERTIES = "__ALL_PROPERTIES__";
7283
7720
  var GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES = 20;
7721
+ var METADATA_DISPLAY_LIMIT = 30;
7284
7722
  async function listAccountSummaries(params) {
7285
7723
  const summaries = [];
7286
7724
  let pageToken;
@@ -7307,6 +7745,48 @@ async function getProperty(params, propertyId) {
7307
7745
  if (!res.ok) return null;
7308
7746
  return await res.json();
7309
7747
  }
7748
+ async function getMetadata(params, propertyId) {
7749
+ try {
7750
+ const res = await dataFetch(
7751
+ params,
7752
+ `/properties/${propertyId}/metadata`
7753
+ );
7754
+ if (!res.ok) return { dimensions: [], metrics: [] };
7755
+ const data = await res.json();
7756
+ return {
7757
+ dimensions: data.dimensions ?? [],
7758
+ metrics: data.metrics ?? []
7759
+ };
7760
+ } catch {
7761
+ return { dimensions: [], metrics: [] };
7762
+ }
7763
+ }
7764
+ function appendMetadataSection(sections, dimensions, metrics) {
7765
+ if (dimensions.length > 0) {
7766
+ sections.push(`#### Dimensions (${dimensions.length})`, "");
7767
+ for (const d of dimensions.slice(0, METADATA_DISPLAY_LIMIT)) {
7768
+ sections.push(`- ${d.apiName ?? d.uiName ?? "(unknown)"}`);
7769
+ }
7770
+ if (dimensions.length > METADATA_DISPLAY_LIMIT) {
7771
+ sections.push(
7772
+ `- \u2026and ${dimensions.length - METADATA_DISPLAY_LIMIT} more`
7773
+ );
7774
+ }
7775
+ sections.push("");
7776
+ }
7777
+ if (metrics.length > 0) {
7778
+ sections.push(`#### Metrics (${metrics.length})`, "");
7779
+ for (const m of metrics.slice(0, METADATA_DISPLAY_LIMIT)) {
7780
+ sections.push(`- ${m.apiName ?? m.uiName ?? "(unknown)"}`);
7781
+ }
7782
+ if (metrics.length > METADATA_DISPLAY_LIMIT) {
7783
+ sections.push(
7784
+ `- \u2026and ${metrics.length - METADATA_DISPLAY_LIMIT} more`
7785
+ );
7786
+ }
7787
+ sections.push("");
7788
+ }
7789
+ }
7310
7790
  var googleAnalyticsSetupFlow = {
7311
7791
  initialState: () => ({}),
7312
7792
  steps: [
@@ -7318,15 +7798,19 @@ var googleAnalyticsSetupFlow = {
7318
7798
  en: "Select a Google Analytics account"
7319
7799
  },
7320
7800
  async fetchOptions(_state, rt) {
7321
- const summaries = await listAccountSummaries(rt.params);
7322
- return summaries.map((s) => {
7323
- const accountResource = s.account ?? s.name ?? "";
7324
- if (!accountResource) return null;
7325
- return {
7326
- value: accountResource,
7327
- label: s.displayName ?? accountResource
7328
- };
7329
- }).filter((v) => v != null);
7801
+ try {
7802
+ const summaries = await listAccountSummaries(rt.params);
7803
+ return summaries.map((s) => {
7804
+ const accountResource = s.account ?? s.name ?? "";
7805
+ if (!accountResource) return null;
7806
+ return {
7807
+ value: accountResource,
7808
+ label: s.displayName ?? accountResource
7809
+ };
7810
+ }).filter((v) => v != null);
7811
+ } catch {
7812
+ return [];
7813
+ }
7330
7814
  },
7331
7815
  applyAnswer: (state, answer) => ({ ...state, account: answer[0] })
7332
7816
  },
@@ -7339,68 +7823,134 @@ var googleAnalyticsSetupFlow = {
7339
7823
  },
7340
7824
  async fetchOptions(state, rt) {
7341
7825
  if (!state.account) return [];
7342
- const summaries = await listAccountSummaries(rt.params);
7343
- const account = summaries.find(
7344
- (s) => (s.account ?? s.name) === state.account
7345
- );
7346
- const props = (account?.propertySummaries ?? []).map((p) => {
7347
- const id = propertyIdFromResource(p.property);
7348
- if (!id) return null;
7349
- return {
7350
- value: id,
7351
- label: p.displayName ? `${p.displayName} (${id})` : id
7352
- };
7353
- }).filter((v) => v != null);
7354
- if (props.length === 0) return [];
7355
- return [
7826
+ try {
7827
+ const summaries = await listAccountSummaries(rt.params);
7828
+ const account = summaries.find(
7829
+ (s) => (s.account ?? s.name) === state.account
7830
+ );
7831
+ const props = (account?.propertySummaries ?? []).map((p) => {
7832
+ const id = propertyIdFromResource(p.property);
7833
+ if (!id) return null;
7834
+ return {
7835
+ value: id,
7836
+ label: p.displayName ? `${p.displayName} (${id})` : id
7837
+ };
7838
+ }).filter((v) => v != null);
7839
+ if (props.length === 0) return [];
7840
+ return [
7841
+ {
7842
+ value: ALL_PROPERTIES,
7843
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30D1\u30C6\u30A3" : "All properties"
7844
+ },
7845
+ ...props
7846
+ ];
7847
+ } catch {
7848
+ return [];
7849
+ }
7850
+ },
7851
+ applyAnswer: (state, answer) => ({ ...state, properties: answer }),
7852
+ toParameterUpdates: (state) => {
7853
+ const first = state.properties?.find((v) => v !== ALL_PROPERTIES);
7854
+ return first ? [{ slug: parameters13.propertyId.slug, value: first }] : [];
7855
+ }
7856
+ },
7857
+ {
7858
+ slug: "manualPropertyId",
7859
+ type: "select",
7860
+ allowFreeText: true,
7861
+ question: {
7862
+ ja: "GA4 \u30D7\u30ED\u30D1\u30C6\u30A3 ID \u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: 123456789\uFF09\u3002GA4 \u7BA1\u7406\u753B\u9762 > \u30D7\u30ED\u30D1\u30C6\u30A3\u8A2D\u5B9A\u3067\u78BA\u8A8D\u3067\u304D\u307E\u3059\u3002",
7863
+ en: "Enter your GA4 Property ID (e.g., 123456789). Found in GA4 Admin > Property Settings."
7864
+ },
7865
+ async fetchOptions(state, rt) {
7866
+ if (state.properties?.length) return [];
7867
+ const existing = rt.params[parameters13.propertyId.slug];
7868
+ return existing ? [{ value: existing, label: existing }] : [
7356
7869
  {
7357
- value: ALL_PROPERTIES,
7358
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30D1\u30C6\u30A3" : "All properties"
7359
- },
7360
- ...props
7870
+ value: "example",
7871
+ label: rt.language === "ja" ? "\u4F8B: 123456789" : "Example: 123456789"
7872
+ }
7361
7873
  ];
7362
7874
  },
7363
- applyAnswer: (state, answer) => ({ ...state, properties: answer })
7875
+ applyAnswer: (state, answer) => ({
7876
+ ...state,
7877
+ manualPropertyId: answer[0]
7878
+ }),
7879
+ toParameterUpdates: (state) => state.manualPropertyId ? [
7880
+ {
7881
+ slug: parameters13.propertyId.slug,
7882
+ value: state.manualPropertyId
7883
+ }
7884
+ ] : []
7364
7885
  }
7365
7886
  ],
7366
7887
  async finalize(state, rt) {
7367
- if (!state.account || !state.properties) {
7368
- throw new Error("Google Analytics setup: incomplete state on finalize");
7369
- }
7370
- const summaries = await listAccountSummaries(rt.params);
7371
- const account = summaries.find(
7372
- (s) => (s.account ?? s.name) === state.account
7373
- );
7374
- const accountLabel2 = account?.displayName ?? state.account.replace(/^accounts\//, "");
7375
- const targetPropertyIds = await resolveSetupSelection({
7376
- selected: state.properties,
7377
- allSentinel: ALL_PROPERTIES,
7378
- fetchAll: async () => (account?.propertySummaries ?? []).map((p) => propertyIdFromResource(p.property)).filter((v) => v),
7379
- limit: GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES
7380
- });
7381
- const sections = [
7382
- "## Google Analytics",
7383
- "",
7384
- `### Account: ${accountLabel2}`,
7385
- ""
7386
- ];
7387
- if (targetPropertyIds.length === 0) {
7388
- sections.push("_No properties selected._", "");
7888
+ const sections = ["## Google Analytics", ""];
7889
+ if (state.account && state.properties) {
7890
+ let summaries = [];
7891
+ try {
7892
+ summaries = await listAccountSummaries(rt.params);
7893
+ } catch {
7894
+ }
7895
+ const account = summaries.find(
7896
+ (s) => (s.account ?? s.name) === state.account
7897
+ );
7898
+ const accountLabel2 = account?.displayName ?? state.account.replace(/^accounts\//, "");
7899
+ const targetPropertyIds = await resolveSetupSelection({
7900
+ selected: state.properties,
7901
+ allSentinel: ALL_PROPERTIES,
7902
+ fetchAll: async () => (account?.propertySummaries ?? []).map((p) => propertyIdFromResource(p.property)).filter((v) => v),
7903
+ limit: GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES
7904
+ });
7905
+ sections.push(`### Account: ${accountLabel2}`, "");
7906
+ if (targetPropertyIds.length === 0) {
7907
+ sections.push("_No properties selected._", "");
7908
+ return sections.join("\n");
7909
+ }
7910
+ sections.push(
7911
+ "| Property ID | Display Name | Time Zone | Currency | Industry |"
7912
+ );
7913
+ sections.push(
7914
+ "|-------------|--------------|-----------|----------|----------|"
7915
+ );
7916
+ for (const pid of targetPropertyIds) {
7917
+ const prop = await getProperty(rt.params, pid).catch(() => null);
7918
+ const displayName3 = (prop?.displayName ?? "-").replace(/\|/g, "\\|");
7919
+ const timeZone = prop?.timeZone ?? "-";
7920
+ const currency = prop?.currencyCode ?? "-";
7921
+ const industry = (prop?.industryCategory ?? "-").replace(
7922
+ /\|/g,
7923
+ "\\|"
7924
+ );
7925
+ sections.push(
7926
+ `| ${pid} | ${displayName3} | ${timeZone} | ${currency} | ${industry} |`
7927
+ );
7928
+ }
7929
+ sections.push("");
7930
+ const firstPropertyId = targetPropertyIds[0];
7931
+ if (firstPropertyId) {
7932
+ const { dimensions, metrics } = await getMetadata(
7933
+ rt.params,
7934
+ firstPropertyId
7935
+ );
7936
+ appendMetadataSection(sections, dimensions, metrics);
7937
+ }
7389
7938
  return sections.join("\n");
7390
7939
  }
7391
- sections.push("| Property ID | Display Name | Time Zone | Currency | Industry |");
7392
- sections.push("|-------------|--------------|-----------|----------|----------|");
7393
- for (const propertyId of targetPropertyIds) {
7394
- const prop = await getProperty(rt.params, propertyId);
7395
- const displayName3 = (prop?.displayName ?? "-").replace(/\|/g, "\\|");
7396
- const timeZone = prop?.timeZone ?? "-";
7397
- const currency = prop?.currencyCode ?? "-";
7398
- const industry = (prop?.industryCategory ?? "-").replace(/\|/g, "\\|");
7399
- sections.push(
7400
- `| ${propertyId} | ${displayName3} | ${timeZone} | ${currency} | ${industry} |`
7940
+ const propertyId = state.manualPropertyId ?? rt.params[parameters13.propertyId.slug];
7941
+ if (propertyId) {
7942
+ sections.push(`### Property: ${propertyId}`, "");
7943
+ const { dimensions, metrics } = await getMetadata(
7944
+ rt.params,
7945
+ propertyId
7401
7946
  );
7947
+ appendMetadataSection(sections, dimensions, metrics);
7948
+ return sections.join("\n");
7402
7949
  }
7403
- sections.push("");
7950
+ sections.push(
7951
+ "_Could not list GA4 accounts. Please enable the Google Analytics Admin API in your GCP project, or set the Property ID parameter manually._",
7952
+ ""
7953
+ );
7404
7954
  return sections.join("\n");
7405
7955
  }
7406
7956
  };
@@ -7648,13 +8198,20 @@ activeUsers, sessions, screenPageViews, bounceRate, averageSessionDuration, conv
7648
8198
  error: "google-analytics: missing service account key"
7649
8199
  };
7650
8200
  }
8201
+ const propertyId = params[parameters13.propertyId.slug];
8202
+ if (!propertyId) {
8203
+ return { success: true };
8204
+ }
7651
8205
  try {
7652
- const res = await adminFetch(params, "/accountSummaries?pageSize=1");
8206
+ const res = await dataFetch(
8207
+ params,
8208
+ `/properties/${propertyId}/metadata`
8209
+ );
7653
8210
  if (!res.ok) {
7654
8211
  const body = await res.text().catch(() => res.statusText);
7655
8212
  return {
7656
8213
  success: false,
7657
- error: `google-analytics: accountSummaries failed (${res.status}): ${body}`
8214
+ error: `Google Analytics API failed: HTTP ${res.status} ${body}`
7658
8215
  };
7659
8216
  }
7660
8217
  return { success: true };
@@ -19294,7 +19851,7 @@ function escapeIdentifier(name) {
19294
19851
  function quoteLiteral2(value) {
19295
19852
  return "'" + value.replace(/'/g, "''") + "'";
19296
19853
  }
19297
- async function fetchTableNames4(params, database) {
19854
+ async function fetchTableNames3(params, database) {
19298
19855
  const rows = await runQuery9(
19299
19856
  params,
19300
19857
  `SELECT name FROM system.tables
@@ -19323,17 +19880,17 @@ var clickhouseSetupFlow = {
19323
19880
  slug: "tables",
19324
19881
  type: "multiSelect",
19325
19882
  question: {
19326
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
19327
- en: "Select target tables (multi-select allowed)"
19883
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
19884
+ en: "Select target tables and views (multi-select allowed)"
19328
19885
  },
19329
19886
  async fetchOptions(state, rt) {
19330
19887
  if (!state.database) return [];
19331
- const names = await fetchTableNames4(rt.params, state.database);
19888
+ const names = await fetchTableNames3(rt.params, state.database);
19332
19889
  const tableOptions = names.map((value) => ({ value }));
19333
19890
  return [
19334
19891
  {
19335
19892
  value: ALL_TABLES11,
19336
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
19893
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
19337
19894
  },
19338
19895
  ...tableOptions
19339
19896
  ];
@@ -19349,7 +19906,7 @@ var clickhouseSetupFlow = {
19349
19906
  const targetTables = await resolveSetupSelection({
19350
19907
  selected: state.tables,
19351
19908
  allSentinel: ALL_TABLES11,
19352
- fetchAll: () => fetchTableNames4(rt.params, database),
19909
+ fetchAll: () => fetchTableNames3(rt.params, database),
19353
19910
  limit: CLICKHOUSE_SETUP_MAX_TABLES
19354
19911
  });
19355
19912
  const sections = [
@@ -23948,7 +24505,7 @@ export default async function handler(c: Context) {
23948
24505
  const region = params[parameters50.region.slug];
23949
24506
  const baseUrl = region === "eu" ? "https://api-eu.customer.io" : "https://api.customer.io";
23950
24507
  try {
23951
- const res = await fetch(`${baseUrl}/v1/accounts/region`, {
24508
+ const res = await fetch(`${baseUrl}/v1/segments`, {
23952
24509
  method: "GET",
23953
24510
  headers: {
23954
24511
  Authorization: `Bearer ${appApiKey}`,
@@ -29504,7 +30061,7 @@ function apiFetch27(params, path4, init) {
29504
30061
  // ../connectors/src/connectors/gamma/setup-flow.ts
29505
30062
  var ALL_FOLDERS = "__ALL_FOLDERS__";
29506
30063
  var GAMMA_SETUP_MAX_FOLDERS = 20;
29507
- var FOLDERS_PAGE_LIMIT = 100;
30064
+ var FOLDERS_PAGE_LIMIT = 50;
29508
30065
  async function listAllFolders(params) {
29509
30066
  const results = [];
29510
30067
  let after;
@@ -29525,7 +30082,7 @@ async function listAllFolders(params) {
29525
30082
  return results;
29526
30083
  }
29527
30084
  async function listThemes(params) {
29528
- const res = await apiFetch27(params, "/themes?limit=100");
30085
+ const res = await apiFetch27(params, "/themes?limit=50");
29529
30086
  if (!res.ok) {
29530
30087
  return [];
29531
30088
  }
@@ -33362,7 +33919,8 @@ var semrushOnboarding = new ConnectorOnboarding({
33362
33919
  });
33363
33920
 
33364
33921
  // ../connectors/src/connectors/semrush/utils.ts
33365
- var PROJECTS_BASE_URL = "https://api.semrush.com/management/v1";
33922
+ var BASE_URL59 = "https://api.semrush.com";
33923
+ var PROJECTS_BASE_URL = `${BASE_URL59}/management/v1`;
33366
33924
  function projectsApiFetch(params, path4, init) {
33367
33925
  const apiKey = params[parameters68.apiKey.slug];
33368
33926
  if (!apiKey) {
@@ -33379,10 +33937,103 @@ function projectsApiFetch(params, path4, init) {
33379
33937
  if (!headers.has("Accept")) headers.set("Accept", "application/json");
33380
33938
  return fetch(url.toString(), { ...init, headers });
33381
33939
  }
33940
+ async function reportFetch(params, query) {
33941
+ const apiKey = params[parameters68.apiKey.slug];
33942
+ if (!apiKey) {
33943
+ throw new Error(
33944
+ `semrush: missing required parameter: ${parameters68.apiKey.slug}`
33945
+ );
33946
+ }
33947
+ const url = new URL(`${BASE_URL59}/`);
33948
+ for (const [k, v] of Object.entries(query)) {
33949
+ url.searchParams.set(k, v);
33950
+ }
33951
+ url.searchParams.set("key", apiKey);
33952
+ const res = await fetch(url.toString());
33953
+ const text = await res.text();
33954
+ if (text.startsWith("ERROR ")) throw new Error(text.trim());
33955
+ return parseSemicolonCsv(text);
33956
+ }
33957
+ async function backlinksFetch(params, query) {
33958
+ const apiKey = params[parameters68.apiKey.slug];
33959
+ if (!apiKey) {
33960
+ throw new Error(
33961
+ `semrush: missing required parameter: ${parameters68.apiKey.slug}`
33962
+ );
33963
+ }
33964
+ const url = new URL(`${BASE_URL59}/analytics/v1/`);
33965
+ for (const [k, v] of Object.entries(query)) {
33966
+ url.searchParams.set(k, v);
33967
+ }
33968
+ url.searchParams.set("key", apiKey);
33969
+ const res = await fetch(url.toString());
33970
+ const text = await res.text();
33971
+ if (text.startsWith("ERROR ")) throw new Error(text.trim());
33972
+ return parseSemicolonCsv(text);
33973
+ }
33974
+ function parseSemicolonCsv(raw) {
33975
+ const lines = raw.trim().split("\n").filter(Boolean);
33976
+ if (lines.length === 0) return { columns: [], rows: [] };
33977
+ const columns = lines[0].split(";");
33978
+ const rows = lines.slice(1).map((line) => {
33979
+ const values = line.split(";");
33980
+ const row = {};
33981
+ for (let i = 0; i < columns.length; i++) {
33982
+ row[columns[i]] = values[i] ?? "";
33983
+ }
33984
+ return row;
33985
+ });
33986
+ return { columns, rows };
33987
+ }
33382
33988
 
33383
33989
  // ../connectors/src/connectors/semrush/setup-flow.ts
33384
33990
  var ALL_PROJECTS6 = "__ALL_PROJECTS__";
33385
33991
  var SEMRUSH_SETUP_MAX_PROJECTS = 10;
33992
+ var SEMRUSH_DATABASES = [
33993
+ { value: "us", label: "United States" },
33994
+ { value: "uk", label: "United Kingdom" },
33995
+ { value: "ca", label: "Canada" },
33996
+ { value: "au", label: "Australia" },
33997
+ { value: "de", label: "Germany" },
33998
+ { value: "fr", label: "France" },
33999
+ { value: "es", label: "Spain" },
34000
+ { value: "it", label: "Italy" },
34001
+ { value: "br", label: "Brazil" },
34002
+ { value: "jp", label: "Japan" },
34003
+ { value: "in", label: "India" },
34004
+ { value: "ru", label: "Russia" },
34005
+ { value: "nl", label: "Netherlands" },
34006
+ { value: "se", label: "Sweden" },
34007
+ { value: "mx", label: "Mexico" },
34008
+ { value: "kr", label: "South Korea" },
34009
+ { value: "sg", label: "Singapore" },
34010
+ { value: "hk", label: "Hong Kong" },
34011
+ { value: "tw", label: "Taiwan" }
34012
+ ];
34013
+ var REPORT_TYPE_LABELS = {
34014
+ domain_overview: {
34015
+ en: "Domain Overview (rank, traffic, keyword count)",
34016
+ ja: "Domain Overview (\u30E9\u30F3\u30AF\u30FB\u30C8\u30E9\u30D5\u30A3\u30C3\u30AF\u30FB\u30AD\u30FC\u30EF\u30FC\u30C9\u6570)"
34017
+ },
34018
+ organic_search: {
34019
+ en: "Organic Search (top organic keywords)",
34020
+ ja: "Organic Search (\u4E0A\u4F4D\u30AA\u30FC\u30AC\u30CB\u30C3\u30AF\u30AD\u30FC\u30EF\u30FC\u30C9)"
34021
+ },
34022
+ paid_search: {
34023
+ en: "Paid Search (top paid keywords)",
34024
+ ja: "Paid Search (\u4E0A\u4F4D\u6709\u6599\u30AD\u30FC\u30EF\u30FC\u30C9)"
34025
+ },
34026
+ backlinks: {
34027
+ en: "Backlinks (referring domains, backlink count)",
34028
+ ja: "Backlinks (\u53C2\u7167\u30C9\u30E1\u30A4\u30F3\u30FB\u88AB\u30EA\u30F3\u30AF\u6570)"
34029
+ }
34030
+ };
34031
+ var REPORT_TYPE_VALUES = [
34032
+ "domain_overview",
34033
+ "organic_search",
34034
+ "paid_search",
34035
+ "backlinks"
34036
+ ];
33386
34037
  function projectId(p) {
33387
34038
  const raw = p.project_id ?? p.id;
33388
34039
  return raw == null ? "" : String(raw);
@@ -33391,7 +34042,7 @@ function projectName(p) {
33391
34042
  return p.project_name ?? p.name ?? p.domain ?? p.url ?? projectId(p);
33392
34043
  }
33393
34044
  function projectDomain(p) {
33394
- return p.domain ?? p.url ?? "";
34045
+ return p.domain ?? p.domain_unicode ?? p.url ?? "";
33395
34046
  }
33396
34047
  function projectCreatedAt(p) {
33397
34048
  return p.created_at ?? p.date_created ?? "";
@@ -33399,13 +34050,19 @@ function projectCreatedAt(p) {
33399
34050
  async function fetchProjects(params) {
33400
34051
  let res;
33401
34052
  try {
33402
- res = await projectsApiFetch(params, "/projects/");
34053
+ res = await projectsApiFetch(params, "/projects");
33403
34054
  } catch (err) {
33404
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
34055
+ return {
34056
+ ok: false,
34057
+ error: err instanceof Error ? err.message : String(err)
34058
+ };
33405
34059
  }
33406
34060
  if (!res.ok) {
33407
34061
  const body2 = await res.text().catch(() => res.statusText);
33408
- return { ok: false, error: `HTTP ${res.status} ${body2 || res.statusText}` };
34062
+ return {
34063
+ ok: false,
34064
+ error: `HTTP ${res.status} ${body2 || res.statusText}`
34065
+ };
33409
34066
  }
33410
34067
  let body;
33411
34068
  try {
@@ -33419,9 +34076,171 @@ async function fetchProjects(params) {
33419
34076
  const projects = Array.isArray(body) ? body : Array.isArray(body?.projects) ? body.projects : Array.isArray(body?.data) ? body.data : [];
33420
34077
  return { ok: true, projects };
33421
34078
  }
34079
+ function formatNumber(n) {
34080
+ return n.toLocaleString("en-US");
34081
+ }
34082
+ async function fetchDomainOverview(params, domain, database) {
34083
+ const sections = [];
34084
+ try {
34085
+ const report = await reportFetch(params, {
34086
+ type: "domain_ranks",
34087
+ domain,
34088
+ database
34089
+ });
34090
+ if (report.rows.length === 0) {
34091
+ sections.push("_No data found for this domain._", "");
34092
+ return sections;
34093
+ }
34094
+ const row = report.rows[0];
34095
+ sections.push("| Metric | Value |");
34096
+ sections.push("|--------|-------|");
34097
+ sections.push(
34098
+ `| Rank | ${row["Rank"] ?? "-"} |`,
34099
+ `| Organic Keywords | ${formatNumber(Number(row["Organic Keywords"]) || 0)} |`,
34100
+ `| Organic Traffic | ${formatNumber(Number(row["Organic Traffic"]) || 0)} |`,
34101
+ `| Organic Cost | $${formatNumber(Number(row["Organic Cost"]) || 0)} |`,
34102
+ `| Adwords Keywords | ${formatNumber(Number(row["Adwords Keywords"]) || 0)} |`,
34103
+ `| Adwords Traffic | ${formatNumber(Number(row["Adwords Traffic"]) || 0)} |`,
34104
+ `| Adwords Cost | $${formatNumber(Number(row["Adwords Cost"]) || 0)} |`
34105
+ );
34106
+ sections.push("");
34107
+ } catch (err) {
34108
+ sections.push(
34109
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
34110
+ ""
34111
+ );
34112
+ }
34113
+ return sections;
34114
+ }
34115
+ async function fetchOrganicSearch(params, domain, database) {
34116
+ return fetchKeywordReport(params, "domain_organic", domain, database);
34117
+ }
34118
+ async function fetchPaidSearch(params, domain, database) {
34119
+ return fetchKeywordReport(params, "domain_adwords", domain, database);
34120
+ }
34121
+ async function fetchKeywordReport(params, type, domain, database) {
34122
+ const sections = [];
34123
+ try {
34124
+ const report = await reportFetch(params, {
34125
+ type,
34126
+ domain,
34127
+ database,
34128
+ display_limit: "5"
34129
+ });
34130
+ if (report.rows.length === 0) {
34131
+ sections.push("_No data found._", "");
34132
+ return sections;
34133
+ }
34134
+ sections.push(...renderCsvTable(report, 5));
34135
+ sections.push("");
34136
+ } catch (err) {
34137
+ sections.push(
34138
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
34139
+ ""
34140
+ );
34141
+ }
34142
+ return sections;
34143
+ }
34144
+ async function fetchBacklinks(params, domain) {
34145
+ const sections = [];
34146
+ try {
34147
+ const report = await backlinksFetch(params, {
34148
+ type: "backlinks_overview",
34149
+ target: domain,
34150
+ target_type: "root_domain"
34151
+ });
34152
+ if (report.rows.length === 0) {
34153
+ sections.push("_No backlink data found._", "");
34154
+ return sections;
34155
+ }
34156
+ const row = report.rows[0];
34157
+ sections.push("| Metric | Value |");
34158
+ sections.push("|--------|-------|");
34159
+ for (const col of report.columns) {
34160
+ sections.push(`| ${col} | ${formatNumber(Number(row[col]) || 0)} |`);
34161
+ }
34162
+ sections.push("");
34163
+ } catch (err) {
34164
+ sections.push(
34165
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
34166
+ ""
34167
+ );
34168
+ }
34169
+ return sections;
34170
+ }
34171
+ function renderCsvTable(report, maxRows) {
34172
+ const cols = report.columns;
34173
+ const rows = report.rows.slice(0, maxRows);
34174
+ const lines = [];
34175
+ lines.push(`| ${cols.join(" | ")} |`);
34176
+ lines.push(`|${cols.map(() => "---").join("|")}|`);
34177
+ for (const row of rows) {
34178
+ const cells = cols.map((c) => (row[c] ?? "").replace(/\|/g, "\\|"));
34179
+ lines.push(`| ${cells.join(" | ")} |`);
34180
+ }
34181
+ return lines;
34182
+ }
33422
34183
  var semrushSetupFlow = {
33423
34184
  initialState: () => ({}),
33424
34185
  steps: [
34186
+ {
34187
+ slug: "domain",
34188
+ type: "select",
34189
+ allowFreeText: true,
34190
+ question: {
34191
+ ja: "\u5206\u6790\u5BFE\u8C61\u306E\u30C9\u30E1\u30A4\u30F3\u3092\u9078\u629E\u307E\u305F\u306F\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
34192
+ en: "Select or enter the domain to analyze"
34193
+ },
34194
+ async fetchOptions(_state, rt) {
34195
+ const result = await fetchProjects(rt.params);
34196
+ if (!result.ok || result.projects.length === 0) return [];
34197
+ const seen = /* @__PURE__ */ new Set();
34198
+ const options = [];
34199
+ for (const p of result.projects) {
34200
+ const d = projectDomain(p);
34201
+ if (d && !seen.has(d)) {
34202
+ seen.add(d);
34203
+ options.push({
34204
+ value: d,
34205
+ label: `${d} (${projectName(p)})`
34206
+ });
34207
+ }
34208
+ }
34209
+ return options;
34210
+ },
34211
+ applyAnswer: (state, answer) => ({ ...state, domain: answer[0] })
34212
+ },
34213
+ {
34214
+ slug: "database",
34215
+ type: "select",
34216
+ allowFreeText: false,
34217
+ question: {
34218
+ 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",
34219
+ en: "Select the region (primary market for the target domain)"
34220
+ },
34221
+ async fetchOptions() {
34222
+ return SEMRUSH_DATABASES.map((db) => ({
34223
+ value: db.value,
34224
+ label: `${db.label} (${db.value})`
34225
+ }));
34226
+ },
34227
+ applyAnswer: (state, answer) => ({ ...state, database: answer[0] })
34228
+ },
34229
+ {
34230
+ slug: "reportTypes",
34231
+ type: "multiSelect",
34232
+ question: {
34233
+ 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",
34234
+ en: "Select report types to explore (multi-select allowed)"
34235
+ },
34236
+ async fetchOptions(_state, rt) {
34237
+ return REPORT_TYPE_VALUES.map((value) => ({
34238
+ value,
34239
+ label: REPORT_TYPE_LABELS[value][rt.language]
34240
+ }));
34241
+ },
34242
+ applyAnswer: (state, answer) => ({ ...state, reportTypes: answer })
34243
+ },
33425
34244
  {
33426
34245
  slug: "projects",
33427
34246
  type: "multiSelect",
@@ -33438,7 +34257,9 @@ var semrushSetupFlow = {
33438
34257
  const id = projectId(p);
33439
34258
  if (!id) return null;
33440
34259
  return { value: id, label: projectName(p) };
33441
- }).filter((opt) => opt !== null);
34260
+ }).filter(
34261
+ (opt) => opt !== null
34262
+ );
33442
34263
  if (options.length === 0) return [];
33443
34264
  return [
33444
34265
  {
@@ -33452,49 +34273,80 @@ var semrushSetupFlow = {
33452
34273
  }
33453
34274
  ],
33454
34275
  async finalize(state, rt) {
33455
- const sections = ["## Semrush", ""];
33456
- if (!state.projects?.length) {
34276
+ if (!state.domain || !state.database || !state.reportTypes) {
34277
+ throw new Error("Semrush setup: incomplete state on finalize");
34278
+ }
34279
+ const domain = state.domain;
34280
+ const database = state.database;
34281
+ const selected = state.reportTypes.filter(
34282
+ (r) => REPORT_TYPE_VALUES.includes(r)
34283
+ );
34284
+ const sections = [
34285
+ "## Semrush",
34286
+ "",
34287
+ `**Domain:** ${domain}`,
34288
+ `**Region:** ${database}`,
34289
+ ""
34290
+ ];
34291
+ for (const reportType of selected) {
33457
34292
  sections.push(
33458
- "_No Semrush Projects API access detected; Standard Analytics reports run by domain/keyword and don't require pre-selection._",
34293
+ `### ${REPORT_TYPE_LABELS[reportType].en}`,
33459
34294
  ""
33460
34295
  );
33461
- return sections.join("\n");
33462
- }
33463
- const result = await fetchProjects(rt.params);
33464
- if (!result.ok) {
33465
- throw new Error(
33466
- `semrush: listProjects failed on finalize: ${result.error}`
33467
- );
33468
- }
33469
- const projectByIdMap = /* @__PURE__ */ new Map();
33470
- for (const p of result.projects) {
33471
- const id = projectId(p);
33472
- if (id) projectByIdMap.set(id, p);
33473
- }
33474
- const targetIds = await resolveSetupSelection({
33475
- selected: state.projects,
33476
- allSentinel: ALL_PROJECTS6,
33477
- fetchAll: async () => result.projects.map((p) => projectId(p)).filter((id) => id),
33478
- limit: SEMRUSH_SETUP_MAX_PROJECTS
33479
- });
33480
- if (!targetIds.length) {
33481
- sections.push("_No projects selected._", "");
33482
- return sections.join("\n");
34296
+ switch (reportType) {
34297
+ case "domain_overview":
34298
+ sections.push(
34299
+ ...await fetchDomainOverview(rt.params, domain, database)
34300
+ );
34301
+ break;
34302
+ case "organic_search":
34303
+ sections.push(
34304
+ ...await fetchOrganicSearch(rt.params, domain, database)
34305
+ );
34306
+ break;
34307
+ case "paid_search":
34308
+ sections.push(
34309
+ ...await fetchPaidSearch(rt.params, domain, database)
34310
+ );
34311
+ break;
34312
+ case "backlinks":
34313
+ sections.push(...await fetchBacklinks(rt.params, domain));
34314
+ break;
34315
+ }
33483
34316
  }
33484
- sections.push("| Project | Domain | Created |");
33485
- sections.push("|---------|--------|---------|");
33486
- for (const id of targetIds) {
33487
- const p = projectByIdMap.get(id);
33488
- if (!p) {
33489
- sections.push(`| ${id} | - | - |`);
33490
- continue;
34317
+ if (state.projects?.length) {
34318
+ const result = await fetchProjects(rt.params);
34319
+ if (result.ok) {
34320
+ const projectByIdMap = /* @__PURE__ */ new Map();
34321
+ for (const p of result.projects) {
34322
+ const id = projectId(p);
34323
+ if (id) projectByIdMap.set(id, p);
34324
+ }
34325
+ const targetIds = await resolveSetupSelection({
34326
+ selected: state.projects,
34327
+ allSentinel: ALL_PROJECTS6,
34328
+ fetchAll: async () => result.projects.map((p) => projectId(p)).filter((id) => id),
34329
+ limit: SEMRUSH_SETUP_MAX_PROJECTS
34330
+ });
34331
+ if (targetIds.length > 0) {
34332
+ sections.push("### Projects", "");
34333
+ sections.push("| Project | Domain | Created |");
34334
+ sections.push("|---------|--------|---------|");
34335
+ for (const id of targetIds) {
34336
+ const p = projectByIdMap.get(id);
34337
+ if (!p) {
34338
+ sections.push(`| ${id} | - | - |`);
34339
+ continue;
34340
+ }
34341
+ const name = projectName(p).replace(/\|/g, "\\|");
34342
+ const dom = projectDomain(p).replace(/\|/g, "\\|") || "-";
34343
+ const created = projectCreatedAt(p) || "-";
34344
+ sections.push(`| ${name} | ${dom} | ${created} |`);
34345
+ }
34346
+ sections.push("");
34347
+ }
33491
34348
  }
33492
- const name = projectName(p).replace(/\|/g, "\\|");
33493
- const domain = projectDomain(p).replace(/\|/g, "\\|") || "-";
33494
- const created = projectCreatedAt(p) || "-";
33495
- sections.push(`| ${name} | ${domain} | ${created} |`);
33496
34349
  }
33497
- sections.push("");
33498
34350
  return sections.join("\n");
33499
34351
  }
33500
34352
  };
@@ -33755,7 +34607,7 @@ API \u30E6\u30CB\u30C3\u30C8\u6B8B\u91CF\u306E\u78BA\u8A8D\uFF08\u7121\u6599\u30
33755
34607
 
33756
34608
  // ../connectors/src/connectors/google-search-console-oauth/tools/list-sites.ts
33757
34609
  import { z as z84 } from "zod";
33758
- var BASE_URL59 = "https://searchconsole.googleapis.com/webmasters/v3";
34610
+ var BASE_URL60 = "https://searchconsole.googleapis.com/webmasters/v3";
33759
34611
  var REQUEST_TIMEOUT_MS67 = 6e4;
33760
34612
  var cachedToken30 = null;
33761
34613
  async function getProxyToken30(config) {
@@ -33826,7 +34678,7 @@ var listSitesTool = new ConnectorTool({
33826
34678
  `[connector-request] google-search-console-oauth/${connection.name}: listSites`
33827
34679
  );
33828
34680
  try {
33829
- const url = `${BASE_URL59}/sites`;
34681
+ const url = `${BASE_URL60}/sites`;
33830
34682
  const token = await getProxyToken30(config.oauthProxy);
33831
34683
  const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
33832
34684
  const controller = new AbortController();
@@ -34008,7 +34860,7 @@ var googleSearchConsoleOauthSetupFlow = {
34008
34860
  import { z as z85 } from "zod";
34009
34861
  var BASE_HOST10 = "https://searchconsole.googleapis.com";
34010
34862
  var BASE_PATH_SEGMENT10 = "/webmasters/v3";
34011
- var BASE_URL60 = `${BASE_HOST10}${BASE_PATH_SEGMENT10}`;
34863
+ var BASE_URL61 = `${BASE_HOST10}${BASE_PATH_SEGMENT10}`;
34012
34864
  var REQUEST_TIMEOUT_MS68 = 6e4;
34013
34865
  var cachedToken31 = null;
34014
34866
  async function getProxyToken31(config) {
@@ -34093,7 +34945,7 @@ For URL Inspection API requests, use the absolute path '/v1/urlInspection/index:
34093
34945
  resolvedPath,
34094
34946
  BASE_PATH_SEGMENT10
34095
34947
  );
34096
- let url = `${BASE_URL60}${normalizedPath}`;
34948
+ let url = `${BASE_URL61}${normalizedPath}`;
34097
34949
  if (queryParams) {
34098
34950
  const searchParams = new URLSearchParams(queryParams);
34099
34951
  url += `?${searchParams.toString()}`;
@@ -34419,7 +35271,7 @@ function isInternalSchema3(name) {
34419
35271
  if (lower.startsWith("pg_toast_")) return true;
34420
35272
  return false;
34421
35273
  }
34422
- async function fetchTableNames5(params, schema) {
35274
+ async function fetchTableNames4(params, schema) {
34423
35275
  const rows = await runQuery10(
34424
35276
  params,
34425
35277
  `SELECT table_name FROM information_schema.tables
@@ -34452,17 +35304,17 @@ var supabaseSetupFlow = {
34452
35304
  slug: "tables",
34453
35305
  type: "multiSelect",
34454
35306
  question: {
34455
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
34456
- en: "Select target tables (multi-select allowed)"
35307
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
35308
+ en: "Select target tables and views (multi-select allowed)"
34457
35309
  },
34458
35310
  async fetchOptions(state, rt) {
34459
35311
  if (!state.schema) return [];
34460
- const names = await fetchTableNames5(rt.params, state.schema);
35312
+ const names = await fetchTableNames4(rt.params, state.schema);
34461
35313
  const tableOptions = names.map((value) => ({ value }));
34462
35314
  return [
34463
35315
  {
34464
35316
  value: ALL_TABLES13,
34465
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
35317
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
34466
35318
  },
34467
35319
  ...tableOptions
34468
35320
  ];
@@ -34478,7 +35330,7 @@ var supabaseSetupFlow = {
34478
35330
  const targetTables = await resolveSetupSelection({
34479
35331
  selected: state.tables,
34480
35332
  allSentinel: ALL_TABLES13,
34481
- fetchAll: () => fetchTableNames5(rt.params, schema),
35333
+ fetchAll: () => fetchTableNames4(rt.params, schema),
34482
35334
  limit: SUPABASE_SETUP_MAX_TABLES
34483
35335
  });
34484
35336
  const sections = [
@@ -34713,13 +35565,13 @@ var parameters71 = {
34713
35565
  };
34714
35566
 
34715
35567
  // ../connectors/src/connectors/clickup/utils.ts
34716
- var BASE_URL61 = "https://api.clickup.com/api/v2";
35568
+ var BASE_URL62 = "https://api.clickup.com/api/v2";
34717
35569
  async function apiFetch30(params, path4, init) {
34718
35570
  const token = params[parameters71.apiToken.slug];
34719
35571
  if (!token) {
34720
35572
  throw new Error("clickup: missing required parameter: api-token");
34721
35573
  }
34722
- const url = `${BASE_URL61}${path4.startsWith("/") ? "" : "/"}${path4}`;
35574
+ const url = `${BASE_URL62}${path4.startsWith("/") ? "" : "/"}${path4}`;
34723
35575
  const headers = new Headers(init?.headers);
34724
35576
  headers.set("Authorization", token);
34725
35577
  if (!headers.has("Accept")) headers.set("Accept", "application/json");
@@ -34847,7 +35699,7 @@ var clickupSetupFlow = {
34847
35699
  import { z as z87 } from "zod";
34848
35700
  var BASE_HOST11 = "https://api.clickup.com";
34849
35701
  var BASE_PATH_SEGMENT11 = "/api/v2";
34850
- var BASE_URL62 = `${BASE_HOST11}${BASE_PATH_SEGMENT11}`;
35702
+ var BASE_URL63 = `${BASE_HOST11}${BASE_PATH_SEGMENT11}`;
34851
35703
  var REQUEST_TIMEOUT_MS69 = 6e4;
34852
35704
  var inputSchema87 = z87.object({
34853
35705
  toolUseIntent: z87.string().optional().describe(
@@ -34920,7 +35772,7 @@ Pagination: ClickUp uses zero-indexed \`page\` query parameter on list endpoints
34920
35772
  try {
34921
35773
  const token = parameters71.apiToken.getValue(connection);
34922
35774
  const normalizedPath = normalizeRequestPath(path4, BASE_PATH_SEGMENT11);
34923
- const url = `${BASE_URL62}${normalizedPath}`;
35775
+ const url = `${BASE_URL63}${normalizedPath}`;
34924
35776
  const controller = new AbortController();
34925
35777
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS69);
34926
35778
  try {
@@ -35250,7 +36102,7 @@ function quoteLiteral4(value) {
35250
36102
  }
35251
36103
  function buildFlow(options) {
35252
36104
  const { connectorName, forceEncrypt } = options;
35253
- async function fetchTableNames7(params, schema) {
36105
+ async function fetchTableNames5(params, schema) {
35254
36106
  const rows = await runSqlServerSetupQuery(
35255
36107
  params,
35256
36108
  `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
@@ -35285,17 +36137,17 @@ function buildFlow(options) {
35285
36137
  slug: "tables",
35286
36138
  type: "multiSelect",
35287
36139
  question: {
35288
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
35289
- en: "Select target tables (multi-select allowed)"
36140
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
36141
+ en: "Select target tables and views (multi-select allowed)"
35290
36142
  },
35291
36143
  async fetchOptions(state, rt) {
35292
36144
  if (!state.schema) return [];
35293
- const names = await fetchTableNames7(rt.params, state.schema);
36145
+ const names = await fetchTableNames5(rt.params, state.schema);
35294
36146
  const tableOptions = names.map((value) => ({ value }));
35295
36147
  return [
35296
36148
  {
35297
36149
  value: ALL_TABLES14,
35298
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
36150
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
35299
36151
  },
35300
36152
  ...tableOptions
35301
36153
  ];
@@ -35311,7 +36163,7 @@ function buildFlow(options) {
35311
36163
  const targetTables = await resolveSetupSelection({
35312
36164
  selected: state.tables,
35313
36165
  allSentinel: ALL_TABLES14,
35314
- fetchAll: () => fetchTableNames7(rt.params, schema),
36166
+ fetchAll: () => fetchTableNames5(rt.params, schema),
35315
36167
  limit: SQLSERVER_SETUP_MAX_TABLES
35316
36168
  });
35317
36169
  const sections = [
@@ -36306,14 +37158,15 @@ function isInternalOwner(name) {
36306
37158
  function quoteLiteral5(value) {
36307
37159
  return "'" + value.replace(/'/g, "''") + "'";
36308
37160
  }
36309
- async function fetchTableNames6(params, owner) {
37161
+ async function fetchTableAndViewNames2(params, owner) {
36310
37162
  const rows = await runOracleSetupQuery(
36311
37163
  params,
36312
- `SELECT TABLE_NAME FROM ALL_TABLES
36313
- WHERE OWNER = ${quoteLiteral5(owner)}
36314
- ORDER BY TABLE_NAME`
37164
+ `SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER = ${quoteLiteral5(owner)}
37165
+ UNION
37166
+ SELECT VIEW_NAME FROM ALL_VIEWS WHERE OWNER = ${quoteLiteral5(owner)}
37167
+ ORDER BY 1`
36315
37168
  );
36316
- return rows.map((r) => String(r["TABLE_NAME"] ?? "")).filter((name) => name);
37169
+ return rows.map((r) => String(r["TABLE_NAME"] ?? r["VIEW_NAME"] ?? "")).filter((name) => name);
36317
37170
  }
36318
37171
  var oracleSetupFlow = {
36319
37172
  initialState: () => ({}),
@@ -36338,17 +37191,17 @@ var oracleSetupFlow = {
36338
37191
  slug: "tables",
36339
37192
  type: "multiSelect",
36340
37193
  question: {
36341
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
36342
- en: "Select target tables (multi-select allowed)"
37194
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
37195
+ en: "Select target tables and views (multi-select allowed)"
36343
37196
  },
36344
37197
  async fetchOptions(state, rt) {
36345
37198
  if (!state.owner) return [];
36346
- const names = await fetchTableNames6(rt.params, state.owner);
37199
+ const names = await fetchTableAndViewNames2(rt.params, state.owner);
36347
37200
  const tableOptions = names.map((value) => ({ value }));
36348
37201
  return [
36349
37202
  {
36350
37203
  value: ALL_TABLES15,
36351
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
37204
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
36352
37205
  },
36353
37206
  ...tableOptions
36354
37207
  ];
@@ -36364,9 +37217,22 @@ var oracleSetupFlow = {
36364
37217
  const targetTables = await resolveSetupSelection({
36365
37218
  selected: state.tables,
36366
37219
  allSentinel: ALL_TABLES15,
36367
- fetchAll: () => fetchTableNames6(rt.params, owner),
37220
+ fetchAll: () => fetchTableAndViewNames2(rt.params, owner),
36368
37221
  limit: ORACLE_SETUP_MAX_TABLES
36369
37222
  });
37223
+ const typeRows = targetTables.length > 0 ? await runOracleSetupQuery(
37224
+ rt.params,
37225
+ `SELECT OBJECT_NAME, OBJECT_TYPE FROM ALL_OBJECTS
37226
+ WHERE OWNER = ${quoteLiteral5(owner)}
37227
+ AND OBJECT_NAME IN (${targetTables.map(quoteLiteral5).join(", ")})
37228
+ AND OBJECT_TYPE IN ('TABLE', 'VIEW')`
37229
+ ) : [];
37230
+ const typeMap = new Map(
37231
+ typeRows.map((r) => [
37232
+ String(r["OBJECT_NAME"] ?? ""),
37233
+ String(r["OBJECT_TYPE"] ?? "TABLE")
37234
+ ])
37235
+ );
36370
37236
  const sections = [
36371
37237
  "## Oracle Database",
36372
37238
  "",
@@ -36374,6 +37240,7 @@ var oracleSetupFlow = {
36374
37240
  ""
36375
37241
  ];
36376
37242
  for (const table of targetTables) {
37243
+ const heading = typeMap.get(table) === "VIEW" ? "View" : "Table";
36377
37244
  const cols = await runOracleSetupQuery(
36378
37245
  rt.params,
36379
37246
  `SELECT COLUMN_NAME, DATA_TYPE, NULLABLE, DATA_DEFAULT
@@ -36382,7 +37249,7 @@ var oracleSetupFlow = {
36382
37249
  AND TABLE_NAME = ${quoteLiteral5(table)}
36383
37250
  ORDER BY COLUMN_ID`
36384
37251
  );
36385
- sections.push(`#### Table: ${table}`, "");
37252
+ sections.push(`#### ${heading}: ${table}`, "");
36386
37253
  sections.push("| Column | Type | Nullable | Default |");
36387
37254
  sections.push("|--------|------|----------|---------|");
36388
37255
  for (const c of cols) {
@@ -38687,7 +39554,7 @@ export default async function handler(c: Context) {
38687
39554
  import { z as z97 } from "zod";
38688
39555
  var BASE_HOST12 = "https://api.powerbi.com";
38689
39556
  var BASE_PATH_SEGMENT18 = "/v1.0/myorg";
38690
- var BASE_URL63 = `${BASE_HOST12}${BASE_PATH_SEGMENT18}`;
39557
+ var BASE_URL64 = `${BASE_HOST12}${BASE_PATH_SEGMENT18}`;
38691
39558
  var REQUEST_TIMEOUT_MS74 = 6e4;
38692
39559
  var cachedToken32 = null;
38693
39560
  async function getProxyToken32(config) {
@@ -38769,7 +39636,7 @@ The signed-in user must have access to the workspace; unlike Service Principals,
38769
39636
  );
38770
39637
  try {
38771
39638
  const normalizedPath = normalizeRequestPath(path4, BASE_PATH_SEGMENT18);
38772
- let url = `${BASE_URL63}${normalizedPath}`;
39639
+ let url = `${BASE_URL64}${normalizedPath}`;
38773
39640
  if (queryParams) {
38774
39641
  const searchParams = new URLSearchParams(queryParams);
38775
39642
  url += `?${searchParams.toString()}`;
@@ -38852,14 +39719,18 @@ var powerbiOauthOnboarding = new ConnectorOnboarding({
38852
39719
  var parameters80 = {};
38853
39720
 
38854
39721
  // ../connectors/src/connectors/powerbi-oauth/utils.ts
38855
- var BASE_URL64 = "https://api.powerbi.com/v1.0/myorg";
39722
+ var BASE_URL65 = "https://api.powerbi.com/v1.0/myorg";
38856
39723
  function apiFetch35(proxyFetch, path4, init) {
38857
- const url = `${BASE_URL64}${path4.startsWith("/") ? "" : "/"}${path4}`;
39724
+ const url = `${BASE_URL65}${path4.startsWith("/") ? "" : "/"}${path4}`;
38858
39725
  return proxyFetch(url, init);
38859
39726
  }
38860
39727
 
38861
39728
  // ../connectors/src/connectors/powerbi-oauth/setup-flow.ts
38862
39729
  var ALL_WORKSPACES = "__ALL_WORKSPACES__";
39730
+ var MY_WORKSPACE = "__MY_WORKSPACE__";
39731
+ var ALL_DATASETS = "__ALL_DATASETS__";
39732
+ var ALL_REPORTS = "__ALL_REPORTS__";
39733
+ var ALL_DASHBOARDS = "__ALL_DASHBOARDS__";
38863
39734
  var POWERBI_SETUP_MAX_WORKSPACES = 10;
38864
39735
  var RESOURCE_DISPLAY_LIMIT = 25;
38865
39736
  var RESOURCE_DATASETS = "datasets";
@@ -38875,14 +39746,12 @@ async function listGroups(proxyFetch) {
38875
39746
  return data.value ?? [];
38876
39747
  }
38877
39748
  async function listResource(proxyFetch, groupId, resource) {
38878
- const res = await apiFetch35(
38879
- proxyFetch,
38880
- `/groups/${encodeURIComponent(groupId)}/${resource}`
38881
- );
39749
+ const path4 = groupId === MY_WORKSPACE ? `/${resource}` : `/groups/${encodeURIComponent(groupId)}/${resource}`;
39750
+ const res = await apiFetch35(proxyFetch, path4);
38882
39751
  if (!res.ok) {
38883
39752
  const body = await res.text().catch(() => res.statusText);
38884
39753
  throw new Error(
38885
- `powerbi: list ${resource} for group ${groupId} failed (${res.status}): ${body}`
39754
+ `powerbi: list ${resource} for ${groupId === MY_WORKSPACE ? "My workspace" : `group ${groupId}`} failed (${res.status}): ${body}`
38886
39755
  );
38887
39756
  }
38888
39757
  const data = await res.json();
@@ -38891,6 +39760,99 @@ async function listResource(proxyFetch, groupId, resource) {
38891
39760
  function resourceLabel(r) {
38892
39761
  return r.name ?? r.displayName ?? r.id ?? "(unknown)";
38893
39762
  }
39763
+ var INTERNAL_TABLE_PREFIXES = [
39764
+ "DateTableTemplate_",
39765
+ "LocalDateTable_"
39766
+ ];
39767
+ function isInternalColumn(name) {
39768
+ return /^RowNumber-[0-9A-Fa-f-]+$/.test(name);
39769
+ }
39770
+ async function fetchDatasetSchema(proxyFetch, wsId, datasetId) {
39771
+ const pathPrefix = wsId === MY_WORKSPACE ? "" : `/groups/${encodeURIComponent(wsId)}`;
39772
+ const daxQuery = 'EVALUATE SELECTCOLUMNS(COLUMNSTATISTICS(), "T", [Table Name], "C", [Column Name])';
39773
+ const res = await apiFetch35(
39774
+ proxyFetch,
39775
+ `${pathPrefix}/datasets/${encodeURIComponent(datasetId)}/executeQueries`,
39776
+ {
39777
+ method: "POST",
39778
+ headers: { "Content-Type": "application/json" },
39779
+ body: JSON.stringify({
39780
+ queries: [{ query: daxQuery }],
39781
+ serializerSettings: { includeNulls: true }
39782
+ })
39783
+ }
39784
+ );
39785
+ if (!res.ok) return /* @__PURE__ */ new Map();
39786
+ const data = await res.json();
39787
+ const rows = data.results?.[0]?.tables?.[0]?.rows ?? [];
39788
+ const schema = /* @__PURE__ */ new Map();
39789
+ for (const row of rows) {
39790
+ const table = row["[T]"] ?? "";
39791
+ const column = row["[C]"] ?? "";
39792
+ if (!table || !column) continue;
39793
+ if (INTERNAL_TABLE_PREFIXES.some((p) => table.startsWith(p))) continue;
39794
+ if (isInternalColumn(column)) continue;
39795
+ if (!schema.has(table)) schema.set(table, []);
39796
+ schema.get(table).push(column);
39797
+ }
39798
+ return schema;
39799
+ }
39800
+ function workspaceName(wsId, groupById, language) {
39801
+ if (wsId === MY_WORKSPACE) {
39802
+ return language === "ja" ? "\u30DE\u30A4 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "My workspace";
39803
+ }
39804
+ return groupById.get(wsId)?.name ?? wsId;
39805
+ }
39806
+ async function resolveWorkspaceIds(selected, proxyFetch) {
39807
+ return resolveSetupSelection({
39808
+ selected,
39809
+ allSentinel: ALL_WORKSPACES,
39810
+ fetchAll: async () => {
39811
+ const groups = await listGroups(proxyFetch);
39812
+ return [MY_WORKSPACE, ...groups.map((g) => g.id).filter(Boolean)];
39813
+ },
39814
+ limit: POWERBI_SETUP_MAX_WORKSPACES
39815
+ });
39816
+ }
39817
+ function compoundKey(wsId, objectId) {
39818
+ return `${wsId}:${objectId}`;
39819
+ }
39820
+ function parseCompoundKey(key) {
39821
+ const idx = key.indexOf(":");
39822
+ if (idx < 0) return { wsId: "", objectId: key };
39823
+ return { wsId: key.slice(0, idx), objectId: key.slice(idx + 1) };
39824
+ }
39825
+ async function fetchObjectOptions(state, resource, allSentinel, allLabelJa, allLabelEn, rt) {
39826
+ if (!state.workspaces?.length || !state.resources?.includes(resource))
39827
+ return [];
39828
+ const wsIds = await resolveWorkspaceIds(
39829
+ state.workspaces,
39830
+ rt.config.proxyFetch
39831
+ );
39832
+ const allGroups = await listGroups(rt.config.proxyFetch);
39833
+ const groupById = new Map(allGroups.map((g) => [g.id, g]));
39834
+ const multiWorkspace = wsIds.length > 1;
39835
+ const options = [];
39836
+ for (const wsId of wsIds) {
39837
+ const wsLabel = workspaceName(wsId, groupById, rt.language);
39838
+ const items = await listResource(rt.config.proxyFetch, wsId, resource);
39839
+ for (const item of items) {
39840
+ if (!item.id) continue;
39841
+ options.push({
39842
+ value: compoundKey(wsId, item.id),
39843
+ label: multiWorkspace ? `${wsLabel} / ${resourceLabel(item)}` : resourceLabel(item)
39844
+ });
39845
+ }
39846
+ }
39847
+ if (options.length === 0) return [];
39848
+ return [
39849
+ {
39850
+ value: allSentinel,
39851
+ label: rt.language === "ja" ? allLabelJa : allLabelEn
39852
+ },
39853
+ ...options
39854
+ ];
39855
+ }
38894
39856
  var powerbiOauthSetupFlow = {
38895
39857
  initialState: () => ({}),
38896
39858
  steps: [
@@ -38909,6 +39871,10 @@ var powerbiOauthSetupFlow = {
38909
39871
  value: ALL_WORKSPACES,
38910
39872
  label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "All workspaces"
38911
39873
  },
39874
+ {
39875
+ value: MY_WORKSPACE,
39876
+ label: rt.language === "ja" ? "\u30DE\u30A4 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "My workspace"
39877
+ },
38912
39878
  ...options
38913
39879
  ];
38914
39880
  },
@@ -38923,16 +39889,82 @@ var powerbiOauthSetupFlow = {
38923
39889
  },
38924
39890
  async fetchOptions(state, rt) {
38925
39891
  if (!state.workspaces?.length) return [];
38926
- const datasetsLabel = rt.language === "ja" ? "\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8" : "Datasets";
38927
- const reportsLabel = rt.language === "ja" ? "\u30EC\u30DD\u30FC\u30C8" : "Reports";
38928
- const dashboardsLabel = rt.language === "ja" ? "\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9" : "Dashboards";
38929
39892
  return [
38930
- { value: RESOURCE_DATASETS, label: datasetsLabel },
38931
- { value: RESOURCE_REPORTS, label: reportsLabel },
38932
- { value: RESOURCE_DASHBOARDS, label: dashboardsLabel }
39893
+ {
39894
+ value: RESOURCE_DATASETS,
39895
+ label: rt.language === "ja" ? "\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8" : "Datasets"
39896
+ },
39897
+ {
39898
+ value: RESOURCE_REPORTS,
39899
+ label: rt.language === "ja" ? "\u30EC\u30DD\u30FC\u30C8" : "Reports"
39900
+ },
39901
+ {
39902
+ value: RESOURCE_DASHBOARDS,
39903
+ label: rt.language === "ja" ? "\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9" : "Dashboards"
39904
+ }
38933
39905
  ];
38934
39906
  },
38935
39907
  applyAnswer: (state, answer) => ({ ...state, resources: answer })
39908
+ },
39909
+ {
39910
+ slug: "datasets",
39911
+ type: "multiSelect",
39912
+ question: {
39913
+ ja: "\u4F7F\u7528\u3059\u308B\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
39914
+ en: "Select the datasets you want to use (multi-select allowed)"
39915
+ },
39916
+ async fetchOptions(state, rt) {
39917
+ return fetchObjectOptions(
39918
+ state,
39919
+ RESOURCE_DATASETS,
39920
+ ALL_DATASETS,
39921
+ "\u3059\u3079\u3066\u306E\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8",
39922
+ "All datasets",
39923
+ rt
39924
+ );
39925
+ },
39926
+ applyAnswer: (state, answer) => ({ ...state, selectedDatasets: answer })
39927
+ },
39928
+ {
39929
+ slug: "reports",
39930
+ type: "multiSelect",
39931
+ question: {
39932
+ ja: "\u4F7F\u7528\u3059\u308B\u30EC\u30DD\u30FC\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
39933
+ en: "Select the reports you want to use (multi-select allowed)"
39934
+ },
39935
+ async fetchOptions(state, rt) {
39936
+ return fetchObjectOptions(
39937
+ state,
39938
+ RESOURCE_REPORTS,
39939
+ ALL_REPORTS,
39940
+ "\u3059\u3079\u3066\u306E\u30EC\u30DD\u30FC\u30C8",
39941
+ "All reports",
39942
+ rt
39943
+ );
39944
+ },
39945
+ applyAnswer: (state, answer) => ({ ...state, selectedReports: answer })
39946
+ },
39947
+ {
39948
+ slug: "dashboards",
39949
+ type: "multiSelect",
39950
+ question: {
39951
+ ja: "\u4F7F\u7528\u3059\u308B\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
39952
+ en: "Select the dashboards you want to use (multi-select allowed)"
39953
+ },
39954
+ async fetchOptions(state, rt) {
39955
+ return fetchObjectOptions(
39956
+ state,
39957
+ RESOURCE_DASHBOARDS,
39958
+ ALL_DASHBOARDS,
39959
+ "\u3059\u3079\u3066\u306E\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9",
39960
+ "All dashboards",
39961
+ rt
39962
+ );
39963
+ },
39964
+ applyAnswer: (state, answer) => ({
39965
+ ...state,
39966
+ selectedDashboards: answer
39967
+ })
38936
39968
  }
38937
39969
  ],
38938
39970
  async finalize(state, rt) {
@@ -38941,37 +39973,119 @@ var powerbiOauthSetupFlow = {
38941
39973
  }
38942
39974
  const allGroups = await listGroups(rt.config.proxyFetch);
38943
39975
  const groupById = new Map(allGroups.map((g) => [g.id, g]));
38944
- const targetIds = await resolveSetupSelection({
38945
- selected: state.workspaces,
38946
- allSentinel: ALL_WORKSPACES,
38947
- fetchAll: async () => allGroups.map((g) => g.id).filter((id) => id),
38948
- limit: POWERBI_SETUP_MAX_WORKSPACES
38949
- });
39976
+ const wsIds = await resolveWorkspaceIds(
39977
+ state.workspaces,
39978
+ rt.config.proxyFetch
39979
+ );
38950
39980
  const selectedResources = new Set(state.resources);
39981
+ async function resolveObjects(selected, allSentinel, resource) {
39982
+ const byWorkspace = /* @__PURE__ */ new Map();
39983
+ if (!selected || !selectedResources.has(resource)) return byWorkspace;
39984
+ if (selected.includes(allSentinel)) {
39985
+ for (const wsId of wsIds) {
39986
+ const items = await listResource(
39987
+ rt.config.proxyFetch,
39988
+ wsId,
39989
+ resource
39990
+ );
39991
+ const ids = items.map((i) => i.id).filter((id) => !!id);
39992
+ if (ids.length > 0) byWorkspace.set(wsId, new Set(ids));
39993
+ }
39994
+ } else {
39995
+ for (const key of selected) {
39996
+ if (key === allSentinel) continue;
39997
+ const { wsId, objectId } = parseCompoundKey(key);
39998
+ if (!byWorkspace.has(wsId)) byWorkspace.set(wsId, /* @__PURE__ */ new Set());
39999
+ byWorkspace.get(wsId).add(objectId);
40000
+ }
40001
+ }
40002
+ return byWorkspace;
40003
+ }
40004
+ const datasetsByWs = await resolveObjects(
40005
+ state.selectedDatasets,
40006
+ ALL_DATASETS,
40007
+ RESOURCE_DATASETS
40008
+ );
40009
+ const reportsByWs = await resolveObjects(
40010
+ state.selectedReports,
40011
+ ALL_REPORTS,
40012
+ RESOURCE_REPORTS
40013
+ );
40014
+ const dashboardsByWs = await resolveObjects(
40015
+ state.selectedDashboards,
40016
+ ALL_DASHBOARDS,
40017
+ RESOURCE_DASHBOARDS
40018
+ );
40019
+ const allWsIds = /* @__PURE__ */ new Set([
40020
+ ...datasetsByWs.keys(),
40021
+ ...reportsByWs.keys(),
40022
+ ...dashboardsByWs.keys()
40023
+ ]);
38951
40024
  const sections = ["## Power BI", ""];
38952
- if (!targetIds.length) {
38953
- sections.push("_No workspaces selected._", "");
40025
+ if (allWsIds.size === 0) {
40026
+ sections.push("_No resources selected._", "");
38954
40027
  return sections.join("\n");
38955
40028
  }
38956
- for (const id of targetIds) {
38957
- const group = groupById.get(id);
38958
- const name = group?.name ?? id;
38959
- sections.push(`### Workspace: ${name}`, "", `- id: \`${id}\``);
38960
- for (const resource of [
38961
- RESOURCE_DATASETS,
38962
- RESOURCE_REPORTS,
38963
- RESOURCE_DASHBOARDS
40029
+ for (const wsId of wsIds) {
40030
+ if (!allWsIds.has(wsId)) continue;
40031
+ const name = workspaceName(wsId, groupById, rt.language);
40032
+ sections.push(`### Workspace: ${name}`, "");
40033
+ if (wsId !== MY_WORKSPACE) {
40034
+ sections.push(`- id: \`${wsId}\``);
40035
+ }
40036
+ const datasetIds = datasetsByWs.get(wsId);
40037
+ if (datasetIds?.size) {
40038
+ const items = await listResource(
40039
+ rt.config.proxyFetch,
40040
+ wsId,
40041
+ RESOURCE_DATASETS
40042
+ );
40043
+ const filtered = items.filter(
40044
+ (item) => item.id && datasetIds.has(item.id)
40045
+ );
40046
+ for (const item of filtered.slice(0, RESOURCE_DISPLAY_LIMIT)) {
40047
+ sections.push(`#### Dataset: ${resourceLabel(item)}`, "");
40048
+ const schema = await fetchDatasetSchema(
40049
+ rt.config.proxyFetch,
40050
+ wsId,
40051
+ item.id
40052
+ );
40053
+ if (schema.size > 0) {
40054
+ for (const [table, columns] of schema) {
40055
+ sections.push(`##### Table: ${table}`, "");
40056
+ sections.push("| Column |");
40057
+ sections.push("|--------|");
40058
+ for (const col of columns) {
40059
+ sections.push(`| ${col} |`);
40060
+ }
40061
+ sections.push("");
40062
+ }
40063
+ } else {
40064
+ sections.push("_Schema not available._", "");
40065
+ }
40066
+ }
40067
+ }
40068
+ for (const [resource, selectedByWs, heading] of [
40069
+ [RESOURCE_REPORTS, reportsByWs, "Reports"],
40070
+ [RESOURCE_DASHBOARDS, dashboardsByWs, "Dashboards"]
38964
40071
  ]) {
38965
- if (!selectedResources.has(resource)) continue;
38966
- const items = await listResource(rt.config.proxyFetch, id, resource);
38967
- const heading = resource === RESOURCE_DATASETS ? "Datasets" : resource === RESOURCE_REPORTS ? "Reports" : "Dashboards";
38968
- sections.push(`- ${heading} (${items.length}):`);
38969
- for (const item of items.slice(0, RESOURCE_DISPLAY_LIMIT)) {
40072
+ const selectedIds = selectedByWs.get(wsId);
40073
+ if (!selectedIds?.size) continue;
40074
+ const items = await listResource(
40075
+ rt.config.proxyFetch,
40076
+ wsId,
40077
+ resource
40078
+ );
40079
+ const filtered = items.filter(
40080
+ (item) => item.id && selectedIds.has(item.id)
40081
+ );
40082
+ sections.push(`- ${heading} (${filtered.length}):`);
40083
+ for (const item of filtered.slice(0, RESOURCE_DISPLAY_LIMIT)) {
38970
40084
  sections.push(` - ${resourceLabel(item)}`);
38971
40085
  }
38972
- if (items.length > RESOURCE_DISPLAY_LIMIT) {
40086
+ if (filtered.length > RESOURCE_DISPLAY_LIMIT) {
38973
40087
  sections.push(
38974
- ` - \u2026and ${items.length - RESOURCE_DISPLAY_LIMIT} more`
40088
+ ` - \u2026and ${filtered.length - RESOURCE_DISPLAY_LIMIT} more`
38975
40089
  );
38976
40090
  }
38977
40091
  }
@@ -39242,7 +40356,11 @@ async function tableauProxyApiFetch(proxyFetch, params, path4, init) {
39242
40356
 
39243
40357
  // ../connectors/src/connectors/tableau/setup-flow.ts
39244
40358
  var ALL_PROJECTS7 = "__ALL_PROJECTS__";
40359
+ var ALL_WORKBOOKS = "__ALL_WORKBOOKS__";
39245
40360
  var TABLEAU_SETUP_MAX_PROJECTS = 10;
40361
+ var MAX_WORKBOOKS_DETAIL = 10;
40362
+ var MAX_VIEWS_PER_WORKBOOK = 5;
40363
+ var MAX_SAMPLE_ROWS = 5;
39246
40364
  var PAGE_SIZE2 = 100;
39247
40365
  async function listAllProjects3(rt) {
39248
40366
  const all = [];
@@ -39291,6 +40409,57 @@ async function listProjectResources(rt, resource) {
39291
40409
  }
39292
40410
  return all;
39293
40411
  }
40412
+ async function listViewsForWorkbook(rt, workbookId) {
40413
+ const res = await tableauProxyApiFetch(
40414
+ rt.config.proxyFetch,
40415
+ rt.params,
40416
+ `/sites/{siteId}/workbooks/${encodeURIComponent(workbookId)}/views`
40417
+ );
40418
+ if (!res.ok) return [];
40419
+ const data = await res.json();
40420
+ return data.views?.view ?? [];
40421
+ }
40422
+ async function fetchViewDataSample(rt, viewId) {
40423
+ try {
40424
+ const res = await tableauProxyApiFetch(
40425
+ rt.config.proxyFetch,
40426
+ rt.params,
40427
+ `/sites/{siteId}/views/${encodeURIComponent(viewId)}/data`
40428
+ );
40429
+ if (!res.ok) return null;
40430
+ const csv = await res.text();
40431
+ const lines = csv.trim().split("\n").filter(Boolean);
40432
+ if (lines.length === 0) return null;
40433
+ const columns = parseCsvLine(lines[0]);
40434
+ const rows = lines.slice(1, 1 + MAX_SAMPLE_ROWS).map(parseCsvLine);
40435
+ return { columns, rows };
40436
+ } catch {
40437
+ return null;
40438
+ }
40439
+ }
40440
+ function parseCsvLine(line) {
40441
+ const result = [];
40442
+ let current = "";
40443
+ let inQuotes = false;
40444
+ for (let i = 0; i < line.length; i++) {
40445
+ const ch = line[i];
40446
+ if (ch === '"') {
40447
+ if (inQuotes && line[i + 1] === '"') {
40448
+ current += '"';
40449
+ i++;
40450
+ } else {
40451
+ inQuotes = !inQuotes;
40452
+ }
40453
+ } else if (ch === "," && !inQuotes) {
40454
+ result.push(current.trim());
40455
+ current = "";
40456
+ } else {
40457
+ current += ch;
40458
+ }
40459
+ }
40460
+ result.push(current.trim());
40461
+ return result;
40462
+ }
39294
40463
  var tableauSetupFlow = {
39295
40464
  initialState: () => ({}),
39296
40465
  steps: [
@@ -39313,6 +40482,39 @@ var tableauSetupFlow = {
39313
40482
  ];
39314
40483
  },
39315
40484
  applyAnswer: (state, answer) => ({ ...state, projects: answer })
40485
+ },
40486
+ {
40487
+ slug: "workbooks",
40488
+ type: "multiSelect",
40489
+ question: {
40490
+ 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",
40491
+ en: "Select the workbooks to analyze (multi-select allowed)"
40492
+ },
40493
+ async fetchOptions(state, rt) {
40494
+ if (!state.projects?.length) return [];
40495
+ const allProjects = await listAllProjects3(rt);
40496
+ const targetIds = await resolveSetupSelection({
40497
+ selected: state.projects,
40498
+ allSentinel: ALL_PROJECTS7,
40499
+ fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
40500
+ limit: TABLEAU_SETUP_MAX_PROJECTS
40501
+ });
40502
+ const targetSet = new Set(targetIds);
40503
+ const workbooks = await listProjectResources(rt, "workbooks");
40504
+ const options = workbooks.filter((wb) => wb.id && wb.project?.id && targetSet.has(wb.project.id)).map((wb) => ({
40505
+ value: wb.id,
40506
+ label: wb.name ?? wb.id ?? "(unknown)"
40507
+ }));
40508
+ if (options.length === 0) return [];
40509
+ return [
40510
+ {
40511
+ value: ALL_WORKBOOKS,
40512
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF" : "All workbooks"
40513
+ },
40514
+ ...options
40515
+ ];
40516
+ },
40517
+ applyAnswer: (state, answer) => ({ ...state, workbooks: answer })
39316
40518
  }
39317
40519
  ],
39318
40520
  async finalize(state, rt) {
@@ -39321,25 +40523,33 @@ var tableauSetupFlow = {
39321
40523
  }
39322
40524
  const allProjects = await listAllProjects3(rt);
39323
40525
  const projectById = new Map(allProjects.map((p) => [p.id, p]));
39324
- const targetIds = await resolveSetupSelection({
40526
+ const targetProjectIds = await resolveSetupSelection({
39325
40527
  selected: state.projects,
39326
40528
  allSentinel: ALL_PROJECTS7,
39327
40529
  fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
39328
40530
  limit: TABLEAU_SETUP_MAX_PROJECTS
39329
40531
  });
39330
40532
  const sections = ["## Tableau", ""];
39331
- if (!targetIds.length) {
40533
+ if (!targetProjectIds.length) {
39332
40534
  sections.push("_No projects selected._", "");
39333
40535
  return sections.join("\n");
39334
40536
  }
39335
- const [workbooks, datasources] = await Promise.all([
40537
+ const [allWorkbooks, datasources] = await Promise.all([
39336
40538
  listProjectResources(rt, "workbooks"),
39337
40539
  listProjectResources(rt, "datasources")
39338
40540
  ]);
40541
+ const projectIdSet = new Set(targetProjectIds);
40542
+ const targetWorkbooks = await resolveSetupSelection({
40543
+ selected: state.workbooks ?? [],
40544
+ allSentinel: ALL_WORKBOOKS,
40545
+ fetchAll: async () => allWorkbooks.filter((wb) => wb.id && wb.project?.id && projectIdSet.has(wb.project.id)).map((wb) => wb.id).filter(Boolean),
40546
+ limit: MAX_WORKBOOKS_DETAIL
40547
+ });
40548
+ const targetWorkbookSet = new Set(targetWorkbooks);
39339
40549
  const workbooksByProject = /* @__PURE__ */ new Map();
39340
- for (const wb of workbooks) {
40550
+ for (const wb of allWorkbooks) {
39341
40551
  const pid = wb.project?.id;
39342
- if (!pid) continue;
40552
+ if (!pid || !projectIdSet.has(pid)) continue;
39343
40553
  const bucket = workbooksByProject.get(pid) ?? [];
39344
40554
  bucket.push(wb);
39345
40555
  workbooksByProject.set(pid, bucket);
@@ -39347,32 +40557,78 @@ var tableauSetupFlow = {
39347
40557
  const datasourcesByProject = /* @__PURE__ */ new Map();
39348
40558
  for (const ds of datasources) {
39349
40559
  const pid = ds.project?.id;
39350
- if (!pid) continue;
40560
+ if (!pid || !projectIdSet.has(pid)) continue;
39351
40561
  const bucket = datasourcesByProject.get(pid) ?? [];
39352
40562
  bucket.push(ds);
39353
40563
  datasourcesByProject.set(pid, bucket);
39354
40564
  }
39355
- for (const id of targetIds) {
39356
- const project = projectById.get(id);
39357
- const name = project?.name ?? id;
39358
- sections.push(`### Project: ${name}`, "", `- id: \`${id}\``);
39359
- const projectWorkbooks = workbooksByProject.get(id) ?? [];
39360
- sections.push(`- Workbooks (${projectWorkbooks.length}):`);
39361
- for (const wb of projectWorkbooks.slice(0, 25)) {
39362
- sections.push(` - ${wb.name ?? wb.id ?? "(unknown)"}`);
39363
- }
39364
- if (projectWorkbooks.length > 25) {
39365
- sections.push(` - \u2026and ${projectWorkbooks.length - 25} more`);
40565
+ const allDiscoveredColumns = [];
40566
+ for (const pid of targetProjectIds) {
40567
+ const project = projectById.get(pid);
40568
+ sections.push(`### Project: ${project?.name ?? pid}`, "");
40569
+ const projectWorkbooks = workbooksByProject.get(pid) ?? [];
40570
+ for (const wb of projectWorkbooks) {
40571
+ if (!wb.id) continue;
40572
+ const isDetailed = targetWorkbookSet.has(wb.id);
40573
+ sections.push(`#### Workbook: ${wb.name ?? wb.id}`, "");
40574
+ if (!isDetailed) continue;
40575
+ const views = await listViewsForWorkbook(rt, wb.id);
40576
+ if (views.length === 0) {
40577
+ sections.push("_No views found._", "");
40578
+ continue;
40579
+ }
40580
+ for (const view of views.slice(0, MAX_VIEWS_PER_WORKBOOK)) {
40581
+ if (!view.id) continue;
40582
+ sections.push(`##### View: ${view.name ?? view.id}`, "");
40583
+ const sample = await fetchViewDataSample(rt, view.id);
40584
+ if (!sample || sample.columns.length === 0) {
40585
+ sections.push("_Data not available._", "");
40586
+ continue;
40587
+ }
40588
+ allDiscoveredColumns.push(...sample.columns);
40589
+ sections.push(`| ${sample.columns.join(" | ")} |`);
40590
+ sections.push(`|${sample.columns.map(() => "---").join("|")}|`);
40591
+ for (const row of sample.rows) {
40592
+ const cells = row.map((c) => c.replace(/\|/g, "\\|"));
40593
+ sections.push(`| ${cells.join(" | ")} |`);
40594
+ }
40595
+ sections.push("");
40596
+ }
39366
40597
  }
39367
- const projectDatasources = datasourcesByProject.get(id) ?? [];
39368
- sections.push(`- Datasources (${projectDatasources.length}):`);
39369
- for (const ds of projectDatasources.slice(0, 25)) {
39370
- sections.push(` - ${ds.name ?? ds.id ?? "(unknown)"}`);
40598
+ const projectDatasources = datasourcesByProject.get(pid) ?? [];
40599
+ if (projectDatasources.length > 0) {
40600
+ sections.push(`#### Datasources (${projectDatasources.length})`, "");
40601
+ for (const ds of projectDatasources.slice(0, 25)) {
40602
+ sections.push(`- ${ds.name ?? ds.id ?? "(unknown)"}`);
40603
+ }
40604
+ sections.push("");
39371
40605
  }
39372
- if (projectDatasources.length > 25) {
39373
- sections.push(` - \u2026and ${projectDatasources.length - 25} more`);
40606
+ }
40607
+ if (allDiscoveredColumns.length > 0) {
40608
+ const unique = [...new Set(allDiscoveredColumns.map((c) => c.toLowerCase()))];
40609
+ const themes = [];
40610
+ const hasPattern = (keywords) => unique.some((c) => keywords.some((k) => c.includes(k)));
40611
+ if (hasPattern(["revenue", "sales", "amount", "price", "cost", "profit", "\u58F2\u4E0A", "\u91D1\u984D", "\u5229\u76CA"]))
40612
+ themes.push("Revenue & profitability analysis (trends, breakdown by segment)");
40613
+ if (hasPattern(["date", "month", "year", "quarter", "day", "\u65E5\u4ED8", "\u6708", "\u5E74", "\u671F"]))
40614
+ themes.push("Time-series trend analysis (YoY, MoM comparisons)");
40615
+ if (hasPattern(["region", "country", "city", "state", "\u5730\u57DF", "\u56FD", "\u90FD\u5E02", "\u5E02"]))
40616
+ themes.push("Geographic/regional performance comparison");
40617
+ if (hasPattern(["product", "category", "item", "sku", "\u5546\u54C1", "\u30AB\u30C6\u30B4\u30EA"]))
40618
+ themes.push("Product/category mix analysis");
40619
+ if (hasPattern(["customer", "user", "account", "\u9867\u5BA2", "\u30E6\u30FC\u30B6\u30FC"]))
40620
+ themes.push("Customer segmentation and behavior analysis");
40621
+ if (hasPattern(["quantity", "count", "volume", "\u6570\u91CF", "\u4EF6\u6570"]))
40622
+ themes.push("Volume and demand pattern analysis");
40623
+ if (hasPattern(["rate", "ratio", "percentage", "%", "\u7387"]))
40624
+ themes.push("KPI ratio tracking and benchmarking");
40625
+ if (themes.length > 0) {
40626
+ sections.push("### Suggested Analysis Themes", "");
40627
+ for (const theme of themes) {
40628
+ sections.push(`- ${theme}`);
40629
+ }
40630
+ sections.push("");
39374
40631
  }
39375
- sections.push("");
39376
40632
  }
39377
40633
  return sections.join("\n");
39378
40634
  }
@@ -39382,27 +40638,36 @@ var tableauSetupFlow = {
39382
40638
  import { z as z98 } from "zod";
39383
40639
  var DEFAULT_API_VERSION2 = "3.28";
39384
40640
  var REQUEST_TIMEOUT_MS75 = 6e4;
39385
- async function fetchTableauSession(connectionId) {
39386
- const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
39387
- const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
39388
- if (!token || !sandboxId) {
39389
- throw new Error(
39390
- "Tableau session manager is not configured. Missing INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL or INTERNAL_SQUADBASE_SANDBOX_ID."
39391
- );
40641
+ var cachedToken33 = null;
40642
+ async function getProxyToken33(config) {
40643
+ if (cachedToken33 && cachedToken33.expiresAt > Date.now() + 6e4) {
40644
+ return cachedToken33.token;
39392
40645
  }
39393
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
39394
- const url = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
40646
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
39395
40647
  const res = await fetch(url, {
39396
40648
  method: "POST",
39397
- headers: { Authorization: `Bearer ${token}` }
40649
+ headers: {
40650
+ "Content-Type": "application/json",
40651
+ "x-api-key": config.appApiKey,
40652
+ "project-id": config.projectId
40653
+ },
40654
+ body: JSON.stringify({
40655
+ sandboxId: config.sandboxId,
40656
+ issuedBy: "coding-agent"
40657
+ })
39398
40658
  });
39399
40659
  if (!res.ok) {
39400
- const errBody = await res.text().catch(() => res.statusText);
40660
+ const errorText = await res.text().catch(() => res.statusText);
39401
40661
  throw new Error(
39402
- `Failed to fetch Tableau session from backend-api (HTTP ${res.status}): ${errBody}`
40662
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
39403
40663
  );
39404
40664
  }
39405
- return await res.json();
40665
+ const data = await res.json();
40666
+ cachedToken33 = {
40667
+ token: data.token,
40668
+ expiresAt: new Date(data.expiresAt).getTime()
40669
+ };
40670
+ return data.token;
39406
40671
  }
39407
40672
  function buildBaseUrl7(serverUrl, apiVersion) {
39408
40673
  return `${serverUrl.replace(/\/$/, "")}/api/${apiVersion}`;
@@ -39444,7 +40709,7 @@ All paths are relative to {serverUrl}/api/{apiVersion}. Use the literal placehol
39444
40709
  Accept and Content-Type headers default to application/json so list responses come back as JSON instead of Tableau's default XML.`,
39445
40710
  inputSchema: inputSchema98,
39446
40711
  outputSchema: outputSchema98,
39447
- async execute({ connectionId, method, path: path4, queryParams, body }, connections) {
40712
+ async execute({ connectionId, method, path: path4, queryParams, body }, connections, config) {
39448
40713
  const connection = connections.find((c) => c.id === connectionId);
39449
40714
  if (!connection) {
39450
40715
  return {
@@ -39459,50 +40724,42 @@ Accept and Content-Type headers default to application/json so list responses co
39459
40724
  const serverUrl = parameters81.serverUrl.getValue(connection);
39460
40725
  const apiVersion = parameters81.apiVersion.tryGetValue(connection) || DEFAULT_API_VERSION2;
39461
40726
  const trimmedPath = path4.trim().replace(/^([^/])/, "/$1");
39462
- const queryString = queryParams ? `?${new URLSearchParams(queryParams).toString()}` : "";
39463
40727
  const baseUrl = buildBaseUrl7(serverUrl, apiVersion);
40728
+ let url = `${baseUrl}${trimmedPath}`;
40729
+ if (queryParams) {
40730
+ url += `?${new URLSearchParams(queryParams).toString()}`;
40731
+ }
40732
+ const token = await getProxyToken33(config.oauthProxy);
40733
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
39464
40734
  const controller = new AbortController();
39465
40735
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS75);
39466
40736
  try {
39467
- let attempt = 0;
39468
- while (true) {
39469
- const session = await fetchTableauSession(connectionId);
39470
- const resolvedPath = trimmedPath.replace(
39471
- /\{siteId\}/g,
39472
- session.siteId
39473
- );
39474
- const url = `${baseUrl}${resolvedPath}${queryString}`;
39475
- const init = {
40737
+ const response = await fetch(proxyUrl, {
40738
+ method: "POST",
40739
+ headers: {
40740
+ "Content-Type": "application/json",
40741
+ Authorization: `Bearer ${token}`
40742
+ },
40743
+ body: JSON.stringify({
40744
+ url,
39476
40745
  method,
39477
- headers: {
39478
- "X-Tableau-Auth": session.authToken,
39479
- Accept: "application/json",
39480
- "Content-Type": "application/json"
39481
- },
39482
- signal: controller.signal
39483
- };
39484
- if (body !== void 0) {
39485
- init.body = JSON.stringify(body);
39486
- }
39487
- const response = await fetch(url, init);
39488
- const text = await response.text();
39489
- const data = text ? (() => {
39490
- try {
39491
- return JSON.parse(text);
39492
- } catch {
39493
- return text;
39494
- }
39495
- })() : null;
39496
- if (response.status === 401 && attempt === 0) {
39497
- attempt++;
39498
- continue;
39499
- }
39500
- if (!response.ok) {
39501
- const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
39502
- return { success: false, error: errorMessage };
40746
+ ...body !== void 0 ? { body } : {}
40747
+ }),
40748
+ signal: controller.signal
40749
+ });
40750
+ const text = await response.text();
40751
+ const data = text ? (() => {
40752
+ try {
40753
+ return JSON.parse(text);
40754
+ } catch {
40755
+ return text;
39503
40756
  }
39504
- return { success: true, status: response.status, data };
40757
+ })() : null;
40758
+ if (!response.ok) {
40759
+ const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
40760
+ return { success: false, error: errorMessage };
39505
40761
  }
40762
+ return { success: true, status: response.status, data };
39506
40763
  } finally {
39507
40764
  clearTimeout(timeout);
39508
40765
  }
@@ -39694,12 +40951,12 @@ export default async function handler(c: Context) {
39694
40951
  import { z as z99 } from "zod";
39695
40952
  var BASE_HOST13 = "https://graph.microsoft.com";
39696
40953
  var BASE_PATH_SEGMENT19 = "/v1.0";
39697
- var BASE_URL65 = `${BASE_HOST13}${BASE_PATH_SEGMENT19}`;
40954
+ var BASE_URL66 = `${BASE_HOST13}${BASE_PATH_SEGMENT19}`;
39698
40955
  var REQUEST_TIMEOUT_MS76 = 6e4;
39699
- var cachedToken33 = null;
39700
- async function getProxyToken33(config) {
39701
- if (cachedToken33 && cachedToken33.expiresAt > Date.now() + 6e4) {
39702
- return cachedToken33.token;
40956
+ var cachedToken34 = null;
40957
+ async function getProxyToken34(config) {
40958
+ if (cachedToken34 && cachedToken34.expiresAt > Date.now() + 6e4) {
40959
+ return cachedToken34.token;
39703
40960
  }
39704
40961
  const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
39705
40962
  const res = await fetch(url, {
@@ -39721,7 +40978,7 @@ async function getProxyToken33(config) {
39721
40978
  );
39722
40979
  }
39723
40980
  const data = await res.json();
39724
- cachedToken33 = {
40981
+ cachedToken34 = {
39725
40982
  token: data.token,
39726
40983
  expiresAt: new Date(data.expiresAt).getTime()
39727
40984
  };
@@ -39773,12 +41030,12 @@ For full-text search use the \`$search\` query parameter (must be wrapped in dou
39773
41030
  );
39774
41031
  try {
39775
41032
  const normalizedPath = normalizeRequestPath(path4, BASE_PATH_SEGMENT19);
39776
- let url = `${BASE_URL65}${normalizedPath}`;
41033
+ let url = `${BASE_URL66}${normalizedPath}`;
39777
41034
  if (queryParams) {
39778
41035
  const searchParams = new URLSearchParams(queryParams);
39779
41036
  url += `?${searchParams.toString()}`;
39780
41037
  }
39781
- const token = await getProxyToken33(config.oauthProxy);
41038
+ const token = await getProxyToken34(config.oauthProxy);
39782
41039
  const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
39783
41040
  const controller = new AbortController();
39784
41041
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS76);