event-storage 1.1.0 → 1.2.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/README.md +21 -0
- package/package.json +3 -5
- package/src/Consumer.js +9 -7
- package/src/EventStore.js +165 -83
- package/src/EventStream.js +49 -11
- package/src/Index/ReadableIndex.js +7 -5
- package/src/Index/WritableIndex.js +4 -6
- package/src/IndexMatcher.js +2 -2
- package/src/JoinEventStream.js +30 -46
- package/src/Partition/ReadablePartition.js +153 -85
- package/src/Partition/WritablePartition.js +37 -28
- package/src/Storage/ReadOnlyStorage.js +3 -3
- package/src/Storage/ReadableStorage.js +73 -102
- package/src/Storage/WritableStorage.js +43 -25
- package/src/Watcher.js +1 -1
- package/src/utils/jsonUtil.js +87 -0
- package/src/utils/metadataUtil.js +247 -0
- package/src/{util.js → utils/util.js} +52 -17
- package/src/metadataUtil.js +0 -126
- /package/src/{fsUtil.js → utils/fsUtil.js} +0 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { assert, assertEqual } from './util.js';
|
|
3
|
+
import { BYTE_OPEN_OBJECT, indexOfSameLevel } from './jsonUtil.js';
|
|
4
|
+
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function propertyMatchesValue(documentValue, matcherValue) {
|
|
10
|
+
if (Array.isArray(matcherValue)) {
|
|
11
|
+
return matcherValue.includes(documentValue);
|
|
12
|
+
} else if (matcherValue && typeof matcherValue === 'object') {
|
|
13
|
+
return matches(documentValue, matcherValue);
|
|
14
|
+
}
|
|
15
|
+
return typeof matcherValue === 'undefined' || documentValue === matcherValue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Build a buffer containing the file magic header and a JSON stringified metadata block, padded to be a multiple of 16 bytes long.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} magic
|
|
22
|
+
* @param {object} metadata
|
|
23
|
+
* @returns {Buffer} A buffer containing the header data
|
|
24
|
+
*/
|
|
25
|
+
function buildMetadataHeader(magic, metadata) {
|
|
26
|
+
assertEqual(magic.length, 8, 'The header magic bytes length is wrong.');
|
|
27
|
+
let metadataString = JSON.stringify(metadata);
|
|
28
|
+
let metadataSize = Buffer.byteLength(metadataString, 'utf8');
|
|
29
|
+
// 8 byte MAGIC, 4 byte metadata size, 1 byte line break
|
|
30
|
+
const pad = (16 - ((8 + 4 + metadataSize + 1) % 16)) % 16;
|
|
31
|
+
metadataString += ' '.repeat(pad) + "\n";
|
|
32
|
+
metadataSize += pad + 1;
|
|
33
|
+
const metadataBuffer = Buffer.allocUnsafe(8 + 4 + metadataSize);
|
|
34
|
+
metadataBuffer.write(magic, 0, 8, 'utf8');
|
|
35
|
+
metadataBuffer.writeUInt32BE(metadataSize, 8);
|
|
36
|
+
metadataBuffer.write(metadataString, 8 + 4, metadataSize, 'utf8');
|
|
37
|
+
return metadataBuffer;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} secret The secret to use for calculating further HMACs
|
|
42
|
+
* @returns {function(string)} A function that calculates the HMAC for a given string
|
|
43
|
+
*/
|
|
44
|
+
const createHmac = secret => string => {
|
|
45
|
+
const hmac = crypto.createHmac('sha256', secret);
|
|
46
|
+
hmac.update(string);
|
|
47
|
+
return hmac.digest('hex');
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {object|function(object):boolean} Matcher
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {object} document The document to check against the matcher.
|
|
56
|
+
* @param {Matcher} matcher An object of properties and their values that need to match in the object or a function that checks if the document matches.
|
|
57
|
+
* @returns {boolean} True if the document matches the matcher or false otherwise.
|
|
58
|
+
*/
|
|
59
|
+
function matches(document, matcher) {
|
|
60
|
+
if (typeof document === 'undefined') return false;
|
|
61
|
+
if (typeof matcher === 'undefined') return true;
|
|
62
|
+
|
|
63
|
+
if (typeof matcher === 'function') return matcher(document);
|
|
64
|
+
|
|
65
|
+
for (let prop of Object.getOwnPropertyNames(matcher)) {
|
|
66
|
+
if (!propertyMatchesValue(document[prop], matcher[prop])) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {Matcher} matcher The matcher object or function that should be serialized.
|
|
75
|
+
* @param {function(string)} hmac A function that calculates a HMAC of the given string.
|
|
76
|
+
* @returns {{matcher: string|object, hmac?: string}}
|
|
77
|
+
*/
|
|
78
|
+
function buildMetadataForMatcher(matcher, hmac) {
|
|
79
|
+
if (!matcher) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
if (typeof matcher === 'object') {
|
|
83
|
+
return { matcher };
|
|
84
|
+
}
|
|
85
|
+
const matcherString = matcher.toString();
|
|
86
|
+
return { matcher: matcherString, hmac: hmac(matcherString) };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {{matcher: string|object, hmac: string}} matcherMetadata The serialized matcher and its HMAC
|
|
91
|
+
* @param {function(string)} hmac A function that calculates a HMAC of the given string.
|
|
92
|
+
* @returns {Matcher} The matcher object or function.
|
|
93
|
+
*/
|
|
94
|
+
function buildMatcherFromMetadata(matcherMetadata, hmac) {
|
|
95
|
+
let matcher;
|
|
96
|
+
if (typeof matcherMetadata.matcher === 'object') {
|
|
97
|
+
matcher = matcherMetadata.matcher;
|
|
98
|
+
} else {
|
|
99
|
+
assert(matcherMetadata.hmac === hmac(matcherMetadata.matcher), 'Invalid HMAC for matcher.');
|
|
100
|
+
|
|
101
|
+
matcher = eval('(' + matcherMetadata.matcher + ')').bind({}); // jshint ignore:line
|
|
102
|
+
}
|
|
103
|
+
return matcher;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Builds a factory function that, given a type string, returns an object matcher for
|
|
108
|
+
* documents whose payload contains that type at the given dot-notation path.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} payloadPath Dot-notation path relative to the event payload (e.g. `'type'`, `'meta.kind'`).
|
|
111
|
+
* @returns {function(string): object} A function `(typeValue) => objectMatcher`.
|
|
112
|
+
*/
|
|
113
|
+
function buildTypeMatcherFn(payloadPath) {
|
|
114
|
+
const parts = payloadPath.split('.');
|
|
115
|
+
return function(typeValue) {
|
|
116
|
+
let obj = typeValue;
|
|
117
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
118
|
+
obj = { [parts[i]]: obj };
|
|
119
|
+
}
|
|
120
|
+
return { payload: obj };
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Builds a raw-buffer matcher.
|
|
126
|
+
* It expects the Buffer to contain compact stringified JSON
|
|
127
|
+
* and supports matcher objects with sub properties and multi-value matches (OR/any of).
|
|
128
|
+
*
|
|
129
|
+
* @param {object} matcher Object matcher.
|
|
130
|
+
* @returns {function(Buffer): boolean}
|
|
131
|
+
*/
|
|
132
|
+
function buildRawBufferMatcher(matcher = {}) {
|
|
133
|
+
assert(matcher && typeof matcher ==='object' && !Array.isArray(matcher), 'Matcher must be an object.', TypeError);
|
|
134
|
+
|
|
135
|
+
const root = buildMatcherTree(matcher);
|
|
136
|
+
if (root.children.length === 0) {
|
|
137
|
+
return () => true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return function matchesRawBuffer(buffer) {
|
|
141
|
+
if (buffer[0] !== BYTE_OPEN_OBJECT) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
if (!preCheck(buffer, 1, root)) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return matchesNode(buffer, 1, root);
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Optimization pass: check that every required byte pattern is present anywhere in the buffer
|
|
153
|
+
* before spending the more expensive per-depth scan in `matchesNode`.
|
|
154
|
+
*/
|
|
155
|
+
function preCheck(buffer, startOffset, node) {
|
|
156
|
+
for (const child of node.children) {
|
|
157
|
+
if (child.valuePatterns && !child.valuePatterns.some((pattern, i) => {
|
|
158
|
+
child.valueMatches[i] = buffer.indexOf(pattern, startOffset);
|
|
159
|
+
return child.valueMatches[i] !== -1;
|
|
160
|
+
})) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
if (child.objectPattern) {
|
|
164
|
+
const objectMatch = buffer.indexOf(child.objectPattern, startOffset);
|
|
165
|
+
if (objectMatch === -1) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
child.objMatch = objectMatch;
|
|
169
|
+
if (!preCheck(buffer, objectMatch, child.node)) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Pre-compile a plain object matcher into a tree of byte patterns so `matchesNode` can scan
|
|
179
|
+
* raw JSON buffers without deserializing them.
|
|
180
|
+
*/
|
|
181
|
+
function buildMatcherTree(matcher) {
|
|
182
|
+
const node = { children: [] };
|
|
183
|
+
|
|
184
|
+
for (const [key, value] of Object.entries(matcher)) {
|
|
185
|
+
node.children.push(buildMatcherTreeChild(key, value));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return node;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function buildMatcherTreeChild(key, value) {
|
|
192
|
+
const keyPrefix = Buffer.from(`${JSON.stringify(key)}:`, 'utf8');
|
|
193
|
+
const child = { objectPattern: null, valuePatterns: null, node: null, objMatch: null, valueMatches: [] };
|
|
194
|
+
if (Array.isArray(value)) {
|
|
195
|
+
if (value.some(item => item && typeof item === 'object')) {
|
|
196
|
+
throw new TypeError('Array matcher values must be scalars.');
|
|
197
|
+
}
|
|
198
|
+
child.valuePatterns = value.map(item => buildValuePattern(keyPrefix, item));
|
|
199
|
+
return child;
|
|
200
|
+
} else if (value && typeof value === 'object') {
|
|
201
|
+
child.objectPattern = Buffer.concat([keyPrefix, Buffer.from('{', 'utf8')]);
|
|
202
|
+
child.node = buildMatcherTree(value);
|
|
203
|
+
return child;
|
|
204
|
+
}
|
|
205
|
+
child.valuePatterns = [buildValuePattern(keyPrefix, value)];
|
|
206
|
+
return child;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function buildValuePattern(keyPrefix, value) {
|
|
210
|
+
return Buffer.concat([keyPrefix, Buffer.from(JSON.stringify(value), 'utf8')]);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Verify that each required byte pattern in the tree is present at the correct JSON nesting
|
|
215
|
+
* depth so values inside nested objects don't satisfy a top-level match requirement.
|
|
216
|
+
*/
|
|
217
|
+
function matchesNode(buffer, startOffset, node) {
|
|
218
|
+
for (const child of node.children) {
|
|
219
|
+
if (child.valuePatterns && !child.valuePatterns.some((pattern, i) => {
|
|
220
|
+
return indexOfSameLevel(buffer, pattern, startOffset, child.valueMatches[i]) !== -1;
|
|
221
|
+
})) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (child.node) {
|
|
226
|
+
const objectIndex = indexOfSameLevel(buffer, child.objectPattern, startOffset, child.objMatch);
|
|
227
|
+
if (objectIndex === -1) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
if (!matchesNode(buffer, objectIndex + child.objectPattern.length, child.node)) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export {
|
|
240
|
+
createHmac,
|
|
241
|
+
matches,
|
|
242
|
+
buildMetadataHeader,
|
|
243
|
+
buildMetadataForMatcher,
|
|
244
|
+
buildMatcherFromMetadata,
|
|
245
|
+
buildTypeMatcherFn,
|
|
246
|
+
buildRawBufferMatcher
|
|
247
|
+
};
|
|
@@ -113,26 +113,60 @@ function wrapAndCheck(index, length) {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
|
-
*
|
|
117
|
-
* Each stream object is mutated in place by the `advance` function.
|
|
116
|
+
* Iterate an array-like list in forward or reverse order.
|
|
118
117
|
*
|
|
119
|
-
* @param {
|
|
120
|
-
* @param {
|
|
121
|
-
* @param {function(object): boolean} advance Advances the stream to its next item.
|
|
122
|
-
* Returns true if the stream has more items within range, false if exhausted.
|
|
123
|
-
* @param {function(object): void} visit Called for each stream state in merged order.
|
|
118
|
+
* @param {Iterable} entries
|
|
119
|
+
* @param {boolean} forwards
|
|
124
120
|
*/
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
121
|
+
function* iterate(entries, forwards) {
|
|
122
|
+
if (forwards) {
|
|
123
|
+
yield* entries;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
128
|
+
yield entries[i];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Perform a k-way merge over multiple iterables in sort-key order.
|
|
134
|
+
*
|
|
135
|
+
* Each iterable is primed by calling `.next()` once at startup. On each merge step the iterable
|
|
136
|
+
* with the best current value is advanced and its value is yielded (after passing through `visit`).
|
|
137
|
+
* An iterable is dropped once its iterator reports `done`.
|
|
138
|
+
*
|
|
139
|
+
* @param {Iterable[]|Iterator[]} iterables Iterables or bare iterators to merge.
|
|
140
|
+
* @param {function(*): number} getSortKey Extracts the numeric sort key from an iterable's current value.
|
|
141
|
+
* @param {boolean} [ascending=true] When true, yields items in ascending key order (min-merge).
|
|
142
|
+
* When false, yields in descending key order (max-merge).
|
|
143
|
+
* @param {function(*): *} [visit] Optional extractor for the yielded value. Defaults to identity.
|
|
144
|
+
* @returns {Generator<*>}
|
|
145
|
+
*/
|
|
146
|
+
function *kWayMerge(iterables, getSortKey, ascending = true, visit = v => v) {
|
|
147
|
+
const states = [];
|
|
148
|
+
for (const iterable of iterables) {
|
|
149
|
+
const iterator = typeof iterable[Symbol.iterator] === 'function' ? iterable[Symbol.iterator]() : iterable;
|
|
150
|
+
const { value, done } = iterator.next();
|
|
151
|
+
if (!done) {
|
|
152
|
+
states.push({ iterator, current: value });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
while (states.length > 0) {
|
|
157
|
+
let bestIdx = 0;
|
|
158
|
+
for (let i = 1; i < states.length; i++) {
|
|
159
|
+
const better = ascending
|
|
160
|
+
? getSortKey(states[i].current) < getSortKey(states[bestIdx].current)
|
|
161
|
+
: getSortKey(states[i].current) > getSortKey(states[bestIdx].current);
|
|
162
|
+
if (better) bestIdx = i;
|
|
132
163
|
}
|
|
133
|
-
visit(
|
|
134
|
-
|
|
135
|
-
|
|
164
|
+
yield visit(states[bestIdx].current);
|
|
165
|
+
const { value, done } = states[bestIdx].iterator.next();
|
|
166
|
+
if (done) {
|
|
167
|
+
states.splice(bestIdx, 1);
|
|
168
|
+
} else {
|
|
169
|
+
states[bestIdx].current = value;
|
|
136
170
|
}
|
|
137
171
|
}
|
|
138
172
|
}
|
|
@@ -160,6 +194,7 @@ export {
|
|
|
160
194
|
assertEqual,
|
|
161
195
|
hash,
|
|
162
196
|
wrapAndCheck,
|
|
197
|
+
iterate,
|
|
163
198
|
binarySearch,
|
|
164
199
|
alignTo,
|
|
165
200
|
kWayMerge,
|
package/src/metadataUtil.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
import { assertEqual } from './util.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Build a buffer containing the file magic header and a JSON stringified metadata block, padded to be a multiple of 16 bytes long.
|
|
6
|
-
*
|
|
7
|
-
* @param {string} magic
|
|
8
|
-
* @param {object} metadata
|
|
9
|
-
* @returns {Buffer} A buffer containing the header data
|
|
10
|
-
*/
|
|
11
|
-
function buildMetadataHeader(magic, metadata) {
|
|
12
|
-
assertEqual(magic.length, 8, 'The header magic bytes length is wrong.');
|
|
13
|
-
let metadataString = JSON.stringify(metadata);
|
|
14
|
-
let metadataSize = Buffer.byteLength(metadataString, 'utf8');
|
|
15
|
-
// 8 byte MAGIC, 4 byte metadata size, 1 byte line break
|
|
16
|
-
const pad = (16 - ((8 + 4 + metadataSize + 1) % 16)) % 16;
|
|
17
|
-
metadataString += ' '.repeat(pad) + "\n";
|
|
18
|
-
metadataSize += pad + 1;
|
|
19
|
-
const metadataBuffer = Buffer.allocUnsafe(8 + 4 + metadataSize);
|
|
20
|
-
metadataBuffer.write(magic, 0, 8, 'utf8');
|
|
21
|
-
metadataBuffer.writeUInt32BE(metadataSize, 8);
|
|
22
|
-
metadataBuffer.write(metadataString, 8 + 4, metadataSize, 'utf8');
|
|
23
|
-
return metadataBuffer;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @param {string} secret The secret to use for calculating further HMACs
|
|
28
|
-
* @returns {function(string)} A function that calculates the HMAC for a given string
|
|
29
|
-
*/
|
|
30
|
-
const createHmac = secret => string => {
|
|
31
|
-
const hmac = crypto.createHmac('sha256', secret);
|
|
32
|
-
hmac.update(string);
|
|
33
|
-
return hmac.digest('hex');
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @typedef {object|function(object):boolean} Matcher
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @param {object} document The document to check against the matcher.
|
|
42
|
-
* @param {Matcher} matcher An object of properties and their values that need to match in the object or a function that checks if the document matches.
|
|
43
|
-
* @returns {boolean} True if the document matches the matcher or false otherwise.
|
|
44
|
-
*/
|
|
45
|
-
function matches(document, matcher) {
|
|
46
|
-
if (typeof document === 'undefined') return false;
|
|
47
|
-
if (typeof matcher === 'undefined') return true;
|
|
48
|
-
|
|
49
|
-
if (typeof matcher === 'function') return matcher(document);
|
|
50
|
-
|
|
51
|
-
for (let prop of Object.getOwnPropertyNames(matcher)) {
|
|
52
|
-
if (Array.isArray(matcher[prop])) {
|
|
53
|
-
if (!matcher[prop].includes(document[prop])) {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
} else if (typeof matcher[prop] === 'object') {
|
|
57
|
-
if (!matches(document[prop], matcher[prop])) {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
} else if (typeof matcher[prop] !== 'undefined' && document[prop] !== matcher[prop]) {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @param {Matcher} matcher The matcher object or function that should be serialized.
|
|
69
|
-
* @param {function(string)} hmac A function that calculates a HMAC of the given string.
|
|
70
|
-
* @returns {{matcher: string|object, hmac?: string}}
|
|
71
|
-
*/
|
|
72
|
-
function buildMetadataForMatcher(matcher, hmac) {
|
|
73
|
-
if (!matcher) {
|
|
74
|
-
return undefined;
|
|
75
|
-
}
|
|
76
|
-
if (typeof matcher === 'object') {
|
|
77
|
-
return { matcher };
|
|
78
|
-
}
|
|
79
|
-
const matcherString = matcher.toString();
|
|
80
|
-
return { matcher: matcherString, hmac: hmac(matcherString) };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* @param {{matcher: string|object, hmac: string}} matcherMetadata The serialized matcher and its HMAC
|
|
85
|
-
* @param {function(string)} hmac A function that calculates a HMAC of the given string.
|
|
86
|
-
* @returns {Matcher} The matcher object or function.
|
|
87
|
-
*/
|
|
88
|
-
function buildMatcherFromMetadata(matcherMetadata, hmac) {
|
|
89
|
-
let matcher;
|
|
90
|
-
if (typeof matcherMetadata.matcher === 'object') {
|
|
91
|
-
matcher = matcherMetadata.matcher;
|
|
92
|
-
} else {
|
|
93
|
-
if (matcherMetadata.hmac !== hmac(matcherMetadata.matcher)) {
|
|
94
|
-
throw new Error('Invalid HMAC for matcher.');
|
|
95
|
-
}
|
|
96
|
-
matcher = eval('(' + matcherMetadata.matcher + ')').bind({}); // jshint ignore:line
|
|
97
|
-
}
|
|
98
|
-
return matcher;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Builds a factory function that, given a type string, returns an object matcher for
|
|
103
|
-
* documents whose payload contains that type at the given dot-notation path.
|
|
104
|
-
*
|
|
105
|
-
* @param {string} payloadPath Dot-notation path relative to the event payload (e.g. `'type'`, `'meta.kind'`).
|
|
106
|
-
* @returns {function(string): object} A function `(typeValue) => objectMatcher`.
|
|
107
|
-
*/
|
|
108
|
-
function buildTypeMatcherFn(payloadPath) {
|
|
109
|
-
const parts = payloadPath.split('.');
|
|
110
|
-
return function(typeValue) {
|
|
111
|
-
let obj = typeValue;
|
|
112
|
-
for (let i = parts.length - 1; i >= 0; i--) {
|
|
113
|
-
obj = { [parts[i]]: obj };
|
|
114
|
-
}
|
|
115
|
-
return { payload: obj };
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export {
|
|
120
|
-
createHmac,
|
|
121
|
-
matches,
|
|
122
|
-
buildMetadataHeader,
|
|
123
|
-
buildMetadataForMatcher,
|
|
124
|
-
buildMatcherFromMetadata,
|
|
125
|
-
buildTypeMatcherFn
|
|
126
|
-
};
|
|
File without changes
|