flipper-plugin-core
Advanced tools
Comparing version 0.206.0 to 0.207.0
@@ -68,3 +68,3 @@ /** | ||
}; | ||
export type DataSourceOptions = { | ||
export type DataSourceOptions<T> = { | ||
/** | ||
@@ -75,5 +75,21 @@ * The maximum amount of records that this DataSource will store. | ||
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>[]; | ||
}; | ||
export declare function createDataSource<T, Key extends keyof T>(initialSet: readonly T[], options: DataSourceOptions & DataSourceOptionKey<Key>): DataSource<T, T[Key] extends string | number ? T[Key] : never>; | ||
export declare function createDataSource<T>(initialSet?: readonly T[], options?: DataSourceOptions): DataSource<T, never>; | ||
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> { | ||
@@ -83,2 +99,4 @@ private nextId; | ||
private _recordsById; | ||
private _secondaryIndices; | ||
private _recordsBySecondaryIndex; | ||
/** | ||
@@ -104,3 +122,3 @@ * @readonly | ||
}; | ||
constructor(keyAttribute: keyof T | undefined); | ||
constructor(keyAttribute: keyof T | undefined, secondaryIndices?: IndexDefinition<T>[]); | ||
get size(): number; | ||
@@ -178,3 +196,28 @@ /** | ||
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; | ||
private getSecondaryIndexValueFromRecord; | ||
/** | ||
* @private | ||
@@ -193,2 +236,3 @@ */ | ||
private filter?; | ||
private filterExceptions?; | ||
/** | ||
@@ -224,2 +268,8 @@ * @readonly | ||
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; | ||
@@ -253,4 +303,5 @@ setReversed(reverse: boolean): void; | ||
private insertSorted; | ||
private applyFilterExceptions; | ||
} | ||
export {}; | ||
//# sourceMappingURL=DataSource.d.ts.map |
@@ -28,3 +28,3 @@ "use strict"; | ||
function createDataSource(initialSet = [], options) { | ||
const ds = new DataSource(options === null || options === void 0 ? void 0 : options.key); | ||
const ds = new DataSource(options === null || options === void 0 ? void 0 : options.key, options === null || options === void 0 ? void 0 : options.indices); | ||
if ((options === null || options === void 0 ? void 0 : options.limit) !== undefined) { | ||
@@ -38,6 +38,7 @@ ds.limit = options.limit; | ||
class DataSource { | ||
constructor(keyAttribute) { | ||
constructor(keyAttribute, secondaryIndices = []) { | ||
this.nextId = 0; | ||
this._records = []; | ||
this._recordsById = new Map(); | ||
this._recordsBySecondaryIndex = new Map(); | ||
this.idToIndex = new Map(); | ||
@@ -51,2 +52,12 @@ // if we shift the window, we increase shiftOffset to correct idToIndex results, rather than remapping all values | ||
this.keyAttribute = keyAttribute; | ||
this._secondaryIndices = new Map(secondaryIndices.map((index) => { | ||
const sortedKeys = index.slice().sort(); | ||
const key = sortedKeys.join(':'); | ||
// immediately reserve a map per index | ||
this._recordsBySecondaryIndex.set(key, new Map()); | ||
return [key, sortedKeys]; | ||
})); | ||
if (this._secondaryIndices.size !== secondaryIndices.length) { | ||
throw new Error(`Duplicate index definition in ${JSON.stringify(secondaryIndices)}`); | ||
} | ||
this.view = new DataSourceView(this, DEFAULT_VIEW_ID); | ||
@@ -128,2 +139,3 @@ this.additionalViews = {}; | ||
} | ||
this.storeSecondaryIndices(value); | ||
const visibleMap = { [DEFAULT_VIEW_ID]: false }; | ||
@@ -189,2 +201,4 @@ const approxIndexMap = { [DEFAULT_VIEW_ID]: -1 }; | ||
} | ||
this.removeSecondaryIndices(oldValue); | ||
this.storeSecondaryIndices(value); | ||
this.emitDataEvent({ | ||
@@ -224,2 +238,3 @@ type: 'update', | ||
} | ||
this.removeSecondaryIndices(entry.value); | ||
this.emitDataEvent({ | ||
@@ -267,2 +282,3 @@ type: 'remove', | ||
} | ||
removed.forEach((entry) => this.removeSecondaryIndices(entry.value)); | ||
if (this.view.isSorted && | ||
@@ -289,6 +305,9 @@ removed.length > 10 && | ||
clear() { | ||
this._records = []; | ||
this._recordsById = new Map(); | ||
this._records.splice(0); | ||
this._recordsById.clear(); | ||
for (const m of this._recordsBySecondaryIndex.values()) { | ||
m.clear(); | ||
} | ||
this.shiftOffset = 0; | ||
this.idToIndex = new Map(); | ||
this.idToIndex.clear(); | ||
this.rebuild(); | ||
@@ -369,3 +388,70 @@ } | ||
} | ||
storeSecondaryIndices(value) { | ||
for (const [indexKey, sortedIndex] of this._secondaryIndices.entries()) { | ||
const indexValue = this.getSecondaryIndexValueFromRecord(value, sortedIndex); | ||
// maps are already set up in constructor | ||
const m = this._recordsBySecondaryIndex.get(indexKey); | ||
const a = m.get(indexValue); | ||
if (!a) { | ||
// not seen this index value yet | ||
m.set(indexValue, [value]); | ||
} | ||
else { | ||
a.push(value); | ||
} | ||
} | ||
} | ||
removeSecondaryIndices(value) { | ||
for (const [indexKey, sortedIndex] of this._secondaryIndices.entries()) { | ||
const indexValue = this.getSecondaryIndexValueFromRecord(value, sortedIndex); | ||
// maps are already set up in constructor | ||
const m = this._recordsBySecondaryIndex.get(indexKey); | ||
// code belows assumes that we have an entry for this secondary | ||
const a = m.get(indexValue); | ||
a.splice(a.indexOf(value), 1); | ||
} | ||
} | ||
/** | ||
* 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) { | ||
var _a; | ||
// normalise indexKey, incl sorting | ||
const sortedKeys = Object.keys(indexQuery).sort(); | ||
const indexKey = sortedKeys.join(':'); | ||
const recordsByIndex = this._recordsBySecondaryIndex.get(indexKey); | ||
if (!recordsByIndex) { | ||
throw new Error(`No index has been defined for the keys ${JSON.stringify(Object.keys(indexQuery))}`); | ||
} | ||
const indexValue = JSON.stringify( | ||
// query object needs reordering and normalised to produce correct indexValue | ||
Object.fromEntries(sortedKeys.map((k) => [k, String(indexQuery[k])]))); | ||
return (_a = recordsByIndex.get(indexValue)) !== null && _a !== void 0 ? _a : []; | ||
} | ||
/** | ||
* Like getAllRecords, but returns the first match only. | ||
* @param indexQuery | ||
* @returns | ||
*/ | ||
getFirstRecordByIndex(indexQuery) { | ||
return this.getAllRecordsByIndex(indexQuery)[0]; | ||
} | ||
getSecondaryIndexValueFromRecord(record, | ||
// assumes keys is already ordered | ||
keys) { | ||
return JSON.stringify(Object.fromEntries(keys.map((k) => [k, String(record[k])]))); | ||
} | ||
/** | ||
* @private | ||
@@ -395,2 +481,3 @@ */ | ||
this.filter = undefined; | ||
this.filterExceptions = undefined; | ||
/** | ||
@@ -476,5 +563,16 @@ * @readonly | ||
this.filter = filter; | ||
// Filter exceptions are relevant for one filter only | ||
this.filterExceptions = undefined; | ||
this.rebuild(); | ||
} | ||
} | ||
/** | ||
* 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) { | ||
this.filterExceptions = ids ? new Set(ids) : undefined; | ||
this.rebuild(); | ||
} | ||
toggleReversed() { | ||
@@ -496,2 +594,3 @@ this.setReversed(!this.reverse); | ||
this.filter = undefined; | ||
this.filterExceptions = undefined; | ||
this.windowStart = 0; | ||
@@ -594,2 +693,3 @@ this.windowEnd = 0; | ||
entry.visible[this.viewId] = filter ? filter(entry.value) : true; | ||
this.applyFilterExceptions(entry); | ||
if (!entry.visible[this.viewId]) { | ||
@@ -613,2 +713,3 @@ // not in filter? skip this entry | ||
entry.visible[this.viewId] = filter ? filter(entry.value) : true; | ||
this.applyFilterExceptions(entry); | ||
// short circuit; no view active so update straight away | ||
@@ -725,2 +826,3 @@ if (!filter && !sortBy) { | ||
entry.visible[this.viewId] = filter(entry.value); | ||
this.applyFilterExceptions(entry); | ||
return entry.visible[this.viewId]; | ||
@@ -778,4 +880,13 @@ }) | ||
} | ||
applyFilterExceptions(entry) { | ||
if (this.datasource.keyAttribute && | ||
this.filter && | ||
this.filterExceptions && | ||
!entry.visible[this.viewId]) { | ||
const keyValue = entry.value[this.datasource.keyAttribute]; | ||
entry.visible[this.viewId] = this.filterExceptions.has(keyValue); | ||
} | ||
} | ||
} | ||
exports.DataSourceView = DataSourceView; | ||
//# sourceMappingURL=DataSource.js.map |
@@ -10,3 +10,3 @@ /** | ||
import { DataSource, DataSourceOptions as BaseDataSourceOptions, DataSourceOptionKey as BaseDataSourceOptionKey } from '../data-source/DataSource'; | ||
type DataSourceOptions = BaseDataSourceOptions & { | ||
type DataSourceOptions<T> = BaseDataSourceOptions<T> & { | ||
/** | ||
@@ -18,5 +18,5 @@ * Should this state persist when exporting a plugin? | ||
}; | ||
export declare function createDataSource<T, Key extends keyof T>(initialSet: readonly T[], options: DataSourceOptions & BaseDataSourceOptionKey<Key>): DataSource<T, T[Key] extends string | number ? T[Key] : never>; | ||
export declare function createDataSource<T>(initialSet?: readonly T[], options?: DataSourceOptions): DataSource<T, never>; | ||
export declare function createDataSource<T, Key extends keyof T>(initialSet: readonly T[], options: DataSourceOptions<T> & BaseDataSourceOptionKey<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 {}; | ||
//# sourceMappingURL=createDataSource.d.ts.map |
{ | ||
"name": "flipper-plugin-core", | ||
"version": "0.206.0", | ||
"version": "0.207.0", | ||
"description": "Flipper Desktop plugin SDK and components", | ||
@@ -13,3 +13,3 @@ "repository": "facebook/flipper", | ||
"eventemitter3": "^4.0.7", | ||
"flipper-common": "0.206.0", | ||
"flipper-common": "0.207.0", | ||
"immer": "^9.0.18", | ||
@@ -16,0 +16,0 @@ "js-base64": "^3.7.5", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
244466
3774
+ Addedflipper-common@0.207.0(transitive)
- Removedflipper-common@0.206.0(transitive)
Updatedflipper-common@0.207.0