@loadstrike/loadstrike-sdk 1.0.30401 → 1.0.31001

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/dist/esm/sinks.js CHANGED
@@ -6,7 +6,26 @@ import { createSocket } from "node:dgram";
6
6
  import { gzip } from "node:zlib";
7
7
  import { Pool } from "pg";
8
8
  import { serializeIterationObservationBatchGzipJson } from "./iteration-observations.js";
9
+ import { redactIterationObservationSecrets } from "./iteration-observation-diagnostics.js";
9
10
  const PORTAL_RUN_TOKEN_PROVIDER = Symbol.for("loadstrike.internal.portal-run-token-provider");
11
+ const MAXIMUM_HTTP_RESPONSE_BODY_BYTES = 256 * 1024;
12
+ const MAXIMUM_HTTP_METADATA_CHARACTERS = 256;
13
+ const HTTP_METADATA_TRUNCATION_SUFFIX = " [truncated]";
14
+ class ReportingSinkHttpError extends Error {
15
+ constructor(sinkName, status, statusText, requestId) {
16
+ const normalizedStatus = Number.isFinite(status) ? Math.max(Math.trunc(status), 0) : 0;
17
+ const normalizedStatusText = normalizeHttpMetadata(statusText);
18
+ const normalizedRequestId = normalizeHttpMetadata(requestId);
19
+ super(`${sinkName} write failed with HTTP status ${normalizedStatus}`
20
+ + (normalizedStatusText ? ` ${normalizedStatusText}` : "")
21
+ + (normalizedRequestId ? ` (requestId=${normalizedRequestId})` : "")
22
+ + ".");
23
+ this.name = "ReportingSinkHttpError";
24
+ this.status = normalizedStatus;
25
+ this.statusText = normalizedStatusText;
26
+ this.requestId = normalizedRequestId;
27
+ }
28
+ }
10
29
  function gzipJsonAsync(value) {
11
30
  const json = Buffer.from(JSON.stringify(value), "utf8");
12
31
  return new Promise((resolve, reject) => {
@@ -2202,6 +2221,7 @@ function removeSdkPercentileFields(event) {
2202
2221
  }
2203
2222
  function createIterationObservationEvents(session, batch) {
2204
2223
  const events = [];
2224
+ const occurredUtc = dateFromUtcNanoseconds(batch.createdUtcNs);
2205
2225
  for (const observation of batch.observations) {
2206
2226
  const tags = {
2207
2227
  run_id: batch.runId,
@@ -2214,7 +2234,7 @@ function createIterationObservationEvents(session, batch) {
2214
2234
  events.push({
2215
2235
  runId: batch.runId,
2216
2236
  eventType: "iteration.observation",
2217
- occurredUtc: new Date(),
2237
+ occurredUtc,
2218
2238
  sessionId: batch.sessionId,
2219
2239
  testSuite: session.testSuite,
2220
2240
  testName: session.testName,
@@ -2249,7 +2269,7 @@ function createIterationObservationEvents(session, batch) {
2249
2269
  events.push({
2250
2270
  runId: batch.runId,
2251
2271
  eventType: "iteration.step",
2252
- occurredUtc: new Date(),
2272
+ occurredUtc,
2253
2273
  sessionId: batch.sessionId,
2254
2274
  testSuite: session.testSuite,
2255
2275
  testName: session.testName,
@@ -2280,10 +2300,11 @@ function createIterationObservationEvents(session, batch) {
2280
2300
  return events;
2281
2301
  }
2282
2302
  function createIterationCompletionEvents(session, completion) {
2303
+ const occurredUtc = dateFromUtcNanoseconds(completion.completedUtcNs);
2283
2304
  return [{
2284
2305
  runId: completion.runId,
2285
2306
  eventType: "observation.stream.completed",
2286
- occurredUtc: new Date(),
2307
+ occurredUtc,
2287
2308
  sessionId: completion.sessionId,
2288
2309
  testSuite: session.testSuite,
2289
2310
  testName: session.testName,
@@ -2342,6 +2363,22 @@ function portalEventId(event, index) {
2342
2363
  });
2343
2364
  return `lsr_${createHash("sha256").update(material).digest("hex").slice(0, 40)}`;
2344
2365
  }
2366
+ function dateFromUtcNanoseconds(value) {
2367
+ try {
2368
+ const nanoseconds = BigInt(value);
2369
+ if (nanoseconds < 0n) {
2370
+ return new Date(0);
2371
+ }
2372
+ const milliseconds = nanoseconds / 1000000n;
2373
+ const numeric = Number(milliseconds);
2374
+ return Number.isFinite(numeric) && numeric <= 8640000000000000
2375
+ ? new Date(numeric)
2376
+ : new Date(0);
2377
+ }
2378
+ catch {
2379
+ return new Date(0);
2380
+ }
2381
+ }
2345
2382
  function addMeasurementFields(fields, prefix, measurement) {
2346
2383
  const request = measurement?.request ?? { count: 0, percent: 0, rps: 0 };
2347
2384
  const latency = measurement?.latency ?? {
@@ -3533,22 +3570,85 @@ async function postWithTimeout(fetchImpl, url, init, timeoutMs, sinkName) {
3533
3570
  const timer = setTimeout(() => controller.abort(), Math.max(timeoutMs, 1));
3534
3571
  try {
3535
3572
  const response = await fetchImpl(url, { ...init, signal: controller.signal });
3536
- const body = await readResponseBodyText(response);
3537
3573
  if (!response.ok) {
3538
- throw new Error(`${sinkName} write failed with status ${response.status}: ${body}`);
3574
+ throw new ReportingSinkHttpError(sinkName, response.status, readHttpResponseStatusText(response), readHttpResponseRequestId(response));
3539
3575
  }
3540
- return body;
3576
+ return await readResponseBodyText(response);
3541
3577
  }
3542
3578
  finally {
3543
3579
  clearTimeout(timer);
3544
3580
  }
3545
3581
  }
3546
3582
  async function readResponseBodyText(response) {
3547
- const candidate = response;
3548
- if (typeof candidate.text !== "function") {
3583
+ const body = response.body;
3584
+ if (body && typeof body.getReader === "function") {
3585
+ const reader = body.getReader();
3586
+ const chunks = [];
3587
+ let bytes = 0;
3588
+ try {
3589
+ while (true) {
3590
+ const next = await reader.read();
3591
+ if (next.done)
3592
+ break;
3593
+ if (!next.value)
3594
+ continue;
3595
+ if (bytes + next.value.byteLength > MAXIMUM_HTTP_RESPONSE_BODY_BYTES) {
3596
+ await reader.cancel();
3597
+ throw new Error("Reporting sink response exceeded the bounded response limit.");
3598
+ }
3599
+ chunks.push(next.value);
3600
+ bytes += next.value.byteLength;
3601
+ }
3602
+ }
3603
+ finally {
3604
+ reader.releaseLock();
3605
+ }
3606
+ return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))).toString("utf8");
3607
+ }
3608
+ return "";
3609
+ }
3610
+ function readHttpResponseStatusText(response) {
3611
+ try {
3612
+ return normalizeHttpMetadata(response.statusText);
3613
+ }
3614
+ catch {
3615
+ return "";
3616
+ }
3617
+ }
3618
+ function readHttpResponseRequestId(response) {
3619
+ for (const name of [
3620
+ "x-request-id",
3621
+ "x-correlation-id",
3622
+ "request-id",
3623
+ "traceparent"
3624
+ ]) {
3625
+ try {
3626
+ const value = response.headers?.get(name);
3627
+ if (value) {
3628
+ return normalizeHttpMetadata(value);
3629
+ }
3630
+ }
3631
+ catch {
3632
+ return "";
3633
+ }
3634
+ }
3635
+ return "";
3636
+ }
3637
+ function normalizeHttpMetadata(value) {
3638
+ let text;
3639
+ try {
3640
+ const source = String(value ?? "");
3641
+ text = source.length <= MAXIMUM_HTTP_METADATA_CHARACTERS
3642
+ ? source
3643
+ : source.slice(0, MAXIMUM_HTTP_METADATA_CHARACTERS - HTTP_METADATA_TRUNCATION_SUFFIX.length) + HTTP_METADATA_TRUNCATION_SUFFIX;
3644
+ }
3645
+ catch {
3549
3646
  return "";
3550
3647
  }
3551
- return String(await candidate.text());
3648
+ return redactIterationObservationSecrets(Buffer.from(text, "utf8").toString("utf8")
3649
+ .replace(/[\u0000-\u001F\u007F-\u009F]/gu, " ")
3650
+ .replace(/\s{2,}/gu, " ")
3651
+ .trim());
3552
3652
  }
3553
3653
  function validatePortalIngestResponse(responseBody) {
3554
3654
  const text = String(responseBody ?? "").trim();
@@ -3601,6 +3701,8 @@ export const __loadstrikeTestExports = {
3601
3701
  cloneScenarioStats,
3602
3702
  cloneSessionStartInfo,
3603
3703
  createFinalStatsEvents,
3704
+ createIterationCompletionEvents,
3705
+ createIterationObservationEvents,
3604
3706
  createRealtimeStatsEvents,
3605
3707
  createRunResultEvents,
3606
3708
  createReportingEvent,
@@ -3617,6 +3719,7 @@ export const __loadstrikeTestExports = {
3617
3719
  normalizeStringMap,
3618
3720
  optionNumber,
3619
3721
  optionString,
3722
+ portalEventPayload,
3620
3723
  pickBooleanValue,
3621
3724
  pickRecordValue,
3622
3725
  postWithTimeout,
@@ -596,14 +596,10 @@ class WebSocketEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
596
596
  parsed = new URL(url);
597
597
  }
598
598
  catch {
599
- // Diagnostic text only; no connection is opened here.
600
- // nosemgrep: vulnerability-tools.semgrep-rules.javascript.lang.security.detect-insecure-websocket
601
- throw new Error("Url must be an absolute ws:// or wss:// URI.");
599
+ throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
602
600
  }
603
601
  if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
604
- // Diagnostic text only; no connection is opened here.
605
- // nosemgrep: vulnerability-tools.semgrep-rules.javascript.lang.security.detect-insecure-websocket
606
- throw new Error("Url must be an absolute ws:// or wss:// URI.");
602
+ throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
607
603
  }
608
604
  if (this.ConnectTimeoutSeconds <= 0) {
609
605
  throw new RangeError("ConnectTimeout must be greater than zero.");
@@ -3132,14 +3128,10 @@ function validateWebSocketEndpoint(endpoint, mode) {
3132
3128
  parsed = new URL(url);
3133
3129
  }
3134
3130
  catch {
3135
- // Diagnostic text only; no connection is opened here.
3136
- // nosemgrep: vulnerability-tools.semgrep-rules.javascript.lang.security.detect-insecure-websocket
3137
- throw new Error("Url must be an absolute ws:// or wss:// URI.");
3131
+ throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
3138
3132
  }
3139
3133
  if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
3140
- // Diagnostic text only; no connection is opened here.
3141
- // nosemgrep: vulnerability-tools.semgrep-rules.javascript.lang.security.detect-insecure-websocket
3142
- throw new Error("Url must be an absolute ws:// or wss:// URI.");
3134
+ throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
3143
3135
  }
3144
3136
  const connectMs = optionNumber(options, "ConnectTimeoutMs", "connectTimeoutMs");
3145
3137
  const connectSeconds = optionNumber(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout");
@@ -3569,7 +3561,13 @@ function extractJsonPath(value, arrayPath) {
3569
3561
  if (!normalized) {
3570
3562
  return value;
3571
3563
  }
3572
- const tokens = normalized.replace(/\[(\d+)\]/g, ".$1").split(".").filter((x) => x.length > 0);
3564
+ let tokens;
3565
+ try {
3566
+ tokens = safeJsonPathSegments(normalized.replace(/\[(\d+)\]/g, ".$1"));
3567
+ }
3568
+ catch {
3569
+ return null;
3570
+ }
3573
3571
  for (const token of tokens) {
3574
3572
  if (current == null) {
3575
3573
  return null;
@@ -3579,13 +3577,17 @@ function extractJsonPath(value, arrayPath) {
3579
3577
  if (!Number.isInteger(index) || index < 0 || index >= current.length) {
3580
3578
  return null;
3581
3579
  }
3582
- current = current[index];
3580
+ current = current.at(index);
3583
3581
  continue;
3584
3582
  }
3585
- if (!isRecord(current) || !(token in current)) {
3583
+ if (!isRecord(current)) {
3586
3584
  return null;
3587
3585
  }
3588
- current = current[token];
3586
+ const descriptor = Object.getOwnPropertyDescriptor(current, token);
3587
+ if (!descriptor || !("value" in descriptor)) {
3588
+ return null;
3589
+ }
3590
+ current = descriptor.value;
3589
3591
  }
3590
3592
  return current;
3591
3593
  }
@@ -3602,7 +3604,7 @@ function parseMaybeJson(value) {
3602
3604
  }
3603
3605
  }
3604
3606
  function headersToRecord(value) {
3605
- const headers = {};
3607
+ const headers = Object.create(null);
3606
3608
  value.forEach((headerValue, key) => {
3607
3609
  headers[key] = headerValue;
3608
3610
  });
@@ -3956,11 +3958,22 @@ function extractTrackingValue(payload, selector) {
3956
3958
  return null;
3957
3959
  }
3958
3960
  let current = body;
3959
- for (const segment of selector.slice("json:".length).trim().replace(/^\$\./, "").split(".").filter(Boolean)) {
3961
+ const path = selector.slice("json:".length).trim().replace(/^\$\./, "");
3962
+ let segments;
3963
+ try {
3964
+ segments = safeJsonPathSegments(path);
3965
+ }
3966
+ catch {
3967
+ return null;
3968
+ }
3969
+ for (const segment of segments) {
3960
3970
  if (!isRecord(current)) {
3961
3971
  return null;
3962
3972
  }
3963
- current = current[segment];
3973
+ if (!Object.prototype.hasOwnProperty.call(current, segment)) {
3974
+ return null;
3975
+ }
3976
+ current = readOwnJsonProperty(current, segment);
3964
3977
  }
3965
3978
  return current == null ? null : String(current);
3966
3979
  }
@@ -3991,23 +4004,61 @@ function injectTrackingValue(payload, selector, value) {
3991
4004
  }
3992
4005
  function setJsonBodyValue(body, path, value) {
3993
4006
  const target = parseBodyObject(body) ?? {};
3994
- const clone = cloneBody(target);
3995
- const segments = path.split(".").filter(Boolean);
4007
+ const clone = cloneJsonRecord(target);
4008
+ const segments = safeJsonPathSegments(path);
3996
4009
  if (!segments.length) {
3997
4010
  return clone;
3998
4011
  }
3999
4012
  let current = clone;
4000
4013
  for (let i = 0; i < segments.length - 1; i += 1) {
4001
4014
  const segment = segments[i];
4002
- const next = current[segment];
4015
+ let next = readOwnJsonProperty(current, segment);
4003
4016
  if (!isRecord(next)) {
4004
- current[segment] = {};
4017
+ next = {};
4018
+ defineJsonProperty(current, segment, next);
4005
4019
  }
4006
- current = current[segment];
4020
+ current = next;
4007
4021
  }
4008
- current[segments[segments.length - 1]] = value;
4022
+ defineJsonProperty(current, segments[segments.length - 1], value);
4009
4023
  return clone;
4010
4024
  }
4025
+ const FORBIDDEN_JSON_PATH_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
4026
+ function safeJsonPathSegments(path) {
4027
+ const segments = path.split(".").filter(Boolean);
4028
+ const forbidden = segments.find((segment) => FORBIDDEN_JSON_PATH_SEGMENTS.has(segment));
4029
+ if (forbidden) {
4030
+ throw new Error(`Tracking selector contains forbidden JSON path segment '${forbidden}'.`);
4031
+ }
4032
+ return segments;
4033
+ }
4034
+ function defineJsonProperty(target, key, value) {
4035
+ Object.defineProperty(target, key, {
4036
+ configurable: true,
4037
+ enumerable: true,
4038
+ value,
4039
+ writable: true
4040
+ });
4041
+ }
4042
+ function readOwnJsonProperty(target, key) {
4043
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
4044
+ return descriptor && "value" in descriptor ? descriptor.value : undefined;
4045
+ }
4046
+ function cloneJsonRecord(source) {
4047
+ const target = {};
4048
+ for (const [key, value] of Object.entries(source)) {
4049
+ defineJsonProperty(target, key, cloneJsonValue(value));
4050
+ }
4051
+ return target;
4052
+ }
4053
+ function cloneJsonValue(value) {
4054
+ if (Array.isArray(value)) {
4055
+ return value.map((entry) => cloneJsonValue(entry));
4056
+ }
4057
+ if (isRecord(value)) {
4058
+ return cloneJsonRecord(value);
4059
+ }
4060
+ return value;
4061
+ }
4011
4062
  function parseBodyObject(body) {
4012
4063
  if (body instanceof Uint8Array) {
4013
4064
  return parseBodyObject(new TextDecoder().decode(body));
@@ -4616,8 +4667,10 @@ export const __loadstrikeTestExports = {
4616
4667
  createNatsHeaders,
4617
4668
  createRedisStreamPayload,
4618
4669
  deserializeBrokerPayloadBody,
4670
+ extractJsonPath,
4619
4671
  extractTrackingValue,
4620
4672
  fromKafkaHeaders,
4673
+ headersToRecord,
4621
4674
  headerValue,
4622
4675
  attachDotNetTrackingPayloadAliases,
4623
4676
  attachPayloadHelpers,
@@ -0,0 +1,21 @@
1
+ export interface IterationObservationDiagnosticLogger {
2
+ debug(message: string): void | PromiseLike<void>;
3
+ warn(message: string): void | PromiseLike<void>;
4
+ error(message: string): void | PromiseLike<void>;
5
+ }
6
+ export interface IterationObservationDiagnosticContext {
7
+ sinkName: string;
8
+ operation: "observation-batch" | "observation-stream-completion" | "reporting-sink-action";
9
+ phase?: string;
10
+ runId: string;
11
+ resultOwnerId: string;
12
+ batchId?: string;
13
+ batchSequence64?: string;
14
+ observationCount: number;
15
+ attempt: number;
16
+ maximumAttempts: number;
17
+ nextDelayMs?: number;
18
+ }
19
+ export declare function logIterationObservationFailure(logger: IterationObservationDiagnosticLogger | undefined, level: "warn" | "error", context: IterationObservationDiagnosticContext, error: unknown): void;
20
+ export declare function logIterationObservationRecovery(logger: IterationObservationDiagnosticLogger | undefined, context: IterationObservationDiagnosticContext): void;
21
+ export declare function redactIterationObservationSecrets(value: string): string;
@@ -1,3 +1,4 @@
1
+ import { type IterationObservationDiagnosticLogger } from "./iteration-observation-diagnostics.js";
1
2
  export declare const ITERATION_OBSERVATION_SCHEMA_VERSION = "loadstrike.iteration-observation/1";
2
3
  export declare const ITERATION_OBSERVATION_BATCH_SCHEMA_VERSION = "loadstrike.iteration-batch/1";
3
4
  export declare const ITERATION_OBSERVATION_STREAM_COMPLETION_SCHEMA_VERSION = "loadstrike.iteration-stream-completion/1";
@@ -192,6 +193,9 @@ export interface IterationObservationReporterOptions {
192
193
  buffer?: ProcessIterationObservationBuffer;
193
194
  clock?: IterationObservationClock;
194
195
  skipValidation?: boolean;
196
+ sinkRetryCount?: number;
197
+ sinkRetryBackoffMs?: number;
198
+ logger?: IterationObservationDiagnosticLogger;
195
199
  }
196
200
  export declare class IterationObservationReporter {
197
201
  private readonly options;
@@ -215,6 +219,7 @@ export declare class IterationObservationReporter {
215
219
  capture(observation: LoadStrikeIterationObservationV1): boolean;
216
220
  flushNow(): void;
217
221
  sealAndDrain(): Promise<IterationObservationDeliveryResult>;
222
+ private completeWithRetries;
218
223
  buildWarnings(): IterationObservationGeneratorWarning[];
219
224
  private createBatch;
220
225
  private createCompletion;
@@ -2526,6 +2526,7 @@ declare function normalizeRuntimeTrackingPayload(payload: TrackingPayload | null
2526
2526
  declare function readRuntimeTrackingId(payload: TrackingPayload, selector: string): string | null;
2527
2527
  declare function runtimeParseBodyAsObject(body: unknown): Record<string, unknown> | null;
2528
2528
  declare function setRuntimeJsonPathValue(body: unknown, path: string, value: string): unknown;
2529
+ declare function pickTrackingValue(source: Record<string, unknown>, ...keys: string[]): unknown;
2529
2530
  declare function pickOptionalTrackingSelectorString(source: Record<string, unknown>, key: string, altKey: string): string | undefined;
2530
2531
  declare function pickTrackingNumber(source: Record<string, unknown>, keys: string[], fallback: number): number;
2531
2532
  declare function toTrackingStringMap(value: unknown): Record<string, string>;
@@ -2691,6 +2692,7 @@ export declare const __loadstrikeTestExports: {
2691
2692
  parseStrictBooleanToken: typeof parseStrictBooleanToken;
2692
2693
  percentile: typeof percentile;
2693
2694
  pickOptionalTrackingSelectorString: typeof pickOptionalTrackingSelectorString;
2695
+ pickTrackingValue: typeof pickTrackingValue;
2694
2696
  pickTrackingNumber: typeof pickTrackingNumber;
2695
2697
  produceOrConsumeTrackingPayload: typeof produceOrConsumeTrackingPayload;
2696
2698
  readConfiguredSinkName: typeof readConfiguredSinkName;
@@ -0,0 +1,9 @@
1
+ export declare const DEFAULT_SINK_RETRY_COUNT = 3;
2
+ export declare const DEFAULT_SINK_RETRY_BACKOFF_MS = 250;
3
+ export declare const MAXIMUM_SINK_RETRY_COUNT = 100;
4
+ export declare const MAXIMUM_TIMER_DELAY_MS = 2147483647;
5
+ export declare function normalizeSinkRetryCount(value: number | undefined): number;
6
+ export declare function normalizeSinkRetryBackoffMs(value: number | undefined): number;
7
+ export declare function sinkRetryDelayMs(baseDelayMs: number, retryNumber: number): number;
8
+ export declare function waitForSinkRetryDelay(delayMs: number): Promise<void>;
9
+ export declare function cooperativeSinkRetryYield(): Promise<void>;
@@ -849,6 +849,9 @@ declare function createRealtimeStatsEvents(session: SinkSessionMetadata, scenari
849
849
  declare function createFinalStatsEvents(session: SinkSessionMetadata, stats: LoadStrikeNodeStats): ReportingSinkEvent[];
850
850
  declare function createRunResultEvents(session: SinkSessionMetadata, result: LoadStrikeRunResult): ReportingSinkEvent[];
851
851
  declare function createReportingEvent(session: SinkSessionMetadata, occurredUtc: Date, eventType: string, scenarioName: string | null, stepName: string | null, tags: Record<string, string>, fields: Record<string, unknown>): ReportingSinkEvent;
852
+ declare function createIterationObservationEvents(session: SinkSessionMetadata, batch: LoadStrikeIterationObservationBatchV1): ReportingSinkEvent[];
853
+ declare function createIterationCompletionEvents(session: SinkSessionMetadata, completion: LoadStrikeIterationObservationStreamCompletionV1): ReportingSinkEvent[];
854
+ declare function portalEventPayload(event: ReportingSinkEvent, index: number): Record<string, unknown>;
852
855
  declare function toOtelAnyValue(value: unknown): Record<string, unknown>;
853
856
  declare function sinkSessionMetadataFromContext(context: LoadStrikeBaseContext, session?: LoadStrikeSessionStartInfo, options?: {
854
857
  distinctRunIdFallback?: boolean;
@@ -897,6 +900,8 @@ export declare const __loadstrikeTestExports: {
897
900
  cloneScenarioStats: typeof cloneScenarioStats;
898
901
  cloneSessionStartInfo: typeof cloneSessionStartInfo;
899
902
  createFinalStatsEvents: typeof createFinalStatsEvents;
903
+ createIterationCompletionEvents: typeof createIterationCompletionEvents;
904
+ createIterationObservationEvents: typeof createIterationObservationEvents;
900
905
  createRealtimeStatsEvents: typeof createRealtimeStatsEvents;
901
906
  createRunResultEvents: typeof createRunResultEvents;
902
907
  createReportingEvent: typeof createReportingEvent;
@@ -913,6 +918,7 @@ export declare const __loadstrikeTestExports: {
913
918
  normalizeStringMap: typeof normalizeStringMap;
914
919
  optionNumber: typeof optionNumber;
915
920
  optionString: typeof optionString;
921
+ portalEventPayload: typeof portalEventPayload;
916
922
  pickBooleanValue: typeof pickBooleanValue;
917
923
  pickRecordValue: typeof pickRecordValue;
918
924
  postWithTimeout: typeof postWithTimeout;
@@ -1072,6 +1072,8 @@ declare function validateTrackingSelectorPath(expression: string, fieldName: "Tr
1072
1072
  declare function validateHttpEndpoint(endpoint: EndpointDefinition, mode: EndpointMode, hasModeDelegate: boolean): void;
1073
1073
  declare function applyHttpAuthHeaders(headers: Record<string, string>, options: HttpEndpointOptions, resolveOAuthToken: () => Promise<string>): Promise<void>;
1074
1074
  declare function buildHttpRequestBody(body: unknown, bodyType: NonNullable<HttpEndpointOptions["bodyType"]>, contentType: string): unknown;
1075
+ declare function extractJsonPath(value: unknown, arrayPath?: string): unknown;
1076
+ declare function headersToRecord(value: Headers): Record<string, string>;
1075
1077
  declare function partitionFromKey(value: string, partitionCount: number): string;
1076
1078
  declare function attachPayloadHelpers(payload: TrackingPayload): TrackingPayload;
1077
1079
  declare function attachDotNetTrackingPayloadAliases<T extends TrackingPayload>(payload: T): T;
@@ -1142,8 +1144,10 @@ export declare const __loadstrikeTestExports: {
1142
1144
  createNatsHeaders: typeof createNatsHeaders;
1143
1145
  createRedisStreamPayload: typeof createRedisStreamPayload;
1144
1146
  deserializeBrokerPayloadBody: typeof deserializeBrokerPayloadBody;
1147
+ extractJsonPath: typeof extractJsonPath;
1145
1148
  extractTrackingValue: typeof extractTrackingValue;
1146
1149
  fromKafkaHeaders: typeof fromKafkaHeaders;
1150
+ headersToRecord: typeof headersToRecord;
1147
1151
  headerValue: typeof headerValue;
1148
1152
  attachDotNetTrackingPayloadAliases: typeof attachDotNetTrackingPayloadAliases;
1149
1153
  attachPayloadHelpers: typeof attachPayloadHelpers;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loadstrike/loadstrike-sdk",
3
- "version": "1.0.30401",
3
+ "version": "1.0.31001",
4
4
  "description": "TypeScript and JavaScript SDK for in-process load execution, traffic correlation, and reporting.",
5
5
  "keywords": [
6
6
  "load-testing",
@@ -81,7 +81,7 @@
81
81
  "typescript": "^5.9.2"
82
82
  },
83
83
  "overrides": {
84
- "brace-expansion": "5.0.7",
84
+ "brace-expansion": "5.0.8",
85
85
  "tar": "7.5.21"
86
86
  }
87
87
  }