@loadstrike/loadstrike-sdk 1.0.22601 → 1.0.23001

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.
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.__loadstrikeTestExports = exports.LoadStrikeRunner = exports.LoadStrikeScenario = exports.LoadStrikeContext = exports.ScenarioTrackingExtensions = exports.CrossPlatformScenarioConfigurator = exports.LoadStrikeThreshold = exports.LoadStrikeMetric = exports.LoadStrikeGauge = exports.LoadStrikeCounter = exports.LoadStrikeSimulation = exports.LoadStrikeStep = exports.LoadStrikeResponse = exports.LoadStrikePluginData = exports.LoadStrikePluginDataTable = exports.LoadStrikeOperationType = exports.LoadStrikeScenarioOperation = exports.LoadStrikeLogLevel = exports.LoadStrikeReportFormat = exports.LoadStrikeNodeType = void 0;
6
+ exports.__loadstrikeTestExports = exports.LoadStrikeRunner = exports.LoadStrikeScenario = exports.LoadStrikeContext = exports.ScenarioTrackingExtensions = exports.CrossPlatformScenarioConfigurator = exports.LoadStrikeThreshold = exports.LoadStrikeMetric = exports.LoadStrikeGauge = exports.LoadStrikeCounter = exports.LoadStrikeSimulation = exports.LoadStrikeStep = exports.LoadStrikeResponse = exports.LoadStrikePluginData = exports.LoadStrikePluginDataTable = exports.CrossPlatformTrackingConfiguration = exports.LoadStrikeOperationType = exports.LoadStrikeScenarioOperation = exports.LoadStrikeLogLevel = exports.LoadStrikeReportFormat = exports.LoadStrikeNodeType = void 0;
7
7
  const node_fs_1 = require("node:fs");
8
8
  const node_crypto_1 = require("node:crypto");
9
9
  const node_os_1 = __importDefault(require("node:os"));
@@ -47,6 +47,33 @@ exports.LoadStrikeOperationType = {
47
47
  Complete: "Complete",
48
48
  Error: "Error"
49
49
  };
50
+ exports.CrossPlatformTrackingConfiguration = {
51
+ forDuration(configuration, durationSeconds, cancellationSignal) {
52
+ if (!configuration || typeof configuration !== "object" || Array.isArray(configuration)) {
53
+ throw new TypeError("Tracking configuration must be provided.");
54
+ }
55
+ if (normalizeTrackingRunMode(pickTrackingString(configuration, "RunMode", "runMode", "GenerateAndCorrelate")) !== "correlateexistingtraffic") {
56
+ throw new Error("ForDuration can only be used with CorrelateExistingTraffic.");
57
+ }
58
+ if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
59
+ throw new RangeError("ForDuration duration must be greater than zero.");
60
+ }
61
+ const result = {
62
+ ...configuration,
63
+ ObservationDurationSeconds: durationSeconds
64
+ };
65
+ if (cancellationSignal !== undefined) {
66
+ if (!(cancellationSignal instanceof AbortSignal)) {
67
+ throw new TypeError("ForDuration cancellation signal must be an AbortSignal.");
68
+ }
69
+ result.ObservationCancellationSignal = cancellationSignal;
70
+ }
71
+ return result;
72
+ },
73
+ ForDuration(configuration, durationSeconds, cancellationSignal) {
74
+ return this.forDuration(configuration, durationSeconds, cancellationSignal);
75
+ }
76
+ };
50
77
  class LoadStrikePluginDataTable {
51
78
  /**
52
79
  * Exposes the public constructor operation.
@@ -1730,6 +1757,9 @@ class LoadStrikeScenario {
1730
1757
  ? mapRuntimeTrackingEndpointSpec(destinationSpec, useLoadStrikeTraceIdHeader)
1731
1758
  : null;
1732
1759
  validateRuntimeTrackingConfiguration(copied, sourceEndpoint, destinationEndpoint);
1760
+ if (isCorrelateExistingTrafficTracking(copied) && this.loadSimulations.length > 0) {
1761
+ throw new Error("CorrelateExistingTraffic uses ForDuration and cannot be combined with WithLoadSimulations.");
1762
+ }
1733
1763
  return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, copied, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail, this.internalLicenseFeatures);
1734
1764
  }
1735
1765
  /**
@@ -1740,6 +1770,9 @@ class LoadStrikeScenario {
1740
1770
  if (!simulations.length) {
1741
1771
  throw new Error("At least one load simulation should be provided.");
1742
1772
  }
1773
+ if (isCorrelateExistingTrafficTracking(this.trackingConfiguration)) {
1774
+ throw new Error("CorrelateExistingTraffic uses ForDuration and cannot be combined with WithLoadSimulations.");
1775
+ }
1743
1776
  return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, simulations.map((simulation) => attachLoadSimulationProjection({ ...simulation })), this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail, this.internalLicenseFeatures);
1744
1777
  }
1745
1778
  /**
@@ -5741,6 +5774,14 @@ class ManagedScenarioTrackingRuntime {
5741
5774
  this.sourceOnlyMode = this.destinationEndpoint == null;
5742
5775
  this.executeOriginalScenarioRun = pickTrackingBoolean(tracking, "ExecuteOriginalScenarioRun", "executeOriginalScenarioRun", false);
5743
5776
  this.correlationTimeoutMs = Math.trunc(pickTrackingNumber(tracking, ["CorrelationTimeoutMs", "correlationTimeoutMs"], pickTrackingNumber(tracking, ["CorrelationTimeoutSeconds", "correlationTimeoutSeconds", "CorrelationTimeout"], 30) * 1000));
5777
+ this.observationDurationMs = Math.trunc(readObservationDurationMs(tracking));
5778
+ const observationCancellationSignal = pickTrackingValue(tracking, "ObservationCancellationSignal", "observationCancellationSignal");
5779
+ if (observationCancellationSignal !== undefined) {
5780
+ if (!(observationCancellationSignal instanceof AbortSignal)) {
5781
+ throw new TypeError("ObservationCancellationSignal must be an AbortSignal.");
5782
+ }
5783
+ this.observationCancellationSignal = observationCancellationSignal;
5784
+ }
5744
5785
  this.timeoutSweepIntervalMs = Math.trunc(pickTrackingNumber(tracking, ["TimeoutSweepIntervalMs", "timeoutSweepIntervalMs"], pickTrackingNumber(tracking, ["TimeoutSweepIntervalSeconds", "timeoutSweepIntervalSeconds"], 1) * 1000));
5745
5786
  this.timeoutBatchSize = Math.trunc(pickTrackingNumber(tracking, ["TimeoutBatchSize", "timeoutBatchSize"], 200));
5746
5787
  this.timeoutCountsAsFailure = pickTrackingBoolean(tracking, "TimeoutCountsAsFailure", "timeoutCountsAsFailure", true);
@@ -5881,8 +5922,7 @@ class ManagedScenarioTrackingRuntime {
5881
5922
  return this.executeSourceOnly();
5882
5923
  }
5883
5924
  if (this.runMode === "correlateexistingtraffic") {
5884
- const outcome = await this.waitForOutcome(context.scenarioCancellationToken);
5885
- return this.toResponse(outcome);
5925
+ return await this.observeExistingTraffic(context.scenarioCancellationToken);
5886
5926
  }
5887
5927
  let producedPayload;
5888
5928
  try {
@@ -5958,6 +5998,45 @@ class ManagedScenarioTrackingRuntime {
5958
5998
  });
5959
5999
  return LoadStrikeResponse.ok("source_success");
5960
6000
  }
6001
+ async observeExistingTraffic(signal) {
6002
+ const deadline = Date.now() + this.observationDurationMs;
6003
+ const observationSignal = this.observationCancellationSignal
6004
+ ? combineAbortSignals(signal, this.observationCancellationSignal)
6005
+ : signal;
6006
+ let failureSeen = false;
6007
+ while (Date.now() < deadline && !observationSignal.aborted) {
6008
+ let outcome;
6009
+ try {
6010
+ outcome = await this.waitForOutcomeUntil(deadline, observationSignal);
6011
+ }
6012
+ catch (error) {
6013
+ if (this.observationCancellationSignal?.aborted && !signal.aborted) {
6014
+ break;
6015
+ }
6016
+ throw error;
6017
+ }
6018
+ if (!outcome) {
6019
+ break;
6020
+ }
6021
+ failureSeen = failureSeen || this.isFailedObservationOutcome(outcome);
6022
+ }
6023
+ this.shutdown = true;
6024
+ await Promise.all([
6025
+ this.sourceLoop,
6026
+ this.destinationLoop,
6027
+ this.timeoutLoop
6028
+ ].filter(Boolean).map((value) => value.catch(() => { })));
6029
+ await this.correlationRuntime.sweepTimeouts(Date.now() + this.correlationTimeoutMs + 1, this.timeoutBatchSize);
6030
+ while (this.outcomeQueue.length > 0) {
6031
+ const outcome = this.outcomeQueue.shift();
6032
+ if (outcome) {
6033
+ failureSeen = failureSeen || this.isFailedObservationOutcome(outcome);
6034
+ }
6035
+ }
6036
+ return failureSeen
6037
+ ? LoadStrikeResponse.fail("tracking_failures", "One or more observed source or destination events did not correlate successfully.", 0)
6038
+ : LoadStrikeResponse.ok("observed");
6039
+ }
5961
6040
  async consumeSourceLoop() {
5962
6041
  while (!this.shutdown) {
5963
6042
  try {
@@ -6171,6 +6250,34 @@ class ManagedScenarioTrackingRuntime {
6171
6250
  }
6172
6251
  }
6173
6252
  }
6253
+ async waitForOutcomeUntil(deadlineMs, signal) {
6254
+ const existing = this.outcomeQueue.shift();
6255
+ if (existing) {
6256
+ return existing;
6257
+ }
6258
+ const remainingMs = deadlineMs - Date.now();
6259
+ if (remainingMs <= 0) {
6260
+ return null;
6261
+ }
6262
+ const waiter = createManagedTrackingWaiter();
6263
+ this.outcomeWaiters.push(waiter);
6264
+ const timeout = sleep(remainingMs).then(() => null);
6265
+ try {
6266
+ return await Promise.race([waiter.promise, timeout, waitForAbort(signal)]);
6267
+ }
6268
+ finally {
6269
+ const index = this.outcomeWaiters.indexOf(waiter);
6270
+ if (index >= 0) {
6271
+ this.outcomeWaiters.splice(index, 1);
6272
+ }
6273
+ }
6274
+ }
6275
+ isFailedObservationOutcome(outcome) {
6276
+ if (outcome.status === "timeout") {
6277
+ return this.timeoutCountsAsFailure;
6278
+ }
6279
+ return outcome.status !== "matched";
6280
+ }
6174
6281
  rejectOutstandingWaiters() {
6175
6282
  for (const rows of this.waiters.values()) {
6176
6283
  for (const waiter of rows) {
@@ -6290,8 +6397,12 @@ function validateRuntimeTrackingConfiguration(tracking, sourceEndpoint, destinat
6290
6397
  if (!metricPrefix.trim()) {
6291
6398
  throw new Error("MetricPrefix must be provided.");
6292
6399
  }
6400
+ const observationDurationMs = readObservationDurationMs(tracking);
6293
6401
  validateRuntimeRedisCorrelationStoreConfiguration(tracking);
6294
6402
  if (runMode === "generateandcorrelate") {
6403
+ if (observationDurationMs > 0) {
6404
+ throw new Error("ForDuration is only supported for CorrelateExistingTraffic.");
6405
+ }
6295
6406
  if (sourceEndpoint.mode !== "Produce") {
6296
6407
  throw new Error("Source endpoint mode must be Produce for GenerateAndCorrelate mode.");
6297
6408
  }
@@ -6301,6 +6412,9 @@ function validateRuntimeTrackingConfiguration(tracking, sourceEndpoint, destinat
6301
6412
  return;
6302
6413
  }
6303
6414
  if (runMode === "correlateexistingtraffic") {
6415
+ if (observationDurationMs <= 0) {
6416
+ throw new Error("CorrelateExistingTraffic requires ForDuration with a duration greater than zero.");
6417
+ }
6304
6418
  if (!destinationEndpoint) {
6305
6419
  throw new Error("Destination endpoint must be provided for CorrelateExistingTraffic mode.");
6306
6420
  }
@@ -6314,6 +6428,15 @@ function validateRuntimeTrackingConfiguration(tracking, sourceEndpoint, destinat
6314
6428
  }
6315
6429
  throw new RangeError(`Unsupported tracking run mode: ${runMode}.`);
6316
6430
  }
6431
+ function readObservationDurationMs(tracking) {
6432
+ return pickTrackingNumber(tracking, ["ObservationDurationMs", "observationDurationMs"], pickTrackingNumber(tracking, ["ObservationDurationSeconds", "observationDurationSeconds", "ObservationDuration"], 0) * 1000);
6433
+ }
6434
+ function isCorrelateExistingTrafficTracking(configuration) {
6435
+ if (!configuration) {
6436
+ return false;
6437
+ }
6438
+ return normalizeTrackingRunMode(pickTrackingString(configuration, "RunMode", "runMode", "GenerateAndCorrelate")) === "correlateexistingtraffic";
6439
+ }
6317
6440
  function readUseLoadStrikeTraceIdHeader(tracking) {
6318
6441
  return pickTrackingBoolean(tracking, "UseLoadStrikeTraceIdHeader", "useLoadStrikeTraceIdHeader", false);
6319
6442
  }
@@ -6503,6 +6626,7 @@ function mapRuntimeTrackingEndpointSpec(spec, useLoadStrikeTraceIdHeader = false
6503
6626
  nats: asTrackingRecord(pickTrackingValue(spec, "Nats", "nats")),
6504
6627
  redisStreams: asTrackingRecord(pickTrackingValue(spec, "RedisStreams", "redisStreams")),
6505
6628
  azureEventHubs: asTrackingRecord(pickTrackingValue(spec, "AzureEventHubs", "azureEventHubs")),
6629
+ sqs: asTrackingRecord(pickTrackingValue(spec, "Sqs", "sqs")),
6506
6630
  pushDiffusion: asTrackingRecord(pickTrackingValue(spec, "PushDiffusion", "pushDiffusion")),
6507
6631
  delegate: typeof delegateProduce === "function"
6508
6632
  || typeof delegateConsume === "function"
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.__loadstrikeTestExports = exports.EndpointAdapterFactory = exports.KafkaSaslOptions = exports.HttpAuthOptions = exports.HttpOAuth2ClientCredentialsOptions = exports.PushDiffusionEndpointDefinition = exports.DelegateStreamEndpointDefinition = exports.AzureEventHubsEndpointDefinition = exports.RedisStreamsEndpointDefinition = exports.NatsEndpointDefinition = exports.RabbitMqEndpointDefinition = exports.KafkaEndpointDefinition = exports.HttpEndpointDefinition = exports.TrafficEndpointDefinition = exports.LOADSTRIKE_TRACE_ID_TRACKING_FIELD = exports.LOADSTRIKE_TRACE_ID_HEADER = void 0;
36
+ exports.__loadstrikeTestExports = exports.EndpointAdapterFactory = exports.KafkaSaslOptions = exports.HttpAuthOptions = exports.HttpOAuth2ClientCredentialsOptions = exports.PushDiffusionEndpointDefinition = exports.DelegateStreamEndpointDefinition = exports.SqsEndpointDefinition = exports.AzureEventHubsEndpointDefinition = exports.RedisStreamsEndpointDefinition = exports.NatsEndpointDefinition = exports.RabbitMqEndpointDefinition = exports.KafkaEndpointDefinition = exports.HttpEndpointDefinition = exports.TrafficEndpointDefinition = exports.LOADSTRIKE_TRACE_ID_TRACKING_FIELD = exports.LOADSTRIKE_TRACE_ID_HEADER = void 0;
37
37
  const node_crypto_1 = require("node:crypto");
38
38
  const correlation_js_1 = require("./correlation.js");
39
39
  exports.LOADSTRIKE_TRACE_ID_HEADER = "loadstrike-trace-id";
@@ -254,6 +254,35 @@ class AzureEventHubsEndpointDefinitionModel extends TrafficEndpointDefinitionMod
254
254
  }
255
255
  }
256
256
  }
257
+ class SqsEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
258
+ constructor(initial) {
259
+ super(initial);
260
+ this.Kind = "Sqs";
261
+ this.QueueUrl = "";
262
+ this.Region = "";
263
+ this.WaitTimeSeconds = 1;
264
+ this.MaxNumberOfMessages = 1;
265
+ this.DeleteAfterConsume = true;
266
+ initializeSqsEndpointDefinitionModel(this, initial);
267
+ }
268
+ Validate() {
269
+ super.Validate();
270
+ requireNonEmptyString(this.QueueUrl, "QueueUrl must be provided for AWS SQS endpoint.");
271
+ requireNonEmptyString(this.Region, "Region must be provided for AWS SQS endpoint.");
272
+ if (String(this.AccessKeyId ?? "").trim() && !String(this.SecretAccessKey ?? "").trim()) {
273
+ throw new Error("SecretAccessKey must be provided when AccessKeyId is configured for AWS SQS endpoint.");
274
+ }
275
+ if (this.WaitTimeSeconds < 0 || this.WaitTimeSeconds > 20) {
276
+ throw new RangeError("WaitTimeSeconds must be between 0 and 20 for AWS SQS endpoint.");
277
+ }
278
+ if (this.MaxNumberOfMessages <= 0 || this.MaxNumberOfMessages > 10) {
279
+ throw new RangeError("MaxNumberOfMessages must be between 1 and 10 for AWS SQS endpoint.");
280
+ }
281
+ if (this.VisibilityTimeoutSeconds != null && this.VisibilityTimeoutSeconds < 0) {
282
+ throw new RangeError("VisibilityTimeoutSeconds must be zero or greater when configured.");
283
+ }
284
+ }
285
+ }
257
286
  class DelegateStreamEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
258
287
  constructor(initial) {
259
288
  super(initial);
@@ -299,6 +328,7 @@ exports.RabbitMqEndpointDefinition = RabbitMqEndpointDefinitionModel;
299
328
  exports.NatsEndpointDefinition = NatsEndpointDefinitionModel;
300
329
  exports.RedisStreamsEndpointDefinition = RedisStreamsEndpointDefinitionModel;
301
330
  exports.AzureEventHubsEndpointDefinition = AzureEventHubsEndpointDefinitionModel;
331
+ exports.SqsEndpointDefinition = SqsEndpointDefinitionModel;
302
332
  exports.DelegateStreamEndpointDefinition = DelegateStreamEndpointDefinitionModel;
303
333
  exports.PushDiffusionEndpointDefinition = PushDiffusionEndpointDefinitionModel;
304
334
  exports.HttpOAuth2ClientCredentialsOptions = HttpOAuth2ClientCredentialsOptionsModel;
@@ -662,6 +692,44 @@ function initializeAzureEventHubsEndpointDefinitionModel(target, initial) {
662
692
  target.PartitionCount = pickOptionalEndpointNumber(raw, "PartitionCount", "partitionCount");
663
693
  }
664
694
  }
695
+ function initializeSqsEndpointDefinitionModel(target, initial) {
696
+ const raw = asRecordOrEmpty(initial);
697
+ const queueUrl = pickOptionalEndpointString(raw, "QueueUrl", "queueUrl");
698
+ if (queueUrl) {
699
+ target.QueueUrl = queueUrl;
700
+ }
701
+ const region = pickOptionalEndpointString(raw, "Region", "region");
702
+ if (region) {
703
+ target.Region = region;
704
+ }
705
+ if (hasAnyEndpointField(raw, ["ServiceUrl", "serviceUrl"])) {
706
+ target.ServiceUrl = pickOptionalEndpointString(raw, "ServiceUrl", "serviceUrl");
707
+ }
708
+ if (hasAnyEndpointField(raw, ["AccessKeyId", "accessKeyId"])) {
709
+ target.AccessKeyId = pickOptionalEndpointString(raw, "AccessKeyId", "accessKeyId");
710
+ }
711
+ if (hasAnyEndpointField(raw, ["SecretAccessKey", "secretAccessKey"])) {
712
+ target.SecretAccessKey = pickOptionalEndpointStringAllowEmpty(raw, "SecretAccessKey", "secretAccessKey");
713
+ }
714
+ if (hasAnyEndpointField(raw, ["SessionToken", "sessionToken"])) {
715
+ target.SessionToken = pickOptionalEndpointString(raw, "SessionToken", "sessionToken");
716
+ }
717
+ const waitTimeSeconds = pickOptionalEndpointNumber(raw, "WaitTimeSeconds", "waitTimeSeconds");
718
+ if (waitTimeSeconds != null) {
719
+ target.WaitTimeSeconds = waitTimeSeconds;
720
+ }
721
+ const maxNumberOfMessages = pickOptionalEndpointNumber(raw, "MaxNumberOfMessages", "maxNumberOfMessages");
722
+ if (maxNumberOfMessages != null) {
723
+ target.MaxNumberOfMessages = maxNumberOfMessages;
724
+ }
725
+ if (hasAnyEndpointField(raw, ["VisibilityTimeoutSeconds", "visibilityTimeoutSeconds"])) {
726
+ target.VisibilityTimeoutSeconds = pickOptionalEndpointNumber(raw, "VisibilityTimeoutSeconds", "visibilityTimeoutSeconds");
727
+ }
728
+ const deleteAfterConsume = pickEndpointBoolean(raw, "DeleteAfterConsume", "deleteAfterConsume");
729
+ if (deleteAfterConsume != null) {
730
+ target.DeleteAfterConsume = deleteAfterConsume;
731
+ }
732
+ }
665
733
  function initializeDelegateStreamEndpointDefinitionModel(target, initial) {
666
734
  const raw = asRecordOrEmpty(initial);
667
735
  const produce = pickEndpointFunction(raw, "Produce", "produce");
@@ -798,6 +866,7 @@ const protocolBus = new class InMemoryProtocolBus {
798
866
  this.redisCounters = new Map();
799
867
  this.eventHubs = new Map();
800
868
  this.eventHubOffsets = new Map();
869
+ this.sqsQueues = new Map();
801
870
  this.pushTopics = new Map();
802
871
  this.pushOffsets = new Map();
803
872
  }
@@ -812,6 +881,7 @@ const protocolBus = new class InMemoryProtocolBus {
812
881
  this.redisCounters.clear();
813
882
  this.eventHubs.clear();
814
883
  this.eventHubOffsets.clear();
884
+ this.sqsQueues.clear();
815
885
  this.pushTopics.clear();
816
886
  this.pushOffsets.clear();
817
887
  }
@@ -976,6 +1046,25 @@ const protocolBus = new class InMemoryProtocolBus {
976
1046
  this.eventHubOffsets.set(cursorKey, rows.length);
977
1047
  return null;
978
1048
  }
1049
+ produceSqs(endpoint, payload) {
1050
+ const options = endpoint.sqs ?? {};
1051
+ const queueUrl = optionString(options, "QueueUrl", "queueUrl") || endpoint.name;
1052
+ const queue = this.sqsQueues.get(queueUrl) ?? [];
1053
+ queue.push({ payload: clonePayload(payload), timestampMs: Date.now() });
1054
+ this.sqsQueues.set(queueUrl, queue);
1055
+ return clonePayload(payload);
1056
+ }
1057
+ consumeSqs(endpoint) {
1058
+ const options = endpoint.sqs ?? {};
1059
+ const queueUrl = optionString(options, "QueueUrl", "queueUrl") || endpoint.name;
1060
+ const queue = this.sqsQueues.get(queueUrl) ?? [];
1061
+ if (!queue.length) {
1062
+ return null;
1063
+ }
1064
+ const next = queue.shift() ?? null;
1065
+ this.sqsQueues.set(queueUrl, queue);
1066
+ return next ? clonePayload(next.payload) : null;
1067
+ }
979
1068
  producePush(endpoint, payload) {
980
1069
  const options = endpoint.pushDiffusion ?? {};
981
1070
  const topic = optionString(options, "TopicPath", "topicPath")
@@ -1717,6 +1806,104 @@ class AzureEventHubsEndpointAdapter extends CallbackAdapter {
1717
1806
  await this.subscriptionPromise;
1718
1807
  }
1719
1808
  }
1809
+ let sqsClientFactoryForTests = null;
1810
+ class SqsEndpointAdapter extends CallbackAdapter {
1811
+ constructor() {
1812
+ super(...arguments);
1813
+ this.clientPromise = null;
1814
+ }
1815
+ async produce(payload) {
1816
+ if (resolveProduceDelegate(this.endpoint)) {
1817
+ return super.produce(payload);
1818
+ }
1819
+ if (this.endpoint.mode !== "Produce") {
1820
+ return null;
1821
+ }
1822
+ const client = await this.getClient();
1823
+ const options = this.endpoint.sqs ?? {};
1824
+ const resolved = prepareProducedPayload(this.endpoint, payload);
1825
+ const wire = toWirePayload(resolved, this.endpoint);
1826
+ const { SendMessageCommand } = await Promise.resolve().then(() => __importStar(require("@aws-sdk/client-sqs")));
1827
+ await client.send(new SendMessageCommand({
1828
+ QueueUrl: optionString(options, "QueueUrl", "queueUrl"),
1829
+ MessageBody: payloadBodyAsUtf8(wire.body),
1830
+ MessageAttributes: toSqsMessageAttributes(wire.headers, wire.contentType)
1831
+ }));
1832
+ return clonePayload(resolved);
1833
+ }
1834
+ async consume() {
1835
+ if (resolveConsumeDelegate(this.endpoint)) {
1836
+ return super.consume();
1837
+ }
1838
+ if (this.endpoint.mode !== "Consume") {
1839
+ return null;
1840
+ }
1841
+ const client = await this.getClient();
1842
+ const options = this.endpoint.sqs ?? {};
1843
+ const { ReceiveMessageCommand, DeleteMessageCommand } = await Promise.resolve().then(() => __importStar(require("@aws-sdk/client-sqs")));
1844
+ const input = {
1845
+ QueueUrl: optionString(options, "QueueUrl", "queueUrl"),
1846
+ MaxNumberOfMessages: hasOptionValue(options, "MaxNumberOfMessages", "maxNumberOfMessages")
1847
+ ? optionNumber(options, "MaxNumberOfMessages", "maxNumberOfMessages")
1848
+ : 1,
1849
+ WaitTimeSeconds: hasOptionValue(options, "WaitTimeSeconds", "waitTimeSeconds")
1850
+ ? optionNumber(options, "WaitTimeSeconds", "waitTimeSeconds")
1851
+ : 1,
1852
+ MessageAttributeNames: ["All"]
1853
+ };
1854
+ const visibilityTimeout = optionNumber(options, "VisibilityTimeoutSeconds", "visibilityTimeoutSeconds");
1855
+ if (visibilityTimeout > 0) {
1856
+ input.VisibilityTimeout = visibilityTimeout;
1857
+ }
1858
+ const response = await client.send(new ReceiveMessageCommand(input));
1859
+ const message = response.Messages?.[0];
1860
+ if (!message) {
1861
+ return null;
1862
+ }
1863
+ const { headers, contentType } = fromSqsMessageAttributes(message.MessageAttributes);
1864
+ const payload = createBrokerPayload(headers, Buffer.from(message.Body ?? "", "utf8"), this.endpoint, contentType);
1865
+ if (optionBoolean(options, true, "DeleteAfterConsume", "deleteAfterConsume") && message.ReceiptHandle) {
1866
+ await client.send(new DeleteMessageCommand({
1867
+ QueueUrl: optionString(options, "QueueUrl", "queueUrl"),
1868
+ ReceiptHandle: message.ReceiptHandle
1869
+ }));
1870
+ }
1871
+ return payload;
1872
+ }
1873
+ async dispose() {
1874
+ const client = await this.clientPromise?.catch(() => null);
1875
+ client?.destroy?.();
1876
+ }
1877
+ async getClient() {
1878
+ if (!this.clientPromise) {
1879
+ this.clientPromise = (async () => {
1880
+ if (sqsClientFactoryForTests) {
1881
+ return await sqsClientFactoryForTests(this.endpoint);
1882
+ }
1883
+ const { SQSClient } = await Promise.resolve().then(() => __importStar(require("@aws-sdk/client-sqs")));
1884
+ const options = this.endpoint.sqs ?? {};
1885
+ const accessKeyId = optionString(options, "AccessKeyId", "accessKeyId");
1886
+ const secretAccessKey = optionString(options, "SecretAccessKey", "secretAccessKey");
1887
+ const config = {
1888
+ region: optionString(options, "Region", "region")
1889
+ };
1890
+ const serviceUrl = optionString(options, "ServiceUrl", "serviceUrl");
1891
+ if (serviceUrl) {
1892
+ config.endpoint = serviceUrl;
1893
+ }
1894
+ if (accessKeyId) {
1895
+ config.credentials = {
1896
+ accessKeyId,
1897
+ secretAccessKey,
1898
+ sessionToken: optionString(options, "SessionToken", "sessionToken") || undefined
1899
+ };
1900
+ }
1901
+ return new SQSClient(config);
1902
+ })();
1903
+ }
1904
+ return this.clientPromise;
1905
+ }
1906
+ }
1720
1907
  class PushDiffusionEndpointAdapter extends CallbackAdapter {
1721
1908
  async produce(payload) {
1722
1909
  return super.produce(payload);
@@ -1747,6 +1934,8 @@ class EndpointAdapterFactory {
1747
1934
  return new RedisStreamsEndpointAdapter(normalized);
1748
1935
  case "AzureEventHubs":
1749
1936
  return new AzureEventHubsEndpointAdapter(normalized);
1937
+ case "Sqs":
1938
+ return new SqsEndpointAdapter(normalized);
1750
1939
  case "PushDiffusion":
1751
1940
  return new PushDiffusionEndpointAdapter(normalized);
1752
1941
  case "DelegateStream":
@@ -1875,6 +2064,28 @@ const AZURE_EVENT_HUBS_ENDPOINT_FLAT_KEYS = [
1875
2064
  "StartFromEarliest",
1876
2065
  "startFromEarliest"
1877
2066
  ];
2067
+ const SQS_ENDPOINT_FLAT_KEYS = [
2068
+ "QueueUrl",
2069
+ "queueUrl",
2070
+ "Region",
2071
+ "region",
2072
+ "ServiceUrl",
2073
+ "serviceUrl",
2074
+ "AccessKeyId",
2075
+ "accessKeyId",
2076
+ "SecretAccessKey",
2077
+ "secretAccessKey",
2078
+ "SessionToken",
2079
+ "sessionToken",
2080
+ "WaitTimeSeconds",
2081
+ "waitTimeSeconds",
2082
+ "MaxNumberOfMessages",
2083
+ "maxNumberOfMessages",
2084
+ "VisibilityTimeoutSeconds",
2085
+ "visibilityTimeoutSeconds",
2086
+ "DeleteAfterConsume",
2087
+ "deleteAfterConsume"
2088
+ ];
1878
2089
  const PUSH_DIFFUSION_ENDPOINT_FLAT_KEYS = [
1879
2090
  "ServerUrl",
1880
2091
  "serverUrl",
@@ -1935,6 +2146,7 @@ function normalizeEndpointDefinition(endpoint) {
1935
2146
  nats: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "Nats", ["nats", "Nats"], NATS_ENDPOINT_FLAT_KEYS)),
1936
2147
  redisStreams: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "RedisStreams", ["redisStreams", "RedisStreams"], REDIS_STREAMS_ENDPOINT_FLAT_KEYS)),
1937
2148
  azureEventHubs: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "AzureEventHubs", ["azureEventHubs", "AzureEventHubs"], AZURE_EVENT_HUBS_ENDPOINT_FLAT_KEYS)),
2149
+ sqs: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "Sqs", ["sqs", "Sqs"], SQS_ENDPOINT_FLAT_KEYS)),
1938
2150
  pushDiffusion: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "PushDiffusion", ["pushDiffusion", "PushDiffusion"], PUSH_DIFFUSION_ENDPOINT_FLAT_KEYS)),
1939
2151
  delegate: normalizeDelegateEndpointOptions(pickEndpointTransportRecord(raw, kind, "DelegateStream", ["delegate", "Delegate", "DelegateStream"], DELEGATE_STREAM_ENDPOINT_FLAT_KEYS))
1940
2152
  };
@@ -1992,6 +2204,22 @@ function resolveEndpointKind(raw) {
1992
2204
  ])) {
1993
2205
  return "AzureEventHubs";
1994
2206
  }
2207
+ if (Object.keys(pickEndpointRecord(raw, "sqs", "Sqs")).length || hasAnyEndpointField(raw, [
2208
+ "QueueUrl",
2209
+ "queueUrl",
2210
+ "ServiceUrl",
2211
+ "serviceUrl",
2212
+ "WaitTimeSeconds",
2213
+ "waitTimeSeconds",
2214
+ "MaxNumberOfMessages",
2215
+ "maxNumberOfMessages",
2216
+ "VisibilityTimeoutSeconds",
2217
+ "visibilityTimeoutSeconds",
2218
+ "DeleteAfterConsume",
2219
+ "deleteAfterConsume"
2220
+ ])) {
2221
+ return "Sqs";
2222
+ }
1995
2223
  if (Object.keys(pickEndpointRecord(raw, "pushDiffusion", "PushDiffusion")).length || hasAnyEndpointField(raw, [
1996
2224
  "TopicPath",
1997
2225
  "topicPath",
@@ -2273,6 +2501,9 @@ function validateEndpointDefinition(endpoint) {
2273
2501
  case "AzureEventHubs":
2274
2502
  validateAzureEventHubsEndpoint(endpoint, mode, hasModeDelegate);
2275
2503
  return;
2504
+ case "Sqs":
2505
+ validateSqsEndpoint(endpoint, mode, hasModeDelegate);
2506
+ return;
2276
2507
  case "PushDiffusion":
2277
2508
  validatePushDiffusionEndpoint(endpoint, mode);
2278
2509
  return;
@@ -2494,6 +2725,32 @@ function validateAzureEventHubsEndpoint(endpoint, mode, hasModeDelegate) {
2494
2725
  throw new RangeError("PartitionCount must be zero or greater when configured.");
2495
2726
  }
2496
2727
  }
2728
+ function validateSqsEndpoint(endpoint, mode, hasModeDelegate) {
2729
+ const options = endpoint.sqs;
2730
+ if ((!options || typeof options !== "object") && !hasModeDelegate) {
2731
+ throw new Error(`Sqs endpoint in ${mode} mode requires sqs options or delegate callback.`);
2732
+ }
2733
+ if (!options || typeof options !== "object") {
2734
+ return;
2735
+ }
2736
+ requireNonEmptyString(optionString(options, "QueueUrl", "queueUrl"), "QueueUrl must be provided for AWS SQS endpoint.");
2737
+ requireNonEmptyString(optionString(options, "Region", "region"), "Region must be provided for AWS SQS endpoint.");
2738
+ if (optionString(options, "AccessKeyId", "accessKeyId") && !optionString(options, "SecretAccessKey", "secretAccessKey")) {
2739
+ throw new Error("SecretAccessKey must be provided when AccessKeyId is configured for AWS SQS endpoint.");
2740
+ }
2741
+ const waitTimeSeconds = optionNumber(options, "WaitTimeSeconds", "waitTimeSeconds");
2742
+ if (waitTimeSeconds < 0 || waitTimeSeconds > 20) {
2743
+ throw new RangeError("WaitTimeSeconds must be between 0 and 20 for AWS SQS endpoint.");
2744
+ }
2745
+ const maxNumberOfMessages = optionNumber(options, "MaxNumberOfMessages", "maxNumberOfMessages") || 1;
2746
+ if (maxNumberOfMessages <= 0 || maxNumberOfMessages > 10) {
2747
+ throw new RangeError("MaxNumberOfMessages must be between 1 and 10 for AWS SQS endpoint.");
2748
+ }
2749
+ const visibilityTimeoutSeconds = optionNumber(options, "VisibilityTimeoutSeconds", "visibilityTimeoutSeconds");
2750
+ if (visibilityTimeoutSeconds < 0) {
2751
+ throw new RangeError("VisibilityTimeoutSeconds must be zero or greater when configured.");
2752
+ }
2753
+ }
2497
2754
  function validatePushDiffusionEndpoint(endpoint, mode) {
2498
2755
  const options = endpoint.pushDiffusion;
2499
2756
  if (!options || typeof options !== "object") {
@@ -2858,6 +3115,12 @@ function optionNumber(options, ...keys) {
2858
3115
  }
2859
3116
  return 0;
2860
3117
  }
3118
+ function hasOptionValue(options, ...keys) {
3119
+ return keys.some((key) => {
3120
+ const value = options[key];
3121
+ return value !== undefined && value !== null && (!(typeof value === "string") || value.trim() !== "");
3122
+ });
3123
+ }
2861
3124
  function optionBoolean(options, fallback, ...keys) {
2862
3125
  for (const key of keys) {
2863
3126
  const value = options[key];
@@ -3741,6 +4004,29 @@ function createRedisStreamPayload(fields, endpoint) {
3741
4004
  }
3742
4005
  return createBrokerPayload(headers, body, endpoint, contentType);
3743
4006
  }
4007
+ function toSqsMessageAttributes(headers, contentType) {
4008
+ const attributes = {};
4009
+ for (const [key, value] of Object.entries(headers)) {
4010
+ attributes[key] = { DataType: "String", StringValue: String(value ?? "") };
4011
+ }
4012
+ if (contentType) {
4013
+ attributes["content-type"] = { DataType: "String", StringValue: contentType };
4014
+ }
4015
+ return attributes;
4016
+ }
4017
+ function fromSqsMessageAttributes(attributes) {
4018
+ const headers = {};
4019
+ let contentType;
4020
+ for (const [key, value] of Object.entries(attributes ?? {})) {
4021
+ const text = String(value?.StringValue ?? "");
4022
+ if (key.toLowerCase() === "content-type") {
4023
+ contentType = text;
4024
+ continue;
4025
+ }
4026
+ headers[key] = text;
4027
+ }
4028
+ return { headers, contentType };
4029
+ }
3744
4030
  function payloadBodyAsUtf8(body) {
3745
4031
  if (body instanceof Uint8Array) {
3746
4032
  return new TextDecoder().decode(body);
@@ -3775,6 +4061,9 @@ exports.__loadstrikeTestExports = {
3775
4061
  RedisStreamsEndpointAdapter,
3776
4062
  RedisStreamsEndpointDefinition: exports.RedisStreamsEndpointDefinition,
3777
4063
  RedisStreamsEndpointDefinitionModel,
4064
+ SqsEndpointAdapter,
4065
+ SqsEndpointDefinition: exports.SqsEndpointDefinition,
4066
+ SqsEndpointDefinitionModel,
3778
4067
  bufferToUint8Array,
3779
4068
  applyHttpAuthHeaders,
3780
4069
  buildHttpRequestBody,
@@ -3809,6 +4098,11 @@ exports.__loadstrikeTestExports = {
3809
4098
  shouldUseConfluentKafkaClient,
3810
4099
  toHeaderRecord,
3811
4100
  toKafkaHeadersWithContentType,
4101
+ toSqsMessageAttributes,
4102
+ fromSqsMessageAttributes,
4103
+ setSqsClientFactoryForTests: (factory) => {
4104
+ sqsClientFactoryForTests = factory;
4105
+ },
3812
4106
  validateHttpEndpoint,
3813
4107
  validateTrackingSelectorPath
3814
4108
  };