@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.
- package/dist/cli/index.js +12374 -883
- package/dist/connectors/airtable-oauth.js +257 -46
- package/dist/connectors/airtable.js +294 -51
- package/dist/connectors/amplitude.js +297 -47
- package/dist/connectors/anthropic.js +135 -47
- package/dist/connectors/asana.js +302 -49
- package/dist/connectors/attio.js +277 -49
- package/dist/connectors/aws-billing.js +262 -46
- package/dist/connectors/azure-sql.js +396 -102
- package/dist/connectors/backlog-api-key.js +292 -47
- package/dist/connectors/clickup.js +313 -49
- package/dist/connectors/cosmosdb.js +280 -50
- package/dist/connectors/customerio.js +294 -47
- package/dist/connectors/dbt.js +315 -47
- package/dist/connectors/freshdesk.js +317 -53
- package/dist/connectors/freshsales.js +308 -52
- package/dist/connectors/freshservice.js +336 -53
- package/dist/connectors/gamma.js +302 -52
- package/dist/connectors/gemini.js +134 -47
- package/dist/connectors/github.js +361 -49
- package/dist/connectors/gmail-oauth.js +179 -7
- package/dist/connectors/gmail.js +325 -47
- package/dist/connectors/google-ads.js +263 -46
- package/dist/connectors/google-analytics-oauth.js +285 -46
- package/dist/connectors/google-analytics.js +387 -49
- package/dist/connectors/google-audit-log.js +413 -47
- package/dist/connectors/google-calendar-oauth.js +234 -46
- package/dist/connectors/google-calendar.js +334 -47
- package/dist/connectors/google-docs.js +195 -6
- package/dist/connectors/google-drive.js +237 -5
- package/dist/connectors/google-search-console-oauth.js +231 -46
- package/dist/connectors/google-sheets.js +247 -47
- package/dist/connectors/google-slides.js +180 -6
- package/dist/connectors/grafana.js +307 -49
- package/dist/connectors/hubspot-oauth.js +183 -5
- package/dist/connectors/hubspot.js +281 -49
- package/dist/connectors/influxdb.js +391 -51
- package/dist/connectors/intercom-oauth.js +185 -5
- package/dist/connectors/intercom.js +277 -49
- package/dist/connectors/jdbc.js +737 -110
- package/dist/connectors/jira-api-key.js +301 -47
- package/dist/connectors/kintone-api-token.js +256 -47
- package/dist/connectors/kintone.js +303 -47
- package/dist/connectors/linear.js +305 -49
- package/dist/connectors/linkedin-ads.js +243 -50
- package/dist/connectors/mailchimp-oauth.js +243 -46
- package/dist/connectors/mailchimp.js +295 -49
- package/dist/connectors/meta-ads-oauth.js +248 -48
- package/dist/connectors/meta-ads.js +260 -50
- package/dist/connectors/mixpanel.js +313 -47
- package/dist/connectors/monday.js +335 -49
- package/dist/connectors/mongodb.js +294 -57
- package/dist/connectors/notion-oauth.js +206 -5
- package/dist/connectors/notion.js +298 -51
- package/dist/connectors/openai.js +134 -47
- package/dist/connectors/oracle.js +414 -103
- package/dist/connectors/outlook-oauth.js +179 -5
- package/dist/connectors/powerbi-oauth.js +226 -5
- package/dist/connectors/salesforce.js +359 -49
- package/dist/connectors/semrush.js +289 -49
- package/dist/connectors/sentry.js +264 -50
- package/dist/connectors/shopify-oauth.js +162 -5
- package/dist/connectors/shopify.js +332 -47
- package/dist/connectors/sqlserver.js +390 -102
- package/dist/connectors/stripe-api-key.js +244 -46
- package/dist/connectors/stripe-oauth.js +177 -5
- package/dist/connectors/supabase.js +278 -48
- package/dist/connectors/tableau.js +389 -184
- package/dist/connectors/tiktok-ads.js +254 -48
- package/dist/connectors/wix-store.js +295 -49
- package/dist/connectors/zendesk-oauth.js +214 -5
- package/dist/connectors/zendesk.js +333 -47
- package/dist/index.d.ts +149 -1
- package/dist/index.js +13677 -1969
- package/dist/main.js +13627 -1927
- package/dist/vite-plugin.js +12391 -890
- package/package.json +1 -1
|
@@ -1,46 +1,57 @@
|
|
|
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/google-sheets/sdk/index.ts
|
|
46
57
|
var SHEETS_BASE_URL = "https://sheets.googleapis.com/v4/spreadsheets";
|
|
@@ -183,6 +194,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
183
194
|
tools;
|
|
184
195
|
query;
|
|
185
196
|
checkConnection;
|
|
197
|
+
/**
|
|
198
|
+
* SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
|
|
199
|
+
* implement this expose a step-by-step exploration flow (database/schema/
|
|
200
|
+
* table/etc. discovery) that the dashboard backend drives via the
|
|
201
|
+
* `/connections/:connectionId/setup` endpoint. Implement by delegating to
|
|
202
|
+
* `runSetupFlow` from `setup-flow.ts`.
|
|
203
|
+
*/
|
|
204
|
+
setup;
|
|
205
|
+
/**
|
|
206
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
207
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
208
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
209
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
210
|
+
*
|
|
211
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
212
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
213
|
+
* connectionId, which doesn't exist until the row is saved
|
|
214
|
+
*
|
|
215
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
216
|
+
* the default verify-on-create behavior without opt-in.
|
|
217
|
+
*/
|
|
218
|
+
skipConnectionCheckOnCreate;
|
|
186
219
|
constructor(config) {
|
|
187
220
|
this.slug = config.slug;
|
|
188
221
|
this.authType = config.authType;
|
|
@@ -199,6 +232,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
199
232
|
this.tools = config.tools;
|
|
200
233
|
this.query = config.query;
|
|
201
234
|
this.checkConnection = config.checkConnection;
|
|
235
|
+
this.setup = config.setup;
|
|
236
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
202
237
|
}
|
|
203
238
|
get connectorKey() {
|
|
204
239
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -263,6 +298,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
263
298
|
}
|
|
264
299
|
};
|
|
265
300
|
|
|
301
|
+
// ../connectors/src/setup-flow.ts
|
|
302
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
303
|
+
const runtime = {
|
|
304
|
+
params,
|
|
305
|
+
language: ctx.language,
|
|
306
|
+
config
|
|
307
|
+
};
|
|
308
|
+
let state = flow.initialState();
|
|
309
|
+
let answerIdx = 0;
|
|
310
|
+
for (const step of flow.steps) {
|
|
311
|
+
const ans = ctx.answers[answerIdx];
|
|
312
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
313
|
+
state = step.applyAnswer(state, ans.answer);
|
|
314
|
+
answerIdx += 1;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (step.type === "text") {
|
|
318
|
+
return {
|
|
319
|
+
type: "nextQuestion",
|
|
320
|
+
questionSlug: step.slug,
|
|
321
|
+
question: step.question[ctx.language],
|
|
322
|
+
questionType: "text"
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
326
|
+
if (options.length === 0) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
type: "nextQuestion",
|
|
331
|
+
questionSlug: step.slug,
|
|
332
|
+
question: step.question[ctx.language],
|
|
333
|
+
questionType: step.type,
|
|
334
|
+
options
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
338
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
339
|
+
}
|
|
340
|
+
async function resolveSetupSelection(params) {
|
|
341
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
342
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
343
|
+
return resolved.slice(0, limit);
|
|
344
|
+
}
|
|
345
|
+
|
|
266
346
|
// ../connectors/src/auth-types.ts
|
|
267
347
|
var AUTH_TYPES = {
|
|
268
348
|
OAUTH: "oauth",
|
|
@@ -461,7 +541,105 @@ var googleSheetsOnboarding = new ConnectorOnboarding({
|
|
|
461
541
|
}
|
|
462
542
|
});
|
|
463
543
|
|
|
544
|
+
// ../connectors/src/connectors/google-sheets/utils.ts
|
|
545
|
+
async function googleApiFetch(config, url) {
|
|
546
|
+
const res = await config.proxyFetch(url, { method: "GET" });
|
|
547
|
+
if (!res.ok) {
|
|
548
|
+
const text = await res.text().catch(() => res.statusText);
|
|
549
|
+
throw new Error(`Google API ${url} failed: HTTP ${res.status} ${text}`);
|
|
550
|
+
}
|
|
551
|
+
return await res.json();
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// ../connectors/src/connectors/google-sheets/setup-flow.ts
|
|
555
|
+
var ALL_SPREADSHEETS = "__ALL_SPREADSHEETS__";
|
|
556
|
+
var GOOGLE_SHEETS_SETUP_MAX_SPREADSHEETS = 10;
|
|
557
|
+
var DRIVE_LIST_PAGE_SIZE = 50;
|
|
558
|
+
async function listSpreadsheets(config) {
|
|
559
|
+
const q = encodeURIComponent(
|
|
560
|
+
"mimeType='application/vnd.google-apps.spreadsheet' and trashed=false"
|
|
561
|
+
);
|
|
562
|
+
const url = `https://www.googleapis.com/drive/v3/files?q=${q}&pageSize=${DRIVE_LIST_PAGE_SIZE}&orderBy=modifiedTime%20desc&fields=files(id,name,modifiedTime)`;
|
|
563
|
+
const data = await googleApiFetch(config, url);
|
|
564
|
+
return data.files ?? [];
|
|
565
|
+
}
|
|
566
|
+
var googleSheetsSetupFlow = {
|
|
567
|
+
initialState: () => ({}),
|
|
568
|
+
steps: [
|
|
569
|
+
{
|
|
570
|
+
slug: "spreadsheets",
|
|
571
|
+
type: "multiSelect",
|
|
572
|
+
question: {
|
|
573
|
+
ja: "\u5BFE\u8C61\u306E\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
574
|
+
en: "Select target spreadsheets (multi-select allowed)"
|
|
575
|
+
},
|
|
576
|
+
async fetchOptions(_state, rt) {
|
|
577
|
+
const files = await listSpreadsheets(rt.config);
|
|
578
|
+
const fileOptions = files.map((f) => ({
|
|
579
|
+
value: f.id,
|
|
580
|
+
label: f.name
|
|
581
|
+
}));
|
|
582
|
+
return [
|
|
583
|
+
{
|
|
584
|
+
value: ALL_SPREADSHEETS,
|
|
585
|
+
label: rt.language === "ja" ? "\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u3059\u3079\u3066\u306E\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8" : "All accessible spreadsheets"
|
|
586
|
+
},
|
|
587
|
+
...fileOptions
|
|
588
|
+
];
|
|
589
|
+
},
|
|
590
|
+
applyAnswer: (state, answer) => ({ ...state, spreadsheets: answer })
|
|
591
|
+
}
|
|
592
|
+
],
|
|
593
|
+
async finalize(state, rt) {
|
|
594
|
+
if (!state.spreadsheets) {
|
|
595
|
+
throw new Error("Google Sheets setup: incomplete state on finalize");
|
|
596
|
+
}
|
|
597
|
+
const targetIds = await resolveSetupSelection({
|
|
598
|
+
selected: state.spreadsheets,
|
|
599
|
+
allSentinel: ALL_SPREADSHEETS,
|
|
600
|
+
fetchAll: async () => {
|
|
601
|
+
const files = await listSpreadsheets(rt.config);
|
|
602
|
+
return files.map((f) => f.id);
|
|
603
|
+
},
|
|
604
|
+
limit: GOOGLE_SHEETS_SETUP_MAX_SPREADSHEETS
|
|
605
|
+
});
|
|
606
|
+
const sections = ["## Google Sheets", ""];
|
|
607
|
+
if (targetIds.length === 0) {
|
|
608
|
+
sections.push(
|
|
609
|
+
rt.language === "ja" ? "\u5BFE\u8C61\u306E\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002" : "No spreadsheets selected."
|
|
610
|
+
);
|
|
611
|
+
return sections.join("\n");
|
|
612
|
+
}
|
|
613
|
+
for (const id of targetIds) {
|
|
614
|
+
const url = `https://sheets.googleapis.com/v4/spreadsheets/${encodeURIComponent(id)}?fields=spreadsheetId,properties(title),sheets(properties(title,sheetId,gridProperties))`;
|
|
615
|
+
const meta = await googleApiFetch(
|
|
616
|
+
rt.config,
|
|
617
|
+
url
|
|
618
|
+
);
|
|
619
|
+
const title = meta.properties?.title ?? id;
|
|
620
|
+
sections.push(`### Spreadsheet: ${title}`, "", `- ID: ${id}`, "");
|
|
621
|
+
const sheets = meta.sheets ?? [];
|
|
622
|
+
if (sheets.length === 0) {
|
|
623
|
+
sections.push("- (no sheet tabs)", "");
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
sections.push("| Sheet | Rows | Columns |");
|
|
627
|
+
sections.push("|-------|------|---------|");
|
|
628
|
+
for (const s of sheets) {
|
|
629
|
+
const p = s.properties ?? {};
|
|
630
|
+
const sheetTitle = p.title ?? "(untitled)";
|
|
631
|
+
const rows = p.gridProperties?.rowCount ?? "-";
|
|
632
|
+
const cols = p.gridProperties?.columnCount ?? "-";
|
|
633
|
+
sections.push(`| ${sheetTitle} | ${rows} | ${cols} |`);
|
|
634
|
+
}
|
|
635
|
+
sections.push("");
|
|
636
|
+
}
|
|
637
|
+
return sections.join("\n");
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
|
|
464
641
|
// ../connectors/src/connectors/google-sheets/parameters.ts
|
|
642
|
+
init_parameter_definition();
|
|
465
643
|
var parameters = {
|
|
466
644
|
spreadsheetId: new ParameterDefinition({
|
|
467
645
|
slug: "spreadsheet-id",
|
|
@@ -479,6 +657,7 @@ var tools = { request: requestTool };
|
|
|
479
657
|
var googleSheetsConnector = new ConnectorPlugin({
|
|
480
658
|
slug: "google-sheets",
|
|
481
659
|
authType: AUTH_TYPES.OAUTH,
|
|
660
|
+
skipConnectionCheckOnCreate: true,
|
|
482
661
|
name: "Google Sheets",
|
|
483
662
|
description: "Connect to Google Sheets for read/write access via OAuth. Any spreadsheet the authenticated Google account can access is supported \u2014 the target spreadsheetId is passed per call.",
|
|
484
663
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1UPQuggyiZmbb26CuaSr2h/032770e8739b183fa00b7625f024e536/google-sheets.svg",
|
|
@@ -644,7 +823,8 @@ await sheets.batchUpdate(spreadsheetId, [
|
|
|
644
823
|
]);
|
|
645
824
|
\`\`\``
|
|
646
825
|
},
|
|
647
|
-
tools
|
|
826
|
+
tools,
|
|
827
|
+
setup: (params, ctx, config) => runSetupFlow(googleSheetsSetupFlow, params, ctx, config)
|
|
648
828
|
});
|
|
649
829
|
|
|
650
830
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -673,6 +853,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
673
853
|
import { getContext } from "hono/context-storage";
|
|
674
854
|
import { getCookie } from "hono/cookie";
|
|
675
855
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
856
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
676
857
|
function normalizeHeaders(input) {
|
|
677
858
|
const out = {};
|
|
678
859
|
if (!input) return out;
|
|
@@ -681,6 +862,11 @@ function normalizeHeaders(input) {
|
|
|
681
862
|
});
|
|
682
863
|
return out;
|
|
683
864
|
}
|
|
865
|
+
function extractInputUrl(input) {
|
|
866
|
+
if (typeof input === "string") return input;
|
|
867
|
+
if (input instanceof URL) return input.href;
|
|
868
|
+
return input.url;
|
|
869
|
+
}
|
|
684
870
|
function createSandboxProxyFetch(connectionId) {
|
|
685
871
|
return async (input, init) => {
|
|
686
872
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -690,10 +876,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
690
876
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
691
877
|
);
|
|
692
878
|
}
|
|
693
|
-
const originalUrl =
|
|
879
|
+
const originalUrl = extractInputUrl(input);
|
|
880
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
881
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
882
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
883
|
+
return fetch(sessionUrl, {
|
|
884
|
+
method: "POST",
|
|
885
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
886
|
+
});
|
|
887
|
+
}
|
|
694
888
|
const originalMethod = init?.method ?? "GET";
|
|
695
889
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
696
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
697
890
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
698
891
|
return fetch(proxyUrl, {
|
|
699
892
|
method: "POST",
|
|
@@ -719,10 +912,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
719
912
|
}
|
|
720
913
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
721
914
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
915
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
722
916
|
return async (input, init) => {
|
|
723
|
-
const originalUrl =
|
|
724
|
-
const originalMethod = init?.method ?? "GET";
|
|
725
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
917
|
+
const originalUrl = extractInputUrl(input);
|
|
726
918
|
const c = getContext();
|
|
727
919
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
728
920
|
if (!appSession) {
|
|
@@ -730,6 +922,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
730
922
|
"No authentication method available for connection proxy."
|
|
731
923
|
);
|
|
732
924
|
}
|
|
925
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
926
|
+
return fetch(sessionUrl, {
|
|
927
|
+
method: "POST",
|
|
928
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
const originalMethod = init?.method ?? "GET";
|
|
932
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
733
933
|
return fetch(proxyUrl, {
|
|
734
934
|
method: "POST",
|
|
735
935
|
headers: {
|
|
@@ -125,6 +125,28 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
125
125
|
tools;
|
|
126
126
|
query;
|
|
127
127
|
checkConnection;
|
|
128
|
+
/**
|
|
129
|
+
* SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
|
|
130
|
+
* implement this expose a step-by-step exploration flow (database/schema/
|
|
131
|
+
* table/etc. discovery) that the dashboard backend drives via the
|
|
132
|
+
* `/connections/:connectionId/setup` endpoint. Implement by delegating to
|
|
133
|
+
* `runSetupFlow` from `setup-flow.ts`.
|
|
134
|
+
*/
|
|
135
|
+
setup;
|
|
136
|
+
/**
|
|
137
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
138
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
139
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
140
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
141
|
+
*
|
|
142
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
143
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
144
|
+
* connectionId, which doesn't exist until the row is saved
|
|
145
|
+
*
|
|
146
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
147
|
+
* the default verify-on-create behavior without opt-in.
|
|
148
|
+
*/
|
|
149
|
+
skipConnectionCheckOnCreate;
|
|
128
150
|
constructor(config) {
|
|
129
151
|
this.slug = config.slug;
|
|
130
152
|
this.authType = config.authType;
|
|
@@ -141,6 +163,8 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
141
163
|
this.tools = config.tools;
|
|
142
164
|
this.query = config.query;
|
|
143
165
|
this.checkConnection = config.checkConnection;
|
|
166
|
+
this.setup = config.setup;
|
|
167
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
144
168
|
}
|
|
145
169
|
get connectorKey() {
|
|
146
170
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -205,6 +229,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
205
229
|
}
|
|
206
230
|
};
|
|
207
231
|
|
|
232
|
+
// ../connectors/src/setup-flow.ts
|
|
233
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
234
|
+
const runtime = {
|
|
235
|
+
params,
|
|
236
|
+
language: ctx.language,
|
|
237
|
+
config
|
|
238
|
+
};
|
|
239
|
+
let state = flow.initialState();
|
|
240
|
+
let answerIdx = 0;
|
|
241
|
+
for (const step of flow.steps) {
|
|
242
|
+
const ans = ctx.answers[answerIdx];
|
|
243
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
244
|
+
state = step.applyAnswer(state, ans.answer);
|
|
245
|
+
answerIdx += 1;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (step.type === "text") {
|
|
249
|
+
return {
|
|
250
|
+
type: "nextQuestion",
|
|
251
|
+
questionSlug: step.slug,
|
|
252
|
+
question: step.question[ctx.language],
|
|
253
|
+
questionType: "text"
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
257
|
+
if (options.length === 0) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
type: "nextQuestion",
|
|
262
|
+
questionSlug: step.slug,
|
|
263
|
+
question: step.question[ctx.language],
|
|
264
|
+
questionType: step.type,
|
|
265
|
+
options
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
269
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
270
|
+
}
|
|
271
|
+
async function resolveSetupSelection(params) {
|
|
272
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
273
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
274
|
+
return resolved.slice(0, limit);
|
|
275
|
+
}
|
|
276
|
+
|
|
208
277
|
// ../connectors/src/auth-types.ts
|
|
209
278
|
var AUTH_TYPES = {
|
|
210
279
|
OAUTH: "oauth",
|
|
@@ -235,6 +304,89 @@ var googleSlidesOnboarding = new ConnectorOnboarding({
|
|
|
235
304
|
}
|
|
236
305
|
});
|
|
237
306
|
|
|
307
|
+
// ../connectors/src/connectors/google-slides/utils.ts
|
|
308
|
+
async function googleApiFetch(config, url) {
|
|
309
|
+
const res = await config.proxyFetch(url, { method: "GET" });
|
|
310
|
+
if (!res.ok) {
|
|
311
|
+
const text = await res.text().catch(() => res.statusText);
|
|
312
|
+
throw new Error(`Google Slides ${url} failed: HTTP ${res.status} ${text}`);
|
|
313
|
+
}
|
|
314
|
+
return await res.json();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ../connectors/src/connectors/google-slides/setup-flow.ts
|
|
318
|
+
var ALL_PRESENTATIONS = "__ALL_PRESENTATIONS__";
|
|
319
|
+
var GOOGLE_SLIDES_SETUP_MAX_PRESENTATIONS = 10;
|
|
320
|
+
var DRIVE_LIST_PAGE_SIZE = 50;
|
|
321
|
+
async function listPresentations(config) {
|
|
322
|
+
const q = encodeURIComponent(
|
|
323
|
+
"mimeType='application/vnd.google-apps.presentation' and trashed=false"
|
|
324
|
+
);
|
|
325
|
+
const url = `https://www.googleapis.com/drive/v3/files?q=${q}&pageSize=${DRIVE_LIST_PAGE_SIZE}&orderBy=modifiedTime%20desc&fields=files(id,name,modifiedTime)`;
|
|
326
|
+
const data = await googleApiFetch(config, url);
|
|
327
|
+
return data.files ?? [];
|
|
328
|
+
}
|
|
329
|
+
var googleSlidesSetupFlow = {
|
|
330
|
+
initialState: () => ({}),
|
|
331
|
+
steps: [
|
|
332
|
+
{
|
|
333
|
+
slug: "presentations",
|
|
334
|
+
type: "multiSelect",
|
|
335
|
+
question: {
|
|
336
|
+
ja: "\u5BFE\u8C61\u306E\u30D7\u30EC\u30BC\u30F3\u30C6\u30FC\u30B7\u30E7\u30F3\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
337
|
+
en: "Select target presentations (multi-select allowed)"
|
|
338
|
+
},
|
|
339
|
+
async fetchOptions(_state, rt) {
|
|
340
|
+
const files = await listPresentations(rt.config);
|
|
341
|
+
return [
|
|
342
|
+
{
|
|
343
|
+
value: ALL_PRESENTATIONS,
|
|
344
|
+
label: rt.language === "ja" ? "\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u3059\u3079\u3066\u306E\u30D7\u30EC\u30BC\u30F3\u30C6\u30FC\u30B7\u30E7\u30F3" : "All accessible presentations"
|
|
345
|
+
},
|
|
346
|
+
...files.map((f) => ({ value: f.id, label: f.name }))
|
|
347
|
+
];
|
|
348
|
+
},
|
|
349
|
+
applyAnswer: (state, answer) => ({ ...state, presentations: answer })
|
|
350
|
+
}
|
|
351
|
+
],
|
|
352
|
+
async finalize(state, rt) {
|
|
353
|
+
if (!state.presentations) {
|
|
354
|
+
throw new Error("Google Slides setup: incomplete state on finalize");
|
|
355
|
+
}
|
|
356
|
+
const targetIds = await resolveSetupSelection({
|
|
357
|
+
selected: state.presentations,
|
|
358
|
+
allSentinel: ALL_PRESENTATIONS,
|
|
359
|
+
fetchAll: async () => {
|
|
360
|
+
const files = await listPresentations(rt.config);
|
|
361
|
+
return files.map((f) => f.id);
|
|
362
|
+
},
|
|
363
|
+
limit: GOOGLE_SLIDES_SETUP_MAX_PRESENTATIONS
|
|
364
|
+
});
|
|
365
|
+
const sections = ["## Google Slides", ""];
|
|
366
|
+
if (targetIds.length === 0) {
|
|
367
|
+
sections.push(
|
|
368
|
+
rt.language === "ja" ? "\u5BFE\u8C61\u306E\u30D7\u30EC\u30BC\u30F3\u30C6\u30FC\u30B7\u30E7\u30F3\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002" : "No presentations selected."
|
|
369
|
+
);
|
|
370
|
+
return sections.join("\n");
|
|
371
|
+
}
|
|
372
|
+
for (const id of targetIds) {
|
|
373
|
+
const url = `https://slides.googleapis.com/v1/presentations/${encodeURIComponent(id)}?fields=presentationId,title,revisionId,slides(objectId)`;
|
|
374
|
+
const meta = await googleApiFetch(
|
|
375
|
+
rt.config,
|
|
376
|
+
url
|
|
377
|
+
);
|
|
378
|
+
const title = meta.title ?? id;
|
|
379
|
+
const slideCount = (meta.slides ?? []).length;
|
|
380
|
+
sections.push(`### Presentation: ${title}`, "");
|
|
381
|
+
sections.push(`- ID: ${id}`);
|
|
382
|
+
sections.push(`- Slides: ${slideCount}`);
|
|
383
|
+
if (meta.revisionId) sections.push(`- Revision: ${meta.revisionId}`);
|
|
384
|
+
sections.push("");
|
|
385
|
+
}
|
|
386
|
+
return sections.join("\n");
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
238
390
|
// ../connectors/src/connectors/google-slides/parameters.ts
|
|
239
391
|
var parameters = {};
|
|
240
392
|
|
|
@@ -365,6 +517,7 @@ var tools = { request: requestTool };
|
|
|
365
517
|
var googleSlidesConnector = new ConnectorPlugin({
|
|
366
518
|
slug: "google-slides",
|
|
367
519
|
authType: AUTH_TYPES.OAUTH,
|
|
520
|
+
skipConnectionCheckOnCreate: true,
|
|
368
521
|
name: "Google Slides",
|
|
369
522
|
description: "Connect to Google Slides for presentation data access and creation using OAuth.",
|
|
370
523
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4oyF4yTRpemMA43X49masx/e1582d25e3b4c9a63ba83df2147c1968/google_slide.png",
|
|
@@ -540,7 +693,8 @@ await slides.batchUpdate(presentationId, [
|
|
|
540
693
|
|
|
541
694
|
\u30CF\u30F3\u30C9\u30E9\u306E\u30C6\u30B9\u30C8\u304C \`Connection proxy is not configured\` \u3067\u5931\u6557\u3059\u308B\u5834\u5408\u306F\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u901A\u5E38\u306F\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306E\u521D\u671F\u5316\u4E2D\u306B\u8D77\u304D\u307E\u3059\u3002SDK \u3092\u8AE6\u3081\u3066 OAuth \u30D7\u30ED\u30AD\u30B7\u306E URL \u3092\u81EA\u5206\u3067\u7D44\u307F\u7ACB\u3066\u308B\u3053\u3068\u306F **\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044**\u3002`
|
|
542
695
|
},
|
|
543
|
-
tools
|
|
696
|
+
tools,
|
|
697
|
+
setup: (params, ctx, config) => runSetupFlow(googleSlidesSetupFlow, params, ctx, config)
|
|
544
698
|
});
|
|
545
699
|
|
|
546
700
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -569,6 +723,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
569
723
|
import { getContext } from "hono/context-storage";
|
|
570
724
|
import { getCookie } from "hono/cookie";
|
|
571
725
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
726
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
572
727
|
function normalizeHeaders(input) {
|
|
573
728
|
const out = {};
|
|
574
729
|
if (!input) return out;
|
|
@@ -577,6 +732,11 @@ function normalizeHeaders(input) {
|
|
|
577
732
|
});
|
|
578
733
|
return out;
|
|
579
734
|
}
|
|
735
|
+
function extractInputUrl(input) {
|
|
736
|
+
if (typeof input === "string") return input;
|
|
737
|
+
if (input instanceof URL) return input.href;
|
|
738
|
+
return input.url;
|
|
739
|
+
}
|
|
580
740
|
function createSandboxProxyFetch(connectionId) {
|
|
581
741
|
return async (input, init) => {
|
|
582
742
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -586,10 +746,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
586
746
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
587
747
|
);
|
|
588
748
|
}
|
|
589
|
-
const originalUrl =
|
|
749
|
+
const originalUrl = extractInputUrl(input);
|
|
750
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
751
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
752
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
753
|
+
return fetch(sessionUrl, {
|
|
754
|
+
method: "POST",
|
|
755
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
756
|
+
});
|
|
757
|
+
}
|
|
590
758
|
const originalMethod = init?.method ?? "GET";
|
|
591
759
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
592
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
593
760
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
594
761
|
return fetch(proxyUrl, {
|
|
595
762
|
method: "POST",
|
|
@@ -615,10 +782,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
615
782
|
}
|
|
616
783
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
617
784
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
785
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
618
786
|
return async (input, init) => {
|
|
619
|
-
const originalUrl =
|
|
620
|
-
const originalMethod = init?.method ?? "GET";
|
|
621
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
787
|
+
const originalUrl = extractInputUrl(input);
|
|
622
788
|
const c = getContext();
|
|
623
789
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
624
790
|
if (!appSession) {
|
|
@@ -626,6 +792,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
626
792
|
"No authentication method available for connection proxy."
|
|
627
793
|
);
|
|
628
794
|
}
|
|
795
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
796
|
+
return fetch(sessionUrl, {
|
|
797
|
+
method: "POST",
|
|
798
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
const originalMethod = init?.method ?? "GET";
|
|
802
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
629
803
|
return fetch(proxyUrl, {
|
|
630
804
|
method: "POST",
|
|
631
805
|
headers: {
|