diff --git a/__tests__/index.ts b/__tests__/index.ts
index 76ec43d..ed9771a 100644
--- a/__tests__/index.ts
+++ b/__tests__/index.ts
@@ -14,7 +14,7 @@ hash.update('foo bar');
const SIMPLE_TYPES: PlainObject = {
boolean: true,
- error: new Error('boom'),
+ error: new TypeError('boom'),
fn() {
return 'foo';
},
@@ -53,7 +53,7 @@ const COMPLEX_TYPES: PlainObject = {
})('foo', 'bar', 'baz'),
array: ['foo', { bar: 'baz' }],
arrayBuffer: new ArrayBuffer(8),
- buffer: new Buffer('this is a test buffer'),
+ buffer: Buffer.from('this is a test buffer'),
customPrototype: Object.create({
method() {
return 'foo';
@@ -62,22 +62,22 @@ const COMPLEX_TYPES: PlainObject = {
}),
dataView: new DataView(new ArrayBuffer(16)),
date: new Date(),
- float32Array: new Float32Array([12, 15]),
- float64Array: new Float64Array([12, 15]),
+ float32Array: new Float32Array([1, 2]),
+ float64Array: new Float64Array([3, 4]),
hash,
- int8Array: new Int8Array([12, 15]),
- int16Array: new Int16Array([12, 15]),
- int32Array: new Int32Array([12, 15]),
+ int8Array: new Int8Array([5, 6]),
+ int16Array: new Int16Array([7, 8]),
+ int32Array: new Int32Array([9, 10]),
map: new Map().set('foo', { bar: { baz: 'quz' } }),
object: { foo: { bar: 'baz' } },
regexp: /foo/,
set: new Set().add('foo').add({ bar: { baz: 'quz' } }),
// Disabling, as jest fails intermittently with blob construction.
// blob: new Blob(['hey!'], {type : 'text/html'}),
- uint8Array: new Uint8Array([12, 15]),
- uint8ClampedArray: new Uint8ClampedArray([12, 15]),
- uint16Array: new Uint16Array([12, 15]),
- uint32Array: new Uint32Array([12, 15]),
+ uint8Array: new Uint8Array([11, 12]),
+ uint8ClampedArray: new Uint8ClampedArray([13, 14]),
+ uint16Array: new Uint16Array([15, 16]),
+ uint32Array: new Uint32Array([17, 18]),
};
Object.defineProperties(COMPLEX_TYPES, {
@@ -168,8 +168,8 @@ describe('copy', () => {
const properties = [].concat(
Object.keys(SIMPLE_TYPES),
Object.getOwnPropertySymbols(SIMPLE_TYPES).filter((symbol) =>
- Object.prototype.propertyIsEnumerable.call(SIMPLE_TYPES, symbol),
- ),
+ Object.prototype.propertyIsEnumerable.call(SIMPLE_TYPES, symbol)
+ )
);
properties.forEach((property: string | symbol) => {
@@ -192,7 +192,7 @@ describe('copy', () => {
const properties = [
...Object.keys(COMPLEX_TYPES),
...Object.getOwnPropertySymbols(COMPLEX_TYPES).filter((symbol) =>
- Object.prototype.propertyIsEnumerable.call(COMPLEX_TYPES, symbol),
+ Object.prototype.propertyIsEnumerable.call(COMPLEX_TYPES, symbol)
),
];
@@ -202,7 +202,7 @@ describe('copy', () => {
expect({ ...result[property] }).toEqual({ ...COMPLEX_TYPES[property] });
} else if (property === 'customPrototype') {
expect(Object.getPrototypeOf(result[property])).toBe(
- Object.getPrototypeOf(COMPLEX_TYPES[property]),
+ Object.getPrototypeOf(COMPLEX_TYPES[property])
);
expect(result[property]).toEqual(COMPLEX_TYPES[property]);
} else {
@@ -226,42 +226,6 @@ describe('copy', () => {
expect(result).toEqual(SPECIAL_TYPES);
});
- it('will handle when buffers are not supported', () => {
- const cleanComplexTypes = Object.keys(COMPLEX_TYPES).reduce(
- (types: PlainObject, key) => {
- if (
- key !== 'arguments' &&
- key !== 'arrayBuffer' &&
- key !== 'buffer' &&
- key !== 'dataView' &&
- !/float(.*)Array/.test(key) &&
- !/int(.*)Array/.test(key)
- ) {
- types[key] = COMPLEX_TYPES[key];
- }
-
- return types;
- },
- {},
- );
-
- const realm = {
- Date: global.Date,
- Error: global.Error,
- Map: global.Map,
- RegExp: global.RegExp,
- Set: global.Set,
- Blob: global.Blob,
- WeakMap: global.WeakMap,
- WeakSet: global.WeakSet,
- } as typeof globalThis;
-
- const result = copy(cleanComplexTypes, { realm });
-
- expect(result).not.toBe(cleanComplexTypes);
- expect(result).toEqual(cleanComplexTypes);
- });
-
it('will copy referenced objects', () => {
const reusedObject = {
name: 'I like trains!',
@@ -347,7 +311,7 @@ describe('copyStrict', () => {
const properties = [].concat(
Object.getOwnPropertyNames(SIMPLE_TYPES),
- Object.getOwnPropertySymbols(SIMPLE_TYPES),
+ Object.getOwnPropertySymbols(SIMPLE_TYPES)
);
properties.forEach((property: string | symbol) => {
@@ -374,7 +338,7 @@ describe('copyStrict', () => {
const properties = [].concat(
Object.getOwnPropertyNames(complexTypes),
- Object.getOwnPropertySymbols(complexTypes),
+ Object.getOwnPropertySymbols(complexTypes)
);
properties.forEach((property: string | symbol) => {
@@ -388,10 +352,10 @@ describe('copyStrict', () => {
});
} else if (property === 'customPrototype') {
expect(Object.getPrototypeOf(result[property])).toBe(
- Object.getPrototypeOf(COMPLEX_TYPES[property]),
+ Object.getPrototypeOf(COMPLEX_TYPES[property])
);
expect(Object.getPrototypeOf(result2[property])).toBe(
- Object.getPrototypeOf(COMPLEX_TYPES[property]),
+ Object.getPrototypeOf(COMPLEX_TYPES[property])
);
expect(result[property]).toEqual(COMPLEX_TYPES[property]);
@@ -427,45 +391,6 @@ describe('copyStrict', () => {
expect(result2).toEqual(SPECIAL_TYPES);
});
- it('will handle when buffers are not supported', () => {
- const cleanComplexTypes = Object.keys(COMPLEX_TYPES).reduce(
- (types: PlainObject, key) => {
- if (
- key !== 'arguments' &&
- key !== 'arrayBuffer' &&
- key !== 'buffer' &&
- key !== 'dataView' &&
- !/float(.*)Array/.test(key) &&
- !/int(.*)Array/.test(key)
- ) {
- types[key] = COMPLEX_TYPES[key];
- }
-
- return types;
- },
- {},
- );
-
- const realm = {
- Date: global.Date,
- Error: global.Error,
- Map: global.Map,
- RegExp: global.RegExp,
- Set: global.Set,
- WeakMap: global.WeakMap,
- WeakSet: global.WeakSet,
- } as typeof globalThis;
-
- const result = copy(cleanComplexTypes, { isStrict: true, realm });
- const result2 = copyStrict(cleanComplexTypes, { realm });
-
- expect(result).not.toBe(cleanComplexTypes);
- expect(result2).not.toBe(cleanComplexTypes);
-
- expect(result).toEqual(cleanComplexTypes);
- expect(result2).toEqual(cleanComplexTypes);
- });
-
it('will copy referenced objects', () => {
const reusedObject = {
name: 'I like trains!',
diff --git a/__tests__/utils.ts b/__tests__/utils.ts
index cb072c7..2b7be8c 100644
--- a/__tests__/utils.ts
+++ b/__tests__/utils.ts
@@ -56,9 +56,8 @@ describe('createCache', () => {
describe('getCleanClone', () => {
it('will return a pure object when there is no constructor', () => {
const object = Object.create(null);
- const realm = global;
- const result = utils.getCleanClone(object, realm);
+ const result = utils.getCleanClone(object);
expect(result).not.toBe(object);
expect(result).toEqual(object);
@@ -71,9 +70,7 @@ describe('getCleanClone', () => {
object.__proto__ = null;
- const realm = global;
-
- const result = utils.getCleanClone(object, realm);
+ const result = utils.getCleanClone(object);
expect(result).not.toBe(object);
expect(result).toEqual(object);
@@ -83,9 +80,8 @@ describe('getCleanClone', () => {
it('will return an empty POJO when the object passed is a POJO', () => {
const object = { foo: 'bar' };
- const realm = global;
- const result = utils.getCleanClone(object, realm);
+ const result = utils.getCleanClone(object);
expect(result).not.toBe(object);
expect(result).toEqual({});
@@ -100,9 +96,7 @@ describe('getCleanClone', () => {
object.foo = 'bar';
- const realm = global;
-
- const result = utils.getCleanClone(object, realm);
+ const result = utils.getCleanClone(object);
expect(result).not.toBe(object);
expect(result).toEqual({});
@@ -112,9 +106,8 @@ describe('getCleanClone', () => {
it('will return an empty object with the given constructor when it is a global constructor', () => {
const object = new Map();
- const realm = global;
- const result = utils.getCleanClone(object, realm);
+ const result = utils.getCleanClone(object);
expect(result).not.toBe(object);
expect(result).toEqual(new Map());
@@ -134,9 +127,8 @@ describe('getCleanClone', () => {
}
const object = new Foo('bar');
- const realm = global;
- const result = utils.getCleanClone(object, realm);
+ const result = utils.getCleanClone(object);
expect(result).not.toBe(object);
expect(result).toEqual(Object.create(Foo.prototype));
@@ -160,11 +152,10 @@ describe('getObjectCloneLoose', () => {
foo: 'bar',
[symbol]: 'blah',
};
- const realm = global;
const handleCopy = jest.fn().mockImplementation((arg) => arg);
const cache = utils.createCache();
- const result = utils.getObjectCloneLoose(object, realm, handleCopy, cache);
+ const result = utils.getObjectCloneLoose(object, handleCopy, cache);
Object.getOwnPropertySymbols = original;
@@ -174,7 +165,7 @@ describe('getObjectCloneLoose', () => {
clone[key] = object[key as keyof typeof object];
return clone;
- }, {}),
+ }, {})
);
expect(handleCopy).toHaveBeenCalledTimes(Object.keys(object).length);
@@ -185,17 +176,16 @@ describe('getObjectCloneLoose', () => {
bar: { baz: 'quz' },
[Symbol('quz')]: 'blah',
};
- const realm = global;
const handleCopy = jest.fn().mockImplementation((arg) => arg);
const cache = utils.createCache();
- const result = utils.getObjectCloneLoose(object, realm, handleCopy, cache);
+ const result = utils.getObjectCloneLoose(object, handleCopy, cache);
expect(result).not.toBe(object);
expect(result).toEqual(object);
expect(handleCopy).toHaveBeenCalledTimes(
- Object.keys(object).length + Object.getOwnPropertySymbols(object).length,
+ Object.keys(object).length + Object.getOwnPropertySymbols(object).length
);
});
});
@@ -222,11 +212,10 @@ describe('getObjectCloneStrict', () => {
value: 'blah',
});
- const realm = global;
const handleCopy = jest.fn().mockImplementation((arg) => arg);
const cache = utils.createCache();
- const result = utils.getObjectCloneStrict(object, realm, handleCopy, cache);
+ const result = utils.getObjectCloneStrict(object, handleCopy, cache);
Object.getOwnPropertySymbols = original;
@@ -238,12 +227,12 @@ describe('getObjectCloneStrict', () => {
return clone;
},
- {},
- ),
+ {}
+ )
);
expect(handleCopy).toHaveBeenCalledTimes(
- Object.getOwnPropertyNames(object).length,
+ Object.getOwnPropertyNames(object).length
);
});
@@ -261,18 +250,17 @@ describe('getObjectCloneStrict', () => {
value: 'blah',
});
- const realm = global;
const handleCopy = jest.fn().mockImplementation((arg) => arg);
const cache = utils.createCache();
- const result = utils.getObjectCloneStrict(object, realm, handleCopy, cache);
+ const result = utils.getObjectCloneStrict(object, handleCopy, cache);
expect(result).not.toBe(object);
expect(result).toEqual(object);
expect(handleCopy).toHaveBeenCalledTimes(
Object.getOwnPropertyNames(object).length +
- Object.getOwnPropertySymbols(object).length,
+ Object.getOwnPropertySymbols(object).length
);
});
});
diff --git a/src/index.ts b/src/index.ts
index 5f8dff1..503b416 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,37 +9,13 @@ import type { Cache, InternalCopier, Realm } from './utils';
export interface Options {
isStrict?: boolean;
- realm?: Realm;
}
export interface StrictOptions extends Omit {}
const { isArray } = Array;
const { assign, getPrototypeOf } = Object;
-
-const GLOBAL_THIS: Realm = (function () {
- if (typeof globalThis !== 'undefined') {
- return globalThis;
- }
-
- if (typeof self !== 'undefined') {
- return self;
- }
-
- if (typeof window !== 'undefined') {
- return window;
- }
-
- if (typeof global !== 'undefined') {
- return global;
- }
-
- if (console && console.error) {
- console.error('Unable to locate global object, returning "this".');
- }
-
- return this;
-})();
+const { toString } = Object.prototype;
/**
* Copy an value deeply as much as possible.
@@ -54,7 +30,6 @@ const GLOBAL_THIS: Realm = (function () {
export function copy(value: Value, options?: Options): Value {
// manually coalesced instead of default parameters for performance
const isStrict = !!(options && options.isStrict);
- const realm = (options && options.realm) || GLOBAL_THIS;
const getObjectClone = isStrict ? getObjectCloneStrict : getObjectCloneLoose;
/**
@@ -79,15 +54,15 @@ export function copy(value: Value, options?: Options): Value {
const Constructor = prototype && prototype.constructor;
// plain objects
- if (!Constructor || Constructor === realm.Object) {
- return getObjectClone(value, realm, handleCopy, cache);
+ if (!Constructor || Constructor === Object) {
+ return getObjectClone(value, handleCopy, cache);
}
// arrays
if (isArray(value)) {
// if strict, include non-standard properties
if (isStrict) {
- return getObjectCloneStrict(value, realm, handleCopy, cache);
+ return getObjectCloneStrict(value, handleCopy, cache);
}
const clone = new Constructor();
@@ -105,13 +80,15 @@ export function copy(value: Value, options?: Options): Value {
return clone;
}
+ const objectClass = toString.call(value);
+
// dates
- if (value instanceof realm.Date) {
+ if (objectClass === '[object Date]') {
return new Constructor(value.getTime());
}
// regexps
- if (value instanceof realm.RegExp) {
+ if (objectClass === '[object RegExp]') {
const clone = new Constructor(
value.source,
value.flags || getRegExpFlags(value)
@@ -123,7 +100,7 @@ export function copy(value: Value, options?: Options): Value {
}
// maps
- if (realm.Map && value instanceof realm.Map) {
+ if (objectClass === '[object Map]') {
const clone = new Constructor();
cache.set(value, clone);
@@ -136,7 +113,7 @@ export function copy(value: Value, options?: Options): Value {
}
// sets
- if (realm.Set && value instanceof realm.Set) {
+ if (objectClass === '[object Set]') {
const clone = new Constructor();
cache.set(value, clone);
@@ -149,41 +126,38 @@ export function copy(value: Value, options?: Options): Value {
}
// blobs
- if (realm.Blob && value instanceof realm.Blob) {
+ if (objectClass === '[object Blob]') {
return value.slice(0, value.size, value.type);
}
- // buffers (node-only)
- if (realm.Buffer && realm.Buffer.isBuffer(value)) {
- const clone = realm.Buffer.allocUnsafe
- ? realm.Buffer.allocUnsafe(value.length)
- : new Constructor(value.length);
+ // dataviews
+ if (objectClass === '[object DataView]') {
+ const clone = new Constructor(value.buffer.slice(0));
cache.set(value, clone);
- value.copy(clone);
return clone;
}
- // arraybuffers / dataviews
- if (realm.ArrayBuffer) {
- // dataviews
- if (realm.ArrayBuffer.isView(value)) {
- const clone = new Constructor(value.buffer.slice(0));
-
- cache.set(value, clone);
-
- return clone;
- }
-
- // arraybuffers
- if (value instanceof realm.ArrayBuffer) {
- const clone = value.slice(0);
+ // array buffers
+ if (
+ objectClass === '[object Float32Array]' ||
+ objectClass === '[object Float64Array]' ||
+ objectClass === '[object Int8Array]' ||
+ objectClass === '[object Int16Array]' ||
+ objectClass === '[object Int32Array]' ||
+ objectClass === '[object Uint8Array]' ||
+ objectClass === '[object Uint8ClampedArray]' ||
+ objectClass === '[object Uint16Array]' ||
+ objectClass === '[object Uint32Array]' ||
+ objectClass === '[object Uint64Array]' ||
+ objectClass === '[object ArrayBuffer]'
+ ) {
+ const clone = value.slice(0);
- cache.set(value, clone);
+ cache.set(value, clone);
- return clone;
- }
+ return clone;
}
// if the value cannot / should not be cloned, don't
@@ -191,17 +165,17 @@ export function copy(value: Value, options?: Options): Value {
// promise-like
typeof value.then === 'function' ||
// errors
- value instanceof Error ||
+ objectClass === '[object Error]' ||
// weakmaps
- (realm.WeakMap && value instanceof realm.WeakMap) ||
+ objectClass === '[object WeakMap]' ||
// weaksets
- (realm.WeakSet && value instanceof realm.WeakSet)
+ objectClass === '[object WeakSet]'
) {
return value;
}
// assume anything left is a custom constructor
- return getObjectClone(value, realm, handleCopy, cache);
+ return getObjectClone(value, handleCopy, cache);
};
return handleCopy(value, createCache());
diff --git a/src/utils.ts b/src/utils.ts
index 960412e..dcb0168 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -10,7 +10,6 @@ export type InternalCopier = (value: Value, cache: Cache) => Value;
export type ObjectCloner = (
object: Value,
- realm: Realm,
handleCopy: InternalCopier,
cache: Cache
) => Value;
@@ -65,7 +64,7 @@ export const createCache =
/**
* Get an empty version of the object with the same prototype it has.
*/
-export function getCleanClone(object: any, realm: Realm): any {
+export function getCleanClone(object: any): any {
const prototype = object.__proto__ || getPrototypeOf(object);
if (!prototype) {
@@ -74,8 +73,8 @@ export function getCleanClone(object: any, realm: Realm): any {
const Constructor = prototype.constructor;
- if (Constructor === realm.Object) {
- return prototype === realm.Object.prototype ? {} : create(prototype);
+ if (Constructor === Object) {
+ return prototype === Object.prototype ? {} : create(prototype);
}
if (~toStringFunction.call(Constructor).indexOf('[native code]')) {
@@ -93,11 +92,10 @@ export function getCleanClone(object: any, realm: Realm): any {
*/
export function getObjectCloneLoose(
object: any,
- realm: Realm,
handleCopy: InternalCopier,
cache: Cache
): any {
- const clone: any = getCleanClone(object, realm);
+ const clone: any = getCleanClone(object);
// set in the cache immediately to be able to reuse the object recursively
cache.set(object, clone);
@@ -143,11 +141,10 @@ const getStrictProperties = SYMBOL_PROPERTIES
*/
export function getObjectCloneStrict(
object: any,
- realm: Realm,
handleCopy: InternalCopier,
cache: Cache
): any {
- const clone: any = getCleanClone(object, realm);
+ const clone: any = getCleanClone(object);
// set in the cache immediately to be able to reuse the object recursively
cache.set(object, clone);