@zlink-systems/http-client 0.10.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.
@@ -0,0 +1,818 @@
1
+ // packages/framework/src/contracts/Common/ZLinkMessageMetadata.ts
2
+ var ImmutableZLinkMessageMetadata = class {
3
+ values;
4
+ constructor(values = /* @__PURE__ */ new Map()) {
5
+ this.values = Object.freeze(new ImmutableMetadataMap(values));
6
+ }
7
+ find(key) {
8
+ return this.values.get(key);
9
+ }
10
+ };
11
+ var ImmutableMetadataMap = class {
12
+ #values;
13
+ constructor(values) {
14
+ this.#values = new Map(
15
+ typeof values[Symbol.iterator] === "function" ? values : Object.entries(values)
16
+ );
17
+ }
18
+ get size() {
19
+ return this.#values.size;
20
+ }
21
+ get(key) {
22
+ return this.#values.get(key);
23
+ }
24
+ has(key) {
25
+ return this.#values.has(key);
26
+ }
27
+ forEach(callbackfn, thisArg) {
28
+ this.#values.forEach((value, key) => callbackfn.call(thisArg, value, key, this));
29
+ }
30
+ entries() {
31
+ return this.#values.entries();
32
+ }
33
+ keys() {
34
+ return this.#values.keys();
35
+ }
36
+ values() {
37
+ return this.#values.values();
38
+ }
39
+ [Symbol.iterator]() {
40
+ return this.#values[Symbol.iterator]();
41
+ }
42
+ };
43
+ var ZLinkMessageMetadataEmpty = Object.freeze(new ImmutableZLinkMessageMetadata());
44
+
45
+ // packages/framework/src/contracts/Errors/ZLinkFrameworkException.ts
46
+ var ZLinkFrameworkException = class extends Error {
47
+ constructor(kind, message, cause) {
48
+ super(message, { cause });
49
+ this.kind = kind;
50
+ this.name = "ZLinkFrameworkException";
51
+ }
52
+ };
53
+
54
+ // packages/framework/src/contracts/Eventing/Metrics.ts
55
+ var ZLinkMeters = Object.freeze({ Framework: "zlink.framework" });
56
+
57
+ // packages/framework/src/contracts/Handlers/JsonContract.ts
58
+ var ZLINK_PACKET_JSON_CONTRACTS = Symbol.for("@zlink-systems/framework:packet-json-contracts");
59
+
60
+ // packages/framework/src/contracts/Handlers/Attributes.ts
61
+ var ZLINK_DECORATOR_METADATA = Symbol.for("@zlink-systems/framework:decorator");
62
+
63
+ // packages/http-client/src/runtime/redirect-policy.ts
64
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
65
+ function isRedirectStatus(status) {
66
+ return REDIRECT_STATUSES.has(status);
67
+ }
68
+ function makeTarget(prefix, path) {
69
+ if (prefix.length === 0 || prefix === "/") {
70
+ return path;
71
+ }
72
+ return prefix.endsWith("/") ? prefix.slice(0, -1) + path : prefix + path;
73
+ }
74
+ function rewriteForRedirect(status, method, body) {
75
+ if (status === 303 || (status === 301 || status === 302) && method === "POST") {
76
+ return { method: "GET", body: void 0 };
77
+ }
78
+ return { method, body };
79
+ }
80
+ function resolveLocation(current, location) {
81
+ try {
82
+ if (location.startsWith("http://") || location.startsWith("https://")) {
83
+ return new URL(location);
84
+ }
85
+ if (location.startsWith("//")) {
86
+ return new URL(current.protocol + location);
87
+ }
88
+ if (location.startsWith("/")) {
89
+ return new URL(current.origin + location);
90
+ }
91
+ } catch {
92
+ throw new ZLinkFrameworkException(
93
+ 5 /* Unavailable */,
94
+ `HTTP redirect location is malformed: ${location}`
95
+ );
96
+ }
97
+ throw new ZLinkFrameworkException(
98
+ 5 /* Unavailable */,
99
+ `HTTP redirect location is not supported: ${location}`
100
+ );
101
+ }
102
+
103
+ // packages/http-client/src/runtime/retry-policy.ts
104
+ function delayMsFor(attempt) {
105
+ const ceiling = Math.min(1e3, 50 << Math.min(attempt, 5));
106
+ return Math.floor(Math.random() * (ceiling + 1));
107
+ }
108
+ var RetryPolicy = class {
109
+ constructor(options) {
110
+ this.options = options;
111
+ }
112
+ async execute(spec, perform) {
113
+ const maxRetries = spec.sink !== void 0 || spec.bodyProvider !== void 0 ? 0 : this.options.retryAttempts;
114
+ const timeoutMs = spec.timeoutMs ?? this.options.timeoutMs;
115
+ for (let attempt = 0; ; attempt++) {
116
+ const controller = new AbortController();
117
+ const timer = setTimeout(() => {
118
+ controller.abort();
119
+ }, timeoutMs);
120
+ try {
121
+ return await perform(spec, controller.signal);
122
+ } catch (error) {
123
+ const failure = mapFailure(error, controller.signal.aborted);
124
+ if (isRetriableHttpFailure(failure) && attempt < maxRetries) {
125
+ await delay(delayMsFor(attempt));
126
+ continue;
127
+ }
128
+ throw failure;
129
+ } finally {
130
+ clearTimeout(timer);
131
+ }
132
+ }
133
+ }
134
+ };
135
+ function mapFailure(error, aborted) {
136
+ if (error instanceof ZLinkFrameworkException) {
137
+ return error;
138
+ }
139
+ if (aborted || error instanceof Error && error.name === "AbortError") {
140
+ return new ZLinkFrameworkException(
141
+ 7 /* DeadlineExceeded */,
142
+ "HTTP request exceeded timeout",
143
+ error
144
+ );
145
+ }
146
+ const message = error instanceof Error ? error.message : "HTTP transport failure";
147
+ return new ZLinkFrameworkException(5 /* Unavailable */, message, error);
148
+ }
149
+ function isRetriableHttpFailure(error) {
150
+ return error.kind === 5 /* Unavailable */ || error.kind === 7 /* DeadlineExceeded */;
151
+ }
152
+ function delay(ms) {
153
+ return new Promise((resolve) => {
154
+ setTimeout(resolve, ms);
155
+ });
156
+ }
157
+
158
+ // packages/http-client/src/runtime/browser-runtime.ts
159
+ var HttpClientRuntime = class {
160
+ constructor(options) {
161
+ this.options = options;
162
+ rejectUnsupportedTransportOptions(options);
163
+ this.retryPolicy = new RetryPolicy(options);
164
+ }
165
+ retryPolicy;
166
+ async executeAsync(spec) {
167
+ return await this.retryPolicy.execute(spec, (request, signal) => this.perform(request, signal));
168
+ }
169
+ async close() {
170
+ }
171
+ async perform(spec, signal) {
172
+ const baseUri = new URL(this.options.baseUrl);
173
+ const origin = baseUri.origin;
174
+ let current = new URL(origin + makeTarget(baseUri.pathname, spec.target));
175
+ let method = spec.method;
176
+ let body = spec.body;
177
+ let bodyProvider = spec.bodyProvider;
178
+ let redirectsLeft = this.options.followRedirects;
179
+ for (; ; ) {
180
+ const headers = this.buildHeaders(spec, current.origin === origin, body !== void 0 || bodyProvider !== void 0);
181
+ const response = await fetch(current, {
182
+ method,
183
+ headers,
184
+ body: body ?? bodyStream(bodyProvider),
185
+ credentials: this.options.cookies ? "include" : "same-origin",
186
+ redirect: "manual",
187
+ signal
188
+ });
189
+ const location = response.headers.get("location");
190
+ if (this.options.followRedirects > 0 && isRedirectStatus(response.status) && location !== null) {
191
+ if (redirectsLeft === 0) {
192
+ await response.body?.cancel();
193
+ throw requestError("HTTP request exceeded the redirect limit");
194
+ }
195
+ redirectsLeft--;
196
+ ({ method, body } = rewriteForRedirect(response.status, method, body));
197
+ bodyProvider = void 0;
198
+ await response.body?.cancel();
199
+ current = resolveLocation(current, location);
200
+ continue;
201
+ }
202
+ const headersResult = collectHeaders(response.headers);
203
+ if (spec.sink !== void 0) {
204
+ await streamResponse(response, spec.sink, this.options.maxResponseBodySize);
205
+ return { status: response.status, headers: headersResult, body: "" };
206
+ }
207
+ const text = await response.text();
208
+ if (new TextEncoder().encode(text).length > this.options.maxResponseBodySize) {
209
+ throw requestError("HTTP response exceeded the maximum body size");
210
+ }
211
+ return { status: response.status, headers: headersResult, body: text };
212
+ }
213
+ }
214
+ buildHeaders(spec, keepAuthorization, hasBody) {
215
+ const headers = { accept: "application/json" };
216
+ applyHeaders(headers, this.options.headers, keepAuthorization);
217
+ applyHeaders(headers, spec.headers, keepAuthorization);
218
+ if (!hasBody) delete headers["content-type"];
219
+ return headers;
220
+ }
221
+ };
222
+ function rejectUnsupportedTransportOptions(options) {
223
+ if (options.trustCertificateFile !== void 0 || options.clientCertificate !== void 0 || options.proxy !== void 0) {
224
+ throw new ZLinkFrameworkException(
225
+ 9 /* ProtocolError */,
226
+ "Browser HTTP clients cannot configure certificate files or a transport proxy."
227
+ );
228
+ }
229
+ }
230
+ function bodyStream(provider) {
231
+ if (provider === void 0) return void 0;
232
+ return new ReadableStream({
233
+ pull(controller) {
234
+ const chunk = provider();
235
+ if (chunk === null) controller.close();
236
+ else controller.enqueue(chunk);
237
+ }
238
+ });
239
+ }
240
+ async function streamResponse(response, sink, maximumSize) {
241
+ if (response.body === null) return;
242
+ const reader = response.body.getReader();
243
+ let total = 0;
244
+ for (; ; ) {
245
+ const result = await reader.read();
246
+ if (result.done) return;
247
+ total += result.value.length;
248
+ if (total > maximumSize) {
249
+ await reader.cancel();
250
+ throw requestError("HTTP response exceeded the maximum body size");
251
+ }
252
+ sink(result.value);
253
+ }
254
+ }
255
+ function collectHeaders(headers) {
256
+ const result = {};
257
+ headers.forEach((value, name) => {
258
+ result[name.toLowerCase()] = value;
259
+ });
260
+ return result;
261
+ }
262
+ function applyHeaders(target, source, keepAuthorization) {
263
+ for (const [name, value] of Object.entries(source)) {
264
+ const lower = name.toLowerCase();
265
+ if (keepAuthorization || lower !== "authorization") target[lower] = value;
266
+ }
267
+ }
268
+ function requestError(message) {
269
+ return new ZLinkFrameworkException(5 /* Unavailable */, message);
270
+ }
271
+
272
+ // packages/http-client/src/runtime/text.ts
273
+ function isBlank(value) {
274
+ return value.length === 0 || /^[\s]*$/u.test(value);
275
+ }
276
+ function requireNonBlank(value, message) {
277
+ if (isBlank(value)) {
278
+ throw new ZLinkFrameworkException(9 /* ProtocolError */, message);
279
+ }
280
+ }
281
+ function requirePositiveTimeout(value) {
282
+ if (!(value > 0)) {
283
+ throw new ZLinkFrameworkException(
284
+ 9 /* ProtocolError */,
285
+ "HTTP client timeout must be greater than zero"
286
+ );
287
+ }
288
+ }
289
+ var unreserved = /[A-Za-z0-9\-_.~]/u;
290
+ function percentEncode(value) {
291
+ const bytes = new TextEncoder().encode(value);
292
+ let encoded = "";
293
+ for (const byte of bytes) {
294
+ const char = String.fromCharCode(byte);
295
+ if (unreserved.test(char)) {
296
+ encoded += char;
297
+ } else {
298
+ encoded += "%" + byte.toString(16).toUpperCase().padStart(2, "0");
299
+ }
300
+ }
301
+ return encoded;
302
+ }
303
+ function basicAuthorization(user, password) {
304
+ return "Basic " + Buffer.from(`${user}:${password}`, "utf8").toString("base64");
305
+ }
306
+ function makeMultipartBoundary() {
307
+ return "zlink-boundary-" + Math.random().toString(16).slice(2).padEnd(16, "0").slice(0, 16);
308
+ }
309
+
310
+ // packages/http-client/src/request-builder.ts
311
+ var ZLinkHttpRequestBuilder = class {
312
+ constructor(client, method, path, clientFactory) {
313
+ this.method = method;
314
+ this.path = path;
315
+ this.clientInstance = client;
316
+ this.clientFactory = clientFactory;
317
+ this.ownsClient = clientFactory !== void 0;
318
+ this.executionScheduler = client?.executionScheduler;
319
+ this.executionTurn = client?.executionScheduler?.capture() ?? clientFactory?.captureExecutionTurn();
320
+ if (path.length === 0 || path[0] !== "/") {
321
+ throw new ZLinkFrameworkException(
322
+ 9 /* ProtocolError */,
323
+ "HTTP request path must start with /"
324
+ );
325
+ }
326
+ }
327
+ bodyValue;
328
+ bodyProviderValue;
329
+ headersValue = {};
330
+ timeoutMsValue;
331
+ queryValue = [];
332
+ formValue = [];
333
+ multipartValue = [];
334
+ clientInstance;
335
+ clientFactory;
336
+ ownsClient;
337
+ executionTurn;
338
+ executionScheduler;
339
+ consumed = false;
340
+ // Resolves the client to run on, building a one-shot client lazily. A one-shot request builder is
341
+ // single-use so its lazily-built client is closed exactly once.
342
+ resolveClient() {
343
+ if (this.ownsClient && this.consumed) {
344
+ throw new ZLinkFrameworkException(
345
+ 9 /* ProtocolError */,
346
+ "A one-shot HTTP request can only be submitted once"
347
+ );
348
+ }
349
+ this.consumed = true;
350
+ if (this.clientInstance === void 0) {
351
+ if (this.clientFactory === void 0) {
352
+ throw new ZLinkFrameworkException(
353
+ 9 /* ProtocolError */,
354
+ "HTTP request has no client"
355
+ );
356
+ }
357
+ this.clientInstance = this.clientFactory.build();
358
+ }
359
+ return this.clientInstance;
360
+ }
361
+ async closeIfOwned() {
362
+ if (this.ownsClient && this.clientInstance !== void 0) {
363
+ await this.clientInstance.close().catch(() => void 0);
364
+ }
365
+ }
366
+ header(name, value) {
367
+ requireNonBlank(name, "HTTP request header name is required");
368
+ this.headersValue[name.toLowerCase()] = value;
369
+ return this;
370
+ }
371
+ query(name, value) {
372
+ requireNonBlank(name, "HTTP request query name is required");
373
+ this.queryValue.push([name, value]);
374
+ return this;
375
+ }
376
+ timeout(milliseconds) {
377
+ requirePositiveTimeout(milliseconds);
378
+ this.timeoutMsValue = milliseconds;
379
+ return this;
380
+ }
381
+ body(value, contentType) {
382
+ if (contentType !== void 0) {
383
+ if (typeof value !== "string") {
384
+ throw new ZLinkFrameworkException(
385
+ 9 /* ProtocolError */,
386
+ "HTTP request raw body content is required"
387
+ );
388
+ }
389
+ requireNonBlank(contentType, "HTTP request body content type is required");
390
+ this.bodyValue = value;
391
+ this.headersValue["content-type"] = contentType;
392
+ return this;
393
+ }
394
+ this.bodyValue = JSON.stringify(value);
395
+ this.headersValue["content-type"] ??= "application/json";
396
+ return this;
397
+ }
398
+ /**
399
+ * Streams the request body chunk by chunk with chunked transfer-encoding; the provider returns
400
+ * `null` when the body is complete. Streamed requests are excluded from retry.
401
+ */
402
+ bodyStream(provider, contentType) {
403
+ if (typeof provider !== "function") {
404
+ throw new ZLinkFrameworkException(
405
+ 9 /* ProtocolError */,
406
+ "HTTP request body stream provider is required"
407
+ );
408
+ }
409
+ requireNonBlank(contentType, "HTTP request body content type is required");
410
+ this.bodyProviderValue = provider;
411
+ this.headersValue["content-type"] = contentType;
412
+ return this;
413
+ }
414
+ form(name, value) {
415
+ requireNonBlank(name, "HTTP request form field name is required");
416
+ this.formValue.push([name, value]);
417
+ return this;
418
+ }
419
+ multipart(name, value) {
420
+ requireNonBlank(name, "HTTP request multipart field name is required");
421
+ this.multipartValue.push({ name, filename: "", content: value, contentType: "" });
422
+ return this;
423
+ }
424
+ multipartFile(name, filename, content, contentType) {
425
+ requireNonBlank(name, "HTTP request multipart field name is required");
426
+ requireNonBlank(filename, "HTTP request multipart filename is required");
427
+ requireNonBlank(contentType, "HTTP request multipart content type is required");
428
+ this.multipartValue.push({ name, filename, content, contentType });
429
+ return this;
430
+ }
431
+ /** Executes the request and returns the raw response while retaining the current turn. */
432
+ async submitRaw() {
433
+ const request = this.makeRequest(void 0);
434
+ const client = this.resolveClient();
435
+ try {
436
+ return await client.runtime.executeAsync(request);
437
+ } finally {
438
+ await this.closeIfOwned();
439
+ }
440
+ }
441
+ /**
442
+ * Streams the response body to `sink` chunk by chunk instead of buffering it; the returned
443
+ * response carries status and headers with an empty body (no decompression of chunks).
444
+ */
445
+ async download(sink) {
446
+ if (typeof sink !== "function") {
447
+ throw new ZLinkFrameworkException(
448
+ 9 /* ProtocolError */,
449
+ "HTTP request download sink is required"
450
+ );
451
+ }
452
+ const request = this.makeRequest(sink);
453
+ const client = this.resolveClient();
454
+ try {
455
+ return await client.runtime.executeAsync(request);
456
+ } finally {
457
+ await this.closeIfOwned();
458
+ }
459
+ }
460
+ async(callback) {
461
+ const pending = this.executeTyped();
462
+ if (callback === void 0) {
463
+ return pending;
464
+ }
465
+ void pending.then(
466
+ (response) => this.completeCallback(() => callback(void 0, response)),
467
+ (error) => this.completeCallback(() => callback(error, void 0))
468
+ );
469
+ }
470
+ async executeTyped() {
471
+ const raw = await this.submitRaw();
472
+ if (raw.status >= 400) {
473
+ throw new ZLinkFrameworkException(
474
+ 12 /* InternalFailure */,
475
+ `HTTP request failed with status ${raw.status}`
476
+ );
477
+ }
478
+ let body;
479
+ if (raw.body.length === 0) {
480
+ body = null;
481
+ } else {
482
+ try {
483
+ body = JSON.parse(raw.body, safeJsonReviver);
484
+ } catch (cause) {
485
+ throw new ZLinkFrameworkException(
486
+ 9 /* ProtocolError */,
487
+ cause instanceof Error ? cause.message : "HTTP response body decode failed",
488
+ cause
489
+ );
490
+ }
491
+ }
492
+ return { status: raw.status, headers: raw.headers, body, rawBody: raw.body };
493
+ }
494
+ /** Returns only the decoded body for client-side scenarios that do not need the HTTP envelope. */
495
+ async fetch() {
496
+ return (await this.executeTyped()).body;
497
+ }
498
+ completeCallback(callback) {
499
+ if (this.executionTurn === void 0) {
500
+ callback();
501
+ return;
502
+ }
503
+ this.executionTurn.post(callback);
504
+ }
505
+ makeRequest(sink) {
506
+ const { body, headers } = this.resolveBodyAndHeaders();
507
+ return {
508
+ method: this.method,
509
+ target: this.resolveTarget(),
510
+ ...body !== void 0 ? { body } : {},
511
+ ...this.bodyProviderValue !== void 0 ? { bodyProvider: this.bodyProviderValue } : {},
512
+ headers,
513
+ ...this.timeoutMsValue !== void 0 ? { timeoutMs: this.timeoutMsValue } : {},
514
+ ...sink !== void 0 ? { sink } : {}
515
+ };
516
+ }
517
+ resolveTarget() {
518
+ if (this.queryValue.length === 0) {
519
+ return this.path;
520
+ }
521
+ let target = this.path;
522
+ let separator = this.path.includes("?") ? "&" : "?";
523
+ for (const [name, value] of this.queryValue) {
524
+ target += `${separator}${percentEncode(name)}=${percentEncode(value)}`;
525
+ separator = "&";
526
+ }
527
+ return target;
528
+ }
529
+ resolveBodyAndHeaders() {
530
+ if (this.countBodySources() > 1) {
531
+ throw new ZLinkFrameworkException(
532
+ 9 /* ProtocolError */,
533
+ "HTTP request accepts a single body source: body, body_stream, form, or multipart"
534
+ );
535
+ }
536
+ const headers = { ...this.headersValue };
537
+ if (this.bodyValue !== void 0) {
538
+ return { body: this.bodyValue, headers };
539
+ }
540
+ if (this.formValue.length > 0) {
541
+ headers["content-type"] = "application/x-www-form-urlencoded";
542
+ return { body: this.encodeFormBody(), headers };
543
+ }
544
+ if (this.multipartValue.length > 0) {
545
+ const boundary = makeMultipartBoundary();
546
+ headers["content-type"] = `multipart/form-data; boundary=${boundary}`;
547
+ return { body: this.encodeMultipartBody(boundary), headers };
548
+ }
549
+ return { body: void 0, headers };
550
+ }
551
+ countBodySources() {
552
+ return (this.bodyValue !== void 0 ? 1 : 0) + (this.bodyProviderValue !== void 0 ? 1 : 0) + (this.formValue.length > 0 ? 1 : 0) + (this.multipartValue.length > 0 ? 1 : 0);
553
+ }
554
+ encodeFormBody() {
555
+ return this.formValue.map(([name, value]) => `${percentEncode(name)}=${percentEncode(value)}`).join("&");
556
+ }
557
+ encodeMultipartBody(boundary) {
558
+ let encoded = "";
559
+ for (const part of this.multipartValue) {
560
+ encoded += `--${boundary}\r
561
+ `;
562
+ encoded += `Content-Disposition: form-data; name="${part.name}"`;
563
+ if (part.filename.length > 0) {
564
+ encoded += `; filename="${part.filename}"`;
565
+ }
566
+ encoded += "\r\n";
567
+ if (part.contentType.length > 0) {
568
+ encoded += `Content-Type: ${part.contentType}\r
569
+ `;
570
+ }
571
+ encoded += "\r\n";
572
+ encoded += part.content;
573
+ encoded += "\r\n";
574
+ }
575
+ encoded += `--${boundary}--\r
576
+ `;
577
+ return encoded;
578
+ }
579
+ };
580
+ var ZLinkFrameworkHttpRequestBuilder = class extends ZLinkHttpRequestBuilder {
581
+ /** Starts a server-side one-way request and ignores its response body. */
582
+ async submit() {
583
+ if (this.executionScheduler === void 0) {
584
+ throw new ZLinkFrameworkException(
585
+ 9 /* ProtocolError */,
586
+ "HTTP submit requires a framework server client"
587
+ );
588
+ }
589
+ await this.submitRaw();
590
+ }
591
+ /** Executes a typed request while yielding the current Spot turn. */
592
+ yield() {
593
+ if (this.executionTurn === void 0) {
594
+ return Promise.reject(new ZLinkFrameworkException(
595
+ 9 /* ProtocolError */,
596
+ "HTTP yield requires a framework Spot turn"
597
+ ));
598
+ }
599
+ return this.executionTurn.yieldPromise(this.executeTyped());
600
+ }
601
+ };
602
+ function createZLinkHttpRequestBuilder(client, method, path) {
603
+ return client.executionScheduler === void 0 ? new ZLinkHttpRequestBuilder(client, method, path) : new ZLinkFrameworkHttpRequestBuilder(client, method, path);
604
+ }
605
+ var prototypeKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
606
+ function safeJsonReviver(key, value) {
607
+ if (prototypeKeys.has(key)) {
608
+ return void 0;
609
+ }
610
+ return value;
611
+ }
612
+
613
+ // packages/http-client/src/client.ts
614
+ var ZLinkHttpClient = class {
615
+ constructor(runtimeInstance, executionScheduler) {
616
+ this.runtimeInstance = runtimeInstance;
617
+ this.executionScheduler = executionScheduler;
618
+ }
619
+ /** @internal */
620
+ get runtime() {
621
+ return this.runtimeInstance;
622
+ }
623
+ static create(baseUrl) {
624
+ const builder = new ZLinkHttpClientBuilder();
625
+ return baseUrl === void 0 ? builder : builder.baseUrl(baseUrl);
626
+ }
627
+ get(path) {
628
+ return createZLinkHttpRequestBuilder(this, "GET", path);
629
+ }
630
+ post(path) {
631
+ return createZLinkHttpRequestBuilder(this, "POST", path);
632
+ }
633
+ put(path) {
634
+ return createZLinkHttpRequestBuilder(this, "PUT", path);
635
+ }
636
+ delete(path) {
637
+ return createZLinkHttpRequestBuilder(this, "DELETE", path);
638
+ }
639
+ patch(path) {
640
+ return createZLinkHttpRequestBuilder(this, "PATCH", path);
641
+ }
642
+ head(path) {
643
+ return createZLinkHttpRequestBuilder(this, "HEAD", path);
644
+ }
645
+ options(path) {
646
+ return createZLinkHttpRequestBuilder(this, "OPTIONS", path);
647
+ }
648
+ /** Releases the underlying dispatcher (connection pool). */
649
+ async close() {
650
+ await this.runtimeInstance.close();
651
+ }
652
+ };
653
+ var ZLinkHttpClientBuilder = class {
654
+ baseUrlValue = "";
655
+ timeoutMsValue = 3e3;
656
+ maxResponseBodySizeValue = 16 * 1024 * 1024;
657
+ headersValue = {};
658
+ trustCertificateFileValue;
659
+ clientCertificateValue;
660
+ followRedirectsValue = 0;
661
+ retryAttemptsValue = 0;
662
+ cookiesValue = false;
663
+ proxyValue;
664
+ proxyAuthorizationValue;
665
+ compressionValue = false;
666
+ executionSchedulerValue;
667
+ baseUrl(value) {
668
+ requireNonBlank(value, "HTTP client base_url is required");
669
+ this.baseUrlValue = value;
670
+ return this;
671
+ }
672
+ timeout(milliseconds) {
673
+ requirePositiveTimeout(milliseconds);
674
+ this.timeoutMsValue = milliseconds;
675
+ return this;
676
+ }
677
+ defaultHeader(name, value) {
678
+ requireNonBlank(name, "HTTP client default header name is required");
679
+ this.headersValue[name.toLowerCase()] = value;
680
+ return this;
681
+ }
682
+ basicAuth(user, password) {
683
+ requireNonBlank(user, "HTTP client basic auth user is required");
684
+ this.headersValue["authorization"] = basicAuthorization(user, password);
685
+ return this;
686
+ }
687
+ bearerToken(token) {
688
+ requireNonBlank(token, "HTTP client bearer token is required");
689
+ this.headersValue["authorization"] = `Bearer ${token}`;
690
+ return this;
691
+ }
692
+ maxResponseBodySize(bytes) {
693
+ if (!(bytes > 0)) {
694
+ throw new ZLinkFrameworkException(
695
+ 9 /* ProtocolError */,
696
+ "HTTP client max response body size must be greater than zero"
697
+ );
698
+ }
699
+ this.maxResponseBodySizeValue = bytes;
700
+ return this;
701
+ }
702
+ trustCertificateFile(path) {
703
+ requireNonBlank(path, "HTTP client trust certificate file is required");
704
+ this.trustCertificateFileValue = path;
705
+ return this;
706
+ }
707
+ clientCertificateFile(certificatePath, keyPath) {
708
+ requireNonBlank(certificatePath, "HTTP client certificate file is required");
709
+ requireNonBlank(keyPath, "HTTP client certificate key file is required");
710
+ this.clientCertificateValue = { certificatePath, keyPath };
711
+ return this;
712
+ }
713
+ followRedirects(maxRedirects = 5) {
714
+ if (!(maxRedirects > 0)) {
715
+ throw new ZLinkFrameworkException(
716
+ 9 /* ProtocolError */,
717
+ "HTTP client follow_redirects must be greater than zero"
718
+ );
719
+ }
720
+ this.followRedirectsValue = maxRedirects;
721
+ return this;
722
+ }
723
+ retry(attempts) {
724
+ if (!(attempts > 0)) {
725
+ throw new ZLinkFrameworkException(
726
+ 9 /* ProtocolError */,
727
+ "HTTP client retry attempts must be greater than zero"
728
+ );
729
+ }
730
+ this.retryAttemptsValue = attempts;
731
+ return this;
732
+ }
733
+ cookies() {
734
+ this.cookiesValue = true;
735
+ return this;
736
+ }
737
+ proxy(url) {
738
+ requireNonBlank(url, "HTTP client proxy url is required");
739
+ if (!url.startsWith("http://")) {
740
+ throw new ZLinkFrameworkException(
741
+ 9 /* ProtocolError */,
742
+ "HTTP client proxy url must start with http://"
743
+ );
744
+ }
745
+ this.proxyValue = url;
746
+ return this;
747
+ }
748
+ proxyBasicAuth(user, password) {
749
+ requireNonBlank(user, "HTTP client proxy auth user is required");
750
+ this.proxyAuthorizationValue = basicAuthorization(user, password);
751
+ return this;
752
+ }
753
+ compression() {
754
+ this.compressionValue = true;
755
+ return this;
756
+ }
757
+ /** Supplies the framework turn scheduler used by server-side terminators. */
758
+ executionScheduler(scheduler) {
759
+ this.executionSchedulerValue = scheduler;
760
+ return this;
761
+ }
762
+ /** @internal Captures a turn for one-shot requests before the client is built. */
763
+ captureExecutionTurn() {
764
+ return this.executionSchedulerValue?.capture();
765
+ }
766
+ build() {
767
+ requireNonBlank(this.baseUrlValue, "HTTP client base_url is required");
768
+ requirePositiveTimeout(this.timeoutMsValue);
769
+ const lower = this.baseUrlValue.toLowerCase();
770
+ if (!lower.startsWith("http://") && !lower.startsWith("https://")) {
771
+ throw new ZLinkFrameworkException(
772
+ 9 /* ProtocolError */,
773
+ "HTTP client base_url must start with http:// or https://"
774
+ );
775
+ }
776
+ const options = {
777
+ baseUrl: this.baseUrlValue,
778
+ timeoutMs: this.timeoutMsValue,
779
+ maxResponseBodySize: this.maxResponseBodySizeValue,
780
+ headers: { ...this.headersValue },
781
+ ...this.trustCertificateFileValue !== void 0 ? { trustCertificateFile: this.trustCertificateFileValue } : {},
782
+ ...this.clientCertificateValue !== void 0 ? { clientCertificate: this.clientCertificateValue } : {},
783
+ followRedirects: this.followRedirectsValue,
784
+ retryAttempts: this.retryAttemptsValue,
785
+ cookies: this.cookiesValue,
786
+ ...this.proxyValue !== void 0 ? { proxy: this.proxyValue } : {},
787
+ ...this.proxyAuthorizationValue !== void 0 ? { proxyAuthorization: this.proxyAuthorizationValue } : {},
788
+ compression: this.compressionValue
789
+ };
790
+ return new ZLinkHttpClient(new HttpClientRuntime(options), this.executionSchedulerValue);
791
+ }
792
+ get(path) {
793
+ return new ZLinkHttpRequestBuilder(void 0, "GET", path, this);
794
+ }
795
+ post(path) {
796
+ return new ZLinkHttpRequestBuilder(void 0, "POST", path, this);
797
+ }
798
+ put(path) {
799
+ return new ZLinkHttpRequestBuilder(void 0, "PUT", path, this);
800
+ }
801
+ delete(path) {
802
+ return new ZLinkHttpRequestBuilder(void 0, "DELETE", path, this);
803
+ }
804
+ patch(path) {
805
+ return new ZLinkHttpRequestBuilder(void 0, "PATCH", path, this);
806
+ }
807
+ head(path) {
808
+ return new ZLinkHttpRequestBuilder(void 0, "HEAD", path, this);
809
+ }
810
+ options(path) {
811
+ return new ZLinkHttpRequestBuilder(void 0, "OPTIONS", path, this);
812
+ }
813
+ };
814
+ export {
815
+ ZLinkHttpClient,
816
+ ZLinkHttpClientBuilder,
817
+ ZLinkHttpRequestBuilder
818
+ };