@rimbu/deep 0.11.2 → 0.12.0
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/main/deep.js +211 -0
- package/dist/main/deep.js.map +1 -0
- package/dist/main/index.js +7 -9
- package/dist/main/index.js.map +1 -1
- package/dist/main/internal.js +6 -4
- package/dist/main/internal.js.map +1 -1
- package/dist/main/match.js +160 -199
- package/dist/main/match.js.map +1 -1
- package/dist/main/patch.js +101 -125
- package/dist/main/patch.js.map +1 -1
- package/dist/main/path.js +109 -62
- package/dist/main/path.js.map +1 -1
- package/dist/main/protected.js +0 -19
- package/dist/main/protected.js.map +1 -1
- package/dist/main/selector.js +40 -0
- package/dist/main/selector.js.map +1 -0
- package/dist/main/tuple.js.map +1 -1
- package/dist/module/deep.js +192 -0
- package/dist/module/deep.js.map +1 -0
- package/dist/module/index.js +9 -1
- package/dist/module/index.js.map +1 -1
- package/dist/module/internal.js +4 -4
- package/dist/module/internal.js.map +1 -1
- package/dist/module/match.js +159 -179
- package/dist/module/match.js.map +1 -1
- package/dist/module/patch.js +91 -112
- package/dist/module/patch.js.map +1 -1
- package/dist/module/path.js +99 -44
- package/dist/module/path.js.map +1 -1
- package/dist/module/protected.js +1 -17
- package/dist/module/protected.js.map +1 -1
- package/dist/module/selector.js +36 -0
- package/dist/module/selector.js.map +1 -0
- package/dist/module/tuple.js.map +1 -1
- package/dist/types/deep.d.ts +284 -0
- package/dist/types/index.d.ts +10 -1
- package/dist/types/internal.d.ts +7 -4
- package/dist/types/match.d.ts +74 -80
- package/dist/types/patch.d.ts +57 -50
- package/dist/types/path.d.ts +177 -34
- package/dist/types/protected.d.ts +1 -16
- package/dist/types/selector.d.ts +47 -0
- package/dist/types/tuple.d.ts +10 -0
- package/package.json +4 -4
- package/src/deep.ts +362 -0
- package/src/index.ts +14 -10
- package/src/internal.ts +7 -4
- package/src/match.ts +396 -212
- package/src/patch.ts +173 -176
- package/src/path.ts +400 -74
- package/src/protected.ts +14 -25
- package/src/selector.ts +90 -0
- package/src/tuple.ts +12 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Deep } from './internal';
|
|
2
|
+
export { match } from './match';
|
|
3
|
+
export { patch } from './patch';
|
|
4
|
+
export { getAt, patchAt } from './path';
|
|
5
|
+
export { select } from './selector';
|
|
6
|
+
/**
|
|
7
|
+
* Returns the same value wrapped in the `Protected` type.
|
|
8
|
+
* @typeparam T - the source value type
|
|
9
|
+
* @param source - the value to wrap
|
|
10
|
+
* @note does not perform any runtime protection, it is only a utility to easily add the `Protected`
|
|
11
|
+
* type to a value
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const obj = Deep.protect({ a: 1, b: { c: true, d: [1] } })
|
|
15
|
+
* obj.a = 2 // compiler error: a is readonly
|
|
16
|
+
* obj.b.c = false // compiler error: c is readonly
|
|
17
|
+
* obj.b.d.push(2) // compiler error: d is a readonly array
|
|
18
|
+
* (obj as any).b.d.push(2) // will actually mutate the object
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function protect(source) {
|
|
22
|
+
return source;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Returns a function that gets the value at the given string `path` inside an object.
|
|
26
|
+
* @typeparam T - the input value type
|
|
27
|
+
* @typeparam P - the string literal path type in the object
|
|
28
|
+
* @param path - the string path in the object
|
|
29
|
+
* @param source - the value from which to extract the path value
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const items = [{ a: { b: 1, c: 'a' } }, { a: { b: 2, c: 'b' } }];
|
|
33
|
+
* items.map(Deep.getAtWith('a.c'));
|
|
34
|
+
* // => ['a', 'b']
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function getAtWith(path) {
|
|
38
|
+
return (source) => Deep.getAt(source, path);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns a function that patches a given `source` with the given `patchItems`.
|
|
42
|
+
* @typeparam T - the patch value type
|
|
43
|
+
* @typeparam TE - utility type
|
|
44
|
+
* @typeparam TT - utility type
|
|
45
|
+
* @param patchItem - the `Patch` definition to update the given value of type `T` with.
|
|
46
|
+
* @param source - the value to use the given `patchItem` on.
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const items = [{ a: 1, b: 'a' }, { a: 2, b: 'b' }];
|
|
50
|
+
* items.map(Deep.patchWith([{ a: v => v + 1 }]));
|
|
51
|
+
* // => [{ a: 2, b: 'a' }, { a: 3, b: 'b' }]
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export function patchWith(patchItem) {
|
|
55
|
+
return (source) => Deep.patch(source, patchItem);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns a function that patches a given `value` with the given `patchItems` at the given `path`.
|
|
59
|
+
* @typeparam T - the patch value type
|
|
60
|
+
* @typeparam P - the string literal path type in the object
|
|
61
|
+
* @typeparam TE - utility type
|
|
62
|
+
* @typeparam TT - utility type
|
|
63
|
+
* @param path - the string path in the object
|
|
64
|
+
* @param patchItem - the `Patch` definition to update the value at the given `path` in `T` with.
|
|
65
|
+
* @param source - the value to use the given `patchItem` on at the given `path`.
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* const items = [{ a: { b: 1, c: 'a' } }, { a: { b: 2, c: 'b' } }];
|
|
69
|
+
* items.map(Deep.patchAtWith('a', [{ b: (v) => v + 1 }]));
|
|
70
|
+
* // => [{ a: { b: 2, c: 'a' } }, { a: { b: 3, c: 'b' } }]
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function patchAtWith(path, patchItem) {
|
|
74
|
+
return (source) => Deep.patchAt(source, path, patchItem);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Returns a function that matches a given `value` with the given `matcher`.
|
|
78
|
+
* @typeparam T - the patch value type
|
|
79
|
+
* @param matcher - a matcher object that matches input values.
|
|
80
|
+
* @param source - the value to use the given `patchItem` on at the given `path`.
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const items = [{ a: 1, b: 'a' }, { a: 2, b: 'b' }];
|
|
84
|
+
* items.filter(Deep.matchWith({ a: 2 }));
|
|
85
|
+
* // => [{ a: 2, b: 'b' }]
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function matchWith(matcher) {
|
|
89
|
+
return (source) => Deep.match(source, matcher);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Returns true if the given `value` object matches the given `matcher` at the given `path`, false otherwise.
|
|
93
|
+
* @typeparam T - the input value type
|
|
94
|
+
* @typeparam P - the string literal path type in the object
|
|
95
|
+
* @param source - the input value
|
|
96
|
+
* @param path - the string path in the object
|
|
97
|
+
* @param matcher - a matcher object or a function taking the matcher API and returning a match object
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const input = { a: 1, b: { c: true, d: 'a' } }
|
|
101
|
+
* Deep.matchAt(input, 'b', { c: true })
|
|
102
|
+
* // => true
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export function matchAt(source, path, matcher) {
|
|
106
|
+
return Deep.match(Deep.getAt(source, path), matcher);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Returns a function that matches a given `value` with the given `matcher` at the given string `path`.
|
|
110
|
+
* @typeparam T - the patch value type
|
|
111
|
+
* @typeparam P - the string literal path type in the object
|
|
112
|
+
* @typeparam TE - utility type
|
|
113
|
+
* @param path - the string path in the object
|
|
114
|
+
* @param matcher - a matcher object that matches input values.
|
|
115
|
+
* @param source - the value to use the given `matcher` on at the given `path`.
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* const items = [{ a: { b: 1, c: 'a' } }, { a: { b: 2, c: 'b' } }];
|
|
119
|
+
* items.filter(Deep.matchAtWith('a.b', 2));
|
|
120
|
+
* // => [{ a: 2, b: 'b' }]
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function matchAtWith(path, matcher) {
|
|
124
|
+
return (source) => Deep.matchAt(source, path, matcher);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Returns a function that selects a certain shape from a given `value` with the given `selector`.
|
|
128
|
+
* @typeparam T - the patch value type
|
|
129
|
+
* @typeparam SL - the selector shape type
|
|
130
|
+
* @param selector - a shape indicating the selection from the source values
|
|
131
|
+
* @param source - the value to use the given `selector` on.
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* const items = [{ a: { b: 1, c: 'a' } }, { a: { b: 2, c: 'b' } }];
|
|
135
|
+
* items.map(Deep.selectWith({ q: 'a.c', z: ['a.b', v => v.a.b + 1] as const }));
|
|
136
|
+
* // => [{ q: 'a', z: [1, 2] }, { q: 'b', z: [2, 3] }]
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export function selectWith(selector) {
|
|
140
|
+
return (source) => Deep.select(source, selector);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Returns the result of applying the given `selector` shape to the given `source` value.
|
|
144
|
+
* @typeparam T - the patch value type
|
|
145
|
+
* @typeparam P - the string literal path type in the object
|
|
146
|
+
* @typeparam SL - the selector shape type
|
|
147
|
+
* @param source - the source value to select from
|
|
148
|
+
* @param path - the string path in the object
|
|
149
|
+
* @param selector - a shape indicating the selection from the source value at the given path
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* const item = { a: { b: 1, c: 'a' } };
|
|
153
|
+
* Deep.selectAt(item, 'a', { q: 'c', z: ['b', v => v.b + 1] as const });
|
|
154
|
+
* // => { q: 'a', z: [1, 2] }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export function selectAt(source, path, selector) {
|
|
158
|
+
return Deep.select(Deep.getAt(source, path), selector);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Returns a function that selects a certain shape from a given `value` with the given `selector` at the given string `path`.
|
|
162
|
+
* @typeparam T - the patch value type
|
|
163
|
+
* @typeparam P - the string literal path type in the object
|
|
164
|
+
* @typeparam SL - the selector shape type
|
|
165
|
+
* @param path - the string path in the object
|
|
166
|
+
* @param selector - a shape indicating the selection from the source values
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* const items = [{ a: { b: 1, c: 'a' } }, { a: { b: 2, c: 'b' } }];
|
|
170
|
+
* items.map(Deep.selectAtWith('a', { q: 'c', z: ['b', v => v.b + 1] as const }));
|
|
171
|
+
* // => [{ q: 'a', z: [1, 2] }, { q: 'b', z: [2, 3] }]
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
export function selectAtWith(path, selector) {
|
|
175
|
+
return (source) => Deep.selectAt(source, path, selector);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Returns a curried API with a known target type. This can be useful for using the methods in
|
|
179
|
+
* contexts where the target type can be inferred from the usage.
|
|
180
|
+
* @typeparam T - the target type
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* const s = { a: 1, b: { c: 'a', d: true }}
|
|
184
|
+
* const upd = Deep.withType<typeof s>().patchWith([{ d: (v) => !v }])
|
|
185
|
+
* upd(s)
|
|
186
|
+
* // => { a: 1, b: { c: 'a', d: false }}
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export function withType() {
|
|
190
|
+
return Deep;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=deep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deep.js","sourceRoot":"","sources":["../../src/deep.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,OAAO,EAAE,KAAK,EAAc,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAc,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAa,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,MAAM,EAAiB,MAAM,YAAY,CAAC;AAGnD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,OAAO,CAAI,MAAS;IAClC,OAAO,MAAsB,CAAC;AAChC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CACvB,IAAO;IAEP,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CACvB,SAAwB;IAExB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,WAAW,CACzB,IAAO,EACP,SAAwD;IAExD,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,SAAgB,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAI,OAAiB;IAC5C,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,OAAO,CACrB,MAAS,EACT,IAAO,EACP,OAAiC;IAEjC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,WAAW,CACzB,IAAO,EACP,OAAsC;IAEtC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,OAAc,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CACxB,QAA4B;IAE5B,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ,CAKtB,MAAS,EACT,IAAO,EACP,QAA4B;IAE5B,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY,CAK1B,IAAO,EACP,QAA4B;IAE5B,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC3D,CAAC;AAgID;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/module/index.js
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* <br/>
|
|
6
6
|
* See the [Rimbu docs Deep overview page](/docs/deep/overview) for more information.
|
|
7
7
|
*/
|
|
8
|
-
export { patch, patchNested, Patch, match, Match, Path, Protected, } from './internal';
|
|
9
8
|
export { Tuple } from './tuple';
|
|
9
|
+
export { Path } from './internal';
|
|
10
|
+
export * from './deep';
|
|
11
|
+
import * as Deep from './deep';
|
|
12
|
+
export {
|
|
13
|
+
/**
|
|
14
|
+
* Convenience namespace offering access to most common functions used in the `@rimbu/deep` package.
|
|
15
|
+
* These are mainly utilities to patch and match plain JavaScript objects.
|
|
16
|
+
*/
|
|
17
|
+
Deep, };
|
|
10
18
|
//# sourceMappingURL=index.js.map
|
package/dist/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGhC,OAAO,EAAE,IAAI,EAA6B,MAAM,YAAY,CAAC;AAE7D,cAAc,QAAQ,CAAC;AAEvB,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO;AACL;;;GAGG;AACH,IAAI,GACL,CAAC"}
|
package/dist/module/internal.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
export
|
|
1
|
+
export { Path } from './path';
|
|
2
|
+
export {} from './match';
|
|
3
|
+
import * as Deep from './deep';
|
|
4
|
+
export { Deep };
|
|
5
5
|
//# sourceMappingURL=internal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.js","sourceRoot":"","sources":["../../src/internal.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"internal.js","sourceRoot":"","sources":["../../src/internal.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAc,MAAM,SAAS,CAAC;AAIrC,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,CAAC"}
|
package/dist/module/match.js
CHANGED
|
@@ -1,222 +1,202 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export var Match;
|
|
3
|
-
(function (Match) {
|
|
4
|
-
/**
|
|
5
|
-
* Returns a matcher that returns true if every given `matchItem` matches the given value.
|
|
6
|
-
* @typeparam T - the type of value to match
|
|
7
|
-
* @typeparam R - the root object type
|
|
8
|
-
* @typeparam Q - a utility type for the matcher
|
|
9
|
-
* @param matchItems - the match specifications to test
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* const input = { a: 1, b: { c: true, d: 'a' } }
|
|
13
|
-
* match(input, Match.every({ a: 1, { c: true } } )) // => true
|
|
14
|
-
* match(input, Match.every({ a: 1, { c: false } } )) // => false
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
function every(...matchItems) {
|
|
18
|
-
return new Every(matchItems);
|
|
19
|
-
}
|
|
20
|
-
Match.every = every;
|
|
21
|
-
/**
|
|
22
|
-
* Returns a matcher that returns true if at least one of given `matchItem` matches the given value.
|
|
23
|
-
* @typeparam T - the type of value to match
|
|
24
|
-
* @typeparam R - the root object type
|
|
25
|
-
* @typeparam Q - a utility type for the matcher
|
|
26
|
-
* @param matchItems - the match specifications to test
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* const input = { a: 1, b: { c: true, d: 'a' } }
|
|
30
|
-
* match(input, Match.some({ a: 5, { c: true } } )) // => true
|
|
31
|
-
* match(input, Match.some({ a: 5, { c: false } } )) // => false
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
function some(...matchItems) {
|
|
35
|
-
return new Some(matchItems);
|
|
36
|
-
}
|
|
37
|
-
Match.some = some;
|
|
38
|
-
/**
|
|
39
|
-
* Returns a matcher that returns true if none of given `matchItem` matches the given value.
|
|
40
|
-
* @typeparam T - the type of value to match
|
|
41
|
-
* @typeparam R - the root object type
|
|
42
|
-
* @typeparam Q - a utility type for the matcher
|
|
43
|
-
* @param matchItems - the match specifications to test
|
|
44
|
-
* @example
|
|
45
|
-
* ```ts
|
|
46
|
-
* const input = { a: 1, b: { c: true, d: 'a' } }
|
|
47
|
-
* match(input, Match.none({ a: 5, { c: true } } )) // => false
|
|
48
|
-
* match(input, Match.none({ a: 5, { c: false } } )) // => true
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
function none(...matchItems) {
|
|
52
|
-
return new None(matchItems);
|
|
53
|
-
}
|
|
54
|
-
Match.none = none;
|
|
55
|
-
/**
|
|
56
|
-
* Returns a matcher that returns true if exactly one of given `matchItem` matches the given value.
|
|
57
|
-
* @typeparam T - the type of value to match
|
|
58
|
-
* @typeparam R - the root object type
|
|
59
|
-
* @typeparam Q - a utility type for the matcher
|
|
60
|
-
* @param matchItems - the match specifications to test
|
|
61
|
-
* @example
|
|
62
|
-
* ```ts
|
|
63
|
-
* const input = { a: 1, b: { c: true, d: 'a' } }
|
|
64
|
-
* match(input, Match.single({ a: 1, { c: true } } )) // => false
|
|
65
|
-
* match(input, Match.single({ a: 1, { c: false } } )) // => true
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
|
-
function single(...matchItems) {
|
|
69
|
-
return new Single(matchItems);
|
|
70
|
-
}
|
|
71
|
-
Match.single = single;
|
|
72
|
-
})(Match || (Match = {}));
|
|
73
|
-
class Every {
|
|
74
|
-
constructor(matchItems) {
|
|
75
|
-
this.matchItems = matchItems;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
class Some {
|
|
79
|
-
constructor(matchItems) {
|
|
80
|
-
this.matchItems = matchItems;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
class None {
|
|
84
|
-
constructor(matchItems) {
|
|
85
|
-
this.matchItems = matchItems;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
class Single {
|
|
89
|
-
constructor(matchItems) {
|
|
90
|
-
this.matchItems = matchItems;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
1
|
+
import { isPlainObj, } from '@rimbu/base';
|
|
93
2
|
/**
|
|
94
3
|
* Returns true if the given `value` object matches the given `matcher`, false otherwise.
|
|
95
4
|
* @typeparam T - the input value type
|
|
96
|
-
* @
|
|
5
|
+
* @typeparam C - utility type
|
|
6
|
+
* @param source - the value to match (should be a plain object)
|
|
97
7
|
* @param matcher - a matcher object or a function taking the matcher API and returning a match object
|
|
8
|
+
* @param errorCollector - (optional) a string array that can be passed to collect reasons why the match failed
|
|
98
9
|
* @example
|
|
99
10
|
* ```ts
|
|
100
11
|
* const input = { a: 1, b: { c: true, d: 'a' } }
|
|
101
12
|
* match(input, { a: 1 }) // => true
|
|
102
13
|
* match(input, { a: 2 }) // => false
|
|
103
|
-
* match(input, { a: v => v > 10 }) // => false
|
|
14
|
+
* match(input, { a: (v) => v > 10 }) // => false
|
|
104
15
|
* match(input, { b: { c: true }}) // => true
|
|
105
|
-
* match(input, (
|
|
16
|
+
* match(input, (['every', { a: (v) => v > 0 }, { b: { c: true } }]) // => true
|
|
106
17
|
* match(input, { b: { c: (v, parent, root) => v && parent.d.length > 0 && root.a > 0 } })
|
|
107
18
|
* // => true
|
|
108
19
|
* ```
|
|
109
20
|
*/
|
|
110
|
-
export function match(
|
|
111
|
-
|
|
112
|
-
return matchOptions(value, value, matcher(Match));
|
|
113
|
-
}
|
|
114
|
-
return matchOptions(value, value, matcher);
|
|
21
|
+
export function match(source, matcher, errorCollector = undefined) {
|
|
22
|
+
return matchEntry(source, source, source, matcher, errorCollector);
|
|
115
23
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (!matchOptions(value, root, matchItems[i])) {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
24
|
+
/**
|
|
25
|
+
* Match a generic match entry against the given source.
|
|
26
|
+
*/
|
|
27
|
+
function matchEntry(source, parent, root, matcher, errorCollector) {
|
|
28
|
+
if (Object.is(source, matcher)) {
|
|
29
|
+
// value and target are exactly the same, always will be true
|
|
126
30
|
return true;
|
|
127
31
|
}
|
|
128
|
-
if (matcher
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
32
|
+
if (matcher === null || matcher === undefined) {
|
|
33
|
+
// these matchers can only be direct matches, and previously it was determined that
|
|
34
|
+
// they are not equal
|
|
35
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`value ${JSON.stringify(source)} did not match matcher ${matcher}`);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (typeof source === 'function') {
|
|
39
|
+
// function source values can only be directly matched
|
|
40
|
+
const result = Object.is(source, matcher);
|
|
41
|
+
if (!result) {
|
|
42
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`both value and matcher are functions, but they do not have the same reference`);
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
if (typeof matcher === 'function') {
|
|
47
|
+
// resolve match function first
|
|
48
|
+
const matcherResult = matcher(source, parent, root);
|
|
49
|
+
if (typeof matcherResult === 'boolean') {
|
|
50
|
+
// function resulted in a direct match result
|
|
51
|
+
if (!matcherResult) {
|
|
52
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`function matcher returned false for value ${JSON.stringify(source)}`);
|
|
135
53
|
}
|
|
54
|
+
return matcherResult;
|
|
136
55
|
}
|
|
137
|
-
|
|
56
|
+
// function resulted in a value that needs to be further matched
|
|
57
|
+
return matchEntry(source, parent, root, matcherResult, errorCollector);
|
|
138
58
|
}
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
59
|
+
if (isPlainObj(source)) {
|
|
60
|
+
// source ia a plain object, can be partially matched
|
|
61
|
+
return matchPlainObj(source, parent, root, matcher, errorCollector);
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(source)) {
|
|
64
|
+
// source is an array
|
|
65
|
+
return matchArr(source, root, matcher, errorCollector);
|
|
66
|
+
}
|
|
67
|
+
// already determined above that the source and matcher are not equal
|
|
68
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`value ${JSON.stringify(source)} does not match given matcher ${JSON.stringify(matcher)}`);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Match an array matcher against the given source.
|
|
73
|
+
*/
|
|
74
|
+
function matchArr(source, root, matcher, errorCollector) {
|
|
75
|
+
if (Array.isArray(matcher)) {
|
|
76
|
+
// directly compare array contents
|
|
77
|
+
const length = source.length;
|
|
78
|
+
if (length !== matcher.length) {
|
|
79
|
+
// if lengths not equal, arrays are not equal
|
|
80
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`array lengths are not equal: value length ${source.length} !== matcher length ${matcher.length}`);
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
// loop over arrays, matching every value
|
|
84
|
+
let index = -1;
|
|
85
|
+
while (++index < length) {
|
|
86
|
+
if (!Object.is(source[index], matcher[index])) {
|
|
87
|
+
// item did not match, return false
|
|
88
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`index ${index} does not match with value ${JSON.stringify(source[index])} and matcher ${matcher[index]}`);
|
|
145
89
|
return false;
|
|
146
90
|
}
|
|
147
91
|
}
|
|
92
|
+
// all items are equal
|
|
148
93
|
return true;
|
|
149
94
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
95
|
+
// matcher is plain object with index keys
|
|
96
|
+
for (const index in matcher) {
|
|
97
|
+
const matcherAtIndex = matcher[index];
|
|
98
|
+
if (!(index in source)) {
|
|
99
|
+
// source does not have item at given index
|
|
100
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`index ${index} does not exist in source ${JSON.stringify(source)} but should match matcher ${JSON.stringify(matcherAtIndex)}`);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
// match the source item at the given index
|
|
104
|
+
const result = matchEntry(source[index], source, root, matcherAtIndex, errorCollector);
|
|
105
|
+
if (!result) {
|
|
106
|
+
// item did not match
|
|
107
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`index ${index} does not match with value ${JSON.stringify(source[index])} and matcher ${JSON.stringify(matcherAtIndex)}`);
|
|
108
|
+
return false;
|
|
162
109
|
}
|
|
163
|
-
return matched;
|
|
164
|
-
}
|
|
165
|
-
if (isPlainObj(matcher)) {
|
|
166
|
-
return matchRecord(value, root, matcher);
|
|
167
110
|
}
|
|
168
|
-
|
|
111
|
+
// all items match
|
|
112
|
+
return true;
|
|
169
113
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Match an object matcher against the given source.
|
|
116
|
+
*/
|
|
117
|
+
function matchPlainObj(source, parent, root, matcher, errorCollector) {
|
|
118
|
+
if (Array.isArray(matcher)) {
|
|
119
|
+
// the matcher is of compound type
|
|
120
|
+
return matchCompound(source, parent, root, matcher, errorCollector);
|
|
173
121
|
}
|
|
122
|
+
// partial object props matcher
|
|
174
123
|
for (const key in matcher) {
|
|
175
|
-
if (!(key in
|
|
124
|
+
if (!(key in source)) {
|
|
125
|
+
// the source does not have the given key
|
|
126
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`key ${key} is specified in matcher but not present in value ${JSON.stringify(source)}`);
|
|
176
127
|
return false;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
128
|
+
}
|
|
129
|
+
// match the source value at the given key with the matcher at given key
|
|
130
|
+
const result = matchEntry(source[key], source, root, matcher[key], errorCollector);
|
|
131
|
+
if (!result) {
|
|
132
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`key ${key} does not match in value ${JSON.stringify(source[key])} with matcher ${JSON.stringify(matcher[key])}`);
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// all properties match
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Match a compound matcher against the given source.
|
|
141
|
+
*/
|
|
142
|
+
function matchCompound(source, parent, root, compound, errorCollector) {
|
|
143
|
+
// first item indicates compound match type
|
|
144
|
+
const matchType = compound[0];
|
|
145
|
+
const length = compound.length;
|
|
146
|
+
// start at index 1
|
|
147
|
+
let index = 0;
|
|
148
|
+
switch (matchType) {
|
|
149
|
+
case 'every': {
|
|
150
|
+
while (++index < length) {
|
|
151
|
+
// if any item does not match, return false
|
|
152
|
+
const result = matchEntry(source, parent, root, compound[index], errorCollector);
|
|
153
|
+
if (!result) {
|
|
154
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "every": match at index ${index} failed`);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
182
157
|
}
|
|
183
|
-
|
|
184
|
-
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
case 'none': {
|
|
161
|
+
// if any item matches, return false
|
|
162
|
+
while (++index < length) {
|
|
163
|
+
const result = matchEntry(source, parent, root, compound[index], errorCollector);
|
|
185
164
|
if (result) {
|
|
186
|
-
|
|
165
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "none": match at index ${index} succeeded`);
|
|
166
|
+
return false;
|
|
187
167
|
}
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
if (!matchRecordItem(target, root, result)) {
|
|
191
|
-
return false;
|
|
192
168
|
}
|
|
169
|
+
return true;
|
|
193
170
|
}
|
|
194
|
-
|
|
195
|
-
if
|
|
196
|
-
|
|
171
|
+
case 'single': {
|
|
172
|
+
// if not exactly one item matches, return false
|
|
173
|
+
let onePassed = false;
|
|
174
|
+
while (++index < length) {
|
|
175
|
+
const result = matchEntry(source, parent, root, compound[index], errorCollector);
|
|
176
|
+
if (result) {
|
|
177
|
+
if (onePassed) {
|
|
178
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "single": multiple matches succeeded`);
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
onePassed = true;
|
|
182
|
+
}
|
|
197
183
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return true;
|
|
201
|
-
}
|
|
202
|
-
function matchRecordItem(value, root, matcher) {
|
|
203
|
-
if (isIterable(matcher) && isIterable(value)) {
|
|
204
|
-
const it1 = value[Symbol.iterator]();
|
|
205
|
-
const it2 = matcher[Symbol.iterator]();
|
|
206
|
-
while (true) {
|
|
207
|
-
const v1 = it1.next();
|
|
208
|
-
const v2 = it2.next();
|
|
209
|
-
if (v1.done !== v2.done || v1.value !== v2.value) {
|
|
210
|
-
return false;
|
|
184
|
+
if (!onePassed) {
|
|
185
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "single": no matches succeeded`);
|
|
211
186
|
}
|
|
212
|
-
|
|
213
|
-
|
|
187
|
+
return onePassed;
|
|
188
|
+
}
|
|
189
|
+
case 'some': {
|
|
190
|
+
// if any item matches, return true
|
|
191
|
+
while (++index < length) {
|
|
192
|
+
const result = matchEntry(source, parent, root, compound[index], errorCollector);
|
|
193
|
+
if (result) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
214
196
|
}
|
|
197
|
+
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "some": no matches succeeded`);
|
|
198
|
+
return false;
|
|
215
199
|
}
|
|
216
200
|
}
|
|
217
|
-
if (isPlainObj(value)) {
|
|
218
|
-
return matchOptions(value, root, matcher);
|
|
219
|
-
}
|
|
220
|
-
return Object.is(value, matcher);
|
|
221
201
|
}
|
|
222
202
|
//# sourceMappingURL=match.js.map
|