api + plugins examples
Summary: Initial commit for Flipper API for JS apps: 1) Plugin, Connection, Client 2) a bridge to work with WebView proxy app 3) examples of ported plugins: AnalyticsLogging and Fury Reviewed By: danielbuechele Differential Revision: D16753405 fbshipit-source-id: cdd4b863db236b2043f09833aed130035f6738de
This commit is contained in:
committed by
Facebook Github Bot
parent
83f00f2485
commit
46e0abecdf
113
src/utils/js-client/api.js
Normal file
113
src/utils/js-client/api.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type FlipperPluginID = string;
|
||||||
|
|
||||||
|
export type FlipperMethodID = string;
|
||||||
|
|
||||||
|
export class FlipperBridge {
|
||||||
|
registerPlugins: (plugins: Array<FlipperPluginID>) => void;
|
||||||
|
|
||||||
|
start: () => void;
|
||||||
|
|
||||||
|
stop: () => void;
|
||||||
|
|
||||||
|
sendData: (
|
||||||
|
plugin: FlipperPluginID,
|
||||||
|
method: FlipperMethodID,
|
||||||
|
data: any,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
subscribe: (
|
||||||
|
plugin: FlipperPluginID,
|
||||||
|
method: FlipperMethodID,
|
||||||
|
handler: (any) => void,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
isAvailable: () => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FlipperResponder {
|
||||||
|
pluginId: FlipperPluginID;
|
||||||
|
methodId: FlipperMethodID;
|
||||||
|
_bridge: FlipperBridge;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
pluginId: FlipperPluginID,
|
||||||
|
methodId: FlipperMethodID,
|
||||||
|
bridge: FlipperBridge,
|
||||||
|
) {
|
||||||
|
this.pluginId = pluginId;
|
||||||
|
this.methodId = methodId;
|
||||||
|
this._bridge = bridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
success(response: any) {}
|
||||||
|
|
||||||
|
error(response: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FlipperReceiver<T> = (
|
||||||
|
params: T,
|
||||||
|
responder: FlipperResponder,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
export class FlipperConnection {
|
||||||
|
pluginId: FlipperPluginID;
|
||||||
|
_bridge: FlipperBridge;
|
||||||
|
|
||||||
|
constructor(pluginId: FlipperPluginID, bridge: FlipperBridge) {
|
||||||
|
this.pluginId = pluginId;
|
||||||
|
this._bridge = bridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
send(method: FlipperMethodID, data: any) {
|
||||||
|
this._bridge.sendData(this.pluginId, method, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
receive(method: FlipperMethodID, receiver: FlipperReceiver<*>) {
|
||||||
|
this._bridge.subscribe(this.pluginId, method, data => {
|
||||||
|
receiver(data, new FlipperResponder(this.pluginId, method, this._bridge));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FlipperPlugin {
|
||||||
|
id: FlipperPluginID;
|
||||||
|
_connection: ?FlipperConnection;
|
||||||
|
|
||||||
|
onConnect(connection: FlipperConnection) {
|
||||||
|
this._connection = connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FlipperClient {
|
||||||
|
_bridge: FlipperBridge;
|
||||||
|
plugins: Map<FlipperPluginID, FlipperPlugin> = new Map();
|
||||||
|
|
||||||
|
constructor(bridge: FlipperBridge) {
|
||||||
|
this._bridge = bridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPlugin(plugin: FlipperPlugin) {
|
||||||
|
plugin.onConnect(new FlipperConnection(plugin.id, this._bridge));
|
||||||
|
this.plugins.set(plugin.id, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlugin(id: FlipperPluginID): ?FlipperPlugin {
|
||||||
|
return this.plugins.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this._bridge.registerPlugins([...this.plugins.keys()]);
|
||||||
|
this._bridge.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this._bridge.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/utils/js-client/example.js
Normal file
38
src/utils/js-client/example.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {FlipperClient} from './api';
|
||||||
|
import {newWebviewClient} from './webviewImpl';
|
||||||
|
import {AnalyticsLoggingFlipperPlugin} from './plugins/analyticsLogging';
|
||||||
|
import {FuryPlugin} from './plugins/fury';
|
||||||
|
|
||||||
|
class FlipperManager {
|
||||||
|
flipperClient: FlipperClient;
|
||||||
|
analyticsPlugin: AnalyticsLoggingFlipperPlugin;
|
||||||
|
furyPlugin: FuryPlugin;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.flipperClient = newWebviewClient();
|
||||||
|
this.analyticsPlugin = new AnalyticsLoggingFlipperPlugin();
|
||||||
|
this.furyPlugin = new FuryPlugin();
|
||||||
|
this.flipperClient.addPlugin(this.analyticsPlugin);
|
||||||
|
this.flipperClient.addPlugin(this.furyPlugin);
|
||||||
|
this.flipperClient.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let flipperManager: ?FlipperManager;
|
||||||
|
|
||||||
|
export function init() {
|
||||||
|
if (!flipperManager) {
|
||||||
|
flipperManager = new FlipperManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function flipper(): ?FlipperManager {
|
||||||
|
return flipperManager;
|
||||||
|
}
|
||||||
11
src/utils/js-client/package.json
Normal file
11
src/utils/js-client/package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "flipper-js-client",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"title": "Flipper JS Client",
|
||||||
|
"icon": "apps",
|
||||||
|
"bugs": {
|
||||||
|
"email": "timurvaliev@fb.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/utils/js-client/plugins/analyticsLogging.js
Normal file
30
src/utils/js-client/plugins/analyticsLogging.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {FlipperPlugin} from '../api';
|
||||||
|
|
||||||
|
import type {FlipperPluginID} from '../api';
|
||||||
|
|
||||||
|
type EventId = string;
|
||||||
|
|
||||||
|
export type AnalyticsEvent = {
|
||||||
|
id: EventId,
|
||||||
|
module: string,
|
||||||
|
name: string,
|
||||||
|
time: number,
|
||||||
|
filter: ?string,
|
||||||
|
highpri: ?boolean,
|
||||||
|
extras: any,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class AnalyticsLoggingFlipperPlugin extends FlipperPlugin {
|
||||||
|
id: FlipperPluginID = 'AnalyticsLogging';
|
||||||
|
|
||||||
|
sendEvent(event: AnalyticsEvent) {
|
||||||
|
this._connection && this._connection.send('reportEvent', event);
|
||||||
|
}
|
||||||
|
}
|
||||||
104
src/utils/js-client/plugins/fury.js
Normal file
104
src/utils/js-client/plugins/fury.js
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {FlipperPlugin, FlipperResponder, FlipperConnection} from '../api';
|
||||||
|
|
||||||
|
import type {FlipperPluginID} from '../api';
|
||||||
|
|
||||||
|
type EventId = string;
|
||||||
|
|
||||||
|
type EventType = number;
|
||||||
|
type ThreadID = number;
|
||||||
|
type SeqID = number;
|
||||||
|
|
||||||
|
type StackTraceElement = {
|
||||||
|
className: string,
|
||||||
|
methodName: string,
|
||||||
|
fileName: string,
|
||||||
|
lineNumber: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
type FuryTaskEvent = {
|
||||||
|
id: EventId,
|
||||||
|
time: number,
|
||||||
|
eventType: EventType,
|
||||||
|
callStack: StackTraceElement[],
|
||||||
|
extras: any,
|
||||||
|
tag: string,
|
||||||
|
parentTid: ThreadID,
|
||||||
|
currentTid: ThreadID,
|
||||||
|
parentSeqId: SeqID,
|
||||||
|
currentSeqId: SeqID,
|
||||||
|
isDirect: boolean,
|
||||||
|
isPoint: boolean,
|
||||||
|
isOnActivated: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReqContext = {
|
||||||
|
tag: string,
|
||||||
|
parentSeqId: SeqID,
|
||||||
|
currentSeqId: SeqID,
|
||||||
|
isDirect: boolean,
|
||||||
|
isPoint: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
const structuredTraceFun = (error, structuredStackTrace) => {
|
||||||
|
return structuredStackTrace;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getStackTrace(): CallSite[] {
|
||||||
|
const oldPrep = Error.prepareStackTrace;
|
||||||
|
Error.prepareStackTrace = structuredTraceFun;
|
||||||
|
const error = {};
|
||||||
|
Error.captureStackTrace(error, getStackTrace);
|
||||||
|
const stack = error.stack;
|
||||||
|
Error.prepareStackTrace = oldPrep;
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FuryPlugin extends FlipperPlugin {
|
||||||
|
id: FlipperPluginID = 'Fury';
|
||||||
|
|
||||||
|
onConnect(connection: FlipperConnection) {
|
||||||
|
super.onConnect(connection);
|
||||||
|
connection.receive(
|
||||||
|
'toggleRecording',
|
||||||
|
(data: any, responder: FlipperResponder) => {
|
||||||
|
window.console.log(data);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
processEvent(reqContext: ReqContext, isOnActivated: boolean) {
|
||||||
|
const stack = getStackTrace();
|
||||||
|
const eventType = reqContext.isPoint ? 2 : isOnActivated ? 0 : 1;
|
||||||
|
const event: FuryTaskEvent = {
|
||||||
|
id: reqContext.currentSeqId + '/' + eventType,
|
||||||
|
time: new Date().getTime(),
|
||||||
|
eventType: eventType,
|
||||||
|
callStack: stack.map(frame => {
|
||||||
|
return {
|
||||||
|
className: frame.getTypeName() || '',
|
||||||
|
methodName: frame.getFunctionName() || '',
|
||||||
|
fileName: frame.getFileName() || '',
|
||||||
|
lineNumber: frame.getLineNumber() || 0,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
extras: {},
|
||||||
|
tag: reqContext.tag,
|
||||||
|
parentTid: -1,
|
||||||
|
currentTid: -1,
|
||||||
|
parentSeqId: reqContext.parentSeqId,
|
||||||
|
currentSeqId: reqContext.currentSeqId,
|
||||||
|
isDirect: reqContext.isDirect,
|
||||||
|
isPoint: reqContext.isPoint,
|
||||||
|
isOnActivated: isOnActivated,
|
||||||
|
};
|
||||||
|
this._connection && this._connection.send('reportEvent', event);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/utils/js-client/webviewImpl.js
Normal file
61
src/utils/js-client/webviewImpl.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {FlipperClient, FlipperBridge} from './api';
|
||||||
|
|
||||||
|
import type {FlipperPluginID, FlipperMethodID} from './api';
|
||||||
|
|
||||||
|
class FlipperWebviewBridgeImpl extends FlipperBridge {
|
||||||
|
_subscriptions: Map<string, (any) => void> = new Map();
|
||||||
|
|
||||||
|
registerPlugins = (plugins: Array<FlipperPluginID>) => {
|
||||||
|
window.FlipperWebviewBridge &&
|
||||||
|
window.FlipperWebviewBridge.registerPlugins(plugins);
|
||||||
|
};
|
||||||
|
|
||||||
|
start = () => {
|
||||||
|
window.FlipperWebviewBridge && window.FlipperWebviewBridge.start();
|
||||||
|
};
|
||||||
|
|
||||||
|
stop = () => {
|
||||||
|
window.FlipperWebviewBridge && window.FlipperWebviewBridge.stop();
|
||||||
|
};
|
||||||
|
|
||||||
|
sendData = (plugin: FlipperPluginID, method: FlipperMethodID, data: any) => {
|
||||||
|
window.FlipperWebviewBridge &&
|
||||||
|
window.FlipperWebviewBridge.sendFlipperObject(
|
||||||
|
plugin,
|
||||||
|
method,
|
||||||
|
JSON.stringify(data),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
subscribe = (
|
||||||
|
plugin: FlipperPluginID,
|
||||||
|
method: FlipperMethodID,
|
||||||
|
handler: any => void,
|
||||||
|
) => {
|
||||||
|
this._subscriptions.set(plugin + method, handler);
|
||||||
|
window.FlipperWebviewBridge &&
|
||||||
|
window.FlipperWebviewBridge.subscribe(plugin, method);
|
||||||
|
};
|
||||||
|
|
||||||
|
isAvailable = () => {
|
||||||
|
return window.FlipperWebviewBridge != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleMessage(plugin: FlipperPluginID, method: FlipperMethodID, data: any) {
|
||||||
|
const handler: ?(any) => void = this._subscriptions.get(plugin + method);
|
||||||
|
handler && handler(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newWebviewClient(): FlipperClient {
|
||||||
|
const bridge = new FlipperWebviewBridgeImpl();
|
||||||
|
window.FlipperBridgeClientSide = bridge;
|
||||||
|
return new FlipperClient(bridge);
|
||||||
|
}
|
||||||
4
src/utils/js-client/yarn.lock
Normal file
4
src/utils/js-client/yarn.lock
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user