@squadbase/vite-server 0.1.12-dev.a9ac647 → 0.1.17-dev.24af54e

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 (77) hide show
  1. package/dist/cli/index.js +12374 -883
  2. package/dist/connectors/airtable-oauth.js +257 -46
  3. package/dist/connectors/airtable.js +294 -51
  4. package/dist/connectors/amplitude.js +297 -47
  5. package/dist/connectors/anthropic.js +135 -47
  6. package/dist/connectors/asana.js +302 -49
  7. package/dist/connectors/attio.js +277 -49
  8. package/dist/connectors/aws-billing.js +262 -46
  9. package/dist/connectors/azure-sql.js +396 -102
  10. package/dist/connectors/backlog-api-key.js +292 -47
  11. package/dist/connectors/clickup.js +313 -49
  12. package/dist/connectors/cosmosdb.js +280 -50
  13. package/dist/connectors/customerio.js +294 -47
  14. package/dist/connectors/dbt.js +315 -47
  15. package/dist/connectors/freshdesk.js +317 -53
  16. package/dist/connectors/freshsales.js +308 -52
  17. package/dist/connectors/freshservice.js +336 -53
  18. package/dist/connectors/gamma.js +302 -52
  19. package/dist/connectors/gemini.js +134 -47
  20. package/dist/connectors/github.js +361 -49
  21. package/dist/connectors/gmail-oauth.js +179 -7
  22. package/dist/connectors/gmail.js +325 -47
  23. package/dist/connectors/google-ads.js +263 -46
  24. package/dist/connectors/google-analytics-oauth.js +285 -46
  25. package/dist/connectors/google-analytics.js +387 -49
  26. package/dist/connectors/google-audit-log.js +413 -47
  27. package/dist/connectors/google-calendar-oauth.js +234 -46
  28. package/dist/connectors/google-calendar.js +334 -47
  29. package/dist/connectors/google-docs.js +195 -6
  30. package/dist/connectors/google-drive.js +237 -5
  31. package/dist/connectors/google-search-console-oauth.js +231 -46
  32. package/dist/connectors/google-sheets.js +247 -47
  33. package/dist/connectors/google-slides.js +180 -6
  34. package/dist/connectors/grafana.js +307 -49
  35. package/dist/connectors/hubspot-oauth.js +183 -5
  36. package/dist/connectors/hubspot.js +281 -49
  37. package/dist/connectors/influxdb.js +391 -51
  38. package/dist/connectors/intercom-oauth.js +185 -5
  39. package/dist/connectors/intercom.js +277 -49
  40. package/dist/connectors/jdbc.js +737 -110
  41. package/dist/connectors/jira-api-key.js +301 -47
  42. package/dist/connectors/kintone-api-token.js +256 -47
  43. package/dist/connectors/kintone.js +303 -47
  44. package/dist/connectors/linear.js +305 -49
  45. package/dist/connectors/linkedin-ads.js +243 -50
  46. package/dist/connectors/mailchimp-oauth.js +243 -46
  47. package/dist/connectors/mailchimp.js +295 -49
  48. package/dist/connectors/meta-ads-oauth.js +248 -48
  49. package/dist/connectors/meta-ads.js +260 -50
  50. package/dist/connectors/mixpanel.js +313 -47
  51. package/dist/connectors/monday.js +335 -49
  52. package/dist/connectors/mongodb.js +294 -57
  53. package/dist/connectors/notion-oauth.js +206 -5
  54. package/dist/connectors/notion.js +298 -51
  55. package/dist/connectors/openai.js +134 -47
  56. package/dist/connectors/oracle.js +414 -103
  57. package/dist/connectors/outlook-oauth.js +179 -5
  58. package/dist/connectors/powerbi-oauth.js +226 -5
  59. package/dist/connectors/salesforce.js +359 -49
  60. package/dist/connectors/semrush.js +289 -49
  61. package/dist/connectors/sentry.js +264 -50
  62. package/dist/connectors/shopify-oauth.js +162 -5
  63. package/dist/connectors/shopify.js +332 -47
  64. package/dist/connectors/sqlserver.js +390 -102
  65. package/dist/connectors/stripe-api-key.js +244 -46
  66. package/dist/connectors/stripe-oauth.js +177 -5
  67. package/dist/connectors/supabase.js +278 -48
  68. package/dist/connectors/tableau.js +389 -184
  69. package/dist/connectors/tiktok-ads.js +254 -48
  70. package/dist/connectors/wix-store.js +295 -49
  71. package/dist/connectors/zendesk-oauth.js +214 -5
  72. package/dist/connectors/zendesk.js +333 -47
  73. package/dist/index.d.ts +149 -1
  74. package/dist/index.js +13677 -1969
  75. package/dist/main.js +13627 -1927
  76. package/dist/vite-plugin.js +12391 -890
  77. package/package.json +1 -1
@@ -185,6 +185,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
185
185
  tools;
186
186
  query;
187
187
  checkConnection;
188
+ /**
189
+ * SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
190
+ * implement this expose a step-by-step exploration flow (database/schema/
191
+ * table/etc. discovery) that the dashboard backend drives via the
192
+ * `/connections/:connectionId/setup` endpoint. Implement by delegating to
193
+ * `runSetupFlow` from `setup-flow.ts`.
194
+ */
195
+ setup;
196
+ /**
197
+ * Opt-out of the default "verify before save" behavior on connection
198
+ * creation. The backend invokes `checkConnection` synchronously while
199
+ * creating the connection and aborts (no row inserted) if it fails — this
200
+ * flag disables that for connectors where the check cannot succeed pre-save:
201
+ *
202
+ * - `squadbase-db` populates `connection-url` only after Neon provisioning
203
+ * - OAuth connectors require an OAuth-aware proxyFetch keyed by the
204
+ * connectionId, which doesn't exist until the row is saved
205
+ *
206
+ * Exceptions are the explicit position; new credential-input connectors get
207
+ * the default verify-on-create behavior without opt-in.
208
+ */
209
+ skipConnectionCheckOnCreate;
188
210
  constructor(config) {
189
211
  this.slug = config.slug;
190
212
  this.authType = config.authType;
@@ -201,6 +223,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
201
223
  this.tools = config.tools;
202
224
  this.query = config.query;
203
225
  this.checkConnection = config.checkConnection;
226
+ this.setup = config.setup;
227
+ this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
204
228
  }
205
229
  get connectorKey() {
206
230
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -265,6 +289,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
265
289
  }
266
290
  };
267
291
 
292
+ // ../connectors/src/setup-flow.ts
293
+ async function runSetupFlow(flow, params, ctx, config) {
294
+ const runtime = {
295
+ params,
296
+ language: ctx.language,
297
+ config
298
+ };
299
+ let state = flow.initialState();
300
+ let answerIdx = 0;
301
+ for (const step of flow.steps) {
302
+ const ans = ctx.answers[answerIdx];
303
+ if (ans && ans.questionSlug === step.slug) {
304
+ state = step.applyAnswer(state, ans.answer);
305
+ answerIdx += 1;
306
+ continue;
307
+ }
308
+ if (step.type === "text") {
309
+ return {
310
+ type: "nextQuestion",
311
+ questionSlug: step.slug,
312
+ question: step.question[ctx.language],
313
+ questionType: "text"
314
+ };
315
+ }
316
+ const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
317
+ if (options.length === 0) {
318
+ continue;
319
+ }
320
+ return {
321
+ type: "nextQuestion",
322
+ questionSlug: step.slug,
323
+ question: step.question[ctx.language],
324
+ questionType: step.type,
325
+ options
326
+ };
327
+ }
328
+ const dataInvestigationResult = await flow.finalize(state, runtime);
329
+ return { type: "fulfilled", dataInvestigationResult };
330
+ }
331
+ async function resolveSetupSelection(params) {
332
+ const { selected, allSentinel, fetchAll, limit } = params;
333
+ const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
334
+ return resolved.slice(0, limit);
335
+ }
336
+
268
337
  // ../connectors/src/auth-types.ts
269
338
  var AUTH_TYPES = {
270
339
  OAUTH: "oauth",
@@ -463,6 +532,89 @@ Calendar
463
532
  }
464
533
  });
465
534
 
535
+ // ../connectors/src/connectors/outlook-oauth/utils.ts
536
+ async function graphApiFetch(config, url) {
537
+ const res = await config.proxyFetch(url, { method: "GET" });
538
+ if (!res.ok) {
539
+ const text = await res.text().catch(() => res.statusText);
540
+ throw new Error(`Microsoft Graph ${url} failed: HTTP ${res.status} ${text}`);
541
+ }
542
+ return await res.json();
543
+ }
544
+
545
+ // ../connectors/src/connectors/outlook-oauth/setup-flow.ts
546
+ var ALL_FOLDERS = "__ALL_FOLDERS__";
547
+ var OUTLOOK_SETUP_MAX_FOLDERS = 20;
548
+ async function listMailFolders(config) {
549
+ const url = "https://graph.microsoft.com/v1.0/me/mailFolders?$top=100&$select=id,displayName,parentFolderId,childFolderCount,unreadItemCount,totalItemCount";
550
+ const data = await graphApiFetch(config, url);
551
+ return data.value ?? [];
552
+ }
553
+ var outlookOauthSetupFlow = {
554
+ initialState: () => ({}),
555
+ steps: [
556
+ {
557
+ slug: "folders",
558
+ type: "multiSelect",
559
+ question: {
560
+ ja: "\u5BFE\u8C61\u306E\u30E1\u30FC\u30EB\u30D5\u30A9\u30EB\u30C0\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
561
+ en: "Select target mail folders (multi-select allowed)"
562
+ },
563
+ async fetchOptions(_state, rt) {
564
+ const folders = await listMailFolders(rt.config);
565
+ return [
566
+ {
567
+ value: ALL_FOLDERS,
568
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D5\u30A9\u30EB\u30C0" : "All folders"
569
+ },
570
+ ...folders.map((f) => ({
571
+ value: f.id,
572
+ label: f.displayName ?? f.id
573
+ }))
574
+ ];
575
+ },
576
+ applyAnswer: (state, answer) => ({ ...state, folders: answer })
577
+ }
578
+ ],
579
+ async finalize(state, rt) {
580
+ if (!state.folders) {
581
+ throw new Error("Outlook setup: incomplete state on finalize");
582
+ }
583
+ const folders = await listMailFolders(rt.config);
584
+ const byId = new Map(folders.map((f) => [f.id, f]));
585
+ const targetIds = await resolveSetupSelection({
586
+ selected: state.folders,
587
+ allSentinel: ALL_FOLDERS,
588
+ fetchAll: async () => folders.map((f) => f.id),
589
+ limit: OUTLOOK_SETUP_MAX_FOLDERS
590
+ });
591
+ const sections = ["## Outlook", ""];
592
+ if (targetIds.length === 0) {
593
+ sections.push(
594
+ rt.language === "ja" ? "\u5BFE\u8C61\u306E\u30D5\u30A9\u30EB\u30C0\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002" : "No folders selected."
595
+ );
596
+ return sections.join("\n");
597
+ }
598
+ sections.push("| Folder | Total | Unread | Sub-folders |");
599
+ sections.push("|--------|-------|--------|-------------|");
600
+ for (const id of targetIds) {
601
+ const f = byId.get(id);
602
+ if (!f) {
603
+ sections.push(`| ${id} | - | - | - |`);
604
+ continue;
605
+ }
606
+ const total = f.totalItemCount ?? "-";
607
+ const unread = f.unreadItemCount ?? "-";
608
+ const subs = f.childFolderCount ?? 0;
609
+ sections.push(
610
+ `| ${f.displayName ?? id} | ${total} | ${unread} | ${subs} |`
611
+ );
612
+ }
613
+ sections.push("");
614
+ return sections.join("\n");
615
+ }
616
+ };
617
+
466
618
  // ../connectors/src/connectors/outlook-oauth/parameters.ts
467
619
  var parameters = {};
468
620
 
@@ -471,6 +623,7 @@ var tools = { request: requestTool };
471
623
  var outlookOauthConnector = new ConnectorPlugin({
472
624
  slug: "outlook",
473
625
  authType: AUTH_TYPES.OAUTH,
626
+ skipConnectionCheckOnCreate: true,
474
627
  name: "Outlook",
475
628
  description: "Connect to Microsoft Outlook (Mail + Calendar) via Microsoft Graph using OAuth. Read-only access to the user's mailbox, mail folders, messages, attachments, calendars, and events.",
476
629
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1J1FrRTYJjOh3CcSIqsz3I/6a467b4d926075ff99dc60820e0ae4b1/Microsoft_Outlook_Icon__2025%C3%A2__present_.svg",
@@ -755,6 +908,7 @@ events.value.forEach(e => console.log(e.start.dateTime, e.subject));
755
908
  \`\`\``
756
909
  },
757
910
  tools,
911
+ setup: (params, ctx, config) => runSetupFlow(outlookOauthSetupFlow, params, ctx, config),
758
912
  async checkConnection(_params, config) {
759
913
  const { proxyFetch } = config;
760
914
  const url = "https://graph.microsoft.com/v1.0/me";
@@ -803,6 +957,7 @@ function resolveEnvVarOptional(entry, key) {
803
957
  import { getContext } from "hono/context-storage";
804
958
  import { getCookie } from "hono/cookie";
805
959
  var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
960
+ var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
806
961
  function normalizeHeaders(input) {
807
962
  const out = {};
808
963
  if (!input) return out;
@@ -811,6 +966,11 @@ function normalizeHeaders(input) {
811
966
  });
812
967
  return out;
813
968
  }
969
+ function extractInputUrl(input) {
970
+ if (typeof input === "string") return input;
971
+ if (input instanceof URL) return input.href;
972
+ return input.url;
973
+ }
814
974
  function createSandboxProxyFetch(connectionId) {
815
975
  return async (input, init) => {
816
976
  const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
@@ -820,10 +980,17 @@ function createSandboxProxyFetch(connectionId) {
820
980
  "Connection proxy is not configured. Please check your deployment settings."
821
981
  );
822
982
  }
823
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
983
+ const originalUrl = extractInputUrl(input);
984
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
985
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
986
+ const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
987
+ return fetch(sessionUrl, {
988
+ method: "POST",
989
+ headers: { Authorization: `Bearer ${token}` }
990
+ });
991
+ }
824
992
  const originalMethod = init?.method ?? "GET";
825
993
  const originalBody = init?.body ? JSON.parse(init.body) : void 0;
826
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
827
994
  const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
828
995
  return fetch(proxyUrl, {
829
996
  method: "POST",
@@ -849,10 +1016,9 @@ function createDeployedAppProxyFetch(connectionId) {
849
1016
  }
850
1017
  const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
851
1018
  const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
1019
+ const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
852
1020
  return async (input, init) => {
853
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
854
- const originalMethod = init?.method ?? "GET";
855
- const originalBody = init?.body ? JSON.parse(init.body) : void 0;
1021
+ const originalUrl = extractInputUrl(input);
856
1022
  const c = getContext();
857
1023
  const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
858
1024
  if (!appSession) {
@@ -860,6 +1026,14 @@ function createDeployedAppProxyFetch(connectionId) {
860
1026
  "No authentication method available for connection proxy."
861
1027
  );
862
1028
  }
1029
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
1030
+ return fetch(sessionUrl, {
1031
+ method: "POST",
1032
+ headers: { Authorization: `Bearer ${appSession}` }
1033
+ });
1034
+ }
1035
+ const originalMethod = init?.method ?? "GET";
1036
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
863
1037
  return fetch(proxyUrl, {
864
1038
  method: "POST",
865
1039
  headers: {
@@ -143,6 +143,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
143
143
  tools;
144
144
  query;
145
145
  checkConnection;
146
+ /**
147
+ * SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
148
+ * implement this expose a step-by-step exploration flow (database/schema/
149
+ * table/etc. discovery) that the dashboard backend drives via the
150
+ * `/connections/:connectionId/setup` endpoint. Implement by delegating to
151
+ * `runSetupFlow` from `setup-flow.ts`.
152
+ */
153
+ setup;
154
+ /**
155
+ * Opt-out of the default "verify before save" behavior on connection
156
+ * creation. The backend invokes `checkConnection` synchronously while
157
+ * creating the connection and aborts (no row inserted) if it fails — this
158
+ * flag disables that for connectors where the check cannot succeed pre-save:
159
+ *
160
+ * - `squadbase-db` populates `connection-url` only after Neon provisioning
161
+ * - OAuth connectors require an OAuth-aware proxyFetch keyed by the
162
+ * connectionId, which doesn't exist until the row is saved
163
+ *
164
+ * Exceptions are the explicit position; new credential-input connectors get
165
+ * the default verify-on-create behavior without opt-in.
166
+ */
167
+ skipConnectionCheckOnCreate;
146
168
  constructor(config) {
147
169
  this.slug = config.slug;
148
170
  this.authType = config.authType;
@@ -159,6 +181,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
159
181
  this.tools = config.tools;
160
182
  this.query = config.query;
161
183
  this.checkConnection = config.checkConnection;
184
+ this.setup = config.setup;
185
+ this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
162
186
  }
163
187
  get connectorKey() {
164
188
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -223,6 +247,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
223
247
  }
224
248
  };
225
249
 
250
+ // ../connectors/src/setup-flow.ts
251
+ async function runSetupFlow(flow, params, ctx, config) {
252
+ const runtime = {
253
+ params,
254
+ language: ctx.language,
255
+ config
256
+ };
257
+ let state = flow.initialState();
258
+ let answerIdx = 0;
259
+ for (const step of flow.steps) {
260
+ const ans = ctx.answers[answerIdx];
261
+ if (ans && ans.questionSlug === step.slug) {
262
+ state = step.applyAnswer(state, ans.answer);
263
+ answerIdx += 1;
264
+ continue;
265
+ }
266
+ if (step.type === "text") {
267
+ return {
268
+ type: "nextQuestion",
269
+ questionSlug: step.slug,
270
+ question: step.question[ctx.language],
271
+ questionType: "text"
272
+ };
273
+ }
274
+ const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
275
+ if (options.length === 0) {
276
+ continue;
277
+ }
278
+ return {
279
+ type: "nextQuestion",
280
+ questionSlug: step.slug,
281
+ question: step.question[ctx.language],
282
+ questionType: step.type,
283
+ options
284
+ };
285
+ }
286
+ const dataInvestigationResult = await flow.finalize(state, runtime);
287
+ return { type: "fulfilled", dataInvestigationResult };
288
+ }
289
+ async function resolveSetupSelection(params) {
290
+ const { selected, allSentinel, fetchAll, limit } = params;
291
+ const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
292
+ return resolved.slice(0, limit);
293
+ }
294
+
226
295
  // ../connectors/src/auth-types.ts
227
296
  var AUTH_TYPES = {
228
297
  OAUTH: "oauth",
@@ -411,11 +480,142 @@ var powerbiOauthOnboarding = new ConnectorOnboarding({
411
480
  // ../connectors/src/connectors/powerbi-oauth/parameters.ts
412
481
  var parameters = {};
413
482
 
483
+ // ../connectors/src/connectors/powerbi-oauth/utils.ts
484
+ var BASE_URL3 = "https://api.powerbi.com/v1.0/myorg";
485
+ function apiFetch(proxyFetch, path2, init) {
486
+ const url = `${BASE_URL3}${path2.startsWith("/") ? "" : "/"}${path2}`;
487
+ return proxyFetch(url, init);
488
+ }
489
+
490
+ // ../connectors/src/connectors/powerbi-oauth/setup-flow.ts
491
+ var ALL_WORKSPACES = "__ALL_WORKSPACES__";
492
+ var POWERBI_SETUP_MAX_WORKSPACES = 10;
493
+ var RESOURCE_DISPLAY_LIMIT = 25;
494
+ var RESOURCE_DATASETS = "datasets";
495
+ var RESOURCE_REPORTS = "reports";
496
+ var RESOURCE_DASHBOARDS = "dashboards";
497
+ async function listGroups(proxyFetch) {
498
+ const res = await apiFetch(proxyFetch, "/groups");
499
+ if (!res.ok) {
500
+ const body = await res.text().catch(() => res.statusText);
501
+ throw new Error(`powerbi: listGroups failed (${res.status}): ${body}`);
502
+ }
503
+ const data = await res.json();
504
+ return data.value ?? [];
505
+ }
506
+ async function listResource(proxyFetch, groupId, resource) {
507
+ const res = await apiFetch(
508
+ proxyFetch,
509
+ `/groups/${encodeURIComponent(groupId)}/${resource}`
510
+ );
511
+ if (!res.ok) {
512
+ const body = await res.text().catch(() => res.statusText);
513
+ throw new Error(
514
+ `powerbi: list ${resource} for group ${groupId} failed (${res.status}): ${body}`
515
+ );
516
+ }
517
+ const data = await res.json();
518
+ return data.value ?? [];
519
+ }
520
+ function resourceLabel(r) {
521
+ return r.name ?? r.displayName ?? r.id ?? "(unknown)";
522
+ }
523
+ var powerbiOauthSetupFlow = {
524
+ initialState: () => ({}),
525
+ steps: [
526
+ {
527
+ slug: "workspaces",
528
+ type: "multiSelect",
529
+ question: {
530
+ ja: "\u5BFE\u8C61\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
531
+ en: "Select target workspaces (multi-select allowed)"
532
+ },
533
+ async fetchOptions(_state, rt) {
534
+ const groups = await listGroups(rt.config.proxyFetch);
535
+ const options = groups.filter((g) => g.id && g.name).map((g) => ({ value: g.id, label: g.name }));
536
+ return [
537
+ {
538
+ value: ALL_WORKSPACES,
539
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "All workspaces"
540
+ },
541
+ ...options
542
+ ];
543
+ },
544
+ applyAnswer: (state, answer) => ({ ...state, workspaces: answer })
545
+ },
546
+ {
547
+ slug: "resources",
548
+ type: "multiSelect",
549
+ question: {
550
+ ja: "\u8981\u7D04\u306B\u542B\u3081\u308B\u30EA\u30BD\u30FC\u30B9\u7A2E\u5225\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
551
+ en: "Select which resource types to include in the summary"
552
+ },
553
+ async fetchOptions(state, rt) {
554
+ if (!state.workspaces?.length) return [];
555
+ const datasetsLabel = rt.language === "ja" ? "\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8" : "Datasets";
556
+ const reportsLabel = rt.language === "ja" ? "\u30EC\u30DD\u30FC\u30C8" : "Reports";
557
+ const dashboardsLabel = rt.language === "ja" ? "\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9" : "Dashboards";
558
+ return [
559
+ { value: RESOURCE_DATASETS, label: datasetsLabel },
560
+ { value: RESOURCE_REPORTS, label: reportsLabel },
561
+ { value: RESOURCE_DASHBOARDS, label: dashboardsLabel }
562
+ ];
563
+ },
564
+ applyAnswer: (state, answer) => ({ ...state, resources: answer })
565
+ }
566
+ ],
567
+ async finalize(state, rt) {
568
+ if (!state.workspaces || !state.resources) {
569
+ throw new Error("Power BI setup: incomplete state on finalize");
570
+ }
571
+ const allGroups = await listGroups(rt.config.proxyFetch);
572
+ const groupById = new Map(allGroups.map((g) => [g.id, g]));
573
+ const targetIds = await resolveSetupSelection({
574
+ selected: state.workspaces,
575
+ allSentinel: ALL_WORKSPACES,
576
+ fetchAll: async () => allGroups.map((g) => g.id).filter((id) => id),
577
+ limit: POWERBI_SETUP_MAX_WORKSPACES
578
+ });
579
+ const selectedResources = new Set(state.resources);
580
+ const sections = ["## Power BI", ""];
581
+ if (!targetIds.length) {
582
+ sections.push("_No workspaces selected._", "");
583
+ return sections.join("\n");
584
+ }
585
+ for (const id of targetIds) {
586
+ const group = groupById.get(id);
587
+ const name = group?.name ?? id;
588
+ sections.push(`### Workspace: ${name}`, "", `- id: \`${id}\``);
589
+ for (const resource of [
590
+ RESOURCE_DATASETS,
591
+ RESOURCE_REPORTS,
592
+ RESOURCE_DASHBOARDS
593
+ ]) {
594
+ if (!selectedResources.has(resource)) continue;
595
+ const items = await listResource(rt.config.proxyFetch, id, resource);
596
+ const heading = resource === RESOURCE_DATASETS ? "Datasets" : resource === RESOURCE_REPORTS ? "Reports" : "Dashboards";
597
+ sections.push(`- ${heading} (${items.length}):`);
598
+ for (const item of items.slice(0, RESOURCE_DISPLAY_LIMIT)) {
599
+ sections.push(` - ${resourceLabel(item)}`);
600
+ }
601
+ if (items.length > RESOURCE_DISPLAY_LIMIT) {
602
+ sections.push(
603
+ ` - \u2026and ${items.length - RESOURCE_DISPLAY_LIMIT} more`
604
+ );
605
+ }
606
+ }
607
+ sections.push("");
608
+ }
609
+ return sections.join("\n");
610
+ }
611
+ };
612
+
414
613
  // ../connectors/src/connectors/powerbi-oauth/index.ts
415
614
  var tools = { request: requestTool };
416
615
  var powerbiOauthConnector = new ConnectorPlugin({
417
616
  slug: "powerbi",
418
617
  authType: AUTH_TYPES.OAUTH,
618
+ skipConnectionCheckOnCreate: true,
419
619
  name: "Power BI",
420
620
  description: "Connect to Microsoft Power BI using OAuth (Microsoft Entra ID). Use it to enumerate workspaces, datasets, and reports the signed-in user has access to, and to run DAX queries.",
421
621
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/2vXQCKGpMJ9kGSaqkZl9IS/cc5669c267fc5d11e7b1f8c01723e461/power-bi-icon.png",
@@ -546,6 +746,7 @@ export default async function handler(c: Context) {
546
746
  - \`executeQueries\` \u306F\u73FE\u72B6 1 \u30EA\u30AF\u30A8\u30B9\u30C8\u306B\u3064\u304D 1 \u30AF\u30A8\u30EA\u306E\u307F`
547
747
  },
548
748
  tools,
749
+ setup: (params, ctx, config) => runSetupFlow(powerbiOauthSetupFlow, params, ctx, config),
549
750
  async checkConnection(_params, config) {
550
751
  const { proxyFetch } = config;
551
752
  const url = "https://api.powerbi.com/v1.0/myorg/groups?$top=1";
@@ -594,6 +795,7 @@ function resolveEnvVarOptional(entry, key) {
594
795
  import { getContext } from "hono/context-storage";
595
796
  import { getCookie } from "hono/cookie";
596
797
  var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
798
+ var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
597
799
  function normalizeHeaders(input) {
598
800
  const out = {};
599
801
  if (!input) return out;
@@ -602,6 +804,11 @@ function normalizeHeaders(input) {
602
804
  });
603
805
  return out;
604
806
  }
807
+ function extractInputUrl(input) {
808
+ if (typeof input === "string") return input;
809
+ if (input instanceof URL) return input.href;
810
+ return input.url;
811
+ }
605
812
  function createSandboxProxyFetch(connectionId) {
606
813
  return async (input, init) => {
607
814
  const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
@@ -611,10 +818,17 @@ function createSandboxProxyFetch(connectionId) {
611
818
  "Connection proxy is not configured. Please check your deployment settings."
612
819
  );
613
820
  }
614
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
821
+ const originalUrl = extractInputUrl(input);
822
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
823
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
824
+ const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
825
+ return fetch(sessionUrl, {
826
+ method: "POST",
827
+ headers: { Authorization: `Bearer ${token}` }
828
+ });
829
+ }
615
830
  const originalMethod = init?.method ?? "GET";
616
831
  const originalBody = init?.body ? JSON.parse(init.body) : void 0;
617
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
618
832
  const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
619
833
  return fetch(proxyUrl, {
620
834
  method: "POST",
@@ -640,10 +854,9 @@ function createDeployedAppProxyFetch(connectionId) {
640
854
  }
641
855
  const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
642
856
  const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
857
+ const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
643
858
  return async (input, init) => {
644
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
645
- const originalMethod = init?.method ?? "GET";
646
- const originalBody = init?.body ? JSON.parse(init.body) : void 0;
859
+ const originalUrl = extractInputUrl(input);
647
860
  const c = getContext();
648
861
  const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
649
862
  if (!appSession) {
@@ -651,6 +864,14 @@ function createDeployedAppProxyFetch(connectionId) {
651
864
  "No authentication method available for connection proxy."
652
865
  );
653
866
  }
867
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
868
+ return fetch(sessionUrl, {
869
+ method: "POST",
870
+ headers: { Authorization: `Bearer ${appSession}` }
871
+ });
872
+ }
873
+ const originalMethod = init?.method ?? "GET";
874
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
654
875
  return fetch(proxyUrl, {
655
876
  method: "POST",
656
877
  headers: {