Feat: Multiplayer sessions added using CRUD database.
This commit is contained in:
676
node_modules/webpack/lib/util/cleverMerge.js
generated
vendored
Normal file
676
node_modules/webpack/lib/util/cleverMerge.js
generated
vendored
Normal file
@@ -0,0 +1,676 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @type {WeakMap<EXPECTED_OBJECT, WeakMap<EXPECTED_OBJECT, EXPECTED_OBJECT>>} */
|
||||
const mergeCache = new WeakMap();
|
||||
/** @typedef {Map<string, Map<string | number | boolean, EXPECTED_OBJECT>>} InnerPropertyCache */
|
||||
/** @type {WeakMap<EXPECTED_OBJECT, InnerPropertyCache>} */
|
||||
const setPropertyCache = new WeakMap();
|
||||
const DELETE = Symbol("DELETE");
|
||||
const DYNAMIC_INFO = Symbol("cleverMerge dynamic info");
|
||||
|
||||
/**
|
||||
* Merges two given objects and caches the result to avoid computation if same objects passed as arguments again.
|
||||
* @template T
|
||||
* @template O
|
||||
* @example
|
||||
* // performs cleverMerge(first, second), stores the result in WeakMap and returns result
|
||||
* cachedCleverMerge({a: 1}, {a: 2})
|
||||
* {a: 2}
|
||||
* // when same arguments passed, gets the result from WeakMap and returns it.
|
||||
* cachedCleverMerge({a: 1}, {a: 2})
|
||||
* {a: 2}
|
||||
* @param {T | null | undefined} first first object
|
||||
* @param {O | null | undefined} second second object
|
||||
* @returns {T & O | T | O} merged object of first and second object
|
||||
*/
|
||||
const cachedCleverMerge = (first, second) => {
|
||||
if (second === undefined) return /** @type {T} */ (first);
|
||||
if (first === undefined) return /** @type {O} */ (second);
|
||||
if (typeof second !== "object" || second === null) {
|
||||
return /** @type {O} */ (second);
|
||||
}
|
||||
if (typeof first !== "object" || first === null) {
|
||||
return /** @type {T} */ (first);
|
||||
}
|
||||
|
||||
let innerCache = mergeCache.get(first);
|
||||
if (innerCache === undefined) {
|
||||
innerCache = new WeakMap();
|
||||
mergeCache.set(first, innerCache);
|
||||
}
|
||||
const prevMerge = /** @type {T & O} */ (innerCache.get(second));
|
||||
if (prevMerge !== undefined) return prevMerge;
|
||||
const newMerge = _cleverMerge(first, second, true);
|
||||
innerCache.set(second, newMerge);
|
||||
return newMerge;
|
||||
};
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {Partial<T>} obj object
|
||||
* @param {string} property property
|
||||
* @param {string | number | boolean} value assignment value
|
||||
* @returns {T} new object
|
||||
*/
|
||||
const cachedSetProperty = (obj, property, value) => {
|
||||
let mapByProperty = setPropertyCache.get(obj);
|
||||
|
||||
if (mapByProperty === undefined) {
|
||||
mapByProperty = new Map();
|
||||
setPropertyCache.set(obj, mapByProperty);
|
||||
}
|
||||
|
||||
let mapByValue = mapByProperty.get(property);
|
||||
|
||||
if (mapByValue === undefined) {
|
||||
mapByValue = new Map();
|
||||
mapByProperty.set(property, mapByValue);
|
||||
}
|
||||
|
||||
let result = mapByValue.get(value);
|
||||
|
||||
if (result) return /** @type {T} */ (result);
|
||||
|
||||
result = {
|
||||
...obj,
|
||||
[property]: value
|
||||
};
|
||||
mapByValue.set(value, result);
|
||||
|
||||
return /** @type {T} */ (result);
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Map<string, EXPECTED_ANY>} ByValues
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {object} ObjectParsedPropertyEntry
|
||||
* @property {T[keyof T] | undefined} base base value
|
||||
* @property {`by${string}` | undefined} byProperty the name of the selector property
|
||||
* @property {ByValues | undefined} byValues value depending on selector property, merged with base
|
||||
*/
|
||||
|
||||
/** @typedef {(function(...EXPECTED_ANY): object) & { [DYNAMIC_INFO]: [DynamicFunction, object] }} DynamicFunction */
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @typedef {Map<keyof T, ObjectParsedPropertyEntry<T>>} ParsedObjectStatic
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @typedef {{ byProperty: `by${string}`, fn: DynamicFunction }} ParsedObjectDynamic
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @typedef {object} ParsedObject
|
||||
* @property {ParsedObjectStatic<T>} static static properties (key is property name)
|
||||
* @property {ParsedObjectDynamic<T> | undefined} dynamic dynamic part
|
||||
*/
|
||||
|
||||
/** @type {WeakMap<EXPECTED_OBJECT, ParsedObject<EXPECTED_ANY>>} */
|
||||
const parseCache = new WeakMap();
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @param {T} obj the object
|
||||
* @returns {ParsedObject<T>} parsed object
|
||||
*/
|
||||
const cachedParseObject = (obj) => {
|
||||
const entry = parseCache.get(/** @type {EXPECTED_OBJECT} */ (obj));
|
||||
if (entry !== undefined) return entry;
|
||||
const result = parseObject(obj);
|
||||
parseCache.set(/** @type {EXPECTED_OBJECT} */ (obj), result);
|
||||
return result;
|
||||
};
|
||||
|
||||
/** @typedef {{ [p: string]: { [p: string]: EXPECTED_ANY } } | DynamicFunction} ByObject */
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @param {T} obj the object
|
||||
* @returns {ParsedObject<T>} parsed object
|
||||
*/
|
||||
const parseObject = (obj) => {
|
||||
/** @type {ParsedObjectStatic<T>} */
|
||||
const info = new Map();
|
||||
/** @type {ParsedObjectDynamic<T> | undefined} */
|
||||
let dynamicInfo;
|
||||
/**
|
||||
* @param {keyof T} p path
|
||||
* @returns {Partial<ObjectParsedPropertyEntry<T>>} object parsed property entry
|
||||
*/
|
||||
const getInfo = (p) => {
|
||||
const entry = info.get(p);
|
||||
if (entry !== undefined) return entry;
|
||||
const newEntry = {
|
||||
base: undefined,
|
||||
byProperty: undefined,
|
||||
byValues: undefined
|
||||
};
|
||||
info.set(p, newEntry);
|
||||
return newEntry;
|
||||
};
|
||||
for (const key_ of Object.keys(obj)) {
|
||||
const key = /** @type {keyof T} */ (key_);
|
||||
if (typeof key === "string" && key.startsWith("by")) {
|
||||
const byProperty = key;
|
||||
const byObj = /** @type {ByObject} */ (obj[byProperty]);
|
||||
if (typeof byObj === "object") {
|
||||
for (const byValue of Object.keys(byObj)) {
|
||||
const obj = byObj[/** @type {keyof (keyof T)} */ (byValue)];
|
||||
for (const key of Object.keys(obj)) {
|
||||
const entry = getInfo(/** @type {keyof T} */ (key));
|
||||
if (entry.byProperty === undefined) {
|
||||
entry.byProperty = /** @type {`by${string}`} */ (byProperty);
|
||||
entry.byValues = new Map();
|
||||
} else if (entry.byProperty !== byProperty) {
|
||||
throw new Error(
|
||||
`${/** @type {string} */ (byProperty)} and ${entry.byProperty} for a single property is not supported`
|
||||
);
|
||||
}
|
||||
/** @type {ByValues} */
|
||||
(entry.byValues).set(byValue, obj[key]);
|
||||
if (byValue === "default") {
|
||||
for (const otherByValue of Object.keys(byObj)) {
|
||||
if (
|
||||
!(
|
||||
/** @type {ByValues} */
|
||||
(entry.byValues).has(otherByValue)
|
||||
)
|
||||
) {
|
||||
/** @type {ByValues} */
|
||||
(entry.byValues).set(otherByValue, undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof byObj === "function") {
|
||||
if (dynamicInfo === undefined) {
|
||||
dynamicInfo = {
|
||||
byProperty: /** @type {`by${string}`} */ (key),
|
||||
fn: byObj
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`${key} and ${dynamicInfo.byProperty} when both are functions is not supported`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const entry = getInfo(key);
|
||||
entry.base = obj[key];
|
||||
}
|
||||
} else {
|
||||
const entry = getInfo(key);
|
||||
entry.base = obj[key];
|
||||
}
|
||||
}
|
||||
return {
|
||||
static: info,
|
||||
dynamic: dynamicInfo
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @param {ParsedObjectStatic<T>} info static properties (key is property name)
|
||||
* @param {{ byProperty: `by${string}`, fn: DynamicFunction } | undefined} dynamicInfo dynamic part
|
||||
* @returns {T} the object
|
||||
*/
|
||||
const serializeObject = (info, dynamicInfo) => {
|
||||
const obj = /** @type {EXPECTED_ANY} */ ({});
|
||||
// Setup byProperty structure
|
||||
for (const entry of info.values()) {
|
||||
if (entry.byProperty !== undefined) {
|
||||
const byProperty = entry.byProperty;
|
||||
const byObj = (obj[byProperty] = obj[byProperty] || {});
|
||||
for (const byValue of /** @type {ByValues} */ (entry.byValues).keys()) {
|
||||
byObj[byValue] = byObj[byValue] || {};
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [key, entry] of info) {
|
||||
if (entry.base !== undefined) {
|
||||
obj[key] = entry.base;
|
||||
}
|
||||
// Fill byProperty structure
|
||||
if (entry.byProperty !== undefined) {
|
||||
const byProperty = entry.byProperty;
|
||||
const byObj = (obj[byProperty] = obj[byProperty] || {});
|
||||
for (const byValue of Object.keys(byObj)) {
|
||||
const value = getFromByValues(
|
||||
/** @type {ByValues} */
|
||||
(entry.byValues),
|
||||
byValue
|
||||
);
|
||||
if (value !== undefined) byObj[byValue][key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dynamicInfo !== undefined) {
|
||||
obj[dynamicInfo.byProperty] = dynamicInfo.fn;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
const VALUE_TYPE_UNDEFINED = 0;
|
||||
const VALUE_TYPE_ATOM = 1;
|
||||
const VALUE_TYPE_ARRAY_EXTEND = 2;
|
||||
const VALUE_TYPE_OBJECT = 3;
|
||||
const VALUE_TYPE_DELETE = 4;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value a single value
|
||||
* @returns {VALUE_TYPE_UNDEFINED | VALUE_TYPE_ATOM | VALUE_TYPE_ARRAY_EXTEND | VALUE_TYPE_OBJECT | VALUE_TYPE_DELETE} value type
|
||||
*/
|
||||
const getValueType = (value) => {
|
||||
if (value === undefined) {
|
||||
return VALUE_TYPE_UNDEFINED;
|
||||
} else if (value === DELETE) {
|
||||
return VALUE_TYPE_DELETE;
|
||||
} else if (Array.isArray(value)) {
|
||||
if (value.includes("...")) return VALUE_TYPE_ARRAY_EXTEND;
|
||||
return VALUE_TYPE_ATOM;
|
||||
} else if (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
(!value.constructor || value.constructor === Object)
|
||||
) {
|
||||
return VALUE_TYPE_OBJECT;
|
||||
}
|
||||
return VALUE_TYPE_ATOM;
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges two objects. Objects are deeply clever merged.
|
||||
* Arrays might reference the old value with "...".
|
||||
* Non-object values take preference over object values.
|
||||
* @template T
|
||||
* @template O
|
||||
* @param {T} first first object
|
||||
* @param {O} second second object
|
||||
* @returns {T & O | T | O} merged object of first and second object
|
||||
*/
|
||||
const cleverMerge = (first, second) => {
|
||||
if (second === undefined) return first;
|
||||
if (first === undefined) return second;
|
||||
if (typeof second !== "object" || second === null) return second;
|
||||
if (typeof first !== "object" || first === null) return first;
|
||||
|
||||
return /** @type {T & O} */ (_cleverMerge(first, second, false));
|
||||
};
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @template {object} O
|
||||
* Merges two objects. Objects are deeply clever merged.
|
||||
* @param {T} first first
|
||||
* @param {O} second second
|
||||
* @param {boolean} internalCaching should parsing of objects and nested merges be cached
|
||||
* @returns {T & O} merged object of first and second object
|
||||
*/
|
||||
const _cleverMerge = (first, second, internalCaching = false) => {
|
||||
const firstObject = internalCaching
|
||||
? cachedParseObject(first)
|
||||
: parseObject(first);
|
||||
const { static: firstInfo, dynamic: firstDynamicInfo } = firstObject;
|
||||
|
||||
// If the first argument has a dynamic part we modify the dynamic part to merge the second argument
|
||||
if (firstDynamicInfo !== undefined) {
|
||||
let { byProperty, fn } = firstDynamicInfo;
|
||||
const fnInfo = fn[DYNAMIC_INFO];
|
||||
if (fnInfo) {
|
||||
second =
|
||||
/** @type {O} */
|
||||
(
|
||||
internalCaching
|
||||
? cachedCleverMerge(fnInfo[1], second)
|
||||
: cleverMerge(fnInfo[1], second)
|
||||
);
|
||||
fn = fnInfo[0];
|
||||
}
|
||||
/** @type {DynamicFunction} */
|
||||
const newFn = (...args) => {
|
||||
const fnResult = fn(...args);
|
||||
return internalCaching
|
||||
? cachedCleverMerge(fnResult, second)
|
||||
: cleverMerge(fnResult, second);
|
||||
};
|
||||
newFn[DYNAMIC_INFO] = [fn, second];
|
||||
return /** @type {T & O} */ (
|
||||
serializeObject(firstObject.static, { byProperty, fn: newFn })
|
||||
);
|
||||
}
|
||||
|
||||
// If the first part is static only, we merge the static parts and keep the dynamic part of the second argument
|
||||
const secondObject = internalCaching
|
||||
? cachedParseObject(second)
|
||||
: parseObject(second);
|
||||
const { static: secondInfo, dynamic: secondDynamicInfo } = secondObject;
|
||||
const resultInfo = new Map();
|
||||
for (const [key, firstEntry] of firstInfo) {
|
||||
const secondEntry = secondInfo.get(
|
||||
/** @type {keyof (T | O)} */
|
||||
(key)
|
||||
);
|
||||
const entry =
|
||||
secondEntry !== undefined
|
||||
? mergeEntries(firstEntry, secondEntry, internalCaching)
|
||||
: firstEntry;
|
||||
resultInfo.set(key, entry);
|
||||
}
|
||||
for (const [key, secondEntry] of secondInfo) {
|
||||
if (!firstInfo.has(/** @type {keyof (T | O)} */ (key))) {
|
||||
resultInfo.set(key, secondEntry);
|
||||
}
|
||||
}
|
||||
return /** @type {T & O} */ (serializeObject(resultInfo, secondDynamicInfo));
|
||||
};
|
||||
|
||||
/**
|
||||
* @template T, O
|
||||
* @param {ObjectParsedPropertyEntry<T>} firstEntry a
|
||||
* @param {ObjectParsedPropertyEntry<O>} secondEntry b
|
||||
* @param {boolean} internalCaching should parsing of objects and nested merges be cached
|
||||
* @returns {ObjectParsedPropertyEntry<T> | ObjectParsedPropertyEntry<O> | ObjectParsedPropertyEntry<T & O>} new entry
|
||||
*/
|
||||
const mergeEntries = (firstEntry, secondEntry, internalCaching) => {
|
||||
switch (getValueType(secondEntry.base)) {
|
||||
case VALUE_TYPE_ATOM:
|
||||
case VALUE_TYPE_DELETE:
|
||||
// No need to consider firstEntry at all
|
||||
// second value override everything
|
||||
// = second.base + second.byProperty
|
||||
return secondEntry;
|
||||
case VALUE_TYPE_UNDEFINED:
|
||||
if (!firstEntry.byProperty) {
|
||||
// = first.base + second.byProperty
|
||||
return {
|
||||
base: firstEntry.base,
|
||||
byProperty: secondEntry.byProperty,
|
||||
byValues: secondEntry.byValues
|
||||
};
|
||||
} else if (firstEntry.byProperty !== secondEntry.byProperty) {
|
||||
throw new Error(
|
||||
`${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
|
||||
);
|
||||
} else {
|
||||
// = first.base + (first.byProperty + second.byProperty)
|
||||
// need to merge first and second byValues
|
||||
/** @type {Map<string, T & O>} */
|
||||
const newByValues = new Map(firstEntry.byValues);
|
||||
for (const [key, value] of /** @type {ByValues} */ (
|
||||
secondEntry.byValues
|
||||
)) {
|
||||
const firstValue = getFromByValues(
|
||||
/** @type {ByValues} */
|
||||
(firstEntry.byValues),
|
||||
key
|
||||
);
|
||||
newByValues.set(
|
||||
key,
|
||||
mergeSingleValue(firstValue, value, internalCaching)
|
||||
);
|
||||
}
|
||||
return {
|
||||
base: firstEntry.base,
|
||||
byProperty: firstEntry.byProperty,
|
||||
byValues: newByValues
|
||||
};
|
||||
}
|
||||
default: {
|
||||
if (!firstEntry.byProperty) {
|
||||
// The simple case
|
||||
// = (first.base + second.base) + second.byProperty
|
||||
return {
|
||||
base:
|
||||
/** @type {T[keyof T] & O[keyof O]} */
|
||||
(
|
||||
mergeSingleValue(
|
||||
firstEntry.base,
|
||||
secondEntry.base,
|
||||
internalCaching
|
||||
)
|
||||
),
|
||||
byProperty: secondEntry.byProperty,
|
||||
byValues: secondEntry.byValues
|
||||
};
|
||||
}
|
||||
/** @type {O[keyof O] | T[keyof T] | (T[keyof T] & O[keyof O]) | (T[keyof T] | undefined)[] | (O[keyof O] | undefined)[] | (O[keyof O] | T[keyof T] | undefined)[] | undefined} */
|
||||
let newBase;
|
||||
/** @type {Map<string, (T & O) | O[keyof O] | (O[keyof O] | undefined)[] | ((T & O) | undefined)[] | (T & O & O[keyof O]) | ((T & O) | O[keyof O] | undefined)[] | undefined>} */
|
||||
const intermediateByValues = new Map(firstEntry.byValues);
|
||||
for (const [key, value] of intermediateByValues) {
|
||||
intermediateByValues.set(
|
||||
key,
|
||||
mergeSingleValue(value, secondEntry.base, internalCaching)
|
||||
);
|
||||
}
|
||||
if (
|
||||
[.../** @type {ByValues} */ (firstEntry.byValues).values()].every(
|
||||
(value) => {
|
||||
const type = getValueType(value);
|
||||
return type === VALUE_TYPE_ATOM || type === VALUE_TYPE_DELETE;
|
||||
}
|
||||
)
|
||||
) {
|
||||
// = (first.base + second.base) + ((first.byProperty + second.base) + second.byProperty)
|
||||
newBase = mergeSingleValue(
|
||||
firstEntry.base,
|
||||
secondEntry.base,
|
||||
internalCaching
|
||||
);
|
||||
} else {
|
||||
// = first.base + ((first.byProperty (+default) + second.base) + second.byProperty)
|
||||
newBase = firstEntry.base;
|
||||
if (!intermediateByValues.has("default")) {
|
||||
intermediateByValues.set("default", secondEntry.base);
|
||||
}
|
||||
}
|
||||
if (!secondEntry.byProperty) {
|
||||
// = first.base + (first.byProperty + second.base)
|
||||
return {
|
||||
base: /** @type {T[keyof T] & O[keyof O]} */ (newBase),
|
||||
byProperty: firstEntry.byProperty,
|
||||
byValues: intermediateByValues
|
||||
};
|
||||
} else if (firstEntry.byProperty !== secondEntry.byProperty) {
|
||||
throw new Error(
|
||||
`${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
|
||||
);
|
||||
}
|
||||
/** @type {Map<string, (T & O) | O[keyof O] | (O[keyof O] | undefined)[] | (T & O & O[keyof O]) | ((T & O) | undefined)[] | ((T & O) | O[keyof O] | undefined)[] | undefined>} */
|
||||
const newByValues = new Map(intermediateByValues);
|
||||
for (const [key, value] of /** @type {ByValues} */ (
|
||||
secondEntry.byValues
|
||||
)) {
|
||||
const firstValue = getFromByValues(intermediateByValues, key);
|
||||
newByValues.set(
|
||||
key,
|
||||
mergeSingleValue(firstValue, value, internalCaching)
|
||||
);
|
||||
}
|
||||
return {
|
||||
base: /** @type {T[keyof T] & O[keyof O]} */ (newBase),
|
||||
byProperty: firstEntry.byProperty,
|
||||
byValues: newByValues
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {ByValues} byValues all values
|
||||
* @param {string} key value of the selector
|
||||
* @returns {V | undefined} value
|
||||
*/
|
||||
const getFromByValues = (byValues, key) => {
|
||||
if (key !== "default" && byValues.has(key)) {
|
||||
return byValues.get(key);
|
||||
}
|
||||
return byValues.get("default");
|
||||
};
|
||||
|
||||
/**
|
||||
* @template A
|
||||
* @template B
|
||||
* @param {A | A[]} a value
|
||||
* @param {B | B[]} b value
|
||||
* @param {boolean} internalCaching should parsing of objects and nested merges be cached
|
||||
* @returns {A & B | (A | B)[] | A | A[] | B | B[]} value
|
||||
*/
|
||||
const mergeSingleValue = (a, b, internalCaching) => {
|
||||
const bType = getValueType(b);
|
||||
const aType = getValueType(a);
|
||||
switch (bType) {
|
||||
case VALUE_TYPE_DELETE:
|
||||
case VALUE_TYPE_ATOM:
|
||||
return b;
|
||||
case VALUE_TYPE_OBJECT: {
|
||||
return aType !== VALUE_TYPE_OBJECT
|
||||
? b
|
||||
: internalCaching
|
||||
? cachedCleverMerge(a, b)
|
||||
: cleverMerge(a, b);
|
||||
}
|
||||
case VALUE_TYPE_UNDEFINED:
|
||||
return a;
|
||||
case VALUE_TYPE_ARRAY_EXTEND:
|
||||
switch (
|
||||
aType !== VALUE_TYPE_ATOM
|
||||
? aType
|
||||
: Array.isArray(a)
|
||||
? VALUE_TYPE_ARRAY_EXTEND
|
||||
: VALUE_TYPE_OBJECT
|
||||
) {
|
||||
case VALUE_TYPE_UNDEFINED:
|
||||
return b;
|
||||
case VALUE_TYPE_DELETE:
|
||||
return /** @type {B[]} */ (b).filter((item) => item !== "...");
|
||||
case VALUE_TYPE_ARRAY_EXTEND: {
|
||||
/** @type {(A | B)[]} */
|
||||
const newArray = [];
|
||||
for (const item of /** @type {B[]} */ (b)) {
|
||||
if (item === "...") {
|
||||
for (const item of /** @type {A[]} */ (a)) {
|
||||
newArray.push(item);
|
||||
}
|
||||
} else {
|
||||
newArray.push(item);
|
||||
}
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
case VALUE_TYPE_OBJECT:
|
||||
return /** @type {(A | B)[]} */ (b).map((item) =>
|
||||
item === "..." ? /** @type {A} */ (a) : item
|
||||
);
|
||||
default:
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
default:
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @param {T} obj the object
|
||||
* @param {(keyof T)[]=} keysToKeepOriginalValue keys to keep original value
|
||||
* @returns {T} the object without operations like "..." or DELETE
|
||||
*/
|
||||
const removeOperations = (obj, keysToKeepOriginalValue = []) => {
|
||||
const newObj = /** @type {T} */ ({});
|
||||
for (const _key of Object.keys(obj)) {
|
||||
const key = /** @type {keyof T} */ (_key);
|
||||
const value = obj[key];
|
||||
const type = getValueType(value);
|
||||
if (type === VALUE_TYPE_OBJECT && keysToKeepOriginalValue.includes(key)) {
|
||||
newObj[key] = value;
|
||||
continue;
|
||||
}
|
||||
switch (type) {
|
||||
case VALUE_TYPE_UNDEFINED:
|
||||
case VALUE_TYPE_DELETE:
|
||||
break;
|
||||
case VALUE_TYPE_OBJECT:
|
||||
newObj[key] =
|
||||
/** @type {T[keyof T]} */
|
||||
(
|
||||
removeOperations(
|
||||
/** @type {T} */
|
||||
(value),
|
||||
keysToKeepOriginalValue
|
||||
)
|
||||
);
|
||||
break;
|
||||
case VALUE_TYPE_ARRAY_EXTEND:
|
||||
newObj[key] =
|
||||
/** @type {T[keyof T]} */
|
||||
(
|
||||
/** @type {EXPECTED_ANY[]} */
|
||||
(value).filter((i) => i !== "...")
|
||||
);
|
||||
break;
|
||||
default:
|
||||
newObj[key] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
};
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template {keyof T} P
|
||||
* @template V
|
||||
* @param {T} obj the object
|
||||
* @param {P} byProperty the by description
|
||||
* @param {...V} values values
|
||||
* @returns {Omit<T, P>} object with merged byProperty
|
||||
*/
|
||||
const resolveByProperty = (obj, byProperty, ...values) => {
|
||||
if (typeof obj !== "object" || obj === null || !(byProperty in obj)) {
|
||||
return obj;
|
||||
}
|
||||
const { [byProperty]: _byValue, ..._remaining } = obj;
|
||||
const remaining = /** @type {T} */ (_remaining);
|
||||
const byValue =
|
||||
/** @type {Record<string, T> | ((...args: V[]) => T)} */
|
||||
(_byValue);
|
||||
if (typeof byValue === "object") {
|
||||
const key = /** @type {string} */ (values[0]);
|
||||
if (key in byValue) {
|
||||
return cachedCleverMerge(remaining, byValue[key]);
|
||||
} else if ("default" in byValue) {
|
||||
return cachedCleverMerge(remaining, byValue.default);
|
||||
}
|
||||
return remaining;
|
||||
} else if (typeof byValue === "function") {
|
||||
// eslint-disable-next-line prefer-spread
|
||||
const result = byValue.apply(null, values);
|
||||
return cachedCleverMerge(
|
||||
remaining,
|
||||
resolveByProperty(result, byProperty, ...values)
|
||||
);
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
module.exports.DELETE = DELETE;
|
||||
module.exports.cachedCleverMerge = cachedCleverMerge;
|
||||
module.exports.cachedSetProperty = cachedSetProperty;
|
||||
module.exports.cleverMerge = cleverMerge;
|
||||
module.exports.removeOperations = removeOperations;
|
||||
module.exports.resolveByProperty = resolveByProperty;
|
||||
Reference in New Issue
Block a user