@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
@@ -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",
@@ -55,15 +67,6 @@ var parameters = {
55
67
  type: "base64EncodedJson",
56
68
  secret: true,
57
69
  required: true
58
- }),
59
- propertyId: new ParameterDefinition({
60
- slug: "property-id",
61
- name: "Google Analytics Property ID",
62
- description: "The Google Analytics 4 property ID (e.g., 123456789).",
63
- envVarBaseKey: "GA_PROPERTY_ID",
64
- type: "text",
65
- secret: false,
66
- required: true
67
70
  })
68
71
  };
69
72
 
@@ -95,15 +98,9 @@ function buildJwt(clientEmail, privateKey, nowSec) {
95
98
  }
96
99
  function createClient(params) {
97
100
  const serviceAccountKeyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
98
- const propertyId = params[parameters.propertyId.slug];
99
- if (!serviceAccountKeyJsonBase64 || !propertyId) {
100
- const required = [
101
- parameters.serviceAccountKeyJsonBase64.slug,
102
- parameters.propertyId.slug
103
- ];
104
- const missing = required.filter((s) => !params[s]);
101
+ if (!serviceAccountKeyJsonBase64) {
105
102
  throw new Error(
106
- `google-analytics: missing required parameters: ${missing.join(", ")}`
103
+ `google-analytics: missing required parameters: ${parameters.serviceAccountKeyJsonBase64.slug}`
107
104
  );
108
105
  }
109
106
  let serviceAccountKey;
@@ -125,7 +122,7 @@ function createClient(params) {
125
122
  }
126
123
  let cachedToken = null;
127
124
  let tokenExpiresAt = 0;
128
- async function getAccessToken() {
125
+ async function getAccessToken2() {
129
126
  const nowSec = Math.floor(Date.now() / 1e3);
130
127
  if (cachedToken && nowSec < tokenExpiresAt - 60) {
131
128
  return cachedToken;
@@ -156,14 +153,13 @@ function createClient(params) {
156
153
  }
157
154
  return {
158
155
  async request(path2, init) {
159
- const accessToken = await getAccessToken();
160
- const resolvedPath = path2.replace(/\{propertyId\}/g, propertyId);
161
- const url = `${BASE_URL.replace(/\/+$/, "")}/${resolvedPath.replace(/^\/+/, "")}`;
156
+ const accessToken = await getAccessToken2();
157
+ const url = `${BASE_URL.replace(/\/+$/, "")}/${path2.replace(/^\/+/, "")}`;
162
158
  const headers = new Headers(init?.headers);
163
159
  headers.set("Authorization", `Bearer ${accessToken}`);
164
160
  return fetch(url, { ...init, headers });
165
161
  },
166
- async runReport(request) {
162
+ async runReport(propertyId, request) {
167
163
  const response = await this.request(
168
164
  `properties/${propertyId}:runReport`,
169
165
  {
@@ -184,7 +180,7 @@ function createClient(params) {
184
180
  rowCount: data.rowCount ?? 0
185
181
  };
186
182
  },
187
- async getMetadata() {
183
+ async getMetadata(propertyId) {
188
184
  const response = await this.request(
189
185
  `properties/${propertyId}/metadata`,
190
186
  { method: "GET" }
@@ -197,7 +193,7 @@ function createClient(params) {
197
193
  }
198
194
  return await response.json();
199
195
  },
200
- async runRealtimeReport(request) {
196
+ async runRealtimeReport(propertyId, request) {
201
197
  const response = await this.request(
202
198
  `properties/${propertyId}:runRealtimeReport`,
203
199
  {
@@ -280,6 +276,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
280
276
  tools;
281
277
  query;
282
278
  checkConnection;
279
+ /**
280
+ * SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
281
+ * implement this expose a step-by-step exploration flow (database/schema/
282
+ * table/etc. discovery) that the dashboard backend drives via the
283
+ * `/connections/:connectionId/setup` endpoint. Implement by delegating to
284
+ * `runSetupFlow` from `setup-flow.ts`.
285
+ */
286
+ setup;
287
+ /**
288
+ * Opt-out of the default "verify before save" behavior on connection
289
+ * creation. The backend invokes `checkConnection` synchronously while
290
+ * creating the connection and aborts (no row inserted) if it fails — this
291
+ * flag disables that for connectors where the check cannot succeed pre-save:
292
+ *
293
+ * - `squadbase-db` populates `connection-url` only after Neon provisioning
294
+ * - OAuth connectors require an OAuth-aware proxyFetch keyed by the
295
+ * connectionId, which doesn't exist until the row is saved
296
+ *
297
+ * Exceptions are the explicit position; new credential-input connectors get
298
+ * the default verify-on-create behavior without opt-in.
299
+ */
300
+ skipConnectionCheckOnCreate;
283
301
  constructor(config) {
284
302
  this.slug = config.slug;
285
303
  this.authType = config.authType;
@@ -296,6 +314,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
296
314
  this.tools = config.tools;
297
315
  this.query = config.query;
298
316
  this.checkConnection = config.checkConnection;
317
+ this.setup = config.setup;
318
+ this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
299
319
  }
300
320
  get connectorKey() {
301
321
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -360,6 +380,76 @@ var ConnectorPlugin = class _ConnectorPlugin {
360
380
  }
361
381
  };
362
382
 
383
+ // ../connectors/src/setup-flow.ts
384
+ async function runSetupFlow(flow, params, ctx, config) {
385
+ const runtime = {
386
+ params,
387
+ language: ctx.language,
388
+ config
389
+ };
390
+ let state = flow.initialState();
391
+ let answerIdx = 0;
392
+ const pendingParameterUpdates = [];
393
+ for (const step of flow.steps) {
394
+ const ans = ctx.answers[answerIdx];
395
+ if (ans && ans.questionSlug === step.slug) {
396
+ state = step.applyAnswer(state, ans.answer);
397
+ if (step.toParameterUpdates) {
398
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
399
+ }
400
+ answerIdx += 1;
401
+ continue;
402
+ }
403
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
404
+ if (step.type === "text") {
405
+ if (step.fetchOptions) {
406
+ const options2 = await step.fetchOptions(state, runtime);
407
+ if (options2.length === 0) {
408
+ continue;
409
+ }
410
+ }
411
+ return {
412
+ type: "nextQuestion",
413
+ questionSlug: step.slug,
414
+ question: step.question[ctx.language],
415
+ questionType: "text",
416
+ allowFreeText: resolvedAllowFreeText,
417
+ ...pendingParameterUpdates.length > 0 && {
418
+ parameterUpdates: pendingParameterUpdates
419
+ }
420
+ };
421
+ }
422
+ const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
423
+ if (options.length === 0) {
424
+ continue;
425
+ }
426
+ return {
427
+ type: "nextQuestion",
428
+ questionSlug: step.slug,
429
+ question: step.question[ctx.language],
430
+ questionType: step.type,
431
+ options,
432
+ allowFreeText: resolvedAllowFreeText,
433
+ ...pendingParameterUpdates.length > 0 && {
434
+ parameterUpdates: pendingParameterUpdates
435
+ }
436
+ };
437
+ }
438
+ const dataInvestigationResult = await flow.finalize(state, runtime);
439
+ return {
440
+ type: "fulfilled",
441
+ dataInvestigationResult,
442
+ ...pendingParameterUpdates.length > 0 && {
443
+ parameterUpdates: pendingParameterUpdates
444
+ }
445
+ };
446
+ }
447
+ async function resolveSetupSelection(params) {
448
+ const { selected, allSentinel, fetchAll, limit } = params;
449
+ const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
450
+ return resolved.slice(0, limit);
451
+ }
452
+
363
453
  // ../connectors/src/auth-types.ts
364
454
  var AUTH_TYPES = {
365
455
  OAUTH: "oauth",
@@ -380,6 +470,330 @@ var googleAnalyticsOnboarding = new ConnectorOnboarding({
380
470
  }
381
471
  });
382
472
 
473
+ // ../connectors/src/connectors/google-analytics/utils.ts
474
+ import * as crypto2 from "crypto";
475
+ var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
476
+ var ADMIN_BASE_URL = "https://analyticsadmin.googleapis.com/v1beta";
477
+ var DATA_BASE_URL = "https://analyticsdata.googleapis.com/v1beta";
478
+ var SCOPE2 = "https://www.googleapis.com/auth/analytics.readonly";
479
+ function base64url2(input) {
480
+ const buf = typeof input === "string" ? Buffer.from(input) : input;
481
+ return buf.toString("base64url");
482
+ }
483
+ function buildJwt2(clientEmail, privateKey, nowSec) {
484
+ const header = base64url2(JSON.stringify({ alg: "RS256", typ: "JWT" }));
485
+ const payload = base64url2(
486
+ JSON.stringify({
487
+ iss: clientEmail,
488
+ scope: SCOPE2,
489
+ aud: TOKEN_URL2,
490
+ iat: nowSec,
491
+ exp: nowSec + 3600
492
+ })
493
+ );
494
+ const signingInput = `${header}.${payload}`;
495
+ const sign = crypto2.createSign("RSA-SHA256");
496
+ sign.update(signingInput);
497
+ sign.end();
498
+ const signature = base64url2(sign.sign(privateKey));
499
+ return `${signingInput}.${signature}`;
500
+ }
501
+ function decodeServiceAccount(serviceAccountKeyJsonBase64) {
502
+ let serviceAccountKey;
503
+ try {
504
+ const decoded = Buffer.from(
505
+ serviceAccountKeyJsonBase64,
506
+ "base64"
507
+ ).toString("utf-8");
508
+ serviceAccountKey = JSON.parse(decoded);
509
+ } catch {
510
+ throw new Error(
511
+ "google-analytics: failed to decode service account key JSON from base64"
512
+ );
513
+ }
514
+ if (!serviceAccountKey.client_email || !serviceAccountKey.private_key) {
515
+ throw new Error(
516
+ "google-analytics: service account key JSON must contain client_email and private_key"
517
+ );
518
+ }
519
+ return serviceAccountKey;
520
+ }
521
+ async function getAccessToken(serviceAccountKey) {
522
+ const nowSec = Math.floor(Date.now() / 1e3);
523
+ const jwt = buildJwt2(
524
+ serviceAccountKey.client_email,
525
+ serviceAccountKey.private_key,
526
+ nowSec
527
+ );
528
+ const response = await fetch(TOKEN_URL2, {
529
+ method: "POST",
530
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
531
+ body: new URLSearchParams({
532
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
533
+ assertion: jwt
534
+ })
535
+ });
536
+ if (!response.ok) {
537
+ const text = await response.text();
538
+ throw new Error(
539
+ `google-analytics: token exchange failed (${response.status}): ${text}`
540
+ );
541
+ }
542
+ const data = await response.json();
543
+ return data.access_token;
544
+ }
545
+ async function adminFetch(params, path2, init) {
546
+ const keyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
547
+ if (!keyJsonBase64) {
548
+ throw new Error(
549
+ "google-analytics: missing required parameter: service-account-key-json-base64"
550
+ );
551
+ }
552
+ const serviceAccountKey = decodeServiceAccount(keyJsonBase64);
553
+ const accessToken = await getAccessToken(serviceAccountKey);
554
+ const url = `${ADMIN_BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`;
555
+ const headers = new Headers(init?.headers);
556
+ headers.set("Authorization", `Bearer ${accessToken}`);
557
+ return fetch(url, { ...init, headers });
558
+ }
559
+ async function dataFetch(params, path2, init) {
560
+ const keyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
561
+ if (!keyJsonBase64) {
562
+ throw new Error(
563
+ "google-analytics: missing required parameter: service-account-key-json-base64"
564
+ );
565
+ }
566
+ const serviceAccountKey = decodeServiceAccount(keyJsonBase64);
567
+ const accessToken = await getAccessToken(serviceAccountKey);
568
+ const url = `${DATA_BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`;
569
+ const headers = new Headers(init?.headers);
570
+ headers.set("Authorization", `Bearer ${accessToken}`);
571
+ return fetch(url, { ...init, headers });
572
+ }
573
+
574
+ // ../connectors/src/connectors/google-analytics/setup-flow.ts
575
+ var ALL_PROPERTIES = "__ALL_PROPERTIES__";
576
+ var GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES = 20;
577
+ var METADATA_DISPLAY_LIMIT = 30;
578
+ async function listAccountSummaries(params) {
579
+ const summaries = [];
580
+ let pageToken;
581
+ do {
582
+ const path2 = pageToken ? `/accountSummaries?pageToken=${encodeURIComponent(pageToken)}` : `/accountSummaries`;
583
+ const res = await adminFetch(params, path2);
584
+ if (!res.ok) {
585
+ const body = await res.text().catch(() => res.statusText);
586
+ throw new Error(
587
+ `google-analytics: accountSummaries failed (${res.status}): ${body}`
588
+ );
589
+ }
590
+ const data = await res.json();
591
+ summaries.push(...data.accountSummaries ?? []);
592
+ pageToken = data.nextPageToken;
593
+ } while (pageToken);
594
+ return summaries;
595
+ }
596
+ function propertyIdFromResource(resource) {
597
+ return (resource ?? "").replace(/^properties\//, "");
598
+ }
599
+ async function getProperty(params, propertyId) {
600
+ const res = await adminFetch(params, `/properties/${propertyId}`);
601
+ if (!res.ok) return null;
602
+ return await res.json();
603
+ }
604
+ async function getMetadata(params, propertyId) {
605
+ try {
606
+ const res = await dataFetch(
607
+ params,
608
+ `/properties/${propertyId}/metadata`
609
+ );
610
+ if (!res.ok) return { dimensions: [], metrics: [] };
611
+ const data = await res.json();
612
+ return {
613
+ dimensions: data.dimensions ?? [],
614
+ metrics: data.metrics ?? []
615
+ };
616
+ } catch {
617
+ return { dimensions: [], metrics: [] };
618
+ }
619
+ }
620
+ function appendMetadataSection(sections, dimensions, metrics) {
621
+ if (dimensions.length > 0) {
622
+ sections.push(`#### Dimensions (${dimensions.length})`, "");
623
+ for (const d of dimensions.slice(0, METADATA_DISPLAY_LIMIT)) {
624
+ sections.push(`- ${d.apiName ?? d.uiName ?? "(unknown)"}`);
625
+ }
626
+ if (dimensions.length > METADATA_DISPLAY_LIMIT) {
627
+ sections.push(
628
+ `- \u2026and ${dimensions.length - METADATA_DISPLAY_LIMIT} more`
629
+ );
630
+ }
631
+ sections.push("");
632
+ }
633
+ if (metrics.length > 0) {
634
+ sections.push(`#### Metrics (${metrics.length})`, "");
635
+ for (const m of metrics.slice(0, METADATA_DISPLAY_LIMIT)) {
636
+ sections.push(`- ${m.apiName ?? m.uiName ?? "(unknown)"}`);
637
+ }
638
+ if (metrics.length > METADATA_DISPLAY_LIMIT) {
639
+ sections.push(
640
+ `- \u2026and ${metrics.length - METADATA_DISPLAY_LIMIT} more`
641
+ );
642
+ }
643
+ sections.push("");
644
+ }
645
+ }
646
+ var googleAnalyticsSetupFlow = {
647
+ initialState: () => ({}),
648
+ steps: [
649
+ {
650
+ slug: "account",
651
+ type: "select",
652
+ question: {
653
+ ja: "Google Analytics \u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044",
654
+ en: "Select a Google Analytics account"
655
+ },
656
+ async fetchOptions(_state, rt) {
657
+ try {
658
+ const summaries = await listAccountSummaries(rt.params);
659
+ return summaries.map((s) => {
660
+ const accountResource = s.account ?? s.name ?? "";
661
+ if (!accountResource) return null;
662
+ return {
663
+ value: accountResource,
664
+ label: s.displayName ?? accountResource
665
+ };
666
+ }).filter((v) => v != null);
667
+ } catch {
668
+ return [];
669
+ }
670
+ },
671
+ applyAnswer: (state, answer) => ({ ...state, account: answer[0] })
672
+ },
673
+ {
674
+ slug: "properties",
675
+ type: "multiSelect",
676
+ question: {
677
+ 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",
678
+ en: "Select target GA4 properties (multi-select allowed)"
679
+ },
680
+ async fetchOptions(state, rt) {
681
+ if (!state.account) return [];
682
+ try {
683
+ const summaries = await listAccountSummaries(rt.params);
684
+ const account = summaries.find(
685
+ (s) => (s.account ?? s.name) === state.account
686
+ );
687
+ const props = (account?.propertySummaries ?? []).map((p) => {
688
+ const id = propertyIdFromResource(p.property);
689
+ if (!id) return null;
690
+ return {
691
+ value: id,
692
+ label: p.displayName ? `${p.displayName} (${id})` : id
693
+ };
694
+ }).filter((v) => v != null);
695
+ if (props.length === 0) return [];
696
+ return [
697
+ {
698
+ value: ALL_PROPERTIES,
699
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30D1\u30C6\u30A3" : "All properties"
700
+ },
701
+ ...props
702
+ ];
703
+ } catch {
704
+ return [];
705
+ }
706
+ },
707
+ applyAnswer: (state, answer) => ({ ...state, properties: answer })
708
+ },
709
+ {
710
+ slug: "manualPropertyId",
711
+ type: "text",
712
+ question: {
713
+ 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",
714
+ en: "Enter your GA4 Property ID (e.g., 123456789). Found in GA4 Admin > Property Settings."
715
+ },
716
+ async fetchOptions(state) {
717
+ if (state.properties?.length) return [];
718
+ return [{ value: "_show", label: "" }];
719
+ },
720
+ applyAnswer: (state, answer) => ({
721
+ ...state,
722
+ manualPropertyId: answer[0]
723
+ })
724
+ }
725
+ ],
726
+ async finalize(state, rt) {
727
+ const sections = ["## Google Analytics", ""];
728
+ if (state.account && state.properties) {
729
+ let summaries = [];
730
+ try {
731
+ summaries = await listAccountSummaries(rt.params);
732
+ } catch {
733
+ }
734
+ const account = summaries.find(
735
+ (s) => (s.account ?? s.name) === state.account
736
+ );
737
+ const accountLabel = account?.displayName ?? state.account.replace(/^accounts\//, "");
738
+ const targetPropertyIds = await resolveSetupSelection({
739
+ selected: state.properties,
740
+ allSentinel: ALL_PROPERTIES,
741
+ fetchAll: async () => (account?.propertySummaries ?? []).map((p) => propertyIdFromResource(p.property)).filter((v) => v),
742
+ limit: GOOGLE_ANALYTICS_SETUP_MAX_PROPERTIES
743
+ });
744
+ sections.push(`### Account: ${accountLabel}`, "");
745
+ if (targetPropertyIds.length === 0) {
746
+ sections.push("_No properties selected._", "");
747
+ return sections.join("\n");
748
+ }
749
+ sections.push(
750
+ "| Property ID | Display Name | Time Zone | Currency | Industry |"
751
+ );
752
+ sections.push(
753
+ "|-------------|--------------|-----------|----------|----------|"
754
+ );
755
+ for (const pid of targetPropertyIds) {
756
+ const prop = await getProperty(rt.params, pid).catch(() => null);
757
+ const displayName = (prop?.displayName ?? "-").replace(/\|/g, "\\|");
758
+ const timeZone = prop?.timeZone ?? "-";
759
+ const currency = prop?.currencyCode ?? "-";
760
+ const industry = (prop?.industryCategory ?? "-").replace(
761
+ /\|/g,
762
+ "\\|"
763
+ );
764
+ sections.push(
765
+ `| ${pid} | ${displayName} | ${timeZone} | ${currency} | ${industry} |`
766
+ );
767
+ }
768
+ sections.push("");
769
+ const firstPropertyId = targetPropertyIds[0];
770
+ if (firstPropertyId) {
771
+ const { dimensions, metrics } = await getMetadata(
772
+ rt.params,
773
+ firstPropertyId
774
+ );
775
+ appendMetadataSection(sections, dimensions, metrics);
776
+ }
777
+ return sections.join("\n");
778
+ }
779
+ const propertyId = state.manualPropertyId;
780
+ if (propertyId) {
781
+ sections.push(`### Property: ${propertyId}`, "");
782
+ const { dimensions, metrics } = await getMetadata(
783
+ rt.params,
784
+ propertyId
785
+ );
786
+ appendMetadataSection(sections, dimensions, metrics);
787
+ return sections.join("\n");
788
+ }
789
+ sections.push(
790
+ "_Could not list GA4 accounts. Please enable the Google Analytics Admin API in your GCP project. Property ID can be specified per request at runtime._",
791
+ ""
792
+ );
793
+ return sections.join("\n");
794
+ }
795
+ };
796
+
383
797
  // ../connectors/src/connectors/google-analytics/tools/request.ts
384
798
  import { z } from "zod";
385
799
  var BASE_URL2 = "https://analyticsdata.googleapis.com/v1beta/";
@@ -387,8 +801,9 @@ var REQUEST_TIMEOUT_MS = 6e4;
387
801
  var inputSchema = z.object({
388
802
  toolUseIntent: z.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
389
803
  connectionId: z.string().describe("ID of the Google Analytics connection to use"),
804
+ propertyId: z.string().describe("GA4 property ID (e.g., '123456789')"),
390
805
  method: z.enum(["GET", "POST"]).describe("HTTP method"),
391
- path: z.string().describe("API path (e.g., 'properties/{propertyId}:runReport'). {propertyId} is automatically replaced."),
806
+ path: z.string().describe("API path (e.g., 'properties/{propertyId}:runReport'). {propertyId} is replaced with the propertyId parameter."),
392
807
  body: z.record(z.string(), z.unknown()).optional().describe("POST request body (JSON)")
393
808
  });
394
809
  var outputSchema = z.discriminatedUnion("success", [
@@ -406,10 +821,10 @@ var requestTool = new ConnectorTool({
406
821
  name: "request",
407
822
  description: `Send authenticated requests to the Google Analytics Data API.
408
823
  Authentication is handled automatically using a service account.
409
- {propertyId} in the path is automatically replaced with the connection's property-id.`,
824
+ {propertyId} in the path is automatically replaced with the propertyId parameter.`,
410
825
  inputSchema,
411
826
  outputSchema,
412
- async execute({ connectionId, method, path: path2, body }, connections) {
827
+ async execute({ connectionId, propertyId, method, path: path2, body }, connections) {
413
828
  const connection2 = connections.find((c) => c.id === connectionId);
414
829
  if (!connection2) {
415
830
  return { success: false, error: `Connection ${connectionId} not found` };
@@ -418,7 +833,6 @@ Authentication is handled automatically using a service account.
418
833
  try {
419
834
  const { GoogleAuth } = await import("google-auth-library");
420
835
  const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
421
- const propertyId = parameters.propertyId.getValue(connection2);
422
836
  const credentials = JSON.parse(
423
837
  Buffer.from(keyJsonBase64, "base64").toString("utf-8")
424
838
  );
@@ -478,16 +892,16 @@ var googleAnalyticsConnector = new ConnectorPlugin({
478
892
  systemPrompt: {
479
893
  en: `### Tools
480
894
 
481
- - \`google-analytics-service-account_request\`: The only way to call the GA4 Data API. Use it to fetch metadata, run reports, or run realtime reports. See the GA4 Data API Reference below for available endpoints and request bodies.
895
+ - \`google-analytics-service-account_request\`: The only way to call the GA4 Data API. Use it to fetch metadata, run reports, or run realtime reports. Requires a \`propertyId\` parameter. See the GA4 Data API Reference below for available endpoints and request bodies.
482
896
 
483
897
  ### Business Logic
484
898
 
485
899
  The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
486
900
 
487
901
  SDK methods (client created via \`connection(connectionId)\`):
488
- - \`client.runReport(request)\` \u2014 run a GA4 report
489
- - \`client.runRealtimeReport(request)\` \u2014 run a realtime report
490
- - \`client.getMetadata()\` \u2014 fetch available dimensions/metrics
902
+ - \`client.runReport(propertyId, request)\` \u2014 run a GA4 report
903
+ - \`client.runRealtimeReport(propertyId, request)\` \u2014 run a realtime report
904
+ - \`client.getMetadata(propertyId)\` \u2014 fetch available dimensions/metrics
491
905
  - \`client.request(path, init?)\` \u2014 low-level authenticated fetch
492
906
 
493
907
  \`\`\`ts
@@ -497,12 +911,13 @@ import { connection } from "@squadbase/vite-server/connectors/google-analytics";
497
911
  const ga = connection("<connectionId>");
498
912
 
499
913
  export default async function handler(c: Context) {
500
- const { startDate = "7daysAgo", endDate = "today" } = await c.req.json<{
914
+ const { propertyId, startDate = "7daysAgo", endDate = "today" } = await c.req.json<{
915
+ propertyId: string;
501
916
  startDate?: string;
502
917
  endDate?: string;
503
918
  }>();
504
919
 
505
- const { rows } = await ga.runReport({
920
+ const { rows } = await ga.runReport(propertyId, {
506
921
  dateRanges: [{ startDate, endDate }],
507
922
  dimensions: [{ name: "date" }],
508
923
  metrics: [{ name: "activeUsers" }, { name: "sessions" }],
@@ -546,16 +961,16 @@ activeUsers, sessions, screenPageViews, bounceRate, averageSessionDuration, conv
546
961
  - Relative: \`"today"\`, \`"yesterday"\`, \`"7daysAgo"\`, \`"30daysAgo"\``,
547
962
  ja: `### \u30C4\u30FC\u30EB
548
963
 
549
- - \`google-analytics-service-account_request\`: GA4 Data API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30E1\u30BF\u30C7\u30FC\u30BF\u306E\u53D6\u5F97\u3001\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u3001\u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3068\u30EA\u30AF\u30A8\u30B9\u30C8\u30DC\u30C7\u30A3\u306F\u4E0B\u90E8\u306E\u300CGA4 Data API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
964
+ - \`google-analytics-service-account_request\`: GA4 Data API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30E1\u30BF\u30C7\u30FC\u30BF\u306E\u53D6\u5F97\u3001\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u3001\u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\`propertyId\` \u30D1\u30E9\u30E1\u30FC\u30BF\u30FC\u304C\u5FC5\u8981\u3067\u3059\u3002\u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3068\u30EA\u30AF\u30A8\u30B9\u30C8\u30DC\u30C7\u30A3\u306F\u4E0B\u90E8\u306E\u300CGA4 Data API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
550
965
 
551
966
  ### Business Logic
552
967
 
553
968
  \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
554
969
 
555
970
  SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
556
- - \`client.runReport(request)\` \u2014 GA4\u30EC\u30DD\u30FC\u30C8\u3092\u5B9F\u884C
557
- - \`client.runRealtimeReport(request)\` \u2014 \u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30EC\u30DD\u30FC\u30C8\u3092\u5B9F\u884C
558
- - \`client.getMetadata()\` \u2014 \u5229\u7528\u53EF\u80FD\u306A\u30C7\u30A3\u30E1\u30F3\u30B7\u30E7\u30F3/\u30E1\u30C8\u30EA\u30AF\u30B9\u3092\u53D6\u5F97
971
+ - \`client.runReport(propertyId, request)\` \u2014 GA4\u30EC\u30DD\u30FC\u30C8\u3092\u5B9F\u884C
972
+ - \`client.runRealtimeReport(propertyId, request)\` \u2014 \u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30EC\u30DD\u30FC\u30C8\u3092\u5B9F\u884C
973
+ - \`client.getMetadata(propertyId)\` \u2014 \u5229\u7528\u53EF\u80FD\u306A\u30C7\u30A3\u30E1\u30F3\u30B7\u30E7\u30F3/\u30E1\u30C8\u30EA\u30AF\u30B9\u3092\u53D6\u5F97
559
974
  - \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch
560
975
 
561
976
  \`\`\`ts
@@ -565,12 +980,13 @@ import { connection } from "@squadbase/vite-server/connectors/google-analytics";
565
980
  const ga = connection("<connectionId>");
566
981
 
567
982
  export default async function handler(c: Context) {
568
- const { startDate = "7daysAgo", endDate = "today" } = await c.req.json<{
983
+ const { propertyId, startDate = "7daysAgo", endDate = "today" } = await c.req.json<{
984
+ propertyId: string;
569
985
  startDate?: string;
570
986
  endDate?: string;
571
987
  }>();
572
988
 
573
- const { rows } = await ga.runReport({
989
+ const { rows } = await ga.runReport(propertyId, {
574
990
  dateRanges: [{ startDate, endDate }],
575
991
  dimensions: [{ name: "date" }],
576
992
  metrics: [{ name: "activeUsers" }, { name: "sessions" }],
@@ -613,7 +1029,31 @@ activeUsers, sessions, screenPageViews, bounceRate, averageSessionDuration, conv
613
1029
  - \u7D76\u5BFE\u5024: \`"2024-01-01"\`
614
1030
  - \u76F8\u5BFE\u5024: \`"today"\`, \`"yesterday"\`, \`"7daysAgo"\`, \`"30daysAgo"\``
615
1031
  },
616
- tools
1032
+ tools,
1033
+ setup: (params, ctx, config) => runSetupFlow(googleAnalyticsSetupFlow, params, ctx, config),
1034
+ async checkConnection(params, _config) {
1035
+ const keyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
1036
+ if (!keyJsonBase64) {
1037
+ return {
1038
+ success: false,
1039
+ error: "google-analytics: missing service account key"
1040
+ };
1041
+ }
1042
+ try {
1043
+ const res = await dataFetch(params, `/metadata`);
1044
+ if (!res.ok) {
1045
+ const body = await res.text().catch(() => res.statusText);
1046
+ return {
1047
+ success: false,
1048
+ error: `Google Analytics API failed: HTTP ${res.status} ${body}`
1049
+ };
1050
+ }
1051
+ return { success: true };
1052
+ } catch (err) {
1053
+ const msg = err instanceof Error ? err.message : String(err);
1054
+ return { success: false, error: msg };
1055
+ }
1056
+ }
617
1057
  });
618
1058
 
619
1059
  // src/connectors/create-connector-sdk.ts
@@ -642,6 +1082,7 @@ function resolveEnvVarOptional(entry, key) {
642
1082
  import { getContext } from "hono/context-storage";
643
1083
  import { getCookie } from "hono/cookie";
644
1084
  var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
1085
+ var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
645
1086
  function normalizeHeaders(input) {
646
1087
  const out = {};
647
1088
  if (!input) return out;
@@ -650,6 +1091,11 @@ function normalizeHeaders(input) {
650
1091
  });
651
1092
  return out;
652
1093
  }
1094
+ function extractInputUrl(input) {
1095
+ if (typeof input === "string") return input;
1096
+ if (input instanceof URL) return input.href;
1097
+ return input.url;
1098
+ }
653
1099
  function createSandboxProxyFetch(connectionId) {
654
1100
  return async (input, init) => {
655
1101
  const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
@@ -659,10 +1105,17 @@ function createSandboxProxyFetch(connectionId) {
659
1105
  "Connection proxy is not configured. Please check your deployment settings."
660
1106
  );
661
1107
  }
662
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1108
+ const originalUrl = extractInputUrl(input);
1109
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
1110
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
1111
+ const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
1112
+ return fetch(sessionUrl, {
1113
+ method: "POST",
1114
+ headers: { Authorization: `Bearer ${token}` }
1115
+ });
1116
+ }
663
1117
  const originalMethod = init?.method ?? "GET";
664
1118
  const originalBody = init?.body ? JSON.parse(init.body) : void 0;
665
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
666
1119
  const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
667
1120
  return fetch(proxyUrl, {
668
1121
  method: "POST",
@@ -688,10 +1141,9 @@ function createDeployedAppProxyFetch(connectionId) {
688
1141
  }
689
1142
  const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
690
1143
  const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
1144
+ const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
691
1145
  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;
1146
+ const originalUrl = extractInputUrl(input);
695
1147
  const c = getContext();
696
1148
  const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
697
1149
  if (!appSession) {
@@ -699,6 +1151,14 @@ function createDeployedAppProxyFetch(connectionId) {
699
1151
  "No authentication method available for connection proxy."
700
1152
  );
701
1153
  }
1154
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
1155
+ return fetch(sessionUrl, {
1156
+ method: "POST",
1157
+ headers: { Authorization: `Bearer ${appSession}` }
1158
+ });
1159
+ }
1160
+ const originalMethod = init?.method ?? "GET";
1161
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
702
1162
  return fetch(proxyUrl, {
703
1163
  method: "POST",
704
1164
  headers: {