@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.
@@ -41,6 +41,33 @@ export const LoadStrikeOperationType = {
41
41
  Complete: "Complete",
42
42
  Error: "Error"
43
43
  };
44
+ export const CrossPlatformTrackingConfiguration = {
45
+ forDuration(configuration, durationSeconds, cancellationSignal) {
46
+ if (!configuration || typeof configuration !== "object" || Array.isArray(configuration)) {
47
+ throw new TypeError("Tracking configuration must be provided.");
48
+ }
49
+ if (normalizeTrackingRunMode(pickTrackingString(configuration, "RunMode", "runMode", "GenerateAndCorrelate")) !== "correlateexistingtraffic") {
50
+ throw new Error("ForDuration can only be used with CorrelateExistingTraffic.");
51
+ }
52
+ if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
53
+ throw new RangeError("ForDuration duration must be greater than zero.");
54
+ }
55
+ const result = {
56
+ ...configuration,
57
+ ObservationDurationSeconds: durationSeconds
58
+ };
59
+ if (cancellationSignal !== undefined) {
60
+ if (!(cancellationSignal instanceof AbortSignal)) {
61
+ throw new TypeError("ForDuration cancellation signal must be an AbortSignal.");
62
+ }
63
+ result.ObservationCancellationSignal = cancellationSignal;
64
+ }
65
+ return result;
66
+ },
67
+ ForDuration(configuration, durationSeconds, cancellationSignal) {
68
+ return this.forDuration(configuration, durationSeconds, cancellationSignal);
69
+ }
70
+ };
44
71
  export class LoadStrikePluginDataTable {
45
72
  /**
46
73
  * Exposes the public constructor operation.
@@ -1712,6 +1739,9 @@ export class LoadStrikeScenario {
1712
1739
  ? mapRuntimeTrackingEndpointSpec(destinationSpec, useLoadStrikeTraceIdHeader)
1713
1740
  : null;
1714
1741
  validateRuntimeTrackingConfiguration(copied, sourceEndpoint, destinationEndpoint);
1742
+ if (isCorrelateExistingTrafficTracking(copied) && this.loadSimulations.length > 0) {
1743
+ throw new Error("CorrelateExistingTraffic uses ForDuration and cannot be combined with WithLoadSimulations.");
1744
+ }
1715
1745
  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);
1716
1746
  }
1717
1747
  /**
@@ -1722,6 +1752,9 @@ export class LoadStrikeScenario {
1722
1752
  if (!simulations.length) {
1723
1753
  throw new Error("At least one load simulation should be provided.");
1724
1754
  }
1755
+ if (isCorrelateExistingTrafficTracking(this.trackingConfiguration)) {
1756
+ throw new Error("CorrelateExistingTraffic uses ForDuration and cannot be combined with WithLoadSimulations.");
1757
+ }
1725
1758
  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);
1726
1759
  }
1727
1760
  /**
@@ -5721,6 +5754,14 @@ class ManagedScenarioTrackingRuntime {
5721
5754
  this.sourceOnlyMode = this.destinationEndpoint == null;
5722
5755
  this.executeOriginalScenarioRun = pickTrackingBoolean(tracking, "ExecuteOriginalScenarioRun", "executeOriginalScenarioRun", false);
5723
5756
  this.correlationTimeoutMs = Math.trunc(pickTrackingNumber(tracking, ["CorrelationTimeoutMs", "correlationTimeoutMs"], pickTrackingNumber(tracking, ["CorrelationTimeoutSeconds", "correlationTimeoutSeconds", "CorrelationTimeout"], 30) * 1000));
5757
+ this.observationDurationMs = Math.trunc(readObservationDurationMs(tracking));
5758
+ const observationCancellationSignal = pickTrackingValue(tracking, "ObservationCancellationSignal", "observationCancellationSignal");
5759
+ if (observationCancellationSignal !== undefined) {
5760
+ if (!(observationCancellationSignal instanceof AbortSignal)) {
5761
+ throw new TypeError("ObservationCancellationSignal must be an AbortSignal.");
5762
+ }
5763
+ this.observationCancellationSignal = observationCancellationSignal;
5764
+ }
5724
5765
  this.timeoutSweepIntervalMs = Math.trunc(pickTrackingNumber(tracking, ["TimeoutSweepIntervalMs", "timeoutSweepIntervalMs"], pickTrackingNumber(tracking, ["TimeoutSweepIntervalSeconds", "timeoutSweepIntervalSeconds"], 1) * 1000));
5725
5766
  this.timeoutBatchSize = Math.trunc(pickTrackingNumber(tracking, ["TimeoutBatchSize", "timeoutBatchSize"], 200));
5726
5767
  this.timeoutCountsAsFailure = pickTrackingBoolean(tracking, "TimeoutCountsAsFailure", "timeoutCountsAsFailure", true);
@@ -5861,8 +5902,7 @@ class ManagedScenarioTrackingRuntime {
5861
5902
  return this.executeSourceOnly();
5862
5903
  }
5863
5904
  if (this.runMode === "correlateexistingtraffic") {
5864
- const outcome = await this.waitForOutcome(context.scenarioCancellationToken);
5865
- return this.toResponse(outcome);
5905
+ return await this.observeExistingTraffic(context.scenarioCancellationToken);
5866
5906
  }
5867
5907
  let producedPayload;
5868
5908
  try {
@@ -5938,6 +5978,45 @@ class ManagedScenarioTrackingRuntime {
5938
5978
  });
5939
5979
  return LoadStrikeResponse.ok("source_success");
5940
5980
  }
5981
+ async observeExistingTraffic(signal) {
5982
+ const deadline = Date.now() + this.observationDurationMs;
5983
+ const observationSignal = this.observationCancellationSignal
5984
+ ? combineAbortSignals(signal, this.observationCancellationSignal)
5985
+ : signal;
5986
+ let failureSeen = false;
5987
+ while (Date.now() < deadline && !observationSignal.aborted) {
5988
+ let outcome;
5989
+ try {
5990
+ outcome = await this.waitForOutcomeUntil(deadline, observationSignal);
5991
+ }
5992
+ catch (error) {
5993
+ if (this.observationCancellationSignal?.aborted && !signal.aborted) {
5994
+ break;
5995
+ }
5996
+ throw error;
5997
+ }
5998
+ if (!outcome) {
5999
+ break;
6000
+ }
6001
+ failureSeen = failureSeen || this.isFailedObservationOutcome(outcome);
6002
+ }
6003
+ this.shutdown = true;
6004
+ await Promise.all([
6005
+ this.sourceLoop,
6006
+ this.destinationLoop,
6007
+ this.timeoutLoop
6008
+ ].filter(Boolean).map((value) => value.catch(() => { })));
6009
+ await this.correlationRuntime.sweepTimeouts(Date.now() + this.correlationTimeoutMs + 1, this.timeoutBatchSize);
6010
+ while (this.outcomeQueue.length > 0) {
6011
+ const outcome = this.outcomeQueue.shift();
6012
+ if (outcome) {
6013
+ failureSeen = failureSeen || this.isFailedObservationOutcome(outcome);
6014
+ }
6015
+ }
6016
+ return failureSeen
6017
+ ? LoadStrikeResponse.fail("tracking_failures", "One or more observed source or destination events did not correlate successfully.", 0)
6018
+ : LoadStrikeResponse.ok("observed");
6019
+ }
5941
6020
  async consumeSourceLoop() {
5942
6021
  while (!this.shutdown) {
5943
6022
  try {
@@ -6151,6 +6230,34 @@ class ManagedScenarioTrackingRuntime {
6151
6230
  }
6152
6231
  }
6153
6232
  }
6233
+ async waitForOutcomeUntil(deadlineMs, signal) {
6234
+ const existing = this.outcomeQueue.shift();
6235
+ if (existing) {
6236
+ return existing;
6237
+ }
6238
+ const remainingMs = deadlineMs - Date.now();
6239
+ if (remainingMs <= 0) {
6240
+ return null;
6241
+ }
6242
+ const waiter = createManagedTrackingWaiter();
6243
+ this.outcomeWaiters.push(waiter);
6244
+ const timeout = sleep(remainingMs).then(() => null);
6245
+ try {
6246
+ return await Promise.race([waiter.promise, timeout, waitForAbort(signal)]);
6247
+ }
6248
+ finally {
6249
+ const index = this.outcomeWaiters.indexOf(waiter);
6250
+ if (index >= 0) {
6251
+ this.outcomeWaiters.splice(index, 1);
6252
+ }
6253
+ }
6254
+ }
6255
+ isFailedObservationOutcome(outcome) {
6256
+ if (outcome.status === "timeout") {
6257
+ return this.timeoutCountsAsFailure;
6258
+ }
6259
+ return outcome.status !== "matched";
6260
+ }
6154
6261
  rejectOutstandingWaiters() {
6155
6262
  for (const rows of this.waiters.values()) {
6156
6263
  for (const waiter of rows) {
@@ -6270,8 +6377,12 @@ function validateRuntimeTrackingConfiguration(tracking, sourceEndpoint, destinat
6270
6377
  if (!metricPrefix.trim()) {
6271
6378
  throw new Error("MetricPrefix must be provided.");
6272
6379
  }
6380
+ const observationDurationMs = readObservationDurationMs(tracking);
6273
6381
  validateRuntimeRedisCorrelationStoreConfiguration(tracking);
6274
6382
  if (runMode === "generateandcorrelate") {
6383
+ if (observationDurationMs > 0) {
6384
+ throw new Error("ForDuration is only supported for CorrelateExistingTraffic.");
6385
+ }
6275
6386
  if (sourceEndpoint.mode !== "Produce") {
6276
6387
  throw new Error("Source endpoint mode must be Produce for GenerateAndCorrelate mode.");
6277
6388
  }
@@ -6281,6 +6392,9 @@ function validateRuntimeTrackingConfiguration(tracking, sourceEndpoint, destinat
6281
6392
  return;
6282
6393
  }
6283
6394
  if (runMode === "correlateexistingtraffic") {
6395
+ if (observationDurationMs <= 0) {
6396
+ throw new Error("CorrelateExistingTraffic requires ForDuration with a duration greater than zero.");
6397
+ }
6284
6398
  if (!destinationEndpoint) {
6285
6399
  throw new Error("Destination endpoint must be provided for CorrelateExistingTraffic mode.");
6286
6400
  }
@@ -6294,6 +6408,15 @@ function validateRuntimeTrackingConfiguration(tracking, sourceEndpoint, destinat
6294
6408
  }
6295
6409
  throw new RangeError(`Unsupported tracking run mode: ${runMode}.`);
6296
6410
  }
6411
+ function readObservationDurationMs(tracking) {
6412
+ return pickTrackingNumber(tracking, ["ObservationDurationMs", "observationDurationMs"], pickTrackingNumber(tracking, ["ObservationDurationSeconds", "observationDurationSeconds", "ObservationDuration"], 0) * 1000);
6413
+ }
6414
+ function isCorrelateExistingTrafficTracking(configuration) {
6415
+ if (!configuration) {
6416
+ return false;
6417
+ }
6418
+ return normalizeTrackingRunMode(pickTrackingString(configuration, "RunMode", "runMode", "GenerateAndCorrelate")) === "correlateexistingtraffic";
6419
+ }
6297
6420
  function readUseLoadStrikeTraceIdHeader(tracking) {
6298
6421
  return pickTrackingBoolean(tracking, "UseLoadStrikeTraceIdHeader", "useLoadStrikeTraceIdHeader", false);
6299
6422
  }
@@ -6483,6 +6606,7 @@ function mapRuntimeTrackingEndpointSpec(spec, useLoadStrikeTraceIdHeader = false
6483
6606
  nats: asTrackingRecord(pickTrackingValue(spec, "Nats", "nats")),
6484
6607
  redisStreams: asTrackingRecord(pickTrackingValue(spec, "RedisStreams", "redisStreams")),
6485
6608
  azureEventHubs: asTrackingRecord(pickTrackingValue(spec, "AzureEventHubs", "azureEventHubs")),
6609
+ sqs: asTrackingRecord(pickTrackingValue(spec, "Sqs", "sqs")),
6486
6610
  pushDiffusion: asTrackingRecord(pickTrackingValue(spec, "PushDiffusion", "pushDiffusion")),
6487
6611
  delegate: typeof delegateProduce === "function"
6488
6612
  || typeof delegateConsume === "function"
@@ -218,6 +218,35 @@ class AzureEventHubsEndpointDefinitionModel extends TrafficEndpointDefinitionMod
218
218
  }
219
219
  }
220
220
  }
221
+ class SqsEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
222
+ constructor(initial) {
223
+ super(initial);
224
+ this.Kind = "Sqs";
225
+ this.QueueUrl = "";
226
+ this.Region = "";
227
+ this.WaitTimeSeconds = 1;
228
+ this.MaxNumberOfMessages = 1;
229
+ this.DeleteAfterConsume = true;
230
+ initializeSqsEndpointDefinitionModel(this, initial);
231
+ }
232
+ Validate() {
233
+ super.Validate();
234
+ requireNonEmptyString(this.QueueUrl, "QueueUrl must be provided for AWS SQS endpoint.");
235
+ requireNonEmptyString(this.Region, "Region must be provided for AWS SQS endpoint.");
236
+ if (String(this.AccessKeyId ?? "").trim() && !String(this.SecretAccessKey ?? "").trim()) {
237
+ throw new Error("SecretAccessKey must be provided when AccessKeyId is configured for AWS SQS endpoint.");
238
+ }
239
+ if (this.WaitTimeSeconds < 0 || this.WaitTimeSeconds > 20) {
240
+ throw new RangeError("WaitTimeSeconds must be between 0 and 20 for AWS SQS endpoint.");
241
+ }
242
+ if (this.MaxNumberOfMessages <= 0 || this.MaxNumberOfMessages > 10) {
243
+ throw new RangeError("MaxNumberOfMessages must be between 1 and 10 for AWS SQS endpoint.");
244
+ }
245
+ if (this.VisibilityTimeoutSeconds != null && this.VisibilityTimeoutSeconds < 0) {
246
+ throw new RangeError("VisibilityTimeoutSeconds must be zero or greater when configured.");
247
+ }
248
+ }
249
+ }
221
250
  class DelegateStreamEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
222
251
  constructor(initial) {
223
252
  super(initial);
@@ -263,6 +292,7 @@ export const RabbitMqEndpointDefinition = RabbitMqEndpointDefinitionModel;
263
292
  export const NatsEndpointDefinition = NatsEndpointDefinitionModel;
264
293
  export const RedisStreamsEndpointDefinition = RedisStreamsEndpointDefinitionModel;
265
294
  export const AzureEventHubsEndpointDefinition = AzureEventHubsEndpointDefinitionModel;
295
+ export const SqsEndpointDefinition = SqsEndpointDefinitionModel;
266
296
  export const DelegateStreamEndpointDefinition = DelegateStreamEndpointDefinitionModel;
267
297
  export const PushDiffusionEndpointDefinition = PushDiffusionEndpointDefinitionModel;
268
298
  export const HttpOAuth2ClientCredentialsOptions = HttpOAuth2ClientCredentialsOptionsModel;
@@ -626,6 +656,44 @@ function initializeAzureEventHubsEndpointDefinitionModel(target, initial) {
626
656
  target.PartitionCount = pickOptionalEndpointNumber(raw, "PartitionCount", "partitionCount");
627
657
  }
628
658
  }
659
+ function initializeSqsEndpointDefinitionModel(target, initial) {
660
+ const raw = asRecordOrEmpty(initial);
661
+ const queueUrl = pickOptionalEndpointString(raw, "QueueUrl", "queueUrl");
662
+ if (queueUrl) {
663
+ target.QueueUrl = queueUrl;
664
+ }
665
+ const region = pickOptionalEndpointString(raw, "Region", "region");
666
+ if (region) {
667
+ target.Region = region;
668
+ }
669
+ if (hasAnyEndpointField(raw, ["ServiceUrl", "serviceUrl"])) {
670
+ target.ServiceUrl = pickOptionalEndpointString(raw, "ServiceUrl", "serviceUrl");
671
+ }
672
+ if (hasAnyEndpointField(raw, ["AccessKeyId", "accessKeyId"])) {
673
+ target.AccessKeyId = pickOptionalEndpointString(raw, "AccessKeyId", "accessKeyId");
674
+ }
675
+ if (hasAnyEndpointField(raw, ["SecretAccessKey", "secretAccessKey"])) {
676
+ target.SecretAccessKey = pickOptionalEndpointStringAllowEmpty(raw, "SecretAccessKey", "secretAccessKey");
677
+ }
678
+ if (hasAnyEndpointField(raw, ["SessionToken", "sessionToken"])) {
679
+ target.SessionToken = pickOptionalEndpointString(raw, "SessionToken", "sessionToken");
680
+ }
681
+ const waitTimeSeconds = pickOptionalEndpointNumber(raw, "WaitTimeSeconds", "waitTimeSeconds");
682
+ if (waitTimeSeconds != null) {
683
+ target.WaitTimeSeconds = waitTimeSeconds;
684
+ }
685
+ const maxNumberOfMessages = pickOptionalEndpointNumber(raw, "MaxNumberOfMessages", "maxNumberOfMessages");
686
+ if (maxNumberOfMessages != null) {
687
+ target.MaxNumberOfMessages = maxNumberOfMessages;
688
+ }
689
+ if (hasAnyEndpointField(raw, ["VisibilityTimeoutSeconds", "visibilityTimeoutSeconds"])) {
690
+ target.VisibilityTimeoutSeconds = pickOptionalEndpointNumber(raw, "VisibilityTimeoutSeconds", "visibilityTimeoutSeconds");
691
+ }
692
+ const deleteAfterConsume = pickEndpointBoolean(raw, "DeleteAfterConsume", "deleteAfterConsume");
693
+ if (deleteAfterConsume != null) {
694
+ target.DeleteAfterConsume = deleteAfterConsume;
695
+ }
696
+ }
629
697
  function initializeDelegateStreamEndpointDefinitionModel(target, initial) {
630
698
  const raw = asRecordOrEmpty(initial);
631
699
  const produce = pickEndpointFunction(raw, "Produce", "produce");
@@ -762,6 +830,7 @@ const protocolBus = new class InMemoryProtocolBus {
762
830
  this.redisCounters = new Map();
763
831
  this.eventHubs = new Map();
764
832
  this.eventHubOffsets = new Map();
833
+ this.sqsQueues = new Map();
765
834
  this.pushTopics = new Map();
766
835
  this.pushOffsets = new Map();
767
836
  }
@@ -776,6 +845,7 @@ const protocolBus = new class InMemoryProtocolBus {
776
845
  this.redisCounters.clear();
777
846
  this.eventHubs.clear();
778
847
  this.eventHubOffsets.clear();
848
+ this.sqsQueues.clear();
779
849
  this.pushTopics.clear();
780
850
  this.pushOffsets.clear();
781
851
  }
@@ -940,6 +1010,25 @@ const protocolBus = new class InMemoryProtocolBus {
940
1010
  this.eventHubOffsets.set(cursorKey, rows.length);
941
1011
  return null;
942
1012
  }
1013
+ produceSqs(endpoint, payload) {
1014
+ const options = endpoint.sqs ?? {};
1015
+ const queueUrl = optionString(options, "QueueUrl", "queueUrl") || endpoint.name;
1016
+ const queue = this.sqsQueues.get(queueUrl) ?? [];
1017
+ queue.push({ payload: clonePayload(payload), timestampMs: Date.now() });
1018
+ this.sqsQueues.set(queueUrl, queue);
1019
+ return clonePayload(payload);
1020
+ }
1021
+ consumeSqs(endpoint) {
1022
+ const options = endpoint.sqs ?? {};
1023
+ const queueUrl = optionString(options, "QueueUrl", "queueUrl") || endpoint.name;
1024
+ const queue = this.sqsQueues.get(queueUrl) ?? [];
1025
+ if (!queue.length) {
1026
+ return null;
1027
+ }
1028
+ const next = queue.shift() ?? null;
1029
+ this.sqsQueues.set(queueUrl, queue);
1030
+ return next ? clonePayload(next.payload) : null;
1031
+ }
943
1032
  producePush(endpoint, payload) {
944
1033
  const options = endpoint.pushDiffusion ?? {};
945
1034
  const topic = optionString(options, "TopicPath", "topicPath")
@@ -1681,6 +1770,104 @@ class AzureEventHubsEndpointAdapter extends CallbackAdapter {
1681
1770
  await this.subscriptionPromise;
1682
1771
  }
1683
1772
  }
1773
+ let sqsClientFactoryForTests = null;
1774
+ class SqsEndpointAdapter extends CallbackAdapter {
1775
+ constructor() {
1776
+ super(...arguments);
1777
+ this.clientPromise = null;
1778
+ }
1779
+ async produce(payload) {
1780
+ if (resolveProduceDelegate(this.endpoint)) {
1781
+ return super.produce(payload);
1782
+ }
1783
+ if (this.endpoint.mode !== "Produce") {
1784
+ return null;
1785
+ }
1786
+ const client = await this.getClient();
1787
+ const options = this.endpoint.sqs ?? {};
1788
+ const resolved = prepareProducedPayload(this.endpoint, payload);
1789
+ const wire = toWirePayload(resolved, this.endpoint);
1790
+ const { SendMessageCommand } = await import("@aws-sdk/client-sqs");
1791
+ await client.send(new SendMessageCommand({
1792
+ QueueUrl: optionString(options, "QueueUrl", "queueUrl"),
1793
+ MessageBody: payloadBodyAsUtf8(wire.body),
1794
+ MessageAttributes: toSqsMessageAttributes(wire.headers, wire.contentType)
1795
+ }));
1796
+ return clonePayload(resolved);
1797
+ }
1798
+ async consume() {
1799
+ if (resolveConsumeDelegate(this.endpoint)) {
1800
+ return super.consume();
1801
+ }
1802
+ if (this.endpoint.mode !== "Consume") {
1803
+ return null;
1804
+ }
1805
+ const client = await this.getClient();
1806
+ const options = this.endpoint.sqs ?? {};
1807
+ const { ReceiveMessageCommand, DeleteMessageCommand } = await import("@aws-sdk/client-sqs");
1808
+ const input = {
1809
+ QueueUrl: optionString(options, "QueueUrl", "queueUrl"),
1810
+ MaxNumberOfMessages: hasOptionValue(options, "MaxNumberOfMessages", "maxNumberOfMessages")
1811
+ ? optionNumber(options, "MaxNumberOfMessages", "maxNumberOfMessages")
1812
+ : 1,
1813
+ WaitTimeSeconds: hasOptionValue(options, "WaitTimeSeconds", "waitTimeSeconds")
1814
+ ? optionNumber(options, "WaitTimeSeconds", "waitTimeSeconds")
1815
+ : 1,
1816
+ MessageAttributeNames: ["All"]
1817
+ };
1818
+ const visibilityTimeout = optionNumber(options, "VisibilityTimeoutSeconds", "visibilityTimeoutSeconds");
1819
+ if (visibilityTimeout > 0) {
1820
+ input.VisibilityTimeout = visibilityTimeout;
1821
+ }
1822
+ const response = await client.send(new ReceiveMessageCommand(input));
1823
+ const message = response.Messages?.[0];
1824
+ if (!message) {
1825
+ return null;
1826
+ }
1827
+ const { headers, contentType } = fromSqsMessageAttributes(message.MessageAttributes);
1828
+ const payload = createBrokerPayload(headers, Buffer.from(message.Body ?? "", "utf8"), this.endpoint, contentType);
1829
+ if (optionBoolean(options, true, "DeleteAfterConsume", "deleteAfterConsume") && message.ReceiptHandle) {
1830
+ await client.send(new DeleteMessageCommand({
1831
+ QueueUrl: optionString(options, "QueueUrl", "queueUrl"),
1832
+ ReceiptHandle: message.ReceiptHandle
1833
+ }));
1834
+ }
1835
+ return payload;
1836
+ }
1837
+ async dispose() {
1838
+ const client = await this.clientPromise?.catch(() => null);
1839
+ client?.destroy?.();
1840
+ }
1841
+ async getClient() {
1842
+ if (!this.clientPromise) {
1843
+ this.clientPromise = (async () => {
1844
+ if (sqsClientFactoryForTests) {
1845
+ return await sqsClientFactoryForTests(this.endpoint);
1846
+ }
1847
+ const { SQSClient } = await import("@aws-sdk/client-sqs");
1848
+ const options = this.endpoint.sqs ?? {};
1849
+ const accessKeyId = optionString(options, "AccessKeyId", "accessKeyId");
1850
+ const secretAccessKey = optionString(options, "SecretAccessKey", "secretAccessKey");
1851
+ const config = {
1852
+ region: optionString(options, "Region", "region")
1853
+ };
1854
+ const serviceUrl = optionString(options, "ServiceUrl", "serviceUrl");
1855
+ if (serviceUrl) {
1856
+ config.endpoint = serviceUrl;
1857
+ }
1858
+ if (accessKeyId) {
1859
+ config.credentials = {
1860
+ accessKeyId,
1861
+ secretAccessKey,
1862
+ sessionToken: optionString(options, "SessionToken", "sessionToken") || undefined
1863
+ };
1864
+ }
1865
+ return new SQSClient(config);
1866
+ })();
1867
+ }
1868
+ return this.clientPromise;
1869
+ }
1870
+ }
1684
1871
  class PushDiffusionEndpointAdapter extends CallbackAdapter {
1685
1872
  async produce(payload) {
1686
1873
  return super.produce(payload);
@@ -1711,6 +1898,8 @@ export class EndpointAdapterFactory {
1711
1898
  return new RedisStreamsEndpointAdapter(normalized);
1712
1899
  case "AzureEventHubs":
1713
1900
  return new AzureEventHubsEndpointAdapter(normalized);
1901
+ case "Sqs":
1902
+ return new SqsEndpointAdapter(normalized);
1714
1903
  case "PushDiffusion":
1715
1904
  return new PushDiffusionEndpointAdapter(normalized);
1716
1905
  case "DelegateStream":
@@ -1838,6 +2027,28 @@ const AZURE_EVENT_HUBS_ENDPOINT_FLAT_KEYS = [
1838
2027
  "StartFromEarliest",
1839
2028
  "startFromEarliest"
1840
2029
  ];
2030
+ const SQS_ENDPOINT_FLAT_KEYS = [
2031
+ "QueueUrl",
2032
+ "queueUrl",
2033
+ "Region",
2034
+ "region",
2035
+ "ServiceUrl",
2036
+ "serviceUrl",
2037
+ "AccessKeyId",
2038
+ "accessKeyId",
2039
+ "SecretAccessKey",
2040
+ "secretAccessKey",
2041
+ "SessionToken",
2042
+ "sessionToken",
2043
+ "WaitTimeSeconds",
2044
+ "waitTimeSeconds",
2045
+ "MaxNumberOfMessages",
2046
+ "maxNumberOfMessages",
2047
+ "VisibilityTimeoutSeconds",
2048
+ "visibilityTimeoutSeconds",
2049
+ "DeleteAfterConsume",
2050
+ "deleteAfterConsume"
2051
+ ];
1841
2052
  const PUSH_DIFFUSION_ENDPOINT_FLAT_KEYS = [
1842
2053
  "ServerUrl",
1843
2054
  "serverUrl",
@@ -1898,6 +2109,7 @@ function normalizeEndpointDefinition(endpoint) {
1898
2109
  nats: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "Nats", ["nats", "Nats"], NATS_ENDPOINT_FLAT_KEYS)),
1899
2110
  redisStreams: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "RedisStreams", ["redisStreams", "RedisStreams"], REDIS_STREAMS_ENDPOINT_FLAT_KEYS)),
1900
2111
  azureEventHubs: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "AzureEventHubs", ["azureEventHubs", "AzureEventHubs"], AZURE_EVENT_HUBS_ENDPOINT_FLAT_KEYS)),
2112
+ sqs: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "Sqs", ["sqs", "Sqs"], SQS_ENDPOINT_FLAT_KEYS)),
1901
2113
  pushDiffusion: normalizeProtocolOptions(pickEndpointTransportRecord(raw, kind, "PushDiffusion", ["pushDiffusion", "PushDiffusion"], PUSH_DIFFUSION_ENDPOINT_FLAT_KEYS)),
1902
2114
  delegate: normalizeDelegateEndpointOptions(pickEndpointTransportRecord(raw, kind, "DelegateStream", ["delegate", "Delegate", "DelegateStream"], DELEGATE_STREAM_ENDPOINT_FLAT_KEYS))
1903
2115
  };
@@ -1955,6 +2167,22 @@ function resolveEndpointKind(raw) {
1955
2167
  ])) {
1956
2168
  return "AzureEventHubs";
1957
2169
  }
2170
+ if (Object.keys(pickEndpointRecord(raw, "sqs", "Sqs")).length || hasAnyEndpointField(raw, [
2171
+ "QueueUrl",
2172
+ "queueUrl",
2173
+ "ServiceUrl",
2174
+ "serviceUrl",
2175
+ "WaitTimeSeconds",
2176
+ "waitTimeSeconds",
2177
+ "MaxNumberOfMessages",
2178
+ "maxNumberOfMessages",
2179
+ "VisibilityTimeoutSeconds",
2180
+ "visibilityTimeoutSeconds",
2181
+ "DeleteAfterConsume",
2182
+ "deleteAfterConsume"
2183
+ ])) {
2184
+ return "Sqs";
2185
+ }
1958
2186
  if (Object.keys(pickEndpointRecord(raw, "pushDiffusion", "PushDiffusion")).length || hasAnyEndpointField(raw, [
1959
2187
  "TopicPath",
1960
2188
  "topicPath",
@@ -2236,6 +2464,9 @@ function validateEndpointDefinition(endpoint) {
2236
2464
  case "AzureEventHubs":
2237
2465
  validateAzureEventHubsEndpoint(endpoint, mode, hasModeDelegate);
2238
2466
  return;
2467
+ case "Sqs":
2468
+ validateSqsEndpoint(endpoint, mode, hasModeDelegate);
2469
+ return;
2239
2470
  case "PushDiffusion":
2240
2471
  validatePushDiffusionEndpoint(endpoint, mode);
2241
2472
  return;
@@ -2457,6 +2688,32 @@ function validateAzureEventHubsEndpoint(endpoint, mode, hasModeDelegate) {
2457
2688
  throw new RangeError("PartitionCount must be zero or greater when configured.");
2458
2689
  }
2459
2690
  }
2691
+ function validateSqsEndpoint(endpoint, mode, hasModeDelegate) {
2692
+ const options = endpoint.sqs;
2693
+ if ((!options || typeof options !== "object") && !hasModeDelegate) {
2694
+ throw new Error(`Sqs endpoint in ${mode} mode requires sqs options or delegate callback.`);
2695
+ }
2696
+ if (!options || typeof options !== "object") {
2697
+ return;
2698
+ }
2699
+ requireNonEmptyString(optionString(options, "QueueUrl", "queueUrl"), "QueueUrl must be provided for AWS SQS endpoint.");
2700
+ requireNonEmptyString(optionString(options, "Region", "region"), "Region must be provided for AWS SQS endpoint.");
2701
+ if (optionString(options, "AccessKeyId", "accessKeyId") && !optionString(options, "SecretAccessKey", "secretAccessKey")) {
2702
+ throw new Error("SecretAccessKey must be provided when AccessKeyId is configured for AWS SQS endpoint.");
2703
+ }
2704
+ const waitTimeSeconds = optionNumber(options, "WaitTimeSeconds", "waitTimeSeconds");
2705
+ if (waitTimeSeconds < 0 || waitTimeSeconds > 20) {
2706
+ throw new RangeError("WaitTimeSeconds must be between 0 and 20 for AWS SQS endpoint.");
2707
+ }
2708
+ const maxNumberOfMessages = optionNumber(options, "MaxNumberOfMessages", "maxNumberOfMessages") || 1;
2709
+ if (maxNumberOfMessages <= 0 || maxNumberOfMessages > 10) {
2710
+ throw new RangeError("MaxNumberOfMessages must be between 1 and 10 for AWS SQS endpoint.");
2711
+ }
2712
+ const visibilityTimeoutSeconds = optionNumber(options, "VisibilityTimeoutSeconds", "visibilityTimeoutSeconds");
2713
+ if (visibilityTimeoutSeconds < 0) {
2714
+ throw new RangeError("VisibilityTimeoutSeconds must be zero or greater when configured.");
2715
+ }
2716
+ }
2460
2717
  function validatePushDiffusionEndpoint(endpoint, mode) {
2461
2718
  const options = endpoint.pushDiffusion;
2462
2719
  if (!options || typeof options !== "object") {
@@ -2821,6 +3078,12 @@ function optionNumber(options, ...keys) {
2821
3078
  }
2822
3079
  return 0;
2823
3080
  }
3081
+ function hasOptionValue(options, ...keys) {
3082
+ return keys.some((key) => {
3083
+ const value = options[key];
3084
+ return value !== undefined && value !== null && (!(typeof value === "string") || value.trim() !== "");
3085
+ });
3086
+ }
2824
3087
  function optionBoolean(options, fallback, ...keys) {
2825
3088
  for (const key of keys) {
2826
3089
  const value = options[key];
@@ -3704,6 +3967,29 @@ function createRedisStreamPayload(fields, endpoint) {
3704
3967
  }
3705
3968
  return createBrokerPayload(headers, body, endpoint, contentType);
3706
3969
  }
3970
+ function toSqsMessageAttributes(headers, contentType) {
3971
+ const attributes = {};
3972
+ for (const [key, value] of Object.entries(headers)) {
3973
+ attributes[key] = { DataType: "String", StringValue: String(value ?? "") };
3974
+ }
3975
+ if (contentType) {
3976
+ attributes["content-type"] = { DataType: "String", StringValue: contentType };
3977
+ }
3978
+ return attributes;
3979
+ }
3980
+ function fromSqsMessageAttributes(attributes) {
3981
+ const headers = {};
3982
+ let contentType;
3983
+ for (const [key, value] of Object.entries(attributes ?? {})) {
3984
+ const text = String(value?.StringValue ?? "");
3985
+ if (key.toLowerCase() === "content-type") {
3986
+ contentType = text;
3987
+ continue;
3988
+ }
3989
+ headers[key] = text;
3990
+ }
3991
+ return { headers, contentType };
3992
+ }
3707
3993
  function payloadBodyAsUtf8(body) {
3708
3994
  if (body instanceof Uint8Array) {
3709
3995
  return new TextDecoder().decode(body);
@@ -3738,6 +4024,9 @@ export const __loadstrikeTestExports = {
3738
4024
  RedisStreamsEndpointAdapter,
3739
4025
  RedisStreamsEndpointDefinition,
3740
4026
  RedisStreamsEndpointDefinitionModel,
4027
+ SqsEndpointAdapter,
4028
+ SqsEndpointDefinition,
4029
+ SqsEndpointDefinitionModel,
3741
4030
  bufferToUint8Array,
3742
4031
  applyHttpAuthHeaders,
3743
4032
  buildHttpRequestBody,
@@ -3772,6 +4061,11 @@ export const __loadstrikeTestExports = {
3772
4061
  shouldUseConfluentKafkaClient,
3773
4062
  toHeaderRecord,
3774
4063
  toKafkaHeadersWithContentType,
4064
+ toSqsMessageAttributes,
4065
+ fromSqsMessageAttributes,
4066
+ setSqsClientFactoryForTests: (factory) => {
4067
+ sqsClientFactoryForTests = factory;
4068
+ },
3775
4069
  validateHttpEndpoint,
3776
4070
  validateTrackingSelectorPath
3777
4071
  };
@@ -13,6 +13,8 @@ export interface LoadStrikeAutopilotOptions {
13
13
  AllowedReplayHosts?: string[];
14
14
  BaseUrlRewrite?: string;
15
15
  TrackingSelector?: string;
16
+ RunnerKey?: string;
17
+ LicenseValidationTimeoutSeconds?: number;
16
18
  SecretBindings?: LoadStrikeAutopilotSecretBinding[];
17
19
  EndpointBindings?: LoadStrikeAutopilotEndpointBinding[];
18
20
  }
@@ -37,7 +37,10 @@ export declare class LoadStrikeAutopilotResult implements LoadStrikeAutopilotRes
37
37
  BuildScenario(): LoadStrikeScenario;
38
38
  }
39
39
  export declare class LoadStrikeAutopilot {
40
- static generate(request: LoadStrikeAutopilotRequest): LoadStrikeAutopilotResult;
41
- static Generate(request: LoadStrikeAutopilotRequest): LoadStrikeAutopilotResult;
40
+ static generate(request: LoadStrikeAutopilotRequest): Promise<LoadStrikeAutopilotResult>;
41
+ static Generate(request: LoadStrikeAutopilotRequest): Promise<LoadStrikeAutopilotResult>;
42
42
  }
43
+ export declare const __private: {
44
+ setAutopilotLicenseValidationBypassForTests(value: boolean): void;
45
+ };
43
46
  export {};
@@ -69,6 +69,7 @@ export interface LoadStrikeTrackingConfigurationSpec {
69
69
  Source: LoadStrikeEndpointSpec;
70
70
  Destination?: LoadStrikeEndpointSpec;
71
71
  RunMode?: string;
72
+ ObservationDurationSeconds?: number;
72
73
  CorrelationTimeoutSeconds?: number;
73
74
  TimeoutSweepIntervalSeconds?: number;
74
75
  TimeoutBatchSize?: number;
@@ -107,6 +108,7 @@ export interface LoadStrikeEndpointSpec {
107
108
  Nats?: LoadStrikeNatsEndpointOptions;
108
109
  RedisStreams?: LoadStrikeRedisStreamsEndpointOptions;
109
110
  AzureEventHubs?: LoadStrikeAzureEventHubsEndpointOptions;
111
+ Sqs?: LoadStrikeSqsEndpointOptions;
110
112
  DelegateStream?: LoadStrikeDelegateEndpointOptions;
111
113
  PushDiffusion?: LoadStrikePushDiffusionEndpointOptions;
112
114
  }
@@ -202,6 +204,18 @@ export interface LoadStrikeAzureEventHubsEndpointOptions {
202
204
  PartitionKey?: string;
203
205
  PartitionCount?: number;
204
206
  }
207
+ export interface LoadStrikeSqsEndpointOptions {
208
+ QueueUrl?: string;
209
+ Region?: string;
210
+ ServiceUrl?: string;
211
+ AccessKeyId?: string;
212
+ SecretAccessKey?: string;
213
+ SessionToken?: string;
214
+ WaitTimeSeconds?: number;
215
+ MaxNumberOfMessages?: number;
216
+ VisibilityTimeoutSeconds?: number;
217
+ DeleteAfterConsume?: boolean;
218
+ }
205
219
  export interface LoadStrikeDelegateEndpointOptions {
206
220
  ProduceCallbackUrl?: string;
207
221
  ConsumeCallbackUrl?: string;