@squadbase/vite-server 0.1.3-dev.13 → 0.1.3-dev.14

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.
@@ -0,0 +1,742 @@
1
+ // ../connectors/src/parameter-definition.ts
2
+ var ParameterDefinition = class {
3
+ slug;
4
+ name;
5
+ description;
6
+ envVarBaseKey;
7
+ type;
8
+ secret;
9
+ required;
10
+ constructor(config) {
11
+ this.slug = config.slug;
12
+ this.name = config.name;
13
+ this.description = config.description;
14
+ this.envVarBaseKey = config.envVarBaseKey;
15
+ this.type = config.type;
16
+ this.secret = config.secret;
17
+ this.required = config.required;
18
+ }
19
+ /**
20
+ * Get the parameter value from a ConnectorConnectionObject.
21
+ */
22
+ getValue(connection2) {
23
+ const param = connection2.parameters.find(
24
+ (p) => p.parameterSlug === this.slug
25
+ );
26
+ if (!param || param.value == null) {
27
+ throw new Error(
28
+ `Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
29
+ );
30
+ }
31
+ return param.value;
32
+ }
33
+ /**
34
+ * Try to get the parameter value. Returns undefined if not found (for optional params).
35
+ */
36
+ tryGetValue(connection2) {
37
+ const param = connection2.parameters.find(
38
+ (p) => p.parameterSlug === this.slug
39
+ );
40
+ if (!param || param.value == null) return void 0;
41
+ return param.value;
42
+ }
43
+ };
44
+
45
+ // ../connectors/src/connectors/mixpanel/parameters.ts
46
+ var parameters = {
47
+ serviceAccountUsername: new ParameterDefinition({
48
+ slug: "service-account-username",
49
+ name: "Service Account Username",
50
+ description: "The Mixpanel service account username for authentication.",
51
+ envVarBaseKey: "MIXPANEL_SERVICE_ACCOUNT_USERNAME",
52
+ type: "text",
53
+ secret: true,
54
+ required: true
55
+ }),
56
+ serviceAccountSecret: new ParameterDefinition({
57
+ slug: "service-account-secret",
58
+ name: "Service Account Secret",
59
+ description: "The Mixpanel service account secret for authentication.",
60
+ envVarBaseKey: "MIXPANEL_SERVICE_ACCOUNT_SECRET",
61
+ type: "text",
62
+ secret: true,
63
+ required: true
64
+ }),
65
+ projectId: new ParameterDefinition({
66
+ slug: "project-id",
67
+ name: "Project ID",
68
+ description: "The Mixpanel project ID. Required for service account authentication. Found in Project Settings.",
69
+ envVarBaseKey: "MIXPANEL_PROJECT_ID",
70
+ type: "text",
71
+ secret: false,
72
+ required: true
73
+ }),
74
+ region: new ParameterDefinition({
75
+ slug: "region",
76
+ name: "Region",
77
+ description: 'Mixpanel data residency region. Set to "eu" for EU, "in" for India. Leave empty for US (default).',
78
+ envVarBaseKey: "MIXPANEL_REGION",
79
+ type: "text",
80
+ secret: false,
81
+ required: false
82
+ })
83
+ };
84
+
85
+ // ../connectors/src/connectors/mixpanel/sdk/index.ts
86
+ function createClient(params) {
87
+ const username = params[parameters.serviceAccountUsername.slug];
88
+ const secret = params[parameters.serviceAccountSecret.slug];
89
+ const projectId = params[parameters.projectId.slug];
90
+ const region = params[parameters.region.slug] ?? "us";
91
+ if (!username) {
92
+ throw new Error(
93
+ `mixpanel: missing required parameter: ${parameters.serviceAccountUsername.slug}`
94
+ );
95
+ }
96
+ if (!secret) {
97
+ throw new Error(
98
+ `mixpanel: missing required parameter: ${parameters.serviceAccountSecret.slug}`
99
+ );
100
+ }
101
+ if (!projectId) {
102
+ throw new Error(
103
+ `mixpanel: missing required parameter: ${parameters.projectId.slug}`
104
+ );
105
+ }
106
+ const queryBase = region === "eu" ? "https://eu.mixpanel.com/api/query" : region === "in" ? "https://in.mixpanel.com/api/query" : "https://mixpanel.com/api/query";
107
+ const exportBase = region === "eu" ? "https://data-eu.mixpanel.com/api/2.0/export" : region === "in" ? "https://data-in.mixpanel.com/api/2.0/export" : "https://data.mixpanel.com/api/2.0/export";
108
+ const authToken = btoa(`${username}:${secret}`);
109
+ function authHeaders(extra) {
110
+ const headers = new Headers(extra);
111
+ headers.set("Authorization", `Basic ${authToken}`);
112
+ headers.set("Accept", "application/json");
113
+ return headers;
114
+ }
115
+ async function assertOk(res, label) {
116
+ if (!res.ok) {
117
+ const body = await res.text().catch(() => "(unreadable body)");
118
+ throw new Error(
119
+ `mixpanel ${label}: ${res.status} ${res.statusText} \u2014 ${body}`
120
+ );
121
+ }
122
+ }
123
+ function withProjectId(url) {
124
+ if (!url.searchParams.has("project_id")) {
125
+ url.searchParams.set("project_id", projectId);
126
+ }
127
+ return url;
128
+ }
129
+ return {
130
+ request(url, init) {
131
+ const parsedUrl = withProjectId(new URL(url));
132
+ const headers = new Headers(init?.headers);
133
+ headers.set("Authorization", `Basic ${authToken}`);
134
+ headers.set("Accept", "application/json");
135
+ return fetch(parsedUrl.toString(), { ...init, headers });
136
+ },
137
+ async exportEvents(options) {
138
+ const url = new URL(exportBase);
139
+ url.searchParams.set("project_id", projectId);
140
+ url.searchParams.set("from_date", options.fromDate);
141
+ url.searchParams.set("to_date", options.toDate);
142
+ if (options.event) {
143
+ url.searchParams.set("event", JSON.stringify(options.event));
144
+ }
145
+ if (options.where) {
146
+ url.searchParams.set("where", options.where);
147
+ }
148
+ if (options.limit !== void 0) {
149
+ url.searchParams.set("limit", String(options.limit));
150
+ }
151
+ const res = await fetch(url.toString(), {
152
+ method: "GET",
153
+ headers: authHeaders()
154
+ });
155
+ await assertOk(res, "exportEvents");
156
+ const text = await res.text();
157
+ const lines = text.split("\n").filter((line) => line.trim().length > 0);
158
+ return lines.map(
159
+ (line) => JSON.parse(line)
160
+ );
161
+ },
162
+ async queryInsights(bookmarkId) {
163
+ const url = withProjectId(new URL(`${queryBase}/insights`));
164
+ url.searchParams.set("bookmark_id", String(bookmarkId));
165
+ const res = await fetch(url.toString(), {
166
+ method: "GET",
167
+ headers: authHeaders()
168
+ });
169
+ await assertOk(res, "queryInsights");
170
+ return await res.json();
171
+ },
172
+ async queryFunnels(options) {
173
+ const url = withProjectId(new URL(`${queryBase}/funnels`));
174
+ url.searchParams.set("funnel_id", String(options.funnelId));
175
+ url.searchParams.set("from_date", options.fromDate);
176
+ url.searchParams.set("to_date", options.toDate);
177
+ if (options.unit) url.searchParams.set("unit", options.unit);
178
+ if (options.interval !== void 0) {
179
+ url.searchParams.set("interval", String(options.interval));
180
+ }
181
+ const res = await fetch(url.toString(), {
182
+ method: "GET",
183
+ headers: authHeaders()
184
+ });
185
+ await assertOk(res, "queryFunnels");
186
+ return await res.json();
187
+ },
188
+ async queryProfiles(options) {
189
+ const url = withProjectId(new URL(`${queryBase}/engage`));
190
+ const bodyParams = new URLSearchParams();
191
+ if (options?.where) bodyParams.set("where", options.where);
192
+ if (options?.outputProperties) {
193
+ bodyParams.set(
194
+ "output_properties",
195
+ JSON.stringify(options.outputProperties)
196
+ );
197
+ }
198
+ if (options?.page !== void 0) {
199
+ bodyParams.set("page", String(options.page));
200
+ }
201
+ if (options?.sessionId) {
202
+ bodyParams.set("session_id", options.sessionId);
203
+ }
204
+ const res = await fetch(url.toString(), {
205
+ method: "POST",
206
+ headers: authHeaders({
207
+ "Content-Type": "application/x-www-form-urlencoded"
208
+ }),
209
+ body: bodyParams.toString()
210
+ });
211
+ await assertOk(res, "queryProfiles");
212
+ return await res.json();
213
+ }
214
+ };
215
+ }
216
+
217
+ // ../connectors/src/connector-onboarding.ts
218
+ var ConnectorOnboarding = class {
219
+ /** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
220
+ connectionSetupInstructions;
221
+ /** Phase 2: Data overview instructions */
222
+ dataOverviewInstructions;
223
+ constructor(config) {
224
+ this.connectionSetupInstructions = config.connectionSetupInstructions;
225
+ this.dataOverviewInstructions = config.dataOverviewInstructions;
226
+ }
227
+ getConnectionSetupPrompt(language) {
228
+ return this.connectionSetupInstructions?.[language] ?? null;
229
+ }
230
+ getDataOverviewInstructions(language) {
231
+ return this.dataOverviewInstructions[language];
232
+ }
233
+ };
234
+
235
+ // ../connectors/src/connector-tool.ts
236
+ var ConnectorTool = class {
237
+ name;
238
+ description;
239
+ inputSchema;
240
+ outputSchema;
241
+ _execute;
242
+ constructor(config) {
243
+ this.name = config.name;
244
+ this.description = config.description;
245
+ this.inputSchema = config.inputSchema;
246
+ this.outputSchema = config.outputSchema;
247
+ this._execute = config.execute;
248
+ }
249
+ createTool(connections, config) {
250
+ return {
251
+ description: this.description,
252
+ inputSchema: this.inputSchema,
253
+ outputSchema: this.outputSchema,
254
+ execute: (input) => this._execute(input, connections, config)
255
+ };
256
+ }
257
+ };
258
+
259
+ // ../connectors/src/connector-plugin.ts
260
+ var ConnectorPlugin = class _ConnectorPlugin {
261
+ slug;
262
+ authType;
263
+ name;
264
+ description;
265
+ iconUrl;
266
+ parameters;
267
+ releaseFlag;
268
+ proxyPolicy;
269
+ experimentalAttributes;
270
+ onboarding;
271
+ systemPrompt;
272
+ tools;
273
+ query;
274
+ checkConnection;
275
+ constructor(config) {
276
+ this.slug = config.slug;
277
+ this.authType = config.authType;
278
+ this.name = config.name;
279
+ this.description = config.description;
280
+ this.iconUrl = config.iconUrl;
281
+ this.parameters = config.parameters;
282
+ this.releaseFlag = config.releaseFlag;
283
+ this.proxyPolicy = config.proxyPolicy;
284
+ this.experimentalAttributes = config.experimentalAttributes;
285
+ this.onboarding = config.onboarding;
286
+ this.systemPrompt = config.systemPrompt;
287
+ this.tools = config.tools;
288
+ this.query = config.query;
289
+ this.checkConnection = config.checkConnection;
290
+ }
291
+ get connectorKey() {
292
+ return _ConnectorPlugin.deriveKey(this.slug, this.authType);
293
+ }
294
+ /**
295
+ * Create tools for connections that belong to this connector.
296
+ * Filters connections by connectorKey internally.
297
+ * Returns tools keyed as `${connectorKey}_${toolName}`.
298
+ */
299
+ createTools(connections, config) {
300
+ const myConnections = connections.filter(
301
+ (c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
302
+ );
303
+ const result = {};
304
+ for (const t of Object.values(this.tools)) {
305
+ result[`${this.connectorKey}_${t.name}`] = t.createTool(
306
+ myConnections,
307
+ config
308
+ );
309
+ }
310
+ return result;
311
+ }
312
+ static deriveKey(slug, authType) {
313
+ return authType ? `${slug}-${authType}` : slug;
314
+ }
315
+ };
316
+
317
+ // ../connectors/src/auth-types.ts
318
+ var AUTH_TYPES = {
319
+ OAUTH: "oauth",
320
+ API_KEY: "api-key",
321
+ JWT: "jwt",
322
+ SERVICE_ACCOUNT: "service-account",
323
+ PAT: "pat",
324
+ USER_PASSWORD: "user-password"
325
+ };
326
+
327
+ // ../connectors/src/connectors/mixpanel/setup.ts
328
+ var mixpanelOnboarding = new ConnectorOnboarding({
329
+ dataOverviewInstructions: {
330
+ en: `1. Check the connection's region parameter to determine the base URL:
331
+ - US (default): https://mixpanel.com/api/query (Query API), https://data.mixpanel.com/api/2.0/export (Export API)
332
+ - EU: https://eu.mixpanel.com/api/query, https://data-eu.mixpanel.com/api/2.0/export
333
+ - India: https://in.mixpanel.com/api/query, https://data-in.mixpanel.com/api/2.0/export
334
+ 2. Call mixpanel_request with GET {exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD (use a short recent date range, e.g. 1-2 days) to export raw events and discover available event types and properties
335
+ 3. Call mixpanel_request with POST {queryBase}/engage to query user profiles and understand user properties
336
+ NOTE: The Query API endpoints (insights, funnels, retention) require a bookmark_id or funnel_id from existing reports in Mixpanel. Use the Export API to retrieve raw events and aggregate in code if you need ad-hoc analysis.`,
337
+ ja: `1. \u63A5\u7D9A\u306Eregion\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u78BA\u8A8D\u3057\u3066\u30D9\u30FC\u30B9URL\u3092\u6C7A\u5B9A\uFF1A
338
+ - US\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\uFF09: https://mixpanel.com/api/query\uFF08Query API\uFF09\u3001https://data.mixpanel.com/api/2.0/export\uFF08Export API\uFF09
339
+ - EU: https://eu.mixpanel.com/api/query\u3001https://data-eu.mixpanel.com/api/2.0/export
340
+ - India: https://in.mixpanel.com/api/query\u3001https://data-in.mixpanel.com/api/2.0/export
341
+ 2. mixpanel_request \u3067 GET {exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD \u3092\u547C\u3073\u51FA\u3057\uFF08\u76F4\u8FD11-2\u65E5\u306E\u77ED\u3044\u671F\u9593\u3092\u4F7F\u7528\uFF09\u3001\u751F\u30A4\u30D9\u30F3\u30C8\u3092\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u3057\u3066\u5229\u7528\u53EF\u80FD\u306A\u30A4\u30D9\u30F3\u30C8\u30BF\u30A4\u30D7\u3068\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u78BA\u8A8D
342
+ 3. mixpanel_request \u3067 POST {queryBase}/engage \u3092\u547C\u3073\u51FA\u3057\u3001\u30E6\u30FC\u30B6\u30FC\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u3092\u7167\u4F1A\u3057\u3066\u30E6\u30FC\u30B6\u30FC\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u628A\u63E1
343
+ \u6CE8\u610F: Query API\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\uFF08insights\u3001funnels\u3001retention\uFF09\u306FMixpanel\u5185\u306E\u65E2\u5B58\u30EC\u30DD\u30FC\u30C8\u306Ebookmark_id\u307E\u305F\u306Ffunnel_id\u304C\u5FC5\u8981\u3067\u3059\u3002\u30A2\u30C9\u30DB\u30C3\u30AF\u5206\u6790\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F\u3001Export API\u3067\u751F\u30A4\u30D9\u30F3\u30C8\u3092\u53D6\u5F97\u3057\u3001\u30B3\u30FC\u30C9\u4E0A\u3067\u96C6\u8A08\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
344
+ }
345
+ });
346
+
347
+ // ../connectors/src/connectors/mixpanel/tools/request.ts
348
+ import { z } from "zod";
349
+ var REQUEST_TIMEOUT_MS = 6e4;
350
+ var inputSchema = z.object({
351
+ toolUseIntent: z.string().optional().describe(
352
+ "Brief description of what you intend to accomplish with this tool call"
353
+ ),
354
+ connectionId: z.string().describe("ID of the Mixpanel connection to use"),
355
+ method: z.enum(["GET", "POST"]).describe(
356
+ "HTTP method. GET for most Query API and Export endpoints. POST for Engage (profile query) endpoint."
357
+ ),
358
+ url: z.string().describe(
359
+ "Full URL including query parameters (e.g., 'https://mixpanel.com/api/query/insights?project_id=12345&bookmark_id=67890'). The project_id query parameter is added automatically if not present."
360
+ ),
361
+ body: z.string().optional().describe(
362
+ "Request body for POST requests. Use application/x-www-form-urlencoded format for the Engage endpoint, or JSON string for other POST endpoints."
363
+ ),
364
+ contentType: z.enum(["application/json", "application/x-www-form-urlencoded"]).optional().describe(
365
+ "Content-Type header. Defaults to application/json. Use application/x-www-form-urlencoded for the Engage endpoint."
366
+ )
367
+ });
368
+ var outputSchema = z.discriminatedUnion("success", [
369
+ z.object({
370
+ success: z.literal(true),
371
+ status: z.number(),
372
+ data: z.unknown()
373
+ }),
374
+ z.object({
375
+ success: z.literal(false),
376
+ error: z.string()
377
+ })
378
+ ]);
379
+ var requestTool = new ConnectorTool({
380
+ name: "request",
381
+ description: `Send authenticated requests to the Mixpanel REST API.
382
+ Authentication is handled automatically using Basic auth (Service Account username + secret).
383
+ Provide the full URL including any query parameters. The project_id query parameter is appended automatically if not present in the URL.
384
+
385
+ Base URLs by region and endpoint type:
386
+ - Query API: US \u2192 https://mixpanel.com/api/query, EU \u2192 https://eu.mixpanel.com/api/query, India \u2192 https://in.mixpanel.com/api/query
387
+ - Export API: US \u2192 https://data.mixpanel.com/api/2.0/export, EU \u2192 https://data-eu.mixpanel.com/api/2.0/export, India \u2192 https://data-in.mixpanel.com/api/2.0/export
388
+
389
+ Key endpoints:
390
+ - GET {queryBase}/insights?project_id=ID&bookmark_id=ID \u2014 Insights report (requires bookmark_id)
391
+ - GET {queryBase}/funnels?project_id=ID&funnel_id=ID&from_date=YYYY-MM-DD&to_date=YYYY-MM-DD \u2014 Funnel analysis
392
+ - GET {queryBase}/retention?project_id=ID&from_date=YYYY-MM-DD&to_date=YYYY-MM-DD \u2014 Retention analysis
393
+ - POST {queryBase}/engage?project_id=ID \u2014 Query user/group profiles
394
+ - GET {exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD \u2014 Export raw events (JSONL)
395
+
396
+ Rate limit: 60 queries/hour, 5 concurrent queries for Query API.`,
397
+ inputSchema,
398
+ outputSchema,
399
+ async execute({ connectionId, method, url, body, contentType }, connections) {
400
+ const connection2 = connections.find((c) => c.id === connectionId);
401
+ if (!connection2) {
402
+ return {
403
+ success: false,
404
+ error: `Connection ${connectionId} not found`
405
+ };
406
+ }
407
+ console.log(
408
+ `[connector-request] mixpanel/${connection2.name}: ${method} ${url}`
409
+ );
410
+ try {
411
+ const username = parameters.serviceAccountUsername.getValue(connection2);
412
+ const secret = parameters.serviceAccountSecret.getValue(connection2);
413
+ const projectId = parameters.projectId.getValue(connection2);
414
+ const authToken = btoa(`${username}:${secret}`);
415
+ let finalUrl = url;
416
+ const urlObj = new URL(url);
417
+ if (!urlObj.searchParams.has("project_id")) {
418
+ urlObj.searchParams.set("project_id", projectId);
419
+ finalUrl = urlObj.toString();
420
+ }
421
+ const controller = new AbortController();
422
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
423
+ try {
424
+ const headers = {
425
+ Authorization: `Basic ${authToken}`,
426
+ Accept: "application/json"
427
+ };
428
+ const effectiveContentType = contentType ?? "application/json";
429
+ if (body) {
430
+ headers["Content-Type"] = effectiveContentType;
431
+ }
432
+ const response = await fetch(finalUrl, {
433
+ method,
434
+ headers,
435
+ body: body ?? void 0,
436
+ signal: controller.signal
437
+ });
438
+ const responseContentType = response.headers.get("content-type") ?? "";
439
+ let data;
440
+ if (responseContentType.includes("application/json")) {
441
+ data = await response.json();
442
+ } else {
443
+ const text = await response.text();
444
+ const lines = text.split("\n").filter((line) => line.trim().length > 0);
445
+ if (lines.length > 0) {
446
+ try {
447
+ data = lines.map(
448
+ (line) => JSON.parse(line)
449
+ );
450
+ } catch {
451
+ data = { raw: text };
452
+ }
453
+ } else {
454
+ data = { raw: text };
455
+ }
456
+ }
457
+ if (!response.ok) {
458
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
459
+ return { success: false, error: errorMessage };
460
+ }
461
+ return { success: true, status: response.status, data };
462
+ } finally {
463
+ clearTimeout(timeout);
464
+ }
465
+ } catch (err) {
466
+ const msg = err instanceof Error ? err.message : String(err);
467
+ return { success: false, error: msg };
468
+ }
469
+ }
470
+ });
471
+
472
+ // ../connectors/src/connectors/mixpanel/index.ts
473
+ var tools = { request: requestTool };
474
+ var mixpanelConnector = new ConnectorPlugin({
475
+ slug: "mixpanel",
476
+ authType: AUTH_TYPES.API_KEY,
477
+ name: "Mixpanel",
478
+ description: "Connect to Mixpanel for product analytics, event tracking, and user behavior analysis.",
479
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7ykz0vNRjMqvTVrxS8wsj1/b2cd795be5b373877dee77cc6147c758/images__3_.png",
480
+ parameters,
481
+ releaseFlag: { dev1: true, dev2: false, prod: false },
482
+ onboarding: mixpanelOnboarding,
483
+ systemPrompt: {
484
+ en: `### Tools
485
+
486
+ - \`mixpanel_request\`: The only way to call the Mixpanel REST API. Use it for exporting raw events, querying insights/funnels/retention reports, and querying user profiles. Authentication (Basic auth with Service Account username + secret) is configured automatically. The project_id query parameter is appended automatically. Provide the full URL including query parameters \u2014 the base URL varies by region and endpoint type.
487
+
488
+ ### Business Logic
489
+
490
+ The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
491
+
492
+ SDK methods (client created via \`connection(connectionId)\`):
493
+ - \`client.request(url, init?)\` \u2014 low-level authenticated fetch (provide full URL; project_id is added automatically)
494
+ - \`client.exportEvents(options)\` \u2014 export raw events (fromDate, toDate, event?, where?, limit?)
495
+ - \`client.queryInsights(bookmarkId)\` \u2014 query an Insights report by bookmark ID
496
+ - \`client.queryFunnels(options)\` \u2014 query a Funnel report (funnelId, fromDate, toDate, unit?, interval?)
497
+ - \`client.queryProfiles(options?)\` \u2014 query user/group profiles (where?, outputProperties?, page?, sessionId?)
498
+
499
+ \`\`\`ts
500
+ import type { Context } from "hono";
501
+ import { connection } from "@squadbase/vite-server/connectors/mixpanel";
502
+
503
+ const mp = connection("<connectionId>");
504
+
505
+ export default async function handler(c: Context) {
506
+ const { fromDate, toDate } = await c.req.json<{
507
+ fromDate: string;
508
+ toDate: string;
509
+ }>();
510
+
511
+ const events = await mp.exportEvents({ fromDate, toDate, limit: 1000 });
512
+
513
+ return c.json(events);
514
+ }
515
+ \`\`\`
516
+
517
+ ### Mixpanel REST API Reference
518
+
519
+ - Authentication: Basic auth (Service Account username:secret, handled automatically)
520
+ - project_id is always appended automatically to all requests
521
+
522
+ #### Base URLs by Region
523
+
524
+ | Region | Query API | Export API |
525
+ |--------|-----------|------------|
526
+ | US (default) | https://mixpanel.com/api/query | https://data.mixpanel.com/api/2.0/export |
527
+ | EU | https://eu.mixpanel.com/api/query | https://data-eu.mixpanel.com/api/2.0/export |
528
+ | India | https://in.mixpanel.com/api/query | https://data-in.mixpanel.com/api/2.0/export |
529
+
530
+ #### Export API (Raw Events)
531
+ - GET \`{exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 Export raw events (JSONL format)
532
+ - Optional: \`event\` (JSON array of event names), \`where\` (filter expression), \`limit\` (max 100,000)
533
+ - Rate limit: 60 queries/hour, 3 queries/second, 100 concurrent max
534
+
535
+ #### Query API (Reports)
536
+ - GET \`{queryBase}/insights?bookmark_id=ID\` \u2014 Insights report (requires bookmark_id from Mixpanel UI)
537
+ - GET \`{queryBase}/funnels?funnel_id=ID&from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 Funnel analysis
538
+ - GET \`{queryBase}/retention?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 Retention analysis
539
+ - POST \`{queryBase}/engage\` \u2014 Query user/group profiles (application/x-www-form-urlencoded body)
540
+ - Rate limit: 60 queries/hour, 5 concurrent max
541
+
542
+ IMPORTANT: The Insights endpoint requires a bookmark_id from an existing report in the Mixpanel UI. For ad-hoc event analysis, use the Export API to retrieve raw events and aggregate them in code. The Export API is the most flexible and universally accessible endpoint.`,
543
+ ja: `### \u30C4\u30FC\u30EB
544
+
545
+ - \`mixpanel_request\`: Mixpanel REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u751F\u30A4\u30D9\u30F3\u30C8\u306E\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u3001\u30A4\u30F3\u30B5\u30A4\u30C8/\u30D5\u30A1\u30CD\u30EB/\u30EA\u30C6\u30F3\u30B7\u30E7\u30F3\u30EC\u30DD\u30FC\u30C8\u306E\u7167\u4F1A\u3001\u30E6\u30FC\u30B6\u30FC\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u7167\u4F1A\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08Service Account\u306E\u30E6\u30FC\u30B6\u30FC\u540D+\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8\u306B\u3088\u308BBasic\u8A8D\u8A3C\uFF09\u306F\u81EA\u52D5\u7684\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002project_id\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u306F\u81EA\u52D5\u7684\u306B\u4ED8\u52A0\u3055\u308C\u307E\u3059\u3002\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u542B\u3080\u5B8C\u5168\u306AURL\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044 \u2014 \u30D9\u30FC\u30B9URL\u306F\u30EA\u30FC\u30B8\u30E7\u30F3\u3068\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u30BF\u30A4\u30D7\u306B\u3088\u3063\u3066\u7570\u306A\u308A\u307E\u3059\u3002
546
+
547
+ ### Business Logic
548
+
549
+ \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
550
+
551
+ SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
552
+ - \`client.request(url, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch\uFF08\u5B8C\u5168\u306AURL\u3092\u6307\u5B9A\u3001project_id\u306F\u81EA\u52D5\u4ED8\u52A0\uFF09
553
+ - \`client.exportEvents(options)\` \u2014 \u751F\u30A4\u30D9\u30F3\u30C8\u306E\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\uFF08fromDate, toDate, event?, where?, limit?\uFF09
554
+ - \`client.queryInsights(bookmarkId)\` \u2014 \u30D6\u30C3\u30AF\u30DE\u30FC\u30AFID\u3067Insights\u30EC\u30DD\u30FC\u30C8\u3092\u7167\u4F1A
555
+ - \`client.queryFunnels(options)\` \u2014 \u30D5\u30A1\u30CD\u30EB\u30EC\u30DD\u30FC\u30C8\u306E\u7167\u4F1A\uFF08funnelId, fromDate, toDate, unit?, interval?\uFF09
556
+ - \`client.queryProfiles(options?)\` \u2014 \u30E6\u30FC\u30B6\u30FC/\u30B0\u30EB\u30FC\u30D7\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u7167\u4F1A\uFF08where?, outputProperties?, page?, sessionId?\uFF09
557
+
558
+ \`\`\`ts
559
+ import type { Context } from "hono";
560
+ import { connection } from "@squadbase/vite-server/connectors/mixpanel";
561
+
562
+ const mp = connection("<connectionId>");
563
+
564
+ export default async function handler(c: Context) {
565
+ const { fromDate, toDate } = await c.req.json<{
566
+ fromDate: string;
567
+ toDate: string;
568
+ }>();
569
+
570
+ const events = await mp.exportEvents({ fromDate, toDate, limit: 1000 });
571
+
572
+ return c.json(events);
573
+ }
574
+ \`\`\`
575
+
576
+ ### Mixpanel REST API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
577
+
578
+ - \u8A8D\u8A3C: Basic\u8A8D\u8A3C\uFF08Service Account\u306E\u30E6\u30FC\u30B6\u30FC\u540D:\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8\u3001\u81EA\u52D5\u8A2D\u5B9A\uFF09
579
+ - project_id\u306F\u3059\u3079\u3066\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u306B\u81EA\u52D5\u7684\u306B\u4ED8\u52A0\u3055\u308C\u307E\u3059
580
+
581
+ #### \u30EA\u30FC\u30B8\u30E7\u30F3\u5225\u30D9\u30FC\u30B9URL
582
+
583
+ | \u30EA\u30FC\u30B8\u30E7\u30F3 | Query API | Export API |
584
+ |-----------|-----------|------------|
585
+ | US\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\uFF09 | https://mixpanel.com/api/query | https://data.mixpanel.com/api/2.0/export |
586
+ | EU | https://eu.mixpanel.com/api/query | https://data-eu.mixpanel.com/api/2.0/export |
587
+ | India | https://in.mixpanel.com/api/query | https://data-in.mixpanel.com/api/2.0/export |
588
+
589
+ #### Export API\uFF08\u751F\u30A4\u30D9\u30F3\u30C8\uFF09
590
+ - GET \`{exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 \u751F\u30A4\u30D9\u30F3\u30C8\u306E\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\uFF08JSONL\u5F62\u5F0F\uFF09
591
+ - \u30AA\u30D7\u30B7\u30E7\u30F3: \`event\`\uFF08\u30A4\u30D9\u30F3\u30C8\u540D\u306EJSON\u914D\u5217\uFF09\u3001\`where\`\uFF08\u30D5\u30A3\u30EB\u30BF\u5F0F\uFF09\u3001\`limit\`\uFF08\u6700\u5927100,000\uFF09
592
+ - \u30EC\u30FC\u30C8\u5236\u9650: 60\u30AF\u30A8\u30EA/\u6642\u30013\u30AF\u30A8\u30EA/\u79D2\u3001\u6700\u5927100\u540C\u6642\u63A5\u7D9A
593
+
594
+ #### Query API\uFF08\u30EC\u30DD\u30FC\u30C8\uFF09
595
+ - GET \`{queryBase}/insights?bookmark_id=ID\` \u2014 Insights\u30EC\u30DD\u30FC\u30C8\uFF08Mixpanel UI\u304B\u3089\u306Ebookmark_id\u304C\u5FC5\u8981\uFF09
596
+ - GET \`{queryBase}/funnels?funnel_id=ID&from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 \u30D5\u30A1\u30CD\u30EB\u5206\u6790
597
+ - GET \`{queryBase}/retention?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 \u30EA\u30C6\u30F3\u30B7\u30E7\u30F3\u5206\u6790
598
+ - POST \`{queryBase}/engage\` \u2014 \u30E6\u30FC\u30B6\u30FC/\u30B0\u30EB\u30FC\u30D7\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u7167\u4F1A\uFF08application/x-www-form-urlencoded\u306Ebody\uFF09
599
+ - \u30EC\u30FC\u30C8\u5236\u9650: 60\u30AF\u30A8\u30EA/\u6642\u3001\u6700\u59275\u540C\u6642\u63A5\u7D9A
600
+
601
+ \u91CD\u8981: Insights\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306F\u3001Mixpanel UI\u306E\u65E2\u5B58\u30EC\u30DD\u30FC\u30C8\u306Ebookmark_id\u304C\u5FC5\u8981\u3067\u3059\u3002\u30A2\u30C9\u30DB\u30C3\u30AF\u306A\u30A4\u30D9\u30F3\u30C8\u5206\u6790\u306B\u306F\u3001Export API\u3067\u751F\u30A4\u30D9\u30F3\u30C8\u3092\u53D6\u5F97\u3057\u3001\u30B3\u30FC\u30C9\u4E0A\u3067\u96C6\u8A08\u3057\u3066\u304F\u3060\u3055\u3044\u3002Export API\u304C\u6700\u3082\u67D4\u8EDF\u3067\u5E83\u304F\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3067\u3059\u3002`
602
+ },
603
+ tools
604
+ });
605
+
606
+ // src/connectors/create-connector-sdk.ts
607
+ import { readFileSync } from "fs";
608
+ import path from "path";
609
+
610
+ // src/connector-client/env.ts
611
+ function resolveEnvVar(entry, key, connectionId) {
612
+ const envVarName = entry.envVars[key];
613
+ if (!envVarName) {
614
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
615
+ }
616
+ const value = process.env[envVarName];
617
+ if (!value) {
618
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
619
+ }
620
+ return value;
621
+ }
622
+ function resolveEnvVarOptional(entry, key) {
623
+ const envVarName = entry.envVars[key];
624
+ if (!envVarName) return void 0;
625
+ return process.env[envVarName] || void 0;
626
+ }
627
+
628
+ // src/connector-client/proxy-fetch.ts
629
+ import { getContext } from "hono/context-storage";
630
+ import { getCookie } from "hono/cookie";
631
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
632
+ function createSandboxProxyFetch(connectionId) {
633
+ return async (input, init) => {
634
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
635
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
636
+ if (!token || !sandboxId) {
637
+ throw new Error(
638
+ "Connection proxy is not configured. Please check your deployment settings."
639
+ );
640
+ }
641
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
642
+ const originalMethod = init?.method ?? "GET";
643
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
644
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
645
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
646
+ return fetch(proxyUrl, {
647
+ method: "POST",
648
+ headers: {
649
+ "Content-Type": "application/json",
650
+ Authorization: `Bearer ${token}`
651
+ },
652
+ body: JSON.stringify({
653
+ url: originalUrl,
654
+ method: originalMethod,
655
+ body: originalBody
656
+ })
657
+ });
658
+ };
659
+ }
660
+ function createDeployedAppProxyFetch(connectionId) {
661
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
662
+ if (!projectId) {
663
+ throw new Error(
664
+ "Connection proxy is not configured. Please check your deployment settings."
665
+ );
666
+ }
667
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
668
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
669
+ return async (input, init) => {
670
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
671
+ const originalMethod = init?.method ?? "GET";
672
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
673
+ const c = getContext();
674
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
675
+ if (!appSession) {
676
+ throw new Error(
677
+ "No authentication method available for connection proxy."
678
+ );
679
+ }
680
+ return fetch(proxyUrl, {
681
+ method: "POST",
682
+ headers: {
683
+ "Content-Type": "application/json",
684
+ Authorization: `Bearer ${appSession}`
685
+ },
686
+ body: JSON.stringify({
687
+ url: originalUrl,
688
+ method: originalMethod,
689
+ body: originalBody
690
+ })
691
+ });
692
+ };
693
+ }
694
+ function createProxyFetch(connectionId) {
695
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
696
+ return createSandboxProxyFetch(connectionId);
697
+ }
698
+ return createDeployedAppProxyFetch(connectionId);
699
+ }
700
+
701
+ // src/connectors/create-connector-sdk.ts
702
+ function loadConnectionsSync() {
703
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
704
+ try {
705
+ const raw = readFileSync(filePath, "utf-8");
706
+ return JSON.parse(raw);
707
+ } catch {
708
+ return {};
709
+ }
710
+ }
711
+ function createConnectorSdk(plugin, createClient2) {
712
+ return (connectionId) => {
713
+ const connections = loadConnectionsSync();
714
+ const entry = connections[connectionId];
715
+ if (!entry) {
716
+ throw new Error(
717
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
718
+ );
719
+ }
720
+ if (entry.connector.slug !== plugin.slug) {
721
+ throw new Error(
722
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
723
+ );
724
+ }
725
+ const params = {};
726
+ for (const param of Object.values(plugin.parameters)) {
727
+ if (param.required) {
728
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
729
+ } else {
730
+ const val = resolveEnvVarOptional(entry, param.slug);
731
+ if (val !== void 0) params[param.slug] = val;
732
+ }
733
+ }
734
+ return createClient2(params, createProxyFetch(connectionId));
735
+ };
736
+ }
737
+
738
+ // src/connectors/entries/mixpanel.ts
739
+ var connection = createConnectorSdk(mixpanelConnector, createClient);
740
+ export {
741
+ connection
742
+ };