@squadbase/vite-server 0.1.12-dev.93b8799 → 0.1.17-dev.24af54e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +12128 -934
- package/dist/connectors/airtable-oauth.js +248 -46
- package/dist/connectors/airtable.js +285 -51
- package/dist/connectors/amplitude.js +288 -47
- package/dist/connectors/anthropic.js +126 -47
- package/dist/connectors/asana.js +293 -49
- package/dist/connectors/attio.js +268 -49
- package/dist/connectors/aws-billing.js +253 -46
- package/dist/connectors/azure-sql.js +387 -102
- package/dist/connectors/backlog-api-key.js +283 -47
- package/dist/connectors/clickup.js +304 -49
- package/dist/connectors/cosmosdb.js +271 -50
- package/dist/connectors/customerio.js +285 -47
- package/dist/connectors/dbt.js +306 -47
- package/dist/connectors/freshdesk.js +308 -53
- package/dist/connectors/freshsales.js +299 -52
- package/dist/connectors/freshservice.js +327 -53
- package/dist/connectors/gamma.js +293 -52
- package/dist/connectors/gemini.js +125 -47
- package/dist/connectors/github.js +352 -49
- package/dist/connectors/gmail-oauth.js +170 -7
- package/dist/connectors/gmail.js +316 -47
- package/dist/connectors/google-ads.js +254 -46
- package/dist/connectors/google-analytics-oauth.js +276 -46
- package/dist/connectors/google-analytics.js +378 -49
- package/dist/connectors/google-audit-log.js +404 -47
- package/dist/connectors/google-calendar-oauth.js +225 -46
- package/dist/connectors/google-calendar.js +325 -47
- package/dist/connectors/google-docs.js +186 -6
- package/dist/connectors/google-drive.js +228 -5
- package/dist/connectors/google-search-console-oauth.js +222 -46
- package/dist/connectors/google-sheets.js +238 -47
- package/dist/connectors/google-slides.js +171 -6
- package/dist/connectors/grafana.js +298 -49
- package/dist/connectors/hubspot-oauth.js +174 -5
- package/dist/connectors/hubspot.js +272 -49
- package/dist/connectors/influxdb.js +382 -51
- package/dist/connectors/intercom-oauth.js +176 -5
- package/dist/connectors/intercom.js +268 -49
- package/dist/connectors/jdbc.js +728 -110
- package/dist/connectors/jira-api-key.js +292 -47
- package/dist/connectors/kintone-api-token.js +247 -47
- package/dist/connectors/kintone.js +294 -47
- package/dist/connectors/linear.js +296 -49
- package/dist/connectors/linkedin-ads.js +234 -50
- package/dist/connectors/mailchimp-oauth.js +234 -46
- package/dist/connectors/mailchimp.js +286 -49
- package/dist/connectors/meta-ads-oauth.js +239 -48
- package/dist/connectors/meta-ads.js +251 -50
- package/dist/connectors/mixpanel.js +304 -47
- package/dist/connectors/monday.js +326 -49
- package/dist/connectors/mongodb.js +285 -57
- package/dist/connectors/notion-oauth.js +197 -5
- package/dist/connectors/notion.js +289 -51
- package/dist/connectors/openai.js +125 -47
- package/dist/connectors/oracle.js +405 -103
- package/dist/connectors/outlook-oauth.js +170 -5
- package/dist/connectors/powerbi-oauth.js +217 -5
- package/dist/connectors/salesforce.js +350 -49
- package/dist/connectors/semrush.js +280 -49
- package/dist/connectors/sentry.js +255 -50
- package/dist/connectors/shopify-oauth.js +153 -5
- package/dist/connectors/shopify.js +323 -47
- package/dist/connectors/sqlserver.js +381 -102
- package/dist/connectors/stripe-api-key.js +235 -46
- package/dist/connectors/stripe-oauth.js +168 -5
- package/dist/connectors/supabase.js +269 -48
- package/dist/connectors/tableau.js +337 -206
- package/dist/connectors/tiktok-ads.js +245 -48
- package/dist/connectors/wix-store.js +286 -49
- package/dist/connectors/zendesk-oauth.js +205 -5
- package/dist/connectors/zendesk.js +324 -47
- package/dist/index.d.ts +149 -1
- package/dist/index.js +18297 -6886
- package/dist/main.js +12785 -1382
- package/dist/vite-plugin.js +12140 -936
- package/package.json +1 -1
|
@@ -121,6 +121,20 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
121
121
|
* `runSetupFlow` from `setup-flow.ts`.
|
|
122
122
|
*/
|
|
123
123
|
setup;
|
|
124
|
+
/**
|
|
125
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
126
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
127
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
128
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
129
|
+
*
|
|
130
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
131
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
132
|
+
* connectionId, which doesn't exist until the row is saved
|
|
133
|
+
*
|
|
134
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
135
|
+
* the default verify-on-create behavior without opt-in.
|
|
136
|
+
*/
|
|
137
|
+
skipConnectionCheckOnCreate;
|
|
124
138
|
constructor(config) {
|
|
125
139
|
this.slug = config.slug;
|
|
126
140
|
this.authType = config.authType;
|
|
@@ -138,6 +152,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
138
152
|
this.query = config.query;
|
|
139
153
|
this.checkConnection = config.checkConnection;
|
|
140
154
|
this.setup = config.setup;
|
|
155
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
141
156
|
}
|
|
142
157
|
get connectorKey() {
|
|
143
158
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -202,6 +217,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
202
217
|
}
|
|
203
218
|
};
|
|
204
219
|
|
|
220
|
+
// ../connectors/src/setup-flow.ts
|
|
221
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
222
|
+
const runtime = {
|
|
223
|
+
params,
|
|
224
|
+
language: ctx.language,
|
|
225
|
+
config
|
|
226
|
+
};
|
|
227
|
+
let state = flow.initialState();
|
|
228
|
+
let answerIdx = 0;
|
|
229
|
+
for (const step of flow.steps) {
|
|
230
|
+
const ans = ctx.answers[answerIdx];
|
|
231
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
232
|
+
state = step.applyAnswer(state, ans.answer);
|
|
233
|
+
answerIdx += 1;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (step.type === "text") {
|
|
237
|
+
return {
|
|
238
|
+
type: "nextQuestion",
|
|
239
|
+
questionSlug: step.slug,
|
|
240
|
+
question: step.question[ctx.language],
|
|
241
|
+
questionType: "text"
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
245
|
+
if (options.length === 0) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
type: "nextQuestion",
|
|
250
|
+
questionSlug: step.slug,
|
|
251
|
+
question: step.question[ctx.language],
|
|
252
|
+
questionType: step.type,
|
|
253
|
+
options
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
257
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
258
|
+
}
|
|
259
|
+
async function resolveSetupSelection(params) {
|
|
260
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
261
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
262
|
+
return resolved.slice(0, limit);
|
|
263
|
+
}
|
|
264
|
+
|
|
205
265
|
// ../connectors/src/auth-types.ts
|
|
206
266
|
var AUTH_TYPES = {
|
|
207
267
|
OAUTH: "oauth",
|
|
@@ -232,6 +292,104 @@ var googleDocsOnboarding = new ConnectorOnboarding({
|
|
|
232
292
|
}
|
|
233
293
|
});
|
|
234
294
|
|
|
295
|
+
// ../connectors/src/connectors/google-docs/utils.ts
|
|
296
|
+
async function googleApiFetch(config, url) {
|
|
297
|
+
const res = await config.proxyFetch(url, { method: "GET" });
|
|
298
|
+
if (!res.ok) {
|
|
299
|
+
const text = await res.text().catch(() => res.statusText);
|
|
300
|
+
throw new Error(`Google Docs ${url} failed: HTTP ${res.status} ${text}`);
|
|
301
|
+
}
|
|
302
|
+
return await res.json();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ../connectors/src/connectors/google-docs/setup-flow.ts
|
|
306
|
+
var ALL_DOCUMENTS = "__ALL_DOCUMENTS__";
|
|
307
|
+
var GOOGLE_DOCS_SETUP_MAX_DOCUMENTS = 10;
|
|
308
|
+
var DRIVE_LIST_PAGE_SIZE = 50;
|
|
309
|
+
async function listDocuments(config) {
|
|
310
|
+
const q = encodeURIComponent(
|
|
311
|
+
"mimeType='application/vnd.google-apps.document' and trashed=false"
|
|
312
|
+
);
|
|
313
|
+
const url = `https://www.googleapis.com/drive/v3/files?q=${q}&pageSize=${DRIVE_LIST_PAGE_SIZE}&orderBy=modifiedTime%20desc&fields=files(id,name,modifiedTime)`;
|
|
314
|
+
const data = await googleApiFetch(config, url);
|
|
315
|
+
return data.files ?? [];
|
|
316
|
+
}
|
|
317
|
+
function extractHeadings(meta, max) {
|
|
318
|
+
const out = [];
|
|
319
|
+
for (const c of meta.body?.content ?? []) {
|
|
320
|
+
const p = c.paragraph;
|
|
321
|
+
if (!p) continue;
|
|
322
|
+
const style = p.paragraphStyle?.namedStyleType ?? "";
|
|
323
|
+
if (!style.startsWith("HEADING_")) continue;
|
|
324
|
+
const text = (p.elements ?? []).map((el) => el.textRun?.content ?? "").join("").trim();
|
|
325
|
+
if (text) out.push(`${style}: ${text}`);
|
|
326
|
+
if (out.length >= max) break;
|
|
327
|
+
}
|
|
328
|
+
return out;
|
|
329
|
+
}
|
|
330
|
+
var googleDocsSetupFlow = {
|
|
331
|
+
initialState: () => ({}),
|
|
332
|
+
steps: [
|
|
333
|
+
{
|
|
334
|
+
slug: "documents",
|
|
335
|
+
type: "multiSelect",
|
|
336
|
+
question: {
|
|
337
|
+
ja: "\u5BFE\u8C61\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
338
|
+
en: "Select target documents (multi-select allowed)"
|
|
339
|
+
},
|
|
340
|
+
async fetchOptions(_state, rt) {
|
|
341
|
+
const files = await listDocuments(rt.config);
|
|
342
|
+
return [
|
|
343
|
+
{
|
|
344
|
+
value: ALL_DOCUMENTS,
|
|
345
|
+
label: rt.language === "ja" ? "\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u3059\u3079\u3066\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8" : "All accessible documents"
|
|
346
|
+
},
|
|
347
|
+
...files.map((f) => ({ value: f.id, label: f.name }))
|
|
348
|
+
];
|
|
349
|
+
},
|
|
350
|
+
applyAnswer: (state, answer) => ({ ...state, documents: answer })
|
|
351
|
+
}
|
|
352
|
+
],
|
|
353
|
+
async finalize(state, rt) {
|
|
354
|
+
if (!state.documents) {
|
|
355
|
+
throw new Error("Google Docs setup: incomplete state on finalize");
|
|
356
|
+
}
|
|
357
|
+
const targetIds = await resolveSetupSelection({
|
|
358
|
+
selected: state.documents,
|
|
359
|
+
allSentinel: ALL_DOCUMENTS,
|
|
360
|
+
fetchAll: async () => {
|
|
361
|
+
const files = await listDocuments(rt.config);
|
|
362
|
+
return files.map((f) => f.id);
|
|
363
|
+
},
|
|
364
|
+
limit: GOOGLE_DOCS_SETUP_MAX_DOCUMENTS
|
|
365
|
+
});
|
|
366
|
+
const sections = ["## Google Docs", ""];
|
|
367
|
+
if (targetIds.length === 0) {
|
|
368
|
+
sections.push(
|
|
369
|
+
rt.language === "ja" ? "\u5BFE\u8C61\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002" : "No documents selected."
|
|
370
|
+
);
|
|
371
|
+
return sections.join("\n");
|
|
372
|
+
}
|
|
373
|
+
for (const id of targetIds) {
|
|
374
|
+
const url = `https://docs.googleapis.com/v1/documents/${encodeURIComponent(
|
|
375
|
+
id
|
|
376
|
+
)}`;
|
|
377
|
+
const meta = await googleApiFetch(rt.config, url);
|
|
378
|
+
const title = meta.title ?? id;
|
|
379
|
+
sections.push(`### Document: ${title}`, "");
|
|
380
|
+
sections.push(`- ID: ${id}`);
|
|
381
|
+
if (meta.revisionId) sections.push(`- Revision: ${meta.revisionId}`);
|
|
382
|
+
const headings = extractHeadings(meta, 5);
|
|
383
|
+
if (headings.length > 0) {
|
|
384
|
+
sections.push("- Outline:");
|
|
385
|
+
for (const h of headings) sections.push(` - ${h}`);
|
|
386
|
+
}
|
|
387
|
+
sections.push("");
|
|
388
|
+
}
|
|
389
|
+
return sections.join("\n");
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
235
393
|
// ../connectors/src/connectors/google-docs/parameters.ts
|
|
236
394
|
var parameters = {};
|
|
237
395
|
|
|
@@ -364,6 +522,7 @@ var tools = { request: requestTool };
|
|
|
364
522
|
var googleDocsConnector = new ConnectorPlugin({
|
|
365
523
|
slug: "google-docs",
|
|
366
524
|
authType: AUTH_TYPES.OAUTH,
|
|
525
|
+
skipConnectionCheckOnCreate: true,
|
|
367
526
|
name: "Google Docs",
|
|
368
527
|
description: "Connect to Google Docs for document data access and creation using OAuth.",
|
|
369
528
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6vvcGJisvXjOumeTvswjzf/e9bb39e453cc0b71a20f26019b23b0d2/google_docs.png",
|
|
@@ -519,7 +678,8 @@ await docs.batchUpdate(documentId, [
|
|
|
519
678
|
]);
|
|
520
679
|
\`\`\``
|
|
521
680
|
},
|
|
522
|
-
tools
|
|
681
|
+
tools,
|
|
682
|
+
setup: (params, ctx, config) => runSetupFlow(googleDocsSetupFlow, params, ctx, config)
|
|
523
683
|
});
|
|
524
684
|
|
|
525
685
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -548,6 +708,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
548
708
|
import { getContext } from "hono/context-storage";
|
|
549
709
|
import { getCookie } from "hono/cookie";
|
|
550
710
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
711
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
551
712
|
function normalizeHeaders(input) {
|
|
552
713
|
const out = {};
|
|
553
714
|
if (!input) return out;
|
|
@@ -556,6 +717,11 @@ function normalizeHeaders(input) {
|
|
|
556
717
|
});
|
|
557
718
|
return out;
|
|
558
719
|
}
|
|
720
|
+
function extractInputUrl(input) {
|
|
721
|
+
if (typeof input === "string") return input;
|
|
722
|
+
if (input instanceof URL) return input.href;
|
|
723
|
+
return input.url;
|
|
724
|
+
}
|
|
559
725
|
function createSandboxProxyFetch(connectionId) {
|
|
560
726
|
return async (input, init) => {
|
|
561
727
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -565,10 +731,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
565
731
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
566
732
|
);
|
|
567
733
|
}
|
|
568
|
-
const originalUrl =
|
|
734
|
+
const originalUrl = extractInputUrl(input);
|
|
735
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
736
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
737
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
738
|
+
return fetch(sessionUrl, {
|
|
739
|
+
method: "POST",
|
|
740
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
741
|
+
});
|
|
742
|
+
}
|
|
569
743
|
const originalMethod = init?.method ?? "GET";
|
|
570
744
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
571
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
572
745
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
573
746
|
return fetch(proxyUrl, {
|
|
574
747
|
method: "POST",
|
|
@@ -594,10 +767,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
594
767
|
}
|
|
595
768
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
596
769
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
770
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
597
771
|
return async (input, init) => {
|
|
598
|
-
const originalUrl =
|
|
599
|
-
const originalMethod = init?.method ?? "GET";
|
|
600
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
772
|
+
const originalUrl = extractInputUrl(input);
|
|
601
773
|
const c = getContext();
|
|
602
774
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
603
775
|
if (!appSession) {
|
|
@@ -605,6 +777,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
605
777
|
"No authentication method available for connection proxy."
|
|
606
778
|
);
|
|
607
779
|
}
|
|
780
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
781
|
+
return fetch(sessionUrl, {
|
|
782
|
+
method: "POST",
|
|
783
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
const originalMethod = init?.method ?? "GET";
|
|
787
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
608
788
|
return fetch(proxyUrl, {
|
|
609
789
|
method: "POST",
|
|
610
790
|
headers: {
|
|
@@ -210,6 +210,20 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
210
210
|
* `runSetupFlow` from `setup-flow.ts`.
|
|
211
211
|
*/
|
|
212
212
|
setup;
|
|
213
|
+
/**
|
|
214
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
215
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
216
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
217
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
218
|
+
*
|
|
219
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
220
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
221
|
+
* connectionId, which doesn't exist until the row is saved
|
|
222
|
+
*
|
|
223
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
224
|
+
* the default verify-on-create behavior without opt-in.
|
|
225
|
+
*/
|
|
226
|
+
skipConnectionCheckOnCreate;
|
|
213
227
|
constructor(config) {
|
|
214
228
|
this.slug = config.slug;
|
|
215
229
|
this.authType = config.authType;
|
|
@@ -227,6 +241,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
227
241
|
this.query = config.query;
|
|
228
242
|
this.checkConnection = config.checkConnection;
|
|
229
243
|
this.setup = config.setup;
|
|
244
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
230
245
|
}
|
|
231
246
|
get connectorKey() {
|
|
232
247
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -291,6 +306,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
291
306
|
}
|
|
292
307
|
};
|
|
293
308
|
|
|
309
|
+
// ../connectors/src/setup-flow.ts
|
|
310
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
311
|
+
const runtime = {
|
|
312
|
+
params,
|
|
313
|
+
language: ctx.language,
|
|
314
|
+
config
|
|
315
|
+
};
|
|
316
|
+
let state = flow.initialState();
|
|
317
|
+
let answerIdx = 0;
|
|
318
|
+
for (const step of flow.steps) {
|
|
319
|
+
const ans = ctx.answers[answerIdx];
|
|
320
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
321
|
+
state = step.applyAnswer(state, ans.answer);
|
|
322
|
+
answerIdx += 1;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (step.type === "text") {
|
|
326
|
+
return {
|
|
327
|
+
type: "nextQuestion",
|
|
328
|
+
questionSlug: step.slug,
|
|
329
|
+
question: step.question[ctx.language],
|
|
330
|
+
questionType: "text"
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
334
|
+
if (options.length === 0) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
type: "nextQuestion",
|
|
339
|
+
questionSlug: step.slug,
|
|
340
|
+
question: step.question[ctx.language],
|
|
341
|
+
questionType: step.type,
|
|
342
|
+
options
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
346
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
347
|
+
}
|
|
348
|
+
async function resolveSetupSelection(params) {
|
|
349
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
350
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
351
|
+
return resolved.slice(0, limit);
|
|
352
|
+
}
|
|
353
|
+
|
|
294
354
|
// ../connectors/src/auth-types.ts
|
|
295
355
|
var AUTH_TYPES = {
|
|
296
356
|
OAUTH: "oauth",
|
|
@@ -321,6 +381,147 @@ var googleDriveOnboarding = new ConnectorOnboarding({
|
|
|
321
381
|
}
|
|
322
382
|
});
|
|
323
383
|
|
|
384
|
+
// ../connectors/src/connectors/google-drive/utils.ts
|
|
385
|
+
async function googleApiFetch(config, url) {
|
|
386
|
+
const res = await config.proxyFetch(url, { method: "GET" });
|
|
387
|
+
if (!res.ok) {
|
|
388
|
+
const text = await res.text().catch(() => res.statusText);
|
|
389
|
+
throw new Error(`Google Drive ${url} failed: HTTP ${res.status} ${text}`);
|
|
390
|
+
}
|
|
391
|
+
return await res.json();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ../connectors/src/connectors/google-drive/setup-flow.ts
|
|
395
|
+
var MY_DRIVE = "__MY_DRIVE__";
|
|
396
|
+
var ALL_ITEMS = "__ALL_ITEMS__";
|
|
397
|
+
var GOOGLE_DRIVE_SETUP_MAX_ITEMS = 25;
|
|
398
|
+
var PAGE_SIZE = 50;
|
|
399
|
+
var FOLDER_MIME = "application/vnd.google-apps.folder";
|
|
400
|
+
async function listSharedDrives(config) {
|
|
401
|
+
const url = `https://www.googleapis.com/drive/v3/drives?pageSize=100&fields=drives(id,name)`;
|
|
402
|
+
try {
|
|
403
|
+
const data = await googleApiFetch(config, url);
|
|
404
|
+
return data.drives ?? [];
|
|
405
|
+
} catch {
|
|
406
|
+
return [];
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function listItemsUrl(driveId) {
|
|
410
|
+
const params = new URLSearchParams({
|
|
411
|
+
pageSize: String(PAGE_SIZE),
|
|
412
|
+
fields: "files(id,name,mimeType,modifiedTime)",
|
|
413
|
+
orderBy: "modifiedTime desc"
|
|
414
|
+
});
|
|
415
|
+
if (driveId) {
|
|
416
|
+
params.set("corpora", "drive");
|
|
417
|
+
params.set("driveId", driveId);
|
|
418
|
+
params.set("includeItemsFromAllDrives", "true");
|
|
419
|
+
params.set("supportsAllDrives", "true");
|
|
420
|
+
params.set("q", `'${driveId}' in parents and trashed=false`);
|
|
421
|
+
} else {
|
|
422
|
+
params.set("q", `'root' in parents and trashed=false`);
|
|
423
|
+
}
|
|
424
|
+
return `https://www.googleapis.com/drive/v3/files?${params.toString()}`;
|
|
425
|
+
}
|
|
426
|
+
async function listItems(config, driveId) {
|
|
427
|
+
const data = await googleApiFetch(
|
|
428
|
+
config,
|
|
429
|
+
listItemsUrl(driveId)
|
|
430
|
+
);
|
|
431
|
+
return data.files ?? [];
|
|
432
|
+
}
|
|
433
|
+
var googleDriveSetupFlow = {
|
|
434
|
+
initialState: () => ({}),
|
|
435
|
+
steps: [
|
|
436
|
+
{
|
|
437
|
+
slug: "drive",
|
|
438
|
+
type: "select",
|
|
439
|
+
question: {
|
|
440
|
+
ja: "\u5BFE\u8C61\u306E\u30C9\u30E9\u30A4\u30D6\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044",
|
|
441
|
+
en: "Select the drive to use"
|
|
442
|
+
},
|
|
443
|
+
async fetchOptions(_state, rt) {
|
|
444
|
+
const shared = await listSharedDrives(rt.config);
|
|
445
|
+
const myDriveLabel = rt.language === "ja" ? "\u30DE\u30A4\u30C9\u30E9\u30A4\u30D6" : "My Drive";
|
|
446
|
+
return [
|
|
447
|
+
{ value: MY_DRIVE, label: myDriveLabel },
|
|
448
|
+
...shared.filter((d) => d.id).map((d) => ({ value: d.id, label: d.name ?? d.id }))
|
|
449
|
+
];
|
|
450
|
+
},
|
|
451
|
+
applyAnswer: (state, answer) => ({ ...state, drive: answer[0] })
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
slug: "items",
|
|
455
|
+
type: "multiSelect",
|
|
456
|
+
question: {
|
|
457
|
+
ja: "\u5BFE\u8C61\u306E\u30D5\u30A9\u30EB\u30C0\u30FB\u30D5\u30A1\u30A4\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
458
|
+
en: "Select target folders or files (multi-select allowed)"
|
|
459
|
+
},
|
|
460
|
+
async fetchOptions(state, rt) {
|
|
461
|
+
if (!state.drive) return [];
|
|
462
|
+
const driveId = state.drive === MY_DRIVE ? null : state.drive;
|
|
463
|
+
const files = await listItems(rt.config, driveId);
|
|
464
|
+
const fileOptions = files.map((f) => {
|
|
465
|
+
const isFolder = f.mimeType === FOLDER_MIME;
|
|
466
|
+
const prefix = isFolder ? "[folder] " : "";
|
|
467
|
+
return { value: f.id, label: `${prefix}${f.name}` };
|
|
468
|
+
});
|
|
469
|
+
return [
|
|
470
|
+
{
|
|
471
|
+
value: ALL_ITEMS,
|
|
472
|
+
label: rt.language === "ja" ? "\u3053\u306E\u30C9\u30E9\u30A4\u30D6\u306E\u3059\u3079\u3066\u306E\u30A2\u30A4\u30C6\u30E0" : "All items in this drive"
|
|
473
|
+
},
|
|
474
|
+
...fileOptions
|
|
475
|
+
];
|
|
476
|
+
},
|
|
477
|
+
applyAnswer: (state, answer) => ({ ...state, items: answer })
|
|
478
|
+
}
|
|
479
|
+
],
|
|
480
|
+
async finalize(state, rt) {
|
|
481
|
+
if (!state.drive || !state.items) {
|
|
482
|
+
throw new Error("Google Drive setup: incomplete state on finalize");
|
|
483
|
+
}
|
|
484
|
+
const driveId = state.drive === MY_DRIVE ? null : state.drive;
|
|
485
|
+
const allFiles = await listItems(rt.config, driveId);
|
|
486
|
+
const filesById = new Map(allFiles.map((f) => [f.id, f]));
|
|
487
|
+
const targetIds = await resolveSetupSelection({
|
|
488
|
+
selected: state.items,
|
|
489
|
+
allSentinel: ALL_ITEMS,
|
|
490
|
+
fetchAll: async () => allFiles.map((f) => f.id),
|
|
491
|
+
limit: GOOGLE_DRIVE_SETUP_MAX_ITEMS
|
|
492
|
+
});
|
|
493
|
+
const driveLabel = state.drive === MY_DRIVE ? rt.language === "ja" ? "\u30DE\u30A4\u30C9\u30E9\u30A4\u30D6" : "My Drive" : state.drive;
|
|
494
|
+
const sections = [
|
|
495
|
+
"## Google Drive",
|
|
496
|
+
"",
|
|
497
|
+
`### Drive: ${driveLabel}`,
|
|
498
|
+
""
|
|
499
|
+
];
|
|
500
|
+
if (targetIds.length === 0) {
|
|
501
|
+
sections.push(
|
|
502
|
+
rt.language === "ja" ? "- \u9078\u629E\u3055\u308C\u305F\u30A2\u30A4\u30C6\u30E0\u306F\u3042\u308A\u307E\u305B\u3093\u3002" : "- No items selected."
|
|
503
|
+
);
|
|
504
|
+
return sections.join("\n");
|
|
505
|
+
}
|
|
506
|
+
sections.push("| Name | Type | Modified |");
|
|
507
|
+
sections.push("|------|------|----------|");
|
|
508
|
+
for (const id of targetIds) {
|
|
509
|
+
const f = filesById.get(id);
|
|
510
|
+
if (!f) {
|
|
511
|
+
sections.push(`| ${id} | (unknown) | - |`);
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
const isFolder = f.mimeType === FOLDER_MIME;
|
|
515
|
+
const typeLabel = isFolder ? "folder" : f.mimeType ?? "file";
|
|
516
|
+
sections.push(
|
|
517
|
+
`| ${f.name} | ${typeLabel} | ${f.modifiedTime ?? "-"} |`
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
sections.push("");
|
|
521
|
+
return sections.join("\n");
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
324
525
|
// ../connectors/src/connectors/google-drive/parameters.ts
|
|
325
526
|
var parameters = {};
|
|
326
527
|
|
|
@@ -448,6 +649,7 @@ var tools = { request: requestTool };
|
|
|
448
649
|
var googleDriveConnector = new ConnectorPlugin({
|
|
449
650
|
slug: "google-drive",
|
|
450
651
|
authType: AUTH_TYPES.OAUTH,
|
|
652
|
+
skipConnectionCheckOnCreate: true,
|
|
451
653
|
name: "Google Drive",
|
|
452
654
|
description: "Connect to Google Drive for file management, sharing, and collaboration using OAuth.",
|
|
453
655
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4GJX5yQTogUgar1buWxXbv/4b43a65353319c508111489f834d22c4/google_drive.png",
|
|
@@ -739,6 +941,7 @@ await drive.updateFile("fileId123", {}, "newFolderId", "oldFolderId");
|
|
|
739
941
|
\`\`\``
|
|
740
942
|
},
|
|
741
943
|
tools,
|
|
944
|
+
setup: (params, ctx, config) => runSetupFlow(googleDriveSetupFlow, params, ctx, config),
|
|
742
945
|
async checkConnection(_params, config) {
|
|
743
946
|
const { proxyFetch } = config;
|
|
744
947
|
const url = "https://www.googleapis.com/drive/v3/about?fields=user";
|
|
@@ -787,6 +990,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
787
990
|
import { getContext } from "hono/context-storage";
|
|
788
991
|
import { getCookie } from "hono/cookie";
|
|
789
992
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
993
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
790
994
|
function normalizeHeaders(input) {
|
|
791
995
|
const out = {};
|
|
792
996
|
if (!input) return out;
|
|
@@ -795,6 +999,11 @@ function normalizeHeaders(input) {
|
|
|
795
999
|
});
|
|
796
1000
|
return out;
|
|
797
1001
|
}
|
|
1002
|
+
function extractInputUrl(input) {
|
|
1003
|
+
if (typeof input === "string") return input;
|
|
1004
|
+
if (input instanceof URL) return input.href;
|
|
1005
|
+
return input.url;
|
|
1006
|
+
}
|
|
798
1007
|
function createSandboxProxyFetch(connectionId) {
|
|
799
1008
|
return async (input, init) => {
|
|
800
1009
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -804,10 +1013,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
804
1013
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
805
1014
|
);
|
|
806
1015
|
}
|
|
807
|
-
const originalUrl =
|
|
1016
|
+
const originalUrl = extractInputUrl(input);
|
|
1017
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
1018
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1019
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
1020
|
+
return fetch(sessionUrl, {
|
|
1021
|
+
method: "POST",
|
|
1022
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
808
1025
|
const originalMethod = init?.method ?? "GET";
|
|
809
1026
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
810
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
811
1027
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
812
1028
|
return fetch(proxyUrl, {
|
|
813
1029
|
method: "POST",
|
|
@@ -833,10 +1049,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
833
1049
|
}
|
|
834
1050
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
835
1051
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1052
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
836
1053
|
return async (input, init) => {
|
|
837
|
-
const originalUrl =
|
|
838
|
-
const originalMethod = init?.method ?? "GET";
|
|
839
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
1054
|
+
const originalUrl = extractInputUrl(input);
|
|
840
1055
|
const c = getContext();
|
|
841
1056
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
842
1057
|
if (!appSession) {
|
|
@@ -844,6 +1059,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
844
1059
|
"No authentication method available for connection proxy."
|
|
845
1060
|
);
|
|
846
1061
|
}
|
|
1062
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
1063
|
+
return fetch(sessionUrl, {
|
|
1064
|
+
method: "POST",
|
|
1065
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
const originalMethod = init?.method ?? "GET";
|
|
1069
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
847
1070
|
return fetch(proxyUrl, {
|
|
848
1071
|
method: "POST",
|
|
849
1072
|
headers: {
|