whatwg-url 5.0.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,29 +2,32 @@
2
2
 
3
3
  whatwg-url is a full implementation of the WHATWG [URL Standard](https://url.spec.whatwg.org/). It can be used standalone, but it also exposes a lot of the internal algorithms that are useful for integrating a URL parser into a project like [jsdom](https://github.com/tmpvar/jsdom).
4
4
 
5
- ## Current Status
5
+ ## Specification conformance
6
6
 
7
- whatwg-url is currently up to date with the URL spec up to commit [a62223](https://github.com/whatwg/url/commit/a622235308342c9adc7fc2fd1659ff059f7d5e2a).
7
+ whatwg-url is currently up to date with the URL spec up to commit [7ae1c69](https://github.com/whatwg/url/commit/7ae1c691c96f0d82fafa24c33aa1e8df9ffbf2bc).
8
+
9
+ For `file:` URLs, whose [origin is left unspecified](https://url.spec.whatwg.org/#concept-url-origin), whatwg-url chooses to use a new opaque origin (which serializes to `"null"`).
8
10
 
9
11
  ## API
10
12
 
11
- ### The `URL` Constructor
13
+ ### The `URL` and `URLSearchParams` classes
12
14
 
13
- The main API is the [`URL`](https://url.spec.whatwg.org/#url) export, which follows the spec's behavior in all ways (including e.g. `USVString` conversion). Most consumers of this library will want to use this.
15
+ The main API is provided by the [`URL`](https://url.spec.whatwg.org/#url-class) and [`URLSearchParams`](https://url.spec.whatwg.org/#interface-urlsearchparams) exports, which follows the spec's behavior in all ways (including e.g. `USVString` conversion). Most consumers of this library will want to use these.
14
16
 
15
17
  ### Low-level URL Standard API
16
18
 
17
- The following methods are exported for use by places like jsdom that need to implement things like [`HTMLHyperlinkElementUtils`](https://html.spec.whatwg.org/#htmlhyperlinkelementutils). They operate on or return an "internal URL" or ["URL record"](https://url.spec.whatwg.org/#concept-url) type.
19
+ The following methods are exported for use by places like jsdom that need to implement things like [`HTMLHyperlinkElementUtils`](https://html.spec.whatwg.org/#htmlhyperlinkelementutils). They mostly operate on or return an "internal URL" or ["URL record"](https://url.spec.whatwg.org/#concept-url) type.
18
20
 
19
21
  - [URL parser](https://url.spec.whatwg.org/#concept-url-parser): `parseURL(input, { baseURL, encodingOverride })`
20
22
  - [Basic URL parser](https://url.spec.whatwg.org/#concept-basic-url-parser): `basicURLParse(input, { baseURL, encodingOverride, url, stateOverride })`
21
23
  - [URL serializer](https://url.spec.whatwg.org/#concept-url-serializer): `serializeURL(urlRecord, excludeFragment)`
22
24
  - [Host serializer](https://url.spec.whatwg.org/#concept-host-serializer): `serializeHost(hostFromURLRecord)`
23
25
  - [Serialize an integer](https://url.spec.whatwg.org/#serialize-an-integer): `serializeInteger(number)`
24
- - [Origin](https://url.spec.whatwg.org/#concept-url-origin) [serializer](https://html.spec.whatwg.org/multipage/browsers.html#serialization-of-an-origin): `serializeURLOrigin(urlRecord)`
26
+ - [Origin](https://url.spec.whatwg.org/#concept-url-origin) [serializer](https://html.spec.whatwg.org/multipage/origin.html#ascii-serialisation-of-an-origin): `serializeURLOrigin(urlRecord)`
25
27
  - [Set the username](https://url.spec.whatwg.org/#set-the-username): `setTheUsername(urlRecord, usernameString)`
26
28
  - [Set the password](https://url.spec.whatwg.org/#set-the-password): `setThePassword(urlRecord, passwordString)`
27
29
  - [Cannot have a username/password/port](https://url.spec.whatwg.org/#cannot-have-a-username-password-port): `cannotHaveAUsernamePasswordPort(urlRecord)`
30
+ - [Percent decode](https://url.spec.whatwg.org/#percent-decode): `percentDecode(buffer)`
28
31
 
29
32
  The `stateOverride` parameter is one of the following strings:
30
33
 
@@ -64,4 +67,32 @@ The URL record type has the following API:
64
67
 
65
68
  These properties should be treated with care, as in general changing them will cause the URL record to be in an inconsistent state until the appropriate invocation of `basicURLParse` is used to fix it up. You can see examples of this in the URL Standard, where there are many step sequences like "4. Set context object’s url’s fragment to the empty string. 5. Basic URL parse _input_ with context object’s url as _url_ and fragment state as _state override_." In between those two steps, a URL record is in an unusable state.
66
69
 
67
- The return value of "failure" in the spec is represented by the string `"failure"`. That is, functions like `parseURL` and `basicURLParse` can return _either_ a URL record _or_ the string `"failure"`.
70
+ The return value of "failure" in the spec is represented by `null`. That is, functions like `parseURL` and `basicURLParse` can return _either_ a URL record _or_ `null`.
71
+
72
+ ## Development instructions
73
+
74
+ First, install [Node.js](https://nodejs.org/). Then, fetch the dependencies of whatwg-url, by running from this directory:
75
+
76
+ npm install
77
+
78
+ To run tests:
79
+
80
+ npm test
81
+
82
+ To generate a coverage report:
83
+
84
+ npm run coverage
85
+
86
+ To build and run the live viewer:
87
+
88
+ npm run build
89
+ npm run build-live-viewer
90
+
91
+ Serve the contents of the `live-viewer` directory using any web server.
92
+
93
+ ## Supporting whatwg-url
94
+
95
+ The jsdom project (including whatwg-url) is a community-driven project maintained by a team of [volunteers](https://github.com/orgs/jsdom/people). You could support us by:
96
+
97
+ - [Getting professional support for whatwg-url](https://tidelift.com/subscription/pkg/npm-whatwg-url?utm_source=npm-whatwg-url&utm_medium=referral&utm_campaign=readme) as part of a Tidelift subscription. Tidelift helps making open source sustainable for us while giving teams assurances for maintenance, licensing, and security.
98
+ - Contributing directly to the project.
package/lib/URL-impl.js CHANGED
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  const usm = require("./url-state-machine");
3
+ const urlencoded = require("./urlencoded");
4
+ const URLSearchParams = require("./URLSearchParams");
3
5
 
4
6
  exports.implementation = class URLImpl {
5
7
  constructor(constructorArgs) {
@@ -9,19 +11,24 @@ exports.implementation = class URLImpl {
9
11
  let parsedBase = null;
10
12
  if (base !== undefined) {
11
13
  parsedBase = usm.basicURLParse(base);
12
- if (parsedBase === "failure") {
13
- throw new TypeError("Invalid base URL");
14
+ if (parsedBase === null) {
15
+ throw new TypeError(`Invalid base URL: ${base}`);
14
16
  }
15
17
  }
16
18
 
17
19
  const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
18
- if (parsedURL === "failure") {
19
- throw new TypeError("Invalid URL");
20
+ if (parsedURL === null) {
21
+ throw new TypeError(`Invalid URL: ${url}`);
20
22
  }
21
23
 
24
+ const query = parsedURL.query !== null ? parsedURL.query : "";
25
+
22
26
  this._url = parsedURL;
23
27
 
24
- // TODO: query stuff
28
+ // We cannot invoke the "new URLSearchParams object" algorithm without going through the constructor, which strips
29
+ // question mark by default. Therefore the doNotStripQMark hack is used.
30
+ this._query = URLSearchParams.createImpl([query], { doNotStripQMark: true });
31
+ this._query._url = this;
25
32
  }
26
33
 
27
34
  get href() {
@@ -30,11 +37,17 @@ exports.implementation = class URLImpl {
30
37
 
31
38
  set href(v) {
32
39
  const parsedURL = usm.basicURLParse(v);
33
- if (parsedURL === "failure") {
34
- throw new TypeError("Invalid URL");
40
+ if (parsedURL === null) {
41
+ throw new TypeError(`Invalid URL: ${v}`);
35
42
  }
36
43
 
37
44
  this._url = parsedURL;
45
+
46
+ this._query._list.splice(0);
47
+ const { query } = parsedURL;
48
+ if (query !== null) {
49
+ this._query._list = urlencoded.parseUrlencoded(query);
50
+ }
38
51
  }
39
52
 
40
53
  get origin() {
@@ -161,18 +174,22 @@ exports.implementation = class URLImpl {
161
174
  }
162
175
 
163
176
  set search(v) {
164
- // TODO: query stuff
165
-
166
177
  const url = this._url;
167
178
 
168
179
  if (v === "") {
169
180
  url.query = null;
181
+ this._query._list = [];
170
182
  return;
171
183
  }
172
184
 
173
185
  const input = v[0] === "?" ? v.substring(1) : v;
174
186
  url.query = "";
175
187
  usm.basicURLParse(input, { url, stateOverride: "query" });
188
+ this._query._list = urlencoded.parseUrlencoded(input);
189
+ }
190
+
191
+ get searchParams() {
192
+ return this._query;
176
193
  }
177
194
 
178
195
  get hash() {
package/lib/URL.js CHANGED
@@ -2,195 +2,334 @@
2
2
 
3
3
  const conversions = require("webidl-conversions");
4
4
  const utils = require("./utils.js");
5
- const Impl = require(".//URL-impl.js");
6
5
 
7
6
  const impl = utils.implSymbol;
8
7
 
9
- function URL(url) {
10
- if (!this || this[impl] || !(this instanceof URL)) {
11
- throw new TypeError("Failed to construct 'URL': Please use the 'new' operator, this DOM object constructor cannot be called as a function.");
8
+ class URL {
9
+ constructor(url) {
10
+ if (arguments.length < 1) {
11
+ throw new TypeError("Failed to construct 'URL': 1 argument required, but only " + arguments.length + " present.");
12
+ }
13
+ const args = [];
14
+ {
15
+ let curArg = arguments[0];
16
+ curArg = conversions["USVString"](curArg, { context: "Failed to construct 'URL': parameter 1" });
17
+ args.push(curArg);
18
+ }
19
+ {
20
+ let curArg = arguments[1];
21
+ if (curArg !== undefined) {
22
+ curArg = conversions["USVString"](curArg, { context: "Failed to construct 'URL': parameter 2" });
23
+ }
24
+ args.push(curArg);
25
+ }
26
+ return iface.setup(Object.create(new.target.prototype), args);
12
27
  }
13
- if (arguments.length < 1) {
14
- throw new TypeError("Failed to construct 'URL': 1 argument required, but only " + arguments.length + " present.");
28
+
29
+ toJSON() {
30
+ if (!this || !module.exports.is(this)) {
31
+ throw new TypeError("Illegal invocation");
32
+ }
33
+
34
+ return this[impl].toJSON();
15
35
  }
16
- const args = [];
17
- for (let i = 0; i < arguments.length && i < 2; ++i) {
18
- args[i] = arguments[i];
36
+
37
+ get href() {
38
+ if (!this || !module.exports.is(this)) {
39
+ throw new TypeError("Illegal invocation");
40
+ }
41
+
42
+ return this[impl]["href"];
19
43
  }
20
- args[0] = conversions["USVString"](args[0]);
21
- if (args[1] !== undefined) {
22
- args[1] = conversions["USVString"](args[1]);
44
+
45
+ set href(V) {
46
+ if (!this || !module.exports.is(this)) {
47
+ throw new TypeError("Illegal invocation");
48
+ }
49
+
50
+ V = conversions["USVString"](V, { context: "Failed to set the 'href' property on 'URL': The provided value" });
51
+
52
+ this[impl]["href"] = V;
23
53
  }
24
54
 
25
- module.exports.setup(this, args);
26
- }
55
+ toString() {
56
+ if (!this || !module.exports.is(this)) {
57
+ throw new TypeError("Illegal invocation");
58
+ }
59
+ return this[impl]["href"];
60
+ }
61
+
62
+ get origin() {
63
+ if (!this || !module.exports.is(this)) {
64
+ throw new TypeError("Illegal invocation");
65
+ }
27
66
 
28
- URL.prototype.toJSON = function toJSON() {
29
- if (!this || !module.exports.is(this)) {
30
- throw new TypeError("Illegal invocation");
67
+ return this[impl]["origin"];
31
68
  }
32
- const args = [];
33
- for (let i = 0; i < arguments.length && i < 0; ++i) {
34
- args[i] = arguments[i];
69
+
70
+ get protocol() {
71
+ if (!this || !module.exports.is(this)) {
72
+ throw new TypeError("Illegal invocation");
73
+ }
74
+
75
+ return this[impl]["protocol"];
35
76
  }
36
- return this[impl].toJSON.apply(this[impl], args);
37
- };
38
- Object.defineProperty(URL.prototype, "href", {
39
- get() {
40
- return this[impl].href;
41
- },
42
- set(V) {
43
- V = conversions["USVString"](V);
44
- this[impl].href = V;
45
- },
46
- enumerable: true,
47
- configurable: true
48
- });
49
77
 
50
- URL.prototype.toString = function () {
51
- if (!this || !module.exports.is(this)) {
52
- throw new TypeError("Illegal invocation");
78
+ set protocol(V) {
79
+ if (!this || !module.exports.is(this)) {
80
+ throw new TypeError("Illegal invocation");
81
+ }
82
+
83
+ V = conversions["USVString"](V, { context: "Failed to set the 'protocol' property on 'URL': The provided value" });
84
+
85
+ this[impl]["protocol"] = V;
53
86
  }
54
- return this.href;
55
- };
56
87
 
57
- Object.defineProperty(URL.prototype, "origin", {
58
- get() {
59
- return this[impl].origin;
60
- },
61
- enumerable: true,
62
- configurable: true
63
- });
88
+ get username() {
89
+ if (!this || !module.exports.is(this)) {
90
+ throw new TypeError("Illegal invocation");
91
+ }
64
92
 
65
- Object.defineProperty(URL.prototype, "protocol", {
66
- get() {
67
- return this[impl].protocol;
68
- },
69
- set(V) {
70
- V = conversions["USVString"](V);
71
- this[impl].protocol = V;
72
- },
73
- enumerable: true,
74
- configurable: true
75
- });
93
+ return this[impl]["username"];
94
+ }
76
95
 
77
- Object.defineProperty(URL.prototype, "username", {
78
- get() {
79
- return this[impl].username;
80
- },
81
- set(V) {
82
- V = conversions["USVString"](V);
83
- this[impl].username = V;
84
- },
85
- enumerable: true,
86
- configurable: true
87
- });
96
+ set username(V) {
97
+ if (!this || !module.exports.is(this)) {
98
+ throw new TypeError("Illegal invocation");
99
+ }
88
100
 
89
- Object.defineProperty(URL.prototype, "password", {
90
- get() {
91
- return this[impl].password;
92
- },
93
- set(V) {
94
- V = conversions["USVString"](V);
95
- this[impl].password = V;
96
- },
97
- enumerable: true,
98
- configurable: true
99
- });
101
+ V = conversions["USVString"](V, { context: "Failed to set the 'username' property on 'URL': The provided value" });
100
102
 
101
- Object.defineProperty(URL.prototype, "host", {
102
- get() {
103
- return this[impl].host;
104
- },
105
- set(V) {
106
- V = conversions["USVString"](V);
107
- this[impl].host = V;
108
- },
109
- enumerable: true,
110
- configurable: true
111
- });
103
+ this[impl]["username"] = V;
104
+ }
112
105
 
113
- Object.defineProperty(URL.prototype, "hostname", {
114
- get() {
115
- return this[impl].hostname;
116
- },
117
- set(V) {
118
- V = conversions["USVString"](V);
119
- this[impl].hostname = V;
120
- },
121
- enumerable: true,
122
- configurable: true
123
- });
106
+ get password() {
107
+ if (!this || !module.exports.is(this)) {
108
+ throw new TypeError("Illegal invocation");
109
+ }
124
110
 
125
- Object.defineProperty(URL.prototype, "port", {
126
- get() {
127
- return this[impl].port;
128
- },
129
- set(V) {
130
- V = conversions["USVString"](V);
131
- this[impl].port = V;
132
- },
133
- enumerable: true,
134
- configurable: true
135
- });
111
+ return this[impl]["password"];
112
+ }
136
113
 
137
- Object.defineProperty(URL.prototype, "pathname", {
138
- get() {
139
- return this[impl].pathname;
140
- },
141
- set(V) {
142
- V = conversions["USVString"](V);
143
- this[impl].pathname = V;
144
- },
145
- enumerable: true,
146
- configurable: true
147
- });
114
+ set password(V) {
115
+ if (!this || !module.exports.is(this)) {
116
+ throw new TypeError("Illegal invocation");
117
+ }
148
118
 
149
- Object.defineProperty(URL.prototype, "search", {
150
- get() {
151
- return this[impl].search;
152
- },
153
- set(V) {
154
- V = conversions["USVString"](V);
155
- this[impl].search = V;
156
- },
157
- enumerable: true,
158
- configurable: true
159
- });
119
+ V = conversions["USVString"](V, { context: "Failed to set the 'password' property on 'URL': The provided value" });
160
120
 
161
- Object.defineProperty(URL.prototype, "hash", {
162
- get() {
163
- return this[impl].hash;
164
- },
165
- set(V) {
166
- V = conversions["USVString"](V);
167
- this[impl].hash = V;
168
- },
169
- enumerable: true,
170
- configurable: true
171
- });
121
+ this[impl]["password"] = V;
122
+ }
123
+
124
+ get host() {
125
+ if (!this || !module.exports.is(this)) {
126
+ throw new TypeError("Illegal invocation");
127
+ }
128
+
129
+ return this[impl]["host"];
130
+ }
131
+
132
+ set host(V) {
133
+ if (!this || !module.exports.is(this)) {
134
+ throw new TypeError("Illegal invocation");
135
+ }
136
+
137
+ V = conversions["USVString"](V, { context: "Failed to set the 'host' property on 'URL': The provided value" });
138
+
139
+ this[impl]["host"] = V;
140
+ }
141
+
142
+ get hostname() {
143
+ if (!this || !module.exports.is(this)) {
144
+ throw new TypeError("Illegal invocation");
145
+ }
146
+
147
+ return this[impl]["hostname"];
148
+ }
149
+
150
+ set hostname(V) {
151
+ if (!this || !module.exports.is(this)) {
152
+ throw new TypeError("Illegal invocation");
153
+ }
154
+
155
+ V = conversions["USVString"](V, { context: "Failed to set the 'hostname' property on 'URL': The provided value" });
156
+
157
+ this[impl]["hostname"] = V;
158
+ }
159
+
160
+ get port() {
161
+ if (!this || !module.exports.is(this)) {
162
+ throw new TypeError("Illegal invocation");
163
+ }
164
+
165
+ return this[impl]["port"];
166
+ }
167
+
168
+ set port(V) {
169
+ if (!this || !module.exports.is(this)) {
170
+ throw new TypeError("Illegal invocation");
171
+ }
172
+
173
+ V = conversions["USVString"](V, { context: "Failed to set the 'port' property on 'URL': The provided value" });
174
+
175
+ this[impl]["port"] = V;
176
+ }
177
+
178
+ get pathname() {
179
+ if (!this || !module.exports.is(this)) {
180
+ throw new TypeError("Illegal invocation");
181
+ }
182
+
183
+ return this[impl]["pathname"];
184
+ }
185
+
186
+ set pathname(V) {
187
+ if (!this || !module.exports.is(this)) {
188
+ throw new TypeError("Illegal invocation");
189
+ }
190
+
191
+ V = conversions["USVString"](V, { context: "Failed to set the 'pathname' property on 'URL': The provided value" });
192
+
193
+ this[impl]["pathname"] = V;
194
+ }
172
195
 
196
+ get search() {
197
+ if (!this || !module.exports.is(this)) {
198
+ throw new TypeError("Illegal invocation");
199
+ }
173
200
 
174
- module.exports = {
201
+ return this[impl]["search"];
202
+ }
203
+
204
+ set search(V) {
205
+ if (!this || !module.exports.is(this)) {
206
+ throw new TypeError("Illegal invocation");
207
+ }
208
+
209
+ V = conversions["USVString"](V, { context: "Failed to set the 'search' property on 'URL': The provided value" });
210
+
211
+ this[impl]["search"] = V;
212
+ }
213
+
214
+ get searchParams() {
215
+ if (!this || !module.exports.is(this)) {
216
+ throw new TypeError("Illegal invocation");
217
+ }
218
+
219
+ return utils.getSameObject(this, "searchParams", () => {
220
+ return utils.tryWrapperForImpl(this[impl]["searchParams"]);
221
+ });
222
+ }
223
+
224
+ get hash() {
225
+ if (!this || !module.exports.is(this)) {
226
+ throw new TypeError("Illegal invocation");
227
+ }
228
+
229
+ return this[impl]["hash"];
230
+ }
231
+
232
+ set hash(V) {
233
+ if (!this || !module.exports.is(this)) {
234
+ throw new TypeError("Illegal invocation");
235
+ }
236
+
237
+ V = conversions["USVString"](V, { context: "Failed to set the 'hash' property on 'URL': The provided value" });
238
+
239
+ this[impl]["hash"] = V;
240
+ }
241
+ }
242
+ Object.defineProperties(URL.prototype, {
243
+ toJSON: { enumerable: true },
244
+ href: { enumerable: true },
245
+ toString: { enumerable: true },
246
+ origin: { enumerable: true },
247
+ protocol: { enumerable: true },
248
+ username: { enumerable: true },
249
+ password: { enumerable: true },
250
+ host: { enumerable: true },
251
+ hostname: { enumerable: true },
252
+ port: { enumerable: true },
253
+ pathname: { enumerable: true },
254
+ search: { enumerable: true },
255
+ searchParams: { enumerable: true },
256
+ hash: { enumerable: true },
257
+ [Symbol.toStringTag]: { value: "URL", configurable: true }
258
+ });
259
+ const iface = {
260
+ // When an interface-module that implements this interface as a mixin is loaded, it will append its own `.is()`
261
+ // method into this array. It allows objects that directly implements *those* interfaces to be recognized as
262
+ // implementing this mixin interface.
263
+ _mixedIntoPredicates: [],
175
264
  is(obj) {
176
- return !!obj && obj[impl] instanceof Impl.implementation;
265
+ if (obj) {
266
+ if (utils.hasOwn(obj, impl) && obj[impl] instanceof Impl.implementation) {
267
+ return true;
268
+ }
269
+ for (const isMixedInto of module.exports._mixedIntoPredicates) {
270
+ if (isMixedInto(obj)) {
271
+ return true;
272
+ }
273
+ }
274
+ }
275
+ return false;
276
+ },
277
+ isImpl(obj) {
278
+ if (obj) {
279
+ if (obj instanceof Impl.implementation) {
280
+ return true;
281
+ }
282
+
283
+ const wrapper = utils.wrapperForImpl(obj);
284
+ for (const isMixedInto of module.exports._mixedIntoPredicates) {
285
+ if (isMixedInto(wrapper)) {
286
+ return true;
287
+ }
288
+ }
289
+ }
290
+ return false;
291
+ },
292
+ convert(obj, { context = "The provided value" } = {}) {
293
+ if (module.exports.is(obj)) {
294
+ return utils.implForWrapper(obj);
295
+ }
296
+ throw new TypeError(`${context} is not of type 'URL'.`);
177
297
  },
298
+
178
299
  create(constructorArgs, privateData) {
179
300
  let obj = Object.create(URL.prototype);
180
- this.setup(obj, constructorArgs, privateData);
301
+ obj = this.setup(obj, constructorArgs, privateData);
181
302
  return obj;
182
303
  },
304
+ createImpl(constructorArgs, privateData) {
305
+ let obj = Object.create(URL.prototype);
306
+ obj = this.setup(obj, constructorArgs, privateData);
307
+ return utils.implForWrapper(obj);
308
+ },
309
+ _internalSetup(obj) {},
183
310
  setup(obj, constructorArgs, privateData) {
184
311
  if (!privateData) privateData = {};
312
+
185
313
  privateData.wrapper = obj;
186
314
 
187
- obj[impl] = new Impl.implementation(constructorArgs, privateData);
315
+ this._internalSetup(obj);
316
+ Object.defineProperty(obj, impl, {
317
+ value: new Impl.implementation(constructorArgs, privateData),
318
+ configurable: true
319
+ });
320
+
188
321
  obj[impl][utils.wrapperSymbol] = obj;
322
+ if (Impl.init) {
323
+ Impl.init(obj[impl], privateData);
324
+ }
325
+ return obj;
189
326
  },
190
327
  interface: URL,
191
328
  expose: {
192
- Window: { URL: URL },
193
- Worker: { URL: URL }
329
+ Window: { URL },
330
+ Worker: { URL }
194
331
  }
195
- };
332
+ }; // iface
333
+ module.exports = iface;
196
334
 
335
+ const Impl = require("./URL-impl.js");