kareem 3.0.0 → 3.1.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/CHANGELOG.md +5 -0
- package/README.md +133 -161
- package/index.js +14 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -15,37 +15,28 @@ Named for the NBA's 2nd all-time leading scorer Kareem Abdul-Jabbar, known for h
|
|
|
15
15
|
|
|
16
16
|
## pre hooks
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
pre and post hooks: pre hooks are called before a given function executes.
|
|
20
|
-
Unlike hooks, kareem stores hooks and other internal state in a separate
|
|
21
|
-
object, rather than relying on inheritance. Furthermore, kareem exposes
|
|
22
|
-
an `execPre()` function that allows you to execute your pre hooks when
|
|
23
|
-
appropriate, giving you more fine-grained control over your function hooks.
|
|
18
|
+
NOTE: this file has some empty comment lines to workaround https://github.com/vkarpov15/acquit/issues/30
|
|
24
19
|
|
|
25
20
|
### It runs without any hooks specified
|
|
26
21
|
|
|
27
22
|
```javascript
|
|
28
|
-
hooks.execPre('cook', null
|
|
29
|
-
// ...
|
|
30
|
-
});
|
|
23
|
+
await hooks.execPre('cook', null);
|
|
31
24
|
```
|
|
32
25
|
|
|
33
26
|
### It runs basic serial pre hooks
|
|
34
27
|
|
|
35
|
-
pre hook functions
|
|
36
|
-
when your pre hook is finished.
|
|
28
|
+
pre hook functions can return a promise that resolves when finished.
|
|
37
29
|
|
|
38
30
|
```javascript
|
|
39
31
|
let count = 0;
|
|
40
32
|
|
|
41
|
-
hooks.pre('cook', function(
|
|
33
|
+
hooks.pre('cook', function() {
|
|
42
34
|
++count;
|
|
43
|
-
|
|
35
|
+
return Promise.resolve();
|
|
44
36
|
});
|
|
45
37
|
|
|
46
|
-
hooks.execPre('cook', null
|
|
47
|
-
|
|
48
|
-
});
|
|
38
|
+
await hooks.execPre('cook', null);
|
|
39
|
+
assert.equal(1, count);
|
|
49
40
|
```
|
|
50
41
|
|
|
51
42
|
### It can run multiple pre hooks
|
|
@@ -54,20 +45,19 @@ hooks.execPre('cook', null, function() {
|
|
|
54
45
|
let count1 = 0;
|
|
55
46
|
let count2 = 0;
|
|
56
47
|
|
|
57
|
-
hooks.pre('cook', function(
|
|
48
|
+
hooks.pre('cook', function() {
|
|
58
49
|
++count1;
|
|
59
|
-
|
|
50
|
+
return Promise.resolve();
|
|
60
51
|
});
|
|
61
52
|
|
|
62
|
-
hooks.pre('cook', function(
|
|
53
|
+
hooks.pre('cook', function() {
|
|
63
54
|
++count2;
|
|
64
|
-
|
|
55
|
+
return Promise.resolve();
|
|
65
56
|
});
|
|
66
57
|
|
|
67
|
-
hooks.execPre('cook', null
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
});
|
|
58
|
+
await hooks.execPre('cook', null);
|
|
59
|
+
assert.equal(1, count1);
|
|
60
|
+
assert.equal(1, count2);
|
|
71
61
|
```
|
|
72
62
|
|
|
73
63
|
### It can run fully synchronous pre hooks
|
|
@@ -87,11 +77,9 @@ hooks.pre('cook', function() {
|
|
|
87
77
|
++count2;
|
|
88
78
|
});
|
|
89
79
|
|
|
90
|
-
hooks.execPre('cook', null
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
assert.equal(1, count2);
|
|
94
|
-
});
|
|
80
|
+
await hooks.execPre('cook', null);
|
|
81
|
+
assert.equal(1, count1);
|
|
82
|
+
assert.equal(1, count2);
|
|
95
83
|
```
|
|
96
84
|
|
|
97
85
|
### It properly attaches context to pre hooks
|
|
@@ -99,63 +87,20 @@ hooks.execPre('cook', null, function(error) {
|
|
|
99
87
|
Pre save hook functions are bound to the second parameter to `execPre()`
|
|
100
88
|
|
|
101
89
|
```javascript
|
|
102
|
-
hooks.pre('cook', function(
|
|
90
|
+
hooks.pre('cook', function() {
|
|
103
91
|
this.bacon = 3;
|
|
104
|
-
done();
|
|
105
92
|
});
|
|
106
93
|
|
|
107
|
-
hooks.pre('cook', function(
|
|
94
|
+
hooks.pre('cook', function() {
|
|
108
95
|
this.eggs = 4;
|
|
109
|
-
done();
|
|
110
96
|
});
|
|
111
97
|
|
|
112
98
|
const obj = { bacon: 0, eggs: 0 };
|
|
113
99
|
|
|
114
100
|
// In the pre hooks, `this` will refer to `obj`
|
|
115
|
-
hooks.execPre('cook', obj
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
assert.equal(4, obj.eggs);
|
|
119
|
-
});
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### It can execute parallel (async) pre hooks
|
|
123
|
-
|
|
124
|
-
Like the hooks module, you can declare "async" pre hooks - these take two
|
|
125
|
-
parameters, the functions `next()` and `done()`. `next()` passes control to
|
|
126
|
-
the next pre hook, but the underlying function won't be called until all
|
|
127
|
-
async pre hooks have called `done()`.
|
|
128
|
-
|
|
129
|
-
```javascript
|
|
130
|
-
hooks.pre('cook', true, function(next, done) {
|
|
131
|
-
this.bacon = 3;
|
|
132
|
-
next();
|
|
133
|
-
setTimeout(function() {
|
|
134
|
-
done();
|
|
135
|
-
}, 5);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
hooks.pre('cook', true, function(next, done) {
|
|
139
|
-
next();
|
|
140
|
-
const _this = this;
|
|
141
|
-
setTimeout(function() {
|
|
142
|
-
_this.eggs = 4;
|
|
143
|
-
done();
|
|
144
|
-
}, 10);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
hooks.pre('cook', function(next) {
|
|
148
|
-
this.waffles = false;
|
|
149
|
-
next();
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
const obj = { bacon: 0, eggs: 0 };
|
|
153
|
-
|
|
154
|
-
hooks.execPre('cook', obj, function() {
|
|
155
|
-
assert.equal(3, obj.bacon);
|
|
156
|
-
assert.equal(4, obj.eggs);
|
|
157
|
-
assert.equal(false, obj.waffles);
|
|
158
|
-
});
|
|
101
|
+
await hooks.execPre('cook', obj);
|
|
102
|
+
assert.equal(3, obj.bacon);
|
|
103
|
+
assert.equal(4, obj.eggs);
|
|
159
104
|
```
|
|
160
105
|
|
|
161
106
|
### It supports returning a promise
|
|
@@ -176,9 +121,32 @@ hooks.pre('cook', function() {
|
|
|
176
121
|
|
|
177
122
|
const obj = { bacon: 0 };
|
|
178
123
|
|
|
179
|
-
hooks.execPre('cook', obj
|
|
180
|
-
|
|
124
|
+
await hooks.execPre('cook', obj);
|
|
125
|
+
assert.equal(3, obj.bacon);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### It supports filtering which hooks to run
|
|
129
|
+
|
|
130
|
+
You can pass a `filter` option to `execPre()` to select which hooks
|
|
131
|
+
to run. The filter function receives each hook object and should return
|
|
132
|
+
`true` to run the hook or `false` to skip it.
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
const execed = [];
|
|
136
|
+
|
|
137
|
+
const fn1 = function() { execed.push('first'); };
|
|
138
|
+
fn1.skipMe = true;
|
|
139
|
+
hooks.pre('cook', fn1);
|
|
140
|
+
|
|
141
|
+
const fn2 = function() { execed.push('second'); };
|
|
142
|
+
hooks.pre('cook', fn2);
|
|
143
|
+
|
|
144
|
+
// Only runs fn2, skips fn1 because fn1.skipMe is true
|
|
145
|
+
await hooks.execPre('cook', null, [], {
|
|
146
|
+
filter: hook => !hook.fn.skipMe
|
|
181
147
|
});
|
|
148
|
+
|
|
149
|
+
assert.deepStrictEqual(execed, ['second']);
|
|
182
150
|
```
|
|
183
151
|
|
|
184
152
|
## post hooks
|
|
@@ -186,27 +154,22 @@ hooks.execPre('cook', obj, function() {
|
|
|
186
154
|
### It runs without any hooks specified
|
|
187
155
|
|
|
188
156
|
```javascript
|
|
189
|
-
hooks.execPost('cook', null, [1]
|
|
190
|
-
|
|
191
|
-
assert.equal(1, eggs);
|
|
192
|
-
done();
|
|
193
|
-
});
|
|
157
|
+
const [eggs] = await hooks.execPost('cook', null, [1]);
|
|
158
|
+
assert.equal(eggs, 1);
|
|
194
159
|
```
|
|
195
160
|
|
|
196
161
|
### It executes with parameters passed in
|
|
197
162
|
|
|
198
163
|
```javascript
|
|
199
164
|
hooks.post('cook', function(eggs, bacon, callback) {
|
|
200
|
-
assert.equal(
|
|
201
|
-
assert.equal(
|
|
165
|
+
assert.equal(eggs, 1);
|
|
166
|
+
assert.equal(bacon, 2);
|
|
202
167
|
callback();
|
|
203
168
|
});
|
|
204
169
|
|
|
205
|
-
hooks.execPost('cook', null, [1, 2]
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
assert.equal(2, bacon);
|
|
209
|
-
});
|
|
170
|
+
const [eggs, bacon] = await hooks.execPost('cook', null, [1, 2]);
|
|
171
|
+
assert.equal(eggs, 1);
|
|
172
|
+
assert.equal(bacon, 2);
|
|
210
173
|
```
|
|
211
174
|
|
|
212
175
|
### It can use synchronous post hooks
|
|
@@ -216,25 +179,23 @@ const execed = {};
|
|
|
216
179
|
|
|
217
180
|
hooks.post('cook', function(eggs, bacon) {
|
|
218
181
|
execed.first = true;
|
|
219
|
-
assert.equal(
|
|
220
|
-
assert.equal(
|
|
182
|
+
assert.equal(eggs, 1);
|
|
183
|
+
assert.equal(bacon, 2);
|
|
221
184
|
});
|
|
222
185
|
|
|
223
186
|
hooks.post('cook', function(eggs, bacon, callback) {
|
|
224
187
|
execed.second = true;
|
|
225
|
-
assert.equal(
|
|
226
|
-
assert.equal(
|
|
188
|
+
assert.equal(eggs, 1);
|
|
189
|
+
assert.equal(bacon, 2);
|
|
227
190
|
callback();
|
|
228
191
|
});
|
|
229
192
|
|
|
230
|
-
hooks.execPost('cook', null, [1, 2]
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
assert.equal(2, bacon);
|
|
237
|
-
});
|
|
193
|
+
const [eggs, bacon] = await hooks.execPost('cook', null, [1, 2]);
|
|
194
|
+
assert.equal(Object.keys(execed).length, 2);
|
|
195
|
+
assert.ok(execed.first);
|
|
196
|
+
assert.ok(execed.second);
|
|
197
|
+
assert.equal(eggs, 1);
|
|
198
|
+
assert.equal(bacon, 2);
|
|
238
199
|
```
|
|
239
200
|
|
|
240
201
|
### It supports returning a promise
|
|
@@ -255,9 +216,32 @@ hooks.post('cook', function() {
|
|
|
255
216
|
|
|
256
217
|
const obj = { bacon: 0 };
|
|
257
218
|
|
|
258
|
-
hooks.execPost('cook', obj, obj
|
|
259
|
-
|
|
219
|
+
await hooks.execPost('cook', obj, [obj]);
|
|
220
|
+
assert.equal(obj.bacon, 3);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### It supports filtering which hooks to run
|
|
224
|
+
|
|
225
|
+
You can pass a `filter` option to `execPost()` to select which hooks
|
|
226
|
+
to run. The filter function receives each hook object and should return
|
|
227
|
+
`true` to run the hook or `false` to skip it.
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
const execed = [];
|
|
231
|
+
|
|
232
|
+
const fn1 = function() { execed.push('first'); };
|
|
233
|
+
fn1.skipMe = true;
|
|
234
|
+
hooks.post('cook', fn1);
|
|
235
|
+
|
|
236
|
+
const fn2 = function() { execed.push('second'); };
|
|
237
|
+
hooks.post('cook', fn2);
|
|
238
|
+
|
|
239
|
+
// Only runs fn2, skips fn1 because fn1.skipMe is true
|
|
240
|
+
await hooks.execPost('cook', null, [], {
|
|
241
|
+
filter: hook => !hook.fn.skipMe
|
|
260
242
|
});
|
|
243
|
+
|
|
244
|
+
assert.deepStrictEqual(execed, ['second']);
|
|
261
245
|
```
|
|
262
246
|
|
|
263
247
|
## wrap()
|
|
@@ -265,26 +249,23 @@ hooks.execPost('cook', obj, obj, function() {
|
|
|
265
249
|
### It wraps pre and post calls into one call
|
|
266
250
|
|
|
267
251
|
```javascript
|
|
268
|
-
hooks.pre('cook',
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
252
|
+
hooks.pre('cook', function() {
|
|
253
|
+
return new Promise(resolve => {
|
|
254
|
+
this.bacon = 3;
|
|
255
|
+
setTimeout(() => {
|
|
256
|
+
resolve();
|
|
257
|
+
}, 5);
|
|
258
|
+
});
|
|
274
259
|
});
|
|
275
260
|
|
|
276
|
-
hooks.pre('cook',
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
setTimeout(function() {
|
|
280
|
-
_this.eggs = 4;
|
|
281
|
-
done();
|
|
282
|
-
}, 10);
|
|
261
|
+
hooks.pre('cook', function() {
|
|
262
|
+
this.eggs = 4;
|
|
263
|
+
return Promise.resolve();
|
|
283
264
|
});
|
|
284
265
|
|
|
285
|
-
hooks.pre('cook', function(
|
|
266
|
+
hooks.pre('cook', function() {
|
|
286
267
|
this.waffles = false;
|
|
287
|
-
|
|
268
|
+
return Promise.resolve();
|
|
288
269
|
});
|
|
289
270
|
|
|
290
271
|
hooks.post('cook', function(obj) {
|
|
@@ -294,28 +275,24 @@ hooks.post('cook', function(obj) {
|
|
|
294
275
|
const obj = { bacon: 0, eggs: 0 };
|
|
295
276
|
|
|
296
277
|
const args = [obj];
|
|
297
|
-
args.push(function(error, result) {
|
|
298
|
-
assert.ifError(error);
|
|
299
|
-
assert.equal(null, error);
|
|
300
|
-
assert.equal(3, obj.bacon);
|
|
301
|
-
assert.equal(4, obj.eggs);
|
|
302
|
-
assert.equal(false, obj.waffles);
|
|
303
|
-
assert.equal('no', obj.tofu);
|
|
304
|
-
|
|
305
|
-
assert.equal(obj, result);
|
|
306
|
-
});
|
|
307
278
|
|
|
308
|
-
hooks.wrap(
|
|
279
|
+
const result = await hooks.wrap(
|
|
309
280
|
'cook',
|
|
310
|
-
function(o
|
|
311
|
-
assert.equal(
|
|
312
|
-
assert.equal(
|
|
313
|
-
assert.equal(
|
|
314
|
-
assert.equal(
|
|
315
|
-
|
|
281
|
+
function(o) {
|
|
282
|
+
assert.equal(obj.bacon, 3);
|
|
283
|
+
assert.equal(obj.eggs, 4);
|
|
284
|
+
assert.equal(obj.waffles, false);
|
|
285
|
+
assert.equal(obj.tofu, undefined);
|
|
286
|
+
return o;
|
|
316
287
|
},
|
|
317
288
|
obj,
|
|
318
289
|
args);
|
|
290
|
+
|
|
291
|
+
assert.equal(obj.bacon, 3);
|
|
292
|
+
assert.equal(obj.eggs, 4);
|
|
293
|
+
assert.equal(obj.waffles, false);
|
|
294
|
+
assert.equal(obj.tofu, 'no');
|
|
295
|
+
assert.equal(result, obj);
|
|
319
296
|
```
|
|
320
297
|
|
|
321
298
|
## createWrapper()
|
|
@@ -323,26 +300,23 @@ hooks.wrap(
|
|
|
323
300
|
### It wraps wrap() into a callable function
|
|
324
301
|
|
|
325
302
|
```javascript
|
|
326
|
-
hooks.pre('cook',
|
|
303
|
+
hooks.pre('cook', function() {
|
|
327
304
|
this.bacon = 3;
|
|
328
|
-
|
|
329
|
-
setTimeout(function() {
|
|
330
|
-
done();
|
|
331
|
-
}, 5);
|
|
305
|
+
return Promise.resolve();
|
|
332
306
|
});
|
|
333
307
|
|
|
334
|
-
hooks.pre('cook',
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
308
|
+
hooks.pre('cook', function() {
|
|
309
|
+
return new Promise(resolve => {
|
|
310
|
+
this.eggs = 4;
|
|
311
|
+
setTimeout(function() {
|
|
312
|
+
resolve();
|
|
313
|
+
}, 10);
|
|
314
|
+
});
|
|
341
315
|
});
|
|
342
316
|
|
|
343
|
-
hooks.pre('cook', function(
|
|
317
|
+
hooks.pre('cook', function() {
|
|
344
318
|
this.waffles = false;
|
|
345
|
-
|
|
319
|
+
return Promise.resolve();
|
|
346
320
|
});
|
|
347
321
|
|
|
348
322
|
hooks.post('cook', function(obj) {
|
|
@@ -353,24 +327,22 @@ const obj = { bacon: 0, eggs: 0 };
|
|
|
353
327
|
|
|
354
328
|
const cook = hooks.createWrapper(
|
|
355
329
|
'cook',
|
|
356
|
-
function(o
|
|
330
|
+
function(o) {
|
|
357
331
|
assert.equal(3, obj.bacon);
|
|
358
332
|
assert.equal(4, obj.eggs);
|
|
359
333
|
assert.equal(false, obj.waffles);
|
|
360
334
|
assert.equal(undefined, obj.tofu);
|
|
361
|
-
|
|
335
|
+
return o;
|
|
362
336
|
},
|
|
363
337
|
obj);
|
|
364
338
|
|
|
365
|
-
cook(obj
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
assert.equal('no', obj.tofu);
|
|
339
|
+
const result = await cook(obj);
|
|
340
|
+
assert.equal(obj.bacon, 3);
|
|
341
|
+
assert.equal(obj.eggs, 4);
|
|
342
|
+
assert.equal(obj.waffles, false);
|
|
343
|
+
assert.equal(obj.tofu, 'no');
|
|
371
344
|
|
|
372
|
-
|
|
373
|
-
});
|
|
345
|
+
assert.equal(result, obj);
|
|
374
346
|
```
|
|
375
347
|
|
|
376
348
|
## clone()
|
package/index.js
CHANGED
|
@@ -37,10 +37,15 @@ Kareem.overwriteArguments = function overwriteArguments() {
|
|
|
37
37
|
* @param {String} name The hook name to execute
|
|
38
38
|
* @param {*} context Overwrite the "this" for the hook
|
|
39
39
|
* @param {Array} args arguments passed to the pre hooks
|
|
40
|
+
* @param {Object} [options] Optional options
|
|
41
|
+
* @param {Function} [options.filter] Filter function to select which hooks to run
|
|
40
42
|
* @returns {Array} The potentially modified arguments
|
|
41
43
|
*/
|
|
42
|
-
Kareem.prototype.execPre = async function execPre(name, context, args) {
|
|
43
|
-
|
|
44
|
+
Kareem.prototype.execPre = async function execPre(name, context, args, options) {
|
|
45
|
+
let pres = this._pres.get(name) || [];
|
|
46
|
+
if (options?.filter) {
|
|
47
|
+
pres = pres.filter(options.filter);
|
|
48
|
+
}
|
|
44
49
|
const numPres = pres.length;
|
|
45
50
|
let $args = args;
|
|
46
51
|
let skipWrappedFunction = null;
|
|
@@ -116,11 +121,16 @@ Kareem.prototype.execPreSync = function(name, context, args) {
|
|
|
116
121
|
* @param {String} name The hook name to execute
|
|
117
122
|
* @param {*} context Overwrite the "this" for the hook
|
|
118
123
|
* @param {Array} args Apply custom arguments to the hook
|
|
119
|
-
* @param {
|
|
124
|
+
* @param {Object} [options] Optional options
|
|
125
|
+
* @param {Error} [options.error] Error to pass to error-handling middleware
|
|
126
|
+
* @param {Function} [options.filter] Filter function to select which hooks to run
|
|
120
127
|
* @returns {void}
|
|
121
128
|
*/
|
|
122
129
|
Kareem.prototype.execPost = async function execPost(name, context, args, options) {
|
|
123
|
-
|
|
130
|
+
let posts = this._posts.get(name) || [];
|
|
131
|
+
if (options?.filter) {
|
|
132
|
+
posts = posts.filter(options.filter);
|
|
133
|
+
}
|
|
124
134
|
const numPosts = posts.length;
|
|
125
135
|
|
|
126
136
|
let firstError = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kareem",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Next-generation take on pre/post function hooks",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"acquit": "1.x",
|
|
18
18
|
"acquit-ignore": "0.2.x",
|
|
19
19
|
"eslint": "8.20.0",
|
|
20
|
-
"mocha": "
|
|
20
|
+
"mocha": "11.x",
|
|
21
21
|
"nyc": "15.1.0"
|
|
22
22
|
},
|
|
23
23
|
"author": "Valeri Karpov <val@karpov.io>",
|