@squadbase/vite-server 0.1.12-dev.a9ac647 → 0.1.17-dev.3b633bb

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 +14375 -1652
  2. package/dist/connectors/airtable-oauth.js +282 -46
  3. package/dist/connectors/airtable.js +319 -51
  4. package/dist/connectors/amplitude.js +322 -47
  5. package/dist/connectors/anthropic.js +135 -47
  6. package/dist/connectors/asana.js +327 -49
  7. package/dist/connectors/attio.js +302 -49
  8. package/dist/connectors/aws-billing.js +287 -46
  9. package/dist/connectors/azure-sql.js +421 -102
  10. package/dist/connectors/backlog-api-key.js +317 -47
  11. package/dist/connectors/clickup.js +338 -49
  12. package/dist/connectors/cosmosdb.js +305 -50
  13. package/dist/connectors/customerio.js +319 -47
  14. package/dist/connectors/dbt.js +340 -47
  15. package/dist/connectors/freshdesk.js +342 -53
  16. package/dist/connectors/freshsales.js +333 -52
  17. package/dist/connectors/freshservice.js +361 -53
  18. package/dist/connectors/gamma.js +327 -52
  19. package/dist/connectors/gemini.js +134 -47
  20. package/dist/connectors/github.js +386 -49
  21. package/dist/connectors/gmail-oauth.js +204 -7
  22. package/dist/connectors/gmail.js +350 -47
  23. package/dist/connectors/google-ads.js +288 -46
  24. package/dist/connectors/google-analytics-oauth.js +310 -46
  25. package/dist/connectors/google-analytics.js +547 -87
  26. package/dist/connectors/google-audit-log.js +438 -47
  27. package/dist/connectors/google-calendar-oauth.js +259 -46
  28. package/dist/connectors/google-calendar.js +359 -47
  29. package/dist/connectors/google-docs.js +220 -6
  30. package/dist/connectors/google-drive.js +262 -5
  31. package/dist/connectors/google-search-console-oauth.js +256 -46
  32. package/dist/connectors/google-sheets.js +272 -47
  33. package/dist/connectors/google-slides.js +205 -6
  34. package/dist/connectors/grafana.js +332 -49
  35. package/dist/connectors/hubspot-oauth.js +208 -5
  36. package/dist/connectors/hubspot.js +306 -49
  37. package/dist/connectors/influxdb.js +416 -51
  38. package/dist/connectors/intercom-oauth.js +210 -5
  39. package/dist/connectors/intercom.js +302 -49
  40. package/dist/connectors/jdbc.js +762 -110
  41. package/dist/connectors/jira-api-key.js +326 -47
  42. package/dist/connectors/kintone-api-token.js +281 -47
  43. package/dist/connectors/kintone.js +328 -47
  44. package/dist/connectors/linear.js +330 -49
  45. package/dist/connectors/linkedin-ads.js +268 -50
  46. package/dist/connectors/mailchimp-oauth.js +268 -46
  47. package/dist/connectors/mailchimp.js +320 -49
  48. package/dist/connectors/meta-ads-oauth.js +273 -48
  49. package/dist/connectors/meta-ads.js +285 -50
  50. package/dist/connectors/mixpanel.js +338 -47
  51. package/dist/connectors/monday.js +360 -49
  52. package/dist/connectors/mongodb.js +319 -57
  53. package/dist/connectors/notion-oauth.js +231 -5
  54. package/dist/connectors/notion.js +323 -51
  55. package/dist/connectors/openai.js +134 -47
  56. package/dist/connectors/oracle.js +454 -103
  57. package/dist/connectors/outlook-oauth.js +204 -5
  58. package/dist/connectors/powerbi-oauth.js +498 -5
  59. package/dist/connectors/salesforce.js +384 -49
  60. package/dist/connectors/semrush.js +609 -49
  61. package/dist/connectors/sentry.js +289 -50
  62. package/dist/connectors/shopify-oauth.js +187 -5
  63. package/dist/connectors/shopify.js +357 -47
  64. package/dist/connectors/sqlserver.js +415 -102
  65. package/dist/connectors/stripe-api-key.js +269 -46
  66. package/dist/connectors/stripe-oauth.js +202 -5
  67. package/dist/connectors/supabase.js +303 -48
  68. package/dist/connectors/tableau.js +536 -163
  69. package/dist/connectors/tiktok-ads.js +279 -48
  70. package/dist/connectors/wix-store.js +320 -49
  71. package/dist/connectors/zendesk-oauth.js +239 -5
  72. package/dist/connectors/zendesk.js +358 -47
  73. package/dist/index.d.ts +149 -1
  74. package/dist/index.js +15057 -2117
  75. package/dist/main.js +15005 -2073
  76. package/dist/vite-plugin.js +14752 -2019
  77. package/package.json +1 -1
@@ -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,76 @@ 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
+ const pendingParameterUpdates = [];
260
+ for (const step of flow.steps) {
261
+ const ans = ctx.answers[answerIdx];
262
+ if (ans && ans.questionSlug === step.slug) {
263
+ state = step.applyAnswer(state, ans.answer);
264
+ if (step.toParameterUpdates) {
265
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
266
+ }
267
+ answerIdx += 1;
268
+ continue;
269
+ }
270
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
271
+ if (step.type === "text") {
272
+ if (step.fetchOptions) {
273
+ const options2 = await step.fetchOptions(state, runtime);
274
+ if (options2.length === 0) {
275
+ continue;
276
+ }
277
+ }
278
+ return {
279
+ type: "nextQuestion",
280
+ questionSlug: step.slug,
281
+ question: step.question[ctx.language],
282
+ questionType: "text",
283
+ allowFreeText: resolvedAllowFreeText,
284
+ ...pendingParameterUpdates.length > 0 && {
285
+ parameterUpdates: pendingParameterUpdates
286
+ }
287
+ };
288
+ }
289
+ const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
290
+ if (options.length === 0) {
291
+ continue;
292
+ }
293
+ return {
294
+ type: "nextQuestion",
295
+ questionSlug: step.slug,
296
+ question: step.question[ctx.language],
297
+ questionType: step.type,
298
+ options,
299
+ allowFreeText: resolvedAllowFreeText,
300
+ ...pendingParameterUpdates.length > 0 && {
301
+ parameterUpdates: pendingParameterUpdates
302
+ }
303
+ };
304
+ }
305
+ const dataInvestigationResult = await flow.finalize(state, runtime);
306
+ return {
307
+ type: "fulfilled",
308
+ dataInvestigationResult,
309
+ ...pendingParameterUpdates.length > 0 && {
310
+ parameterUpdates: pendingParameterUpdates
311
+ }
312
+ };
313
+ }
314
+ async function resolveSetupSelection(params) {
315
+ const { selected, allSentinel, fetchAll, limit } = params;
316
+ const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
317
+ return resolved.slice(0, limit);
318
+ }
319
+
226
320
  // ../connectors/src/auth-types.ts
227
321
  var AUTH_TYPES = {
228
322
  OAUTH: "oauth",
@@ -411,11 +505,389 @@ var powerbiOauthOnboarding = new ConnectorOnboarding({
411
505
  // ../connectors/src/connectors/powerbi-oauth/parameters.ts
412
506
  var parameters = {};
413
507
 
508
+ // ../connectors/src/connectors/powerbi-oauth/utils.ts
509
+ var BASE_URL3 = "https://api.powerbi.com/v1.0/myorg";
510
+ function apiFetch(proxyFetch, path2, init) {
511
+ const url = `${BASE_URL3}${path2.startsWith("/") ? "" : "/"}${path2}`;
512
+ return proxyFetch(url, init);
513
+ }
514
+
515
+ // ../connectors/src/connectors/powerbi-oauth/setup-flow.ts
516
+ var ALL_WORKSPACES = "__ALL_WORKSPACES__";
517
+ var MY_WORKSPACE = "__MY_WORKSPACE__";
518
+ var ALL_DATASETS = "__ALL_DATASETS__";
519
+ var ALL_REPORTS = "__ALL_REPORTS__";
520
+ var ALL_DASHBOARDS = "__ALL_DASHBOARDS__";
521
+ var POWERBI_SETUP_MAX_WORKSPACES = 10;
522
+ var RESOURCE_DISPLAY_LIMIT = 25;
523
+ var RESOURCE_DATASETS = "datasets";
524
+ var RESOURCE_REPORTS = "reports";
525
+ var RESOURCE_DASHBOARDS = "dashboards";
526
+ async function listGroups(proxyFetch) {
527
+ const res = await apiFetch(proxyFetch, "/groups");
528
+ if (!res.ok) {
529
+ const body = await res.text().catch(() => res.statusText);
530
+ throw new Error(`powerbi: listGroups failed (${res.status}): ${body}`);
531
+ }
532
+ const data = await res.json();
533
+ return data.value ?? [];
534
+ }
535
+ async function listResource(proxyFetch, groupId, resource) {
536
+ const path2 = groupId === MY_WORKSPACE ? `/${resource}` : `/groups/${encodeURIComponent(groupId)}/${resource}`;
537
+ const res = await apiFetch(proxyFetch, path2);
538
+ if (!res.ok) {
539
+ const body = await res.text().catch(() => res.statusText);
540
+ throw new Error(
541
+ `powerbi: list ${resource} for ${groupId === MY_WORKSPACE ? "My workspace" : `group ${groupId}`} failed (${res.status}): ${body}`
542
+ );
543
+ }
544
+ const data = await res.json();
545
+ return data.value ?? [];
546
+ }
547
+ function resourceLabel(r) {
548
+ return r.name ?? r.displayName ?? r.id ?? "(unknown)";
549
+ }
550
+ var INTERNAL_TABLE_PREFIXES = [
551
+ "DateTableTemplate_",
552
+ "LocalDateTable_"
553
+ ];
554
+ function isInternalColumn(name) {
555
+ return /^RowNumber-[0-9A-Fa-f-]+$/.test(name);
556
+ }
557
+ async function fetchDatasetSchema(proxyFetch, wsId, datasetId) {
558
+ const pathPrefix = wsId === MY_WORKSPACE ? "" : `/groups/${encodeURIComponent(wsId)}`;
559
+ const daxQuery = 'EVALUATE SELECTCOLUMNS(COLUMNSTATISTICS(), "T", [Table Name], "C", [Column Name])';
560
+ const res = await apiFetch(
561
+ proxyFetch,
562
+ `${pathPrefix}/datasets/${encodeURIComponent(datasetId)}/executeQueries`,
563
+ {
564
+ method: "POST",
565
+ headers: { "Content-Type": "application/json" },
566
+ body: JSON.stringify({
567
+ queries: [{ query: daxQuery }],
568
+ serializerSettings: { includeNulls: true }
569
+ })
570
+ }
571
+ );
572
+ if (!res.ok) return /* @__PURE__ */ new Map();
573
+ const data = await res.json();
574
+ const rows = data.results?.[0]?.tables?.[0]?.rows ?? [];
575
+ const schema = /* @__PURE__ */ new Map();
576
+ for (const row of rows) {
577
+ const table = row["[T]"] ?? "";
578
+ const column = row["[C]"] ?? "";
579
+ if (!table || !column) continue;
580
+ if (INTERNAL_TABLE_PREFIXES.some((p) => table.startsWith(p))) continue;
581
+ if (isInternalColumn(column)) continue;
582
+ if (!schema.has(table)) schema.set(table, []);
583
+ schema.get(table).push(column);
584
+ }
585
+ return schema;
586
+ }
587
+ function workspaceName(wsId, groupById, language) {
588
+ if (wsId === MY_WORKSPACE) {
589
+ return language === "ja" ? "\u30DE\u30A4 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "My workspace";
590
+ }
591
+ return groupById.get(wsId)?.name ?? wsId;
592
+ }
593
+ async function resolveWorkspaceIds(selected, proxyFetch) {
594
+ return resolveSetupSelection({
595
+ selected,
596
+ allSentinel: ALL_WORKSPACES,
597
+ fetchAll: async () => {
598
+ const groups = await listGroups(proxyFetch);
599
+ return [MY_WORKSPACE, ...groups.map((g) => g.id).filter(Boolean)];
600
+ },
601
+ limit: POWERBI_SETUP_MAX_WORKSPACES
602
+ });
603
+ }
604
+ function compoundKey(wsId, objectId) {
605
+ return `${wsId}:${objectId}`;
606
+ }
607
+ function parseCompoundKey(key) {
608
+ const idx = key.indexOf(":");
609
+ if (idx < 0) return { wsId: "", objectId: key };
610
+ return { wsId: key.slice(0, idx), objectId: key.slice(idx + 1) };
611
+ }
612
+ async function fetchObjectOptions(state, resource, allSentinel, allLabelJa, allLabelEn, rt) {
613
+ if (!state.workspaces?.length || !state.resources?.includes(resource))
614
+ return [];
615
+ const wsIds = await resolveWorkspaceIds(
616
+ state.workspaces,
617
+ rt.config.proxyFetch
618
+ );
619
+ const allGroups = await listGroups(rt.config.proxyFetch);
620
+ const groupById = new Map(allGroups.map((g) => [g.id, g]));
621
+ const multiWorkspace = wsIds.length > 1;
622
+ const options = [];
623
+ for (const wsId of wsIds) {
624
+ const wsLabel = workspaceName(wsId, groupById, rt.language);
625
+ const items = await listResource(rt.config.proxyFetch, wsId, resource);
626
+ for (const item of items) {
627
+ if (!item.id) continue;
628
+ options.push({
629
+ value: compoundKey(wsId, item.id),
630
+ label: multiWorkspace ? `${wsLabel} / ${resourceLabel(item)}` : resourceLabel(item)
631
+ });
632
+ }
633
+ }
634
+ if (options.length === 0) return [];
635
+ return [
636
+ {
637
+ value: allSentinel,
638
+ label: rt.language === "ja" ? allLabelJa : allLabelEn
639
+ },
640
+ ...options
641
+ ];
642
+ }
643
+ var powerbiOauthSetupFlow = {
644
+ initialState: () => ({}),
645
+ steps: [
646
+ {
647
+ slug: "workspaces",
648
+ type: "multiSelect",
649
+ question: {
650
+ 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",
651
+ en: "Select target workspaces (multi-select allowed)"
652
+ },
653
+ async fetchOptions(_state, rt) {
654
+ const groups = await listGroups(rt.config.proxyFetch);
655
+ const options = groups.filter((g) => g.id && g.name).map((g) => ({ value: g.id, label: g.name }));
656
+ return [
657
+ {
658
+ value: ALL_WORKSPACES,
659
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "All workspaces"
660
+ },
661
+ {
662
+ value: MY_WORKSPACE,
663
+ label: rt.language === "ja" ? "\u30DE\u30A4 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9" : "My workspace"
664
+ },
665
+ ...options
666
+ ];
667
+ },
668
+ applyAnswer: (state, answer) => ({ ...state, workspaces: answer })
669
+ },
670
+ {
671
+ slug: "resources",
672
+ type: "multiSelect",
673
+ question: {
674
+ 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",
675
+ en: "Select which resource types to include in the summary"
676
+ },
677
+ async fetchOptions(state, rt) {
678
+ if (!state.workspaces?.length) return [];
679
+ return [
680
+ {
681
+ value: RESOURCE_DATASETS,
682
+ label: rt.language === "ja" ? "\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8" : "Datasets"
683
+ },
684
+ {
685
+ value: RESOURCE_REPORTS,
686
+ label: rt.language === "ja" ? "\u30EC\u30DD\u30FC\u30C8" : "Reports"
687
+ },
688
+ {
689
+ value: RESOURCE_DASHBOARDS,
690
+ label: rt.language === "ja" ? "\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9" : "Dashboards"
691
+ }
692
+ ];
693
+ },
694
+ applyAnswer: (state, answer) => ({ ...state, resources: answer })
695
+ },
696
+ {
697
+ slug: "datasets",
698
+ type: "multiSelect",
699
+ question: {
700
+ 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",
701
+ en: "Select the datasets you want to use (multi-select allowed)"
702
+ },
703
+ async fetchOptions(state, rt) {
704
+ return fetchObjectOptions(
705
+ state,
706
+ RESOURCE_DATASETS,
707
+ ALL_DATASETS,
708
+ "\u3059\u3079\u3066\u306E\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8",
709
+ "All datasets",
710
+ rt
711
+ );
712
+ },
713
+ applyAnswer: (state, answer) => ({ ...state, selectedDatasets: answer })
714
+ },
715
+ {
716
+ slug: "reports",
717
+ type: "multiSelect",
718
+ question: {
719
+ ja: "\u4F7F\u7528\u3059\u308B\u30EC\u30DD\u30FC\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
720
+ en: "Select the reports you want to use (multi-select allowed)"
721
+ },
722
+ async fetchOptions(state, rt) {
723
+ return fetchObjectOptions(
724
+ state,
725
+ RESOURCE_REPORTS,
726
+ ALL_REPORTS,
727
+ "\u3059\u3079\u3066\u306E\u30EC\u30DD\u30FC\u30C8",
728
+ "All reports",
729
+ rt
730
+ );
731
+ },
732
+ applyAnswer: (state, answer) => ({ ...state, selectedReports: answer })
733
+ },
734
+ {
735
+ slug: "dashboards",
736
+ type: "multiSelect",
737
+ question: {
738
+ 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",
739
+ en: "Select the dashboards you want to use (multi-select allowed)"
740
+ },
741
+ async fetchOptions(state, rt) {
742
+ return fetchObjectOptions(
743
+ state,
744
+ RESOURCE_DASHBOARDS,
745
+ ALL_DASHBOARDS,
746
+ "\u3059\u3079\u3066\u306E\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9",
747
+ "All dashboards",
748
+ rt
749
+ );
750
+ },
751
+ applyAnswer: (state, answer) => ({
752
+ ...state,
753
+ selectedDashboards: answer
754
+ })
755
+ }
756
+ ],
757
+ async finalize(state, rt) {
758
+ if (!state.workspaces || !state.resources) {
759
+ throw new Error("Power BI setup: incomplete state on finalize");
760
+ }
761
+ const allGroups = await listGroups(rt.config.proxyFetch);
762
+ const groupById = new Map(allGroups.map((g) => [g.id, g]));
763
+ const wsIds = await resolveWorkspaceIds(
764
+ state.workspaces,
765
+ rt.config.proxyFetch
766
+ );
767
+ const selectedResources = new Set(state.resources);
768
+ async function resolveObjects(selected, allSentinel, resource) {
769
+ const byWorkspace = /* @__PURE__ */ new Map();
770
+ if (!selected || !selectedResources.has(resource)) return byWorkspace;
771
+ if (selected.includes(allSentinel)) {
772
+ for (const wsId of wsIds) {
773
+ const items = await listResource(
774
+ rt.config.proxyFetch,
775
+ wsId,
776
+ resource
777
+ );
778
+ const ids = items.map((i) => i.id).filter((id) => !!id);
779
+ if (ids.length > 0) byWorkspace.set(wsId, new Set(ids));
780
+ }
781
+ } else {
782
+ for (const key of selected) {
783
+ if (key === allSentinel) continue;
784
+ const { wsId, objectId } = parseCompoundKey(key);
785
+ if (!byWorkspace.has(wsId)) byWorkspace.set(wsId, /* @__PURE__ */ new Set());
786
+ byWorkspace.get(wsId).add(objectId);
787
+ }
788
+ }
789
+ return byWorkspace;
790
+ }
791
+ const datasetsByWs = await resolveObjects(
792
+ state.selectedDatasets,
793
+ ALL_DATASETS,
794
+ RESOURCE_DATASETS
795
+ );
796
+ const reportsByWs = await resolveObjects(
797
+ state.selectedReports,
798
+ ALL_REPORTS,
799
+ RESOURCE_REPORTS
800
+ );
801
+ const dashboardsByWs = await resolveObjects(
802
+ state.selectedDashboards,
803
+ ALL_DASHBOARDS,
804
+ RESOURCE_DASHBOARDS
805
+ );
806
+ const allWsIds = /* @__PURE__ */ new Set([
807
+ ...datasetsByWs.keys(),
808
+ ...reportsByWs.keys(),
809
+ ...dashboardsByWs.keys()
810
+ ]);
811
+ const sections = ["## Power BI", ""];
812
+ if (allWsIds.size === 0) {
813
+ sections.push("_No resources selected._", "");
814
+ return sections.join("\n");
815
+ }
816
+ for (const wsId of wsIds) {
817
+ if (!allWsIds.has(wsId)) continue;
818
+ const name = workspaceName(wsId, groupById, rt.language);
819
+ sections.push(`### Workspace: ${name}`, "");
820
+ if (wsId !== MY_WORKSPACE) {
821
+ sections.push(`- id: \`${wsId}\``);
822
+ }
823
+ const datasetIds = datasetsByWs.get(wsId);
824
+ if (datasetIds?.size) {
825
+ const items = await listResource(
826
+ rt.config.proxyFetch,
827
+ wsId,
828
+ RESOURCE_DATASETS
829
+ );
830
+ const filtered = items.filter(
831
+ (item) => item.id && datasetIds.has(item.id)
832
+ );
833
+ for (const item of filtered.slice(0, RESOURCE_DISPLAY_LIMIT)) {
834
+ sections.push(`#### Dataset: ${resourceLabel(item)}`, "");
835
+ const schema = await fetchDatasetSchema(
836
+ rt.config.proxyFetch,
837
+ wsId,
838
+ item.id
839
+ );
840
+ if (schema.size > 0) {
841
+ for (const [table, columns] of schema) {
842
+ sections.push(`##### Table: ${table}`, "");
843
+ sections.push("| Column |");
844
+ sections.push("|--------|");
845
+ for (const col of columns) {
846
+ sections.push(`| ${col} |`);
847
+ }
848
+ sections.push("");
849
+ }
850
+ } else {
851
+ sections.push("_Schema not available._", "");
852
+ }
853
+ }
854
+ }
855
+ for (const [resource, selectedByWs, heading] of [
856
+ [RESOURCE_REPORTS, reportsByWs, "Reports"],
857
+ [RESOURCE_DASHBOARDS, dashboardsByWs, "Dashboards"]
858
+ ]) {
859
+ const selectedIds = selectedByWs.get(wsId);
860
+ if (!selectedIds?.size) continue;
861
+ const items = await listResource(
862
+ rt.config.proxyFetch,
863
+ wsId,
864
+ resource
865
+ );
866
+ const filtered = items.filter(
867
+ (item) => item.id && selectedIds.has(item.id)
868
+ );
869
+ sections.push(`- ${heading} (${filtered.length}):`);
870
+ for (const item of filtered.slice(0, RESOURCE_DISPLAY_LIMIT)) {
871
+ sections.push(` - ${resourceLabel(item)}`);
872
+ }
873
+ if (filtered.length > RESOURCE_DISPLAY_LIMIT) {
874
+ sections.push(
875
+ ` - \u2026and ${filtered.length - RESOURCE_DISPLAY_LIMIT} more`
876
+ );
877
+ }
878
+ }
879
+ sections.push("");
880
+ }
881
+ return sections.join("\n");
882
+ }
883
+ };
884
+
414
885
  // ../connectors/src/connectors/powerbi-oauth/index.ts
415
886
  var tools = { request: requestTool };
416
887
  var powerbiOauthConnector = new ConnectorPlugin({
417
888
  slug: "powerbi",
418
889
  authType: AUTH_TYPES.OAUTH,
890
+ skipConnectionCheckOnCreate: true,
419
891
  name: "Power BI",
420
892
  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
893
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/2vXQCKGpMJ9kGSaqkZl9IS/cc5669c267fc5d11e7b1f8c01723e461/power-bi-icon.png",
@@ -546,6 +1018,7 @@ export default async function handler(c: Context) {
546
1018
  - \`executeQueries\` \u306F\u73FE\u72B6 1 \u30EA\u30AF\u30A8\u30B9\u30C8\u306B\u3064\u304D 1 \u30AF\u30A8\u30EA\u306E\u307F`
547
1019
  },
548
1020
  tools,
1021
+ setup: (params, ctx, config) => runSetupFlow(powerbiOauthSetupFlow, params, ctx, config),
549
1022
  async checkConnection(_params, config) {
550
1023
  const { proxyFetch } = config;
551
1024
  const url = "https://api.powerbi.com/v1.0/myorg/groups?$top=1";
@@ -594,6 +1067,7 @@ function resolveEnvVarOptional(entry, key) {
594
1067
  import { getContext } from "hono/context-storage";
595
1068
  import { getCookie } from "hono/cookie";
596
1069
  var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
1070
+ var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
597
1071
  function normalizeHeaders(input) {
598
1072
  const out = {};
599
1073
  if (!input) return out;
@@ -602,6 +1076,11 @@ function normalizeHeaders(input) {
602
1076
  });
603
1077
  return out;
604
1078
  }
1079
+ function extractInputUrl(input) {
1080
+ if (typeof input === "string") return input;
1081
+ if (input instanceof URL) return input.href;
1082
+ return input.url;
1083
+ }
605
1084
  function createSandboxProxyFetch(connectionId) {
606
1085
  return async (input, init) => {
607
1086
  const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
@@ -611,10 +1090,17 @@ function createSandboxProxyFetch(connectionId) {
611
1090
  "Connection proxy is not configured. Please check your deployment settings."
612
1091
  );
613
1092
  }
614
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1093
+ const originalUrl = extractInputUrl(input);
1094
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
1095
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
1096
+ const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
1097
+ return fetch(sessionUrl, {
1098
+ method: "POST",
1099
+ headers: { Authorization: `Bearer ${token}` }
1100
+ });
1101
+ }
615
1102
  const originalMethod = init?.method ?? "GET";
616
1103
  const originalBody = init?.body ? JSON.parse(init.body) : void 0;
617
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
618
1104
  const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
619
1105
  return fetch(proxyUrl, {
620
1106
  method: "POST",
@@ -640,10 +1126,9 @@ function createDeployedAppProxyFetch(connectionId) {
640
1126
  }
641
1127
  const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
642
1128
  const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
1129
+ const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
643
1130
  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;
1131
+ const originalUrl = extractInputUrl(input);
647
1132
  const c = getContext();
648
1133
  const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
649
1134
  if (!appSession) {
@@ -651,6 +1136,14 @@ function createDeployedAppProxyFetch(connectionId) {
651
1136
  "No authentication method available for connection proxy."
652
1137
  );
653
1138
  }
1139
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
1140
+ return fetch(sessionUrl, {
1141
+ method: "POST",
1142
+ headers: { Authorization: `Bearer ${appSession}` }
1143
+ });
1144
+ }
1145
+ const originalMethod = init?.method ?? "GET";
1146
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
654
1147
  return fetch(proxyUrl, {
655
1148
  method: "POST",
656
1149
  headers: {