From a4efc52870a5958f0b4199b109088065f5d1746c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:26:24 +0000 Subject: [PATCH 1/2] Initial plan From bc2592cc8b7d6ee709df14ff7953ffcf73ef60b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:31:32 +0000 Subject: [PATCH 2/2] feat: add polyfill for new Set methods (union, intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, isDisjointFrom) Co-authored-by: mattcosta7 <8616962+mattcosta7@users.noreply.github.com> --- src/index.ts | 2 + src/set-methods.ts | 131 +++++++++++++++++++++++++++++++++++++++ test/set-methods.js | 148 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 src/set-methods.ts create mode 100644 test/set-methods.js diff --git a/src/index.ts b/src/index.ts index 3824bd6..0e03ff2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import * as objectGroupBy from './object-groupby.js' import * as mapGroupBy from './map-groupby.js' import * as promiseTry from './promise-try.js' import * as iteratorHelpers from './iterator-helpers.js' +import * as setMethods from './set-methods.js' let supportsModalPseudo = false try { @@ -61,6 +62,7 @@ export const polyfills = { mapGroupBy, promiseTry, iteratorHelpers, + setMethods, } export function isSupported() { diff --git a/src/set-methods.ts b/src/set-methods.ts new file mode 100644 index 0000000..b9b1d67 --- /dev/null +++ b/src/set-methods.ts @@ -0,0 +1,131 @@ +// Set-like interface per TC39 spec: must have has, keys, and size +interface SetLike { + has(value: T): boolean + keys(): IterableIterator + readonly size: number +} + +/*#__PURE__*/ +export function union(this: Set, other: SetLike): Set { + const result = new Set(this) + for (const value of other.keys()) { + result.add(value) + } + return result +} + +/*#__PURE__*/ +export function intersection(this: Set, other: SetLike): Set { + const result = new Set() + for (const value of this) { + if (other.has(value)) { + result.add(value) + } + } + return result +} + +/*#__PURE__*/ +export function difference(this: Set, other: SetLike): Set { + const result = new Set() + for (const value of this) { + if (!other.has(value)) { + result.add(value) + } + } + return result +} + +/*#__PURE__*/ +export function symmetricDifference(this: Set, other: SetLike): Set { + const result = new Set(this) + for (const value of other.keys()) { + if (result.has(value)) { + result.delete(value) + } else { + result.add(value) + } + } + return result +} + +/*#__PURE__*/ +export function isSubsetOf(this: Set, other: SetLike): boolean { + if (this.size > other.size) return false + for (const value of this) { + if (!other.has(value)) { + return false + } + } + return true +} + +/*#__PURE__*/ +export function isSupersetOf(this: Set, other: SetLike): boolean { + for (const value of other.keys()) { + if (!this.has(value)) { + return false + } + } + return true +} + +/*#__PURE__*/ +export function isDisjointFrom(this: Set, other: SetLike): boolean { + if (this.size <= other.size) { + for (const value of this) { + if (other.has(value)) { + return false + } + } + } else { + for (const value of other.keys()) { + if (this.has(value)) { + return false + } + } + } + return true +} + +/*#__PURE__*/ +export function isSupported(): boolean { + return ( + 'union' in Set.prototype && + 'intersection' in Set.prototype && + 'difference' in Set.prototype && + 'symmetricDifference' in Set.prototype && + 'isSubsetOf' in Set.prototype && + 'isSupersetOf' in Set.prototype && + 'isDisjointFrom' in Set.prototype + ) +} + +/*#__PURE__*/ +export function isPolyfilled(): boolean { + const proto = Set.prototype as unknown as Record + return ( + 'union' in Set.prototype && + proto['union'] === union && + proto['intersection'] === intersection && + proto['difference'] === difference && + proto['symmetricDifference'] === symmetricDifference && + proto['isSubsetOf'] === isSubsetOf && + proto['isSupersetOf'] === isSupersetOf && + proto['isDisjointFrom'] === isDisjointFrom + ) +} + +export function apply(): void { + if (!isSupported()) { + Object.assign(Set.prototype, { + union, + intersection, + difference, + symmetricDifference, + isSubsetOf, + isSupersetOf, + isDisjointFrom, + }) + } +} diff --git a/test/set-methods.js b/test/set-methods.js new file mode 100644 index 0000000..bfecbb8 --- /dev/null +++ b/test/set-methods.js @@ -0,0 +1,148 @@ +import {expect} from 'chai' +import { + apply, + isPolyfilled, + isSupported, + union, + intersection, + difference, + symmetricDifference, + isSubsetOf, + isSupersetOf, + isDisjointFrom, +} from '../src/set-methods.ts' + +// eslint-disable-next-line i18n-text/no-en +describe('Set methods', () => { + it('has standard isSupported, isPolyfilled, apply API', () => { + expect(isSupported).to.be.a('function') + expect(isPolyfilled).to.be.a('function') + expect(apply).to.be.a('function') + expect(isSupported()).to.be.a('boolean') + expect(isPolyfilled()).to.equal(false) + }) + + describe('union', () => { + it('returns a new Set with elements from both sets', () => { + const a = new Set([1, 2, 3]) + const b = new Set([3, 4, 5]) + const result = union.call(a, b) + expect(result).to.be.instanceof(Set) + expect([...result].sort()).to.eql([1, 2, 3, 4, 5]) + }) + + it('handles empty sets', () => { + const a = new Set([1, 2]) + const result = union.call(a, new Set()) + expect([...result].sort()).to.eql([1, 2]) + }) + }) + + describe('intersection', () => { + it('returns a new Set with elements in both sets', () => { + const a = new Set([1, 2, 3]) + const b = new Set([2, 3, 4]) + const result = intersection.call(a, b) + expect(result).to.be.instanceof(Set) + expect([...result].sort()).to.eql([2, 3]) + }) + + it('returns empty set when no common elements', () => { + const a = new Set([1, 2]) + const b = new Set([3, 4]) + const result = intersection.call(a, b) + expect(result.size).to.equal(0) + }) + }) + + describe('difference', () => { + it('returns a new Set with elements in this but not other', () => { + const a = new Set([1, 2, 3]) + const b = new Set([2, 3, 4]) + const result = difference.call(a, b) + expect(result).to.be.instanceof(Set) + expect([...result]).to.eql([1]) + }) + + it('returns a copy of this when no overlap', () => { + const a = new Set([1, 2]) + const b = new Set([3, 4]) + const result = difference.call(a, b) + expect([...result].sort()).to.eql([1, 2]) + }) + }) + + describe('symmetricDifference', () => { + it('returns elements in one set but not both', () => { + const a = new Set([1, 2, 3]) + const b = new Set([2, 3, 4]) + const result = symmetricDifference.call(a, b) + expect(result).to.be.instanceof(Set) + expect([...result].sort()).to.eql([1, 4]) + }) + + it('returns empty set for identical sets', () => { + const a = new Set([1, 2]) + const result = symmetricDifference.call(a, new Set([1, 2])) + expect(result.size).to.equal(0) + }) + }) + + describe('isSubsetOf', () => { + it('returns true when all elements are in the other set', () => { + const a = new Set([1, 2]) + const b = new Set([1, 2, 3]) + expect(isSubsetOf.call(a, b)).to.equal(true) + }) + + it('returns false when some elements are not in the other set', () => { + const a = new Set([1, 2, 4]) + const b = new Set([1, 2, 3]) + expect(isSubsetOf.call(a, b)).to.equal(false) + }) + + it('returns true for empty set', () => { + const a = new Set() + const b = new Set([1, 2]) + expect(isSubsetOf.call(a, b)).to.equal(true) + }) + }) + + describe('isSupersetOf', () => { + it('returns true when this set contains all elements of other', () => { + const a = new Set([1, 2, 3]) + const b = new Set([1, 2]) + expect(isSupersetOf.call(a, b)).to.equal(true) + }) + + it('returns false when other has elements not in this', () => { + const a = new Set([1, 2]) + const b = new Set([1, 2, 3]) + expect(isSupersetOf.call(a, b)).to.equal(false) + }) + + it('returns true when other is empty', () => { + const a = new Set([1, 2]) + expect(isSupersetOf.call(a, new Set())).to.equal(true) + }) + }) + + describe('isDisjointFrom', () => { + it('returns true when sets have no common elements', () => { + const a = new Set([1, 2]) + const b = new Set([3, 4]) + expect(isDisjointFrom.call(a, b)).to.equal(true) + }) + + it('returns false when sets share elements', () => { + const a = new Set([1, 2, 3]) + const b = new Set([3, 4]) + expect(isDisjointFrom.call(a, b)).to.equal(false) + }) + + it('returns true when either set is empty', () => { + const a = new Set([1, 2]) + expect(isDisjointFrom.call(a, new Set())).to.equal(true) + }) + }) +})