@optionfactory/fml 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +22 -0
  3. package/dist/client-errors.iife.js +109 -0
  4. package/dist/client-errors.iife.js.map +1 -0
  5. package/dist/client-errors.iife.min.js +2 -0
  6. package/dist/client-errors.iife.min.js.map +1 -0
  7. package/dist/fml.css +2 -0
  8. package/dist/fml.css.map +1 -0
  9. package/dist/fml.d.mts +1241 -0
  10. package/dist/fml.iife.js +8340 -0
  11. package/dist/fml.iife.js.map +1 -0
  12. package/dist/fml.iife.min.js +2 -0
  13. package/dist/fml.iife.min.js.map +1 -0
  14. package/dist/fml.min.mjs +2 -0
  15. package/dist/fml.min.mjs.map +1 -0
  16. package/dist/fml.mjs +8284 -0
  17. package/dist/fml.mjs.map +1 -0
  18. package/dist/ftl.d.mts +361 -0
  19. package/dist/ftl.iife.js +4719 -0
  20. package/dist/ftl.iife.js.map +1 -0
  21. package/dist/ftl.iife.min.js +2 -0
  22. package/dist/ftl.iife.min.js.map +1 -0
  23. package/dist/ftl.min.mjs +2 -0
  24. package/dist/ftl.min.mjs.map +1 -0
  25. package/dist/ftl.mjs +4700 -0
  26. package/dist/ftl.mjs.map +1 -0
  27. package/dist/ful.css +2 -0
  28. package/dist/ful.css.map +1 -0
  29. package/dist/ful.d.mts +581 -0
  30. package/dist/ful.iife.js +2814 -0
  31. package/dist/ful.iife.js.map +1 -0
  32. package/dist/ful.iife.min.js +2 -0
  33. package/dist/ful.iife.min.js.map +1 -0
  34. package/dist/ful.min.mjs +2 -0
  35. package/dist/ful.min.mjs.map +1 -0
  36. package/dist/ful.mjs +2780 -0
  37. package/dist/ful.mjs.map +1 -0
  38. package/dist/httpc.d.mts +306 -0
  39. package/dist/httpc.iife.js +755 -0
  40. package/dist/httpc.iife.js.map +1 -0
  41. package/dist/httpc.iife.min.js +2 -0
  42. package/dist/httpc.iife.min.js.map +1 -0
  43. package/dist/httpc.min.mjs +2 -0
  44. package/dist/httpc.min.mjs.map +1 -0
  45. package/dist/httpc.mjs +743 -0
  46. package/dist/httpc.mjs.map +1 -0
  47. package/package.json +72 -0
@@ -0,0 +1,755 @@
1
+ var httpc = (function (exports) {
2
+ 'use strict';
3
+
4
+ /**
5
+ * @typedef {{ type: string; context: string?; reason: string; details: any?; }} Problem
6
+ */
7
+ class Failure extends Error {
8
+ /**
9
+ *
10
+ * @param {string} message
11
+ * @param {Problem[]} problems
12
+ * @param {*} cause
13
+ */
14
+ constructor(message, problems, cause) {
15
+ super(message, { cause });
16
+ this.name = 'Failure';
17
+ this.problems = problems;
18
+ }
19
+ dropping(prefix) {
20
+ return new Failure(this.message, Failure.dropProblemsContext(this.problems, prefix), this);
21
+ }
22
+ static dropProblemsContext(problems, prefix) {
23
+ return problems.map(({ type, context, reason, details }) => {
24
+ const nctx = context?.startsWith(prefix) ? context.substring(prefix.length) : context;
25
+ return { type, context: nctx, reason, details };
26
+ });
27
+ }
28
+ }
29
+
30
+ class Base64 {
31
+ static encode(arrayBuffer, dialect) {
32
+ const d = dialect || Base64.URL_SAFE;
33
+ const len = arrayBuffer.byteLength;
34
+ const view = new Uint8Array(arrayBuffer);
35
+ let res = '';
36
+ for (let i = 0; i < len; i += 3) {
37
+ const v1 = d[view[i] >> 2];
38
+ const v2 = d[((view[i] & 3) << 4) | (view[i + 1] >> 4)];
39
+ const v3 = d[((view[i + 1] & 15) << 2) | (view[i + 2] >> 6)];
40
+ const v4 = d[view[i + 2] & 63];
41
+ res += v1 + v2 + v3 + v4;
42
+ }
43
+ if (len % 3 === 2) {
44
+ res = res.substring(0, res.length - 1);
45
+ } else if (len % 3 === 1) {
46
+ res = res.substring(0, res.length - 2);
47
+ }
48
+ return res;
49
+ }
50
+ static decode(str, dialect) {
51
+ const d = dialect || Base64.URL_SAFE;
52
+ let nbytes = Math.floor(str.length * 0.75);
53
+ for (let i = 0; i !== str.length; ++i) {
54
+ if (str[str.length - i - 1] !== '=') {
55
+ break;
56
+ }
57
+ --nbytes;
58
+ }
59
+ const view = new Uint8Array(nbytes);
60
+
61
+ let vi = 0;
62
+ let si = 0;
63
+ while (vi < str.length * 0.75) {
64
+ const v1 = d.indexOf(str.charAt(si++));
65
+ const v2 = d.indexOf(str.charAt(si++));
66
+ const v3 = d.indexOf(str.charAt(si++));
67
+ const v4 = d.indexOf(str.charAt(si++));
68
+ view[vi++] = (v1 << 2) | (v2 >> 4);
69
+ view[vi++] = ((v2 & 15) << 4) | (v3 >> 2);
70
+ view[vi++] = ((v3 & 3) << 6) | v4;
71
+ }
72
+
73
+ return view.buffer;
74
+ }
75
+ }
76
+
77
+ Base64.STANDARD = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
78
+ Base64.URL_SAFE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
79
+
80
+ class Hex {
81
+ static decode(hex) {
82
+ if (hex.length % 2 !== 0) {
83
+ throw new Error('invalid length');
84
+ }
85
+ const lenInBytes = hex.length / 2;
86
+ return new Uint8Array(lenInBytes).map((e, i) => {
87
+ const offset = i * 2;
88
+ const octet = hex.substring(offset, offset + 2);
89
+ return parseInt(octet, 16);
90
+ });
91
+ }
92
+ static encode(bytes, upper) {
93
+ return Array.from(bytes)
94
+ .map((b) => b.toString(16))
95
+ .map((b) => (upper ? b.toUpperCase() : b))
96
+ .map((o) => o.padStart(2, 0))
97
+ .join('');
98
+ }
99
+ }
100
+
101
+ class MediaType {
102
+ #type;
103
+ #subtype;
104
+ constructor(type, subtype) {
105
+ this.#type = type;
106
+ this.#subtype = subtype;
107
+ }
108
+ get normalized() {
109
+ return `${this.#type}/${this.#subtype}`;
110
+ }
111
+ get type() {
112
+ return this.#type;
113
+ }
114
+ get subtype() {
115
+ return this.#subtype;
116
+ }
117
+ /**
118
+ *
119
+ * @param {string|null|undefined} v
120
+ * @returns
121
+ */
122
+ static parse(v) {
123
+ if (!v) {
124
+ return new MediaType('unknown', 'unknown');
125
+ }
126
+ const [prefix, _] = v.split(';');
127
+ const [ptype, psubtype] = prefix.trim().split('/');
128
+ return new MediaType(ptype.toLowerCase(), psubtype?.toLowerCase());
129
+ }
130
+ }
131
+
132
+ /**
133
+ * @typedef {Int8Array| Uint8Array| Uint8ClampedArray| Int16Array| Uint16Array| Int32Array| Uint32Array| Float32Array| Float64Array| BigInt64Array| BigUint64Array} TypedArray
134
+ */
135
+ /**
136
+ * @typedef {object} HttpInterceptor
137
+ * @property {(url: URL, init: RequestInit|undefined, chain: HttpInterceptorChain) => Promise<Response>} intercept
138
+ */
139
+
140
+ class HttpClientError extends Failure {
141
+ /**
142
+ * @param {string} message
143
+ * @param {number} status
144
+ * @param {{ type: string; context: string?; reason: string; details: any?; }[]} problems
145
+ * @param {Error|undefined} [cause]
146
+ */
147
+ constructor(message, status, problems, cause) {
148
+ super(message, problems, cause);
149
+ this.name = 'HttpClientError';
150
+ this.status = status;
151
+ }
152
+ dropping(prefix) {
153
+ return new HttpClientError(this.message, this.status, Failure.dropProblemsContext(this.problems, prefix), this);
154
+ }
155
+ /**
156
+ *
157
+ * @param {string} type
158
+ * @param {any} cause
159
+ * @returns
160
+ */
161
+ static of(type, cause) {
162
+ return new HttpClientError(
163
+ cause.message,
164
+ 0,
165
+ [
166
+ {
167
+ type,
168
+ context: null,
169
+ reason: cause.message,
170
+ details: null,
171
+ },
172
+ ],
173
+ cause,
174
+ );
175
+ }
176
+ /**
177
+ * Creates an HttpClientError from a Response.
178
+ * @param {Response} response
179
+ * @returns an HttpClientError
180
+ */
181
+ static async fromResponse(response) {
182
+ switch (MediaType.parse(response.headers.get('Content-Type')).normalized) {
183
+ case 'application/failures+json': {
184
+ const data = await response.json();
185
+ const message = `${response.status} ${response.statusText}: ${data.length} failures`;
186
+ return new HttpClientError(message, response.status, data);
187
+ }
188
+ case 'application/problem+json': {
189
+ const data = await response.json();
190
+ const message = `${response.status} ${response.statusText}: ${data.title} ${data.detail}`;
191
+ return new HttpClientError(
192
+ message,
193
+ response.status,
194
+ data.problems || [
195
+ {
196
+ type: 'GENERIC_PROBLEM',
197
+ context: null,
198
+ reason: message,
199
+ details: null,
200
+ },
201
+ ],
202
+ );
203
+ }
204
+ default: {
205
+ const text = await response.text();
206
+ const message = `${response.status} ${response.statusText}: ${text}`;
207
+ return new HttpClientError(message, response.status, [
208
+ {
209
+ type: 'GENERIC_PROBLEM',
210
+ context: null,
211
+ reason: message,
212
+ details: null,
213
+ },
214
+ ]);
215
+ }
216
+ }
217
+ }
218
+ }
219
+
220
+ /**
221
+ * @implements {HttpInterceptor}
222
+ */
223
+ class CsrfTokenInterceptor {
224
+ #k;
225
+ #v;
226
+ constructor() {
227
+ this.#k = document.querySelector("meta[name='_csrf_header']")?.getAttribute('content');
228
+ this.#v = document.querySelector("meta[name='_csrf']")?.getAttribute('content');
229
+ }
230
+ async intercept(url, request, chain) {
231
+ if (this.#k && this.#v) {
232
+ request.headers.set(this.#k, this.#v);
233
+ }
234
+ return await chain.proceed(url, request);
235
+ }
236
+ }
237
+ /**
238
+ * @implements {HttpInterceptor}
239
+ */
240
+ class RedirectOnUnauthorizedInterceptor {
241
+ #redirectUri;
242
+ /**
243
+ * @param {string} redirectUri
244
+ */
245
+ constructor(redirectUri) {
246
+ this.#redirectUri = redirectUri;
247
+ }
248
+ async intercept(url, request, chain) {
249
+ const response = await chain.proceed(url, request);
250
+ if (response.status === 401) {
251
+ window.location.href = this.#redirectUri;
252
+ return new Promise(() => {});
253
+ }
254
+ return response;
255
+ }
256
+ }
257
+
258
+ class HttpClientBuilder {
259
+ /**
260
+ * @type {HttpInterceptor[]}
261
+ */
262
+ #interceptors;
263
+ constructor() {
264
+ this.#interceptors = [];
265
+ }
266
+ withCsrfToken() {
267
+ this.#interceptors.push(new CsrfTokenInterceptor());
268
+ return this;
269
+ }
270
+ withRedirectOnUnauthorized(redirectUri) {
271
+ this.#interceptors.push(new RedirectOnUnauthorizedInterceptor(redirectUri));
272
+ return this;
273
+ }
274
+ /**
275
+ * @param {...HttpInterceptor} interceptors
276
+ */
277
+ withInterceptors(...interceptors) {
278
+ this.#interceptors.push(...interceptors);
279
+ return this;
280
+ }
281
+ build() {
282
+ return new HttpClient(this.#interceptors);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * @implements {HttpInterceptor}
288
+ */
289
+ class HttpCall {
290
+ async intercept(url, request, chain) {
291
+ return await fetch(url, request);
292
+ }
293
+ }
294
+
295
+ class HttpInterceptorChain {
296
+ #interceptors;
297
+ #current;
298
+ /**
299
+ *
300
+ * @param {HttpInterceptor[]} interceptors
301
+ * @param {number} current
302
+ */
303
+ constructor(interceptors, current) {
304
+ this.#interceptors = interceptors;
305
+ this.#current = current;
306
+ }
307
+ /**
308
+ *
309
+ * @param {URL} url
310
+ * @param {RequestInit} request
311
+ * @returns {Promise<Response>} the response
312
+ */
313
+ async proceed(url, request) {
314
+ const interceptor = this.#interceptors[this.#current];
315
+ return await interceptor.intercept(
316
+ url,
317
+ request,
318
+ new HttpInterceptorChain(this.#interceptors, this.#current + 1),
319
+ );
320
+ }
321
+ }
322
+
323
+ class HttpClient {
324
+ #interceptors;
325
+ /**
326
+ * Creates a builder for an HttpClient.
327
+ * @returns {HttpClientBuilder} the client builder
328
+ */
329
+ static builder() {
330
+ return new HttpClientBuilder();
331
+ }
332
+ /**
333
+ * Creates an HttpClient.
334
+ * @param {HttpInterceptor[]|undefined} interceptors - a list of interceptors to be registered for every request performed by the created client.
335
+ */
336
+ constructor(interceptors) {
337
+ this.#interceptors = interceptors || [];
338
+ }
339
+ /**
340
+ * Performs an HTTP exchange.
341
+ * @async
342
+ * @param {string} uri - the (possibly relative) request url
343
+ * @param {RequestInit|undefined} options - fetch options
344
+ * @param {HttpInterceptor[]|undefined} interceptors - the HttpInterceptors to be registered for this exchange.
345
+ * @returns {Promise<Response>} the response
346
+ */
347
+ async exchange(uri, options, interceptors) {
348
+ const is = [...this.#interceptors, ...(interceptors || []), new HttpCall()];
349
+ const chain = new HttpInterceptorChain(is, 0);
350
+ const url = new URL(new Request(uri).url);
351
+ return await chain.proceed(url, options ?? {});
352
+ }
353
+ /**
354
+ * Creates a request builder.
355
+ * @param {string} method - the HTTP method to be used
356
+ * @param {string} uri - the (possibly relative) request url
357
+ * @returns {HttpRequestBuilder} the request builder
358
+ */
359
+ request(method, uri) {
360
+ return HttpRequestBuilder.create(this, method, uri);
361
+ }
362
+ /**
363
+ * Creates a request builder.
364
+ * @param {string} uri - the (possibly relative) request url
365
+ * @returns {HttpRequestBuilder} the request builder
366
+ */
367
+ get(uri) {
368
+ return HttpRequestBuilder.create(this, 'GET', uri);
369
+ }
370
+ /**
371
+ * Creates a request builder.
372
+ * @param {string} uri - the (possibly relative) request url
373
+ * @returns {HttpRequestBuilder} the request builder
374
+ */
375
+ head(uri) {
376
+ return HttpRequestBuilder.create(this, 'HEAD', uri);
377
+ }
378
+ /**
379
+ * Creates a request builder.
380
+ * @param {string} uri - the (possibly relative) request url
381
+ * @returns {HttpRequestBuilder} the request builder
382
+ */
383
+ post(uri) {
384
+ return HttpRequestBuilder.create(this, 'POST', uri);
385
+ }
386
+ /**
387
+ * Creates a request builder.
388
+ * @param {string} uri - the (possibly relative) request url
389
+ * @returns {HttpRequestBuilder} the request builder
390
+ */
391
+ put(uri) {
392
+ return HttpRequestBuilder.create(this, 'PUT', uri);
393
+ }
394
+ /**
395
+ * Creates a request builder.
396
+ * @param {string} uri - the (possibly relative) request url
397
+ * @returns {HttpRequestBuilder} the request builder
398
+ */
399
+ patch(uri) {
400
+ return HttpRequestBuilder.create(this, 'PATCH', uri);
401
+ }
402
+ /**
403
+ * Creates a request builder.
404
+ * @param {string} uri - the (possibly relative) request url
405
+ * @returns {HttpRequestBuilder} the request builder
406
+ */
407
+ delete(uri) {
408
+ return HttpRequestBuilder.create(this, 'DELETE', uri);
409
+ }
410
+ }
411
+
412
+ /**
413
+ *
414
+ * @param {Response} response
415
+ * @param {'text'|'json'|'blob'|'arrayBuffer'} type
416
+ * @returns
417
+ */
418
+ const unmarshal = async (response, type) => {
419
+ try {
420
+ return await response[type]();
421
+ } catch (ex) {
422
+ throw HttpClientError.of('UNMARSHALING_PROBLEM', ex);
423
+ }
424
+ };
425
+
426
+ class HttpRequestBuilder {
427
+ #client;
428
+ #method;
429
+ #uri;
430
+ #params;
431
+ #headers;
432
+ #body;
433
+ #options;
434
+ #interceptors;
435
+ /**
436
+ * Creates an HttpRequestBuilder.
437
+ * @param {HttpClient} client
438
+ * @param {string} method - the HTTP method to be used
439
+ * @param {string} uri - the (possibly relative) request url
440
+ * @returns {HttpRequestBuilder} the builder
441
+ */
442
+ static create(client, method, uri) {
443
+ const [baseUri, queryString = ''] = uri.split('?');
444
+ return new HttpRequestBuilder(
445
+ client,
446
+ method,
447
+ baseUri,
448
+ new URLSearchParams(queryString),
449
+ new Headers(),
450
+ undefined,
451
+ {},
452
+ [],
453
+ );
454
+ }
455
+ /**
456
+ * Creates an HttpRequestBuilder.
457
+ * @param {HttpClient} client
458
+ * @param {string} method - the HTTP method to be used
459
+ * @param {string} uri - the (possibly relative) request url
460
+ * @param {URLSearchParams} params
461
+ * @param {Headers} headers
462
+ * @param {any} body
463
+ * @param {Omit<RequestInit,"headers"|"method"|"body">} options
464
+ * @param {HttpInterceptor[]} interceptors
465
+ */
466
+ constructor(client, method, uri, params, headers, body, options, interceptors) {
467
+ this.#client = client;
468
+ this.#method = method;
469
+ this.#uri = uri;
470
+ this.#params = params;
471
+ this.#body = body;
472
+ this.#headers = headers;
473
+ this.#options = options;
474
+ this.#interceptors = interceptors;
475
+ }
476
+ /**
477
+ * Add all passed headers to the request, overriding existing ones if that key already exists. Null and undefined values cause the key to be removed.
478
+ * @param {HeadersInit} hs
479
+ * @returns {HttpRequestBuilder} this builder
480
+ */
481
+ headers(hs) {
482
+ for (const [k, v] of new Headers(hs).entries()) {
483
+ if (v == null) {
484
+ this.#headers.delete(k);
485
+ } else {
486
+ this.#headers.set(k, v);
487
+ }
488
+ }
489
+ return this;
490
+ }
491
+ /**
492
+ * Adds an header to the request, overriding it if it already exists. Null and undefined values cause the key to be removed
493
+ * @param {string} k
494
+ * @param {string} v
495
+ * @returns {HttpRequestBuilder} this builder
496
+ */
497
+ header(k, v) {
498
+ if (v == null) {
499
+ this.#headers.delete(k);
500
+ } else {
501
+ this.#headers.set(k, v);
502
+ }
503
+ return this;
504
+ }
505
+ /**
506
+ * Add all query parameters to the request, overriding existing ones if that key already exists. Null and undefined values cause the key to be removed
507
+ * @param {URLSearchParams|Record<string,string>|string[][]|string} ps
508
+ * @returns {HttpRequestBuilder} this builder
509
+ */
510
+ params(ps) {
511
+ for (const [k, v] of new URLSearchParams(ps).entries()) {
512
+ if (v == null) {
513
+ this.#params.delete(k);
514
+ } else {
515
+ this.#params.set(k, v);
516
+ }
517
+ }
518
+ return this;
519
+ }
520
+ /**
521
+ * Adds a query parameter to the request, overriding it if it already exists. Empty vs, or a single null or undefined value cause the key to be removed.
522
+ * @param {string} k
523
+ * @param {...string} vs
524
+ * @returns {HttpRequestBuilder} this builder
525
+ */
526
+ param(k, ...vs) {
527
+ if (vs.length === 0 || vs[0] == null) {
528
+ this.#params.delete(k);
529
+ return this;
530
+ }
531
+ for (const v of vs) {
532
+ this.#params.append(k, v);
533
+ }
534
+ return this;
535
+ }
536
+ /**
537
+ * Sets the request body.
538
+ * `Content-Type: multipart/form-data` header is automatically added by fetch when data is a FormData instance if not explicitly set.
539
+ * `Content-Type: application/x-www-form-urlencoded` header is automatically added by fetch when data is an URLSearchParams instance if not explicitly set.
540
+ * `Content-Type: text/plain` header is automatically added by fetch when data is a string instance if not explicitly set.
541
+ * @param {string|ArrayBuffer|Blob|DataView|File|FormData|TypedArray|URLSearchParams|ReadableStream} data
542
+ * @returns {HttpRequestBuilder} this builder
543
+ */
544
+ body(data) {
545
+ this.#body = data;
546
+ return this;
547
+ }
548
+ /**
549
+ * Sets the request body that will be serialized as json. Calling this method adds the `Content-Type application/json` header for the request.
550
+ * @param {any} body - the body to be serialized as json
551
+ * @returns {HttpRequestBuilder} this builder
552
+ */
553
+ json(body) {
554
+ this.#headers.set('Content-Type', 'application/json');
555
+ this.#body = JSON.stringify(body);
556
+ return this;
557
+ }
558
+ /**
559
+ * Sets the request body as a FormData configured using the callback.
560
+ * `Content-Type: multipart/form-data` header is automatically added by fetch if not explicitly set.
561
+ * @param {function(HttpMultipartRequestCustomizer):void} callback
562
+ */
563
+ multipart(callback) {
564
+ const formData = new FormData();
565
+ const builder = new HttpMultipartRequestCustomizer(formData);
566
+ callback(builder);
567
+ this.#body = formData;
568
+ return this;
569
+ }
570
+ /**
571
+ * Sets a fetch options for the request.
572
+ * @param {Omit<RequestInit,"headers"|"method"|"body">} kvs
573
+ * @returns {HttpRequestBuilder} this builder
574
+ */
575
+ options(kvs) {
576
+ for (const [k, v] of Object.entries(kvs)) {
577
+ this.#options[k] = v;
578
+ }
579
+ return this;
580
+ }
581
+ /**
582
+ * Sets a fetch option for the request.
583
+ * @param {keyof Omit<RequestInit,"headers"|"method"|"body">} k
584
+ * @param {*} v
585
+ * @returns {HttpRequestBuilder} this builder
586
+ */
587
+ option(k, v) {
588
+ this.#options[k] = v;
589
+ return this;
590
+ }
591
+ /**
592
+ * Adds interceptors to the request.
593
+ * @param {[HttpInterceptor]} is - the interceptor to be regisered
594
+ * @returns {HttpRequestBuilder} this builder
595
+ */
596
+ interceptors(is) {
597
+ for (const i of is) {
598
+ this.#interceptors.push(i);
599
+ }
600
+ return this;
601
+ }
602
+ /**
603
+ * Adds an interceptor to the request.
604
+ * @param {HttpInterceptor} i - the interceptor to be regisered
605
+ * @returns {HttpRequestBuilder} this builder
606
+ */
607
+ interceptor(i) {
608
+ this.#interceptors.push(i);
609
+ return this;
610
+ }
611
+ /**
612
+ * Performs an HTTP exchange using the configured client, request and interceptors.
613
+ * @returns {Promise<Response>} the response
614
+ */
615
+ async exchange() {
616
+ const uri = this.#params.size ? `${this.#uri}?${this.#params}` : this.#uri;
617
+ const opts = {
618
+ ...this.#options,
619
+ headers: this.#headers,
620
+ method: this.#method,
621
+ body: this.#body,
622
+ };
623
+ return await this.#client.exchange(uri, opts, this.#interceptors);
624
+ }
625
+ /**
626
+ * Performs an HTTP exchange using the configured client request, and interceptos throwing a failure when response status is not in the 200-299 range.
627
+ * @returns {Promise<Response>} the response
628
+ */
629
+ async fetch() {
630
+ const uri = this.#params.size ? `${this.#uri}?${this.#params}` : this.#uri;
631
+ const opts = {
632
+ ...this.#options,
633
+ headers: this.#headers,
634
+ method: this.#method,
635
+ body: this.#body,
636
+ };
637
+ try {
638
+ const response = await this.#client.exchange(uri, opts, this.#interceptors);
639
+ if (!response.ok) {
640
+ throw await HttpClientError.fromResponse(response);
641
+ }
642
+ return response;
643
+ } catch (ex) {
644
+ if (ex instanceof Failure) {
645
+ throw ex;
646
+ }
647
+ throw HttpClientError.of('CONNECTION_PROBLEM', ex);
648
+ }
649
+ }
650
+ /**
651
+ * Performs an HTTP exchange using the configured client request, and interceptos throwing a failure when response status is not in the 200-299 range.
652
+ * @returns {Promise<string>} the response body, as text
653
+ */
654
+ async fetchText() {
655
+ const response = await this.fetch();
656
+ return await unmarshal(response, 'text');
657
+ }
658
+ /**
659
+ * Performs an HTTP exchange using the configured client request, and interceptos throwing a failure when response status is not in the 200-299 range.
660
+ * @returns {Promise<any>} the response body, deserialized as JSON
661
+ */
662
+ async fetchJson() {
663
+ const response = await this.fetch();
664
+ return await unmarshal(response, 'json');
665
+ }
666
+ /**
667
+ * Performs an HTTP exchange using the configured client request, and interceptos throwing a failure when response status is not in the 200-299 range.
668
+ * @returns {Promise<Blob>} the response body, as a Blob
669
+ */
670
+ async fetchBlob() {
671
+ const response = await this.fetch();
672
+ return await unmarshal(response, 'blob');
673
+ }
674
+ /**
675
+ * Performs an HTTP exchange using the configured client request, and interceptos throwing a failure when response status is not in the 200-299 range.
676
+ * @returns {Promise<ArrayBuffer>} the response body, as an ArrayBuffer
677
+ */
678
+ async fetchArrayBuffer() {
679
+ const response = await this.fetch();
680
+ return await unmarshal(response, 'arrayBuffer');
681
+ }
682
+ }
683
+
684
+ class HttpMultipartRequestCustomizer {
685
+ #formData;
686
+ /**
687
+ *
688
+ * @param {FormData} formData
689
+ */
690
+ constructor(formData) {
691
+ this.#formData = formData;
692
+ }
693
+ /**
694
+ * Appends a value to the FormData.
695
+ * @param {string} name
696
+ * @param {*} value
697
+ * @returns this builder
698
+ */
699
+ field(name, value) {
700
+ this.#formData.append(name, value);
701
+ return this;
702
+ }
703
+ /**
704
+ * Appends a Blob to the FormData.
705
+ * If `filename` is omitted, FormData defaults are applied:
706
+ * The default filename for Blob objects is "blob";
707
+ * The default filename for File objects is the file's filename.
708
+ * @param {string} name
709
+ * @param {Blob} value
710
+ * @param {string|undefined} filename
711
+ * @returns this builder
712
+ */
713
+ blob(name, value, filename) {
714
+ this.#formData.append(name, value, filename);
715
+ return this;
716
+ }
717
+ /**
718
+ * Appends multiple Blobs to the FormData with the same name.
719
+ * The default filename for Blob objects is "blob";
720
+ * The default filename for File objects is the file's filename.
721
+ * @param {string} name
722
+ * @param {Blob[]} values
723
+ * @returns this builder
724
+ */
725
+ blobs(name, values) {
726
+ for (let v of values) {
727
+ this.#formData.append(name, v);
728
+ }
729
+ return this;
730
+ }
731
+ /**
732
+ * Appends a JSON serialized blob to the FormData.
733
+ * @param {string} name
734
+ * @param {any} value
735
+ * @param {string|undefined} filename
736
+ * @returns this builder
737
+ */
738
+ json(name, value, filename) {
739
+ const blob = new Blob([JSON.stringify(value)], { type: 'application/json' });
740
+ this.#formData.append(name, blob, filename);
741
+ return this;
742
+ }
743
+ }
744
+
745
+ exports.Base64 = Base64;
746
+ exports.Failure = Failure;
747
+ exports.Hex = Hex;
748
+ exports.HttpClient = HttpClient;
749
+ exports.HttpClientError = HttpClientError;
750
+ exports.MediaType = MediaType;
751
+
752
+ return exports;
753
+
754
+ })({});
755
+ //# sourceMappingURL=httpc.iife.js.map