feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,67 @@
import { EventEmitter, EventSubscription } from 'fbemitter';
import { WebSocketBackingStore } from './WebSocketBackingStore';
import type { ConnectionInfo, DevToolsPluginClientOptions } from './devtools.types';
export declare const MESSAGE_PROTOCOL_VERSION = 2;
export declare const DevToolsPluginMethod = "Expo:DevToolsPlugin";
/**
* This client is for the Expo DevTools Plugins to communicate between the app and the DevTools webpage hosted in a browser.
* All the code should be both compatible with browsers and React Native.
*/
export declare abstract class DevToolsPluginClient {
readonly connectionInfo: ConnectionInfo;
private readonly options?;
protected eventEmitter: EventEmitter;
private static defaultWSStore;
private readonly wsStore;
protected isClosed: boolean;
protected retries: number;
private readonly useTransportationNext;
private readonly messageFramePacker;
constructor(connectionInfo: ConnectionInfo, options?: DevToolsPluginClientOptions | undefined);
/**
* Initialize the connection.
* @hidden
*/
initAsync(): Promise<void>;
/**
* Close the connection.
*/
closeAsync(): Promise<void>;
/**
* Send a message to the other end of DevTools.
* @param method A method name.
* @param params any extra payload.
*/
sendMessage(method: string, params: any): void;
private sendMessageImplLegacy;
private sendMessageImplTransportationNext;
/**
* Subscribe to a message from the other end of DevTools.
* @param method Subscribe to a message with a method name.
* @param listener Listener to be called when a message is received.
*/
addMessageListener(method: string, listener: (params: any) => void): EventSubscription;
/**
* Subscribe to a message from the other end of DevTools just once.
* @param method Subscribe to a message with a method name.
* @param listener Listener to be called when a message is received.
*/
addMessageListenerOnce(method: string, listener: (params: any) => void): void;
/**
* Returns whether the client is connected to the server.
*/
isConnected(): boolean;
/**
* The method to create the WebSocket connection.
*/
protected connectAsync(): Promise<WebSocket>;
protected handleMessage: (event: WebSocketMessageEvent) => void;
private handleMessageImplLegacy;
private handleMessageImplTransportationNext;
/**
* Get the WebSocket backing store. Exposed for testing.
* @hidden
*/
getWebSocketBackingStore(): WebSocketBackingStore;
}
//# sourceMappingURL=DevToolsPluginClient.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DevToolsPluginClient.d.ts","sourceRoot":"","sources":["../../src/devtools/DevToolsPluginClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAG5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,KAAK,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAIpF,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAO1D;;;GAGG;AACH,8BAAsB,oBAAoB;aAYtB,cAAc,EAAE,cAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAZ3B,SAAS,CAAC,YAAY,EAAE,YAAY,CAAsB;IAE1D,OAAO,CAAC,MAAM,CAAC,cAAc,CAAsD;IACnF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8D;IAEtF,SAAS,CAAC,QAAQ,UAAS;IAC3B,SAAS,CAAC,OAAO,SAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAU;IAChD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA0D;gBAG3E,cAAc,EAAE,cAAc,EAC7B,OAAO,CAAC,yCAA6B;IAOxD;;;OAGG;IACU,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAQvC;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAWxC;;;;OAIG;IACI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG;IAY9C,OAAO,CAAC,qBAAqB;YAaf,iCAAiC;IAa/C;;;;OAIG;IACI,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,GAAG,iBAAiB;IAI7F;;;;OAIG;IACI,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAIpF;;OAEG;IACI,WAAW,IAAI,OAAO;IAI7B;;OAEG;IACH,SAAS,CAAC,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC;IAyB5C,SAAS,CAAC,aAAa,UAAW,qBAAqB,UAMrD;IAEF,OAAO,CAAC,uBAAuB,CAiB7B;IAEF,OAAO,CAAC,mCAAmC,CAmBzC;IAEF;;;OAGG;IACI,wBAAwB,IAAI,qBAAqB;CAGzD"}

View File

@@ -0,0 +1,201 @@
import { EventEmitter } from 'fbemitter';
import { MessageFramePacker } from './MessageFramePacker';
import { WebSocketBackingStore } from './WebSocketBackingStore';
import { WebSocketWithReconnect } from './WebSocketWithReconnect';
import { blobToArrayBufferAsync } from './blobUtils';
import * as logger from './logger';
// This version should be synced with the one in the **createMessageSocketEndpoint.ts** in @react-native-community/cli-server-api
export const MESSAGE_PROTOCOL_VERSION = 2;
export const DevToolsPluginMethod = 'Expo:DevToolsPlugin';
/**
* This client is for the Expo DevTools Plugins to communicate between the app and the DevTools webpage hosted in a browser.
* All the code should be both compatible with browsers and React Native.
*/
export class DevToolsPluginClient {
connectionInfo;
options;
eventEmitter = new EventEmitter();
static defaultWSStore = new WebSocketBackingStore();
wsStore = DevToolsPluginClient.defaultWSStore;
isClosed = false;
retries = 0;
useTransportationNext;
messageFramePacker;
constructor(connectionInfo, options) {
this.connectionInfo = connectionInfo;
this.options = options;
this.wsStore = connectionInfo.wsStore || DevToolsPluginClient.defaultWSStore;
this.useTransportationNext = options?.useTransportationNext ?? false;
this.messageFramePacker = this.useTransportationNext ? new MessageFramePacker() : null;
}
/**
* Initialize the connection.
* @hidden
*/
async initAsync() {
if (this.wsStore.ws == null) {
this.wsStore.ws = await this.connectAsync();
}
this.wsStore.refCount += 1;
this.wsStore.ws.addEventListener('message', this.handleMessage);
}
/**
* Close the connection.
*/
async closeAsync() {
this.isClosed = true;
this.wsStore.ws?.removeEventListener('message', this.handleMessage);
this.wsStore.refCount -= 1;
if (this.wsStore.refCount < 1) {
this.wsStore.ws?.close();
this.wsStore.ws = null;
}
this.eventEmitter.removeAllListeners();
}
/**
* Send a message to the other end of DevTools.
* @param method A method name.
* @param params any extra payload.
*/
sendMessage(method, params) {
if (this.wsStore.ws?.readyState === WebSocket.CLOSED) {
logger.warn('Unable to send message in a disconnected state.');
return;
}
if (this.useTransportationNext) {
this.sendMessageImplTransportationNext(method, params);
}
else {
this.sendMessageImplLegacy(method, params);
}
}
sendMessageImplLegacy(method, params) {
const payload = {
version: MESSAGE_PROTOCOL_VERSION,
pluginName: this.connectionInfo.pluginName,
method: DevToolsPluginMethod,
params: {
method,
params,
},
};
this.wsStore.ws?.send(JSON.stringify(payload));
}
async sendMessageImplTransportationNext(method, params) {
if (this.messageFramePacker == null) {
logger.warn('MessageFramePacker is not initialized');
return;
}
const messageKey = {
pluginName: this.connectionInfo.pluginName,
method,
};
const packedData = await this.messageFramePacker.pack({ messageKey, payload: params });
this.wsStore.ws?.send(packedData);
}
/**
* Subscribe to a message from the other end of DevTools.
* @param method Subscribe to a message with a method name.
* @param listener Listener to be called when a message is received.
*/
addMessageListener(method, listener) {
return this.eventEmitter.addListener(method, listener);
}
/**
* Subscribe to a message from the other end of DevTools just once.
* @param method Subscribe to a message with a method name.
* @param listener Listener to be called when a message is received.
*/
addMessageListenerOnce(method, listener) {
this.eventEmitter.once(method, listener);
}
/**
* Returns whether the client is connected to the server.
*/
isConnected() {
return this.wsStore.ws?.readyState === WebSocket.OPEN;
}
/**
* The method to create the WebSocket connection.
*/
connectAsync() {
return new Promise((resolve, reject) => {
const endpoint = this.useTransportationNext ? 'expo-dev-plugins/broadcast' : 'message';
const ws = new WebSocketWithReconnect(`ws://${this.connectionInfo.devServer}/${endpoint}`, {
binaryType: this.options?.websocketBinaryType,
onError: (e) => {
if (e instanceof Error) {
console.warn(`Error happened from the WebSocket connection: ${e.message}\n${e.stack}`);
}
else {
console.warn(`Error happened from the WebSocket connection: ${JSON.stringify(e)}`);
}
},
});
ws.addEventListener('open', () => {
resolve(ws);
});
ws.addEventListener('error', (e) => {
reject(e);
});
ws.addEventListener('close', (e) => {
logger.info('WebSocket closed', e.code, e.reason);
});
});
}
handleMessage = (event) => {
if (this.useTransportationNext) {
this.handleMessageImplTransportationNext(event);
}
else {
this.handleMessageImplLegacy(event);
}
};
handleMessageImplLegacy = (event) => {
let payload;
try {
payload = JSON.parse(event.data);
}
catch (e) {
logger.info('Failed to parse JSON', e);
return;
}
if (payload.version !== MESSAGE_PROTOCOL_VERSION || payload.method !== DevToolsPluginMethod) {
return;
}
if (payload.pluginName && payload.pluginName !== this.connectionInfo.pluginName) {
return;
}
this.eventEmitter.emit(payload.params.method, payload.params.params);
};
handleMessageImplTransportationNext = async (event) => {
if (this.messageFramePacker == null) {
logger.warn('MessageFramePacker is not initialized');
return;
}
let buffer;
if (event.data instanceof ArrayBuffer) {
buffer = event.data;
}
else if (event.data instanceof Blob) {
buffer = await blobToArrayBufferAsync(event.data);
}
else {
logger.warn('Unsupported received data type in handleMessageImplTransportationNext');
return;
}
const { messageKey, payload } = await this.messageFramePacker.unpack(buffer);
if (messageKey.pluginName && messageKey.pluginName !== this.connectionInfo.pluginName) {
return;
}
this.eventEmitter.emit(messageKey.method, payload);
};
/**
* Get the WebSocket backing store. Exposed for testing.
* @hidden
*/
getWebSocketBackingStore() {
return this.wsStore;
}
}
//# sourceMappingURL=DevToolsPluginClient.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
import type { DevToolsPluginClient } from './DevToolsPluginClient';
import type { ConnectionInfo, DevToolsPluginClientOptions } from './devtools.types';
/**
* Factory of DevToolsPluginClient based on sender types.
* @hidden
*/
export declare function createDevToolsPluginClient(connectionInfo: ConnectionInfo, options?: DevToolsPluginClientOptions): Promise<DevToolsPluginClient>;
/**
* Public API to get the DevToolsPluginClient instance.
*/
export declare function getDevToolsPluginClientAsync(pluginName: string, options?: DevToolsPluginClientOptions): Promise<DevToolsPluginClient>;
/**
* Internal testing API to cleanup all DevToolsPluginClient instances.
*/
export declare function cleanupDevToolsPluginInstances(): void;
//# sourceMappingURL=DevToolsPluginClientFactory.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DevToolsPluginClientFactory.d.ts","sourceRoot":"","sources":["../../src/devtools/DevToolsPluginClientFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAKpF;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,cAAc,EAAE,cAAc,EAC9B,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,oBAAoB,CAAC,CAS/B;AAED;;GAEG;AACH,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,oBAAoB,CAAC,CAyB/B;AAED;;GAEG;AACH,wBAAgB,8BAA8B,SAU7C"}

View File

@@ -0,0 +1,60 @@
import { DevToolsPluginClientImplApp } from './DevToolsPluginClientImplApp';
import { DevToolsPluginClientImplBrowser } from './DevToolsPluginClientImplBrowser';
import { getConnectionInfo } from './getConnectionInfo';
const instanceMap = {};
/**
* Factory of DevToolsPluginClient based on sender types.
* @hidden
*/
export async function createDevToolsPluginClient(connectionInfo, options) {
let client;
if (connectionInfo.sender === 'app') {
client = new DevToolsPluginClientImplApp(connectionInfo, options);
}
else {
client = new DevToolsPluginClientImplBrowser(connectionInfo, options);
}
await client.initAsync();
return client;
}
/**
* Public API to get the DevToolsPluginClient instance.
*/
export async function getDevToolsPluginClientAsync(pluginName, options) {
const connectionInfo = getConnectionInfo();
let instance = instanceMap[pluginName];
if (instance != null) {
if (instance instanceof Promise) {
return instance;
}
if (instance.isConnected() === false ||
instance.connectionInfo.devServer !== connectionInfo.devServer) {
await instance.closeAsync();
delete instanceMap[pluginName];
instance = null;
}
}
if (instance == null) {
const instancePromise = createDevToolsPluginClient({ ...connectionInfo, pluginName }, options);
instanceMap[pluginName] = instancePromise;
instance = await instancePromise;
instanceMap[pluginName] = instance;
}
return instance;
}
/**
* Internal testing API to cleanup all DevToolsPluginClient instances.
*/
export function cleanupDevToolsPluginInstances() {
for (const pluginName of Object.keys(instanceMap)) {
const instance = instanceMap[pluginName];
delete instanceMap[pluginName];
if (instance instanceof Promise) {
instance.then((instance) => instance.closeAsync());
}
else {
instance.closeAsync();
}
}
}
//# sourceMappingURL=DevToolsPluginClientFactory.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DevToolsPluginClientFactory.js","sourceRoot":"","sources":["../../src/devtools/DevToolsPluginClientFactory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AAEpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,GAAyE,EAAE,CAAC;AAE7F;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,cAA8B,EAC9B,OAAqC;IAErC,IAAI,MAA4B,CAAC;IACjC,IAAI,cAAc,CAAC,MAAM,KAAK,KAAK,EAAE;QACnC,MAAM,GAAG,IAAI,2BAA2B,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;KACnE;SAAM;QACL,MAAM,GAAG,IAAI,+BAA+B,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;KACvE;IACD,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;IACzB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,UAAkB,EAClB,OAAqC;IAErC,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAC;IAE3C,IAAI,QAAQ,GACV,WAAW,CAAC,UAAU,CAAC,CAAC;IAC1B,IAAI,QAAQ,IAAI,IAAI,EAAE;QACpB,IAAI,QAAQ,YAAY,OAAO,EAAE;YAC/B,OAAO,QAAQ,CAAC;SACjB;QACD,IACE,QAAQ,CAAC,WAAW,EAAE,KAAK,KAAK;YAChC,QAAQ,CAAC,cAAc,CAAC,SAAS,KAAK,cAAc,CAAC,SAAS,EAC9D;YACA,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC;YAC5B,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC;YAC/B,QAAQ,GAAG,IAAI,CAAC;SACjB;KACF;IACD,IAAI,QAAQ,IAAI,IAAI,EAAE;QACpB,MAAM,eAAe,GAAG,0BAA0B,CAAC,EAAE,GAAG,cAAc,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,CAAC;QAC/F,WAAW,CAAC,UAAU,CAAC,GAAG,eAAe,CAAC;QAC1C,QAAQ,GAAG,MAAM,eAAe,CAAC;QACjC,WAAW,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC;KACpC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,8BAA8B;IAC5C,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;QACjD,MAAM,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;QACzC,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC;QAC/B,IAAI,QAAQ,YAAY,OAAO,EAAE;YAC/B,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;SACpD;aAAM;YACL,QAAQ,CAAC,UAAU,EAAE,CAAC;SACvB;KACF;AACH,CAAC","sourcesContent":["import type { DevToolsPluginClient } from './DevToolsPluginClient';\nimport { DevToolsPluginClientImplApp } from './DevToolsPluginClientImplApp';\nimport { DevToolsPluginClientImplBrowser } from './DevToolsPluginClientImplBrowser';\nimport type { ConnectionInfo, DevToolsPluginClientOptions } from './devtools.types';\nimport { getConnectionInfo } from './getConnectionInfo';\n\nconst instanceMap: Record<string, DevToolsPluginClient | Promise<DevToolsPluginClient>> = {};\n\n/**\n * Factory of DevToolsPluginClient based on sender types.\n * @hidden\n */\nexport async function createDevToolsPluginClient(\n connectionInfo: ConnectionInfo,\n options?: DevToolsPluginClientOptions\n): Promise<DevToolsPluginClient> {\n let client: DevToolsPluginClient;\n if (connectionInfo.sender === 'app') {\n client = new DevToolsPluginClientImplApp(connectionInfo, options);\n } else {\n client = new DevToolsPluginClientImplBrowser(connectionInfo, options);\n }\n await client.initAsync();\n return client;\n}\n\n/**\n * Public API to get the DevToolsPluginClient instance.\n */\nexport async function getDevToolsPluginClientAsync(\n pluginName: string,\n options?: DevToolsPluginClientOptions\n): Promise<DevToolsPluginClient> {\n const connectionInfo = getConnectionInfo();\n\n let instance: DevToolsPluginClient | Promise<DevToolsPluginClient> | null =\n instanceMap[pluginName];\n if (instance != null) {\n if (instance instanceof Promise) {\n return instance;\n }\n if (\n instance.isConnected() === false ||\n instance.connectionInfo.devServer !== connectionInfo.devServer\n ) {\n await instance.closeAsync();\n delete instanceMap[pluginName];\n instance = null;\n }\n }\n if (instance == null) {\n const instancePromise = createDevToolsPluginClient({ ...connectionInfo, pluginName }, options);\n instanceMap[pluginName] = instancePromise;\n instance = await instancePromise;\n instanceMap[pluginName] = instance;\n }\n return instance;\n}\n\n/**\n * Internal testing API to cleanup all DevToolsPluginClient instances.\n */\nexport function cleanupDevToolsPluginInstances() {\n for (const pluginName of Object.keys(instanceMap)) {\n const instance = instanceMap[pluginName];\n delete instanceMap[pluginName];\n if (instance instanceof Promise) {\n instance.then((instance) => instance.closeAsync());\n } else {\n instance.closeAsync();\n }\n }\n}\n"]}

View File

@@ -0,0 +1,14 @@
import { DevToolsPluginClient } from './DevToolsPluginClient';
/**
* The DevToolsPluginClient for the app -> browser communication.
*/
export declare class DevToolsPluginClientImplApp extends DevToolsPluginClient {
private browserClientMap;
/**
* Initialize the connection.
* @hidden
*/
initAsync(): Promise<void>;
private addHandshakeHandler;
}
//# sourceMappingURL=DevToolsPluginClientImplApp.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DevToolsPluginClientImplApp.d.ts","sourceRoot":"","sources":["../../src/devtools/DevToolsPluginClientImplApp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAI9D;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,oBAAoB;IAEnE,OAAO,CAAC,gBAAgB,CAA8B;IAEtD;;;OAGG;IACY,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAKzC,OAAO,CAAC,mBAAmB;CAY5B"}

View File

@@ -0,0 +1,28 @@
import { DevToolsPluginClient } from './DevToolsPluginClient';
import * as logger from './logger';
/**
* The DevToolsPluginClient for the app -> browser communication.
*/
export class DevToolsPluginClientImplApp extends DevToolsPluginClient {
// Map of pluginName -> browserClientId
browserClientMap = {};
/**
* Initialize the connection.
* @hidden
*/
async initAsync() {
await super.initAsync();
this.addHandshakeHandler();
}
addHandshakeHandler() {
this.addMessageListener('handshake', (params) => {
const previousBrowserClientId = this.browserClientMap[params.pluginName];
if (previousBrowserClientId != null && previousBrowserClientId !== params.browserClientId) {
logger.info(`Terminate the previous browser client connection - previousBrowserClientId[${previousBrowserClientId}]`);
this.sendMessage('terminateBrowserClient', { browserClientId: previousBrowserClientId });
}
this.browserClientMap[params.pluginName] = params.browserClientId;
});
}
}
//# sourceMappingURL=DevToolsPluginClientImplApp.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DevToolsPluginClientImplApp.js","sourceRoot":"","sources":["../../src/devtools/DevToolsPluginClientImplApp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAEnC;;GAEG;AACH,MAAM,OAAO,2BAA4B,SAAQ,oBAAoB;IACnE,uCAAuC;IAC/B,gBAAgB,GAA2B,EAAE,CAAC;IAEtD;;;OAGG;IACM,KAAK,CAAC,SAAS;QACtB,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;QACxB,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAC7B,CAAC;IAEO,mBAAmB;QACzB,IAAI,CAAC,kBAAkB,CAAC,WAAW,EAAE,CAAC,MAA8B,EAAE,EAAE;YACtE,MAAM,uBAAuB,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACzE,IAAI,uBAAuB,IAAI,IAAI,IAAI,uBAAuB,KAAK,MAAM,CAAC,eAAe,EAAE;gBACzF,MAAM,CAAC,IAAI,CACT,8EAA8E,uBAAuB,GAAG,CACzG,CAAC;gBACF,IAAI,CAAC,WAAW,CAAC,wBAAwB,EAAE,EAAE,eAAe,EAAE,uBAAuB,EAAE,CAAC,CAAC;aAC1F;YACD,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,eAAe,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC;CACF","sourcesContent":["import { DevToolsPluginClient } from './DevToolsPluginClient';\nimport type { HandshakeMessageParams } from './devtools.types';\nimport * as logger from './logger';\n\n/**\n * The DevToolsPluginClient for the app -> browser communication.\n */\nexport class DevToolsPluginClientImplApp extends DevToolsPluginClient {\n // Map of pluginName -> browserClientId\n private browserClientMap: Record<string, string> = {};\n\n /**\n * Initialize the connection.\n * @hidden\n */\n override async initAsync(): Promise<void> {\n await super.initAsync();\n this.addHandshakeHandler();\n }\n\n private addHandshakeHandler() {\n this.addMessageListener('handshake', (params: HandshakeMessageParams) => {\n const previousBrowserClientId = this.browserClientMap[params.pluginName];\n if (previousBrowserClientId != null && previousBrowserClientId !== params.browserClientId) {\n logger.info(\n `Terminate the previous browser client connection - previousBrowserClientId[${previousBrowserClientId}]`\n );\n this.sendMessage('terminateBrowserClient', { browserClientId: previousBrowserClientId });\n }\n this.browserClientMap[params.pluginName] = params.browserClientId;\n });\n }\n}\n"]}

View File

@@ -0,0 +1,14 @@
import { DevToolsPluginClient } from './DevToolsPluginClient';
/**
* The DevToolsPluginClient for the browser -> app communication.
*/
export declare class DevToolsPluginClientImplBrowser extends DevToolsPluginClient {
private browserClientId;
/**
* Initialize the connection.
* @hidden
*/
initAsync(): Promise<void>;
private startHandshake;
}
//# sourceMappingURL=DevToolsPluginClientImplBrowser.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DevToolsPluginClientImplBrowser.d.ts","sourceRoot":"","sources":["../../src/devtools/DevToolsPluginClientImplBrowser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG9D;;GAEG;AACH,qBAAa,+BAAgC,SAAQ,oBAAoB;IACvE,OAAO,CAAC,eAAe,CAAiC;IAExD;;;OAGG;IACY,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAKzC,OAAO,CAAC,cAAc;CAavB"}

View File

@@ -0,0 +1,30 @@
import { DevToolsPluginClient } from './DevToolsPluginClient';
import * as logger from './logger';
/**
* The DevToolsPluginClient for the browser -> app communication.
*/
export class DevToolsPluginClientImplBrowser extends DevToolsPluginClient {
browserClientId = Date.now().toString();
/**
* Initialize the connection.
* @hidden
*/
async initAsync() {
await super.initAsync();
this.startHandshake();
}
startHandshake() {
this.addMessageListener('terminateBrowserClient', (params) => {
if (this.browserClientId !== params.browserClientId) {
return;
}
logger.info('Received terminateBrowserClient messages and terminate the current connection');
this.closeAsync();
});
this.sendMessage('handshake', {
browserClientId: this.browserClientId,
pluginName: this.connectionInfo.pluginName,
});
}
}
//# sourceMappingURL=DevToolsPluginClientImplBrowser.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DevToolsPluginClientImplBrowser.js","sourceRoot":"","sources":["../../src/devtools/DevToolsPluginClientImplBrowser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAEnC;;GAEG;AACH,MAAM,OAAO,+BAAgC,SAAQ,oBAAoB;IAC/D,eAAe,GAAW,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAExD;;;OAGG;IACM,KAAK,CAAC,SAAS;QACtB,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;QACxB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAEO,cAAc;QACpB,IAAI,CAAC,kBAAkB,CAAC,wBAAwB,EAAE,CAAC,MAAM,EAAE,EAAE;YAC3D,IAAI,IAAI,CAAC,eAAe,KAAK,MAAM,CAAC,eAAe,EAAE;gBACnD,OAAO;aACR;YACD,MAAM,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;YAC7F,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE;YAC5B,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,UAAU,EAAE,IAAI,CAAC,cAAc,CAAC,UAAU;SAC3C,CAAC,CAAC;IACL,CAAC;CACF","sourcesContent":["import { DevToolsPluginClient } from './DevToolsPluginClient';\nimport * as logger from './logger';\n\n/**\n * The DevToolsPluginClient for the browser -> app communication.\n */\nexport class DevToolsPluginClientImplBrowser extends DevToolsPluginClient {\n private browserClientId: string = Date.now().toString();\n\n /**\n * Initialize the connection.\n * @hidden\n */\n override async initAsync(): Promise<void> {\n await super.initAsync();\n this.startHandshake();\n }\n\n private startHandshake() {\n this.addMessageListener('terminateBrowserClient', (params) => {\n if (this.browserClientId !== params.browserClientId) {\n return;\n }\n logger.info('Received terminateBrowserClient messages and terminate the current connection');\n this.closeAsync();\n });\n this.sendMessage('handshake', {\n browserClientId: this.browserClientId,\n pluginName: this.connectionInfo.pluginName,\n });\n }\n}\n"]}

View File

@@ -0,0 +1,40 @@
/**
* A message frame packer that serializes a messageKey and a payload into a binary format.
*
* +------------------+-------------------+----------------------------+--------------------------+
* | 4 bytes (Uint32) | Variable length | 1 byte (Uint8) | Variable length |
* | MessageKeyLength | MessageKey (JSON) | PayloadTypeIndicator (enum)| Payload (binary data) |
* +------------------+-------------------+----------------------------+--------------------------+
*
* MessageFrame Format:
*
* 1. MessageKeyLength (4 bytes):
* - A 4-byte unsigned integer indicating the length of the MessageKey JSON string.
*
* 2. MessageKey (Variable length):
* - The JSON string representing the message key, encoded as UTF-8.
*
* 3. PayloadTypeIndicator (1 byte):
* - A single byte enum value representing the type of the payload (e.g., Uint8Array, String, Object, ArrayBuffer, Blob).
*
* 4. Payload (Variable length):
* - The actual payload data, which can vary in type and length depending on the PayloadType.
*
*/
type MessageKeyTypeBase = string | object;
type PayloadType = Uint8Array | string | number | null | undefined | object | ArrayBuffer | Blob;
interface MessageFrame<T extends MessageKeyTypeBase> {
messageKey: T;
payload?: PayloadType;
}
export declare class MessageFramePacker<T extends MessageKeyTypeBase> {
private textEncoder;
private textDecoder;
pack({ messageKey, payload }: MessageFrame<T>): Promise<Uint8Array>;
unpack(packedData: ArrayBuffer): Promise<MessageFrame<T>>;
private payloadToUint8Array;
private deserializePayload;
private static getPayloadTypeIndicator;
}
export {};
//# sourceMappingURL=MessageFramePacker.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"MessageFramePacker.d.ts","sourceRoot":"","sources":["../../src/devtools/MessageFramePacker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,KAAK,kBAAkB,GAAG,MAAM,GAAG,MAAM,CAAC;AAC1C,KAAK,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;AAajG,UAAU,YAAY,CAAC,CAAC,SAAS,kBAAkB;IACjD,UAAU,EAAE,CAAC,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,qBAAa,kBAAkB,CAAC,CAAC,SAAS,kBAAkB;IAC1D,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,WAAW,CAAqB;IAE3B,IAAI,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IA2BnE,MAAM,CAAC,UAAU,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAqBxD,mBAAmB;YAuBnB,kBAAkB;IAqChC,OAAO,CAAC,MAAM,CAAC,uBAAuB;CAqBvC"}

View File

@@ -0,0 +1,167 @@
/**
* A message frame packer that serializes a messageKey and a payload into a binary format.
*
* +------------------+-------------------+----------------------------+--------------------------+
* | 4 bytes (Uint32) | Variable length | 1 byte (Uint8) | Variable length |
* | MessageKeyLength | MessageKey (JSON) | PayloadTypeIndicator (enum)| Payload (binary data) |
* +------------------+-------------------+----------------------------+--------------------------+
*
* MessageFrame Format:
*
* 1. MessageKeyLength (4 bytes):
* - A 4-byte unsigned integer indicating the length of the MessageKey JSON string.
*
* 2. MessageKey (Variable length):
* - The JSON string representing the message key, encoded as UTF-8.
*
* 3. PayloadTypeIndicator (1 byte):
* - A single byte enum value representing the type of the payload (e.g., Uint8Array, String, Object, ArrayBuffer, Blob).
*
* 4. Payload (Variable length):
* - The actual payload data, which can vary in type and length depending on the PayloadType.
*
*/
import { blobToArrayBufferAsync } from './blobUtils';
var PayloadTypeIndicator;
(function (PayloadTypeIndicator) {
PayloadTypeIndicator[PayloadTypeIndicator["Uint8Array"] = 1] = "Uint8Array";
PayloadTypeIndicator[PayloadTypeIndicator["String"] = 2] = "String";
PayloadTypeIndicator[PayloadTypeIndicator["Number"] = 3] = "Number";
PayloadTypeIndicator[PayloadTypeIndicator["Null"] = 4] = "Null";
PayloadTypeIndicator[PayloadTypeIndicator["Undefined"] = 5] = "Undefined";
PayloadTypeIndicator[PayloadTypeIndicator["Object"] = 6] = "Object";
PayloadTypeIndicator[PayloadTypeIndicator["ArrayBuffer"] = 7] = "ArrayBuffer";
PayloadTypeIndicator[PayloadTypeIndicator["Blob"] = 8] = "Blob";
})(PayloadTypeIndicator || (PayloadTypeIndicator = {}));
export class MessageFramePacker {
textEncoder = new TextEncoder();
textDecoder = new TextDecoder();
async pack({ messageKey, payload }) {
const messageKeyString = JSON.stringify(messageKey);
const messageKeyBytes = this.textEncoder.encode(messageKeyString);
const messageKeyLength = messageKeyBytes.length;
const payloadBinary = await this.payloadToUint8Array(payload);
const totalLength = 4 + messageKeyLength + 1 + payloadBinary.byteLength;
const buffer = new ArrayBuffer(totalLength);
const packedArray = new Uint8Array(buffer);
// [0] messageKeyLength (4 bytes)
const messageKeyLengthView = new DataView(buffer, 0, 4);
messageKeyLengthView.setUint32(0, messageKeyLength, false);
// [1] messageKey (variable length)
packedArray.set(messageKeyBytes, 4);
// [2] payloadTypeIndicator (1 byte)
const payloadTypeView = new DataView(buffer, 4 + messageKeyLength, 1);
payloadTypeView.setUint8(0, MessageFramePacker.getPayloadTypeIndicator(payload));
// [3] payload (variable length)
packedArray.set(payloadBinary, 4 + messageKeyLength + 1);
return packedArray;
}
async unpack(packedData) {
// [0] messageKeyLength (4 bytes)
const messageKeyLengthView = new DataView(packedData, 0, 4);
const messageKeyLength = messageKeyLengthView.getUint32(0, false);
// [1] messageKey (variable length)
const messageKeyBytes = packedData.slice(4, 4 + messageKeyLength);
const messageKeyString = this.textDecoder.decode(messageKeyBytes);
const messageKey = JSON.parse(messageKeyString);
// [2] payloadTypeIndicator (1 byte)
const payloadTypeView = new DataView(packedData, 4 + messageKeyLength, 1);
const payloadType = payloadTypeView.getUint8(0);
// [3] payload (variable length)
const payloadBinary = packedData.slice(4 + messageKeyLength + 1);
const payload = await this.deserializePayload(payloadBinary, payloadType);
return { messageKey, payload };
}
async payloadToUint8Array(payload) {
if (payload instanceof Uint8Array) {
return payload;
}
else if (typeof payload === 'string') {
return this.textEncoder.encode(payload);
}
else if (typeof payload === 'number') {
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setFloat64(0, payload, false);
return new Uint8Array(buffer);
}
else if (payload === null) {
return new Uint8Array(0);
}
else if (payload === undefined) {
return new Uint8Array(0);
}
else if (payload instanceof ArrayBuffer) {
return new Uint8Array(payload);
}
else if (payload instanceof Blob) {
return new Uint8Array(await blobToArrayBufferAsync(payload));
}
else {
return this.textEncoder.encode(JSON.stringify(payload));
}
}
async deserializePayload(payloadBinary, payloadTypeIndicator) {
switch (payloadTypeIndicator) {
case PayloadTypeIndicator.Uint8Array: {
return new Uint8Array(payloadBinary);
}
case PayloadTypeIndicator.String: {
return this.textDecoder.decode(payloadBinary);
}
case PayloadTypeIndicator.Number: {
const view = new DataView(payloadBinary);
return view.getFloat64(0, false);
}
case PayloadTypeIndicator.Null: {
return null;
}
case PayloadTypeIndicator.Undefined: {
return undefined;
}
case PayloadTypeIndicator.Object: {
const jsonString = this.textDecoder.decode(payloadBinary);
return JSON.parse(jsonString);
}
case PayloadTypeIndicator.ArrayBuffer: {
return payloadBinary;
}
case PayloadTypeIndicator.Blob: {
// @ts-ignore: We only test ArrayBuffer -> Blob on Node.js runtime
return new Blob([payloadBinary]);
}
default:
throw new Error('Unsupported payload type');
}
}
static getPayloadTypeIndicator(payload) {
if (payload instanceof Uint8Array) {
return PayloadTypeIndicator.Uint8Array;
}
else if (typeof payload === 'string') {
return PayloadTypeIndicator.String;
}
else if (typeof payload === 'number') {
return PayloadTypeIndicator.Number;
}
else if (payload === null) {
return PayloadTypeIndicator.Null;
}
else if (payload === undefined) {
return PayloadTypeIndicator.Undefined;
}
else if (payload instanceof ArrayBuffer) {
return PayloadTypeIndicator.ArrayBuffer;
}
else if (payload instanceof Blob) {
return PayloadTypeIndicator.Blob;
}
else if (typeof payload === 'object') {
return PayloadTypeIndicator.Object;
}
else {
throw new Error('Unsupported payload type');
}
}
}
//# sourceMappingURL=MessageFramePacker.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
/**
* The backing store for the WebSocket connection and reference count.
* This is used for connection multiplexing.
*/
export declare class WebSocketBackingStore {
ws: WebSocket | null;
refCount: number;
constructor(ws?: WebSocket | null, refCount?: number);
}
//# sourceMappingURL=WebSocketBackingStore.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"WebSocketBackingStore.d.ts","sourceRoot":"","sources":["../../src/devtools/WebSocketBackingStore.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,qBAAqB;IAEvB,EAAE,EAAE,SAAS,GAAG,IAAI;IACpB,QAAQ,EAAE,MAAM;gBADhB,EAAE,GAAE,SAAS,GAAG,IAAW,EAC3B,QAAQ,GAAE,MAAU;CAE9B"}

View File

@@ -0,0 +1,13 @@
/**
* The backing store for the WebSocket connection and reference count.
* This is used for connection multiplexing.
*/
export class WebSocketBackingStore {
ws;
refCount;
constructor(ws = null, refCount = 0) {
this.ws = ws;
this.refCount = refCount;
}
}
//# sourceMappingURL=WebSocketBackingStore.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"WebSocketBackingStore.js","sourceRoot":"","sources":["../../src/devtools/WebSocketBackingStore.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,qBAAqB;IAEvB;IACA;IAFT,YACS,KAAuB,IAAI,EAC3B,WAAmB,CAAC;QADpB,OAAE,GAAF,EAAE,CAAyB;QAC3B,aAAQ,GAAR,QAAQ,CAAY;IAC1B,CAAC;CACL","sourcesContent":["/**\n * The backing store for the WebSocket connection and reference count.\n * This is used for connection multiplexing.\n */\nexport class WebSocketBackingStore {\n constructor(\n public ws: WebSocket | null = null,\n public refCount: number = 0\n ) {}\n}\n"]}

View File

@@ -0,0 +1,81 @@
import type { DevToolsPluginClientOptions } from './devtools.types';
export interface Options {
/**
* Reconnect interval in milliseconds.
* @default 1500
*/
retriesInterval?: number;
/**
* The maximum number of retries.
* @default 200
*/
maxRetries?: number;
/**
* The timeout in milliseconds for the WebSocket connecting.
*/
connectTimeout?: number;
/**
* The error handler.
* @default throwing an error
*/
onError?: (error: Error) => void;
/**
* The callback to be called when the WebSocket is reconnected.
* @default no-op
*/
onReconnect?: (reason: string) => void;
/**
* The [`binaryType`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/binaryType).
*/
binaryType?: DevToolsPluginClientOptions['websocketBinaryType'];
}
export declare class WebSocketWithReconnect implements WebSocket {
readonly url: string;
private readonly retriesInterval;
private readonly maxRetries;
private readonly connectTimeout;
private readonly onError;
private readonly onReconnect;
private ws;
private retries;
private connectTimeoutHandle;
private isClosed;
private sendQueue;
private lastCloseEvent;
private readonly emitter;
private readonly eventSubscriptions;
private readonly wsBinaryType?;
constructor(url: string, options?: Options);
close(code?: number, reason?: string): void;
addEventListener(event: 'message', listener: (event: WebSocketMessageEvent) => void): void;
addEventListener(event: 'open', listener: () => void): void;
addEventListener(event: 'error', listener: (event: WebSocketErrorEvent) => void): void;
addEventListener(event: 'close', listener: (event: WebSocketCloseEvent) => void): void;
removeEventListener(event: string, listener: (event: any) => void): void;
private connect;
send(data: string | ArrayBufferView | Blob | ArrayBufferLike): void;
private handleOpen;
private handleMessage;
private handleError;
private handleClose;
private handleConnectTimeout;
private clearConnectTimeoutIfNeeded;
private reconnectIfNeeded;
private wsClose;
get readyState(): number;
readonly CONNECTING = 0;
readonly OPEN = 1;
readonly CLOSING = 2;
readonly CLOSED = 3;
get binaryType(): BinaryType;
get bufferedAmount(): number;
get extensions(): string;
get protocol(): string;
ping(): void;
dispatchEvent(event: Event): boolean;
set onclose(value: ((e: WebSocketCloseEvent) => any) | null);
set onerror(value: ((e: Event) => any) | null);
set onmessage(value: ((e: WebSocketMessageEvent) => any) | null);
set onopen(value: (() => any) | null);
}
//# sourceMappingURL=WebSocketWithReconnect.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"WebSocketWithReconnect.d.ts","sourceRoot":"","sources":["../../src/devtools/WebSocketWithReconnect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAEpE,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAEvC;;OAEG;IACH,UAAU,CAAC,EAAE,2BAA2B,CAAC,qBAAqB,CAAC,CAAC;CACjE;AAED,qBAAa,sBAAuB,YAAW,SAAS;aAmBpC,GAAG,EAAE,MAAM;IAlB7B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2B;IAEvD,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,oBAAoB,CAA8C;IAC1E,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAA6D;IAC9E,OAAO,CAAC,cAAc,CAAqE;IAE3F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA2B;IAC9D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAwB;gBAGpC,GAAG,EAAE,MAAM,EAC3B,OAAO,CAAC,EAAE,OAAO;IAgBZ,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAqBpC,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,GAAG,IAAI;IAC1F,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAC3D,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,IAAI;IACtF,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,IAAI;IAKtF,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI;IAYxE,OAAO,CAAC,OAAO;IAiBR,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,GAAG,eAAe,GAAG,IAAI;IAqB1E,OAAO,CAAC,UAAU,CAUhB;IAEF,OAAO,CAAC,aAAa,CAEnB;IAEF,OAAO,CAAC,WAAW,CAIjB;IAEF,OAAO,CAAC,WAAW,CAQjB;IAEF,OAAO,CAAC,oBAAoB,CAE1B;IAEF,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,OAAO;IAWf,IAAW,UAAU,WAWpB;IAMD,SAAgB,UAAU,KAAK;IAC/B,SAAgB,IAAI,KAAK;IACzB,SAAgB,OAAO,KAAK;IAC5B,SAAgB,MAAM,KAAK;IAE3B,IAAW,UAAU,eAEpB;IAED,IAAW,cAAc,WAExB;IAED,IAAW,UAAU,WAEpB;IAED,IAAW,QAAQ,WAElB;IAEM,IAAI,IAAI,IAAI;IAIZ,aAAa,CAAC,KAAK,EAAE,KAAK;IAQjC,IAAW,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,mBAAmB,KAAK,GAAG,CAAC,GAAG,IAAI,EAEjE;IAED,IAAW,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,GAAG,CAAC,GAAG,IAAI,EAEnD;IAED,IAAW,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,qBAAqB,KAAK,GAAG,CAAC,GAAG,IAAI,EAErE;IAED,IAAW,MAAM,CAAC,KAAK,EAAE,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,EAE1C;CAGF"}

View File

@@ -0,0 +1,208 @@
import { EventEmitter } from 'fbemitter';
export class WebSocketWithReconnect {
url;
retriesInterval;
maxRetries;
connectTimeout;
onError;
onReconnect;
ws = null;
retries = 0;
connectTimeoutHandle = null;
isClosed = false;
sendQueue = [];
lastCloseEvent = null;
emitter = new EventEmitter();
eventSubscriptions = [];
wsBinaryType;
constructor(url, options) {
this.url = url;
this.retriesInterval = options?.retriesInterval ?? 1500;
this.maxRetries = options?.maxRetries ?? 200;
this.connectTimeout = options?.connectTimeout ?? 5000;
this.onError =
options?.onError ??
((error) => {
throw error;
});
this.onReconnect = options?.onReconnect ?? (() => { });
this.wsBinaryType = options?.binaryType;
this.connect();
}
close(code, reason) {
this.clearConnectTimeoutIfNeeded();
this.emitter.emit('close', this.lastCloseEvent ?? {
code: code ?? 1000,
reason: reason ?? 'Explicit closing',
message: 'Explicit closing',
});
this.lastCloseEvent = null;
this.isClosed = true;
this.emitter.removeAllListeners();
this.sendQueue = [];
if (this.ws != null) {
const ws = this.ws;
this.ws = null;
this.wsClose(ws);
}
}
addEventListener(event, listener) {
this.eventSubscriptions.push(this.emitter.addListener(event, listener));
}
removeEventListener(event, listener) {
const index = this.eventSubscriptions.findIndex((subscription) => subscription.listener === listener);
if (index >= 0) {
this.eventSubscriptions[index].remove();
this.eventSubscriptions.splice(index, 1);
}
}
//#region Internals
connect() {
if (this.ws != null) {
return;
}
this.connectTimeoutHandle = setTimeout(this.handleConnectTimeout, this.connectTimeout);
this.ws = new WebSocket(this.url.toString());
if (this.wsBinaryType != null) {
this.ws.binaryType = this.wsBinaryType;
}
this.ws.addEventListener('message', this.handleMessage);
this.ws.addEventListener('open', this.handleOpen);
// @ts-ignore TypeScript expects (e: Event) => any, but we want (e: WebSocketErrorEvent) => any
this.ws.addEventListener('error', this.handleError);
this.ws.addEventListener('close', this.handleClose);
}
send(data) {
if (this.isClosed) {
this.onError(new Error('Unable to send data: WebSocket is closed'));
return;
}
if (this.retries >= this.maxRetries) {
this.onError(new Error(`Unable to send data: Exceeded max retries - retries[${this.retries}]`));
return;
}
const ws = this.ws;
if (ws != null && ws.readyState === WebSocket.OPEN) {
ws.send(data);
}
else {
this.sendQueue.push(data);
}
}
handleOpen = () => {
this.clearConnectTimeoutIfNeeded();
this.lastCloseEvent = null;
this.emitter.emit('open');
const sendQueue = this.sendQueue;
this.sendQueue = [];
for (const data of sendQueue) {
this.send(data);
}
};
handleMessage = (event) => {
this.emitter.emit('message', event);
};
handleError = (event) => {
this.clearConnectTimeoutIfNeeded();
this.emitter.emit('error', event);
this.reconnectIfNeeded(`WebSocket error - ${event.message}`);
};
handleClose = (event) => {
this.clearConnectTimeoutIfNeeded();
this.lastCloseEvent = {
code: event.code,
reason: event.reason,
message: event.message,
};
this.reconnectIfNeeded(`WebSocket closed - code[${event.code}] reason[${event.reason}]`);
};
handleConnectTimeout = () => {
this.reconnectIfNeeded('Timeout from connecting to the WebSocket');
};
clearConnectTimeoutIfNeeded() {
if (this.connectTimeoutHandle != null) {
clearTimeout(this.connectTimeoutHandle);
this.connectTimeoutHandle = null;
}
}
reconnectIfNeeded(reason) {
if (this.ws != null) {
this.wsClose(this.ws);
this.ws = null;
}
if (this.isClosed) {
return;
}
if (this.retries >= this.maxRetries) {
this.onError(new Error('Exceeded max retries'));
this.close();
return;
}
setTimeout(() => {
this.retries += 1;
this.connect();
this.onReconnect(reason);
}, this.retriesInterval);
}
wsClose(ws) {
try {
ws.removeEventListener('message', this.handleMessage);
ws.removeEventListener('open', this.handleOpen);
// @ts-ignore: TypeScript expects (e: Event) => any, but we want (e: WebSocketErrorEvent) => any
ws.removeEventListener('error', this.handleError);
ws.removeEventListener('close', this.handleClose);
ws.close();
}
catch { }
}
get readyState() {
// Only return closed if the WebSocket is explicitly closed or exceeds max retries.
if (this.isClosed) {
return WebSocket.CLOSED;
}
const readyState = this.ws?.readyState;
if (readyState === WebSocket.CLOSED) {
return WebSocket.CONNECTING;
}
return readyState ?? WebSocket.CONNECTING;
}
//#endregion
//#region WebSocket API proxy
CONNECTING = 0;
OPEN = 1;
CLOSING = 2;
CLOSED = 3;
get binaryType() {
return this.ws?.binaryType ?? 'blob';
}
get bufferedAmount() {
return this.ws?.bufferedAmount ?? 0;
}
get extensions() {
return this.ws?.extensions ?? '';
}
get protocol() {
return this.ws?.protocol ?? '';
}
ping() {
return this.ws?.ping();
}
dispatchEvent(event) {
return this.ws?.dispatchEvent(event) ?? false;
}
//#endregion
//#regions Unsupported legacy properties
set onclose(value) {
throw new Error('Unsupported legacy property, use addEventListener instead');
}
set onerror(value) {
throw new Error('Unsupported legacy property, use addEventListener instead');
}
set onmessage(value) {
throw new Error('Unsupported legacy property, use addEventListener instead');
}
set onopen(value) {
throw new Error('Unsupported legacy property, use addEventListener instead');
}
}
//# sourceMappingURL=WebSocketWithReconnect.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
/**
* Converts a Blob to an ArrayBuffer.
*/
export declare function blobToArrayBufferAsync(blob: Blob): Promise<ArrayBuffer>;
/**
* Converts a Blob to an ArrayBuffer using the FileReader API.
*/
export declare function legacyBlobToArrayBufferAsync(blob: Blob): Promise<ArrayBuffer>;
//# sourceMappingURL=blobUtils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"blobUtils.d.ts","sourceRoot":"","sources":["../../src/devtools/blobUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAKvE;AAED;;GAEG;AACH,wBAAsB,4BAA4B,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CASnF"}

View File

@@ -0,0 +1,23 @@
/**
* Converts a Blob to an ArrayBuffer.
*/
export function blobToArrayBufferAsync(blob) {
if (typeof blob.arrayBuffer === 'function') {
return blob.arrayBuffer();
}
return legacyBlobToArrayBufferAsync(blob);
}
/**
* Converts a Blob to an ArrayBuffer using the FileReader API.
*/
export async function legacyBlobToArrayBufferAsync(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = reject;
reader.readAsArrayBuffer(blob);
});
}
//# sourceMappingURL=blobUtils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"blobUtils.js","sourceRoot":"","sources":["../../src/devtools/blobUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAU;IAC/C,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,UAAU,EAAE;QAC1C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;KAC3B;IACD,OAAO,4BAA4B,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,IAAU;IAC3D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE;YACnB,OAAO,CAAC,MAAM,CAAC,MAAqB,CAAC,CAAC;QACxC,CAAC,CAAC;QACF,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC;QACxB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Converts a Blob to an ArrayBuffer.\n */\nexport function blobToArrayBufferAsync(blob: Blob): Promise<ArrayBuffer> {\n if (typeof blob.arrayBuffer === 'function') {\n return blob.arrayBuffer();\n }\n return legacyBlobToArrayBufferAsync(blob);\n}\n\n/**\n * Converts a Blob to an ArrayBuffer using the FileReader API.\n */\nexport async function legacyBlobToArrayBufferAsync(blob: Blob): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n resolve(reader.result as ArrayBuffer);\n };\n reader.onerror = reject;\n reader.readAsArrayBuffer(blob);\n });\n}\n"]}

View File

@@ -0,0 +1,40 @@
import type { WebSocketBackingStore } from './WebSocketBackingStore';
/**
* The connection info for devtools plugins client.
*/
export interface ConnectionInfo {
/** Indicates the sender towards the devtools plugin. */
sender: 'app' | 'browser';
/** Dev server address. */
devServer: string;
/** The plugin name. */
pluginName: string;
/**
* The backing store for the WebSocket connection. Exposed for testing.
* If not provided, the default singleton instance will be used.
* @hidden
*/
wsStore?: WebSocketBackingStore;
}
/**
* Parameters for the `handshake` message.
* @hidden
*/
export interface HandshakeMessageParams {
browserClientId: string;
pluginName: string;
}
/**
* Options for the devtools plugin client.
*/
export interface DevToolsPluginClientOptions {
/**
* The underlying WebSocket [`binaryType`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/binaryType).
*/
websocketBinaryType?: 'arraybuffer' | 'blob';
/**
* Whether to use the new transportation protocol.
*/
useTransportationNext?: boolean;
}
//# sourceMappingURL=devtools.types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"devtools.types.d.ts","sourceRoot":"","sources":["../../src/devtools/devtools.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,MAAM,EACF,KAAK,GACL,SAAS,CAAC;IAEd,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAElB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,qBAAqB,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,mBAAmB,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IAE7C;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=devtools.types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"devtools.types.js","sourceRoot":"","sources":["../../src/devtools/devtools.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { WebSocketBackingStore } from './WebSocketBackingStore';\n\n/**\n * The connection info for devtools plugins client.\n */\nexport interface ConnectionInfo {\n /** Indicates the sender towards the devtools plugin. */\n sender:\n | 'app' // client running in the app environment.\n | 'browser'; // client running in the browser environment.\n\n /** Dev server address. */\n devServer: string;\n\n /** The plugin name. */\n pluginName: string;\n\n /**\n * The backing store for the WebSocket connection. Exposed for testing.\n * If not provided, the default singleton instance will be used.\n * @hidden\n */\n wsStore?: WebSocketBackingStore;\n}\n\n/**\n * Parameters for the `handshake` message.\n * @hidden\n */\nexport interface HandshakeMessageParams {\n browserClientId: string;\n pluginName: string;\n}\n\n/**\n * Options for the devtools plugin client.\n */\nexport interface DevToolsPluginClientOptions {\n /**\n * The underlying WebSocket [`binaryType`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/binaryType).\n */\n websocketBinaryType?: 'arraybuffer' | 'blob';\n\n /**\n * Whether to use the new transportation protocol.\n */\n useTransportationNext?: boolean;\n}\n"]}

View File

@@ -0,0 +1,6 @@
/**
* Get the dev server address.
*/
import type { ConnectionInfo } from './devtools.types';
export declare function getConnectionInfo(): Omit<ConnectionInfo, 'pluginName'>;
//# sourceMappingURL=getConnectionInfo.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getConnectionInfo.d.ts","sourceRoot":"","sources":["../../src/devtools/getConnectionInfo.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,wBAAgB,iBAAiB,IAAI,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAOtE"}

View File

@@ -0,0 +1,12 @@
/**
* Get the dev server address.
*/
export function getConnectionInfo() {
const devServerQuery = new URLSearchParams(window.location.search).get('devServer');
const host = window.location.origin.replace(/^https?:\/\//, '');
return {
sender: 'browser',
devServer: devServerQuery || host,
};
}
//# sourceMappingURL=getConnectionInfo.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getConnectionInfo.js","sourceRoot":"","sources":["../../src/devtools/getConnectionInfo.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,cAAc,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAChE,OAAO;QACL,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,cAAc,IAAI,IAAI;KAClC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Get the dev server address.\n */\n\nimport type { ConnectionInfo } from './devtools.types';\n\nexport function getConnectionInfo(): Omit<ConnectionInfo, 'pluginName'> {\n const devServerQuery = new URLSearchParams(window.location.search).get('devServer');\n const host = window.location.origin.replace(/^https?:\\/\\//, '');\n return {\n sender: 'browser',\n devServer: devServerQuery || host,\n };\n}\n"]}

View File

@@ -0,0 +1,6 @@
/**
* Get the dev server address.
*/
import type { ConnectionInfo } from './devtools.types';
export declare function getConnectionInfo(): Omit<ConnectionInfo, 'pluginName'>;
//# sourceMappingURL=getConnectionInfo.native.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getConnectionInfo.native.d.ts","sourceRoot":"","sources":["../../src/devtools/getConnectionInfo.native.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,wBAAgB,iBAAiB,IAAI,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAStE"}

View File

@@ -0,0 +1,14 @@
/**
* Get the dev server address.
*/
export function getConnectionInfo() {
const getDevServer = require('react-native/Libraries/Core/Devtools/getDevServer');
const devServer = getDevServer()
.url.replace(/^https?:\/\//, '')
.replace(/\/?$/, '');
return {
sender: 'app',
devServer,
};
}
//# sourceMappingURL=getConnectionInfo.native.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getConnectionInfo.native.js","sourceRoot":"","sources":["../../src/devtools/getConnectionInfo.native.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,YAAY,GAAG,OAAO,CAAC,mDAAmD,CAAC,CAAC;IAClF,MAAM,SAAS,GAAG,YAAY,EAAE;SAC7B,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;SAC/B,OAAO,CAAC,MAAM,EAAE,EAAE,CAAW,CAAC;IACjC,OAAO;QACL,MAAM,EAAE,KAAK;QACb,SAAS;KACV,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Get the dev server address.\n */\n\nimport type { ConnectionInfo } from './devtools.types';\n\nexport function getConnectionInfo(): Omit<ConnectionInfo, 'pluginName'> {\n const getDevServer = require('react-native/Libraries/Core/Devtools/getDevServer');\n const devServer = getDevServer()\n .url.replace(/^https?:\\/\\//, '')\n .replace(/\\/?$/, '') as string;\n return {\n sender: 'app',\n devServer,\n };\n}\n"]}

View File

@@ -0,0 +1,12 @@
import { DevToolsPluginClient } from './DevToolsPluginClient';
import { getDevToolsPluginClientAsync } from './DevToolsPluginClientFactory';
import type { DevToolsPluginClientOptions } from './devtools.types';
export { getDevToolsPluginClientAsync, DevToolsPluginClient };
export type { DevToolsPluginClientOptions };
export type { EventSubscription } from 'fbemitter';
export { setEnableLogging } from './logger';
/**
* A React hook to get the DevToolsPluginClient instance.
*/
export declare function useDevToolsPluginClient(pluginName: string, options?: DevToolsPluginClientOptions): DevToolsPluginClient | null;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/devtools/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,CAAC;AAC9D,YAAY,EAAE,2BAA2B,EAAE,CAAC;AAE5C,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,2BAA2B,GACpC,oBAAoB,GAAG,IAAI,CAkC7B"}

View File

@@ -0,0 +1,40 @@
import { useState, useEffect } from 'react';
import { DevToolsPluginClient } from './DevToolsPluginClient';
import { getDevToolsPluginClientAsync } from './DevToolsPluginClientFactory';
export { getDevToolsPluginClientAsync, DevToolsPluginClient };
export { setEnableLogging } from './logger';
/**
* A React hook to get the DevToolsPluginClient instance.
*/
export function useDevToolsPluginClient(pluginName, options) {
const [client, setClient] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
async function setup() {
try {
const client = await getDevToolsPluginClientAsync(pluginName, options);
setClient(client);
}
catch (e) {
setError(new Error('Failed to setup client from useDevToolsPluginClient: ' + e.toString()));
}
}
async function teardown() {
try {
await client?.closeAsync();
}
catch (e) {
setError(new Error('Failed to teardown client from useDevToolsPluginClient: ' + e.toString()));
}
}
setup();
return () => {
teardown();
};
}, [pluginName]);
if (error != null) {
throw error;
}
return client;
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/devtools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAG7E,OAAO,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,CAAC;AAI9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C;;GAEG;AACH,MAAM,UAAU,uBAAuB,CACrC,UAAkB,EAClB,OAAqC;IAErC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAA8B,IAAI,CAAC,CAAC;IACxE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,UAAU,KAAK;YAClB,IAAI;gBACF,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBACvE,SAAS,CAAC,MAAM,CAAC,CAAC;aACnB;YAAC,OAAO,CAAC,EAAE;gBACV,QAAQ,CAAC,IAAI,KAAK,CAAC,uDAAuD,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;aAC7F;QACH,CAAC;QAED,KAAK,UAAU,QAAQ;YACrB,IAAI;gBACF,MAAM,MAAM,EAAE,UAAU,EAAE,CAAC;aAC5B;YAAC,OAAO,CAAC,EAAE;gBACV,QAAQ,CACN,IAAI,KAAK,CAAC,0DAA0D,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CACrF,CAAC;aACH;QACH,CAAC;QAED,KAAK,EAAE,CAAC;QACR,OAAO,GAAG,EAAE;YACV,QAAQ,EAAE,CAAC;QACb,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,IAAI,KAAK,IAAI,IAAI,EAAE;QACjB,MAAM,KAAK,CAAC;KACb;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { useState, useEffect } from 'react';\n\nimport { DevToolsPluginClient } from './DevToolsPluginClient';\nimport { getDevToolsPluginClientAsync } from './DevToolsPluginClientFactory';\nimport type { DevToolsPluginClientOptions } from './devtools.types';\n\nexport { getDevToolsPluginClientAsync, DevToolsPluginClient };\nexport type { DevToolsPluginClientOptions };\n// Export the EventSubscription type if people need to use explicit type from `addMessageListener`\nexport type { EventSubscription } from 'fbemitter';\nexport { setEnableLogging } from './logger';\n\n/**\n * A React hook to get the DevToolsPluginClient instance.\n */\nexport function useDevToolsPluginClient(\n pluginName: string,\n options?: DevToolsPluginClientOptions\n): DevToolsPluginClient | null {\n const [client, setClient] = useState<DevToolsPluginClient | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n async function setup() {\n try {\n const client = await getDevToolsPluginClientAsync(pluginName, options);\n setClient(client);\n } catch (e) {\n setError(new Error('Failed to setup client from useDevToolsPluginClient: ' + e.toString()));\n }\n }\n\n async function teardown() {\n try {\n await client?.closeAsync();\n } catch (e) {\n setError(\n new Error('Failed to teardown client from useDevToolsPluginClient: ' + e.toString())\n );\n }\n }\n\n setup();\n return () => {\n teardown();\n };\n }, [pluginName]);\n\n if (error != null) {\n throw error;\n }\n return client;\n}\n"]}

View File

@@ -0,0 +1,6 @@
export declare function log(...params: Parameters<typeof console.log>): void;
export declare function debug(...params: Parameters<typeof console.debug>): void;
export declare function info(...params: Parameters<typeof console.info>): void;
export declare function warn(...params: Parameters<typeof console.info>): void;
export declare function setEnableLogging(enabled: boolean): void;
//# sourceMappingURL=logger.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/devtools/logger.ts"],"names":[],"mappings":"AAEA,wBAAgB,GAAG,CAAC,GAAG,MAAM,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC,QAI5D;AAED,wBAAgB,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,KAAK,CAAC,QAIhE;AAED,wBAAgB,IAAI,CAAC,GAAG,MAAM,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,IAAI,CAAC,QAI9D;AAED,wBAAgB,IAAI,CAAC,GAAG,MAAM,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,IAAI,CAAC,QAI9D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,QAEhD"}

View File

@@ -0,0 +1,25 @@
let enableLogging = false;
export function log(...params) {
if (enableLogging) {
console.log(...params);
}
}
export function debug(...params) {
if (enableLogging) {
console.debug(...params);
}
}
export function info(...params) {
if (enableLogging) {
console.info(...params);
}
}
export function warn(...params) {
if (enableLogging) {
console.warn(...params);
}
}
export function setEnableLogging(enabled) {
enableLogging = enabled;
}
//# sourceMappingURL=logger.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/devtools/logger.ts"],"names":[],"mappings":"AAAA,IAAI,aAAa,GAAG,KAAK,CAAC;AAE1B,MAAM,UAAU,GAAG,CAAC,GAAG,MAAsC;IAC3D,IAAI,aAAa,EAAE;QACjB,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;KACxB;AACH,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAG,MAAwC;IAC/D,IAAI,aAAa,EAAE;QACjB,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC;KAC1B;AACH,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,GAAG,MAAuC;IAC7D,IAAI,aAAa,EAAE;QACjB,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;KACzB;AACH,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,GAAG,MAAuC;IAC7D,IAAI,aAAa,EAAE;QACjB,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;KACzB;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,aAAa,GAAG,OAAO,CAAC;AAC1B,CAAC","sourcesContent":["let enableLogging = false;\n\nexport function log(...params: Parameters<typeof console.log>) {\n if (enableLogging) {\n console.log(...params);\n }\n}\n\nexport function debug(...params: Parameters<typeof console.debug>) {\n if (enableLogging) {\n console.debug(...params);\n }\n}\n\nexport function info(...params: Parameters<typeof console.info>) {\n if (enableLogging) {\n console.info(...params);\n }\n}\n\nexport function warn(...params: Parameters<typeof console.info>) {\n if (enableLogging) {\n console.warn(...params);\n }\n}\n\nexport function setEnableLogging(enabled: boolean) {\n enableLogging = enabled;\n}\n"]}