@omnikit-ai/sdk 2.0.9 → 2.2.0
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/index.d.mts +333 -6
- package/dist/index.d.ts +333 -6
- package/dist/index.js +467 -55
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +464 -56
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -435,6 +435,53 @@ var LiveVoiceSessionImpl = class {
|
|
|
435
435
|
}
|
|
436
436
|
};
|
|
437
437
|
|
|
438
|
+
// src/connectors.ts
|
|
439
|
+
function createConnectorsModule(makeRequest, appId, baseUrl, getServiceToken) {
|
|
440
|
+
return {
|
|
441
|
+
async getAccessToken(connectorType) {
|
|
442
|
+
const serviceToken = getServiceToken();
|
|
443
|
+
if (!serviceToken) {
|
|
444
|
+
throw new Error(
|
|
445
|
+
"Service token is required to get connector access token. This method is only available in backend functions. Make sure you created the client with a serviceToken."
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
return makeRequest(
|
|
449
|
+
`${baseUrl}/apps/${appId}/connectors/${connectorType}/access-token`,
|
|
450
|
+
"GET",
|
|
451
|
+
null,
|
|
452
|
+
{ useServiceToken: true }
|
|
453
|
+
);
|
|
454
|
+
},
|
|
455
|
+
async isConnected(connectorType) {
|
|
456
|
+
try {
|
|
457
|
+
const response = await makeRequest(
|
|
458
|
+
`${baseUrl}/apps/${appId}/connectors/${connectorType}`,
|
|
459
|
+
"GET"
|
|
460
|
+
);
|
|
461
|
+
return response?.connector?.status === "connected";
|
|
462
|
+
} catch {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
async getStatus(connectorType) {
|
|
467
|
+
try {
|
|
468
|
+
const response = await makeRequest(
|
|
469
|
+
`${baseUrl}/apps/${appId}/connectors/${connectorType}`,
|
|
470
|
+
"GET"
|
|
471
|
+
);
|
|
472
|
+
return {
|
|
473
|
+
success: true,
|
|
474
|
+
connector: response?.connector
|
|
475
|
+
};
|
|
476
|
+
} catch (error) {
|
|
477
|
+
return {
|
|
478
|
+
success: false
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
438
485
|
// src/client.ts
|
|
439
486
|
var LLM_MODEL_MAP = {
|
|
440
487
|
// Gemini 2.5 models
|
|
@@ -529,10 +576,96 @@ var APIClient = class {
|
|
|
529
576
|
this.userToken = detectedToken;
|
|
530
577
|
}
|
|
531
578
|
}
|
|
579
|
+
this.initializeBuiltInServices();
|
|
532
580
|
this.ensureInitialized().catch((err) => {
|
|
533
581
|
console.warn("[Omnikit SDK] Background initialization failed:", err);
|
|
534
582
|
});
|
|
535
583
|
}
|
|
584
|
+
/**
|
|
585
|
+
* Initialize built-in services with their definitions
|
|
586
|
+
* These services don't require backend schema - they're defined in the SDK
|
|
587
|
+
*/
|
|
588
|
+
initializeBuiltInServices() {
|
|
589
|
+
const builtInServices = [
|
|
590
|
+
{
|
|
591
|
+
name: "SendEmail",
|
|
592
|
+
path: "/services/email",
|
|
593
|
+
description: "Send an email",
|
|
594
|
+
method: "POST",
|
|
595
|
+
params: { to: "string", subject: "string", body: "string" }
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
name: "InvokeLLM",
|
|
599
|
+
path: "/services/llm",
|
|
600
|
+
description: "Invoke a language model",
|
|
601
|
+
method: "POST",
|
|
602
|
+
params: { messages: "array", model: "string" }
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
name: "GenerateImage",
|
|
606
|
+
path: "/services/images",
|
|
607
|
+
description: "Generate an image from a prompt",
|
|
608
|
+
method: "POST",
|
|
609
|
+
async: true,
|
|
610
|
+
params: { prompt: "string" }
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
name: "GenerateSpeech",
|
|
614
|
+
path: "/services/speech",
|
|
615
|
+
description: "Generate speech from text",
|
|
616
|
+
method: "POST",
|
|
617
|
+
async: true,
|
|
618
|
+
params: { text: "string" }
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
name: "GenerateVideo",
|
|
622
|
+
path: "/services/video",
|
|
623
|
+
description: "Generate a video from a prompt",
|
|
624
|
+
method: "POST",
|
|
625
|
+
async: true,
|
|
626
|
+
params: { prompt: "string" }
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
name: "ExtractData",
|
|
630
|
+
path: "/services/extract-text",
|
|
631
|
+
description: "Extract structured data from text or files",
|
|
632
|
+
method: "POST",
|
|
633
|
+
async: true,
|
|
634
|
+
params: { file_url: "string" }
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
name: "SendSMS",
|
|
638
|
+
path: "/services/sms",
|
|
639
|
+
description: "Send an SMS message",
|
|
640
|
+
method: "POST",
|
|
641
|
+
params: { to: "string", body: "string" }
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
name: "UploadFile",
|
|
645
|
+
path: "/services/files",
|
|
646
|
+
description: "Upload a file (public)",
|
|
647
|
+
method: "POST",
|
|
648
|
+
params: { file: "File" }
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
name: "UploadPrivateFile",
|
|
652
|
+
path: "/services/files/private",
|
|
653
|
+
description: "Upload a private file",
|
|
654
|
+
method: "POST",
|
|
655
|
+
params: { file: "File" }
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
name: "CreateFileSignedUrl",
|
|
659
|
+
path: "/services/files/signed-url",
|
|
660
|
+
description: "Create a signed URL for a private file",
|
|
661
|
+
method: "POST",
|
|
662
|
+
params: { file_uri: "string" }
|
|
663
|
+
}
|
|
664
|
+
];
|
|
665
|
+
builtInServices.forEach((service) => {
|
|
666
|
+
this._services[service.name] = this.createServiceMethod(service);
|
|
667
|
+
});
|
|
668
|
+
}
|
|
536
669
|
/**
|
|
537
670
|
* Load metadata from localStorage cache, falling back to initial config
|
|
538
671
|
* Guards localStorage access for Deno/Node compatibility
|
|
@@ -672,13 +805,13 @@ var APIClient = class {
|
|
|
672
805
|
return this.createAuthProxy();
|
|
673
806
|
}
|
|
674
807
|
/**
|
|
675
|
-
*
|
|
676
|
-
* Only available when serviceToken is provided
|
|
808
|
+
* Service-level operations (elevated privileges).
|
|
809
|
+
* Only available when serviceToken is provided (e.g., via createServerClient).
|
|
677
810
|
*/
|
|
678
|
-
get
|
|
811
|
+
get service() {
|
|
679
812
|
if (!this._serviceToken) {
|
|
680
813
|
throw new OmnikitError(
|
|
681
|
-
"Service token is required
|
|
814
|
+
"Service token is required. Use createServerClient(req) in backend functions.",
|
|
682
815
|
403,
|
|
683
816
|
"SERVICE_TOKEN_REQUIRED"
|
|
684
817
|
);
|
|
@@ -691,12 +824,51 @@ var APIClient = class {
|
|
|
691
824
|
// Service role collections
|
|
692
825
|
services: this.createServicesProxy(true),
|
|
693
826
|
// Service role services (new flat structure)
|
|
694
|
-
integrations: this.createIntegrationsProxy(true)
|
|
827
|
+
integrations: this.createIntegrationsProxy(true),
|
|
695
828
|
// Service role integrations (legacy)
|
|
829
|
+
connectors: this.connectors
|
|
830
|
+
// Connectors module (requires service token)
|
|
696
831
|
// Note: auth not available in service role for security
|
|
697
832
|
};
|
|
698
833
|
return this._asServiceRole;
|
|
699
834
|
}
|
|
835
|
+
/**
|
|
836
|
+
* @deprecated Use service instead
|
|
837
|
+
*/
|
|
838
|
+
get asServiceRole() {
|
|
839
|
+
return this.service;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Lazy getter for connectors module
|
|
843
|
+
* Like Base44's connectors - provides access to external service tokens
|
|
844
|
+
*
|
|
845
|
+
* SECURITY: getAccessToken requires service token authentication.
|
|
846
|
+
* Only use in backend functions, not frontend code.
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```typescript
|
|
850
|
+
* // In a backend function
|
|
851
|
+
* const { access_token } = await omnikit.connectors.getAccessToken('slack');
|
|
852
|
+
*
|
|
853
|
+
* // Make direct Slack API call
|
|
854
|
+
* const response = await fetch('https://slack.com/api/chat.postMessage', {
|
|
855
|
+
* method: 'POST',
|
|
856
|
+
* headers: { Authorization: `Bearer ${access_token}` },
|
|
857
|
+
* body: JSON.stringify({ channel: '#general', text: 'Hello!' })
|
|
858
|
+
* });
|
|
859
|
+
* ```
|
|
860
|
+
*/
|
|
861
|
+
get connectors() {
|
|
862
|
+
if (!this._connectors) {
|
|
863
|
+
this._connectors = createConnectorsModule(
|
|
864
|
+
this.makeRequest.bind(this),
|
|
865
|
+
this.appId,
|
|
866
|
+
this.baseUrl,
|
|
867
|
+
() => this._serviceToken
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
return this._connectors;
|
|
871
|
+
}
|
|
700
872
|
/**
|
|
701
873
|
* Create auth proxy that auto-initializes
|
|
702
874
|
*/
|
|
@@ -1191,57 +1363,11 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
1191
1363
|
if (method) {
|
|
1192
1364
|
response = await method(params, useServiceToken);
|
|
1193
1365
|
} else {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
"
|
|
1198
|
-
"GenerateSpeech": "services/speech",
|
|
1199
|
-
"GenerateVideo": "services/video",
|
|
1200
|
-
"ExtractData": "services/extract-text",
|
|
1201
|
-
"SendSMS": "services/sms",
|
|
1202
|
-
"UploadFile": "services/files",
|
|
1203
|
-
"UploadPrivateFile": "services/files/private/init",
|
|
1204
|
-
"CreateFileSignedUrl": "services/files/signed-url"
|
|
1205
|
-
};
|
|
1206
|
-
const servicePath = servicePathMap[normalizedName];
|
|
1207
|
-
if (!servicePath) {
|
|
1208
|
-
throw new OmnikitError(
|
|
1209
|
-
`Service '${serviceName}' not found. Known services: ${Object.keys(servicePathMap).join(", ")} (camelCase also supported)`,
|
|
1210
|
-
404,
|
|
1211
|
-
"SERVICE_NOT_FOUND"
|
|
1212
|
-
);
|
|
1213
|
-
}
|
|
1214
|
-
const headers = {
|
|
1215
|
-
"Content-Type": "application/json"
|
|
1216
|
-
};
|
|
1217
|
-
if (client._apiKey) {
|
|
1218
|
-
headers["X-API-Key"] = client._apiKey;
|
|
1219
|
-
} else if (client.userToken) {
|
|
1220
|
-
headers["Authorization"] = `Bearer ${client.userToken}`;
|
|
1221
|
-
}
|
|
1222
|
-
if ((normalizedName === "UploadFile" || normalizedName === "uploadFile") && params?.file instanceof File) {
|
|
1223
|
-
return client.handleFileUpload(params.file, servicePath, useServiceToken);
|
|
1224
|
-
}
|
|
1225
|
-
if ((normalizedName === "UploadPrivateFile" || normalizedName === "uploadPrivateFile") && params?.file instanceof File) {
|
|
1226
|
-
return client.handleFileUpload(params.file, servicePath, useServiceToken);
|
|
1227
|
-
}
|
|
1228
|
-
const fetchResponse = await fetch(
|
|
1229
|
-
`${client.baseUrl}/apps/${client.appId}/${servicePath}`,
|
|
1230
|
-
{
|
|
1231
|
-
method: "POST",
|
|
1232
|
-
headers,
|
|
1233
|
-
body: JSON.stringify(params || {})
|
|
1234
|
-
}
|
|
1366
|
+
throw new OmnikitError(
|
|
1367
|
+
`Service '${serviceName}' not found. Known services: SendEmail, InvokeLLM, GenerateImage, GenerateSpeech, GenerateVideo, ExtractData, SendSMS, UploadFile, UploadPrivateFile, CreateFileSignedUrl (camelCase also supported)`,
|
|
1368
|
+
404,
|
|
1369
|
+
"SERVICE_NOT_FOUND"
|
|
1235
1370
|
);
|
|
1236
|
-
if (!fetchResponse.ok) {
|
|
1237
|
-
const error = await fetchResponse.json().catch(() => ({}));
|
|
1238
|
-
throw new OmnikitError(
|
|
1239
|
-
error.detail || `Service call failed: ${fetchResponse.statusText}`,
|
|
1240
|
-
fetchResponse.status,
|
|
1241
|
-
"SERVICE_CALL_FAILED"
|
|
1242
|
-
);
|
|
1243
|
-
}
|
|
1244
|
-
response = await fetchResponse.json();
|
|
1245
1371
|
}
|
|
1246
1372
|
if (response && response.async && response.job_id) {
|
|
1247
1373
|
if (asyncOptions?.returnJobId) {
|
|
@@ -2196,12 +2322,298 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
|
|
|
2196
2322
|
function createClient(config) {
|
|
2197
2323
|
return new APIClient(config);
|
|
2198
2324
|
}
|
|
2325
|
+
function createServerClient(request) {
|
|
2326
|
+
const getHeader = (name) => {
|
|
2327
|
+
if (typeof request.headers.get === "function") {
|
|
2328
|
+
return request.headers.get(name);
|
|
2329
|
+
}
|
|
2330
|
+
return request.headers[name] || request.headers[name.toLowerCase()] || null;
|
|
2331
|
+
};
|
|
2332
|
+
const authHeader = getHeader("Authorization") || getHeader("authorization");
|
|
2333
|
+
const userToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
|
|
2334
|
+
const serviceToken = getHeader("X-Omnikit-Service-Authorization") || getHeader("x-omnikit-service-authorization");
|
|
2335
|
+
const appId = getHeader("X-Omnikit-App-Id") || getHeader("x-omnikit-app-id");
|
|
2336
|
+
const serverUrl = getHeader("X-Omnikit-Server-Url") || getHeader("x-omnikit-server-url") || "https://omnikit.ai/api";
|
|
2337
|
+
if (!appId) {
|
|
2338
|
+
throw new OmnikitError(
|
|
2339
|
+
"X-Omnikit-App-Id header is required. This function should be invoked through the Omnikit platform.",
|
|
2340
|
+
400,
|
|
2341
|
+
"MISSING_APP_ID"
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
return createClient({
|
|
2345
|
+
appId,
|
|
2346
|
+
serverUrl,
|
|
2347
|
+
token: userToken || void 0,
|
|
2348
|
+
serviceToken: serviceToken || void 0,
|
|
2349
|
+
autoInitAuth: false
|
|
2350
|
+
// Don't auto-detect from localStorage in backend
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
var createClientFromRequest = createServerClient;
|
|
2354
|
+
|
|
2355
|
+
// src/analytics.ts
|
|
2356
|
+
var STORAGE_KEY = "omnikit_session";
|
|
2357
|
+
var SESSION_TIMEOUT = 30 * 60 * 1e3;
|
|
2358
|
+
var FLUSH_INTERVAL = 5e3;
|
|
2359
|
+
var MAX_RETRIES = 3;
|
|
2360
|
+
var Analytics = class {
|
|
2361
|
+
constructor(config) {
|
|
2362
|
+
this.eventQueue = [];
|
|
2363
|
+
this.config = config;
|
|
2364
|
+
this.enabled = config.enabled !== false;
|
|
2365
|
+
this.sessionId = config.sessionId || this.initSession();
|
|
2366
|
+
this.userId = config.userId;
|
|
2367
|
+
if (this.enabled && typeof window !== "undefined") {
|
|
2368
|
+
this.startFlushTimer();
|
|
2369
|
+
window.addEventListener("beforeunload", () => this.flush());
|
|
2370
|
+
window.addEventListener("pagehide", () => this.flush());
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
// ==========================================================================
|
|
2374
|
+
// Session Management
|
|
2375
|
+
// ==========================================================================
|
|
2376
|
+
initSession() {
|
|
2377
|
+
if (typeof window === "undefined" || typeof localStorage === "undefined") {
|
|
2378
|
+
return this.generateId();
|
|
2379
|
+
}
|
|
2380
|
+
try {
|
|
2381
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
2382
|
+
if (stored) {
|
|
2383
|
+
const { id, timestamp } = JSON.parse(stored);
|
|
2384
|
+
if (Date.now() - timestamp < SESSION_TIMEOUT) {
|
|
2385
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
2386
|
+
id,
|
|
2387
|
+
timestamp: Date.now()
|
|
2388
|
+
}));
|
|
2389
|
+
return id;
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
} catch {
|
|
2393
|
+
}
|
|
2394
|
+
const newId = this.generateId();
|
|
2395
|
+
this.saveSession(newId);
|
|
2396
|
+
return newId;
|
|
2397
|
+
}
|
|
2398
|
+
generateId() {
|
|
2399
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
2400
|
+
return crypto.randomUUID();
|
|
2401
|
+
}
|
|
2402
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
2403
|
+
const r = Math.random() * 16 | 0;
|
|
2404
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
2405
|
+
return v.toString(16);
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
2408
|
+
saveSession(id) {
|
|
2409
|
+
if (typeof localStorage === "undefined") return;
|
|
2410
|
+
try {
|
|
2411
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
2412
|
+
id,
|
|
2413
|
+
timestamp: Date.now()
|
|
2414
|
+
}));
|
|
2415
|
+
} catch {
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
/**
|
|
2419
|
+
* Get the current session ID
|
|
2420
|
+
*/
|
|
2421
|
+
getSessionId() {
|
|
2422
|
+
return this.sessionId;
|
|
2423
|
+
}
|
|
2424
|
+
/**
|
|
2425
|
+
* Start a new session (e.g., after logout)
|
|
2426
|
+
*/
|
|
2427
|
+
startNewSession() {
|
|
2428
|
+
this.sessionId = this.generateId();
|
|
2429
|
+
this.saveSession(this.sessionId);
|
|
2430
|
+
return this.sessionId;
|
|
2431
|
+
}
|
|
2432
|
+
// ==========================================================================
|
|
2433
|
+
// User Identification
|
|
2434
|
+
// ==========================================================================
|
|
2435
|
+
/**
|
|
2436
|
+
* Associate events with a user ID
|
|
2437
|
+
*/
|
|
2438
|
+
setUserId(userId) {
|
|
2439
|
+
this.userId = userId;
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Clear user association (e.g., on logout)
|
|
2443
|
+
*/
|
|
2444
|
+
clearUserId() {
|
|
2445
|
+
this.userId = void 0;
|
|
2446
|
+
}
|
|
2447
|
+
// ==========================================================================
|
|
2448
|
+
// Core Logging Methods
|
|
2449
|
+
// ==========================================================================
|
|
2450
|
+
/**
|
|
2451
|
+
* Log a custom event
|
|
2452
|
+
*/
|
|
2453
|
+
async logEvent(eventType, payload = {}) {
|
|
2454
|
+
if (!this.enabled) return;
|
|
2455
|
+
this.eventQueue.push({
|
|
2456
|
+
eventType,
|
|
2457
|
+
payload,
|
|
2458
|
+
timestamp: Date.now()
|
|
2459
|
+
});
|
|
2460
|
+
if (eventType === "error" || eventType === "api_error") {
|
|
2461
|
+
await this.flush();
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2465
|
+
* Log a page view event
|
|
2466
|
+
*/
|
|
2467
|
+
async logPageView(pageName, metadata) {
|
|
2468
|
+
if (!this.enabled) return;
|
|
2469
|
+
const url = typeof window !== "undefined" ? window.location.href : "";
|
|
2470
|
+
const referrer = typeof document !== "undefined" ? document.referrer : "";
|
|
2471
|
+
await this.logEvent("page_view", {
|
|
2472
|
+
page_name: pageName,
|
|
2473
|
+
metadata: {
|
|
2474
|
+
url,
|
|
2475
|
+
referrer,
|
|
2476
|
+
...metadata
|
|
2477
|
+
}
|
|
2478
|
+
});
|
|
2479
|
+
}
|
|
2480
|
+
/**
|
|
2481
|
+
* Log an error event
|
|
2482
|
+
*/
|
|
2483
|
+
async logError(error, componentStack) {
|
|
2484
|
+
if (!this.enabled) return;
|
|
2485
|
+
const url = typeof window !== "undefined" ? window.location.href : "";
|
|
2486
|
+
await this.logEvent("error", {
|
|
2487
|
+
error_message: error.message,
|
|
2488
|
+
error_stack: error.stack,
|
|
2489
|
+
metadata: {
|
|
2490
|
+
name: error.name,
|
|
2491
|
+
component_stack: componentStack,
|
|
2492
|
+
url
|
|
2493
|
+
}
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
/**
|
|
2497
|
+
* Log an API error event
|
|
2498
|
+
*/
|
|
2499
|
+
async logApiError(endpoint, statusCode, errorMessage, metadata) {
|
|
2500
|
+
if (!this.enabled) return;
|
|
2501
|
+
await this.logEvent("api_error", {
|
|
2502
|
+
error_message: errorMessage,
|
|
2503
|
+
metadata: {
|
|
2504
|
+
endpoint,
|
|
2505
|
+
status_code: statusCode,
|
|
2506
|
+
...metadata
|
|
2507
|
+
}
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
// ==========================================================================
|
|
2511
|
+
// Event Batching & Flushing
|
|
2512
|
+
// ==========================================================================
|
|
2513
|
+
startFlushTimer() {
|
|
2514
|
+
if (this.flushTimer) return;
|
|
2515
|
+
this.flushTimer = setInterval(() => {
|
|
2516
|
+
if (this.eventQueue.length > 0) {
|
|
2517
|
+
this.flush();
|
|
2518
|
+
}
|
|
2519
|
+
}, FLUSH_INTERVAL);
|
|
2520
|
+
}
|
|
2521
|
+
/**
|
|
2522
|
+
* Flush all queued events to the server
|
|
2523
|
+
*/
|
|
2524
|
+
async flush() {
|
|
2525
|
+
if (this.eventQueue.length === 0) return;
|
|
2526
|
+
const events = [...this.eventQueue];
|
|
2527
|
+
this.eventQueue = [];
|
|
2528
|
+
const sendPromises = events.map(
|
|
2529
|
+
({ eventType, payload }) => this.sendEvent(eventType, payload)
|
|
2530
|
+
);
|
|
2531
|
+
await Promise.allSettled(sendPromises);
|
|
2532
|
+
}
|
|
2533
|
+
async sendEvent(eventType, payload) {
|
|
2534
|
+
const pageName = payload.page_name || (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
2535
|
+
const url = `${this.config.apiUrl}/api/app-logs/${this.config.appId}/log-event`;
|
|
2536
|
+
const body = {
|
|
2537
|
+
session_id: this.sessionId,
|
|
2538
|
+
user_id: this.userId,
|
|
2539
|
+
event_type: eventType,
|
|
2540
|
+
page_name: pageName,
|
|
2541
|
+
action: payload.action,
|
|
2542
|
+
inputs: payload.inputs,
|
|
2543
|
+
metadata: payload.metadata,
|
|
2544
|
+
is_error: eventType === "error" || eventType === "api_error",
|
|
2545
|
+
error_message: payload.error_message,
|
|
2546
|
+
error_stack: payload.error_stack
|
|
2547
|
+
};
|
|
2548
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
2549
|
+
try {
|
|
2550
|
+
const response = await fetch(url, {
|
|
2551
|
+
method: "POST",
|
|
2552
|
+
headers: { "Content-Type": "application/json" },
|
|
2553
|
+
body: JSON.stringify(body),
|
|
2554
|
+
keepalive: true
|
|
2555
|
+
// Ensures request completes even on page unload
|
|
2556
|
+
});
|
|
2557
|
+
if (response.ok) {
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
if (response.status >= 400 && response.status < 500) {
|
|
2561
|
+
console.warn(`[Omnikit Analytics] Event rejected: ${response.status}`);
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
} catch (err) {
|
|
2565
|
+
}
|
|
2566
|
+
if (attempt < MAX_RETRIES - 1) {
|
|
2567
|
+
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
console.warn("[Omnikit Analytics] Failed to send event after retries");
|
|
2571
|
+
}
|
|
2572
|
+
// ==========================================================================
|
|
2573
|
+
// Lifecycle
|
|
2574
|
+
// ==========================================================================
|
|
2575
|
+
/**
|
|
2576
|
+
* Enable or disable analytics
|
|
2577
|
+
*/
|
|
2578
|
+
setEnabled(enabled) {
|
|
2579
|
+
this.enabled = enabled;
|
|
2580
|
+
if (enabled && typeof window !== "undefined") {
|
|
2581
|
+
this.startFlushTimer();
|
|
2582
|
+
} else if (this.flushTimer) {
|
|
2583
|
+
clearInterval(this.flushTimer);
|
|
2584
|
+
this.flushTimer = void 0;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
/**
|
|
2588
|
+
* Check if analytics is enabled
|
|
2589
|
+
*/
|
|
2590
|
+
isEnabled() {
|
|
2591
|
+
return this.enabled;
|
|
2592
|
+
}
|
|
2593
|
+
/**
|
|
2594
|
+
* Clean up resources
|
|
2595
|
+
*/
|
|
2596
|
+
destroy() {
|
|
2597
|
+
if (this.flushTimer) {
|
|
2598
|
+
clearInterval(this.flushTimer);
|
|
2599
|
+
this.flushTimer = void 0;
|
|
2600
|
+
}
|
|
2601
|
+
this.flush();
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
function createAnalytics(config) {
|
|
2605
|
+
return new Analytics(config);
|
|
2606
|
+
}
|
|
2199
2607
|
|
|
2200
2608
|
exports.APIClient = APIClient;
|
|
2609
|
+
exports.Analytics = Analytics;
|
|
2201
2610
|
exports.LiveVoiceSessionImpl = LiveVoiceSessionImpl;
|
|
2202
2611
|
exports.OmnikitError = OmnikitError;
|
|
2203
2612
|
exports.cleanTokenFromUrl = cleanTokenFromUrl;
|
|
2613
|
+
exports.createAnalytics = createAnalytics;
|
|
2204
2614
|
exports.createClient = createClient;
|
|
2615
|
+
exports.createClientFromRequest = createClientFromRequest;
|
|
2616
|
+
exports.createServerClient = createServerClient;
|
|
2205
2617
|
exports.getAccessToken = getAccessToken;
|
|
2206
2618
|
exports.isTokenInUrl = isTokenInUrl;
|
|
2207
2619
|
exports.removeAccessToken = removeAccessToken;
|