@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.
- package/dist/cli/index.js +14375 -1652
- package/dist/connectors/airtable-oauth.js +282 -46
- package/dist/connectors/airtable.js +319 -51
- package/dist/connectors/amplitude.js +322 -47
- package/dist/connectors/anthropic.js +135 -47
- package/dist/connectors/asana.js +327 -49
- package/dist/connectors/attio.js +302 -49
- package/dist/connectors/aws-billing.js +287 -46
- package/dist/connectors/azure-sql.js +421 -102
- package/dist/connectors/backlog-api-key.js +317 -47
- package/dist/connectors/clickup.js +338 -49
- package/dist/connectors/cosmosdb.js +305 -50
- package/dist/connectors/customerio.js +319 -47
- package/dist/connectors/dbt.js +340 -47
- package/dist/connectors/freshdesk.js +342 -53
- package/dist/connectors/freshsales.js +333 -52
- package/dist/connectors/freshservice.js +361 -53
- package/dist/connectors/gamma.js +327 -52
- package/dist/connectors/gemini.js +134 -47
- package/dist/connectors/github.js +386 -49
- package/dist/connectors/gmail-oauth.js +204 -7
- package/dist/connectors/gmail.js +350 -47
- package/dist/connectors/google-ads.js +288 -46
- package/dist/connectors/google-analytics-oauth.js +310 -46
- package/dist/connectors/google-analytics.js +547 -87
- package/dist/connectors/google-audit-log.js +438 -47
- package/dist/connectors/google-calendar-oauth.js +259 -46
- package/dist/connectors/google-calendar.js +359 -47
- package/dist/connectors/google-docs.js +220 -6
- package/dist/connectors/google-drive.js +262 -5
- package/dist/connectors/google-search-console-oauth.js +256 -46
- package/dist/connectors/google-sheets.js +272 -47
- package/dist/connectors/google-slides.js +205 -6
- package/dist/connectors/grafana.js +332 -49
- package/dist/connectors/hubspot-oauth.js +208 -5
- package/dist/connectors/hubspot.js +306 -49
- package/dist/connectors/influxdb.js +416 -51
- package/dist/connectors/intercom-oauth.js +210 -5
- package/dist/connectors/intercom.js +302 -49
- package/dist/connectors/jdbc.js +762 -110
- package/dist/connectors/jira-api-key.js +326 -47
- package/dist/connectors/kintone-api-token.js +281 -47
- package/dist/connectors/kintone.js +328 -47
- package/dist/connectors/linear.js +330 -49
- package/dist/connectors/linkedin-ads.js +268 -50
- package/dist/connectors/mailchimp-oauth.js +268 -46
- package/dist/connectors/mailchimp.js +320 -49
- package/dist/connectors/meta-ads-oauth.js +273 -48
- package/dist/connectors/meta-ads.js +285 -50
- package/dist/connectors/mixpanel.js +338 -47
- package/dist/connectors/monday.js +360 -49
- package/dist/connectors/mongodb.js +319 -57
- package/dist/connectors/notion-oauth.js +231 -5
- package/dist/connectors/notion.js +323 -51
- package/dist/connectors/openai.js +134 -47
- package/dist/connectors/oracle.js +454 -103
- package/dist/connectors/outlook-oauth.js +204 -5
- package/dist/connectors/powerbi-oauth.js +498 -5
- package/dist/connectors/salesforce.js +384 -49
- package/dist/connectors/semrush.js +609 -49
- package/dist/connectors/sentry.js +289 -50
- package/dist/connectors/shopify-oauth.js +187 -5
- package/dist/connectors/shopify.js +357 -47
- package/dist/connectors/sqlserver.js +415 -102
- package/dist/connectors/stripe-api-key.js +269 -46
- package/dist/connectors/stripe-oauth.js +202 -5
- package/dist/connectors/supabase.js +303 -48
- package/dist/connectors/tableau.js +536 -163
- package/dist/connectors/tiktok-ads.js +279 -48
- package/dist/connectors/wix-store.js +320 -49
- package/dist/connectors/zendesk-oauth.js +239 -5
- package/dist/connectors/zendesk.js +358 -47
- package/dist/index.d.ts +149 -1
- package/dist/index.js +15057 -2117
- package/dist/main.js +15005 -2073
- package/dist/vite-plugin.js +14752 -2019
- package/package.json +1 -1
package/dist/connectors/asana.js
CHANGED
|
@@ -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/asana/parameters.ts
|
|
57
|
+
init_parameter_definition();
|
|
46
58
|
var parameters = {
|
|
47
59
|
personalAccessToken: new ParameterDefinition({
|
|
48
60
|
slug: "personal-access-token",
|
|
@@ -253,6 +265,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
253
265
|
tools;
|
|
254
266
|
query;
|
|
255
267
|
checkConnection;
|
|
268
|
+
/**
|
|
269
|
+
* SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
|
|
270
|
+
* implement this expose a step-by-step exploration flow (database/schema/
|
|
271
|
+
* table/etc. discovery) that the dashboard backend drives via the
|
|
272
|
+
* `/connections/:connectionId/setup` endpoint. Implement by delegating to
|
|
273
|
+
* `runSetupFlow` from `setup-flow.ts`.
|
|
274
|
+
*/
|
|
275
|
+
setup;
|
|
276
|
+
/**
|
|
277
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
278
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
279
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
280
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
281
|
+
*
|
|
282
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
283
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
284
|
+
* connectionId, which doesn't exist until the row is saved
|
|
285
|
+
*
|
|
286
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
287
|
+
* the default verify-on-create behavior without opt-in.
|
|
288
|
+
*/
|
|
289
|
+
skipConnectionCheckOnCreate;
|
|
256
290
|
constructor(config) {
|
|
257
291
|
this.slug = config.slug;
|
|
258
292
|
this.authType = config.authType;
|
|
@@ -269,6 +303,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
269
303
|
this.tools = config.tools;
|
|
270
304
|
this.query = config.query;
|
|
271
305
|
this.checkConnection = config.checkConnection;
|
|
306
|
+
this.setup = config.setup;
|
|
307
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
272
308
|
}
|
|
273
309
|
get connectorKey() {
|
|
274
310
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -333,6 +369,76 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
333
369
|
}
|
|
334
370
|
};
|
|
335
371
|
|
|
372
|
+
// ../connectors/src/setup-flow.ts
|
|
373
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
374
|
+
const runtime = {
|
|
375
|
+
params,
|
|
376
|
+
language: ctx.language,
|
|
377
|
+
config
|
|
378
|
+
};
|
|
379
|
+
let state = flow.initialState();
|
|
380
|
+
let answerIdx = 0;
|
|
381
|
+
const pendingParameterUpdates = [];
|
|
382
|
+
for (const step of flow.steps) {
|
|
383
|
+
const ans = ctx.answers[answerIdx];
|
|
384
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
385
|
+
state = step.applyAnswer(state, ans.answer);
|
|
386
|
+
if (step.toParameterUpdates) {
|
|
387
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
388
|
+
}
|
|
389
|
+
answerIdx += 1;
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
393
|
+
if (step.type === "text") {
|
|
394
|
+
if (step.fetchOptions) {
|
|
395
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
396
|
+
if (options2.length === 0) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
type: "nextQuestion",
|
|
402
|
+
questionSlug: step.slug,
|
|
403
|
+
question: step.question[ctx.language],
|
|
404
|
+
questionType: "text",
|
|
405
|
+
allowFreeText: resolvedAllowFreeText,
|
|
406
|
+
...pendingParameterUpdates.length > 0 && {
|
|
407
|
+
parameterUpdates: pendingParameterUpdates
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
412
|
+
if (options.length === 0) {
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
type: "nextQuestion",
|
|
417
|
+
questionSlug: step.slug,
|
|
418
|
+
question: step.question[ctx.language],
|
|
419
|
+
questionType: step.type,
|
|
420
|
+
options,
|
|
421
|
+
allowFreeText: resolvedAllowFreeText,
|
|
422
|
+
...pendingParameterUpdates.length > 0 && {
|
|
423
|
+
parameterUpdates: pendingParameterUpdates
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
428
|
+
return {
|
|
429
|
+
type: "fulfilled",
|
|
430
|
+
dataInvestigationResult,
|
|
431
|
+
...pendingParameterUpdates.length > 0 && {
|
|
432
|
+
parameterUpdates: pendingParameterUpdates
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
async function resolveSetupSelection(params) {
|
|
437
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
438
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
439
|
+
return resolved.slice(0, limit);
|
|
440
|
+
}
|
|
441
|
+
|
|
336
442
|
// ../connectors/src/auth-types.ts
|
|
337
443
|
var AUTH_TYPES = {
|
|
338
444
|
OAUTH: "oauth",
|
|
@@ -367,11 +473,131 @@ var asanaOnboarding = new ConnectorOnboarding({
|
|
|
367
473
|
}
|
|
368
474
|
});
|
|
369
475
|
|
|
476
|
+
// ../connectors/src/connectors/asana/utils.ts
|
|
477
|
+
var BASE_URL2 = "https://app.asana.com/api/1.0";
|
|
478
|
+
async function apiFetch(params, path2, init) {
|
|
479
|
+
const token = params[parameters.personalAccessToken.slug];
|
|
480
|
+
if (!token) {
|
|
481
|
+
throw new Error("asana: missing required parameter: personal-access-token");
|
|
482
|
+
}
|
|
483
|
+
const url = `${BASE_URL2}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
484
|
+
const headers = new Headers(init?.headers);
|
|
485
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
486
|
+
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
487
|
+
return fetch(url, { ...init, headers });
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ../connectors/src/connectors/asana/setup-flow.ts
|
|
491
|
+
var ALL_PROJECTS = "__ALL_PROJECTS__";
|
|
492
|
+
var ASANA_SETUP_MAX_PROJECTS = 20;
|
|
493
|
+
async function listWorkspaces(params) {
|
|
494
|
+
const res = await apiFetch(params, "/workspaces?limit=100");
|
|
495
|
+
if (!res.ok) {
|
|
496
|
+
const body = await res.text().catch(() => res.statusText);
|
|
497
|
+
throw new Error(`asana: listWorkspaces failed (${res.status}): ${body}`);
|
|
498
|
+
}
|
|
499
|
+
const data = await res.json();
|
|
500
|
+
return data.data ?? [];
|
|
501
|
+
}
|
|
502
|
+
async function listProjects(params, workspaceGid) {
|
|
503
|
+
const all = [];
|
|
504
|
+
const fields = encodeURIComponent("name,team.name,current_status");
|
|
505
|
+
let offset;
|
|
506
|
+
while (all.length < 500) {
|
|
507
|
+
const query = `workspace=${encodeURIComponent(workspaceGid)}&limit=100&opt_fields=${fields}${offset ? `&offset=${encodeURIComponent(offset)}` : ""}`;
|
|
508
|
+
const res = await apiFetch(params, `/projects?${query}`);
|
|
509
|
+
if (!res.ok) {
|
|
510
|
+
const body = await res.text().catch(() => res.statusText);
|
|
511
|
+
throw new Error(`asana: listProjects failed (${res.status}): ${body}`);
|
|
512
|
+
}
|
|
513
|
+
const data = await res.json();
|
|
514
|
+
const batch = data.data ?? [];
|
|
515
|
+
all.push(...batch);
|
|
516
|
+
const next = data.next_page?.offset;
|
|
517
|
+
if (!next || batch.length === 0) break;
|
|
518
|
+
offset = next;
|
|
519
|
+
}
|
|
520
|
+
return all;
|
|
521
|
+
}
|
|
522
|
+
var asanaSetupFlow = {
|
|
523
|
+
initialState: () => ({}),
|
|
524
|
+
steps: [
|
|
525
|
+
{
|
|
526
|
+
slug: "workspace",
|
|
527
|
+
type: "select",
|
|
528
|
+
question: {
|
|
529
|
+
ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u306B\u4F7F\u3046\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044",
|
|
530
|
+
en: "Select the workspace to use for setup"
|
|
531
|
+
},
|
|
532
|
+
async fetchOptions(_state, rt) {
|
|
533
|
+
const workspaces = await listWorkspaces(rt.params);
|
|
534
|
+
return workspaces.filter((w) => w.gid && w.name).map((w) => ({ value: w.gid, label: w.name }));
|
|
535
|
+
},
|
|
536
|
+
applyAnswer: (state, answer) => ({ ...state, workspace: answer[0] })
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
slug: "projects",
|
|
540
|
+
type: "multiSelect",
|
|
541
|
+
question: {
|
|
542
|
+
ja: "\u5BFE\u8C61\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
543
|
+
en: "Select target projects (multi-select allowed)"
|
|
544
|
+
},
|
|
545
|
+
async fetchOptions(state, rt) {
|
|
546
|
+
if (!state.workspace) return [];
|
|
547
|
+
const projects = await listProjects(rt.params, state.workspace);
|
|
548
|
+
const projectOptions = projects.filter((p) => p.gid && p.name).map((p) => ({ value: p.gid, label: p.name }));
|
|
549
|
+
return [
|
|
550
|
+
{
|
|
551
|
+
value: ALL_PROJECTS,
|
|
552
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8" : "All projects"
|
|
553
|
+
},
|
|
554
|
+
...projectOptions
|
|
555
|
+
];
|
|
556
|
+
},
|
|
557
|
+
applyAnswer: (state, answer) => ({ ...state, projects: answer })
|
|
558
|
+
}
|
|
559
|
+
],
|
|
560
|
+
async finalize(state, rt) {
|
|
561
|
+
if (!state.workspace || !state.projects) {
|
|
562
|
+
throw new Error("Asana setup: incomplete state on finalize");
|
|
563
|
+
}
|
|
564
|
+
const workspaceGid = state.workspace;
|
|
565
|
+
const projects = await listProjects(rt.params, workspaceGid);
|
|
566
|
+
const projectByGid = new Map(projects.map((p) => [p.gid, p]));
|
|
567
|
+
const targetGids = await resolveSetupSelection({
|
|
568
|
+
selected: state.projects,
|
|
569
|
+
allSentinel: ALL_PROJECTS,
|
|
570
|
+
fetchAll: async () => projects.map((p) => p.gid).filter((g) => g),
|
|
571
|
+
limit: ASANA_SETUP_MAX_PROJECTS
|
|
572
|
+
});
|
|
573
|
+
const sections = [
|
|
574
|
+
"## Asana",
|
|
575
|
+
"",
|
|
576
|
+
`### Workspace: ${workspaceGid}`,
|
|
577
|
+
""
|
|
578
|
+
];
|
|
579
|
+
for (const gid of targetGids) {
|
|
580
|
+
const project = projectByGid.get(gid);
|
|
581
|
+
if (!project) {
|
|
582
|
+
sections.push(`#### Project: ${gid}`, "", "_Not found._", "");
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
sections.push(`#### Project: ${project.name}`, "");
|
|
586
|
+
const team = project.team?.name ?? "-";
|
|
587
|
+
const status = project.current_status?.title ?? project.current_status?.text ?? "-";
|
|
588
|
+
sections.push(`- Team: ${team}`);
|
|
589
|
+
sections.push(`- Current status: ${status}`);
|
|
590
|
+
sections.push("");
|
|
591
|
+
}
|
|
592
|
+
return sections.join("\n");
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
|
|
370
596
|
// ../connectors/src/connectors/asana/tools/request.ts
|
|
371
597
|
import { z } from "zod";
|
|
372
598
|
var BASE_HOST = "https://app.asana.com";
|
|
373
599
|
var BASE_PATH_SEGMENT = "/api/1.0";
|
|
374
|
-
var
|
|
600
|
+
var BASE_URL3 = `${BASE_HOST}${BASE_PATH_SEGMENT}`;
|
|
375
601
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
376
602
|
var inputSchema = z.object({
|
|
377
603
|
toolUseIntent: z.string().optional().describe(
|
|
@@ -436,7 +662,7 @@ Pagination: Use limit (1-100) and offset query parameters. The response includes
|
|
|
436
662
|
try {
|
|
437
663
|
const token = parameters.personalAccessToken.getValue(connection2);
|
|
438
664
|
const normalizedPath = normalizeRequestPath(path2, BASE_PATH_SEGMENT);
|
|
439
|
-
const url = `${
|
|
665
|
+
const url = `${BASE_URL3}${normalizedPath}`;
|
|
440
666
|
const controller = new AbortController();
|
|
441
667
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
442
668
|
try {
|
|
@@ -655,7 +881,39 @@ export default async function handler(c: Context) {
|
|
|
655
881
|
- \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8: name, archived, created_at, modified_at, owner, owner.name, team, team.name, members
|
|
656
882
|
- \u30E6\u30FC\u30B6\u30FC: name, email, photo`
|
|
657
883
|
},
|
|
658
|
-
tools
|
|
884
|
+
tools,
|
|
885
|
+
setup: (params, ctx, config) => runSetupFlow(asanaSetupFlow, params, ctx, config),
|
|
886
|
+
async checkConnection(params, _config) {
|
|
887
|
+
try {
|
|
888
|
+
const pat = params[parameters.personalAccessToken.slug];
|
|
889
|
+
if (!pat) {
|
|
890
|
+
return {
|
|
891
|
+
success: false,
|
|
892
|
+
error: "Missing required parameter: personal-access-token"
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
const res = await fetch("https://app.asana.com/api/1.0/users/me", {
|
|
896
|
+
method: "GET",
|
|
897
|
+
headers: {
|
|
898
|
+
Authorization: `Bearer ${pat}`,
|
|
899
|
+
Accept: "application/json"
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
if (!res.ok) {
|
|
903
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
904
|
+
return {
|
|
905
|
+
success: false,
|
|
906
|
+
error: `Asana API failed: HTTP ${res.status} ${errorText}`
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
return { success: true };
|
|
910
|
+
} catch (error) {
|
|
911
|
+
return {
|
|
912
|
+
success: false,
|
|
913
|
+
error: error instanceof Error ? error.message : String(error)
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
}
|
|
659
917
|
});
|
|
660
918
|
|
|
661
919
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -684,6 +942,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
684
942
|
import { getContext } from "hono/context-storage";
|
|
685
943
|
import { getCookie } from "hono/cookie";
|
|
686
944
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
945
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
687
946
|
function normalizeHeaders(input) {
|
|
688
947
|
const out = {};
|
|
689
948
|
if (!input) return out;
|
|
@@ -692,6 +951,11 @@ function normalizeHeaders(input) {
|
|
|
692
951
|
});
|
|
693
952
|
return out;
|
|
694
953
|
}
|
|
954
|
+
function extractInputUrl(input) {
|
|
955
|
+
if (typeof input === "string") return input;
|
|
956
|
+
if (input instanceof URL) return input.href;
|
|
957
|
+
return input.url;
|
|
958
|
+
}
|
|
695
959
|
function createSandboxProxyFetch(connectionId) {
|
|
696
960
|
return async (input, init) => {
|
|
697
961
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -701,10 +965,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
701
965
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
702
966
|
);
|
|
703
967
|
}
|
|
704
|
-
const originalUrl =
|
|
968
|
+
const originalUrl = extractInputUrl(input);
|
|
969
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
970
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
971
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
972
|
+
return fetch(sessionUrl, {
|
|
973
|
+
method: "POST",
|
|
974
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
975
|
+
});
|
|
976
|
+
}
|
|
705
977
|
const originalMethod = init?.method ?? "GET";
|
|
706
978
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
707
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
708
979
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
709
980
|
return fetch(proxyUrl, {
|
|
710
981
|
method: "POST",
|
|
@@ -730,10 +1001,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
730
1001
|
}
|
|
731
1002
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
732
1003
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1004
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
733
1005
|
return async (input, init) => {
|
|
734
|
-
const originalUrl =
|
|
735
|
-
const originalMethod = init?.method ?? "GET";
|
|
736
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1006
|
+
const originalUrl = extractInputUrl(input);
|
|
737
1007
|
const c = getContext();
|
|
738
1008
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
739
1009
|
if (!appSession) {
|
|
@@ -741,6 +1011,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
741
1011
|
"No authentication method available for connection proxy."
|
|
742
1012
|
);
|
|
743
1013
|
}
|
|
1014
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1015
|
+
return fetch(sessionUrl, {
|
|
1016
|
+
method: "POST",
|
|
1017
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
const originalMethod = init?.method ?? "GET";
|
|
1021
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
744
1022
|
return fetch(proxyUrl, {
|
|
745
1023
|
method: "POST",
|
|
746
1024
|
headers: {
|