@squadbase/vite-server 0.1.17-dev.a9ddcfa → 0.1.17-dev.d4fff69

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.
Files changed (80) hide show
  1. package/dist/cli/index.js +4299 -821
  2. package/dist/connectors/airtable-oauth.js +48 -8
  3. package/dist/connectors/airtable.js +44 -8
  4. package/dist/connectors/amplitude.js +8 -8
  5. package/dist/connectors/anthropic.js +2 -2
  6. package/dist/connectors/asana.js +37 -10
  7. package/dist/connectors/attio.js +30 -13
  8. package/dist/connectors/aws-billing.js +8 -8
  9. package/dist/connectors/azure-sql.js +47 -10
  10. package/dist/connectors/backlog-api-key.js +40 -15
  11. package/dist/connectors/clickup.js +50 -10
  12. package/dist/connectors/cosmosdb.js +12 -12
  13. package/dist/connectors/customerio.js +8 -8
  14. package/dist/connectors/dbt.js +686 -25
  15. package/dist/connectors/freshdesk.js +82 -8
  16. package/dist/connectors/freshsales.js +8 -8
  17. package/dist/connectors/freshservice.js +8 -8
  18. package/dist/connectors/gamma.js +15 -15
  19. package/dist/connectors/gemini.js +2 -2
  20. package/dist/connectors/github.js +12 -12
  21. package/dist/connectors/gmail-oauth.js +10 -10
  22. package/dist/connectors/gmail.js +4 -4
  23. package/dist/connectors/google-ads.js +8 -8
  24. package/dist/connectors/google-analytics-oauth.js +152 -25
  25. package/dist/connectors/google-analytics.js +490 -96
  26. package/dist/connectors/google-audit-log.js +4 -4
  27. package/dist/connectors/google-calendar-oauth.js +61 -15
  28. package/dist/connectors/google-calendar.js +61 -11
  29. package/dist/connectors/google-docs.js +10 -10
  30. package/dist/connectors/google-drive.js +32 -10
  31. package/dist/connectors/google-search-console-oauth.js +126 -17
  32. package/dist/connectors/google-sheets.js +6 -6
  33. package/dist/connectors/google-slides.js +10 -10
  34. package/dist/connectors/grafana.js +45 -10
  35. package/dist/connectors/hackernews.d.ts +5 -0
  36. package/dist/connectors/hackernews.js +890 -0
  37. package/dist/connectors/hubspot-oauth.js +41 -9
  38. package/dist/connectors/hubspot.js +25 -9
  39. package/dist/connectors/influxdb.js +8 -8
  40. package/dist/connectors/intercom-oauth.js +72 -12
  41. package/dist/connectors/intercom.js +12 -12
  42. package/dist/connectors/jdbc.js +37 -10
  43. package/dist/connectors/jira-api-key.js +68 -11
  44. package/dist/connectors/kintone-api-token.js +66 -18
  45. package/dist/connectors/kintone.js +54 -11
  46. package/dist/connectors/linear.js +54 -12
  47. package/dist/connectors/linkedin-ads.js +41 -14
  48. package/dist/connectors/mailchimp-oauth.js +6 -6
  49. package/dist/connectors/mailchimp.js +6 -6
  50. package/dist/connectors/meta-ads-oauth.js +33 -14
  51. package/dist/connectors/meta-ads.js +35 -14
  52. package/dist/connectors/mixpanel.js +8 -8
  53. package/dist/connectors/monday.js +9 -9
  54. package/dist/connectors/mongodb.js +8 -8
  55. package/dist/connectors/notion-oauth.js +60 -11
  56. package/dist/connectors/notion.js +60 -11
  57. package/dist/connectors/openai.js +2 -2
  58. package/dist/connectors/oracle.js +39 -11
  59. package/dist/connectors/outlook-oauth.js +21 -21
  60. package/dist/connectors/powerbi-oauth.js +13 -13
  61. package/dist/connectors/salesforce.js +42 -9
  62. package/dist/connectors/semrush.js +6 -6
  63. package/dist/connectors/sentry.js +36 -10
  64. package/dist/connectors/shopify-oauth.js +43 -10
  65. package/dist/connectors/shopify.js +8 -8
  66. package/dist/connectors/sqlserver.js +47 -10
  67. package/dist/connectors/stripe-api-key.js +66 -15
  68. package/dist/connectors/stripe-oauth.js +70 -19
  69. package/dist/connectors/supabase.js +31 -6
  70. package/dist/connectors/tableau.js +15 -15
  71. package/dist/connectors/tiktok-ads.js +37 -16
  72. package/dist/connectors/wix-store.js +8 -8
  73. package/dist/connectors/x.d.ts +5 -0
  74. package/dist/connectors/x.js +927 -0
  75. package/dist/connectors/zendesk-oauth.js +55 -12
  76. package/dist/connectors/zendesk.js +12 -12
  77. package/dist/index.js +4317 -819
  78. package/dist/main.js +4317 -819
  79. package/dist/vite-plugin.js +4297 -819
  80. package/package.json +9 -1
@@ -0,0 +1,927 @@
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
+
6
+ // ../connectors/src/parameter-definition.ts
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
+ isDeprecated;
20
+ constructor(config) {
21
+ this.slug = config.slug;
22
+ this.name = config.name;
23
+ this.description = config.description;
24
+ this.envVarBaseKey = config.envVarBaseKey;
25
+ this.type = config.type;
26
+ this.secret = config.secret;
27
+ this.required = config.required;
28
+ this.isDeprecated = config.isDeprecated ?? false;
29
+ }
30
+ /**
31
+ * Get the parameter value from a ConnectorConnectionObject.
32
+ */
33
+ getValue(connection2) {
34
+ const param = connection2.parameters.find(
35
+ (p) => p.parameterSlug === this.slug
36
+ );
37
+ if (!param || param.value == null) {
38
+ throw new Error(
39
+ `Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
40
+ );
41
+ }
42
+ return param.value;
43
+ }
44
+ /**
45
+ * Try to get the parameter value. Returns undefined if not found (for optional params).
46
+ */
47
+ tryGetValue(connection2) {
48
+ const param = connection2.parameters.find(
49
+ (p) => p.parameterSlug === this.slug
50
+ );
51
+ if (!param || param.value == null) return void 0;
52
+ return param.value;
53
+ }
54
+ };
55
+ }
56
+ });
57
+
58
+ // ../connectors/src/connectors/x/parameters.ts
59
+ init_parameter_definition();
60
+ var parameters = {
61
+ bearerToken: new ParameterDefinition({
62
+ slug: "bearer-token",
63
+ name: "X API Bearer Token",
64
+ description: "The X API Bearer Token for app-only authentication. Create it in the X Developer Console for your app.",
65
+ envVarBaseKey: "X_BEARER_TOKEN",
66
+ type: "text",
67
+ secret: true,
68
+ required: true
69
+ })
70
+ };
71
+
72
+ // ../connectors/src/connectors/x/sdk/index.ts
73
+ var BASE_URL = "https://api.x.com";
74
+ function createClient(params) {
75
+ const bearerToken = params[parameters.bearerToken.slug];
76
+ if (!bearerToken) {
77
+ throw new Error(
78
+ `x: missing required parameter: ${parameters.bearerToken.slug}`
79
+ );
80
+ }
81
+ function buildUrl(path2, query) {
82
+ if (/^https?:\/\//i.test(path2)) {
83
+ throw new Error("x: absolute URLs are not allowed");
84
+ }
85
+ const url = new URL(`${BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`);
86
+ if (query) {
87
+ for (const [k, v] of Object.entries(query)) {
88
+ if (v !== void 0) url.searchParams.set(k, String(v));
89
+ }
90
+ }
91
+ return url.toString();
92
+ }
93
+ async function request(path2, init) {
94
+ const { query, headers: initHeaders, ...rest } = init ?? {};
95
+ const headers = new Headers(initHeaders);
96
+ headers.set("Authorization", `Bearer ${bearerToken}`);
97
+ headers.set("Accept", "application/json");
98
+ if (rest.body && !headers.has("Content-Type")) {
99
+ headers.set("Content-Type", "application/json");
100
+ }
101
+ return fetch(buildUrl(path2, query), { ...rest, headers });
102
+ }
103
+ async function json(path2, query) {
104
+ const res = await request(path2, { method: "GET", query });
105
+ const text = await res.text();
106
+ const data = text ? JSON.parse(text) : {};
107
+ if (!res.ok) {
108
+ throw new Error(`x: ${res.status} ${res.statusText}: ${text}`);
109
+ }
110
+ return data;
111
+ }
112
+ return {
113
+ request,
114
+ getUserByUsername(username, options) {
115
+ return json(
116
+ `/2/users/by/username/${encodeURIComponent(username)}`,
117
+ options?.query
118
+ );
119
+ },
120
+ getUser(id, options) {
121
+ return json(`/2/users/${encodeURIComponent(id)}`, options?.query);
122
+ },
123
+ getTweet(id, options) {
124
+ return json(`/2/tweets/${encodeURIComponent(id)}`, options?.query);
125
+ },
126
+ getUserTweets(userId, options) {
127
+ return json(
128
+ `/2/users/${encodeURIComponent(userId)}/tweets`,
129
+ options?.query
130
+ );
131
+ },
132
+ searchRecent(query, options) {
133
+ return json("/2/tweets/search/recent", {
134
+ query,
135
+ ...options?.query ?? {}
136
+ });
137
+ },
138
+ searchAll(query, options) {
139
+ return json("/2/tweets/search/all", {
140
+ query,
141
+ ...options?.query ?? {}
142
+ });
143
+ }
144
+ };
145
+ }
146
+
147
+ // ../connectors/src/connector-onboarding.ts
148
+ var ConnectorOnboarding = class {
149
+ /** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
150
+ connectionSetupInstructions;
151
+ /** Phase 2: Data overview instructions */
152
+ dataOverviewInstructions;
153
+ constructor(config) {
154
+ this.connectionSetupInstructions = config.connectionSetupInstructions;
155
+ this.dataOverviewInstructions = config.dataOverviewInstructions;
156
+ }
157
+ getConnectionSetupPrompt(language) {
158
+ return this.connectionSetupInstructions?.[language] ?? null;
159
+ }
160
+ getDataOverviewInstructions(language) {
161
+ return this.dataOverviewInstructions[language];
162
+ }
163
+ };
164
+
165
+ // ../connectors/src/connector-tool.ts
166
+ var ConnectorTool = class {
167
+ name;
168
+ description;
169
+ inputSchema;
170
+ outputSchema;
171
+ _execute;
172
+ constructor(config) {
173
+ this.name = config.name;
174
+ this.description = config.description;
175
+ this.inputSchema = config.inputSchema;
176
+ this.outputSchema = config.outputSchema;
177
+ this._execute = config.execute;
178
+ }
179
+ createTool(connections, config) {
180
+ return {
181
+ description: this.description,
182
+ inputSchema: this.inputSchema,
183
+ outputSchema: this.outputSchema,
184
+ execute: (input) => this._execute(input, connections, config)
185
+ };
186
+ }
187
+ };
188
+
189
+ // ../connectors/src/connector-plugin.ts
190
+ var ConnectorPlugin = class _ConnectorPlugin {
191
+ slug;
192
+ authType;
193
+ name;
194
+ description;
195
+ iconUrl;
196
+ parameters;
197
+ releaseFlag;
198
+ proxyPolicy;
199
+ experimentalAttributes;
200
+ categories;
201
+ onboarding;
202
+ systemPrompt;
203
+ tools;
204
+ query;
205
+ checkConnection;
206
+ /**
207
+ * SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
208
+ * implement this expose a step-by-step exploration flow (database/schema/
209
+ * table/etc. discovery) that the dashboard backend drives via the
210
+ * `/connections/:connectionId/setup` endpoint. Implement by delegating to
211
+ * `runSetupFlow` from `setup-flow.ts`.
212
+ */
213
+ setup;
214
+ /**
215
+ * Opt-out of the default "verify before save" behavior on connection
216
+ * creation. The backend invokes `checkConnection` synchronously while
217
+ * creating the connection and aborts (no row inserted) if it fails — this
218
+ * flag disables that for connectors where the check cannot succeed pre-save:
219
+ *
220
+ * - `squadbase-db` populates `connection-url` only after Neon provisioning
221
+ * - OAuth connectors require an OAuth-aware proxyFetch keyed by the
222
+ * connectionId, which doesn't exist until the row is saved
223
+ *
224
+ * Exceptions are the explicit position; new credential-input connectors get
225
+ * the default verify-on-create behavior without opt-in.
226
+ */
227
+ skipConnectionCheckOnCreate;
228
+ constructor(config) {
229
+ this.slug = config.slug;
230
+ this.authType = config.authType;
231
+ this.name = config.name;
232
+ this.description = config.description;
233
+ this.iconUrl = config.iconUrl;
234
+ this.parameters = config.parameters;
235
+ this.releaseFlag = config.releaseFlag;
236
+ this.proxyPolicy = config.proxyPolicy;
237
+ this.experimentalAttributes = config.experimentalAttributes;
238
+ this.categories = config.categories ?? [];
239
+ this.onboarding = config.onboarding;
240
+ this.systemPrompt = config.systemPrompt;
241
+ this.tools = config.tools;
242
+ this.query = config.query;
243
+ this.checkConnection = config.checkConnection;
244
+ this.setup = config.setup;
245
+ this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
246
+ }
247
+ get connectorKey() {
248
+ return _ConnectorPlugin.deriveKey(this.slug, this.authType);
249
+ }
250
+ /**
251
+ * Create tools for connections that belong to this connector.
252
+ * Filters connections by connectorKey internally.
253
+ * Returns tools keyed as `connector_${connectorKey}_${toolName}`.
254
+ */
255
+ createTools(connections, config, opts) {
256
+ const myConnections = connections.filter(
257
+ (c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
258
+ );
259
+ const result = {};
260
+ for (const t of Object.values(this.tools)) {
261
+ const tool = t.createTool(myConnections, config);
262
+ const originalToModelOutput = tool.toModelOutput;
263
+ result[`connector_${this.connectorKey}_${t.name}`] = {
264
+ ...tool,
265
+ toModelOutput: async (options) => {
266
+ if (!originalToModelOutput) {
267
+ return opts.truncateOutput(options.output);
268
+ }
269
+ const modelOutput = await originalToModelOutput(options);
270
+ if (modelOutput.type === "text" || modelOutput.type === "json") {
271
+ return opts.truncateOutput(modelOutput.value);
272
+ }
273
+ return modelOutput;
274
+ }
275
+ };
276
+ }
277
+ return result;
278
+ }
279
+ static deriveKey(slug, authType) {
280
+ if (authType) return `${slug}-${authType}`;
281
+ const LEGACY_NULL_AUTH_TYPE_MAP = {
282
+ // user-password
283
+ "postgresql": "user-password",
284
+ "mysql": "user-password",
285
+ "clickhouse": "user-password",
286
+ "kintone": "user-password",
287
+ "squadbase-db": "user-password",
288
+ // service-account
289
+ "snowflake": "service-account",
290
+ "bigquery": "service-account",
291
+ "google-analytics": "service-account",
292
+ "google-calendar": "service-account",
293
+ "aws-athena": "service-account",
294
+ "redshift": "service-account",
295
+ // api-key
296
+ "databricks": "api-key",
297
+ "dbt": "api-key",
298
+ "airtable": "api-key",
299
+ "openai": "api-key",
300
+ "gemini": "api-key",
301
+ "anthropic": "api-key",
302
+ "wix-store": "api-key"
303
+ };
304
+ const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
305
+ if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
306
+ return slug;
307
+ }
308
+ };
309
+
310
+ // ../connectors/src/setup-flow.ts
311
+ async function runSetupFlow(flow, params, ctx, config) {
312
+ const runtime = {
313
+ params,
314
+ language: ctx.language,
315
+ config
316
+ };
317
+ let state = flow.initialState();
318
+ let answerIdx = 0;
319
+ const pendingParameterUpdates = [];
320
+ for (const step of flow.steps) {
321
+ const ans = ctx.answers[answerIdx];
322
+ if (ans && ans.questionSlug === step.slug) {
323
+ state = step.applyAnswer(state, ans.answer);
324
+ if (step.toParameterUpdates) {
325
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
326
+ }
327
+ answerIdx += 1;
328
+ continue;
329
+ }
330
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
331
+ if (step.type === "text") {
332
+ if (step.fetchOptions) {
333
+ const options2 = await step.fetchOptions(state, runtime);
334
+ if (options2.length === 0) {
335
+ continue;
336
+ }
337
+ }
338
+ return {
339
+ type: "nextQuestion",
340
+ questionSlug: step.slug,
341
+ question: step.question[ctx.language],
342
+ questionType: "text",
343
+ allowFreeText: resolvedAllowFreeText,
344
+ ...pendingParameterUpdates.length > 0 && {
345
+ parameterUpdates: pendingParameterUpdates
346
+ }
347
+ };
348
+ }
349
+ const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
350
+ if (options.length === 0) {
351
+ continue;
352
+ }
353
+ return {
354
+ type: "nextQuestion",
355
+ questionSlug: step.slug,
356
+ question: step.question[ctx.language],
357
+ questionType: step.type,
358
+ options,
359
+ allowFreeText: resolvedAllowFreeText,
360
+ ...pendingParameterUpdates.length > 0 && {
361
+ parameterUpdates: pendingParameterUpdates
362
+ }
363
+ };
364
+ }
365
+ const dataInvestigationResult = await flow.finalize(state, runtime);
366
+ return {
367
+ type: "fulfilled",
368
+ dataInvestigationResult,
369
+ ...pendingParameterUpdates.length > 0 && {
370
+ parameterUpdates: pendingParameterUpdates
371
+ }
372
+ };
373
+ }
374
+
375
+ // ../connectors/src/auth-types.ts
376
+ var AUTH_TYPES = {
377
+ OAUTH: "oauth",
378
+ API_KEY: "api-key",
379
+ JWT: "jwt",
380
+ SERVICE_ACCOUNT: "service-account",
381
+ PAT: "pat",
382
+ USER_PASSWORD: "user-password"
383
+ };
384
+
385
+ // ../connectors/src/connectors/x/tools/request.ts
386
+ import { z } from "zod";
387
+ var BASE_URL2 = "https://api.x.com";
388
+ var REQUEST_TIMEOUT_MS = 6e4;
389
+ function readRateLimit(headers) {
390
+ const limit = headers.get("x-rate-limit-limit");
391
+ const remaining = headers.get("x-rate-limit-remaining");
392
+ const reset = headers.get("x-rate-limit-reset");
393
+ return {
394
+ limit: limit ? Number(limit) : void 0,
395
+ remaining: remaining ? Number(remaining) : void 0,
396
+ reset: reset ? Number(reset) : void 0
397
+ };
398
+ }
399
+ var inputSchema = z.object({
400
+ toolUseIntent: z.string().optional().describe(
401
+ "Brief description of what you intend to accomplish with this tool call"
402
+ ),
403
+ connectionId: z.string().describe("ID of the X connection to use"),
404
+ method: z.enum(["GET", "POST", "PUT", "DELETE"]).describe(
405
+ "HTTP method. Use GET for dashboard data reads. Use POST/PUT/DELETE only when the user explicitly asks for mutations supported by their X API access level."
406
+ ),
407
+ path: z.string().describe(
408
+ "API path appended to https://api.x.com. Use '/2/...' paths such as '/2/users/by/username/xdevelopers', '/2/users/{id}/tweets', or '/2/tweets/search/recent'. Absolute URLs are not allowed."
409
+ ),
410
+ queryParams: z.record(z.string(), z.string()).optional().describe(
411
+ "Query parameters to append to the request URL. Examples: tweet.fields=created_at,public_metrics, user.fields=created_at,public_metrics, max_results=10, query=from:xdevelopers. Keep max_results small for dashboard exploration."
412
+ ),
413
+ body: z.record(z.string(), z.unknown()).optional().describe("JSON request body for POST/PUT requests.")
414
+ });
415
+ var outputSchema = z.discriminatedUnion("success", [
416
+ z.object({
417
+ success: z.literal(true),
418
+ status: z.number(),
419
+ rateLimit: z.object({
420
+ limit: z.number().optional(),
421
+ remaining: z.number().optional(),
422
+ reset: z.number().optional()
423
+ }),
424
+ data: z.unknown()
425
+ }),
426
+ z.object({
427
+ success: z.literal(false),
428
+ status: z.number().optional(),
429
+ rateLimit: z.object({
430
+ limit: z.number().optional(),
431
+ remaining: z.number().optional(),
432
+ reset: z.number().optional()
433
+ }).optional(),
434
+ error: z.string()
435
+ })
436
+ ]);
437
+ var requestTool = new ConnectorTool({
438
+ name: "request",
439
+ description: `Send authenticated requests to the X API (https://api.x.com).
440
+ Use this tool for X API v2 reads such as looking up users, fetching posts, retrieving timelines, and performing recent search. Authentication is handled automatically with the connection's Bearer Token; never include Authorization headers yourself.
441
+
442
+ X rate limits are endpoint-specific and commonly reset on fixed windows. This tool returns x-rate-limit-limit, x-rate-limit-remaining, and x-rate-limit-reset when X sends them. Before making repeated calls or adding pagination, inspect these values and keep dashboard requests small. Do not use this tool for frequent polling; if a request returns 429, wait until the reset timestamp instead of retrying immediately.`,
443
+ inputSchema,
444
+ outputSchema,
445
+ async execute({ connectionId, method, path: path2, queryParams, body }, connections) {
446
+ const connection2 = connections.find((c) => c.id === connectionId);
447
+ if (!connection2) {
448
+ return {
449
+ success: false,
450
+ error: `Connection ${connectionId} not found`
451
+ };
452
+ }
453
+ console.log(`[connector-request] x/${connection2.name}: ${method} ${path2}`);
454
+ try {
455
+ if (/^https?:\/\//i.test(path2)) {
456
+ return {
457
+ success: false,
458
+ error: "Absolute URLs are not allowed. Pass a path such as /2/users/me."
459
+ };
460
+ }
461
+ const bearerToken = parameters.bearerToken.getValue(connection2);
462
+ const url = new URL(
463
+ `${BASE_URL2}${path2.startsWith("/") ? "" : "/"}${path2}`
464
+ );
465
+ if (queryParams) {
466
+ for (const [k, v] of Object.entries(queryParams)) {
467
+ url.searchParams.set(k, v);
468
+ }
469
+ }
470
+ const controller = new AbortController();
471
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
472
+ try {
473
+ const response = await fetch(url.toString(), {
474
+ method,
475
+ headers: {
476
+ Authorization: `Bearer ${bearerToken}`,
477
+ Accept: "application/json",
478
+ ...body ? { "Content-Type": "application/json" } : {}
479
+ },
480
+ body: body ? JSON.stringify(body) : void 0,
481
+ signal: controller.signal
482
+ });
483
+ const rateLimit = readRateLimit(response.headers);
484
+ const text = await response.text();
485
+ let data = text;
486
+ if (text.length > 0) {
487
+ try {
488
+ data = JSON.parse(text);
489
+ } catch {
490
+ data = text;
491
+ }
492
+ }
493
+ if (!response.ok) {
494
+ const error = typeof data === "object" && data != null ? JSON.stringify(data) : String(data || response.statusText);
495
+ return {
496
+ success: false,
497
+ status: response.status,
498
+ rateLimit,
499
+ error
500
+ };
501
+ }
502
+ return {
503
+ success: true,
504
+ status: response.status,
505
+ rateLimit,
506
+ data
507
+ };
508
+ } finally {
509
+ clearTimeout(timeout);
510
+ }
511
+ } catch (err) {
512
+ const msg = err instanceof Error ? err.message : String(err);
513
+ return { success: false, error: msg };
514
+ }
515
+ }
516
+ });
517
+
518
+ // ../connectors/src/connectors/x/setup.ts
519
+ var requestToolName = `x-api-key_${requestTool.name}`;
520
+ var xOnboarding = new ConnectorOnboarding({
521
+ connectionSetupInstructions: {
522
+ en: `Follow these steps to set up the X connection.
523
+
524
+ 1. Call \`${requestToolName}\` to verify the Bearer Token:
525
+ - \`method\`: \`"GET"\`
526
+ - \`path\`: \`"/2/users/by/username/xdevelopers"\`
527
+ 2. If an error is returned, ask the user to verify their Bearer Token (it must be a valid App-only Bearer Token with at least Basic access)
528
+
529
+ #### Constraints
530
+ - **Do NOT read post or user data beyond the single verification call above**
531
+ - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`,
532
+ ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067X\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
533
+
534
+ 1. \`${requestToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066 Bearer Token \u3092\u691C\u8A3C\u3059\u308B:
535
+ - \`method\`: \`"GET"\`
536
+ - \`path\`: \`"/2/users/by/username/xdevelopers"\`
537
+ 2. \u30A8\u30E9\u30FC\u304C\u8FD4\u3055\u308C\u305F\u5834\u5408\u3001\u30E6\u30FC\u30B6\u30FC\u306B Bearer Token \u306E\u78BA\u8A8D\u3092\u4F9D\u983C\u3059\u308B\uFF08Basic \u30A2\u30AF\u30BB\u30B9\u4EE5\u4E0A\u306E\u6A29\u9650\u3092\u6301\u3064\u6709\u52B9\u306A App-only Bearer Token \u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u304B\u78BA\u8A8D\uFF09
538
+
539
+ #### \u5236\u7D04
540
+ - **\u4E0A\u8A18\u306E\u691C\u8A3C\u547C\u3073\u51FA\u3057 1 \u56DE\u3092\u8D85\u3048\u3066\u6295\u7A3F\u3084\u30E6\u30FC\u30B6\u30FC\u30C7\u30FC\u30BF\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3053\u3068**
541
+ - \u30C4\u30FC\u30EB\u9593\u306F 1 \u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057\u3002\u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`
542
+ },
543
+ dataOverviewInstructions: {
544
+ en: `1. Use connector_x-api-key_request with GET /2/users/by/username/{username} to inspect target accounts
545
+ 2. Use GET /2/users/{id}/tweets with a small max_results value to sample recent posts
546
+ 3. Use GET /2/tweets/search/recent only when keyword search is required
547
+ 4. Always check rateLimit.remaining and rateLimit.reset in tool results before issuing additional calls`,
548
+ ja: `1. connector_x-api-key_request \u3067 GET /2/users/by/username/{username} \u3092\u547C\u3073\u51FA\u3057\u3001\u5BFE\u8C61\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u78BA\u8A8D\u3057\u307E\u3059
549
+ 2. GET /2/users/{id}/tweets \u3092\u5C0F\u3055\u306A max_results \u3067\u547C\u3073\u51FA\u3057\u3001\u6700\u8FD1\u306E\u6295\u7A3F\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u3057\u307E\u3059
550
+ 3. \u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22\u304C\u5FC5\u8981\u306A\u5834\u5408\u306E\u307F GET /2/tweets/search/recent \u3092\u4F7F\u7528\u3057\u307E\u3059
551
+ 4. \u8FFD\u52A0\u306E\u547C\u3073\u51FA\u3057\u524D\u306B\u3001\u30C4\u30FC\u30EB\u7D50\u679C\u306E rateLimit.remaining \u3068 rateLimit.reset \u3092\u5FC5\u305A\u78BA\u8A8D\u3057\u307E\u3059`
552
+ }
553
+ });
554
+
555
+ // ../connectors/src/connectors/x/setup-flow.ts
556
+ var xSetupFlow = {
557
+ initialState: () => ({}),
558
+ steps: [],
559
+ async finalize(_state, rt) {
560
+ const sections = rt.language === "ja" ? [
561
+ "## X",
562
+ "",
563
+ "- \u63A5\u7D9A\u306F Bearer Token \u3067\u8A8D\u8A3C\u3055\u308C\u307E\u3059\u3002",
564
+ "- \u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u5B9F\u88C5\u3067\u306F\u3001\u540C\u3058\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u77ED\u5468\u671F\u30DD\u30FC\u30EA\u30F3\u30B0\u3084\u5927\u91CF\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u3092\u907F\u3051\u3066\u304F\u3060\u3055\u3044\u3002",
565
+ "- X API \u306E rate limit \u306F\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u5358\u4F4D\u3067\u7BA1\u7406\u3055\u308C\u307E\u3059\u3002\u30EC\u30B9\u30DD\u30F3\u30B9\u30D8\u30C3\u30C0\u30FC\u306E `x-rate-limit-remaining` \u3068 `x-rate-limit-reset` \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
566
+ "- 429 \u304C\u8FD4\u3063\u305F\u5834\u5408\u306F\u3001reset \u6642\u523B\u307E\u3067\u518D\u8A66\u884C\u3092\u6B62\u3081\u3066\u304F\u3060\u3055\u3044\u3002"
567
+ ] : [
568
+ "## X",
569
+ "",
570
+ "- The connection authenticates with a Bearer Token.",
571
+ "- When building dashboards, avoid frequent polling and large pagination against the same endpoint.",
572
+ "- X API rate limits are endpoint-specific. Check the `x-rate-limit-remaining` and `x-rate-limit-reset` response headers.",
573
+ "- If a request returns 429, stop retrying until the reset time."
574
+ ];
575
+ return sections.join("\n");
576
+ }
577
+ };
578
+
579
+ // ../connectors/src/connectors/x/index.ts
580
+ var tools = { request: requestTool };
581
+ var xConnector = new ConnectorPlugin({
582
+ slug: "x",
583
+ authType: AUTH_TYPES.API_KEY,
584
+ name: "X",
585
+ description: "Connect to X API v2 for posts, users, search, timelines, trends, and related social media data using a user-provided Bearer Token.",
586
+ iconUrl: "https://cdn.simpleicons.org/x/000000",
587
+ parameters,
588
+ releaseFlag: { dev1: true, dev2: true, prod: false },
589
+ categories: ["social_media"],
590
+ onboarding: xOnboarding,
591
+ systemPrompt: {
592
+ en: `### Tools
593
+
594
+ - \`connector_x-api-key_request\`: Send authenticated requests to X API v2. Use it for reading users, posts, timelines, and recent search. Authentication is handled automatically with the connection's Bearer Token. The tool returns rate limit headers when available; inspect \`rateLimit.remaining\` and \`rateLimit.reset\` before making repeated calls.
595
+
596
+ ### Business Logic
597
+
598
+ The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
599
+
600
+ SDK methods (client created via \`connection(connectionId)\`):
601
+ - \`client.request(path, init?)\` \u2014 low-level authenticated fetch against \`https://api.x.com\`
602
+ - \`client.getUserByUsername(username, options?)\` \u2014 get an X user by username
603
+ - \`client.getUser(id, options?)\` \u2014 get an X user by ID
604
+ - \`client.getTweet(id, options?)\` \u2014 get a post by ID
605
+ - \`client.getUserTweets(userId, options?)\` \u2014 get recent posts for a user
606
+ - \`client.searchRecent(query, options?)\` \u2014 search recent posts
607
+ - \`client.searchAll(query, options?)\` \u2014 search the full archive when the connected X API access tier supports it
608
+
609
+ When building dashboards, keep API calls small and cache or reuse responses in your own handler where possible. Avoid short-interval polling, avoid fetching many pages during rendering, and avoid repeated recent-search calls for the same query. If X returns HTTP 429, do not retry immediately; wait until the \`x-rate-limit-reset\` timestamp.
610
+
611
+ \`\`\`ts
612
+ import type { Context } from "hono";
613
+ import { connection } from "@squadbase/vite-server/connectors/x";
614
+
615
+ const x = connection("<connectionId>");
616
+
617
+ export default async function handler(c: Context) {
618
+ const { username = "xdevelopers", maxResults = 10 } = await c.req.json<{
619
+ username?: string;
620
+ maxResults?: number;
621
+ }>();
622
+
623
+ const user = await x.getUserByUsername(username, {
624
+ query: { "user.fields": "created_at,description,public_metrics" },
625
+ });
626
+ const userId = (user.data as { id: string }).id;
627
+ const tweets = await x.getUserTweets(userId, {
628
+ query: {
629
+ max_results: Math.min(maxResults, 25),
630
+ "tweet.fields": "created_at,public_metrics",
631
+ },
632
+ });
633
+
634
+ return c.json({ user: user.data, tweets: tweets.data ?? [] });
635
+ }
636
+ \`\`\`
637
+
638
+ ### X API Reference
639
+
640
+ - Base URL: \`https://api.x.com\`
641
+ - Authentication: Bearer Token (handled automatically)
642
+ - Rate limits: endpoint-specific; response headers may include \`x-rate-limit-limit\`, \`x-rate-limit-remaining\`, and \`x-rate-limit-reset\`
643
+
644
+ #### Common Endpoints
645
+ - GET \`/2/users/by/username/{username}\` \u2014 Look up a user by username
646
+ - GET \`/2/users/{id}\` \u2014 Look up a user by ID
647
+ - GET \`/2/users/{id}/tweets\` \u2014 Get a user's recent posts
648
+ - GET \`/2/tweets/{id}\` \u2014 Look up a post by ID
649
+ - GET \`/2/tweets/search/recent\` \u2014 Search recent posts
650
+ - GET \`/2/tweets/search/all\` \u2014 Search the full archive (requires X API access that includes full-archive search)
651
+ - GET \`/2/tweets/counts/recent\` \u2014 Count recent posts matching a query
652
+
653
+ #### Tips
654
+ - Request only fields you need with \`tweet.fields\`, \`user.fields\`, and \`expansions\`
655
+ - Use small \`max_results\` values while exploring; dashboards rarely need hundreds of posts per render
656
+ - Prefer counts endpoints for trend charts when the user needs aggregate volume instead of individual posts`,
657
+ ja: `### \u30C4\u30FC\u30EB
658
+
659
+ - \`connector_x-api-key_request\`: X API v2 \u306B\u8A8D\u8A3C\u6E08\u307F\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u9001\u4FE1\u3057\u307E\u3059\u3002\u30E6\u30FC\u30B6\u30FC\u3001\u6295\u7A3F\u3001\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u3001recent search \u306E\u8AAD\u307F\u53D6\u308A\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\u306F\u63A5\u7D9A\u306E Bearer Token \u3067\u81EA\u52D5\u7684\u306B\u884C\u308F\u308C\u307E\u3059\u3002\u5229\u7528\u53EF\u80FD\u306A\u5834\u5408\u306F rate limit \u30D8\u30C3\u30C0\u30FC\u3082\u8FD4\u3059\u305F\u3081\u3001\u7E70\u308A\u8FD4\u3057\u547C\u3073\u51FA\u3059\u524D\u306B \`rateLimit.remaining\` \u3068 \`rateLimit.reset\` \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002
660
+
661
+ ### Business Logic
662
+
663
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
664
+
665
+ SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
666
+ - \`client.request(path, init?)\` \u2014 \`https://api.x.com\` \u306B\u5BFE\u3059\u308B\u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch
667
+ - \`client.getUserByUsername(username, options?)\` \u2014 username \u3067 X \u30E6\u30FC\u30B6\u30FC\u3092\u53D6\u5F97
668
+ - \`client.getUser(id, options?)\` \u2014 ID \u3067 X \u30E6\u30FC\u30B6\u30FC\u3092\u53D6\u5F97
669
+ - \`client.getTweet(id, options?)\` \u2014 ID \u3067\u6295\u7A3F\u3092\u53D6\u5F97
670
+ - \`client.getUserTweets(userId, options?)\` \u2014 \u30E6\u30FC\u30B6\u30FC\u306E\u6700\u8FD1\u306E\u6295\u7A3F\u3092\u53D6\u5F97
671
+ - \`client.searchRecent(query, options?)\` \u2014 \u6700\u8FD1\u306E\u6295\u7A3F\u3092\u691C\u7D22
672
+ - \`client.searchAll(query, options?)\` \u2014 \u63A5\u7D9A\u4E2D\u306E X API access tier \u304C\u5BFE\u5FDC\u3057\u3066\u3044\u308B\u5834\u5408\u306B full archive \u3092\u691C\u7D22
673
+
674
+ \u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3092\u4F5C\u308B\u6642\u306F API \u547C\u3073\u51FA\u3057\u3092\u5C0F\u3055\u304F\u3057\u3001\u53EF\u80FD\u3067\u3042\u308C\u3070\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u518D\u5229\u7528\u30FB\u30AD\u30E3\u30C3\u30B7\u30E5\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u77ED\u5468\u671F\u30DD\u30FC\u30EA\u30F3\u30B0\u3001\u30EC\u30F3\u30C0\u30EA\u30F3\u30B0\u3054\u3068\u306E\u5927\u91CF\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u3001\u540C\u3058\u30AF\u30A8\u30EA\u3067\u306E recent search \u9023\u6253\u306F\u907F\u3051\u3066\u304F\u3060\u3055\u3044\u3002X \u304C HTTP 429 \u3092\u8FD4\u3057\u305F\u5834\u5408\u306F\u5373\u6642\u30EA\u30C8\u30E9\u30A4\u305B\u305A\u3001\`x-rate-limit-reset\` \u306E\u6642\u523B\u307E\u3067\u5F85\u3063\u3066\u304F\u3060\u3055\u3044\u3002
675
+
676
+ \`\`\`ts
677
+ import type { Context } from "hono";
678
+ import { connection } from "@squadbase/vite-server/connectors/x";
679
+
680
+ const x = connection("<connectionId>");
681
+
682
+ export default async function handler(c: Context) {
683
+ const { username = "xdevelopers", maxResults = 10 } = await c.req.json<{
684
+ username?: string;
685
+ maxResults?: number;
686
+ }>();
687
+
688
+ const user = await x.getUserByUsername(username, {
689
+ query: { "user.fields": "created_at,description,public_metrics" },
690
+ });
691
+ const userId = (user.data as { id: string }).id;
692
+ const tweets = await x.getUserTweets(userId, {
693
+ query: {
694
+ max_results: Math.min(maxResults, 25),
695
+ "tweet.fields": "created_at,public_metrics",
696
+ },
697
+ });
698
+
699
+ return c.json({ user: user.data, tweets: tweets.data ?? [] });
700
+ }
701
+ \`\`\`
702
+
703
+ ### X API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
704
+
705
+ - \u30D9\u30FC\u30B9URL: \`https://api.x.com\`
706
+ - \u8A8D\u8A3C: Bearer Token\uFF08\u81EA\u52D5\u8A2D\u5B9A\uFF09
707
+ - Rate limit: \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u5358\u4F4D\u3002\u30EC\u30B9\u30DD\u30F3\u30B9\u30D8\u30C3\u30C0\u30FC\u306B \`x-rate-limit-limit\`, \`x-rate-limit-remaining\`, \`x-rate-limit-reset\` \u304C\u542B\u307E\u308C\u308B\u3053\u3068\u304C\u3042\u308A\u307E\u3059
708
+
709
+ #### \u4E3B\u8981\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
710
+ - GET \`/2/users/by/username/{username}\` \u2014 username \u3067\u30E6\u30FC\u30B6\u30FC\u3092\u53D6\u5F97
711
+ - GET \`/2/users/{id}\` \u2014 ID \u3067\u30E6\u30FC\u30B6\u30FC\u3092\u53D6\u5F97
712
+ - GET \`/2/users/{id}/tweets\` \u2014 \u30E6\u30FC\u30B6\u30FC\u306E\u6700\u8FD1\u306E\u6295\u7A3F\u3092\u53D6\u5F97
713
+ - GET \`/2/tweets/{id}\` \u2014 ID \u3067\u6295\u7A3F\u3092\u53D6\u5F97
714
+ - GET \`/2/tweets/search/recent\` \u2014 \u6700\u8FD1\u306E\u6295\u7A3F\u3092\u691C\u7D22
715
+ - GET \`/2/tweets/search/all\` \u2014 full archive \u3092\u691C\u7D22\uFF08full-archive search \u3092\u542B\u3080 X API access \u304C\u5FC5\u8981\uFF09
716
+ - GET \`/2/tweets/counts/recent\` \u2014 \u30AF\u30A8\u30EA\u306B\u4E00\u81F4\u3059\u308B\u6700\u8FD1\u306E\u6295\u7A3F\u6570\u3092\u96C6\u8A08
717
+
718
+ #### \u30D2\u30F3\u30C8
719
+ - \`tweet.fields\`, \`user.fields\`, \`expansions\` \u3067\u5FC5\u8981\u306A\u30D5\u30A3\u30FC\u30EB\u30C9\u3060\u3051\u3092\u53D6\u5F97\u3057\u3066\u304F\u3060\u3055\u3044
720
+ - \u63A2\u7D22\u6642\u306E \`max_results\` \u306F\u5C0F\u3055\u304F\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u591A\u304F\u306E\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3067\u306F1\u56DE\u306E\u63CF\u753B\u306B\u6570\u767E\u4EF6\u306E\u6295\u7A3F\u306F\u4E0D\u8981\u3067\u3059
721
+ - \u500B\u5225\u6295\u7A3F\u3067\u306F\u306A\u304F\u4EF6\u6570\u30C8\u30EC\u30F3\u30C9\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F counts endpoint \u3092\u512A\u5148\u3057\u3066\u304F\u3060\u3055\u3044`
722
+ },
723
+ tools,
724
+ setup: (params, ctx, config) => runSetupFlow(xSetupFlow, params, ctx, config),
725
+ async checkConnection(params) {
726
+ const bearerToken = params[parameters.bearerToken.slug];
727
+ if (!bearerToken) {
728
+ return {
729
+ success: false,
730
+ error: `Missing required parameter: ${parameters.bearerToken.slug}`
731
+ };
732
+ }
733
+ try {
734
+ const res = await fetch(
735
+ "https://api.x.com/2/users/by/username/xdevelopers",
736
+ {
737
+ method: "GET",
738
+ headers: {
739
+ Authorization: `Bearer ${bearerToken}`,
740
+ Accept: "application/json"
741
+ }
742
+ }
743
+ );
744
+ if (!res.ok) {
745
+ const errorText = await res.text().catch(() => res.statusText);
746
+ return {
747
+ success: false,
748
+ error: `X API failed: HTTP ${res.status} ${errorText}`
749
+ };
750
+ }
751
+ return { success: true };
752
+ } catch (error) {
753
+ return {
754
+ success: false,
755
+ error: error instanceof Error ? error.message : String(error)
756
+ };
757
+ }
758
+ }
759
+ });
760
+
761
+ // src/connectors/create-connector-sdk.ts
762
+ import { readFileSync } from "fs";
763
+ import path from "path";
764
+
765
+ // src/connector-client/env.ts
766
+ function resolveEnvVar(entry, key, connectionId) {
767
+ const envVarName = entry.envVars[key];
768
+ if (!envVarName) {
769
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
770
+ }
771
+ const value = process.env[envVarName];
772
+ if (!value) {
773
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
774
+ }
775
+ return value;
776
+ }
777
+ function resolveEnvVarOptional(entry, key) {
778
+ const envVarName = entry.envVars[key];
779
+ if (!envVarName) return void 0;
780
+ return process.env[envVarName] || void 0;
781
+ }
782
+
783
+ // src/connector-client/proxy-fetch.ts
784
+ import { getContext } from "hono/context-storage";
785
+ import { getCookie } from "hono/cookie";
786
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
787
+ var TABLEAU_SESSION_SENTINEL_URL = "squadbase://tableau-session/";
788
+ function normalizeHeaders(input) {
789
+ const out = {};
790
+ if (!input) return out;
791
+ new Headers(input).forEach((value, key) => {
792
+ out[key] = value;
793
+ });
794
+ return out;
795
+ }
796
+ function extractInputUrl(input) {
797
+ if (typeof input === "string") return input;
798
+ if (input instanceof URL) return input.href;
799
+ return input.url;
800
+ }
801
+ function createSandboxProxyFetch(connectionId) {
802
+ return async (input, init) => {
803
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
804
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
805
+ if (!token || !sandboxId) {
806
+ throw new Error(
807
+ "Connection proxy is not configured. Please check your deployment settings."
808
+ );
809
+ }
810
+ const originalUrl = extractInputUrl(input);
811
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
812
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
813
+ const sessionUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
814
+ return fetch(sessionUrl, {
815
+ method: "POST",
816
+ headers: { Authorization: `Bearer ${token}` }
817
+ });
818
+ }
819
+ const originalMethod = init?.method ?? "GET";
820
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
821
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
822
+ return fetch(proxyUrl, {
823
+ method: "POST",
824
+ headers: {
825
+ "Content-Type": "application/json",
826
+ Authorization: `Bearer ${token}`
827
+ },
828
+ body: JSON.stringify({
829
+ url: originalUrl,
830
+ method: originalMethod,
831
+ headers: normalizeHeaders(init?.headers),
832
+ body: originalBody
833
+ })
834
+ });
835
+ };
836
+ }
837
+ function createDeployedAppProxyFetch(connectionId) {
838
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
839
+ if (!projectId) {
840
+ throw new Error(
841
+ "Connection proxy is not configured. Please check your deployment settings."
842
+ );
843
+ }
844
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
845
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
846
+ const sessionUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
847
+ return async (input, init) => {
848
+ const originalUrl = extractInputUrl(input);
849
+ const c = getContext();
850
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
851
+ if (!appSession) {
852
+ throw new Error(
853
+ "No authentication method available for connection proxy."
854
+ );
855
+ }
856
+ if (originalUrl === TABLEAU_SESSION_SENTINEL_URL) {
857
+ return fetch(sessionUrl, {
858
+ method: "POST",
859
+ headers: { Authorization: `Bearer ${appSession}` }
860
+ });
861
+ }
862
+ const originalMethod = init?.method ?? "GET";
863
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
864
+ return fetch(proxyUrl, {
865
+ method: "POST",
866
+ headers: {
867
+ "Content-Type": "application/json",
868
+ Authorization: `Bearer ${appSession}`
869
+ },
870
+ body: JSON.stringify({
871
+ url: originalUrl,
872
+ method: originalMethod,
873
+ headers: normalizeHeaders(init?.headers),
874
+ body: originalBody
875
+ })
876
+ });
877
+ };
878
+ }
879
+ function createProxyFetch(connectionId) {
880
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
881
+ return createSandboxProxyFetch(connectionId);
882
+ }
883
+ return createDeployedAppProxyFetch(connectionId);
884
+ }
885
+
886
+ // src/connectors/create-connector-sdk.ts
887
+ function loadConnectionsSync() {
888
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
889
+ try {
890
+ const raw = readFileSync(filePath, "utf-8");
891
+ return JSON.parse(raw);
892
+ } catch {
893
+ return {};
894
+ }
895
+ }
896
+ function createConnectorSdk(plugin, createClient2) {
897
+ return (connectionId) => {
898
+ const connections = loadConnectionsSync();
899
+ const entry = connections[connectionId];
900
+ if (!entry) {
901
+ throw new Error(
902
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
903
+ );
904
+ }
905
+ if (entry.connector.slug !== plugin.slug) {
906
+ throw new Error(
907
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
908
+ );
909
+ }
910
+ const params = {};
911
+ for (const param of Object.values(plugin.parameters)) {
912
+ if (param.required) {
913
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
914
+ } else {
915
+ const val = resolveEnvVarOptional(entry, param.slug);
916
+ if (val !== void 0) params[param.slug] = val;
917
+ }
918
+ }
919
+ return createClient2(params, createProxyFetch(connectionId));
920
+ };
921
+ }
922
+
923
+ // src/connectors/entries/x.ts
924
+ var connection = createConnectorSdk(xConnector, createClient);
925
+ export {
926
+ connection
927
+ };