@d1g1tal/transportr 1.2.2 → 1.3.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.
@@ -23,38 +23,6 @@ var Transportr = (() => {
23
23
  default: () => Transportr
24
24
  });
25
25
 
26
- // node_modules/@d1g1tal/chrysalis/src/esm/object-type.js
27
- var _type = (object) => object?.constructor ?? object?.prototype?.constructor ?? globalThis[Object.prototype.toString.call(object).slice(8, -1)] ?? object;
28
- var object_type_default = _type;
29
-
30
- // node_modules/@d1g1tal/chrysalis/src/esm/object-is-empty.js
31
- var _objectIsEmpty = (object) => !!object && Object.keys(object).length === 0 && object.constructor === Object;
32
- var object_is_empty_default = _objectIsEmpty;
33
-
34
- // node_modules/@d1g1tal/chrysalis/src/esm/object-merge.js
35
- var _objectMerge = (...objects) => {
36
- const target = {};
37
- for (const source of objects) {
38
- if (object_type_default(source) != Object)
39
- return void 0;
40
- let descriptor, sourceType;
41
- for (const property of Object.getOwnPropertyNames(source)) {
42
- descriptor = Object.getOwnPropertyDescriptor(source, property);
43
- if (descriptor.enumerable) {
44
- sourceType = object_type_default(source[property]);
45
- if (sourceType == Object) {
46
- descriptor.value = object_type_default(target[property]) == Object ? _objectMerge(target[property], source[property]) : { ...source[property] };
47
- } else if (sourceType == Array) {
48
- descriptor.value = Array.isArray(target[property]) ? [.../* @__PURE__ */ new Set([...source[property], ...target[property]])] : [...source[property]];
49
- }
50
- target[property] = descriptor.value;
51
- }
52
- }
53
- }
54
- return target;
55
- };
56
- var object_merge_default = _objectMerge;
57
-
58
26
  // node_modules/@d1g1tal/collections/src/set-multi-map.js
59
27
  var SetMultiMap = class extends Map {
60
28
  /**
@@ -101,602 +69,176 @@ var Transportr = (() => {
101
69
  };
102
70
  var set_multi_map_default = SetMultiMap;
103
71
 
104
- // node_modules/@d1g1tal/media-type/src/utils.js
105
- var whitespaceCharacters = [" ", " ", "\n", "\r"];
106
- var leadingWhitespace = /^[ \t\n\r]+/u;
107
- var trailingWhitespace = /[ \t\n\r]+$/u;
108
- var httpTokenCodePoints = /^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u;
109
- var httpQuotedTokenCodePoints = /^[\t\u0020-\u007E\u0080-\u00FF]*$/u;
110
- var removeLeadingAndTrailingHTTPWhitespace = (string) => string.replace(leadingWhitespace, "").replace(trailingWhitespace, "");
111
- var removeTrailingHTTPWhitespace = (string) => string.replace(trailingWhitespace, "");
112
- var isHTTPWhitespaceChar = (char) => whitespaceCharacters.includes(char);
113
- var solelyContainsHTTPTokenCodePoints = (string) => httpTokenCodePoints.test(string);
114
- var solelyContainsHTTPQuotedStringTokenCodePoints = (string) => httpQuotedTokenCodePoints.test(string);
115
- var asciiLowercase = (string) => {
116
- let result = "";
117
- for (const [char, charCode = char.charCodeAt(0)] of string) {
118
- result += charCode >= 65 && charCode <= 90 ? String.fromCharCode(charCode + 32) : char;
72
+ // node_modules/@d1g1tal/subscribr/node_modules/@d1g1tal/collections/src/set-multi-map.js
73
+ var SetMultiMap2 = class extends Map {
74
+ /**
75
+ * Adds a new element with a specified key and value to the SetMultiMap. If an element with the same key already exists, the value will be added to the underlying {@link Set}.
76
+ *
77
+ * @param {*} key The key to set.
78
+ * @param {*} value The value to add to the SetMultiMap
79
+ * @returns {SetMultiMap} The SetMultiMap with the updated key and value.
80
+ */
81
+ set(key, value) {
82
+ super.set(key, (super.get(key) ?? /* @__PURE__ */ new Set()).add(value));
83
+ return this;
119
84
  }
120
- return result;
121
- };
122
- var collectAnHTTPQuotedString = (input, position) => {
123
- let value = "";
124
- for (let length = input.length, char; ++position < length; ) {
125
- char = input[position];
126
- if (char == "\\") {
127
- value += ++position < length ? input[position] : char;
128
- } else if (char == '"') {
129
- break;
130
- } else {
131
- value += char;
132
- }
85
+ [Symbol.toStringTag]() {
86
+ return "SetMultiMap";
133
87
  }
134
- return [value, position];
135
88
  };
136
89
 
137
- // node_modules/@d1g1tal/media-type/src/media-type-parameters.js
138
- var MediaTypeParameters = class {
139
- /** @type {Map<string, string>} */
140
- #map;
90
+ // node_modules/@d1g1tal/subscribr/src/context-event-handler.js
91
+ var ContextEventHandler = class {
92
+ #context;
93
+ #eventHandler;
141
94
  /**
142
- * Create a new MediaTypeParameters instance.
143
- *
144
- * @param {Array<Array<string>>} entries An array of [name, value] tuples.
95
+ * @param {*} context The context to bind to the event handler.
96
+ * @param {function(*): void} eventHandler The event handler to call when the event is published.
145
97
  */
146
- constructor(entries) {
147
- this.#map = new Map(entries);
98
+ constructor(context, eventHandler) {
99
+ this.#context = context;
100
+ this.#eventHandler = eventHandler;
148
101
  }
149
102
  /**
150
- * Gets the number of media type parameters.
103
+ * Call the event handler for the provided event.
151
104
  *
152
- * @returns {number} The number of media type parameters
105
+ * @param {Event} event The event to handle
106
+ * @param {*} [data] The value to be passed to the event handler as a parameter.
153
107
  */
154
- get size() {
155
- return this.#map.size;
108
+ handle(event, data) {
109
+ this.#eventHandler.call(this.#context, event, data);
110
+ }
111
+ get [Symbol.toStringTag]() {
112
+ return "ContextEventHandler";
156
113
  }
114
+ };
115
+
116
+ // node_modules/@d1g1tal/subscribr/src/subscription.js
117
+ var Subscription = class {
118
+ #eventName;
119
+ #contextEventHandler;
157
120
  /**
158
- * Gets the media type parameter value for the supplied name.
159
- *
160
- * @param {string} name The name of the media type parameter to retrieve.
161
- * @returns {string} The media type parameter value.
121
+ * @param {string} eventName The event name.
122
+ * @param {ContextEventHandler} contextEventHandler Then context event handler.
162
123
  */
163
- get(name) {
164
- return this.#map.get(asciiLowercase(String(name)));
124
+ constructor(eventName, contextEventHandler) {
125
+ this.#eventName = eventName;
126
+ this.#contextEventHandler = contextEventHandler;
165
127
  }
166
128
  /**
167
- * Indicates whether the media type parameter with the specified name exists or not.
129
+ * Gets the event name for the subscription.
168
130
  *
169
- * @param {string} name The name of the media type parameter to check.
170
- * @returns {boolean} true if the media type parameter exists, false otherwise.
131
+ * @returns {string} The event name.
171
132
  */
172
- has(name) {
173
- return this.#map.has(asciiLowercase(String(name)));
133
+ get eventName() {
134
+ return this.#eventName;
174
135
  }
175
136
  /**
176
- * Adds a new media type parameter using the specified name and value to the MediaTypeParameters.
177
- * If an parameter with the same name already exists, the parameter will be updated.
137
+ * Gets the context event handler.
178
138
  *
179
- * @param {string} name The name of the media type parameter to set.
180
- * @param {string} value The media type parameter value.
181
- * @returns {MediaTypeParameters} This instance.
139
+ * @returns {ContextEventHandler} The context event handler
182
140
  */
183
- set(name, value) {
184
- name = asciiLowercase(String(name));
185
- value = String(value);
186
- if (!solelyContainsHTTPTokenCodePoints(name)) {
187
- throw new Error(`Invalid media type parameter name "${name}": only HTTP token code points are valid.`);
188
- }
189
- if (!solelyContainsHTTPQuotedStringTokenCodePoints(value)) {
190
- throw new Error(`Invalid media type parameter value "${value}": only HTTP quoted-string token code points are valid.`);
191
- }
192
- this.#map.set(name, value);
193
- return this;
141
+ get contextEventHandler() {
142
+ return this.#contextEventHandler;
194
143
  }
195
- /**
196
- * Clears all the media type parameters.
197
- */
198
- clear() {
199
- this.#map.clear();
144
+ get [Symbol.toStringTag]() {
145
+ return "Subscription";
146
+ }
147
+ };
148
+
149
+ // node_modules/@d1g1tal/subscribr/src/subscribr.js
150
+ var Subscribr = class {
151
+ /** @type {SetMultiMap<string, ContextEventHandler>} */
152
+ #subscribers;
153
+ constructor() {
154
+ this.#subscribers = new SetMultiMap2();
200
155
  }
201
156
  /**
202
- * Removes the media type parameter using the specified name.
157
+ * Subscribe to an event
203
158
  *
204
- * @param {string} name The name of the media type parameter to delete.
205
- * @returns {boolean} true if the parameter existed and has been removed, or false if the parameter does not exist.
159
+ * @param {string} eventName The event name to subscribe to.
160
+ * @param {function(Event, *): void} eventHandler The event handler to call when the event is published.
161
+ * @param {*} [context] The context to bind to the event handler.
162
+ * @returns {Subscription} An object used to check if the subscription still exists and to unsubscribe from the event.
206
163
  */
207
- delete(name) {
208
- name = asciiLowercase(String(name));
209
- return this.#map.delete(name);
164
+ subscribe(eventName, eventHandler, context = eventHandler) {
165
+ const contextEventHandler = new ContextEventHandler(context, eventHandler);
166
+ this.#subscribers.set(eventName, contextEventHandler);
167
+ return new Subscription(eventName, contextEventHandler);
210
168
  }
211
169
  /**
212
- * Executes a provided function once per each name/value pair in the MediaTypeParameters, in insertion order.
170
+ * Unsubscribe from the event
213
171
  *
214
- * @param {function(string, string): void} callback The function called on each iteration.
215
- * @param {*} [thisArg] Optional object when binding 'this' to the callback.
172
+ * @param {Subscription} subscription The subscription to unsubscribe.
173
+ * @param {string} subscription.eventName The event name to subscribe to.
174
+ * @param {ContextEventHandler} subscription.contextEventHandler The event handler to call when the event is published.
175
+ * @returns {boolean} true if eventListener has been removed successfully. false if the value is not found or if the value is not an object.
216
176
  */
217
- forEach(callback, thisArg) {
218
- this.#map.forEach(callback, thisArg);
177
+ unsubscribe({ eventName, contextEventHandler }) {
178
+ const contextEventHandlers = this.#subscribers.get(eventName);
179
+ const removed = contextEventHandlers?.delete(contextEventHandler);
180
+ if (removed && contextEventHandlers.size == 0) {
181
+ this.#subscribers.delete(eventName);
182
+ }
183
+ return removed;
219
184
  }
220
185
  /**
221
- * Returns an iterable of parameter names.
186
+ * Publish an event
222
187
  *
223
- * @returns {IterableIterator<string>} The {@link IterableIterator} of media type parameter names.
188
+ * @param {string} eventName The name of the event.
189
+ * @param {Event} event The event to be handled.
190
+ * @param {*} [data] The value to be passed to the event handler as a parameter.
224
191
  */
225
- keys() {
226
- return this.#map.keys();
192
+ publish(eventName, event = new CustomEvent(eventName), data) {
193
+ if (data == null && !(event instanceof Event)) {
194
+ [data, event] = [event, new CustomEvent(eventName)];
195
+ }
196
+ this.#subscribers.get(eventName)?.forEach((contextEventHandler) => contextEventHandler.handle(event, data));
227
197
  }
228
198
  /**
229
- * Returns an iterable of parameter values.
199
+ * Check if the event and handler are subscribed.
230
200
  *
231
- * @returns {IterableIterator<string>} The {@link IterableIterator} of media type parameter values.
201
+ * @param {Subscription} subscription The subscription object.
202
+ * @param {string} subscription.eventName The name of the event to check.
203
+ * @param {ContextEventHandler} subscription.contextEventHandler The event handler to check.
204
+ * @returns {boolean} true if the event name and handler are subscribed, false otherwise.
232
205
  */
233
- values() {
234
- return this.#map.values();
206
+ isSubscribed({ eventName, contextEventHandler }) {
207
+ return this.#subscribers.get(eventName)?.has(contextEventHandler);
235
208
  }
209
+ get [Symbol.toStringTag]() {
210
+ return "Subscribr";
211
+ }
212
+ };
213
+
214
+ // src/http-error.js
215
+ var HttpError = class extends Error {
216
+ /** @type {ResponseBody} */
217
+ #entity;
218
+ /** @type {ResponseStatus} */
219
+ #responseStatus;
236
220
  /**
237
- * Returns an iterable of name, value pairs for every parameter entry in the media type parameters.
238
- *
239
- * @returns {IterableIterator<Array<Array<string>>>} The media type parameter entries.
221
+ * @param {string} [message] The error message.
222
+ * @param {HttpErrorOptions} [httpErrorOptions] The http error options.
223
+ * @param {any} [httpErrorOptions.cause] The cause of the error.
224
+ * @param {ResponseStatus} [httpErrorOptions.status] The response status.
225
+ * @param {ResponseBody} [httpErrorOptions.entity] The error entity from the server, if any.
240
226
  */
241
- entries() {
242
- return this.#map.entries();
227
+ constructor(message, { cause, status, entity }) {
228
+ super(message, { cause });
229
+ this.#entity = entity;
230
+ this.#responseStatus = status;
243
231
  }
244
232
  /**
245
- * A method that returns the default iterator for the {@link MediaTypeParameters}. Called by the semantics of the for-of statement.
233
+ * It returns the value of the private variable #entity.
246
234
  *
247
- * @returns {Iterator<string, string, undefined>} The {@link Symbol.iterator} for the media type parameters.
235
+ * @returns {ResponseBody} The entity property of the class.
248
236
  */
249
- [Symbol.iterator]() {
250
- return this.#map[Symbol.iterator]();
237
+ get entity() {
238
+ return this.#entity;
251
239
  }
252
240
  /**
253
- * Returns a string representation of the media type parameters.
254
- * This method is called by the `String()` function.
255
- *
256
- * @example
257
- * const parameters = new MediaTypeParameters(new Map([['charset', 'utf-8']]));
258
- * String(parameters); // 'charset=utf-8'
259
- * parameters.toString(); // 'charset=utf-8'
260
- * parameters + ''; // 'charset=utf-8'
261
- * `${parameters}`; // 'charset=utf-8'
262
- * parameters[Symbol.toStringTag]; // 'MediaTypeParameters'
263
- * parameters[Symbol.toStringTag](); // 'MediaTypeParameters'
264
- * Object.prototype.toString.call(parameters); // '[object MediaTypeParameters]'
265
- * parameters + ''; // 'charset=utf-8'
266
- * @returns {string} The string representation of the media type parameters.
267
- */
268
- [Symbol.toStringTag]() {
269
- return "MediaTypeParameters";
270
- }
271
- };
272
-
273
- // node_modules/@d1g1tal/media-type/src/parser.js
274
- var parse = (input) => {
275
- input = removeLeadingAndTrailingHTTPWhitespace(input);
276
- let position = 0;
277
- let type = "";
278
- while (position < input.length && input[position] != "/") {
279
- type += input[position];
280
- ++position;
281
- }
282
- if (type.length === 0 || !solelyContainsHTTPTokenCodePoints(type)) {
283
- return null;
284
- }
285
- if (position >= input.length) {
286
- return null;
287
- }
288
- ++position;
289
- let subtype = "";
290
- while (position < input.length && input[position] != ";") {
291
- subtype += input[position];
292
- ++position;
293
- }
294
- subtype = removeTrailingHTTPWhitespace(subtype);
295
- if (subtype.length === 0 || !solelyContainsHTTPTokenCodePoints(subtype)) {
296
- return null;
297
- }
298
- const mediaType = {
299
- type: asciiLowercase(type),
300
- subtype: asciiLowercase(subtype),
301
- parameters: /* @__PURE__ */ new Map()
302
- };
303
- while (position < input.length) {
304
- ++position;
305
- while (isHTTPWhitespaceChar(input[position])) {
306
- ++position;
307
- }
308
- let parameterName = "";
309
- while (position < input.length && input[position] != ";" && input[position] != "=") {
310
- parameterName += input[position];
311
- ++position;
312
- }
313
- parameterName = asciiLowercase(parameterName);
314
- if (position < input.length) {
315
- if (input[position] == ";") {
316
- continue;
317
- }
318
- ++position;
319
- }
320
- let parameterValue = null;
321
- if (input[position] == '"') {
322
- [parameterValue, position] = collectAnHTTPQuotedString(input, position);
323
- while (position < input.length && input[position] != ";") {
324
- ++position;
325
- }
326
- } else {
327
- parameterValue = "";
328
- while (position < input.length && input[position] != ";") {
329
- parameterValue += input[position];
330
- ++position;
331
- }
332
- parameterValue = removeTrailingHTTPWhitespace(parameterValue);
333
- if (parameterValue === "") {
334
- continue;
335
- }
336
- }
337
- if (parameterName.length > 0 && solelyContainsHTTPTokenCodePoints(parameterName) && solelyContainsHTTPQuotedStringTokenCodePoints(parameterValue) && !mediaType.parameters.has(parameterName)) {
338
- mediaType.parameters.set(parameterName, parameterValue);
339
- }
340
- }
341
- return mediaType;
342
- };
343
- var parser_default = parse;
344
-
345
- // node_modules/@d1g1tal/media-type/src/serializer.js
346
- var serialize = (mediaType) => {
347
- let serialization = `${mediaType.type}/${mediaType.subtype}`;
348
- if (mediaType.parameters.size === 0) {
349
- return serialization;
350
- }
351
- for (let [name, value] of mediaType.parameters) {
352
- serialization += `;${name}=`;
353
- if (!solelyContainsHTTPTokenCodePoints(value) || value.length === 0) {
354
- value = `"${value.replace(/(["\\])/ug, "\\$1")}"`;
355
- }
356
- serialization += value;
357
- }
358
- return serialization;
359
- };
360
- var serializer_default = serialize;
361
-
362
- // node_modules/@d1g1tal/media-type/src/media-type.js
363
- var MediaType = class _MediaType {
364
- /** @type {string} */
365
- #type;
366
- /** @type {string} */
367
- #subtype;
368
- /** @type {MediaTypeParameters} */
369
- #parameters;
370
- /**
371
- * Create a new MediaType instance from a string representation.
372
- *
373
- * @param {string} mediaType The media type to parse.
374
- * @param {Object} [parameters] Optional parameters.
375
- */
376
- constructor(mediaType, parameters = {}) {
377
- const { type, subtype, parameters: parsedParameters } = parser_default(mediaType);
378
- this.#type = type;
379
- this.#subtype = subtype;
380
- this.#parameters = new MediaTypeParameters([...parsedParameters, ...Object.entries(parameters).map(([name, value]) => [asciiLowercase(name), asciiLowercase(value)])]);
381
- }
382
- /**
383
- * Static factory method for parsing a media type.
384
- *
385
- * @param {string} string The media type to parse.
386
- * @returns {MediaType} The parsed {@link MediaType} object or null if the string could not be parsed.
387
- */
388
- static parse(string) {
389
- try {
390
- return new _MediaType(string);
391
- } catch (e) {
392
- throw new Error(`Could not parse media type string '${string}'`);
393
- }
394
- }
395
- /**
396
- * Gets the media type essence (type/subtype).
397
- *
398
- * @returns {string} The media type without any parameters
399
- */
400
- get essence() {
401
- return `${this.#type}/${this.#subtype}`;
402
- }
403
- /**
404
- * Gets the type.
405
- *
406
- * @returns {string} The type.
407
- */
408
- get type() {
409
- return this.#type;
410
- }
411
- /**
412
- * Sets the type.
413
- */
414
- set type(value) {
415
- value = asciiLowercase(String(value));
416
- if (value.length === 0) {
417
- throw new Error("Invalid type: must be a non-empty string");
418
- }
419
- if (!solelyContainsHTTPTokenCodePoints(value)) {
420
- throw new Error(`Invalid type ${value}: must contain only HTTP token code points`);
421
- }
422
- this.#type = value;
423
- }
424
- /**
425
- * Gets the subtype.
426
- *
427
- * @returns {string} The subtype.
428
- */
429
- get subtype() {
430
- return this.#subtype;
431
- }
432
- /**
433
- * Sets the subtype.
434
- */
435
- set subtype(value) {
436
- value = asciiLowercase(String(value));
437
- if (value.length === 0) {
438
- throw new Error("Invalid subtype: must be a non-empty string");
439
- }
440
- if (!solelyContainsHTTPTokenCodePoints(value)) {
441
- throw new Error(`Invalid subtype ${value}: must contain only HTTP token code points`);
442
- }
443
- this.#subtype = value;
444
- }
445
- /**
446
- * Gets the parameters.
447
- *
448
- * @returns {MediaTypeParameters} The media type parameters.
449
- */
450
- get parameters() {
451
- return this.#parameters;
452
- }
453
- /**
454
- * Gets the serialized version of the media type.
455
- *
456
- * @returns {string} The serialized media type.
457
- */
458
- toString() {
459
- return serializer_default(this);
460
- }
461
- /**
462
- * Determines if this instance is a JavaScript media type.
463
- *
464
- * @param {Object} [options] Optional options.
465
- * @param {boolean} [options.prohibitParameters=false] The option to prohibit parameters when checking if the media type is JavaScript.
466
- * @returns {boolean} true if this instance represents a JavaScript media type, false otherwise.
467
- */
468
- isJavaScript({ prohibitParameters = false } = {}) {
469
- switch (this.#type) {
470
- case "text": {
471
- switch (this.#subtype) {
472
- case "ecmascript":
473
- case "javascript":
474
- case "javascript1.0":
475
- case "javascript1.1":
476
- case "javascript1.2":
477
- case "javascript1.3":
478
- case "javascript1.4":
479
- case "javascript1.5":
480
- case "jscript":
481
- case "livescript":
482
- case "x-ecmascript":
483
- case "x-javascript":
484
- return !prohibitParameters || this.#parameters.size === 0;
485
- default:
486
- return false;
487
- }
488
- }
489
- case "application": {
490
- switch (this.#subtype) {
491
- case "ecmascript":
492
- case "javascript":
493
- case "x-ecmascript":
494
- case "x-javascript":
495
- return !prohibitParameters || this.#parameters.size === 0;
496
- default:
497
- return false;
498
- }
499
- }
500
- default:
501
- return false;
502
- }
503
- }
504
- /**
505
- * Determines if this instance is an XML media type.
506
- *
507
- * @returns {boolean} true if this instance represents an XML media type, false otherwise.
508
- */
509
- isXML() {
510
- return this.#subtype === "xml" && (this.#type === "text" || this.#type === "application") || this.#subtype.endsWith("+xml");
511
- }
512
- /**
513
- * Determines if this instance is an HTML media type.
514
- *
515
- * @returns {boolean} true if this instance represents an HTML media type, false otherwise.
516
- */
517
- isHTML() {
518
- return this.#subtype === "html" && this.#type === "text";
519
- }
520
- /**
521
- * Gets the name of the class.
522
- *
523
- * @returns {string} The class name
524
- */
525
- get [Symbol.toStringTag]() {
526
- return "MediaType";
527
- }
528
- };
529
-
530
- // node_modules/@d1g1tal/subscribr/node_modules/@d1g1tal/collections/src/set-multi-map.js
531
- var SetMultiMap2 = class extends Map {
532
- /**
533
- * Adds a new element with a specified key and value to the SetMultiMap. If an element with the same key already exists, the value will be added to the underlying {@link Set}.
534
- *
535
- * @param {*} key The key to set.
536
- * @param {*} value The value to add to the SetMultiMap
537
- * @returns {SetMultiMap} The SetMultiMap with the updated key and value.
538
- */
539
- set(key, value) {
540
- super.set(key, (super.get(key) ?? /* @__PURE__ */ new Set()).add(value));
541
- return this;
542
- }
543
- [Symbol.toStringTag]() {
544
- return "SetMultiMap";
545
- }
546
- };
547
-
548
- // node_modules/@d1g1tal/subscribr/src/context-event-handler.js
549
- var ContextEventHandler = class {
550
- #context;
551
- #eventHandler;
552
- /**
553
- * @param {*} context The context to bind to the event handler.
554
- * @param {function(*): void} eventHandler The event handler to call when the event is published.
555
- */
556
- constructor(context, eventHandler) {
557
- this.#context = context;
558
- this.#eventHandler = eventHandler;
559
- }
560
- /**
561
- * Call the event handler for the provided event.
562
- *
563
- * @param {Event} event The event to handle
564
- * @param {*} [data] The value to be passed to the event handler as a parameter.
565
- */
566
- handle(event, data) {
567
- this.#eventHandler.call(this.#context, event, data);
568
- }
569
- get [Symbol.toStringTag]() {
570
- return "ContextEventHandler";
571
- }
572
- };
573
-
574
- // node_modules/@d1g1tal/subscribr/src/subscription.js
575
- var Subscription = class {
576
- #eventName;
577
- #contextEventHandler;
578
- /**
579
- * @param {string} eventName The event name.
580
- * @param {ContextEventHandler} contextEventHandler Then context event handler.
581
- */
582
- constructor(eventName, contextEventHandler) {
583
- this.#eventName = eventName;
584
- this.#contextEventHandler = contextEventHandler;
585
- }
586
- /**
587
- * Gets the event name for the subscription.
588
- *
589
- * @returns {string} The event name.
590
- */
591
- get eventName() {
592
- return this.#eventName;
593
- }
594
- /**
595
- * Gets the context event handler.
596
- *
597
- * @returns {ContextEventHandler} The context event handler
598
- */
599
- get contextEventHandler() {
600
- return this.#contextEventHandler;
601
- }
602
- get [Symbol.toStringTag]() {
603
- return "Subscription";
604
- }
605
- };
606
-
607
- // node_modules/@d1g1tal/subscribr/src/subscribr.js
608
- var Subscribr = class {
609
- /** @type {SetMultiMap<string, ContextEventHandler>} */
610
- #subscribers;
611
- constructor() {
612
- this.#subscribers = new SetMultiMap2();
613
- }
614
- /**
615
- * Subscribe to an event
616
- *
617
- * @param {string} eventName The event name to subscribe to.
618
- * @param {function(Event, *): void} eventHandler The event handler to call when the event is published.
619
- * @param {*} [context] The context to bind to the event handler.
620
- * @returns {Subscription} An object used to check if the subscription still exists and to unsubscribe from the event.
621
- */
622
- subscribe(eventName, eventHandler, context = eventHandler) {
623
- const contextEventHandler = new ContextEventHandler(context, eventHandler);
624
- this.#subscribers.set(eventName, contextEventHandler);
625
- return new Subscription(eventName, contextEventHandler);
626
- }
627
- /**
628
- * Unsubscribe from the event
629
- *
630
- * @param {Subscription} subscription The subscription to unsubscribe.
631
- * @param {string} subscription.eventName The event name to subscribe to.
632
- * @param {ContextEventHandler} subscription.contextEventHandler The event handler to call when the event is published.
633
- * @returns {boolean} true if eventListener has been removed successfully. false if the value is not found or if the value is not an object.
634
- */
635
- unsubscribe({ eventName, contextEventHandler }) {
636
- const contextEventHandlers = this.#subscribers.get(eventName);
637
- const removed = contextEventHandlers?.delete(contextEventHandler);
638
- if (removed && contextEventHandlers.size == 0) {
639
- this.#subscribers.delete(eventName);
640
- }
641
- return removed;
642
- }
643
- /**
644
- * Publish an event
645
- *
646
- * @param {string} eventName The name of the event.
647
- * @param {Event} event The event to be handled.
648
- * @param {*} [data] The value to be passed to the event handler as a parameter.
649
- */
650
- publish(eventName, event = new CustomEvent(eventName), data) {
651
- if (data == null && !(event instanceof Event)) {
652
- [data, event] = [event, new CustomEvent(eventName)];
653
- }
654
- this.#subscribers.get(eventName)?.forEach((contextEventHandler) => contextEventHandler.handle(event, data));
655
- }
656
- /**
657
- * Check if the event and handler are subscribed.
658
- *
659
- * @param {Subscription} subscription The subscription object.
660
- * @param {string} subscription.eventName The name of the event to check.
661
- * @param {ContextEventHandler} subscription.contextEventHandler The event handler to check.
662
- * @returns {boolean} true if the event name and handler are subscribed, false otherwise.
663
- */
664
- isSubscribed({ eventName, contextEventHandler }) {
665
- return this.#subscribers.get(eventName)?.has(contextEventHandler);
666
- }
667
- get [Symbol.toStringTag]() {
668
- return "Subscribr";
669
- }
670
- };
671
-
672
- // src/http-error.js
673
- var HttpError = class extends Error {
674
- /** @type {ResponseBody} */
675
- #entity;
676
- /** @type {ResponseStatus} */
677
- #responseStatus;
678
- /**
679
- * @param {string} [message] The error message.
680
- * @param {HttpErrorOptions} [httpErrorOptions] The http error options.
681
- * @param {any} [httpErrorOptions.cause] The cause of the error.
682
- * @param {ResponseStatus} [httpErrorOptions.status] The response status.
683
- * @param {ResponseBody} [httpErrorOptions.entity] The error entity from the server, if any.
684
- */
685
- constructor(message, { cause, status, entity }) {
686
- super(message, { cause });
687
- this.#entity = entity;
688
- this.#responseStatus = status;
689
- }
690
- /**
691
- * It returns the value of the private variable #entity.
692
- *
693
- * @returns {ResponseBody} The entity property of the class.
694
- */
695
- get entity() {
696
- return this.#entity;
697
- }
698
- /**
699
- * It returns the status code of the {@link Response}.
241
+ * It returns the status code of the {@link Response}.
700
242
  *
701
243
  * @returns {number} The status code of the {@link Response}.
702
244
  */
@@ -783,6 +325,8 @@ var Transportr = (() => {
783
325
  JSON: "application/json",
784
326
  /** JavaScript Object Notation LD Format */
785
327
  JSON_LD: "application/ld+json",
328
+ /** JavaScript Object Notation (JSON) Merge Patch */
329
+ JSON_MERGE_PATCH: "application/merge-patch+json",
786
330
  /** Musical Instrument Digital Interface (MIDI) */
787
331
  MID: "audio/midi",
788
332
  /** Musical Instrument Digital Interface (MIDI) */
@@ -1465,452 +1009,1228 @@ var Transportr = (() => {
1465
1009
  * 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.
1466
1010
  *
1467
1011
  * @example
1468
- * proxy-connection: keep-alive
1012
+ * proxy-connection: keep-alive
1013
+ */
1014
+ PROXY_CONNECTION: "proxy-connection",
1015
+ /**
1016
+ * Server-side deep packet insertion of a unique ID identifying customers of Verizon Wireless, also known as "perma-cookie" or "supercookie"
1017
+ *
1018
+ * @example
1019
+ * x-uidh: ...
1020
+ */
1021
+ X_UIDH: "x-uidh",
1022
+ /**
1023
+ * Used to prevent cross-site request forgery. Alternative header names are: X-CSRFToken and X-XSRF-TOKEN
1024
+ *
1025
+ * @example
1026
+ * x-csrf-token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql
1027
+ */
1028
+ X_CSRF_TOKEN: "x-csrf-token",
1029
+ /**
1030
+ * Specifying which web sites can participate in cross-origin resource sharing
1031
+ *
1032
+ * @example
1033
+ * access-control-allow-origin: *
1034
+ * Provisional
1035
+ */
1036
+ ACCESS_CONTROL_ALLOW_ORIGIN: "access-control-allow-origin",
1037
+ /**
1038
+ * Specifies which patch document formats this server supports
1039
+ *
1040
+ * @example
1041
+ * accept-patch: text/example,charset=utf-8
1042
+ * Permanent
1043
+ */
1044
+ ACCEPT_PATCH: "accept-patch",
1045
+ /**
1046
+ * What partial content range types this server supports via byte serving
1047
+ *
1048
+ * @example
1049
+ * accept-ranges: bytes
1050
+ * Permanent
1051
+ */
1052
+ ACCEPT_RANGES: "accept-ranges",
1053
+ /**
1054
+ * The age the object has been in a proxy cache in seconds
1055
+ *
1056
+ * @example
1057
+ * age: 12
1058
+ * Permanent
1059
+ */
1060
+ AGE: "age",
1061
+ /**
1062
+ * Valid actions for a specified resource. To be used for a 405 Method not allowed
1063
+ *
1064
+ * @example
1065
+ * allow: GET, HEAD
1066
+ * Permanent
1067
+ */
1068
+ ALLOW: "allow",
1069
+ /**
1070
+ * Tells all caching mechanisms from server to client whether they may cache this object. It is measured in seconds
1071
+ *
1072
+ * @example
1073
+ * cache-control: max-age=3600
1074
+ * Permanent
1075
+ */
1076
+ CACHE_CONTROL: "cache-control",
1077
+ /**
1078
+ * Control options for the current connection and list of hop-by-hop response fields
1079
+ *
1080
+ * @example
1081
+ * connection: close
1082
+ * Permanent
1083
+ */
1084
+ CONNECTION: "connection",
1085
+ /**
1086
+ * An opportunity to raise a "File Download" dialogue box for a known MIME type with binary format or suggest a filename for dynamic content. Quotes are necessary with special characters.
1087
+ *
1088
+ * @example
1089
+ * content-disposition: attachment, filename="fname.ext"
1090
+ * Permanent
1091
+ */
1092
+ CONTENT_DISPOSITION: "content-disposition",
1093
+ /**
1094
+ * The type of encoding used on the data. See HTTP compression.
1095
+ *
1096
+ * @example
1097
+ * content-encoding: gzip
1098
+ * Permanent
1099
+ */
1100
+ CONTENT_ENCODING: "content-encoding",
1101
+ /**
1102
+ * The natural language or languages of the intended audience for the enclosed content
1103
+ *
1104
+ * @example
1105
+ * content-language: da
1106
+ * Permanent
1107
+ */
1108
+ CONTENT_LANGUAGE: "content-language",
1109
+ /**
1110
+ * The length of the response body in octets (8-bit bytes)
1111
+ *
1112
+ * @example
1113
+ * content-length: 348
1114
+ * Permanent
1115
+ */
1116
+ CONTENT_LENGTH: "content-length",
1117
+ /**
1118
+ * An alternate location for the returned data
1119
+ *
1120
+ * @example
1121
+ * content-location: /index.htm
1122
+ * Permanent
1123
+ */
1124
+ CONTENT_LOCATION: "content-location",
1125
+ /**
1126
+ * Where in a full body message this partial message belongs
1127
+ *
1128
+ * @example
1129
+ * content-range: bytes 21010-47021/47022
1130
+ * Permanent
1131
+ */
1132
+ CONTENT_RANGE: "content-range",
1133
+ /**
1134
+ * The MIME type of this content
1135
+ *
1136
+ * @example
1137
+ * content-type: text/html, charset=utf-8
1138
+ * Permanent
1139
+ */
1140
+ CONTENT_TYPE: "content-type",
1141
+ /**
1142
+ * The date and time that the message was sent (in "HTTP-date" format as defined by RFC 7231)
1143
+ *
1144
+ * @example
1145
+ * date: Tue, 15 Nov 1994 08:12:31 GMT
1146
+ * Permanent
1147
+ */
1148
+ DATE: "date",
1149
+ /**
1150
+ * An identifier for a specific version of a resource, often a message digest
1151
+ *
1152
+ * @example
1153
+ * etag: "737060cd8c284d8af7ad3082f209582d"
1154
+ * Permanent
1155
+ */
1156
+ ETAG: "etag",
1157
+ /**
1158
+ * Gives the date/time after which the response is considered stale (in "HTTP-date" format as defined by RFC 7231)
1159
+ *
1160
+ * @example
1161
+ * expires: Thu, 01 Dec 1994 16:00:00 GMT
1162
+ * Permanent
1163
+ */
1164
+ EXPIRES: "expires",
1165
+ /**
1166
+ * The last modified date for the requested object (in "HTTP-date" format as defined by RFC 7231)
1167
+ *
1168
+ * @example
1169
+ * last-modified: Tue, 15 Nov 1994 12:45:26 GMT
1170
+ * Permanent
1171
+ */
1172
+ LAST_MODIFIED: "last-modified",
1173
+ /**
1174
+ * Used to express a typed relationship with another resource, where the relation type is defined by RFC 5988
1175
+ *
1176
+ * @example
1177
+ * link: </feed>, rel="alternate"
1178
+ * Permanent
1179
+ */
1180
+ LINK: "link",
1181
+ /**
1182
+ * Used in redirection, or when a new resource has been created.
1183
+ *
1184
+ * @example
1185
+ * location: http://www.w3.org/pub/WWW/People.html
1186
+ * Permanent
1187
+ */
1188
+ LOCATION: "location",
1189
+ /**
1190
+ * This field is supposed to set P3P policy, in the form of P3P:CP="your_compact_policy". However, P3P did not take off, most browsers have never fully
1191
+ * implemented it, a lot of websites set this field with fake policy text, that was enough to fool browsers the existence of P3P policy and grant permissions for third party cookies.
1192
+ *
1193
+ * @example
1194
+ * p3p: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
1195
+ * Permanent
1196
+ */
1197
+ P3P: "p3p",
1198
+ /**
1199
+ * Implementation-specific fields that may have various effects anywhere along the request-response chain.
1200
+ *
1201
+ * @example
1202
+ * pragma: no-cache
1203
+ * Permanent
1204
+ */
1205
+ PRAGMA: "pragma",
1206
+ /**
1207
+ * Request authentication to access the proxy.
1208
+ *
1209
+ * @example
1210
+ * proxy-authenticate: Basic
1211
+ * Permanent
1212
+ */
1213
+ PROXY_AUTHENTICATION: "proxy-authenticate",
1214
+ /**
1215
+ * HTTP Public Key Pinning, announces hash of website's authentic TLS certificate
1216
+ *
1217
+ * @example
1218
+ * public-key-pins: max-age=2592000, pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=",
1219
+ * Permanent
1220
+ */
1221
+ PUBLIC_KEY_PINS: "public-key-pins",
1222
+ /**
1223
+ * If an entity is temporarily unavailable, this instructs the client to try again later. Value could be a specified period of time (in seconds) or a HTTP-date.
1224
+ *
1225
+ * @example
1226
+ * retry-after: 120
1227
+ * retry-after: Fri, 07 Nov 2014 23:59:59 GMT
1228
+ * Permanent
1229
+ */
1230
+ RETRY_AFTER: "retry-after",
1231
+ /**
1232
+ * A name for the server
1233
+ *
1234
+ * @example
1235
+ * server: Apache/2.4.1 (Unix)
1236
+ * Permanent
1237
+ */
1238
+ SERVER: "server",
1239
+ /**
1240
+ * An HTTP cookie
1241
+ *
1242
+ * @example
1243
+ * set-cookie: UserID=JohnDoe, Max-Age=3600, Version=1
1244
+ * Permanent
1245
+ */
1246
+ SET_COOKIE: "set-cookie",
1247
+ /**
1248
+ * CGI header field specifying the status of the HTTP response. Normal HTTP responses use a separate "Status-Line" instead, defined by RFC 7230.
1249
+ *
1250
+ * @example
1251
+ * status: 200 OK
1469
1252
  */
1470
- PROXY_CONNECTION: "proxy-connection",
1253
+ STATUS: "status",
1471
1254
  /**
1472
- * Server-side deep packet insertion of a unique ID identifying customers of Verizon Wireless, also known as "perma-cookie" or "supercookie"
1255
+ * A HSTS Policy informing the HTTP client how long to cache the HTTPS only policy and whether this applies to subdomains.
1473
1256
  *
1474
1257
  * @example
1475
- * x-uidh: ...
1258
+ * strict-transport-security: max-age=16070400, includeSubDomains
1259
+ * Permanent
1476
1260
  */
1477
- X_UIDH: "x-uidh",
1261
+ STRICT_TRANSPORT_SECURITY: "strict-transport-security",
1478
1262
  /**
1479
- * Used to prevent cross-site request forgery. Alternative header names are: X-CSRFToken and X-XSRF-TOKEN
1263
+ * The Trailer general field value indicates that the given set of header fields is present in the trailer of a message encoded with chunked transfer coding.
1480
1264
  *
1481
1265
  * @example
1482
- * x-csrf-token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql
1266
+ * trailer: Max-Forwards
1267
+ * Permanent
1483
1268
  */
1484
- X_CSRF_TOKEN: "x-csrf-token",
1269
+ TRAILER: "trailer",
1485
1270
  /**
1486
- * Specifying which web sites can participate in cross-origin resource sharing
1271
+ * The form of encoding used to safely transfer the entity to the user. Currently defined methods are: chunked, compress, deflate, gzip, identity.
1487
1272
  *
1488
1273
  * @example
1489
- * access-control-allow-origin: *
1490
- * Provisional
1274
+ * transfer-encoding: chunked
1275
+ * Permanent
1491
1276
  */
1492
- ACCESS_CONTROL_ALLOW_ORIGIN: "access-control-allow-origin",
1277
+ TRANSFER_ENCODING: "transfer-encoding",
1493
1278
  /**
1494
- * Specifies which patch document formats this server supports
1279
+ * Ask the client to upgrade to another protocol.
1495
1280
  *
1496
1281
  * @example
1497
- * accept-patch: text/example,charset=utf-8
1282
+ * upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
1498
1283
  * Permanent
1499
1284
  */
1500
- ACCEPT_PATCH: "accept-patch",
1285
+ UPGRADE: "upgrade",
1501
1286
  /**
1502
- * What partial content range types this server supports via byte serving
1287
+ * Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server.
1503
1288
  *
1504
1289
  * @example
1505
- * accept-ranges: bytes
1290
+ * vary: *
1506
1291
  * Permanent
1507
1292
  */
1508
- ACCEPT_RANGES: "accept-ranges",
1293
+ VARY: "vary",
1509
1294
  /**
1510
- * The age the object has been in a proxy cache in seconds
1295
+ * Informs the client of proxies through which the response was sent.
1511
1296
  *
1512
1297
  * @example
1513
- * age: 12
1298
+ * via: 1.0 fred, 1.1 example.com (Apache/1.1)
1514
1299
  * Permanent
1515
1300
  */
1516
- AGE: "age",
1301
+ VIA: "via",
1517
1302
  /**
1518
- * Valid actions for a specified resource. To be used for a 405 Method not allowed
1303
+ * A general warning about possible problems with the entity body.
1519
1304
  *
1520
1305
  * @example
1521
- * allow: GET, HEAD
1306
+ * warning: 199 Miscellaneous warning
1522
1307
  * Permanent
1523
1308
  */
1524
- ALLOW: "allow",
1309
+ WARNING: "warning",
1525
1310
  /**
1526
- * Tells all caching mechanisms from server to client whether they may cache this object. It is measured in seconds
1311
+ * Indicates the authentication scheme that should be used to access the requested entity.
1527
1312
  *
1528
1313
  * @example
1529
- * cache-control: max-age=3600
1314
+ * www-authenticate: Basic
1530
1315
  * Permanent
1531
1316
  */
1532
- CACHE_CONTROL: "cache-control",
1317
+ WWW_AUTHENTICATE: "www-authenticate",
1533
1318
  /**
1534
- * Control options for the current connection and list of hop-by-hop response fields
1319
+ * Cross-site scripting (XSS) filter
1535
1320
  *
1536
1321
  * @example
1537
- * connection: close
1538
- * Permanent
1322
+ * x-xss-protection: 1, mode=block
1539
1323
  */
1540
- CONNECTION: "connection",
1324
+ X_XSS_PROTECTION: "x-xss-protection",
1541
1325
  /**
1542
- * An opportunity to raise a "File Download" dialogue box for a known MIME type with binary format or suggest a filename for dynamic content. Quotes are necessary with special characters.
1326
+ * The HTTP Content-Security-Policy response header allows web site administrators to control resources the user agent is allowed
1327
+ * to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints.
1328
+ * This helps guard against cross-site scripting attacks (Cross-site_scripting).
1543
1329
  *
1544
1330
  * @example
1545
- * content-disposition: attachment, filename="fname.ext"
1546
- * Permanent
1331
+ * content-security-policy: default-src
1547
1332
  */
1548
- CONTENT_DISPOSITION: "content-disposition",
1333
+ CONTENT_SECURITY_POLICY: "content-security-policy",
1549
1334
  /**
1550
- * The type of encoding used on the data. See HTTP compression.
1335
+ * The only defined value, "nosniff", prevents Internet Explorer from MIME-sniffing a response away from the declared content-type. This also applies to Google Chrome, when downloading extensions.
1551
1336
  *
1552
1337
  * @example
1553
- * content-encoding: gzip
1554
- * Permanent
1338
+ * x-content-type-options: nosniff
1555
1339
  */
1556
- CONTENT_ENCODING: "content-encoding",
1340
+ X_CONTENT_TYPE_OPTIONS: "x-content-type-options",
1557
1341
  /**
1558
- * The natural language or languages of the intended audience for the enclosed content
1342
+ * specifies the technology (e.g. ASP.NET, PHP, JBoss) supporting the web application (version details are often in X-Runtime, X-Version, or X-AspNet-Version)
1559
1343
  *
1560
1344
  * @example
1561
- * content-language: da
1562
- * Permanent
1345
+ * x-powered-by: PHP/5.4.0
1563
1346
  */
1564
- CONTENT_LANGUAGE: "content-language",
1347
+ X_POWERED_BY: "x-powered-by"
1348
+ };
1349
+ var http_response_headers_default = HttpResponseHeader;
1350
+
1351
+ // src/response-status.js
1352
+ var ResponseStatus = class {
1353
+ /** @type {number} */
1354
+ #code;
1355
+ /** @type {string} */
1356
+ #text;
1357
+ /**
1358
+ *
1359
+ * @param {number} code The status code from the {@link Response}
1360
+ * @param {string} text The status text from the {@link Response}
1361
+ */
1362
+ constructor(code, text) {
1363
+ this.#code = code;
1364
+ this.#text = text;
1365
+ }
1366
+ /**
1367
+ * Returns the status code from the {@link Response}
1368
+ *
1369
+ * @returns {number} The status code.
1370
+ */
1371
+ get code() {
1372
+ return this.#code;
1373
+ }
1374
+ /**
1375
+ * Returns the status text from the {@link Response}.
1376
+ *
1377
+ * @returns {string} The status text.
1378
+ */
1379
+ get text() {
1380
+ return this.#text;
1381
+ }
1382
+ /**
1383
+ * A String value that is used in the creation of the default string
1384
+ * description of an object. Called by the built-in method {@link Object.prototype.toString}.
1385
+ *
1386
+ * @override
1387
+ * @returns {string} The default string description of this object.
1388
+ */
1389
+ get [Symbol.toStringTag]() {
1390
+ return "ResponseStatus";
1391
+ }
1392
+ /**
1393
+ * tostring method for the class.
1394
+ *
1395
+ * @override
1396
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString|Object.prototype.toString}
1397
+ * @returns {string} The status code and status text.
1398
+ */
1399
+ toString() {
1400
+ return `${this.#code} ${this.#text}`;
1401
+ }
1402
+ };
1403
+
1404
+ // src/parameter-map.js
1405
+ var ParameterMap = class _ParameterMap extends Map {
1406
+ /**
1407
+ * @param {Iterable<[string, *]>|Object} parameters The initial parameters to set.
1408
+ * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value.
1409
+ */
1410
+ constructor(parameters = {}) {
1411
+ super();
1412
+ for (const [key, value] of _ParameterMap.#entries(parameters)) {
1413
+ this.append(key, value);
1414
+ }
1415
+ }
1416
+ /**
1417
+ * Adds a new element with a specified key and value to the ParameterMap.
1418
+ * If an element with the same key already exists, the value will be replaced in the underlying {@link Set}.
1419
+ *
1420
+ * @override
1421
+ * @param {string} key The key to set.
1422
+ * @param {*} value The value to add to the ParameterMap
1423
+ * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value.
1424
+ */
1425
+ set(key, value) {
1426
+ const array = super.get(key);
1427
+ if (array?.length > 0) {
1428
+ array.length = 0;
1429
+ array[0] = value;
1430
+ } else {
1431
+ super.set(key, [value]);
1432
+ }
1433
+ return this;
1434
+ }
1435
+ /**
1436
+ * Adds all key-value pairs from an iterable or object to the ParameterMap.
1437
+ * If a key already exists, the value will be replaced in the underlying {@link Array}.
1438
+ *
1439
+ * @param {Iterable<[string, *]>|Object} parameters The parameters to set.
1440
+ * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value.
1441
+ */
1442
+ setAll(parameters) {
1443
+ for (const [key, value] of _ParameterMap.#entries(parameters)) {
1444
+ this.set(key, value);
1445
+ }
1446
+ return this;
1447
+ }
1448
+ /**
1449
+ * Returns the value associated to the key, or undefined if there is none.
1450
+ * If the key has multiple values, the first value will be returned.
1451
+ * If the key has no values, undefined will be returned.
1452
+ *
1453
+ * @override
1454
+ * @param {string} key The key to get.
1455
+ * @returns {*} The value associated to the key, or undefined if there is none.
1456
+ */
1457
+ get(key) {
1458
+ return super.get(key)?.[0];
1459
+ }
1460
+ /**
1461
+ * Returns an array of all values associated to the key, or undefined if there are none.
1462
+ *
1463
+ * @param {string} key The key to get.
1464
+ * @returns {Array<*>} An array of all values associated to the key, or undefined if there are none.
1465
+ */
1466
+ getAll(key) {
1467
+ return super.get(key);
1468
+ }
1469
+ /**
1470
+ * Appends a new value to an existing key inside a ParameterMap, or adds the new key if it does not exist.
1471
+ *
1472
+ * @param {string} key The key to append.
1473
+ * @param {*} value The value to append.
1474
+ * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value to allow chaining.
1475
+ */
1476
+ append(key, value) {
1477
+ const array = super.get(key);
1478
+ if (array?.length > 0) {
1479
+ array.push(value);
1480
+ } else {
1481
+ super.set(key, [value]);
1482
+ }
1483
+ return this;
1484
+ }
1485
+ /**
1486
+ * Appends all key-value pairs from an iterable or object to the ParameterMap.
1487
+ * If a key already exists, the value will be appended to the underlying {@link Array}.
1488
+ * If a key does not exist, the key and value will be added to the ParameterMap.
1489
+ *
1490
+ * @param {Iterable<[string, *]>|Object} parameters The parameters to append.
1491
+ * @returns {ParameterMap<string, *>} The ParameterMap with the updated key and value.
1492
+ */
1493
+ appendAll(parameters) {
1494
+ for (const [key, value] of _ParameterMap.#entries(parameters)) {
1495
+ this.append(key, value);
1496
+ }
1497
+ return this;
1498
+ }
1499
+ /**
1500
+ * Checks if a specific key has a specific value.
1501
+ *
1502
+ * @param {*} value The value to check.
1503
+ * @returns {boolean} True if the key has the value, false otherwise.
1504
+ */
1505
+ hasValue(value) {
1506
+ for (const array of super.values()) {
1507
+ if (array.includes(value)) {
1508
+ return true;
1509
+ }
1510
+ }
1511
+ return false;
1512
+ }
1513
+ /**
1514
+ * Removes a specific value from a specific key.
1515
+ *
1516
+ * @param {*} value The value to remove.
1517
+ * @returns {boolean} True if the value was removed, false otherwise.
1518
+ */
1519
+ deleteValue(value) {
1520
+ for (const array of this.values()) {
1521
+ if (array.includes(value)) {
1522
+ return array.splice(array.indexOf(value), 1).length > 0;
1523
+ }
1524
+ }
1525
+ return false;
1526
+ }
1527
+ /**
1528
+ * Determines whether the ParameterMap contains anything.
1529
+ *
1530
+ * @returns {boolean} True if the ParameterMap size is greater than 0, false otherwise.
1531
+ */
1532
+ isEmpty() {
1533
+ return this.size === 0;
1534
+ }
1535
+ /**
1536
+ * Returns an Object that can be serialized to JSON.
1537
+ * If a key has only one value, the value will be a single value.
1538
+ * If a key has multiple values, the value will be an array of values.
1539
+ * If a key has no values, the value will be undefined.
1540
+ *
1541
+ * @override
1542
+ * @returns {Object} The Object to be serialized to JSON.
1543
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON_behavior}
1544
+ */
1545
+ toJSON() {
1546
+ const obj = /* @__PURE__ */ Object.create(null);
1547
+ for (const [key, values] of super.entries()) {
1548
+ obj[key] = values.length === 1 ? values[0] : values;
1549
+ }
1550
+ return obj;
1551
+ }
1552
+ /**
1553
+ * Returns an iterator that yields all key-value pairs in the map as arrays in their insertion order.
1554
+ *
1555
+ * @override
1556
+ * @yields {[string, *]} An iterator for the key-value pairs in the map.
1557
+ */
1558
+ *entries() {
1559
+ for (const [key, array] of super.entries()) {
1560
+ for (const value of array) {
1561
+ yield [key, value];
1562
+ }
1563
+ }
1564
+ }
1565
+ /**
1566
+ * Returns an iterator that yields all key-value pairs in the map as arrays in their insertion order.
1567
+ *
1568
+ * @override
1569
+ * @yields {[string, *]} An iterator for the key-value pairs in the map.
1570
+ */
1571
+ *[Symbol.iterator]() {
1572
+ yield* this.entries();
1573
+ }
1574
+ /**
1575
+ * Returns an iterable of key, value pairs for every entry in the parameters object.
1576
+ *
1577
+ * @private
1578
+ * @static
1579
+ * @param {Iterable<[string, *]>|Object} parameters The parameters to set.
1580
+ * @returns {Iterable<[string, *]>} An iterable of key, value pairs for every entry in the parameters object.
1581
+ */
1582
+ static #entries(parameters) {
1583
+ return parameters[Symbol.iterator] ? parameters : Object.entries(parameters);
1584
+ }
1585
+ /**
1586
+ * A String value that is used in the creation of the default string description of an object.
1587
+ * Called by the built-in method {@link Object.prototype.toString}.
1588
+ *
1589
+ * @override
1590
+ * @returns {string} The default string description of this object.
1591
+ */
1592
+ get [Symbol.toStringTag]() {
1593
+ return "ParameterMap";
1594
+ }
1595
+ };
1596
+
1597
+ // node_modules/@d1g1tal/media-type/src/utils.js
1598
+ var whitespaceCharacters = [" ", " ", "\n", "\r"];
1599
+ var leadingWhitespace = /^[ \t\n\r]+/u;
1600
+ var trailingWhitespace = /[ \t\n\r]+$/u;
1601
+ var httpTokenCodePoints = /^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u;
1602
+ var httpQuotedTokenCodePoints = /^[\t\u0020-\u007E\u0080-\u00FF]*$/u;
1603
+ var removeLeadingAndTrailingHTTPWhitespace = (string) => string.replace(leadingWhitespace, "").replace(trailingWhitespace, "");
1604
+ var removeTrailingHTTPWhitespace = (string) => string.replace(trailingWhitespace, "");
1605
+ var isHTTPWhitespaceChar = (char) => whitespaceCharacters.includes(char);
1606
+ var solelyContainsHTTPTokenCodePoints = (string) => httpTokenCodePoints.test(string);
1607
+ var solelyContainsHTTPQuotedStringTokenCodePoints = (string) => httpQuotedTokenCodePoints.test(string);
1608
+ var asciiLowercase = (string) => {
1609
+ let result = "";
1610
+ for (const [char, charCode = char.charCodeAt(0)] of string) {
1611
+ result += charCode >= 65 && charCode <= 90 ? String.fromCharCode(charCode + 32) : char;
1612
+ }
1613
+ return result;
1614
+ };
1615
+ var collectAnHTTPQuotedString = (input, position) => {
1616
+ let value = "";
1617
+ for (let length = input.length, char; ++position < length; ) {
1618
+ char = input[position];
1619
+ if (char == "\\") {
1620
+ value += ++position < length ? input[position] : char;
1621
+ } else if (char == '"') {
1622
+ break;
1623
+ } else {
1624
+ value += char;
1625
+ }
1626
+ }
1627
+ return [value, position];
1628
+ };
1629
+
1630
+ // node_modules/@d1g1tal/media-type/src/media-type-parameters.js
1631
+ var MediaTypeParameters = class {
1632
+ /** @type {Map<string, string>} */
1633
+ #map;
1565
1634
  /**
1566
- * The length of the response body in octets (8-bit bytes)
1635
+ * Create a new MediaTypeParameters instance.
1567
1636
  *
1568
- * @example
1569
- * content-length: 348
1570
- * Permanent
1637
+ * @param {Array<Array<string>>} entries An array of [name, value] tuples.
1571
1638
  */
1572
- CONTENT_LENGTH: "content-length",
1639
+ constructor(entries) {
1640
+ this.#map = new Map(entries);
1641
+ }
1573
1642
  /**
1574
- * An alternate location for the returned data
1643
+ * Gets the number of media type parameters.
1575
1644
  *
1576
- * @example
1577
- * content-location: /index.htm
1578
- * Permanent
1645
+ * @returns {number} The number of media type parameters
1579
1646
  */
1580
- CONTENT_LOCATION: "content-location",
1647
+ get size() {
1648
+ return this.#map.size;
1649
+ }
1581
1650
  /**
1582
- * Where in a full body message this partial message belongs
1651
+ * Gets the media type parameter value for the supplied name.
1583
1652
  *
1584
- * @example
1585
- * content-range: bytes 21010-47021/47022
1586
- * Permanent
1653
+ * @param {string} name The name of the media type parameter to retrieve.
1654
+ * @returns {string} The media type parameter value.
1587
1655
  */
1588
- CONTENT_RANGE: "content-range",
1656
+ get(name) {
1657
+ return this.#map.get(asciiLowercase(String(name)));
1658
+ }
1589
1659
  /**
1590
- * The MIME type of this content
1660
+ * Indicates whether the media type parameter with the specified name exists or not.
1591
1661
  *
1592
- * @example
1593
- * content-type: text/html, charset=utf-8
1594
- * Permanent
1662
+ * @param {string} name The name of the media type parameter to check.
1663
+ * @returns {boolean} true if the media type parameter exists, false otherwise.
1595
1664
  */
1596
- CONTENT_TYPE: "content-type",
1665
+ has(name) {
1666
+ return this.#map.has(asciiLowercase(String(name)));
1667
+ }
1597
1668
  /**
1598
- * The date and time that the message was sent (in "HTTP-date" format as defined by RFC 7231)
1669
+ * Adds a new media type parameter using the specified name and value to the MediaTypeParameters.
1670
+ * If an parameter with the same name already exists, the parameter will be updated.
1599
1671
  *
1600
- * @example
1601
- * date: Tue, 15 Nov 1994 08:12:31 GMT
1602
- * Permanent
1672
+ * @param {string} name The name of the media type parameter to set.
1673
+ * @param {string} value The media type parameter value.
1674
+ * @returns {MediaTypeParameters} This instance.
1603
1675
  */
1604
- DATE: "date",
1676
+ set(name, value) {
1677
+ name = asciiLowercase(String(name));
1678
+ value = String(value);
1679
+ if (!solelyContainsHTTPTokenCodePoints(name)) {
1680
+ throw new Error(`Invalid media type parameter name "${name}": only HTTP token code points are valid.`);
1681
+ }
1682
+ if (!solelyContainsHTTPQuotedStringTokenCodePoints(value)) {
1683
+ throw new Error(`Invalid media type parameter value "${value}": only HTTP quoted-string token code points are valid.`);
1684
+ }
1685
+ this.#map.set(name, value);
1686
+ return this;
1687
+ }
1605
1688
  /**
1606
- * An identifier for a specific version of a resource, often a message digest
1607
- *
1608
- * @example
1609
- * etag: "737060cd8c284d8af7ad3082f209582d"
1610
- * Permanent
1689
+ * Clears all the media type parameters.
1611
1690
  */
1612
- ETAG: "etag",
1691
+ clear() {
1692
+ this.#map.clear();
1693
+ }
1613
1694
  /**
1614
- * Gives the date/time after which the response is considered stale (in "HTTP-date" format as defined by RFC 7231)
1695
+ * Removes the media type parameter using the specified name.
1615
1696
  *
1616
- * @example
1617
- * expires: Thu, 01 Dec 1994 16:00:00 GMT
1618
- * Permanent
1697
+ * @param {string} name The name of the media type parameter to delete.
1698
+ * @returns {boolean} true if the parameter existed and has been removed, or false if the parameter does not exist.
1619
1699
  */
1620
- EXPIRES: "expires",
1700
+ delete(name) {
1701
+ name = asciiLowercase(String(name));
1702
+ return this.#map.delete(name);
1703
+ }
1621
1704
  /**
1622
- * The last modified date for the requested object (in "HTTP-date" format as defined by RFC 7231)
1705
+ * Executes a provided function once per each name/value pair in the MediaTypeParameters, in insertion order.
1623
1706
  *
1624
- * @example
1625
- * last-modified: Tue, 15 Nov 1994 12:45:26 GMT
1626
- * Permanent
1707
+ * @param {function(string, string): void} callback The function called on each iteration.
1708
+ * @param {*} [thisArg] Optional object when binding 'this' to the callback.
1627
1709
  */
1628
- LAST_MODIFIED: "last-modified",
1710
+ forEach(callback, thisArg) {
1711
+ this.#map.forEach(callback, thisArg);
1712
+ }
1629
1713
  /**
1630
- * Used to express a typed relationship with another resource, where the relation type is defined by RFC 5988
1714
+ * Returns an iterable of parameter names.
1631
1715
  *
1632
- * @example
1633
- * link: </feed>, rel="alternate"
1634
- * Permanent
1716
+ * @returns {IterableIterator<string>} The {@link IterableIterator} of media type parameter names.
1635
1717
  */
1636
- LINK: "link",
1718
+ keys() {
1719
+ return this.#map.keys();
1720
+ }
1637
1721
  /**
1638
- * Used in redirection, or when a new resource has been created.
1722
+ * Returns an iterable of parameter values.
1639
1723
  *
1640
- * @example
1641
- * location: http://www.w3.org/pub/WWW/People.html
1642
- * Permanent
1724
+ * @returns {IterableIterator<string>} The {@link IterableIterator} of media type parameter values.
1643
1725
  */
1644
- LOCATION: "location",
1726
+ values() {
1727
+ return this.#map.values();
1728
+ }
1645
1729
  /**
1646
- * This field is supposed to set P3P policy, in the form of P3P:CP="your_compact_policy". However, P3P did not take off, most browsers have never fully
1647
- * implemented it, a lot of websites set this field with fake policy text, that was enough to fool browsers the existence of P3P policy and grant permissions for third party cookies.
1730
+ * Returns an iterable of name, value pairs for every parameter entry in the media type parameters.
1648
1731
  *
1649
- * @example
1650
- * p3p: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
1651
- * Permanent
1732
+ * @returns {IterableIterator<Array<Array<string>>>} The media type parameter entries.
1652
1733
  */
1653
- P3P: "p3p",
1734
+ entries() {
1735
+ return this.#map.entries();
1736
+ }
1654
1737
  /**
1655
- * Implementation-specific fields that may have various effects anywhere along the request-response chain.
1738
+ * A method that returns the default iterator for the {@link MediaTypeParameters}. Called by the semantics of the for-of statement.
1656
1739
  *
1657
- * @example
1658
- * pragma: no-cache
1659
- * Permanent
1740
+ * @returns {Iterator<string, string, undefined>} The {@link Symbol.iterator} for the media type parameters.
1660
1741
  */
1661
- PRAGMA: "pragma",
1742
+ [Symbol.iterator]() {
1743
+ return this.#map[Symbol.iterator]();
1744
+ }
1662
1745
  /**
1663
- * Request authentication to access the proxy.
1746
+ * Returns a string representation of the media type parameters.
1747
+ * This method is called by the `String()` function.
1664
1748
  *
1665
1749
  * @example
1666
- * proxy-authenticate: Basic
1667
- * Permanent
1750
+ * const parameters = new MediaTypeParameters(new Map([['charset', 'utf-8']]));
1751
+ * String(parameters); // 'charset=utf-8'
1752
+ * parameters.toString(); // 'charset=utf-8'
1753
+ * parameters + ''; // 'charset=utf-8'
1754
+ * `${parameters}`; // 'charset=utf-8'
1755
+ * parameters[Symbol.toStringTag]; // 'MediaTypeParameters'
1756
+ * parameters[Symbol.toStringTag](); // 'MediaTypeParameters'
1757
+ * Object.prototype.toString.call(parameters); // '[object MediaTypeParameters]'
1758
+ * parameters + ''; // 'charset=utf-8'
1759
+ * @returns {string} The string representation of the media type parameters.
1668
1760
  */
1669
- PROXY_AUTHENTICATION: "proxy-authenticate",
1761
+ [Symbol.toStringTag]() {
1762
+ return "MediaTypeParameters";
1763
+ }
1764
+ };
1765
+
1766
+ // node_modules/@d1g1tal/media-type/src/parser.js
1767
+ var parse = (input) => {
1768
+ input = removeLeadingAndTrailingHTTPWhitespace(input);
1769
+ let position = 0;
1770
+ let type = "";
1771
+ while (position < input.length && input[position] != "/") {
1772
+ type += input[position];
1773
+ ++position;
1774
+ }
1775
+ if (type.length === 0 || !solelyContainsHTTPTokenCodePoints(type)) {
1776
+ return null;
1777
+ }
1778
+ if (position >= input.length) {
1779
+ return null;
1780
+ }
1781
+ ++position;
1782
+ let subtype = "";
1783
+ while (position < input.length && input[position] != ";") {
1784
+ subtype += input[position];
1785
+ ++position;
1786
+ }
1787
+ subtype = removeTrailingHTTPWhitespace(subtype);
1788
+ if (subtype.length === 0 || !solelyContainsHTTPTokenCodePoints(subtype)) {
1789
+ return null;
1790
+ }
1791
+ const mediaType = {
1792
+ type: asciiLowercase(type),
1793
+ subtype: asciiLowercase(subtype),
1794
+ parameters: /* @__PURE__ */ new Map()
1795
+ };
1796
+ while (position < input.length) {
1797
+ ++position;
1798
+ while (isHTTPWhitespaceChar(input[position])) {
1799
+ ++position;
1800
+ }
1801
+ let parameterName = "";
1802
+ while (position < input.length && input[position] != ";" && input[position] != "=") {
1803
+ parameterName += input[position];
1804
+ ++position;
1805
+ }
1806
+ parameterName = asciiLowercase(parameterName);
1807
+ if (position < input.length) {
1808
+ if (input[position] == ";") {
1809
+ continue;
1810
+ }
1811
+ ++position;
1812
+ }
1813
+ let parameterValue = null;
1814
+ if (input[position] == '"') {
1815
+ [parameterValue, position] = collectAnHTTPQuotedString(input, position);
1816
+ while (position < input.length && input[position] != ";") {
1817
+ ++position;
1818
+ }
1819
+ } else {
1820
+ parameterValue = "";
1821
+ while (position < input.length && input[position] != ";") {
1822
+ parameterValue += input[position];
1823
+ ++position;
1824
+ }
1825
+ parameterValue = removeTrailingHTTPWhitespace(parameterValue);
1826
+ if (parameterValue === "") {
1827
+ continue;
1828
+ }
1829
+ }
1830
+ if (parameterName.length > 0 && solelyContainsHTTPTokenCodePoints(parameterName) && solelyContainsHTTPQuotedStringTokenCodePoints(parameterValue) && !mediaType.parameters.has(parameterName)) {
1831
+ mediaType.parameters.set(parameterName, parameterValue);
1832
+ }
1833
+ }
1834
+ return mediaType;
1835
+ };
1836
+ var parser_default = parse;
1837
+
1838
+ // node_modules/@d1g1tal/media-type/src/serializer.js
1839
+ var serialize = (mediaType) => {
1840
+ let serialization = `${mediaType.type}/${mediaType.subtype}`;
1841
+ if (mediaType.parameters.size === 0) {
1842
+ return serialization;
1843
+ }
1844
+ for (let [name, value] of mediaType.parameters) {
1845
+ serialization += `;${name}=`;
1846
+ if (!solelyContainsHTTPTokenCodePoints(value) || value.length === 0) {
1847
+ value = `"${value.replace(/(["\\])/ug, "\\$1")}"`;
1848
+ }
1849
+ serialization += value;
1850
+ }
1851
+ return serialization;
1852
+ };
1853
+ var serializer_default = serialize;
1854
+
1855
+ // node_modules/@d1g1tal/media-type/src/media-type.js
1856
+ var MediaType = class _MediaType {
1857
+ /** @type {string} */
1858
+ #type;
1859
+ /** @type {string} */
1860
+ #subtype;
1861
+ /** @type {MediaTypeParameters} */
1862
+ #parameters;
1670
1863
  /**
1671
- * HTTP Public Key Pinning, announces hash of website's authentic TLS certificate
1864
+ * Create a new MediaType instance from a string representation.
1672
1865
  *
1673
- * @example
1674
- * public-key-pins: max-age=2592000, pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=",
1675
- * Permanent
1866
+ * @param {string} mediaType The media type to parse.
1867
+ * @param {Object} [parameters] Optional parameters.
1676
1868
  */
1677
- PUBLIC_KEY_PINS: "public-key-pins",
1869
+ constructor(mediaType, parameters = {}) {
1870
+ const { type, subtype, parameters: parsedParameters } = parser_default(mediaType);
1871
+ this.#type = type;
1872
+ this.#subtype = subtype;
1873
+ this.#parameters = new MediaTypeParameters([...parsedParameters, ...Object.entries(parameters).map(([name, value]) => [asciiLowercase(name), asciiLowercase(value)])]);
1874
+ }
1678
1875
  /**
1679
- * If an entity is temporarily unavailable, this instructs the client to try again later. Value could be a specified period of time (in seconds) or a HTTP-date.
1876
+ * Static factory method for parsing a media type.
1680
1877
  *
1681
- * @example
1682
- * retry-after: 120
1683
- * retry-after: Fri, 07 Nov 2014 23:59:59 GMT
1684
- * Permanent
1878
+ * @param {string} string The media type to parse.
1879
+ * @returns {MediaType} The parsed {@link MediaType} object or null if the string could not be parsed.
1685
1880
  */
1686
- RETRY_AFTER: "retry-after",
1881
+ static parse(string) {
1882
+ try {
1883
+ return new _MediaType(string);
1884
+ } catch (e) {
1885
+ throw new Error(`Could not parse media type string '${string}'`);
1886
+ }
1887
+ }
1687
1888
  /**
1688
- * A name for the server
1889
+ * Gets the media type essence (type/subtype).
1689
1890
  *
1690
- * @example
1691
- * server: Apache/2.4.1 (Unix)
1692
- * Permanent
1891
+ * @returns {string} The media type without any parameters
1693
1892
  */
1694
- SERVER: "server",
1893
+ get essence() {
1894
+ return `${this.#type}/${this.#subtype}`;
1895
+ }
1695
1896
  /**
1696
- * An HTTP cookie
1897
+ * Gets the type.
1697
1898
  *
1698
- * @example
1699
- * set-cookie: UserID=JohnDoe, Max-Age=3600, Version=1
1700
- * Permanent
1899
+ * @returns {string} The type.
1701
1900
  */
1702
- SET_COOKIE: "set-cookie",
1901
+ get type() {
1902
+ return this.#type;
1903
+ }
1703
1904
  /**
1704
- * CGI header field specifying the status of the HTTP response. Normal HTTP responses use a separate "Status-Line" instead, defined by RFC 7230.
1705
- *
1706
- * @example
1707
- * status: 200 OK
1905
+ * Sets the type.
1708
1906
  */
1709
- STATUS: "status",
1907
+ set type(value) {
1908
+ value = asciiLowercase(String(value));
1909
+ if (value.length === 0) {
1910
+ throw new Error("Invalid type: must be a non-empty string");
1911
+ }
1912
+ if (!solelyContainsHTTPTokenCodePoints(value)) {
1913
+ throw new Error(`Invalid type ${value}: must contain only HTTP token code points`);
1914
+ }
1915
+ this.#type = value;
1916
+ }
1710
1917
  /**
1711
- * A HSTS Policy informing the HTTP client how long to cache the HTTPS only policy and whether this applies to subdomains.
1918
+ * Gets the subtype.
1712
1919
  *
1713
- * @example
1714
- * strict-transport-security: max-age=16070400, includeSubDomains
1715
- * Permanent
1920
+ * @returns {string} The subtype.
1716
1921
  */
1717
- STRICT_TRANSPORT_SECURITY: "strict-transport-security",
1922
+ get subtype() {
1923
+ return this.#subtype;
1924
+ }
1718
1925
  /**
1719
- * The Trailer general field value indicates that the given set of header fields is present in the trailer of a message encoded with chunked transfer coding.
1720
- *
1721
- * @example
1722
- * trailer: Max-Forwards
1723
- * Permanent
1926
+ * Sets the subtype.
1724
1927
  */
1725
- TRAILER: "trailer",
1928
+ set subtype(value) {
1929
+ value = asciiLowercase(String(value));
1930
+ if (value.length === 0) {
1931
+ throw new Error("Invalid subtype: must be a non-empty string");
1932
+ }
1933
+ if (!solelyContainsHTTPTokenCodePoints(value)) {
1934
+ throw new Error(`Invalid subtype ${value}: must contain only HTTP token code points`);
1935
+ }
1936
+ this.#subtype = value;
1937
+ }
1726
1938
  /**
1727
- * The form of encoding used to safely transfer the entity to the user. Currently defined methods are: chunked, compress, deflate, gzip, identity.
1939
+ * Gets the parameters.
1728
1940
  *
1729
- * @example
1730
- * transfer-encoding: chunked
1731
- * Permanent
1941
+ * @returns {MediaTypeParameters} The media type parameters.
1732
1942
  */
1733
- TRANSFER_ENCODING: "transfer-encoding",
1943
+ get parameters() {
1944
+ return this.#parameters;
1945
+ }
1734
1946
  /**
1735
- * Ask the client to upgrade to another protocol.
1947
+ * Gets the serialized version of the media type.
1736
1948
  *
1737
- * @example
1738
- * upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
1739
- * Permanent
1949
+ * @returns {string} The serialized media type.
1740
1950
  */
1741
- UPGRADE: "upgrade",
1951
+ toString() {
1952
+ return serializer_default(this);
1953
+ }
1742
1954
  /**
1743
- * Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server.
1955
+ * Determines if this instance is a JavaScript media type.
1744
1956
  *
1745
- * @example
1746
- * vary: *
1747
- * Permanent
1957
+ * @param {Object} [options] Optional options.
1958
+ * @param {boolean} [options.prohibitParameters=false] The option to prohibit parameters when checking if the media type is JavaScript.
1959
+ * @returns {boolean} true if this instance represents a JavaScript media type, false otherwise.
1748
1960
  */
1749
- VARY: "vary",
1961
+ isJavaScript({ prohibitParameters = false } = {}) {
1962
+ switch (this.#type) {
1963
+ case "text": {
1964
+ switch (this.#subtype) {
1965
+ case "ecmascript":
1966
+ case "javascript":
1967
+ case "javascript1.0":
1968
+ case "javascript1.1":
1969
+ case "javascript1.2":
1970
+ case "javascript1.3":
1971
+ case "javascript1.4":
1972
+ case "javascript1.5":
1973
+ case "jscript":
1974
+ case "livescript":
1975
+ case "x-ecmascript":
1976
+ case "x-javascript":
1977
+ return !prohibitParameters || this.#parameters.size === 0;
1978
+ default:
1979
+ return false;
1980
+ }
1981
+ }
1982
+ case "application": {
1983
+ switch (this.#subtype) {
1984
+ case "ecmascript":
1985
+ case "javascript":
1986
+ case "x-ecmascript":
1987
+ case "x-javascript":
1988
+ return !prohibitParameters || this.#parameters.size === 0;
1989
+ default:
1990
+ return false;
1991
+ }
1992
+ }
1993
+ default:
1994
+ return false;
1995
+ }
1996
+ }
1750
1997
  /**
1751
- * Informs the client of proxies through which the response was sent.
1998
+ * Determines if this instance is an XML media type.
1752
1999
  *
1753
- * @example
1754
- * via: 1.0 fred, 1.1 example.com (Apache/1.1)
1755
- * Permanent
2000
+ * @returns {boolean} true if this instance represents an XML media type, false otherwise.
1756
2001
  */
1757
- VIA: "via",
2002
+ isXML() {
2003
+ return this.#subtype === "xml" && (this.#type === "text" || this.#type === "application") || this.#subtype.endsWith("+xml");
2004
+ }
1758
2005
  /**
1759
- * A general warning about possible problems with the entity body.
2006
+ * Determines if this instance is an HTML media type.
1760
2007
  *
1761
- * @example
1762
- * warning: 199 Miscellaneous warning
1763
- * Permanent
2008
+ * @returns {boolean} true if this instance represents an HTML media type, false otherwise.
1764
2009
  */
1765
- WARNING: "warning",
2010
+ isHTML() {
2011
+ return this.#subtype === "html" && this.#type === "text";
2012
+ }
1766
2013
  /**
1767
- * Indicates the authentication scheme that should be used to access the requested entity.
2014
+ * Gets the name of the class.
1768
2015
  *
1769
- * @example
1770
- * www-authenticate: Basic
1771
- * Permanent
2016
+ * @returns {string} The class name
1772
2017
  */
1773
- WWW_AUTHENTICATE: "www-authenticate",
2018
+ get [Symbol.toStringTag]() {
2019
+ return "MediaType";
2020
+ }
2021
+ };
2022
+
2023
+ // src/constants.js
2024
+ var defaultCharset = "utf-8";
2025
+ var endsWithSlashRegEx = /\/$/;
2026
+ var mediaTypes = /* @__PURE__ */ new Map([
2027
+ [http_media_type_default.PNG, new MediaType(http_media_type_default.PNG)],
2028
+ [http_media_type_default.TEXT, new MediaType(http_media_type_default.TEXT, { defaultCharset })],
2029
+ [http_media_type_default.JSON, new MediaType(http_media_type_default.JSON, { defaultCharset })],
2030
+ [http_media_type_default.HTML, new MediaType(http_media_type_default.HTML, { defaultCharset })],
2031
+ [http_media_type_default.JAVA_SCRIPT, new MediaType(http_media_type_default.JAVA_SCRIPT, { defaultCharset })],
2032
+ [http_media_type_default.CSS, new MediaType(http_media_type_default.CSS, { defaultCharset })],
2033
+ [http_media_type_default.XML, new MediaType(http_media_type_default.XML, { defaultCharset })],
2034
+ [http_media_type_default.BIN, new MediaType(http_media_type_default.BIN)]
2035
+ ]);
2036
+ var RequestEvents = Object.freeze({
2037
+ CONFIGURED: "configured",
2038
+ SUCCESS: "success",
2039
+ ERROR: "error",
2040
+ ABORTED: "aborted",
2041
+ TIMEOUT: "timeout",
2042
+ COMPLETE: "complete",
2043
+ ALL_COMPLETE: "all-complete"
2044
+ });
2045
+ var SignalEvents = Object.freeze({
2046
+ ABORT: "abort",
2047
+ TIMEOUT: "timeout"
2048
+ });
2049
+ var _abortEvent = new CustomEvent(SignalEvents.ABORT, { detail: { cause: new DOMException("The request was aborted", "AbortError") } });
2050
+ var requestBodyMethods = [http_request_methods_default.POST, http_request_methods_default.PUT, http_request_methods_default.PATCH];
2051
+
2052
+ // src/abort-signal.js
2053
+ var NativeAbortSignal = globalThis.AbortSignal;
2054
+ var AbortSignal = class _AbortSignal extends EventTarget {
2055
+ /** @type {AbortController} */
2056
+ #abortController;
2057
+ /** @type {number} */
2058
+ #timeoutId;
1774
2059
  /**
1775
- * Cross-site scripting (XSS) filter
1776
- *
1777
- * @example
1778
- * x-xss-protection: 1, mode=block
2060
+ * @param {AbortSignal} signal The signal to listen to
1779
2061
  */
1780
- X_XSS_PROTECTION: "x-xss-protection",
2062
+ constructor(signal) {
2063
+ super();
2064
+ this.#abortController = new AbortController();
2065
+ signal?.addEventListener(SignalEvents.ABORT, (event) => this.#abort(event));
2066
+ }
1781
2067
  /**
1782
- * The HTTP Content-Security-Policy response header allows web site administrators to control resources the user agent is allowed
1783
- * to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints.
1784
- * This helps guard against cross-site scripting attacks (Cross-site_scripting).
2068
+ * Returns an {@link AbortSignal} instance that is already set as aborted.
1785
2069
  *
1786
- * @example
1787
- * content-security-policy: default-src
2070
+ * @static
2071
+ * @returns {AbortSignal} The abort signal
1788
2072
  */
1789
- CONTENT_SECURITY_POLICY: "content-security-policy",
2073
+ static abort() {
2074
+ return NativeAbortSignal.abort();
2075
+ }
1790
2076
  /**
1791
- * The only defined value, "nosniff", prevents Internet Explorer from MIME-sniffing a response away from the declared content-type. This also applies to Google Chrome, when downloading extensions.
2077
+ * Returns an AbortSignal instance that will automatically abort after a specified time.
1792
2078
  *
1793
- * @example
1794
- * x-content-type-options: nosniff
2079
+ * @static
2080
+ * @param {number} time The time in milliseconds to wait before aborting
2081
+ * @returns {AbortSignal} The abort signal
1795
2082
  */
1796
- X_CONTENT_TYPE_OPTIONS: "x-content-type-options",
2083
+ static timeout(time) {
2084
+ return NativeAbortSignal.timeout(time);
2085
+ }
1797
2086
  /**
1798
- * specifies the technology (e.g. ASP.NET, PHP, JBoss) supporting the web application (version details are often in X-Runtime, X-Version, or X-AspNet-Version)
2087
+ * The aborted property is a Boolean that indicates whether the request has been aborted (true) or not (false).
1799
2088
  *
1800
- * @example
1801
- * x-powered-by: PHP/5.4.0
1802
- */
1803
- X_POWERED_BY: "x-powered-by"
1804
- };
1805
- var http_response_headers_default = HttpResponseHeader;
1806
-
1807
- // src/response-status.js
1808
- var ResponseStatus = class {
1809
- /** @type {number} */
1810
- #code;
1811
- /** @type {string} */
1812
- #text;
2089
+ * @returns {boolean} Whether the signal was aborted or not
2090
+ */
2091
+ get aborted() {
2092
+ return this.#abortController.signal.aborted;
2093
+ }
1813
2094
  /**
2095
+ * The reason property returns a DOMException object indicating the reason the operation was aborted, or null if the operation is not aborted.
1814
2096
  *
1815
- * @param {number} code The status code from the {@link Response}
1816
- * @param {string} text The status text from the {@link Response}
2097
+ * @returns {DOMException} The reason the signal was aborted
1817
2098
  */
1818
- constructor(code, text) {
1819
- this.#code = code;
1820
- this.#text = text;
2099
+ get reason() {
2100
+ return this.#abortController.signal.reason;
1821
2101
  }
1822
2102
  /**
1823
- * Returns the status code from the {@link Response}
2103
+ * throws the signal's abort reason if the signal has been aborted; otherwise it does nothing.
1824
2104
  *
1825
- * @returns {number} The status code.
2105
+ * @returns {void}
1826
2106
  */
1827
- get code() {
1828
- return this.#code;
2107
+ throwIfAborted() {
2108
+ this.#abortController.signal.throwIfAborted();
1829
2109
  }
1830
2110
  /**
1831
- * Returns the status text from the {@link Response}.
2111
+ * Returns an AbortSignal instance that will be aborted when the provided amount of milliseconds have passed.
2112
+ * A value of -1 (which is the default) means there is no timeout.
2113
+ * Note: You can't set this property to a value less than 0.
1832
2114
  *
1833
- * @returns {string} The status text.
2115
+ * @param {number} timeout The timeout in milliseconds
2116
+ * @returns {AbortSignal} The abort signal
1834
2117
  */
1835
- get text() {
1836
- return this.#text;
2118
+ withTimeout(timeout) {
2119
+ if (timeout < 0) {
2120
+ throw new RangeError("The timeout cannot be negative");
2121
+ }
2122
+ this.#timeoutId ??= setTimeout(() => this.#abort(_AbortSignal.#generateTimeoutEvent(timeout), true), timeout);
2123
+ return this.#abortController.signal;
1837
2124
  }
1838
2125
  /**
1839
- * A String value that is used in the creation of the default string
1840
- * description of an object. Called by the built-in method {@link Object.prototype.toString}.
2126
+ * Clears the timeout.
2127
+ * Note: This does not abort the signal, dispatch the timeout event, or reset the timeout.
1841
2128
  *
1842
- * @override
1843
- * @returns {string} The default string description of this object.
2129
+ * @returns {void}
1844
2130
  */
1845
- get [Symbol.toStringTag]() {
1846
- return "ResponseStatus";
2131
+ clearTimeout() {
2132
+ clearTimeout(this.#timeoutId);
1847
2133
  }
1848
2134
  /**
1849
- * tostring method for the class.
2135
+ * Adds an event listener for the specified event type.
1850
2136
  *
1851
2137
  * @override
1852
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString|Object.prototype.toString}
1853
- * @returns {string} The status code and status text.
2138
+ * @param {string} eventName The name of the event to listen to
2139
+ * @param {EventListener} listener The listener to add
2140
+ * @returns {void}
1854
2141
  */
1855
- toString() {
1856
- return `${this.#code} ${this.#text}`;
2142
+ addEventListener(eventName, listener) {
2143
+ this.#abortController.signal.addEventListener(eventName, listener);
1857
2144
  }
1858
- };
1859
-
1860
- // src/signal-controller.js
1861
- var SignalController = class {
1862
- /** @type {AbortController} */
1863
- #abortController;
1864
2145
  /**
1865
- * @param {AbortSignal} [signal] The signal to be used to abort the request.
2146
+ * Dispatches an event to this EventTarget.
2147
+ *
2148
+ * @override
2149
+ * @param {Event} event The event to dispatch
2150
+ * @returns {boolean} Whether the event was dispatched or not
1866
2151
  */
1867
- constructor(signal) {
1868
- this.#abortController = new AbortController();
1869
- signal?.addEventListener("abort", () => this.#abortController.abort());
2152
+ dispatchEvent(event) {
2153
+ return this.#abortController.signal.dispatchEvent(event);
1870
2154
  }
1871
2155
  /**
1872
- * Returns the {@link AbortSignal} object associated with this object.
2156
+ * Removes an event listener for the specified event type.
1873
2157
  *
1874
- * @returns {AbortSignal} The {@link AbortSignal} object associated with this object.
2158
+ * @override
2159
+ * @param {string} eventName The name of the event to listen to
2160
+ * @param {EventListener} listener The listener to remove
2161
+ * @returns {void}
1875
2162
  */
1876
- get signal() {
1877
- return this.#abortController.signal;
2163
+ removeEventListener(eventName, listener) {
2164
+ this.#abortController.signal.removeEventListener(eventName, listener);
1878
2165
  }
1879
2166
  /**
1880
- * Aborts a DOM request before it has completed.
1881
- * This is able to abort fetch requests, data sent using the XMLHttpRequest API, and Web Sockets.
2167
+ * Aborts the signal. This is so naughty. ¯\_(ツ)_/¯
1882
2168
  *
1883
- * @param {DOMException} [reason] The reason for aborting the request.
2169
+ * @param {Event} event The event to abort with
1884
2170
  * @returns {void}
1885
2171
  */
1886
- abort(reason) {
1887
- this.#abortController.abort(reason);
2172
+ abort(event) {
2173
+ this.#abort(event);
1888
2174
  }
1889
2175
  /**
1890
- * A String value that is used in the creation of the default string
1891
- * description of an object. Called by the built-in method {@link Object.prototype.toString}.
2176
+ * Aborts the signal.
1892
2177
  *
1893
- * @override
1894
- * @returns {string} The default string description of this object.
2178
+ * @private
2179
+ * @param {Event} event The event to abort with
2180
+ * @param {boolean} [dispatchEvent = false] Whether to dispatch the event or not
2181
+ * @returns {void}
2182
+ * @fires SignalEvents.ABORT When the signal is aborted
2183
+ * @fires SignalEvents.TIMEOUT When the signal times out
2184
+ */
2185
+ #abort(event = _abortEvent, dispatchEvent = false) {
2186
+ clearTimeout(this.#timeoutId);
2187
+ this.#abortController.abort(event.detail?.cause);
2188
+ if (dispatchEvent) {
2189
+ this.#abortController.signal.dispatchEvent(event);
2190
+ }
2191
+ }
2192
+ /**
2193
+ * Generates a timeout event.
2194
+ *
2195
+ * @private
2196
+ * @static
2197
+ * @param {number} timeout The timeout in milliseconds
2198
+ * @returns {CustomEvent} The timeout event
1895
2199
  */
1896
- get [Symbol.toStringTag]() {
1897
- return "SignalController";
2200
+ static #generateTimeoutEvent(timeout) {
2201
+ return new CustomEvent(SignalEvents.TIMEOUT, { detail: { timeout, cause: new DOMException(`The request timed-out after ${timeout / 1e3} seconds`, "TimeoutError") } });
2202
+ }
2203
+ };
2204
+
2205
+ // node_modules/@d1g1tal/chrysalis/src/esm/object-type.js
2206
+ var _type = (object) => object?.constructor ?? object?.prototype?.constructor ?? globalThis[Object.prototype.toString.call(object).slice(8, -1)] ?? object;
2207
+ var object_type_default = _type;
2208
+
2209
+ // node_modules/@d1g1tal/chrysalis/src/esm/object-merge.js
2210
+ var _objectMerge = (...objects) => {
2211
+ const target = {};
2212
+ for (const source of objects) {
2213
+ if (object_type_default(source) != Object)
2214
+ return void 0;
2215
+ let descriptor, sourceType;
2216
+ for (const property of Object.getOwnPropertyNames(source)) {
2217
+ descriptor = Object.getOwnPropertyDescriptor(source, property);
2218
+ if (descriptor.enumerable) {
2219
+ sourceType = object_type_default(source[property]);
2220
+ if (sourceType == Object) {
2221
+ descriptor.value = object_type_default(target[property]) == Object ? _objectMerge(target[property], source[property]) : { ...source[property] };
2222
+ } else if (sourceType == Array) {
2223
+ descriptor.value = Array.isArray(target[property]) ? [.../* @__PURE__ */ new Set([...source[property], ...target[property]])] : [...source[property]];
2224
+ }
2225
+ target[property] = descriptor.value;
2226
+ }
2227
+ }
1898
2228
  }
2229
+ return target;
1899
2230
  };
2231
+ var object_merge_default = _objectMerge;
1900
2232
 
1901
2233
  // src/transportr.js
1902
- var endsWithSlashRegEx = /\/$/;
1903
- var charset = "utf-8";
1904
- var _mediaTypes = /* @__PURE__ */ new Map([
1905
- [http_media_type_default.PNG, new MediaType(http_media_type_default.PNG)],
1906
- [http_media_type_default.TEXT, new MediaType(http_media_type_default.TEXT, { charset })],
1907
- [http_media_type_default.JSON, new MediaType(http_media_type_default.JSON, { charset })],
1908
- [http_media_type_default.HTML, new MediaType(http_media_type_default.HTML, { charset })],
1909
- [http_media_type_default.JAVA_SCRIPT, new MediaType(http_media_type_default.JAVA_SCRIPT, { charset })],
1910
- [http_media_type_default.CSS, new MediaType(http_media_type_default.CSS, { charset })],
1911
- [http_media_type_default.XML, new MediaType(http_media_type_default.XML, { charset })],
1912
- [http_media_type_default.BIN, new MediaType(http_media_type_default.BIN)]
1913
- ]);
1914
2234
  var _handleText = async (response) => await response.text();
1915
2235
  var _handleScript = async (response) => {
1916
2236
  const objectURL = URL.createObjectURL(await response.blob());
@@ -1932,7 +2252,6 @@ var Transportr = (() => {
1932
2252
  var _handleXml = async (response) => new DOMParser().parseFromString(await response.text(), http_media_type_default.XML);
1933
2253
  var _handleHtml = async (response) => new DOMParser().parseFromString(await response.text(), http_media_type_default.HTML);
1934
2254
  var _handleHtmlFragment = async (response) => document.createRange().createContextualFragment(await response.text());
1935
- var _typeConverter = (data) => Object.fromEntries(Array.from(data.keys()).map((key, index, keys, value = data.getAll(key)) => [key, value.length > 1 ? value : value[0]]));
1936
2255
  var Transportr = class _Transportr {
1937
2256
  /** @type {URL} */
1938
2257
  #baseUrl;
@@ -1942,32 +2261,18 @@ var Transportr = (() => {
1942
2261
  #subscribr;
1943
2262
  /** @type {Subscribr} */
1944
2263
  static #globalSubscribr = new Subscribr();
1945
- /** @type {Array<SignalController>} */
1946
- static #activeRequests = [];
1947
- /**
1948
- * @private
1949
- * @static
1950
- * @type {SetMultiMap<ResponseHandler<ResponseBody>, string>}
1951
- */
2264
+ /** @type {Set<AbortSignal>} */
2265
+ static #activeRequests = /* @__PURE__ */ new Set();
2266
+ /** @type {SetMultiMap<ResponseHandler<ResponseBody>, string>} */
1952
2267
  static #contentTypeHandlers = new set_multi_map_default([
1953
- [_handleImage, _mediaTypes.get(http_media_type_default.PNG).type],
1954
- [_handleText, _mediaTypes.get(http_media_type_default.TEXT).type],
1955
- [_handleJson, _mediaTypes.get(http_media_type_default.JSON).subtype],
1956
- [_handleHtml, _mediaTypes.get(http_media_type_default.HTML).subtype],
1957
- [_handleScript, _mediaTypes.get(http_media_type_default.JAVA_SCRIPT).subtype],
1958
- [_handleCss, _mediaTypes.get(http_media_type_default.CSS).subtype],
1959
- [_handleXml, _mediaTypes.get(http_media_type_default.XML).subtype],
1960
- [_handleReadableStream, _mediaTypes.get(http_media_type_default.BIN).subtype]
1961
- ]);
1962
- /**
1963
- * @private
1964
- * @static
1965
- * @type {Set<PropertyTypeConverter>}
1966
- */
1967
- static #propertyTypeConverters = /* @__PURE__ */ new Set([
1968
- [{ property: "body", type: FormData, converter: _typeConverter }],
1969
- [{ property: "searchParams", type: URLSearchParams, converter: _typeConverter }],
1970
- [{ property: "headers", type: Headers, converter: Object.fromEntries }]
2268
+ [_handleImage, mediaTypes.get(http_media_type_default.PNG).type],
2269
+ [_handleText, mediaTypes.get(http_media_type_default.TEXT).type],
2270
+ [_handleJson, mediaTypes.get(http_media_type_default.JSON).subtype],
2271
+ [_handleHtml, mediaTypes.get(http_media_type_default.HTML).subtype],
2272
+ [_handleScript, mediaTypes.get(http_media_type_default.JAVA_SCRIPT).subtype],
2273
+ [_handleCss, mediaTypes.get(http_media_type_default.CSS).subtype],
2274
+ [_handleXml, mediaTypes.get(http_media_type_default.XML).subtype],
2275
+ [_handleReadableStream, mediaTypes.get(http_media_type_default.BIN).subtype]
1971
2276
  ]);
1972
2277
  /**
1973
2278
  * Create a new Transportr instance with the provided location or origin and context path.
@@ -1975,65 +2280,14 @@ var Transportr = (() => {
1975
2280
  * @param {URL|string|RequestOptions} [url=location.origin] The URL for {@link fetch} requests.
1976
2281
  * @param {RequestOptions} [options={}] The default {@link RequestOptions} for this instance.
1977
2282
  */
1978
- constructor(url = location.origin, options = {}) {
1979
- const type = object_type_default(url);
1980
- if (type == Object) {
1981
- options = url;
1982
- url = location.origin;
1983
- } else if (type != URL) {
1984
- url = url.startsWith("/") ? new URL(url, location.origin) : new URL(url);
2283
+ constructor(url = globalThis.location.origin, options = {}) {
2284
+ if (object_type_default(url) == Object) {
2285
+ [url, options] = [globalThis.location.origin, url];
1985
2286
  }
1986
- this.#baseUrl = url;
1987
- this.#options = object_merge_default(_Transportr.#defaultRequestOptions, _Transportr.#convertRequestOptions(options));
2287
+ this.#baseUrl = _Transportr.#getBaseUrl(url);
2288
+ this.#options = _Transportr.#createOptions(options, _Transportr.#defaultRequestOptions);
1988
2289
  this.#subscribr = new Subscribr();
1989
2290
  }
1990
- /**
1991
- * Returns a {@link SignalController} used for aborting requests.
1992
- *
1993
- * @static
1994
- * @param {AbortSignal} [signal] The optional {@link AbortSignal} to used for chaining.
1995
- * @returns {SignalController} A new {@link SignalController} instance.
1996
- */
1997
- static signalController(signal) {
1998
- return new SignalController(signal);
1999
- }
2000
- /**
2001
- * Returns a {@link EventRegistration} used for subscribing to global events.
2002
- *
2003
- * @static
2004
- * @param {TransportrEvent} event The event to subscribe to.
2005
- * @param {function(Event, *): void} handler The event handler.
2006
- * @param {*} context The context to bind the handler to.
2007
- * @returns {EventRegistration} A new {@link EventRegistration} instance.
2008
- */
2009
- static register(event, handler, context) {
2010
- return _Transportr.#globalSubscribr.subscribe(event, handler, context);
2011
- }
2012
- /**
2013
- * Removes a {@link EventRegistration} from the global event handler.
2014
- *
2015
- * @static
2016
- * @param {EventRegistration} eventRegistration The {@link EventRegistration} to remove.
2017
- * @returns {boolean} True if the {@link EventRegistration} was removed, false otherwise.
2018
- */
2019
- static unregister(eventRegistration) {
2020
- return _Transportr.#globalSubscribr.unsubscribe(eventRegistration);
2021
- }
2022
- /**
2023
- * Aborts all active requests.
2024
- * This is useful for when the user navigates away from the current page.
2025
- * This will also clear the {@link Transportr#activeRequests} array.
2026
- * This is called automatically when the {@link Transportr#abort} method is called.
2027
- *
2028
- * @static
2029
- * @returns {void}
2030
- */
2031
- static abortAll() {
2032
- for (const signalController of this.#activeRequests) {
2033
- signalController.abort();
2034
- }
2035
- this.#activeRequests = [];
2036
- }
2037
2291
  /**
2038
2292
  * @static
2039
2293
  * @constant {Object<string, HttpRequestMethod>}
@@ -2113,15 +2367,7 @@ var Transportr = (() => {
2113
2367
  * @static
2114
2368
  * @constant {Object<string, TransportrEvent>}
2115
2369
  */
2116
- static Events = Object.freeze({
2117
- CONFIGURED: "configured",
2118
- SUCCESS: "success",
2119
- ERROR: "error",
2120
- ABORTED: "aborted",
2121
- TIMEOUT: "timeout",
2122
- COMPLETE: "complete",
2123
- ALL_COMPLETE: "all-complete"
2124
- });
2370
+ static RequestEvents = RequestEvents;
2125
2371
  /**
2126
2372
  * @private
2127
2373
  * @static
@@ -2131,7 +2377,7 @@ var Transportr = (() => {
2131
2377
  body: null,
2132
2378
  cache: _Transportr.CachingPolicy.NO_STORE,
2133
2379
  credentials: _Transportr.CredentialsPolicy.SAME_ORIGIN,
2134
- headers: { [http_request_headers_default.CONTENT_TYPE]: _mediaTypes.get(http_media_type_default.JSON).toString(), [http_request_headers_default.ACCEPT]: _mediaTypes.get(http_media_type_default.JSON).toString() },
2380
+ headers: { [http_request_headers_default.CONTENT_TYPE]: mediaTypes.get(http_media_type_default.JSON).toString(), [http_request_headers_default.ACCEPT]: mediaTypes.get(http_media_type_default.JSON).toString() },
2135
2381
  searchParams: {},
2136
2382
  integrity: void 0,
2137
2383
  keepalive: void 0,
@@ -2151,9 +2397,54 @@ var Transportr = (() => {
2151
2397
  * @type {Map<TransportrEvent, ResponseStatus>}
2152
2398
  */
2153
2399
  static #eventResponseStatuses = /* @__PURE__ */ new Map([
2154
- [_Transportr.Events.ABORTED, new ResponseStatus(499, "Aborted")],
2155
- [_Transportr.Events.TIMEOUT, new ResponseStatus(504, "Gateway Timeout")]
2400
+ [RequestEvents.ABORTED, new ResponseStatus(499, "Aborted")],
2401
+ [RequestEvents.TIMEOUT, new ResponseStatus(504, "Gateway Timeout")]
2156
2402
  ]);
2403
+ /**
2404
+ * Returns a {@link AbortSignal} used for aborting requests.
2405
+ *
2406
+ * @static
2407
+ * @returns {AbortSignal} A new {@link AbortSignal} instance.
2408
+ */
2409
+ static abortSignal() {
2410
+ return new AbortSignal();
2411
+ }
2412
+ /**
2413
+ * Returns a {@link EventRegistration} used for subscribing to global events.
2414
+ *
2415
+ * @static
2416
+ * @param {TransportrEvent} event The event to subscribe to.
2417
+ * @param {function(Event, *): void} handler The event handler.
2418
+ * @param {*} context The context to bind the handler to.
2419
+ * @returns {EventRegistration} A new {@link EventRegistration} instance.
2420
+ */
2421
+ static register(event, handler, context) {
2422
+ return _Transportr.#globalSubscribr.subscribe(event, handler, context);
2423
+ }
2424
+ /**
2425
+ * Removes a {@link EventRegistration} from the global event handler.
2426
+ *
2427
+ * @static
2428
+ * @param {EventRegistration} eventRegistration The {@link EventRegistration} to remove.
2429
+ * @returns {boolean} True if the {@link EventRegistration} was removed, false otherwise.
2430
+ */
2431
+ static unregister(eventRegistration) {
2432
+ return _Transportr.#globalSubscribr.unsubscribe(eventRegistration);
2433
+ }
2434
+ /**
2435
+ * Aborts all active requests.
2436
+ * This is useful for when the user navigates away from the current page.
2437
+ * This will also clear the {@link Transportr#activeRequests} set.
2438
+ *
2439
+ * @static
2440
+ * @returns {void}
2441
+ */
2442
+ static abortAll() {
2443
+ for (const abortSignal of this.#activeRequests) {
2444
+ abortSignal.abort(_abortEvent);
2445
+ }
2446
+ this.#activeRequests.clear();
2447
+ }
2157
2448
  /**
2158
2449
  * It returns the base {@link URL} for the API.
2159
2450
  *
@@ -2252,26 +2543,27 @@ var Transportr = (() => {
2252
2543
  return this.#request(path, options, { method: http_request_methods_default.HEAD });
2253
2544
  }
2254
2545
  /**
2255
- * It takes a path and options, and returns a request with the method set to OPTIONS.
2546
+ * It returns a promise that resolves to the allowed request methods for the given resource path.
2256
2547
  *
2257
2548
  * @async
2258
2549
  * @param {string} [path] The path to the resource.
2259
2550
  * @param {RequestOptions} [options] The options for the request.
2260
- * @returns {Promise<ResponseBody>} The return value of the #request method.
2551
+ * @returns {Promise<string[]>} A promise that resolves to an array of allowed request methods for this resource.
2261
2552
  */
2262
2553
  async options(path, options) {
2263
- return this.#request(path, options, { method: http_request_methods_default.OPTIONS });
2554
+ const response = await this.#request(path, options, { method: http_request_methods_default.OPTIONS });
2555
+ return response.headers.get("allow").split(",").map((method) => method.trim());
2264
2556
  }
2265
2557
  /**
2266
2558
  * It takes a path and options, and makes a request to the server.
2267
2559
  *
2268
2560
  * @async
2269
2561
  * @param {string} [path] The path to the endpoint you want to hit.
2270
- * @param {RequestOptions} [options] The options for the request.
2562
+ * @param {RequestOptions} [userOptions] The options for the request.
2271
2563
  * @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.
2272
2564
  */
2273
- async request(path, options) {
2274
- return this.#request(path, options);
2565
+ async request(path, userOptions) {
2566
+ return this.#request(path, userOptions, {}, (response) => response);
2275
2567
  }
2276
2568
  /**
2277
2569
  * It gets a JSON resource from the server.
@@ -2282,7 +2574,7 @@ var Transportr = (() => {
2282
2574
  * @returns {Promise<JsonObject>} A promise that resolves to the response body as a JSON object.
2283
2575
  */
2284
2576
  async getJson(path, options) {
2285
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: _mediaTypes.get(http_media_type_default.JSON).toString() } }, _handleJson);
2577
+ return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: mediaTypes.get(http_media_type_default.JSON).toString() } }, _handleJson);
2286
2578
  }
2287
2579
  /**
2288
2580
  * It gets the XML representation of the resource at the given path.
@@ -2293,7 +2585,7 @@ var Transportr = (() => {
2293
2585
  * @returns {Promise<Document>} The result of the function call to #get.
2294
2586
  */
2295
2587
  async getXml(path, options) {
2296
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: _mediaTypes.get(http_media_type_default.XML).toString() } }, _handleXml);
2588
+ return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: mediaTypes.get(http_media_type_default.XML).toString() } }, _handleXml);
2297
2589
  }
2298
2590
  /**
2299
2591
  * Get the HTML content of the specified path.
@@ -2306,7 +2598,7 @@ var Transportr = (() => {
2306
2598
  * method of the promise returned by the `#get` method.
2307
2599
  */
2308
2600
  async getHtml(path, options) {
2309
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: _mediaTypes.get(http_media_type_default.HTML).toString() } }, _handleHtml);
2601
+ return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: mediaTypes.get(http_media_type_default.HTML).toString() } }, _handleHtml);
2310
2602
  }
2311
2603
  /**
2312
2604
  * It returns a promise that resolves to the HTML fragment at the given path.
@@ -2318,7 +2610,7 @@ var Transportr = (() => {
2318
2610
  * @returns {Promise<DocumentFragment>} A promise that resolves to an HTML fragment.
2319
2611
  */
2320
2612
  async getHtmlFragment(path, options) {
2321
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: _mediaTypes.get(http_media_type_default.HTML).toString() } }, _handleHtmlFragment);
2613
+ return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: mediaTypes.get(http_media_type_default.HTML).toString() } }, _handleHtmlFragment);
2322
2614
  }
2323
2615
  /**
2324
2616
  * It gets a script from the server, and appends the script to the {@link Document} {@link HTMLHeadElement}
@@ -2330,7 +2622,7 @@ var Transportr = (() => {
2330
2622
  * @returns {Promise<void>} A promise that has been resolved.
2331
2623
  */
2332
2624
  async getScript(path, options) {
2333
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: _mediaTypes.get(http_media_type_default.JAVA_SCRIPT).toString() } }, _handleScript);
2625
+ return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: mediaTypes.get(http_media_type_default.JAVA_SCRIPT).toString() } }, _handleScript);
2334
2626
  }
2335
2627
  /**
2336
2628
  * Gets a stylesheet from the server, and adds it as a {@link Blob} {@link URL}.
@@ -2341,7 +2633,7 @@ var Transportr = (() => {
2341
2633
  * @returns {Promise<void>} A promise that has been resolved.
2342
2634
  */
2343
2635
  async getStylesheet(path, options) {
2344
- return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: _mediaTypes.get(http_media_type_default.CSS).toString() } }, _handleCss);
2636
+ return this.#get(path, options, { headers: { [http_request_headers_default.ACCEPT]: mediaTypes.get(http_media_type_default.CSS).toString() } }, _handleCss);
2345
2637
  }
2346
2638
  /**
2347
2639
  * It returns a blob from the specified path.
@@ -2400,8 +2692,7 @@ var Transportr = (() => {
2400
2692
  * @returns {Promise<ResponseBody>} The result of the #request method.
2401
2693
  */
2402
2694
  async #get(path, userOptions, options, responseHandler) {
2403
- delete userOptions?.method;
2404
- return this.#request(path, userOptions, options, responseHandler);
2695
+ return this.#request(path, userOptions, { ...options, method: http_request_methods_default.GET }, responseHandler);
2405
2696
  }
2406
2697
  /**
2407
2698
  * It takes a path, options, and a response handler, and returns a promise that resolves to the
@@ -2418,135 +2709,151 @@ var Transportr = (() => {
2418
2709
  */
2419
2710
  async #request(path, userOptions = {}, options = {}, responseHandler) {
2420
2711
  if (object_type_default(path) == Object) {
2421
- userOptions = path;
2422
- path = void 0;
2712
+ [path, userOptions] = [void 0, path];
2423
2713
  }
2424
- const requestOptions = object_merge_default(this.#options, _Transportr.#convertRequestOptions(userOptions), options);
2425
- const url = _Transportr.#createUrl(this.#baseUrl, path, requestOptions.searchParams);
2426
- const signalController = new SignalController(requestOptions.signal);
2427
- _Transportr.#activeRequests.push(signalController);
2428
- requestOptions.signal = signalController.signal;
2429
- if (_Transportr.#needsSerialization(requestOptions.method, requestOptions.headers[http_request_headers_default.CONTENT_TYPE])) {
2430
- try {
2431
- requestOptions.body = JSON.stringify(requestOptions.body);
2432
- } catch (error) {
2433
- return Promise.reject(new HttpError(url, { cause: error }));
2434
- }
2435
- } else if (requestOptions.method == http_request_methods_default.GET && requestOptions.headers[http_request_headers_default.CONTENT_TYPE] != "") {
2436
- delete requestOptions.headers[http_request_headers_default.CONTENT_TYPE];
2437
- delete requestOptions.body;
2714
+ try {
2715
+ options = this.#processRequestOptions(userOptions, options);
2716
+ } catch (cause) {
2717
+ return Promise.reject(new HttpError("Unable to process request options", { cause }));
2438
2718
  }
2439
- requestOptions.signal.addEventListener("abort", (event) => this.#publish(_Transportr.Events.ABORTED, requestOptions.global, event));
2440
- requestOptions.signal.addEventListener("timeout", (event) => this.#publish(_Transportr.Events.TIMEOUT, requestOptions.global, event));
2441
- this.#publish(_Transportr.Events.CONFIGURED, requestOptions.global, requestOptions);
2442
- let result, timeoutId, response;
2719
+ this.#publish({ name: RequestEvents.CONFIGURED, data: options, global: options.global });
2720
+ const url = _Transportr.#createUrl(this.#baseUrl, path);
2721
+ if (object_type_default(options.signal) != AbortSignal) {
2722
+ options.signal = new AbortSignal(options.signal);
2723
+ }
2724
+ options.signal.addEventListener(SignalEvents.ABORT, (event) => this.#publish({ name: RequestEvents.ABORTED, event, global: options.global }));
2725
+ options.signal.addEventListener(SignalEvents.TIMEOUT, (event) => this.#publish({ name: RequestEvents.TIMEOUT, event, global: options.global }));
2726
+ _Transportr.#activeRequests.add(options.signal);
2727
+ let response, result;
2443
2728
  try {
2444
- timeoutId = setTimeout(() => {
2445
- const cause = new DOMException(`The call to '${url}' timed-out after ${requestOptions.timeout / 1e3} seconds`, "TimeoutError");
2446
- signalController.abort(cause);
2447
- requestOptions.signal.dispatchEvent(new CustomEvent(_Transportr.Events.TIMEOUT, { detail: { url, options: requestOptions, cause } }));
2448
- }, requestOptions.timeout);
2449
- response = await fetch(url, requestOptions);
2729
+ response = await fetch(url, new Proxy(options, { get: _Transportr.#requestOptionsProxyHandler(options.timeout) }));
2730
+ if (!responseHandler && response.status != 204 && response.headers.has(http_response_headers_default.CONTENT_TYPE)) {
2731
+ responseHandler = this.#getResponseHandler(response.headers.get(http_response_headers_default.CONTENT_TYPE));
2732
+ }
2733
+ result = await responseHandler?.(response) ?? response;
2450
2734
  if (!response.ok) {
2451
- return Promise.reject(this.#handleError(url, { status: _Transportr.#generateResponseStatusFromError("ResponseError", response), entity: await this.#processResponse(response, url) }));
2735
+ return Promise.reject(this.#handleError(url, { status: _Transportr.#generateResponseStatusFromError("ResponseError", response), entity: result }));
2452
2736
  }
2453
- result = await this.#processResponse(response, url, responseHandler);
2454
- this.#publish(_Transportr.Events.SUCCESS, requestOptions.global, result);
2737
+ this.#publish({ name: RequestEvents.SUCCESS, data: result, global: options.global });
2455
2738
  } catch (error) {
2456
2739
  return Promise.reject(this.#handleError(url, { cause: error, status: _Transportr.#generateResponseStatusFromError(error.name, response) }));
2457
2740
  } finally {
2458
- clearTimeout(timeoutId);
2459
- if (!requestOptions.signal.aborted) {
2460
- this.#publish(_Transportr.Events.COMPLETE, requestOptions.global, response);
2461
- const index = _Transportr.#activeRequests.indexOf(signalController);
2462
- if (index > -1) {
2463
- _Transportr.#activeRequests.splice(index, 1);
2464
- }
2465
- if (_Transportr.#activeRequests.length === 0) {
2466
- this.#publish(_Transportr.Events.ALL_COMPLETE, requestOptions.global, response);
2741
+ options.signal.clearTimeout();
2742
+ if (!options.signal.aborted) {
2743
+ this.#publish({ name: RequestEvents.COMPLETE, data: response, global: options.global });
2744
+ _Transportr.#activeRequests.delete(options.signal);
2745
+ if (_Transportr.#activeRequests.size === 0) {
2746
+ this.#publish({ name: RequestEvents.ALL_COMPLETE, global: options.global });
2467
2747
  }
2468
2748
  }
2469
2749
  }
2470
2750
  return result;
2471
2751
  }
2472
2752
  /**
2473
- * Handles an error by logging it and throwing it.
2753
+ * Creates the options for a {@link Transportr} instance.
2474
2754
  *
2475
2755
  * @private
2476
- * @param {URL} url The path to the resource you want to access.
2477
- * @param {import('./http-error.js').HttpErrorOptions} options The options for the HttpError.
2478
- * @returns {HttpError} The HttpError.
2756
+ * @static
2757
+ * @param {RequestOptions} userOptions The {@link RequestOptions} to convert.
2758
+ * @param {RequestOptions} options The default {@link RequestOptions}.
2759
+ * @returns {RequestOptions} The converted {@link RequestOptions}.
2479
2760
  */
2480
- #handleError(url, options) {
2481
- const error = new HttpError(`An error has occurred with your request to: '${url}'`, options);
2482
- this.#publish(_Transportr.Events.ERROR, true, error);
2483
- return error;
2761
+ static #createOptions({ body, headers: userHeaders, searchParams: userSearchParams, ...userOptions }, { headers, searchParams, ...options }) {
2762
+ return object_merge_default(options, userOptions, {
2763
+ body: [FormData, URLSearchParams, Object].includes(object_type_default(body)) ? new ParameterMap(body) : body,
2764
+ headers: _Transportr.#mergeOptions(new Headers(), userHeaders, headers),
2765
+ searchParams: _Transportr.#mergeOptions(new URLSearchParams(), userSearchParams, searchParams)
2766
+ });
2484
2767
  }
2485
2768
  /**
2486
- * Publishes an event to the global and instance subscribers.
2769
+ * Merge the user options and request options into the target.
2487
2770
  *
2488
2771
  * @private
2489
- * @param {string} eventName The name of the event.
2490
- * @param {boolean} global Whether or not to publish the event to the global subscribers.
2491
- * @param {Event} [event] The event object.
2492
- * @param {*} [data] The data to pass to the subscribers.
2493
- * @returns {void}
2494
- */
2495
- #publish(eventName, global, event, data) {
2496
- if (global) {
2497
- _Transportr.#globalSubscribr.publish(eventName, event, data);
2772
+ * @static
2773
+ * @param {Headers|URLSearchParams|FormData} target The target to merge the options into.
2774
+ * @param {Headers|URLSearchParams|FormData|Object} userOption The user options to merge into the target.
2775
+ * @param {Headers|URLSearchParams|FormData|Object} requestOption The request options to merge into the target.
2776
+ * @returns {Headers|URLSearchParams} The target.
2777
+ */
2778
+ static #mergeOptions(target, userOption = {}, requestOption = {}) {
2779
+ for (const option of [userOption, requestOption]) {
2780
+ for (const [name, value] of option.entries?.() ?? Object.entries(option)) {
2781
+ target.set(name, value);
2782
+ }
2498
2783
  }
2499
- this.#subscribr.publish(eventName, event, data);
2784
+ return target;
2500
2785
  }
2501
2786
  /**
2502
- * Generates a ResponseStatus object based on the error name and the response.
2787
+ * Merges the user options and request options with the instance options into a new object that is used for the request.
2503
2788
  *
2504
2789
  * @private
2505
- * @static
2506
- * @param {string} errorName The name of the error.
2507
- * @param {Response} response The response object returned by the fetch API.
2508
- * @returns {ResponseStatus} The response status object.
2509
- */
2510
- static #generateResponseStatusFromError(errorName, response) {
2511
- switch (errorName) {
2512
- case "AbortError":
2513
- return _Transportr.#eventResponseStatuses.get(_Transportr.Events.ABORTED);
2514
- case "TimeoutError":
2515
- return _Transportr.#eventResponseStatuses.get(_Transportr.Events.TIMEOUT);
2516
- default:
2517
- return response ? new ResponseStatus(response.status, response.statusText) : new ResponseStatus(500, "Internal Server Error");
2790
+ * @param {RequestOptions} userOptions The user options to merge into the request options.
2791
+ * @param {RequestOptions} options The request options to merge into the user options.
2792
+ * @returns {RequestOptions} The merged options.
2793
+ */
2794
+ #processRequestOptions({ body: userBody, headers: userHeaders, searchParams: userSearchParams, ...userOptions }, { headers, searchParams, ...options }) {
2795
+ const requestOptions = object_merge_default(this.#options, userOptions, options, {
2796
+ headers: _Transportr.#mergeOptions(new Headers(this.#options.headers), userHeaders, headers),
2797
+ searchParams: _Transportr.#mergeOptions(new URLSearchParams(this.#options.searchParams), userSearchParams, searchParams)
2798
+ });
2799
+ if (requestBodyMethods.includes(requestOptions.method)) {
2800
+ if ([ParameterMap, FormData, URLSearchParams, Object].includes(object_type_default(userBody))) {
2801
+ const contentType = requestOptions.headers.get(http_request_headers_default.CONTENT_TYPE);
2802
+ const mediaType = (mediaTypes.get(contentType) ?? MediaType.parse(contentType))?.subtype;
2803
+ if (mediaType == http_media_type_default.MULTIPART_FORM_DATA) {
2804
+ requestOptions.body = _Transportr.#mergeOptions(new FormData(requestOptions.body), userBody);
2805
+ } else if (mediaType == http_media_type_default.FORM) {
2806
+ requestOptions.body = _Transportr.#mergeOptions(new URLSearchParams(requestOptions.body), userBody);
2807
+ } else if (mediaType.includes("json")) {
2808
+ requestOptions.body = JSON.stringify(_Transportr.#mergeOptions(new ParameterMap(requestOptions.body), userBody));
2809
+ } else {
2810
+ requestOptions.body = _Transportr.#mergeOptions(new ParameterMap(requestOptions.body), userBody);
2811
+ }
2812
+ } else {
2813
+ requestOptions.body = userBody;
2814
+ }
2815
+ } else {
2816
+ requestOptions.headers.delete(http_request_headers_default.CONTENT_TYPE);
2817
+ if (!requestOptions.body?.isEmpty()) {
2818
+ _Transportr.#mergeOptions(requestOptions.searchParams, requestOptions.body);
2819
+ }
2820
+ requestOptions.body = void 0;
2518
2821
  }
2822
+ return requestOptions;
2519
2823
  }
2520
2824
  /**
2521
- * It takes a response and a handler, and if the handler is not defined, it tries to find a handler
2522
- * based on the response's content type
2825
+ * Returns a Proxy of the options object that traps for 'signal' access.
2826
+ * If the signal has not been aborted, then it will return a new signal with a timeout.
2523
2827
  *
2524
2828
  * @private
2525
2829
  * @static
2526
- * @async
2527
- * @param {Response} response The response object returned by the fetch API.
2528
- * @param {URL} url The path to the resource you want to access. Used for error handling.
2529
- * @param {ResponseHandler<ResponseBody>} [handler] The handler to use for processing the response.
2530
- * @returns {Promise<ResponseBody>} The response is being returned.
2830
+ * @param {number} timeout The timeout in milliseconds before the signal is aborted.
2831
+ * @returns {Proxy<RequestOptions>} A proxy for the options object.
2531
2832
  */
2532
- async #processResponse(response, url, handler) {
2533
- try {
2534
- let mediaType;
2535
- if (!handler) {
2536
- mediaType = MediaType.parse(response.headers.get(http_response_headers_default.CONTENT_TYPE));
2537
- if (mediaType) {
2538
- for (const [responseHandler, contentTypes] of _Transportr.#contentTypeHandlers) {
2539
- if (contentTypes.has(mediaType.type) || contentTypes.has(mediaType.subtype)) {
2540
- handler = responseHandler;
2541
- break;
2542
- }
2543
- }
2544
- }
2545
- }
2546
- return (handler ?? _handleText)(response);
2547
- } catch (error) {
2548
- console.error("Unable to process response.", error, response);
2549
- return Promise.reject(this.#handleError(url, { cause: error }));
2833
+ static #requestOptionsProxyHandler(timeout) {
2834
+ return (target, property) => {
2835
+ const value = Reflect.get(target, property);
2836
+ return property == "signal" && !value.aborted ? value.withTimeout(timeout) : value;
2837
+ };
2838
+ }
2839
+ /**
2840
+ * It takes a url or a string, and returns a {@link URL} instance.
2841
+ * If the url is a string and starts with a slash, then the origin of the current page is used as the base url.
2842
+ *
2843
+ * @private
2844
+ * @static
2845
+ * @param {URL|string} url The URL to convert to a {@link URL} instance.
2846
+ * @returns {URL} A {@link URL} instance.
2847
+ * @throws {TypeError} If the url is not a string or {@link URL} instance.
2848
+ */
2849
+ static #getBaseUrl(url) {
2850
+ switch (object_type_default(url)) {
2851
+ case URL:
2852
+ return url;
2853
+ case String:
2854
+ return new URL(url, url.startsWith("/") ? globalThis.location.origin : void 0);
2855
+ default:
2856
+ throw new TypeError("Invalid URL");
2550
2857
  }
2551
2858
  }
2552
2859
  /**
@@ -2556,48 +2863,81 @@ var Transportr = (() => {
2556
2863
  * @private
2557
2864
  * @static
2558
2865
  * @param {URL} url The URL to use as a base.
2559
- * @param {string} [path] The path to the resource. This can be a relative path or a full URL.
2560
- * @param {Object<string, string>} [searchParams={}] An object containing the query parameters to be added to the URL.
2866
+ * @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.
2561
2867
  * @returns {URL} A new URL object with the pathname and origin of the url parameter, and the path parameter
2562
2868
  * appended to the end of the pathname.
2563
2869
  */
2564
- static #createUrl(url, path, searchParams = {}) {
2565
- let _url;
2566
- if (path) {
2567
- _url = path.startsWith("/") ? new URL(`${url.pathname.replace(endsWithSlashRegEx, "")}${path}`, url.origin) : new URL(path);
2568
- } else {
2569
- _url = new URL(url);
2570
- }
2571
- Object.entries(searchParams).forEach(([key, value]) => _url.searchParams.append(key, value));
2572
- return _url;
2870
+ static #createUrl(url, path) {
2871
+ return path ? new URL(`${url.pathname.replace(endsWithSlashRegEx, "")}${path}`, url.origin) : new URL(url);
2573
2872
  }
2574
2873
  /**
2575
- * If the request method is POST, PUT, or PATCH, and the content type is JSON, then the request body
2576
- * needs to be serialized.
2874
+ * Generates a ResponseStatus object based on the error name and the response.
2577
2875
  *
2578
2876
  * @private
2579
2877
  * @static
2580
- * @param {RequestMethod} method The HTTP request method.
2581
- * @param {HttpMediaType} contentType The headers of the request.
2582
- * @returns {boolean} `true` if the request body needs to be serialized, `false` otherwise.
2878
+ * @param {string} errorName The name of the error.
2879
+ * @param {Response} response The response object returned by the fetch API.
2880
+ * @returns {ResponseStatus} The response status object.
2881
+ */
2882
+ static #generateResponseStatusFromError(errorName, response) {
2883
+ switch (errorName) {
2884
+ case "AbortError":
2885
+ return _Transportr.#eventResponseStatuses.get(RequestEvents.ABORTED);
2886
+ case "TimeoutError":
2887
+ return _Transportr.#eventResponseStatuses.get(RequestEvents.TIMEOUT);
2888
+ default:
2889
+ return response ? new ResponseStatus(response.status, response.statusText) : new ResponseStatus(500, "Internal Server Error");
2890
+ }
2891
+ }
2892
+ /**
2893
+ * Handles an error by logging it and throwing it.
2894
+ *
2895
+ * @private
2896
+ * @param {URL} url The path to the resource you want to access.
2897
+ * @param {import('./http-error.js').HttpErrorOptions} options The options for the HttpError.
2898
+ * @returns {HttpError} The HttpError.
2583
2899
  */
2584
- static #needsSerialization(method, contentType) {
2585
- return (_mediaTypes.get(contentType) ?? new MediaType(contentType)).essence == http_media_type_default.JSON && [http_request_methods_default.POST, http_request_methods_default.PUT, http_request_methods_default.PATCH].includes(method);
2900
+ #handleError(url, options) {
2901
+ const error = new HttpError(`An error has occurred with your request to: '${url}'`, options);
2902
+ this.#publish({ name: RequestEvents.ERROR, data: error });
2903
+ return error;
2586
2904
  }
2587
2905
  /**
2906
+ * Publishes an event to the global and instance subscribers.
2588
2907
  *
2589
- * @param {RequestOptions} options The options passed to the public function to use for the request.
2590
- * @returns {RequestOptions} The options to use for the request.
2908
+ * @private
2909
+ * @param {Object} options The options for the event.
2910
+ * @param {string} options.name The name of the event.
2911
+ * @param {Event} [options.event] The event object.
2912
+ * @param {*} [options.data] The data to pass to the subscribers.
2913
+ * @param {boolean} [options.global=true] Whether or not to publish the event to the global subscribers.
2914
+ * @returns {void}
2591
2915
  */
2592
- static #convertRequestOptions(options) {
2593
- if (!object_is_empty_default(options)) {
2594
- for (const [{ property, type, converter }, option = options[property]] of _Transportr.#propertyTypeConverters) {
2595
- if (option instanceof type) {
2596
- options[property] = converter(option);
2916
+ #publish({ name, event = new CustomEvent(name), data, global = true } = {}) {
2917
+ if (global) {
2918
+ _Transportr.#globalSubscribr.publish(name, event, data);
2919
+ }
2920
+ this.#subscribr.publish(name, event, data);
2921
+ }
2922
+ /**
2923
+ * Returns a response handler for the given content type.
2924
+ *
2925
+ * @private
2926
+ * @param {string} contentType The content type of the response.
2927
+ * @returns {ResponseHandler<ResponseBody>} The response handler.
2928
+ */
2929
+ #getResponseHandler(contentType) {
2930
+ let handler;
2931
+ const mediaType = MediaType.parse(contentType);
2932
+ if (mediaType) {
2933
+ for (const [responseHandler, contentTypes] of _Transportr.#contentTypeHandlers) {
2934
+ if (contentTypes.has(mediaType.type) || contentTypes.has(mediaType.subtype)) {
2935
+ handler = responseHandler;
2936
+ break;
2597
2937
  }
2598
2938
  }
2599
2939
  }
2600
- return options;
2940
+ return handler;
2601
2941
  }
2602
2942
  /**
2603
2943
  * A String value that is used in the creation of the default string