@loadstrike/loadstrike-sdk 1.0.30201 → 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/README.md +28 -0
- package/dist/cjs/cluster.js +2417 -7
- package/dist/cjs/index.js +13 -2
- package/dist/cjs/iteration-observation-diagnostics.js +513 -0
- package/dist/cjs/iteration-observations.js +884 -0
- package/dist/cjs/load-engine-v2.js +966 -0
- package/dist/cjs/local.js +128 -30
- package/dist/cjs/reporting.js +148 -19
- package/dist/cjs/runtime.js +2514 -196
- package/dist/cjs/sink-retry-policy.js +52 -0
- package/dist/cjs/sinks.js +580 -23
- package/dist/cjs/transports.js +154 -163
- package/dist/esm/cluster.js +2386 -7
- package/dist/esm/index.js +1 -0
- package/dist/esm/iteration-observation-diagnostics.js +508 -0
- package/dist/esm/iteration-observations.js +871 -0
- package/dist/esm/load-engine-v2.js +942 -0
- package/dist/esm/local.js +128 -30
- package/dist/esm/reporting.js +148 -19
- package/dist/esm/runtime.js +2515 -197
- package/dist/esm/sink-retry-policy.js +44 -0
- package/dist/esm/sinks.js +580 -23
- package/dist/esm/transports.js +154 -163
- package/dist/types/cluster.d.ts +379 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/iteration-observation-diagnostics.d.ts +21 -0
- package/dist/types/iteration-observations.d.ts +230 -0
- package/dist/types/load-engine-v2.d.ts +147 -0
- package/dist/types/runtime.d.ts +216 -8
- package/dist/types/sink-retry-policy.d.ts +9 -0
- package/dist/types/sinks.d.ts +73 -0
- package/dist/types/transports.d.ts +6 -8
- package/package.json +3 -4
package/dist/esm/transports.js
CHANGED
|
@@ -596,10 +596,10 @@ class WebSocketEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
|
|
|
596
596
|
parsed = new URL(url);
|
|
597
597
|
}
|
|
598
598
|
catch {
|
|
599
|
-
throw new Error("Url must be an absolute ws
|
|
599
|
+
throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
|
|
600
600
|
}
|
|
601
601
|
if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
|
|
602
|
-
throw new Error("Url must be an absolute ws
|
|
602
|
+
throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
|
|
603
603
|
}
|
|
604
604
|
if (this.ConnectTimeoutSeconds <= 0) {
|
|
605
605
|
throw new RangeError("ConnectTimeout must be greater than zero.");
|
|
@@ -1233,11 +1233,13 @@ function validateHttpAuthOptionsModel(target) {
|
|
|
1233
1233
|
}
|
|
1234
1234
|
function validateKafkaSaslOptionsModel(target) {
|
|
1235
1235
|
switch (normalizeToken(target.Mechanism)) {
|
|
1236
|
-
case "oauthbearer":
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1236
|
+
case "oauthbearer": {
|
|
1237
|
+
const additionalSettings = target.AdditionalSettings;
|
|
1238
|
+
validateKafkaOAuthBearerCredentials(String(target.AccessToken ?? "").trim() || String(target.OAuthBearerToken ?? "").trim(), String(target.OAuthBearerTokenEndpointUrl ?? "").trim()
|
|
1239
|
+
|| String(target.TokenEndpoint ?? "").trim()
|
|
1240
|
+
|| optionString(additionalSettings, "TokenEndpoint", "tokenEndpoint"), optionString(additionalSettings, "ClientId", "clientId"), optionString(additionalSettings, "ClientSecret", "clientSecret"));
|
|
1240
1241
|
return;
|
|
1242
|
+
}
|
|
1241
1243
|
default:
|
|
1242
1244
|
if (!String(target.Username ?? "").trim()) {
|
|
1243
1245
|
throw new Error("Username must be provided for SASL authentication.");
|
|
@@ -1786,21 +1788,12 @@ class KafkaEndpointAdapter extends CallbackAdapter {
|
|
|
1786
1788
|
if (!this.consumerStartPromise) {
|
|
1787
1789
|
this.consumerStartPromise = (async () => {
|
|
1788
1790
|
const options = this.endpoint.kafka ?? {};
|
|
1789
|
-
const useConfluentClient = shouldUseConfluentKafkaClient(this.endpoint);
|
|
1790
1791
|
const kafka = await createKafkaClient(this.endpoint);
|
|
1791
1792
|
const consumer = kafka.consumer(buildKafkaConsumerOptions(this.endpoint));
|
|
1792
1793
|
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
|
-
}
|
|
1794
|
+
await consumer.subscribe({
|
|
1795
|
+
topics: [optionString(options, "Topic", "topic")]
|
|
1796
|
+
});
|
|
1804
1797
|
await consumer.run({
|
|
1805
1798
|
eachMessage: async ({ message }) => {
|
|
1806
1799
|
this.queue.push(createBrokerPayload(fromKafkaHeaders(message.headers), bufferToUint8Array(message.value), this.endpoint, headerValue(message.headers, "content-type")));
|
|
@@ -3135,10 +3128,10 @@ function validateWebSocketEndpoint(endpoint, mode) {
|
|
|
3135
3128
|
parsed = new URL(url);
|
|
3136
3129
|
}
|
|
3137
3130
|
catch {
|
|
3138
|
-
throw new Error("Url must be an absolute ws
|
|
3131
|
+
throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
|
|
3139
3132
|
}
|
|
3140
3133
|
if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
|
|
3141
|
-
throw new Error("Url must be an absolute ws
|
|
3134
|
+
throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
|
|
3142
3135
|
}
|
|
3143
3136
|
const connectMs = optionNumber(options, "ConnectTimeoutMs", "connectTimeoutMs");
|
|
3144
3137
|
const connectSeconds = optionNumber(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout");
|
|
@@ -3213,7 +3206,8 @@ function validateKafkaEndpoint(endpoint, mode, hasModeDelegate) {
|
|
|
3213
3206
|
function validateKafkaSaslOptions(options) {
|
|
3214
3207
|
const mechanism = optionString(options, "Mechanism", "mechanism") || "Plain";
|
|
3215
3208
|
if (mechanism.toLowerCase() === "oauthbearer") {
|
|
3216
|
-
|
|
3209
|
+
const additionalSettings = asRecordOrEmpty(pickProtocolValue(options, "AdditionalSettings", "additionalSettings"));
|
|
3210
|
+
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
3211
|
return;
|
|
3218
3212
|
}
|
|
3219
3213
|
requireNonEmptyString(optionString(options, "Username", "username"), "Username must be provided for SASL authentication.");
|
|
@@ -3567,7 +3561,13 @@ function extractJsonPath(value, arrayPath) {
|
|
|
3567
3561
|
if (!normalized) {
|
|
3568
3562
|
return value;
|
|
3569
3563
|
}
|
|
3570
|
-
|
|
3564
|
+
let tokens;
|
|
3565
|
+
try {
|
|
3566
|
+
tokens = safeJsonPathSegments(normalized.replace(/\[(\d+)\]/g, ".$1"));
|
|
3567
|
+
}
|
|
3568
|
+
catch {
|
|
3569
|
+
return null;
|
|
3570
|
+
}
|
|
3571
3571
|
for (const token of tokens) {
|
|
3572
3572
|
if (current == null) {
|
|
3573
3573
|
return null;
|
|
@@ -3577,13 +3577,17 @@ function extractJsonPath(value, arrayPath) {
|
|
|
3577
3577
|
if (!Number.isInteger(index) || index < 0 || index >= current.length) {
|
|
3578
3578
|
return null;
|
|
3579
3579
|
}
|
|
3580
|
-
current = current
|
|
3580
|
+
current = current.at(index);
|
|
3581
3581
|
continue;
|
|
3582
3582
|
}
|
|
3583
|
-
if (!isRecord(current)
|
|
3583
|
+
if (!isRecord(current)) {
|
|
3584
3584
|
return null;
|
|
3585
3585
|
}
|
|
3586
|
-
|
|
3586
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, token);
|
|
3587
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
3588
|
+
return null;
|
|
3589
|
+
}
|
|
3590
|
+
current = descriptor.value;
|
|
3587
3591
|
}
|
|
3588
3592
|
return current;
|
|
3589
3593
|
}
|
|
@@ -3600,7 +3604,7 @@ function parseMaybeJson(value) {
|
|
|
3600
3604
|
}
|
|
3601
3605
|
}
|
|
3602
3606
|
function headersToRecord(value) {
|
|
3603
|
-
const headers =
|
|
3607
|
+
const headers = Object.create(null);
|
|
3604
3608
|
value.forEach((headerValue, key) => {
|
|
3605
3609
|
headers[key] = headerValue;
|
|
3606
3610
|
});
|
|
@@ -3954,11 +3958,22 @@ function extractTrackingValue(payload, selector) {
|
|
|
3954
3958
|
return null;
|
|
3955
3959
|
}
|
|
3956
3960
|
let current = body;
|
|
3957
|
-
|
|
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) {
|
|
3958
3970
|
if (!isRecord(current)) {
|
|
3959
3971
|
return null;
|
|
3960
3972
|
}
|
|
3961
|
-
current
|
|
3973
|
+
if (!Object.prototype.hasOwnProperty.call(current, segment)) {
|
|
3974
|
+
return null;
|
|
3975
|
+
}
|
|
3976
|
+
current = readOwnJsonProperty(current, segment);
|
|
3962
3977
|
}
|
|
3963
3978
|
return current == null ? null : String(current);
|
|
3964
3979
|
}
|
|
@@ -3989,23 +4004,61 @@ function injectTrackingValue(payload, selector, value) {
|
|
|
3989
4004
|
}
|
|
3990
4005
|
function setJsonBodyValue(body, path, value) {
|
|
3991
4006
|
const target = parseBodyObject(body) ?? {};
|
|
3992
|
-
const clone =
|
|
3993
|
-
const segments = path
|
|
4007
|
+
const clone = cloneJsonRecord(target);
|
|
4008
|
+
const segments = safeJsonPathSegments(path);
|
|
3994
4009
|
if (!segments.length) {
|
|
3995
4010
|
return clone;
|
|
3996
4011
|
}
|
|
3997
4012
|
let current = clone;
|
|
3998
4013
|
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
3999
4014
|
const segment = segments[i];
|
|
4000
|
-
|
|
4015
|
+
let next = readOwnJsonProperty(current, segment);
|
|
4001
4016
|
if (!isRecord(next)) {
|
|
4002
|
-
|
|
4017
|
+
next = {};
|
|
4018
|
+
defineJsonProperty(current, segment, next);
|
|
4003
4019
|
}
|
|
4004
|
-
current =
|
|
4020
|
+
current = next;
|
|
4005
4021
|
}
|
|
4006
|
-
current
|
|
4022
|
+
defineJsonProperty(current, segments[segments.length - 1], value);
|
|
4007
4023
|
return clone;
|
|
4008
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
|
+
}
|
|
4009
4062
|
function parseBodyObject(body) {
|
|
4010
4063
|
if (body instanceof Uint8Array) {
|
|
4011
4064
|
return parseBodyObject(new TextDecoder().decode(body));
|
|
@@ -4206,74 +4259,31 @@ function pickProtocolValue(options, ...keys) {
|
|
|
4206
4259
|
}
|
|
4207
4260
|
return undefined;
|
|
4208
4261
|
}
|
|
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
|
-
}
|
|
4262
|
+
let kafkaClientFactoryForTests = null;
|
|
4263
|
+
async function createKafkaClient(endpoint) {
|
|
4264
|
+
if (kafkaClientFactoryForTests) {
|
|
4265
|
+
return await kafkaClientFactoryForTests(endpoint);
|
|
4239
4266
|
}
|
|
4240
|
-
|
|
4267
|
+
const { KafkaJS } = await import("@confluentinc/kafka-javascript");
|
|
4268
|
+
return new KafkaJS.Kafka(buildConfluentKafkaClientOptions(endpoint));
|
|
4241
4269
|
}
|
|
4242
|
-
|
|
4243
|
-
if (
|
|
4244
|
-
|
|
4245
|
-
|
|
4270
|
+
function validateKafkaOAuthBearerCredentials(directToken, tokenEndpoint, clientId, clientSecret) {
|
|
4271
|
+
if (directToken.trim()) {
|
|
4272
|
+
return;
|
|
4273
|
+
}
|
|
4274
|
+
if (!tokenEndpoint.trim() || !clientId.trim() || !clientSecret.trim()) {
|
|
4275
|
+
throw new Error("Kafka OAuthBearer requires a direct access token or token endpoint with client credentials.");
|
|
4246
4276
|
}
|
|
4247
|
-
const { Kafka } = await import("kafkajs");
|
|
4248
|
-
return new Kafka(buildKafkaClientOptions(endpoint));
|
|
4249
4277
|
}
|
|
4250
4278
|
function buildKafkaConsumerOptions(endpoint) {
|
|
4251
4279
|
const options = endpoint.kafka ?? {};
|
|
4252
4280
|
const groupId = optionString(options, "ConsumerGroupId", "consumerGroupId");
|
|
4253
4281
|
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);
|
|
4282
|
+
return {
|
|
4283
|
+
"group.id": groupId,
|
|
4284
|
+
"auto.offset.reset": fromBeginning ? "earliest" : "latest",
|
|
4285
|
+
"enable.auto.commit": true
|
|
4286
|
+
};
|
|
4277
4287
|
}
|
|
4278
4288
|
function buildConfluentKafkaClientOptions(endpoint) {
|
|
4279
4289
|
const options = endpoint.kafka ?? {};
|
|
@@ -4289,30 +4299,41 @@ function buildConfluentKafkaClientOptions(endpoint) {
|
|
|
4289
4299
|
const additionalSettings = toStringRecord(pickProtocolValue(saslOptions, "AdditionalSettings", "additionalSettings"));
|
|
4290
4300
|
base["sasl.mechanism"] = mapConfluentKafkaSaslMechanism(mechanism);
|
|
4291
4301
|
if (mechanism.toLowerCase() === "oauthbearer") {
|
|
4302
|
+
const directToken = optionString(saslOptions, "AccessToken", "accessToken", "OAuthBearerToken", "oauthBearerToken");
|
|
4292
4303
|
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
4304
|
const clientId = optionString(additionalSettings, "ClientId", "clientId");
|
|
4298
|
-
if (clientId) {
|
|
4299
|
-
base["sasl.oauthbearer.client.id"] = clientId;
|
|
4300
|
-
}
|
|
4301
4305
|
const clientSecret = optionString(additionalSettings, "ClientSecret", "clientSecret");
|
|
4302
|
-
if (clientSecret) {
|
|
4303
|
-
base["sasl.oauthbearer.client.secret"] = clientSecret;
|
|
4304
|
-
}
|
|
4305
4306
|
const scope = optionString(additionalSettings, "Scope", "scope");
|
|
4306
|
-
if (scope) {
|
|
4307
|
-
base["sasl.oauthbearer.scope"] = scope;
|
|
4308
|
-
}
|
|
4309
4307
|
const grantType = optionString(additionalSettings, "GrantType", "grantType");
|
|
4310
|
-
if (grantType) {
|
|
4311
|
-
base["sasl.oauthbearer.grant.type"] = grantType;
|
|
4312
|
-
}
|
|
4313
4308
|
const extensions = optionString(additionalSettings, "Extensions", "extensions");
|
|
4314
|
-
if (
|
|
4315
|
-
|
|
4309
|
+
if (directToken) {
|
|
4310
|
+
const principal = optionString(additionalSettings, "Principal", "principal")
|
|
4311
|
+
|| optionString(saslOptions, "Username", "username")
|
|
4312
|
+
|| "loadstrike";
|
|
4313
|
+
base.oauthbearer_token_refresh_cb = async () => ({
|
|
4314
|
+
tokenValue: directToken,
|
|
4315
|
+
lifetime: Date.now() + 60 * 60 * 1000,
|
|
4316
|
+
principal,
|
|
4317
|
+
extensions: parseKafkaOAuthExtensions(extensions)
|
|
4318
|
+
});
|
|
4319
|
+
}
|
|
4320
|
+
else {
|
|
4321
|
+
if (!tokenEndpoint || !clientId || !clientSecret) {
|
|
4322
|
+
throw new Error("Kafka OAuthBearer requires a direct access token or token endpoint with client credentials.");
|
|
4323
|
+
}
|
|
4324
|
+
base["sasl.oauthbearer.method"] = "oidc";
|
|
4325
|
+
base["sasl.oauthbearer.token.endpoint.url"] = tokenEndpoint;
|
|
4326
|
+
base["sasl.oauthbearer.client.id"] = clientId;
|
|
4327
|
+
base["sasl.oauthbearer.client.secret"] = clientSecret;
|
|
4328
|
+
if (scope) {
|
|
4329
|
+
base["sasl.oauthbearer.scope"] = scope;
|
|
4330
|
+
}
|
|
4331
|
+
if (grantType) {
|
|
4332
|
+
base["sasl.oauthbearer.grant.type"] = grantType;
|
|
4333
|
+
}
|
|
4334
|
+
if (extensions) {
|
|
4335
|
+
base["sasl.oauthbearer.extensions"] = extensions;
|
|
4336
|
+
}
|
|
4316
4337
|
}
|
|
4317
4338
|
}
|
|
4318
4339
|
else {
|
|
@@ -4331,7 +4352,9 @@ function buildConfluentKafkaClientOptions(endpoint) {
|
|
|
4331
4352
|
|| key === "Extensions"
|
|
4332
4353
|
|| key === "extensions"
|
|
4333
4354
|
|| key === "TokenEndpoint"
|
|
4334
|
-
|| key === "tokenEndpoint"
|
|
4355
|
+
|| key === "tokenEndpoint"
|
|
4356
|
+
|| key === "Principal"
|
|
4357
|
+
|| key === "principal") {
|
|
4335
4358
|
continue;
|
|
4336
4359
|
}
|
|
4337
4360
|
base[key] = value;
|
|
@@ -4342,53 +4365,20 @@ function buildConfluentKafkaClientOptions(endpoint) {
|
|
|
4342
4365
|
}
|
|
4343
4366
|
return base;
|
|
4344
4367
|
}
|
|
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}.`);
|
|
4368
|
+
function parseKafkaOAuthExtensions(value) {
|
|
4369
|
+
const parsed = {};
|
|
4370
|
+
for (const entry of value.split(",")) {
|
|
4371
|
+
const separatorIndex = entry.indexOf("=");
|
|
4372
|
+
if (separatorIndex <= 0) {
|
|
4373
|
+
continue;
|
|
4374
|
+
}
|
|
4375
|
+
const key = entry.slice(0, separatorIndex).trim();
|
|
4376
|
+
if (!key) {
|
|
4377
|
+
continue;
|
|
4378
|
+
}
|
|
4379
|
+
parsed[key] = entry.slice(separatorIndex + 1).trim();
|
|
4391
4380
|
}
|
|
4381
|
+
return parsed;
|
|
4392
4382
|
}
|
|
4393
4383
|
function mapConfluentKafkaSecurityProtocol(protocol) {
|
|
4394
4384
|
switch (protocol.toLowerCase()) {
|
|
@@ -4672,14 +4662,15 @@ export const __loadstrikeTestExports = {
|
|
|
4672
4662
|
applyHttpAuthHeaders,
|
|
4673
4663
|
buildHttpRequestBody,
|
|
4674
4664
|
buildConfluentKafkaClientOptions,
|
|
4675
|
-
buildKafkaClientOptions,
|
|
4676
4665
|
canonicalizeHttpResponseSource,
|
|
4677
4666
|
createDelegateRequestEndpointView,
|
|
4678
4667
|
createNatsHeaders,
|
|
4679
4668
|
createRedisStreamPayload,
|
|
4680
4669
|
deserializeBrokerPayloadBody,
|
|
4670
|
+
extractJsonPath,
|
|
4681
4671
|
extractTrackingValue,
|
|
4682
4672
|
fromKafkaHeaders,
|
|
4673
|
+
headersToRecord,
|
|
4683
4674
|
headerValue,
|
|
4684
4675
|
attachDotNetTrackingPayloadAliases,
|
|
4685
4676
|
attachPayloadHelpers,
|
|
@@ -4688,7 +4679,6 @@ export const __loadstrikeTestExports = {
|
|
|
4688
4679
|
injectTrackingValue,
|
|
4689
4680
|
mapConfluentKafkaSaslMechanism,
|
|
4690
4681
|
mapConfluentKafkaSecurityProtocol,
|
|
4691
|
-
mapKafkaSaslMechanism,
|
|
4692
4682
|
parseBodyObject,
|
|
4693
4683
|
partitionFromKey,
|
|
4694
4684
|
payloadBodyAsUtf8,
|
|
@@ -4696,10 +4686,8 @@ export const __loadstrikeTestExports = {
|
|
|
4696
4686
|
protocolBus: protocolBus,
|
|
4697
4687
|
readFirstRedisStreamEntry,
|
|
4698
4688
|
resolveConnectionMetadata,
|
|
4699
|
-
resolveKafkaOAuthBearerToken,
|
|
4700
4689
|
serializePayloadBody,
|
|
4701
4690
|
setJsonBodyValue,
|
|
4702
|
-
shouldUseConfluentKafkaClient,
|
|
4703
4691
|
toHeaderRecord,
|
|
4704
4692
|
toKafkaHeadersWithContentType,
|
|
4705
4693
|
toSqsMessageAttributes,
|
|
@@ -4707,6 +4695,9 @@ export const __loadstrikeTestExports = {
|
|
|
4707
4695
|
setSqsClientFactoryForTests: (factory) => {
|
|
4708
4696
|
sqsClientFactoryForTests = factory;
|
|
4709
4697
|
},
|
|
4698
|
+
setKafkaClientFactoryForTests: (factory) => {
|
|
4699
|
+
kafkaClientFactoryForTests = factory;
|
|
4700
|
+
},
|
|
4710
4701
|
validateHttpEndpoint,
|
|
4711
4702
|
validateTrackingSelectorPath
|
|
4712
4703
|
};
|