@player-ui/partial-match-registry 0.0.1-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +80 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.esm.js +70 -0
- package/package.json +18 -0
- package/src/deep-partial-matcher.ts +52 -0
- package/src/index.ts +78 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var SortedArray = require('sorted-array');
|
|
6
|
+
var dlv = require('dlv');
|
|
7
|
+
|
|
8
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
|
+
|
|
10
|
+
var SortedArray__default = /*#__PURE__*/_interopDefaultLegacy(SortedArray);
|
|
11
|
+
var dlv__default = /*#__PURE__*/_interopDefaultLegacy(dlv);
|
|
12
|
+
|
|
13
|
+
function traverseObj(object, path = [], pairs = new Map()) {
|
|
14
|
+
for (const key of Object.keys(object)) {
|
|
15
|
+
const val = object[key];
|
|
16
|
+
const nestedPath = [...path, key];
|
|
17
|
+
if (typeof val === "object") {
|
|
18
|
+
traverseObj(val, nestedPath, pairs);
|
|
19
|
+
} else {
|
|
20
|
+
pairs.set(nestedPath, val);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return pairs;
|
|
24
|
+
}
|
|
25
|
+
function createMatcher(partialObj) {
|
|
26
|
+
const pairs = traverseObj(partialObj);
|
|
27
|
+
const matchFunction = (searchObj) => {
|
|
28
|
+
for (const entry of pairs) {
|
|
29
|
+
const [path, value] = entry;
|
|
30
|
+
if (dlv__default["default"](searchObj, path) !== value) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
};
|
|
36
|
+
matchFunction.count = pairs.size;
|
|
37
|
+
return matchFunction;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createBasicMatcher(seed) {
|
|
41
|
+
const matcher = (match) => seed === match;
|
|
42
|
+
matcher.count = 1;
|
|
43
|
+
return matcher;
|
|
44
|
+
}
|
|
45
|
+
const createSortedArray = () => new SortedArray__default["default"]([], (c) => c.matcher.count);
|
|
46
|
+
class Registry {
|
|
47
|
+
constructor(initialSet) {
|
|
48
|
+
this.store = createSortedArray();
|
|
49
|
+
initialSet == null ? void 0 : initialSet.forEach(([match, value]) => {
|
|
50
|
+
this.set(match, value);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
set(match, value) {
|
|
54
|
+
const matcher = typeof match === "object" ? createMatcher(match) : createBasicMatcher(match);
|
|
55
|
+
this.store.insert({
|
|
56
|
+
key: match,
|
|
57
|
+
value,
|
|
58
|
+
matcher
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
get(query) {
|
|
62
|
+
for (const entry of this.store.array) {
|
|
63
|
+
if (entry.matcher(query)) {
|
|
64
|
+
return entry.value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
forEach(callbackfn) {
|
|
69
|
+
for (const entry of this.store.array) {
|
|
70
|
+
callbackfn(entry);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
clear() {
|
|
74
|
+
this.store = createSortedArray();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
exports.Registry = Registry;
|
|
79
|
+
exports.createObjectMatcher = createMatcher;
|
|
80
|
+
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** A function that checks overlapping properties against a reference value */
|
|
2
|
+
declare type Matcher = ((searchObj: object) => boolean) & {
|
|
3
|
+
/** The count represents the specificity of this matcher */
|
|
4
|
+
count: number;
|
|
5
|
+
};
|
|
6
|
+
/** Given an object, create a function that compares any set key/value pairs in the given object against a new value */
|
|
7
|
+
declare function createMatcher(partialObj: object): Matcher;
|
|
8
|
+
|
|
9
|
+
interface RegistryIndex<V> {
|
|
10
|
+
/** The original object we wanted to match on */
|
|
11
|
+
key: object;
|
|
12
|
+
/** The value to return */
|
|
13
|
+
value: V;
|
|
14
|
+
/** The matcher function for this entry */
|
|
15
|
+
matcher: Matcher;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A partial match registry is a map that uses an object to "match" against keys.
|
|
19
|
+
* More specific matches take precedence over less specific ones.
|
|
20
|
+
*/
|
|
21
|
+
declare class Registry<V> {
|
|
22
|
+
private store;
|
|
23
|
+
constructor(initialSet?: Array<[any, V]>);
|
|
24
|
+
/** Add match -> value mapping to the registry */
|
|
25
|
+
set(match: any, value: V): void;
|
|
26
|
+
/** Fetch the best match in the registry */
|
|
27
|
+
get(query: any): V | undefined;
|
|
28
|
+
/** Loop over all entries and run callback */
|
|
29
|
+
forEach(callbackfn: (value: RegistryIndex<V>) => void): void;
|
|
30
|
+
/** Reset the items in the registry */
|
|
31
|
+
clear(): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { Registry, createMatcher as createObjectMatcher };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import SortedArray from 'sorted-array';
|
|
2
|
+
import dlv from 'dlv';
|
|
3
|
+
|
|
4
|
+
function traverseObj(object, path = [], pairs = new Map()) {
|
|
5
|
+
for (const key of Object.keys(object)) {
|
|
6
|
+
const val = object[key];
|
|
7
|
+
const nestedPath = [...path, key];
|
|
8
|
+
if (typeof val === "object") {
|
|
9
|
+
traverseObj(val, nestedPath, pairs);
|
|
10
|
+
} else {
|
|
11
|
+
pairs.set(nestedPath, val);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return pairs;
|
|
15
|
+
}
|
|
16
|
+
function createMatcher(partialObj) {
|
|
17
|
+
const pairs = traverseObj(partialObj);
|
|
18
|
+
const matchFunction = (searchObj) => {
|
|
19
|
+
for (const entry of pairs) {
|
|
20
|
+
const [path, value] = entry;
|
|
21
|
+
if (dlv(searchObj, path) !== value) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
};
|
|
27
|
+
matchFunction.count = pairs.size;
|
|
28
|
+
return matchFunction;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function createBasicMatcher(seed) {
|
|
32
|
+
const matcher = (match) => seed === match;
|
|
33
|
+
matcher.count = 1;
|
|
34
|
+
return matcher;
|
|
35
|
+
}
|
|
36
|
+
const createSortedArray = () => new SortedArray([], (c) => c.matcher.count);
|
|
37
|
+
class Registry {
|
|
38
|
+
constructor(initialSet) {
|
|
39
|
+
this.store = createSortedArray();
|
|
40
|
+
initialSet == null ? void 0 : initialSet.forEach(([match, value]) => {
|
|
41
|
+
this.set(match, value);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
set(match, value) {
|
|
45
|
+
const matcher = typeof match === "object" ? createMatcher(match) : createBasicMatcher(match);
|
|
46
|
+
this.store.insert({
|
|
47
|
+
key: match,
|
|
48
|
+
value,
|
|
49
|
+
matcher
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
get(query) {
|
|
53
|
+
for (const entry of this.store.array) {
|
|
54
|
+
if (entry.matcher(query)) {
|
|
55
|
+
return entry.value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
forEach(callbackfn) {
|
|
60
|
+
for (const entry of this.store.array) {
|
|
61
|
+
callbackfn(entry);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
clear() {
|
|
65
|
+
this.store = createSortedArray();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { Registry, createMatcher as createObjectMatcher };
|
|
70
|
+
//# sourceMappingURL=index.esm.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@player-ui/partial-match-registry",
|
|
3
|
+
"version": "0.0.1-next.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"registry": "https://registry.npmjs.org"
|
|
7
|
+
},
|
|
8
|
+
"peerDependencies": {},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"dlv": "^1.1.3",
|
|
11
|
+
"@types/dlv": "^1.1.2",
|
|
12
|
+
"sorted-array": "^2.0.4",
|
|
13
|
+
"@babel/runtime": "7.15.4"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/index.cjs.js",
|
|
16
|
+
"module": "dist/index.esm.js",
|
|
17
|
+
"typings": "dist/index.d.ts"
|
|
18
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import dlv from 'dlv';
|
|
2
|
+
|
|
3
|
+
/** A function that checks overlapping properties against a reference value */
|
|
4
|
+
export type Matcher = ((searchObj: object) => boolean) & {
|
|
5
|
+
/** The count represents the specificity of this matcher */
|
|
6
|
+
count: number;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/** Traverse an object and collect any key/value pairs including nested keys */
|
|
10
|
+
function traverseObj(
|
|
11
|
+
object: Record<any, any>,
|
|
12
|
+
path: string[] = [],
|
|
13
|
+
pairs: Map<string[], any> = new Map()
|
|
14
|
+
): Map<string[], any> {
|
|
15
|
+
for (const key of Object.keys(object)) {
|
|
16
|
+
const val: any = object[key];
|
|
17
|
+
const nestedPath = [...path, key];
|
|
18
|
+
('');
|
|
19
|
+
|
|
20
|
+
if (typeof val === 'object') {
|
|
21
|
+
traverseObj(val, nestedPath, pairs);
|
|
22
|
+
} else {
|
|
23
|
+
pairs.set(nestedPath, val);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return pairs;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Given an object, create a function that compares any set key/value pairs in the given object against a new value */
|
|
31
|
+
export default function createMatcher(partialObj: object): Matcher {
|
|
32
|
+
// Convert the partial object into a list of [key, value] pairs;
|
|
33
|
+
const pairs = traverseObj(partialObj);
|
|
34
|
+
|
|
35
|
+
/** Generate a function to match against all of the properties we care about */
|
|
36
|
+
const matchFunction = (searchObj: object) => {
|
|
37
|
+
for (const entry of pairs) {
|
|
38
|
+
const [path, value] = entry;
|
|
39
|
+
|
|
40
|
+
if (dlv(searchObj, path) !== value) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Keep track of the specificity of the comparator
|
|
49
|
+
matchFunction.count = pairs.size;
|
|
50
|
+
|
|
51
|
+
return matchFunction;
|
|
52
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import SortedArray from 'sorted-array';
|
|
2
|
+
import type { Matcher } from './deep-partial-matcher';
|
|
3
|
+
import createObjectMatcher from './deep-partial-matcher';
|
|
4
|
+
|
|
5
|
+
export { default as createObjectMatcher } from './deep-partial-matcher';
|
|
6
|
+
|
|
7
|
+
/** create a matcher function that matches exactly */
|
|
8
|
+
function createBasicMatcher(seed: any): Matcher {
|
|
9
|
+
/** a simple matcher function that only matches itself */
|
|
10
|
+
const matcher = (match: any) => seed === match;
|
|
11
|
+
matcher.count = 1;
|
|
12
|
+
|
|
13
|
+
return matcher;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface RegistryIndex<V> {
|
|
17
|
+
/** The original object we wanted to match on */
|
|
18
|
+
key: object;
|
|
19
|
+
|
|
20
|
+
/** The value to return */
|
|
21
|
+
value: V;
|
|
22
|
+
|
|
23
|
+
/** The matcher function for this entry */
|
|
24
|
+
matcher: Matcher;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** create an empty sorted array using the matcher count */
|
|
28
|
+
const createSortedArray = <V>() =>
|
|
29
|
+
new SortedArray<RegistryIndex<V>>([], (c) => c.matcher.count);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A partial match registry is a map that uses an object to "match" against keys.
|
|
33
|
+
* More specific matches take precedence over less specific ones.
|
|
34
|
+
*/
|
|
35
|
+
export class Registry<V> {
|
|
36
|
+
private store = createSortedArray<V>();
|
|
37
|
+
|
|
38
|
+
constructor(initialSet?: Array<[any, V]>) {
|
|
39
|
+
initialSet?.forEach(([match, value]) => {
|
|
40
|
+
this.set(match, value);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Add match -> value mapping to the registry */
|
|
45
|
+
set(match: any, value: V) {
|
|
46
|
+
const matcher =
|
|
47
|
+
typeof match === 'object'
|
|
48
|
+
? createObjectMatcher(match)
|
|
49
|
+
: createBasicMatcher(match);
|
|
50
|
+
|
|
51
|
+
this.store.insert({
|
|
52
|
+
key: match,
|
|
53
|
+
value,
|
|
54
|
+
matcher,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Fetch the best match in the registry */
|
|
59
|
+
get(query: any): V | undefined {
|
|
60
|
+
for (const entry of this.store.array) {
|
|
61
|
+
if (entry.matcher(query)) {
|
|
62
|
+
return entry.value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Loop over all entries and run callback */
|
|
68
|
+
forEach(callbackfn: (value: RegistryIndex<V>) => void): void {
|
|
69
|
+
for (const entry of this.store.array) {
|
|
70
|
+
callbackfn(entry);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Reset the items in the registry */
|
|
75
|
+
clear() {
|
|
76
|
+
this.store = createSortedArray<V>();
|
|
77
|
+
}
|
|
78
|
+
}
|