Skip to content

Commit

Permalink
Merge pull request #647 from zloirock/map-upsert
Browse files Browse the repository at this point in the history
Update `Map#updateOrInsert` -> `Map#upsert`
  • Loading branch information
zloirock committed Oct 1, 2019
2 parents acc2d72 + 6f67b65 commit d38d01c
Show file tree
Hide file tree
Showing 26 changed files with 266 additions and 27 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
- `AsyncIterator#take`
- `AsyncIterator#toArray`
- `AsyncIterator#@@toStringTag`
- Updated `Map#upsert` [stage 1 proposal](https://github.com/thumbsupep/proposal-upsert)
- `Map#updateOrInsert` renamed to `Map#upsert`
- Added `WeakMap#upsert`
- Added a workaround for iOS Safari MessageChannel + bfcache bug, [#624](https://github.com/zloirock/core-js/issues/624)
- Added a workaround for Chrome 33 / Android 4.4.4 `Promise` bug, [#640](https://github.com/zloirock/core-js/issues/640)
- Added compat data for Node 12.9, FF 69 and Phantom 1.9
Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1905,7 +1905,7 @@ Promise.try(() => { throw 42; }).catch(it => console.log(`Promise, rejected as $
```
* New collections methods proposals:
- New `Set` and `Map` methods [proposal](https://github.com/tc39/proposal-collection-methods) - modules [`esnext.set.add-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.add-all.js), [`esnext.set.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.delete-all.js), [`esnext.set.every`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.every.js), [`esnext.set.filter`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.filter.js), [`esnext.set.find`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.find.js), [`esnext.set.join`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.join.js), [`esnext.set.map`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.map.js), [`esnext.set.reduce`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.reduce.js), [`esnext.set.some`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.some.js), [`esnext.map.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.delete-all.js), [`esnext.map.every`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.every.js), [`esnext.map.filter`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.filter.js), [`esnext.map.find`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.find.js), [`esnext.map.find-key`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.find-key.js), [`esnext.map.group-by`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.group-by.js), [`esnext.map.includes`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.includes.js), [`esnext.map.key-by`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.key-by.js), [`esnext.map.key-of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.key-of.js), [`esnext.map.map-keys`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.map-keys.js), [`esnext.map.map-values`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.map-values.js), [`esnext.map.merge`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.merge.js), [`esnext.map.reduce`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.reduce.js), [`esnext.map.some`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.some.js), [`esnext.map.update`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.update.js), [`esnext.weak-set.add-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.add-all.js), [`esnext.weak-set.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.delete-all.js), [`esnext.weak-map.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-map.delete-all.js)
- `Map#updateOrInsert` [proposal](https://docs.google.com/presentation/d/1_xtrGSoN1-l2Q74eCXPHBbbrBHsVyqArWN0ebnW-pVQ/) - module [`esnext.map.update-or-insert`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.update-or-insert.js)
- `Map#upsert` [proposal](https://github.com/thumbsupep/proposal-upsert) - modules [`esnext.map.upsert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.map.upsert.js), [`esnext.map.update-or-insert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.map.update-or-insert.js) and [`esnext.weak-map.update-or-insert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.weak-map.update-or-insert.js)
- `.of` and `.from` methods on collection constructors [proposal](https://github.com/tc39/proposal-setmap-offrom) - modules [`esnext.set.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.of.js), [`esnext.set.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.from.js), [`esnext.map.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.of.js), [`esnext.map.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.from.js), [`esnext.weak-set.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.of.js), [`esnext.weak-set.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.from.js), [`esnext.weak-map.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-map.of.js), [`esnext.weak-map.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-map.from.js)
```js
class Set {
Expand Down Expand Up @@ -1940,7 +1940,8 @@ class Map {
reduce(callbackfn: (memo: any, value: any, key: any, target: any) => any, initialValue?: any): any;
some(callbackfn: (value: any, key: any, target: any) => boolean, thisArg?: any): boolean;
update(key: any, callbackfn: (value: any, key: any, target: any) => any, thunk?: (key: any, target: any) => any): this;
updateOrInsert(key: any, onUpdate: (value: any) => updated: any, onInsert: () => value: any): updated | value;
updateOrInsert(key: any, onUpdate: (value: any) => updated: any, onInsert: () => value: any): updated | value; (obsolete in favor `.upsert`)
upsert(key: any, onUpdate: (value: any) => updated: any, onInsert: () => value: any): updated | value;
}

class WeakSet {
Expand All @@ -1954,13 +1955,14 @@ class WeakMap {
static of(...args: Array<[key, value]>): WeakMap;
static from(iterable: Iterable<mixed>, mapFn?: (value: any, index: number) => [key: Object, value: any], thisArg?: any): WeakMap;
deleteAll(...args: Array<mixed>): boolean;
upsert(key: Object, onUpdate: (value: any) => updated: any, onInsert: () => value: any): updated | value;
}
```
[*CommonJS entry points:*](#commonjs-api)
```js
core-js/proposals/collection-methods
core-js/proposals/collection-of-from
core-js/proposals/map-update-or-insert
core-js/proposals/map-upsert
core-js(-pure)/features/set/add-all
core-js(-pure)/features/set/delete-all
core-js(-pure)/features/set/every
Expand Down Expand Up @@ -1990,21 +1992,23 @@ core-js(-pure)/features/map/reduce
core-js(-pure)/features/map/some
core-js(-pure)/features/map/update
core-js(-pure)/features/map/update-or-insert
core-js(-pure)/features/map/upsert
core-js(-pure)/features/weak-set/add-all
core-js(-pure)/features/weak-set/delete-all
core-js(-pure)/features/weak-set/of
core-js(-pure)/features/weak-set/from
core-js(-pure)/features/weak-map/delete-all
core-js(-pure)/features/weak-map/of
core-js(-pure)/features/weak-map/from
core-js(-pure)/features/weak-map/upsert
```
`Map#updateOrInsert` [*examples*](http://es6.zloirock.ru/#const%20map%20%3D%20new%20Map(%5B%5B'a'%2C%202%5D%5D)%3B%0A%0Amap.updateOrInsert('a'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%204%0A%0Amap.updateOrInsert('b'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%203%0A%0Alog(map)%3B%20%2F%2F%20%3D%3E%20%7B%20'a'%3A%204%2C%20'b'%3A%203%20%7D):
`Map#upsert` [*examples*](http://es6.zloirock.ru/#const%20map%20%3D%20new%20Map(%5B%5B'a'%2C%202%5D%5D)%3B%0A%0Amap.upsert('a'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%204%0A%0Amap.upsert('b'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%203%0A%0Alog(map)%3B%20%2F%2F%20%3D%3E%20%7B%20'a'%3A%204%2C%20'b'%3A%203%20%7D):
```js
const map = new Map([['a', 2]]);

map.updateOrInsert('a', it => it ** 2, () => 3); // => 4
map.upsert('a', it => it ** 2, () => 3); // => 4

map.updateOrInsert('b', it => it ** 2, () => 3); // => 3
map.upsert('b', it => it ** 2, () => 3); // => 3

console.log(map); // => Map { 'a': 4, 'b': 3 }
```
Expand Down
4 changes: 4 additions & 0 deletions packages/core-js-compat/src/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,8 @@ const data = {
},
'esnext.map.update-or-insert': {
},
'esnext.map.upsert': {
},
'esnext.math.clamp': {
},
'esnext.math.deg-per-rad': {
Expand Down Expand Up @@ -1415,6 +1417,8 @@ const data = {
},
'esnext.weak-map.of': {
},
'esnext.weak-map.upsert': {
},
'esnext.weak-set.add-all': {
},
'esnext.weak-set.delete-all': {
Expand Down
2 changes: 2 additions & 0 deletions packages/core-js-compat/src/modules-by-versions.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@ module.exports = {
'esnext.iterator.some',
'esnext.iterator.take',
'esnext.iterator.to-array',
'esnext.map.upsert',
'esnext.weak-map.upsert',
],
};
2 changes: 2 additions & 0 deletions packages/core-js/features/map/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ require('../../modules/esnext.map.merge');
require('../../modules/esnext.map.reduce');
require('../../modules/esnext.map.some');
require('../../modules/esnext.map.update');
// TODO: remove from `core-js@4`
require('../../modules/esnext.map.update-or-insert');
require('../../modules/esnext.map.upsert');
1 change: 1 addition & 0 deletions packages/core-js/features/map/update-or-insert.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// TODO: remove from `core-js@4`
require('../../modules/es.map');
require('../../modules/esnext.map.update-or-insert');
var entryUnbind = require('../../internals/entry-unbind');
Expand Down
5 changes: 5 additions & 0 deletions packages/core-js/features/map/upsert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require('../../modules/es.map');
require('../../modules/esnext.map.upsert');
var entryUnbind = require('../../internals/entry-unbind');

module.exports = entryUnbind('Map', 'upsert');
1 change: 1 addition & 0 deletions packages/core-js/features/weak-map/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ module.exports = require('../../es/weak-map');
require('../../modules/esnext.weak-map.from');
require('../../modules/esnext.weak-map.of');
require('../../modules/esnext.weak-map.delete-all');
require('../../modules/esnext.weak-map.upsert');
5 changes: 5 additions & 0 deletions packages/core-js/features/weak-map/upsert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require('../../modules/es.weak-map');
require('../../modules/esnext.weak-map.upsert');
var entryUnbind = require('../../internals/entry-unbind');

module.exports = entryUnbind('WeakMap', 'upsert');
22 changes: 22 additions & 0 deletions packages/core-js/internals/map-upsert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';
var anObject = require('../internals/an-object');

// `Map.prototype.upsert` method
// https://github.com/thumbsupep/proposal-upsert
module.exports = function upsert(key, updateFn, insertFn) {
var map = anObject(this);
if (typeof updateFn != 'function' && typeof insertFn != 'function') {
throw TypeError('At least one callback required');
}
var value;
if (map.has(key)) {
value = map.get(key);
if (typeof updateFn == 'function') {
value = updateFn(value);
map.set(key, value);
}
} else if (typeof insertFn == 'function') {
value = insertFn();
map.set(key, value);
} return value;
};
17 changes: 5 additions & 12 deletions packages/core-js/modules/esnext.map.update-or-insert.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
'use strict';
// TODO: remove from `core-js@4`
var $ = require('../internals/export');
var IS_PURE = require('../internals/is-pure');
var anObject = require('../internals/an-object');
var aFunction = require('../internals/a-function');
var $upsert = require('../internals/map-upsert');

// `Set.prototype.updateOrInsert` method
// https://docs.google.com/presentation/d/1_xtrGSoN1-l2Q74eCXPHBbbrBHsVyqArWN0ebnW-pVQ/
// `Map.prototype.updateOrInsert` method (replaced by `Map.prototype.upsert`)
// https://github.com/thumbsupep/proposal-upsert
$({ target: 'Map', proto: true, real: true, forced: IS_PURE }, {
updateOrInsert: function updateOrInsert(key, onUpdate, onInsert) {
var map = anObject(this);
aFunction(onUpdate);
aFunction(onInsert);
var value = map.has(key) ? onUpdate(map.get(key)) : onInsert();
map.set(key, value);
return value;
}
updateOrInsert: $upsert
});
10 changes: 10 additions & 0 deletions packages/core-js/modules/esnext.map.upsert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';
var $ = require('../internals/export');
var IS_PURE = require('../internals/is-pure');
var $upsert = require('../internals/map-upsert');

// `Map.prototype.upsert` method
// https://github.com/thumbsupep/proposal-upsert
$({ target: 'Map', proto: true, real: true, forced: IS_PURE }, {
upsert: $upsert
});
10 changes: 10 additions & 0 deletions packages/core-js/modules/esnext.weak-map.upsert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';
var $ = require('../internals/export');
var IS_PURE = require('../internals/is-pure');
var $upsert = require('../internals/map-upsert');

// `WeakMap.prototype.upsert` method
// https://github.com/thumbsupep/proposal-upsert
$({ target: 'WeakMap', proto: true, real: true, forced: IS_PURE }, {
upsert: $upsert
});
3 changes: 2 additions & 1 deletion packages/core-js/proposals/map-update-or-insert.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
require('../modules/esnext.map.update-or-insert');
// TODO: remove from `core-js@4`
require('./map-upsert');
5 changes: 5 additions & 0 deletions packages/core-js/proposals/map-upsert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// https://github.com/thumbsupep/proposal-upsert
// TODO: remove from `core-js@4`
require('../modules/esnext.map.update-or-insert');
require('../modules/esnext.map.upsert');
require('../modules/esnext.weak-map.upsert');
2 changes: 1 addition & 1 deletion packages/core-js/stage/1.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ require('../proposals/array-last');
require('../proposals/collection-methods');
require('../proposals/collection-of-from');
require('../proposals/keys-composition');
require('../proposals/map-update-or-insert');
require('../proposals/map-upsert');
require('../proposals/math-extensions');
require('../proposals/math-signbit');
require('../proposals/number-from-string');
Expand Down
2 changes: 2 additions & 0 deletions tests/commonjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) {
ok(load('features/map/some')(new Map([[1, 2], [2, 3], [3, 4]]), it => it % 2) === true);
ok(load('features/map/update')(new Map([[1, 2]]), 1, it => it * 2).get(1) === 4);
ok(load('features/map/update-or-insert')(new Map([[1, 2]]), 1, it => it ** 2, () => 42) === 4);
ok(load('features/map/upsert')(new Map([[1, 2]]), 1, it => it ** 2, () => 42) === 4);
ok(load('features/set/add-all')(new Set([1, 2, 3]), 4, 5).size === 5);
ok(load('features/set/delete-all')(new Set([1, 2, 3]), 4, 5) === false);
ok(load('features/set/difference')(new Set([1, 2, 3]), [3, 4, 5]).size === 2);
Expand All @@ -343,6 +344,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) {
ok(load('features/set/symmetric-difference')(new Set([1, 2, 3]), [3, 4, 5]).size === 4);
ok(load('features/set/union')(new Set([1, 2, 3]), [3, 4, 5]).size === 5);
ok(load('features/weak-map/delete-all')(new WeakMap(), [], {}) === false);
ok(load('features/weak-map/upsert')(new WeakMap(), {}, null, () => 42) === 42);
ok(load('features/weak-set/add-all')(new WeakSet(), [], {}) instanceof WeakSet);
ok(load('features/weak-set/delete-all')(new WeakSet(), [], {}) === false);
let Promise = load('features/promise');
Expand Down
9 changes: 9 additions & 0 deletions tests/compat/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,12 @@ GLOBAL.tests = {
'esnext.map.update': function () {
return Map.prototype.update;
},
'esnext.map.update-or-insert': function () {
return Map.prototype.updateOrInsert;
},
'esnext.map.upsert': function () {
return Map.prototype.upsert;
},
'esnext.math.clamp': function () {
return Math.clamp;
},
Expand Down Expand Up @@ -1327,6 +1333,9 @@ GLOBAL.tests = {
'esnext.weak-map.of': function () {
return WeakMap.of;
},
'esnext.weak-map.upsert': function () {
return WeakMap.prototype.upsert;
},
'esnext.weak-set.add-all': function () {
return WeakSet.prototype.addAll;
},
Expand Down
7 changes: 4 additions & 3 deletions tests/pure/esnext.map.update-or-insert.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ QUnit.test('Map#updateOrInsert', assert => {
const { updateOrInsert } = Map.prototype;
assert.isFunction(updateOrInsert);
assert.arity(updateOrInsert, 3);
assert.name(updateOrInsert, 'updateOrInsert');
assert.name(updateOrInsert, 'upsert');
assert.nonEnumerable(Map.prototype, 'updateOrInsert');

const map = new Map([['a', 2]]);
Expand All @@ -27,9 +27,10 @@ QUnit.test('Map#updateOrInsert', assert => {
assert.same(map.get('a'), 4, 'correct result #1');
assert.same(map.get('b'), 3, 'correct result #2');

assert.throws(() => new Map([['a', 2]]).updateOrInsert('b', null, () => 3), TypeError);
assert.throws(() => new Map([['a', 2]]).updateOrInsert('a', value => value ** 2), TypeError);
assert.same(new Map([['a', 2]]).updateOrInsert('b', null, () => 3), 3);
assert.same(new Map([['a', 2]]).updateOrInsert('a', value => value ** 2), 4);

assert.throws(() => new Map().updateOrInsert('a'), TypeError);
assert.throws(() => updateOrInsert.call({}, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError);
assert.throws(() => updateOrInsert.call([], 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError);
assert.throws(() => updateOrInsert.call(undefined, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError);
Expand Down
38 changes: 38 additions & 0 deletions tests/pure/esnext.map.upsert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Map from 'core-js-pure/features/map';

QUnit.test('Map#upsert', assert => {
const { upsert } = Map.prototype;
assert.isFunction(upsert);
assert.arity(upsert, 3);
assert.name(upsert, 'upsert');
assert.nonEnumerable(Map.prototype, 'upsert');

const map = new Map([['a', 2]]);
assert.same(map.upsert('a', function (value) {
assert.same(arguments.length, 1, 'correct number of callback arguments');
assert.same(value, 2, 'correct value in callback');
return value ** 2;
}, () => {
assert.ok(false, 'should not be called');
return 3;
}), 4, 'returns a correct value');
assert.same(map.upsert('b', value => {
assert.ok(false, 'should not be called');
return value ** 2;
}, function () {
assert.same(arguments.length, 0, 'correct number of callback arguments');
return 3;
}), 3, 'returns a correct value');
assert.same(map.size, 2, 'correct size');
assert.same(map.get('a'), 4, 'correct result #1');
assert.same(map.get('b'), 3, 'correct result #2');

assert.same(new Map([['a', 2]]).upsert('b', null, () => 3), 3);
assert.same(new Map([['a', 2]]).upsert('a', value => value ** 2), 4);

assert.throws(() => new Map().upsert('a'), TypeError);
assert.throws(() => upsert.call({}, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError);
assert.throws(() => upsert.call([], 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError);
assert.throws(() => upsert.call(undefined, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError);
assert.throws(() => upsert.call(null, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError);
});

0 comments on commit d38d01c

Please sign in to comment.