kareem 2.3.2 → 2.3.5

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ <a name="2.3.4"></a>
4
+ ## 2.3.4 (2022-02-10)
5
+
6
+ * perf: various performance improvements #27 #24 #23 #22 #21 #20
7
+
8
+ <a name="2.3.3"></a>
9
+ ## 2.3.3 (2021-12-26)
10
+
11
+ * fix: handle sync errors in `wrap()`
12
+
3
13
  <a name="2.3.2"></a>
4
14
  ## 2.3.2 (2020-12-08)
5
15
 
package/index.js CHANGED
@@ -10,7 +10,7 @@ Kareem.prototype.execPre = function(name, context, args, callback) {
10
10
  callback = args;
11
11
  args = [];
12
12
  }
13
- var pres = get(this._pres, name, []);
13
+ var pres = this._pres.get(name) || [];
14
14
  var numPres = pres.length;
15
15
  var numAsyncPres = pres.numAsync || 0;
16
16
  var currentPre = 0;
@@ -19,7 +19,7 @@ Kareem.prototype.execPre = function(name, context, args, callback) {
19
19
  var $args = args;
20
20
 
21
21
  if (!numPres) {
22
- return process.nextTick(function() {
22
+ return nextTick(function() {
23
23
  callback(null);
24
24
  });
25
25
  }
@@ -57,24 +57,24 @@ Kareem.prototype.execPre = function(name, context, args, callback) {
57
57
 
58
58
  callMiddlewareFunction(pre.fn, context, args, args[0]);
59
59
  } else {
60
- let maybePromise = null;
60
+ let maybePromiseLike = null;
61
61
  try {
62
- maybePromise = pre.fn.call(context);
62
+ maybePromiseLike = pre.fn.call(context);
63
63
  } catch (err) {
64
64
  if (err != null) {
65
65
  return callback(err);
66
66
  }
67
67
  }
68
68
 
69
- if (isPromise(maybePromise)) {
70
- maybePromise.then(() => _next(), err => _next(err));
69
+ if (isPromiseLike(maybePromiseLike)) {
70
+ maybePromiseLike.then(() => _next(), err => _next(err));
71
71
  } else {
72
72
  if (++currentPre >= numPres) {
73
73
  if (asyncPresLeft > 0) {
74
74
  // Leave parallel hooks to run
75
75
  return;
76
76
  } else {
77
- return process.nextTick(function() {
77
+ return nextTick(function() {
78
78
  callback(null);
79
79
  });
80
80
  }
@@ -109,7 +109,7 @@ Kareem.prototype.execPre = function(name, context, args, callback) {
109
109
  };
110
110
 
111
111
  Kareem.prototype.execPreSync = function(name, context, args) {
112
- var pres = get(this._pres, name, []);
112
+ var pres = this._pres.get(name) || [];
113
113
  var numPres = pres.length;
114
114
 
115
115
  for (var i = 0; i < numPres; ++i) {
@@ -122,7 +122,7 @@ Kareem.prototype.execPost = function(name, context, args, options, callback) {
122
122
  callback = options;
123
123
  options = null;
124
124
  }
125
- var posts = get(this._posts, name, []);
125
+ var posts = this._posts.get(name) || [];
126
126
  var numPosts = posts.length;
127
127
  var currentPost = 0;
128
128
 
@@ -132,7 +132,7 @@ Kareem.prototype.execPost = function(name, context, args, options, callback) {
132
132
  }
133
133
 
134
134
  if (!numPosts) {
135
- return process.nextTick(function() {
135
+ return nextTick(function() {
136
136
  callback.apply(null, [firstError].concat(args));
137
137
  });
138
138
  }
@@ -151,7 +151,7 @@ Kareem.prototype.execPost = function(name, context, args, options, callback) {
151
151
 
152
152
  if (firstError) {
153
153
  if (post.length === numArgs + 2) {
154
- var _cb = decorateNextFn(function(error) {
154
+ const _cb = decorateNextFn(function(error) {
155
155
  if (error) {
156
156
  firstError = error;
157
157
  }
@@ -194,23 +194,23 @@ Kareem.prototype.execPost = function(name, context, args, options, callback) {
194
194
  callMiddlewareFunction(post, context, newArgs.concat([_cb]), _cb);
195
195
  } else {
196
196
  let error;
197
- let maybePromise;
197
+ let maybePromiseLike;
198
198
  try {
199
- maybePromise = post.apply(context, newArgs);
199
+ maybePromiseLike = post.apply(context, newArgs);
200
200
  } catch (err) {
201
201
  error = err;
202
202
  firstError = err;
203
203
  }
204
204
 
205
- if (isPromise(maybePromise)) {
206
- return maybePromise.then(() => _cb(), err => _cb(err));
205
+ if (isPromiseLike(maybePromiseLike)) {
206
+ return maybePromiseLike.then(() => _cb(), err => _cb(err));
207
207
  }
208
208
 
209
209
  if (++currentPost >= numPosts) {
210
210
  return callback.apply(null, [error].concat(args));
211
211
  }
212
212
 
213
- next(error);
213
+ next();
214
214
  }
215
215
  }
216
216
  };
@@ -219,7 +219,7 @@ Kareem.prototype.execPost = function(name, context, args, options, callback) {
219
219
  };
220
220
 
221
221
  Kareem.prototype.execPostSync = function(name, context, args) {
222
- const posts = get(this._posts, name, []);
222
+ const posts = this._posts.get(name) || [];
223
223
  const numPosts = posts.length;
224
224
 
225
225
  for (let i = 0; i < numPosts; ++i) {
@@ -242,22 +242,18 @@ Kareem.prototype.createWrapperSync = function(name, fn) {
242
242
 
243
243
  function _handleWrapError(instance, error, name, context, args, options, callback) {
244
244
  if (options.useErrorHandlers) {
245
- var _options = { error: error };
246
- return instance.execPost(name, context, args, _options, function(error) {
245
+ return instance.execPost(name, context, args, { error: error }, function(error) {
247
246
  return typeof callback === 'function' && callback(error);
248
247
  });
249
248
  } else {
250
- return typeof callback === 'function' ?
251
- callback(error) :
252
- undefined;
249
+ return typeof callback === 'function' && callback(error);
253
250
  }
254
251
  }
255
252
 
256
253
  Kareem.prototype.wrap = function(name, fn, context, args, options) {
257
254
  const lastArg = (args.length > 0 ? args[args.length - 1] : null);
258
- const argsWithoutCb = typeof lastArg === 'function' ?
259
- args.slice(0, args.length - 1) :
260
- args;
255
+ const argsWithoutCb = Array.from(args);
256
+ typeof lastArg === 'function' && argsWithoutCb.pop();
261
257
  const _this = this;
262
258
 
263
259
  options = options || {};
@@ -274,12 +270,16 @@ Kareem.prototype.wrap = function(name, fn, context, args, options) {
274
270
  options, lastArg);
275
271
  }
276
272
 
277
- const end = (typeof lastArg === 'function' ? args.length - 1 : args.length);
278
273
  const numParameters = fn.length;
279
- const ret = fn.apply(context, args.slice(0, end).concat(_cb));
274
+ let ret;
275
+ try {
276
+ ret = fn.apply(context, argsWithoutCb.concat(_cb));
277
+ } catch (err) {
278
+ return _cb(err);
279
+ }
280
280
 
281
281
  if (checkForPromise) {
282
- if (ret != null && typeof ret.then === 'function') {
282
+ if (isPromiseLike(ret)) {
283
283
  // Thenable, use it
284
284
  return ret.then(
285
285
  res => _cb(null, res),
@@ -289,14 +289,14 @@ Kareem.prototype.wrap = function(name, fn, context, args, options) {
289
289
 
290
290
  // If `fn()` doesn't have a callback argument and doesn't return a
291
291
  // promise, assume it is sync
292
- if (numParameters < end + 1) {
292
+ if (numParameters < argsWithoutCb.length + 1) {
293
293
  return _cb(null, ret);
294
294
  }
295
295
  }
296
296
 
297
297
  function _cb() {
298
- const args = arguments;
299
- const argsWithoutError = Array.prototype.slice.call(arguments, 1);
298
+ const argsWithoutError = Array.from(arguments);
299
+ argsWithoutError.shift();
300
300
  if (options.nullResultByDefault && argsWithoutError.length === 0) {
301
301
  argsWithoutError.push(null);
302
302
  }
@@ -306,15 +306,12 @@ Kareem.prototype.wrap = function(name, fn, context, args, options) {
306
306
  argsWithoutError, options, lastArg);
307
307
  } else {
308
308
  _this.execPost(name, context, argsWithoutError, function() {
309
- if (arguments[0]) {
310
- return typeof lastArg === 'function' ?
311
- lastArg(arguments[0]) :
312
- undefined;
309
+ if (lastArg === null) {
310
+ return;
313
311
  }
314
-
315
- return typeof lastArg === 'function' ?
316
- lastArg.apply(context, arguments) :
317
- undefined;
312
+ arguments[0]
313
+ ? lastArg(arguments[0])
314
+ : lastArg.apply(context, arguments)
318
315
  });
319
316
  }
320
317
  }
@@ -367,28 +364,26 @@ Kareem.prototype.createWrapper = function(name, fn, context, options) {
367
364
  // Fast path: if there's no hooks for this function, just return the
368
365
  // function wrapped in a nextTick()
369
366
  return function() {
370
- process.nextTick(() => fn.apply(this, arguments));
367
+ nextTick(() => fn.apply(this, arguments));
371
368
  };
372
369
  }
373
370
  return function() {
374
371
  var _context = context || this;
375
- var args = Array.prototype.slice.call(arguments);
376
- _this.wrap(name, fn, _context, args, options);
372
+ _this.wrap(name, fn, _context, Array.from(arguments), options);
377
373
  };
378
374
  };
379
375
 
380
376
  Kareem.prototype.pre = function(name, isAsync, fn, error, unshift) {
381
377
  let options = {};
382
- if (typeof isAsync === 'object' && isAsync != null) {
378
+ if (typeof isAsync === 'object' && isAsync !== null) {
383
379
  options = isAsync;
384
380
  isAsync = options.isAsync;
385
381
  } else if (typeof arguments[1] !== 'boolean') {
386
- error = fn;
387
382
  fn = isAsync;
388
383
  isAsync = false;
389
384
  }
390
385
 
391
- const pres = get(this._pres, name, []);
386
+ const pres = this._pres.get(name) || [];
392
387
  this._pres.set(name, pres);
393
388
 
394
389
  if (isAsync) {
@@ -410,7 +405,7 @@ Kareem.prototype.pre = function(name, isAsync, fn, error, unshift) {
410
405
  };
411
406
 
412
407
  Kareem.prototype.post = function(name, options, fn, unshift) {
413
- const hooks = get(this._posts, name, []);
408
+ const hooks = this._posts.get(name) || [];
414
409
 
415
410
  if (typeof options === 'function') {
416
411
  unshift = !!fn;
@@ -451,7 +446,7 @@ Kareem.prototype.merge = function(other, clone) {
451
446
  var ret = clone ? this.clone() : this;
452
447
 
453
448
  for (let key of other._pres.keys()) {
454
- const sourcePres = get(ret._pres, key, []);
449
+ const sourcePres = ret._pres.get(key) || [];
455
450
  const deduplicated = other._pres.get(key).
456
451
  // Deduplicate based on `fn`
457
452
  filter(p => sourcePres.map(_p => _p.fn).indexOf(p.fn) === -1);
@@ -461,7 +456,7 @@ Kareem.prototype.merge = function(other, clone) {
461
456
  ret._pres.set(key, combined);
462
457
  }
463
458
  for (let key of other._posts.keys()) {
464
- const sourcePosts = get(ret._posts, key, []);
459
+ const sourcePosts = ret._posts.get(key) || [];
465
460
  const deduplicated = other._posts.get(key).
466
461
  filter(p => sourcePosts.indexOf(p) === -1);
467
462
  ret._posts.set(key, sourcePosts.concat(deduplicated));
@@ -470,28 +465,21 @@ Kareem.prototype.merge = function(other, clone) {
470
465
  return ret;
471
466
  };
472
467
 
473
- function get(map, key, def) {
474
- if (map.has(key)) {
475
- return map.get(key);
476
- }
477
- return def;
478
- }
479
-
480
468
  function callMiddlewareFunction(fn, context, args, next) {
481
- let maybePromise;
469
+ let maybePromiseLike;
482
470
  try {
483
- maybePromise = fn.apply(context, args);
471
+ maybePromiseLike = fn.apply(context, args);
484
472
  } catch (error) {
485
473
  return next(error);
486
474
  }
487
475
 
488
- if (isPromise(maybePromise)) {
489
- maybePromise.then(() => next(), err => next(err));
476
+ if (isPromiseLike(maybePromiseLike)) {
477
+ maybePromiseLike.then(() => next(), err => next(err));
490
478
  }
491
479
  }
492
480
 
493
- function isPromise(v) {
494
- return v != null && typeof v.then === 'function';
481
+ function isPromiseLike(v) {
482
+ return (typeof v === 'object' && v !== null && typeof v.then === 'function');
495
483
  }
496
484
 
497
485
  function decorateNextFn(fn) {
@@ -505,8 +493,12 @@ function decorateNextFn(fn) {
505
493
  called = true;
506
494
  // Make sure to clear the stack so try/catch doesn't catch errors
507
495
  // in subsequent middleware
508
- return process.nextTick(() => fn.apply(_this, arguments));
496
+ return nextTick(() => fn.apply(_this, arguments));
509
497
  };
510
498
  }
511
499
 
500
+ const nextTick = typeof process === 'object' && process !== null && process.nextTick || function nextTick(cb) {
501
+ setTimeout(cb, 0);
502
+ }
503
+
512
504
  module.exports = Kareem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kareem",
3
- "version": "2.3.2",
3
+ "version": "2.3.5",
4
4
  "description": "Next-generation take on pre/post function hooks",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,8 +14,8 @@
14
14
  "devDependencies": {
15
15
  "acquit": "1.x",
16
16
  "acquit-ignore": "0.1.x",
17
- "nyc": "11.x",
18
- "mocha": "5.x"
17
+ "mocha": "9.2.0",
18
+ "nyc": "15.1.0"
19
19
  },
20
20
  "author": "Valeri Karpov <val@karpov.io>",
21
21
  "license": "Apache-2.0"
package/test/wrap.test.js CHANGED
@@ -319,6 +319,30 @@ describe('wrap()', function() {
319
319
  25);
320
320
  });
321
321
 
322
+ it('catches sync errors', function(done) {
323
+ hooks.pre('cook', function(done) {
324
+ done();
325
+ });
326
+
327
+ hooks.post('cook', function(callback) {
328
+ callback();
329
+ });
330
+
331
+ var args = [];
332
+ args.push(function(error) {
333
+ assert.equal(error.message, 'oops!');
334
+ done();
335
+ });
336
+
337
+ hooks.wrap(
338
+ 'cook',
339
+ function() {
340
+ throw new Error('oops!');
341
+ },
342
+ null,
343
+ args);
344
+ });
345
+
322
346
  it('sync wrappers', function() {
323
347
  var calledPre = 0;
324
348
  var calledFn = 0;