@loadstrike/loadstrike-sdk 1.0.31601 → 1.0.32601

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 (33) hide show
  1. package/README.md +14 -2
  2. package/dist/cjs/internal/prometheus-remote-write.js +37 -0
  3. package/dist/cjs/internal/reporting-sink-http-error.js +17 -0
  4. package/dist/cjs/internal/vendor-metric-payloads.js +390 -0
  5. package/dist/cjs/iteration-observations.js +24 -8
  6. package/dist/cjs/local.js +48 -63
  7. package/dist/cjs/reporting-containment.js +242 -0
  8. package/dist/cjs/runtime.js +83 -3
  9. package/dist/cjs/sinks.js +1337 -38
  10. package/dist/cjs/transports.js +1339 -151
  11. package/dist/esm/internal/prometheus-remote-write.js +31 -0
  12. package/dist/esm/internal/reporting-sink-http-error.js +13 -0
  13. package/dist/esm/internal/vendor-metric-payloads.js +382 -0
  14. package/dist/esm/iteration-observations.js +24 -8
  15. package/dist/esm/local.js +49 -64
  16. package/dist/esm/reporting-containment.js +238 -0
  17. package/dist/esm/runtime.js +85 -5
  18. package/dist/esm/sinks.js +1334 -35
  19. package/dist/esm/transports.js +1335 -151
  20. package/dist/types/contracts.d.ts +1 -0
  21. package/dist/types/index.d.ts +1 -1
  22. package/dist/types/internal/prometheus-remote-write.d.ts +2 -0
  23. package/dist/types/internal/reporting-sink-http-error.d.ts +6 -0
  24. package/dist/types/internal/vendor-metric-payloads.d.ts +48 -0
  25. package/dist/types/local.d.ts +0 -6
  26. package/dist/types/reporting-containment.d.ts +2 -0
  27. package/dist/types/runtime.d.ts +1 -0
  28. package/dist/types/sinks.d.ts +134 -17
  29. package/dist/types/transports.d.ts +2 -0
  30. package/package.json +9 -3
  31. package/dist/cjs/internal-build.js +0 -4
  32. package/dist/esm/internal-build.js +0 -1
  33. package/dist/types/internal-build.d.ts +0 -1
@@ -1,7 +1,9 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import WebSocket from "ws";
2
3
  import { TrackingFieldSelector } from "./correlation.js";
3
4
  export const LOADSTRIKE_TRACE_ID_HEADER = "loadstrike-trace-id";
4
5
  export const LOADSTRIKE_TRACE_ID_TRACKING_FIELD = `header:${LOADSTRIKE_TRACE_ID_HEADER}`;
6
+ const NATIVE_GRPC_EXECUTION_UNAVAILABLE_MESSAGE = "Native gRPC execution is not available in this SDK version. Provide the endpoint Produce/Consume delegate instead.";
5
7
  class TrafficEndpointDefinitionModel {
6
8
  get JsonSerializerSettings() {
7
9
  return this.JsonSettings;
@@ -421,6 +423,15 @@ class GrpcEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
421
423
  }
422
424
  Validate() {
423
425
  super.Validate();
426
+ const hasModeAsyncDelegate = this.Mode === "Produce"
427
+ ? typeof this.ProduceAsync === "function"
428
+ : typeof this.ConsumeAsync === "function";
429
+ const hasModeDelegate = this.Mode === "Produce"
430
+ ? typeof this.Produce === "function" || hasModeAsyncDelegate
431
+ : typeof this.Consume === "function" || hasModeAsyncDelegate;
432
+ if (this.NativeClient && !hasModeDelegate) {
433
+ throw new Error(NATIVE_GRPC_EXECUTION_UNAVAILABLE_MESSAGE);
434
+ }
424
435
  requireNonEmptyString(this.Target, "Target must be provided for gRPC endpoint definitions.");
425
436
  requireNonEmptyString(this.ServiceName, "ServiceName must be provided for gRPC endpoint definitions.");
426
437
  requireNonEmptyString(this.MethodName, "MethodName must be provided for gRPC endpoint definitions.");
@@ -428,57 +439,204 @@ class GrpcEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
428
439
  if (this.DeadlineSeconds <= 0) {
429
440
  throw new RangeError("Deadline must be greater than zero.");
430
441
  }
431
- this.NativeClient?.validate();
432
- if (this.Mode === "Produce" && typeof this.ProduceAsync !== "function" && !this.NativeClient) {
442
+ if (this.Mode === "Produce" && !hasModeDelegate && !this.NativeClient) {
433
443
  throw new Error("ProduceAsync delegate must be provided when endpoint mode is Produce.");
434
444
  }
435
- if (this.Mode === "Consume" && typeof this.ConsumeAsync !== "function" && !this.NativeClient) {
445
+ if (this.Mode === "Consume" && !hasModeDelegate && !this.NativeClient) {
436
446
  throw new Error("ConsumeAsync delegate must be provided when endpoint mode is Consume.");
437
447
  }
438
448
  }
439
449
  }
450
+ const webSocketReconnectValidationInputs = new WeakMap();
451
+ const webSocketMessageValidationInputs = new WeakMap();
452
+ const webSocketExpectedValidationInputs = new WeakMap();
453
+ const webSocketNativeValidationInputs = new WeakMap();
454
+ const NATIVE_WEBSOCKET_RECONNECT_INPUT_FIELDS = [
455
+ ["MaxAttempts", "maxAttempts"],
456
+ ["DelayMs", "delayMs"],
457
+ ["DelaySeconds", "delaySeconds", "Delay", "delay"]
458
+ ];
459
+ const NATIVE_WEBSOCKET_MESSAGE_INPUT_FIELDS = [
460
+ ["Kind", "kind"],
461
+ ["TextPayload", "textPayload"],
462
+ ["BinaryPayload", "binaryPayload"]
463
+ ];
464
+ const NATIVE_WEBSOCKET_EXPECTED_INPUT_FIELDS = [
465
+ ["ContainsText", "containsText"],
466
+ ["ContainsBytes", "containsBytes"],
467
+ ["TimeoutMs", "timeoutMs"],
468
+ ["TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout"]
469
+ ];
470
+ const NATIVE_WEBSOCKET_CLIENT_INPUT_FIELDS = [
471
+ ["Headers", "headers"],
472
+ ["Cookies", "cookies"],
473
+ ["PingIntervalMs", "pingIntervalMs"],
474
+ ["PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval"],
475
+ ["Reconnect", "reconnect"],
476
+ ["Messages", "messages"],
477
+ ["ExpectedMessages", "expectedMessages"]
478
+ ];
479
+ function captureSelectedNativeWebSocketFields(source, fieldGroups) {
480
+ const captured = {};
481
+ for (const fieldGroup of fieldGroups) {
482
+ const canonicalName = fieldGroup[0];
483
+ for (const fieldName of fieldGroup) {
484
+ if (fieldName in source) {
485
+ captured[canonicalName] = source[fieldName];
486
+ break;
487
+ }
488
+ }
489
+ }
490
+ return captured;
491
+ }
492
+ function captureNativeWebSocketRecordEntries(value) {
493
+ if (!isRecord(value)) {
494
+ return value;
495
+ }
496
+ const captured = {};
497
+ for (const [key, entry] of Object.entries(value)) {
498
+ captured[key] = entry;
499
+ }
500
+ return captured;
501
+ }
502
+ function captureNativeWebSocketReconnectInput(source) {
503
+ return captureSelectedNativeWebSocketFields(source, NATIVE_WEBSOCKET_RECONNECT_INPUT_FIELDS);
504
+ }
505
+ function captureNativeWebSocketMessageInput(source) {
506
+ const captured = captureSelectedNativeWebSocketFields(source, NATIVE_WEBSOCKET_MESSAGE_INPUT_FIELDS);
507
+ if (Array.isArray(captured.BinaryPayload)) {
508
+ captured.BinaryPayload = Array.from(captured.BinaryPayload);
509
+ }
510
+ return captured;
511
+ }
512
+ function captureNativeWebSocketExpectedInput(source) {
513
+ const captured = captureSelectedNativeWebSocketFields(source, NATIVE_WEBSOCKET_EXPECTED_INPUT_FIELDS);
514
+ if (Array.isArray(captured.ContainsBytes)) {
515
+ captured.ContainsBytes = Array.from(captured.ContainsBytes);
516
+ }
517
+ return captured;
518
+ }
519
+ function captureNativeWebSocketOptionsInput(source) {
520
+ const captured = captureSelectedNativeWebSocketFields(source, NATIVE_WEBSOCKET_CLIENT_INPUT_FIELDS);
521
+ if ("Headers" in captured) {
522
+ captured.Headers = captureNativeWebSocketRecordEntries(captured.Headers);
523
+ }
524
+ if (Array.isArray(captured.Cookies)) {
525
+ captured.Cookies = Array.from(captured.Cookies);
526
+ }
527
+ if (isRecord(captured.Reconnect) && !(captured.Reconnect instanceof WebSocketReconnectPolicy)) {
528
+ captured.Reconnect = captureNativeWebSocketReconnectInput(captured.Reconnect);
529
+ }
530
+ if (Array.isArray(captured.Messages)) {
531
+ captured.Messages = captured.Messages.map((entry) => (entry instanceof WebSocketMessageSpec || !isRecord(entry)
532
+ ? entry
533
+ : captureNativeWebSocketMessageInput(entry)));
534
+ }
535
+ if (Array.isArray(captured.ExpectedMessages)) {
536
+ captured.ExpectedMessages = captured.ExpectedMessages.map((entry) => (entry instanceof WebSocketExpectedMessage || !isRecord(entry)
537
+ ? entry
538
+ : captureNativeWebSocketExpectedInput(entry)));
539
+ }
540
+ return captured;
541
+ }
542
+ function snapshotNativeWebSocketInput(value, seen = new WeakMap()) {
543
+ if (value == null || typeof value !== "object") {
544
+ return value;
545
+ }
546
+ if (value instanceof WebSocketReconnectPolicy
547
+ || value instanceof WebSocketMessageSpec
548
+ || value instanceof WebSocketExpectedMessage) {
549
+ return value;
550
+ }
551
+ const existing = seen.get(value);
552
+ if (existing !== undefined) {
553
+ return existing;
554
+ }
555
+ if (value instanceof Uint8Array) {
556
+ return new Uint8Array(value);
557
+ }
558
+ if (value instanceof ArrayBuffer) {
559
+ return value.slice(0);
560
+ }
561
+ if (Array.isArray(value)) {
562
+ const clone = [];
563
+ seen.set(value, clone);
564
+ for (const entry of value) {
565
+ clone.push(snapshotNativeWebSocketInput(entry, seen));
566
+ }
567
+ return clone;
568
+ }
569
+ const clone = {};
570
+ seen.set(value, clone);
571
+ for (const [key, entry] of Object.entries(value)) {
572
+ clone[key] = snapshotNativeWebSocketInput(entry, seen);
573
+ }
574
+ return clone;
575
+ }
576
+ function defineSynchronizedPublicAlias(target, alias, read, write) {
577
+ Object.defineProperty(target, alias, {
578
+ configurable: true,
579
+ enumerable: true,
580
+ get: read,
581
+ set: write
582
+ });
583
+ }
440
584
  export class WebSocketReconnectPolicy {
441
585
  constructor(initial = {}) {
442
586
  this.MaxAttempts = 0;
443
587
  this.maxAttempts = 0;
444
588
  this.DelaySeconds = 1;
445
589
  this.delaySeconds = 1;
446
- const raw = asRecordOrEmpty(initial);
447
- this.MaxAttempts = Math.trunc(pickOptionalEndpointNumber(raw, "MaxAttempts", "maxAttempts") ?? 0);
590
+ defineSynchronizedPublicAlias(this, "maxAttempts", () => this.MaxAttempts, (value) => {
591
+ this.MaxAttempts = value;
592
+ });
593
+ defineSynchronizedPublicAlias(this, "delaySeconds", () => this.DelaySeconds, (value) => {
594
+ this.DelaySeconds = value;
595
+ });
596
+ const raw = captureNativeWebSocketReconnectInput(asRecordOrEmpty(initial));
597
+ webSocketReconnectValidationInputs.set(this, snapshotNativeWebSocketInput(raw));
598
+ this.MaxAttempts = (pickEndpointValue(raw, "MaxAttempts", "maxAttempts") ?? 0);
448
599
  this.maxAttempts = this.MaxAttempts;
449
- const delayMs = pickOptionalEndpointNumber(raw, "DelayMs", "delayMs");
600
+ const delayMs = pickEndpointValue(raw, "DelayMs", "delayMs");
601
+ const delaySeconds = pickEndpointValue(raw, "DelaySeconds", "delaySeconds", "Delay", "delay");
450
602
  this.DelaySeconds = delayMs != null
451
603
  ? delayMs / 1000
452
- : (pickOptionalEndpointNumber(raw, "DelaySeconds", "delaySeconds", "Delay", "delay") ?? 1);
604
+ : (delaySeconds ?? 1);
453
605
  this.delaySeconds = this.DelaySeconds;
454
606
  }
455
607
  Validate() {
456
608
  this.validate();
457
609
  }
458
610
  validate() {
459
- if (this.MaxAttempts < 0) {
460
- throw new RangeError("MaxAttempts cannot be negative.");
461
- }
462
- if (this.DelaySeconds < 0) {
463
- throw new RangeError("Delay cannot be negative.");
464
- }
611
+ validateNativeWebSocketReconnectPolicy(webSocketReconnectValidationInputs.get(this) ?? {});
612
+ validateNativeWebSocketReconnectPolicy(this);
465
613
  }
466
614
  }
467
615
  export class WebSocketMessageSpec {
468
616
  constructor(initial = {}) {
469
617
  this.Kind = "Text";
470
618
  this.kind = "Text";
471
- const raw = asRecordOrEmpty(initial);
619
+ defineSynchronizedPublicAlias(this, "kind", () => this.Kind, (value) => {
620
+ this.Kind = value;
621
+ });
622
+ defineSynchronizedPublicAlias(this, "textPayload", () => this.TextPayload, (value) => {
623
+ this.TextPayload = value;
624
+ });
625
+ defineSynchronizedPublicAlias(this, "binaryPayload", () => this.BinaryPayload, (value) => {
626
+ this.BinaryPayload = value;
627
+ });
628
+ const raw = captureNativeWebSocketMessageInput(asRecordOrEmpty(initial));
629
+ webSocketMessageValidationInputs.set(this, snapshotNativeWebSocketInput(raw));
472
630
  this.Kind = pickOptionalEndpointString(raw, "Kind", "kind") ?? "Text";
473
631
  this.kind = this.Kind;
474
- this.TextPayload = pickOptionalEndpointString(raw, "TextPayload", "textPayload");
632
+ this.TextPayload = pickOptionalEndpointExactString(raw, "TextPayload", "textPayload");
475
633
  this.textPayload = this.TextPayload;
476
634
  const binary = pickEndpointValue(raw, "BinaryPayload", "binaryPayload");
477
635
  if (binary instanceof Uint8Array) {
478
636
  this.BinaryPayload = binary;
479
637
  }
480
638
  else if (Array.isArray(binary)) {
481
- this.BinaryPayload = Uint8Array.from(binary.map((value) => Number(value) & 0xff));
639
+ this.BinaryPayload = Uint8Array.from(binary.map((value) => Number(value)));
482
640
  }
483
641
  this.binaryPayload = this.BinaryPayload;
484
642
  }
@@ -492,42 +650,48 @@ export class WebSocketMessageSpec {
492
650
  this.validate();
493
651
  }
494
652
  validate() {
495
- if (this.Kind.toLowerCase() === "text" && this.TextPayload == null) {
496
- throw new Error("Text WebSocket messages require TextPayload.");
497
- }
498
- if (this.Kind.toLowerCase() === "binary" && this.BinaryPayload == null) {
499
- throw new Error("Binary WebSocket messages require BinaryPayload.");
500
- }
653
+ validateOriginalNativeWebSocketMessage(webSocketMessageValidationInputs.get(this) ?? {});
654
+ validateNativeWebSocketMessage(this);
501
655
  }
502
656
  }
503
657
  export class WebSocketExpectedMessage {
504
658
  constructor(initial = {}) {
505
659
  this.TimeoutSeconds = 30;
506
660
  this.timeoutSeconds = 30;
507
- const raw = asRecordOrEmpty(initial);
508
- this.ContainsText = pickOptionalEndpointString(raw, "ContainsText", "containsText");
661
+ defineSynchronizedPublicAlias(this, "containsText", () => this.ContainsText, (value) => {
662
+ this.ContainsText = value;
663
+ });
664
+ defineSynchronizedPublicAlias(this, "containsBytes", () => this.ContainsBytes, (value) => {
665
+ this.ContainsBytes = value;
666
+ });
667
+ defineSynchronizedPublicAlias(this, "timeoutSeconds", () => this.TimeoutSeconds, (value) => {
668
+ this.TimeoutSeconds = value;
669
+ });
670
+ const raw = captureNativeWebSocketExpectedInput(asRecordOrEmpty(initial));
671
+ webSocketExpectedValidationInputs.set(this, snapshotNativeWebSocketInput(raw));
672
+ this.ContainsText = pickOptionalEndpointExactString(raw, "ContainsText", "containsText");
509
673
  this.containsText = this.ContainsText;
510
674
  const bytes = pickEndpointValue(raw, "ContainsBytes", "containsBytes");
511
675
  if (bytes instanceof Uint8Array) {
512
676
  this.ContainsBytes = bytes;
513
677
  }
514
678
  else if (Array.isArray(bytes)) {
515
- this.ContainsBytes = Uint8Array.from(bytes.map((value) => Number(value) & 0xff));
679
+ this.ContainsBytes = Uint8Array.from(bytes.map((value) => Number(value)));
516
680
  }
517
681
  this.containsBytes = this.ContainsBytes;
518
- const timeoutMs = pickOptionalEndpointNumber(raw, "TimeoutMs", "timeoutMs");
682
+ const timeoutMs = pickEndpointValue(raw, "TimeoutMs", "timeoutMs");
683
+ const timeoutSeconds = pickEndpointValue(raw, "TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout");
519
684
  this.TimeoutSeconds = timeoutMs != null
520
685
  ? timeoutMs / 1000
521
- : (pickOptionalEndpointNumber(raw, "TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout") ?? 30);
686
+ : (timeoutSeconds ?? 30);
522
687
  this.timeoutSeconds = this.TimeoutSeconds;
523
688
  }
524
689
  Validate() {
525
690
  this.validate();
526
691
  }
527
692
  validate() {
528
- if (this.TimeoutSeconds <= 0) {
529
- throw new RangeError("Timeout must be greater than zero.");
530
- }
693
+ validateNativeWebSocketExpectedMessage(webSocketExpectedValidationInputs.get(this) ?? {});
694
+ validateNativeWebSocketExpectedMessage(this);
531
695
  }
532
696
  }
533
697
  export class WebSocketNativeClientOptions {
@@ -542,13 +706,32 @@ export class WebSocketNativeClientOptions {
542
706
  this.messages = [];
543
707
  this.ExpectedMessages = [];
544
708
  this.expectedMessages = [];
545
- const raw = asRecordOrEmpty(initial);
709
+ defineSynchronizedPublicAlias(this, "headers", () => this.Headers, (value) => {
710
+ this.Headers = value;
711
+ });
712
+ defineSynchronizedPublicAlias(this, "cookies", () => this.Cookies, (value) => {
713
+ this.Cookies = value;
714
+ });
715
+ defineSynchronizedPublicAlias(this, "pingIntervalSeconds", () => this.PingIntervalSeconds, (value) => {
716
+ this.PingIntervalSeconds = value;
717
+ });
718
+ defineSynchronizedPublicAlias(this, "reconnect", () => this.Reconnect, (value) => {
719
+ this.Reconnect = value;
720
+ });
721
+ defineSynchronizedPublicAlias(this, "messages", () => this.Messages, (value) => {
722
+ this.Messages = value;
723
+ });
724
+ defineSynchronizedPublicAlias(this, "expectedMessages", () => this.ExpectedMessages, (value) => {
725
+ this.ExpectedMessages = value;
726
+ });
727
+ const raw = captureNativeWebSocketOptionsInput(asRecordOrEmpty(initial));
728
+ webSocketNativeValidationInputs.set(this, snapshotNativeWebSocketInput(raw));
546
729
  this.Headers = pickEndpointStringRecord(raw, "Headers", "headers");
547
730
  this.headers = this.Headers;
548
731
  this.Cookies = pickEndpointStringArray(raw, "Cookies", "cookies") ?? [];
549
732
  this.cookies = this.Cookies;
550
- const pingMs = pickOptionalEndpointNumber(raw, "PingIntervalMs", "pingIntervalMs");
551
- const pingSeconds = pickOptionalEndpointNumber(raw, "PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval");
733
+ const pingMs = pickEndpointValue(raw, "PingIntervalMs", "pingIntervalMs");
734
+ const pingSeconds = pickEndpointValue(raw, "PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval");
552
735
  this.PingIntervalSeconds = pingMs != null ? pingMs / 1000 : pingSeconds;
553
736
  this.pingIntervalSeconds = this.PingIntervalSeconds;
554
737
  const reconnect = pickEndpointValue(raw, "Reconnect", "reconnect");
@@ -565,16 +748,8 @@ export class WebSocketNativeClientOptions {
565
748
  this.validate();
566
749
  }
567
750
  validate() {
568
- if (this.PingIntervalSeconds != null && this.PingIntervalSeconds <= 0) {
569
- throw new RangeError("PingInterval must be greater than zero.");
570
- }
571
- this.Reconnect.validate();
572
- for (const message of this.Messages) {
573
- message.validate();
574
- }
575
- for (const expected of this.ExpectedMessages) {
576
- expected.validate();
577
- }
751
+ validateNativeWebSocketOptions(webSocketNativeValidationInputs.get(this) ?? {});
752
+ validateNativeWebSocketOptions(this);
578
753
  }
579
754
  }
580
755
  class WebSocketEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
@@ -590,6 +765,23 @@ class WebSocketEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
590
765
  }
591
766
  Validate() {
592
767
  super.Validate();
768
+ const hasModeAsyncDelegate = this.Mode === "Produce"
769
+ ? typeof this.ProduceAsync === "function"
770
+ : typeof this.ConsumeAsync === "function";
771
+ const hasModeDelegate = this.Mode === "Produce"
772
+ ? typeof this.Produce === "function" || hasModeAsyncDelegate
773
+ : typeof this.Consume === "function" || hasModeAsyncDelegate;
774
+ if (this.NativeClient != null && !hasModeDelegate) {
775
+ if (this.Mode === "Consume" && this.TrackingField.trim().toLowerCase().startsWith("header:")) {
776
+ throw new Error("Native WebSocket Consume cannot use a header TrackingField because WebSocket messages do not contain HTTP response headers.");
777
+ }
778
+ if (this.Mode === "Consume" && this.GatherByField?.trim().toLowerCase().startsWith("header:")) {
779
+ throw new Error("Native WebSocket Consume cannot use a header GatherByField because WebSocket messages do not contain HTTP response headers.");
780
+ }
781
+ validateNativeWebSocketSubprotocols(this.Subprotocols);
782
+ validateNativeWebSocketHeaderLayer(this.ConnectionMetadata, "ConnectionMetadata");
783
+ validateNativeWebSocketOptionsWithInstance(this.NativeClient);
784
+ }
593
785
  const url = requireNonEmptyString(this.Url, "Url must be provided for WebSocket endpoint definitions.");
594
786
  let parsed;
595
787
  try {
@@ -598,20 +790,17 @@ class WebSocketEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
598
790
  catch {
599
791
  throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
600
792
  }
601
- if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
793
+ if ((parsed.protocol !== "ws:" && parsed.protocol !== "wss:")
794
+ || !parsed.hostname
795
+ || Boolean(parsed.hash)) {
602
796
  throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
603
797
  }
604
- if (this.ConnectTimeoutSeconds <= 0) {
605
- throw new RangeError("ConnectTimeout must be greater than zero.");
606
- }
607
- if (this.CloseTimeoutSeconds <= 0) {
608
- throw new RangeError("CloseTimeout must be greater than zero.");
609
- }
610
- this.NativeClient?.validate();
611
- if (this.Mode === "Produce" && typeof this.ProduceAsync !== "function" && !this.NativeClient) {
798
+ validateNativeWebSocketTimerDelay(undefined, this.ConnectTimeoutSeconds, "ConnectTimeout");
799
+ validateNativeWebSocketTimerDelay(undefined, this.CloseTimeoutSeconds, "CloseTimeout");
800
+ if (this.Mode === "Produce" && !hasModeDelegate && this.NativeClient == null) {
612
801
  throw new Error("ProduceAsync delegate must be provided when endpoint mode is Produce.");
613
802
  }
614
- if (this.Mode === "Consume" && typeof this.ConsumeAsync !== "function" && !this.NativeClient) {
803
+ if (this.Mode === "Consume" && !hasModeDelegate && this.NativeClient == null) {
615
804
  throw new Error("ConsumeAsync delegate must be provided when endpoint mode is Consume.");
616
805
  }
617
806
  }
@@ -1138,25 +1327,31 @@ function initializeWebSocketEndpointDefinitionModel(target, initial) {
1138
1327
  if (url) {
1139
1328
  target.Url = url;
1140
1329
  }
1141
- const subprotocols = pickEndpointStringArray(raw, "Subprotocols", "subprotocols");
1142
- if (subprotocols) {
1143
- target.Subprotocols = subprotocols;
1330
+ const subprotocols = pickEndpointValue(raw, "Subprotocols", "subprotocols");
1331
+ if (subprotocols != null) {
1332
+ target.Subprotocols = normalizeWebSocketEndpointStringArrayInput(subprotocols);
1144
1333
  }
1145
- const connectMs = pickOptionalEndpointNumber(raw, "ConnectTimeoutMs", "connectTimeoutMs");
1146
- const connectSeconds = pickOptionalEndpointNumber(raw, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout");
1334
+ const connectMs = pickEndpointValue(raw, "ConnectTimeoutMs", "connectTimeoutMs");
1335
+ const connectSeconds = pickEndpointValue(raw, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout");
1147
1336
  if (connectMs != null) {
1148
- target.ConnectTimeoutSeconds = connectMs / 1000;
1337
+ const normalized = normalizeWebSocketEndpointNumberInput(connectMs);
1338
+ target.ConnectTimeoutSeconds = typeof normalized === "number"
1339
+ ? normalized / 1000
1340
+ : normalized;
1149
1341
  }
1150
1342
  else if (connectSeconds != null) {
1151
- target.ConnectTimeoutSeconds = connectSeconds;
1343
+ target.ConnectTimeoutSeconds = normalizeWebSocketEndpointNumberInput(connectSeconds);
1152
1344
  }
1153
- const closeMs = pickOptionalEndpointNumber(raw, "CloseTimeoutMs", "closeTimeoutMs");
1154
- const closeSeconds = pickOptionalEndpointNumber(raw, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout");
1345
+ const closeMs = pickEndpointValue(raw, "CloseTimeoutMs", "closeTimeoutMs");
1346
+ const closeSeconds = pickEndpointValue(raw, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout");
1155
1347
  if (closeMs != null) {
1156
- target.CloseTimeoutSeconds = closeMs / 1000;
1348
+ const normalized = normalizeWebSocketEndpointNumberInput(closeMs);
1349
+ target.CloseTimeoutSeconds = typeof normalized === "number"
1350
+ ? normalized / 1000
1351
+ : normalized;
1157
1352
  }
1158
1353
  else if (closeSeconds != null) {
1159
- target.CloseTimeoutSeconds = closeSeconds;
1354
+ target.CloseTimeoutSeconds = normalizeWebSocketEndpointNumberInput(closeSeconds);
1160
1355
  }
1161
1356
  const produce = pickEndpointFunction(raw, "Produce", "produce");
1162
1357
  if (produce) {
@@ -1175,15 +1370,45 @@ function initializeWebSocketEndpointDefinitionModel(target, initial) {
1175
1370
  target.ConsumeAsync = consumeAsync;
1176
1371
  }
1177
1372
  if (hasAnyEndpointField(raw, ["ConnectionMetadata", "connectionMetadata"])) {
1178
- target.ConnectionMetadata = pickEndpointStringRecord(raw, "ConnectionMetadata", "connectionMetadata");
1373
+ target.ConnectionMetadata = normalizeWebSocketEndpointMetadataInput(pickEndpointValue(raw, "ConnectionMetadata", "connectionMetadata"));
1179
1374
  }
1180
1375
  if (hasAnyEndpointField(raw, ["NativeClient", "nativeClient"])) {
1181
1376
  const nativeClient = pickEndpointValue(raw, "NativeClient", "nativeClient");
1182
1377
  target.NativeClient = nativeClient instanceof WebSocketNativeClientOptions
1183
1378
  ? nativeClient
1184
- : new WebSocketNativeClientOptions(asRecordOrEmpty(nativeClient));
1379
+ : isRecord(nativeClient)
1380
+ ? new WebSocketNativeClientOptions(nativeClient)
1381
+ : nativeClient;
1185
1382
  }
1186
1383
  }
1384
+ function normalizeWebSocketEndpointNumberInput(value) {
1385
+ if (typeof value === "number") {
1386
+ return value;
1387
+ }
1388
+ if (typeof value === "string" && value.trim()) {
1389
+ const parsed = Number(value);
1390
+ if (Number.isFinite(parsed)) {
1391
+ return parsed;
1392
+ }
1393
+ }
1394
+ return value;
1395
+ }
1396
+ function normalizeWebSocketEndpointStringArrayInput(value) {
1397
+ if (!Array.isArray(value)) {
1398
+ return value;
1399
+ }
1400
+ return value.map((entry) => typeof entry === "string" ? entry.trim() : entry);
1401
+ }
1402
+ function normalizeWebSocketEndpointMetadataInput(value) {
1403
+ if (!isRecord(value)) {
1404
+ return value;
1405
+ }
1406
+ const normalized = {};
1407
+ for (const [key, entry] of Object.entries(value)) {
1408
+ normalized[key] = entry;
1409
+ }
1410
+ return normalized;
1411
+ }
1187
1412
  function validateTrafficEndpointDefinitionModel(target) {
1188
1413
  requireNonEmptyString(target.Name, "Endpoint name must be provided.");
1189
1414
  if (target.Mode !== "Produce" && target.Mode !== "Consume") {
@@ -1517,13 +1742,12 @@ class CallbackAdapter {
1517
1742
  return null;
1518
1743
  }
1519
1744
  const resolved = prepareProducedPayload(this.endpoint, payload);
1520
- const structuredDelegate = resolveStructuredProduceDelegate(this.endpoint);
1521
- if (structuredDelegate) {
1522
- return await invokeStructuredProduceDelegate(this.endpoint, resolved, structuredDelegate);
1745
+ const callbacks = selectProduceCallbackGroup(this.endpoint);
1746
+ if (callbacks.asyncDelegate) {
1747
+ return await invokeStructuredProduceDelegate(this.endpoint, resolved, callbacks.asyncDelegate);
1523
1748
  }
1524
- const delegate = resolveProduceDelegate(this.endpoint);
1525
- if (delegate) {
1526
- const next = await delegate(clonePayload(resolved));
1749
+ if (callbacks.syncDelegate) {
1750
+ const next = await callbacks.syncDelegate(clonePayload(resolved));
1527
1751
  return next == null ? null : normalizeTrackingPayload(next);
1528
1752
  }
1529
1753
  return clonePayload(resolved);
@@ -1535,19 +1759,17 @@ class CallbackAdapter {
1535
1759
  if (this.consumeQueue.length > 0) {
1536
1760
  return this.consumeQueue.shift() ?? null;
1537
1761
  }
1538
- const structuredDelegate = resolveStructuredConsumeDelegate(this.endpoint);
1539
- if (structuredDelegate) {
1540
- const next = await structuredDelegate();
1541
- return normalizeConsumedDelegatePayload(next);
1542
- }
1543
- const streamDelegate = resolveStructuredConsumeStreamDelegate(this.endpoint);
1544
- if (streamDelegate) {
1545
- this.ensureConsumeStreamStarted(streamDelegate);
1762
+ const callbacks = selectConsumeCallbackGroup(this.endpoint);
1763
+ if (callbacks.asyncDelegate) {
1764
+ if (callbacks.asyncDelegate.length === 0) {
1765
+ const next = await callbacks.asyncDelegate();
1766
+ return normalizeConsumedDelegatePayload(next);
1767
+ }
1768
+ this.ensureConsumeStreamStarted(callbacks.asyncDelegate);
1546
1769
  return this.consumeQueue.shift() ?? null;
1547
1770
  }
1548
- const delegate = resolveConsumeDelegate(this.endpoint);
1549
- if (delegate) {
1550
- const next = await delegate();
1771
+ if (callbacks.syncDelegate) {
1772
+ const next = await callbacks.syncDelegate();
1551
1773
  return next == null ? null : normalizeTrackingPayload(next);
1552
1774
  }
1553
1775
  return this.defaultPayload();
@@ -2313,7 +2535,610 @@ class DelegateStreamEndpointAdapter extends CallbackAdapter {
2313
2535
  }
2314
2536
  class GrpcEndpointAdapter extends CallbackAdapter {
2315
2537
  }
2538
+ const NATIVE_WEBSOCKET_FRAME_QUEUE_CAPACITY = 64;
2539
+ const NATIVE_WEBSOCKET_FRAME_QUEUE_MAX_BYTES = 1024 * 1024;
2540
+ const NATIVE_WEBSOCKET_MAX_PAYLOAD_BYTES = 1024 * 1024;
2541
+ class NativeWebSocketAbortError extends Error {
2542
+ constructor() {
2543
+ super("WebSocket native client was interrupted.");
2544
+ this.name = "AbortError";
2545
+ }
2546
+ }
2547
+ class NativeWebSocketPeerClosedError extends Error {
2548
+ constructor(code, reason) {
2549
+ super(code == null
2550
+ ? "WebSocket peer closed before the operation completed."
2551
+ : `WebSocket peer closed before the operation completed (code ${code}${reason ? `: ${reason}` : ""}).`);
2552
+ }
2553
+ }
2554
+ class NativeWebSocketReceiveTimeoutError extends Error {
2555
+ constructor() {
2556
+ super("WebSocket receive timed out before the expected message arrived.");
2557
+ }
2558
+ }
2559
+ class NativeWebSocketSendTimeoutError extends Error {
2560
+ constructor() {
2561
+ super("WebSocket send timed out before the frame was written.");
2562
+ this.code = "ETIMEDOUT";
2563
+ }
2564
+ }
2565
+ class NativeWebSocketQueueOverflowError extends Error {
2566
+ constructor() {
2567
+ super("WebSocket receive queue exceeded its bounded frame limit.");
2568
+ }
2569
+ }
2570
+ class NativeWebSocketFrameQueue {
2571
+ constructor(socket) {
2572
+ this.socket = socket;
2573
+ this.frames = [];
2574
+ this.queuedBytes = 0;
2575
+ this.terminalError = null;
2576
+ this.wakeResolver = null;
2577
+ this.onMessage = (data, isBinary) => {
2578
+ const copy = copyNativeWebSocketRawData(data);
2579
+ if (this.frames.length >= NATIVE_WEBSOCKET_FRAME_QUEUE_CAPACITY
2580
+ || this.queuedBytes + copy.byteLength > NATIVE_WEBSOCKET_FRAME_QUEUE_MAX_BYTES) {
2581
+ this.fail(new NativeWebSocketQueueOverflowError());
2582
+ this.socket.terminate();
2583
+ return;
2584
+ }
2585
+ this.frames.push({ data: copy, isBinary });
2586
+ this.queuedBytes += copy.byteLength;
2587
+ this.wake();
2588
+ };
2589
+ this.onError = (error) => {
2590
+ this.fail(error);
2591
+ };
2592
+ this.onClose = (code, reason) => {
2593
+ this.fail(new NativeWebSocketPeerClosedError(code, reason.toString("utf8")));
2594
+ };
2595
+ socket.on("message", this.onMessage);
2596
+ socket.on("error", this.onError);
2597
+ socket.on("close", this.onClose);
2598
+ }
2599
+ async next(deadlineMs, signal) {
2600
+ while (true) {
2601
+ if (signal.aborted) {
2602
+ throw new NativeWebSocketAbortError();
2603
+ }
2604
+ if (isNativeWebSocketOverflowError(this.terminalError)) {
2605
+ throw this.terminalError;
2606
+ }
2607
+ const frame = this.frames.shift();
2608
+ if (frame) {
2609
+ this.queuedBytes -= frame.data.byteLength;
2610
+ return frame;
2611
+ }
2612
+ if (this.terminalError) {
2613
+ throw this.terminalError;
2614
+ }
2615
+ const remainingMs = deadlineMs - Date.now();
2616
+ if (remainingMs <= 0) {
2617
+ throw new NativeWebSocketReceiveTimeoutError();
2618
+ }
2619
+ await this.waitForFrame(remainingMs, signal);
2620
+ }
2621
+ }
2622
+ dispose() {
2623
+ this.socket.off("message", this.onMessage);
2624
+ this.socket.off("error", this.onError);
2625
+ this.socket.off("close", this.onClose);
2626
+ this.wake();
2627
+ }
2628
+ waitForFrame(timeoutMs, signal) {
2629
+ return new Promise((resolve, reject) => {
2630
+ let settled = false;
2631
+ const finish = (error) => {
2632
+ if (settled) {
2633
+ return;
2634
+ }
2635
+ settled = true;
2636
+ clearTimeout(timeout);
2637
+ signal.removeEventListener("abort", onAbort);
2638
+ if (this.wakeResolver === onWake) {
2639
+ this.wakeResolver = null;
2640
+ }
2641
+ if (error) {
2642
+ reject(error);
2643
+ }
2644
+ else {
2645
+ resolve();
2646
+ }
2647
+ };
2648
+ const onWake = () => finish();
2649
+ const onAbort = () => finish(new NativeWebSocketAbortError());
2650
+ const timeout = setTimeout(() => finish(new NativeWebSocketReceiveTimeoutError()), timeoutMs);
2651
+ this.wakeResolver = onWake;
2652
+ signal.addEventListener("abort", onAbort, { once: true });
2653
+ if (signal.aborted) {
2654
+ onAbort();
2655
+ }
2656
+ });
2657
+ }
2658
+ fail(error) {
2659
+ this.terminalError ?? (this.terminalError = error);
2660
+ this.wake();
2661
+ }
2662
+ wake() {
2663
+ const resolve = this.wakeResolver;
2664
+ this.wakeResolver = null;
2665
+ resolve?.();
2666
+ }
2667
+ }
2668
+ function isNativeWebSocketOverflowError(error) {
2669
+ return error instanceof NativeWebSocketQueueOverflowError
2670
+ || String(error?.code ?? "") === "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH";
2671
+ }
2672
+ function copyNativeWebSocketRawData(data) {
2673
+ if (Array.isArray(data)) {
2674
+ return new Uint8Array(Buffer.concat(data));
2675
+ }
2676
+ if (data instanceof ArrayBuffer) {
2677
+ return new Uint8Array(data.slice(0));
2678
+ }
2679
+ return new Uint8Array(Buffer.from(data));
2680
+ }
2316
2681
  class WebSocketEndpointAdapter extends CallbackAdapter {
2682
+ constructor() {
2683
+ super(...arguments);
2684
+ this.nativeAbort = new AbortController();
2685
+ this.activeSockets = new Set();
2686
+ }
2687
+ async produce(payload) {
2688
+ if (this.endpoint.mode !== "Produce") {
2689
+ return null;
2690
+ }
2691
+ const delegates = selectProduceCallbackGroup(this.endpoint);
2692
+ if (delegates.asyncDelegate || delegates.syncDelegate) {
2693
+ return super.produce(payload);
2694
+ }
2695
+ const resolved = prepareProducedPayload(this.endpoint, payload);
2696
+ const configuration = normalizeNativeWebSocketConfiguration(this.endpoint, resolved);
2697
+ await this.executeNative(configuration);
2698
+ resolved.producedUtc ?? (resolved.producedUtc = new Date().toISOString());
2699
+ return resolved;
2700
+ }
2701
+ async consume() {
2702
+ if (this.endpoint.mode !== "Consume") {
2703
+ return null;
2704
+ }
2705
+ const delegates = selectConsumeCallbackGroup(this.endpoint);
2706
+ if (delegates.asyncDelegate || delegates.syncDelegate) {
2707
+ return super.consume();
2708
+ }
2709
+ const configuration = normalizeNativeWebSocketConfiguration(this.endpoint);
2710
+ return this.executeNative(configuration);
2711
+ }
2712
+ interrupt() {
2713
+ if (!this.nativeAbort.signal.aborted) {
2714
+ this.nativeAbort.abort();
2715
+ }
2716
+ for (const socket of this.activeSockets) {
2717
+ socket.terminate();
2718
+ }
2719
+ }
2720
+ async dispose() {
2721
+ this.interrupt();
2722
+ }
2723
+ async executeNative(configuration) {
2724
+ const totalAttempts = configuration.maxAttempts + 1;
2725
+ let lastError;
2726
+ for (let attempt = 1; attempt <= totalAttempts; attempt += 1) {
2727
+ if (this.nativeAbort.signal.aborted) {
2728
+ throw new NativeWebSocketAbortError();
2729
+ }
2730
+ try {
2731
+ return await this.executeNativeOnce(configuration);
2732
+ }
2733
+ catch (error) {
2734
+ lastError = error;
2735
+ if (!isReconnectableNativeWebSocketError(error) || attempt >= totalAttempts) {
2736
+ throw error;
2737
+ }
2738
+ await waitForNativeWebSocketRetry(configuration.reconnectDelayMs, this.nativeAbort.signal);
2739
+ }
2740
+ }
2741
+ throw lastError;
2742
+ }
2743
+ async executeNativeOnce(configuration) {
2744
+ const options = {
2745
+ headers: configuration.headers,
2746
+ handshakeTimeout: configuration.connectTimeoutMs,
2747
+ followRedirects: false,
2748
+ maxPayload: NATIVE_WEBSOCKET_MAX_PAYLOAD_BYTES
2749
+ };
2750
+ const socket = configuration.subprotocols.length > 0
2751
+ ? new WebSocket(configuration.url, configuration.subprotocols, options)
2752
+ : new WebSocket(configuration.url, options);
2753
+ this.activeSockets.add(socket);
2754
+ const frames = new NativeWebSocketFrameQueue(socket);
2755
+ let pingTimer;
2756
+ try {
2757
+ await waitForNativeWebSocketOpen(socket, configuration.connectTimeoutMs, this.nativeAbort.signal);
2758
+ if (configuration.pingIntervalMs != null) {
2759
+ pingTimer = setInterval(() => {
2760
+ if (socket.readyState === WebSocket.OPEN) {
2761
+ socket.ping();
2762
+ }
2763
+ }, configuration.pingIntervalMs);
2764
+ pingTimer.unref?.();
2765
+ }
2766
+ for (const message of configuration.messages) {
2767
+ await sendNativeWebSocketFrame(socket, message, configuration.connectTimeoutMs, this.nativeAbort.signal);
2768
+ }
2769
+ if (this.endpoint.mode === "Produce") {
2770
+ for (const expected of configuration.expectedMessages) {
2771
+ await receiveExpectedNativeWebSocketFrame(frames, expected, this.nativeAbort.signal);
2772
+ }
2773
+ return null;
2774
+ }
2775
+ if (configuration.expectedMessages.length > 0) {
2776
+ let payload = null;
2777
+ for (const expected of configuration.expectedMessages) {
2778
+ const frame = await receiveExpectedNativeWebSocketFrame(frames, expected, this.nativeAbort.signal);
2779
+ payload = nativeWebSocketFrameToTrackingPayload(frame);
2780
+ }
2781
+ return payload;
2782
+ }
2783
+ const frame = await frames.next(Date.now() + configuration.connectTimeoutMs, this.nativeAbort.signal);
2784
+ return nativeWebSocketFrameToTrackingPayload(frame);
2785
+ }
2786
+ finally {
2787
+ if (pingTimer) {
2788
+ clearInterval(pingTimer);
2789
+ }
2790
+ frames.dispose();
2791
+ await closeNativeWebSocket(socket, configuration.closeTimeoutMs);
2792
+ this.activeSockets.delete(socket);
2793
+ }
2794
+ }
2795
+ }
2796
+ function normalizeNativeWebSocketConfiguration(endpoint, payload) {
2797
+ const options = asRecordOrEmpty(endpoint.webSocket);
2798
+ const native = asRecordOrEmpty(pickEndpointValue(options, "NativeClient", "nativeClient"));
2799
+ const reconnect = asRecordOrEmpty(pickEndpointValue(native, "Reconnect", "reconnect"));
2800
+ if (payload) {
2801
+ validateNativeWebSocketHeaderLayer(payload.headers ?? {}, "payload headers");
2802
+ }
2803
+ const headers = mergeNativeWebSocketHeaders(toStringRecord(pickEndpointValue(native, "Headers", "headers")), resolveConnectionMetadata(endpoint), payload?.headers ?? {});
2804
+ const configuredCookies = pickEndpointValue(native, "Cookies", "cookies");
2805
+ const cookies = Array.isArray(configuredCookies)
2806
+ ? configuredCookies.map((value) => String(value).trim())
2807
+ : [];
2808
+ if (cookies.length > 0) {
2809
+ const cookieName = Object.keys(headers).find((name) => name.toLowerCase() === "cookie");
2810
+ const existing = cookieName ? headers[cookieName] : "";
2811
+ if (cookieName) {
2812
+ delete headers[cookieName];
2813
+ }
2814
+ headers.Cookie = [existing, ...cookies].filter(Boolean).join("; ");
2815
+ }
2816
+ const rawMessages = pickEndpointValue(native, "Messages", "messages");
2817
+ const messages = Array.isArray(rawMessages)
2818
+ ? rawMessages.map(normalizeNativeWebSocketMessage)
2819
+ : [];
2820
+ if (messages.length === 0 && payload) {
2821
+ const projected = projectNativeWebSocketPayload(payload.body);
2822
+ if (projected) {
2823
+ messages.push(projected);
2824
+ }
2825
+ }
2826
+ if (endpoint.mode === "Produce" && messages.length === 0) {
2827
+ throw new Error("Native WebSocket Produce requires at least one configured message or a projected payload body.");
2828
+ }
2829
+ const rawExpected = pickEndpointValue(native, "ExpectedMessages", "expectedMessages");
2830
+ const expectedMessages = Array.isArray(rawExpected)
2831
+ ? rawExpected.map(normalizeNativeWebSocketExpectedMessage)
2832
+ : [];
2833
+ const connectTimeoutMs = nativeWebSocketTimeoutMs(options, 30000, ["ConnectTimeoutMs", "connectTimeoutMs"], ["ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout"]);
2834
+ const closeTimeoutMs = nativeWebSocketTimeoutMs(options, 5000, ["CloseTimeoutMs", "closeTimeoutMs"], ["CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout"]);
2835
+ const pingMsValue = pickEndpointValue(native, "PingIntervalMs", "pingIntervalMs");
2836
+ const pingSecondsValue = pickEndpointValue(native, "PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval");
2837
+ const pingIntervalMs = pingMsValue != null
2838
+ ? Number(pingMsValue)
2839
+ : (pingSecondsValue != null ? Number(pingSecondsValue) * 1000 : undefined);
2840
+ const delayMsValue = pickEndpointValue(reconnect, "DelayMs", "delayMs");
2841
+ const delaySecondsValue = pickEndpointValue(reconnect, "DelaySeconds", "delaySeconds", "Delay", "delay");
2842
+ return {
2843
+ url: optionString(options, "Url", "url"),
2844
+ subprotocols: nativeWebSocketStringArray(pickEndpointValue(options, "Subprotocols", "subprotocols")),
2845
+ connectTimeoutMs,
2846
+ closeTimeoutMs,
2847
+ headers,
2848
+ pingIntervalMs,
2849
+ maxAttempts: Number(pickEndpointValue(reconnect, "MaxAttempts", "maxAttempts") ?? 0),
2850
+ reconnectDelayMs: delayMsValue != null
2851
+ ? Number(delayMsValue)
2852
+ : Number(delaySecondsValue ?? 1) * 1000,
2853
+ messages,
2854
+ expectedMessages
2855
+ };
2856
+ }
2857
+ function nativeWebSocketTimeoutMs(options, fallbackMs, millisecondKeys, secondKeys) {
2858
+ const millisecondValue = pickEndpointValue(options, ...millisecondKeys);
2859
+ if (millisecondValue != null) {
2860
+ return Number(millisecondValue);
2861
+ }
2862
+ const secondValue = pickEndpointValue(options, ...secondKeys);
2863
+ return secondValue == null ? fallbackMs : Number(secondValue) * 1000;
2864
+ }
2865
+ function nativeWebSocketStringArray(value) {
2866
+ return Array.isArray(value) ? value.map((entry) => String(entry).trim()) : [];
2867
+ }
2868
+ function normalizeNativeWebSocketMessage(value) {
2869
+ const options = asRecordOrEmpty(value);
2870
+ const kind = optionString(options, "Kind", "kind").toLowerCase();
2871
+ if (kind === "binary") {
2872
+ const bytes = pickEndpointValue(options, "BinaryPayload", "binaryPayload");
2873
+ return {
2874
+ data: bytes instanceof Uint8Array ? new Uint8Array(bytes) : Uint8Array.from(bytes),
2875
+ isBinary: true
2876
+ };
2877
+ }
2878
+ return {
2879
+ data: String(pickEndpointValue(options, "TextPayload", "textPayload") ?? ""),
2880
+ isBinary: false
2881
+ };
2882
+ }
2883
+ function normalizeNativeWebSocketExpectedMessage(value) {
2884
+ const options = asRecordOrEmpty(value);
2885
+ const bytes = pickEndpointValue(options, "ContainsBytes", "containsBytes");
2886
+ return {
2887
+ containsText: pickEndpointValue(options, "ContainsText", "containsText") == null
2888
+ ? undefined
2889
+ : String(pickEndpointValue(options, "ContainsText", "containsText")),
2890
+ containsBytes: bytes == null
2891
+ ? undefined
2892
+ : (bytes instanceof Uint8Array ? new Uint8Array(bytes) : Uint8Array.from(bytes)),
2893
+ timeoutMs: nativeWebSocketTimeoutMs(options, 30000, ["TimeoutMs", "timeoutMs"], ["TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout"])
2894
+ };
2895
+ }
2896
+ function projectNativeWebSocketPayload(value) {
2897
+ if (value == null) {
2898
+ return null;
2899
+ }
2900
+ if (value instanceof Uint8Array) {
2901
+ return { data: new Uint8Array(value), isBinary: true };
2902
+ }
2903
+ if (value instanceof ArrayBuffer) {
2904
+ return { data: new Uint8Array(value), isBinary: true };
2905
+ }
2906
+ if (typeof value === "string") {
2907
+ return { data: value, isBinary: false };
2908
+ }
2909
+ const serialized = JSON.stringify(value);
2910
+ if (serialized === undefined) {
2911
+ throw new TypeError("Native WebSocket payload cannot be serialized as JSON.");
2912
+ }
2913
+ return {
2914
+ data: serialized,
2915
+ isBinary: false
2916
+ };
2917
+ }
2918
+ function mergeNativeWebSocketHeaders(...layers) {
2919
+ const merged = new Map();
2920
+ for (const layer of layers) {
2921
+ for (const [name, value] of Object.entries(layer)) {
2922
+ merged.set(name.toLowerCase(), { name, value: String(value) });
2923
+ }
2924
+ }
2925
+ return Object.fromEntries([...merged.values()].map(({ name, value }) => [name, value]));
2926
+ }
2927
+ function waitForNativeWebSocketOpen(socket, timeoutMs, signal) {
2928
+ return new Promise((resolve, reject) => {
2929
+ let settled = false;
2930
+ const finish = (error) => {
2931
+ if (settled) {
2932
+ return;
2933
+ }
2934
+ settled = true;
2935
+ clearTimeout(timeout);
2936
+ socket.off("open", onOpen);
2937
+ socket.off("error", onError);
2938
+ socket.off("close", onClose);
2939
+ socket.off("unexpected-response", onUnexpectedResponse);
2940
+ signal.removeEventListener("abort", onAbort);
2941
+ if (error) {
2942
+ reject(error);
2943
+ }
2944
+ else {
2945
+ resolve();
2946
+ }
2947
+ };
2948
+ const onOpen = () => finish();
2949
+ const onError = (error) => finish(error);
2950
+ const onClose = (code, reason) => {
2951
+ finish(new NativeWebSocketPeerClosedError(code, reason.toString("utf8")));
2952
+ };
2953
+ const onUnexpectedResponse = (_request, response) => {
2954
+ response.resume();
2955
+ finish(new Error(`WebSocket opening handshake was rejected with HTTP status ${response.statusCode ?? "unknown"}.`));
2956
+ socket.terminate();
2957
+ };
2958
+ const onAbort = () => {
2959
+ finish(new NativeWebSocketAbortError());
2960
+ socket.terminate();
2961
+ };
2962
+ const timeout = setTimeout(() => {
2963
+ const error = new Error("WebSocket connection timed out.");
2964
+ error.code = "ETIMEDOUT";
2965
+ finish(error);
2966
+ socket.terminate();
2967
+ }, timeoutMs);
2968
+ socket.once("open", onOpen);
2969
+ socket.once("error", onError);
2970
+ socket.once("close", onClose);
2971
+ socket.once("unexpected-response", onUnexpectedResponse);
2972
+ signal.addEventListener("abort", onAbort, { once: true });
2973
+ if (signal.aborted) {
2974
+ onAbort();
2975
+ }
2976
+ });
2977
+ }
2978
+ function sendNativeWebSocketFrame(socket, message, timeoutMs, signal) {
2979
+ if (signal.aborted) {
2980
+ return Promise.reject(new NativeWebSocketAbortError());
2981
+ }
2982
+ return new Promise((resolve, reject) => {
2983
+ let settled = false;
2984
+ let timeout;
2985
+ const finish = (error) => {
2986
+ if (settled) {
2987
+ return;
2988
+ }
2989
+ settled = true;
2990
+ if (timeout) {
2991
+ clearTimeout(timeout);
2992
+ }
2993
+ signal.removeEventListener("abort", onAbort);
2994
+ error ? reject(error) : resolve();
2995
+ };
2996
+ const onAbort = () => {
2997
+ finish(new NativeWebSocketAbortError());
2998
+ socket.terminate();
2999
+ };
3000
+ const onTimeout = () => {
3001
+ finish(new NativeWebSocketSendTimeoutError());
3002
+ socket.terminate();
3003
+ };
3004
+ timeout = setTimeout(onTimeout, timeoutMs);
3005
+ signal.addEventListener("abort", onAbort, { once: true });
3006
+ try {
3007
+ socket.send(message.data, { binary: message.isBinary }, (error) => {
3008
+ finish(error ? normalizeNativeWebSocketSendError(socket, error) : undefined);
3009
+ });
3010
+ }
3011
+ catch (error) {
3012
+ finish(normalizeNativeWebSocketSendError(socket, error));
3013
+ }
3014
+ });
3015
+ }
3016
+ function normalizeNativeWebSocketSendError(socket, error) {
3017
+ const normalized = error instanceof Error ? error : new Error(String(error));
3018
+ const code = String(normalized.code ?? "");
3019
+ const isClosingState = socket.readyState === WebSocket.CLOSING
3020
+ || socket.readyState === WebSocket.CLOSED;
3021
+ const isWsNotOpenStateError = /^WebSocket is not open: readyState [23] \((?:CLOSING|CLOSED)\)$/.test(normalized.message);
3022
+ return !code && isClosingState && isWsNotOpenStateError
3023
+ ? new NativeWebSocketPeerClosedError()
3024
+ : normalized;
3025
+ }
3026
+ async function receiveExpectedNativeWebSocketFrame(frames, expected, signal) {
3027
+ const deadline = Date.now() + expected.timeoutMs;
3028
+ while (true) {
3029
+ const frame = await frames.next(deadline, signal);
3030
+ if (nativeWebSocketFrameMatches(frame, expected)) {
3031
+ return frame;
3032
+ }
3033
+ }
3034
+ }
3035
+ function nativeWebSocketFrameMatches(frame, expected) {
3036
+ if (expected.containsBytes) {
3037
+ return bufferContains(frame.data, expected.containsBytes);
3038
+ }
3039
+ if (expected.containsText != null) {
3040
+ return !frame.isBinary && Buffer.from(frame.data).toString("utf8").includes(expected.containsText);
3041
+ }
3042
+ return frame.data.byteLength > 0;
3043
+ }
3044
+ function bufferContains(value, expected) {
3045
+ if (expected.byteLength === 0) {
3046
+ return true;
3047
+ }
3048
+ return Buffer.from(value).indexOf(Buffer.from(expected)) >= 0;
3049
+ }
3050
+ function nativeWebSocketFrameToTrackingPayload(frame) {
3051
+ if (frame.isBinary) {
3052
+ return attachPayloadHelpers({
3053
+ headers: {},
3054
+ body: new Uint8Array(frame.data),
3055
+ contentType: "application/octet-stream"
3056
+ });
3057
+ }
3058
+ const text = Buffer.from(frame.data).toString("utf8");
3059
+ let body = text;
3060
+ let contentType = "text/plain";
3061
+ try {
3062
+ body = JSON.parse(text);
3063
+ contentType = "application/json";
3064
+ }
3065
+ catch {
3066
+ // Non-JSON text is returned verbatim.
3067
+ }
3068
+ return attachPayloadHelpers({ headers: {}, body, contentType });
3069
+ }
3070
+ function isReconnectableNativeWebSocketError(error) {
3071
+ if (error instanceof NativeWebSocketPeerClosedError) {
3072
+ return true;
3073
+ }
3074
+ if (!error || typeof error !== "object") {
3075
+ return false;
3076
+ }
3077
+ const code = String(error.code ?? "");
3078
+ return new Set([
3079
+ "ECONNABORTED",
3080
+ "ECONNREFUSED",
3081
+ "ECONNRESET",
3082
+ "EHOSTUNREACH",
3083
+ "ENETUNREACH",
3084
+ "ENOTFOUND",
3085
+ "EAI_AGAIN",
3086
+ "EPIPE",
3087
+ "ETIMEDOUT"
3088
+ ]).has(code);
3089
+ }
3090
+ function waitForNativeWebSocketRetry(delayMs, signal) {
3091
+ if (signal.aborted) {
3092
+ return Promise.reject(new NativeWebSocketAbortError());
3093
+ }
3094
+ return new Promise((resolve, reject) => {
3095
+ const onAbort = () => {
3096
+ clearTimeout(timer);
3097
+ reject(new NativeWebSocketAbortError());
3098
+ };
3099
+ const timer = setTimeout(() => {
3100
+ signal.removeEventListener("abort", onAbort);
3101
+ resolve();
3102
+ }, delayMs);
3103
+ signal.addEventListener("abort", onAbort, { once: true });
3104
+ });
3105
+ }
3106
+ function closeNativeWebSocket(socket, timeoutMs) {
3107
+ if (socket.readyState === WebSocket.CLOSED) {
3108
+ return Promise.resolve();
3109
+ }
3110
+ if (socket.readyState === WebSocket.CONNECTING) {
3111
+ socket.terminate();
3112
+ return Promise.resolve();
3113
+ }
3114
+ return new Promise((resolve) => {
3115
+ let settled = false;
3116
+ const finish = () => {
3117
+ if (settled) {
3118
+ return;
3119
+ }
3120
+ settled = true;
3121
+ clearTimeout(timeout);
3122
+ socket.off("close", onClose);
3123
+ socket.off("error", onError);
3124
+ resolve();
3125
+ };
3126
+ const onClose = () => finish();
3127
+ const onError = () => finish();
3128
+ const timeout = setTimeout(() => {
3129
+ socket.terminate();
3130
+ finish();
3131
+ }, timeoutMs);
3132
+ socket.once("close", onClose);
3133
+ socket.once("error", onError);
3134
+ try {
3135
+ socket.close(1000);
3136
+ }
3137
+ catch {
3138
+ socket.terminate();
3139
+ finish();
3140
+ }
3141
+ });
2317
3142
  }
2318
3143
  export class EndpointAdapterFactory {
2319
3144
  static create(endpoint) {
@@ -2322,6 +3147,7 @@ export class EndpointAdapterFactory {
2322
3147
  }
2323
3148
  const normalized = normalizeEndpointDefinition(endpoint);
2324
3149
  validateEndpointDefinition(normalized);
3150
+ validateRawNativeWebSocketHeaderInputs(endpoint, normalized);
2325
3151
  switch (normalized.kind) {
2326
3152
  case "Http":
2327
3153
  return new HttpEndpointAdapter(normalized);
@@ -2350,6 +3176,56 @@ export class EndpointAdapterFactory {
2350
3176
  }
2351
3177
  }
2352
3178
  }
3179
+ function validateRawNativeWebSocketHeaderInputs(input, endpoint) {
3180
+ if (endpoint.kind !== "WebSocket") {
3181
+ return;
3182
+ }
3183
+ const callbacks = endpoint.mode === "Produce"
3184
+ ? selectProduceCallbackGroup(endpoint)
3185
+ : selectConsumeCallbackGroup(endpoint);
3186
+ if (callbacks.asyncDelegate || callbacks.syncDelegate) {
3187
+ return;
3188
+ }
3189
+ const raw = asRecordOrEmpty(input);
3190
+ const options = pickEndpointTransportRecord(raw, "WebSocket", "WebSocket", ["webSocket", "WebSocket"], WEB_SOCKET_ENDPOINT_FLAT_KEYS);
3191
+ if (pickEndpointValue(options, "NativeClient", "nativeClient") == null) {
3192
+ return;
3193
+ }
3194
+ const protocolMetadata = pickEndpointValue(options, "ConnectionMetadata", "connectionMetadata");
3195
+ if (protocolMetadata != null) {
3196
+ validateNativeWebSocketHeaderLayer(protocolMetadata, "ConnectionMetadata");
3197
+ }
3198
+ const rootMetadata = pickEndpointValue(raw, "ConnectionMetadata", "connectionMetadata");
3199
+ if (rootMetadata != null && rootMetadata !== protocolMetadata) {
3200
+ validateNativeWebSocketHeaderLayer(rootMetadata, "ConnectionMetadata");
3201
+ }
3202
+ const delegate = pickEndpointValue(raw, "Delegate", "delegate", "DelegateStream");
3203
+ if (isRecord(delegate)) {
3204
+ const delegateMetadata = pickEndpointValue(delegate, "ConnectionMetadata", "connectionMetadata");
3205
+ if (delegateMetadata != null) {
3206
+ validateNativeWebSocketHeaderLayer(delegateMetadata, "ConnectionMetadata");
3207
+ }
3208
+ }
3209
+ }
3210
+ export function validateNativeEndpointExecutionSupport(endpoint) {
3211
+ const normalized = normalizeEndpointDefinition(endpoint);
3212
+ if (!normalized || typeof normalized !== "object") {
3213
+ return;
3214
+ }
3215
+ validateRawNativeWebSocketHeaderInputs(endpoint, normalized);
3216
+ const kind = String(normalized.kind ?? "").trim().toLowerCase();
3217
+ const modeToken = String(normalized.mode ?? "").trim().toLowerCase();
3218
+ if (modeToken !== "produce" && modeToken !== "consume") {
3219
+ return;
3220
+ }
3221
+ const mode = modeToken === "produce" ? "Produce" : "Consume";
3222
+ if (kind === "grpc") {
3223
+ validateGrpcEndpoint(normalized, mode);
3224
+ }
3225
+ else if (kind === "websocket") {
3226
+ validateWebSocketEndpoint(normalized, mode);
3227
+ }
3228
+ }
2353
3229
  const HTTP_ENDPOINT_FLAT_KEYS = [
2354
3230
  "Url",
2355
3231
  "url",
@@ -2576,7 +3452,9 @@ const WEB_SOCKET_ENDPOINT_FLAT_KEYS = [
2576
3452
  "ConsumeAsync",
2577
3453
  "consumeAsync",
2578
3454
  "ConnectionMetadata",
2579
- "connectionMetadata"
3455
+ "connectionMetadata",
3456
+ "NativeClient",
3457
+ "nativeClient"
2580
3458
  ];
2581
3459
  function normalizeEndpointDefinition(endpoint) {
2582
3460
  if (!endpoint || typeof endpoint !== "object") {
@@ -2882,6 +3760,10 @@ function pickOptionalEndpointString(record, ...keys) {
2882
3760
  const value = optionString(record, ...keys).trim();
2883
3761
  return value ? value : undefined;
2884
3762
  }
3763
+ function pickOptionalEndpointExactString(record, ...keys) {
3764
+ const value = pickEndpointValue(record, ...keys);
3765
+ return typeof value === "string" ? value : undefined;
3766
+ }
2885
3767
  function pickOptionalEndpointStringAllowEmpty(record, ...keys) {
2886
3768
  const value = pickEndpointValue(record, ...keys);
2887
3769
  return typeof value === "string" ? value.trim() : undefined;
@@ -2985,8 +3867,10 @@ function validateEndpointDefinition(endpoint) {
2985
3867
  if (mode !== "Produce" && mode !== "Consume") {
2986
3868
  throw new Error(`Unsupported endpoint mode: ${mode}.`);
2987
3869
  }
2988
- const hasProduceDelegate = Boolean(resolveProduceDelegate(endpoint) || resolveStructuredProduceDelegate(endpoint));
2989
- const hasConsumeDelegate = Boolean(resolveConsumeDelegate(endpoint) || resolveStructuredConsumeDelegate(endpoint) || resolveStructuredConsumeStreamDelegate(endpoint));
3870
+ const produceCallbacks = selectProduceCallbackGroup(endpoint);
3871
+ const consumeCallbacks = selectConsumeCallbackGroup(endpoint);
3872
+ const hasProduceDelegate = Boolean(produceCallbacks.asyncDelegate || produceCallbacks.syncDelegate);
3873
+ const hasConsumeDelegate = Boolean(consumeCallbacks.asyncDelegate || consumeCallbacks.syncDelegate);
2990
3874
  const hasModeDelegate = mode === "Produce" ? hasProduceDelegate : hasConsumeDelegate;
2991
3875
  switch (kind) {
2992
3876
  case "Http":
@@ -3098,7 +3982,14 @@ function validateDelegateStreamEndpoint(endpoint, mode) {
3098
3982
  }
3099
3983
  }
3100
3984
  function validateGrpcEndpoint(endpoint, mode) {
3985
+ const callbacks = mode === "Produce"
3986
+ ? selectProduceCallbackGroup(endpoint)
3987
+ : selectConsumeCallbackGroup(endpoint);
3101
3988
  const options = asRecordOrEmpty(endpoint.grpc);
3989
+ const nativeClient = pickEndpointValue(options, "NativeClient", "nativeClient");
3990
+ if (nativeClient != null && !callbacks.asyncDelegate && !callbacks.syncDelegate) {
3991
+ throw new Error(NATIVE_GRPC_EXECUTION_UNAVAILABLE_MESSAGE);
3992
+ }
3102
3993
  requireNonEmptyString(optionString(options, "Target", "target"), "Target must be provided for gRPC endpoint definitions.");
3103
3994
  requireNonEmptyString(optionString(options, "ServiceName", "serviceName"), "ServiceName must be provided for gRPC endpoint definitions.");
3104
3995
  requireNonEmptyString(optionString(options, "MethodName", "methodName"), "MethodName must be provided for gRPC endpoint definitions.");
@@ -3109,18 +4000,17 @@ function validateGrpcEndpoint(endpoint, mode) {
3109
4000
  || (hasOptionValue(options, "DeadlineSeconds", "deadlineSeconds", "Deadline", "deadline") && deadlineSeconds <= 0)) {
3110
4001
  throw new RangeError("Deadline must be greater than zero.");
3111
4002
  }
3112
- const nativeClient = pickEndpointValue(options, "NativeClient", "nativeClient");
3113
- if (nativeClient != null) {
3114
- new GrpcNativeClientOptions(asRecordOrEmpty(nativeClient)).validate();
3115
- }
3116
- if (mode === "Produce" && !resolveStructuredProduceDelegate(endpoint) && !resolveProduceDelegate(endpoint) && nativeClient == null) {
4003
+ if (mode === "Produce" && !callbacks.asyncDelegate && !callbacks.syncDelegate && nativeClient == null) {
3117
4004
  throw new Error("ProduceAsync delegate must be provided when endpoint mode is Produce.");
3118
4005
  }
3119
- if (mode === "Consume" && !resolveStructuredConsumeDelegate(endpoint) && !resolveStructuredConsumeStreamDelegate(endpoint) && !resolveConsumeDelegate(endpoint) && nativeClient == null) {
4006
+ if (mode === "Consume" && !callbacks.asyncDelegate && !callbacks.syncDelegate && nativeClient == null) {
3120
4007
  throw new Error("ConsumeAsync delegate must be provided when endpoint mode is Consume.");
3121
4008
  }
3122
4009
  }
3123
4010
  function validateWebSocketEndpoint(endpoint, mode) {
4011
+ const callbacks = mode === "Produce"
4012
+ ? selectProduceCallbackGroup(endpoint)
4013
+ : selectConsumeCallbackGroup(endpoint);
3124
4014
  const options = asRecordOrEmpty(endpoint.webSocket);
3125
4015
  const url = requireNonEmptyString(optionString(options, "Url", "url"), "Url must be provided for WebSocket endpoint definitions.");
3126
4016
  let parsed;
@@ -3130,32 +4020,277 @@ function validateWebSocketEndpoint(endpoint, mode) {
3130
4020
  catch {
3131
4021
  throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
3132
4022
  }
3133
- if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
4023
+ if ((parsed.protocol !== "ws:" && parsed.protocol !== "wss:")
4024
+ || !parsed.hostname
4025
+ || Boolean(parsed.hash)) {
3134
4026
  throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
3135
4027
  }
3136
- const connectMs = optionNumber(options, "ConnectTimeoutMs", "connectTimeoutMs");
3137
- const connectSeconds = optionNumber(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout");
3138
- const closeMs = optionNumber(options, "CloseTimeoutMs", "closeTimeoutMs");
3139
- const closeSeconds = optionNumber(options, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout");
3140
- if ((hasOptionValue(options, "ConnectTimeoutMs", "connectTimeoutMs") && connectMs <= 0)
3141
- || (hasOptionValue(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout") && connectSeconds <= 0)) {
3142
- throw new RangeError("ConnectTimeout must be greater than zero.");
3143
- }
3144
- if ((hasOptionValue(options, "CloseTimeoutMs", "closeTimeoutMs") && closeMs <= 0)
3145
- || (hasOptionValue(options, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout") && closeSeconds <= 0)) {
3146
- throw new RangeError("CloseTimeout must be greater than zero.");
3147
- }
4028
+ validateNativeWebSocketTimerDelay(hasOptionValue(options, "ConnectTimeoutMs", "connectTimeoutMs")
4029
+ ? optionNumber(options, "ConnectTimeoutMs", "connectTimeoutMs")
4030
+ : undefined, hasOptionValue(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout")
4031
+ ? optionNumber(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout")
4032
+ : undefined, "ConnectTimeout");
4033
+ validateNativeWebSocketTimerDelay(hasOptionValue(options, "CloseTimeoutMs", "closeTimeoutMs")
4034
+ ? optionNumber(options, "CloseTimeoutMs", "closeTimeoutMs")
4035
+ : undefined, hasOptionValue(options, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout")
4036
+ ? optionNumber(options, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout")
4037
+ : undefined, "CloseTimeout");
3148
4038
  const nativeClient = pickEndpointValue(options, "NativeClient", "nativeClient");
3149
- if (nativeClient != null) {
3150
- new WebSocketNativeClientOptions(asRecordOrEmpty(nativeClient)).validate();
4039
+ const hasSelectedDelegate = Boolean(callbacks.asyncDelegate || callbacks.syncDelegate);
4040
+ if (nativeClient != null && !hasSelectedDelegate) {
4041
+ if (mode === "Consume" && endpoint.trackingField.trim().toLowerCase().startsWith("header:")) {
4042
+ throw new Error("Native WebSocket Consume cannot use a header TrackingField because WebSocket messages do not contain HTTP response headers.");
4043
+ }
4044
+ if (mode === "Consume" && endpoint.gatherByField?.trim().toLowerCase().startsWith("header:")) {
4045
+ throw new Error("Native WebSocket Consume cannot use a header GatherByField because WebSocket messages do not contain HTTP response headers.");
4046
+ }
4047
+ validateNativeWebSocketSubprotocols(pickEndpointValue(options, "Subprotocols", "subprotocols"));
4048
+ validateNativeWebSocketOptionsWithInstance(nativeClient);
4049
+ validateNativeWebSocketHeaderLayer(resolveConnectionMetadata(endpoint), "ConnectionMetadata");
3151
4050
  }
3152
- if (mode === "Produce" && !resolveStructuredProduceDelegate(endpoint) && !resolveProduceDelegate(endpoint) && nativeClient == null) {
4051
+ if (mode === "Produce" && !callbacks.asyncDelegate && !callbacks.syncDelegate && nativeClient == null) {
3153
4052
  throw new Error("ProduceAsync delegate must be provided when endpoint mode is Produce.");
3154
4053
  }
3155
- if (mode === "Consume" && !resolveStructuredConsumeDelegate(endpoint) && !resolveStructuredConsumeStreamDelegate(endpoint) && !resolveConsumeDelegate(endpoint) && nativeClient == null) {
4054
+ if (mode === "Consume" && !callbacks.asyncDelegate && !callbacks.syncDelegate && nativeClient == null) {
3156
4055
  throw new Error("ConsumeAsync delegate must be provided when endpoint mode is Consume.");
3157
4056
  }
3158
4057
  }
4058
+ const NATIVE_WEBSOCKET_MANAGED_HEADERS = new Set([
4059
+ "connection",
4060
+ "content-length",
4061
+ "expect",
4062
+ "host",
4063
+ "sec-websocket-accept",
4064
+ "sec-websocket-extensions",
4065
+ "sec-websocket-key",
4066
+ "sec-websocket-protocol",
4067
+ "sec-websocket-version",
4068
+ "upgrade"
4069
+ ]);
4070
+ const NATIVE_WEBSOCKET_HTTP_TOKEN_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
4071
+ const NATIVE_WEBSOCKET_MAX_RECONNECT_ATTEMPTS = 2147483646;
4072
+ const NATIVE_WEBSOCKET_MAX_TIMER_DELAY_MS = 2147483647;
4073
+ function validateNativeWebSocketReconnectPolicy(value) {
4074
+ if (!isRecord(value)) {
4075
+ throw new TypeError("WebSocket NativeClient.Reconnect must be an object.");
4076
+ }
4077
+ const maxAttempts = pickEndpointValue(value, "MaxAttempts", "maxAttempts");
4078
+ if (maxAttempts != null) {
4079
+ const number = nativeWebSocketStrictNumber(maxAttempts, "MaxAttempts");
4080
+ if (!Number.isInteger(number) || number < 0 || number > NATIVE_WEBSOCKET_MAX_RECONNECT_ATTEMPTS) {
4081
+ throw new RangeError("MaxAttempts must be a finite nonnegative whole number.");
4082
+ }
4083
+ }
4084
+ validateNativeWebSocketTimerDelay(pickEndpointValue(value, "DelayMs", "delayMs"), pickEndpointValue(value, "DelaySeconds", "delaySeconds", "Delay", "delay"), "Delay", true);
4085
+ }
4086
+ function validateNativeWebSocketOptions(value) {
4087
+ if (!isRecord(value)) {
4088
+ throw new TypeError("WebSocket NativeClient must be an object.");
4089
+ }
4090
+ const native = value;
4091
+ const headers = pickEndpointValue(native, "Headers", "headers");
4092
+ if (headers != null) {
4093
+ validateNativeWebSocketHeaderLayer(headers, "NativeClient.Headers");
4094
+ }
4095
+ const cookies = pickEndpointValue(native, "Cookies", "cookies");
4096
+ if (cookies != null && !Array.isArray(cookies)) {
4097
+ throw new TypeError("WebSocket NativeClient.Cookies must be an array.");
4098
+ }
4099
+ if (Array.isArray(cookies)) {
4100
+ cookies.forEach((cookie, index) => {
4101
+ if (typeof cookie !== "string") {
4102
+ throw new TypeError(`WebSocket NativeClient.Cookies[${index}] must be a string.`);
4103
+ }
4104
+ if (!cookie.trim()) {
4105
+ throw new Error(`WebSocket NativeClient.Cookies[${index}] must not be blank.`);
4106
+ }
4107
+ if (/\r|\n/.test(cookie)) {
4108
+ throw new Error(`WebSocket NativeClient.Cookies[${index}] must not contain CR or LF characters.`);
4109
+ }
4110
+ });
4111
+ }
4112
+ const pingMs = pickEndpointValue(native, "PingIntervalMs", "pingIntervalMs");
4113
+ const pingSeconds = pickEndpointValue(native, "PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval");
4114
+ validateNativeWebSocketTimerDelay(pingMs, pingSeconds, "PingInterval");
4115
+ const reconnectValue = pickEndpointValue(native, "Reconnect", "reconnect");
4116
+ if (reconnectValue != null) {
4117
+ validateNativeWebSocketReconnectPolicy(reconnectValue);
4118
+ if (reconnectValue instanceof WebSocketReconnectPolicy) {
4119
+ reconnectValue.validate();
4120
+ }
4121
+ }
4122
+ const messages = pickEndpointValue(native, "Messages", "messages");
4123
+ if (messages != null && !Array.isArray(messages)) {
4124
+ throw new TypeError("WebSocket NativeClient.Messages must be an array.");
4125
+ }
4126
+ if (Array.isArray(messages)) {
4127
+ for (const message of messages) {
4128
+ if (message instanceof WebSocketMessageSpec) {
4129
+ message.validate();
4130
+ }
4131
+ else {
4132
+ validateNativeWebSocketMessage(message);
4133
+ }
4134
+ }
4135
+ }
4136
+ const expectedMessages = pickEndpointValue(native, "ExpectedMessages", "expectedMessages");
4137
+ if (expectedMessages != null && !Array.isArray(expectedMessages)) {
4138
+ throw new TypeError("WebSocket NativeClient.ExpectedMessages must be an array.");
4139
+ }
4140
+ if (Array.isArray(expectedMessages)) {
4141
+ for (const expected of expectedMessages) {
4142
+ if (expected instanceof WebSocketExpectedMessage) {
4143
+ expected.validate();
4144
+ }
4145
+ else {
4146
+ validateNativeWebSocketExpectedMessage(expected);
4147
+ }
4148
+ }
4149
+ }
4150
+ }
4151
+ function validateNativeWebSocketOptionsWithInstance(value) {
4152
+ validateNativeWebSocketOptions(value);
4153
+ if (value instanceof WebSocketNativeClientOptions) {
4154
+ value.validate();
4155
+ }
4156
+ }
4157
+ function validateNativeWebSocketHeaderLayer(value, layerName) {
4158
+ if (!isRecord(value)) {
4159
+ throw new TypeError(`WebSocket ${layerName} must be an object.`);
4160
+ }
4161
+ for (const [name, headerValue] of Object.entries(value)) {
4162
+ const normalizedName = name.trim();
4163
+ if (!normalizedName) {
4164
+ throw new Error(`WebSocket ${layerName} contains an empty header name.`);
4165
+ }
4166
+ if (name !== normalizedName) {
4167
+ throw new Error(`WebSocket ${layerName} header names must not contain surrounding whitespace.`);
4168
+ }
4169
+ if (/\r|\n/.test(name) || /\r|\n/.test(String(headerValue ?? ""))) {
4170
+ throw new Error(`WebSocket ${layerName} must not contain CR or LF characters.`);
4171
+ }
4172
+ if (!NATIVE_WEBSOCKET_HTTP_TOKEN_PATTERN.test(normalizedName)) {
4173
+ throw new Error(`WebSocket ${layerName} contains an invalid HTTP header name.`);
4174
+ }
4175
+ if (typeof headerValue !== "string") {
4176
+ throw new TypeError(`WebSocket ${layerName} header values must be strings.`);
4177
+ }
4178
+ if (NATIVE_WEBSOCKET_MANAGED_HEADERS.has(normalizedName.toLowerCase())) {
4179
+ throw new Error(`WebSocket ${layerName} must not configure ws-managed header '${normalizedName}'.`);
4180
+ }
4181
+ }
4182
+ }
4183
+ function validateNativeWebSocketSubprotocols(value) {
4184
+ if (value == null) {
4185
+ return;
4186
+ }
4187
+ if (!Array.isArray(value)) {
4188
+ throw new TypeError("WebSocket Subprotocols must be an array.");
4189
+ }
4190
+ const seen = new Set();
4191
+ value.forEach((protocol, index) => {
4192
+ if (typeof protocol !== "string" || !protocol.trim()) {
4193
+ throw new TypeError(`WebSocket Subprotocols[${index}] must be a nonblank string.`);
4194
+ }
4195
+ const normalized = protocol.trim();
4196
+ if (!NATIVE_WEBSOCKET_HTTP_TOKEN_PATTERN.test(normalized)) {
4197
+ throw new Error(`WebSocket Subprotocols[${index}] must be a valid RFC 6455 protocol token.`);
4198
+ }
4199
+ if (seen.has(normalized)) {
4200
+ throw new Error("WebSocket Subprotocols must not contain duplicate values.");
4201
+ }
4202
+ seen.add(normalized);
4203
+ });
4204
+ }
4205
+ function validateOriginalNativeWebSocketMessage(value) {
4206
+ validateNativeWebSocketMessage({
4207
+ Kind: pickEndpointValue(value, "Kind", "kind") ?? "Text",
4208
+ TextPayload: pickEndpointValue(value, "TextPayload", "textPayload"),
4209
+ BinaryPayload: pickEndpointValue(value, "BinaryPayload", "binaryPayload")
4210
+ });
4211
+ }
4212
+ function validateNativeWebSocketMessage(value) {
4213
+ if (!isRecord(value)) {
4214
+ throw new TypeError("WebSocket NativeClient.Messages entries must be message objects.");
4215
+ }
4216
+ const kind = optionString(value, "Kind", "kind").toLowerCase();
4217
+ if (kind !== "text" && kind !== "binary") {
4218
+ throw new Error("WebSocket Message kind must be Text or Binary.");
4219
+ }
4220
+ if (kind === "text") {
4221
+ if (typeof pickEndpointValue(value, "TextPayload", "textPayload") !== "string") {
4222
+ throw new TypeError("Text WebSocket messages require TextPayload.");
4223
+ }
4224
+ return;
4225
+ }
4226
+ validateNativeWebSocketBytes(pickEndpointValue(value, "BinaryPayload", "binaryPayload"), "WebSocket binary payload");
4227
+ }
4228
+ function validateNativeWebSocketExpectedMessage(value) {
4229
+ if (!isRecord(value)) {
4230
+ throw new TypeError("WebSocket NativeClient.ExpectedMessages entries must be expected-message objects.");
4231
+ }
4232
+ const containsText = pickEndpointValue(value, "ContainsText", "containsText");
4233
+ if (containsText != null && typeof containsText !== "string") {
4234
+ throw new TypeError("WebSocket expected ContainsText must be a string.");
4235
+ }
4236
+ const containsBytes = pickEndpointValue(value, "ContainsBytes", "containsBytes");
4237
+ if (containsBytes != null) {
4238
+ validateNativeWebSocketBytes(containsBytes, "WebSocket expected ContainsBytes");
4239
+ }
4240
+ const timeoutMs = pickEndpointValue(value, "TimeoutMs", "timeoutMs");
4241
+ const timeoutSeconds = pickEndpointValue(value, "TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout");
4242
+ validateNativeWebSocketTimerDelay(timeoutMs, timeoutSeconds, "Timeout");
4243
+ }
4244
+ function validateNativeWebSocketBytes(value, fieldName) {
4245
+ if (value instanceof Uint8Array) {
4246
+ return;
4247
+ }
4248
+ if (!Array.isArray(value) || value.some((byte) => (typeof byte !== "number" || !Number.isInteger(byte) || byte < 0 || byte > 255))) {
4249
+ throw new TypeError(`${fieldName} must contain only byte values from 0 through 255.`);
4250
+ }
4251
+ }
4252
+ function validateNativeWebSocketTimerDelay(millisecondValue, secondValue, fieldName, allowZero = false) {
4253
+ const validateValue = (value, multiplier) => {
4254
+ const number = nativeWebSocketStrictNumber(value, fieldName);
4255
+ if (allowZero ? number < 0 : number <= 0) {
4256
+ throw new RangeError(allowZero
4257
+ ? `${fieldName} must be zero or greater and finite.`
4258
+ : `${fieldName} must be greater than zero and finite.`);
4259
+ }
4260
+ if (number > NATIVE_WEBSOCKET_MAX_TIMER_DELAY_MS / multiplier) {
4261
+ throw new RangeError(`${fieldName} must not exceed 2,147,483,647 milliseconds.`);
4262
+ }
4263
+ return number * multiplier;
4264
+ };
4265
+ const milliseconds = millisecondValue == null
4266
+ ? undefined
4267
+ : validateValue(millisecondValue, 1);
4268
+ const seconds = secondValue == null
4269
+ ? undefined
4270
+ : validateValue(secondValue, 1000);
4271
+ return milliseconds ?? seconds;
4272
+ }
4273
+ function nativeWebSocketStrictNumber(value, fieldName) {
4274
+ if (typeof value !== "number" || !Number.isFinite(value)) {
4275
+ throw new TypeError(`${fieldName} must be a finite number.`);
4276
+ }
4277
+ return value;
4278
+ }
4279
+ function validateUnsupportedNativeEndpointMode(endpoint, protocol, mode, hasSelectedDelegate) {
4280
+ const options = protocol === "gRPC"
4281
+ ? asRecordOrEmpty(endpoint.grpc)
4282
+ : asRecordOrEmpty(endpoint.webSocket);
4283
+ const nativeClient = pickEndpointValue(options, "NativeClient", "nativeClient");
4284
+ if (nativeClient != null && !hasSelectedDelegate) {
4285
+ throw unsupportedNativeEndpointModeError(protocol, mode);
4286
+ }
4287
+ }
4288
+ function unsupportedNativeEndpointModeError(protocol, mode) {
4289
+ const callbackName = mode === "Produce" ? "Produce" : "Consume";
4290
+ const asyncCallbackName = `${callbackName}Async`;
4291
+ return new Error(`Native ${protocol} ${mode} mode is not supported by the TypeScript SDK. `
4292
+ + `Provide a ${callbackName} or ${asyncCallbackName} callback instead.`);
4293
+ }
3159
4294
  function validateHttpAuthOptions(options) {
3160
4295
  const auth = options.auth;
3161
4296
  if (!auth) {
@@ -3330,13 +4465,13 @@ function validatePushDiffusionEndpoint(endpoint, mode) {
3330
4465
  }
3331
4466
  requireNonEmptyString(optionString(options, "ServerUrl", "serverUrl"), "ServerUrl must be provided for Push Diffusion endpoint.");
3332
4467
  requireNonEmptyString(optionString(options, "TopicPath", "topicPath"), "TopicPath must be provided for Push Diffusion endpoint.");
3333
- if (mode === "Produce" && !resolveStructuredProduceDelegate(endpoint) && !resolveProduceDelegate(endpoint)) {
4468
+ const callbacks = mode === "Produce"
4469
+ ? selectProduceCallbackGroup(endpoint)
4470
+ : selectConsumeCallbackGroup(endpoint);
4471
+ if (mode === "Produce" && !callbacks.asyncDelegate && !callbacks.syncDelegate) {
3334
4472
  throw new Error("PublishAsync delegate must be provided for Push Diffusion producer endpoint.");
3335
4473
  }
3336
- if (mode === "Consume"
3337
- && !resolveStructuredConsumeDelegate(endpoint)
3338
- && !resolveStructuredConsumeStreamDelegate(endpoint)
3339
- && !resolveConsumeDelegate(endpoint)) {
4474
+ if (mode === "Consume" && !callbacks.asyncDelegate && !callbacks.syncDelegate) {
3340
4475
  throw new Error("SubscribeAsync delegate must be provided for Push Diffusion consumer endpoint.");
3341
4476
  }
3342
4477
  }
@@ -4077,60 +5212,109 @@ function parseBodyObject(body) {
4077
5212
  return null;
4078
5213
  }
4079
5214
  }
5215
+ function selectProduceCallbackGroup(endpoint) {
5216
+ const genericAsync = endpoint.delegate?.produceAsync;
5217
+ const genericSync = endpoint.delegate?.produce;
5218
+ if (typeof genericAsync === "function" || typeof genericSync === "function") {
5219
+ return {
5220
+ asyncDelegate: typeof genericAsync === "function" ? genericAsync : undefined,
5221
+ syncDelegate: typeof genericSync === "function" ? genericSync : undefined
5222
+ };
5223
+ }
5224
+ let activeAsync;
5225
+ let activeSync;
5226
+ if (endpoint.kind === "PushDiffusion") {
5227
+ activeAsync = endpoint.pushDiffusion?.PublishAsync
5228
+ ?? endpoint.pushDiffusion?.publishAsync;
5229
+ }
5230
+ else if (endpoint.kind === "Grpc") {
5231
+ activeAsync = endpoint.grpc?.ProduceAsync
5232
+ ?? endpoint.grpc?.produceAsync;
5233
+ activeSync = endpoint.grpc?.Produce
5234
+ ?? endpoint.grpc?.produce;
5235
+ }
5236
+ else if (endpoint.kind === "WebSocket") {
5237
+ activeAsync = endpoint.webSocket?.ProduceAsync
5238
+ ?? endpoint.webSocket?.produceAsync;
5239
+ activeSync = endpoint.webSocket?.Produce
5240
+ ?? endpoint.webSocket?.produce;
5241
+ }
5242
+ return {
5243
+ asyncDelegate: typeof activeAsync === "function"
5244
+ ? activeAsync
5245
+ : undefined,
5246
+ syncDelegate: typeof activeSync === "function"
5247
+ ? activeSync
5248
+ : undefined
5249
+ };
5250
+ }
5251
+ function selectConsumeCallbackGroup(endpoint) {
5252
+ const genericAsync = endpoint.delegate?.consumeAsync;
5253
+ const genericSync = endpoint.delegate?.consume;
5254
+ if (typeof genericAsync === "function" || typeof genericSync === "function") {
5255
+ return {
5256
+ asyncDelegate: typeof genericAsync === "function" ? genericAsync : undefined,
5257
+ syncDelegate: typeof genericSync === "function" ? genericSync : undefined
5258
+ };
5259
+ }
5260
+ let activeAsync;
5261
+ let activeSync;
5262
+ if (endpoint.kind === "PushDiffusion") {
5263
+ activeAsync = endpoint.pushDiffusion?.SubscribeAsync
5264
+ ?? endpoint.pushDiffusion?.subscribeAsync;
5265
+ }
5266
+ else if (endpoint.kind === "Grpc") {
5267
+ activeAsync = endpoint.grpc?.ConsumeAsync
5268
+ ?? endpoint.grpc?.consumeAsync;
5269
+ activeSync = endpoint.grpc?.Consume
5270
+ ?? endpoint.grpc?.consume;
5271
+ }
5272
+ else if (endpoint.kind === "WebSocket") {
5273
+ activeAsync = endpoint.webSocket?.ConsumeAsync
5274
+ ?? endpoint.webSocket?.consumeAsync;
5275
+ activeSync = endpoint.webSocket?.Consume
5276
+ ?? endpoint.webSocket?.consume;
5277
+ }
5278
+ return {
5279
+ asyncDelegate: typeof activeAsync === "function" ? activeAsync : undefined,
5280
+ syncDelegate: typeof activeSync === "function"
5281
+ ? activeSync
5282
+ : undefined
5283
+ };
5284
+ }
4080
5285
  function resolveStructuredProduceDelegate(endpoint) {
4081
- return endpoint.delegate?.produceAsync
4082
- ?? endpoint.pushDiffusion?.PublishAsync
4083
- ?? endpoint.pushDiffusion?.publishAsync
4084
- ?? endpoint.grpc?.ProduceAsync
4085
- ?? endpoint.grpc?.produceAsync
4086
- ?? endpoint.webSocket?.ProduceAsync
4087
- ?? endpoint.webSocket?.produceAsync;
5286
+ return selectProduceCallbackGroup(endpoint).asyncDelegate;
5287
+ }
5288
+ function resolveProduceDelegate(endpoint) {
5289
+ return selectProduceCallbackGroup(endpoint).syncDelegate;
4088
5290
  }
4089
5291
  function resolveStructuredConsumeDelegate(endpoint) {
4090
- const delegate = endpoint.delegate?.consumeAsync
4091
- ?? endpoint.pushDiffusion?.SubscribeAsync
4092
- ?? endpoint.pushDiffusion?.subscribeAsync
4093
- ?? endpoint.grpc?.ConsumeAsync
4094
- ?? endpoint.grpc?.consumeAsync
4095
- ?? endpoint.webSocket?.ConsumeAsync
4096
- ?? endpoint.webSocket?.consumeAsync;
5292
+ const delegate = selectConsumeCallbackGroup(endpoint).asyncDelegate;
4097
5293
  return typeof delegate === "function" && delegate.length === 0
4098
5294
  ? delegate
4099
5295
  : undefined;
4100
5296
  }
4101
5297
  function resolveStructuredConsumeStreamDelegate(endpoint) {
4102
- const delegate = endpoint.delegate?.consumeAsync
4103
- ?? endpoint.pushDiffusion?.SubscribeAsync
4104
- ?? endpoint.pushDiffusion?.subscribeAsync
4105
- ?? endpoint.grpc?.ConsumeAsync
4106
- ?? endpoint.grpc?.consumeAsync
4107
- ?? endpoint.webSocket?.ConsumeAsync
4108
- ?? endpoint.webSocket?.consumeAsync;
5298
+ const delegate = selectConsumeCallbackGroup(endpoint).asyncDelegate;
4109
5299
  return typeof delegate === "function" && delegate.length > 0
4110
5300
  ? delegate
4111
5301
  : undefined;
4112
5302
  }
4113
- function resolveProduceDelegate(endpoint) {
4114
- return endpoint.delegate?.produce
4115
- ?? endpoint.grpc?.Produce
4116
- ?? endpoint.grpc?.produce
4117
- ?? endpoint.webSocket?.Produce
4118
- ?? endpoint.webSocket?.produce;
4119
- }
4120
5303
  function resolveConsumeDelegate(endpoint) {
4121
- return endpoint.delegate?.consume
4122
- ?? endpoint.grpc?.Consume
4123
- ?? endpoint.grpc?.consume
4124
- ?? endpoint.webSocket?.Consume
4125
- ?? endpoint.webSocket?.consume;
5304
+ return selectConsumeCallbackGroup(endpoint).syncDelegate;
4126
5305
  }
4127
5306
  function resolveConnectionMetadata(endpoint) {
5307
+ const activeProtocolMetadata = endpoint.kind === "PushDiffusion"
5308
+ ? toStringRecord(endpoint.pushDiffusion?.ConnectionProperties ?? endpoint.pushDiffusion?.connectionProperties)
5309
+ : endpoint.kind === "Grpc"
5310
+ ? toStringRecord(endpoint.grpc?.ConnectionMetadata ?? endpoint.grpc?.connectionMetadata)
5311
+ : endpoint.kind === "WebSocket"
5312
+ ? toStringRecord(endpoint.webSocket?.ConnectionMetadata ?? endpoint.webSocket?.connectionMetadata)
5313
+ : {};
4128
5314
  return {
5315
+ ...activeProtocolMetadata,
4129
5316
  ...(endpoint.connectionMetadata ?? {}),
4130
- ...(endpoint.delegate?.connectionMetadata ?? {}),
4131
- ...toStringRecord(endpoint.pushDiffusion?.ConnectionProperties ?? endpoint.pushDiffusion?.connectionProperties),
4132
- ...toStringRecord(endpoint.grpc?.ConnectionMetadata ?? endpoint.grpc?.connectionMetadata),
4133
- ...toStringRecord(endpoint.webSocket?.ConnectionMetadata ?? endpoint.webSocket?.connectionMetadata)
5317
+ ...(endpoint.delegate?.connectionMetadata ?? {})
4134
5318
  };
4135
5319
  }
4136
5320
  function isStructuredProduceResult(value) {