@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
@@ -1,51 +1,63 @@
1
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2
+ var __esm = (fn, res) => function __init() {
3
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
+ };
5
+
1
6
  // ../connectors/src/parameter-definition.ts
2
- var ParameterDefinition = class {
3
- slug;
4
- name;
5
- description;
6
- envVarBaseKey;
7
- type;
8
- secret;
9
- required;
10
- constructor(config) {
11
- this.slug = config.slug;
12
- this.name = config.name;
13
- this.description = config.description;
14
- this.envVarBaseKey = config.envVarBaseKey;
15
- this.type = config.type;
16
- this.secret = config.secret;
17
- this.required = config.required;
18
- }
19
- /**
20
- * Get the parameter value from a ConnectorConnectionObject.
21
- */
22
- getValue(connection2) {
23
- const param = connection2.parameters.find(
24
- (p) => p.parameterSlug === this.slug
25
- );
26
- if (!param || param.value == null) {
27
- throw new Error(
28
- `Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
29
- );
30
- }
31
- return param.value;
32
- }
33
- /**
34
- * Try to get the parameter value. Returns undefined if not found (for optional params).
35
- */
36
- tryGetValue(connection2) {
37
- const param = connection2.parameters.find(
38
- (p) => p.parameterSlug === this.slug
39
- );
40
- if (!param || param.value == null) return void 0;
41
- return param.value;
7
+ var ParameterDefinition;
8
+ var init_parameter_definition = __esm({
9
+ "../connectors/src/parameter-definition.ts"() {
10
+ "use strict";
11
+ ParameterDefinition = class {
12
+ slug;
13
+ name;
14
+ description;
15
+ envVarBaseKey;
16
+ type;
17
+ secret;
18
+ required;
19
+ constructor(config) {
20
+ this.slug = config.slug;
21
+ this.name = config.name;
22
+ this.description = config.description;
23
+ this.envVarBaseKey = config.envVarBaseKey;
24
+ this.type = config.type;
25
+ this.secret = config.secret;
26
+ this.required = config.required;
27
+ }
28
+ /**
29
+ * Get the parameter value from a ConnectorConnectionObject.
30
+ */
31
+ getValue(connection2) {
32
+ const param = connection2.parameters.find(
33
+ (p) => p.parameterSlug === this.slug
34
+ );
35
+ if (!param || param.value == null) {
36
+ throw new Error(
37
+ `Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
38
+ );
39
+ }
40
+ return param.value;
41
+ }
42
+ /**
43
+ * Try to get the parameter value. Returns undefined if not found (for optional params).
44
+ */
45
+ tryGetValue(connection2) {
46
+ const param = connection2.parameters.find(
47
+ (p) => p.parameterSlug === this.slug
48
+ );
49
+ if (!param || param.value == null) return void 0;
50
+ return param.value;
51
+ }
52
+ };
42
53
  }
43
- };
54
+ });
44
55
 
45
56
  // ../connectors/src/connectors/google-analytics/sdk/index.ts
46
57
  import * as crypto from "crypto";
47
58
 
48
59
  // ../connectors/src/connectors/google-analytics/parameters.ts
60
+ init_parameter_definition();
49
61
  var parameters = {
50
62
  serviceAccountKeyJsonBase64: new ParameterDefinition({
51
63
  slug: "service-account-key-json-base64",
@@ -125,7 +137,7 @@ function createClient(params) {
125
137
  }
126
138
  let cachedToken = null;
127
139
  let tokenExpiresAt = 0;
128
- async function getAccessToken() {
140
+ async function getAccessToken2() {
129
141
  const nowSec = Math.floor(Date.now() / 1e3);
130
142
  if (cachedToken && nowSec < tokenExpiresAt - 60) {
131
143
  return cachedToken;
@@ -156,7 +168,7 @@ function createClient(params) {
156
168
  }
157
169
  return {
158
170
  async request(path2, init) {
159
- const accessToken = await getAccessToken();
171
+ const accessToken = await getAccessToken2();
160
172
  const resolvedPath = path2.replace(/\{propertyId\}/g, propertyId);
161
173
  const url = `${BASE_URL.replace(/\/+$/, "")}/${resolvedPath.replace(/^\/+/, "")}`;
162
174
  const headers = new Headers(init?.headers);
@@ -280,6 +292,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
280
292
  tools;
281
293
  query;
282
294
  checkConnection;
295
+ /**
296
+ * SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
297
+ * implement this expose a step-by-step exploration flow (database/schema/
298
+ * table/etc. discovery) that the dashboard backend drives via the
299
+ * `/connections/:connectionId/setup` endpoint. Implement by delegating to
300
+ * `runSetupFlow` from `setup-flow.ts`.
301
+ */
302
+ setup;
303
+ /**
304
+ * Opt-out of the default "verify before save" behavior on connection
305
+ * creation. The backend invokes `checkConnection` synchronously while
306
+ * creating the connection and aborts (no row inserted) if it fails — this
307
+ * flag disables that for connectors where the check cannot succeed pre-save:
308
+ *
309
+ * - `squadbase-db` populates `connection-url` only after Neon provisioning
310
+ * - OAuth connectors require an OAuth-aware proxyFetch keyed by the
311
+ * connectionId, which doesn't exist until the row is saved
312
+ *
313
+ * Exceptions are the explicit position; new credential-input connectors get
314
+ * the default verify-on-create behavior without opt-in.
315
+ */
316
+ skipConnectionCheckOnCreate;
283
317
  constructor(config) {
284
318
  this.slug = config.slug;
285
319
  this.authType = config.authType;
@@ -296,6 +330,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
296
330
  this.tools = config.tools;
297
331
  this.query = config.query;
298
332
  this.checkConnection = config.checkConnection;
333
+ this.setup = config.setup;
334
+ this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
299
335
  }
300
336
  get connectorKey() {
301
337
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -360,6 +396,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
360
396
  }
361
397
  };
362
398
 
399
+ // ../connectors/src/setup-flow.ts
400
+ async function runSetupFlow(flow, params, ctx, config) {
401
+ const runtime = {
402
+ params,
403
+ language: ctx.language,
404
+ config
405
+ };
406
+ let state = flow.initialState();
407
+ let answerIdx = 0;
408
+ for (const step of flow.steps) {
409
+ const ans = ctx.answers[answerIdx];
410
+ if (ans && ans.questionSlug === step.slug) {
411
+ state = step.applyAnswer(state, ans.answer);
412
+ answerIdx += 1;
413
+ continue;
414
+ }
415
+ if (step.type === "text") {
416
+ return {
417
+ type: "nextQuestion",
418
+ questionSlug: step.slug,
419
+ question: step.question[ctx.language],
420
+ questionType: "text"
421
+ };
422
+ }
423
+ const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
424
+ if (options.length === 0) {
425
+ continue;
426
+ }
427
+ return {
428
+ type: "nextQuestion",
429
+ questionSlug: step.slug,
430
+ question: step.question[ctx.language],
431
+ questionType: step.type,
432
+ options
433
+ };
434
+ }
435
+ const dataInvestigationResult = await flow.finalize(state, runtime);
436
+ return { type: "fulfilled", dataInvestigationResult };
437
+ }
438
+ async function resolveSetupSelection(params) {
439
+ const { selected, allSentinel, fetchAll, limit } = params;
440
+ const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
441
+ return resolved.slice(0, limit);
442
+ }
443
+
363
444
  // ../connectors/src/auth-types.ts
364
445
  var AUTH_TYPES = {
365
446
  OAUTH: "oauth",
@@ -380,6 +461,219 @@ var googleAnalyticsOnboarding = new ConnectorOnboarding({
380
461
  }
381
462
  });
382
463
 
464
+ // ../connectors/src/connectors/google-analytics/utils.ts
465
+ import * as crypto2 from "crypto";
466
+ var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
467
+ var ADMIN_BASE_URL = "https://analyticsadmin.googleapis.com/v1beta";
468
+ var SCOPE2 = "https://www.googleapis.com/auth/analytics.readonly";
469
+ function base64url2(input) {
470
+ const buf = typeof input === "string" ? Buffer.from(input) : input;
471
+ return buf.toString("base64url");
472
+ }
473
+ function buildJwt2(clientEmail, privateKey, nowSec) {
474
+ const header = base64url2(JSON.stringify({ alg: "RS256", typ: "JWT" }));
475
+ const payload = base64url2(
476
+ JSON.stringify({
477
+ iss: clientEmail,
478
+ scope: SCOPE2,
479
+ aud: TOKEN_URL2,
480
+ iat: nowSec,
481
+ exp: nowSec + 3600
482
+ })
483
+ );
484
+ const signingInput = `${header}.${payload}`;
485
+ const sign = crypto2.createSign("RSA-SHA256");
486
+ sign.update(signingInput);
487
+ sign.end();
488
+ const signature = base64url2(sign.sign(privateKey));
489
+ return `${signingInput}.${signature}`;
490
+ }
491
+ function decodeServiceAccount(serviceAccountKeyJsonBase64) {
492
+ let serviceAccountKey;
493
+ try {
494
+ const decoded = Buffer.from(
495
+ serviceAccountKeyJsonBase64,
496
+ "base64"
497
+ ).toString("utf-8");
498
+ serviceAccountKey = JSON.parse(decoded);
499
+ } catch {
500
+ throw new Error(
501
+ "google-analytics: failed to decode service account key JSON from base64"
502
+ );
503
+ }
504
+ if (!serviceAccountKey.client_email || !serviceAccountKey.private_key) {
505
+ throw new Error(
506
+ "google-analytics: service account key JSON must contain client_email and private_key"
507
+ );
508
+ }
509
+ return serviceAccountKey;
510
+ }
511
+ async function getAccessToken(serviceAccountKey) {
512
+ const nowSec = Math.floor(Date.now() / 1e3);
513
+ const jwt = buildJwt2(
514
+ serviceAccountKey.client_email,
515
+ serviceAccountKey.private_key,
516
+ nowSec
517
+ );
518
+ const response = await fetch(TOKEN_URL2, {
519
+ method: "POST",
520
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
521
+ body: new URLSearchParams({
522
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
523
+ assertion: jwt
524
+ })
525
+ });
526
+ if (!response.ok) {
527
+ const text = await response.text();
528
+ throw new Error(
529
+ `google-analytics: token exchange failed (${response.status}): ${text}`
530
+ );
531
+ }
532
+ const data = await response.json();
533
+ return data.access_token;
534
+ }
535
+ async function adminFetch(params, path2, init) {
536
+ const keyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
537
+ if (!keyJsonBase64) {
538
+ throw new Error(
539
+ "google-analytics: missing required parameter: service-account-key-json-base64"
540
+ );
541
+ }
542
+ const serviceAccountKey = decodeServiceAccount(keyJsonBase64);
543
+ const accessToken = await getAccessToken(serviceAccountKey);
544
+ const url = `${ADMIN_BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`;
545
+ const headers = new Headers(init?.headers);
546
+ headers.set("Authorization", `Bearer ${accessToken}`);
547
+ return fetch(url, { ...init, headers });
548
+ }
549
+
550
+ // ../connectors/src/connectors/google-analytics/setup-flow.ts
551
+ var ALL_PROPERTIES = "__ALL_PROPERTIES__";
552
+ var GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES = 20;
553
+ async function listAccountSummaries(params) {
554
+ const summaries = [];
555
+ let pageToken;
556
+ do {
557
+ const path2 = pageToken ? `/accountSummaries?pageToken=${encodeURIComponent(pageToken)}` : `/accountSummaries`;
558
+ const res = await adminFetch(params, path2);
559
+ if (!res.ok) {
560
+ const body = await res.text().catch(() => res.statusText);
561
+ throw new Error(
562
+ `google-analytics: accountSummaries failed (${res.status}): ${body}`
563
+ );
564
+ }
565
+ const data = await res.json();
566
+ summaries.push(...data.accountSummaries ?? []);
567
+ pageToken = data.nextPageToken;
568
+ } while (pageToken);
569
+ return summaries;
570
+ }
571
+ function propertyIdFromResource(resource) {
572
+ return (resource ?? "").replace(/^properties\//, "");
573
+ }
574
+ async function getProperty(params, propertyId) {
575
+ const res = await adminFetch(params, `/properties/${propertyId}`);
576
+ if (!res.ok) return null;
577
+ return await res.json();
578
+ }
579
+ var googleAnalyticsSetupFlow = {
580
+ initialState: () => ({}),
581
+ steps: [
582
+ {
583
+ slug: "account",
584
+ type: "select",
585
+ question: {
586
+ ja: "Google Analytics \u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044",
587
+ en: "Select a Google Analytics account"
588
+ },
589
+ async fetchOptions(_state, rt) {
590
+ const summaries = await listAccountSummaries(rt.params);
591
+ return summaries.map((s) => {
592
+ const accountResource = s.account ?? s.name ?? "";
593
+ if (!accountResource) return null;
594
+ return {
595
+ value: accountResource,
596
+ label: s.displayName ?? accountResource
597
+ };
598
+ }).filter((v) => v != null);
599
+ },
600
+ applyAnswer: (state, answer) => ({ ...state, account: answer[0] })
601
+ },
602
+ {
603
+ slug: "properties",
604
+ type: "multiSelect",
605
+ question: {
606
+ ja: "\u5BFE\u8C61\u306E GA4 \u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
607
+ en: "Select target GA4 properties (multi-select allowed)"
608
+ },
609
+ async fetchOptions(state, rt) {
610
+ if (!state.account) return [];
611
+ const summaries = await listAccountSummaries(rt.params);
612
+ const account = summaries.find(
613
+ (s) => (s.account ?? s.name) === state.account
614
+ );
615
+ const props = (account?.propertySummaries ?? []).map((p) => {
616
+ const id = propertyIdFromResource(p.property);
617
+ if (!id) return null;
618
+ return {
619
+ value: id,
620
+ label: p.displayName ? `${p.displayName} (${id})` : id
621
+ };
622
+ }).filter((v) => v != null);
623
+ if (props.length === 0) return [];
624
+ return [
625
+ {
626
+ value: ALL_PROPERTIES,
627
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30D1\u30C6\u30A3" : "All properties"
628
+ },
629
+ ...props
630
+ ];
631
+ },
632
+ applyAnswer: (state, answer) => ({ ...state, properties: answer })
633
+ }
634
+ ],
635
+ async finalize(state, rt) {
636
+ if (!state.account || !state.properties) {
637
+ throw new Error("Google Analytics setup: incomplete state on finalize");
638
+ }
639
+ const summaries = await listAccountSummaries(rt.params);
640
+ const account = summaries.find(
641
+ (s) => (s.account ?? s.name) === state.account
642
+ );
643
+ const accountLabel = account?.displayName ?? state.account.replace(/^accounts\//, "");
644
+ const targetPropertyIds = await resolveSetupSelection({
645
+ selected: state.properties,
646
+ allSentinel: ALL_PROPERTIES,
647
+ fetchAll: async () => (account?.propertySummaries ?? []).map((p) => propertyIdFromResource(p.property)).filter((v) => v),
648
+ limit: GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES
649
+ });
650
+ const sections = [
651
+ "## Google Analytics",
652
+ "",
653
+ `### Account: ${accountLabel}`,
654
+ ""
655
+ ];
656
+ if (targetPropertyIds.length === 0) {
657
+ sections.push("_No properties selected._", "");
658
+ return sections.join("\n");
659
+ }
660
+ sections.push("| Property ID | Display Name | Time Zone | Currency | Industry |");
661
+ sections.push("|-------------|--------------|-----------|----------|----------|");
662
+ for (const propertyId of targetPropertyIds) {
663
+ const prop = await getProperty(rt.params, propertyId);
664
+ const displayName = (prop?.displayName ?? "-").replace(/\|/g, "\\|");
665
+ const timeZone = prop?.timeZone ?? "-";
666
+ const currency = prop?.currencyCode ?? "-";
667
+ const industry = (prop?.industryCategory ?? "-").replace(/\|/g, "\\|");
668
+ sections.push(
669
+ `| ${propertyId} | ${displayName} | ${timeZone} | ${currency} | ${industry} |`
670
+ );
671
+ }
672
+ sections.push("");
673
+ return sections.join("\n");
674
+ }
675
+ };
676
+
383
677
  // ../connectors/src/connectors/google-analytics/tools/request.ts
384
678
  import { z } from "zod";
385
679
  var BASE_URL2 = "https://analyticsdata.googleapis.com/v1beta/";
@@ -613,7 +907,31 @@ activeUsers, sessions, screenPageViews, bounceRate, averageSessionDuration, conv
613
907
  - \u7D76\u5BFE\u5024: \`"2024-01-01"\`
614
908
  - \u76F8\u5BFE\u5024: \`"today"\`, \`"yesterday"\`, \`"7daysAgo"\`, \`"30daysAgo"\``
615
909
  },
616
- tools
910
+ tools,
911
+ setup: (params, ctx, config) => runSetupFlow(googleAnalyticsSetupFlow, params, ctx, config),
912
+ async checkConnection(params, _config) {
913
+ const keyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
914
+ if (!keyJsonBase64) {
915
+ return {
916
+ success: false,
917
+ error: "google-analytics: missing service account key"
918
+ };
919
+ }
920
+ try {
921
+ const res = await adminFetch(params, "/accountSummaries?pageSize=1");
922
+ if (!res.ok) {
923
+ const body = await res.text().catch(() => res.statusText);
924
+ return {
925
+ success: false,
926
+ error: `google-analytics: accountSummaries failed (${res.status}): ${body}`
927
+ };
928
+ }
929
+ return { success: true };
930
+ } catch (err) {
931
+ const msg = err instanceof Error ? err.message : String(err);
932
+ return { success: false, error: msg };
933
+ }
934
+ }
617
935
  });
618
936
 
619
937
  // src/connectors/create-connector-sdk.ts
@@ -642,6 +960,7 @@ function resolveEnvVarOptional(entry, key) {
642
960
  import { getContext } from "hono/context-storage";
643
961
  import { getCookie } from "hono/cookie";
644
962
  var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
963
+ var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
645
964
  function normalizeHeaders(input) {
646
965
  const out = {};
647
966
  if (!input) return out;
@@ -650,6 +969,11 @@ function normalizeHeaders(input) {
650
969
  });
651
970
  return out;
652
971
  }
972
+ function extractInputUrl(input) {
973
+ if (typeof input === "string") return input;
974
+ if (input instanceof URL) return input.href;
975
+ return input.url;
976
+ }
653
977
  function createSandboxProxyFetch(connectionId) {
654
978
  return async (input, init) => {
655
979
  const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
@@ -659,10 +983,17 @@ function createSandboxProxyFetch(connectionId) {
659
983
  "Connection proxy is not configured. Please check your deployment settings."
660
984
  );
661
985
  }
662
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
986
+ const originalUrl = extractInputUrl(input);
987
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
988
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
989
+ const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
990
+ return fetch(sessionUrl, {
991
+ method: "POST",
992
+ headers: { Authorization: `Bearer ${token}` }
993
+ });
994
+ }
663
995
  const originalMethod = init?.method ?? "GET";
664
996
  const originalBody = init?.body ? JSON.parse(init.body) : void 0;
665
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
666
997
  const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
667
998
  return fetch(proxyUrl, {
668
999
  method: "POST",
@@ -688,10 +1019,9 @@ function createDeployedAppProxyFetch(connectionId) {
688
1019
  }
689
1020
  const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
690
1021
  const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
1022
+ const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
691
1023
  return async (input, init) => {
692
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
693
- const originalMethod = init?.method ?? "GET";
694
- const originalBody = init?.body ? JSON.parse(init.body) : void 0;
1024
+ const originalUrl = extractInputUrl(input);
695
1025
  const c = getContext();
696
1026
  const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
697
1027
  if (!appSession) {
@@ -699,6 +1029,14 @@ function createDeployedAppProxyFetch(connectionId) {
699
1029
  "No authentication method available for connection proxy."
700
1030
  );
701
1031
  }
1032
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
1033
+ return fetch(sessionUrl, {
1034
+ method: "POST",
1035
+ headers: { Authorization: `Bearer ${appSession}` }
1036
+ });
1037
+ }
1038
+ const originalMethod = init?.method ?? "GET";
1039
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
702
1040
  return fetch(proxyUrl, {
703
1041
  method: "POST",
704
1042
  headers: {