@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,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,76 @@ 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
|
+
const pendingParameterUpdates = [];
|
|
311
|
+
for (const step of flow.steps) {
|
|
312
|
+
const ans = ctx.answers[answerIdx];
|
|
313
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
314
|
+
state = step.applyAnswer(state, ans.answer);
|
|
315
|
+
if (step.toParameterUpdates) {
|
|
316
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
317
|
+
}
|
|
318
|
+
answerIdx += 1;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
322
|
+
if (step.type === "text") {
|
|
323
|
+
if (step.fetchOptions) {
|
|
324
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
325
|
+
if (options2.length === 0) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
type: "nextQuestion",
|
|
331
|
+
questionSlug: step.slug,
|
|
332
|
+
question: step.question[ctx.language],
|
|
333
|
+
questionType: "text",
|
|
334
|
+
allowFreeText: resolvedAllowFreeText,
|
|
335
|
+
...pendingParameterUpdates.length > 0 && {
|
|
336
|
+
parameterUpdates: pendingParameterUpdates
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
341
|
+
if (options.length === 0) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
type: "nextQuestion",
|
|
346
|
+
questionSlug: step.slug,
|
|
347
|
+
question: step.question[ctx.language],
|
|
348
|
+
questionType: step.type,
|
|
349
|
+
options,
|
|
350
|
+
allowFreeText: resolvedAllowFreeText,
|
|
351
|
+
...pendingParameterUpdates.length > 0 && {
|
|
352
|
+
parameterUpdates: pendingParameterUpdates
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
357
|
+
return {
|
|
358
|
+
type: "fulfilled",
|
|
359
|
+
dataInvestigationResult,
|
|
360
|
+
...pendingParameterUpdates.length > 0 && {
|
|
361
|
+
parameterUpdates: pendingParameterUpdates
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
async function resolveSetupSelection(params) {
|
|
366
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
367
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
368
|
+
return resolved.slice(0, limit);
|
|
369
|
+
}
|
|
370
|
+
|
|
266
371
|
// ../connectors/src/auth-types.ts
|
|
267
372
|
var AUTH_TYPES = {
|
|
268
373
|
OAUTH: "oauth",
|
|
@@ -461,7 +566,105 @@ var googleSheetsOnboarding = new ConnectorOnboarding({
|
|
|
461
566
|
}
|
|
462
567
|
});
|
|
463
568
|
|
|
569
|
+
// ../connectors/src/connectors/google-sheets/utils.ts
|
|
570
|
+
async function googleApiFetch(config, url) {
|
|
571
|
+
const res = await config.proxyFetch(url, { method: "GET" });
|
|
572
|
+
if (!res.ok) {
|
|
573
|
+
const text = await res.text().catch(() => res.statusText);
|
|
574
|
+
throw new Error(`Google API ${url} failed: HTTP ${res.status} ${text}`);
|
|
575
|
+
}
|
|
576
|
+
return await res.json();
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// ../connectors/src/connectors/google-sheets/setup-flow.ts
|
|
580
|
+
var ALL_SPREADSHEETS = "__ALL_SPREADSHEETS__";
|
|
581
|
+
var GOOGLE_SHEETS_SETUP_MAX_SPREADSHEETS = 10;
|
|
582
|
+
var DRIVE_LIST_PAGE_SIZE = 50;
|
|
583
|
+
async function listSpreadsheets(config) {
|
|
584
|
+
const q = encodeURIComponent(
|
|
585
|
+
"mimeType='application/vnd.google-apps.spreadsheet' and trashed=false"
|
|
586
|
+
);
|
|
587
|
+
const url = `https://www.googleapis.com/drive/v3/files?q=${q}&pageSize=${DRIVE_LIST_PAGE_SIZE}&orderBy=modifiedTime%20desc&fields=files(id,name,modifiedTime)`;
|
|
588
|
+
const data = await googleApiFetch(config, url);
|
|
589
|
+
return data.files ?? [];
|
|
590
|
+
}
|
|
591
|
+
var googleSheetsSetupFlow = {
|
|
592
|
+
initialState: () => ({}),
|
|
593
|
+
steps: [
|
|
594
|
+
{
|
|
595
|
+
slug: "spreadsheets",
|
|
596
|
+
type: "multiSelect",
|
|
597
|
+
question: {
|
|
598
|
+
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",
|
|
599
|
+
en: "Select target spreadsheets (multi-select allowed)"
|
|
600
|
+
},
|
|
601
|
+
async fetchOptions(_state, rt) {
|
|
602
|
+
const files = await listSpreadsheets(rt.config);
|
|
603
|
+
const fileOptions = files.map((f) => ({
|
|
604
|
+
value: f.id,
|
|
605
|
+
label: f.name
|
|
606
|
+
}));
|
|
607
|
+
return [
|
|
608
|
+
{
|
|
609
|
+
value: ALL_SPREADSHEETS,
|
|
610
|
+
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"
|
|
611
|
+
},
|
|
612
|
+
...fileOptions
|
|
613
|
+
];
|
|
614
|
+
},
|
|
615
|
+
applyAnswer: (state, answer) => ({ ...state, spreadsheets: answer })
|
|
616
|
+
}
|
|
617
|
+
],
|
|
618
|
+
async finalize(state, rt) {
|
|
619
|
+
if (!state.spreadsheets) {
|
|
620
|
+
throw new Error("Google Sheets setup: incomplete state on finalize");
|
|
621
|
+
}
|
|
622
|
+
const targetIds = await resolveSetupSelection({
|
|
623
|
+
selected: state.spreadsheets,
|
|
624
|
+
allSentinel: ALL_SPREADSHEETS,
|
|
625
|
+
fetchAll: async () => {
|
|
626
|
+
const files = await listSpreadsheets(rt.config);
|
|
627
|
+
return files.map((f) => f.id);
|
|
628
|
+
},
|
|
629
|
+
limit: GOOGLE_SHEETS_SETUP_MAX_SPREADSHEETS
|
|
630
|
+
});
|
|
631
|
+
const sections = ["## Google Sheets", ""];
|
|
632
|
+
if (targetIds.length === 0) {
|
|
633
|
+
sections.push(
|
|
634
|
+
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."
|
|
635
|
+
);
|
|
636
|
+
return sections.join("\n");
|
|
637
|
+
}
|
|
638
|
+
for (const id of targetIds) {
|
|
639
|
+
const url = `https://sheets.googleapis.com/v4/spreadsheets/${encodeURIComponent(id)}?fields=spreadsheetId,properties(title),sheets(properties(title,sheetId,gridProperties))`;
|
|
640
|
+
const meta = await googleApiFetch(
|
|
641
|
+
rt.config,
|
|
642
|
+
url
|
|
643
|
+
);
|
|
644
|
+
const title = meta.properties?.title ?? id;
|
|
645
|
+
sections.push(`### Spreadsheet: ${title}`, "", `- ID: ${id}`, "");
|
|
646
|
+
const sheets = meta.sheets ?? [];
|
|
647
|
+
if (sheets.length === 0) {
|
|
648
|
+
sections.push("- (no sheet tabs)", "");
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
sections.push("| Sheet | Rows | Columns |");
|
|
652
|
+
sections.push("|-------|------|---------|");
|
|
653
|
+
for (const s of sheets) {
|
|
654
|
+
const p = s.properties ?? {};
|
|
655
|
+
const sheetTitle = p.title ?? "(untitled)";
|
|
656
|
+
const rows = p.gridProperties?.rowCount ?? "-";
|
|
657
|
+
const cols = p.gridProperties?.columnCount ?? "-";
|
|
658
|
+
sections.push(`| ${sheetTitle} | ${rows} | ${cols} |`);
|
|
659
|
+
}
|
|
660
|
+
sections.push("");
|
|
661
|
+
}
|
|
662
|
+
return sections.join("\n");
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
|
|
464
666
|
// ../connectors/src/connectors/google-sheets/parameters.ts
|
|
667
|
+
init_parameter_definition();
|
|
465
668
|
var parameters = {
|
|
466
669
|
spreadsheetId: new ParameterDefinition({
|
|
467
670
|
slug: "spreadsheet-id",
|
|
@@ -479,6 +682,7 @@ var tools = { request: requestTool };
|
|
|
479
682
|
var googleSheetsConnector = new ConnectorPlugin({
|
|
480
683
|
slug: "google-sheets",
|
|
481
684
|
authType: AUTH_TYPES.OAUTH,
|
|
685
|
+
skipConnectionCheckOnCreate: true,
|
|
482
686
|
name: "Google Sheets",
|
|
483
687
|
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
688
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1UPQuggyiZmbb26CuaSr2h/032770e8739b183fa00b7625f024e536/google-sheets.svg",
|
|
@@ -644,7 +848,8 @@ await sheets.batchUpdate(spreadsheetId, [
|
|
|
644
848
|
]);
|
|
645
849
|
\`\`\``
|
|
646
850
|
},
|
|
647
|
-
tools
|
|
851
|
+
tools,
|
|
852
|
+
setup: (params, ctx, config) => runSetupFlow(googleSheetsSetupFlow, params, ctx, config)
|
|
648
853
|
});
|
|
649
854
|
|
|
650
855
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -673,6 +878,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
673
878
|
import { getContext } from "hono/context-storage";
|
|
674
879
|
import { getCookie } from "hono/cookie";
|
|
675
880
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
881
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
676
882
|
function normalizeHeaders(input) {
|
|
677
883
|
const out = {};
|
|
678
884
|
if (!input) return out;
|
|
@@ -681,6 +887,11 @@ function normalizeHeaders(input) {
|
|
|
681
887
|
});
|
|
682
888
|
return out;
|
|
683
889
|
}
|
|
890
|
+
function extractInputUrl(input) {
|
|
891
|
+
if (typeof input === "string") return input;
|
|
892
|
+
if (input instanceof URL) return input.href;
|
|
893
|
+
return input.url;
|
|
894
|
+
}
|
|
684
895
|
function createSandboxProxyFetch(connectionId) {
|
|
685
896
|
return async (input, init) => {
|
|
686
897
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -690,10 +901,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
690
901
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
691
902
|
);
|
|
692
903
|
}
|
|
693
|
-
const originalUrl =
|
|
904
|
+
const originalUrl = extractInputUrl(input);
|
|
905
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
906
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
907
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
908
|
+
return fetch(sessionUrl, {
|
|
909
|
+
method: "POST",
|
|
910
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
911
|
+
});
|
|
912
|
+
}
|
|
694
913
|
const originalMethod = init?.method ?? "GET";
|
|
695
914
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
696
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
697
915
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
698
916
|
return fetch(proxyUrl, {
|
|
699
917
|
method: "POST",
|
|
@@ -719,10 +937,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
719
937
|
}
|
|
720
938
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
721
939
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
940
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
722
941
|
return async (input, init) => {
|
|
723
|
-
const originalUrl =
|
|
724
|
-
const originalMethod = init?.method ?? "GET";
|
|
725
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
942
|
+
const originalUrl = extractInputUrl(input);
|
|
726
943
|
const c = getContext();
|
|
727
944
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
728
945
|
if (!appSession) {
|
|
@@ -730,6 +947,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
730
947
|
"No authentication method available for connection proxy."
|
|
731
948
|
);
|
|
732
949
|
}
|
|
950
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
951
|
+
return fetch(sessionUrl, {
|
|
952
|
+
method: "POST",
|
|
953
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
const originalMethod = init?.method ?? "GET";
|
|
957
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
733
958
|
return fetch(proxyUrl, {
|
|
734
959
|
method: "POST",
|
|
735
960
|
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,76 @@ 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
|
+
const pendingParameterUpdates = [];
|
|
242
|
+
for (const step of flow.steps) {
|
|
243
|
+
const ans = ctx.answers[answerIdx];
|
|
244
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
245
|
+
state = step.applyAnswer(state, ans.answer);
|
|
246
|
+
if (step.toParameterUpdates) {
|
|
247
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
248
|
+
}
|
|
249
|
+
answerIdx += 1;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
253
|
+
if (step.type === "text") {
|
|
254
|
+
if (step.fetchOptions) {
|
|
255
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
256
|
+
if (options2.length === 0) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
type: "nextQuestion",
|
|
262
|
+
questionSlug: step.slug,
|
|
263
|
+
question: step.question[ctx.language],
|
|
264
|
+
questionType: "text",
|
|
265
|
+
allowFreeText: resolvedAllowFreeText,
|
|
266
|
+
...pendingParameterUpdates.length > 0 && {
|
|
267
|
+
parameterUpdates: pendingParameterUpdates
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
272
|
+
if (options.length === 0) {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
type: "nextQuestion",
|
|
277
|
+
questionSlug: step.slug,
|
|
278
|
+
question: step.question[ctx.language],
|
|
279
|
+
questionType: step.type,
|
|
280
|
+
options,
|
|
281
|
+
allowFreeText: resolvedAllowFreeText,
|
|
282
|
+
...pendingParameterUpdates.length > 0 && {
|
|
283
|
+
parameterUpdates: pendingParameterUpdates
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
288
|
+
return {
|
|
289
|
+
type: "fulfilled",
|
|
290
|
+
dataInvestigationResult,
|
|
291
|
+
...pendingParameterUpdates.length > 0 && {
|
|
292
|
+
parameterUpdates: pendingParameterUpdates
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
async function resolveSetupSelection(params) {
|
|
297
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
298
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
299
|
+
return resolved.slice(0, limit);
|
|
300
|
+
}
|
|
301
|
+
|
|
208
302
|
// ../connectors/src/auth-types.ts
|
|
209
303
|
var AUTH_TYPES = {
|
|
210
304
|
OAUTH: "oauth",
|
|
@@ -235,6 +329,89 @@ var googleSlidesOnboarding = new ConnectorOnboarding({
|
|
|
235
329
|
}
|
|
236
330
|
});
|
|
237
331
|
|
|
332
|
+
// ../connectors/src/connectors/google-slides/utils.ts
|
|
333
|
+
async function googleApiFetch(config, url) {
|
|
334
|
+
const res = await config.proxyFetch(url, { method: "GET" });
|
|
335
|
+
if (!res.ok) {
|
|
336
|
+
const text = await res.text().catch(() => res.statusText);
|
|
337
|
+
throw new Error(`Google Slides ${url} failed: HTTP ${res.status} ${text}`);
|
|
338
|
+
}
|
|
339
|
+
return await res.json();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ../connectors/src/connectors/google-slides/setup-flow.ts
|
|
343
|
+
var ALL_PRESENTATIONS = "__ALL_PRESENTATIONS__";
|
|
344
|
+
var GOOGLE_SLIDES_SETUP_MAX_PRESENTATIONS = 10;
|
|
345
|
+
var DRIVE_LIST_PAGE_SIZE = 50;
|
|
346
|
+
async function listPresentations(config) {
|
|
347
|
+
const q = encodeURIComponent(
|
|
348
|
+
"mimeType='application/vnd.google-apps.presentation' and trashed=false"
|
|
349
|
+
);
|
|
350
|
+
const url = `https://www.googleapis.com/drive/v3/files?q=${q}&pageSize=${DRIVE_LIST_PAGE_SIZE}&orderBy=modifiedTime%20desc&fields=files(id,name,modifiedTime)`;
|
|
351
|
+
const data = await googleApiFetch(config, url);
|
|
352
|
+
return data.files ?? [];
|
|
353
|
+
}
|
|
354
|
+
var googleSlidesSetupFlow = {
|
|
355
|
+
initialState: () => ({}),
|
|
356
|
+
steps: [
|
|
357
|
+
{
|
|
358
|
+
slug: "presentations",
|
|
359
|
+
type: "multiSelect",
|
|
360
|
+
question: {
|
|
361
|
+
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",
|
|
362
|
+
en: "Select target presentations (multi-select allowed)"
|
|
363
|
+
},
|
|
364
|
+
async fetchOptions(_state, rt) {
|
|
365
|
+
const files = await listPresentations(rt.config);
|
|
366
|
+
return [
|
|
367
|
+
{
|
|
368
|
+
value: ALL_PRESENTATIONS,
|
|
369
|
+
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"
|
|
370
|
+
},
|
|
371
|
+
...files.map((f) => ({ value: f.id, label: f.name }))
|
|
372
|
+
];
|
|
373
|
+
},
|
|
374
|
+
applyAnswer: (state, answer) => ({ ...state, presentations: answer })
|
|
375
|
+
}
|
|
376
|
+
],
|
|
377
|
+
async finalize(state, rt) {
|
|
378
|
+
if (!state.presentations) {
|
|
379
|
+
throw new Error("Google Slides setup: incomplete state on finalize");
|
|
380
|
+
}
|
|
381
|
+
const targetIds = await resolveSetupSelection({
|
|
382
|
+
selected: state.presentations,
|
|
383
|
+
allSentinel: ALL_PRESENTATIONS,
|
|
384
|
+
fetchAll: async () => {
|
|
385
|
+
const files = await listPresentations(rt.config);
|
|
386
|
+
return files.map((f) => f.id);
|
|
387
|
+
},
|
|
388
|
+
limit: GOOGLE_SLIDES_SETUP_MAX_PRESENTATIONS
|
|
389
|
+
});
|
|
390
|
+
const sections = ["## Google Slides", ""];
|
|
391
|
+
if (targetIds.length === 0) {
|
|
392
|
+
sections.push(
|
|
393
|
+
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."
|
|
394
|
+
);
|
|
395
|
+
return sections.join("\n");
|
|
396
|
+
}
|
|
397
|
+
for (const id of targetIds) {
|
|
398
|
+
const url = `https://slides.googleapis.com/v1/presentations/${encodeURIComponent(id)}?fields=presentationId,title,revisionId,slides(objectId)`;
|
|
399
|
+
const meta = await googleApiFetch(
|
|
400
|
+
rt.config,
|
|
401
|
+
url
|
|
402
|
+
);
|
|
403
|
+
const title = meta.title ?? id;
|
|
404
|
+
const slideCount = (meta.slides ?? []).length;
|
|
405
|
+
sections.push(`### Presentation: ${title}`, "");
|
|
406
|
+
sections.push(`- ID: ${id}`);
|
|
407
|
+
sections.push(`- Slides: ${slideCount}`);
|
|
408
|
+
if (meta.revisionId) sections.push(`- Revision: ${meta.revisionId}`);
|
|
409
|
+
sections.push("");
|
|
410
|
+
}
|
|
411
|
+
return sections.join("\n");
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
238
415
|
// ../connectors/src/connectors/google-slides/parameters.ts
|
|
239
416
|
var parameters = {};
|
|
240
417
|
|
|
@@ -365,6 +542,7 @@ var tools = { request: requestTool };
|
|
|
365
542
|
var googleSlidesConnector = new ConnectorPlugin({
|
|
366
543
|
slug: "google-slides",
|
|
367
544
|
authType: AUTH_TYPES.OAUTH,
|
|
545
|
+
skipConnectionCheckOnCreate: true,
|
|
368
546
|
name: "Google Slides",
|
|
369
547
|
description: "Connect to Google Slides for presentation data access and creation using OAuth.",
|
|
370
548
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4oyF4yTRpemMA43X49masx/e1582d25e3b4c9a63ba83df2147c1968/google_slide.png",
|
|
@@ -540,7 +718,8 @@ await slides.batchUpdate(presentationId, [
|
|
|
540
718
|
|
|
541
719
|
\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
720
|
},
|
|
543
|
-
tools
|
|
721
|
+
tools,
|
|
722
|
+
setup: (params, ctx, config) => runSetupFlow(googleSlidesSetupFlow, params, ctx, config)
|
|
544
723
|
});
|
|
545
724
|
|
|
546
725
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -569,6 +748,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
569
748
|
import { getContext } from "hono/context-storage";
|
|
570
749
|
import { getCookie } from "hono/cookie";
|
|
571
750
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
751
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
572
752
|
function normalizeHeaders(input) {
|
|
573
753
|
const out = {};
|
|
574
754
|
if (!input) return out;
|
|
@@ -577,6 +757,11 @@ function normalizeHeaders(input) {
|
|
|
577
757
|
});
|
|
578
758
|
return out;
|
|
579
759
|
}
|
|
760
|
+
function extractInputUrl(input) {
|
|
761
|
+
if (typeof input === "string") return input;
|
|
762
|
+
if (input instanceof URL) return input.href;
|
|
763
|
+
return input.url;
|
|
764
|
+
}
|
|
580
765
|
function createSandboxProxyFetch(connectionId) {
|
|
581
766
|
return async (input, init) => {
|
|
582
767
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -586,10 +771,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
586
771
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
587
772
|
);
|
|
588
773
|
}
|
|
589
|
-
const originalUrl =
|
|
774
|
+
const originalUrl = extractInputUrl(input);
|
|
775
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
776
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
777
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
778
|
+
return fetch(sessionUrl, {
|
|
779
|
+
method: "POST",
|
|
780
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
781
|
+
});
|
|
782
|
+
}
|
|
590
783
|
const originalMethod = init?.method ?? "GET";
|
|
591
784
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
592
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
593
785
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
594
786
|
return fetch(proxyUrl, {
|
|
595
787
|
method: "POST",
|
|
@@ -615,10 +807,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
615
807
|
}
|
|
616
808
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
617
809
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
810
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
618
811
|
return async (input, init) => {
|
|
619
|
-
const originalUrl =
|
|
620
|
-
const originalMethod = init?.method ?? "GET";
|
|
621
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
812
|
+
const originalUrl = extractInputUrl(input);
|
|
622
813
|
const c = getContext();
|
|
623
814
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
624
815
|
if (!appSession) {
|
|
@@ -626,6 +817,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
626
817
|
"No authentication method available for connection proxy."
|
|
627
818
|
);
|
|
628
819
|
}
|
|
820
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
821
|
+
return fetch(sessionUrl, {
|
|
822
|
+
method: "POST",
|
|
823
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
const originalMethod = init?.method ?? "GET";
|
|
827
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
629
828
|
return fetch(proxyUrl, {
|
|
630
829
|
method: "POST",
|
|
631
830
|
headers: {
|