@loadstrike/loadstrike-sdk 1.0.10101 → 1.0.10801

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
@@ -1,6 +1,6 @@
1
1
  # LoadStrike for TypeScript and JavaScript
2
2
 
3
- LoadStrike is a TypeScript and JavaScript SDK for defining and running load, traffic-correlation, and reporting scenarios directly inside your application or test process.
3
+ LoadStrike for TypeScript and JavaScript lets you define, run, and report load scenarios directly from your application or test suite.
4
4
 
5
5
  ## Requirements
6
6
 
@@ -12,12 +12,12 @@ LoadStrike is a TypeScript and JavaScript SDK for defining and running load, tra
12
12
  npm install @loadstrike/loadstrike-sdk
13
13
  ```
14
14
 
15
- ## What It Provides
15
+ ## What You Can Do
16
16
 
17
- - Scenario, step, load-simulation, threshold, and metric primitives
18
- - Native in-process execution with structured run results
19
- - Local and distributed cluster execution helpers
20
- - HTML, TXT, CSV, and Markdown report generation
17
+ - Define scenarios, steps, load simulations, thresholds, and metrics in code
18
+ - Run workloads in-process and collect structured results
19
+ - Generate HTML, Markdown, TXT, and CSV reports
20
+ - Extend runs with correlation, distributed execution, and reporting sinks when needed
21
21
 
22
22
  ## Supported Transports
23
23
 
@@ -66,8 +66,8 @@ const result = await LoadStrikeRunner
66
66
  .run();
67
67
  ```
68
68
 
69
- A valid `RunnerKey` is required to execute live workloads.
69
+ Use a runner key from your LoadStrike portal account when executing live workloads.
70
70
 
71
- ## Documentation
71
+ ## Documentation And Examples
72
72
 
73
73
  https://loadstrike.com/documentation
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DistributedClusterAgent = exports.DistributedClusterCoordinator = exports.LocalClusterCoordinator = void 0;
3
+ exports.__loadstrikeTestExports = exports.DistributedClusterAgent = exports.DistributedClusterCoordinator = exports.LocalClusterCoordinator = void 0;
4
4
  exports.planAgentScenarioAssignments = planAgentScenarioAssignments;
5
5
  const node_crypto_1 = require("node:crypto");
6
6
  const transports_js_1 = require("./transports.js");
@@ -408,3 +408,20 @@ function booleanOrDefault(value, fallback) {
408
408
  async function sleep(ms) {
409
409
  await new Promise((resolve) => setTimeout(resolve, ms));
410
410
  }
411
+ exports.__loadstrikeTestExports = {
412
+ DistributedClusterAgent,
413
+ DistributedClusterCoordinator,
414
+ LocalClusterCoordinator,
415
+ arrayOrUndefined,
416
+ booleanOrDefault,
417
+ buildWeightedCycle,
418
+ convertRunResult,
419
+ numberOrDefault,
420
+ parseRunCommand,
421
+ parseRunResult,
422
+ planAgentScenarioAssignments,
423
+ recordOrUndefined,
424
+ sanitizeToken,
425
+ sleep,
426
+ stringOrDefault
427
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CrossPlatformTrackingRuntime = exports.TrackingFieldSelector = exports.RedisCorrelationStore = exports.InMemoryCorrelationStore = exports.CorrelationStoreConfiguration = exports.RedisCorrelationStoreOptions = exports.TrackingPayloadBuilder = void 0;
3
+ exports.__loadstrikeTestExports = exports.CrossPlatformTrackingRuntime = exports.TrackingFieldSelector = exports.RedisCorrelationStore = exports.InMemoryCorrelationStore = exports.CorrelationStoreConfiguration = exports.RedisCorrelationStoreOptions = exports.TrackingPayloadBuilder = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const redis_1 = require("redis");
6
6
  class TrackingPayloadBuilder {
@@ -559,11 +559,10 @@ class TrackingFieldSelector {
559
559
  }
560
560
  extract(payload) {
561
561
  if (this.kind === "header") {
562
- const headerName = this.path.toLowerCase();
563
562
  const headers = payload.headers ?? {};
564
563
  for (const [key, value] of Object.entries(headers)) {
565
564
  const parsed = String(value ?? "").trim();
566
- if (key.toLowerCase() === headerName && parsed) {
565
+ if (key === this.path && parsed) {
567
566
  return parsed;
568
567
  }
569
568
  }
@@ -604,6 +603,8 @@ function normalizeTrackingFieldLocation(location) {
604
603
  }
605
604
  class CrossPlatformTrackingRuntime {
606
605
  constructor(options) {
606
+ this.trackingIdDisplayByEventId = new Map();
607
+ this.gatherByDisplayByComparisonKey = new Map();
607
608
  this.producedCount = 0;
608
609
  this.consumedCount = 0;
609
610
  this.matchedCount = 0;
@@ -617,6 +618,8 @@ class CrossPlatformTrackingRuntime {
617
618
  this.gatherSelector = options.destinationGatherByField
618
619
  ? TrackingFieldSelector.parse(options.destinationGatherByField)
619
620
  : null;
621
+ this.trackingFieldValueCaseSensitive = options.trackingFieldValueCaseSensitive ?? true;
622
+ this.gatherByFieldValueCaseSensitive = options.gatherByFieldValueCaseSensitive ?? true;
620
623
  this.timeoutMs = Math.max(options.correlationTimeoutMs ?? 30000, 1);
621
624
  this.timeoutCountsAsFailure = options.timeoutCountsAsFailure ?? true;
622
625
  this.store = options.store ?? new InMemoryCorrelationStore();
@@ -639,13 +642,17 @@ class CrossPlatformTrackingRuntime {
639
642
  return result;
640
643
  }
641
644
  result.trackingId = trackingId;
642
- const sourceOccurrences = await this.store.incrementSourceOccurrences(trackingId);
645
+ const comparisonTrackingId = normalizeComparisonValue(trackingId, this.trackingFieldValueCaseSensitive);
646
+ const sourceOccurrences = await this.store.incrementSourceOccurrences(comparisonTrackingId);
643
647
  if (sourceOccurrences > 1) {
644
648
  this.duplicateSourceCount += 1;
645
649
  result.duplicateSource = true;
646
650
  }
647
- const registration = await this.store.registerSource(trackingId, nowMs);
651
+ const registration = await this.store.registerSource(comparisonTrackingId, nowMs);
648
652
  result.eventId = registration.eventId ?? "";
653
+ if (registration.eventId) {
654
+ this.trackingIdDisplayByEventId.set(registration.eventId, trackingId);
655
+ }
649
656
  if (registration.eventId) {
650
657
  await this.store.tryExpire(registration.eventId);
651
658
  }
@@ -686,12 +693,13 @@ class CrossPlatformTrackingRuntime {
686
693
  return result;
687
694
  }
688
695
  result.trackingId = trackingId;
689
- const destinationOccurrences = await this.store.incrementDestinationOccurrences(trackingId);
696
+ const comparisonTrackingId = normalizeComparisonValue(trackingId, this.trackingFieldValueCaseSensitive);
697
+ const destinationOccurrences = await this.store.incrementDestinationOccurrences(comparisonTrackingId);
690
698
  if (destinationOccurrences > 1) {
691
699
  this.duplicateDestinationCount += 1;
692
700
  result.duplicateDestination = true;
693
701
  }
694
- const source = await this.store.tryMatchDestination(trackingId, nowMs);
702
+ const source = await this.store.tryMatchDestination(comparisonTrackingId, nowMs);
695
703
  if (!source) {
696
704
  result.message = "Destination tracking id had no source match.";
697
705
  return result;
@@ -699,10 +707,12 @@ class CrossPlatformTrackingRuntime {
699
707
  this.matchedCount += 1;
700
708
  const latencyMs = Math.max(nowMs - (source.producedUtc ?? source.sourceTimestampUtc ?? nowMs), 0);
701
709
  this.totalLatencyMs += latencyMs;
710
+ const displayTrackingId = this.resolveTrackingIdDisplay(source.eventId, source.trackingId, true);
702
711
  const gatherKey = this.resolveGatherKey(payload);
703
712
  result.eventId = source.eventId ?? "";
704
713
  result.sourceTimestampUtc = source.sourceTimestampUtc ?? source.producedUtc ?? 0;
705
714
  result.destinationTimestampUtc = nowMs;
715
+ result.trackingId = displayTrackingId;
706
716
  result.gatherKey = gatherKey;
707
717
  result.matched = true;
708
718
  result.latencyMs = latencyMs;
@@ -712,7 +722,7 @@ class CrossPlatformTrackingRuntime {
712
722
  this.gathered.set(gatherKey, row);
713
723
  for (const plugin of this.plugins) {
714
724
  if (plugin.onMatched) {
715
- await plugin.onMatched(trackingId, source.payload, payload, latencyMs);
725
+ await plugin.onMatched(displayTrackingId, source.payload, payload, latencyMs, gatherKey);
716
726
  }
717
727
  }
718
728
  return result;
@@ -760,7 +770,9 @@ class CrossPlatformTrackingRuntime {
760
770
  expiredEntries.push(entry);
761
771
  }
762
772
  for (const entry of expiredEntries) {
773
+ const displayTrackingId = this.resolveTrackingIdDisplay(entry.eventId, entry.trackingId, true);
763
774
  this.timeoutCount += 1;
775
+ entry.trackingId = displayTrackingId;
764
776
  const gatherKey = this.resolveGatherKey(entry.payload);
765
777
  const row = this.gathered.get(gatherKey) ?? { total: 0, matched: 0, timedOut: 0 };
766
778
  row.total += 1;
@@ -768,7 +780,7 @@ class CrossPlatformTrackingRuntime {
768
780
  this.gathered.set(gatherKey, row);
769
781
  for (const plugin of this.plugins) {
770
782
  if (plugin.onTimeout) {
771
- await plugin.onTimeout(entry.trackingId, entry.payload);
783
+ await plugin.onTimeout(displayTrackingId, entry.payload);
772
784
  }
773
785
  }
774
786
  }
@@ -792,13 +804,42 @@ class CrossPlatformTrackingRuntime {
792
804
  if (!this.gatherSelector) {
793
805
  return "__ungrouped__";
794
806
  }
795
- return this.gatherSelector.extract(payload) || "__unknown__";
807
+ const gatherKey = this.gatherSelector.extract(payload);
808
+ if (!gatherKey) {
809
+ return "__unknown__";
810
+ }
811
+ if (this.gatherByFieldValueCaseSensitive) {
812
+ return gatherKey;
813
+ }
814
+ const comparisonKey = normalizeComparisonValue(gatherKey, false);
815
+ const existing = this.gatherByDisplayByComparisonKey.get(comparisonKey);
816
+ if (existing) {
817
+ return existing;
818
+ }
819
+ this.gatherByDisplayByComparisonKey.set(comparisonKey, gatherKey);
820
+ return gatherKey;
796
821
  }
797
822
  isTimeoutCountedAsFailure() {
798
823
  return this.timeoutCountsAsFailure;
799
824
  }
825
+ resolveTrackingIdDisplay(eventId, fallbackTrackingId, remove) {
826
+ if (!eventId?.trim()) {
827
+ return fallbackTrackingId;
828
+ }
829
+ if (remove) {
830
+ const displayTrackingId = this.trackingIdDisplayByEventId.get(eventId);
831
+ this.trackingIdDisplayByEventId.delete(eventId);
832
+ return displayTrackingId ?? fallbackTrackingId;
833
+ }
834
+ return this.trackingIdDisplayByEventId.get(eventId) ?? fallbackTrackingId;
835
+ }
800
836
  }
801
837
  exports.CrossPlatformTrackingRuntime = CrossPlatformTrackingRuntime;
838
+ function normalizeComparisonValue(value, caseSensitive) {
839
+ return caseSensitive
840
+ ? value
841
+ : value.toUpperCase();
842
+ }
802
843
  function normalizeJsonBody(body) {
803
844
  if (body instanceof Uint8Array) {
804
845
  return normalizeJsonBody(trackingBodyToUtf8(body));
@@ -917,7 +958,9 @@ function uniqueStringList(values) {
917
958
  return [...new Set(values.filter((value) => value.trim().length > 0))];
918
959
  }
919
960
  function createRedisStoreClient(connectionString, database) {
920
- const client = (0, redis_1.createClient)({
961
+ const createClientOverride = globalThis
962
+ .__wave15CreateRedisClient;
963
+ const client = (typeof createClientOverride === "function" ? createClientOverride : redis_1.createClient)({
921
964
  url: connectionString,
922
965
  database: database >= 0 ? database : undefined
923
966
  });
@@ -1007,3 +1050,18 @@ function readJsonPathSegment(current, segment) {
1007
1050
  }
1008
1051
  return undefined;
1009
1052
  }
1053
+ exports.__loadstrikeTestExports = {
1054
+ CorrelationStoreConfiguration,
1055
+ InMemoryCorrelationStore,
1056
+ RedisCorrelationStore,
1057
+ RedisCorrelationStoreOptions,
1058
+ TrackingFieldSelector,
1059
+ createRedisStoreClient,
1060
+ hydrateCorrelationEntry,
1061
+ normalizeCorrelationEntry,
1062
+ parseStringList,
1063
+ readJsonPathSegment,
1064
+ sanitizeLooseJson,
1065
+ tryParseObject,
1066
+ uniqueStringList
1067
+ };
package/dist/cjs/index.js CHANGED
@@ -1,17 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DatadogReportingSinkOptions = exports.DatadogReportingSink = exports.ConsoleReportingSink = exports.CompositeReportingSink = exports.planAgentScenarioAssignments = exports.LocalClusterCoordinator = exports.DistributedClusterCoordinator = exports.DistributedClusterAgent = exports.HttpAuthOptions = exports.HttpOAuth2ClientCredentialsOptions = exports.PushDiffusionEndpointDefinition = exports.DelegateStreamEndpointDefinition = exports.AzureEventHubsEndpointDefinition = exports.RedisStreamsEndpointDefinition = exports.NatsEndpointDefinition = exports.RabbitMqEndpointDefinition = exports.KafkaSaslOptions = exports.KafkaEndpointDefinition = exports.HttpEndpointDefinition = exports.TrafficEndpointDefinition = exports.EndpointAdapterFactory = 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.LoadStrikeSimulation = exports.LoadStrikeScenario = exports.LoadStrikeRunner = exports.LoadStrikeOperationType = exports.LoadStrikeScenarioOperation = exports.LoadStrikeLogLevel = exports.LoadStrikeResponse = exports.LoadStrikeReportFormat = exports.LoadStrikeNodeType = exports.LoadStrikeReportData = exports.LoadStrikePluginDataTable = exports.LoadStrikePluginData = exports.LoadStrikeContext = exports.ScenarioTrackingExtensions = exports.CrossPlatformScenarioConfigurator = exports.LoadStrikeLocalClient = exports.DEFAULT_LICENSING_API_BASE_URL = void 0;
4
- exports.TimescaleDbReportingSinkOptions = exports.TimescaleDbReportingSink = exports.SplunkReportingSinkOptions = exports.SplunkReportingSink = exports.OtelCollectorReportingSinkOptions = exports.OtelCollectorReportingSink = exports.MemoryReportingSink = exports.InfluxDbReportingSinkOptions = exports.InfluxDbReportingSink = exports.GrafanaLokiReportingSinkOptions = exports.GrafanaLokiReportingSink = void 0;
5
- var local_js_1 = require("./local.js");
6
- Object.defineProperty(exports, "DEFAULT_LICENSING_API_BASE_URL", { enumerable: true, get: function () { return local_js_1.DEFAULT_LICENSING_API_BASE_URL; } });
7
- Object.defineProperty(exports, "LoadStrikeLocalClient", { enumerable: true, get: function () { return local_js_1.LoadStrikeLocalClient; } });
3
+ exports.TimescaleDbReportingSink = exports.SplunkReportingSinkOptions = exports.SplunkReportingSink = exports.OtelCollectorReportingSinkOptions = exports.OtelCollectorReportingSink = exports.InfluxDbReportingSinkOptions = exports.InfluxDbReportingSink = exports.GrafanaLokiReportingSinkOptions = exports.GrafanaLokiReportingSink = exports.DatadogReportingSinkOptions = exports.DatadogReportingSink = exports.HttpAuthOptions = exports.HttpOAuth2ClientCredentialsOptions = exports.PushDiffusionEndpointDefinition = exports.DelegateStreamEndpointDefinition = exports.AzureEventHubsEndpointDefinition = exports.RedisStreamsEndpointDefinition = exports.NatsEndpointDefinition = exports.RabbitMqEndpointDefinition = exports.KafkaSaslOptions = exports.KafkaEndpointDefinition = exports.HttpEndpointDefinition = exports.TrafficEndpointDefinition = exports.EndpointAdapterFactory = 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.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 = void 0;
4
+ exports.TimescaleDbReportingSinkOptions = void 0;
8
5
  var runtime_js_1 = require("./runtime.js");
9
6
  Object.defineProperty(exports, "CrossPlatformScenarioConfigurator", { enumerable: true, get: function () { return runtime_js_1.CrossPlatformScenarioConfigurator; } });
10
7
  Object.defineProperty(exports, "ScenarioTrackingExtensions", { enumerable: true, get: function () { return runtime_js_1.ScenarioTrackingExtensions; } });
11
8
  Object.defineProperty(exports, "LoadStrikeContext", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeContext; } });
12
9
  Object.defineProperty(exports, "LoadStrikePluginData", { enumerable: true, get: function () { return runtime_js_1.LoadStrikePluginData; } });
13
10
  Object.defineProperty(exports, "LoadStrikePluginDataTable", { enumerable: true, get: function () { return runtime_js_1.LoadStrikePluginDataTable; } });
14
- Object.defineProperty(exports, "LoadStrikeReportData", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeReportData; } });
15
11
  Object.defineProperty(exports, "LoadStrikeNodeType", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeNodeType; } });
16
12
  Object.defineProperty(exports, "LoadStrikeReportFormat", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeReportFormat; } });
17
13
  Object.defineProperty(exports, "LoadStrikeResponse", { enumerable: true, get: function () { return runtime_js_1.LoadStrikeResponse; } });
@@ -48,21 +44,13 @@ Object.defineProperty(exports, "DelegateStreamEndpointDefinition", { enumerable:
48
44
  Object.defineProperty(exports, "PushDiffusionEndpointDefinition", { enumerable: true, get: function () { return transports_js_1.PushDiffusionEndpointDefinition; } });
49
45
  Object.defineProperty(exports, "HttpOAuth2ClientCredentialsOptions", { enumerable: true, get: function () { return transports_js_1.HttpOAuth2ClientCredentialsOptions; } });
50
46
  Object.defineProperty(exports, "HttpAuthOptions", { enumerable: true, get: function () { return transports_js_1.HttpAuthOptions; } });
51
- var cluster_js_1 = require("./cluster.js");
52
- Object.defineProperty(exports, "DistributedClusterAgent", { enumerable: true, get: function () { return cluster_js_1.DistributedClusterAgent; } });
53
- Object.defineProperty(exports, "DistributedClusterCoordinator", { enumerable: true, get: function () { return cluster_js_1.DistributedClusterCoordinator; } });
54
- Object.defineProperty(exports, "LocalClusterCoordinator", { enumerable: true, get: function () { return cluster_js_1.LocalClusterCoordinator; } });
55
- Object.defineProperty(exports, "planAgentScenarioAssignments", { enumerable: true, get: function () { return cluster_js_1.planAgentScenarioAssignments; } });
56
47
  var sinks_js_1 = require("./sinks.js");
57
- Object.defineProperty(exports, "CompositeReportingSink", { enumerable: true, get: function () { return sinks_js_1.CompositeReportingSink; } });
58
- Object.defineProperty(exports, "ConsoleReportingSink", { enumerable: true, get: function () { return sinks_js_1.ConsoleReportingSink; } });
59
48
  Object.defineProperty(exports, "DatadogReportingSink", { enumerable: true, get: function () { return sinks_js_1.DatadogReportingSink; } });
60
49
  Object.defineProperty(exports, "DatadogReportingSinkOptions", { enumerable: true, get: function () { return sinks_js_1.DatadogReportingSinkOptions; } });
61
50
  Object.defineProperty(exports, "GrafanaLokiReportingSink", { enumerable: true, get: function () { return sinks_js_1.GrafanaLokiReportingSink; } });
62
51
  Object.defineProperty(exports, "GrafanaLokiReportingSinkOptions", { enumerable: true, get: function () { return sinks_js_1.GrafanaLokiReportingSinkOptions; } });
63
52
  Object.defineProperty(exports, "InfluxDbReportingSink", { enumerable: true, get: function () { return sinks_js_1.InfluxDbReportingSink; } });
64
53
  Object.defineProperty(exports, "InfluxDbReportingSinkOptions", { enumerable: true, get: function () { return sinks_js_1.InfluxDbReportingSinkOptions; } });
65
- Object.defineProperty(exports, "MemoryReportingSink", { enumerable: true, get: function () { return sinks_js_1.MemoryReportingSink; } });
66
54
  Object.defineProperty(exports, "OtelCollectorReportingSink", { enumerable: true, get: function () { return sinks_js_1.OtelCollectorReportingSink; } });
67
55
  Object.defineProperty(exports, "OtelCollectorReportingSinkOptions", { enumerable: true, get: function () { return sinks_js_1.OtelCollectorReportingSinkOptions; } });
68
56
  Object.defineProperty(exports, "SplunkReportingSink", { enumerable: true, get: function () { return sinks_js_1.SplunkReportingSink; } });
package/dist/cjs/local.js CHANGED
@@ -36,14 +36,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.LoadStrikeLocalClient = exports.DEFAULT_LICENSING_API_BASE_URL = void 0;
39
+ exports.__loadstrikeTestExports = exports.LoadStrikeLocalClient = void 0;
40
40
  const node_os_1 = __importDefault(require("node:os"));
41
41
  const fs = __importStar(require("node:fs"));
42
42
  const childProcess = __importStar(require("node:child_process"));
43
43
  const node_crypto_1 = require("node:crypto");
44
44
  const correlation_js_1 = require("./correlation.js");
45
45
  const transports_js_1 = require("./transports.js");
46
- exports.DEFAULT_LICENSING_API_BASE_URL = "https://licensing.loadstrike.com";
46
+ const DEFAULT_LICENSING_API_BASE_URL = "https://licensing.loadstrike.com";
47
+ let developmentLicensingApiBaseUrlOverride;
47
48
  const BUILT_IN_WORKER_PLUGIN_NAMES = new Set([
48
49
  "loadstrike failed responses",
49
50
  "loadstrike correlation"
@@ -87,7 +88,7 @@ class LoadStrikeLocalClient {
87
88
  constructor(options = {}) {
88
89
  this.signingKeyCache = new Map();
89
90
  assertNoDisableLicenseEnforcementOption(options, "LoadStrikeLocalClient");
90
- this.licensingApiBaseUrl = normalizeLicensingApiBaseUrl(options.licensingApiBaseUrl);
91
+ this.licensingApiBaseUrl = resolveLicensingApiBaseUrl();
91
92
  this.licenseValidationTimeoutMs = normalizeTimeoutMs(options.licenseValidationTimeoutMs);
92
93
  }
93
94
  async run(request) {
@@ -257,7 +258,9 @@ class LoadStrikeLocalClient {
257
258
  }
258
259
  async getSigningKey(keyId, algorithm) {
259
260
  const nowMs = Date.now();
260
- const cacheKey = this.licensingApiBaseUrl.toLowerCase();
261
+ const normalizedKeyId = stringOrDefault(keyId, "default").trim() || "default";
262
+ const normalizedAlgorithm = stringOrDefault(algorithm, "RS256").toUpperCase();
263
+ const cacheKey = `${this.licensingApiBaseUrl.toLowerCase()}|${normalizedAlgorithm}:${normalizedKeyId}`;
261
264
  const cached = this.signingKeyCache.get(cacheKey);
262
265
  if (cached && cached.expiresAtUtc > nowMs) {
263
266
  return cached;
@@ -265,17 +268,21 @@ class LoadStrikeLocalClient {
265
268
  const controller = new AbortController();
266
269
  const timer = setTimeout(() => controller.abort(), this.licenseValidationTimeoutMs);
267
270
  try {
268
- const { response, json } = await this.getLicensingRequest("/api/v1/licenses/signing-public-key", controller.signal);
271
+ const { response, json } = await this.getLicensingRequest(`/api/v1/licenses/signing-public-key?kid=${encodeURIComponent(normalizedKeyId)}`, controller.signal);
269
272
  if (!response.ok) {
270
273
  throw new Error(`Failed to resolve licensing signing key. status=${response.status}`);
271
274
  }
272
- const resolvedAlgorithm = stringOrDefault(pickValue(json, "Algorithm", "alg"), algorithm).toUpperCase();
275
+ const resolvedAlgorithm = stringOrDefault(pickValue(json, "Algorithm", "alg"), normalizedAlgorithm).toUpperCase();
273
276
  const publicKeyPem = stringOrDefault(pickValue(json, "PublicKeyPem", "PublicKey", "Pem"), "");
274
277
  if (!publicKeyPem.trim()) {
275
278
  throw new Error("Runner key validation failed: signing key response is empty.");
276
279
  }
280
+ const resolvedKeyId = stringOrDefault(pickValue(json, "KeyId", "Kid", "kid"), normalizedKeyId).trim() || normalizedKeyId;
281
+ if (resolvedKeyId !== normalizedKeyId) {
282
+ throw new Error("Runner key validation failed: signing key response key id does not match requested key id.");
283
+ }
277
284
  const keyRecord = {
278
- keyId: stringOrDefault(pickValue(json, "KeyId", "Kid", "kid"), keyId),
285
+ keyId: resolvedKeyId,
279
286
  algorithm: resolvedAlgorithm,
280
287
  issuer: stringOrDefault(pickValue(json, "Issuer", "issuer"), ""),
281
288
  audience: stringOrDefault(pickValue(json, "Audience", "audience"), ""),
@@ -394,7 +401,13 @@ function sanitizeRequest(request) {
394
401
  }
395
402
  function normalizeLicensingApiBaseUrl(value) {
396
403
  const normalized = (value ?? "").trim();
397
- return normalized || exports.DEFAULT_LICENSING_API_BASE_URL;
404
+ return normalized || DEFAULT_LICENSING_API_BASE_URL;
405
+ }
406
+ function resolveLicensingApiBaseUrl() {
407
+ return normalizeLicensingApiBaseUrl(developmentLicensingApiBaseUrlOverride);
408
+ }
409
+ function setDevelopmentLicensingApiBaseUrlOverride(value) {
410
+ developmentLicensingApiBaseUrlOverride = value;
398
411
  }
399
412
  function normalizeTimeoutMs(value) {
400
413
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
@@ -451,14 +464,8 @@ function collectRequestedFeatures(request) {
451
464
  if (hasCustomSink) {
452
465
  features.add("extensions.reporting_sinks.custom");
453
466
  }
454
- if (pickValue(context, "ReportFinalizer", "reportFinalizer") != null) {
455
- features.add("policy.report_finalizer_and_threshold_controls");
456
- }
457
467
  for (const scenarioValue of scenarios) {
458
468
  const scenario = asRecord(scenarioValue);
459
- for (const feature of normalizeStringArray(pickValue(scenario, "LicenseFeatures", "licenseFeatures"))) {
460
- features.add(feature);
461
- }
462
469
  if (asList(scenario.Thresholds).length > 0) {
463
470
  features.add("policy.report_finalizer_and_threshold_controls");
464
471
  }
@@ -748,6 +755,8 @@ async function evaluateScenarioOutcome(scenario, requestCount, context) {
748
755
  sourceTrackingField: sourceEndpoint.trackingField,
749
756
  destinationTrackingField: destinationEndpoint?.trackingField,
750
757
  destinationGatherByField: destinationEndpoint?.gatherByField,
758
+ trackingFieldValueCaseSensitive: toBoolean(pickValue(tracking, "TrackingFieldValueCaseSensitive", "trackingFieldValueCaseSensitive"), true),
759
+ gatherByFieldValueCaseSensitive: toBoolean(pickValue(tracking, "GatherByFieldValueCaseSensitive", "gatherByFieldValueCaseSensitive"), true),
751
760
  correlationTimeoutMs: timeoutMs,
752
761
  timeoutCountsAsFailure,
753
762
  store: correlationStore ?? undefined
@@ -1128,11 +1137,11 @@ function normalizePayload(payload, endpoint, index) {
1128
1137
  function readTrackingId(payload, selector) {
1129
1138
  const normalized = selector.trim().toLowerCase();
1130
1139
  if (normalized.startsWith("header:")) {
1131
- const expected = selector.slice("header:".length).trim().toLowerCase();
1140
+ const expected = selector.slice("header:".length).trim();
1132
1141
  const headers = payload.headers ?? {};
1133
1142
  for (const [key, value] of Object.entries(headers)) {
1134
1143
  const normalizedValue = String(value ?? "").trim();
1135
- if (key.toLowerCase() === expected && normalizedValue) {
1144
+ if (key === expected && normalizedValue) {
1136
1145
  return normalizedValue;
1137
1146
  }
1138
1147
  }
@@ -1573,8 +1582,8 @@ function isRuntimeReportingSink(value) {
1573
1582
  "SaveRealtimeStats",
1574
1583
  "saveRealtimeMetrics",
1575
1584
  "SaveRealtimeMetrics",
1576
- "saveFinalStats",
1577
- "SaveFinalStats",
1585
+ "saveRunResult",
1586
+ "SaveRunResult",
1578
1587
  "stop",
1579
1588
  "Stop"
1580
1589
  ].some((name) => typeof record[name] === "function");
@@ -1882,3 +1891,64 @@ function resolveTimeoutSeconds(context, secondsKey, secondsAlias, millisecondsKe
1882
1891
  function asList(value) {
1883
1892
  return Array.isArray(value) ? value : [];
1884
1893
  }
1894
+ exports.__loadstrikeTestExports = {
1895
+ addIfNotBlank,
1896
+ asNullableInt,
1897
+ asNumber,
1898
+ buildThresholdExpression,
1899
+ collectClaimStrings,
1900
+ collectRequestedFeatures,
1901
+ computeScenarioRequestCount,
1902
+ countCustomWorkerPlugins,
1903
+ countTargetedScenarios,
1904
+ decodeBase64Url,
1905
+ decodeJwtJsonSegment,
1906
+ deviceHash,
1907
+ enforceEntitlementClaims,
1908
+ enforceJwtLifetime,
1909
+ enforceRuntimePolicyClaims,
1910
+ evaluateScenarioOutcome,
1911
+ evaluateScenarioThresholds,
1912
+ fileContains,
1913
+ hasLinuxDesktopSession,
1914
+ hasTruthyEnv,
1915
+ inferLegacyHttpResponseSourceValue,
1916
+ isContainerHost,
1917
+ isLinuxLocalHost,
1918
+ isLocalHost,
1919
+ isRuntimeReportingSink,
1920
+ mapCorrelationStore,
1921
+ mapEnvironmentClassification,
1922
+ mapEndpointSpec,
1923
+ normalizeEndpointPayloadValue,
1924
+ normalizeLicensingApiBaseUrl,
1925
+ resolveLicensingApiBaseUrl,
1926
+ normalizeNodeType,
1927
+ normalizePayload,
1928
+ normalizeStringArray,
1929
+ normalizeTimeoutMs,
1930
+ parseBodyAsObject,
1931
+ parseJwt,
1932
+ readEpochClaim,
1933
+ readLinuxMachineId,
1934
+ readMacPlatformUuid,
1935
+ readOptionalTrackingSelectorValue,
1936
+ readRedisCorrelationStoreOptions,
1937
+ readTrackingId,
1938
+ readWindowsMachineGuid,
1939
+ resolveRuntimePolicy,
1940
+ resolveTimeoutSeconds,
1941
+ sanitizeLooseJson,
1942
+ sanitizeRequest,
1943
+ setJsonPathValue,
1944
+ setDevelopmentLicensingApiBaseUrlOverride,
1945
+ sleep,
1946
+ stringOrDefault,
1947
+ toBoolean,
1948
+ toContractNodeType,
1949
+ toStringMap,
1950
+ tryParseJson,
1951
+ validateRedisCorrelationStoreConfiguration,
1952
+ validateTrackingConfiguration,
1953
+ verifyJwtSignature
1954
+ };
@@ -1,17 +1,14 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.__loadstrikeTestExports = void 0;
6
4
  exports.buildDotnetTxtReport = buildDotnetTxtReport;
7
5
  exports.buildDotnetCsvReport = buildDotnetCsvReport;
8
6
  exports.buildDotnetMarkdownReport = buildDotnetMarkdownReport;
9
7
  exports.buildDotnetHtmlReport = buildDotnetHtmlReport;
10
8
  const node_fs_1 = require("node:fs");
11
- const node_os_1 = __importDefault(require("node:os"));
12
9
  const node_path_1 = require("node:path");
13
10
  const node_url_1 = require("node:url");
14
- const REPORT_EOL = node_os_1.default.EOL;
11
+ const REPORT_EOL = "\n";
15
12
  const REPORT_FALLBACK_SVG = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'><defs><linearGradient id='g' x1='0' y1='0' x2='1' y2='1'><stop offset='0' stop-color='#64b5ff'/><stop offset='1' stop-color='#2f66db'/></linearGradient></defs><rect width='128' height='128' rx='24' fill='#081325'/><path d='M24 94L56 24l16 34 14-22 18 58h-16l-7-23-9 13-10-21-14 31H24z' fill='url(#g)'/></svg>";
16
13
  const REPORT_LOGO_CACHE = new Map();
17
14
  function resolveReportModuleDir() {
@@ -1265,3 +1262,20 @@ if(btns.length>0){show(btns[0].dataset.tab);}renderAllCharts();initPanePan();win
1265
1262
  .split("__CHART_DATA__").join(chartDataJson)
1266
1263
  .concat(REPORT_EOL);
1267
1264
  }
1265
+ exports.__loadstrikeTestExports = {
1266
+ buildDotnetFailedResponseContent,
1267
+ buildDotnetFailedResponseHtml,
1268
+ buildDotnetGroupedCorrelationSummaryHtml,
1269
+ buildDotnetHtmlTabs,
1270
+ buildDotnetMetricHtml,
1271
+ buildDotnetPluginHints,
1272
+ buildDotnetScenarioHtml,
1273
+ buildDotnetScenarioMeasurementHtml,
1274
+ buildDotnetStatusCodeHtml,
1275
+ buildDotnetStepHtml,
1276
+ buildDotnetStepMeasurementHtml,
1277
+ buildDotnetThresholdHtml,
1278
+ buildDotnetUngroupedCorrelationSummaryHtml,
1279
+ buildUngroupedCorrelationChartPayload,
1280
+ classifyStatusCodeBucket
1281
+ };