@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,48 +1,60 @@
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/amplitude/parameters.ts
57
+ init_parameter_definition();
46
58
  var parameters = {
47
59
  apiKey: new ParameterDefinition({
48
60
  slug: "api-key",
@@ -216,6 +228,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
216
228
  tools;
217
229
  query;
218
230
  checkConnection;
231
+ /**
232
+ * SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
233
+ * implement this expose a step-by-step exploration flow (database/schema/
234
+ * table/etc. discovery) that the dashboard backend drives via the
235
+ * `/connections/:connectionId/setup` endpoint. Implement by delegating to
236
+ * `runSetupFlow` from `setup-flow.ts`.
237
+ */
238
+ setup;
239
+ /**
240
+ * Opt-out of the default "verify before save" behavior on connection
241
+ * creation. The backend invokes `checkConnection` synchronously while
242
+ * creating the connection and aborts (no row inserted) if it fails — this
243
+ * flag disables that for connectors where the check cannot succeed pre-save:
244
+ *
245
+ * - `squadbase-db` populates `connection-url` only after Neon provisioning
246
+ * - OAuth connectors require an OAuth-aware proxyFetch keyed by the
247
+ * connectionId, which doesn't exist until the row is saved
248
+ *
249
+ * Exceptions are the explicit position; new credential-input connectors get
250
+ * the default verify-on-create behavior without opt-in.
251
+ */
252
+ skipConnectionCheckOnCreate;
219
253
  constructor(config) {
220
254
  this.slug = config.slug;
221
255
  this.authType = config.authType;
@@ -232,6 +266,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
232
266
  this.tools = config.tools;
233
267
  this.query = config.query;
234
268
  this.checkConnection = config.checkConnection;
269
+ this.setup = config.setup;
270
+ this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
235
271
  }
236
272
  get connectorKey() {
237
273
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -296,6 +332,46 @@ var ConnectorPlugin = class _ConnectorPlugin {
296
332
  }
297
333
  };
298
334
 
335
+ // ../connectors/src/setup-flow.ts
336
+ async function runSetupFlow(flow, params, ctx, config) {
337
+ const runtime = {
338
+ params,
339
+ language: ctx.language,
340
+ config
341
+ };
342
+ let state = flow.initialState();
343
+ let answerIdx = 0;
344
+ for (const step of flow.steps) {
345
+ const ans = ctx.answers[answerIdx];
346
+ if (ans && ans.questionSlug === step.slug) {
347
+ state = step.applyAnswer(state, ans.answer);
348
+ answerIdx += 1;
349
+ continue;
350
+ }
351
+ if (step.type === "text") {
352
+ return {
353
+ type: "nextQuestion",
354
+ questionSlug: step.slug,
355
+ question: step.question[ctx.language],
356
+ questionType: "text"
357
+ };
358
+ }
359
+ const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
360
+ if (options.length === 0) {
361
+ continue;
362
+ }
363
+ return {
364
+ type: "nextQuestion",
365
+ questionSlug: step.slug,
366
+ question: step.question[ctx.language],
367
+ questionType: step.type,
368
+ options
369
+ };
370
+ }
371
+ const dataInvestigationResult = await flow.finalize(state, runtime);
372
+ return { type: "fulfilled", dataInvestigationResult };
373
+ }
374
+
299
375
  // ../connectors/src/auth-types.ts
300
376
  var AUTH_TYPES = {
301
377
  OAUTH: "oauth",
@@ -322,6 +398,125 @@ NOTE: The Dashboard REST API endpoints (events/segmentation, funnels, retention,
322
398
  }
323
399
  });
324
400
 
401
+ // ../connectors/src/connectors/amplitude/utils.ts
402
+ function getBaseUrl(params) {
403
+ const region = params[parameters.region.slug];
404
+ return region === "eu" ? "https://analytics.eu.amplitude.com" : "https://amplitude.com";
405
+ }
406
+ async function apiFetch(params, path2, init) {
407
+ const apiKey = params[parameters.apiKey.slug];
408
+ const secretKey = params[parameters.secretKey.slug];
409
+ if (!apiKey || !secretKey) {
410
+ throw new Error(
411
+ "amplitude: missing required parameters: api-key and secret-key"
412
+ );
413
+ }
414
+ const url = `${getBaseUrl(params)}${path2.startsWith("/") ? "" : "/"}${path2}`;
415
+ const headers = new Headers(init?.headers);
416
+ headers.set("Authorization", `Basic ${btoa(`${apiKey}:${secretKey}`)}`);
417
+ return fetch(url, { ...init, headers });
418
+ }
419
+
420
+ // ../connectors/src/connectors/amplitude/setup-flow.ts
421
+ var AMPLITUDE_SETUP_MAX_ENTITIES = 10;
422
+ var AMPLITUDE_SETUP_MAX_EVENTS = 50;
423
+ var ENTITY_LABELS = {
424
+ events: { en: "Events", ja: "Events (\u30A4\u30D9\u30F3\u30C8)" },
425
+ userProperties: {
426
+ en: "User properties",
427
+ ja: "User properties (\u30E6\u30FC\u30B6\u30FC\u30D7\u30ED\u30D1\u30C6\u30A3)"
428
+ },
429
+ cohorts: { en: "Cohorts", ja: "Cohorts (\u30B3\u30DB\u30FC\u30C8)" },
430
+ charts: { en: "Charts", ja: "Charts (\u30C1\u30E3\u30FC\u30C8)" },
431
+ dashboards: { en: "Dashboards", ja: "Dashboards (\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9)" }
432
+ };
433
+ var ENTITY_DESCRIPTIONS = {
434
+ events: {
435
+ en: "Tracked event types with metadata.",
436
+ ja: "\u30E1\u30BF\u30C7\u30FC\u30BF\u4ED8\u304D\u3067\u8FFD\u8DE1\u3055\u308C\u3066\u3044\u308B\u30A4\u30D9\u30F3\u30C8\u30BF\u30A4\u30D7\u3002"
437
+ },
438
+ userProperties: {
439
+ en: "User-level properties used for segmentation.",
440
+ ja: "\u30BB\u30B0\u30E1\u30F3\u30C6\u30FC\u30B7\u30E7\u30F3\u306B\u4F7F\u308F\u308C\u308B\u30E6\u30FC\u30B6\u30FC\u30EC\u30D9\u30EB\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u3002"
441
+ },
442
+ cohorts: {
443
+ en: "Saved user cohorts.",
444
+ ja: "\u4FDD\u5B58\u6E08\u307F\u306E\u30E6\u30FC\u30B6\u30FC\u30B3\u30DB\u30FC\u30C8\u3002"
445
+ },
446
+ charts: {
447
+ en: "Saved analytics charts.",
448
+ ja: "\u4FDD\u5B58\u6E08\u307F\u306E\u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u30C1\u30E3\u30FC\u30C8\u3002"
449
+ },
450
+ dashboards: {
451
+ en: "Saved dashboards.",
452
+ ja: "\u4FDD\u5B58\u6E08\u307F\u306E\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3002"
453
+ }
454
+ };
455
+ var ENTITY_VALUES = [
456
+ "events",
457
+ "userProperties",
458
+ "cohorts",
459
+ "charts",
460
+ "dashboards"
461
+ ];
462
+ async function fetchEventNames(params) {
463
+ try {
464
+ const res = await apiFetch(params, "/api/2/taxonomy/event");
465
+ if (!res.ok) return [];
466
+ const data = await res.json();
467
+ return (data.data ?? []).map((e) => e.event_type ?? "").filter((name) => name);
468
+ } catch {
469
+ return [];
470
+ }
471
+ }
472
+ var amplitudeSetupFlow = {
473
+ initialState: () => ({}),
474
+ steps: [
475
+ {
476
+ slug: "entities",
477
+ type: "multiSelect",
478
+ question: {
479
+ ja: "\u5BFE\u8C61\u306E\u30A8\u30F3\u30C6\u30A3\u30C6\u30A3\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
480
+ en: "Select target entities (multi-select allowed)"
481
+ },
482
+ async fetchOptions(_state, rt) {
483
+ return ENTITY_VALUES.map((value) => ({
484
+ value,
485
+ label: ENTITY_LABELS[value][rt.language]
486
+ }));
487
+ },
488
+ applyAnswer: (state, answer) => ({ ...state, entities: answer })
489
+ }
490
+ ],
491
+ async finalize(state, rt) {
492
+ if (!state.entities) {
493
+ throw new Error("Amplitude setup: incomplete state on finalize");
494
+ }
495
+ const selected = state.entities.filter(
496
+ (e) => ENTITY_VALUES.includes(e)
497
+ ).slice(0, AMPLITUDE_SETUP_MAX_ENTITIES);
498
+ const sections = ["## Amplitude", ""];
499
+ for (const entity of selected) {
500
+ sections.push(`### ${ENTITY_LABELS[entity].en}`, "");
501
+ sections.push(ENTITY_DESCRIPTIONS[entity][rt.language], "");
502
+ if (entity === "events") {
503
+ const names = (await fetchEventNames(rt.params)).slice(
504
+ 0,
505
+ AMPLITUDE_SETUP_MAX_EVENTS
506
+ );
507
+ if (names.length > 0) {
508
+ sections.push("Event names:", "");
509
+ for (const name of names) {
510
+ sections.push(`- ${name}`);
511
+ }
512
+ sections.push("");
513
+ }
514
+ }
515
+ }
516
+ return sections.join("\n");
517
+ }
518
+ };
519
+
325
520
  // ../connectors/src/connectors/amplitude/tools/request.ts
326
521
  import { z } from "zod";
327
522
  var REQUEST_TIMEOUT_MS = 6e4;
@@ -550,7 +745,42 @@ export default async function handler(c: Context) {
550
745
  - \`i\` \u2014 \u30A4\u30F3\u30BF\u30FC\u30D0\u30EB\uFF081=\u65E5\u6B21\u30017=\u9031\u6B21\u300130=\u6708\u6B21\uFF09
551
746
  - \`g\` \u2014 \u30B0\u30EB\u30FC\u30D7\u5316\u30D7\u30ED\u30D1\u30C6\u30A3`
552
747
  },
553
- tools
748
+ tools,
749
+ setup: (params, ctx, config) => runSetupFlow(amplitudeSetupFlow, params, ctx, config),
750
+ async checkConnection(params, _config) {
751
+ const apiKey = params[parameters.apiKey.slug];
752
+ const secretKey = params[parameters.secretKey.slug];
753
+ if (!apiKey || !secretKey) {
754
+ return {
755
+ success: false,
756
+ error: "Missing required parameters: api-key and secret-key"
757
+ };
758
+ }
759
+ const region = params[parameters.region.slug];
760
+ const baseUrl = region === "eu" ? "https://analytics.eu.amplitude.com" : "https://amplitude.com";
761
+ try {
762
+ const res = await fetch(`${baseUrl}/api/2/events/list`, {
763
+ method: "GET",
764
+ headers: {
765
+ Authorization: `Basic ${btoa(`${apiKey}:${secretKey}`)}`,
766
+ Accept: "application/json"
767
+ }
768
+ });
769
+ if (!res.ok) {
770
+ const errorText = await res.text().catch(() => res.statusText);
771
+ return {
772
+ success: false,
773
+ error: `Amplitude API failed: HTTP ${res.status} ${errorText}`
774
+ };
775
+ }
776
+ return { success: true };
777
+ } catch (error) {
778
+ return {
779
+ success: false,
780
+ error: error instanceof Error ? error.message : String(error)
781
+ };
782
+ }
783
+ }
554
784
  });
555
785
 
556
786
  // src/connectors/create-connector-sdk.ts
@@ -579,6 +809,7 @@ function resolveEnvVarOptional(entry, key) {
579
809
  import { getContext } from "hono/context-storage";
580
810
  import { getCookie } from "hono/cookie";
581
811
  var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
812
+ var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
582
813
  function normalizeHeaders(input) {
583
814
  const out = {};
584
815
  if (!input) return out;
@@ -587,6 +818,11 @@ function normalizeHeaders(input) {
587
818
  });
588
819
  return out;
589
820
  }
821
+ function extractInputUrl(input) {
822
+ if (typeof input === "string") return input;
823
+ if (input instanceof URL) return input.href;
824
+ return input.url;
825
+ }
590
826
  function createSandboxProxyFetch(connectionId) {
591
827
  return async (input, init) => {
592
828
  const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
@@ -596,10 +832,17 @@ function createSandboxProxyFetch(connectionId) {
596
832
  "Connection proxy is not configured. Please check your deployment settings."
597
833
  );
598
834
  }
599
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
835
+ const originalUrl = extractInputUrl(input);
836
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
837
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
838
+ const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
839
+ return fetch(sessionUrl, {
840
+ method: "POST",
841
+ headers: { Authorization: `Bearer ${token}` }
842
+ });
843
+ }
600
844
  const originalMethod = init?.method ?? "GET";
601
845
  const originalBody = init?.body ? JSON.parse(init.body) : void 0;
602
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
603
846
  const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
604
847
  return fetch(proxyUrl, {
605
848
  method: "POST",
@@ -625,10 +868,9 @@ function createDeployedAppProxyFetch(connectionId) {
625
868
  }
626
869
  const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
627
870
  const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
871
+ const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
628
872
  return async (input, init) => {
629
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
630
- const originalMethod = init?.method ?? "GET";
631
- const originalBody = init?.body ? JSON.parse(init.body) : void 0;
873
+ const originalUrl = extractInputUrl(input);
632
874
  const c = getContext();
633
875
  const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
634
876
  if (!appSession) {
@@ -636,6 +878,14 @@ function createDeployedAppProxyFetch(connectionId) {
636
878
  "No authentication method available for connection proxy."
637
879
  );
638
880
  }
881
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
882
+ return fetch(sessionUrl, {
883
+ method: "POST",
884
+ headers: { Authorization: `Bearer ${appSession}` }
885
+ });
886
+ }
887
+ const originalMethod = init?.method ?? "GET";
888
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
639
889
  return fetch(proxyUrl, {
640
890
  method: "POST",
641
891
  headers: {
@@ -1,48 +1,60 @@
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/anthropic/parameters.ts
57
+ init_parameter_definition();
46
58
  var parameters = {
47
59
  apiKey: new ParameterDefinition({
48
60
  slug: "api-key",
@@ -83,6 +95,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
83
95
  tools;
84
96
  query;
85
97
  checkConnection;
98
+ /**
99
+ * SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
100
+ * implement this expose a step-by-step exploration flow (database/schema/
101
+ * table/etc. discovery) that the dashboard backend drives via the
102
+ * `/connections/:connectionId/setup` endpoint. Implement by delegating to
103
+ * `runSetupFlow` from `setup-flow.ts`.
104
+ */
105
+ setup;
106
+ /**
107
+ * Opt-out of the default "verify before save" behavior on connection
108
+ * creation. The backend invokes `checkConnection` synchronously while
109
+ * creating the connection and aborts (no row inserted) if it fails — this
110
+ * flag disables that for connectors where the check cannot succeed pre-save:
111
+ *
112
+ * - `squadbase-db` populates `connection-url` only after Neon provisioning
113
+ * - OAuth connectors require an OAuth-aware proxyFetch keyed by the
114
+ * connectionId, which doesn't exist until the row is saved
115
+ *
116
+ * Exceptions are the explicit position; new credential-input connectors get
117
+ * the default verify-on-create behavior without opt-in.
118
+ */
119
+ skipConnectionCheckOnCreate;
86
120
  constructor(config) {
87
121
  this.slug = config.slug;
88
122
  this.authType = config.authType;
@@ -99,6 +133,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
99
133
  this.tools = config.tools;
100
134
  this.query = config.query;
101
135
  this.checkConnection = config.checkConnection;
136
+ this.setup = config.setup;
137
+ this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
102
138
  }
103
139
  get connectorKey() {
104
140
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -234,7 +270,39 @@ export default async function handler(c: Context) {
234
270
  }
235
271
  \`\`\``
236
272
  },
237
- tools
273
+ tools,
274
+ async checkConnection(params, _config) {
275
+ try {
276
+ const apiKey = params[parameters.apiKey.slug];
277
+ if (!apiKey) {
278
+ return {
279
+ success: false,
280
+ error: "Missing required parameter: api-key"
281
+ };
282
+ }
283
+ const res = await fetch("https://api.anthropic.com/v1/models", {
284
+ method: "GET",
285
+ headers: {
286
+ "x-api-key": apiKey,
287
+ "anthropic-version": "2023-06-01",
288
+ Accept: "application/json"
289
+ }
290
+ });
291
+ if (!res.ok) {
292
+ const errorText = await res.text().catch(() => res.statusText);
293
+ return {
294
+ success: false,
295
+ error: `Anthropic API failed: HTTP ${res.status} ${errorText}`
296
+ };
297
+ }
298
+ return { success: true };
299
+ } catch (error) {
300
+ return {
301
+ success: false,
302
+ error: error instanceof Error ? error.message : String(error)
303
+ };
304
+ }
305
+ }
238
306
  });
239
307
 
240
308
  // src/connectors/create-connector-sdk.ts
@@ -263,6 +331,7 @@ function resolveEnvVarOptional(entry, key) {
263
331
  import { getContext } from "hono/context-storage";
264
332
  import { getCookie } from "hono/cookie";
265
333
  var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
334
+ var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
266
335
  function normalizeHeaders(input) {
267
336
  const out = {};
268
337
  if (!input) return out;
@@ -271,6 +340,11 @@ function normalizeHeaders(input) {
271
340
  });
272
341
  return out;
273
342
  }
343
+ function extractInputUrl(input) {
344
+ if (typeof input === "string") return input;
345
+ if (input instanceof URL) return input.href;
346
+ return input.url;
347
+ }
274
348
  function createSandboxProxyFetch(connectionId) {
275
349
  return async (input, init) => {
276
350
  const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
@@ -280,10 +354,17 @@ function createSandboxProxyFetch(connectionId) {
280
354
  "Connection proxy is not configured. Please check your deployment settings."
281
355
  );
282
356
  }
283
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
357
+ const originalUrl = extractInputUrl(input);
358
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
359
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
360
+ const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
361
+ return fetch(sessionUrl, {
362
+ method: "POST",
363
+ headers: { Authorization: `Bearer ${token}` }
364
+ });
365
+ }
284
366
  const originalMethod = init?.method ?? "GET";
285
367
  const originalBody = init?.body ? JSON.parse(init.body) : void 0;
286
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
287
368
  const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
288
369
  return fetch(proxyUrl, {
289
370
  method: "POST",
@@ -309,10 +390,9 @@ function createDeployedAppProxyFetch(connectionId) {
309
390
  }
310
391
  const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
311
392
  const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
393
+ const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
312
394
  return async (input, init) => {
313
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
314
- const originalMethod = init?.method ?? "GET";
315
- const originalBody = init?.body ? JSON.parse(init.body) : void 0;
395
+ const originalUrl = extractInputUrl(input);
316
396
  const c = getContext();
317
397
  const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
318
398
  if (!appSession) {
@@ -320,6 +400,14 @@ function createDeployedAppProxyFetch(connectionId) {
320
400
  "No authentication method available for connection proxy."
321
401
  );
322
402
  }
403
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
404
+ return fetch(sessionUrl, {
405
+ method: "POST",
406
+ headers: { Authorization: `Bearer ${appSession}` }
407
+ });
408
+ }
409
+ const originalMethod = init?.method ?? "GET";
410
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
323
411
  return fetch(proxyUrl, {
324
412
  method: "POST",
325
413
  headers: {