@loadstrike/loadstrike-sdk 0.1.10001 → 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.
@@ -551,11 +551,10 @@ export class TrackingFieldSelector {
551
551
  }
552
552
  extract(payload) {
553
553
  if (this.kind === "header") {
554
- const headerName = this.path.toLowerCase();
555
554
  const headers = payload.headers ?? {};
556
555
  for (const [key, value] of Object.entries(headers)) {
557
556
  const parsed = String(value ?? "").trim();
558
- if (key.toLowerCase() === headerName && parsed) {
557
+ if (key === this.path && parsed) {
559
558
  return parsed;
560
559
  }
561
560
  }
@@ -595,6 +594,8 @@ function normalizeTrackingFieldLocation(location) {
595
594
  }
596
595
  export class CrossPlatformTrackingRuntime {
597
596
  constructor(options) {
597
+ this.trackingIdDisplayByEventId = new Map();
598
+ this.gatherByDisplayByComparisonKey = new Map();
598
599
  this.producedCount = 0;
599
600
  this.consumedCount = 0;
600
601
  this.matchedCount = 0;
@@ -608,6 +609,8 @@ export class CrossPlatformTrackingRuntime {
608
609
  this.gatherSelector = options.destinationGatherByField
609
610
  ? TrackingFieldSelector.parse(options.destinationGatherByField)
610
611
  : null;
612
+ this.trackingFieldValueCaseSensitive = options.trackingFieldValueCaseSensitive ?? true;
613
+ this.gatherByFieldValueCaseSensitive = options.gatherByFieldValueCaseSensitive ?? true;
611
614
  this.timeoutMs = Math.max(options.correlationTimeoutMs ?? 30000, 1);
612
615
  this.timeoutCountsAsFailure = options.timeoutCountsAsFailure ?? true;
613
616
  this.store = options.store ?? new InMemoryCorrelationStore();
@@ -630,13 +633,17 @@ export class CrossPlatformTrackingRuntime {
630
633
  return result;
631
634
  }
632
635
  result.trackingId = trackingId;
633
- const sourceOccurrences = await this.store.incrementSourceOccurrences(trackingId);
636
+ const comparisonTrackingId = normalizeComparisonValue(trackingId, this.trackingFieldValueCaseSensitive);
637
+ const sourceOccurrences = await this.store.incrementSourceOccurrences(comparisonTrackingId);
634
638
  if (sourceOccurrences > 1) {
635
639
  this.duplicateSourceCount += 1;
636
640
  result.duplicateSource = true;
637
641
  }
638
- const registration = await this.store.registerSource(trackingId, nowMs);
642
+ const registration = await this.store.registerSource(comparisonTrackingId, nowMs);
639
643
  result.eventId = registration.eventId ?? "";
644
+ if (registration.eventId) {
645
+ this.trackingIdDisplayByEventId.set(registration.eventId, trackingId);
646
+ }
640
647
  if (registration.eventId) {
641
648
  await this.store.tryExpire(registration.eventId);
642
649
  }
@@ -677,12 +684,13 @@ export class CrossPlatformTrackingRuntime {
677
684
  return result;
678
685
  }
679
686
  result.trackingId = trackingId;
680
- const destinationOccurrences = await this.store.incrementDestinationOccurrences(trackingId);
687
+ const comparisonTrackingId = normalizeComparisonValue(trackingId, this.trackingFieldValueCaseSensitive);
688
+ const destinationOccurrences = await this.store.incrementDestinationOccurrences(comparisonTrackingId);
681
689
  if (destinationOccurrences > 1) {
682
690
  this.duplicateDestinationCount += 1;
683
691
  result.duplicateDestination = true;
684
692
  }
685
- const source = await this.store.tryMatchDestination(trackingId, nowMs);
693
+ const source = await this.store.tryMatchDestination(comparisonTrackingId, nowMs);
686
694
  if (!source) {
687
695
  result.message = "Destination tracking id had no source match.";
688
696
  return result;
@@ -690,10 +698,12 @@ export class CrossPlatformTrackingRuntime {
690
698
  this.matchedCount += 1;
691
699
  const latencyMs = Math.max(nowMs - (source.producedUtc ?? source.sourceTimestampUtc ?? nowMs), 0);
692
700
  this.totalLatencyMs += latencyMs;
701
+ const displayTrackingId = this.resolveTrackingIdDisplay(source.eventId, source.trackingId, true);
693
702
  const gatherKey = this.resolveGatherKey(payload);
694
703
  result.eventId = source.eventId ?? "";
695
704
  result.sourceTimestampUtc = source.sourceTimestampUtc ?? source.producedUtc ?? 0;
696
705
  result.destinationTimestampUtc = nowMs;
706
+ result.trackingId = displayTrackingId;
697
707
  result.gatherKey = gatherKey;
698
708
  result.matched = true;
699
709
  result.latencyMs = latencyMs;
@@ -703,7 +713,7 @@ export class CrossPlatformTrackingRuntime {
703
713
  this.gathered.set(gatherKey, row);
704
714
  for (const plugin of this.plugins) {
705
715
  if (plugin.onMatched) {
706
- await plugin.onMatched(trackingId, source.payload, payload, latencyMs);
716
+ await plugin.onMatched(displayTrackingId, source.payload, payload, latencyMs, gatherKey);
707
717
  }
708
718
  }
709
719
  return result;
@@ -751,7 +761,9 @@ export class CrossPlatformTrackingRuntime {
751
761
  expiredEntries.push(entry);
752
762
  }
753
763
  for (const entry of expiredEntries) {
764
+ const displayTrackingId = this.resolveTrackingIdDisplay(entry.eventId, entry.trackingId, true);
754
765
  this.timeoutCount += 1;
766
+ entry.trackingId = displayTrackingId;
755
767
  const gatherKey = this.resolveGatherKey(entry.payload);
756
768
  const row = this.gathered.get(gatherKey) ?? { total: 0, matched: 0, timedOut: 0 };
757
769
  row.total += 1;
@@ -759,7 +771,7 @@ export class CrossPlatformTrackingRuntime {
759
771
  this.gathered.set(gatherKey, row);
760
772
  for (const plugin of this.plugins) {
761
773
  if (plugin.onTimeout) {
762
- await plugin.onTimeout(entry.trackingId, entry.payload);
774
+ await plugin.onTimeout(displayTrackingId, entry.payload);
763
775
  }
764
776
  }
765
777
  }
@@ -783,11 +795,40 @@ export class CrossPlatformTrackingRuntime {
783
795
  if (!this.gatherSelector) {
784
796
  return "__ungrouped__";
785
797
  }
786
- return this.gatherSelector.extract(payload) || "__unknown__";
798
+ const gatherKey = this.gatherSelector.extract(payload);
799
+ if (!gatherKey) {
800
+ return "__unknown__";
801
+ }
802
+ if (this.gatherByFieldValueCaseSensitive) {
803
+ return gatherKey;
804
+ }
805
+ const comparisonKey = normalizeComparisonValue(gatherKey, false);
806
+ const existing = this.gatherByDisplayByComparisonKey.get(comparisonKey);
807
+ if (existing) {
808
+ return existing;
809
+ }
810
+ this.gatherByDisplayByComparisonKey.set(comparisonKey, gatherKey);
811
+ return gatherKey;
787
812
  }
788
813
  isTimeoutCountedAsFailure() {
789
814
  return this.timeoutCountsAsFailure;
790
815
  }
816
+ resolveTrackingIdDisplay(eventId, fallbackTrackingId, remove) {
817
+ if (!eventId?.trim()) {
818
+ return fallbackTrackingId;
819
+ }
820
+ if (remove) {
821
+ const displayTrackingId = this.trackingIdDisplayByEventId.get(eventId);
822
+ this.trackingIdDisplayByEventId.delete(eventId);
823
+ return displayTrackingId ?? fallbackTrackingId;
824
+ }
825
+ return this.trackingIdDisplayByEventId.get(eventId) ?? fallbackTrackingId;
826
+ }
827
+ }
828
+ function normalizeComparisonValue(value, caseSensitive) {
829
+ return caseSensitive
830
+ ? value
831
+ : value.toUpperCase();
791
832
  }
792
833
  function normalizeJsonBody(body) {
793
834
  if (body instanceof Uint8Array) {
@@ -907,7 +948,9 @@ function uniqueStringList(values) {
907
948
  return [...new Set(values.filter((value) => value.trim().length > 0))];
908
949
  }
909
950
  function createRedisStoreClient(connectionString, database) {
910
- const client = createClient({
951
+ const createClientOverride = globalThis
952
+ .__wave15CreateRedisClient;
953
+ const client = (typeof createClientOverride === "function" ? createClientOverride : createClient)({
911
954
  url: connectionString,
912
955
  database: database >= 0 ? database : undefined
913
956
  });
@@ -997,3 +1040,18 @@ function readJsonPathSegment(current, segment) {
997
1040
  }
998
1041
  return undefined;
999
1042
  }
1043
+ export const __loadstrikeTestExports = {
1044
+ CorrelationStoreConfiguration,
1045
+ InMemoryCorrelationStore,
1046
+ RedisCorrelationStore,
1047
+ RedisCorrelationStoreOptions,
1048
+ TrackingFieldSelector,
1049
+ createRedisStoreClient,
1050
+ hydrateCorrelationEntry,
1051
+ normalizeCorrelationEntry,
1052
+ parseStringList,
1053
+ readJsonPathSegment,
1054
+ sanitizeLooseJson,
1055
+ tryParseObject,
1056
+ uniqueStringList
1057
+ };
package/dist/esm/index.js CHANGED
@@ -1,6 +1,4 @@
1
- export { DEFAULT_LICENSING_API_BASE_URL, LoadStrikeLocalClient } from "./local.js";
2
- export { CrossPlatformScenarioConfigurator, ScenarioTrackingExtensions, LoadStrikeContext, LoadStrikePluginData, LoadStrikePluginDataTable, LoadStrikeReportData, LoadStrikeNodeType, LoadStrikeReportFormat, LoadStrikeResponse, LoadStrikeLogLevel, LoadStrikeScenarioOperation, LoadStrikeOperationType, LoadStrikeRunner, LoadStrikeScenario, LoadStrikeSimulation, LoadStrikeMetric, LoadStrikeCounter, LoadStrikeGauge, LoadStrikeStep, LoadStrikeThreshold } from "./runtime.js";
1
+ export { CrossPlatformScenarioConfigurator, ScenarioTrackingExtensions, LoadStrikeContext, LoadStrikePluginData, LoadStrikePluginDataTable, LoadStrikeNodeType, LoadStrikeReportFormat, LoadStrikeResponse, LoadStrikeLogLevel, LoadStrikeScenarioOperation, LoadStrikeOperationType, LoadStrikeRunner, LoadStrikeScenario, LoadStrikeSimulation, LoadStrikeMetric, LoadStrikeCounter, LoadStrikeGauge, LoadStrikeStep, LoadStrikeThreshold } from "./runtime.js";
3
2
  export { CorrelationStoreConfiguration, CrossPlatformTrackingRuntime, InMemoryCorrelationStore, RedisCorrelationStoreOptions, RedisCorrelationStore, TrackingPayloadBuilder, TrackingFieldSelector } from "./correlation.js";
4
3
  export { EndpointAdapterFactory, TrafficEndpointDefinition, HttpEndpointDefinition, KafkaEndpointDefinition, KafkaSaslOptions, RabbitMqEndpointDefinition, NatsEndpointDefinition, RedisStreamsEndpointDefinition, AzureEventHubsEndpointDefinition, DelegateStreamEndpointDefinition, PushDiffusionEndpointDefinition, HttpOAuth2ClientCredentialsOptions, HttpAuthOptions } from "./transports.js";
5
- export { DistributedClusterAgent, DistributedClusterCoordinator, LocalClusterCoordinator, planAgentScenarioAssignments } from "./cluster.js";
6
- export { CompositeReportingSink, ConsoleReportingSink, DatadogReportingSink, DatadogReportingSinkOptions, GrafanaLokiReportingSink, GrafanaLokiReportingSinkOptions, InfluxDbReportingSink, InfluxDbReportingSinkOptions, MemoryReportingSink, OtelCollectorReportingSink, OtelCollectorReportingSinkOptions, SplunkReportingSink, SplunkReportingSinkOptions, TimescaleDbReportingSink, TimescaleDbReportingSinkOptions } from "./sinks.js";
4
+ export { DatadogReportingSink, DatadogReportingSinkOptions, GrafanaLokiReportingSink, GrafanaLokiReportingSinkOptions, InfluxDbReportingSink, InfluxDbReportingSinkOptions, OtelCollectorReportingSink, OtelCollectorReportingSinkOptions, SplunkReportingSink, SplunkReportingSinkOptions, TimescaleDbReportingSink, TimescaleDbReportingSinkOptions } from "./sinks.js";
package/dist/esm/local.js CHANGED
@@ -4,7 +4,8 @@ import * as childProcess from "node:child_process";
4
4
  import { createHash, createVerify, randomUUID } from "node:crypto";
5
5
  import { CorrelationStoreConfiguration, CrossPlatformTrackingRuntime, RedisCorrelationStoreOptions, RedisCorrelationStore, TrackingFieldSelector } from "./correlation.js";
6
6
  import { EndpointAdapterFactory } from "./transports.js";
7
- export const DEFAULT_LICENSING_API_BASE_URL = "https://licensing.loadstrike.com";
7
+ const DEFAULT_LICENSING_API_BASE_URL = "https://licensing.loadstrike.com";
8
+ let developmentLicensingApiBaseUrlOverride;
8
9
  const BUILT_IN_WORKER_PLUGIN_NAMES = new Set([
9
10
  "loadstrike failed responses",
10
11
  "loadstrike correlation"
@@ -48,7 +49,7 @@ export class LoadStrikeLocalClient {
48
49
  constructor(options = {}) {
49
50
  this.signingKeyCache = new Map();
50
51
  assertNoDisableLicenseEnforcementOption(options, "LoadStrikeLocalClient");
51
- this.licensingApiBaseUrl = normalizeLicensingApiBaseUrl(options.licensingApiBaseUrl);
52
+ this.licensingApiBaseUrl = resolveLicensingApiBaseUrl();
52
53
  this.licenseValidationTimeoutMs = normalizeTimeoutMs(options.licenseValidationTimeoutMs);
53
54
  }
54
55
  async run(request) {
@@ -218,7 +219,9 @@ export class LoadStrikeLocalClient {
218
219
  }
219
220
  async getSigningKey(keyId, algorithm) {
220
221
  const nowMs = Date.now();
221
- const cacheKey = this.licensingApiBaseUrl.toLowerCase();
222
+ const normalizedKeyId = stringOrDefault(keyId, "default").trim() || "default";
223
+ const normalizedAlgorithm = stringOrDefault(algorithm, "RS256").toUpperCase();
224
+ const cacheKey = `${this.licensingApiBaseUrl.toLowerCase()}|${normalizedAlgorithm}:${normalizedKeyId}`;
222
225
  const cached = this.signingKeyCache.get(cacheKey);
223
226
  if (cached && cached.expiresAtUtc > nowMs) {
224
227
  return cached;
@@ -226,17 +229,21 @@ export class LoadStrikeLocalClient {
226
229
  const controller = new AbortController();
227
230
  const timer = setTimeout(() => controller.abort(), this.licenseValidationTimeoutMs);
228
231
  try {
229
- const { response, json } = await this.getLicensingRequest("/api/v1/licenses/signing-public-key", controller.signal);
232
+ const { response, json } = await this.getLicensingRequest(`/api/v1/licenses/signing-public-key?kid=${encodeURIComponent(normalizedKeyId)}`, controller.signal);
230
233
  if (!response.ok) {
231
234
  throw new Error(`Failed to resolve licensing signing key. status=${response.status}`);
232
235
  }
233
- const resolvedAlgorithm = stringOrDefault(pickValue(json, "Algorithm", "alg"), algorithm).toUpperCase();
236
+ const resolvedAlgorithm = stringOrDefault(pickValue(json, "Algorithm", "alg"), normalizedAlgorithm).toUpperCase();
234
237
  const publicKeyPem = stringOrDefault(pickValue(json, "PublicKeyPem", "PublicKey", "Pem"), "");
235
238
  if (!publicKeyPem.trim()) {
236
239
  throw new Error("Runner key validation failed: signing key response is empty.");
237
240
  }
241
+ const resolvedKeyId = stringOrDefault(pickValue(json, "KeyId", "Kid", "kid"), normalizedKeyId).trim() || normalizedKeyId;
242
+ if (resolvedKeyId !== normalizedKeyId) {
243
+ throw new Error("Runner key validation failed: signing key response key id does not match requested key id.");
244
+ }
238
245
  const keyRecord = {
239
- keyId: stringOrDefault(pickValue(json, "KeyId", "Kid", "kid"), keyId),
246
+ keyId: resolvedKeyId,
240
247
  algorithm: resolvedAlgorithm,
241
248
  issuer: stringOrDefault(pickValue(json, "Issuer", "issuer"), ""),
242
249
  audience: stringOrDefault(pickValue(json, "Audience", "audience"), ""),
@@ -356,6 +363,12 @@ function normalizeLicensingApiBaseUrl(value) {
356
363
  const normalized = (value ?? "").trim();
357
364
  return normalized || DEFAULT_LICENSING_API_BASE_URL;
358
365
  }
366
+ function resolveLicensingApiBaseUrl() {
367
+ return normalizeLicensingApiBaseUrl(developmentLicensingApiBaseUrlOverride);
368
+ }
369
+ function setDevelopmentLicensingApiBaseUrlOverride(value) {
370
+ developmentLicensingApiBaseUrlOverride = value;
371
+ }
359
372
  function normalizeTimeoutMs(value) {
360
373
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
361
374
  return 10000;
@@ -411,14 +424,8 @@ function collectRequestedFeatures(request) {
411
424
  if (hasCustomSink) {
412
425
  features.add("extensions.reporting_sinks.custom");
413
426
  }
414
- if (pickValue(context, "ReportFinalizer", "reportFinalizer") != null) {
415
- features.add("policy.report_finalizer_and_threshold_controls");
416
- }
417
427
  for (const scenarioValue of scenarios) {
418
428
  const scenario = asRecord(scenarioValue);
419
- for (const feature of normalizeStringArray(pickValue(scenario, "LicenseFeatures", "licenseFeatures"))) {
420
- features.add(feature);
421
- }
422
429
  if (asList(scenario.Thresholds).length > 0) {
423
430
  features.add("policy.report_finalizer_and_threshold_controls");
424
431
  }
@@ -708,6 +715,8 @@ async function evaluateScenarioOutcome(scenario, requestCount, context) {
708
715
  sourceTrackingField: sourceEndpoint.trackingField,
709
716
  destinationTrackingField: destinationEndpoint?.trackingField,
710
717
  destinationGatherByField: destinationEndpoint?.gatherByField,
718
+ trackingFieldValueCaseSensitive: toBoolean(pickValue(tracking, "TrackingFieldValueCaseSensitive", "trackingFieldValueCaseSensitive"), true),
719
+ gatherByFieldValueCaseSensitive: toBoolean(pickValue(tracking, "GatherByFieldValueCaseSensitive", "gatherByFieldValueCaseSensitive"), true),
711
720
  correlationTimeoutMs: timeoutMs,
712
721
  timeoutCountsAsFailure,
713
722
  store: correlationStore ?? undefined
@@ -1088,11 +1097,11 @@ function normalizePayload(payload, endpoint, index) {
1088
1097
  function readTrackingId(payload, selector) {
1089
1098
  const normalized = selector.trim().toLowerCase();
1090
1099
  if (normalized.startsWith("header:")) {
1091
- const expected = selector.slice("header:".length).trim().toLowerCase();
1100
+ const expected = selector.slice("header:".length).trim();
1092
1101
  const headers = payload.headers ?? {};
1093
1102
  for (const [key, value] of Object.entries(headers)) {
1094
1103
  const normalizedValue = String(value ?? "").trim();
1095
- if (key.toLowerCase() === expected && normalizedValue) {
1104
+ if (key === expected && normalizedValue) {
1096
1105
  return normalizedValue;
1097
1106
  }
1098
1107
  }
@@ -1533,8 +1542,8 @@ function isRuntimeReportingSink(value) {
1533
1542
  "SaveRealtimeStats",
1534
1543
  "saveRealtimeMetrics",
1535
1544
  "SaveRealtimeMetrics",
1536
- "saveFinalStats",
1537
- "SaveFinalStats",
1545
+ "saveRunResult",
1546
+ "SaveRunResult",
1538
1547
  "stop",
1539
1548
  "Stop"
1540
1549
  ].some((name) => typeof record[name] === "function");
@@ -1842,3 +1851,64 @@ function resolveTimeoutSeconds(context, secondsKey, secondsAlias, millisecondsKe
1842
1851
  function asList(value) {
1843
1852
  return Array.isArray(value) ? value : [];
1844
1853
  }
1854
+ export const __loadstrikeTestExports = {
1855
+ addIfNotBlank,
1856
+ asNullableInt,
1857
+ asNumber,
1858
+ buildThresholdExpression,
1859
+ collectClaimStrings,
1860
+ collectRequestedFeatures,
1861
+ computeScenarioRequestCount,
1862
+ countCustomWorkerPlugins,
1863
+ countTargetedScenarios,
1864
+ decodeBase64Url,
1865
+ decodeJwtJsonSegment,
1866
+ deviceHash,
1867
+ enforceEntitlementClaims,
1868
+ enforceJwtLifetime,
1869
+ enforceRuntimePolicyClaims,
1870
+ evaluateScenarioOutcome,
1871
+ evaluateScenarioThresholds,
1872
+ fileContains,
1873
+ hasLinuxDesktopSession,
1874
+ hasTruthyEnv,
1875
+ inferLegacyHttpResponseSourceValue,
1876
+ isContainerHost,
1877
+ isLinuxLocalHost,
1878
+ isLocalHost,
1879
+ isRuntimeReportingSink,
1880
+ mapCorrelationStore,
1881
+ mapEnvironmentClassification,
1882
+ mapEndpointSpec,
1883
+ normalizeEndpointPayloadValue,
1884
+ normalizeLicensingApiBaseUrl,
1885
+ resolveLicensingApiBaseUrl,
1886
+ normalizeNodeType,
1887
+ normalizePayload,
1888
+ normalizeStringArray,
1889
+ normalizeTimeoutMs,
1890
+ parseBodyAsObject,
1891
+ parseJwt,
1892
+ readEpochClaim,
1893
+ readLinuxMachineId,
1894
+ readMacPlatformUuid,
1895
+ readOptionalTrackingSelectorValue,
1896
+ readRedisCorrelationStoreOptions,
1897
+ readTrackingId,
1898
+ readWindowsMachineGuid,
1899
+ resolveRuntimePolicy,
1900
+ resolveTimeoutSeconds,
1901
+ sanitizeLooseJson,
1902
+ sanitizeRequest,
1903
+ setJsonPathValue,
1904
+ setDevelopmentLicensingApiBaseUrlOverride,
1905
+ sleep,
1906
+ stringOrDefault,
1907
+ toBoolean,
1908
+ toContractNodeType,
1909
+ toStringMap,
1910
+ tryParseJson,
1911
+ validateRedisCorrelationStoreConfiguration,
1912
+ validateTrackingConfiguration,
1913
+ verifyJwtSignature
1914
+ };
@@ -1,8 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
- import os from "node:os";
3
2
  import { dirname, resolve } from "node:path";
4
3
  import { fileURLToPath } from "node:url";
5
- const REPORT_EOL = os.EOL;
4
+ const REPORT_EOL = "\n";
6
5
  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>";
7
6
  const REPORT_LOGO_CACHE = new Map();
8
7
  function resolveReportModuleDir() {
@@ -1256,3 +1255,20 @@ if(btns.length>0){show(btns[0].dataset.tab);}renderAllCharts();initPanePan();win
1256
1255
  .split("__CHART_DATA__").join(chartDataJson)
1257
1256
  .concat(REPORT_EOL);
1258
1257
  }
1258
+ export const __loadstrikeTestExports = {
1259
+ buildDotnetFailedResponseContent,
1260
+ buildDotnetFailedResponseHtml,
1261
+ buildDotnetGroupedCorrelationSummaryHtml,
1262
+ buildDotnetHtmlTabs,
1263
+ buildDotnetMetricHtml,
1264
+ buildDotnetPluginHints,
1265
+ buildDotnetScenarioHtml,
1266
+ buildDotnetScenarioMeasurementHtml,
1267
+ buildDotnetStatusCodeHtml,
1268
+ buildDotnetStepHtml,
1269
+ buildDotnetStepMeasurementHtml,
1270
+ buildDotnetThresholdHtml,
1271
+ buildDotnetUngroupedCorrelationSummaryHtml,
1272
+ buildUngroupedCorrelationChartPayload,
1273
+ classifyStatusCodeBucket
1274
+ };