@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
|
@@ -1,48 +1,60 @@
|
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __esm = (fn, res) => function __init() {
|
|
3
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
|
+
};
|
|
5
|
+
|
|
1
6
|
// ../connectors/src/parameter-definition.ts
|
|
2
|
-
var ParameterDefinition
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
7
|
+
var ParameterDefinition;
|
|
8
|
+
var init_parameter_definition = __esm({
|
|
9
|
+
"../connectors/src/parameter-definition.ts"() {
|
|
10
|
+
"use strict";
|
|
11
|
+
ParameterDefinition = class {
|
|
12
|
+
slug;
|
|
13
|
+
name;
|
|
14
|
+
description;
|
|
15
|
+
envVarBaseKey;
|
|
16
|
+
type;
|
|
17
|
+
secret;
|
|
18
|
+
required;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.slug = config.slug;
|
|
21
|
+
this.name = config.name;
|
|
22
|
+
this.description = config.description;
|
|
23
|
+
this.envVarBaseKey = config.envVarBaseKey;
|
|
24
|
+
this.type = config.type;
|
|
25
|
+
this.secret = config.secret;
|
|
26
|
+
this.required = config.required;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the parameter value from a ConnectorConnectionObject.
|
|
30
|
+
*/
|
|
31
|
+
getValue(connection2) {
|
|
32
|
+
const param = connection2.parameters.find(
|
|
33
|
+
(p) => p.parameterSlug === this.slug
|
|
34
|
+
);
|
|
35
|
+
if (!param || param.value == null) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return param.value;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Try to get the parameter value. Returns undefined if not found (for optional params).
|
|
44
|
+
*/
|
|
45
|
+
tryGetValue(connection2) {
|
|
46
|
+
const param = connection2.parameters.find(
|
|
47
|
+
(p) => p.parameterSlug === this.slug
|
|
48
|
+
);
|
|
49
|
+
if (!param || param.value == null) return void 0;
|
|
50
|
+
return param.value;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
42
53
|
}
|
|
43
|
-
};
|
|
54
|
+
});
|
|
44
55
|
|
|
45
56
|
// ../connectors/src/connectors/grafana/parameters.ts
|
|
57
|
+
init_parameter_definition();
|
|
46
58
|
var parameters = {
|
|
47
59
|
url: new ParameterDefinition({
|
|
48
60
|
slug: "url",
|
|
@@ -83,7 +95,7 @@ function createClient(params) {
|
|
|
83
95
|
headers.set("Accept", "application/json");
|
|
84
96
|
return fetch(url, { ...init, headers });
|
|
85
97
|
}
|
|
86
|
-
async function
|
|
98
|
+
async function listDatasources2() {
|
|
87
99
|
const response = await request("/api/datasources");
|
|
88
100
|
if (!response.ok) {
|
|
89
101
|
const body = await response.text();
|
|
@@ -155,7 +167,7 @@ function createClient(params) {
|
|
|
155
167
|
}
|
|
156
168
|
return {
|
|
157
169
|
request,
|
|
158
|
-
listDatasources,
|
|
170
|
+
listDatasources: listDatasources2,
|
|
159
171
|
getDatasource,
|
|
160
172
|
queryDatasource,
|
|
161
173
|
searchDashboards,
|
|
@@ -230,6 +242,20 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
230
242
|
* `runSetupFlow` from `setup-flow.ts`.
|
|
231
243
|
*/
|
|
232
244
|
setup;
|
|
245
|
+
/**
|
|
246
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
247
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
248
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
249
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
250
|
+
*
|
|
251
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
252
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
253
|
+
* connectionId, which doesn't exist until the row is saved
|
|
254
|
+
*
|
|
255
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
256
|
+
* the default verify-on-create behavior without opt-in.
|
|
257
|
+
*/
|
|
258
|
+
skipConnectionCheckOnCreate;
|
|
233
259
|
constructor(config) {
|
|
234
260
|
this.slug = config.slug;
|
|
235
261
|
this.authType = config.authType;
|
|
@@ -247,6 +273,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
247
273
|
this.query = config.query;
|
|
248
274
|
this.checkConnection = config.checkConnection;
|
|
249
275
|
this.setup = config.setup;
|
|
276
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
250
277
|
}
|
|
251
278
|
get connectorKey() {
|
|
252
279
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -311,6 +338,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
311
338
|
}
|
|
312
339
|
};
|
|
313
340
|
|
|
341
|
+
// ../connectors/src/setup-flow.ts
|
|
342
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
343
|
+
const runtime = {
|
|
344
|
+
params,
|
|
345
|
+
language: ctx.language,
|
|
346
|
+
config
|
|
347
|
+
};
|
|
348
|
+
let state = flow.initialState();
|
|
349
|
+
let answerIdx = 0;
|
|
350
|
+
for (const step of flow.steps) {
|
|
351
|
+
const ans = ctx.answers[answerIdx];
|
|
352
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
353
|
+
state = step.applyAnswer(state, ans.answer);
|
|
354
|
+
answerIdx += 1;
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (step.type === "text") {
|
|
358
|
+
return {
|
|
359
|
+
type: "nextQuestion",
|
|
360
|
+
questionSlug: step.slug,
|
|
361
|
+
question: step.question[ctx.language],
|
|
362
|
+
questionType: "text"
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
366
|
+
if (options.length === 0) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
type: "nextQuestion",
|
|
371
|
+
questionSlug: step.slug,
|
|
372
|
+
question: step.question[ctx.language],
|
|
373
|
+
questionType: step.type,
|
|
374
|
+
options
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
378
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
379
|
+
}
|
|
380
|
+
async function resolveSetupSelection(params) {
|
|
381
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
382
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
383
|
+
return resolved.slice(0, limit);
|
|
384
|
+
}
|
|
385
|
+
|
|
314
386
|
// ../connectors/src/auth-types.ts
|
|
315
387
|
var AUTH_TYPES = {
|
|
316
388
|
OAUTH: "oauth",
|
|
@@ -333,6 +405,129 @@ var grafanaOnboarding = new ConnectorOnboarding({
|
|
|
333
405
|
}
|
|
334
406
|
});
|
|
335
407
|
|
|
408
|
+
// ../connectors/src/connectors/grafana/utils.ts
|
|
409
|
+
async function apiFetch(params, path2, init) {
|
|
410
|
+
const apiKey = params[parameters.apiKey.slug];
|
|
411
|
+
const rawUrl = params[parameters.url.slug];
|
|
412
|
+
if (!apiKey || !rawUrl) {
|
|
413
|
+
throw new Error("grafana: missing required parameters: url and api-key");
|
|
414
|
+
}
|
|
415
|
+
const baseUrl = rawUrl.replace(/\/+$/, "");
|
|
416
|
+
const url = `${baseUrl}${path2.startsWith("/") ? path2 : `/${path2}`}`;
|
|
417
|
+
const headers = new Headers(init?.headers);
|
|
418
|
+
headers.set("Authorization", `Bearer ${apiKey}`);
|
|
419
|
+
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
420
|
+
return fetch(url, { ...init, headers });
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ../connectors/src/connectors/grafana/setup-flow.ts
|
|
424
|
+
var ALL_DATASOURCES = "__ALL_DATASOURCES__";
|
|
425
|
+
var GRAFANA_SETUP_MAX_DATASOURCES = 10;
|
|
426
|
+
async function listOrgs(params) {
|
|
427
|
+
try {
|
|
428
|
+
const res = await apiFetch(params, "/api/orgs");
|
|
429
|
+
if (res.ok) {
|
|
430
|
+
const data = await res.json();
|
|
431
|
+
if (Array.isArray(data)) return data;
|
|
432
|
+
}
|
|
433
|
+
} catch {
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
const res = await apiFetch(params, "/api/org");
|
|
437
|
+
if (!res.ok) return [];
|
|
438
|
+
const data = await res.json();
|
|
439
|
+
if (data && typeof data.id === "number" && typeof data.name === "string") {
|
|
440
|
+
return [data];
|
|
441
|
+
}
|
|
442
|
+
} catch {
|
|
443
|
+
}
|
|
444
|
+
return [];
|
|
445
|
+
}
|
|
446
|
+
async function listDatasources(params) {
|
|
447
|
+
const res = await apiFetch(params, "/api/datasources");
|
|
448
|
+
if (!res.ok) {
|
|
449
|
+
const body = await res.text().catch(() => res.statusText);
|
|
450
|
+
throw new Error(`grafana: listDatasources failed (${res.status}): ${body}`);
|
|
451
|
+
}
|
|
452
|
+
const data = await res.json();
|
|
453
|
+
return data ?? [];
|
|
454
|
+
}
|
|
455
|
+
var grafanaSetupFlow = {
|
|
456
|
+
initialState: () => ({}),
|
|
457
|
+
steps: [
|
|
458
|
+
{
|
|
459
|
+
slug: "organization",
|
|
460
|
+
type: "select",
|
|
461
|
+
question: {
|
|
462
|
+
ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u306B\u4F7F\u3046\u7D44\u7E54\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044",
|
|
463
|
+
en: "Select the organization to use for setup"
|
|
464
|
+
},
|
|
465
|
+
async fetchOptions(_state, rt) {
|
|
466
|
+
const orgs = await listOrgs(rt.params);
|
|
467
|
+
return orgs.map((o) => ({ value: String(o.id), label: o.name }));
|
|
468
|
+
},
|
|
469
|
+
applyAnswer: (state, answer) => ({ ...state, organization: answer[0] })
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
slug: "datasources",
|
|
473
|
+
type: "multiSelect",
|
|
474
|
+
question: {
|
|
475
|
+
ja: "\u5BFE\u8C61\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
476
|
+
en: "Select target datasources (multi-select allowed)"
|
|
477
|
+
},
|
|
478
|
+
async fetchOptions(_state, rt) {
|
|
479
|
+
const datasources = await listDatasources(rt.params);
|
|
480
|
+
const opts = datasources.filter((d) => d.uid && d.name).map((d) => ({
|
|
481
|
+
value: d.uid,
|
|
482
|
+
label: `${d.name} (${d.type})`
|
|
483
|
+
}));
|
|
484
|
+
if (opts.length === 0) return [];
|
|
485
|
+
return [
|
|
486
|
+
{
|
|
487
|
+
value: ALL_DATASOURCES,
|
|
488
|
+
label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9" : "All datasources"
|
|
489
|
+
},
|
|
490
|
+
...opts
|
|
491
|
+
];
|
|
492
|
+
},
|
|
493
|
+
applyAnswer: (state, answer) => ({ ...state, datasources: answer })
|
|
494
|
+
}
|
|
495
|
+
],
|
|
496
|
+
async finalize(state, rt) {
|
|
497
|
+
if (!state.datasources) {
|
|
498
|
+
throw new Error("Grafana setup: incomplete state on finalize");
|
|
499
|
+
}
|
|
500
|
+
const datasources = await listDatasources(rt.params);
|
|
501
|
+
const byUid = new Map(datasources.map((d) => [d.uid, d]));
|
|
502
|
+
const targetUids = await resolveSetupSelection({
|
|
503
|
+
selected: state.datasources,
|
|
504
|
+
allSentinel: ALL_DATASOURCES,
|
|
505
|
+
fetchAll: async () => datasources.map((d) => d.uid).filter((u) => u),
|
|
506
|
+
limit: GRAFANA_SETUP_MAX_DATASOURCES
|
|
507
|
+
});
|
|
508
|
+
const sections = ["## Grafana", ""];
|
|
509
|
+
if (state.organization) {
|
|
510
|
+
sections.push(`### Organization: ${state.organization}`, "");
|
|
511
|
+
}
|
|
512
|
+
for (const uid of targetUids) {
|
|
513
|
+
const ds = byUid.get(uid);
|
|
514
|
+
if (!ds) {
|
|
515
|
+
sections.push(`#### Datasource: ${uid}`, "", "_Not found._", "");
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
sections.push(`#### Datasource: ${ds.name}`, "");
|
|
519
|
+
sections.push("| Field | Value |");
|
|
520
|
+
sections.push("|-------|-------|");
|
|
521
|
+
sections.push(`| UID | ${ds.uid} |`);
|
|
522
|
+
sections.push(`| Type | ${ds.type} |`);
|
|
523
|
+
sections.push(`| URL | ${ds.url ?? "-"} |`);
|
|
524
|
+
sections.push(`| Default | ${ds.isDefault ? "yes" : "no"} |`);
|
|
525
|
+
sections.push("");
|
|
526
|
+
}
|
|
527
|
+
return sections.join("\n");
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
336
531
|
// ../connectors/src/connectors/grafana/tools/request.ts
|
|
337
532
|
import { z } from "zod";
|
|
338
533
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
@@ -545,7 +740,41 @@ export default async function handler(c: Context) {
|
|
|
545
740
|
- \`GET /api/org\` \u2014 \u73FE\u5728\u306E\u7D44\u7E54\u3092\u53D6\u5F97
|
|
546
741
|
- \`GET /api/health\` \u2014 \u30D8\u30EB\u30B9\u30C1\u30A7\u30C3\u30AF`
|
|
547
742
|
},
|
|
548
|
-
tools
|
|
743
|
+
tools,
|
|
744
|
+
setup: (params, ctx, config) => runSetupFlow(grafanaSetupFlow, params, ctx, config),
|
|
745
|
+
async checkConnection(params, _config) {
|
|
746
|
+
const apiKey = params[parameters.apiKey.slug];
|
|
747
|
+
const rawUrl = params[parameters.url.slug];
|
|
748
|
+
if (!apiKey || !rawUrl) {
|
|
749
|
+
return {
|
|
750
|
+
success: false,
|
|
751
|
+
error: "Missing required parameters: url and api-key"
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
const baseUrl = rawUrl.replace(/\/+$/, "");
|
|
755
|
+
try {
|
|
756
|
+
const res = await fetch(`${baseUrl}/api/org`, {
|
|
757
|
+
method: "GET",
|
|
758
|
+
headers: {
|
|
759
|
+
Authorization: `Bearer ${apiKey}`,
|
|
760
|
+
Accept: "application/json"
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
if (!res.ok) {
|
|
764
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
765
|
+
return {
|
|
766
|
+
success: false,
|
|
767
|
+
error: `Grafana API failed: HTTP ${res.status} ${errorText}`
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
return { success: true };
|
|
771
|
+
} catch (error) {
|
|
772
|
+
return {
|
|
773
|
+
success: false,
|
|
774
|
+
error: error instanceof Error ? error.message : String(error)
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
}
|
|
549
778
|
});
|
|
550
779
|
|
|
551
780
|
// src/connectors/create-connector-sdk.ts
|
|
@@ -574,6 +803,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
574
803
|
import { getContext } from "hono/context-storage";
|
|
575
804
|
import { getCookie } from "hono/cookie";
|
|
576
805
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
806
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
577
807
|
function normalizeHeaders(input) {
|
|
578
808
|
const out = {};
|
|
579
809
|
if (!input) return out;
|
|
@@ -582,6 +812,11 @@ function normalizeHeaders(input) {
|
|
|
582
812
|
});
|
|
583
813
|
return out;
|
|
584
814
|
}
|
|
815
|
+
function extractInputUrl(input) {
|
|
816
|
+
if (typeof input === "string") return input;
|
|
817
|
+
if (input instanceof URL) return input.href;
|
|
818
|
+
return input.url;
|
|
819
|
+
}
|
|
585
820
|
function createSandboxProxyFetch(connectionId) {
|
|
586
821
|
return async (input, init) => {
|
|
587
822
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -591,10 +826,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
591
826
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
592
827
|
);
|
|
593
828
|
}
|
|
594
|
-
const originalUrl =
|
|
829
|
+
const originalUrl = extractInputUrl(input);
|
|
830
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
831
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
832
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
833
|
+
return fetch(sessionUrl, {
|
|
834
|
+
method: "POST",
|
|
835
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
836
|
+
});
|
|
837
|
+
}
|
|
595
838
|
const originalMethod = init?.method ?? "GET";
|
|
596
839
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
597
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
598
840
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
599
841
|
return fetch(proxyUrl, {
|
|
600
842
|
method: "POST",
|
|
@@ -620,10 +862,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
620
862
|
}
|
|
621
863
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
622
864
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
865
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
623
866
|
return async (input, init) => {
|
|
624
|
-
const originalUrl =
|
|
625
|
-
const originalMethod = init?.method ?? "GET";
|
|
626
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
867
|
+
const originalUrl = extractInputUrl(input);
|
|
627
868
|
const c = getContext();
|
|
628
869
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
629
870
|
if (!appSession) {
|
|
@@ -631,6 +872,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
631
872
|
"No authentication method available for connection proxy."
|
|
632
873
|
);
|
|
633
874
|
}
|
|
875
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
876
|
+
return fetch(sessionUrl, {
|
|
877
|
+
method: "POST",
|
|
878
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
const originalMethod = init?.method ?? "GET";
|
|
882
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
634
883
|
return fetch(proxyUrl, {
|
|
635
884
|
method: "POST",
|
|
636
885
|
headers: {
|
|
@@ -75,6 +75,20 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
75
75
|
* `runSetupFlow` from `setup-flow.ts`.
|
|
76
76
|
*/
|
|
77
77
|
setup;
|
|
78
|
+
/**
|
|
79
|
+
* Opt-out of the default "verify before save" behavior on connection
|
|
80
|
+
* creation. The backend invokes `checkConnection` synchronously while
|
|
81
|
+
* creating the connection and aborts (no row inserted) if it fails — this
|
|
82
|
+
* flag disables that for connectors where the check cannot succeed pre-save:
|
|
83
|
+
*
|
|
84
|
+
* - `squadbase-db` populates `connection-url` only after Neon provisioning
|
|
85
|
+
* - OAuth connectors require an OAuth-aware proxyFetch keyed by the
|
|
86
|
+
* connectionId, which doesn't exist until the row is saved
|
|
87
|
+
*
|
|
88
|
+
* Exceptions are the explicit position; new credential-input connectors get
|
|
89
|
+
* the default verify-on-create behavior without opt-in.
|
|
90
|
+
*/
|
|
91
|
+
skipConnectionCheckOnCreate;
|
|
78
92
|
constructor(config) {
|
|
79
93
|
this.slug = config.slug;
|
|
80
94
|
this.authType = config.authType;
|
|
@@ -92,6 +106,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
92
106
|
this.query = config.query;
|
|
93
107
|
this.checkConnection = config.checkConnection;
|
|
94
108
|
this.setup = config.setup;
|
|
109
|
+
this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
|
|
95
110
|
}
|
|
96
111
|
get connectorKey() {
|
|
97
112
|
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
@@ -156,6 +171,51 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
156
171
|
}
|
|
157
172
|
};
|
|
158
173
|
|
|
174
|
+
// ../connectors/src/setup-flow.ts
|
|
175
|
+
async function runSetupFlow(flow, params, ctx, config) {
|
|
176
|
+
const runtime = {
|
|
177
|
+
params,
|
|
178
|
+
language: ctx.language,
|
|
179
|
+
config
|
|
180
|
+
};
|
|
181
|
+
let state = flow.initialState();
|
|
182
|
+
let answerIdx = 0;
|
|
183
|
+
for (const step of flow.steps) {
|
|
184
|
+
const ans = ctx.answers[answerIdx];
|
|
185
|
+
if (ans && ans.questionSlug === step.slug) {
|
|
186
|
+
state = step.applyAnswer(state, ans.answer);
|
|
187
|
+
answerIdx += 1;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (step.type === "text") {
|
|
191
|
+
return {
|
|
192
|
+
type: "nextQuestion",
|
|
193
|
+
questionSlug: step.slug,
|
|
194
|
+
question: step.question[ctx.language],
|
|
195
|
+
questionType: "text"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
199
|
+
if (options.length === 0) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
type: "nextQuestion",
|
|
204
|
+
questionSlug: step.slug,
|
|
205
|
+
question: step.question[ctx.language],
|
|
206
|
+
questionType: step.type,
|
|
207
|
+
options
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
211
|
+
return { type: "fulfilled", dataInvestigationResult };
|
|
212
|
+
}
|
|
213
|
+
async function resolveSetupSelection(params) {
|
|
214
|
+
const { selected, allSentinel, fetchAll, limit } = params;
|
|
215
|
+
const resolved = selected.includes(allSentinel) ? await fetchAll() : selected.filter((v) => v !== allSentinel);
|
|
216
|
+
return resolved.slice(0, limit);
|
|
217
|
+
}
|
|
218
|
+
|
|
159
219
|
// ../connectors/src/auth-types.ts
|
|
160
220
|
var AUTH_TYPES = {
|
|
161
221
|
OAUTH: "oauth",
|
|
@@ -316,6 +376,93 @@ var hubspotOnboarding = new ConnectorOnboarding({
|
|
|
316
376
|
}
|
|
317
377
|
});
|
|
318
378
|
|
|
379
|
+
// ../connectors/src/connectors/hubspot-oauth/utils.ts
|
|
380
|
+
var BASE_URL3 = "https://api.hubapi.com";
|
|
381
|
+
function apiFetch(proxyFetch, path2, init) {
|
|
382
|
+
const url = `${BASE_URL3}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
383
|
+
return proxyFetch(url, init);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ../connectors/src/connectors/hubspot-oauth/setup-flow.ts
|
|
387
|
+
var HUBSPOT_SETUP_MAX_OBJECT_TYPES = 10;
|
|
388
|
+
var HUBSPOT_SETUP_MAX_PROPERTIES = 50;
|
|
389
|
+
var HUBSPOT_OBJECT_TYPES = [
|
|
390
|
+
{ value: "contacts", label: "Contacts" },
|
|
391
|
+
{ value: "companies", label: "Companies" },
|
|
392
|
+
{ value: "deals", label: "Deals" },
|
|
393
|
+
{ value: "tickets", label: "Tickets" },
|
|
394
|
+
{ value: "products", label: "Products" },
|
|
395
|
+
{ value: "notes", label: "Notes" },
|
|
396
|
+
{ value: "calls", label: "Calls" },
|
|
397
|
+
{ value: "emails", label: "Emails" },
|
|
398
|
+
{ value: "meetings", label: "Meetings" },
|
|
399
|
+
{ value: "tasks", label: "Tasks" }
|
|
400
|
+
];
|
|
401
|
+
async function listProperties(proxyFetch, objectType) {
|
|
402
|
+
const res = await apiFetch(proxyFetch, `/crm/v3/properties/${objectType}`);
|
|
403
|
+
if (!res.ok) {
|
|
404
|
+
const body = await res.text().catch(() => res.statusText);
|
|
405
|
+
throw new Error(
|
|
406
|
+
`hubspot-oauth: listProperties(${objectType}) failed (${res.status}): ${body}`
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
const data = await res.json();
|
|
410
|
+
return data.results ?? [];
|
|
411
|
+
}
|
|
412
|
+
var hubspotOauthSetupFlow = {
|
|
413
|
+
initialState: () => ({}),
|
|
414
|
+
steps: [
|
|
415
|
+
{
|
|
416
|
+
slug: "objectTypes",
|
|
417
|
+
type: "multiSelect",
|
|
418
|
+
question: {
|
|
419
|
+
ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3067\u6982\u89B3\u3057\u305F\u3044CRM\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
|
|
420
|
+
en: "Select the CRM object types to include in setup (multi-select allowed)"
|
|
421
|
+
},
|
|
422
|
+
async fetchOptions(_state, _rt) {
|
|
423
|
+
return HUBSPOT_OBJECT_TYPES.map((o) => ({
|
|
424
|
+
value: o.value,
|
|
425
|
+
label: o.label
|
|
426
|
+
}));
|
|
427
|
+
},
|
|
428
|
+
applyAnswer: (state, answer) => ({ ...state, objectTypes: answer })
|
|
429
|
+
}
|
|
430
|
+
],
|
|
431
|
+
async finalize(state, rt) {
|
|
432
|
+
if (!state.objectTypes) {
|
|
433
|
+
throw new Error("HubSpot setup: incomplete state on finalize");
|
|
434
|
+
}
|
|
435
|
+
const targetObjectTypes = await resolveSetupSelection({
|
|
436
|
+
selected: state.objectTypes,
|
|
437
|
+
allSentinel: "__ALL__",
|
|
438
|
+
fetchAll: async () => HUBSPOT_OBJECT_TYPES.map((o) => o.value),
|
|
439
|
+
limit: HUBSPOT_SETUP_MAX_OBJECT_TYPES
|
|
440
|
+
});
|
|
441
|
+
const sections = ["## HubSpot", ""];
|
|
442
|
+
for (const objectType of targetObjectTypes) {
|
|
443
|
+
sections.push(`### Object: ${objectType}`, "");
|
|
444
|
+
const props = await listProperties(rt.config.proxyFetch, objectType);
|
|
445
|
+
const limited = props.slice(0, HUBSPOT_SETUP_MAX_PROPERTIES);
|
|
446
|
+
sections.push("| Property | Type | Label |");
|
|
447
|
+
sections.push("|----------|------|-------|");
|
|
448
|
+
for (const p of limited) {
|
|
449
|
+
const name = (p.name ?? "").replace(/\|/g, "\\|");
|
|
450
|
+
const type = (p.type ?? "").replace(/\|/g, "\\|");
|
|
451
|
+
const label = (p.label ?? "").replace(/\|/g, "\\|");
|
|
452
|
+
sections.push(`| ${name} | ${type} | ${label || "-"} |`);
|
|
453
|
+
}
|
|
454
|
+
if (props.length > HUBSPOT_SETUP_MAX_PROPERTIES) {
|
|
455
|
+
sections.push(
|
|
456
|
+
"",
|
|
457
|
+
`_Showing first ${HUBSPOT_SETUP_MAX_PROPERTIES} of ${props.length} properties._`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
sections.push("");
|
|
461
|
+
}
|
|
462
|
+
return sections.join("\n");
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
319
466
|
// ../connectors/src/connectors/hubspot-oauth/parameters.ts
|
|
320
467
|
var parameters = {};
|
|
321
468
|
|
|
@@ -324,6 +471,7 @@ var tools = { request: requestTool };
|
|
|
324
471
|
var hubspotOauthConnector = new ConnectorPlugin({
|
|
325
472
|
slug: "hubspot",
|
|
326
473
|
authType: AUTH_TYPES.OAUTH,
|
|
474
|
+
skipConnectionCheckOnCreate: true,
|
|
327
475
|
name: "HubSpot",
|
|
328
476
|
description: "Connect to HubSpot CRM for contacts, deals, companies, and marketing data using OAuth.",
|
|
329
477
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/5UcSkKkzhUMA4RsM45ynuo/43b967e36915ca0fc5d277684b204320/hubspot.svg",
|
|
@@ -432,6 +580,7 @@ const data = await res.json();
|
|
|
432
580
|
\`\`\``
|
|
433
581
|
},
|
|
434
582
|
tools,
|
|
583
|
+
setup: (params, ctx, config) => runSetupFlow(hubspotOauthSetupFlow, params, ctx, config),
|
|
435
584
|
async checkConnection(_params, config) {
|
|
436
585
|
const { proxyFetch } = config;
|
|
437
586
|
try {
|
|
@@ -482,6 +631,7 @@ function resolveEnvVarOptional(entry, key) {
|
|
|
482
631
|
import { getContext } from "hono/context-storage";
|
|
483
632
|
import { getCookie } from "hono/cookie";
|
|
484
633
|
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
634
|
+
var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
|
|
485
635
|
function normalizeHeaders(input) {
|
|
486
636
|
const out = {};
|
|
487
637
|
if (!input) return out;
|
|
@@ -490,6 +640,11 @@ function normalizeHeaders(input) {
|
|
|
490
640
|
});
|
|
491
641
|
return out;
|
|
492
642
|
}
|
|
643
|
+
function extractInputUrl(input) {
|
|
644
|
+
if (typeof input === "string") return input;
|
|
645
|
+
if (input instanceof URL) return input.href;
|
|
646
|
+
return input.url;
|
|
647
|
+
}
|
|
493
648
|
function createSandboxProxyFetch(connectionId) {
|
|
494
649
|
return async (input, init) => {
|
|
495
650
|
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
@@ -499,10 +654,17 @@ function createSandboxProxyFetch(connectionId) {
|
|
|
499
654
|
"Connection proxy is not configured. Please check your deployment settings."
|
|
500
655
|
);
|
|
501
656
|
}
|
|
502
|
-
const originalUrl =
|
|
657
|
+
const originalUrl = extractInputUrl(input);
|
|
658
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
659
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
660
|
+
const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
661
|
+
return fetch(sessionUrl, {
|
|
662
|
+
method: "POST",
|
|
663
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
664
|
+
});
|
|
665
|
+
}
|
|
503
666
|
const originalMethod = init?.method ?? "GET";
|
|
504
667
|
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
505
|
-
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
506
668
|
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
507
669
|
return fetch(proxyUrl, {
|
|
508
670
|
method: "POST",
|
|
@@ -528,10 +690,9 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
528
690
|
}
|
|
529
691
|
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
530
692
|
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
693
|
+
const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
|
|
531
694
|
return async (input, init) => {
|
|
532
|
-
const originalUrl =
|
|
533
|
-
const originalMethod = init?.method ?? "GET";
|
|
534
|
-
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
695
|
+
const originalUrl = extractInputUrl(input);
|
|
535
696
|
const c = getContext();
|
|
536
697
|
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
537
698
|
if (!appSession) {
|
|
@@ -539,6 +700,14 @@ function createDeployedAppProxyFetch(connectionId) {
|
|
|
539
700
|
"No authentication method available for connection proxy."
|
|
540
701
|
);
|
|
541
702
|
}
|
|
703
|
+
if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
|
|
704
|
+
return fetch(sessionUrl, {
|
|
705
|
+
method: "POST",
|
|
706
|
+
headers: { Authorization: `Bearer ${appSession}` }
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
const originalMethod = init?.method ?? "GET";
|
|
710
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
542
711
|
return fetch(proxyUrl, {
|
|
543
712
|
method: "POST",
|
|
544
713
|
headers: {
|