@tangle-network/agent-integrations 0.53.29 → 0.53.30

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.
@@ -181,6 +181,7 @@ __export(adapters_exports, {
181
181
  copperConnector: () => copperConnector,
182
182
  copyAiConnector: () => copyAiConnector,
183
183
  couchbaseConnector: () => couchbaseConnector,
184
+ createKafkaConnector: () => createKafkaConnector,
184
185
  createSftpConnector: () => createSftpConnector,
185
186
  createTangleIdentityClient: () => createTangleIdentityClient,
186
187
  crispConnector: () => crispConnector,
@@ -352,6 +353,7 @@ __export(adapters_exports, {
352
353
  jotformConnector: () => jotformConnector,
353
354
  justInvoiceConnector: () => justInvoiceConnector,
354
355
  justcallConnector: () => justcallConnector,
356
+ kafkaConnector: () => kafkaConnector,
355
357
  kallabotAiConnector: () => kallabotAiConnector,
356
358
  kapsoConnector: () => kapsoConnector,
357
359
  kimaiConnector: () => kimaiConnector,
@@ -498,6 +500,7 @@ __export(adapters_exports, {
498
500
  predictLeadsConnector: () => predictLeadsConnector,
499
501
  productboardConnector: () => productboardConnector,
500
502
  proxycurlConnector: () => proxycurlConnector,
503
+ publicKafkaSocketFactory: () => publicKafkaSocketFactory,
501
504
  pushbulletConnector: () => pushbulletConnector,
502
505
  pushoverConnector: () => pushoverConnector,
503
506
  qawafelConnector: () => qawafelConnector,
@@ -33157,6 +33160,548 @@ var justcallConnector = declarativeRestConnector({
33157
33160
  ]
33158
33161
  });
33159
33162
 
33163
+ // src/connectors/adapters/kafka.ts
33164
+ import { isIP as isIP2 } from "net";
33165
+ import tls from "tls";
33166
+ import {
33167
+ Kafka,
33168
+ logLevel
33169
+ } from "kafkajs";
33170
+
33171
+ // src/connectors/adapters/public-network.ts
33172
+ import { lookup } from "dns/promises";
33173
+ import ipaddr from "ipaddr.js";
33174
+ function isPublicNetworkAddress(value) {
33175
+ if (!ipaddr.isValid(value)) return false;
33176
+ let address = ipaddr.parse(value);
33177
+ if (address.kind() === "ipv6" && address.isIPv4MappedAddress()) {
33178
+ address = address.toIPv4Address();
33179
+ }
33180
+ return address.range() === "unicast";
33181
+ }
33182
+ async function resolvePublicHostAddresses(host) {
33183
+ const addresses = ipaddr.isValid(host) ? [host] : (await lookup(host, { all: true, verbatim: true })).map((entry) => entry.address);
33184
+ if (addresses.length === 0 || addresses.some((address) => !isPublicNetworkAddress(address))) {
33185
+ throw new Error("host is not a public network target");
33186
+ }
33187
+ return addresses;
33188
+ }
33189
+ var publicDnsLookup = (hostname, options, callback) => {
33190
+ const normalizedOptions = typeof options === "number" ? { family: options } : options;
33191
+ lookup(hostname, { ...normalizedOptions, all: true, verbatim: true }).then((entries) => {
33192
+ if (entries.length === 0 || entries.some((entry) => !isPublicNetworkAddress(entry.address))) {
33193
+ callback(new Error("host is not a public network target"), "", 0);
33194
+ return;
33195
+ }
33196
+ if (normalizedOptions?.all) {
33197
+ callback(null, entries, void 0);
33198
+ return;
33199
+ }
33200
+ callback(null, entries[0].address, entries[0].family);
33201
+ }).catch((error) => callback(error instanceof Error ? error : new Error("DNS lookup failed"), "", 0));
33202
+ };
33203
+
33204
+ // src/connectors/adapters/kafka.ts
33205
+ var MAX_TOPICS = 100;
33206
+ var MAX_MESSAGES = 100;
33207
+ var MAX_MESSAGE_BYTES2 = 1024 * 1024;
33208
+ var MAX_BATCH_BYTES2 = MAX_FILE_BYTES;
33209
+ var MAX_HEADERS = 64;
33210
+ var MAX_POLL_MS = 3e4;
33211
+ function createKafkaConnector(options = {}) {
33212
+ const createClient = options.createClient ?? ((config) => new Kafka(config));
33213
+ return {
33214
+ manifest: {
33215
+ kind: "kafka",
33216
+ displayName: "Apache Kafka",
33217
+ description: "Discover topics and groups, inspect offsets, produce records, and run bounded approved consumers over TLS.",
33218
+ auth: {
33219
+ kind: "api-key",
33220
+ hint: "JSON with brokers and optional SASL or mutual-TLS credentials. TLS is mandatory and broker addresses must resolve publicly."
33221
+ },
33222
+ defaultConsistencyModel: "advisory",
33223
+ category: "webhook",
33224
+ rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
33225
+ capabilities: [
33226
+ readCapability5("kafka.topics.list", "List Kafka topics visible to the connection.", emptySchema()),
33227
+ readCapability5("kafka.topics.describe", "Read partition and replica metadata for selected topics.", {
33228
+ type: "object",
33229
+ properties: { topics: topicArraySchema() },
33230
+ required: ["topics"],
33231
+ additionalProperties: false
33232
+ }),
33233
+ readCapability5("kafka.offsets.list", "Read low, high, and current offsets for one topic.", topicSchema()),
33234
+ readCapability5("kafka.groups.list", "List consumer groups visible to the connection.", emptySchema()),
33235
+ readCapability5("kafka.group.offsets", "Read committed offsets for a consumer group.", {
33236
+ type: "object",
33237
+ properties: {
33238
+ groupId: { type: "string", minLength: 1, maxLength: 255 },
33239
+ topics: topicArraySchema()
33240
+ },
33241
+ required: ["groupId"],
33242
+ additionalProperties: false
33243
+ }),
33244
+ mutationCapability5("kafka.messages.produce", "Produce one or more records with all acknowledgements enabled.", {
33245
+ type: "object",
33246
+ properties: {
33247
+ topic: topicNameSchema(),
33248
+ encoding: { type: "string", enum: ["utf8", "base64"], default: "utf8" },
33249
+ messages: {
33250
+ type: "array",
33251
+ minItems: 1,
33252
+ maxItems: MAX_MESSAGES,
33253
+ items: {
33254
+ type: "object",
33255
+ properties: {
33256
+ value: { type: "string" },
33257
+ key: { type: "string" },
33258
+ partition: { type: "integer", minimum: 0 },
33259
+ headers: { type: "object", additionalProperties: { type: "string" }, maxProperties: MAX_HEADERS }
33260
+ },
33261
+ required: ["value"],
33262
+ additionalProperties: false
33263
+ }
33264
+ }
33265
+ },
33266
+ required: ["topic", "messages"],
33267
+ additionalProperties: false
33268
+ }),
33269
+ mutationCapability5("kafka.messages.consume", "Join a consumer group and return a bounded batch without automatically committing offsets.", {
33270
+ type: "object",
33271
+ properties: {
33272
+ topic: topicNameSchema(),
33273
+ groupId: { type: "string", minLength: 1, maxLength: 255 },
33274
+ encoding: { type: "string", enum: ["utf8", "base64"], default: "utf8" },
33275
+ fromBeginning: { type: "boolean", default: false },
33276
+ limit: { type: "integer", minimum: 1, maximum: MAX_MESSAGES, default: 25 },
33277
+ timeoutMs: { type: "integer", minimum: 250, maximum: MAX_POLL_MS, default: 5e3 }
33278
+ },
33279
+ required: ["topic", "groupId"],
33280
+ additionalProperties: false
33281
+ }),
33282
+ mutationCapability5("kafka.offsets.commit", "Commit explicit next offsets for a consumer group.", {
33283
+ type: "object",
33284
+ properties: {
33285
+ groupId: { type: "string", minLength: 1, maxLength: 255 },
33286
+ offsets: {
33287
+ type: "array",
33288
+ minItems: 1,
33289
+ maxItems: MAX_MESSAGES,
33290
+ items: {
33291
+ type: "object",
33292
+ properties: {
33293
+ topic: topicNameSchema(),
33294
+ partition: { type: "integer", minimum: 0 },
33295
+ offset: { type: "string", pattern: "^[0-9]+$" },
33296
+ metadata: { type: "string", maxLength: 4096 }
33297
+ },
33298
+ required: ["topic", "partition", "offset"],
33299
+ additionalProperties: false
33300
+ }
33301
+ }
33302
+ },
33303
+ required: ["groupId", "offsets"],
33304
+ additionalProperties: false
33305
+ }),
33306
+ mutationCapability5("kafka.topics.create", "Create one Kafka topic with explicit partition and replication counts.", {
33307
+ type: "object",
33308
+ properties: {
33309
+ topic: topicNameSchema(),
33310
+ partitions: { type: "integer", minimum: 1, maximum: 100, default: 1 },
33311
+ replicationFactor: { type: "integer", minimum: 1, maximum: 10, default: 1 }
33312
+ },
33313
+ required: ["topic"],
33314
+ additionalProperties: false
33315
+ }),
33316
+ mutationCapability5("kafka.topics.delete", "Delete one or more Kafka topics.", {
33317
+ type: "object",
33318
+ properties: { topics: topicArraySchema() },
33319
+ required: ["topics"],
33320
+ additionalProperties: false
33321
+ })
33322
+ ]
33323
+ },
33324
+ async executeRead({ source, capabilityName, args }) {
33325
+ const client = createClient(readCredentials(source).config);
33326
+ return withAdmin(client.admin(), async (admin) => {
33327
+ let data4;
33328
+ if (capabilityName === "kafka.topics.list") {
33329
+ data4 = { topics: await admin.listTopics() };
33330
+ } else if (capabilityName === "kafka.topics.describe") {
33331
+ data4 = await admin.fetchTopicMetadata({ topics: readTopics(args.topics) });
33332
+ } else if (capabilityName === "kafka.offsets.list") {
33333
+ const topic = readTopic(args.topic);
33334
+ data4 = { topic, partitions: await admin.fetchTopicOffsets(topic) };
33335
+ } else if (capabilityName === "kafka.groups.list") {
33336
+ data4 = await admin.listGroups();
33337
+ } else if (capabilityName === "kafka.group.offsets") {
33338
+ const groupId5 = readGroupId2(args.groupId);
33339
+ const topics = args.topics === void 0 ? void 0 : readTopics(args.topics);
33340
+ data4 = { groupId: groupId5, offsets: await admin.fetchOffsets({ groupId: groupId5, topics, resolveOffsets: true }) };
33341
+ } else {
33342
+ throw new Error(`Unknown Kafka read capability: ${capabilityName}`);
33343
+ }
33344
+ return { data: jsonSafe(data4), fetchedAt: Date.now() };
33345
+ });
33346
+ },
33347
+ async executeMutation({ source, capabilityName, args }) {
33348
+ const client = createClient(readCredentials(source).config);
33349
+ let data4;
33350
+ if (capabilityName === "kafka.messages.produce") {
33351
+ data4 = await produce(client.producer({ idempotent: true, maxInFlightRequests: 1 }), args);
33352
+ } else if (capabilityName === "kafka.messages.consume") {
33353
+ data4 = await consume(client.consumer({
33354
+ groupId: readGroupId2(args.groupId),
33355
+ allowAutoTopicCreation: false,
33356
+ maxBytes: MAX_BATCH_BYTES2,
33357
+ maxBytesPerPartition: MAX_BATCH_BYTES2
33358
+ }), args);
33359
+ } else if (capabilityName === "kafka.offsets.commit") {
33360
+ data4 = await commitOffsets(client.consumer({
33361
+ groupId: readGroupId2(args.groupId),
33362
+ allowAutoTopicCreation: false
33363
+ }), args);
33364
+ } else if (capabilityName === "kafka.topics.create") {
33365
+ data4 = await withAdmin(client.admin(), async (admin) => ({
33366
+ created: await admin.createTopics({
33367
+ waitForLeaders: true,
33368
+ topics: [{
33369
+ topic: readTopic(args.topic),
33370
+ numPartitions: readBoundedInteger(args.partitions, 1, 1, 100, "partitions"),
33371
+ replicationFactor: readBoundedInteger(args.replicationFactor, 1, 1, 10, "replicationFactor")
33372
+ }]
33373
+ })
33374
+ }));
33375
+ } else if (capabilityName === "kafka.topics.delete") {
33376
+ const topics = readTopics(args.topics);
33377
+ data4 = await withAdmin(client.admin(), async (admin) => {
33378
+ await admin.deleteTopics({ topics });
33379
+ return { topics };
33380
+ });
33381
+ } else {
33382
+ throw new Error(`Unknown Kafka mutation capability: ${capabilityName}`);
33383
+ }
33384
+ return {
33385
+ status: "committed",
33386
+ data: jsonSafe(data4),
33387
+ committedAt: Date.now(),
33388
+ idempotentReplay: false
33389
+ };
33390
+ },
33391
+ async test(source) {
33392
+ try {
33393
+ const admin = createClient(readCredentials(source).config).admin();
33394
+ await withAdmin(admin, (connected) => connected.listTopics());
33395
+ return { ok: true };
33396
+ } catch (error) {
33397
+ return { ok: false, reason: error instanceof Error ? error.message : "Kafka connection test failed" };
33398
+ }
33399
+ }
33400
+ };
33401
+ }
33402
+ var kafkaConnector = createKafkaConnector();
33403
+ var publicKafkaSocketFactory = ({ host, port, ssl, onConnect }) => {
33404
+ validateBrokerHost(host);
33405
+ if (isIP2(host) !== 0 && !isPublicNetworkAddress(host)) {
33406
+ throw new Error("Kafka broker is not a public network target");
33407
+ }
33408
+ const socket = tls.connect({
33409
+ ...ssl,
33410
+ host,
33411
+ port,
33412
+ servername: isIP2(host) === 0 ? host : void 0,
33413
+ lookup: publicDnsLookup
33414
+ }, onConnect);
33415
+ socket.setKeepAlive(true, 6e4);
33416
+ return socket;
33417
+ };
33418
+ async function withAdmin(admin, run) {
33419
+ try {
33420
+ await admin.connect();
33421
+ return await run(admin);
33422
+ } finally {
33423
+ await admin.disconnect().catch(() => void 0);
33424
+ }
33425
+ }
33426
+ async function produce(producer, args) {
33427
+ const topic = readTopic(args.topic);
33428
+ const encoding = readEncoding(args.encoding);
33429
+ const messages = readMessageInputs(args.messages).map((message) => ({
33430
+ value: decodeMessageValue(message.value, encoding, "value"),
33431
+ key: message.key === void 0 ? void 0 : decodeMessageValue(message.key, encoding, "key"),
33432
+ partition: message.partition === void 0 ? void 0 : readBoundedInteger(message.partition, 0, 0, Number.MAX_SAFE_INTEGER, "partition"),
33433
+ headers: readHeaders(message.headers)
33434
+ }));
33435
+ const batchBytes = messages.reduce((total, message) => total + message.value.byteLength + (message.key?.byteLength ?? 0) + headerBytes(message.headers), 0);
33436
+ if (batchBytes > MAX_BATCH_BYTES2) throw new Error(`messages exceed the ${MAX_BATCH_BYTES2}-byte batch limit`);
33437
+ try {
33438
+ await producer.connect();
33439
+ return { topic, records: await producer.send({ topic, messages, acks: -1, timeout: 3e4 }) };
33440
+ } finally {
33441
+ await producer.disconnect().catch(() => void 0);
33442
+ }
33443
+ }
33444
+ async function consume(consumer, args) {
33445
+ const topic = readTopic(args.topic);
33446
+ const encoding = readEncoding(args.encoding);
33447
+ const limit = readBoundedInteger(args.limit, 25, 1, MAX_MESSAGES, "limit");
33448
+ const timeoutMs = readBoundedInteger(args.timeoutMs, 5e3, 250, MAX_POLL_MS, "timeoutMs");
33449
+ const messages = [];
33450
+ let batchBytes = 0;
33451
+ let resolveDone = () => void 0;
33452
+ let rejectDone = () => void 0;
33453
+ const done = new Promise((resolve, reject) => {
33454
+ resolveDone = resolve;
33455
+ rejectDone = reject;
33456
+ });
33457
+ let timer;
33458
+ try {
33459
+ await consumer.connect();
33460
+ await consumer.subscribe({ topics: [topic], fromBeginning: readBoolean(args.fromBeginning, false, "fromBeginning") });
33461
+ const run = consumer.run({
33462
+ autoCommit: false,
33463
+ partitionsConsumedConcurrently: 1,
33464
+ eachMessage: async ({ partition, message }) => {
33465
+ if (messages.length >= limit) return;
33466
+ const messageBytes = (message.key?.byteLength ?? 0) + (message.value?.byteLength ?? 0) + headerBytes(message.headers);
33467
+ if (messageBytes > MAX_MESSAGE_BYTES2) throw new Error(`Kafka message exceeds the ${MAX_MESSAGE_BYTES2}-byte limit`);
33468
+ batchBytes += messageBytes;
33469
+ if (batchBytes > MAX_BATCH_BYTES2) throw new Error(`Kafka batch exceeds the ${MAX_BATCH_BYTES2}-byte limit`);
33470
+ messages.push({
33471
+ topic,
33472
+ partition,
33473
+ offset: message.offset,
33474
+ timestamp: message.timestamp,
33475
+ key: encodeMessageValue(message.key, encoding),
33476
+ value: encodeMessageValue(message.value, encoding),
33477
+ headers: Object.fromEntries(Object.entries(message.headers ?? {}).map(([key, value]) => [key, encodeHeader(value, encoding)]))
33478
+ });
33479
+ if (messages.length >= limit) resolveDone();
33480
+ }
33481
+ });
33482
+ run.catch(rejectDone);
33483
+ timer = setTimeout(resolveDone, timeoutMs);
33484
+ await done;
33485
+ await consumer.stop();
33486
+ await run;
33487
+ return { topic, messages, messageCount: messages.length, offsetsCommitted: false };
33488
+ } finally {
33489
+ if (timer) clearTimeout(timer);
33490
+ await consumer.disconnect().catch(() => void 0);
33491
+ }
33492
+ }
33493
+ async function commitOffsets(consumer, args) {
33494
+ if (!Array.isArray(args.offsets) || args.offsets.length === 0 || args.offsets.length > MAX_MESSAGES) {
33495
+ throw new Error(`offsets must contain 1 through ${MAX_MESSAGES} entries`);
33496
+ }
33497
+ const offsets = args.offsets.map((entry, index) => {
33498
+ if (!isPlainRecord(entry)) throw new Error(`offsets[${index}] must be an object`);
33499
+ const offset = readOptionalString(entry.offset, `offsets[${index}].offset`);
33500
+ if (!offset || !/^[0-9]+$/.test(offset) || BigInt(offset) > 9223372036854775807n) {
33501
+ throw new Error(`offsets[${index}].offset must be a signed 64-bit non-negative integer string`);
33502
+ }
33503
+ return {
33504
+ topic: readTopic(entry.topic),
33505
+ partition: readBoundedInteger(entry.partition, 0, 0, Number.MAX_SAFE_INTEGER, `offsets[${index}].partition`),
33506
+ offset,
33507
+ metadata: readOptionalString(entry.metadata, `offsets[${index}].metadata`)
33508
+ };
33509
+ });
33510
+ try {
33511
+ await consumer.connect();
33512
+ await consumer.commitOffsets(offsets);
33513
+ return { groupId: readGroupId2(args.groupId), offsets };
33514
+ } finally {
33515
+ await consumer.disconnect().catch(() => void 0);
33516
+ }
33517
+ }
33518
+ function readCredentials(source) {
33519
+ let raw;
33520
+ if (source.credentials.kind === "custom") raw = source.credentials.values;
33521
+ else if (source.credentials.kind === "api-key") {
33522
+ try {
33523
+ raw = JSON.parse(source.credentials.apiKey);
33524
+ } catch {
33525
+ throw new Error("Kafka credential must be valid JSON");
33526
+ }
33527
+ } else {
33528
+ throw new Error("Kafka requires a structured credential bundle");
33529
+ }
33530
+ if (!isPlainRecord(raw)) throw new Error("Kafka credential must be a JSON object");
33531
+ const brokers = readBrokers(raw.brokers);
33532
+ const clientId = readOptionalString(raw.clientId, "clientId") ?? "tangle-integration-hub";
33533
+ if (!/^[A-Za-z0-9._-]{1,128}$/.test(clientId)) throw new Error("clientId contains unsupported characters");
33534
+ const sasl = readSasl(raw);
33535
+ const ca = readPemList(raw.tlsCa, "tlsCa");
33536
+ const cert = readOptionalString(raw.tlsCert, "tlsCert");
33537
+ const key = readOptionalString(raw.tlsKey, "tlsKey");
33538
+ if (Boolean(cert) !== Boolean(key)) throw new Error("tlsCert and tlsKey must be supplied together");
33539
+ return {
33540
+ config: {
33541
+ brokers,
33542
+ clientId,
33543
+ ssl: {
33544
+ minVersion: "TLSv1.2",
33545
+ rejectUnauthorized: true,
33546
+ ca,
33547
+ cert,
33548
+ key,
33549
+ passphrase: readOptionalString(raw.tlsPassphrase, "tlsPassphrase")
33550
+ },
33551
+ sasl,
33552
+ socketFactory: publicKafkaSocketFactory,
33553
+ connectionTimeout: 1e4,
33554
+ authenticationTimeout: 1e4,
33555
+ requestTimeout: 3e4,
33556
+ enforceRequestTimeout: true,
33557
+ retry: { retries: 3, maxRetryTime: 3e4 },
33558
+ logLevel: logLevel.NOTHING
33559
+ }
33560
+ };
33561
+ }
33562
+ function readBrokers(value) {
33563
+ if (!Array.isArray(value) || value.length === 0 || value.length > 20) {
33564
+ throw new Error("brokers must contain 1 through 20 host:port entries");
33565
+ }
33566
+ return value.map((entry, index) => {
33567
+ if (typeof entry !== "string" || entry.length === 0) throw new Error(`brokers[${index}] must be a host:port string`);
33568
+ let parsed;
33569
+ try {
33570
+ parsed = new URL(`tcp://${entry}`);
33571
+ } catch {
33572
+ throw new Error(`brokers[${index}] is not a valid host:port`);
33573
+ }
33574
+ const host = parsed.hostname.replace(/^\[|\]$/g, "");
33575
+ if (!parsed.port || parsed.username || parsed.password || !["", "/"].includes(parsed.pathname)) {
33576
+ throw new Error(`brokers[${index}] must contain only host and port`);
33577
+ }
33578
+ validateBrokerHost(host);
33579
+ if (isIP2(host) !== 0 && !isPublicNetworkAddress(host)) throw new Error(`brokers[${index}] is not a public network target`);
33580
+ const port = Number(parsed.port);
33581
+ if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`brokers[${index}] port is invalid`);
33582
+ return entry;
33583
+ });
33584
+ }
33585
+ function validateBrokerHost(host) {
33586
+ const normalized = host.toLowerCase();
33587
+ if (host.length === 0 || host.length > 253 || normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@]/.test(host)) {
33588
+ throw new Error("Kafka broker must be a public hostname or IP address");
33589
+ }
33590
+ }
33591
+ function readSasl(raw) {
33592
+ if (raw.saslMechanism === void 0 || raw.saslMechanism === null) return void 0;
33593
+ if (!["plain", "scram-sha-256", "scram-sha-512"].includes(String(raw.saslMechanism))) {
33594
+ throw new Error("saslMechanism must be plain, scram-sha-256, or scram-sha-512");
33595
+ }
33596
+ const username = readOptionalString(raw.saslUsername, "saslUsername");
33597
+ const password = readOptionalString(raw.saslPassword, "saslPassword");
33598
+ if (!username || !password) throw new Error("SASL requires saslUsername and saslPassword");
33599
+ return { mechanism: raw.saslMechanism, username, password };
33600
+ }
33601
+ function readPemList(value, label) {
33602
+ if (value === void 0 || value === null) return void 0;
33603
+ const values = Array.isArray(value) ? value : [value];
33604
+ if (values.length === 0 || values.some((entry) => typeof entry !== "string" || entry.length === 0)) {
33605
+ throw new Error(`${label} must be a PEM string or non-empty array of PEM strings`);
33606
+ }
33607
+ return values;
33608
+ }
33609
+ function readTopics(value) {
33610
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_TOPICS) {
33611
+ throw new Error(`topics must contain 1 through ${MAX_TOPICS} names`);
33612
+ }
33613
+ const topics = value.map(readTopic);
33614
+ if (new Set(topics).size !== topics.length) throw new Error("topics must not contain duplicates");
33615
+ return topics;
33616
+ }
33617
+ function readTopic(value) {
33618
+ if (typeof value !== "string" || !/^[A-Za-z0-9._-]{1,249}$/.test(value) || value === "." || value === "..") {
33619
+ throw new Error("topic must be 1 through 249 Kafka-safe characters");
33620
+ }
33621
+ return value;
33622
+ }
33623
+ function readGroupId2(value) {
33624
+ if (typeof value !== "string" || value.length === 0 || value.length > 255 || /[\u0000-\u001f]/.test(value)) {
33625
+ throw new Error("groupId must be 1 through 255 printable characters");
33626
+ }
33627
+ return value;
33628
+ }
33629
+ function readEncoding(value) {
33630
+ if (value === void 0 || value === null || value === "utf8") return "utf8";
33631
+ if (value === "base64") return "base64";
33632
+ throw new Error("encoding must be utf8 or base64");
33633
+ }
33634
+ function readMessageInputs(value) {
33635
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_MESSAGES) {
33636
+ throw new Error(`messages must contain 1 through ${MAX_MESSAGES} entries`);
33637
+ }
33638
+ return value.map((entry, index) => {
33639
+ if (!isPlainRecord(entry)) throw new Error(`messages[${index}] must be an object`);
33640
+ return entry;
33641
+ });
33642
+ }
33643
+ function decodeMessageValue(value, encoding, label) {
33644
+ if (typeof value !== "string") throw new Error(`${label} must be a string`);
33645
+ if (encoding === "base64" && (value.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value))) {
33646
+ throw new Error(`${label} must be canonical base64`);
33647
+ }
33648
+ const decoded = Buffer.from(value, encoding);
33649
+ if (decoded.byteLength > MAX_MESSAGE_BYTES2) throw new Error(`${label} exceeds the ${MAX_MESSAGE_BYTES2}-byte limit`);
33650
+ return decoded;
33651
+ }
33652
+ function encodeMessageValue(value, encoding) {
33653
+ return value ? value.toString(encoding) : null;
33654
+ }
33655
+ function encodeHeader(value, encoding) {
33656
+ if (value === void 0) return void 0;
33657
+ if (Array.isArray(value)) return value.map((entry) => Buffer.isBuffer(entry) ? entry.toString(encoding) : entry);
33658
+ return Buffer.isBuffer(value) ? value.toString(encoding) : value;
33659
+ }
33660
+ function readHeaders(value) {
33661
+ if (value === void 0 || value === null) return void 0;
33662
+ if (!isPlainRecord(value) || Object.keys(value).length > MAX_HEADERS) throw new Error(`headers must have at most ${MAX_HEADERS} string fields`);
33663
+ const headers = Object.fromEntries(Object.entries(value).map(([key, entry]) => {
33664
+ if (typeof entry !== "string") throw new Error(`header ${key} must be a string`);
33665
+ return [key, entry];
33666
+ }));
33667
+ if (headerBytes(headers) > MAX_MESSAGE_BYTES2) throw new Error(`headers exceed the ${MAX_MESSAGE_BYTES2}-byte limit`);
33668
+ return headers;
33669
+ }
33670
+ function headerBytes(headers) {
33671
+ if (!headers) return 0;
33672
+ return Object.entries(headers).reduce((total, [key, value]) => {
33673
+ const values = Array.isArray(value) ? value : [value];
33674
+ return total + Buffer.byteLength(key, "utf8") + values.reduce((sum, entry) => {
33675
+ if (Buffer.isBuffer(entry)) return sum + entry.byteLength;
33676
+ if (typeof entry === "string") return sum + Buffer.byteLength(entry, "utf8");
33677
+ return sum;
33678
+ }, 0);
33679
+ }, 0);
33680
+ }
33681
+ function emptySchema() {
33682
+ return { type: "object", properties: {}, additionalProperties: false };
33683
+ }
33684
+ function topicSchema() {
33685
+ return {
33686
+ type: "object",
33687
+ properties: { topic: topicNameSchema() },
33688
+ required: ["topic"],
33689
+ additionalProperties: false
33690
+ };
33691
+ }
33692
+ function topicArraySchema() {
33693
+ return { type: "array", minItems: 1, maxItems: MAX_TOPICS, items: topicNameSchema() };
33694
+ }
33695
+ function topicNameSchema() {
33696
+ return { type: "string", minLength: 1, maxLength: 249, pattern: "^[A-Za-z0-9._-]+$" };
33697
+ }
33698
+ function readCapability5(name, description, parameters) {
33699
+ return { name, class: "read", description, parameters };
33700
+ }
33701
+ function mutationCapability5(name, description, parameters) {
33702
+ return { name, class: "mutation", description, parameters, cas: "none", externalEffect: true };
33703
+ }
33704
+
33160
33705
  // src/connectors/adapters/lead-connector.ts
33161
33706
  var leadConnectorConnector = declarativeRestConnector({
33162
33707
  kind: "lead-connector",
@@ -44720,16 +45265,14 @@ var salesforceConnector = declarativeRestConnector({
44720
45265
  });
44721
45266
 
44722
45267
  // src/connectors/adapters/sftp.ts
44723
- import { lookup } from "dns/promises";
44724
45268
  import { timingSafeEqual as timingSafeEqual4 } from "crypto";
44725
45269
  import { posix } from "path";
44726
- import ipaddr from "ipaddr.js";
44727
45270
  import SftpClient from "ssh2-sftp-client";
44728
45271
  var MAX_LIST_ENTRIES = 1e3;
44729
45272
  var MAX_PATH_LENGTH = 1024;
44730
45273
  function createSftpConnector(options = {}) {
44731
45274
  const createClient = options.createClient ?? (() => new SftpClient());
44732
- const lookupHost = options.lookupHost ?? resolvePublicAddresses;
45275
+ const lookupHost = options.lookupHost ?? resolvePublicHostAddresses;
44733
45276
  return {
44734
45277
  manifest: {
44735
45278
  kind: "sftp",
@@ -44828,7 +45371,7 @@ function createSftpConnector(options = {}) {
44828
45371
  ]
44829
45372
  },
44830
45373
  async executeRead({ source, capabilityName, args }) {
44831
- const credentials = readCredentials(source);
45374
+ const credentials = readCredentials2(source);
44832
45375
  if (capabilityName === "sftp.list") {
44833
45376
  const path = resolveRemotePath(credentials.rootPath, args.path ?? "/");
44834
45377
  const limit = readBoundedInteger(args.limit, MAX_LIST_ENTRIES, 1, MAX_LIST_ENTRIES, "limit");
@@ -44861,7 +45404,7 @@ function createSftpConnector(options = {}) {
44861
45404
  throw new Error(`Unknown SFTP read capability: ${capabilityName}`);
44862
45405
  },
44863
45406
  async executeMutation({ source, capabilityName, args }) {
44864
- const credentials = readCredentials(source);
45407
+ const credentials = readCredentials2(source);
44865
45408
  const prepared = prepareMutation(capabilityName, args, credentials.rootPath);
44866
45409
  return withSftp(credentials, createClient, lookupHost, async (client) => {
44867
45410
  let data4;
@@ -44895,7 +45438,7 @@ function createSftpConnector(options = {}) {
44895
45438
  },
44896
45439
  async test(source) {
44897
45440
  try {
44898
- const credentials = readCredentials(source);
45441
+ const credentials = readCredentials2(source);
44899
45442
  await withSftp(credentials, createClient, lookupHost, (client) => client.cwd());
44900
45443
  return { ok: true };
44901
45444
  } catch (error) {
@@ -44963,7 +45506,7 @@ async function withSftp(credentials, createClient, lookupHost, run) {
44963
45506
  await client.end().catch(() => false);
44964
45507
  }
44965
45508
  }
44966
- function readCredentials(source) {
45509
+ function readCredentials2(source) {
44967
45510
  let raw;
44968
45511
  if (source.credentials.kind === "custom") raw = source.credentials.values;
44969
45512
  else if (source.credentials.kind === "api-key") {
@@ -45004,21 +45547,6 @@ function validateHostname(host) {
45004
45547
  throw new Error("SFTP host must be a public hostname or IP address without a scheme");
45005
45548
  }
45006
45549
  }
45007
- async function resolvePublicAddresses(host) {
45008
- const addresses = ipaddr.isValid(host) ? [host] : (await lookup(host, { all: true, verbatim: true })).map((entry) => entry.address);
45009
- if (addresses.length === 0 || addresses.some((address) => !isPublicAddress(address))) {
45010
- throw new Error("SFTP host is not a public network target");
45011
- }
45012
- return addresses;
45013
- }
45014
- function isPublicAddress(value) {
45015
- if (!ipaddr.isValid(value)) return false;
45016
- let address = ipaddr.parse(value);
45017
- if (address.kind() === "ipv6" && address.isIPv4MappedAddress()) {
45018
- address = address.toIPv4Address();
45019
- }
45020
- return address.range() === "unicast";
45021
- }
45022
45550
  function parseFingerprint(value) {
45023
45551
  if (/^[a-fA-F0-9]{64}$/.test(value)) return Buffer.from(value, "hex");
45024
45552
  const match = /^SHA256:([A-Za-z0-9+/]{43}=?)$/.exec(value);
@@ -54681,17 +55209,17 @@ var zohoBookingsConnector = {
54681
55209
  category: "calendar",
54682
55210
  defaultConsistencyModel: "authoritative",
54683
55211
  capabilities: [
54684
- readCapability5("workspaces.list", "List Zoho Bookings workspaces.", {}),
54685
- readCapability5("services.list", "List services, optionally within one workspace.", {
55212
+ readCapability6("workspaces.list", "List Zoho Bookings workspaces.", {}),
55213
+ readCapability6("services.list", "List services, optionally within one workspace.", {
54686
55214
  workspace_id: { type: "string" }
54687
55215
  }),
54688
- readCapability5("resources.list", "List bookable resources, optionally for one service.", {
55216
+ readCapability6("resources.list", "List bookable resources, optionally for one service.", {
54689
55217
  service_id: { type: "string" }
54690
55218
  }),
54691
- readCapability5("staff.list", "List staff, optionally for one service.", {
55219
+ readCapability6("staff.list", "List staff, optionally for one service.", {
54692
55220
  service_id: { type: "string" }
54693
55221
  }),
54694
- readCapability5("appointments.list", "List appointments from a required start time.", {
55222
+ readCapability6("appointments.list", "List appointments from a required start time.", {
54695
55223
  from_time: {
54696
55224
  type: "string",
54697
55225
  description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format."
@@ -54701,17 +55229,17 @@ var zohoBookingsConnector = {
54701
55229
  staff_id: { type: "string" },
54702
55230
  status: { type: "string" }
54703
55231
  }, ["from_time"]),
54704
- readCapability5("appointments.get", "Read one appointment by booking id.", {
55232
+ readCapability6("appointments.get", "Read one appointment by booking id.", {
54705
55233
  booking_id: { type: "string" }
54706
55234
  }, ["booking_id"]),
54707
- readCapability5("availability.fetch", "Fetch open time slots for a service and date.", {
55235
+ readCapability6("availability.fetch", "Fetch open time slots for a service and date.", {
54708
55236
  service_id: { type: "string" },
54709
55237
  selected_date: { type: "string", description: "Date in YYYY-MM-DD format." },
54710
55238
  staff_id: { type: "string" },
54711
55239
  group_id: { type: "string" },
54712
55240
  resource_id: { type: "string" }
54713
55241
  }, ["service_id", "selected_date"]),
54714
- mutationCapability5("appointments.book", "Book an appointment for a customer.", {
55242
+ mutationCapability6("appointments.book", "Book an appointment for a customer.", {
54715
55243
  service_id: { type: "string" },
54716
55244
  from_time: { type: "string", description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format." },
54717
55245
  customer_details: {
@@ -54727,13 +55255,13 @@ var zohoBookingsConnector = {
54727
55255
  additional_fields: { type: "object" },
54728
55256
  payment_info: { type: "object" }
54729
55257
  }, ["service_id", "from_time", "customer_details"]),
54730
- mutationCapability5("appointments.reschedule", "Move an appointment to a new time, staff member, or group.", {
55258
+ mutationCapability6("appointments.reschedule", "Move an appointment to a new time, staff member, or group.", {
54731
55259
  booking_id: { type: "string" },
54732
55260
  start_time: { type: "string", description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format." },
54733
55261
  staff_id: { type: "string" },
54734
55262
  group_id: { type: "string" }
54735
55263
  }, ["booking_id"]),
54736
- mutationCapability5("appointments.cancel", "Cancel an appointment by booking id.", {
55264
+ mutationCapability6("appointments.cancel", "Cancel an appointment by booking id.", {
54737
55265
  booking_id: { type: "string" }
54738
55266
  }, ["booking_id"])
54739
55267
  ]
@@ -54850,7 +55378,7 @@ var zohoBookingsConnector = {
54850
55378
  }
54851
55379
  }
54852
55380
  };
54853
- function readCapability5(name, description, properties, required = []) {
55381
+ function readCapability6(name, description, properties, required = []) {
54854
55382
  return {
54855
55383
  name,
54856
55384
  class: "read",
@@ -54859,7 +55387,7 @@ function readCapability5(name, description, properties, required = []) {
54859
55387
  requiredScopes: ["zohobookings.data.READ"]
54860
55388
  };
54861
55389
  }
54862
- function mutationCapability5(name, description, properties, required) {
55390
+ function mutationCapability6(name, description, properties, required) {
54863
55391
  return {
54864
55392
  name,
54865
55393
  class: "mutation",
@@ -63755,6 +64283,7 @@ var CONNECTOR_ADAPTER_FACTORIES = [
63755
64283
  defineFactoryAdapter(() => amazonSqsConnector, {}),
63756
64284
  defineFactoryAdapter(() => httpConnector, {}),
63757
64285
  defineFactoryAdapter(() => rssConnector, {}),
64286
+ defineFactoryAdapter(() => kafkaConnector, {}),
63758
64287
  defineFactoryAdapter(() => csvFilesConnector, {}),
63759
64288
  defineFactoryAdapter(() => excelFilesConnector, {}),
63760
64289
  defineFactoryAdapter(() => parquetFilesConnector, {}),
@@ -69735,7 +70264,7 @@ var sanityConnector = declarativeRestConnector({
69735
70264
  var figmaConnector = declarativeRestConnector({
69736
70265
  kind: "figma",
69737
70266
  displayName: "Figma",
69738
- description: "Read Figma files, components, comments, and team projects; post comments back into the design source of truth.",
70267
+ description: "Read Figma files, components, comments, and project metadata; post comments back into the design source of truth.",
69739
70268
  auth: {
69740
70269
  kind: "oauth2",
69741
70270
  authorizationUrl: "https://www.figma.com/oauth",
@@ -69748,7 +70277,7 @@ var figmaConnector = declarativeRestConnector({
69748
70277
  "file_dev_resources:read",
69749
70278
  "file_versions:read",
69750
70279
  "library_content:read",
69751
- "projects:read",
70280
+ "project_metadata:read",
69752
70281
  "team_library_content:read",
69753
70282
  "webhooks:write"
69754
70283
  ],
@@ -69964,35 +70493,16 @@ var figmaConnector = declarativeRestConnector({
69964
70493
  requiredScopes: ["file_comments:read"]
69965
70494
  },
69966
70495
  {
69967
- name: "teams.projects.list",
69968
- class: "read",
69969
- description: "List projects inside a Figma team.",
69970
- parameters: {
69971
- type: "object",
69972
- properties: { team_id: { type: "string" } },
69973
- required: ["team_id"]
69974
- },
69975
- request: { method: "GET", path: "/v1/teams/{team_id}/projects" },
69976
- requiredScopes: ["projects:read"]
69977
- },
69978
- {
69979
- name: "projects.files.list",
70496
+ name: "projects.metadata.get",
69980
70497
  class: "read",
69981
- description: "List Figma files inside a project.",
70498
+ description: "Get metadata for a Figma project visible to the authenticated user.",
69982
70499
  parameters: {
69983
70500
  type: "object",
69984
- properties: {
69985
- project_id: { type: "string" },
69986
- branch_data: { type: "boolean" }
69987
- },
70501
+ properties: { project_id: { type: "string" } },
69988
70502
  required: ["project_id"]
69989
70503
  },
69990
- request: {
69991
- method: "GET",
69992
- path: "/v1/projects/{project_id}/files",
69993
- query: { branch_data: "{branch_data}" }
69994
- },
69995
- requiredScopes: ["projects:read"]
70504
+ request: { method: "GET", path: "/v1/projects/{project_id}/meta" },
70505
+ requiredScopes: ["project_metadata:read"]
69996
70506
  },
69997
70507
  {
69998
70508
  name: "teams.components.list",
@@ -70159,7 +70669,7 @@ var figmaConnector = declarativeRestConnector({
70159
70669
  var figjamConnector = declarativeRestConnector({
70160
70670
  kind: "figjam",
70161
70671
  displayName: "FigJam",
70162
- description: "Read FigJam boards (stickies, connectors, shapes, sections), list team/project boards, render board images, and post or delete board comments through the Figma REST API.",
70672
+ description: "Read FigJam boards (stickies, connectors, shapes, sections), project metadata, render board images, and post or delete board comments through the Figma REST API.",
70163
70673
  auth: {
70164
70674
  kind: "oauth2",
70165
70675
  authorizationUrl: "https://www.figma.com/oauth",
@@ -70170,7 +70680,7 @@ var figjamConnector = declarativeRestConnector({
70170
70680
  "file_comments:write",
70171
70681
  "file_content:read",
70172
70682
  "file_versions:read",
70173
- "projects:read",
70683
+ "project_metadata:read",
70174
70684
  "webhooks:write"
70175
70685
  ],
70176
70686
  clientIdEnv: "FIGMA_OAUTH_CLIENT_ID",
@@ -70328,33 +70838,16 @@ var figjamConnector = declarativeRestConnector({
70328
70838
  requiredScopes: ["file_comments:read"]
70329
70839
  },
70330
70840
  {
70331
- name: "teams.projects.list",
70841
+ name: "projects.metadata.get",
70332
70842
  class: "read",
70333
- description: "List projects inside a Figma team (FigJam boards live alongside Figma design files inside projects).",
70843
+ description: "Get metadata for a Figma/FigJam project visible to the authenticated user.",
70334
70844
  parameters: {
70335
70845
  type: "object",
70336
- properties: { team_id: { type: "string" } },
70337
- required: ["team_id"]
70338
- },
70339
- request: { method: "GET", path: "/v1/teams/{team_id}/projects" },
70340
- requiredScopes: ["projects:read"]
70341
- },
70342
- {
70343
- name: "projects.files.list",
70344
- class: "read",
70345
- description: 'List files inside a project \u2014 response items expose a `type` field; FigJam boards are returned with type "jam" and design files with type "design".',
70346
- parameters: {
70347
- type: "object",
70348
- properties: {
70349
- project_id: { type: "string" }
70350
- },
70846
+ properties: { project_id: { type: "string" } },
70351
70847
  required: ["project_id"]
70352
70848
  },
70353
- request: {
70354
- method: "GET",
70355
- path: "/v1/projects/{project_id}/files"
70356
- },
70357
- requiredScopes: ["projects:read"]
70849
+ request: { method: "GET", path: "/v1/projects/{project_id}/meta" },
70850
+ requiredScopes: ["project_metadata:read"]
70358
70851
  },
70359
70852
  {
70360
70853
  name: "files.comments.create",
@@ -143754,6 +144247,9 @@ export {
143754
144247
  jiraCloudConnector,
143755
144248
  jotformConnector,
143756
144249
  justcallConnector,
144250
+ createKafkaConnector,
144251
+ kafkaConnector,
144252
+ publicKafkaSocketFactory,
143757
144253
  leadConnectorConnector,
143758
144254
  lemonSqueezyConnector,
143759
144255
  linearConnector,
@@ -144249,4 +144745,4 @@ export {
144249
144745
  demandbaseConnector,
144250
144746
  adapters_exports
144251
144747
  };
144252
- //# sourceMappingURL=chunk-ARONCYSG.js.map
144748
+ //# sourceMappingURL=chunk-5LDZLVKP.js.map