From 39b1b37172c45bbe11b83b13c462398d5e4554ff Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 7 Nov 2023 10:57:06 -0800 Subject: [PATCH 001/112] Off vpn error banner Summary: Some features of bloks do not work off vpn. Until we figure out how to make them off vpn Reviewed By: LukeDefeo Differential Revision: D51076469 fbshipit-source-id: c83d96e89d33d245845312b39928a7460a235217 --- desktop/flipper-common/src/server-types.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index cb285b123..fafae066b 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -371,6 +371,7 @@ export type FlipperServerCommands = { timeout?: number; internGraphUrl?: string; headers?: Record; + vpnMode?: 'vpn' | 'vpnless'; }, ) => Promise; 'intern-upload-scribe-logs': ( From 3536ffe73706b2d4907bdad930c99b0ad77195ba Mon Sep 17 00:00:00 2001 From: Aria Fallah Date: Tue, 7 Nov 2023 11:09:10 -0800 Subject: [PATCH 002/112] Add worker plugin to plugin esbuild Summary: ## Context https://fb.workplace.com/groups/flippersupport/posts/1722856878194963 ## Changes * Add a worker plugin that takes modules suffixed with `?worker`, bundles them, treats them as web workers, and returns a function as a default export that instanitates a new worker Reviewed By: antonk52 Differential Revision: D51059224 fbshipit-source-id: cef89486f7a2d5b8ce38354df4a5749271a6c41d --- desktop/pkg-lib/src/runBuild.tsx | 36 +++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/desktop/pkg-lib/src/runBuild.tsx b/desktop/pkg-lib/src/runBuild.tsx index ac07f17f4..020d5329a 100644 --- a/desktop/pkg-lib/src/runBuild.tsx +++ b/desktop/pkg-lib/src/runBuild.tsx @@ -28,6 +28,40 @@ const resolveFbStubsToFbPlugin: Plugin = { }, }; +const workerPlugin: Plugin = { + name: 'worker-plugin', + setup({onResolve, onLoad}) { + onResolve({filter: /\?worker$/}, (args) => { + return { + path: require.resolve(args.path.slice(0, -7), { + paths: [args.resolveDir], + }), + namespace: 'worker', + }; + }); + + onLoad({filter: /.*/, namespace: 'worker'}, async (args) => { + // Bundle the worker file + const result = await build({ + entryPoints: [args.path], + bundle: true, + write: false, + format: 'iife', + platform: 'browser', + }); + + const dataUri = `data:text/javascript;base64,${Buffer.from( + result.outputFiles[0].text, + ).toString('base64')}`; + + return { + contents: `export default function() { return new Worker("${dataUri}"); }`, + loader: 'js', + }; + }); + }, +}; + interface RunBuildConfig { pluginDir: string; entry: string; @@ -73,7 +107,7 @@ async function runBuild({ ], sourcemap: dev ? 'inline' : 'external', minify: !dev, - plugins: intern ? [resolveFbStubsToFbPlugin] : undefined, + plugins: [workerPlugin, ...(intern ? [resolveFbStubsToFbPlugin] : [])], loader: { '.ttf': 'dataurl', }, From da5856138ddd25e8f56db8a3fac6fb975c990cb1 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 8 Nov 2023 02:08:25 -0800 Subject: [PATCH 003/112] Add event emitter to datasource Reviewed By: LukeDefeo Differential Revision: D51026560 fbshipit-source-id: 8348c6765633d7eecf0d1c80bc5bbd5af8130298 --- desktop/flipper-plugin-core/src/data-source/DataSource.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/desktop/flipper-plugin-core/src/data-source/DataSource.tsx b/desktop/flipper-plugin-core/src/data-source/DataSource.tsx index 75444ea62..3dd7e94bb 100644 --- a/desktop/flipper-plugin-core/src/data-source/DataSource.tsx +++ b/desktop/flipper-plugin-core/src/data-source/DataSource.tsx @@ -11,6 +11,7 @@ import sortedIndexBy from 'lodash/sortedIndexBy'; import sortedLastIndexBy from 'lodash/sortedLastIndexBy'; import property from 'lodash/property'; import lodashSort from 'lodash/sortBy'; +import EventEmitter from 'eventemitter3'; // If the dataSource becomes to large, after how many records will we start to drop items? const dropFactor = 0.1; @@ -180,6 +181,8 @@ export class DataSource { [viewId: string]: DataSourceView; }; + public readonly outputEventEmitter = new EventEmitter(); + constructor( keyAttribute: keyof T | undefined, secondaryIndices: IndexDefinition[] = [], @@ -550,6 +553,7 @@ export class DataSource { Object.entries(this.additionalViews).forEach(([, dataView]) => { dataView.processEvent(event); }); + this.outputEventEmitter.emit(event.type, event); } private storeSecondaryIndices(value: T) { From 701ae01501e3c033a0f9f00dfc17f3a5b893cb9d Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 8 Nov 2023 02:08:25 -0800 Subject: [PATCH 004/112] Support tracking secondary indecies in DataSource Reviewed By: LukeDefeo Differential Revision: D51026559 fbshipit-source-id: 1f8f40ceedf70dfdc8978e0d6e447a1a58f8f82a --- .../src/data-source/DataSource.tsx | 52 ++++++++++++- .../__tests__/datasource-basics.node.tsx | 75 +++++++++++++++++++ 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/desktop/flipper-plugin-core/src/data-source/DataSource.tsx b/desktop/flipper-plugin-core/src/data-source/DataSource.tsx index 3dd7e94bb..cb7dc2041 100644 --- a/desktop/flipper-plugin-core/src/data-source/DataSource.tsx +++ b/desktop/flipper-plugin-core/src/data-source/DataSource.tsx @@ -46,12 +46,23 @@ type ShiftEvent = { entries: Entry[]; amount: number; }; +type SINewIndexValueEvent = { + type: 'siNewIndexValue'; + indexKey: string; + value: T; + firstOfKind: boolean; +}; +type ClearEvent = { + type: 'clear'; +}; type DataEvent = | AppendEvent | UpdateEvent | RemoveEvent - | ShiftEvent; + | ShiftEvent + | SINewIndexValueEvent + | ClearEvent; type Entry = { value: T; @@ -181,7 +192,7 @@ export class DataSource { [viewId: string]: DataSourceView; }; - public readonly outputEventEmitter = new EventEmitter(); + private readonly outputEventEmitter = new EventEmitter(); constructor( keyAttribute: keyof T | undefined, @@ -262,6 +273,10 @@ export class DataSource { }; } + public secondaryIndicesKeys(): string[] { + return [...this._secondaryIndices.keys()]; + } + /** * Returns the index of a specific key in the *records* set. * Returns -1 if the record wansn't found @@ -469,6 +484,7 @@ export class DataSource { this.shiftOffset = 0; this.idToIndex.clear(); this.rebuild(); + this.emitDataEvent({type: 'clear'}); } /** @@ -522,6 +538,16 @@ export class DataSource { } } + public addDataListener['type']>( + event: E, + cb: (data: Extract, {type: E}>) => void, + ) { + this.outputEventEmitter.addListener(event, cb); + return () => { + this.outputEventEmitter.removeListener(event, cb); + }; + } + private assertKeySet() { if (!this.keyAttribute) { throw new Error( @@ -571,6 +597,12 @@ export class DataSource { } else { a.push(value); } + this.emitDataEvent({ + type: 'siNewIndexValue', + indexKey: indexValue, + value, + firstOfKind: !a, + }); } } @@ -631,11 +663,21 @@ export class DataSource { return this.getAllRecordsByIndex(indexQuery)[0]; } + public getAllIndexValues(index: IndexDefinition) { + const sortedKeys = index.slice().sort(); + const indexKey = sortedKeys.join(':'); + const recordsByIndex = this._recordsBySecondaryIndex.get(indexKey); + if (!recordsByIndex) { + return; + } + return [...recordsByIndex.keys()]; + } + private getSecondaryIndexValueFromRecord( record: T, // assumes keys is already ordered keys: IndexDefinition, - ): any { + ): string { return JSON.stringify( Object.fromEntries(keys.map((k) => [k, String(record[k])])), ); @@ -993,6 +1035,10 @@ export class DataSourceView { } break; } + case 'clear': + case 'siNewIndexValue': { + break; + } default: throw new Error('unknown event type'); } diff --git a/desktop/flipper-plugin-core/src/data-source/__tests__/datasource-basics.node.tsx b/desktop/flipper-plugin-core/src/data-source/__tests__/datasource-basics.node.tsx index 20088bf10..0fb0a4705 100644 --- a/desktop/flipper-plugin-core/src/data-source/__tests__/datasource-basics.node.tsx +++ b/desktop/flipper-plugin-core/src/data-source/__tests__/datasource-basics.node.tsx @@ -912,6 +912,13 @@ test('secondary keys - lookup by single key', () => { indices: [['id'], ['title'], ['done']], }); + expect(ds.secondaryIndicesKeys()).toEqual(['id', 'title', 'done']); + expect(ds.getAllIndexValues(['id'])).toEqual([ + JSON.stringify({id: 'cookie'}), + JSON.stringify({id: 'coffee'}), + JSON.stringify({id: 'bug'}), + ]); + expect( ds.getAllRecordsByIndex({ title: 'eat a cookie', @@ -938,6 +945,12 @@ test('secondary keys - lookup by single key', () => { }), ).toEqual(submitBug); + expect(ds.getAllIndexValues(['id'])).toEqual([ + JSON.stringify({id: 'cookie'}), + JSON.stringify({id: 'coffee'}), + JSON.stringify({id: 'bug'}), + ]); + ds.delete(0); // eat Cookie expect( ds.getAllRecordsByIndex({ @@ -945,6 +958,13 @@ test('secondary keys - lookup by single key', () => { }), ).toEqual([cookie2]); + // We do not remove empty index values (for now) + expect(ds.getAllIndexValues(['id'])).toEqual([ + JSON.stringify({id: 'cookie'}), + JSON.stringify({id: 'coffee'}), + JSON.stringify({id: 'bug'}), + ]); + // replace submit Bug const n = { id: 'bug', @@ -972,6 +992,12 @@ test('secondary keys - lookup by single key', () => { title: 'eat a cookie', }), ).toEqual([cookie2]); + + expect(ds.getAllIndexValues(['id'])).toEqual([ + JSON.stringify({id: 'cookie'}), + JSON.stringify({id: 'coffee'}), + JSON.stringify({id: 'bug'}), + ]); }); test('secondary keys - lookup by combined keys', () => { @@ -983,6 +1009,13 @@ test('secondary keys - lookup by combined keys', () => { ], }); + expect(ds.secondaryIndicesKeys()).toEqual(['id:title', 'done:title']); + expect(ds.getAllIndexValues(['id', 'title'])).toEqual([ + JSON.stringify({id: 'cookie', title: 'eat a cookie'}), + JSON.stringify({id: 'coffee', title: 'drink coffee'}), + JSON.stringify({id: 'bug', title: 'submit a bug'}), + ]); + expect( ds.getAllRecordsByIndex({ id: 'cookie', @@ -1014,6 +1047,13 @@ test('secondary keys - lookup by combined keys', () => { }), ).toEqual([eatCookie, cookie2]); + expect(ds.getAllIndexValues(['id', 'title'])).toEqual([ + JSON.stringify({id: 'cookie', title: 'eat a cookie'}), + JSON.stringify({id: 'coffee', title: 'drink coffee'}), + JSON.stringify({id: 'bug', title: 'submit a bug'}), + JSON.stringify({id: 'cookie2', title: 'eat a cookie'}), + ]); + const upsertedCookie = { id: 'cookie', title: 'eat a cookie', @@ -1041,6 +1081,16 @@ test('secondary keys - lookup by combined keys', () => { }), ).toEqual(undefined); + expect(ds.getAllIndexValues(['id', 'title'])).toEqual([ + JSON.stringify({id: 'cookie', title: 'eat a cookie'}), + JSON.stringify({id: 'coffee', title: 'drink coffee'}), + JSON.stringify({id: 'bug', title: 'submit a bug'}), + JSON.stringify({id: 'cookie2', title: 'eat a cookie'}), + ]); + + const clearSub = jest.fn(); + ds.addDataListener('clear', clearSub); + ds.clear(); expect( ds.getAllRecordsByIndex({ @@ -1049,6 +1099,12 @@ test('secondary keys - lookup by combined keys', () => { }), ).toEqual([]); + expect(ds.getAllIndexValues(['id', 'title'])).toEqual([]); + expect(clearSub).toBeCalledTimes(1); + + const newIndexValueSub = jest.fn(); + ds.addDataListener('siNewIndexValue', newIndexValueSub); + ds.append(cookie2); expect( ds.getAllRecordsByIndex({ @@ -1056,4 +1112,23 @@ test('secondary keys - lookup by combined keys', () => { title: 'eat a cookie', }), ).toEqual([cookie2]); + + expect(ds.getAllIndexValues(['id', 'title'])).toEqual([ + JSON.stringify({id: 'cookie2', title: 'eat a cookie'}), + ]); + + // Because we have 2 indecies + expect(newIndexValueSub).toBeCalledTimes(2); + expect(newIndexValueSub).toBeCalledWith({ + type: 'siNewIndexValue', + indexKey: JSON.stringify({id: 'cookie2', title: 'eat a cookie'}), + firstOfKind: true, + value: cookie2, + }); + expect(newIndexValueSub).toBeCalledWith({ + type: 'siNewIndexValue', + indexKey: JSON.stringify({done: 'true', title: 'eat a cookie'}), + firstOfKind: true, + value: cookie2, + }); }); From a8f5fecc2bdfad05c187d15567115838e32ebc45 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 8 Nov 2023 02:08:25 -0800 Subject: [PATCH 005/112] Infer enum labels Reviewed By: LukeDefeo Differential Revision: D51067952 fbshipit-source-id: ed39d3ab037a2169120187bf20bf4a023488c025 --- .../flipper-plugin/src/__tests__/api.node.tsx | 1 + .../src/ui/PowerSearch/PowerSearchConfig.tsx | 7 +- .../ui/PowerSearch/PowerSearchEnumSetTerm.tsx | 3 +- .../ui/PowerSearch/PowerSearchEnumTerm.tsx | 7 +- .../src/ui/PowerSearch/index.tsx | 9 +- .../data-table/DataTableWithPowerSearch.tsx | 99 ++++++++++++++++++- 6 files changed, 118 insertions(+), 8 deletions(-) diff --git a/desktop/flipper-plugin/src/__tests__/api.node.tsx b/desktop/flipper-plugin/src/__tests__/api.node.tsx index a42f69b26..695740797 100644 --- a/desktop/flipper-plugin/src/__tests__/api.node.tsx +++ b/desktop/flipper-plugin/src/__tests__/api.node.tsx @@ -121,6 +121,7 @@ test('Correct top level API exposed', () => { "ElementSearchResultSet", "ElementsInspectorElement", "ElementsInspectorProps", + "EnumLabels", "FieldConfig", "FileDescriptor", "FileEncoding", diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx index f2775d0b1..b262ca63f 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx @@ -55,11 +55,16 @@ export type FloatOperatorConfig = { precision?: number; }; +/** + * { value: label } + */ +export type EnumLabels = {[key: string | number]: string | number}; + export type EnumOperatorConfig = { valueType: EnumFilterValueType; key: string; label: string; - enumLabels: {[key: string]: string}; + enumLabels: EnumLabels; }; export type AbsoluteDateOperatorConfig = { diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx index 3aad64df0..6c2e08129 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx @@ -9,11 +9,12 @@ import {Select} from 'antd'; import React from 'react'; +import {EnumLabels} from './PowerSearchConfig'; type PowerSearchEnumSetTermProps = { onCancel: () => void; onChange: (value: string[]) => void; - enumLabels: {[key: string]: string}; + enumLabels: EnumLabels; defaultValue?: string[]; }; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumTerm.tsx index 7d8fb9b73..679a9c906 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumTerm.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumTerm.tsx @@ -9,11 +9,12 @@ import {Button, Select} from 'antd'; import React from 'react'; +import {EnumLabels} from './PowerSearchConfig'; type PowerSearchEnumTermProps = { onCancel: () => void; onChange: (value: string) => void; - enumLabels: {[key: string]: string}; + enumLabels: EnumLabels; defaultValue?: string; }; @@ -38,8 +39,8 @@ export const PowerSearchEnumTerm: React.FC = ({ let longestOptionLabelWidth = 0; Object.values(enumLabels).forEach((label) => { - if (label.length > longestOptionLabelWidth) { - longestOptionLabelWidth = label.length; + if (label.toString().length > longestOptionLabelWidth) { + longestOptionLabelWidth = label.toString().length; } }); diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx index cf6b50c1f..79521d914 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx @@ -13,6 +13,7 @@ import { PowerSearchConfig, FieldConfig, OperatorConfig, + EnumLabels, } from './PowerSearchConfig'; import {PowerSearchContainer} from './PowerSearchContainer'; import { @@ -31,7 +32,13 @@ import {theme} from '../theme'; import {SearchOutlined} from '@ant-design/icons'; import {getFlipperLib} from 'flipper-plugin-core'; -export {PowerSearchConfig, OperatorConfig, FieldConfig, SearchExpressionTerm}; +export { + PowerSearchConfig, + EnumLabels, + OperatorConfig, + FieldConfig, + SearchExpressionTerm, +}; type PowerSearchProps = { config: PowerSearchConfig; diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx index d7dd34108..63f1620c0 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -67,6 +67,7 @@ import { FieldConfig, OperatorConfig, SearchExpressionTerm, + EnumLabels, } from '../PowerSearch'; import { dataTablePowerSearchOperatorProcessorConfig, @@ -154,7 +155,16 @@ export type DataTableColumn = { powerSearchConfig?: | OperatorConfig[] | false - | {operators: OperatorConfig[]; useWholeRow?: boolean}; + | { + operators: OperatorConfig[]; + useWholeRow?: boolean; + /** + * Auto-generate enum options based on the data. + * Requires the column to be set as a secondary "index" (single column, not a compound multi-column index). + * See https://fburl.com/code/0waicx6p + */ + inferEnumOptionsFromData?: boolean; + }; }; export interface TableRowRenderContext { @@ -263,6 +273,73 @@ export function DataTable( [columns], ); + // Collecting a hashmap of unique values for every column we infer the power search enum labels for (hashmap of hashmaps). + // It could be a hashmap of sets, but then we would need to convert a set to a hashpmap when rendering enum power search term, so it is just more convenient to make it a hashmap of hashmaps + const [inferredPowerSearchEnumLabels, setInferredPowerSearchEnumLabels] = + React.useState>({}); + React.useEffect(() => { + const columnKeysToInferOptionsFor: string[] = []; + const secondaryIndeciesKeys = new Set(dataSource.secondaryIndicesKeys()); + + for (const column of columns) { + if ( + typeof column.powerSearchConfig === 'object' && + !Array.isArray(column.powerSearchConfig) && + column.powerSearchConfig.inferEnumOptionsFromData + ) { + if (!secondaryIndeciesKeys.has(column.key)) { + console.warn( + 'inferEnumOptionsFromData work only if the same column key is specified as a DataSource secondary index! See https://fburl.com/code/0waicx6p. Missing index definition!', + column.key, + ); + continue; + } + columnKeysToInferOptionsFor.push(column.key); + } + } + + if (columnKeysToInferOptionsFor.length > 0) { + const getInferredLabels = () => { + const newInferredLabels: Record = + {}; + + for (const key of columnKeysToInferOptionsFor) { + newInferredLabels[key] = {}; + for (const indexValue of dataSource.getAllIndexValues([ + key as keyof T, + ]) ?? []) { + // `indexValue` is a stringified JSON in a format of { key: value } + const value = Object.values(JSON.parse(indexValue))[0] as string; + newInferredLabels[key][value] = value; + } + } + + return newInferredLabels; + }; + setInferredPowerSearchEnumLabels(getInferredLabels()); + + const unsubscribeIndexUpdates = dataSource.addDataListener( + 'siNewIndexValue', + ({firstOfKind}) => { + if (firstOfKind) { + setInferredPowerSearchEnumLabels(getInferredLabels()); + } + }, + ); + const unsubscribeDataSourceClear = dataSource.addDataListener( + 'clear', + () => { + setInferredPowerSearchEnumLabels(getInferredLabels()); + }, + ); + + return () => { + unsubscribeIndexUpdates(); + unsubscribeDataSourceClear(); + }; + } + }, [columns, dataSource]); + const powerSearchConfig: PowerSearchConfig = useMemo(() => { const res: PowerSearchConfig = {fields: {}}; @@ -290,6 +367,20 @@ export function DataTable( } else { columnPowerSearchOperators = column.powerSearchConfig.operators; useWholeRow = !!column.powerSearchConfig.useWholeRow; + + const inferredPowerSearchEnumLabelsForColumn = + inferredPowerSearchEnumLabels[column.key]; + if ( + inferredPowerSearchEnumLabelsForColumn && + column.powerSearchConfig.inferEnumOptionsFromData + ) { + columnPowerSearchOperators = columnPowerSearchOperators.map( + (operator) => ({ + ...operator, + enumLabels: inferredPowerSearchEnumLabelsForColumn, + }), + ); + } } const columnFieldConfig: FieldConfig = { @@ -305,7 +396,11 @@ export function DataTable( } return res; - }, [columns, props.enablePowerSearchWholeRowSearch]); + }, [ + columns, + props.enablePowerSearchWholeRowSearch, + inferredPowerSearchEnumLabels, + ]); const renderingConfig = useMemo>(() => { let startIndex = 0; From 4bb0f59ab82a3d5550af2c778c0f5c64d8845e82 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 8 Nov 2023 02:08:25 -0800 Subject: [PATCH 006/112] Migrate to power search Reviewed By: LukeDefeo Differential Revision: D51027189 fbshipit-source-id: 4fb3699a278db280237e5182d41d3c746e44a2bb --- .../public/network/__tests__/chunks.node.tsx | 2 +- .../network/__tests__/customheaders.node.tsx | 2 +- .../network/__tests__/encoding.node.tsx | 4 +- desktop/plugins/public/network/index.tsx | 90 ++++++++++++++++--- .../request-mocking/NetworkRouteManager.tsx | 2 +- desktop/plugins/public/network/types.tsx | 2 +- desktop/plugins/public/network/utils.tsx | 4 - 7 files changed, 85 insertions(+), 21 deletions(-) diff --git a/desktop/plugins/public/network/__tests__/chunks.node.tsx b/desktop/plugins/public/network/__tests__/chunks.node.tsx index 88f8e796e..bdc5bc024 100644 --- a/desktop/plugins/public/network/__tests__/chunks.node.tsx +++ b/desktop/plugins/public/network/__tests__/chunks.node.tsx @@ -180,7 +180,7 @@ test('Reducer correctly combines initial response and followup chunk', () => { responseHeaders: [{key: 'Content-Type', value: 'text/plain'}], responseIsMock: false, responseLength: 5, - status: 200, + status: '200', url: 'http://test.com', }); }); diff --git a/desktop/plugins/public/network/__tests__/customheaders.node.tsx b/desktop/plugins/public/network/__tests__/customheaders.node.tsx index 63e860d44..0751e478e 100644 --- a/desktop/plugins/public/network/__tests__/customheaders.node.tsx +++ b/desktop/plugins/public/network/__tests__/customheaders.node.tsx @@ -113,7 +113,7 @@ test('Can handle custom headers', async () => { responseIsMock: false, responseLength: 0, 'response_header_second-test-header': 'dolphins', - status: 200, + status: '200', url: 'http://www.fbflipper.com', }, ]); diff --git a/desktop/plugins/public/network/__tests__/encoding.node.tsx b/desktop/plugins/public/network/__tests__/encoding.node.tsx index 29297838b..2df48b125 100644 --- a/desktop/plugins/public/network/__tests__/encoding.node.tsx +++ b/desktop/plugins/public/network/__tests__/encoding.node.tsx @@ -233,7 +233,7 @@ test('binary data gets serialized correctly', async () => { ], responseIsMock: false, responseLength: 24838, - status: 200, + status: '200', url: 'http://www.fbflipper.com', }, ], @@ -265,7 +265,7 @@ test('binary data gets serialized correctly', async () => { ], responseIsMock: false, responseLength: 24838, - status: 200, + status: '200', url: 'http://www.fbflipper.com', }); }); diff --git a/desktop/plugins/public/network/index.tsx b/desktop/plugins/public/network/index.tsx index 5a991a007..cc0e7fd7c 100644 --- a/desktop/plugins/public/network/index.tsx +++ b/desktop/plugins/public/network/index.tsx @@ -28,12 +28,13 @@ import { usePlugin, useValue, createDataSource, - DataTableLegacy as DataTable, - DataTableColumnLegacy as DataTableColumn, - DataTableManagerLegacy as DataTableManager, + _DataTableWithPowerSearch as DataTable, + _DataTableColumnWithPowerSearch as DataTableColumn, + _DataTableWithPowerSearchManager as DataTableManager, theme, renderReactRoot, batch, + dataTablePowerSearchOperators, } from 'flipper-plugin'; import { Request, @@ -50,7 +51,6 @@ import { getHeaderValue, getResponseLength, getRequestLength, - formatStatus, formatBytes, formatDuration, requestsToText, @@ -118,6 +118,7 @@ export function plugin(client: PluginClient) { ); const requests = createDataSource([], { key: 'id', + indices: [['method'], ['status']], }); const selectedId = createState(undefined); const tableManagerRef = createRef>(); @@ -136,11 +137,16 @@ export function plugin(client: PluginClient) { return; } else if (payload.startsWith(searchTermDelim)) { tableManagerRef.current?.clearSelection(); - tableManagerRef.current?.setSearchValue( - payload.slice(searchTermDelim.length), - ); + tableManagerRef.current?.setSearchExpression([ + { + field: {label: 'Row', key: 'entireRow', useWholeRow: true}, + operator: + dataTablePowerSearchOperators.searializable_object_contains(), + searchValue: payload.slice(searchTermDelim.length), + }, + ]); } else { - tableManagerRef.current?.setSearchValue(''); + tableManagerRef.current?.setSearchExpression([]); tableManagerRef.current?.selectItemById(payload); } }); @@ -537,6 +543,7 @@ function createRequestFromRequestInfo( domain, requestHeaders: data.headers, requestData: decodeBody(data.headers, data.data), + status: '...', }; customColumns .filter((c) => c.type === 'request') @@ -557,7 +564,7 @@ function updateRequestWithResponseInfo( const res = { ...request, responseTime: new Date(response.timestamp), - status: response.status, + status: response.status.toString(), reason: response.reason, responseHeaders: response.headers, responseData: decodeBody(response.headers, response.data), @@ -659,12 +666,20 @@ const baseColumns: DataTableColumn[] = [ key: 'requestTime', title: 'Request Time', width: 120, + powerSearchConfig: [ + dataTablePowerSearchOperators.older_than_absolute_date(), + dataTablePowerSearchOperators.newer_than_absolute_date(), + ], }, { key: 'responseTime', title: 'Response Time', width: 120, visible: false, + powerSearchConfig: [ + dataTablePowerSearchOperators.older_than_absolute_date(), + dataTablePowerSearchOperators.newer_than_absolute_date(), + ], }, { key: 'requestData', @@ -672,26 +687,55 @@ const baseColumns: DataTableColumn[] = [ width: 120, visible: false, formatters: formatOperationName, + powerSearchConfig: [ + dataTablePowerSearchOperators.searializable_object_contains(), + dataTablePowerSearchOperators.searializable_object_not_contains(), + ], }, { key: 'domain', + powerSearchConfig: [ + dataTablePowerSearchOperators.string_contains(), + dataTablePowerSearchOperators.string_not_contains(), + dataTablePowerSearchOperators.string_matches_exactly(), + dataTablePowerSearchOperators.string_not_matches_exactly(), + ], }, { key: 'url', title: 'Full URL', visible: false, + powerSearchConfig: [ + dataTablePowerSearchOperators.string_contains(), + dataTablePowerSearchOperators.string_not_contains(), + dataTablePowerSearchOperators.string_matches_exactly(), + dataTablePowerSearchOperators.string_not_matches_exactly(), + ], }, { key: 'method', title: 'Method', width: 70, + powerSearchConfig: { + operators: [ + dataTablePowerSearchOperators.enum_set_is_any_of({}), + dataTablePowerSearchOperators.enum_set_is_none_of({}), + ], + inferEnumOptionsFromData: true, + }, }, { key: 'status', title: 'Status', width: 70, - formatters: formatStatus, align: 'right', + powerSearchConfig: { + operators: [ + dataTablePowerSearchOperators.enum_set_is_any_of({}), + dataTablePowerSearchOperators.enum_set_is_none_of({}), + ], + inferEnumOptionsFromData: true, + }, }, { key: 'requestLength', @@ -699,6 +743,13 @@ const baseColumns: DataTableColumn[] = [ width: 100, formatters: formatBytes, align: 'right', + powerSearchConfig: [ + dataTablePowerSearchOperators.float_equals(), + dataTablePowerSearchOperators.float_greater_than(), + dataTablePowerSearchOperators.float_less_than(), + dataTablePowerSearchOperators.float_greater_or_equal(), + dataTablePowerSearchOperators.float_less_or_equal(), + ], }, { key: 'responseLength', @@ -706,6 +757,13 @@ const baseColumns: DataTableColumn[] = [ width: 100, formatters: formatBytes, align: 'right', + powerSearchConfig: [ + dataTablePowerSearchOperators.float_equals(), + dataTablePowerSearchOperators.float_greater_than(), + dataTablePowerSearchOperators.float_less_than(), + dataTablePowerSearchOperators.float_greater_or_equal(), + dataTablePowerSearchOperators.float_less_or_equal(), + ], }, { key: 'duration', @@ -713,6 +771,13 @@ const baseColumns: DataTableColumn[] = [ width: 100, formatters: formatDuration, align: 'right', + powerSearchConfig: [ + dataTablePowerSearchOperators.float_equals(), + dataTablePowerSearchOperators.float_greater_than(), + dataTablePowerSearchOperators.float_less_than(), + dataTablePowerSearchOperators.float_greater_or_equal(), + dataTablePowerSearchOperators.float_less_or_equal(), + ], }, ]; @@ -727,7 +792,10 @@ const errorStyle = { function getRowStyle(row: Request) { return row.responseIsMock ? mockingStyle - : row.status && row.status >= 400 && row.status < 600 + : row.status && + row.status !== '...' && + parseInt(row.status, 10) >= 400 && + parseInt(row.status, 10) < 600 ? errorStyle : undefined; } diff --git a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx index d058e0201..ab3163bf8 100644 --- a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx +++ b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx @@ -9,7 +9,7 @@ import { Atom, - DataTableManagerLegacy as DataTableManager, + _DataTableWithPowerSearchManager as DataTableManager, getFlipperLib, } from 'flipper-plugin'; import {createContext} from 'react'; diff --git a/desktop/plugins/public/network/types.tsx b/desktop/plugins/public/network/types.tsx index 383d71eb2..1eef07226 100644 --- a/desktop/plugins/public/network/types.tsx +++ b/desktop/plugins/public/network/types.tsx @@ -23,7 +23,7 @@ export interface Request { requestData: string | Uint8Array | undefined; // response responseTime?: Date; - status?: number; + status: string; reason?: string; responseHeaders?: Array
; responseData?: string | Uint8Array | undefined; diff --git a/desktop/plugins/public/network/utils.tsx b/desktop/plugins/public/network/utils.tsx index a6a62c681..6f65d775d 100644 --- a/desktop/plugins/public/network/utils.tsx +++ b/desktop/plugins/public/network/utils.tsx @@ -268,10 +268,6 @@ export function formatBytes(count: number | undefined): string { return count + ' B'; } -export function formatStatus(status: number | undefined) { - return status ? '' + status : ''; -} - export function formatOperationName(requestData: string): string { try { const parsedData = JSON.parse(requestData); From 3dc9cc5d3d5e8cf86ca760dd375e330ae9f33cf6 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 8 Nov 2023 02:13:47 -0800 Subject: [PATCH 007/112] Skip unknown values during filtering by default Reviewed By: LukeDefeo Differential Revision: D51078471 fbshipit-source-id: b3a005f31eebd77e9ff77349640133032f4b0164 --- .../src/ui/data-table/DataTableWithPowerSearch.tsx | 6 ++++++ .../src/ui/data-table/DataTableWithPowerSearchManager.tsx | 8 ++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx index 63f1620c0..d3f6beb5e 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -105,6 +105,10 @@ type DataTableBaseProps = { * @default true */ enablePowerSearchWholeRowSearch?: boolean; + /** If set to `true` and row[columnKey] is undefined, then it is going to pass filtering (search). + * @default false + */ + treatUndefinedValuesAsMatchingFiltering?: boolean; }; const powerSearchConfigEntireRow: FieldConfig = { @@ -556,6 +560,7 @@ export function DataTable( computeDataTableFilter( tableState.searchExpression, dataTablePowerSearchOperatorProcessorConfig, + props.treatUndefinedValuesAsMatchingFiltering, ), ); dataView.setFilterExpections( @@ -914,6 +919,7 @@ DataTable.defaultProps = { enablePersistSettings: true, onRenderEmpty: undefined, enablePowerSearchWholeRowSearch: true, + treatUndefinedValuesAsMatchingFiltering: false, } as Partial>; /* eslint-disable react-hooks/rules-of-hooks */ diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx index 7295309f9..109bc03c3 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx @@ -508,6 +508,7 @@ export function getValueAtPath(obj: Record, keyPath: string): any { export function computeDataTableFilter( searchExpression: SearchExpressionTerm[] | undefined, powerSearchProcessors: PowerSearchOperatorProcessorConfig, + treatUndefinedValuesAsMatchingFiltering: boolean = false, ) { return function dataTableFilter(item: any) { if (!searchExpression || !searchExpression.length) { @@ -518,12 +519,7 @@ export function computeDataTableFilter( ? item : getValueAtPath(item, searchTerm.field.key); if (!value) { - console.warn( - 'computeDataTableFilter -> value at searchTerm.field.key is not recognized', - searchTerm, - item, - ); - return true; + return treatUndefinedValuesAsMatchingFiltering; } const processor = From 9164e04e29c5b03aaa2a56f57bca3e208f2fba26 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Wed, 8 Nov 2023 02:51:34 -0800 Subject: [PATCH 008/112] Better appId and productName Summary: As to not conflict with non-electron installations, this will ensure the product name and app id matches the installation. By doing this, both Flipper installations can co-exist, which I think is desirable. Reviewed By: antonk52 Differential Revision: D51078955 fbshipit-source-id: fabaa6eb2a45ac542297b0456a09e938a2ec2e0b --- desktop/scripts/build-release.tsx | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/desktop/scripts/build-release.tsx b/desktop/scripts/build-release.tsx index 8e79ee791..c71a3937a 100755 --- a/desktop/scripts/build-release.tsx +++ b/desktop/scripts/build-release.tsx @@ -210,6 +210,13 @@ async function buildDist(buildFolder: string) { const targetsRaw: Map>[] = []; const postBuildCallbacks: (() => void)[] = []; + const productName = process.env.FLIPPER_REACT_NATIVE_ONLY + ? 'Flipper-Electron' + : 'Flipper'; + const appId = process.env.FLIPPER_REACT_NATIVE_ONLY + ? 'com.facebook.sonar-electron' + : `com.facebook.sonar`; + if (argv.mac || argv['mac-dmg']) { let macPath = path.join( distDir, @@ -226,10 +233,14 @@ async function buildDist(buildFolder: string) { } } postBuildCallbacks.push(() => - spawn('zip', ['-qyr9', '../Flipper-mac.zip', 'Flipper.app'], { - cwd: macPath, - encoding: 'utf-8', - }), + spawn( + 'zip', + ['-qyr9', `../${productName}-mac.zip`, `${productName}.app`], + { + cwd: macPath, + encoding: 'utf-8', + }, + ), ); } if (argv.linux || argv['linux-deb'] || argv['linux-snap']) { @@ -268,8 +279,8 @@ async function buildDist(buildFolder: string) { await build({ publish: 'never', config: { - appId: `com.facebook.sonar`, - productName: 'Flipper', + appId, + productName, directories: { buildResources: buildFolder, output: distDir, From 137e75ad46465086737b6a397816539d2b77dbe1 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Wed, 8 Nov 2023 09:20:13 -0800 Subject: [PATCH 009/112] Refactor Open Summary: Extract our launch UI logic into flipper-server-core. Reviewed By: passy Differential Revision: D51115241 fbshipit-source-id: 185e381eab6b480d86a5e1201f45c070104d0cea --- desktop/flipper-server-core/src/index.tsx | 2 +- .../flipper-server-core/src/utils/openUI.tsx | 59 +++++++++++++++++++ desktop/flipper-server/src/index.tsx | 48 +++------------ 3 files changed, 68 insertions(+), 41 deletions(-) create mode 100644 desktop/flipper-server-core/src/utils/openUI.tsx diff --git a/desktop/flipper-server-core/src/index.tsx b/desktop/flipper-server-core/src/index.tsx index 951b26a67..df488c048 100644 --- a/desktop/flipper-server-core/src/index.tsx +++ b/desktop/flipper-server-core/src/index.tsx @@ -13,13 +13,13 @@ export * from './tracker'; export {loadLauncherSettings} from './utils/launcherSettings'; export {loadProcessConfig} from './utils/processConfig'; export {getEnvironmentInfo} from './utils/environmentInfo'; -export {findInstallation} from './utils/findInstallation'; export {getGatekeepers} from './gk'; export {setupPrefetcher} from './fb-stubs/Prefetcher'; export * from './server/attachSocketServer'; export * from './server/startFlipperServer'; export * from './server/startServer'; export * from './server/utilities'; +export * from './utils/openUI'; export {isFBBuild} from './fb-stubs/constants'; export {initializeLogger} from './fb-stubs/Logger'; diff --git a/desktop/flipper-server-core/src/utils/openUI.tsx b/desktop/flipper-server-core/src/utils/openUI.tsx new file mode 100644 index 000000000..7a4fcb511 --- /dev/null +++ b/desktop/flipper-server-core/src/utils/openUI.tsx @@ -0,0 +1,59 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import open from 'open'; +import {getAuthToken} from '../app-connectivity/certificate-exchange/certificate-utils'; +import {findInstallation} from './findInstallation'; +import {tracker} from '../tracker'; + +export enum UIPreference { + Browser, + PWA, +} + +export async function openUI(preference: UIPreference, port: number) { + console.info('[flipper-server] Launch UI'); + + const token = await getAuthToken(); + console.info( + `[flipper-server] Get authentication token: ${token?.length != 0}`, + ); + + const openInBrowser = async () => { + console.info('[flipper-server] Open in browser'); + const url = new URL(`http://localhost:${port}`); + + console.info(`[flipper-server] Go to: ${url.toString()}`); + + open(url.toString(), {app: {name: open.apps.chrome}}); + + tracker.track('server-open-ui', { + browser: true, + hasToken: token?.length != 0, + }); + }; + + if (preference === UIPreference.Browser) { + await openInBrowser(); + } else { + const path = await findInstallation(); + if (path) { + console.info('[flipper-server] Open in PWA. Location:', path); + tracker.track('server-open-ui', { + browser: false, + hasToken: token?.length != 0, + }); + open(path); + } else { + await openInBrowser(); + } + } + + console.info('[flipper-server] Launch UI completed'); +} diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index ed49dadf6..ba4ccbc10 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -16,22 +16,22 @@ import {attachDevServer} from './attachDevServer'; import {initializeLogger} from './logger'; import fs from 'fs-extra'; import yargs from 'yargs'; -import open from 'open'; import os from 'os'; import {initCompanionEnv} from 'flipper-server-companion'; import { + UIPreference, checkPortInUse, checkServerRunning, compareServerVersion, getEnvironmentInfo, + openUI, shutdownRunningInstance, startFlipperServer, startServer, tracker, } from 'flipper-server-core'; -import {addLogTailer, isTest, LoggerFormat} from 'flipper-common'; +import {addLogTailer, isProduction, isTest, LoggerFormat} from 'flipper-common'; import exitHook from 'exit-hook'; -import {getAuthToken, findInstallation} from 'flipper-server-core'; const argv = yargs .usage('yarn flipper-server [args]') @@ -295,47 +295,15 @@ async function start() { } async function launch() { - console.info('[flipper-server] Launch UI'); - - const token = await getAuthToken(); - console.info( - `[flipper-server] Get authentication token: ${token?.length != 0}`, - ); - if (!argv.open) { + console.warn( + '[flipper-server] Not opening UI, --open flag was not provided', + ); return; } - const openInBrowser = async () => { - console.info('[flipper-server] Open in browser'); - const url = new URL(`http://localhost:${argv.port}`); - - console.info(`[flipper-server] Go to: ${chalk.blue(url.toString())}`); - - open(url.toString(), {app: {name: open.apps.chrome}}); - - tracker.track('server-open-ui', { - browser: true, - hasToken: token?.length != 0, - }); - }; - - if (argv.bundler) { - await openInBrowser(); - } else { - const path = await findInstallation(); - if (path) { - tracker.track('server-open-ui', { - browser: false, - hasToken: token?.length != 0, - }); - open(path); - } else { - await openInBrowser(); - } - } - - console.info('[flipper-server] Launch UI completed'); + const preference = isProduction() ? UIPreference.PWA : UIPreference.Browser; + openUI(preference, argv.port); } process.on('uncaughtException', (error) => { From 9b9eb00b636d079d436507f4815d64d23477d2cd Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Wed, 8 Nov 2023 09:20:13 -0800 Subject: [PATCH 010/112] Endpoint to Open UI Summary: Expose an endpoint to open Flipper UI. This will be used by the Flipper Server Cocoa app to open the UI. Reviewed By: antonk52 Differential Revision: D51115327 fbshipit-source-id: 1ab1c32d93945cf8d75b145905983738331a6468 --- desktop/flipper-server-core/src/server/startServer.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/desktop/flipper-server-core/src/server/startServer.tsx b/desktop/flipper-server-core/src/server/startServer.tsx index 5b7a4420b..51f1639fa 100644 --- a/desktop/flipper-server-core/src/server/startServer.tsx +++ b/desktop/flipper-server-core/src/server/startServer.tsx @@ -23,6 +23,7 @@ import {tracker} from '../tracker'; import {EnvironmentInfo, isProduction} from 'flipper-common'; import {GRAPH_SECRET} from '../fb-stubs/constants'; import {sessionId} from '../sessionId'; +import {UIPreference, openUI} from '../utils/openUI'; type Config = { port: number; @@ -186,6 +187,13 @@ async function startHTTPServer( res.end('flipper-ok'); }); + app.get('/open-ui', (_req, res) => { + tracker.track('server-endpoint-hit', {name: 'open-ui'}); + const preference = isProduction() ? UIPreference.PWA : UIPreference.Browser; + openUI(preference, config.port); + res.json({success: true}); + }); + app.use(express.static(config.staticPath)); const server = http.createServer(app); From 92d14541407038e8db53a0f61515c68cbfa866a5 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Wed, 8 Nov 2023 09:20:13 -0800 Subject: [PATCH 011/112] Open flag when starting server in debug mode Summary: It was hard-coded to always open the first time. This allows to run the server on debug mode without opening UI. Reviewed By: antonk52 Differential Revision: D51115746 fbshipit-source-id: 9467f0fbff45987247a2bb3bf5eb1aa578de1913 --- desktop/scripts/start-flipper-server-dev.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/desktop/scripts/start-flipper-server-dev.tsx b/desktop/scripts/start-flipper-server-dev.tsx index af87be91b..75056c22d 100644 --- a/desktop/scripts/start-flipper-server-dev.tsx +++ b/desktop/scripts/start-flipper-server-dev.tsx @@ -49,6 +49,11 @@ const argv = yargs choices: ['stable', 'insiders'], default: 'stable', }, + open: { + describe: 'Open Flipper in the default browser after starting', + type: 'boolean', + default: true, + }, }) .version('DEV') .help() @@ -103,7 +108,9 @@ async function copyStaticResources() { async function restartServer() { try { await compileServerMain(); - await launchServer(true, ++startCount === 1); // only open on the first time + // Only open the UI the first time it runs. Subsequent runs, likely triggered after + // saving changes, should just reload the existing UI. + await launchServer(true, argv.open && ++startCount === 1); } catch (e) { console.error( chalk.red( From e461229075f374aac4b28ce6c2ef5b85f03e073e Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 8 Nov 2023 10:30:55 -0800 Subject: [PATCH 012/112] Enum dropdown width matches content rather than parent input Reviewed By: aigoncharov Differential Revision: D51112879 fbshipit-source-id: e3647df81ce7bbed91a606e68d44a503c367c948 --- .../flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx index 6c2e08129..3ab8f9ec7 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx @@ -45,6 +45,7 @@ export const PowerSearchEnumSetTerm: React.FC = ({ options={options} defaultOpen={!defaultValue} defaultValue={defaultValue} + dropdownMatchSelectWidth={false} onBlur={() => { if (!selectValueRef.current?.length) { onCancel(); From 03c2828630dcdbb576b0c43f45f7b6eb9e68fa6f Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 8 Nov 2023 10:30:55 -0800 Subject: [PATCH 013/112] Power search config for framework event table Summary: Basic config, added inferred enum support for the event type. Reviewed By: aigoncharov Differential Revision: D51113094 fbshipit-source-id: 13acd83e7f7a5d4ee6b62641b13616cc49377e0a --- .../components/FrameworkEventsTable.tsx | 177 +++++++++++++----- desktop/plugins/public/ui-debugger/index.tsx | 5 +- 2 files changed, 132 insertions(+), 50 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx b/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx index 01466dd58..2ce268bc2 100644 --- a/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx +++ b/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx @@ -9,11 +9,13 @@ import {DeleteOutlined, PartitionOutlined} from '@ant-design/icons'; import { - DataTable, + _DataTableWithPowerSearch as DataTable, DataTableColumn, - DataTableManager, DetailSidebar, Layout, + _DataTableColumnWithPowerSearch, + _DataTableWithPowerSearchManager, + dataTablePowerSearchOperators, usePlugin, useValue, } from 'flipper-plugin'; @@ -41,9 +43,11 @@ export function FrameworkEventsTable({ const instance = usePlugin(plugin); const focusedNode = useValue(instance.uiState.focusedNode); - const managerRef = useRef | null>( - null, - ); + + const managerRef = + useRef<_DataTableWithPowerSearchManager | null>( + null, + ); useEffect(() => { tracker.track('framework-event-table-opened', {}); @@ -51,13 +55,31 @@ export function FrameworkEventsTable({ if (nodeId != null) { managerRef.current?.resetFilters(); if (isTree) { - managerRef.current?.addColumnFilter('treeId', nodeId as string, { - exact: true, - }); + managerRef.current?.setSearchExpression([ + { + field: { + key: 'treeId', + label: 'TreeId', + }, + operator: { + ...dataTablePowerSearchOperators.int_equals(), + }, + searchValue: nodeId, + }, + ]); } else { - managerRef.current?.addColumnFilter('nodeId', nodeId as string, { - exact: true, - }); + managerRef.current?.setSearchExpression([ + { + field: { + key: 'nodeId', + label: 'NodeId', + }, + operator: { + ...dataTablePowerSearchOperators.int_equals(), + }, + searchValue: nodeId, + }, + ]); } } }, [instance.uiActions, isTree, nodeId]); @@ -71,6 +93,7 @@ export function FrameworkEventsTable({ key: customKey, title: startCase(customKey), onRender: (row: AugmentedFrameworkEvent) => row.payload?.[customKey], + powerSearchConfig: enumLikeString, } as DataTableColumn), ); @@ -135,42 +158,98 @@ export function FrameworkEventsTable({ ); } -const staticColumns: DataTableColumn[] = [ - { - key: 'timestamp', - onRender: (row: FrameworkEvent) => formatTimestampMillis(row.timestamp), - title: 'Timestamp', - }, - { - key: 'type', - title: 'Event type', - onRender: (row: FrameworkEvent) => eventTypeToName(row.type), - }, - { - key: 'duration', - title: 'Duration', - onRender: (row: FrameworkEvent) => - row.duration != null ? formatDuration(row.duration) : null, - }, - { - key: 'treeId', - title: 'TreeId', - }, - { - key: 'rootComponentName', - title: 'Root component name', - }, - { - key: 'nodeId', - title: 'Component ID', - }, - { - key: 'nodeName', - title: 'Component name', - }, - { - key: 'thread', - title: 'Thread', - onRender: (row: FrameworkEvent) => startCase(row.thread), - }, +const MonoSpace = (t: any) => ( + {t} +); + +const stringConfig = [ + dataTablePowerSearchOperators.string_contains(), + dataTablePowerSearchOperators.string_not_contains(), + dataTablePowerSearchOperators.string_matches_exactly(), ]; +const idConfig = [dataTablePowerSearchOperators.int_equals()]; + +//todo replace with auto mode +const enumLikeString = [ + dataTablePowerSearchOperators.string_matches_exactly(true), + dataTablePowerSearchOperators.string_not_matches_exactly(true), +]; + +const inferredEnum = [ + dataTablePowerSearchOperators.enum_set_is_any_of({}), + dataTablePowerSearchOperators.enum_is({}), + dataTablePowerSearchOperators.enum_set_is_none_of({}), + dataTablePowerSearchOperators.enum_is_not({}), +]; + +const staticColumns: _DataTableColumnWithPowerSearch[] = + [ + { + key: 'timestamp', + sortable: true, + onRender: (row: FrameworkEvent) => formatTimestampMillis(row.timestamp), + title: 'Timestamp', + formatters: MonoSpace, + + powerSearchConfig: [ + dataTablePowerSearchOperators.newer_than_absolute_date(), + dataTablePowerSearchOperators.older_than_absolute_date(), + ], + }, + { + key: 'type', + title: 'Event type', + onRender: (row: FrameworkEvent) => eventTypeToName(row.type), + powerSearchConfig: { + inferEnumOptionsFromData: true, + operators: inferredEnum, + }, + }, + { + key: 'duration', + title: 'Duration (Nanos)', + onRender: (row: FrameworkEvent) => + row.duration != null ? formatDuration(row.duration) : null, + formatters: MonoSpace, + + powerSearchConfig: [ + dataTablePowerSearchOperators.int_greater_or_equal(), + dataTablePowerSearchOperators.int_greater_than(), + dataTablePowerSearchOperators.int_equals(), + dataTablePowerSearchOperators.int_less_or_equal(), + dataTablePowerSearchOperators.int_less_than(), + ], + }, + { + key: 'treeId', + title: 'TreeId', + powerSearchConfig: idConfig, + + formatters: MonoSpace, + }, + { + key: 'rootComponentName', + title: 'Root component name', + powerSearchConfig: stringConfig, + formatters: MonoSpace, + }, + { + key: 'nodeId', + title: 'Component ID', + powerSearchConfig: idConfig, + formatters: MonoSpace, + }, + { + key: 'nodeName', + title: 'Component name', + powerSearchConfig: stringConfig, + formatters: MonoSpace, + }, + { + key: 'thread', + title: 'Thread', + onRender: (row: FrameworkEvent) => startCase(row.thread), + powerSearchConfig: stringConfig, + formatters: MonoSpace, + }, + ]; diff --git a/desktop/plugins/public/ui-debugger/index.tsx b/desktop/plugins/public/ui-debugger/index.tsx index 170952a40..a7cd12a87 100644 --- a/desktop/plugins/public/ui-debugger/index.tsx +++ b/desktop/plugins/public/ui-debugger/index.tsx @@ -51,7 +51,10 @@ export function plugin(client: PluginClient) { const snapshot = createState(null); const nodesAtom = createState>(new Map()); const frameworkEvents = createDataSource([], { - indices: [['nodeId']], + indices: [ + ['nodeId'], + ['type'], //for inferred values + ], limit: 10000, }); const frameworkEventsCustomColumns = createState>(new Set()); From 4d0a5ff42b0eebc3fecaf4793015764f4916d11b Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 8 Nov 2023 10:30:55 -0800 Subject: [PATCH 014/112] Change how custom columsn work Summary: Previously the render function was selecting the data from the paylaod object, this mean what was on screen and waht powersearch saw was diffferent. Now we supply a dotted key path and remove this from render, power search operator also uses this dotted key path so the search works changelog: UIdebugger added powersearch operators to Framework event table Reviewed By: aigoncharov Differential Revision: D51113095 fbshipit-source-id: 3c951c2a8a7a0a35e0aa79a194b979b699f83008 --- .../ui-debugger/components/FrameworkEventsTable.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx b/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx index 2ce268bc2..fa05a353b 100644 --- a/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx +++ b/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx @@ -90,10 +90,9 @@ export function FrameworkEventsTable({ const customColumns = [...customColumnKeys].map( (customKey: string) => ({ - key: customKey, + key: `payload.${customKey}` as any, title: startCase(customKey), - onRender: (row: AugmentedFrameworkEvent) => row.payload?.[customKey], - powerSearchConfig: enumLikeString, + powerSearchConfig: stringConfig, } as DataTableColumn), ); @@ -169,12 +168,6 @@ const stringConfig = [ ]; const idConfig = [dataTablePowerSearchOperators.int_equals()]; -//todo replace with auto mode -const enumLikeString = [ - dataTablePowerSearchOperators.string_matches_exactly(true), - dataTablePowerSearchOperators.string_not_matches_exactly(true), -]; - const inferredEnum = [ dataTablePowerSearchOperators.enum_set_is_any_of({}), dataTablePowerSearchOperators.enum_is({}), From 640fb86edc5708a2fd0d8c87ddfe8aad7b24861c Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Wed, 8 Nov 2023 10:39:18 -0800 Subject: [PATCH 015/112] Do not shutdown when there's no connected clients Summary: ^ Also, track client closes with their code and/or error. Reviewed By: antonk52 Differential Revision: D51110574 fbshipit-source-id: 2416e36256b000664b7677fcf2c03b045d318ed2 --- .../src/server/attachSocketServer.tsx | 31 +++++-------------- desktop/flipper-server-core/src/tracker.tsx | 1 + 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/desktop/flipper-server-core/src/server/attachSocketServer.tsx b/desktop/flipper-server-core/src/server/attachSocketServer.tsx index d89eb358a..4b85ecbdc 100644 --- a/desktop/flipper-server-core/src/server/attachSocketServer.tsx +++ b/desktop/flipper-server-core/src/server/attachSocketServer.tsx @@ -17,7 +17,6 @@ import { SystemError, getLogger, CompanionEventWebSocketMessage, - isProduction, } from 'flipper-common'; import {FlipperServerImpl} from '../FlipperServerImpl'; import {RawData, WebSocketServer} from 'ws'; @@ -26,7 +25,6 @@ import { FlipperServerCompanionEnv, } from 'flipper-server-companion'; import {URLSearchParams} from 'url'; -import {getFlipperServerConfig} from '../FlipperServerConfig'; import {tracker} from '../tracker'; import {performance} from 'perf_hooks'; @@ -40,6 +38,7 @@ const safe = (f: () => void) => { } }; +/* eslint-disable @typescript-eslint/no-unused-vars */ let numberOfConnectedClients = 0; /** @@ -242,7 +241,7 @@ export function attachSocketServer( safe(() => onClientMessage(data)); }); - async function onClientClose(closeOnIdle: boolean) { + async function onClientClose(code?: number, error?: string) { console.log(`Client disconnected ${clientAddress}`); numberOfConnectedClients--; @@ -251,29 +250,15 @@ export function attachSocketServer( server.offAny(onServerEvent); flipperServerCompanion?.destroyAll(); - if ( - getFlipperServerConfig().environmentInfo.isHeadlessBuild && - closeOnIdle - ) { - if (numberOfConnectedClients === 0 && isProduction()) { - console.info('Shutdown as no clients are currently connected'); - process.exit(0); - } - } + tracker.track('server-client-close', { + code, + error, + }); } client.on('close', (code, _reason) => { console.info('[flipper-server] Client close with code', code); - /** - * The socket will close as the endpoint is terminating - * the connection. Status code 1000 and 1001 are used for normal - * closures. Either the connection is no longer needed or the - * endpoint is going away i.e. browser navigating away from the - * current page. - * WS RFC: https://www.rfc-editor.org/rfc/rfc6455 - */ - const closeOnIdle = code === 1000 || code === 1001; - safe(() => onClientClose(closeOnIdle)); + safe(() => onClientClose(code)); }); client.on('error', (error) => { @@ -283,7 +268,7 @@ export function attachSocketServer( * do not close on idle as there's a high probability the * client will attempt to connect again. */ - onClientClose(false); + onClientClose(undefined, error.message); console.error('Client disconnected with error', error); }); }); diff --git a/desktop/flipper-server-core/src/tracker.tsx b/desktop/flipper-server-core/src/tracker.tsx index dd5ba2067..03dc25d3a 100644 --- a/desktop/flipper-server-core/src/tracker.tsx +++ b/desktop/flipper-server-core/src/tracker.tsx @@ -48,6 +48,7 @@ type TrackerEvents = { }; 'server-socket-already-in-use': {}; 'server-open-ui': {browser: boolean; hasToken: boolean}; + 'server-client-close': {code?: number; error?: string}; 'server-ws-server-error': {port: number; error: string}; 'server-ready-timeout': {timeout: number}; 'browser-connection-created': { From 3993e7461dc53af5f23945514fe4ef14db985fb8 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Wed, 8 Nov 2023 10:39:18 -0800 Subject: [PATCH 016/112] Shutdown after 5 hours Summary: If after 5 hours there are no connected clients, shutdown the server. This is to prevent cases whereas the long-lived instance makes users use stale versions of Flipper if Flipper stays indefinitely running in the background. Reviewed By: passy Differential Revision: D51111926 fbshipit-source-id: 4c38e392cf8a6a4fb840bffdea92c0b0314aefb9 --- .../src/server/attachSocketServer.tsx | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/desktop/flipper-server-core/src/server/attachSocketServer.tsx b/desktop/flipper-server-core/src/server/attachSocketServer.tsx index 4b85ecbdc..dd389522d 100644 --- a/desktop/flipper-server-core/src/server/attachSocketServer.tsx +++ b/desktop/flipper-server-core/src/server/attachSocketServer.tsx @@ -17,6 +17,7 @@ import { SystemError, getLogger, CompanionEventWebSocketMessage, + isProduction, } from 'flipper-common'; import {FlipperServerImpl} from '../FlipperServerImpl'; import {RawData, WebSocketServer} from 'ws'; @@ -27,6 +28,7 @@ import { import {URLSearchParams} from 'url'; import {tracker} from '../tracker'; import {performance} from 'perf_hooks'; +import {getFlipperServerConfig} from '../FlipperServerConfig'; const safe = (f: () => void) => { try { @@ -38,8 +40,8 @@ const safe = (f: () => void) => { } }; -/* eslint-disable @typescript-eslint/no-unused-vars */ let numberOfConnectedClients = 0; +let disconnectTimeout: NodeJS.Timeout | undefined; /** * Attach and handle incoming messages from clients. @@ -68,6 +70,10 @@ export function attachSocketServer( console.log('Client connected', clientAddress); numberOfConnectedClients++; + if (disconnectTimeout) { + clearTimeout(disconnectTimeout); + } + clearTimeout(browserConnectionTimeout); tracker.track('browser-connection-created', { successful: true, @@ -254,6 +260,25 @@ export function attachSocketServer( code, error, }); + + if ( + getFlipperServerConfig().environmentInfo.isHeadlessBuild && + isProduction() + ) { + const FIVE_HOURS = 5 * 60 * 60 * 1000; + if (disconnectTimeout) { + clearTimeout(disconnectTimeout); + } + + disconnectTimeout = setTimeout(() => { + if (numberOfConnectedClients === 0) { + console.info( + '[flipper-server] Shutdown as no clients are currently connected', + ); + process.exit(0); + } + }, FIVE_HOURS); + } } client.on('close', (code, _reason) => { From f856cedf81860839d26bc10a43f4e2ab90cd5618 Mon Sep 17 00:00:00 2001 From: Pascal Hartig Date: Thu, 9 Nov 2023 02:21:11 -0800 Subject: [PATCH 017/112] Update release docs Summary: Document the new release flow that does no longer involve local builds. Reviewed By: antonk52 Differential Revision: D51115563 fbshipit-source-id: 0c518e51dba64b2325047d6b1e485216e48d9777 --- .../img/flipper-launcher-release-skycastle.png | Bin 0 -> 25579 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 website/static/img/flipper-launcher-release-skycastle.png diff --git a/website/static/img/flipper-launcher-release-skycastle.png b/website/static/img/flipper-launcher-release-skycastle.png new file mode 100644 index 0000000000000000000000000000000000000000..ca00ff1d3c4d651cd09c0132c10e21955353663a GIT binary patch literal 25579 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

77cg!Po+wcf1&Nw*;_`w5u z-fW+pQI#v-N7ckDUPF6@{+F@%00{shmV$yU=RKKP-41`zzCH&kZgd!5hc$#IlTtVn zQE(Cj`k42jEaZGX;f~Pm19vYN49?mcI_qO!AGrIPZ}d&pF@58RAqtGP9$w2U7wqAh zt^RAY(eR1#<5}e1Z&YEYZv!s>jJ^#TY zwb{AW-e-Z@u83tP#3SZLe<#*N9>D|w9zMCl zrHp}XgmKSq2K<-97-xb()#2ii}x^3MTI7SK>cQ5yU+`jAD2->)UD78E5LY`%Z0H zZhLy2EVB>ib4EPf?|<+-LZckeX|a!Mx$*+>MG8Dje|BPC9&hSRdmRfOa9`AR^0~(Q zGGAq^st)qiok-dbT?-YS8F&Te*6b&N*X1g2>11?5T>!8pj)L4rpm- zABbRh=Wt4p$@VDe!)CMKotzdhe zbh&)&Nnfkl@~<<&7I9lq(a3S>QHDsN*KLig(!PrKf8w&qGabp%()YgL2E{d&oxmTT z_4!Q)<4!L8_4AZWhnadt^DF0v`@{uhlS{|MH`I5m+8?Pf`!n=!QS+1q_XX;9iPo>_n1$e|l^t{qNXFJ;g7byHswzZHz5mSCm}= z^HrJ3J^BpKh)&58OqA`lQ9+Oo2k_H@FMfL$mnD1Ou=7&)Mqg@s;GKM*?&?^*XQlWV zQ*dwQ;M!aUf6xRoS8=wxHKz{_42B4%Z1so#^uuBEC&}lvXs+&Y*$==#M<vlg#YfwHV$w?;|ydD%<;VUmmX2KFQgTX2nmYjO_1#ce6{ zZs+Wb3AE-l6;G8{MNW+Tpy^>UeyDbPsws&5*}#Sd7Uj5wc%{RlB?D-ZyR3*9$`=~g z>+O0*noK2MoBj=Ey&>SK``t+Q&S3lj5Z@i;ctg>8-jjCZ;_LPHSSH&8Tj(Sf5wRba z5{UI_c19}V+6$Ne*4G^EHu7S#0jk6IzCMQFqHSH{U>IQ}x;THe)2Uxyuz=%ocbLrU z>PIrG+32v%XM=Ptmmj0IcCRBI{Q?yV0 z+^G45VzjHz52)(JOy`NbuA96w+p1N`e;kE;^BF3s7%hb9@lL#_2ZKJZTt%bZ)lbyi zS5RIN!)e$X=%pc(Mo;njXdv$O8X}w|Xy8vFFqYJPcAZABzEHlqr`~|V0R9+U98oA=;jpCZnQ0-^24Nx znHgk(=c_!_M#daP+@Czs<6rlsL-@r(KKh=B3Uix|t#3;WnpPwt%8VUObyYS+d9H*@ zLCEf<>sDYXS_nGq4Nl7(rU*RTEc0_8h63LD+ty zCz{h^^Dj;`)(-*Vi;{P~ZFNXQ*FjG=3o?4Z>v9$6y$|v@GrY6;A+RV?j*hcRwQ`xP zN0THY@9d{lVUX^6-zch1f;hI*a`5N@)w(1n%O0~tZYq?DDJ?$0+2>HeOrw+NxaH9I zt;=JVN!g-wgM|JF6lp1`NXDxvSV+^`cqxXG__Md#rd%FF0Q`KvRABj{1;O`|q3-ISQF?^?Ll}Q3&}Wm7*F}$#sD4)0W3Fp-=B}bNT&lOwD4d|%KFSsuNe$4Aqeze(BcZVDrF|>! zv!O{_=@&p9pE)CA>oh3fkgPbs$X~>7{(W7N;qs?7731AL*dJ5%uLf9T382y z-6SU}7RReHR@pDMG$pS<$tKIjpIyf7C&cPYP&EX&&%2fG_o=-ulKv~+S`bfmq)EKb zaVM;3^P8DqyuNoI$8xwu&xtB8RoS~xNx!%t>nA8iAkEa|`_|X<>2=jVy*xTxy2ffR z?`(-vOmDG;Ng_8fi4MfHS9JtHKtLSli+Y;QR+&A@wd)9Wk1#x%lw)CHJzqzsFsi8M za|8rIKoneljRLQ?nvobcIlLxrObCKN0|4^yRyrKw`AQb&1Db=@)vA5(sr5-qb&Wk4 zl|Fu4qjNl*m45JJv6_B&adDHHj?X>?%*zz?t?9Cq3xGv=QbJVn%$*i2>0BzR)Y&qZ zO@BE|%H@`nn_TytjJN9K`#mt(%!Nv7L{lkue*36KQ%UIwH|}IU6cT9gd8N_HLNI1{ zV20g#WS!sV_z|OD;2O``PQg%mp8IkMTK3`3*dxbj?f_AwEY3J&j85oLN?p%oMeD7ncJGQl+{%; zo~9N;9_GMmRRVO;?p4sY1qD~KN~xk zS&jy$>i2=_>x&^amo+r}*b51jAKhp*V^xD=6A||*0pD|6Xz2LLo#|d}Pq#w9D17$w zW0mQ6)|~dNO*48DbP(n`=|G-uVbVGjm1pdxXPxo4aibG80-YzVDQl+`fi&o;q zX*C^7k#>*-4idUg%{83@2YkBSd{-%di}^g?!FdW)%8Thf%`w#v`1%$uq$I#X|Hx;p z=hN>;7Tk_I!O^FJku85PWd|*$&!;ER0uG&Gz}+94?Fi7(-;2la0tbjn&_|;Y-V&t? zzG_o_*0*E8=d@2)(6=M$TKDZ&CVk;>F{_VtJ1Q_tjUd15T$ISe*6ar848?O>%dg{C zizKYF=kE@Gg%gAeV+}WJi1k`_kKzMoZ8amxHNUlrWj6~WX?bh0FzU#AK z4#dysfws`h5HTIhSR|bCdoK~k;&ZBySnu6D9`}VI(z+ga;j%cVJT(|K6w9ZpW6@`n z9g&GyP3Gy{8j;l1&`nmI*M8~?>vASt6cW)Pe`=PpklEUAgWS{h(kr?oQU~PX>9)+d25cJML_6ea=GCB@!Zdp ziu~7T3BH(v-A~S;swyP1XsQHM17Wky1A@$w*UEFWyQL7L`it65D(>0B;rJnqeCTxz0@0-6fZJB{R&Hww8`q{H8JZ!4^bs|p&S z8D;r(YqZZ#Rwq4$5AW{ntL7(5uQ?x>_29uG?MtQ8PLGP5$ps1yLC+S5THI^zg#~#a ziNqvaL06J~VJkEr*RJ=yXYCr9pB7XgHKjfEM zJmPo9lPC2l3pnPh^~lO0l}E!6_n}u>9RNVZ)Ut|0M*fn##K?Po@*TCaSWK!o*@X#0 z)5Y^d>$W5FTYD2|4i^U;7UK*QH&IPX*0-;)TPD{UWYGG;pgCotfxo^ju$xx-x(;V) zNf9j?f8BL5UYy?c!*BCB06JRj^L0KP&$cx7c?q%k__GmcDKM#gxYo^Jf1-JKX3=3# zsxnRfW_n(eaM!7+UfH;AcetpHV4z9V?M-r7GL^#5gb01>zQs>YD$jHr1A)Wu)Fniy zq0$PzA#iCP42%>Q^-Co2>!4Uo-%%`e`IL38wDi1DOD3m>)ZaOvb-p=NyWbk*hH!|w zW?XK&%n?VkuB_YPtV$(-eTLgq-(gKA&+}56f2p~9<68Rk`Cz#z;+Esr zwEyVTb7SC80PahJfO%!fNg|y^srQ*lSRP~UvQ1D$jLb6cB^^FpjLpE<63;Q5zW7i+ z1o#ZjrGnqpiHGy`B6?8ZT%nc;gFDKLX(UaO;Uo^~0Iy@O&%;X))E-&S!}2;Y97R4d zF^Oy%eHu32>`X8tlzt@~BsxNYjHVtu>f#-|N~2+f4|ZqZCi+a(!Kn7ngPZvko-?G8 zHsc6ZmP{{2uN8j5QkE;6-!Q^JAcj<_|6XiPE)T9nh#QNK1bw-!tn*=U#f%=6qJd+( zV7I$*lH1h)a=eDW(|k|P65G+*1v@3Z8FD<4TpYSG6woKR?8|b`N=v_*rgW=eQ`S$U zC~cKuDI>ucM~dAJI4L_>E*rsfF#Pb*TTE1eVMBhN8R_xB1|h)l*;dGV(iM;M5DCjSL_>x&uo|2R>u z6R~QM*j*nR{#=?QK_Z89A$6FxwQfx{F%UGIUkU$FEmRo)JWv{r3CH4juv=9ICn!Cd zZZu=QtEKiP-PIHAf3DFo5T;-vW^T z0K>vSi4J4p?1t+0#z>CPW9b_Qr^a*a$yFWye}Qq1;=Ns3Rh83gZB#l-B+Nz$*YYlI-KZ-^LfprY=?}B2oy@OQxucI-|#C-n&G=HIl361by zk6L6v;UGm=jp8)r!1}*7ne!z<`TE_xE|7U`aa;al;z%=y3)-(A&uCO01~1VT3I479 zD+uxq=xG&%pZs0-=FP7isqk>;=_yaFM>h`iC<(uA@|{tEJq8HUsUJjQIQ3NGep~s$ z_#o7Y$O5M*lgy)*gl%$Ova0FKPMH-Om>R}Z$9KNcH!?AD9fW@!{6QUM-i`@p*-w;t zr=#f7TI-A*_;xqWlLWM99~3g;1CZoDV6XDQ|6?Uj{vfDK+Q-MT1kiRRsrJV5Z%knh zNphRnv}&f2YKHA#ESOX^0fS$eg_yw*O_m`2zS2@>Jp0CFf@K8dvUK-|1$0-PF*ukT zKNA?Oc-OF;!3zwcf=3$O1<*3oWO&2LH`N{_QsZziuW}_|{kQ0N+Ct?^+}~#ucNeu$)LL`Q31r%0qxyR zrv}j`7Ysnr@1@vOOD3De$mv=KW)%|%4^PHrGbdlX_cH!3A3aa$&E@sj-TV>~d{*yq zwtmydGuj~x*q+ey0&TaeKiEX(l%a|vg`Er^$=I0q=40WW+e(3%o*&9a?6vpr`~80Au439pu(H#$p}2nuZT_JRk%H-E%|GEP^qPdqn9 znFKZ+6`cC#8j9gUlqi!6Kj%t+-VL3XukJB)-?zSacH$Yc^;N^rRca@SxI^Ez;{8oX zu^W`gERC7;^);tpgKF!f(Net_Ai+f|;=!iOGv}JjWl+s_b{z7{9Q-fMn-uwM$7FEuHSK%-djPzG zE0P#O5yhdK_0St>j0&7-RB=v#NSNAE01HC6mJ{UE2rt*>u~dpLDU z=($2H#|de2(1Al*x_PH0xzP`b7Ormn{8|dEhSbm54gf!1YMqwqyO@Q z(m-zR0Y}{62c)_*AnM&m|Dba+*v z0d;vX6)4KYB#ZXj7CXmkA3ffDr?PTxr6LBqUFvO?#&PbgQg?W% z+#vqY=6kc1{SlT*()Wi;T9u#t@CLhlFIb25A+kG;C}D?fJ+m(6>7mq8SGil( zTut@90@8*-jvK_?hg;i^I^H*Gh_VErB%CD3q^s-#YW3Var&(U0r?~wz!k^w{fv@RK z+`dW*zvhNND@2}?FI(6+0BnaL^WMOKGRs*LuZq$uK*~5OJLSXrdFauwpi2H>$4ElO zsw`_~(~o{mj3e=BUWHgH24E@Hkbo_nrk*2Sek9`})slV%IC!m3d|N~(@N|upTRi=F zZ%Rnk%LXM1b}d;TLtaGw6y8Qyr(LXW_?Zd-C^*qLMYXlHHR6pZJk^P(6}AR|GlsEz zjy?Oa{(QhFIQIMq=;~N>z1H{*u0_%;deUyMh^gV!+9A+TnwbI9RU%UWn4(%BKS!LU4`USQ)|~fBU#F z7kIb&D*n0=jOelbZRCzp!{~@4P6Mh|8`}kAUb~?y_&iPYVCx-BjzD-PalEkmO6=~L zM4)2#k-r1LG1l7BjQ;&kyXOCz*vVAg&R-y(ird^_B38%-NEY&i92&v ze4m+77}y{w6=WFrjBG^nf|YrR3Ntb#t$hi#_--GG@-m^^r&(;GrDfQ7{7_rZ6z=3% zqgXz)W__F|@Z+rRtfS8O6=m#{e}F6idQpj}{m8y$ZDTXsjy`pEH!&Vt4dGP&VJUUr@FjYs`fJgbqMDEcL8foV-x#tW zbnlcO7c1VCc|7%2xo~(@wM62J%6q(#{2oYqlFWB74YEBve#4(wp0P&DS(E`=UF+r1?5%j_JnjjHwqq>m;1~4=; z4QCZy=a}V$A18S&WSp?caqeV3UMexfG6BpTrKLHT)Jo-)@=dUlu->R0a2Wu0VOTmQ zF*VhCYZh+Ar7DMR8C%u^$7EoZ&<7x(u#^KLiEi(^mh6GGfyW{*!K%2_wPVI@tjxf0 zDI^%@%Z-fk5oY#_kQo7_!vD2 zkn!0dhTyrgx1(UwFbFk6a;r9N#je;HKxYV%Qa3Q2 z+mrp2<%dWmc4jUq(UM2}c0obk3?!A8)C;E!o6jT+s!!hBD*+#zFRnL1Lw_#Cj5QQ0 zdIj|*c0U-M^_hz(n}(YBie`;wI|wzpuBG%WKzz0(RXZB|?)Sbi8c6HXz|)O-z-n_Y{-P;P$=x-Cqb(<^OI^7Qx z&${`jofg^gJ_r%-n)NaOU)2 z;{z~>Wa8pd^|Qdj!Lh5L(#9ForIdmT*10aqKl@sXfFg2G7pL~`U95>D31Dm(6(l}y zUjmFSNTX}V`R3f9_*vPxM6|*$P8Rd?DxOiw z7H#m@7*hjqNQL24{^Jp!F)Lhu2CqLo<|9WRu_zD(Qpp9)z51?}s-~8xvPzhU-A{vs z(cey}SBtB-Yy6<9O++_}y@f+Jzx;1Zk^|my2)wfwD51cf!o9 z**g|ovs&;eAJky_fGwgj=16+%ur#sEu4_oy?s7?M>s(Ar8`?DTc-7IEJs(|+&zdJ; z%Ft;pov=e=h0vN5pJu@Vo!w5cpO=qWe4->mR{HGg@mwfz?NJoe@$pAjEBS2dQmLmd z_m-Stq(rdm*=!(3w~xD)1KaxJOuM!DLm?%3Bg&O^R$G`?0blKr??#&(-#^0&{FPE0NtKIOsR;Bs~RQ z>jsU*2{2GL4{z5W!@v#raiCD`j#3(F0$p`}^yxq&mIl?5X(w?V*laHD5_Ng5;ruYQ z$4*s|^_onHcuOc_PvL0RY?ZoJK%7hBkxo3R50XnO_o1WR#LDgX#a)dgRV_qiL6cOH zJB@?PM;MStn7;E~BkZXLJX3k!Cr{DN0NI(b#X*am@_nMVa>{#TNz5%Ux{j|+kb=%t zuQ@U~TBEwxy5ss${OlvEF8#TaYI$f2tuTbc^FvXJhz*8kFG+jKT$|NF6rZQs*%xL$ zFHciip2-b~(H6BPj#Pmcv|U$4s>{;PmgH`gpIraocaFDnj0LR^1PUkkP8Brq=uJ z1zXQ~!qU~lZnXhwQ(;b=9klbh2 zLmJ<^@A#gfUb0nMcxezOB??XZBPjQkw!c~Rwx&k;@h=}kc*mv9OpdgBpoIt~eBGn2wVcz;^OY882GAqo_3 zSoouU?q`vN3b(M)4FoMOC>K_O(S}_oy^Mh6-US>eun>`*^$bLNyz-k$MxPeIIAa*e zj$OFt&+&b|3B|5K5*Ac`UXb4U5U0*%(MBm{h{YUeW+XT!D3+B-x;nOJ5SX981ut*j z^Mubu$ad{`A#_G4e!N{m^N+J#b~YbLVK4Iy?`F`?V09K-abVq8AP^0ve{cZh%L=3? zKwPk%)e63VZigc_QmHcN6%}}%rxSTCj68WxI26nP zBFm;ajl1MZ#KeK>e-_u}mo~knheLd)Y$fcHDe!n6*8~NSlfGJYt$(s~#8pa4np#=t z>LpkfiVqCs{CZuX{)q_ng|_rl^5lbdLb5H%L@7&tkZve0@z`Me!Exqq>t?#tYwdA6 zq0B93ndFIcadB`QGymOiCpswEF5{i-`N@#&mR{$m>7+52({HCkb&f{o0aQ?M*1cz> zCVc13H14Uu0#K%yw>9t9vFXnVs{}bCRs@}5R=+~FE}oV|hMyr28@=Ut3>KBAcc`Tc zP-I1hw`w&Wskhw_;$4c9Dgigqt|L0@d42lmH*wl!-Y$HJeE+ST?gF~CrfSILW8cp= z>9tfeP+l#E2kOo&aiIjsR3I(}bfIeU=We>I9a8P)v1Ct9DRaX@o^!bJ(Tdu=P%iB; zCmiK-Y~0&iZ(`y3cw`s7LdaoVnzoKo3X3^3iTm(M+(!#X6r6hSqW}7SmhwbQL8r5t zFr6bA$VR0YEue3M2MtA^&4>9r8{EdvmcG>HlF+ssDm>8o97Pw$+UAbLn5dJ=}hise9-56w?@pm7u*~jD7DW= zCtSpBZ4+4xYEP?-bEAq8cDkMajBId4=&LM-ahU@aPdWgp`X+1HyZJrd!jlnn2W+l+ z325IU*`r~U27}>_KZ`>i$9;UGHi2ugJW*wp$@vvFj(;KGdJh;23Oah}66|4olC&~T z5H47pvB>dY{IB*KNsKDBIiN~{;t?bOa4g)SzWXKOx0?)kiCf#L(~WAMevytG9?TG6 z?S7o1YDV$|>kE!Btr}I!2^y&T-1_juYQQoOE(i7Jd4RwyvuUyHCWv_FCx~F@%bDc{ zag!t3E>W#wKwFR7PnBdO-EcT@&F#>--84A`3zewAViU~cj}7N>}y%sHgq z^h4$uc3igN<$i&;MGhNp1o-JL!`Dgyy2Ji`-BM}GS&oFsCi@*aj#4CkDvEBSGS&;8 z(%BJX-B12KyCZF{=Dt4VeeD`61oI0=j+DiVW8b&}ebD(;Ez8u28cn;>ZF?G=Ma}<; z5sxC-78@H|I1axn*&S5FXK}yiqC?y&+Kve`UR}S9f3kPkas;X|h)bN3n3#wQPzfVH z-zmLDW&&v)-zO_&TK<%!S0Rthb8AHK1e#asd?>ST(Cqv6LSta51m+Dk7MV9$9H1&j4nP`{*kMcO_aX;tJU(~6GHk2cs`_=aJH0#ZWe+% zxj)G}k{Hq9fJD>j#ymm%+Zy%n8hiySAkl2_@Zl={>F0_1SLzU#UdZ|1-~VkVJHA)y z&;_Y`@ULr>f42YML(geN?JBKo9Ds!+f7|ar2lD}A0HygC)_uthCZmPm6mA>wcQjT~ z|KZDUP*_;la}+9PO2&)56HZjN?q$XG$Lo^BNFarA>Q$&T);^oQ;(ZtCYU2MNd)5Kb zbFFQj;3o@fpW z+W;EfJld`-KCH8;7jIEXDA|*M4xewV|2Oa9KTQN|xa0&+`VS_KmTZqk{AO( zH*%E^zsgMh;RQf$fgl)Wi9_HcpeeUj;aJ!60A*+Ps! z55GnJo$(i0^WSF=6S~sLiC$ML{kc=Q$OlWu3UK`9pa12;TqT30RcR${QvQ79bFr Date: Thu, 9 Nov 2023 03:57:59 -0800 Subject: [PATCH 018/112] Add more logging Reviewed By: antonk52 Differential Revision: D51154065 fbshipit-source-id: 7532d1e141562adacb9170b112185bd6f730cc5a --- desktop/flipper-server-core/src/FlipperServerImpl.tsx | 4 +--- .../certificate-exchange/certificate-utils.tsx | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index c2407ff2e..84e32cd5b 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -118,9 +118,7 @@ export class FlipperServerImpl implements FlipperServer { keytarModule?: KeytarModule, ) { setFlipperServerConfig(config); - console.log( - 'Loaded flipper config, paths: ' + JSON.stringify(config.paths, null, 2), - ); + console.info('Loaded flipper config: ' + JSON.stringify(config, null, 2)); setProcessState(config.settings); const server = (this.server = new ServerController(this)); diff --git a/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx index ba5d9702a..78d47ca6e 100644 --- a/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx @@ -311,6 +311,7 @@ const exportTokenToManifest = async (token: string) => { const manifestPath = getManifestPath(config); try { + console.info('Reading manifest at path', manifestPath); const manifestData = await fs.readFile(manifestPath, { encoding: 'utf-8', }); @@ -319,10 +320,12 @@ const exportTokenToManifest = async (token: string) => { const newManifestData = JSON.stringify(manifest, null, 4); + console.info('Export token to manifest at path', manifestPath); await fs.writeFile(manifestPath, newManifestData); } catch (e) { console.error( 'Unable to export authentication token to manifest, may be non existent.', + e, ); } }; From 284dee046070241e7f68606eea5ef3400a1c6cdf Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 9 Nov 2023 05:08:16 -0800 Subject: [PATCH 019/112] Extend default list of operators for unconfigured columns Summary: It does not make too much sense to keep the unknown value conversion behind a flag. It is relatively cheap. Let's do it for all strings. Also, let's extend the list of default string operators Reviewed By: lblasa Differential Revision: D51115573 fbshipit-source-id: a62c08a90d8ddf6f23f59412c3fd981e19225e47 --- .../src/ui/PowerSearch/PowerSearchConfig.tsx | 1 - .../DataTableDefaultPowerSearchOperators.tsx | 58 ++++++------------- .../data-table/DataTableWithPowerSearch.tsx | 10 ++-- 3 files changed, 25 insertions(+), 44 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx index b262ca63f..67a65ca0a 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx @@ -33,7 +33,6 @@ export type StringOperatorConfig = { valueType: StringFilterValueType; key: string; label: string; - handleUnknownValues?: boolean; }; export type StringSetOperatorConfig = { diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx index 3641c88b6..82384ad18 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx @@ -8,12 +8,8 @@ */ import dayjs from 'dayjs'; -import {getFlipperLib} from 'flipper-plugin-core'; import {OperatorConfig} from '../PowerSearch'; -import { - FloatOperatorConfig, - StringOperatorConfig, -} from '../PowerSearch/PowerSearchConfig'; +import {FloatOperatorConfig} from '../PowerSearch/PowerSearchConfig'; export type PowerSearchOperatorProcessor = ( powerSearchOperatorConfig: OperatorConfig, @@ -22,29 +18,25 @@ export type PowerSearchOperatorProcessor = ( ) => boolean; export const dataTablePowerSearchOperators = { - string_contains: (handleUnknownValues?: boolean) => ({ + string_contains: () => ({ label: 'contains', key: 'string_contains', valueType: 'STRING', - handleUnknownValues, }), - string_not_contains: (handleUnknownValues?: boolean) => ({ + string_not_contains: () => ({ label: 'does not contain', key: 'string_not_contains', valueType: 'STRING', - handleUnknownValues, }), - string_matches_exactly: (handleUnknownValues?: boolean) => ({ + string_matches_exactly: () => ({ label: 'is', key: 'string_matches_exactly', valueType: 'STRING', - handleUnknownValues, }), - string_not_matches_exactly: (handleUnknownValues?: boolean) => ({ + string_not_matches_exactly: () => ({ label: 'is not', key: 'string_not_matches_exactly', valueType: 'STRING', - handleUnknownValues, }), searializable_object_contains: () => ({ label: 'contains', @@ -223,22 +215,12 @@ const tryConvertingUnknownToString = (value: unknown): string | null => { }; export const dataTablePowerSearchOperatorProcessorConfig = { - string_contains: (operator, searchValue: string, value: string) => - !!( - (operator as StringOperatorConfig).handleUnknownValues && - getFlipperLib().GK('flipper_power_search_auto_json_stringify') - ? tryConvertingUnknownToString(value) - : value - ) + string_contains: (_operator, searchValue: string, value: string) => + !!tryConvertingUnknownToString(value) ?.toLowerCase() .includes(searchValue.toLowerCase()), - string_not_contains: (operator, searchValue: string, value: string) => - !( - (operator as StringOperatorConfig).handleUnknownValues && - getFlipperLib().GK('flipper_power_search_auto_json_stringify') - ? tryConvertingUnknownToString(value) - : value - ) + string_not_contains: (_operator, searchValue: string, value: string) => + !tryConvertingUnknownToString(value) ?.toLowerCase() .includes(searchValue.toLowerCase()), searializable_object_contains: ( @@ -251,16 +233,10 @@ export const dataTablePowerSearchOperatorProcessorConfig = { searchValue: string, value: object, ) => !JSON.stringify(value).toLowerCase().includes(searchValue.toLowerCase()), - string_matches_exactly: (operator, searchValue: string, value: string) => - ((operator as StringOperatorConfig).handleUnknownValues && - getFlipperLib().GK('flipper_power_search_auto_json_stringify') - ? tryConvertingUnknownToString(value) - : value) === searchValue, - string_not_matches_exactly: (operator, searchValue: string, value: string) => - ((operator as StringOperatorConfig).handleUnknownValues && - getFlipperLib().GK('flipper_power_search_auto_json_stringify') - ? tryConvertingUnknownToString(value) - : value) !== searchValue, + string_matches_exactly: (_operator, searchValue: string, value: string) => + tryConvertingUnknownToString(value) === searchValue, + string_not_matches_exactly: (_operator, searchValue: string, value: string) => + tryConvertingUnknownToString(value) !== searchValue, // See PowerSearchStringSetTerm string_set_contains_any_of: ( _operator, @@ -268,7 +244,9 @@ export const dataTablePowerSearchOperatorProcessorConfig = { value: string, ) => searchValue.some((item) => - value.toLowerCase().includes(item.toLowerCase()), + tryConvertingUnknownToString(value) + ?.toLowerCase() + .includes(item.toLowerCase()), ), string_set_contains_none_of: ( _operator, @@ -276,7 +254,9 @@ export const dataTablePowerSearchOperatorProcessorConfig = { value: string, ) => !searchValue.some((item) => - value.toLowerCase().includes(item.toLowerCase()), + tryConvertingUnknownToString(value) + ?.toLowerCase() + .includes(item.toLowerCase()), ), int_equals: (_operator, searchValue: number, value: number) => value === searchValue, diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx index d3f6beb5e..6082a1f14 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -361,10 +361,12 @@ export function DataTable( // If no power search config provided we treat every input as a string if (!column.powerSearchConfig) { columnPowerSearchOperators = [ - dataTablePowerSearchOperators.string_contains(true), - dataTablePowerSearchOperators.string_not_contains(true), - dataTablePowerSearchOperators.string_matches_exactly(true), - dataTablePowerSearchOperators.string_not_matches_exactly(true), + dataTablePowerSearchOperators.string_contains(), + dataTablePowerSearchOperators.string_not_contains(), + dataTablePowerSearchOperators.string_matches_exactly(), + dataTablePowerSearchOperators.string_not_matches_exactly(), + dataTablePowerSearchOperators.string_set_contains_any_of(), + dataTablePowerSearchOperators.string_set_contains_none_of(), ]; } else if (Array.isArray(column.powerSearchConfig)) { columnPowerSearchOperators = column.powerSearchConfig; From b5cb7fcce21fe375f16a5c4019d5176dbe563d29 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 9 Nov 2023 05:08:16 -0800 Subject: [PATCH 020/112] Support simplified power search config Summary: It is quite cumbersome to list all of the operators. Much simpler to use a predefined set of power search operators we set up for each specific filed type Reviewed By: lblasa Differential Revision: D51116029 fbshipit-source-id: 5dd0b7f4176097109666107dfc3cab996379b818 --- .../DataTableDefaultPowerSearchOperators.tsx | 17 ++- .../data-table/DataTableWithPowerSearch.tsx | 133 ++++++++++++++++-- 2 files changed, 130 insertions(+), 20 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx index 82384ad18..6b8d8d250 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx @@ -9,7 +9,10 @@ import dayjs from 'dayjs'; import {OperatorConfig} from '../PowerSearch'; -import {FloatOperatorConfig} from '../PowerSearch/PowerSearchConfig'; +import { + EnumLabels, + FloatOperatorConfig, +} from '../PowerSearch/PowerSearchConfig'; export type PowerSearchOperatorProcessor = ( powerSearchOperatorConfig: OperatorConfig, @@ -110,38 +113,38 @@ export const dataTablePowerSearchOperators = { valueType: 'FLOAT', }), // { [enumValue]: enumLabel } - enum_is: (enumLabels: Record) => ({ + enum_is: (enumLabels: EnumLabels) => ({ label: 'is', key: 'enum_is', valueType: 'ENUM', enumLabels, }), - enum_is_nullish_or: (enumLabels: Record) => ({ + enum_is_nullish_or: (enumLabels: EnumLabels) => ({ label: 'is nullish or', key: 'enum_is_nullish_or', valueType: 'ENUM', enumLabels, }), - enum_is_not: (enumLabels: Record) => ({ + enum_is_not: (enumLabels: EnumLabels) => ({ label: 'is not', key: 'enum_is_not', valueType: 'ENUM', enumLabels, }), // TODO: Support logical operations (AND, OR, NOT) to combine primitive operators instead of adding new complex operators! - enum_set_is_nullish_or_any_of: (enumLabels: Record) => ({ + enum_set_is_nullish_or_any_of: (enumLabels: EnumLabels) => ({ label: 'is nullish or any of', key: 'enum_set_is_nullish_or_any_of', valueType: 'ENUM_SET', enumLabels, }), - enum_set_is_any_of: (enumLabels: Record) => ({ + enum_set_is_any_of: (enumLabels: EnumLabels) => ({ label: 'is any of', key: 'enum_set_is_any_of', valueType: 'ENUM_SET', enumLabels, }), - enum_set_is_none_of: (enumLabels: Record) => ({ + enum_set_is_none_of: (enumLabels: EnumLabels) => ({ label: 'is none of', key: 'enum_set_is_none_of', valueType: 'ENUM_SET', diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx index 6082a1f14..e2a38c57e 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -143,6 +143,36 @@ type DataTableInput = dataSource?: undefined; }; +type PowerSearchSimplifiedConfig = + | {type: 'enum'; enumLabels: EnumLabels} + | {type: 'int'} + | {type: 'float'} + | {type: 'string'} + | {type: 'date'} + | {type: 'dateTime'} + | {type: 'object'}; +type PowerSearchExtendedConfig = { + operators: OperatorConfig[]; + useWholeRow?: boolean; + /** + * Auto-generate enum options based on the data. + * Requires the column to be set as a secondary "index" (single column, not a compound multi-column index). + * See https://fburl.com/code/0waicx6p + */ + inferEnumOptionsFromData?: boolean; +}; + +const powerSearchConfigIsExtendedConfig = ( + powerSearchConfig: + | undefined + | PowerSearchSimplifiedConfig + | OperatorConfig[] + | false + | PowerSearchExtendedConfig, +): powerSearchConfig is PowerSearchExtendedConfig => + !!powerSearchConfig && + Array.isArray((powerSearchConfig as PowerSearchExtendedConfig).operators); + export type DataTableColumn = { //this can be a dotted path into a nest objects. e.g foo.bar key: keyof T & string; @@ -157,18 +187,10 @@ export type DataTableColumn = { inversed?: boolean; sortable?: boolean; powerSearchConfig?: + | PowerSearchSimplifiedConfig | OperatorConfig[] | false - | { - operators: OperatorConfig[]; - useWholeRow?: boolean; - /** - * Auto-generate enum options based on the data. - * Requires the column to be set as a secondary "index" (single column, not a compound multi-column index). - * See https://fburl.com/code/0waicx6p - */ - inferEnumOptionsFromData?: boolean; - }; + | PowerSearchExtendedConfig; }; export interface TableRowRenderContext { @@ -287,8 +309,7 @@ export function DataTable( for (const column of columns) { if ( - typeof column.powerSearchConfig === 'object' && - !Array.isArray(column.powerSearchConfig) && + powerSearchConfigIsExtendedConfig(column.powerSearchConfig) && column.powerSearchConfig.inferEnumOptionsFromData ) { if (!secondaryIndeciesKeys.has(column.key)) { @@ -370,7 +391,7 @@ export function DataTable( ]; } else if (Array.isArray(column.powerSearchConfig)) { columnPowerSearchOperators = column.powerSearchConfig; - } else { + } else if (powerSearchConfigIsExtendedConfig(column.powerSearchConfig)) { columnPowerSearchOperators = column.powerSearchConfig.operators; useWholeRow = !!column.powerSearchConfig.useWholeRow; @@ -387,6 +408,92 @@ export function DataTable( }), ); } + } else { + switch (column.powerSearchConfig.type) { + case 'date': { + columnPowerSearchOperators = [ + dataTablePowerSearchOperators.same_as_absolute_date_no_time(), + dataTablePowerSearchOperators.older_than_absolute_date_no_time(), + dataTablePowerSearchOperators.newer_than_absolute_date_no_time(), + ]; + break; + } + case 'dateTime': { + columnPowerSearchOperators = [ + dataTablePowerSearchOperators.older_than_absolute_date(), + dataTablePowerSearchOperators.newer_than_absolute_date(), + ]; + break; + } + case 'string': { + columnPowerSearchOperators = [ + dataTablePowerSearchOperators.string_contains(), + dataTablePowerSearchOperators.string_not_contains(), + dataTablePowerSearchOperators.string_matches_exactly(), + dataTablePowerSearchOperators.string_not_matches_exactly(), + dataTablePowerSearchOperators.string_set_contains_any_of(), + dataTablePowerSearchOperators.string_set_contains_none_of(), + ]; + break; + } + case 'int': { + columnPowerSearchOperators = [ + dataTablePowerSearchOperators.int_equals(), + dataTablePowerSearchOperators.int_greater_or_equal(), + dataTablePowerSearchOperators.int_greater_than(), + dataTablePowerSearchOperators.int_less_or_equal(), + dataTablePowerSearchOperators.int_less_than(), + ]; + break; + } + case 'float': { + columnPowerSearchOperators = [ + dataTablePowerSearchOperators.float_equals(), + dataTablePowerSearchOperators.float_greater_or_equal(), + dataTablePowerSearchOperators.float_greater_than(), + dataTablePowerSearchOperators.float_less_or_equal(), + dataTablePowerSearchOperators.float_less_than(), + ]; + break; + } + case 'enum': { + columnPowerSearchOperators = [ + dataTablePowerSearchOperators.enum_is( + column.powerSearchConfig.enumLabels, + ), + dataTablePowerSearchOperators.enum_is_not( + column.powerSearchConfig.enumLabels, + ), + dataTablePowerSearchOperators.enum_is_nullish_or( + column.powerSearchConfig.enumLabels, + ), + dataTablePowerSearchOperators.enum_set_is_any_of( + column.powerSearchConfig.enumLabels, + ), + dataTablePowerSearchOperators.enum_set_is_none_of( + column.powerSearchConfig.enumLabels, + ), + dataTablePowerSearchOperators.enum_set_is_nullish_or_any_of( + column.powerSearchConfig.enumLabels, + ), + ]; + break; + } + case 'object': { + columnPowerSearchOperators = [ + dataTablePowerSearchOperators.searializable_object_contains(), + dataTablePowerSearchOperators.searializable_object_not_contains(), + ]; + break; + } + default: { + throw new Error( + `Unknown power search config type ${JSON.stringify( + column.powerSearchConfig, + )}`, + ); + } + } } const columnFieldConfig: FieldConfig = { From 54217f2c79b18e42ead3f381329cf5bd345c7e0d Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 9 Nov 2023 05:08:16 -0800 Subject: [PATCH 021/112] Use simplified power search config Reviewed By: lblasa Differential Revision: D51116200 fbshipit-source-id: a73036020649c06cb5afeb78d9c219a77dac7d4a --- desktop/plugins/public/network/index.tsx | 57 +++++------------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/desktop/plugins/public/network/index.tsx b/desktop/plugins/public/network/index.tsx index cc0e7fd7c..833355774 100644 --- a/desktop/plugins/public/network/index.tsx +++ b/desktop/plugins/public/network/index.tsx @@ -666,20 +666,14 @@ const baseColumns: DataTableColumn[] = [ key: 'requestTime', title: 'Request Time', width: 120, - powerSearchConfig: [ - dataTablePowerSearchOperators.older_than_absolute_date(), - dataTablePowerSearchOperators.newer_than_absolute_date(), - ], + powerSearchConfig: {type: 'dateTime'}, }, { key: 'responseTime', title: 'Response Time', width: 120, visible: false, - powerSearchConfig: [ - dataTablePowerSearchOperators.older_than_absolute_date(), - dataTablePowerSearchOperators.newer_than_absolute_date(), - ], + powerSearchConfig: {type: 'dateTime'}, }, { key: 'requestData', @@ -687,30 +681,17 @@ const baseColumns: DataTableColumn[] = [ width: 120, visible: false, formatters: formatOperationName, - powerSearchConfig: [ - dataTablePowerSearchOperators.searializable_object_contains(), - dataTablePowerSearchOperators.searializable_object_not_contains(), - ], + powerSearchConfig: {type: 'object'}, }, { key: 'domain', - powerSearchConfig: [ - dataTablePowerSearchOperators.string_contains(), - dataTablePowerSearchOperators.string_not_contains(), - dataTablePowerSearchOperators.string_matches_exactly(), - dataTablePowerSearchOperators.string_not_matches_exactly(), - ], + powerSearchConfig: {type: 'string'}, }, { key: 'url', title: 'Full URL', visible: false, - powerSearchConfig: [ - dataTablePowerSearchOperators.string_contains(), - dataTablePowerSearchOperators.string_not_contains(), - dataTablePowerSearchOperators.string_matches_exactly(), - dataTablePowerSearchOperators.string_not_matches_exactly(), - ], + powerSearchConfig: {type: 'string'}, }, { key: 'method', @@ -718,6 +699,8 @@ const baseColumns: DataTableColumn[] = [ width: 70, powerSearchConfig: { operators: [ + dataTablePowerSearchOperators.enum_is({}), + dataTablePowerSearchOperators.enum_is_not({}), dataTablePowerSearchOperators.enum_set_is_any_of({}), dataTablePowerSearchOperators.enum_set_is_none_of({}), ], @@ -731,6 +714,8 @@ const baseColumns: DataTableColumn[] = [ align: 'right', powerSearchConfig: { operators: [ + dataTablePowerSearchOperators.enum_is({}), + dataTablePowerSearchOperators.enum_is_not({}), dataTablePowerSearchOperators.enum_set_is_any_of({}), dataTablePowerSearchOperators.enum_set_is_none_of({}), ], @@ -743,13 +728,7 @@ const baseColumns: DataTableColumn[] = [ width: 100, formatters: formatBytes, align: 'right', - powerSearchConfig: [ - dataTablePowerSearchOperators.float_equals(), - dataTablePowerSearchOperators.float_greater_than(), - dataTablePowerSearchOperators.float_less_than(), - dataTablePowerSearchOperators.float_greater_or_equal(), - dataTablePowerSearchOperators.float_less_or_equal(), - ], + powerSearchConfig: {type: 'float'}, }, { key: 'responseLength', @@ -757,13 +736,7 @@ const baseColumns: DataTableColumn[] = [ width: 100, formatters: formatBytes, align: 'right', - powerSearchConfig: [ - dataTablePowerSearchOperators.float_equals(), - dataTablePowerSearchOperators.float_greater_than(), - dataTablePowerSearchOperators.float_less_than(), - dataTablePowerSearchOperators.float_greater_or_equal(), - dataTablePowerSearchOperators.float_less_or_equal(), - ], + powerSearchConfig: {type: 'float'}, }, { key: 'duration', @@ -771,13 +744,7 @@ const baseColumns: DataTableColumn[] = [ width: 100, formatters: formatDuration, align: 'right', - powerSearchConfig: [ - dataTablePowerSearchOperators.float_equals(), - dataTablePowerSearchOperators.float_greater_than(), - dataTablePowerSearchOperators.float_less_than(), - dataTablePowerSearchOperators.float_greater_or_equal(), - dataTablePowerSearchOperators.float_less_or_equal(), - ], + powerSearchConfig: {type: 'float'}, }, ]; From d54bd7c3baa944ef39b158bd9fdb6e2163414a29 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 9 Nov 2023 08:25:28 -0800 Subject: [PATCH 022/112] Refactor browser connection performance tracking Reviewed By: lblasa Differential Revision: D51158256 fbshipit-source-id: 17e020dd3c26ac73bf2cf0ceb4c664638c6778e9 --- desktop/flipper-common/src/server-types.tsx | 1 + .../src/server/attachSocketServer.tsx | 15 +---------- desktop/flipper-server-core/src/tracker.tsx | 1 + desktop/flipper-server/src/index.tsx | 26 +++++++++++++++++-- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index fafae066b..d5962caa1 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -168,6 +168,7 @@ export type FlipperServerEvents = { 'plugins-server-add-on-message': ExecuteMessage; 'download-file-update': DownloadFileUpdate; 'server-log': LoggerInfo; + 'browser-connection-created': {}; }; export type OS = diff --git a/desktop/flipper-server-core/src/server/attachSocketServer.tsx b/desktop/flipper-server-core/src/server/attachSocketServer.tsx index dd389522d..b397bbf37 100644 --- a/desktop/flipper-server-core/src/server/attachSocketServer.tsx +++ b/desktop/flipper-server-core/src/server/attachSocketServer.tsx @@ -27,7 +27,6 @@ import { } from 'flipper-server-companion'; import {URLSearchParams} from 'url'; import {tracker} from '../tracker'; -import {performance} from 'perf_hooks'; import {getFlipperServerConfig} from '../FlipperServerConfig'; const safe = (f: () => void) => { @@ -53,14 +52,6 @@ export function attachSocketServer( server: FlipperServerImpl, companionEnv: FlipperServerCompanionEnv, ) { - const t0 = performance.now(); - const browserConnectionTimeout = setTimeout(() => { - tracker.track('browser-connection-created', { - successful: false, - timeMS: performance.now() - t0, - }); - }, 20000); - socket.on('connection', (client, req) => { const clientAddress = (req.socket.remoteAddress && @@ -74,11 +65,7 @@ export function attachSocketServer( clearTimeout(disconnectTimeout); } - clearTimeout(browserConnectionTimeout); - tracker.track('browser-connection-created', { - successful: true, - timeMS: performance.now() - t0, - }); + server.emit('browser-connection-created', {}); let connected = true; diff --git a/desktop/flipper-server-core/src/tracker.tsx b/desktop/flipper-server-core/src/tracker.tsx index 03dc25d3a..e9a8f3e97 100644 --- a/desktop/flipper-server-core/src/tracker.tsx +++ b/desktop/flipper-server-core/src/tracker.tsx @@ -54,6 +54,7 @@ type TrackerEvents = { 'browser-connection-created': { successful: boolean; timeMS: number; + timedOut: boolean; }; 'app-connection-created': AppConnectionPayload; 'app-connection-secure-attempt': AppConnectionPayload; diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index ba4ccbc10..95403e0e2 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -95,9 +95,25 @@ const rootPath = argv.bundler : path.resolve(__dirname, '..'); // In pre-packaged versions of the server, static is copied inside the package. const staticPath = path.join(rootPath, 'static'); -async function start() { - const t0 = performance.now(); +const t0 = performance.now(); +const browserConnectionTimeout = setTimeout(() => { + tracker.track('browser-connection-created', { + successful: false, + timeMS: performance.now() - t0, + timedOut: true, + }); +}, 10000); +const reportBrowserConnection = (successful: boolean) => { + clearTimeout(browserConnectionTimeout); + tracker.track('browser-connection-created', { + successful, + timeMS: performance.now() - t0, + timedOut: false, + }); +}; + +async function start() { const isProduction = process.env.NODE_ENV !== 'development' && process.env.NODE_ENV !== 'test'; const environmentInfo = await getEnvironmentInfo( @@ -206,6 +222,10 @@ async function start() { environmentInfo, ); + flipperServer.once('browser-connection-created', () => { + reportBrowserConnection(true); + }); + const t5 = performance.now(); const serverCreatedMS = t5 - t4; console.info( @@ -311,6 +331,7 @@ process.on('uncaughtException', (error) => { '[flipper-server] uncaught exception, process will exit.', error, ); + reportBrowserConnection(false); process.exit(1); }); @@ -325,5 +346,6 @@ process.on('unhandledRejection', (reason, promise) => { start().catch((e) => { console.error(chalk.red('Server startup error: '), e); + reportBrowserConnection(false); process.exit(1); }); From 69378c4b095be76e6202ef385f3d8bcaca78f09a Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Thu, 9 Nov 2023 09:08:33 -0800 Subject: [PATCH 023/112] Add logs for early exit when writing token to manifest Summary: ^ Reviewed By: aigoncharov Differential Revision: D51154908 fbshipit-source-id: 0c04ee50d07db1478ec5a77faa6d6157b7c9960b --- .../certificate-exchange/certificate-utils.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx index 78d47ca6e..6e632d0e4 100644 --- a/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx @@ -306,6 +306,10 @@ const exportTokenToManifest = async (token: string) => { } if (!config || !config.environmentInfo.isHeadlessBuild) { + console.warn( + 'No configuration or not headless build detected, skipping exporting token to manifest', + config, + ); return; } From 8ef29c8160b07fdaefc9776134bd55d7997b4369 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 9 Nov 2023 14:05:43 -0800 Subject: [PATCH 024/112] Embed auth token into HTML Summary: Auth token used be injected in the manifest file. Instead, have the server injected into the main HTML page. The main driver to this change are: - Simplify - There are instances in which for some reason reading/writing the token from the manifest fails. This will address that problem. Reviewed By: lblasa Differential Revision: D51160521 fbshipit-source-id: 4626fd8f56bc8b61182a53a5d9cf5acad1e723bc --- .../certificate-utils.tsx | 53 +------------------ .../src/server/startServer.tsx | 14 ++++- desktop/flipper-ui-browser/src/global.tsx | 1 + desktop/flipper-ui-browser/src/index.tsx | 16 +++--- desktop/static/index.web.dev.html | 7 ++- desktop/static/index.web.html | 3 +- 6 files changed, 25 insertions(+), 69 deletions(-) diff --git a/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx index 6e632d0e4..216f7b579 100644 --- a/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx @@ -16,11 +16,10 @@ import { } from './openssl-wrapper-with-promises'; import path from 'path'; import tmp, {FileOptions} from 'tmp'; -import {FlipperServerConfig, reportPlatformFailures} from 'flipper-common'; +import {reportPlatformFailures} from 'flipper-common'; import {isTest} from 'flipper-common'; import {flipperDataFolder} from '../../utils/paths'; import * as jwt from 'jsonwebtoken'; -import {getFlipperServerConfig} from '../../FlipperServerConfig'; import {Mutex} from 'async-mutex'; import {createSecureContext} from 'tls'; @@ -288,52 +287,6 @@ const writeToTempFile = async (content: string): Promise => { await fs.writeFile(path, content); return path; }; - -const manifestFilename = 'manifest.json'; -const getManifestPath = (config: FlipperServerConfig): string => { - return path.resolve(config.paths.staticPath, manifestFilename); -}; - -const exportTokenToManifest = async (token: string) => { - console.info('Export token to manifest'); - let config: FlipperServerConfig | undefined; - try { - config = getFlipperServerConfig(); - } catch { - console.warn( - 'Unable to obtain server configuration whilst exporting token to manifest', - ); - } - - if (!config || !config.environmentInfo.isHeadlessBuild) { - console.warn( - 'No configuration or not headless build detected, skipping exporting token to manifest', - config, - ); - return; - } - - const manifestPath = getManifestPath(config); - try { - console.info('Reading manifest at path', manifestPath); - const manifestData = await fs.readFile(manifestPath, { - encoding: 'utf-8', - }); - const manifest = JSON.parse(manifestData); - manifest.token = token; - - const newManifestData = JSON.stringify(manifest, null, 4); - - console.info('Export token to manifest at path', manifestPath); - await fs.writeFile(manifestPath, newManifestData); - } catch (e) { - console.error( - 'Unable to export authentication token to manifest, may be non existent.', - e, - ); - } -}; - export const generateAuthToken = async () => { console.info('Generate client authentication token'); @@ -347,8 +300,6 @@ export const generateAuthToken = async () => { await fs.writeFile(serverAuthToken, token); - await exportTokenToManifest(token); - return token; }; @@ -382,8 +333,6 @@ export const getAuthToken = async (): Promise => { return generateAuthToken(); } - await exportTokenToManifest(token); - return token; }; diff --git a/desktop/flipper-server-core/src/server/startServer.tsx b/desktop/flipper-server-core/src/server/startServer.tsx index 51f1639fa..b1df9b460 100644 --- a/desktop/flipper-server-core/src/server/startServer.tsx +++ b/desktop/flipper-server-core/src/server/startServer.tsx @@ -18,7 +18,10 @@ import exitHook from 'exit-hook'; import {attachSocketServer} from './attachSocketServer'; import {FlipperServerImpl} from '../FlipperServerImpl'; import {FlipperServerCompanionEnv} from 'flipper-server-companion'; -import {validateAuthToken} from '../app-connectivity/certificate-exchange/certificate-utils'; +import { + getAuthToken, + validateAuthToken, +} from '../app-connectivity/certificate-exchange/certificate-utils'; import {tracker} from '../tracker'; import {EnvironmentInfo, isProduction} from 'flipper-common'; import {GRAPH_SECRET} from '../fb-stubs/constants'; @@ -38,6 +41,7 @@ type ReadyForConnections = ( const verifyAuthToken = (req: http.IncomingMessage): boolean => { let token: string | null = null; + if (req.url) { const url = new URL(req.url, `http://${req.headers.host}`); token = url.searchParams.get('token'); @@ -47,6 +51,10 @@ const verifyAuthToken = (req: http.IncomingMessage): boolean => { token = req.headers['x-access-token'] as string; } + if (!isProduction()) { + console.info('[conn] verifyAuthToken -> token', token); + } + if (!token) { console.warn('[conn] A token is required for authentication'); tracker.track('server-auth-token-verification', { @@ -146,16 +154,18 @@ async function startHTTPServer( next(); }); - app.get('/', (_req, res) => { + app.get('/', async (_req, res) => { const resource = isReady ? path.join(config.staticPath, config.entry) : path.join(config.staticPath, 'loading.html'); + const token = await getAuthToken(); fs.readFile(resource, (_err, content) => { const processedContent = content .toString() .replace('GRAPH_SECRET_REPLACE_ME', GRAPH_SECRET) .replace('FLIPPER_APP_VERSION_REPLACE_ME', environmentInfo.appVersion) .replace('FLIPPER_UNIXNAME_REPLACE_ME', environmentInfo.os.unixname) + .replace('FLIPPER_AUTH_TOKEN_REPLACE_ME', token) .replace('FLIPPER_SESSION_ID_REPLACE_ME', sessionId); res.end(processedContent); }); diff --git a/desktop/flipper-ui-browser/src/global.tsx b/desktop/flipper-ui-browser/src/global.tsx index edc96f165..606a72df0 100644 --- a/desktop/flipper-ui-browser/src/global.tsx +++ b/desktop/flipper-ui-browser/src/global.tsx @@ -22,6 +22,7 @@ declare global { FLIPPER_APP_VERSION: string; FLIPPER_SESSION_ID: string; FLIPPER_UNIXNAME: string; + FLIPPER_AUTH_TOKEN: string; flipperShowMessage?(message: string): void; flipperHideMessage?(): void; diff --git a/desktop/flipper-ui-browser/src/index.tsx b/desktop/flipper-ui-browser/src/index.tsx index d6c4b018e..f0d1fc63f 100644 --- a/desktop/flipper-ui-browser/src/index.tsx +++ b/desktop/flipper-ui-browser/src/index.tsx @@ -55,17 +55,12 @@ async function start() { const providerParams = new URL(location.href).searchParams; let token = providerParams.get('token'); if (!token) { - console.info( - '[flipper-client][ui-browser] Get token from manifest instead', - ); - try { - const manifestResponse = await fetch('manifest.json'); - const manifest = await manifestResponse.json(); - token = manifest.token; - } catch (e) { + console.info('[flipper-client][ui-browser] Get token from HTML instead'); + token = window.FLIPPER_AUTH_TOKEN; + if (!token || token === 'FLIPPER_AUTH_TOKEN_REPLACE_ME') { console.warn( - '[flipper-client][ui-browser] Failed to get token from manifest. Error:', - e.message, + '[flipper-client][ui-browser] Failed to get token from HTML', + token, ); } } @@ -73,6 +68,7 @@ async function start() { getLogger().info( '[flipper-client][ui-browser] Token is available: ', token?.length != 0, + token?.length === 460, ); return token; diff --git a/desktop/static/index.web.dev.html b/desktop/static/index.web.dev.html index 0c3606a0a..59d1b1c2d 100644 --- a/desktop/static/index.web.dev.html +++ b/desktop/static/index.web.dev.html @@ -141,13 +141,12 @@ window.FLIPPER_APP_VERSION = 'FLIPPER_APP_VERSION_REPLACE_ME'; window.FLIPPER_SESSION_ID = 'FLIPPER_SESSION_ID_REPLACE_ME'; window.FLIPPER_UNIXNAME = 'FLIPPER_UNIXNAME_REPLACE_ME'; + window.FLIPPER_AUTH_TOKEN = 'FLIPPER_AUTH_TOKEN_REPLACE_ME'; const params = new URL(location.href).searchParams; let token = params.get('token'); if (!token) { - const manifestResponse = await fetch('manifest.json'); - const manifest = await manifestResponse.json(); - token = manifest.token; + token = window.FLIPPER_AUTH_TOKEN } const socket = new WebSocket(`ws://${location.host}?token=${token}`); @@ -212,7 +211,7 @@ setTimeout(() => retry(retries), 1000); } } - + retry(3); }; diff --git a/desktop/static/index.web.html b/desktop/static/index.web.html index 80c2bdc17..dae259b93 100644 --- a/desktop/static/index.web.html +++ b/desktop/static/index.web.html @@ -90,6 +90,7 @@ window.FLIPPER_APP_VERSION = 'FLIPPER_APP_VERSION_REPLACE_ME'; window.FLIPPER_SESSION_ID = 'FLIPPER_SESSION_ID_REPLACE_ME'; window.FLIPPER_UNIXNAME = 'FLIPPER_UNIXNAME_REPLACE_ME'; + window.FLIPPER_AUTH_TOKEN = 'FLIPPER_AUTH_TOKEN_REPLACE_ME'; // load correct theme (n.b. this doesn't handle system value specifically, will assume light in such cases) try { @@ -117,7 +118,7 @@ setTimeout(() => retry(retries), 1000); } } - + retry(3); }; From 6e19c4155c9b2059cbc73b0425323b10725e9ff6 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 9 Nov 2023 14:34:52 -0800 Subject: [PATCH 025/112] Track session length Differential Revision: D51172955 fbshipit-source-id: d4f93564a94e232066347c945fa4798033dc0da1 --- desktop/flipper-server-core/src/server/attachSocketServer.tsx | 4 ++++ desktop/flipper-server-core/src/tracker.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/desktop/flipper-server-core/src/server/attachSocketServer.tsx b/desktop/flipper-server-core/src/server/attachSocketServer.tsx index b397bbf37..f66b0aaa9 100644 --- a/desktop/flipper-server-core/src/server/attachSocketServer.tsx +++ b/desktop/flipper-server-core/src/server/attachSocketServer.tsx @@ -28,6 +28,7 @@ import { import {URLSearchParams} from 'url'; import {tracker} from '../tracker'; import {getFlipperServerConfig} from '../FlipperServerConfig'; +import {performance} from 'perf_hooks'; const safe = (f: () => void) => { try { @@ -53,6 +54,8 @@ export function attachSocketServer( companionEnv: FlipperServerCompanionEnv, ) { socket.on('connection', (client, req) => { + const t0 = performance.now(); + const clientAddress = (req.socket.remoteAddress && ` ${req.socket.remoteAddress}:${req.socket.remotePort}`) || @@ -246,6 +249,7 @@ export function attachSocketServer( tracker.track('server-client-close', { code, error, + sessionLength: performance.now() - t0, }); if ( diff --git a/desktop/flipper-server-core/src/tracker.tsx b/desktop/flipper-server-core/src/tracker.tsx index e9a8f3e97..f39ab7324 100644 --- a/desktop/flipper-server-core/src/tracker.tsx +++ b/desktop/flipper-server-core/src/tracker.tsx @@ -48,7 +48,7 @@ type TrackerEvents = { }; 'server-socket-already-in-use': {}; 'server-open-ui': {browser: boolean; hasToken: boolean}; - 'server-client-close': {code?: number; error?: string}; + 'server-client-close': {code?: number; error?: string; sessionLength: number}; 'server-ws-server-error': {port: number; error: string}; 'server-ready-timeout': {timeout: number}; 'browser-connection-created': { From 8348d617d0b0a9487ffec8f3bd3bb8ff040e1583 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Fri, 10 Nov 2023 03:35:14 -0800 Subject: [PATCH 026/112] Fix token replacements Reviewed By: lblasa Differential Revision: D51196650 fbshipit-source-id: 184c104b32a1d619163c799ae70419e6aad23e98 --- desktop/flipper-server-core/src/server/startServer.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/desktop/flipper-server-core/src/server/startServer.tsx b/desktop/flipper-server-core/src/server/startServer.tsx index b1df9b460..413508674 100644 --- a/desktop/flipper-server-core/src/server/startServer.tsx +++ b/desktop/flipper-server-core/src/server/startServer.tsx @@ -7,7 +7,7 @@ * @format */ -import express, {Express} from 'express'; +import express, {Express, RequestHandler} from 'express'; import http from 'http'; import path from 'path'; import fs from 'fs-extra'; @@ -154,7 +154,7 @@ async function startHTTPServer( next(); }); - app.get('/', async (_req, res) => { + const serveRoot: RequestHandler = async (_req, res) => { const resource = isReady ? path.join(config.staticPath, config.entry) : path.join(config.staticPath, 'loading.html'); @@ -169,7 +169,10 @@ async function startHTTPServer( .replace('FLIPPER_SESSION_ID_REPLACE_ME', sessionId); res.end(processedContent); }); - }); + }; + app.get('/', serveRoot); + app.get('/index.web.html', serveRoot); + app.get('/index.web.dev.html', serveRoot); app.get('/ready', (_req, res) => { tracker.track('server-endpoint-hit', {name: 'ready'}); From 4b3f572205dbf4992dc3955fbec474343e8de75b Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Fri, 10 Nov 2023 03:39:32 -0800 Subject: [PATCH 027/112] Preserve previous error messages Reviewed By: passy Differential Revision: D51197113 fbshipit-source-id: 237c6f1f894cb4d758150ff2bddf14c104d3b381 --- .../src/FlipperServerClient.tsx | 6 ++--- desktop/flipper-ui-browser/src/HMRClient.tsx | 2 +- desktop/flipper-ui-browser/src/global.tsx | 2 +- desktop/flipper-ui-browser/src/index.tsx | 13 +++++++---- desktop/static/index.web.dev.html | 22 +++++++++++++++++-- desktop/static/index.web.html | 21 ++++++++++++++++-- 6 files changed, 53 insertions(+), 13 deletions(-) diff --git a/desktop/flipper-server-client/src/FlipperServerClient.tsx b/desktop/flipper-server-client/src/FlipperServerClient.tsx index 698245198..29985f65e 100644 --- a/desktop/flipper-server-client/src/FlipperServerClient.tsx +++ b/desktop/flipper-server-client/src/FlipperServerClient.tsx @@ -30,11 +30,11 @@ export type {FlipperServer, FlipperServerCommands, FlipperServerExecOptions}; export function createFlipperServer( host: string, port: number, - tokenProvider: () => Promise, + tokenProvider: () => string | null | undefined, onStateChange: (state: FlipperServerState) => void, ): Promise { - const URLProvider = async () => { - const token = await tokenProvider(); + const URLProvider = () => { + const token = tokenProvider(); return `ws://${host}:${port}?token=${token}`; }; diff --git a/desktop/flipper-ui-browser/src/HMRClient.tsx b/desktop/flipper-ui-browser/src/HMRClient.tsx index ad76b730e..be6888346 100644 --- a/desktop/flipper-ui-browser/src/HMRClient.tsx +++ b/desktop/flipper-ui-browser/src/HMRClient.tsx @@ -270,7 +270,7 @@ function showCompileError() { // Symbolicating compile errors is wasted effort // because the stack trace is meaningless: (error as any).preventSymbolication = true; - window.flipperShowMessage?.(message); + window.flipperShowMessage?.({detail: message}); throw error; } diff --git a/desktop/flipper-ui-browser/src/global.tsx b/desktop/flipper-ui-browser/src/global.tsx index 606a72df0..566fd8657 100644 --- a/desktop/flipper-ui-browser/src/global.tsx +++ b/desktop/flipper-ui-browser/src/global.tsx @@ -24,7 +24,7 @@ declare global { FLIPPER_UNIXNAME: string; FLIPPER_AUTH_TOKEN: string; - flipperShowMessage?(message: string): void; + flipperShowMessage?(message: {title?: string; detail?: string}): void; flipperHideMessage?(): void; } } diff --git a/desktop/flipper-ui-browser/src/index.tsx b/desktop/flipper-ui-browser/src/index.tsx index f0d1fc63f..c07298a8f 100644 --- a/desktop/flipper-ui-browser/src/index.tsx +++ b/desktop/flipper-ui-browser/src/index.tsx @@ -51,7 +51,7 @@ async function start() { const params = new URL(location.href).searchParams; - const tokenProvider = async () => { + const tokenProvider = () => { const providerParams = new URL(location.href).searchParams; let token = providerParams.get('token'); if (!token) { @@ -62,6 +62,11 @@ async function start() { '[flipper-client][ui-browser] Failed to get token from HTML', token, ); + window.flipperShowMessage?.({ + detail: + '[flipper-client][ui-browser] Failed to get token from HTML: ' + + token, + }); } } @@ -108,7 +113,7 @@ async function start() { switch (state) { case FlipperServerState.CONNECTING: getLogger().info('[flipper-client] Connecting to server'); - window.flipperShowMessage?.('Connecting to server...'); + window.flipperShowMessage?.({title: 'Connecting to server...'}); break; case FlipperServerState.CONNECTED: getLogger().info( @@ -118,7 +123,7 @@ async function start() { break; case FlipperServerState.DISCONNECTED: getLogger().info('[flipper-client] Disconnected from server'); - window.flipperShowMessage?.('Waiting for server...'); + window.flipperShowMessage?.({title: 'Waiting for server...'}); break; } }, @@ -172,7 +177,7 @@ start().catch((e) => { error: getStringFromErrorLike(e), pwa: window.matchMedia('(display-mode: standalone)').matches, }); - window.flipperShowMessage?.('Failed to start UI with error: ' + e); + window.flipperShowMessage?.({detail: 'Failed to start UI with error: ' + e}); }); async function initializePWA() { diff --git a/desktop/static/index.web.dev.html b/desktop/static/index.web.dev.html index 59d1b1c2d..e79cd0932 100644 --- a/desktop/static/index.web.dev.html +++ b/desktop/static/index.web.dev.html @@ -32,6 +32,7 @@ padding: 50px; overflow: auto; display: flex; + flex-direction: column; align-items: center; justify-content: center; font-size: 20px; @@ -39,6 +40,10 @@ text-align: center; } + .message p { + font-size: 12px; + } + .console { font-family: 'Fira Mono'; width: 600px; @@ -98,6 +103,8 @@

+

+

@@ -121,9 +128,18 @@ const root = document.getElementById('root'); const troubleshootBox = document.getElementById('troubleshoot'); + const troubleshootBoxTitle = document.getElementById('tourbleshoot_title'); + const troubleshootBoxDetails = document.getElementById('tourbleshoot_details'); - function showMessage(text, centered) { - troubleshootBox.innerText = text; + function showMessage({ title, detail }) { + if (title) { + troubleshootBoxTitle.innerText = title + } + if (detail) { + const newMessage = document.createElement('p') + newMessage.innerText = detail; + troubleshootBoxDetails.appendChild(newMessage) + } root.style.display = 'none'; troubleshootBox.style.display = 'flex'; @@ -132,6 +148,8 @@ function hideMessage() { root.style.display = 'block'; troubleshootBox.style.display = 'none'; + troubleshootBoxTitle.innerHTML = '' + troubleshootBoxDetails.innerHTML = '' } window.flipperShowMessage = showMessage; diff --git a/desktop/static/index.web.html b/desktop/static/index.web.html index dae259b93..ec7fda6e4 100644 --- a/desktop/static/index.web.html +++ b/desktop/static/index.web.html @@ -32,12 +32,16 @@ padding: 50px; overflow: auto; display: flex; + flex-direction: column; align-items: center; justify-content: center; font-size: 20px; color: #525252; text-align: center; } + .message p { + font-size: 12px; + } #troubleshoot { display: none; @@ -48,6 +52,8 @@
+

+

@@ -70,9 +76,18 @@ const root = document.getElementById('root'); const troubleshootBox = document.getElementById('troubleshoot'); + const troubleshootBoxTitle = document.getElementById('tourbleshoot_title'); + const troubleshootBoxDetails = document.getElementById('tourbleshoot_details'); - function showMessage(text) { - troubleshootBox.innerText = text; + function showMessage({ title, detail }) { + if (title) { + troubleshootBoxTitle.innerText = title + } + if (detail) { + const newMessage = document.createElement('p') + newMessage.innerText = detail; + troubleshootBoxDetails.appendChild(newMessage) + } root.style.display = 'none'; troubleshootBox.style.display = 'flex'; @@ -81,6 +96,8 @@ function hideMessage() { root.style.display = 'block'; troubleshootBox.style.display = 'none'; + troubleshootBoxTitle.innerHTML = '' + troubleshootBoxDetails.innerHTML = '' } window.flipperShowMessage = showMessage; From 04b4bf7bdfc814d8ed1614d4c494a1ffde03874d Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Fri, 10 Nov 2023 04:11:35 -0800 Subject: [PATCH 028/112] Setup prefetcher during startup Reviewed By: passy Differential Revision: D51198236 fbshipit-source-id: 00781afda72258f2aee64a9d0481117979c49031 --- desktop/flipper-server/src/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index 95403e0e2..b30f56a7b 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -25,6 +25,7 @@ import { compareServerVersion, getEnvironmentInfo, openUI, + setupPrefetcher, shutdownRunningInstance, startFlipperServer, startServer, @@ -297,6 +298,8 @@ async function start() { )} (${serverStartedMS} ms)`, ); + setupPrefetcher(flipperServer.config.settings); + const startupMS = t10 - t0; tracker.track('server-bootstrap-performance', { From d023bcc42e48ec008435f10b2f853ff63bd2e795 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Fri, 10 Nov 2023 04:33:02 -0800 Subject: [PATCH 029/112] remove shortcuts Reviewed By: aigoncharov Differential Revision: D51159922 fbshipit-source-id: fbb59b416f92b9156c74a12247da8d0df07f1a4e --- desktop/flipper-common/src/settings.tsx | 7 --- .../src/utils/settings.tsx | 7 --- .../src/chrome/SettingsSheet.tsx | 56 ------------------ .../flipper-ui-core/src/dispatcher/index.tsx | 2 - .../src/dispatcher/reactNative.tsx | 58 ------------------- desktop/scripts/jest-setup-after.tsx | 3 - 6 files changed, 133 deletions(-) delete mode 100644 desktop/flipper-ui-core/src/dispatcher/reactNative.tsx diff --git a/desktop/flipper-common/src/settings.tsx b/desktop/flipper-common/src/settings.tsx index 9568d7680..14d2ae646 100644 --- a/desktop/flipper-common/src/settings.tsx +++ b/desktop/flipper-common/src/settings.tsx @@ -29,13 +29,6 @@ export type Settings = { */ enablePrefetching: Tristate; idbPath: string; - reactNative: { - shortcuts: { - enabled: boolean; - reload: string; - openDevMenu: string; - }; - }; darkMode: 'dark' | 'light' | 'system'; showWelcomeAtStartup: boolean; suppressPluginErrors: boolean; diff --git a/desktop/flipper-server-core/src/utils/settings.tsx b/desktop/flipper-server-core/src/utils/settings.tsx index 5c4a9b3ba..996a5fe67 100644 --- a/desktop/flipper-server-core/src/utils/settings.tsx +++ b/desktop/flipper-server-core/src/utils/settings.tsx @@ -56,13 +56,6 @@ function getDefaultSettings(): Settings { enablePhysicalIOS: os.platform() === 'darwin', enablePrefetching: Tristate.Unset, idbPath: '/usr/local/bin/idb', - reactNative: { - shortcuts: { - enabled: false, - reload: 'Alt+Shift+R', - openDevMenu: 'Alt+Shift+D', - }, - }, darkMode: 'light', showWelcomeAtStartup: true, suppressPluginErrors: false, diff --git a/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx b/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx index 96cf0a942..373bf7fe5 100644 --- a/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx +++ b/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx @@ -23,7 +23,6 @@ import { ConfigText, URLConfigField, } from './settings/configFields'; -import KeyboardShortcutInput from './settings/KeyboardShortcutInput'; import {isEqual, isMatch, isEmpty} from 'lodash'; import LauncherSettingsPanel from '../fb-stubs/LauncherSettingsPanel'; import { @@ -124,7 +123,6 @@ class SettingsSheet extends Component { enablePhysicalIOS, enablePrefetching, idbPath, - reactNative, darkMode, suppressPluginErrors, persistDeviceData, @@ -294,60 +292,6 @@ class SettingsSheet extends Component { Use System Setting - { - this.setState((prevState) => ({ - updatedSettings: { - ...prevState.updatedSettings, - reactNative: { - ...prevState.updatedSettings.reactNative, - shortcuts: { - ...prevState.updatedSettings.reactNative.shortcuts, - enabled, - }, - }, - }, - })); - }}> - { - this.setState((prevState) => ({ - updatedSettings: { - ...prevState.updatedSettings, - reactNative: { - ...prevState.updatedSettings.reactNative, - shortcuts: { - ...prevState.updatedSettings.reactNative.shortcuts, - reload, - }, - }, - }, - })); - }} - /> - { - this.setState((prevState) => ({ - updatedSettings: { - ...prevState.updatedSettings, - reactNative: { - ...prevState.updatedSettings.reactNative, - shortcuts: { - ...prevState.updatedSettings.reactNative.shortcuts, - openDevMenu, - }, - }, - }, - })); - }} - /> - { - const settings = store.getState().settingsState.reactNative; - - if (!settings?.shortcuts.enabled) { - return; - } - - const shortcuts: ShortcutEventCommand[] = [ - settings.shortcuts.reload && { - shortcut: settings.shortcuts.reload, - command: 'reload', - }, - settings.shortcuts.openDevMenu && { - shortcut: settings.shortcuts.openDevMenu, - command: 'devMenu', - }, - ]; - - shortcuts.forEach( - (shortcut: ShortcutEventCommand) => - shortcut && - shortcut.shortcut && - registerShortcut(shortcut.shortcut, () => { - const devices = store - .getState() - .connections.devices.filter( - (device) => device.os === 'Metro' && !device.isArchived, - ); - - devices.forEach((device) => - device.flipperServer.exec( - 'metro-command', - device.serial, - shortcut.command, - ), - ); - }), - ); -}; diff --git a/desktop/scripts/jest-setup-after.tsx b/desktop/scripts/jest-setup-after.tsx index 57a3066e7..20e2eafc9 100644 --- a/desktop/scripts/jest-setup-after.tsx +++ b/desktop/scripts/jest-setup-after.tsx @@ -184,9 +184,6 @@ function createStubRenderHost(): RenderHost { enablePhysicalIOS: false, enablePrefetching: Tristate.False, idbPath: `/dev/null`, - reactNative: { - shortcuts: {enabled: false, openDevMenu: '', reload: ''}, - }, showWelcomeAtStartup: false, suppressPluginErrors: false, persistDeviceData: false, From a1070b8ceab6fb7dbe9a23423d51def182930666 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Fri, 10 Nov 2023 04:34:09 -0800 Subject: [PATCH 030/112] Add regex support Reviewed By: LukeDefeo Differential Revision: D51198464 fbshipit-source-id: 445cc47f90c2730f3b0728e5bf667330274d103d --- .../DataTableDefaultPowerSearchOperators.tsx | 31 +++++++++++++++++++ .../data-table/DataTableWithPowerSearch.tsx | 5 +++ 2 files changed, 36 insertions(+) diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx index 6b8d8d250..6a58391fc 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx @@ -21,6 +21,11 @@ export type PowerSearchOperatorProcessor = ( ) => boolean; export const dataTablePowerSearchOperators = { + string_matches_regex: () => ({ + label: 'matches regex', + key: 'string_matches_regex', + valueType: 'STRING', + }), string_contains: () => ({ label: 'contains', key: 'string_contains', @@ -46,6 +51,11 @@ export const dataTablePowerSearchOperators = { key: 'searializable_object_contains', valueType: 'STRING', }), + searializable_object_matches_regex: () => ({ + label: 'matches regex', + key: 'searializable_object_matches_regex', + valueType: 'STRING', + }), searializable_object_not_contains: () => ({ label: 'does not contain', key: 'searializable_object_not_contains', @@ -217,7 +227,23 @@ const tryConvertingUnknownToString = (value: unknown): string | null => { } }; +const regexCache: Record = {}; +function safeCreateRegExp(source: string): RegExp | undefined { + try { + if (!regexCache[source]) { + regexCache[source] = new RegExp(source); + } + return regexCache[source]; + } catch (_e) { + return undefined; + } +} + export const dataTablePowerSearchOperatorProcessorConfig = { + string_matches_regex: (_operator, searchValue: string, value: string) => + !!safeCreateRegExp(searchValue)?.test( + tryConvertingUnknownToString(value) ?? '', + ), string_contains: (_operator, searchValue: string, value: string) => !!tryConvertingUnknownToString(value) ?.toLowerCase() @@ -226,6 +252,11 @@ export const dataTablePowerSearchOperatorProcessorConfig = { !tryConvertingUnknownToString(value) ?.toLowerCase() .includes(searchValue.toLowerCase()), + searializable_object_matches_regex: ( + _operator, + searchValue: string, + value: object, + ) => !!safeCreateRegExp(searchValue)?.test(JSON.stringify(value)), searializable_object_contains: ( _operator, searchValue: string, diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx index e2a38c57e..7dd62e905 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -119,6 +119,8 @@ const powerSearchConfigEntireRow: FieldConfig = { dataTablePowerSearchOperators.searializable_object_contains(), searializable_object_not_contains: dataTablePowerSearchOperators.searializable_object_not_contains(), + searializable_object_matches_regex: + dataTablePowerSearchOperators.searializable_object_matches_regex(), }, useWholeRow: true, }; @@ -388,6 +390,7 @@ export function DataTable( dataTablePowerSearchOperators.string_not_matches_exactly(), dataTablePowerSearchOperators.string_set_contains_any_of(), dataTablePowerSearchOperators.string_set_contains_none_of(), + dataTablePowerSearchOperators.string_matches_regex(), ]; } else if (Array.isArray(column.powerSearchConfig)) { columnPowerSearchOperators = column.powerSearchConfig; @@ -433,6 +436,7 @@ export function DataTable( dataTablePowerSearchOperators.string_not_matches_exactly(), dataTablePowerSearchOperators.string_set_contains_any_of(), dataTablePowerSearchOperators.string_set_contains_none_of(), + dataTablePowerSearchOperators.string_matches_regex(), ]; break; } @@ -483,6 +487,7 @@ export function DataTable( columnPowerSearchOperators = [ dataTablePowerSearchOperators.searializable_object_contains(), dataTablePowerSearchOperators.searializable_object_not_contains(), + dataTablePowerSearchOperators.searializable_object_matches_regex(), ]; break; } From 91efcce5c5340a2b7167f5b599776ee4ebd590e6 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 Date: Fri, 10 Nov 2023 06:50:40 -0800 Subject: [PATCH 031/112] Flipper Release: v0.237.0 Summary: Releasing version 0.237.0 Reviewed By: aigoncharov Differential Revision: D51199543 fbshipit-source-id: 4579641a9c113cd14127db69a180e76358b68de9 --- desktop/package.json | 2 +- desktop/plugins/public/layout/docs/setup.mdx | 2 +- desktop/plugins/public/leak_canary/docs/setup.mdx | 2 +- desktop/plugins/public/network/docs/setup.mdx | 2 +- desktop/static/CHANGELOG.md | 5 +++++ docs/getting-started/android-native.mdx | 4 ++-- docs/getting-started/react-native-ios.mdx | 2 +- docs/getting-started/react-native.mdx | 4 ++-- gradle.properties | 2 +- js/js-flipper/package.json | 2 +- react-native/react-native-flipper/package.json | 2 +- 11 files changed, 17 insertions(+), 12 deletions(-) diff --git a/desktop/package.json b/desktop/package.json index d38a3eb76..7713eb64e 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -170,7 +170,7 @@ "npm": "use yarn instead", "yarn": "^1.16" }, - "version": "0.236.0", + "version": "0.237.0", "workspaces": { "packages": [ "scripts", diff --git a/desktop/plugins/public/layout/docs/setup.mdx b/desktop/plugins/public/layout/docs/setup.mdx index 6e412487f..fe6761c39 100644 --- a/desktop/plugins/public/layout/docs/setup.mdx +++ b/desktop/plugins/public/layout/docs/setup.mdx @@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.236.0' + debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.237.0' debugImplementation 'com.facebook.litho:litho-annotations:0.19.0' // ... } diff --git a/desktop/plugins/public/leak_canary/docs/setup.mdx b/desktop/plugins/public/leak_canary/docs/setup.mdx index de5fa68a8..68fe9457f 100644 --- a/desktop/plugins/public/leak_canary/docs/setup.mdx +++ b/desktop/plugins/public/leak_canary/docs/setup.mdx @@ -8,7 +8,7 @@ To setup the LeakCan ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.236.0' + debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.237.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' } ``` diff --git a/desktop/plugins/public/network/docs/setup.mdx b/desktop/plugins/public/network/docs/setup.mdx index f48aa99c8..95545b026 100644 --- a/desktop/plugins/public/network/docs/setup.mdx +++ b/desktop/plugins/public/network/docs/setup.mdx @@ -12,7 +12,7 @@ The network plugin is shipped as a separate Maven artifact, as follows: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.236.0' + debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.237.0' } ``` diff --git a/desktop/static/CHANGELOG.md b/desktop/static/CHANGELOG.md index 93ffb478a..3fe09459d 100644 --- a/desktop/static/CHANGELOG.md +++ b/desktop/static/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.237.0 (10/11/2023) + + * [D51113095](https://github.com/facebook/flipper/search?q=D51113095&type=Commits) - UIdebugger added powersearch operators to Framework event table + + # 0.234.0 (1/11/2023) * [D50595987](https://github.com/facebook/flipper/search?q=D50595987&type=Commits) - UIDebugger, new sidebar design diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 8d5c5bc09..09f80d8ee 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -24,10 +24,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.236.0' + debugImplementation 'com.facebook.flipper:flipper:0.237.0' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.236.0' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.237.0' } ``` diff --git a/docs/getting-started/react-native-ios.mdx b/docs/getting-started/react-native-ios.mdx index 55ae4a470..993cecb31 100644 --- a/docs/getting-started/react-native-ios.mdx +++ b/docs/getting-started/react-native-ios.mdx @@ -51,7 +51,7 @@ Add all of the code below to your `ios/Podfile`: platform :ios, '9.0' def flipper_pods() - flipperkit_version = '0.236.0' # should match the version of your Flipper client app + flipperkit_version = '0.237.0' # should match the version of your Flipper client app pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug' diff --git a/docs/getting-started/react-native.mdx b/docs/getting-started/react-native.mdx index 3ae9808fb..cccf15c2b 100644 --- a/docs/getting-started/react-native.mdx +++ b/docs/getting-started/react-native.mdx @@ -34,7 +34,7 @@ Latest version of Flipper requires react-native 0.69+! If you use react-native < Android: -1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.236.0`. +1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.237.0`. 2. Run `./gradlew clean` in the `android` directory. iOS: @@ -44,7 +44,7 @@ react-native version => 0.69.0 2. Run `pod install --repo-update` in the `ios` directory. react-native version < 0.69.0 -1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.236.0' })`. +1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.237.0' })`. 2. Run `pod install --repo-update` in the `ios` directory. ## Manual Setup diff --git a/gradle.properties b/gradle.properties index 534de3e27..04ffcfb95 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.236.1-SNAPSHOT +VERSION_NAME=0.237.0 GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper diff --git a/js/js-flipper/package.json b/js/js-flipper/package.json index fb8effe64..6c3c5ac8b 100644 --- a/js/js-flipper/package.json +++ b/js/js-flipper/package.json @@ -1,7 +1,7 @@ { "name": "js-flipper", "title": "JS Flipper Bindings for Web-Socket based clients", - "version": "0.236.0", + "version": "0.237.0", "main": "lib/index.js", "browser": { "os": false diff --git a/react-native/react-native-flipper/package.json b/react-native/react-native-flipper/package.json index b423a3895..31f9b2584 100644 --- a/react-native/react-native-flipper/package.json +++ b/react-native/react-native-flipper/package.json @@ -1,7 +1,7 @@ { "name": "react-native-flipper", "title": "React Native Flipper Bindings", - "version": "0.236.0", + "version": "0.237.0", "description": "Flipper bindings for React Native", "main": "index.js", "types": "index.d.ts", From b08e6feb4410a9d3f782d2c9efd730bcac8476a6 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 Date: Fri, 10 Nov 2023 06:50:40 -0800 Subject: [PATCH 032/112] Flipper Snapshot Bump: v0.237.1-SNAPSHOT Summary: Releasing snapshot version 0.237.1-SNAPSHOT Reviewed By: aigoncharov Differential Revision: D51199541 fbshipit-source-id: 97420b530bee02d2721af752a13dcd8020ada369 --- docs/getting-started/android-native.mdx | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 09f80d8ee..4506816a8 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -124,10 +124,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.236.1-SNAPSHOT' + debugImplementation 'com.facebook.flipper:flipper:0.237.1-SNAPSHOT' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.236.1-SNAPSHOT' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.237.1-SNAPSHOT' } ``` diff --git a/gradle.properties b/gradle.properties index 04ffcfb95..6e28d734b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.237.0 +VERSION_NAME=0.237.1-SNAPSHOT GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper From e5f6ad0ca617a2f092eb072dad6f78882b78ac6d Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Fri, 10 Nov 2023 08:03:34 -0800 Subject: [PATCH 033/112] default adb path Summary: As I was setting up a new mac I had to change this setting in flipper settings. From a few android people that I've spoken to no one could answer me if the old one is still used. Ideally we should infer this during the first time flipper starts - `/opt/android_sdk` current default - `$HOME/Library/Android/sdk` adb from Android Studio - `$HOME/fbsource/third-party/toolchains/android-sdk` adb that is always available in fbsource Reviewed By: lblasa Differential Revision: D51120929 fbshipit-source-id: edb2a58b9c9f37465ea2fc5493975dd427d5523b --- .../src/utils/settings.tsx | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/desktop/flipper-server-core/src/utils/settings.tsx b/desktop/flipper-server-core/src/utils/settings.tsx index 996a5fe67..47a808dd6 100644 --- a/desktop/flipper-server-core/src/utils/settings.tsx +++ b/desktop/flipper-server-core/src/utils/settings.tsx @@ -8,6 +8,7 @@ */ import os from 'os'; +import fs from 'fs-extra'; import {resolve} from 'path'; import {Settings, Tristate} from 'flipper-common'; import {readFile, writeFile, pathExists, mkdirp} from 'fs-extra'; @@ -18,7 +19,7 @@ export async function loadSettings( ): Promise { if (settingsString !== '') { try { - return replaceDefaultSettings(JSON.parse(settingsString)); + return await replaceDefaultSettings(JSON.parse(settingsString)); } catch (e) { throw new Error("couldn't read the user settingsString"); } @@ -48,9 +49,9 @@ function getSettingsFile() { export const DEFAULT_ANDROID_SDK_PATH = getDefaultAndroidSdkPath(); -function getDefaultSettings(): Settings { +async function getDefaultSettings(): Promise { return { - androidHome: getDefaultAndroidSdkPath(), + androidHome: await getDefaultAndroidSdkPath(), enableAndroid: true, enableIOS: os.platform() === 'darwin', enablePhysicalIOS: os.platform() === 'darwin', @@ -69,14 +70,24 @@ function getDefaultSettings(): Settings { }; } -function getDefaultAndroidSdkPath() { - return os.platform() === 'win32' ? getWindowsSdkPath() : '/opt/android_sdk'; +async function getDefaultAndroidSdkPath() { + if (os.platform() === 'win32') { + return `${os.homedir()}\\AppData\\Local\\android\\sdk`; + } + + // non windows platforms + + // created when created a project in Android Studio + const androidStudioSdkPath = `${os.homedir()}/Library/Android/sdk`; + if (await fs.exists(androidStudioSdkPath)) { + return androidStudioSdkPath; + } + + return '/opt/android_sdk'; } -function getWindowsSdkPath() { - return `${os.homedir()}\\AppData\\Local\\android\\sdk`; -} - -function replaceDefaultSettings(userSettings: Partial): Settings { - return {...getDefaultSettings(), ...userSettings}; +async function replaceDefaultSettings( + userSettings: Partial, +): Promise { + return {...(await getDefaultSettings()), ...userSettings}; } From 2d253b13877c48efa082e3fd1e8984643257eaa0 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Fri, 10 Nov 2023 08:03:34 -0800 Subject: [PATCH 034/112] fix displayed error in virtual device picker Summary: Error thrown from server is serialiased that is a string, not instance of Error Reviewed By: LukeDefeo Differential Revision: D51201870 fbshipit-source-id: 833818789a50a21d3d27e0388635e0e2b7470c9d --- .../src/sandy-chrome/appinspect/LaunchEmulator.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx index 894c23f4d..2bf708c89 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx @@ -126,7 +126,7 @@ export const LaunchEmulatorDialog = withTrackingScope( setIosEmulators(simulators); } catch (error) { console.warn('Failed to find iOS simulators', error); - setiOSMessage(`Error: ${error.message} \nRetrying...`); + setiOSMessage(`Error: ${error.message ?? error} \nRetrying...`); setTimeout(getiOSSimulators, 1000); } }; @@ -148,7 +148,7 @@ export const LaunchEmulatorDialog = withTrackingScope( setAndroidEmulators(emulators); } catch (error) { console.warn('Failed to find Android emulators', error); - setAndroidMessage(`Error: ${error.message} \nRetrying...`); + setAndroidMessage(`Error: ${error.message ?? error} \nRetrying...`); setTimeout(getAndroidEmulators, 1000); } }; From 2d28ca2c378c36d76f0c98b944315306dac24518 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Fri, 10 Nov 2023 09:08:28 -0800 Subject: [PATCH 035/112] Support enum label inferral for simplified config Reviewed By: LukeDefeo Differential Revision: D51199436 fbshipit-source-id: c611f0d5a21d7500447342d3e2a5e12c55e9b67d --- .../data-table/DataTableWithPowerSearch.tsx | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx index 7dd62e905..d7199e5ea 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -146,7 +146,8 @@ type DataTableInput = }; type PowerSearchSimplifiedConfig = - | {type: 'enum'; enumLabels: EnumLabels} + | {type: 'enum'; enumLabels: EnumLabels; inferEnumOptionsFromData?: false} + | {type: 'enum'; enumLabels?: never; inferEnumOptionsFromData: true} | {type: 'int'} | {type: 'float'} | {type: 'string'} @@ -175,6 +176,17 @@ const powerSearchConfigIsExtendedConfig = ( !!powerSearchConfig && Array.isArray((powerSearchConfig as PowerSearchExtendedConfig).operators); +const powerSearchConfigIsSimplifiedConfig = ( + powerSearchConfig: + | undefined + | PowerSearchSimplifiedConfig + | OperatorConfig[] + | false + | PowerSearchExtendedConfig, +): powerSearchConfig is PowerSearchSimplifiedConfig => + !!powerSearchConfig && + typeof (powerSearchConfig as PowerSearchSimplifiedConfig).type === 'string'; + export type DataTableColumn = { //this can be a dotted path into a nest objects. e.g foo.bar key: keyof T & string; @@ -311,7 +323,9 @@ export function DataTable( for (const column of columns) { if ( - powerSearchConfigIsExtendedConfig(column.powerSearchConfig) && + (powerSearchConfigIsExtendedConfig(column.powerSearchConfig) || + (powerSearchConfigIsSimplifiedConfig(column.powerSearchConfig) && + column.powerSearchConfig.type === 'enum')) && column.powerSearchConfig.inferEnumOptionsFromData ) { if (!secondaryIndeciesKeys.has(column.key)) { @@ -461,24 +475,22 @@ export function DataTable( break; } case 'enum': { + let enumLabels: EnumLabels; + + if (column.powerSearchConfig.inferEnumOptionsFromData) { + enumLabels = inferredPowerSearchEnumLabels[column.key] ?? {}; + } else { + enumLabels = column.powerSearchConfig.enumLabels; + } + columnPowerSearchOperators = [ - dataTablePowerSearchOperators.enum_is( - column.powerSearchConfig.enumLabels, - ), - dataTablePowerSearchOperators.enum_is_not( - column.powerSearchConfig.enumLabels, - ), - dataTablePowerSearchOperators.enum_is_nullish_or( - column.powerSearchConfig.enumLabels, - ), - dataTablePowerSearchOperators.enum_set_is_any_of( - column.powerSearchConfig.enumLabels, - ), - dataTablePowerSearchOperators.enum_set_is_none_of( - column.powerSearchConfig.enumLabels, - ), + dataTablePowerSearchOperators.enum_is(enumLabels), + dataTablePowerSearchOperators.enum_is_not(enumLabels), + dataTablePowerSearchOperators.enum_is_nullish_or(enumLabels), + dataTablePowerSearchOperators.enum_set_is_any_of(enumLabels), + dataTablePowerSearchOperators.enum_set_is_none_of(enumLabels), dataTablePowerSearchOperators.enum_set_is_nullish_or_any_of( - column.powerSearchConfig.enumLabels, + enumLabels, ), ]; break; From cd9db40e4f7eda5c4958c068bd1d411a58ccff0f Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Fri, 10 Nov 2023 09:08:28 -0800 Subject: [PATCH 036/112] Support occasionally stringified null and undefined in enums Reviewed By: LukeDefeo Differential Revision: D51202685 fbshipit-source-id: 74d0a3d7ed956f3fafc393f180b30cd2fcc55384 --- .../DataTableDefaultPowerSearchOperators.tsx | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx index 6a58391fc..c37c24ce7 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx @@ -239,6 +239,23 @@ function safeCreateRegExp(source: string): RegExp | undefined { } } +const enumPredicateForWhenValueCouldBeAStringifiedNullish = ( + // searchValue is typed as a string here, but originally it could have been an undefined or a null and we stringified them during inference (search for `inferEnumOptionsFromData`) + searchValue: string, + value: string | null | undefined, +): boolean => { + if (searchValue === value) { + return true; + } + if (value === null && searchValue === 'null') { + return true; + } + if (value === undefined && searchValue === 'undefined') { + return true; + } + return false; +}; + export const dataTablePowerSearchOperatorProcessorConfig = { string_matches_regex: (_operator, searchValue: string, value: string) => !!safeCreateRegExp(searchValue)?.test( @@ -315,20 +332,29 @@ export const dataTablePowerSearchOperatorProcessorConfig = { float_less_or_equal: (_operator, searchValue: number, value: number) => value <= searchValue, enum_is: (_operator, searchValue: string, value: string) => - searchValue === value, + enumPredicateForWhenValueCouldBeAStringifiedNullish(searchValue, value), enum_is_nullish_or: (_operator, searchValue: string, value?: string | null) => - value == null || searchValue === value, + value == null || + enumPredicateForWhenValueCouldBeAStringifiedNullish(searchValue, value), enum_is_not: (_operator, searchValue: string, value: string) => - searchValue !== value, + !enumPredicateForWhenValueCouldBeAStringifiedNullish(searchValue, value), enum_set_is_nullish_or_any_of: ( _operator, searchValue: string[], value?: string | null, - ) => value == null || searchValue.some((item) => value === item), + ) => + value == null || + searchValue.some((item) => + enumPredicateForWhenValueCouldBeAStringifiedNullish(item, value), + ), enum_set_is_any_of: (_operator, searchValue: string[], value: string) => - searchValue.some((item) => value === item), + searchValue.some((item) => + enumPredicateForWhenValueCouldBeAStringifiedNullish(item, value), + ), enum_set_is_none_of: (_operator, searchValue: string[], value: string) => - !searchValue.some((item) => value === item), + !searchValue.some((item) => + enumPredicateForWhenValueCouldBeAStringifiedNullish(item, value), + ), is_nullish: (_operator, _searchValue, value) => value == null, // See PowerSearchAbsoluteDateTerm newer_than_absolute_date: (_operator, searchValue: Date, value: any) => { From 9ca6f01c40e2698436edbd1c7550a05a8a0879c9 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Fri, 10 Nov 2023 09:08:28 -0800 Subject: [PATCH 037/112] Use simplified enum config for network plugin Reviewed By: LukeDefeo Differential Revision: D51203032 fbshipit-source-id: fc2a9a1dcd55ca05381037743622b5aa9b45f24f --- desktop/plugins/public/network/index.tsx | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/desktop/plugins/public/network/index.tsx b/desktop/plugins/public/network/index.tsx index 833355774..023647e3a 100644 --- a/desktop/plugins/public/network/index.tsx +++ b/desktop/plugins/public/network/index.tsx @@ -698,12 +698,7 @@ const baseColumns: DataTableColumn[] = [ title: 'Method', width: 70, powerSearchConfig: { - operators: [ - dataTablePowerSearchOperators.enum_is({}), - dataTablePowerSearchOperators.enum_is_not({}), - dataTablePowerSearchOperators.enum_set_is_any_of({}), - dataTablePowerSearchOperators.enum_set_is_none_of({}), - ], + type: 'enum', inferEnumOptionsFromData: true, }, }, @@ -713,12 +708,7 @@ const baseColumns: DataTableColumn[] = [ width: 70, align: 'right', powerSearchConfig: { - operators: [ - dataTablePowerSearchOperators.enum_is({}), - dataTablePowerSearchOperators.enum_is_not({}), - dataTablePowerSearchOperators.enum_set_is_any_of({}), - dataTablePowerSearchOperators.enum_set_is_none_of({}), - ], + type: 'enum', inferEnumOptionsFromData: true, }, }, From 5269800738d925bfcc22b59db15e9203bf4d4205 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Fri, 10 Nov 2023 09:34:52 -0800 Subject: [PATCH 038/112] remove metro button Reviewed By: lblasa Differential Revision: D51199560 fbshipit-source-id: ebd844a57b0b30859b186361136cc8a2f897d9f2 --- .../src/chrome/MetroButton.tsx | 94 ------------------- .../sandy-chrome/appinspect/AppInspect.tsx | 6 -- 2 files changed, 100 deletions(-) delete mode 100644 desktop/flipper-ui-core/src/chrome/MetroButton.tsx diff --git a/desktop/flipper-ui-core/src/chrome/MetroButton.tsx b/desktop/flipper-ui-core/src/chrome/MetroButton.tsx deleted file mode 100644 index 54652960a..000000000 --- a/desktop/flipper-ui-core/src/chrome/MetroButton.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import React, {useCallback, useEffect, useState} from 'react'; -import {MetroReportableEvent} from 'flipper-common'; -import {useStore} from '../utils/useStore'; -import {Button as AntButton} from 'antd'; -import {MenuOutlined, ReloadOutlined} from '@ant-design/icons'; -import {theme} from 'flipper-plugin'; -import {BaseDevice} from 'flipper-frontend-core'; - -export default function MetroButton() { - const device = useStore((state) => - state.connections.devices.find( - (device) => device.os === 'Metro' && device.connected.get(), - ), - ) as BaseDevice | undefined; - - const sendCommand = useCallback( - (command: string) => { - device?.sendMetroCommand(command); - }, - [device], - ); - const [progress, setProgress] = useState(1); - const [_hasBuildError, setHasBuildError] = useState(false); - - useEffect(() => { - if (!device) { - return; - } - function metroEventListener(event: MetroReportableEvent) { - if (event.type === 'bundle_build_started') { - setHasBuildError(false); - setProgress(0); - } else if (event.type === 'bundle_build_failed') { - setHasBuildError(true); - setProgress(1); - } else if (event.type === 'bundle_build_done') { - setHasBuildError(false); - setProgress(1); - } else if (event.type === 'bundle_transform_progressed') { - setProgress(event.transformedFileCount / event.totalFileCount); - } - } - - const handle = device.addLogListener((l) => { - if (l.tag !== 'client_log') { - try { - metroEventListener(JSON.parse(l.message)); - } catch (e) { - console.warn('Failed to parse metro message: ', l, e); - } - } - }); - - return () => { - device.removeLogListener(handle); - }; - }, [device]); - - if (!device) { - return null; - } - - return ( - <> - } - title="Reload React Native App" - type="ghost" - onClick={() => { - sendCommand('reload'); - }} - loading={progress < 1} - style={{color: _hasBuildError ? theme.errorColor : undefined}} - /> - } - title="Open the React Native Dev Menu on the device" - type="ghost" - onClick={() => { - sendCommand('devMenu'); - }} - /> - - ); -} diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx index 72dcbb6dd..845d4dac9 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx @@ -13,7 +13,6 @@ import {LeftSidebar, SidebarTitle} from '../LeftSidebar'; import {Layout, styled} from '../../ui'; import {theme, useValue} from 'flipper-plugin'; import {PluginList} from './PluginList'; -import MetroButton from '../../chrome/MetroButton'; import {BookmarkSection} from './BookmarkSection'; import Client from '../../Client'; import {BaseDevice} from 'flipper-frontend-core'; @@ -47,11 +46,6 @@ export function AppInspect() { {isDeviceConnected && isAppConnected && } - {isDeviceConnected && activeDevice && ( - - - - )} From b7a4741e4090f9608247ccc7e94273506eff29b8 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Fri, 10 Nov 2023 09:34:52 -0800 Subject: [PATCH 039/112] remove all mentions of metro from flipper-ui-core Summary: All functionality was about selecting plugins Reviewed By: lblasa Differential Revision: D51200129 fbshipit-source-id: edc33c2b989eabec2ca4a7e285f92c50950977ed --- .../reducers/__tests__/connections.node.tsx | 71 +---- .../sandy-chrome/appinspect/AppInspect.tsx | 13 +- .../sandy-chrome/appinspect/PluginList.tsx | 45 +--- .../appinspect/__tests__/PluginList.spec.tsx | 244 +----------------- .../src/selectors/connections.tsx | 15 +- .../flipper-ui-core/src/utils/pluginUtils.tsx | 31 +-- 6 files changed, 13 insertions(+), 406 deletions(-) diff --git a/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx b/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx index 02ff3095a..e4339f836 100644 --- a/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx +++ b/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx @@ -56,39 +56,6 @@ test('doing a double REGISTER_DEVICE fails', () => { }).toThrow('still connected'); }); -test('register, remove, re-register a metro device works correctly', () => { - const device1 = new TestDevice( - 'http://localhost:8081', - 'emulator', - 'React Native', - 'Metro', - ); - let state: State = reducer(undefined, { - type: 'REGISTER_DEVICE', - payload: device1, - }); - expect(state.devices.length).toBe(1); - expect(state.devices[0].displayTitle()).toBe('React Native'); - - device1.disconnect(); - - expect(state.devices.length).toBe(1); - expect(state.devices[0].displayTitle()).toBe('React Native (Offline)'); - - state = reducer(state, { - type: 'REGISTER_DEVICE', - payload: new TestDevice( - 'http://localhost:8081', - 'emulator', - 'React Native', - 'Metro', - ), - }); - expect(state.devices.length).toBe(1); - expect(state.devices[0].displayTitle()).toBe('React Native'); - expect(state.devices[0]).not.toBe(device1); -}); - test('selectPlugin sets deepLinkPayload correctly', () => { const device1 = new TestDevice( 'http://localhost:8081', @@ -233,10 +200,8 @@ describe('selection changes', () => { let device1: BaseDevice; let device2: BaseDevice; - let metroDevice: BaseDevice; let d1app1: Client; let d1app2: Client; - let d2app1: Client; let d2app2: Client; let store: Store; let mockFlipper: MockFlipperResult; @@ -249,13 +214,8 @@ describe('selection changes', () => { device1 = mockFlipper.device; device2 = mockFlipper.createDevice({}); - metroDevice = mockFlipper.createDevice({ - os: 'Metro', - serial: 'http://localhost:8081', - }); d1app1 = mockFlipper.client; d1app2 = await mockFlipper.createClient(device1, 'd1app2'); - d2app1 = await mockFlipper.createClient(device2, 'd2app1'); d2app2 = await mockFlipper.createClient(device2, 'd2app2'); store = mockFlipper.store; }); @@ -319,31 +279,6 @@ describe('selection changes', () => { expect(getActiveDevice(store.getState())).toBe(device1); }); - test('select a metro device', async () => { - store.dispatch( - selectPlugin({ - selectedPlugin: DevicePlugin1.id, - selectedDevice: metroDevice, - selectedAppId: d2app1.id, // this app will determine the active device - }), - ); - - const state = store.getState(); - expect(state.connections).toMatchObject({ - selectedDevice: metroDevice, - selectedPlugin: DevicePlugin1.id, - selectedAppId: d2app1.id, - userPreferredDevice: metroDevice.title, - // other prefs not updated - userPreferredPlugin: DevicePlugin1.id, - userPreferredApp: d2app1.query.app, - }); - - // used by plugin list, to keep main device / app selection correct - expect(getActiveClient(state)).toBe(d2app1); - expect(getActiveDevice(state)).toBe(device2); - }); - test('introducing new client does not select it', async () => { await mockFlipper.createClient(device2, 'd2app3'); expect(store.getState().connections).toMatchObject({ @@ -401,12 +336,12 @@ describe('selection changes', () => { }); test('select device', () => { - store.dispatch(selectDevice(metroDevice)); + store.dispatch(selectDevice(device1)); expect(store.getState().connections).toMatchObject({ - selectedDevice: metroDevice, + selectedDevice: device1, selectedPlugin: TestPlugin1.id, selectedAppId: null, - userPreferredDevice: metroDevice.title, + userPreferredDevice: device1.title, // other prefs not updated userPreferredPlugin: TestPlugin1.id, userPreferredApp: d1app1.query.app, diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx index 845d4dac9..b54caee10 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx @@ -18,18 +18,13 @@ import Client from '../../Client'; import {BaseDevice} from 'flipper-frontend-core'; import {ExclamationCircleOutlined, FieldTimeOutlined} from '@ant-design/icons'; import {useSelector} from 'react-redux'; -import { - getActiveClient, - getActiveDevice, - getMetroDevice, -} from '../../selectors/connections'; +import {getActiveClient, getActiveDevice} from '../../selectors/connections'; import * as connections from '../../selectors/connections'; import {PluginActionsMenu} from '../../chrome/PluginActionsMenu'; const {Text} = Typography; export function AppInspect() { - const metroDevice = useSelector(getMetroDevice); const client = useSelector(getActiveClient); const activeDevice = useSelector(getActiveDevice); const isDeviceConnected = useValue(activeDevice?.connected, false); @@ -50,11 +45,7 @@ export function AppInspect() { {activeDevice ? ( - + ) : null} diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/PluginList.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/PluginList.tsx index 7f2b2bd20..0d74fcee1 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/PluginList.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/PluginList.tsx @@ -42,11 +42,9 @@ const {Text} = Typography; export const PluginList = memo(function PluginList({ client, activeDevice, - metroDevice, }: { client: Client | null; activeDevice: BaseDevice | null; - metroDevice: BaseDevice | null; }) { const dispatch = useDispatch(); const connections = useStore((state) => state.connections); @@ -54,11 +52,9 @@ export const PluginList = memo(function PluginList({ const pluginLists = useSelector(getPluginLists); const downloads = useStore((state) => state.pluginDownloads); const isConnected = useValue(activeDevice?.connected, false); - const metroConnected = useValue(metroDevice?.connected, false); const { devicePlugins, - metroPlugins, enabledPlugins, disabledPlugins, unavailablePlugins, @@ -96,19 +92,6 @@ export const PluginList = memo(function PluginList({ }, [dispatch, activeDevice, connections.selectedAppId], ); - const handleMetroPluginClick = useCallback( - (pluginId) => { - dispatch( - selectPlugin({ - selectedPlugin: pluginId, - selectedAppId: connections.selectedAppId, - deepLinkPayload: null, - selectedDevice: metroDevice, - }), - ); - }, - [dispatch, metroDevice, connections.selectedAppId], - ); const handleEnablePlugin = useCallback( (id: string) => { const plugin = (plugins.clientPlugins.get(id) ?? @@ -207,39 +190,15 @@ export const PluginList = memo(function PluginList({ {}} - defaultOpenKeys={['enabled', 'metro']} + defaultOpenKeys={['enabled']} selectedKeys={ - connections.selectedPlugin - ? [ - (connections.selectedDevice === metroDevice ? 'metro:' : '') + - connections.selectedPlugin, - ] - : [] + connections.selectedPlugin ? [connections.selectedPlugin] : [] } mode="inline"> {allEnabledPlugins} - {!isArchived && metroConnected && ( - - {metroPlugins.map((plugin) => ( - - ))} - - )} {isConnected && ( {} @@ -63,221 +39,3 @@ describe('basic getActiveDevice', () => { expect(getActiveDevice(store.getState())).toBe(device); }); }); - -describe('basic getActiveDevice with metro present', () => { - let flipper: MockFlipperResult; - let metro: BaseDevice; - let testDevice: BaseDevice; - - beforeEach(async () => { - flipper = await createMockFlipperWithPlugin(logsPlugin); - flipper.device.supportsPlugin = (p) => { - return p.id !== 'unsupportedDevicePlugin'; - }; - testDevice = flipper.device; - // flipper.store.dispatch(registerPlugins([LogsPlugin])) - flipper.store.dispatch({ - type: 'REGISTER_DEVICE', - payload: new TestDevice( - 'http://localhost:8081', - 'physical', - 'metro', - 'Metro', - ), - }); - metro = getMetroDevice(flipper.store.getState())!; - metro.supportsPlugin = (p) => { - return p.id !== 'unsupportedDevicePlugin'; - }; - }); - - test('findMetroDevice', () => { - expect(metro.os).toBe('Metro'); - }); - - test('correct base selection state', () => { - const state = flipper.store.getState(); - const {connections} = state; - expect(connections).toMatchObject({ - devices: [testDevice, metro], - selectedDevice: testDevice, - selectedPlugin: 'DeviceLogs', - userPreferredDevice: 'MockAndroidDevice', - userPreferredPlugin: 'DeviceLogs', - userPreferredApp: 'TestApp', - }); - expect(getActiveClient(state)).toBe(flipper.client); - }); - - test('selecting Metro Logs works but keeps normal device preferred', () => { - expect(getActiveClient(flipper.store.getState())).toBe(flipper.client); - flipper.store.dispatch( - selectPlugin({ - selectedPlugin: logsPlugin.id, - selectedAppId: flipper.client.id, - selectedDevice: metro, - deepLinkPayload: null, - }), - ); - expect(flipper.store.getState().connections).toMatchObject({ - devices: [testDevice, metro], - selectedAppId: 'TestApp#Android#MockAndroidDevice#serial', - selectedDevice: metro, - selectedPlugin: 'DeviceLogs', - userPreferredDevice: 'MockAndroidDevice', // Not metro! - userPreferredPlugin: 'DeviceLogs', - userPreferredApp: 'TestApp', - }); - const state = flipper.store.getState(); - // find best device is still metro - expect(getActiveDevice(state)).toBe(testDevice); - // find best client still returns app - expect(getActiveClient(state)).toBe(flipper.client); - }); - - test('computePluginLists', () => { - const state = flipper.store.getState(); - expect(getPluginLists(state)).toEqual({ - downloadablePlugins: [], - devicePlugins: [logsPlugin], - metroPlugins: [logsPlugin], - enabledPlugins: [], - disabledPlugins: [], - unavailablePlugins: [], - }); - }); - - test('computePluginLists with problematic plugins', () => { - const noopPlugin = { - plugin() {}, - Component() { - return null; - }, - }; - - const unsupportedDevicePlugin = new _SandyPluginDefinition( - createMockPluginDetails({ - id: 'unsupportedDevicePlugin', - title: 'Unsupported Device Plugin', - }), - { - devicePlugin() { - return {}; - }, - supportsDevice() { - return false; - }, - Component() { - return null; - }, - }, - ); - const unsupportedPlugin = new _SandyPluginDefinition( - createMockPluginDetails({ - id: 'unsupportedPlugin', - title: 'Unsupported Plugin', - }), - noopPlugin, - ); - - const gateKeepedPlugin = createMockPluginDetails({ - id: 'gateKeepedPlugin', - title: 'Gatekeeped Plugin', - gatekeeper: 'not for you', - }); - - const plugin1 = new _SandyPluginDefinition( - createMockPluginDetails({ - id: 'plugin1', - title: 'Plugin 1', - }), - { - plugin() {}, - Component() { - return null; - }, - }, - ); - - const plugin2 = new _SandyPluginDefinition( - createMockPluginDetails({ - id: 'plugin2', - title: 'Plugin 2', - }), - noopPlugin, - ); - - const supportedDownloadablePlugin = createMockDownloadablePluginDetails({ - id: 'supportedUninstalledPlugin', - title: 'Supported Uninstalled Plugin', - }); - - const unsupportedDownloadablePlugin = createMockDownloadablePluginDetails({ - id: 'unsupportedUninstalledPlugin', - title: 'Unsupported Uninstalled Plugin', - }); - - flipper.store.dispatch( - registerPlugins([ - unsupportedDevicePlugin, - unsupportedPlugin, - plugin1, - plugin2, - ]), - ); - flipper.store.dispatch(addGatekeepedPlugins([gateKeepedPlugin])); - flipper.store.dispatch( - registerMarketplacePlugins([ - supportedDownloadablePlugin, - unsupportedDownloadablePlugin, - ]), - ); - - // ok, this is a little hackish - flipper.client.plugins = new Set([ - 'plugin1', - 'plugin2', - 'supportedUninstalledPlugin', - ]); - - let state = flipper.store.getState(); - const pluginLists = getPluginLists(state); - expect(pluginLists).toEqual({ - devicePlugins: [logsPlugin], - metroPlugins: [logsPlugin], - enabledPlugins: [], - disabledPlugins: [plugin1, plugin2], - unavailablePlugins: [ - [ - gateKeepedPlugin, - "Plugin 'Gatekeeped Plugin' is only available to members of gatekeeper 'not for you'", - ], - [ - unsupportedDevicePlugin.details, - "Device plugin 'Unsupported Device Plugin' is not supported by the selected device 'MockAndroidDevice' (Android)", - ], - [ - unsupportedPlugin.details, - "Plugin 'Unsupported Plugin' is not supported by the selected application 'TestApp' (Android)", - ], - [ - unsupportedDownloadablePlugin, - "Plugin 'Unsupported Uninstalled Plugin' is not supported by the selected application 'TestApp' (Android) and not installed in Flipper", - ], - ], - downloadablePlugins: [supportedDownloadablePlugin], - }); - - flipper.store.dispatch( - switchPlugin({ - plugin: plugin2, - selectedApp: flipper.client.query.app, - }), - ); - state = flipper.store.getState(); - expect(getPluginLists(state)).toMatchObject({ - enabledPlugins: [plugin2], - disabledPlugins: [plugin1], - }); - }); -}); diff --git a/desktop/flipper-ui-core/src/selectors/connections.tsx b/desktop/flipper-ui-core/src/selectors/connections.tsx index e5a5a7e8f..99ca68c45 100644 --- a/desktop/flipper-ui-core/src/selectors/connections.tsx +++ b/desktop/flipper-ui-core/src/selectors/connections.tsx @@ -26,13 +26,6 @@ const getPluginDownloads = (state: State) => state.pluginDownloads; export const getActiveClient = (state: State) => state.connections.clients.get(state.connections.selectedAppId!) ?? null; -export const getMetroDevice = createSelector(getDevices, (devices) => { - return ( - devices.find((device) => device.os === 'Metro' && !device.isArchived) ?? - null - ); -}); - export const getSelectableDevices = createSelector( getDevices, getClients, @@ -55,12 +48,7 @@ export const hasSelectableDevices = createSelector( export const getActiveDevice = createSelector( getSelectedDevice, getActiveClient, - getMetroDevice, - (selectedDevice, client, metroDevice) => { - // if not Metro device, use the selected device as metro device - if (selectedDevice !== metroDevice) { - return selectedDevice; - } + (selectedDevice, client) => { // if there is an active app, use device owning the app if (client) { // TODO: Will be fixed later in the stack @@ -102,7 +90,6 @@ export const getPluginLists = createSelector( failedPlugins, }), getActiveDevice, - getMetroDevice, getActiveClient, computePluginLists, ); diff --git a/desktop/flipper-ui-core/src/utils/pluginUtils.tsx b/desktop/flipper-ui-core/src/utils/pluginUtils.tsx index 3f53ff33e..b702e0205 100644 --- a/desktop/flipper-ui-core/src/utils/pluginUtils.tsx +++ b/desktop/flipper-ui-core/src/utils/pluginUtils.tsx @@ -25,7 +25,6 @@ import {getAppVersion} from './info'; export type PluginLists = { devicePlugins: PluginDefinition[]; - metroPlugins: PluginDefinition[]; enabledPlugins: PluginDefinition[]; disabledPlugins: PluginDefinition[]; unavailablePlugins: [plugin: PluginDetails, reason: string][]; @@ -170,11 +169,9 @@ export function computePluginLists( | 'clientPlugins' >, device: BaseDevice | null, - metroDevice: BaseDevice | null, client: Client | null, ): { devicePlugins: PluginDefinition[]; - metroPlugins: PluginDefinition[]; enabledPlugins: PluginDefinition[]; disabledPlugins: PluginDefinition[]; unavailablePlugins: [plugin: PluginDetails, reason: string][]; @@ -189,26 +186,19 @@ export function computePluginLists( const devicePlugins: PluginDefinition[] = [...plugins.devicePlugins.values()] .filter((p) => device?.supportsPlugin(p)) .filter((p) => enabledDevicePluginsState.has(p.id)); - const metroPlugins: PluginDefinition[] = [...plugins.devicePlugins.values()] - .filter((p) => metroDevice?.supportsPlugin(p)) - .filter((p) => enabledDevicePluginsState.has(p.id)); const enabledPlugins: PluginDefinition[] = []; const disabledPlugins: PluginDefinition[] = [ ...plugins.devicePlugins.values(), ] - .filter( - (p) => - device?.supportsPlugin(p.details) || - metroDevice?.supportsPlugin(p.details), - ) + .filter((p) => device?.supportsPlugin(p.details)) .filter((p) => !enabledDevicePluginsState.has(p.id)); const unavailablePlugins: [plugin: PluginDetails, reason: string][] = []; const downloadablePlugins: DownloadablePluginDetails[] = []; if (device) { - // find all device plugins that aren't part of the current device / metro + // find all device plugins that aren't part of the current device for (const p of plugins.devicePlugins.values()) { - if (!device.supportsPlugin(p) && !metroDevice?.supportsPlugin(p)) { + if (!device.supportsPlugin(p)) { unavailablePlugins.push([ p.details, `Device plugin '${getPluginTitle( @@ -222,10 +212,7 @@ export function computePluginLists( for (const plugin of uninstalledMarketplacePlugins.filter( (d) => d.pluginType === 'device', )) { - if ( - device.supportsPlugin(plugin) || - metroDevice?.supportsPlugin(plugin) - ) { + if (device.supportsPlugin(plugin)) { downloadablePlugins.push(plugin); } } @@ -327,7 +314,6 @@ export function computePluginLists( enabledPlugins.sort(sortPluginsByName); devicePlugins.sort(sortPluginsByName); disabledPlugins.sort(sortPluginsByName); - metroPlugins.sort(sortPluginsByName); unavailablePlugins.sort(([a], [b]) => { return getPluginTitle(a) > getPluginTitle(b) ? 1 : -1; }); @@ -337,7 +323,6 @@ export function computePluginLists( return { devicePlugins, - metroPlugins, enabledPlugins, disabledPlugins, unavailablePlugins, @@ -371,7 +356,6 @@ function getFavoritePlugins( export function computeActivePluginList({ enabledPlugins, devicePlugins, - metroPlugins, disabledPlugins, downloadablePlugins, unavailablePlugins, @@ -391,13 +375,6 @@ export function computeActivePluginList({ definition: plugin, }; } - for (const plugin of metroPlugins) { - pluginList[plugin.id] = { - status: 'enabled', - details: plugin.details, - definition: plugin, - }; - } for (const plugin of disabledPlugins) { pluginList[plugin.id] = { status: 'disabled', From 45157c36753714a6b03b69f648927a29cdd67560 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Fri, 10 Nov 2023 09:54:08 -0800 Subject: [PATCH 040/112] Change order of ios checks Summary: Current order is confusion, the new order better reflects order at which users gets started with iOS 1. Install Xcode 2. Point Xcode-select at most recent xcode version 3. Install SDK 4. Accept Xcode SDK license 5. check idb health Reviewed By: lblasa Differential Revision: D51202202 fbshipit-source-id: da39acf7ef325889c2288b3941cef0ca803356cd --- desktop/doctor/src/index.tsx | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index 22fb87da6..41953c166 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -137,26 +137,6 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { isRequired: false, isSkipped: false, healthchecks: [ - { - key: 'ios.sdk', - label: 'SDK Installed', - isRequired: true, - run: async (e: FlipperDoctor.EnvironmentInfo) => { - const hasProblem = - !e.SDKs['iOS SDK'] || - !e.SDKs['iOS SDK'].Platforms || - !e.SDKs['iOS SDK'].Platforms.length; - const message = hasProblem - ? 'iOS SDK is not installed. You can install it using Xcode (https://developer.apple.com/xcode/).' - : `iOS SDK is installed for the following platforms: ${JSON.stringify( - e.SDKs['iOS SDK'].Platforms, - )}.`; - return { - hasProblem, - message, - }; - }, - }, { key: 'ios.xcode', label: 'XCode Installed', @@ -217,6 +197,26 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { }; }, }, + { + key: 'ios.sdk', + label: 'SDK Installed', + isRequired: true, + run: async (e: FlipperDoctor.EnvironmentInfo) => { + const hasProblem = + !e.SDKs['iOS SDK'] || + !e.SDKs['iOS SDK'].Platforms || + !e.SDKs['iOS SDK'].Platforms.length; + const message = hasProblem + ? 'iOS SDK is not installed. You can install it using Xcode (https://developer.apple.com/xcode/).' + : `iOS SDK is installed for the following platforms: ${JSON.stringify( + e.SDKs['iOS SDK'].Platforms, + )}.`; + return { + hasProblem, + message, + }; + }, + }, { key: 'ios.xctrace', label: 'xctrace exists', From 0cbd640e5c1cce8a01bf11af3a3bdbb3e5b229bb Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Fri, 10 Nov 2023 09:54:08 -0800 Subject: [PATCH 041/112] expand on ios doctor suggestions Summary: this should help to address support posts like this one https://fb.workplace.com/groups/flippersupport/posts/1724631861350798 Reviewed By: potomak Differential Revision: D51203549 fbshipit-source-id: 097faeb6c9d506bc1fa2ac4602cba95e99a4e2cb --- .../{idbInstallationInstructions.tsx => ios.tsx} | 6 ++++++ desktop/doctor/src/index.tsx | 10 +++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) rename desktop/doctor/src/fb-stubs/{idbInstallationInstructions.tsx => ios.tsx} (71%) diff --git a/desktop/doctor/src/fb-stubs/idbInstallationInstructions.tsx b/desktop/doctor/src/fb-stubs/ios.tsx similarity index 71% rename from desktop/doctor/src/fb-stubs/idbInstallationInstructions.tsx rename to desktop/doctor/src/fb-stubs/ios.tsx index cb326a3a8..cfa1bdc85 100644 --- a/desktop/doctor/src/fb-stubs/idbInstallationInstructions.tsx +++ b/desktop/doctor/src/fb-stubs/ios.tsx @@ -9,3 +9,9 @@ export const getIdbInstallationInstructions = (idbPath: string) => `IDB is required to use Flipper with iOS devices. It can be installed from https://github.com/facebook/idb and configured in Flipper settings. You can also disable physical iOS device support in settings. Current setting: ${idbPath} isn't a valid IDB installation.`; + +export const installXcode = + 'Install Xcode from the App Store or download it from https://developer.apple.com'; + +export const installSDK = + 'You can install it using Xcode (https://developer.apple.com/xcode/).'; diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index 41953c166..f02f59dcc 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -17,7 +17,11 @@ import * as fs from 'fs'; import * as path from 'path'; import type {FlipperDoctor} from 'flipper-common'; import * as fs_extra from 'fs-extra'; -import {getIdbInstallationInstructions} from './fb-stubs/idbInstallationInstructions'; +import { + getIdbInstallationInstructions, + installXcode, + installSDK, +} from './fb-stubs/ios'; import {validateSelectedXcodeVersion} from './fb-stubs/validateSelectedXcodeVersion'; export function getHealthchecks(): FlipperDoctor.Healthchecks { @@ -144,7 +148,7 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { run: async (e: FlipperDoctor.EnvironmentInfo) => { const hasProblem = e.IDEs == null || e.IDEs.Xcode == null; const message = hasProblem - ? 'Xcode (https://developer.apple.com/xcode/) is not installed.' + ? `Xcode is not installed. ${installXcode}.` : `Xcode version ${e.IDEs.Xcode.version} is installed at "${e.IDEs.Xcode.path}".`; return { hasProblem, @@ -207,7 +211,7 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { !e.SDKs['iOS SDK'].Platforms || !e.SDKs['iOS SDK'].Platforms.length; const message = hasProblem - ? 'iOS SDK is not installed. You can install it using Xcode (https://developer.apple.com/xcode/).' + ? `iOS SDK is not installed. ${installSDK}` : `iOS SDK is installed for the following platforms: ${JSON.stringify( e.SDKs['iOS SDK'].Platforms, )}.`; From a400eb2872db62542fcc448195172ee9eaa84a8a Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Sat, 11 Nov 2023 08:21:12 -0800 Subject: [PATCH 042/112] Finalize log stream before exiting process Reviewed By: antonk52 Differential Revision: D51229230 fbshipit-source-id: 0e7f657a170eb8602ade9abf1db1976c5b51dc3f --- .../src/FlipperServerImpl.tsx | 1 + desktop/flipper-server-core/src/index.tsx | 1 + .../src/server/attachSocketServer.tsx | 3 +- .../src/server/startServer.tsx | 6 ++- .../src/server/utilities.tsx | 8 +++- .../src/utils/processExit.tsx | 44 +++++++++++++++++++ desktop/flipper-server/src/index.tsx | 22 +++++++--- desktop/flipper-server/src/logger.tsx | 15 ++++++- 8 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 desktop/flipper-server-core/src/utils/processExit.tsx diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index 84e32cd5b..dd07f3889 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -583,6 +583,7 @@ export class FlipperServerImpl implements FlipperServer { return uploadRes; }, shutdown: async () => { + // Do not use processExit helper. We want to server immediatelly quit when this call is triggerred process.exit(0); }, 'is-logged-in': async () => { diff --git a/desktop/flipper-server-core/src/index.tsx b/desktop/flipper-server-core/src/index.tsx index df488c048..344ee7099 100644 --- a/desktop/flipper-server-core/src/index.tsx +++ b/desktop/flipper-server-core/src/index.tsx @@ -13,6 +13,7 @@ export * from './tracker'; export {loadLauncherSettings} from './utils/launcherSettings'; export {loadProcessConfig} from './utils/processConfig'; export {getEnvironmentInfo} from './utils/environmentInfo'; +export {processExit, setProcessExitRoutine} from './utils/processExit'; export {getGatekeepers} from './gk'; export {setupPrefetcher} from './fb-stubs/Prefetcher'; export * from './server/attachSocketServer'; diff --git a/desktop/flipper-server-core/src/server/attachSocketServer.tsx b/desktop/flipper-server-core/src/server/attachSocketServer.tsx index f66b0aaa9..7a56d498b 100644 --- a/desktop/flipper-server-core/src/server/attachSocketServer.tsx +++ b/desktop/flipper-server-core/src/server/attachSocketServer.tsx @@ -29,6 +29,7 @@ import {URLSearchParams} from 'url'; import {tracker} from '../tracker'; import {getFlipperServerConfig} from '../FlipperServerConfig'; import {performance} from 'perf_hooks'; +import {processExit} from '../utils/processExit'; const safe = (f: () => void) => { try { @@ -266,7 +267,7 @@ export function attachSocketServer( console.info( '[flipper-server] Shutdown as no clients are currently connected', ); - process.exit(0); + processExit(0); } }, FIVE_HOURS); } diff --git a/desktop/flipper-server-core/src/server/startServer.tsx b/desktop/flipper-server-core/src/server/startServer.tsx index 413508674..daeb1a7ee 100644 --- a/desktop/flipper-server-core/src/server/startServer.tsx +++ b/desktop/flipper-server-core/src/server/startServer.tsx @@ -27,6 +27,7 @@ import {EnvironmentInfo, isProduction} from 'flipper-common'; import {GRAPH_SECRET} from '../fb-stubs/constants'; import {sessionId} from '../sessionId'; import {UIPreference, openUI} from '../utils/openUI'; +import {processExit} from '../utils/processExit'; type Config = { port: number; @@ -123,7 +124,7 @@ export async function startServer( console.error( `[flipper-server] Unable to become ready within ${timeoutSeconds} seconds, exit`, ); - process.exit(1); + processExit(1); } }, timeoutSeconds * 1000); @@ -192,6 +193,7 @@ async function startHTTPServer( res.json({success: true}); // Just exit the process, this will trigger the shutdown hooks. + // Do not use prcoessExit util as we want the serve to shutdown immediately process.exit(0); }); @@ -226,7 +228,7 @@ async function startHTTPServer( `[flipper-server] Unable to listen at port: ${config.port}, is already in use`, ); tracker.track('server-socket-already-in-use', {}); - process.exit(1); + processExit(1); } }); diff --git a/desktop/flipper-server-core/src/server/utilities.tsx b/desktop/flipper-server-core/src/server/utilities.tsx index 46764bbd5..b842a2c08 100644 --- a/desktop/flipper-server-core/src/server/utilities.tsx +++ b/desktop/flipper-server-core/src/server/utilities.tsx @@ -50,7 +50,9 @@ export async function checkServerRunning( port: number, ): Promise { try { - const response = await fetch(`http://localhost:${port}/info`); + const response = await fetch(`http://localhost:${port}/info`, { + timeout: 1000, + }); if (response.status >= 200 && response.status < 300) { const environmentInfo: EnvironmentInfo = await response.json(); return environmentInfo.appVersion; @@ -74,7 +76,9 @@ export async function checkServerRunning( */ export async function shutdownRunningInstance(port: number): Promise { try { - const response = await fetch(`http://localhost:${port}/shutdown`); + const response = await fetch(`http://localhost:${port}/shutdown`, { + timeout: 1000, + }); if (response.status >= 200 && response.status < 300) { const json = await response.json(); console.info( diff --git a/desktop/flipper-server-core/src/utils/processExit.tsx b/desktop/flipper-server-core/src/utils/processExit.tsx new file mode 100644 index 000000000..a002709de --- /dev/null +++ b/desktop/flipper-server-core/src/utils/processExit.tsx @@ -0,0 +1,44 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +const onBeforeExitFns: (() => void | Promise)[] = []; +export const setProcessExitRoutine = ( + onBeforeExit: () => void | Promise, +) => { + onBeforeExitFns.push(onBeforeExit); +}; + +const resIsPromise = (res: void | Promise): res is Promise => + res instanceof Promise; +export const processExit = async (code: number) => { + console.debug('processExit', code); + + // eslint-disable-next-line promise/catch-or-return + await Promise.all( + onBeforeExitFns.map(async (fn) => { + try { + const res = fn(); + if (resIsPromise(res)) { + return res.catch((e) => { + console.error('Process exit routine failed', e); + }); + } + } catch (e) { + console.error('Process exit routine failed', e); + } + }), + ).finally(() => { + process.exit(code); + }); + + setTimeout(() => { + console.error('Process exit routines timed out'); + process.exit(code); + }, 5000); +}; diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index b30f56a7b..5ab044506 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -30,6 +30,7 @@ import { startFlipperServer, startServer, tracker, + processExit, } from 'flipper-server-core'; import {addLogTailer, isProduction, isTest, LoggerFormat} from 'flipper-common'; import exitHook from 'exit-hook'; @@ -265,7 +266,7 @@ async function start() { console.error( '[flipper-server] state changed to error, process will exit.', ); - process.exit(1); + processExit(1); } }); } @@ -335,7 +336,7 @@ process.on('uncaughtException', (error) => { error, ); reportBrowserConnection(false); - process.exit(1); + processExit(1); }); process.on('unhandledRejection', (reason, promise) => { @@ -347,8 +348,15 @@ process.on('unhandledRejection', (reason, promise) => { ); }); -start().catch((e) => { - console.error(chalk.red('Server startup error: '), e); - reportBrowserConnection(false); - process.exit(1); -}); +// Node.js process never waits for all promises to settle and exits as soon as there is not pending timers or open sockets or tasks in teh macroqueue +const runtimeTimeout = setTimeout(() => {}, Number.MAX_SAFE_INTEGER); +// eslint-disable-next-line promise/catch-or-return +start() + .catch((e) => { + console.error(chalk.red('Server startup error: '), e); + reportBrowserConnection(false); + return processExit(1); + }) + .finally(() => { + clearTimeout(runtimeTimeout); + }); diff --git a/desktop/flipper-server/src/logger.tsx b/desktop/flipper-server/src/logger.tsx index 484e964a7..497e7d4c5 100644 --- a/desktop/flipper-server/src/logger.tsx +++ b/desktop/flipper-server/src/logger.tsx @@ -21,7 +21,10 @@ import fsRotator from 'file-stream-rotator'; import {ensureFile} from 'fs-extra'; import {access} from 'fs/promises'; import {constants} from 'fs'; -import {initializeLogger as initLogger} from 'flipper-server-core'; +import { + initializeLogger as initLogger, + setProcessExitRoutine, +} from 'flipper-server-core'; export const loggerOutputFile = 'flipper-server-log.out'; @@ -64,4 +67,14 @@ export async function initializeLogger( logStream?.write(`${name}: \n${stack}\n`); } }); + + const finalizeLogger = async () => { + const logStreamToEnd = logStream; + // Prevent future writes + logStream = undefined; + await new Promise((resolve) => { + logStreamToEnd?.end(resolve); + }); + }; + setProcessExitRoutine(finalizeLogger); } From 4ada8b9322bf9587785ccb06154bdbc79caeaf00 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Mon, 13 Nov 2023 04:43:05 -0800 Subject: [PATCH 043/112] Summary: Make as manything as inferred enum as possible changelog: [Logs] Improve power search config to populate dropdown for level, PID & Tag Reviewed By: aigoncharov Differential Revision: D51199644 fbshipit-source-id: 383b61abca5d91a8e318bbfb1aac7d3852074167 --- .../public/logs/__tests__/logs.node.tsx | 11 ++- desktop/plugins/public/logs/index.tsx | 70 +++++++++++++++---- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/desktop/plugins/public/logs/__tests__/logs.node.tsx b/desktop/plugins/public/logs/__tests__/logs.node.tsx index 96532406c..0588c2533 100644 --- a/desktop/plugins/public/logs/__tests__/logs.node.tsx +++ b/desktop/plugins/public/logs/__tests__/logs.node.tsx @@ -57,6 +57,7 @@ test('it will merge equal rows', () => { "date": 2021-01-28T17:15:12.859Z, "message": "test1", "pid": 0, + "pidStr": "0", "tag": "test", "tid": 1, "type": "error", @@ -67,6 +68,7 @@ test('it will merge equal rows', () => { "date": 2021-01-28T17:15:17.859Z, "message": "test2", "pid": 2, + "pidStr": "2", "tag": "test", "tid": 3, "type": "warn", @@ -77,6 +79,7 @@ test('it will merge equal rows', () => { "date": 2021-01-28T17:15:12.859Z, "message": "test3", "pid": 0, + "pidStr": "0", "tag": "test", "tid": 1, "type": "error", @@ -103,9 +106,12 @@ test('it supports deeplink and select nodes + navigating to bottom', async () => await sleep(1000); - expect(instance.tableManagerRef.current?.getSelectedItems()).toEqual([ + const current = instance.tableManagerRef.current; + console.error('ref', current); + expect(current?.getSelectedItems()).toEqual([ { ...entry2, + pidStr: '2', count: 1, }, ]); @@ -116,6 +122,7 @@ test('it supports deeplink and select nodes + navigating to bottom', async () => expect(instance.tableManagerRef.current?.getSelectedItems()).toEqual([ { ...entry3, + pidStr: '0', count: 1, }, ]); @@ -138,6 +145,7 @@ test('export / import plugin does work', async () => { "date": 2021-01-28T17:15:12.859Z, "message": "test1", "pid": 0, + "pidStr": "0", "tag": "test", "tid": 1, "type": "error", @@ -148,6 +156,7 @@ test('export / import plugin does work', async () => { "date": 2021-01-28T17:15:17.859Z, "message": "test2", "pid": 2, + "pidStr": "2", "tag": "test", "tid": 3, "type": "warn", diff --git a/desktop/plugins/public/logs/index.tsx b/desktop/plugins/public/logs/index.tsx index bd7b62c98..87d124e25 100644 --- a/desktop/plugins/public/logs/index.tsx +++ b/desktop/plugins/public/logs/index.tsx @@ -12,13 +12,16 @@ import { DeviceLogEntry, usePlugin, createDataSource, - DataTableColumn, + dataTablePowerSearchOperators, + _DataTableColumnWithPowerSearch, + _DataTableWithPowerSearch, theme, - DataTableManager, + _DataTableWithPowerSearchManager, createState, useValue, DataFormatter, - DataTable, + EnumLabels, + SearchExpressionTerm, } from 'flipper-plugin'; import { PlayCircleOutlined, @@ -32,28 +35,31 @@ import {baseRowStyle, logTypes} from './logTypes'; export type ExtendedLogEntry = DeviceLogEntry & { count: number; + pidStr: string; //for the purposes of inferring (only supports string type) }; +const logLevelEnumLabels = Object.entries(logTypes).reduce( + (res, [key, {label}]) => { + res[key] = label; + return res; + }, + {} as EnumLabels, +); + function createColumnConfig( _os: 'iOS' | 'Android' | 'Metro', -): DataTableColumn[] { +): _DataTableColumnWithPowerSearch[] { return [ { key: 'type', - title: '', + title: 'Level', width: 30, - filters: Object.entries(logTypes).map(([value, config]) => ({ - label: config.label, - value, - enabled: config.enabled, - })), onRender(entry) { return entry.count > 1 ? ( item.enabled) + .map(([key]) => key), + }, +]; + export function devicePlugin(client: DevicePluginClient) { const rows = createDataSource([], { limit: 200000, persist: 'logs', + indices: [['pid'], ['tag']], //there are for inferring enum types }); const isPaused = createState(true); const tableManagerRef = createRef< - undefined | DataTableManager + undefined | _DataTableWithPowerSearchManager >(); client.onDeepLink((payload: unknown) => { if (typeof payload === 'string') { + tableManagerRef.current?.setSearchExpression(powerSearchInitialState); // timeout as we want to await restoring any previous scroll positin first, then scroll to the setTimeout(() => { let hasMatch = false; @@ -168,11 +207,13 @@ export function devicePlugin(client: DevicePluginClient) { ) { rows.update(lastIndex, { ...previousRow, + pidStr: previousRow.pid.toString(), count: previousRow.count + 1, }); } else { rows.append({ ...entry, + pidStr: entry.pid.toString(), count: 1, }); } @@ -225,7 +266,7 @@ export function Component() { const plugin = usePlugin(devicePlugin); const paused = useValue(plugin.isPaused); return ( - + <_DataTableWithPowerSearch dataSource={plugin.rows} columns={plugin.columns} enableAutoScroll @@ -248,6 +289,7 @@ export function Component() { ) : undefined } tableManagerRef={plugin.tableManagerRef} + powerSearchInitialState={powerSearchInitialState} /> ); } From 437e67cd7f9e6ff3d092ecf9316770015cae9bfd Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Mon, 13 Nov 2023 06:02:32 -0800 Subject: [PATCH 044/112] Remove uneeded operators from simplified config Summary: The single value == / contains are covered by the setlike oper /ators. Less operators is better as less overwhelming. Plus with the group operators you can add futher options or'ed togther which is a complain from the user. Reviewed By: aigoncharov Differential Revision: D51255253 fbshipit-source-id: 72f909319fd3d8034ebe4725a5a5254ecfeb074b --- .../src/ui/data-table/DataTableWithPowerSearch.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx index d7199e5ea..bcb55359e 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -444,8 +444,6 @@ export function DataTable( } case 'string': { columnPowerSearchOperators = [ - dataTablePowerSearchOperators.string_contains(), - dataTablePowerSearchOperators.string_not_contains(), dataTablePowerSearchOperators.string_matches_exactly(), dataTablePowerSearchOperators.string_not_matches_exactly(), dataTablePowerSearchOperators.string_set_contains_any_of(), @@ -484,9 +482,6 @@ export function DataTable( } columnPowerSearchOperators = [ - dataTablePowerSearchOperators.enum_is(enumLabels), - dataTablePowerSearchOperators.enum_is_not(enumLabels), - dataTablePowerSearchOperators.enum_is_nullish_or(enumLabels), dataTablePowerSearchOperators.enum_set_is_any_of(enumLabels), dataTablePowerSearchOperators.enum_set_is_none_of(enumLabels), dataTablePowerSearchOperators.enum_set_is_nullish_or_any_of( From 9dea899701d062ba6ad9d9c74c92480eea248ace Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Mon, 13 Nov 2023 06:02:32 -0800 Subject: [PATCH 045/112] use new api for enum Summary: Also fixed time column Reviewed By: aigoncharov Differential Revision: D51255567 fbshipit-source-id: 70a708a0e502eeaf1e51b8ace41dc588b9c0543d --- desktop/plugins/public/logs/index.tsx | 28 +++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/desktop/plugins/public/logs/index.tsx b/desktop/plugins/public/logs/index.tsx index 87d124e25..e865a884e 100644 --- a/desktop/plugins/public/logs/index.tsx +++ b/desktop/plugins/public/logs/index.tsx @@ -71,23 +71,27 @@ function createColumnConfig( ); }, powerSearchConfig: { - operators: [ - dataTablePowerSearchOperators.enum_set_is_any_of(logLevelEnumLabels), - dataTablePowerSearchOperators.enum_set_is_none_of(logLevelEnumLabels), - ], + type: 'enum', + inferEnumOptionsFromData: true, }, }, { key: 'date', title: 'Time', width: 120, + powerSearchConfig: { + type: 'dateTime', + }, }, { key: 'pidStr', title: 'PID', width: 60, visible: true, - powerSearchConfig: enumPowerSearchConfig, + powerSearchConfig: { + type: 'enum', + inferEnumOptionsFromData: true, + }, }, { key: 'tid', @@ -99,7 +103,10 @@ function createColumnConfig( key: 'tag', title: 'Tag', width: 160, - powerSearchConfig: enumPowerSearchConfig, + powerSearchConfig: { + type: 'enum', + inferEnumOptionsFromData: true, + }, }, { key: 'app', @@ -120,15 +127,6 @@ function createColumnConfig( ]; } -const enumPowerSearchConfig = { - inferEnumOptionsFromData: true, - - operators: [ - dataTablePowerSearchOperators.enum_set_is_any_of({}), - dataTablePowerSearchOperators.enum_set_is_none_of({}), - ], -}; - function getRowStyle(entry: DeviceLogEntry): CSSProperties | undefined { return (logTypes[entry.type]?.style as any) ?? baseRowStyle; } From cb485613e45cb03508ba378938d5faea24a7cb24 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Mon, 13 Nov 2023 08:26:13 -0800 Subject: [PATCH 046/112] Internal Xcode install instruction Reviewed By: elboman Differential Revision: D51257660 fbshipit-source-id: 6cf213135f1070def3e62e936d6147b15749b7de --- desktop/doctor/src/index.tsx | 2 +- .../flipper-ui-core/src/sandy-chrome/SetupDoctorScreen.tsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index f02f59dcc..dc5244a6f 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -148,7 +148,7 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { run: async (e: FlipperDoctor.EnvironmentInfo) => { const hasProblem = e.IDEs == null || e.IDEs.Xcode == null; const message = hasProblem - ? `Xcode is not installed. ${installXcode}.` + ? `Xcode is not installed.\n${installXcode}.` : `Xcode version ${e.IDEs.Xcode.version} is installed at "${e.IDEs.Xcode.path}".`; return { hasProblem, diff --git a/desktop/flipper-ui-core/src/sandy-chrome/SetupDoctorScreen.tsx b/desktop/flipper-ui-core/src/sandy-chrome/SetupDoctorScreen.tsx index 552a7329e..569839f90 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/SetupDoctorScreen.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/SetupDoctorScreen.tsx @@ -133,7 +133,11 @@ function CollapsableCategory(props: { key={check.key} header={check.label} extra={}> - {check.result.message} + {check.result.message?.split('\n').map((line, index) => ( + + {line} + + ))} {check.result.commands && ( {check.result.commands.map(({title, command}, i) => ( From 1199e1f6670c5422221e88d6d3e6a76418058123 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Mon, 13 Nov 2023 08:26:13 -0800 Subject: [PATCH 047/112] Update settings location to reflect new nav bar Reviewed By: elboman Differential Revision: D51257953 fbshipit-source-id: f19d2a1343276ee066b422e85b71e4d0a8e7bdb9 --- desktop/doctor/src/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index dc5244a6f..ed7133499 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -78,12 +78,12 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { if (!androidHome) { androidHomeResult = { hasProblem: true, - message: `ANDROID_HOME is not defined. You can use Flipper Settings (File > Preferences) to point to its location.`, + message: `ANDROID_HOME is not defined. You can use Flipper Settings (More > Settings) to point to its location.`, }; } else if (!fs.existsSync(androidHome)) { androidHomeResult = { hasProblem: true, - message: `ANDROID_HOME point to a folder which does not exist: ${androidHome}. You can use Flipper Settings (File > Preferences) to point to a different location.`, + message: `ANDROID_HOME point to a folder which does not exist: ${androidHome}. You can use Flipper Settings (More > Settings) to point to a different location.`, }; } else { const platformToolsDir = path.join(androidHome, 'platform-tools'); @@ -106,12 +106,12 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { if (!androidSdkRoot) { androidSdkRootResult = { hasProblem: true, - message: `ANDROID_SDK_ROOT is not defined. You can use Flipper Settings (File > Preferences) to point to its location.`, + message: `ANDROID_SDK_ROOT is not defined. You can use Flipper Settings (More > Settings) to point to its location.`, }; } else if (!fs.existsSync(androidSdkRoot)) { androidSdkRootResult = { hasProblem: true, - message: `ANDROID_SDK_ROOT point to a folder which does not exist: ${androidSdkRoot}. You can use Flipper Settings (File > Preferences) to point to a different location.`, + message: `ANDROID_SDK_ROOT point to a folder which does not exist: ${androidSdkRoot}. You can use Flipper Settings (More > Settings) to point to a different location.`, }; } else { const platformToolsDir = path.join( From bc77dcf3266c822572bcfa11dfd73ed27d00fd60 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Mon, 13 Nov 2023 12:51:45 -0800 Subject: [PATCH 048/112] Simplify shutdown logic Summary: The previous logic aimed to reuse an existing server during bootstrap if the launched version was higher than the running one. This is no longer required or wanted. ## Risk assessment: LOW ### Rationale It is extremely rare to launch Flipper whilst already having another instance running. This can happen during development, but it is extremely rare in production. Launcher (singleton) launches Server (singleton). Launcher can be executed multiple times and this will not create newer server instances. If anything, if we are unable to kill any other instance, whatever that may be, continue. This is to cover the cases where a shutdown may have been acknowledged but the process is still shutting down. Reviewed By: antonk52 Differential Revision: D51232901 fbshipit-source-id: 8b8b85f4bac68f5670b1878e827871f78dc68999 --- desktop/flipper-server/src/index.tsx | 29 +++++++++------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index 5ab044506..36837a0a9 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -22,7 +22,6 @@ import { UIPreference, checkPortInUse, checkServerRunning, - compareServerVersion, getEnvironmentInfo, openUI, setupPrefetcher, @@ -161,30 +160,24 @@ async function start() { `[flipper-server][bootstrap] Keytar loaded (${keytarLoadedMS} ms)`, ); - let launchAndFinish = false; - console.info('[flipper-server] Check for running instances'); const existingRunningInstanceVersion = await checkServerRunning(argv.port); if (existingRunningInstanceVersion) { console.info( `[flipper-server] Running instance found with version: ${existingRunningInstanceVersion}, current version: ${environmentInfo.appVersion}`, ); - if ( - compareServerVersion( - environmentInfo.appVersion, - existingRunningInstanceVersion, - ) > 0 - ) { - console.info(`[flipper-server] Shutdown running instance`); - await shutdownRunningInstance(argv.port); - } else { - launchAndFinish = true; - } + console.info(`[flipper-server] Shutdown running instance`); + const success = await shutdownRunningInstance(argv.port); + console.info( + `[flipper-server] Shutdown running instance acknowledged: ${success}`, + ); } else { console.info('[flipper-server] Checking if port is in use (TCP)'); if (await checkPortInUse(argv.port)) { - console.info(`[flipper-server] Shutdown running instance`); - await shutdownRunningInstance(argv.port); + const success = await shutdownRunningInstance(argv.port); + console.info( + `[flipper-server] Shutdown running instance acknowledged: ${success}`, + ); } } @@ -194,10 +187,6 @@ async function start() { `[flipper-server][bootstrap] Check for running instances completed (${runningInstanceShutdownMS} ms)`, ); - if (launchAndFinish) { - return await launch(); - } - const {app, server, socket, readyForIncomingConnections} = await startServer( { staticPath, From d88cf41a246fa11cad7fc6b9faa210ecb027e979 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 14 Nov 2023 02:54:59 -0800 Subject: [PATCH 049/112] Always write ANDROID_HOME and ANDROID_SDK_ROOT Summary: When flipper starts it writes to both env vars the value from settings. This is needed for subprocesses to have access to them. The problem with the check for both env vars is - In prod environment applications does not have access to them - In dev, when developers launch flipper from terminal (`yarn run flipper-server`) it picks up these env vars if they are set which can cause different behaviour in dev and prod To make flipper work more deterministic, for users and us. I removed this check and now we always write to these env vars what the user has provided in flipper settings or the default. Reviewed By: lblasa Differential Revision: D51266495 fbshipit-source-id: cf3adfd4ba83a733a30b5b0b29c270b32ff3a61a --- desktop/flipper-server-core/src/FlipperServerImpl.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index dd07f3889..1cef26dae 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -75,10 +75,8 @@ function setProcessState(settings: Settings) { const androidHome = settings.androidHome; const idbPath = settings.idbPath; - if (!process.env.ANDROID_HOME && !process.env.ANDROID_SDK_ROOT) { - process.env.ANDROID_HOME = androidHome; - process.env.ANDROID_SDK_ROOT = androidHome; - } + process.env.ANDROID_HOME = androidHome; + process.env.ANDROID_SDK_ROOT = androidHome; // emulator/emulator is more reliable than tools/emulator, so prefer it if // it exists From f6445fea43d658299f1cca727e60c290b002d0c7 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 14 Nov 2023 04:29:59 -0800 Subject: [PATCH 050/112] Check if Android Studio is installed Summary: Since Android Studio is technically not required, I made this check optional. Though, these steps will gurantee that android env will work for an engineer who opened flipper for the first time Reviewed By: lblasa Differential Revision: D51267272 fbshipit-source-id: f30e58f322ea080b00a27ae86b871df2b39e1bb9 --- .../src/fb-stubs/{ios.tsx => messages.tsx} | 5 ++++ desktop/doctor/src/index.tsx | 26 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) rename desktop/doctor/src/fb-stubs/{ios.tsx => messages.tsx} (82%) diff --git a/desktop/doctor/src/fb-stubs/ios.tsx b/desktop/doctor/src/fb-stubs/messages.tsx similarity index 82% rename from desktop/doctor/src/fb-stubs/ios.tsx rename to desktop/doctor/src/fb-stubs/messages.tsx index cfa1bdc85..5ef437295 100644 --- a/desktop/doctor/src/fb-stubs/ios.tsx +++ b/desktop/doctor/src/fb-stubs/messages.tsx @@ -15,3 +15,8 @@ export const installXcode = export const installSDK = 'You can install it using Xcode (https://developer.apple.com/xcode/).'; + +export const installAndroidStudio = [ + 'Android Studio is not installed.', + 'Install Android Studio from https://developer.android.com/studio', +].join('\n'); diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index ed7133499..6f433e5b0 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -21,7 +21,8 @@ import { getIdbInstallationInstructions, installXcode, installSDK, -} from './fb-stubs/ios'; + installAndroidStudio, +} from './fb-stubs/messages'; import {validateSelectedXcodeVersion} from './fb-stubs/validateSelectedXcodeVersion'; export function getHealthchecks(): FlipperDoctor.Healthchecks { @@ -66,6 +67,29 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { isRequired: false, isSkipped: false, healthchecks: [ + ...(process.platform === 'darwin' + ? [ + { + key: 'android.android-studio', + label: 'Android Studio Installed', + isRequired: false, + run: async (_: FlipperDoctor.EnvironmentInfo) => { + const hasProblem = !fs.existsSync( + '/Applications/Android Studio.app', + ); + + const message = hasProblem + ? installAndroidStudio + : `Android Studio is installed.`; + + return { + hasProblem, + message, + }; + }, + }, + ] + : []), { key: 'android.sdk', label: 'SDK Installed', From 9910807826fa7a1559cff4fd8b58e9fd1fbee5f4 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 14 Nov 2023 06:28:11 -0800 Subject: [PATCH 051/112] Fix power search for slogs Reviewed By: antonk52 Differential Revision: D51232391 fbshipit-source-id: 6501f60ee4168c62d1d4efefcfcc698d4954e7ac --- .../DataTableWithPowerSearchManager.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx index 109bc03c3..4aa54a630 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx @@ -518,9 +518,6 @@ export function computeDataTableFilter( const value = searchTerm.field.useWholeRow ? item : getValueAtPath(item, searchTerm.field.key); - if (!value) { - return treatUndefinedValuesAsMatchingFiltering; - } const processor = powerSearchProcessors[ @@ -535,7 +532,21 @@ export function computeDataTableFilter( return true; } - return processor(searchTerm.operator, searchTerm.searchValue, value); + try { + const res = processor( + searchTerm.operator, + searchTerm.searchValue, + value, + ); + + if (!res && !value) { + return treatUndefinedValuesAsMatchingFiltering; + } + + return res; + } catch { + return treatUndefinedValuesAsMatchingFiltering; + } }); }; } From ed801517680cc54c14fff1a296d2d98adfb5a6cf Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 Date: Tue, 14 Nov 2023 06:28:51 -0800 Subject: [PATCH 052/112] Flipper Release: v0.238.0 Summary: Releasing version 0.238.0 Reviewed By: aigoncharov Differential Revision: D51298282 fbshipit-source-id: ed64ff1a72194f6f4de0a87a8e05a2de83e29cc1 --- desktop/package.json | 2 +- desktop/plugins/public/layout/docs/setup.mdx | 2 +- desktop/plugins/public/leak_canary/docs/setup.mdx | 2 +- desktop/plugins/public/network/docs/setup.mdx | 2 +- desktop/static/CHANGELOG.md | 6 ++++++ docs/getting-started/android-native.mdx | 4 ++-- docs/getting-started/react-native-ios.mdx | 2 +- docs/getting-started/react-native.mdx | 4 ++-- gradle.properties | 2 +- js/js-flipper/package.json | 2 +- react-native/react-native-flipper/package.json | 2 +- 11 files changed, 18 insertions(+), 12 deletions(-) diff --git a/desktop/package.json b/desktop/package.json index 7713eb64e..866c86b29 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -170,7 +170,7 @@ "npm": "use yarn instead", "yarn": "^1.16" }, - "version": "0.237.0", + "version": "0.238.0", "workspaces": { "packages": [ "scripts", diff --git a/desktop/plugins/public/layout/docs/setup.mdx b/desktop/plugins/public/layout/docs/setup.mdx index fe6761c39..d86a56f62 100644 --- a/desktop/plugins/public/layout/docs/setup.mdx +++ b/desktop/plugins/public/layout/docs/setup.mdx @@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.237.0' + debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.238.0' debugImplementation 'com.facebook.litho:litho-annotations:0.19.0' // ... } diff --git a/desktop/plugins/public/leak_canary/docs/setup.mdx b/desktop/plugins/public/leak_canary/docs/setup.mdx index 68fe9457f..c3d990da9 100644 --- a/desktop/plugins/public/leak_canary/docs/setup.mdx +++ b/desktop/plugins/public/leak_canary/docs/setup.mdx @@ -8,7 +8,7 @@ To setup the LeakCan ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.237.0' + debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.238.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' } ``` diff --git a/desktop/plugins/public/network/docs/setup.mdx b/desktop/plugins/public/network/docs/setup.mdx index 95545b026..d664cc3f4 100644 --- a/desktop/plugins/public/network/docs/setup.mdx +++ b/desktop/plugins/public/network/docs/setup.mdx @@ -12,7 +12,7 @@ The network plugin is shipped as a separate Maven artifact, as follows: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.237.0' + debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.238.0' } ``` diff --git a/desktop/static/CHANGELOG.md b/desktop/static/CHANGELOG.md index 3fe09459d..544d45aca 100644 --- a/desktop/static/CHANGELOG.md +++ b/desktop/static/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.238.0 (14/11/2023) + + * [D51199644](https://github.com/facebook/flipper/search?q=D51199644&type=Commits) - [Logs] Improve power search config to populate dropdown for level, PID & Tag + * [D51199783](https://github.com/facebook/flipper/search?q=D51199783&type=Commits) - [Analytics] Improve power search config to populate dropdown for low cardinality columns + + # 0.237.0 (10/11/2023) * [D51113095](https://github.com/facebook/flipper/search?q=D51113095&type=Commits) - UIdebugger added powersearch operators to Framework event table diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 4506816a8..54fca03ce 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -24,10 +24,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.237.0' + debugImplementation 'com.facebook.flipper:flipper:0.238.0' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.237.0' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.238.0' } ``` diff --git a/docs/getting-started/react-native-ios.mdx b/docs/getting-started/react-native-ios.mdx index 993cecb31..5763e53e9 100644 --- a/docs/getting-started/react-native-ios.mdx +++ b/docs/getting-started/react-native-ios.mdx @@ -51,7 +51,7 @@ Add all of the code below to your `ios/Podfile`: platform :ios, '9.0' def flipper_pods() - flipperkit_version = '0.237.0' # should match the version of your Flipper client app + flipperkit_version = '0.238.0' # should match the version of your Flipper client app pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug' diff --git a/docs/getting-started/react-native.mdx b/docs/getting-started/react-native.mdx index cccf15c2b..206949a52 100644 --- a/docs/getting-started/react-native.mdx +++ b/docs/getting-started/react-native.mdx @@ -34,7 +34,7 @@ Latest version of Flipper requires react-native 0.69+! If you use react-native < Android: -1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.237.0`. +1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.238.0`. 2. Run `./gradlew clean` in the `android` directory. iOS: @@ -44,7 +44,7 @@ react-native version => 0.69.0 2. Run `pod install --repo-update` in the `ios` directory. react-native version < 0.69.0 -1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.237.0' })`. +1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.238.0' })`. 2. Run `pod install --repo-update` in the `ios` directory. ## Manual Setup diff --git a/gradle.properties b/gradle.properties index 6e28d734b..acd98509a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.237.1-SNAPSHOT +VERSION_NAME=0.238.0 GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper diff --git a/js/js-flipper/package.json b/js/js-flipper/package.json index 6c3c5ac8b..3d53872ff 100644 --- a/js/js-flipper/package.json +++ b/js/js-flipper/package.json @@ -1,7 +1,7 @@ { "name": "js-flipper", "title": "JS Flipper Bindings for Web-Socket based clients", - "version": "0.237.0", + "version": "0.238.0", "main": "lib/index.js", "browser": { "os": false diff --git a/react-native/react-native-flipper/package.json b/react-native/react-native-flipper/package.json index 31f9b2584..56318b16f 100644 --- a/react-native/react-native-flipper/package.json +++ b/react-native/react-native-flipper/package.json @@ -1,7 +1,7 @@ { "name": "react-native-flipper", "title": "React Native Flipper Bindings", - "version": "0.237.0", + "version": "0.238.0", "description": "Flipper bindings for React Native", "main": "index.js", "types": "index.d.ts", From b6337661993540d610cb1e0f20d9c972c66403d1 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 Date: Tue, 14 Nov 2023 06:28:51 -0800 Subject: [PATCH 053/112] Flipper Snapshot Bump: v0.238.1-SNAPSHOT Summary: Releasing snapshot version 0.238.1-SNAPSHOT Reviewed By: aigoncharov Differential Revision: D51298284 fbshipit-source-id: 51f30bd24af2bb2a40b3e4d87cd7acb7e9bbe4ce --- docs/getting-started/android-native.mdx | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 54fca03ce..f363c20d4 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -124,10 +124,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.237.1-SNAPSHOT' + debugImplementation 'com.facebook.flipper:flipper:0.238.1-SNAPSHOT' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.237.1-SNAPSHOT' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.238.1-SNAPSHOT' } ``` diff --git a/gradle.properties b/gradle.properties index acd98509a..33d6ce8cf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.238.0 +VERSION_NAME=0.238.1-SNAPSHOT GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper From 6c4c579f2703fa46dca5b79586056058c8e045dd Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 14 Nov 2023 08:14:37 -0800 Subject: [PATCH 054/112] Increase max width for resizable sidebar Reviewed By: LukeDefeo Differential Revision: D51305977 fbshipit-source-id: 03fbdba01d01aaecdd182538aed64ae396818f4d --- desktop/flipper-plugin/src/ui/Sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/flipper-plugin/src/ui/Sidebar.tsx b/desktop/flipper-plugin/src/ui/Sidebar.tsx index a2e5f52aa..36e2c6a03 100644 --- a/desktop/flipper-plugin/src/ui/Sidebar.tsx +++ b/desktop/flipper-plugin/src/ui/Sidebar.tsx @@ -168,7 +168,7 @@ export class Sidebar extends Component { if (horizontal) { width = width == null ? 200 : width; minWidth = (minWidth == null ? 100 : minWidth) + gutterWidth; - maxWidth = maxWidth == null ? 600 : maxWidth; + maxWidth = maxWidth == null ? 1200 : maxWidth; } else { height = height == null ? 200 : height; minHeight = minHeight == null ? 100 : minHeight; From 0889a0e02dec941933dc9c8358db0f75f4693ecc Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Tue, 14 Nov 2023 08:40:48 -0800 Subject: [PATCH 055/112] Tooltip for node type icon Reviewed By: aigoncharov Differential Revision: D51307310 fbshipit-source-id: 9a522928102de6a28c0307568451acaed969edc5 --- .../ui-debugger/components/tree/Tree.tsx | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx b/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx index 52d9b42b9..7d9160c92 100644 --- a/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx +++ b/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx @@ -30,7 +30,7 @@ import { } from 'flipper-plugin'; import {plugin} from '../../index'; import {head, last} from 'lodash'; -import {Badge, Typography} from 'antd'; +import {Badge, Tooltip, Typography} from 'antd'; import {useVirtualizer} from '@tanstack/react-virtual'; import {ContextMenu} from './ContextMenu'; @@ -577,33 +577,52 @@ function HighlightedText(props: {text: string}) { } function nodeIcon(node: TreeNode) { + const [icon, tooltip] = nodeData(node); + + const iconComp = + typeof icon === 'string' ? : icon; + + if (tooltip == null) { + return iconComp; + } else { + return {iconComp}; + } +} + +function nodeData(node: TreeNode) { if (node.tags.includes('LithoMountable')) { - return ; + return ['icons/litho-logo-blue.png', 'Litho Mountable (Primitive)']; } else if (node.tags.includes('Litho')) { - return ; + return ['icons/litho-logo.png', 'Litho Component']; } else if (node.tags.includes('CK')) { if (node.tags.includes('iOS')) { - return ; + return ['icons/ck-mounted-logo.png', 'CK Mounted Component']; } - return ; + return ['icons/ck-logo.png', 'CK Component']; } else if (node.tags.includes('BloksBoundTree')) { - return ; + return ['facebook/bloks-logo-orange.png', 'Bloks Bridged component']; } else if (node.tags.includes('BloksDerived')) { - return ; + return ['facebook/bloks-logo-blue.png', 'Bloks Derived (Server) component']; } else if (node.tags.includes('Warning')) { - return ( - - ); + return [ + , + null, + ]; } else { - return ( + return [
- ); + />, + null, + ]; } } From 49abb4dd41c5ecb2f3dfed8bcb0d91eb044c9d39 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Tue, 14 Nov 2023 10:53:09 -0800 Subject: [PATCH 056/112] Move constants to flipperConfig Summary: Recently we have added a few constants to be used by our main entry point. This change moves them to a central place: flipperConfig. Reviewed By: aigoncharov Differential Revision: D51307088 fbshipit-source-id: 09f0ef0e69e2067ce5c8501367629eeec7523858 --- desktop/flipper-ui-browser/src/global.tsx | 10 +++++----- desktop/flipper-ui-browser/src/index.tsx | 2 +- desktop/static/index.web.dev.html | 13 ++++++------- desktop/static/index.web.html | 11 +++++------ 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/desktop/flipper-ui-browser/src/global.tsx b/desktop/flipper-ui-browser/src/global.tsx index 566fd8657..7f7b0a1ae 100644 --- a/desktop/flipper-ui-browser/src/global.tsx +++ b/desktop/flipper-ui-browser/src/global.tsx @@ -17,12 +17,12 @@ declare global { theme: 'light' | 'dark' | 'system'; entryPoint: string; debug: boolean; + GRAPH_SECRET: string; + FLIPPER_APP_VERSION: string; + FLIPPER_SESSION_ID: string; + FLIPPER_UNIXNAME: string; + FLIPPER_AUTH_TOKEN: string; }; - GRAPH_SECRET: string; - FLIPPER_APP_VERSION: string; - FLIPPER_SESSION_ID: string; - FLIPPER_UNIXNAME: string; - FLIPPER_AUTH_TOKEN: string; flipperShowMessage?(message: {title?: string; detail?: string}): void; flipperHideMessage?(): void; diff --git a/desktop/flipper-ui-browser/src/index.tsx b/desktop/flipper-ui-browser/src/index.tsx index c07298a8f..ef24f2202 100644 --- a/desktop/flipper-ui-browser/src/index.tsx +++ b/desktop/flipper-ui-browser/src/index.tsx @@ -56,7 +56,7 @@ async function start() { let token = providerParams.get('token'); if (!token) { console.info('[flipper-client][ui-browser] Get token from HTML instead'); - token = window.FLIPPER_AUTH_TOKEN; + token = window.flipperConfig.FLIPPER_AUTH_TOKEN; if (!token || token === 'FLIPPER_AUTH_TOKEN_REPLACE_ME') { console.warn( '[flipper-client][ui-browser] Failed to get token from HTML', diff --git a/desktop/static/index.web.dev.html b/desktop/static/index.web.dev.html index e79cd0932..b90b3afd0 100644 --- a/desktop/static/index.web.dev.html +++ b/desktop/static/index.web.dev.html @@ -18,6 +18,11 @@ theme: 'light', entryPoint: 'flipper-ui-browser/src/index-fast-refresh.bundle?platform=web&dev=true&minify=false', debug: true, + GRAPH_SECRET: 'GRAPH_SECRET_REPLACE_ME', + FLIPPER_APP_VERSION: 'FLIPPER_APP_VERSION_REPLACE_ME', + FLIPPER_SESSION_ID: 'FLIPPER_SESSION_ID_REPLACE_ME', + FLIPPER_UNIXNAME: 'FLIPPER_UNIXNAME_REPLACE_ME', + FLIPPER_AUTH_TOKEN: 'FLIPPER_AUTH_TOKEN_REPLACE_ME', } @@ -60,7 +62,7 @@ From 6b54bd317328d1b8ca947be253689a8c44b90118 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Tue, 14 Nov 2023 10:53:09 -0800 Subject: [PATCH 064/112] Remove no longer needed index.web.dev.html Summary: This is a duplicate, is not needed, causes confusion. Reviewed By: aigoncharov Differential Revision: D51307091 fbshipit-source-id: 4d55d727ea5f20100ecd15ad6e23aa0c01722524 --- desktop/app/src/init.tsx | 2 +- .../src/server/startServer.tsx | 1 - desktop/flipper-server/src/index.tsx | 2 +- .../scripts/build-flipper-server-release.tsx | 1 - desktop/static/index.web.dev.html | 142 ------------------ 5 files changed, 2 insertions(+), 146 deletions(-) delete mode 100644 desktop/static/index.web.dev.html diff --git a/desktop/app/src/init.tsx b/desktop/app/src/init.tsx index 697d66c49..756a678ed 100644 --- a/desktop/app/src/init.tsx +++ b/desktop/app/src/init.tsx @@ -143,7 +143,7 @@ async function getFlipperServer( const {readyForIncomingConnections} = await startServer( { staticPath, - entry: 'index.web.dev.html', + entry: 'index.web.html', port, }, environmentInfo, diff --git a/desktop/flipper-server-core/src/server/startServer.tsx b/desktop/flipper-server-core/src/server/startServer.tsx index 26231b248..ff3bc2340 100644 --- a/desktop/flipper-server-core/src/server/startServer.tsx +++ b/desktop/flipper-server-core/src/server/startServer.tsx @@ -185,7 +185,6 @@ async function startHTTPServer( }; app.get('/', serveRoot); app.get('/index.web.html', serveRoot); - app.get('/index.web.dev.html', serveRoot); app.get('/ready', (_req, res) => { tracker.track('server-endpoint-hit', {name: 'ready'}); diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index 36837a0a9..51bf99b5c 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -190,7 +190,7 @@ async function start() { const {app, server, socket, readyForIncomingConnections} = await startServer( { staticPath, - entry: `index.web${argv.bundler ? '.dev' : ''}.html`, + entry: `index.web.html`, port: argv.port, }, environmentInfo, diff --git a/desktop/scripts/build-flipper-server-release.tsx b/desktop/scripts/build-flipper-server-release.tsx index 3dddd977c..bd58b1f84 100644 --- a/desktop/scripts/build-flipper-server-release.tsx +++ b/desktop/scripts/build-flipper-server-release.tsx @@ -244,7 +244,6 @@ async function copyStaticResources(outDir: string, versionNumber: string) { 'icon.png', 'icon_grey.png', 'icons.json', - 'index.web.dev.html', 'index.web.html', 'install_desktop.svg', 'loading.html', diff --git a/desktop/static/index.web.dev.html b/desktop/static/index.web.dev.html deleted file mode 100644 index 13d691aea..000000000 --- a/desktop/static/index.web.dev.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - Flipper - - - - - -
-

-

-
- -
-
- Loading... -
-
- - - - - From 6ccae929185f6e77b7b3daad49e3501fd1fa41bf Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Wed, 15 Nov 2023 03:45:25 -0800 Subject: [PATCH 065/112] PWA for development Summary: This was not possible in the past because the entry points for dev and production were different. This is no longer the case. Reviewed By: antonk52 Differential Revision: D51346250 fbshipit-source-id: fc482a9d90352ea4e897092e7670920f148fa274 --- desktop/flipper-server/src/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index 51bf99b5c..9740fe9eb 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -31,7 +31,7 @@ import { tracker, processExit, } from 'flipper-server-core'; -import {addLogTailer, isProduction, isTest, LoggerFormat} from 'flipper-common'; +import {addLogTailer, isTest, LoggerFormat} from 'flipper-common'; import exitHook from 'exit-hook'; const argv = yargs @@ -315,8 +315,7 @@ async function launch() { return; } - const preference = isProduction() ? UIPreference.PWA : UIPreference.Browser; - openUI(preference, argv.port); + openUI(UIPreference.PWA, argv.port); } process.on('uncaughtException', (error) => { From ed7a7f7bd0a132eab04234d91579d3e7d590a91b Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 15 Nov 2023 03:46:46 -0800 Subject: [PATCH 066/112] Fix processExit Summary: setTimeout should terminate process if graceful termination takes too long. Before, we scheduled the timer only *after* the graceful termination completes which does not make any sense Reviewed By: lblasa Differential Revision: D51346673 fbshipit-source-id: b5adadbcf474a8c66839e1fc70bcc6482c47635e --- desktop/flipper-server-core/src/utils/processExit.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/desktop/flipper-server-core/src/utils/processExit.tsx b/desktop/flipper-server-core/src/utils/processExit.tsx index a002709de..841f746d4 100644 --- a/desktop/flipper-server-core/src/utils/processExit.tsx +++ b/desktop/flipper-server-core/src/utils/processExit.tsx @@ -19,6 +19,11 @@ const resIsPromise = (res: void | Promise): res is Promise => export const processExit = async (code: number) => { console.debug('processExit', code); + setTimeout(() => { + console.error('Process exit routines timed out'); + process.exit(code); + }, 5000); + // eslint-disable-next-line promise/catch-or-return await Promise.all( onBeforeExitFns.map(async (fn) => { @@ -36,9 +41,4 @@ export const processExit = async (code: number) => { ).finally(() => { process.exit(code); }); - - setTimeout(() => { - console.error('Process exit routines timed out'); - process.exit(code); - }, 5000); }; From f01568bf5908b2b302b77b90c67400ea1d4287e2 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 15 Nov 2023 04:22:11 -0800 Subject: [PATCH 067/112] Prevent duplicate browser connection reporting Reviewed By: lblasa Differential Revision: D51347107 fbshipit-source-id: 14f4507835794d76b17f9a6891f22dbc0cc1a8f7 --- desktop/flipper-server/src/index.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index 9740fe9eb..bb438869b 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -105,8 +105,13 @@ const browserConnectionTimeout = setTimeout(() => { timedOut: true, }); }, 10000); +let reported = false; const reportBrowserConnection = (successful: boolean) => { + if (reported) { + return; + } clearTimeout(browserConnectionTimeout); + reported = true; tracker.track('browser-connection-created', { successful, timeMS: performance.now() - t0, From b34718ac32e78be3e8fd72256ecc7a411dcdb332 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 15 Nov 2023 05:00:21 -0800 Subject: [PATCH 068/112] Extend docs Reviewed By: antonk52 Differential Revision: D51305961 fbshipit-source-id: a4ca7365def6ebf43b84668f63887cc81f82fb36 --- docs/extending/power-search.mdx | 95 ++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/docs/extending/power-search.mdx b/docs/extending/power-search.mdx index 5b424a5f0..5fed042f0 100644 --- a/docs/extending/power-search.mdx +++ b/docs/extending/power-search.mdx @@ -8,6 +8,99 @@ By default, your [table](../tutorial/js-table.mdx) has a power search bar. It al For instance, for string values it can check if a string contains a substring or even matches some other string exactly. At the same time, for dates Flipper can filter out records after or before a certain date. Since Flipper does not have a way of identifying the column type in advance, it always assumes that every column is a string. If you want you can tell Flipper how to handle a column and what power search operators should be available. -You can see a live example of how you can provide the power search config [here](https://fburl.com/code/rrxj86e5). +## Simplified config + +Power search provides a list of default predicates for every column data type. You can specify the column data type like this: + +```tsx +import {DataTableColumn} from 'flipper-plugin' + +type MyRow = { + timestamp: number; + eventType: string; +} + +const columns: DataTableColumn[] = [ + { + key: 'timestamp', + title: 'Timestamp', + sortable: true, + powerSearchConfig: {type: 'dateTime'}, + }, + { + key: 'eventType', + title: 'Event', + powerSearchConfig: {type: 'enum'} + }, +] +``` + +[Complete list of possible "types"](https://github.com/facebook/flipper/blob/main/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx#L148). + +## Advanced config + +If the default list of predicates is not tailored enouhg for your use-case, you can provide a list of predicates explicitly. + +```tsx +import {DataTableColumn, dataTablePowerSearchOperators} from 'flipper-plugin' + +type MyRow = { + timestamp: number; + eventType: string; +} + +const EVENT_TYPE_ENUM_LABELS = { + yodaValue: 'Yoda Label', + lukeValue: 'Luke Label' +} + +const columns: DataTableColumn[] = [ + { + key: 'timestamp', + title: 'Timestamp', + sortable: true, + powerSearchConfig: [ + dataTablePowerSearchOperators.same_as_absolute_date_no_time(), + ] + }, + { + key: 'eventType', + title: 'Event', + powerSearchConfig: { + // You can also provide power search config as an object + operators: [ + dataTablePowerSearchOperators.enum_is(EVENT_TYPE_ENUM_LABELS), + dataTablePowerSearchOperators.enum_is_not(EVENT_TYPE_ENUM_LABELS), + ], + // It could have exra options + // See https://github.com/facebook/flipper/blob/main/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx#L157 + } + }, +] +``` + +## Using legacy search + +While we would encourage using the new power search, some plugins might decide to stick to the legacy experience. To do that you have to use different imports from 'flipper-plugin': `MasterDetailLegacy` instead of `MasterDetail`, `DataTableLegacy` instead of `DataTable`, `DataTableColumnLegacy` instead of `DataTable`, `DataTableManagerLegacy` instead of `DataTableManager`. + +```tsx +import {MasterDetailLegacy, DataTableColumnLegacy} from 'flipper-plugin'; + +const columns: DataTableColumnLegacy[] = [ + // colun definition +] + +export const Component = () => { + return +} +``` + +## Examples + +You can see a live examplse of how you can provide the power search config here: + +0. [Logs](https://github.com/facebook/flipper/blob/main/desktop/plugins/public/logs/index.tsx#L49) +0. [Network](https://github.com/facebook/flipper/blob/main/desktop/plugins/public/network/index.tsx#L664) +0. [Intern-only](https://fburl.com/code/liiu1wns). You can find the complete list of supported operators [here](https://github.com/facebook/flipper/blob/main/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx). From bfc4e959bc29e0e030adb04fea853f25ec340860 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 15 Nov 2023 05:00:21 -0800 Subject: [PATCH 069/112] Export power search enabled components and types by default Summary: Provides power search typings to all users by default Reviewed By: LukeDefeo Differential Revision: D51306600 fbshipit-source-id: c1f0d318d8b6953dd02af16d2c51abbf0e6e9590 --- desktop/flipper-plugin/src/index.tsx | 15 +- .../__tests__/flipper_messages.node.tsx | 103 +++++-------- .../src/dispatcher/plugins.tsx | 6 +- desktop/plugins/public/logs/index.tsx | 12 +- desktop/plugins/public/network/index.tsx | 6 +- .../request-mocking/NetworkRouteManager.tsx | 6 +- .../components/FrameworkEventsTable.tsx | 145 +++++++++--------- 7 files changed, 125 insertions(+), 168 deletions(-) diff --git a/desktop/flipper-plugin/src/index.tsx b/desktop/flipper-plugin/src/index.tsx index 7b6e0ce6a..4935c88f8 100644 --- a/desktop/flipper-plugin/src/index.tsx +++ b/desktop/flipper-plugin/src/index.tsx @@ -38,9 +38,8 @@ export {Sidebar as _Sidebar} from './ui/Sidebar'; export {DetailSidebar} from './ui/DetailSidebar'; export {Toolbar} from './ui/Toolbar'; -export {MasterDetail} from './ui/MasterDetail'; +export {MasterDetailWithPowerSearch as MasterDetail} from './ui/MasterDetailWithPowerSearch'; export {MasterDetail as MasterDetailLegacy} from './ui/MasterDetail'; -export {MasterDetailWithPowerSearch as _MasterDetailWithPowerSearch} from './ui/MasterDetailWithPowerSearch'; export {CodeBlock} from './ui/CodeBlock'; export {renderReactRoot, _PortalsManager} from './utils/renderReactRoot'; @@ -59,19 +58,17 @@ export {DataFormatter} from './ui/DataFormatter'; export {useLogger, _LoggerContext} from './utils/useLogger'; -export {DataTable, DataTableColumn} from './ui/data-table/DataTable'; +export { + DataTable, + DataTableColumn, +} from './ui/data-table/DataTableWithPowerSearch'; export { DataTable as DataTableLegacy, DataTableColumn as DataTableColumnLegacy, } from './ui/data-table/DataTable'; -export {DataTableManager} from './ui/data-table/DataTableManager'; +export {DataTableManager} from './ui/data-table/DataTableWithPowerSearchManager'; export {DataTableManager as DataTableManagerLegacy} from './ui/data-table/DataTableManager'; -export { - DataTable as _DataTableWithPowerSearch, - DataTableColumn as _DataTableColumnWithPowerSearch, -} from './ui/data-table/DataTableWithPowerSearch'; export {dataTablePowerSearchOperators} from './ui/data-table/DataTableDefaultPowerSearchOperators'; -export {DataTableManager as _DataTableWithPowerSearchManager} from './ui/data-table/DataTableWithPowerSearchManager'; export {DataList} from './ui/DataList'; export {Spinner} from './ui/Spinner'; export * from './ui/PowerSearch'; diff --git a/desktop/flipper-ui-core/src/chrome/__tests__/flipper_messages.node.tsx b/desktop/flipper-ui-core/src/chrome/__tests__/flipper_messages.node.tsx index c29eb592c..d10ccab3b 100644 --- a/desktop/flipper-ui-core/src/chrome/__tests__/flipper_messages.node.tsx +++ b/desktop/flipper-ui-core/src/chrome/__tests__/flipper_messages.node.tsx @@ -121,82 +121,49 @@ test('It can render rows', async () => { (await renderer.findByText('unique-string')).parentElement?.parentElement, ).toMatchInlineSnapshot(`
- - +
00:00:00.000 - -
-
- - +
+
Android Phone - -
-
- - +
+
FB4A - -
-
- - +
+
unique-string - -
-
- - - -
-
- - - -
-
- - +
+
+
+
toClient:send - +
`); diff --git a/desktop/flipper-ui-core/src/dispatcher/plugins.tsx b/desktop/flipper-ui-core/src/dispatcher/plugins.tsx index 49364b099..63597fca8 100644 --- a/desktop/flipper-ui-core/src/dispatcher/plugins.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/plugins.tsx @@ -105,11 +105,11 @@ class UIPluginInitializer extends AbstractPluginInitializer { let uiPluginInitializer: UIPluginInitializer; export default async (store: Store, _logger: Logger) => { let FlipperPlugin = FlipperPluginSDK; - if (getRenderHostInstance().GK('flipper_power_search')) { + if (!getRenderHostInstance().GK('flipper_power_search')) { FlipperPlugin = { ...FlipperPlugin, - MasterDetail: FlipperPlugin._MasterDetailWithPowerSearch as any, - DataTable: FlipperPlugin._DataTableWithPowerSearch as any, + MasterDetail: FlipperPlugin.MasterDetailLegacy as any, + DataTable: FlipperPlugin.DataTableLegacy as any, }; } diff --git a/desktop/plugins/public/logs/index.tsx b/desktop/plugins/public/logs/index.tsx index e865a884e..5d0e6de10 100644 --- a/desktop/plugins/public/logs/index.tsx +++ b/desktop/plugins/public/logs/index.tsx @@ -13,10 +13,10 @@ import { usePlugin, createDataSource, dataTablePowerSearchOperators, - _DataTableColumnWithPowerSearch, - _DataTableWithPowerSearch, + DataTableColumn, + DataTable, theme, - _DataTableWithPowerSearchManager, + DataTableManager, createState, useValue, DataFormatter, @@ -48,7 +48,7 @@ const logLevelEnumLabels = Object.entries(logTypes).reduce( function createColumnConfig( _os: 'iOS' | 'Android' | 'Metro', -): _DataTableColumnWithPowerSearch[] { +): DataTableColumn[] { return [ { key: 'type', @@ -153,7 +153,7 @@ export function devicePlugin(client: DevicePluginClient) { }); const isPaused = createState(true); const tableManagerRef = createRef< - undefined | _DataTableWithPowerSearchManager + undefined | DataTableManager >(); client.onDeepLink((payload: unknown) => { @@ -264,7 +264,7 @@ export function Component() { const plugin = usePlugin(devicePlugin); const paused = useValue(plugin.isPaused); return ( - <_DataTableWithPowerSearch + dataSource={plugin.rows} columns={plugin.columns} enableAutoScroll diff --git a/desktop/plugins/public/network/index.tsx b/desktop/plugins/public/network/index.tsx index 023647e3a..1b364a8c2 100644 --- a/desktop/plugins/public/network/index.tsx +++ b/desktop/plugins/public/network/index.tsx @@ -28,9 +28,9 @@ import { usePlugin, useValue, createDataSource, - _DataTableWithPowerSearch as DataTable, - _DataTableColumnWithPowerSearch as DataTableColumn, - _DataTableWithPowerSearchManager as DataTableManager, + DataTable, + DataTableColumn, + DataTableManager, theme, renderReactRoot, batch, diff --git a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx index ab3163bf8..9322d0bc8 100644 --- a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx +++ b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx @@ -7,11 +7,7 @@ * @format */ -import { - Atom, - _DataTableWithPowerSearchManager as DataTableManager, - getFlipperLib, -} from 'flipper-plugin'; +import {Atom, DataTableManager, getFlipperLib} from 'flipper-plugin'; import {createContext} from 'react'; import {Header, Request} from '../types'; diff --git a/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx b/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx index fa05a353b..3eb3cf0e6 100644 --- a/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx +++ b/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx @@ -9,12 +9,11 @@ import {DeleteOutlined, PartitionOutlined} from '@ant-design/icons'; import { - _DataTableWithPowerSearch as DataTable, + DataTable, DataTableColumn, DetailSidebar, Layout, - _DataTableColumnWithPowerSearch, - _DataTableWithPowerSearchManager, + DataTableManager, dataTablePowerSearchOperators, usePlugin, useValue, @@ -44,10 +43,9 @@ export function FrameworkEventsTable({ const focusedNode = useValue(instance.uiState.focusedNode); - const managerRef = - useRef<_DataTableWithPowerSearchManager | null>( - null, - ); + const managerRef = useRef | null>( + null, + ); useEffect(() => { tracker.track('framework-event-table-opened', {}); @@ -175,74 +173,73 @@ const inferredEnum = [ dataTablePowerSearchOperators.enum_is_not({}), ]; -const staticColumns: _DataTableColumnWithPowerSearch[] = - [ - { - key: 'timestamp', - sortable: true, - onRender: (row: FrameworkEvent) => formatTimestampMillis(row.timestamp), - title: 'Timestamp', - formatters: MonoSpace, +const staticColumns: DataTableColumn[] = [ + { + key: 'timestamp', + sortable: true, + onRender: (row: FrameworkEvent) => formatTimestampMillis(row.timestamp), + title: 'Timestamp', + formatters: MonoSpace, - powerSearchConfig: [ - dataTablePowerSearchOperators.newer_than_absolute_date(), - dataTablePowerSearchOperators.older_than_absolute_date(), - ], + powerSearchConfig: [ + dataTablePowerSearchOperators.newer_than_absolute_date(), + dataTablePowerSearchOperators.older_than_absolute_date(), + ], + }, + { + key: 'type', + title: 'Event type', + onRender: (row: FrameworkEvent) => eventTypeToName(row.type), + powerSearchConfig: { + inferEnumOptionsFromData: true, + operators: inferredEnum, }, - { - key: 'type', - title: 'Event type', - onRender: (row: FrameworkEvent) => eventTypeToName(row.type), - powerSearchConfig: { - inferEnumOptionsFromData: true, - operators: inferredEnum, - }, - }, - { - key: 'duration', - title: 'Duration (Nanos)', - onRender: (row: FrameworkEvent) => - row.duration != null ? formatDuration(row.duration) : null, - formatters: MonoSpace, + }, + { + key: 'duration', + title: 'Duration (Nanos)', + onRender: (row: FrameworkEvent) => + row.duration != null ? formatDuration(row.duration) : null, + formatters: MonoSpace, - powerSearchConfig: [ - dataTablePowerSearchOperators.int_greater_or_equal(), - dataTablePowerSearchOperators.int_greater_than(), - dataTablePowerSearchOperators.int_equals(), - dataTablePowerSearchOperators.int_less_or_equal(), - dataTablePowerSearchOperators.int_less_than(), - ], - }, - { - key: 'treeId', - title: 'TreeId', - powerSearchConfig: idConfig, + powerSearchConfig: [ + dataTablePowerSearchOperators.int_greater_or_equal(), + dataTablePowerSearchOperators.int_greater_than(), + dataTablePowerSearchOperators.int_equals(), + dataTablePowerSearchOperators.int_less_or_equal(), + dataTablePowerSearchOperators.int_less_than(), + ], + }, + { + key: 'treeId', + title: 'TreeId', + powerSearchConfig: idConfig, - formatters: MonoSpace, - }, - { - key: 'rootComponentName', - title: 'Root component name', - powerSearchConfig: stringConfig, - formatters: MonoSpace, - }, - { - key: 'nodeId', - title: 'Component ID', - powerSearchConfig: idConfig, - formatters: MonoSpace, - }, - { - key: 'nodeName', - title: 'Component name', - powerSearchConfig: stringConfig, - formatters: MonoSpace, - }, - { - key: 'thread', - title: 'Thread', - onRender: (row: FrameworkEvent) => startCase(row.thread), - powerSearchConfig: stringConfig, - formatters: MonoSpace, - }, - ]; + formatters: MonoSpace, + }, + { + key: 'rootComponentName', + title: 'Root component name', + powerSearchConfig: stringConfig, + formatters: MonoSpace, + }, + { + key: 'nodeId', + title: 'Component ID', + powerSearchConfig: idConfig, + formatters: MonoSpace, + }, + { + key: 'nodeName', + title: 'Component name', + powerSearchConfig: stringConfig, + formatters: MonoSpace, + }, + { + key: 'thread', + title: 'Thread', + onRender: (row: FrameworkEvent) => startCase(row.thread), + powerSearchConfig: stringConfig, + formatters: MonoSpace, + }, +]; From 39d84e3bfc05534bcc1889321e9a9c81492eb0f7 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 15 Nov 2023 05:09:56 -0800 Subject: [PATCH 070/112] Fix occasional scrollbars Summary: Sometimes after a while scroll bars woudl start to appear in the text areas like so {F1150744703} Its hard to repro but seemed to be after flipper was open for a long period. Playing with chrome dev tools setting overflow: hidden makes the problem go away changelog: UIDebugger fix issue with scrollbars sometimes appearing in sidebar Reviewed By: antonk52 Differential Revision: D51346366 fbshipit-source-id: 2d9218ac582164c9726a92c3c0c99567382f8929 --- .../ui-debugger/components/sidebarV2/AttributesInspector.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/desktop/plugins/public/ui-debugger/components/sidebarV2/AttributesInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebarV2/AttributesInspector.tsx index 9e6e2cf0d..73684e3db 100644 --- a/desktop/plugins/public/ui-debugger/components/sidebarV2/AttributesInspector.tsx +++ b/desktop/plugins/public/ui-debugger/components/sidebarV2/AttributesInspector.tsx @@ -315,6 +315,7 @@ function NamedAttribute({ * disables hover and focsued states */ const readOnlyInput = css` + overflow: hidden; //stop random scrollbars from showing up font-size: small; :hover { border-color: ${theme.disabledColor} !important; @@ -388,7 +389,7 @@ function StyledTextArea({ return ( Date: Wed, 15 Nov 2023 07:30:47 -0800 Subject: [PATCH 071/112] Fix resetFilters not resetting power search Reviewed By: antonk52 Differential Revision: D51351225 fbshipit-source-id: 4f0406b047ed1cc44d7de54976db2a422b07b7c0 --- .../DataTableWithPowerSearchManager.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx index 4aa54a630..60a03e755 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx @@ -38,7 +38,7 @@ const emptySelection: Selection = { type PersistedState = { /** Active search value */ - searchExpression?: SearchExpressionTerm[]; + searchExpression: SearchExpressionTerm[]; /** current selection, describes the index index in the datasources's current output (not window!) */ selection: {current: number; items: number[]}; /** The currently applicable sorting, if any */ @@ -116,7 +116,7 @@ export type DataManagerState = { sorting: Sorting | undefined; selection: Selection; autoScroll: boolean; - searchExpression?: SearchExpressionTerm[]; + searchExpression: SearchExpressionTerm[]; filterExceptions: string[] | undefined; sideBySide: boolean; }; @@ -136,13 +136,13 @@ export const dataTableManagerReducer = produce< case 'reset': { draft.columns = computeInitialColumns(config.defaultColumns); draft.sorting = undefined; - draft.searchExpression = undefined; + draft.searchExpression = []; draft.selection = castDraft(emptySelection); draft.filterExceptions = undefined; break; } case 'resetFilters': { - draft.searchExpression = undefined; + draft.searchExpression = []; draft.filterExceptions = undefined; break; } @@ -169,7 +169,7 @@ export const dataTableManagerReducer = produce< } case 'setSearchExpression': { getFlipperLib().logger.track('usage', 'data-table:filter:power-search'); - draft.searchExpression = action.searchExpression; + draft.searchExpression = action.searchExpression ?? []; draft.filterExceptions = undefined; break; } @@ -374,7 +374,7 @@ export function createInitialState( }); } - let searchExpression = config.initialSearchExpression; + let searchExpression = config.initialSearchExpression ?? []; if (prefs?.searchExpression?.length) { searchExpression = prefs.searchExpression; } @@ -506,12 +506,12 @@ export function getValueAtPath(obj: Record, keyPath: string): any { } export function computeDataTableFilter( - searchExpression: SearchExpressionTerm[] | undefined, + searchExpression: SearchExpressionTerm[], powerSearchProcessors: PowerSearchOperatorProcessorConfig, treatUndefinedValuesAsMatchingFiltering: boolean = false, ) { return function dataTableFilter(item: any) { - if (!searchExpression || !searchExpression.length) { + if (!searchExpression.length) { return true; } return searchExpression.every((searchTerm) => { From 7c972a982a312627bc516d05eea8436c6d260866 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 16 Nov 2023 03:14:11 -0800 Subject: [PATCH 072/112] Fix plugin backward compatibility Reviewed By: lblasa Differential Revision: D51391318 fbshipit-source-id: f4cd42077bbd358bda30860659e845fa0ee8e845 --- desktop/flipper-plugin/src/index.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/desktop/flipper-plugin/src/index.tsx b/desktop/flipper-plugin/src/index.tsx index 4935c88f8..2627eddc4 100644 --- a/desktop/flipper-plugin/src/index.tsx +++ b/desktop/flipper-plugin/src/index.tsx @@ -39,6 +39,7 @@ export {DetailSidebar} from './ui/DetailSidebar'; export {Toolbar} from './ui/Toolbar'; export {MasterDetailWithPowerSearch as MasterDetail} from './ui/MasterDetailWithPowerSearch'; +export {MasterDetailWithPowerSearch as _MasterDetailWithPowerSearch} from './ui/MasterDetailWithPowerSearch'; export {MasterDetail as MasterDetailLegacy} from './ui/MasterDetail'; export {CodeBlock} from './ui/CodeBlock'; @@ -62,11 +63,16 @@ export { DataTable, DataTableColumn, } from './ui/data-table/DataTableWithPowerSearch'; +export { + DataTable as _DataTableWithPowerSearch, + DataTableColumn as _DataTableColumnWithPowerSearch, +} from './ui/data-table/DataTableWithPowerSearch'; export { DataTable as DataTableLegacy, DataTableColumn as DataTableColumnLegacy, } from './ui/data-table/DataTable'; export {DataTableManager} from './ui/data-table/DataTableWithPowerSearchManager'; +export {DataTableManager as _DataTableWithPowerSearchManager} from './ui/data-table/DataTableWithPowerSearchManager'; export {DataTableManager as DataTableManagerLegacy} from './ui/data-table/DataTableManager'; export {dataTablePowerSearchOperators} from './ui/data-table/DataTableDefaultPowerSearchOperators'; export {DataList} from './ui/DataList'; From d515342526f406a15c081653657a44205be0ae52 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 16 Nov 2023 04:18:41 -0800 Subject: [PATCH 073/112] Add FlipperServerDisconnectedError to prevent excessive error logging Reviewed By: passy Differential Revision: D51393196 fbshipit-source-id: f49857b397a3fb629ad44f89a4c59b12ba2f67c4 --- desktop/flipper-common/src/index.tsx | 1 + desktop/flipper-common/src/utils/errors.tsx | 6 ++++++ desktop/flipper-frontend-core/src/plugins.tsx | 11 ++++++++++- .../flipper-server-client/src/FlipperServerClient.tsx | 3 ++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/desktop/flipper-common/src/index.tsx b/desktop/flipper-common/src/index.tsx index 7925006b6..b89847fee 100644 --- a/desktop/flipper-common/src/index.tsx +++ b/desktop/flipper-common/src/index.tsx @@ -54,6 +54,7 @@ export { isConnectivityOrAuthError, isError, isAuthError, + FlipperServerDisconnectedError, getStringFromErrorLike, getErrorFromErrorLike, deserializeRemoteError, diff --git a/desktop/flipper-common/src/utils/errors.tsx b/desktop/flipper-common/src/utils/errors.tsx index 781c24113..76b1a1a6b 100644 --- a/desktop/flipper-common/src/utils/errors.tsx +++ b/desktop/flipper-common/src/utils/errors.tsx @@ -96,6 +96,12 @@ export class NoLongerConnectedToClientError extends Error { name: 'NoLongerConnectedToClientError'; } +export class FlipperServerDisconnectedError extends Error { + constructor(public readonly reason: 'ws-close') { + super(`Flipper Server disconnected. Reason: ${reason}`); + } +} + declare global { interface Error { interaction?: unknown; diff --git a/desktop/flipper-frontend-core/src/plugins.tsx b/desktop/flipper-frontend-core/src/plugins.tsx index 2375080b7..bd5573a2b 100644 --- a/desktop/flipper-frontend-core/src/plugins.tsx +++ b/desktop/flipper-frontend-core/src/plugins.tsx @@ -11,6 +11,7 @@ import { InstalledPluginDetails, tryCatchReportPluginFailuresAsync, notNull, + FlipperServerDisconnectedError, } from 'flipper-common'; import {ActivatablePluginDetails, ConcretePluginDetails} from 'flipper-common'; import {reportUsage} from 'flipper-common'; @@ -229,7 +230,15 @@ export const createRequirePluginFunction = return pluginDefinition; } catch (e) { failedPlugins.push([pluginDetails, e.message]); - console.error(`Plugin ${pluginDetails.id} failed to load`, e); + + let severity: 'error' | 'warn' = 'error'; + if ( + e instanceof FlipperServerDisconnectedError && + e.reason === 'ws-close' + ) { + severity = 'warn'; + } + console[severity](`Plugin ${pluginDetails.id} failed to load`, e); return null; } }; diff --git a/desktop/flipper-server-client/src/FlipperServerClient.tsx b/desktop/flipper-server-client/src/FlipperServerClient.tsx index 29985f65e..2f0b7323b 100644 --- a/desktop/flipper-server-client/src/FlipperServerClient.tsx +++ b/desktop/flipper-server-client/src/FlipperServerClient.tsx @@ -14,6 +14,7 @@ import { FlipperServerCommands, FlipperServerExecOptions, ServerWebSocketMessage, + FlipperServerDisconnectedError, } from 'flipper-common'; import ReconnectingWebSocket from 'reconnecting-websocket'; @@ -90,7 +91,7 @@ export function createFlipperServerWithSocket( onStateChange(FlipperServerState.DISCONNECTED); pendingRequests.forEach((r) => - r.reject(new Error('flipper-server disconnected')), + r.reject(new FlipperServerDisconnectedError('ws-close')), ); pendingRequests.clear(); }); From 7fa24636cacf693cca57eb8df38fae9096ebd05e Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 Date: Thu, 16 Nov 2023 05:36:45 -0800 Subject: [PATCH 074/112] Flipper Release: v0.239.0 Summary: Releasing version 0.239.0 Reviewed By: aigoncharov Differential Revision: D51393961 fbshipit-source-id: ce286c17042b59b227396bbd2c22f0d0257edcd2 --- desktop/package.json | 2 +- desktop/plugins/public/layout/docs/setup.mdx | 2 +- desktop/plugins/public/leak_canary/docs/setup.mdx | 2 +- desktop/plugins/public/network/docs/setup.mdx | 2 +- desktop/static/CHANGELOG.md | 5 +++++ docs/getting-started/android-native.mdx | 4 ++-- docs/getting-started/react-native-ios.mdx | 2 +- docs/getting-started/react-native.mdx | 4 ++-- gradle.properties | 2 +- js/js-flipper/package.json | 2 +- react-native/react-native-flipper/package.json | 2 +- 11 files changed, 17 insertions(+), 12 deletions(-) diff --git a/desktop/package.json b/desktop/package.json index 866c86b29..c7a192a76 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -170,7 +170,7 @@ "npm": "use yarn instead", "yarn": "^1.16" }, - "version": "0.238.0", + "version": "0.239.0", "workspaces": { "packages": [ "scripts", diff --git a/desktop/plugins/public/layout/docs/setup.mdx b/desktop/plugins/public/layout/docs/setup.mdx index d86a56f62..754d0f77b 100644 --- a/desktop/plugins/public/layout/docs/setup.mdx +++ b/desktop/plugins/public/layout/docs/setup.mdx @@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.238.0' + debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.239.0' debugImplementation 'com.facebook.litho:litho-annotations:0.19.0' // ... } diff --git a/desktop/plugins/public/leak_canary/docs/setup.mdx b/desktop/plugins/public/leak_canary/docs/setup.mdx index c3d990da9..824c5bd3d 100644 --- a/desktop/plugins/public/leak_canary/docs/setup.mdx +++ b/desktop/plugins/public/leak_canary/docs/setup.mdx @@ -8,7 +8,7 @@ To setup the LeakCan ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.238.0' + debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.239.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' } ``` diff --git a/desktop/plugins/public/network/docs/setup.mdx b/desktop/plugins/public/network/docs/setup.mdx index d664cc3f4..68e1262f8 100644 --- a/desktop/plugins/public/network/docs/setup.mdx +++ b/desktop/plugins/public/network/docs/setup.mdx @@ -12,7 +12,7 @@ The network plugin is shipped as a separate Maven artifact, as follows: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.238.0' + debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.239.0' } ``` diff --git a/desktop/static/CHANGELOG.md b/desktop/static/CHANGELOG.md index 544d45aca..92e452e07 100644 --- a/desktop/static/CHANGELOG.md +++ b/desktop/static/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.239.0 (16/11/2023) + + * [D51346366](https://github.com/facebook/flipper/search?q=D51346366&type=Commits) - UIDebugger fix issue with scrollbars sometimes appearing in sidebar + + # 0.238.0 (14/11/2023) * [D51199644](https://github.com/facebook/flipper/search?q=D51199644&type=Commits) - [Logs] Improve power search config to populate dropdown for level, PID & Tag diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index f363c20d4..3222a23a6 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -24,10 +24,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.238.0' + debugImplementation 'com.facebook.flipper:flipper:0.239.0' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.238.0' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.239.0' } ``` diff --git a/docs/getting-started/react-native-ios.mdx b/docs/getting-started/react-native-ios.mdx index 5763e53e9..837a25712 100644 --- a/docs/getting-started/react-native-ios.mdx +++ b/docs/getting-started/react-native-ios.mdx @@ -51,7 +51,7 @@ Add all of the code below to your `ios/Podfile`: platform :ios, '9.0' def flipper_pods() - flipperkit_version = '0.238.0' # should match the version of your Flipper client app + flipperkit_version = '0.239.0' # should match the version of your Flipper client app pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug' diff --git a/docs/getting-started/react-native.mdx b/docs/getting-started/react-native.mdx index 206949a52..2d6fdd86b 100644 --- a/docs/getting-started/react-native.mdx +++ b/docs/getting-started/react-native.mdx @@ -34,7 +34,7 @@ Latest version of Flipper requires react-native 0.69+! If you use react-native < Android: -1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.238.0`. +1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.239.0`. 2. Run `./gradlew clean` in the `android` directory. iOS: @@ -44,7 +44,7 @@ react-native version => 0.69.0 2. Run `pod install --repo-update` in the `ios` directory. react-native version < 0.69.0 -1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.238.0' })`. +1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.239.0' })`. 2. Run `pod install --repo-update` in the `ios` directory. ## Manual Setup diff --git a/gradle.properties b/gradle.properties index 33d6ce8cf..dd716c754 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.238.1-SNAPSHOT +VERSION_NAME=0.239.0 GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper diff --git a/js/js-flipper/package.json b/js/js-flipper/package.json index 3d53872ff..0c2184ec7 100644 --- a/js/js-flipper/package.json +++ b/js/js-flipper/package.json @@ -1,7 +1,7 @@ { "name": "js-flipper", "title": "JS Flipper Bindings for Web-Socket based clients", - "version": "0.238.0", + "version": "0.239.0", "main": "lib/index.js", "browser": { "os": false diff --git a/react-native/react-native-flipper/package.json b/react-native/react-native-flipper/package.json index 56318b16f..b072b23ab 100644 --- a/react-native/react-native-flipper/package.json +++ b/react-native/react-native-flipper/package.json @@ -1,7 +1,7 @@ { "name": "react-native-flipper", "title": "React Native Flipper Bindings", - "version": "0.238.0", + "version": "0.239.0", "description": "Flipper bindings for React Native", "main": "index.js", "types": "index.d.ts", From ace3626938b5c7ff8754bf42273677147226d77a Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 Date: Thu, 16 Nov 2023 05:36:45 -0800 Subject: [PATCH 075/112] Flipper Snapshot Bump: v0.239.1-SNAPSHOT Summary: Releasing snapshot version 0.239.1-SNAPSHOT Reviewed By: aigoncharov Differential Revision: D51393963 fbshipit-source-id: 96a5ffd64465c0391694707fb8d5d14992807ea1 --- docs/getting-started/android-native.mdx | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 3222a23a6..f18c458ec 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -124,10 +124,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.238.1-SNAPSHOT' + debugImplementation 'com.facebook.flipper:flipper:0.239.1-SNAPSHOT' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.238.1-SNAPSHOT' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.239.1-SNAPSHOT' } ``` diff --git a/gradle.properties b/gradle.properties index dd716c754..807a0bd4a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.239.0 +VERSION_NAME=0.239.1-SNAPSHOT GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper From e225d9e1c3753c74d39555925ec312844dfcf76b Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Fri, 17 Nov 2023 03:34:58 -0800 Subject: [PATCH 076/112] Bring back RatingButton Summary: Removed previously during redesign by mistake Reviewed By: antonk52 Differential Revision: D51347837 fbshipit-source-id: d634ad4c8983271a3936f458cabb63f006a4bb0a --- .../src/sandy-chrome/Navbar.tsx | 2 + .../src/sandy-chrome/RatingButton.tsx | 349 ++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 desktop/flipper-ui-core/src/sandy-chrome/RatingButton.tsx diff --git a/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx b/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx index 014f9a941..94daf13f2 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx @@ -72,6 +72,7 @@ import {TroubleshootingGuide} from './appinspect/fb-stubs/TroubleshootingGuide'; import {FlipperDevTools} from '../chrome/FlipperDevTools'; import {TroubleshootingHub} from '../chrome/TroubleshootingHub'; import {Notification} from './notification/Notification'; +import {SandyRatingButton} from './RatingButton'; export const Navbar = withTrackingScope(function Navbar() { return ( @@ -104,6 +105,7 @@ export const Navbar = withTrackingScope(function Navbar() { + {getRenderHostInstance().serverConfig.environmentInfo diff --git a/desktop/flipper-ui-core/src/sandy-chrome/RatingButton.tsx b/desktop/flipper-ui-core/src/sandy-chrome/RatingButton.tsx new file mode 100644 index 000000000..b15a8a777 --- /dev/null +++ b/desktop/flipper-ui-core/src/sandy-chrome/RatingButton.tsx @@ -0,0 +1,349 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React, { + Component, + ReactElement, + useCallback, + useEffect, + useState, +} from 'react'; +import {styled, Input, Link, FlexColumn, FlexRow} from '../ui'; +import * as UserFeedback from '../fb-stubs/UserFeedback'; +import {FeedbackPrompt} from '../fb-stubs/UserFeedback'; +import {StarOutlined} from '@ant-design/icons'; +import {Button, Checkbox, Popover, Rate} from 'antd'; +import {currentUser} from '../fb-stubs/user'; +import {theme, useValue} from 'flipper-plugin'; +import {reportPlatformFailures} from 'flipper-common'; +import {getRenderHostInstance} from 'flipper-frontend-core'; +import {NavbarButton} from './Navbar'; + +type NextAction = 'select-rating' | 'leave-comment' | 'finished'; + +class PredefinedComment extends Component<{ + comment: string; + selected: boolean; + onClick: (_: unknown) => unknown; +}> { + static Container = styled.div<{selected: boolean}>((props) => { + return { + border: '1px solid #f2f3f5', + cursor: 'pointer', + borderRadius: 24, + backgroundColor: props.selected ? '#ecf3ff' : '#f2f3f5', + marginBottom: 4, + marginRight: 4, + padding: '4px 8px', + color: props.selected ? 'rgb(56, 88, 152)' : undefined, + borderColor: props.selected ? '#3578e5' : undefined, + ':hover': { + borderColor: '#3578e5', + }, + }; + }); + render() { + return ( + + {this.props.comment} + + ); + } +} + +const Row = styled(FlexRow)({ + marginTop: 5, + marginBottom: 5, + justifyContent: 'center', + textAlign: 'center', + color: '#9a9a9a', + flexWrap: 'wrap', +}); + +const DismissRow = styled(Row)({ + marginBottom: 0, + marginTop: 10, +}); + +const DismissButton = styled.span({ + '&:hover': { + textDecoration: 'underline', + cursor: 'pointer', + }, +}); + +const Spacer = styled(FlexColumn)({ + flexGrow: 1, +}); + +function dismissRow(dismiss: () => void) { + return ( + + + Dismiss + + + ); +} + +type FeedbackComponentState = { + rating: number | null; + hoveredRating: number; + allowUserInfoSharing: boolean; + nextAction: NextAction; + predefinedComments: {[key: string]: boolean}; + comment: string; +}; + +class FeedbackComponent extends Component< + { + submitRating: (rating: number) => void; + submitComment: ( + rating: number, + comment: string, + selectedPredefinedComments: Array, + allowUserInfoSharing: boolean, + ) => void; + close: () => void; + dismiss: () => void; + promptData: FeedbackPrompt; + }, + FeedbackComponentState +> { + state: FeedbackComponentState = { + rating: null, + hoveredRating: 0, + allowUserInfoSharing: true, + nextAction: 'select-rating' as NextAction, + predefinedComments: this.props.promptData.predefinedComments.reduce( + (acc, cv) => ({...acc, [cv]: false}), + {}, + ), + comment: '', + }; + onSubmitRating(newRating: number) { + const nextAction = newRating <= 2 ? 'leave-comment' : 'finished'; + this.setState({rating: newRating, nextAction: nextAction}); + this.props.submitRating(newRating); + if (nextAction === 'finished') { + setTimeout(this.props.close, 5000); + } + } + onCommentSubmitted(comment: string) { + this.setState({nextAction: 'finished'}); + const selectedPredefinedComments: Array = Object.entries( + this.state.predefinedComments, + ) + .map((x) => ({comment: x[0], enabled: x[1]})) + .filter((x) => x.enabled) + .map((x) => x.comment); + const currentRating = this.state.rating; + if (currentRating) { + this.props.submitComment( + currentRating, + comment, + selectedPredefinedComments, + this.state.allowUserInfoSharing, + ); + } else { + console.error('Illegal state: Submitting comment with no rating set.'); + } + setTimeout(this.props.close, 1000); + } + onAllowUserSharingChanged(allowed: boolean) { + this.setState({allowUserInfoSharing: allowed}); + } + render() { + let body: Array; + switch (this.state.nextAction) { + case 'select-rating': + body = [ + {this.props.promptData.bodyText}, + + this.onSubmitRating(newRating)} /> + , + dismissRow(this.props.dismiss), + ]; + break; + case 'leave-comment': + const predefinedComments = Object.entries( + this.state.predefinedComments, + ).map((c: [string, unknown], idx: number) => ( + + this.setState({ + predefinedComments: { + ...this.state.predefinedComments, + [c[0]]: !c[1], + }, + }) + } + /> + )); + body = [ + {predefinedComments}, + + this.setState({comment: e.target.value})} + onKeyDown={(e) => + e.key == 'Enter' && this.onCommentSubmitted(this.state.comment) + } + autoFocus + /> + , + + this.onAllowUserSharingChanged(e.target.checked)} + /> + {'Tool owner can contact me '} + , + + + , + dismissRow(this.props.dismiss), + ]; + break; + case 'finished': + body = [ + + Thanks for the feedback! You can now help + + prioritize bugs and features for Flipper in Papercuts + + , + dismissRow(this.props.dismiss), + ]; + break; + default: { + console.error('Illegal state: nextAction: ' + this.state.nextAction); + return null; + } + } + return ( + + + {this.state.nextAction === 'finished' + ? this.props.promptData.postSubmitHeading + : this.props.promptData.preSubmitHeading} + + {body} + + ); + } +} + +export function SandyRatingButton() { + const [promptData, setPromptData] = + useState(null); + const [isShown, setIsShown] = useState(false); + const [hasTriggered, setHasTriggered] = useState(false); + const sessionId = getRenderHostInstance().serverConfig.sessionId; + const loggedIn = useValue(currentUser()); + + const triggerPopover = useCallback(() => { + if (!hasTriggered) { + setIsShown(true); + setHasTriggered(true); + } + }, [hasTriggered]); + + useEffect(() => { + if ( + getRenderHostInstance().GK('flipper_enable_star_ratiings') && + !hasTriggered && + loggedIn + ) { + reportPlatformFailures( + UserFeedback.getPrompt().then((prompt) => { + setPromptData(prompt); + setTimeout(triggerPopover, 30000); + }), + 'RatingButton:getPrompt', + ).catch((e) => { + console.warn('Failed to load ratings prompt:', e); + }); + } + }, [triggerPopover, hasTriggered, loggedIn]); + + const onClick = () => { + const willBeShown = !isShown; + setIsShown(willBeShown); + setHasTriggered(true); + if (!willBeShown) { + UserFeedback.dismiss(sessionId); + } + }; + + const submitRating = (rating: number) => { + UserFeedback.submitRating(rating, sessionId); + }; + + const submitComment = ( + rating: number, + comment: string, + selectedPredefinedComments: Array, + allowUserInfoSharing: boolean, + ) => { + UserFeedback.submitComment( + rating, + comment, + selectedPredefinedComments, + allowUserInfoSharing, + sessionId, + ); + }; + + if (!promptData) { + return null; + } + if (!promptData.shouldPopup || (hasTriggered && !isShown)) { + return null; + } + return ( + { + setIsShown(false); + }} + dismiss={onClick} + promptData={promptData} + /> + } + placement="right" + trigger="click"> + + + ); +} From dd9279bf7abb1c334457d45b3ea64b28cfeb7dd1 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Fri, 17 Nov 2023 04:18:41 -0800 Subject: [PATCH 077/112] Bloks debugger state deeplink from UIDebugger Summary: This adds a deeplink into bloks debugger from uidebugger for state events changelog: UIDebugger - show bloks state & deeplink to bloks debugger Reviewed By: lblasa Differential Revision: D51349212 fbshipit-source-id: 6f7ca826228ce11a01fe5eb197f6ce092d2757a9 --- .../public/ui-debugger/ClientTypes.tsx | 8 ++++ .../sidebarV2/AttributesInspector.tsx | 38 +++++++++++++++++++ desktop/plugins/public/ui-debugger/index.tsx | 1 + 3 files changed, 47 insertions(+) diff --git a/desktop/plugins/public/ui-debugger/ClientTypes.tsx b/desktop/plugins/public/ui-debugger/ClientTypes.tsx index d58e71514..85a11890b 100644 --- a/desktop/plugins/public/ui-debugger/ClientTypes.tsx +++ b/desktop/plugins/public/ui-debugger/ClientTypes.tsx @@ -228,6 +228,7 @@ export type Inspectable = | InspectableSize | InspectableBounds | InspectableSpaceBox + | InspectablePluginDeepLink | InspectableUnknown; export type InspectableText = { @@ -285,6 +286,13 @@ export type InspectableObject = { fields: Record; }; +export type InspectablePluginDeepLink = { + type: 'pluginDeeplink'; + label?: string; + pluginId: string; + deeplinkPayload: unknown; +}; + export type InspectableArray = { type: 'array'; items: Inspectable[]; diff --git a/desktop/plugins/public/ui-debugger/components/sidebarV2/AttributesInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebarV2/AttributesInspector.tsx index 73684e3db..2169bf2ba 100644 --- a/desktop/plugins/public/ui-debugger/components/sidebarV2/AttributesInspector.tsx +++ b/desktop/plugins/public/ui-debugger/components/sidebarV2/AttributesInspector.tsx @@ -16,6 +16,7 @@ import { Layout, styled, useLocalStorageState, + usePlugin, } from 'flipper-plugin'; import React, {useState} from 'react'; import { @@ -33,6 +34,9 @@ import {any} from 'lodash/fp'; import {InspectableColor} from '../../ClientTypes'; import {transformAny} from '../../utils/dataTransform'; import {SearchOutlined} from '@ant-design/icons'; +import {plugin} from '../../index'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import {Glyph} from 'flipper'; type ModalData = { data: unknown; @@ -433,6 +437,7 @@ function AttributeValue({ name: string; inspectable: Inspectable; }) { + const instance = usePlugin(plugin); switch (inspectable.type) { case 'boolean': return ( @@ -551,6 +556,39 @@ function AttributeValue({
); + case 'pluginDeeplink': + return ( + + ); } return null; } diff --git a/desktop/plugins/public/ui-debugger/index.tsx b/desktop/plugins/public/ui-debugger/index.tsx index a7cd12a87..10dcc97f6 100644 --- a/desktop/plugins/public/ui-debugger/index.tsx +++ b/desktop/plugins/public/ui-debugger/index.tsx @@ -297,6 +297,7 @@ export function plugin(client: PluginClient) { metadata, perfEvents, os: client.device.os, + client, }; } From 288e8e2d48d09bdc508e46fc00d1fa0e9392c2e5 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Fri, 17 Nov 2023 04:18:41 -0800 Subject: [PATCH 078/112] Make search expand relevant nodes Summary: Previously we were searching the tree nodes which did not include all nodes since if a not was not expanded it isnt in the tree node list, so now we expand first then find the first tree node that matches changelog: UIDebugger search now expands matching elements Reviewed By: lblasa Differential Revision: D51349213 fbshipit-source-id: 4eef436781ed6ec23187d1aec7633ee4b959d3fa --- .../ui-debugger/components/tree/Tree.tsx | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx b/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx index 7d9160c92..fac4d7524 100644 --- a/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx +++ b/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx @@ -7,7 +7,7 @@ * @format */ -import {Id, ClientNode, MetadataId, Metadata} from '../../ClientTypes'; +import {Id, ClientNode, NodeMap, MetadataId, Metadata} from '../../ClientTypes'; import {Color, OnSelectNode} from '../../DesktopTypes'; import React, { CSSProperties, @@ -60,7 +60,7 @@ export function Tree2({ additionalHeightOffset, }: { additionalHeightOffset: number; - nodes: Map; + nodes: NodeMap; metadata: Map; rootId: Id; }) { @@ -125,12 +125,21 @@ export function Tree2({ return; } prevSearchTerm.current = searchTerm; - const matchingIndexes = findSearchMatchingIndexes(treeNodes, searchTerm); + const matchingNodesIds = findMatchingNodes(nodes, searchTerm); - if (matchingIndexes.length > 0) { - rowVirtualizer.scrollToIndex(matchingIndexes[0], {align: 'start'}); + matchingNodesIds.forEach((id) => { + instance.uiActions.ensureAncestorsExpanded(id); + }); + + if (matchingNodesIds.length > 0) { + const firstTreeNode = treeNodes.find(searchPredicate(searchTerm)); + + const idx = firstTreeNode?.idx; + if (idx != null) { + rowVirtualizer.scrollToIndex(idx, {align: 'start'}); + } } - }, [rowVirtualizer, searchTerm, treeNodes]); + }, [instance.uiActions, nodes, rowVirtualizer, searchTerm, treeNodes]); useKeyboardControls( treeNodes, @@ -638,22 +647,21 @@ const NodeIconImage = styled.img({...nodeiconStyle}); const renderDepthOffset = 12; -//due to virtualisation the out of the box dom based scrolling doesnt work -function findSearchMatchingIndexes( - treeNodes: TreeNode[], - searchTerm: string, -): number[] { +function findMatchingNodes(nodes: NodeMap, searchTerm: string): Id[] { if (!searchTerm) { return []; } - return treeNodes - .map((value, index) => [value, index] as [TreeNode, number]) - .filter( - ([value, _]) => - value.name.toLowerCase().includes(searchTerm) || - Object.values(value.inlineAttributes).find((inlineAttr) => - inlineAttr.toLocaleLowerCase().includes(searchTerm), - ), - ) - .map(([_, index]) => index); + return [...nodes.values()] + .filter(searchPredicate(searchTerm)) + .map((node) => node.id); +} + +function searchPredicate( + searchTerm: string, +): (node: ClientNode) => string | true | undefined { + return (node: ClientNode): string | true | undefined => + node.name.toLowerCase().includes(searchTerm) || + Object.values(node.inlineAttributes).find((inlineAttr) => + inlineAttr.toLocaleLowerCase().includes(searchTerm), + ); } From bf67b19c4a1f902e6c238882747396ebca22a033 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Fri, 17 Nov 2023 04:18:41 -0800 Subject: [PATCH 079/112] Allow searching on inline attribute keys Summary: useful for bloks debugging Reviewed By: lblasa Differential Revision: D51349211 fbshipit-source-id: a11eca19bdecf989ceb6a95e9a3cb504020c7467 --- .../plugins/public/ui-debugger/components/tree/Tree.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx b/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx index fac4d7524..88a548a5f 100644 --- a/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx +++ b/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx @@ -497,7 +497,9 @@ function InlineAttributes({attributes}: {attributes: Record}) { <> {Object.entries(attributes ?? {}).map(([key, value]) => ( - {key} + + {highlightManager.render(key)} + ={highlightManager.render(value)} ))} @@ -661,6 +663,9 @@ function searchPredicate( ): (node: ClientNode) => string | true | undefined { return (node: ClientNode): string | true | undefined => node.name.toLowerCase().includes(searchTerm) || + Object.keys(node.inlineAttributes).find((inlineAttr) => + inlineAttr.toLocaleLowerCase().includes(searchTerm), + ) || Object.values(node.inlineAttributes).find((inlineAttr) => inlineAttr.toLocaleLowerCase().includes(searchTerm), ); From b9fa86e77faf5093ad02f4d8500619f2fc8ef834 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Fri, 17 Nov 2023 05:31:25 -0800 Subject: [PATCH 080/112] Fix filter from selection Reviewed By: LukeDefeo Differential Revision: D51425090 fbshipit-source-id: 53dda8f65d2e8d17468903c20e88d45038e65be1 --- .../DataTableWithPowerSearchManager.tsx | 34 ++++++++++++++++++- .../PowerSearchTableContextMenu.tsx | 6 ++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx index 60a03e755..e7a3335b4 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx @@ -14,7 +14,10 @@ import {DataSourceVirtualizer} from '../../data-source/index'; import produce, {castDraft, immerable, original} from 'immer'; import {DataSource, getFlipperLib, _DataSourceView} from 'flipper-plugin-core'; import {SearchExpressionTerm} from '../PowerSearch'; -import {PowerSearchOperatorProcessorConfig} from './DataTableDefaultPowerSearchOperators'; +import { + dataTablePowerSearchOperators, + PowerSearchOperatorProcessorConfig, +} from './DataTableDefaultPowerSearchOperators'; import {DataTableManager as DataTableManagerLegacy} from './DataTableManager'; export type OnColumnResize = (id: string, size: number | Percentage) => void; @@ -87,6 +90,7 @@ type DataManagerActions = } > | Action<'clearSelection', {}> + | Action<'setSearchExpressionFromSelection', {column: DataTableColumn}> | Action<'setFilterExceptions', {exceptions: string[] | undefined}> | Action<'appliedInitialScroll'> | Action<'toggleAutoScroll'> @@ -173,6 +177,34 @@ export const dataTableManagerReducer = produce< draft.filterExceptions = undefined; break; } + case 'setSearchExpressionFromSelection': { + getFlipperLib().logger.track( + 'usage', + 'data-table:filter:power-search-from-selection', + ); + draft.filterExceptions = undefined; + const items = getSelectedItems( + config.dataView as _DataSourceView, + draft.selection, + ); + + const searchExpressionFromSelection: SearchExpressionTerm[] = [ + { + field: { + key: action.column.key, + label: action.column.title ?? action.column.key, + }, + operator: dataTablePowerSearchOperators.enum_set_is_any_of({}), + searchValue: items.map((item) => + getValueAtPath(item, action.column.key), + ), + }, + ]; + + draft.searchExpression = searchExpressionFromSelection; + draft.filterExceptions = undefined; + break; + } case 'selectItem': { const {nextIndex, addToSelection, allowUnselect} = action; draft.selection = castDraft( diff --git a/desktop/flipper-plugin/src/ui/data-table/PowerSearchTableContextMenu.tsx b/desktop/flipper-plugin/src/ui/data-table/PowerSearchTableContextMenu.tsx index 066d2365e..f904b2775 100644 --- a/desktop/flipper-plugin/src/ui/data-table/PowerSearchTableContextMenu.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/PowerSearchTableContextMenu.tsx @@ -15,7 +15,7 @@ import { getSelectedItems, getValueAtPath, Selection, -} from './DataTableManager'; +} from './DataTableWithPowerSearchManager'; import React from 'react'; import { _tryGetFlipperLibImplementation, @@ -65,8 +65,8 @@ export function tableContextMenuFactory( key={column.key ?? idx} onClick={() => { dispatch({ - type: 'setColumnFilterFromSelection', - column: column.key, + type: 'setSearchExpressionFromSelection', + column, }); }}> {friendlyColumnTitle(column)} From e3038a33933d862774ef90ff87845b4dbf2e48db Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Fri, 17 Nov 2023 05:51:47 -0800 Subject: [PATCH 081/112] MacOS app to be built on each release build Summary: So far we have used a 'template' approach, in which the release build script will copy and use to create the final deliverable. The template itself was updated locally by running: cd ~/fbsource/xplat/sonar/facebook/flipper-server yarn start This would: - Build the MacOS app for all supported architectures: x64 and arm64 - Update the template found on the static directory After the update, we would just commit the changes to the repo. ## About this change Instead, build the MacOS app when the release script is used. This is leaves way less margin for error as we have removed all the manual steps that had to be done as listed above. Reviewed By: antonk52 Differential Revision: D51397661 fbshipit-source-id: 2234c9996fa98f32db244764acf3e35dc9a388c9 --- desktop/scripts/build-flipper-server-release.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/desktop/scripts/build-flipper-server-release.tsx b/desktop/scripts/build-flipper-server-release.tsx index bd58b1f84..fa0cf6d34 100644 --- a/desktop/scripts/build-flipper-server-release.tsx +++ b/desktop/scripts/build-flipper-server-release.tsx @@ -746,12 +746,28 @@ async function setUpMacBundle( let appTemplate = path.join(staticDir, 'flipper-server-app-template'); if (isFB) { + const {BuildArchitecture, buildFlipperServer} = await import( + // @ts-ignore only used inside Meta + './fb/build-flipper-server-macos' + ); + + const architecture = + platform === BuildPlatform.MAC_AARCH64 + ? BuildArchitecture.MAC_AARCH64 + : BuildArchitecture.MAC_X64; + const outputPath = await buildFlipperServer(architecture); + console.log( + `⚙️ Successfully built platform: ${platform}, output: ${outputPath}`, + ); + appTemplate = path.join( staticDir, 'facebook', 'flipper-server-app-template', platform, ); + + await fs.copy(outputPath, path.join(appTemplate, 'Flipper.app')); console.info('⚙️ Using internal template from: ' + appTemplate); } From ec9bc8a29fb06d3864caf5d1880d49dd1c1884cb Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Fri, 17 Nov 2023 05:51:47 -0800 Subject: [PATCH 082/112] Avoid template copy Summary: As mentioned on the previous diff, we no longer use a pre-built template when creating a release build. This change moves the built binary directly into place instead of updating the existing template after which it was moved to its final destination. Reviewed By: antonk52 Differential Revision: D51424944 fbshipit-source-id: 284fb53bb5f00f92b3ca9db3b28cfd1e4dacfa19 --- .../scripts/build-flipper-server-release.tsx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/desktop/scripts/build-flipper-server-release.tsx b/desktop/scripts/build-flipper-server-release.tsx index fa0cf6d34..a6d2f07b7 100644 --- a/desktop/scripts/build-flipper-server-release.tsx +++ b/desktop/scripts/build-flipper-server-release.tsx @@ -744,7 +744,6 @@ async function setUpMacBundle( ): Promise<{nodePath: string; resourcesPath: string}> { console.log(`⚙️ Creating Mac bundle in ${outputDir}`); - let appTemplate = path.join(staticDir, 'flipper-server-app-template'); if (isFB) { const {BuildArchitecture, buildFlipperServer} = await import( // @ts-ignore only used inside Meta @@ -760,19 +759,15 @@ async function setUpMacBundle( `⚙️ Successfully built platform: ${platform}, output: ${outputPath}`, ); - appTemplate = path.join( - staticDir, - 'facebook', - 'flipper-server-app-template', - platform, - ); + const appPath = path.join(outputDir, 'Flipper.app'); + await fs.emptyDir(appPath); - await fs.copy(outputPath, path.join(appTemplate, 'Flipper.app')); - console.info('⚙️ Using internal template from: ' + appTemplate); + await fs.copy(outputPath, appPath); + } else { + const template = path.join(staticDir, 'flipper-server-app-template'); + await fs.copy(template, outputDir); } - await fs.copy(appTemplate, outputDir); - function replacePropertyValue( obj: any, targetValue: string, From 387d546e7713cc2ab5f267b909abefbd3c108c57 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Fri, 17 Nov 2023 10:06:47 -0800 Subject: [PATCH 083/112] inconsistent right padding Summary: There is too much padding on the right wether right sidebar open or not, this fixes it Reviewed By: lblasa Differential Revision: D51428892 fbshipit-source-id: 6fa9b0c06ddf32c32d162a7fd2bd765e8996c00a --- desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx b/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx index 156ebfe33..6f015e3f6 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx @@ -224,7 +224,6 @@ const outOfContentsContainer = ( const MainContainer = styled(Layout.Container)({ background: theme.backgroundWash, - padding: `0 ${theme.space.large}px ${theme.space.large}px 0`, overflow: 'hidden', }); From fb37f27ff52997ef5f7128f7679821a38f2df6fd Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Mon, 20 Nov 2023 07:14:09 -0800 Subject: [PATCH 084/112] Suport device-open-app command Summary: It is an escape hatch for testing iOS Sample app if `navigate('/')` cannot be made to work easily (D51394323) Reviewed By: lblasa Differential Revision: D51466697 fbshipit-source-id: fd6c2f37504807b4b634b85cb10e837523dadba8 --- desktop/flipper-common/src/server-types.tsx | 1 + desktop/flipper-server-core/src/FlipperServerImpl.tsx | 3 +++ .../flipper-server-core/src/devices/ServerDevice.tsx | 4 ++++ .../flipper-server-core/src/devices/ios/IOSBridge.tsx | 10 ++++++++++ .../flipper-server-core/src/devices/ios/IOSDevice.tsx | 4 ++++ 5 files changed, 22 insertions(+) diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index d5962caa1..723f4f99f 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -288,6 +288,7 @@ export type FlipperServerCommands = { serial: string, appBundlePath: string, ) => Promise; + 'device-open-app': (serial: string, name: string) => Promise; 'device-forward-port': ( serial: string, local: string, diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index 1cef26dae..ef9c437af 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -365,6 +365,9 @@ export class FlipperServerImpl implements FlipperServer { 'device-install-app': async (serial, bundlePath) => { return this.devices.get(serial)?.installApp(bundlePath); }, + 'device-open-app': async (serial, name) => { + return this.devices.get(serial)?.openApp(name); + }, 'get-server-state': async () => ({ state: this.state, error: this.stateError, diff --git a/desktop/flipper-server-core/src/devices/ServerDevice.tsx b/desktop/flipper-server-core/src/devices/ServerDevice.tsx index 5d65f8454..3366dde02 100644 --- a/desktop/flipper-server-core/src/devices/ServerDevice.tsx +++ b/desktop/flipper-server-core/src/devices/ServerDevice.tsx @@ -82,4 +82,8 @@ export abstract class ServerDevice { async installApp(_appBundlePath: string): Promise { throw new Error('installApp not implemented'); } + + async openApp(_name: string): Promise { + throw new Error('openApp not implemented'); + } } diff --git a/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx b/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx index 1a50a26a8..b6162d1fa 100644 --- a/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx +++ b/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx @@ -63,6 +63,7 @@ export interface IOSBridge { ipaPath: string, tempPath: string, ) => Promise; + openApp: (serial: string, name: string) => Promise; getInstalledApps: (serial: string) => Promise; ls: (serial: string, appBundleId: string, path: string) => Promise; pull: ( @@ -149,6 +150,11 @@ export class IDBBridge implements IOSBridge { await this._execIdb(`install ${ipaPath} --udid ${serial}`); } + async openApp(serial: string, name: string): Promise { + console.log(`Opening app via IDB ${name} ${serial}`); + await this._execIdb(`launch ${name} --udid ${serial} -f`); + } + async getActiveDevices(bootedOnly: boolean): Promise { return iosUtil .targets(this.idbPath, this.enablePhysicalDevices, bootedOnly) @@ -217,6 +223,10 @@ export class SimctlBridge implements IOSBridge { ); } + async openApp(): Promise { + throw new Error('openApp is not implemented for SimctlBridge'); + } + async installApp( serial: string, ipaPath: string, diff --git a/desktop/flipper-server-core/src/devices/ios/IOSDevice.tsx b/desktop/flipper-server-core/src/devices/ios/IOSDevice.tsx index 3e88ad3b8..5ec90b9f4 100644 --- a/desktop/flipper-server-core/src/devices/ios/IOSDevice.tsx +++ b/desktop/flipper-server-core/src/devices/ios/IOSDevice.tsx @@ -140,6 +140,10 @@ export default class IOSDevice ); } + async openApp(name: string): Promise { + return this.iOSBridge.openApp(this.serial, name); + } + async readFlipperFolderForAllApps(): Promise { console.debug('IOSDevice.readFlipperFolderForAllApps', this.info.serial); const installedApps = await this.iOSBridge.getInstalledApps( From 7e2508f0454eae35138d2d0109cbecabd5f5952a Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 21 Nov 2023 04:13:12 -0800 Subject: [PATCH 085/112] Fix server dummy timeout value Summary: It has to fit in 32 bit int Reviewed By: lblasa Differential Revision: D51478071 fbshipit-source-id: c4512e946c3e47bc235920b133d9f6c739c5ac76 --- desktop/flipper-server/src/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index bb438869b..c7c81ec42 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -341,8 +341,10 @@ process.on('unhandledRejection', (reason, promise) => { ); }); +// It has to fit in 32 bit int +const MAX_TIMEOUT = 2147483647; // Node.js process never waits for all promises to settle and exits as soon as there is not pending timers or open sockets or tasks in teh macroqueue -const runtimeTimeout = setTimeout(() => {}, Number.MAX_SAFE_INTEGER); +const runtimeTimeout = setTimeout(() => {}, MAX_TIMEOUT); // eslint-disable-next-line promise/catch-or-return start() .catch((e) => { From 877253191d3cd7498298510bdad62e7790cc5035 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 21 Nov 2023 07:56:19 -0800 Subject: [PATCH 086/112] Support multi-line wrapping for long queries / small screens Reviewed By: antonk52 Differential Revision: D51497340 fbshipit-source-id: 23bcc9a69c3e59e17ee9505c954c955f2a1c9e5f --- .../flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx | 3 ++- desktop/flipper-plugin/src/ui/PowerSearch/index.tsx | 2 +- .../src/ui/data-table/DataTableWithPowerSearch.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx index 3d67666bb..cb63d4c75 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx @@ -12,10 +12,11 @@ import {css} from '@emotion/css'; import {theme} from '../theme'; const containerStyle = css` - flex: 1 0 auto; + flex: 1 1 auto; background-color: ${theme.backgroundDefault}; display: flex; flex-direction: row; + flex-wrap: wrap; border-radius: ${theme.borderRadius}; border: 1px solid ${theme.borderColor}; transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx index 79521d914..906b74521 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx @@ -129,7 +129,7 @@ export const PowerSearch: React.FC = ({ return ( - + ( {props.actionsTop ? {props.actionsTop} : null} {props.enableSearchbar && ( - + Date: Tue, 21 Nov 2023 10:30:06 -0800 Subject: [PATCH 087/112] Recover from corrupted config.json Summary: This has caused a few tasks to be raised. We do replace broken configs with a fresh template elsewhere, but we shouldn't throw in this place since we have a fallback. Reviewed By: antonk52 Differential Revision: D51393314 fbshipit-source-id: 8c04946c5b0e74f5f0d42c9492aa381aa608fd35 --- desktop/plugin-lib/src/pluginPaths.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/desktop/plugin-lib/src/pluginPaths.tsx b/desktop/plugin-lib/src/pluginPaths.tsx index 382f14087..16d6dda80 100644 --- a/desktop/plugin-lib/src/pluginPaths.tsx +++ b/desktop/plugin-lib/src/pluginPaths.tsx @@ -30,7 +30,12 @@ export async function getPluginSourceFolders(): Promise { const pluginFolders: string[] = []; const flipperConfigPath = path.join(homedir(), '.flipper', 'config.json'); if (await fs.pathExists(flipperConfigPath)) { - const config = await fs.readJson(flipperConfigPath); + let config = {pluginPaths: []}; + try { + config = await fs.readJson(flipperConfigPath); + } catch (e) { + console.error('Failed to read local flipper config: ', e); + } if (config.pluginPaths) { pluginFolders.push(...config.pluginPaths); } From 294f39eceb4ef56ad48bedba08ee01e8cf007db5 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 21 Nov 2023 11:40:23 -0800 Subject: [PATCH 088/112] Allow freeform entries for enum terms Summary: Solves https://fb.workplace.com/groups/flippersupport/posts/1726178417862809/?comment_id=1726429847837666&reply_comment_id=1730206487460002 Reviewed By: passy Differential Revision: D51497924 fbshipit-source-id: d0737b2b82f29ff8ae654e7cad2ef1daa8244756 --- .../src/ui/PowerSearch/PowerSearchConfig.tsx | 1 + .../ui/PowerSearch/PowerSearchEnumSetTerm.tsx | 4 +- .../ui/PowerSearch/PowerSearchEnumTerm.tsx | 5 ++- .../src/ui/PowerSearch/PowerSearchTerm.tsx | 2 + .../DataTableDefaultPowerSearchOperators.tsx | 21 +++++++--- .../data-table/DataTableWithPowerSearch.tsx | 38 +++++++++++++++++-- 6 files changed, 59 insertions(+), 12 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx index 67a65ca0a..a9602b3e7 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx @@ -64,6 +64,7 @@ export type EnumOperatorConfig = { key: string; label: string; enumLabels: EnumLabels; + allowFreeform?: boolean; }; export type AbsoluteDateOperatorConfig = { diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx index 3ab8f9ec7..99a6c1fbc 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx @@ -16,6 +16,7 @@ type PowerSearchEnumSetTermProps = { onChange: (value: string[]) => void; enumLabels: EnumLabels; defaultValue?: string[]; + allowFreeform?: boolean; }; export const PowerSearchEnumSetTerm: React.FC = ({ @@ -23,6 +24,7 @@ export const PowerSearchEnumSetTerm: React.FC = ({ onChange, enumLabels, defaultValue, + allowFreeform, }) => { const options = React.useMemo(() => { return Object.entries(enumLabels).map(([key, label]) => ({ @@ -38,7 +40,7 @@ export const PowerSearchEnumSetTerm: React.FC = ({ return ( = ({ return ( ); }; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx index bef2230bf..1a9de9fe6 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx @@ -115,6 +115,7 @@ export const PowerSearchTerm: React.FC = ({ }); }} enumLabels={searchTerm.operator.enumLabels} + allowFreeform={searchTerm.operator.allowFreeform} defaultValue={searchTerm.searchValue} /> ); @@ -131,6 +132,7 @@ export const PowerSearchTerm: React.FC = ({ }); }} enumLabels={searchTerm.operator.enumLabels} + allowFreeform={searchTerm.operator.allowFreeform} defaultValue={searchTerm.searchValue} /> ); diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx index c37c24ce7..1234ab969 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx @@ -123,42 +123,51 @@ export const dataTablePowerSearchOperators = { valueType: 'FLOAT', }), // { [enumValue]: enumLabel } - enum_is: (enumLabels: EnumLabels) => ({ + enum_is: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({ label: 'is', key: 'enum_is', valueType: 'ENUM', enumLabels, + allowFreeform, }), - enum_is_nullish_or: (enumLabels: EnumLabels) => ({ + enum_is_nullish_or: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({ label: 'is nullish or', key: 'enum_is_nullish_or', valueType: 'ENUM', enumLabels, + allowFreeform, }), - enum_is_not: (enumLabels: EnumLabels) => ({ + enum_is_not: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({ label: 'is not', key: 'enum_is_not', valueType: 'ENUM', enumLabels, + allowFreeform, }), // TODO: Support logical operations (AND, OR, NOT) to combine primitive operators instead of adding new complex operators! - enum_set_is_nullish_or_any_of: (enumLabels: EnumLabels) => ({ + enum_set_is_nullish_or_any_of: ( + enumLabels: EnumLabels, + allowFreeform?: boolean, + ) => ({ label: 'is nullish or any of', key: 'enum_set_is_nullish_or_any_of', valueType: 'ENUM_SET', enumLabels, + allowFreeform, }), - enum_set_is_any_of: (enumLabels: EnumLabels) => ({ + enum_set_is_any_of: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({ label: 'is any of', key: 'enum_set_is_any_of', valueType: 'ENUM_SET', enumLabels, + allowFreeform, }), - enum_set_is_none_of: (enumLabels: EnumLabels) => ({ + enum_set_is_none_of: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({ label: 'is none of', key: 'enum_set_is_none_of', valueType: 'ENUM_SET', enumLabels, + allowFreeform, }), is_nullish: () => ({ label: 'is nullish', diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx index 03844f789..76e04ec61 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -146,8 +146,18 @@ type DataTableInput = }; type PowerSearchSimplifiedConfig = - | {type: 'enum'; enumLabels: EnumLabels; inferEnumOptionsFromData?: false} - | {type: 'enum'; enumLabels?: never; inferEnumOptionsFromData: true} + | { + type: 'enum'; + enumLabels: EnumLabels; + inferEnumOptionsFromData?: false; + allowFreeform?: boolean; + } + | { + type: 'enum'; + enumLabels?: never; + inferEnumOptionsFromData: true; + allowFreeform?: boolean; + } | {type: 'int'} | {type: 'float'} | {type: 'string'} @@ -163,6 +173,12 @@ type PowerSearchExtendedConfig = { * See https://fburl.com/code/0waicx6p */ inferEnumOptionsFromData?: boolean; + /** + * Allows freeform entries for enum column types. Makes most sense together with `inferEnumOptionsFromData`. + * If `inferEnumOptionsFromData=true`, then it is `true` by default. + * See use-case https://fburl.com/workplace/0kx6fkhm + */ + allowFreeform?: boolean; }; const powerSearchConfigIsExtendedConfig = ( @@ -418,10 +434,12 @@ export function DataTable( inferredPowerSearchEnumLabelsForColumn && column.powerSearchConfig.inferEnumOptionsFromData ) { + const allowFreeform = column.powerSearchConfig.allowFreeform ?? true; columnPowerSearchOperators = columnPowerSearchOperators.map( (operator) => ({ ...operator, enumLabels: inferredPowerSearchEnumLabelsForColumn, + allowFreeform, }), ); } @@ -474,18 +492,30 @@ export function DataTable( } case 'enum': { let enumLabels: EnumLabels; + let allowFreeform = column.powerSearchConfig.allowFreeform; if (column.powerSearchConfig.inferEnumOptionsFromData) { enumLabels = inferredPowerSearchEnumLabels[column.key] ?? {}; + // Fallback to `true` by default when we use inferred labels + if (allowFreeform === undefined) { + allowFreeform = true; + } } else { enumLabels = column.powerSearchConfig.enumLabels; } columnPowerSearchOperators = [ - dataTablePowerSearchOperators.enum_set_is_any_of(enumLabels), - dataTablePowerSearchOperators.enum_set_is_none_of(enumLabels), + dataTablePowerSearchOperators.enum_set_is_any_of( + enumLabels, + allowFreeform, + ), + dataTablePowerSearchOperators.enum_set_is_none_of( + enumLabels, + allowFreeform, + ), dataTablePowerSearchOperators.enum_set_is_nullish_or_any_of( enumLabels, + allowFreeform, ), ]; break; From 202e6b6148e692409500b7deb4ca708f8749abe4 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 21 Nov 2023 11:40:23 -0800 Subject: [PATCH 089/112] Fix autocomplete for Logs plugin Summary: It was a typo before Reviewed By: passy Differential Revision: D51502529 fbshipit-source-id: c4ed789b382301ba9e183e5dd7a491592efd22dd --- desktop/plugins/public/logs/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/plugins/public/logs/index.tsx b/desktop/plugins/public/logs/index.tsx index 5d0e6de10..e4ade415c 100644 --- a/desktop/plugins/public/logs/index.tsx +++ b/desktop/plugins/public/logs/index.tsx @@ -149,7 +149,7 @@ export function devicePlugin(client: DevicePluginClient) { const rows = createDataSource([], { limit: 200000, persist: 'logs', - indices: [['pid'], ['tag']], //there are for inferring enum types + indices: [['pidStr'], ['tag']], //there are for inferring enum types }); const isPaused = createState(true); const tableManagerRef = createRef< From 0425dfd4e5ea8766f1a6b6610683d97f824edc56 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 21 Nov 2023 12:48:27 -0800 Subject: [PATCH 090/112] Add vertical spacing in multiline mode Reviewed By: mweststrate Differential Revision: D51505906 fbshipit-source-id: 6cbf94173d404077de1f8d7c764d91799e25404c --- .../src/ui/PowerSearch/PowerSearchContainer.tsx | 2 +- .../flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx | 3 ++- desktop/flipper-plugin/src/ui/PowerSearch/index.tsx | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx index cb63d4c75..42f0528fe 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx @@ -20,7 +20,7 @@ const containerStyle = css` border-radius: ${theme.borderRadius}; border: 1px solid ${theme.borderColor}; transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); - padding: 0 ${theme.space.tiny}px; + padding: ${theme.space.tiny / 2}px; &:focus-within, &:hover { diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx index 1a9de9fe6..34561c0c0 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx @@ -10,6 +10,7 @@ import {CloseOutlined} from '@ant-design/icons'; import {Button, Space} from 'antd'; import * as React from 'react'; +import {theme} from '../theme'; import {PowerSearchAbsoluteDateTerm} from './PowerSearchAbsoluteDateTerm'; import {OperatorConfig} from './PowerSearchConfig'; import {PowerSearchEnumSetTerm} from './PowerSearchEnumSetTerm'; @@ -168,7 +169,7 @@ export const PowerSearchTerm: React.FC = ({ } return ( - + diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx index 906b74521..525660fc5 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx @@ -129,11 +129,10 @@ export const PowerSearch: React.FC = ({ return ( - + From 88d85673109d779e29080c682bb55b319bc16617 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 21 Nov 2023 12:48:27 -0800 Subject: [PATCH 091/112] Align buttons to the top Reviewed By: mweststrate Differential Revision: D51506570 fbshipit-source-id: 5fc52b214eb61f5da0c3417016e87a79866d4506 --- desktop/flipper-plugin/src/ui/MasterDetailWithPowerSearch.tsx | 4 +--- .../src/ui/data-table/DataTableWithPowerSearch.tsx | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/MasterDetailWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/MasterDetailWithPowerSearch.tsx index d6b873431..d165bfe2e 100644 --- a/desktop/flipper-plugin/src/ui/MasterDetailWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/MasterDetailWithPowerSearch.tsx @@ -213,7 +213,6 @@ export function MasterDetailWithPowerSearch({ )} diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx index 76e04ec61..0e01d276f 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -921,7 +921,7 @@ export function DataTable( {props.actionsTop ? {props.actionsTop} : null} {props.enableSearchbar && ( - + ( /> {contexMenu && ( - From 1c706b52bc928785939b271efbe6e50bbeec9c53 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 21 Nov 2023 12:48:27 -0800 Subject: [PATCH 092/112] Add an empty line for a term finder only when it is absolutely necessary Reviewed By: mweststrate Differential Revision: D51507094 fbshipit-source-id: cb5e18ab702f60ebbf2e5573180fd21299371139 --- .../ui/PowerSearch/PowerSearchContainer.tsx | 1 + .../src/ui/PowerSearch/PowerSearchTerm.tsx | 2 +- .../ui/PowerSearch/PowerSearchTermFinder.tsx | 1 + .../src/ui/PowerSearch/index.tsx | 65 +++++++++---------- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx index 42f0528fe..ce1a1b45b 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx @@ -17,6 +17,7 @@ const containerStyle = css` display: flex; flex-direction: row; flex-wrap: wrap; + align-items: baseline; border-radius: ${theme.borderRadius}; border: 1px solid ${theme.borderColor}; transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx index 34561c0c0..3268ca107 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx @@ -169,7 +169,7 @@ export const PowerSearchTerm: React.FC = ({ } return ( - + diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTermFinder.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTermFinder.tsx index 208e80fbc..8c3e0ad15 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTermFinder.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTermFinder.tsx @@ -67,6 +67,7 @@ export const PowerSearchTermFinder = React.forwardRef< setSearchTermFinderValue(null); }}> { if (event.key === 'Enter') { diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx index 525660fc5..052c8a58d 100644 --- a/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx +++ b/desktop/flipper-plugin/src/ui/PowerSearch/index.tsx @@ -8,7 +8,6 @@ */ import * as React from 'react'; -import {Space} from 'antd'; import { PowerSearchConfig, FieldConfig, @@ -129,43 +128,41 @@ export const PowerSearch: React.FC = ({ return ( - - - {searchExpression.map((searchTerm, i) => { - return ( - { - setSearchExpression((prevSearchExpression) => { - if (prevSearchExpression[i]) { - return [ - ...prevSearchExpression.slice(0, i), - ...prevSearchExpression.slice(i + 1), - ]; - } - return prevSearchExpression; - }); - }} - onFinalize={(finalSearchTerm) => { - setSearchExpression((prevSearchExpression) => { + + {searchExpression.map((searchTerm, i) => { + return ( + { + setSearchExpression((prevSearchExpression) => { + if (prevSearchExpression[i]) { return [ ...prevSearchExpression.slice(0, i), - finalSearchTerm, ...prevSearchExpression.slice(i + 1), ]; - }); - searchTermFinderRef.current?.focus(); - }} - /> - ); - })} - + } + return prevSearchExpression; + }); + }} + onFinalize={(finalSearchTerm) => { + setSearchExpression((prevSearchExpression) => { + return [ + ...prevSearchExpression.slice(0, i), + finalSearchTerm, + ...prevSearchExpression.slice(i + 1), + ]; + }); + searchTermFinderRef.current?.focus(); + }} + /> + ); + })} Date: Wed, 22 Nov 2023 02:59:27 -0800 Subject: [PATCH 093/112] Disconnect all mobile clients when all UI clients leave Summary: Context https://fb.workplace.com/groups/flippersupport/permalink/1730762380737746/ Reviewed By: lblasa Differential Revision: D51510348 fbshipit-source-id: afafcdd6b89bf1038fec65a7c3e8c2dd9cfd0768 --- .../src/FlipperServerImpl.tsx | 31 +++++++++++++++++++ .../BrowserServerWebSocket.tsx | 4 +++ .../src/app-connectivity/ServerRSocket.tsx | 5 +++ .../src/app-connectivity/ServerWebSocket.tsx | 9 ++++++ .../app-connectivity/ServerWebSocketBase.tsx | 20 ++++++++++++ .../src/server/attachSocketServer.tsx | 5 +++ 6 files changed, 74 insertions(+) diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index ef9c437af..577075b7f 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -59,6 +59,7 @@ import {DebuggableDevice} from './devices/DebuggableDevice'; import {jfUpload} from './fb-stubs/jf'; import path from 'path'; import {movePWA} from './utils/findInstallation'; +import GK from './fb-stubs/GK'; const {access, copyFile, mkdir, unlink, stat, readlink, readFile, writeFile} = promises; @@ -109,6 +110,7 @@ export class FlipperServerImpl implements FlipperServer { keytarManager: KeytarManager; pluginManager: PluginManager; unresponsiveClients: Set = new Set(); + private acceptingNewConections = true; constructor( public config: FlipperServerConfig, @@ -175,6 +177,35 @@ export class FlipperServerImpl implements FlipperServer { ); } + startAcceptingNewConections() { + if (!GK.get('flipper_disconnect_device_when_ui_offline')) { + return; + } + if (this.acceptingNewConections) { + return; + } + this.acceptingNewConections = true; + + this.server.insecureServer?.startAcceptingNewConections(); + this.server.altInsecureServer?.startAcceptingNewConections(); + this.server.secureServer?.startAcceptingNewConections(); + this.server.altSecureServer?.startAcceptingNewConections(); + this.server.browserServer?.startAcceptingNewConections(); + } + + stopAcceptingNewConections() { + if (!GK.get('flipper_disconnect_device_when_ui_offline')) { + return; + } + this.acceptingNewConections = false; + + this.server.insecureServer?.stopAcceptingNewConections(); + this.server.altInsecureServer?.stopAcceptingNewConections(); + this.server.secureServer?.stopAcceptingNewConections(); + this.server.altSecureServer?.stopAcceptingNewConections(); + this.server.browserServer?.stopAcceptingNewConections(); + } + setServerState(state: FlipperServerState, error?: Error) { this.state = state; this.stateError = '' + error; diff --git a/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx index 72512b872..f1afa65c7 100644 --- a/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx @@ -148,6 +148,10 @@ class BrowserServerWebSocket extends SecureServerWebSocket { protected verifyClient(): ws.VerifyClientCallbackSync { return (info: {origin: string; req: IncomingMessage; secure: boolean}) => { + if (!this.acceptingNewConections) { + return false; + } + if (isFBBuild) { try { const urlObj = new URL(info.origin); diff --git a/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx index 84515b68d..375101781 100644 --- a/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx @@ -293,6 +293,11 @@ class ServerRSocket extends ServerWebSocketBase { }, }; }; + + protected stopAcceptingNewConectionsImpl(): void { + // Did not find a straightforard way to iterate through RSocket open connections and close them. + // We probably should not care and invest in it anyway as we are going to remove RScokets. + } } export default ServerRSocket; diff --git a/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx index 100ba2d48..58511fb81 100644 --- a/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx @@ -294,11 +294,20 @@ class ServerWebSocket extends ServerWebSocketBase { */ protected verifyClient(): VerifyClientCallbackSync { return (_info: {origin: string; req: IncomingMessage; secure: boolean}) => { + if (!this.acceptingNewConections) { + return false; + } // Client verification is not necessary. The connected client has // already been verified using its certificate signed by the server. return true; }; } + + protected stopAcceptingNewConectionsImpl(): void { + this.wsServer?.clients.forEach((client) => + client.close(WSCloseCode.GoingAway), + ); + } } export default ServerWebSocket; diff --git a/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx index b7ae2824e..84e1cbdb5 100644 --- a/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx @@ -15,6 +15,7 @@ import { SignCertificateMessage, } from 'flipper-common'; import {SecureServerConfig} from './certificate-exchange/certificate-utils'; +import GK from '../fb-stubs/GK'; /** * Defines an interface for events triggered by a running server interacting @@ -98,6 +99,8 @@ export interface ServerEventsListener { * RSocket, WebSocket, etc. */ abstract class ServerWebSocketBase { + protected acceptingNewConections = true; + constructor(protected listener: ServerEventsListener) {} /** @@ -169,6 +172,23 @@ abstract class ServerWebSocketBase { return undefined; } + + startAcceptingNewConections() { + if (!GK.get('flipper_disconnect_device_when_ui_offline')) { + return; + } + this.acceptingNewConections = true; + } + + stopAcceptingNewConections() { + if (!GK.get('flipper_disconnect_device_when_ui_offline')) { + return; + } + this.acceptingNewConections = false; + this.stopAcceptingNewConectionsImpl(); + } + + protected abstract stopAcceptingNewConectionsImpl(): void; } export default ServerWebSocketBase; diff --git a/desktop/flipper-server-core/src/server/attachSocketServer.tsx b/desktop/flipper-server-core/src/server/attachSocketServer.tsx index 7a56d498b..aad29f673 100644 --- a/desktop/flipper-server-core/src/server/attachSocketServer.tsx +++ b/desktop/flipper-server-core/src/server/attachSocketServer.tsx @@ -72,6 +72,7 @@ export function attachSocketServer( server.emit('browser-connection-created', {}); let connected = true; + server.startAcceptingNewConections(); let flipperServerCompanion: FlipperServerCompanion | undefined; if (req.url) { @@ -253,6 +254,10 @@ export function attachSocketServer( sessionLength: performance.now() - t0, }); + if (numberOfConnectedClients === 0) { + server.stopAcceptingNewConections(); + } + if ( getFlipperServerConfig().environmentInfo.isHeadlessBuild && isProduction() From b908ab50ac0a4d2f44ca3f5b56c07fdcbb03b976 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 07:05:56 -0800 Subject: [PATCH 094/112] Bump com.google.protobuf:protobuf-java from 3.23.4 to 3.25.1 (#5300) Summary: Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 3.23.4 to 3.25.1.
Commits
  • 7f94235 Updating version.json and repo version numbers to: 25.1
  • e4b00c7 Add support for extensions in CRuby, JRuby, and FFI Ruby (#14703) (#14756)
  • 2495d4f Add support for options in CRuby, JRuby and FFI (#14594) (#14739)
  • a29f47d Bump mac PHP version to 8.2 to fix non-hermetic breakages. (#14741)
  • f36432a Merge pull request #14674 from anandolee/25.x
  • 74f5cf4 Raise warnings for python syntax usages
  • edb1afd Move python/BUILD to python/BUILD.bazel (#14658)
  • 666689e Merge pull request #14620 from protocolbuffers/win2019-25.x
  • 1577c30 Error on staleness failure
  • 1155c80 Update cc_file_list_aspect to handle targets with missing hdrs/textual_hdrs
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.google.protobuf:protobuf-java&package-manager=gradle&previous-version=3.23.4&new-version=3.25.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `dependabot rebase` will rebase this PR - `dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `dependabot merge` will merge this PR after your CI passes on it - `dependabot squash and merge` will squash and merge this PR after your CI passes on it - `dependabot cancel merge` will cancel a previously requested merge and block automerging - `dependabot reopen` will reopen this PR if it is closed - `dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Pull Request resolved: https://github.com/facebook/flipper/pull/5300 Reviewed By: antonk52 Differential Revision: D51525558 Pulled By: passy fbshipit-source-id: df9f980c75af74e622cc3a899c5a53684ffd8b90 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 48fc54c05..a30924ed0 100644 --- a/build.gradle +++ b/build.gradle @@ -109,7 +109,7 @@ ext.deps = [ okhttp3 : 'com.squareup.okhttp3:okhttp:4.11.0', leakcanary : 'com.squareup.leakcanary:leakcanary-android:1.6.3', leakcanary2 : 'com.squareup.leakcanary:leakcanary-android:2.8.1', - protobuf : 'com.google.protobuf:protobuf-java:3.23.4', + protobuf : 'com.google.protobuf:protobuf-java:3.25.1', testCore : 'androidx.test:core:1.4.0', testRules : 'androidx.test:rules:1.5.0', // Plugin dependencies From 4477f3b550169b1cac37cfdf86eb0bb01049654d Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Thu, 23 Nov 2023 02:56:07 -0800 Subject: [PATCH 095/112] Update esbuild Summary: Update esbuild to the latest version that is backward compatible to the current version. Specifically I was looking for a version that adds support for the typescript's `satisfies` feature [0.15.3](https://github.com/evanw/esbuild/releases/tag/v0.15.13) Reviewed By: passy Differential Revision: D51537364 fbshipit-source-id: 6fb8f01ee7b4d19db048d03277be6b6642124002 --- desktop/pkg-lib/package.json | 2 +- desktop/yarn.lock | 224 ++++++++++++++++++----------------- 2 files changed, 116 insertions(+), 110 deletions(-) diff --git a/desktop/pkg-lib/package.json b/desktop/pkg-lib/package.json index 4ddd84909..31b20ea86 100644 --- a/desktop/pkg-lib/package.json +++ b/desktop/pkg-lib/package.json @@ -10,7 +10,7 @@ "bugs": "https://github.com/facebook/flipper/issues", "dependencies": { "chalk": "^4", - "esbuild": "^0.15.7", + "esbuild": "^0.15.18", "fb-watchman": "^2.0.2", "flipper-common": "0.0.0", "flipper-plugin-lib": "0.0.0", diff --git a/desktop/yarn.lock b/desktop/yarn.lock index 9068715f3..06b38ee5d 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -2965,10 +2965,15 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== -"@esbuild/linux-loong64@0.15.7": - version "0.15.7" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz#1ec4af4a16c554cbd402cc557ccdd874e3f7be53" - integrity sha512-IKznSJOsVUuyt7cDzzSZyqBEcZe+7WlBqTVXiF1OXP/4Nm387ToaXZ0fyLwI1iBlI/bzpxVq411QE2/Bt2XWWw== +"@esbuild/android-arm@0.15.18": + version "0.15.18" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80" + integrity sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw== + +"@esbuild/linux-loong64@0.15.18": + version "0.15.18" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz#128b76ecb9be48b60cf5cfc1c63a4f00691a3239" + integrity sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ== "@eslint/eslintrc@^0.4.3": version "0.4.3" @@ -7533,132 +7538,133 @@ es6-error@^4.1.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild-android-64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.7.tgz#a521604d8c4c6befc7affedc897df8ccde189bea" - integrity sha512-p7rCvdsldhxQr3YHxptf1Jcd86dlhvc3EQmQJaZzzuAxefO9PvcI0GLOa5nCWem1AJ8iMRu9w0r5TG8pHmbi9w== +esbuild-android-64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz#20a7ae1416c8eaade917fb2453c1259302c637a5" + integrity sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA== -esbuild-android-arm64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.7.tgz#307b81f1088bf1e81dfe5f3d1d63a2d2a2e3e68e" - integrity sha512-L775l9ynJT7rVqRM5vo+9w5g2ysbOCfsdLV4CWanTZ1k/9Jb3IYlQ06VCI1edhcosTYJRECQFJa3eAvkx72eyQ== +esbuild-android-arm64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz#9cc0ec60581d6ad267568f29cf4895ffdd9f2f04" + integrity sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ== -esbuild-darwin-64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.7.tgz#270117b0c4ec6bcbc5cf3a297a7d11954f007e11" - integrity sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg== +esbuild-darwin-64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz#428e1730ea819d500808f220fbc5207aea6d4410" + integrity sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg== -esbuild-darwin-arm64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.7.tgz#97851eacd11dacb7719713602e3319e16202fc77" - integrity sha512-kBIHvtVqbSGajN88lYMnR3aIleH3ABZLLFLxwL2stiuIGAjGlQW741NxVTpUHQXUmPzxi6POqc9npkXa8AcSZQ== +esbuild-darwin-arm64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz#b6dfc7799115a2917f35970bfbc93ae50256b337" + integrity sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA== -esbuild-freebsd-64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.7.tgz#1de15ffaf5ae916aa925800aa6d02579960dd8c4" - integrity sha512-hESZB91qDLV5MEwNxzMxPfbjAhOmtfsr9Wnuci7pY6TtEh4UDuevmGmkUIjX/b+e/k4tcNBMf7SRQ2mdNuK/HQ== +esbuild-freebsd-64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz#4e190d9c2d1e67164619ae30a438be87d5eedaf2" + integrity sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA== -esbuild-freebsd-arm64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.7.tgz#0f160dbf5c9a31a1d8dd87acbbcb1a04b7031594" - integrity sha512-dLFR0ChH5t+b3J8w0fVKGvtwSLWCv7GYT2Y2jFGulF1L5HftQLzVGN+6pi1SivuiVSmTh28FwUhi9PwQicXI6Q== +esbuild-freebsd-arm64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz#18a4c0344ee23bd5a6d06d18c76e2fd6d3f91635" + integrity sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA== -esbuild-linux-32@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.7.tgz#422eb853370a5e40bdce8b39525380de11ccadec" - integrity sha512-v3gT/LsONGUZcjbt2swrMjwxo32NJzk+7sAgtxhGx1+ZmOFaTRXBAi1PPfgpeo/J//Un2jIKm/I+qqeo4caJvg== +esbuild-linux-32@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz#9a329731ee079b12262b793fb84eea762e82e0ce" + integrity sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg== -esbuild-linux-64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.7.tgz#f89c468453bb3194b14f19dc32e0b99612e81d2b" - integrity sha512-LxXEfLAKwOVmm1yecpMmWERBshl+Kv5YJ/1KnyAr6HRHFW8cxOEsEfisD3sVl/RvHyW//lhYUVSuy9jGEfIRAQ== +esbuild-linux-64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz#532738075397b994467b514e524aeb520c191b6c" + integrity sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw== -esbuild-linux-arm64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.7.tgz#68a79d6eb5e032efb9168a0f340ccfd33d6350a1" - integrity sha512-P3cfhudpzWDkglutWgXcT2S7Ft7o2e3YDMrP1n0z2dlbUZghUkKCyaWw0zhp4KxEEzt/E7lmrtRu/pGWnwb9vw== +esbuild-linux-arm64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz#5372e7993ac2da8f06b2ba313710d722b7a86e5d" + integrity sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug== -esbuild-linux-arm@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.7.tgz#2b7c784d0b3339878013dfa82bf5eaf82c7ce7d3" - integrity sha512-JKgAHtMR5f75wJTeuNQbyznZZa+pjiUHV7sRZp42UNdyXC6TiUYMW/8z8yIBAr2Fpad8hM1royZKQisqPABPvQ== +esbuild-linux-arm@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz#e734aaf259a2e3d109d4886c9e81ec0f2fd9a9cc" + integrity sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA== -esbuild-linux-mips64le@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.7.tgz#bb8330a50b14aa84673816cb63cc6c8b9beb62cc" - integrity sha512-T7XKuxl0VpeFLCJXub6U+iybiqh0kM/bWOTb4qcPyDDwNVhLUiPcGdG2/0S7F93czUZOKP57YiLV8YQewgLHKw== +esbuild-linux-mips64le@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz#c0487c14a9371a84eb08fab0e1d7b045a77105eb" + integrity sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ== -esbuild-linux-ppc64le@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.7.tgz#52544e7fa992811eb996674090d0bc41f067a14b" - integrity sha512-6mGuC19WpFN7NYbecMIJjeQgvDb5aMuvyk0PDYBJrqAEMkTwg3Z98kEKuCm6THHRnrgsdr7bp4SruSAxEM4eJw== +esbuild-linux-ppc64le@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz#af048ad94eed0ce32f6d5a873f7abe9115012507" + integrity sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w== -esbuild-linux-riscv64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.7.tgz#a43ae60697992b957e454cbb622f7ee5297e8159" - integrity sha512-uUJsezbswAYo/X7OU/P+PuL/EI9WzxsEQXDekfwpQ23uGiooxqoLFAPmXPcRAt941vjlY9jtITEEikWMBr+F/g== +esbuild-linux-riscv64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz#423ed4e5927bd77f842bd566972178f424d455e6" + integrity sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg== -esbuild-linux-s390x@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.7.tgz#8c76a125dd10a84c166294d77416caaf5e1c7b64" - integrity sha512-+tO+xOyTNMc34rXlSxK7aCwJgvQyffqEM5MMdNDEeMU3ss0S6wKvbBOQfgd5jRPblfwJ6b+bKiz0g5nABpY0QQ== +esbuild-linux-s390x@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz#21d21eaa962a183bfb76312e5a01cc5ae48ce8eb" + integrity sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ== -esbuild-netbsd-64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.7.tgz#19b2e75449d7d9c32b5d8a222bac2f1e0c3b08fd" - integrity sha512-yVc4Wz+Pu3cP5hzm5kIygNPrjar/v5WCSoRmIjCPWfBVJkZNb5brEGKUlf+0Y759D48BCWa0WHrWXaNy0DULTQ== +esbuild-netbsd-64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz#ae75682f60d08560b1fe9482bfe0173e5110b998" + integrity sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg== -esbuild-openbsd-64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.7.tgz#1357b2bf72fd037d9150e751420a1fe4c8618ad7" - integrity sha512-GsimbwC4FSR4lN3wf8XmTQ+r8/0YSQo21rWDL0XFFhLHKlzEA4SsT1Tl8bPYu00IU6UWSJ+b3fG/8SB69rcuEQ== +esbuild-openbsd-64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz#79591a90aa3b03e4863f93beec0d2bab2853d0a8" + integrity sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ== -esbuild-sunos-64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.7.tgz#87ab2c604592a9c3c763e72969da0d72bcde91d2" - integrity sha512-8CDI1aL/ts0mDGbWzjEOGKXnU7p3rDzggHSBtVryQzkSOsjCHRVe0iFYUuhczlxU1R3LN/E7HgUO4NXzGGP/Ag== +esbuild-sunos-64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz#fd528aa5da5374b7e1e93d36ef9b07c3dfed2971" + integrity sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw== -esbuild-windows-32@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.7.tgz#c81e688c0457665a8d463a669e5bf60870323e99" - integrity sha512-cOnKXUEPS8EGCzRSFa1x6NQjGhGsFlVgjhqGEbLTPsA7x4RRYiy2RKoArNUU4iR2vHmzqS5Gr84MEumO/wxYKA== +esbuild-windows-32@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz#0e92b66ecdf5435a76813c4bc5ccda0696f4efc3" + integrity sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ== -esbuild-windows-64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.7.tgz#2421d1ae34b0561a9d6767346b381961266c4eff" - integrity sha512-7MI08Ec2sTIDv+zH6StNBKO+2hGUYIT42GmFyW6MBBWWtJhTcQLinKS6ldIN1d52MXIbiJ6nXyCJ+LpL4jBm3Q== +esbuild-windows-64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz#0fc761d785414284fc408e7914226d33f82420d0" + integrity sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw== -esbuild-windows-arm64@0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.7.tgz#7d5e9e060a7b454cb2f57f84a3f3c23c8f30b7d2" - integrity sha512-R06nmqBlWjKHddhRJYlqDd3Fabx9LFdKcjoOy08YLimwmsswlFBJV4rXzZCxz/b7ZJXvrZgj8DDv1ewE9+StMw== +esbuild-windows-arm64@0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz#5b5bdc56d341d0922ee94965c89ee120a6a86eb7" + integrity sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ== -esbuild@^0.15.7: - version "0.15.7" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.7.tgz#8a1f1aff58671a3199dd24df95314122fc1ddee8" - integrity sha512-7V8tzllIbAQV1M4QoE52ImKu8hT/NLGlGXkiDsbEU5PS6K8Mn09ZnYoS+dcmHxOS9CRsV4IRAMdT3I67IyUNXw== +esbuild@^0.15.18: + version "0.15.18" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.18.tgz#ea894adaf3fbc036d32320a00d4d6e4978a2f36d" + integrity sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q== optionalDependencies: - "@esbuild/linux-loong64" "0.15.7" - esbuild-android-64 "0.15.7" - esbuild-android-arm64 "0.15.7" - esbuild-darwin-64 "0.15.7" - esbuild-darwin-arm64 "0.15.7" - esbuild-freebsd-64 "0.15.7" - esbuild-freebsd-arm64 "0.15.7" - esbuild-linux-32 "0.15.7" - esbuild-linux-64 "0.15.7" - esbuild-linux-arm "0.15.7" - esbuild-linux-arm64 "0.15.7" - esbuild-linux-mips64le "0.15.7" - esbuild-linux-ppc64le "0.15.7" - esbuild-linux-riscv64 "0.15.7" - esbuild-linux-s390x "0.15.7" - esbuild-netbsd-64 "0.15.7" - esbuild-openbsd-64 "0.15.7" - esbuild-sunos-64 "0.15.7" - esbuild-windows-32 "0.15.7" - esbuild-windows-64 "0.15.7" - esbuild-windows-arm64 "0.15.7" + "@esbuild/android-arm" "0.15.18" + "@esbuild/linux-loong64" "0.15.18" + esbuild-android-64 "0.15.18" + esbuild-android-arm64 "0.15.18" + esbuild-darwin-64 "0.15.18" + esbuild-darwin-arm64 "0.15.18" + esbuild-freebsd-64 "0.15.18" + esbuild-freebsd-arm64 "0.15.18" + esbuild-linux-32 "0.15.18" + esbuild-linux-64 "0.15.18" + esbuild-linux-arm "0.15.18" + esbuild-linux-arm64 "0.15.18" + esbuild-linux-mips64le "0.15.18" + esbuild-linux-ppc64le "0.15.18" + esbuild-linux-riscv64 "0.15.18" + esbuild-linux-s390x "0.15.18" + esbuild-netbsd-64 "0.15.18" + esbuild-openbsd-64 "0.15.18" + esbuild-sunos-64 "0.15.18" + esbuild-windows-32 "0.15.18" + esbuild-windows-64 "0.15.18" + esbuild-windows-arm64 "0.15.18" escalade@^3.1.1: version "3.1.1" From 685a0e53d76031d2369f666e14940efe24cae909 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 23 Nov 2023 04:08:07 -0800 Subject: [PATCH 096/112] Add command to fetch new Flipper version Summary: It is a copy-paste of what we have in jest-e2e Reviewed By: passy Differential Revision: D51527040 fbshipit-source-id: 36b62edee006580023e9ce2cd309dddfd0dbce84 --- desktop/flipper-common/src/server-types.tsx | 1 + desktop/flipper-server-core/src/FlipperServerImpl.tsx | 2 ++ .../src/fb-stubs/fetchNewVersion.tsx | 10 ++++++++++ 3 files changed, 13 insertions(+) create mode 100644 desktop/flipper-server-core/src/fb-stubs/fetchNewVersion.tsx diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index 723f4f99f..64c04e6ef 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -384,6 +384,7 @@ export type FlipperServerCommands = { 'is-logged-in': () => Promise; 'environment-info': () => Promise; 'move-pwa': () => Promise; + 'fetch-new-version': (version: string) => Promise; }; export type GraphResponse = { diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index 577075b7f..76a756ed6 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -60,6 +60,7 @@ import {jfUpload} from './fb-stubs/jf'; import path from 'path'; import {movePWA} from './utils/findInstallation'; import GK from './fb-stubs/GK'; +import {fetchNewVersion} from './fb-stubs/fetchNewVersion'; const {access, copyFile, mkdir, unlink, stat, readlink, readFile, writeFile} = promises; @@ -632,6 +633,7 @@ export class FlipperServerImpl implements FlipperServer { 'move-pwa': async () => { await movePWA(); }, + 'fetch-new-version': fetchNewVersion, }; registerDevice(device: ServerDevice) { diff --git a/desktop/flipper-server-core/src/fb-stubs/fetchNewVersion.tsx b/desktop/flipper-server-core/src/fb-stubs/fetchNewVersion.tsx new file mode 100644 index 000000000..5e3930797 --- /dev/null +++ b/desktop/flipper-server-core/src/fb-stubs/fetchNewVersion.tsx @@ -0,0 +1,10 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +export const fetchNewVersion = async (): Promise => {}; From 72a92e1380414de0f28c9becb522e515897bd0a4 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 23 Nov 2023 04:08:07 -0800 Subject: [PATCH 097/112] Shutdown Flipper when new version is downloaded Reviewed By: passy Differential Revision: D51527986 fbshipit-source-id: d4cf1ec82070821afff5c9cdee5a85cb1421a7ef --- .../src/chrome/UpdateIndicator.tsx | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/desktop/flipper-ui-core/src/chrome/UpdateIndicator.tsx b/desktop/flipper-ui-core/src/chrome/UpdateIndicator.tsx index fdaaa3851..108dc4429 100644 --- a/desktop/flipper-ui-core/src/chrome/UpdateIndicator.tsx +++ b/desktop/flipper-ui-core/src/chrome/UpdateIndicator.tsx @@ -7,7 +7,7 @@ * @format */ -import {notification, Typography} from 'antd'; +import {Button, notification, Typography} from 'antd'; import isProduction from '../utils/isProduction'; import {reportPlatformFailures, ReleaseChannel} from 'flipper-common'; import React, {useEffect, useState} from 'react'; @@ -91,17 +91,29 @@ export default function UpdateIndicator() { isProduction() ) { reportPlatformFailures( - checkForUpdate(version).then((res) => { - if (res.kind === 'error') { - console.warn('Version check failure: ', res); + checkForUpdate(version) + .then((res) => { + if (res.kind === 'error') { + throw new Error(res.msg); + } + if (res.kind === 'up-to-date') { + setVersionCheckResult(res); + return; + } + + return getRenderHostInstance() + .flipperServer.exec('fetch-new-version', res.version) + .then(() => { + setVersionCheckResult(res); + }); + }) + .catch((e) => { + console.warn('Version check failure: ', e); setVersionCheckResult({ kind: 'error', - msg: res.msg, + msg: e, }); - } else { - setVersionCheckResult(res); - } - }), + }), 'publicVersionCheck', ); } @@ -114,18 +126,31 @@ export function getUpdateAvailableMessage(versionCheckResult: { url: string; version: string; }): React.ReactNode { + const {launcherSettings} = getRenderHostInstance().serverConfig; + + const shutdownFlipper = () => { + getRenderHostInstance().flipperServer.exec('shutdown'); + window.close(); + }; + return ( <> Flipper version {versionCheckResult.version} is now available. {fbConfig.isFBBuild ? ( - fbConfig.getReleaseChannel() === ReleaseChannel.INSIDERS ? ( - <> Restart Flipper to update to the latest version. + fbConfig.getReleaseChannel() === ReleaseChannel.INSIDERS || + launcherSettings.ignoreLocalPin ? ( + ) : ( <> {' '} Run arc pull (optionally with --latest) in{' '} - ~/fbsource and restart Flipper to update to the latest - version. + ~/fbsource and{' '} + + . ) ) : ( From d22d362c312f3c0de686135c5e218b9c8a96ed1a Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Mon, 27 Nov 2023 04:55:05 -0800 Subject: [PATCH 098/112] Codesign capabilities Summary: Add codesign capabilities to Flipper Server Cocoa app. Also, push the version to the build step instead of overriding once built. Otherwise, the Info.plist will be marked as 'being tampered with'. Note: we are still not using the signing, but the capability is there. Reviewed By: antonk52 Differential Revision: D51547008 fbshipit-source-id: 33abcd2fce33a7daf2ae8941b54989dba82fc0e3 --- .../scripts/build-flipper-server-release.tsx | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/desktop/scripts/build-flipper-server-release.tsx b/desktop/scripts/build-flipper-server-release.tsx index a6d2f07b7..4f6744432 100644 --- a/desktop/scripts/build-flipper-server-release.tsx +++ b/desktop/scripts/build-flipper-server-release.tsx @@ -754,7 +754,11 @@ async function setUpMacBundle( platform === BuildPlatform.MAC_AARCH64 ? BuildArchitecture.MAC_AARCH64 : BuildArchitecture.MAC_X64; - const outputPath = await buildFlipperServer(architecture); + const outputPath = await buildFlipperServer( + architecture, + versionNumber, + false, + ); console.log( `⚙️ Successfully built platform: ${platform}, output: ${outputPath}`, ); @@ -766,47 +770,47 @@ async function setUpMacBundle( } else { const template = path.join(staticDir, 'flipper-server-app-template'); await fs.copy(template, outputDir); - } - function replacePropertyValue( - obj: any, - targetValue: string, - replacementValue: string, - ): any { - if (typeof obj === 'object' && !Array.isArray(obj) && obj !== null) { - for (const key in obj) { - if (obj.hasOwnProperty(key)) { - obj[key] = replacePropertyValue( - obj[key], - targetValue, - replacementValue, - ); + function replacePropertyValue( + obj: any, + targetValue: string, + replacementValue: string, + ): any { + if (typeof obj === 'object' && !Array.isArray(obj) && obj !== null) { + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + obj[key] = replacePropertyValue( + obj[key], + targetValue, + replacementValue, + ); + } } + } else if (typeof obj === 'string' && obj === targetValue) { + obj = replacementValue; } - } else if (typeof obj === 'string' && obj === targetValue) { - obj = replacementValue; + return obj; } - return obj; + + console.log(`⚙️ Writing plist`); + const plistPath = path.join( + outputDir, + 'Flipper.app', + 'Contents', + 'Info.plist', + ); + + /* eslint-disable node/no-sync*/ + const pListContents: Record = plist.readFileSync(plistPath); + replacePropertyValue( + pListContents, + '{flipper-server-version}', + versionNumber, + ); + plist.writeBinaryFileSync(plistPath, pListContents); + /* eslint-enable node/no-sync*/ } - console.log(`⚙️ Writing plist`); - const plistPath = path.join( - outputDir, - 'Flipper.app', - 'Contents', - 'Info.plist', - ); - - /* eslint-disable node/no-sync*/ - const pListContents: Record = plist.readFileSync(plistPath); - replacePropertyValue( - pListContents, - '{flipper-server-version}', - versionNumber, - ); - plist.writeBinaryFileSync(plistPath, pListContents); - /* eslint-enable node/no-sync*/ - const resourcesOutputDir = path.join( outputDir, 'Flipper.app', From 5e26d863f1b5a16cc0e93fa44e23b9166bb80642 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Mon, 27 Nov 2023 04:55:05 -0800 Subject: [PATCH 099/112] Packaging changes Summary: This change has a few changes in the way our MacOS app was built and packaged. - Instead of placing the Node binary inside the MacOS folder, place it inside the Resources folder. This is more in compliance with how an app is bundled. Also, with the added benefit of making it a resource which makes it eligible for code signing. - Both, Node binary and server bundle are placed in the right location before building the MacOS app. By doing this, we ensure the app is not modified after the built process which messes up with code signing, if in place. Reviewed By: antonk52 Differential Revision: D51568778 fbshipit-source-id: 0b1b0ad9947550ddf0f6d4b04e5aff41f7edcdee --- .gitignore | 1 + .../scripts/build-flipper-server-release.tsx | 195 +++++++++++------- desktop/scripts/paths.tsx | 1 + 3 files changed, 125 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index 9a301999a..d614cbca7 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ xplat/build # Mac OS X *.DS_Store +facebook/flipper-server/Resources/ # Automatically generated docs/extending/ui-components.mdx diff --git a/desktop/scripts/build-flipper-server-release.tsx b/desktop/scripts/build-flipper-server-release.tsx index 4f6744432..15ffa83f3 100644 --- a/desktop/scripts/build-flipper-server-release.tsx +++ b/desktop/scripts/build-flipper-server-release.tsx @@ -26,6 +26,7 @@ import { serverDir, staticDir, rootDir, + sonarDir, } from './paths'; import isFB from './isFB'; import yargs from 'yargs'; @@ -650,8 +651,11 @@ async function installNodeBinary(outputPath: string, platform: BuildPlatform) { console.log(`✅ Node successfully downloaded and unpacked.`); } - console.log(`⚙️ Copying node binary from ${nodePath} to ${outputPath}`); - await fs.copyFile(nodePath, outputPath); + console.log(`⚙️ Moving node binary from ${nodePath} to ${outputPath}`); + if (await fs.exists(outputPath)) { + await fs.rm(outputPath); + } + await fs.move(nodePath, outputPath); } else { console.log(`⚙️ Downloading node version for ${platform} using pkg-fetch`); const nodePath = await pkgFetch({ @@ -660,8 +664,11 @@ async function installNodeBinary(outputPath: string, platform: BuildPlatform) { nodeRange: SUPPORTED_NODE_PLATFORM, }); - console.log(`⚙️ Copying node binary from ${nodePath} to ${outputPath}`); - await fs.copyFile(nodePath, outputPath); + console.log(`⚙️ Moving node binary from ${nodePath} to ${outputPath}`); + if (await fs.exists(outputPath)) { + await fs.rm(outputPath); + } + await fs.move(nodePath, outputPath); } if ( @@ -739,35 +746,21 @@ async function setUpWindowsBundle(outputDir: string) { async function setUpMacBundle( outputDir: string, + serverDir: string, platform: BuildPlatform, versionNumber: string, -): Promise<{nodePath: string; resourcesPath: string}> { +) { console.log(`⚙️ Creating Mac bundle in ${outputDir}`); - if (isFB) { - const {BuildArchitecture, buildFlipperServer} = await import( - // @ts-ignore only used inside Meta - './fb/build-flipper-server-macos' - ); + let serverOutputDir = ''; + let nodeBinaryFile = ''; - const architecture = - platform === BuildPlatform.MAC_AARCH64 - ? BuildArchitecture.MAC_AARCH64 - : BuildArchitecture.MAC_X64; - const outputPath = await buildFlipperServer( - architecture, - versionNumber, - false, - ); - console.log( - `⚙️ Successfully built platform: ${platform}, output: ${outputPath}`, - ); - - const appPath = path.join(outputDir, 'Flipper.app'); - await fs.emptyDir(appPath); - - await fs.copy(outputPath, appPath); - } else { + /** + * Use the most basic template for MacOS. + * - Copy the contents of the template into the output directory. + * - Replace the version placeholder value with the actual version. + */ + if (!isFB) { const template = path.join(staticDir, 'flipper-server-app-template'); await fs.copy(template, outputDir); @@ -792,7 +785,7 @@ async function setUpMacBundle( return obj; } - console.log(`⚙️ Writing plist`); + console.log(`⚙️ Update plist with build information`); const plistPath = path.join( outputDir, 'Flipper.app', @@ -809,31 +802,90 @@ async function setUpMacBundle( ); plist.writeBinaryFileSync(plistPath, pListContents); /* eslint-enable node/no-sync*/ + + serverOutputDir = path.join( + outputDir, + 'Flipper.app', + 'Contents', + 'Resources', + 'server', + ); + + nodeBinaryFile = path.join( + outputDir, + 'Flipper.app', + 'Contents', + 'MacOS', + 'flipper-runtime', + ); + } else { + serverOutputDir = path.join( + sonarDir, + 'facebook', + 'flipper-server', + 'Resources', + 'server', + ); + nodeBinaryFile = path.join( + sonarDir, + 'facebook', + 'flipper-server', + 'Resources', + 'flipper-runtime', + ); } - const resourcesOutputDir = path.join( - outputDir, - 'Flipper.app', - 'Contents', - 'Resources', - 'server', - ); - - if (!(await fs.exists(resourcesOutputDir))) { - await fs.mkdir(resourcesOutputDir); + if (await fs.exists(serverOutputDir)) { + await fs.rm(serverOutputDir, {recursive: true, force: true}); + } + await fs.mkdirp(serverOutputDir); + + console.log(`⚙️ Copying from ${serverDir} to ${serverOutputDir}`); + + // Copy resources instead of moving. This is because we want to keep the original + // files in the right location because they are used whilst bundling for + // other platforms. + await fs.copy(serverDir, serverOutputDir, { + overwrite: true, + dereference: true, + }); + + console.log(`⚙️ Downloading compatible node version`); + await installNodeBinary(nodeBinaryFile, platform); + + if (isFB) { + const {BuildArchitecture, buildFlipperServer} = await import( + // @ts-ignore only used inside Meta + './fb/build-flipper-server-macos' + ); + + const architecture = + platform === BuildPlatform.MAC_AARCH64 + ? BuildArchitecture.MAC_AARCH64 + : BuildArchitecture.MAC_X64; + const outputPath = await buildFlipperServer( + architecture, + versionNumber, + false, + ); + console.log( + `⚙️ Successfully built platform: ${platform}, output: ${outputPath}`, + ); + + const appPath = path.join(outputDir, 'Flipper.app'); + await fs.emptyDir(appPath); + await fs.copy(outputPath, appPath); + + // const appPath = path.join(outputDir, 'Flipper.app'); + // if (await fs.exists(appPath)) { + // await fs.rm(appPath, {recursive: true, force: true}); + // } + // await fs.move(outputPath, appPath); } - const nodeOutputPath = path.join( - outputDir, - 'Flipper.app', - 'Contents', - 'MacOS', - 'flipper-runtime', - ); - return {resourcesPath: resourcesOutputDir, nodePath: nodeOutputPath}; } async function bundleServerReleaseForPlatform( - dir: string, + bundleDir: string, versionNumber: string, platform: BuildPlatform, ) { @@ -844,39 +896,38 @@ async function bundleServerReleaseForPlatform( ); await fs.mkdirp(outputDir); - let outputPaths = { - nodePath: path.join(outputDir, 'flipper-runtime'), - resourcesPath: outputDir, - }; - // On the mac, we need to set up a resource bundle which expects paths // to be in different places from Linux/Windows bundles. if ( platform === BuildPlatform.MAC_X64 || platform === BuildPlatform.MAC_AARCH64 ) { - outputPaths = await setUpMacBundle(outputDir, platform, versionNumber); - } else if (platform === BuildPlatform.LINUX) { - await setUpLinuxBundle(outputDir); - } else if (platform === BuildPlatform.WINDOWS) { - await setUpWindowsBundle(outputDir); - } + await setUpMacBundle(outputDir, bundleDir, platform, versionNumber); + if (argv.dmg) { + await createMacDMG(platform, outputDir, distDir); + } + } else { + const outputPaths = { + nodePath: path.join(outputDir, 'flipper-runtime'), + resourcesPath: outputDir, + }; - console.log(`⚙️ Copying from ${dir} to ${outputPaths.resourcesPath}`); - await fs.copy(dir, outputPaths.resourcesPath, { - overwrite: true, - dereference: true, - }); + if (platform === BuildPlatform.LINUX) { + await setUpLinuxBundle(outputDir); + } else if (platform === BuildPlatform.WINDOWS) { + await setUpWindowsBundle(outputDir); + } - console.log(`⚙️ Downloading compatible node version`); - await installNodeBinary(outputPaths.nodePath, platform); + console.log( + `⚙️ Copying from ${bundleDir} to ${outputPaths.resourcesPath}`, + ); + await fs.copy(bundleDir, outputPaths.resourcesPath, { + overwrite: true, + dereference: true, + }); - if ( - argv.dmg && - (platform === BuildPlatform.MAC_X64 || - platform === BuildPlatform.MAC_AARCH64) - ) { - await createMacDMG(platform, outputDir, distDir); + console.log(`⚙️ Downloading compatible node version`); + await installNodeBinary(outputPaths.nodePath, platform); } console.log(`✅ Wrote ${platform}-specific server version to ${outputDir}`); diff --git a/desktop/scripts/paths.tsx b/desktop/scripts/paths.tsx index daa12d36a..87e5fe5d4 100644 --- a/desktop/scripts/paths.tsx +++ b/desktop/scripts/paths.tsx @@ -10,6 +10,7 @@ import path from 'path'; export const rootDir = path.resolve(__dirname, '..'); +export const sonarDir = path.resolve(__dirname, '..', '..'); export const appDir = path.join(rootDir, 'app'); export const browserUiDir = path.join(rootDir, 'flipper-ui-browser'); export const staticDir = path.join(rootDir, 'static'); From 2318bffd076d38893b4a0b10fbf3910f03a8f8f0 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Mon, 27 Nov 2023 06:23:06 -0800 Subject: [PATCH 100/112] Built artifact is already universal, no architecture is needed Summary: ^ Reviewed By: antonk52 Differential Revision: D51568857 fbshipit-source-id: 124d77e4dd175a13f491f8242b3a28a898ff5670 --- desktop/scripts/build-flipper-server-release.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/desktop/scripts/build-flipper-server-release.tsx b/desktop/scripts/build-flipper-server-release.tsx index 15ffa83f3..d7100e652 100644 --- a/desktop/scripts/build-flipper-server-release.tsx +++ b/desktop/scripts/build-flipper-server-release.tsx @@ -854,20 +854,12 @@ async function setUpMacBundle( await installNodeBinary(nodeBinaryFile, platform); if (isFB) { - const {BuildArchitecture, buildFlipperServer} = await import( + const {buildFlipperServer} = await import( // @ts-ignore only used inside Meta './fb/build-flipper-server-macos' ); - const architecture = - platform === BuildPlatform.MAC_AARCH64 - ? BuildArchitecture.MAC_AARCH64 - : BuildArchitecture.MAC_X64; - const outputPath = await buildFlipperServer( - architecture, - versionNumber, - false, - ); + const outputPath = await buildFlipperServer(versionNumber, false); console.log( `⚙️ Successfully built platform: ${platform}, output: ${outputPath}`, ); From ce5527513e014a511cabe37c9b9e9e4594bb2e2f Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Mon, 27 Nov 2023 08:42:12 -0800 Subject: [PATCH 101/112] only simulators in "Virtual device" Summary: Currently if you plug in a physical iOS device via USB, flipper will show it to you in "Virtual device menu". See "ak iphone mini" in the test plan. This diff ensures that we only show virtual device in this menu. Reviewed By: lblasa Differential Revision: D51588117 fbshipit-source-id: e2853a6b34ed3fe6d821a9c504b9ffd19e4074fa --- .../src/sandy-chrome/appinspect/LaunchEmulator.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx index 2bf708c89..185ed421f 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx @@ -122,8 +122,12 @@ export const LaunchEmulatorDialog = withTrackingScope( 'ios-get-simulators', false, ); + + const nonPhysical = simulators.filter( + (simulator) => simulator.type !== 'physical', + ); setWaitingForIos(false); - setIosEmulators(simulators); + setIosEmulators(nonPhysical); } catch (error) { console.warn('Failed to find iOS simulators', error); setiOSMessage(`Error: ${error.message ?? error} \nRetrying...`); From a9c5dd746a8d904ea0c645617aac45d852c8f672 Mon Sep 17 00:00:00 2001 From: Pascal Hartig Date: Mon, 27 Nov 2023 09:04:07 -0800 Subject: [PATCH 102/112] Store plugin ID Summary: We need the build ID from GraphQL to fetch the download VPN off-VPN. Changelog: Plugin Marketplace now stores a unique ID per build Reviewed By: LukeDefeo Differential Revision: D51525419 fbshipit-source-id: d6740b3c4724d15cc996155b6e7581b0f16a9da6 --- desktop/flipper-common/src/PluginDetails.tsx | 1 + desktop/flipper-ui-core/src/utils/testUtils.tsx | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/desktop/flipper-common/src/PluginDetails.tsx b/desktop/flipper-common/src/PluginDetails.tsx index c7a400e9c..ed8d88624 100644 --- a/desktop/flipper-common/src/PluginDetails.tsx +++ b/desktop/flipper-common/src/PluginDetails.tsx @@ -82,6 +82,7 @@ export type ActivatablePluginDetails = InstalledPluginDetails; // Describes plugin available for downloading. Until downloaded to the disk it is not available for activation in Flipper. export interface DownloadablePluginDetails extends ConcretePluginDetails { isActivatable: false; + buildId: string; downloadUrl: string; lastUpdated: Date; // Indicates whether plugin should be enabled by default for new users diff --git a/desktop/flipper-ui-core/src/utils/testUtils.tsx b/desktop/flipper-ui-core/src/utils/testUtils.tsx index 6dfc57add..4b89fb69a 100644 --- a/desktop/flipper-ui-core/src/utils/testUtils.tsx +++ b/desktop/flipper-ui-core/src/utils/testUtils.tsx @@ -24,8 +24,17 @@ export function createMockDownloadablePluginDetails( lastUpdated?: Date; } = {}, ): DownloadablePluginDetails { - const {id, version, title, flipperEngineVersion, gatekeeper, lastUpdated} = { + const { + id, + buildId, + version, + title, + flipperEngineVersion, + gatekeeper, + lastUpdated, + } = { id: 'test', + buildId: '1337', version: '3.0.1', flipperEngineVersion: '0.46.0', lastUpdated: new Date(1591226525 * 1000), @@ -36,6 +45,7 @@ export function createMockDownloadablePluginDetails( const details: DownloadablePluginDetails = { name: name || `flipper-plugin-${lowercasedID}`, id: id, + buildId, bugs: { email: 'bugs@localhost', url: 'bugs.localhost', From 244573abe344bdb1930e73a026c3921ae13f4d8e Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Mon, 27 Nov 2023 15:33:57 -0800 Subject: [PATCH 103/112] Back out "remove metro button" Summary: Original commit changeset: ebd844a57b0b Original Phabricator Diff: D51199560 Reviewed By: lblasa Differential Revision: D51586734 fbshipit-source-id: dfb37d4a6f6bd03da96ab17cd4af8e43de2b0f07 --- .../src/chrome/MetroButton.tsx | 94 +++++++++++++++++++ .../sandy-chrome/appinspect/AppInspect.tsx | 6 ++ 2 files changed, 100 insertions(+) create mode 100644 desktop/flipper-ui-core/src/chrome/MetroButton.tsx diff --git a/desktop/flipper-ui-core/src/chrome/MetroButton.tsx b/desktop/flipper-ui-core/src/chrome/MetroButton.tsx new file mode 100644 index 000000000..54652960a --- /dev/null +++ b/desktop/flipper-ui-core/src/chrome/MetroButton.tsx @@ -0,0 +1,94 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React, {useCallback, useEffect, useState} from 'react'; +import {MetroReportableEvent} from 'flipper-common'; +import {useStore} from '../utils/useStore'; +import {Button as AntButton} from 'antd'; +import {MenuOutlined, ReloadOutlined} from '@ant-design/icons'; +import {theme} from 'flipper-plugin'; +import {BaseDevice} from 'flipper-frontend-core'; + +export default function MetroButton() { + const device = useStore((state) => + state.connections.devices.find( + (device) => device.os === 'Metro' && device.connected.get(), + ), + ) as BaseDevice | undefined; + + const sendCommand = useCallback( + (command: string) => { + device?.sendMetroCommand(command); + }, + [device], + ); + const [progress, setProgress] = useState(1); + const [_hasBuildError, setHasBuildError] = useState(false); + + useEffect(() => { + if (!device) { + return; + } + function metroEventListener(event: MetroReportableEvent) { + if (event.type === 'bundle_build_started') { + setHasBuildError(false); + setProgress(0); + } else if (event.type === 'bundle_build_failed') { + setHasBuildError(true); + setProgress(1); + } else if (event.type === 'bundle_build_done') { + setHasBuildError(false); + setProgress(1); + } else if (event.type === 'bundle_transform_progressed') { + setProgress(event.transformedFileCount / event.totalFileCount); + } + } + + const handle = device.addLogListener((l) => { + if (l.tag !== 'client_log') { + try { + metroEventListener(JSON.parse(l.message)); + } catch (e) { + console.warn('Failed to parse metro message: ', l, e); + } + } + }); + + return () => { + device.removeLogListener(handle); + }; + }, [device]); + + if (!device) { + return null; + } + + return ( + <> + } + title="Reload React Native App" + type="ghost" + onClick={() => { + sendCommand('reload'); + }} + loading={progress < 1} + style={{color: _hasBuildError ? theme.errorColor : undefined}} + /> + } + title="Open the React Native Dev Menu on the device" + type="ghost" + onClick={() => { + sendCommand('devMenu'); + }} + /> + + ); +} diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx index b54caee10..ce9aa6586 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx @@ -13,6 +13,7 @@ import {LeftSidebar, SidebarTitle} from '../LeftSidebar'; import {Layout, styled} from '../../ui'; import {theme, useValue} from 'flipper-plugin'; import {PluginList} from './PluginList'; +import MetroButton from '../../chrome/MetroButton'; import {BookmarkSection} from './BookmarkSection'; import Client from '../../Client'; import {BaseDevice} from 'flipper-frontend-core'; @@ -41,6 +42,11 @@ export function AppInspect() { {isDeviceConnected && isAppConnected && } + {isDeviceConnected && activeDevice && ( + + + + )}
From d8f507dba0be5e4bcd7e163ccf1c49126c7df37e Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Mon, 27 Nov 2023 15:33:57 -0800 Subject: [PATCH 104/112] Back out "remove all mentions of metro from flipper-ui-core" Summary: Original commit changeset: edc33c2b989e Original Phabricator Diff: D51200129 Reviewed By: lblasa Differential Revision: D51586736 fbshipit-source-id: e81355f7a0f0533ebc47f4cfc6c40af4865dbad0 --- .../reducers/__tests__/connections.node.tsx | 71 ++++- .../sandy-chrome/appinspect/AppInspect.tsx | 13 +- .../sandy-chrome/appinspect/PluginList.tsx | 45 +++- .../appinspect/__tests__/PluginList.spec.tsx | 244 +++++++++++++++++- .../src/selectors/connections.tsx | 15 +- .../flipper-ui-core/src/utils/pluginUtils.tsx | 31 ++- 6 files changed, 406 insertions(+), 13 deletions(-) diff --git a/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx b/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx index e4339f836..02ff3095a 100644 --- a/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx +++ b/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx @@ -56,6 +56,39 @@ test('doing a double REGISTER_DEVICE fails', () => { }).toThrow('still connected'); }); +test('register, remove, re-register a metro device works correctly', () => { + const device1 = new TestDevice( + 'http://localhost:8081', + 'emulator', + 'React Native', + 'Metro', + ); + let state: State = reducer(undefined, { + type: 'REGISTER_DEVICE', + payload: device1, + }); + expect(state.devices.length).toBe(1); + expect(state.devices[0].displayTitle()).toBe('React Native'); + + device1.disconnect(); + + expect(state.devices.length).toBe(1); + expect(state.devices[0].displayTitle()).toBe('React Native (Offline)'); + + state = reducer(state, { + type: 'REGISTER_DEVICE', + payload: new TestDevice( + 'http://localhost:8081', + 'emulator', + 'React Native', + 'Metro', + ), + }); + expect(state.devices.length).toBe(1); + expect(state.devices[0].displayTitle()).toBe('React Native'); + expect(state.devices[0]).not.toBe(device1); +}); + test('selectPlugin sets deepLinkPayload correctly', () => { const device1 = new TestDevice( 'http://localhost:8081', @@ -200,8 +233,10 @@ describe('selection changes', () => { let device1: BaseDevice; let device2: BaseDevice; + let metroDevice: BaseDevice; let d1app1: Client; let d1app2: Client; + let d2app1: Client; let d2app2: Client; let store: Store; let mockFlipper: MockFlipperResult; @@ -214,8 +249,13 @@ describe('selection changes', () => { device1 = mockFlipper.device; device2 = mockFlipper.createDevice({}); + metroDevice = mockFlipper.createDevice({ + os: 'Metro', + serial: 'http://localhost:8081', + }); d1app1 = mockFlipper.client; d1app2 = await mockFlipper.createClient(device1, 'd1app2'); + d2app1 = await mockFlipper.createClient(device2, 'd2app1'); d2app2 = await mockFlipper.createClient(device2, 'd2app2'); store = mockFlipper.store; }); @@ -279,6 +319,31 @@ describe('selection changes', () => { expect(getActiveDevice(store.getState())).toBe(device1); }); + test('select a metro device', async () => { + store.dispatch( + selectPlugin({ + selectedPlugin: DevicePlugin1.id, + selectedDevice: metroDevice, + selectedAppId: d2app1.id, // this app will determine the active device + }), + ); + + const state = store.getState(); + expect(state.connections).toMatchObject({ + selectedDevice: metroDevice, + selectedPlugin: DevicePlugin1.id, + selectedAppId: d2app1.id, + userPreferredDevice: metroDevice.title, + // other prefs not updated + userPreferredPlugin: DevicePlugin1.id, + userPreferredApp: d2app1.query.app, + }); + + // used by plugin list, to keep main device / app selection correct + expect(getActiveClient(state)).toBe(d2app1); + expect(getActiveDevice(state)).toBe(device2); + }); + test('introducing new client does not select it', async () => { await mockFlipper.createClient(device2, 'd2app3'); expect(store.getState().connections).toMatchObject({ @@ -336,12 +401,12 @@ describe('selection changes', () => { }); test('select device', () => { - store.dispatch(selectDevice(device1)); + store.dispatch(selectDevice(metroDevice)); expect(store.getState().connections).toMatchObject({ - selectedDevice: device1, + selectedDevice: metroDevice, selectedPlugin: TestPlugin1.id, selectedAppId: null, - userPreferredDevice: device1.title, + userPreferredDevice: metroDevice.title, // other prefs not updated userPreferredPlugin: TestPlugin1.id, userPreferredApp: d1app1.query.app, diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx index ce9aa6586..72dcbb6dd 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx @@ -19,13 +19,18 @@ import Client from '../../Client'; import {BaseDevice} from 'flipper-frontend-core'; import {ExclamationCircleOutlined, FieldTimeOutlined} from '@ant-design/icons'; import {useSelector} from 'react-redux'; -import {getActiveClient, getActiveDevice} from '../../selectors/connections'; +import { + getActiveClient, + getActiveDevice, + getMetroDevice, +} from '../../selectors/connections'; import * as connections from '../../selectors/connections'; import {PluginActionsMenu} from '../../chrome/PluginActionsMenu'; const {Text} = Typography; export function AppInspect() { + const metroDevice = useSelector(getMetroDevice); const client = useSelector(getActiveClient); const activeDevice = useSelector(getActiveDevice); const isDeviceConnected = useValue(activeDevice?.connected, false); @@ -51,7 +56,11 @@ export function AppInspect() {
{activeDevice ? ( - + ) : null} diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/PluginList.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/PluginList.tsx index 0d74fcee1..7f2b2bd20 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/PluginList.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/PluginList.tsx @@ -42,9 +42,11 @@ const {Text} = Typography; export const PluginList = memo(function PluginList({ client, activeDevice, + metroDevice, }: { client: Client | null; activeDevice: BaseDevice | null; + metroDevice: BaseDevice | null; }) { const dispatch = useDispatch(); const connections = useStore((state) => state.connections); @@ -52,9 +54,11 @@ export const PluginList = memo(function PluginList({ const pluginLists = useSelector(getPluginLists); const downloads = useStore((state) => state.pluginDownloads); const isConnected = useValue(activeDevice?.connected, false); + const metroConnected = useValue(metroDevice?.connected, false); const { devicePlugins, + metroPlugins, enabledPlugins, disabledPlugins, unavailablePlugins, @@ -92,6 +96,19 @@ export const PluginList = memo(function PluginList({ }, [dispatch, activeDevice, connections.selectedAppId], ); + const handleMetroPluginClick = useCallback( + (pluginId) => { + dispatch( + selectPlugin({ + selectedPlugin: pluginId, + selectedAppId: connections.selectedAppId, + deepLinkPayload: null, + selectedDevice: metroDevice, + }), + ); + }, + [dispatch, metroDevice, connections.selectedAppId], + ); const handleEnablePlugin = useCallback( (id: string) => { const plugin = (plugins.clientPlugins.get(id) ?? @@ -190,15 +207,39 @@ export const PluginList = memo(function PluginList({ {}} - defaultOpenKeys={['enabled']} + defaultOpenKeys={['enabled', 'metro']} selectedKeys={ - connections.selectedPlugin ? [connections.selectedPlugin] : [] + connections.selectedPlugin + ? [ + (connections.selectedDevice === metroDevice ? 'metro:' : '') + + connections.selectedPlugin, + ] + : [] } mode="inline"> {allEnabledPlugins} + {!isArchived && metroConnected && ( + + {metroPlugins.map((plugin) => ( + + ))} + + )} {isConnected && ( {} @@ -39,3 +63,221 @@ describe('basic getActiveDevice', () => { expect(getActiveDevice(store.getState())).toBe(device); }); }); + +describe('basic getActiveDevice with metro present', () => { + let flipper: MockFlipperResult; + let metro: BaseDevice; + let testDevice: BaseDevice; + + beforeEach(async () => { + flipper = await createMockFlipperWithPlugin(logsPlugin); + flipper.device.supportsPlugin = (p) => { + return p.id !== 'unsupportedDevicePlugin'; + }; + testDevice = flipper.device; + // flipper.store.dispatch(registerPlugins([LogsPlugin])) + flipper.store.dispatch({ + type: 'REGISTER_DEVICE', + payload: new TestDevice( + 'http://localhost:8081', + 'physical', + 'metro', + 'Metro', + ), + }); + metro = getMetroDevice(flipper.store.getState())!; + metro.supportsPlugin = (p) => { + return p.id !== 'unsupportedDevicePlugin'; + }; + }); + + test('findMetroDevice', () => { + expect(metro.os).toBe('Metro'); + }); + + test('correct base selection state', () => { + const state = flipper.store.getState(); + const {connections} = state; + expect(connections).toMatchObject({ + devices: [testDevice, metro], + selectedDevice: testDevice, + selectedPlugin: 'DeviceLogs', + userPreferredDevice: 'MockAndroidDevice', + userPreferredPlugin: 'DeviceLogs', + userPreferredApp: 'TestApp', + }); + expect(getActiveClient(state)).toBe(flipper.client); + }); + + test('selecting Metro Logs works but keeps normal device preferred', () => { + expect(getActiveClient(flipper.store.getState())).toBe(flipper.client); + flipper.store.dispatch( + selectPlugin({ + selectedPlugin: logsPlugin.id, + selectedAppId: flipper.client.id, + selectedDevice: metro, + deepLinkPayload: null, + }), + ); + expect(flipper.store.getState().connections).toMatchObject({ + devices: [testDevice, metro], + selectedAppId: 'TestApp#Android#MockAndroidDevice#serial', + selectedDevice: metro, + selectedPlugin: 'DeviceLogs', + userPreferredDevice: 'MockAndroidDevice', // Not metro! + userPreferredPlugin: 'DeviceLogs', + userPreferredApp: 'TestApp', + }); + const state = flipper.store.getState(); + // find best device is still metro + expect(getActiveDevice(state)).toBe(testDevice); + // find best client still returns app + expect(getActiveClient(state)).toBe(flipper.client); + }); + + test('computePluginLists', () => { + const state = flipper.store.getState(); + expect(getPluginLists(state)).toEqual({ + downloadablePlugins: [], + devicePlugins: [logsPlugin], + metroPlugins: [logsPlugin], + enabledPlugins: [], + disabledPlugins: [], + unavailablePlugins: [], + }); + }); + + test('computePluginLists with problematic plugins', () => { + const noopPlugin = { + plugin() {}, + Component() { + return null; + }, + }; + + const unsupportedDevicePlugin = new _SandyPluginDefinition( + createMockPluginDetails({ + id: 'unsupportedDevicePlugin', + title: 'Unsupported Device Plugin', + }), + { + devicePlugin() { + return {}; + }, + supportsDevice() { + return false; + }, + Component() { + return null; + }, + }, + ); + const unsupportedPlugin = new _SandyPluginDefinition( + createMockPluginDetails({ + id: 'unsupportedPlugin', + title: 'Unsupported Plugin', + }), + noopPlugin, + ); + + const gateKeepedPlugin = createMockPluginDetails({ + id: 'gateKeepedPlugin', + title: 'Gatekeeped Plugin', + gatekeeper: 'not for you', + }); + + const plugin1 = new _SandyPluginDefinition( + createMockPluginDetails({ + id: 'plugin1', + title: 'Plugin 1', + }), + { + plugin() {}, + Component() { + return null; + }, + }, + ); + + const plugin2 = new _SandyPluginDefinition( + createMockPluginDetails({ + id: 'plugin2', + title: 'Plugin 2', + }), + noopPlugin, + ); + + const supportedDownloadablePlugin = createMockDownloadablePluginDetails({ + id: 'supportedUninstalledPlugin', + title: 'Supported Uninstalled Plugin', + }); + + const unsupportedDownloadablePlugin = createMockDownloadablePluginDetails({ + id: 'unsupportedUninstalledPlugin', + title: 'Unsupported Uninstalled Plugin', + }); + + flipper.store.dispatch( + registerPlugins([ + unsupportedDevicePlugin, + unsupportedPlugin, + plugin1, + plugin2, + ]), + ); + flipper.store.dispatch(addGatekeepedPlugins([gateKeepedPlugin])); + flipper.store.dispatch( + registerMarketplacePlugins([ + supportedDownloadablePlugin, + unsupportedDownloadablePlugin, + ]), + ); + + // ok, this is a little hackish + flipper.client.plugins = new Set([ + 'plugin1', + 'plugin2', + 'supportedUninstalledPlugin', + ]); + + let state = flipper.store.getState(); + const pluginLists = getPluginLists(state); + expect(pluginLists).toEqual({ + devicePlugins: [logsPlugin], + metroPlugins: [logsPlugin], + enabledPlugins: [], + disabledPlugins: [plugin1, plugin2], + unavailablePlugins: [ + [ + gateKeepedPlugin, + "Plugin 'Gatekeeped Plugin' is only available to members of gatekeeper 'not for you'", + ], + [ + unsupportedDevicePlugin.details, + "Device plugin 'Unsupported Device Plugin' is not supported by the selected device 'MockAndroidDevice' (Android)", + ], + [ + unsupportedPlugin.details, + "Plugin 'Unsupported Plugin' is not supported by the selected application 'TestApp' (Android)", + ], + [ + unsupportedDownloadablePlugin, + "Plugin 'Unsupported Uninstalled Plugin' is not supported by the selected application 'TestApp' (Android) and not installed in Flipper", + ], + ], + downloadablePlugins: [supportedDownloadablePlugin], + }); + + flipper.store.dispatch( + switchPlugin({ + plugin: plugin2, + selectedApp: flipper.client.query.app, + }), + ); + state = flipper.store.getState(); + expect(getPluginLists(state)).toMatchObject({ + enabledPlugins: [plugin2], + disabledPlugins: [plugin1], + }); + }); +}); diff --git a/desktop/flipper-ui-core/src/selectors/connections.tsx b/desktop/flipper-ui-core/src/selectors/connections.tsx index 99ca68c45..e5a5a7e8f 100644 --- a/desktop/flipper-ui-core/src/selectors/connections.tsx +++ b/desktop/flipper-ui-core/src/selectors/connections.tsx @@ -26,6 +26,13 @@ const getPluginDownloads = (state: State) => state.pluginDownloads; export const getActiveClient = (state: State) => state.connections.clients.get(state.connections.selectedAppId!) ?? null; +export const getMetroDevice = createSelector(getDevices, (devices) => { + return ( + devices.find((device) => device.os === 'Metro' && !device.isArchived) ?? + null + ); +}); + export const getSelectableDevices = createSelector( getDevices, getClients, @@ -48,7 +55,12 @@ export const hasSelectableDevices = createSelector( export const getActiveDevice = createSelector( getSelectedDevice, getActiveClient, - (selectedDevice, client) => { + getMetroDevice, + (selectedDevice, client, metroDevice) => { + // if not Metro device, use the selected device as metro device + if (selectedDevice !== metroDevice) { + return selectedDevice; + } // if there is an active app, use device owning the app if (client) { // TODO: Will be fixed later in the stack @@ -90,6 +102,7 @@ export const getPluginLists = createSelector( failedPlugins, }), getActiveDevice, + getMetroDevice, getActiveClient, computePluginLists, ); diff --git a/desktop/flipper-ui-core/src/utils/pluginUtils.tsx b/desktop/flipper-ui-core/src/utils/pluginUtils.tsx index b702e0205..3f53ff33e 100644 --- a/desktop/flipper-ui-core/src/utils/pluginUtils.tsx +++ b/desktop/flipper-ui-core/src/utils/pluginUtils.tsx @@ -25,6 +25,7 @@ import {getAppVersion} from './info'; export type PluginLists = { devicePlugins: PluginDefinition[]; + metroPlugins: PluginDefinition[]; enabledPlugins: PluginDefinition[]; disabledPlugins: PluginDefinition[]; unavailablePlugins: [plugin: PluginDetails, reason: string][]; @@ -169,9 +170,11 @@ export function computePluginLists( | 'clientPlugins' >, device: BaseDevice | null, + metroDevice: BaseDevice | null, client: Client | null, ): { devicePlugins: PluginDefinition[]; + metroPlugins: PluginDefinition[]; enabledPlugins: PluginDefinition[]; disabledPlugins: PluginDefinition[]; unavailablePlugins: [plugin: PluginDetails, reason: string][]; @@ -186,19 +189,26 @@ export function computePluginLists( const devicePlugins: PluginDefinition[] = [...plugins.devicePlugins.values()] .filter((p) => device?.supportsPlugin(p)) .filter((p) => enabledDevicePluginsState.has(p.id)); + const metroPlugins: PluginDefinition[] = [...plugins.devicePlugins.values()] + .filter((p) => metroDevice?.supportsPlugin(p)) + .filter((p) => enabledDevicePluginsState.has(p.id)); const enabledPlugins: PluginDefinition[] = []; const disabledPlugins: PluginDefinition[] = [ ...plugins.devicePlugins.values(), ] - .filter((p) => device?.supportsPlugin(p.details)) + .filter( + (p) => + device?.supportsPlugin(p.details) || + metroDevice?.supportsPlugin(p.details), + ) .filter((p) => !enabledDevicePluginsState.has(p.id)); const unavailablePlugins: [plugin: PluginDetails, reason: string][] = []; const downloadablePlugins: DownloadablePluginDetails[] = []; if (device) { - // find all device plugins that aren't part of the current device + // find all device plugins that aren't part of the current device / metro for (const p of plugins.devicePlugins.values()) { - if (!device.supportsPlugin(p)) { + if (!device.supportsPlugin(p) && !metroDevice?.supportsPlugin(p)) { unavailablePlugins.push([ p.details, `Device plugin '${getPluginTitle( @@ -212,7 +222,10 @@ export function computePluginLists( for (const plugin of uninstalledMarketplacePlugins.filter( (d) => d.pluginType === 'device', )) { - if (device.supportsPlugin(plugin)) { + if ( + device.supportsPlugin(plugin) || + metroDevice?.supportsPlugin(plugin) + ) { downloadablePlugins.push(plugin); } } @@ -314,6 +327,7 @@ export function computePluginLists( enabledPlugins.sort(sortPluginsByName); devicePlugins.sort(sortPluginsByName); disabledPlugins.sort(sortPluginsByName); + metroPlugins.sort(sortPluginsByName); unavailablePlugins.sort(([a], [b]) => { return getPluginTitle(a) > getPluginTitle(b) ? 1 : -1; }); @@ -323,6 +337,7 @@ export function computePluginLists( return { devicePlugins, + metroPlugins, enabledPlugins, disabledPlugins, unavailablePlugins, @@ -356,6 +371,7 @@ function getFavoritePlugins( export function computeActivePluginList({ enabledPlugins, devicePlugins, + metroPlugins, disabledPlugins, downloadablePlugins, unavailablePlugins, @@ -375,6 +391,13 @@ export function computeActivePluginList({ definition: plugin, }; } + for (const plugin of metroPlugins) { + pluginList[plugin.id] = { + status: 'enabled', + details: plugin.details, + definition: plugin, + }; + } for (const plugin of disabledPlugins) { pluginList[plugin.id] = { status: 'disabled', From 4e10bb1b431489a167a01d84889c18050bf0b3b7 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Mon, 27 Nov 2023 15:33:57 -0800 Subject: [PATCH 105/112] Back out "remove shortcuts" Summary: Original commit changeset: fbb59b416f92 Original Phabricator Diff: D51159922 Reviewed By: lblasa Differential Revision: D51586735 fbshipit-source-id: 3317b590ec72e0040a67be41e74ddb5a72722598 --- desktop/flipper-common/src/settings.tsx | 7 +++ .../src/utils/settings.tsx | 7 +++ .../src/chrome/SettingsSheet.tsx | 56 ++++++++++++++++++ .../flipper-ui-core/src/dispatcher/index.tsx | 2 + .../src/dispatcher/reactNative.tsx | 58 +++++++++++++++++++ desktop/scripts/jest-setup-after.tsx | 3 + 6 files changed, 133 insertions(+) create mode 100644 desktop/flipper-ui-core/src/dispatcher/reactNative.tsx diff --git a/desktop/flipper-common/src/settings.tsx b/desktop/flipper-common/src/settings.tsx index 14d2ae646..9568d7680 100644 --- a/desktop/flipper-common/src/settings.tsx +++ b/desktop/flipper-common/src/settings.tsx @@ -29,6 +29,13 @@ export type Settings = { */ enablePrefetching: Tristate; idbPath: string; + reactNative: { + shortcuts: { + enabled: boolean; + reload: string; + openDevMenu: string; + }; + }; darkMode: 'dark' | 'light' | 'system'; showWelcomeAtStartup: boolean; suppressPluginErrors: boolean; diff --git a/desktop/flipper-server-core/src/utils/settings.tsx b/desktop/flipper-server-core/src/utils/settings.tsx index 47a808dd6..bbc75869c 100644 --- a/desktop/flipper-server-core/src/utils/settings.tsx +++ b/desktop/flipper-server-core/src/utils/settings.tsx @@ -57,6 +57,13 @@ async function getDefaultSettings(): Promise { enablePhysicalIOS: os.platform() === 'darwin', enablePrefetching: Tristate.Unset, idbPath: '/usr/local/bin/idb', + reactNative: { + shortcuts: { + enabled: false, + reload: 'Alt+Shift+R', + openDevMenu: 'Alt+Shift+D', + }, + }, darkMode: 'light', showWelcomeAtStartup: true, suppressPluginErrors: false, diff --git a/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx b/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx index 373bf7fe5..96cf0a942 100644 --- a/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx +++ b/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx @@ -23,6 +23,7 @@ import { ConfigText, URLConfigField, } from './settings/configFields'; +import KeyboardShortcutInput from './settings/KeyboardShortcutInput'; import {isEqual, isMatch, isEmpty} from 'lodash'; import LauncherSettingsPanel from '../fb-stubs/LauncherSettingsPanel'; import { @@ -123,6 +124,7 @@ class SettingsSheet extends Component { enablePhysicalIOS, enablePrefetching, idbPath, + reactNative, darkMode, suppressPluginErrors, persistDeviceData, @@ -292,6 +294,60 @@ class SettingsSheet extends Component { Use System Setting + { + this.setState((prevState) => ({ + updatedSettings: { + ...prevState.updatedSettings, + reactNative: { + ...prevState.updatedSettings.reactNative, + shortcuts: { + ...prevState.updatedSettings.reactNative.shortcuts, + enabled, + }, + }, + }, + })); + }}> + { + this.setState((prevState) => ({ + updatedSettings: { + ...prevState.updatedSettings, + reactNative: { + ...prevState.updatedSettings.reactNative, + shortcuts: { + ...prevState.updatedSettings.reactNative.shortcuts, + reload, + }, + }, + }, + })); + }} + /> + { + this.setState((prevState) => ({ + updatedSettings: { + ...prevState.updatedSettings, + reactNative: { + ...prevState.updatedSettings.reactNative, + shortcuts: { + ...prevState.updatedSettings.reactNative.shortcuts, + openDevMenu, + }, + }, + }, + })); + }} + /> + { + const settings = store.getState().settingsState.reactNative; + + if (!settings?.shortcuts.enabled) { + return; + } + + const shortcuts: ShortcutEventCommand[] = [ + settings.shortcuts.reload && { + shortcut: settings.shortcuts.reload, + command: 'reload', + }, + settings.shortcuts.openDevMenu && { + shortcut: settings.shortcuts.openDevMenu, + command: 'devMenu', + }, + ]; + + shortcuts.forEach( + (shortcut: ShortcutEventCommand) => + shortcut && + shortcut.shortcut && + registerShortcut(shortcut.shortcut, () => { + const devices = store + .getState() + .connections.devices.filter( + (device) => device.os === 'Metro' && !device.isArchived, + ); + + devices.forEach((device) => + device.flipperServer.exec( + 'metro-command', + device.serial, + shortcut.command, + ), + ); + }), + ); +}; diff --git a/desktop/scripts/jest-setup-after.tsx b/desktop/scripts/jest-setup-after.tsx index 20e2eafc9..57a3066e7 100644 --- a/desktop/scripts/jest-setup-after.tsx +++ b/desktop/scripts/jest-setup-after.tsx @@ -184,6 +184,9 @@ function createStubRenderHost(): RenderHost { enablePhysicalIOS: false, enablePrefetching: Tristate.False, idbPath: `/dev/null`, + reactNative: { + shortcuts: {enabled: false, openDevMenu: '', reload: ''}, + }, showWelcomeAtStartup: false, suppressPluginErrors: false, persistDeviceData: false, From 3b1763bd7dc9e52563ce3e3b52186d1c8bb21692 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 28 Nov 2023 03:40:23 -0800 Subject: [PATCH 106/112] explicit instructions for idb Summary: Add explicit instructions how to install idb internally Reviewed By: LukeDefeo Differential Revision: D51600754 fbshipit-source-id: c40d5d07c4cb570a7c51699ce0e4b8a67c506147 --- desktop/doctor/src/fb-stubs/messages.tsx | 8 ++++++-- desktop/doctor/src/index.tsx | 16 ++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/desktop/doctor/src/fb-stubs/messages.tsx b/desktop/doctor/src/fb-stubs/messages.tsx index 5ef437295..7f6dfb1e1 100644 --- a/desktop/doctor/src/fb-stubs/messages.tsx +++ b/desktop/doctor/src/fb-stubs/messages.tsx @@ -7,8 +7,12 @@ * @format */ -export const getIdbInstallationInstructions = (idbPath: string) => - `IDB is required to use Flipper with iOS devices. It can be installed from https://github.com/facebook/idb and configured in Flipper settings. You can also disable physical iOS device support in settings. Current setting: ${idbPath} isn't a valid IDB installation.`; +export const getIdbInstallationInstructions = ( + idbPath: string, +): {message: string; commands: {title: string; command: string}[]} => ({ + message: `IDB is required to use Flipper with iOS devices. It can be installed from https://github.com/facebook/idb and configured in Flipper settings. You can also disable physical iOS device support in settings. Current setting: ${idbPath} isn't a valid IDB installation.`, + commands: [], +}); export const installXcode = 'Install Xcode from the App Store or download it from https://developer.apple.com'; diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index 6f433e5b0..f6b7b6376 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -288,13 +288,17 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { const result = await tryExecuteCommand( `${settings?.idbPath} --help`, ); - const hasProblem = result.hasProblem; - const message = hasProblem - ? getIdbInstallationInstructions(settings.idbPath) - : 'Flipper is configured to use your IDB installation.'; + if (result.hasProblem) { + return { + hasProblem: true, + ...getIdbInstallationInstructions(settings.idbPath), + }; + } + return { - hasProblem, - message, + hasProblem: false, + message: + 'Flipper is configured to use your IDB installation.', }; }, }, From 1a4c602fd8b895dce01c6008d6366240db3823dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 03:47:27 -0800 Subject: [PATCH 107/112] Bump serde from 1.0.188 to 1.0.193 in /packer (#5311) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Bumps [serde](https://github.com/serde-rs/serde) from 1.0.188 to 1.0.193.
Release notes

Sourced from serde's releases.

v1.0.193

v1.0.192

v1.0.191

  • Documentation improvements

v1.0.190

v1.0.189

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde&package-manager=cargo&previous-version=1.0.188&new-version=1.0.193)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `dependabot rebase` will rebase this PR - `dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `dependabot merge` will merge this PR after your CI passes on it - `dependabot squash and merge` will squash and merge this PR after your CI passes on it - `dependabot cancel merge` will cancel a previously requested merge and block automerging - `dependabot reopen` will reopen this PR if it is closed - `dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Pull Request resolved: https://github.com/facebook/flipper/pull/5311 Reviewed By: mweststrate Differential Revision: D51588875 Pulled By: passy fbshipit-source-id: 14d7861ca16790b353f4a9d3bfd09750366080ea --- packer/Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packer/Cargo.lock b/packer/Cargo.lock index 6749dd28a..d87d79ba2 100644 --- a/packer/Cargo.lock +++ b/packer/Cargo.lock @@ -661,18 +661,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", From ba6133e38a4420154d95285d834e41439f2acf51 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 28 Nov 2023 04:39:59 -0800 Subject: [PATCH 108/112] Ask to restart flipper to pick up ios SDK Summary: Running a check is run it only executes the `run` function. The `run` function for `ios.sdk` is a pure function that detects if ios SDK available from `EnvironmentInfo`. This info is picked up when flipper first started. Even if you installed ios SDK flipper won't pick it up while running. Restarting flipper will pick up the SDK and make it available. We should be clear about it. Reviewed By: mweststrate Differential Revision: D51614258 fbshipit-source-id: 8b2a8b5b94ec0038acf9f383e55e166c52b2ccab --- desktop/doctor/src/fb-stubs/messages.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/doctor/src/fb-stubs/messages.tsx b/desktop/doctor/src/fb-stubs/messages.tsx index 7f6dfb1e1..77008f47d 100644 --- a/desktop/doctor/src/fb-stubs/messages.tsx +++ b/desktop/doctor/src/fb-stubs/messages.tsx @@ -18,7 +18,7 @@ export const installXcode = 'Install Xcode from the App Store or download it from https://developer.apple.com'; export const installSDK = - 'You can install it using Xcode (https://developer.apple.com/xcode/).'; + 'You can install it using Xcode (https://developer.apple.com/xcode/). Once installed, restart flipper.'; export const installAndroidStudio = [ 'Android Studio is not installed.', From f24d7dbf6a36ff4eb188eb5c48236cbc0db89093 Mon Sep 17 00:00:00 2001 From: Richard Zito Date: Tue, 28 Nov 2023 04:46:33 -0800 Subject: [PATCH 109/112] CKComponentViewContext -> RCComponentViewContext Summary: This lives in RenderCore, it should have an RC prefix. Differential Revision: D51586116 fbshipit-source-id: 102c71f68d6505d34b5a4aac2bd97a49dcaf8408 --- .../SKComponentLayoutDescriptor.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutComponentKitSupport/SKComponentLayoutDescriptor.mm b/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutComponentKitSupport/SKComponentLayoutDescriptor.mm index dfc18b098..a0eae87a2 100644 --- a/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutComponentKitSupport/SKComponentLayoutDescriptor.mm +++ b/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutComponentKitSupport/SKComponentLayoutDescriptor.mm @@ -200,7 +200,7 @@ static std::vector& attributeGenerators() { forNode:(SKComponentLayoutWrapper*)node { SKHighlightOverlay* overlay = [SKHighlightOverlay sharedInstance]; if (highlighted) { - CKComponentViewContext viewContext = node.component.viewContext; + RCComponentViewContext viewContext = node.component.viewContext; [overlay mountInView:viewContext.view withFrame:viewContext.frame]; } else { [overlay unmount]; From 272b96f1d40f4065fea8204320e60b970e3765e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 05:34:00 -0800 Subject: [PATCH 110/112] Bump data-encoding from 2.4.0 to 2.5.0 in /packer (#5310) Summary: Bumps [data-encoding](https://github.com/ia0/data-encoding) from 2.4.0 to 2.5.0.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=data-encoding&package-manager=cargo&previous-version=2.4.0&new-version=2.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `dependabot rebase` will rebase this PR - `dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `dependabot merge` will merge this PR after your CI passes on it - `dependabot squash and merge` will squash and merge this PR after your CI passes on it - `dependabot cancel merge` will cancel a previously requested merge and block automerging - `dependabot reopen` will reopen this PR if it is closed - `dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Pull Request resolved: https://github.com/facebook/flipper/pull/5310 Reviewed By: mweststrate Differential Revision: D51588872 Pulled By: passy fbshipit-source-id: bbce09141bf6adf384fad0ae2cc6d91f5b36af6e --- packer/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packer/Cargo.lock b/packer/Cargo.lock index d87d79ba2..85f38cc73 100644 --- a/packer/Cargo.lock +++ b/packer/Cargo.lock @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "digest" From 24fa44448e130f6a7acaea3f21c573aa234f3ed2 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 28 Nov 2023 06:51:03 -0800 Subject: [PATCH 111/112] Use SESSION_ID Reviewed By: lblasa Differential Revision: D51585227 fbshipit-source-id: e5574a27b77311b699630a30f72a2d67187ed544 --- desktop/flipper-server-core/src/sessionId.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/desktop/flipper-server-core/src/sessionId.tsx b/desktop/flipper-server-core/src/sessionId.tsx index 4df6f1020..8588035df 100644 --- a/desktop/flipper-server-core/src/sessionId.tsx +++ b/desktop/flipper-server-core/src/sessionId.tsx @@ -9,4 +9,9 @@ import {uuid} from 'flipper-common'; -export const sessionId = uuid(); +if (process.env.FLIPPER_SESSION_ID) { + console.info('Use external session ID', process.env.FLIPPER_SESSION_ID); +} +export const sessionId = `${ + process.env.FLIPPER_SESSION_ID ?? 'unset' +}::${uuid()}`; From 57584a38fab99199a48748bda7303d71beb98d75 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Tue, 28 Nov 2023 09:03:54 -0800 Subject: [PATCH 112/112] Electron distribution announcement Summary: Update our README file with an announcement of our distribution change. I will pair the announcement with an update to our GitHub workflow as to not produce any more Electron builds for future releases. Changelog: Flipper Electron distribution change announcement. Reviewed By: aigoncharov Differential Revision: D51616454 fbshipit-source-id: 5ff513b3d99c8100ed8241d1bdafebf1d6dcfa10 --- .github/workflows/release.yml | 121 +--------------------------------- README.md | 18 +++++ 2 files changed, 19 insertions(+), 120 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a1ff8029..1a4df5d9a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,38 +45,6 @@ jobs: draft: false prerelease: false - build-mac: - needs: - - release - runs-on: macos-latest - env: - desktop-directory: ./desktop - - steps: - - uses: actions/checkout@v3.5.3 - with: - ref: ${{ needs.release.outputs.tag }} - - uses: actions/setup-node@v3.6.0 - with: - node-version: '18.x' - - name: Install - uses: nick-fields/retry@v2.8.3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: cd ${{env.desktop-directory}} && yarn - - name: Build - uses: nick-fields/retry@v2.8.3 - with: - timeout_minutes: 30 - max_attempts: 3 - command: cd ${{env.desktop-directory}} && yarn build --mac --mac-dmg - - name: Upload - uses: actions/upload-artifact@v3.1.2 - with: - name: 'Flipper-mac.dmg' - path: 'dist/Flipper-mac.dmg' - build-server-mac: needs: - release @@ -112,72 +80,6 @@ jobs: name: 'Flipper-server-mac-aarch64.dmg' path: 'dist/Flipper-server-mac-aarch64.dmg' - build-linux: - needs: - - release - runs-on: ubuntu-latest - env: - desktop-directory: ./desktop - - steps: - - uses: actions/checkout@v3.5.3 - with: - ref: ${{ needs.release.outputs.tag }} - - uses: actions/setup-node@v3.6.0 - with: - node-version: '18.x' - - name: Install - uses: nick-fields/retry@v2.8.3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: cd ${{env.desktop-directory}} && yarn - - name: Build - uses: nick-fields/retry@v2.8.3 - with: - timeout_minutes: 30 - max_attempts: 3 - command: cd ${{env.desktop-directory}} && yarn build --linux - - name: Upload Linux - uses: actions/upload-artifact@v3.1.2 - with: - name: 'Flipper-linux.zip' - path: 'dist/Flipper-linux.zip' - - build-win: - needs: - - release - runs-on: windows-latest - env: - desktop-directory: ./desktop - - steps: - - uses: actions/checkout@v3.5.3 - with: - ref: ${{ needs.release.outputs.tag }} - - uses: actions/setup-node@v3.6.0 - with: - node-version: '18.x' - - name: Install - uses: nick-fields/retry@v2.8.3 - with: - timeout_minutes: 10 - max_attempts: 3 - shell: pwsh - command: cd ${{env.desktop-directory}}; yarn - - name: Build - uses: nick-fields/retry@v2.8.3 - with: - timeout_minutes: 30 - max_attempts: 3 - shell: pwsh - command: cd ${{env.desktop-directory}}; yarn build --win - - name: Upload Windows - uses: actions/upload-artifact@v3.1.2 - with: - name: 'Flipper-win.zip' - path: 'dist/Flipper-win.zip' - build-flipper-server: needs: - release @@ -210,9 +112,6 @@ jobs: publish: needs: - - build-win - - build-linux - - build-mac - build-server-mac - build-flipper-server - release @@ -222,12 +121,6 @@ jobs: - uses: actions/checkout@v3.5.3 with: ref: ${{ needs.release.outputs.tag }} - - name: Download Mac - if: ${{ needs.release.outputs.tag != '' }} - uses: actions/download-artifact@v1 - with: - name: 'Flipper-mac.dmg' - path: 'Flipper-mac.dmg' - name: Download Flipper Server x86-64 if: ${{ needs.release.outputs.tag != '' }} uses: actions/download-artifact@v1 @@ -240,18 +133,6 @@ jobs: with: name: 'Flipper-server-mac-aarch64.dmg' path: 'Flipper-server-mac-aarch64.dmg' - - name: Download Linux - if: ${{ needs.release.outputs.tag != '' }} - uses: actions/download-artifact@v1 - with: - name: 'Flipper-linux.zip' - path: 'Flipper-linux.zip' - - name: Download Windows - if: ${{ needs.release.outputs.tag != '' }} - uses: actions/download-artifact@v1 - with: - name: 'Flipper-win.zip' - path: 'Flipper-win.zip' - name: Download Flipper Server if: ${{ needs.release.outputs.tag != '' }} uses: actions/download-artifact@v1 @@ -265,7 +146,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: created_tag: ${{ needs.release.outputs.tag }} - args: Flipper-mac.dmg/Flipper-mac.dmg Flipper-linux.zip/Flipper-linux.zip Flipper-win.zip/Flipper-win.zip flipper-server.tgz/flipper-server.tgz Flipper-server-mac-x64.dmg/Flipper-server-mac-x64.dmg Flipper-server-mac-aarch64.dmg/Flipper-server-mac-aarch64.dmg + args: flipper-server.tgz/flipper-server.tgz Flipper-server-mac-x64.dmg/Flipper-server-mac-x64.dmg Flipper-server-mac-aarch64.dmg/Flipper-server-mac-aarch64.dmg - name: Set up npm token run: echo "//registry.yarnpkg.com/:_authToken=${{ secrets.FLIPPER_NPM_TOKEN }}" >> ~/.npmrc - name: Publish flipper-server on NPM diff --git a/README.md b/README.md index 727eed423..e6eb68ffd 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,24 @@

+--- +## Important Accouncement + +Flipper is moving away from its Electron distribution to an in-Browser experience. + +**How does this affect me?** + +Functionality hasn't changed. The UI remains unchanged. Flipper will run in your default browser instead of a standalone application. +If you build from source, Flipper will open in the browser instead of a standalone app. We also provide a MacOS app for the Flipper runtime which can be run and will also open Flipper in the browser. + +The last Electron release is [v0.239.0](https://github.com/facebook/flipper/releases/tag/v0.239.0). As such, future releases will not include Electron artifacts. + +### React Native support + +If you are debugging React Native applications, v0.239.0 will be the last release with support for it due to technical limitations for React Dev Tools and Hermes Debugger plugins. As such, please refer to that release when debugging React Native applications. + +--- +

Flipper (formerly Sonar) is a platform for debugging mobile apps on iOS and Android and JS apps in your browser or in Node.js. Visualize, inspect, and control your apps from a simple desktop interface. Use Flipper as is or extend it using the plugin API.