@loadstrike/loadstrike-sdk 1.0.22801 → 1.0.23001

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/local.js CHANGED
@@ -5,7 +5,6 @@ import { createHash, createVerify, randomUUID } from "node:crypto";
5
5
  import { CorrelationStoreConfiguration, CrossPlatformTrackingRuntime, RedisCorrelationStoreOptions, RedisCorrelationStore, TrackingFieldSelector } from "./correlation.js";
6
6
  import { EndpointAdapterFactory, LOADSTRIKE_TRACE_ID_TRACKING_FIELD } from "./transports.js";
7
7
  const DEFAULT_LICENSING_API_BASE_URL = "https://licensing.loadstrike.com";
8
- const INTERNAL_BLACKBOX_LICENSING_API_BASE_URL_ENVIRONMENT_VARIABLE = "LOADSTRIKE_INTERNAL_BLACKBOX_API_BASE_URL";
9
8
  let developmentLicensingApiBaseUrlOverride;
10
9
  const BUILT_IN_WORKER_PLUGIN_NAMES = new Set([
11
10
  "loadstrike failed responses",
@@ -27,7 +26,8 @@ const TRACKING_FEATURE_BY_KIND = {
27
26
  pushdiffusion: "endpoint.push_diffusion",
28
27
  delegatestream: "endpoint.delegate_stream",
29
28
  nats: "endpoint.nats",
30
- redisstreams: "endpoint.redis_streams"
29
+ redisstreams: "endpoint.redis_streams",
30
+ sqs: "endpoint.sqs"
31
31
  };
32
32
  const CI_ENVIRONMENT_VARIABLES = [
33
33
  "GITHUB_ACTIONS",
@@ -466,25 +466,8 @@ function normalizeLicensingApiBaseUrl(value) {
466
466
  const normalized = (value ?? "").trim();
467
467
  return normalized || DEFAULT_LICENSING_API_BASE_URL;
468
468
  }
469
- function resolveInternalBlackboxLicensingApiBaseUrlOverride(value) {
470
- const normalized = String(value ?? "").trim();
471
- if (!normalized) {
472
- return undefined;
473
- }
474
- try {
475
- const parsed = new URL(normalized);
476
- const host = parsed.hostname.replace(/^\[|\]$/g, "").toLowerCase();
477
- if (["http:", "https:"].includes(parsed.protocol) && ["127.0.0.1", "localhost", "::1"].includes(host)) {
478
- return normalized.replace(/\/+$/, "");
479
- }
480
- }
481
- catch {
482
- return undefined;
483
- }
484
- return undefined;
485
- }
486
469
  function resolveLicensingApiBaseUrl() {
487
- return normalizeLicensingApiBaseUrl(resolveInternalBlackboxLicensingApiBaseUrlOverride(process.env[INTERNAL_BLACKBOX_LICENSING_API_BASE_URL_ENVIRONMENT_VARIABLE]) ?? developmentLicensingApiBaseUrlOverride);
470
+ return normalizeLicensingApiBaseUrl(developmentLicensingApiBaseUrlOverride);
488
471
  }
489
472
  function setDevelopmentLicensingApiBaseUrlOverride(value) {
490
473
  developmentLicensingApiBaseUrlOverride = value;
@@ -523,6 +506,9 @@ function collectRequestedFeatures(request) {
523
506
  if (countCustomWorkerPlugins(context) > 0) {
524
507
  features.add("extensions.worker_plugins.custom");
525
508
  }
509
+ if (asList(pickValue(context, "RuntimePolicies", "runtimePolicies")).length > 0) {
510
+ features.add("policy.runtime_controls");
511
+ }
526
512
  const reportingSinks = asList(pickValue(context, "ReportingSinks", "reportingSinks"));
527
513
  let hasCustomSink = false;
528
514
  for (const sink of reportingSinks) {
@@ -818,159 +804,171 @@ async function evaluateScenarioOutcome(scenario, requestCount, context) {
818
804
  validateTrackingConfiguration(tracking, sourceEndpoint, destinationEndpoint);
819
805
  const sourceAdapter = EndpointAdapterFactory.create(sourceEndpoint);
820
806
  const destinationAdapter = destinationEndpoint ? EndpointAdapterFactory.create(destinationEndpoint) : null;
821
- const correlationTimeoutOverride = pickValue(tracking, "CorrelationTimeoutMs", "correlationTimeoutMs");
822
- const correlationTimeoutSeconds = pickValue(tracking, "CorrelationTimeoutSeconds", "correlationTimeoutSeconds", "CorrelationTimeout");
823
- const timeoutMs = correlationTimeoutOverride != null && String(correlationTimeoutOverride).trim() !== ""
824
- ? asInt(correlationTimeoutOverride)
825
- : Math.trunc((correlationTimeoutSeconds == null || String(correlationTimeoutSeconds).trim() === ""
826
- ? 30
827
- : asNumber(correlationTimeoutSeconds)) * 1000);
828
- const timeoutCountsAsFailure = toBoolean(pickValue(tracking, "TimeoutCountsAsFailure", "timeoutCountsAsFailure"), true);
829
- const correlationStore = mapCorrelationStore(tracking, buildTrackingRunNamespace(stringOrDefault(pickValue(context, "SessionId", "sessionId"), "session"), stringOrDefault(pickValue(scenario, "Name", "name"), "scenario"), sourceEndpoint.name, destinationEndpoint?.name));
830
- const timeoutSweepIntervalOverride = pickValue(tracking, "TimeoutSweepIntervalMs", "timeoutSweepIntervalMs");
831
- const timeoutSweepIntervalSeconds = pickValue(tracking, "TimeoutSweepIntervalSeconds", "timeoutSweepIntervalSeconds");
832
- const timeoutSweepIntervalMs = timeoutSweepIntervalOverride != null && String(timeoutSweepIntervalOverride).trim() !== ""
833
- ? asInt(timeoutSweepIntervalOverride)
834
- : Math.trunc((timeoutSweepIntervalSeconds == null || String(timeoutSweepIntervalSeconds).trim() === ""
835
- ? 1
836
- : asNumber(timeoutSweepIntervalSeconds)) * 1000);
837
- const timeoutBatchSizeValue = pickValue(tracking, "TimeoutBatchSize", "timeoutBatchSize");
838
- const timeoutBatchSize = timeoutBatchSizeValue == null || String(timeoutBatchSizeValue).trim() === ""
839
- ? 200
840
- : asInt(timeoutBatchSizeValue);
841
- const runtime = new CrossPlatformTrackingRuntime({
842
- sourceTrackingField: sourceEndpoint.trackingField,
843
- destinationTrackingField: destinationEndpoint?.trackingField,
844
- destinationGatherByField: destinationEndpoint?.gatherByField,
845
- trackingFieldValueCaseSensitive: toBoolean(pickValue(tracking, "TrackingFieldValueCaseSensitive", "trackingFieldValueCaseSensitive"), true),
846
- gatherByFieldValueCaseSensitive: toBoolean(pickValue(tracking, "GatherByFieldValueCaseSensitive", "gatherByFieldValueCaseSensitive"), true),
847
- correlationTimeoutMs: timeoutMs,
848
- timeoutCountsAsFailure,
849
- store: correlationStore ?? undefined
850
- });
851
- const runMode = stringOrDefault(pickValue(tracking, "RunMode", "runMode"), "GenerateAndCorrelate").trim().toLowerCase();
852
- const sourceOnlyMode = !destinationEndpoint;
853
- const restartOnFail = toBoolean(pickValue(scenario, "RestartIterationOnFail", "restartIterationOnFail"), false);
854
- const restartMaxAttempts = Math.max(asInt(pickValue(context, "RestartIterationMaxAttempts", "restartIterationMaxAttempts")), 0);
855
- const maxFailCount = Math.max(asInt(pickValue(scenario, "MaxFailCount", "maxFailCount")), 0);
856
- const scenarioCompletionTimeoutSeconds = Math.max(asNumber(pickValue(context, "ScenarioCompletionTimeoutSeconds", "scenarioCompletionTimeoutSeconds")), 0);
857
- const scenarioDeadlineMs = scenarioCompletionTimeoutSeconds > 0
858
- ? Date.now() + Math.trunc(scenarioCompletionTimeoutSeconds * 1000)
859
- : Number.POSITIVE_INFINITY;
860
- let okCount = 0;
861
- let failCount = 0;
862
- let nowMs = Date.now();
863
- let processedIterations = 0;
864
- let lastSweepAtMs = nowMs;
865
- for (let i = 0; i < requestCount; i += 1) {
866
- if (Date.now() > scenarioDeadlineMs) {
867
- break;
868
- }
869
- let iterationComplete = false;
870
- let attempts = 0;
871
- const maxAttempts = 1 + (restartOnFail ? restartMaxAttempts : 0);
872
- while (!iterationComplete) {
873
- attempts += 1;
807
+ let correlationStore = null;
808
+ try {
809
+ await sourceAdapter.initialize?.();
810
+ await destinationAdapter?.initialize?.();
811
+ const correlationTimeoutOverride = pickValue(tracking, "CorrelationTimeoutMs", "correlationTimeoutMs");
812
+ const correlationTimeoutSeconds = pickValue(tracking, "CorrelationTimeoutSeconds", "correlationTimeoutSeconds", "CorrelationTimeout");
813
+ const timeoutMs = correlationTimeoutOverride != null && String(correlationTimeoutOverride).trim() !== ""
814
+ ? asInt(correlationTimeoutOverride)
815
+ : Math.trunc((correlationTimeoutSeconds == null || String(correlationTimeoutSeconds).trim() === ""
816
+ ? 30
817
+ : asNumber(correlationTimeoutSeconds)) * 1000);
818
+ const timeoutCountsAsFailure = toBoolean(pickValue(tracking, "TimeoutCountsAsFailure", "timeoutCountsAsFailure"), true);
819
+ correlationStore = mapCorrelationStore(tracking, buildTrackingRunNamespace(stringOrDefault(pickValue(context, "SessionId", "sessionId"), "session"), stringOrDefault(pickValue(scenario, "Name", "name"), "scenario"), sourceEndpoint.name, destinationEndpoint?.name));
820
+ const timeoutSweepIntervalOverride = pickValue(tracking, "TimeoutSweepIntervalMs", "timeoutSweepIntervalMs");
821
+ const timeoutSweepIntervalSeconds = pickValue(tracking, "TimeoutSweepIntervalSeconds", "timeoutSweepIntervalSeconds");
822
+ const timeoutSweepIntervalMs = timeoutSweepIntervalOverride != null && String(timeoutSweepIntervalOverride).trim() !== ""
823
+ ? asInt(timeoutSweepIntervalOverride)
824
+ : Math.trunc((timeoutSweepIntervalSeconds == null || String(timeoutSweepIntervalSeconds).trim() === ""
825
+ ? 1
826
+ : asNumber(timeoutSweepIntervalSeconds)) * 1000);
827
+ const timeoutBatchSizeValue = pickValue(tracking, "TimeoutBatchSize", "timeoutBatchSize");
828
+ const timeoutBatchSize = timeoutBatchSizeValue == null || String(timeoutBatchSizeValue).trim() === ""
829
+ ? 200
830
+ : asInt(timeoutBatchSizeValue);
831
+ const runtime = new CrossPlatformTrackingRuntime({
832
+ sourceTrackingField: sourceEndpoint.trackingField,
833
+ destinationTrackingField: destinationEndpoint?.trackingField,
834
+ destinationGatherByField: destinationEndpoint?.gatherByField,
835
+ trackingFieldValueCaseSensitive: toBoolean(pickValue(tracking, "TrackingFieldValueCaseSensitive", "trackingFieldValueCaseSensitive"), true),
836
+ gatherByFieldValueCaseSensitive: toBoolean(pickValue(tracking, "GatherByFieldValueCaseSensitive", "gatherByFieldValueCaseSensitive"), true),
837
+ correlationTimeoutMs: timeoutMs,
838
+ timeoutCountsAsFailure,
839
+ store: correlationStore ?? undefined
840
+ });
841
+ const runMode = stringOrDefault(pickValue(tracking, "RunMode", "runMode"), "GenerateAndCorrelate").trim().toLowerCase();
842
+ const sourceOnlyMode = !destinationEndpoint;
843
+ const restartOnFail = toBoolean(pickValue(scenario, "RestartIterationOnFail", "restartIterationOnFail"), false);
844
+ const restartMaxAttempts = Math.max(asInt(pickValue(context, "RestartIterationMaxAttempts", "restartIterationMaxAttempts")), 0);
845
+ const maxFailCount = Math.max(asInt(pickValue(scenario, "MaxFailCount", "maxFailCount")), 0);
846
+ const scenarioCompletionTimeoutSeconds = Math.max(asNumber(pickValue(context, "ScenarioCompletionTimeoutSeconds", "scenarioCompletionTimeoutSeconds")), 0);
847
+ const scenarioDeadlineMs = scenarioCompletionTimeoutSeconds > 0
848
+ ? Date.now() + Math.trunc(scenarioCompletionTimeoutSeconds * 1000)
849
+ : Number.POSITIVE_INFINITY;
850
+ let okCount = 0;
851
+ let failCount = 0;
852
+ let nowMs = Date.now();
853
+ let processedIterations = 0;
854
+ let lastSweepAtMs = nowMs;
855
+ for (let i = 0; i < requestCount; i += 1) {
874
856
  if (Date.now() > scenarioDeadlineMs) {
875
- iterationComplete = true;
876
857
  break;
877
858
  }
878
- let sourcePayload = runMode === "correlateexistingtraffic"
879
- ? await sourceAdapter.consume()
880
- : await sourceAdapter.produce();
881
- sourcePayload = normalizePayload(sourcePayload, sourceEndpoint, i);
882
- const sourceTrackingId = sourceOnlyMode
883
- ? readTrackingId(sourcePayload, sourceEndpoint.trackingField)
884
- : await runtime.onSourceProduced(sourcePayload, nowMs);
885
- if (!sourceTrackingId) {
886
- if (restartOnFail && attempts < maxAttempts) {
859
+ let iterationComplete = false;
860
+ let attempts = 0;
861
+ const maxAttempts = 1 + (restartOnFail ? restartMaxAttempts : 0);
862
+ while (!iterationComplete) {
863
+ attempts += 1;
864
+ if (Date.now() > scenarioDeadlineMs) {
865
+ iterationComplete = true;
866
+ break;
867
+ }
868
+ let sourcePayload = runMode === "correlateexistingtraffic"
869
+ ? await sourceAdapter.consume()
870
+ : await sourceAdapter.produce();
871
+ sourcePayload = normalizePayload(sourcePayload, sourceEndpoint, i);
872
+ const sourceTrackingId = sourceOnlyMode
873
+ ? readTrackingId(sourcePayload, sourceEndpoint.trackingField)
874
+ : await runtime.onSourceProduced(sourcePayload, nowMs);
875
+ if (!sourceTrackingId) {
876
+ if (restartOnFail && attempts < maxAttempts) {
877
+ nowMs += 1;
878
+ continue;
879
+ }
880
+ failCount += 1;
881
+ iterationComplete = true;
887
882
  nowMs += 1;
888
- continue;
883
+ break;
889
884
  }
890
- failCount += 1;
891
- iterationComplete = true;
892
- nowMs += 1;
893
- break;
894
- }
895
- if (sourceOnlyMode || !destinationAdapter || !destinationEndpoint) {
896
- okCount += 1;
897
- iterationComplete = true;
898
- nowMs += 1;
899
- break;
900
- }
901
- let destinationPayload = await destinationAdapter.consume();
902
- destinationPayload = normalizePayload(destinationPayload, destinationEndpoint, i);
903
- let matched = await runtime.onDestinationConsumed(destinationPayload, nowMs + 1);
904
- const correlationDeadlineMs = nowMs + timeoutMs;
905
- while (!matched && Date.now() <= scenarioDeadlineMs && nowMs < correlationDeadlineMs) {
906
- const remainingTimeoutMs = correlationDeadlineMs - nowMs;
907
- if (remainingTimeoutMs <= 0) {
885
+ if (sourceOnlyMode || !destinationAdapter || !destinationEndpoint) {
886
+ okCount += 1;
887
+ iterationComplete = true;
888
+ nowMs += 1;
908
889
  break;
909
890
  }
910
- await sleep(Math.min(destinationEndpoint.pollIntervalMs ?? 250, remainingTimeoutMs));
911
- destinationPayload = normalizePayload(await destinationAdapter.consume(), destinationEndpoint, i);
912
- matched = await runtime.onDestinationConsumed(destinationPayload, nowMs + 1);
891
+ let destinationPayload = await destinationAdapter.consume();
892
+ destinationPayload = normalizePayload(destinationPayload, destinationEndpoint, i);
893
+ let matched = await runtime.onDestinationConsumed(destinationPayload, nowMs + 1);
894
+ const correlationDeadlineMs = nowMs + timeoutMs;
895
+ while (!matched && Date.now() <= scenarioDeadlineMs && nowMs < correlationDeadlineMs) {
896
+ const remainingTimeoutMs = correlationDeadlineMs - nowMs;
897
+ if (remainingTimeoutMs <= 0) {
898
+ break;
899
+ }
900
+ await sleep(Math.min(destinationEndpoint.pollIntervalMs ?? 250, remainingTimeoutMs));
901
+ destinationPayload = normalizePayload(await destinationAdapter.consume(), destinationEndpoint, i);
902
+ matched = await runtime.onDestinationConsumed(destinationPayload, nowMs + 1);
903
+ if (matched) {
904
+ break;
905
+ }
906
+ nowMs += Math.max(destinationEndpoint.pollIntervalMs ?? 250, 1);
907
+ }
913
908
  if (matched) {
914
- break;
909
+ okCount += 1;
910
+ iterationComplete = true;
915
911
  }
916
- nowMs += Math.max(destinationEndpoint.pollIntervalMs ?? 250, 1);
917
- }
918
- if (matched) {
919
- okCount += 1;
920
- iterationComplete = true;
921
- }
922
- else if (restartOnFail && attempts < maxAttempts) {
923
- nowMs += 2;
924
- continue;
925
- }
926
- else {
927
- await runtime.sweepTimeouts(nowMs + timeoutMs + 1, timeoutBatchSize || undefined);
928
- failCount += 1;
929
- iterationComplete = true;
930
- }
931
- nowMs += 2;
932
- }
933
- processedIterations += 1;
934
- if (timeoutSweepIntervalMs > 0 && nowMs - lastSweepAtMs >= timeoutSweepIntervalMs) {
935
- const swept = await runtime.sweepTimeouts(nowMs, timeoutBatchSize || undefined);
936
- if (swept > 0) {
937
- if (timeoutCountsAsFailure) {
938
- failCount += swept;
912
+ else if (restartOnFail && attempts < maxAttempts) {
913
+ nowMs += 2;
914
+ continue;
939
915
  }
940
916
  else {
941
- okCount += swept;
917
+ await runtime.sweepTimeouts(nowMs + timeoutMs + 1, timeoutBatchSize || undefined);
918
+ failCount += 1;
919
+ iterationComplete = true;
942
920
  }
921
+ nowMs += 2;
922
+ }
923
+ processedIterations += 1;
924
+ if (timeoutSweepIntervalMs > 0 && nowMs - lastSweepAtMs >= timeoutSweepIntervalMs) {
925
+ const swept = await runtime.sweepTimeouts(nowMs, timeoutBatchSize || undefined);
926
+ if (swept > 0) {
927
+ if (timeoutCountsAsFailure) {
928
+ failCount += swept;
929
+ }
930
+ else {
931
+ okCount += swept;
932
+ }
933
+ }
934
+ lastSweepAtMs = nowMs;
935
+ }
936
+ if (maxFailCount > 0 && failCount >= maxFailCount) {
937
+ break;
943
938
  }
944
- lastSweepAtMs = nowMs;
945
- }
946
- if (maxFailCount > 0 && failCount >= maxFailCount) {
947
- break;
948
939
  }
949
- }
950
- let expired = 0;
951
- let sweptChunk = 0;
952
- do {
953
- sweptChunk = await runtime.sweepTimeouts(nowMs + timeoutMs + 1, timeoutBatchSize || undefined);
954
- expired += sweptChunk;
955
- } while (timeoutBatchSize > 0 && sweptChunk >= timeoutBatchSize);
956
- if (expired > 0) {
957
- if (timeoutCountsAsFailure) {
958
- failCount += expired;
940
+ let expired = 0;
941
+ let sweptChunk = 0;
942
+ do {
943
+ sweptChunk = await runtime.sweepTimeouts(nowMs + timeoutMs + 1, timeoutBatchSize || undefined);
944
+ expired += sweptChunk;
945
+ } while (timeoutBatchSize > 0 && sweptChunk >= timeoutBatchSize);
946
+ if (expired > 0) {
947
+ if (timeoutCountsAsFailure) {
948
+ failCount += expired;
949
+ }
950
+ else {
951
+ okCount += expired;
952
+ }
959
953
  }
960
- else {
961
- okCount += expired;
954
+ if (processedIterations < requestCount && Date.now() > scenarioDeadlineMs) {
955
+ const timedOutIterations = requestCount - processedIterations;
956
+ if (timeoutCountsAsFailure) {
957
+ failCount += timedOutIterations;
958
+ }
959
+ else {
960
+ okCount += timedOutIterations;
961
+ }
962
962
  }
963
+ return { requestCount: processedIterations, okCount, failCount };
963
964
  }
964
- if (processedIterations < requestCount && Date.now() > scenarioDeadlineMs) {
965
- const timedOutIterations = requestCount - processedIterations;
966
- if (timeoutCountsAsFailure) {
967
- failCount += timedOutIterations;
968
- }
969
- else {
970
- okCount += timedOutIterations;
971
- }
965
+ finally {
966
+ await Promise.allSettled([
967
+ sourceAdapter.dispose?.(),
968
+ destinationAdapter?.dispose?.(),
969
+ correlationStore?.close?.()
970
+ ].filter((value) => Boolean(value)));
972
971
  }
973
- return { requestCount: processedIterations, okCount, failCount };
974
972
  }
975
973
  function validateTrackingConfiguration(tracking, sourceEndpoint, destinationEndpoint) {
976
974
  if (sourceEndpoint.gatherByField?.trim()) {
@@ -6606,6 +6606,7 @@ function mapRuntimeTrackingEndpointSpec(spec, useLoadStrikeTraceIdHeader = false
6606
6606
  nats: asTrackingRecord(pickTrackingValue(spec, "Nats", "nats")),
6607
6607
  redisStreams: asTrackingRecord(pickTrackingValue(spec, "RedisStreams", "redisStreams")),
6608
6608
  azureEventHubs: asTrackingRecord(pickTrackingValue(spec, "AzureEventHubs", "azureEventHubs")),
6609
+ sqs: asTrackingRecord(pickTrackingValue(spec, "Sqs", "sqs")),
6609
6610
  pushDiffusion: asTrackingRecord(pickTrackingValue(spec, "PushDiffusion", "pushDiffusion")),
6610
6611
  delegate: typeof delegateProduce === "function"
6611
6612
  || typeof delegateConsume === "function"