@loadstrike/loadstrike-sdk 1.0.31001 → 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 (45) hide show
  1. package/README.md +16 -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-report-input.js +21 -0
  7. package/dist/cjs/local.js +48 -63
  8. package/dist/cjs/report-history.js +421 -0
  9. package/dist/cjs/reporting-containment.js +242 -0
  10. package/dist/cjs/reporting-svg.js +116 -0
  11. package/dist/cjs/reporting.js +413 -136
  12. package/dist/cjs/runtime.js +237 -8
  13. package/dist/cjs/sinks.js +1337 -38
  14. package/dist/cjs/transports.js +1339 -151
  15. package/dist/esm/internal/prometheus-remote-write.js +31 -0
  16. package/dist/esm/internal/reporting-sink-http-error.js +13 -0
  17. package/dist/esm/internal/vendor-metric-payloads.js +382 -0
  18. package/dist/esm/iteration-observations.js +24 -8
  19. package/dist/esm/local-report-input.js +17 -0
  20. package/dist/esm/local.js +49 -64
  21. package/dist/esm/report-history.js +413 -0
  22. package/dist/esm/reporting-containment.js +238 -0
  23. package/dist/esm/reporting-svg.js +113 -0
  24. package/dist/esm/reporting.js +413 -136
  25. package/dist/esm/runtime.js +239 -10
  26. package/dist/esm/sinks.js +1334 -35
  27. package/dist/esm/transports.js +1335 -151
  28. package/dist/types/contracts.d.ts +1 -0
  29. package/dist/types/index.d.ts +1 -1
  30. package/dist/types/internal/prometheus-remote-write.d.ts +2 -0
  31. package/dist/types/internal/reporting-sink-http-error.d.ts +6 -0
  32. package/dist/types/internal/vendor-metric-payloads.d.ts +48 -0
  33. package/dist/types/local-report-input.d.ts +6 -0
  34. package/dist/types/local.d.ts +0 -6
  35. package/dist/types/report-history.d.ts +124 -0
  36. package/dist/types/reporting-containment.d.ts +2 -0
  37. package/dist/types/reporting-svg.d.ts +2 -0
  38. package/dist/types/reporting.d.ts +6 -3
  39. package/dist/types/runtime.d.ts +24 -0
  40. package/dist/types/sinks.d.ts +134 -17
  41. package/dist/types/transports.d.ts +2 -0
  42. package/package.json +9 -3
  43. package/dist/cjs/internal-build.js +0 -4
  44. package/dist/esm/internal-build.js +0 -1
  45. package/dist/types/internal-build.d.ts +0 -1
@@ -32,12 +32,18 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.__loadstrikeTestExports = exports.EndpointAdapterFactory = exports.KafkaSaslOptions = exports.HttpAuthOptions = exports.HttpOAuth2ClientCredentialsOptions = exports.WebSocketEndpointDefinition = exports.GrpcEndpointDefinition = exports.PushDiffusionEndpointDefinition = exports.DelegateStreamEndpointDefinition = exports.SqsEndpointDefinition = exports.AzureEventHubsEndpointDefinition = exports.RedisStreamsEndpointDefinition = exports.NatsEndpointDefinition = exports.RabbitMqEndpointDefinition = exports.KafkaEndpointDefinition = exports.HttpEndpointDefinition = exports.TrafficEndpointDefinition = exports.WebSocketNativeClientOptions = exports.WebSocketExpectedMessage = exports.WebSocketMessageSpec = exports.WebSocketReconnectPolicy = exports.ProtocolMetricSnapshot = exports.GrpcStatusMapper = exports.GrpcNativeClientOptions = exports.GrpcMethodTypes = exports.LOADSTRIKE_TRACE_ID_TRACKING_FIELD = exports.LOADSTRIKE_TRACE_ID_HEADER = void 0;
40
+ exports.validateNativeEndpointExecutionSupport = validateNativeEndpointExecutionSupport;
37
41
  const node_crypto_1 = require("node:crypto");
42
+ const ws_1 = __importDefault(require("ws"));
38
43
  const correlation_js_1 = require("./correlation.js");
39
44
  exports.LOADSTRIKE_TRACE_ID_HEADER = "loadstrike-trace-id";
40
45
  exports.LOADSTRIKE_TRACE_ID_TRACKING_FIELD = `header:${exports.LOADSTRIKE_TRACE_ID_HEADER}`;
46
+ const NATIVE_GRPC_EXECUTION_UNAVAILABLE_MESSAGE = "Native gRPC execution is not available in this SDK version. Provide the endpoint Produce/Consume delegate instead.";
41
47
  class TrafficEndpointDefinitionModel {
42
48
  get JsonSerializerSettings() {
43
49
  return this.JsonSettings;
@@ -459,6 +465,15 @@ class GrpcEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
459
465
  }
460
466
  Validate() {
461
467
  super.Validate();
468
+ const hasModeAsyncDelegate = this.Mode === "Produce"
469
+ ? typeof this.ProduceAsync === "function"
470
+ : typeof this.ConsumeAsync === "function";
471
+ const hasModeDelegate = this.Mode === "Produce"
472
+ ? typeof this.Produce === "function" || hasModeAsyncDelegate
473
+ : typeof this.Consume === "function" || hasModeAsyncDelegate;
474
+ if (this.NativeClient && !hasModeDelegate) {
475
+ throw new Error(NATIVE_GRPC_EXECUTION_UNAVAILABLE_MESSAGE);
476
+ }
462
477
  requireNonEmptyString(this.Target, "Target must be provided for gRPC endpoint definitions.");
463
478
  requireNonEmptyString(this.ServiceName, "ServiceName must be provided for gRPC endpoint definitions.");
464
479
  requireNonEmptyString(this.MethodName, "MethodName must be provided for gRPC endpoint definitions.");
@@ -466,40 +481,177 @@ class GrpcEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
466
481
  if (this.DeadlineSeconds <= 0) {
467
482
  throw new RangeError("Deadline must be greater than zero.");
468
483
  }
469
- this.NativeClient?.validate();
470
- if (this.Mode === "Produce" && typeof this.ProduceAsync !== "function" && !this.NativeClient) {
484
+ if (this.Mode === "Produce" && !hasModeDelegate && !this.NativeClient) {
471
485
  throw new Error("ProduceAsync delegate must be provided when endpoint mode is Produce.");
472
486
  }
473
- if (this.Mode === "Consume" && typeof this.ConsumeAsync !== "function" && !this.NativeClient) {
487
+ if (this.Mode === "Consume" && !hasModeDelegate && !this.NativeClient) {
474
488
  throw new Error("ConsumeAsync delegate must be provided when endpoint mode is Consume.");
475
489
  }
476
490
  }
477
491
  }
492
+ const webSocketReconnectValidationInputs = new WeakMap();
493
+ const webSocketMessageValidationInputs = new WeakMap();
494
+ const webSocketExpectedValidationInputs = new WeakMap();
495
+ const webSocketNativeValidationInputs = new WeakMap();
496
+ const NATIVE_WEBSOCKET_RECONNECT_INPUT_FIELDS = [
497
+ ["MaxAttempts", "maxAttempts"],
498
+ ["DelayMs", "delayMs"],
499
+ ["DelaySeconds", "delaySeconds", "Delay", "delay"]
500
+ ];
501
+ const NATIVE_WEBSOCKET_MESSAGE_INPUT_FIELDS = [
502
+ ["Kind", "kind"],
503
+ ["TextPayload", "textPayload"],
504
+ ["BinaryPayload", "binaryPayload"]
505
+ ];
506
+ const NATIVE_WEBSOCKET_EXPECTED_INPUT_FIELDS = [
507
+ ["ContainsText", "containsText"],
508
+ ["ContainsBytes", "containsBytes"],
509
+ ["TimeoutMs", "timeoutMs"],
510
+ ["TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout"]
511
+ ];
512
+ const NATIVE_WEBSOCKET_CLIENT_INPUT_FIELDS = [
513
+ ["Headers", "headers"],
514
+ ["Cookies", "cookies"],
515
+ ["PingIntervalMs", "pingIntervalMs"],
516
+ ["PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval"],
517
+ ["Reconnect", "reconnect"],
518
+ ["Messages", "messages"],
519
+ ["ExpectedMessages", "expectedMessages"]
520
+ ];
521
+ function captureSelectedNativeWebSocketFields(source, fieldGroups) {
522
+ const captured = {};
523
+ for (const fieldGroup of fieldGroups) {
524
+ const canonicalName = fieldGroup[0];
525
+ for (const fieldName of fieldGroup) {
526
+ if (fieldName in source) {
527
+ captured[canonicalName] = source[fieldName];
528
+ break;
529
+ }
530
+ }
531
+ }
532
+ return captured;
533
+ }
534
+ function captureNativeWebSocketRecordEntries(value) {
535
+ if (!isRecord(value)) {
536
+ return value;
537
+ }
538
+ const captured = {};
539
+ for (const [key, entry] of Object.entries(value)) {
540
+ captured[key] = entry;
541
+ }
542
+ return captured;
543
+ }
544
+ function captureNativeWebSocketReconnectInput(source) {
545
+ return captureSelectedNativeWebSocketFields(source, NATIVE_WEBSOCKET_RECONNECT_INPUT_FIELDS);
546
+ }
547
+ function captureNativeWebSocketMessageInput(source) {
548
+ const captured = captureSelectedNativeWebSocketFields(source, NATIVE_WEBSOCKET_MESSAGE_INPUT_FIELDS);
549
+ if (Array.isArray(captured.BinaryPayload)) {
550
+ captured.BinaryPayload = Array.from(captured.BinaryPayload);
551
+ }
552
+ return captured;
553
+ }
554
+ function captureNativeWebSocketExpectedInput(source) {
555
+ const captured = captureSelectedNativeWebSocketFields(source, NATIVE_WEBSOCKET_EXPECTED_INPUT_FIELDS);
556
+ if (Array.isArray(captured.ContainsBytes)) {
557
+ captured.ContainsBytes = Array.from(captured.ContainsBytes);
558
+ }
559
+ return captured;
560
+ }
561
+ function captureNativeWebSocketOptionsInput(source) {
562
+ const captured = captureSelectedNativeWebSocketFields(source, NATIVE_WEBSOCKET_CLIENT_INPUT_FIELDS);
563
+ if ("Headers" in captured) {
564
+ captured.Headers = captureNativeWebSocketRecordEntries(captured.Headers);
565
+ }
566
+ if (Array.isArray(captured.Cookies)) {
567
+ captured.Cookies = Array.from(captured.Cookies);
568
+ }
569
+ if (isRecord(captured.Reconnect) && !(captured.Reconnect instanceof WebSocketReconnectPolicy)) {
570
+ captured.Reconnect = captureNativeWebSocketReconnectInput(captured.Reconnect);
571
+ }
572
+ if (Array.isArray(captured.Messages)) {
573
+ captured.Messages = captured.Messages.map((entry) => (entry instanceof WebSocketMessageSpec || !isRecord(entry)
574
+ ? entry
575
+ : captureNativeWebSocketMessageInput(entry)));
576
+ }
577
+ if (Array.isArray(captured.ExpectedMessages)) {
578
+ captured.ExpectedMessages = captured.ExpectedMessages.map((entry) => (entry instanceof WebSocketExpectedMessage || !isRecord(entry)
579
+ ? entry
580
+ : captureNativeWebSocketExpectedInput(entry)));
581
+ }
582
+ return captured;
583
+ }
584
+ function snapshotNativeWebSocketInput(value, seen = new WeakMap()) {
585
+ if (value == null || typeof value !== "object") {
586
+ return value;
587
+ }
588
+ if (value instanceof WebSocketReconnectPolicy
589
+ || value instanceof WebSocketMessageSpec
590
+ || value instanceof WebSocketExpectedMessage) {
591
+ return value;
592
+ }
593
+ const existing = seen.get(value);
594
+ if (existing !== undefined) {
595
+ return existing;
596
+ }
597
+ if (value instanceof Uint8Array) {
598
+ return new Uint8Array(value);
599
+ }
600
+ if (value instanceof ArrayBuffer) {
601
+ return value.slice(0);
602
+ }
603
+ if (Array.isArray(value)) {
604
+ const clone = [];
605
+ seen.set(value, clone);
606
+ for (const entry of value) {
607
+ clone.push(snapshotNativeWebSocketInput(entry, seen));
608
+ }
609
+ return clone;
610
+ }
611
+ const clone = {};
612
+ seen.set(value, clone);
613
+ for (const [key, entry] of Object.entries(value)) {
614
+ clone[key] = snapshotNativeWebSocketInput(entry, seen);
615
+ }
616
+ return clone;
617
+ }
618
+ function defineSynchronizedPublicAlias(target, alias, read, write) {
619
+ Object.defineProperty(target, alias, {
620
+ configurable: true,
621
+ enumerable: true,
622
+ get: read,
623
+ set: write
624
+ });
625
+ }
478
626
  class WebSocketReconnectPolicy {
479
627
  constructor(initial = {}) {
480
628
  this.MaxAttempts = 0;
481
629
  this.maxAttempts = 0;
482
630
  this.DelaySeconds = 1;
483
631
  this.delaySeconds = 1;
484
- const raw = asRecordOrEmpty(initial);
485
- this.MaxAttempts = Math.trunc(pickOptionalEndpointNumber(raw, "MaxAttempts", "maxAttempts") ?? 0);
632
+ defineSynchronizedPublicAlias(this, "maxAttempts", () => this.MaxAttempts, (value) => {
633
+ this.MaxAttempts = value;
634
+ });
635
+ defineSynchronizedPublicAlias(this, "delaySeconds", () => this.DelaySeconds, (value) => {
636
+ this.DelaySeconds = value;
637
+ });
638
+ const raw = captureNativeWebSocketReconnectInput(asRecordOrEmpty(initial));
639
+ webSocketReconnectValidationInputs.set(this, snapshotNativeWebSocketInput(raw));
640
+ this.MaxAttempts = (pickEndpointValue(raw, "MaxAttempts", "maxAttempts") ?? 0);
486
641
  this.maxAttempts = this.MaxAttempts;
487
- const delayMs = pickOptionalEndpointNumber(raw, "DelayMs", "delayMs");
642
+ const delayMs = pickEndpointValue(raw, "DelayMs", "delayMs");
643
+ const delaySeconds = pickEndpointValue(raw, "DelaySeconds", "delaySeconds", "Delay", "delay");
488
644
  this.DelaySeconds = delayMs != null
489
645
  ? delayMs / 1000
490
- : (pickOptionalEndpointNumber(raw, "DelaySeconds", "delaySeconds", "Delay", "delay") ?? 1);
646
+ : (delaySeconds ?? 1);
491
647
  this.delaySeconds = this.DelaySeconds;
492
648
  }
493
649
  Validate() {
494
650
  this.validate();
495
651
  }
496
652
  validate() {
497
- if (this.MaxAttempts < 0) {
498
- throw new RangeError("MaxAttempts cannot be negative.");
499
- }
500
- if (this.DelaySeconds < 0) {
501
- throw new RangeError("Delay cannot be negative.");
502
- }
653
+ validateNativeWebSocketReconnectPolicy(webSocketReconnectValidationInputs.get(this) ?? {});
654
+ validateNativeWebSocketReconnectPolicy(this);
503
655
  }
504
656
  }
505
657
  exports.WebSocketReconnectPolicy = WebSocketReconnectPolicy;
@@ -507,17 +659,27 @@ class WebSocketMessageSpec {
507
659
  constructor(initial = {}) {
508
660
  this.Kind = "Text";
509
661
  this.kind = "Text";
510
- const raw = asRecordOrEmpty(initial);
662
+ defineSynchronizedPublicAlias(this, "kind", () => this.Kind, (value) => {
663
+ this.Kind = value;
664
+ });
665
+ defineSynchronizedPublicAlias(this, "textPayload", () => this.TextPayload, (value) => {
666
+ this.TextPayload = value;
667
+ });
668
+ defineSynchronizedPublicAlias(this, "binaryPayload", () => this.BinaryPayload, (value) => {
669
+ this.BinaryPayload = value;
670
+ });
671
+ const raw = captureNativeWebSocketMessageInput(asRecordOrEmpty(initial));
672
+ webSocketMessageValidationInputs.set(this, snapshotNativeWebSocketInput(raw));
511
673
  this.Kind = pickOptionalEndpointString(raw, "Kind", "kind") ?? "Text";
512
674
  this.kind = this.Kind;
513
- this.TextPayload = pickOptionalEndpointString(raw, "TextPayload", "textPayload");
675
+ this.TextPayload = pickOptionalEndpointExactString(raw, "TextPayload", "textPayload");
514
676
  this.textPayload = this.TextPayload;
515
677
  const binary = pickEndpointValue(raw, "BinaryPayload", "binaryPayload");
516
678
  if (binary instanceof Uint8Array) {
517
679
  this.BinaryPayload = binary;
518
680
  }
519
681
  else if (Array.isArray(binary)) {
520
- this.BinaryPayload = Uint8Array.from(binary.map((value) => Number(value) & 0xff));
682
+ this.BinaryPayload = Uint8Array.from(binary.map((value) => Number(value)));
521
683
  }
522
684
  this.binaryPayload = this.BinaryPayload;
523
685
  }
@@ -531,12 +693,8 @@ class WebSocketMessageSpec {
531
693
  this.validate();
532
694
  }
533
695
  validate() {
534
- if (this.Kind.toLowerCase() === "text" && this.TextPayload == null) {
535
- throw new Error("Text WebSocket messages require TextPayload.");
536
- }
537
- if (this.Kind.toLowerCase() === "binary" && this.BinaryPayload == null) {
538
- throw new Error("Binary WebSocket messages require BinaryPayload.");
539
- }
696
+ validateOriginalNativeWebSocketMessage(webSocketMessageValidationInputs.get(this) ?? {});
697
+ validateNativeWebSocketMessage(this);
540
698
  }
541
699
  }
542
700
  exports.WebSocketMessageSpec = WebSocketMessageSpec;
@@ -544,30 +702,40 @@ class WebSocketExpectedMessage {
544
702
  constructor(initial = {}) {
545
703
  this.TimeoutSeconds = 30;
546
704
  this.timeoutSeconds = 30;
547
- const raw = asRecordOrEmpty(initial);
548
- this.ContainsText = pickOptionalEndpointString(raw, "ContainsText", "containsText");
705
+ defineSynchronizedPublicAlias(this, "containsText", () => this.ContainsText, (value) => {
706
+ this.ContainsText = value;
707
+ });
708
+ defineSynchronizedPublicAlias(this, "containsBytes", () => this.ContainsBytes, (value) => {
709
+ this.ContainsBytes = value;
710
+ });
711
+ defineSynchronizedPublicAlias(this, "timeoutSeconds", () => this.TimeoutSeconds, (value) => {
712
+ this.TimeoutSeconds = value;
713
+ });
714
+ const raw = captureNativeWebSocketExpectedInput(asRecordOrEmpty(initial));
715
+ webSocketExpectedValidationInputs.set(this, snapshotNativeWebSocketInput(raw));
716
+ this.ContainsText = pickOptionalEndpointExactString(raw, "ContainsText", "containsText");
549
717
  this.containsText = this.ContainsText;
550
718
  const bytes = pickEndpointValue(raw, "ContainsBytes", "containsBytes");
551
719
  if (bytes instanceof Uint8Array) {
552
720
  this.ContainsBytes = bytes;
553
721
  }
554
722
  else if (Array.isArray(bytes)) {
555
- this.ContainsBytes = Uint8Array.from(bytes.map((value) => Number(value) & 0xff));
723
+ this.ContainsBytes = Uint8Array.from(bytes.map((value) => Number(value)));
556
724
  }
557
725
  this.containsBytes = this.ContainsBytes;
558
- const timeoutMs = pickOptionalEndpointNumber(raw, "TimeoutMs", "timeoutMs");
726
+ const timeoutMs = pickEndpointValue(raw, "TimeoutMs", "timeoutMs");
727
+ const timeoutSeconds = pickEndpointValue(raw, "TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout");
559
728
  this.TimeoutSeconds = timeoutMs != null
560
729
  ? timeoutMs / 1000
561
- : (pickOptionalEndpointNumber(raw, "TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout") ?? 30);
730
+ : (timeoutSeconds ?? 30);
562
731
  this.timeoutSeconds = this.TimeoutSeconds;
563
732
  }
564
733
  Validate() {
565
734
  this.validate();
566
735
  }
567
736
  validate() {
568
- if (this.TimeoutSeconds <= 0) {
569
- throw new RangeError("Timeout must be greater than zero.");
570
- }
737
+ validateNativeWebSocketExpectedMessage(webSocketExpectedValidationInputs.get(this) ?? {});
738
+ validateNativeWebSocketExpectedMessage(this);
571
739
  }
572
740
  }
573
741
  exports.WebSocketExpectedMessage = WebSocketExpectedMessage;
@@ -583,13 +751,32 @@ class WebSocketNativeClientOptions {
583
751
  this.messages = [];
584
752
  this.ExpectedMessages = [];
585
753
  this.expectedMessages = [];
586
- const raw = asRecordOrEmpty(initial);
754
+ defineSynchronizedPublicAlias(this, "headers", () => this.Headers, (value) => {
755
+ this.Headers = value;
756
+ });
757
+ defineSynchronizedPublicAlias(this, "cookies", () => this.Cookies, (value) => {
758
+ this.Cookies = value;
759
+ });
760
+ defineSynchronizedPublicAlias(this, "pingIntervalSeconds", () => this.PingIntervalSeconds, (value) => {
761
+ this.PingIntervalSeconds = value;
762
+ });
763
+ defineSynchronizedPublicAlias(this, "reconnect", () => this.Reconnect, (value) => {
764
+ this.Reconnect = value;
765
+ });
766
+ defineSynchronizedPublicAlias(this, "messages", () => this.Messages, (value) => {
767
+ this.Messages = value;
768
+ });
769
+ defineSynchronizedPublicAlias(this, "expectedMessages", () => this.ExpectedMessages, (value) => {
770
+ this.ExpectedMessages = value;
771
+ });
772
+ const raw = captureNativeWebSocketOptionsInput(asRecordOrEmpty(initial));
773
+ webSocketNativeValidationInputs.set(this, snapshotNativeWebSocketInput(raw));
587
774
  this.Headers = pickEndpointStringRecord(raw, "Headers", "headers");
588
775
  this.headers = this.Headers;
589
776
  this.Cookies = pickEndpointStringArray(raw, "Cookies", "cookies") ?? [];
590
777
  this.cookies = this.Cookies;
591
- const pingMs = pickOptionalEndpointNumber(raw, "PingIntervalMs", "pingIntervalMs");
592
- const pingSeconds = pickOptionalEndpointNumber(raw, "PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval");
778
+ const pingMs = pickEndpointValue(raw, "PingIntervalMs", "pingIntervalMs");
779
+ const pingSeconds = pickEndpointValue(raw, "PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval");
593
780
  this.PingIntervalSeconds = pingMs != null ? pingMs / 1000 : pingSeconds;
594
781
  this.pingIntervalSeconds = this.PingIntervalSeconds;
595
782
  const reconnect = pickEndpointValue(raw, "Reconnect", "reconnect");
@@ -606,16 +793,8 @@ class WebSocketNativeClientOptions {
606
793
  this.validate();
607
794
  }
608
795
  validate() {
609
- if (this.PingIntervalSeconds != null && this.PingIntervalSeconds <= 0) {
610
- throw new RangeError("PingInterval must be greater than zero.");
611
- }
612
- this.Reconnect.validate();
613
- for (const message of this.Messages) {
614
- message.validate();
615
- }
616
- for (const expected of this.ExpectedMessages) {
617
- expected.validate();
618
- }
796
+ validateNativeWebSocketOptions(webSocketNativeValidationInputs.get(this) ?? {});
797
+ validateNativeWebSocketOptions(this);
619
798
  }
620
799
  }
621
800
  exports.WebSocketNativeClientOptions = WebSocketNativeClientOptions;
@@ -632,6 +811,23 @@ class WebSocketEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
632
811
  }
633
812
  Validate() {
634
813
  super.Validate();
814
+ const hasModeAsyncDelegate = this.Mode === "Produce"
815
+ ? typeof this.ProduceAsync === "function"
816
+ : typeof this.ConsumeAsync === "function";
817
+ const hasModeDelegate = this.Mode === "Produce"
818
+ ? typeof this.Produce === "function" || hasModeAsyncDelegate
819
+ : typeof this.Consume === "function" || hasModeAsyncDelegate;
820
+ if (this.NativeClient != null && !hasModeDelegate) {
821
+ if (this.Mode === "Consume" && this.TrackingField.trim().toLowerCase().startsWith("header:")) {
822
+ throw new Error("Native WebSocket Consume cannot use a header TrackingField because WebSocket messages do not contain HTTP response headers.");
823
+ }
824
+ if (this.Mode === "Consume" && this.GatherByField?.trim().toLowerCase().startsWith("header:")) {
825
+ throw new Error("Native WebSocket Consume cannot use a header GatherByField because WebSocket messages do not contain HTTP response headers.");
826
+ }
827
+ validateNativeWebSocketSubprotocols(this.Subprotocols);
828
+ validateNativeWebSocketHeaderLayer(this.ConnectionMetadata, "ConnectionMetadata");
829
+ validateNativeWebSocketOptionsWithInstance(this.NativeClient);
830
+ }
635
831
  const url = requireNonEmptyString(this.Url, "Url must be provided for WebSocket endpoint definitions.");
636
832
  let parsed;
637
833
  try {
@@ -640,20 +836,17 @@ class WebSocketEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
640
836
  catch {
641
837
  throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
642
838
  }
643
- if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
839
+ if ((parsed.protocol !== "ws:" && parsed.protocol !== "wss:")
840
+ || !parsed.hostname
841
+ || Boolean(parsed.hash)) {
644
842
  throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
645
843
  }
646
- if (this.ConnectTimeoutSeconds <= 0) {
647
- throw new RangeError("ConnectTimeout must be greater than zero.");
648
- }
649
- if (this.CloseTimeoutSeconds <= 0) {
650
- throw new RangeError("CloseTimeout must be greater than zero.");
651
- }
652
- this.NativeClient?.validate();
653
- if (this.Mode === "Produce" && typeof this.ProduceAsync !== "function" && !this.NativeClient) {
844
+ validateNativeWebSocketTimerDelay(undefined, this.ConnectTimeoutSeconds, "ConnectTimeout");
845
+ validateNativeWebSocketTimerDelay(undefined, this.CloseTimeoutSeconds, "CloseTimeout");
846
+ if (this.Mode === "Produce" && !hasModeDelegate && this.NativeClient == null) {
654
847
  throw new Error("ProduceAsync delegate must be provided when endpoint mode is Produce.");
655
848
  }
656
- if (this.Mode === "Consume" && typeof this.ConsumeAsync !== "function" && !this.NativeClient) {
849
+ if (this.Mode === "Consume" && !hasModeDelegate && this.NativeClient == null) {
657
850
  throw new Error("ConsumeAsync delegate must be provided when endpoint mode is Consume.");
658
851
  }
659
852
  }
@@ -1180,25 +1373,31 @@ function initializeWebSocketEndpointDefinitionModel(target, initial) {
1180
1373
  if (url) {
1181
1374
  target.Url = url;
1182
1375
  }
1183
- const subprotocols = pickEndpointStringArray(raw, "Subprotocols", "subprotocols");
1184
- if (subprotocols) {
1185
- target.Subprotocols = subprotocols;
1376
+ const subprotocols = pickEndpointValue(raw, "Subprotocols", "subprotocols");
1377
+ if (subprotocols != null) {
1378
+ target.Subprotocols = normalizeWebSocketEndpointStringArrayInput(subprotocols);
1186
1379
  }
1187
- const connectMs = pickOptionalEndpointNumber(raw, "ConnectTimeoutMs", "connectTimeoutMs");
1188
- const connectSeconds = pickOptionalEndpointNumber(raw, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout");
1380
+ const connectMs = pickEndpointValue(raw, "ConnectTimeoutMs", "connectTimeoutMs");
1381
+ const connectSeconds = pickEndpointValue(raw, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout");
1189
1382
  if (connectMs != null) {
1190
- target.ConnectTimeoutSeconds = connectMs / 1000;
1383
+ const normalized = normalizeWebSocketEndpointNumberInput(connectMs);
1384
+ target.ConnectTimeoutSeconds = typeof normalized === "number"
1385
+ ? normalized / 1000
1386
+ : normalized;
1191
1387
  }
1192
1388
  else if (connectSeconds != null) {
1193
- target.ConnectTimeoutSeconds = connectSeconds;
1389
+ target.ConnectTimeoutSeconds = normalizeWebSocketEndpointNumberInput(connectSeconds);
1194
1390
  }
1195
- const closeMs = pickOptionalEndpointNumber(raw, "CloseTimeoutMs", "closeTimeoutMs");
1196
- const closeSeconds = pickOptionalEndpointNumber(raw, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout");
1391
+ const closeMs = pickEndpointValue(raw, "CloseTimeoutMs", "closeTimeoutMs");
1392
+ const closeSeconds = pickEndpointValue(raw, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout");
1197
1393
  if (closeMs != null) {
1198
- target.CloseTimeoutSeconds = closeMs / 1000;
1394
+ const normalized = normalizeWebSocketEndpointNumberInput(closeMs);
1395
+ target.CloseTimeoutSeconds = typeof normalized === "number"
1396
+ ? normalized / 1000
1397
+ : normalized;
1199
1398
  }
1200
1399
  else if (closeSeconds != null) {
1201
- target.CloseTimeoutSeconds = closeSeconds;
1400
+ target.CloseTimeoutSeconds = normalizeWebSocketEndpointNumberInput(closeSeconds);
1202
1401
  }
1203
1402
  const produce = pickEndpointFunction(raw, "Produce", "produce");
1204
1403
  if (produce) {
@@ -1217,15 +1416,45 @@ function initializeWebSocketEndpointDefinitionModel(target, initial) {
1217
1416
  target.ConsumeAsync = consumeAsync;
1218
1417
  }
1219
1418
  if (hasAnyEndpointField(raw, ["ConnectionMetadata", "connectionMetadata"])) {
1220
- target.ConnectionMetadata = pickEndpointStringRecord(raw, "ConnectionMetadata", "connectionMetadata");
1419
+ target.ConnectionMetadata = normalizeWebSocketEndpointMetadataInput(pickEndpointValue(raw, "ConnectionMetadata", "connectionMetadata"));
1221
1420
  }
1222
1421
  if (hasAnyEndpointField(raw, ["NativeClient", "nativeClient"])) {
1223
1422
  const nativeClient = pickEndpointValue(raw, "NativeClient", "nativeClient");
1224
1423
  target.NativeClient = nativeClient instanceof WebSocketNativeClientOptions
1225
1424
  ? nativeClient
1226
- : new WebSocketNativeClientOptions(asRecordOrEmpty(nativeClient));
1425
+ : isRecord(nativeClient)
1426
+ ? new WebSocketNativeClientOptions(nativeClient)
1427
+ : nativeClient;
1227
1428
  }
1228
1429
  }
1430
+ function normalizeWebSocketEndpointNumberInput(value) {
1431
+ if (typeof value === "number") {
1432
+ return value;
1433
+ }
1434
+ if (typeof value === "string" && value.trim()) {
1435
+ const parsed = Number(value);
1436
+ if (Number.isFinite(parsed)) {
1437
+ return parsed;
1438
+ }
1439
+ }
1440
+ return value;
1441
+ }
1442
+ function normalizeWebSocketEndpointStringArrayInput(value) {
1443
+ if (!Array.isArray(value)) {
1444
+ return value;
1445
+ }
1446
+ return value.map((entry) => typeof entry === "string" ? entry.trim() : entry);
1447
+ }
1448
+ function normalizeWebSocketEndpointMetadataInput(value) {
1449
+ if (!isRecord(value)) {
1450
+ return value;
1451
+ }
1452
+ const normalized = {};
1453
+ for (const [key, entry] of Object.entries(value)) {
1454
+ normalized[key] = entry;
1455
+ }
1456
+ return normalized;
1457
+ }
1229
1458
  function validateTrafficEndpointDefinitionModel(target) {
1230
1459
  requireNonEmptyString(target.Name, "Endpoint name must be provided.");
1231
1460
  if (target.Mode !== "Produce" && target.Mode !== "Consume") {
@@ -1559,13 +1788,12 @@ class CallbackAdapter {
1559
1788
  return null;
1560
1789
  }
1561
1790
  const resolved = prepareProducedPayload(this.endpoint, payload);
1562
- const structuredDelegate = resolveStructuredProduceDelegate(this.endpoint);
1563
- if (structuredDelegate) {
1564
- return await invokeStructuredProduceDelegate(this.endpoint, resolved, structuredDelegate);
1791
+ const callbacks = selectProduceCallbackGroup(this.endpoint);
1792
+ if (callbacks.asyncDelegate) {
1793
+ return await invokeStructuredProduceDelegate(this.endpoint, resolved, callbacks.asyncDelegate);
1565
1794
  }
1566
- const delegate = resolveProduceDelegate(this.endpoint);
1567
- if (delegate) {
1568
- const next = await delegate(clonePayload(resolved));
1795
+ if (callbacks.syncDelegate) {
1796
+ const next = await callbacks.syncDelegate(clonePayload(resolved));
1569
1797
  return next == null ? null : normalizeTrackingPayload(next);
1570
1798
  }
1571
1799
  return clonePayload(resolved);
@@ -1577,19 +1805,17 @@ class CallbackAdapter {
1577
1805
  if (this.consumeQueue.length > 0) {
1578
1806
  return this.consumeQueue.shift() ?? null;
1579
1807
  }
1580
- const structuredDelegate = resolveStructuredConsumeDelegate(this.endpoint);
1581
- if (structuredDelegate) {
1582
- const next = await structuredDelegate();
1583
- return normalizeConsumedDelegatePayload(next);
1584
- }
1585
- const streamDelegate = resolveStructuredConsumeStreamDelegate(this.endpoint);
1586
- if (streamDelegate) {
1587
- this.ensureConsumeStreamStarted(streamDelegate);
1808
+ const callbacks = selectConsumeCallbackGroup(this.endpoint);
1809
+ if (callbacks.asyncDelegate) {
1810
+ if (callbacks.asyncDelegate.length === 0) {
1811
+ const next = await callbacks.asyncDelegate();
1812
+ return normalizeConsumedDelegatePayload(next);
1813
+ }
1814
+ this.ensureConsumeStreamStarted(callbacks.asyncDelegate);
1588
1815
  return this.consumeQueue.shift() ?? null;
1589
1816
  }
1590
- const delegate = resolveConsumeDelegate(this.endpoint);
1591
- if (delegate) {
1592
- const next = await delegate();
1817
+ if (callbacks.syncDelegate) {
1818
+ const next = await callbacks.syncDelegate();
1593
1819
  return next == null ? null : normalizeTrackingPayload(next);
1594
1820
  }
1595
1821
  return this.defaultPayload();
@@ -2355,7 +2581,610 @@ class DelegateStreamEndpointAdapter extends CallbackAdapter {
2355
2581
  }
2356
2582
  class GrpcEndpointAdapter extends CallbackAdapter {
2357
2583
  }
2584
+ const NATIVE_WEBSOCKET_FRAME_QUEUE_CAPACITY = 64;
2585
+ const NATIVE_WEBSOCKET_FRAME_QUEUE_MAX_BYTES = 1024 * 1024;
2586
+ const NATIVE_WEBSOCKET_MAX_PAYLOAD_BYTES = 1024 * 1024;
2587
+ class NativeWebSocketAbortError extends Error {
2588
+ constructor() {
2589
+ super("WebSocket native client was interrupted.");
2590
+ this.name = "AbortError";
2591
+ }
2592
+ }
2593
+ class NativeWebSocketPeerClosedError extends Error {
2594
+ constructor(code, reason) {
2595
+ super(code == null
2596
+ ? "WebSocket peer closed before the operation completed."
2597
+ : `WebSocket peer closed before the operation completed (code ${code}${reason ? `: ${reason}` : ""}).`);
2598
+ }
2599
+ }
2600
+ class NativeWebSocketReceiveTimeoutError extends Error {
2601
+ constructor() {
2602
+ super("WebSocket receive timed out before the expected message arrived.");
2603
+ }
2604
+ }
2605
+ class NativeWebSocketSendTimeoutError extends Error {
2606
+ constructor() {
2607
+ super("WebSocket send timed out before the frame was written.");
2608
+ this.code = "ETIMEDOUT";
2609
+ }
2610
+ }
2611
+ class NativeWebSocketQueueOverflowError extends Error {
2612
+ constructor() {
2613
+ super("WebSocket receive queue exceeded its bounded frame limit.");
2614
+ }
2615
+ }
2616
+ class NativeWebSocketFrameQueue {
2617
+ constructor(socket) {
2618
+ this.socket = socket;
2619
+ this.frames = [];
2620
+ this.queuedBytes = 0;
2621
+ this.terminalError = null;
2622
+ this.wakeResolver = null;
2623
+ this.onMessage = (data, isBinary) => {
2624
+ const copy = copyNativeWebSocketRawData(data);
2625
+ if (this.frames.length >= NATIVE_WEBSOCKET_FRAME_QUEUE_CAPACITY
2626
+ || this.queuedBytes + copy.byteLength > NATIVE_WEBSOCKET_FRAME_QUEUE_MAX_BYTES) {
2627
+ this.fail(new NativeWebSocketQueueOverflowError());
2628
+ this.socket.terminate();
2629
+ return;
2630
+ }
2631
+ this.frames.push({ data: copy, isBinary });
2632
+ this.queuedBytes += copy.byteLength;
2633
+ this.wake();
2634
+ };
2635
+ this.onError = (error) => {
2636
+ this.fail(error);
2637
+ };
2638
+ this.onClose = (code, reason) => {
2639
+ this.fail(new NativeWebSocketPeerClosedError(code, reason.toString("utf8")));
2640
+ };
2641
+ socket.on("message", this.onMessage);
2642
+ socket.on("error", this.onError);
2643
+ socket.on("close", this.onClose);
2644
+ }
2645
+ async next(deadlineMs, signal) {
2646
+ while (true) {
2647
+ if (signal.aborted) {
2648
+ throw new NativeWebSocketAbortError();
2649
+ }
2650
+ if (isNativeWebSocketOverflowError(this.terminalError)) {
2651
+ throw this.terminalError;
2652
+ }
2653
+ const frame = this.frames.shift();
2654
+ if (frame) {
2655
+ this.queuedBytes -= frame.data.byteLength;
2656
+ return frame;
2657
+ }
2658
+ if (this.terminalError) {
2659
+ throw this.terminalError;
2660
+ }
2661
+ const remainingMs = deadlineMs - Date.now();
2662
+ if (remainingMs <= 0) {
2663
+ throw new NativeWebSocketReceiveTimeoutError();
2664
+ }
2665
+ await this.waitForFrame(remainingMs, signal);
2666
+ }
2667
+ }
2668
+ dispose() {
2669
+ this.socket.off("message", this.onMessage);
2670
+ this.socket.off("error", this.onError);
2671
+ this.socket.off("close", this.onClose);
2672
+ this.wake();
2673
+ }
2674
+ waitForFrame(timeoutMs, signal) {
2675
+ return new Promise((resolve, reject) => {
2676
+ let settled = false;
2677
+ const finish = (error) => {
2678
+ if (settled) {
2679
+ return;
2680
+ }
2681
+ settled = true;
2682
+ clearTimeout(timeout);
2683
+ signal.removeEventListener("abort", onAbort);
2684
+ if (this.wakeResolver === onWake) {
2685
+ this.wakeResolver = null;
2686
+ }
2687
+ if (error) {
2688
+ reject(error);
2689
+ }
2690
+ else {
2691
+ resolve();
2692
+ }
2693
+ };
2694
+ const onWake = () => finish();
2695
+ const onAbort = () => finish(new NativeWebSocketAbortError());
2696
+ const timeout = setTimeout(() => finish(new NativeWebSocketReceiveTimeoutError()), timeoutMs);
2697
+ this.wakeResolver = onWake;
2698
+ signal.addEventListener("abort", onAbort, { once: true });
2699
+ if (signal.aborted) {
2700
+ onAbort();
2701
+ }
2702
+ });
2703
+ }
2704
+ fail(error) {
2705
+ this.terminalError ?? (this.terminalError = error);
2706
+ this.wake();
2707
+ }
2708
+ wake() {
2709
+ const resolve = this.wakeResolver;
2710
+ this.wakeResolver = null;
2711
+ resolve?.();
2712
+ }
2713
+ }
2714
+ function isNativeWebSocketOverflowError(error) {
2715
+ return error instanceof NativeWebSocketQueueOverflowError
2716
+ || String(error?.code ?? "") === "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH";
2717
+ }
2718
+ function copyNativeWebSocketRawData(data) {
2719
+ if (Array.isArray(data)) {
2720
+ return new Uint8Array(Buffer.concat(data));
2721
+ }
2722
+ if (data instanceof ArrayBuffer) {
2723
+ return new Uint8Array(data.slice(0));
2724
+ }
2725
+ return new Uint8Array(Buffer.from(data));
2726
+ }
2358
2727
  class WebSocketEndpointAdapter extends CallbackAdapter {
2728
+ constructor() {
2729
+ super(...arguments);
2730
+ this.nativeAbort = new AbortController();
2731
+ this.activeSockets = new Set();
2732
+ }
2733
+ async produce(payload) {
2734
+ if (this.endpoint.mode !== "Produce") {
2735
+ return null;
2736
+ }
2737
+ const delegates = selectProduceCallbackGroup(this.endpoint);
2738
+ if (delegates.asyncDelegate || delegates.syncDelegate) {
2739
+ return super.produce(payload);
2740
+ }
2741
+ const resolved = prepareProducedPayload(this.endpoint, payload);
2742
+ const configuration = normalizeNativeWebSocketConfiguration(this.endpoint, resolved);
2743
+ await this.executeNative(configuration);
2744
+ resolved.producedUtc ?? (resolved.producedUtc = new Date().toISOString());
2745
+ return resolved;
2746
+ }
2747
+ async consume() {
2748
+ if (this.endpoint.mode !== "Consume") {
2749
+ return null;
2750
+ }
2751
+ const delegates = selectConsumeCallbackGroup(this.endpoint);
2752
+ if (delegates.asyncDelegate || delegates.syncDelegate) {
2753
+ return super.consume();
2754
+ }
2755
+ const configuration = normalizeNativeWebSocketConfiguration(this.endpoint);
2756
+ return this.executeNative(configuration);
2757
+ }
2758
+ interrupt() {
2759
+ if (!this.nativeAbort.signal.aborted) {
2760
+ this.nativeAbort.abort();
2761
+ }
2762
+ for (const socket of this.activeSockets) {
2763
+ socket.terminate();
2764
+ }
2765
+ }
2766
+ async dispose() {
2767
+ this.interrupt();
2768
+ }
2769
+ async executeNative(configuration) {
2770
+ const totalAttempts = configuration.maxAttempts + 1;
2771
+ let lastError;
2772
+ for (let attempt = 1; attempt <= totalAttempts; attempt += 1) {
2773
+ if (this.nativeAbort.signal.aborted) {
2774
+ throw new NativeWebSocketAbortError();
2775
+ }
2776
+ try {
2777
+ return await this.executeNativeOnce(configuration);
2778
+ }
2779
+ catch (error) {
2780
+ lastError = error;
2781
+ if (!isReconnectableNativeWebSocketError(error) || attempt >= totalAttempts) {
2782
+ throw error;
2783
+ }
2784
+ await waitForNativeWebSocketRetry(configuration.reconnectDelayMs, this.nativeAbort.signal);
2785
+ }
2786
+ }
2787
+ throw lastError;
2788
+ }
2789
+ async executeNativeOnce(configuration) {
2790
+ const options = {
2791
+ headers: configuration.headers,
2792
+ handshakeTimeout: configuration.connectTimeoutMs,
2793
+ followRedirects: false,
2794
+ maxPayload: NATIVE_WEBSOCKET_MAX_PAYLOAD_BYTES
2795
+ };
2796
+ const socket = configuration.subprotocols.length > 0
2797
+ ? new ws_1.default(configuration.url, configuration.subprotocols, options)
2798
+ : new ws_1.default(configuration.url, options);
2799
+ this.activeSockets.add(socket);
2800
+ const frames = new NativeWebSocketFrameQueue(socket);
2801
+ let pingTimer;
2802
+ try {
2803
+ await waitForNativeWebSocketOpen(socket, configuration.connectTimeoutMs, this.nativeAbort.signal);
2804
+ if (configuration.pingIntervalMs != null) {
2805
+ pingTimer = setInterval(() => {
2806
+ if (socket.readyState === ws_1.default.OPEN) {
2807
+ socket.ping();
2808
+ }
2809
+ }, configuration.pingIntervalMs);
2810
+ pingTimer.unref?.();
2811
+ }
2812
+ for (const message of configuration.messages) {
2813
+ await sendNativeWebSocketFrame(socket, message, configuration.connectTimeoutMs, this.nativeAbort.signal);
2814
+ }
2815
+ if (this.endpoint.mode === "Produce") {
2816
+ for (const expected of configuration.expectedMessages) {
2817
+ await receiveExpectedNativeWebSocketFrame(frames, expected, this.nativeAbort.signal);
2818
+ }
2819
+ return null;
2820
+ }
2821
+ if (configuration.expectedMessages.length > 0) {
2822
+ let payload = null;
2823
+ for (const expected of configuration.expectedMessages) {
2824
+ const frame = await receiveExpectedNativeWebSocketFrame(frames, expected, this.nativeAbort.signal);
2825
+ payload = nativeWebSocketFrameToTrackingPayload(frame);
2826
+ }
2827
+ return payload;
2828
+ }
2829
+ const frame = await frames.next(Date.now() + configuration.connectTimeoutMs, this.nativeAbort.signal);
2830
+ return nativeWebSocketFrameToTrackingPayload(frame);
2831
+ }
2832
+ finally {
2833
+ if (pingTimer) {
2834
+ clearInterval(pingTimer);
2835
+ }
2836
+ frames.dispose();
2837
+ await closeNativeWebSocket(socket, configuration.closeTimeoutMs);
2838
+ this.activeSockets.delete(socket);
2839
+ }
2840
+ }
2841
+ }
2842
+ function normalizeNativeWebSocketConfiguration(endpoint, payload) {
2843
+ const options = asRecordOrEmpty(endpoint.webSocket);
2844
+ const native = asRecordOrEmpty(pickEndpointValue(options, "NativeClient", "nativeClient"));
2845
+ const reconnect = asRecordOrEmpty(pickEndpointValue(native, "Reconnect", "reconnect"));
2846
+ if (payload) {
2847
+ validateNativeWebSocketHeaderLayer(payload.headers ?? {}, "payload headers");
2848
+ }
2849
+ const headers = mergeNativeWebSocketHeaders(toStringRecord(pickEndpointValue(native, "Headers", "headers")), resolveConnectionMetadata(endpoint), payload?.headers ?? {});
2850
+ const configuredCookies = pickEndpointValue(native, "Cookies", "cookies");
2851
+ const cookies = Array.isArray(configuredCookies)
2852
+ ? configuredCookies.map((value) => String(value).trim())
2853
+ : [];
2854
+ if (cookies.length > 0) {
2855
+ const cookieName = Object.keys(headers).find((name) => name.toLowerCase() === "cookie");
2856
+ const existing = cookieName ? headers[cookieName] : "";
2857
+ if (cookieName) {
2858
+ delete headers[cookieName];
2859
+ }
2860
+ headers.Cookie = [existing, ...cookies].filter(Boolean).join("; ");
2861
+ }
2862
+ const rawMessages = pickEndpointValue(native, "Messages", "messages");
2863
+ const messages = Array.isArray(rawMessages)
2864
+ ? rawMessages.map(normalizeNativeWebSocketMessage)
2865
+ : [];
2866
+ if (messages.length === 0 && payload) {
2867
+ const projected = projectNativeWebSocketPayload(payload.body);
2868
+ if (projected) {
2869
+ messages.push(projected);
2870
+ }
2871
+ }
2872
+ if (endpoint.mode === "Produce" && messages.length === 0) {
2873
+ throw new Error("Native WebSocket Produce requires at least one configured message or a projected payload body.");
2874
+ }
2875
+ const rawExpected = pickEndpointValue(native, "ExpectedMessages", "expectedMessages");
2876
+ const expectedMessages = Array.isArray(rawExpected)
2877
+ ? rawExpected.map(normalizeNativeWebSocketExpectedMessage)
2878
+ : [];
2879
+ const connectTimeoutMs = nativeWebSocketTimeoutMs(options, 30000, ["ConnectTimeoutMs", "connectTimeoutMs"], ["ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout"]);
2880
+ const closeTimeoutMs = nativeWebSocketTimeoutMs(options, 5000, ["CloseTimeoutMs", "closeTimeoutMs"], ["CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout"]);
2881
+ const pingMsValue = pickEndpointValue(native, "PingIntervalMs", "pingIntervalMs");
2882
+ const pingSecondsValue = pickEndpointValue(native, "PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval");
2883
+ const pingIntervalMs = pingMsValue != null
2884
+ ? Number(pingMsValue)
2885
+ : (pingSecondsValue != null ? Number(pingSecondsValue) * 1000 : undefined);
2886
+ const delayMsValue = pickEndpointValue(reconnect, "DelayMs", "delayMs");
2887
+ const delaySecondsValue = pickEndpointValue(reconnect, "DelaySeconds", "delaySeconds", "Delay", "delay");
2888
+ return {
2889
+ url: optionString(options, "Url", "url"),
2890
+ subprotocols: nativeWebSocketStringArray(pickEndpointValue(options, "Subprotocols", "subprotocols")),
2891
+ connectTimeoutMs,
2892
+ closeTimeoutMs,
2893
+ headers,
2894
+ pingIntervalMs,
2895
+ maxAttempts: Number(pickEndpointValue(reconnect, "MaxAttempts", "maxAttempts") ?? 0),
2896
+ reconnectDelayMs: delayMsValue != null
2897
+ ? Number(delayMsValue)
2898
+ : Number(delaySecondsValue ?? 1) * 1000,
2899
+ messages,
2900
+ expectedMessages
2901
+ };
2902
+ }
2903
+ function nativeWebSocketTimeoutMs(options, fallbackMs, millisecondKeys, secondKeys) {
2904
+ const millisecondValue = pickEndpointValue(options, ...millisecondKeys);
2905
+ if (millisecondValue != null) {
2906
+ return Number(millisecondValue);
2907
+ }
2908
+ const secondValue = pickEndpointValue(options, ...secondKeys);
2909
+ return secondValue == null ? fallbackMs : Number(secondValue) * 1000;
2910
+ }
2911
+ function nativeWebSocketStringArray(value) {
2912
+ return Array.isArray(value) ? value.map((entry) => String(entry).trim()) : [];
2913
+ }
2914
+ function normalizeNativeWebSocketMessage(value) {
2915
+ const options = asRecordOrEmpty(value);
2916
+ const kind = optionString(options, "Kind", "kind").toLowerCase();
2917
+ if (kind === "binary") {
2918
+ const bytes = pickEndpointValue(options, "BinaryPayload", "binaryPayload");
2919
+ return {
2920
+ data: bytes instanceof Uint8Array ? new Uint8Array(bytes) : Uint8Array.from(bytes),
2921
+ isBinary: true
2922
+ };
2923
+ }
2924
+ return {
2925
+ data: String(pickEndpointValue(options, "TextPayload", "textPayload") ?? ""),
2926
+ isBinary: false
2927
+ };
2928
+ }
2929
+ function normalizeNativeWebSocketExpectedMessage(value) {
2930
+ const options = asRecordOrEmpty(value);
2931
+ const bytes = pickEndpointValue(options, "ContainsBytes", "containsBytes");
2932
+ return {
2933
+ containsText: pickEndpointValue(options, "ContainsText", "containsText") == null
2934
+ ? undefined
2935
+ : String(pickEndpointValue(options, "ContainsText", "containsText")),
2936
+ containsBytes: bytes == null
2937
+ ? undefined
2938
+ : (bytes instanceof Uint8Array ? new Uint8Array(bytes) : Uint8Array.from(bytes)),
2939
+ timeoutMs: nativeWebSocketTimeoutMs(options, 30000, ["TimeoutMs", "timeoutMs"], ["TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout"])
2940
+ };
2941
+ }
2942
+ function projectNativeWebSocketPayload(value) {
2943
+ if (value == null) {
2944
+ return null;
2945
+ }
2946
+ if (value instanceof Uint8Array) {
2947
+ return { data: new Uint8Array(value), isBinary: true };
2948
+ }
2949
+ if (value instanceof ArrayBuffer) {
2950
+ return { data: new Uint8Array(value), isBinary: true };
2951
+ }
2952
+ if (typeof value === "string") {
2953
+ return { data: value, isBinary: false };
2954
+ }
2955
+ const serialized = JSON.stringify(value);
2956
+ if (serialized === undefined) {
2957
+ throw new TypeError("Native WebSocket payload cannot be serialized as JSON.");
2958
+ }
2959
+ return {
2960
+ data: serialized,
2961
+ isBinary: false
2962
+ };
2963
+ }
2964
+ function mergeNativeWebSocketHeaders(...layers) {
2965
+ const merged = new Map();
2966
+ for (const layer of layers) {
2967
+ for (const [name, value] of Object.entries(layer)) {
2968
+ merged.set(name.toLowerCase(), { name, value: String(value) });
2969
+ }
2970
+ }
2971
+ return Object.fromEntries([...merged.values()].map(({ name, value }) => [name, value]));
2972
+ }
2973
+ function waitForNativeWebSocketOpen(socket, timeoutMs, signal) {
2974
+ return new Promise((resolve, reject) => {
2975
+ let settled = false;
2976
+ const finish = (error) => {
2977
+ if (settled) {
2978
+ return;
2979
+ }
2980
+ settled = true;
2981
+ clearTimeout(timeout);
2982
+ socket.off("open", onOpen);
2983
+ socket.off("error", onError);
2984
+ socket.off("close", onClose);
2985
+ socket.off("unexpected-response", onUnexpectedResponse);
2986
+ signal.removeEventListener("abort", onAbort);
2987
+ if (error) {
2988
+ reject(error);
2989
+ }
2990
+ else {
2991
+ resolve();
2992
+ }
2993
+ };
2994
+ const onOpen = () => finish();
2995
+ const onError = (error) => finish(error);
2996
+ const onClose = (code, reason) => {
2997
+ finish(new NativeWebSocketPeerClosedError(code, reason.toString("utf8")));
2998
+ };
2999
+ const onUnexpectedResponse = (_request, response) => {
3000
+ response.resume();
3001
+ finish(new Error(`WebSocket opening handshake was rejected with HTTP status ${response.statusCode ?? "unknown"}.`));
3002
+ socket.terminate();
3003
+ };
3004
+ const onAbort = () => {
3005
+ finish(new NativeWebSocketAbortError());
3006
+ socket.terminate();
3007
+ };
3008
+ const timeout = setTimeout(() => {
3009
+ const error = new Error("WebSocket connection timed out.");
3010
+ error.code = "ETIMEDOUT";
3011
+ finish(error);
3012
+ socket.terminate();
3013
+ }, timeoutMs);
3014
+ socket.once("open", onOpen);
3015
+ socket.once("error", onError);
3016
+ socket.once("close", onClose);
3017
+ socket.once("unexpected-response", onUnexpectedResponse);
3018
+ signal.addEventListener("abort", onAbort, { once: true });
3019
+ if (signal.aborted) {
3020
+ onAbort();
3021
+ }
3022
+ });
3023
+ }
3024
+ function sendNativeWebSocketFrame(socket, message, timeoutMs, signal) {
3025
+ if (signal.aborted) {
3026
+ return Promise.reject(new NativeWebSocketAbortError());
3027
+ }
3028
+ return new Promise((resolve, reject) => {
3029
+ let settled = false;
3030
+ let timeout;
3031
+ const finish = (error) => {
3032
+ if (settled) {
3033
+ return;
3034
+ }
3035
+ settled = true;
3036
+ if (timeout) {
3037
+ clearTimeout(timeout);
3038
+ }
3039
+ signal.removeEventListener("abort", onAbort);
3040
+ error ? reject(error) : resolve();
3041
+ };
3042
+ const onAbort = () => {
3043
+ finish(new NativeWebSocketAbortError());
3044
+ socket.terminate();
3045
+ };
3046
+ const onTimeout = () => {
3047
+ finish(new NativeWebSocketSendTimeoutError());
3048
+ socket.terminate();
3049
+ };
3050
+ timeout = setTimeout(onTimeout, timeoutMs);
3051
+ signal.addEventListener("abort", onAbort, { once: true });
3052
+ try {
3053
+ socket.send(message.data, { binary: message.isBinary }, (error) => {
3054
+ finish(error ? normalizeNativeWebSocketSendError(socket, error) : undefined);
3055
+ });
3056
+ }
3057
+ catch (error) {
3058
+ finish(normalizeNativeWebSocketSendError(socket, error));
3059
+ }
3060
+ });
3061
+ }
3062
+ function normalizeNativeWebSocketSendError(socket, error) {
3063
+ const normalized = error instanceof Error ? error : new Error(String(error));
3064
+ const code = String(normalized.code ?? "");
3065
+ const isClosingState = socket.readyState === ws_1.default.CLOSING
3066
+ || socket.readyState === ws_1.default.CLOSED;
3067
+ const isWsNotOpenStateError = /^WebSocket is not open: readyState [23] \((?:CLOSING|CLOSED)\)$/.test(normalized.message);
3068
+ return !code && isClosingState && isWsNotOpenStateError
3069
+ ? new NativeWebSocketPeerClosedError()
3070
+ : normalized;
3071
+ }
3072
+ async function receiveExpectedNativeWebSocketFrame(frames, expected, signal) {
3073
+ const deadline = Date.now() + expected.timeoutMs;
3074
+ while (true) {
3075
+ const frame = await frames.next(deadline, signal);
3076
+ if (nativeWebSocketFrameMatches(frame, expected)) {
3077
+ return frame;
3078
+ }
3079
+ }
3080
+ }
3081
+ function nativeWebSocketFrameMatches(frame, expected) {
3082
+ if (expected.containsBytes) {
3083
+ return bufferContains(frame.data, expected.containsBytes);
3084
+ }
3085
+ if (expected.containsText != null) {
3086
+ return !frame.isBinary && Buffer.from(frame.data).toString("utf8").includes(expected.containsText);
3087
+ }
3088
+ return frame.data.byteLength > 0;
3089
+ }
3090
+ function bufferContains(value, expected) {
3091
+ if (expected.byteLength === 0) {
3092
+ return true;
3093
+ }
3094
+ return Buffer.from(value).indexOf(Buffer.from(expected)) >= 0;
3095
+ }
3096
+ function nativeWebSocketFrameToTrackingPayload(frame) {
3097
+ if (frame.isBinary) {
3098
+ return attachPayloadHelpers({
3099
+ headers: {},
3100
+ body: new Uint8Array(frame.data),
3101
+ contentType: "application/octet-stream"
3102
+ });
3103
+ }
3104
+ const text = Buffer.from(frame.data).toString("utf8");
3105
+ let body = text;
3106
+ let contentType = "text/plain";
3107
+ try {
3108
+ body = JSON.parse(text);
3109
+ contentType = "application/json";
3110
+ }
3111
+ catch {
3112
+ // Non-JSON text is returned verbatim.
3113
+ }
3114
+ return attachPayloadHelpers({ headers: {}, body, contentType });
3115
+ }
3116
+ function isReconnectableNativeWebSocketError(error) {
3117
+ if (error instanceof NativeWebSocketPeerClosedError) {
3118
+ return true;
3119
+ }
3120
+ if (!error || typeof error !== "object") {
3121
+ return false;
3122
+ }
3123
+ const code = String(error.code ?? "");
3124
+ return new Set([
3125
+ "ECONNABORTED",
3126
+ "ECONNREFUSED",
3127
+ "ECONNRESET",
3128
+ "EHOSTUNREACH",
3129
+ "ENETUNREACH",
3130
+ "ENOTFOUND",
3131
+ "EAI_AGAIN",
3132
+ "EPIPE",
3133
+ "ETIMEDOUT"
3134
+ ]).has(code);
3135
+ }
3136
+ function waitForNativeWebSocketRetry(delayMs, signal) {
3137
+ if (signal.aborted) {
3138
+ return Promise.reject(new NativeWebSocketAbortError());
3139
+ }
3140
+ return new Promise((resolve, reject) => {
3141
+ const onAbort = () => {
3142
+ clearTimeout(timer);
3143
+ reject(new NativeWebSocketAbortError());
3144
+ };
3145
+ const timer = setTimeout(() => {
3146
+ signal.removeEventListener("abort", onAbort);
3147
+ resolve();
3148
+ }, delayMs);
3149
+ signal.addEventListener("abort", onAbort, { once: true });
3150
+ });
3151
+ }
3152
+ function closeNativeWebSocket(socket, timeoutMs) {
3153
+ if (socket.readyState === ws_1.default.CLOSED) {
3154
+ return Promise.resolve();
3155
+ }
3156
+ if (socket.readyState === ws_1.default.CONNECTING) {
3157
+ socket.terminate();
3158
+ return Promise.resolve();
3159
+ }
3160
+ return new Promise((resolve) => {
3161
+ let settled = false;
3162
+ const finish = () => {
3163
+ if (settled) {
3164
+ return;
3165
+ }
3166
+ settled = true;
3167
+ clearTimeout(timeout);
3168
+ socket.off("close", onClose);
3169
+ socket.off("error", onError);
3170
+ resolve();
3171
+ };
3172
+ const onClose = () => finish();
3173
+ const onError = () => finish();
3174
+ const timeout = setTimeout(() => {
3175
+ socket.terminate();
3176
+ finish();
3177
+ }, timeoutMs);
3178
+ socket.once("close", onClose);
3179
+ socket.once("error", onError);
3180
+ try {
3181
+ socket.close(1000);
3182
+ }
3183
+ catch {
3184
+ socket.terminate();
3185
+ finish();
3186
+ }
3187
+ });
2359
3188
  }
2360
3189
  class EndpointAdapterFactory {
2361
3190
  static create(endpoint) {
@@ -2364,6 +3193,7 @@ class EndpointAdapterFactory {
2364
3193
  }
2365
3194
  const normalized = normalizeEndpointDefinition(endpoint);
2366
3195
  validateEndpointDefinition(normalized);
3196
+ validateRawNativeWebSocketHeaderInputs(endpoint, normalized);
2367
3197
  switch (normalized.kind) {
2368
3198
  case "Http":
2369
3199
  return new HttpEndpointAdapter(normalized);
@@ -2393,6 +3223,56 @@ class EndpointAdapterFactory {
2393
3223
  }
2394
3224
  }
2395
3225
  exports.EndpointAdapterFactory = EndpointAdapterFactory;
3226
+ function validateRawNativeWebSocketHeaderInputs(input, endpoint) {
3227
+ if (endpoint.kind !== "WebSocket") {
3228
+ return;
3229
+ }
3230
+ const callbacks = endpoint.mode === "Produce"
3231
+ ? selectProduceCallbackGroup(endpoint)
3232
+ : selectConsumeCallbackGroup(endpoint);
3233
+ if (callbacks.asyncDelegate || callbacks.syncDelegate) {
3234
+ return;
3235
+ }
3236
+ const raw = asRecordOrEmpty(input);
3237
+ const options = pickEndpointTransportRecord(raw, "WebSocket", "WebSocket", ["webSocket", "WebSocket"], WEB_SOCKET_ENDPOINT_FLAT_KEYS);
3238
+ if (pickEndpointValue(options, "NativeClient", "nativeClient") == null) {
3239
+ return;
3240
+ }
3241
+ const protocolMetadata = pickEndpointValue(options, "ConnectionMetadata", "connectionMetadata");
3242
+ if (protocolMetadata != null) {
3243
+ validateNativeWebSocketHeaderLayer(protocolMetadata, "ConnectionMetadata");
3244
+ }
3245
+ const rootMetadata = pickEndpointValue(raw, "ConnectionMetadata", "connectionMetadata");
3246
+ if (rootMetadata != null && rootMetadata !== protocolMetadata) {
3247
+ validateNativeWebSocketHeaderLayer(rootMetadata, "ConnectionMetadata");
3248
+ }
3249
+ const delegate = pickEndpointValue(raw, "Delegate", "delegate", "DelegateStream");
3250
+ if (isRecord(delegate)) {
3251
+ const delegateMetadata = pickEndpointValue(delegate, "ConnectionMetadata", "connectionMetadata");
3252
+ if (delegateMetadata != null) {
3253
+ validateNativeWebSocketHeaderLayer(delegateMetadata, "ConnectionMetadata");
3254
+ }
3255
+ }
3256
+ }
3257
+ function validateNativeEndpointExecutionSupport(endpoint) {
3258
+ const normalized = normalizeEndpointDefinition(endpoint);
3259
+ if (!normalized || typeof normalized !== "object") {
3260
+ return;
3261
+ }
3262
+ validateRawNativeWebSocketHeaderInputs(endpoint, normalized);
3263
+ const kind = String(normalized.kind ?? "").trim().toLowerCase();
3264
+ const modeToken = String(normalized.mode ?? "").trim().toLowerCase();
3265
+ if (modeToken !== "produce" && modeToken !== "consume") {
3266
+ return;
3267
+ }
3268
+ const mode = modeToken === "produce" ? "Produce" : "Consume";
3269
+ if (kind === "grpc") {
3270
+ validateGrpcEndpoint(normalized, mode);
3271
+ }
3272
+ else if (kind === "websocket") {
3273
+ validateWebSocketEndpoint(normalized, mode);
3274
+ }
3275
+ }
2396
3276
  const HTTP_ENDPOINT_FLAT_KEYS = [
2397
3277
  "Url",
2398
3278
  "url",
@@ -2619,7 +3499,9 @@ const WEB_SOCKET_ENDPOINT_FLAT_KEYS = [
2619
3499
  "ConsumeAsync",
2620
3500
  "consumeAsync",
2621
3501
  "ConnectionMetadata",
2622
- "connectionMetadata"
3502
+ "connectionMetadata",
3503
+ "NativeClient",
3504
+ "nativeClient"
2623
3505
  ];
2624
3506
  function normalizeEndpointDefinition(endpoint) {
2625
3507
  if (!endpoint || typeof endpoint !== "object") {
@@ -2925,6 +3807,10 @@ function pickOptionalEndpointString(record, ...keys) {
2925
3807
  const value = optionString(record, ...keys).trim();
2926
3808
  return value ? value : undefined;
2927
3809
  }
3810
+ function pickOptionalEndpointExactString(record, ...keys) {
3811
+ const value = pickEndpointValue(record, ...keys);
3812
+ return typeof value === "string" ? value : undefined;
3813
+ }
2928
3814
  function pickOptionalEndpointStringAllowEmpty(record, ...keys) {
2929
3815
  const value = pickEndpointValue(record, ...keys);
2930
3816
  return typeof value === "string" ? value.trim() : undefined;
@@ -3028,8 +3914,10 @@ function validateEndpointDefinition(endpoint) {
3028
3914
  if (mode !== "Produce" && mode !== "Consume") {
3029
3915
  throw new Error(`Unsupported endpoint mode: ${mode}.`);
3030
3916
  }
3031
- const hasProduceDelegate = Boolean(resolveProduceDelegate(endpoint) || resolveStructuredProduceDelegate(endpoint));
3032
- const hasConsumeDelegate = Boolean(resolveConsumeDelegate(endpoint) || resolveStructuredConsumeDelegate(endpoint) || resolveStructuredConsumeStreamDelegate(endpoint));
3917
+ const produceCallbacks = selectProduceCallbackGroup(endpoint);
3918
+ const consumeCallbacks = selectConsumeCallbackGroup(endpoint);
3919
+ const hasProduceDelegate = Boolean(produceCallbacks.asyncDelegate || produceCallbacks.syncDelegate);
3920
+ const hasConsumeDelegate = Boolean(consumeCallbacks.asyncDelegate || consumeCallbacks.syncDelegate);
3033
3921
  const hasModeDelegate = mode === "Produce" ? hasProduceDelegate : hasConsumeDelegate;
3034
3922
  switch (kind) {
3035
3923
  case "Http":
@@ -3141,7 +4029,14 @@ function validateDelegateStreamEndpoint(endpoint, mode) {
3141
4029
  }
3142
4030
  }
3143
4031
  function validateGrpcEndpoint(endpoint, mode) {
4032
+ const callbacks = mode === "Produce"
4033
+ ? selectProduceCallbackGroup(endpoint)
4034
+ : selectConsumeCallbackGroup(endpoint);
3144
4035
  const options = asRecordOrEmpty(endpoint.grpc);
4036
+ const nativeClient = pickEndpointValue(options, "NativeClient", "nativeClient");
4037
+ if (nativeClient != null && !callbacks.asyncDelegate && !callbacks.syncDelegate) {
4038
+ throw new Error(NATIVE_GRPC_EXECUTION_UNAVAILABLE_MESSAGE);
4039
+ }
3145
4040
  requireNonEmptyString(optionString(options, "Target", "target"), "Target must be provided for gRPC endpoint definitions.");
3146
4041
  requireNonEmptyString(optionString(options, "ServiceName", "serviceName"), "ServiceName must be provided for gRPC endpoint definitions.");
3147
4042
  requireNonEmptyString(optionString(options, "MethodName", "methodName"), "MethodName must be provided for gRPC endpoint definitions.");
@@ -3152,18 +4047,17 @@ function validateGrpcEndpoint(endpoint, mode) {
3152
4047
  || (hasOptionValue(options, "DeadlineSeconds", "deadlineSeconds", "Deadline", "deadline") && deadlineSeconds <= 0)) {
3153
4048
  throw new RangeError("Deadline must be greater than zero.");
3154
4049
  }
3155
- const nativeClient = pickEndpointValue(options, "NativeClient", "nativeClient");
3156
- if (nativeClient != null) {
3157
- new GrpcNativeClientOptions(asRecordOrEmpty(nativeClient)).validate();
3158
- }
3159
- if (mode === "Produce" && !resolveStructuredProduceDelegate(endpoint) && !resolveProduceDelegate(endpoint) && nativeClient == null) {
4050
+ if (mode === "Produce" && !callbacks.asyncDelegate && !callbacks.syncDelegate && nativeClient == null) {
3160
4051
  throw new Error("ProduceAsync delegate must be provided when endpoint mode is Produce.");
3161
4052
  }
3162
- if (mode === "Consume" && !resolveStructuredConsumeDelegate(endpoint) && !resolveStructuredConsumeStreamDelegate(endpoint) && !resolveConsumeDelegate(endpoint) && nativeClient == null) {
4053
+ if (mode === "Consume" && !callbacks.asyncDelegate && !callbacks.syncDelegate && nativeClient == null) {
3163
4054
  throw new Error("ConsumeAsync delegate must be provided when endpoint mode is Consume.");
3164
4055
  }
3165
4056
  }
3166
4057
  function validateWebSocketEndpoint(endpoint, mode) {
4058
+ const callbacks = mode === "Produce"
4059
+ ? selectProduceCallbackGroup(endpoint)
4060
+ : selectConsumeCallbackGroup(endpoint);
3167
4061
  const options = asRecordOrEmpty(endpoint.webSocket);
3168
4062
  const url = requireNonEmptyString(optionString(options, "Url", "url"), "Url must be provided for WebSocket endpoint definitions.");
3169
4063
  let parsed;
@@ -3173,32 +4067,277 @@ function validateWebSocketEndpoint(endpoint, mode) {
3173
4067
  catch {
3174
4068
  throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
3175
4069
  }
3176
- if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
4070
+ if ((parsed.protocol !== "ws:" && parsed.protocol !== "wss:")
4071
+ || !parsed.hostname
4072
+ || Boolean(parsed.hash)) {
3177
4073
  throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
3178
4074
  }
3179
- const connectMs = optionNumber(options, "ConnectTimeoutMs", "connectTimeoutMs");
3180
- const connectSeconds = optionNumber(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout");
3181
- const closeMs = optionNumber(options, "CloseTimeoutMs", "closeTimeoutMs");
3182
- const closeSeconds = optionNumber(options, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout");
3183
- if ((hasOptionValue(options, "ConnectTimeoutMs", "connectTimeoutMs") && connectMs <= 0)
3184
- || (hasOptionValue(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout") && connectSeconds <= 0)) {
3185
- throw new RangeError("ConnectTimeout must be greater than zero.");
3186
- }
3187
- if ((hasOptionValue(options, "CloseTimeoutMs", "closeTimeoutMs") && closeMs <= 0)
3188
- || (hasOptionValue(options, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout") && closeSeconds <= 0)) {
3189
- throw new RangeError("CloseTimeout must be greater than zero.");
3190
- }
4075
+ validateNativeWebSocketTimerDelay(hasOptionValue(options, "ConnectTimeoutMs", "connectTimeoutMs")
4076
+ ? optionNumber(options, "ConnectTimeoutMs", "connectTimeoutMs")
4077
+ : undefined, hasOptionValue(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout")
4078
+ ? optionNumber(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout")
4079
+ : undefined, "ConnectTimeout");
4080
+ validateNativeWebSocketTimerDelay(hasOptionValue(options, "CloseTimeoutMs", "closeTimeoutMs")
4081
+ ? optionNumber(options, "CloseTimeoutMs", "closeTimeoutMs")
4082
+ : undefined, hasOptionValue(options, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout")
4083
+ ? optionNumber(options, "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeout", "closeTimeout")
4084
+ : undefined, "CloseTimeout");
3191
4085
  const nativeClient = pickEndpointValue(options, "NativeClient", "nativeClient");
3192
- if (nativeClient != null) {
3193
- new WebSocketNativeClientOptions(asRecordOrEmpty(nativeClient)).validate();
4086
+ const hasSelectedDelegate = Boolean(callbacks.asyncDelegate || callbacks.syncDelegate);
4087
+ if (nativeClient != null && !hasSelectedDelegate) {
4088
+ if (mode === "Consume" && endpoint.trackingField.trim().toLowerCase().startsWith("header:")) {
4089
+ throw new Error("Native WebSocket Consume cannot use a header TrackingField because WebSocket messages do not contain HTTP response headers.");
4090
+ }
4091
+ if (mode === "Consume" && endpoint.gatherByField?.trim().toLowerCase().startsWith("header:")) {
4092
+ throw new Error("Native WebSocket Consume cannot use a header GatherByField because WebSocket messages do not contain HTTP response headers.");
4093
+ }
4094
+ validateNativeWebSocketSubprotocols(pickEndpointValue(options, "Subprotocols", "subprotocols"));
4095
+ validateNativeWebSocketOptionsWithInstance(nativeClient);
4096
+ validateNativeWebSocketHeaderLayer(resolveConnectionMetadata(endpoint), "ConnectionMetadata");
3194
4097
  }
3195
- if (mode === "Produce" && !resolveStructuredProduceDelegate(endpoint) && !resolveProduceDelegate(endpoint) && nativeClient == null) {
4098
+ if (mode === "Produce" && !callbacks.asyncDelegate && !callbacks.syncDelegate && nativeClient == null) {
3196
4099
  throw new Error("ProduceAsync delegate must be provided when endpoint mode is Produce.");
3197
4100
  }
3198
- if (mode === "Consume" && !resolveStructuredConsumeDelegate(endpoint) && !resolveStructuredConsumeStreamDelegate(endpoint) && !resolveConsumeDelegate(endpoint) && nativeClient == null) {
4101
+ if (mode === "Consume" && !callbacks.asyncDelegate && !callbacks.syncDelegate && nativeClient == null) {
3199
4102
  throw new Error("ConsumeAsync delegate must be provided when endpoint mode is Consume.");
3200
4103
  }
3201
4104
  }
4105
+ const NATIVE_WEBSOCKET_MANAGED_HEADERS = new Set([
4106
+ "connection",
4107
+ "content-length",
4108
+ "expect",
4109
+ "host",
4110
+ "sec-websocket-accept",
4111
+ "sec-websocket-extensions",
4112
+ "sec-websocket-key",
4113
+ "sec-websocket-protocol",
4114
+ "sec-websocket-version",
4115
+ "upgrade"
4116
+ ]);
4117
+ const NATIVE_WEBSOCKET_HTTP_TOKEN_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
4118
+ const NATIVE_WEBSOCKET_MAX_RECONNECT_ATTEMPTS = 2147483646;
4119
+ const NATIVE_WEBSOCKET_MAX_TIMER_DELAY_MS = 2147483647;
4120
+ function validateNativeWebSocketReconnectPolicy(value) {
4121
+ if (!isRecord(value)) {
4122
+ throw new TypeError("WebSocket NativeClient.Reconnect must be an object.");
4123
+ }
4124
+ const maxAttempts = pickEndpointValue(value, "MaxAttempts", "maxAttempts");
4125
+ if (maxAttempts != null) {
4126
+ const number = nativeWebSocketStrictNumber(maxAttempts, "MaxAttempts");
4127
+ if (!Number.isInteger(number) || number < 0 || number > NATIVE_WEBSOCKET_MAX_RECONNECT_ATTEMPTS) {
4128
+ throw new RangeError("MaxAttempts must be a finite nonnegative whole number.");
4129
+ }
4130
+ }
4131
+ validateNativeWebSocketTimerDelay(pickEndpointValue(value, "DelayMs", "delayMs"), pickEndpointValue(value, "DelaySeconds", "delaySeconds", "Delay", "delay"), "Delay", true);
4132
+ }
4133
+ function validateNativeWebSocketOptions(value) {
4134
+ if (!isRecord(value)) {
4135
+ throw new TypeError("WebSocket NativeClient must be an object.");
4136
+ }
4137
+ const native = value;
4138
+ const headers = pickEndpointValue(native, "Headers", "headers");
4139
+ if (headers != null) {
4140
+ validateNativeWebSocketHeaderLayer(headers, "NativeClient.Headers");
4141
+ }
4142
+ const cookies = pickEndpointValue(native, "Cookies", "cookies");
4143
+ if (cookies != null && !Array.isArray(cookies)) {
4144
+ throw new TypeError("WebSocket NativeClient.Cookies must be an array.");
4145
+ }
4146
+ if (Array.isArray(cookies)) {
4147
+ cookies.forEach((cookie, index) => {
4148
+ if (typeof cookie !== "string") {
4149
+ throw new TypeError(`WebSocket NativeClient.Cookies[${index}] must be a string.`);
4150
+ }
4151
+ if (!cookie.trim()) {
4152
+ throw new Error(`WebSocket NativeClient.Cookies[${index}] must not be blank.`);
4153
+ }
4154
+ if (/\r|\n/.test(cookie)) {
4155
+ throw new Error(`WebSocket NativeClient.Cookies[${index}] must not contain CR or LF characters.`);
4156
+ }
4157
+ });
4158
+ }
4159
+ const pingMs = pickEndpointValue(native, "PingIntervalMs", "pingIntervalMs");
4160
+ const pingSeconds = pickEndpointValue(native, "PingIntervalSeconds", "pingIntervalSeconds", "PingInterval", "pingInterval");
4161
+ validateNativeWebSocketTimerDelay(pingMs, pingSeconds, "PingInterval");
4162
+ const reconnectValue = pickEndpointValue(native, "Reconnect", "reconnect");
4163
+ if (reconnectValue != null) {
4164
+ validateNativeWebSocketReconnectPolicy(reconnectValue);
4165
+ if (reconnectValue instanceof WebSocketReconnectPolicy) {
4166
+ reconnectValue.validate();
4167
+ }
4168
+ }
4169
+ const messages = pickEndpointValue(native, "Messages", "messages");
4170
+ if (messages != null && !Array.isArray(messages)) {
4171
+ throw new TypeError("WebSocket NativeClient.Messages must be an array.");
4172
+ }
4173
+ if (Array.isArray(messages)) {
4174
+ for (const message of messages) {
4175
+ if (message instanceof WebSocketMessageSpec) {
4176
+ message.validate();
4177
+ }
4178
+ else {
4179
+ validateNativeWebSocketMessage(message);
4180
+ }
4181
+ }
4182
+ }
4183
+ const expectedMessages = pickEndpointValue(native, "ExpectedMessages", "expectedMessages");
4184
+ if (expectedMessages != null && !Array.isArray(expectedMessages)) {
4185
+ throw new TypeError("WebSocket NativeClient.ExpectedMessages must be an array.");
4186
+ }
4187
+ if (Array.isArray(expectedMessages)) {
4188
+ for (const expected of expectedMessages) {
4189
+ if (expected instanceof WebSocketExpectedMessage) {
4190
+ expected.validate();
4191
+ }
4192
+ else {
4193
+ validateNativeWebSocketExpectedMessage(expected);
4194
+ }
4195
+ }
4196
+ }
4197
+ }
4198
+ function validateNativeWebSocketOptionsWithInstance(value) {
4199
+ validateNativeWebSocketOptions(value);
4200
+ if (value instanceof WebSocketNativeClientOptions) {
4201
+ value.validate();
4202
+ }
4203
+ }
4204
+ function validateNativeWebSocketHeaderLayer(value, layerName) {
4205
+ if (!isRecord(value)) {
4206
+ throw new TypeError(`WebSocket ${layerName} must be an object.`);
4207
+ }
4208
+ for (const [name, headerValue] of Object.entries(value)) {
4209
+ const normalizedName = name.trim();
4210
+ if (!normalizedName) {
4211
+ throw new Error(`WebSocket ${layerName} contains an empty header name.`);
4212
+ }
4213
+ if (name !== normalizedName) {
4214
+ throw new Error(`WebSocket ${layerName} header names must not contain surrounding whitespace.`);
4215
+ }
4216
+ if (/\r|\n/.test(name) || /\r|\n/.test(String(headerValue ?? ""))) {
4217
+ throw new Error(`WebSocket ${layerName} must not contain CR or LF characters.`);
4218
+ }
4219
+ if (!NATIVE_WEBSOCKET_HTTP_TOKEN_PATTERN.test(normalizedName)) {
4220
+ throw new Error(`WebSocket ${layerName} contains an invalid HTTP header name.`);
4221
+ }
4222
+ if (typeof headerValue !== "string") {
4223
+ throw new TypeError(`WebSocket ${layerName} header values must be strings.`);
4224
+ }
4225
+ if (NATIVE_WEBSOCKET_MANAGED_HEADERS.has(normalizedName.toLowerCase())) {
4226
+ throw new Error(`WebSocket ${layerName} must not configure ws-managed header '${normalizedName}'.`);
4227
+ }
4228
+ }
4229
+ }
4230
+ function validateNativeWebSocketSubprotocols(value) {
4231
+ if (value == null) {
4232
+ return;
4233
+ }
4234
+ if (!Array.isArray(value)) {
4235
+ throw new TypeError("WebSocket Subprotocols must be an array.");
4236
+ }
4237
+ const seen = new Set();
4238
+ value.forEach((protocol, index) => {
4239
+ if (typeof protocol !== "string" || !protocol.trim()) {
4240
+ throw new TypeError(`WebSocket Subprotocols[${index}] must be a nonblank string.`);
4241
+ }
4242
+ const normalized = protocol.trim();
4243
+ if (!NATIVE_WEBSOCKET_HTTP_TOKEN_PATTERN.test(normalized)) {
4244
+ throw new Error(`WebSocket Subprotocols[${index}] must be a valid RFC 6455 protocol token.`);
4245
+ }
4246
+ if (seen.has(normalized)) {
4247
+ throw new Error("WebSocket Subprotocols must not contain duplicate values.");
4248
+ }
4249
+ seen.add(normalized);
4250
+ });
4251
+ }
4252
+ function validateOriginalNativeWebSocketMessage(value) {
4253
+ validateNativeWebSocketMessage({
4254
+ Kind: pickEndpointValue(value, "Kind", "kind") ?? "Text",
4255
+ TextPayload: pickEndpointValue(value, "TextPayload", "textPayload"),
4256
+ BinaryPayload: pickEndpointValue(value, "BinaryPayload", "binaryPayload")
4257
+ });
4258
+ }
4259
+ function validateNativeWebSocketMessage(value) {
4260
+ if (!isRecord(value)) {
4261
+ throw new TypeError("WebSocket NativeClient.Messages entries must be message objects.");
4262
+ }
4263
+ const kind = optionString(value, "Kind", "kind").toLowerCase();
4264
+ if (kind !== "text" && kind !== "binary") {
4265
+ throw new Error("WebSocket Message kind must be Text or Binary.");
4266
+ }
4267
+ if (kind === "text") {
4268
+ if (typeof pickEndpointValue(value, "TextPayload", "textPayload") !== "string") {
4269
+ throw new TypeError("Text WebSocket messages require TextPayload.");
4270
+ }
4271
+ return;
4272
+ }
4273
+ validateNativeWebSocketBytes(pickEndpointValue(value, "BinaryPayload", "binaryPayload"), "WebSocket binary payload");
4274
+ }
4275
+ function validateNativeWebSocketExpectedMessage(value) {
4276
+ if (!isRecord(value)) {
4277
+ throw new TypeError("WebSocket NativeClient.ExpectedMessages entries must be expected-message objects.");
4278
+ }
4279
+ const containsText = pickEndpointValue(value, "ContainsText", "containsText");
4280
+ if (containsText != null && typeof containsText !== "string") {
4281
+ throw new TypeError("WebSocket expected ContainsText must be a string.");
4282
+ }
4283
+ const containsBytes = pickEndpointValue(value, "ContainsBytes", "containsBytes");
4284
+ if (containsBytes != null) {
4285
+ validateNativeWebSocketBytes(containsBytes, "WebSocket expected ContainsBytes");
4286
+ }
4287
+ const timeoutMs = pickEndpointValue(value, "TimeoutMs", "timeoutMs");
4288
+ const timeoutSeconds = pickEndpointValue(value, "TimeoutSeconds", "timeoutSeconds", "Timeout", "timeout");
4289
+ validateNativeWebSocketTimerDelay(timeoutMs, timeoutSeconds, "Timeout");
4290
+ }
4291
+ function validateNativeWebSocketBytes(value, fieldName) {
4292
+ if (value instanceof Uint8Array) {
4293
+ return;
4294
+ }
4295
+ if (!Array.isArray(value) || value.some((byte) => (typeof byte !== "number" || !Number.isInteger(byte) || byte < 0 || byte > 255))) {
4296
+ throw new TypeError(`${fieldName} must contain only byte values from 0 through 255.`);
4297
+ }
4298
+ }
4299
+ function validateNativeWebSocketTimerDelay(millisecondValue, secondValue, fieldName, allowZero = false) {
4300
+ const validateValue = (value, multiplier) => {
4301
+ const number = nativeWebSocketStrictNumber(value, fieldName);
4302
+ if (allowZero ? number < 0 : number <= 0) {
4303
+ throw new RangeError(allowZero
4304
+ ? `${fieldName} must be zero or greater and finite.`
4305
+ : `${fieldName} must be greater than zero and finite.`);
4306
+ }
4307
+ if (number > NATIVE_WEBSOCKET_MAX_TIMER_DELAY_MS / multiplier) {
4308
+ throw new RangeError(`${fieldName} must not exceed 2,147,483,647 milliseconds.`);
4309
+ }
4310
+ return number * multiplier;
4311
+ };
4312
+ const milliseconds = millisecondValue == null
4313
+ ? undefined
4314
+ : validateValue(millisecondValue, 1);
4315
+ const seconds = secondValue == null
4316
+ ? undefined
4317
+ : validateValue(secondValue, 1000);
4318
+ return milliseconds ?? seconds;
4319
+ }
4320
+ function nativeWebSocketStrictNumber(value, fieldName) {
4321
+ if (typeof value !== "number" || !Number.isFinite(value)) {
4322
+ throw new TypeError(`${fieldName} must be a finite number.`);
4323
+ }
4324
+ return value;
4325
+ }
4326
+ function validateUnsupportedNativeEndpointMode(endpoint, protocol, mode, hasSelectedDelegate) {
4327
+ const options = protocol === "gRPC"
4328
+ ? asRecordOrEmpty(endpoint.grpc)
4329
+ : asRecordOrEmpty(endpoint.webSocket);
4330
+ const nativeClient = pickEndpointValue(options, "NativeClient", "nativeClient");
4331
+ if (nativeClient != null && !hasSelectedDelegate) {
4332
+ throw unsupportedNativeEndpointModeError(protocol, mode);
4333
+ }
4334
+ }
4335
+ function unsupportedNativeEndpointModeError(protocol, mode) {
4336
+ const callbackName = mode === "Produce" ? "Produce" : "Consume";
4337
+ const asyncCallbackName = `${callbackName}Async`;
4338
+ return new Error(`Native ${protocol} ${mode} mode is not supported by the TypeScript SDK. `
4339
+ + `Provide a ${callbackName} or ${asyncCallbackName} callback instead.`);
4340
+ }
3202
4341
  function validateHttpAuthOptions(options) {
3203
4342
  const auth = options.auth;
3204
4343
  if (!auth) {
@@ -3373,13 +4512,13 @@ function validatePushDiffusionEndpoint(endpoint, mode) {
3373
4512
  }
3374
4513
  requireNonEmptyString(optionString(options, "ServerUrl", "serverUrl"), "ServerUrl must be provided for Push Diffusion endpoint.");
3375
4514
  requireNonEmptyString(optionString(options, "TopicPath", "topicPath"), "TopicPath must be provided for Push Diffusion endpoint.");
3376
- if (mode === "Produce" && !resolveStructuredProduceDelegate(endpoint) && !resolveProduceDelegate(endpoint)) {
4515
+ const callbacks = mode === "Produce"
4516
+ ? selectProduceCallbackGroup(endpoint)
4517
+ : selectConsumeCallbackGroup(endpoint);
4518
+ if (mode === "Produce" && !callbacks.asyncDelegate && !callbacks.syncDelegate) {
3377
4519
  throw new Error("PublishAsync delegate must be provided for Push Diffusion producer endpoint.");
3378
4520
  }
3379
- if (mode === "Consume"
3380
- && !resolveStructuredConsumeDelegate(endpoint)
3381
- && !resolveStructuredConsumeStreamDelegate(endpoint)
3382
- && !resolveConsumeDelegate(endpoint)) {
4521
+ if (mode === "Consume" && !callbacks.asyncDelegate && !callbacks.syncDelegate) {
3383
4522
  throw new Error("SubscribeAsync delegate must be provided for Push Diffusion consumer endpoint.");
3384
4523
  }
3385
4524
  }
@@ -4120,60 +5259,109 @@ function parseBodyObject(body) {
4120
5259
  return null;
4121
5260
  }
4122
5261
  }
5262
+ function selectProduceCallbackGroup(endpoint) {
5263
+ const genericAsync = endpoint.delegate?.produceAsync;
5264
+ const genericSync = endpoint.delegate?.produce;
5265
+ if (typeof genericAsync === "function" || typeof genericSync === "function") {
5266
+ return {
5267
+ asyncDelegate: typeof genericAsync === "function" ? genericAsync : undefined,
5268
+ syncDelegate: typeof genericSync === "function" ? genericSync : undefined
5269
+ };
5270
+ }
5271
+ let activeAsync;
5272
+ let activeSync;
5273
+ if (endpoint.kind === "PushDiffusion") {
5274
+ activeAsync = endpoint.pushDiffusion?.PublishAsync
5275
+ ?? endpoint.pushDiffusion?.publishAsync;
5276
+ }
5277
+ else if (endpoint.kind === "Grpc") {
5278
+ activeAsync = endpoint.grpc?.ProduceAsync
5279
+ ?? endpoint.grpc?.produceAsync;
5280
+ activeSync = endpoint.grpc?.Produce
5281
+ ?? endpoint.grpc?.produce;
5282
+ }
5283
+ else if (endpoint.kind === "WebSocket") {
5284
+ activeAsync = endpoint.webSocket?.ProduceAsync
5285
+ ?? endpoint.webSocket?.produceAsync;
5286
+ activeSync = endpoint.webSocket?.Produce
5287
+ ?? endpoint.webSocket?.produce;
5288
+ }
5289
+ return {
5290
+ asyncDelegate: typeof activeAsync === "function"
5291
+ ? activeAsync
5292
+ : undefined,
5293
+ syncDelegate: typeof activeSync === "function"
5294
+ ? activeSync
5295
+ : undefined
5296
+ };
5297
+ }
5298
+ function selectConsumeCallbackGroup(endpoint) {
5299
+ const genericAsync = endpoint.delegate?.consumeAsync;
5300
+ const genericSync = endpoint.delegate?.consume;
5301
+ if (typeof genericAsync === "function" || typeof genericSync === "function") {
5302
+ return {
5303
+ asyncDelegate: typeof genericAsync === "function" ? genericAsync : undefined,
5304
+ syncDelegate: typeof genericSync === "function" ? genericSync : undefined
5305
+ };
5306
+ }
5307
+ let activeAsync;
5308
+ let activeSync;
5309
+ if (endpoint.kind === "PushDiffusion") {
5310
+ activeAsync = endpoint.pushDiffusion?.SubscribeAsync
5311
+ ?? endpoint.pushDiffusion?.subscribeAsync;
5312
+ }
5313
+ else if (endpoint.kind === "Grpc") {
5314
+ activeAsync = endpoint.grpc?.ConsumeAsync
5315
+ ?? endpoint.grpc?.consumeAsync;
5316
+ activeSync = endpoint.grpc?.Consume
5317
+ ?? endpoint.grpc?.consume;
5318
+ }
5319
+ else if (endpoint.kind === "WebSocket") {
5320
+ activeAsync = endpoint.webSocket?.ConsumeAsync
5321
+ ?? endpoint.webSocket?.consumeAsync;
5322
+ activeSync = endpoint.webSocket?.Consume
5323
+ ?? endpoint.webSocket?.consume;
5324
+ }
5325
+ return {
5326
+ asyncDelegate: typeof activeAsync === "function" ? activeAsync : undefined,
5327
+ syncDelegate: typeof activeSync === "function"
5328
+ ? activeSync
5329
+ : undefined
5330
+ };
5331
+ }
4123
5332
  function resolveStructuredProduceDelegate(endpoint) {
4124
- return endpoint.delegate?.produceAsync
4125
- ?? endpoint.pushDiffusion?.PublishAsync
4126
- ?? endpoint.pushDiffusion?.publishAsync
4127
- ?? endpoint.grpc?.ProduceAsync
4128
- ?? endpoint.grpc?.produceAsync
4129
- ?? endpoint.webSocket?.ProduceAsync
4130
- ?? endpoint.webSocket?.produceAsync;
5333
+ return selectProduceCallbackGroup(endpoint).asyncDelegate;
5334
+ }
5335
+ function resolveProduceDelegate(endpoint) {
5336
+ return selectProduceCallbackGroup(endpoint).syncDelegate;
4131
5337
  }
4132
5338
  function resolveStructuredConsumeDelegate(endpoint) {
4133
- const delegate = endpoint.delegate?.consumeAsync
4134
- ?? endpoint.pushDiffusion?.SubscribeAsync
4135
- ?? endpoint.pushDiffusion?.subscribeAsync
4136
- ?? endpoint.grpc?.ConsumeAsync
4137
- ?? endpoint.grpc?.consumeAsync
4138
- ?? endpoint.webSocket?.ConsumeAsync
4139
- ?? endpoint.webSocket?.consumeAsync;
5339
+ const delegate = selectConsumeCallbackGroup(endpoint).asyncDelegate;
4140
5340
  return typeof delegate === "function" && delegate.length === 0
4141
5341
  ? delegate
4142
5342
  : undefined;
4143
5343
  }
4144
5344
  function resolveStructuredConsumeStreamDelegate(endpoint) {
4145
- const delegate = endpoint.delegate?.consumeAsync
4146
- ?? endpoint.pushDiffusion?.SubscribeAsync
4147
- ?? endpoint.pushDiffusion?.subscribeAsync
4148
- ?? endpoint.grpc?.ConsumeAsync
4149
- ?? endpoint.grpc?.consumeAsync
4150
- ?? endpoint.webSocket?.ConsumeAsync
4151
- ?? endpoint.webSocket?.consumeAsync;
5345
+ const delegate = selectConsumeCallbackGroup(endpoint).asyncDelegate;
4152
5346
  return typeof delegate === "function" && delegate.length > 0
4153
5347
  ? delegate
4154
5348
  : undefined;
4155
5349
  }
4156
- function resolveProduceDelegate(endpoint) {
4157
- return endpoint.delegate?.produce
4158
- ?? endpoint.grpc?.Produce
4159
- ?? endpoint.grpc?.produce
4160
- ?? endpoint.webSocket?.Produce
4161
- ?? endpoint.webSocket?.produce;
4162
- }
4163
5350
  function resolveConsumeDelegate(endpoint) {
4164
- return endpoint.delegate?.consume
4165
- ?? endpoint.grpc?.Consume
4166
- ?? endpoint.grpc?.consume
4167
- ?? endpoint.webSocket?.Consume
4168
- ?? endpoint.webSocket?.consume;
5351
+ return selectConsumeCallbackGroup(endpoint).syncDelegate;
4169
5352
  }
4170
5353
  function resolveConnectionMetadata(endpoint) {
5354
+ const activeProtocolMetadata = endpoint.kind === "PushDiffusion"
5355
+ ? toStringRecord(endpoint.pushDiffusion?.ConnectionProperties ?? endpoint.pushDiffusion?.connectionProperties)
5356
+ : endpoint.kind === "Grpc"
5357
+ ? toStringRecord(endpoint.grpc?.ConnectionMetadata ?? endpoint.grpc?.connectionMetadata)
5358
+ : endpoint.kind === "WebSocket"
5359
+ ? toStringRecord(endpoint.webSocket?.ConnectionMetadata ?? endpoint.webSocket?.connectionMetadata)
5360
+ : {};
4171
5361
  return {
5362
+ ...activeProtocolMetadata,
4172
5363
  ...(endpoint.connectionMetadata ?? {}),
4173
- ...(endpoint.delegate?.connectionMetadata ?? {}),
4174
- ...toStringRecord(endpoint.pushDiffusion?.ConnectionProperties ?? endpoint.pushDiffusion?.connectionProperties),
4175
- ...toStringRecord(endpoint.grpc?.ConnectionMetadata ?? endpoint.grpc?.connectionMetadata),
4176
- ...toStringRecord(endpoint.webSocket?.ConnectionMetadata ?? endpoint.webSocket?.connectionMetadata)
5364
+ ...(endpoint.delegate?.connectionMetadata ?? {})
4177
5365
  };
4178
5366
  }
4179
5367
  function isStructuredProduceResult(value) {