@d1g1tal/transportr 1.4.2 → 2.0.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.
@@ -1,818 +1,604 @@
1
- // node_modules/.pnpm/@d1g1tal+collections@0.2.5/node_modules/@d1g1tal/collections/src/set-multi-map.js
2
- var SetMultiMap = class extends Map {
1
+ // node_modules/.pnpm/@d1g1tal+media-type@6.0.4/node_modules/@d1g1tal/media-type/dist/media-type.js
2
+ var httpTokenCodePoints = /^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u;
3
+ var matcher = /(["\\])/ug;
4
+ var httpQuotedStringTokenCodePoints = /^[\t\u0020-\u007E\u0080-\u00FF]*$/u;
5
+ var MediaTypeParameters = class _MediaTypeParameters extends Map {
3
6
  /**
4
- * Adds a new element with a specified key and value to the SetMultiMap.
5
- * If an element with the same key already exists, the value will be added to the underlying {@link Set}.
6
- * If the value already exists in the {@link Set}, it will not be added again.
7
+ * Create a new MediaTypeParameters instance.
7
8
  *
8
- * @param {K} key The key to set.
9
- * @param {V} value The value to add to the SetMultiMap
10
- * @returns {SetMultiMap<K, V>} The SetMultiMap with the updated key and value.
9
+ * @param entries An array of [ name, value ] tuples.
11
10
  */
12
- set(key, value) {
13
- super.set(key, (super.get(key) ?? /* @__PURE__ */ new Set()).add(value));
14
- return this;
11
+ constructor(entries2 = []) {
12
+ super(entries2);
15
13
  }
16
14
  /**
17
- * Checks if a specific key has a specific value.
15
+ * Indicates whether the supplied name and value are valid media type parameters.
18
16
  *
19
- * @param {K} key The key to check.
20
- * @param {V} value The value to check.
21
- * @returns {boolean} True if the key has the value, false otherwise.
17
+ * @param name The name of the media type parameter to validate.
18
+ * @param value The media type parameter value to validate.
19
+ * @returns true if the media type parameter is valid, false otherwise.
22
20
  */
23
- hasValue(key, value) {
24
- const values = super.get(key);
25
- return values ? values.has(value) : false;
21
+ static isValid(name, value) {
22
+ return httpTokenCodePoints.test(name) && httpQuotedStringTokenCodePoints.test(value);
26
23
  }
27
24
  /**
28
- * Removes a specific value from a specific key.
25
+ * Gets the media type parameter value for the supplied name.
29
26
  *
30
- * @param {K} key The key to remove the value from.
31
- * @param {V} value The value to remove.
32
- * @returns {boolean} True if the value was removed, false otherwise.
27
+ * @param name The name of the media type parameter to retrieve.
28
+ * @returns The media type parameter value.
33
29
  */
34
- deleteValue(key, value) {
35
- const values = super.get(key);
36
- if (values) {
37
- return values.delete(value);
38
- }
39
- return false;
40
- }
41
- get [Symbol.toStringTag]() {
42
- return "SetMultiMap";
30
+ get(name) {
31
+ return super.get(name.toLowerCase());
43
32
  }
44
- };
45
-
46
- // node_modules/.pnpm/@d1g1tal+subscribr@3.0.2/node_modules/@d1g1tal/subscribr/src/context-event-handler.js
47
- var ContextEventHandler = class {
48
- #context;
49
- #eventHandler;
50
33
  /**
51
- * @param {*} context The context to bind to the event handler.
52
- * @param {function(*): void} eventHandler The event handler to call when the event is published.
34
+ * Indicates whether the media type parameter with the specified name exists or not.
35
+ *
36
+ * @param name The name of the media type parameter to check.
37
+ * @returns true if the media type parameter exists, false otherwise.
53
38
  */
54
- constructor(context, eventHandler) {
55
- this.#context = context;
56
- this.#eventHandler = eventHandler;
39
+ has(name) {
40
+ return super.has(name.toLowerCase());
57
41
  }
58
42
  /**
59
- * Call the event handler for the provided event.
43
+ * Adds a new media type parameter using the specified name and value to the MediaTypeParameters.
44
+ * If an parameter with the same name already exists, the parameter will be updated.
60
45
  *
61
- * @param {Event} event The event to handle
62
- * @param {*} [data] The value to be passed to the event handler as a parameter.
46
+ * @param name The name of the media type parameter to set.
47
+ * @param value The media type parameter value.
48
+ * @returns This instance.
63
49
  */
64
- handle(event, data) {
65
- this.#eventHandler.call(this.#context, event, data);
66
- }
67
- get [Symbol.toStringTag]() {
68
- return "ContextEventHandler";
50
+ set(name, value) {
51
+ if (!_MediaTypeParameters.isValid(name, value)) {
52
+ throw new Error(`Invalid media type parameter name/value: ${name}/${value}`);
53
+ }
54
+ super.set(name.toLowerCase(), value);
55
+ return this;
69
56
  }
70
- };
71
-
72
- // node_modules/.pnpm/@d1g1tal+subscribr@3.0.2/node_modules/@d1g1tal/subscribr/src/subscription.js
73
- var Subscription = class {
74
- #eventName;
75
- #contextEventHandler;
76
57
  /**
77
- * @param {string} eventName The event name.
78
- * @param {ContextEventHandler} contextEventHandler Then context event handler.
58
+ * Removes the media type parameter using the specified name.
59
+ *
60
+ * @param name The name of the media type parameter to delete.
61
+ * @returns true if the parameter existed and has been removed, or false if the parameter does not exist.
79
62
  */
80
- constructor(eventName, contextEventHandler) {
81
- this.#eventName = eventName;
82
- this.#contextEventHandler = contextEventHandler;
63
+ delete(name) {
64
+ return super.delete(name.toLowerCase());
83
65
  }
84
66
  /**
85
- * Gets the event name for the subscription.
67
+ * Returns a string representation of the media type parameters.
86
68
  *
87
- * @returns {string} The event name.
69
+ * @returns The string representation of the media type parameters.
88
70
  */
89
- get eventName() {
90
- return this.#eventName;
71
+ toString() {
72
+ return Array.from(this).map(([name, value]) => `;${name}=${!value || !httpTokenCodePoints.test(value) ? `"${value.replace(matcher, "\\$1")}"` : value}`).join("");
91
73
  }
92
74
  /**
93
- * Gets the context event handler.
75
+ * Returns the name of this class.
94
76
  *
95
- * @returns {ContextEventHandler} The context event handler
77
+ * @returns The name of this class.
96
78
  */
97
- get contextEventHandler() {
98
- return this.#contextEventHandler;
99
- }
100
79
  get [Symbol.toStringTag]() {
101
- return "Subscription";
80
+ return "MediaTypeParameters";
102
81
  }
103
82
  };
104
-
105
- // node_modules/.pnpm/@d1g1tal+subscribr@3.0.2/node_modules/@d1g1tal/subscribr/src/subscribr.js
106
- var Subscribr = class {
107
- /** @type {SetMultiMap<string, ContextEventHandler>} */
108
- #subscribers;
109
- constructor() {
110
- this.#subscribers = new SetMultiMap();
83
+ var whitespaceChars = /* @__PURE__ */ new Set([" ", " ", "\n", "\r"]);
84
+ var trailingWhitespace = /[ \t\n\r]+$/u;
85
+ var leadingAndTrailingWhitespace = /^[ \t\n\r]+|[ \t\n\r]+$/ug;
86
+ var MediaTypeParser = class _MediaTypeParser {
87
+ /**
88
+ * Function to parse a media type.
89
+ * @param input The media type to parse
90
+ * @returns An object populated with the parsed media type properties and any parameters.
91
+ */
92
+ static parse(input) {
93
+ input = input.replace(leadingAndTrailingWhitespace, "");
94
+ let position = 0;
95
+ const [type, typeEnd] = _MediaTypeParser.collect(input, position, ["/"]);
96
+ position = typeEnd;
97
+ if (!type.length || position >= input.length || !httpTokenCodePoints.test(type)) {
98
+ throw new TypeError(_MediaTypeParser.generateErrorMessage("type", type));
99
+ }
100
+ ++position;
101
+ const [subtype, subtypeEnd] = _MediaTypeParser.collect(input, position, [";"], true, true);
102
+ position = subtypeEnd;
103
+ if (!subtype.length || !httpTokenCodePoints.test(subtype)) {
104
+ throw new TypeError(_MediaTypeParser.generateErrorMessage("subtype", subtype));
105
+ }
106
+ const parameters = new MediaTypeParameters();
107
+ while (position < input.length) {
108
+ ++position;
109
+ while (whitespaceChars.has(input[position])) {
110
+ ++position;
111
+ }
112
+ let name;
113
+ [name, position] = _MediaTypeParser.collect(input, position, [";", "="], false);
114
+ if (position >= input.length || input[position] === ";") {
115
+ continue;
116
+ }
117
+ ++position;
118
+ let value;
119
+ if (input[position] === '"') {
120
+ [value, position] = _MediaTypeParser.collectHttpQuotedString(input, position);
121
+ while (position < input.length && input[position] !== ";") {
122
+ ++position;
123
+ }
124
+ } else {
125
+ [value, position] = _MediaTypeParser.collect(input, position, [";"], false, true);
126
+ if (!value) {
127
+ continue;
128
+ }
129
+ }
130
+ if (name && MediaTypeParameters.isValid(name, value) && !parameters.has(name)) {
131
+ parameters.set(name, value);
132
+ }
133
+ }
134
+ return { type, subtype, parameters };
111
135
  }
112
136
  /**
113
- * Subscribe to an event
114
- *
115
- * @param {string} eventName The event name to subscribe to.
116
- * @param {function(Event, *): void} eventHandler The event handler to call when the event is published.
117
- * @param {*} [context] The context to bind to the event handler.
118
- * @returns {Subscription} An object used to check if the subscription still exists and to unsubscribe from the event.
137
+ * Gets the name of this class.
138
+ * @returns The string tag of this class.
119
139
  */
120
- subscribe(eventName, eventHandler, context = eventHandler) {
121
- const contextEventHandler = new ContextEventHandler(context, eventHandler);
122
- this.#subscribers.set(eventName, contextEventHandler);
123
- return new Subscription(eventName, contextEventHandler);
140
+ get [Symbol.toStringTag]() {
141
+ return "MediaTypeParser";
124
142
  }
125
143
  /**
126
- * Unsubscribe from the event
127
- *
128
- * @param {Subscription} subscription The subscription to unsubscribe.
129
- * @param {string} subscription.eventName The event name to subscribe to.
130
- * @param {ContextEventHandler} subscription.contextEventHandler The event handler to call when the event is published.
131
- * @returns {boolean} true if eventListener has been removed successfully. false if the value is not found or if the value is not an object.
144
+ * Collects characters from `input` starting at `pos` until a stop character is found.
145
+ * @param input The input string.
146
+ * @param pos The starting position.
147
+ * @param stopChars Characters that end collection.
148
+ * @param lowerCase Whether to ASCII-lowercase the result.
149
+ * @param trim Whether to strip trailing HTTP whitespace.
150
+ * @returns A tuple of the collected string and the updated position.
132
151
  */
133
- unsubscribe({ eventName, contextEventHandler }) {
134
- const contextEventHandlers = this.#subscribers.get(eventName);
135
- const removed = contextEventHandlers?.delete(contextEventHandler);
136
- if (removed && contextEventHandlers.size == 0) {
137
- this.#subscribers.delete(eventName);
152
+ static collect(input, pos, stopChars, lowerCase = true, trim = false) {
153
+ let result = "";
154
+ for (const { length } = input; pos < length && !stopChars.includes(input[pos]); pos++) {
155
+ result += input[pos];
138
156
  }
139
- return removed;
157
+ if (lowerCase) {
158
+ result = result.toLowerCase();
159
+ }
160
+ if (trim) {
161
+ result = result.replace(trailingWhitespace, "");
162
+ }
163
+ return [result, pos];
140
164
  }
141
165
  /**
142
- * Publish an event
143
- *
144
- * @param {string} eventName The name of the event.
145
- * @param {Event} [event=new CustomEvent(eventName)] The event to be handled.
146
- * @param {*} [data] The value to be passed to the event handler as a parameter.
166
+ * Collects all the HTTP quoted strings.
167
+ * This variant only implements it with the extract-value flag set.
168
+ * @param input The string to process.
169
+ * @param position The starting position.
170
+ * @returns An array that includes the resulting string and updated position.
147
171
  */
148
- publish(eventName, event = new CustomEvent(eventName), data) {
149
- if (data == null && !(event instanceof Event)) {
150
- [data, event] = [event, new CustomEvent(eventName)];
172
+ static collectHttpQuotedString(input, position) {
173
+ let value = "";
174
+ for (let length = input.length, char; ++position < length; ) {
175
+ if ((char = input[position]) === '"') {
176
+ break;
177
+ }
178
+ value += char == "\\" && ++position < length ? input[position] : char;
151
179
  }
152
- this.#subscribers.get(eventName)?.forEach((contextEventHandler) => contextEventHandler.handle(event, data));
180
+ return [value, position];
153
181
  }
154
182
  /**
155
- * Check if the event and handler are subscribed.
156
- *
157
- * @param {Subscription} subscription The subscription object.
158
- * @param {string} subscription.eventName The name of the event to check.
159
- * @param {ContextEventHandler} subscription.contextEventHandler The event handler to check.
160
- * @returns {boolean} true if the event name and handler are subscribed, false otherwise.
183
+ * Generates an error message.
184
+ * @param component The component name.
185
+ * @param value The component value.
186
+ * @returns The error message.
161
187
  */
162
- isSubscribed({ eventName, contextEventHandler }) {
163
- return this.#subscribers.get(eventName)?.has(contextEventHandler);
164
- }
165
- get [Symbol.toStringTag]() {
166
- return "Subscribr";
188
+ static generateErrorMessage(component, value) {
189
+ return `Invalid ${component} "${value}": only HTTP token code points are valid.`;
167
190
  }
168
191
  };
169
-
170
- // src/http-request-methods.js
171
- var HttpRequestMethod = {
192
+ var MediaType = class _MediaType {
193
+ _type;
194
+ _subtype;
195
+ _parameters;
172
196
  /**
173
- * The OPTIONS method represents a request for information about the communication options available on the
174
- * request/response chain identified by the Request-URI. This method allows the client to determine the options and/or
175
- * requirements associated with a resource, or the capabilities of a server, without implying a resource action or
176
- * initiating a resource retrieval.
177
- *
178
- * Responses to this method are not cacheable.
179
- *
180
- * If the OPTIONS request includes an entity-body (as indicated by the presence of Content-Length or
181
- * Transfer-Encoding), then the media type MUST be indicated by a Content-Type field. Although this specification does
182
- * not define any use for such a body, future extensions to HTTP might use the OPTIONS body to make more detailed
183
- * queries on the server. A server that does not support such an extension MAY discard the request body.
184
- *
185
- * If the Request-URI is an asterisk ("*"), the OPTIONS request is intended to apply to the server in general rather
186
- * than to a specific resource. Since a server's communication options typically depend on the resource, the "*"
187
- * request is only useful as a "ping" or "no-op" type of method, it does nothing beyond allowing the client to test the
188
- * capabilities of the server. For example, this can be used to test a proxy for HTTP/1.1 compliance (or lack thereof).
189
- *
190
- * If the Request-URI is not an asterisk, the OPTIONS request applies only to the options that are available when
191
- * communicating with that resource.
192
- *
193
- * A 200 response SHOULD include any header fields that indicate optional features implemented by the server and
194
- * applicable to that resource (e.g., Allow), possibly including extensions not defined by this specification. The
195
- * response body, if any, SHOULD also include information about the communication options. The format for such a body
196
- * is not defined by this specification, but might be defined by future extensions to HTTP. Content negotiation MAY be
197
- * used to select the appropriate response format. If no response body is included, the response MUST include a
198
- * Content-Length field with a field-value of "0".
199
- *
200
- * The Max-Forwards request-header field MAY be used to target a specific proxy in the request chain. When a proxy
201
- * receives an OPTIONS request on an absoluteURI for which request forwarding is permitted, the proxy MUST check for a
202
- * Max-Forwards field. If the Max-Forwards field-value is zero ("0"), the proxy MUST NOT forward the message, instead,
203
- * the proxy SHOULD respond with its own communication options. If the Max-Forwards field-value is an integer greater
204
- * than zero, the proxy MUST decrement the field-value when it forwards the request. If no Max-Forwards field is
205
- * present in the request, then the forwarded request MUST NOT include a Max-Forwards field.
197
+ * Create a new MediaType instance from a string representation.
198
+ * @param mediaType The media type to parse.
199
+ * @param parameters Optional parameters.
206
200
  */
207
- OPTIONS: "OPTIONS",
201
+ constructor(mediaType, parameters = {}) {
202
+ if (parameters === null || typeof parameters !== "object" || Array.isArray(parameters)) {
203
+ throw new TypeError("The parameters argument must be an object");
204
+ }
205
+ ({ type: this._type, subtype: this._subtype, parameters: this._parameters } = MediaTypeParser.parse(mediaType));
206
+ for (const [name, value] of Object.entries(parameters)) {
207
+ this._parameters.set(name, value);
208
+ }
209
+ }
208
210
  /**
209
- * The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI. If
210
- * the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity in
211
- * the response and not the source text of the process, unless that text happens to be the output of the process.
212
- *
213
- * The semantics of the GET method change to a "conditional GET" if the request message includes an If-Modified-Since;
214
- * If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. A conditional GET method requests that the
215
- * entity be transferred only under the circumstances described by the conditional header field(s). The conditional GET
216
- * method is intended to reduce unnecessary network usage by allowing cached entities to be refreshed without requiring
217
- * multiple requests or transferring data already held by the client.
218
- *
219
- * The semantics of the GET method change to a "partial GET" if the request message includes a Range header field. A
220
- * partial GET requests that only part of the entity be transferred, as described in section 14.35. The partial GET
221
- * method is intended to reduce unnecessary network usage by allowing partially-retrieved entities to be completed
222
- * without transferring data already held by the client.
223
- *
224
- * The response to a GET request is cacheable if and only if it meets the requirements for HTTP caching described in
225
- * section 13.
226
- *
227
- * See section 15.1.3 for security considerations when used for forms.
211
+ * Parses a media type string.
212
+ * @param mediaType The media type to parse.
213
+ * @returns The parsed media type or null if the mediaType cannot be parsed.
228
214
  */
229
- GET: "GET",
215
+ static parse(mediaType) {
216
+ try {
217
+ return new _MediaType(mediaType);
218
+ } catch {
219
+ }
220
+ return null;
221
+ }
230
222
  /**
231
- * The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. The
232
- * meta information contained in the HTTP headers in response to a HEAD request SHOULD be identical to the information
233
- * sent in response to a GET request. This method can be used for obtaining meta information about the entity implied by
234
- * the request without transferring the entity-body itself. This method is often used for testing hypertext links for
235
- * validity, accessibility, and recent modification.
236
- *
237
- * The response to a HEAD request MAY be cacheable in the sense that the information contained in the response MAY be
238
- * used to update a previously cached entity from that resource. If the new field values indicate that the cached
239
- * entity differs from the current entity (as would be indicated by a change in Content-Length, Content-MD5, ETag or
240
- * Last-Modified), then the cache MUST treat the cache entry as stale.
223
+ * Gets the type.
224
+ * @returns The type.
241
225
  */
242
- HEAD: "HEAD",
226
+ get type() {
227
+ return this._type;
228
+ }
243
229
  /**
244
- * The POST method is used to request that the origin server accept the entity enclosed in the request as a new
245
- * subordinate of the resource identified by the Request-URI in the Request-Line. POST is designed to allow a uniform
246
- * method to cover the following functions:
247
- * <ul>
248
- * <li>Annotation of existing resources,</li>
249
- * <li>Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles,</li>
250
- * <li>Providing a block of data, such as the result of submitting a form, to a data-handling process,</li>
251
- * <li>Extending a database through an append operation.</li>
252
- * </ul>
253
- *
254
- * The actual function performed by the POST method is determined by the server and is usually dependent on the
255
- * Request-URI. The posted entity is subordinate to that URI in the same way that a file is subordinate to a directory
256
- * containing it, a news article is subordinate to a newsgroup to which it is posted, or a record is subordinate to a
257
- * database.
258
- *
259
- * The action performed by the POST method might not result in a resource that can be identified by a URI. In this
260
- * case, either 200 (OK) or 204 (No Content) is the appropriate response status, depending on whether or not the
261
- * response includes an entity that describes the result.
262
- *
263
- * If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity
264
- * which describes the status of the request and refers to the new resource, and a Location header (see section 14.30).
265
- *
266
- * Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header
267
- * fields. However, the 303 (See Other) response can be used to direct the user agent to retrieve a cacheable resource.
268
- *
269
- * POST requests MUST obey the message transmission requirements set out in section 8.2.
270
- *
271
- * See section 15.1.3 for security considerations.
230
+ * Gets the subtype.
231
+ * @returns The subtype.
272
232
  */
273
- POST: "POST",
233
+ get subtype() {
234
+ return this._subtype;
235
+ }
274
236
  /**
275
- * The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers
276
- * to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing
277
- * on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being
278
- * defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. If
279
- * a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. If an
280
- * existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate
281
- * successful completion of the request. If the resource could not be created or modified with the Request-URI, an
282
- * appropriate error response SHOULD be given that reflects the nature of the problem. The recipient of the entity MUST
283
- * \NOT ignore any Content-* (e.g. Content-Range) headers that it does not understand or implement and MUST return a
284
- * 501 (Not Implemented) response in such cases.
285
- *
286
- * If the request passes through a cache and the Request-URI identifies one or more currently cached entities, those
287
- * entries SHOULD be treated as stale. Responses to this method are not cacheable.
288
- *
289
- * The fundamental difference between the POST and PUT requests is reflected in the different meaning of the
290
- * Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource
291
- * might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations.
292
- * In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what
293
- * URI is intended and the server MUST NOT attempt to apply the request to some other resource. If the server desires
294
- * that the request be applied to a different URI, it MUST send a 301 (Moved Permanently) response, the user agent MAY
295
- * then make its own decision regarding whether or not to redirect the request.
296
- *
297
- * A single resource MAY be identified by many different URIs. For example, an article might have a URI for identifying
298
- * "the current version" which is separate from the URI identifying each particular version. In this case, a PUT
299
- * request on a general URI might result in several other URIs being defined by the origin server.
300
- *
301
- * HTTP/1.1 does not define how a PUT method affects the state of an origin server.
302
- *
303
- * PUT requests MUST obey the message transmission requirements set out in section 8.2.
304
- *
305
- * Unless otherwise specified for a particular entity-header, the entity-headers in the PUT request SHOULD be applied
306
- * to the resource created or modified by the PUT.
237
+ * Gets the media type essence (type/subtype).
238
+ * @returns The media type without any parameters
307
239
  */
308
- PUT: "PUT",
240
+ get essence() {
241
+ return `${this._type}/${this._subtype}`;
242
+ }
309
243
  /**
310
- * The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY
311
- * be overridden by human intervention (or other means) on the origin server. The client cannot be guaranteed that the
312
- * operation has been carried out, even if the status code returned from the origin server indicates that the action
313
- * has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response
314
- * is given, it intends to delete the resource or move it to an inaccessible location.
315
- *
316
- * A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if
317
- * the action has not yet been enacted, or 204 (No Content) if the action has been enacted but the response does not
318
- * include an entity.
319
- *
320
- * If the request passes through a cache and the Request-URI identifies one or more currently cached entities, those
321
- * entries SHOULD be treated as stale. Responses to this method are not cacheable.
244
+ * Gets the parameters.
245
+ * @returns The media type parameters.
322
246
  */
323
- DELETE: "DELETE",
247
+ get parameters() {
248
+ return this._parameters;
249
+ }
324
250
  /**
325
- * The TRACE method is used to invoke a remote, application-layer loop- back of the request message. The final
326
- * recipient of the request SHOULD reflect the message received back to the client as the entity-body of a 200 (OK)
327
- * response. The final recipient is either the origin server or the first proxy or gateway to receive a Max-Forwards
328
- * value of zero (0) in the request (see section 14.31). A TRACE request MUST NOT include an entity.
329
- *
330
- * TRACE allows the client to see what is being received at the other end of the request chain and use that data for
331
- * testing or diagnostic information. The value of the Via header field (section 14.45) is of particular interest,
332
- * since it acts as a trace of the request chain. Use of the Max-Forwards header field allows the client to limit the
333
- * length of the request chain, which is useful for testing a chain of proxies forwarding messages in an infinite loop.
251
+ * Checks if the media type matches the specified type.
334
252
  *
335
- * If the request is valid, the response SHOULD contain the entire request message in the entity-body, with a
336
- * Content-Type of "message/http". Responses to this method MUST NOT be cached.
253
+ * @param mediaType The media type to check.
254
+ * @returns true if the media type matches the specified type, false otherwise.
337
255
  */
338
- TRACE: "TRACE",
256
+ matches(mediaType) {
257
+ return typeof mediaType === "string" ? this.essence.includes(mediaType) : this._type === mediaType._type && this._subtype === mediaType._subtype;
258
+ }
339
259
  /**
340
- * This specification reserves the method name CONNECT for use with a proxy that can dynamically switch to being a
341
- * tunnel (e.g. SSL tunneling [44]).
260
+ * Gets the serialized version of the media type.
261
+ *
262
+ * @returns The serialized media type.
342
263
  */
343
- CONNECT: "CONNECT",
264
+ toString() {
265
+ return `${this.essence}${this._parameters.toString()}`;
266
+ }
344
267
  /**
345
- * The PATCH method requests that a set of changes described in the
346
- * request entity be applied to the resource identified by the Request-
347
- * URI. The set of changes is represented in a format called a "patch
348
- * document" identified by a media type. If the Request-URI does not
349
- * point to an existing resource, the server MAY create a new resource,
350
- * depending on the patch document type (whether it can logically modify
351
- * a null resource) and permissions, etc.
352
- *
353
- * The difference between the PUT and PATCH requests is reflected in the
354
- * way the server processes the enclosed entity to modify the resource
355
- * identified by the Request-URI. In a PUT request, the enclosed entity
356
- * is considered to be a modified version of the resource stored on the
357
- * origin server, and the client is requesting that the stored version
358
- * be replaced. With PATCH, however, the enclosed entity contains a set
359
- * of instructions describing how a resource currently residing on the
360
- * origin server should be modified to produce a new version. The PATCH
361
- * method affects the resource identified by the Request-URI, and it
362
- * also MAY have side effects on other resources; i.e., new resources
363
- * may be created, or existing ones modified, by the application of a
364
- * PATCH.
365
- *
366
- * PATCH is neither safe nor idempotent as defined by [RFC2616], Section
367
- * 9.1.
368
- *
369
- * A PATCH request can be issued in such a way as to be idempotent,
370
- * which also helps prevent bad outcomes from collisions between two
371
- * PATCH requests on the same resource in a similar time frame.
372
- * Collisions from multiple PATCH requests may be more dangerous than
373
- * PUT collisions because some patch formats need to operate from a
374
- * known base-point or else they will corrupt the resource. Clients
375
- * using this kind of patch application SHOULD use a conditional request
376
- * such that the request will fail if the resource has been updated
377
- * since the client last accessed the resource. For example, the client
378
- * can use a strong ETag [RFC2616] in an If-Match header on the PATCH
379
- * request.
380
- *
381
- * There are also cases where patch formats do not need to operate from
382
- * a known base-point (e.g., appending text lines to log files, or non-
383
- * colliding rows to database tables), in which case the same care in
384
- * client requests is not needed.
385
- *
386
- * The server MUST apply the entire set of changes atomically and never
387
- * provide (e.g., in response to a GET during this operation) a
388
- * partially modified representation. If the entire patch document
389
- * cannot be successfully applied, then the server MUST NOT apply any of
390
- * the changes. The determination of what constitutes a successful
391
- * PATCH can vary depending on the patch document and the type of
392
- * resource(s) being modified. For example, the common 'diff' utility
393
- * can generate a patch document that applies to multiple files in a
394
- * directory hierarchy. The atomicity requirement holds for all
395
- * directly affected files. See "Error Handling", Section 2.2, for
396
- * details on status codes and possible error conditions.
397
- *
398
- * If the request passes through a cache and the Request-URI identifies
399
- * one or more currently cached entities, those entries SHOULD be
400
- * treated as stale. A response to this method is only cacheable if it
401
- * contains explicit freshness information (such as an Expires header or
402
- * "Cache-Control: max-age" directive) as well as the Content-Location
403
- * header matching the Request-URI, indicating that the PATCH response
404
- * body is a resource representation. A cached PATCH response can only
405
- * be used to respond to subsequent GET and HEAD requests; it MUST NOT
406
- * be used to respond to other methods (in particular, PATCH).
407
- *
408
- * Note that entity-headers contained in the request apply only to the
409
- * contained patch document and MUST NOT be applied to the resource
410
- * being modified. Thus, a Content-Language header could be present on
411
- * the request, but it would only mean (for whatever that's worth) that
412
- * the patch document had a language. Servers SHOULD NOT store such
413
- * headers except as trace information, and SHOULD NOT use such header
414
- * values the same way they might be used on PUT requests. Therefore,
415
- * this document does not specify a way to modify a document's Content-
416
- * Type or Content-Language value through headers, though a mechanism
417
- * could well be designed to achieve this goal through a patch document.
418
- *
419
- * There is no guarantee that a resource can be modified with PATCH.
420
- * Further, it is expected that different patch document formats will be
421
- * appropriate for different types of resources and that no single
422
- * format will be appropriate for all types of resources. Therefore,
423
- * there is no single default patch document format that implementations
424
- * are required to support. Servers MUST ensure that a received patch
425
- * document is appropriate for the type of resource identified by the
426
- * Request-URI.
427
- *
428
- * Clients need to choose when to use PATCH rather than PUT. For
429
- * example, if the patch document size is larger than the size of the
430
- * new resource data that would be used in a PUT, then it might make
431
- * sense to use PUT instead of PATCH. A comparison to POST is even more
432
- * difficult, because POST is used in widely varying ways and can
433
- * encompass PUT and PATCH-like operations if the server chooses. If
434
- * the operation does not modify the resource identified by the Request-
435
- * URI in a predictable way, POST should be considered instead of PATCH
436
- * or PUT.
268
+ * Gets the name of the class.
269
+ * @returns The class name
437
270
  */
438
- PATCH: "PATCH"
271
+ get [Symbol.toStringTag]() {
272
+ return "MediaType";
273
+ }
439
274
  };
440
- var http_request_methods_default = HttpRequestMethod;
441
275
 
442
- // src/response-status.js
443
- var ResponseStatus = class {
444
- /** @type {number} */
445
- #code;
446
- /** @type {string} */
447
- #text;
276
+ // node_modules/.pnpm/@d1g1tal+subscribr@4.1.8_typescript@5.9.3/node_modules/@d1g1tal/subscribr/dist/subscribr.js
277
+ var SetMultiMap = class extends Map {
448
278
  /**
279
+ * Adds a new element with a specified key and value to the SetMultiMap.
280
+ * If an element with the same key already exists, the value will be added to the underlying {@link Set}.
281
+ * If the value already exists in the {@link Set}, it will not be added again.
449
282
  *
450
- * @param {number} code The status code from the {@link Response}
451
- * @param {string} text The status text from the {@link Response}
283
+ * @param key The key to set.
284
+ * @param value The value to add to the SetMultiMap
285
+ * @returns The SetMultiMap with the updated key and value.
452
286
  */
453
- constructor(code, text) {
454
- this.#code = code;
455
- this.#text = text;
287
+ set(key, value) {
288
+ super.set(key, value instanceof Set ? value : (super.get(key) ?? /* @__PURE__ */ new Set()).add(value));
289
+ return this;
456
290
  }
457
291
  /**
458
- * Returns the status code from the {@link Response}
459
- *
460
- * @returns {number} The status code.
292
+ * Finds a specific value for a specific key using an iterator function.
293
+ * @param key The key to find the value for.
294
+ * @param iterator The iterator function to use to find the value.
295
+ * @returns The value for the specified key
461
296
  */
462
- get code() {
463
- return this.#code;
297
+ find(key, iterator) {
298
+ const values = this.get(key);
299
+ if (values !== void 0) {
300
+ return Array.from(values).find(iterator);
301
+ }
302
+ return void 0;
464
303
  }
465
304
  /**
466
- * Returns the status text from the {@link Response}.
305
+ * Checks if a specific key has a specific value.
467
306
  *
468
- * @returns {string} The status text.
307
+ * @param key The key to check.
308
+ * @param value The value to check.
309
+ * @returns True if the key has the value, false otherwise.
469
310
  */
470
- get text() {
471
- return this.#text;
311
+ hasValue(key, value) {
312
+ const values = super.get(key);
313
+ return values ? values.has(value) : false;
472
314
  }
473
315
  /**
474
- * A String value that is used in the creation of the default string
475
- * description of an object. Called by the built-in method {@link Object.prototype.toString}.
476
- *
477
- * @override
478
- * @returns {string} The default string description of this object.
316
+ * Removes a specific value from a specific key.
317
+ * @param key The key to remove the value from.
318
+ * @param value The value to remove.
319
+ * @returns True if the value was removed, false otherwise.
320
+ */
321
+ deleteValue(key, value) {
322
+ if (value === void 0) {
323
+ return this.delete(key);
324
+ }
325
+ const values = super.get(key);
326
+ if (values) {
327
+ const deleted = values.delete(value);
328
+ if (values.size === 0) {
329
+ super.delete(key);
330
+ }
331
+ return deleted;
332
+ }
333
+ return false;
334
+ }
335
+ /**
336
+ * The string tag of the SetMultiMap.
337
+ * @returns The string tag of the SetMultiMap.
479
338
  */
480
339
  get [Symbol.toStringTag]() {
481
- return "ResponseStatus";
340
+ return "SetMultiMap";
482
341
  }
342
+ };
343
+ var ContextEventHandler = class {
344
+ context;
345
+ eventHandler;
483
346
  /**
484
- * tostring method for the class.
347
+ * @param context The context to bind to the event handler.
348
+ * @param eventHandler The event handler to call when the event is published.
349
+ */
350
+ constructor(context, eventHandler) {
351
+ this.context = context;
352
+ this.eventHandler = eventHandler;
353
+ }
354
+ /**
355
+ * Call the event handler for the provided event.
485
356
  *
486
- * @override
487
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString|Object.prototype.toString}
488
- * @returns {string} The status code and status text.
357
+ * @param event The event to handle
358
+ * @param data The value to be passed to the event handler as a parameter.
489
359
  */
490
- toString() {
491
- return `${this.#code} ${this.#text}`;
360
+ handle(event, data) {
361
+ this.eventHandler.call(this.context, event, data);
492
362
  }
493
- };
494
-
495
- // node_modules/.pnpm/@d1g1tal+chrysalis@2.2.0/node_modules/@d1g1tal/chrysalis/src/esm/object-type.js
496
- var _type = (object) => object?.constructor ?? object?.prototype?.constructor ?? globalThis[Object.prototype.toString.call(object).slice(8, -1)] ?? object;
497
- var object_type_default = _type;
498
-
499
- // node_modules/.pnpm/@d1g1tal+chrysalis@2.2.0/node_modules/@d1g1tal/chrysalis/src/esm/object-merge.js
500
- var _objectMerge = (...objects) => {
501
- const target = {};
502
- for (const source of objects) {
503
- if (object_type_default(source) != Object)
504
- return void 0;
505
- let descriptor, sourceType;
506
- for (const property of Object.getOwnPropertyNames(source)) {
507
- descriptor = Object.getOwnPropertyDescriptor(source, property);
508
- if (descriptor.enumerable) {
509
- sourceType = object_type_default(source[property]);
510
- if (sourceType == Object) {
511
- descriptor.value = object_type_default(target[property]) == Object ? _objectMerge(target[property], source[property]) : { ...source[property] };
512
- } else if (sourceType == Array) {
513
- descriptor.value = Array.isArray(target[property]) ? [.../* @__PURE__ */ new Set([...source[property], ...target[property]])] : [...source[property]];
514
- }
515
- target[property] = descriptor.value;
516
- }
517
- }
363
+ /**
364
+ * A String value that is used in the creation of the default string
365
+ * description of an object. Called by the built-in method {@link Object.prototype.toString}.
366
+ *
367
+ * @returns The default string description of this object.
368
+ */
369
+ get [Symbol.toStringTag]() {
370
+ return "ContextEventHandler";
518
371
  }
519
- return target;
520
- };
521
- var object_merge_default = _objectMerge;
522
-
523
- // node_modules/.pnpm/@d1g1tal+media-type@5.0.0/node_modules/@d1g1tal/media-type/src/utils.js
524
- var httpTokenCodePoints = /^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u;
525
- var utils_default = httpTokenCodePoints;
526
-
527
- // node_modules/.pnpm/@d1g1tal+media-type@5.0.0/node_modules/@d1g1tal/media-type/src/media-type-parameters.js
528
- var matcher = /(["\\])/ug;
529
- var httpQuotedStringTokenCodePoints = /^[\t\u0020-\u007E\u0080-\u00FF]*$/u;
530
- var MediaTypeParameters = class _MediaTypeParameters extends Map {
372
+ };
373
+ var Subscription = class {
374
+ _eventName;
375
+ _contextEventHandler;
531
376
  /**
532
- * Create a new MediaTypeParameters instance.
533
- *
534
- * @param {Array<[string, string]>} entries An array of [name, value] tuples.
377
+ * @param eventName The event name.
378
+ * @param contextEventHandler The context event handler.
535
379
  */
536
- constructor(entries = []) {
537
- super(entries);
380
+ constructor(eventName, contextEventHandler) {
381
+ this._eventName = eventName;
382
+ this._contextEventHandler = contextEventHandler;
538
383
  }
539
384
  /**
540
- * Indicates whether the supplied name and value are valid media type parameters.
385
+ * Gets the event name for the subscription.
541
386
  *
542
- * @static
543
- * @param {string} name The name of the media type parameter to validate.
544
- * @param {string} value The media type parameter value to validate.
545
- * @returns {boolean} true if the media type parameter is valid, false otherwise.
387
+ * @returns The event name.
546
388
  */
547
- static isValid(name, value) {
548
- return utils_default.test(name) && httpQuotedStringTokenCodePoints.test(value);
389
+ get eventName() {
390
+ return this._eventName;
549
391
  }
550
392
  /**
551
- * Gets the media type parameter value for the supplied name.
393
+ * Gets the context event handler.
552
394
  *
553
- * @param {string} name The name of the media type parameter to retrieve.
554
- * @returns {string} The media type parameter value.
395
+ * @returns The context event handler
555
396
  */
556
- get(name) {
557
- return super.get(name.toLowerCase());
397
+ get contextEventHandler() {
398
+ return this._contextEventHandler;
558
399
  }
559
400
  /**
560
- * Indicates whether the media type parameter with the specified name exists or not.
401
+ * A String value that is used in the creation of the default string
402
+ * description of an object. Called by the built-in method {@link Object.prototype.toString}.
561
403
  *
562
- * @param {string} name The name of the media type parameter to check.
563
- * @returns {boolean} true if the media type parameter exists, false otherwise.
404
+ * @returns The default string description of this object.
564
405
  */
565
- has(name) {
566
- return super.has(name.toLowerCase());
406
+ get [Symbol.toStringTag]() {
407
+ return "Subscription";
567
408
  }
409
+ };
410
+ var Subscribr = class {
411
+ subscribers = new SetMultiMap();
412
+ errorHandler;
568
413
  /**
569
- * Adds a new media type parameter using the specified name and value to the MediaTypeParameters.
570
- * If an parameter with the same name already exists, the parameter will be updated.
414
+ * Set a custom error handler for handling errors that occur in event listeners.
415
+ * If not set, errors will be logged to the console.
571
416
  *
572
- * @param {string} name The name of the media type parameter to set.
573
- * @param {string} value The media type parameter value.
574
- * @returns {MediaTypeParameters} This instance.
417
+ * @param errorHandler The error handler function to call when an error occurs in an event listener.
575
418
  */
576
- set(name, value) {
577
- if (!_MediaTypeParameters.isValid(name, value)) {
578
- throw new Error(`Invalid media type parameter name/value: ${name}/${value}`);
579
- }
580
- super.set(name.toLowerCase(), value);
581
- return this;
419
+ setErrorHandler(errorHandler) {
420
+ this.errorHandler = errorHandler;
582
421
  }
583
422
  /**
584
- * Removes the media type parameter using the specified name.
423
+ * Subscribe to an event
585
424
  *
586
- * @param {string} name The name of the media type parameter to delete.
587
- * @returns {boolean} true if the parameter existed and has been removed, or false if the parameter does not exist.
588
- */
589
- delete(name) {
590
- return super.delete(name.toLowerCase());
425
+ * @param eventName The event name to subscribe to.
426
+ * @param eventHandler The event handler to call when the event is published.
427
+ * @param context The context to bind to the event handler.
428
+ * @param options Subscription options.
429
+ * @returns An object used to check if the subscription still exists and to unsubscribe from the event.
430
+ */
431
+ subscribe(eventName, eventHandler, context = eventHandler, options) {
432
+ this.validateEventName(eventName);
433
+ if (options?.once) {
434
+ const originalHandler = eventHandler;
435
+ eventHandler = (event, data) => {
436
+ originalHandler.call(context, event, data);
437
+ this.unsubscribe(subscription);
438
+ };
439
+ }
440
+ const contextEventHandler = new ContextEventHandler(context, eventHandler);
441
+ this.subscribers.set(eventName, contextEventHandler);
442
+ const subscription = new Subscription(eventName, contextEventHandler);
443
+ return subscription;
591
444
  }
592
445
  /**
593
- * Returns a string representation of the media type parameters.
446
+ * Unsubscribe from the event
594
447
  *
595
- * @override
596
- * @returns {string} The string representation of the media type parameters.
448
+ * @param subscription The subscription to unsubscribe.
449
+ * @returns true if eventListener has been removed successfully. false if the value is not found or if the value is not an object.
597
450
  */
598
- toString() {
599
- return Array.from(this).map(([name, value]) => `;${name}=${!value || !utils_default.test(value) ? `"${value.replace(matcher, "\\$1")}"` : value}`).join("");
451
+ unsubscribe({ eventName, contextEventHandler }) {
452
+ const contextEventHandlers = this.subscribers.get(eventName) ?? /* @__PURE__ */ new Set();
453
+ const removed = contextEventHandlers.delete(contextEventHandler);
454
+ if (removed && contextEventHandlers.size === 0) {
455
+ this.subscribers.delete(eventName);
456
+ }
457
+ return removed;
600
458
  }
601
459
  /**
602
- * Returns the name of this class.
460
+ * Publish an event
603
461
  *
604
- * @override
605
- * @returns {string} The name of this class.
462
+ * @template T
463
+ * @param eventName The name of the event.
464
+ * @param event The event to be handled.
465
+ * @param data The value to be passed to the event handler as a parameter.
606
466
  */
607
- [Symbol.toStringTag]() {
608
- return "MediaTypeParameters";
467
+ publish(eventName, event = new CustomEvent(eventName), data) {
468
+ this.validateEventName(eventName);
469
+ this.subscribers.get(eventName)?.forEach((contextEventHandler) => {
470
+ try {
471
+ contextEventHandler.handle(event, data);
472
+ } catch (error) {
473
+ if (this.errorHandler) {
474
+ this.errorHandler(error, eventName, event, data);
475
+ } else {
476
+ console.error(`Error in event handler for '${eventName}':`, error);
477
+ }
478
+ }
479
+ });
609
480
  }
610
- };
611
-
612
- // node_modules/.pnpm/@d1g1tal+media-type@5.0.0/node_modules/@d1g1tal/media-type/src/media-type-parser.js
613
- var whitespaceCharacters = [" ", " ", "\n", "\r"];
614
- var trailingWhitespace = /[ \t\n\r]+$/u;
615
- var leadingAndTrailingWhitespace = /^[ \t\n\r]+|[ \t\n\r]+$/ug;
616
- var MediaTypeParser = class _MediaTypeParser {
617
481
  /**
618
- * Function to parse a media type.
482
+ * Check if the event and handler are subscribed.
619
483
  *
620
- * @static
621
- * @param {string} input The media type to parse
622
- * @returns {{ type: string, subtype: string, parameters: MediaTypeParameters }} An object populated with the parsed media type properties and any parameters.
484
+ * @param subscription The subscription object.
485
+ * @returns true if the event name and handler are subscribed, false otherwise.
623
486
  */
624
- static parse(input) {
625
- input = input.replace(leadingAndTrailingWhitespace, "");
626
- const length = input.length, trim = true, lowerCase = false;
627
- let { position, result: type, subtype = "" } = _MediaTypeParser.#filterComponent({ input }, "/");
628
- if (!type.length || position >= length || !utils_default.test(type)) {
629
- throw new TypeError(_MediaTypeParser.#generateErrorMessage("type", type));
630
- }
631
- ({ position, result: subtype } = _MediaTypeParser.#filterComponent({ position: ++position, input, trim }, ";"));
632
- if (!subtype.length || !utils_default.test(subtype)) {
633
- throw new TypeError(_MediaTypeParser.#generateErrorMessage("subtype", subtype));
634
- }
635
- let parameterName = "", parameterValue = null;
636
- const parameters = new MediaTypeParameters();
637
- while (position++ < length) {
638
- while (whitespaceCharacters.includes(input[position])) {
639
- ++position;
640
- }
641
- ({ position, result: parameterName } = _MediaTypeParser.#filterComponent({ position, input, lowerCase }, ";", "="));
642
- if (position < length) {
643
- if (input[position] == ";") {
644
- continue;
645
- }
646
- ++position;
647
- }
648
- if (input[position] == '"') {
649
- [parameterValue, position] = _MediaTypeParser.#collectHttpQuotedString(input, position);
650
- while (position < length && input[position] != ";") {
651
- ++position;
652
- }
653
- } else {
654
- ({ position, result: parameterValue } = _MediaTypeParser.#filterComponent({ position, input, lowerCase, trim }, ";"));
655
- if (!parameterValue) {
656
- continue;
657
- }
658
- }
659
- if (parameterName && MediaTypeParameters.isValid(parameterName, parameterValue) && !parameters.has(parameterName)) {
660
- parameters.set(parameterName, parameterValue);
661
- }
662
- }
663
- return { type, subtype, parameters };
487
+ isSubscribed({ eventName, contextEventHandler }) {
488
+ return this.subscribers.get(eventName)?.has(contextEventHandler) ?? false;
664
489
  }
665
490
  /**
666
- * Filters a component from the input string.
491
+ * Validate the event name
667
492
  *
668
- * @private
669
- * @static
670
- * @param {Object} options The options.
671
- * @param {number} [options.position] The starting position.
672
- * @param {string} options.input The input string.
673
- * @param {boolean} [options.lowerCase] Indicates whether the result should be lowercased.
674
- * @param {boolean} [options.trim] Indicates whether the result should be trimmed.
675
- * @param {string[]} charactersToFilter The characters to filter.
676
- * @returns {{ position: number, result: string }} An object that includes the resulting string and updated position.
493
+ * @param eventName The event name to validate.
494
+ * @throws {TypeError} If the event name is not a non-empty string.
495
+ * @throws {Error} If the event name has leading or trailing whitespace.
677
496
  */
678
- static #filterComponent({ position = 0, input, lowerCase = true, trim = false }, ...charactersToFilter) {
679
- let result = "";
680
- for (const length = input.length; position < length && !charactersToFilter.includes(input[position]); position++) {
681
- result += input[position];
682
- }
683
- if (lowerCase) {
684
- result = result.toLowerCase();
497
+ validateEventName(eventName) {
498
+ if (!eventName || typeof eventName !== "string") {
499
+ throw new TypeError("Event name must be a non-empty string");
685
500
  }
686
- if (trim) {
687
- result = result.replace(trailingWhitespace, "");
501
+ if (eventName.trim() !== eventName) {
502
+ throw new Error("Event name cannot have leading or trailing whitespace");
688
503
  }
689
- return { position, result };
690
504
  }
691
505
  /**
692
- * Collects all the HTTP quoted strings.
693
- * This variant only implements it with the extract-value flag set.
694
- *
695
- * @private
696
- * @static
697
- * @param {string} input The string to process.
698
- * @param {number} position The starting position.
699
- * @returns {Array<string|number>} An array that includes the resulting string and updated position.
506
+ * Clears all subscriptions. The instance should not be used after calling this method.
700
507
  */
701
- static #collectHttpQuotedString(input, position) {
702
- let value = "";
703
- for (let length = input.length, char; ++position < length; ) {
704
- if ((char = input[position]) == '"') {
705
- break;
706
- }
707
- value += char == "\\" && ++position < length ? input[position] : char;
708
- }
709
- return [value, position];
508
+ destroy() {
509
+ this.subscribers.clear();
710
510
  }
711
511
  /**
712
- * Generates an error message.
512
+ * A String value that is used in the creation of the default string
513
+ * description of an object. Called by the built-in method {@link Object.prototype.toString}.
713
514
  *
714
- * @private
715
- * @static
716
- * @param {string} component The component name.
717
- * @param {string} value The component value.
718
- * @returns {string} The error message.
515
+ * @returns The default string description of this object.
719
516
  */
720
- static #generateErrorMessage(component, value) {
721
- return `Invalid ${component} "${value}": only HTTP token code points are valid.`;
517
+ get [Symbol.toStringTag]() {
518
+ return "Subscribr";
722
519
  }
723
520
  };
724
521
 
725
- // node_modules/.pnpm/@d1g1tal+media-type@5.0.0/node_modules/@d1g1tal/media-type/src/media-type.js
726
- var MediaType = class _MediaType {
727
- /** @type {string} */
728
- #type;
729
- /** @type {string} */
730
- #subtype;
731
- /** @type {MediaTypeParameters} */
732
- #parameters;
522
+ // src/http-error.ts
523
+ var HttpError = class extends Error {
524
+ _entity;
525
+ responseStatus;
526
+ _url;
527
+ _method;
528
+ _timing;
733
529
  /**
734
- * Create a new MediaType instance from a string representation.
735
- *
736
- * @param {string} mediaType The media type to parse.
737
- * @param {Object} [parameters] Optional parameters.
530
+ * Creates an instance of HttpError.
531
+ * @param status The status code and status text of the {@link Response}.
532
+ * @param httpErrorOptions The http error options.
738
533
  */
739
- constructor(mediaType, parameters = {}) {
740
- if (object_type_default(parameters) != Object) {
741
- throw new TypeError("The parameters argument must be an object");
742
- }
743
- ({ type: this.#type, subtype: this.#subtype, parameters: this.#parameters } = MediaTypeParser.parse(mediaType));
744
- for (const [name, value] of Object.entries(parameters)) {
745
- this.#parameters.set(name, value);
746
- }
534
+ constructor(status, { message, cause, entity, url, method, timing } = {}) {
535
+ super(message, { cause });
536
+ this._entity = entity;
537
+ this.responseStatus = status;
538
+ this._url = url;
539
+ this._method = method;
540
+ this._timing = timing;
747
541
  }
748
- static parse(mediaType) {
749
- try {
750
- return new _MediaType(mediaType);
751
- } catch (e) {
752
- }
753
- return null;
542
+ /**
543
+ * It returns the value of the private variable #entity.
544
+ * @returns The entity property of the class.
545
+ */
546
+ get entity() {
547
+ return this._entity;
754
548
  }
755
549
  /**
756
- * Gets the type.
757
- *
758
- * @returns {string} The type.
550
+ * It returns the status code of the {@link Response}.
551
+ * @returns The status code of the {@link Response}.
759
552
  */
760
- get type() {
761
- return this.#type;
553
+ get statusCode() {
554
+ return this.responseStatus.code;
762
555
  }
763
556
  /**
764
- * Gets the subtype.
765
- *
766
- * @returns {string} The subtype.
557
+ * It returns the status text of the {@link Response}.
558
+ * @returns The status code and status text of the {@link Response}.
767
559
  */
768
- get subtype() {
769
- return this.#subtype;
560
+ get statusText() {
561
+ return this.responseStatus?.text;
770
562
  }
771
563
  /**
772
- * Gets the media type essence (type/subtype).
773
- *
774
- * @returns {string} The media type without any parameters
564
+ * The request URL that caused the error.
565
+ * @returns The URL or undefined.
775
566
  */
776
- get essence() {
777
- return `${this.#type}/${this.#subtype}`;
567
+ get url() {
568
+ return this._url;
778
569
  }
779
570
  /**
780
- * Gets the parameters.
781
- *
782
- * @returns {MediaTypeParameters} The media type parameters.
571
+ * The HTTP method that was used for the failed request.
572
+ * @returns The method string or undefined.
783
573
  */
784
- get parameters() {
785
- return this.#parameters;
574
+ get method() {
575
+ return this._method;
786
576
  }
787
577
  /**
788
- * Checks if the media type matches the specified type.
789
- *
790
- * @todo Fix string handling to parse the type and subtype from the string.
791
- * @param {MediaType|string} type The media type to check.
792
- * @returns {boolean} true if the media type matches the specified type, false otherwise.
578
+ * Timing information for the failed request.
579
+ * @returns The timing object or undefined.
793
580
  */
794
- matches(type) {
795
- return this.#type == type?.type && this.#subtype == type?.subtype || this.essence.includes(type);
581
+ get timing() {
582
+ return this._timing;
796
583
  }
797
584
  /**
798
- * Gets the serialized version of the media type.
799
- *
800
- * @returns {string} The serialized media type.
585
+ * A String value representing the name of the error.
586
+ * @returns The name of the error.
801
587
  */
802
- toString() {
803
- return `${this.essence}${this.#parameters.toString()}`;
588
+ get name() {
589
+ return "HttpError";
804
590
  }
805
591
  /**
806
- * Gets the name of the class.
807
- *
808
- * @returns {string} The class name
592
+ * A String value that is used in the creation of the default string
593
+ * description of an object. Called by the built-in method {@link Object.prototype.toString}.
594
+ * @returns The default string description of this object.
809
595
  */
810
596
  get [Symbol.toStringTag]() {
811
- return "MediaType";
597
+ return this.name;
812
598
  }
813
599
  };
814
600
 
815
- // src/http-media-type.js
601
+ // src/http-media-type.ts
816
602
  var HttpMediaType = {
817
603
  /** Advanced Audio Coding (AAC) */
818
604
  AAC: "audio/aac",
@@ -967,204 +753,8 @@ var HttpMediaType = {
967
753
  /** 7-Zip Archive */
968
754
  "7Z": "application/x-7z-compressed"
969
755
  };
970
- var http_media_type_default = HttpMediaType;
971
-
972
- // src/constants.js
973
- var defaultCharset = "utf-8";
974
- var endsWithSlashRegEx = /\/$/;
975
- var mediaTypes = /* @__PURE__ */ new Map([
976
- [http_media_type_default.PNG, new MediaType(http_media_type_default.PNG)],
977
- [http_media_type_default.TEXT, new MediaType(http_media_type_default.TEXT, { defaultCharset })],
978
- [http_media_type_default.JSON, new MediaType(http_media_type_default.JSON, { defaultCharset })],
979
- [http_media_type_default.HTML, new MediaType(http_media_type_default.HTML, { defaultCharset })],
980
- [http_media_type_default.JAVA_SCRIPT, new MediaType(http_media_type_default.JAVA_SCRIPT, { defaultCharset })],
981
- [http_media_type_default.CSS, new MediaType(http_media_type_default.CSS, { defaultCharset })],
982
- [http_media_type_default.XML, new MediaType(http_media_type_default.XML, { defaultCharset })],
983
- [http_media_type_default.BIN, new MediaType(http_media_type_default.BIN)]
984
- ]);
985
- var RequestEvents = Object.freeze({
986
- CONFIGURED: "configured",
987
- SUCCESS: "success",
988
- ERROR: "error",
989
- ABORTED: "aborted",
990
- TIMEOUT: "timeout",
991
- COMPLETE: "complete",
992
- ALL_COMPLETE: "all-complete"
993
- });
994
- var SignalEvents = Object.freeze({
995
- ABORT: "abort",
996
- TIMEOUT: "timeout"
997
- });
998
- var _abortEvent = new CustomEvent(SignalEvents.ABORT, { detail: { cause: new DOMException("The request was aborted", "AbortError") } });
999
- var requestBodyMethods = [http_request_methods_default.POST, http_request_methods_default.PUT, http_request_methods_default.PATCH];
1000
- var internalServerError = new ResponseStatus(500, "Internal Server Error");
1001
- var eventResponseStatuses = Object.freeze({
1002
- [RequestEvents.ABORTED]: new ResponseStatus(499, "Aborted"),
1003
- [RequestEvents.TIMEOUT]: new ResponseStatus(504, "Request Timeout")
1004
- });
1005
- var abortSignalProxyHandler = { get: (target, property) => property == "signal" ? target.signal.timeout(target.timeout) : Reflect.get(target, property) };
1006
-
1007
- // src/abort-signal.js
1008
- var AbortSignal = class {
1009
- /** @type {AbortController} */
1010
- #abortController;
1011
- /** @type {number} */
1012
- #timeoutId;
1013
- /**
1014
- * @param {NativeAbortSignal} signal The signal to chain.
1015
- * This signal will be able to abort the request, but will not be notified if the request is aborted by the timeout.
1016
- */
1017
- constructor(signal) {
1018
- this.#abortController = new AbortController();
1019
- signal?.addEventListener(SignalEvents.ABORT, (event) => this.#abort(event));
1020
- }
1021
- /**
1022
- * The aborted property is a Boolean that indicates whether the request has been aborted (true) or not (false).
1023
- *
1024
- * @returns {boolean} Whether the signal was aborted or not
1025
- */
1026
- get aborted() {
1027
- return this.#abortController.signal.aborted;
1028
- }
1029
- /**
1030
- * The reason property returns a DOMException object indicating the reason the operation was aborted, or null if the operation is not aborted.
1031
- *
1032
- * @returns {DOMException} The reason the signal was aborted
1033
- */
1034
- get reason() {
1035
- return this.#abortController.signal.reason;
1036
- }
1037
- /**
1038
- * Returns an AbortSignal instance that will be aborted when the provided amount of milliseconds have passed.
1039
- * A value of {@link Infinity} means there is no timeout.
1040
- * Note: You can't set this property to a value less than 0.
1041
- *
1042
- * @param {number} timeout The timeout in milliseconds
1043
- * @returns {AbortSignal} The abort signal
1044
- */
1045
- timeout(timeout) {
1046
- if (timeout < 0) {
1047
- throw new RangeError("The timeout cannot be negative");
1048
- }
1049
- if (timeout != Infinity) {
1050
- this.#timeoutId ??= setTimeout(() => this.#abort(new CustomEvent(SignalEvents.TIMEOUT, { detail: { timeout, cause: new DOMException(`The request timed-out after ${timeout / 1e3} seconds`, "TimeoutError") } }), true), timeout);
1051
- }
1052
- return this.#abortController.signal;
1053
- }
1054
- /**
1055
- * Clears the timeout.
1056
- * Note: This does not abort the signal, dispatch the timeout event, or reset the timeout.
1057
- *
1058
- * @returns {void}
1059
- */
1060
- clearTimeout() {
1061
- clearTimeout(this.#timeoutId);
1062
- }
1063
- /**
1064
- * Adds an event listener for the 'abort' event.
1065
- *
1066
- * @param {EventListener} listener The listener to add
1067
- * @returns {AbortSignal} The AbortSignal
1068
- */
1069
- onAbort(listener) {
1070
- this.#abortController.signal.addEventListener(SignalEvents.ABORT, listener);
1071
- return this;
1072
- }
1073
- /**
1074
- * Adds an event listener for the 'timeout' event.
1075
- *
1076
- * @param {EventListener} listener The listener to add
1077
- * @returns {AbortSignal} The AbortSignal
1078
- */
1079
- onTimeout(listener) {
1080
- this.#abortController.signal.addEventListener(SignalEvents.TIMEOUT, listener);
1081
- return this;
1082
- }
1083
- /**
1084
- * Aborts the signal. This is so naughty. ¯\_(ツ)_/¯
1085
- *
1086
- * @param {Event} event The event to abort with
1087
- * @returns {void}
1088
- */
1089
- abort(event) {
1090
- this.#abort(event);
1091
- }
1092
- /**
1093
- * Aborts the signal.
1094
- *
1095
- * @private
1096
- * @param {Event} event The event to abort with
1097
- * @param {boolean} [dispatchEvent = false] Whether to dispatch the event or not
1098
- * @returns {void}
1099
- * @fires SignalEvents.ABORT When the signal is aborted
1100
- * @fires SignalEvents.TIMEOUT When the signal times out
1101
- */
1102
- #abort(event = _abortEvent, dispatchEvent = false) {
1103
- clearTimeout(this.#timeoutId);
1104
- this.#abortController.abort(event.detail?.cause);
1105
- if (dispatchEvent) {
1106
- this.#abortController.signal.dispatchEvent(event);
1107
- }
1108
- }
1109
- };
1110
-
1111
- // src/http-error.js
1112
- var HttpError = class extends Error {
1113
- /** @type {ResponseBody} */
1114
- #entity;
1115
- /** @type {ResponseStatus} */
1116
- #responseStatus;
1117
- /**
1118
- * @param {string} [message] The error message.
1119
- * @param {HttpErrorOptions} [httpErrorOptions] The http error options.
1120
- * @param {any} [httpErrorOptions.cause] The cause of the error.
1121
- * @param {ResponseStatus} [httpErrorOptions.status] The response status.
1122
- * @param {ResponseBody} [httpErrorOptions.entity] The error entity from the server, if any.
1123
- */
1124
- constructor(message, { cause, status, entity }) {
1125
- super(message, { cause });
1126
- this.#entity = entity;
1127
- this.#responseStatus = status;
1128
- }
1129
- /**
1130
- * It returns the value of the private variable #entity.
1131
- *
1132
- * @returns {ResponseBody} The entity property of the class.
1133
- */
1134
- get entity() {
1135
- return this.#entity;
1136
- }
1137
- /**
1138
- * It returns the status code of the {@link Response}.
1139
- *
1140
- * @returns {number} The status code of the {@link Response}.
1141
- */
1142
- get statusCode() {
1143
- return this.#responseStatus?.code;
1144
- }
1145
- /**
1146
- * It returns the status text of the {@link Response}.
1147
- *
1148
- * @returns {string} The status code and status text of the {@link Response}.
1149
- */
1150
- get statusText() {
1151
- return this.#responseStatus?.text;
1152
- }
1153
- get name() {
1154
- return "HttpError";
1155
- }
1156
- /**
1157
- * A String value that is used in the creation of the default string
1158
- * description of an object. Called by the built-in method {@link Object.prototype.toString}.
1159
- *
1160
- * @returns {string} The default string description of this object.
1161
- */
1162
- get [Symbol.toStringTag]() {
1163
- return "HttpError";
1164
- }
1165
- };
1166
756
 
1167
- // src/http-request-headers.js
757
+ // src/http-request-headers.ts
1168
758
  var HttpRequestHeader = {
1169
759
  /**
1170
760
  * Content-Types that are acceptable for the response. See Content negotiation. Permanent.
@@ -1245,26 +835,12 @@ var HttpRequestHeader = {
1245
835
  CONTENT_TYPE: "content-type",
1246
836
  /**
1247
837
  * The date and time that the message was sent (in "HTTP-date" format as defined by RFC 7231 Date/Time Formats).
1248
- * Permanent.
1249
- *
1250
- * @example
1251
- * <code>Date: Tue, 15 Nov 1994 08:12:31 GMT</code>
1252
- */
1253
- DATE: "date",
1254
- /**
1255
- * Indicates that particular server behaviors are required by the client. Permanent.
1256
- *
1257
- * @example
1258
- * <code>Expect: 100-continue</code>
1259
- */
1260
- EXPECT: "expect",
1261
- /**
1262
- * The email address of the user making the request. Permanent.
838
+ * Permanent.
1263
839
  *
1264
840
  * @example
1265
- * <code>From: user@example.com</code>
841
+ * <code>Date: Tue, 15 Nov 1994 08:12:31 GMT</code>
1266
842
  */
1267
- FROM: "from",
843
+ DATE: "date",
1268
844
  /**
1269
845
  * The domain name of the server (for virtual hosting), and the TCP port number on which the server is listening. The
1270
846
  * port number may be omitted if the port is the standard port for the service requested. Permanent. Mandatory since
@@ -1380,13 +956,6 @@ var HttpRequestHeader = {
1380
956
  * <code>Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11</code>
1381
957
  */
1382
958
  UPGRADE: "upgrade",
1383
- /**
1384
- * Informs the server of proxies through which the request was sent. Permanent.
1385
- *
1386
- * @example
1387
- * <code>Via: 1.0 fred, 1.1 example.com (Apache/1.1)</code>
1388
- */
1389
- VIA: "via",
1390
959
  /**
1391
960
  * A general warning about possible problems with the entity body. Permanent.
1392
961
  *
@@ -1401,16 +970,6 @@ var HttpRequestHeader = {
1401
970
  * <code>X-Requested-With: XMLHttpRequest</code>
1402
971
  */
1403
972
  X_REQUESTED_WITH: "x-requested-with",
1404
- /**
1405
- * Requests a web application to disable their tracking of a user. This is Mozilla's version of the X-Do-Not-Track
1406
- * header field (since Firefox 4.0 Beta 11). Safari and IE9 also have support for this field. On March 7, 2011, a
1407
- * draft proposal was submitted to IETF. The W3C Tracking Protection Working Group is producing a specification.
1408
- *
1409
- * @example
1410
- * <code>DNT: 1 (Do Not Track Enabled)</code>
1411
- * <code>DNT: 0 (Do Not Track Disabled)</code>
1412
- */
1413
- DNT: "dnt",
1414
973
  /**
1415
974
  * A de facto standard for identifying the originating IP address of a client connecting to a web server through an
1416
975
  * HTTP proxy or load balancer.
@@ -1438,40 +997,281 @@ var HttpRequestHeader = {
1438
997
  * @example
1439
998
  * <code>X-Forwarded-Proto: https</code>
1440
999
  */
1441
- X_FORWARDED_PROTO: "x-forwarded-proto",
1000
+ X_FORWARDED_PROTO: "x-forwarded-proto"
1001
+ };
1002
+
1003
+ // src/http-request-methods.ts
1004
+ var HttpRequestMethod = {
1442
1005
  /**
1443
- * Non-standard header field used by Microsoft applications and load-balancers.
1006
+ * The OPTIONS method represents a request for information about the communication options available on the
1007
+ * request/response chain identified by the Request-URI. This method allows the client to determine the options and/or
1008
+ * requirements associated with a resource, or the capabilities of a server, without implying a resource action or
1009
+ * initiating a resource retrieval.
1444
1010
  *
1445
- * @example
1446
- * <code>Front-End-Https: on</code>
1011
+ * Responses to this method are not cacheable.
1012
+ *
1013
+ * If the OPTIONS request includes an entity-body (as indicated by the presence of Content-Length or
1014
+ * Transfer-Encoding), then the media type MUST be indicated by a Content-Type field. Although this specification does
1015
+ * not define any use for such a body, future extensions to HTTP might use the OPTIONS body to make more detailed
1016
+ * queries on the server. A server that does not support such an extension MAY discard the request body.
1017
+ *
1018
+ * If the Request-URI is an asterisk ("*"), the OPTIONS request is intended to apply to the server in general rather
1019
+ * than to a specific resource. Since a server's communication options typically depend on the resource, the "*"
1020
+ * request is only useful as a "ping" or "no-op" type of method, it does nothing beyond allowing the client to test the
1021
+ * capabilities of the server. For example, this can be used to test a proxy for HTTP/1.1 compliance (or lack thereof).
1022
+ *
1023
+ * If the Request-URI is not an asterisk, the OPTIONS request applies only to the options that are available when
1024
+ * communicating with that resource.
1025
+ *
1026
+ * A 200 response SHOULD include any header fields that indicate optional features implemented by the server and
1027
+ * applicable to that resource (e.g., Allow), possibly including extensions not defined by this specification. The
1028
+ * response body, if any, SHOULD also include information about the communication options. The format for such a body
1029
+ * is not defined by this specification, but might be defined by future extensions to HTTP. Content negotiation MAY be
1030
+ * used to select the appropriate response format. If no response body is included, the response MUST include a
1031
+ * Content-Length field with a field-value of "0".
1032
+ *
1033
+ * The Max-Forwards request-header field MAY be used to target a specific proxy in the request chain. When a proxy
1034
+ * receives an OPTIONS request on an absoluteURI for which request forwarding is permitted, the proxy MUST check for a
1035
+ * Max-Forwards field. If the Max-Forwards field-value is zero ("0"), the proxy MUST NOT forward the message, instead,
1036
+ * the proxy SHOULD respond with its own communication options. If the Max-Forwards field-value is an integer greater
1037
+ * than zero, the proxy MUST decrement the field-value when it forwards the request. If no Max-Forwards field is
1038
+ * present in the request, then the forwarded request MUST NOT include a Max-Forwards field.
1039
+ */
1040
+ OPTIONS: "OPTIONS",
1041
+ /**
1042
+ * The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI. If
1043
+ * the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity in
1044
+ * the response and not the source text of the process, unless that text happens to be the output of the process.
1045
+ *
1046
+ * The semantics of the GET method change to a "conditional GET" if the request message includes an If-Modified-Since;
1047
+ * If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. A conditional GET method requests that the
1048
+ * entity be transferred only under the circumstances described by the conditional header field(s). The conditional GET
1049
+ * method is intended to reduce unnecessary network usage by allowing cached entities to be refreshed without requiring
1050
+ * multiple requests or transferring data already held by the client.
1051
+ *
1052
+ * The semantics of the GET method change to a "partial GET" if the request message includes a Range header field. A
1053
+ * partial GET requests that only part of the entity be transferred, as described in section 14.35. The partial GET
1054
+ * method is intended to reduce unnecessary network usage by allowing partially-retrieved entities to be completed
1055
+ * without transferring data already held by the client.
1056
+ *
1057
+ * The response to a GET request is cacheable if and only if it meets the requirements for HTTP caching described in
1058
+ * section 13.
1059
+ *
1060
+ * See section 15.1.3 for security considerations when used for forms.
1061
+ */
1062
+ GET: "GET",
1063
+ /**
1064
+ * The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. The
1065
+ * meta information contained in the HTTP headers in response to a HEAD request SHOULD be identical to the information
1066
+ * sent in response to a GET request. This method can be used for obtaining meta information about the entity implied by
1067
+ * the request without transferring the entity-body itself. This method is often used for testing hypertext links for
1068
+ * validity, accessibility, and recent modification.
1069
+ *
1070
+ * The response to a HEAD request MAY be cacheable in the sense that the information contained in the response MAY be
1071
+ * used to update a previously cached entity from that resource. If the new field values indicate that the cached
1072
+ * entity differs from the current entity (as would be indicated by a change in Content-Length, Content-MD5, ETag or
1073
+ * Last-Modified), then the cache MUST treat the cache entry as stale.
1074
+ */
1075
+ HEAD: "HEAD",
1076
+ /**
1077
+ * The POST method is used to request that the origin server accept the entity enclosed in the request as a new
1078
+ * subordinate of the resource identified by the Request-URI in the Request-Line. POST is designed to allow a uniform
1079
+ * method to cover the following functions:
1080
+ * <ul>
1081
+ * <li>Annotation of existing resources,</li>
1082
+ * <li>Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles,</li>
1083
+ * <li>Providing a block of data, such as the result of submitting a form, to a data-handling process,</li>
1084
+ * <li>Extending a database through an append operation.</li>
1085
+ * </ul>
1086
+ *
1087
+ * The actual function performed by the POST method is determined by the server and is usually dependent on the
1088
+ * Request-URI. The posted entity is subordinate to that URI in the same way that a file is subordinate to a directory
1089
+ * containing it, a news article is subordinate to a newsgroup to which it is posted, or a record is subordinate to a
1090
+ * database.
1091
+ *
1092
+ * The action performed by the POST method might not result in a resource that can be identified by a URI. In this
1093
+ * case, either 200 (OK) or 204 (No Content) is the appropriate response status, depending on whether or not the
1094
+ * response includes an entity that describes the result.
1095
+ *
1096
+ * If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity
1097
+ * which describes the status of the request and refers to the new resource, and a Location header (see section 14.30).
1098
+ *
1099
+ * Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header
1100
+ * fields. However, the 303 (See Other) response can be used to direct the user agent to retrieve a cacheable resource.
1101
+ *
1102
+ * POST requests MUST obey the message transmission requirements set out in section 8.2.
1103
+ *
1104
+ * See section 15.1.3 for security considerations.
1105
+ */
1106
+ POST: "POST",
1107
+ /**
1108
+ * The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers
1109
+ * to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing
1110
+ * on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being
1111
+ * defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. If
1112
+ * a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. If an
1113
+ * existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate
1114
+ * successful completion of the request. If the resource could not be created or modified with the Request-URI, an
1115
+ * appropriate error response SHOULD be given that reflects the nature of the problem. The recipient of the entity MUST
1116
+ * \NOT ignore any Content-* (e.g. Content-Range) headers that it does not understand or implement and MUST return a
1117
+ * 501 (Not Implemented) response in such cases.
1118
+ *
1119
+ * If the request passes through a cache and the Request-URI identifies one or more currently cached entities, those
1120
+ * entries SHOULD be treated as stale. Responses to this method are not cacheable.
1121
+ *
1122
+ * The fundamental difference between the POST and PUT requests is reflected in the different meaning of the
1123
+ * Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource
1124
+ * might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations.
1125
+ * In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what
1126
+ * URI is intended and the server MUST NOT attempt to apply the request to some other resource. If the server desires
1127
+ * that the request be applied to a different URI, it MUST send a 301 (Moved Permanently) response, the user agent MAY
1128
+ * then make its own decision regarding whether or not to redirect the request.
1129
+ *
1130
+ * A single resource MAY be identified by many different URIs. For example, an article might have a URI for identifying
1131
+ * "the current version" which is separate from the URI identifying each particular version. In this case, a PUT
1132
+ * request on a general URI might result in several other URIs being defined by the origin server.
1133
+ *
1134
+ * HTTP/1.1 does not define how a PUT method affects the state of an origin server.
1135
+ *
1136
+ * PUT requests MUST obey the message transmission requirements set out in section 8.2.
1137
+ *
1138
+ * Unless otherwise specified for a particular entity-header, the entity-headers in the PUT request SHOULD be applied
1139
+ * to the resource created or modified by the PUT.
1140
+ */
1141
+ PUT: "PUT",
1142
+ /**
1143
+ * The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY
1144
+ * be overridden by human intervention (or other means) on the origin server. The client cannot be guaranteed that the
1145
+ * operation has been carried out, even if the status code returned from the origin server indicates that the action
1146
+ * has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response
1147
+ * is given, it intends to delete the resource or move it to an inaccessible location.
1148
+ *
1149
+ * A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if
1150
+ * the action has not yet been enacted, or 204 (No Content) if the action has been enacted but the response does not
1151
+ * include an entity.
1152
+ *
1153
+ * If the request passes through a cache and the Request-URI identifies one or more currently cached entities, those
1154
+ * entries SHOULD be treated as stale. Responses to this method are not cacheable.
1155
+ */
1156
+ DELETE: "DELETE",
1157
+ /**
1158
+ * The TRACE method is used to invoke a remote, application-layer loop- back of the request message. The final
1159
+ * recipient of the request SHOULD reflect the message received back to the client as the entity-body of a 200 (OK)
1160
+ * response. The final recipient is either the origin server or the first proxy or gateway to receive a Max-Forwards
1161
+ * value of zero (0) in the request (see section 14.31). A TRACE request MUST NOT include an entity.
1162
+ *
1163
+ * TRACE allows the client to see what is being received at the other end of the request chain and use that data for
1164
+ * testing or diagnostic information. The value of the Via header field (section 14.45) is of particular interest,
1165
+ * since it acts as a trace of the request chain. Use of the Max-Forwards header field allows the client to limit the
1166
+ * length of the request chain, which is useful for testing a chain of proxies forwarding messages in an infinite loop.
1167
+ *
1168
+ * If the request is valid, the response SHOULD contain the entire request message in the entity-body, with a
1169
+ * Content-Type of "message/http". Responses to this method MUST NOT be cached.
1170
+ */
1171
+ TRACE: "TRACE",
1172
+ /**
1173
+ * This specification reserves the method name CONNECT for use with a proxy that can dynamically switch to being a
1174
+ * tunnel (e.g. SSL tunneling [44]).
1447
1175
  */
1448
- FRONT_END_HTTPS: "front-end-https",
1176
+ CONNECT: "CONNECT",
1449
1177
  /**
1450
- * Requests a web application override the method specified in the request (typically POST) with the method given in
1451
- * the header field (typically PUT or DELETE). Can be used when a user agent or firewall prevents PUT or DELETE methods
1452
- * from being sent directly (note that this either a bug in the software component, which ought to be fixed, or an
1453
- * intentional configuration, in which case bypassing it may be the wrong thing to do).
1178
+ * The PATCH method requests that a set of changes described in the
1179
+ * request entity be applied to the resource identified by the Request-
1180
+ * URI. The set of changes is represented in a format called a "patch
1181
+ * document" identified by a media type. If the Request-URI does not
1182
+ * point to an existing resource, the server MAY create a new resource,
1183
+ * depending on the patch document type (whether it can logically modify
1184
+ * a null resource) and permissions, etc.
1185
+ *
1186
+ * The difference between the PUT and PATCH requests is reflected in the
1187
+ * way the server processes the enclosed entity to modify the resource
1188
+ * identified by the Request-URI. In a PUT request, the enclosed entity
1189
+ * is considered to be a modified version of the resource stored on the
1190
+ * origin server, and the client is requesting that the stored version
1191
+ * be replaced. With PATCH, however, the enclosed entity contains a set
1192
+ * of instructions describing how a resource currently residing on the
1193
+ * origin server should be modified to produce a new version. The PATCH
1194
+ * method affects the resource identified by the Request-URI, and it
1195
+ * also MAY have side effects on other resources; i.e., new resources
1196
+ * may be created, or existing ones modified, by the application of a
1197
+ * PATCH.
1198
+ *
1199
+ * PATCH is neither safe nor idempotent as defined by [RFC2616], Section
1200
+ * 9.1.
1201
+ *
1202
+ * A PATCH request can be issued in such a way as to be idempotent,
1203
+ * which also helps prevent bad outcomes from collisions between two
1204
+ * PATCH requests on the same resource in a similar time frame.
1205
+ * Collisions from multiple PATCH requests may be more dangerous than
1206
+ * PUT collisions because some patch formats need to operate from a
1207
+ * known base-point or else they will corrupt the resource. Clients
1208
+ * using this kind of patch application SHOULD use a conditional request
1209
+ * such that the request will fail if the resource has been updated
1210
+ * since the client last accessed the resource. For example, the client
1211
+ * can use a strong ETag [RFC2616] in an If-Match header on the PATCH
1212
+ * request.
1213
+ *
1214
+ * There are also cases where patch formats do not need to operate from
1215
+ * a known base-point (e.g., appending text lines to log files, or non-
1216
+ * colliding rows to database tables), in which case the same care in
1217
+ * client requests is not needed.
1218
+ *
1219
+ * The server MUST apply the entire set of changes atomically and never
1220
+ * provide (e.g., in response to a GET during this operation) a
1221
+ * partially modified representation. If the entire patch document
1222
+ * cannot be successfully applied, then the server MUST NOT apply any of
1223
+ * the changes. The determination of what constitutes a successful
1224
+ * PATCH can vary depending on the patch document and the type of
1225
+ * resource(s) being modified. For example, the common 'diff' utility
1226
+ * can generate a patch document that applies to multiple files in a
1227
+ * directory hierarchy. The atomicity requirement holds for all
1228
+ * directly affected files. See "Error Handling", Section 2.2, for
1229
+ * details on status codes and possible error conditions.
1230
+ *
1231
+ * If the request passes through a cache and the Request-URI identifies
1232
+ * one or more currently cached entities, those entries SHOULD be
1233
+ * treated as stale. A response to this method is only cacheable if it
1234
+ * contains explicit freshness information (such as an Expires header or
1235
+ * "Cache-Control: max-age" directive) as well as the Content-Location
1236
+ * header matching the Request-URI, indicating that the PATCH response
1237
+ * body is a resource representation. A cached PATCH response can only
1238
+ * be used to respond to subsequent GET and HEAD requests; it MUST NOT
1239
+ * be used to respond to other methods (in particular, PATCH).
1240
+ *
1241
+ * Note that entity-headers contained in the request apply only to the
1242
+ * contained patch document and MUST NOT be applied to the resource
1243
+ * being modified. Thus, a Content-Language header could be present on
1244
+ * the request, but it would only mean (for whatever that's worth) that
1245
+ * the patch document had a language. Servers SHOULD NOT store such
1246
+ * headers except as trace information, and SHOULD NOT use such header
1247
+ * values the same way they might be used on PUT requests. Therefore,
1248
+ * this document does not specify a way to modify a document's Content-
1249
+ * Type or Content-Language value through headers, though a mechanism
1250
+ * could well be designed to achieve this goal through a patch document.
1454
1251
  *
1455
- * @example
1456
- * <code>X-HTTP-Method-Override: DELETE</code>
1457
- */
1458
- X_HTTP_METHOD_OVERRIDE: "x-http-method-override",
1459
- /**
1460
- * Allows easier parsing of the MakeModel/Firmware that is usually found in the User-Agent String of AT&T Devices.
1252
+ * There is no guarantee that a resource can be modified with PATCH.
1253
+ * Further, it is expected that different patch document formats will be
1254
+ * appropriate for different types of resources and that no single
1255
+ * format will be appropriate for all types of resources. Therefore,
1256
+ * there is no single default patch document format that implementations
1257
+ * are required to support. Servers MUST ensure that a received patch
1258
+ * document is appropriate for the type of resource identified by the
1259
+ * Request-URI.
1461
1260
  *
1462
- * @example
1463
- * <code>X-Att-Deviceid: GT-P7320/P7320XXLPG</code>
1464
- */
1465
- X_ATT_DEVICE_ID: "x-att-deviceid",
1466
- /**
1467
- * Links to an XML file on the Internet with a full description and details about the device currently connecting. In the example to the right is an XML file for an AT&T Samsung Galaxy S2.
1468
- * x-wap-profile: http://wap.samsungmobile.com/uaprof/SGH-I777.xml
1261
+ * Clients need to choose when to use PATCH rather than PUT. For
1262
+ * example, if the patch document size is larger than the size of the
1263
+ * new resource data that would be used in a PUT, then it might make
1264
+ * sense to use PUT instead of PATCH. A comparison to POST is even more
1265
+ * difficult, because POST is used in widely varying ways and can
1266
+ * encompass PUT and PATCH-like operations if the server chooses. If
1267
+ * the operation does not modify the resource identified by the Request-
1268
+ * URI in a predictable way, POST should be considered instead of PATCH
1269
+ * or PUT.
1469
1270
  */
1470
- X_WAP_PROFILE: "x-wap-profile"
1271
+ PATCH: "PATCH"
1471
1272
  };
1472
- var http_request_headers_default = HttpRequestHeader;
1473
1273
 
1474
- // src/http-response-headers.js
1274
+ // src/http-response-headers.ts
1475
1275
  var HttpResponseHeader = {
1476
1276
  /**
1477
1277
  * Implemented as a misunderstanding of the HTTP specifications. Common because of mistakes in implementations of early HTTP versions. Has exactly the same functionality as standard Connection field.
@@ -1814,325 +1614,1463 @@ var HttpResponseHeader = {
1814
1614
  */
1815
1615
  X_POWERED_BY: "x-powered-by"
1816
1616
  };
1817
- var http_response_headers_default = HttpResponseHeader;
1818
1617
 
1819
- // src/parameter-map.js
1820
- var ParameterMap = class _ParameterMap extends Map {
1618
+ // src/response-status.ts
1619
+ var ResponseStatus = class {
1620
+ _code;
1621
+ _text;
1821
1622
  /**
1822
- * @param {Iterable<[string, *]>|Object} parameters The initial parameters to set.
1823
- * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value.
1623
+ *
1624
+ * @param code The status code from the {@link Response}
1625
+ * @param text The status text from the {@link Response}
1824
1626
  */
1825
- constructor(parameters = {}) {
1826
- super();
1827
- for (const [key, value] of _ParameterMap.#entries(parameters)) {
1828
- this.append(key, value);
1829
- }
1627
+ constructor(code, text2) {
1628
+ this._code = code;
1629
+ this._text = text2;
1830
1630
  }
1831
1631
  /**
1832
- * Adds a new element with a specified key and value to the ParameterMap.
1833
- * If an element with the same key already exists, the value will be replaced in the underlying {@link Set}.
1632
+ * Returns the status code from the {@link Response}
1834
1633
  *
1835
- * @override
1836
- * @param {string} key The key to set.
1837
- * @param {*} value The value to add to the ParameterMap
1838
- * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value.
1634
+ * @returns The status code.
1839
1635
  */
1840
- set(key, value) {
1841
- const array = super.get(key);
1842
- if (array?.length > 0) {
1843
- array.length = 0;
1844
- array[0] = value;
1845
- } else {
1846
- super.set(key, [value]);
1847
- }
1848
- return this;
1636
+ get code() {
1637
+ return this._code;
1638
+ }
1639
+ /**
1640
+ * Returns the status text from the {@link Response}.
1641
+ *
1642
+ * @returns The status text.
1643
+ */
1644
+ get text() {
1645
+ return this._text;
1646
+ }
1647
+ /**
1648
+ * A String value that is used in the creation of the default string
1649
+ * description of an object. Called by the built-in method {@link Object.prototype.toString}.
1650
+ *
1651
+ * @returns The default string description of this object.
1652
+ */
1653
+ get [Symbol.toStringTag]() {
1654
+ return "ResponseStatus";
1849
1655
  }
1850
1656
  /**
1851
- * Adds all key-value pairs from an iterable or object to the ParameterMap.
1852
- * If a key already exists, the value will be replaced in the underlying {@link Array}.
1657
+ * tostring method for the class.
1853
1658
  *
1854
- * @param {Iterable<[string, *]>|Object} parameters The parameters to set.
1855
- * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value.
1659
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString|Object.prototype.toString}
1660
+ * @returns The status code and status text.
1661
+ */
1662
+ toString() {
1663
+ return `${this._code} ${this._text}`;
1664
+ }
1665
+ };
1666
+
1667
+ // src/constants.ts
1668
+ var charset = { charset: "utf-8" };
1669
+ var endsWithSlashRegEx = /\/$/;
1670
+ var XSRF_COOKIE_NAME = "XSRF-TOKEN";
1671
+ var XSRF_HEADER_NAME = "X-XSRF-TOKEN";
1672
+ var mediaTypes = {
1673
+ PNG: new MediaType(HttpMediaType.PNG),
1674
+ TEXT: new MediaType(HttpMediaType.TEXT, charset),
1675
+ JSON: new MediaType(HttpMediaType.JSON, charset),
1676
+ HTML: new MediaType(HttpMediaType.HTML, charset),
1677
+ JAVA_SCRIPT: new MediaType(HttpMediaType.JAVA_SCRIPT, charset),
1678
+ CSS: new MediaType(HttpMediaType.CSS, charset),
1679
+ XML: new MediaType(HttpMediaType.XML, charset),
1680
+ BIN: new MediaType(HttpMediaType.BIN)
1681
+ };
1682
+ var defaultMediaType = mediaTypes.JSON.toString();
1683
+ var RequestCachingPolicy = {
1684
+ DEFAULT: "default",
1685
+ FORCE_CACHE: "force-cache",
1686
+ NO_CACHE: "no-cache",
1687
+ NO_STORE: "no-store",
1688
+ ONLY_IF_CACHED: "only-if-cached",
1689
+ RELOAD: "reload"
1690
+ };
1691
+ var RequestEvent = {
1692
+ CONFIGURED: "configured",
1693
+ SUCCESS: "success",
1694
+ ERROR: "error",
1695
+ ABORTED: "aborted",
1696
+ TIMEOUT: "timeout",
1697
+ RETRY: "retry",
1698
+ COMPLETE: "complete",
1699
+ ALL_COMPLETE: "all-complete"
1700
+ };
1701
+ var SignalEvents = {
1702
+ ABORT: "abort",
1703
+ TIMEOUT: "timeout"
1704
+ };
1705
+ var SignalErrors = {
1706
+ ABORT: "AbortError",
1707
+ TIMEOUT: "TimeoutError"
1708
+ };
1709
+ var eventListenerOptions = { once: true, passive: true };
1710
+ var abortEvent = () => new CustomEvent(SignalEvents.ABORT, { detail: { cause: SignalErrors.ABORT } });
1711
+ var timeoutEvent = () => new CustomEvent(SignalEvents.TIMEOUT, { detail: { cause: SignalErrors.TIMEOUT } });
1712
+ var requestBodyMethods = [HttpRequestMethod.POST, HttpRequestMethod.PUT, HttpRequestMethod.PATCH, HttpRequestMethod.DELETE];
1713
+ var internalServerError = new ResponseStatus(500, "Internal Server Error");
1714
+ var aborted = new ResponseStatus(499, "Aborted");
1715
+ var timedOut = new ResponseStatus(504, "Request Timeout");
1716
+ var retryStatusCodes = [408, 413, 429, 500, 502, 503, 504];
1717
+ var retryMethods = [HttpRequestMethod.GET, HttpRequestMethod.PUT, HttpRequestMethod.HEAD, HttpRequestMethod.DELETE, HttpRequestMethod.OPTIONS];
1718
+ var retryDelay = 300;
1719
+ var retryBackoffFactor = 2;
1720
+
1721
+ // src/signal-controller.ts
1722
+ var SignalController = class {
1723
+ abortSignal;
1724
+ abortController = new AbortController();
1725
+ events = /* @__PURE__ */ new Map();
1726
+ /**
1727
+ * Creates a new SignalController instance.
1728
+ * @param options - The options for the SignalController.
1729
+ * @param options.signal - The signal to listen for abort events. Defaults to the internal abort signal.
1730
+ * @param options.timeout - The timeout value in milliseconds. Defaults to Infinity.
1731
+ * @throws {RangeError} If the timeout value is negative.
1732
+ */
1733
+ constructor({ signal, timeout = Infinity } = {}) {
1734
+ if (timeout < 0) {
1735
+ throw new RangeError("The timeout cannot be negative");
1736
+ }
1737
+ const signals = [this.abortController.signal];
1738
+ if (signal != null) {
1739
+ signals.push(signal);
1740
+ }
1741
+ if (timeout !== Infinity) {
1742
+ signals.push(AbortSignal.timeout(timeout));
1743
+ }
1744
+ (this.abortSignal = AbortSignal.any(signals)).addEventListener(SignalEvents.ABORT, this, eventListenerOptions);
1745
+ }
1746
+ /**
1747
+ * Handles the 'abort' event. If the event is caused by a timeout, the 'timeout' event is dispatched.
1748
+ * Guards against a timeout firing after a manual abort to prevent spurious timeout events.
1749
+ * @param event The event to abort with
1750
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#specifying_this_using_bind
1856
1751
  */
1857
- setAll(parameters) {
1858
- for (const [key, value] of _ParameterMap.#entries(parameters)) {
1859
- this.set(key, value);
1752
+ handleEvent({ target: { reason } }) {
1753
+ if (this.abortController.signal.aborted) {
1754
+ return;
1860
1755
  }
1861
- return this;
1756
+ if (reason instanceof DOMException && reason.name === SignalErrors.TIMEOUT) {
1757
+ this.abortSignal.dispatchEvent(timeoutEvent());
1758
+ }
1759
+ }
1760
+ /**
1761
+ * Gets the signal. This signal will be able to abort the request, but will not be notified if the request is aborted by the timeout.
1762
+ * @returns The signal
1763
+ */
1764
+ get signal() {
1765
+ return this.abortSignal;
1766
+ }
1767
+ /**
1768
+ * Adds an event listener for the 'abort' event.
1769
+ *
1770
+ * @param eventListener The listener to add
1771
+ * @returns The SignalController
1772
+ */
1773
+ onAbort(eventListener) {
1774
+ return this.addEventListener(SignalEvents.ABORT, eventListener);
1775
+ }
1776
+ /**
1777
+ * Adds an event listener for the 'timeout' event.
1778
+ *
1779
+ * @param eventListener The listener to add
1780
+ * @returns The SignalController
1781
+ */
1782
+ onTimeout(eventListener) {
1783
+ return this.addEventListener(SignalEvents.TIMEOUT, eventListener);
1784
+ }
1785
+ /**
1786
+ * Aborts the signal.
1787
+ *
1788
+ * @param event The event to abort with
1789
+ */
1790
+ abort(event = abortEvent()) {
1791
+ this.abortController.abort(event.detail?.cause);
1862
1792
  }
1863
1793
  /**
1864
- * Returns the value associated to the key, or undefined if there is none.
1865
- * If the key has multiple values, the first value will be returned.
1866
- * If the key has no values, undefined will be returned.
1794
+ * Removes all event listeners from the signal.
1867
1795
  *
1868
- * @override
1869
- * @param {string} key The key to get.
1870
- * @returns {*} The value associated to the key, or undefined if there is none.
1796
+ * @returns The SignalController
1871
1797
  */
1872
- get(key) {
1873
- return super.get(key)?.[0];
1798
+ destroy() {
1799
+ this.abortSignal.removeEventListener(SignalEvents.ABORT, this, eventListenerOptions);
1800
+ for (const [eventListener, type] of this.events) {
1801
+ this.abortSignal.removeEventListener(type, eventListener, eventListenerOptions);
1802
+ }
1803
+ this.events.clear();
1804
+ return this;
1874
1805
  }
1875
1806
  /**
1876
- * Returns an array of all values associated to the key, or undefined if there are none.
1807
+ * Adds an event listener for the specified event type.
1877
1808
  *
1878
- * @param {string} key The key to get.
1879
- * @returns {Array<*>} An array of all values associated to the key, or undefined if there are none.
1809
+ * @param type The event type to listen for
1810
+ * @param eventListener The listener to add
1811
+ * @returns The SignalController
1880
1812
  */
1881
- getAll(key) {
1882
- return super.get(key);
1813
+ addEventListener(type, eventListener) {
1814
+ this.abortSignal.addEventListener(type, eventListener, eventListenerOptions);
1815
+ this.events.set(eventListener, type);
1816
+ return this;
1883
1817
  }
1884
1818
  /**
1885
- * Appends a new value to an existing key inside a ParameterMap, or adds the new key if it does not exist.
1819
+ * A String value that is used in the creation of the default string
1820
+ * description of an object. Called by the built-in method {@link Object.prototype.toString}.
1886
1821
  *
1887
- * @param {string} key The key to append.
1888
- * @param {*} value The value to append.
1889
- * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value to allow chaining.
1822
+ * @returns The default string description of this object.
1890
1823
  */
1891
- append(key, value) {
1892
- const array = super.get(key);
1893
- if (array?.length > 0) {
1894
- array.push(value);
1824
+ get [Symbol.toStringTag]() {
1825
+ return "SignalController";
1826
+ }
1827
+ };
1828
+
1829
+ // node_modules/.pnpm/dompurify@3.3.3/node_modules/dompurify/dist/purify.es.mjs
1830
+ var {
1831
+ entries,
1832
+ setPrototypeOf,
1833
+ isFrozen,
1834
+ getPrototypeOf,
1835
+ getOwnPropertyDescriptor
1836
+ } = Object;
1837
+ var {
1838
+ freeze,
1839
+ seal,
1840
+ create
1841
+ } = Object;
1842
+ var {
1843
+ apply,
1844
+ construct
1845
+ } = typeof Reflect !== "undefined" && Reflect;
1846
+ if (!freeze) {
1847
+ freeze = function freeze2(x) {
1848
+ return x;
1849
+ };
1850
+ }
1851
+ if (!seal) {
1852
+ seal = function seal2(x) {
1853
+ return x;
1854
+ };
1855
+ }
1856
+ if (!apply) {
1857
+ apply = function apply2(func, thisArg) {
1858
+ for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
1859
+ args[_key - 2] = arguments[_key];
1860
+ }
1861
+ return func.apply(thisArg, args);
1862
+ };
1863
+ }
1864
+ if (!construct) {
1865
+ construct = function construct2(Func) {
1866
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
1867
+ args[_key2 - 1] = arguments[_key2];
1868
+ }
1869
+ return new Func(...args);
1870
+ };
1871
+ }
1872
+ var arrayForEach = unapply(Array.prototype.forEach);
1873
+ var arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
1874
+ var arrayPop = unapply(Array.prototype.pop);
1875
+ var arrayPush = unapply(Array.prototype.push);
1876
+ var arraySplice = unapply(Array.prototype.splice);
1877
+ var stringToLowerCase = unapply(String.prototype.toLowerCase);
1878
+ var stringToString = unapply(String.prototype.toString);
1879
+ var stringMatch = unapply(String.prototype.match);
1880
+ var stringReplace = unapply(String.prototype.replace);
1881
+ var stringIndexOf = unapply(String.prototype.indexOf);
1882
+ var stringTrim = unapply(String.prototype.trim);
1883
+ var objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
1884
+ var regExpTest = unapply(RegExp.prototype.test);
1885
+ var typeErrorCreate = unconstruct(TypeError);
1886
+ function unapply(func) {
1887
+ return function(thisArg) {
1888
+ if (thisArg instanceof RegExp) {
1889
+ thisArg.lastIndex = 0;
1890
+ }
1891
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
1892
+ args[_key3 - 1] = arguments[_key3];
1893
+ }
1894
+ return apply(func, thisArg, args);
1895
+ };
1896
+ }
1897
+ function unconstruct(Func) {
1898
+ return function() {
1899
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
1900
+ args[_key4] = arguments[_key4];
1901
+ }
1902
+ return construct(Func, args);
1903
+ };
1904
+ }
1905
+ function addToSet(set, array) {
1906
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : stringToLowerCase;
1907
+ if (setPrototypeOf) {
1908
+ setPrototypeOf(set, null);
1909
+ }
1910
+ let l = array.length;
1911
+ while (l--) {
1912
+ let element = array[l];
1913
+ if (typeof element === "string") {
1914
+ const lcElement = transformCaseFunc(element);
1915
+ if (lcElement !== element) {
1916
+ if (!isFrozen(array)) {
1917
+ array[l] = lcElement;
1918
+ }
1919
+ element = lcElement;
1920
+ }
1921
+ }
1922
+ set[element] = true;
1923
+ }
1924
+ return set;
1925
+ }
1926
+ function cleanArray(array) {
1927
+ for (let index = 0; index < array.length; index++) {
1928
+ const isPropertyExist = objectHasOwnProperty(array, index);
1929
+ if (!isPropertyExist) {
1930
+ array[index] = null;
1931
+ }
1932
+ }
1933
+ return array;
1934
+ }
1935
+ function clone(object) {
1936
+ const newObject = create(null);
1937
+ for (const [property, value] of entries(object)) {
1938
+ const isPropertyExist = objectHasOwnProperty(object, property);
1939
+ if (isPropertyExist) {
1940
+ if (Array.isArray(value)) {
1941
+ newObject[property] = cleanArray(value);
1942
+ } else if (value && typeof value === "object" && value.constructor === Object) {
1943
+ newObject[property] = clone(value);
1944
+ } else {
1945
+ newObject[property] = value;
1946
+ }
1947
+ }
1948
+ }
1949
+ return newObject;
1950
+ }
1951
+ function lookupGetter(object, prop) {
1952
+ while (object !== null) {
1953
+ const desc = getOwnPropertyDescriptor(object, prop);
1954
+ if (desc) {
1955
+ if (desc.get) {
1956
+ return unapply(desc.get);
1957
+ }
1958
+ if (typeof desc.value === "function") {
1959
+ return unapply(desc.value);
1960
+ }
1961
+ }
1962
+ object = getPrototypeOf(object);
1963
+ }
1964
+ function fallbackValue() {
1965
+ return null;
1966
+ }
1967
+ return fallbackValue;
1968
+ }
1969
+ var html$1 = freeze(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]);
1970
+ var svg$1 = freeze(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "enterkeyhint", "exportparts", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "inputmode", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "part", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]);
1971
+ var svgFilters = freeze(["feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence"]);
1972
+ var svgDisallowed = freeze(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]);
1973
+ var mathMl$1 = freeze(["math", "menclose", "merror", "mfenced", "mfrac", "mglyph", "mi", "mlabeledtr", "mmultiscripts", "mn", "mo", "mover", "mpadded", "mphantom", "mroot", "mrow", "ms", "mspace", "msqrt", "mstyle", "msub", "msup", "msubsup", "mtable", "mtd", "mtext", "mtr", "munder", "munderover", "mprescripts"]);
1974
+ var mathMlDisallowed = freeze(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]);
1975
+ var text = freeze(["#text"]);
1976
+ var html = freeze(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns", "slot"]);
1977
+ var svg = freeze(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "mask-type", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]);
1978
+ var mathMl = freeze(["accent", "accentunder", "align", "bevelled", "close", "columnsalign", "columnlines", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lspace", "lquote", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]);
1979
+ var xml = freeze(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]);
1980
+ var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm);
1981
+ var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
1982
+ var TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm);
1983
+ var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/);
1984
+ var ARIA_ATTR = seal(/^aria-[\-\w]+$/);
1985
+ var IS_ALLOWED_URI = seal(
1986
+ /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
1987
+ // eslint-disable-line no-useless-escape
1988
+ );
1989
+ var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
1990
+ var ATTR_WHITESPACE = seal(
1991
+ /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g
1992
+ // eslint-disable-line no-control-regex
1993
+ );
1994
+ var DOCTYPE_NAME = seal(/^html$/i);
1995
+ var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
1996
+ var EXPRESSIONS = /* @__PURE__ */ Object.freeze({
1997
+ __proto__: null,
1998
+ ARIA_ATTR,
1999
+ ATTR_WHITESPACE,
2000
+ CUSTOM_ELEMENT,
2001
+ DATA_ATTR,
2002
+ DOCTYPE_NAME,
2003
+ ERB_EXPR,
2004
+ IS_ALLOWED_URI,
2005
+ IS_SCRIPT_OR_DATA,
2006
+ MUSTACHE_EXPR,
2007
+ TMPLIT_EXPR
2008
+ });
2009
+ var NODE_TYPE = {
2010
+ element: 1,
2011
+ attribute: 2,
2012
+ text: 3,
2013
+ cdataSection: 4,
2014
+ entityReference: 5,
2015
+ // Deprecated
2016
+ entityNode: 6,
2017
+ // Deprecated
2018
+ progressingInstruction: 7,
2019
+ comment: 8,
2020
+ document: 9,
2021
+ documentType: 10,
2022
+ documentFragment: 11,
2023
+ notation: 12
2024
+ // Deprecated
2025
+ };
2026
+ var getGlobal = function getGlobal2() {
2027
+ return typeof window === "undefined" ? null : window;
2028
+ };
2029
+ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy2(trustedTypes, purifyHostElement) {
2030
+ if (typeof trustedTypes !== "object" || typeof trustedTypes.createPolicy !== "function") {
2031
+ return null;
2032
+ }
2033
+ let suffix = null;
2034
+ const ATTR_NAME = "data-tt-policy-suffix";
2035
+ if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
2036
+ suffix = purifyHostElement.getAttribute(ATTR_NAME);
2037
+ }
2038
+ const policyName = "dompurify" + (suffix ? "#" + suffix : "");
2039
+ try {
2040
+ return trustedTypes.createPolicy(policyName, {
2041
+ createHTML(html2) {
2042
+ return html2;
2043
+ },
2044
+ createScriptURL(scriptUrl) {
2045
+ return scriptUrl;
2046
+ }
2047
+ });
2048
+ } catch (_) {
2049
+ console.warn("TrustedTypes policy " + policyName + " could not be created.");
2050
+ return null;
2051
+ }
2052
+ };
2053
+ var _createHooksMap = function _createHooksMap2() {
2054
+ return {
2055
+ afterSanitizeAttributes: [],
2056
+ afterSanitizeElements: [],
2057
+ afterSanitizeShadowDOM: [],
2058
+ beforeSanitizeAttributes: [],
2059
+ beforeSanitizeElements: [],
2060
+ beforeSanitizeShadowDOM: [],
2061
+ uponSanitizeAttribute: [],
2062
+ uponSanitizeElement: [],
2063
+ uponSanitizeShadowNode: []
2064
+ };
2065
+ };
2066
+ function createDOMPurify() {
2067
+ let window2 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
2068
+ const DOMPurify = (root) => createDOMPurify(root);
2069
+ DOMPurify.version = "3.3.3";
2070
+ DOMPurify.removed = [];
2071
+ if (!window2 || !window2.document || window2.document.nodeType !== NODE_TYPE.document || !window2.Element) {
2072
+ DOMPurify.isSupported = false;
2073
+ return DOMPurify;
2074
+ }
2075
+ let {
2076
+ document: document2
2077
+ } = window2;
2078
+ const originalDocument = document2;
2079
+ const currentScript = originalDocument.currentScript;
2080
+ const {
2081
+ DocumentFragment: DocumentFragment2,
2082
+ HTMLTemplateElement,
2083
+ Node,
2084
+ Element,
2085
+ NodeFilter,
2086
+ NamedNodeMap = window2.NamedNodeMap || window2.MozNamedAttrMap,
2087
+ HTMLFormElement,
2088
+ DOMParser: DOMParser2,
2089
+ trustedTypes
2090
+ } = window2;
2091
+ const ElementPrototype = Element.prototype;
2092
+ const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
2093
+ const remove = lookupGetter(ElementPrototype, "remove");
2094
+ const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
2095
+ const getChildNodes = lookupGetter(ElementPrototype, "childNodes");
2096
+ const getParentNode = lookupGetter(ElementPrototype, "parentNode");
2097
+ if (typeof HTMLTemplateElement === "function") {
2098
+ const template = document2.createElement("template");
2099
+ if (template.content && template.content.ownerDocument) {
2100
+ document2 = template.content.ownerDocument;
2101
+ }
2102
+ }
2103
+ let trustedTypesPolicy;
2104
+ let emptyHTML = "";
2105
+ const {
2106
+ implementation,
2107
+ createNodeIterator,
2108
+ createDocumentFragment,
2109
+ getElementsByTagName
2110
+ } = document2;
2111
+ const {
2112
+ importNode
2113
+ } = originalDocument;
2114
+ let hooks = _createHooksMap();
2115
+ DOMPurify.isSupported = typeof entries === "function" && typeof getParentNode === "function" && implementation && implementation.createHTMLDocument !== void 0;
2116
+ const {
2117
+ MUSTACHE_EXPR: MUSTACHE_EXPR2,
2118
+ ERB_EXPR: ERB_EXPR2,
2119
+ TMPLIT_EXPR: TMPLIT_EXPR2,
2120
+ DATA_ATTR: DATA_ATTR2,
2121
+ ARIA_ATTR: ARIA_ATTR2,
2122
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA2,
2123
+ ATTR_WHITESPACE: ATTR_WHITESPACE2,
2124
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT2
2125
+ } = EXPRESSIONS;
2126
+ let {
2127
+ IS_ALLOWED_URI: IS_ALLOWED_URI$1
2128
+ } = EXPRESSIONS;
2129
+ let ALLOWED_TAGS = null;
2130
+ const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
2131
+ let ALLOWED_ATTR = null;
2132
+ const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
2133
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
2134
+ tagNameCheck: {
2135
+ writable: true,
2136
+ configurable: false,
2137
+ enumerable: true,
2138
+ value: null
2139
+ },
2140
+ attributeNameCheck: {
2141
+ writable: true,
2142
+ configurable: false,
2143
+ enumerable: true,
2144
+ value: null
2145
+ },
2146
+ allowCustomizedBuiltInElements: {
2147
+ writable: true,
2148
+ configurable: false,
2149
+ enumerable: true,
2150
+ value: false
2151
+ }
2152
+ }));
2153
+ let FORBID_TAGS = null;
2154
+ let FORBID_ATTR = null;
2155
+ const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {
2156
+ tagCheck: {
2157
+ writable: true,
2158
+ configurable: false,
2159
+ enumerable: true,
2160
+ value: null
2161
+ },
2162
+ attributeCheck: {
2163
+ writable: true,
2164
+ configurable: false,
2165
+ enumerable: true,
2166
+ value: null
2167
+ }
2168
+ }));
2169
+ let ALLOW_ARIA_ATTR = true;
2170
+ let ALLOW_DATA_ATTR = true;
2171
+ let ALLOW_UNKNOWN_PROTOCOLS = false;
2172
+ let ALLOW_SELF_CLOSE_IN_ATTR = true;
2173
+ let SAFE_FOR_TEMPLATES = false;
2174
+ let SAFE_FOR_XML = true;
2175
+ let WHOLE_DOCUMENT = false;
2176
+ let SET_CONFIG = false;
2177
+ let FORCE_BODY = false;
2178
+ let RETURN_DOM = false;
2179
+ let RETURN_DOM_FRAGMENT = false;
2180
+ let RETURN_TRUSTED_TYPE = false;
2181
+ let SANITIZE_DOM = true;
2182
+ let SANITIZE_NAMED_PROPS = false;
2183
+ const SANITIZE_NAMED_PROPS_PREFIX = "user-content-";
2184
+ let KEEP_CONTENT = true;
2185
+ let IN_PLACE = false;
2186
+ let USE_PROFILES = {};
2187
+ let FORBID_CONTENTS = null;
2188
+ const DEFAULT_FORBID_CONTENTS = addToSet({}, ["annotation-xml", "audio", "colgroup", "desc", "foreignobject", "head", "iframe", "math", "mi", "mn", "mo", "ms", "mtext", "noembed", "noframes", "noscript", "plaintext", "script", "style", "svg", "template", "thead", "title", "video", "xmp"]);
2189
+ let DATA_URI_TAGS = null;
2190
+ const DEFAULT_DATA_URI_TAGS = addToSet({}, ["audio", "video", "img", "source", "image", "track"]);
2191
+ let URI_SAFE_ATTRIBUTES = null;
2192
+ const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ["alt", "class", "for", "id", "label", "name", "pattern", "placeholder", "role", "summary", "title", "value", "style", "xmlns"]);
2193
+ const MATHML_NAMESPACE = "http://www.w3.org/1998/Math/MathML";
2194
+ const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
2195
+ const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
2196
+ let NAMESPACE = HTML_NAMESPACE;
2197
+ let IS_EMPTY_INPUT = false;
2198
+ let ALLOWED_NAMESPACES = null;
2199
+ const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
2200
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ["mi", "mo", "mn", "ms", "mtext"]);
2201
+ let HTML_INTEGRATION_POINTS = addToSet({}, ["annotation-xml"]);
2202
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ["title", "style", "font", "a", "script"]);
2203
+ let PARSER_MEDIA_TYPE = null;
2204
+ const SUPPORTED_PARSER_MEDIA_TYPES = ["application/xhtml+xml", "text/html"];
2205
+ const DEFAULT_PARSER_MEDIA_TYPE = "text/html";
2206
+ let transformCaseFunc = null;
2207
+ let CONFIG = null;
2208
+ const formElement = document2.createElement("form");
2209
+ const isRegexOrFunction = function isRegexOrFunction2(testValue) {
2210
+ return testValue instanceof RegExp || testValue instanceof Function;
2211
+ };
2212
+ const _parseConfig = function _parseConfig2() {
2213
+ let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
2214
+ if (CONFIG && CONFIG === cfg) {
2215
+ return;
2216
+ }
2217
+ if (!cfg || typeof cfg !== "object") {
2218
+ cfg = {};
2219
+ }
2220
+ cfg = clone(cfg);
2221
+ PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
2222
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
2223
+ transformCaseFunc = PARSER_MEDIA_TYPE === "application/xhtml+xml" ? stringToString : stringToLowerCase;
2224
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, "ALLOWED_TAGS") ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
2225
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, "ALLOWED_ATTR") ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
2226
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, "ALLOWED_NAMESPACES") ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
2227
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, "ADD_URI_SAFE_ATTR") ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
2228
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, "ADD_DATA_URI_TAGS") ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
2229
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, "FORBID_CONTENTS") ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
2230
+ FORBID_TAGS = objectHasOwnProperty(cfg, "FORBID_TAGS") ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
2231
+ FORBID_ATTR = objectHasOwnProperty(cfg, "FORBID_ATTR") ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
2232
+ USE_PROFILES = objectHasOwnProperty(cfg, "USE_PROFILES") ? cfg.USE_PROFILES : false;
2233
+ ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;
2234
+ ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;
2235
+ ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false;
2236
+ ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false;
2237
+ SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false;
2238
+ SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false;
2239
+ WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false;
2240
+ RETURN_DOM = cfg.RETURN_DOM || false;
2241
+ RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false;
2242
+ RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false;
2243
+ FORCE_BODY = cfg.FORCE_BODY || false;
2244
+ SANITIZE_DOM = cfg.SANITIZE_DOM !== false;
2245
+ SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false;
2246
+ KEEP_CONTENT = cfg.KEEP_CONTENT !== false;
2247
+ IN_PLACE = cfg.IN_PLACE || false;
2248
+ IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
2249
+ NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
2250
+ MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
2251
+ HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
2252
+ CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
2253
+ if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
2254
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
2255
+ }
2256
+ if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
2257
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
2258
+ }
2259
+ if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === "boolean") {
2260
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
2261
+ }
2262
+ if (SAFE_FOR_TEMPLATES) {
2263
+ ALLOW_DATA_ATTR = false;
2264
+ }
2265
+ if (RETURN_DOM_FRAGMENT) {
2266
+ RETURN_DOM = true;
2267
+ }
2268
+ if (USE_PROFILES) {
2269
+ ALLOWED_TAGS = addToSet({}, text);
2270
+ ALLOWED_ATTR = create(null);
2271
+ if (USE_PROFILES.html === true) {
2272
+ addToSet(ALLOWED_TAGS, html$1);
2273
+ addToSet(ALLOWED_ATTR, html);
2274
+ }
2275
+ if (USE_PROFILES.svg === true) {
2276
+ addToSet(ALLOWED_TAGS, svg$1);
2277
+ addToSet(ALLOWED_ATTR, svg);
2278
+ addToSet(ALLOWED_ATTR, xml);
2279
+ }
2280
+ if (USE_PROFILES.svgFilters === true) {
2281
+ addToSet(ALLOWED_TAGS, svgFilters);
2282
+ addToSet(ALLOWED_ATTR, svg);
2283
+ addToSet(ALLOWED_ATTR, xml);
2284
+ }
2285
+ if (USE_PROFILES.mathMl === true) {
2286
+ addToSet(ALLOWED_TAGS, mathMl$1);
2287
+ addToSet(ALLOWED_ATTR, mathMl);
2288
+ addToSet(ALLOWED_ATTR, xml);
2289
+ }
2290
+ }
2291
+ if (!objectHasOwnProperty(cfg, "ADD_TAGS")) {
2292
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
2293
+ }
2294
+ if (!objectHasOwnProperty(cfg, "ADD_ATTR")) {
2295
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
2296
+ }
2297
+ if (cfg.ADD_TAGS) {
2298
+ if (typeof cfg.ADD_TAGS === "function") {
2299
+ EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
2300
+ } else {
2301
+ if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
2302
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
2303
+ }
2304
+ addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
2305
+ }
2306
+ }
2307
+ if (cfg.ADD_ATTR) {
2308
+ if (typeof cfg.ADD_ATTR === "function") {
2309
+ EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
2310
+ } else {
2311
+ if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
2312
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
2313
+ }
2314
+ addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
2315
+ }
2316
+ }
2317
+ if (cfg.ADD_URI_SAFE_ATTR) {
2318
+ addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
2319
+ }
2320
+ if (cfg.FORBID_CONTENTS) {
2321
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
2322
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
2323
+ }
2324
+ addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
2325
+ }
2326
+ if (cfg.ADD_FORBID_CONTENTS) {
2327
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
2328
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
2329
+ }
2330
+ addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
2331
+ }
2332
+ if (KEEP_CONTENT) {
2333
+ ALLOWED_TAGS["#text"] = true;
2334
+ }
2335
+ if (WHOLE_DOCUMENT) {
2336
+ addToSet(ALLOWED_TAGS, ["html", "head", "body"]);
2337
+ }
2338
+ if (ALLOWED_TAGS.table) {
2339
+ addToSet(ALLOWED_TAGS, ["tbody"]);
2340
+ delete FORBID_TAGS.tbody;
2341
+ }
2342
+ if (cfg.TRUSTED_TYPES_POLICY) {
2343
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== "function") {
2344
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
2345
+ }
2346
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== "function") {
2347
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
2348
+ }
2349
+ trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
2350
+ emptyHTML = trustedTypesPolicy.createHTML("");
2351
+ } else {
2352
+ if (trustedTypesPolicy === void 0) {
2353
+ trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
2354
+ }
2355
+ if (trustedTypesPolicy !== null && typeof emptyHTML === "string") {
2356
+ emptyHTML = trustedTypesPolicy.createHTML("");
2357
+ }
2358
+ }
2359
+ if (freeze) {
2360
+ freeze(cfg);
2361
+ }
2362
+ CONFIG = cfg;
2363
+ };
2364
+ const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
2365
+ const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
2366
+ const _checkValidNamespace = function _checkValidNamespace2(element) {
2367
+ let parent = getParentNode(element);
2368
+ if (!parent || !parent.tagName) {
2369
+ parent = {
2370
+ namespaceURI: NAMESPACE,
2371
+ tagName: "template"
2372
+ };
2373
+ }
2374
+ const tagName = stringToLowerCase(element.tagName);
2375
+ const parentTagName = stringToLowerCase(parent.tagName);
2376
+ if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
2377
+ return false;
2378
+ }
2379
+ if (element.namespaceURI === SVG_NAMESPACE) {
2380
+ if (parent.namespaceURI === HTML_NAMESPACE) {
2381
+ return tagName === "svg";
2382
+ }
2383
+ if (parent.namespaceURI === MATHML_NAMESPACE) {
2384
+ return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
2385
+ }
2386
+ return Boolean(ALL_SVG_TAGS[tagName]);
2387
+ }
2388
+ if (element.namespaceURI === MATHML_NAMESPACE) {
2389
+ if (parent.namespaceURI === HTML_NAMESPACE) {
2390
+ return tagName === "math";
2391
+ }
2392
+ if (parent.namespaceURI === SVG_NAMESPACE) {
2393
+ return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName];
2394
+ }
2395
+ return Boolean(ALL_MATHML_TAGS[tagName]);
2396
+ }
2397
+ if (element.namespaceURI === HTML_NAMESPACE) {
2398
+ if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
2399
+ return false;
2400
+ }
2401
+ if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
2402
+ return false;
2403
+ }
2404
+ return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
2405
+ }
2406
+ if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[element.namespaceURI]) {
2407
+ return true;
2408
+ }
2409
+ return false;
2410
+ };
2411
+ const _forceRemove = function _forceRemove2(node) {
2412
+ arrayPush(DOMPurify.removed, {
2413
+ element: node
2414
+ });
2415
+ try {
2416
+ getParentNode(node).removeChild(node);
2417
+ } catch (_) {
2418
+ remove(node);
2419
+ }
2420
+ };
2421
+ const _removeAttribute = function _removeAttribute2(name, element) {
2422
+ try {
2423
+ arrayPush(DOMPurify.removed, {
2424
+ attribute: element.getAttributeNode(name),
2425
+ from: element
2426
+ });
2427
+ } catch (_) {
2428
+ arrayPush(DOMPurify.removed, {
2429
+ attribute: null,
2430
+ from: element
2431
+ });
2432
+ }
2433
+ element.removeAttribute(name);
2434
+ if (name === "is") {
2435
+ if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
2436
+ try {
2437
+ _forceRemove(element);
2438
+ } catch (_) {
2439
+ }
2440
+ } else {
2441
+ try {
2442
+ element.setAttribute(name, "");
2443
+ } catch (_) {
2444
+ }
2445
+ }
2446
+ }
2447
+ };
2448
+ const _initDocument = function _initDocument2(dirty) {
2449
+ let doc = null;
2450
+ let leadingWhitespace = null;
2451
+ if (FORCE_BODY) {
2452
+ dirty = "<remove></remove>" + dirty;
2453
+ } else {
2454
+ const matches = stringMatch(dirty, /^[\r\n\t ]+/);
2455
+ leadingWhitespace = matches && matches[0];
2456
+ }
2457
+ if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && NAMESPACE === HTML_NAMESPACE) {
2458
+ dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + "</body></html>";
2459
+ }
2460
+ const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
2461
+ if (NAMESPACE === HTML_NAMESPACE) {
2462
+ try {
2463
+ doc = new DOMParser2().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
2464
+ } catch (_) {
2465
+ }
2466
+ }
2467
+ if (!doc || !doc.documentElement) {
2468
+ doc = implementation.createDocument(NAMESPACE, "template", null);
2469
+ try {
2470
+ doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
2471
+ } catch (_) {
2472
+ }
2473
+ }
2474
+ const body = doc.body || doc.documentElement;
2475
+ if (dirty && leadingWhitespace) {
2476
+ body.insertBefore(document2.createTextNode(leadingWhitespace), body.childNodes[0] || null);
2477
+ }
2478
+ if (NAMESPACE === HTML_NAMESPACE) {
2479
+ return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? "html" : "body")[0];
2480
+ }
2481
+ return WHOLE_DOCUMENT ? doc.documentElement : body;
2482
+ };
2483
+ const _createNodeIterator = function _createNodeIterator2(root) {
2484
+ return createNodeIterator.call(
2485
+ root.ownerDocument || root,
2486
+ root,
2487
+ // eslint-disable-next-line no-bitwise
2488
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION,
2489
+ null
2490
+ );
2491
+ };
2492
+ const _isClobbered = function _isClobbered2(element) {
2493
+ return element instanceof HTMLFormElement && (typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function");
2494
+ };
2495
+ const _isNode = function _isNode2(value) {
2496
+ return typeof Node === "function" && value instanceof Node;
2497
+ };
2498
+ function _executeHooks(hooks2, currentNode, data) {
2499
+ arrayForEach(hooks2, (hook) => {
2500
+ hook.call(DOMPurify, currentNode, data, CONFIG);
2501
+ });
2502
+ }
2503
+ const _sanitizeElements = function _sanitizeElements2(currentNode) {
2504
+ let content = null;
2505
+ _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
2506
+ if (_isClobbered(currentNode)) {
2507
+ _forceRemove(currentNode);
2508
+ return true;
2509
+ }
2510
+ const tagName = transformCaseFunc(currentNode.nodeName);
2511
+ _executeHooks(hooks.uponSanitizeElement, currentNode, {
2512
+ tagName,
2513
+ allowedTags: ALLOWED_TAGS
2514
+ });
2515
+ if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
2516
+ _forceRemove(currentNode);
2517
+ return true;
2518
+ }
2519
+ if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
2520
+ _forceRemove(currentNode);
2521
+ return true;
2522
+ }
2523
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
2524
+ _forceRemove(currentNode);
2525
+ return true;
2526
+ }
2527
+ if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
2528
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
2529
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
2530
+ return false;
2531
+ }
2532
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
2533
+ return false;
2534
+ }
2535
+ }
2536
+ if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
2537
+ const parentNode = getParentNode(currentNode) || currentNode.parentNode;
2538
+ const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
2539
+ if (childNodes && parentNode) {
2540
+ const childCount = childNodes.length;
2541
+ for (let i = childCount - 1; i >= 0; --i) {
2542
+ const childClone = cloneNode(childNodes[i], true);
2543
+ childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
2544
+ parentNode.insertBefore(childClone, getNextSibling(currentNode));
2545
+ }
2546
+ }
2547
+ }
2548
+ _forceRemove(currentNode);
2549
+ return true;
2550
+ }
2551
+ if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
2552
+ _forceRemove(currentNode);
2553
+ return true;
2554
+ }
2555
+ if ((tagName === "noscript" || tagName === "noembed" || tagName === "noframes") && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
2556
+ _forceRemove(currentNode);
2557
+ return true;
2558
+ }
2559
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
2560
+ content = currentNode.textContent;
2561
+ arrayForEach([MUSTACHE_EXPR2, ERB_EXPR2, TMPLIT_EXPR2], (expr) => {
2562
+ content = stringReplace(content, expr, " ");
2563
+ });
2564
+ if (currentNode.textContent !== content) {
2565
+ arrayPush(DOMPurify.removed, {
2566
+ element: currentNode.cloneNode()
2567
+ });
2568
+ currentNode.textContent = content;
2569
+ }
2570
+ }
2571
+ _executeHooks(hooks.afterSanitizeElements, currentNode, null);
2572
+ return false;
2573
+ };
2574
+ const _isValidAttribute = function _isValidAttribute2(lcTag, lcName, value) {
2575
+ if (FORBID_ATTR[lcName]) {
2576
+ return false;
2577
+ }
2578
+ if (SANITIZE_DOM && (lcName === "id" || lcName === "name") && (value in document2 || value in formElement)) {
2579
+ return false;
2580
+ }
2581
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR2, lcName)) ;
2582
+ else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR2, lcName)) ;
2583
+ else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ;
2584
+ else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
2585
+ if (
2586
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
2587
+ // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
2588
+ // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
2589
+ _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) || // Alternative, second condition checks if it's an `is`-attribute, AND
2590
+ // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
2591
+ lcName === "is" && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))
2592
+ ) ;
2593
+ else {
2594
+ return false;
2595
+ }
2596
+ } else if (URI_SAFE_ATTRIBUTES[lcName]) ;
2597
+ else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE2, ""))) ;
2598
+ else if ((lcName === "src" || lcName === "xlink:href" || lcName === "href") && lcTag !== "script" && stringIndexOf(value, "data:") === 0 && DATA_URI_TAGS[lcTag]) ;
2599
+ else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA2, stringReplace(value, ATTR_WHITESPACE2, ""))) ;
2600
+ else if (value) {
2601
+ return false;
2602
+ } else ;
2603
+ return true;
2604
+ };
2605
+ const _isBasicCustomElement = function _isBasicCustomElement2(tagName) {
2606
+ return tagName !== "annotation-xml" && stringMatch(tagName, CUSTOM_ELEMENT2);
2607
+ };
2608
+ const _sanitizeAttributes = function _sanitizeAttributes2(currentNode) {
2609
+ _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
2610
+ const {
2611
+ attributes
2612
+ } = currentNode;
2613
+ if (!attributes || _isClobbered(currentNode)) {
2614
+ return;
2615
+ }
2616
+ const hookEvent = {
2617
+ attrName: "",
2618
+ attrValue: "",
2619
+ keepAttr: true,
2620
+ allowedAttributes: ALLOWED_ATTR,
2621
+ forceKeepAttr: void 0
2622
+ };
2623
+ let l = attributes.length;
2624
+ while (l--) {
2625
+ const attr = attributes[l];
2626
+ const {
2627
+ name,
2628
+ namespaceURI,
2629
+ value: attrValue
2630
+ } = attr;
2631
+ const lcName = transformCaseFunc(name);
2632
+ const initValue = attrValue;
2633
+ let value = name === "value" ? initValue : stringTrim(initValue);
2634
+ hookEvent.attrName = lcName;
2635
+ hookEvent.attrValue = value;
2636
+ hookEvent.keepAttr = true;
2637
+ hookEvent.forceKeepAttr = void 0;
2638
+ _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
2639
+ value = hookEvent.attrValue;
2640
+ if (SANITIZE_NAMED_PROPS && (lcName === "id" || lcName === "name")) {
2641
+ _removeAttribute(name, currentNode);
2642
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
2643
+ }
2644
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
2645
+ _removeAttribute(name, currentNode);
2646
+ continue;
2647
+ }
2648
+ if (lcName === "attributename" && stringMatch(value, "href")) {
2649
+ _removeAttribute(name, currentNode);
2650
+ continue;
2651
+ }
2652
+ if (hookEvent.forceKeepAttr) {
2653
+ continue;
2654
+ }
2655
+ if (!hookEvent.keepAttr) {
2656
+ _removeAttribute(name, currentNode);
2657
+ continue;
2658
+ }
2659
+ if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
2660
+ _removeAttribute(name, currentNode);
2661
+ continue;
2662
+ }
2663
+ if (SAFE_FOR_TEMPLATES) {
2664
+ arrayForEach([MUSTACHE_EXPR2, ERB_EXPR2, TMPLIT_EXPR2], (expr) => {
2665
+ value = stringReplace(value, expr, " ");
2666
+ });
2667
+ }
2668
+ const lcTag = transformCaseFunc(currentNode.nodeName);
2669
+ if (!_isValidAttribute(lcTag, lcName, value)) {
2670
+ _removeAttribute(name, currentNode);
2671
+ continue;
2672
+ }
2673
+ if (trustedTypesPolicy && typeof trustedTypes === "object" && typeof trustedTypes.getAttributeType === "function") {
2674
+ if (namespaceURI) ;
2675
+ else {
2676
+ switch (trustedTypes.getAttributeType(lcTag, lcName)) {
2677
+ case "TrustedHTML": {
2678
+ value = trustedTypesPolicy.createHTML(value);
2679
+ break;
2680
+ }
2681
+ case "TrustedScriptURL": {
2682
+ value = trustedTypesPolicy.createScriptURL(value);
2683
+ break;
2684
+ }
2685
+ }
2686
+ }
2687
+ }
2688
+ if (value !== initValue) {
2689
+ try {
2690
+ if (namespaceURI) {
2691
+ currentNode.setAttributeNS(namespaceURI, name, value);
2692
+ } else {
2693
+ currentNode.setAttribute(name, value);
2694
+ }
2695
+ if (_isClobbered(currentNode)) {
2696
+ _forceRemove(currentNode);
2697
+ } else {
2698
+ arrayPop(DOMPurify.removed);
2699
+ }
2700
+ } catch (_) {
2701
+ _removeAttribute(name, currentNode);
2702
+ }
2703
+ }
2704
+ }
2705
+ _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
2706
+ };
2707
+ const _sanitizeShadowDOM = function _sanitizeShadowDOM2(fragment) {
2708
+ let shadowNode = null;
2709
+ const shadowIterator = _createNodeIterator(fragment);
2710
+ _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
2711
+ while (shadowNode = shadowIterator.nextNode()) {
2712
+ _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
2713
+ _sanitizeElements(shadowNode);
2714
+ _sanitizeAttributes(shadowNode);
2715
+ if (shadowNode.content instanceof DocumentFragment2) {
2716
+ _sanitizeShadowDOM2(shadowNode.content);
2717
+ }
2718
+ }
2719
+ _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
2720
+ };
2721
+ DOMPurify.sanitize = function(dirty) {
2722
+ let cfg = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
2723
+ let body = null;
2724
+ let importedNode = null;
2725
+ let currentNode = null;
2726
+ let returnNode = null;
2727
+ IS_EMPTY_INPUT = !dirty;
2728
+ if (IS_EMPTY_INPUT) {
2729
+ dirty = "<!-->";
2730
+ }
2731
+ if (typeof dirty !== "string" && !_isNode(dirty)) {
2732
+ if (typeof dirty.toString === "function") {
2733
+ dirty = dirty.toString();
2734
+ if (typeof dirty !== "string") {
2735
+ throw typeErrorCreate("dirty is not a string, aborting");
2736
+ }
2737
+ } else {
2738
+ throw typeErrorCreate("toString is not a function");
2739
+ }
2740
+ }
2741
+ if (!DOMPurify.isSupported) {
2742
+ return dirty;
2743
+ }
2744
+ if (!SET_CONFIG) {
2745
+ _parseConfig(cfg);
2746
+ }
2747
+ DOMPurify.removed = [];
2748
+ if (typeof dirty === "string") {
2749
+ IN_PLACE = false;
2750
+ }
2751
+ if (IN_PLACE) {
2752
+ if (dirty.nodeName) {
2753
+ const tagName = transformCaseFunc(dirty.nodeName);
2754
+ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
2755
+ throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
2756
+ }
2757
+ }
2758
+ } else if (dirty instanceof Node) {
2759
+ body = _initDocument("<!---->");
2760
+ importedNode = body.ownerDocument.importNode(dirty, true);
2761
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === "BODY") {
2762
+ body = importedNode;
2763
+ } else if (importedNode.nodeName === "HTML") {
2764
+ body = importedNode;
2765
+ } else {
2766
+ body.appendChild(importedNode);
2767
+ }
1895
2768
  } else {
1896
- super.set(key, [value]);
2769
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
2770
+ dirty.indexOf("<") === -1) {
2771
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
2772
+ }
2773
+ body = _initDocument(dirty);
2774
+ if (!body) {
2775
+ return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : "";
2776
+ }
1897
2777
  }
1898
- return this;
1899
- }
1900
- /**
1901
- * Appends all key-value pairs from an iterable or object to the ParameterMap.
1902
- * If a key already exists, the value will be appended to the underlying {@link Array}.
1903
- * If a key does not exist, the key and value will be added to the ParameterMap.
1904
- *
1905
- * @param {Iterable<[string, *]>|Object} parameters The parameters to append.
1906
- * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value.
1907
- */
1908
- appendAll(parameters) {
1909
- for (const [key, value] of _ParameterMap.#entries(parameters)) {
1910
- this.append(key, value);
2778
+ if (body && FORCE_BODY) {
2779
+ _forceRemove(body.firstChild);
1911
2780
  }
1912
- return this;
1913
- }
1914
- /**
1915
- * Checks if a specific key has a specific value.
1916
- *
1917
- * @param {*} value The value to check.
1918
- * @returns {boolean} True if the key has the value, false otherwise.
1919
- */
1920
- hasValue(value) {
1921
- for (const array of super.values()) {
1922
- if (array.includes(value)) {
1923
- return true;
2781
+ const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
2782
+ while (currentNode = nodeIterator.nextNode()) {
2783
+ _sanitizeElements(currentNode);
2784
+ _sanitizeAttributes(currentNode);
2785
+ if (currentNode.content instanceof DocumentFragment2) {
2786
+ _sanitizeShadowDOM(currentNode.content);
1924
2787
  }
1925
2788
  }
1926
- return false;
1927
- }
1928
- /**
1929
- * Removes a specific value from a specific key.
1930
- *
1931
- * @param {*} value The value to remove.
1932
- * @returns {boolean} True if the value was removed, false otherwise.
1933
- */
1934
- deleteValue(value) {
1935
- for (const array of this.values()) {
1936
- if (array.includes(value)) {
1937
- return array.splice(array.indexOf(value), 1).length > 0;
2789
+ if (IN_PLACE) {
2790
+ return dirty;
2791
+ }
2792
+ if (RETURN_DOM) {
2793
+ if (RETURN_DOM_FRAGMENT) {
2794
+ returnNode = createDocumentFragment.call(body.ownerDocument);
2795
+ while (body.firstChild) {
2796
+ returnNode.appendChild(body.firstChild);
2797
+ }
2798
+ } else {
2799
+ returnNode = body;
1938
2800
  }
2801
+ if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
2802
+ returnNode = importNode.call(originalDocument, returnNode, true);
2803
+ }
2804
+ return returnNode;
2805
+ }
2806
+ let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
2807
+ if (WHOLE_DOCUMENT && ALLOWED_TAGS["!doctype"] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
2808
+ serializedHTML = "<!DOCTYPE " + body.ownerDocument.doctype.name + ">\n" + serializedHTML;
2809
+ }
2810
+ if (SAFE_FOR_TEMPLATES) {
2811
+ arrayForEach([MUSTACHE_EXPR2, ERB_EXPR2, TMPLIT_EXPR2], (expr) => {
2812
+ serializedHTML = stringReplace(serializedHTML, expr, " ");
2813
+ });
2814
+ }
2815
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
2816
+ };
2817
+ DOMPurify.setConfig = function() {
2818
+ let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
2819
+ _parseConfig(cfg);
2820
+ SET_CONFIG = true;
2821
+ };
2822
+ DOMPurify.clearConfig = function() {
2823
+ CONFIG = null;
2824
+ SET_CONFIG = false;
2825
+ };
2826
+ DOMPurify.isValidAttribute = function(tag, attr, value) {
2827
+ if (!CONFIG) {
2828
+ _parseConfig({});
2829
+ }
2830
+ const lcTag = transformCaseFunc(tag);
2831
+ const lcName = transformCaseFunc(attr);
2832
+ return _isValidAttribute(lcTag, lcName, value);
2833
+ };
2834
+ DOMPurify.addHook = function(entryPoint, hookFunction) {
2835
+ if (typeof hookFunction !== "function") {
2836
+ return;
2837
+ }
2838
+ arrayPush(hooks[entryPoint], hookFunction);
2839
+ };
2840
+ DOMPurify.removeHook = function(entryPoint, hookFunction) {
2841
+ if (hookFunction !== void 0) {
2842
+ const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
2843
+ return index === -1 ? void 0 : arraySplice(hooks[entryPoint], index, 1)[0];
2844
+ }
2845
+ return arrayPop(hooks[entryPoint]);
2846
+ };
2847
+ DOMPurify.removeHooks = function(entryPoint) {
2848
+ hooks[entryPoint] = [];
2849
+ };
2850
+ DOMPurify.removeAllHooks = function() {
2851
+ hooks = _createHooksMap();
2852
+ };
2853
+ return DOMPurify;
2854
+ }
2855
+ var purify = createDOMPurify();
2856
+
2857
+ // src/response-handlers.ts
2858
+ var domReady;
2859
+ var ensureDom = async () => {
2860
+ if (typeof document !== "undefined" && typeof DOMParser !== "undefined" && typeof DocumentFragment !== "undefined") {
2861
+ return Promise.resolve();
2862
+ }
2863
+ return domReady ??= import("jsdom").then(({ JSDOM }) => {
2864
+ const { window: window2 } = new JSDOM("<!DOCTYPE html><html><head></head><body></body></html>");
2865
+ globalThis.window = window2;
2866
+ Object.assign(globalThis, { document: window2.document, DOMParser: window2.DOMParser, DocumentFragment: window2.DocumentFragment });
2867
+ });
2868
+ };
2869
+ var handleText = async (response) => await response.text();
2870
+ var handleScript = async (response) => {
2871
+ await ensureDom();
2872
+ const objectURL = URL.createObjectURL(await response.blob());
2873
+ return new Promise((resolve, reject) => {
2874
+ const script = document.createElement("script");
2875
+ Object.assign(script, { src: objectURL, type: HttpMediaType.JAVA_SCRIPT, async: true });
2876
+ script.onload = () => {
2877
+ URL.revokeObjectURL(objectURL);
2878
+ document.head.removeChild(script);
2879
+ resolve();
2880
+ };
2881
+ script.onerror = () => {
2882
+ URL.revokeObjectURL(objectURL);
2883
+ document.head.removeChild(script);
2884
+ reject(new Error("Script failed to load"));
2885
+ };
2886
+ document.head.appendChild(script);
2887
+ });
2888
+ };
2889
+ var handleCss = async (response) => {
2890
+ await ensureDom();
2891
+ const objectURL = URL.createObjectURL(await response.blob());
2892
+ return new Promise((resolve, reject) => {
2893
+ const link = document.createElement("link");
2894
+ Object.assign(link, { href: objectURL, type: HttpMediaType.CSS, rel: "stylesheet" });
2895
+ link.onload = () => resolve(URL.revokeObjectURL(objectURL));
2896
+ link.onerror = () => {
2897
+ URL.revokeObjectURL(objectURL);
2898
+ document.head.removeChild(link);
2899
+ reject(new Error("Stylesheet load failed"));
2900
+ };
2901
+ document.head.appendChild(link);
2902
+ });
2903
+ };
2904
+ var handleJson = async (response) => await response.json();
2905
+ var handleBlob = async (response) => await response.blob();
2906
+ var handleImage = async (response) => {
2907
+ await ensureDom();
2908
+ const objectURL = URL.createObjectURL(await response.blob());
2909
+ return new Promise((resolve, reject) => {
2910
+ const img = new Image();
2911
+ img.onload = () => {
2912
+ URL.revokeObjectURL(objectURL);
2913
+ resolve(img);
2914
+ };
2915
+ img.onerror = () => {
2916
+ URL.revokeObjectURL(objectURL);
2917
+ reject(new Error("Image failed to load"));
2918
+ };
2919
+ img.src = objectURL;
2920
+ });
2921
+ };
2922
+ var handleBuffer = async (response) => await response.arrayBuffer();
2923
+ var handleReadableStream = async (response) => Promise.resolve(response.body);
2924
+ var handleXml = async (response) => {
2925
+ await ensureDom();
2926
+ return new DOMParser().parseFromString(purify.sanitize(await response.text()), HttpMediaType.XML);
2927
+ };
2928
+ var handleHtml = async (response) => {
2929
+ await ensureDom();
2930
+ return new DOMParser().parseFromString(purify.sanitize(await response.text()), HttpMediaType.HTML);
2931
+ };
2932
+ var handleHtmlFragment = async (response) => {
2933
+ await ensureDom();
2934
+ return document.createRange().createContextualFragment(purify.sanitize(await response.text()));
2935
+ };
2936
+
2937
+ // src/utils.ts
2938
+ var isRequestBodyMethod = (method) => method !== void 0 && requestBodyMethods.includes(method);
2939
+ var isRawBody = (body) => body instanceof FormData || body instanceof Blob || body instanceof ArrayBuffer || body instanceof ReadableStream || body instanceof URLSearchParams || ArrayBuffer.isView(body);
2940
+ var getCookieValue = (name) => {
2941
+ if (typeof document === "undefined" || !document.cookie) {
2942
+ return;
2943
+ }
2944
+ const prefix = `${name}=`;
2945
+ const cookies = document.cookie.split(";");
2946
+ for (let i = 0, length = cookies.length; i < length; i++) {
2947
+ const cookie = cookies[i].trim();
2948
+ if (cookie.startsWith(prefix)) {
2949
+ return decodeURIComponent(cookie.slice(prefix.length));
1939
2950
  }
1940
- return false;
1941
2951
  }
1942
- /**
1943
- * Determines whether the ParameterMap contains anything.
1944
- *
1945
- * @returns {boolean} True if the ParameterMap size is greater than 0, false otherwise.
1946
- */
1947
- isEmpty() {
1948
- return this.size === 0;
2952
+ return void 0;
2953
+ };
2954
+ var serialize = (data) => JSON.stringify(data);
2955
+ var isString = (value) => value !== null && typeof value === "string";
2956
+ var isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
2957
+ var objectMerge = (...objects) => {
2958
+ const length = objects.length;
2959
+ if (length === 0) {
2960
+ return void 0;
1949
2961
  }
1950
- /**
1951
- * Returns an Object that can be serialized to JSON.
1952
- * If a key has only one value, the value will be a single value.
1953
- * If a key has multiple values, the value will be an array of values.
1954
- * If a key has no values, the value will be undefined.
1955
- *
1956
- * @override
1957
- * @returns {Object} The Object to be serialized to JSON.
1958
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON_behavior}
1959
- */
1960
- toJSON() {
1961
- const obj = /* @__PURE__ */ Object.create(null);
1962
- for (const [key, values] of super.entries()) {
1963
- obj[key] = values.length === 1 ? values[0] : values;
2962
+ if (length === 1) {
2963
+ const [obj] = objects;
2964
+ if (!isObject(obj)) {
2965
+ return obj;
1964
2966
  }
1965
- return obj;
2967
+ return deepClone(obj);
1966
2968
  }
1967
- /**
1968
- * Returns an iterator that yields all key-value pairs in the map as arrays in their insertion order.
1969
- *
1970
- * @override
1971
- * @yields {[string, *]} An iterator for the key-value pairs in the map.
1972
- */
1973
- *entries() {
1974
- for (const [key, array] of super.entries()) {
1975
- for (const value of array) {
1976
- yield [key, value];
2969
+ const target = {};
2970
+ for (const source of objects) {
2971
+ if (!isObject(source)) {
2972
+ return void 0;
2973
+ }
2974
+ for (const [property, sourceValue] of Object.entries(source)) {
2975
+ const targetValue = target[property];
2976
+ if (Array.isArray(sourceValue)) {
2977
+ target[property] = [...sourceValue, ...Array.isArray(targetValue) ? targetValue.filter((item) => !sourceValue.includes(item)) : []];
2978
+ } else if (isObject(sourceValue)) {
2979
+ target[property] = isObject(targetValue) ? objectMerge(targetValue, sourceValue) : deepClone(sourceValue);
2980
+ } else {
2981
+ target[property] = sourceValue;
1977
2982
  }
1978
2983
  }
1979
2984
  }
1980
- /**
1981
- * Returns an iterator that yields all key-value pairs in the map as arrays in their insertion order.
1982
- *
1983
- * @override
1984
- * @yields {[string, *]} An iterator for the key-value pairs in the map.
1985
- */
1986
- *[Symbol.iterator]() {
1987
- yield* this.entries();
1988
- }
1989
- /**
1990
- * Returns an iterable of key, value pairs for every entry in the parameters object.
1991
- *
1992
- * @private
1993
- * @static
1994
- * @param {Iterable<[string, *]>|Object} parameters The parameters to set.
1995
- * @returns {Iterable<[string, *]>} An iterable of key, value pairs for every entry in the parameters object.
1996
- */
1997
- static #entries(parameters) {
1998
- return parameters[Symbol.iterator] ? parameters : Object.entries(parameters);
1999
- }
2000
- /**
2001
- * A String value that is used in the creation of the default string description of an object.
2002
- * Called by the built-in method {@link Object.prototype.toString}.
2003
- *
2004
- * @override
2005
- * @returns {string} The default string description of this object.
2006
- */
2007
- get [Symbol.toStringTag]() {
2008
- return "ParameterMap";
2009
- }
2985
+ return target;
2010
2986
  };
2987
+ function deepClone(object) {
2988
+ if (isObject(object)) {
2989
+ const cloned = {};
2990
+ const keys = Object.keys(object);
2991
+ for (let i = 0, length = keys.length, key; i < length; i++) {
2992
+ key = keys[i];
2993
+ cloned[key] = deepClone(object[key]);
2994
+ }
2995
+ return cloned;
2996
+ }
2997
+ return object;
2998
+ }
2011
2999
 
2012
- // src/transportr.js
2013
- var _handleText = async (response) => await response.text();
2014
- var _handleScript = async (response) => {
2015
- const objectURL = URL.createObjectURL(await response.blob());
2016
- document.head.removeChild(document.head.appendChild(Object.assign(document.createElement("script"), { src: objectURL, type: http_media_type_default.JAVA_SCRIPT, async: true })));
2017
- URL.revokeObjectURL(objectURL);
2018
- return Promise.resolve();
2019
- };
2020
- var _handleCss = async (response) => {
2021
- const objectURL = URL.createObjectURL(await response.blob());
2022
- document.head.appendChild(Object.assign(document.createElement("link"), { href: objectURL, type: http_media_type_default.CSS, rel: "stylesheet" }));
2023
- URL.revokeObjectURL(objectURL);
2024
- return Promise.resolve();
2025
- };
2026
- var _handleJson = async (response) => await response.json();
2027
- var _handleBlob = async (response) => await response.blob();
2028
- var _handleImage = async (response) => URL.createObjectURL(await response.blob());
2029
- var _handleBuffer = async (response) => await response.arrayBuffer();
2030
- var _handleReadableStream = async (response) => response.body;
2031
- var _handleXml = async (response) => new DOMParser().parseFromString(await response.text(), http_media_type_default.XML);
2032
- var _handleHtml = async (response) => new DOMParser().parseFromString(await response.text(), http_media_type_default.HTML);
2033
- var _handleHtmlFragment = async (response) => document.createRange().createContextualFragment(await response.text());
3000
+ // src/transportr.ts
2034
3001
  var Transportr = class _Transportr {
2035
- /** @type {URL} */
2036
- #baseUrl;
2037
- /** @type {RequestOptions} */
2038
- #options;
2039
- /** @type {Subscribr} */
2040
- #subscribr;
2041
- /** @type {Subscribr} */
2042
- static #globalSubscribr = new Subscribr();
2043
- /** @type {Set<AbortSignal>} */
2044
- static #activeRequests = /* @__PURE__ */ new Set();
2045
- /** @type {Map<ResponseHandler<ResponseBody>, string>} */
2046
- static #contentTypeHandlers = /* @__PURE__ */ new Map([
2047
- [_handleImage, mediaTypes.get(http_media_type_default.PNG).type],
2048
- [_handleText, mediaTypes.get(http_media_type_default.TEXT).type],
2049
- [_handleJson, mediaTypes.get(http_media_type_default.JSON).subtype],
2050
- [_handleHtml, mediaTypes.get(http_media_type_default.HTML).subtype],
2051
- [_handleScript, mediaTypes.get(http_media_type_default.JAVA_SCRIPT).subtype],
2052
- [_handleCss, mediaTypes.get(http_media_type_default.CSS).subtype],
2053
- [_handleXml, mediaTypes.get(http_media_type_default.XML).subtype],
2054
- [_handleReadableStream, mediaTypes.get(http_media_type_default.BIN).subtype]
2055
- ]);
3002
+ _baseUrl;
3003
+ _options;
3004
+ subscribr;
3005
+ hooks = { beforeRequest: [], afterResponse: [], beforeError: [] };
3006
+ static globalSubscribr = new Subscribr();
3007
+ static globalHooks = { beforeRequest: [], afterResponse: [], beforeError: [] };
3008
+ static signalControllers = /* @__PURE__ */ new Set();
3009
+ /** Map of in-flight deduplicated requests keyed by URL + method */
3010
+ static inflightRequests = /* @__PURE__ */ new Map();
3011
+ /** Cache for parsed MediaType instances to avoid re-parsing the same content-type strings */
3012
+ static mediaTypeCache = new Map(Object.values(mediaTypes).map((mediaType) => [mediaType.toString(), mediaType]));
3013
+ static contentTypeHandlers = [
3014
+ [mediaTypes.TEXT.type, handleText],
3015
+ [mediaTypes.JSON.subtype, handleJson],
3016
+ [mediaTypes.BIN.subtype, handleReadableStream],
3017
+ [mediaTypes.HTML.subtype, handleHtml],
3018
+ [mediaTypes.XML.subtype, handleXml],
3019
+ [mediaTypes.PNG.type, handleImage],
3020
+ [mediaTypes.JAVA_SCRIPT.subtype, handleScript],
3021
+ [mediaTypes.CSS.subtype, handleCss]
3022
+ ];
2056
3023
  /**
2057
3024
  * Create a new Transportr instance with the provided location or origin and context path.
2058
3025
  *
2059
- * @param {URL|string|RequestOptions} [url=location.origin] The URL for {@link fetch} requests.
2060
- * @param {RequestOptions} [options={}] The default {@link RequestOptions} for this instance.
3026
+ * @param url The URL for {@link fetch} requests.
3027
+ * @param options The default {@link RequestOptions} for this instance.
2061
3028
  */
2062
- constructor(url = globalThis.location.origin, options = {}) {
2063
- if (object_type_default(url) == Object) {
2064
- [url, options] = [globalThis.location.origin, url];
3029
+ constructor(url = globalThis.location?.origin ?? "http://localhost", options = {}) {
3030
+ if (isObject(url)) {
3031
+ [url, options] = [globalThis.location?.origin ?? "http://localhost", url];
2065
3032
  }
2066
- this.#baseUrl = _Transportr.#getBaseUrl(url);
2067
- this.#options = _Transportr.#createOptions(options, _Transportr.#defaultRequestOptions);
2068
- this.#subscribr = new Subscribr();
2069
- }
2070
- /**
2071
- * @static
2072
- * @constant {Object<string, HttpRequestMethod>}
2073
- */
2074
- static Method = Object.freeze(http_request_methods_default);
2075
- /**
2076
- * @static
2077
- * @constant {Object<string, HttpMediaType>}
2078
- */
2079
- static MediaType = Object.freeze(http_media_type_default);
2080
- /**
2081
- * @static
2082
- * @see {@link HttpRequestHeader}
2083
- * @constant {Object<string, HttpRequestHeader>}
2084
- */
2085
- static RequestHeader = Object.freeze(http_request_headers_default);
2086
- /**
2087
- * @static
2088
- * @constant {Object<string, HttpResponseHeader>}
2089
- */
2090
- static ResponseHeader = Object.freeze(http_response_headers_default);
2091
- /**
2092
- * @static
2093
- * @constant {Object<string, RequestCache>}
2094
- */
2095
- static CachingPolicy = Object.freeze({
2096
- DEFAULT: "default",
2097
- FORCE_CACHE: "force-cache",
2098
- NO_CACHE: "no-cache",
2099
- NO_STORE: "no-store",
2100
- ONLY_IF_CACHED: "only-if-cached",
2101
- RELOAD: "reload"
2102
- });
2103
- /**
2104
- * @static
2105
- * @constant {Object<string, RequestCredentials>}
2106
- */
2107
- static CredentialsPolicy = Object.freeze({
3033
+ this._baseUrl = _Transportr.getBaseUrl(url);
3034
+ this._options = _Transportr.createOptions(options, _Transportr.defaultRequestOptions);
3035
+ this.subscribr = new Subscribr();
3036
+ }
3037
+ /** HTTP Media Types */
3038
+ static MediaType = HttpMediaType;
3039
+ /** HTTP Request Methods */
3040
+ static RequestMethod = HttpRequestMethod;
3041
+ /** HTTP Request Headers */
3042
+ static RequestHeader = HttpRequestHeader;
3043
+ /** HTTP Response Headers */
3044
+ static ResponseHeader = HttpResponseHeader;
3045
+ /** Request Caching Policy */
3046
+ static CachingPolicy = RequestCachingPolicy;
3047
+ /** Credentials Policy */
3048
+ static CredentialsPolicy = {
2108
3049
  INCLUDE: "include",
2109
3050
  OMIT: "omit",
2110
3051
  SAME_ORIGIN: "same-origin"
2111
- });
2112
- /**
2113
- * @static
2114
- * @constant {Object<string, RequestMode>}
2115
- */
2116
- static RequestMode = Object.freeze({
3052
+ };
3053
+ /** Request Modes */
3054
+ static RequestModes = {
2117
3055
  CORS: "cors",
2118
3056
  NAVIGATE: "navigate",
2119
3057
  NO_CORS: "no-cors",
2120
3058
  SAME_ORIGIN: "same-origin"
2121
- });
2122
- /**
2123
- * @static
2124
- * @constant {Object<string, RequestRedirect>}
2125
- */
2126
- static RedirectPolicy = Object.freeze({
3059
+ };
3060
+ /** Request Priorities */
3061
+ static RequestPriorities = {
3062
+ HIGH: "high",
3063
+ LOW: "low",
3064
+ AUTO: "auto"
3065
+ };
3066
+ /** Redirect Policies */
3067
+ static RedirectPolicies = {
2127
3068
  ERROR: "error",
2128
3069
  FOLLOW: "follow",
2129
3070
  MANUAL: "manual"
2130
- });
2131
- /**
2132
- * @static
2133
- * @constant {Object<string, ReferrerPolicy>}
2134
- */
2135
- static ReferrerPolicy = Object.freeze({
3071
+ };
3072
+ /** Referrer Policies */
3073
+ static ReferrerPolicy = {
2136
3074
  NO_REFERRER: "no-referrer",
2137
3075
  NO_REFERRER_WHEN_DOWNGRADE: "no-referrer-when-downgrade",
2138
3076
  ORIGIN: "origin",
@@ -2141,555 +3079,867 @@ var Transportr = class _Transportr {
2141
3079
  STRICT_ORIGIN: "strict-origin",
2142
3080
  STRICT_ORIGIN_WHEN_CROSS_ORIGIN: "strict-origin-when-cross-origin",
2143
3081
  UNSAFE_URL: "unsafe-url"
2144
- });
2145
- /**
2146
- * @static
2147
- * @constant {Object<string, TransportrEvent>}
2148
- */
2149
- static Events = RequestEvents;
2150
- /**
2151
- * @private
2152
- * @static
2153
- * @type {RequestOptions}
2154
- */
2155
- static #defaultRequestOptions = Object.freeze({
2156
- body: null,
2157
- cache: _Transportr.CachingPolicy.NO_STORE,
3082
+ };
3083
+ /** Request Events */
3084
+ static RequestEvents = RequestEvent;
3085
+ /** Default Request Options */
3086
+ static defaultRequestOptions = {
3087
+ body: void 0,
3088
+ cache: RequestCachingPolicy.NO_STORE,
2158
3089
  credentials: _Transportr.CredentialsPolicy.SAME_ORIGIN,
2159
- headers: { [http_request_headers_default.CONTENT_TYPE]: `${mediaTypes.get(http_media_type_default.JSON)}`, [http_request_headers_default.ACCEPT]: `${mediaTypes.get(http_media_type_default.JSON)}` },
2160
- searchParams: {},
3090
+ headers: new Headers({ [HttpRequestHeader.CONTENT_TYPE]: defaultMediaType, [HttpRequestHeader.ACCEPT]: defaultMediaType }),
3091
+ searchParams: void 0,
2161
3092
  integrity: void 0,
2162
3093
  keepalive: void 0,
2163
- method: http_request_methods_default.GET,
2164
- mode: _Transportr.RequestMode.CORS,
2165
- redirect: _Transportr.RedirectPolicy.FOLLOW,
3094
+ method: HttpRequestMethod.GET,
3095
+ mode: _Transportr.RequestModes.CORS,
3096
+ priority: _Transportr.RequestPriorities.AUTO,
3097
+ redirect: _Transportr.RedirectPolicies.FOLLOW,
2166
3098
  referrer: "about:client",
2167
3099
  referrerPolicy: _Transportr.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
2168
3100
  signal: void 0,
2169
3101
  timeout: 3e4,
2170
- global: true,
2171
- window: null
2172
- });
3102
+ global: true
3103
+ };
2173
3104
  /**
2174
3105
  * Returns a {@link EventRegistration} used for subscribing to global events.
2175
3106
  *
2176
- * @static
2177
- * @param {TransportrEvent} event The event to subscribe to.
2178
- * @param {function(Event, *): void} handler The event handler.
2179
- * @param {*} context The context to bind the handler to.
2180
- * @returns {EventRegistration} A new {@link EventRegistration} instance.
3107
+ * @param event The event to subscribe to.
3108
+ * @param handler The event handler.
3109
+ * @param context The context to bind the handler to.
3110
+ * @returns A new {@link EventRegistration} instance.
2181
3111
  */
2182
3112
  static register(event, handler, context) {
2183
- return _Transportr.#globalSubscribr.subscribe(event, handler, context);
3113
+ return _Transportr.globalSubscribr.subscribe(event, handler, context);
2184
3114
  }
2185
3115
  /**
2186
3116
  * Removes a {@link EventRegistration} from the global event handler.
2187
3117
  *
2188
- * @static
2189
- * @param {EventRegistration} eventRegistration The {@link EventRegistration} to remove.
2190
- * @returns {boolean} True if the {@link EventRegistration} was removed, false otherwise.
3118
+ * @param eventRegistration The {@link EventRegistration} to remove.
3119
+ * @returns True if the {@link EventRegistration} was removed, false otherwise.
2191
3120
  */
2192
3121
  static unregister(eventRegistration) {
2193
- return _Transportr.#globalSubscribr.unsubscribe(eventRegistration);
3122
+ return _Transportr.globalSubscribr.unsubscribe(eventRegistration);
2194
3123
  }
2195
3124
  /**
2196
3125
  * Aborts all active requests.
2197
3126
  * This is useful for when the user navigates away from the current page.
2198
- * This will also clear the {@link Transportr#activeRequests} set.
2199
- *
2200
- * @static
2201
- * @returns {void}
3127
+ * This will also clear the {@link Transportr#signalControllers} set.
2202
3128
  */
2203
3129
  static abortAll() {
2204
- for (const abortSignal of this.#activeRequests) {
2205
- abortSignal.abort(_abortEvent);
3130
+ for (const signalController of this.signalControllers) {
3131
+ signalController.abort(abortEvent());
3132
+ }
3133
+ this.signalControllers.clear();
3134
+ }
3135
+ /**
3136
+ * Registers a custom content-type response handler.
3137
+ * The handler will be matched against response content-type headers using MediaType matching.
3138
+ * New handlers are prepended so they take priority over built-in handlers.
3139
+ *
3140
+ * @param contentType The content-type string to match (e.g. 'application/pdf', 'text', 'csv').
3141
+ * @param handler The response handler function.
3142
+ */
3143
+ static registerContentTypeHandler(contentType, handler) {
3144
+ _Transportr.contentTypeHandlers.unshift([contentType, handler]);
3145
+ }
3146
+ /**
3147
+ * Removes a previously registered content-type response handler.
3148
+ *
3149
+ * @param contentType The content-type string to remove.
3150
+ * @returns True if the handler was found and removed, false otherwise.
3151
+ */
3152
+ static unregisterContentTypeHandler(contentType) {
3153
+ const index = _Transportr.contentTypeHandlers.findIndex(([type]) => type === contentType);
3154
+ if (index === -1) {
3155
+ return false;
3156
+ }
3157
+ _Transportr.contentTypeHandlers.splice(index, 1);
3158
+ return true;
3159
+ }
3160
+ /**
3161
+ * Registers global lifecycle hooks that run on all requests from all instances.
3162
+ * Global hooks execute before instance and per-request hooks.
3163
+ *
3164
+ * @param hooks The hooks to register globally.
3165
+ */
3166
+ static addHooks(hooks) {
3167
+ if (hooks.beforeRequest) {
3168
+ _Transportr.globalHooks.beforeRequest.push(...hooks.beforeRequest);
2206
3169
  }
2207
- this.#activeRequests.clear();
3170
+ if (hooks.afterResponse) {
3171
+ _Transportr.globalHooks.afterResponse.push(...hooks.afterResponse);
3172
+ }
3173
+ if (hooks.beforeError) {
3174
+ _Transportr.globalHooks.beforeError.push(...hooks.beforeError);
3175
+ }
3176
+ }
3177
+ /**
3178
+ * Removes all global lifecycle hooks.
3179
+ */
3180
+ static clearHooks() {
3181
+ _Transportr.globalHooks = { beforeRequest: [], afterResponse: [], beforeError: [] };
3182
+ }
3183
+ /**
3184
+ * Tears down all global state: aborts in-flight requests, clears global event subscriptions,
3185
+ * hooks, in-flight deduplication map, and media type cache (retaining built-in entries).
3186
+ */
3187
+ static unregisterAll() {
3188
+ _Transportr.abortAll();
3189
+ _Transportr.globalSubscribr = new Subscribr();
3190
+ _Transportr.clearHooks();
3191
+ _Transportr.inflightRequests.clear();
2208
3192
  }
2209
3193
  /**
2210
3194
  * It returns the base {@link URL} for the API.
2211
3195
  *
2212
- * @returns {URL} The baseUrl property.
3196
+ * @returns The baseUrl property.
2213
3197
  */
2214
3198
  get baseUrl() {
2215
- return this.#baseUrl;
3199
+ return this._baseUrl;
2216
3200
  }
2217
3201
  /**
2218
3202
  * Registers an event handler with a {@link Transportr} instance.
2219
3203
  *
2220
- * @param {TransportrEvent} event The name of the event to listen for.
2221
- * @param {function(Event, *): void} handler The function to call when the event is triggered.
2222
- * @param {*} [context] The context to bind to the handler.
2223
- * @returns {EventRegistration} An object that can be used to remove the event handler.
3204
+ * @param event The name of the event to listen for.
3205
+ * @param handler The function to call when the event is triggered.
3206
+ * @param context The context to bind to the handler.
3207
+ * @returns An object that can be used to remove the event handler.
2224
3208
  */
2225
3209
  register(event, handler, context) {
2226
- return this.#subscribr.subscribe(event, handler, context);
3210
+ return this.subscribr.subscribe(event, handler, context);
2227
3211
  }
2228
3212
  /**
2229
3213
  * Unregisters an event handler from a {@link Transportr} instance.
2230
3214
  *
2231
- * @param {EventRegistration} eventRegistration The event registration to remove.
2232
- * @returns {void}
3215
+ * @param eventRegistration The event registration to remove.
3216
+ * @returns True if the {@link EventRegistration} was removed, false otherwise.
2233
3217
  */
2234
3218
  unregister(eventRegistration) {
2235
- this.#subscribr.unsubscribe(eventRegistration);
3219
+ return this.subscribr.unsubscribe(eventRegistration);
3220
+ }
3221
+ /**
3222
+ * Registers instance-level lifecycle hooks that run on all requests from this instance.
3223
+ * Instance hooks execute after global hooks but before per-request hooks.
3224
+ *
3225
+ * @param hooks The hooks to register on this instance.
3226
+ * @returns This instance for method chaining.
3227
+ */
3228
+ addHooks(hooks) {
3229
+ if (hooks.beforeRequest) {
3230
+ this.hooks.beforeRequest.push(...hooks.beforeRequest);
3231
+ }
3232
+ if (hooks.afterResponse) {
3233
+ this.hooks.afterResponse.push(...hooks.afterResponse);
3234
+ }
3235
+ if (hooks.beforeError) {
3236
+ this.hooks.beforeError.push(...hooks.beforeError);
3237
+ }
3238
+ return this;
3239
+ }
3240
+ /**
3241
+ * Removes all instance-level lifecycle hooks.
3242
+ * @returns This instance for method chaining.
3243
+ */
3244
+ clearHooks() {
3245
+ this.hooks.beforeRequest.length = 0;
3246
+ this.hooks.afterResponse.length = 0;
3247
+ this.hooks.beforeError.length = 0;
3248
+ return this;
3249
+ }
3250
+ /**
3251
+ * Tears down this instance: clears all instance subscriptions and hooks.
3252
+ * The instance should not be used after calling this method.
3253
+ */
3254
+ destroy() {
3255
+ this.clearHooks();
3256
+ this.subscribr.destroy();
2236
3257
  }
2237
3258
  /**
2238
3259
  * This function returns a promise that resolves to the result of a request to the specified path with
2239
3260
  * the specified options, where the method is GET.
2240
3261
  *
2241
3262
  * @async
2242
- * @param {string} [path] The path to the resource you want to get.
2243
- * @param {RequestOptions} [options] The options for the request.
2244
- * @returns {Promise<ResponseBody>} A promise that resolves to the response of the request.
3263
+ * @param path The path to the resource you want to get.
3264
+ * @param options The options for the request.
3265
+ * @returns A promise that resolves to the response of the request.
2245
3266
  */
2246
3267
  async get(path, options) {
2247
- return this.#get(path, options);
3268
+ return this._get(path, options);
2248
3269
  }
2249
3270
  /**
2250
3271
  * This function makes a POST request to the given path with the given body and options.
2251
3272
  *
2252
3273
  * @async
2253
- * @param {string} [path] The path to the endpoint you want to call.
2254
- * @param {RequestBody} body The body of the request.
2255
- * @param {RequestOptions} [options] The options for the request.
2256
- * @returns {Promise<ResponseBody>} A promise that resolves to the response body.
2257
- */
2258
- async post(path, body = {}, options = {}) {
2259
- if (object_type_default(path) != String) {
2260
- [path, body, options] = [void 0, path, body];
3274
+ * @template T The expected response type (defaults to ResponseBody)
3275
+ * @param path The path to the endpoint you want to call.
3276
+ * @param options The options for the request.
3277
+ * @returns A promise that resolves to the response body.
3278
+ */
3279
+ async post(path, options) {
3280
+ if (typeof path !== "string") {
3281
+ [path, options] = [void 0, path];
2261
3282
  }
2262
- return this.#request(path, Object.assign(options, { body }), { method: http_request_methods_default.POST });
3283
+ return this.execute(path, options, { method: HttpRequestMethod.POST });
2263
3284
  }
2264
3285
  /**
2265
3286
  * This function returns a promise that resolves to the result of a request to the specified path with
2266
3287
  * the specified options, where the method is PUT.
2267
3288
  *
2268
3289
  * @async
2269
- * @param {string} [path] The path to the endpoint you want to call.
2270
- * @param {RequestOptions} [options] The options for the request.
2271
- * @returns {Promise<ResponseBody>} The return value of the #request method.
3290
+ * @template T The expected response type (defaults to ResponseBody)
3291
+ * @param path The path to the endpoint you want to call.
3292
+ * @param options The options for the request.
3293
+ * @returns The return value of the #request method.
2272
3294
  */
2273
3295
  async put(path, options) {
2274
- return this.#request(path, options, { method: http_request_methods_default.PUT });
3296
+ return this.execute(path, options, { method: HttpRequestMethod.PUT });
2275
3297
  }
2276
3298
  /**
2277
3299
  * It takes a path and options, and returns a request with the method set to PATCH.
2278
3300
  *
2279
3301
  * @async
2280
- * @param {string} [path] The path to the endpoint you want to hit.
2281
- * @param {RequestOptions} [options] The options for the request.
2282
- * @returns {Promise<ResponseBody>} A promise that resolves to the response of the request.
3302
+ * @template T The expected response type (defaults to ResponseBody)
3303
+ * @param path The path to the endpoint you want to hit.
3304
+ * @param options The options for the request.
3305
+ * @returns A promise that resolves to the response of the request.
2283
3306
  */
2284
3307
  async patch(path, options) {
2285
- return this.#request(path, options, { method: http_request_methods_default.PATCH });
3308
+ return this.execute(path, options, { method: HttpRequestMethod.PATCH });
2286
3309
  }
2287
3310
  /**
2288
3311
  * It takes a path and options, and returns a request with the method set to DELETE.
2289
3312
  *
2290
3313
  * @async
2291
- * @param {string} [path] The path to the resource you want to access.
2292
- * @param {RequestOptions} [options] The options for the request.
2293
- * @returns {Promise<ResponseBody>} The result of the request.
3314
+ * @param path The path to the resource you want to access.
3315
+ * @param options The options for the request.
3316
+ * @returns The result of the request.
2294
3317
  */
2295
3318
  async delete(path, options) {
2296
- return this.#request(path, options, { method: http_request_methods_default.DELETE });
3319
+ return this.execute(path, options, { method: HttpRequestMethod.DELETE });
2297
3320
  }
2298
3321
  /**
2299
3322
  * Returns the response headers of a request to the given path.
2300
3323
  *
2301
3324
  * @async
2302
- * @param {string} [path] The path to the resource you want to access.
2303
- * @param {RequestOptions} [options] The options for the request.
2304
- * @returns {Promise<ResponseBody>} A promise that resolves to the response object.
3325
+ * @param path The path to the resource you want to access.
3326
+ * @param options The options for the request.
3327
+ * @returns A promise that resolves to the response object.
2305
3328
  */
2306
3329
  async head(path, options) {
2307
- return this.#request(path, options, { method: http_request_methods_default.HEAD });
3330
+ return this.execute(path, options, { method: HttpRequestMethod.HEAD });
2308
3331
  }
2309
3332
  /**
2310
3333
  * It returns a promise that resolves to the allowed request methods for the given resource path.
2311
3334
  *
2312
3335
  * @async
2313
- * @param {string} [path] The path to the resource.
2314
- * @param {RequestOptions} [options] The options for the request.
2315
- * @returns {Promise<string[]>} A promise that resolves to an array of allowed request methods for this resource.
3336
+ * @param path The path to the resource.
3337
+ * @param options The options for the request.
3338
+ * @returns A promise that resolves to an array of allowed request methods for this resource.
2316
3339
  */
2317
- async options(path, options) {
2318
- const response = await this.#request(path, options, { method: http_request_methods_default.OPTIONS });
2319
- return response.headers.get("allow").split(",").map((method) => method.trim());
3340
+ async options(path, options = {}) {
3341
+ if (isObject(path)) {
3342
+ [path, options] = [void 0, path];
3343
+ }
3344
+ const requestConfig = this.processRequestOptions(options, { method: HttpRequestMethod.OPTIONS });
3345
+ const { requestOptions } = requestConfig;
3346
+ const requestHooks = requestOptions.hooks;
3347
+ let url = _Transportr.createUrl(this._baseUrl, path, requestOptions.searchParams);
3348
+ const beforeRequestHookSets = [_Transportr.globalHooks.beforeRequest, this.hooks.beforeRequest, requestHooks?.beforeRequest];
3349
+ for (const hooks of beforeRequestHookSets) {
3350
+ if (!hooks) {
3351
+ continue;
3352
+ }
3353
+ for (const hook of hooks) {
3354
+ const result = await hook(requestOptions, url);
3355
+ if (result) {
3356
+ Object.assign(requestOptions, result);
3357
+ if (result.searchParams !== void 0) {
3358
+ url = _Transportr.createUrl(this._baseUrl, path, requestOptions.searchParams);
3359
+ }
3360
+ }
3361
+ }
3362
+ }
3363
+ let response = await this._request(path, requestConfig);
3364
+ const afterResponseHookSets = [_Transportr.globalHooks.afterResponse, this.hooks.afterResponse, requestHooks?.afterResponse];
3365
+ for (const hooks of afterResponseHookSets) {
3366
+ if (!hooks) {
3367
+ continue;
3368
+ }
3369
+ for (const hook of hooks) {
3370
+ const result = await hook(response, requestOptions);
3371
+ if (result) {
3372
+ response = result;
3373
+ }
3374
+ }
3375
+ }
3376
+ const allowedMethods = response.headers.get("allow")?.split(",").map((method) => method.trim());
3377
+ this.publish({ name: RequestEvent.SUCCESS, data: allowedMethods, global: options.global });
3378
+ return allowedMethods;
2320
3379
  }
2321
3380
  /**
2322
- * It takes a path and options, and makes a request to the server.
2323
- *
3381
+ * It takes a path and options, and makes a request to the server
2324
3382
  * @async
2325
- * @param {string} [path] The path to the endpoint you want to hit.
2326
- * @param {RequestOptions} [userOptions] The options for the request.
2327
- * @returns {Promise<ResponseBody>} The return value of the function is the return value of the function that is passed to the `then` method of the promise returned by the `fetch` method.
2328
- */
2329
- async request(path, userOptions) {
2330
- return this.#request(path, userOptions, {}, (response) => response);
3383
+ * @param path The path to the endpoint you want to hit.
3384
+ * @param options The options for the request.
3385
+ * @returns The return value of the function is the return value of the function that is passed to the `then` method of the promise returned by the `fetch` method.
3386
+ * @throws {HttpError} If an error occurs during the request.
3387
+ */
3388
+ async request(path, options = {}) {
3389
+ if (isObject(path)) {
3390
+ [path, options] = [void 0, path];
3391
+ }
3392
+ const response = await this._request(path, this.processRequestOptions(options, {}));
3393
+ this.publish({ name: RequestEvent.SUCCESS, data: response, global: options.global });
3394
+ return response;
2331
3395
  }
2332
3396
  /**
2333
- * It gets a JSON resource from the server.
3397
+ * It gets the JSON representation of the resource at the given path.
2334
3398
  *
2335
3399
  * @async
2336
- * @param {string} [path] The path to the resource.
2337
- * @param {RequestOptions} [options] The options object to pass to the request.
2338
- * @returns {Promise<JsonObject>} A promise that resolves to the response body as a JSON object.
3400
+ * @template T The expected JSON response type (defaults to JsonObject)
3401
+ * @param path The path to the resource.
3402
+ * @param options The options object to pass to the request.
3403
+ * @returns A promise that resolves to the response body as a typed JSON value.
2339
3404
  */
2340
3405
  async getJson(path, options) {
2341
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: `${mediaTypes.get(http_media_type_default.JSON)}` } }, _handleJson);
3406
+ return this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: `${mediaTypes.JSON}` } }, handleJson);
2342
3407
  }
2343
3408
  /**
2344
3409
  * It gets the XML representation of the resource at the given path.
2345
3410
  *
2346
3411
  * @async
2347
- * @param {string} [path] The path to the resource you want to get.
2348
- * @param {RequestOptions} [options] The options for the request.
2349
- * @returns {Promise<Document>} The result of the function call to #get.
3412
+ * @param path The path to the resource you want to get.
3413
+ * @param options The options for the request.
3414
+ * @returns The result of the function call to #get.
2350
3415
  */
2351
3416
  async getXml(path, options) {
2352
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: `${mediaTypes.get(http_media_type_default.XML)}` } }, _handleXml);
3417
+ return this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: `${mediaTypes.XML}` } }, handleXml);
2353
3418
  }
2354
3419
  /**
2355
3420
  * Get the HTML content of the specified path.
3421
+ * When a selector is provided, returns only the first matching element from the parsed document.
2356
3422
  *
2357
- * @todo Add way to return portion of the retrieved HTML using a selector. Like jQuery.
2358
3423
  * @async
2359
- * @param {string} [path] The path to the resource.
2360
- * @param {RequestOptions} [options] The options for the request.
2361
- * @returns {Promise<Document>} The return value of the function is the return value of the function passed to the `then`
2362
- * method of the promise returned by the `#get` method.
3424
+ * @param path The path to the resource.
3425
+ * @param options The options for the request.
3426
+ * @param selector An optional CSS selector to extract a specific element from the parsed HTML.
3427
+ * @returns A promise that resolves to a Document, an Element (if selector matched), or void.
2363
3428
  */
2364
- async getHtml(path, options) {
2365
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: `${mediaTypes.get(http_media_type_default.HTML)}` } }, _handleHtml);
3429
+ async getHtml(path, options, selector) {
3430
+ const doc = await this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: `${mediaTypes.HTML}` } }, handleHtml);
3431
+ return selector && doc ? doc.querySelector(selector) : doc;
2366
3432
  }
2367
3433
  /**
2368
3434
  * It returns a promise that resolves to the HTML fragment at the given path.
3435
+ * When a selector is provided, returns only the first matching element from the parsed fragment.
2369
3436
  *
2370
- * @todo Add way to return portion of the retrieved HTML using a selector. Like jQuery.
2371
3437
  * @async
2372
- * @param {string} [path] The path to the resource.
2373
- * @param {RequestOptions} [options] The options for the request.
2374
- * @returns {Promise<DocumentFragment>} A promise that resolves to an HTML fragment.
3438
+ * @param path The path to the resource.
3439
+ * @param options The options for the request.
3440
+ * @param selector An optional CSS selector to extract a specific element from the parsed fragment.
3441
+ * @returns A promise that resolves to a DocumentFragment, an Element (if selector matched), or void.
2375
3442
  */
2376
- async getHtmlFragment(path, options) {
2377
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: `${mediaTypes.get(http_media_type_default.HTML)}` } }, _handleHtmlFragment);
3443
+ async getHtmlFragment(path, options, selector) {
3444
+ const fragment = await this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: `${mediaTypes.HTML}` } }, handleHtmlFragment);
3445
+ return selector && fragment ? fragment.querySelector(selector) : fragment;
2378
3446
  }
2379
3447
  /**
2380
- * It gets a script from the server, and appends the script to the {@link Document} {@link HTMLHeadElement}
2381
- * CORS is enabled by default.
2382
- *
2383
- * @async
2384
- * @param {string} [path] The path to the script.
2385
- * @param {RequestOptions} [options] The options for the request.
2386
- * @returns {Promise<void>} A promise that has been resolved.
3448
+ * It gets a script from the server, and appends the script to the Document HTMLHeadElement
3449
+ * @param path The path to the script.
3450
+ * @param options The options for the request.
3451
+ * @returns A promise that resolves to void.
2387
3452
  */
2388
3453
  async getScript(path, options) {
2389
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: `${mediaTypes.get(http_media_type_default.JAVA_SCRIPT)}` } }, _handleScript);
3454
+ return this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: `${mediaTypes.JAVA_SCRIPT}` } }, handleScript);
2390
3455
  }
2391
3456
  /**
2392
- * Gets a stylesheet from the server, and adds it as a {@link Blob} {@link URL}.
2393
- *
2394
- * @async
2395
- * @param {string} [path] The path to the stylesheet.
2396
- * @param {RequestOptions} [options] The options for the request.
2397
- * @returns {Promise<void>} A promise that has been resolved.
3457
+ * Gets a stylesheet from the server, and adds it as a Blob URL.
3458
+ * @param path The path to the stylesheet.
3459
+ * @param options The options for the request.
3460
+ * @returns A promise that resolves to void.
2398
3461
  */
2399
3462
  async getStylesheet(path, options) {
2400
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: `${mediaTypes.get(http_media_type_default.CSS)}` } }, _handleCss);
3463
+ return this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: `${mediaTypes.CSS}` } }, handleCss);
2401
3464
  }
2402
3465
  /**
2403
3466
  * It returns a blob from the specified path.
2404
- *
2405
- * @async
2406
- * @param {string} [path] The path to the resource.
2407
- * @param {RequestOptions} [options] The options for the request.
2408
- * @returns {Promise<Blob>} A promise that resolves to a blob.
3467
+ * @param path The path to the resource.
3468
+ * @param options The options for the request.
3469
+ * @returns A promise that resolves to a Blob or void.
2409
3470
  */
2410
3471
  async getBlob(path, options) {
2411
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: http_media_type_default.BIN } }, _handleBlob);
3472
+ return this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: HttpMediaType.BIN } }, handleBlob);
2412
3473
  }
2413
3474
  /**
2414
- * It returns a promise that resolves to an object URL.
2415
- *
2416
- * @async
2417
- * @param {string|RequestOptions} [path] The path to the resource.
2418
- * @param {RequestOptions} [options] The options for the request.
2419
- * @returns {Promise<string>} A promise that resolves to an object URL.
3475
+ * It returns a promise that resolves to an `HTMLImageElement`.
3476
+ * The object URL created to load the image is automatically revoked to prevent memory leaks.
3477
+ * Works in both browser and Node.js (via JSDOM) environments.
3478
+ * @param path The path to the image.
3479
+ * @param options The options for the request.
3480
+ * @returns A promise that resolves to an `HTMLImageElement` or `void`.
2420
3481
  */
2421
3482
  async getImage(path, options) {
2422
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: "image/*" } }, _handleImage);
3483
+ return this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: "image/*" } }, handleImage);
2423
3484
  }
2424
3485
  /**
2425
3486
  * It gets a buffer from the specified path
2426
- *
2427
- * @async
2428
- * @param {string} [path] The path to the resource.
2429
- * @param {RequestOptions} [options] The options for the request.
2430
- * @returns {Promise<ArrayBuffer>} A promise that resolves to a buffer.
3487
+ * @param path The path to the resource.
3488
+ * @param options The options for the request.
3489
+ * @returns A promise that resolves to an ArrayBuffer or void.
2431
3490
  */
2432
3491
  async getBuffer(path, options) {
2433
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: http_media_type_default.BIN } }, _handleBuffer);
3492
+ return this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: HttpMediaType.BIN } }, handleBuffer);
2434
3493
  }
2435
3494
  /**
2436
3495
  * It returns a readable stream of the response body from the specified path.
2437
- *
2438
- * @async
2439
- * @param {string} [path] The path to the resource.
2440
- * @param {RequestOptions} [options] The options for the request.
2441
- * @returns {Promise<ReadableStream<Uint8Array>>} A readable stream.
3496
+ * @param path The path to the resource.
3497
+ * @param options The options for the request.
3498
+ * @returns A promise that resolves to a ReadableStream, null, or void.
2442
3499
  */
2443
3500
  async getStream(path, options) {
2444
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: http_media_type_default.BIN } }, _handleReadableStream);
3501
+ return this._get(path, options, { headers: { [HttpRequestHeader.ACCEPT]: HttpMediaType.BIN } }, handleReadableStream);
2445
3502
  }
2446
3503
  /**
2447
- * Makes a GET request to the given path, using the given options, and then calls the
2448
- * given response handler with the response.
2449
- *
2450
- * @private
3504
+ * Handles a GET request.
2451
3505
  * @async
2452
- * @param {string} [path] The path to the endpoint you want to call.
2453
- * @param {RequestOptions} [userOptions] The options passed to the public function to use for the request.
2454
- * @param {RequestOptions} [options={}] The options for the request.
2455
- * @param {ResponseHandler<ResponseBody>} [responseHandler] A function that will be called with the response object.
2456
- * @returns {Promise<ResponseBody>} The result of the #request method.
2457
- */
2458
- async #get(path, userOptions, options = {}, responseHandler) {
2459
- return this.#request(path, userOptions, Object.assign(options, { method: http_request_methods_default.GET }), responseHandler);
3506
+ * @param path The path to the resource.
3507
+ * @param userOptions The user options for the request.
3508
+ * @param options The options for the request.
3509
+ * @param responseHandler The response handler for the request.
3510
+ * @returns A promise that resolves to the response body or void.
3511
+ */
3512
+ async _get(path, userOptions, options = {}, responseHandler) {
3513
+ return this.execute(path, userOptions, { ...options, method: HttpRequestMethod.GET, body: void 0 }, responseHandler);
3514
+ }
3515
+ /**
3516
+ * It processes the request options and returns a new object with the processed options.
3517
+ * @param path The path to the resource.
3518
+ * @param processedRequestOptions The user options for the request.
3519
+ * @returns A new object with the processed options.
3520
+ */
3521
+ async _request(path, { signalController, requestOptions, global }) {
3522
+ _Transportr.signalControllers.add(signalController);
3523
+ const retryConfig = _Transportr.normalizeRetryOptions(requestOptions.retry);
3524
+ const method = requestOptions.method ?? "GET";
3525
+ const canRetry = retryConfig.limit > 0 && retryConfig.methods.includes(method);
3526
+ const canDedupe = requestOptions.dedupe === true && (method === "GET" || method === "HEAD");
3527
+ let attempt = 0;
3528
+ const startTime = performance.now();
3529
+ const getTiming = () => {
3530
+ const end = performance.now();
3531
+ return { start: startTime, end, duration: end - startTime };
3532
+ };
3533
+ try {
3534
+ const url = _Transportr.createUrl(this._baseUrl, path, requestOptions.searchParams);
3535
+ const dedupeKey = canDedupe ? `${method}:${url.href}` : "";
3536
+ if (canDedupe) {
3537
+ const inflight = _Transportr.inflightRequests.get(dedupeKey);
3538
+ if (inflight) {
3539
+ return (await inflight).clone();
3540
+ }
3541
+ }
3542
+ const doFetch = async () => {
3543
+ while (true) {
3544
+ try {
3545
+ const response = await fetch(url, requestOptions);
3546
+ if (!response.ok) {
3547
+ if (canRetry && attempt < retryConfig.limit && retryConfig.statusCodes.includes(response.status)) {
3548
+ attempt++;
3549
+ this.publish({ name: RequestEvent.RETRY, data: { attempt, status: response.status, method, path, timing: getTiming() }, global });
3550
+ await _Transportr.retryDelay(retryConfig, attempt);
3551
+ continue;
3552
+ }
3553
+ let entity;
3554
+ try {
3555
+ entity = await response.text();
3556
+ } catch {
3557
+ }
3558
+ throw await this.handleError(path, response, { entity, url, method, timing: getTiming() }, requestOptions);
3559
+ }
3560
+ return response;
3561
+ } catch (cause) {
3562
+ if (cause instanceof HttpError) {
3563
+ throw cause;
3564
+ }
3565
+ if (canRetry && attempt < retryConfig.limit) {
3566
+ attempt++;
3567
+ this.publish({ name: RequestEvent.RETRY, data: { attempt, error: cause.message, method, path, timing: getTiming() }, global });
3568
+ await _Transportr.retryDelay(retryConfig, attempt);
3569
+ continue;
3570
+ }
3571
+ throw await this.handleError(path, void 0, { cause, url, method, timing: getTiming() }, requestOptions);
3572
+ }
3573
+ }
3574
+ };
3575
+ if (canDedupe) {
3576
+ const promise = doFetch();
3577
+ _Transportr.inflightRequests.set(dedupeKey, promise);
3578
+ try {
3579
+ const response = await promise;
3580
+ return response;
3581
+ } finally {
3582
+ _Transportr.inflightRequests.delete(dedupeKey);
3583
+ }
3584
+ }
3585
+ return await doFetch();
3586
+ } finally {
3587
+ _Transportr.signalControllers.delete(signalController.destroy());
3588
+ if (!requestOptions.signal?.aborted) {
3589
+ const timing = getTiming();
3590
+ this.publish({ name: RequestEvent.COMPLETE, data: { timing }, global });
3591
+ if (_Transportr.signalControllers.size === 0) {
3592
+ this.publish({ name: RequestEvent.ALL_COMPLETE, global });
3593
+ }
3594
+ }
3595
+ }
2460
3596
  }
2461
3597
  /**
2462
- * It takes a path, options, and a response handler, and returns a promise that resolves to the
2463
- * response entity.
2464
- *
2465
- * @private
2466
- * @async
2467
- * @param {string} [path] The path to the resource you want to access.
2468
- * @param {RequestOptions} [userOptions={}] The options passed to the public function to use for the request.
2469
- * @param {RequestOptions} [options={}] The options to use for the request.
2470
- * @param {ResponseHandler<ResponseBody>} [responseHandler] A function that will be called with the response body as a parameter. This
2471
- * is useful if you want to do something with the response body before returning it.
2472
- * @returns {Promise<ResponseBody>} The response from the API call.
2473
- */
2474
- async #request(path, userOptions = {}, options = {}, responseHandler) {
2475
- if (object_type_default(path) == Object) {
3598
+ * Normalizes a retry option into a full RetryOptions object.
3599
+ * @param retry The retry option from request options.
3600
+ * @returns Normalized retry configuration.
3601
+ */
3602
+ static normalizeRetryOptions(retry) {
3603
+ if (retry === void 0) {
3604
+ return { limit: 0, statusCodes: [], methods: [], delay: retryDelay, backoffFactor: retryBackoffFactor };
3605
+ }
3606
+ if (typeof retry === "number") {
3607
+ return { limit: retry, statusCodes: [...retryStatusCodes], methods: [...retryMethods], delay: retryDelay, backoffFactor: retryBackoffFactor };
3608
+ }
3609
+ return {
3610
+ limit: retry.limit ?? 0,
3611
+ statusCodes: retry.statusCodes ?? [...retryStatusCodes],
3612
+ methods: retry.methods ?? [...retryMethods],
3613
+ delay: retry.delay ?? retryDelay,
3614
+ backoffFactor: retry.backoffFactor ?? retryBackoffFactor
3615
+ };
3616
+ }
3617
+ /**
3618
+ * Waits for the appropriate delay before a retry attempt.
3619
+ * @param config The retry configuration.
3620
+ * @param attempt The current attempt number (1-based).
3621
+ * @returns A promise that resolves after the delay.
3622
+ */
3623
+ static retryDelay(config, attempt) {
3624
+ const ms = typeof config.delay === "function" ? config.delay(attempt) : config.delay * config.backoffFactor ** (attempt - 1);
3625
+ return new Promise((resolve) => setTimeout(resolve, ms));
3626
+ }
3627
+ /**
3628
+ * It returns a response handler based on the content type of the response.
3629
+ * @param path The path to the resource.
3630
+ * @param userOptions The user options for the request.
3631
+ * @param options The options for the request.
3632
+ * @param responseHandler The response handler for the request.
3633
+ * @returns A response handler function.
3634
+ */
3635
+ async execute(path, userOptions = {}, options = {}, responseHandler) {
3636
+ if (isObject(path)) {
2476
3637
  [path, userOptions] = [void 0, path];
2477
3638
  }
2478
- options = this.#processRequestOptions(userOptions, options);
2479
- let response;
2480
- const url = _Transportr.#createUrl(this.#baseUrl, path, options.searchParams);
2481
- try {
2482
- _Transportr.#activeRequests.add(options.signal);
2483
- response = await fetch(url, new Proxy(options, abortSignalProxyHandler));
2484
- if (!responseHandler && response.status != 204) {
2485
- responseHandler = this.#getResponseHandler(response.headers.get(http_response_headers_default.CONTENT_TYPE));
3639
+ const requestConfig = this.processRequestOptions(userOptions, options);
3640
+ const { requestOptions } = requestConfig;
3641
+ const requestHooks = requestOptions.hooks;
3642
+ let url = _Transportr.createUrl(this._baseUrl, path, requestOptions.searchParams);
3643
+ const beforeRequestHookSets = [_Transportr.globalHooks.beforeRequest, this.hooks.beforeRequest, requestHooks?.beforeRequest];
3644
+ for (const hooks of beforeRequestHookSets) {
3645
+ if (!hooks) {
3646
+ continue;
2486
3647
  }
2487
- const result = await responseHandler?.(response) ?? response;
2488
- if (!response.ok) {
2489
- return Promise.reject(this.#handleError(url, { status: _Transportr.#generateResponseStatusFromError("ResponseError", response), entity: result }));
3648
+ for (const hook of hooks) {
3649
+ const result = await hook(requestOptions, url);
3650
+ if (result) {
3651
+ Object.assign(requestOptions, result);
3652
+ if (result.searchParams !== void 0) {
3653
+ url = _Transportr.createUrl(this._baseUrl, path, requestOptions.searchParams);
3654
+ }
3655
+ }
2490
3656
  }
2491
- this.#publish({ name: RequestEvents.SUCCESS, data: result, global: options.global });
2492
- return result;
2493
- } catch (cause) {
2494
- return Promise.reject(this.#handleError(url, { cause, status: _Transportr.#generateResponseStatusFromError(cause.name, response) }));
2495
- } finally {
2496
- options.signal.clearTimeout();
2497
- if (!options.signal.aborted) {
2498
- this.#publish({ name: RequestEvents.COMPLETE, data: response, global: options.global });
2499
- _Transportr.#activeRequests.delete(options.signal);
2500
- if (_Transportr.#activeRequests.size == 0) {
2501
- this.#publish({ name: RequestEvents.ALL_COMPLETE, global: options.global });
3657
+ }
3658
+ let response = await this._request(path, requestConfig);
3659
+ const afterResponseHookSets = [_Transportr.globalHooks.afterResponse, this.hooks.afterResponse, requestHooks?.afterResponse];
3660
+ for (const hooks of afterResponseHookSets) {
3661
+ if (!hooks) {
3662
+ continue;
3663
+ }
3664
+ for (const hook of hooks) {
3665
+ const result = await hook(response, requestOptions);
3666
+ if (result) {
3667
+ response = result;
2502
3668
  }
2503
3669
  }
2504
3670
  }
3671
+ try {
3672
+ if (!responseHandler && response.status !== 204) {
3673
+ responseHandler = this.getResponseHandler(response.headers.get(HttpResponseHeader.CONTENT_TYPE));
3674
+ }
3675
+ const data = await responseHandler?.(response);
3676
+ this.publish({ name: RequestEvent.SUCCESS, data, global: requestConfig.global });
3677
+ return data;
3678
+ } catch (cause) {
3679
+ throw await this.handleError(path, response, { cause }, requestOptions);
3680
+ }
2505
3681
  }
2506
3682
  /**
2507
- * Creates the options for a {@link Transportr} instance.
2508
- *
2509
- * @private
2510
- * @static
2511
- * @param {RequestOptions} userOptions The {@link RequestOptions} to convert.
2512
- * @param {RequestOptions} options The default {@link RequestOptions}.
2513
- * @returns {RequestOptions} The converted {@link RequestOptions}.
3683
+ * Creates a new set of options for a request.
3684
+ * @param options The user options for the request.
3685
+ * @param userOptions The default options for the request.
3686
+ * @returns A new set of options for the request.
2514
3687
  */
2515
- static #createOptions({ body, headers: userHeaders, searchParams: userSearchParams, ...userOptions }, { headers, searchParams, ...options }) {
2516
- return object_merge_default(options, userOptions, {
2517
- body: [FormData, URLSearchParams, Object].includes(object_type_default(body)) ? new ParameterMap(body) : body,
2518
- headers: _Transportr.#mergeOptions(new Headers(), userHeaders, headers),
2519
- searchParams: _Transportr.#mergeOptions(new URLSearchParams(), userSearchParams, searchParams)
2520
- });
3688
+ static createOptions({ headers: userHeaders, searchParams: userSearchParams, ...userOptions }, { headers, searchParams, ...options }) {
3689
+ headers = _Transportr.mergeHeaders(new Headers(), userHeaders, headers);
3690
+ searchParams = _Transportr.mergeSearchParams(new URLSearchParams(), userSearchParams, searchParams);
3691
+ return { ...objectMerge(options, userOptions) ?? {}, headers, searchParams };
2521
3692
  }
2522
3693
  /**
2523
- * Merge the user options and request options into the target.
2524
- *
2525
- * @private
2526
- * @static
2527
- * @param {Headers|URLSearchParams|FormData} target The target to merge the options into.
2528
- * @param {Headers|URLSearchParams|FormData|Object} userOption The user options to merge into the target.
2529
- * @param {Headers|URLSearchParams|FormData|Object} requestOption The request options to merge into the target.
2530
- * @returns {Headers|URLSearchParams} The target.
3694
+ * Merges user and request headers into the target Headers object.
3695
+ * @param target The target Headers object.
3696
+ * @param headerSources Variable number of header sources to merge.
3697
+ * @returns The merged Headers object.
2531
3698
  */
2532
- static #mergeOptions(target, userOption = {}, requestOption = {}) {
2533
- for (const option of [userOption, requestOption]) {
2534
- for (const [name, value] of option.entries?.() ?? Object.entries(option)) {
2535
- target.set(name, value);
3699
+ static mergeHeaders(target, ...headerSources) {
3700
+ for (const headers of headerSources) {
3701
+ if (headers === void 0) {
3702
+ continue;
3703
+ }
3704
+ if (headers instanceof Headers) {
3705
+ headers.forEach((value, name) => target.set(name, value));
3706
+ } else if (Array.isArray(headers)) {
3707
+ for (const [name, value] of headers) {
3708
+ target.set(name, value);
3709
+ }
3710
+ } else {
3711
+ const record = headers;
3712
+ const keys = Object.keys(record);
3713
+ for (let i = 0; i < keys.length; i++) {
3714
+ const name = keys[i];
3715
+ const value = record[name];
3716
+ if (value !== void 0) {
3717
+ target.set(name, String(value));
3718
+ }
3719
+ }
2536
3720
  }
2537
3721
  }
2538
3722
  return target;
2539
3723
  }
2540
3724
  /**
2541
- * Merges the user options and request options with the instance options into a new object that is used for the request.
2542
- *
2543
- * @private
2544
- * @param {RequestOptions} userOptions The user options to merge into the request options.
2545
- * @param {RequestOptions} options The request options to merge into the user options.
2546
- * @returns {RequestOptions} The merged options.
3725
+ * Merges user and request search parameters into the target URLSearchParams object.
3726
+ * @param target The target URLSearchParams object.
3727
+ * @param sources The search parameters to merge.
3728
+ * @returns The merged URLSearchParams object.
2547
3729
  */
2548
- #processRequestOptions({ body: userBody, headers: userHeaders, searchParams: userSearchParams, ...userOptions }, { headers, searchParams, ...options }) {
2549
- const requestOptions = object_merge_default(this.#options, userOptions, options, {
2550
- headers: _Transportr.#mergeOptions(new Headers(this.#options.headers), userHeaders, headers),
2551
- searchParams: _Transportr.#mergeOptions(new URLSearchParams(this.#options.searchParams), userSearchParams, searchParams)
2552
- });
2553
- if (requestBodyMethods.includes(requestOptions.method)) {
2554
- if ([ParameterMap, FormData, URLSearchParams, Object].includes(object_type_default(userBody))) {
2555
- const contentType = requestOptions.headers.get(http_request_headers_default.CONTENT_TYPE);
2556
- const mediaType = (mediaTypes.get(contentType) ?? MediaType.parse(contentType))?.subtype;
2557
- if (mediaType == http_media_type_default.MULTIPART_FORM_DATA) {
2558
- requestOptions.body = _Transportr.#mergeOptions(new FormData(requestOptions.body), userBody);
2559
- } else if (mediaType == http_media_type_default.FORM) {
2560
- requestOptions.body = _Transportr.#mergeOptions(new URLSearchParams(requestOptions.body), userBody);
2561
- } else if (mediaType.includes("json")) {
2562
- requestOptions.body = JSON.stringify(_Transportr.#mergeOptions(new ParameterMap(requestOptions.body), userBody));
2563
- } else {
2564
- requestOptions.body = _Transportr.#mergeOptions(new ParameterMap(requestOptions.body), userBody);
3730
+ static mergeSearchParams(target, ...sources) {
3731
+ for (const searchParams of sources) {
3732
+ if (searchParams === void 0) {
3733
+ continue;
3734
+ }
3735
+ if (searchParams instanceof URLSearchParams) {
3736
+ searchParams.forEach((value, name) => target.set(name, value));
3737
+ } else if (isString(searchParams) || Array.isArray(searchParams)) {
3738
+ for (const [name, value] of new URLSearchParams(searchParams)) {
3739
+ target.set(name, value);
2565
3740
  }
2566
3741
  } else {
3742
+ const keys = Object.keys(searchParams);
3743
+ for (let i = 0; i < keys.length; i++) {
3744
+ const name = keys[i];
3745
+ const value = searchParams[name];
3746
+ if (value !== void 0) {
3747
+ target.set(name, String(value));
3748
+ }
3749
+ }
3750
+ }
3751
+ }
3752
+ return target;
3753
+ }
3754
+ /**
3755
+ * Processes request options by merging user, instance, and method-specific options.
3756
+ * This method optimizes performance by using cached instance options and performing
3757
+ * shallow merges where possible instead of deep object cloning.
3758
+ * @param userOptions The user-provided options for the request.
3759
+ * @param options Additional method-specific options.
3760
+ * @returns Processed request options with signal controller and global flag.
3761
+ */
3762
+ processRequestOptions({ body: userBody, headers: userHeaders, searchParams: userSearchParams, ...userOptions }, { headers, searchParams, ...options }) {
3763
+ const requestOptions = {
3764
+ // Spread instance options (already merged with defaults)
3765
+ ...this._options,
3766
+ // Spread user options (shallow merge, sufficient for flat properties)
3767
+ ...userOptions,
3768
+ // Spread method-specific options (e.g., method: 'POST')
3769
+ ...options,
3770
+ // Deep merge required for headers and searchParams
3771
+ headers: _Transportr.mergeHeaders(new Headers(), this._options.headers, userHeaders, headers),
3772
+ searchParams: _Transportr.mergeSearchParams(new URLSearchParams(), this._options.searchParams, userSearchParams, searchParams)
3773
+ };
3774
+ if (isRequestBodyMethod(requestOptions.method)) {
3775
+ if (isRawBody(userBody)) {
2567
3776
  requestOptions.body = userBody;
3777
+ requestOptions.headers.delete(HttpRequestHeader.CONTENT_TYPE);
3778
+ } else {
3779
+ const isJson = requestOptions.headers.get(HttpRequestHeader.CONTENT_TYPE)?.includes("json") ?? false;
3780
+ requestOptions.body = isJson && isObject(userBody) ? serialize(userBody) : userBody;
2568
3781
  }
2569
3782
  } else {
2570
- requestOptions.headers.delete(http_request_headers_default.CONTENT_TYPE);
2571
- if (requestOptions.body) {
2572
- _Transportr.#mergeOptions(requestOptions.searchParams, requestOptions.body);
3783
+ requestOptions.headers.delete(HttpRequestHeader.CONTENT_TYPE);
3784
+ if (requestOptions.body instanceof URLSearchParams) {
3785
+ _Transportr.mergeSearchParams(requestOptions.searchParams, requestOptions.body);
2573
3786
  }
2574
3787
  requestOptions.body = void 0;
2575
3788
  }
2576
- requestOptions.signal = new AbortSignal(requestOptions.signal).onAbort((event) => this.#publish({ name: RequestEvents.ABORTED, event, global: requestOptions.global })).onTimeout((event) => this.#publish({ name: RequestEvents.TIMEOUT, event, global: requestOptions.global }));
2577
- this.#publish({ name: RequestEvents.CONFIGURED, data: requestOptions, global: requestOptions.global });
2578
- return requestOptions;
3789
+ const { signal, timeout, global = false, xsrf } = requestOptions;
3790
+ if (xsrf) {
3791
+ const xsrfConfig = typeof xsrf === "object" ? xsrf : {};
3792
+ const token = getCookieValue(xsrfConfig.cookieName ?? XSRF_COOKIE_NAME);
3793
+ if (token) {
3794
+ requestOptions.headers.set(xsrfConfig.headerName ?? XSRF_HEADER_NAME, token);
3795
+ }
3796
+ }
3797
+ const signalController = new SignalController({ signal, timeout }).onAbort((event) => this.publish({ name: RequestEvent.ABORTED, event, global })).onTimeout((event) => this.publish({ name: RequestEvent.TIMEOUT, event, global }));
3798
+ requestOptions.signal = signalController.signal;
3799
+ this.publish({ name: RequestEvent.CONFIGURED, data: requestOptions, global });
3800
+ return { signalController, requestOptions, global };
2579
3801
  }
2580
3802
  /**
2581
- * It takes a url or a string, and returns a {@link URL} instance.
2582
- * If the url is a string and starts with a slash, then the origin of the current page is used as the base url.
2583
- *
2584
- * @private
2585
- * @static
2586
- * @param {URL|string} url The URL to convert to a {@link URL} instance.
2587
- * @returns {URL} A {@link URL} instance.
2588
- * @throws {TypeError} If the url is not a string or {@link URL} instance.
3803
+ * Gets the base URL from a URL or string.
3804
+ * @param url The URL or string to parse.
3805
+ * @returns The base URL.
2589
3806
  */
2590
- static #getBaseUrl(url) {
2591
- switch (object_type_default(url)) {
2592
- case URL:
2593
- return url;
2594
- case String:
2595
- return new URL(url, url.startsWith("/") ? globalThis.location.origin : void 0);
2596
- default:
2597
- throw new TypeError("Invalid URL");
3807
+ static getBaseUrl(url) {
3808
+ if (url instanceof URL) {
3809
+ return url;
3810
+ }
3811
+ if (!isString(url)) {
3812
+ throw new TypeError("Invalid URL");
2598
3813
  }
3814
+ return new URL(url, url.startsWith("/") ? globalThis.location.origin : void 0);
2599
3815
  }
2600
3816
  /**
2601
- * It takes a URL, a path, and a set of search parameters, and returns a new URL with the path and
2602
- * search parameters applied.
2603
- *
2604
- * @private
2605
- * @static
2606
- * @param {URL} url The URL to use as a base.
2607
- * @param {string} [path] The optional, relative path to the resource. This MUST be a relative path, otherwise, you should create a new {@link Transportr} instance.
2608
- * @param {URLSearchParams} [searchParams] The optional search parameters to append to the URL.
2609
- * @returns {URL} A new URL object with the pathname and origin of the url parameter, and the path parameter appended to the end of the pathname.
3817
+ * Parses a content-type string into a MediaType instance with caching.
3818
+ * This method caches parsed MediaType instances to avoid re-parsing the same content-type strings,
3819
+ * which significantly improves performance for repeated requests with the same content types.
3820
+ * @param contentType The content-type string to parse.
3821
+ * @returns The parsed MediaType instance, or undefined if parsing fails.
3822
+ */
3823
+ static getOrParseMediaType(contentType) {
3824
+ if (contentType === null) {
3825
+ return;
3826
+ }
3827
+ let mediaType = _Transportr.mediaTypeCache.get(contentType);
3828
+ if (mediaType !== void 0) {
3829
+ return mediaType;
3830
+ }
3831
+ mediaType = MediaType.parse(contentType) ?? void 0;
3832
+ if (mediaType !== void 0) {
3833
+ if (_Transportr.mediaTypeCache.size >= 100) {
3834
+ const firstKey = _Transportr.mediaTypeCache.keys().next().value;
3835
+ if (firstKey !== void 0) {
3836
+ _Transportr.mediaTypeCache.delete(firstKey);
3837
+ }
3838
+ }
3839
+ _Transportr.mediaTypeCache.set(contentType, mediaType);
3840
+ }
3841
+ return mediaType;
3842
+ }
3843
+ /**
3844
+ * Creates a new URL with the given path and search parameters.
3845
+ * @param url The base URL.
3846
+ * @param path The path to append to the base URL.
3847
+ * @param searchParams The search parameters to append to the URL.
3848
+ * @returns A new URL with the given path and search parameters.
2610
3849
  */
2611
- static #createUrl(url, path, searchParams) {
3850
+ static createUrl(url, path, searchParams) {
2612
3851
  const requestUrl = path ? new URL(`${url.pathname.replace(endsWithSlashRegEx, "")}${path}`, url.origin) : new URL(url);
2613
- searchParams?.forEach((value, name) => requestUrl.searchParams.append(name, value));
3852
+ if (searchParams) {
3853
+ _Transportr.mergeSearchParams(requestUrl.searchParams, searchParams);
3854
+ }
2614
3855
  return requestUrl;
2615
3856
  }
2616
3857
  /**
2617
- * Generates a ResponseStatus object based on the error name and the response.
2618
- *
2619
- * @private
2620
- * @static
2621
- * @param {string} errorName The name of the error.
2622
- * @param {Response} response The response object returned by the fetch API.
2623
- * @returns {ResponseStatus} The response status object.
3858
+ * It generates a ResponseStatus object from an error name and a Response object.
3859
+ * @param errorName The name of the error.
3860
+ * @param response The Response object.
3861
+ * @returns A ResponseStatus object.
2624
3862
  */
2625
- static #generateResponseStatusFromError(errorName, response) {
3863
+ static generateResponseStatusFromError(errorName, { status, statusText } = new Response()) {
2626
3864
  switch (errorName) {
2627
- case "AbortError":
2628
- return eventResponseStatuses[RequestEvents.ABORTED];
2629
- case "TimeoutError":
2630
- return eventResponseStatuses[RequestEvents.TIMEOUT];
3865
+ case SignalErrors.ABORT:
3866
+ return aborted;
3867
+ case SignalErrors.TIMEOUT:
3868
+ return timedOut;
2631
3869
  default:
2632
- return response ? new ResponseStatus(response.status, response.statusText) : internalServerError;
3870
+ return status >= 400 ? new ResponseStatus(status, statusText) : internalServerError;
2633
3871
  }
2634
3872
  }
2635
3873
  /**
2636
- * Handles an error by logging it and throwing it.
2637
- *
2638
- * @private
2639
- * @param {URL} url The path to the resource you want to access.
2640
- * @param {import('./http-error.js').HttpErrorOptions} options The options for the HttpError.
2641
- * @returns {HttpError} The HttpError.
2642
- */
2643
- #handleError(url, options) {
2644
- const error = new HttpError(`An error has occurred with your request to: '${url}'`, options);
2645
- this.#publish({ name: RequestEvents.ERROR, data: error });
3874
+ * Handles an error that occurs during a request.
3875
+ * @param path The path of the request.
3876
+ * @param response The Response object.
3877
+ * @param options Additional error context including cause, entity, url, method, and timing.
3878
+ * @param requestOptions The original request options that led to the error, used for hooks context.
3879
+ * @returns An HttpError object.
3880
+ */
3881
+ async handleError(path, response, { cause, entity, url, method, timing } = {}, requestOptions) {
3882
+ const message = method && url ? `${method} ${url.href} failed${response ? ` with status ${response.status}` : ""}` : `An error has occurred with your request to: '${path}'`;
3883
+ let error = new HttpError(_Transportr.generateResponseStatusFromError(cause?.name, response), { message, cause, entity, url, method, timing });
3884
+ const beforeErrorHookSets = [_Transportr.globalHooks.beforeError, this.hooks.beforeError, requestOptions?.hooks?.beforeError];
3885
+ for (const hooks of beforeErrorHookSets) {
3886
+ if (!hooks) {
3887
+ continue;
3888
+ }
3889
+ for (const hook of hooks) {
3890
+ const result = await hook(error);
3891
+ if (result instanceof HttpError) {
3892
+ error = result;
3893
+ }
3894
+ }
3895
+ }
3896
+ this.publish({ name: RequestEvent.ERROR, data: error });
2646
3897
  return error;
2647
3898
  }
2648
3899
  /**
2649
- * Publishes an event to the global and instance subscribers.
2650
- *
2651
- * @private
2652
- * @param {Object} options The options for the event.
2653
- * @param {string} options.name The name of the event.
2654
- * @param {Event} [options.event] The event object.
2655
- * @param {*} [options.data] The data to pass to the subscribers.
2656
- * @param {boolean} [options.global=true] Whether or not to publish the event to the global subscribers.
2657
- * @returns {void}
3900
+ * Publishes an event to the global and instance event handlers.
3901
+ * @param eventObject The event object to publish.
2658
3902
  */
2659
- #publish({ name, event = new CustomEvent(name), data, global = true } = {}) {
3903
+ publish({ name, event = new CustomEvent(name), data, global = true }) {
2660
3904
  if (global) {
2661
- _Transportr.#globalSubscribr.publish(name, event, data);
3905
+ _Transportr.globalSubscribr.publish(name, event, data);
2662
3906
  }
2663
- this.#subscribr.publish(name, event, data);
3907
+ this.subscribr.publish(name, event, data);
2664
3908
  }
2665
3909
  /**
2666
- * Returns a response handler for the given content type.
2667
- *
2668
- * @private
2669
- * @param {string} contentType The content type of the response.
2670
- * @returns {ResponseHandler<ResponseBody>} The response handler.
3910
+ * It returns a response handler based on the content type of the response.
3911
+ * @param contentType The content type of the response.
3912
+ * @returns A response handler function.
2671
3913
  */
2672
- #getResponseHandler(contentType) {
2673
- const mediaType = MediaType.parse(contentType);
2674
- if (mediaType) {
2675
- for (const [responseHandler, contentType2] of _Transportr.#contentTypeHandlers) {
2676
- if (mediaType.matches(contentType2)) {
2677
- return responseHandler;
2678
- }
3914
+ getResponseHandler(contentType) {
3915
+ if (!contentType) {
3916
+ return;
3917
+ }
3918
+ const mediaType = _Transportr.getOrParseMediaType(contentType);
3919
+ if (!mediaType) {
3920
+ return;
3921
+ }
3922
+ for (const [contentType2, responseHandler] of _Transportr.contentTypeHandlers) {
3923
+ if (mediaType.matches(contentType2)) {
3924
+ return responseHandler;
2679
3925
  }
2680
3926
  }
2681
3927
  return void 0;
2682
3928
  }
2683
3929
  /**
2684
- * A String value that is used in the creation of the default string
2685
- * description of an object. Called by the built-in method {@link Object.prototype.toString}.
2686
- *
2687
- * @returns {string} The default string description of this object.
3930
+ * A string representation of the Transportr instance.
3931
+ * @returns The string 'Transportr'.
2688
3932
  */
2689
3933
  get [Symbol.toStringTag]() {
2690
3934
  return "Transportr";
2691
3935
  }
2692
3936
  };
2693
3937
  export {
2694
- Transportr as default
3938
+ Transportr
2695
3939
  };
3940
+ /*! Bundled license information:
3941
+
3942
+ dompurify/dist/purify.es.mjs:
3943
+ (*! @license DOMPurify 3.3.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.3/LICENSE *)
3944
+ */
3945
+ //# sourceMappingURL=transportr.js.map