Add API spec for find() sibling wait and RECONNECT structure event#23
Add API spec for find() sibling wait and RECONNECT structure event#23stefanrammo wants to merge 1 commit intomainfrom
Conversation
Specifies two changes to improve multi-app usability: 1. find() waits for sibling apps by default instead of failing immediately 2. subscribeToStructure gets a RECONNECT event (value 2) to distinguish app restarts from first-time appearances Includes 7 use case examples, backwards compatibility analysis, and comparison with Java/C++ StudioAPI clients. CDP-6069
| ```javascript | ||
| client.find('App2.CPULoad') // waits (default timeout) for App2 to appear | ||
| client.find('App2.CPULoad', { timeout: 5000 }) // waits up to 5s | ||
| client.find('App2.CPULoad', { timeout: 0 }) // fails immediately if App2 not available (old behavior) |
There was a problem hiding this comment.
I think should allow for no timeout. E.g. if one has a panel that only is meant to show some App2 value then indefinite hang might be fine. Maybe that should be even the default?
| - **Immediate fail:** `{ timeout: 0 }` preserves the old behavior — rejects immediately if the app is not available. | ||
| - **No prior `root()` required:** `find()` must trigger the connection internally if not already connected. Callers should not need to call `root()` first. | ||
| - **Direct mode guard (immediate fail only):** When using `{ timeout: 0 }` in direct mode (no proxy protocol), `find()` rejects with `"AppName is not available"` if the app was previously connected but is now disconnected. This prevents returning stale cached nodes whose underlying WebSocket is down. When waiting (default), this guard does not apply — the wait resolves when the app reconnects. In proxy mode, the primary connection maintains the node tree so the guard is not needed. | ||
| - **Direct mode discovery:** In direct mode, `find()` triggers on-demand structure refreshes (every 2s while waiting) to discover new siblings. In proxy mode, discovery is real-time via `ServicesNotification`. |
There was a problem hiding this comment.
"every 2s while waiting" - polling should not be necessary, CDP will notify of any new apps appearing. Although I can't remember if one had to subscribe to root node structure or if the notifications come automatically
There was a problem hiding this comment.
I think how it worked was that CDP app sends invalidate on root node (that is a structure_change_response with id of 0). And after receiving invalidate of root, CDP should poll for structure change to find sibling apps.
| - **Proxy mode:** Sibling app disappears from proxy services, then reappears — fires REMOVE then RECONNECT. | ||
| - **Direct mode:** Direct WebSocket connection drops, then reconnects — fires REMOVE then RECONNECT. | ||
|
|
||
| **Direct mode discovery:** Without this change, `subscribeToStructure` in direct mode does not discover apps that start after the subscription — it only sees apps that were already connected. This change adds periodic structure polling (every 2s) while subscribers exist, so that ADD/RECONNECT events fire for late-starting apps in direct mode. In proxy mode, discovery is real-time via `ServicesNotification` so no polling is needed. Polling stops automatically when all subscribers unsubscribe or `client.close()` is called. |
There was a problem hiding this comment.
Even in direct mode CDP apps notify their siblings are started/stopped. You don't need to poll. One could only consider automatic reconnect for resiliency when first CDP app reports siblings but connection had failed (some networking issue or something).
| - **Immediate fail:** `{ timeout: 0 }` preserves the old behavior — rejects immediately if the app is not available. | ||
| - **No prior `root()` required:** `find()` must trigger the connection internally if not already connected. Callers should not need to call `root()` first. | ||
| - **Direct mode guard (immediate fail only):** When using `{ timeout: 0 }` in direct mode (no proxy protocol), `find()` rejects with `"AppName is not available"` if the app was previously connected but is now disconnected. This prevents returning stale cached nodes whose underlying WebSocket is down. When waiting (default), this guard does not apply — the wait resolves when the app reconnects. In proxy mode, the primary connection maintains the node tree so the guard is not needed. | ||
| - **Direct mode discovery:** In direct mode, `find()` triggers on-demand structure refreshes (every 2s while waiting) to discover new siblings. In proxy mode, discovery is real-time via `ServicesNotification`. |
There was a problem hiding this comment.
I think how it worked was that CDP app sends invalidate on root node (that is a structure_change_response with id of 0). And after receiving invalidate of root, CDP should poll for structure change to find sibling apps.
| **Direct mode discovery:** Without this change, `subscribeToStructure` in direct mode does not discover apps that start after the subscription — it only sees apps that were already connected. This change adds periodic structure polling (every 2s) while subscribers exist, so that ADD/RECONNECT events fire for late-starting apps in direct mode. In proxy mode, discovery is real-time via `ServicesNotification` so no polling is needed. Polling stops automatically when all subscribers unsubscribe or `client.close()` is called. | ||
|
|
||
| - **Backwards compatible for `=== ADD` checks:** Existing code checking `change === studio.api.structure.ADD` still works — new apps still get ADD. Only restarted apps get RECONNECT. | ||
| - **Not compatible with `!== ADD` patterns:** Code that treats any non-ADD change as REMOVE (e.g. `if (change !== ADD) handleRemove()`) will incorrectly trigger on RECONNECT. Such code should be updated to check `change === REMOVE` explicitly. |
There was a problem hiding this comment.
I think we can update the version number to new major version and list the API changes. The RECONNECT sounds like a good change.
|
|
||
| ```javascript | ||
| studio.api.structure.ADD // 1 — app appeared for the first time | ||
| studio.api.structure.REMOVE // 0 — app went offline |
There was a problem hiding this comment.
I think REMOVE and DISCONNECT should be different behaviors as you can do real removals then the node will not come back
There was a problem hiding this comment.
Though applications can't be removed as such, but it sounds as it should still a different thing in the constants... to diff form operator REMOVALS that mean it is gone forever.
So in my opinion there should be "DISCONNECT" also in the list that applies to application nodes leaving. (and the constants are not not about apps, but nodes in general, so the comments are wrong)
| ```java | ||
| eChildRemoved, // ≈ JS REMOVE | ||
| eChildAdded, // ≈ JS ADD | ||
| eSubscribedNodeLost, // connection to app lost (fires recursively on subtree) |
There was a problem hiding this comment.
I think java client got also this DISCONNECT as a fourth structure event type right.
Specifies two changes to improve multi-app usability:
Includes 7 use case examples, backwards compatibility analysis, and comparison with Java/C++ StudioAPI clients.
CDP-6069