@loadstrike/loadstrike-sdk 1.0.26701 → 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 +7 -0
- package/dist/cjs/index.js +4 -2
- package/dist/cjs/local.js +59 -9
- package/dist/cjs/runtime.js +236 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/local.js +59 -9
- package/dist/esm/runtime.js +233 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/local.d.ts +1 -2
- package/dist/types/runtime.d.ts +77 -0
- package/package.json +3 -2
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.
|
|
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
|
|
107
|
+
_LoadStrikeLocalClient_licensingApiBaseUrl.set(this, void 0);
|
|
108
|
+
_LoadStrikeLocalClient_signingKeyCache.set(this, new Map());
|
|
96
109
|
assertNoDisableLicenseEnforcementOption(options, "LoadStrikeLocalClient");
|
|
97
|
-
this
|
|
110
|
+
__classPrivateFieldSet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, resolveLicensingApiBaseUrl(), "f");
|
|
98
111
|
this.licenseValidationTimeoutMs = normalizeTimeoutMs(options.licenseValidationTimeoutMs);
|
|
99
112
|
}
|
|
100
113
|
portalReportingIngestUrl() {
|
|
101
|
-
return `${this.
|
|
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.
|
|
371
|
-
const cached = this.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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) {
|
package/dist/cjs/runtime.js
CHANGED
|
@@ -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.
|
|
@@ -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);
|
|
@@ -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
|
|
68
|
+
_LoadStrikeLocalClient_licensingApiBaseUrl.set(this, void 0);
|
|
69
|
+
_LoadStrikeLocalClient_signingKeyCache.set(this, new Map());
|
|
57
70
|
assertNoDisableLicenseEnforcementOption(options, "LoadStrikeLocalClient");
|
|
58
|
-
this
|
|
71
|
+
__classPrivateFieldSet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, resolveLicensingApiBaseUrl(), "f");
|
|
59
72
|
this.licenseValidationTimeoutMs = normalizeTimeoutMs(options.licenseValidationTimeoutMs);
|
|
60
73
|
}
|
|
61
74
|
portalReportingIngestUrl() {
|
|
62
|
-
return `${this.
|
|
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.
|
|
332
|
-
const cached = this.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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) {
|
package/dist/esm/runtime.js
CHANGED
|
@@ -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.
|
|
@@ -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);
|
|
@@ -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,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/types/local.d.ts
CHANGED
|
@@ -21,9 +21,8 @@ export interface LicenseValidationSession {
|
|
|
21
21
|
heartbeatTimer?: ReturnType<typeof setInterval>;
|
|
22
22
|
}
|
|
23
23
|
export declare class LoadStrikeLocalClient {
|
|
24
|
-
private
|
|
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.
|
package/dist/types/runtime.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
85
|
+
"brace-expansion": "5.0.6",
|
|
86
|
+
"tar": "7.5.16"
|
|
86
87
|
}
|
|
87
88
|
}
|