@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/cli/index.js CHANGED
@@ -856,19 +856,28 @@ async function runSetupFlow(flow, params, ctx, config) {
856
856
  };
857
857
  let state = flow.initialState();
858
858
  let answerIdx = 0;
859
+ const pendingParameterUpdates = [];
859
860
  for (const step of flow.steps) {
860
861
  const ans = ctx.answers[answerIdx];
861
862
  if (ans && ans.questionSlug === step.slug) {
862
863
  state = step.applyAnswer(state, ans.answer);
864
+ if (step.toParameterUpdates) {
865
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
866
+ }
863
867
  answerIdx += 1;
864
868
  continue;
865
869
  }
870
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
866
871
  if (step.type === "text") {
867
872
  return {
868
873
  type: "nextQuestion",
869
874
  questionSlug: step.slug,
870
875
  question: step.question[ctx.language],
871
- questionType: "text"
876
+ questionType: "text",
877
+ allowFreeText: resolvedAllowFreeText,
878
+ ...pendingParameterUpdates.length > 0 && {
879
+ parameterUpdates: pendingParameterUpdates
880
+ }
872
881
  };
873
882
  }
874
883
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -880,11 +889,21 @@ async function runSetupFlow(flow, params, ctx, config) {
880
889
  questionSlug: step.slug,
881
890
  question: step.question[ctx.language],
882
891
  questionType: step.type,
883
- options
892
+ options,
893
+ allowFreeText: resolvedAllowFreeText,
894
+ ...pendingParameterUpdates.length > 0 && {
895
+ parameterUpdates: pendingParameterUpdates
896
+ }
884
897
  };
885
898
  }
886
899
  const dataInvestigationResult = await flow.finalize(state, runtime);
887
- return { type: "fulfilled", dataInvestigationResult };
900
+ return {
901
+ type: "fulfilled",
902
+ dataInvestigationResult,
903
+ ...pendingParameterUpdates.length > 0 && {
904
+ parameterUpdates: pendingParameterUpdates
905
+ }
906
+ };
888
907
  }
889
908
  async function resolveSetupSelection(params) {
890
909
  const { selected, allSentinel, fetchAll, limit } = params;
@@ -1063,7 +1082,46 @@ var INTERNAL_SCHEMAS = /* @__PURE__ */ new Set([
1063
1082
  "READER_ACCOUNT_USAGE",
1064
1083
  "DATA_SHARING_USAGE"
1065
1084
  ]);
1085
+ var OBJECT_TYPE_LABELS = {
1086
+ table: { ja: "\u30C6\u30FC\u30D6\u30EB", en: "Table" },
1087
+ view: { ja: "\u30D3\u30E5\u30FC", en: "View" },
1088
+ stream: { ja: "\u30B9\u30C8\u30EA\u30FC\u30E0", en: "Stream" },
1089
+ dynamic_table: { ja: "\u30C0\u30A4\u30CA\u30DF\u30C3\u30AF\u30C6\u30FC\u30D6\u30EB", en: "Dynamic Table" },
1090
+ pipe: { ja: "\u30D1\u30A4\u30D7", en: "Pipe" },
1091
+ task: { ja: "\u30BF\u30B9\u30AF", en: "Task" }
1092
+ };
1093
+ var HEADING_LABELS = {
1094
+ table: "Table",
1095
+ view: "View",
1096
+ stream: "Stream",
1097
+ dynamic_table: "Dynamic Table",
1098
+ pipe: "Pipe",
1099
+ task: "Task"
1100
+ };
1066
1101
  function createSnowflakeSetupFlow(runQuery11) {
1102
+ const objectTypes = /* @__PURE__ */ new Map();
1103
+ async function fetchAllObjects(params, db, schema) {
1104
+ objectTypes.clear();
1105
+ const typeQueries = [
1106
+ ["table", `SHOW TABLES IN SCHEMA "${db}"."${schema}"`],
1107
+ ["view", `SHOW VIEWS IN SCHEMA "${db}"."${schema}"`],
1108
+ ["stream", `SHOW STREAMS IN SCHEMA "${db}"."${schema}"`],
1109
+ ["dynamic_table", `SHOW DYNAMIC TABLES IN SCHEMA "${db}"."${schema}"`],
1110
+ ["pipe", `SHOW PIPES IN SCHEMA "${db}"."${schema}"`],
1111
+ ["task", `SHOW TASKS IN SCHEMA "${db}"."${schema}"`]
1112
+ ];
1113
+ const results = await Promise.all(
1114
+ typeQueries.map(([, sql]) => runQuery11(params, sql).catch(() => []))
1115
+ );
1116
+ for (let i = 0; i < results.length; i++) {
1117
+ const type = typeQueries[i][0];
1118
+ for (const r of results[i]) {
1119
+ const name = String(r["name"] ?? "");
1120
+ if (name) objectTypes.set(name, type);
1121
+ }
1122
+ }
1123
+ return Array.from(objectTypes.keys());
1124
+ }
1067
1125
  return {
1068
1126
  initialState: () => ({}),
1069
1127
  steps: [
@@ -1103,22 +1161,27 @@ function createSnowflakeSetupFlow(runQuery11) {
1103
1161
  slug: "tables",
1104
1162
  type: "multiSelect",
1105
1163
  question: {
1106
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
1107
- en: "Select target tables (multi-select allowed)"
1164
+ 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",
1165
+ en: "Select target objects (tables, views, streams, etc. \u2014 multi-select allowed)"
1108
1166
  },
1109
1167
  async fetchOptions(state, rt) {
1110
1168
  if (!state.database || !state.schema) return [];
1111
- const rows = await runQuery11(
1169
+ const allNames = await fetchAllObjects(
1112
1170
  rt.params,
1113
- `SHOW TABLES IN SCHEMA "${state.database}"."${state.schema}"`
1171
+ state.database,
1172
+ state.schema
1114
1173
  );
1115
- const tableOptions = rows.map((r) => String(r["name"] ?? "")).filter((name) => name).map((value) => ({ value }));
1174
+ const options = allNames.map((name) => {
1175
+ const type = objectTypes.get(name) ?? "table";
1176
+ const typeLabel = OBJECT_TYPE_LABELS[type][rt.language];
1177
+ return { value: name, label: `${name} (${typeLabel})` };
1178
+ });
1116
1179
  return [
1117
1180
  {
1118
1181
  value: ALL_TABLES,
1119
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
1182
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8" : "All objects"
1120
1183
  },
1121
- ...tableOptions
1184
+ ...options
1122
1185
  ];
1123
1186
  },
1124
1187
  applyAnswer: (state, answer) => ({ ...state, tables: answer })
@@ -1130,16 +1193,10 @@ function createSnowflakeSetupFlow(runQuery11) {
1130
1193
  }
1131
1194
  const db = state.database;
1132
1195
  const schema = state.schema;
1133
- const targetTables = await resolveSetupSelection({
1196
+ const targetObjects = await resolveSetupSelection({
1134
1197
  selected: state.tables,
1135
1198
  allSentinel: ALL_TABLES,
1136
- fetchAll: async () => {
1137
- const rows = await runQuery11(
1138
- rt.params,
1139
- `SHOW TABLES IN SCHEMA "${db}"."${schema}"`
1140
- );
1141
- return rows.map((r) => String(r["name"] ?? "")).filter((name) => name);
1142
- },
1199
+ fetchAll: () => fetchAllObjects(rt.params, db, schema),
1143
1200
  limit: SNOWFLAKE_SETUP_MAX_TABLES
1144
1201
  });
1145
1202
  const sections = [
@@ -1150,24 +1207,66 @@ function createSnowflakeSetupFlow(runQuery11) {
1150
1207
  `#### Schema: ${schema}`,
1151
1208
  ""
1152
1209
  ];
1153
- for (const table of targetTables) {
1154
- const cols = await runQuery11(
1155
- rt.params,
1156
- `DESCRIBE TABLE "${db}"."${schema}"."${table}"`
1157
- );
1158
- sections.push(`##### Table: ${table}`, "");
1159
- sections.push("| Column | Type | Nullable | Default |");
1160
- sections.push("|--------|------|----------|---------|");
1161
- for (const c of cols) {
1162
- const name = String(c["name"] ?? "");
1163
- const type = String(c["type"] ?? "");
1164
- const nullable = String(c["null?"] ?? "");
1165
- const defaultValue = c["default"] == null ? "-" : String(c["default"]);
1166
- sections.push(
1167
- `| ${name} | ${type} | ${nullable} | ${defaultValue} |`
1168
- );
1210
+ for (const name of targetObjects) {
1211
+ const type = objectTypes.get(name) ?? "table";
1212
+ const heading = HEADING_LABELS[type];
1213
+ switch (type) {
1214
+ case "table":
1215
+ case "view":
1216
+ case "dynamic_table":
1217
+ case "stream": {
1218
+ const cols = await runQuery11(
1219
+ rt.params,
1220
+ `DESCRIBE TABLE "${db}"."${schema}"."${name}"`
1221
+ ).catch(() => []);
1222
+ sections.push(`##### ${heading}: ${name}`, "");
1223
+ if (cols.length > 0) {
1224
+ sections.push("| Column | Type | Nullable | Default |");
1225
+ sections.push("|--------|------|----------|---------|");
1226
+ for (const c of cols) {
1227
+ const colName = String(c["name"] ?? "");
1228
+ const colType = String(c["type"] ?? "");
1229
+ const nullable = String(c["null?"] ?? "");
1230
+ const defaultValue = c["default"] == null ? "-" : String(c["default"]);
1231
+ sections.push(
1232
+ `| ${colName} | ${colType} | ${nullable} | ${defaultValue} |`
1233
+ );
1234
+ }
1235
+ }
1236
+ sections.push("");
1237
+ break;
1238
+ }
1239
+ case "pipe": {
1240
+ const rows = await runQuery11(
1241
+ rt.params,
1242
+ `SHOW PIPES LIKE '${name.replace(/'/g, "''")}' IN SCHEMA "${db}"."${schema}"`
1243
+ ).catch(() => []);
1244
+ sections.push(`##### Pipe: ${name}`, "");
1245
+ if (rows.length > 0) {
1246
+ const definition = String(rows[0]["definition"] ?? "-");
1247
+ sections.push(`Definition: \`${definition}\``, "");
1248
+ }
1249
+ sections.push("");
1250
+ break;
1251
+ }
1252
+ case "task": {
1253
+ const rows = await runQuery11(
1254
+ rt.params,
1255
+ `SHOW TASKS LIKE '${name.replace(/'/g, "''")}' IN SCHEMA "${db}"."${schema}"`
1256
+ ).catch(() => []);
1257
+ sections.push(`##### Task: ${name}`, "");
1258
+ if (rows.length > 0) {
1259
+ const schedule = String(rows[0]["schedule"] ?? "-");
1260
+ const taskState = String(rows[0]["state"] ?? "-");
1261
+ const definition = String(rows[0]["definition"] ?? "-");
1262
+ sections.push(`Schedule: ${schedule}`, "");
1263
+ sections.push(`State: ${taskState}`, "");
1264
+ sections.push(`Definition: \`${definition}\``, "");
1265
+ }
1266
+ sections.push("");
1267
+ break;
1268
+ }
1169
1269
  }
1170
- sections.push("");
1171
1270
  }
1172
1271
  return sections.join("\n");
1173
1272
  }
@@ -1980,8 +2079,8 @@ var postgresqlSetupFlow = {
1980
2079
  slug: "tables",
1981
2080
  type: "multiSelect",
1982
2081
  question: {
1983
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
1984
- en: "Select target tables (multi-select allowed)"
2082
+ 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",
2083
+ en: "Select target tables and views (multi-select allowed)"
1985
2084
  },
1986
2085
  async fetchOptions(state, rt) {
1987
2086
  if (!state.schema) return [];
@@ -1990,7 +2089,7 @@ var postgresqlSetupFlow = {
1990
2089
  return [
1991
2090
  {
1992
2091
  value: ALL_TABLES2,
1993
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
2092
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
1994
2093
  },
1995
2094
  ...tableOptions
1996
2095
  ];
@@ -2327,8 +2426,8 @@ var mysqlSetupFlow = {
2327
2426
  slug: "tables",
2328
2427
  type: "multiSelect",
2329
2428
  question: {
2330
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
2331
- en: "Select target tables (multi-select allowed)"
2429
+ 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",
2430
+ en: "Select target tables and views (multi-select allowed)"
2332
2431
  },
2333
2432
  async fetchOptions(state, rt) {
2334
2433
  if (!state.database) return [];
@@ -2337,7 +2436,7 @@ var mysqlSetupFlow = {
2337
2436
  return [
2338
2437
  {
2339
2438
  value: ALL_TABLES3,
2340
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
2439
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
2341
2440
  },
2342
2441
  ...tableOptions
2343
2442
  ];
@@ -2788,6 +2887,72 @@ var bigqueryOnboarding = new ConnectorOnboarding({
2788
2887
  }
2789
2888
  });
2790
2889
 
2890
+ // ../connectors/src/table-grouping.ts
2891
+ var SHARD_GROUP_PREFIX = "__SHARD__";
2892
+ var DEFAULT_MIN_SHARD_GROUP_SIZE = 3;
2893
+ function extractDateShardSuffix(name) {
2894
+ const m = name.match(/^(.+)_(\d{8})$/);
2895
+ if (!m) return null;
2896
+ const [, prefix, suffix] = m;
2897
+ const y = +suffix.slice(0, 4);
2898
+ const mo = +suffix.slice(4, 6);
2899
+ const d = +suffix.slice(6, 8);
2900
+ if (y < 2e3 || y > 2099 || mo < 1 || mo > 12 || d < 1 || d > 31) {
2901
+ return null;
2902
+ }
2903
+ return { prefix, suffix };
2904
+ }
2905
+ function detectDateShardCandidates(tableNames, minCount = DEFAULT_MIN_SHARD_GROUP_SIZE) {
2906
+ const prefixMap = /* @__PURE__ */ new Map();
2907
+ const nonSharded = [];
2908
+ for (const name of tableNames) {
2909
+ const parsed = extractDateShardSuffix(name);
2910
+ if (parsed) {
2911
+ const existing = prefixMap.get(parsed.prefix);
2912
+ if (existing) {
2913
+ existing.push(name);
2914
+ } else {
2915
+ prefixMap.set(parsed.prefix, [name]);
2916
+ }
2917
+ } else {
2918
+ nonSharded.push(name);
2919
+ }
2920
+ }
2921
+ const candidates = [];
2922
+ const singles = [...nonSharded];
2923
+ for (const [prefix, members] of prefixMap) {
2924
+ if (members.length >= minCount) {
2925
+ candidates.push({ prefix, count: members.length, members });
2926
+ } else {
2927
+ singles.push(...members);
2928
+ }
2929
+ }
2930
+ return { singles, candidates };
2931
+ }
2932
+ function schemasMatch(columnsA, columnsB) {
2933
+ if (columnsA.length !== columnsB.length) return false;
2934
+ return columnsA.every(
2935
+ (a, i) => String(a["column_name"] ?? "") === String(columnsB[i]["column_name"] ?? "") && String(a["data_type"] ?? "") === String(columnsB[i]["data_type"] ?? "")
2936
+ );
2937
+ }
2938
+ function isShardGroupValue(value) {
2939
+ return value.startsWith(SHARD_GROUP_PREFIX);
2940
+ }
2941
+ function getShardGroupPrefix(value) {
2942
+ return value.slice(SHARD_GROUP_PREFIX.length);
2943
+ }
2944
+ function shardGroupValue(prefix) {
2945
+ return `${SHARD_GROUP_PREFIX}${prefix}`;
2946
+ }
2947
+ function buildGroupedTableOptions(singles, confirmedGroups, language) {
2948
+ const groupOptions = confirmedGroups.sort((a, b) => a.prefix.localeCompare(b.prefix)).map((g) => ({
2949
+ value: shardGroupValue(g.prefix),
2950
+ label: language === "ja" ? `${g.prefix}_* (\u65E5\u4ED8\u30B7\u30E3\u30FC\u30C9: ${g.count} \u30C6\u30FC\u30D6\u30EB)` : `${g.prefix}_* (date-sharded: ${g.count} tables)`
2951
+ }));
2952
+ const singleOptions = singles.sort().map((name) => ({ value: name }));
2953
+ return [...groupOptions, ...singleOptions];
2954
+ }
2955
+
2791
2956
  // ../connectors/src/connectors/bigquery/utils.ts
2792
2957
  async function runQuery5(params, sql) {
2793
2958
  const { BigQuery } = await import("@google-cloud/bigquery");
@@ -2818,13 +2983,23 @@ async function listProjects(params) {
2818
2983
  scopes: ["https://www.googleapis.com/auth/bigquery"]
2819
2984
  });
2820
2985
  const client = await auth.getClient();
2821
- const res = await client.request({
2822
- url: "https://bigquery.googleapis.com/bigquery/v2/projects"
2823
- });
2824
- return (res.data.projects ?? []).map((p) => ({
2825
- projectId: p.projectReference?.projectId ?? "",
2826
- friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
2827
- })).filter((p) => p.projectId !== "");
2986
+ const projects = [];
2987
+ let pageToken;
2988
+ do {
2989
+ const url = pageToken ? `https://bigquery.googleapis.com/bigquery/v2/projects?pageToken=${encodeURIComponent(pageToken)}` : "https://bigquery.googleapis.com/bigquery/v2/projects";
2990
+ const res = await client.request({ url });
2991
+ for (const p of res.data.projects ?? []) {
2992
+ const id = p.projectReference?.projectId ?? "";
2993
+ if (id) {
2994
+ projects.push({
2995
+ projectId: id,
2996
+ friendlyName: p.friendlyName ?? id
2997
+ });
2998
+ }
2999
+ }
3000
+ pageToken = res.data.nextPageToken;
3001
+ } while (pageToken);
3002
+ return projects;
2828
3003
  }
2829
3004
  async function listDatasets(params, projectId2) {
2830
3005
  const { BigQuery } = await import("@google-cloud/bigquery");
@@ -2862,23 +3037,25 @@ var PUBLIC_DATASETS = [
2862
3037
  labelEn: "bigquery-public-data.austin_311 (public dataset / customer service requests)"
2863
3038
  }
2864
3039
  ];
3040
+ var SAME_AS_BILLING = "__SAME_AS_BILLING__";
2865
3041
  function resolveDatasetRef(state) {
2866
3042
  const dataset = state.dataset ?? "";
2867
3043
  if (dataset.includes(".")) {
2868
3044
  const [datasetProject, ...rest] = dataset.split(".");
2869
3045
  return { datasetProject, datasetId: rest.join(".") };
2870
3046
  }
2871
- return { datasetProject: state.project ?? "", datasetId: dataset };
3047
+ return { datasetProject: state.databaseProject ?? "", datasetId: dataset };
2872
3048
  }
2873
3049
  var bigquerySetupFlow = {
2874
3050
  initialState: () => ({}),
2875
3051
  steps: [
2876
3052
  {
2877
- slug: "project",
3053
+ slug: "billingProject",
2878
3054
  type: "select",
3055
+ allowFreeText: false,
2879
3056
  question: {
2880
- 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",
2881
- en: "Select the GCP project to use for setup (also required as the billing project when querying public datasets)"
3057
+ 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",
3058
+ en: "Select the Google Cloud project to run BigQuery queries (this project will be billed for BigQuery usage)"
2882
3059
  },
2883
3060
  async fetchOptions(_state, rt) {
2884
3061
  const projects = await listProjects(rt.params);
@@ -2887,7 +3064,40 @@ var bigquerySetupFlow = {
2887
3064
  label: p.friendlyName && p.friendlyName !== p.projectId ? `${p.friendlyName} (${p.projectId})` : p.projectId
2888
3065
  }));
2889
3066
  },
2890
- applyAnswer: (state, answer) => ({ ...state, project: answer[0] })
3067
+ applyAnswer: (state, answer) => ({
3068
+ ...state,
3069
+ billingProject: answer[0]
3070
+ }),
3071
+ toParameterUpdates: (state) => state.billingProject ? [{ slug: "project-id", value: state.billingProject }] : []
3072
+ },
3073
+ {
3074
+ slug: "databaseProject",
3075
+ type: "select",
3076
+ question: {
3077
+ 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",
3078
+ en: "Select the GCP project where your data resides"
3079
+ },
3080
+ async fetchOptions(state, rt) {
3081
+ if (!state.billingProject) return [];
3082
+ const sameAsBillingOption = {
3083
+ value: SAME_AS_BILLING,
3084
+ label: rt.language === "ja" ? `\u8AB2\u91D1\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3068\u540C\u3058 (${state.billingProject})` : `Same as billing project (${state.billingProject})`
3085
+ };
3086
+ const projects = await listProjects(rt.params);
3087
+ const projectOptions = projects.filter((p) => p.projectId !== state.billingProject).map((p) => ({
3088
+ value: p.projectId,
3089
+ label: p.friendlyName && p.friendlyName !== p.projectId ? `${p.friendlyName} (${p.projectId})` : p.projectId
3090
+ }));
3091
+ const publicProjectOption = {
3092
+ value: "bigquery-public-data",
3093
+ 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)"
3094
+ };
3095
+ return [sameAsBillingOption, ...projectOptions, publicProjectOption];
3096
+ },
3097
+ applyAnswer: (state, answer) => ({
3098
+ ...state,
3099
+ databaseProject: answer[0] === SAME_AS_BILLING ? state.billingProject : answer[0]
3100
+ })
2891
3101
  },
2892
3102
  {
2893
3103
  slug: "dataset",
@@ -2897,8 +3107,8 @@ var bigquerySetupFlow = {
2897
3107
  en: "Select the dataset to use for setup"
2898
3108
  },
2899
3109
  async fetchOptions(state, rt) {
2900
- if (!state.project) return [];
2901
- const datasets = await listDatasets(rt.params, state.project);
3110
+ if (!state.databaseProject) return [];
3111
+ const datasets = await listDatasets(rt.params, state.databaseProject);
2902
3112
  const projectDatasetOptions = datasets.map((d) => ({
2903
3113
  value: d.datasetId,
2904
3114
  label: d.location ? `${d.datasetId} (${d.location})` : d.datasetId
@@ -2915,66 +3125,140 @@ var bigquerySetupFlow = {
2915
3125
  slug: "tables",
2916
3126
  type: "multiSelect",
2917
3127
  question: {
2918
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
2919
- en: "Select target tables (multi-select allowed)"
3128
+ 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",
3129
+ en: "Select target tables and views (multi-select allowed)"
2920
3130
  },
2921
3131
  async fetchOptions(state, rt) {
2922
- if (!state.project || !state.dataset) return [];
3132
+ if (!state.billingProject || !state.dataset) return [];
2923
3133
  const { datasetProject, datasetId } = resolveDatasetRef(state);
2924
3134
  const rows = await runQuery5(
2925
3135
  rt.params,
2926
3136
  `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
2927
3137
  );
2928
- const tableOptions = rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name).map((value) => ({ value }));
3138
+ const tableNames = rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3139
+ const { singles, candidates } = detectDateShardCandidates(tableNames);
3140
+ const confirmedGroups = [];
3141
+ const rejectedSingles = [];
3142
+ for (const candidate of candidates) {
3143
+ const [first, second] = candidate.members;
3144
+ const colRows = await runQuery5(
3145
+ rt.params,
3146
+ `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`
3147
+ );
3148
+ const colsA = colRows.filter(
3149
+ (r) => String(r["table_name"]) === first
3150
+ );
3151
+ const colsB = colRows.filter(
3152
+ (r) => String(r["table_name"]) === second
3153
+ );
3154
+ if (schemasMatch(colsA, colsB)) {
3155
+ confirmedGroups.push(candidate);
3156
+ } else {
3157
+ rejectedSingles.push(...candidate.members);
3158
+ }
3159
+ }
3160
+ const allSingles = [...singles, ...rejectedSingles];
2929
3161
  return [
2930
3162
  {
2931
3163
  value: ALL_TABLES4,
2932
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
3164
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
2933
3165
  },
2934
- ...tableOptions
3166
+ ...buildGroupedTableOptions(allSingles, confirmedGroups, rt.language)
2935
3167
  ];
2936
3168
  },
2937
3169
  applyAnswer: (state, answer) => ({ ...state, tables: answer })
2938
3170
  }
2939
3171
  ],
2940
3172
  async finalize(state, rt) {
2941
- if (!state.project || !state.dataset || !state.tables) {
3173
+ if (!state.billingProject || !state.databaseProject || !state.dataset || !state.tables) {
2942
3174
  throw new Error("BigQuery setup: incomplete state on finalize");
2943
3175
  }
2944
3176
  const { datasetProject, datasetId } = resolveDatasetRef(state);
2945
- const targetTables = await resolveSetupSelection({
2946
- selected: state.tables,
2947
- allSentinel: ALL_TABLES4,
2948
- fetchAll: async () => {
2949
- const rows = await runQuery5(
2950
- rt.params,
2951
- `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
2952
- );
2953
- return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
2954
- },
2955
- limit: BIGQUERY_SETUP_MAX_TABLES
2956
- });
3177
+ const fetchAllTableNames = async () => {
3178
+ const rows = await runQuery5(
3179
+ rt.params,
3180
+ `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
3181
+ );
3182
+ return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3183
+ };
3184
+ let entries;
3185
+ if (state.tables.includes(ALL_TABLES4)) {
3186
+ const allNames = await fetchAllTableNames();
3187
+ const { singles, candidates } = detectDateShardCandidates(allNames);
3188
+ entries = [
3189
+ ...candidates.map(
3190
+ (g) => ({
3191
+ type: "shardGroup",
3192
+ prefix: g.prefix,
3193
+ count: g.count,
3194
+ representative: g.members[0]
3195
+ })
3196
+ ),
3197
+ ...singles.map((name) => ({ type: "table", name }))
3198
+ ];
3199
+ } else {
3200
+ const shardSelections = state.tables.filter(isShardGroupValue);
3201
+ const tableSelections = state.tables.filter(
3202
+ (v) => !isShardGroupValue(v)
3203
+ );
3204
+ entries = tableSelections.map(
3205
+ (name) => ({ type: "table", name })
3206
+ );
3207
+ if (shardSelections.length > 0) {
3208
+ const allNames = await fetchAllTableNames();
3209
+ for (const value of shardSelections) {
3210
+ const prefix = getShardGroupPrefix(value);
3211
+ const members = allNames.filter((n) => {
3212
+ const parsed = extractDateShardSuffix(n);
3213
+ return parsed != null && parsed.prefix === prefix;
3214
+ });
3215
+ if (members.length > 0) {
3216
+ entries.push({
3217
+ type: "shardGroup",
3218
+ prefix,
3219
+ count: members.length,
3220
+ representative: members[0]
3221
+ });
3222
+ }
3223
+ }
3224
+ }
3225
+ }
3226
+ entries = entries.slice(0, BIGQUERY_SETUP_MAX_TABLES);
3227
+ const queryColumns = async (tableName) => runQuery5(
3228
+ rt.params,
3229
+ `SELECT column_name, data_type, is_nullable FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${tableName.replaceAll("'", "''")}' ORDER BY ordinal_position`
3230
+ );
2957
3231
  const sections = [
2958
3232
  "## BigQuery",
2959
3233
  "",
2960
- `### Billing project: ${state.project}`,
3234
+ `### Billing project: ${state.billingProject}`,
3235
+ "",
3236
+ `### Database project: ${state.databaseProject}`,
2961
3237
  "",
2962
3238
  `#### Dataset: ${datasetProject}.${datasetId}`,
2963
3239
  ""
2964
3240
  ];
2965
- for (const table of targetTables) {
2966
- const cols = await runQuery5(
2967
- rt.params,
2968
- `SELECT column_name, data_type, is_nullable FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${table.replaceAll("'", "''")}' ORDER BY ordinal_position`
2969
- );
2970
- sections.push(`##### Table: ${table}`, "");
3241
+ for (const entry of entries) {
3242
+ const tableName = entry.type === "shardGroup" ? entry.representative : entry.name;
3243
+ const cols = await queryColumns(tableName);
3244
+ if (entry.type === "shardGroup") {
3245
+ sections.push(
3246
+ `##### Table: ${entry.prefix}_* (date-sharded, ${entry.count} tables)`,
3247
+ ""
3248
+ );
3249
+ sections.push(
3250
+ `_Query all shards: \`SELECT * FROM \`${datasetProject}.${datasetId}.${entry.prefix}_*\` WHERE _TABLE_SUFFIX BETWEEN 'YYYYMMDD' AND 'YYYYMMDD'\`_`,
3251
+ ""
3252
+ );
3253
+ } else {
3254
+ sections.push(`##### Table: ${entry.name}`, "");
3255
+ }
2971
3256
  sections.push("| Column | Type | Nullable |");
2972
3257
  sections.push("|--------|------|----------|");
2973
3258
  for (const c of cols) {
2974
- const name = String(c["column_name"] ?? "");
2975
- const type = String(c["data_type"] ?? "");
2976
- const nullable = String(c["is_nullable"] ?? "");
2977
- sections.push(`| ${name} | ${type} | ${nullable} |`);
3259
+ sections.push(
3260
+ `| ${String(c["column_name"] ?? "")} | ${String(c["data_type"] ?? "")} | ${String(c["is_nullable"] ?? "")} |`
3261
+ );
2978
3262
  }
2979
3263
  sections.push("");
2980
3264
  }
@@ -3472,21 +3756,30 @@ async function runQuery6(proxyFetch, projectId2, sql) {
3472
3756
  return parseQueryResponse(data);
3473
3757
  }
3474
3758
  async function listProjects2(proxyFetch) {
3475
- const res = await proxyFetch(
3476
- "https://bigquery.googleapis.com/bigquery/v2/projects",
3477
- { method: "GET" }
3478
- );
3479
- if (!res.ok) {
3480
- const errorText = await res.text().catch(() => res.statusText);
3481
- throw new Error(
3482
- `BigQuery listProjects failed: HTTP ${res.status} ${errorText}`
3483
- );
3484
- }
3485
- const data = await res.json();
3486
- return (data.projects ?? []).map((p) => ({
3487
- projectId: p.projectReference?.projectId ?? "",
3488
- friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
3489
- })).filter((p) => p.projectId !== "");
3759
+ const projects = [];
3760
+ let pageToken;
3761
+ do {
3762
+ const url = pageToken ? `https://bigquery.googleapis.com/bigquery/v2/projects?pageToken=${encodeURIComponent(pageToken)}` : "https://bigquery.googleapis.com/bigquery/v2/projects";
3763
+ const res = await proxyFetch(url, { method: "GET" });
3764
+ if (!res.ok) {
3765
+ const errorText = await res.text().catch(() => res.statusText);
3766
+ throw new Error(
3767
+ `BigQuery listProjects failed: HTTP ${res.status} ${errorText}`
3768
+ );
3769
+ }
3770
+ const data = await res.json();
3771
+ for (const p of data.projects ?? []) {
3772
+ const id = p.projectReference?.projectId ?? "";
3773
+ if (id) {
3774
+ projects.push({
3775
+ projectId: id,
3776
+ friendlyName: p.friendlyName ?? id
3777
+ });
3778
+ }
3779
+ }
3780
+ pageToken = data.nextPageToken;
3781
+ } while (pageToken);
3782
+ return projects;
3490
3783
  }
3491
3784
  async function listDatasets2(proxyFetch, projectId2) {
3492
3785
  const res = await proxyFetch(
@@ -3526,23 +3819,25 @@ var PUBLIC_DATASETS2 = [
3526
3819
  labelEn: "bigquery-public-data.austin_311 (public dataset / customer service requests)"
3527
3820
  }
3528
3821
  ];
3822
+ var SAME_AS_BILLING2 = "__SAME_AS_BILLING__";
3529
3823
  function resolveDatasetRef2(state) {
3530
3824
  const dataset = state.dataset ?? "";
3531
3825
  if (dataset.includes(".")) {
3532
3826
  const [datasetProject, ...rest] = dataset.split(".");
3533
3827
  return { datasetProject, datasetId: rest.join(".") };
3534
3828
  }
3535
- return { datasetProject: state.project ?? "", datasetId: dataset };
3829
+ return { datasetProject: state.databaseProject ?? "", datasetId: dataset };
3536
3830
  }
3537
3831
  var bigqueryOauthSetupFlow = {
3538
3832
  initialState: () => ({}),
3539
3833
  steps: [
3540
3834
  {
3541
- slug: "project",
3835
+ slug: "billingProject",
3542
3836
  type: "select",
3837
+ allowFreeText: false,
3543
3838
  question: {
3544
- 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",
3545
- en: "Select the GCP project to use for setup (also required as the billing project when querying public datasets)"
3839
+ 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",
3840
+ en: "Select the Google Cloud project to run BigQuery queries (this project will be billed for BigQuery usage)"
3546
3841
  },
3547
3842
  async fetchOptions(_state, rt) {
3548
3843
  const projects = await listProjects2(rt.config.proxyFetch);
@@ -3551,7 +3846,40 @@ var bigqueryOauthSetupFlow = {
3551
3846
  label: p.friendlyName && p.friendlyName !== p.projectId ? `${p.friendlyName} (${p.projectId})` : p.projectId
3552
3847
  }));
3553
3848
  },
3554
- applyAnswer: (state, answer) => ({ ...state, project: answer[0] })
3849
+ applyAnswer: (state, answer) => ({
3850
+ ...state,
3851
+ billingProject: answer[0]
3852
+ }),
3853
+ toParameterUpdates: (state) => state.billingProject ? [{ slug: "project-id", value: state.billingProject }] : []
3854
+ },
3855
+ {
3856
+ slug: "databaseProject",
3857
+ type: "select",
3858
+ question: {
3859
+ 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",
3860
+ en: "Select the GCP project where your data resides"
3861
+ },
3862
+ async fetchOptions(state, rt) {
3863
+ if (!state.billingProject) return [];
3864
+ const sameAsBillingOption = {
3865
+ value: SAME_AS_BILLING2,
3866
+ label: rt.language === "ja" ? `\u8AB2\u91D1\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3068\u540C\u3058 (${state.billingProject})` : `Same as billing project (${state.billingProject})`
3867
+ };
3868
+ const projects = await listProjects2(rt.config.proxyFetch);
3869
+ const projectOptions = projects.filter((p) => p.projectId !== state.billingProject).map((p) => ({
3870
+ value: p.projectId,
3871
+ label: p.friendlyName && p.friendlyName !== p.projectId ? `${p.friendlyName} (${p.projectId})` : p.projectId
3872
+ }));
3873
+ const publicProjectOption = {
3874
+ value: "bigquery-public-data",
3875
+ 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)"
3876
+ };
3877
+ return [sameAsBillingOption, ...projectOptions, publicProjectOption];
3878
+ },
3879
+ applyAnswer: (state, answer) => ({
3880
+ ...state,
3881
+ databaseProject: answer[0] === SAME_AS_BILLING2 ? state.billingProject : answer[0]
3882
+ })
3555
3883
  },
3556
3884
  {
3557
3885
  slug: "dataset",
@@ -3561,8 +3889,11 @@ var bigqueryOauthSetupFlow = {
3561
3889
  en: "Select the dataset to use for setup"
3562
3890
  },
3563
3891
  async fetchOptions(state, rt) {
3564
- if (!state.project) return [];
3565
- const datasets = await listDatasets2(rt.config.proxyFetch, state.project);
3892
+ if (!state.databaseProject) return [];
3893
+ const datasets = await listDatasets2(
3894
+ rt.config.proxyFetch,
3895
+ state.databaseProject
3896
+ );
3566
3897
  const projectDatasetOptions = datasets.map((d) => ({
3567
3898
  value: d.datasetId,
3568
3899
  label: d.location ? `${d.datasetId} (${d.location})` : d.datasetId
@@ -3579,70 +3910,145 @@ var bigqueryOauthSetupFlow = {
3579
3910
  slug: "tables",
3580
3911
  type: "multiSelect",
3581
3912
  question: {
3582
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
3583
- en: "Select target tables (multi-select allowed)"
3913
+ 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",
3914
+ en: "Select target tables and views (multi-select allowed)"
3584
3915
  },
3585
3916
  async fetchOptions(state, rt) {
3586
- if (!state.project || !state.dataset) return [];
3917
+ if (!state.billingProject || !state.dataset) return [];
3587
3918
  const { datasetProject, datasetId } = resolveDatasetRef2(state);
3588
3919
  const rows = await runQuery6(
3589
3920
  rt.config.proxyFetch,
3590
- state.project,
3921
+ state.billingProject,
3591
3922
  `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
3592
3923
  );
3593
- const tableOptions = rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name).map((value) => ({ value }));
3924
+ const tableNames = rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3925
+ const { singles, candidates } = detectDateShardCandidates(tableNames);
3926
+ const confirmedGroups = [];
3927
+ const rejectedSingles = [];
3928
+ for (const candidate of candidates) {
3929
+ const [first, second] = candidate.members;
3930
+ const colRows = await runQuery6(
3931
+ rt.config.proxyFetch,
3932
+ state.billingProject,
3933
+ `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`
3934
+ );
3935
+ const colsA = colRows.filter(
3936
+ (r) => String(r["table_name"]) === first
3937
+ );
3938
+ const colsB = colRows.filter(
3939
+ (r) => String(r["table_name"]) === second
3940
+ );
3941
+ if (schemasMatch(colsA, colsB)) {
3942
+ confirmedGroups.push(candidate);
3943
+ } else {
3944
+ rejectedSingles.push(...candidate.members);
3945
+ }
3946
+ }
3947
+ const allSingles = [...singles, ...rejectedSingles];
3594
3948
  return [
3595
3949
  {
3596
3950
  value: ALL_TABLES5,
3597
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
3951
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
3598
3952
  },
3599
- ...tableOptions
3953
+ ...buildGroupedTableOptions(allSingles, confirmedGroups, rt.language)
3600
3954
  ];
3601
3955
  },
3602
3956
  applyAnswer: (state, answer) => ({ ...state, tables: answer })
3603
3957
  }
3604
3958
  ],
3605
3959
  async finalize(state, rt) {
3606
- if (!state.project || !state.dataset || !state.tables) {
3960
+ if (!state.billingProject || !state.databaseProject || !state.dataset || !state.tables) {
3607
3961
  throw new Error("BigQuery OAuth setup: incomplete state on finalize");
3608
3962
  }
3609
- const billingProject = state.project;
3963
+ const billingProject = state.billingProject;
3610
3964
  const { datasetProject, datasetId } = resolveDatasetRef2(state);
3611
- const targetTables = await resolveSetupSelection({
3612
- selected: state.tables,
3613
- allSentinel: ALL_TABLES5,
3614
- fetchAll: async () => {
3615
- const rows = await runQuery6(
3616
- rt.config.proxyFetch,
3617
- billingProject,
3618
- `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
3619
- );
3620
- return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3621
- },
3622
- limit: BIGQUERY_OAUTH_SETUP_MAX_TABLES
3623
- });
3965
+ const fetchAllTableNames = async () => {
3966
+ const rows = await runQuery6(
3967
+ rt.config.proxyFetch,
3968
+ billingProject,
3969
+ `SELECT table_name FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.TABLES`
3970
+ );
3971
+ return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
3972
+ };
3973
+ let entries;
3974
+ if (state.tables.includes(ALL_TABLES5)) {
3975
+ const allNames = await fetchAllTableNames();
3976
+ const { singles, candidates } = detectDateShardCandidates(allNames);
3977
+ entries = [
3978
+ ...candidates.map(
3979
+ (g) => ({
3980
+ type: "shardGroup",
3981
+ prefix: g.prefix,
3982
+ count: g.count,
3983
+ representative: g.members[0]
3984
+ })
3985
+ ),
3986
+ ...singles.map((name) => ({ type: "table", name }))
3987
+ ];
3988
+ } else {
3989
+ const shardSelections = state.tables.filter(isShardGroupValue);
3990
+ const tableSelections = state.tables.filter(
3991
+ (v) => !isShardGroupValue(v)
3992
+ );
3993
+ entries = tableSelections.map(
3994
+ (name) => ({ type: "table", name })
3995
+ );
3996
+ if (shardSelections.length > 0) {
3997
+ const allNames = await fetchAllTableNames();
3998
+ for (const value of shardSelections) {
3999
+ const prefix = getShardGroupPrefix(value);
4000
+ const members = allNames.filter((n) => {
4001
+ const parsed = extractDateShardSuffix(n);
4002
+ return parsed != null && parsed.prefix === prefix;
4003
+ });
4004
+ if (members.length > 0) {
4005
+ entries.push({
4006
+ type: "shardGroup",
4007
+ prefix,
4008
+ count: members.length,
4009
+ representative: members[0]
4010
+ });
4011
+ }
4012
+ }
4013
+ }
4014
+ }
4015
+ entries = entries.slice(0, BIGQUERY_OAUTH_SETUP_MAX_TABLES);
4016
+ const queryColumns = async (tableName) => runQuery6(
4017
+ rt.config.proxyFetch,
4018
+ billingProject,
4019
+ `SELECT column_name, data_type, is_nullable FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${tableName.replaceAll("'", "''")}' ORDER BY ordinal_position`
4020
+ );
3624
4021
  const sections = [
3625
4022
  "## BigQuery (OAuth)",
3626
4023
  "",
3627
4024
  `### Billing project: ${billingProject}`,
3628
4025
  "",
4026
+ `### Database project: ${state.databaseProject}`,
4027
+ "",
3629
4028
  `#### Dataset: ${datasetProject}.${datasetId}`,
3630
4029
  ""
3631
4030
  ];
3632
- for (const table of targetTables) {
3633
- const cols = await runQuery6(
3634
- rt.config.proxyFetch,
3635
- billingProject,
3636
- `SELECT column_name, data_type, is_nullable FROM \`${datasetProject}.${datasetId}\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${table.replaceAll("'", "''")}' ORDER BY ordinal_position`
3637
- );
3638
- sections.push(`##### Table: ${table}`, "");
4031
+ for (const entry of entries) {
4032
+ const tableName = entry.type === "shardGroup" ? entry.representative : entry.name;
4033
+ const cols = await queryColumns(tableName);
4034
+ if (entry.type === "shardGroup") {
4035
+ sections.push(
4036
+ `##### Table: ${entry.prefix}_* (date-sharded, ${entry.count} tables)`,
4037
+ ""
4038
+ );
4039
+ sections.push(
4040
+ `_Query all shards: \`SELECT * FROM \`${datasetProject}.${datasetId}.${entry.prefix}_*\` WHERE _TABLE_SUFFIX BETWEEN 'YYYYMMDD' AND 'YYYYMMDD'\`_`,
4041
+ ""
4042
+ );
4043
+ } else {
4044
+ sections.push(`##### Table: ${entry.name}`, "");
4045
+ }
3639
4046
  sections.push("| Column | Type | Nullable |");
3640
4047
  sections.push("|--------|------|----------|");
3641
4048
  for (const c of cols) {
3642
- const name = String(c["column_name"] ?? "");
3643
- const type = String(c["data_type"] ?? "");
3644
- const nullable = String(c["is_nullable"] ?? "");
3645
- sections.push(`| ${name} | ${type} | ${nullable} |`);
4049
+ sections.push(
4050
+ `| ${String(c["column_name"] ?? "")} | ${String(c["data_type"] ?? "")} | ${String(c["is_nullable"] ?? "")} |`
4051
+ );
3646
4052
  }
3647
4053
  sections.push("");
3648
4054
  }
@@ -5351,14 +5757,15 @@ function isInternalSchema2(name) {
5351
5757
  function quoteLiteral(value) {
5352
5758
  return "'" + value.replace(/'/g, "''") + "'";
5353
5759
  }
5354
- async function fetchTableNames3(params, schema) {
5760
+ async function fetchTableAndViewNames(params, schema) {
5355
5761
  const rows = await runQuery7(
5356
5762
  params,
5357
- `SELECT tablename FROM pg_tables
5358
- WHERE schemaname = ${quoteLiteral(schema)}
5359
- ORDER BY tablename`
5763
+ `SELECT table_name FROM information_schema.tables
5764
+ WHERE table_schema = ${quoteLiteral(schema)}
5765
+ AND table_type IN ('BASE TABLE', 'VIEW')
5766
+ ORDER BY table_name`
5360
5767
  );
5361
- return rows.map((r) => String(r["tablename"] ?? "")).filter((name) => name);
5768
+ return rows.map((r) => String(r["table_name"] ?? "")).filter((name) => name);
5362
5769
  }
5363
5770
  var redshiftSetupFlow = {
5364
5771
  initialState: () => ({}),
@@ -5373,9 +5780,11 @@ var redshiftSetupFlow = {
5373
5780
  async fetchOptions(_state, rt) {
5374
5781
  const rows = await runQuery7(
5375
5782
  rt.params,
5376
- `SELECT DISTINCT schemaname FROM pg_tables ORDER BY schemaname`
5783
+ `SELECT DISTINCT table_schema FROM information_schema.tables
5784
+ WHERE table_type IN ('BASE TABLE', 'VIEW')
5785
+ ORDER BY table_schema`
5377
5786
  );
5378
- return rows.map((r) => String(r["schemaname"] ?? "")).filter((name) => name && !isInternalSchema2(name)).map((value) => ({ value }));
5787
+ return rows.map((r) => String(r["table_schema"] ?? "")).filter((name) => name && !isInternalSchema2(name)).map((value) => ({ value }));
5379
5788
  },
5380
5789
  applyAnswer: (state, answer) => ({ ...state, schema: answer[0] })
5381
5790
  },
@@ -5383,17 +5792,17 @@ var redshiftSetupFlow = {
5383
5792
  slug: "tables",
5384
5793
  type: "multiSelect",
5385
5794
  question: {
5386
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
5387
- en: "Select target tables (multi-select allowed)"
5795
+ 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",
5796
+ en: "Select target tables and views (multi-select allowed)"
5388
5797
  },
5389
5798
  async fetchOptions(state, rt) {
5390
5799
  if (!state.schema) return [];
5391
- const names = await fetchTableNames3(rt.params, state.schema);
5800
+ const names = await fetchTableAndViewNames(rt.params, state.schema);
5392
5801
  const tableOptions = names.map((value) => ({ value }));
5393
5802
  return [
5394
5803
  {
5395
5804
  value: ALL_TABLES7,
5396
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
5805
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
5397
5806
  },
5398
5807
  ...tableOptions
5399
5808
  ];
@@ -5409,9 +5818,21 @@ var redshiftSetupFlow = {
5409
5818
  const targetTables = await resolveSetupSelection({
5410
5819
  selected: state.tables,
5411
5820
  allSentinel: ALL_TABLES7,
5412
- fetchAll: () => fetchTableNames3(rt.params, schema),
5821
+ fetchAll: () => fetchTableAndViewNames(rt.params, schema),
5413
5822
  limit: REDSHIFT_SETUP_MAX_TABLES
5414
5823
  });
5824
+ const typeRows = targetTables.length > 0 ? await runQuery7(
5825
+ rt.params,
5826
+ `SELECT table_name, table_type FROM information_schema.tables
5827
+ WHERE table_schema = ${quoteLiteral(schema)}
5828
+ AND table_name IN (${targetTables.map(quoteLiteral).join(", ")})`
5829
+ ) : [];
5830
+ const typeMap = new Map(
5831
+ typeRows.map((r) => [
5832
+ String(r["table_name"] ?? ""),
5833
+ String(r["table_type"] ?? "BASE TABLE")
5834
+ ])
5835
+ );
5415
5836
  const sections = [
5416
5837
  "## Redshift",
5417
5838
  "",
@@ -5419,6 +5840,7 @@ var redshiftSetupFlow = {
5419
5840
  ""
5420
5841
  ];
5421
5842
  for (const table of targetTables) {
5843
+ const heading = typeMap.get(table) === "VIEW" ? "View" : "Table";
5422
5844
  const cols = await runQuery7(
5423
5845
  rt.params,
5424
5846
  `SELECT column_name, data_type, is_nullable, column_default
@@ -5427,7 +5849,7 @@ var redshiftSetupFlow = {
5427
5849
  AND table_name = ${quoteLiteral(table)}
5428
5850
  ORDER BY ordinal_position`
5429
5851
  );
5430
- sections.push(`#### Table: ${table}`, "");
5852
+ sections.push(`#### ${heading}: ${table}`, "");
5431
5853
  sections.push("| Column | Type | Nullable | Default |");
5432
5854
  sections.push("|--------|------|----------|---------|");
5433
5855
  for (const c of cols) {
@@ -5855,8 +6277,8 @@ var databricksSetupFlow = {
5855
6277
  slug: "tables",
5856
6278
  type: "multiSelect",
5857
6279
  question: {
5858
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
5859
- en: "Select target tables (multi-select allowed)"
6280
+ 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",
6281
+ en: "Select target tables and views (multi-select allowed)"
5860
6282
  },
5861
6283
  async fetchOptions(state, rt) {
5862
6284
  if (!state.catalog || !state.schema) return [];
@@ -5868,7 +6290,7 @@ var databricksSetupFlow = {
5868
6290
  return [
5869
6291
  {
5870
6292
  value: ALL_TABLES8,
5871
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
6293
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
5872
6294
  },
5873
6295
  ...tableOptions
5874
6296
  ];
@@ -7248,11 +7670,11 @@ var parameters13 = {
7248
7670
  propertyId: new ParameterDefinition({
7249
7671
  slug: "property-id",
7250
7672
  name: "Google Analytics Property ID",
7251
- description: "The Google Analytics 4 property ID (e.g., 123456789).",
7673
+ description: "The Google Analytics 4 property ID (e.g., 123456789). Automatically set during the setup flow.",
7252
7674
  envVarBaseKey: "GA_PROPERTY_ID",
7253
7675
  type: "text",
7254
7676
  secret: false,
7255
- required: true
7677
+ required: false
7256
7678
  })
7257
7679
  };
7258
7680
 
@@ -7260,6 +7682,7 @@ var parameters13 = {
7260
7682
  import * as crypto2 from "crypto";
7261
7683
  var TOKEN_URL = "https://oauth2.googleapis.com/token";
7262
7684
  var ADMIN_BASE_URL = "https://analyticsadmin.googleapis.com/v1beta";
7685
+ var DATA_BASE_URL = "https://analyticsdata.googleapis.com/v1beta";
7263
7686
  var SCOPE = "https://www.googleapis.com/auth/analytics.readonly";
7264
7687
  function base64url(input) {
7265
7688
  const buf = typeof input === "string" ? Buffer.from(input) : input;
@@ -7341,10 +7764,25 @@ async function adminFetch(params, path5, init) {
7341
7764
  headers.set("Authorization", `Bearer ${accessToken}`);
7342
7765
  return fetch(url, { ...init, headers });
7343
7766
  }
7767
+ async function dataFetch(params, path5, init) {
7768
+ const keyJsonBase64 = params[parameters13.serviceAccountKeyJsonBase64.slug];
7769
+ if (!keyJsonBase64) {
7770
+ throw new Error(
7771
+ "google-analytics: missing required parameter: service-account-key-json-base64"
7772
+ );
7773
+ }
7774
+ const serviceAccountKey = decodeServiceAccount(keyJsonBase64);
7775
+ const accessToken = await getAccessToken(serviceAccountKey);
7776
+ const url = `${DATA_BASE_URL}${path5.startsWith("/") ? "" : "/"}${path5}`;
7777
+ const headers = new Headers(init?.headers);
7778
+ headers.set("Authorization", `Bearer ${accessToken}`);
7779
+ return fetch(url, { ...init, headers });
7780
+ }
7344
7781
 
7345
7782
  // ../connectors/src/connectors/google-analytics/setup-flow.ts
7346
7783
  var ALL_PROPERTIES = "__ALL_PROPERTIES__";
7347
7784
  var GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES = 20;
7785
+ var METADATA_DISPLAY_LIMIT = 30;
7348
7786
  async function listAccountSummaries(params) {
7349
7787
  const summaries = [];
7350
7788
  let pageToken;
@@ -7371,6 +7809,48 @@ async function getProperty(params, propertyId) {
7371
7809
  if (!res.ok) return null;
7372
7810
  return await res.json();
7373
7811
  }
7812
+ async function getMetadata(params, propertyId) {
7813
+ try {
7814
+ const res = await dataFetch(
7815
+ params,
7816
+ `/properties/${propertyId}/metadata`
7817
+ );
7818
+ if (!res.ok) return { dimensions: [], metrics: [] };
7819
+ const data = await res.json();
7820
+ return {
7821
+ dimensions: data.dimensions ?? [],
7822
+ metrics: data.metrics ?? []
7823
+ };
7824
+ } catch {
7825
+ return { dimensions: [], metrics: [] };
7826
+ }
7827
+ }
7828
+ function appendMetadataSection(sections, dimensions, metrics) {
7829
+ if (dimensions.length > 0) {
7830
+ sections.push(`#### Dimensions (${dimensions.length})`, "");
7831
+ for (const d of dimensions.slice(0, METADATA_DISPLAY_LIMIT)) {
7832
+ sections.push(`- ${d.apiName ?? d.uiName ?? "(unknown)"}`);
7833
+ }
7834
+ if (dimensions.length > METADATA_DISPLAY_LIMIT) {
7835
+ sections.push(
7836
+ `- \u2026and ${dimensions.length - METADATA_DISPLAY_LIMIT} more`
7837
+ );
7838
+ }
7839
+ sections.push("");
7840
+ }
7841
+ if (metrics.length > 0) {
7842
+ sections.push(`#### Metrics (${metrics.length})`, "");
7843
+ for (const m of metrics.slice(0, METADATA_DISPLAY_LIMIT)) {
7844
+ sections.push(`- ${m.apiName ?? m.uiName ?? "(unknown)"}`);
7845
+ }
7846
+ if (metrics.length > METADATA_DISPLAY_LIMIT) {
7847
+ sections.push(
7848
+ `- \u2026and ${metrics.length - METADATA_DISPLAY_LIMIT} more`
7849
+ );
7850
+ }
7851
+ sections.push("");
7852
+ }
7853
+ }
7374
7854
  var googleAnalyticsSetupFlow = {
7375
7855
  initialState: () => ({}),
7376
7856
  steps: [
@@ -7382,15 +7862,19 @@ var googleAnalyticsSetupFlow = {
7382
7862
  en: "Select a Google Analytics account"
7383
7863
  },
7384
7864
  async fetchOptions(_state, rt) {
7385
- const summaries = await listAccountSummaries(rt.params);
7386
- return summaries.map((s) => {
7387
- const accountResource = s.account ?? s.name ?? "";
7388
- if (!accountResource) return null;
7389
- return {
7390
- value: accountResource,
7391
- label: s.displayName ?? accountResource
7392
- };
7393
- }).filter((v) => v != null);
7865
+ try {
7866
+ const summaries = await listAccountSummaries(rt.params);
7867
+ return summaries.map((s) => {
7868
+ const accountResource = s.account ?? s.name ?? "";
7869
+ if (!accountResource) return null;
7870
+ return {
7871
+ value: accountResource,
7872
+ label: s.displayName ?? accountResource
7873
+ };
7874
+ }).filter((v) => v != null);
7875
+ } catch {
7876
+ return [];
7877
+ }
7394
7878
  },
7395
7879
  applyAnswer: (state, answer) => ({ ...state, account: answer[0] })
7396
7880
  },
@@ -7403,68 +7887,134 @@ var googleAnalyticsSetupFlow = {
7403
7887
  },
7404
7888
  async fetchOptions(state, rt) {
7405
7889
  if (!state.account) return [];
7406
- const summaries = await listAccountSummaries(rt.params);
7407
- const account = summaries.find(
7408
- (s) => (s.account ?? s.name) === state.account
7409
- );
7410
- const props = (account?.propertySummaries ?? []).map((p) => {
7411
- const id = propertyIdFromResource(p.property);
7412
- if (!id) return null;
7413
- return {
7414
- value: id,
7415
- label: p.displayName ? `${p.displayName} (${id})` : id
7416
- };
7417
- }).filter((v) => v != null);
7418
- if (props.length === 0) return [];
7419
- return [
7890
+ try {
7891
+ const summaries = await listAccountSummaries(rt.params);
7892
+ const account = summaries.find(
7893
+ (s) => (s.account ?? s.name) === state.account
7894
+ );
7895
+ const props = (account?.propertySummaries ?? []).map((p) => {
7896
+ const id = propertyIdFromResource(p.property);
7897
+ if (!id) return null;
7898
+ return {
7899
+ value: id,
7900
+ label: p.displayName ? `${p.displayName} (${id})` : id
7901
+ };
7902
+ }).filter((v) => v != null);
7903
+ if (props.length === 0) return [];
7904
+ return [
7905
+ {
7906
+ value: ALL_PROPERTIES,
7907
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30D1\u30C6\u30A3" : "All properties"
7908
+ },
7909
+ ...props
7910
+ ];
7911
+ } catch {
7912
+ return [];
7913
+ }
7914
+ },
7915
+ applyAnswer: (state, answer) => ({ ...state, properties: answer }),
7916
+ toParameterUpdates: (state) => {
7917
+ const first = state.properties?.find((v) => v !== ALL_PROPERTIES);
7918
+ return first ? [{ slug: parameters13.propertyId.slug, value: first }] : [];
7919
+ }
7920
+ },
7921
+ {
7922
+ slug: "manualPropertyId",
7923
+ type: "select",
7924
+ allowFreeText: true,
7925
+ question: {
7926
+ 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",
7927
+ en: "Enter your GA4 Property ID (e.g., 123456789). Found in GA4 Admin > Property Settings."
7928
+ },
7929
+ async fetchOptions(state, rt) {
7930
+ if (state.properties?.length) return [];
7931
+ const existing = rt.params[parameters13.propertyId.slug];
7932
+ return existing ? [{ value: existing, label: existing }] : [
7420
7933
  {
7421
- value: ALL_PROPERTIES,
7422
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30D1\u30C6\u30A3" : "All properties"
7423
- },
7424
- ...props
7934
+ value: "example",
7935
+ label: rt.language === "ja" ? "\u4F8B: 123456789" : "Example: 123456789"
7936
+ }
7425
7937
  ];
7426
7938
  },
7427
- applyAnswer: (state, answer) => ({ ...state, properties: answer })
7939
+ applyAnswer: (state, answer) => ({
7940
+ ...state,
7941
+ manualPropertyId: answer[0]
7942
+ }),
7943
+ toParameterUpdates: (state) => state.manualPropertyId ? [
7944
+ {
7945
+ slug: parameters13.propertyId.slug,
7946
+ value: state.manualPropertyId
7947
+ }
7948
+ ] : []
7428
7949
  }
7429
7950
  ],
7430
7951
  async finalize(state, rt) {
7431
- if (!state.account || !state.properties) {
7432
- throw new Error("Google Analytics setup: incomplete state on finalize");
7433
- }
7434
- const summaries = await listAccountSummaries(rt.params);
7435
- const account = summaries.find(
7436
- (s) => (s.account ?? s.name) === state.account
7437
- );
7438
- const accountLabel2 = account?.displayName ?? state.account.replace(/^accounts\//, "");
7439
- const targetPropertyIds = await resolveSetupSelection({
7440
- selected: state.properties,
7441
- allSentinel: ALL_PROPERTIES,
7442
- fetchAll: async () => (account?.propertySummaries ?? []).map((p) => propertyIdFromResource(p.property)).filter((v) => v),
7443
- limit: GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES
7444
- });
7445
- const sections = [
7446
- "## Google Analytics",
7447
- "",
7448
- `### Account: ${accountLabel2}`,
7449
- ""
7450
- ];
7451
- if (targetPropertyIds.length === 0) {
7452
- sections.push("_No properties selected._", "");
7952
+ const sections = ["## Google Analytics", ""];
7953
+ if (state.account && state.properties) {
7954
+ let summaries = [];
7955
+ try {
7956
+ summaries = await listAccountSummaries(rt.params);
7957
+ } catch {
7958
+ }
7959
+ const account = summaries.find(
7960
+ (s) => (s.account ?? s.name) === state.account
7961
+ );
7962
+ const accountLabel2 = account?.displayName ?? state.account.replace(/^accounts\//, "");
7963
+ const targetPropertyIds = await resolveSetupSelection({
7964
+ selected: state.properties,
7965
+ allSentinel: ALL_PROPERTIES,
7966
+ fetchAll: async () => (account?.propertySummaries ?? []).map((p) => propertyIdFromResource(p.property)).filter((v) => v),
7967
+ limit: GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES
7968
+ });
7969
+ sections.push(`### Account: ${accountLabel2}`, "");
7970
+ if (targetPropertyIds.length === 0) {
7971
+ sections.push("_No properties selected._", "");
7972
+ return sections.join("\n");
7973
+ }
7974
+ sections.push(
7975
+ "| Property ID | Display Name | Time Zone | Currency | Industry |"
7976
+ );
7977
+ sections.push(
7978
+ "|-------------|--------------|-----------|----------|----------|"
7979
+ );
7980
+ for (const pid of targetPropertyIds) {
7981
+ const prop = await getProperty(rt.params, pid).catch(() => null);
7982
+ const displayName3 = (prop?.displayName ?? "-").replace(/\|/g, "\\|");
7983
+ const timeZone = prop?.timeZone ?? "-";
7984
+ const currency = prop?.currencyCode ?? "-";
7985
+ const industry = (prop?.industryCategory ?? "-").replace(
7986
+ /\|/g,
7987
+ "\\|"
7988
+ );
7989
+ sections.push(
7990
+ `| ${pid} | ${displayName3} | ${timeZone} | ${currency} | ${industry} |`
7991
+ );
7992
+ }
7993
+ sections.push("");
7994
+ const firstPropertyId = targetPropertyIds[0];
7995
+ if (firstPropertyId) {
7996
+ const { dimensions, metrics } = await getMetadata(
7997
+ rt.params,
7998
+ firstPropertyId
7999
+ );
8000
+ appendMetadataSection(sections, dimensions, metrics);
8001
+ }
7453
8002
  return sections.join("\n");
7454
8003
  }
7455
- sections.push("| Property ID | Display Name | Time Zone | Currency | Industry |");
7456
- sections.push("|-------------|--------------|-----------|----------|----------|");
7457
- for (const propertyId of targetPropertyIds) {
7458
- const prop = await getProperty(rt.params, propertyId);
7459
- const displayName3 = (prop?.displayName ?? "-").replace(/\|/g, "\\|");
7460
- const timeZone = prop?.timeZone ?? "-";
7461
- const currency = prop?.currencyCode ?? "-";
7462
- const industry = (prop?.industryCategory ?? "-").replace(/\|/g, "\\|");
7463
- sections.push(
7464
- `| ${propertyId} | ${displayName3} | ${timeZone} | ${currency} | ${industry} |`
8004
+ const propertyId = state.manualPropertyId ?? rt.params[parameters13.propertyId.slug];
8005
+ if (propertyId) {
8006
+ sections.push(`### Property: ${propertyId}`, "");
8007
+ const { dimensions, metrics } = await getMetadata(
8008
+ rt.params,
8009
+ propertyId
7465
8010
  );
8011
+ appendMetadataSection(sections, dimensions, metrics);
8012
+ return sections.join("\n");
7466
8013
  }
7467
- sections.push("");
8014
+ sections.push(
8015
+ "_Could not list GA4 accounts. Please enable the Google Analytics Admin API in your GCP project, or set the Property ID parameter manually._",
8016
+ ""
8017
+ );
7468
8018
  return sections.join("\n");
7469
8019
  }
7470
8020
  };
@@ -7712,13 +8262,20 @@ activeUsers, sessions, screenPageViews, bounceRate, averageSessionDuration, conv
7712
8262
  error: "google-analytics: missing service account key"
7713
8263
  };
7714
8264
  }
8265
+ const propertyId = params[parameters13.propertyId.slug];
8266
+ if (!propertyId) {
8267
+ return { success: true };
8268
+ }
7715
8269
  try {
7716
- const res = await adminFetch(params, "/accountSummaries?pageSize=1");
8270
+ const res = await dataFetch(
8271
+ params,
8272
+ `/properties/${propertyId}/metadata`
8273
+ );
7717
8274
  if (!res.ok) {
7718
8275
  const body = await res.text().catch(() => res.statusText);
7719
8276
  return {
7720
8277
  success: false,
7721
- error: `google-analytics: accountSummaries failed (${res.status}): ${body}`
8278
+ error: `Google Analytics API failed: HTTP ${res.status} ${body}`
7722
8279
  };
7723
8280
  }
7724
8281
  return { success: true };
@@ -19358,7 +19915,7 @@ function escapeIdentifier(name) {
19358
19915
  function quoteLiteral2(value) {
19359
19916
  return "'" + value.replace(/'/g, "''") + "'";
19360
19917
  }
19361
- async function fetchTableNames4(params, database) {
19918
+ async function fetchTableNames3(params, database) {
19362
19919
  const rows = await runQuery9(
19363
19920
  params,
19364
19921
  `SELECT name FROM system.tables
@@ -19387,17 +19944,17 @@ var clickhouseSetupFlow = {
19387
19944
  slug: "tables",
19388
19945
  type: "multiSelect",
19389
19946
  question: {
19390
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
19391
- en: "Select target tables (multi-select allowed)"
19947
+ 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",
19948
+ en: "Select target tables and views (multi-select allowed)"
19392
19949
  },
19393
19950
  async fetchOptions(state, rt) {
19394
19951
  if (!state.database) return [];
19395
- const names = await fetchTableNames4(rt.params, state.database);
19952
+ const names = await fetchTableNames3(rt.params, state.database);
19396
19953
  const tableOptions = names.map((value) => ({ value }));
19397
19954
  return [
19398
19955
  {
19399
19956
  value: ALL_TABLES11,
19400
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
19957
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
19401
19958
  },
19402
19959
  ...tableOptions
19403
19960
  ];
@@ -19413,7 +19970,7 @@ var clickhouseSetupFlow = {
19413
19970
  const targetTables = await resolveSetupSelection({
19414
19971
  selected: state.tables,
19415
19972
  allSentinel: ALL_TABLES11,
19416
- fetchAll: () => fetchTableNames4(rt.params, database),
19973
+ fetchAll: () => fetchTableNames3(rt.params, database),
19417
19974
  limit: CLICKHOUSE_SETUP_MAX_TABLES
19418
19975
  });
19419
19976
  const sections = [
@@ -24012,7 +24569,7 @@ export default async function handler(c: Context) {
24012
24569
  const region = params[parameters50.region.slug];
24013
24570
  const baseUrl = region === "eu" ? "https://api-eu.customer.io" : "https://api.customer.io";
24014
24571
  try {
24015
- const res = await fetch(`${baseUrl}/v1/accounts/region`, {
24572
+ const res = await fetch(`${baseUrl}/v1/segments`, {
24016
24573
  method: "GET",
24017
24574
  headers: {
24018
24575
  Authorization: `Bearer ${appApiKey}`,
@@ -29568,7 +30125,7 @@ function apiFetch27(params, path5, init) {
29568
30125
  // ../connectors/src/connectors/gamma/setup-flow.ts
29569
30126
  var ALL_FOLDERS = "__ALL_FOLDERS__";
29570
30127
  var GAMMA_SETUP_MAX_FOLDERS = 20;
29571
- var FOLDERS_PAGE_LIMIT = 100;
30128
+ var FOLDERS_PAGE_LIMIT = 50;
29572
30129
  async function listAllFolders(params) {
29573
30130
  const results = [];
29574
30131
  let after;
@@ -29589,7 +30146,7 @@ async function listAllFolders(params) {
29589
30146
  return results;
29590
30147
  }
29591
30148
  async function listThemes(params) {
29592
- const res = await apiFetch27(params, "/themes?limit=100");
30149
+ const res = await apiFetch27(params, "/themes?limit=50");
29593
30150
  if (!res.ok) {
29594
30151
  return [];
29595
30152
  }
@@ -33426,7 +33983,8 @@ var semrushOnboarding = new ConnectorOnboarding({
33426
33983
  });
33427
33984
 
33428
33985
  // ../connectors/src/connectors/semrush/utils.ts
33429
- var PROJECTS_BASE_URL = "https://api.semrush.com/management/v1";
33986
+ var BASE_URL59 = "https://api.semrush.com";
33987
+ var PROJECTS_BASE_URL = `${BASE_URL59}/management/v1`;
33430
33988
  function projectsApiFetch(params, path5, init) {
33431
33989
  const apiKey = params[parameters68.apiKey.slug];
33432
33990
  if (!apiKey) {
@@ -33443,10 +34001,103 @@ function projectsApiFetch(params, path5, init) {
33443
34001
  if (!headers.has("Accept")) headers.set("Accept", "application/json");
33444
34002
  return fetch(url.toString(), { ...init, headers });
33445
34003
  }
34004
+ async function reportFetch(params, query) {
34005
+ const apiKey = params[parameters68.apiKey.slug];
34006
+ if (!apiKey) {
34007
+ throw new Error(
34008
+ `semrush: missing required parameter: ${parameters68.apiKey.slug}`
34009
+ );
34010
+ }
34011
+ const url = new URL(`${BASE_URL59}/`);
34012
+ for (const [k, v] of Object.entries(query)) {
34013
+ url.searchParams.set(k, v);
34014
+ }
34015
+ url.searchParams.set("key", apiKey);
34016
+ const res = await fetch(url.toString());
34017
+ const text = await res.text();
34018
+ if (text.startsWith("ERROR ")) throw new Error(text.trim());
34019
+ return parseSemicolonCsv(text);
34020
+ }
34021
+ async function backlinksFetch(params, query) {
34022
+ const apiKey = params[parameters68.apiKey.slug];
34023
+ if (!apiKey) {
34024
+ throw new Error(
34025
+ `semrush: missing required parameter: ${parameters68.apiKey.slug}`
34026
+ );
34027
+ }
34028
+ const url = new URL(`${BASE_URL59}/analytics/v1/`);
34029
+ for (const [k, v] of Object.entries(query)) {
34030
+ url.searchParams.set(k, v);
34031
+ }
34032
+ url.searchParams.set("key", apiKey);
34033
+ const res = await fetch(url.toString());
34034
+ const text = await res.text();
34035
+ if (text.startsWith("ERROR ")) throw new Error(text.trim());
34036
+ return parseSemicolonCsv(text);
34037
+ }
34038
+ function parseSemicolonCsv(raw) {
34039
+ const lines = raw.trim().split("\n").filter(Boolean);
34040
+ if (lines.length === 0) return { columns: [], rows: [] };
34041
+ const columns = lines[0].split(";");
34042
+ const rows = lines.slice(1).map((line) => {
34043
+ const values = line.split(";");
34044
+ const row = {};
34045
+ for (let i = 0; i < columns.length; i++) {
34046
+ row[columns[i]] = values[i] ?? "";
34047
+ }
34048
+ return row;
34049
+ });
34050
+ return { columns, rows };
34051
+ }
33446
34052
 
33447
34053
  // ../connectors/src/connectors/semrush/setup-flow.ts
33448
34054
  var ALL_PROJECTS6 = "__ALL_PROJECTS__";
33449
34055
  var SEMRUSH_SETUP_MAX_PROJECTS = 10;
34056
+ var SEMRUSH_DATABASES = [
34057
+ { value: "us", label: "United States" },
34058
+ { value: "uk", label: "United Kingdom" },
34059
+ { value: "ca", label: "Canada" },
34060
+ { value: "au", label: "Australia" },
34061
+ { value: "de", label: "Germany" },
34062
+ { value: "fr", label: "France" },
34063
+ { value: "es", label: "Spain" },
34064
+ { value: "it", label: "Italy" },
34065
+ { value: "br", label: "Brazil" },
34066
+ { value: "jp", label: "Japan" },
34067
+ { value: "in", label: "India" },
34068
+ { value: "ru", label: "Russia" },
34069
+ { value: "nl", label: "Netherlands" },
34070
+ { value: "se", label: "Sweden" },
34071
+ { value: "mx", label: "Mexico" },
34072
+ { value: "kr", label: "South Korea" },
34073
+ { value: "sg", label: "Singapore" },
34074
+ { value: "hk", label: "Hong Kong" },
34075
+ { value: "tw", label: "Taiwan" }
34076
+ ];
34077
+ var REPORT_TYPE_LABELS = {
34078
+ domain_overview: {
34079
+ en: "Domain Overview (rank, traffic, keyword count)",
34080
+ ja: "Domain Overview (\u30E9\u30F3\u30AF\u30FB\u30C8\u30E9\u30D5\u30A3\u30C3\u30AF\u30FB\u30AD\u30FC\u30EF\u30FC\u30C9\u6570)"
34081
+ },
34082
+ organic_search: {
34083
+ en: "Organic Search (top organic keywords)",
34084
+ ja: "Organic Search (\u4E0A\u4F4D\u30AA\u30FC\u30AC\u30CB\u30C3\u30AF\u30AD\u30FC\u30EF\u30FC\u30C9)"
34085
+ },
34086
+ paid_search: {
34087
+ en: "Paid Search (top paid keywords)",
34088
+ ja: "Paid Search (\u4E0A\u4F4D\u6709\u6599\u30AD\u30FC\u30EF\u30FC\u30C9)"
34089
+ },
34090
+ backlinks: {
34091
+ en: "Backlinks (referring domains, backlink count)",
34092
+ ja: "Backlinks (\u53C2\u7167\u30C9\u30E1\u30A4\u30F3\u30FB\u88AB\u30EA\u30F3\u30AF\u6570)"
34093
+ }
34094
+ };
34095
+ var REPORT_TYPE_VALUES = [
34096
+ "domain_overview",
34097
+ "organic_search",
34098
+ "paid_search",
34099
+ "backlinks"
34100
+ ];
33450
34101
  function projectId(p) {
33451
34102
  const raw = p.project_id ?? p.id;
33452
34103
  return raw == null ? "" : String(raw);
@@ -33455,7 +34106,7 @@ function projectName(p) {
33455
34106
  return p.project_name ?? p.name ?? p.domain ?? p.url ?? projectId(p);
33456
34107
  }
33457
34108
  function projectDomain(p) {
33458
- return p.domain ?? p.url ?? "";
34109
+ return p.domain ?? p.domain_unicode ?? p.url ?? "";
33459
34110
  }
33460
34111
  function projectCreatedAt(p) {
33461
34112
  return p.created_at ?? p.date_created ?? "";
@@ -33463,13 +34114,19 @@ function projectCreatedAt(p) {
33463
34114
  async function fetchProjects(params) {
33464
34115
  let res;
33465
34116
  try {
33466
- res = await projectsApiFetch(params, "/projects/");
34117
+ res = await projectsApiFetch(params, "/projects");
33467
34118
  } catch (err) {
33468
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
34119
+ return {
34120
+ ok: false,
34121
+ error: err instanceof Error ? err.message : String(err)
34122
+ };
33469
34123
  }
33470
34124
  if (!res.ok) {
33471
34125
  const body2 = await res.text().catch(() => res.statusText);
33472
- return { ok: false, error: `HTTP ${res.status} ${body2 || res.statusText}` };
34126
+ return {
34127
+ ok: false,
34128
+ error: `HTTP ${res.status} ${body2 || res.statusText}`
34129
+ };
33473
34130
  }
33474
34131
  let body;
33475
34132
  try {
@@ -33483,9 +34140,171 @@ async function fetchProjects(params) {
33483
34140
  const projects = Array.isArray(body) ? body : Array.isArray(body?.projects) ? body.projects : Array.isArray(body?.data) ? body.data : [];
33484
34141
  return { ok: true, projects };
33485
34142
  }
34143
+ function formatNumber(n) {
34144
+ return n.toLocaleString("en-US");
34145
+ }
34146
+ async function fetchDomainOverview(params, domain, database) {
34147
+ const sections = [];
34148
+ try {
34149
+ const report = await reportFetch(params, {
34150
+ type: "domain_ranks",
34151
+ domain,
34152
+ database
34153
+ });
34154
+ if (report.rows.length === 0) {
34155
+ sections.push("_No data found for this domain._", "");
34156
+ return sections;
34157
+ }
34158
+ const row = report.rows[0];
34159
+ sections.push("| Metric | Value |");
34160
+ sections.push("|--------|-------|");
34161
+ sections.push(
34162
+ `| Rank | ${row["Rank"] ?? "-"} |`,
34163
+ `| Organic Keywords | ${formatNumber(Number(row["Organic Keywords"]) || 0)} |`,
34164
+ `| Organic Traffic | ${formatNumber(Number(row["Organic Traffic"]) || 0)} |`,
34165
+ `| Organic Cost | $${formatNumber(Number(row["Organic Cost"]) || 0)} |`,
34166
+ `| Adwords Keywords | ${formatNumber(Number(row["Adwords Keywords"]) || 0)} |`,
34167
+ `| Adwords Traffic | ${formatNumber(Number(row["Adwords Traffic"]) || 0)} |`,
34168
+ `| Adwords Cost | $${formatNumber(Number(row["Adwords Cost"]) || 0)} |`
34169
+ );
34170
+ sections.push("");
34171
+ } catch (err) {
34172
+ sections.push(
34173
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
34174
+ ""
34175
+ );
34176
+ }
34177
+ return sections;
34178
+ }
34179
+ async function fetchOrganicSearch(params, domain, database) {
34180
+ return fetchKeywordReport(params, "domain_organic", domain, database);
34181
+ }
34182
+ async function fetchPaidSearch(params, domain, database) {
34183
+ return fetchKeywordReport(params, "domain_adwords", domain, database);
34184
+ }
34185
+ async function fetchKeywordReport(params, type, domain, database) {
34186
+ const sections = [];
34187
+ try {
34188
+ const report = await reportFetch(params, {
34189
+ type,
34190
+ domain,
34191
+ database,
34192
+ display_limit: "5"
34193
+ });
34194
+ if (report.rows.length === 0) {
34195
+ sections.push("_No data found._", "");
34196
+ return sections;
34197
+ }
34198
+ sections.push(...renderCsvTable(report, 5));
34199
+ sections.push("");
34200
+ } catch (err) {
34201
+ sections.push(
34202
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
34203
+ ""
34204
+ );
34205
+ }
34206
+ return sections;
34207
+ }
34208
+ async function fetchBacklinks(params, domain) {
34209
+ const sections = [];
34210
+ try {
34211
+ const report = await backlinksFetch(params, {
34212
+ type: "backlinks_overview",
34213
+ target: domain,
34214
+ target_type: "root_domain"
34215
+ });
34216
+ if (report.rows.length === 0) {
34217
+ sections.push("_No backlink data found._", "");
34218
+ return sections;
34219
+ }
34220
+ const row = report.rows[0];
34221
+ sections.push("| Metric | Value |");
34222
+ sections.push("|--------|-------|");
34223
+ for (const col of report.columns) {
34224
+ sections.push(`| ${col} | ${formatNumber(Number(row[col]) || 0)} |`);
34225
+ }
34226
+ sections.push("");
34227
+ } catch (err) {
34228
+ sections.push(
34229
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
34230
+ ""
34231
+ );
34232
+ }
34233
+ return sections;
34234
+ }
34235
+ function renderCsvTable(report, maxRows) {
34236
+ const cols = report.columns;
34237
+ const rows = report.rows.slice(0, maxRows);
34238
+ const lines = [];
34239
+ lines.push(`| ${cols.join(" | ")} |`);
34240
+ lines.push(`|${cols.map(() => "---").join("|")}|`);
34241
+ for (const row of rows) {
34242
+ const cells = cols.map((c) => (row[c] ?? "").replace(/\|/g, "\\|"));
34243
+ lines.push(`| ${cells.join(" | ")} |`);
34244
+ }
34245
+ return lines;
34246
+ }
33486
34247
  var semrushSetupFlow = {
33487
34248
  initialState: () => ({}),
33488
34249
  steps: [
34250
+ {
34251
+ slug: "domain",
34252
+ type: "select",
34253
+ allowFreeText: true,
34254
+ question: {
34255
+ ja: "\u5206\u6790\u5BFE\u8C61\u306E\u30C9\u30E1\u30A4\u30F3\u3092\u9078\u629E\u307E\u305F\u306F\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
34256
+ en: "Select or enter the domain to analyze"
34257
+ },
34258
+ async fetchOptions(_state, rt) {
34259
+ const result = await fetchProjects(rt.params);
34260
+ if (!result.ok || result.projects.length === 0) return [];
34261
+ const seen = /* @__PURE__ */ new Set();
34262
+ const options = [];
34263
+ for (const p of result.projects) {
34264
+ const d = projectDomain(p);
34265
+ if (d && !seen.has(d)) {
34266
+ seen.add(d);
34267
+ options.push({
34268
+ value: d,
34269
+ label: `${d} (${projectName(p)})`
34270
+ });
34271
+ }
34272
+ }
34273
+ return options;
34274
+ },
34275
+ applyAnswer: (state, answer) => ({ ...state, domain: answer[0] })
34276
+ },
34277
+ {
34278
+ slug: "database",
34279
+ type: "select",
34280
+ allowFreeText: false,
34281
+ question: {
34282
+ 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",
34283
+ en: "Select the region (primary market for the target domain)"
34284
+ },
34285
+ async fetchOptions() {
34286
+ return SEMRUSH_DATABASES.map((db) => ({
34287
+ value: db.value,
34288
+ label: `${db.label} (${db.value})`
34289
+ }));
34290
+ },
34291
+ applyAnswer: (state, answer) => ({ ...state, database: answer[0] })
34292
+ },
34293
+ {
34294
+ slug: "reportTypes",
34295
+ type: "multiSelect",
34296
+ question: {
34297
+ 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",
34298
+ en: "Select report types to explore (multi-select allowed)"
34299
+ },
34300
+ async fetchOptions(_state, rt) {
34301
+ return REPORT_TYPE_VALUES.map((value) => ({
34302
+ value,
34303
+ label: REPORT_TYPE_LABELS[value][rt.language]
34304
+ }));
34305
+ },
34306
+ applyAnswer: (state, answer) => ({ ...state, reportTypes: answer })
34307
+ },
33489
34308
  {
33490
34309
  slug: "projects",
33491
34310
  type: "multiSelect",
@@ -33502,7 +34321,9 @@ var semrushSetupFlow = {
33502
34321
  const id = projectId(p);
33503
34322
  if (!id) return null;
33504
34323
  return { value: id, label: projectName(p) };
33505
- }).filter((opt) => opt !== null);
34324
+ }).filter(
34325
+ (opt) => opt !== null
34326
+ );
33506
34327
  if (options.length === 0) return [];
33507
34328
  return [
33508
34329
  {
@@ -33516,49 +34337,80 @@ var semrushSetupFlow = {
33516
34337
  }
33517
34338
  ],
33518
34339
  async finalize(state, rt) {
33519
- const sections = ["## Semrush", ""];
33520
- if (!state.projects?.length) {
34340
+ if (!state.domain || !state.database || !state.reportTypes) {
34341
+ throw new Error("Semrush setup: incomplete state on finalize");
34342
+ }
34343
+ const domain = state.domain;
34344
+ const database = state.database;
34345
+ const selected = state.reportTypes.filter(
34346
+ (r) => REPORT_TYPE_VALUES.includes(r)
34347
+ );
34348
+ const sections = [
34349
+ "## Semrush",
34350
+ "",
34351
+ `**Domain:** ${domain}`,
34352
+ `**Region:** ${database}`,
34353
+ ""
34354
+ ];
34355
+ for (const reportType of selected) {
33521
34356
  sections.push(
33522
- "_No Semrush Projects API access detected; Standard Analytics reports run by domain/keyword and don't require pre-selection._",
34357
+ `### ${REPORT_TYPE_LABELS[reportType].en}`,
33523
34358
  ""
33524
34359
  );
33525
- return sections.join("\n");
33526
- }
33527
- const result = await fetchProjects(rt.params);
33528
- if (!result.ok) {
33529
- throw new Error(
33530
- `semrush: listProjects failed on finalize: ${result.error}`
33531
- );
33532
- }
33533
- const projectByIdMap = /* @__PURE__ */ new Map();
33534
- for (const p of result.projects) {
33535
- const id = projectId(p);
33536
- if (id) projectByIdMap.set(id, p);
33537
- }
33538
- const targetIds = await resolveSetupSelection({
33539
- selected: state.projects,
33540
- allSentinel: ALL_PROJECTS6,
33541
- fetchAll: async () => result.projects.map((p) => projectId(p)).filter((id) => id),
33542
- limit: SEMRUSH_SETUP_MAX_PROJECTS
33543
- });
33544
- if (!targetIds.length) {
33545
- sections.push("_No projects selected._", "");
33546
- return sections.join("\n");
34360
+ switch (reportType) {
34361
+ case "domain_overview":
34362
+ sections.push(
34363
+ ...await fetchDomainOverview(rt.params, domain, database)
34364
+ );
34365
+ break;
34366
+ case "organic_search":
34367
+ sections.push(
34368
+ ...await fetchOrganicSearch(rt.params, domain, database)
34369
+ );
34370
+ break;
34371
+ case "paid_search":
34372
+ sections.push(
34373
+ ...await fetchPaidSearch(rt.params, domain, database)
34374
+ );
34375
+ break;
34376
+ case "backlinks":
34377
+ sections.push(...await fetchBacklinks(rt.params, domain));
34378
+ break;
34379
+ }
33547
34380
  }
33548
- sections.push("| Project | Domain | Created |");
33549
- sections.push("|---------|--------|---------|");
33550
- for (const id of targetIds) {
33551
- const p = projectByIdMap.get(id);
33552
- if (!p) {
33553
- sections.push(`| ${id} | - | - |`);
33554
- continue;
34381
+ if (state.projects?.length) {
34382
+ const result = await fetchProjects(rt.params);
34383
+ if (result.ok) {
34384
+ const projectByIdMap = /* @__PURE__ */ new Map();
34385
+ for (const p of result.projects) {
34386
+ const id = projectId(p);
34387
+ if (id) projectByIdMap.set(id, p);
34388
+ }
34389
+ const targetIds = await resolveSetupSelection({
34390
+ selected: state.projects,
34391
+ allSentinel: ALL_PROJECTS6,
34392
+ fetchAll: async () => result.projects.map((p) => projectId(p)).filter((id) => id),
34393
+ limit: SEMRUSH_SETUP_MAX_PROJECTS
34394
+ });
34395
+ if (targetIds.length > 0) {
34396
+ sections.push("### Projects", "");
34397
+ sections.push("| Project | Domain | Created |");
34398
+ sections.push("|---------|--------|---------|");
34399
+ for (const id of targetIds) {
34400
+ const p = projectByIdMap.get(id);
34401
+ if (!p) {
34402
+ sections.push(`| ${id} | - | - |`);
34403
+ continue;
34404
+ }
34405
+ const name = projectName(p).replace(/\|/g, "\\|");
34406
+ const dom = projectDomain(p).replace(/\|/g, "\\|") || "-";
34407
+ const created = projectCreatedAt(p) || "-";
34408
+ sections.push(`| ${name} | ${dom} | ${created} |`);
34409
+ }
34410
+ sections.push("");
34411
+ }
33555
34412
  }
33556
- const name = projectName(p).replace(/\|/g, "\\|");
33557
- const domain = projectDomain(p).replace(/\|/g, "\\|") || "-";
33558
- const created = projectCreatedAt(p) || "-";
33559
- sections.push(`| ${name} | ${domain} | ${created} |`);
33560
34413
  }
33561
- sections.push("");
33562
34414
  return sections.join("\n");
33563
34415
  }
33564
34416
  };
@@ -33819,7 +34671,7 @@ API \u30E6\u30CB\u30C3\u30C8\u6B8B\u91CF\u306E\u78BA\u8A8D\uFF08\u7121\u6599\u30
33819
34671
 
33820
34672
  // ../connectors/src/connectors/google-search-console-oauth/tools/list-sites.ts
33821
34673
  import { z as z84 } from "zod";
33822
- var BASE_URL59 = "https://searchconsole.googleapis.com/webmasters/v3";
34674
+ var BASE_URL60 = "https://searchconsole.googleapis.com/webmasters/v3";
33823
34675
  var REQUEST_TIMEOUT_MS67 = 6e4;
33824
34676
  var cachedToken30 = null;
33825
34677
  async function getProxyToken30(config) {
@@ -33890,7 +34742,7 @@ var listSitesTool = new ConnectorTool({
33890
34742
  `[connector-request] google-search-console-oauth/${connection.name}: listSites`
33891
34743
  );
33892
34744
  try {
33893
- const url = `${BASE_URL59}/sites`;
34745
+ const url = `${BASE_URL60}/sites`;
33894
34746
  const token = await getProxyToken30(config.oauthProxy);
33895
34747
  const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
33896
34748
  const controller = new AbortController();
@@ -34072,7 +34924,7 @@ var googleSearchConsoleOauthSetupFlow = {
34072
34924
  import { z as z85 } from "zod";
34073
34925
  var BASE_HOST10 = "https://searchconsole.googleapis.com";
34074
34926
  var BASE_PATH_SEGMENT10 = "/webmasters/v3";
34075
- var BASE_URL60 = `${BASE_HOST10}${BASE_PATH_SEGMENT10}`;
34927
+ var BASE_URL61 = `${BASE_HOST10}${BASE_PATH_SEGMENT10}`;
34076
34928
  var REQUEST_TIMEOUT_MS68 = 6e4;
34077
34929
  var cachedToken31 = null;
34078
34930
  async function getProxyToken31(config) {
@@ -34157,7 +35009,7 @@ For URL Inspection API requests, use the absolute path '/v1/urlInspection/index:
34157
35009
  resolvedPath,
34158
35010
  BASE_PATH_SEGMENT10
34159
35011
  );
34160
- let url = `${BASE_URL60}${normalizedPath}`;
35012
+ let url = `${BASE_URL61}${normalizedPath}`;
34161
35013
  if (queryParams) {
34162
35014
  const searchParams = new URLSearchParams(queryParams);
34163
35015
  url += `?${searchParams.toString()}`;
@@ -34483,7 +35335,7 @@ function isInternalSchema3(name) {
34483
35335
  if (lower.startsWith("pg_toast_")) return true;
34484
35336
  return false;
34485
35337
  }
34486
- async function fetchTableNames5(params, schema) {
35338
+ async function fetchTableNames4(params, schema) {
34487
35339
  const rows = await runQuery10(
34488
35340
  params,
34489
35341
  `SELECT table_name FROM information_schema.tables
@@ -34516,17 +35368,17 @@ var supabaseSetupFlow = {
34516
35368
  slug: "tables",
34517
35369
  type: "multiSelect",
34518
35370
  question: {
34519
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
34520
- en: "Select target tables (multi-select allowed)"
35371
+ 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",
35372
+ en: "Select target tables and views (multi-select allowed)"
34521
35373
  },
34522
35374
  async fetchOptions(state, rt) {
34523
35375
  if (!state.schema) return [];
34524
- const names = await fetchTableNames5(rt.params, state.schema);
35376
+ const names = await fetchTableNames4(rt.params, state.schema);
34525
35377
  const tableOptions = names.map((value) => ({ value }));
34526
35378
  return [
34527
35379
  {
34528
35380
  value: ALL_TABLES13,
34529
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
35381
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
34530
35382
  },
34531
35383
  ...tableOptions
34532
35384
  ];
@@ -34542,7 +35394,7 @@ var supabaseSetupFlow = {
34542
35394
  const targetTables = await resolveSetupSelection({
34543
35395
  selected: state.tables,
34544
35396
  allSentinel: ALL_TABLES13,
34545
- fetchAll: () => fetchTableNames5(rt.params, schema),
35397
+ fetchAll: () => fetchTableNames4(rt.params, schema),
34546
35398
  limit: SUPABASE_SETUP_MAX_TABLES
34547
35399
  });
34548
35400
  const sections = [
@@ -34777,13 +35629,13 @@ var parameters71 = {
34777
35629
  };
34778
35630
 
34779
35631
  // ../connectors/src/connectors/clickup/utils.ts
34780
- var BASE_URL61 = "https://api.clickup.com/api/v2";
35632
+ var BASE_URL62 = "https://api.clickup.com/api/v2";
34781
35633
  async function apiFetch30(params, path5, init) {
34782
35634
  const token = params[parameters71.apiToken.slug];
34783
35635
  if (!token) {
34784
35636
  throw new Error("clickup: missing required parameter: api-token");
34785
35637
  }
34786
- const url = `${BASE_URL61}${path5.startsWith("/") ? "" : "/"}${path5}`;
35638
+ const url = `${BASE_URL62}${path5.startsWith("/") ? "" : "/"}${path5}`;
34787
35639
  const headers = new Headers(init?.headers);
34788
35640
  headers.set("Authorization", token);
34789
35641
  if (!headers.has("Accept")) headers.set("Accept", "application/json");
@@ -34911,7 +35763,7 @@ var clickupSetupFlow = {
34911
35763
  import { z as z87 } from "zod";
34912
35764
  var BASE_HOST11 = "https://api.clickup.com";
34913
35765
  var BASE_PATH_SEGMENT11 = "/api/v2";
34914
- var BASE_URL62 = `${BASE_HOST11}${BASE_PATH_SEGMENT11}`;
35766
+ var BASE_URL63 = `${BASE_HOST11}${BASE_PATH_SEGMENT11}`;
34915
35767
  var REQUEST_TIMEOUT_MS69 = 6e4;
34916
35768
  var inputSchema87 = z87.object({
34917
35769
  toolUseIntent: z87.string().optional().describe(
@@ -34984,7 +35836,7 @@ Pagination: ClickUp uses zero-indexed \`page\` query parameter on list endpoints
34984
35836
  try {
34985
35837
  const token = parameters71.apiToken.getValue(connection);
34986
35838
  const normalizedPath = normalizeRequestPath(path5, BASE_PATH_SEGMENT11);
34987
- const url = `${BASE_URL62}${normalizedPath}`;
35839
+ const url = `${BASE_URL63}${normalizedPath}`;
34988
35840
  const controller = new AbortController();
34989
35841
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS69);
34990
35842
  try {
@@ -35314,7 +36166,7 @@ function quoteLiteral4(value) {
35314
36166
  }
35315
36167
  function buildFlow(options) {
35316
36168
  const { connectorName, forceEncrypt } = options;
35317
- async function fetchTableNames7(params, schema) {
36169
+ async function fetchTableNames5(params, schema) {
35318
36170
  const rows = await runSqlServerSetupQuery(
35319
36171
  params,
35320
36172
  `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
@@ -35349,17 +36201,17 @@ function buildFlow(options) {
35349
36201
  slug: "tables",
35350
36202
  type: "multiSelect",
35351
36203
  question: {
35352
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
35353
- en: "Select target tables (multi-select allowed)"
36204
+ 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",
36205
+ en: "Select target tables and views (multi-select allowed)"
35354
36206
  },
35355
36207
  async fetchOptions(state, rt) {
35356
36208
  if (!state.schema) return [];
35357
- const names = await fetchTableNames7(rt.params, state.schema);
36209
+ const names = await fetchTableNames5(rt.params, state.schema);
35358
36210
  const tableOptions = names.map((value) => ({ value }));
35359
36211
  return [
35360
36212
  {
35361
36213
  value: ALL_TABLES14,
35362
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
36214
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
35363
36215
  },
35364
36216
  ...tableOptions
35365
36217
  ];
@@ -35375,7 +36227,7 @@ function buildFlow(options) {
35375
36227
  const targetTables = await resolveSetupSelection({
35376
36228
  selected: state.tables,
35377
36229
  allSentinel: ALL_TABLES14,
35378
- fetchAll: () => fetchTableNames7(rt.params, schema),
36230
+ fetchAll: () => fetchTableNames5(rt.params, schema),
35379
36231
  limit: SQLSERVER_SETUP_MAX_TABLES
35380
36232
  });
35381
36233
  const sections = [
@@ -36370,14 +37222,15 @@ function isInternalOwner(name) {
36370
37222
  function quoteLiteral5(value) {
36371
37223
  return "'" + value.replace(/'/g, "''") + "'";
36372
37224
  }
36373
- async function fetchTableNames6(params, owner) {
37225
+ async function fetchTableAndViewNames2(params, owner) {
36374
37226
  const rows = await runOracleSetupQuery(
36375
37227
  params,
36376
- `SELECT TABLE_NAME FROM ALL_TABLES
36377
- WHERE OWNER = ${quoteLiteral5(owner)}
36378
- ORDER BY TABLE_NAME`
37228
+ `SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER = ${quoteLiteral5(owner)}
37229
+ UNION
37230
+ SELECT VIEW_NAME FROM ALL_VIEWS WHERE OWNER = ${quoteLiteral5(owner)}
37231
+ ORDER BY 1`
36379
37232
  );
36380
- return rows.map((r) => String(r["TABLE_NAME"] ?? "")).filter((name) => name);
37233
+ return rows.map((r) => String(r["TABLE_NAME"] ?? r["VIEW_NAME"] ?? "")).filter((name) => name);
36381
37234
  }
36382
37235
  var oracleSetupFlow = {
36383
37236
  initialState: () => ({}),
@@ -36402,17 +37255,17 @@ var oracleSetupFlow = {
36402
37255
  slug: "tables",
36403
37256
  type: "multiSelect",
36404
37257
  question: {
36405
- ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
36406
- en: "Select target tables (multi-select allowed)"
37258
+ 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",
37259
+ en: "Select target tables and views (multi-select allowed)"
36407
37260
  },
36408
37261
  async fetchOptions(state, rt) {
36409
37262
  if (!state.owner) return [];
36410
- const names = await fetchTableNames6(rt.params, state.owner);
37263
+ const names = await fetchTableAndViewNames2(rt.params, state.owner);
36411
37264
  const tableOptions = names.map((value) => ({ value }));
36412
37265
  return [
36413
37266
  {
36414
37267
  value: ALL_TABLES15,
36415
- label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
37268
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB\u30FB\u30D3\u30E5\u30FC" : "All tables and views"
36416
37269
  },
36417
37270
  ...tableOptions
36418
37271
  ];
@@ -36428,9 +37281,22 @@ var oracleSetupFlow = {
36428
37281
  const targetTables = await resolveSetupSelection({
36429
37282
  selected: state.tables,
36430
37283
  allSentinel: ALL_TABLES15,
36431
- fetchAll: () => fetchTableNames6(rt.params, owner),
37284
+ fetchAll: () => fetchTableAndViewNames2(rt.params, owner),
36432
37285
  limit: ORACLE_SETUP_MAX_TABLES
36433
37286
  });
37287
+ const typeRows = targetTables.length > 0 ? await runOracleSetupQuery(
37288
+ rt.params,
37289
+ `SELECT OBJECT_NAME, OBJECT_TYPE FROM ALL_OBJECTS
37290
+ WHERE OWNER = ${quoteLiteral5(owner)}
37291
+ AND OBJECT_NAME IN (${targetTables.map(quoteLiteral5).join(", ")})
37292
+ AND OBJECT_TYPE IN ('TABLE', 'VIEW')`
37293
+ ) : [];
37294
+ const typeMap = new Map(
37295
+ typeRows.map((r) => [
37296
+ String(r["OBJECT_NAME"] ?? ""),
37297
+ String(r["OBJECT_TYPE"] ?? "TABLE")
37298
+ ])
37299
+ );
36434
37300
  const sections = [
36435
37301
  "## Oracle Database",
36436
37302
  "",
@@ -36438,6 +37304,7 @@ var oracleSetupFlow = {
36438
37304
  ""
36439
37305
  ];
36440
37306
  for (const table of targetTables) {
37307
+ const heading = typeMap.get(table) === "VIEW" ? "View" : "Table";
36441
37308
  const cols = await runOracleSetupQuery(
36442
37309
  rt.params,
36443
37310
  `SELECT COLUMN_NAME, DATA_TYPE, NULLABLE, DATA_DEFAULT
@@ -36446,7 +37313,7 @@ var oracleSetupFlow = {
36446
37313
  AND TABLE_NAME = ${quoteLiteral5(table)}
36447
37314
  ORDER BY COLUMN_ID`
36448
37315
  );
36449
- sections.push(`#### Table: ${table}`, "");
37316
+ sections.push(`#### ${heading}: ${table}`, "");
36450
37317
  sections.push("| Column | Type | Nullable | Default |");
36451
37318
  sections.push("|--------|------|----------|---------|");
36452
37319
  for (const c of cols) {
@@ -38751,7 +39618,7 @@ export default async function handler(c: Context) {
38751
39618
  import { z as z97 } from "zod";
38752
39619
  var BASE_HOST12 = "https://api.powerbi.com";
38753
39620
  var BASE_PATH_SEGMENT18 = "/v1.0/myorg";
38754
- var BASE_URL63 = `${BASE_HOST12}${BASE_PATH_SEGMENT18}`;
39621
+ var BASE_URL64 = `${BASE_HOST12}${BASE_PATH_SEGMENT18}`;
38755
39622
  var REQUEST_TIMEOUT_MS74 = 6e4;
38756
39623
  var cachedToken32 = null;
38757
39624
  async function getProxyToken32(config) {
@@ -38833,7 +39700,7 @@ The signed-in user must have access to the workspace; unlike Service Principals,
38833
39700
  );
38834
39701
  try {
38835
39702
  const normalizedPath = normalizeRequestPath(path5, BASE_PATH_SEGMENT18);
38836
- let url = `${BASE_URL63}${normalizedPath}`;
39703
+ let url = `${BASE_URL64}${normalizedPath}`;
38837
39704
  if (queryParams) {
38838
39705
  const searchParams = new URLSearchParams(queryParams);
38839
39706
  url += `?${searchParams.toString()}`;
@@ -38916,14 +39783,18 @@ var powerbiOauthOnboarding = new ConnectorOnboarding({
38916
39783
  var parameters80 = {};
38917
39784
 
38918
39785
  // ../connectors/src/connectors/powerbi-oauth/utils.ts
38919
- var BASE_URL64 = "https://api.powerbi.com/v1.0/myorg";
39786
+ var BASE_URL65 = "https://api.powerbi.com/v1.0/myorg";
38920
39787
  function apiFetch35(proxyFetch, path5, init) {
38921
- const url = `${BASE_URL64}${path5.startsWith("/") ? "" : "/"}${path5}`;
39788
+ const url = `${BASE_URL65}${path5.startsWith("/") ? "" : "/"}${path5}`;
38922
39789
  return proxyFetch(url, init);
38923
39790
  }
38924
39791
 
38925
39792
  // ../connectors/src/connectors/powerbi-oauth/setup-flow.ts
38926
39793
  var ALL_WORKSPACES = "__ALL_WORKSPACES__";
39794
+ var MY_WORKSPACE = "__MY_WORKSPACE__";
39795
+ var ALL_DATASETS = "__ALL_DATASETS__";
39796
+ var ALL_REPORTS = "__ALL_REPORTS__";
39797
+ var ALL_DASHBOARDS = "__ALL_DASHBOARDS__";
38927
39798
  var POWERBI_SETUP_MAX_WORKSPACES = 10;
38928
39799
  var RESOURCE_DISPLAY_LIMIT = 25;
38929
39800
  var RESOURCE_DATASETS = "datasets";
@@ -38939,14 +39810,12 @@ async function listGroups(proxyFetch) {
38939
39810
  return data.value ?? [];
38940
39811
  }
38941
39812
  async function listResource(proxyFetch, groupId, resource) {
38942
- const res = await apiFetch35(
38943
- proxyFetch,
38944
- `/groups/${encodeURIComponent(groupId)}/${resource}`
38945
- );
39813
+ const path5 = groupId === MY_WORKSPACE ? `/${resource}` : `/groups/${encodeURIComponent(groupId)}/${resource}`;
39814
+ const res = await apiFetch35(proxyFetch, path5);
38946
39815
  if (!res.ok) {
38947
39816
  const body = await res.text().catch(() => res.statusText);
38948
39817
  throw new Error(
38949
- `powerbi: list ${resource} for group ${groupId} failed (${res.status}): ${body}`
39818
+ `powerbi: list ${resource} for ${groupId === MY_WORKSPACE ? "My workspace" : `group ${groupId}`} failed (${res.status}): ${body}`
38950
39819
  );
38951
39820
  }
38952
39821
  const data = await res.json();
@@ -38955,6 +39824,99 @@ async function listResource(proxyFetch, groupId, resource) {
38955
39824
  function resourceLabel(r) {
38956
39825
  return r.name ?? r.displayName ?? r.id ?? "(unknown)";
38957
39826
  }
39827
+ var INTERNAL_TABLE_PREFIXES = [
39828
+ "DateTableTemplate_",
39829
+ "LocalDateTable_"
39830
+ ];
39831
+ function isInternalColumn(name) {
39832
+ return /^RowNumber-[0-9A-Fa-f-]+$/.test(name);
39833
+ }
39834
+ async function fetchDatasetSchema(proxyFetch, wsId, datasetId) {
39835
+ const pathPrefix = wsId === MY_WORKSPACE ? "" : `/groups/${encodeURIComponent(wsId)}`;
39836
+ const daxQuery = 'EVALUATE SELECTCOLUMNS(COLUMNSTATISTICS(), "T", [Table Name], "C", [Column Name])';
39837
+ const res = await apiFetch35(
39838
+ proxyFetch,
39839
+ `${pathPrefix}/datasets/${encodeURIComponent(datasetId)}/executeQueries`,
39840
+ {
39841
+ method: "POST",
39842
+ headers: { "Content-Type": "application/json" },
39843
+ body: JSON.stringify({
39844
+ queries: [{ query: daxQuery }],
39845
+ serializerSettings: { includeNulls: true }
39846
+ })
39847
+ }
39848
+ );
39849
+ if (!res.ok) return /* @__PURE__ */ new Map();
39850
+ const data = await res.json();
39851
+ const rows = data.results?.[0]?.tables?.[0]?.rows ?? [];
39852
+ const schema = /* @__PURE__ */ new Map();
39853
+ for (const row of rows) {
39854
+ const table = row["[T]"] ?? "";
39855
+ const column = row["[C]"] ?? "";
39856
+ if (!table || !column) continue;
39857
+ if (INTERNAL_TABLE_PREFIXES.some((p) => table.startsWith(p))) continue;
39858
+ if (isInternalColumn(column)) continue;
39859
+ if (!schema.has(table)) schema.set(table, []);
39860
+ schema.get(table).push(column);
39861
+ }
39862
+ return schema;
39863
+ }
39864
+ function workspaceName(wsId, groupById, language) {
39865
+ if (wsId === MY_WORKSPACE) {
39866
+ return language === "ja" ? "\u30DE\u30A4 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "My workspace";
39867
+ }
39868
+ return groupById.get(wsId)?.name ?? wsId;
39869
+ }
39870
+ async function resolveWorkspaceIds(selected, proxyFetch) {
39871
+ return resolveSetupSelection({
39872
+ selected,
39873
+ allSentinel: ALL_WORKSPACES,
39874
+ fetchAll: async () => {
39875
+ const groups = await listGroups(proxyFetch);
39876
+ return [MY_WORKSPACE, ...groups.map((g) => g.id).filter(Boolean)];
39877
+ },
39878
+ limit: POWERBI_SETUP_MAX_WORKSPACES
39879
+ });
39880
+ }
39881
+ function compoundKey(wsId, objectId) {
39882
+ return `${wsId}:${objectId}`;
39883
+ }
39884
+ function parseCompoundKey(key) {
39885
+ const idx = key.indexOf(":");
39886
+ if (idx < 0) return { wsId: "", objectId: key };
39887
+ return { wsId: key.slice(0, idx), objectId: key.slice(idx + 1) };
39888
+ }
39889
+ async function fetchObjectOptions(state, resource, allSentinel, allLabelJa, allLabelEn, rt) {
39890
+ if (!state.workspaces?.length || !state.resources?.includes(resource))
39891
+ return [];
39892
+ const wsIds = await resolveWorkspaceIds(
39893
+ state.workspaces,
39894
+ rt.config.proxyFetch
39895
+ );
39896
+ const allGroups = await listGroups(rt.config.proxyFetch);
39897
+ const groupById = new Map(allGroups.map((g) => [g.id, g]));
39898
+ const multiWorkspace = wsIds.length > 1;
39899
+ const options = [];
39900
+ for (const wsId of wsIds) {
39901
+ const wsLabel = workspaceName(wsId, groupById, rt.language);
39902
+ const items = await listResource(rt.config.proxyFetch, wsId, resource);
39903
+ for (const item of items) {
39904
+ if (!item.id) continue;
39905
+ options.push({
39906
+ value: compoundKey(wsId, item.id),
39907
+ label: multiWorkspace ? `${wsLabel} / ${resourceLabel(item)}` : resourceLabel(item)
39908
+ });
39909
+ }
39910
+ }
39911
+ if (options.length === 0) return [];
39912
+ return [
39913
+ {
39914
+ value: allSentinel,
39915
+ label: rt.language === "ja" ? allLabelJa : allLabelEn
39916
+ },
39917
+ ...options
39918
+ ];
39919
+ }
38958
39920
  var powerbiOauthSetupFlow = {
38959
39921
  initialState: () => ({}),
38960
39922
  steps: [
@@ -38973,6 +39935,10 @@ var powerbiOauthSetupFlow = {
38973
39935
  value: ALL_WORKSPACES,
38974
39936
  label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "All workspaces"
38975
39937
  },
39938
+ {
39939
+ value: MY_WORKSPACE,
39940
+ label: rt.language === "ja" ? "\u30DE\u30A4 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "My workspace"
39941
+ },
38976
39942
  ...options
38977
39943
  ];
38978
39944
  },
@@ -38987,16 +39953,82 @@ var powerbiOauthSetupFlow = {
38987
39953
  },
38988
39954
  async fetchOptions(state, rt) {
38989
39955
  if (!state.workspaces?.length) return [];
38990
- const datasetsLabel = rt.language === "ja" ? "\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8" : "Datasets";
38991
- const reportsLabel = rt.language === "ja" ? "\u30EC\u30DD\u30FC\u30C8" : "Reports";
38992
- const dashboardsLabel = rt.language === "ja" ? "\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9" : "Dashboards";
38993
39956
  return [
38994
- { value: RESOURCE_DATASETS, label: datasetsLabel },
38995
- { value: RESOURCE_REPORTS, label: reportsLabel },
38996
- { value: RESOURCE_DASHBOARDS, label: dashboardsLabel }
39957
+ {
39958
+ value: RESOURCE_DATASETS,
39959
+ label: rt.language === "ja" ? "\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8" : "Datasets"
39960
+ },
39961
+ {
39962
+ value: RESOURCE_REPORTS,
39963
+ label: rt.language === "ja" ? "\u30EC\u30DD\u30FC\u30C8" : "Reports"
39964
+ },
39965
+ {
39966
+ value: RESOURCE_DASHBOARDS,
39967
+ label: rt.language === "ja" ? "\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9" : "Dashboards"
39968
+ }
38997
39969
  ];
38998
39970
  },
38999
39971
  applyAnswer: (state, answer) => ({ ...state, resources: answer })
39972
+ },
39973
+ {
39974
+ slug: "datasets",
39975
+ type: "multiSelect",
39976
+ question: {
39977
+ 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",
39978
+ en: "Select the datasets you want to use (multi-select allowed)"
39979
+ },
39980
+ async fetchOptions(state, rt) {
39981
+ return fetchObjectOptions(
39982
+ state,
39983
+ RESOURCE_DATASETS,
39984
+ ALL_DATASETS,
39985
+ "\u3059\u3079\u3066\u306E\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8",
39986
+ "All datasets",
39987
+ rt
39988
+ );
39989
+ },
39990
+ applyAnswer: (state, answer) => ({ ...state, selectedDatasets: answer })
39991
+ },
39992
+ {
39993
+ slug: "reports",
39994
+ type: "multiSelect",
39995
+ question: {
39996
+ ja: "\u4F7F\u7528\u3059\u308B\u30EC\u30DD\u30FC\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
39997
+ en: "Select the reports you want to use (multi-select allowed)"
39998
+ },
39999
+ async fetchOptions(state, rt) {
40000
+ return fetchObjectOptions(
40001
+ state,
40002
+ RESOURCE_REPORTS,
40003
+ ALL_REPORTS,
40004
+ "\u3059\u3079\u3066\u306E\u30EC\u30DD\u30FC\u30C8",
40005
+ "All reports",
40006
+ rt
40007
+ );
40008
+ },
40009
+ applyAnswer: (state, answer) => ({ ...state, selectedReports: answer })
40010
+ },
40011
+ {
40012
+ slug: "dashboards",
40013
+ type: "multiSelect",
40014
+ question: {
40015
+ 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",
40016
+ en: "Select the dashboards you want to use (multi-select allowed)"
40017
+ },
40018
+ async fetchOptions(state, rt) {
40019
+ return fetchObjectOptions(
40020
+ state,
40021
+ RESOURCE_DASHBOARDS,
40022
+ ALL_DASHBOARDS,
40023
+ "\u3059\u3079\u3066\u306E\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9",
40024
+ "All dashboards",
40025
+ rt
40026
+ );
40027
+ },
40028
+ applyAnswer: (state, answer) => ({
40029
+ ...state,
40030
+ selectedDashboards: answer
40031
+ })
39000
40032
  }
39001
40033
  ],
39002
40034
  async finalize(state, rt) {
@@ -39005,37 +40037,119 @@ var powerbiOauthSetupFlow = {
39005
40037
  }
39006
40038
  const allGroups = await listGroups(rt.config.proxyFetch);
39007
40039
  const groupById = new Map(allGroups.map((g) => [g.id, g]));
39008
- const targetIds = await resolveSetupSelection({
39009
- selected: state.workspaces,
39010
- allSentinel: ALL_WORKSPACES,
39011
- fetchAll: async () => allGroups.map((g) => g.id).filter((id) => id),
39012
- limit: POWERBI_SETUP_MAX_WORKSPACES
39013
- });
40040
+ const wsIds = await resolveWorkspaceIds(
40041
+ state.workspaces,
40042
+ rt.config.proxyFetch
40043
+ );
39014
40044
  const selectedResources = new Set(state.resources);
40045
+ async function resolveObjects(selected, allSentinel, resource) {
40046
+ const byWorkspace = /* @__PURE__ */ new Map();
40047
+ if (!selected || !selectedResources.has(resource)) return byWorkspace;
40048
+ if (selected.includes(allSentinel)) {
40049
+ for (const wsId of wsIds) {
40050
+ const items = await listResource(
40051
+ rt.config.proxyFetch,
40052
+ wsId,
40053
+ resource
40054
+ );
40055
+ const ids = items.map((i) => i.id).filter((id) => !!id);
40056
+ if (ids.length > 0) byWorkspace.set(wsId, new Set(ids));
40057
+ }
40058
+ } else {
40059
+ for (const key of selected) {
40060
+ if (key === allSentinel) continue;
40061
+ const { wsId, objectId } = parseCompoundKey(key);
40062
+ if (!byWorkspace.has(wsId)) byWorkspace.set(wsId, /* @__PURE__ */ new Set());
40063
+ byWorkspace.get(wsId).add(objectId);
40064
+ }
40065
+ }
40066
+ return byWorkspace;
40067
+ }
40068
+ const datasetsByWs = await resolveObjects(
40069
+ state.selectedDatasets,
40070
+ ALL_DATASETS,
40071
+ RESOURCE_DATASETS
40072
+ );
40073
+ const reportsByWs = await resolveObjects(
40074
+ state.selectedReports,
40075
+ ALL_REPORTS,
40076
+ RESOURCE_REPORTS
40077
+ );
40078
+ const dashboardsByWs = await resolveObjects(
40079
+ state.selectedDashboards,
40080
+ ALL_DASHBOARDS,
40081
+ RESOURCE_DASHBOARDS
40082
+ );
40083
+ const allWsIds = /* @__PURE__ */ new Set([
40084
+ ...datasetsByWs.keys(),
40085
+ ...reportsByWs.keys(),
40086
+ ...dashboardsByWs.keys()
40087
+ ]);
39015
40088
  const sections = ["## Power BI", ""];
39016
- if (!targetIds.length) {
39017
- sections.push("_No workspaces selected._", "");
40089
+ if (allWsIds.size === 0) {
40090
+ sections.push("_No resources selected._", "");
39018
40091
  return sections.join("\n");
39019
40092
  }
39020
- for (const id of targetIds) {
39021
- const group = groupById.get(id);
39022
- const name = group?.name ?? id;
39023
- sections.push(`### Workspace: ${name}`, "", `- id: \`${id}\``);
39024
- for (const resource of [
39025
- RESOURCE_DATASETS,
39026
- RESOURCE_REPORTS,
39027
- RESOURCE_DASHBOARDS
40093
+ for (const wsId of wsIds) {
40094
+ if (!allWsIds.has(wsId)) continue;
40095
+ const name = workspaceName(wsId, groupById, rt.language);
40096
+ sections.push(`### Workspace: ${name}`, "");
40097
+ if (wsId !== MY_WORKSPACE) {
40098
+ sections.push(`- id: \`${wsId}\``);
40099
+ }
40100
+ const datasetIds = datasetsByWs.get(wsId);
40101
+ if (datasetIds?.size) {
40102
+ const items = await listResource(
40103
+ rt.config.proxyFetch,
40104
+ wsId,
40105
+ RESOURCE_DATASETS
40106
+ );
40107
+ const filtered = items.filter(
40108
+ (item) => item.id && datasetIds.has(item.id)
40109
+ );
40110
+ for (const item of filtered.slice(0, RESOURCE_DISPLAY_LIMIT)) {
40111
+ sections.push(`#### Dataset: ${resourceLabel(item)}`, "");
40112
+ const schema = await fetchDatasetSchema(
40113
+ rt.config.proxyFetch,
40114
+ wsId,
40115
+ item.id
40116
+ );
40117
+ if (schema.size > 0) {
40118
+ for (const [table, columns] of schema) {
40119
+ sections.push(`##### Table: ${table}`, "");
40120
+ sections.push("| Column |");
40121
+ sections.push("|--------|");
40122
+ for (const col of columns) {
40123
+ sections.push(`| ${col} |`);
40124
+ }
40125
+ sections.push("");
40126
+ }
40127
+ } else {
40128
+ sections.push("_Schema not available._", "");
40129
+ }
40130
+ }
40131
+ }
40132
+ for (const [resource, selectedByWs, heading] of [
40133
+ [RESOURCE_REPORTS, reportsByWs, "Reports"],
40134
+ [RESOURCE_DASHBOARDS, dashboardsByWs, "Dashboards"]
39028
40135
  ]) {
39029
- if (!selectedResources.has(resource)) continue;
39030
- const items = await listResource(rt.config.proxyFetch, id, resource);
39031
- const heading = resource === RESOURCE_DATASETS ? "Datasets" : resource === RESOURCE_REPORTS ? "Reports" : "Dashboards";
39032
- sections.push(`- ${heading} (${items.length}):`);
39033
- for (const item of items.slice(0, RESOURCE_DISPLAY_LIMIT)) {
40136
+ const selectedIds = selectedByWs.get(wsId);
40137
+ if (!selectedIds?.size) continue;
40138
+ const items = await listResource(
40139
+ rt.config.proxyFetch,
40140
+ wsId,
40141
+ resource
40142
+ );
40143
+ const filtered = items.filter(
40144
+ (item) => item.id && selectedIds.has(item.id)
40145
+ );
40146
+ sections.push(`- ${heading} (${filtered.length}):`);
40147
+ for (const item of filtered.slice(0, RESOURCE_DISPLAY_LIMIT)) {
39034
40148
  sections.push(` - ${resourceLabel(item)}`);
39035
40149
  }
39036
- if (items.length > RESOURCE_DISPLAY_LIMIT) {
40150
+ if (filtered.length > RESOURCE_DISPLAY_LIMIT) {
39037
40151
  sections.push(
39038
- ` - \u2026and ${items.length - RESOURCE_DISPLAY_LIMIT} more`
40152
+ ` - \u2026and ${filtered.length - RESOURCE_DISPLAY_LIMIT} more`
39039
40153
  );
39040
40154
  }
39041
40155
  }
@@ -39306,7 +40420,11 @@ async function tableauProxyApiFetch(proxyFetch, params, path5, init) {
39306
40420
 
39307
40421
  // ../connectors/src/connectors/tableau/setup-flow.ts
39308
40422
  var ALL_PROJECTS7 = "__ALL_PROJECTS__";
40423
+ var ALL_WORKBOOKS = "__ALL_WORKBOOKS__";
39309
40424
  var TABLEAU_SETUP_MAX_PROJECTS = 10;
40425
+ var MAX_WORKBOOKS_DETAIL = 10;
40426
+ var MAX_VIEWS_PER_WORKBOOK = 5;
40427
+ var MAX_SAMPLE_ROWS = 5;
39310
40428
  var PAGE_SIZE2 = 100;
39311
40429
  async function listAllProjects3(rt) {
39312
40430
  const all = [];
@@ -39355,6 +40473,57 @@ async function listProjectResources(rt, resource) {
39355
40473
  }
39356
40474
  return all;
39357
40475
  }
40476
+ async function listViewsForWorkbook(rt, workbookId) {
40477
+ const res = await tableauProxyApiFetch(
40478
+ rt.config.proxyFetch,
40479
+ rt.params,
40480
+ `/sites/{siteId}/workbooks/${encodeURIComponent(workbookId)}/views`
40481
+ );
40482
+ if (!res.ok) return [];
40483
+ const data = await res.json();
40484
+ return data.views?.view ?? [];
40485
+ }
40486
+ async function fetchViewDataSample(rt, viewId) {
40487
+ try {
40488
+ const res = await tableauProxyApiFetch(
40489
+ rt.config.proxyFetch,
40490
+ rt.params,
40491
+ `/sites/{siteId}/views/${encodeURIComponent(viewId)}/data`
40492
+ );
40493
+ if (!res.ok) return null;
40494
+ const csv = await res.text();
40495
+ const lines = csv.trim().split("\n").filter(Boolean);
40496
+ if (lines.length === 0) return null;
40497
+ const columns = parseCsvLine(lines[0]);
40498
+ const rows = lines.slice(1, 1 + MAX_SAMPLE_ROWS).map(parseCsvLine);
40499
+ return { columns, rows };
40500
+ } catch {
40501
+ return null;
40502
+ }
40503
+ }
40504
+ function parseCsvLine(line) {
40505
+ const result = [];
40506
+ let current = "";
40507
+ let inQuotes = false;
40508
+ for (let i = 0; i < line.length; i++) {
40509
+ const ch = line[i];
40510
+ if (ch === '"') {
40511
+ if (inQuotes && line[i + 1] === '"') {
40512
+ current += '"';
40513
+ i++;
40514
+ } else {
40515
+ inQuotes = !inQuotes;
40516
+ }
40517
+ } else if (ch === "," && !inQuotes) {
40518
+ result.push(current.trim());
40519
+ current = "";
40520
+ } else {
40521
+ current += ch;
40522
+ }
40523
+ }
40524
+ result.push(current.trim());
40525
+ return result;
40526
+ }
39358
40527
  var tableauSetupFlow = {
39359
40528
  initialState: () => ({}),
39360
40529
  steps: [
@@ -39377,6 +40546,39 @@ var tableauSetupFlow = {
39377
40546
  ];
39378
40547
  },
39379
40548
  applyAnswer: (state, answer) => ({ ...state, projects: answer })
40549
+ },
40550
+ {
40551
+ slug: "workbooks",
40552
+ type: "multiSelect",
40553
+ question: {
40554
+ 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",
40555
+ en: "Select the workbooks to analyze (multi-select allowed)"
40556
+ },
40557
+ async fetchOptions(state, rt) {
40558
+ if (!state.projects?.length) return [];
40559
+ const allProjects = await listAllProjects3(rt);
40560
+ const targetIds = await resolveSetupSelection({
40561
+ selected: state.projects,
40562
+ allSentinel: ALL_PROJECTS7,
40563
+ fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
40564
+ limit: TABLEAU_SETUP_MAX_PROJECTS
40565
+ });
40566
+ const targetSet = new Set(targetIds);
40567
+ const workbooks = await listProjectResources(rt, "workbooks");
40568
+ const options = workbooks.filter((wb) => wb.id && wb.project?.id && targetSet.has(wb.project.id)).map((wb) => ({
40569
+ value: wb.id,
40570
+ label: wb.name ?? wb.id ?? "(unknown)"
40571
+ }));
40572
+ if (options.length === 0) return [];
40573
+ return [
40574
+ {
40575
+ value: ALL_WORKBOOKS,
40576
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF" : "All workbooks"
40577
+ },
40578
+ ...options
40579
+ ];
40580
+ },
40581
+ applyAnswer: (state, answer) => ({ ...state, workbooks: answer })
39380
40582
  }
39381
40583
  ],
39382
40584
  async finalize(state, rt) {
@@ -39385,25 +40587,33 @@ var tableauSetupFlow = {
39385
40587
  }
39386
40588
  const allProjects = await listAllProjects3(rt);
39387
40589
  const projectById = new Map(allProjects.map((p) => [p.id, p]));
39388
- const targetIds = await resolveSetupSelection({
40590
+ const targetProjectIds = await resolveSetupSelection({
39389
40591
  selected: state.projects,
39390
40592
  allSentinel: ALL_PROJECTS7,
39391
40593
  fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
39392
40594
  limit: TABLEAU_SETUP_MAX_PROJECTS
39393
40595
  });
39394
40596
  const sections = ["## Tableau", ""];
39395
- if (!targetIds.length) {
40597
+ if (!targetProjectIds.length) {
39396
40598
  sections.push("_No projects selected._", "");
39397
40599
  return sections.join("\n");
39398
40600
  }
39399
- const [workbooks, datasources] = await Promise.all([
40601
+ const [allWorkbooks, datasources] = await Promise.all([
39400
40602
  listProjectResources(rt, "workbooks"),
39401
40603
  listProjectResources(rt, "datasources")
39402
40604
  ]);
40605
+ const projectIdSet = new Set(targetProjectIds);
40606
+ const targetWorkbooks = await resolveSetupSelection({
40607
+ selected: state.workbooks ?? [],
40608
+ allSentinel: ALL_WORKBOOKS,
40609
+ fetchAll: async () => allWorkbooks.filter((wb) => wb.id && wb.project?.id && projectIdSet.has(wb.project.id)).map((wb) => wb.id).filter(Boolean),
40610
+ limit: MAX_WORKBOOKS_DETAIL
40611
+ });
40612
+ const targetWorkbookSet = new Set(targetWorkbooks);
39403
40613
  const workbooksByProject = /* @__PURE__ */ new Map();
39404
- for (const wb of workbooks) {
40614
+ for (const wb of allWorkbooks) {
39405
40615
  const pid = wb.project?.id;
39406
- if (!pid) continue;
40616
+ if (!pid || !projectIdSet.has(pid)) continue;
39407
40617
  const bucket = workbooksByProject.get(pid) ?? [];
39408
40618
  bucket.push(wb);
39409
40619
  workbooksByProject.set(pid, bucket);
@@ -39411,32 +40621,78 @@ var tableauSetupFlow = {
39411
40621
  const datasourcesByProject = /* @__PURE__ */ new Map();
39412
40622
  for (const ds of datasources) {
39413
40623
  const pid = ds.project?.id;
39414
- if (!pid) continue;
40624
+ if (!pid || !projectIdSet.has(pid)) continue;
39415
40625
  const bucket = datasourcesByProject.get(pid) ?? [];
39416
40626
  bucket.push(ds);
39417
40627
  datasourcesByProject.set(pid, bucket);
39418
40628
  }
39419
- for (const id of targetIds) {
39420
- const project = projectById.get(id);
39421
- const name = project?.name ?? id;
39422
- sections.push(`### Project: ${name}`, "", `- id: \`${id}\``);
39423
- const projectWorkbooks = workbooksByProject.get(id) ?? [];
39424
- sections.push(`- Workbooks (${projectWorkbooks.length}):`);
39425
- for (const wb of projectWorkbooks.slice(0, 25)) {
39426
- sections.push(` - ${wb.name ?? wb.id ?? "(unknown)"}`);
39427
- }
39428
- if (projectWorkbooks.length > 25) {
39429
- sections.push(` - \u2026and ${projectWorkbooks.length - 25} more`);
40629
+ const allDiscoveredColumns = [];
40630
+ for (const pid of targetProjectIds) {
40631
+ const project = projectById.get(pid);
40632
+ sections.push(`### Project: ${project?.name ?? pid}`, "");
40633
+ const projectWorkbooks = workbooksByProject.get(pid) ?? [];
40634
+ for (const wb of projectWorkbooks) {
40635
+ if (!wb.id) continue;
40636
+ const isDetailed = targetWorkbookSet.has(wb.id);
40637
+ sections.push(`#### Workbook: ${wb.name ?? wb.id}`, "");
40638
+ if (!isDetailed) continue;
40639
+ const views = await listViewsForWorkbook(rt, wb.id);
40640
+ if (views.length === 0) {
40641
+ sections.push("_No views found._", "");
40642
+ continue;
40643
+ }
40644
+ for (const view of views.slice(0, MAX_VIEWS_PER_WORKBOOK)) {
40645
+ if (!view.id) continue;
40646
+ sections.push(`##### View: ${view.name ?? view.id}`, "");
40647
+ const sample = await fetchViewDataSample(rt, view.id);
40648
+ if (!sample || sample.columns.length === 0) {
40649
+ sections.push("_Data not available._", "");
40650
+ continue;
40651
+ }
40652
+ allDiscoveredColumns.push(...sample.columns);
40653
+ sections.push(`| ${sample.columns.join(" | ")} |`);
40654
+ sections.push(`|${sample.columns.map(() => "---").join("|")}|`);
40655
+ for (const row of sample.rows) {
40656
+ const cells = row.map((c) => c.replace(/\|/g, "\\|"));
40657
+ sections.push(`| ${cells.join(" | ")} |`);
40658
+ }
40659
+ sections.push("");
40660
+ }
39430
40661
  }
39431
- const projectDatasources = datasourcesByProject.get(id) ?? [];
39432
- sections.push(`- Datasources (${projectDatasources.length}):`);
39433
- for (const ds of projectDatasources.slice(0, 25)) {
39434
- sections.push(` - ${ds.name ?? ds.id ?? "(unknown)"}`);
40662
+ const projectDatasources = datasourcesByProject.get(pid) ?? [];
40663
+ if (projectDatasources.length > 0) {
40664
+ sections.push(`#### Datasources (${projectDatasources.length})`, "");
40665
+ for (const ds of projectDatasources.slice(0, 25)) {
40666
+ sections.push(`- ${ds.name ?? ds.id ?? "(unknown)"}`);
40667
+ }
40668
+ sections.push("");
39435
40669
  }
39436
- if (projectDatasources.length > 25) {
39437
- sections.push(` - \u2026and ${projectDatasources.length - 25} more`);
40670
+ }
40671
+ if (allDiscoveredColumns.length > 0) {
40672
+ const unique = [...new Set(allDiscoveredColumns.map((c) => c.toLowerCase()))];
40673
+ const themes = [];
40674
+ const hasPattern = (keywords) => unique.some((c) => keywords.some((k) => c.includes(k)));
40675
+ if (hasPattern(["revenue", "sales", "amount", "price", "cost", "profit", "\u58F2\u4E0A", "\u91D1\u984D", "\u5229\u76CA"]))
40676
+ themes.push("Revenue & profitability analysis (trends, breakdown by segment)");
40677
+ if (hasPattern(["date", "month", "year", "quarter", "day", "\u65E5\u4ED8", "\u6708", "\u5E74", "\u671F"]))
40678
+ themes.push("Time-series trend analysis (YoY, MoM comparisons)");
40679
+ if (hasPattern(["region", "country", "city", "state", "\u5730\u57DF", "\u56FD", "\u90FD\u5E02", "\u5E02"]))
40680
+ themes.push("Geographic/regional performance comparison");
40681
+ if (hasPattern(["product", "category", "item", "sku", "\u5546\u54C1", "\u30AB\u30C6\u30B4\u30EA"]))
40682
+ themes.push("Product/category mix analysis");
40683
+ if (hasPattern(["customer", "user", "account", "\u9867\u5BA2", "\u30E6\u30FC\u30B6\u30FC"]))
40684
+ themes.push("Customer segmentation and behavior analysis");
40685
+ if (hasPattern(["quantity", "count", "volume", "\u6570\u91CF", "\u4EF6\u6570"]))
40686
+ themes.push("Volume and demand pattern analysis");
40687
+ if (hasPattern(["rate", "ratio", "percentage", "%", "\u7387"]))
40688
+ themes.push("KPI ratio tracking and benchmarking");
40689
+ if (themes.length > 0) {
40690
+ sections.push("### Suggested Analysis Themes", "");
40691
+ for (const theme of themes) {
40692
+ sections.push(`- ${theme}`);
40693
+ }
40694
+ sections.push("");
39438
40695
  }
39439
- sections.push("");
39440
40696
  }
39441
40697
  return sections.join("\n");
39442
40698
  }
@@ -39446,27 +40702,36 @@ var tableauSetupFlow = {
39446
40702
  import { z as z98 } from "zod";
39447
40703
  var DEFAULT_API_VERSION2 = "3.28";
39448
40704
  var REQUEST_TIMEOUT_MS75 = 6e4;
39449
- async function fetchTableauSession(connectionId) {
39450
- const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
39451
- const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
39452
- if (!token || !sandboxId) {
39453
- throw new Error(
39454
- "Tableau session manager is not configured. Missing INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL or INTERNAL_SQUADBASE_SANDBOX_ID."
39455
- );
40705
+ var cachedToken33 = null;
40706
+ async function getProxyToken33(config) {
40707
+ if (cachedToken33 && cachedToken33.expiresAt > Date.now() + 6e4) {
40708
+ return cachedToken33.token;
39456
40709
  }
39457
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
39458
- const url = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
40710
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
39459
40711
  const res = await fetch(url, {
39460
40712
  method: "POST",
39461
- headers: { Authorization: `Bearer ${token}` }
40713
+ headers: {
40714
+ "Content-Type": "application/json",
40715
+ "x-api-key": config.appApiKey,
40716
+ "project-id": config.projectId
40717
+ },
40718
+ body: JSON.stringify({
40719
+ sandboxId: config.sandboxId,
40720
+ issuedBy: "coding-agent"
40721
+ })
39462
40722
  });
39463
40723
  if (!res.ok) {
39464
- const errBody = await res.text().catch(() => res.statusText);
40724
+ const errorText = await res.text().catch(() => res.statusText);
39465
40725
  throw new Error(
39466
- `Failed to fetch Tableau session from backend-api (HTTP ${res.status}): ${errBody}`
40726
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
39467
40727
  );
39468
40728
  }
39469
- return await res.json();
40729
+ const data = await res.json();
40730
+ cachedToken33 = {
40731
+ token: data.token,
40732
+ expiresAt: new Date(data.expiresAt).getTime()
40733
+ };
40734
+ return data.token;
39470
40735
  }
39471
40736
  function buildBaseUrl7(serverUrl, apiVersion) {
39472
40737
  return `${serverUrl.replace(/\/$/, "")}/api/${apiVersion}`;
@@ -39508,7 +40773,7 @@ All paths are relative to {serverUrl}/api/{apiVersion}. Use the literal placehol
39508
40773
  Accept and Content-Type headers default to application/json so list responses come back as JSON instead of Tableau's default XML.`,
39509
40774
  inputSchema: inputSchema98,
39510
40775
  outputSchema: outputSchema98,
39511
- async execute({ connectionId, method, path: path5, queryParams, body }, connections) {
40776
+ async execute({ connectionId, method, path: path5, queryParams, body }, connections, config) {
39512
40777
  const connection = connections.find((c) => c.id === connectionId);
39513
40778
  if (!connection) {
39514
40779
  return {
@@ -39523,50 +40788,42 @@ Accept and Content-Type headers default to application/json so list responses co
39523
40788
  const serverUrl = parameters81.serverUrl.getValue(connection);
39524
40789
  const apiVersion = parameters81.apiVersion.tryGetValue(connection) || DEFAULT_API_VERSION2;
39525
40790
  const trimmedPath = path5.trim().replace(/^([^/])/, "/$1");
39526
- const queryString = queryParams ? `?${new URLSearchParams(queryParams).toString()}` : "";
39527
40791
  const baseUrl = buildBaseUrl7(serverUrl, apiVersion);
40792
+ let url = `${baseUrl}${trimmedPath}`;
40793
+ if (queryParams) {
40794
+ url += `?${new URLSearchParams(queryParams).toString()}`;
40795
+ }
40796
+ const token = await getProxyToken33(config.oauthProxy);
40797
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
39528
40798
  const controller = new AbortController();
39529
40799
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS75);
39530
40800
  try {
39531
- let attempt = 0;
39532
- while (true) {
39533
- const session = await fetchTableauSession(connectionId);
39534
- const resolvedPath = trimmedPath.replace(
39535
- /\{siteId\}/g,
39536
- session.siteId
39537
- );
39538
- const url = `${baseUrl}${resolvedPath}${queryString}`;
39539
- const init = {
40801
+ const response = await fetch(proxyUrl, {
40802
+ method: "POST",
40803
+ headers: {
40804
+ "Content-Type": "application/json",
40805
+ Authorization: `Bearer ${token}`
40806
+ },
40807
+ body: JSON.stringify({
40808
+ url,
39540
40809
  method,
39541
- headers: {
39542
- "X-Tableau-Auth": session.authToken,
39543
- Accept: "application/json",
39544
- "Content-Type": "application/json"
39545
- },
39546
- signal: controller.signal
39547
- };
39548
- if (body !== void 0) {
39549
- init.body = JSON.stringify(body);
39550
- }
39551
- const response = await fetch(url, init);
39552
- const text = await response.text();
39553
- const data = text ? (() => {
39554
- try {
39555
- return JSON.parse(text);
39556
- } catch {
39557
- return text;
39558
- }
39559
- })() : null;
39560
- if (response.status === 401 && attempt === 0) {
39561
- attempt++;
39562
- continue;
39563
- }
39564
- if (!response.ok) {
39565
- const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
39566
- return { success: false, error: errorMessage };
40810
+ ...body !== void 0 ? { body } : {}
40811
+ }),
40812
+ signal: controller.signal
40813
+ });
40814
+ const text = await response.text();
40815
+ const data = text ? (() => {
40816
+ try {
40817
+ return JSON.parse(text);
40818
+ } catch {
40819
+ return text;
39567
40820
  }
39568
- return { success: true, status: response.status, data };
40821
+ })() : null;
40822
+ if (!response.ok) {
40823
+ const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
40824
+ return { success: false, error: errorMessage };
39569
40825
  }
40826
+ return { success: true, status: response.status, data };
39570
40827
  } finally {
39571
40828
  clearTimeout(timeout);
39572
40829
  }
@@ -39758,12 +41015,12 @@ export default async function handler(c: Context) {
39758
41015
  import { z as z99 } from "zod";
39759
41016
  var BASE_HOST13 = "https://graph.microsoft.com";
39760
41017
  var BASE_PATH_SEGMENT19 = "/v1.0";
39761
- var BASE_URL65 = `${BASE_HOST13}${BASE_PATH_SEGMENT19}`;
41018
+ var BASE_URL66 = `${BASE_HOST13}${BASE_PATH_SEGMENT19}`;
39762
41019
  var REQUEST_TIMEOUT_MS76 = 6e4;
39763
- var cachedToken33 = null;
39764
- async function getProxyToken33(config) {
39765
- if (cachedToken33 && cachedToken33.expiresAt > Date.now() + 6e4) {
39766
- return cachedToken33.token;
41020
+ var cachedToken34 = null;
41021
+ async function getProxyToken34(config) {
41022
+ if (cachedToken34 && cachedToken34.expiresAt > Date.now() + 6e4) {
41023
+ return cachedToken34.token;
39767
41024
  }
39768
41025
  const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
39769
41026
  const res = await fetch(url, {
@@ -39785,7 +41042,7 @@ async function getProxyToken33(config) {
39785
41042
  );
39786
41043
  }
39787
41044
  const data = await res.json();
39788
- cachedToken33 = {
41045
+ cachedToken34 = {
39789
41046
  token: data.token,
39790
41047
  expiresAt: new Date(data.expiresAt).getTime()
39791
41048
  };
@@ -39837,12 +41094,12 @@ For full-text search use the \`$search\` query parameter (must be wrapped in dou
39837
41094
  );
39838
41095
  try {
39839
41096
  const normalizedPath = normalizeRequestPath(path5, BASE_PATH_SEGMENT19);
39840
- let url = `${BASE_URL65}${normalizedPath}`;
41097
+ let url = `${BASE_URL66}${normalizedPath}`;
39841
41098
  if (queryParams) {
39842
41099
  const searchParams = new URLSearchParams(queryParams);
39843
41100
  url += `?${searchParams.toString()}`;
39844
41101
  }
39845
- const token = await getProxyToken33(config.oauthProxy);
41102
+ const token = await getProxyToken34(config.oauthProxy);
39846
41103
  const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
39847
41104
  const controller = new AbortController();
39848
41105
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS76);