cleverMerge.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /** @type {WeakMap<EXPECTED_OBJECT, WeakMap<EXPECTED_OBJECT, EXPECTED_OBJECT>>} */
  7. const mergeCache = new WeakMap();
  8. /** @type {WeakMap<EXPECTED_OBJECT, Map<string, Map<string | number | boolean, EXPECTED_OBJECT>>>} */
  9. const setPropertyCache = new WeakMap();
  10. const DELETE = Symbol("DELETE");
  11. const DYNAMIC_INFO = Symbol("cleverMerge dynamic info");
  12. /**
  13. * Merges two given objects and caches the result to avoid computation if same objects passed as arguments again.
  14. * @template T
  15. * @template O
  16. * @example
  17. * // performs cleverMerge(first, second), stores the result in WeakMap and returns result
  18. * cachedCleverMerge({a: 1}, {a: 2})
  19. * {a: 2}
  20. * // when same arguments passed, gets the result from WeakMap and returns it.
  21. * cachedCleverMerge({a: 1}, {a: 2})
  22. * {a: 2}
  23. * @param {T | null | undefined} first first object
  24. * @param {O | null | undefined} second second object
  25. * @returns {T & O | T | O} merged object of first and second object
  26. */
  27. const cachedCleverMerge = (first, second) => {
  28. if (second === undefined) return /** @type {T} */ (first);
  29. if (first === undefined) return /** @type {O} */ (second);
  30. if (typeof second !== "object" || second === null) {
  31. return /** @type {O} */ (second);
  32. }
  33. if (typeof first !== "object" || first === null) {
  34. return /** @type {T} */ (first);
  35. }
  36. let innerCache = mergeCache.get(first);
  37. if (innerCache === undefined) {
  38. innerCache = new WeakMap();
  39. mergeCache.set(first, innerCache);
  40. }
  41. const prevMerge = /** @type {T & O} */ (innerCache.get(second));
  42. if (prevMerge !== undefined) return prevMerge;
  43. const newMerge = _cleverMerge(first, second, true);
  44. innerCache.set(second, newMerge);
  45. return newMerge;
  46. };
  47. /**
  48. * @template T
  49. * @param {Partial<T>} obj object
  50. * @param {string} property property
  51. * @param {string | number | boolean} value assignment value
  52. * @returns {T} new object
  53. */
  54. const cachedSetProperty = (obj, property, value) => {
  55. let mapByProperty = setPropertyCache.get(obj);
  56. if (mapByProperty === undefined) {
  57. mapByProperty = new Map();
  58. setPropertyCache.set(obj, mapByProperty);
  59. }
  60. let mapByValue = mapByProperty.get(property);
  61. if (mapByValue === undefined) {
  62. mapByValue = new Map();
  63. mapByProperty.set(property, mapByValue);
  64. }
  65. let result = mapByValue.get(value);
  66. if (result) return /** @type {T} */ (result);
  67. result = {
  68. ...obj,
  69. [property]: value
  70. };
  71. mapByValue.set(value, result);
  72. return /** @type {T} */ (result);
  73. };
  74. /**
  75. * @typedef {Map<string, EXPECTED_ANY>} ByValues
  76. */
  77. /**
  78. * @template T
  79. * @typedef {object} ObjectParsedPropertyEntry
  80. * @property {T[keyof T] | undefined} base base value
  81. * @property {string | undefined} byProperty the name of the selector property
  82. * @property {ByValues | undefined} byValues value depending on selector property, merged with base
  83. */
  84. /** @typedef {(function(...EXPECTED_ANY): object) & { [DYNAMIC_INFO]: [DynamicFunction, object] }} DynamicFunction */
  85. /**
  86. * @template {object} T
  87. * @typedef {Map<keyof T, ObjectParsedPropertyEntry<T>>} ParsedObjectStatic
  88. */
  89. /**
  90. * @template {object} T
  91. * @typedef {{ byProperty: string, fn: DynamicFunction }} ParsedObjectDynamic
  92. */
  93. /**
  94. * @template {object} T
  95. * @typedef {object} ParsedObject
  96. * @property {ParsedObjectStatic<T>} static static properties (key is property name)
  97. * @property {ParsedObjectDynamic<T> | undefined} dynamic dynamic part
  98. */
  99. /** @type {WeakMap<EXPECTED_OBJECT, ParsedObject<EXPECTED_ANY>>} */
  100. const parseCache = new WeakMap();
  101. /**
  102. * @template {object} T
  103. * @param {T} obj the object
  104. * @returns {ParsedObject<T>} parsed object
  105. */
  106. const cachedParseObject = obj => {
  107. const entry = parseCache.get(/** @type {EXPECTED_OBJECT} */ (obj));
  108. if (entry !== undefined) return entry;
  109. const result = parseObject(obj);
  110. parseCache.set(/** @type {EXPECTED_OBJECT} */ (obj), result);
  111. return result;
  112. };
  113. /** @typedef {{ [p: string]: { [p: string]: EXPECTED_ANY } } | DynamicFunction} ByObject */
  114. /**
  115. * @template {object} T
  116. * @param {T} obj the object
  117. * @returns {ParsedObject<T>} parsed object
  118. */
  119. const parseObject = obj => {
  120. /** @type {ParsedObjectStatic<T>} */
  121. const info = new Map();
  122. /** @type {ParsedObjectDynamic<T> | undefined} */
  123. let dynamicInfo;
  124. /**
  125. * @param {keyof T} p path
  126. * @returns {Partial<ObjectParsedPropertyEntry<T>>} object parsed property entry
  127. */
  128. const getInfo = p => {
  129. const entry = info.get(p);
  130. if (entry !== undefined) return entry;
  131. const newEntry = {
  132. base: undefined,
  133. byProperty: undefined,
  134. byValues: undefined
  135. };
  136. info.set(p, newEntry);
  137. return newEntry;
  138. };
  139. for (const key_ of Object.keys(obj)) {
  140. const key = /** @type {keyof T} */ (key_);
  141. if (typeof key === "string" && key.startsWith("by")) {
  142. const byProperty = key;
  143. const byObj = /** @type {ByObject} */ (obj[byProperty]);
  144. if (typeof byObj === "object") {
  145. for (const byValue of Object.keys(byObj)) {
  146. const obj = byObj[/** @type {keyof (keyof T)} */ (byValue)];
  147. for (const key of Object.keys(obj)) {
  148. const entry = getInfo(/** @type {keyof T} */ (key));
  149. if (entry.byProperty === undefined) {
  150. entry.byProperty = byProperty;
  151. entry.byValues = new Map();
  152. } else if (entry.byProperty !== byProperty) {
  153. throw new Error(
  154. `${/** @type {string} */ (byProperty)} and ${entry.byProperty} for a single property is not supported`
  155. );
  156. }
  157. /** @type {ByValues} */
  158. (entry.byValues).set(byValue, obj[key]);
  159. if (byValue === "default") {
  160. for (const otherByValue of Object.keys(byObj)) {
  161. if (
  162. !(
  163. /** @type {ByValues} */
  164. (entry.byValues).has(otherByValue)
  165. )
  166. ) {
  167. /** @type {ByValues} */
  168. (entry.byValues).set(otherByValue, undefined);
  169. }
  170. }
  171. }
  172. }
  173. }
  174. } else if (typeof byObj === "function") {
  175. if (dynamicInfo === undefined) {
  176. dynamicInfo = {
  177. byProperty: key,
  178. fn: byObj
  179. };
  180. } else {
  181. throw new Error(
  182. `${key} and ${dynamicInfo.byProperty} when both are functions is not supported`
  183. );
  184. }
  185. } else {
  186. const entry = getInfo(key);
  187. entry.base = obj[key];
  188. }
  189. } else {
  190. const entry = getInfo(key);
  191. entry.base = obj[key];
  192. }
  193. }
  194. return {
  195. static: info,
  196. dynamic: dynamicInfo
  197. };
  198. };
  199. /**
  200. * @template {object} T
  201. * @param {ParsedObjectStatic<T>} info static properties (key is property name)
  202. * @param {{ byProperty: string, fn: DynamicFunction } | undefined} dynamicInfo dynamic part
  203. * @returns {T} the object
  204. */
  205. const serializeObject = (info, dynamicInfo) => {
  206. const obj = /** @type {T} */ ({});
  207. // Setup byProperty structure
  208. for (const entry of info.values()) {
  209. if (entry.byProperty !== undefined) {
  210. const byProperty = /** @type {keyof T} */ (entry.byProperty);
  211. const byObj = (obj[byProperty] =
  212. obj[byProperty] || /** @type {TODO} */ ({}));
  213. for (const byValue of /** @type {ByValues} */ (entry.byValues).keys()) {
  214. byObj[byValue] = byObj[byValue] || {};
  215. }
  216. }
  217. }
  218. for (const [key, entry] of info) {
  219. if (entry.base !== undefined) {
  220. obj[/** @type {keyof T} */ (key)] = entry.base;
  221. }
  222. // Fill byProperty structure
  223. if (entry.byProperty !== undefined) {
  224. const byProperty = /** @type {keyof T} */ (entry.byProperty);
  225. const byObj = (obj[byProperty] =
  226. obj[byProperty] || /** @type {TODO} */ ({}));
  227. for (const byValue of Object.keys(byObj)) {
  228. const value = getFromByValues(
  229. /** @type {ByValues} */
  230. (entry.byValues),
  231. byValue
  232. );
  233. if (value !== undefined) byObj[byValue][key] = value;
  234. }
  235. }
  236. }
  237. if (dynamicInfo !== undefined) {
  238. /** @type {TODO} */
  239. (obj)[dynamicInfo.byProperty] = dynamicInfo.fn;
  240. }
  241. return obj;
  242. };
  243. const VALUE_TYPE_UNDEFINED = 0;
  244. const VALUE_TYPE_ATOM = 1;
  245. const VALUE_TYPE_ARRAY_EXTEND = 2;
  246. const VALUE_TYPE_OBJECT = 3;
  247. const VALUE_TYPE_DELETE = 4;
  248. /**
  249. * @template T
  250. * @param {T} value a single value
  251. * @returns {VALUE_TYPE_UNDEFINED | VALUE_TYPE_ATOM | VALUE_TYPE_ARRAY_EXTEND | VALUE_TYPE_OBJECT | VALUE_TYPE_DELETE} value type
  252. */
  253. const getValueType = value => {
  254. if (value === undefined) {
  255. return VALUE_TYPE_UNDEFINED;
  256. } else if (value === DELETE) {
  257. return VALUE_TYPE_DELETE;
  258. } else if (Array.isArray(value)) {
  259. if (value.includes("...")) return VALUE_TYPE_ARRAY_EXTEND;
  260. return VALUE_TYPE_ATOM;
  261. } else if (
  262. typeof value === "object" &&
  263. value !== null &&
  264. (!value.constructor || value.constructor === Object)
  265. ) {
  266. return VALUE_TYPE_OBJECT;
  267. }
  268. return VALUE_TYPE_ATOM;
  269. };
  270. /**
  271. * Merges two objects. Objects are deeply clever merged.
  272. * Arrays might reference the old value with "...".
  273. * Non-object values take preference over object values.
  274. * @template T
  275. * @template O
  276. * @param {T} first first object
  277. * @param {O} second second object
  278. * @returns {T & O | T | O} merged object of first and second object
  279. */
  280. const cleverMerge = (first, second) => {
  281. if (second === undefined) return first;
  282. if (first === undefined) return second;
  283. if (typeof second !== "object" || second === null) return second;
  284. if (typeof first !== "object" || first === null) return first;
  285. return /** @type {T & O} */ (_cleverMerge(first, second, false));
  286. };
  287. /**
  288. * @template {object} T
  289. * @template {object} O
  290. * Merges two objects. Objects are deeply clever merged.
  291. * @param {T} first first
  292. * @param {O} second second
  293. * @param {boolean} internalCaching should parsing of objects and nested merges be cached
  294. * @returns {T & O} merged object of first and second object
  295. */
  296. const _cleverMerge = (first, second, internalCaching = false) => {
  297. const firstObject = internalCaching
  298. ? cachedParseObject(first)
  299. : parseObject(first);
  300. const { static: firstInfo, dynamic: firstDynamicInfo } = firstObject;
  301. // If the first argument has a dynamic part we modify the dynamic part to merge the second argument
  302. if (firstDynamicInfo !== undefined) {
  303. let { byProperty, fn } = firstDynamicInfo;
  304. const fnInfo = fn[DYNAMIC_INFO];
  305. if (fnInfo) {
  306. second =
  307. /** @type {O} */
  308. (
  309. internalCaching
  310. ? cachedCleverMerge(fnInfo[1], second)
  311. : cleverMerge(fnInfo[1], second)
  312. );
  313. fn = fnInfo[0];
  314. }
  315. /** @type {DynamicFunction} */
  316. const newFn = (...args) => {
  317. const fnResult = fn(...args);
  318. return internalCaching
  319. ? cachedCleverMerge(fnResult, second)
  320. : cleverMerge(fnResult, second);
  321. };
  322. newFn[DYNAMIC_INFO] = [fn, second];
  323. return /** @type {T & O} */ (
  324. serializeObject(firstObject.static, { byProperty, fn: newFn })
  325. );
  326. }
  327. // If the first part is static only, we merge the static parts and keep the dynamic part of the second argument
  328. const secondObject = internalCaching
  329. ? cachedParseObject(second)
  330. : parseObject(second);
  331. const { static: secondInfo, dynamic: secondDynamicInfo } = secondObject;
  332. const resultInfo = new Map();
  333. for (const [key, firstEntry] of firstInfo) {
  334. const secondEntry = secondInfo.get(
  335. /** @type {keyof (T | O)} */
  336. (key)
  337. );
  338. const entry =
  339. secondEntry !== undefined
  340. ? mergeEntries(firstEntry, secondEntry, internalCaching)
  341. : firstEntry;
  342. resultInfo.set(key, entry);
  343. }
  344. for (const [key, secondEntry] of secondInfo) {
  345. if (!firstInfo.has(/** @type {keyof (T | O)} */ (key))) {
  346. resultInfo.set(key, secondEntry);
  347. }
  348. }
  349. return /** @type {T & O} */ (serializeObject(resultInfo, secondDynamicInfo));
  350. };
  351. /**
  352. * @template T, O
  353. * @param {ObjectParsedPropertyEntry<T>} firstEntry a
  354. * @param {ObjectParsedPropertyEntry<O>} secondEntry b
  355. * @param {boolean} internalCaching should parsing of objects and nested merges be cached
  356. * @returns {ObjectParsedPropertyEntry<TODO>} new entry
  357. */
  358. const mergeEntries = (firstEntry, secondEntry, internalCaching) => {
  359. switch (getValueType(secondEntry.base)) {
  360. case VALUE_TYPE_ATOM:
  361. case VALUE_TYPE_DELETE:
  362. // No need to consider firstEntry at all
  363. // second value override everything
  364. // = second.base + second.byProperty
  365. return secondEntry;
  366. case VALUE_TYPE_UNDEFINED:
  367. if (!firstEntry.byProperty) {
  368. // = first.base + second.byProperty
  369. return {
  370. base: firstEntry.base,
  371. byProperty: secondEntry.byProperty,
  372. byValues: secondEntry.byValues
  373. };
  374. } else if (firstEntry.byProperty !== secondEntry.byProperty) {
  375. throw new Error(
  376. `${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
  377. );
  378. } else {
  379. // = first.base + (first.byProperty + second.byProperty)
  380. // need to merge first and second byValues
  381. const newByValues = new Map(firstEntry.byValues);
  382. for (const [key, value] of /** @type {ByValues} */ (
  383. secondEntry.byValues
  384. )) {
  385. const firstValue = getFromByValues(
  386. /** @type {ByValues} */
  387. (firstEntry.byValues),
  388. key
  389. );
  390. newByValues.set(
  391. key,
  392. mergeSingleValue(firstValue, value, internalCaching)
  393. );
  394. }
  395. return {
  396. base: firstEntry.base,
  397. byProperty: firstEntry.byProperty,
  398. byValues: newByValues
  399. };
  400. }
  401. default: {
  402. if (!firstEntry.byProperty) {
  403. // The simple case
  404. // = (first.base + second.base) + second.byProperty
  405. return {
  406. base:
  407. /** @type {T[keyof T] & O[keyof O]} */
  408. (
  409. mergeSingleValue(
  410. firstEntry.base,
  411. secondEntry.base,
  412. internalCaching
  413. )
  414. ),
  415. byProperty: secondEntry.byProperty,
  416. byValues: secondEntry.byValues
  417. };
  418. }
  419. let newBase;
  420. const intermediateByValues = new Map(firstEntry.byValues);
  421. for (const [key, value] of intermediateByValues) {
  422. intermediateByValues.set(
  423. key,
  424. mergeSingleValue(value, secondEntry.base, internalCaching)
  425. );
  426. }
  427. if (
  428. [.../** @type {ByValues} */ (firstEntry.byValues).values()].every(
  429. value => {
  430. const type = getValueType(value);
  431. return type === VALUE_TYPE_ATOM || type === VALUE_TYPE_DELETE;
  432. }
  433. )
  434. ) {
  435. // = (first.base + second.base) + ((first.byProperty + second.base) + second.byProperty)
  436. newBase = mergeSingleValue(
  437. firstEntry.base,
  438. secondEntry.base,
  439. internalCaching
  440. );
  441. } else {
  442. // = first.base + ((first.byProperty (+default) + second.base) + second.byProperty)
  443. newBase = firstEntry.base;
  444. if (!intermediateByValues.has("default")) {
  445. intermediateByValues.set("default", secondEntry.base);
  446. }
  447. }
  448. if (!secondEntry.byProperty) {
  449. // = first.base + (first.byProperty + second.base)
  450. return {
  451. base: newBase,
  452. byProperty: firstEntry.byProperty,
  453. byValues: intermediateByValues
  454. };
  455. } else if (firstEntry.byProperty !== secondEntry.byProperty) {
  456. throw new Error(
  457. `${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
  458. );
  459. }
  460. const newByValues = new Map(intermediateByValues);
  461. for (const [key, value] of /** @type {ByValues} */ (
  462. secondEntry.byValues
  463. )) {
  464. const firstValue = getFromByValues(intermediateByValues, key);
  465. newByValues.set(
  466. key,
  467. mergeSingleValue(firstValue, value, internalCaching)
  468. );
  469. }
  470. return {
  471. base: newBase,
  472. byProperty: firstEntry.byProperty,
  473. byValues: newByValues
  474. };
  475. }
  476. }
  477. };
  478. /**
  479. * @template V
  480. * @param {ByValues} byValues all values
  481. * @param {string} key value of the selector
  482. * @returns {V | undefined} value
  483. */
  484. const getFromByValues = (byValues, key) => {
  485. if (key !== "default" && byValues.has(key)) {
  486. return byValues.get(key);
  487. }
  488. return byValues.get("default");
  489. };
  490. /**
  491. * @template A
  492. * @template B
  493. * @param {A | A[]} a value
  494. * @param {B | B[]} b value
  495. * @param {boolean} internalCaching should parsing of objects and nested merges be cached
  496. * @returns {A & B | (A | B)[] | A | A[] | B | B[]} value
  497. */
  498. const mergeSingleValue = (a, b, internalCaching) => {
  499. const bType = getValueType(b);
  500. const aType = getValueType(a);
  501. switch (bType) {
  502. case VALUE_TYPE_DELETE:
  503. case VALUE_TYPE_ATOM:
  504. return b;
  505. case VALUE_TYPE_OBJECT: {
  506. return aType !== VALUE_TYPE_OBJECT
  507. ? b
  508. : internalCaching
  509. ? cachedCleverMerge(a, b)
  510. : cleverMerge(a, b);
  511. }
  512. case VALUE_TYPE_UNDEFINED:
  513. return a;
  514. case VALUE_TYPE_ARRAY_EXTEND:
  515. switch (
  516. aType !== VALUE_TYPE_ATOM
  517. ? aType
  518. : Array.isArray(a)
  519. ? VALUE_TYPE_ARRAY_EXTEND
  520. : VALUE_TYPE_OBJECT
  521. ) {
  522. case VALUE_TYPE_UNDEFINED:
  523. return b;
  524. case VALUE_TYPE_DELETE:
  525. return /** @type {B[]} */ (b).filter(item => item !== "...");
  526. case VALUE_TYPE_ARRAY_EXTEND: {
  527. /** @type {(A | B)[]} */
  528. const newArray = [];
  529. for (const item of /** @type {B[]} */ (b)) {
  530. if (item === "...") {
  531. for (const item of /** @type {A[]} */ (a)) {
  532. newArray.push(item);
  533. }
  534. } else {
  535. newArray.push(item);
  536. }
  537. }
  538. return newArray;
  539. }
  540. case VALUE_TYPE_OBJECT:
  541. return /** @type {(A | B)[]} */ (b).map(item =>
  542. item === "..." ? /** @type {A} */ (a) : item
  543. );
  544. default:
  545. throw new Error("Not implemented");
  546. }
  547. default:
  548. throw new Error("Not implemented");
  549. }
  550. };
  551. /**
  552. * @template {object} T
  553. * @param {T} obj the object
  554. * @param {(keyof T)[]=} keysToKeepOriginalValue keys to keep original value
  555. * @returns {T} the object without operations like "..." or DELETE
  556. */
  557. const removeOperations = (obj, keysToKeepOriginalValue = []) => {
  558. const newObj = /** @type {T} */ ({});
  559. for (const _key of Object.keys(obj)) {
  560. const key = /** @type {keyof T} */ (_key);
  561. const value = obj[key];
  562. const type = getValueType(value);
  563. if (type === VALUE_TYPE_OBJECT && keysToKeepOriginalValue.includes(key)) {
  564. newObj[key] = value;
  565. continue;
  566. }
  567. switch (type) {
  568. case VALUE_TYPE_UNDEFINED:
  569. case VALUE_TYPE_DELETE:
  570. break;
  571. case VALUE_TYPE_OBJECT:
  572. newObj[key] =
  573. /** @type {T[keyof T]} */
  574. (
  575. removeOperations(
  576. /** @type {T} */
  577. (value),
  578. keysToKeepOriginalValue
  579. )
  580. );
  581. break;
  582. case VALUE_TYPE_ARRAY_EXTEND:
  583. newObj[key] =
  584. /** @type {T[keyof T]} */
  585. (
  586. /** @type {EXPECTED_ANY[]} */
  587. (value).filter(i => i !== "...")
  588. );
  589. break;
  590. default:
  591. newObj[key] = value;
  592. break;
  593. }
  594. }
  595. return newObj;
  596. };
  597. /**
  598. * @template T
  599. * @template {keyof T} P
  600. * @template V
  601. * @param {T} obj the object
  602. * @param {P} byProperty the by description
  603. * @param {...V} values values
  604. * @returns {Omit<T, P>} object with merged byProperty
  605. */
  606. const resolveByProperty = (obj, byProperty, ...values) => {
  607. if (typeof obj !== "object" || obj === null || !(byProperty in obj)) {
  608. return obj;
  609. }
  610. const { [byProperty]: _byValue, ..._remaining } = obj;
  611. const remaining = /** @type {T} */ (_remaining);
  612. const byValue =
  613. /** @type {Record<string, T> | ((...args: V[]) => T)} */
  614. (_byValue);
  615. if (typeof byValue === "object") {
  616. const key = /** @type {string} */ (values[0]);
  617. if (key in byValue) {
  618. return cachedCleverMerge(remaining, byValue[key]);
  619. } else if ("default" in byValue) {
  620. return cachedCleverMerge(remaining, byValue.default);
  621. }
  622. return remaining;
  623. } else if (typeof byValue === "function") {
  624. // eslint-disable-next-line prefer-spread
  625. const result = byValue.apply(null, values);
  626. return cachedCleverMerge(
  627. remaining,
  628. resolveByProperty(result, byProperty, ...values)
  629. );
  630. }
  631. return obj;
  632. };
  633. module.exports.DELETE = DELETE;
  634. module.exports.cachedCleverMerge = cachedCleverMerge;
  635. module.exports.cachedSetProperty = cachedSetProperty;
  636. module.exports.cleverMerge = cleverMerge;
  637. module.exports.removeOperations = removeOperations;
  638. module.exports.resolveByProperty = resolveByProperty;