Initial commit
[yaffs-website] / node_modules / hoek / lib / index.js
1 // Load modules
2
3 var Crypto = require('crypto');
4 var Path = require('path');
5 var Util = require('util');
6 var Escape = require('./escape');
7
8
9 // Declare internals
10
11 var internals = {};
12
13
14 // Clone object or array
15
16 exports.clone = function (obj, seen) {
17
18     if (typeof obj !== 'object' ||
19         obj === null) {
20
21         return obj;
22     }
23
24     seen = seen || { orig: [], copy: [] };
25
26     var lookup = seen.orig.indexOf(obj);
27     if (lookup !== -1) {
28         return seen.copy[lookup];
29     }
30
31     var newObj;
32     var cloneDeep = false;
33
34     if (!Array.isArray(obj)) {
35         if (Buffer.isBuffer(obj)) {
36             newObj = new Buffer(obj);
37         }
38         else if (obj instanceof Date) {
39             newObj = new Date(obj.getTime());
40         }
41         else if (obj instanceof RegExp) {
42             newObj = new RegExp(obj);
43         }
44         else {
45             var proto = Object.getPrototypeOf(obj);
46             if (proto &&
47                 proto.isImmutable) {
48
49                 newObj = obj;
50             }
51             else {
52                 newObj = Object.create(proto);
53                 cloneDeep = true;
54             }
55         }
56     }
57     else {
58         newObj = [];
59         cloneDeep = true;
60     }
61
62     seen.orig.push(obj);
63     seen.copy.push(newObj);
64
65     if (cloneDeep) {
66         var keys = Object.getOwnPropertyNames(obj);
67         for (var i = 0, il = keys.length; i < il; ++i) {
68             var key = keys[i];
69             var descriptor = Object.getOwnPropertyDescriptor(obj, key);
70             if (descriptor &&
71                 (descriptor.get ||
72                  descriptor.set)) {
73
74                 Object.defineProperty(newObj, key, descriptor);
75             }
76             else {
77                 newObj[key] = exports.clone(obj[key], seen);
78             }
79         }
80     }
81
82     return newObj;
83 };
84
85
86 // Merge all the properties of source into target, source wins in conflict, and by default null and undefined from source are applied
87 /*eslint-disable */
88 exports.merge = function (target, source, isNullOverride /* = true */, isMergeArrays /* = true */) {
89 /*eslint-enable */
90     exports.assert(target && typeof target === 'object', 'Invalid target value: must be an object');
91     exports.assert(source === null || source === undefined || typeof source === 'object', 'Invalid source value: must be null, undefined, or an object');
92
93     if (!source) {
94         return target;
95     }
96
97     if (Array.isArray(source)) {
98         exports.assert(Array.isArray(target), 'Cannot merge array onto an object');
99         if (isMergeArrays === false) {                                                  // isMergeArrays defaults to true
100             target.length = 0;                                                          // Must not change target assignment
101         }
102
103         for (var i = 0, il = source.length; i < il; ++i) {
104             target.push(exports.clone(source[i]));
105         }
106
107         return target;
108     }
109
110     var keys = Object.keys(source);
111     for (var k = 0, kl = keys.length; k < kl; ++k) {
112         var key = keys[k];
113         var value = source[key];
114         if (value &&
115             typeof value === 'object') {
116
117             if (!target[key] ||
118                 typeof target[key] !== 'object' ||
119                 (Array.isArray(target[key]) ^ Array.isArray(value)) ||
120                 value instanceof Date ||
121                 Buffer.isBuffer(value) ||
122                 value instanceof RegExp) {
123
124                 target[key] = exports.clone(value);
125             }
126             else {
127                 exports.merge(target[key], value, isNullOverride, isMergeArrays);
128             }
129         }
130         else {
131             if (value !== null &&
132                 value !== undefined) {                              // Explicit to preserve empty strings
133
134                 target[key] = value;
135             }
136             else if (isNullOverride !== false) {                    // Defaults to true
137                 target[key] = value;
138             }
139         }
140     }
141
142     return target;
143 };
144
145
146 // Apply options to a copy of the defaults
147
148 exports.applyToDefaults = function (defaults, options, isNullOverride) {
149
150     exports.assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
151     exports.assert(!options || options === true || typeof options === 'object', 'Invalid options value: must be true, falsy or an object');
152
153     if (!options) {                                                 // If no options, return null
154         return null;
155     }
156
157     var copy = exports.clone(defaults);
158
159     if (options === true) {                                         // If options is set to true, use defaults
160         return copy;
161     }
162
163     return exports.merge(copy, options, isNullOverride === true, false);
164 };
165
166
167 // Clone an object except for the listed keys which are shallow copied
168
169 exports.cloneWithShallow = function (source, keys) {
170
171     if (!source ||
172         typeof source !== 'object') {
173
174         return source;
175     }
176
177     var storage = internals.store(source, keys);    // Move shallow copy items to storage
178     var copy = exports.clone(source);               // Deep copy the rest
179     internals.restore(copy, source, storage);       // Shallow copy the stored items and restore
180     return copy;
181 };
182
183
184 internals.store = function (source, keys) {
185
186     var storage = {};
187     for (var i = 0, il = keys.length; i < il; ++i) {
188         var key = keys[i];
189         var value = exports.reach(source, key);
190         if (value !== undefined) {
191             storage[key] = value;
192             internals.reachSet(source, key, undefined);
193         }
194     }
195
196     return storage;
197 };
198
199
200 internals.restore = function (copy, source, storage) {
201
202     var keys = Object.keys(storage);
203     for (var i = 0, il = keys.length; i < il; ++i) {
204         var key = keys[i];
205         internals.reachSet(copy, key, storage[key]);
206         internals.reachSet(source, key, storage[key]);
207     }
208 };
209
210
211 internals.reachSet = function (obj, key, value) {
212
213     var path = key.split('.');
214     var ref = obj;
215     for (var i = 0, il = path.length; i < il; ++i) {
216         var segment = path[i];
217         if (i + 1 === il) {
218             ref[segment] = value;
219         }
220
221         ref = ref[segment];
222     }
223 };
224
225
226 // Apply options to defaults except for the listed keys which are shallow copied from option without merging
227
228 exports.applyToDefaultsWithShallow = function (defaults, options, keys) {
229
230     exports.assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
231     exports.assert(!options || options === true || typeof options === 'object', 'Invalid options value: must be true, falsy or an object');
232     exports.assert(keys && Array.isArray(keys), 'Invalid keys');
233
234     if (!options) {                                                 // If no options, return null
235         return null;
236     }
237
238     var copy = exports.cloneWithShallow(defaults, keys);
239
240     if (options === true) {                                         // If options is set to true, use defaults
241         return copy;
242     }
243
244     var storage = internals.store(options, keys);   // Move shallow copy items to storage
245     exports.merge(copy, options, false, false);     // Deep copy the rest
246     internals.restore(copy, options, storage);      // Shallow copy the stored items and restore
247     return copy;
248 };
249
250
251 // Deep object or array comparison
252
253 exports.deepEqual = function (obj, ref, options, seen) {
254
255     options = options || { prototype: true };
256
257     var type = typeof obj;
258
259     if (type !== typeof ref) {
260         return false;
261     }
262
263     if (type !== 'object' ||
264         obj === null ||
265         ref === null) {
266
267         if (obj === ref) {                                                      // Copied from Deep-eql, copyright(c) 2013 Jake Luer, jake@alogicalparadox.com, MIT Licensed, https://github.com/chaijs/deep-eql
268             return obj !== 0 || 1 / obj === 1 / ref;        // -0 / +0
269         }
270
271         return obj !== obj && ref !== ref;                  // NaN
272     }
273
274     seen = seen || [];
275     if (seen.indexOf(obj) !== -1) {
276         return true;                            // If previous comparison failed, it would have stopped execution
277     }
278
279     seen.push(obj);
280
281     if (Array.isArray(obj)) {
282         if (!Array.isArray(ref)) {
283             return false;
284         }
285
286         if (!options.part && obj.length !== ref.length) {
287             return false;
288         }
289
290         for (var i = 0, il = obj.length; i < il; ++i) {
291             if (options.part) {
292                 var found = false;
293                 for (var r = 0, rl = ref.length; r < rl; ++r) {
294                     if (exports.deepEqual(obj[i], ref[r], options, seen)) {
295                         found = true;
296                         break;
297                     }
298                 }
299
300                 return found;
301             }
302
303             if (!exports.deepEqual(obj[i], ref[i], options, seen)) {
304                 return false;
305             }
306         }
307
308         return true;
309     }
310
311     if (Buffer.isBuffer(obj)) {
312         if (!Buffer.isBuffer(ref)) {
313             return false;
314         }
315
316         if (obj.length !== ref.length) {
317             return false;
318         }
319
320         for (var j = 0, jl = obj.length; j < jl; ++j) {
321             if (obj[j] !== ref[j]) {
322                 return false;
323             }
324         }
325
326         return true;
327     }
328
329     if (obj instanceof Date) {
330         return (ref instanceof Date && obj.getTime() === ref.getTime());
331     }
332
333     if (obj instanceof RegExp) {
334         return (ref instanceof RegExp && obj.toString() === ref.toString());
335     }
336
337     if (options.prototype) {
338         if (Object.getPrototypeOf(obj) !== Object.getPrototypeOf(ref)) {
339             return false;
340         }
341     }
342
343     var keys = Object.getOwnPropertyNames(obj);
344
345     if (!options.part && keys.length !== Object.getOwnPropertyNames(ref).length) {
346         return false;
347     }
348
349     for (var k = 0, kl = keys.length; k < kl; ++k) {
350         var key = keys[k];
351         var descriptor = Object.getOwnPropertyDescriptor(obj, key);
352         if (descriptor.get) {
353             if (!exports.deepEqual(descriptor, Object.getOwnPropertyDescriptor(ref, key), options, seen)) {
354                 return false;
355             }
356         }
357         else if (!exports.deepEqual(obj[key], ref[key], options, seen)) {
358             return false;
359         }
360     }
361
362     return true;
363 };
364
365
366 // Remove duplicate items from array
367
368 exports.unique = function (array, key) {
369
370     var index = {};
371     var result = [];
372
373     for (var i = 0, il = array.length; i < il; ++i) {
374         var id = (key ? array[i][key] : array[i]);
375         if (index[id] !== true) {
376
377             result.push(array[i]);
378             index[id] = true;
379         }
380     }
381
382     return result;
383 };
384
385
386 // Convert array into object
387
388 exports.mapToObject = function (array, key) {
389
390     if (!array) {
391         return null;
392     }
393
394     var obj = {};
395     for (var i = 0, il = array.length; i < il; ++i) {
396         if (key) {
397             if (array[i][key]) {
398                 obj[array[i][key]] = true;
399             }
400         }
401         else {
402             obj[array[i]] = true;
403         }
404     }
405
406     return obj;
407 };
408
409
410 // Find the common unique items in two arrays
411
412 exports.intersect = function (array1, array2, justFirst) {
413
414     if (!array1 || !array2) {
415         return [];
416     }
417
418     var common = [];
419     var hash = (Array.isArray(array1) ? exports.mapToObject(array1) : array1);
420     var found = {};
421     for (var i = 0, il = array2.length; i < il; ++i) {
422         if (hash[array2[i]] && !found[array2[i]]) {
423             if (justFirst) {
424                 return array2[i];
425             }
426
427             common.push(array2[i]);
428             found[array2[i]] = true;
429         }
430     }
431
432     return (justFirst ? null : common);
433 };
434
435
436 // Test if the reference contains the values
437
438 exports.contain = function (ref, values, options) {
439
440     /*
441         string -> string(s)
442         array -> item(s)
443         object -> key(s)
444         object -> object (key:value)
445     */
446
447     var valuePairs = null;
448     if (typeof ref === 'object' &&
449         typeof values === 'object' &&
450         !Array.isArray(ref) &&
451         !Array.isArray(values)) {
452
453         valuePairs = values;
454         values = Object.keys(values);
455     }
456     else {
457         values = [].concat(values);
458     }
459
460     options = options || {};            // deep, once, only, part
461
462     exports.assert(arguments.length >= 2, 'Insufficient arguments');
463     exports.assert(typeof ref === 'string' || typeof ref === 'object', 'Reference must be string or an object');
464     exports.assert(values.length, 'Values array cannot be empty');
465
466     var compare, compareFlags;
467     if (options.deep) {
468         compare = exports.deepEqual;
469
470         var hasOnly = options.hasOwnProperty('only'), hasPart = options.hasOwnProperty('part');
471
472         compareFlags = {
473             prototype: hasOnly ? options.only : hasPart ? !options.part : false,
474             part: hasOnly ? !options.only : hasPart ? options.part : true
475         };
476     }
477     else {
478         compare = function (a, b) {
479
480             return a === b;
481         };
482     }
483
484     var misses = false;
485     var matches = new Array(values.length);
486     for (var i = 0, il = matches.length; i < il; ++i) {
487         matches[i] = 0;
488     }
489
490     if (typeof ref === 'string') {
491         var pattern = '(';
492         for (i = 0, il = values.length; i < il; ++i) {
493             var value = values[i];
494             exports.assert(typeof value === 'string', 'Cannot compare string reference to non-string value');
495             pattern += (i ? '|' : '') + exports.escapeRegex(value);
496         }
497
498         var regex = new RegExp(pattern + ')', 'g');
499         var leftovers = ref.replace(regex, function ($0, $1) {
500
501             var index = values.indexOf($1);
502             ++matches[index];
503             return '';          // Remove from string
504         });
505
506         misses = !!leftovers;
507     }
508     else if (Array.isArray(ref)) {
509         for (i = 0, il = ref.length; i < il; ++i) {
510             for (var j = 0, jl = values.length, matched = false; j < jl && matched === false; ++j) {
511                 matched = compare(values[j], ref[i], compareFlags) && j;
512             }
513
514             if (matched !== false) {
515                 ++matches[matched];
516             }
517             else {
518                 misses = true;
519             }
520         }
521     }
522     else {
523         var keys = Object.keys(ref);
524         for (i = 0, il = keys.length; i < il; ++i) {
525             var key = keys[i];
526             var pos = values.indexOf(key);
527             if (pos !== -1) {
528                 if (valuePairs &&
529                     !compare(valuePairs[key], ref[key], compareFlags)) {
530
531                     return false;
532                 }
533
534                 ++matches[pos];
535             }
536             else {
537                 misses = true;
538             }
539         }
540     }
541
542     var result = false;
543     for (i = 0, il = matches.length; i < il; ++i) {
544         result = result || !!matches[i];
545         if ((options.once && matches[i] > 1) ||
546             (!options.part && !matches[i])) {
547
548             return false;
549         }
550     }
551
552     if (options.only &&
553         misses) {
554
555         return false;
556     }
557
558     return result;
559 };
560
561
562 // Flatten array
563
564 exports.flatten = function (array, target) {
565
566     var result = target || [];
567
568     for (var i = 0, il = array.length; i < il; ++i) {
569         if (Array.isArray(array[i])) {
570             exports.flatten(array[i], result);
571         }
572         else {
573             result.push(array[i]);
574         }
575     }
576
577     return result;
578 };
579
580
581 // Convert an object key chain string ('a.b.c') to reference (object[a][b][c])
582
583 exports.reach = function (obj, chain, options) {
584
585     if (chain === false ||
586         chain === null ||
587         typeof chain === 'undefined') {
588
589         return obj;
590     }
591
592     options = options || {};
593     if (typeof options === 'string') {
594         options = { separator: options };
595     }
596
597     var path = chain.split(options.separator || '.');
598     var ref = obj;
599     for (var i = 0, il = path.length; i < il; ++i) {
600         var key = path[i];
601         if (key[0] === '-' && Array.isArray(ref)) {
602             key = key.slice(1, key.length);
603             key = ref.length - key;
604         }
605
606         if (!ref ||
607             !ref.hasOwnProperty(key) ||
608             (typeof ref !== 'object' && options.functions === false)) {         // Only object and function can have properties
609
610             exports.assert(!options.strict || i + 1 === il, 'Missing segment', key, 'in reach path ', chain);
611             exports.assert(typeof ref === 'object' || options.functions === true || typeof ref !== 'function', 'Invalid segment', key, 'in reach path ', chain);
612             ref = options.default;
613             break;
614         }
615
616         ref = ref[key];
617     }
618
619     return ref;
620 };
621
622
623 exports.reachTemplate = function (obj, template, options) {
624
625     return template.replace(/{([^}]+)}/g, function ($0, chain) {
626
627         var value = exports.reach(obj, chain, options);
628         return (value === undefined || value === null ? '' : value);
629     });
630 };
631
632
633 exports.formatStack = function (stack) {
634
635     var trace = [];
636     for (var i = 0, il = stack.length; i < il; ++i) {
637         var item = stack[i];
638         trace.push([item.getFileName(), item.getLineNumber(), item.getColumnNumber(), item.getFunctionName(), item.isConstructor()]);
639     }
640
641     return trace;
642 };
643
644
645 exports.formatTrace = function (trace) {
646
647     var display = [];
648
649     for (var i = 0, il = trace.length; i < il; ++i) {
650         var row = trace[i];
651         display.push((row[4] ? 'new ' : '') + row[3] + ' (' + row[0] + ':' + row[1] + ':' + row[2] + ')');
652     }
653
654     return display;
655 };
656
657
658 exports.callStack = function (slice) {
659
660     // http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
661
662     var v8 = Error.prepareStackTrace;
663     Error.prepareStackTrace = function (err, stack) {
664
665         return stack;
666     };
667
668     var capture = {};
669     Error.captureStackTrace(capture, arguments.callee);     /*eslint no-caller:0 */
670     var stack = capture.stack;
671
672     Error.prepareStackTrace = v8;
673
674     var trace = exports.formatStack(stack);
675
676     if (slice) {
677         return trace.slice(slice);
678     }
679
680     return trace;
681 };
682
683
684 exports.displayStack = function (slice) {
685
686     var trace = exports.callStack(slice === undefined ? 1 : slice + 1);
687
688     return exports.formatTrace(trace);
689 };
690
691
692 exports.abortThrow = false;
693
694
695 exports.abort = function (message, hideStack) {
696
697     if (process.env.NODE_ENV === 'test' || exports.abortThrow === true) {
698         throw new Error(message || 'Unknown error');
699     }
700
701     var stack = '';
702     if (!hideStack) {
703         stack = exports.displayStack(1).join('\n\t');
704     }
705     console.log('ABORT: ' + message + '\n\t' + stack);
706     process.exit(1);
707 };
708
709
710 exports.assert = function (condition /*, msg1, msg2, msg3 */) {
711
712     if (condition) {
713         return;
714     }
715
716     if (arguments.length === 2 && arguments[1] instanceof Error) {
717         throw arguments[1];
718     }
719
720     var msgs = [];
721     for (var i = 1, il = arguments.length; i < il; ++i) {
722         if (arguments[i] !== '') {
723             msgs.push(arguments[i]);            // Avoids Array.slice arguments leak, allowing for V8 optimizations
724         }
725     }
726
727     msgs = msgs.map(function (msg) {
728
729         return typeof msg === 'string' ? msg : msg instanceof Error ? msg.message : exports.stringify(msg);
730     });
731     throw new Error(msgs.join(' ') || 'Unknown error');
732 };
733
734
735 exports.Timer = function () {
736
737     this.ts = 0;
738     this.reset();
739 };
740
741
742 exports.Timer.prototype.reset = function () {
743
744     this.ts = Date.now();
745 };
746
747
748 exports.Timer.prototype.elapsed = function () {
749
750     return Date.now() - this.ts;
751 };
752
753
754 exports.Bench = function () {
755
756     this.ts = 0;
757     this.reset();
758 };
759
760
761 exports.Bench.prototype.reset = function () {
762
763     this.ts = exports.Bench.now();
764 };
765
766
767 exports.Bench.prototype.elapsed = function () {
768
769     return exports.Bench.now() - this.ts;
770 };
771
772
773 exports.Bench.now = function () {
774
775     var ts = process.hrtime();
776     return (ts[0] * 1e3) + (ts[1] / 1e6);
777 };
778
779
780 // Escape string for Regex construction
781
782 exports.escapeRegex = function (string) {
783
784     // Escape ^$.*+-?=!:|\/()[]{},
785     return string.replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, '\\$&');
786 };
787
788
789 // Base64url (RFC 4648) encode
790
791 exports.base64urlEncode = function (value, encoding) {
792
793     var buf = (Buffer.isBuffer(value) ? value : new Buffer(value, encoding || 'binary'));
794     return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
795 };
796
797
798 // Base64url (RFC 4648) decode
799
800 exports.base64urlDecode = function (value, encoding) {
801
802     if (value &&
803         !/^[\w\-]*$/.test(value)) {
804
805         return new Error('Invalid character');
806     }
807
808     try {
809         var buf = new Buffer(value, 'base64');
810         return (encoding === 'buffer' ? buf : buf.toString(encoding || 'binary'));
811     }
812     catch (err) {
813         return err;
814     }
815 };
816
817
818 // Escape attribute value for use in HTTP header
819
820 exports.escapeHeaderAttribute = function (attribute) {
821
822     // Allowed value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9, \, "
823
824     exports.assert(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~\"\\]*$/.test(attribute), 'Bad attribute value (' + attribute + ')');
825
826     return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"');                             // Escape quotes and slash
827 };
828
829
830 exports.escapeHtml = function (string) {
831
832     return Escape.escapeHtml(string);
833 };
834
835
836 exports.escapeJavaScript = function (string) {
837
838     return Escape.escapeJavaScript(string);
839 };
840
841
842 exports.nextTick = function (callback) {
843
844     return function () {
845
846         var args = arguments;
847         process.nextTick(function () {
848
849             callback.apply(null, args);
850         });
851     };
852 };
853
854
855 exports.once = function (method) {
856
857     if (method._hoekOnce) {
858         return method;
859     }
860
861     var once = false;
862     var wrapped = function () {
863
864         if (!once) {
865             once = true;
866             method.apply(null, arguments);
867         }
868     };
869
870     wrapped._hoekOnce = true;
871
872     return wrapped;
873 };
874
875
876 exports.isAbsolutePath = function (path, platform) {
877
878     if (!path) {
879         return false;
880     }
881
882     if (Path.isAbsolute) {                      // node >= 0.11
883         return Path.isAbsolute(path);
884     }
885
886     platform = platform || process.platform;
887
888     // Unix
889
890     if (platform !== 'win32') {
891         return path[0] === '/';
892     }
893
894     // Windows
895
896     return !!/^(?:[a-zA-Z]:[\\\/])|(?:[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/])/.test(path);        // C:\ or \\something\something
897 };
898
899
900 exports.isInteger = function (value) {
901
902     return (typeof value === 'number' &&
903             parseFloat(value) === parseInt(value, 10) &&
904             !isNaN(value));
905 };
906
907
908 exports.ignore = function () { };
909
910
911 exports.inherits = Util.inherits;
912
913
914 exports.format = Util.format;
915
916
917 exports.transform = function (source, transform, options) {
918
919     exports.assert(source === null || source === undefined || typeof source === 'object' || Array.isArray(source), 'Invalid source object: must be null, undefined, an object, or an array');
920
921     if (Array.isArray(source)) {
922         var results = [];
923         for (var i = 0, il = source.length; i < il; ++i) {
924             results.push(exports.transform(source[i], transform, options));
925         }
926         return results;
927     }
928
929     var result = {};
930     var keys = Object.keys(transform);
931
932     for (var k = 0, kl = keys.length; k < kl; ++k) {
933         var key = keys[k];
934         var path = key.split('.');
935         var sourcePath = transform[key];
936
937         exports.assert(typeof sourcePath === 'string', 'All mappings must be "." delineated strings');
938
939         var segment;
940         var res = result;
941
942         while (path.length > 1) {
943             segment = path.shift();
944             if (!res[segment]) {
945                 res[segment] = {};
946             }
947             res = res[segment];
948         }
949         segment = path.shift();
950         res[segment] = exports.reach(source, sourcePath, options);
951     }
952
953     return result;
954 };
955
956
957 exports.uniqueFilename = function (path, extension) {
958
959     if (extension) {
960         extension = extension[0] !== '.' ? '.' + extension : extension;
961     }
962     else {
963         extension = '';
964     }
965
966     path = Path.resolve(path);
967     var name = [Date.now(), process.pid, Crypto.randomBytes(8).toString('hex')].join('-') + extension;
968     return Path.join(path, name);
969 };
970
971
972 exports.stringify = function () {
973
974     try {
975         return JSON.stringify.apply(null, arguments);
976     }
977     catch (err) {
978         return '[Cannot display object: ' + err.message + ']';
979     }
980 };
981
982
983 exports.shallow = function (source) {
984
985     var target = {};
986     var keys = Object.keys(source);
987     for (var i = 0, il = keys.length; i < il; ++i) {
988         var key = keys[i];
989         target[key] = source[key];
990     }
991
992     return target;
993 };