@loadstrike/loadstrike-sdk 1.0.30201 → 1.0.30401
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist/cjs/cluster.js +2417 -7
- package/dist/cjs/index.js +13 -2
- package/dist/cjs/iteration-observations.js +711 -0
- package/dist/cjs/load-engine-v2.js +966 -0
- package/dist/cjs/local.js +73 -20
- package/dist/cjs/reporting.js +122 -12
- package/dist/cjs/runtime.js +2368 -129
- package/dist/cjs/sinks.js +471 -17
- package/dist/cjs/transports.js +84 -146
- package/dist/esm/cluster.js +2386 -7
- package/dist/esm/index.js +1 -0
- package/dist/esm/iteration-observations.js +698 -0
- package/dist/esm/load-engine-v2.js +942 -0
- package/dist/esm/local.js +73 -20
- package/dist/esm/reporting.js +122 -12
- package/dist/esm/runtime.js +2369 -130
- package/dist/esm/sinks.js +471 -17
- package/dist/esm/transports.js +84 -146
- package/dist/types/cluster.d.ts +379 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/iteration-observations.d.ts +225 -0
- package/dist/types/load-engine-v2.d.ts +147 -0
- package/dist/types/runtime.d.ts +214 -8
- package/dist/types/sinks.d.ts +67 -0
- package/dist/types/transports.d.ts +2 -8
- package/package.json +3 -4
package/dist/esm/transports.js
CHANGED
|
@@ -596,9 +596,13 @@ 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
|
|
599
601
|
throw new Error("Url must be an absolute ws:// or wss:// URI.");
|
|
600
602
|
}
|
|
601
603
|
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
|
|
602
606
|
throw new Error("Url must be an absolute ws:// or wss:// URI.");
|
|
603
607
|
}
|
|
604
608
|
if (this.ConnectTimeoutSeconds <= 0) {
|
|
@@ -1233,11 +1237,13 @@ function validateHttpAuthOptionsModel(target) {
|
|
|
1233
1237
|
}
|
|
1234
1238
|
function validateKafkaSaslOptionsModel(target) {
|
|
1235
1239
|
switch (normalizeToken(target.Mechanism)) {
|
|
1236
|
-
case "oauthbearer":
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
+
case "oauthbearer": {
|
|
1241
|
+
const additionalSettings = target.AdditionalSettings;
|
|
1242
|
+
validateKafkaOAuthBearerCredentials(String(target.AccessToken ?? "").trim() || String(target.OAuthBearerToken ?? "").trim(), String(target.OAuthBearerTokenEndpointUrl ?? "").trim()
|
|
1243
|
+
|| String(target.TokenEndpoint ?? "").trim()
|
|
1244
|
+
|| optionString(additionalSettings, "TokenEndpoint", "tokenEndpoint"), optionString(additionalSettings, "ClientId", "clientId"), optionString(additionalSettings, "ClientSecret", "clientSecret"));
|
|
1240
1245
|
return;
|
|
1246
|
+
}
|
|
1241
1247
|
default:
|
|
1242
1248
|
if (!String(target.Username ?? "").trim()) {
|
|
1243
1249
|
throw new Error("Username must be provided for SASL authentication.");
|
|
@@ -1786,21 +1792,12 @@ class KafkaEndpointAdapter extends CallbackAdapter {
|
|
|
1786
1792
|
if (!this.consumerStartPromise) {
|
|
1787
1793
|
this.consumerStartPromise = (async () => {
|
|
1788
1794
|
const options = this.endpoint.kafka ?? {};
|
|
1789
|
-
const useConfluentClient = shouldUseConfluentKafkaClient(this.endpoint);
|
|
1790
1795
|
const kafka = await createKafkaClient(this.endpoint);
|
|
1791
1796
|
const consumer = kafka.consumer(buildKafkaConsumerOptions(this.endpoint));
|
|
1792
1797
|
await consumer.connect();
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
});
|
|
1797
|
-
}
|
|
1798
|
-
else {
|
|
1799
|
-
await consumer.subscribe({
|
|
1800
|
-
topic: optionString(options, "Topic", "topic"),
|
|
1801
|
-
fromBeginning: optionBoolean(options, true, "StartFromEarliest", "startFromEarliest")
|
|
1802
|
-
});
|
|
1803
|
-
}
|
|
1798
|
+
await consumer.subscribe({
|
|
1799
|
+
topics: [optionString(options, "Topic", "topic")]
|
|
1800
|
+
});
|
|
1804
1801
|
await consumer.run({
|
|
1805
1802
|
eachMessage: async ({ message }) => {
|
|
1806
1803
|
this.queue.push(createBrokerPayload(fromKafkaHeaders(message.headers), bufferToUint8Array(message.value), this.endpoint, headerValue(message.headers, "content-type")));
|
|
@@ -3135,9 +3132,13 @@ function validateWebSocketEndpoint(endpoint, mode) {
|
|
|
3135
3132
|
parsed = new URL(url);
|
|
3136
3133
|
}
|
|
3137
3134
|
catch {
|
|
3135
|
+
// Diagnostic text only; no connection is opened here.
|
|
3136
|
+
// nosemgrep: vulnerability-tools.semgrep-rules.javascript.lang.security.detect-insecure-websocket
|
|
3138
3137
|
throw new Error("Url must be an absolute ws:// or wss:// URI.");
|
|
3139
3138
|
}
|
|
3140
3139
|
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
|
|
3141
3142
|
throw new Error("Url must be an absolute ws:// or wss:// URI.");
|
|
3142
3143
|
}
|
|
3143
3144
|
const connectMs = optionNumber(options, "ConnectTimeoutMs", "connectTimeoutMs");
|
|
@@ -3213,7 +3214,8 @@ function validateKafkaEndpoint(endpoint, mode, hasModeDelegate) {
|
|
|
3213
3214
|
function validateKafkaSaslOptions(options) {
|
|
3214
3215
|
const mechanism = optionString(options, "Mechanism", "mechanism") || "Plain";
|
|
3215
3216
|
if (mechanism.toLowerCase() === "oauthbearer") {
|
|
3216
|
-
|
|
3217
|
+
const additionalSettings = asRecordOrEmpty(pickProtocolValue(options, "AdditionalSettings", "additionalSettings"));
|
|
3218
|
+
validateKafkaOAuthBearerCredentials(optionString(options, "AccessToken", "accessToken", "OAuthBearerToken", "oauthBearerToken"), optionString(options, "OAuthBearerTokenEndpointUrl", "oauthBearerTokenEndpointUrl", "TokenEndpoint", "tokenEndpoint") || optionString(additionalSettings, "TokenEndpoint", "tokenEndpoint"), optionString(additionalSettings, "ClientId", "clientId"), optionString(additionalSettings, "ClientSecret", "clientSecret"));
|
|
3217
3219
|
return;
|
|
3218
3220
|
}
|
|
3219
3221
|
requireNonEmptyString(optionString(options, "Username", "username"), "Username must be provided for SASL authentication.");
|
|
@@ -4206,74 +4208,31 @@ function pickProtocolValue(options, ...keys) {
|
|
|
4206
4208
|
}
|
|
4207
4209
|
return undefined;
|
|
4208
4210
|
}
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
.split(",")
|
|
4214
|
-
.map((value) => value.trim())
|
|
4215
|
-
.filter((value) => value.length > 0);
|
|
4216
|
-
const clientOptions = {
|
|
4217
|
-
clientId: `${endpoint.name}-${randomUUID().slice(0, 8)}`,
|
|
4218
|
-
brokers: bootstrapServers,
|
|
4219
|
-
ssl: securityProtocol === "ssl" || securityProtocol === "saslssl"
|
|
4220
|
-
};
|
|
4221
|
-
if (securityProtocol === "saslplaintext" || securityProtocol === "saslssl") {
|
|
4222
|
-
const saslOptions = asRecordOrEmpty(pickProtocolValue(options, "Sasl", "sasl"));
|
|
4223
|
-
const mechanism = (optionString(saslOptions, "Mechanism", "mechanism") || "Plain").toLowerCase();
|
|
4224
|
-
if (mechanism === "oauthbearer") {
|
|
4225
|
-
clientOptions.sasl = {
|
|
4226
|
-
mechanism: "oauthbearer",
|
|
4227
|
-
oauthBearerProvider: async () => ({
|
|
4228
|
-
value: await resolveKafkaOAuthBearerToken(saslOptions)
|
|
4229
|
-
})
|
|
4230
|
-
};
|
|
4231
|
-
}
|
|
4232
|
-
else {
|
|
4233
|
-
clientOptions.sasl = {
|
|
4234
|
-
mechanism: mapKafkaSaslMechanism(mechanism),
|
|
4235
|
-
username: optionString(saslOptions, "Username", "username"),
|
|
4236
|
-
password: optionString(saslOptions, "Password", "password")
|
|
4237
|
-
};
|
|
4238
|
-
}
|
|
4211
|
+
let kafkaClientFactoryForTests = null;
|
|
4212
|
+
async function createKafkaClient(endpoint) {
|
|
4213
|
+
if (kafkaClientFactoryForTests) {
|
|
4214
|
+
return await kafkaClientFactoryForTests(endpoint);
|
|
4239
4215
|
}
|
|
4240
|
-
|
|
4216
|
+
const { KafkaJS } = await import("@confluentinc/kafka-javascript");
|
|
4217
|
+
return new KafkaJS.Kafka(buildConfluentKafkaClientOptions(endpoint));
|
|
4241
4218
|
}
|
|
4242
|
-
|
|
4243
|
-
if (
|
|
4244
|
-
|
|
4245
|
-
|
|
4219
|
+
function validateKafkaOAuthBearerCredentials(directToken, tokenEndpoint, clientId, clientSecret) {
|
|
4220
|
+
if (directToken.trim()) {
|
|
4221
|
+
return;
|
|
4222
|
+
}
|
|
4223
|
+
if (!tokenEndpoint.trim() || !clientId.trim() || !clientSecret.trim()) {
|
|
4224
|
+
throw new Error("Kafka OAuthBearer requires a direct access token or token endpoint with client credentials.");
|
|
4246
4225
|
}
|
|
4247
|
-
const { Kafka } = await import("kafkajs");
|
|
4248
|
-
return new Kafka(buildKafkaClientOptions(endpoint));
|
|
4249
4226
|
}
|
|
4250
4227
|
function buildKafkaConsumerOptions(endpoint) {
|
|
4251
4228
|
const options = endpoint.kafka ?? {};
|
|
4252
4229
|
const groupId = optionString(options, "ConsumerGroupId", "consumerGroupId");
|
|
4253
4230
|
const fromBeginning = optionBoolean(options, true, "StartFromEarliest", "startFromEarliest");
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
};
|
|
4260
|
-
}
|
|
4261
|
-
return { groupId };
|
|
4262
|
-
}
|
|
4263
|
-
function shouldUseConfluentKafkaClient(endpoint) {
|
|
4264
|
-
const options = endpoint.kafka ?? {};
|
|
4265
|
-
const securityProtocol = (optionString(options, "SecurityProtocol", "securityProtocol") || "Plaintext").toLowerCase();
|
|
4266
|
-
if (securityProtocol !== "saslplaintext" && securityProtocol !== "saslssl") {
|
|
4267
|
-
return Object.keys(toStringRecord(pickProtocolValue(options, "ConfluentSettings", "confluentSettings"))).length > 0;
|
|
4268
|
-
}
|
|
4269
|
-
const saslOptions = asRecordOrEmpty(pickProtocolValue(options, "Sasl", "sasl"));
|
|
4270
|
-
const mechanism = (optionString(saslOptions, "Mechanism", "mechanism") || "Plain").toLowerCase();
|
|
4271
|
-
const confluentSettings = toStringRecord(pickProtocolValue(options, "ConfluentSettings", "confluentSettings"));
|
|
4272
|
-
const additionalSettings = toStringRecord(pickProtocolValue(saslOptions, "AdditionalSettings", "additionalSettings"));
|
|
4273
|
-
const hasDirectOAuthToken = mechanism === "oauthbearer" && Boolean(optionString(saslOptions, "AccessToken", "accessToken", "OAuthBearerToken", "oauthBearerToken"));
|
|
4274
|
-
return mechanism === "gssapi"
|
|
4275
|
-
|| Object.keys(confluentSettings).length > 0
|
|
4276
|
-
|| (Object.keys(additionalSettings).length > 0 && !hasDirectOAuthToken);
|
|
4231
|
+
return {
|
|
4232
|
+
"group.id": groupId,
|
|
4233
|
+
"auto.offset.reset": fromBeginning ? "earliest" : "latest",
|
|
4234
|
+
"enable.auto.commit": true
|
|
4235
|
+
};
|
|
4277
4236
|
}
|
|
4278
4237
|
function buildConfluentKafkaClientOptions(endpoint) {
|
|
4279
4238
|
const options = endpoint.kafka ?? {};
|
|
@@ -4289,30 +4248,41 @@ function buildConfluentKafkaClientOptions(endpoint) {
|
|
|
4289
4248
|
const additionalSettings = toStringRecord(pickProtocolValue(saslOptions, "AdditionalSettings", "additionalSettings"));
|
|
4290
4249
|
base["sasl.mechanism"] = mapConfluentKafkaSaslMechanism(mechanism);
|
|
4291
4250
|
if (mechanism.toLowerCase() === "oauthbearer") {
|
|
4251
|
+
const directToken = optionString(saslOptions, "AccessToken", "accessToken", "OAuthBearerToken", "oauthBearerToken");
|
|
4292
4252
|
const tokenEndpoint = optionString(saslOptions, "OAuthBearerTokenEndpointUrl", "oauthBearerTokenEndpointUrl", "TokenEndpoint", "tokenEndpoint") || optionString(additionalSettings, "TokenEndpoint", "tokenEndpoint");
|
|
4293
|
-
if (tokenEndpoint) {
|
|
4294
|
-
base["sasl.oauthbearer.method"] = "oidc";
|
|
4295
|
-
base["sasl.oauthbearer.token.endpoint.url"] = tokenEndpoint;
|
|
4296
|
-
}
|
|
4297
4253
|
const clientId = optionString(additionalSettings, "ClientId", "clientId");
|
|
4298
|
-
if (clientId) {
|
|
4299
|
-
base["sasl.oauthbearer.client.id"] = clientId;
|
|
4300
|
-
}
|
|
4301
4254
|
const clientSecret = optionString(additionalSettings, "ClientSecret", "clientSecret");
|
|
4302
|
-
if (clientSecret) {
|
|
4303
|
-
base["sasl.oauthbearer.client.secret"] = clientSecret;
|
|
4304
|
-
}
|
|
4305
4255
|
const scope = optionString(additionalSettings, "Scope", "scope");
|
|
4306
|
-
if (scope) {
|
|
4307
|
-
base["sasl.oauthbearer.scope"] = scope;
|
|
4308
|
-
}
|
|
4309
4256
|
const grantType = optionString(additionalSettings, "GrantType", "grantType");
|
|
4310
|
-
if (grantType) {
|
|
4311
|
-
base["sasl.oauthbearer.grant.type"] = grantType;
|
|
4312
|
-
}
|
|
4313
4257
|
const extensions = optionString(additionalSettings, "Extensions", "extensions");
|
|
4314
|
-
if (
|
|
4315
|
-
|
|
4258
|
+
if (directToken) {
|
|
4259
|
+
const principal = optionString(additionalSettings, "Principal", "principal")
|
|
4260
|
+
|| optionString(saslOptions, "Username", "username")
|
|
4261
|
+
|| "loadstrike";
|
|
4262
|
+
base.oauthbearer_token_refresh_cb = async () => ({
|
|
4263
|
+
tokenValue: directToken,
|
|
4264
|
+
lifetime: Date.now() + 60 * 60 * 1000,
|
|
4265
|
+
principal,
|
|
4266
|
+
extensions: parseKafkaOAuthExtensions(extensions)
|
|
4267
|
+
});
|
|
4268
|
+
}
|
|
4269
|
+
else {
|
|
4270
|
+
if (!tokenEndpoint || !clientId || !clientSecret) {
|
|
4271
|
+
throw new Error("Kafka OAuthBearer requires a direct access token or token endpoint with client credentials.");
|
|
4272
|
+
}
|
|
4273
|
+
base["sasl.oauthbearer.method"] = "oidc";
|
|
4274
|
+
base["sasl.oauthbearer.token.endpoint.url"] = tokenEndpoint;
|
|
4275
|
+
base["sasl.oauthbearer.client.id"] = clientId;
|
|
4276
|
+
base["sasl.oauthbearer.client.secret"] = clientSecret;
|
|
4277
|
+
if (scope) {
|
|
4278
|
+
base["sasl.oauthbearer.scope"] = scope;
|
|
4279
|
+
}
|
|
4280
|
+
if (grantType) {
|
|
4281
|
+
base["sasl.oauthbearer.grant.type"] = grantType;
|
|
4282
|
+
}
|
|
4283
|
+
if (extensions) {
|
|
4284
|
+
base["sasl.oauthbearer.extensions"] = extensions;
|
|
4285
|
+
}
|
|
4316
4286
|
}
|
|
4317
4287
|
}
|
|
4318
4288
|
else {
|
|
@@ -4331,7 +4301,9 @@ function buildConfluentKafkaClientOptions(endpoint) {
|
|
|
4331
4301
|
|| key === "Extensions"
|
|
4332
4302
|
|| key === "extensions"
|
|
4333
4303
|
|| key === "TokenEndpoint"
|
|
4334
|
-
|| key === "tokenEndpoint"
|
|
4304
|
+
|| key === "tokenEndpoint"
|
|
4305
|
+
|| key === "Principal"
|
|
4306
|
+
|| key === "principal") {
|
|
4335
4307
|
continue;
|
|
4336
4308
|
}
|
|
4337
4309
|
base[key] = value;
|
|
@@ -4342,53 +4314,20 @@ function buildConfluentKafkaClientOptions(endpoint) {
|
|
|
4342
4314
|
}
|
|
4343
4315
|
return base;
|
|
4344
4316
|
}
|
|
4345
|
-
|
|
4346
|
-
const
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
const form = new URLSearchParams();
|
|
4358
|
-
form.set("grant_type", "client_credentials");
|
|
4359
|
-
form.set("client_id", clientId);
|
|
4360
|
-
form.set("client_secret", clientSecret);
|
|
4361
|
-
const scope = optionString(additional, "Scope", "scope");
|
|
4362
|
-
if (scope) {
|
|
4363
|
-
form.set("scope", scope);
|
|
4364
|
-
}
|
|
4365
|
-
const response = await fetch(tokenEndpoint, {
|
|
4366
|
-
method: "POST",
|
|
4367
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
4368
|
-
body: form.toString()
|
|
4369
|
-
});
|
|
4370
|
-
if (!response.ok) {
|
|
4371
|
-
throw new Error(`Kafka OAuthBearer token request failed with status ${response.status}.`);
|
|
4372
|
-
}
|
|
4373
|
-
const payload = parseMaybeJson(await response.text());
|
|
4374
|
-
if (!isRecord(payload) || !payload.access_token) {
|
|
4375
|
-
throw new Error("Kafka OAuthBearer token response did not contain access_token.");
|
|
4376
|
-
}
|
|
4377
|
-
return String(payload.access_token);
|
|
4378
|
-
}
|
|
4379
|
-
function mapKafkaSaslMechanism(mechanism) {
|
|
4380
|
-
switch (mechanism) {
|
|
4381
|
-
case "plain":
|
|
4382
|
-
return "plain";
|
|
4383
|
-
case "scramsha256":
|
|
4384
|
-
case "scram-sha-256":
|
|
4385
|
-
return "scram-sha-256";
|
|
4386
|
-
case "scramsha512":
|
|
4387
|
-
case "scram-sha-512":
|
|
4388
|
-
return "scram-sha-512";
|
|
4389
|
-
default:
|
|
4390
|
-
throw new Error(`Unsupported Kafka SASL mechanism: ${mechanism}.`);
|
|
4317
|
+
function parseKafkaOAuthExtensions(value) {
|
|
4318
|
+
const parsed = {};
|
|
4319
|
+
for (const entry of value.split(",")) {
|
|
4320
|
+
const separatorIndex = entry.indexOf("=");
|
|
4321
|
+
if (separatorIndex <= 0) {
|
|
4322
|
+
continue;
|
|
4323
|
+
}
|
|
4324
|
+
const key = entry.slice(0, separatorIndex).trim();
|
|
4325
|
+
if (!key) {
|
|
4326
|
+
continue;
|
|
4327
|
+
}
|
|
4328
|
+
parsed[key] = entry.slice(separatorIndex + 1).trim();
|
|
4391
4329
|
}
|
|
4330
|
+
return parsed;
|
|
4392
4331
|
}
|
|
4393
4332
|
function mapConfluentKafkaSecurityProtocol(protocol) {
|
|
4394
4333
|
switch (protocol.toLowerCase()) {
|
|
@@ -4672,7 +4611,6 @@ export const __loadstrikeTestExports = {
|
|
|
4672
4611
|
applyHttpAuthHeaders,
|
|
4673
4612
|
buildHttpRequestBody,
|
|
4674
4613
|
buildConfluentKafkaClientOptions,
|
|
4675
|
-
buildKafkaClientOptions,
|
|
4676
4614
|
canonicalizeHttpResponseSource,
|
|
4677
4615
|
createDelegateRequestEndpointView,
|
|
4678
4616
|
createNatsHeaders,
|
|
@@ -4688,7 +4626,6 @@ export const __loadstrikeTestExports = {
|
|
|
4688
4626
|
injectTrackingValue,
|
|
4689
4627
|
mapConfluentKafkaSaslMechanism,
|
|
4690
4628
|
mapConfluentKafkaSecurityProtocol,
|
|
4691
|
-
mapKafkaSaslMechanism,
|
|
4692
4629
|
parseBodyObject,
|
|
4693
4630
|
partitionFromKey,
|
|
4694
4631
|
payloadBodyAsUtf8,
|
|
@@ -4696,10 +4633,8 @@ export const __loadstrikeTestExports = {
|
|
|
4696
4633
|
protocolBus: protocolBus,
|
|
4697
4634
|
readFirstRedisStreamEntry,
|
|
4698
4635
|
resolveConnectionMetadata,
|
|
4699
|
-
resolveKafkaOAuthBearerToken,
|
|
4700
4636
|
serializePayloadBody,
|
|
4701
4637
|
setJsonBodyValue,
|
|
4702
|
-
shouldUseConfluentKafkaClient,
|
|
4703
4638
|
toHeaderRecord,
|
|
4704
4639
|
toKafkaHeadersWithContentType,
|
|
4705
4640
|
toSqsMessageAttributes,
|
|
@@ -4707,6 +4642,9 @@ export const __loadstrikeTestExports = {
|
|
|
4707
4642
|
setSqsClientFactoryForTests: (factory) => {
|
|
4708
4643
|
sqsClientFactoryForTests = factory;
|
|
4709
4644
|
},
|
|
4645
|
+
setKafkaClientFactoryForTests: (factory) => {
|
|
4646
|
+
kafkaClientFactoryForTests = factory;
|
|
4647
|
+
},
|
|
4710
4648
|
validateHttpEndpoint,
|
|
4711
4649
|
validateTrackingSelectorPath
|
|
4712
4650
|
};
|