Skip to content

Commit 62bc8d3

Browse files
authored
Merge pull request #920 from wheresrhys/rhys/is-subset-of
fix: remove deprecated is-subset-of
2 parents c9d7eb2 + 7877887 commit 62bc8d3

File tree

6 files changed

+336
-21
lines changed

6 files changed

+336
-21
lines changed

docs/docs/Usage/configuration.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Sets a `Content-Length` header on each response, with the exception of responses
2525

2626
`{Boolean}` default: `false`
2727

28-
Match calls that only partially match a specified body json. Uses the [is-subset](https://www.npmjs.com/package/is-subset) library under the hood, which implements behaviour the same as jest's [.objectContaining()](https://jestjs.io/docs/en/expect#expectobjectcontainingobject) method.
28+
Match calls that only partially match a specified body json or FormData.
2929

3030
### allowRelativeUrls<sup>†</sup>
3131

package-lock.json

-18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/fetch-mock/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
"@types/glob-to-regexp": "^0.4.4",
2525
"dequal": "^2.0.3",
2626
"glob-to-regexp": "^0.4.1",
27-
"is-subset-of": "^3.1.10",
2827
"regexparam": "^3.0.0"
2928
},
3029
"repository": {

packages/fetch-mock/src/IsSubsetOf.ts

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
/** Copied from deprecated https://www.npmjs.com/package/is-subset-of **/
3+
import { Type } from './TypeDescriptor.js';
4+
5+
const allowedTypes = new Set(['array', 'object', 'function', 'null']);
6+
7+
const isSubsetOf = function (
8+
subset: any[] | Record<string, any | undefined> | null,
9+
superset: any[] | Record<string, any | undefined> | null,
10+
visited: string[] = [],
11+
): boolean {
12+
const subsetType = Type.of(subset);
13+
const supersetType = Type.of(superset);
14+
15+
if (!allowedTypes.has(subsetType)) {
16+
throw new Error(`Type '${subsetType}' is not supported.`);
17+
}
18+
if (!allowedTypes.has(supersetType)) {
19+
throw new Error(`Type '${supersetType}' is not supported.`);
20+
}
21+
22+
if (Type.isFunction(subset)) {
23+
if (!Type.isFunction(superset)) {
24+
throw new Error(
25+
`Types '${subsetType}' and '${supersetType}' do not match.`,
26+
);
27+
}
28+
29+
return subset.toString() === superset.toString();
30+
}
31+
32+
if (Type.isArray(subset)) {
33+
if (!Type.isArray(superset)) {
34+
throw new Error(
35+
`Types '${subsetType}' and '${supersetType}' do not match.`,
36+
);
37+
}
38+
if (subset.length > superset.length) {
39+
return false;
40+
}
41+
42+
for (const subsetItem of subset) {
43+
const subsetItemType = Type.of(subsetItem);
44+
45+
let isItemInSuperset;
46+
47+
switch (subsetItemType) {
48+
case 'array':
49+
case 'object':
50+
case 'function': {
51+
if (visited.includes(subsetItem)) {
52+
continue;
53+
}
54+
55+
visited.push(subsetItem);
56+
57+
isItemInSuperset = superset.some((supersetItem: any): boolean => {
58+
try {
59+
return isSubsetOf(subsetItem, supersetItem, visited);
60+
} catch {
61+
return false;
62+
}
63+
});
64+
break;
65+
}
66+
default: {
67+
isItemInSuperset = superset.includes(subsetItem);
68+
}
69+
}
70+
71+
if (!isItemInSuperset) {
72+
return false;
73+
}
74+
}
75+
76+
return true;
77+
}
78+
79+
if (Type.isObject(subset)) {
80+
if (!Type.isObject(superset) || Type.isArray(superset)) {
81+
throw new Error(
82+
`Types '${subsetType}' and '${supersetType}' do not match.`,
83+
);
84+
}
85+
if (Object.keys(subset).length > Object.keys(superset).length) {
86+
return false;
87+
}
88+
89+
for (const [subsetKey, subsetValue] of Object.entries(subset)) {
90+
const supersetValue = superset[subsetKey];
91+
92+
const subsetValueType = Type.of(subsetValue);
93+
94+
switch (subsetValueType) {
95+
case 'array':
96+
case 'object':
97+
case 'function': {
98+
if (visited.includes(subsetValue)) {
99+
continue;
100+
}
101+
102+
visited.push(subsetValue);
103+
104+
try {
105+
const isInSuperset = isSubsetOf(
106+
subsetValue,
107+
supersetValue,
108+
visited,
109+
);
110+
111+
if (!isInSuperset) {
112+
return false;
113+
}
114+
} catch {
115+
return false;
116+
}
117+
break;
118+
}
119+
default: {
120+
if (subsetValue !== supersetValue) {
121+
return false;
122+
}
123+
}
124+
}
125+
}
126+
127+
return true;
128+
}
129+
130+
if (Type.isNull(subset)) {
131+
if (!Type.isNull(superset)) {
132+
throw new Error(
133+
`Types '${subsetType}' and '${supersetType}' do not match.`,
134+
);
135+
}
136+
137+
return true;
138+
}
139+
140+
throw new Error('Invalid operation.');
141+
};
142+
143+
isSubsetOf.structural = function (
144+
subset: Record<string, any> | null,
145+
superset: Record<string, any> | null,
146+
visited: string[] = [],
147+
): boolean {
148+
if (!Type.isObject(subset)) {
149+
throw new Error(`Type '${Type.of(subset)}' is not supported.`);
150+
}
151+
152+
if (!Type.isObject(superset)) {
153+
throw new Error(`Type '${Type.of(superset)}' is not supported.`);
154+
}
155+
156+
for (const [subsetKey, subsetValue] of Object.entries(subset)) {
157+
if (superset[subsetKey] === undefined) {
158+
return false;
159+
}
160+
161+
const subsetValueType = Type.of(subsetValue);
162+
const supersetValue = superset[subsetKey];
163+
164+
if (subsetValueType === 'object') {
165+
if (visited.includes(subsetValue)) {
166+
continue;
167+
}
168+
169+
visited.push(subsetValue);
170+
171+
try {
172+
const isInSuperset = isSubsetOf.structural(
173+
subsetValue,
174+
supersetValue,
175+
visited,
176+
);
177+
178+
if (!isInSuperset) {
179+
return false;
180+
}
181+
} catch {
182+
return false;
183+
}
184+
}
185+
}
186+
187+
return true;
188+
};
189+
190+
export { isSubsetOf };

packages/fetch-mock/src/Matchers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { RouteConfig } from './Route.js';
22
import { CallLog } from './CallHistory.js';
33
import glob from 'glob-to-regexp';
44
import * as regexparam from 'regexparam';
5-
import { isSubsetOf } from 'is-subset-of';
5+
import { isSubsetOf } from './IsSubsetOf.js';
66
import { dequal as isEqual } from 'dequal';
77
import { normalizeHeaders, getPath, normalizeUrl } from './RequestUtils.js';
88

0 commit comments

Comments
 (0)