@loadstrike/loadstrike-sdk 1.0.31601 → 1.0.32601

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.
Files changed (33) hide show
  1. package/README.md +14 -2
  2. package/dist/cjs/internal/prometheus-remote-write.js +37 -0
  3. package/dist/cjs/internal/reporting-sink-http-error.js +17 -0
  4. package/dist/cjs/internal/vendor-metric-payloads.js +390 -0
  5. package/dist/cjs/iteration-observations.js +24 -8
  6. package/dist/cjs/local.js +48 -63
  7. package/dist/cjs/reporting-containment.js +242 -0
  8. package/dist/cjs/runtime.js +83 -3
  9. package/dist/cjs/sinks.js +1337 -38
  10. package/dist/cjs/transports.js +1339 -151
  11. package/dist/esm/internal/prometheus-remote-write.js +31 -0
  12. package/dist/esm/internal/reporting-sink-http-error.js +13 -0
  13. package/dist/esm/internal/vendor-metric-payloads.js +382 -0
  14. package/dist/esm/iteration-observations.js +24 -8
  15. package/dist/esm/local.js +49 -64
  16. package/dist/esm/reporting-containment.js +238 -0
  17. package/dist/esm/runtime.js +85 -5
  18. package/dist/esm/sinks.js +1334 -35
  19. package/dist/esm/transports.js +1335 -151
  20. package/dist/types/contracts.d.ts +1 -0
  21. package/dist/types/index.d.ts +1 -1
  22. package/dist/types/internal/prometheus-remote-write.d.ts +2 -0
  23. package/dist/types/internal/reporting-sink-http-error.d.ts +6 -0
  24. package/dist/types/internal/vendor-metric-payloads.d.ts +48 -0
  25. package/dist/types/local.d.ts +0 -6
  26. package/dist/types/reporting-containment.d.ts +2 -0
  27. package/dist/types/runtime.d.ts +1 -0
  28. package/dist/types/sinks.d.ts +134 -17
  29. package/dist/types/transports.d.ts +2 -0
  30. package/package.json +9 -3
  31. package/dist/cjs/internal-build.js +0 -4
  32. package/dist/esm/internal-build.js +0 -1
  33. package/dist/types/internal-build.d.ts +0 -1
package/dist/cjs/local.js CHANGED
@@ -55,9 +55,8 @@ const childProcess = __importStar(require("node:child_process"));
55
55
  const node_crypto_1 = require("node:crypto");
56
56
  const correlation_js_1 = require("./correlation.js");
57
57
  const transports_js_1 = require("./transports.js");
58
- const internal_build_js_1 = require("./internal-build.js");
58
+ const reporting_containment_js_1 = require("./reporting-containment.js");
59
59
  const DEFAULT_LICENSING_API_BASE_URL = "https://licensing.loadstrike.com";
60
- let developmentLicensingApiBaseUrlOverride;
61
60
  const BUILT_IN_WORKER_PLUGIN_NAMES = new Set([
62
61
  "loadstrike failed responses",
63
62
  "loadstrike correlation"
@@ -111,7 +110,7 @@ class LoadStrikeLocalClient {
111
110
  _LoadStrikeLocalClient_signingKeyCache.set(this, new Map());
112
111
  _LoadStrikeLocalClient_heartbeatDrains.set(this, new WeakMap());
113
112
  assertNoDisableLicenseEnforcementOption(options, "LoadStrikeLocalClient");
114
- __classPrivateFieldSet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, resolveLicensingApiBaseUrl(), "f");
113
+ __classPrivateFieldSet(this, _LoadStrikeLocalClient_licensingApiBaseUrl, DEFAULT_LICENSING_API_BASE_URL, "f");
115
114
  this.licenseValidationTimeoutMs = normalizeTimeoutMs(options.licenseValidationTimeoutMs);
116
115
  }
117
116
  portalReportingIngestUrl() {
@@ -119,6 +118,7 @@ class LoadStrikeLocalClient {
119
118
  }
120
119
  async run(request) {
121
120
  const sanitized = sanitizeRequest(request);
121
+ validateContainmentRequest(sanitized);
122
122
  const licenseSession = await this.acquireLicenseLease(sanitized);
123
123
  try {
124
124
  const createdUtc = new Date();
@@ -574,66 +574,27 @@ function sanitizeRequest(request) {
574
574
  RunArgs: runArgs
575
575
  };
576
576
  }
577
- function normalizeLicensingApiBaseUrl(value) {
578
- const normalized = (value ?? "").trim();
579
- return normalized || DEFAULT_LICENSING_API_BASE_URL;
580
- }
581
- function resolveLicensingApiBaseUrl() {
582
- return normalizeLicensingApiBaseUrl(developmentLicensingApiBaseUrlOverride ?? resolveInternalBlackBoxLicensingApiBaseUrl());
583
- }
584
- function setDevelopmentLicensingApiBaseUrlOverride(value) {
585
- if (value != null && value.trim()) {
586
- assertInternalTestHarnessLicensingOverride();
587
- const normalized = normalizeLicensingApiBaseUrl(value);
588
- if (!isLoopbackHttpBaseUrl(normalized)) {
589
- throw new TypeError("Internal development licensing API overrides must use a loopback http URL.");
577
+ function validateContainmentRequest(request) {
578
+ const scenarios = Array.isArray(request.Scenarios) ? request.Scenarios : [];
579
+ for (const scenarioValue of scenarios) {
580
+ const scenario = asRecord(scenarioValue);
581
+ const tracking = asRecord(pickValue(scenario, "Tracking", "tracking"));
582
+ for (const field of ["Source", "Destination"]) {
583
+ const endpoint = asRecord(pickValue(tracking, field, field.toLowerCase()));
584
+ if (Object.keys(endpoint).length > 0) {
585
+ (0, transports_js_1.validateNativeEndpointExecutionSupport)(endpoint);
586
+ }
590
587
  }
591
- developmentLicensingApiBaseUrlOverride = normalized;
592
- return;
593
- }
594
- if (value != null) {
595
- assertInternalTestHarnessLicensingOverride();
596
- }
597
- developmentLicensingApiBaseUrlOverride = undefined;
598
- }
599
- function assertInternalTestHarnessLicensingOverride() {
600
- if (isInternalTestHarnessCall()) {
601
- return;
602
- }
603
- throw new TypeError("Internal development licensing API override is available only to LoadStrike SDK tests.");
604
- }
605
- function isInternalTestHarnessCall() {
606
- const stack = String(new Error().stack ?? "").replace(/\\/g, "/").toLowerCase();
607
- return stack.includes("/sdk/ts/tests/");
608
- }
609
- function isLoopbackHttpBaseUrl(value) {
610
- let parsed;
611
- try {
612
- parsed = new URL(value);
613
- }
614
- catch {
615
- return false;
616
- }
617
- if (parsed.protocol !== "http:") {
618
- return false;
619
588
  }
620
- const host = parsed.hostname.trim().toLowerCase();
621
- return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1" || host.endsWith(".localhost");
622
- }
623
- function resolveInternalBlackBoxLicensingApiBaseUrl() {
624
- const compiledToken = String(internal_build_js_1.BLACKBOX_LICENSING_API_BASE_URL_TOKEN ?? "").trim();
625
- if (!compiledToken) {
626
- return undefined;
627
- }
628
- const envToken = String(process.env.LOADSTRIKE_INTERNAL_BLACKBOX_API_BASE_URL_TOKEN ?? "").trim();
629
- if (!envToken || envToken !== compiledToken) {
630
- return undefined;
631
- }
632
- const candidate = normalizeLicensingApiBaseUrl(process.env.LOADSTRIKE_INTERNAL_BLACKBOX_API_BASE_URL);
633
- if (!isLoopbackHttpBaseUrl(candidate)) {
634
- return undefined;
589
+ const context = asRecord(request.Context);
590
+ const reportingSinks = asList(pickValue(context, "ReportingSinks", "reportingSinks"));
591
+ for (const [key, value] of Object.entries(context)) {
592
+ const normalizedKey = key.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
593
+ if (normalizedKey === "infraconfig") {
594
+ (0, reporting_containment_js_1.assertNoUnsupportedReportingInfraConfig)(value, reportingSinks);
595
+ }
635
596
  }
636
- return candidate;
597
+ (0, reporting_containment_js_1.assertNoUnsupportedReportingSinkGraph)(reportingSinks);
637
598
  }
638
599
  function normalizeTimeoutMs(value) {
639
600
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
@@ -1341,6 +1302,20 @@ function mapEndpointSpec(spec, useLoadStrikeTraceIdHeader = false) {
1341
1302
  redisStreams: asRecord(pickValue(spec, "RedisStreams", "redisStreams")),
1342
1303
  azureEventHubs: asRecord(pickValue(spec, "AzureEventHubs", "azureEventHubs")),
1343
1304
  pushDiffusion: asRecord(pickValue(spec, "PushDiffusion", "pushDiffusion")),
1305
+ grpc: mapEndpointProtocolOptions(spec, ["Grpc", "grpc"], [
1306
+ "Target", "target", "ServiceName", "serviceName", "MethodName", "methodName",
1307
+ "MethodType", "methodType", "Deadline", "deadline", "DeadlineSeconds", "deadlineSeconds",
1308
+ "DeadlineMs", "deadlineMs", "Metadata", "metadata", "Produce", "produce", "Consume", "consume",
1309
+ "ProduceAsync", "produceAsync", "ConsumeAsync", "consumeAsync", "ConnectionMetadata", "connectionMetadata",
1310
+ "NativeClient", "nativeClient"
1311
+ ]),
1312
+ webSocket: mapEndpointProtocolOptions(spec, ["WebSocket", "webSocket"], [
1313
+ "Url", "url", "Subprotocols", "subprotocols", "ConnectTimeout", "connectTimeout",
1314
+ "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeoutMs", "connectTimeoutMs",
1315
+ "CloseTimeout", "closeTimeout", "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeoutMs", "closeTimeoutMs",
1316
+ "Produce", "produce", "Consume", "consume", "ProduceAsync", "produceAsync", "ConsumeAsync", "consumeAsync",
1317
+ "ConnectionMetadata", "connectionMetadata", "NativeClient", "nativeClient"
1318
+ ]),
1344
1319
  delegate: (typeof delegateProduce === "function"
1345
1320
  || typeof delegateConsume === "function"
1346
1321
  || typeof delegateProduceAsync === "function"
@@ -1363,6 +1338,19 @@ function mapEndpointSpec(spec, useLoadStrikeTraceIdHeader = false) {
1363
1338
  : undefined
1364
1339
  };
1365
1340
  }
1341
+ function mapEndpointProtocolOptions(spec, nestedKeys, flatKeys) {
1342
+ const nested = asRecord(pickValue(spec, ...nestedKeys));
1343
+ if (Object.keys(nested).length > 0) {
1344
+ return nested;
1345
+ }
1346
+ const options = {};
1347
+ for (const key of flatKeys) {
1348
+ if (Object.prototype.hasOwnProperty.call(spec, key)) {
1349
+ options[key] = spec[key];
1350
+ }
1351
+ }
1352
+ return options;
1353
+ }
1366
1354
  function normalizePayload(payload, endpoint, index) {
1367
1355
  const resolvedBody = payload?.body ?? endpoint.messagePayload;
1368
1356
  const normalized = {
@@ -2319,8 +2307,6 @@ exports.__loadstrikeTestExports = {
2319
2307
  mapEnvironmentClassification,
2320
2308
  mapEndpointSpec,
2321
2309
  normalizeEndpointPayloadValue,
2322
- normalizeLicensingApiBaseUrl,
2323
- resolveLicensingApiBaseUrl,
2324
2310
  normalizeNodeType,
2325
2311
  normalizePayload,
2326
2312
  normalizeStringArray,
@@ -2339,7 +2325,6 @@ exports.__loadstrikeTestExports = {
2339
2325
  sanitizeLooseJson,
2340
2326
  sanitizeRequest,
2341
2327
  setJsonPathValue,
2342
- setDevelopmentLicensingApiBaseUrlOverride,
2343
2328
  sleep,
2344
2329
  stringOrDefault,
2345
2330
  toBoolean,
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertNoUnsupportedReportingInfraConfig = assertNoUnsupportedReportingInfraConfig;
4
+ exports.assertNoUnsupportedReportingSinkGraph = assertNoUnsupportedReportingSinkGraph;
5
+ const UNBOUND_EXPANDED_SECTIONS = [
6
+ ["PrometheusRemoteWrite", "PrometheusRemoteWriteReportingSink"],
7
+ ["CloudWatch", "CloudWatchReportingSink"],
8
+ ["Dynatrace", "DynatraceReportingSink"],
9
+ ["NewRelic", "NewRelicReportingSink"],
10
+ ["Elasticsearch", "ElasticsearchReportingSink"],
11
+ ["OpenSearch", "OpenSearchReportingSink"],
12
+ ["Kafka", "KafkaReportingSink"],
13
+ ["StatsD", "StatsDReportingSink"],
14
+ ["DogStatsD", "DogStatsDReportingSink"],
15
+ ["Netdata", "NetdataStatsDReportingSink"],
16
+ ["Jsonl", "JsonlFileReportingSink"],
17
+ ["Webhook", "GenericWebhookReportingSink"]
18
+ ];
19
+ function assertNoUnsupportedReportingInfraConfig(infraConfig, sinks = []) {
20
+ if (!isRecord(infraConfig)) {
21
+ return;
22
+ }
23
+ const selectedSections = selectedExpandedReportingSections(sinks);
24
+ for (const [sectionName, sinkType] of UNBOUND_EXPANDED_SECTIONS) {
25
+ if (selectedSections.has(sectionName)
26
+ && hasNonEmptyReportingSectionAlias(infraConfig, sectionName)) {
27
+ throw new Error(`Configuration section 'LoadStrike:ReportingSinks:${sectionName}' is not bound automatically by the TypeScript SDK. `
28
+ + `Construct ${sinkType} explicitly and pass its options directly.`);
29
+ }
30
+ }
31
+ }
32
+ function selectedExpandedReportingSections(sinks) {
33
+ const selected = new Set();
34
+ const visited = new Set();
35
+ const inspect = (sink) => {
36
+ if (!isRecord(sink) || visited.has(sink)) {
37
+ return;
38
+ }
39
+ visited.add(sink);
40
+ const identities = [
41
+ sink.licenseFeature,
42
+ sink.LicenseFeature,
43
+ sink.feature,
44
+ sink.Feature
45
+ ];
46
+ if (!hasReportingSinkLifecycle(sink)) {
47
+ identities.push(sink.kind, sink.Kind);
48
+ }
49
+ const normalizedIdentities = identities
50
+ .map(normalizeExpandedSinkIdentity)
51
+ .filter(Boolean);
52
+ for (const identity of normalizedIdentities) {
53
+ const sectionName = EXPANDED_SECTION_BY_IDENTITY.get(identity);
54
+ if (sectionName) {
55
+ selected.add(sectionName);
56
+ }
57
+ }
58
+ const children = Array.isArray(sink.sinks)
59
+ ? sink.sinks
60
+ : Array.isArray(sink.Sinks)
61
+ ? sink.Sinks
62
+ : [];
63
+ for (const child of children) {
64
+ inspect(child);
65
+ }
66
+ };
67
+ for (const sink of sinks) {
68
+ inspect(sink);
69
+ }
70
+ return selected;
71
+ }
72
+ const EXPANDED_SECTION_BY_IDENTITY = new Map([
73
+ ["prometheusremotewrite", "PrometheusRemoteWrite"],
74
+ ["cloudwatch", "CloudWatch"],
75
+ ["dynatrace", "Dynatrace"],
76
+ ["elasticsearch", "Elasticsearch"],
77
+ ["opensearch", "OpenSearch"],
78
+ ["newrelic", "NewRelic"],
79
+ ["webhook", "Webhook"],
80
+ ["genericwebhook", "Webhook"],
81
+ ["kafka", "Kafka"],
82
+ ["statsd", "StatsD"],
83
+ ["dogstatsd", "DogStatsD"],
84
+ ["netdata", "Netdata"],
85
+ ["netdatastatsd", "Netdata"],
86
+ ["jsonl", "Jsonl"],
87
+ ["jsonlfile", "Jsonl"]
88
+ ]);
89
+ function normalizeExpandedSinkIdentity(value) {
90
+ let normalized = normalizeIdentityToken(value);
91
+ const featurePrefix = "extensionsreportingsinks";
92
+ if (normalized.startsWith(featurePrefix)) {
93
+ normalized = normalized.slice(featurePrefix.length);
94
+ }
95
+ const classSuffix = "reportingsink";
96
+ if (normalized.endsWith(classSuffix)) {
97
+ normalized = normalized.slice(0, -classSuffix.length);
98
+ }
99
+ return normalized;
100
+ }
101
+ function assertNoUnsupportedReportingSinkGraph(sinks) {
102
+ const visited = new Set();
103
+ const active = new Set();
104
+ const inspect = (sink) => {
105
+ if (!isRecord(sink)) {
106
+ return;
107
+ }
108
+ if (active.has(sink)) {
109
+ throw new Error("Composite reporting sink graph cannot contain a cycle.");
110
+ }
111
+ if (visited.has(sink)) {
112
+ return;
113
+ }
114
+ active.add(sink);
115
+ const identityTokens = [sink.sinkName, sink.SinkName, sink.kind, sink.Kind]
116
+ .map(normalizeIdentityToken)
117
+ .filter(Boolean);
118
+ const featureTokens = [sink.licenseFeature, sink.LicenseFeature, sink.feature, sink.Feature]
119
+ .map((value) => String(value ?? "").trim().toLowerCase())
120
+ .filter(Boolean);
121
+ const hasLifecycle = hasReportingSinkLifecycle(sink);
122
+ const displayName = directVendorDisplayName(hasLifecycle ? [] : identityTokens, hasLifecycle ? [] : featureTokens);
123
+ if (displayName) {
124
+ throw unsupportedDirectVendorReportingError(displayName);
125
+ }
126
+ if (identityTokens.includes("composite") || identityTokens.includes("compositereportingsink")) {
127
+ const children = Array.isArray(sink.sinks)
128
+ ? sink.sinks
129
+ : Array.isArray(sink.Sinks)
130
+ ? sink.Sinks
131
+ : [];
132
+ for (const child of children) {
133
+ inspect(child);
134
+ }
135
+ }
136
+ active.delete(sink);
137
+ visited.add(sink);
138
+ };
139
+ for (const sink of sinks) {
140
+ inspect(sink);
141
+ }
142
+ }
143
+ function hasNonEmptyReportingSectionAlias(infraConfig, sectionName) {
144
+ const segments = ["LoadStrike", "ReportingSinks", sectionName];
145
+ const values = [
146
+ ...readDirectValuesCaseInsensitive(infraConfig, segments.join(":")),
147
+ ...readDirectValuesCaseInsensitive(infraConfig, segments.join(".")),
148
+ ...readNestedValuesCaseInsensitive(infraConfig, segments)
149
+ ];
150
+ return values.some(isNonEmptySection);
151
+ }
152
+ function readDirectValuesCaseInsensitive(record, key) {
153
+ const expected = key.toLowerCase();
154
+ return Object.entries(record)
155
+ .filter(([candidate]) => candidate.toLowerCase() === expected)
156
+ .map(([, value]) => value);
157
+ }
158
+ function readNestedValuesCaseInsensitive(record, segments) {
159
+ let current = [record];
160
+ for (const segment of segments) {
161
+ const next = [];
162
+ for (const candidate of current) {
163
+ if (isRecord(candidate)) {
164
+ next.push(...readDirectValuesCaseInsensitive(candidate, segment));
165
+ }
166
+ }
167
+ if (next.length === 0) {
168
+ return [];
169
+ }
170
+ current = next;
171
+ }
172
+ return current;
173
+ }
174
+ function isNonEmptySection(value) {
175
+ if (value == null) {
176
+ return false;
177
+ }
178
+ if (typeof value === "string") {
179
+ return value.trim().length > 0;
180
+ }
181
+ if (Array.isArray(value)) {
182
+ return value.length > 0;
183
+ }
184
+ if (isRecord(value)) {
185
+ return Object.keys(value).length > 0;
186
+ }
187
+ return true;
188
+ }
189
+ function directVendorDisplayName(identityTokens, featureTokens) {
190
+ if (identityTokens.includes("prometheusremotewrite")
191
+ || identityTokens.includes("prometheusremotewritereportingsink")
192
+ || featureTokens.includes("extensions.reporting_sinks.prometheus_remote_write")) {
193
+ return "Prometheus Remote Write";
194
+ }
195
+ if (identityTokens.includes("cloudwatch")
196
+ || identityTokens.includes("cloudwatchreportingsink")
197
+ || featureTokens.includes("extensions.reporting_sinks.cloudwatch")) {
198
+ return "CloudWatch";
199
+ }
200
+ if (identityTokens.includes("dynatrace")
201
+ || identityTokens.includes("dynatracereportingsink")
202
+ || featureTokens.includes("extensions.reporting_sinks.dynatrace")) {
203
+ return "Dynatrace";
204
+ }
205
+ if (identityTokens.includes("newrelic")
206
+ || identityTokens.includes("newrelicreportingsink")
207
+ || featureTokens.includes("extensions.reporting_sinks.new_relic")) {
208
+ return "New Relic";
209
+ }
210
+ return undefined;
211
+ }
212
+ function unsupportedDirectVendorReportingError(displayName) {
213
+ return new Error(`Direct ${displayName} reporting is not supported by the TypeScript SDK. `
214
+ + "Use GenericWebhookReportingSink through a converting gateway or a custom LoadStrikeReportingSink.");
215
+ }
216
+ function normalizeIdentityToken(value) {
217
+ return String(value ?? "")
218
+ .trim()
219
+ .toLowerCase()
220
+ .replace(/[^a-z0-9]+/g, "");
221
+ }
222
+ function hasReportingSinkLifecycle(sink) {
223
+ return [
224
+ "init",
225
+ "Init",
226
+ "start",
227
+ "Start",
228
+ "saveRealtimeStats",
229
+ "SaveRealtimeStats",
230
+ "saveRealtimeMetrics",
231
+ "SaveRealtimeMetrics",
232
+ "saveRunResult",
233
+ "SaveRunResult",
234
+ "saveIterationBatch",
235
+ "SaveIterationBatch",
236
+ "stop",
237
+ "Stop"
238
+ ].some((member) => typeof sink[member] === "function");
239
+ }
240
+ function isRecord(value) {
241
+ return value !== null && typeof value === "object" && !Array.isArray(value);
242
+ }
@@ -16,12 +16,15 @@ const reporting_js_1 = require("./reporting.js");
16
16
  const report_history_js_1 = require("./report-history.js");
17
17
  const local_report_input_js_1 = require("./local-report-input.js");
18
18
  const sinks_js_1 = require("./sinks.js");
19
+ const reporting_sink_http_error_js_1 = require("./internal/reporting-sink-http-error.js");
20
+ const reporting_containment_js_1 = require("./reporting-containment.js");
19
21
  const load_engine_v2_js_1 = require("./load-engine-v2.js");
20
22
  const iteration_observations_js_1 = require("./iteration-observations.js");
21
23
  const iteration_observation_diagnostics_js_1 = require("./iteration-observation-diagnostics.js");
22
24
  const sink_retry_policy_js_1 = require("./sink-retry-policy.js");
23
25
  const LOAD_ENGINE_V2_SCHEDULER_DISTRIBUTIONS = Symbol("loadstrike.load-engine-v2.scheduler-distributions");
24
26
  const PORTAL_RUN_TOKEN_PROVIDER = Symbol.for("loadstrike.internal.portal-run-token-provider");
27
+ const REPORTING_INTERVAL_SECONDS = Symbol.for("loadstrike.internal.reporting-interval-seconds");
25
28
  exports.LoadStrikeNodeType = {
26
29
  SingleNode: "SingleNode",
27
30
  Coordinator: "Coordinator",
@@ -747,6 +750,15 @@ class ScenarioStatsAccumulator {
747
750
  return attachScenarioStatsAliases(scenario);
748
751
  }
749
752
  }
753
+ function isRetryableReportingSinkError(sink, error) {
754
+ if (!(sink instanceof sinks_js_1.PrometheusRemoteWriteReportingSink)) {
755
+ return true;
756
+ }
757
+ if (!(error instanceof reporting_sink_http_error_js_1.ReportingSinkHttpError)) {
758
+ return true;
759
+ }
760
+ return error.status === 429 || error.status < 400 || error.status >= 500;
761
+ }
750
762
  class LoadStrikeResponse {
751
763
  /**
752
764
  * Creates a successful reply.
@@ -3495,6 +3507,8 @@ class LoadStrikeRunner {
3495
3507
  return this.buildContext();
3496
3508
  }
3497
3509
  async run(args = []) {
3510
+ (0, reporting_containment_js_1.assertNoUnsupportedReportingSinkGraph)(this.options.reportingSinks ?? []);
3511
+ assertNoUnsupportedNativeTrackingScenarios(this.scenarios);
3498
3512
  if (this.contextConfigurators.length) {
3499
3513
  return new LoadStrikeRunner(this.scenarios, this.buildContext().toRunnerOptions(), [], this.internalOptions).run(args);
3500
3514
  }
@@ -3506,7 +3520,7 @@ class LoadStrikeRunner {
3506
3520
  const scenarioAccumulators = new Map();
3507
3521
  const scenarioDurationsMs = new Map();
3508
3522
  const stepStats = new Map();
3509
- const sinks = (this.options.reportingSinks ?? []).map((sink) => (0, sinks_js_1.cloneReportingSinkForRun)(sink));
3523
+ const sinks = (this.options.reportingSinks ?? []).map((sink) => (0, sinks_js_1.cloneReportingSinkForRun)(sink, this.options.infraConfig ?? {}));
3510
3524
  const sinkStates = sinks.map((sink, index) => ({
3511
3525
  sink,
3512
3526
  disabled: false,
@@ -3631,6 +3645,12 @@ class LoadStrikeRunner {
3631
3645
  testInfo,
3632
3646
  getNodeInfo: () => attachNodeInfoAliases({ ...nodeInfo })
3633
3647
  };
3648
+ Object.defineProperty(baseContext, REPORTING_INTERVAL_SECONDS, {
3649
+ value: this.options.reportingIntervalSeconds ?? 5,
3650
+ enumerable: false,
3651
+ configurable: false,
3652
+ writable: false
3653
+ });
3634
3654
  attachBaseContextAliases(baseContext);
3635
3655
  const sinkSession = {
3636
3656
  startedUtc: createdUtc,
@@ -3685,6 +3705,7 @@ class LoadStrikeRunner {
3685
3705
  name: state.name,
3686
3706
  iterationObservationPortalSink: Boolean(state.sink.iterationObservationPortalSink),
3687
3707
  iterationObservationShapeLimited: Boolean(state.sink.iterationObservationShapeLimited),
3708
+ retryableErrorClassifier: (error) => isRetryableReportingSinkError(state.sink, error),
3688
3709
  saveIterationBatch: resolveSinkSaveIterationBatch(state.sink),
3689
3710
  completeIterationObservationStream: resolveSinkCompleteIterationObservationStream(state.sink)
3690
3711
  }));
@@ -4319,7 +4340,9 @@ class LoadStrikeRunner {
4319
4340
  return;
4320
4341
  }
4321
4342
  catch (error) {
4322
- const exhausted = attempts >= maximumAttempts;
4343
+ const retryable = isRetryableReportingSinkError(state.sink, error);
4344
+ const exhausted = attempts >= maximumAttempts || !retryable;
4345
+ const reportedMaximumAttempts = retryable ? maximumAttempts : attempts;
4323
4346
  const nextDelayMs = exhausted ? 0 : (0, sink_retry_policy_js_1.sinkRetryDelayMs)(backoffMs, attempts);
4324
4347
  (0, iteration_observation_diagnostics_js_1.logIterationObservationFailure)(logger, exhausted ? "error" : "warn", {
4325
4348
  sinkName: state.name,
@@ -4329,7 +4352,7 @@ class LoadStrikeRunner {
4329
4352
  resultOwnerId: "",
4330
4353
  observationCount: 0,
4331
4354
  attempt: attempts,
4332
- maximumAttempts,
4355
+ maximumAttempts: reportedMaximumAttempts,
4333
4356
  nextDelayMs
4334
4357
  }, error);
4335
4358
  if (exhausted) {
@@ -8896,6 +8919,7 @@ class ManagedScenarioTrackingRuntime {
8896
8919
  }
8897
8920
  async dispose() {
8898
8921
  this.shutdown = true;
8922
+ this.interruptEndpointAdapters();
8899
8923
  this.rejectOutstandingWaiters();
8900
8924
  await Promise.all([
8901
8925
  this.sourceLoop,
@@ -8961,6 +8985,7 @@ class ManagedScenarioTrackingRuntime {
8961
8985
  failureSeen = failureSeen || this.isFailedObservationOutcome(outcome);
8962
8986
  }
8963
8987
  this.shutdown = true;
8988
+ this.interruptEndpointAdapters();
8964
8989
  await Promise.all([
8965
8990
  this.sourceLoop,
8966
8991
  this.destinationLoop,
@@ -8977,6 +9002,20 @@ class ManagedScenarioTrackingRuntime {
8977
9002
  ? LoadStrikeResponse.fail("tracking_failures", "One or more observed source or destination events did not correlate successfully.", 0)
8978
9003
  : LoadStrikeResponse.ok("observed");
8979
9004
  }
9005
+ interruptEndpointAdapters() {
9006
+ try {
9007
+ this.sourceAdapter.interrupt?.();
9008
+ }
9009
+ catch {
9010
+ // Shutdown remains best-effort and still disposes every adapter below.
9011
+ }
9012
+ try {
9013
+ this.destinationAdapter?.interrupt?.();
9014
+ }
9015
+ catch {
9016
+ // Shutdown remains best-effort and still disposes every adapter below.
9017
+ }
9018
+ }
8980
9019
  async consumeSourceLoop() {
8981
9020
  while (!this.shutdown) {
8982
9021
  try {
@@ -9568,6 +9607,20 @@ function mapRuntimeTrackingEndpointSpec(spec, useLoadStrikeTraceIdHeader = false
9568
9607
  azureEventHubs: asTrackingRecord(pickTrackingValue(spec, "AzureEventHubs", "azureEventHubs")),
9569
9608
  sqs: asTrackingRecord(pickTrackingValue(spec, "Sqs", "sqs")),
9570
9609
  pushDiffusion: asTrackingRecord(pickTrackingValue(spec, "PushDiffusion", "pushDiffusion")),
9610
+ grpc: mapRuntimeEndpointProtocolOptions(spec, ["Grpc", "grpc"], [
9611
+ "Target", "target", "ServiceName", "serviceName", "MethodName", "methodName",
9612
+ "MethodType", "methodType", "Deadline", "deadline", "DeadlineSeconds", "deadlineSeconds",
9613
+ "DeadlineMs", "deadlineMs", "Metadata", "metadata", "Produce", "produce", "Consume", "consume",
9614
+ "ProduceAsync", "produceAsync", "ConsumeAsync", "consumeAsync", "ConnectionMetadata", "connectionMetadata",
9615
+ "NativeClient", "nativeClient"
9616
+ ]),
9617
+ webSocket: mapRuntimeEndpointProtocolOptions(spec, ["WebSocket", "webSocket"], [
9618
+ "Url", "url", "Subprotocols", "subprotocols", "ConnectTimeout", "connectTimeout",
9619
+ "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeoutMs", "connectTimeoutMs",
9620
+ "CloseTimeout", "closeTimeout", "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeoutMs", "closeTimeoutMs",
9621
+ "Produce", "produce", "Consume", "consume", "ProduceAsync", "produceAsync", "ConsumeAsync", "consumeAsync",
9622
+ "ConnectionMetadata", "connectionMetadata", "NativeClient", "nativeClient"
9623
+ ]),
9571
9624
  delegate: typeof delegateProduce === "function"
9572
9625
  || typeof delegateConsume === "function"
9573
9626
  || typeof delegateProduceAsync === "function"
@@ -9590,6 +9643,19 @@ function mapRuntimeTrackingEndpointSpec(spec, useLoadStrikeTraceIdHeader = false
9590
9643
  : undefined
9591
9644
  };
9592
9645
  }
9646
+ function mapRuntimeEndpointProtocolOptions(spec, nestedKeys, flatKeys) {
9647
+ const nested = asTrackingRecord(pickTrackingValue(spec, ...nestedKeys));
9648
+ if (Object.keys(nested).length > 0) {
9649
+ return nested;
9650
+ }
9651
+ const options = {};
9652
+ for (const key of flatKeys) {
9653
+ if (Object.prototype.hasOwnProperty.call(spec, key)) {
9654
+ options[key] = pickTrackingValue(spec, key);
9655
+ }
9656
+ }
9657
+ return options;
9658
+ }
9593
9659
  function normalizeRuntimeTrackingPayload(payload, endpoint, index) {
9594
9660
  const normalized = {
9595
9661
  headers: {
@@ -10353,6 +10419,20 @@ function assertNoDisableLicenseEnforcementOption(value, source) {
10353
10419
  }
10354
10420
  }
10355
10421
  }
10422
+ function assertNoUnsupportedNativeTrackingScenarios(scenarios) {
10423
+ for (const scenario of scenarios) {
10424
+ const tracking = scenario.getTrackingConfiguration();
10425
+ if (!tracking) {
10426
+ continue;
10427
+ }
10428
+ for (const field of ["Source", "Destination"]) {
10429
+ const endpoint = asTrackingRecord(pickTrackingValue(tracking, field, field.toLowerCase()));
10430
+ if (Object.keys(endpoint).length > 0) {
10431
+ (0, transports_js_1.validateNativeEndpointExecutionSupport)(endpoint);
10432
+ }
10433
+ }
10434
+ }
10435
+ }
10356
10436
  function normalizeRunContextCollectionShapes(values) {
10357
10437
  assertNoDisableLicenseEnforcementOption(values, "LoadStrikeContext");
10358
10438
  const normalized = {