@flotrace/runtime 0.1.0 → 0.1.2

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
@@ -5,11 +5,11 @@ Runtime package for FloTrace — enables real-time React component tree visualiz
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @flotrace/runtime
8
+ npm install -D @flotrace/runtime
9
9
  # or
10
- yarn add @flotrace/runtime
10
+ yarn add -D @flotrace/runtime
11
11
  # or
12
- pnpm add @flotrace/runtime
12
+ pnpm add -D @flotrace/runtime
13
13
  ```
14
14
 
15
15
  **Peer dependencies:** React >= 16.9.0 (requires `<Profiler>` API)
package/dist/index.d.mts CHANGED
@@ -668,6 +668,11 @@ interface TrackingOptions {
668
668
  * FloTrace provider configuration
669
669
  */
670
670
  interface FloTraceConfig {
671
+ /** WebSocket server host (default: '127.0.0.1').
672
+ * React Native users: set to your Mac's LAN IP (e.g. '192.168.1.5') for
673
+ * physical devices, or '10.0.2.2' for Android emulator. iOS simulator
674
+ * can use the default '127.0.0.1'. */
675
+ host?: string;
671
676
  /** WebSocket server port (default: 3457) */
672
677
  port?: number;
673
678
  /** App name to display in FloTrace */
package/dist/index.d.ts CHANGED
@@ -668,6 +668,11 @@ interface TrackingOptions {
668
668
  * FloTrace provider configuration
669
669
  */
670
670
  interface FloTraceConfig {
671
+ /** WebSocket server host (default: '127.0.0.1').
672
+ * React Native users: set to your Mac's LAN IP (e.g. '192.168.1.5') for
673
+ * physical devices, or '10.0.2.2' for Android emulator. iOS simulator
674
+ * can use the default '127.0.0.1'. */
675
+ host?: string;
671
676
  /** WebSocket server port (default: 3457) */
672
677
  port?: number;
673
678
  /** App name to display in FloTrace */
package/dist/index.js CHANGED
@@ -73,6 +73,7 @@ var import_react = __toESM(require("react"));
73
73
 
74
74
  // src/types.ts
75
75
  var DEFAULT_CONFIG = {
76
+ host: "127.0.0.1",
76
77
  port: 3457,
77
78
  appName: "React App",
78
79
  enabled: process.env.NODE_ENV === "development",
@@ -112,25 +113,31 @@ var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
112
113
  console.log("[FloTrace] Runtime disabled, skipping connection");
113
114
  return;
114
115
  }
115
- if (typeof window === "undefined" || typeof WebSocket === "undefined") {
116
- console.log("[FloTrace] Not in browser environment, skipping connection");
116
+ if (typeof WebSocket === "undefined") {
117
+ console.log("[FloTrace] WebSocket not available, skipping connection");
117
118
  return;
118
119
  }
119
120
  this.isConnecting = true;
120
121
  try {
121
- const url = `ws://127.0.0.1:${this.config.port}`;
122
+ const url = `ws://${this.config.host}:${this.config.port}`;
122
123
  console.log(`[FloTrace] Connecting to ${url}...`);
123
124
  this.ws = new WebSocket(url);
124
125
  this.ws.onopen = () => {
125
126
  this.isConnecting = false;
126
127
  this.reconnectAttempts = 0;
127
- console.log("[FloTrace] Connected to VS Code extension");
128
+ console.log("[FloTrace] Connected to FloTrace desktop");
128
129
  this.notifyConnectionChange(true);
130
+ let appUrl;
131
+ try {
132
+ appUrl = typeof window !== "undefined" && window.location ? window.location.href : void 0;
133
+ } catch {
134
+ appUrl = void 0;
135
+ }
129
136
  this.send({
130
137
  type: "runtime:ready",
131
138
  appName: this.config.appName,
132
139
  reactVersion: this.getReactVersion(),
133
- appUrl: typeof window !== "undefined" ? window.location.href : void 0
140
+ appUrl
134
141
  });
135
142
  this.flush();
136
143
  };
@@ -4078,7 +4085,9 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4078
4085
  reduxStoreRef.current = reduxStore;
4079
4086
  const queryClientRef = (0, import_react.useRef)(queryClient);
4080
4087
  queryClientRef.current = queryClient;
4081
- if (mergedConfig.enabled && typeof window !== "undefined") {
4088
+ const enabledRef = (0, import_react.useRef)(mergedConfig.enabled);
4089
+ enabledRef.current = mergedConfig.enabled;
4090
+ if (mergedConfig.enabled && typeof WebSocket !== "undefined") {
4082
4091
  getWebSocketClient(mergedConfig);
4083
4092
  installFiberTreeWalker();
4084
4093
  prewarmNetworkTracker();
@@ -4142,56 +4151,44 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4142
4151
  console.log("[FloTrace] Tree tracking stopped");
4143
4152
  break;
4144
4153
  case "ext:requestNodeProps": {
4145
- const nodeId = message.nodeId;
4146
- if (nodeId) {
4147
- const props = getNodeProps(nodeId);
4148
- client4.sendImmediate({
4149
- type: "runtime:nodeProps",
4150
- nodeId,
4151
- props: props || {},
4152
- timestamp: Date.now()
4153
- });
4154
- }
4154
+ const props = getNodeProps(message.nodeId);
4155
+ client4.sendImmediate({
4156
+ type: "runtime:nodeProps",
4157
+ nodeId: message.nodeId,
4158
+ props: props || {},
4159
+ timestamp: Date.now()
4160
+ });
4155
4161
  break;
4156
4162
  }
4157
4163
  case "ext:requestNodeHooks": {
4158
- const hookNodeId = message.nodeId;
4159
- if (hookNodeId) {
4160
- const hooks = getNodeHooks(hookNodeId);
4161
- client4.sendImmediate({
4162
- type: "runtime:nodeHooks",
4163
- nodeId: hookNodeId,
4164
- hooks: hooks || [],
4165
- timestamp: Date.now()
4166
- });
4167
- }
4164
+ const hooks = getNodeHooks(message.nodeId);
4165
+ client4.sendImmediate({
4166
+ type: "runtime:nodeHooks",
4167
+ nodeId: message.nodeId,
4168
+ hooks: hooks || [],
4169
+ timestamp: Date.now()
4170
+ });
4168
4171
  break;
4169
4172
  }
4170
4173
  case "ext:requestNodeEffects": {
4171
- const effectNodeId = message.nodeId;
4172
- if (effectNodeId) {
4173
- const effects = getNodeEffects(effectNodeId);
4174
- client4.sendImmediate({
4175
- type: "runtime:nodeEffects",
4176
- nodeId: effectNodeId,
4177
- effects: effects || [],
4178
- timestamp: Date.now()
4179
- });
4180
- }
4174
+ const effects = getNodeEffects(message.nodeId);
4175
+ client4.sendImmediate({
4176
+ type: "runtime:nodeEffects",
4177
+ nodeId: message.nodeId,
4178
+ effects: effects || [],
4179
+ timestamp: Date.now()
4180
+ });
4181
4181
  break;
4182
4182
  }
4183
4183
  case "ext:requestDetailedRenderReason": {
4184
- const reasonNodeId = message.nodeId;
4185
- if (reasonNodeId) {
4186
- const reason = getDetailedRenderReason(reasonNodeId);
4187
- if (reason) {
4188
- client4.sendImmediate({
4189
- type: "runtime:detailedRenderReason",
4190
- nodeId: reasonNodeId,
4191
- reason,
4192
- timestamp: Date.now()
4193
- });
4194
- }
4184
+ const reason = getDetailedRenderReason(message.nodeId);
4185
+ if (reason) {
4186
+ client4.sendImmediate({
4187
+ type: "runtime:detailedRenderReason",
4188
+ nodeId: message.nodeId,
4189
+ reason,
4190
+ timestamp: Date.now()
4191
+ });
4195
4192
  }
4196
4193
  break;
4197
4194
  }
@@ -4200,18 +4197,15 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4200
4197
  console.log("[FloTrace] Full snapshot requested by extension");
4201
4198
  break;
4202
4199
  case "ext:requestTimeline": {
4203
- const timelineNodeId = message.nodeId;
4204
- if (timelineNodeId) {
4205
- const events = getTimeline(timelineNodeId);
4206
- const componentName = timelineNodeId.split("/").pop()?.replace(/-\d+$/, "") ?? "Unknown";
4207
- for (const event of events) {
4208
- client4.sendImmediate({
4209
- type: "runtime:timelineEvent",
4210
- nodeId: timelineNodeId,
4211
- componentName,
4212
- event
4213
- });
4214
- }
4200
+ const events = getTimeline(message.nodeId);
4201
+ const componentName = message.nodeId.split("/").pop()?.replace(/-\d+$/, "") ?? "Unknown";
4202
+ for (const event of events) {
4203
+ client4.sendImmediate({
4204
+ type: "runtime:timelineEvent",
4205
+ nodeId: message.nodeId,
4206
+ componentName,
4207
+ event
4208
+ });
4215
4209
  }
4216
4210
  break;
4217
4211
  }
@@ -4223,9 +4217,7 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4223
4217
  break;
4224
4218
  // --- Individual tracker start/stop (sidebar panel show/hide) ---
4225
4219
  case "ext:startReduxTracking":
4226
- if (reduxStoreRef.current) {
4227
- safeTrackerOp("Redux install", () => installReduxTracker(reduxStoreRef.current, client4));
4228
- }
4220
+ if (reduxStoreRef.current) safeTrackerOp("Redux install", () => installReduxTracker(reduxStoreRef.current, client4));
4229
4221
  break;
4230
4222
  case "ext:stopReduxTracking":
4231
4223
  safeTrackerOp("Redux uninstall", uninstallReduxTracker);
@@ -4238,19 +4230,14 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4238
4230
  break;
4239
4231
  case "ext:startZustandTracking":
4240
4232
  if (storesRef.current && Object.keys(storesRef.current).length > 0) {
4241
- safeTrackerOp("Zustand install", () => installZustandTracker(
4242
- storesRef.current,
4243
- client4
4244
- ));
4233
+ safeTrackerOp("Zustand install", () => installZustandTracker(storesRef.current, client4));
4245
4234
  }
4246
4235
  break;
4247
4236
  case "ext:stopZustandTracking":
4248
4237
  safeTrackerOp("Zustand uninstall", uninstallZustandTracker);
4249
4238
  break;
4250
4239
  case "ext:startTanstackTracking":
4251
- if (queryClientRef.current) {
4252
- safeTrackerOp("TanStack Query install", () => installTanStackQueryTracker(queryClientRef.current, client4));
4253
- }
4240
+ if (queryClientRef.current) safeTrackerOp("TanStack Query install", () => installTanStackQueryTracker(queryClientRef.current, client4));
4254
4241
  break;
4255
4242
  case "ext:stopTanstackTracking":
4256
4243
  safeTrackerOp("TanStack Query uninstall", uninstallTanStackQueryTracker);
@@ -4279,15 +4266,11 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4279
4266
  }, 100);
4280
4267
  };
4281
4268
  }, [mergedConfig.enabled, mergedConfig.port, mergedConfig.appName]);
4282
- const onRenderCallback = (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
4269
+ const onRenderCallback = (0, import_react.useCallback)((id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
4283
4270
  try {
4284
- if (!mergedConfig.enabled) {
4285
- return;
4286
- }
4271
+ if (!enabledRef.current) return;
4287
4272
  const client4 = getWebSocketClient();
4288
- if (!client4.connected) {
4289
- return;
4290
- }
4273
+ if (!client4.connected) return;
4291
4274
  const normalizedPhase = phase === "nested-update" ? "update" : phase;
4292
4275
  client4.send({
4293
4276
  type: "runtime:render",
@@ -4301,7 +4284,7 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4301
4284
  } catch (error) {
4302
4285
  console.error("[FloTrace] Error in Profiler callback:", error);
4303
4286
  }
4304
- };
4287
+ }, []);
4305
4288
  const contextValue = {
4306
4289
  connected,
4307
4290
  enabled: mergedConfig.enabled,
package/dist/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  // src/FloTraceProvider.tsx
2
- import React, { useEffect, useRef, createContext, useContext, Profiler } from "react";
2
+ import React, { useCallback, useEffect, useRef, createContext, useContext, Profiler } from "react";
3
3
 
4
4
  // src/types.ts
5
5
  var DEFAULT_CONFIG = {
6
+ host: "127.0.0.1",
6
7
  port: 3457,
7
8
  appName: "React App",
8
9
  enabled: process.env.NODE_ENV === "development",
@@ -42,25 +43,31 @@ var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
42
43
  console.log("[FloTrace] Runtime disabled, skipping connection");
43
44
  return;
44
45
  }
45
- if (typeof window === "undefined" || typeof WebSocket === "undefined") {
46
- console.log("[FloTrace] Not in browser environment, skipping connection");
46
+ if (typeof WebSocket === "undefined") {
47
+ console.log("[FloTrace] WebSocket not available, skipping connection");
47
48
  return;
48
49
  }
49
50
  this.isConnecting = true;
50
51
  try {
51
- const url = `ws://127.0.0.1:${this.config.port}`;
52
+ const url = `ws://${this.config.host}:${this.config.port}`;
52
53
  console.log(`[FloTrace] Connecting to ${url}...`);
53
54
  this.ws = new WebSocket(url);
54
55
  this.ws.onopen = () => {
55
56
  this.isConnecting = false;
56
57
  this.reconnectAttempts = 0;
57
- console.log("[FloTrace] Connected to VS Code extension");
58
+ console.log("[FloTrace] Connected to FloTrace desktop");
58
59
  this.notifyConnectionChange(true);
60
+ let appUrl;
61
+ try {
62
+ appUrl = typeof window !== "undefined" && window.location ? window.location.href : void 0;
63
+ } catch {
64
+ appUrl = void 0;
65
+ }
59
66
  this.send({
60
67
  type: "runtime:ready",
61
68
  appName: this.config.appName,
62
69
  reactVersion: this.getReactVersion(),
63
- appUrl: typeof window !== "undefined" ? window.location.href : void 0
70
+ appUrl
64
71
  });
65
72
  this.flush();
66
73
  };
@@ -4008,7 +4015,9 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4008
4015
  reduxStoreRef.current = reduxStore;
4009
4016
  const queryClientRef = useRef(queryClient);
4010
4017
  queryClientRef.current = queryClient;
4011
- if (mergedConfig.enabled && typeof window !== "undefined") {
4018
+ const enabledRef = useRef(mergedConfig.enabled);
4019
+ enabledRef.current = mergedConfig.enabled;
4020
+ if (mergedConfig.enabled && typeof WebSocket !== "undefined") {
4012
4021
  getWebSocketClient(mergedConfig);
4013
4022
  installFiberTreeWalker();
4014
4023
  prewarmNetworkTracker();
@@ -4072,56 +4081,44 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4072
4081
  console.log("[FloTrace] Tree tracking stopped");
4073
4082
  break;
4074
4083
  case "ext:requestNodeProps": {
4075
- const nodeId = message.nodeId;
4076
- if (nodeId) {
4077
- const props = getNodeProps(nodeId);
4078
- client4.sendImmediate({
4079
- type: "runtime:nodeProps",
4080
- nodeId,
4081
- props: props || {},
4082
- timestamp: Date.now()
4083
- });
4084
- }
4084
+ const props = getNodeProps(message.nodeId);
4085
+ client4.sendImmediate({
4086
+ type: "runtime:nodeProps",
4087
+ nodeId: message.nodeId,
4088
+ props: props || {},
4089
+ timestamp: Date.now()
4090
+ });
4085
4091
  break;
4086
4092
  }
4087
4093
  case "ext:requestNodeHooks": {
4088
- const hookNodeId = message.nodeId;
4089
- if (hookNodeId) {
4090
- const hooks = getNodeHooks(hookNodeId);
4091
- client4.sendImmediate({
4092
- type: "runtime:nodeHooks",
4093
- nodeId: hookNodeId,
4094
- hooks: hooks || [],
4095
- timestamp: Date.now()
4096
- });
4097
- }
4094
+ const hooks = getNodeHooks(message.nodeId);
4095
+ client4.sendImmediate({
4096
+ type: "runtime:nodeHooks",
4097
+ nodeId: message.nodeId,
4098
+ hooks: hooks || [],
4099
+ timestamp: Date.now()
4100
+ });
4098
4101
  break;
4099
4102
  }
4100
4103
  case "ext:requestNodeEffects": {
4101
- const effectNodeId = message.nodeId;
4102
- if (effectNodeId) {
4103
- const effects = getNodeEffects(effectNodeId);
4104
- client4.sendImmediate({
4105
- type: "runtime:nodeEffects",
4106
- nodeId: effectNodeId,
4107
- effects: effects || [],
4108
- timestamp: Date.now()
4109
- });
4110
- }
4104
+ const effects = getNodeEffects(message.nodeId);
4105
+ client4.sendImmediate({
4106
+ type: "runtime:nodeEffects",
4107
+ nodeId: message.nodeId,
4108
+ effects: effects || [],
4109
+ timestamp: Date.now()
4110
+ });
4111
4111
  break;
4112
4112
  }
4113
4113
  case "ext:requestDetailedRenderReason": {
4114
- const reasonNodeId = message.nodeId;
4115
- if (reasonNodeId) {
4116
- const reason = getDetailedRenderReason(reasonNodeId);
4117
- if (reason) {
4118
- client4.sendImmediate({
4119
- type: "runtime:detailedRenderReason",
4120
- nodeId: reasonNodeId,
4121
- reason,
4122
- timestamp: Date.now()
4123
- });
4124
- }
4114
+ const reason = getDetailedRenderReason(message.nodeId);
4115
+ if (reason) {
4116
+ client4.sendImmediate({
4117
+ type: "runtime:detailedRenderReason",
4118
+ nodeId: message.nodeId,
4119
+ reason,
4120
+ timestamp: Date.now()
4121
+ });
4125
4122
  }
4126
4123
  break;
4127
4124
  }
@@ -4130,18 +4127,15 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4130
4127
  console.log("[FloTrace] Full snapshot requested by extension");
4131
4128
  break;
4132
4129
  case "ext:requestTimeline": {
4133
- const timelineNodeId = message.nodeId;
4134
- if (timelineNodeId) {
4135
- const events = getTimeline(timelineNodeId);
4136
- const componentName = timelineNodeId.split("/").pop()?.replace(/-\d+$/, "") ?? "Unknown";
4137
- for (const event of events) {
4138
- client4.sendImmediate({
4139
- type: "runtime:timelineEvent",
4140
- nodeId: timelineNodeId,
4141
- componentName,
4142
- event
4143
- });
4144
- }
4130
+ const events = getTimeline(message.nodeId);
4131
+ const componentName = message.nodeId.split("/").pop()?.replace(/-\d+$/, "") ?? "Unknown";
4132
+ for (const event of events) {
4133
+ client4.sendImmediate({
4134
+ type: "runtime:timelineEvent",
4135
+ nodeId: message.nodeId,
4136
+ componentName,
4137
+ event
4138
+ });
4145
4139
  }
4146
4140
  break;
4147
4141
  }
@@ -4153,9 +4147,7 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4153
4147
  break;
4154
4148
  // --- Individual tracker start/stop (sidebar panel show/hide) ---
4155
4149
  case "ext:startReduxTracking":
4156
- if (reduxStoreRef.current) {
4157
- safeTrackerOp("Redux install", () => installReduxTracker(reduxStoreRef.current, client4));
4158
- }
4150
+ if (reduxStoreRef.current) safeTrackerOp("Redux install", () => installReduxTracker(reduxStoreRef.current, client4));
4159
4151
  break;
4160
4152
  case "ext:stopReduxTracking":
4161
4153
  safeTrackerOp("Redux uninstall", uninstallReduxTracker);
@@ -4168,19 +4160,14 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4168
4160
  break;
4169
4161
  case "ext:startZustandTracking":
4170
4162
  if (storesRef.current && Object.keys(storesRef.current).length > 0) {
4171
- safeTrackerOp("Zustand install", () => installZustandTracker(
4172
- storesRef.current,
4173
- client4
4174
- ));
4163
+ safeTrackerOp("Zustand install", () => installZustandTracker(storesRef.current, client4));
4175
4164
  }
4176
4165
  break;
4177
4166
  case "ext:stopZustandTracking":
4178
4167
  safeTrackerOp("Zustand uninstall", uninstallZustandTracker);
4179
4168
  break;
4180
4169
  case "ext:startTanstackTracking":
4181
- if (queryClientRef.current) {
4182
- safeTrackerOp("TanStack Query install", () => installTanStackQueryTracker(queryClientRef.current, client4));
4183
- }
4170
+ if (queryClientRef.current) safeTrackerOp("TanStack Query install", () => installTanStackQueryTracker(queryClientRef.current, client4));
4184
4171
  break;
4185
4172
  case "ext:stopTanstackTracking":
4186
4173
  safeTrackerOp("TanStack Query uninstall", uninstallTanStackQueryTracker);
@@ -4209,15 +4196,11 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4209
4196
  }, 100);
4210
4197
  };
4211
4198
  }, [mergedConfig.enabled, mergedConfig.port, mergedConfig.appName]);
4212
- const onRenderCallback = (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
4199
+ const onRenderCallback = useCallback((id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
4213
4200
  try {
4214
- if (!mergedConfig.enabled) {
4215
- return;
4216
- }
4201
+ if (!enabledRef.current) return;
4217
4202
  const client4 = getWebSocketClient();
4218
- if (!client4.connected) {
4219
- return;
4220
- }
4203
+ if (!client4.connected) return;
4221
4204
  const normalizedPhase = phase === "nested-update" ? "update" : phase;
4222
4205
  client4.send({
4223
4206
  type: "runtime:render",
@@ -4231,7 +4214,7 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
4231
4214
  } catch (error) {
4232
4215
  console.error("[FloTrace] Error in Profiler callback:", error);
4233
4216
  }
4234
- };
4217
+ }, []);
4235
4218
  const contextValue = {
4236
4219
  connected,
4237
4220
  enabled: mergedConfig.enabled,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flotrace/runtime",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Runtime package for FloTrace - enables real-time render tracking in your React app",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",