Skip to content

Commit

Permalink
Merge pull request #8813 from geekuillaume/main
Browse files Browse the repository at this point in the history
Tolerate `TypedArray` and other unfreezable objects in `maybeDeepFreeze`
  • Loading branch information
benjamn authored Sep 20, 2021
2 parents fcd65e6 + 81417ad commit 4b9a77c
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
- Fix internal `canUseSymbol` import within `@apollo/client/utilities` to avoid breaking bundlers/builds. <br/>
[@benjamn](https://github.com/benjamn) in [#8817](https://github.com/apollographql/apollo-client/pull/8817)

- Tolerate unfreezable objects like `Uint8Array` and `Buffer` in `maybeDeepFreeze`. <br/>
[@geekuillaume](https://github.com/geekuillaume) and [@benjamn](https://github.com/benjamn) in [#8813](https://github.com/apollographql/apollo-client/pull/8813)

## Apollo Client 3.4.12

### Bug Fixes
Expand Down
24 changes: 24 additions & 0 deletions src/utilities/common/__tests__/maybeDeepFeeze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,28 @@ describe('maybeDeepFreeze', () => {
maybeDeepFreeze(foo);
expect(() => (foo.bar = 1)).toThrow();
});

it('should avoid freezing Uint8Array', () => {
const result = maybeDeepFreeze({ array: new Uint8Array(1) });
expect(Object.isFrozen(result)).toBe(true);
expect(Object.isFrozen(result.array)).toBe(false);
});

it('should avoid freezing Buffer', () => {
const result = maybeDeepFreeze({ oyez: Buffer.from("oyez") });
expect(Object.isFrozen(result)).toBe(true);
expect(Object.isFrozen(result.oyez)).toBe(false);
});

it('should not freeze child properties of unfreezable objects', () => {
const result = maybeDeepFreeze({
buffer: Object.assign(Buffer.from("oyez"), {
doNotFreeze: { please: "thanks" },
}),
});
expect(Object.isFrozen(result)).toBe(true);
expect(Object.isFrozen(result.buffer)).toBe(false);
expect(Object.isFrozen(result.buffer.doNotFreeze)).toBe(false);
expect(result.buffer.doNotFreeze).toEqual({ please: "thanks" });
});
});
18 changes: 16 additions & 2 deletions src/utilities/common/maybeDeepFreeze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { isNonNullObject } from './objects';
function deepFreeze(value: any) {
const workSet = new Set([value]);
workSet.forEach(obj => {
if (isNonNullObject(obj)) {
if (!Object.isFrozen(obj)) Object.freeze(obj);
if (isNonNullObject(obj) && shallowFreeze(obj) === obj) {
Object.getOwnPropertyNames(obj).forEach(name => {
if (isNonNullObject(obj[name])) workSet.add(obj[name]);
});
Expand All @@ -14,6 +13,21 @@ function deepFreeze(value: any) {
return value;
}

function shallowFreeze<T extends object>(obj: T): T | null {
if (__DEV__ && !Object.isFrozen(obj)) {
try {
Object.freeze(obj);
} catch (e) {
// Some types like Uint8Array and Node.js's Buffer cannot be frozen, but
// they all throw a TypeError when you try, so we re-throw any exceptions
// that are not TypeErrors, since that would be unexpected.
if (e instanceof TypeError) return null;
throw e;
}
}
return obj;
}

export function maybeDeepFreeze<T>(obj: T): T {
if (__DEV__) {
deepFreeze(obj);
Expand Down

0 comments on commit 4b9a77c

Please sign in to comment.