summaryrefslogtreecommitdiff
path: root/ext/mixed
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mixed')
-rw-r--r--ext/mixed/js/core.js67
1 files changed, 67 insertions, 0 deletions
diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js
index 21b7bf5e..9b3ea3e2 100644
--- a/ext/mixed/js/core.js
+++ b/ext/mixed/js/core.js
@@ -157,6 +157,73 @@ function getSetDifference(set1, set2) {
);
}
+const clone = (() => {
+ // eslint-disable-next-line no-shadow
+ function clone(value) {
+ if (value === null) { return null; }
+ switch (typeof value) {
+ case 'boolean':
+ case 'number':
+ case 'string':
+ case 'bigint':
+ case 'symbol':
+ case 'undefined':
+ return value;
+ default:
+ return cloneInternal(value, new Set());
+ }
+ }
+
+ function cloneInternal(value, visited) {
+ if (value === null) { return null; }
+ switch (typeof value) {
+ case 'boolean':
+ case 'number':
+ case 'string':
+ case 'bigint':
+ case 'symbol':
+ case 'undefined':
+ return value;
+ case 'function':
+ return cloneObject(value, visited);
+ case 'object':
+ return Array.isArray(value) ? cloneArray(value, visited) : cloneObject(value, visited);
+ }
+ }
+
+ function cloneArray(value, visited) {
+ if (visited.has(value)) { throw new Error('Circular'); }
+ try {
+ visited.add(value);
+ const result = [];
+ for (const item of value) {
+ result.push(cloneInternal(item, visited));
+ }
+ return result;
+ } finally {
+ visited.delete(value);
+ }
+ }
+
+ function cloneObject(value, visited) {
+ if (visited.has(value)) { throw new Error('Circular'); }
+ try {
+ visited.add(value);
+ const result = {};
+ for (const key in value) {
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
+ result[key] = cloneInternal(value[key], visited);
+ }
+ }
+ return result;
+ } finally {
+ visited.delete(value);
+ }
+ }
+
+ return clone;
+})();
+
/*
* Async utilities