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