flipper-plugin
Version:
Flipper Desktop plugin SDK and components
314 lines • 10.1 kB
TypeScript
/**
* 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
*/
type AppendEvent<T> = {
type: 'append';
entry: Entry<T>;
};
type UpdateEvent<T> = {
type: 'update';
entry: Entry<T>;
oldValue: T;
oldVisible: {
[viewId: string]: boolean;
};
index: number;
};
type RemoveEvent<T> = {
type: 'remove';
entry: Entry<T>;
index: number;
};
type ShiftEvent<T> = {
type: 'shift';
entries: Entry<T>[];
amount: number;
};
type SINewIndexValueEvent<T> = {
type: 'siNewIndexValue';
indexKey: string;
value: T;
firstOfKind: boolean;
};
type ClearEvent = {
type: 'clear';
};
type DataEvent<T> = AppendEvent<T> | UpdateEvent<T> | RemoveEvent<T> | ShiftEvent<T> | SINewIndexValueEvent<T> | ClearEvent;
type Entry<T> = {
value: T;
id: number;
visible: {
[viewId: string]: boolean;
};
approxIndex: {
[viewId: string]: number;
};
};
type Primitive = number | string | boolean | null | undefined;
type OutputChange = {
type: 'shift';
index: number;
location: 'before' | 'in' | 'after';
delta: number;
newCount: number;
} | {
type: 'update';
index: number;
} | {
type: 'reset';
newCount: number;
} | {
type: 'windowChange';
newStart: number;
newEnd: number;
};
export type DataSourceOptionKey<K extends PropertyKey> = {
/**
* If a key is set, the given field of the records is assumed to be unique,
* and it's value can be used to perform lookups and upserts.
*/
key?: K;
};
export type DataSourceOptions<T> = {
/**
* The maximum amount of records that this DataSource will store.
* If the limit is exceeded, the oldest records will automatically be dropped to make place for the new ones
*/
limit?: number;
/**
* Secondary indices, that can be used to perform O(1) lookups on specific keys later on.
* A combination of keys is allowed.
*
* For example:
* indices: [["title"], ["id", "title"]]
*
* Enables:
* dataSource.getAllRecordsByIndex({
* id: 123,
* title: "Test"
* })
*/
indices?: IndexDefinition<T>[];
};
type IndexDefinition<T> = Array<keyof T>;
type IndexQuery<T> = Partial<T>;
export declare function createDataSource<T, Key extends keyof T>(initialSet: readonly T[], options: DataSourceOptions<T> & DataSourceOptionKey<Key>): DataSource<T, T[Key] extends string | number ? T[Key] : never>;
export declare function createDataSource<T>(initialSet?: readonly T[], options?: DataSourceOptions<T>): DataSource<T, never>;
export declare class DataSource<T extends any, KeyType = never> {
private nextId;
private _records;
private _recordsById;
private _secondaryIndices;
private _recordsBySecondaryIndex;
/**
* @readonly
*/
keyAttribute: keyof T | undefined;
private idToIndex;
private shiftOffset;
/**
* The maximum amount of records this DataSource can have
*/
limit: number;
/**
* The default view on this data source. A view applies
* sorting, filtering and windowing to get more constrained output.
*
* Additional views can created through the fork method.
*/
readonly view: DataSourceView<T, KeyType>;
readonly additionalViews: {
[viewId: string]: DataSourceView<T, KeyType>;
};
private readonly outputEventEmitter;
constructor(keyAttribute: keyof T | undefined, secondaryIndices?: IndexDefinition<T>[]);
get size(): number;
/**
* Returns a defensive copy of the stored records.
* This is a O(n) operation! Prefer using .size and .get instead if only a subset is needed.
*/
records(): readonly T[];
get(index: number): T;
has(key: KeyType): boolean;
getById(key: KeyType): T | undefined;
keys(): IterableIterator<KeyType>;
entries(): IterableIterator<[KeyType, T]>;
[Symbol.iterator](): IterableIterator<T>;
secondaryIndicesKeys(): string[];
/**
* Returns the index of a specific key in the *records* set.
* Returns -1 if the record wansn't found
*/
getIndexOfKey(key: KeyType): number;
append(value: T): void;
/**
* Updates or adds a record. Returns `true` if the record already existed.
* Can only be used if a key is used.
*/
upsert(value: T): boolean;
/**
* Replaces an item in the base data collection.
* Note that the index is based on the insertion order, and not based on the current view
*/
update(index: number, value: T): void;
/**
* @param index
*
* Warning: this operation can be O(n) if a key is set
*/
delete(index: number): void;
/**
* Removes the item with the given key from this dataSource.
* Returns false if no record with the given key was found
*
* Warning: this operation can be O(n) if a key is set
*/
deleteByKey(keyValue: KeyType): boolean;
/**
* Removes the first N entries.
* @param amount
*/
shift(amount: number): void;
/**
* The clear operation removes any records stored, but will keep the current view preferences such as sorting and filtering
*/
clear(): void;
/**
* The rebuild function that would support rebuilding multiple views all at once
*/
rebuild(): void;
/**
* Returns a fork of this dataSource, that shares the source data with this dataSource,
* but has it's own FSRW pipeline, to allow multiple views on the same data
*/
private fork;
/**
* Returns a new view of the `DataSource` if there doesn't exist a `DataSourceView` with the `viewId` passed in.
* The view will allow different filters and sortings on the `DataSource` which can be helpful in cases
* where multiple tables/views are needed.
* @param viewId id for the `DataSourceView`
* @returns `DataSourceView` that corresponds to the `viewId`
*/
getAdditionalView(viewId: string): DataSourceView<T, KeyType>;
deleteView(viewId: string): void;
addDataListener<E extends DataEvent<T>['type']>(event: E, cb: (data: Extract<DataEvent<T>, {
type: E;
}>) => void): () => void;
private assertKeySet;
private getKey;
private storeIndexOfKey;
private emitDataEvent;
private storeSecondaryIndices;
private removeSecondaryIndices;
/**
* Returns all items matching the specified index query.
*
* Note that the results are unordered, unless
* records have not been updated using upsert / update, in that case
* insertion order is maintained.
*
* Example:
* `ds.getAllRecordsByIndex({title: 'subit a bug', done: false})`
*
* If no index has been specified for this exact keyset in the indexQuery (see options.indices), this method will throw
*
* @param indexQuery
* @returns
*/
getAllRecordsByIndex(indexQuery: IndexQuery<T>): readonly T[];
/**
* Like getAllRecords, but returns the first match only.
* @param indexQuery
* @returns
*/
getFirstRecordByIndex(indexQuery: IndexQuery<T>): T | undefined;
getAllIndexValues(index: IndexDefinition<T>): string[] | undefined;
private getSecondaryIndexValueFromRecord;
/**
* @private
*/
serialize(): readonly T[];
/**
* @private
*/
deserialize(value: any[]): void;
}
export declare class DataSourceView<T, KeyType> {
readonly datasource: DataSource<T, KeyType>;
private sortBy;
private reverse;
private filter?;
private filterExceptions?;
/**
* @readonly
*/
windowStart: number;
/**
* @readonly
*/
windowEnd: number;
private viewId;
private outputChangeListeners;
/**
* This is the base view data, that is filtered and sorted, but not reversed or windowed
*/
private _output;
constructor(datasource: DataSource<T, KeyType>, viewId: string);
get size(): number;
get isSorted(): boolean;
get isFiltered(): boolean;
get isReversed(): boolean;
/**
* Returns a defensive copy of the current output.
* Sort, filter, reverse and are applied.
* Start and end behave like slice, and default to the currently active window.
*/
output(start?: number, end?: number): readonly T[];
getViewIndex(entry: T): number;
setWindow(start: number, end: number): void;
addListener(listener: (change: OutputChange) => void): () => void;
setSortBy(sortBy: undefined | keyof T | ((a: T) => Primitive)): void;
setFilter(filter: undefined | ((value: T) => boolean)): void;
/**
* Granular control over filters to add one-off exceptions to them.
* They allow us to add singular items to table views.
* Extremely useful for Bloks Debugger where we have to jump between multiple types of rows that could be filtered out
*/
setFilterExpections(ids: KeyType[] | undefined): void;
toggleReversed(): void;
setReversed(reverse: boolean): void;
/**
* The reset operation resets any view preferences such as sorting and filtering, but keeps the current set of records.
*/
reset(): void;
private normalizeIndex;
get(viewIndex: number): T;
getEntry(viewIndex: number): Entry<T>;
getViewIndexOfEntry(entry: Entry<T>): number;
[Symbol.iterator](): IterableIterator<T>;
private notifyAllListeners;
private notifyItemUpdated;
private notifyItemShift;
private notifyReset;
/**
* @private
*/
processEvent(event: DataEvent<T>): void;
private processRemoveEvent;
/**
* Rebuilds the entire view. Typically there should be no need to call this manually
* @private
*/
rebuild(): void;
private sortHelper;
private getSortedIndex;
private insertSorted;
private applyFilterExceptions;
}
export {};
//# sourceMappingURL=DataSource.d.ts.map