@squadbase/vite-server 0.1.12-dev.93b8799 → 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.
- package/dist/cli/index.js +12128 -934
- package/dist/connectors/airtable-oauth.js +248 -46
- package/dist/connectors/airtable.js +285 -51
- package/dist/connectors/amplitude.js +288 -47
- package/dist/connectors/anthropic.js +126 -47
- package/dist/connectors/asana.js +293 -49
- package/dist/connectors/attio.js +268 -49
- package/dist/connectors/aws-billing.js +253 -46
- package/dist/connectors/azure-sql.js +387 -102
- package/dist/connectors/backlog-api-key.js +283 -47
- package/dist/connectors/clickup.js +304 -49
- package/dist/connectors/cosmosdb.js +271 -50
- package/dist/connectors/customerio.js +285 -47
- package/dist/connectors/dbt.js +306 -47
- package/dist/connectors/freshdesk.js +308 -53
- package/dist/connectors/freshsales.js +299 -52
- package/dist/connectors/freshservice.js +327 -53
- package/dist/connectors/gamma.js +293 -52
- package/dist/connectors/gemini.js +125 -47
- package/dist/connectors/github.js +352 -49
- package/dist/connectors/gmail-oauth.js +170 -7
- package/dist/connectors/gmail.js +316 -47
- package/dist/connectors/google-ads.js +254 -46
- package/dist/connectors/google-analytics-oauth.js +276 -46
- package/dist/connectors/google-analytics.js +378 -49
- package/dist/connectors/google-audit-log.js +404 -47
- package/dist/connectors/google-calendar-oauth.js +225 -46
- package/dist/connectors/google-calendar.js +325 -47
- package/dist/connectors/google-docs.js +186 -6
- package/dist/connectors/google-drive.js +228 -5
- package/dist/connectors/google-search-console-oauth.js +222 -46
- package/dist/connectors/google-sheets.js +238 -47
- package/dist/connectors/google-slides.js +171 -6
- package/dist/connectors/grafana.js +298 -49
- package/dist/connectors/hubspot-oauth.js +174 -5
- package/dist/connectors/hubspot.js +272 -49
- package/dist/connectors/influxdb.js +382 -51
- package/dist/connectors/intercom-oauth.js +176 -5
- package/dist/connectors/intercom.js +268 -49
- package/dist/connectors/jdbc.js +728 -110
- package/dist/connectors/jira-api-key.js +292 -47
- package/dist/connectors/kintone-api-token.js +247 -47
- package/dist/connectors/kintone.js +294 -47
- package/dist/connectors/linear.js +296 -49
- package/dist/connectors/linkedin-ads.js +234 -50
- package/dist/connectors/mailchimp-oauth.js +234 -46
- package/dist/connectors/mailchimp.js +286 -49
- package/dist/connectors/meta-ads-oauth.js +239 -48
- package/dist/connectors/meta-ads.js +251 -50
- package/dist/connectors/mixpanel.js +304 -47
- package/dist/connectors/monday.js +326 -49
- package/dist/connectors/mongodb.js +285 -57
- package/dist/connectors/notion-oauth.js +197 -5
- package/dist/connectors/notion.js +289 -51
- package/dist/connectors/openai.js +125 -47
- package/dist/connectors/oracle.js +405 -103
- package/dist/connectors/outlook-oauth.js +170 -5
- package/dist/connectors/powerbi-oauth.js +217 -5
- package/dist/connectors/salesforce.js +350 -49
- package/dist/connectors/semrush.js +280 -49
- package/dist/connectors/sentry.js +255 -50
- package/dist/connectors/shopify-oauth.js +153 -5
- package/dist/connectors/shopify.js +323 -47
- package/dist/connectors/sqlserver.js +381 -102
- package/dist/connectors/stripe-api-key.js +235 -46
- package/dist/connectors/stripe-oauth.js +168 -5
- package/dist/connectors/supabase.js +269 -48
- package/dist/connectors/tableau.js +337 -206
- package/dist/connectors/tiktok-ads.js +245 -48
- package/dist/connectors/wix-store.js +286 -49
- package/dist/connectors/zendesk-oauth.js +205 -5
- package/dist/connectors/zendesk.js +324 -47
- package/dist/index.d.ts +149 -1
- package/dist/index.js +18297 -6886
- package/dist/main.js +12785 -1382
- package/dist/vite-plugin.js +12140 -936
- 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
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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/salesforce/parameters.ts
|
|
57
|
+
init_parameter_definition();
|
|
46
58
|
var parameters = {
|
|
47
59
|
instanceUrl: new ParameterDefinition({
|
|
48
60
|
slug: "instance-url",
|
|
@@ -280,6 +292,20 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
280
292
|
* `runSetupFlow` from `setup-flow.ts`.
|
|
281
293
|
*/
|
|
282
294
|
setup;
|
|
295
|
+
/**
|
|
296
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
297
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
298
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
299
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
300
|
+
*
|
|
301
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
302
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
303
|
+
* connectionId, which doesn't exist until the row is saved
|
|
304
|
+
*
|
|
305
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
306
|
+
* the default verify-on-create behavior without opt-in.
|
|
307
|
+
*/
|
|
308
|
+
skipConnectionCheckOnCreate;
|
|
283
309
|
constructor(config) {
|
|
284
310
|
this.slug = config.slug;
|
|
285
311
|
this.authType = config.authType;
|
|
@@ -297,6 +323,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
297
323
|
this.query = config.query;
|
|
298
324
|
this.checkConnection = config.checkConnection;
|
|
299
325
|
this.setup = config.setup;
|
|
326
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
300
327
|
}
|
|
301
328
|
get connectorKey() {
|
|
302
329
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -361,6 +388,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
361
388
|
}
|
|
362
389
|
};
|
|
363
390
|
|
|
391
|
+
// ../connectors/src/setup-flow.ts
|
|
392
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
393
|
+
const runtime = {
|
|
394
|
+
params,
|
|
395
|
+
language: ctx.language,
|
|
396
|
+
config
|
|
397
|
+
};
|
|
398
|
+
let state = flow.initialState();
|
|
399
|
+
let answerIdx = 0;
|
|
400
|
+
for (const step of flow.steps) {
|
|
401
|
+
const ans = ctx.answers[answerIdx];
|
|
402
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
403
|
+
state = step.applyAnswer(state, ans.answer);
|
|
404
|
+
answerIdx += 1;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (step.type === "text") {
|
|
408
|
+
return {
|
|
409
|
+
type: "nextQuestion",
|
|
410
|
+
questionSlug: step.slug,
|
|
411
|
+
question: step.question[ctx.language],
|
|
412
|
+
questionType: "text"
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
416
|
+
if (options.length === 0) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
type: "nextQuestion",
|
|
421
|
+
questionSlug: step.slug,
|
|
422
|
+
question: step.question[ctx.language],
|
|
423
|
+
questionType: step.type,
|
|
424
|
+
options
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
428
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
429
|
+
}
|
|
430
|
+
async function resolveSetupSelection(params) {
|
|
431
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
432
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
433
|
+
return resolved.slice(0, limit);
|
|
434
|
+
}
|
|
435
|
+
|
|
364
436
|
// ../connectors/src/auth-types.ts
|
|
365
437
|
var AUTH_TYPES = {
|
|
366
438
|
OAUTH: "oauth",
|
|
@@ -383,6 +455,151 @@ var salesforceOnboarding = new ConnectorOnboarding({
|
|
|
383
455
|
}
|
|
384
456
|
});
|
|
385
457
|
|
|
458
|
+
// ../connectors/src/connectors/salesforce/utils.ts
|
|
459
|
+
var API_VERSION = "v60.0";
|
|
460
|
+
function normalizeInstanceUrl2(raw) {
|
|
461
|
+
const trimmed = raw.trim().replace(/\/+$/, "");
|
|
462
|
+
if (!/^https?:\/\//i.test(trimmed)) {
|
|
463
|
+
return `https://${trimmed}`;
|
|
464
|
+
}
|
|
465
|
+
return trimmed;
|
|
466
|
+
}
|
|
467
|
+
async function getAccessToken(params) {
|
|
468
|
+
const instanceUrlParam = params[parameters.instanceUrl.slug];
|
|
469
|
+
const clientId = params[parameters.clientId.slug];
|
|
470
|
+
const clientSecret = params[parameters.clientSecret.slug];
|
|
471
|
+
if (!instanceUrlParam || !clientId || !clientSecret) {
|
|
472
|
+
throw new Error(
|
|
473
|
+
"salesforce: missing required parameter(s) (instance-url, client-id, client-secret)"
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
const instanceUrl = normalizeInstanceUrl2(instanceUrlParam);
|
|
477
|
+
const tokenBody = new URLSearchParams({
|
|
478
|
+
grant_type: "client_credentials",
|
|
479
|
+
client_id: clientId,
|
|
480
|
+
client_secret: clientSecret
|
|
481
|
+
});
|
|
482
|
+
const tokenRes = await fetch(`${instanceUrl}/services/oauth2/token`, {
|
|
483
|
+
method: "POST",
|
|
484
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
485
|
+
body: tokenBody.toString()
|
|
486
|
+
});
|
|
487
|
+
if (!tokenRes.ok) {
|
|
488
|
+
const errText = await tokenRes.text().catch(() => tokenRes.statusText);
|
|
489
|
+
throw new Error(
|
|
490
|
+
`salesforce: token request failed (${tokenRes.status}): ${errText}`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
const tokenJson = await tokenRes.json();
|
|
494
|
+
if (!tokenJson.access_token) {
|
|
495
|
+
throw new Error("salesforce: access_token missing in token response");
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
accessToken: tokenJson.access_token,
|
|
499
|
+
instanceUrl: tokenJson.instance_url ?? instanceUrl
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
async function apiFetch(params, path2, init) {
|
|
503
|
+
const token = await getAccessToken(params);
|
|
504
|
+
const url = `${token.instanceUrl}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
505
|
+
const headers = new Headers(init?.headers);
|
|
506
|
+
headers.set("Authorization", `Bearer ${token.accessToken}`);
|
|
507
|
+
headers.set("Accept", "application/json");
|
|
508
|
+
return fetch(url, { ...init, headers });
|
|
509
|
+
}
|
|
510
|
+
var SALESFORCE_API_VERSION = API_VERSION;
|
|
511
|
+
|
|
512
|
+
// ../connectors/src/connectors/salesforce/setup-flow.ts
|
|
513
|
+
var SALESFORCE_SETUP_MAX_OBJECTS = 10;
|
|
514
|
+
var SALESFORCE_SETUP_MAX_FIELDS = 50;
|
|
515
|
+
async function listSObjects(params) {
|
|
516
|
+
const res = await apiFetch(
|
|
517
|
+
params,
|
|
518
|
+
`/services/data/${SALESFORCE_API_VERSION}/sobjects/`
|
|
519
|
+
);
|
|
520
|
+
if (!res.ok) {
|
|
521
|
+
const body = await res.text().catch(() => res.statusText);
|
|
522
|
+
throw new Error(
|
|
523
|
+
`salesforce: listSObjects failed (${res.status}): ${body}`
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
const data = await res.json();
|
|
527
|
+
return data.sobjects ?? [];
|
|
528
|
+
}
|
|
529
|
+
async function describeSObject(params, name) {
|
|
530
|
+
const res = await apiFetch(
|
|
531
|
+
params,
|
|
532
|
+
`/services/data/${SALESFORCE_API_VERSION}/sobjects/${name}/describe`
|
|
533
|
+
);
|
|
534
|
+
if (!res.ok) {
|
|
535
|
+
const body = await res.text().catch(() => res.statusText);
|
|
536
|
+
throw new Error(
|
|
537
|
+
`salesforce: describeSObject(${name}) failed (${res.status}): ${body}`
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
const data = await res.json();
|
|
541
|
+
return data.fields ?? [];
|
|
542
|
+
}
|
|
543
|
+
var salesforceSetupFlow = {
|
|
544
|
+
initialState: () => ({}),
|
|
545
|
+
steps: [
|
|
546
|
+
{
|
|
547
|
+
slug: "objects",
|
|
548
|
+
type: "multiSelect",
|
|
549
|
+
question: {
|
|
550
|
+
ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3067\u6982\u89B3\u3057\u305F\u3044Salesforce\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
551
|
+
en: "Select the Salesforce sObjects to include in setup (multi-select allowed)"
|
|
552
|
+
},
|
|
553
|
+
async fetchOptions(_state, rt) {
|
|
554
|
+
const all = await listSObjects(rt.params);
|
|
555
|
+
return all.filter((s) => s.queryable !== false && s.name).map((s) => ({
|
|
556
|
+
value: String(s.name),
|
|
557
|
+
label: s.label ? `${s.label} (${s.name})` : String(s.name)
|
|
558
|
+
}));
|
|
559
|
+
},
|
|
560
|
+
applyAnswer: (state, answer) => ({ ...state, objects: answer })
|
|
561
|
+
}
|
|
562
|
+
],
|
|
563
|
+
async finalize(state, rt) {
|
|
564
|
+
if (!state.objects) {
|
|
565
|
+
throw new Error("Salesforce setup: incomplete state on finalize");
|
|
566
|
+
}
|
|
567
|
+
const targetObjects = await resolveSetupSelection({
|
|
568
|
+
selected: state.objects,
|
|
569
|
+
allSentinel: "__ALL__",
|
|
570
|
+
fetchAll: async () => {
|
|
571
|
+
const all = await listSObjects(rt.params);
|
|
572
|
+
return all.filter((s) => s.queryable !== false && s.name).map((s) => String(s.name));
|
|
573
|
+
},
|
|
574
|
+
limit: SALESFORCE_SETUP_MAX_OBJECTS
|
|
575
|
+
});
|
|
576
|
+
const sections = ["## Salesforce", ""];
|
|
577
|
+
for (const objectName of targetObjects) {
|
|
578
|
+
sections.push(`### sObject: ${objectName}`, "");
|
|
579
|
+
const fields = await describeSObject(rt.params, objectName);
|
|
580
|
+
const limited = fields.slice(0, SALESFORCE_SETUP_MAX_FIELDS);
|
|
581
|
+
sections.push("| Field | Type | Label | Custom |");
|
|
582
|
+
sections.push("|-------|------|-------|--------|");
|
|
583
|
+
for (const f of limited) {
|
|
584
|
+
const name = (f.name ?? "").replace(/\|/g, "\\|");
|
|
585
|
+
const type = (f.type ?? "").replace(/\|/g, "\\|");
|
|
586
|
+
const label = (f.label ?? "").replace(/\|/g, "\\|");
|
|
587
|
+
sections.push(
|
|
588
|
+
`| ${name} | ${type} | ${label || "-"} | ${f.custom ? "yes" : "no"} |`
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
if (fields.length > SALESFORCE_SETUP_MAX_FIELDS) {
|
|
592
|
+
sections.push(
|
|
593
|
+
"",
|
|
594
|
+
`_Showing first ${SALESFORCE_SETUP_MAX_FIELDS} of ${fields.length} fields._`
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
sections.push("");
|
|
598
|
+
}
|
|
599
|
+
return sections.join("\n");
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
|
|
386
603
|
// ../connectors/src/connectors/salesforce/tools/request.ts
|
|
387
604
|
import { z } from "zod";
|
|
388
605
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
@@ -410,7 +627,7 @@ var outputSchema = z.discriminatedUnion("success", [
|
|
|
410
627
|
error: z.string()
|
|
411
628
|
})
|
|
412
629
|
]);
|
|
413
|
-
function
|
|
630
|
+
function normalizeInstanceUrl3(raw) {
|
|
414
631
|
const trimmed = raw.trim().replace(/\/+$/, "");
|
|
415
632
|
if (!/^https?:\/\//i.test(trimmed)) {
|
|
416
633
|
return `https://${trimmed}`;
|
|
@@ -440,7 +657,7 @@ Prefer SOQL via the /query endpoint for filtered, joined, or aggregated reads ra
|
|
|
440
657
|
const instanceUrlParam = parameters.instanceUrl.getValue(connection2);
|
|
441
658
|
const clientId = parameters.clientId.getValue(connection2);
|
|
442
659
|
const clientSecret = parameters.clientSecret.getValue(connection2);
|
|
443
|
-
const instanceUrl =
|
|
660
|
+
const instanceUrl = normalizeInstanceUrl3(instanceUrlParam);
|
|
444
661
|
const tokenBody = new URLSearchParams({
|
|
445
662
|
grant_type: "client_credentials",
|
|
446
663
|
client_id: clientId,
|
|
@@ -669,7 +886,71 @@ export default async function handler(c: Context) {
|
|
|
669
886
|
- \u96C6\u8A08: \`GROUP BY\`, \`HAVING\`, \`COUNT()\`, \`SUM()\`, \`AVG()\`, \`MIN()\`, \`MAX()\`
|
|
670
887
|
- \u89AA\u2192\u5B50\u30B5\u30D6\u30AF\u30A8\u30EA: \`SELECT Id, Name, (SELECT Id, Email FROM Contacts) FROM Account\``
|
|
671
888
|
},
|
|
672
|
-
tools
|
|
889
|
+
tools,
|
|
890
|
+
setup: (params, ctx, config) => runSetupFlow(salesforceSetupFlow, params, ctx, config),
|
|
891
|
+
async checkConnection(params, _config) {
|
|
892
|
+
const instanceUrlParam = params[parameters.instanceUrl.slug];
|
|
893
|
+
const clientId = params[parameters.clientId.slug];
|
|
894
|
+
const clientSecret = params[parameters.clientSecret.slug];
|
|
895
|
+
if (!instanceUrlParam || !clientId || !clientSecret) {
|
|
896
|
+
return {
|
|
897
|
+
success: false,
|
|
898
|
+
error: `Missing required parameter(s): ${parameters.instanceUrl.slug}, ${parameters.clientId.slug}, ${parameters.clientSecret.slug}`
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
const trimmed = instanceUrlParam.trim().replace(/\/+$/, "");
|
|
902
|
+
const instanceUrl = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
903
|
+
try {
|
|
904
|
+
const tokenBody = new URLSearchParams({
|
|
905
|
+
grant_type: "client_credentials",
|
|
906
|
+
client_id: clientId,
|
|
907
|
+
client_secret: clientSecret
|
|
908
|
+
});
|
|
909
|
+
const tokenRes = await fetch(`${instanceUrl}/services/oauth2/token`, {
|
|
910
|
+
method: "POST",
|
|
911
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
912
|
+
body: tokenBody.toString()
|
|
913
|
+
});
|
|
914
|
+
if (!tokenRes.ok) {
|
|
915
|
+
const errText = await tokenRes.text().catch(() => tokenRes.statusText);
|
|
916
|
+
return {
|
|
917
|
+
success: false,
|
|
918
|
+
error: `Salesforce token request failed: HTTP ${tokenRes.status} ${errText}`
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
const tokenJson = await tokenRes.json();
|
|
922
|
+
if (!tokenJson.access_token) {
|
|
923
|
+
return {
|
|
924
|
+
success: false,
|
|
925
|
+
error: "Salesforce: access_token missing in token response"
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
const resolvedInstanceUrl = tokenJson.instance_url ?? instanceUrl;
|
|
929
|
+
const res = await fetch(
|
|
930
|
+
`${resolvedInstanceUrl}/services/data/v60.0/limits`,
|
|
931
|
+
{
|
|
932
|
+
method: "GET",
|
|
933
|
+
headers: {
|
|
934
|
+
Authorization: `Bearer ${tokenJson.access_token}`,
|
|
935
|
+
Accept: "application/json"
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
);
|
|
939
|
+
if (!res.ok) {
|
|
940
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
941
|
+
return {
|
|
942
|
+
success: false,
|
|
943
|
+
error: `Salesforce API failed: HTTP ${res.status} ${errText}`
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
return { success: true };
|
|
947
|
+
} catch (error) {
|
|
948
|
+
return {
|
|
949
|
+
success: false,
|
|
950
|
+
error: error instanceof Error ? error.message : String(error)
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
}
|
|
673
954
|
});
|
|
674
955
|
|
|
675
956
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -698,6 +979,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
698
979
|
import { getContext } from "hono/context-storage";
|
|
699
980
|
import { getCookie } from "hono/cookie";
|
|
700
981
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
982
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
701
983
|
function normalizeHeaders(input) {
|
|
702
984
|
const out = {};
|
|
703
985
|
if (!input) return out;
|
|
@@ -706,6 +988,11 @@ function normalizeHeaders(input) {
|
|
|
706
988
|
});
|
|
707
989
|
return out;
|
|
708
990
|
}
|
|
991
|
+
function extractInputUrl(input) {
|
|
992
|
+
if (typeof input === "string") return input;
|
|
993
|
+
if (input instanceof URL) return input.href;
|
|
994
|
+
return input.url;
|
|
995
|
+
}
|
|
709
996
|
function createSandboxProxyFetch(connectionId) {
|
|
710
997
|
return async (input, init) => {
|
|
711
998
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -715,10 +1002,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
715
1002
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
716
1003
|
);
|
|
717
1004
|
}
|
|
718
|
-
const originalUrl =
|
|
1005
|
+
const originalUrl = extractInputUrl(input);
|
|
1006
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
1007
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1008
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
1009
|
+
return fetch(sessionUrl, {
|
|
1010
|
+
method: "POST",
|
|
1011
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
719
1014
|
const originalMethod = init?.method ?? "GET";
|
|
720
1015
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
721
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
722
1016
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
723
1017
|
return fetch(proxyUrl, {
|
|
724
1018
|
method: "POST",
|
|
@@ -744,10 +1038,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
744
1038
|
}
|
|
745
1039
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
746
1040
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1041
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
747
1042
|
return async (input, init) => {
|
|
748
|
-
const originalUrl =
|
|
749
|
-
const originalMethod = init?.method ?? "GET";
|
|
750
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1043
|
+
const originalUrl = extractInputUrl(input);
|
|
751
1044
|
const c = getContext();
|
|
752
1045
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
753
1046
|
if (!appSession) {
|
|
@@ -755,6 +1048,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
755
1048
|
"No authentication method available for connection proxy."
|
|
756
1049
|
);
|
|
757
1050
|
}
|
|
1051
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1052
|
+
return fetch(sessionUrl, {
|
|
1053
|
+
method: "POST",
|
|
1054
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
const originalMethod = init?.method ?? "GET";
|
|
1058
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
758
1059
|
return fetch(proxyUrl, {
|
|
759
1060
|
method: "POST",
|
|
760
1061
|
headers: {
|