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