newrelic 4.8.1 → 4.9.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.
@@ -0,0 +1,563 @@
1
+ 'use strict'
2
+
3
+ const logger = require('../logger').child({component: 'PromiseShim'})
4
+ const Shim = require('./shim')
5
+
6
+ /**
7
+ * A helper class for wrapping promise modules.
8
+ *
9
+ * @extends Shim
10
+ */
11
+ class PromiseShim extends Shim {
12
+ /**
13
+ * Constructs a shim associated with the given agent instance, specialized for
14
+ * instrumenting promise libraries.
15
+ *
16
+ * @param {Agent} agent
17
+ * The agent this shim will use.
18
+ *
19
+ * @param {string} moduleName
20
+ * The name of the module being instrumented.
21
+ *
22
+ * @param {string} resolvedName
23
+ * The full path to the loaded module.
24
+ *
25
+ * @see Shim
26
+ */
27
+ constructor(agent, moduleName, resolvedName) {
28
+ super(agent, moduleName, resolvedName)
29
+ this._logger = logger.child({module: moduleName})
30
+ this._class = null
31
+ }
32
+
33
+ /**
34
+ * Grants access to the `Contextualizer` class used by the `PromiseShim` to
35
+ * propagate context down promise chains.
36
+ *
37
+ * @private
38
+ */
39
+ static get Contextualizer() {
40
+ return Contextualizer
41
+ }
42
+
43
+ /**
44
+ * Sets the class used to indentify promises from the wrapped promise library.
45
+ *
46
+ * @param {function} clss - The promise library's class.
47
+ */
48
+ setClass(clss) {
49
+ this._class = clss
50
+ }
51
+
52
+ /**
53
+ * Checks if the given object is an instance of a promise from the promise
54
+ * library being wrapped.
55
+ *
56
+ * @param {*} obj - The object to check the instance type of.
57
+ *
58
+ * @return {bool} True if the provided object is an instance of a promise from
59
+ * this promise library.
60
+ *
61
+ * @see PromiseShim#setClass
62
+ */
63
+ isPromiseInstance(obj) {
64
+ return !!this._class && obj instanceof this._class
65
+ }
66
+
67
+ /**
68
+ * Wraps the given properties as constructors for the promise library.
69
+ *
70
+ * - `wrapConstructor(nodule, properties)`
71
+ * - `wrapConstructor(func)`
72
+ *
73
+ * It is only necessary to wrap the constructor for the class if there is no
74
+ * other way to access the executor function. Some libraries expose a separate
75
+ * method which is called to execute the executor. If that is available, it is
76
+ * better to wrap that using {@link PromiseShim#wrapExecutorCaller} than to
77
+ * use this method.
78
+ *
79
+ * @param {object|function} nodule
80
+ * The source of the properties to wrap, or a single function to wrap.
81
+ *
82
+ * @param {string|array.<string>} [properties]
83
+ * One or more properties to wrap. If omitted, the `nodule` parameter is
84
+ * assumed to be the constructor to wrap.
85
+ *
86
+ * @return {object|function} The first parameter to this function, after
87
+ * wrapping it or its properties.
88
+ *
89
+ * @see PromiseShim#wrapExecutorCaller
90
+ */
91
+ wrapConstructor(nodule, properties) {
92
+ return this.wrapClass(nodule, properties, {
93
+ pre: function prePromise(shim, Promise, name, args) {
94
+ // We are expecting one function argument for executor, anything else is
95
+ // non-standard, do not attempt to wrap. Also do not attempt to wrap if
96
+ // we are not in a transaction.
97
+ if (args.length !== 1 || !shim.isFunction(args[0]) || !shim.getActiveSegment()) {
98
+ return
99
+ }
100
+ _wrapExecutorContext(shim, args)
101
+ },
102
+ post: function postPromise(shim, Promise, name, args) {
103
+ // This extra property is added by `_wrapExecutorContext` in the pre step.
104
+ const executor = args[0]
105
+ const context = executor && executor.__NR_executorContext
106
+ if (!context || !shim.isFunction(context.executor)) {
107
+ return
108
+ }
109
+
110
+ context.promise = this
111
+ Contextualizer.link(null, this, shim.getSegment())
112
+ try {
113
+ // Must run after promise is defined so that `__NR_wrapper` can be set.
114
+ context.executor.apply(context.self, context.args)
115
+ } catch (e) {
116
+ const reject = context.args[1]
117
+ reject(e)
118
+ }
119
+ }
120
+ })
121
+ }
122
+
123
+ /**
124
+ * Wraps the given properties as the caller of promise executors.
125
+ *
126
+ * - `wrapExecutorCaller(nodule, properties)`
127
+ * - `wrapExecutorCaller(func)`
128
+ *
129
+ * Wrapping the executor caller method directly is preferable to wrapping
130
+ * the constructor of the promise class.
131
+ *
132
+ * @param {object|function} nodule
133
+ * The source of the properties to wrap, or a single function to wrap.
134
+ *
135
+ * @param {string|array.<string>} [properties]
136
+ * One or more properties to wrap. If omitted, the `nodule` parameter is
137
+ * assumed to be the function to wrap.
138
+ *
139
+ * @return {object|function} The first parameter to this function, after
140
+ * wrapping it or its properties.
141
+ *
142
+ * @see PromiseShim#wrapConstructor
143
+ */
144
+ wrapExecutorCaller(nodule, properties) {
145
+ return this.wrap(nodule, properties, function executorWrapper(shim, caller) {
146
+ if (!shim.isFunction(caller) || shim.isWrapped(caller)) {
147
+ return
148
+ }
149
+
150
+ return function wrappedExecutorCaller(executor) {
151
+ var parent = shim.getActiveSegment()
152
+ if (!this || !parent) {
153
+ return caller.apply(this, arguments)
154
+ }
155
+
156
+ if (!this.__NR_context) {
157
+ Contextualizer.link(null, this, parent)
158
+ }
159
+
160
+ const args = shim.argsToArray.apply(shim, arguments)
161
+ _wrapExecutorContext(shim, args)
162
+ const ret = caller.apply(this, args)
163
+ const context = args[0].__NR_executorContext
164
+ context.promise = this
165
+
166
+ // Bluebird catches executor errors and auto-rejects when it catches them,
167
+ // thus we need to do so as well.
168
+ //
169
+ // When adding new libraries, make sure to check that they behave the same
170
+ // way. We may need to enhance the promise spec to handle this variance.
171
+ try {
172
+ executor.apply(context.self, context.args)
173
+ } catch (e) {
174
+ const reject = context.args[1]
175
+ reject(e)
176
+ }
177
+ return ret
178
+ }
179
+ })
180
+ }
181
+
182
+ /**
183
+ * Wraps the given properties as methods which take is some value other than
184
+ * a function to call and return a promise.
185
+ *
186
+ * - `wrapCast(nodule, properties)`
187
+ * - `wrapCast(func)`
188
+ *
189
+ * Examples of promise cast methods include `Promise.resolve`, `Promise.all`,
190
+ * and Bluebird's `Promise.delay`. These are static methods which accept some
191
+ * arbitrary value and return a Promise instance.
192
+ *
193
+ * @param {object|function} nodule
194
+ * The source of the properties to wrap, or a single function to wrap.
195
+ *
196
+ * @param {string|array.<string>} [properties]
197
+ * One or more properties to wrap. If omitted, the `nodule` parameter is
198
+ * assumed to be the function to wrap.
199
+ *
200
+ * @return {object|function} The first parameter to this function, after
201
+ * wrapping it or its properties.
202
+ */
203
+ wrapCast(nodule, properties) {
204
+ return this.wrap(nodule, properties, function castWrapper(shim, cast) {
205
+ if (!shim.isFunction(cast) || shim.isWrapped(cast)) {
206
+ return
207
+ }
208
+
209
+ return function __NR_wrappedCast() {
210
+ const segment = shim.getSegment()
211
+ const prom = cast.apply(this, arguments)
212
+ if (segment) {
213
+ Contextualizer.link(null, prom, segment)
214
+ }
215
+ return prom
216
+ }
217
+ })
218
+ }
219
+
220
+ /**
221
+ * Wraps the given properties as promise chaining methods.
222
+ *
223
+ * - `wrapThen(nodule, properties)`
224
+ * - `wrapThen(func)`
225
+ *
226
+ * NOTE: You must set class used by the library before wrapping then-methods.
227
+ *
228
+ * Examples of promise then methods include `Promise#then`, `Promise#finally`,
229
+ * and Bluebird's `Promise#map`. These are methods which take a function to
230
+ * execute once the promise resolves and hands back a new promise.
231
+ *
232
+ * @param {object|function} nodule
233
+ * The source of the properties to wrap, or a single function to wrap.
234
+ *
235
+ * @param {string|array.<string>} [properties]
236
+ * One or more properties to wrap. If omitted, the `nodule` parameter is
237
+ * assumed to be the function to wrap.
238
+ *
239
+ * @return {object|function} The first parameter to this function, after
240
+ * wrapping it or its properties.
241
+ *
242
+ * @see PromiseShim#setClass
243
+ * @see PromiseShim#wrapCatch
244
+ */
245
+ wrapThen(nodule, properties) {
246
+ return this.wrap(nodule, properties, _wrapThen, [true])
247
+ }
248
+
249
+ /**
250
+ * Wraps the given properties as rejected promise chaining methods.
251
+ *
252
+ * - `wrapCatch(nodule, properties)`
253
+ * - `wrapCatch(func)`
254
+ *
255
+ * NOTE: You must set class used by the library before wrapping catch-methods.
256
+ *
257
+ * Promise catch methods differ from then methods in that only one function
258
+ * will be executed and only if the promise is rejected. Some libraries accept
259
+ * an additional argument to `Promise#catch` which is usually an error class
260
+ * to filter rejections by. This wrap method will handle that case.
261
+ *
262
+ * @param {object|function} nodule
263
+ * The source of the properties to wrap, or a single function to wrap.
264
+ *
265
+ * @param {string|array.<string>} [properties]
266
+ * One or more properties to wrap. If omitted, the `nodule` parameter is
267
+ * assumed to be the function to wrap.
268
+ *
269
+ * @return {object|function} The first parameter to this function, after
270
+ * wrapping it or its properties.
271
+ *
272
+ * @see PromiseShim#setClass
273
+ * @see PromiseShim#wrapThen
274
+ */
275
+ wrapCatch(nodule, properties) {
276
+ return this.wrap(nodule, properties, _wrapThen, [false])
277
+ }
278
+
279
+ /**
280
+ * Wraps the given properties as callback-to-promise conversion methods.
281
+ *
282
+ * - `wrapPromisify(nodule, properties)`
283
+ * - `wrapPromisify(func)`
284
+ *
285
+ * @param {object|function} nodule
286
+ * The source of the properties to wrap, or a single function to wrap.
287
+ *
288
+ * @param {string|array.<string>} [properties]
289
+ * One or more properties to wrap. If omitted, the `nodule` parameter is
290
+ * assumed to be the function to wrap.
291
+ *
292
+ * @return {object|function} The first parameter to this function, after
293
+ * wrapping it or its properties.
294
+ */
295
+ wrapPromisify(nodule, properties) {
296
+ return this.wrap(nodule, properties, function promisifyWrapper(shim, promisify) {
297
+ if (!shim.isFunction(promisify) || shim.isWrapped(promisify)) {
298
+ return
299
+ }
300
+
301
+ return function __NR_wrappedPromisify() {
302
+ const promisified = promisify.apply(this, arguments)
303
+ if (typeof promisified !== 'function') {
304
+ return promisified
305
+ }
306
+
307
+ Object.keys(promisified).forEach(function forEachProperty(prop) {
308
+ __NR_wrappedPromisified[prop] = promisified[prop]
309
+ })
310
+
311
+ return __NR_wrappedPromisified
312
+ function __NR_wrappedPromisified() {
313
+ const segment = shim.getActiveSegment()
314
+ if (!segment) {
315
+ return promisified.apply(this, arguments)
316
+ }
317
+
318
+ const prom = shim.applySegment(promisified, segment, true, this, arguments)
319
+ Contextualizer.link(null, prom, segment)
320
+ return prom
321
+ }
322
+ }
323
+ })
324
+ }
325
+ }
326
+ module.exports = PromiseShim
327
+
328
+ // -------------------------------------------------------------------------- //
329
+
330
+ /**
331
+ * @private
332
+ */
333
+ function _wrapExecutorContext(shim, args) {
334
+ const context = {
335
+ executor: args[0],
336
+ promise: null,
337
+ self: null,
338
+ args: null
339
+ }
340
+ contextExporter.__NR_executorContext = context
341
+ args[0] = contextExporter
342
+
343
+ function contextExporter(resolve, reject) {
344
+ context.self = this
345
+ context.args = shim.argsToArray.apply(shim, arguments)
346
+ context.args[0] = _wrapResolver(context, resolve)
347
+ context.args[1] = _wrapResolver(context, reject)
348
+ }
349
+ }
350
+
351
+ /**
352
+ * @private
353
+ */
354
+ function _wrapResolver(context, fn) {
355
+ return function wrappedResolveReject(val) {
356
+ const promise = context.promise
357
+ if (promise && promise.__NR_context) {
358
+ promise.__NR_context.getSegment().touch()
359
+ }
360
+ fn(val)
361
+ }
362
+ }
363
+
364
+ /**
365
+ * @private
366
+ */
367
+ function _wrapThen(shim, fn, name, useAllParams) {
368
+ // Don't wrap non-functions.
369
+ if (shim.isWrapped(fn) || !shim.isFunction(fn)) {
370
+ return
371
+ }
372
+
373
+ return function __NR_wrappedThen() {
374
+ if (!(this instanceof shim._class)) {
375
+ return fn.apply(this, arguments)
376
+ }
377
+
378
+ const thenSegment = shim.getSegment()
379
+ const promise = this
380
+
381
+ // Wrap up the arguments and execute the real then.
382
+ let isWrapped = false
383
+ const args = new Array(arguments.length)
384
+ for (let i = 0; i < arguments.length; ++i) {
385
+ args[i] = wrapHandler(arguments[i], i, arguments.length)
386
+ }
387
+ const next = fn.apply(this, args)
388
+
389
+ // If we got a promise (which we should have), link the parent's context.
390
+ if (!isWrapped && next instanceof shim._class && next !== promise) {
391
+ Contextualizer.link(promise, next, thenSegment)
392
+ }
393
+ return next
394
+
395
+ function wrapHandler(handler, i, length) {
396
+ if (
397
+ !shim.isFunction(handler) || // Not a function
398
+ shim.isWrapped(handler) || // Already wrapped
399
+ (!useAllParams && i !== (length - 1)) // Don't want all and not last
400
+ ) {
401
+ isWrapped = shim.isWrapped(handler)
402
+ return handler
403
+ }
404
+
405
+ return function __NR_wrappedThenHandler() {
406
+ if (!next || !next.__NR_context) {
407
+ return handler.apply(this, arguments)
408
+ }
409
+
410
+ let promSegment = next.__NR_context.getSegment()
411
+ const segment = promSegment || shim.getSegment()
412
+ if (segment && segment !== promSegment) {
413
+ next.__NR_context.setSegment(segment)
414
+ promSegment = segment
415
+ }
416
+
417
+ let ret = null
418
+ try {
419
+ ret = shim.applySegment(handler, promSegment, true, this, arguments)
420
+ } finally {
421
+ if (ret && typeof ret.then === 'function') {
422
+ ret = next.__NR_context.continueContext(ret)
423
+ }
424
+ }
425
+ return ret
426
+ }
427
+ }
428
+ }
429
+ }
430
+
431
+ /**
432
+ * @private
433
+ */
434
+ class Context {
435
+ constructor(segment) {
436
+ this.segments = [segment]
437
+ }
438
+
439
+ branch() {
440
+ return this.segments.push(null) - 1
441
+ }
442
+ }
443
+
444
+ /**
445
+ * @private
446
+ */
447
+ class Contextualizer {
448
+ constructor(idx, context) {
449
+ this.parentIdx = -1
450
+ this.idx = idx
451
+ this.context = context
452
+ this.child = null
453
+ }
454
+
455
+ static link(prev, next, segment) {
456
+ let ctxlzr = prev && prev.__NR_context
457
+ if (ctxlzr && !ctxlzr.isActive()) {
458
+ ctxlzr = prev.__NR_context = null
459
+ }
460
+
461
+ if (ctxlzr) {
462
+ // If prev has one child already, branch the context and update the child.
463
+ if (ctxlzr.child) {
464
+ // When the branch-point is the 2nd through nth link in the chain, it is
465
+ // necessary to track its segment separately so the branches can parent
466
+ // their segments on the branch-point.
467
+ if (ctxlzr.parentIdx !== -1) {
468
+ ctxlzr.idx = ctxlzr.context.branch()
469
+ }
470
+
471
+ // The first child needs to be updated to have its own branch as well. And
472
+ // each of that child's children must be updated with the new parent index.
473
+ // This is the only non-constant-time action for linking, but it only
474
+ // happens with branching promise chains specifically when the 2nd branch
475
+ // is added.
476
+ //
477
+ // Note: This does not account for branches of branches. That may result
478
+ // in improperly parented segments.
479
+ let parent = ctxlzr
480
+ let child = ctxlzr.child
481
+ const branchIdx = ctxlzr.context.branch()
482
+ do {
483
+ child.parentIdx = parent.idx
484
+ child.idx = branchIdx
485
+ parent = child
486
+ child = child.child
487
+ } while (child)
488
+
489
+ // We set the child to something falsey that isn't `null` so we can
490
+ // distinguish between having no child, having one child, and having
491
+ // multiple children.
492
+ ctxlzr.child = false
493
+ }
494
+
495
+ // If this is a branching link then create a new branch for the next promise.
496
+ // Otherwise, we can just piggy-back on the previous link's spot.
497
+ const idx = ctxlzr.child === false ? ctxlzr.context.branch() : ctxlzr.idx
498
+
499
+ // Create a new context for this next promise.
500
+ next.__NR_context = new Contextualizer(idx, ctxlzr.context)
501
+ next.__NR_context.parentIdx = ctxlzr.idx
502
+
503
+ // If this was our first child, remember it in case we have a 2nd.
504
+ if (ctxlzr.child === null) {
505
+ ctxlzr.child = next.__NR_context
506
+ }
507
+ } else if (segment) {
508
+ // This next promise is the root of a chain. Either there was no previous
509
+ // promise or the promise was created out of context.
510
+ next.__NR_context = new Contextualizer(0, new Context(segment))
511
+ }
512
+ }
513
+
514
+ isActive() {
515
+ const segments = this.context.segments
516
+ const segment = segments[this.idx] || segments[this.parentIdx] || segments[0]
517
+ return segment && segment.transaction.isActive()
518
+ }
519
+
520
+ getSegment() {
521
+ const segments = this.context.segments
522
+ let segment = segments[this.idx]
523
+ if (segment == null) {
524
+ segment = segments[this.idx] = segments[this.parentIdx] || segments[0]
525
+ }
526
+ return segment
527
+ }
528
+
529
+ setSegment(segment) {
530
+ return this.context.segments[this.idx] = segment
531
+ }
532
+
533
+ toJSON() {
534
+ // No-op.
535
+ }
536
+
537
+ continueContext(prom) {
538
+ const self = this
539
+ const nextContext = prom.__NR_context
540
+ if (!nextContext) {
541
+ return prom
542
+ }
543
+
544
+ // If we have `finally`, use that to sneak our context update.
545
+ if (typeof prom.finally === 'function') {
546
+ return prom.finally(__NR_continueContext)
547
+ }
548
+
549
+ // No `finally` means we need to hook into resolve and reject individually and
550
+ // pass through whatever happened.
551
+ return prom.then(function __NR_thenContext(val) {
552
+ __NR_continueContext()
553
+ return val
554
+ }, function __NR_catchContext(err) {
555
+ __NR_continueContext()
556
+ throw err // Re-throwing promise rejection, this is not New Relic's error.
557
+ })
558
+
559
+ function __NR_continueContext() {
560
+ self.setSegment(nextContext.getSegment())
561
+ }
562
+ }
563
+ }
package/lib/shim/shim.js CHANGED
@@ -18,6 +18,10 @@ try {
18
18
  logger.debug(err, 'Failed to load es6 shimming methods.')
19
19
  }
20
20
 
21
+ // Some modules do terrible things, like change the prototype of functions. To
22
+ // avoid crashing things we'll use a cached copy of apply everywhere.
23
+ const fnApply = Function.prototype.apply
24
+
21
25
  /**
22
26
  * Constructs a shim associated with the given agent instance.
23
27
  *
@@ -118,6 +122,7 @@ Shim.prototype.isString = isString
118
122
  Shim.prototype.isNumber = isNumber
119
123
  Shim.prototype.isBoolean = isBoolean
120
124
  Shim.prototype.isArray = isArray
125
+ Shim.prototype.isNull = isNull
121
126
  Shim.prototype.toArray = toArray
122
127
  Shim.prototype.argsToArray = argsToArray
123
128
  Shim.prototype.normalizeIndex = normalizeIndex
@@ -592,7 +597,7 @@ function wrap(nodule, properties, spec, args) {
592
597
  spec = this.setDefaults(spec, {matchArity: false})
593
598
 
594
599
  // If we're just wrapping one thing, just wrap it and return.
595
- if (!properties) {
600
+ if (properties == null) {
596
601
  this.logger.trace('Wrapping nodule itself.')
597
602
  return _wrap(this, nodule, this.getName(nodule), spec, args)
598
603
  }
@@ -696,7 +701,7 @@ function wrapReturn(nodule, properties, spec, args) {
696
701
  fnArgs.unshift(fn) // `unshift` === `push_front`
697
702
  ctx = ret = new (fn.bind.apply(fn, fnArgs))()
698
703
  } else {
699
- ret = fn.apply(ctx, arguments)
704
+ ret = fnApply.call(fn, ctx, arguments)
700
705
  }
701
706
 
702
707
  // Assemble the arguments to hand to the spec.
@@ -889,7 +894,7 @@ function record(nodule, properties, recordNamer) {
889
894
  var segDesc = recordNamer.call(this, shim, fn, name, args)
890
895
  if (!segDesc) {
891
896
  shim.logger.trace('No segment descriptor for "%s", not recording.', name)
892
- return fn.apply(this, args)
897
+ return fnApply.call(fn, this, args)
893
898
  }
894
899
  segDesc = new specs.RecorderSpec(segDesc)
895
900
 
@@ -905,12 +910,14 @@ function record(nodule, properties, recordNamer) {
905
910
 
906
911
  if (!parent) {
907
912
  shim.logger.debug('Not recording function %s, not in a transaction.', name)
908
- return fn.apply(this, arguments)
913
+ return fnApply.call(fn, this, arguments)
909
914
  }
910
915
 
911
- if (segDesc.callbackRequired &&
912
- !_hasValidCallbackArg(shim, args, segDesc.callback)) {
913
- return fn.apply(this, arguments)
916
+ if (
917
+ segDesc.callbackRequired &&
918
+ !_hasValidCallbackArg(shim, args, segDesc.callback)
919
+ ) {
920
+ return fnApply.call(fn, this, arguments)
914
921
  }
915
922
 
916
923
  // Only create a segment if:
@@ -1362,7 +1369,7 @@ function applySegment(func, segment, full, context, args, inContextCB) {
1362
1369
 
1363
1370
  if (!segment) {
1364
1371
  this.logger.trace('No segment to apply to function.')
1365
- return func.apply(context, args)
1372
+ return fnApply.call(func, context, args)
1366
1373
  }
1367
1374
  this.logger.trace('Applying segment %s', segment.name)
1368
1375
 
@@ -1380,7 +1387,13 @@ function applySegment(func, segment, full, context, args, inContextCB) {
1380
1387
 
1381
1388
  // Execute the function and then return the tracer segment to the old one.
1382
1389
  try {
1383
- return func.apply(context, args)
1390
+ return fnApply.call(func, context, args)
1391
+ } catch (error) {
1392
+ if (prevSegment === null && process.domain != null) {
1393
+ process.domain.__NR_segment = tracer.segment
1394
+ }
1395
+
1396
+ throw error // Rethrowing application error, this is not an agent error.
1384
1397
  } finally {
1385
1398
  if (full) {
1386
1399
  segment.touch()
@@ -1416,9 +1429,10 @@ function createSegment(name, recorder, parent) {
1416
1429
  var opts = null
1417
1430
  if (this.isString(name)) {
1418
1431
  // createSegment(name [, recorder] [, parent])
1419
- opts = new specs.SegmentSpec({name: name})
1432
+ opts = new specs.SegmentSpec({name})
1420
1433
 
1421
- if (this.isFunction(recorder)) {
1434
+ // if the recorder arg is not used, it can either be omitted or null
1435
+ if (this.isFunction(recorder) || this.isNull(recorder)) {
1422
1436
  // createSegment(name, recorder [, parent])
1423
1437
  opts.recorder = recorder
1424
1438
  opts.parent = parent
@@ -1556,6 +1570,19 @@ function isPromise(obj) {
1556
1570
  return obj && typeof obj.then === 'function'
1557
1571
  }
1558
1572
 
1573
+ /**
1574
+ * Determines if the given value is null.
1575
+ *
1576
+ * @memberof Shim.prototype
1577
+ *
1578
+ * @param {*} val - The value to check.
1579
+ *
1580
+ * @return {bool} True if the value is null, else false.
1581
+ */
1582
+ function isNull(val) {
1583
+ return val === null
1584
+ }
1585
+
1559
1586
  /**
1560
1587
  * Converts an array-like object into an array.
1561
1588
  *
package/lib/shimmer.js CHANGED
@@ -13,6 +13,7 @@ var SHIM_TYPE_MAP = Object.create(null)
13
13
  SHIM_TYPE_MAP[MODULE_TYPE.GENERIC] = shims.Shim
14
14
  SHIM_TYPE_MAP[MODULE_TYPE.DATASTORE] = shims.DatastoreShim
15
15
  SHIM_TYPE_MAP[MODULE_TYPE.MESSAGE] = shims.MessageShim
16
+ SHIM_TYPE_MAP[MODULE_TYPE.PROMISE] = shims.PromiseShim
16
17
  SHIM_TYPE_MAP[MODULE_TYPE.TRANSACTION] = shims.TransactionShim
17
18
  SHIM_TYPE_MAP[MODULE_TYPE.WEB_FRAMEWORK] = shims.WebFrameworkShim
18
19