@loadstrike/loadstrike-sdk 1.0.23601 → 1.0.26901

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.
package/README.md CHANGED
@@ -8,6 +8,7 @@ LoadStrike is a developer-first load testing SDK for Node.js applications, servi
8
8
  - Generate safe starter scenarios from captured HAR, OpenTelemetry trace JSON, browser recordings, or message pairs with Trace-to-test Autopilot.
9
9
  - Exercise HTTP and event-driven workflows across multiple steps.
10
10
  - Apply load simulations, thresholds, and custom metrics to real transactions.
11
+ - Split a single load profile across weighted scenario mixes.
11
12
  - Generate local reports and, on Business and Enterprise, publish observability data to supported sinks.
12
13
 
13
14
  Built-in transport coverage includes HTTP, Kafka, RabbitMQ, NATS, Redis Streams, Azure Event Hubs, Push Diffusion, and delegate-based custom streams. Local report output supports HTML, Markdown, TXT, and CSV, and Business and Enterprise can publish to InfluxDB, TimescaleDB, Grafana Loki, Datadog, Splunk HEC, and OpenTelemetry Collector.
@@ -53,6 +54,12 @@ const result = await LoadStrikeRunner
53
54
 
54
55
  `run()` returns the detailed run result, including generated report files, scenario statistics, metrics, and sink status.
55
56
 
57
+ ## Traffic Mixes
58
+
59
+ Use `LoadStrikeTrafficMix` on Business and Enterprise plans when one total load profile should be distributed across multiple scenario lanes. For example, a 1000 requests-per-second profile with scenario weights of 60, 30, and 10 sends roughly 600 requests per second to the first scenario, 300 to the second, and 100 to the third.
60
+
61
+ Each lane is still a normal scenario with its own named steps, thresholds, reports, and portal results. Register the mix with `LoadStrikeRunner.registerTrafficMix(...)` or add it to a runner with `.addTrafficMix(...)`.
62
+
56
63
  ## Trace-To-Test Autopilot
57
64
 
58
65
  Use `await LoadStrikeAutopilot.generate(...)` to infer a starter plan from a captured artifact. Set `Options.RunnerKey` so generation can validate the Trace-To-Test Autopilot entitlement. Check `result.Readiness` and `result.ReadinessFailures` first; call `result.buildScenario()` only when it is `LoadStrikeAutopilotReadiness.Ready`, then execute the scenario through the normal runner with a valid `RunnerKey`.
package/dist/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InfluxDbReportingSink = exports.GrafanaLokiReportingSinkOptions = exports.GrafanaLokiReportingSink = exports.DatadogReportingSinkOptions = exports.DatadogReportingSink = exports.HttpAuthOptions = exports.HttpOAuth2ClientCredentialsOptions = exports.PushDiffusionEndpointDefinition = exports.DelegateStreamEndpointDefinition = exports.SqsEndpointDefinition = exports.AzureEventHubsEndpointDefinition = exports.RedisStreamsEndpointDefinition = exports.NatsEndpointDefinition = exports.RabbitMqEndpointDefinition = exports.KafkaSaslOptions = exports.KafkaEndpointDefinition = exports.HttpEndpointDefinition = exports.TrafficEndpointDefinition = exports.LOADSTRIKE_TRACE_ID_TRACKING_FIELD = exports.LOADSTRIKE_TRACE_ID_HEADER = exports.TrackingFieldSelector = exports.TrackingPayloadBuilder = exports.RedisCorrelationStore = exports.RedisCorrelationStoreOptions = exports.InMemoryCorrelationStore = exports.CrossPlatformTrackingRuntime = exports.CorrelationStoreConfiguration = exports.LoadStrikeThreshold = exports.LoadStrikeStep = exports.LoadStrikeGauge = exports.LoadStrikeCounter = exports.LoadStrikeMetric = exports.CrossPlatformTrackingConfiguration = exports.LoadStrikeSimulation = exports.LoadStrikeScenario = exports.LoadStrikeRunner = exports.LoadStrikeOperationType = exports.LoadStrikeScenarioOperation = exports.LoadStrikeLogLevel = exports.LoadStrikeResponse = exports.LoadStrikeReportFormat = exports.LoadStrikeNodeType = exports.LoadStrikePluginDataTable = exports.LoadStrikePluginData = exports.LoadStrikeContext = exports.ScenarioTrackingExtensions = exports.CrossPlatformScenarioConfigurator = exports.LoadStrikeAutopilotReadiness = exports.LoadStrikeAutopilotResult = exports.LoadStrikeAutopilot = void 0;
4
- exports.TimescaleDbReportingSinkOptions = exports.TimescaleDbReportingSink = exports.SplunkReportingSinkOptions = exports.SplunkReportingSink = exports.PortalReportingSink = exports.OtelCollectorReportingSinkOptions = exports.OtelCollectorReportingSink = exports.InfluxDbReportingSinkOptions = void 0;
3
+ exports.GrafanaLokiReportingSink = exports.DatadogReportingSinkOptions = exports.DatadogReportingSink = exports.HttpAuthOptions = exports.HttpOAuth2ClientCredentialsOptions = exports.PushDiffusionEndpointDefinition = exports.DelegateStreamEndpointDefinition = exports.SqsEndpointDefinition = exports.AzureEventHubsEndpointDefinition = exports.RedisStreamsEndpointDefinition = exports.NatsEndpointDefinition = exports.RabbitMqEndpointDefinition = exports.KafkaSaslOptions = exports.KafkaEndpointDefinition = exports.HttpEndpointDefinition = exports.TrafficEndpointDefinition = exports.LOADSTRIKE_TRACE_ID_TRACKING_FIELD = exports.LOADSTRIKE_TRACE_ID_HEADER = exports.TrackingFieldSelector = exports.TrackingPayloadBuilder = exports.RedisCorrelationStore = exports.RedisCorrelationStoreOptions = exports.InMemoryCorrelationStore = exports.CrossPlatformTrackingRuntime = exports.CorrelationStoreConfiguration = exports.LoadStrikeThreshold = exports.LoadStrikeStep = exports.LoadStrikeGauge = exports.LoadStrikeCounter = exports.LoadStrikeMetric = exports.CrossPlatformTrackingConfiguration = exports.LoadStrikeTrafficMix = exports.LoadStrikeSimulation = exports.LoadStrikeScenarioShare = exports.LoadStrikeScenario = exports.LoadStrikeRunner = exports.LoadStrikeOperationType = exports.LoadStrikeScenarioOperation = exports.LoadStrikeLogLevel = exports.LoadStrikeResponse = exports.LoadStrikeReportFormat = exports.LoadStrikeNodeType = exports.LoadStrikePluginDataTable = exports.LoadStrikePluginData = exports.LoadStrikeContext = exports.ScenarioTrackingExtensions = exports.CrossPlatformScenarioConfigurator = exports.LoadStrikeAutopilotReadiness = exports.LoadStrikeAutopilotResult = exports.LoadStrikeAutopilot = void 0;
4
+ exports.TimescaleDbReportingSinkOptions = exports.TimescaleDbReportingSink = exports.SplunkReportingSinkOptions = exports.SplunkReportingSink = exports.PortalReportingSink = exports.OtelCollectorReportingSinkOptions = exports.OtelCollectorReportingSink = exports.InfluxDbReportingSinkOptions = exports.InfluxDbReportingSink = exports.GrafanaLokiReportingSinkOptions = void 0;
5
5
  var autopilot_js_1 = require("./autopilot.js");
6
6
  Object.defineProperty(exports, "LoadStrikeAutopilot", { enumerable: true, get: function () { return autopilot_js_1.LoadStrikeAutopilot; } });
7
7
  Object.defineProperty(exports, "LoadStrikeAutopilotResult", { enumerable: true, get: function () { return autopilot_js_1.LoadStrikeAutopilotResult; } });
@@ -21,7 +21,9 @@ Object.defineProperty(exports, "LoadStrikeScenarioOperation", { enumerable: true
21
21
  Object.defineProperty(exports, "LoadStrikeOperationType", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeOperationType; } });
22
22
  Object.defineProperty(exports, "LoadStrikeRunner", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeRunner; } });
23
23
  Object.defineProperty(exports, "LoadStrikeScenario", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeScenario; } });
24
+ Object.defineProperty(exports, "LoadStrikeScenarioShare", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeScenarioShare; } });
24
25
  Object.defineProperty(exports, "LoadStrikeSimulation", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeSimulation; } });
26
+ Object.defineProperty(exports, "LoadStrikeTrafficMix", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeTrafficMix; } });
25
27
  Object.defineProperty(exports, "CrossPlatformTrackingConfiguration", { enumerable: true, get: function () { return runtime_js_1.CrossPlatformTrackingConfiguration; } });
26
28
  Object.defineProperty(exports, "LoadStrikeMetric", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeMetric; } });
27
29
  Object.defineProperty(exports, "LoadStrikeCounter", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeCounter; } });
package/dist/cjs/local.js CHANGED
@@ -32,9 +32,21 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
36
+ if (kind === "m") throw new TypeError("Private method is not writable");
37
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
38
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
39
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
40
+ };
41
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
42
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
43
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
44
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
45
+ };
35
46
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
47
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
48
  };
49
+ var _LoadStrikeLocalClient_licensingApiBaseUrl, _LoadStrikeLocalClient_signingKeyCache;
38
50
  Object.defineProperty(exports, "__esModule", { value: true });
39
51
  exports.__loadstrikeTestExports = exports.LoadStrikeLocalClient = void 0;
40
52
  const node_os_1 = __importDefault(require("node:os"));
@@ -92,13 +104,14 @@ class LoadStrikeLocalClient {
92
104
  * Use this when the surrounding wrapper type makes this operation the clearest way to express your intent.
93
105
  */
94
106
  constructor(options = {}) {
95
- this.signingKeyCache = new Map();
107
+ _LoadStrikeLocalClient_licensingApiBaseUrl.set(this, void 0);
108
+ _LoadStrikeLocalClient_signingKeyCache.set(this, new Map());
96
109
  assertNoDisableLicenseEnforcementOption(options, "LoadStrikeLocalClient");
97
- this.licensingApiBaseUrl = resolveLicensingApiBaseUrl();
110
+ __classPrivateFieldSet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, resolveLicensingApiBaseUrl(), "f");
98
111
  this.licenseValidationTimeoutMs = normalizeTimeoutMs(options.licenseValidationTimeoutMs);
99
112
  }
100
113
  portalReportingIngestUrl() {
101
- return `${this.licensingApiBaseUrl.replace(/\/+$/, "")}/api/v1/reporting/ingest`;
114
+ return `${__classPrivateFieldGet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, "f").replace(/\/+$/, "")}/api/v1/reporting/ingest`;
102
115
  }
103
116
  async run(request) {
104
117
  const sanitized = sanitizeRequest(request);
@@ -367,8 +380,8 @@ class LoadStrikeLocalClient {
367
380
  const nowMs = Date.now();
368
381
  const normalizedKeyId = stringOrDefault(keyId, "default").trim() || "default";
369
382
  const normalizedAlgorithm = stringOrDefault(algorithm, "RS256").toUpperCase();
370
- const cacheKey = `${this.licensingApiBaseUrl.toLowerCase()}|${normalizedAlgorithm}:${normalizedKeyId}`;
371
- const cached = this.signingKeyCache.get(cacheKey);
383
+ const cacheKey = `${__classPrivateFieldGet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, "f").toLowerCase()}|${normalizedAlgorithm}:${normalizedKeyId}`;
384
+ const cached = __classPrivateFieldGet(this, _LoadStrikeLocalClient_signingKeyCache, "f").get(cacheKey);
372
385
  if (cached && cached.expiresAtUtc > nowMs) {
373
386
  return cached;
374
387
  }
@@ -396,7 +409,7 @@ class LoadStrikeLocalClient {
396
409
  publicKeyPem,
397
410
  expiresAtUtc: nowMs + (15 * 60 * 1000)
398
411
  };
399
- this.signingKeyCache.set(cacheKey, keyRecord);
412
+ __classPrivateFieldGet(this, _LoadStrikeLocalClient_signingKeyCache, "f").set(cacheKey, keyRecord);
400
413
  return keyRecord;
401
414
  }
402
415
  finally {
@@ -448,7 +461,7 @@ class LoadStrikeLocalClient {
448
461
  }
449
462
  }
450
463
  async postLicensingRequest(path, payload, signal) {
451
- const response = await fetch(`${this.licensingApiBaseUrl.replace(/\/+$/, "")}${path}`, {
464
+ const response = await fetch(`${__classPrivateFieldGet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, "f").replace(/\/+$/, "")}${path}`, {
452
465
  method: "POST",
453
466
  headers: {
454
467
  "Content-Type": "application/json",
@@ -467,7 +480,7 @@ class LoadStrikeLocalClient {
467
480
  return { response, json };
468
481
  }
469
482
  async getLicensingRequest(path, signal) {
470
- const response = await fetch(`${this.licensingApiBaseUrl.replace(/\/+$/, "")}${path}`, {
483
+ const response = await fetch(`${__classPrivateFieldGet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, "f").replace(/\/+$/, "")}${path}`, {
471
484
  method: "GET",
472
485
  headers: {
473
486
  Accept: "application/json"
@@ -485,6 +498,7 @@ class LoadStrikeLocalClient {
485
498
  }
486
499
  }
487
500
  exports.LoadStrikeLocalClient = LoadStrikeLocalClient;
501
+ _LoadStrikeLocalClient_licensingApiBaseUrl = new WeakMap(), _LoadStrikeLocalClient_signingKeyCache = new WeakMap();
488
502
  function assertNoDisableLicenseEnforcementOption(value, source) {
489
503
  if (value == null || typeof value !== "object" || Array.isArray(value)) {
490
504
  return;
@@ -514,7 +528,43 @@ function resolveLicensingApiBaseUrl() {
514
528
  return normalizeLicensingApiBaseUrl(developmentLicensingApiBaseUrlOverride);
515
529
  }
516
530
  function setDevelopmentLicensingApiBaseUrlOverride(value) {
517
- developmentLicensingApiBaseUrlOverride = value;
531
+ if (value != null && value.trim()) {
532
+ assertInternalTestHarnessLicensingOverride();
533
+ const normalized = normalizeLicensingApiBaseUrl(value);
534
+ if (!isLoopbackHttpBaseUrl(normalized)) {
535
+ throw new TypeError("Internal development licensing API overrides must use a loopback http URL.");
536
+ }
537
+ developmentLicensingApiBaseUrlOverride = normalized;
538
+ return;
539
+ }
540
+ if (value != null) {
541
+ assertInternalTestHarnessLicensingOverride();
542
+ }
543
+ developmentLicensingApiBaseUrlOverride = undefined;
544
+ }
545
+ function assertInternalTestHarnessLicensingOverride() {
546
+ if (isInternalTestHarnessCall()) {
547
+ return;
548
+ }
549
+ throw new TypeError("Internal development licensing API override is available only to LoadStrike SDK tests.");
550
+ }
551
+ function isInternalTestHarnessCall() {
552
+ const stack = String(new Error().stack ?? "").replace(/\\/g, "/").toLowerCase();
553
+ return stack.includes("/sdk/ts/tests/");
554
+ }
555
+ function isLoopbackHttpBaseUrl(value) {
556
+ let parsed;
557
+ try {
558
+ parsed = new URL(value);
559
+ }
560
+ catch {
561
+ return false;
562
+ }
563
+ if (parsed.protocol !== "http:") {
564
+ return false;
565
+ }
566
+ const host = parsed.hostname.trim().toLowerCase();
567
+ return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1" || host.endsWith(".localhost");
518
568
  }
519
569
  function normalizeTimeoutMs(value) {
520
570
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
@@ -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.CrossPlatformTrackingConfiguration = exports.LoadStrikeOperationType = exports.LoadStrikeScenarioOperation = exports.LoadStrikeLogLevel = exports.LoadStrikeReportFormat = exports.LoadStrikeNodeType = void 0;
6
+ exports.__loadstrikeTestExports = exports.LoadStrikeRunner = exports.LoadStrikeTrafficMix = exports.LoadStrikeScenarioShare = 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"));
@@ -75,6 +75,7 @@ exports.CrossPlatformTrackingConfiguration = {
75
75
  return this.forDuration(configuration, durationSeconds, cancellationSignal);
76
76
  }
77
77
  };
78
+ const TRAFFIC_MIX_FEATURE = "workload.traffic_mix";
78
79
  class LoadStrikePluginDataTable {
79
80
  /**
80
81
  * Exposes the public constructor operation.
@@ -1967,6 +1968,105 @@ class LoadStrikeScenario {
1967
1968
  }
1968
1969
  }
1969
1970
  exports.LoadStrikeScenario = LoadStrikeScenario;
1971
+ class LoadStrikeScenarioShare {
1972
+ constructor(scenario, weight) {
1973
+ this.scenario = scenario;
1974
+ this.weight = weight;
1975
+ }
1976
+ /**
1977
+ * Creates a weighted scenario lane for a traffic mix.
1978
+ * Use this when a total workload should be distributed across several scenarios.
1979
+ */
1980
+ static create(scenario, weight) {
1981
+ if (!(scenario instanceof LoadStrikeScenario)) {
1982
+ throw new TypeError("Scenario must be provided.");
1983
+ }
1984
+ const normalizedWeight = Math.trunc(weight);
1985
+ if (!Number.isFinite(weight) || normalizedWeight <= 0) {
1986
+ throw new RangeError("Scenario share weight should be greater than zero.");
1987
+ }
1988
+ return new LoadStrikeScenarioShare(scenario, normalizedWeight);
1989
+ }
1990
+ /**
1991
+ * Creates a weighted scenario lane for a traffic mix.
1992
+ * Use this when a total workload should be distributed across several scenarios.
1993
+ */
1994
+ static Create(scenario, weight) {
1995
+ return LoadStrikeScenarioShare.create(scenario, weight);
1996
+ }
1997
+ }
1998
+ exports.LoadStrikeScenarioShare = LoadStrikeScenarioShare;
1999
+ class LoadStrikeTrafficMix {
2000
+ constructor(name, totalLoad = [], scenarioMix = []) {
2001
+ this.name = name;
2002
+ this.totalLoad = totalLoad.map((simulation) => attachLoadSimulationProjection({ ...simulation }));
2003
+ this.scenarioMix = [...scenarioMix];
2004
+ }
2005
+ /**
2006
+ * Creates a traffic mix definition.
2007
+ * Use this when one total load profile should be split across multiple scenario lanes.
2008
+ */
2009
+ static create(name) {
2010
+ return new LoadStrikeTrafficMix(requireNonEmpty(name, "Traffic mix name must be provided."));
2011
+ }
2012
+ /**
2013
+ * Creates a traffic mix definition.
2014
+ * Use this when one total load profile should be split across multiple scenario lanes.
2015
+ */
2016
+ static Create(name) {
2017
+ return LoadStrikeTrafficMix.create(name);
2018
+ }
2019
+ /**
2020
+ * Sets the total workload shape for the mix.
2021
+ * Use this when the same high-level load profile should be distributed by scenario weights.
2022
+ */
2023
+ withTotalLoad(...totalLoad) {
2024
+ if (!totalLoad.length) {
2025
+ throw new Error("At least one total load simulation should be provided.");
2026
+ }
2027
+ return new LoadStrikeTrafficMix(this.name, totalLoad, this.scenarioMix);
2028
+ }
2029
+ /**
2030
+ * Sets the total workload shape for the mix.
2031
+ * Use this when the same high-level load profile should be distributed by scenario weights.
2032
+ */
2033
+ WithTotalLoad(...totalLoad) {
2034
+ return this.withTotalLoad(...totalLoad);
2035
+ }
2036
+ /**
2037
+ * Sets the weighted scenario lanes for the mix.
2038
+ * Use this when each operation should receive a fixed proportion of the total workload.
2039
+ */
2040
+ withScenarioMix(...scenarioMix) {
2041
+ if (!scenarioMix.length) {
2042
+ throw new Error("At least one scenario share should be provided.");
2043
+ }
2044
+ if (scenarioMix.some((share) => !(share instanceof LoadStrikeScenarioShare))) {
2045
+ throw new TypeError("Scenario share collection cannot contain null values.");
2046
+ }
2047
+ return new LoadStrikeTrafficMix(this.name, this.totalLoad, scenarioMix);
2048
+ }
2049
+ /**
2050
+ * Sets the weighted scenario lanes for the mix.
2051
+ * Use this when each operation should receive a fixed proportion of the total workload.
2052
+ */
2053
+ WithScenarioMix(...scenarioMix) {
2054
+ return this.withScenarioMix(...scenarioMix);
2055
+ }
2056
+ expandScenarios() {
2057
+ return expandTrafficMixScenarios(this);
2058
+ }
2059
+ ExpandScenarios() {
2060
+ return this.expandScenarios();
2061
+ }
2062
+ __loadStrikeTotalLoad() {
2063
+ return this.totalLoad.map((simulation) => attachLoadSimulationProjection({ ...simulation }));
2064
+ }
2065
+ __loadStrikeScenarioMix() {
2066
+ return [...this.scenarioMix];
2067
+ }
2068
+ }
2069
+ exports.LoadStrikeTrafficMix = LoadStrikeTrafficMix;
1970
2070
  class LoadStrikeRunner {
1971
2071
  constructor(scenarios, options, contextConfigurators = []) {
1972
2072
  this.scenarios = scenarios;
@@ -2002,6 +2102,20 @@ class LoadStrikeRunner {
2002
2102
  static RegisterScenarios(...scenarios) {
2003
2103
  return LoadStrikeRunner.registerScenarios(...scenarios);
2004
2104
  }
2105
+ /**
2106
+ * Registers a traffic mix on a fresh runnable context.
2107
+ * Use this when one total load profile should be split across weighted scenario lanes.
2108
+ */
2109
+ static registerTrafficMix(trafficMix) {
2110
+ return LoadStrikeRunner.registerScenarios(...expandTrafficMixScenarios(trafficMix));
2111
+ }
2112
+ /**
2113
+ * Registers a traffic mix on a fresh runnable context.
2114
+ * Use this when one total load profile should be split across weighted scenario lanes.
2115
+ */
2116
+ static RegisterTrafficMix(trafficMix) {
2117
+ return LoadStrikeRunner.registerTrafficMix(trafficMix);
2118
+ }
2005
2119
  /**
2006
2120
  * Toggles realtime console metric output.
2007
2121
  * Use this when a local run or CI log should stream live throughput and latency updates.
@@ -2256,6 +2370,21 @@ class LoadStrikeRunner {
2256
2370
  AddScenarios(...scenarios) {
2257
2371
  return this.addScenarios(...scenarios);
2258
2372
  }
2373
+ /**
2374
+ * Adds a traffic mix to the current runner.
2375
+ * Use this when one total load profile should be split across weighted scenario lanes.
2376
+ */
2377
+ addTrafficMix(trafficMix) {
2378
+ this.scenarios = [...this.scenarios, ...expandTrafficMixScenarios(trafficMix)];
2379
+ return this;
2380
+ }
2381
+ /**
2382
+ * Adds a traffic mix to the current runner.
2383
+ * Use this when one total load profile should be split across weighted scenario lanes.
2384
+ */
2385
+ AddTrafficMix(trafficMix) {
2386
+ return this.addTrafficMix(trafficMix);
2387
+ }
2259
2388
  /**
2260
2389
  * Applies a grouped configuration change to the current builder or context.
2261
2390
  * Use this when several related settings should be supplied in one step.
@@ -3680,7 +3809,7 @@ function normalizeReply(value) {
3680
3809
  statusCode: normalizeStatusCode(value?.statusCode, Boolean(value?.isSuccess)),
3681
3810
  message: value?.message ? String(value.message) : "",
3682
3811
  sizeBytes: Number.isFinite(value?.sizeBytes) ? Number(value.sizeBytes) : 0,
3683
- customLatencyMs: Number.isFinite(value?.customLatencyMs) ? Number(value.customLatencyMs) : 0,
3812
+ customLatencyMs: Number.isFinite(value?.customLatencyMs) ? Number(value.customLatencyMs) : -1,
3684
3813
  payload: value?.payload
3685
3814
  };
3686
3815
  }
@@ -3692,7 +3821,7 @@ function attachReplyProjection(reply) {
3692
3821
  SizeBytes: "sizeBytes",
3693
3822
  Payload: "payload"
3694
3823
  });
3695
- defineAliasProperty(reply, "CustomLatencyMs", () => Number.isFinite(reply.customLatencyMs) ? Number(reply.customLatencyMs) : 0);
3824
+ defineAliasProperty(reply, "CustomLatencyMs", () => Number.isFinite(reply.customLatencyMs) ? Number(reply.customLatencyMs) : -1);
3696
3825
  const asReply = () => attachReplyProjection({
3697
3826
  isSuccess: reply.isSuccess,
3698
3827
  statusCode: reply.statusCode,
@@ -4408,6 +4537,111 @@ function attachLoadSimulationProjection(simulation) {
4408
4537
  });
4409
4538
  return simulation;
4410
4539
  }
4540
+ function expandTrafficMixScenarios(trafficMix) {
4541
+ if (!(trafficMix instanceof LoadStrikeTrafficMix)) {
4542
+ throw new TypeError("Traffic mix must be provided.");
4543
+ }
4544
+ const totalLoad = trafficMix.__loadStrikeTotalLoad();
4545
+ if (!totalLoad.length) {
4546
+ throw new Error("Traffic mix total load must be configured before registration.");
4547
+ }
4548
+ const scenarioMix = trafficMix.__loadStrikeScenarioMix();
4549
+ if (!scenarioMix.length) {
4550
+ throw new Error("Traffic mix scenario shares must be configured before registration.");
4551
+ }
4552
+ const weights = scenarioMix.map((share) => share.weight);
4553
+ return scenarioMix.map((share, index) => {
4554
+ const splitSimulations = totalLoad
4555
+ .map((simulation) => splitTrafficSimulation(simulation, weights, index))
4556
+ .filter((simulation) => simulation != null);
4557
+ const scenario = !splitSimulations.length
4558
+ ? share.scenario.withLoadSimulations(LoadStrikeSimulation.pause(0))
4559
+ : share.scenario.withLoadSimulations(...splitSimulations);
4560
+ return scenario.__loadStrikeWithInternalLicenseFeatures(TRAFFIC_MIX_FEATURE);
4561
+ });
4562
+ }
4563
+ function splitTrafficSimulation(simulation, weights, index) {
4564
+ const kind = String(simulation.Kind ?? "");
4565
+ const duringSeconds = readFiniteSimulationNumber(simulation, "DuringSeconds");
4566
+ const intervalSeconds = readFiniteSimulationNumber(simulation, "IntervalSeconds");
4567
+ if (kind === "Inject") {
4568
+ const rate = splitTrafficValue(readFiniteSimulationNumber(simulation, "Rate"), weights)[index];
4569
+ return rate > 0 ? LoadStrikeSimulation.inject(rate, intervalSeconds, duringSeconds) : null;
4570
+ }
4571
+ if (kind === "RampingInject") {
4572
+ const rate = splitTrafficValue(readFiniteSimulationNumber(simulation, "Rate"), weights)[index];
4573
+ return rate > 0 ? LoadStrikeSimulation.rampingInject(rate, intervalSeconds, duringSeconds) : null;
4574
+ }
4575
+ if (kind === "InjectRandom") {
4576
+ const minRate = splitTrafficValue(readFiniteSimulationNumber(simulation, "MinRate"), weights)[index];
4577
+ const maxRate = splitTrafficValue(readFiniteSimulationNumber(simulation, "MaxRate"), weights)[index];
4578
+ if (maxRate <= 0) {
4579
+ return null;
4580
+ }
4581
+ return LoadStrikeSimulation.injectRandom(Math.min(minRate, maxRate), maxRate, intervalSeconds, duringSeconds);
4582
+ }
4583
+ if (kind === "IterationsForInject") {
4584
+ const rate = splitTrafficValue(readFiniteSimulationNumber(simulation, "Rate"), weights)[index];
4585
+ const iterations = splitTrafficValue(readFiniteSimulationNumber(simulation, "Iterations"), weights)[index];
4586
+ return rate > 0 && iterations > 0
4587
+ ? LoadStrikeSimulation.iterationsForInject(rate, intervalSeconds, iterations)
4588
+ : null;
4589
+ }
4590
+ if (kind === "IterationsForConstant") {
4591
+ const copies = splitTrafficValue(readFiniteSimulationNumber(simulation, "Copies"), weights)[index];
4592
+ const iterations = splitTrafficValue(readFiniteSimulationNumber(simulation, "Iterations"), weights)[index];
4593
+ return copies > 0 && iterations > 0
4594
+ ? LoadStrikeSimulation.iterationsForConstant(copies, iterations)
4595
+ : null;
4596
+ }
4597
+ if (kind === "KeepConstant") {
4598
+ const copies = splitTrafficValue(readFiniteSimulationNumber(simulation, "Copies"), weights)[index];
4599
+ return copies > 0 ? LoadStrikeSimulation.keepConstant(copies, duringSeconds) : null;
4600
+ }
4601
+ if (kind === "RampingConstant") {
4602
+ const copies = splitTrafficValue(readFiniteSimulationNumber(simulation, "Copies"), weights)[index];
4603
+ return copies > 0 ? LoadStrikeSimulation.rampingConstant(copies, duringSeconds) : null;
4604
+ }
4605
+ if (kind === "Pause") {
4606
+ return LoadStrikeSimulation.pause(duringSeconds);
4607
+ }
4608
+ return attachLoadSimulationProjection({ ...simulation });
4609
+ }
4610
+ function splitTrafficValue(totalValue, weights) {
4611
+ const normalizedTotal = Math.trunc(Number.isFinite(totalValue) ? totalValue : 0);
4612
+ if (normalizedTotal <= 0) {
4613
+ return weights.map(() => 0);
4614
+ }
4615
+ const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
4616
+ if (totalWeight <= 0) {
4617
+ throw new Error("Traffic mix scenario weights must add up to more than zero.");
4618
+ }
4619
+ const exactShares = weights.map((weight, index) => {
4620
+ const exact = (normalizedTotal * weight) / totalWeight;
4621
+ return {
4622
+ index,
4623
+ value: Math.floor(exact),
4624
+ remainder: exact - Math.floor(exact)
4625
+ };
4626
+ });
4627
+ const split = exactShares.map((share) => share.value);
4628
+ let remaining = normalizedTotal - split.reduce((sum, value) => sum + value, 0);
4629
+ for (const share of [...exactShares].sort((left, right) => {
4630
+ const remainderDelta = right.remainder - left.remainder;
4631
+ return remainderDelta !== 0 ? remainderDelta : left.index - right.index;
4632
+ })) {
4633
+ if (remaining <= 0) {
4634
+ break;
4635
+ }
4636
+ split[share.index] += 1;
4637
+ remaining -= 1;
4638
+ }
4639
+ return split;
4640
+ }
4641
+ function readFiniteSimulationNumber(simulation, key) {
4642
+ const value = Number(simulation[key]);
4643
+ return Number.isFinite(value) ? value : 0;
4644
+ }
4411
4645
  function attachScenarioStatsAliases(scenario) {
4412
4646
  const normalized = normalizeScenarioStatsValue(scenario);
4413
4647
  normalized.ok = attachMeasurementStatsAliases(normalized.ok);
@@ -4590,15 +4824,15 @@ function attachRunResultAliases(result) {
4590
4824
  defineAliasProperty(projected, "Duration", () => projected.durationMs);
4591
4825
  return projected;
4592
4826
  }
4593
- function resolveResponseCustomLatency(customLatencyMs, usesOverloadDefaults) {
4827
+ function resolveResponseCustomLatency(customLatencyMs, _usesOverloadDefaults) {
4594
4828
  if (!Number.isFinite(customLatencyMs)) {
4595
- return usesOverloadDefaults ? -1 : 0;
4829
+ return -1;
4596
4830
  }
4597
4831
  return Number(customLatencyMs);
4598
4832
  }
4599
4833
  function resolveRecordedLatency(customLatencyMs, observedLatencyMs) {
4600
4834
  if (!Number.isFinite(customLatencyMs)) {
4601
- return 0;
4835
+ return Math.max(observedLatencyMs, 0);
4602
4836
  }
4603
4837
  const resolved = Number(customLatencyMs);
4604
4838
  if (resolved >= 0) {
@@ -8366,6 +8600,7 @@ exports.__loadstrikeTestExports = {
8366
8600
  detailedToNodeStats,
8367
8601
  evaluateThresholdsForScenarios,
8368
8602
  executeTrackedScenarioInvocation,
8603
+ expandTrafficMixScenarios,
8369
8604
  extractContextOverridesFromArgs,
8370
8605
  extractContextOverridesFromConfig,
8371
8606
  findCaseInsensitiveKey,
package/dist/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { LoadStrikeAutopilot, LoadStrikeAutopilotResult } from "./autopilot.js";
2
2
  export { LoadStrikeAutopilotReadiness } from "./autopilot-contracts.js";
3
- export { CrossPlatformScenarioConfigurator, ScenarioTrackingExtensions, LoadStrikeContext, LoadStrikePluginData, LoadStrikePluginDataTable, LoadStrikeNodeType, LoadStrikeReportFormat, LoadStrikeResponse, LoadStrikeLogLevel, LoadStrikeScenarioOperation, LoadStrikeOperationType, LoadStrikeRunner, LoadStrikeScenario, LoadStrikeSimulation, CrossPlatformTrackingConfiguration, LoadStrikeMetric, LoadStrikeCounter, LoadStrikeGauge, LoadStrikeStep, LoadStrikeThreshold } from "./runtime.js";
3
+ export { CrossPlatformScenarioConfigurator, ScenarioTrackingExtensions, LoadStrikeContext, LoadStrikePluginData, LoadStrikePluginDataTable, LoadStrikeNodeType, LoadStrikeReportFormat, LoadStrikeResponse, LoadStrikeLogLevel, LoadStrikeScenarioOperation, LoadStrikeOperationType, LoadStrikeRunner, LoadStrikeScenario, LoadStrikeScenarioShare, LoadStrikeSimulation, LoadStrikeTrafficMix, CrossPlatformTrackingConfiguration, LoadStrikeMetric, LoadStrikeCounter, LoadStrikeGauge, LoadStrikeStep, LoadStrikeThreshold } from "./runtime.js";
4
4
  export { CorrelationStoreConfiguration, CrossPlatformTrackingRuntime, InMemoryCorrelationStore, RedisCorrelationStoreOptions, RedisCorrelationStore, TrackingPayloadBuilder, TrackingFieldSelector } from "./correlation.js";
5
5
  export { LOADSTRIKE_TRACE_ID_HEADER, LOADSTRIKE_TRACE_ID_TRACKING_FIELD, TrafficEndpointDefinition, HttpEndpointDefinition, KafkaEndpointDefinition, KafkaSaslOptions, RabbitMqEndpointDefinition, NatsEndpointDefinition, RedisStreamsEndpointDefinition, AzureEventHubsEndpointDefinition, SqsEndpointDefinition, DelegateStreamEndpointDefinition, PushDiffusionEndpointDefinition, HttpOAuth2ClientCredentialsOptions, HttpAuthOptions } from "./transports.js";
6
6
  export { DatadogReportingSink, DatadogReportingSinkOptions, GrafanaLokiReportingSink, GrafanaLokiReportingSinkOptions, InfluxDbReportingSink, InfluxDbReportingSinkOptions, OtelCollectorReportingSink, OtelCollectorReportingSinkOptions, PortalReportingSink, SplunkReportingSink, SplunkReportingSinkOptions, TimescaleDbReportingSink, TimescaleDbReportingSinkOptions } from "./sinks.js";
package/dist/esm/local.js CHANGED
@@ -1,3 +1,15 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _LoadStrikeLocalClient_licensingApiBaseUrl, _LoadStrikeLocalClient_signingKeyCache;
1
13
  import os from "node:os";
2
14
  import * as fs from "node:fs";
3
15
  import * as childProcess from "node:child_process";
@@ -53,13 +65,14 @@ export class LoadStrikeLocalClient {
53
65
  * Use this when the surrounding wrapper type makes this operation the clearest way to express your intent.
54
66
  */
55
67
  constructor(options = {}) {
56
- this.signingKeyCache = new Map();
68
+ _LoadStrikeLocalClient_licensingApiBaseUrl.set(this, void 0);
69
+ _LoadStrikeLocalClient_signingKeyCache.set(this, new Map());
57
70
  assertNoDisableLicenseEnforcementOption(options, "LoadStrikeLocalClient");
58
- this.licensingApiBaseUrl = resolveLicensingApiBaseUrl();
71
+ __classPrivateFieldSet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, resolveLicensingApiBaseUrl(), "f");
59
72
  this.licenseValidationTimeoutMs = normalizeTimeoutMs(options.licenseValidationTimeoutMs);
60
73
  }
61
74
  portalReportingIngestUrl() {
62
- return `${this.licensingApiBaseUrl.replace(/\/+$/, "")}/api/v1/reporting/ingest`;
75
+ return `${__classPrivateFieldGet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, "f").replace(/\/+$/, "")}/api/v1/reporting/ingest`;
63
76
  }
64
77
  async run(request) {
65
78
  const sanitized = sanitizeRequest(request);
@@ -328,8 +341,8 @@ export class LoadStrikeLocalClient {
328
341
  const nowMs = Date.now();
329
342
  const normalizedKeyId = stringOrDefault(keyId, "default").trim() || "default";
330
343
  const normalizedAlgorithm = stringOrDefault(algorithm, "RS256").toUpperCase();
331
- const cacheKey = `${this.licensingApiBaseUrl.toLowerCase()}|${normalizedAlgorithm}:${normalizedKeyId}`;
332
- const cached = this.signingKeyCache.get(cacheKey);
344
+ const cacheKey = `${__classPrivateFieldGet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, "f").toLowerCase()}|${normalizedAlgorithm}:${normalizedKeyId}`;
345
+ const cached = __classPrivateFieldGet(this, _LoadStrikeLocalClient_signingKeyCache, "f").get(cacheKey);
333
346
  if (cached && cached.expiresAtUtc > nowMs) {
334
347
  return cached;
335
348
  }
@@ -357,7 +370,7 @@ export class LoadStrikeLocalClient {
357
370
  publicKeyPem,
358
371
  expiresAtUtc: nowMs + (15 * 60 * 1000)
359
372
  };
360
- this.signingKeyCache.set(cacheKey, keyRecord);
373
+ __classPrivateFieldGet(this, _LoadStrikeLocalClient_signingKeyCache, "f").set(cacheKey, keyRecord);
361
374
  return keyRecord;
362
375
  }
363
376
  finally {
@@ -409,7 +422,7 @@ export class LoadStrikeLocalClient {
409
422
  }
410
423
  }
411
424
  async postLicensingRequest(path, payload, signal) {
412
- const response = await fetch(`${this.licensingApiBaseUrl.replace(/\/+$/, "")}${path}`, {
425
+ const response = await fetch(`${__classPrivateFieldGet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, "f").replace(/\/+$/, "")}${path}`, {
413
426
  method: "POST",
414
427
  headers: {
415
428
  "Content-Type": "application/json",
@@ -428,7 +441,7 @@ export class LoadStrikeLocalClient {
428
441
  return { response, json };
429
442
  }
430
443
  async getLicensingRequest(path, signal) {
431
- const response = await fetch(`${this.licensingApiBaseUrl.replace(/\/+$/, "")}${path}`, {
444
+ const response = await fetch(`${__classPrivateFieldGet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, "f").replace(/\/+$/, "")}${path}`, {
432
445
  method: "GET",
433
446
  headers: {
434
447
  Accept: "application/json"
@@ -445,6 +458,7 @@ export class LoadStrikeLocalClient {
445
458
  return { response, json };
446
459
  }
447
460
  }
461
+ _LoadStrikeLocalClient_licensingApiBaseUrl = new WeakMap(), _LoadStrikeLocalClient_signingKeyCache = new WeakMap();
448
462
  function assertNoDisableLicenseEnforcementOption(value, source) {
449
463
  if (value == null || typeof value !== "object" || Array.isArray(value)) {
450
464
  return;
@@ -474,7 +488,43 @@ function resolveLicensingApiBaseUrl() {
474
488
  return normalizeLicensingApiBaseUrl(developmentLicensingApiBaseUrlOverride);
475
489
  }
476
490
  function setDevelopmentLicensingApiBaseUrlOverride(value) {
477
- developmentLicensingApiBaseUrlOverride = value;
491
+ if (value != null && value.trim()) {
492
+ assertInternalTestHarnessLicensingOverride();
493
+ const normalized = normalizeLicensingApiBaseUrl(value);
494
+ if (!isLoopbackHttpBaseUrl(normalized)) {
495
+ throw new TypeError("Internal development licensing API overrides must use a loopback http URL.");
496
+ }
497
+ developmentLicensingApiBaseUrlOverride = normalized;
498
+ return;
499
+ }
500
+ if (value != null) {
501
+ assertInternalTestHarnessLicensingOverride();
502
+ }
503
+ developmentLicensingApiBaseUrlOverride = undefined;
504
+ }
505
+ function assertInternalTestHarnessLicensingOverride() {
506
+ if (isInternalTestHarnessCall()) {
507
+ return;
508
+ }
509
+ throw new TypeError("Internal development licensing API override is available only to LoadStrike SDK tests.");
510
+ }
511
+ function isInternalTestHarnessCall() {
512
+ const stack = String(new Error().stack ?? "").replace(/\\/g, "/").toLowerCase();
513
+ return stack.includes("/sdk/ts/tests/");
514
+ }
515
+ function isLoopbackHttpBaseUrl(value) {
516
+ let parsed;
517
+ try {
518
+ parsed = new URL(value);
519
+ }
520
+ catch {
521
+ return false;
522
+ }
523
+ if (parsed.protocol !== "http:") {
524
+ return false;
525
+ }
526
+ const host = parsed.hostname.trim().toLowerCase();
527
+ return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1" || host.endsWith(".localhost");
478
528
  }
479
529
  function normalizeTimeoutMs(value) {
480
530
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
@@ -69,6 +69,7 @@ export const CrossPlatformTrackingConfiguration = {
69
69
  return this.forDuration(configuration, durationSeconds, cancellationSignal);
70
70
  }
71
71
  };
72
+ const TRAFFIC_MIX_FEATURE = "workload.traffic_mix";
72
73
  export class LoadStrikePluginDataTable {
73
74
  /**
74
75
  * Exposes the public constructor operation.
@@ -1948,6 +1949,103 @@ export class LoadStrikeScenario {
1948
1949
  return this.withWeight(weight);
1949
1950
  }
1950
1951
  }
1952
+ export class LoadStrikeScenarioShare {
1953
+ constructor(scenario, weight) {
1954
+ this.scenario = scenario;
1955
+ this.weight = weight;
1956
+ }
1957
+ /**
1958
+ * Creates a weighted scenario lane for a traffic mix.
1959
+ * Use this when a total workload should be distributed across several scenarios.
1960
+ */
1961
+ static create(scenario, weight) {
1962
+ if (!(scenario instanceof LoadStrikeScenario)) {
1963
+ throw new TypeError("Scenario must be provided.");
1964
+ }
1965
+ const normalizedWeight = Math.trunc(weight);
1966
+ if (!Number.isFinite(weight) || normalizedWeight <= 0) {
1967
+ throw new RangeError("Scenario share weight should be greater than zero.");
1968
+ }
1969
+ return new LoadStrikeScenarioShare(scenario, normalizedWeight);
1970
+ }
1971
+ /**
1972
+ * Creates a weighted scenario lane for a traffic mix.
1973
+ * Use this when a total workload should be distributed across several scenarios.
1974
+ */
1975
+ static Create(scenario, weight) {
1976
+ return LoadStrikeScenarioShare.create(scenario, weight);
1977
+ }
1978
+ }
1979
+ export class LoadStrikeTrafficMix {
1980
+ constructor(name, totalLoad = [], scenarioMix = []) {
1981
+ this.name = name;
1982
+ this.totalLoad = totalLoad.map((simulation) => attachLoadSimulationProjection({ ...simulation }));
1983
+ this.scenarioMix = [...scenarioMix];
1984
+ }
1985
+ /**
1986
+ * Creates a traffic mix definition.
1987
+ * Use this when one total load profile should be split across multiple scenario lanes.
1988
+ */
1989
+ static create(name) {
1990
+ return new LoadStrikeTrafficMix(requireNonEmpty(name, "Traffic mix name must be provided."));
1991
+ }
1992
+ /**
1993
+ * Creates a traffic mix definition.
1994
+ * Use this when one total load profile should be split across multiple scenario lanes.
1995
+ */
1996
+ static Create(name) {
1997
+ return LoadStrikeTrafficMix.create(name);
1998
+ }
1999
+ /**
2000
+ * Sets the total workload shape for the mix.
2001
+ * Use this when the same high-level load profile should be distributed by scenario weights.
2002
+ */
2003
+ withTotalLoad(...totalLoad) {
2004
+ if (!totalLoad.length) {
2005
+ throw new Error("At least one total load simulation should be provided.");
2006
+ }
2007
+ return new LoadStrikeTrafficMix(this.name, totalLoad, this.scenarioMix);
2008
+ }
2009
+ /**
2010
+ * Sets the total workload shape for the mix.
2011
+ * Use this when the same high-level load profile should be distributed by scenario weights.
2012
+ */
2013
+ WithTotalLoad(...totalLoad) {
2014
+ return this.withTotalLoad(...totalLoad);
2015
+ }
2016
+ /**
2017
+ * Sets the weighted scenario lanes for the mix.
2018
+ * Use this when each operation should receive a fixed proportion of the total workload.
2019
+ */
2020
+ withScenarioMix(...scenarioMix) {
2021
+ if (!scenarioMix.length) {
2022
+ throw new Error("At least one scenario share should be provided.");
2023
+ }
2024
+ if (scenarioMix.some((share) => !(share instanceof LoadStrikeScenarioShare))) {
2025
+ throw new TypeError("Scenario share collection cannot contain null values.");
2026
+ }
2027
+ return new LoadStrikeTrafficMix(this.name, this.totalLoad, scenarioMix);
2028
+ }
2029
+ /**
2030
+ * Sets the weighted scenario lanes for the mix.
2031
+ * Use this when each operation should receive a fixed proportion of the total workload.
2032
+ */
2033
+ WithScenarioMix(...scenarioMix) {
2034
+ return this.withScenarioMix(...scenarioMix);
2035
+ }
2036
+ expandScenarios() {
2037
+ return expandTrafficMixScenarios(this);
2038
+ }
2039
+ ExpandScenarios() {
2040
+ return this.expandScenarios();
2041
+ }
2042
+ __loadStrikeTotalLoad() {
2043
+ return this.totalLoad.map((simulation) => attachLoadSimulationProjection({ ...simulation }));
2044
+ }
2045
+ __loadStrikeScenarioMix() {
2046
+ return [...this.scenarioMix];
2047
+ }
2048
+ }
1951
2049
  export class LoadStrikeRunner {
1952
2050
  constructor(scenarios, options, contextConfigurators = []) {
1953
2051
  this.scenarios = scenarios;
@@ -1983,6 +2081,20 @@ export class LoadStrikeRunner {
1983
2081
  static RegisterScenarios(...scenarios) {
1984
2082
  return LoadStrikeRunner.registerScenarios(...scenarios);
1985
2083
  }
2084
+ /**
2085
+ * Registers a traffic mix on a fresh runnable context.
2086
+ * Use this when one total load profile should be split across weighted scenario lanes.
2087
+ */
2088
+ static registerTrafficMix(trafficMix) {
2089
+ return LoadStrikeRunner.registerScenarios(...expandTrafficMixScenarios(trafficMix));
2090
+ }
2091
+ /**
2092
+ * Registers a traffic mix on a fresh runnable context.
2093
+ * Use this when one total load profile should be split across weighted scenario lanes.
2094
+ */
2095
+ static RegisterTrafficMix(trafficMix) {
2096
+ return LoadStrikeRunner.registerTrafficMix(trafficMix);
2097
+ }
1986
2098
  /**
1987
2099
  * Toggles realtime console metric output.
1988
2100
  * Use this when a local run or CI log should stream live throughput and latency updates.
@@ -2237,6 +2349,21 @@ export class LoadStrikeRunner {
2237
2349
  AddScenarios(...scenarios) {
2238
2350
  return this.addScenarios(...scenarios);
2239
2351
  }
2352
+ /**
2353
+ * Adds a traffic mix to the current runner.
2354
+ * Use this when one total load profile should be split across weighted scenario lanes.
2355
+ */
2356
+ addTrafficMix(trafficMix) {
2357
+ this.scenarios = [...this.scenarios, ...expandTrafficMixScenarios(trafficMix)];
2358
+ return this;
2359
+ }
2360
+ /**
2361
+ * Adds a traffic mix to the current runner.
2362
+ * Use this when one total load profile should be split across weighted scenario lanes.
2363
+ */
2364
+ AddTrafficMix(trafficMix) {
2365
+ return this.addTrafficMix(trafficMix);
2366
+ }
2240
2367
  /**
2241
2368
  * Applies a grouped configuration change to the current builder or context.
2242
2369
  * Use this when several related settings should be supplied in one step.
@@ -3660,7 +3787,7 @@ function normalizeReply(value) {
3660
3787
  statusCode: normalizeStatusCode(value?.statusCode, Boolean(value?.isSuccess)),
3661
3788
  message: value?.message ? String(value.message) : "",
3662
3789
  sizeBytes: Number.isFinite(value?.sizeBytes) ? Number(value.sizeBytes) : 0,
3663
- customLatencyMs: Number.isFinite(value?.customLatencyMs) ? Number(value.customLatencyMs) : 0,
3790
+ customLatencyMs: Number.isFinite(value?.customLatencyMs) ? Number(value.customLatencyMs) : -1,
3664
3791
  payload: value?.payload
3665
3792
  };
3666
3793
  }
@@ -3672,7 +3799,7 @@ function attachReplyProjection(reply) {
3672
3799
  SizeBytes: "sizeBytes",
3673
3800
  Payload: "payload"
3674
3801
  });
3675
- defineAliasProperty(reply, "CustomLatencyMs", () => Number.isFinite(reply.customLatencyMs) ? Number(reply.customLatencyMs) : 0);
3802
+ defineAliasProperty(reply, "CustomLatencyMs", () => Number.isFinite(reply.customLatencyMs) ? Number(reply.customLatencyMs) : -1);
3676
3803
  const asReply = () => attachReplyProjection({
3677
3804
  isSuccess: reply.isSuccess,
3678
3805
  statusCode: reply.statusCode,
@@ -4388,6 +4515,111 @@ function attachLoadSimulationProjection(simulation) {
4388
4515
  });
4389
4516
  return simulation;
4390
4517
  }
4518
+ function expandTrafficMixScenarios(trafficMix) {
4519
+ if (!(trafficMix instanceof LoadStrikeTrafficMix)) {
4520
+ throw new TypeError("Traffic mix must be provided.");
4521
+ }
4522
+ const totalLoad = trafficMix.__loadStrikeTotalLoad();
4523
+ if (!totalLoad.length) {
4524
+ throw new Error("Traffic mix total load must be configured before registration.");
4525
+ }
4526
+ const scenarioMix = trafficMix.__loadStrikeScenarioMix();
4527
+ if (!scenarioMix.length) {
4528
+ throw new Error("Traffic mix scenario shares must be configured before registration.");
4529
+ }
4530
+ const weights = scenarioMix.map((share) => share.weight);
4531
+ return scenarioMix.map((share, index) => {
4532
+ const splitSimulations = totalLoad
4533
+ .map((simulation) => splitTrafficSimulation(simulation, weights, index))
4534
+ .filter((simulation) => simulation != null);
4535
+ const scenario = !splitSimulations.length
4536
+ ? share.scenario.withLoadSimulations(LoadStrikeSimulation.pause(0))
4537
+ : share.scenario.withLoadSimulations(...splitSimulations);
4538
+ return scenario.__loadStrikeWithInternalLicenseFeatures(TRAFFIC_MIX_FEATURE);
4539
+ });
4540
+ }
4541
+ function splitTrafficSimulation(simulation, weights, index) {
4542
+ const kind = String(simulation.Kind ?? "");
4543
+ const duringSeconds = readFiniteSimulationNumber(simulation, "DuringSeconds");
4544
+ const intervalSeconds = readFiniteSimulationNumber(simulation, "IntervalSeconds");
4545
+ if (kind === "Inject") {
4546
+ const rate = splitTrafficValue(readFiniteSimulationNumber(simulation, "Rate"), weights)[index];
4547
+ return rate > 0 ? LoadStrikeSimulation.inject(rate, intervalSeconds, duringSeconds) : null;
4548
+ }
4549
+ if (kind === "RampingInject") {
4550
+ const rate = splitTrafficValue(readFiniteSimulationNumber(simulation, "Rate"), weights)[index];
4551
+ return rate > 0 ? LoadStrikeSimulation.rampingInject(rate, intervalSeconds, duringSeconds) : null;
4552
+ }
4553
+ if (kind === "InjectRandom") {
4554
+ const minRate = splitTrafficValue(readFiniteSimulationNumber(simulation, "MinRate"), weights)[index];
4555
+ const maxRate = splitTrafficValue(readFiniteSimulationNumber(simulation, "MaxRate"), weights)[index];
4556
+ if (maxRate <= 0) {
4557
+ return null;
4558
+ }
4559
+ return LoadStrikeSimulation.injectRandom(Math.min(minRate, maxRate), maxRate, intervalSeconds, duringSeconds);
4560
+ }
4561
+ if (kind === "IterationsForInject") {
4562
+ const rate = splitTrafficValue(readFiniteSimulationNumber(simulation, "Rate"), weights)[index];
4563
+ const iterations = splitTrafficValue(readFiniteSimulationNumber(simulation, "Iterations"), weights)[index];
4564
+ return rate > 0 && iterations > 0
4565
+ ? LoadStrikeSimulation.iterationsForInject(rate, intervalSeconds, iterations)
4566
+ : null;
4567
+ }
4568
+ if (kind === "IterationsForConstant") {
4569
+ const copies = splitTrafficValue(readFiniteSimulationNumber(simulation, "Copies"), weights)[index];
4570
+ const iterations = splitTrafficValue(readFiniteSimulationNumber(simulation, "Iterations"), weights)[index];
4571
+ return copies > 0 && iterations > 0
4572
+ ? LoadStrikeSimulation.iterationsForConstant(copies, iterations)
4573
+ : null;
4574
+ }
4575
+ if (kind === "KeepConstant") {
4576
+ const copies = splitTrafficValue(readFiniteSimulationNumber(simulation, "Copies"), weights)[index];
4577
+ return copies > 0 ? LoadStrikeSimulation.keepConstant(copies, duringSeconds) : null;
4578
+ }
4579
+ if (kind === "RampingConstant") {
4580
+ const copies = splitTrafficValue(readFiniteSimulationNumber(simulation, "Copies"), weights)[index];
4581
+ return copies > 0 ? LoadStrikeSimulation.rampingConstant(copies, duringSeconds) : null;
4582
+ }
4583
+ if (kind === "Pause") {
4584
+ return LoadStrikeSimulation.pause(duringSeconds);
4585
+ }
4586
+ return attachLoadSimulationProjection({ ...simulation });
4587
+ }
4588
+ function splitTrafficValue(totalValue, weights) {
4589
+ const normalizedTotal = Math.trunc(Number.isFinite(totalValue) ? totalValue : 0);
4590
+ if (normalizedTotal <= 0) {
4591
+ return weights.map(() => 0);
4592
+ }
4593
+ const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
4594
+ if (totalWeight <= 0) {
4595
+ throw new Error("Traffic mix scenario weights must add up to more than zero.");
4596
+ }
4597
+ const exactShares = weights.map((weight, index) => {
4598
+ const exact = (normalizedTotal * weight) / totalWeight;
4599
+ return {
4600
+ index,
4601
+ value: Math.floor(exact),
4602
+ remainder: exact - Math.floor(exact)
4603
+ };
4604
+ });
4605
+ const split = exactShares.map((share) => share.value);
4606
+ let remaining = normalizedTotal - split.reduce((sum, value) => sum + value, 0);
4607
+ for (const share of [...exactShares].sort((left, right) => {
4608
+ const remainderDelta = right.remainder - left.remainder;
4609
+ return remainderDelta !== 0 ? remainderDelta : left.index - right.index;
4610
+ })) {
4611
+ if (remaining <= 0) {
4612
+ break;
4613
+ }
4614
+ split[share.index] += 1;
4615
+ remaining -= 1;
4616
+ }
4617
+ return split;
4618
+ }
4619
+ function readFiniteSimulationNumber(simulation, key) {
4620
+ const value = Number(simulation[key]);
4621
+ return Number.isFinite(value) ? value : 0;
4622
+ }
4391
4623
  function attachScenarioStatsAliases(scenario) {
4392
4624
  const normalized = normalizeScenarioStatsValue(scenario);
4393
4625
  normalized.ok = attachMeasurementStatsAliases(normalized.ok);
@@ -4570,15 +4802,15 @@ function attachRunResultAliases(result) {
4570
4802
  defineAliasProperty(projected, "Duration", () => projected.durationMs);
4571
4803
  return projected;
4572
4804
  }
4573
- function resolveResponseCustomLatency(customLatencyMs, usesOverloadDefaults) {
4805
+ function resolveResponseCustomLatency(customLatencyMs, _usesOverloadDefaults) {
4574
4806
  if (!Number.isFinite(customLatencyMs)) {
4575
- return usesOverloadDefaults ? -1 : 0;
4807
+ return -1;
4576
4808
  }
4577
4809
  return Number(customLatencyMs);
4578
4810
  }
4579
4811
  function resolveRecordedLatency(customLatencyMs, observedLatencyMs) {
4580
4812
  if (!Number.isFinite(customLatencyMs)) {
4581
- return 0;
4813
+ return Math.max(observedLatencyMs, 0);
4582
4814
  }
4583
4815
  const resolved = Number(customLatencyMs);
4584
4816
  if (resolved >= 0) {
@@ -8346,6 +8578,7 @@ export const __loadstrikeTestExports = {
8346
8578
  detailedToNodeStats,
8347
8579
  evaluateThresholdsForScenarios,
8348
8580
  executeTrackedScenarioInvocation,
8581
+ expandTrafficMixScenarios,
8349
8582
  extractContextOverridesFromArgs,
8350
8583
  extractContextOverridesFromConfig,
8351
8584
  findCaseInsensitiveKey,
@@ -2,7 +2,7 @@ export type { LoadStrikeDateValue, LoadStrikeObject, LoadStrikeRunContext as Loa
2
2
  export { LoadStrikeAutopilot, LoadStrikeAutopilotResult } from "./autopilot.js";
3
3
  export type { LoadStrikeAutopilotEndpoint, LoadStrikeAutopilotEndpointBinding, LoadStrikeAutopilotLoadSimulationSuggestion, LoadStrikeAutopilotMessageSample, LoadStrikeAutopilotOptions, LoadStrikeAutopilotPlan, LoadStrikeAutopilotPreviewReport, LoadStrikeAutopilotReadinessFailure, LoadStrikeAutopilotRedaction, LoadStrikeAutopilotRequest, LoadStrikeAutopilotResultShape, LoadStrikeAutopilotScenarioPlan, LoadStrikeAutopilotSecretBinding, LoadStrikeAutopilotThresholdSuggestion, LoadStrikeAutopilotTrackingSelectorSuggestion } from "./autopilot-contracts.js";
4
4
  export { LoadStrikeAutopilotReadiness } from "./autopilot-contracts.js";
5
- export { CrossPlatformScenarioConfigurator, ScenarioTrackingExtensions, LoadStrikeContext, LoadStrikePluginData, LoadStrikePluginDataTable, LoadStrikeNodeType, LoadStrikeReportFormat, LoadStrikeResponse, LoadStrikeLogLevel, LoadStrikeScenarioOperation, LoadStrikeOperationType, LoadStrikeRunner, LoadStrikeScenario, LoadStrikeSimulation, CrossPlatformTrackingConfiguration, LoadStrikeMetric, LoadStrikeCounter, LoadStrikeGauge, LoadStrikeStep, LoadStrikeThreshold } from "./runtime.js";
5
+ export { CrossPlatformScenarioConfigurator, ScenarioTrackingExtensions, LoadStrikeContext, LoadStrikePluginData, LoadStrikePluginDataTable, LoadStrikeNodeType, LoadStrikeReportFormat, LoadStrikeResponse, LoadStrikeLogLevel, LoadStrikeScenarioOperation, LoadStrikeOperationType, LoadStrikeRunner, LoadStrikeScenario, LoadStrikeScenarioShare, LoadStrikeSimulation, LoadStrikeTrafficMix, CrossPlatformTrackingConfiguration, LoadStrikeMetric, LoadStrikeCounter, LoadStrikeGauge, LoadStrikeStep, LoadStrikeThreshold } from "./runtime.js";
6
6
  export type { ILoadStrikeReportingSink, ILoadStrikeWorkerPlugin, LoadStrikeRunResult, LoadStrikeReportingSink, LoadStrikeRuntimePolicy, LoadStrikeRunnerOptions, LoadStrikeRunContext as LoadStrikeRuntimeContext, LoadStrikeRunContext, LoadStrikeBaseContext, LoadStrikeCounterStats, LoadStrikeDataTransferStats, LoadStrikeGaugeStats, LoadStrikeLogger, LoadStrikeLoadSimulationStats, LoadStrikeMeasurementStats, LoadStrikeMetricStats, LoadStrikeNodeInfo, LoadStrikeMetricValue, LoadStrikeRandom, LoadStrikeReply, LoadStrikeLatencyCount, LoadStrikeLatencyStats, LoadStrikeLoadSimulation, LoadStrikeScenarioInfo, LoadStrikeScenarioInitContext, LoadStrikeScenarioPartition, LoadStrikeScenarioStartInfo, LoadStrikeScenarioContext, LoadStrikeScenarioStats, LoadStrikeTestInfo, LoadStrikeScenarioRuntime, LoadStrikeSessionStartInfo, LoadStrikeSinkSession, LoadStrikeSinkError, LoadStrikeStatusCodeStats, LoadStrikeThresholdResult, LoadStrikeThresholdPredicateContext, LoadStrikeStepReply, LoadStrikeStepStats, LoadStrikeStepRuntime, LoadStrikeThresholdOptions, LoadStrikeRequestStats, LoadStrikeWorkerPlugin } from "./runtime.js";
7
7
  export { CorrelationStoreConfiguration, CrossPlatformTrackingRuntime, InMemoryCorrelationStore, RedisCorrelationStoreOptions, RedisCorrelationStore, TrackingPayloadBuilder, TrackingFieldSelector } from "./correlation.js";
8
8
  export type { CorrelationEntry, DestinationConsumeResult, GatheredRow, CorrelationStoreKind, CorrelationRuntimeOptions, CorrelationRuntimePlugin, CorrelationRuntimeStats, CorrelationStore, SourceProduceResult, TrackingFieldLocation, TrackingPayload } from "./correlation.js";
@@ -21,9 +21,8 @@ export interface LicenseValidationSession {
21
21
  heartbeatTimer?: ReturnType<typeof setInterval>;
22
22
  }
23
23
  export declare class LoadStrikeLocalClient {
24
- private readonly licensingApiBaseUrl;
24
+ #private;
25
25
  private readonly licenseValidationTimeoutMs;
26
- private readonly signingKeyCache;
27
26
  /**
28
27
  * Exposes the public constructor operation.
29
28
  * Use this when the surrounding wrapper type makes this operation the clearest way to express your intent.
@@ -1674,6 +1674,61 @@ export declare class LoadStrikeScenario {
1674
1674
  */
1675
1675
  WithWeight(weight: number): LoadStrikeScenario;
1676
1676
  }
1677
+ export declare class LoadStrikeScenarioShare {
1678
+ readonly scenario: LoadStrikeScenario;
1679
+ readonly weight: number;
1680
+ private constructor();
1681
+ /**
1682
+ * Creates a weighted scenario lane for a traffic mix.
1683
+ * Use this when a total workload should be distributed across several scenarios.
1684
+ */
1685
+ static create(scenario: LoadStrikeScenario, weight: number): LoadStrikeScenarioShare;
1686
+ /**
1687
+ * Creates a weighted scenario lane for a traffic mix.
1688
+ * Use this when a total workload should be distributed across several scenarios.
1689
+ */
1690
+ static Create(scenario: LoadStrikeScenario, weight: number): LoadStrikeScenarioShare;
1691
+ }
1692
+ export declare class LoadStrikeTrafficMix {
1693
+ readonly name: string;
1694
+ private readonly totalLoad;
1695
+ private readonly scenarioMix;
1696
+ private constructor();
1697
+ /**
1698
+ * Creates a traffic mix definition.
1699
+ * Use this when one total load profile should be split across multiple scenario lanes.
1700
+ */
1701
+ static create(name: string): LoadStrikeTrafficMix;
1702
+ /**
1703
+ * Creates a traffic mix definition.
1704
+ * Use this when one total load profile should be split across multiple scenario lanes.
1705
+ */
1706
+ static Create(name: string): LoadStrikeTrafficMix;
1707
+ /**
1708
+ * Sets the total workload shape for the mix.
1709
+ * Use this when the same high-level load profile should be distributed by scenario weights.
1710
+ */
1711
+ withTotalLoad(...totalLoad: Array<Record<string, unknown>>): LoadStrikeTrafficMix;
1712
+ /**
1713
+ * Sets the total workload shape for the mix.
1714
+ * Use this when the same high-level load profile should be distributed by scenario weights.
1715
+ */
1716
+ WithTotalLoad(...totalLoad: Array<Record<string, unknown>>): LoadStrikeTrafficMix;
1717
+ /**
1718
+ * Sets the weighted scenario lanes for the mix.
1719
+ * Use this when each operation should receive a fixed proportion of the total workload.
1720
+ */
1721
+ withScenarioMix(...scenarioMix: LoadStrikeScenarioShare[]): LoadStrikeTrafficMix;
1722
+ /**
1723
+ * Sets the weighted scenario lanes for the mix.
1724
+ * Use this when each operation should receive a fixed proportion of the total workload.
1725
+ */
1726
+ WithScenarioMix(...scenarioMix: LoadStrikeScenarioShare[]): LoadStrikeTrafficMix;
1727
+ expandScenarios(): LoadStrikeScenario[];
1728
+ ExpandScenarios(): LoadStrikeScenario[];
1729
+ __loadStrikeTotalLoad(): Array<Record<string, unknown>>;
1730
+ __loadStrikeScenarioMix(): LoadStrikeScenarioShare[];
1731
+ }
1677
1732
  export declare class LoadStrikeRunner {
1678
1733
  private scenarios;
1679
1734
  private options;
@@ -1699,6 +1754,16 @@ export declare class LoadStrikeRunner {
1699
1754
  * Use this when you want a context immediately without composing a runner first.
1700
1755
  */
1701
1756
  static RegisterScenarios(...scenarios: LoadStrikeScenario[]): LoadStrikeContext;
1757
+ /**
1758
+ * Registers a traffic mix on a fresh runnable context.
1759
+ * Use this when one total load profile should be split across weighted scenario lanes.
1760
+ */
1761
+ static registerTrafficMix(trafficMix: LoadStrikeTrafficMix): LoadStrikeContext;
1762
+ /**
1763
+ * Registers a traffic mix on a fresh runnable context.
1764
+ * Use this when one total load profile should be split across weighted scenario lanes.
1765
+ */
1766
+ static RegisterTrafficMix(trafficMix: LoadStrikeTrafficMix): LoadStrikeContext;
1702
1767
  /**
1703
1768
  * Toggles realtime console metric output.
1704
1769
  * Use this when a local run or CI log should stream live throughput and latency updates.
@@ -1868,6 +1933,16 @@ export declare class LoadStrikeRunner {
1868
1933
  * Use this when the run should execute a batch of scenario definitions together.
1869
1934
  */
1870
1935
  AddScenarios(...scenarios: LoadStrikeScenario[]): LoadStrikeRunner;
1936
+ /**
1937
+ * Adds a traffic mix to the current runner.
1938
+ * Use this when one total load profile should be split across weighted scenario lanes.
1939
+ */
1940
+ addTrafficMix(trafficMix: LoadStrikeTrafficMix): LoadStrikeRunner;
1941
+ /**
1942
+ * Adds a traffic mix to the current runner.
1943
+ * Use this when one total load profile should be split across weighted scenario lanes.
1944
+ */
1945
+ AddTrafficMix(trafficMix: LoadStrikeTrafficMix): LoadStrikeRunner;
1871
1946
  /**
1872
1947
  * Applies a grouped configuration change to the current builder or context.
1873
1948
  * Use this when several related settings should be supplied in one step.
@@ -2010,6 +2085,7 @@ declare function combineAbortSignals(...signals: AbortSignal[]): AbortSignal;
2010
2085
  declare function delayWithAbort(durationMs: number, signal: AbortSignal): Promise<void>;
2011
2086
  declare function createRuntimeRandom(): LoadStrikeRandom;
2012
2087
  declare function parseAliasDate(value: string | Date | null | undefined): Date | undefined;
2088
+ declare function expandTrafficMixScenarios(trafficMix: LoadStrikeTrafficMix): LoadStrikeScenario[];
2013
2089
  declare function attachScenarioStatsAliases(scenario: Omit<LoadStrikeScenarioStats, "FindStepStats" | "GetStepStats"> & Partial<Pick<LoadStrikeScenarioStats, "FindStepStats" | "GetStepStats">>): LoadStrikeScenarioStats;
2014
2090
  declare function attachNodeStatsAliases(stats: Omit<LoadStrikeNodeStats, "FindScenarioStats" | "GetScenarioStats"> & Partial<Pick<LoadStrikeNodeStats, "FindScenarioStats" | "GetScenarioStats">>): LoadStrikeNodeStats;
2015
2091
  declare function attachRunResultAliases(result: LoadStrikeRunResult): LoadStrikeRunResult;
@@ -2255,6 +2331,7 @@ export declare const __loadstrikeTestExports: {
2255
2331
  detailedToNodeStats: typeof detailedToNodeStats;
2256
2332
  evaluateThresholdsForScenarios: typeof evaluateThresholdsForScenarios;
2257
2333
  executeTrackedScenarioInvocation: typeof executeTrackedScenarioInvocation;
2334
+ expandTrafficMixScenarios: typeof expandTrafficMixScenarios;
2258
2335
  extractContextOverridesFromArgs: typeof extractContextOverridesFromArgs;
2259
2336
  extractContextOverridesFromConfig: typeof extractContextOverridesFromConfig;
2260
2337
  findCaseInsensitiveKey: typeof findCaseInsensitiveKey;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loadstrike/loadstrike-sdk",
3
- "version": "1.0.23601",
3
+ "version": "1.0.26901",
4
4
  "description": "TypeScript and JavaScript SDK for in-process load execution, traffic correlation, and reporting.",
5
5
  "keywords": [
6
6
  "load-testing",
@@ -82,6 +82,7 @@
82
82
  "typescript": "^5.9.2"
83
83
  },
84
84
  "overrides": {
85
- "brace-expansion": "5.0.5"
85
+ "brace-expansion": "5.0.6",
86
+ "tar": "7.5.16"
86
87
  }
87
88
  }