@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
|
@@ -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/airtable/parameters.ts
|
|
57
|
+
init_parameter_definition();
|
|
46
58
|
var parameters = {
|
|
47
59
|
baseId: new ParameterDefinition({
|
|
48
60
|
slug: "base-id",
|
|
@@ -176,7 +188,7 @@ function createClient(params) {
|
|
|
176
188
|
}
|
|
177
189
|
return await response.json();
|
|
178
190
|
}
|
|
179
|
-
async function
|
|
191
|
+
async function listTables2() {
|
|
180
192
|
const path2 = `/meta/bases/${baseId}/tables`;
|
|
181
193
|
const response = await request(path2);
|
|
182
194
|
if (!response.ok) {
|
|
@@ -193,7 +205,7 @@ function createClient(params) {
|
|
|
193
205
|
getRecord,
|
|
194
206
|
createRecords,
|
|
195
207
|
updateRecords,
|
|
196
|
-
listTables
|
|
208
|
+
listTables: listTables2
|
|
197
209
|
};
|
|
198
210
|
}
|
|
199
211
|
|
|
@@ -256,6 +268,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
256
268
|
tools;
|
|
257
269
|
query;
|
|
258
270
|
checkConnection;
|
|
271
|
+
/**
|
|
272
|
+
* SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
|
|
273
|
+
* implement this expose a step-by-step exploration flow (database/schema/
|
|
274
|
+
* table/etc. discovery) that the dashboard backend drives via the
|
|
275
|
+
* `/connections/:connectionId/setup` endpoint. Implement by delegating to
|
|
276
|
+
* `runSetupFlow` from `setup-flow.ts`.
|
|
277
|
+
*/
|
|
278
|
+
setup;
|
|
279
|
+
/**
|
|
280
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
281
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
282
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
283
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
284
|
+
*
|
|
285
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
286
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
287
|
+
* connectionId, which doesn't exist until the row is saved
|
|
288
|
+
*
|
|
289
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
290
|
+
* the default verify-on-create behavior without opt-in.
|
|
291
|
+
*/
|
|
292
|
+
skipConnectionCheckOnCreate;
|
|
259
293
|
constructor(config) {
|
|
260
294
|
this.slug = config.slug;
|
|
261
295
|
this.authType = config.authType;
|
|
@@ -272,6 +306,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
272
306
|
this.tools = config.tools;
|
|
273
307
|
this.query = config.query;
|
|
274
308
|
this.checkConnection = config.checkConnection;
|
|
309
|
+
this.setup = config.setup;
|
|
310
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
275
311
|
}
|
|
276
312
|
get connectorKey() {
|
|
277
313
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -336,6 +372,76 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
336
372
|
}
|
|
337
373
|
};
|
|
338
374
|
|
|
375
|
+
// ../connectors/src/setup-flow.ts
|
|
376
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
377
|
+
const runtime = {
|
|
378
|
+
params,
|
|
379
|
+
language: ctx.language,
|
|
380
|
+
config
|
|
381
|
+
};
|
|
382
|
+
let state = flow.initialState();
|
|
383
|
+
let answerIdx = 0;
|
|
384
|
+
const pendingParameterUpdates = [];
|
|
385
|
+
for (const step of flow.steps) {
|
|
386
|
+
const ans = ctx.answers[answerIdx];
|
|
387
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
388
|
+
state = step.applyAnswer(state, ans.answer);
|
|
389
|
+
if (step.toParameterUpdates) {
|
|
390
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
391
|
+
}
|
|
392
|
+
answerIdx += 1;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
396
|
+
if (step.type === "text") {
|
|
397
|
+
if (step.fetchOptions) {
|
|
398
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
399
|
+
if (options2.length === 0) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
type: "nextQuestion",
|
|
405
|
+
questionSlug: step.slug,
|
|
406
|
+
question: step.question[ctx.language],
|
|
407
|
+
questionType: "text",
|
|
408
|
+
allowFreeText: resolvedAllowFreeText,
|
|
409
|
+
...pendingParameterUpdates.length > 0 && {
|
|
410
|
+
parameterUpdates: pendingParameterUpdates
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
415
|
+
if (options.length === 0) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
type: "nextQuestion",
|
|
420
|
+
questionSlug: step.slug,
|
|
421
|
+
question: step.question[ctx.language],
|
|
422
|
+
questionType: step.type,
|
|
423
|
+
options,
|
|
424
|
+
allowFreeText: resolvedAllowFreeText,
|
|
425
|
+
...pendingParameterUpdates.length > 0 && {
|
|
426
|
+
parameterUpdates: pendingParameterUpdates
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
431
|
+
return {
|
|
432
|
+
type: "fulfilled",
|
|
433
|
+
dataInvestigationResult,
|
|
434
|
+
...pendingParameterUpdates.length > 0 && {
|
|
435
|
+
parameterUpdates: pendingParameterUpdates
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
async function resolveSetupSelection(params) {
|
|
440
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
441
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
442
|
+
return resolved.slice(0, limit);
|
|
443
|
+
}
|
|
444
|
+
|
|
339
445
|
// ../connectors/src/auth-types.ts
|
|
340
446
|
var AUTH_TYPES = {
|
|
341
447
|
OAUTH: "oauth",
|
|
@@ -356,9 +462,119 @@ var airtableOnboarding = new ConnectorOnboarding({
|
|
|
356
462
|
}
|
|
357
463
|
});
|
|
358
464
|
|
|
465
|
+
// ../connectors/src/connectors/airtable/utils.ts
|
|
466
|
+
var BASE_URL2 = "https://api.airtable.com/v0";
|
|
467
|
+
async function apiFetch(params, path2, init) {
|
|
468
|
+
const apiKey = params[parameters.apiKey.slug];
|
|
469
|
+
if (!apiKey) {
|
|
470
|
+
throw new Error("airtable: missing required parameter: api-key");
|
|
471
|
+
}
|
|
472
|
+
const url = `${BASE_URL2}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
473
|
+
const headers = new Headers(init?.headers);
|
|
474
|
+
headers.set("Authorization", `Bearer ${apiKey}`);
|
|
475
|
+
return fetch(url, { ...init, headers });
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ../connectors/src/connectors/airtable/setup-flow.ts
|
|
479
|
+
var ALL_TABLES = "__ALL_TABLES__";
|
|
480
|
+
var AIRTABLE_SETUP_MAX_TABLES = 20;
|
|
481
|
+
async function listBases(params) {
|
|
482
|
+
const res = await apiFetch(params, "/meta/bases");
|
|
483
|
+
if (!res.ok) {
|
|
484
|
+
const body = await res.text().catch(() => res.statusText);
|
|
485
|
+
throw new Error(`airtable: listBases failed (${res.status}): ${body}`);
|
|
486
|
+
}
|
|
487
|
+
const data = await res.json();
|
|
488
|
+
return data.bases ?? [];
|
|
489
|
+
}
|
|
490
|
+
async function listTables(params, baseId) {
|
|
491
|
+
const res = await apiFetch(params, `/meta/bases/${baseId}/tables`);
|
|
492
|
+
if (!res.ok) {
|
|
493
|
+
const body = await res.text().catch(() => res.statusText);
|
|
494
|
+
throw new Error(`airtable: listTables failed (${res.status}): ${body}`);
|
|
495
|
+
}
|
|
496
|
+
const data = await res.json();
|
|
497
|
+
return data.tables ?? [];
|
|
498
|
+
}
|
|
499
|
+
var airtableSetupFlow = {
|
|
500
|
+
initialState: () => ({}),
|
|
501
|
+
steps: [
|
|
502
|
+
{
|
|
503
|
+
slug: "base",
|
|
504
|
+
type: "select",
|
|
505
|
+
question: {
|
|
506
|
+
ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u306B\u4F7F\u3046\u30D9\u30FC\u30B9\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044",
|
|
507
|
+
en: "Select the base to use for setup"
|
|
508
|
+
},
|
|
509
|
+
async fetchOptions(_state, rt) {
|
|
510
|
+
const bases = await listBases(rt.params);
|
|
511
|
+
return bases.filter((b) => b.id && b.name).map((b) => ({ value: b.id, label: b.name }));
|
|
512
|
+
},
|
|
513
|
+
applyAnswer: (state, answer) => ({ ...state, base: answer[0] })
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
slug: "tables",
|
|
517
|
+
type: "multiSelect",
|
|
518
|
+
question: {
|
|
519
|
+
ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
520
|
+
en: "Select target tables (multi-select allowed)"
|
|
521
|
+
},
|
|
522
|
+
async fetchOptions(state, rt) {
|
|
523
|
+
if (!state.base) return [];
|
|
524
|
+
const tables = await listTables(rt.params, state.base);
|
|
525
|
+
const tableOptions = tables.filter((t) => t.name).map((t) => ({ value: t.name }));
|
|
526
|
+
return [
|
|
527
|
+
{
|
|
528
|
+
value: ALL_TABLES,
|
|
529
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
|
|
530
|
+
},
|
|
531
|
+
...tableOptions
|
|
532
|
+
];
|
|
533
|
+
},
|
|
534
|
+
applyAnswer: (state, answer) => ({ ...state, tables: answer })
|
|
535
|
+
}
|
|
536
|
+
],
|
|
537
|
+
async finalize(state, rt) {
|
|
538
|
+
if (!state.base || !state.tables) {
|
|
539
|
+
throw new Error("Airtable setup: incomplete state on finalize");
|
|
540
|
+
}
|
|
541
|
+
const baseId = state.base;
|
|
542
|
+
const tables = await listTables(rt.params, baseId);
|
|
543
|
+
const tableByName = new Map(tables.map((t) => [t.name, t]));
|
|
544
|
+
const targetTableNames = await resolveSetupSelection({
|
|
545
|
+
selected: state.tables,
|
|
546
|
+
allSentinel: ALL_TABLES,
|
|
547
|
+
fetchAll: async () => tables.map((t) => t.name).filter((n) => n),
|
|
548
|
+
limit: AIRTABLE_SETUP_MAX_TABLES
|
|
549
|
+
});
|
|
550
|
+
const sections = [
|
|
551
|
+
"## Airtable",
|
|
552
|
+
"",
|
|
553
|
+
`### Base: ${baseId}`,
|
|
554
|
+
""
|
|
555
|
+
];
|
|
556
|
+
for (const tableName of targetTableNames) {
|
|
557
|
+
const table = tableByName.get(tableName);
|
|
558
|
+
if (!table) {
|
|
559
|
+
sections.push(`#### Table: ${tableName}`, "", "_Not found._", "");
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
sections.push(`#### Table: ${table.name}`, "");
|
|
563
|
+
sections.push("| Field | Type | Description |");
|
|
564
|
+
sections.push("|-------|------|-------------|");
|
|
565
|
+
for (const f of table.fields ?? []) {
|
|
566
|
+
const description = (f.description ?? "").replace(/\|/g, "\\|");
|
|
567
|
+
sections.push(`| ${f.name} | ${f.type} | ${description || "-"} |`);
|
|
568
|
+
}
|
|
569
|
+
sections.push("");
|
|
570
|
+
}
|
|
571
|
+
return sections.join("\n");
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
|
|
359
575
|
// ../connectors/src/connectors/airtable/tools/request.ts
|
|
360
576
|
import { z } from "zod";
|
|
361
|
-
var
|
|
577
|
+
var BASE_URL3 = "https://api.airtable.com/v0/";
|
|
362
578
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
363
579
|
var inputSchema = z.object({
|
|
364
580
|
toolUseIntent: z.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
|
|
@@ -395,7 +611,7 @@ Authentication is handled automatically using the API Key.
|
|
|
395
611
|
const apiKey = parameters.apiKey.getValue(connection2);
|
|
396
612
|
const baseId = parameters.baseId.getValue(connection2);
|
|
397
613
|
const resolvedPath = path2.replace(/\{baseId\}/g, baseId);
|
|
398
|
-
const url = `${
|
|
614
|
+
const url = `${BASE_URL3}${resolvedPath}`;
|
|
399
615
|
const controller = new AbortController();
|
|
400
616
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
401
617
|
try {
|
|
@@ -559,7 +775,39 @@ export default async function handler(c: Context) {
|
|
|
559
775
|
#### \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3
|
|
560
776
|
- \u30EC\u30B9\u30DD\u30F3\u30B9\u306B \`offset\` \u304C\u542B\u307E\u308C\u308B\u5834\u5408\u3001\u6B21\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u306B \`?offset={offset}\` \u3092\u8FFD\u52A0\u3057\u3066\u6B21\u306E\u30DA\u30FC\u30B8\u3092\u53D6\u5F97\u3057\u307E\u3059`
|
|
561
777
|
},
|
|
562
|
-
tools
|
|
778
|
+
tools,
|
|
779
|
+
setup: (params, ctx, config) => runSetupFlow(airtableSetupFlow, params, ctx, config),
|
|
780
|
+
async checkConnection(params, _config) {
|
|
781
|
+
const apiKey = params[parameters.apiKey.slug];
|
|
782
|
+
if (!apiKey) {
|
|
783
|
+
return {
|
|
784
|
+
success: false,
|
|
785
|
+
error: `Missing required parameter: ${parameters.apiKey.slug}`
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
try {
|
|
789
|
+
const res = await fetch("https://api.airtable.com/v0/meta/whoami", {
|
|
790
|
+
method: "GET",
|
|
791
|
+
headers: {
|
|
792
|
+
Authorization: `Bearer ${apiKey}`,
|
|
793
|
+
Accept: "application/json"
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
if (!res.ok) {
|
|
797
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
798
|
+
return {
|
|
799
|
+
success: false,
|
|
800
|
+
error: `Airtable API failed: HTTP ${res.status} ${errorText}`
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
return { success: true };
|
|
804
|
+
} catch (error) {
|
|
805
|
+
return {
|
|
806
|
+
success: false,
|
|
807
|
+
error: error instanceof Error ? error.message : String(error)
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
}
|
|
563
811
|
});
|
|
564
812
|
|
|
565
813
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -588,6 +836,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
588
836
|
import { getContext } from "hono/context-storage";
|
|
589
837
|
import { getCookie } from "hono/cookie";
|
|
590
838
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
839
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
591
840
|
function normalizeHeaders(input) {
|
|
592
841
|
const out = {};
|
|
593
842
|
if (!input) return out;
|
|
@@ -596,6 +845,11 @@ function normalizeHeaders(input) {
|
|
|
596
845
|
});
|
|
597
846
|
return out;
|
|
598
847
|
}
|
|
848
|
+
function extractInputUrl(input) {
|
|
849
|
+
if (typeof input === "string") return input;
|
|
850
|
+
if (input instanceof URL) return input.href;
|
|
851
|
+
return input.url;
|
|
852
|
+
}
|
|
599
853
|
function createSandboxProxyFetch(connectionId) {
|
|
600
854
|
return async (input, init) => {
|
|
601
855
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -605,10 +859,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
605
859
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
606
860
|
);
|
|
607
861
|
}
|
|
608
|
-
const originalUrl =
|
|
862
|
+
const originalUrl = extractInputUrl(input);
|
|
863
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
864
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
865
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
866
|
+
return fetch(sessionUrl, {
|
|
867
|
+
method: "POST",
|
|
868
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
869
|
+
});
|
|
870
|
+
}
|
|
609
871
|
const originalMethod = init?.method ?? "GET";
|
|
610
872
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
611
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
612
873
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
613
874
|
return fetch(proxyUrl, {
|
|
614
875
|
method: "POST",
|
|
@@ -634,10 +895,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
634
895
|
}
|
|
635
896
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
636
897
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
898
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
637
899
|
return async (input, init) => {
|
|
638
|
-
const originalUrl =
|
|
639
|
-
const originalMethod = init?.method ?? "GET";
|
|
640
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
900
|
+
const originalUrl = extractInputUrl(input);
|
|
641
901
|
const c = getContext();
|
|
642
902
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
643
903
|
if (!appSession) {
|
|
@@ -645,6 +905,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
645
905
|
"No authentication method available for connection proxy."
|
|
646
906
|
);
|
|
647
907
|
}
|
|
908
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
909
|
+
return fetch(sessionUrl, {
|
|
910
|
+
method: "POST",
|
|
911
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
const originalMethod = init?.method ?? "GET";
|
|
915
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
648
916
|
return fetch(proxyUrl, {
|
|
649
917
|
method: "POST",
|
|
650
918
|
headers: {
|