@getlimelight/sdk 0.7.9 → 0.7.11

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.9" : "test-version";
295
+ var SDK_VERSION = true ? "0.7.11" : "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) => {
@@ -558,6 +561,7 @@ var formatRequestName = (url) => {
558
561
 
559
562
  // src/helpers/utils/environment.ts
560
563
  var hasDOM = () => typeof window !== "undefined" && typeof document !== "undefined";
564
+ var isServer = () => !hasDOM() && typeof process !== "undefined" && typeof process.versions !== "undefined" && typeof process.versions.node !== "undefined";
561
565
 
562
566
  // src/helpers/render/generateRenderId.ts
563
567
  var counter = 0;
@@ -2946,6 +2950,119 @@ var createWithLimelight = (sendMessage, getSessionId, getConfig, options) => {
2946
2950
  };
2947
2951
  };
2948
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("session_started");
3047
+ },
3048
+ sessionEnded(durationSeconds, eventCount) {
3049
+ capture("session_ended", {
3050
+ duration_seconds: durationSeconds,
3051
+ event_count: eventCount
3052
+ });
3053
+ },
3054
+ timelineGenerated(eventsCorrelated) {
3055
+ capture("timeline_generated", {
3056
+ events_correlated: eventsCorrelated,
3057
+ framework
3058
+ });
3059
+ },
3060
+ shutdown() {
3061
+ enabled = false;
3062
+ anonymousId = null;
3063
+ }
3064
+ };
3065
+
2949
3066
  // src/limelight/LimelightClient.ts
2950
3067
  var LimelightClient = class {
2951
3068
  ws = null;
@@ -2957,6 +3074,8 @@ var LimelightClient = class {
2957
3074
  reconnectTimer = null;
2958
3075
  messageQueue = [];
2959
3076
  maxQueueSize = 100;
3077
+ sessionStartTime = 0;
3078
+ sessionEventCount = 0;
2960
3079
  networkInterceptor;
2961
3080
  xhrInterceptor;
2962
3081
  httpInterceptor;
@@ -3039,17 +3158,17 @@ var LimelightClient = class {
3039
3158
  if (typeof XMLHttpRequest !== "undefined") {
3040
3159
  this.xhrInterceptor.setup(this.config);
3041
3160
  }
3042
- if (!hasDOM()) {
3161
+ if (isServer()) {
3043
3162
  this.httpInterceptor.setup(this.config);
3044
3163
  }
3045
3164
  }
3046
3165
  if (this.config.enableConsole) {
3047
3166
  this.consoleInterceptor.setup(this.config);
3048
- if (!hasDOM()) {
3167
+ if (isServer()) {
3049
3168
  this.errorInterceptor.setup(this.config);
3050
3169
  }
3051
3170
  }
3052
- if (this.config.enableRenderInspector && hasDOM()) {
3171
+ if (this.config.enableRenderInspector && !isServer()) {
3053
3172
  this.renderInterceptor.setup(this.config);
3054
3173
  }
3055
3174
  if (this.config.stores && this.config.enableStateInspector) {
@@ -3060,6 +3179,7 @@ var LimelightClient = class {
3060
3179
  console.error("[Limelight] Failed to setup interceptors:", error);
3061
3180
  }
3062
3181
  }
3182
+ telemetry.init(this.config.telemetry ?? true);
3063
3183
  }
3064
3184
  /**
3065
3185
  * Establishes a WebSocket connection to the Limelight server.
@@ -3124,8 +3244,11 @@ var LimelightClient = class {
3124
3244
  };
3125
3245
  this.ws.onopen = () => {
3126
3246
  this.reconnectAttempts = 0;
3247
+ this.sessionStartTime = Date.now();
3248
+ this.sessionEventCount = 0;
3127
3249
  this.flushMessageQueue();
3128
3250
  this.sendMessage(message);
3251
+ telemetry.sessionStarted();
3129
3252
  };
3130
3253
  this.ws.onmessage = (event) => {
3131
3254
  try {
@@ -3208,6 +3331,12 @@ var LimelightClient = class {
3208
3331
  * @returns {void}
3209
3332
  */
3210
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
+ }
3211
3340
  if (this.ws?.readyState === 1) {
3212
3341
  this.flushMessageQueue();
3213
3342
  if (this.ws?.readyState === 1) {
@@ -3273,7 +3402,15 @@ var LimelightClient = class {
3273
3402
  this.renderInterceptor.cleanup();
3274
3403
  this.stateInterceptor.cleanup();
3275
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
+ }
3276
3411
  this.reconnectAttempts = 0;
3412
+ this.sessionStartTime = 0;
3413
+ this.sessionEventCount = 0;
3277
3414
  this.messageQueue = [];
3278
3415
  }
3279
3416
  /**
@@ -3284,6 +3421,7 @@ var LimelightClient = class {
3284
3421
  */
3285
3422
  reset() {
3286
3423
  this.disconnect();
3424
+ telemetry.shutdown();
3287
3425
  this.config = null;
3288
3426
  this.sessionId = "";
3289
3427
  }