@getlimelight/sdk 0.7.10 → 0.7.12

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/README.md CHANGED
@@ -137,6 +137,25 @@ Client (fetch) → Your Server (middleware) → Downstream Service (http interce
137
137
 
138
138
  No additional setup is required — just connect the SDK and add the middleware.
139
139
 
140
+ ## Telemetry
141
+
142
+ The SDK collects **anonymous** usage telemetry to help us understand adoption and improve the product. **No runtime data, user code, network request bodies, state values, or anything from your app is ever sent.** Only:
143
+
144
+ - Framework type (react, react-native, next, node)
145
+ - SDK version
146
+ - Event counts and session durations
147
+ - A random anonymous device ID (not tied to any user identity)
148
+
149
+ Telemetry is **enabled by default**. To opt out:
150
+
151
+ ```typescript
152
+ Limelight.connect({
153
+ telemetry: false,
154
+ });
155
+ ```
156
+
157
+ When `telemetry` is `false`, no events are sent and no anonymous ID is generated or stored.
158
+
140
159
  ## Learn More
141
160
 
142
161
  - [Quick Start Guide](https://docs.getlimelight.io/quickstart)
package/dist/index.d.mts CHANGED
@@ -461,6 +461,12 @@ interface LimelightConfig {
461
461
  * Limelight.connect({ webSocketImpl: WebSocket });
462
462
  */
463
463
  webSocketImpl?: new (url: string, protocols?: string | string[]) => WebSocket;
464
+ /**
465
+ * Enable anonymous usage telemetry. Only sends event counts, durations,
466
+ * and framework type — never any runtime data, user code, or request content.
467
+ * @default true
468
+ */
469
+ telemetry?: boolean;
464
470
  }
465
471
  /**
466
472
  * Represents a connection or disconnection event in the Limelight SDK.
@@ -571,6 +577,8 @@ declare class LimelightClient {
571
577
  private reconnectTimer;
572
578
  private messageQueue;
573
579
  private maxQueueSize;
580
+ private sessionStartTime;
581
+ private sessionEventCount;
574
582
  private networkInterceptor;
575
583
  private xhrInterceptor;
576
584
  private httpInterceptor;
package/dist/index.d.ts CHANGED
@@ -461,6 +461,12 @@ interface LimelightConfig {
461
461
  * Limelight.connect({ webSocketImpl: WebSocket });
462
462
  */
463
463
  webSocketImpl?: new (url: string, protocols?: string | string[]) => WebSocket;
464
+ /**
465
+ * Enable anonymous usage telemetry. Only sends event counts, durations,
466
+ * and framework type — never any runtime data, user code, or request content.
467
+ * @default true
468
+ */
469
+ telemetry?: boolean;
464
470
  }
465
471
  /**
466
472
  * Represents a connection or disconnection event in the Limelight SDK.
@@ -571,6 +577,8 @@ declare class LimelightClient {
571
577
  private reconnectTimer;
572
578
  private messageQueue;
573
579
  private maxQueueSize;
580
+ private sessionStartTime;
581
+ private sessionEventCount;
574
582
  private networkInterceptor;
575
583
  private xhrInterceptor;
576
584
  private httpInterceptor;
package/dist/index.js CHANGED
@@ -292,7 +292,7 @@ var LIMELIGHT_WEB_WSS_URL = "wss://api.getlimelight.io";
292
292
  var LIMELIGHT_DESKTOP_WSS_URL = "ws://localhost:8484";
293
293
  var LIMELIGHT_MCP_WS_URL = "ws://localhost:9229";
294
294
  var WS_PATH = "/limelight";
295
- var SDK_VERSION = true ? "0.7.10" : "test-version";
295
+ var SDK_VERSION = true ? "0.7.12" : "test-version";
296
296
  var RENDER_THRESHOLDS = {
297
297
  HOT_VELOCITY: 5,
298
298
  HIGH_RENDER_COUNT: 50,
@@ -316,6 +316,9 @@ var BINARY_CONTENT_TYPES = [
316
316
  "application/gzip"
317
317
  ];
318
318
  var MAX_BODY_SIZE = 1024 * 1024;
319
+ var POSTHOG_API_KEY = true ? "phc_I4dF0Du3McRPRCdSiJfvTBMWrbsapXoTP83uaHTA1ry" : "";
320
+ var POSTHOG_HOST = true ? "https://us.i.posthog.com" : "";
321
+ var STORAGE_KEY = "limelight_anon_id";
319
322
 
320
323
  // src/helpers/safety/redactSensitiveHeaders.ts
321
324
  var redactSensitiveHeaders = (headers) => {
@@ -2947,6 +2950,119 @@ var createWithLimelight = (sendMessage, getSessionId, getConfig, options) => {
2947
2950
  };
2948
2951
  };
2949
2952
 
2953
+ // src/limelight/telemetry.ts
2954
+ var anonymousId = null;
2955
+ var enabled = false;
2956
+ var framework = "unknown";
2957
+ var detectFramework = () => {
2958
+ try {
2959
+ if (typeof navigator !== "undefined" && "ReactNative" in navigator)
2960
+ return "react-native";
2961
+ if (typeof process !== "undefined" && (process.env?.__NEXT_RUNTIME__ || process.env?.NEXT_RUNTIME))
2962
+ return "next";
2963
+ if (typeof window !== "undefined" && window.__NEXT_DATA__)
2964
+ return "next";
2965
+ if (hasDOM()) return "react";
2966
+ if (isServer()) return "node";
2967
+ } catch {
2968
+ }
2969
+ return "unknown";
2970
+ };
2971
+ var generateId = () => {
2972
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
2973
+ const r = Math.random() * 16 | 0;
2974
+ return (c === "x" ? r : r & 3 | 8).toString(16);
2975
+ });
2976
+ };
2977
+ var getOrCreateAnonymousId = () => {
2978
+ if (anonymousId) return anonymousId;
2979
+ try {
2980
+ if (typeof localStorage !== "undefined") {
2981
+ const stored = localStorage.getItem(STORAGE_KEY);
2982
+ if (stored) {
2983
+ anonymousId = stored;
2984
+ return anonymousId;
2985
+ }
2986
+ anonymousId = generateId();
2987
+ localStorage.setItem(STORAGE_KEY, anonymousId);
2988
+ return anonymousId;
2989
+ }
2990
+ } catch {
2991
+ }
2992
+ try {
2993
+ const _require = globalThis["require"];
2994
+ if (_require) {
2995
+ const fs = _require("fs");
2996
+ const os = _require("os");
2997
+ const path = _require("path");
2998
+ const filePath = path.join(os.homedir(), ".limelight_telemetry_id");
2999
+ try {
3000
+ anonymousId = fs.readFileSync(filePath, "utf-8").trim();
3001
+ if (anonymousId) return anonymousId;
3002
+ } catch {
3003
+ }
3004
+ anonymousId = generateId();
3005
+ fs.writeFileSync(filePath, anonymousId, "utf-8");
3006
+ return anonymousId;
3007
+ }
3008
+ } catch {
3009
+ }
3010
+ anonymousId = generateId();
3011
+ return anonymousId;
3012
+ };
3013
+ var capture = (event, properties = {}) => {
3014
+ if (!enabled) return;
3015
+ try {
3016
+ const payload = {
3017
+ api_key: POSTHOG_API_KEY,
3018
+ event,
3019
+ distinct_id: getOrCreateAnonymousId(),
3020
+ properties: {
3021
+ ...properties,
3022
+ sdk_version: SDK_VERSION,
3023
+ framework
3024
+ }
3025
+ };
3026
+ fetch(`${POSTHOG_HOST}/capture/`, {
3027
+ method: "POST",
3028
+ headers: { "Content-Type": "application/json" },
3029
+ body: JSON.stringify(payload)
3030
+ }).catch(() => {
3031
+ });
3032
+ } catch {
3033
+ }
3034
+ };
3035
+ var telemetry = {
3036
+ init(telemetryEnabled) {
3037
+ enabled = telemetryEnabled;
3038
+ if (!enabled) return;
3039
+ framework = detectFramework();
3040
+ capture("sdk_initialized", {
3041
+ framework,
3042
+ device_id: getOrCreateAnonymousId()
3043
+ });
3044
+ },
3045
+ sessionStarted() {
3046
+ capture("sdk_session_started");
3047
+ },
3048
+ sessionEnded(durationSeconds, eventCount) {
3049
+ capture("sdk_session_ended", {
3050
+ duration_seconds: durationSeconds,
3051
+ event_count: eventCount
3052
+ });
3053
+ },
3054
+ timelineGenerated(eventsCorrelated) {
3055
+ capture("sdk_timeline_generated", {
3056
+ events_correlated: eventsCorrelated,
3057
+ framework
3058
+ });
3059
+ },
3060
+ shutdown() {
3061
+ enabled = false;
3062
+ anonymousId = null;
3063
+ }
3064
+ };
3065
+
2950
3066
  // src/limelight/LimelightClient.ts
2951
3067
  var LimelightClient = class {
2952
3068
  ws = null;
@@ -2958,6 +3074,8 @@ var LimelightClient = class {
2958
3074
  reconnectTimer = null;
2959
3075
  messageQueue = [];
2960
3076
  maxQueueSize = 100;
3077
+ sessionStartTime = 0;
3078
+ sessionEventCount = 0;
2961
3079
  networkInterceptor;
2962
3080
  xhrInterceptor;
2963
3081
  httpInterceptor;
@@ -3061,6 +3179,7 @@ var LimelightClient = class {
3061
3179
  console.error("[Limelight] Failed to setup interceptors:", error);
3062
3180
  }
3063
3181
  }
3182
+ telemetry.init(this.config.telemetry ?? true);
3064
3183
  }
3065
3184
  /**
3066
3185
  * Establishes a WebSocket connection to the Limelight server.
@@ -3125,8 +3244,11 @@ var LimelightClient = class {
3125
3244
  };
3126
3245
  this.ws.onopen = () => {
3127
3246
  this.reconnectAttempts = 0;
3247
+ this.sessionStartTime = Date.now();
3248
+ this.sessionEventCount = 0;
3128
3249
  this.flushMessageQueue();
3129
3250
  this.sendMessage(message);
3251
+ telemetry.sessionStarted();
3130
3252
  };
3131
3253
  this.ws.onmessage = (event) => {
3132
3254
  try {
@@ -3209,6 +3331,12 @@ var LimelightClient = class {
3209
3331
  * @returns {void}
3210
3332
  */
3211
3333
  sendMessage(message) {
3334
+ this.sessionEventCount++;
3335
+ if ("phase" in message && message.phase === "RENDER_SNAPSHOT" && "profiles" in message) {
3336
+ telemetry.timelineGenerated(
3337
+ message.profiles?.length ?? 0
3338
+ );
3339
+ }
3212
3340
  if (this.ws?.readyState === 1) {
3213
3341
  this.flushMessageQueue();
3214
3342
  if (this.ws?.readyState === 1) {
@@ -3274,7 +3402,15 @@ var LimelightClient = class {
3274
3402
  this.renderInterceptor.cleanup();
3275
3403
  this.stateInterceptor.cleanup();
3276
3404
  this.requestBridge.cleanup();
3405
+ if (this.sessionStartTime > 0) {
3406
+ const durationSeconds = Math.round(
3407
+ (Date.now() - this.sessionStartTime) / 1e3
3408
+ );
3409
+ telemetry.sessionEnded(durationSeconds, this.sessionEventCount);
3410
+ }
3277
3411
  this.reconnectAttempts = 0;
3412
+ this.sessionStartTime = 0;
3413
+ this.sessionEventCount = 0;
3278
3414
  this.messageQueue = [];
3279
3415
  }
3280
3416
  /**
@@ -3285,6 +3421,7 @@ var LimelightClient = class {
3285
3421
  */
3286
3422
  reset() {
3287
3423
  this.disconnect();
3424
+ telemetry.shutdown();
3288
3425
  this.config = null;
3289
3426
  this.sessionId = "";
3290
3427
  }