index.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. 'use strict';
  2. // Load modules
  3. const Crypto = require('crypto');
  4. const Path = require('path');
  5. const Util = require('util');
  6. const Escape = require('./escape');
  7. // Declare internals
  8. const internals = {};
  9. // Clone object or array
  10. exports.clone = function (obj, seen) {
  11. if (typeof obj !== 'object' ||
  12. obj === null) {
  13. return obj;
  14. }
  15. seen = seen || new Map();
  16. const lookup = seen.get(obj);
  17. if (lookup) {
  18. return lookup;
  19. }
  20. let newObj;
  21. let cloneDeep = false;
  22. if (!Array.isArray(obj)) {
  23. if (Buffer.isBuffer(obj)) {
  24. newObj = new Buffer(obj);
  25. }
  26. else if (obj instanceof Date) {
  27. newObj = new Date(obj.getTime());
  28. }
  29. else if (obj instanceof RegExp) {
  30. newObj = new RegExp(obj);
  31. }
  32. else {
  33. const proto = Object.getPrototypeOf(obj);
  34. if (proto &&
  35. proto.isImmutable) {
  36. newObj = obj;
  37. }
  38. else {
  39. newObj = Object.create(proto);
  40. cloneDeep = true;
  41. }
  42. }
  43. }
  44. else {
  45. newObj = [];
  46. cloneDeep = true;
  47. }
  48. seen.set(obj, newObj);
  49. if (cloneDeep) {
  50. const keys = Object.getOwnPropertyNames(obj);
  51. for (let i = 0; i < keys.length; ++i) {
  52. const key = keys[i];
  53. const descriptor = Object.getOwnPropertyDescriptor(obj, key);
  54. if (descriptor &&
  55. (descriptor.get ||
  56. descriptor.set)) {
  57. Object.defineProperty(newObj, key, descriptor);
  58. }
  59. else {
  60. newObj[key] = exports.clone(obj[key], seen);
  61. }
  62. }
  63. }
  64. return newObj;
  65. };
  66. // Merge all the properties of source into target, source wins in conflict, and by default null and undefined from source are applied
  67. /*eslint-disable */
  68. exports.merge = function (target, source, isNullOverride /* = true */, isMergeArrays /* = true */) {
  69. /*eslint-enable */
  70. exports.assert(target && typeof target === 'object', 'Invalid target value: must be an object');
  71. exports.assert(source === null || source === undefined || typeof source === 'object', 'Invalid source value: must be null, undefined, or an object');
  72. if (!source) {
  73. return target;
  74. }
  75. if (Array.isArray(source)) {
  76. exports.assert(Array.isArray(target), 'Cannot merge array onto an object');
  77. if (isMergeArrays === false) { // isMergeArrays defaults to true
  78. target.length = 0; // Must not change target assignment
  79. }
  80. for (let i = 0; i < source.length; ++i) {
  81. target.push(exports.clone(source[i]));
  82. }
  83. return target;
  84. }
  85. const keys = Object.keys(source);
  86. for (let i = 0; i < keys.length; ++i) {
  87. const key = keys[i];
  88. if (key === '__proto__') {
  89. continue;
  90. }
  91. const value = source[key];
  92. if (value &&
  93. typeof value === 'object') {
  94. if (!target[key] ||
  95. typeof target[key] !== 'object' ||
  96. (Array.isArray(target[key]) !== Array.isArray(value)) ||
  97. value instanceof Date ||
  98. Buffer.isBuffer(value) ||
  99. value instanceof RegExp) {
  100. target[key] = exports.clone(value);
  101. }
  102. else {
  103. exports.merge(target[key], value, isNullOverride, isMergeArrays);
  104. }
  105. }
  106. else {
  107. if (value !== null &&
  108. value !== undefined) { // Explicit to preserve empty strings
  109. target[key] = value;
  110. }
  111. else if (isNullOverride !== false) { // Defaults to true
  112. target[key] = value;
  113. }
  114. }
  115. }
  116. return target;
  117. };
  118. // Apply options to a copy of the defaults
  119. exports.applyToDefaults = function (defaults, options, isNullOverride) {
  120. exports.assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
  121. exports.assert(!options || options === true || typeof options === 'object', 'Invalid options value: must be true, falsy or an object');
  122. if (!options) { // If no options, return null
  123. return null;
  124. }
  125. const copy = exports.clone(defaults);
  126. if (options === true) { // If options is set to true, use defaults
  127. return copy;
  128. }
  129. return exports.merge(copy, options, isNullOverride === true, false);
  130. };
  131. // Clone an object except for the listed keys which are shallow copied
  132. exports.cloneWithShallow = function (source, keys) {
  133. if (!source ||
  134. typeof source !== 'object') {
  135. return source;
  136. }
  137. const storage = internals.store(source, keys); // Move shallow copy items to storage
  138. const copy = exports.clone(source); // Deep copy the rest
  139. internals.restore(copy, source, storage); // Shallow copy the stored items and restore
  140. return copy;
  141. };
  142. internals.store = function (source, keys) {
  143. const storage = {};
  144. for (let i = 0; i < keys.length; ++i) {
  145. const key = keys[i];
  146. const value = exports.reach(source, key);
  147. if (value !== undefined) {
  148. storage[key] = value;
  149. internals.reachSet(source, key, undefined);
  150. }
  151. }
  152. return storage;
  153. };
  154. internals.restore = function (copy, source, storage) {
  155. const keys = Object.keys(storage);
  156. for (let i = 0; i < keys.length; ++i) {
  157. const key = keys[i];
  158. internals.reachSet(copy, key, storage[key]);
  159. internals.reachSet(source, key, storage[key]);
  160. }
  161. };
  162. internals.reachSet = function (obj, key, value) {
  163. const path = key.split('.');
  164. let ref = obj;
  165. for (let i = 0; i < path.length; ++i) {
  166. const segment = path[i];
  167. if (i + 1 === path.length) {
  168. ref[segment] = value;
  169. }
  170. ref = ref[segment];
  171. }
  172. };
  173. // Apply options to defaults except for the listed keys which are shallow copied from option without merging
  174. exports.applyToDefaultsWithShallow = function (defaults, options, keys) {
  175. exports.assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
  176. exports.assert(!options || options === true || typeof options === 'object', 'Invalid options value: must be true, falsy or an object');
  177. exports.assert(keys && Array.isArray(keys), 'Invalid keys');
  178. if (!options) { // If no options, return null
  179. return null;
  180. }
  181. const copy = exports.cloneWithShallow(defaults, keys);
  182. if (options === true) { // If options is set to true, use defaults
  183. return copy;
  184. }
  185. const storage = internals.store(options, keys); // Move shallow copy items to storage
  186. exports.merge(copy, options, false, false); // Deep copy the rest
  187. internals.restore(copy, options, storage); // Shallow copy the stored items and restore
  188. return copy;
  189. };
  190. // Deep object or array comparison
  191. exports.deepEqual = function (obj, ref, options, seen) {
  192. options = options || { prototype: true };
  193. const type = typeof obj;
  194. if (type !== typeof ref) {
  195. return false;
  196. }
  197. if (type !== 'object' ||
  198. obj === null ||
  199. ref === null) {
  200. if (obj === ref) { // Copied from Deep-eql, copyright(c) 2013 Jake Luer, jake@alogicalparadox.com, MIT Licensed, https://github.com/chaijs/deep-eql
  201. return obj !== 0 || 1 / obj === 1 / ref; // -0 / +0
  202. }
  203. return obj !== obj && ref !== ref; // NaN
  204. }
  205. seen = seen || [];
  206. if (seen.indexOf(obj) !== -1) {
  207. return true; // If previous comparison failed, it would have stopped execution
  208. }
  209. seen.push(obj);
  210. if (Array.isArray(obj)) {
  211. if (!Array.isArray(ref)) {
  212. return false;
  213. }
  214. if (!options.part && obj.length !== ref.length) {
  215. return false;
  216. }
  217. for (let i = 0; i < obj.length; ++i) {
  218. if (options.part) {
  219. let found = false;
  220. for (let j = 0; j < ref.length; ++j) {
  221. if (exports.deepEqual(obj[i], ref[j], options)) {
  222. found = true;
  223. break;
  224. }
  225. }
  226. return found;
  227. }
  228. if (!exports.deepEqual(obj[i], ref[i], options)) {
  229. return false;
  230. }
  231. }
  232. return true;
  233. }
  234. if (Buffer.isBuffer(obj)) {
  235. if (!Buffer.isBuffer(ref)) {
  236. return false;
  237. }
  238. if (obj.length !== ref.length) {
  239. return false;
  240. }
  241. for (let i = 0; i < obj.length; ++i) {
  242. if (obj[i] !== ref[i]) {
  243. return false;
  244. }
  245. }
  246. return true;
  247. }
  248. if (obj instanceof Date) {
  249. return (ref instanceof Date && obj.getTime() === ref.getTime());
  250. }
  251. if (obj instanceof RegExp) {
  252. return (ref instanceof RegExp && obj.toString() === ref.toString());
  253. }
  254. if (options.prototype) {
  255. if (Object.getPrototypeOf(obj) !== Object.getPrototypeOf(ref)) {
  256. return false;
  257. }
  258. }
  259. const keys = Object.getOwnPropertyNames(obj);
  260. if (!options.part && keys.length !== Object.getOwnPropertyNames(ref).length) {
  261. return false;
  262. }
  263. for (let i = 0; i < keys.length; ++i) {
  264. const key = keys[i];
  265. const descriptor = Object.getOwnPropertyDescriptor(obj, key);
  266. if (descriptor.get) {
  267. if (!exports.deepEqual(descriptor, Object.getOwnPropertyDescriptor(ref, key), options, seen)) {
  268. return false;
  269. }
  270. }
  271. else if (!exports.deepEqual(obj[key], ref[key], options, seen)) {
  272. return false;
  273. }
  274. }
  275. return true;
  276. };
  277. // Remove duplicate items from array
  278. exports.unique = (array, key) => {
  279. let result;
  280. if (key) {
  281. result = [];
  282. const index = new Set();
  283. array.forEach((item) => {
  284. const identifier = item[key];
  285. if (!index.has(identifier)) {
  286. index.add(identifier);
  287. result.push(item);
  288. }
  289. });
  290. }
  291. else {
  292. result = Array.from(new Set(array));
  293. }
  294. return result;
  295. };
  296. // Convert array into object
  297. exports.mapToObject = function (array, key) {
  298. if (!array) {
  299. return null;
  300. }
  301. const obj = {};
  302. for (let i = 0; i < array.length; ++i) {
  303. if (key) {
  304. if (array[i][key]) {
  305. obj[array[i][key]] = true;
  306. }
  307. }
  308. else {
  309. obj[array[i]] = true;
  310. }
  311. }
  312. return obj;
  313. };
  314. // Find the common unique items in two arrays
  315. exports.intersect = function (array1, array2, justFirst) {
  316. if (!array1 || !array2) {
  317. return [];
  318. }
  319. const common = [];
  320. const hash = (Array.isArray(array1) ? exports.mapToObject(array1) : array1);
  321. const found = {};
  322. for (let i = 0; i < array2.length; ++i) {
  323. if (hash[array2[i]] && !found[array2[i]]) {
  324. if (justFirst) {
  325. return array2[i];
  326. }
  327. common.push(array2[i]);
  328. found[array2[i]] = true;
  329. }
  330. }
  331. return (justFirst ? null : common);
  332. };
  333. // Test if the reference contains the values
  334. exports.contain = function (ref, values, options) {
  335. /*
  336. string -> string(s)
  337. array -> item(s)
  338. object -> key(s)
  339. object -> object (key:value)
  340. */
  341. let valuePairs = null;
  342. if (typeof ref === 'object' &&
  343. typeof values === 'object' &&
  344. !Array.isArray(ref) &&
  345. !Array.isArray(values)) {
  346. valuePairs = values;
  347. values = Object.keys(values);
  348. }
  349. else {
  350. values = [].concat(values);
  351. }
  352. options = options || {}; // deep, once, only, part
  353. exports.assert(arguments.length >= 2, 'Insufficient arguments');
  354. exports.assert(typeof ref === 'string' || typeof ref === 'object', 'Reference must be string or an object');
  355. exports.assert(values.length, 'Values array cannot be empty');
  356. let compare;
  357. let compareFlags;
  358. if (options.deep) {
  359. compare = exports.deepEqual;
  360. const hasOnly = options.hasOwnProperty('only');
  361. const hasPart = options.hasOwnProperty('part');
  362. compareFlags = {
  363. prototype: hasOnly ? options.only : hasPart ? !options.part : false,
  364. part: hasOnly ? !options.only : hasPart ? options.part : true
  365. };
  366. }
  367. else {
  368. compare = (a, b) => a === b;
  369. }
  370. let misses = false;
  371. const matches = new Array(values.length);
  372. for (let i = 0; i < matches.length; ++i) {
  373. matches[i] = 0;
  374. }
  375. if (typeof ref === 'string') {
  376. let pattern = '(';
  377. for (let i = 0; i < values.length; ++i) {
  378. const value = values[i];
  379. exports.assert(typeof value === 'string', 'Cannot compare string reference to non-string value');
  380. pattern += (i ? '|' : '') + exports.escapeRegex(value);
  381. }
  382. const regex = new RegExp(pattern + ')', 'g');
  383. const leftovers = ref.replace(regex, ($0, $1) => {
  384. const index = values.indexOf($1);
  385. ++matches[index];
  386. return ''; // Remove from string
  387. });
  388. misses = !!leftovers;
  389. }
  390. else if (Array.isArray(ref)) {
  391. for (let i = 0; i < ref.length; ++i) {
  392. let matched = false;
  393. for (let j = 0; j < values.length && matched === false; ++j) {
  394. matched = compare(values[j], ref[i], compareFlags) && j;
  395. }
  396. if (matched !== false) {
  397. ++matches[matched];
  398. }
  399. else {
  400. misses = true;
  401. }
  402. }
  403. }
  404. else {
  405. const keys = Object.getOwnPropertyNames(ref);
  406. for (let i = 0; i < keys.length; ++i) {
  407. const key = keys[i];
  408. const pos = values.indexOf(key);
  409. if (pos !== -1) {
  410. if (valuePairs &&
  411. !compare(valuePairs[key], ref[key], compareFlags)) {
  412. return false;
  413. }
  414. ++matches[pos];
  415. }
  416. else {
  417. misses = true;
  418. }
  419. }
  420. }
  421. let result = false;
  422. for (let i = 0; i < matches.length; ++i) {
  423. result = result || !!matches[i];
  424. if ((options.once && matches[i] > 1) ||
  425. (!options.part && !matches[i])) {
  426. return false;
  427. }
  428. }
  429. if (options.only &&
  430. misses) {
  431. return false;
  432. }
  433. return result;
  434. };
  435. // Flatten array
  436. exports.flatten = function (array, target) {
  437. const result = target || [];
  438. for (let i = 0; i < array.length; ++i) {
  439. if (Array.isArray(array[i])) {
  440. exports.flatten(array[i], result);
  441. }
  442. else {
  443. result.push(array[i]);
  444. }
  445. }
  446. return result;
  447. };
  448. // Convert an object key chain string ('a.b.c') to reference (object[a][b][c])
  449. exports.reach = function (obj, chain, options) {
  450. if (chain === false ||
  451. chain === null ||
  452. typeof chain === 'undefined') {
  453. return obj;
  454. }
  455. options = options || {};
  456. if (typeof options === 'string') {
  457. options = { separator: options };
  458. }
  459. const path = chain.split(options.separator || '.');
  460. let ref = obj;
  461. for (let i = 0; i < path.length; ++i) {
  462. let key = path[i];
  463. if (key[0] === '-' && Array.isArray(ref)) {
  464. key = key.slice(1, key.length);
  465. key = ref.length - key;
  466. }
  467. if (!ref ||
  468. !((typeof ref === 'object' || typeof ref === 'function') && key in ref) ||
  469. (typeof ref !== 'object' && options.functions === false)) { // Only object and function can have properties
  470. exports.assert(!options.strict || i + 1 === path.length, 'Missing segment', key, 'in reach path ', chain);
  471. exports.assert(typeof ref === 'object' || options.functions === true || typeof ref !== 'function', 'Invalid segment', key, 'in reach path ', chain);
  472. ref = options.default;
  473. break;
  474. }
  475. ref = ref[key];
  476. }
  477. return ref;
  478. };
  479. exports.reachTemplate = function (obj, template, options) {
  480. return template.replace(/{([^}]+)}/g, ($0, chain) => {
  481. const value = exports.reach(obj, chain, options);
  482. return (value === undefined || value === null ? '' : value);
  483. });
  484. };
  485. exports.formatStack = function (stack) {
  486. const trace = [];
  487. for (let i = 0; i < stack.length; ++i) {
  488. const item = stack[i];
  489. trace.push([item.getFileName(), item.getLineNumber(), item.getColumnNumber(), item.getFunctionName(), item.isConstructor()]);
  490. }
  491. return trace;
  492. };
  493. exports.formatTrace = function (trace) {
  494. const display = [];
  495. for (let i = 0; i < trace.length; ++i) {
  496. const row = trace[i];
  497. display.push((row[4] ? 'new ' : '') + row[3] + ' (' + row[0] + ':' + row[1] + ':' + row[2] + ')');
  498. }
  499. return display;
  500. };
  501. exports.callStack = function (slice) {
  502. // http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
  503. const v8 = Error.prepareStackTrace;
  504. Error.prepareStackTrace = function (_, stack) {
  505. return stack;
  506. };
  507. const capture = {};
  508. Error.captureStackTrace(capture, this); // arguments.callee is not supported in strict mode so we use this and slice the trace of this off the result
  509. const stack = capture.stack;
  510. Error.prepareStackTrace = v8;
  511. const trace = exports.formatStack(stack);
  512. return trace.slice(1 + slice);
  513. };
  514. exports.displayStack = function (slice) {
  515. const trace = exports.callStack(slice === undefined ? 1 : slice + 1);
  516. return exports.formatTrace(trace);
  517. };
  518. exports.abortThrow = false;
  519. exports.abort = function (message, hideStack) {
  520. if (process.env.NODE_ENV === 'test' || exports.abortThrow === true) {
  521. throw new Error(message || 'Unknown error');
  522. }
  523. let stack = '';
  524. if (!hideStack) {
  525. stack = exports.displayStack(1).join('\n\t');
  526. }
  527. console.log('ABORT: ' + message + '\n\t' + stack);
  528. process.exit(1);
  529. };
  530. exports.assert = function (condition /*, msg1, msg2, msg3 */) {
  531. if (condition) {
  532. return;
  533. }
  534. if (arguments.length === 2 && arguments[1] instanceof Error) {
  535. throw arguments[1];
  536. }
  537. let msgs = [];
  538. for (let i = 1; i < arguments.length; ++i) {
  539. if (arguments[i] !== '') {
  540. msgs.push(arguments[i]); // Avoids Array.slice arguments leak, allowing for V8 optimizations
  541. }
  542. }
  543. msgs = msgs.map((msg) => {
  544. return typeof msg === 'string' ? msg : msg instanceof Error ? msg.message : exports.stringify(msg);
  545. });
  546. throw new Error(msgs.join(' ') || 'Unknown error');
  547. };
  548. exports.Timer = function () {
  549. this.ts = 0;
  550. this.reset();
  551. };
  552. exports.Timer.prototype.reset = function () {
  553. this.ts = Date.now();
  554. };
  555. exports.Timer.prototype.elapsed = function () {
  556. return Date.now() - this.ts;
  557. };
  558. exports.Bench = function () {
  559. this.ts = 0;
  560. this.reset();
  561. };
  562. exports.Bench.prototype.reset = function () {
  563. this.ts = exports.Bench.now();
  564. };
  565. exports.Bench.prototype.elapsed = function () {
  566. return exports.Bench.now() - this.ts;
  567. };
  568. exports.Bench.now = function () {
  569. const ts = process.hrtime();
  570. return (ts[0] * 1e3) + (ts[1] / 1e6);
  571. };
  572. // Escape string for Regex construction
  573. exports.escapeRegex = function (string) {
  574. // Escape ^$.*+-?=!:|\/()[]{},
  575. return string.replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, '\\$&');
  576. };
  577. // Base64url (RFC 4648) encode
  578. exports.base64urlEncode = function (value, encoding) {
  579. exports.assert(typeof value === 'string' || Buffer.isBuffer(value), 'value must be string or buffer');
  580. const buf = (Buffer.isBuffer(value) ? value : new Buffer(value, encoding || 'binary'));
  581. return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
  582. };
  583. // Base64url (RFC 4648) decode
  584. exports.base64urlDecode = function (value, encoding) {
  585. if (typeof value !== 'string') {
  586. return new Error('Value not a string');
  587. }
  588. if (!/^[\w\-]*$/.test(value)) {
  589. return new Error('Invalid character');
  590. }
  591. const buf = new Buffer(value, 'base64');
  592. return (encoding === 'buffer' ? buf : buf.toString(encoding || 'binary'));
  593. };
  594. // Escape attribute value for use in HTTP header
  595. exports.escapeHeaderAttribute = function (attribute) {
  596. // Allowed value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9, \, "
  597. exports.assert(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~\"\\]*$/.test(attribute), 'Bad attribute value (' + attribute + ')');
  598. return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"'); // Escape quotes and slash
  599. };
  600. exports.escapeHtml = function (string) {
  601. return Escape.escapeHtml(string);
  602. };
  603. exports.escapeJavaScript = function (string) {
  604. return Escape.escapeJavaScript(string);
  605. };
  606. exports.escapeJson = function (string) {
  607. return Escape.escapeJson(string);
  608. };
  609. exports.nextTick = function (callback) {
  610. return function () {
  611. const args = arguments;
  612. process.nextTick(() => {
  613. callback.apply(null, args);
  614. });
  615. };
  616. };
  617. exports.once = function (method) {
  618. if (method._hoekOnce) {
  619. return method;
  620. }
  621. let once = false;
  622. const wrapped = function () {
  623. if (!once) {
  624. once = true;
  625. method.apply(null, arguments);
  626. }
  627. };
  628. wrapped._hoekOnce = true;
  629. return wrapped;
  630. };
  631. exports.isInteger = Number.isSafeInteger;
  632. exports.ignore = function () { };
  633. exports.inherits = Util.inherits;
  634. exports.format = Util.format;
  635. exports.transform = function (source, transform, options) {
  636. exports.assert(source === null || source === undefined || typeof source === 'object' || Array.isArray(source), 'Invalid source object: must be null, undefined, an object, or an array');
  637. const separator = (typeof options === 'object' && options !== null) ? (options.separator || '.') : '.';
  638. if (Array.isArray(source)) {
  639. const results = [];
  640. for (let i = 0; i < source.length; ++i) {
  641. results.push(exports.transform(source[i], transform, options));
  642. }
  643. return results;
  644. }
  645. const result = {};
  646. const keys = Object.keys(transform);
  647. for (let i = 0; i < keys.length; ++i) {
  648. const key = keys[i];
  649. const path = key.split(separator);
  650. const sourcePath = transform[key];
  651. exports.assert(typeof sourcePath === 'string', 'All mappings must be "." delineated strings');
  652. let segment;
  653. let res = result;
  654. while (path.length > 1) {
  655. segment = path.shift();
  656. if (!res[segment]) {
  657. res[segment] = {};
  658. }
  659. res = res[segment];
  660. }
  661. segment = path.shift();
  662. res[segment] = exports.reach(source, sourcePath, options);
  663. }
  664. return result;
  665. };
  666. exports.uniqueFilename = function (path, extension) {
  667. if (extension) {
  668. extension = extension[0] !== '.' ? '.' + extension : extension;
  669. }
  670. else {
  671. extension = '';
  672. }
  673. path = Path.resolve(path);
  674. const name = [Date.now(), process.pid, Crypto.randomBytes(8).toString('hex')].join('-') + extension;
  675. return Path.join(path, name);
  676. };
  677. exports.stringify = function () {
  678. try {
  679. return JSON.stringify.apply(null, arguments);
  680. }
  681. catch (err) {
  682. return '[Cannot display object: ' + err.message + ']';
  683. }
  684. };
  685. exports.shallow = function (source) {
  686. const target = {};
  687. const keys = Object.keys(source);
  688. for (let i = 0; i < keys.length; ++i) {
  689. const key = keys[i];
  690. target[key] = source[key];
  691. }
  692. return target;
  693. };