JS Flipper announcement
Summary: Annonce JS flipper Reviewed By: mweststrate Differential Revision: D33565875 fbshipit-source-id: da7140ea3a331ff3d387801410e88d1f6746bee3
This commit is contained in:
committed by
Facebook GitHub Bot
parent
ab0775a987
commit
4d1d9648f0
268
website/blog/2022-02-21-js-flipper-announcement.md
Normal file
268
website/blog/2022-02-21-js-flipper-announcement.md
Normal file
@@ -0,0 +1,268 @@
|
||||
---
|
||||
title: Flipper is coming to your web and Node.js apps
|
||||
author: Andrey Goncharov
|
||||
author_title: Software Engineer
|
||||
author_url: https://github.com/aigoncharov
|
||||
author_image_url: https://avatars.githubusercontent.com/u/12794628?v=4
|
||||
tags: [flipper, web, react, node.js]
|
||||
description:
|
||||
Flipper now provides an official JavaScript client. We will see what
|
||||
`js-flipper` is, go over Flipper communication protocol, talk about what it
|
||||
takes to build a new Flipper client.
|
||||
image: /img/js-flipper.jpg
|
||||
hide_table_of_contents: false
|
||||
---
|
||||
|
||||

|
||||
|
||||
For quite some time already, Flipper has secretly provided an experimental
|
||||
JavaScript SDK to support connections from browsers and Node.js under the name
|
||||
of `flipper-js-client-sdk`. With the ongoing migration of all our clients to
|
||||
WebSockets, we have committed to providing an official documented SDK for
|
||||
JavaScript clients. Without further ado, welcome
|
||||
[js-flipper](https://www.npmjs.com/package/js-flipper)!
|
||||
|
||||
In this post we will:
|
||||
|
||||
- See what `js-flipper` is
|
||||
- Get acquainted with how to build a Flipper plugin for a React app
|
||||
- Learn how Flipper talks to a mobile device
|
||||
- Dive deeper into the message structure
|
||||
- Glance at what it takes to support a new platform
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
## What `js-flipper` is and why it matters
|
||||
|
||||
Flipper supports native iOS, native Android apps and React Native apps out of
|
||||
the box. Now with `js-flipper`, Flipper also supports JavaScript apps. Any
|
||||
JavaScript app, whether they run in your browser or on your Node.js server, can
|
||||
now connect to Flipper for a debugging session.
|
||||
|
||||
`js-flipper` is a new NPM package that exposes a Flipper client to your
|
||||
JavaScript apps. Any Flipper client, in its turn, is a set of abstractions that
|
||||
let your device connect and talk to Flipper. Long story short, `js-flipper`
|
||||
allows you to easily write Flipper plugins for your web and Node.js apps.
|
||||
|
||||
> [Here](https://fbflipper.com/docs/tutorial/javascript/) is how you can write
|
||||
> your first simple plugin.
|
||||
|
||||
Why does it matter?
|
||||
|
||||
It's a huge deal for two reasons:
|
||||
|
||||
1. Flipper at its core is just a device discovery service with a message bus.
|
||||
Its power comes from the plugins and the ecosystem.
|
||||
2. It brings us one step closer to our goal of running Flipper everywhere. Bring
|
||||
Flipper to your microwave! On a serious note, more platforms -> bigger
|
||||
community -> more developers -> more plugins -> better Flipper for everyone.
|
||||
|
||||
Let's take a quick look at the principal architecture of Flipper:
|
||||
|
||||

|
||||
|
||||
Here is what happens there:
|
||||
|
||||
1. Flipper constantly polls
|
||||
[ADB](https://developer.android.com/studio/command-line/adb) for available
|
||||
Android devices and [IDB](https://fbidb.io/) for available iOS devices.
|
||||
2. If the device is running an app with an enabled Flipper client, the client
|
||||
tries to connect to Flipper on your laptop. It lets Flipper know that there
|
||||
is an app that it can talk to. Flipper and app chit-chat a bit negotiating
|
||||
the security and the list of supported plugins.
|
||||
3. The developer picks one of the connected apps / devices. Say, it's the app.
|
||||
4. The developer clicks one of the available plugins.
|
||||
5. The plugin starts talking to the app on the device via the message bus
|
||||
exposed by Flipper. The plugin asks for necessary data from the app and shows
|
||||
it in a pretty UI.
|
||||
|
||||
At Meta, we have many active plugins, across a wide variety of devices, not just
|
||||
phones, but also Quests, desktop applications, etc. At its core, Flipper is
|
||||
data-agnostic and connect data flows to plugin displays. All Flipper core (we
|
||||
call it Flipper Server) knows is what devices and Flipper-enabled apps are out
|
||||
there. I hope it gets us on the same page regarding why plugins (and plugin
|
||||
developers!) are crucial for Flipper.
|
||||
|
||||
Another important conclusion you could draw from the diagram is that the state
|
||||
of Flipper plugins is ephemeral and lives in the UI.
|
||||
|
||||
## How Flipper talks to a mobile device
|
||||
|
||||
Let's dive a bit deeper into how exactly the device and Flipper talk. Flipper
|
||||
pulls device logs from ADB/IDB. For everything else, Flipper expects the app
|
||||
(Flipper client inside of the app) to open a WebSocket connection to Flipper.
|
||||
|
||||
The algorithm looks like this:
|
||||
|
||||
1. The app opens a WebSocket connection to Flipper.
|
||||
2. They exchange certificates. Flipper connects to the app using ADB/IDB and
|
||||
writes a certificate to the app storage.
|
||||
3. The app opens a secure WebSocket connection to Flipper using the certificate.
|
||||
|
||||
Why do we even bother with the certificate exchange process? One of the
|
||||
potential attack vectors is that a developer could install a malicious app on
|
||||
the testing device. That app could spin up a WebSocket server and mask itself as
|
||||
Flipper. However, unlike Flipper, the malicious app can't access the file
|
||||
storage of another app. As a result, it can't complete the certificate exchange
|
||||
process.
|
||||
|
||||
On mobile devices certificate exchange is important, so that other apps on the
|
||||
phone can't impersonate Flipper. For browser apps this isn't an issue as the
|
||||
browser already makes sure a malicious page cannot act as Flipper server. For
|
||||
platforms like this, we use a simplified connection algorithm:
|
||||
|
||||
1. The app opens a WebSocket connection to Flipper.
|
||||
2. Bingo!
|
||||
|
||||
`js-flipper` implements the second algorithm, without the certificate exchange.
|
||||
|
||||
## Message protocol and structure
|
||||
|
||||
Once the final WebSocket connection is established, Flipper starts talking to
|
||||
the app:
|
||||
|
||||
1. It sends `getPlugins` and `getBackgroundPlugins` messages to get a list of
|
||||
plugins supported by the app.
|
||||
2. Flipper displays the available plugins to the developer.
|
||||
3. Developer clicks on one of the plugins (enables a plugin).
|
||||
4. Flipper loads the UI for the plugin. Let's settle on calling the part of the
|
||||
plugin "desktop plugin" and the device part of the plugin "client plugin".
|
||||
5. Flipper sends `init` message to the app.
|
||||
6. Client plugin `onConnect` code is executed. Read more about Client Plugin API
|
||||
[here](https://fbflipper.com/docs/extending/create-plugin/).
|
||||
7. Whenever a "desktop plugin" needs some data from the device it sends an
|
||||
`execute` message to the "client plugin" on the device.
|
||||
8. "Client plugin" replies with the data.
|
||||
9. "Client plugin" might force the "desktop plugin" to do something as well by
|
||||
sending an `execute` message as well. However, it is rare. In the current
|
||||
implementation, the "client plugin" can never expect a reply back from the
|
||||
"desktop plugin". In other words, consider it as an event sink, not as a way
|
||||
to extract some data from the "desktop plugin".
|
||||
10. When the plugin is deactivated a `deinit` message is sent to the "client
|
||||
plugin".
|
||||
11. Client plugin `onDisconnect` code is executed.
|
||||
|
||||
> The process above is for the insecure WebSocket connections we currently use
|
||||
> in `js-flipper`. It is more complicated for secure WebSocket connections that
|
||||
> require certificate exchange.
|
||||
|
||||
Flipper expects each message to have the following structure:
|
||||
|
||||
```ts
|
||||
export interface FlipperRequest {
|
||||
method: string; // 'getPlugins' | 'getBackgroundPlugins' | 'init' | 'deinit' | 'execute' | 'isMethodSupported'
|
||||
params?: {
|
||||
api: string; // Plugin ID (name)
|
||||
// These nested `method` and `params` could be anything.
|
||||
// You set them yourself as you see fit to support the data exchange between the "desktop plugin" and the "client plugin".
|
||||
// For example, for 'ReactNativeTicTacToe' we support 2 methods: 'SetState' and 'GetState'.
|
||||
// We pass a game state with a 'SetState' message. See https://fbflipper.com/docs/tutorial/javascript/#step-3-call-addplugin-to-add-your-plugin
|
||||
method: string;
|
||||
params?: unknown;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The only exception is the response message the "client plugin" sends back when
|
||||
the data is requested.
|
||||
|
||||
```ts
|
||||
export type FlipperResponse = {
|
||||
id: number;
|
||||
success?: object | string | number | boolean | null;
|
||||
error?: {
|
||||
message: string;
|
||||
stacktrace?: string;
|
||||
name?: string;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Building a new client
|
||||
|
||||
At this point, you know what messages your client needs to support in a Flipper
|
||||
client:
|
||||
|
||||
- `getPlugins`
|
||||
- `getBackgroundPlugins`
|
||||
- `init`
|
||||
- `deinit`
|
||||
- `execute`
|
||||
|
||||
One other message we did not mention before is `isMethodSupported`. Its job is
|
||||
to reply back to a "desktop plugin" whether a "client plugin" supports one of
|
||||
plugin messages (that nested `method` field). It's useful when you have a single
|
||||
"desktop plugin" implementation, but different "client plugin" implementations.
|
||||
For example, some operations might not be supported on iOS, but are supported on
|
||||
Android. Alternatively, it can address version differences between the plugin
|
||||
installed on the device and the one loaded into Flipper.
|
||||
|
||||
If you want to build a proper Flipper client, you also need to provide an
|
||||
abstraction for plugin developers. Consider matching
|
||||
[what we have for existing clients](https://fbflipper.com/docs/extending/create-plugin/#flipperplugin).
|
||||
|
||||
Most of the groundwork for handling connections and doing certificate exchange
|
||||
is already done in our
|
||||
[C++ engine](https://github.com/facebook/flipper/tree/main/xplat). Our iOS,
|
||||
Android, React Native clients use it under the hood. `js-flipper` implements
|
||||
everything from scratch using native browser APIs (for Node.js apps we
|
||||
[require developers to provide a WebSocket implementation](https://github.com/facebook/flipper/tree/main/js/js-flipper#nodejs)).
|
||||
|
||||
Here is a detailed document on how to
|
||||
[implement a client](https://fbflipper.com/docs/extending/new-clients/). You
|
||||
might also want to check the source code of our existing clients:
|
||||
|
||||
- [iOS](https://github.com/facebook/flipper/tree/main/iOS/FlipperKit)
|
||||
- [Android](https://github.com/facebook/flipper/tree/main/android/src/main)
|
||||
- [React Native](https://github.com/facebook/flipper/tree/main/react-native/react-native-flipper)
|
||||
- [JavaScript](https://github.com/facebook/flipper/tree/main/js/js-flipper)
|
||||
|
||||
## What's next?
|
||||
|
||||
As of now, we do not provide any default plugins you might be used to for
|
||||
`js-flipper` (Layout, Logs, Navigation, Crash Reporter, and others). We hope
|
||||
this will change in the future with the help of ur beloved open-source
|
||||
community!
|
||||
|
||||
_Call to action!_
|
||||
|
||||
We would like to encourage you to play with `js-flipper`. See how it fits your
|
||||
use-case and get back back to us with your feedback on
|
||||
[GitHub](https://github.com/facebook/flipper/issues). If you find yourself
|
||||
implementing one of your favorite Flipper plugins for `js-flipper`, do not
|
||||
hesitate and raise a PR!
|
||||
|
||||
Plugins can be either generic or very application specific. Plugins can interact
|
||||
with Redux or MobX stores, read performance data or console logs from the
|
||||
browser. At Meta, we also see a lot of plugins that are very application
|
||||
specific. For example, plugins that allow logging in as specific test users with
|
||||
a single click, reading the internal state of NewsFeed and interacting with it,
|
||||
simulating photos captured by a smartphone, etc. A Flipper plugin can be any
|
||||
form of UI that is useful to speed up debugging and tasks on things you work on
|
||||
frequently!
|
||||
|
||||
## P.S. Flipper needs you!
|
||||
|
||||
Flipper is maintained by a small team at Meta, yet is serving over a hundred
|
||||
plugins and dozens of different targets. Our team's goal is to support Flipper
|
||||
as a plugin-based platform for which we maintain the infrastructure. We don't
|
||||
typically invest in individual plugins, but we do love plugin improvements. For
|
||||
example, the support for mocking network requests (on Android) was entirely
|
||||
contributed by the community (thanks
|
||||
[James Harmon](https://github.com/bizzguy)!). As was Protobuf support (thanks
|
||||
[Harold Martin](https://github.com/hbmartin)!).
|
||||
|
||||
For that reason, we've marked many requests in the issue tracker as
|
||||
[PR Welcome](https://github.com/facebook/flipper/issues?q=is%3Aissue+is%3Aopen+label%3A%22PR+welcome%22).
|
||||
Contributing changes should be as simple as cloning the
|
||||
[repository](https://github.com/facebook/flipper) and running
|
||||
`yarn && yarn start` in the `desktop/` folder.
|
||||
|
||||
Investing in debugging tools, both generic ones or just for specific apps, will
|
||||
benefit iteration speed. And we hope Flipper will make it as hassle free as
|
||||
possible to create your debugging tools. For an overview of Flipper for React
|
||||
Native, and why and how to build your own plugins, we recommend checking out the
|
||||
[Flipper: The Extensible DevTool Platform for React Native](https://youtu.be/WltZTn3ODW4)
|
||||
talk.
|
||||
|
||||
Happy debugging!
|
||||
16
website/static/img/flipper-arch.svg
Normal file
16
website/static/img/flipper-arch.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 223 KiB |
BIN
website/static/img/js-flipper.jpg
Normal file
BIN
website/static/img/js-flipper.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
Reference in New Issue
Block a user