@tangle-network/agent-integrations 0.53.30 → 0.53.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/activepieces-catalog.json +5 -5
- package/dist/catalog.js +5 -5
- package/dist/{chunk-DQIIDGA2.js → chunk-3NQFKQTR.js} +4 -3
- package/dist/chunk-3NQFKQTR.js.map +1 -0
- package/dist/{chunk-LBJJMBVE.js → chunk-AG4ORH2A.js} +8 -5
- package/dist/chunk-AG4ORH2A.js.map +1 -0
- package/dist/{chunk-UEJFZZ2E.js → chunk-EC675ZAS.js} +185 -4
- package/dist/chunk-EC675ZAS.js.map +1 -0
- package/dist/{chunk-DSIV6RU6.js → chunk-QOY5LZXH.js} +2 -2
- package/dist/{chunk-5LDZLVKP.js → chunk-R5GQB3SQ.js} +3045 -1056
- package/dist/{chunk-5LDZLVKP.js.map → chunk-R5GQB3SQ.js.map} +1 -1
- package/dist/connectors/adapters/index.d.ts +146 -23
- package/dist/connectors/adapters/index.js +25 -1
- package/dist/connectors/index.d.ts +6 -1
- package/dist/connectors/index.js +26 -2
- package/dist/coverage-catalog.js +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +29 -5
- package/dist/mcp.js +5 -5
- package/dist/registry.js +5 -5
- package/dist/runtime.js +5 -5
- package/dist/specs.js +4 -4
- package/docs/integration-execution-audit.md +28 -21
- package/docs/integration-execution-matrix.json +429 -479
- package/package.json +8 -1
- package/dist/chunk-DQIIDGA2.js.map +0 -1
- package/dist/chunk-LBJJMBVE.js.map +0 -1
- package/dist/chunk-UEJFZZ2E.js.map +0 -1
- /package/dist/{chunk-DSIV6RU6.js.map → chunk-QOY5LZXH.js.map} +0 -0
|
@@ -182,6 +182,12 @@ __export(adapters_exports, {
|
|
|
182
182
|
copyAiConnector: () => copyAiConnector,
|
|
183
183
|
couchbaseConnector: () => couchbaseConnector,
|
|
184
184
|
createKafkaConnector: () => createKafkaConnector,
|
|
185
|
+
createMongoDbConnector: () => createMongoDbConnector,
|
|
186
|
+
createMySqlConnector: () => createMySqlConnector,
|
|
187
|
+
createPostgresConnector: () => createPostgresConnector,
|
|
188
|
+
createRabbitMqConnector: () => createRabbitMqConnector,
|
|
189
|
+
createRedisConnector: () => createRedisConnector,
|
|
190
|
+
createRedshiftConnector: () => createRedshiftConnector,
|
|
185
191
|
createSftpConnector: () => createSftpConnector,
|
|
186
192
|
createTangleIdentityClient: () => createTangleIdentityClient,
|
|
187
193
|
crispConnector: () => crispConnector,
|
|
@@ -218,6 +224,7 @@ __export(adapters_exports, {
|
|
|
218
224
|
dripConnector: () => dripConnector,
|
|
219
225
|
dropboxConnector: () => dropboxConnector,
|
|
220
226
|
dubConnector: () => dubConnector,
|
|
227
|
+
duckdbConnector: () => duckdbConnector,
|
|
221
228
|
dumplingAiConnector: () => dumplingAiConnector,
|
|
222
229
|
dustConnector: () => dustConnector,
|
|
223
230
|
easyPeasyAiConnector: () => easyPeasyAiConnector,
|
|
@@ -438,9 +445,11 @@ __export(adapters_exports, {
|
|
|
438
445
|
mondayConnector: () => mondayConnector,
|
|
439
446
|
mongodbConnector: () => mongodbConnector,
|
|
440
447
|
mycaseConnector: () => mycaseConnector,
|
|
448
|
+
mysqlConnector: () => mysqlConnector,
|
|
441
449
|
n8nConnector: () => n8nConnector,
|
|
442
450
|
netlifyConnector: () => netlifyConnector,
|
|
443
451
|
netsuiteConnector: () => netsuiteConnector,
|
|
452
|
+
neverbounceConnector: () => neverbounceConnector,
|
|
444
453
|
niftyConnector: () => niftyConnector,
|
|
445
454
|
ninjapipeConnector: () => ninjapipeConnector,
|
|
446
455
|
nocodbConnector: () => nocodbConnector,
|
|
@@ -508,6 +517,7 @@ __export(adapters_exports, {
|
|
|
508
517
|
quadernoConnector: () => quadernoConnector,
|
|
509
518
|
quickbooksConnector: () => quickbooksConnector,
|
|
510
519
|
quickzuConnector: () => quickzuConnector,
|
|
520
|
+
rabbitMqConnector: () => rabbitMqConnector,
|
|
511
521
|
raiaAiConnector: () => raiaAiConnector,
|
|
512
522
|
rampConnector: () => rampConnector,
|
|
513
523
|
rapidtextAiConnector: () => rapidtextAiConnector,
|
|
@@ -515,6 +525,8 @@ __export(adapters_exports, {
|
|
|
515
525
|
recallAiConnector: () => recallAiConnector,
|
|
516
526
|
recurlyConnector: () => recurlyConnector,
|
|
517
527
|
redditConnector: () => redditConnector,
|
|
528
|
+
redisConnector: () => redisConnector,
|
|
529
|
+
redshiftConnector: () => redshiftConnector,
|
|
518
530
|
reoonVerifierConnector: () => reoonVerifierConnector,
|
|
519
531
|
replyIoConnector: () => replyIoConnector,
|
|
520
532
|
resolveConnectorAdapterFactoryOptions: () => resolveConnectorAdapterFactoryOptions,
|
|
@@ -1123,6 +1135,10 @@ function sameUrl(left, right) {
|
|
|
1123
1135
|
function applyCredentials(headers, url, placement, credentials) {
|
|
1124
1136
|
const token = credentialToken(credentials);
|
|
1125
1137
|
if (placement.kind === "bearer") headers.authorization = `Bearer ${token}`;
|
|
1138
|
+
if (placement.kind === "authorization-by-auth") {
|
|
1139
|
+
const prefix = credentials.kind === "oauth2" ? placement.oauth2Prefix ?? "Bearer " : placement.apiKeyPrefix ?? "";
|
|
1140
|
+
headers.authorization = `${prefix}${token}`;
|
|
1141
|
+
}
|
|
1126
1142
|
if (placement.kind === "basic-api-key") {
|
|
1127
1143
|
headers.authorization = `Basic ${Buffer.from(`${token}:`).toString("base64")}`;
|
|
1128
1144
|
}
|
|
@@ -6500,6 +6516,294 @@ var digitalPilotConnector = declarativeRestConnector({
|
|
|
6500
6516
|
]
|
|
6501
6517
|
});
|
|
6502
6518
|
|
|
6519
|
+
// src/connectors/adapters/duckdb.ts
|
|
6520
|
+
import {
|
|
6521
|
+
DuckDBInstance
|
|
6522
|
+
} from "@duckdb/node-api";
|
|
6523
|
+
var MAX_ARGUMENTS = 100;
|
|
6524
|
+
var MAX_COLUMNS2 = 256;
|
|
6525
|
+
var MAX_EXECUTION_MS = 5e3;
|
|
6526
|
+
var MAX_INPUT_BYTES = 10 * 1024 * 1024;
|
|
6527
|
+
var MAX_QUERY_BYTES = 2e4;
|
|
6528
|
+
var MAX_RESULT_BYTES = 10 * 1024 * 1024;
|
|
6529
|
+
var MAX_ROWS = 1e4;
|
|
6530
|
+
var MAX_SCHEMA_DEPTH = 16;
|
|
6531
|
+
var MAX_TABLES = 16;
|
|
6532
|
+
var TABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,62}$/;
|
|
6533
|
+
var duckdbConnector = {
|
|
6534
|
+
manifest: {
|
|
6535
|
+
kind: "duckdb",
|
|
6536
|
+
displayName: "DuckDB",
|
|
6537
|
+
description: "Load bounded JSON tables into a throwaway in-memory DuckDB database and run a parameterized query.",
|
|
6538
|
+
auth: { kind: "none" },
|
|
6539
|
+
defaultConsistencyModel: "advisory",
|
|
6540
|
+
category: "database",
|
|
6541
|
+
capabilities: [
|
|
6542
|
+
{
|
|
6543
|
+
name: "create.and.query.db",
|
|
6544
|
+
class: "read",
|
|
6545
|
+
description: "Create in-memory tables from JSON arrays and return a bounded parameterized SQL query result.",
|
|
6546
|
+
parameters: {
|
|
6547
|
+
type: "object",
|
|
6548
|
+
properties: {
|
|
6549
|
+
tables: {
|
|
6550
|
+
type: "array",
|
|
6551
|
+
minItems: 1,
|
|
6552
|
+
maxItems: MAX_TABLES,
|
|
6553
|
+
items: {
|
|
6554
|
+
type: "object",
|
|
6555
|
+
properties: {
|
|
6556
|
+
name: { type: "string", pattern: TABLE_NAME.source, maxLength: 63 },
|
|
6557
|
+
data: { type: "array", maxItems: MAX_ROWS, items: { type: "object" } },
|
|
6558
|
+
schema: { type: "object", maxProperties: MAX_COLUMNS2 }
|
|
6559
|
+
},
|
|
6560
|
+
required: ["name", "data"],
|
|
6561
|
+
additionalProperties: false
|
|
6562
|
+
}
|
|
6563
|
+
},
|
|
6564
|
+
query: {
|
|
6565
|
+
type: "string",
|
|
6566
|
+
minLength: 1,
|
|
6567
|
+
maxLength: MAX_QUERY_BYTES,
|
|
6568
|
+
description: "A single relation-returning SQL query. Use $1, $2, and args for dynamic values."
|
|
6569
|
+
},
|
|
6570
|
+
args: {
|
|
6571
|
+
type: "array",
|
|
6572
|
+
maxItems: MAX_ARGUMENTS,
|
|
6573
|
+
items: { type: ["string", "number", "boolean", "null"] }
|
|
6574
|
+
},
|
|
6575
|
+
maxRows: { type: "integer", minimum: 1, maximum: MAX_ROWS, default: 1e3 }
|
|
6576
|
+
},
|
|
6577
|
+
required: ["tables", "query"],
|
|
6578
|
+
additionalProperties: false
|
|
6579
|
+
}
|
|
6580
|
+
}
|
|
6581
|
+
]
|
|
6582
|
+
},
|
|
6583
|
+
async executeRead({ capabilityName, args }) {
|
|
6584
|
+
if (capabilityName !== "create.and.query.db") {
|
|
6585
|
+
throw new Error(`Unknown DuckDB capability: ${capabilityName}`);
|
|
6586
|
+
}
|
|
6587
|
+
const tables = readTables(args.tables);
|
|
6588
|
+
const query2 = readQuery(args.query);
|
|
6589
|
+
const queryArgs = readArguments(args.args);
|
|
6590
|
+
const maxRows = readBoundedInteger(args.maxRows, 1e3, 1, MAX_ROWS, "maxRows");
|
|
6591
|
+
const instance = await DuckDBInstance.create(":memory:");
|
|
6592
|
+
let connection;
|
|
6593
|
+
let timedOut = false;
|
|
6594
|
+
let timeout;
|
|
6595
|
+
try {
|
|
6596
|
+
const activeConnection = await instance.connect();
|
|
6597
|
+
connection = activeConnection;
|
|
6598
|
+
timeout = setTimeout(() => {
|
|
6599
|
+
timedOut = true;
|
|
6600
|
+
activeConnection.interrupt();
|
|
6601
|
+
}, MAX_EXECUTION_MS);
|
|
6602
|
+
await configureConnection(activeConnection);
|
|
6603
|
+
throwIfTimedOut(timedOut);
|
|
6604
|
+
for (const table of tables) {
|
|
6605
|
+
await loadTable(activeConnection, table);
|
|
6606
|
+
throwIfTimedOut(timedOut);
|
|
6607
|
+
}
|
|
6608
|
+
const reader = await activeConnection.runAndReadAll(
|
|
6609
|
+
`SELECT * FROM (${query2}) AS "_tangle_result" LIMIT ${maxRows + 1}`,
|
|
6610
|
+
queryArgs
|
|
6611
|
+
);
|
|
6612
|
+
throwIfTimedOut(timedOut);
|
|
6613
|
+
const allRows = reader.getRowObjectsJson();
|
|
6614
|
+
const truncated = allRows.length > maxRows;
|
|
6615
|
+
const rows = truncated ? allRows.slice(0, maxRows) : allRows;
|
|
6616
|
+
assertResultSize(rows);
|
|
6617
|
+
return {
|
|
6618
|
+
data: {
|
|
6619
|
+
rows,
|
|
6620
|
+
rowCount: rows.length,
|
|
6621
|
+
truncated,
|
|
6622
|
+
columns: reader.columnNameAndTypeObjectsJson()
|
|
6623
|
+
},
|
|
6624
|
+
fetchedAt: Date.now()
|
|
6625
|
+
};
|
|
6626
|
+
} catch (error) {
|
|
6627
|
+
if (timedOut) throw new Error(`DuckDB execution exceeded ${MAX_EXECUTION_MS}ms`);
|
|
6628
|
+
throw error;
|
|
6629
|
+
} finally {
|
|
6630
|
+
if (timeout) clearTimeout(timeout);
|
|
6631
|
+
try {
|
|
6632
|
+
connection?.closeSync();
|
|
6633
|
+
} finally {
|
|
6634
|
+
instance.closeSync();
|
|
6635
|
+
}
|
|
6636
|
+
}
|
|
6637
|
+
},
|
|
6638
|
+
async test() {
|
|
6639
|
+
const instance = await DuckDBInstance.create(":memory:");
|
|
6640
|
+
let connection;
|
|
6641
|
+
try {
|
|
6642
|
+
const activeConnection = await instance.connect();
|
|
6643
|
+
connection = activeConnection;
|
|
6644
|
+
await configureConnection(activeConnection);
|
|
6645
|
+
const reader = await activeConnection.runAndReadAll("SELECT 1 AS ok");
|
|
6646
|
+
if (reader.getRowObjectsJson()[0]?.ok !== 1) return { ok: false, reason: "DuckDB SELECT 1 returned an unexpected result" };
|
|
6647
|
+
return { ok: true };
|
|
6648
|
+
} catch (error) {
|
|
6649
|
+
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
6650
|
+
} finally {
|
|
6651
|
+
try {
|
|
6652
|
+
connection?.closeSync();
|
|
6653
|
+
} finally {
|
|
6654
|
+
instance.closeSync();
|
|
6655
|
+
}
|
|
6656
|
+
}
|
|
6657
|
+
}
|
|
6658
|
+
};
|
|
6659
|
+
async function configureConnection(connection) {
|
|
6660
|
+
await connection.run(`
|
|
6661
|
+
SET memory_limit = '256MB';
|
|
6662
|
+
SET max_temp_directory_size = '0B';
|
|
6663
|
+
SET threads = 2;
|
|
6664
|
+
SET enable_logging = false;
|
|
6665
|
+
SET enable_external_access = false;
|
|
6666
|
+
SET lock_configuration = true;
|
|
6667
|
+
`);
|
|
6668
|
+
}
|
|
6669
|
+
async function loadTable(connection, table) {
|
|
6670
|
+
const sourceData = stringifyJson(table.data, `${table.name}.data`);
|
|
6671
|
+
let sourceSchema;
|
|
6672
|
+
if (table.schema) {
|
|
6673
|
+
assertDuckDbSchema(table.schema, `${table.name}.schema`);
|
|
6674
|
+
sourceSchema = stringifyJson([table.schema], `${table.name}.schema`);
|
|
6675
|
+
} else {
|
|
6676
|
+
if (table.data.length === 0) {
|
|
6677
|
+
throw new Error(`${table.name}.schema is required when data is empty`);
|
|
6678
|
+
}
|
|
6679
|
+
const detected = await connection.runAndReadAll(
|
|
6680
|
+
"SELECT json_structure($sourceData) AS schema",
|
|
6681
|
+
{ sourceData }
|
|
6682
|
+
);
|
|
6683
|
+
const value = detected.getRowsJson()[0]?.[0];
|
|
6684
|
+
if (typeof value !== "string") throw new Error(`Could not infer a schema for ${table.name}`);
|
|
6685
|
+
let detectedSchema;
|
|
6686
|
+
try {
|
|
6687
|
+
detectedSchema = JSON.parse(value);
|
|
6688
|
+
} catch {
|
|
6689
|
+
throw new Error(`Could not parse the inferred schema for ${table.name}`);
|
|
6690
|
+
}
|
|
6691
|
+
assertDuckDbSchema(detectedSchema, `${table.name}.inferredSchema`);
|
|
6692
|
+
sourceSchema = value;
|
|
6693
|
+
}
|
|
6694
|
+
await connection.run(
|
|
6695
|
+
`CREATE TABLE ${quoteIdentifier(table.name)} AS
|
|
6696
|
+
SELECT UNNEST(JSON_TRANSFORM($sourceData, $sourceSchema), recursive := true)`,
|
|
6697
|
+
{ sourceData, sourceSchema }
|
|
6698
|
+
);
|
|
6699
|
+
}
|
|
6700
|
+
function readTables(value) {
|
|
6701
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > MAX_TABLES) {
|
|
6702
|
+
throw new Error(`tables must contain 1 through ${MAX_TABLES} entries`);
|
|
6703
|
+
}
|
|
6704
|
+
assertInputSize(value);
|
|
6705
|
+
let totalRows = 0;
|
|
6706
|
+
const tables = value.map((entry, tableIndex) => {
|
|
6707
|
+
if (!isPlainRecord(entry)) throw new Error(`tables[${tableIndex}] must be an object`);
|
|
6708
|
+
const { name, data: data4, schema } = entry;
|
|
6709
|
+
if (typeof name !== "string" || !TABLE_NAME.test(name)) {
|
|
6710
|
+
throw new Error(`tables[${tableIndex}].name must be a safe SQL identifier`);
|
|
6711
|
+
}
|
|
6712
|
+
if (!Array.isArray(data4) || data4.length > MAX_ROWS) {
|
|
6713
|
+
throw new Error(`tables[${tableIndex}].data must contain at most ${MAX_ROWS} rows`);
|
|
6714
|
+
}
|
|
6715
|
+
const records = data4.map((record, rowIndex) => {
|
|
6716
|
+
if (!isPlainRecord(record)) throw new Error(`tables[${tableIndex}].data[${rowIndex}] must be an object`);
|
|
6717
|
+
if (Object.keys(record).length > MAX_COLUMNS2) {
|
|
6718
|
+
throw new Error(`tables[${tableIndex}].data[${rowIndex}] exceeds ${MAX_COLUMNS2} columns`);
|
|
6719
|
+
}
|
|
6720
|
+
return record;
|
|
6721
|
+
});
|
|
6722
|
+
const columns = new Set(records.flatMap((record) => Object.keys(record)));
|
|
6723
|
+
if (columns.size > MAX_COLUMNS2) {
|
|
6724
|
+
throw new Error(`tables[${tableIndex}].data exceeds ${MAX_COLUMNS2} distinct columns`);
|
|
6725
|
+
}
|
|
6726
|
+
totalRows += records.length;
|
|
6727
|
+
if (totalRows > MAX_ROWS) throw new Error(`tables exceed the ${MAX_ROWS}-row total limit`);
|
|
6728
|
+
if (schema !== void 0 && (!isPlainRecord(schema) || Object.keys(schema).length > MAX_COLUMNS2)) {
|
|
6729
|
+
throw new Error(`tables[${tableIndex}].schema must contain at most ${MAX_COLUMNS2} fields`);
|
|
6730
|
+
}
|
|
6731
|
+
return { name, data: records, schema };
|
|
6732
|
+
});
|
|
6733
|
+
if (new Set(tables.map((table) => table.name.toLowerCase())).size !== tables.length) {
|
|
6734
|
+
throw new Error("table names must be unique ignoring case");
|
|
6735
|
+
}
|
|
6736
|
+
return tables;
|
|
6737
|
+
}
|
|
6738
|
+
function readQuery(value) {
|
|
6739
|
+
if (typeof value !== "string" || value.trim().length === 0) throw new Error("query is required");
|
|
6740
|
+
if (Buffer.byteLength(value, "utf8") > MAX_QUERY_BYTES) {
|
|
6741
|
+
throw new Error(`query exceeds the ${MAX_QUERY_BYTES}-byte limit`);
|
|
6742
|
+
}
|
|
6743
|
+
return value.trim().replace(/;\s*$/, "");
|
|
6744
|
+
}
|
|
6745
|
+
function readArguments(value) {
|
|
6746
|
+
if (value === void 0 || value === null) return {};
|
|
6747
|
+
if (!Array.isArray(value) || value.length > MAX_ARGUMENTS) {
|
|
6748
|
+
throw new Error(`args must contain at most ${MAX_ARGUMENTS} scalar values`);
|
|
6749
|
+
}
|
|
6750
|
+
return Object.fromEntries(value.map((entry, index) => {
|
|
6751
|
+
if (entry !== null && typeof entry !== "string" && typeof entry !== "number" && typeof entry !== "boolean") {
|
|
6752
|
+
throw new Error(`args[${index}] must be a string, number, boolean, or null`);
|
|
6753
|
+
}
|
|
6754
|
+
if (typeof entry === "number" && !Number.isFinite(entry)) throw new Error(`args[${index}] must be finite`);
|
|
6755
|
+
return [String(index + 1), entry];
|
|
6756
|
+
}));
|
|
6757
|
+
}
|
|
6758
|
+
function assertInputSize(value) {
|
|
6759
|
+
const serialized = stringifyJson(value, "tables");
|
|
6760
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_INPUT_BYTES) {
|
|
6761
|
+
throw new Error(`tables exceed the ${MAX_INPUT_BYTES}-byte input limit`);
|
|
6762
|
+
}
|
|
6763
|
+
}
|
|
6764
|
+
function assertResultSize(rows) {
|
|
6765
|
+
const serialized = stringifyJson(rows, "DuckDB result");
|
|
6766
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_RESULT_BYTES) {
|
|
6767
|
+
throw new Error(`DuckDB result exceeds the ${MAX_RESULT_BYTES}-byte output limit`);
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
function assertDuckDbSchema(value, label) {
|
|
6771
|
+
let leaves = 0;
|
|
6772
|
+
const visit = (entry, depth) => {
|
|
6773
|
+
if (depth > MAX_SCHEMA_DEPTH) throw new Error(`${label} exceeds ${MAX_SCHEMA_DEPTH} nested levels`);
|
|
6774
|
+
if (typeof entry === "string") {
|
|
6775
|
+
leaves += 1;
|
|
6776
|
+
if (leaves > MAX_COLUMNS2) throw new Error(`${label} exceeds ${MAX_COLUMNS2} leaf columns`);
|
|
6777
|
+
return;
|
|
6778
|
+
}
|
|
6779
|
+
if (Array.isArray(entry)) {
|
|
6780
|
+
if (entry.length !== 1) throw new Error(`${label} array schemas must contain exactly one element type`);
|
|
6781
|
+
visit(entry[0], depth + 1);
|
|
6782
|
+
return;
|
|
6783
|
+
}
|
|
6784
|
+
if (!isPlainRecord(entry) || Object.keys(entry).length === 0) {
|
|
6785
|
+
throw new Error(`${label} must describe at least one DuckDB column type`);
|
|
6786
|
+
}
|
|
6787
|
+
for (const child of Object.values(entry)) visit(child, depth + 1);
|
|
6788
|
+
};
|
|
6789
|
+
visit(value, 0);
|
|
6790
|
+
}
|
|
6791
|
+
function stringifyJson(value, label) {
|
|
6792
|
+
try {
|
|
6793
|
+
const serialized = JSON.stringify(value);
|
|
6794
|
+
if (serialized === void 0) throw new Error("undefined");
|
|
6795
|
+
return serialized;
|
|
6796
|
+
} catch {
|
|
6797
|
+
throw new Error(`${label} must be JSON-serializable`);
|
|
6798
|
+
}
|
|
6799
|
+
}
|
|
6800
|
+
function quoteIdentifier(value) {
|
|
6801
|
+
return `"${value.replaceAll('"', '""')}"`;
|
|
6802
|
+
}
|
|
6803
|
+
function throwIfTimedOut(timedOut) {
|
|
6804
|
+
if (timedOut) throw new Error(`DuckDB execution exceeded ${MAX_EXECUTION_MS}ms`);
|
|
6805
|
+
}
|
|
6806
|
+
|
|
6503
6807
|
// src/connectors/adapters/docsbot.ts
|
|
6504
6808
|
var docsbotConnector = declarativeRestConnector({
|
|
6505
6809
|
kind: "docsbot",
|
|
@@ -6917,7 +7221,7 @@ var elevenlabsConnector = declarativeRestConnector({
|
|
|
6917
7221
|
// src/connectors/adapters/excel-files.ts
|
|
6918
7222
|
import readXlsxFile from "read-excel-file/node";
|
|
6919
7223
|
import writeXlsxFile from "write-excel-file/node";
|
|
6920
|
-
var
|
|
7224
|
+
var MAX_COLUMNS3 = 256;
|
|
6921
7225
|
var MAX_SHEETS = 50;
|
|
6922
7226
|
var MAX_UNCOMPRESSED_WORKBOOK_BYTES = 50 * 1024 * 1024;
|
|
6923
7227
|
var MAX_ZIP_ENTRIES = 1e3;
|
|
@@ -6958,7 +7262,7 @@ var excelFilesConnector = {
|
|
|
6958
7262
|
type: "array",
|
|
6959
7263
|
minItems: 1,
|
|
6960
7264
|
maxItems: MAX_RECORDS,
|
|
6961
|
-
items: { type: "array", maxItems:
|
|
7265
|
+
items: { type: "array", maxItems: MAX_COLUMNS3 }
|
|
6962
7266
|
},
|
|
6963
7267
|
sheetName: { type: "string", minLength: 1, maxLength: 31, default: "Sheet1" }
|
|
6964
7268
|
},
|
|
@@ -6988,8 +7292,8 @@ var excelFilesConnector = {
|
|
|
6988
7292
|
totalRows += sheet.data.length;
|
|
6989
7293
|
if (totalRows > MAX_RECORDS) throw new Error(`workbook exceeds the ${MAX_RECORDS}-row limit`);
|
|
6990
7294
|
for (const [rowIndex, row] of sheet.data.entries()) {
|
|
6991
|
-
if (row.length >
|
|
6992
|
-
throw new Error(`sheet ${sheet.sheet} row ${rowIndex + 1} exceeds the ${
|
|
7295
|
+
if (row.length > MAX_COLUMNS3) {
|
|
7296
|
+
throw new Error(`sheet ${sheet.sheet} row ${rowIndex + 1} exceeds the ${MAX_COLUMNS3}-column limit`);
|
|
6993
7297
|
}
|
|
6994
7298
|
}
|
|
6995
7299
|
return { name: sheet.sheet, rows: jsonSafe(sheet.data) };
|
|
@@ -7003,7 +7307,7 @@ var excelFilesConnector = {
|
|
|
7003
7307
|
if (capabilityName !== "excel.create") throw new Error(`Unknown Excel capability: ${capabilityName}`);
|
|
7004
7308
|
const rows = readRows(args.rows);
|
|
7005
7309
|
for (const [index, row] of rows.entries()) {
|
|
7006
|
-
if (row.length >
|
|
7310
|
+
if (row.length > MAX_COLUMNS3) throw new Error(`rows[${index}] exceeds the ${MAX_COLUMNS3}-column limit`);
|
|
7007
7311
|
}
|
|
7008
7312
|
const sheetName = readOptionalString(args.sheetName, "sheetName") ?? "Sheet1";
|
|
7009
7313
|
validateSheetName(sheetName);
|
|
@@ -16301,16 +16605,27 @@ var clickupConnector = declarativeRestConnector({
|
|
|
16301
16605
|
displayName: "ClickUp",
|
|
16302
16606
|
description: "Read and mutate ClickUp workspaces, spaces, folders, lists, tasks, comments, and time entries via the ClickUp REST v2 API.",
|
|
16303
16607
|
auth: {
|
|
16304
|
-
kind: "
|
|
16305
|
-
|
|
16306
|
-
|
|
16307
|
-
|
|
16308
|
-
|
|
16309
|
-
|
|
16608
|
+
kind: "one_of",
|
|
16609
|
+
preferred: "api-key",
|
|
16610
|
+
options: [
|
|
16611
|
+
{
|
|
16612
|
+
kind: "api-key",
|
|
16613
|
+
hint: "ClickUp personal API token (starts with pk_)."
|
|
16614
|
+
},
|
|
16615
|
+
{
|
|
16616
|
+
kind: "oauth2",
|
|
16617
|
+
authorizationUrl: "https://app.clickup.com/api",
|
|
16618
|
+
tokenUrl: "https://api.clickup.com/api/v2/oauth/token",
|
|
16619
|
+
scopes: [],
|
|
16620
|
+
clientIdEnv: "CLICKUP_OAUTH_CLIENT_ID",
|
|
16621
|
+
clientSecretEnv: "CLICKUP_OAUTH_CLIENT_SECRET"
|
|
16622
|
+
}
|
|
16623
|
+
]
|
|
16310
16624
|
},
|
|
16311
16625
|
category: "other",
|
|
16312
16626
|
defaultConsistencyModel: "authoritative",
|
|
16313
16627
|
baseUrl: "https://api.clickup.com/api/v2",
|
|
16628
|
+
credentialPlacement: { kind: "authorization-by-auth" },
|
|
16314
16629
|
// GET /user — returns the authorized user; used as a cheap liveness probe.
|
|
16315
16630
|
test: { method: "GET", path: "/user" },
|
|
16316
16631
|
capabilities: [
|
|
@@ -38895,6 +39210,628 @@ var mollieConnector = declarativeRestConnector({
|
|
|
38895
39210
|
]
|
|
38896
39211
|
});
|
|
38897
39212
|
|
|
39213
|
+
// src/connectors/adapters/miro.ts
|
|
39214
|
+
var miroConnector = declarativeRestConnector({
|
|
39215
|
+
kind: "miro",
|
|
39216
|
+
displayName: "Miro",
|
|
39217
|
+
description: "Read and edit Miro boards: list and search boards, read board items (sticky notes, shapes, text, cards, images, frames, connectors), create and update items, and post board content back into the source of truth.",
|
|
39218
|
+
auth: {
|
|
39219
|
+
kind: "oauth2",
|
|
39220
|
+
authorizationUrl: "https://miro.com/oauth/authorize",
|
|
39221
|
+
tokenUrl: "https://api.miro.com/v1/oauth/token",
|
|
39222
|
+
scopes: [
|
|
39223
|
+
"boards:read",
|
|
39224
|
+
"boards:write",
|
|
39225
|
+
"identity:read"
|
|
39226
|
+
],
|
|
39227
|
+
clientIdEnv: "MIRO_OAUTH_CLIENT_ID",
|
|
39228
|
+
clientSecretEnv: "MIRO_OAUTH_CLIENT_SECRET"
|
|
39229
|
+
},
|
|
39230
|
+
category: "doc",
|
|
39231
|
+
defaultConsistencyModel: "authoritative",
|
|
39232
|
+
baseUrl: "https://api.miro.com",
|
|
39233
|
+
defaultHeaders: { accept: "application/json" },
|
|
39234
|
+
// GET /v2/me returns the authenticated user — Miro's documented health probe.
|
|
39235
|
+
test: { method: "GET", path: "/v2/me" },
|
|
39236
|
+
capabilities: [
|
|
39237
|
+
{
|
|
39238
|
+
name: "me.get",
|
|
39239
|
+
class: "read",
|
|
39240
|
+
description: "Return the authenticated Miro user.",
|
|
39241
|
+
parameters: { type: "object", properties: {} },
|
|
39242
|
+
request: { method: "GET", path: "/v2/me" },
|
|
39243
|
+
requiredScopes: ["identity:read"]
|
|
39244
|
+
},
|
|
39245
|
+
{
|
|
39246
|
+
name: "boards.list",
|
|
39247
|
+
class: "read",
|
|
39248
|
+
description: "List boards accessible to the authenticated user. Supports cursor pagination via `cursor` and team / sort filters.",
|
|
39249
|
+
parameters: {
|
|
39250
|
+
type: "object",
|
|
39251
|
+
properties: {
|
|
39252
|
+
team_id: { type: "string", description: "Filter to a single team id." },
|
|
39253
|
+
query: { type: "string", description: "Free-text board name filter." },
|
|
39254
|
+
owner: { type: "string", description: "Filter by owner user id." },
|
|
39255
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
39256
|
+
offset: { type: "integer", minimum: 0 },
|
|
39257
|
+
sort: {
|
|
39258
|
+
type: "string",
|
|
39259
|
+
enum: ["default", "last_modified", "last_opened", "last_created", "alphabetically"]
|
|
39260
|
+
}
|
|
39261
|
+
}
|
|
39262
|
+
},
|
|
39263
|
+
request: {
|
|
39264
|
+
method: "GET",
|
|
39265
|
+
path: "/v2/boards",
|
|
39266
|
+
query: {
|
|
39267
|
+
team_id: "{team_id}",
|
|
39268
|
+
query: "{query}",
|
|
39269
|
+
owner: "{owner}",
|
|
39270
|
+
limit: "{limit}",
|
|
39271
|
+
offset: "{offset}",
|
|
39272
|
+
sort: "{sort}"
|
|
39273
|
+
}
|
|
39274
|
+
},
|
|
39275
|
+
requiredScopes: ["boards:read"]
|
|
39276
|
+
},
|
|
39277
|
+
{
|
|
39278
|
+
name: "boards.get",
|
|
39279
|
+
class: "read",
|
|
39280
|
+
description: "Fetch a single Miro board by id.",
|
|
39281
|
+
parameters: {
|
|
39282
|
+
type: "object",
|
|
39283
|
+
properties: { board_id: { type: "string" } },
|
|
39284
|
+
required: ["board_id"]
|
|
39285
|
+
},
|
|
39286
|
+
request: { method: "GET", path: "/v2/boards/{board_id}" },
|
|
39287
|
+
requiredScopes: ["boards:read"]
|
|
39288
|
+
},
|
|
39289
|
+
{
|
|
39290
|
+
name: "boards.members.list",
|
|
39291
|
+
class: "read",
|
|
39292
|
+
description: "List members (collaborators) of a Miro board.",
|
|
39293
|
+
parameters: {
|
|
39294
|
+
type: "object",
|
|
39295
|
+
properties: {
|
|
39296
|
+
board_id: { type: "string" },
|
|
39297
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
39298
|
+
offset: { type: "integer", minimum: 0 }
|
|
39299
|
+
},
|
|
39300
|
+
required: ["board_id"]
|
|
39301
|
+
},
|
|
39302
|
+
request: {
|
|
39303
|
+
method: "GET",
|
|
39304
|
+
path: "/v2/boards/{board_id}/members",
|
|
39305
|
+
query: { limit: "{limit}", offset: "{offset}" }
|
|
39306
|
+
},
|
|
39307
|
+
requiredScopes: ["boards:read"]
|
|
39308
|
+
},
|
|
39309
|
+
{
|
|
39310
|
+
name: "items.list",
|
|
39311
|
+
class: "read",
|
|
39312
|
+
description: "List all items on a board, optionally filtered by item type (sticky_note, text, shape, card, image, frame, connector, embed, app_card, document).",
|
|
39313
|
+
parameters: {
|
|
39314
|
+
type: "object",
|
|
39315
|
+
properties: {
|
|
39316
|
+
board_id: { type: "string" },
|
|
39317
|
+
type: {
|
|
39318
|
+
type: "string",
|
|
39319
|
+
enum: [
|
|
39320
|
+
"sticky_note",
|
|
39321
|
+
"text",
|
|
39322
|
+
"shape",
|
|
39323
|
+
"card",
|
|
39324
|
+
"image",
|
|
39325
|
+
"frame",
|
|
39326
|
+
"connector",
|
|
39327
|
+
"embed",
|
|
39328
|
+
"app_card",
|
|
39329
|
+
"document",
|
|
39330
|
+
"preview"
|
|
39331
|
+
]
|
|
39332
|
+
},
|
|
39333
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
39334
|
+
cursor: { type: "string" }
|
|
39335
|
+
},
|
|
39336
|
+
required: ["board_id"]
|
|
39337
|
+
},
|
|
39338
|
+
request: {
|
|
39339
|
+
method: "GET",
|
|
39340
|
+
path: "/v2/boards/{board_id}/items",
|
|
39341
|
+
query: { type: "{type}", limit: "{limit}", cursor: "{cursor}" }
|
|
39342
|
+
},
|
|
39343
|
+
requiredScopes: ["boards:read"]
|
|
39344
|
+
},
|
|
39345
|
+
{
|
|
39346
|
+
name: "items.get",
|
|
39347
|
+
class: "read",
|
|
39348
|
+
description: "Fetch a single board item by id.",
|
|
39349
|
+
parameters: {
|
|
39350
|
+
type: "object",
|
|
39351
|
+
properties: {
|
|
39352
|
+
board_id: { type: "string" },
|
|
39353
|
+
item_id: { type: "string" }
|
|
39354
|
+
},
|
|
39355
|
+
required: ["board_id", "item_id"]
|
|
39356
|
+
},
|
|
39357
|
+
request: { method: "GET", path: "/v2/boards/{board_id}/items/{item_id}" },
|
|
39358
|
+
requiredScopes: ["boards:read"]
|
|
39359
|
+
},
|
|
39360
|
+
{
|
|
39361
|
+
name: "connectors.list",
|
|
39362
|
+
class: "read",
|
|
39363
|
+
description: "List connector lines on a board (arrows / lines between items).",
|
|
39364
|
+
parameters: {
|
|
39365
|
+
type: "object",
|
|
39366
|
+
properties: {
|
|
39367
|
+
board_id: { type: "string" },
|
|
39368
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
39369
|
+
cursor: { type: "string" }
|
|
39370
|
+
},
|
|
39371
|
+
required: ["board_id"]
|
|
39372
|
+
},
|
|
39373
|
+
request: {
|
|
39374
|
+
method: "GET",
|
|
39375
|
+
path: "/v2/boards/{board_id}/connectors",
|
|
39376
|
+
query: { limit: "{limit}", cursor: "{cursor}" }
|
|
39377
|
+
},
|
|
39378
|
+
requiredScopes: ["boards:read"]
|
|
39379
|
+
},
|
|
39380
|
+
{
|
|
39381
|
+
name: "tags.list",
|
|
39382
|
+
class: "read",
|
|
39383
|
+
description: "List tags defined on a board.",
|
|
39384
|
+
parameters: {
|
|
39385
|
+
type: "object",
|
|
39386
|
+
properties: {
|
|
39387
|
+
board_id: { type: "string" },
|
|
39388
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
39389
|
+
offset: { type: "integer", minimum: 0 }
|
|
39390
|
+
},
|
|
39391
|
+
required: ["board_id"]
|
|
39392
|
+
},
|
|
39393
|
+
request: {
|
|
39394
|
+
method: "GET",
|
|
39395
|
+
path: "/v2/boards/{board_id}/tags",
|
|
39396
|
+
query: { limit: "{limit}", offset: "{offset}" }
|
|
39397
|
+
},
|
|
39398
|
+
requiredScopes: ["boards:read"]
|
|
39399
|
+
},
|
|
39400
|
+
{
|
|
39401
|
+
name: "boards.create",
|
|
39402
|
+
class: "mutation",
|
|
39403
|
+
description: "Create a new Miro board. Accepts an optional name, description, team_id, and sharing policy. Returns the new board id used by every items.* endpoint.",
|
|
39404
|
+
parameters: {
|
|
39405
|
+
type: "object",
|
|
39406
|
+
properties: {
|
|
39407
|
+
name: { type: "string", maxLength: 60 },
|
|
39408
|
+
description: { type: "string", maxLength: 300 },
|
|
39409
|
+
team_id: { type: "string" },
|
|
39410
|
+
policy: {
|
|
39411
|
+
type: "object",
|
|
39412
|
+
description: "Permissions + sharing policy. See https://developers.miro.com/reference/create-board for the full PolicyChange shape."
|
|
39413
|
+
}
|
|
39414
|
+
},
|
|
39415
|
+
required: ["name"]
|
|
39416
|
+
},
|
|
39417
|
+
request: {
|
|
39418
|
+
method: "POST",
|
|
39419
|
+
path: "/v2/boards",
|
|
39420
|
+
body: {
|
|
39421
|
+
name: "{name}",
|
|
39422
|
+
description: "{description}",
|
|
39423
|
+
teamId: "{team_id}",
|
|
39424
|
+
policy: "{policy}"
|
|
39425
|
+
}
|
|
39426
|
+
},
|
|
39427
|
+
cas: "native-idempotency",
|
|
39428
|
+
requiredScopes: ["boards:write"]
|
|
39429
|
+
},
|
|
39430
|
+
{
|
|
39431
|
+
name: "boards.update",
|
|
39432
|
+
class: "mutation",
|
|
39433
|
+
description: "Update a Miro board (name, description, team, or sharing policy).",
|
|
39434
|
+
parameters: {
|
|
39435
|
+
type: "object",
|
|
39436
|
+
properties: {
|
|
39437
|
+
board_id: { type: "string" },
|
|
39438
|
+
name: { type: "string", maxLength: 60 },
|
|
39439
|
+
description: { type: "string", maxLength: 300 },
|
|
39440
|
+
team_id: { type: "string" },
|
|
39441
|
+
policy: { type: "object" }
|
|
39442
|
+
},
|
|
39443
|
+
required: ["board_id"]
|
|
39444
|
+
},
|
|
39445
|
+
request: {
|
|
39446
|
+
method: "PATCH",
|
|
39447
|
+
path: "/v2/boards/{board_id}",
|
|
39448
|
+
body: {
|
|
39449
|
+
name: "{name}",
|
|
39450
|
+
description: "{description}",
|
|
39451
|
+
teamId: "{team_id}",
|
|
39452
|
+
policy: "{policy}"
|
|
39453
|
+
}
|
|
39454
|
+
},
|
|
39455
|
+
cas: "native-idempotency",
|
|
39456
|
+
requiredScopes: ["boards:write"]
|
|
39457
|
+
},
|
|
39458
|
+
{
|
|
39459
|
+
name: "boards.delete",
|
|
39460
|
+
class: "mutation",
|
|
39461
|
+
description: "Delete a Miro board (irreversible).",
|
|
39462
|
+
parameters: {
|
|
39463
|
+
type: "object",
|
|
39464
|
+
properties: { board_id: { type: "string" } },
|
|
39465
|
+
required: ["board_id"]
|
|
39466
|
+
},
|
|
39467
|
+
request: { method: "DELETE", path: "/v2/boards/{board_id}" },
|
|
39468
|
+
cas: "native-idempotency",
|
|
39469
|
+
requiredScopes: ["boards:write"]
|
|
39470
|
+
},
|
|
39471
|
+
{
|
|
39472
|
+
name: "sticky_notes.create",
|
|
39473
|
+
class: "mutation",
|
|
39474
|
+
description: "Create a sticky note on a board. `data.content` accepts plain text; `style.fillColor` is a Miro sticky-note color keyword (e.g. yellow, light_yellow, pink).",
|
|
39475
|
+
parameters: {
|
|
39476
|
+
type: "object",
|
|
39477
|
+
properties: {
|
|
39478
|
+
board_id: { type: "string" },
|
|
39479
|
+
data: {
|
|
39480
|
+
type: "object",
|
|
39481
|
+
properties: {
|
|
39482
|
+
content: { type: "string" },
|
|
39483
|
+
shape: { type: "string", enum: ["square", "rectangle"] }
|
|
39484
|
+
},
|
|
39485
|
+
required: ["content"]
|
|
39486
|
+
},
|
|
39487
|
+
style: {
|
|
39488
|
+
type: "object",
|
|
39489
|
+
properties: {
|
|
39490
|
+
fillColor: { type: "string" },
|
|
39491
|
+
textAlign: { type: "string", enum: ["left", "center", "right"] },
|
|
39492
|
+
textAlignVertical: { type: "string", enum: ["top", "middle", "bottom"] }
|
|
39493
|
+
}
|
|
39494
|
+
},
|
|
39495
|
+
position: {
|
|
39496
|
+
type: "object",
|
|
39497
|
+
properties: {
|
|
39498
|
+
x: { type: "number" },
|
|
39499
|
+
y: { type: "number" },
|
|
39500
|
+
origin: { type: "string", enum: ["center"] }
|
|
39501
|
+
}
|
|
39502
|
+
},
|
|
39503
|
+
geometry: {
|
|
39504
|
+
type: "object",
|
|
39505
|
+
properties: {
|
|
39506
|
+
width: { type: "number" },
|
|
39507
|
+
height: { type: "number" }
|
|
39508
|
+
}
|
|
39509
|
+
},
|
|
39510
|
+
parent: {
|
|
39511
|
+
type: "object",
|
|
39512
|
+
description: "Attach the sticky note to a frame: { id: <frame_item_id> }.",
|
|
39513
|
+
properties: { id: { type: "string" } }
|
|
39514
|
+
}
|
|
39515
|
+
},
|
|
39516
|
+
required: ["board_id", "data"]
|
|
39517
|
+
},
|
|
39518
|
+
request: {
|
|
39519
|
+
method: "POST",
|
|
39520
|
+
path: "/v2/boards/{board_id}/sticky_notes",
|
|
39521
|
+
body: {
|
|
39522
|
+
data: "{data}",
|
|
39523
|
+
style: "{style}",
|
|
39524
|
+
position: "{position}",
|
|
39525
|
+
geometry: "{geometry}",
|
|
39526
|
+
parent: "{parent}"
|
|
39527
|
+
}
|
|
39528
|
+
},
|
|
39529
|
+
cas: "native-idempotency",
|
|
39530
|
+
requiredScopes: ["boards:write"]
|
|
39531
|
+
},
|
|
39532
|
+
{
|
|
39533
|
+
name: "sticky_notes.update",
|
|
39534
|
+
class: "mutation",
|
|
39535
|
+
description: "Update a sticky note (content, style, position, geometry, or parent frame).",
|
|
39536
|
+
parameters: {
|
|
39537
|
+
type: "object",
|
|
39538
|
+
properties: {
|
|
39539
|
+
board_id: { type: "string" },
|
|
39540
|
+
item_id: { type: "string" },
|
|
39541
|
+
data: { type: "object" },
|
|
39542
|
+
style: { type: "object" },
|
|
39543
|
+
position: { type: "object" },
|
|
39544
|
+
geometry: { type: "object" },
|
|
39545
|
+
parent: { type: "object" }
|
|
39546
|
+
},
|
|
39547
|
+
required: ["board_id", "item_id"]
|
|
39548
|
+
},
|
|
39549
|
+
request: {
|
|
39550
|
+
method: "PATCH",
|
|
39551
|
+
path: "/v2/boards/{board_id}/sticky_notes/{item_id}",
|
|
39552
|
+
body: {
|
|
39553
|
+
data: "{data}",
|
|
39554
|
+
style: "{style}",
|
|
39555
|
+
position: "{position}",
|
|
39556
|
+
geometry: "{geometry}",
|
|
39557
|
+
parent: "{parent}"
|
|
39558
|
+
}
|
|
39559
|
+
},
|
|
39560
|
+
cas: "native-idempotency",
|
|
39561
|
+
requiredScopes: ["boards:write"]
|
|
39562
|
+
},
|
|
39563
|
+
{
|
|
39564
|
+
name: "text.create",
|
|
39565
|
+
class: "mutation",
|
|
39566
|
+
description: "Create a text item on a board.",
|
|
39567
|
+
parameters: {
|
|
39568
|
+
type: "object",
|
|
39569
|
+
properties: {
|
|
39570
|
+
board_id: { type: "string" },
|
|
39571
|
+
data: {
|
|
39572
|
+
type: "object",
|
|
39573
|
+
properties: { content: { type: "string" } },
|
|
39574
|
+
required: ["content"]
|
|
39575
|
+
},
|
|
39576
|
+
style: { type: "object" },
|
|
39577
|
+
position: { type: "object" },
|
|
39578
|
+
geometry: { type: "object" },
|
|
39579
|
+
parent: { type: "object" }
|
|
39580
|
+
},
|
|
39581
|
+
required: ["board_id", "data"]
|
|
39582
|
+
},
|
|
39583
|
+
request: {
|
|
39584
|
+
method: "POST",
|
|
39585
|
+
path: "/v2/boards/{board_id}/texts",
|
|
39586
|
+
body: {
|
|
39587
|
+
data: "{data}",
|
|
39588
|
+
style: "{style}",
|
|
39589
|
+
position: "{position}",
|
|
39590
|
+
geometry: "{geometry}",
|
|
39591
|
+
parent: "{parent}"
|
|
39592
|
+
}
|
|
39593
|
+
},
|
|
39594
|
+
cas: "native-idempotency",
|
|
39595
|
+
requiredScopes: ["boards:write"]
|
|
39596
|
+
},
|
|
39597
|
+
{
|
|
39598
|
+
name: "shapes.create",
|
|
39599
|
+
class: "mutation",
|
|
39600
|
+
description: "Create a shape item on a board. `data.shape` selects the geometric primitive (rectangle, round_rectangle, circle, triangle, rhombus, etc.).",
|
|
39601
|
+
parameters: {
|
|
39602
|
+
type: "object",
|
|
39603
|
+
properties: {
|
|
39604
|
+
board_id: { type: "string" },
|
|
39605
|
+
data: {
|
|
39606
|
+
type: "object",
|
|
39607
|
+
properties: {
|
|
39608
|
+
shape: {
|
|
39609
|
+
type: "string",
|
|
39610
|
+
description: "Miro shape kind; full enum at https://developers.miro.com/reference/create-shape-item."
|
|
39611
|
+
},
|
|
39612
|
+
content: { type: "string" }
|
|
39613
|
+
},
|
|
39614
|
+
required: ["shape"]
|
|
39615
|
+
},
|
|
39616
|
+
style: { type: "object" },
|
|
39617
|
+
position: { type: "object" },
|
|
39618
|
+
geometry: { type: "object" },
|
|
39619
|
+
parent: { type: "object" }
|
|
39620
|
+
},
|
|
39621
|
+
required: ["board_id", "data"]
|
|
39622
|
+
},
|
|
39623
|
+
request: {
|
|
39624
|
+
method: "POST",
|
|
39625
|
+
path: "/v2/boards/{board_id}/shapes",
|
|
39626
|
+
body: {
|
|
39627
|
+
data: "{data}",
|
|
39628
|
+
style: "{style}",
|
|
39629
|
+
position: "{position}",
|
|
39630
|
+
geometry: "{geometry}",
|
|
39631
|
+
parent: "{parent}"
|
|
39632
|
+
}
|
|
39633
|
+
},
|
|
39634
|
+
cas: "native-idempotency",
|
|
39635
|
+
requiredScopes: ["boards:write"]
|
|
39636
|
+
},
|
|
39637
|
+
{
|
|
39638
|
+
name: "cards.create",
|
|
39639
|
+
class: "mutation",
|
|
39640
|
+
description: "Create a card item (title + description + assignee + due-date) on a board.",
|
|
39641
|
+
parameters: {
|
|
39642
|
+
type: "object",
|
|
39643
|
+
properties: {
|
|
39644
|
+
board_id: { type: "string" },
|
|
39645
|
+
data: {
|
|
39646
|
+
type: "object",
|
|
39647
|
+
properties: {
|
|
39648
|
+
title: { type: "string" },
|
|
39649
|
+
description: { type: "string" },
|
|
39650
|
+
dueDate: { type: "string", format: "date-time" },
|
|
39651
|
+
assigneeId: { type: "string" }
|
|
39652
|
+
},
|
|
39653
|
+
required: ["title"]
|
|
39654
|
+
},
|
|
39655
|
+
style: { type: "object" },
|
|
39656
|
+
position: { type: "object" },
|
|
39657
|
+
geometry: { type: "object" },
|
|
39658
|
+
parent: { type: "object" }
|
|
39659
|
+
},
|
|
39660
|
+
required: ["board_id", "data"]
|
|
39661
|
+
},
|
|
39662
|
+
request: {
|
|
39663
|
+
method: "POST",
|
|
39664
|
+
path: "/v2/boards/{board_id}/cards",
|
|
39665
|
+
body: {
|
|
39666
|
+
data: "{data}",
|
|
39667
|
+
style: "{style}",
|
|
39668
|
+
position: "{position}",
|
|
39669
|
+
geometry: "{geometry}",
|
|
39670
|
+
parent: "{parent}"
|
|
39671
|
+
}
|
|
39672
|
+
},
|
|
39673
|
+
cas: "native-idempotency",
|
|
39674
|
+
requiredScopes: ["boards:write"]
|
|
39675
|
+
},
|
|
39676
|
+
{
|
|
39677
|
+
name: "frames.create",
|
|
39678
|
+
class: "mutation",
|
|
39679
|
+
description: "Create a frame on a board. Frames group items; child items can be attached via `parent.id` on item creation.",
|
|
39680
|
+
parameters: {
|
|
39681
|
+
type: "object",
|
|
39682
|
+
properties: {
|
|
39683
|
+
board_id: { type: "string" },
|
|
39684
|
+
data: {
|
|
39685
|
+
type: "object",
|
|
39686
|
+
properties: {
|
|
39687
|
+
title: { type: "string" },
|
|
39688
|
+
format: { type: "string", enum: ["custom"] },
|
|
39689
|
+
type: { type: "string", enum: ["freeform"] }
|
|
39690
|
+
}
|
|
39691
|
+
},
|
|
39692
|
+
style: { type: "object" },
|
|
39693
|
+
position: { type: "object" },
|
|
39694
|
+
geometry: { type: "object" }
|
|
39695
|
+
},
|
|
39696
|
+
required: ["board_id"]
|
|
39697
|
+
},
|
|
39698
|
+
request: {
|
|
39699
|
+
method: "POST",
|
|
39700
|
+
path: "/v2/boards/{board_id}/frames",
|
|
39701
|
+
body: {
|
|
39702
|
+
data: "{data}",
|
|
39703
|
+
style: "{style}",
|
|
39704
|
+
position: "{position}",
|
|
39705
|
+
geometry: "{geometry}"
|
|
39706
|
+
}
|
|
39707
|
+
},
|
|
39708
|
+
cas: "native-idempotency",
|
|
39709
|
+
requiredScopes: ["boards:write"]
|
|
39710
|
+
},
|
|
39711
|
+
{
|
|
39712
|
+
name: "images.create.by_url",
|
|
39713
|
+
class: "mutation",
|
|
39714
|
+
description: "Create an image item from a public URL (Miro fetches the image server-side).",
|
|
39715
|
+
parameters: {
|
|
39716
|
+
type: "object",
|
|
39717
|
+
properties: {
|
|
39718
|
+
board_id: { type: "string" },
|
|
39719
|
+
data: {
|
|
39720
|
+
type: "object",
|
|
39721
|
+
properties: {
|
|
39722
|
+
url: { type: "string", format: "uri" },
|
|
39723
|
+
title: { type: "string" }
|
|
39724
|
+
},
|
|
39725
|
+
required: ["url"]
|
|
39726
|
+
},
|
|
39727
|
+
position: { type: "object" },
|
|
39728
|
+
geometry: { type: "object" },
|
|
39729
|
+
parent: { type: "object" }
|
|
39730
|
+
},
|
|
39731
|
+
required: ["board_id", "data"]
|
|
39732
|
+
},
|
|
39733
|
+
request: {
|
|
39734
|
+
method: "POST",
|
|
39735
|
+
path: "/v2/boards/{board_id}/images",
|
|
39736
|
+
body: {
|
|
39737
|
+
data: "{data}",
|
|
39738
|
+
position: "{position}",
|
|
39739
|
+
geometry: "{geometry}",
|
|
39740
|
+
parent: "{parent}"
|
|
39741
|
+
}
|
|
39742
|
+
},
|
|
39743
|
+
cas: "native-idempotency",
|
|
39744
|
+
requiredScopes: ["boards:write"]
|
|
39745
|
+
},
|
|
39746
|
+
{
|
|
39747
|
+
name: "connectors.create",
|
|
39748
|
+
class: "mutation",
|
|
39749
|
+
description: "Create a connector line between two items on the board. `startItem.id` and `endItem.id` reference existing item ids.",
|
|
39750
|
+
parameters: {
|
|
39751
|
+
type: "object",
|
|
39752
|
+
properties: {
|
|
39753
|
+
board_id: { type: "string" },
|
|
39754
|
+
startItem: {
|
|
39755
|
+
type: "object",
|
|
39756
|
+
properties: { id: { type: "string" } },
|
|
39757
|
+
required: ["id"]
|
|
39758
|
+
},
|
|
39759
|
+
endItem: {
|
|
39760
|
+
type: "object",
|
|
39761
|
+
properties: { id: { type: "string" } },
|
|
39762
|
+
required: ["id"]
|
|
39763
|
+
},
|
|
39764
|
+
captions: {
|
|
39765
|
+
type: "array",
|
|
39766
|
+
items: { type: "object", properties: { content: { type: "string" } } }
|
|
39767
|
+
},
|
|
39768
|
+
style: { type: "object" },
|
|
39769
|
+
shape: { type: "string", enum: ["straight", "elbowed", "curved"] }
|
|
39770
|
+
},
|
|
39771
|
+
required: ["board_id", "startItem", "endItem"]
|
|
39772
|
+
},
|
|
39773
|
+
request: {
|
|
39774
|
+
method: "POST",
|
|
39775
|
+
path: "/v2/boards/{board_id}/connectors",
|
|
39776
|
+
body: {
|
|
39777
|
+
startItem: "{startItem}",
|
|
39778
|
+
endItem: "{endItem}",
|
|
39779
|
+
captions: "{captions}",
|
|
39780
|
+
style: "{style}",
|
|
39781
|
+
shape: "{shape}"
|
|
39782
|
+
}
|
|
39783
|
+
},
|
|
39784
|
+
cas: "native-idempotency",
|
|
39785
|
+
requiredScopes: ["boards:write"]
|
|
39786
|
+
},
|
|
39787
|
+
{
|
|
39788
|
+
name: "items.delete",
|
|
39789
|
+
class: "mutation",
|
|
39790
|
+
description: "Delete an item from a board (works across all item types).",
|
|
39791
|
+
parameters: {
|
|
39792
|
+
type: "object",
|
|
39793
|
+
properties: {
|
|
39794
|
+
board_id: { type: "string" },
|
|
39795
|
+
item_id: { type: "string" }
|
|
39796
|
+
},
|
|
39797
|
+
required: ["board_id", "item_id"]
|
|
39798
|
+
},
|
|
39799
|
+
request: { method: "DELETE", path: "/v2/boards/{board_id}/items/{item_id}" },
|
|
39800
|
+
cas: "native-idempotency",
|
|
39801
|
+
requiredScopes: ["boards:write"]
|
|
39802
|
+
},
|
|
39803
|
+
{
|
|
39804
|
+
name: "boards.share",
|
|
39805
|
+
class: "mutation",
|
|
39806
|
+
description: "Invite users to a Miro board by email. Roles: viewer, commenter, editor, coowner.",
|
|
39807
|
+
parameters: {
|
|
39808
|
+
type: "object",
|
|
39809
|
+
properties: {
|
|
39810
|
+
board_id: { type: "string" },
|
|
39811
|
+
emails: { type: "array", items: { type: "string", format: "email" } },
|
|
39812
|
+
role: {
|
|
39813
|
+
type: "string",
|
|
39814
|
+
enum: ["viewer", "commenter", "editor", "coowner"]
|
|
39815
|
+
},
|
|
39816
|
+
message: { type: "string" }
|
|
39817
|
+
},
|
|
39818
|
+
required: ["board_id", "emails", "role"]
|
|
39819
|
+
},
|
|
39820
|
+
request: {
|
|
39821
|
+
method: "POST",
|
|
39822
|
+
path: "/v2/boards/{board_id}/members",
|
|
39823
|
+
body: {
|
|
39824
|
+
emails: "{emails}",
|
|
39825
|
+
role: "{role}",
|
|
39826
|
+
message: "{message}"
|
|
39827
|
+
}
|
|
39828
|
+
},
|
|
39829
|
+
cas: "native-idempotency",
|
|
39830
|
+
requiredScopes: ["boards:write"]
|
|
39831
|
+
}
|
|
39832
|
+
]
|
|
39833
|
+
});
|
|
39834
|
+
|
|
38898
39835
|
// src/connectors/adapters/monday.ts
|
|
38899
39836
|
var BOARD_FRAGMENT = `
|
|
38900
39837
|
id
|
|
@@ -39019,7 +39956,7 @@ var mondayConnector = declarativeRestConnector({
|
|
|
39019
39956
|
auth: {
|
|
39020
39957
|
kind: "oauth2",
|
|
39021
39958
|
authorizationUrl: "https://auth.monday.com/oauth2/authorize",
|
|
39022
|
-
tokenUrl: "https://auth.monday.com/
|
|
39959
|
+
tokenUrl: "https://auth.monday.com/oauth_ms/oauth/token",
|
|
39023
39960
|
scopes: [
|
|
39024
39961
|
"me:read",
|
|
39025
39962
|
"boards:read",
|
|
@@ -39409,6 +40346,475 @@ var mondayConnector = declarativeRestConnector({
|
|
|
39409
40346
|
]
|
|
39410
40347
|
});
|
|
39411
40348
|
|
|
40349
|
+
// src/connectors/adapters/mongodb.ts
|
|
40350
|
+
import { isIP as isIP3 } from "net";
|
|
40351
|
+
import { createSecureContext } from "tls";
|
|
40352
|
+
import {
|
|
40353
|
+
BSON,
|
|
40354
|
+
MongoClient
|
|
40355
|
+
} from "mongodb";
|
|
40356
|
+
var MAX_NAME_BYTES = 120;
|
|
40357
|
+
var MAX_FIELD_BYTES = 256;
|
|
40358
|
+
var MAX_FILTERS = 20;
|
|
40359
|
+
var MAX_PROJECTION_FIELDS = 64;
|
|
40360
|
+
var MAX_SORT_FIELDS = 8;
|
|
40361
|
+
var MAX_COLLECTIONS = 1e3;
|
|
40362
|
+
var MAX_INDEXES = 200;
|
|
40363
|
+
var MAX_DOCUMENTS = 1e3;
|
|
40364
|
+
var MAX_RESULT_BYTES2 = 10 * 1024 * 1024;
|
|
40365
|
+
var MAX_FILTER_BYTES = 1024 * 1024;
|
|
40366
|
+
var MAX_SCALAR_STRING_BYTES = 64 * 1024;
|
|
40367
|
+
var MAX_TLS_CA_BYTES = 256 * 1024;
|
|
40368
|
+
function createMongoDbConnector(options = {}) {
|
|
40369
|
+
const createClient = options.createClient ?? defaultCreateClient;
|
|
40370
|
+
const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
|
|
40371
|
+
return {
|
|
40372
|
+
manifest: {
|
|
40373
|
+
kind: "mongodb",
|
|
40374
|
+
displayName: "MongoDB",
|
|
40375
|
+
description: "Inspect MongoDB collections and indexes and run bounded structured document reads over a verified TLS connection.",
|
|
40376
|
+
auth: {
|
|
40377
|
+
kind: "api-key",
|
|
40378
|
+
hint: "JSON with a public MongoDB host, database, user, password, optional port/auth source, and optional TLS CA. Verified TLS is mandatory."
|
|
40379
|
+
},
|
|
40380
|
+
category: "database",
|
|
40381
|
+
defaultConsistencyModel: "authoritative",
|
|
40382
|
+
rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
|
|
40383
|
+
capabilities: [
|
|
40384
|
+
readCapability6("mongodb.collections.list", "List collections visible in the configured database.", {
|
|
40385
|
+
type: "object",
|
|
40386
|
+
properties: {},
|
|
40387
|
+
additionalProperties: false
|
|
40388
|
+
}),
|
|
40389
|
+
readCapability6("mongodb.collections.describe", "Read options and metadata for one collection.", collectionSchema()),
|
|
40390
|
+
readCapability6("mongodb.indexes.list", "List bounded index metadata for one collection.", collectionSchema()),
|
|
40391
|
+
readCapability6("mongodb.documents.find", "Read bounded documents using fixed scalar predicates, projection, and sort fields.", {
|
|
40392
|
+
type: "object",
|
|
40393
|
+
properties: {
|
|
40394
|
+
collection: nameParameter("Collection name."),
|
|
40395
|
+
filters: filterArraySchema(),
|
|
40396
|
+
fields: {
|
|
40397
|
+
type: "array",
|
|
40398
|
+
minItems: 1,
|
|
40399
|
+
maxItems: MAX_PROJECTION_FIELDS,
|
|
40400
|
+
uniqueItems: true,
|
|
40401
|
+
items: fieldParameter()
|
|
40402
|
+
},
|
|
40403
|
+
includeId: { type: "boolean", default: true },
|
|
40404
|
+
sort: {
|
|
40405
|
+
type: "array",
|
|
40406
|
+
maxItems: MAX_SORT_FIELDS,
|
|
40407
|
+
items: {
|
|
40408
|
+
type: "object",
|
|
40409
|
+
properties: {
|
|
40410
|
+
field: fieldParameter(),
|
|
40411
|
+
direction: { type: "string", enum: ["asc", "desc"], default: "asc" }
|
|
40412
|
+
},
|
|
40413
|
+
required: ["field"],
|
|
40414
|
+
additionalProperties: false
|
|
40415
|
+
}
|
|
40416
|
+
},
|
|
40417
|
+
limit: { type: "integer", minimum: 1, maximum: MAX_DOCUMENTS, default: 100 },
|
|
40418
|
+
skip: { type: "integer", minimum: 0, maximum: 1e6, default: 0 }
|
|
40419
|
+
},
|
|
40420
|
+
required: ["collection"],
|
|
40421
|
+
additionalProperties: false
|
|
40422
|
+
}),
|
|
40423
|
+
readCapability6("mongodb.documents.count", "Count documents matching fixed scalar predicates within a bounded server execution time.", {
|
|
40424
|
+
type: "object",
|
|
40425
|
+
properties: {
|
|
40426
|
+
collection: nameParameter("Collection name."),
|
|
40427
|
+
filters: filterArraySchema()
|
|
40428
|
+
},
|
|
40429
|
+
required: ["collection"],
|
|
40430
|
+
additionalProperties: false
|
|
40431
|
+
})
|
|
40432
|
+
]
|
|
40433
|
+
},
|
|
40434
|
+
async test(source) {
|
|
40435
|
+
try {
|
|
40436
|
+
await withClient(source, createClient, resolveHost, async (client, credentials) => {
|
|
40437
|
+
const response = await client.db(credentials.database).command(
|
|
40438
|
+
{ ping: 1 },
|
|
40439
|
+
{ maxTimeMS: credentials.commandTimeoutMs }
|
|
40440
|
+
);
|
|
40441
|
+
if (response.ok !== 1) throw new Error("MongoDB ping returned an unexpected response");
|
|
40442
|
+
});
|
|
40443
|
+
return { ok: true };
|
|
40444
|
+
} catch (error) {
|
|
40445
|
+
return { ok: false, reason: safeErrorMessage(error, credentialSecrets(source)) };
|
|
40446
|
+
}
|
|
40447
|
+
},
|
|
40448
|
+
async executeRead(invocation) {
|
|
40449
|
+
const request4 = prepareRead(invocation);
|
|
40450
|
+
const data4 = await withClient(invocation.source, createClient, resolveHost, async (client, credentials) => {
|
|
40451
|
+
return executeRead(request4, client.db(credentials.database), credentials.commandTimeoutMs);
|
|
40452
|
+
});
|
|
40453
|
+
return { data: data4, fetchedAt: Date.now() };
|
|
40454
|
+
}
|
|
40455
|
+
};
|
|
40456
|
+
}
|
|
40457
|
+
var mongodbConnector = createMongoDbConnector();
|
|
40458
|
+
function defaultCreateClient(uri, options) {
|
|
40459
|
+
return new MongoClient(uri, options);
|
|
40460
|
+
}
|
|
40461
|
+
async function withClient(source, createClient, resolveHost, run) {
|
|
40462
|
+
const credentials = readCredentials2(source);
|
|
40463
|
+
const addresses = await resolveHost(credentials.host);
|
|
40464
|
+
if (addresses.length === 0) throw new Error("MongoDB host did not resolve");
|
|
40465
|
+
const uriHost = isIP3(credentials.host) === 6 ? `[${credentials.host}]` : credentials.host;
|
|
40466
|
+
const client = createClient(`mongodb://${uriHost}:${credentials.port}/`, {
|
|
40467
|
+
appName: "tangle-integration-hub",
|
|
40468
|
+
auth: { username: credentials.user, password: credentials.password },
|
|
40469
|
+
authSource: credentials.authSource,
|
|
40470
|
+
connectTimeoutMS: credentials.connectTimeoutMs,
|
|
40471
|
+
directConnection: true,
|
|
40472
|
+
lookup: pinnedLookup(addresses[0]),
|
|
40473
|
+
maxIdleTimeMS: 5e3,
|
|
40474
|
+
maxPoolSize: 1,
|
|
40475
|
+
minPoolSize: 0,
|
|
40476
|
+
rejectUnauthorized: true,
|
|
40477
|
+
retryReads: true,
|
|
40478
|
+
retryWrites: false,
|
|
40479
|
+
serverSelectionTimeoutMS: credentials.connectTimeoutMs,
|
|
40480
|
+
servername: isIP3(credentials.host) === 0 ? credentials.host : void 0,
|
|
40481
|
+
secureContext: createSecureContext({ ca: credentials.tlsCa, minVersion: "TLSv1.2" }),
|
|
40482
|
+
socketTimeoutMS: credentials.commandTimeoutMs,
|
|
40483
|
+
tls: true
|
|
40484
|
+
});
|
|
40485
|
+
try {
|
|
40486
|
+
await client.connect();
|
|
40487
|
+
return await run(client, credentials);
|
|
40488
|
+
} catch (error) {
|
|
40489
|
+
throw new Error(safeErrorMessage(error, credentials.secrets));
|
|
40490
|
+
} finally {
|
|
40491
|
+
await client.close().catch(() => void 0);
|
|
40492
|
+
}
|
|
40493
|
+
}
|
|
40494
|
+
async function executeRead(request4, database, commandTimeoutMs) {
|
|
40495
|
+
if (request4.kind === "collections.list") {
|
|
40496
|
+
const collections = await readCursor(
|
|
40497
|
+
database.listCollections({}, { nameOnly: true, maxTimeMS: commandTimeoutMs }),
|
|
40498
|
+
MAX_COLLECTIONS,
|
|
40499
|
+
"MongoDB collection list"
|
|
40500
|
+
);
|
|
40501
|
+
return boundedResult({ collections }, "MongoDB collection list");
|
|
40502
|
+
}
|
|
40503
|
+
if (request4.kind === "collections.describe") {
|
|
40504
|
+
const matches = await readCursor(
|
|
40505
|
+
database.listCollections({ name: request4.collection }, { nameOnly: false, maxTimeMS: commandTimeoutMs }),
|
|
40506
|
+
1,
|
|
40507
|
+
"MongoDB collection description"
|
|
40508
|
+
);
|
|
40509
|
+
return boundedResult({ collection: matches[0] ?? null }, "MongoDB collection description");
|
|
40510
|
+
}
|
|
40511
|
+
if (request4.kind === "indexes.list") {
|
|
40512
|
+
const indexes = await readCursor(
|
|
40513
|
+
database.collection(request4.collection).listIndexes({ maxTimeMS: commandTimeoutMs }),
|
|
40514
|
+
MAX_INDEXES,
|
|
40515
|
+
"MongoDB index list"
|
|
40516
|
+
);
|
|
40517
|
+
return boundedResult({ collection: request4.collection, indexes }, "MongoDB index list");
|
|
40518
|
+
}
|
|
40519
|
+
if (request4.kind === "documents.find") {
|
|
40520
|
+
const cursor = database.collection(request4.collection).find(request4.filter, {
|
|
40521
|
+
...request4.projection ? { projection: request4.projection } : {},
|
|
40522
|
+
maxTimeMS: commandTimeoutMs
|
|
40523
|
+
});
|
|
40524
|
+
if (Object.keys(request4.sort).length > 0) cursor.sort(request4.sort);
|
|
40525
|
+
cursor.skip(request4.skip).limit(request4.limit);
|
|
40526
|
+
const documents = await readCursor(cursor, request4.limit, "MongoDB document result");
|
|
40527
|
+
return boundedResult({ collection: request4.collection, documents }, "MongoDB document result");
|
|
40528
|
+
}
|
|
40529
|
+
if (request4.kind === "documents.count") {
|
|
40530
|
+
const count = await database.collection(request4.collection).countDocuments(request4.filter, { maxTimeMS: commandTimeoutMs });
|
|
40531
|
+
if (!Number.isSafeInteger(count) || count < 0) throw new Error("MongoDB returned a malformed document count");
|
|
40532
|
+
return { collection: request4.collection, count };
|
|
40533
|
+
}
|
|
40534
|
+
}
|
|
40535
|
+
function prepareRead(invocation) {
|
|
40536
|
+
if (invocation.capabilityName === "mongodb.collections.list") return { kind: "collections.list" };
|
|
40537
|
+
if (invocation.capabilityName === "mongodb.collections.describe") {
|
|
40538
|
+
return { kind: "collections.describe", collection: readCollectionName(invocation.args.collection) };
|
|
40539
|
+
}
|
|
40540
|
+
if (invocation.capabilityName === "mongodb.indexes.list") {
|
|
40541
|
+
return { kind: "indexes.list", collection: readCollectionName(invocation.args.collection) };
|
|
40542
|
+
}
|
|
40543
|
+
if (invocation.capabilityName === "mongodb.documents.find") {
|
|
40544
|
+
return {
|
|
40545
|
+
kind: "documents.find",
|
|
40546
|
+
collection: readCollectionName(invocation.args.collection),
|
|
40547
|
+
filter: readFilters(invocation.args.filters),
|
|
40548
|
+
projection: readProjection(invocation.args.fields, invocation.args.includeId),
|
|
40549
|
+
sort: readSort(invocation.args.sort),
|
|
40550
|
+
limit: readBoundedInteger(invocation.args.limit, 100, 1, MAX_DOCUMENTS, "limit"),
|
|
40551
|
+
skip: readBoundedInteger(invocation.args.skip, 0, 0, 1e6, "skip")
|
|
40552
|
+
};
|
|
40553
|
+
}
|
|
40554
|
+
if (invocation.capabilityName === "mongodb.documents.count") {
|
|
40555
|
+
return {
|
|
40556
|
+
kind: "documents.count",
|
|
40557
|
+
collection: readCollectionName(invocation.args.collection),
|
|
40558
|
+
filter: readFilters(invocation.args.filters)
|
|
40559
|
+
};
|
|
40560
|
+
}
|
|
40561
|
+
throw new Error(`Unknown MongoDB read capability: ${invocation.capabilityName}`);
|
|
40562
|
+
}
|
|
40563
|
+
async function readCursor(cursor, maximum, label) {
|
|
40564
|
+
const entries = [];
|
|
40565
|
+
let serializedBytes = 2;
|
|
40566
|
+
try {
|
|
40567
|
+
while (entries.length < maximum) {
|
|
40568
|
+
const entry = await cursor.next();
|
|
40569
|
+
if (entry === null) return entries;
|
|
40570
|
+
serializedBytes += Buffer.byteLength(
|
|
40571
|
+
JSON.stringify(BSON.EJSON.serialize(entry, { relaxed: true })),
|
|
40572
|
+
"utf8"
|
|
40573
|
+
) + 1;
|
|
40574
|
+
if (serializedBytes > MAX_RESULT_BYTES2) {
|
|
40575
|
+
throw new Error(`${label} exceeds the ${MAX_RESULT_BYTES2}-byte limit`);
|
|
40576
|
+
}
|
|
40577
|
+
entries.push(entry);
|
|
40578
|
+
}
|
|
40579
|
+
if (await cursor.next() !== null) throw new Error(`${label} exceeds ${maximum} entries`);
|
|
40580
|
+
return entries;
|
|
40581
|
+
} finally {
|
|
40582
|
+
await cursor.close().catch(() => void 0);
|
|
40583
|
+
}
|
|
40584
|
+
}
|
|
40585
|
+
function readCredentials2(source) {
|
|
40586
|
+
let raw;
|
|
40587
|
+
if (source.credentials.kind === "custom") raw = source.credentials.values;
|
|
40588
|
+
else if (source.credentials.kind === "api-key") {
|
|
40589
|
+
try {
|
|
40590
|
+
raw = JSON.parse(source.credentials.apiKey);
|
|
40591
|
+
} catch {
|
|
40592
|
+
throw new Error("MongoDB credential must be valid JSON");
|
|
40593
|
+
}
|
|
40594
|
+
} else {
|
|
40595
|
+
throw new Error("MongoDB requires a structured credential bundle");
|
|
40596
|
+
}
|
|
40597
|
+
if (!isPlainRecord(raw)) throw new Error("MongoDB credential must be a JSON object");
|
|
40598
|
+
const host = readHost(raw.host);
|
|
40599
|
+
const database = readDatabaseName(raw.database, "database");
|
|
40600
|
+
const user = readCredentialString(raw.user ?? raw.username, "user", 128);
|
|
40601
|
+
const password = readCredentialString(raw.password, "password", 4096);
|
|
40602
|
+
const authSource = raw.authSource === void 0 ? "admin" : readDatabaseName(raw.authSource, "authSource");
|
|
40603
|
+
const tlsCa = readOptionalString(raw.tlsCa, "tlsCa");
|
|
40604
|
+
if (tlsCa && Buffer.byteLength(tlsCa, "utf8") > MAX_TLS_CA_BYTES) {
|
|
40605
|
+
throw new Error(`MongoDB tlsCa exceeds the ${MAX_TLS_CA_BYTES}-byte limit`);
|
|
40606
|
+
}
|
|
40607
|
+
return {
|
|
40608
|
+
host,
|
|
40609
|
+
port: readBoundedInteger(raw.port, 27017, 1, 65535, "port"),
|
|
40610
|
+
database,
|
|
40611
|
+
user,
|
|
40612
|
+
password,
|
|
40613
|
+
authSource,
|
|
40614
|
+
tlsCa,
|
|
40615
|
+
connectTimeoutMs: readBoundedInteger(raw.connectTimeoutMs, 1e4, 1e3, 3e4, "connectTimeoutMs"),
|
|
40616
|
+
commandTimeoutMs: readBoundedInteger(raw.commandTimeoutMs, 3e4, 1e3, 6e4, "commandTimeoutMs"),
|
|
40617
|
+
secrets: [password]
|
|
40618
|
+
};
|
|
40619
|
+
}
|
|
40620
|
+
function readFilters(value) {
|
|
40621
|
+
if (value === void 0 || value === null) return {};
|
|
40622
|
+
if (!Array.isArray(value) || value.length > MAX_FILTERS) {
|
|
40623
|
+
throw new Error(`filters must contain at most ${MAX_FILTERS} entries`);
|
|
40624
|
+
}
|
|
40625
|
+
const predicates = value.map((entry, index) => {
|
|
40626
|
+
if (!isPlainRecord(entry)) throw new Error(`filters[${index}] must be an object`);
|
|
40627
|
+
const field = readField(entry.field, `filters[${index}].field`);
|
|
40628
|
+
const operator = entry.operator;
|
|
40629
|
+
if (!["eq", "ne", "lt", "lte", "gt", "gte"].includes(String(operator))) {
|
|
40630
|
+
throw new Error(`filters[${index}].operator is unsupported`);
|
|
40631
|
+
}
|
|
40632
|
+
const scalar = readScalar(entry.value, `filters[${index}].value`);
|
|
40633
|
+
return { [field]: { [mongoOperator(String(operator))]: scalar } };
|
|
40634
|
+
});
|
|
40635
|
+
const filter = predicates.length === 0 ? {} : { $and: predicates };
|
|
40636
|
+
if (Buffer.byteLength(JSON.stringify(filter), "utf8") > MAX_FILTER_BYTES) {
|
|
40637
|
+
throw new Error(`MongoDB filters exceed the ${MAX_FILTER_BYTES}-byte limit`);
|
|
40638
|
+
}
|
|
40639
|
+
return filter;
|
|
40640
|
+
}
|
|
40641
|
+
function readProjection(fieldsValue, includeIdValue) {
|
|
40642
|
+
const includeId = includeIdValue === void 0 || includeIdValue === null ? true : readBoolean2(includeIdValue, "includeId");
|
|
40643
|
+
if (fieldsValue === void 0 || fieldsValue === null) return includeId ? void 0 : { _id: 0 };
|
|
40644
|
+
const fields = readFieldArray(fieldsValue, "fields", MAX_PROJECTION_FIELDS, true);
|
|
40645
|
+
return Object.fromEntries([
|
|
40646
|
+
...fields.map((field) => [field, 1]),
|
|
40647
|
+
...!includeId && !fields.includes("_id") ? [["_id", 0]] : []
|
|
40648
|
+
]);
|
|
40649
|
+
}
|
|
40650
|
+
function readSort(value) {
|
|
40651
|
+
if (value === void 0 || value === null) return {};
|
|
40652
|
+
if (!Array.isArray(value) || value.length > MAX_SORT_FIELDS) {
|
|
40653
|
+
throw new Error(`sort must contain at most ${MAX_SORT_FIELDS} entries`);
|
|
40654
|
+
}
|
|
40655
|
+
const entries = value.map((entry, index) => {
|
|
40656
|
+
if (!isPlainRecord(entry)) throw new Error(`sort[${index}] must be an object`);
|
|
40657
|
+
const field = readField(entry.field, `sort[${index}].field`);
|
|
40658
|
+
const direction = entry.direction ?? "asc";
|
|
40659
|
+
if (direction !== "asc" && direction !== "desc") {
|
|
40660
|
+
throw new Error(`sort[${index}].direction must be asc or desc`);
|
|
40661
|
+
}
|
|
40662
|
+
return [field, direction === "asc" ? 1 : -1];
|
|
40663
|
+
});
|
|
40664
|
+
if (new Set(entries.map(([field]) => field)).size !== entries.length) {
|
|
40665
|
+
throw new Error("sort must not contain duplicate fields");
|
|
40666
|
+
}
|
|
40667
|
+
return Object.fromEntries(entries);
|
|
40668
|
+
}
|
|
40669
|
+
function readFieldArray(value, label, maximum, required) {
|
|
40670
|
+
if (!Array.isArray(value) || required && value.length === 0 || value.length > maximum) {
|
|
40671
|
+
throw new Error(`${label} must contain ${required ? "1-" : "0-"}${maximum} fields`);
|
|
40672
|
+
}
|
|
40673
|
+
const fields = value.map((entry, index) => readField(entry, `${label}[${index}]`));
|
|
40674
|
+
if (new Set(fields).size !== fields.length) throw new Error(`${label} must not contain duplicates`);
|
|
40675
|
+
return fields;
|
|
40676
|
+
}
|
|
40677
|
+
function readScalar(value, label) {
|
|
40678
|
+
if (value === null || typeof value === "boolean") return value;
|
|
40679
|
+
if (typeof value === "number") {
|
|
40680
|
+
if (!Number.isFinite(value)) throw new Error(`${label} must be finite`);
|
|
40681
|
+
return value;
|
|
40682
|
+
}
|
|
40683
|
+
if (typeof value === "string") {
|
|
40684
|
+
if (Buffer.byteLength(value, "utf8") > MAX_SCALAR_STRING_BYTES) {
|
|
40685
|
+
throw new Error(`${label} exceeds the ${MAX_SCALAR_STRING_BYTES}-byte limit`);
|
|
40686
|
+
}
|
|
40687
|
+
return value;
|
|
40688
|
+
}
|
|
40689
|
+
throw new Error(`${label} must be a JSON scalar or null`);
|
|
40690
|
+
}
|
|
40691
|
+
function mongoOperator(value) {
|
|
40692
|
+
return { eq: "$eq", ne: "$ne", lt: "$lt", lte: "$lte", gt: "$gt", gte: "$gte" }[value];
|
|
40693
|
+
}
|
|
40694
|
+
function boundedResult(value, label) {
|
|
40695
|
+
const serialized = BSON.EJSON.serialize(value, { relaxed: true });
|
|
40696
|
+
if (Buffer.byteLength(JSON.stringify(serialized), "utf8") > MAX_RESULT_BYTES2) {
|
|
40697
|
+
throw new Error(`${label} exceeds the ${MAX_RESULT_BYTES2}-byte limit`);
|
|
40698
|
+
}
|
|
40699
|
+
return serialized;
|
|
40700
|
+
}
|
|
40701
|
+
function readHost(value) {
|
|
40702
|
+
const host = readCredentialString(value, "host", 253);
|
|
40703
|
+
const ipVersion = isIP3(host);
|
|
40704
|
+
if (ipVersion !== 0) {
|
|
40705
|
+
if (!isPublicNetworkAddress(host)) throw new Error("MongoDB host is not a public network target");
|
|
40706
|
+
return host;
|
|
40707
|
+
}
|
|
40708
|
+
const normalized = host.toLowerCase();
|
|
40709
|
+
if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@:\[\]]/.test(host)) {
|
|
40710
|
+
throw new Error("MongoDB host must be a public hostname or IP address without a scheme or port");
|
|
40711
|
+
}
|
|
40712
|
+
return host;
|
|
40713
|
+
}
|
|
40714
|
+
function readName(value, label) {
|
|
40715
|
+
if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > MAX_NAME_BYTES || /[\u0000-\u001f\u007f/\\]/.test(value)) {
|
|
40716
|
+
throw new Error(`${label} must be a non-empty MongoDB name under ${MAX_NAME_BYTES} bytes`);
|
|
40717
|
+
}
|
|
40718
|
+
return value;
|
|
40719
|
+
}
|
|
40720
|
+
function readCollectionName(value) {
|
|
40721
|
+
const collection = readName(value, "collection");
|
|
40722
|
+
if (collection.startsWith("system.") || collection.includes("$")) {
|
|
40723
|
+
throw new Error("collection must not target MongoDB system or command namespaces");
|
|
40724
|
+
}
|
|
40725
|
+
return collection;
|
|
40726
|
+
}
|
|
40727
|
+
function readDatabaseName(value, label) {
|
|
40728
|
+
const database = readName(value, label);
|
|
40729
|
+
if (Buffer.byteLength(database, "utf8") > 63 || !/^[A-Za-z0-9_-]+$/.test(database)) {
|
|
40730
|
+
throw new Error(`${label} must use only letters, numbers, underscores, or hyphens and stay under 63 bytes`);
|
|
40731
|
+
}
|
|
40732
|
+
return database;
|
|
40733
|
+
}
|
|
40734
|
+
function readField(value, label) {
|
|
40735
|
+
if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > MAX_FIELD_BYTES || !/^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(value)) {
|
|
40736
|
+
throw new Error(`${label} must be a dotted MongoDB field path under ${MAX_FIELD_BYTES} bytes`);
|
|
40737
|
+
}
|
|
40738
|
+
return value;
|
|
40739
|
+
}
|
|
40740
|
+
function readCredentialString(value, label, maximum) {
|
|
40741
|
+
if (typeof value !== "string" || value.length === 0 || value.length > maximum || label !== "password" && /[\u0000-\u001f\u007f]/.test(value)) {
|
|
40742
|
+
throw new Error(`MongoDB credential ${label} must be a non-empty string under ${maximum} characters`);
|
|
40743
|
+
}
|
|
40744
|
+
return value;
|
|
40745
|
+
}
|
|
40746
|
+
function readBoolean2(value, label) {
|
|
40747
|
+
if (typeof value !== "boolean") throw new Error(`${label} must be a boolean`);
|
|
40748
|
+
return value;
|
|
40749
|
+
}
|
|
40750
|
+
function pinnedLookup(address) {
|
|
40751
|
+
const family = isIP3(address);
|
|
40752
|
+
return (_hostname, options, callback) => {
|
|
40753
|
+
const normalizedOptions = typeof options === "number" ? { family: options } : options;
|
|
40754
|
+
if (normalizedOptions?.family && normalizedOptions.family !== family) {
|
|
40755
|
+
callback(new Error("MongoDB pinned address does not match the requested network family"), "", 0);
|
|
40756
|
+
return;
|
|
40757
|
+
}
|
|
40758
|
+
if (normalizedOptions?.all) {
|
|
40759
|
+
callback(null, [{ address, family }], void 0);
|
|
40760
|
+
return;
|
|
40761
|
+
}
|
|
40762
|
+
callback(null, address, family);
|
|
40763
|
+
};
|
|
40764
|
+
}
|
|
40765
|
+
function safeErrorMessage(error, secrets) {
|
|
40766
|
+
let message = error instanceof Error ? error.message : String(error);
|
|
40767
|
+
for (const secret of secrets) {
|
|
40768
|
+
if (secret.length >= 4) message = message.replaceAll(secret, "[REDACTED]");
|
|
40769
|
+
}
|
|
40770
|
+
return message;
|
|
40771
|
+
}
|
|
40772
|
+
function credentialSecrets(source) {
|
|
40773
|
+
try {
|
|
40774
|
+
return readCredentials2(source).secrets;
|
|
40775
|
+
} catch {
|
|
40776
|
+
return [];
|
|
40777
|
+
}
|
|
40778
|
+
}
|
|
40779
|
+
function collectionSchema() {
|
|
40780
|
+
return {
|
|
40781
|
+
type: "object",
|
|
40782
|
+
properties: { collection: nameParameter("Collection name.") },
|
|
40783
|
+
required: ["collection"],
|
|
40784
|
+
additionalProperties: false
|
|
40785
|
+
};
|
|
40786
|
+
}
|
|
40787
|
+
function filterArraySchema() {
|
|
40788
|
+
return {
|
|
40789
|
+
type: "array",
|
|
40790
|
+
maxItems: MAX_FILTERS,
|
|
40791
|
+
items: {
|
|
40792
|
+
type: "object",
|
|
40793
|
+
properties: {
|
|
40794
|
+
field: fieldParameter(),
|
|
40795
|
+
operator: { type: "string", enum: ["eq", "ne", "lt", "lte", "gt", "gte"] },
|
|
40796
|
+
value: { type: ["string", "number", "boolean", "null"] }
|
|
40797
|
+
},
|
|
40798
|
+
required: ["field", "operator", "value"],
|
|
40799
|
+
additionalProperties: false
|
|
40800
|
+
}
|
|
40801
|
+
};
|
|
40802
|
+
}
|
|
40803
|
+
function nameParameter(description) {
|
|
40804
|
+
return { type: "string", minLength: 1, maxLength: MAX_NAME_BYTES, description };
|
|
40805
|
+
}
|
|
40806
|
+
function fieldParameter() {
|
|
40807
|
+
return {
|
|
40808
|
+
type: "string",
|
|
40809
|
+
minLength: 1,
|
|
40810
|
+
maxLength: MAX_FIELD_BYTES,
|
|
40811
|
+
pattern: "^[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)*$"
|
|
40812
|
+
};
|
|
40813
|
+
}
|
|
40814
|
+
function readCapability6(name, description, parameters) {
|
|
40815
|
+
return { name, class: "read", description, parameters };
|
|
40816
|
+
}
|
|
40817
|
+
|
|
39412
40818
|
// src/connectors/adapters/mycase.ts
|
|
39413
40819
|
var pageProperties = {
|
|
39414
40820
|
page: { type: "integer", minimum: 1 },
|
|
@@ -43883,7 +45289,7 @@ var pipedriveConnector = declarativeRestConnector({
|
|
|
43883
45289
|
// src/connectors/adapters/parquet-files.ts
|
|
43884
45290
|
import { parquetReadObjects } from "hyparquet";
|
|
43885
45291
|
import { parquetWriteBuffer } from "hyparquet-writer";
|
|
43886
|
-
var
|
|
45292
|
+
var MAX_COLUMNS4 = 256;
|
|
43887
45293
|
var parquetFilesConnector = {
|
|
43888
45294
|
manifest: {
|
|
43889
45295
|
kind: "parquet-files",
|
|
@@ -43901,7 +45307,7 @@ var parquetFilesConnector = {
|
|
|
43901
45307
|
type: "object",
|
|
43902
45308
|
properties: {
|
|
43903
45309
|
fileBase64: { type: "string", description: "Parquet file content encoded as canonical base64." },
|
|
43904
|
-
columns: { type: "array", maxItems:
|
|
45310
|
+
columns: { type: "array", maxItems: MAX_COLUMNS4, items: { type: "string", minLength: 1 } },
|
|
43905
45311
|
rowStart: { type: "integer", minimum: 0, default: 0 },
|
|
43906
45312
|
rowLimit: { type: "integer", minimum: 1, maximum: MAX_RECORDS, default: MAX_RECORDS }
|
|
43907
45313
|
},
|
|
@@ -43947,8 +45353,8 @@ var parquetFilesConnector = {
|
|
|
43947
45353
|
if (capabilityName !== "parquet.create") throw new Error(`Unknown Parquet capability: ${capabilityName}`);
|
|
43948
45354
|
const records = readRecords(args.records);
|
|
43949
45355
|
const columnNames = [...new Set(records.flatMap((record) => Object.keys(record)))];
|
|
43950
|
-
if (columnNames.length === 0 || columnNames.length >
|
|
43951
|
-
throw new Error(`records must contain 1 through ${
|
|
45356
|
+
if (columnNames.length === 0 || columnNames.length > MAX_COLUMNS4) {
|
|
45357
|
+
throw new Error(`records must contain 1 through ${MAX_COLUMNS4} columns`);
|
|
43952
45358
|
}
|
|
43953
45359
|
if (columnNames.some((name) => name.length === 0)) throw new Error("column names must not be empty");
|
|
43954
45360
|
const columnData = columnNames.map((name) => {
|
|
@@ -43978,8 +45384,8 @@ function asyncBuffer(file) {
|
|
|
43978
45384
|
}
|
|
43979
45385
|
function readColumns2(value) {
|
|
43980
45386
|
if (value === void 0 || value === null) return void 0;
|
|
43981
|
-
if (!Array.isArray(value) || value.length === 0 || value.length >
|
|
43982
|
-
throw new Error(`columns must contain 1 through ${
|
|
45387
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > MAX_COLUMNS4) {
|
|
45388
|
+
throw new Error(`columns must contain 1 through ${MAX_COLUMNS4} names`);
|
|
43983
45389
|
}
|
|
43984
45390
|
const columns = value.map((entry, index) => {
|
|
43985
45391
|
if (typeof entry !== "string" || entry.length === 0) throw new Error(`columns[${index}] must be a non-empty string`);
|
|
@@ -44435,6 +45841,1143 @@ var quickbooksConnector = declarativeRestConnector({
|
|
|
44435
45841
|
]
|
|
44436
45842
|
});
|
|
44437
45843
|
|
|
45844
|
+
// src/connectors/adapters/rabbitmq.ts
|
|
45845
|
+
import { createHash as createHash7 } from "crypto";
|
|
45846
|
+
import { isIP as isIP4 } from "net";
|
|
45847
|
+
import {
|
|
45848
|
+
connect as amqpConnect
|
|
45849
|
+
} from "amqplib";
|
|
45850
|
+
var MAX_MESSAGE_BYTES3 = 1024 * 1024;
|
|
45851
|
+
var MAX_NAME_BYTES2 = 255;
|
|
45852
|
+
var MAX_PEM_BYTES = 64 * 1024;
|
|
45853
|
+
var MAX_CA_CERTIFICATES = 16;
|
|
45854
|
+
var MAX_CA_BYTES = 256 * 1024;
|
|
45855
|
+
function createRabbitMqConnector(options = {}) {
|
|
45856
|
+
const connect = options.connect ?? ((connectOptions, socketOptions) => amqpConnect(connectOptions, socketOptions));
|
|
45857
|
+
const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
|
|
45858
|
+
return {
|
|
45859
|
+
manifest: {
|
|
45860
|
+
kind: "rabbitmq",
|
|
45861
|
+
displayName: "RabbitMQ",
|
|
45862
|
+
description: "Inspect queues and exchanges and publish confirmed JSON messages over verified TLS.",
|
|
45863
|
+
auth: {
|
|
45864
|
+
kind: "api-key",
|
|
45865
|
+
hint: "JSON with a public RabbitMQ host, username, password, virtual host, and optional TLS client credentials."
|
|
45866
|
+
},
|
|
45867
|
+
defaultConsistencyModel: "advisory",
|
|
45868
|
+
category: "webhook",
|
|
45869
|
+
rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
|
|
45870
|
+
capabilities: [
|
|
45871
|
+
readCapability7("rabbitmq.queues.inspect", "Check a queue and return its current message and consumer counts.", {
|
|
45872
|
+
type: "object",
|
|
45873
|
+
properties: { queue: nameSchema("queue") },
|
|
45874
|
+
required: ["queue"],
|
|
45875
|
+
additionalProperties: false
|
|
45876
|
+
}),
|
|
45877
|
+
readCapability7("rabbitmq.exchanges.inspect", "Check whether a named exchange exists and is visible to the connection.", {
|
|
45878
|
+
type: "object",
|
|
45879
|
+
properties: { exchange: nameSchema("exchange") },
|
|
45880
|
+
required: ["exchange"],
|
|
45881
|
+
additionalProperties: false
|
|
45882
|
+
}),
|
|
45883
|
+
mutationCapability6("send.message.to.queue", "Publish a persistent JSON message directly to an existing queue with broker confirmation.", {
|
|
45884
|
+
type: "object",
|
|
45885
|
+
properties: {
|
|
45886
|
+
queue: nameSchema("queue"),
|
|
45887
|
+
data: {},
|
|
45888
|
+
persistent: { type: "boolean", default: true },
|
|
45889
|
+
expirationMs: { type: "integer", minimum: 1, maximum: 6048e5 },
|
|
45890
|
+
priority: { type: "integer", minimum: 0, maximum: 255 }
|
|
45891
|
+
},
|
|
45892
|
+
required: ["queue", "data"],
|
|
45893
|
+
additionalProperties: false
|
|
45894
|
+
}),
|
|
45895
|
+
mutationCapability6("send.message.to.exchange", "Publish a persistent JSON message to an existing exchange with broker confirmation.", {
|
|
45896
|
+
type: "object",
|
|
45897
|
+
properties: {
|
|
45898
|
+
exchange: nameSchema("exchange"),
|
|
45899
|
+
routingKey: { type: "string", maxLength: MAX_NAME_BYTES2, default: "" },
|
|
45900
|
+
data: {},
|
|
45901
|
+
persistent: { type: "boolean", default: true },
|
|
45902
|
+
expirationMs: { type: "integer", minimum: 1, maximum: 6048e5 },
|
|
45903
|
+
priority: { type: "integer", minimum: 0, maximum: 255 }
|
|
45904
|
+
},
|
|
45905
|
+
required: ["exchange", "data"],
|
|
45906
|
+
additionalProperties: false
|
|
45907
|
+
})
|
|
45908
|
+
]
|
|
45909
|
+
},
|
|
45910
|
+
async executeRead({ source, capabilityName, args }) {
|
|
45911
|
+
if (capabilityName === "rabbitmq.queues.inspect") {
|
|
45912
|
+
const queue = readName2(args.queue, "queue");
|
|
45913
|
+
const data4 = await withChannel(source, connect, resolveHost, async (channel) => {
|
|
45914
|
+
const details = await channel.checkQueue(queue);
|
|
45915
|
+
return { queue: details.queue, messageCount: details.messageCount, consumerCount: details.consumerCount };
|
|
45916
|
+
});
|
|
45917
|
+
return { data: data4, fetchedAt: Date.now() };
|
|
45918
|
+
}
|
|
45919
|
+
if (capabilityName === "rabbitmq.exchanges.inspect") {
|
|
45920
|
+
const exchange = readName2(args.exchange, "exchange");
|
|
45921
|
+
const data4 = await withChannel(source, connect, resolveHost, async (channel) => {
|
|
45922
|
+
await channel.checkExchange(exchange);
|
|
45923
|
+
return { exchange, exists: true };
|
|
45924
|
+
});
|
|
45925
|
+
return { data: data4, fetchedAt: Date.now() };
|
|
45926
|
+
}
|
|
45927
|
+
throw new Error(`Unknown RabbitMQ read capability: ${capabilityName}`);
|
|
45928
|
+
},
|
|
45929
|
+
async executeMutation({ source, capabilityName, args, idempotencyKey }) {
|
|
45930
|
+
let data4;
|
|
45931
|
+
if (capabilityName === "send.message.to.queue") {
|
|
45932
|
+
const queue = readName2(args.queue, "queue");
|
|
45933
|
+
const body = readJsonMessage(args.data);
|
|
45934
|
+
const publishOptions = readPublishOptions(args, idempotencyKey);
|
|
45935
|
+
data4 = await withConfirmChannel(source, connect, resolveHost, async (channel) => {
|
|
45936
|
+
await channel.checkQueue(queue);
|
|
45937
|
+
await publishConfirmed(channel, publishOptions, (options2) => channel.sendToQueue(queue, body, options2));
|
|
45938
|
+
return { queue, confirmed: true, messageId: publishOptions.messageId, byteLength: body.byteLength };
|
|
45939
|
+
});
|
|
45940
|
+
} else if (capabilityName === "send.message.to.exchange") {
|
|
45941
|
+
const exchange = readName2(args.exchange, "exchange");
|
|
45942
|
+
const routingKey = readRoutingKey(args.routingKey);
|
|
45943
|
+
const body = readJsonMessage(args.data);
|
|
45944
|
+
const publishOptions = readPublishOptions(args, idempotencyKey);
|
|
45945
|
+
data4 = await withConfirmChannel(source, connect, resolveHost, async (channel) => {
|
|
45946
|
+
await channel.checkExchange(exchange);
|
|
45947
|
+
await publishConfirmed(channel, publishOptions, (options2) => channel.publish(exchange, routingKey, body, options2));
|
|
45948
|
+
return { exchange, routingKey, confirmed: true, messageId: publishOptions.messageId, byteLength: body.byteLength };
|
|
45949
|
+
});
|
|
45950
|
+
} else {
|
|
45951
|
+
throw new Error(`Unknown RabbitMQ mutation capability: ${capabilityName}`);
|
|
45952
|
+
}
|
|
45953
|
+
return { status: "committed", data: data4, committedAt: Date.now(), idempotentReplay: false };
|
|
45954
|
+
},
|
|
45955
|
+
async test(source) {
|
|
45956
|
+
try {
|
|
45957
|
+
const credentials = readCredentials3(source);
|
|
45958
|
+
await resolveHost(credentials.host);
|
|
45959
|
+
const model2 = await connect(credentials.connectOptions, credentials.socketOptions);
|
|
45960
|
+
await model2.close();
|
|
45961
|
+
return { ok: true };
|
|
45962
|
+
} catch (error) {
|
|
45963
|
+
return { ok: false, reason: safeErrorMessage2(error, credentialSecrets2(source)) };
|
|
45964
|
+
}
|
|
45965
|
+
}
|
|
45966
|
+
};
|
|
45967
|
+
}
|
|
45968
|
+
var rabbitMqConnector = createRabbitMqConnector();
|
|
45969
|
+
async function withChannel(source, connect, resolveHost, run) {
|
|
45970
|
+
const credentials = readCredentials3(source);
|
|
45971
|
+
await resolveHost(credentials.host);
|
|
45972
|
+
let model2;
|
|
45973
|
+
let channel;
|
|
45974
|
+
try {
|
|
45975
|
+
model2 = await connect(credentials.connectOptions, credentials.socketOptions);
|
|
45976
|
+
channel = await model2.createChannel();
|
|
45977
|
+
return await run(channel);
|
|
45978
|
+
} catch (error) {
|
|
45979
|
+
throw new Error(safeErrorMessage2(error, credentials.secrets));
|
|
45980
|
+
} finally {
|
|
45981
|
+
await channel?.close().catch(() => void 0);
|
|
45982
|
+
await model2?.close().catch(() => void 0);
|
|
45983
|
+
}
|
|
45984
|
+
}
|
|
45985
|
+
async function withConfirmChannel(source, connect, resolveHost, run) {
|
|
45986
|
+
const credentials = readCredentials3(source);
|
|
45987
|
+
await resolveHost(credentials.host);
|
|
45988
|
+
let model2;
|
|
45989
|
+
let channel;
|
|
45990
|
+
try {
|
|
45991
|
+
model2 = await connect(credentials.connectOptions, credentials.socketOptions);
|
|
45992
|
+
channel = await model2.createConfirmChannel();
|
|
45993
|
+
return await run(channel);
|
|
45994
|
+
} catch (error) {
|
|
45995
|
+
throw new Error(safeErrorMessage2(error, credentials.secrets));
|
|
45996
|
+
} finally {
|
|
45997
|
+
await channel?.close().catch(() => void 0);
|
|
45998
|
+
await model2?.close().catch(() => void 0);
|
|
45999
|
+
}
|
|
46000
|
+
}
|
|
46001
|
+
async function publishConfirmed(channel, publishOptions, publish) {
|
|
46002
|
+
let returned;
|
|
46003
|
+
const onReturn = (message) => {
|
|
46004
|
+
returned = message;
|
|
46005
|
+
};
|
|
46006
|
+
channel.on("return", onReturn);
|
|
46007
|
+
try {
|
|
46008
|
+
publish(publishOptions);
|
|
46009
|
+
await channel.waitForConfirms();
|
|
46010
|
+
if (returned) throw new Error("RabbitMQ accepted but could not route the message");
|
|
46011
|
+
} finally {
|
|
46012
|
+
channel.removeListener("return", onReturn);
|
|
46013
|
+
}
|
|
46014
|
+
}
|
|
46015
|
+
function readCredentials3(source) {
|
|
46016
|
+
let raw;
|
|
46017
|
+
if (source.credentials.kind === "custom") raw = source.credentials.values;
|
|
46018
|
+
else if (source.credentials.kind === "api-key") {
|
|
46019
|
+
try {
|
|
46020
|
+
raw = JSON.parse(source.credentials.apiKey);
|
|
46021
|
+
} catch {
|
|
46022
|
+
throw new Error("RabbitMQ credential must be valid JSON");
|
|
46023
|
+
}
|
|
46024
|
+
} else {
|
|
46025
|
+
throw new Error("RabbitMQ requires a structured credential bundle");
|
|
46026
|
+
}
|
|
46027
|
+
if (!isPlainRecord(raw)) throw new Error("RabbitMQ credential must be a JSON object");
|
|
46028
|
+
const host = readHost2(raw.host);
|
|
46029
|
+
const port = readBoundedInteger(raw.port, 5671, 1, 65535, "port");
|
|
46030
|
+
const username = readRequiredString(raw.username, "username", 1024);
|
|
46031
|
+
const password = readRequiredString(raw.password, "password", 4096);
|
|
46032
|
+
const vhost = readOptionalString(raw.vhost, "vhost") ?? "/";
|
|
46033
|
+
if (Buffer.byteLength(vhost, "utf8") > MAX_NAME_BYTES2 || /[\u0000-\u001f\u007f]/.test(vhost)) {
|
|
46034
|
+
throw new Error(`vhost must be at most ${MAX_NAME_BYTES2} bytes without control characters`);
|
|
46035
|
+
}
|
|
46036
|
+
const ca = readPem(raw.tlsCa, "tlsCa", true);
|
|
46037
|
+
const cert = readPem(raw.tlsCert, "tlsCert", false);
|
|
46038
|
+
const key = readPem(raw.tlsKey, "tlsKey", false);
|
|
46039
|
+
if (Boolean(cert) !== Boolean(key)) throw new Error("tlsCert and tlsKey must be supplied together");
|
|
46040
|
+
const passphrase = readOptionalString(raw.tlsPassphrase, "tlsPassphrase");
|
|
46041
|
+
return {
|
|
46042
|
+
host,
|
|
46043
|
+
connectOptions: {
|
|
46044
|
+
protocol: "amqps",
|
|
46045
|
+
hostname: host,
|
|
46046
|
+
port,
|
|
46047
|
+
username,
|
|
46048
|
+
password,
|
|
46049
|
+
vhost,
|
|
46050
|
+
heartbeat: readBoundedInteger(raw.heartbeatSeconds, 30, 5, 300, "heartbeatSeconds"),
|
|
46051
|
+
frameMax: MAX_MESSAGE_BYTES3 + 4096
|
|
46052
|
+
},
|
|
46053
|
+
socketOptions: {
|
|
46054
|
+
minVersion: "TLSv1.2",
|
|
46055
|
+
rejectUnauthorized: true,
|
|
46056
|
+
servername: isIP4(host) === 0 ? host : void 0,
|
|
46057
|
+
lookup: publicDnsLookup,
|
|
46058
|
+
timeout: readBoundedInteger(raw.connectionTimeoutMs, 1e4, 1e3, 3e4, "connectionTimeoutMs"),
|
|
46059
|
+
noDelay: true,
|
|
46060
|
+
keepAlive: true,
|
|
46061
|
+
keepAliveDelay: 6e4,
|
|
46062
|
+
ca,
|
|
46063
|
+
cert,
|
|
46064
|
+
key,
|
|
46065
|
+
passphrase
|
|
46066
|
+
},
|
|
46067
|
+
secrets: [password, ...cert ? [cert] : [], ...key ? [key] : [], ...passphrase ? [passphrase] : []]
|
|
46068
|
+
};
|
|
46069
|
+
}
|
|
46070
|
+
function readHost2(value) {
|
|
46071
|
+
const host = readRequiredString(value, "host", 253);
|
|
46072
|
+
const ipVersion = isIP4(host);
|
|
46073
|
+
if (ipVersion !== 0) {
|
|
46074
|
+
if (!isPublicNetworkAddress(host)) throw new Error("RabbitMQ host is not a public network target");
|
|
46075
|
+
return host;
|
|
46076
|
+
}
|
|
46077
|
+
const normalized = host.toLowerCase();
|
|
46078
|
+
if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@:\[\]]/.test(host)) {
|
|
46079
|
+
throw new Error("RabbitMQ host must be a public hostname or IP address without a scheme or port");
|
|
46080
|
+
}
|
|
46081
|
+
return host;
|
|
46082
|
+
}
|
|
46083
|
+
function readPublishOptions(args, idempotencyKey) {
|
|
46084
|
+
const expirationMs = args.expirationMs === void 0 ? void 0 : readBoundedInteger(args.expirationMs, 0, 1, 6048e5, "expirationMs");
|
|
46085
|
+
const priority = args.priority === void 0 ? void 0 : readBoundedInteger(args.priority, 0, 0, 255, "priority");
|
|
46086
|
+
return {
|
|
46087
|
+
mandatory: true,
|
|
46088
|
+
persistent: readBoolean(args.persistent, true, "persistent"),
|
|
46089
|
+
contentType: "application/json",
|
|
46090
|
+
contentEncoding: "utf-8",
|
|
46091
|
+
messageId: createHash7("sha256").update(idempotencyKey).digest("hex"),
|
|
46092
|
+
timestamp: Math.floor(Date.now() / 1e3),
|
|
46093
|
+
appId: "tangle-integration-hub",
|
|
46094
|
+
expiration: expirationMs === void 0 ? void 0 : String(expirationMs),
|
|
46095
|
+
priority
|
|
46096
|
+
};
|
|
46097
|
+
}
|
|
46098
|
+
function readJsonMessage(value) {
|
|
46099
|
+
if (value === void 0) throw new Error("data is required");
|
|
46100
|
+
let serialized;
|
|
46101
|
+
try {
|
|
46102
|
+
serialized = JSON.stringify(value);
|
|
46103
|
+
} catch {
|
|
46104
|
+
throw new Error("data must be JSON-serializable");
|
|
46105
|
+
}
|
|
46106
|
+
if (serialized === void 0) throw new Error("data must be JSON-serializable");
|
|
46107
|
+
const body = Buffer.from(serialized);
|
|
46108
|
+
if (body.byteLength > MAX_MESSAGE_BYTES3) throw new Error(`data exceeds the ${MAX_MESSAGE_BYTES3}-byte limit`);
|
|
46109
|
+
return body;
|
|
46110
|
+
}
|
|
46111
|
+
function readName2(value, label) {
|
|
46112
|
+
const name = readRequiredString(value, label, MAX_NAME_BYTES2);
|
|
46113
|
+
if (Buffer.byteLength(name, "utf8") > MAX_NAME_BYTES2 || /[\u0000-\u001f\u007f]/.test(name)) {
|
|
46114
|
+
throw new Error(`${label} must be at most ${MAX_NAME_BYTES2} bytes without control characters`);
|
|
46115
|
+
}
|
|
46116
|
+
return name;
|
|
46117
|
+
}
|
|
46118
|
+
function readRoutingKey(value) {
|
|
46119
|
+
if (value === void 0 || value === null || value === "") return "";
|
|
46120
|
+
return readName2(value, "routingKey");
|
|
46121
|
+
}
|
|
46122
|
+
function readRequiredString(value, label, maxLength) {
|
|
46123
|
+
if (typeof value !== "string" || value.length === 0 || value.length > maxLength) {
|
|
46124
|
+
throw new Error(`${label} must be a non-empty string no longer than ${maxLength} characters`);
|
|
46125
|
+
}
|
|
46126
|
+
return value;
|
|
46127
|
+
}
|
|
46128
|
+
function readPem(value, label, allowArray) {
|
|
46129
|
+
if (value === void 0 || value === null) return void 0;
|
|
46130
|
+
const values = Array.isArray(value) && allowArray ? value : [value];
|
|
46131
|
+
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) {
|
|
46132
|
+
throw new Error(
|
|
46133
|
+
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`
|
|
46134
|
+
);
|
|
46135
|
+
}
|
|
46136
|
+
return allowArray ? values : values[0];
|
|
46137
|
+
}
|
|
46138
|
+
function credentialSecrets2(source) {
|
|
46139
|
+
try {
|
|
46140
|
+
return readCredentials3(source).secrets;
|
|
46141
|
+
} catch {
|
|
46142
|
+
return [];
|
|
46143
|
+
}
|
|
46144
|
+
}
|
|
46145
|
+
function safeErrorMessage2(error, secrets) {
|
|
46146
|
+
let message = error instanceof Error ? error.message : String(error);
|
|
46147
|
+
for (const secret of secrets) {
|
|
46148
|
+
if (secret.length >= 4) message = message.replaceAll(secret, "[REDACTED]");
|
|
46149
|
+
}
|
|
46150
|
+
return message;
|
|
46151
|
+
}
|
|
46152
|
+
function readCapability7(name, description, parameters) {
|
|
46153
|
+
return { name, class: "read", description, parameters };
|
|
46154
|
+
}
|
|
46155
|
+
function mutationCapability6(name, description, parameters) {
|
|
46156
|
+
return { name, class: "mutation", description, parameters, cas: "none", externalEffect: true };
|
|
46157
|
+
}
|
|
46158
|
+
function nameSchema(label) {
|
|
46159
|
+
return { type: "string", minLength: 1, maxLength: MAX_NAME_BYTES2, description: `Existing RabbitMQ ${label} name.` };
|
|
46160
|
+
}
|
|
46161
|
+
|
|
46162
|
+
// src/connectors/adapters/redis.ts
|
|
46163
|
+
import { isIP as isIP5 } from "net";
|
|
46164
|
+
import {
|
|
46165
|
+
createClient as createRedisClient
|
|
46166
|
+
} from "redis";
|
|
46167
|
+
var MAX_KEY_BYTES = 1024;
|
|
46168
|
+
var MAX_PATTERN_BYTES = 4096;
|
|
46169
|
+
var MAX_VALUE_BYTES = 1024 * 1024;
|
|
46170
|
+
var MAX_SCAN_KEYS = 1e3;
|
|
46171
|
+
var MAX_SCAN_BYTES = 1024 * 1024;
|
|
46172
|
+
var MAX_TLS_CA_BYTES2 = 256 * 1024;
|
|
46173
|
+
var MAX_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
46174
|
+
var SCAN_SCRIPT = `
|
|
46175
|
+
local page
|
|
46176
|
+
if ARGV[4] == '' then
|
|
46177
|
+
page = redis.call('SCAN', ARGV[1], 'MATCH', ARGV[2], 'COUNT', ARGV[3])
|
|
46178
|
+
else
|
|
46179
|
+
page = redis.call('SCAN', ARGV[1], 'MATCH', ARGV[2], 'COUNT', ARGV[3], 'TYPE', ARGV[4])
|
|
46180
|
+
end
|
|
46181
|
+
local keys = page[2]
|
|
46182
|
+
if #keys > tonumber(ARGV[5]) then return redis.error_reply('Redis SCAN page exceeds the configured key limit') end
|
|
46183
|
+
local bytes = 0
|
|
46184
|
+
for _, key in ipairs(keys) do
|
|
46185
|
+
if string.len(key) > tonumber(ARGV[7]) then return redis.error_reply('Redis SCAN key exceeds the configured key limit') end
|
|
46186
|
+
bytes = bytes + string.len(key)
|
|
46187
|
+
if bytes > tonumber(ARGV[6]) then return redis.error_reply('Redis SCAN page exceeds the configured byte limit') end
|
|
46188
|
+
end
|
|
46189
|
+
return page
|
|
46190
|
+
`;
|
|
46191
|
+
var INSPECT_SCRIPT = `
|
|
46192
|
+
local kind = redis.call('TYPE', KEYS[1])
|
|
46193
|
+
if type(kind) == 'table' then kind = kind['ok'] end
|
|
46194
|
+
return { kind, redis.call('PTTL', KEYS[1]) }
|
|
46195
|
+
`;
|
|
46196
|
+
var READ_STRING_SCRIPT = `
|
|
46197
|
+
local kind = redis.call('TYPE', KEYS[1])
|
|
46198
|
+
if type(kind) == 'table' then kind = kind['ok'] end
|
|
46199
|
+
local ttl = redis.call('PTTL', KEYS[1])
|
|
46200
|
+
if kind == 'none' then return { kind, ttl, false, 0 } end
|
|
46201
|
+
if kind ~= 'string' then return redis.error_reply('WRONGTYPE key does not contain a string') end
|
|
46202
|
+
local length = redis.call('STRLEN', KEYS[1])
|
|
46203
|
+
if length > tonumber(ARGV[1]) then return redis.error_reply('Redis value exceeds the configured read limit') end
|
|
46204
|
+
return { kind, ttl, redis.call('GET', KEYS[1]), length }
|
|
46205
|
+
`;
|
|
46206
|
+
var COMPARE_SET_SCRIPT = `
|
|
46207
|
+
if redis.call('GET', KEYS[1]) ~= ARGV[1] then return 0 end
|
|
46208
|
+
if ARGV[3] ~= '' then
|
|
46209
|
+
redis.call('SET', KEYS[1], ARGV[2], 'PX', ARGV[3])
|
|
46210
|
+
elseif ARGV[4] == '1' then
|
|
46211
|
+
redis.call('SET', KEYS[1], ARGV[2], 'KEEPTTL')
|
|
46212
|
+
else
|
|
46213
|
+
redis.call('SET', KEYS[1], ARGV[2])
|
|
46214
|
+
end
|
|
46215
|
+
return 1
|
|
46216
|
+
`;
|
|
46217
|
+
var COMPARE_DELETE_SCRIPT = `
|
|
46218
|
+
if redis.call('GET', KEYS[1]) ~= ARGV[1] then return 0 end
|
|
46219
|
+
return redis.call('DEL', KEYS[1])
|
|
46220
|
+
`;
|
|
46221
|
+
function createRedisConnector(options = {}) {
|
|
46222
|
+
const createClient = options.createClient ?? defaultCreateClient2;
|
|
46223
|
+
const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
|
|
46224
|
+
return {
|
|
46225
|
+
manifest: {
|
|
46226
|
+
kind: "redis",
|
|
46227
|
+
displayName: "Redis",
|
|
46228
|
+
description: "Inspect and scan Redis keys and perform bounded, approved string compare-and-swap mutations over verified TLS.",
|
|
46229
|
+
auth: {
|
|
46230
|
+
kind: "api-key",
|
|
46231
|
+
hint: "JSON with a public Redis host, password, optional ACL username/database, and optional TLS CA. Verified TLS is mandatory."
|
|
46232
|
+
},
|
|
46233
|
+
defaultConsistencyModel: "authoritative",
|
|
46234
|
+
category: "database",
|
|
46235
|
+
rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
|
|
46236
|
+
capabilities: [
|
|
46237
|
+
readCapability8("redis.keys.scan", "Read one bounded SCAN page without blocking the server with KEYS.", {
|
|
46238
|
+
type: "object",
|
|
46239
|
+
properties: {
|
|
46240
|
+
cursor: { type: "string", pattern: "^[0-9]+$", default: "0" },
|
|
46241
|
+
match: { type: "string", maxLength: MAX_PATTERN_BYTES, default: "*" },
|
|
46242
|
+
count: { type: "integer", minimum: 1, maximum: MAX_SCAN_KEYS, default: 100 },
|
|
46243
|
+
type: { type: "string", enum: ["string", "list", "set", "zset", "hash", "stream"] }
|
|
46244
|
+
},
|
|
46245
|
+
additionalProperties: false
|
|
46246
|
+
}),
|
|
46247
|
+
readCapability8("redis.key.inspect", "Read a key type and remaining expiry atomically.", keySchema()),
|
|
46248
|
+
readCapability8("redis.string.get", "Read one bounded string value, type, and remaining expiry atomically.", keySchema()),
|
|
46249
|
+
mutationCapability7("redis.string.set", "Set a bounded string only when the key is absent, present, or matches an expected value.", {
|
|
46250
|
+
type: "object",
|
|
46251
|
+
properties: {
|
|
46252
|
+
key: keyParameter(),
|
|
46253
|
+
value: { type: "string", maxLength: MAX_VALUE_BYTES },
|
|
46254
|
+
condition: { type: "string", enum: ["absent", "present", "matches"] },
|
|
46255
|
+
expectedValue: { type: "string", maxLength: MAX_VALUE_BYTES },
|
|
46256
|
+
ttlMs: { type: "integer", minimum: 1, maximum: MAX_TTL_MS },
|
|
46257
|
+
keepTtl: { type: "boolean", default: false }
|
|
46258
|
+
},
|
|
46259
|
+
required: ["key", "value", "condition"],
|
|
46260
|
+
additionalProperties: false
|
|
46261
|
+
}),
|
|
46262
|
+
mutationCapability7("redis.string.delete", "Delete a string key only when its current value exactly matches the caller expectation.", {
|
|
46263
|
+
type: "object",
|
|
46264
|
+
properties: {
|
|
46265
|
+
key: keyParameter(),
|
|
46266
|
+
expectedValue: { type: "string", maxLength: MAX_VALUE_BYTES }
|
|
46267
|
+
},
|
|
46268
|
+
required: ["key", "expectedValue"],
|
|
46269
|
+
additionalProperties: false
|
|
46270
|
+
})
|
|
46271
|
+
]
|
|
46272
|
+
},
|
|
46273
|
+
async test(source) {
|
|
46274
|
+
try {
|
|
46275
|
+
await withClient2(source, createClient, resolveHost, async (client) => {
|
|
46276
|
+
const response = await client.ping();
|
|
46277
|
+
if (response !== "PONG") throw new Error("Redis PING returned an unexpected response");
|
|
46278
|
+
});
|
|
46279
|
+
return { ok: true };
|
|
46280
|
+
} catch (error) {
|
|
46281
|
+
return { ok: false, reason: safeErrorMessage3(error, credentialSecrets3(source)) };
|
|
46282
|
+
}
|
|
46283
|
+
},
|
|
46284
|
+
async executeRead({ source, capabilityName, args }) {
|
|
46285
|
+
if (capabilityName === "redis.keys.scan") {
|
|
46286
|
+
const cursor = readCursor2(args.cursor);
|
|
46287
|
+
const match = readPattern(args.match);
|
|
46288
|
+
const count = readBoundedInteger(args.count, 100, 1, MAX_SCAN_KEYS, "count");
|
|
46289
|
+
const type = readScanType(args.type);
|
|
46290
|
+
const data4 = await withClient2(source, createClient, resolveHost, async (client) => {
|
|
46291
|
+
const reply = readArrayReply(await client.eval(SCAN_SCRIPT, {
|
|
46292
|
+
keys: [],
|
|
46293
|
+
arguments: [
|
|
46294
|
+
cursor,
|
|
46295
|
+
match,
|
|
46296
|
+
String(count),
|
|
46297
|
+
type ?? "",
|
|
46298
|
+
String(MAX_SCAN_KEYS),
|
|
46299
|
+
String(MAX_SCAN_BYTES),
|
|
46300
|
+
String(MAX_KEY_BYTES)
|
|
46301
|
+
]
|
|
46302
|
+
}), "scan");
|
|
46303
|
+
const keys = readStringArray(reply[1], "scan keys");
|
|
46304
|
+
assertScanPage(keys);
|
|
46305
|
+
return { cursor: readCursor2(readReplyString(reply[0], "cursor")), keys };
|
|
46306
|
+
});
|
|
46307
|
+
return { data: data4, fetchedAt: Date.now() };
|
|
46308
|
+
}
|
|
46309
|
+
if (capabilityName === "redis.key.inspect") {
|
|
46310
|
+
const key = readKey(args.key);
|
|
46311
|
+
const data4 = await withClient2(source, createClient, resolveHost, async (client) => {
|
|
46312
|
+
const reply = readArrayReply(await client.eval(INSPECT_SCRIPT, { keys: [key], arguments: [] }), "inspect");
|
|
46313
|
+
const type = readReplyString(reply[0], "type");
|
|
46314
|
+
return { key, exists: type !== "none", type, ttlMs: normalizeTtl(reply[1]) };
|
|
46315
|
+
});
|
|
46316
|
+
return { data: data4, fetchedAt: Date.now() };
|
|
46317
|
+
}
|
|
46318
|
+
if (capabilityName === "redis.string.get") {
|
|
46319
|
+
const key = readKey(args.key);
|
|
46320
|
+
const data4 = await withClient2(source, createClient, resolveHost, async (client) => {
|
|
46321
|
+
const reply = readArrayReply(await client.eval(READ_STRING_SCRIPT, {
|
|
46322
|
+
keys: [key],
|
|
46323
|
+
arguments: [String(MAX_VALUE_BYTES)]
|
|
46324
|
+
}), "string read");
|
|
46325
|
+
const type = readReplyString(reply[0], "type");
|
|
46326
|
+
const exists = type !== "none";
|
|
46327
|
+
const value = exists ? readReplyString(reply[2], "value") : null;
|
|
46328
|
+
if (value !== null && Buffer.byteLength(value, "utf8") > MAX_VALUE_BYTES) {
|
|
46329
|
+
throw new Error(`Redis value exceeds the ${MAX_VALUE_BYTES}-byte limit`);
|
|
46330
|
+
}
|
|
46331
|
+
return {
|
|
46332
|
+
key,
|
|
46333
|
+
exists,
|
|
46334
|
+
type,
|
|
46335
|
+
ttlMs: normalizeTtl(reply[1]),
|
|
46336
|
+
value,
|
|
46337
|
+
byteLength: exists ? readReplyInteger(reply[3], "byteLength") : 0
|
|
46338
|
+
};
|
|
46339
|
+
});
|
|
46340
|
+
return { data: data4, fetchedAt: Date.now() };
|
|
46341
|
+
}
|
|
46342
|
+
throw new Error(`Unknown Redis read capability: ${capabilityName}`);
|
|
46343
|
+
},
|
|
46344
|
+
async executeMutation({ source, capabilityName, args }) {
|
|
46345
|
+
if (capabilityName === "redis.string.set") {
|
|
46346
|
+
const input = readSetInput(args);
|
|
46347
|
+
return withClient2(source, createClient, resolveHost, async (client) => {
|
|
46348
|
+
let committed;
|
|
46349
|
+
if (input.condition === "matches") {
|
|
46350
|
+
committed = readReplyInteger(await client.eval(COMPARE_SET_SCRIPT, {
|
|
46351
|
+
keys: [input.key],
|
|
46352
|
+
arguments: [input.expectedValue, input.value, input.ttlMs === void 0 ? "" : String(input.ttlMs), input.keepTtl ? "1" : "0"]
|
|
46353
|
+
}), "compare-and-set result") === 1;
|
|
46354
|
+
} else {
|
|
46355
|
+
const response = await client.set(input.key, input.value, {
|
|
46356
|
+
condition: input.condition === "absent" ? "NX" : "XX",
|
|
46357
|
+
expiration: input.ttlMs === void 0 ? input.keepTtl ? "KEEPTTL" : void 0 : { type: "PX", value: input.ttlMs }
|
|
46358
|
+
});
|
|
46359
|
+
committed = response === "OK";
|
|
46360
|
+
}
|
|
46361
|
+
if (!committed) return conflict(`Redis key ${input.key} did not satisfy condition ${input.condition}`);
|
|
46362
|
+
return committedResult({ key: input.key, condition: input.condition, byteLength: Buffer.byteLength(input.value, "utf8") });
|
|
46363
|
+
});
|
|
46364
|
+
}
|
|
46365
|
+
if (capabilityName === "redis.string.delete") {
|
|
46366
|
+
const key = readKey(args.key);
|
|
46367
|
+
const expectedValue = readValue(args.expectedValue, "expectedValue");
|
|
46368
|
+
return withClient2(source, createClient, resolveHost, async (client) => {
|
|
46369
|
+
const deleted = readReplyInteger(await client.eval(COMPARE_DELETE_SCRIPT, {
|
|
46370
|
+
keys: [key],
|
|
46371
|
+
arguments: [expectedValue]
|
|
46372
|
+
}), "compare-and-delete result");
|
|
46373
|
+
if (deleted !== 1) return conflict(`Redis key ${key} did not match the expected value`);
|
|
46374
|
+
return committedResult({ key, deleted: true });
|
|
46375
|
+
});
|
|
46376
|
+
}
|
|
46377
|
+
throw new Error(`Unknown Redis mutation capability: ${capabilityName}`);
|
|
46378
|
+
}
|
|
46379
|
+
};
|
|
46380
|
+
}
|
|
46381
|
+
var redisConnector = createRedisConnector();
|
|
46382
|
+
function defaultCreateClient2(options) {
|
|
46383
|
+
return createRedisClient(options);
|
|
46384
|
+
}
|
|
46385
|
+
async function withClient2(source, createClient, resolveHost, run) {
|
|
46386
|
+
const credentials = readCredentials4(source);
|
|
46387
|
+
const addresses = await resolveHost(credentials.host);
|
|
46388
|
+
if (addresses.length === 0) throw new Error("Redis host did not resolve");
|
|
46389
|
+
let client;
|
|
46390
|
+
try {
|
|
46391
|
+
client = createClient({
|
|
46392
|
+
username: credentials.username,
|
|
46393
|
+
password: credentials.password,
|
|
46394
|
+
database: credentials.database,
|
|
46395
|
+
name: "tangle-integration-hub",
|
|
46396
|
+
disableOfflineQueue: true,
|
|
46397
|
+
commandsQueueMaxLength: 10,
|
|
46398
|
+
maintNotifications: "disabled",
|
|
46399
|
+
commandOptions: { abortSignal: AbortSignal.timeout(credentials.commandTimeoutMs) },
|
|
46400
|
+
socket: {
|
|
46401
|
+
host: addresses[0],
|
|
46402
|
+
port: credentials.port,
|
|
46403
|
+
tls: true,
|
|
46404
|
+
servername: isIP5(credentials.host) === 0 ? credentials.host : void 0,
|
|
46405
|
+
minVersion: "TLSv1.2",
|
|
46406
|
+
rejectUnauthorized: true,
|
|
46407
|
+
ca: credentials.tlsCa,
|
|
46408
|
+
connectTimeout: credentials.connectTimeoutMs,
|
|
46409
|
+
socketTimeout: credentials.commandTimeoutMs,
|
|
46410
|
+
reconnectStrategy: false
|
|
46411
|
+
}
|
|
46412
|
+
});
|
|
46413
|
+
client.on("error", () => void 0);
|
|
46414
|
+
await client.connect();
|
|
46415
|
+
return await run(client);
|
|
46416
|
+
} catch (error) {
|
|
46417
|
+
throw new Error(safeErrorMessage3(error, credentials.secrets));
|
|
46418
|
+
} finally {
|
|
46419
|
+
if (client?.isOpen) await client.close().catch(() => client?.destroy());
|
|
46420
|
+
else client?.destroy();
|
|
46421
|
+
}
|
|
46422
|
+
}
|
|
46423
|
+
function readCredentials4(source) {
|
|
46424
|
+
let raw;
|
|
46425
|
+
if (source.credentials.kind === "custom") raw = source.credentials.values;
|
|
46426
|
+
else if (source.credentials.kind === "api-key") {
|
|
46427
|
+
try {
|
|
46428
|
+
raw = JSON.parse(source.credentials.apiKey);
|
|
46429
|
+
} catch {
|
|
46430
|
+
throw new Error("Redis credential must be valid JSON");
|
|
46431
|
+
}
|
|
46432
|
+
} else {
|
|
46433
|
+
throw new Error("Redis requires a structured credential bundle");
|
|
46434
|
+
}
|
|
46435
|
+
if (!isPlainRecord(raw)) throw new Error("Redis credential must be a JSON object");
|
|
46436
|
+
const host = readHost3(raw.host);
|
|
46437
|
+
const username = readOptionalString(raw.username, "username");
|
|
46438
|
+
if (username && (username.length > 128 || /[\u0000-\u001f\u007f]/.test(username))) {
|
|
46439
|
+
throw new Error("Redis username must be at most 128 characters without control characters");
|
|
46440
|
+
}
|
|
46441
|
+
const password = readRequiredString2(raw.password, "password", 4096);
|
|
46442
|
+
const tlsCa = readOptionalString(raw.tlsCa, "tlsCa");
|
|
46443
|
+
if (tlsCa && Buffer.byteLength(tlsCa, "utf8") > MAX_TLS_CA_BYTES2) {
|
|
46444
|
+
throw new Error(`Redis tlsCa exceeds the ${MAX_TLS_CA_BYTES2}-byte limit`);
|
|
46445
|
+
}
|
|
46446
|
+
return {
|
|
46447
|
+
host,
|
|
46448
|
+
port: readBoundedInteger(raw.port, 6380, 1, 65535, "port"),
|
|
46449
|
+
username,
|
|
46450
|
+
password,
|
|
46451
|
+
database: readBoundedInteger(raw.database, 0, 0, 65535, "database"),
|
|
46452
|
+
tlsCa,
|
|
46453
|
+
connectTimeoutMs: readBoundedInteger(raw.connectTimeoutMs, 1e4, 1e3, 3e4, "connectTimeoutMs"),
|
|
46454
|
+
commandTimeoutMs: readBoundedInteger(raw.commandTimeoutMs, 1e4, 1e3, 3e4, "commandTimeoutMs"),
|
|
46455
|
+
secrets: [password]
|
|
46456
|
+
};
|
|
46457
|
+
}
|
|
46458
|
+
function readHost3(value) {
|
|
46459
|
+
const host = readRequiredString2(value, "host", 253);
|
|
46460
|
+
const ipVersion = isIP5(host);
|
|
46461
|
+
if (ipVersion !== 0) {
|
|
46462
|
+
if (!isPublicNetworkAddress(host)) throw new Error("Redis host is not a public network target");
|
|
46463
|
+
return host;
|
|
46464
|
+
}
|
|
46465
|
+
const normalized = host.toLowerCase();
|
|
46466
|
+
if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@:\[\]]/.test(host)) {
|
|
46467
|
+
throw new Error("Redis host must be a public hostname or IP address without a scheme or port");
|
|
46468
|
+
}
|
|
46469
|
+
return host;
|
|
46470
|
+
}
|
|
46471
|
+
function readSetInput(args) {
|
|
46472
|
+
const key = readKey(args.key);
|
|
46473
|
+
const value = readValue(args.value, "value");
|
|
46474
|
+
const condition = args.condition;
|
|
46475
|
+
if (!["absent", "present", "matches"].includes(String(condition))) {
|
|
46476
|
+
throw new Error("condition must be absent, present, or matches");
|
|
46477
|
+
}
|
|
46478
|
+
const expectedValue = args.expectedValue === void 0 ? void 0 : readValue(args.expectedValue, "expectedValue");
|
|
46479
|
+
if (condition === "matches" && expectedValue === void 0) throw new Error("expectedValue is required when condition is matches");
|
|
46480
|
+
if (condition !== "matches" && expectedValue !== void 0) throw new Error("expectedValue is only valid when condition is matches");
|
|
46481
|
+
const ttlMs = args.ttlMs === void 0 ? void 0 : readBoundedInteger(args.ttlMs, 0, 1, MAX_TTL_MS, "ttlMs");
|
|
46482
|
+
const keepTtl = readBoolean(args.keepTtl, false, "keepTtl");
|
|
46483
|
+
if (ttlMs !== void 0 && keepTtl) throw new Error("ttlMs and keepTtl cannot be used together");
|
|
46484
|
+
return { key, value, condition, expectedValue, ttlMs, keepTtl };
|
|
46485
|
+
}
|
|
46486
|
+
function readCursor2(value) {
|
|
46487
|
+
if (value === void 0 || value === null || value === "") return "0";
|
|
46488
|
+
if (typeof value !== "string" || !/^[0-9]+$/.test(value) || value.length > 32) {
|
|
46489
|
+
throw new Error("cursor must be a Redis numeric cursor string");
|
|
46490
|
+
}
|
|
46491
|
+
return value;
|
|
46492
|
+
}
|
|
46493
|
+
function readPattern(value) {
|
|
46494
|
+
if (value === void 0 || value === null || value === "") return "*";
|
|
46495
|
+
if (typeof value !== "string" || Buffer.byteLength(value, "utf8") > MAX_PATTERN_BYTES || /[\u0000-\u001f\u007f]/.test(value)) {
|
|
46496
|
+
throw new Error(`match must be under ${MAX_PATTERN_BYTES} bytes without control characters`);
|
|
46497
|
+
}
|
|
46498
|
+
return value;
|
|
46499
|
+
}
|
|
46500
|
+
function readScanType(value) {
|
|
46501
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
46502
|
+
if (!["string", "list", "set", "zset", "hash", "stream"].includes(String(value))) {
|
|
46503
|
+
throw new Error("type must be string, list, set, zset, hash, or stream");
|
|
46504
|
+
}
|
|
46505
|
+
return String(value);
|
|
46506
|
+
}
|
|
46507
|
+
function assertScanPage(keys) {
|
|
46508
|
+
if (!Array.isArray(keys) || keys.length > MAX_SCAN_KEYS) {
|
|
46509
|
+
throw new Error(`Redis SCAN page exceeds ${MAX_SCAN_KEYS} keys`);
|
|
46510
|
+
}
|
|
46511
|
+
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) {
|
|
46512
|
+
throw new Error(`Redis SCAN page exceeds the ${MAX_SCAN_BYTES}-byte limit`);
|
|
46513
|
+
}
|
|
46514
|
+
}
|
|
46515
|
+
function readKey(value) {
|
|
46516
|
+
return readBoundedString(value, "key", MAX_KEY_BYTES);
|
|
46517
|
+
}
|
|
46518
|
+
function readValue(value, label) {
|
|
46519
|
+
if (typeof value !== "string") throw new Error(`${label} must be a string`);
|
|
46520
|
+
if (Buffer.byteLength(value, "utf8") > MAX_VALUE_BYTES) {
|
|
46521
|
+
throw new Error(`${label} exceeds the ${MAX_VALUE_BYTES}-byte limit`);
|
|
46522
|
+
}
|
|
46523
|
+
return value;
|
|
46524
|
+
}
|
|
46525
|
+
function readBoundedString(value, label, maxBytes) {
|
|
46526
|
+
if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > maxBytes || /[\u0000-\u001f\u007f]/.test(value)) {
|
|
46527
|
+
throw new Error(`${label} must be a non-empty string under ${maxBytes} bytes without control characters`);
|
|
46528
|
+
}
|
|
46529
|
+
return value;
|
|
46530
|
+
}
|
|
46531
|
+
function readRequiredString2(value, label, maxLength) {
|
|
46532
|
+
if (typeof value !== "string" || value.length === 0 || value.length > maxLength) {
|
|
46533
|
+
throw new Error(`${label} must be a non-empty string no longer than ${maxLength} characters`);
|
|
46534
|
+
}
|
|
46535
|
+
return value;
|
|
46536
|
+
}
|
|
46537
|
+
function readArrayReply(value, label) {
|
|
46538
|
+
if (!Array.isArray(value)) throw new Error(`Redis ${label} returned a malformed response`);
|
|
46539
|
+
return value;
|
|
46540
|
+
}
|
|
46541
|
+
function readStringArray(value, label) {
|
|
46542
|
+
if (!Array.isArray(value)) throw new Error(`Redis response ${label} is malformed`);
|
|
46543
|
+
return value.map((entry) => readReplyString(entry, label));
|
|
46544
|
+
}
|
|
46545
|
+
function readReplyString(value, label) {
|
|
46546
|
+
if (typeof value === "string") return value;
|
|
46547
|
+
if (Buffer.isBuffer(value)) return value.toString("utf8");
|
|
46548
|
+
throw new Error(`Redis response ${label} is malformed`);
|
|
46549
|
+
}
|
|
46550
|
+
function readReplyInteger(value, label) {
|
|
46551
|
+
const parsed = typeof value === "bigint" ? Number(value) : typeof value === "string" ? Number(value) : value;
|
|
46552
|
+
if (typeof parsed !== "number" || !Number.isSafeInteger(parsed)) throw new Error(`Redis response ${label} is malformed`);
|
|
46553
|
+
return parsed;
|
|
46554
|
+
}
|
|
46555
|
+
function normalizeTtl(value) {
|
|
46556
|
+
const ttl = readReplyInteger(value, "ttlMs");
|
|
46557
|
+
return ttl >= 0 ? ttl : null;
|
|
46558
|
+
}
|
|
46559
|
+
function safeErrorMessage3(error, secrets) {
|
|
46560
|
+
let message = error instanceof Error ? error.message : String(error);
|
|
46561
|
+
for (const secret of secrets) {
|
|
46562
|
+
if (secret.length >= 4) message = message.replaceAll(secret, "[REDACTED]");
|
|
46563
|
+
}
|
|
46564
|
+
return message;
|
|
46565
|
+
}
|
|
46566
|
+
function credentialSecrets3(source) {
|
|
46567
|
+
try {
|
|
46568
|
+
return readCredentials4(source).secrets;
|
|
46569
|
+
} catch {
|
|
46570
|
+
return [];
|
|
46571
|
+
}
|
|
46572
|
+
}
|
|
46573
|
+
function committedResult(data4) {
|
|
46574
|
+
return { status: "committed", data: data4, committedAt: Date.now(), idempotentReplay: false };
|
|
46575
|
+
}
|
|
46576
|
+
function conflict(message) {
|
|
46577
|
+
return { status: "conflict", alternatives: [], currentState: null, message };
|
|
46578
|
+
}
|
|
46579
|
+
function readCapability8(name, description, parameters) {
|
|
46580
|
+
return { name, class: "read", description, parameters };
|
|
46581
|
+
}
|
|
46582
|
+
function mutationCapability7(name, description, parameters) {
|
|
46583
|
+
return { name, class: "mutation", description, parameters, cas: "optimistic-read-verify", externalEffect: true };
|
|
46584
|
+
}
|
|
46585
|
+
function keyParameter() {
|
|
46586
|
+
return { type: "string", minLength: 1, maxLength: MAX_KEY_BYTES };
|
|
46587
|
+
}
|
|
46588
|
+
function keySchema() {
|
|
46589
|
+
return {
|
|
46590
|
+
type: "object",
|
|
46591
|
+
properties: { key: keyParameter() },
|
|
46592
|
+
required: ["key"],
|
|
46593
|
+
additionalProperties: false
|
|
46594
|
+
};
|
|
46595
|
+
}
|
|
46596
|
+
|
|
46597
|
+
// src/connectors/adapters/redshift.ts
|
|
46598
|
+
import { isIP as isIP6 } from "net";
|
|
46599
|
+
import {
|
|
46600
|
+
Client as PgClient
|
|
46601
|
+
} from "pg";
|
|
46602
|
+
var MAX_IDENTIFIER_BYTES = 127;
|
|
46603
|
+
var MAX_COLUMNS5 = 64;
|
|
46604
|
+
var MAX_FILTERS2 = 20;
|
|
46605
|
+
var MAX_ORDER_COLUMNS = 8;
|
|
46606
|
+
var MAX_PARAMETERS_BYTES = 10 * 1024 * 1024;
|
|
46607
|
+
var MAX_RESULT_ROWS = 1e4;
|
|
46608
|
+
var MAX_RESULT_BYTES3 = 10 * 1024 * 1024;
|
|
46609
|
+
var MAX_TLS_CA_BYTES3 = 256 * 1024;
|
|
46610
|
+
var REDSHIFT_PROVIDER = {
|
|
46611
|
+
kind: "redshift",
|
|
46612
|
+
displayName: "Amazon Redshift",
|
|
46613
|
+
description: "Inspect Redshift schemas and tables and run bounded structured row reads over the verified PostgreSQL wire protocol.",
|
|
46614
|
+
authHint: "JSON with a public Redshift host, database, user, password, optional port, and optional TLS CA. Verified TLS is mandatory.",
|
|
46615
|
+
defaultPort: 5439
|
|
46616
|
+
};
|
|
46617
|
+
function createRedshiftConnector(options = {}) {
|
|
46618
|
+
return createPostgresWireReadConnector(REDSHIFT_PROVIDER, options);
|
|
46619
|
+
}
|
|
46620
|
+
function createPostgresWireReadConnector(provider2, options = {}) {
|
|
46621
|
+
const createClient = options.createClient ?? defaultCreateClient3;
|
|
46622
|
+
const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
|
|
46623
|
+
return {
|
|
46624
|
+
manifest: {
|
|
46625
|
+
kind: provider2.kind,
|
|
46626
|
+
displayName: provider2.displayName,
|
|
46627
|
+
description: provider2.description,
|
|
46628
|
+
auth: {
|
|
46629
|
+
kind: "api-key",
|
|
46630
|
+
hint: provider2.authHint
|
|
46631
|
+
},
|
|
46632
|
+
defaultConsistencyModel: "authoritative",
|
|
46633
|
+
category: "database",
|
|
46634
|
+
rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
|
|
46635
|
+
capabilities: [
|
|
46636
|
+
readCapability9(`${provider2.kind}.schemas.list`, `List schemas visible to the connected ${provider2.displayName} user.`, emptySchema2()),
|
|
46637
|
+
readCapability9(`${provider2.kind}.tables.list`, "List tables and views in one schema.", {
|
|
46638
|
+
type: "object",
|
|
46639
|
+
properties: { schema: identifierSchema("Schema name; defaults to public.") },
|
|
46640
|
+
additionalProperties: false
|
|
46641
|
+
}),
|
|
46642
|
+
readCapability9(`${provider2.kind}.tables.describe`, "Read ordered column metadata for one table or view.", {
|
|
46643
|
+
type: "object",
|
|
46644
|
+
properties: {
|
|
46645
|
+
schema: identifierSchema("Schema name; defaults to public."),
|
|
46646
|
+
table: identifierSchema("Table or view name.")
|
|
46647
|
+
},
|
|
46648
|
+
required: ["table"],
|
|
46649
|
+
additionalProperties: false
|
|
46650
|
+
}),
|
|
46651
|
+
readCapability9(`${provider2.kind}.rows.select`, "Read bounded rows using an identifier-safe query builder with scalar predicates.", {
|
|
46652
|
+
type: "object",
|
|
46653
|
+
properties: {
|
|
46654
|
+
schema: identifierSchema("Schema name; defaults to public."),
|
|
46655
|
+
table: identifierSchema("Table or view name."),
|
|
46656
|
+
columns: {
|
|
46657
|
+
type: "array",
|
|
46658
|
+
minItems: 1,
|
|
46659
|
+
maxItems: MAX_COLUMNS5,
|
|
46660
|
+
uniqueItems: true,
|
|
46661
|
+
items: identifierSchema("Column name.")
|
|
46662
|
+
},
|
|
46663
|
+
filters: {
|
|
46664
|
+
type: "array",
|
|
46665
|
+
maxItems: MAX_FILTERS2,
|
|
46666
|
+
items: {
|
|
46667
|
+
type: "object",
|
|
46668
|
+
properties: {
|
|
46669
|
+
column: identifierSchema("Column name."),
|
|
46670
|
+
operator: { type: "string", enum: ["eq", "ne", "lt", "lte", "gt", "gte", "like", "is-null", "not-null"] },
|
|
46671
|
+
value: { type: ["string", "number", "boolean", "null"] }
|
|
46672
|
+
},
|
|
46673
|
+
required: ["column", "operator"],
|
|
46674
|
+
additionalProperties: false
|
|
46675
|
+
}
|
|
46676
|
+
},
|
|
46677
|
+
orderBy: {
|
|
46678
|
+
type: "array",
|
|
46679
|
+
maxItems: MAX_ORDER_COLUMNS,
|
|
46680
|
+
items: {
|
|
46681
|
+
type: "object",
|
|
46682
|
+
properties: {
|
|
46683
|
+
column: identifierSchema("Column name."),
|
|
46684
|
+
direction: { type: "string", enum: ["asc", "desc"], default: "asc" }
|
|
46685
|
+
},
|
|
46686
|
+
required: ["column"],
|
|
46687
|
+
additionalProperties: false
|
|
46688
|
+
}
|
|
46689
|
+
},
|
|
46690
|
+
limit: { type: "integer", minimum: 1, maximum: MAX_RESULT_ROWS, default: 100 },
|
|
46691
|
+
offset: { type: "integer", minimum: 0, maximum: 1e6, default: 0 }
|
|
46692
|
+
},
|
|
46693
|
+
required: ["table", "columns"],
|
|
46694
|
+
additionalProperties: false
|
|
46695
|
+
})
|
|
46696
|
+
]
|
|
46697
|
+
},
|
|
46698
|
+
async test(source) {
|
|
46699
|
+
try {
|
|
46700
|
+
await withClient3(source, provider2, createClient, resolveHost, async (client) => {
|
|
46701
|
+
await client.query({ text: "SELECT current_database() AS database_name, current_user AS user_name", values: [] });
|
|
46702
|
+
});
|
|
46703
|
+
return { ok: true };
|
|
46704
|
+
} catch (error) {
|
|
46705
|
+
return { ok: false, reason: safeErrorMessage4(error, credentialSecrets4(source, provider2)) };
|
|
46706
|
+
}
|
|
46707
|
+
},
|
|
46708
|
+
async executeRead(inv) {
|
|
46709
|
+
const query2 = buildQuery(inv, provider2);
|
|
46710
|
+
const data4 = await withClient3(inv.source, provider2, createClient, resolveHost, async (client) => {
|
|
46711
|
+
await client.query({ text: "BEGIN READ ONLY", values: [] });
|
|
46712
|
+
try {
|
|
46713
|
+
const result = await client.query(query2);
|
|
46714
|
+
return boundedResult2(result, provider2.displayName);
|
|
46715
|
+
} finally {
|
|
46716
|
+
await client.query({ text: "ROLLBACK", values: [] }).catch(() => void 0);
|
|
46717
|
+
}
|
|
46718
|
+
});
|
|
46719
|
+
return { data: data4, fetchedAt: Date.now() };
|
|
46720
|
+
}
|
|
46721
|
+
};
|
|
46722
|
+
}
|
|
46723
|
+
var redshiftConnector = createRedshiftConnector();
|
|
46724
|
+
function defaultCreateClient3(config) {
|
|
46725
|
+
return new PgClient(config);
|
|
46726
|
+
}
|
|
46727
|
+
async function withClient3(source, provider2, createClient, resolveHost, run) {
|
|
46728
|
+
const credentials = readCredentials5(source, provider2);
|
|
46729
|
+
const addresses = await resolveHost(credentials.host);
|
|
46730
|
+
if (addresses.length === 0) throw new Error(`${provider2.displayName} host did not resolve`);
|
|
46731
|
+
let client;
|
|
46732
|
+
try {
|
|
46733
|
+
client = createClient({
|
|
46734
|
+
host: addresses[0],
|
|
46735
|
+
port: credentials.port,
|
|
46736
|
+
user: credentials.user,
|
|
46737
|
+
password: credentials.password,
|
|
46738
|
+
database: credentials.database,
|
|
46739
|
+
application_name: "tangle-integration-hub",
|
|
46740
|
+
connectionTimeoutMillis: credentials.connectTimeoutMs,
|
|
46741
|
+
query_timeout: credentials.queryTimeoutMs,
|
|
46742
|
+
statement_timeout: credentials.queryTimeoutMs,
|
|
46743
|
+
lock_timeout: credentials.queryTimeoutMs,
|
|
46744
|
+
keepAlive: true,
|
|
46745
|
+
keepAliveInitialDelayMillis: 6e4,
|
|
46746
|
+
ssl: {
|
|
46747
|
+
servername: isIP6(credentials.host) === 0 ? credentials.host : void 0,
|
|
46748
|
+
minVersion: "TLSv1.2",
|
|
46749
|
+
rejectUnauthorized: true,
|
|
46750
|
+
ca: credentials.tlsCa
|
|
46751
|
+
}
|
|
46752
|
+
});
|
|
46753
|
+
client.on("error", () => void 0);
|
|
46754
|
+
await client.connect();
|
|
46755
|
+
return await run(client);
|
|
46756
|
+
} catch (error) {
|
|
46757
|
+
throw new Error(safeErrorMessage4(error, credentials.secrets));
|
|
46758
|
+
} finally {
|
|
46759
|
+
await client?.end().catch(() => void 0);
|
|
46760
|
+
}
|
|
46761
|
+
}
|
|
46762
|
+
function readCredentials5(source, provider2) {
|
|
46763
|
+
let raw;
|
|
46764
|
+
if (source.credentials.kind === "custom") raw = source.credentials.values;
|
|
46765
|
+
else if (source.credentials.kind === "api-key") {
|
|
46766
|
+
try {
|
|
46767
|
+
raw = JSON.parse(source.credentials.apiKey);
|
|
46768
|
+
} catch {
|
|
46769
|
+
throw new Error(`${provider2.displayName} credential must be valid JSON`);
|
|
46770
|
+
}
|
|
46771
|
+
} else {
|
|
46772
|
+
throw new Error(`${provider2.displayName} requires a structured credential bundle`);
|
|
46773
|
+
}
|
|
46774
|
+
if (!isPlainRecord(raw)) throw new Error(`${provider2.displayName} credential must be a JSON object`);
|
|
46775
|
+
const host = readHost4(raw.host, provider2.displayName);
|
|
46776
|
+
const user = readCredentialString2(raw.user ?? raw.username, "user", 128, provider2.displayName);
|
|
46777
|
+
const password = readCredentialString2(raw.password, "password", 4096, provider2.displayName);
|
|
46778
|
+
const database = readCredentialString2(raw.database, "database", MAX_IDENTIFIER_BYTES, provider2.displayName);
|
|
46779
|
+
const tlsCa = readOptionalString(raw.tlsCa, "tlsCa");
|
|
46780
|
+
if (tlsCa && Buffer.byteLength(tlsCa, "utf8") > MAX_TLS_CA_BYTES3) {
|
|
46781
|
+
throw new Error(`${provider2.displayName} tlsCa exceeds the ${MAX_TLS_CA_BYTES3}-byte limit`);
|
|
46782
|
+
}
|
|
46783
|
+
return {
|
|
46784
|
+
host,
|
|
46785
|
+
port: readBoundedInteger(raw.port, provider2.defaultPort, 1, 65535, "port"),
|
|
46786
|
+
user,
|
|
46787
|
+
password,
|
|
46788
|
+
database,
|
|
46789
|
+
tlsCa,
|
|
46790
|
+
connectTimeoutMs: readBoundedInteger(raw.connectTimeoutMs, 1e4, 1e3, 3e4, "connectTimeoutMs"),
|
|
46791
|
+
queryTimeoutMs: readBoundedInteger(raw.queryTimeoutMs, 3e4, 1e3, 6e4, "queryTimeoutMs"),
|
|
46792
|
+
secrets: [password]
|
|
46793
|
+
};
|
|
46794
|
+
}
|
|
46795
|
+
function buildQuery(inv, provider2) {
|
|
46796
|
+
if (inv.capabilityName === `${provider2.kind}.schemas.list`) {
|
|
46797
|
+
return {
|
|
46798
|
+
text: `SELECT schema_name AS "schemaName", schema_owner AS "schemaOwner" FROM information_schema.schemata WHERE schema_name NOT LIKE 'pg_%' AND schema_name <> 'information_schema' ORDER BY schema_name LIMIT ${MAX_RESULT_ROWS + 1}`,
|
|
46799
|
+
values: []
|
|
46800
|
+
};
|
|
46801
|
+
}
|
|
46802
|
+
if (inv.capabilityName === `${provider2.kind}.tables.list`) {
|
|
46803
|
+
return {
|
|
46804
|
+
text: `SELECT table_schema AS "schemaName", table_name AS "tableName", table_type AS "tableType" FROM information_schema.tables WHERE table_schema = $1 ORDER BY table_name LIMIT ${MAX_RESULT_ROWS + 1}`,
|
|
46805
|
+
values: [readSchema(inv.args.schema)]
|
|
46806
|
+
};
|
|
46807
|
+
}
|
|
46808
|
+
if (inv.capabilityName === `${provider2.kind}.tables.describe`) {
|
|
46809
|
+
return {
|
|
46810
|
+
text: `SELECT table_schema AS "schemaName", table_name AS "tableName", column_name AS "columnName", ordinal_position AS "ordinalPosition", column_default AS "columnDefault", is_nullable AS "isNullable", data_type AS "dataType", character_maximum_length AS "characterMaximumLength", numeric_precision AS "numericPrecision", numeric_scale AS "numericScale" FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 ORDER BY ordinal_position LIMIT ${MAX_RESULT_ROWS + 1}`,
|
|
46811
|
+
values: [readSchema(inv.args.schema), readIdentifier(inv.args.table, "table")]
|
|
46812
|
+
};
|
|
46813
|
+
}
|
|
46814
|
+
if (inv.capabilityName === `${provider2.kind}.rows.select`) return buildRowSelect(inv.args, provider2.displayName);
|
|
46815
|
+
throw new Error(`Unknown ${provider2.displayName} read capability: ${inv.capabilityName}`);
|
|
46816
|
+
}
|
|
46817
|
+
function buildRowSelect(args, providerLabel) {
|
|
46818
|
+
const schema = readSchema(args.schema);
|
|
46819
|
+
const table = readIdentifier(args.table, "table");
|
|
46820
|
+
const columns = readIdentifierArray(args.columns, "columns", MAX_COLUMNS5, true);
|
|
46821
|
+
const filters = readFilters2(args.filters);
|
|
46822
|
+
const orderBy = readOrderBy(args.orderBy);
|
|
46823
|
+
const limit = readBoundedInteger(args.limit, 100, 1, MAX_RESULT_ROWS, "limit");
|
|
46824
|
+
const offset = readBoundedInteger(args.offset, 0, 0, 1e6, "offset");
|
|
46825
|
+
const values = [];
|
|
46826
|
+
const predicates = filters.map((filter) => {
|
|
46827
|
+
const column = quoteIdentifier2(filter.column);
|
|
46828
|
+
if (filter.operator === "is-null") return `${column} IS NULL`;
|
|
46829
|
+
if (filter.operator === "not-null") return `${column} IS NOT NULL`;
|
|
46830
|
+
values.push(filter.value);
|
|
46831
|
+
return `${column} ${sqlOperator(filter.operator)} $${values.length}`;
|
|
46832
|
+
});
|
|
46833
|
+
assertParameterBytes(values, providerLabel);
|
|
46834
|
+
const where = predicates.length > 0 ? ` WHERE ${predicates.join(" AND ")}` : "";
|
|
46835
|
+
const order = orderBy.length > 0 ? ` ORDER BY ${orderBy.map((entry) => `${quoteIdentifier2(entry.column)} ${entry.direction.toUpperCase()}`).join(", ")}` : "";
|
|
46836
|
+
return {
|
|
46837
|
+
text: `SELECT ${columns.map(quoteIdentifier2).join(", ")} FROM ${quoteIdentifier2(schema)}.${quoteIdentifier2(table)}${where}${order} LIMIT ${limit} OFFSET ${offset}`,
|
|
46838
|
+
values
|
|
46839
|
+
};
|
|
46840
|
+
}
|
|
46841
|
+
function readFilters2(value) {
|
|
46842
|
+
if (value === void 0 || value === null) return [];
|
|
46843
|
+
if (!Array.isArray(value) || value.length > MAX_FILTERS2) throw new Error(`filters must contain at most ${MAX_FILTERS2} entries`);
|
|
46844
|
+
return value.map((entry, index) => {
|
|
46845
|
+
if (!isPlainRecord(entry)) throw new Error(`filters[${index}] must be an object`);
|
|
46846
|
+
const column = readIdentifier(entry.column, `filters[${index}].column`);
|
|
46847
|
+
const operator = entry.operator;
|
|
46848
|
+
if (!["eq", "ne", "lt", "lte", "gt", "gte", "like", "is-null", "not-null"].includes(String(operator))) {
|
|
46849
|
+
throw new Error(`filters[${index}].operator is unsupported`);
|
|
46850
|
+
}
|
|
46851
|
+
const valueRequired = !["is-null", "not-null"].includes(String(operator));
|
|
46852
|
+
if (valueRequired) assertScalar(entry.value, `filters[${index}].value`);
|
|
46853
|
+
else if (entry.value !== void 0) throw new Error(`filters[${index}].value is not valid for ${operator}`);
|
|
46854
|
+
if (valueRequired && entry.value === null) throw new Error(`filters[${index}] must use is-null or not-null for null values`);
|
|
46855
|
+
if (operator === "like" && typeof entry.value !== "string") throw new Error(`filters[${index}].value must be a string for like`);
|
|
46856
|
+
return { column, operator: String(operator), value: entry.value };
|
|
46857
|
+
});
|
|
46858
|
+
}
|
|
46859
|
+
function readOrderBy(value) {
|
|
46860
|
+
if (value === void 0 || value === null) return [];
|
|
46861
|
+
if (!Array.isArray(value) || value.length > MAX_ORDER_COLUMNS) throw new Error(`orderBy must contain at most ${MAX_ORDER_COLUMNS} entries`);
|
|
46862
|
+
return value.map((entry, index) => {
|
|
46863
|
+
if (!isPlainRecord(entry)) throw new Error(`orderBy[${index}] must be an object`);
|
|
46864
|
+
const column = readIdentifier(entry.column, `orderBy[${index}].column`);
|
|
46865
|
+
const direction = entry.direction ?? "asc";
|
|
46866
|
+
if (direction !== "asc" && direction !== "desc") throw new Error(`orderBy[${index}].direction must be asc or desc`);
|
|
46867
|
+
return { column, direction };
|
|
46868
|
+
});
|
|
46869
|
+
}
|
|
46870
|
+
function readIdentifierArray(value, label, max, required) {
|
|
46871
|
+
if (!Array.isArray(value) || required && value.length === 0 || value.length > max) {
|
|
46872
|
+
throw new Error(`${label} must contain ${required ? "1-" : "0-"}${max} identifiers`);
|
|
46873
|
+
}
|
|
46874
|
+
const identifiers = value.map((entry, index) => readIdentifier(entry, `${label}[${index}]`));
|
|
46875
|
+
if (new Set(identifiers).size !== identifiers.length) throw new Error(`${label} must not contain duplicates`);
|
|
46876
|
+
return identifiers;
|
|
46877
|
+
}
|
|
46878
|
+
function boundedResult2(result, providerLabel) {
|
|
46879
|
+
if (!Array.isArray(result.rows) || result.rows.length > MAX_RESULT_ROWS) {
|
|
46880
|
+
throw new Error(`${providerLabel} result exceeds ${MAX_RESULT_ROWS} rows`);
|
|
46881
|
+
}
|
|
46882
|
+
if (result.rowCount !== null && (!Number.isSafeInteger(result.rowCount) || result.rowCount < 0 || result.rowCount > MAX_RESULT_ROWS)) {
|
|
46883
|
+
throw new Error(`${providerLabel} result returned a malformed row count`);
|
|
46884
|
+
}
|
|
46885
|
+
const data4 = {
|
|
46886
|
+
rows: jsonSafe(result.rows),
|
|
46887
|
+
columns: result.fields.map((field) => ({
|
|
46888
|
+
name: field.name,
|
|
46889
|
+
tableId: field.tableID,
|
|
46890
|
+
columnId: field.columnID,
|
|
46891
|
+
dataTypeId: field.dataTypeID
|
|
46892
|
+
})),
|
|
46893
|
+
rowCount: result.rowCount ?? result.rows.length
|
|
46894
|
+
};
|
|
46895
|
+
const serialized = JSON.stringify(data4);
|
|
46896
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_RESULT_BYTES3) {
|
|
46897
|
+
throw new Error(`${providerLabel} result exceeds the ${MAX_RESULT_BYTES3}-byte limit`);
|
|
46898
|
+
}
|
|
46899
|
+
return data4;
|
|
46900
|
+
}
|
|
46901
|
+
function assertParameterBytes(values, providerLabel) {
|
|
46902
|
+
if (Buffer.byteLength(JSON.stringify(values), "utf8") > MAX_PARAMETERS_BYTES) {
|
|
46903
|
+
throw new Error(`${providerLabel} filter values exceed the ${MAX_PARAMETERS_BYTES}-byte limit`);
|
|
46904
|
+
}
|
|
46905
|
+
}
|
|
46906
|
+
function assertScalar(value, label) {
|
|
46907
|
+
if (value !== null && !["string", "number", "boolean"].includes(typeof value)) throw new Error(`${label} must be a JSON scalar or null`);
|
|
46908
|
+
if (typeof value === "number" && !Number.isFinite(value)) throw new Error(`${label} must be finite`);
|
|
46909
|
+
}
|
|
46910
|
+
function sqlOperator(value) {
|
|
46911
|
+
return { eq: "=", ne: "<>", lt: "<", lte: "<=", gt: ">", gte: ">=", like: "LIKE" }[value];
|
|
46912
|
+
}
|
|
46913
|
+
function readSchema(value) {
|
|
46914
|
+
return value === void 0 || value === null ? "public" : readIdentifier(value, "schema");
|
|
46915
|
+
}
|
|
46916
|
+
function readIdentifier(value, label) {
|
|
46917
|
+
if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > MAX_IDENTIFIER_BYTES || !/^[A-Za-z_][A-Za-z0-9_$]*$/.test(value)) {
|
|
46918
|
+
throw new Error(`${label} must be a SQL identifier under ${MAX_IDENTIFIER_BYTES} bytes`);
|
|
46919
|
+
}
|
|
46920
|
+
return value;
|
|
46921
|
+
}
|
|
46922
|
+
function quoteIdentifier2(value) {
|
|
46923
|
+
return `"${value.replaceAll('"', '""')}"`;
|
|
46924
|
+
}
|
|
46925
|
+
function readHost4(value, providerLabel) {
|
|
46926
|
+
const host = readCredentialString2(value, "host", 253, providerLabel);
|
|
46927
|
+
const ipVersion = isIP6(host);
|
|
46928
|
+
if (ipVersion !== 0) {
|
|
46929
|
+
if (!isPublicNetworkAddress(host)) throw new Error(`${providerLabel} host is not a public network target`);
|
|
46930
|
+
return host;
|
|
46931
|
+
}
|
|
46932
|
+
const normalized = host.toLowerCase();
|
|
46933
|
+
if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@:\[\]]/.test(host)) {
|
|
46934
|
+
throw new Error(`${providerLabel} host must be a public hostname or IP address without a scheme or port`);
|
|
46935
|
+
}
|
|
46936
|
+
return host;
|
|
46937
|
+
}
|
|
46938
|
+
function readCredentialString2(value, label, maxLength, providerLabel) {
|
|
46939
|
+
if (typeof value !== "string" || value.length === 0 || value.length > maxLength || label !== "password" && /[\u0000-\u001f\u007f]/.test(value)) {
|
|
46940
|
+
throw new Error(`${providerLabel} credential ${label} must be a non-empty string under ${maxLength} characters`);
|
|
46941
|
+
}
|
|
46942
|
+
return value;
|
|
46943
|
+
}
|
|
46944
|
+
function safeErrorMessage4(error, secrets) {
|
|
46945
|
+
let message = error instanceof Error ? error.message : String(error);
|
|
46946
|
+
for (const secret of secrets) {
|
|
46947
|
+
if (secret.length >= 4) message = message.replaceAll(secret, "[REDACTED]");
|
|
46948
|
+
}
|
|
46949
|
+
return message;
|
|
46950
|
+
}
|
|
46951
|
+
function credentialSecrets4(source, provider2) {
|
|
46952
|
+
try {
|
|
46953
|
+
return readCredentials5(source, provider2).secrets;
|
|
46954
|
+
} catch {
|
|
46955
|
+
return [];
|
|
46956
|
+
}
|
|
46957
|
+
}
|
|
46958
|
+
function emptySchema2() {
|
|
46959
|
+
return { type: "object", properties: {}, additionalProperties: false };
|
|
46960
|
+
}
|
|
46961
|
+
function identifierSchema(description) {
|
|
46962
|
+
return { type: "string", minLength: 1, maxLength: MAX_IDENTIFIER_BYTES, pattern: "^[A-Za-z_][A-Za-z0-9_$]*$", description };
|
|
46963
|
+
}
|
|
46964
|
+
function readCapability9(name, description, parameters) {
|
|
46965
|
+
return { name, class: "read", description, parameters };
|
|
46966
|
+
}
|
|
46967
|
+
|
|
46968
|
+
// src/connectors/adapters/postgres.ts
|
|
46969
|
+
var POSTGRES_PROVIDER = {
|
|
46970
|
+
kind: "postgres",
|
|
46971
|
+
displayName: "PostgreSQL",
|
|
46972
|
+
description: "Inspect PostgreSQL schemas and tables and run bounded structured row reads over a verified TLS connection.",
|
|
46973
|
+
authHint: "JSON with a public PostgreSQL host, database, user, password, optional port, and optional TLS CA. Verified TLS is mandatory.",
|
|
46974
|
+
defaultPort: 5432
|
|
46975
|
+
};
|
|
46976
|
+
function createPostgresConnector(options = {}) {
|
|
46977
|
+
return createPostgresWireReadConnector(POSTGRES_PROVIDER, options);
|
|
46978
|
+
}
|
|
46979
|
+
var postgresConnector = createPostgresConnector();
|
|
46980
|
+
|
|
44438
46981
|
// src/connectors/adapters/recall-ai.ts
|
|
44439
46982
|
var recallAiRegions = [
|
|
44440
46983
|
"https://us-east-1.recall.ai",
|
|
@@ -45371,7 +47914,7 @@ function createSftpConnector(options = {}) {
|
|
|
45371
47914
|
]
|
|
45372
47915
|
},
|
|
45373
47916
|
async executeRead({ source, capabilityName, args }) {
|
|
45374
|
-
const credentials =
|
|
47917
|
+
const credentials = readCredentials6(source);
|
|
45375
47918
|
if (capabilityName === "sftp.list") {
|
|
45376
47919
|
const path = resolveRemotePath(credentials.rootPath, args.path ?? "/");
|
|
45377
47920
|
const limit = readBoundedInteger(args.limit, MAX_LIST_ENTRIES, 1, MAX_LIST_ENTRIES, "limit");
|
|
@@ -45404,7 +47947,7 @@ function createSftpConnector(options = {}) {
|
|
|
45404
47947
|
throw new Error(`Unknown SFTP read capability: ${capabilityName}`);
|
|
45405
47948
|
},
|
|
45406
47949
|
async executeMutation({ source, capabilityName, args }) {
|
|
45407
|
-
const credentials =
|
|
47950
|
+
const credentials = readCredentials6(source);
|
|
45408
47951
|
const prepared = prepareMutation(capabilityName, args, credentials.rootPath);
|
|
45409
47952
|
return withSftp(credentials, createClient, lookupHost, async (client) => {
|
|
45410
47953
|
let data4;
|
|
@@ -45438,7 +47981,7 @@ function createSftpConnector(options = {}) {
|
|
|
45438
47981
|
},
|
|
45439
47982
|
async test(source) {
|
|
45440
47983
|
try {
|
|
45441
|
-
const credentials =
|
|
47984
|
+
const credentials = readCredentials6(source);
|
|
45442
47985
|
await withSftp(credentials, createClient, lookupHost, (client) => client.cwd());
|
|
45443
47986
|
return { ok: true };
|
|
45444
47987
|
} catch (error) {
|
|
@@ -45506,7 +48049,7 @@ async function withSftp(credentials, createClient, lookupHost, run) {
|
|
|
45506
48049
|
await client.end().catch(() => false);
|
|
45507
48050
|
}
|
|
45508
48051
|
}
|
|
45509
|
-
function
|
|
48052
|
+
function readCredentials6(source) {
|
|
45510
48053
|
let raw;
|
|
45511
48054
|
if (source.credentials.kind === "custom") raw = source.credentials.values;
|
|
45512
48055
|
else if (source.credentials.kind === "api-key") {
|
|
@@ -48855,7 +51398,15 @@ var squareConnector = declarativeRestConnector({
|
|
|
48855
51398
|
kind: "oauth2",
|
|
48856
51399
|
authorizationUrl: "https://connect.squareup.com/oauth2/authorize",
|
|
48857
51400
|
tokenUrl: "https://connect.squareup.com/oauth2/token",
|
|
48858
|
-
scopes: [
|
|
51401
|
+
scopes: [
|
|
51402
|
+
"CUSTOMERS_READ",
|
|
51403
|
+
"CUSTOMERS_WRITE",
|
|
51404
|
+
"PAYMENTS_READ",
|
|
51405
|
+
"PAYMENTS_WRITE",
|
|
51406
|
+
"INVOICES_READ",
|
|
51407
|
+
"INVOICES_WRITE",
|
|
51408
|
+
"ITEMS_WRITE"
|
|
51409
|
+
],
|
|
48859
51410
|
clientIdEnv: "SQUARE_OAUTH_CLIENT_ID",
|
|
48860
51411
|
clientSecretEnv: "SQUARE_OAUTH_CLIENT_SECRET"
|
|
48861
51412
|
},
|
|
@@ -55209,17 +57760,17 @@ var zohoBookingsConnector = {
|
|
|
55209
57760
|
category: "calendar",
|
|
55210
57761
|
defaultConsistencyModel: "authoritative",
|
|
55211
57762
|
capabilities: [
|
|
55212
|
-
|
|
55213
|
-
|
|
57763
|
+
readCapability10("workspaces.list", "List Zoho Bookings workspaces.", {}),
|
|
57764
|
+
readCapability10("services.list", "List services, optionally within one workspace.", {
|
|
55214
57765
|
workspace_id: { type: "string" }
|
|
55215
57766
|
}),
|
|
55216
|
-
|
|
57767
|
+
readCapability10("resources.list", "List bookable resources, optionally for one service.", {
|
|
55217
57768
|
service_id: { type: "string" }
|
|
55218
57769
|
}),
|
|
55219
|
-
|
|
57770
|
+
readCapability10("staff.list", "List staff, optionally for one service.", {
|
|
55220
57771
|
service_id: { type: "string" }
|
|
55221
57772
|
}),
|
|
55222
|
-
|
|
57773
|
+
readCapability10("appointments.list", "List appointments from a required start time.", {
|
|
55223
57774
|
from_time: {
|
|
55224
57775
|
type: "string",
|
|
55225
57776
|
description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format."
|
|
@@ -55229,17 +57780,17 @@ var zohoBookingsConnector = {
|
|
|
55229
57780
|
staff_id: { type: "string" },
|
|
55230
57781
|
status: { type: "string" }
|
|
55231
57782
|
}, ["from_time"]),
|
|
55232
|
-
|
|
57783
|
+
readCapability10("appointments.get", "Read one appointment by booking id.", {
|
|
55233
57784
|
booking_id: { type: "string" }
|
|
55234
57785
|
}, ["booking_id"]),
|
|
55235
|
-
|
|
57786
|
+
readCapability10("availability.fetch", "Fetch open time slots for a service and date.", {
|
|
55236
57787
|
service_id: { type: "string" },
|
|
55237
57788
|
selected_date: { type: "string", description: "Date in YYYY-MM-DD format." },
|
|
55238
57789
|
staff_id: { type: "string" },
|
|
55239
57790
|
group_id: { type: "string" },
|
|
55240
57791
|
resource_id: { type: "string" }
|
|
55241
57792
|
}, ["service_id", "selected_date"]),
|
|
55242
|
-
|
|
57793
|
+
mutationCapability8("appointments.book", "Book an appointment for a customer.", {
|
|
55243
57794
|
service_id: { type: "string" },
|
|
55244
57795
|
from_time: { type: "string", description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format." },
|
|
55245
57796
|
customer_details: {
|
|
@@ -55255,13 +57806,13 @@ var zohoBookingsConnector = {
|
|
|
55255
57806
|
additional_fields: { type: "object" },
|
|
55256
57807
|
payment_info: { type: "object" }
|
|
55257
57808
|
}, ["service_id", "from_time", "customer_details"]),
|
|
55258
|
-
|
|
57809
|
+
mutationCapability8("appointments.reschedule", "Move an appointment to a new time, staff member, or group.", {
|
|
55259
57810
|
booking_id: { type: "string" },
|
|
55260
57811
|
start_time: { type: "string", description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format." },
|
|
55261
57812
|
staff_id: { type: "string" },
|
|
55262
57813
|
group_id: { type: "string" }
|
|
55263
57814
|
}, ["booking_id"]),
|
|
55264
|
-
|
|
57815
|
+
mutationCapability8("appointments.cancel", "Cancel an appointment by booking id.", {
|
|
55265
57816
|
booking_id: { type: "string" }
|
|
55266
57817
|
}, ["booking_id"])
|
|
55267
57818
|
]
|
|
@@ -55378,7 +57929,7 @@ var zohoBookingsConnector = {
|
|
|
55378
57929
|
}
|
|
55379
57930
|
}
|
|
55380
57931
|
};
|
|
55381
|
-
function
|
|
57932
|
+
function readCapability10(name, description, properties, required = []) {
|
|
55382
57933
|
return {
|
|
55383
57934
|
name,
|
|
55384
57935
|
class: "read",
|
|
@@ -55387,7 +57938,7 @@ function readCapability6(name, description, properties, required = []) {
|
|
|
55387
57938
|
requiredScopes: ["zohobookings.data.READ"]
|
|
55388
57939
|
};
|
|
55389
57940
|
}
|
|
55390
|
-
function
|
|
57941
|
+
function mutationCapability8(name, description, properties, required) {
|
|
55391
57942
|
return {
|
|
55392
57943
|
name,
|
|
55393
57944
|
class: "mutation",
|
|
@@ -62731,6 +65282,393 @@ var metabaseConnector = declarativeRestConnector({
|
|
|
62731
65282
|
]
|
|
62732
65283
|
});
|
|
62733
65284
|
|
|
65285
|
+
// src/connectors/adapters/mysql.ts
|
|
65286
|
+
import { connect as createSocket, isIP as isIP7 } from "net";
|
|
65287
|
+
import {
|
|
65288
|
+
createConnection as createMysqlConnection
|
|
65289
|
+
} from "mysql2/promise";
|
|
65290
|
+
var MAX_PARAMETERS = 1e3;
|
|
65291
|
+
var MAX_PARAMETER_BYTES = 10 * 1024 * 1024;
|
|
65292
|
+
var MAX_RESULT_ROWS2 = 1e4;
|
|
65293
|
+
var MAX_RESULT_BYTES4 = 10 * 1024 * 1024;
|
|
65294
|
+
var DEFAULT_QUERY_TIMEOUT_MS = 3e4;
|
|
65295
|
+
function createMySqlConnector(options = {}) {
|
|
65296
|
+
const createConnection = options.createConnection ?? defaultCreateConnection;
|
|
65297
|
+
const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
|
|
65298
|
+
return {
|
|
65299
|
+
manifest: {
|
|
65300
|
+
kind: "mysql",
|
|
65301
|
+
displayName: "MySQL",
|
|
65302
|
+
description: "Inspect schemas and run bounded parameterized reads or approved compare-and-swap writes against MySQL over verified TLS.",
|
|
65303
|
+
auth: {
|
|
65304
|
+
kind: "api-key",
|
|
65305
|
+
hint: "JSON with host, port, user, password, optional database, and optional tlsCa. TLS and public network targets are mandatory."
|
|
65306
|
+
},
|
|
65307
|
+
defaultConsistencyModel: "authoritative",
|
|
65308
|
+
category: "database",
|
|
65309
|
+
rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
|
|
65310
|
+
capabilities: [
|
|
65311
|
+
{
|
|
65312
|
+
name: "mysql.databases.list",
|
|
65313
|
+
class: "read",
|
|
65314
|
+
description: "List databases visible to the connected MySQL user.",
|
|
65315
|
+
parameters: emptySchema3()
|
|
65316
|
+
},
|
|
65317
|
+
{
|
|
65318
|
+
name: "mysql.tables.list",
|
|
65319
|
+
class: "read",
|
|
65320
|
+
description: "List base tables and views in one database.",
|
|
65321
|
+
parameters: {
|
|
65322
|
+
type: "object",
|
|
65323
|
+
properties: { database: identifierSchema2("Database name; defaults to the connection database.") },
|
|
65324
|
+
additionalProperties: false
|
|
65325
|
+
}
|
|
65326
|
+
},
|
|
65327
|
+
{
|
|
65328
|
+
name: "mysql.tables.describe",
|
|
65329
|
+
class: "read",
|
|
65330
|
+
description: "Read columns, types, nullability, defaults, and keys for one table.",
|
|
65331
|
+
parameters: {
|
|
65332
|
+
type: "object",
|
|
65333
|
+
properties: {
|
|
65334
|
+
database: identifierSchema2("Database name; defaults to the connection database."),
|
|
65335
|
+
table: identifierSchema2("Table name.")
|
|
65336
|
+
},
|
|
65337
|
+
required: ["table"],
|
|
65338
|
+
additionalProperties: false
|
|
65339
|
+
}
|
|
65340
|
+
},
|
|
65341
|
+
{
|
|
65342
|
+
name: "mysql.query",
|
|
65343
|
+
class: "read",
|
|
65344
|
+
description: "Run one parameterized SELECT, SHOW, DESCRIBE, or EXPLAIN statement inside a read-only transaction.",
|
|
65345
|
+
parameters: statementSchema(false)
|
|
65346
|
+
},
|
|
65347
|
+
{
|
|
65348
|
+
name: "mysql.execute",
|
|
65349
|
+
class: "mutation",
|
|
65350
|
+
description: "Run one approved parameterized INSERT, UPDATE, DELETE, or REPLACE statement and commit only when the affected-row count matches the caller expectation.",
|
|
65351
|
+
parameters: statementSchema(true),
|
|
65352
|
+
cas: "optimistic-read-verify",
|
|
65353
|
+
externalEffect: true
|
|
65354
|
+
}
|
|
65355
|
+
]
|
|
65356
|
+
},
|
|
65357
|
+
async test(source) {
|
|
65358
|
+
try {
|
|
65359
|
+
await withConnection(source, createConnection, resolveHost, async (connection) => {
|
|
65360
|
+
await connection.ping();
|
|
65361
|
+
});
|
|
65362
|
+
return { ok: true };
|
|
65363
|
+
} catch (error) {
|
|
65364
|
+
return { ok: false, reason: safeErrorMessage5(error) };
|
|
65365
|
+
}
|
|
65366
|
+
},
|
|
65367
|
+
async executeRead(inv) {
|
|
65368
|
+
const query2 = readQuery2(inv);
|
|
65369
|
+
assertReadStatement(query2.statement);
|
|
65370
|
+
const data4 = await withConnection(inv.source, createConnection, resolveHost, async (connection) => {
|
|
65371
|
+
await executeStatement(connection, {
|
|
65372
|
+
statement: "START TRANSACTION READ ONLY",
|
|
65373
|
+
parameters: []
|
|
65374
|
+
}, queryTimeout(inv.source));
|
|
65375
|
+
try {
|
|
65376
|
+
const [rows, fields] = await executeStatement(connection, query2, queryTimeout(inv.source));
|
|
65377
|
+
if (!Array.isArray(rows)) throw new Error("MySQL read returned a non-row result");
|
|
65378
|
+
return boundedResult3(rows, fields);
|
|
65379
|
+
} finally {
|
|
65380
|
+
await connection.rollback().catch(() => void 0);
|
|
65381
|
+
}
|
|
65382
|
+
});
|
|
65383
|
+
return { data: data4, fetchedAt: Date.now() };
|
|
65384
|
+
},
|
|
65385
|
+
async executeMutation(inv) {
|
|
65386
|
+
if (inv.capabilityName !== "mysql.execute") {
|
|
65387
|
+
throw new Error(`Unknown MySQL mutation capability: ${inv.capabilityName}`);
|
|
65388
|
+
}
|
|
65389
|
+
const query2 = readStatementArgs(inv.args);
|
|
65390
|
+
assertMutationStatement(query2.statement);
|
|
65391
|
+
const expectedAffectedRows = readBoundedInteger(
|
|
65392
|
+
inv.args.expectedAffectedRows,
|
|
65393
|
+
-1,
|
|
65394
|
+
0,
|
|
65395
|
+
MAX_RESULT_ROWS2,
|
|
65396
|
+
"expectedAffectedRows"
|
|
65397
|
+
);
|
|
65398
|
+
if (expectedAffectedRows < 0) throw new Error("expectedAffectedRows is required");
|
|
65399
|
+
return withConnection(inv.source, createConnection, resolveHost, async (connection) => {
|
|
65400
|
+
await connection.beginTransaction();
|
|
65401
|
+
try {
|
|
65402
|
+
const [result] = await executeStatement(connection, query2, queryTimeout(inv.source));
|
|
65403
|
+
const affectedRows = readAffectedRows(result);
|
|
65404
|
+
if (affectedRows !== expectedAffectedRows) {
|
|
65405
|
+
await connection.rollback();
|
|
65406
|
+
return {
|
|
65407
|
+
status: "conflict",
|
|
65408
|
+
alternatives: [],
|
|
65409
|
+
currentState: { affectedRows },
|
|
65410
|
+
message: `MySQL write affected ${affectedRows} rows; expected ${expectedAffectedRows}. The transaction was rolled back.`
|
|
65411
|
+
};
|
|
65412
|
+
}
|
|
65413
|
+
await connection.commit();
|
|
65414
|
+
return committedMutation(result, affectedRows);
|
|
65415
|
+
} catch (error) {
|
|
65416
|
+
await connection.rollback().catch(() => void 0);
|
|
65417
|
+
throw error;
|
|
65418
|
+
}
|
|
65419
|
+
});
|
|
65420
|
+
}
|
|
65421
|
+
};
|
|
65422
|
+
}
|
|
65423
|
+
var mysqlConnector = createMySqlConnector();
|
|
65424
|
+
async function defaultCreateConnection(config) {
|
|
65425
|
+
return createMysqlConnection(config);
|
|
65426
|
+
}
|
|
65427
|
+
async function withConnection(source, createConnection, resolveHost, run) {
|
|
65428
|
+
const credentials = readCredentials7(source);
|
|
65429
|
+
const addresses = await resolveHost(credentials.host);
|
|
65430
|
+
if (addresses.length === 0) throw new Error("MySQL host did not resolve");
|
|
65431
|
+
const address = addresses[0];
|
|
65432
|
+
let connection;
|
|
65433
|
+
try {
|
|
65434
|
+
connection = await createConnection({
|
|
65435
|
+
host: credentials.host,
|
|
65436
|
+
port: credentials.port,
|
|
65437
|
+
user: credentials.user,
|
|
65438
|
+
password: credentials.password,
|
|
65439
|
+
database: credentials.database,
|
|
65440
|
+
connectTimeout: credentials.connectTimeoutMs,
|
|
65441
|
+
multipleStatements: false,
|
|
65442
|
+
namedPlaceholders: false,
|
|
65443
|
+
supportBigNumbers: true,
|
|
65444
|
+
bigNumberStrings: true,
|
|
65445
|
+
decimalNumbers: false,
|
|
65446
|
+
dateStrings: true,
|
|
65447
|
+
timezone: "Z",
|
|
65448
|
+
charset: "utf8mb4",
|
|
65449
|
+
enableKeepAlive: true,
|
|
65450
|
+
maxPreparedStatements: 100,
|
|
65451
|
+
stream: () => createSocket({ host: address, port: credentials.port }),
|
|
65452
|
+
ssl: {
|
|
65453
|
+
ca: credentials.tlsCa,
|
|
65454
|
+
minVersion: "TLSv1.2",
|
|
65455
|
+
rejectUnauthorized: true,
|
|
65456
|
+
verifyIdentity: true
|
|
65457
|
+
}
|
|
65458
|
+
});
|
|
65459
|
+
return await run(connection);
|
|
65460
|
+
} finally {
|
|
65461
|
+
if (connection) await connection.end().catch(() => void 0);
|
|
65462
|
+
}
|
|
65463
|
+
}
|
|
65464
|
+
function readCredentials7(source) {
|
|
65465
|
+
let raw;
|
|
65466
|
+
if (source.credentials.kind === "custom") raw = source.credentials.values;
|
|
65467
|
+
else if (source.credentials.kind === "api-key") {
|
|
65468
|
+
try {
|
|
65469
|
+
raw = JSON.parse(source.credentials.apiKey);
|
|
65470
|
+
} catch {
|
|
65471
|
+
throw new Error("MySQL credential must be valid JSON");
|
|
65472
|
+
}
|
|
65473
|
+
} else {
|
|
65474
|
+
throw new Error("MySQL requires a structured credential bundle");
|
|
65475
|
+
}
|
|
65476
|
+
if (!isPlainRecord(raw)) throw new Error("MySQL credential must be a JSON object");
|
|
65477
|
+
const host = requiredCredentialString3(raw.host, "host", 253);
|
|
65478
|
+
validateHost(host);
|
|
65479
|
+
const user = requiredCredentialString3(raw.user ?? raw.username, "user", 128);
|
|
65480
|
+
const password = requiredCredentialString3(raw.password, "password", 4096);
|
|
65481
|
+
const database = readOptionalString(raw.database, "database");
|
|
65482
|
+
if (database && database.length > 64) throw new Error("MySQL credential database exceeds 64 characters");
|
|
65483
|
+
const tlsCa = readOptionalString(raw.tlsCa, "tlsCa");
|
|
65484
|
+
if (tlsCa && Buffer.byteLength(tlsCa, "utf8") > 1024 * 1024) {
|
|
65485
|
+
throw new Error("MySQL credential tlsCa exceeds the 1 MiB limit");
|
|
65486
|
+
}
|
|
65487
|
+
return {
|
|
65488
|
+
host,
|
|
65489
|
+
port: readBoundedInteger(raw.port, 3306, 1, 65535, "port"),
|
|
65490
|
+
user,
|
|
65491
|
+
password,
|
|
65492
|
+
database,
|
|
65493
|
+
tlsCa,
|
|
65494
|
+
connectTimeoutMs: readBoundedInteger(raw.connectTimeoutMs, 1e4, 1e3, 3e4, "connectTimeoutMs"),
|
|
65495
|
+
queryTimeoutMs: readBoundedInteger(raw.queryTimeoutMs, DEFAULT_QUERY_TIMEOUT_MS, 1e3, 6e4, "queryTimeoutMs")
|
|
65496
|
+
};
|
|
65497
|
+
}
|
|
65498
|
+
function readQuery2(inv) {
|
|
65499
|
+
if (inv.capabilityName === "mysql.databases.list") {
|
|
65500
|
+
return {
|
|
65501
|
+
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",
|
|
65502
|
+
parameters: []
|
|
65503
|
+
};
|
|
65504
|
+
}
|
|
65505
|
+
if (inv.capabilityName === "mysql.tables.list") {
|
|
65506
|
+
const database = readDatabase(inv.args.database, inv.source);
|
|
65507
|
+
return {
|
|
65508
|
+
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",
|
|
65509
|
+
parameters: [database]
|
|
65510
|
+
};
|
|
65511
|
+
}
|
|
65512
|
+
if (inv.capabilityName === "mysql.tables.describe") {
|
|
65513
|
+
const database = readDatabase(inv.args.database, inv.source);
|
|
65514
|
+
const table = readIdentifier2(inv.args.table, "table");
|
|
65515
|
+
return {
|
|
65516
|
+
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",
|
|
65517
|
+
parameters: [database, table]
|
|
65518
|
+
};
|
|
65519
|
+
}
|
|
65520
|
+
if (inv.capabilityName === "mysql.query") return readStatementArgs(inv.args);
|
|
65521
|
+
throw new Error(`Unknown MySQL read capability: ${inv.capabilityName}`);
|
|
65522
|
+
}
|
|
65523
|
+
function readStatementArgs(args) {
|
|
65524
|
+
const statement = requiredString7(args.statement, "statement");
|
|
65525
|
+
if (Buffer.byteLength(statement, "utf8") > 1024 * 1024) throw new Error("statement exceeds the 1 MiB limit");
|
|
65526
|
+
const parameters = args.parameters === void 0 ? [] : args.parameters;
|
|
65527
|
+
if (!Array.isArray(parameters) || parameters.length > MAX_PARAMETERS) {
|
|
65528
|
+
throw new Error(`parameters must be an array with at most ${MAX_PARAMETERS} entries`);
|
|
65529
|
+
}
|
|
65530
|
+
for (const [index, value] of parameters.entries()) {
|
|
65531
|
+
if (value !== null && !["string", "number", "boolean"].includes(typeof value)) {
|
|
65532
|
+
throw new Error(`parameters[${index}] must be a JSON scalar or null`);
|
|
65533
|
+
}
|
|
65534
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
65535
|
+
throw new Error(`parameters[${index}] must be finite`);
|
|
65536
|
+
}
|
|
65537
|
+
}
|
|
65538
|
+
if (Buffer.byteLength(JSON.stringify(parameters), "utf8") > MAX_PARAMETER_BYTES) {
|
|
65539
|
+
throw new Error(`parameters exceed the ${MAX_PARAMETER_BYTES}-byte limit`);
|
|
65540
|
+
}
|
|
65541
|
+
return { statement, parameters };
|
|
65542
|
+
}
|
|
65543
|
+
function assertReadStatement(statement) {
|
|
65544
|
+
const keyword = leadingKeyword(statement);
|
|
65545
|
+
if (!["SELECT", "SHOW", "DESCRIBE", "DESC", "EXPLAIN", "WITH"].includes(keyword)) {
|
|
65546
|
+
throw new Error("mysql.query accepts only SELECT, SHOW, DESCRIBE, or EXPLAIN statements");
|
|
65547
|
+
}
|
|
65548
|
+
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)) {
|
|
65549
|
+
throw new Error("mysql.query rejects file, lock, and delay side effects");
|
|
65550
|
+
}
|
|
65551
|
+
}
|
|
65552
|
+
function assertMutationStatement(statement) {
|
|
65553
|
+
const keyword = leadingKeyword(statement);
|
|
65554
|
+
if (!["INSERT", "UPDATE", "DELETE", "REPLACE"].includes(keyword)) {
|
|
65555
|
+
throw new Error("mysql.execute accepts only INSERT, UPDATE, DELETE, or REPLACE statements");
|
|
65556
|
+
}
|
|
65557
|
+
}
|
|
65558
|
+
function leadingKeyword(statement) {
|
|
65559
|
+
const withoutComments = statement.replace(/^\s*(?:--[^\n]*(?:\n|$)|#[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/\s*)*/u, "").trimStart();
|
|
65560
|
+
return /^[A-Za-z]+/.exec(withoutComments)?.[0]?.toUpperCase() ?? "";
|
|
65561
|
+
}
|
|
65562
|
+
async function executeStatement(connection, query2, timeout) {
|
|
65563
|
+
return connection.execute({ sql: query2.statement, values: query2.parameters, timeout });
|
|
65564
|
+
}
|
|
65565
|
+
function boundedResult3(rows, fields) {
|
|
65566
|
+
if (rows.length > MAX_RESULT_ROWS2) throw new Error(`MySQL result exceeds ${MAX_RESULT_ROWS2} rows`);
|
|
65567
|
+
const data4 = {
|
|
65568
|
+
rows: jsonSafe(rows),
|
|
65569
|
+
columns: fields.map((field) => ({
|
|
65570
|
+
name: field.name,
|
|
65571
|
+
table: field.table,
|
|
65572
|
+
database: field.schema,
|
|
65573
|
+
type: field.type
|
|
65574
|
+
})),
|
|
65575
|
+
rowCount: rows.length
|
|
65576
|
+
};
|
|
65577
|
+
const serialized = JSON.stringify(data4);
|
|
65578
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_RESULT_BYTES4) {
|
|
65579
|
+
throw new Error(`MySQL result exceeds the ${MAX_RESULT_BYTES4}-byte limit`);
|
|
65580
|
+
}
|
|
65581
|
+
return data4;
|
|
65582
|
+
}
|
|
65583
|
+
function readAffectedRows(result) {
|
|
65584
|
+
if (!isPlainRecord(result) || !Number.isInteger(result.affectedRows) || result.affectedRows < 0) {
|
|
65585
|
+
throw new Error("MySQL mutation did not return an affected-row count");
|
|
65586
|
+
}
|
|
65587
|
+
return result.affectedRows;
|
|
65588
|
+
}
|
|
65589
|
+
function committedMutation(result, affectedRows) {
|
|
65590
|
+
const header2 = result;
|
|
65591
|
+
const rawInsertId = header2.insertId;
|
|
65592
|
+
const insertId = typeof rawInsertId === "number" && Number.isSafeInteger(rawInsertId) ? rawInsertId : typeof rawInsertId === "string" ? rawInsertId : void 0;
|
|
65593
|
+
return {
|
|
65594
|
+
status: "committed",
|
|
65595
|
+
data: jsonSafe({
|
|
65596
|
+
affectedRows,
|
|
65597
|
+
changedRows: header2.changedRows,
|
|
65598
|
+
insertId,
|
|
65599
|
+
warningStatus: header2.warningStatus
|
|
65600
|
+
}),
|
|
65601
|
+
committedAt: Date.now(),
|
|
65602
|
+
idempotentReplay: false
|
|
65603
|
+
};
|
|
65604
|
+
}
|
|
65605
|
+
function readDatabase(value, source) {
|
|
65606
|
+
const explicit = value === void 0 ? void 0 : readIdentifier2(value, "database");
|
|
65607
|
+
if (explicit) return explicit;
|
|
65608
|
+
const credentials = readCredentials7(source);
|
|
65609
|
+
if (!credentials.database) throw new Error("database is required when the connection has no default database");
|
|
65610
|
+
return credentials.database;
|
|
65611
|
+
}
|
|
65612
|
+
function queryTimeout(source) {
|
|
65613
|
+
return readCredentials7(source).queryTimeoutMs;
|
|
65614
|
+
}
|
|
65615
|
+
function requiredString7(value, label) {
|
|
65616
|
+
const parsed = readOptionalString(value, label);
|
|
65617
|
+
if (!parsed) throw new Error(`${label} is required`);
|
|
65618
|
+
return parsed;
|
|
65619
|
+
}
|
|
65620
|
+
function requiredCredentialString3(value, label, maxLength) {
|
|
65621
|
+
const parsed = readOptionalString(value, label);
|
|
65622
|
+
if (!parsed) throw new Error(`MySQL credential ${label} is required`);
|
|
65623
|
+
if (parsed.length > maxLength) throw new Error(`MySQL credential ${label} exceeds ${maxLength} characters`);
|
|
65624
|
+
return parsed;
|
|
65625
|
+
}
|
|
65626
|
+
function readIdentifier2(value, label) {
|
|
65627
|
+
const parsed = requiredString7(value, label);
|
|
65628
|
+
if (parsed.length > 64) throw new Error(`${label} exceeds 64 characters`);
|
|
65629
|
+
return parsed;
|
|
65630
|
+
}
|
|
65631
|
+
function validateHost(host) {
|
|
65632
|
+
const normalized = host.toLowerCase();
|
|
65633
|
+
if (host.length > 253 || host.includes("://") || /[\s/@]/.test(host) || normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || isIP7(host) === 0 && !/^[A-Za-z0-9.-]+$/.test(host) || host.split(".").some((label) => !label || label.startsWith("-") || label.endsWith("-"))) {
|
|
65634
|
+
throw new Error("MySQL host must be a public hostname or IP address without a scheme");
|
|
65635
|
+
}
|
|
65636
|
+
}
|
|
65637
|
+
function emptySchema3() {
|
|
65638
|
+
return { type: "object", properties: {}, additionalProperties: false };
|
|
65639
|
+
}
|
|
65640
|
+
function identifierSchema2(description) {
|
|
65641
|
+
return { type: "string", minLength: 1, maxLength: 64, description };
|
|
65642
|
+
}
|
|
65643
|
+
function statementSchema(mutation) {
|
|
65644
|
+
const properties = {
|
|
65645
|
+
statement: { type: "string", minLength: 1, maxLength: 1024 * 1024 },
|
|
65646
|
+
parameters: {
|
|
65647
|
+
type: "array",
|
|
65648
|
+
maxItems: MAX_PARAMETERS,
|
|
65649
|
+
items: { type: ["string", "number", "boolean", "null"] },
|
|
65650
|
+
description: "Values bound to ? placeholders in order."
|
|
65651
|
+
}
|
|
65652
|
+
};
|
|
65653
|
+
if (mutation) {
|
|
65654
|
+
properties.expectedAffectedRows = {
|
|
65655
|
+
type: "integer",
|
|
65656
|
+
minimum: 0,
|
|
65657
|
+
maximum: MAX_RESULT_ROWS2,
|
|
65658
|
+
description: "Commit only when MySQL reports exactly this many affected rows."
|
|
65659
|
+
};
|
|
65660
|
+
}
|
|
65661
|
+
return {
|
|
65662
|
+
type: "object",
|
|
65663
|
+
properties,
|
|
65664
|
+
required: mutation ? ["statement", "expectedAffectedRows"] : ["statement"],
|
|
65665
|
+
additionalProperties: false
|
|
65666
|
+
};
|
|
65667
|
+
}
|
|
65668
|
+
function safeErrorMessage5(error) {
|
|
65669
|
+
return error instanceof Error && error.message ? error.message : "MySQL connection failed";
|
|
65670
|
+
}
|
|
65671
|
+
|
|
62734
65672
|
// src/connectors/adapters/segment.ts
|
|
62735
65673
|
var segmentConnector = declarativeRestConnector({
|
|
62736
65674
|
kind: "segment",
|
|
@@ -63875,6 +66813,49 @@ var hunterConnector = declarativeRestConnector({
|
|
|
63875
66813
|
]
|
|
63876
66814
|
});
|
|
63877
66815
|
|
|
66816
|
+
// src/connectors/adapters/neverbounce.ts
|
|
66817
|
+
var neverbounceConnector = declarativeRestConnector({
|
|
66818
|
+
kind: "neverbounce",
|
|
66819
|
+
displayName: "NeverBounce",
|
|
66820
|
+
description: "Verify email deliverability with the NeverBounce API.",
|
|
66821
|
+
auth: {
|
|
66822
|
+
kind: "api-key",
|
|
66823
|
+
hint: "NeverBounce API key from Apps > API in the NeverBounce dashboard."
|
|
66824
|
+
},
|
|
66825
|
+
category: "crm",
|
|
66826
|
+
defaultConsistencyModel: "authoritative",
|
|
66827
|
+
baseUrl: "https://api.neverbounce.com/v4.2",
|
|
66828
|
+
credentialPlacement: { kind: "query", parameter: "key" },
|
|
66829
|
+
test: {
|
|
66830
|
+
method: "GET",
|
|
66831
|
+
path: "/account/info"
|
|
66832
|
+
},
|
|
66833
|
+
capabilities: [
|
|
66834
|
+
{
|
|
66835
|
+
name: "verify.email.address",
|
|
66836
|
+
class: "mutation",
|
|
66837
|
+
description: "Verify one email address and return deliverability status such as valid, invalid, disposable, catch-all, or unknown.",
|
|
66838
|
+
parameters: {
|
|
66839
|
+
type: "object",
|
|
66840
|
+
properties: {
|
|
66841
|
+
email: {
|
|
66842
|
+
type: "string",
|
|
66843
|
+
description: "Email address to verify."
|
|
66844
|
+
}
|
|
66845
|
+
},
|
|
66846
|
+
required: ["email"]
|
|
66847
|
+
},
|
|
66848
|
+
request: {
|
|
66849
|
+
method: "GET",
|
|
66850
|
+
path: "/single/check",
|
|
66851
|
+
query: { email: "{email}" }
|
|
66852
|
+
},
|
|
66853
|
+
cas: "native-idempotency",
|
|
66854
|
+
externalEffect: true
|
|
66855
|
+
}
|
|
66856
|
+
]
|
|
66857
|
+
});
|
|
66858
|
+
|
|
63878
66859
|
// src/connectors/adapters/theirstack.ts
|
|
63879
66860
|
var theirstackConnector = declarativeRestConnector({
|
|
63880
66861
|
kind: "theirstack",
|
|
@@ -64226,6 +67207,7 @@ var CONNECTOR_ADAPTER_FACTORIES = [
|
|
|
64226
67207
|
defineFactoryAdapter(() => digitalOceanConnector, {}),
|
|
64227
67208
|
defineFactoryAdapter(() => clicksendConnector, {}),
|
|
64228
67209
|
defineFactoryAdapter(() => metabaseConnector, {}),
|
|
67210
|
+
defineFactoryAdapter(() => mysqlConnector, {}),
|
|
64229
67211
|
defineFactoryAdapter(
|
|
64230
67212
|
() => microsoftDynamics365BusinessCentralConnector,
|
|
64231
67213
|
microsoftOAuthEnvMap
|
|
@@ -64284,6 +67266,12 @@ var CONNECTOR_ADAPTER_FACTORIES = [
|
|
|
64284
67266
|
defineFactoryAdapter(() => httpConnector, {}),
|
|
64285
67267
|
defineFactoryAdapter(() => rssConnector, {}),
|
|
64286
67268
|
defineFactoryAdapter(() => kafkaConnector, {}),
|
|
67269
|
+
defineFactoryAdapter(() => duckdbConnector, {}),
|
|
67270
|
+
defineFactoryAdapter(() => rabbitMqConnector, {}),
|
|
67271
|
+
defineFactoryAdapter(() => redisConnector, {}),
|
|
67272
|
+
defineFactoryAdapter(() => redshiftConnector, {}),
|
|
67273
|
+
defineFactoryAdapter(() => postgresConnector, {}),
|
|
67274
|
+
defineFactoryAdapter(() => mongodbConnector, {}),
|
|
64287
67275
|
defineFactoryAdapter(() => csvFilesConnector, {}),
|
|
64288
67276
|
defineFactoryAdapter(() => excelFilesConnector, {}),
|
|
64289
67277
|
defineFactoryAdapter(() => parquetFilesConnector, {}),
|
|
@@ -64352,14 +67340,15 @@ var CONNECTOR_ADAPTER_FACTORIES = [
|
|
|
64352
67340
|
clientId: "LINEAR_OAUTH_CLIENT_ID",
|
|
64353
67341
|
clientSecret: "LINEAR_OAUTH_CLIENT_SECRET"
|
|
64354
67342
|
}),
|
|
67343
|
+
defineFactoryAdapter(() => miroConnector, {
|
|
67344
|
+
clientId: "MIRO_OAUTH_CLIENT_ID",
|
|
67345
|
+
clientSecret: "MIRO_OAUTH_CLIENT_SECRET"
|
|
67346
|
+
}),
|
|
64355
67347
|
defineFactoryAdapter(() => mondayConnector, {
|
|
64356
67348
|
clientId: "MONDAY_OAUTH_CLIENT_ID",
|
|
64357
67349
|
clientSecret: "MONDAY_OAUTH_CLIENT_SECRET"
|
|
64358
67350
|
}),
|
|
64359
|
-
defineFactoryAdapter(() => clickupConnector, {
|
|
64360
|
-
clientId: "CLICKUP_OAUTH_CLIENT_ID",
|
|
64361
|
-
clientSecret: "CLICKUP_OAUTH_CLIENT_SECRET"
|
|
64362
|
-
}),
|
|
67351
|
+
defineFactoryAdapter(() => clickupConnector, {}),
|
|
64363
67352
|
defineFactoryAdapter(() => basecampConnector, {
|
|
64364
67353
|
clientId: "BASECAMP_OAUTH_CLIENT_ID",
|
|
64365
67354
|
clientSecret: "BASECAMP_OAUTH_CLIENT_SECRET"
|
|
@@ -64581,6 +67570,7 @@ var CONNECTOR_ADAPTER_FACTORIES = [
|
|
|
64581
67570
|
defineFactoryAdapter(() => builtwithConnector, {}),
|
|
64582
67571
|
defineFactoryAdapter(() => fullenrichConnector, {}),
|
|
64583
67572
|
defineFactoryAdapter(() => hunterConnector, {}),
|
|
67573
|
+
defineFactoryAdapter(() => neverbounceConnector, {}),
|
|
64584
67574
|
defineFactoryAdapter(() => theirstackConnector, {}),
|
|
64585
67575
|
defineFactoryAdapter(() => zerobounceConnector, {})
|
|
64586
67576
|
];
|
|
@@ -70966,677 +73956,6 @@ var figjamConnector = declarativeRestConnector({
|
|
|
70966
73956
|
]
|
|
70967
73957
|
});
|
|
70968
73958
|
|
|
70969
|
-
// src/connectors/adapters/miro.ts
|
|
70970
|
-
var miroConnector = declarativeRestConnector({
|
|
70971
|
-
kind: "miro",
|
|
70972
|
-
displayName: "Miro",
|
|
70973
|
-
description: "Read and edit Miro boards: list and search boards, read board items (sticky notes, shapes, text, cards, images, frames, connectors), create and update items, and post board content back into the source of truth.",
|
|
70974
|
-
auth: {
|
|
70975
|
-
kind: "oauth2",
|
|
70976
|
-
authorizationUrl: "https://miro.com/oauth/authorize",
|
|
70977
|
-
tokenUrl: "https://api.miro.com/v1/oauth/token",
|
|
70978
|
-
scopes: [
|
|
70979
|
-
"boards:read",
|
|
70980
|
-
"boards:write",
|
|
70981
|
-
"identity:read",
|
|
70982
|
-
"team:read",
|
|
70983
|
-
"organizations:read"
|
|
70984
|
-
],
|
|
70985
|
-
clientIdEnv: "MIRO_OAUTH_CLIENT_ID",
|
|
70986
|
-
clientSecretEnv: "MIRO_OAUTH_CLIENT_SECRET"
|
|
70987
|
-
},
|
|
70988
|
-
category: "doc",
|
|
70989
|
-
defaultConsistencyModel: "authoritative",
|
|
70990
|
-
baseUrl: "https://api.miro.com",
|
|
70991
|
-
defaultHeaders: { accept: "application/json" },
|
|
70992
|
-
// GET /v2/me returns the authenticated user — Miro's documented health probe.
|
|
70993
|
-
test: { method: "GET", path: "/v2/me" },
|
|
70994
|
-
capabilities: [
|
|
70995
|
-
{
|
|
70996
|
-
name: "me.get",
|
|
70997
|
-
class: "read",
|
|
70998
|
-
description: "Return the authenticated Miro user.",
|
|
70999
|
-
parameters: { type: "object", properties: {} },
|
|
71000
|
-
request: { method: "GET", path: "/v2/me" },
|
|
71001
|
-
requiredScopes: ["identity:read"]
|
|
71002
|
-
},
|
|
71003
|
-
{
|
|
71004
|
-
name: "boards.list",
|
|
71005
|
-
class: "read",
|
|
71006
|
-
description: "List boards accessible to the authenticated user. Supports cursor pagination via `cursor` and team / sort filters.",
|
|
71007
|
-
parameters: {
|
|
71008
|
-
type: "object",
|
|
71009
|
-
properties: {
|
|
71010
|
-
team_id: { type: "string", description: "Filter to a single team id." },
|
|
71011
|
-
query: { type: "string", description: "Free-text board name filter." },
|
|
71012
|
-
owner: { type: "string", description: "Filter by owner user id." },
|
|
71013
|
-
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
71014
|
-
offset: { type: "integer", minimum: 0 },
|
|
71015
|
-
sort: {
|
|
71016
|
-
type: "string",
|
|
71017
|
-
enum: ["default", "last_modified", "last_opened", "last_created", "alphabetically"]
|
|
71018
|
-
}
|
|
71019
|
-
}
|
|
71020
|
-
},
|
|
71021
|
-
request: {
|
|
71022
|
-
method: "GET",
|
|
71023
|
-
path: "/v2/boards",
|
|
71024
|
-
query: {
|
|
71025
|
-
team_id: "{team_id}",
|
|
71026
|
-
query: "{query}",
|
|
71027
|
-
owner: "{owner}",
|
|
71028
|
-
limit: "{limit}",
|
|
71029
|
-
offset: "{offset}",
|
|
71030
|
-
sort: "{sort}"
|
|
71031
|
-
}
|
|
71032
|
-
},
|
|
71033
|
-
requiredScopes: ["boards:read"]
|
|
71034
|
-
},
|
|
71035
|
-
{
|
|
71036
|
-
name: "boards.get",
|
|
71037
|
-
class: "read",
|
|
71038
|
-
description: "Fetch a single Miro board by id.",
|
|
71039
|
-
parameters: {
|
|
71040
|
-
type: "object",
|
|
71041
|
-
properties: { board_id: { type: "string" } },
|
|
71042
|
-
required: ["board_id"]
|
|
71043
|
-
},
|
|
71044
|
-
request: { method: "GET", path: "/v2/boards/{board_id}" },
|
|
71045
|
-
requiredScopes: ["boards:read"]
|
|
71046
|
-
},
|
|
71047
|
-
{
|
|
71048
|
-
name: "boards.members.list",
|
|
71049
|
-
class: "read",
|
|
71050
|
-
description: "List members (collaborators) of a Miro board.",
|
|
71051
|
-
parameters: {
|
|
71052
|
-
type: "object",
|
|
71053
|
-
properties: {
|
|
71054
|
-
board_id: { type: "string" },
|
|
71055
|
-
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
71056
|
-
offset: { type: "integer", minimum: 0 }
|
|
71057
|
-
},
|
|
71058
|
-
required: ["board_id"]
|
|
71059
|
-
},
|
|
71060
|
-
request: {
|
|
71061
|
-
method: "GET",
|
|
71062
|
-
path: "/v2/boards/{board_id}/members",
|
|
71063
|
-
query: { limit: "{limit}", offset: "{offset}" }
|
|
71064
|
-
},
|
|
71065
|
-
requiredScopes: ["boards:read"]
|
|
71066
|
-
},
|
|
71067
|
-
{
|
|
71068
|
-
name: "items.list",
|
|
71069
|
-
class: "read",
|
|
71070
|
-
description: "List all items on a board, optionally filtered by item type (sticky_note, text, shape, card, image, frame, connector, embed, app_card, document).",
|
|
71071
|
-
parameters: {
|
|
71072
|
-
type: "object",
|
|
71073
|
-
properties: {
|
|
71074
|
-
board_id: { type: "string" },
|
|
71075
|
-
type: {
|
|
71076
|
-
type: "string",
|
|
71077
|
-
enum: [
|
|
71078
|
-
"sticky_note",
|
|
71079
|
-
"text",
|
|
71080
|
-
"shape",
|
|
71081
|
-
"card",
|
|
71082
|
-
"image",
|
|
71083
|
-
"frame",
|
|
71084
|
-
"connector",
|
|
71085
|
-
"embed",
|
|
71086
|
-
"app_card",
|
|
71087
|
-
"document",
|
|
71088
|
-
"preview"
|
|
71089
|
-
]
|
|
71090
|
-
},
|
|
71091
|
-
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
71092
|
-
cursor: { type: "string" }
|
|
71093
|
-
},
|
|
71094
|
-
required: ["board_id"]
|
|
71095
|
-
},
|
|
71096
|
-
request: {
|
|
71097
|
-
method: "GET",
|
|
71098
|
-
path: "/v2/boards/{board_id}/items",
|
|
71099
|
-
query: { type: "{type}", limit: "{limit}", cursor: "{cursor}" }
|
|
71100
|
-
},
|
|
71101
|
-
requiredScopes: ["boards:read"]
|
|
71102
|
-
},
|
|
71103
|
-
{
|
|
71104
|
-
name: "items.get",
|
|
71105
|
-
class: "read",
|
|
71106
|
-
description: "Fetch a single board item by id.",
|
|
71107
|
-
parameters: {
|
|
71108
|
-
type: "object",
|
|
71109
|
-
properties: {
|
|
71110
|
-
board_id: { type: "string" },
|
|
71111
|
-
item_id: { type: "string" }
|
|
71112
|
-
},
|
|
71113
|
-
required: ["board_id", "item_id"]
|
|
71114
|
-
},
|
|
71115
|
-
request: { method: "GET", path: "/v2/boards/{board_id}/items/{item_id}" },
|
|
71116
|
-
requiredScopes: ["boards:read"]
|
|
71117
|
-
},
|
|
71118
|
-
{
|
|
71119
|
-
name: "connectors.list",
|
|
71120
|
-
class: "read",
|
|
71121
|
-
description: "List connector lines on a board (arrows / lines between items).",
|
|
71122
|
-
parameters: {
|
|
71123
|
-
type: "object",
|
|
71124
|
-
properties: {
|
|
71125
|
-
board_id: { type: "string" },
|
|
71126
|
-
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
71127
|
-
cursor: { type: "string" }
|
|
71128
|
-
},
|
|
71129
|
-
required: ["board_id"]
|
|
71130
|
-
},
|
|
71131
|
-
request: {
|
|
71132
|
-
method: "GET",
|
|
71133
|
-
path: "/v2/boards/{board_id}/connectors",
|
|
71134
|
-
query: { limit: "{limit}", cursor: "{cursor}" }
|
|
71135
|
-
},
|
|
71136
|
-
requiredScopes: ["boards:read"]
|
|
71137
|
-
},
|
|
71138
|
-
{
|
|
71139
|
-
name: "tags.list",
|
|
71140
|
-
class: "read",
|
|
71141
|
-
description: "List tags defined on a board.",
|
|
71142
|
-
parameters: {
|
|
71143
|
-
type: "object",
|
|
71144
|
-
properties: {
|
|
71145
|
-
board_id: { type: "string" },
|
|
71146
|
-
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
71147
|
-
offset: { type: "integer", minimum: 0 }
|
|
71148
|
-
},
|
|
71149
|
-
required: ["board_id"]
|
|
71150
|
-
},
|
|
71151
|
-
request: {
|
|
71152
|
-
method: "GET",
|
|
71153
|
-
path: "/v2/boards/{board_id}/tags",
|
|
71154
|
-
query: { limit: "{limit}", offset: "{offset}" }
|
|
71155
|
-
},
|
|
71156
|
-
requiredScopes: ["boards:read"]
|
|
71157
|
-
},
|
|
71158
|
-
{
|
|
71159
|
-
name: "organizations.get",
|
|
71160
|
-
class: "read",
|
|
71161
|
-
description: "Read an organization (Enterprise plan).",
|
|
71162
|
-
parameters: {
|
|
71163
|
-
type: "object",
|
|
71164
|
-
properties: { org_id: { type: "string" } },
|
|
71165
|
-
required: ["org_id"]
|
|
71166
|
-
},
|
|
71167
|
-
request: { method: "GET", path: "/v2/orgs/{org_id}" },
|
|
71168
|
-
requiredScopes: ["organizations:read"]
|
|
71169
|
-
},
|
|
71170
|
-
{
|
|
71171
|
-
name: "organizations.teams.list",
|
|
71172
|
-
class: "read",
|
|
71173
|
-
description: "List teams in an organization (Enterprise plan).",
|
|
71174
|
-
parameters: {
|
|
71175
|
-
type: "object",
|
|
71176
|
-
properties: {
|
|
71177
|
-
org_id: { type: "string" },
|
|
71178
|
-
limit: { type: "integer", minimum: 1, maximum: 100 },
|
|
71179
|
-
cursor: { type: "string" }
|
|
71180
|
-
},
|
|
71181
|
-
required: ["org_id"]
|
|
71182
|
-
},
|
|
71183
|
-
request: {
|
|
71184
|
-
method: "GET",
|
|
71185
|
-
path: "/v2/orgs/{org_id}/teams",
|
|
71186
|
-
query: { limit: "{limit}", cursor: "{cursor}" }
|
|
71187
|
-
},
|
|
71188
|
-
requiredScopes: ["organizations:read"]
|
|
71189
|
-
},
|
|
71190
|
-
{
|
|
71191
|
-
name: "teams.get",
|
|
71192
|
-
class: "read",
|
|
71193
|
-
description: "Fetch a single team by id.",
|
|
71194
|
-
parameters: {
|
|
71195
|
-
type: "object",
|
|
71196
|
-
properties: {
|
|
71197
|
-
org_id: { type: "string" },
|
|
71198
|
-
team_id: { type: "string" }
|
|
71199
|
-
},
|
|
71200
|
-
required: ["org_id", "team_id"]
|
|
71201
|
-
},
|
|
71202
|
-
request: { method: "GET", path: "/v2/orgs/{org_id}/teams/{team_id}" },
|
|
71203
|
-
requiredScopes: ["team:read"]
|
|
71204
|
-
},
|
|
71205
|
-
{
|
|
71206
|
-
name: "boards.create",
|
|
71207
|
-
class: "mutation",
|
|
71208
|
-
description: "Create a new Miro board. Accepts an optional name, description, team_id, and sharing policy. Returns the new board id used by every items.* endpoint.",
|
|
71209
|
-
parameters: {
|
|
71210
|
-
type: "object",
|
|
71211
|
-
properties: {
|
|
71212
|
-
name: { type: "string", maxLength: 60 },
|
|
71213
|
-
description: { type: "string", maxLength: 300 },
|
|
71214
|
-
team_id: { type: "string" },
|
|
71215
|
-
policy: {
|
|
71216
|
-
type: "object",
|
|
71217
|
-
description: "Permissions + sharing policy. See https://developers.miro.com/reference/create-board for the full PolicyChange shape."
|
|
71218
|
-
}
|
|
71219
|
-
},
|
|
71220
|
-
required: ["name"]
|
|
71221
|
-
},
|
|
71222
|
-
request: {
|
|
71223
|
-
method: "POST",
|
|
71224
|
-
path: "/v2/boards",
|
|
71225
|
-
body: {
|
|
71226
|
-
name: "{name}",
|
|
71227
|
-
description: "{description}",
|
|
71228
|
-
teamId: "{team_id}",
|
|
71229
|
-
policy: "{policy}"
|
|
71230
|
-
}
|
|
71231
|
-
},
|
|
71232
|
-
cas: "native-idempotency",
|
|
71233
|
-
requiredScopes: ["boards:write"]
|
|
71234
|
-
},
|
|
71235
|
-
{
|
|
71236
|
-
name: "boards.update",
|
|
71237
|
-
class: "mutation",
|
|
71238
|
-
description: "Update a Miro board (name, description, team, or sharing policy).",
|
|
71239
|
-
parameters: {
|
|
71240
|
-
type: "object",
|
|
71241
|
-
properties: {
|
|
71242
|
-
board_id: { type: "string" },
|
|
71243
|
-
name: { type: "string", maxLength: 60 },
|
|
71244
|
-
description: { type: "string", maxLength: 300 },
|
|
71245
|
-
team_id: { type: "string" },
|
|
71246
|
-
policy: { type: "object" }
|
|
71247
|
-
},
|
|
71248
|
-
required: ["board_id"]
|
|
71249
|
-
},
|
|
71250
|
-
request: {
|
|
71251
|
-
method: "PATCH",
|
|
71252
|
-
path: "/v2/boards/{board_id}",
|
|
71253
|
-
body: {
|
|
71254
|
-
name: "{name}",
|
|
71255
|
-
description: "{description}",
|
|
71256
|
-
teamId: "{team_id}",
|
|
71257
|
-
policy: "{policy}"
|
|
71258
|
-
}
|
|
71259
|
-
},
|
|
71260
|
-
cas: "native-idempotency",
|
|
71261
|
-
requiredScopes: ["boards:write"]
|
|
71262
|
-
},
|
|
71263
|
-
{
|
|
71264
|
-
name: "boards.delete",
|
|
71265
|
-
class: "mutation",
|
|
71266
|
-
description: "Delete a Miro board (irreversible).",
|
|
71267
|
-
parameters: {
|
|
71268
|
-
type: "object",
|
|
71269
|
-
properties: { board_id: { type: "string" } },
|
|
71270
|
-
required: ["board_id"]
|
|
71271
|
-
},
|
|
71272
|
-
request: { method: "DELETE", path: "/v2/boards/{board_id}" },
|
|
71273
|
-
cas: "native-idempotency",
|
|
71274
|
-
requiredScopes: ["boards:write"]
|
|
71275
|
-
},
|
|
71276
|
-
{
|
|
71277
|
-
name: "sticky_notes.create",
|
|
71278
|
-
class: "mutation",
|
|
71279
|
-
description: "Create a sticky note on a board. `data.content` accepts plain text; `style.fillColor` is a Miro sticky-note color keyword (e.g. yellow, light_yellow, pink).",
|
|
71280
|
-
parameters: {
|
|
71281
|
-
type: "object",
|
|
71282
|
-
properties: {
|
|
71283
|
-
board_id: { type: "string" },
|
|
71284
|
-
data: {
|
|
71285
|
-
type: "object",
|
|
71286
|
-
properties: {
|
|
71287
|
-
content: { type: "string" },
|
|
71288
|
-
shape: { type: "string", enum: ["square", "rectangle"] }
|
|
71289
|
-
},
|
|
71290
|
-
required: ["content"]
|
|
71291
|
-
},
|
|
71292
|
-
style: {
|
|
71293
|
-
type: "object",
|
|
71294
|
-
properties: {
|
|
71295
|
-
fillColor: { type: "string" },
|
|
71296
|
-
textAlign: { type: "string", enum: ["left", "center", "right"] },
|
|
71297
|
-
textAlignVertical: { type: "string", enum: ["top", "middle", "bottom"] }
|
|
71298
|
-
}
|
|
71299
|
-
},
|
|
71300
|
-
position: {
|
|
71301
|
-
type: "object",
|
|
71302
|
-
properties: {
|
|
71303
|
-
x: { type: "number" },
|
|
71304
|
-
y: { type: "number" },
|
|
71305
|
-
origin: { type: "string", enum: ["center"] }
|
|
71306
|
-
}
|
|
71307
|
-
},
|
|
71308
|
-
geometry: {
|
|
71309
|
-
type: "object",
|
|
71310
|
-
properties: {
|
|
71311
|
-
width: { type: "number" },
|
|
71312
|
-
height: { type: "number" }
|
|
71313
|
-
}
|
|
71314
|
-
},
|
|
71315
|
-
parent: {
|
|
71316
|
-
type: "object",
|
|
71317
|
-
description: "Attach the sticky note to a frame: { id: <frame_item_id> }.",
|
|
71318
|
-
properties: { id: { type: "string" } }
|
|
71319
|
-
}
|
|
71320
|
-
},
|
|
71321
|
-
required: ["board_id", "data"]
|
|
71322
|
-
},
|
|
71323
|
-
request: {
|
|
71324
|
-
method: "POST",
|
|
71325
|
-
path: "/v2/boards/{board_id}/sticky_notes",
|
|
71326
|
-
body: {
|
|
71327
|
-
data: "{data}",
|
|
71328
|
-
style: "{style}",
|
|
71329
|
-
position: "{position}",
|
|
71330
|
-
geometry: "{geometry}",
|
|
71331
|
-
parent: "{parent}"
|
|
71332
|
-
}
|
|
71333
|
-
},
|
|
71334
|
-
cas: "native-idempotency",
|
|
71335
|
-
requiredScopes: ["boards:write"]
|
|
71336
|
-
},
|
|
71337
|
-
{
|
|
71338
|
-
name: "sticky_notes.update",
|
|
71339
|
-
class: "mutation",
|
|
71340
|
-
description: "Update a sticky note (content, style, position, geometry, or parent frame).",
|
|
71341
|
-
parameters: {
|
|
71342
|
-
type: "object",
|
|
71343
|
-
properties: {
|
|
71344
|
-
board_id: { type: "string" },
|
|
71345
|
-
item_id: { type: "string" },
|
|
71346
|
-
data: { type: "object" },
|
|
71347
|
-
style: { type: "object" },
|
|
71348
|
-
position: { type: "object" },
|
|
71349
|
-
geometry: { type: "object" },
|
|
71350
|
-
parent: { type: "object" }
|
|
71351
|
-
},
|
|
71352
|
-
required: ["board_id", "item_id"]
|
|
71353
|
-
},
|
|
71354
|
-
request: {
|
|
71355
|
-
method: "PATCH",
|
|
71356
|
-
path: "/v2/boards/{board_id}/sticky_notes/{item_id}",
|
|
71357
|
-
body: {
|
|
71358
|
-
data: "{data}",
|
|
71359
|
-
style: "{style}",
|
|
71360
|
-
position: "{position}",
|
|
71361
|
-
geometry: "{geometry}",
|
|
71362
|
-
parent: "{parent}"
|
|
71363
|
-
}
|
|
71364
|
-
},
|
|
71365
|
-
cas: "native-idempotency",
|
|
71366
|
-
requiredScopes: ["boards:write"]
|
|
71367
|
-
},
|
|
71368
|
-
{
|
|
71369
|
-
name: "text.create",
|
|
71370
|
-
class: "mutation",
|
|
71371
|
-
description: "Create a text item on a board.",
|
|
71372
|
-
parameters: {
|
|
71373
|
-
type: "object",
|
|
71374
|
-
properties: {
|
|
71375
|
-
board_id: { type: "string" },
|
|
71376
|
-
data: {
|
|
71377
|
-
type: "object",
|
|
71378
|
-
properties: { content: { type: "string" } },
|
|
71379
|
-
required: ["content"]
|
|
71380
|
-
},
|
|
71381
|
-
style: { type: "object" },
|
|
71382
|
-
position: { type: "object" },
|
|
71383
|
-
geometry: { type: "object" },
|
|
71384
|
-
parent: { type: "object" }
|
|
71385
|
-
},
|
|
71386
|
-
required: ["board_id", "data"]
|
|
71387
|
-
},
|
|
71388
|
-
request: {
|
|
71389
|
-
method: "POST",
|
|
71390
|
-
path: "/v2/boards/{board_id}/texts",
|
|
71391
|
-
body: {
|
|
71392
|
-
data: "{data}",
|
|
71393
|
-
style: "{style}",
|
|
71394
|
-
position: "{position}",
|
|
71395
|
-
geometry: "{geometry}",
|
|
71396
|
-
parent: "{parent}"
|
|
71397
|
-
}
|
|
71398
|
-
},
|
|
71399
|
-
cas: "native-idempotency",
|
|
71400
|
-
requiredScopes: ["boards:write"]
|
|
71401
|
-
},
|
|
71402
|
-
{
|
|
71403
|
-
name: "shapes.create",
|
|
71404
|
-
class: "mutation",
|
|
71405
|
-
description: "Create a shape item on a board. `data.shape` selects the geometric primitive (rectangle, round_rectangle, circle, triangle, rhombus, etc.).",
|
|
71406
|
-
parameters: {
|
|
71407
|
-
type: "object",
|
|
71408
|
-
properties: {
|
|
71409
|
-
board_id: { type: "string" },
|
|
71410
|
-
data: {
|
|
71411
|
-
type: "object",
|
|
71412
|
-
properties: {
|
|
71413
|
-
shape: {
|
|
71414
|
-
type: "string",
|
|
71415
|
-
description: "Miro shape kind; full enum at https://developers.miro.com/reference/create-shape-item."
|
|
71416
|
-
},
|
|
71417
|
-
content: { type: "string" }
|
|
71418
|
-
},
|
|
71419
|
-
required: ["shape"]
|
|
71420
|
-
},
|
|
71421
|
-
style: { type: "object" },
|
|
71422
|
-
position: { type: "object" },
|
|
71423
|
-
geometry: { type: "object" },
|
|
71424
|
-
parent: { type: "object" }
|
|
71425
|
-
},
|
|
71426
|
-
required: ["board_id", "data"]
|
|
71427
|
-
},
|
|
71428
|
-
request: {
|
|
71429
|
-
method: "POST",
|
|
71430
|
-
path: "/v2/boards/{board_id}/shapes",
|
|
71431
|
-
body: {
|
|
71432
|
-
data: "{data}",
|
|
71433
|
-
style: "{style}",
|
|
71434
|
-
position: "{position}",
|
|
71435
|
-
geometry: "{geometry}",
|
|
71436
|
-
parent: "{parent}"
|
|
71437
|
-
}
|
|
71438
|
-
},
|
|
71439
|
-
cas: "native-idempotency",
|
|
71440
|
-
requiredScopes: ["boards:write"]
|
|
71441
|
-
},
|
|
71442
|
-
{
|
|
71443
|
-
name: "cards.create",
|
|
71444
|
-
class: "mutation",
|
|
71445
|
-
description: "Create a card item (title + description + assignee + due-date) on a board.",
|
|
71446
|
-
parameters: {
|
|
71447
|
-
type: "object",
|
|
71448
|
-
properties: {
|
|
71449
|
-
board_id: { type: "string" },
|
|
71450
|
-
data: {
|
|
71451
|
-
type: "object",
|
|
71452
|
-
properties: {
|
|
71453
|
-
title: { type: "string" },
|
|
71454
|
-
description: { type: "string" },
|
|
71455
|
-
dueDate: { type: "string", format: "date-time" },
|
|
71456
|
-
assigneeId: { type: "string" }
|
|
71457
|
-
},
|
|
71458
|
-
required: ["title"]
|
|
71459
|
-
},
|
|
71460
|
-
style: { type: "object" },
|
|
71461
|
-
position: { type: "object" },
|
|
71462
|
-
geometry: { type: "object" },
|
|
71463
|
-
parent: { type: "object" }
|
|
71464
|
-
},
|
|
71465
|
-
required: ["board_id", "data"]
|
|
71466
|
-
},
|
|
71467
|
-
request: {
|
|
71468
|
-
method: "POST",
|
|
71469
|
-
path: "/v2/boards/{board_id}/cards",
|
|
71470
|
-
body: {
|
|
71471
|
-
data: "{data}",
|
|
71472
|
-
style: "{style}",
|
|
71473
|
-
position: "{position}",
|
|
71474
|
-
geometry: "{geometry}",
|
|
71475
|
-
parent: "{parent}"
|
|
71476
|
-
}
|
|
71477
|
-
},
|
|
71478
|
-
cas: "native-idempotency",
|
|
71479
|
-
requiredScopes: ["boards:write"]
|
|
71480
|
-
},
|
|
71481
|
-
{
|
|
71482
|
-
name: "frames.create",
|
|
71483
|
-
class: "mutation",
|
|
71484
|
-
description: "Create a frame on a board. Frames group items; child items can be attached via `parent.id` on item creation.",
|
|
71485
|
-
parameters: {
|
|
71486
|
-
type: "object",
|
|
71487
|
-
properties: {
|
|
71488
|
-
board_id: { type: "string" },
|
|
71489
|
-
data: {
|
|
71490
|
-
type: "object",
|
|
71491
|
-
properties: {
|
|
71492
|
-
title: { type: "string" },
|
|
71493
|
-
format: { type: "string", enum: ["custom"] },
|
|
71494
|
-
type: { type: "string", enum: ["freeform"] }
|
|
71495
|
-
}
|
|
71496
|
-
},
|
|
71497
|
-
style: { type: "object" },
|
|
71498
|
-
position: { type: "object" },
|
|
71499
|
-
geometry: { type: "object" }
|
|
71500
|
-
},
|
|
71501
|
-
required: ["board_id"]
|
|
71502
|
-
},
|
|
71503
|
-
request: {
|
|
71504
|
-
method: "POST",
|
|
71505
|
-
path: "/v2/boards/{board_id}/frames",
|
|
71506
|
-
body: {
|
|
71507
|
-
data: "{data}",
|
|
71508
|
-
style: "{style}",
|
|
71509
|
-
position: "{position}",
|
|
71510
|
-
geometry: "{geometry}"
|
|
71511
|
-
}
|
|
71512
|
-
},
|
|
71513
|
-
cas: "native-idempotency",
|
|
71514
|
-
requiredScopes: ["boards:write"]
|
|
71515
|
-
},
|
|
71516
|
-
{
|
|
71517
|
-
name: "images.create.by_url",
|
|
71518
|
-
class: "mutation",
|
|
71519
|
-
description: "Create an image item from a public URL (Miro fetches the image server-side).",
|
|
71520
|
-
parameters: {
|
|
71521
|
-
type: "object",
|
|
71522
|
-
properties: {
|
|
71523
|
-
board_id: { type: "string" },
|
|
71524
|
-
data: {
|
|
71525
|
-
type: "object",
|
|
71526
|
-
properties: {
|
|
71527
|
-
url: { type: "string", format: "uri" },
|
|
71528
|
-
title: { type: "string" }
|
|
71529
|
-
},
|
|
71530
|
-
required: ["url"]
|
|
71531
|
-
},
|
|
71532
|
-
position: { type: "object" },
|
|
71533
|
-
geometry: { type: "object" },
|
|
71534
|
-
parent: { type: "object" }
|
|
71535
|
-
},
|
|
71536
|
-
required: ["board_id", "data"]
|
|
71537
|
-
},
|
|
71538
|
-
request: {
|
|
71539
|
-
method: "POST",
|
|
71540
|
-
path: "/v2/boards/{board_id}/images",
|
|
71541
|
-
body: {
|
|
71542
|
-
data: "{data}",
|
|
71543
|
-
position: "{position}",
|
|
71544
|
-
geometry: "{geometry}",
|
|
71545
|
-
parent: "{parent}"
|
|
71546
|
-
}
|
|
71547
|
-
},
|
|
71548
|
-
cas: "native-idempotency",
|
|
71549
|
-
requiredScopes: ["boards:write"]
|
|
71550
|
-
},
|
|
71551
|
-
{
|
|
71552
|
-
name: "connectors.create",
|
|
71553
|
-
class: "mutation",
|
|
71554
|
-
description: "Create a connector line between two items on the board. `startItem.id` and `endItem.id` reference existing item ids.",
|
|
71555
|
-
parameters: {
|
|
71556
|
-
type: "object",
|
|
71557
|
-
properties: {
|
|
71558
|
-
board_id: { type: "string" },
|
|
71559
|
-
startItem: {
|
|
71560
|
-
type: "object",
|
|
71561
|
-
properties: { id: { type: "string" } },
|
|
71562
|
-
required: ["id"]
|
|
71563
|
-
},
|
|
71564
|
-
endItem: {
|
|
71565
|
-
type: "object",
|
|
71566
|
-
properties: { id: { type: "string" } },
|
|
71567
|
-
required: ["id"]
|
|
71568
|
-
},
|
|
71569
|
-
captions: {
|
|
71570
|
-
type: "array",
|
|
71571
|
-
items: { type: "object", properties: { content: { type: "string" } } }
|
|
71572
|
-
},
|
|
71573
|
-
style: { type: "object" },
|
|
71574
|
-
shape: { type: "string", enum: ["straight", "elbowed", "curved"] }
|
|
71575
|
-
},
|
|
71576
|
-
required: ["board_id", "startItem", "endItem"]
|
|
71577
|
-
},
|
|
71578
|
-
request: {
|
|
71579
|
-
method: "POST",
|
|
71580
|
-
path: "/v2/boards/{board_id}/connectors",
|
|
71581
|
-
body: {
|
|
71582
|
-
startItem: "{startItem}",
|
|
71583
|
-
endItem: "{endItem}",
|
|
71584
|
-
captions: "{captions}",
|
|
71585
|
-
style: "{style}",
|
|
71586
|
-
shape: "{shape}"
|
|
71587
|
-
}
|
|
71588
|
-
},
|
|
71589
|
-
cas: "native-idempotency",
|
|
71590
|
-
requiredScopes: ["boards:write"]
|
|
71591
|
-
},
|
|
71592
|
-
{
|
|
71593
|
-
name: "items.delete",
|
|
71594
|
-
class: "mutation",
|
|
71595
|
-
description: "Delete an item from a board (works across all item types).",
|
|
71596
|
-
parameters: {
|
|
71597
|
-
type: "object",
|
|
71598
|
-
properties: {
|
|
71599
|
-
board_id: { type: "string" },
|
|
71600
|
-
item_id: { type: "string" }
|
|
71601
|
-
},
|
|
71602
|
-
required: ["board_id", "item_id"]
|
|
71603
|
-
},
|
|
71604
|
-
request: { method: "DELETE", path: "/v2/boards/{board_id}/items/{item_id}" },
|
|
71605
|
-
cas: "native-idempotency",
|
|
71606
|
-
requiredScopes: ["boards:write"]
|
|
71607
|
-
},
|
|
71608
|
-
{
|
|
71609
|
-
name: "boards.share",
|
|
71610
|
-
class: "mutation",
|
|
71611
|
-
description: "Invite users to a Miro board by email. Roles: viewer, commenter, editor, coowner.",
|
|
71612
|
-
parameters: {
|
|
71613
|
-
type: "object",
|
|
71614
|
-
properties: {
|
|
71615
|
-
board_id: { type: "string" },
|
|
71616
|
-
emails: { type: "array", items: { type: "string", format: "email" } },
|
|
71617
|
-
role: {
|
|
71618
|
-
type: "string",
|
|
71619
|
-
enum: ["viewer", "commenter", "editor", "coowner"]
|
|
71620
|
-
},
|
|
71621
|
-
message: { type: "string" }
|
|
71622
|
-
},
|
|
71623
|
-
required: ["board_id", "emails", "role"]
|
|
71624
|
-
},
|
|
71625
|
-
request: {
|
|
71626
|
-
method: "POST",
|
|
71627
|
-
path: "/v2/boards/{board_id}/members",
|
|
71628
|
-
body: {
|
|
71629
|
-
emails: "{emails}",
|
|
71630
|
-
role: "{role}",
|
|
71631
|
-
message: "{message}"
|
|
71632
|
-
}
|
|
71633
|
-
},
|
|
71634
|
-
cas: "native-idempotency",
|
|
71635
|
-
requiredScopes: ["boards:write"]
|
|
71636
|
-
}
|
|
71637
|
-
]
|
|
71638
|
-
});
|
|
71639
|
-
|
|
71640
73959
|
// src/connectors/adapters/canva.ts
|
|
71641
73960
|
var canvaConnector = declarativeRestConnector({
|
|
71642
73961
|
kind: "canva",
|
|
@@ -119253,309 +121572,6 @@ var mindStudioConnector = declarativeRestConnector({
|
|
|
119253
121572
|
]
|
|
119254
121573
|
});
|
|
119255
121574
|
|
|
119256
|
-
// src/connectors/adapters/mongodb.ts
|
|
119257
|
-
var mongodbConnector = declarativeRestConnector({
|
|
119258
|
-
kind: "mongodb",
|
|
119259
|
-
displayName: "MongoDB",
|
|
119260
|
-
description: "Query, insert, update, and delete MongoDB documents via the Data API.",
|
|
119261
|
-
auth: { kind: "api-key", hint: "MongoDB Data API key." },
|
|
119262
|
-
category: "database",
|
|
119263
|
-
defaultConsistencyModel: "authoritative",
|
|
119264
|
-
baseUrl: "https://data.mongodb-api.com/app",
|
|
119265
|
-
test: { method: "POST", path: "/{appId}/endpoint/data/v1/action/findOne" },
|
|
119266
|
-
capabilities: [
|
|
119267
|
-
{
|
|
119268
|
-
name: "documents.find",
|
|
119269
|
-
class: "read",
|
|
119270
|
-
description: "Query documents in a collection.",
|
|
119271
|
-
parameters: {
|
|
119272
|
-
type: "object",
|
|
119273
|
-
properties: {
|
|
119274
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119275
|
-
dataSourceName: { type: "string", description: 'Data source name (e.g., "Cluster0").' },
|
|
119276
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119277
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119278
|
-
filter: { type: "object", description: 'Query filter (e.g., { "status": "active" }).' },
|
|
119279
|
-
projection: { type: "object", description: "Fields to include/exclude." },
|
|
119280
|
-
sort: { type: "object", description: "Sort criteria." },
|
|
119281
|
-
limit: { type: "integer", description: "Maximum documents to return." },
|
|
119282
|
-
skip: { type: "integer", description: "Documents to skip." }
|
|
119283
|
-
},
|
|
119284
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName"]
|
|
119285
|
-
},
|
|
119286
|
-
request: {
|
|
119287
|
-
method: "POST",
|
|
119288
|
-
path: "/{appId}/endpoint/data/v1/action/find",
|
|
119289
|
-
body: {
|
|
119290
|
-
dataSource: "{dataSourceName}",
|
|
119291
|
-
database: "{databaseName}",
|
|
119292
|
-
collection: "{collectionName}",
|
|
119293
|
-
filter: "{filter}",
|
|
119294
|
-
projection: "{projection}",
|
|
119295
|
-
sort: "{sort}",
|
|
119296
|
-
limit: "{limit}",
|
|
119297
|
-
skip: "{skip}"
|
|
119298
|
-
}
|
|
119299
|
-
}
|
|
119300
|
-
},
|
|
119301
|
-
{
|
|
119302
|
-
name: "documents.findOne",
|
|
119303
|
-
class: "read",
|
|
119304
|
-
description: "Find a single document matching a filter.",
|
|
119305
|
-
parameters: {
|
|
119306
|
-
type: "object",
|
|
119307
|
-
properties: {
|
|
119308
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119309
|
-
dataSourceName: { type: "string", description: "Data source name." },
|
|
119310
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119311
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119312
|
-
filter: { type: "object", description: "Query filter." },
|
|
119313
|
-
projection: { type: "object", description: "Fields to include/exclude." }
|
|
119314
|
-
},
|
|
119315
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName"]
|
|
119316
|
-
},
|
|
119317
|
-
request: {
|
|
119318
|
-
method: "POST",
|
|
119319
|
-
path: "/{appId}/endpoint/data/v1/action/findOne",
|
|
119320
|
-
body: {
|
|
119321
|
-
dataSource: "{dataSourceName}",
|
|
119322
|
-
database: "{databaseName}",
|
|
119323
|
-
collection: "{collectionName}",
|
|
119324
|
-
filter: "{filter}",
|
|
119325
|
-
projection: "{projection}"
|
|
119326
|
-
}
|
|
119327
|
-
}
|
|
119328
|
-
},
|
|
119329
|
-
{
|
|
119330
|
-
name: "documents.insertOne",
|
|
119331
|
-
class: "mutation",
|
|
119332
|
-
description: "Insert a single document into a collection.",
|
|
119333
|
-
parameters: {
|
|
119334
|
-
type: "object",
|
|
119335
|
-
properties: {
|
|
119336
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119337
|
-
dataSourceName: { type: "string", description: "Data source name." },
|
|
119338
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119339
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119340
|
-
document: { type: "object", description: "Document to insert." }
|
|
119341
|
-
},
|
|
119342
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName", "document"]
|
|
119343
|
-
},
|
|
119344
|
-
request: {
|
|
119345
|
-
method: "POST",
|
|
119346
|
-
path: "/{appId}/endpoint/data/v1/action/insertOne",
|
|
119347
|
-
body: {
|
|
119348
|
-
dataSource: "{dataSourceName}",
|
|
119349
|
-
database: "{databaseName}",
|
|
119350
|
-
collection: "{collectionName}",
|
|
119351
|
-
document: "{document}"
|
|
119352
|
-
}
|
|
119353
|
-
},
|
|
119354
|
-
cas: "native-idempotency"
|
|
119355
|
-
},
|
|
119356
|
-
{
|
|
119357
|
-
name: "documents.insertMany",
|
|
119358
|
-
class: "mutation",
|
|
119359
|
-
description: "Insert multiple documents into a collection.",
|
|
119360
|
-
parameters: {
|
|
119361
|
-
type: "object",
|
|
119362
|
-
properties: {
|
|
119363
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119364
|
-
dataSourceName: { type: "string", description: "Data source name." },
|
|
119365
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119366
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119367
|
-
documents: { type: "array", items: { type: "object" }, description: "Array of documents to insert." }
|
|
119368
|
-
},
|
|
119369
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName", "documents"]
|
|
119370
|
-
},
|
|
119371
|
-
request: {
|
|
119372
|
-
method: "POST",
|
|
119373
|
-
path: "/{appId}/endpoint/data/v1/action/insertMany",
|
|
119374
|
-
body: {
|
|
119375
|
-
dataSource: "{dataSourceName}",
|
|
119376
|
-
database: "{databaseName}",
|
|
119377
|
-
collection: "{collectionName}",
|
|
119378
|
-
documents: "{documents}"
|
|
119379
|
-
}
|
|
119380
|
-
},
|
|
119381
|
-
cas: "native-idempotency"
|
|
119382
|
-
},
|
|
119383
|
-
{
|
|
119384
|
-
name: "documents.updateOne",
|
|
119385
|
-
class: "mutation",
|
|
119386
|
-
description: "Update a single document matching a filter.",
|
|
119387
|
-
parameters: {
|
|
119388
|
-
type: "object",
|
|
119389
|
-
properties: {
|
|
119390
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119391
|
-
dataSourceName: { type: "string", description: "Data source name." },
|
|
119392
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119393
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119394
|
-
filter: { type: "object", description: "Query filter." },
|
|
119395
|
-
update: { type: "object", description: 'Update operations (e.g., { "$set": { "status": "inactive" } }).' },
|
|
119396
|
-
upsert: { type: "boolean", description: "Insert if no match found." }
|
|
119397
|
-
},
|
|
119398
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName", "filter", "update"]
|
|
119399
|
-
},
|
|
119400
|
-
request: {
|
|
119401
|
-
method: "POST",
|
|
119402
|
-
path: "/{appId}/endpoint/data/v1/action/updateOne",
|
|
119403
|
-
body: {
|
|
119404
|
-
dataSource: "{dataSourceName}",
|
|
119405
|
-
database: "{databaseName}",
|
|
119406
|
-
collection: "{collectionName}",
|
|
119407
|
-
filter: "{filter}",
|
|
119408
|
-
update: "{update}",
|
|
119409
|
-
upsert: "{upsert}"
|
|
119410
|
-
}
|
|
119411
|
-
},
|
|
119412
|
-
cas: "optimistic-read-verify"
|
|
119413
|
-
},
|
|
119414
|
-
{
|
|
119415
|
-
name: "documents.updateMany",
|
|
119416
|
-
class: "mutation",
|
|
119417
|
-
description: "Update multiple documents matching a filter.",
|
|
119418
|
-
parameters: {
|
|
119419
|
-
type: "object",
|
|
119420
|
-
properties: {
|
|
119421
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119422
|
-
dataSourceName: { type: "string", description: "Data source name." },
|
|
119423
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119424
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119425
|
-
filter: { type: "object", description: "Query filter." },
|
|
119426
|
-
update: { type: "object", description: "Update operations." },
|
|
119427
|
-
upsert: { type: "boolean", description: "Insert if no match found." }
|
|
119428
|
-
},
|
|
119429
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName", "filter", "update"]
|
|
119430
|
-
},
|
|
119431
|
-
request: {
|
|
119432
|
-
method: "POST",
|
|
119433
|
-
path: "/{appId}/endpoint/data/v1/action/updateMany",
|
|
119434
|
-
body: {
|
|
119435
|
-
dataSource: "{dataSourceName}",
|
|
119436
|
-
database: "{databaseName}",
|
|
119437
|
-
collection: "{collectionName}",
|
|
119438
|
-
filter: "{filter}",
|
|
119439
|
-
update: "{update}",
|
|
119440
|
-
upsert: "{upsert}"
|
|
119441
|
-
}
|
|
119442
|
-
},
|
|
119443
|
-
cas: "optimistic-read-verify"
|
|
119444
|
-
},
|
|
119445
|
-
{
|
|
119446
|
-
name: "documents.replaceOne",
|
|
119447
|
-
class: "mutation",
|
|
119448
|
-
description: "Replace a single document entirely.",
|
|
119449
|
-
parameters: {
|
|
119450
|
-
type: "object",
|
|
119451
|
-
properties: {
|
|
119452
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119453
|
-
dataSourceName: { type: "string", description: "Data source name." },
|
|
119454
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119455
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119456
|
-
filter: { type: "object", description: "Query filter." },
|
|
119457
|
-
replacement: { type: "object", description: "Replacement document." },
|
|
119458
|
-
upsert: { type: "boolean", description: "Insert if no match found." }
|
|
119459
|
-
},
|
|
119460
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName", "filter", "replacement"]
|
|
119461
|
-
},
|
|
119462
|
-
request: {
|
|
119463
|
-
method: "POST",
|
|
119464
|
-
path: "/{appId}/endpoint/data/v1/action/replaceOne",
|
|
119465
|
-
body: {
|
|
119466
|
-
dataSource: "{dataSourceName}",
|
|
119467
|
-
database: "{databaseName}",
|
|
119468
|
-
collection: "{collectionName}",
|
|
119469
|
-
filter: "{filter}",
|
|
119470
|
-
replacement: "{replacement}",
|
|
119471
|
-
upsert: "{upsert}"
|
|
119472
|
-
}
|
|
119473
|
-
},
|
|
119474
|
-
cas: "etag-if-match"
|
|
119475
|
-
},
|
|
119476
|
-
{
|
|
119477
|
-
name: "documents.deleteOne",
|
|
119478
|
-
class: "mutation",
|
|
119479
|
-
description: "Delete a single document matching a filter.",
|
|
119480
|
-
parameters: {
|
|
119481
|
-
type: "object",
|
|
119482
|
-
properties: {
|
|
119483
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119484
|
-
dataSourceName: { type: "string", description: "Data source name." },
|
|
119485
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119486
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119487
|
-
filter: { type: "object", description: "Query filter." }
|
|
119488
|
-
},
|
|
119489
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName", "filter"]
|
|
119490
|
-
},
|
|
119491
|
-
request: {
|
|
119492
|
-
method: "POST",
|
|
119493
|
-
path: "/{appId}/endpoint/data/v1/action/deleteOne",
|
|
119494
|
-
body: {
|
|
119495
|
-
dataSource: "{dataSourceName}",
|
|
119496
|
-
database: "{databaseName}",
|
|
119497
|
-
collection: "{collectionName}",
|
|
119498
|
-
filter: "{filter}"
|
|
119499
|
-
}
|
|
119500
|
-
},
|
|
119501
|
-
cas: "native-idempotency"
|
|
119502
|
-
},
|
|
119503
|
-
{
|
|
119504
|
-
name: "documents.deleteMany",
|
|
119505
|
-
class: "mutation",
|
|
119506
|
-
description: "Delete multiple documents matching a filter.",
|
|
119507
|
-
parameters: {
|
|
119508
|
-
type: "object",
|
|
119509
|
-
properties: {
|
|
119510
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119511
|
-
dataSourceName: { type: "string", description: "Data source name." },
|
|
119512
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119513
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119514
|
-
filter: { type: "object", description: "Query filter." }
|
|
119515
|
-
},
|
|
119516
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName", "filter"]
|
|
119517
|
-
},
|
|
119518
|
-
request: {
|
|
119519
|
-
method: "POST",
|
|
119520
|
-
path: "/{appId}/endpoint/data/v1/action/deleteMany",
|
|
119521
|
-
body: {
|
|
119522
|
-
dataSource: "{dataSourceName}",
|
|
119523
|
-
database: "{databaseName}",
|
|
119524
|
-
collection: "{collectionName}",
|
|
119525
|
-
filter: "{filter}"
|
|
119526
|
-
}
|
|
119527
|
-
},
|
|
119528
|
-
cas: "native-idempotency"
|
|
119529
|
-
},
|
|
119530
|
-
{
|
|
119531
|
-
name: "documents.aggregate",
|
|
119532
|
-
class: "read",
|
|
119533
|
-
description: "Run an aggregation pipeline over a collection.",
|
|
119534
|
-
parameters: {
|
|
119535
|
-
type: "object",
|
|
119536
|
-
properties: {
|
|
119537
|
-
appId: { type: "string", description: "MongoDB Data API application ID." },
|
|
119538
|
-
dataSourceName: { type: "string", description: "Data source name." },
|
|
119539
|
-
databaseName: { type: "string", description: "Database name." },
|
|
119540
|
-
collectionName: { type: "string", description: "Collection name." },
|
|
119541
|
-
pipeline: { type: "array", items: { type: "object" }, description: "Aggregation stages." }
|
|
119542
|
-
},
|
|
119543
|
-
required: ["appId", "dataSourceName", "databaseName", "collectionName", "pipeline"]
|
|
119544
|
-
},
|
|
119545
|
-
request: {
|
|
119546
|
-
method: "POST",
|
|
119547
|
-
path: "/{appId}/endpoint/data/v1/action/aggregate",
|
|
119548
|
-
body: {
|
|
119549
|
-
dataSource: "{dataSourceName}",
|
|
119550
|
-
database: "{databaseName}",
|
|
119551
|
-
collection: "{collectionName}",
|
|
119552
|
-
pipeline: "{pipeline}"
|
|
119553
|
-
}
|
|
119554
|
-
}
|
|
119555
|
-
}
|
|
119556
|
-
]
|
|
119557
|
-
});
|
|
119558
|
-
|
|
119559
121575
|
// src/connectors/adapters/nifty.ts
|
|
119560
121576
|
var niftyConnector = declarativeRestConnector({
|
|
119561
121577
|
kind: "nifty",
|
|
@@ -124137,45 +126153,6 @@ var pollybotAiConnector = declarativeRestConnector({
|
|
|
124137
126153
|
]
|
|
124138
126154
|
});
|
|
124139
126155
|
|
|
124140
|
-
// src/connectors/adapters/postgres.ts
|
|
124141
|
-
var postgresConnector = declarativeRestConnector({
|
|
124142
|
-
kind: "postgres",
|
|
124143
|
-
displayName: "PostgreSQL",
|
|
124144
|
-
description: "Execute queries and monitor PostgreSQL databases.",
|
|
124145
|
-
auth: { kind: "api-key", hint: "PostgreSQL connection credentials." },
|
|
124146
|
-
category: "database",
|
|
124147
|
-
defaultConsistencyModel: "authoritative",
|
|
124148
|
-
baseUrl: "postgresql://",
|
|
124149
|
-
test: { method: "GET", path: "/" },
|
|
124150
|
-
capabilities: [
|
|
124151
|
-
{
|
|
124152
|
-
name: "query.execute",
|
|
124153
|
-
class: "read",
|
|
124154
|
-
description: "Execute a SQL query against the database.",
|
|
124155
|
-
parameters: {
|
|
124156
|
-
type: "object",
|
|
124157
|
-
properties: {
|
|
124158
|
-
host: { type: "string", description: "PostgreSQL server hostname" },
|
|
124159
|
-
port: { type: "integer", description: "PostgreSQL server port" },
|
|
124160
|
-
user: { type: "string", description: "Database user" },
|
|
124161
|
-
password: { type: "string", description: "Database password" },
|
|
124162
|
-
database: { type: "string", description: "Database name" },
|
|
124163
|
-
query: { type: "string", description: "SQL query to execute" },
|
|
124164
|
-
args: { type: "array", description: "Query arguments for parameterized queries" },
|
|
124165
|
-
query_timeout: { type: "integer", description: "Query timeout in milliseconds" },
|
|
124166
|
-
connection_timeout_ms: { type: "integer", description: "Connection timeout in milliseconds" },
|
|
124167
|
-
application_name: { type: "string", description: "Application name for the connection" },
|
|
124168
|
-
enable_ssl: { type: "boolean", description: "Enable SSL connection" },
|
|
124169
|
-
reject_unauthorized: { type: "boolean", description: "Verify server certificate" },
|
|
124170
|
-
certificate: { type: "string", description: "CA certificate for SSL verification" }
|
|
124171
|
-
},
|
|
124172
|
-
required: ["host", "port", "user", "password", "database", "query"]
|
|
124173
|
-
},
|
|
124174
|
-
request: { method: "POST", path: "/query", body: "{query}" }
|
|
124175
|
-
}
|
|
124176
|
-
]
|
|
124177
|
-
});
|
|
124178
|
-
|
|
124179
126156
|
// src/connectors/adapters/postiz.ts
|
|
124180
126157
|
var postizConnector = declarativeRestConnector({
|
|
124181
126158
|
kind: "postiz",
|
|
@@ -144139,6 +146116,7 @@ export {
|
|
|
144139
146116
|
deepgramConnector,
|
|
144140
146117
|
detectingAiConnector,
|
|
144141
146118
|
digitalPilotConnector,
|
|
146119
|
+
duckdbConnector,
|
|
144142
146120
|
docsbotConnector,
|
|
144143
146121
|
dripConnector,
|
|
144144
146122
|
echowinConnector,
|
|
@@ -144269,7 +146247,10 @@ export {
|
|
|
144269
146247
|
mastodonConnector,
|
|
144270
146248
|
meetgeekAiConnector,
|
|
144271
146249
|
mollieConnector,
|
|
146250
|
+
miroConnector,
|
|
144272
146251
|
mondayConnector,
|
|
146252
|
+
createMongoDbConnector,
|
|
146253
|
+
mongodbConnector,
|
|
144273
146254
|
mycaseConnector,
|
|
144274
146255
|
notionConnector,
|
|
144275
146256
|
notion,
|
|
@@ -144287,6 +146268,14 @@ export {
|
|
|
144287
146268
|
pipedriveConnector,
|
|
144288
146269
|
parquetFilesConnector,
|
|
144289
146270
|
quickbooksConnector,
|
|
146271
|
+
createRabbitMqConnector,
|
|
146272
|
+
rabbitMqConnector,
|
|
146273
|
+
createRedisConnector,
|
|
146274
|
+
redisConnector,
|
|
146275
|
+
createRedshiftConnector,
|
|
146276
|
+
redshiftConnector,
|
|
146277
|
+
createPostgresConnector,
|
|
146278
|
+
postgresConnector,
|
|
144290
146279
|
recallAiConnector,
|
|
144291
146280
|
rampConnector,
|
|
144292
146281
|
ringcentralConnector,
|
|
@@ -144354,11 +146343,14 @@ export {
|
|
|
144354
146343
|
googleBigqueryConnector,
|
|
144355
146344
|
hightouchConnector,
|
|
144356
146345
|
metabaseConnector,
|
|
146346
|
+
createMySqlConnector,
|
|
146347
|
+
mysqlConnector,
|
|
144357
146348
|
segmentConnector,
|
|
144358
146349
|
supabaseConnector,
|
|
144359
146350
|
builtwithConnector,
|
|
144360
146351
|
fullenrichConnector,
|
|
144361
146352
|
hunterConnector,
|
|
146353
|
+
neverbounceConnector,
|
|
144362
146354
|
theirstackConnector,
|
|
144363
146355
|
zerobounceConnector,
|
|
144364
146356
|
CONNECTOR_ADAPTER_FACTORIES,
|
|
@@ -144384,7 +146376,6 @@ export {
|
|
|
144384
146376
|
sanityConnector,
|
|
144385
146377
|
figmaConnector,
|
|
144386
146378
|
figjamConnector,
|
|
144387
|
-
miroConnector,
|
|
144388
146379
|
canvaConnector,
|
|
144389
146380
|
adobeCreativeCloudConnector,
|
|
144390
146381
|
clioConnector,
|
|
@@ -144604,7 +146595,6 @@ export {
|
|
|
144604
146595
|
mailchainConnector,
|
|
144605
146596
|
medullarConnector,
|
|
144606
146597
|
mindStudioConnector,
|
|
144607
|
-
mongodbConnector,
|
|
144608
146598
|
niftyConnector,
|
|
144609
146599
|
nocodbConnector,
|
|
144610
146600
|
ntfyConnector,
|
|
@@ -144630,7 +146620,6 @@ export {
|
|
|
144630
146620
|
plausibleConnector,
|
|
144631
146621
|
pocketbaseConnector,
|
|
144632
146622
|
pollybotAiConnector,
|
|
144633
|
-
postgresConnector,
|
|
144634
146623
|
postizConnector,
|
|
144635
146624
|
posthogConnector,
|
|
144636
146625
|
predictLeadsConnector,
|
|
@@ -144745,4 +146734,4 @@ export {
|
|
|
144745
146734
|
demandbaseConnector,
|
|
144746
146735
|
adapters_exports
|
|
144747
146736
|
};
|
|
144748
|
-
//# sourceMappingURL=chunk-
|
|
146737
|
+
//# sourceMappingURL=chunk-R5GQB3SQ.js.map
|