@tangle-network/agent-integrations 0.53.29 → 0.53.32

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,10 @@ __export(adapters_exports, {
181
181
  copperConnector: () => copperConnector,
182
182
  copyAiConnector: () => copyAiConnector,
183
183
  couchbaseConnector: () => couchbaseConnector,
184
+ createKafkaConnector: () => createKafkaConnector,
185
+ createMySqlConnector: () => createMySqlConnector,
186
+ createRabbitMqConnector: () => createRabbitMqConnector,
187
+ createRedisConnector: () => createRedisConnector,
184
188
  createSftpConnector: () => createSftpConnector,
185
189
  createTangleIdentityClient: () => createTangleIdentityClient,
186
190
  crispConnector: () => crispConnector,
@@ -217,6 +221,7 @@ __export(adapters_exports, {
217
221
  dripConnector: () => dripConnector,
218
222
  dropboxConnector: () => dropboxConnector,
219
223
  dubConnector: () => dubConnector,
224
+ duckdbConnector: () => duckdbConnector,
220
225
  dumplingAiConnector: () => dumplingAiConnector,
221
226
  dustConnector: () => dustConnector,
222
227
  easyPeasyAiConnector: () => easyPeasyAiConnector,
@@ -352,6 +357,7 @@ __export(adapters_exports, {
352
357
  jotformConnector: () => jotformConnector,
353
358
  justInvoiceConnector: () => justInvoiceConnector,
354
359
  justcallConnector: () => justcallConnector,
360
+ kafkaConnector: () => kafkaConnector,
355
361
  kallabotAiConnector: () => kallabotAiConnector,
356
362
  kapsoConnector: () => kapsoConnector,
357
363
  kimaiConnector: () => kimaiConnector,
@@ -436,6 +442,7 @@ __export(adapters_exports, {
436
442
  mondayConnector: () => mondayConnector,
437
443
  mongodbConnector: () => mongodbConnector,
438
444
  mycaseConnector: () => mycaseConnector,
445
+ mysqlConnector: () => mysqlConnector,
439
446
  n8nConnector: () => n8nConnector,
440
447
  netlifyConnector: () => netlifyConnector,
441
448
  netsuiteConnector: () => netsuiteConnector,
@@ -498,6 +505,7 @@ __export(adapters_exports, {
498
505
  predictLeadsConnector: () => predictLeadsConnector,
499
506
  productboardConnector: () => productboardConnector,
500
507
  proxycurlConnector: () => proxycurlConnector,
508
+ publicKafkaSocketFactory: () => publicKafkaSocketFactory,
501
509
  pushbulletConnector: () => pushbulletConnector,
502
510
  pushoverConnector: () => pushoverConnector,
503
511
  qawafelConnector: () => qawafelConnector,
@@ -505,6 +513,7 @@ __export(adapters_exports, {
505
513
  quadernoConnector: () => quadernoConnector,
506
514
  quickbooksConnector: () => quickbooksConnector,
507
515
  quickzuConnector: () => quickzuConnector,
516
+ rabbitMqConnector: () => rabbitMqConnector,
508
517
  raiaAiConnector: () => raiaAiConnector,
509
518
  rampConnector: () => rampConnector,
510
519
  rapidtextAiConnector: () => rapidtextAiConnector,
@@ -512,6 +521,7 @@ __export(adapters_exports, {
512
521
  recallAiConnector: () => recallAiConnector,
513
522
  recurlyConnector: () => recurlyConnector,
514
523
  redditConnector: () => redditConnector,
524
+ redisConnector: () => redisConnector,
515
525
  reoonVerifierConnector: () => reoonVerifierConnector,
516
526
  replyIoConnector: () => replyIoConnector,
517
527
  resolveConnectorAdapterFactoryOptions: () => resolveConnectorAdapterFactoryOptions,
@@ -1120,6 +1130,10 @@ function sameUrl(left, right) {
1120
1130
  function applyCredentials(headers, url, placement, credentials) {
1121
1131
  const token = credentialToken(credentials);
1122
1132
  if (placement.kind === "bearer") headers.authorization = `Bearer ${token}`;
1133
+ if (placement.kind === "authorization-by-auth") {
1134
+ const prefix = credentials.kind === "oauth2" ? placement.oauth2Prefix ?? "Bearer " : placement.apiKeyPrefix ?? "";
1135
+ headers.authorization = `${prefix}${token}`;
1136
+ }
1123
1137
  if (placement.kind === "basic-api-key") {
1124
1138
  headers.authorization = `Basic ${Buffer.from(`${token}:`).toString("base64")}`;
1125
1139
  }
@@ -6497,6 +6511,294 @@ var digitalPilotConnector = declarativeRestConnector({
6497
6511
  ]
6498
6512
  });
6499
6513
 
6514
+ // src/connectors/adapters/duckdb.ts
6515
+ import {
6516
+ DuckDBInstance
6517
+ } from "@duckdb/node-api";
6518
+ var MAX_ARGUMENTS = 100;
6519
+ var MAX_COLUMNS2 = 256;
6520
+ var MAX_EXECUTION_MS = 5e3;
6521
+ var MAX_INPUT_BYTES = 10 * 1024 * 1024;
6522
+ var MAX_QUERY_BYTES = 2e4;
6523
+ var MAX_RESULT_BYTES = 10 * 1024 * 1024;
6524
+ var MAX_ROWS = 1e4;
6525
+ var MAX_SCHEMA_DEPTH = 16;
6526
+ var MAX_TABLES = 16;
6527
+ var TABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,62}$/;
6528
+ var duckdbConnector = {
6529
+ manifest: {
6530
+ kind: "duckdb",
6531
+ displayName: "DuckDB",
6532
+ description: "Load bounded JSON tables into a throwaway in-memory DuckDB database and run a parameterized query.",
6533
+ auth: { kind: "none" },
6534
+ defaultConsistencyModel: "advisory",
6535
+ category: "database",
6536
+ capabilities: [
6537
+ {
6538
+ name: "create.and.query.db",
6539
+ class: "read",
6540
+ description: "Create in-memory tables from JSON arrays and return a bounded parameterized SQL query result.",
6541
+ parameters: {
6542
+ type: "object",
6543
+ properties: {
6544
+ tables: {
6545
+ type: "array",
6546
+ minItems: 1,
6547
+ maxItems: MAX_TABLES,
6548
+ items: {
6549
+ type: "object",
6550
+ properties: {
6551
+ name: { type: "string", pattern: TABLE_NAME.source, maxLength: 63 },
6552
+ data: { type: "array", maxItems: MAX_ROWS, items: { type: "object" } },
6553
+ schema: { type: "object", maxProperties: MAX_COLUMNS2 }
6554
+ },
6555
+ required: ["name", "data"],
6556
+ additionalProperties: false
6557
+ }
6558
+ },
6559
+ query: {
6560
+ type: "string",
6561
+ minLength: 1,
6562
+ maxLength: MAX_QUERY_BYTES,
6563
+ description: "A single relation-returning SQL query. Use $1, $2, and args for dynamic values."
6564
+ },
6565
+ args: {
6566
+ type: "array",
6567
+ maxItems: MAX_ARGUMENTS,
6568
+ items: { type: ["string", "number", "boolean", "null"] }
6569
+ },
6570
+ maxRows: { type: "integer", minimum: 1, maximum: MAX_ROWS, default: 1e3 }
6571
+ },
6572
+ required: ["tables", "query"],
6573
+ additionalProperties: false
6574
+ }
6575
+ }
6576
+ ]
6577
+ },
6578
+ async executeRead({ capabilityName, args }) {
6579
+ if (capabilityName !== "create.and.query.db") {
6580
+ throw new Error(`Unknown DuckDB capability: ${capabilityName}`);
6581
+ }
6582
+ const tables = readTables(args.tables);
6583
+ const query2 = readQuery(args.query);
6584
+ const queryArgs = readArguments(args.args);
6585
+ const maxRows = readBoundedInteger(args.maxRows, 1e3, 1, MAX_ROWS, "maxRows");
6586
+ const instance = await DuckDBInstance.create(":memory:");
6587
+ let connection;
6588
+ let timedOut = false;
6589
+ let timeout;
6590
+ try {
6591
+ const activeConnection = await instance.connect();
6592
+ connection = activeConnection;
6593
+ timeout = setTimeout(() => {
6594
+ timedOut = true;
6595
+ activeConnection.interrupt();
6596
+ }, MAX_EXECUTION_MS);
6597
+ await configureConnection(activeConnection);
6598
+ throwIfTimedOut(timedOut);
6599
+ for (const table of tables) {
6600
+ await loadTable(activeConnection, table);
6601
+ throwIfTimedOut(timedOut);
6602
+ }
6603
+ const reader = await activeConnection.runAndReadAll(
6604
+ `SELECT * FROM (${query2}) AS "_tangle_result" LIMIT ${maxRows + 1}`,
6605
+ queryArgs
6606
+ );
6607
+ throwIfTimedOut(timedOut);
6608
+ const allRows = reader.getRowObjectsJson();
6609
+ const truncated = allRows.length > maxRows;
6610
+ const rows = truncated ? allRows.slice(0, maxRows) : allRows;
6611
+ assertResultSize(rows);
6612
+ return {
6613
+ data: {
6614
+ rows,
6615
+ rowCount: rows.length,
6616
+ truncated,
6617
+ columns: reader.columnNameAndTypeObjectsJson()
6618
+ },
6619
+ fetchedAt: Date.now()
6620
+ };
6621
+ } catch (error) {
6622
+ if (timedOut) throw new Error(`DuckDB execution exceeded ${MAX_EXECUTION_MS}ms`);
6623
+ throw error;
6624
+ } finally {
6625
+ if (timeout) clearTimeout(timeout);
6626
+ try {
6627
+ connection?.closeSync();
6628
+ } finally {
6629
+ instance.closeSync();
6630
+ }
6631
+ }
6632
+ },
6633
+ async test() {
6634
+ const instance = await DuckDBInstance.create(":memory:");
6635
+ let connection;
6636
+ try {
6637
+ const activeConnection = await instance.connect();
6638
+ connection = activeConnection;
6639
+ await configureConnection(activeConnection);
6640
+ const reader = await activeConnection.runAndReadAll("SELECT 1 AS ok");
6641
+ if (reader.getRowObjectsJson()[0]?.ok !== 1) return { ok: false, reason: "DuckDB SELECT 1 returned an unexpected result" };
6642
+ return { ok: true };
6643
+ } catch (error) {
6644
+ return { ok: false, reason: error instanceof Error ? error.message : String(error) };
6645
+ } finally {
6646
+ try {
6647
+ connection?.closeSync();
6648
+ } finally {
6649
+ instance.closeSync();
6650
+ }
6651
+ }
6652
+ }
6653
+ };
6654
+ async function configureConnection(connection) {
6655
+ await connection.run(`
6656
+ SET memory_limit = '256MB';
6657
+ SET max_temp_directory_size = '0B';
6658
+ SET threads = 2;
6659
+ SET enable_logging = false;
6660
+ SET enable_external_access = false;
6661
+ SET lock_configuration = true;
6662
+ `);
6663
+ }
6664
+ async function loadTable(connection, table) {
6665
+ const sourceData = stringifyJson(table.data, `${table.name}.data`);
6666
+ let sourceSchema;
6667
+ if (table.schema) {
6668
+ assertDuckDbSchema(table.schema, `${table.name}.schema`);
6669
+ sourceSchema = stringifyJson([table.schema], `${table.name}.schema`);
6670
+ } else {
6671
+ if (table.data.length === 0) {
6672
+ throw new Error(`${table.name}.schema is required when data is empty`);
6673
+ }
6674
+ const detected = await connection.runAndReadAll(
6675
+ "SELECT json_structure($sourceData) AS schema",
6676
+ { sourceData }
6677
+ );
6678
+ const value = detected.getRowsJson()[0]?.[0];
6679
+ if (typeof value !== "string") throw new Error(`Could not infer a schema for ${table.name}`);
6680
+ let detectedSchema;
6681
+ try {
6682
+ detectedSchema = JSON.parse(value);
6683
+ } catch {
6684
+ throw new Error(`Could not parse the inferred schema for ${table.name}`);
6685
+ }
6686
+ assertDuckDbSchema(detectedSchema, `${table.name}.inferredSchema`);
6687
+ sourceSchema = value;
6688
+ }
6689
+ await connection.run(
6690
+ `CREATE TABLE ${quoteIdentifier(table.name)} AS
6691
+ SELECT UNNEST(JSON_TRANSFORM($sourceData, $sourceSchema), recursive := true)`,
6692
+ { sourceData, sourceSchema }
6693
+ );
6694
+ }
6695
+ function readTables(value) {
6696
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_TABLES) {
6697
+ throw new Error(`tables must contain 1 through ${MAX_TABLES} entries`);
6698
+ }
6699
+ assertInputSize(value);
6700
+ let totalRows = 0;
6701
+ const tables = value.map((entry, tableIndex) => {
6702
+ if (!isPlainRecord(entry)) throw new Error(`tables[${tableIndex}] must be an object`);
6703
+ const { name, data: data4, schema } = entry;
6704
+ if (typeof name !== "string" || !TABLE_NAME.test(name)) {
6705
+ throw new Error(`tables[${tableIndex}].name must be a safe SQL identifier`);
6706
+ }
6707
+ if (!Array.isArray(data4) || data4.length > MAX_ROWS) {
6708
+ throw new Error(`tables[${tableIndex}].data must contain at most ${MAX_ROWS} rows`);
6709
+ }
6710
+ const records = data4.map((record, rowIndex) => {
6711
+ if (!isPlainRecord(record)) throw new Error(`tables[${tableIndex}].data[${rowIndex}] must be an object`);
6712
+ if (Object.keys(record).length > MAX_COLUMNS2) {
6713
+ throw new Error(`tables[${tableIndex}].data[${rowIndex}] exceeds ${MAX_COLUMNS2} columns`);
6714
+ }
6715
+ return record;
6716
+ });
6717
+ const columns = new Set(records.flatMap((record) => Object.keys(record)));
6718
+ if (columns.size > MAX_COLUMNS2) {
6719
+ throw new Error(`tables[${tableIndex}].data exceeds ${MAX_COLUMNS2} distinct columns`);
6720
+ }
6721
+ totalRows += records.length;
6722
+ if (totalRows > MAX_ROWS) throw new Error(`tables exceed the ${MAX_ROWS}-row total limit`);
6723
+ if (schema !== void 0 && (!isPlainRecord(schema) || Object.keys(schema).length > MAX_COLUMNS2)) {
6724
+ throw new Error(`tables[${tableIndex}].schema must contain at most ${MAX_COLUMNS2} fields`);
6725
+ }
6726
+ return { name, data: records, schema };
6727
+ });
6728
+ if (new Set(tables.map((table) => table.name.toLowerCase())).size !== tables.length) {
6729
+ throw new Error("table names must be unique ignoring case");
6730
+ }
6731
+ return tables;
6732
+ }
6733
+ function readQuery(value) {
6734
+ if (typeof value !== "string" || value.trim().length === 0) throw new Error("query is required");
6735
+ if (Buffer.byteLength(value, "utf8") > MAX_QUERY_BYTES) {
6736
+ throw new Error(`query exceeds the ${MAX_QUERY_BYTES}-byte limit`);
6737
+ }
6738
+ return value.trim().replace(/;\s*$/, "");
6739
+ }
6740
+ function readArguments(value) {
6741
+ if (value === void 0 || value === null) return {};
6742
+ if (!Array.isArray(value) || value.length > MAX_ARGUMENTS) {
6743
+ throw new Error(`args must contain at most ${MAX_ARGUMENTS} scalar values`);
6744
+ }
6745
+ return Object.fromEntries(value.map((entry, index) => {
6746
+ if (entry !== null && typeof entry !== "string" && typeof entry !== "number" && typeof entry !== "boolean") {
6747
+ throw new Error(`args[${index}] must be a string, number, boolean, or null`);
6748
+ }
6749
+ if (typeof entry === "number" && !Number.isFinite(entry)) throw new Error(`args[${index}] must be finite`);
6750
+ return [String(index + 1), entry];
6751
+ }));
6752
+ }
6753
+ function assertInputSize(value) {
6754
+ const serialized = stringifyJson(value, "tables");
6755
+ if (Buffer.byteLength(serialized, "utf8") > MAX_INPUT_BYTES) {
6756
+ throw new Error(`tables exceed the ${MAX_INPUT_BYTES}-byte input limit`);
6757
+ }
6758
+ }
6759
+ function assertResultSize(rows) {
6760
+ const serialized = stringifyJson(rows, "DuckDB result");
6761
+ if (Buffer.byteLength(serialized, "utf8") > MAX_RESULT_BYTES) {
6762
+ throw new Error(`DuckDB result exceeds the ${MAX_RESULT_BYTES}-byte output limit`);
6763
+ }
6764
+ }
6765
+ function assertDuckDbSchema(value, label) {
6766
+ let leaves = 0;
6767
+ const visit = (entry, depth) => {
6768
+ if (depth > MAX_SCHEMA_DEPTH) throw new Error(`${label} exceeds ${MAX_SCHEMA_DEPTH} nested levels`);
6769
+ if (typeof entry === "string") {
6770
+ leaves += 1;
6771
+ if (leaves > MAX_COLUMNS2) throw new Error(`${label} exceeds ${MAX_COLUMNS2} leaf columns`);
6772
+ return;
6773
+ }
6774
+ if (Array.isArray(entry)) {
6775
+ if (entry.length !== 1) throw new Error(`${label} array schemas must contain exactly one element type`);
6776
+ visit(entry[0], depth + 1);
6777
+ return;
6778
+ }
6779
+ if (!isPlainRecord(entry) || Object.keys(entry).length === 0) {
6780
+ throw new Error(`${label} must describe at least one DuckDB column type`);
6781
+ }
6782
+ for (const child of Object.values(entry)) visit(child, depth + 1);
6783
+ };
6784
+ visit(value, 0);
6785
+ }
6786
+ function stringifyJson(value, label) {
6787
+ try {
6788
+ const serialized = JSON.stringify(value);
6789
+ if (serialized === void 0) throw new Error("undefined");
6790
+ return serialized;
6791
+ } catch {
6792
+ throw new Error(`${label} must be JSON-serializable`);
6793
+ }
6794
+ }
6795
+ function quoteIdentifier(value) {
6796
+ return `"${value.replaceAll('"', '""')}"`;
6797
+ }
6798
+ function throwIfTimedOut(timedOut) {
6799
+ if (timedOut) throw new Error(`DuckDB execution exceeded ${MAX_EXECUTION_MS}ms`);
6800
+ }
6801
+
6500
6802
  // src/connectors/adapters/docsbot.ts
6501
6803
  var docsbotConnector = declarativeRestConnector({
6502
6804
  kind: "docsbot",
@@ -6914,7 +7216,7 @@ var elevenlabsConnector = declarativeRestConnector({
6914
7216
  // src/connectors/adapters/excel-files.ts
6915
7217
  import readXlsxFile from "read-excel-file/node";
6916
7218
  import writeXlsxFile from "write-excel-file/node";
6917
- var MAX_COLUMNS2 = 256;
7219
+ var MAX_COLUMNS3 = 256;
6918
7220
  var MAX_SHEETS = 50;
6919
7221
  var MAX_UNCOMPRESSED_WORKBOOK_BYTES = 50 * 1024 * 1024;
6920
7222
  var MAX_ZIP_ENTRIES = 1e3;
@@ -6955,7 +7257,7 @@ var excelFilesConnector = {
6955
7257
  type: "array",
6956
7258
  minItems: 1,
6957
7259
  maxItems: MAX_RECORDS,
6958
- items: { type: "array", maxItems: MAX_COLUMNS2 }
7260
+ items: { type: "array", maxItems: MAX_COLUMNS3 }
6959
7261
  },
6960
7262
  sheetName: { type: "string", minLength: 1, maxLength: 31, default: "Sheet1" }
6961
7263
  },
@@ -6985,8 +7287,8 @@ var excelFilesConnector = {
6985
7287
  totalRows += sheet.data.length;
6986
7288
  if (totalRows > MAX_RECORDS) throw new Error(`workbook exceeds the ${MAX_RECORDS}-row limit`);
6987
7289
  for (const [rowIndex, row] of sheet.data.entries()) {
6988
- if (row.length > MAX_COLUMNS2) {
6989
- throw new Error(`sheet ${sheet.sheet} row ${rowIndex + 1} exceeds the ${MAX_COLUMNS2}-column limit`);
7290
+ if (row.length > MAX_COLUMNS3) {
7291
+ throw new Error(`sheet ${sheet.sheet} row ${rowIndex + 1} exceeds the ${MAX_COLUMNS3}-column limit`);
6990
7292
  }
6991
7293
  }
6992
7294
  return { name: sheet.sheet, rows: jsonSafe(sheet.data) };
@@ -7000,7 +7302,7 @@ var excelFilesConnector = {
7000
7302
  if (capabilityName !== "excel.create") throw new Error(`Unknown Excel capability: ${capabilityName}`);
7001
7303
  const rows = readRows(args.rows);
7002
7304
  for (const [index, row] of rows.entries()) {
7003
- if (row.length > MAX_COLUMNS2) throw new Error(`rows[${index}] exceeds the ${MAX_COLUMNS2}-column limit`);
7305
+ if (row.length > MAX_COLUMNS3) throw new Error(`rows[${index}] exceeds the ${MAX_COLUMNS3}-column limit`);
7004
7306
  }
7005
7307
  const sheetName = readOptionalString(args.sheetName, "sheetName") ?? "Sheet1";
7006
7308
  validateSheetName(sheetName);
@@ -16298,16 +16600,27 @@ var clickupConnector = declarativeRestConnector({
16298
16600
  displayName: "ClickUp",
16299
16601
  description: "Read and mutate ClickUp workspaces, spaces, folders, lists, tasks, comments, and time entries via the ClickUp REST v2 API.",
16300
16602
  auth: {
16301
- kind: "oauth2",
16302
- authorizationUrl: "https://app.clickup.com/api",
16303
- tokenUrl: "https://api.clickup.com/api/v2/oauth/token",
16304
- scopes: [],
16305
- clientIdEnv: "CLICKUP_OAUTH_CLIENT_ID",
16306
- clientSecretEnv: "CLICKUP_OAUTH_CLIENT_SECRET"
16603
+ kind: "one_of",
16604
+ preferred: "api-key",
16605
+ options: [
16606
+ {
16607
+ kind: "api-key",
16608
+ hint: "ClickUp personal API token (starts with pk_)."
16609
+ },
16610
+ {
16611
+ kind: "oauth2",
16612
+ authorizationUrl: "https://app.clickup.com/api",
16613
+ tokenUrl: "https://api.clickup.com/api/v2/oauth/token",
16614
+ scopes: [],
16615
+ clientIdEnv: "CLICKUP_OAUTH_CLIENT_ID",
16616
+ clientSecretEnv: "CLICKUP_OAUTH_CLIENT_SECRET"
16617
+ }
16618
+ ]
16307
16619
  },
16308
16620
  category: "other",
16309
16621
  defaultConsistencyModel: "authoritative",
16310
16622
  baseUrl: "https://api.clickup.com/api/v2",
16623
+ credentialPlacement: { kind: "authorization-by-auth" },
16311
16624
  // GET /user — returns the authorized user; used as a cheap liveness probe.
16312
16625
  test: { method: "GET", path: "/user" },
16313
16626
  capabilities: [
@@ -33157,6 +33470,548 @@ var justcallConnector = declarativeRestConnector({
33157
33470
  ]
33158
33471
  });
33159
33472
 
33473
+ // src/connectors/adapters/kafka.ts
33474
+ import { isIP as isIP2 } from "net";
33475
+ import tls from "tls";
33476
+ import {
33477
+ Kafka,
33478
+ logLevel
33479
+ } from "kafkajs";
33480
+
33481
+ // src/connectors/adapters/public-network.ts
33482
+ import { lookup } from "dns/promises";
33483
+ import ipaddr from "ipaddr.js";
33484
+ function isPublicNetworkAddress(value) {
33485
+ if (!ipaddr.isValid(value)) return false;
33486
+ let address = ipaddr.parse(value);
33487
+ if (address.kind() === "ipv6" && address.isIPv4MappedAddress()) {
33488
+ address = address.toIPv4Address();
33489
+ }
33490
+ return address.range() === "unicast";
33491
+ }
33492
+ async function resolvePublicHostAddresses(host) {
33493
+ const addresses = ipaddr.isValid(host) ? [host] : (await lookup(host, { all: true, verbatim: true })).map((entry) => entry.address);
33494
+ if (addresses.length === 0 || addresses.some((address) => !isPublicNetworkAddress(address))) {
33495
+ throw new Error("host is not a public network target");
33496
+ }
33497
+ return addresses;
33498
+ }
33499
+ var publicDnsLookup = (hostname, options, callback) => {
33500
+ const normalizedOptions = typeof options === "number" ? { family: options } : options;
33501
+ lookup(hostname, { ...normalizedOptions, all: true, verbatim: true }).then((entries) => {
33502
+ if (entries.length === 0 || entries.some((entry) => !isPublicNetworkAddress(entry.address))) {
33503
+ callback(new Error("host is not a public network target"), "", 0);
33504
+ return;
33505
+ }
33506
+ if (normalizedOptions?.all) {
33507
+ callback(null, entries, void 0);
33508
+ return;
33509
+ }
33510
+ callback(null, entries[0].address, entries[0].family);
33511
+ }).catch((error) => callback(error instanceof Error ? error : new Error("DNS lookup failed"), "", 0));
33512
+ };
33513
+
33514
+ // src/connectors/adapters/kafka.ts
33515
+ var MAX_TOPICS = 100;
33516
+ var MAX_MESSAGES = 100;
33517
+ var MAX_MESSAGE_BYTES2 = 1024 * 1024;
33518
+ var MAX_BATCH_BYTES2 = MAX_FILE_BYTES;
33519
+ var MAX_HEADERS = 64;
33520
+ var MAX_POLL_MS = 3e4;
33521
+ function createKafkaConnector(options = {}) {
33522
+ const createClient = options.createClient ?? ((config) => new Kafka(config));
33523
+ return {
33524
+ manifest: {
33525
+ kind: "kafka",
33526
+ displayName: "Apache Kafka",
33527
+ description: "Discover topics and groups, inspect offsets, produce records, and run bounded approved consumers over TLS.",
33528
+ auth: {
33529
+ kind: "api-key",
33530
+ hint: "JSON with brokers and optional SASL or mutual-TLS credentials. TLS is mandatory and broker addresses must resolve publicly."
33531
+ },
33532
+ defaultConsistencyModel: "advisory",
33533
+ category: "webhook",
33534
+ rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
33535
+ capabilities: [
33536
+ readCapability5("kafka.topics.list", "List Kafka topics visible to the connection.", emptySchema()),
33537
+ readCapability5("kafka.topics.describe", "Read partition and replica metadata for selected topics.", {
33538
+ type: "object",
33539
+ properties: { topics: topicArraySchema() },
33540
+ required: ["topics"],
33541
+ additionalProperties: false
33542
+ }),
33543
+ readCapability5("kafka.offsets.list", "Read low, high, and current offsets for one topic.", topicSchema()),
33544
+ readCapability5("kafka.groups.list", "List consumer groups visible to the connection.", emptySchema()),
33545
+ readCapability5("kafka.group.offsets", "Read committed offsets for a consumer group.", {
33546
+ type: "object",
33547
+ properties: {
33548
+ groupId: { type: "string", minLength: 1, maxLength: 255 },
33549
+ topics: topicArraySchema()
33550
+ },
33551
+ required: ["groupId"],
33552
+ additionalProperties: false
33553
+ }),
33554
+ mutationCapability5("kafka.messages.produce", "Produce one or more records with all acknowledgements enabled.", {
33555
+ type: "object",
33556
+ properties: {
33557
+ topic: topicNameSchema(),
33558
+ encoding: { type: "string", enum: ["utf8", "base64"], default: "utf8" },
33559
+ messages: {
33560
+ type: "array",
33561
+ minItems: 1,
33562
+ maxItems: MAX_MESSAGES,
33563
+ items: {
33564
+ type: "object",
33565
+ properties: {
33566
+ value: { type: "string" },
33567
+ key: { type: "string" },
33568
+ partition: { type: "integer", minimum: 0 },
33569
+ headers: { type: "object", additionalProperties: { type: "string" }, maxProperties: MAX_HEADERS }
33570
+ },
33571
+ required: ["value"],
33572
+ additionalProperties: false
33573
+ }
33574
+ }
33575
+ },
33576
+ required: ["topic", "messages"],
33577
+ additionalProperties: false
33578
+ }),
33579
+ mutationCapability5("kafka.messages.consume", "Join a consumer group and return a bounded batch without automatically committing offsets.", {
33580
+ type: "object",
33581
+ properties: {
33582
+ topic: topicNameSchema(),
33583
+ groupId: { type: "string", minLength: 1, maxLength: 255 },
33584
+ encoding: { type: "string", enum: ["utf8", "base64"], default: "utf8" },
33585
+ fromBeginning: { type: "boolean", default: false },
33586
+ limit: { type: "integer", minimum: 1, maximum: MAX_MESSAGES, default: 25 },
33587
+ timeoutMs: { type: "integer", minimum: 250, maximum: MAX_POLL_MS, default: 5e3 }
33588
+ },
33589
+ required: ["topic", "groupId"],
33590
+ additionalProperties: false
33591
+ }),
33592
+ mutationCapability5("kafka.offsets.commit", "Commit explicit next offsets for a consumer group.", {
33593
+ type: "object",
33594
+ properties: {
33595
+ groupId: { type: "string", minLength: 1, maxLength: 255 },
33596
+ offsets: {
33597
+ type: "array",
33598
+ minItems: 1,
33599
+ maxItems: MAX_MESSAGES,
33600
+ items: {
33601
+ type: "object",
33602
+ properties: {
33603
+ topic: topicNameSchema(),
33604
+ partition: { type: "integer", minimum: 0 },
33605
+ offset: { type: "string", pattern: "^[0-9]+$" },
33606
+ metadata: { type: "string", maxLength: 4096 }
33607
+ },
33608
+ required: ["topic", "partition", "offset"],
33609
+ additionalProperties: false
33610
+ }
33611
+ }
33612
+ },
33613
+ required: ["groupId", "offsets"],
33614
+ additionalProperties: false
33615
+ }),
33616
+ mutationCapability5("kafka.topics.create", "Create one Kafka topic with explicit partition and replication counts.", {
33617
+ type: "object",
33618
+ properties: {
33619
+ topic: topicNameSchema(),
33620
+ partitions: { type: "integer", minimum: 1, maximum: 100, default: 1 },
33621
+ replicationFactor: { type: "integer", minimum: 1, maximum: 10, default: 1 }
33622
+ },
33623
+ required: ["topic"],
33624
+ additionalProperties: false
33625
+ }),
33626
+ mutationCapability5("kafka.topics.delete", "Delete one or more Kafka topics.", {
33627
+ type: "object",
33628
+ properties: { topics: topicArraySchema() },
33629
+ required: ["topics"],
33630
+ additionalProperties: false
33631
+ })
33632
+ ]
33633
+ },
33634
+ async executeRead({ source, capabilityName, args }) {
33635
+ const client = createClient(readCredentials(source).config);
33636
+ return withAdmin(client.admin(), async (admin) => {
33637
+ let data4;
33638
+ if (capabilityName === "kafka.topics.list") {
33639
+ data4 = { topics: await admin.listTopics() };
33640
+ } else if (capabilityName === "kafka.topics.describe") {
33641
+ data4 = await admin.fetchTopicMetadata({ topics: readTopics(args.topics) });
33642
+ } else if (capabilityName === "kafka.offsets.list") {
33643
+ const topic = readTopic(args.topic);
33644
+ data4 = { topic, partitions: await admin.fetchTopicOffsets(topic) };
33645
+ } else if (capabilityName === "kafka.groups.list") {
33646
+ data4 = await admin.listGroups();
33647
+ } else if (capabilityName === "kafka.group.offsets") {
33648
+ const groupId5 = readGroupId2(args.groupId);
33649
+ const topics = args.topics === void 0 ? void 0 : readTopics(args.topics);
33650
+ data4 = { groupId: groupId5, offsets: await admin.fetchOffsets({ groupId: groupId5, topics, resolveOffsets: true }) };
33651
+ } else {
33652
+ throw new Error(`Unknown Kafka read capability: ${capabilityName}`);
33653
+ }
33654
+ return { data: jsonSafe(data4), fetchedAt: Date.now() };
33655
+ });
33656
+ },
33657
+ async executeMutation({ source, capabilityName, args }) {
33658
+ const client = createClient(readCredentials(source).config);
33659
+ let data4;
33660
+ if (capabilityName === "kafka.messages.produce") {
33661
+ data4 = await produce(client.producer({ idempotent: true, maxInFlightRequests: 1 }), args);
33662
+ } else if (capabilityName === "kafka.messages.consume") {
33663
+ data4 = await consume(client.consumer({
33664
+ groupId: readGroupId2(args.groupId),
33665
+ allowAutoTopicCreation: false,
33666
+ maxBytes: MAX_BATCH_BYTES2,
33667
+ maxBytesPerPartition: MAX_BATCH_BYTES2
33668
+ }), args);
33669
+ } else if (capabilityName === "kafka.offsets.commit") {
33670
+ data4 = await commitOffsets(client.consumer({
33671
+ groupId: readGroupId2(args.groupId),
33672
+ allowAutoTopicCreation: false
33673
+ }), args);
33674
+ } else if (capabilityName === "kafka.topics.create") {
33675
+ data4 = await withAdmin(client.admin(), async (admin) => ({
33676
+ created: await admin.createTopics({
33677
+ waitForLeaders: true,
33678
+ topics: [{
33679
+ topic: readTopic(args.topic),
33680
+ numPartitions: readBoundedInteger(args.partitions, 1, 1, 100, "partitions"),
33681
+ replicationFactor: readBoundedInteger(args.replicationFactor, 1, 1, 10, "replicationFactor")
33682
+ }]
33683
+ })
33684
+ }));
33685
+ } else if (capabilityName === "kafka.topics.delete") {
33686
+ const topics = readTopics(args.topics);
33687
+ data4 = await withAdmin(client.admin(), async (admin) => {
33688
+ await admin.deleteTopics({ topics });
33689
+ return { topics };
33690
+ });
33691
+ } else {
33692
+ throw new Error(`Unknown Kafka mutation capability: ${capabilityName}`);
33693
+ }
33694
+ return {
33695
+ status: "committed",
33696
+ data: jsonSafe(data4),
33697
+ committedAt: Date.now(),
33698
+ idempotentReplay: false
33699
+ };
33700
+ },
33701
+ async test(source) {
33702
+ try {
33703
+ const admin = createClient(readCredentials(source).config).admin();
33704
+ await withAdmin(admin, (connected) => connected.listTopics());
33705
+ return { ok: true };
33706
+ } catch (error) {
33707
+ return { ok: false, reason: error instanceof Error ? error.message : "Kafka connection test failed" };
33708
+ }
33709
+ }
33710
+ };
33711
+ }
33712
+ var kafkaConnector = createKafkaConnector();
33713
+ var publicKafkaSocketFactory = ({ host, port, ssl, onConnect }) => {
33714
+ validateBrokerHost(host);
33715
+ if (isIP2(host) !== 0 && !isPublicNetworkAddress(host)) {
33716
+ throw new Error("Kafka broker is not a public network target");
33717
+ }
33718
+ const socket = tls.connect({
33719
+ ...ssl,
33720
+ host,
33721
+ port,
33722
+ servername: isIP2(host) === 0 ? host : void 0,
33723
+ lookup: publicDnsLookup
33724
+ }, onConnect);
33725
+ socket.setKeepAlive(true, 6e4);
33726
+ return socket;
33727
+ };
33728
+ async function withAdmin(admin, run) {
33729
+ try {
33730
+ await admin.connect();
33731
+ return await run(admin);
33732
+ } finally {
33733
+ await admin.disconnect().catch(() => void 0);
33734
+ }
33735
+ }
33736
+ async function produce(producer, args) {
33737
+ const topic = readTopic(args.topic);
33738
+ const encoding = readEncoding(args.encoding);
33739
+ const messages = readMessageInputs(args.messages).map((message) => ({
33740
+ value: decodeMessageValue(message.value, encoding, "value"),
33741
+ key: message.key === void 0 ? void 0 : decodeMessageValue(message.key, encoding, "key"),
33742
+ partition: message.partition === void 0 ? void 0 : readBoundedInteger(message.partition, 0, 0, Number.MAX_SAFE_INTEGER, "partition"),
33743
+ headers: readHeaders(message.headers)
33744
+ }));
33745
+ const batchBytes = messages.reduce((total, message) => total + message.value.byteLength + (message.key?.byteLength ?? 0) + headerBytes(message.headers), 0);
33746
+ if (batchBytes > MAX_BATCH_BYTES2) throw new Error(`messages exceed the ${MAX_BATCH_BYTES2}-byte batch limit`);
33747
+ try {
33748
+ await producer.connect();
33749
+ return { topic, records: await producer.send({ topic, messages, acks: -1, timeout: 3e4 }) };
33750
+ } finally {
33751
+ await producer.disconnect().catch(() => void 0);
33752
+ }
33753
+ }
33754
+ async function consume(consumer, args) {
33755
+ const topic = readTopic(args.topic);
33756
+ const encoding = readEncoding(args.encoding);
33757
+ const limit = readBoundedInteger(args.limit, 25, 1, MAX_MESSAGES, "limit");
33758
+ const timeoutMs = readBoundedInteger(args.timeoutMs, 5e3, 250, MAX_POLL_MS, "timeoutMs");
33759
+ const messages = [];
33760
+ let batchBytes = 0;
33761
+ let resolveDone = () => void 0;
33762
+ let rejectDone = () => void 0;
33763
+ const done = new Promise((resolve, reject) => {
33764
+ resolveDone = resolve;
33765
+ rejectDone = reject;
33766
+ });
33767
+ let timer;
33768
+ try {
33769
+ await consumer.connect();
33770
+ await consumer.subscribe({ topics: [topic], fromBeginning: readBoolean(args.fromBeginning, false, "fromBeginning") });
33771
+ const run = consumer.run({
33772
+ autoCommit: false,
33773
+ partitionsConsumedConcurrently: 1,
33774
+ eachMessage: async ({ partition, message }) => {
33775
+ if (messages.length >= limit) return;
33776
+ const messageBytes = (message.key?.byteLength ?? 0) + (message.value?.byteLength ?? 0) + headerBytes(message.headers);
33777
+ if (messageBytes > MAX_MESSAGE_BYTES2) throw new Error(`Kafka message exceeds the ${MAX_MESSAGE_BYTES2}-byte limit`);
33778
+ batchBytes += messageBytes;
33779
+ if (batchBytes > MAX_BATCH_BYTES2) throw new Error(`Kafka batch exceeds the ${MAX_BATCH_BYTES2}-byte limit`);
33780
+ messages.push({
33781
+ topic,
33782
+ partition,
33783
+ offset: message.offset,
33784
+ timestamp: message.timestamp,
33785
+ key: encodeMessageValue(message.key, encoding),
33786
+ value: encodeMessageValue(message.value, encoding),
33787
+ headers: Object.fromEntries(Object.entries(message.headers ?? {}).map(([key, value]) => [key, encodeHeader(value, encoding)]))
33788
+ });
33789
+ if (messages.length >= limit) resolveDone();
33790
+ }
33791
+ });
33792
+ run.catch(rejectDone);
33793
+ timer = setTimeout(resolveDone, timeoutMs);
33794
+ await done;
33795
+ await consumer.stop();
33796
+ await run;
33797
+ return { topic, messages, messageCount: messages.length, offsetsCommitted: false };
33798
+ } finally {
33799
+ if (timer) clearTimeout(timer);
33800
+ await consumer.disconnect().catch(() => void 0);
33801
+ }
33802
+ }
33803
+ async function commitOffsets(consumer, args) {
33804
+ if (!Array.isArray(args.offsets) || args.offsets.length === 0 || args.offsets.length > MAX_MESSAGES) {
33805
+ throw new Error(`offsets must contain 1 through ${MAX_MESSAGES} entries`);
33806
+ }
33807
+ const offsets = args.offsets.map((entry, index) => {
33808
+ if (!isPlainRecord(entry)) throw new Error(`offsets[${index}] must be an object`);
33809
+ const offset = readOptionalString(entry.offset, `offsets[${index}].offset`);
33810
+ if (!offset || !/^[0-9]+$/.test(offset) || BigInt(offset) > 9223372036854775807n) {
33811
+ throw new Error(`offsets[${index}].offset must be a signed 64-bit non-negative integer string`);
33812
+ }
33813
+ return {
33814
+ topic: readTopic(entry.topic),
33815
+ partition: readBoundedInteger(entry.partition, 0, 0, Number.MAX_SAFE_INTEGER, `offsets[${index}].partition`),
33816
+ offset,
33817
+ metadata: readOptionalString(entry.metadata, `offsets[${index}].metadata`)
33818
+ };
33819
+ });
33820
+ try {
33821
+ await consumer.connect();
33822
+ await consumer.commitOffsets(offsets);
33823
+ return { groupId: readGroupId2(args.groupId), offsets };
33824
+ } finally {
33825
+ await consumer.disconnect().catch(() => void 0);
33826
+ }
33827
+ }
33828
+ function readCredentials(source) {
33829
+ let raw;
33830
+ if (source.credentials.kind === "custom") raw = source.credentials.values;
33831
+ else if (source.credentials.kind === "api-key") {
33832
+ try {
33833
+ raw = JSON.parse(source.credentials.apiKey);
33834
+ } catch {
33835
+ throw new Error("Kafka credential must be valid JSON");
33836
+ }
33837
+ } else {
33838
+ throw new Error("Kafka requires a structured credential bundle");
33839
+ }
33840
+ if (!isPlainRecord(raw)) throw new Error("Kafka credential must be a JSON object");
33841
+ const brokers = readBrokers(raw.brokers);
33842
+ const clientId = readOptionalString(raw.clientId, "clientId") ?? "tangle-integration-hub";
33843
+ if (!/^[A-Za-z0-9._-]{1,128}$/.test(clientId)) throw new Error("clientId contains unsupported characters");
33844
+ const sasl = readSasl(raw);
33845
+ const ca = readPemList(raw.tlsCa, "tlsCa");
33846
+ const cert = readOptionalString(raw.tlsCert, "tlsCert");
33847
+ const key = readOptionalString(raw.tlsKey, "tlsKey");
33848
+ if (Boolean(cert) !== Boolean(key)) throw new Error("tlsCert and tlsKey must be supplied together");
33849
+ return {
33850
+ config: {
33851
+ brokers,
33852
+ clientId,
33853
+ ssl: {
33854
+ minVersion: "TLSv1.2",
33855
+ rejectUnauthorized: true,
33856
+ ca,
33857
+ cert,
33858
+ key,
33859
+ passphrase: readOptionalString(raw.tlsPassphrase, "tlsPassphrase")
33860
+ },
33861
+ sasl,
33862
+ socketFactory: publicKafkaSocketFactory,
33863
+ connectionTimeout: 1e4,
33864
+ authenticationTimeout: 1e4,
33865
+ requestTimeout: 3e4,
33866
+ enforceRequestTimeout: true,
33867
+ retry: { retries: 3, maxRetryTime: 3e4 },
33868
+ logLevel: logLevel.NOTHING
33869
+ }
33870
+ };
33871
+ }
33872
+ function readBrokers(value) {
33873
+ if (!Array.isArray(value) || value.length === 0 || value.length > 20) {
33874
+ throw new Error("brokers must contain 1 through 20 host:port entries");
33875
+ }
33876
+ return value.map((entry, index) => {
33877
+ if (typeof entry !== "string" || entry.length === 0) throw new Error(`brokers[${index}] must be a host:port string`);
33878
+ let parsed;
33879
+ try {
33880
+ parsed = new URL(`tcp://${entry}`);
33881
+ } catch {
33882
+ throw new Error(`brokers[${index}] is not a valid host:port`);
33883
+ }
33884
+ const host = parsed.hostname.replace(/^\[|\]$/g, "");
33885
+ if (!parsed.port || parsed.username || parsed.password || !["", "/"].includes(parsed.pathname)) {
33886
+ throw new Error(`brokers[${index}] must contain only host and port`);
33887
+ }
33888
+ validateBrokerHost(host);
33889
+ if (isIP2(host) !== 0 && !isPublicNetworkAddress(host)) throw new Error(`brokers[${index}] is not a public network target`);
33890
+ const port = Number(parsed.port);
33891
+ if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`brokers[${index}] port is invalid`);
33892
+ return entry;
33893
+ });
33894
+ }
33895
+ function validateBrokerHost(host) {
33896
+ const normalized = host.toLowerCase();
33897
+ if (host.length === 0 || host.length > 253 || normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@]/.test(host)) {
33898
+ throw new Error("Kafka broker must be a public hostname or IP address");
33899
+ }
33900
+ }
33901
+ function readSasl(raw) {
33902
+ if (raw.saslMechanism === void 0 || raw.saslMechanism === null) return void 0;
33903
+ if (!["plain", "scram-sha-256", "scram-sha-512"].includes(String(raw.saslMechanism))) {
33904
+ throw new Error("saslMechanism must be plain, scram-sha-256, or scram-sha-512");
33905
+ }
33906
+ const username = readOptionalString(raw.saslUsername, "saslUsername");
33907
+ const password = readOptionalString(raw.saslPassword, "saslPassword");
33908
+ if (!username || !password) throw new Error("SASL requires saslUsername and saslPassword");
33909
+ return { mechanism: raw.saslMechanism, username, password };
33910
+ }
33911
+ function readPemList(value, label) {
33912
+ if (value === void 0 || value === null) return void 0;
33913
+ const values = Array.isArray(value) ? value : [value];
33914
+ if (values.length === 0 || values.some((entry) => typeof entry !== "string" || entry.length === 0)) {
33915
+ throw new Error(`${label} must be a PEM string or non-empty array of PEM strings`);
33916
+ }
33917
+ return values;
33918
+ }
33919
+ function readTopics(value) {
33920
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_TOPICS) {
33921
+ throw new Error(`topics must contain 1 through ${MAX_TOPICS} names`);
33922
+ }
33923
+ const topics = value.map(readTopic);
33924
+ if (new Set(topics).size !== topics.length) throw new Error("topics must not contain duplicates");
33925
+ return topics;
33926
+ }
33927
+ function readTopic(value) {
33928
+ if (typeof value !== "string" || !/^[A-Za-z0-9._-]{1,249}$/.test(value) || value === "." || value === "..") {
33929
+ throw new Error("topic must be 1 through 249 Kafka-safe characters");
33930
+ }
33931
+ return value;
33932
+ }
33933
+ function readGroupId2(value) {
33934
+ if (typeof value !== "string" || value.length === 0 || value.length > 255 || /[\u0000-\u001f]/.test(value)) {
33935
+ throw new Error("groupId must be 1 through 255 printable characters");
33936
+ }
33937
+ return value;
33938
+ }
33939
+ function readEncoding(value) {
33940
+ if (value === void 0 || value === null || value === "utf8") return "utf8";
33941
+ if (value === "base64") return "base64";
33942
+ throw new Error("encoding must be utf8 or base64");
33943
+ }
33944
+ function readMessageInputs(value) {
33945
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_MESSAGES) {
33946
+ throw new Error(`messages must contain 1 through ${MAX_MESSAGES} entries`);
33947
+ }
33948
+ return value.map((entry, index) => {
33949
+ if (!isPlainRecord(entry)) throw new Error(`messages[${index}] must be an object`);
33950
+ return entry;
33951
+ });
33952
+ }
33953
+ function decodeMessageValue(value, encoding, label) {
33954
+ if (typeof value !== "string") throw new Error(`${label} must be a string`);
33955
+ if (encoding === "base64" && (value.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value))) {
33956
+ throw new Error(`${label} must be canonical base64`);
33957
+ }
33958
+ const decoded = Buffer.from(value, encoding);
33959
+ if (decoded.byteLength > MAX_MESSAGE_BYTES2) throw new Error(`${label} exceeds the ${MAX_MESSAGE_BYTES2}-byte limit`);
33960
+ return decoded;
33961
+ }
33962
+ function encodeMessageValue(value, encoding) {
33963
+ return value ? value.toString(encoding) : null;
33964
+ }
33965
+ function encodeHeader(value, encoding) {
33966
+ if (value === void 0) return void 0;
33967
+ if (Array.isArray(value)) return value.map((entry) => Buffer.isBuffer(entry) ? entry.toString(encoding) : entry);
33968
+ return Buffer.isBuffer(value) ? value.toString(encoding) : value;
33969
+ }
33970
+ function readHeaders(value) {
33971
+ if (value === void 0 || value === null) return void 0;
33972
+ if (!isPlainRecord(value) || Object.keys(value).length > MAX_HEADERS) throw new Error(`headers must have at most ${MAX_HEADERS} string fields`);
33973
+ const headers = Object.fromEntries(Object.entries(value).map(([key, entry]) => {
33974
+ if (typeof entry !== "string") throw new Error(`header ${key} must be a string`);
33975
+ return [key, entry];
33976
+ }));
33977
+ if (headerBytes(headers) > MAX_MESSAGE_BYTES2) throw new Error(`headers exceed the ${MAX_MESSAGE_BYTES2}-byte limit`);
33978
+ return headers;
33979
+ }
33980
+ function headerBytes(headers) {
33981
+ if (!headers) return 0;
33982
+ return Object.entries(headers).reduce((total, [key, value]) => {
33983
+ const values = Array.isArray(value) ? value : [value];
33984
+ return total + Buffer.byteLength(key, "utf8") + values.reduce((sum, entry) => {
33985
+ if (Buffer.isBuffer(entry)) return sum + entry.byteLength;
33986
+ if (typeof entry === "string") return sum + Buffer.byteLength(entry, "utf8");
33987
+ return sum;
33988
+ }, 0);
33989
+ }, 0);
33990
+ }
33991
+ function emptySchema() {
33992
+ return { type: "object", properties: {}, additionalProperties: false };
33993
+ }
33994
+ function topicSchema() {
33995
+ return {
33996
+ type: "object",
33997
+ properties: { topic: topicNameSchema() },
33998
+ required: ["topic"],
33999
+ additionalProperties: false
34000
+ };
34001
+ }
34002
+ function topicArraySchema() {
34003
+ return { type: "array", minItems: 1, maxItems: MAX_TOPICS, items: topicNameSchema() };
34004
+ }
34005
+ function topicNameSchema() {
34006
+ return { type: "string", minLength: 1, maxLength: 249, pattern: "^[A-Za-z0-9._-]+$" };
34007
+ }
34008
+ function readCapability5(name, description, parameters) {
34009
+ return { name, class: "read", description, parameters };
34010
+ }
34011
+ function mutationCapability5(name, description, parameters) {
34012
+ return { name, class: "mutation", description, parameters, cas: "none", externalEffect: true };
34013
+ }
34014
+
33160
34015
  // src/connectors/adapters/lead-connector.ts
33161
34016
  var leadConnectorConnector = declarativeRestConnector({
33162
34017
  kind: "lead-connector",
@@ -43338,7 +44193,7 @@ var pipedriveConnector = declarativeRestConnector({
43338
44193
  // src/connectors/adapters/parquet-files.ts
43339
44194
  import { parquetReadObjects } from "hyparquet";
43340
44195
  import { parquetWriteBuffer } from "hyparquet-writer";
43341
- var MAX_COLUMNS3 = 256;
44196
+ var MAX_COLUMNS4 = 256;
43342
44197
  var parquetFilesConnector = {
43343
44198
  manifest: {
43344
44199
  kind: "parquet-files",
@@ -43356,7 +44211,7 @@ var parquetFilesConnector = {
43356
44211
  type: "object",
43357
44212
  properties: {
43358
44213
  fileBase64: { type: "string", description: "Parquet file content encoded as canonical base64." },
43359
- columns: { type: "array", maxItems: MAX_COLUMNS3, items: { type: "string", minLength: 1 } },
44214
+ columns: { type: "array", maxItems: MAX_COLUMNS4, items: { type: "string", minLength: 1 } },
43360
44215
  rowStart: { type: "integer", minimum: 0, default: 0 },
43361
44216
  rowLimit: { type: "integer", minimum: 1, maximum: MAX_RECORDS, default: MAX_RECORDS }
43362
44217
  },
@@ -43402,8 +44257,8 @@ var parquetFilesConnector = {
43402
44257
  if (capabilityName !== "parquet.create") throw new Error(`Unknown Parquet capability: ${capabilityName}`);
43403
44258
  const records = readRecords(args.records);
43404
44259
  const columnNames = [...new Set(records.flatMap((record) => Object.keys(record)))];
43405
- if (columnNames.length === 0 || columnNames.length > MAX_COLUMNS3) {
43406
- throw new Error(`records must contain 1 through ${MAX_COLUMNS3} columns`);
44260
+ if (columnNames.length === 0 || columnNames.length > MAX_COLUMNS4) {
44261
+ throw new Error(`records must contain 1 through ${MAX_COLUMNS4} columns`);
43407
44262
  }
43408
44263
  if (columnNames.some((name) => name.length === 0)) throw new Error("column names must not be empty");
43409
44264
  const columnData = columnNames.map((name) => {
@@ -43433,8 +44288,8 @@ function asyncBuffer(file) {
43433
44288
  }
43434
44289
  function readColumns2(value) {
43435
44290
  if (value === void 0 || value === null) return void 0;
43436
- if (!Array.isArray(value) || value.length === 0 || value.length > MAX_COLUMNS3) {
43437
- throw new Error(`columns must contain 1 through ${MAX_COLUMNS3} names`);
44291
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_COLUMNS4) {
44292
+ throw new Error(`columns must contain 1 through ${MAX_COLUMNS4} names`);
43438
44293
  }
43439
44294
  const columns = value.map((entry, index) => {
43440
44295
  if (typeof entry !== "string" || entry.length === 0) throw new Error(`columns[${index}] must be a non-empty string`);
@@ -43890,6 +44745,759 @@ var quickbooksConnector = declarativeRestConnector({
43890
44745
  ]
43891
44746
  });
43892
44747
 
44748
+ // src/connectors/adapters/rabbitmq.ts
44749
+ import { createHash as createHash7 } from "crypto";
44750
+ import { isIP as isIP3 } from "net";
44751
+ import {
44752
+ connect as amqpConnect
44753
+ } from "amqplib";
44754
+ var MAX_MESSAGE_BYTES3 = 1024 * 1024;
44755
+ var MAX_NAME_BYTES = 255;
44756
+ var MAX_PEM_BYTES = 64 * 1024;
44757
+ var MAX_CA_CERTIFICATES = 16;
44758
+ var MAX_CA_BYTES = 256 * 1024;
44759
+ function createRabbitMqConnector(options = {}) {
44760
+ const connect = options.connect ?? ((connectOptions, socketOptions) => amqpConnect(connectOptions, socketOptions));
44761
+ const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
44762
+ return {
44763
+ manifest: {
44764
+ kind: "rabbitmq",
44765
+ displayName: "RabbitMQ",
44766
+ description: "Inspect queues and exchanges and publish confirmed JSON messages over verified TLS.",
44767
+ auth: {
44768
+ kind: "api-key",
44769
+ hint: "JSON with a public RabbitMQ host, username, password, virtual host, and optional TLS client credentials."
44770
+ },
44771
+ defaultConsistencyModel: "advisory",
44772
+ category: "webhook",
44773
+ rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
44774
+ capabilities: [
44775
+ readCapability6("rabbitmq.queues.inspect", "Check a queue and return its current message and consumer counts.", {
44776
+ type: "object",
44777
+ properties: { queue: nameSchema("queue") },
44778
+ required: ["queue"],
44779
+ additionalProperties: false
44780
+ }),
44781
+ readCapability6("rabbitmq.exchanges.inspect", "Check whether a named exchange exists and is visible to the connection.", {
44782
+ type: "object",
44783
+ properties: { exchange: nameSchema("exchange") },
44784
+ required: ["exchange"],
44785
+ additionalProperties: false
44786
+ }),
44787
+ mutationCapability6("send.message.to.queue", "Publish a persistent JSON message directly to an existing queue with broker confirmation.", {
44788
+ type: "object",
44789
+ properties: {
44790
+ queue: nameSchema("queue"),
44791
+ data: {},
44792
+ persistent: { type: "boolean", default: true },
44793
+ expirationMs: { type: "integer", minimum: 1, maximum: 6048e5 },
44794
+ priority: { type: "integer", minimum: 0, maximum: 255 }
44795
+ },
44796
+ required: ["queue", "data"],
44797
+ additionalProperties: false
44798
+ }),
44799
+ mutationCapability6("send.message.to.exchange", "Publish a persistent JSON message to an existing exchange with broker confirmation.", {
44800
+ type: "object",
44801
+ properties: {
44802
+ exchange: nameSchema("exchange"),
44803
+ routingKey: { type: "string", maxLength: MAX_NAME_BYTES, default: "" },
44804
+ data: {},
44805
+ persistent: { type: "boolean", default: true },
44806
+ expirationMs: { type: "integer", minimum: 1, maximum: 6048e5 },
44807
+ priority: { type: "integer", minimum: 0, maximum: 255 }
44808
+ },
44809
+ required: ["exchange", "data"],
44810
+ additionalProperties: false
44811
+ })
44812
+ ]
44813
+ },
44814
+ async executeRead({ source, capabilityName, args }) {
44815
+ if (capabilityName === "rabbitmq.queues.inspect") {
44816
+ const queue = readName(args.queue, "queue");
44817
+ const data4 = await withChannel(source, connect, resolveHost, async (channel) => {
44818
+ const details = await channel.checkQueue(queue);
44819
+ return { queue: details.queue, messageCount: details.messageCount, consumerCount: details.consumerCount };
44820
+ });
44821
+ return { data: data4, fetchedAt: Date.now() };
44822
+ }
44823
+ if (capabilityName === "rabbitmq.exchanges.inspect") {
44824
+ const exchange = readName(args.exchange, "exchange");
44825
+ const data4 = await withChannel(source, connect, resolveHost, async (channel) => {
44826
+ await channel.checkExchange(exchange);
44827
+ return { exchange, exists: true };
44828
+ });
44829
+ return { data: data4, fetchedAt: Date.now() };
44830
+ }
44831
+ throw new Error(`Unknown RabbitMQ read capability: ${capabilityName}`);
44832
+ },
44833
+ async executeMutation({ source, capabilityName, args, idempotencyKey }) {
44834
+ let data4;
44835
+ if (capabilityName === "send.message.to.queue") {
44836
+ const queue = readName(args.queue, "queue");
44837
+ const body = readJsonMessage(args.data);
44838
+ const publishOptions = readPublishOptions(args, idempotencyKey);
44839
+ data4 = await withConfirmChannel(source, connect, resolveHost, async (channel) => {
44840
+ await channel.checkQueue(queue);
44841
+ await publishConfirmed(channel, publishOptions, (options2) => channel.sendToQueue(queue, body, options2));
44842
+ return { queue, confirmed: true, messageId: publishOptions.messageId, byteLength: body.byteLength };
44843
+ });
44844
+ } else if (capabilityName === "send.message.to.exchange") {
44845
+ const exchange = readName(args.exchange, "exchange");
44846
+ const routingKey = readRoutingKey(args.routingKey);
44847
+ const body = readJsonMessage(args.data);
44848
+ const publishOptions = readPublishOptions(args, idempotencyKey);
44849
+ data4 = await withConfirmChannel(source, connect, resolveHost, async (channel) => {
44850
+ await channel.checkExchange(exchange);
44851
+ await publishConfirmed(channel, publishOptions, (options2) => channel.publish(exchange, routingKey, body, options2));
44852
+ return { exchange, routingKey, confirmed: true, messageId: publishOptions.messageId, byteLength: body.byteLength };
44853
+ });
44854
+ } else {
44855
+ throw new Error(`Unknown RabbitMQ mutation capability: ${capabilityName}`);
44856
+ }
44857
+ return { status: "committed", data: data4, committedAt: Date.now(), idempotentReplay: false };
44858
+ },
44859
+ async test(source) {
44860
+ try {
44861
+ const credentials = readCredentials2(source);
44862
+ await resolveHost(credentials.host);
44863
+ const model2 = await connect(credentials.connectOptions, credentials.socketOptions);
44864
+ await model2.close();
44865
+ return { ok: true };
44866
+ } catch (error) {
44867
+ return { ok: false, reason: safeErrorMessage(error, credentialSecrets(source)) };
44868
+ }
44869
+ }
44870
+ };
44871
+ }
44872
+ var rabbitMqConnector = createRabbitMqConnector();
44873
+ async function withChannel(source, connect, resolveHost, run) {
44874
+ const credentials = readCredentials2(source);
44875
+ await resolveHost(credentials.host);
44876
+ let model2;
44877
+ let channel;
44878
+ try {
44879
+ model2 = await connect(credentials.connectOptions, credentials.socketOptions);
44880
+ channel = await model2.createChannel();
44881
+ return await run(channel);
44882
+ } catch (error) {
44883
+ throw new Error(safeErrorMessage(error, credentials.secrets));
44884
+ } finally {
44885
+ await channel?.close().catch(() => void 0);
44886
+ await model2?.close().catch(() => void 0);
44887
+ }
44888
+ }
44889
+ async function withConfirmChannel(source, connect, resolveHost, run) {
44890
+ const credentials = readCredentials2(source);
44891
+ await resolveHost(credentials.host);
44892
+ let model2;
44893
+ let channel;
44894
+ try {
44895
+ model2 = await connect(credentials.connectOptions, credentials.socketOptions);
44896
+ channel = await model2.createConfirmChannel();
44897
+ return await run(channel);
44898
+ } catch (error) {
44899
+ throw new Error(safeErrorMessage(error, credentials.secrets));
44900
+ } finally {
44901
+ await channel?.close().catch(() => void 0);
44902
+ await model2?.close().catch(() => void 0);
44903
+ }
44904
+ }
44905
+ async function publishConfirmed(channel, publishOptions, publish) {
44906
+ let returned;
44907
+ const onReturn = (message) => {
44908
+ returned = message;
44909
+ };
44910
+ channel.on("return", onReturn);
44911
+ try {
44912
+ publish(publishOptions);
44913
+ await channel.waitForConfirms();
44914
+ if (returned) throw new Error("RabbitMQ accepted but could not route the message");
44915
+ } finally {
44916
+ channel.removeListener("return", onReturn);
44917
+ }
44918
+ }
44919
+ function readCredentials2(source) {
44920
+ let raw;
44921
+ if (source.credentials.kind === "custom") raw = source.credentials.values;
44922
+ else if (source.credentials.kind === "api-key") {
44923
+ try {
44924
+ raw = JSON.parse(source.credentials.apiKey);
44925
+ } catch {
44926
+ throw new Error("RabbitMQ credential must be valid JSON");
44927
+ }
44928
+ } else {
44929
+ throw new Error("RabbitMQ requires a structured credential bundle");
44930
+ }
44931
+ if (!isPlainRecord(raw)) throw new Error("RabbitMQ credential must be a JSON object");
44932
+ const host = readHost(raw.host);
44933
+ const port = readBoundedInteger(raw.port, 5671, 1, 65535, "port");
44934
+ const username = readRequiredString(raw.username, "username", 1024);
44935
+ const password = readRequiredString(raw.password, "password", 4096);
44936
+ const vhost = readOptionalString(raw.vhost, "vhost") ?? "/";
44937
+ if (Buffer.byteLength(vhost, "utf8") > MAX_NAME_BYTES || /[\u0000-\u001f\u007f]/.test(vhost)) {
44938
+ throw new Error(`vhost must be at most ${MAX_NAME_BYTES} bytes without control characters`);
44939
+ }
44940
+ const ca = readPem(raw.tlsCa, "tlsCa", true);
44941
+ const cert = readPem(raw.tlsCert, "tlsCert", false);
44942
+ const key = readPem(raw.tlsKey, "tlsKey", false);
44943
+ if (Boolean(cert) !== Boolean(key)) throw new Error("tlsCert and tlsKey must be supplied together");
44944
+ const passphrase = readOptionalString(raw.tlsPassphrase, "tlsPassphrase");
44945
+ return {
44946
+ host,
44947
+ connectOptions: {
44948
+ protocol: "amqps",
44949
+ hostname: host,
44950
+ port,
44951
+ username,
44952
+ password,
44953
+ vhost,
44954
+ heartbeat: readBoundedInteger(raw.heartbeatSeconds, 30, 5, 300, "heartbeatSeconds"),
44955
+ frameMax: MAX_MESSAGE_BYTES3 + 4096
44956
+ },
44957
+ socketOptions: {
44958
+ minVersion: "TLSv1.2",
44959
+ rejectUnauthorized: true,
44960
+ servername: isIP3(host) === 0 ? host : void 0,
44961
+ lookup: publicDnsLookup,
44962
+ timeout: readBoundedInteger(raw.connectionTimeoutMs, 1e4, 1e3, 3e4, "connectionTimeoutMs"),
44963
+ noDelay: true,
44964
+ keepAlive: true,
44965
+ keepAliveDelay: 6e4,
44966
+ ca,
44967
+ cert,
44968
+ key,
44969
+ passphrase
44970
+ },
44971
+ secrets: [password, ...cert ? [cert] : [], ...key ? [key] : [], ...passphrase ? [passphrase] : []]
44972
+ };
44973
+ }
44974
+ function readHost(value) {
44975
+ const host = readRequiredString(value, "host", 253);
44976
+ const ipVersion = isIP3(host);
44977
+ if (ipVersion !== 0) {
44978
+ if (!isPublicNetworkAddress(host)) throw new Error("RabbitMQ host is not a public network target");
44979
+ return host;
44980
+ }
44981
+ const normalized = host.toLowerCase();
44982
+ if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@:\[\]]/.test(host)) {
44983
+ throw new Error("RabbitMQ host must be a public hostname or IP address without a scheme or port");
44984
+ }
44985
+ return host;
44986
+ }
44987
+ function readPublishOptions(args, idempotencyKey) {
44988
+ const expirationMs = args.expirationMs === void 0 ? void 0 : readBoundedInteger(args.expirationMs, 0, 1, 6048e5, "expirationMs");
44989
+ const priority = args.priority === void 0 ? void 0 : readBoundedInteger(args.priority, 0, 0, 255, "priority");
44990
+ return {
44991
+ mandatory: true,
44992
+ persistent: readBoolean(args.persistent, true, "persistent"),
44993
+ contentType: "application/json",
44994
+ contentEncoding: "utf-8",
44995
+ messageId: createHash7("sha256").update(idempotencyKey).digest("hex"),
44996
+ timestamp: Math.floor(Date.now() / 1e3),
44997
+ appId: "tangle-integration-hub",
44998
+ expiration: expirationMs === void 0 ? void 0 : String(expirationMs),
44999
+ priority
45000
+ };
45001
+ }
45002
+ function readJsonMessage(value) {
45003
+ if (value === void 0) throw new Error("data is required");
45004
+ let serialized;
45005
+ try {
45006
+ serialized = JSON.stringify(value);
45007
+ } catch {
45008
+ throw new Error("data must be JSON-serializable");
45009
+ }
45010
+ if (serialized === void 0) throw new Error("data must be JSON-serializable");
45011
+ const body = Buffer.from(serialized);
45012
+ if (body.byteLength > MAX_MESSAGE_BYTES3) throw new Error(`data exceeds the ${MAX_MESSAGE_BYTES3}-byte limit`);
45013
+ return body;
45014
+ }
45015
+ function readName(value, label) {
45016
+ const name = readRequiredString(value, label, MAX_NAME_BYTES);
45017
+ if (Buffer.byteLength(name, "utf8") > MAX_NAME_BYTES || /[\u0000-\u001f\u007f]/.test(name)) {
45018
+ throw new Error(`${label} must be at most ${MAX_NAME_BYTES} bytes without control characters`);
45019
+ }
45020
+ return name;
45021
+ }
45022
+ function readRoutingKey(value) {
45023
+ if (value === void 0 || value === null || value === "") return "";
45024
+ return readName(value, "routingKey");
45025
+ }
45026
+ function readRequiredString(value, label, maxLength) {
45027
+ if (typeof value !== "string" || value.length === 0 || value.length > maxLength) {
45028
+ throw new Error(`${label} must be a non-empty string no longer than ${maxLength} characters`);
45029
+ }
45030
+ return value;
45031
+ }
45032
+ function readPem(value, label, allowArray) {
45033
+ if (value === void 0 || value === null) return void 0;
45034
+ const values = Array.isArray(value) && allowArray ? value : [value];
45035
+ if (values.length === 0 || values.length > MAX_CA_CERTIFICATES || values.some((entry) => typeof entry !== "string" || entry.length === 0 || Buffer.byteLength(entry, "utf8") > MAX_PEM_BYTES) || values.reduce((total, entry) => total + (typeof entry === "string" ? Buffer.byteLength(entry, "utf8") : 0), 0) > MAX_CA_BYTES) {
45036
+ throw new Error(
45037
+ allowArray ? `${label} must contain 1-${MAX_CA_CERTIFICATES} PEM strings, each under ${MAX_PEM_BYTES} bytes and ${MAX_CA_BYTES} bytes total` : `${label} must be a PEM string under ${MAX_PEM_BYTES} bytes`
45038
+ );
45039
+ }
45040
+ return allowArray ? values : values[0];
45041
+ }
45042
+ function credentialSecrets(source) {
45043
+ try {
45044
+ return readCredentials2(source).secrets;
45045
+ } catch {
45046
+ return [];
45047
+ }
45048
+ }
45049
+ function safeErrorMessage(error, secrets) {
45050
+ let message = error instanceof Error ? error.message : String(error);
45051
+ for (const secret of secrets) {
45052
+ if (secret.length >= 4) message = message.replaceAll(secret, "[REDACTED]");
45053
+ }
45054
+ return message;
45055
+ }
45056
+ function readCapability6(name, description, parameters) {
45057
+ return { name, class: "read", description, parameters };
45058
+ }
45059
+ function mutationCapability6(name, description, parameters) {
45060
+ return { name, class: "mutation", description, parameters, cas: "none", externalEffect: true };
45061
+ }
45062
+ function nameSchema(label) {
45063
+ return { type: "string", minLength: 1, maxLength: MAX_NAME_BYTES, description: `Existing RabbitMQ ${label} name.` };
45064
+ }
45065
+
45066
+ // src/connectors/adapters/redis.ts
45067
+ import { isIP as isIP4 } from "net";
45068
+ import {
45069
+ createClient as createRedisClient
45070
+ } from "redis";
45071
+ var MAX_KEY_BYTES = 1024;
45072
+ var MAX_PATTERN_BYTES = 4096;
45073
+ var MAX_VALUE_BYTES = 1024 * 1024;
45074
+ var MAX_SCAN_KEYS = 1e3;
45075
+ var MAX_SCAN_BYTES = 1024 * 1024;
45076
+ var MAX_TLS_CA_BYTES = 256 * 1024;
45077
+ var MAX_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
45078
+ var SCAN_SCRIPT = `
45079
+ local page
45080
+ if ARGV[4] == '' then
45081
+ page = redis.call('SCAN', ARGV[1], 'MATCH', ARGV[2], 'COUNT', ARGV[3])
45082
+ else
45083
+ page = redis.call('SCAN', ARGV[1], 'MATCH', ARGV[2], 'COUNT', ARGV[3], 'TYPE', ARGV[4])
45084
+ end
45085
+ local keys = page[2]
45086
+ if #keys > tonumber(ARGV[5]) then return redis.error_reply('Redis SCAN page exceeds the configured key limit') end
45087
+ local bytes = 0
45088
+ for _, key in ipairs(keys) do
45089
+ if string.len(key) > tonumber(ARGV[7]) then return redis.error_reply('Redis SCAN key exceeds the configured key limit') end
45090
+ bytes = bytes + string.len(key)
45091
+ if bytes > tonumber(ARGV[6]) then return redis.error_reply('Redis SCAN page exceeds the configured byte limit') end
45092
+ end
45093
+ return page
45094
+ `;
45095
+ var INSPECT_SCRIPT = `
45096
+ local kind = redis.call('TYPE', KEYS[1])
45097
+ if type(kind) == 'table' then kind = kind['ok'] end
45098
+ return { kind, redis.call('PTTL', KEYS[1]) }
45099
+ `;
45100
+ var READ_STRING_SCRIPT = `
45101
+ local kind = redis.call('TYPE', KEYS[1])
45102
+ if type(kind) == 'table' then kind = kind['ok'] end
45103
+ local ttl = redis.call('PTTL', KEYS[1])
45104
+ if kind == 'none' then return { kind, ttl, false, 0 } end
45105
+ if kind ~= 'string' then return redis.error_reply('WRONGTYPE key does not contain a string') end
45106
+ local length = redis.call('STRLEN', KEYS[1])
45107
+ if length > tonumber(ARGV[1]) then return redis.error_reply('Redis value exceeds the configured read limit') end
45108
+ return { kind, ttl, redis.call('GET', KEYS[1]), length }
45109
+ `;
45110
+ var COMPARE_SET_SCRIPT = `
45111
+ if redis.call('GET', KEYS[1]) ~= ARGV[1] then return 0 end
45112
+ if ARGV[3] ~= '' then
45113
+ redis.call('SET', KEYS[1], ARGV[2], 'PX', ARGV[3])
45114
+ elseif ARGV[4] == '1' then
45115
+ redis.call('SET', KEYS[1], ARGV[2], 'KEEPTTL')
45116
+ else
45117
+ redis.call('SET', KEYS[1], ARGV[2])
45118
+ end
45119
+ return 1
45120
+ `;
45121
+ var COMPARE_DELETE_SCRIPT = `
45122
+ if redis.call('GET', KEYS[1]) ~= ARGV[1] then return 0 end
45123
+ return redis.call('DEL', KEYS[1])
45124
+ `;
45125
+ function createRedisConnector(options = {}) {
45126
+ const createClient = options.createClient ?? defaultCreateClient;
45127
+ const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
45128
+ return {
45129
+ manifest: {
45130
+ kind: "redis",
45131
+ displayName: "Redis",
45132
+ description: "Inspect and scan Redis keys and perform bounded, approved string compare-and-swap mutations over verified TLS.",
45133
+ auth: {
45134
+ kind: "api-key",
45135
+ hint: "JSON with a public Redis host, password, optional ACL username/database, and optional TLS CA. Verified TLS is mandatory."
45136
+ },
45137
+ defaultConsistencyModel: "authoritative",
45138
+ category: "database",
45139
+ rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
45140
+ capabilities: [
45141
+ readCapability7("redis.keys.scan", "Read one bounded SCAN page without blocking the server with KEYS.", {
45142
+ type: "object",
45143
+ properties: {
45144
+ cursor: { type: "string", pattern: "^[0-9]+$", default: "0" },
45145
+ match: { type: "string", maxLength: MAX_PATTERN_BYTES, default: "*" },
45146
+ count: { type: "integer", minimum: 1, maximum: MAX_SCAN_KEYS, default: 100 },
45147
+ type: { type: "string", enum: ["string", "list", "set", "zset", "hash", "stream"] }
45148
+ },
45149
+ additionalProperties: false
45150
+ }),
45151
+ readCapability7("redis.key.inspect", "Read a key type and remaining expiry atomically.", keySchema()),
45152
+ readCapability7("redis.string.get", "Read one bounded string value, type, and remaining expiry atomically.", keySchema()),
45153
+ mutationCapability7("redis.string.set", "Set a bounded string only when the key is absent, present, or matches an expected value.", {
45154
+ type: "object",
45155
+ properties: {
45156
+ key: keyParameter(),
45157
+ value: { type: "string", maxLength: MAX_VALUE_BYTES },
45158
+ condition: { type: "string", enum: ["absent", "present", "matches"] },
45159
+ expectedValue: { type: "string", maxLength: MAX_VALUE_BYTES },
45160
+ ttlMs: { type: "integer", minimum: 1, maximum: MAX_TTL_MS },
45161
+ keepTtl: { type: "boolean", default: false }
45162
+ },
45163
+ required: ["key", "value", "condition"],
45164
+ additionalProperties: false
45165
+ }),
45166
+ mutationCapability7("redis.string.delete", "Delete a string key only when its current value exactly matches the caller expectation.", {
45167
+ type: "object",
45168
+ properties: {
45169
+ key: keyParameter(),
45170
+ expectedValue: { type: "string", maxLength: MAX_VALUE_BYTES }
45171
+ },
45172
+ required: ["key", "expectedValue"],
45173
+ additionalProperties: false
45174
+ })
45175
+ ]
45176
+ },
45177
+ async test(source) {
45178
+ try {
45179
+ await withClient(source, createClient, resolveHost, async (client) => {
45180
+ const response = await client.ping();
45181
+ if (response !== "PONG") throw new Error("Redis PING returned an unexpected response");
45182
+ });
45183
+ return { ok: true };
45184
+ } catch (error) {
45185
+ return { ok: false, reason: safeErrorMessage2(error, credentialSecrets2(source)) };
45186
+ }
45187
+ },
45188
+ async executeRead({ source, capabilityName, args }) {
45189
+ if (capabilityName === "redis.keys.scan") {
45190
+ const cursor = readCursor(args.cursor);
45191
+ const match = readPattern(args.match);
45192
+ const count = readBoundedInteger(args.count, 100, 1, MAX_SCAN_KEYS, "count");
45193
+ const type = readScanType(args.type);
45194
+ const data4 = await withClient(source, createClient, resolveHost, async (client) => {
45195
+ const reply = readArrayReply(await client.eval(SCAN_SCRIPT, {
45196
+ keys: [],
45197
+ arguments: [
45198
+ cursor,
45199
+ match,
45200
+ String(count),
45201
+ type ?? "",
45202
+ String(MAX_SCAN_KEYS),
45203
+ String(MAX_SCAN_BYTES),
45204
+ String(MAX_KEY_BYTES)
45205
+ ]
45206
+ }), "scan");
45207
+ const keys = readStringArray(reply[1], "scan keys");
45208
+ assertScanPage(keys);
45209
+ return { cursor: readCursor(readReplyString(reply[0], "cursor")), keys };
45210
+ });
45211
+ return { data: data4, fetchedAt: Date.now() };
45212
+ }
45213
+ if (capabilityName === "redis.key.inspect") {
45214
+ const key = readKey(args.key);
45215
+ const data4 = await withClient(source, createClient, resolveHost, async (client) => {
45216
+ const reply = readArrayReply(await client.eval(INSPECT_SCRIPT, { keys: [key], arguments: [] }), "inspect");
45217
+ const type = readReplyString(reply[0], "type");
45218
+ return { key, exists: type !== "none", type, ttlMs: normalizeTtl(reply[1]) };
45219
+ });
45220
+ return { data: data4, fetchedAt: Date.now() };
45221
+ }
45222
+ if (capabilityName === "redis.string.get") {
45223
+ const key = readKey(args.key);
45224
+ const data4 = await withClient(source, createClient, resolveHost, async (client) => {
45225
+ const reply = readArrayReply(await client.eval(READ_STRING_SCRIPT, {
45226
+ keys: [key],
45227
+ arguments: [String(MAX_VALUE_BYTES)]
45228
+ }), "string read");
45229
+ const type = readReplyString(reply[0], "type");
45230
+ const exists = type !== "none";
45231
+ const value = exists ? readReplyString(reply[2], "value") : null;
45232
+ if (value !== null && Buffer.byteLength(value, "utf8") > MAX_VALUE_BYTES) {
45233
+ throw new Error(`Redis value exceeds the ${MAX_VALUE_BYTES}-byte limit`);
45234
+ }
45235
+ return {
45236
+ key,
45237
+ exists,
45238
+ type,
45239
+ ttlMs: normalizeTtl(reply[1]),
45240
+ value,
45241
+ byteLength: exists ? readReplyInteger(reply[3], "byteLength") : 0
45242
+ };
45243
+ });
45244
+ return { data: data4, fetchedAt: Date.now() };
45245
+ }
45246
+ throw new Error(`Unknown Redis read capability: ${capabilityName}`);
45247
+ },
45248
+ async executeMutation({ source, capabilityName, args }) {
45249
+ if (capabilityName === "redis.string.set") {
45250
+ const input = readSetInput(args);
45251
+ return withClient(source, createClient, resolveHost, async (client) => {
45252
+ let committed;
45253
+ if (input.condition === "matches") {
45254
+ committed = readReplyInteger(await client.eval(COMPARE_SET_SCRIPT, {
45255
+ keys: [input.key],
45256
+ arguments: [input.expectedValue, input.value, input.ttlMs === void 0 ? "" : String(input.ttlMs), input.keepTtl ? "1" : "0"]
45257
+ }), "compare-and-set result") === 1;
45258
+ } else {
45259
+ const response = await client.set(input.key, input.value, {
45260
+ condition: input.condition === "absent" ? "NX" : "XX",
45261
+ expiration: input.ttlMs === void 0 ? input.keepTtl ? "KEEPTTL" : void 0 : { type: "PX", value: input.ttlMs }
45262
+ });
45263
+ committed = response === "OK";
45264
+ }
45265
+ if (!committed) return conflict(`Redis key ${input.key} did not satisfy condition ${input.condition}`);
45266
+ return committedResult({ key: input.key, condition: input.condition, byteLength: Buffer.byteLength(input.value, "utf8") });
45267
+ });
45268
+ }
45269
+ if (capabilityName === "redis.string.delete") {
45270
+ const key = readKey(args.key);
45271
+ const expectedValue = readValue(args.expectedValue, "expectedValue");
45272
+ return withClient(source, createClient, resolveHost, async (client) => {
45273
+ const deleted = readReplyInteger(await client.eval(COMPARE_DELETE_SCRIPT, {
45274
+ keys: [key],
45275
+ arguments: [expectedValue]
45276
+ }), "compare-and-delete result");
45277
+ if (deleted !== 1) return conflict(`Redis key ${key} did not match the expected value`);
45278
+ return committedResult({ key, deleted: true });
45279
+ });
45280
+ }
45281
+ throw new Error(`Unknown Redis mutation capability: ${capabilityName}`);
45282
+ }
45283
+ };
45284
+ }
45285
+ var redisConnector = createRedisConnector();
45286
+ function defaultCreateClient(options) {
45287
+ return createRedisClient(options);
45288
+ }
45289
+ async function withClient(source, createClient, resolveHost, run) {
45290
+ const credentials = readCredentials3(source);
45291
+ const addresses = await resolveHost(credentials.host);
45292
+ if (addresses.length === 0) throw new Error("Redis host did not resolve");
45293
+ let client;
45294
+ try {
45295
+ client = createClient({
45296
+ username: credentials.username,
45297
+ password: credentials.password,
45298
+ database: credentials.database,
45299
+ name: "tangle-integration-hub",
45300
+ disableOfflineQueue: true,
45301
+ commandsQueueMaxLength: 10,
45302
+ maintNotifications: "disabled",
45303
+ commandOptions: { abortSignal: AbortSignal.timeout(credentials.commandTimeoutMs) },
45304
+ socket: {
45305
+ host: addresses[0],
45306
+ port: credentials.port,
45307
+ tls: true,
45308
+ servername: isIP4(credentials.host) === 0 ? credentials.host : void 0,
45309
+ minVersion: "TLSv1.2",
45310
+ rejectUnauthorized: true,
45311
+ ca: credentials.tlsCa,
45312
+ connectTimeout: credentials.connectTimeoutMs,
45313
+ socketTimeout: credentials.commandTimeoutMs,
45314
+ reconnectStrategy: false
45315
+ }
45316
+ });
45317
+ client.on("error", () => void 0);
45318
+ await client.connect();
45319
+ return await run(client);
45320
+ } catch (error) {
45321
+ throw new Error(safeErrorMessage2(error, credentials.secrets));
45322
+ } finally {
45323
+ if (client?.isOpen) await client.close().catch(() => client?.destroy());
45324
+ else client?.destroy();
45325
+ }
45326
+ }
45327
+ function readCredentials3(source) {
45328
+ let raw;
45329
+ if (source.credentials.kind === "custom") raw = source.credentials.values;
45330
+ else if (source.credentials.kind === "api-key") {
45331
+ try {
45332
+ raw = JSON.parse(source.credentials.apiKey);
45333
+ } catch {
45334
+ throw new Error("Redis credential must be valid JSON");
45335
+ }
45336
+ } else {
45337
+ throw new Error("Redis requires a structured credential bundle");
45338
+ }
45339
+ if (!isPlainRecord(raw)) throw new Error("Redis credential must be a JSON object");
45340
+ const host = readHost2(raw.host);
45341
+ const username = readOptionalString(raw.username, "username");
45342
+ if (username && (username.length > 128 || /[\u0000-\u001f\u007f]/.test(username))) {
45343
+ throw new Error("Redis username must be at most 128 characters without control characters");
45344
+ }
45345
+ const password = readRequiredString2(raw.password, "password", 4096);
45346
+ const tlsCa = readOptionalString(raw.tlsCa, "tlsCa");
45347
+ if (tlsCa && Buffer.byteLength(tlsCa, "utf8") > MAX_TLS_CA_BYTES) {
45348
+ throw new Error(`Redis tlsCa exceeds the ${MAX_TLS_CA_BYTES}-byte limit`);
45349
+ }
45350
+ return {
45351
+ host,
45352
+ port: readBoundedInteger(raw.port, 6380, 1, 65535, "port"),
45353
+ username,
45354
+ password,
45355
+ database: readBoundedInteger(raw.database, 0, 0, 65535, "database"),
45356
+ tlsCa,
45357
+ connectTimeoutMs: readBoundedInteger(raw.connectTimeoutMs, 1e4, 1e3, 3e4, "connectTimeoutMs"),
45358
+ commandTimeoutMs: readBoundedInteger(raw.commandTimeoutMs, 1e4, 1e3, 3e4, "commandTimeoutMs"),
45359
+ secrets: [password]
45360
+ };
45361
+ }
45362
+ function readHost2(value) {
45363
+ const host = readRequiredString2(value, "host", 253);
45364
+ const ipVersion = isIP4(host);
45365
+ if (ipVersion !== 0) {
45366
+ if (!isPublicNetworkAddress(host)) throw new Error("Redis host is not a public network target");
45367
+ return host;
45368
+ }
45369
+ const normalized = host.toLowerCase();
45370
+ if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@:\[\]]/.test(host)) {
45371
+ throw new Error("Redis host must be a public hostname or IP address without a scheme or port");
45372
+ }
45373
+ return host;
45374
+ }
45375
+ function readSetInput(args) {
45376
+ const key = readKey(args.key);
45377
+ const value = readValue(args.value, "value");
45378
+ const condition = args.condition;
45379
+ if (!["absent", "present", "matches"].includes(String(condition))) {
45380
+ throw new Error("condition must be absent, present, or matches");
45381
+ }
45382
+ const expectedValue = args.expectedValue === void 0 ? void 0 : readValue(args.expectedValue, "expectedValue");
45383
+ if (condition === "matches" && expectedValue === void 0) throw new Error("expectedValue is required when condition is matches");
45384
+ if (condition !== "matches" && expectedValue !== void 0) throw new Error("expectedValue is only valid when condition is matches");
45385
+ const ttlMs = args.ttlMs === void 0 ? void 0 : readBoundedInteger(args.ttlMs, 0, 1, MAX_TTL_MS, "ttlMs");
45386
+ const keepTtl = readBoolean(args.keepTtl, false, "keepTtl");
45387
+ if (ttlMs !== void 0 && keepTtl) throw new Error("ttlMs and keepTtl cannot be used together");
45388
+ return { key, value, condition, expectedValue, ttlMs, keepTtl };
45389
+ }
45390
+ function readCursor(value) {
45391
+ if (value === void 0 || value === null || value === "") return "0";
45392
+ if (typeof value !== "string" || !/^[0-9]+$/.test(value) || value.length > 32) {
45393
+ throw new Error("cursor must be a Redis numeric cursor string");
45394
+ }
45395
+ return value;
45396
+ }
45397
+ function readPattern(value) {
45398
+ if (value === void 0 || value === null || value === "") return "*";
45399
+ if (typeof value !== "string" || Buffer.byteLength(value, "utf8") > MAX_PATTERN_BYTES || /[\u0000-\u001f\u007f]/.test(value)) {
45400
+ throw new Error(`match must be under ${MAX_PATTERN_BYTES} bytes without control characters`);
45401
+ }
45402
+ return value;
45403
+ }
45404
+ function readScanType(value) {
45405
+ if (value === void 0 || value === null || value === "") return void 0;
45406
+ if (!["string", "list", "set", "zset", "hash", "stream"].includes(String(value))) {
45407
+ throw new Error("type must be string, list, set, zset, hash, or stream");
45408
+ }
45409
+ return String(value);
45410
+ }
45411
+ function assertScanPage(keys) {
45412
+ if (!Array.isArray(keys) || keys.length > MAX_SCAN_KEYS) {
45413
+ throw new Error(`Redis SCAN page exceeds ${MAX_SCAN_KEYS} keys`);
45414
+ }
45415
+ if (keys.some((key) => typeof key !== "string" || Buffer.byteLength(key, "utf8") > MAX_KEY_BYTES || /[\u0000-\u001f\u007f]/.test(key)) || Buffer.byteLength(JSON.stringify(keys), "utf8") > MAX_SCAN_BYTES) {
45416
+ throw new Error(`Redis SCAN page exceeds the ${MAX_SCAN_BYTES}-byte limit`);
45417
+ }
45418
+ }
45419
+ function readKey(value) {
45420
+ return readBoundedString(value, "key", MAX_KEY_BYTES);
45421
+ }
45422
+ function readValue(value, label) {
45423
+ if (typeof value !== "string") throw new Error(`${label} must be a string`);
45424
+ if (Buffer.byteLength(value, "utf8") > MAX_VALUE_BYTES) {
45425
+ throw new Error(`${label} exceeds the ${MAX_VALUE_BYTES}-byte limit`);
45426
+ }
45427
+ return value;
45428
+ }
45429
+ function readBoundedString(value, label, maxBytes) {
45430
+ if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > maxBytes || /[\u0000-\u001f\u007f]/.test(value)) {
45431
+ throw new Error(`${label} must be a non-empty string under ${maxBytes} bytes without control characters`);
45432
+ }
45433
+ return value;
45434
+ }
45435
+ function readRequiredString2(value, label, maxLength) {
45436
+ if (typeof value !== "string" || value.length === 0 || value.length > maxLength) {
45437
+ throw new Error(`${label} must be a non-empty string no longer than ${maxLength} characters`);
45438
+ }
45439
+ return value;
45440
+ }
45441
+ function readArrayReply(value, label) {
45442
+ if (!Array.isArray(value)) throw new Error(`Redis ${label} returned a malformed response`);
45443
+ return value;
45444
+ }
45445
+ function readStringArray(value, label) {
45446
+ if (!Array.isArray(value)) throw new Error(`Redis response ${label} is malformed`);
45447
+ return value.map((entry) => readReplyString(entry, label));
45448
+ }
45449
+ function readReplyString(value, label) {
45450
+ if (typeof value === "string") return value;
45451
+ if (Buffer.isBuffer(value)) return value.toString("utf8");
45452
+ throw new Error(`Redis response ${label} is malformed`);
45453
+ }
45454
+ function readReplyInteger(value, label) {
45455
+ const parsed = typeof value === "bigint" ? Number(value) : typeof value === "string" ? Number(value) : value;
45456
+ if (typeof parsed !== "number" || !Number.isSafeInteger(parsed)) throw new Error(`Redis response ${label} is malformed`);
45457
+ return parsed;
45458
+ }
45459
+ function normalizeTtl(value) {
45460
+ const ttl = readReplyInteger(value, "ttlMs");
45461
+ return ttl >= 0 ? ttl : null;
45462
+ }
45463
+ function safeErrorMessage2(error, secrets) {
45464
+ let message = error instanceof Error ? error.message : String(error);
45465
+ for (const secret of secrets) {
45466
+ if (secret.length >= 4) message = message.replaceAll(secret, "[REDACTED]");
45467
+ }
45468
+ return message;
45469
+ }
45470
+ function credentialSecrets2(source) {
45471
+ try {
45472
+ return readCredentials3(source).secrets;
45473
+ } catch {
45474
+ return [];
45475
+ }
45476
+ }
45477
+ function committedResult(data4) {
45478
+ return { status: "committed", data: data4, committedAt: Date.now(), idempotentReplay: false };
45479
+ }
45480
+ function conflict(message) {
45481
+ return { status: "conflict", alternatives: [], currentState: null, message };
45482
+ }
45483
+ function readCapability7(name, description, parameters) {
45484
+ return { name, class: "read", description, parameters };
45485
+ }
45486
+ function mutationCapability7(name, description, parameters) {
45487
+ return { name, class: "mutation", description, parameters, cas: "optimistic-read-verify", externalEffect: true };
45488
+ }
45489
+ function keyParameter() {
45490
+ return { type: "string", minLength: 1, maxLength: MAX_KEY_BYTES };
45491
+ }
45492
+ function keySchema() {
45493
+ return {
45494
+ type: "object",
45495
+ properties: { key: keyParameter() },
45496
+ required: ["key"],
45497
+ additionalProperties: false
45498
+ };
45499
+ }
45500
+
43893
45501
  // src/connectors/adapters/recall-ai.ts
43894
45502
  var recallAiRegions = [
43895
45503
  "https://us-east-1.recall.ai",
@@ -44720,16 +46328,14 @@ var salesforceConnector = declarativeRestConnector({
44720
46328
  });
44721
46329
 
44722
46330
  // src/connectors/adapters/sftp.ts
44723
- import { lookup } from "dns/promises";
44724
46331
  import { timingSafeEqual as timingSafeEqual4 } from "crypto";
44725
46332
  import { posix } from "path";
44726
- import ipaddr from "ipaddr.js";
44727
46333
  import SftpClient from "ssh2-sftp-client";
44728
46334
  var MAX_LIST_ENTRIES = 1e3;
44729
46335
  var MAX_PATH_LENGTH = 1024;
44730
46336
  function createSftpConnector(options = {}) {
44731
46337
  const createClient = options.createClient ?? (() => new SftpClient());
44732
- const lookupHost = options.lookupHost ?? resolvePublicAddresses;
46338
+ const lookupHost = options.lookupHost ?? resolvePublicHostAddresses;
44733
46339
  return {
44734
46340
  manifest: {
44735
46341
  kind: "sftp",
@@ -44828,7 +46434,7 @@ function createSftpConnector(options = {}) {
44828
46434
  ]
44829
46435
  },
44830
46436
  async executeRead({ source, capabilityName, args }) {
44831
- const credentials = readCredentials(source);
46437
+ const credentials = readCredentials4(source);
44832
46438
  if (capabilityName === "sftp.list") {
44833
46439
  const path = resolveRemotePath(credentials.rootPath, args.path ?? "/");
44834
46440
  const limit = readBoundedInteger(args.limit, MAX_LIST_ENTRIES, 1, MAX_LIST_ENTRIES, "limit");
@@ -44861,7 +46467,7 @@ function createSftpConnector(options = {}) {
44861
46467
  throw new Error(`Unknown SFTP read capability: ${capabilityName}`);
44862
46468
  },
44863
46469
  async executeMutation({ source, capabilityName, args }) {
44864
- const credentials = readCredentials(source);
46470
+ const credentials = readCredentials4(source);
44865
46471
  const prepared = prepareMutation(capabilityName, args, credentials.rootPath);
44866
46472
  return withSftp(credentials, createClient, lookupHost, async (client) => {
44867
46473
  let data4;
@@ -44895,7 +46501,7 @@ function createSftpConnector(options = {}) {
44895
46501
  },
44896
46502
  async test(source) {
44897
46503
  try {
44898
- const credentials = readCredentials(source);
46504
+ const credentials = readCredentials4(source);
44899
46505
  await withSftp(credentials, createClient, lookupHost, (client) => client.cwd());
44900
46506
  return { ok: true };
44901
46507
  } catch (error) {
@@ -44963,7 +46569,7 @@ async function withSftp(credentials, createClient, lookupHost, run) {
44963
46569
  await client.end().catch(() => false);
44964
46570
  }
44965
46571
  }
44966
- function readCredentials(source) {
46572
+ function readCredentials4(source) {
44967
46573
  let raw;
44968
46574
  if (source.credentials.kind === "custom") raw = source.credentials.values;
44969
46575
  else if (source.credentials.kind === "api-key") {
@@ -45004,21 +46610,6 @@ function validateHostname(host) {
45004
46610
  throw new Error("SFTP host must be a public hostname or IP address without a scheme");
45005
46611
  }
45006
46612
  }
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
46613
  function parseFingerprint(value) {
45023
46614
  if (/^[a-fA-F0-9]{64}$/.test(value)) return Buffer.from(value, "hex");
45024
46615
  const match = /^SHA256:([A-Za-z0-9+/]{43}=?)$/.exec(value);
@@ -54681,17 +56272,17 @@ var zohoBookingsConnector = {
54681
56272
  category: "calendar",
54682
56273
  defaultConsistencyModel: "authoritative",
54683
56274
  capabilities: [
54684
- readCapability5("workspaces.list", "List Zoho Bookings workspaces.", {}),
54685
- readCapability5("services.list", "List services, optionally within one workspace.", {
56275
+ readCapability8("workspaces.list", "List Zoho Bookings workspaces.", {}),
56276
+ readCapability8("services.list", "List services, optionally within one workspace.", {
54686
56277
  workspace_id: { type: "string" }
54687
56278
  }),
54688
- readCapability5("resources.list", "List bookable resources, optionally for one service.", {
56279
+ readCapability8("resources.list", "List bookable resources, optionally for one service.", {
54689
56280
  service_id: { type: "string" }
54690
56281
  }),
54691
- readCapability5("staff.list", "List staff, optionally for one service.", {
56282
+ readCapability8("staff.list", "List staff, optionally for one service.", {
54692
56283
  service_id: { type: "string" }
54693
56284
  }),
54694
- readCapability5("appointments.list", "List appointments from a required start time.", {
56285
+ readCapability8("appointments.list", "List appointments from a required start time.", {
54695
56286
  from_time: {
54696
56287
  type: "string",
54697
56288
  description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format."
@@ -54701,17 +56292,17 @@ var zohoBookingsConnector = {
54701
56292
  staff_id: { type: "string" },
54702
56293
  status: { type: "string" }
54703
56294
  }, ["from_time"]),
54704
- readCapability5("appointments.get", "Read one appointment by booking id.", {
56295
+ readCapability8("appointments.get", "Read one appointment by booking id.", {
54705
56296
  booking_id: { type: "string" }
54706
56297
  }, ["booking_id"]),
54707
- readCapability5("availability.fetch", "Fetch open time slots for a service and date.", {
56298
+ readCapability8("availability.fetch", "Fetch open time slots for a service and date.", {
54708
56299
  service_id: { type: "string" },
54709
56300
  selected_date: { type: "string", description: "Date in YYYY-MM-DD format." },
54710
56301
  staff_id: { type: "string" },
54711
56302
  group_id: { type: "string" },
54712
56303
  resource_id: { type: "string" }
54713
56304
  }, ["service_id", "selected_date"]),
54714
- mutationCapability5("appointments.book", "Book an appointment for a customer.", {
56305
+ mutationCapability8("appointments.book", "Book an appointment for a customer.", {
54715
56306
  service_id: { type: "string" },
54716
56307
  from_time: { type: "string", description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format." },
54717
56308
  customer_details: {
@@ -54727,13 +56318,13 @@ var zohoBookingsConnector = {
54727
56318
  additional_fields: { type: "object" },
54728
56319
  payment_info: { type: "object" }
54729
56320
  }, ["service_id", "from_time", "customer_details"]),
54730
- mutationCapability5("appointments.reschedule", "Move an appointment to a new time, staff member, or group.", {
56321
+ mutationCapability8("appointments.reschedule", "Move an appointment to a new time, staff member, or group.", {
54731
56322
  booking_id: { type: "string" },
54732
56323
  start_time: { type: "string", description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format." },
54733
56324
  staff_id: { type: "string" },
54734
56325
  group_id: { type: "string" }
54735
56326
  }, ["booking_id"]),
54736
- mutationCapability5("appointments.cancel", "Cancel an appointment by booking id.", {
56327
+ mutationCapability8("appointments.cancel", "Cancel an appointment by booking id.", {
54737
56328
  booking_id: { type: "string" }
54738
56329
  }, ["booking_id"])
54739
56330
  ]
@@ -54850,7 +56441,7 @@ var zohoBookingsConnector = {
54850
56441
  }
54851
56442
  }
54852
56443
  };
54853
- function readCapability5(name, description, properties, required = []) {
56444
+ function readCapability8(name, description, properties, required = []) {
54854
56445
  return {
54855
56446
  name,
54856
56447
  class: "read",
@@ -54859,7 +56450,7 @@ function readCapability5(name, description, properties, required = []) {
54859
56450
  requiredScopes: ["zohobookings.data.READ"]
54860
56451
  };
54861
56452
  }
54862
- function mutationCapability5(name, description, properties, required) {
56453
+ function mutationCapability8(name, description, properties, required) {
54863
56454
  return {
54864
56455
  name,
54865
56456
  class: "mutation",
@@ -62203,6 +63794,393 @@ var metabaseConnector = declarativeRestConnector({
62203
63794
  ]
62204
63795
  });
62205
63796
 
63797
+ // src/connectors/adapters/mysql.ts
63798
+ import { connect as createSocket, isIP as isIP5 } from "net";
63799
+ import {
63800
+ createConnection as createMysqlConnection
63801
+ } from "mysql2/promise";
63802
+ var MAX_PARAMETERS = 1e3;
63803
+ var MAX_PARAMETER_BYTES = 10 * 1024 * 1024;
63804
+ var MAX_RESULT_ROWS = 1e4;
63805
+ var MAX_RESULT_BYTES2 = 10 * 1024 * 1024;
63806
+ var DEFAULT_QUERY_TIMEOUT_MS = 3e4;
63807
+ function createMySqlConnector(options = {}) {
63808
+ const createConnection = options.createConnection ?? defaultCreateConnection;
63809
+ const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
63810
+ return {
63811
+ manifest: {
63812
+ kind: "mysql",
63813
+ displayName: "MySQL",
63814
+ description: "Inspect schemas and run bounded parameterized reads or approved compare-and-swap writes against MySQL over verified TLS.",
63815
+ auth: {
63816
+ kind: "api-key",
63817
+ hint: "JSON with host, port, user, password, optional database, and optional tlsCa. TLS and public network targets are mandatory."
63818
+ },
63819
+ defaultConsistencyModel: "authoritative",
63820
+ category: "database",
63821
+ rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
63822
+ capabilities: [
63823
+ {
63824
+ name: "mysql.databases.list",
63825
+ class: "read",
63826
+ description: "List databases visible to the connected MySQL user.",
63827
+ parameters: emptySchema2()
63828
+ },
63829
+ {
63830
+ name: "mysql.tables.list",
63831
+ class: "read",
63832
+ description: "List base tables and views in one database.",
63833
+ parameters: {
63834
+ type: "object",
63835
+ properties: { database: identifierSchema("Database name; defaults to the connection database.") },
63836
+ additionalProperties: false
63837
+ }
63838
+ },
63839
+ {
63840
+ name: "mysql.tables.describe",
63841
+ class: "read",
63842
+ description: "Read columns, types, nullability, defaults, and keys for one table.",
63843
+ parameters: {
63844
+ type: "object",
63845
+ properties: {
63846
+ database: identifierSchema("Database name; defaults to the connection database."),
63847
+ table: identifierSchema("Table name.")
63848
+ },
63849
+ required: ["table"],
63850
+ additionalProperties: false
63851
+ }
63852
+ },
63853
+ {
63854
+ name: "mysql.query",
63855
+ class: "read",
63856
+ description: "Run one parameterized SELECT, SHOW, DESCRIBE, or EXPLAIN statement inside a read-only transaction.",
63857
+ parameters: statementSchema(false)
63858
+ },
63859
+ {
63860
+ name: "mysql.execute",
63861
+ class: "mutation",
63862
+ description: "Run one approved parameterized INSERT, UPDATE, DELETE, or REPLACE statement and commit only when the affected-row count matches the caller expectation.",
63863
+ parameters: statementSchema(true),
63864
+ cas: "optimistic-read-verify",
63865
+ externalEffect: true
63866
+ }
63867
+ ]
63868
+ },
63869
+ async test(source) {
63870
+ try {
63871
+ await withConnection(source, createConnection, resolveHost, async (connection) => {
63872
+ await connection.ping();
63873
+ });
63874
+ return { ok: true };
63875
+ } catch (error) {
63876
+ return { ok: false, reason: safeErrorMessage3(error) };
63877
+ }
63878
+ },
63879
+ async executeRead(inv) {
63880
+ const query2 = readQuery2(inv);
63881
+ assertReadStatement(query2.statement);
63882
+ const data4 = await withConnection(inv.source, createConnection, resolveHost, async (connection) => {
63883
+ await executeStatement(connection, {
63884
+ statement: "START TRANSACTION READ ONLY",
63885
+ parameters: []
63886
+ }, queryTimeout(inv.source));
63887
+ try {
63888
+ const [rows, fields] = await executeStatement(connection, query2, queryTimeout(inv.source));
63889
+ if (!Array.isArray(rows)) throw new Error("MySQL read returned a non-row result");
63890
+ return boundedResult(rows, fields);
63891
+ } finally {
63892
+ await connection.rollback().catch(() => void 0);
63893
+ }
63894
+ });
63895
+ return { data: data4, fetchedAt: Date.now() };
63896
+ },
63897
+ async executeMutation(inv) {
63898
+ if (inv.capabilityName !== "mysql.execute") {
63899
+ throw new Error(`Unknown MySQL mutation capability: ${inv.capabilityName}`);
63900
+ }
63901
+ const query2 = readStatementArgs(inv.args);
63902
+ assertMutationStatement(query2.statement);
63903
+ const expectedAffectedRows = readBoundedInteger(
63904
+ inv.args.expectedAffectedRows,
63905
+ -1,
63906
+ 0,
63907
+ MAX_RESULT_ROWS,
63908
+ "expectedAffectedRows"
63909
+ );
63910
+ if (expectedAffectedRows < 0) throw new Error("expectedAffectedRows is required");
63911
+ return withConnection(inv.source, createConnection, resolveHost, async (connection) => {
63912
+ await connection.beginTransaction();
63913
+ try {
63914
+ const [result] = await executeStatement(connection, query2, queryTimeout(inv.source));
63915
+ const affectedRows = readAffectedRows(result);
63916
+ if (affectedRows !== expectedAffectedRows) {
63917
+ await connection.rollback();
63918
+ return {
63919
+ status: "conflict",
63920
+ alternatives: [],
63921
+ currentState: { affectedRows },
63922
+ message: `MySQL write affected ${affectedRows} rows; expected ${expectedAffectedRows}. The transaction was rolled back.`
63923
+ };
63924
+ }
63925
+ await connection.commit();
63926
+ return committedMutation(result, affectedRows);
63927
+ } catch (error) {
63928
+ await connection.rollback().catch(() => void 0);
63929
+ throw error;
63930
+ }
63931
+ });
63932
+ }
63933
+ };
63934
+ }
63935
+ var mysqlConnector = createMySqlConnector();
63936
+ async function defaultCreateConnection(config) {
63937
+ return createMysqlConnection(config);
63938
+ }
63939
+ async function withConnection(source, createConnection, resolveHost, run) {
63940
+ const credentials = readCredentials5(source);
63941
+ const addresses = await resolveHost(credentials.host);
63942
+ if (addresses.length === 0) throw new Error("MySQL host did not resolve");
63943
+ const address = addresses[0];
63944
+ let connection;
63945
+ try {
63946
+ connection = await createConnection({
63947
+ host: credentials.host,
63948
+ port: credentials.port,
63949
+ user: credentials.user,
63950
+ password: credentials.password,
63951
+ database: credentials.database,
63952
+ connectTimeout: credentials.connectTimeoutMs,
63953
+ multipleStatements: false,
63954
+ namedPlaceholders: false,
63955
+ supportBigNumbers: true,
63956
+ bigNumberStrings: true,
63957
+ decimalNumbers: false,
63958
+ dateStrings: true,
63959
+ timezone: "Z",
63960
+ charset: "utf8mb4",
63961
+ enableKeepAlive: true,
63962
+ maxPreparedStatements: 100,
63963
+ stream: () => createSocket({ host: address, port: credentials.port }),
63964
+ ssl: {
63965
+ ca: credentials.tlsCa,
63966
+ minVersion: "TLSv1.2",
63967
+ rejectUnauthorized: true,
63968
+ verifyIdentity: true
63969
+ }
63970
+ });
63971
+ return await run(connection);
63972
+ } finally {
63973
+ if (connection) await connection.end().catch(() => void 0);
63974
+ }
63975
+ }
63976
+ function readCredentials5(source) {
63977
+ let raw;
63978
+ if (source.credentials.kind === "custom") raw = source.credentials.values;
63979
+ else if (source.credentials.kind === "api-key") {
63980
+ try {
63981
+ raw = JSON.parse(source.credentials.apiKey);
63982
+ } catch {
63983
+ throw new Error("MySQL credential must be valid JSON");
63984
+ }
63985
+ } else {
63986
+ throw new Error("MySQL requires a structured credential bundle");
63987
+ }
63988
+ if (!isPlainRecord(raw)) throw new Error("MySQL credential must be a JSON object");
63989
+ const host = requiredCredentialString3(raw.host, "host", 253);
63990
+ validateHost(host);
63991
+ const user = requiredCredentialString3(raw.user ?? raw.username, "user", 128);
63992
+ const password = requiredCredentialString3(raw.password, "password", 4096);
63993
+ const database = readOptionalString(raw.database, "database");
63994
+ if (database && database.length > 64) throw new Error("MySQL credential database exceeds 64 characters");
63995
+ const tlsCa = readOptionalString(raw.tlsCa, "tlsCa");
63996
+ if (tlsCa && Buffer.byteLength(tlsCa, "utf8") > 1024 * 1024) {
63997
+ throw new Error("MySQL credential tlsCa exceeds the 1 MiB limit");
63998
+ }
63999
+ return {
64000
+ host,
64001
+ port: readBoundedInteger(raw.port, 3306, 1, 65535, "port"),
64002
+ user,
64003
+ password,
64004
+ database,
64005
+ tlsCa,
64006
+ connectTimeoutMs: readBoundedInteger(raw.connectTimeoutMs, 1e4, 1e3, 3e4, "connectTimeoutMs"),
64007
+ queryTimeoutMs: readBoundedInteger(raw.queryTimeoutMs, DEFAULT_QUERY_TIMEOUT_MS, 1e3, 6e4, "queryTimeoutMs")
64008
+ };
64009
+ }
64010
+ function readQuery2(inv) {
64011
+ if (inv.capabilityName === "mysql.databases.list") {
64012
+ return {
64013
+ statement: "SELECT SCHEMA_NAME AS databaseName, DEFAULT_CHARACTER_SET_NAME AS characterSet, DEFAULT_COLLATION_NAME AS collation FROM information_schema.SCHEMATA ORDER BY SCHEMA_NAME",
64014
+ parameters: []
64015
+ };
64016
+ }
64017
+ if (inv.capabilityName === "mysql.tables.list") {
64018
+ const database = readDatabase(inv.args.database, inv.source);
64019
+ return {
64020
+ statement: "SELECT TABLE_NAME AS tableName, TABLE_TYPE AS tableType, ENGINE AS engine, TABLE_ROWS AS estimatedRows, TABLE_COLLATION AS collation FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME",
64021
+ parameters: [database]
64022
+ };
64023
+ }
64024
+ if (inv.capabilityName === "mysql.tables.describe") {
64025
+ const database = readDatabase(inv.args.database, inv.source);
64026
+ const table = readIdentifier(inv.args.table, "table");
64027
+ return {
64028
+ statement: "SELECT COLUMN_NAME AS columnName, ORDINAL_POSITION AS ordinalPosition, COLUMN_DEFAULT AS columnDefault, IS_NULLABLE AS isNullable, DATA_TYPE AS dataType, COLUMN_TYPE AS columnType, COLUMN_KEY AS columnKey, EXTRA AS extra FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION",
64029
+ parameters: [database, table]
64030
+ };
64031
+ }
64032
+ if (inv.capabilityName === "mysql.query") return readStatementArgs(inv.args);
64033
+ throw new Error(`Unknown MySQL read capability: ${inv.capabilityName}`);
64034
+ }
64035
+ function readStatementArgs(args) {
64036
+ const statement = requiredString7(args.statement, "statement");
64037
+ if (Buffer.byteLength(statement, "utf8") > 1024 * 1024) throw new Error("statement exceeds the 1 MiB limit");
64038
+ const parameters = args.parameters === void 0 ? [] : args.parameters;
64039
+ if (!Array.isArray(parameters) || parameters.length > MAX_PARAMETERS) {
64040
+ throw new Error(`parameters must be an array with at most ${MAX_PARAMETERS} entries`);
64041
+ }
64042
+ for (const [index, value] of parameters.entries()) {
64043
+ if (value !== null && !["string", "number", "boolean"].includes(typeof value)) {
64044
+ throw new Error(`parameters[${index}] must be a JSON scalar or null`);
64045
+ }
64046
+ if (typeof value === "number" && !Number.isFinite(value)) {
64047
+ throw new Error(`parameters[${index}] must be finite`);
64048
+ }
64049
+ }
64050
+ if (Buffer.byteLength(JSON.stringify(parameters), "utf8") > MAX_PARAMETER_BYTES) {
64051
+ throw new Error(`parameters exceed the ${MAX_PARAMETER_BYTES}-byte limit`);
64052
+ }
64053
+ return { statement, parameters };
64054
+ }
64055
+ function assertReadStatement(statement) {
64056
+ const keyword = leadingKeyword(statement);
64057
+ if (!["SELECT", "SHOW", "DESCRIBE", "DESC", "EXPLAIN", "WITH"].includes(keyword)) {
64058
+ throw new Error("mysql.query accepts only SELECT, SHOW, DESCRIBE, or EXPLAIN statements");
64059
+ }
64060
+ if (/\b(?:INTO\s+(?:OUTFILE|DUMPFILE)|LOAD_FILE\s*\(|GET_LOCK\s*\(|RELEASE_LOCK\s*\(|SLEEP\s*\(|BENCHMARK\s*\(|FOR\s+UPDATE|LOCK\s+IN\s+SHARE\s+MODE)\b/i.test(statement)) {
64061
+ throw new Error("mysql.query rejects file, lock, and delay side effects");
64062
+ }
64063
+ }
64064
+ function assertMutationStatement(statement) {
64065
+ const keyword = leadingKeyword(statement);
64066
+ if (!["INSERT", "UPDATE", "DELETE", "REPLACE"].includes(keyword)) {
64067
+ throw new Error("mysql.execute accepts only INSERT, UPDATE, DELETE, or REPLACE statements");
64068
+ }
64069
+ }
64070
+ function leadingKeyword(statement) {
64071
+ const withoutComments = statement.replace(/^\s*(?:--[^\n]*(?:\n|$)|#[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/\s*)*/u, "").trimStart();
64072
+ return /^[A-Za-z]+/.exec(withoutComments)?.[0]?.toUpperCase() ?? "";
64073
+ }
64074
+ async function executeStatement(connection, query2, timeout) {
64075
+ return connection.execute({ sql: query2.statement, values: query2.parameters, timeout });
64076
+ }
64077
+ function boundedResult(rows, fields) {
64078
+ if (rows.length > MAX_RESULT_ROWS) throw new Error(`MySQL result exceeds ${MAX_RESULT_ROWS} rows`);
64079
+ const data4 = {
64080
+ rows: jsonSafe(rows),
64081
+ columns: fields.map((field) => ({
64082
+ name: field.name,
64083
+ table: field.table,
64084
+ database: field.schema,
64085
+ type: field.type
64086
+ })),
64087
+ rowCount: rows.length
64088
+ };
64089
+ const serialized = JSON.stringify(data4);
64090
+ if (Buffer.byteLength(serialized, "utf8") > MAX_RESULT_BYTES2) {
64091
+ throw new Error(`MySQL result exceeds the ${MAX_RESULT_BYTES2}-byte limit`);
64092
+ }
64093
+ return data4;
64094
+ }
64095
+ function readAffectedRows(result) {
64096
+ if (!isPlainRecord(result) || !Number.isInteger(result.affectedRows) || result.affectedRows < 0) {
64097
+ throw new Error("MySQL mutation did not return an affected-row count");
64098
+ }
64099
+ return result.affectedRows;
64100
+ }
64101
+ function committedMutation(result, affectedRows) {
64102
+ const header2 = result;
64103
+ const rawInsertId = header2.insertId;
64104
+ const insertId = typeof rawInsertId === "number" && Number.isSafeInteger(rawInsertId) ? rawInsertId : typeof rawInsertId === "string" ? rawInsertId : void 0;
64105
+ return {
64106
+ status: "committed",
64107
+ data: jsonSafe({
64108
+ affectedRows,
64109
+ changedRows: header2.changedRows,
64110
+ insertId,
64111
+ warningStatus: header2.warningStatus
64112
+ }),
64113
+ committedAt: Date.now(),
64114
+ idempotentReplay: false
64115
+ };
64116
+ }
64117
+ function readDatabase(value, source) {
64118
+ const explicit = value === void 0 ? void 0 : readIdentifier(value, "database");
64119
+ if (explicit) return explicit;
64120
+ const credentials = readCredentials5(source);
64121
+ if (!credentials.database) throw new Error("database is required when the connection has no default database");
64122
+ return credentials.database;
64123
+ }
64124
+ function queryTimeout(source) {
64125
+ return readCredentials5(source).queryTimeoutMs;
64126
+ }
64127
+ function requiredString7(value, label) {
64128
+ const parsed = readOptionalString(value, label);
64129
+ if (!parsed) throw new Error(`${label} is required`);
64130
+ return parsed;
64131
+ }
64132
+ function requiredCredentialString3(value, label, maxLength) {
64133
+ const parsed = readOptionalString(value, label);
64134
+ if (!parsed) throw new Error(`MySQL credential ${label} is required`);
64135
+ if (parsed.length > maxLength) throw new Error(`MySQL credential ${label} exceeds ${maxLength} characters`);
64136
+ return parsed;
64137
+ }
64138
+ function readIdentifier(value, label) {
64139
+ const parsed = requiredString7(value, label);
64140
+ if (parsed.length > 64) throw new Error(`${label} exceeds 64 characters`);
64141
+ return parsed;
64142
+ }
64143
+ function validateHost(host) {
64144
+ const normalized = host.toLowerCase();
64145
+ if (host.length > 253 || host.includes("://") || /[\s/@]/.test(host) || normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || isIP5(host) === 0 && !/^[A-Za-z0-9.-]+$/.test(host) || host.split(".").some((label) => !label || label.startsWith("-") || label.endsWith("-"))) {
64146
+ throw new Error("MySQL host must be a public hostname or IP address without a scheme");
64147
+ }
64148
+ }
64149
+ function emptySchema2() {
64150
+ return { type: "object", properties: {}, additionalProperties: false };
64151
+ }
64152
+ function identifierSchema(description) {
64153
+ return { type: "string", minLength: 1, maxLength: 64, description };
64154
+ }
64155
+ function statementSchema(mutation) {
64156
+ const properties = {
64157
+ statement: { type: "string", minLength: 1, maxLength: 1024 * 1024 },
64158
+ parameters: {
64159
+ type: "array",
64160
+ maxItems: MAX_PARAMETERS,
64161
+ items: { type: ["string", "number", "boolean", "null"] },
64162
+ description: "Values bound to ? placeholders in order."
64163
+ }
64164
+ };
64165
+ if (mutation) {
64166
+ properties.expectedAffectedRows = {
64167
+ type: "integer",
64168
+ minimum: 0,
64169
+ maximum: MAX_RESULT_ROWS,
64170
+ description: "Commit only when MySQL reports exactly this many affected rows."
64171
+ };
64172
+ }
64173
+ return {
64174
+ type: "object",
64175
+ properties,
64176
+ required: mutation ? ["statement", "expectedAffectedRows"] : ["statement"],
64177
+ additionalProperties: false
64178
+ };
64179
+ }
64180
+ function safeErrorMessage3(error) {
64181
+ return error instanceof Error && error.message ? error.message : "MySQL connection failed";
64182
+ }
64183
+
62206
64184
  // src/connectors/adapters/segment.ts
62207
64185
  var segmentConnector = declarativeRestConnector({
62208
64186
  kind: "segment",
@@ -63698,6 +65676,7 @@ var CONNECTOR_ADAPTER_FACTORIES = [
63698
65676
  defineFactoryAdapter(() => digitalOceanConnector, {}),
63699
65677
  defineFactoryAdapter(() => clicksendConnector, {}),
63700
65678
  defineFactoryAdapter(() => metabaseConnector, {}),
65679
+ defineFactoryAdapter(() => mysqlConnector, {}),
63701
65680
  defineFactoryAdapter(
63702
65681
  () => microsoftDynamics365BusinessCentralConnector,
63703
65682
  microsoftOAuthEnvMap
@@ -63755,6 +65734,10 @@ var CONNECTOR_ADAPTER_FACTORIES = [
63755
65734
  defineFactoryAdapter(() => amazonSqsConnector, {}),
63756
65735
  defineFactoryAdapter(() => httpConnector, {}),
63757
65736
  defineFactoryAdapter(() => rssConnector, {}),
65737
+ defineFactoryAdapter(() => kafkaConnector, {}),
65738
+ defineFactoryAdapter(() => duckdbConnector, {}),
65739
+ defineFactoryAdapter(() => rabbitMqConnector, {}),
65740
+ defineFactoryAdapter(() => redisConnector, {}),
63758
65741
  defineFactoryAdapter(() => csvFilesConnector, {}),
63759
65742
  defineFactoryAdapter(() => excelFilesConnector, {}),
63760
65743
  defineFactoryAdapter(() => parquetFilesConnector, {}),
@@ -63827,10 +65810,7 @@ var CONNECTOR_ADAPTER_FACTORIES = [
63827
65810
  clientId: "MONDAY_OAUTH_CLIENT_ID",
63828
65811
  clientSecret: "MONDAY_OAUTH_CLIENT_SECRET"
63829
65812
  }),
63830
- defineFactoryAdapter(() => clickupConnector, {
63831
- clientId: "CLICKUP_OAUTH_CLIENT_ID",
63832
- clientSecret: "CLICKUP_OAUTH_CLIENT_SECRET"
63833
- }),
65813
+ defineFactoryAdapter(() => clickupConnector, {}),
63834
65814
  defineFactoryAdapter(() => basecampConnector, {
63835
65815
  clientId: "BASECAMP_OAUTH_CLIENT_ID",
63836
65816
  clientSecret: "BASECAMP_OAUTH_CLIENT_SECRET"
@@ -69735,7 +71715,7 @@ var sanityConnector = declarativeRestConnector({
69735
71715
  var figmaConnector = declarativeRestConnector({
69736
71716
  kind: "figma",
69737
71717
  displayName: "Figma",
69738
- description: "Read Figma files, components, comments, and team projects; post comments back into the design source of truth.",
71718
+ description: "Read Figma files, components, comments, and project metadata; post comments back into the design source of truth.",
69739
71719
  auth: {
69740
71720
  kind: "oauth2",
69741
71721
  authorizationUrl: "https://www.figma.com/oauth",
@@ -69748,7 +71728,7 @@ var figmaConnector = declarativeRestConnector({
69748
71728
  "file_dev_resources:read",
69749
71729
  "file_versions:read",
69750
71730
  "library_content:read",
69751
- "projects:read",
71731
+ "project_metadata:read",
69752
71732
  "team_library_content:read",
69753
71733
  "webhooks:write"
69754
71734
  ],
@@ -69964,35 +71944,16 @@ var figmaConnector = declarativeRestConnector({
69964
71944
  requiredScopes: ["file_comments:read"]
69965
71945
  },
69966
71946
  {
69967
- name: "teams.projects.list",
71947
+ name: "projects.metadata.get",
69968
71948
  class: "read",
69969
- description: "List projects inside a Figma team.",
71949
+ description: "Get metadata for a Figma project visible to the authenticated user.",
69970
71950
  parameters: {
69971
71951
  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",
69980
- class: "read",
69981
- description: "List Figma files inside a project.",
69982
- parameters: {
69983
- type: "object",
69984
- properties: {
69985
- project_id: { type: "string" },
69986
- branch_data: { type: "boolean" }
69987
- },
71952
+ properties: { project_id: { type: "string" } },
69988
71953
  required: ["project_id"]
69989
71954
  },
69990
- request: {
69991
- method: "GET",
69992
- path: "/v1/projects/{project_id}/files",
69993
- query: { branch_data: "{branch_data}" }
69994
- },
69995
- requiredScopes: ["projects:read"]
71955
+ request: { method: "GET", path: "/v1/projects/{project_id}/meta" },
71956
+ requiredScopes: ["project_metadata:read"]
69996
71957
  },
69997
71958
  {
69998
71959
  name: "teams.components.list",
@@ -70159,7 +72120,7 @@ var figmaConnector = declarativeRestConnector({
70159
72120
  var figjamConnector = declarativeRestConnector({
70160
72121
  kind: "figjam",
70161
72122
  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.",
72123
+ 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
72124
  auth: {
70164
72125
  kind: "oauth2",
70165
72126
  authorizationUrl: "https://www.figma.com/oauth",
@@ -70170,7 +72131,7 @@ var figjamConnector = declarativeRestConnector({
70170
72131
  "file_comments:write",
70171
72132
  "file_content:read",
70172
72133
  "file_versions:read",
70173
- "projects:read",
72134
+ "project_metadata:read",
70174
72135
  "webhooks:write"
70175
72136
  ],
70176
72137
  clientIdEnv: "FIGMA_OAUTH_CLIENT_ID",
@@ -70328,33 +72289,16 @@ var figjamConnector = declarativeRestConnector({
70328
72289
  requiredScopes: ["file_comments:read"]
70329
72290
  },
70330
72291
  {
70331
- name: "teams.projects.list",
70332
- class: "read",
70333
- description: "List projects inside a Figma team (FigJam boards live alongside Figma design files inside projects).",
70334
- parameters: {
70335
- 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",
72292
+ name: "projects.metadata.get",
70344
72293
  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".',
72294
+ description: "Get metadata for a Figma/FigJam project visible to the authenticated user.",
70346
72295
  parameters: {
70347
72296
  type: "object",
70348
- properties: {
70349
- project_id: { type: "string" }
70350
- },
72297
+ properties: { project_id: { type: "string" } },
70351
72298
  required: ["project_id"]
70352
72299
  },
70353
- request: {
70354
- method: "GET",
70355
- path: "/v1/projects/{project_id}/files"
70356
- },
70357
- requiredScopes: ["projects:read"]
72300
+ request: { method: "GET", path: "/v1/projects/{project_id}/meta" },
72301
+ requiredScopes: ["project_metadata:read"]
70358
72302
  },
70359
72303
  {
70360
72304
  name: "files.comments.create",
@@ -143646,6 +145590,7 @@ export {
143646
145590
  deepgramConnector,
143647
145591
  detectingAiConnector,
143648
145592
  digitalPilotConnector,
145593
+ duckdbConnector,
143649
145594
  docsbotConnector,
143650
145595
  dripConnector,
143651
145596
  echowinConnector,
@@ -143754,6 +145699,9 @@ export {
143754
145699
  jiraCloudConnector,
143755
145700
  jotformConnector,
143756
145701
  justcallConnector,
145702
+ createKafkaConnector,
145703
+ kafkaConnector,
145704
+ publicKafkaSocketFactory,
143757
145705
  leadConnectorConnector,
143758
145706
  lemonSqueezyConnector,
143759
145707
  linearConnector,
@@ -143791,6 +145739,10 @@ export {
143791
145739
  pipedriveConnector,
143792
145740
  parquetFilesConnector,
143793
145741
  quickbooksConnector,
145742
+ createRabbitMqConnector,
145743
+ rabbitMqConnector,
145744
+ createRedisConnector,
145745
+ redisConnector,
143794
145746
  recallAiConnector,
143795
145747
  rampConnector,
143796
145748
  ringcentralConnector,
@@ -143858,6 +145810,8 @@ export {
143858
145810
  googleBigqueryConnector,
143859
145811
  hightouchConnector,
143860
145812
  metabaseConnector,
145813
+ createMySqlConnector,
145814
+ mysqlConnector,
143861
145815
  segmentConnector,
143862
145816
  supabaseConnector,
143863
145817
  builtwithConnector,
@@ -144249,4 +146203,4 @@ export {
144249
146203
  demandbaseConnector,
144250
146204
  adapters_exports
144251
146205
  };
144252
- //# sourceMappingURL=chunk-ARONCYSG.js.map
146206
+ //# sourceMappingURL=chunk-FOIC32DF.js.map