index.cjs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. 'use strict';
  2. /**
  3. * Flatten array, one level deep.
  4. *
  5. * @template T
  6. *
  7. * @param {T[][] | T[] | null} [arr]
  8. *
  9. * @return {T[]}
  10. */
  11. function flatten(arr) {
  12. return Array.prototype.concat.apply([], arr);
  13. }
  14. const nativeToString = Object.prototype.toString;
  15. const nativeHasOwnProperty = Object.prototype.hasOwnProperty;
  16. function isUndefined(obj) {
  17. return obj === undefined;
  18. }
  19. function isDefined(obj) {
  20. return obj !== undefined;
  21. }
  22. function isNil(obj) {
  23. return obj == null;
  24. }
  25. function isArray(obj) {
  26. return nativeToString.call(obj) === '[object Array]';
  27. }
  28. function isObject(obj) {
  29. return nativeToString.call(obj) === '[object Object]';
  30. }
  31. function isNumber(obj) {
  32. return nativeToString.call(obj) === '[object Number]';
  33. }
  34. /**
  35. * @param {any} obj
  36. *
  37. * @return {boolean}
  38. */
  39. function isFunction(obj) {
  40. const tag = nativeToString.call(obj);
  41. return (
  42. tag === '[object Function]' ||
  43. tag === '[object AsyncFunction]' ||
  44. tag === '[object GeneratorFunction]' ||
  45. tag === '[object AsyncGeneratorFunction]' ||
  46. tag === '[object Proxy]'
  47. );
  48. }
  49. function isString(obj) {
  50. return nativeToString.call(obj) === '[object String]';
  51. }
  52. /**
  53. * Ensure collection is an array.
  54. *
  55. * @param {Object} obj
  56. */
  57. function ensureArray(obj) {
  58. if (isArray(obj)) {
  59. return;
  60. }
  61. throw new Error('must supply array');
  62. }
  63. /**
  64. * Return true, if target owns a property with the given key.
  65. *
  66. * @param {Object} target
  67. * @param {String} key
  68. *
  69. * @return {Boolean}
  70. */
  71. function has(target, key) {
  72. return nativeHasOwnProperty.call(target, key);
  73. }
  74. /**
  75. * @template T
  76. * @typedef { (
  77. * ((e: T) => boolean) |
  78. * ((e: T, idx: number) => boolean) |
  79. * ((e: T, key: string) => boolean) |
  80. * string |
  81. * number
  82. * ) } Matcher
  83. */
  84. /**
  85. * @template T
  86. * @template U
  87. *
  88. * @typedef { (
  89. * ((e: T) => U) | string | number
  90. * ) } Extractor
  91. */
  92. /**
  93. * @template T
  94. * @typedef { (val: T, key: any) => boolean } MatchFn
  95. */
  96. /**
  97. * @template T
  98. * @typedef { T[] } ArrayCollection
  99. */
  100. /**
  101. * @template T
  102. * @typedef { { [key: string]: T } } StringKeyValueCollection
  103. */
  104. /**
  105. * @template T
  106. * @typedef { { [key: number]: T } } NumberKeyValueCollection
  107. */
  108. /**
  109. * @template T
  110. * @typedef { StringKeyValueCollection<T> | NumberKeyValueCollection<T> } KeyValueCollection
  111. */
  112. /**
  113. * @template T
  114. * @typedef { KeyValueCollection<T> | ArrayCollection<T> } Collection
  115. */
  116. /**
  117. * Find element in collection.
  118. *
  119. * @template T
  120. * @param {Collection<T>} collection
  121. * @param {Matcher<T>} matcher
  122. *
  123. * @return {Object}
  124. */
  125. function find(collection, matcher) {
  126. const matchFn = toMatcher(matcher);
  127. let match;
  128. forEach(collection, function(val, key) {
  129. if (matchFn(val, key)) {
  130. match = val;
  131. return false;
  132. }
  133. });
  134. return match;
  135. }
  136. /**
  137. * Find element index in collection.
  138. *
  139. * @template T
  140. * @param {Collection<T>} collection
  141. * @param {Matcher<T>} matcher
  142. *
  143. * @return {number}
  144. */
  145. function findIndex(collection, matcher) {
  146. const matchFn = toMatcher(matcher);
  147. let idx = isArray(collection) ? -1 : undefined;
  148. forEach(collection, function(val, key) {
  149. if (matchFn(val, key)) {
  150. idx = key;
  151. return false;
  152. }
  153. });
  154. return idx;
  155. }
  156. /**
  157. * Filter elements in collection.
  158. *
  159. * @template T
  160. * @param {Collection<T>} collection
  161. * @param {Matcher<T>} matcher
  162. *
  163. * @return {T[]} result
  164. */
  165. function filter(collection, matcher) {
  166. const matchFn = toMatcher(matcher);
  167. let result = [];
  168. forEach(collection, function(val, key) {
  169. if (matchFn(val, key)) {
  170. result.push(val);
  171. }
  172. });
  173. return result;
  174. }
  175. /**
  176. * Iterate over collection; returning something
  177. * (non-undefined) will stop iteration.
  178. *
  179. * @template T
  180. * @param {Collection<T>} collection
  181. * @param { ((item: T, idx: number) => (boolean|void)) | ((item: T, key: string) => (boolean|void)) } iterator
  182. *
  183. * @return {T} return result that stopped the iteration
  184. */
  185. function forEach(collection, iterator) {
  186. let val,
  187. result;
  188. if (isUndefined(collection)) {
  189. return;
  190. }
  191. const convertKey = isArray(collection) ? toNum : identity;
  192. for (let key in collection) {
  193. if (has(collection, key)) {
  194. val = collection[key];
  195. result = iterator(val, convertKey(key));
  196. if (result === false) {
  197. return val;
  198. }
  199. }
  200. }
  201. }
  202. /**
  203. * Return collection without element.
  204. *
  205. * @template T
  206. * @param {ArrayCollection<T>} arr
  207. * @param {Matcher<T>} matcher
  208. *
  209. * @return {T[]}
  210. */
  211. function without(arr, matcher) {
  212. if (isUndefined(arr)) {
  213. return [];
  214. }
  215. ensureArray(arr);
  216. const matchFn = toMatcher(matcher);
  217. return arr.filter(function(el, idx) {
  218. return !matchFn(el, idx);
  219. });
  220. }
  221. /**
  222. * Reduce collection, returning a single result.
  223. *
  224. * @template T
  225. * @template V
  226. *
  227. * @param {Collection<T>} collection
  228. * @param {(result: V, entry: T, index: any) => V} iterator
  229. * @param {V} result
  230. *
  231. * @return {V} result returned from last iterator
  232. */
  233. function reduce(collection, iterator, result) {
  234. forEach(collection, function(value, idx) {
  235. result = iterator(result, value, idx);
  236. });
  237. return result;
  238. }
  239. /**
  240. * Return true if every element in the collection
  241. * matches the criteria.
  242. *
  243. * @param {Object|Array} collection
  244. * @param {Function} matcher
  245. *
  246. * @return {Boolean}
  247. */
  248. function every(collection, matcher) {
  249. return !!reduce(collection, function(matches, val, key) {
  250. return matches && matcher(val, key);
  251. }, true);
  252. }
  253. /**
  254. * Return true if some elements in the collection
  255. * match the criteria.
  256. *
  257. * @param {Object|Array} collection
  258. * @param {Function} matcher
  259. *
  260. * @return {Boolean}
  261. */
  262. function some(collection, matcher) {
  263. return !!find(collection, matcher);
  264. }
  265. /**
  266. * Transform a collection into another collection
  267. * by piping each member through the given fn.
  268. *
  269. * @param {Object|Array} collection
  270. * @param {Function} fn
  271. *
  272. * @return {Array} transformed collection
  273. */
  274. function map(collection, fn) {
  275. let result = [];
  276. forEach(collection, function(val, key) {
  277. result.push(fn(val, key));
  278. });
  279. return result;
  280. }
  281. /**
  282. * Get the collections keys.
  283. *
  284. * @param {Object|Array} collection
  285. *
  286. * @return {Array}
  287. */
  288. function keys(collection) {
  289. return collection && Object.keys(collection) || [];
  290. }
  291. /**
  292. * Shorthand for `keys(o).length`.
  293. *
  294. * @param {Object|Array} collection
  295. *
  296. * @return {Number}
  297. */
  298. function size(collection) {
  299. return keys(collection).length;
  300. }
  301. /**
  302. * Get the values in the collection.
  303. *
  304. * @param {Object|Array} collection
  305. *
  306. * @return {Array}
  307. */
  308. function values(collection) {
  309. return map(collection, (val) => val);
  310. }
  311. /**
  312. * Group collection members by attribute.
  313. *
  314. * @param {Object|Array} collection
  315. * @param {Extractor} extractor
  316. *
  317. * @return {Object} map with { attrValue => [ a, b, c ] }
  318. */
  319. function groupBy(collection, extractor, grouped = {}) {
  320. extractor = toExtractor(extractor);
  321. forEach(collection, function(val) {
  322. let discriminator = extractor(val) || '_';
  323. let group = grouped[discriminator];
  324. if (!group) {
  325. group = grouped[discriminator] = [];
  326. }
  327. group.push(val);
  328. });
  329. return grouped;
  330. }
  331. function uniqueBy(extractor, ...collections) {
  332. extractor = toExtractor(extractor);
  333. let grouped = {};
  334. forEach(collections, (c) => groupBy(c, extractor, grouped));
  335. let result = map(grouped, function(val, key) {
  336. return val[0];
  337. });
  338. return result;
  339. }
  340. const unionBy = uniqueBy;
  341. /**
  342. * Sort collection by criteria.
  343. *
  344. * @template T
  345. *
  346. * @param {Collection<T>} collection
  347. * @param {Extractor<T, number | string>} extractor
  348. *
  349. * @return {Array}
  350. */
  351. function sortBy(collection, extractor) {
  352. extractor = toExtractor(extractor);
  353. let sorted = [];
  354. forEach(collection, function(value, key) {
  355. let disc = extractor(value, key);
  356. let entry = {
  357. d: disc,
  358. v: value
  359. };
  360. for (var idx = 0; idx < sorted.length; idx++) {
  361. let { d } = sorted[idx];
  362. if (disc < d) {
  363. sorted.splice(idx, 0, entry);
  364. return;
  365. }
  366. }
  367. // not inserted, append (!)
  368. sorted.push(entry);
  369. });
  370. return map(sorted, (e) => e.v);
  371. }
  372. /**
  373. * Create an object pattern matcher.
  374. *
  375. * @example
  376. *
  377. * ```javascript
  378. * const matcher = matchPattern({ id: 1 });
  379. *
  380. * let element = find(elements, matcher);
  381. * ```
  382. *
  383. * @template T
  384. *
  385. * @param {T} pattern
  386. *
  387. * @return { (el: any) => boolean } matcherFn
  388. */
  389. function matchPattern(pattern) {
  390. return function(el) {
  391. return every(pattern, function(val, key) {
  392. return el[key] === val;
  393. });
  394. };
  395. }
  396. /**
  397. * @param {string | ((e: any) => any) } extractor
  398. *
  399. * @return { (e: any) => any }
  400. */
  401. function toExtractor(extractor) {
  402. /**
  403. * @satisfies { (e: any) => any }
  404. */
  405. return isFunction(extractor) ? extractor : (e) => {
  406. // @ts-ignore: just works
  407. return e[extractor];
  408. };
  409. }
  410. /**
  411. * @template T
  412. * @param {Matcher<T>} matcher
  413. *
  414. * @return {MatchFn<T>}
  415. */
  416. function toMatcher(matcher) {
  417. return isFunction(matcher) ? matcher : (e) => {
  418. return e === matcher;
  419. };
  420. }
  421. function identity(arg) {
  422. return arg;
  423. }
  424. function toNum(arg) {
  425. return Number(arg);
  426. }
  427. /* global setTimeout clearTimeout */
  428. /**
  429. * @typedef { {
  430. * (...args: any[]): any;
  431. * flush: () => void;
  432. * cancel: () => void;
  433. * } } DebouncedFunction
  434. */
  435. /**
  436. * Debounce fn, calling it only once if the given time
  437. * elapsed between calls.
  438. *
  439. * Lodash-style the function exposes methods to `#clear`
  440. * and `#flush` to control internal behavior.
  441. *
  442. * @param {Function} fn
  443. * @param {Number} timeout
  444. *
  445. * @return {DebouncedFunction} debounced function
  446. */
  447. function debounce(fn, timeout) {
  448. let timer;
  449. let lastArgs;
  450. let lastThis;
  451. let lastNow;
  452. function fire(force) {
  453. let now = Date.now();
  454. let scheduledDiff = force ? 0 : (lastNow + timeout) - now;
  455. if (scheduledDiff > 0) {
  456. return schedule(scheduledDiff);
  457. }
  458. fn.apply(lastThis, lastArgs);
  459. clear();
  460. }
  461. function schedule(timeout) {
  462. timer = setTimeout(fire, timeout);
  463. }
  464. function clear() {
  465. if (timer) {
  466. clearTimeout(timer);
  467. }
  468. timer = lastNow = lastArgs = lastThis = undefined;
  469. }
  470. function flush() {
  471. if (timer) {
  472. fire(true);
  473. }
  474. clear();
  475. }
  476. /**
  477. * @type { DebouncedFunction }
  478. */
  479. function callback(...args) {
  480. lastNow = Date.now();
  481. lastArgs = args;
  482. lastThis = this;
  483. // ensure an execution is scheduled
  484. if (!timer) {
  485. schedule(timeout);
  486. }
  487. }
  488. callback.flush = flush;
  489. callback.cancel = clear;
  490. return callback;
  491. }
  492. /**
  493. * Throttle fn, calling at most once
  494. * in the given interval.
  495. *
  496. * @param {Function} fn
  497. * @param {Number} interval
  498. *
  499. * @return {Function} throttled function
  500. */
  501. function throttle(fn, interval) {
  502. let throttling = false;
  503. return function(...args) {
  504. if (throttling) {
  505. return;
  506. }
  507. fn(...args);
  508. throttling = true;
  509. setTimeout(() => {
  510. throttling = false;
  511. }, interval);
  512. };
  513. }
  514. /**
  515. * Bind function against target <this>.
  516. *
  517. * @param {Function} fn
  518. * @param {Object} target
  519. *
  520. * @return {Function} bound function
  521. */
  522. function bind(fn, target) {
  523. return fn.bind(target);
  524. }
  525. /**
  526. * Convenience wrapper for `Object.assign`.
  527. *
  528. * @param {Object} target
  529. * @param {...Object} others
  530. *
  531. * @return {Object} the target
  532. */
  533. function assign(target, ...others) {
  534. return Object.assign(target, ...others);
  535. }
  536. /**
  537. * Sets a nested property of a given object to the specified value.
  538. *
  539. * This mutates the object and returns it.
  540. *
  541. * @template T
  542. *
  543. * @param {T} target The target of the set operation.
  544. * @param {(string|number)[]} path The path to the nested value.
  545. * @param {any} value The value to set.
  546. *
  547. * @return {T}
  548. */
  549. function set(target, path, value) {
  550. let currentTarget = target;
  551. forEach(path, function(key, idx) {
  552. if (typeof key !== 'number' && typeof key !== 'string') {
  553. throw new Error('illegal key type: ' + typeof key + '. Key should be of type number or string.');
  554. }
  555. if (key === 'constructor') {
  556. throw new Error('illegal key: constructor');
  557. }
  558. if (key === '__proto__') {
  559. throw new Error('illegal key: __proto__');
  560. }
  561. let nextKey = path[idx + 1];
  562. let nextTarget = currentTarget[key];
  563. if (isDefined(nextKey) && isNil(nextTarget)) {
  564. nextTarget = currentTarget[key] = isNaN(+nextKey) ? {} : [];
  565. }
  566. if (isUndefined(nextKey)) {
  567. if (isUndefined(value)) {
  568. delete currentTarget[key];
  569. } else {
  570. currentTarget[key] = value;
  571. }
  572. } else {
  573. currentTarget = nextTarget;
  574. }
  575. });
  576. return target;
  577. }
  578. /**
  579. * Gets a nested property of a given object.
  580. *
  581. * @param {Object} target The target of the get operation.
  582. * @param {(string|number)[]} path The path to the nested value.
  583. * @param {any} [defaultValue] The value to return if no value exists.
  584. *
  585. * @return {any}
  586. */
  587. function get(target, path, defaultValue) {
  588. let currentTarget = target;
  589. forEach(path, function(key) {
  590. // accessing nil property yields <undefined>
  591. if (isNil(currentTarget)) {
  592. currentTarget = undefined;
  593. return false;
  594. }
  595. currentTarget = currentTarget[key];
  596. });
  597. return isUndefined(currentTarget) ? defaultValue : currentTarget;
  598. }
  599. /**
  600. * Pick properties from the given target.
  601. *
  602. * @template T
  603. * @template {any[]} V
  604. *
  605. * @param {T} target
  606. * @param {V} properties
  607. *
  608. * @return Pick<T, V>
  609. */
  610. function pick(target, properties) {
  611. let result = {};
  612. let obj = Object(target);
  613. forEach(properties, function(prop) {
  614. if (prop in obj) {
  615. result[prop] = target[prop];
  616. }
  617. });
  618. return result;
  619. }
  620. /**
  621. * Pick all target properties, excluding the given ones.
  622. *
  623. * @template T
  624. * @template {any[]} V
  625. *
  626. * @param {T} target
  627. * @param {V} properties
  628. *
  629. * @return {Omit<T, V>} target
  630. */
  631. function omit(target, properties) {
  632. let result = {};
  633. let obj = Object(target);
  634. forEach(obj, function(prop, key) {
  635. if (properties.indexOf(key) === -1) {
  636. result[key] = prop;
  637. }
  638. });
  639. return result;
  640. }
  641. /**
  642. * Recursively merge `...sources` into given target.
  643. *
  644. * Does support merging objects; does not support merging arrays.
  645. *
  646. * @param {Object} target
  647. * @param {...Object} sources
  648. *
  649. * @return {Object} the target
  650. */
  651. function merge(target, ...sources) {
  652. if (!sources.length) {
  653. return target;
  654. }
  655. forEach(sources, function(source) {
  656. // skip non-obj sources, i.e. null
  657. if (!source || !isObject(source)) {
  658. return;
  659. }
  660. forEach(source, function(sourceVal, key) {
  661. if (key === '__proto__') {
  662. return;
  663. }
  664. let targetVal = target[key];
  665. if (isObject(sourceVal)) {
  666. if (!isObject(targetVal)) {
  667. // override target[key] with object
  668. targetVal = {};
  669. }
  670. target[key] = merge(targetVal, sourceVal);
  671. } else {
  672. target[key] = sourceVal;
  673. }
  674. });
  675. });
  676. return target;
  677. }
  678. exports.assign = assign;
  679. exports.bind = bind;
  680. exports.debounce = debounce;
  681. exports.ensureArray = ensureArray;
  682. exports.every = every;
  683. exports.filter = filter;
  684. exports.find = find;
  685. exports.findIndex = findIndex;
  686. exports.flatten = flatten;
  687. exports.forEach = forEach;
  688. exports.get = get;
  689. exports.groupBy = groupBy;
  690. exports.has = has;
  691. exports.isArray = isArray;
  692. exports.isDefined = isDefined;
  693. exports.isFunction = isFunction;
  694. exports.isNil = isNil;
  695. exports.isNumber = isNumber;
  696. exports.isObject = isObject;
  697. exports.isString = isString;
  698. exports.isUndefined = isUndefined;
  699. exports.keys = keys;
  700. exports.map = map;
  701. exports.matchPattern = matchPattern;
  702. exports.merge = merge;
  703. exports.omit = omit;
  704. exports.pick = pick;
  705. exports.reduce = reduce;
  706. exports.set = set;
  707. exports.size = size;
  708. exports.some = some;
  709. exports.sortBy = sortBy;
  710. exports.throttle = throttle;
  711. exports.unionBy = unionBy;
  712. exports.uniqueBy = uniqueBy;
  713. exports.values = values;
  714. exports.without = without;