@superblocksteam/sdk-api 2.0.104-next.0 → 2.0.104-next.1

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.
@@ -22,7 +22,7 @@ import type { TraceMetadata } from "../registry.js";
22
22
  * });
23
23
  *
24
24
  * const logs = await athena.query(
25
- * 'SELECT * FROM logs WHERE level = $1',
25
+ * 'SELECT * FROM logs WHERE level = ?',
26
26
  * LogSchema,
27
27
  * ['ERROR']
28
28
  * );
@@ -32,7 +32,7 @@ export interface AthenaClient extends BaseIntegrationClient {
32
32
  /**
33
33
  * Execute a SQL query and validate results against a Zod schema.
34
34
  *
35
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
35
+ * @param sql - SQL query string (use ? placeholders for parameters)
36
36
  * @param schema - Zod schema to validate the query results
37
37
  * @param params - Optional query parameters
38
38
  * @returns Promise resolving to validated query results
@@ -22,7 +22,7 @@ import type { TraceMetadata } from "../registry.js";
22
22
  * });
23
23
  *
24
24
  * const events = await bigquery.query(
25
- * 'SELECT * FROM events WHERE date = $1',
25
+ * 'SELECT * FROM events WHERE date = ?',
26
26
  * EventSchema,
27
27
  * ['2024-01-01']
28
28
  * );
@@ -32,9 +32,9 @@ export interface BigQueryClient extends BaseIntegrationClient {
32
32
  /**
33
33
  * Execute a SQL query and validate results against a Zod schema.
34
34
  *
35
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
35
+ * @param sql - SQL query string (use ? placeholders for parameters)
36
36
  * @param schema - Zod schema to validate the query results
37
- * @param params - Optional query parameters
37
+ * @param params - Optional query parameters, bound positionally from the provided array
38
38
  * @returns Promise resolving to validated query results
39
39
  *
40
40
  * @throws {QueryValidationError} If results don't match schema
@@ -22,7 +22,7 @@ import type { TraceMetadata } from "../registry.js";
22
22
  * });
23
23
  *
24
24
  * const data = await databricks.query(
25
- * 'SELECT * FROM data WHERE date = $1',
25
+ * 'SELECT * FROM data WHERE date = :PARAM_1',
26
26
  * DataSchema,
27
27
  * ['2024-01-01']
28
28
  * );
@@ -32,9 +32,9 @@ export interface DatabricksClient extends BaseIntegrationClient {
32
32
  /**
33
33
  * Execute a SQL query and validate results against a Zod schema.
34
34
  *
35
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
35
+ * @param sql - SQL query string (use :PARAM_1, :PARAM_2, etc. for parameters)
36
36
  * @param schema - Zod schema to validate the query results
37
- * @param params - Optional query parameters
37
+ * @param params - Optional query parameters, bound positionally so the first value maps to :PARAM_1
38
38
  * @returns Promise resolving to validated query results
39
39
  *
40
40
  * @throws {QueryValidationError} If results don't match schema
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=documentation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documentation.test.d.ts","sourceRoot":"","sources":["../../src/integrations/documentation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,83 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { describe, expect, it } from "vitest";
5
+ import { SUPPORTED_PLUGINS } from "./registry.js";
6
+ const TEST_DIR = dirname(fileURLToPath(import.meta.url));
7
+ const DOC_DIRECTORY_ALIASES = {
8
+ confluent: "kafka",
9
+ graphqlintegration: "graphql",
10
+ redpanda: "kafka",
11
+ };
12
+ const DOC_DIRECTORY_EXCLUSIONS = new Set(["base"]);
13
+ function hasDocDirectoryAlias(pluginId) {
14
+ return pluginId in DOC_DIRECTORY_ALIASES;
15
+ }
16
+ const EXPECTED_DOC_DIRECTORIES = Array.from(new Set(Object.values(SUPPORTED_PLUGINS)
17
+ .map((pluginId) => hasDocDirectoryAlias(pluginId)
18
+ ? DOC_DIRECTORY_ALIASES[pluginId]
19
+ : pluginId)
20
+ .filter((directory) => !DOC_DIRECTORY_EXCLUSIONS.has(directory)))).sort();
21
+ const ACTUAL_DOC_DIRECTORIES = readdirSync(TEST_DIR, { withFileTypes: true })
22
+ .filter((entry) => entry.isDirectory())
23
+ .map((entry) => entry.name)
24
+ .filter((directory) => !DOC_DIRECTORY_EXCLUSIONS.has(directory))
25
+ .sort();
26
+ const PLACEHOLDER_DOC_CASES = [
27
+ { integrationId: "athena", expectedPlaceholder: "?" },
28
+ { integrationId: "bigquery", expectedPlaceholder: "?" },
29
+ {
30
+ integrationId: "cockroachdb",
31
+ expectedPlaceholder: "$1",
32
+ rejectsPostgresStyle: false,
33
+ },
34
+ { integrationId: "databricks", expectedPlaceholder: ":PARAM_1" },
35
+ { integrationId: "dynamodb", expectedPlaceholder: "?" },
36
+ {
37
+ integrationId: "lakebase",
38
+ expectedPlaceholder: "$1",
39
+ rejectsPostgresStyle: false,
40
+ },
41
+ { integrationId: "mariadb", expectedPlaceholder: "?" },
42
+ { integrationId: "mssql", expectedPlaceholder: "@PARAM_1" },
43
+ { integrationId: "mysql", expectedPlaceholder: "?" },
44
+ { integrationId: "oracledb", expectedPlaceholder: ":1" },
45
+ {
46
+ integrationId: "redshift",
47
+ expectedPlaceholder: "$1",
48
+ rejectsPostgresStyle: false,
49
+ },
50
+ { integrationId: "snowflake", expectedPlaceholder: "?" },
51
+ {
52
+ integrationId: "snowflakepostgres",
53
+ expectedPlaceholder: "$1",
54
+ rejectsPostgresStyle: false,
55
+ },
56
+ ];
57
+ function readIntegrationFile(integrationId, fileName) {
58
+ return readFileSync(resolve(TEST_DIR, integrationId, fileName), "utf8");
59
+ }
60
+ describe("SQL integration documentation", () => {
61
+ it("documents every supported integration directory", () => {
62
+ expect(ACTUAL_DOC_DIRECTORIES).toEqual(EXPECTED_DOC_DIRECTORIES);
63
+ for (const integrationId of EXPECTED_DOC_DIRECTORIES) {
64
+ expect(existsSync(resolve(TEST_DIR, integrationId, "README.md"))).toBe(true);
65
+ expect(existsSync(resolve(TEST_DIR, integrationId, "types.ts"))).toBe(true);
66
+ }
67
+ });
68
+ it.each(PLACEHOLDER_DOC_CASES)("$integrationId README uses $expectedPlaceholder placeholders", ({ integrationId, expectedPlaceholder, rejectsPostgresStyle = true }) => {
69
+ const readme = readIntegrationFile(integrationId, "README.md");
70
+ expect(readme).toContain(expectedPlaceholder);
71
+ if (rejectsPostgresStyle) {
72
+ expect(readme).not.toMatch(/\$\d+/);
73
+ }
74
+ });
75
+ it.each(PLACEHOLDER_DOC_CASES)("$integrationId JSDoc uses $expectedPlaceholder placeholders", ({ integrationId, expectedPlaceholder, rejectsPostgresStyle = true }) => {
76
+ const typeDocs = readIntegrationFile(integrationId, "types.ts");
77
+ expect(typeDocs).toContain(expectedPlaceholder);
78
+ if (rejectsPostgresStyle) {
79
+ expect(typeDocs).not.toMatch(/\$\d+/);
80
+ }
81
+ });
82
+ });
83
+ //# sourceMappingURL=documentation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documentation.test.js","sourceRoot":"","sources":["../../src/integrations/documentation.test.ts"],"names":[],"mappings":"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"}
@@ -16,14 +16,14 @@ import type { TraceMetadata } from "../registry.js";
16
16
  *
17
17
  * // Parameterized query
18
18
  * const users = await lb.query(
19
- * 'SELECT * FROM users WHERE status = ?',
19
+ * 'SELECT * FROM users WHERE status = $1',
20
20
  * UserSchema,
21
21
  * ['active'],
22
22
  * );
23
23
  *
24
24
  * // Execute a mutation
25
25
  * const result = await lb.execute(
26
- * 'INSERT INTO users (name, email) VALUES (?, ?)',
26
+ * 'INSERT INTO users (name, email) VALUES ($1, $2)',
27
27
  * ['Alice', 'alice@example.com'],
28
28
  * );
29
29
  * ```
@@ -32,7 +32,7 @@ export interface LakebaseClient extends BaseIntegrationClient {
32
32
  /**
33
33
  * Execute a SQL query and return validated rows.
34
34
  *
35
- * @param sql - SQL query string with `?` placeholders for parameters
35
+ * @param sql - SQL query string with `$1`, `$2` placeholders for parameters
36
36
  * @param schema - Zod schema for validating each result row
37
37
  * @param params - Optional query parameters for server-side binding
38
38
  * @returns Array of validated rows
@@ -41,7 +41,7 @@ export interface LakebaseClient extends BaseIntegrationClient {
41
41
  /**
42
42
  * Execute a SQL statement (INSERT, UPDATE, DELETE).
43
43
  *
44
- * @param sql - SQL statement with `?` placeholders for parameters
44
+ * @param sql - SQL statement with `$1`, `$2` placeholders for parameters
45
45
  * @param params - Optional query parameters for server-side binding
46
46
  * @returns The affected row count
47
47
  */
@@ -22,7 +22,7 @@ import type { TraceMetadata } from "../registry.js";
22
22
  * });
23
23
  *
24
24
  * const users = await mariadb.query(
25
- * 'SELECT * FROM users WHERE status = $1',
25
+ * 'SELECT * FROM users WHERE status = ?',
26
26
  * UserSchema,
27
27
  * ['active']
28
28
  * );
@@ -32,7 +32,7 @@ export interface MariaDBClient extends BaseIntegrationClient {
32
32
  /**
33
33
  * Execute a SQL query and validate results against a Zod schema.
34
34
  *
35
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
35
+ * @param sql - SQL query string (use ? placeholders for parameters)
36
36
  * @param schema - Zod schema to validate the query results
37
37
  * @param params - Optional query parameters
38
38
  * @returns Promise resolving to validated query results
@@ -22,7 +22,7 @@ import type { TraceMetadata } from "../registry.js";
22
22
  * });
23
23
  *
24
24
  * const users = await mssql.query(
25
- * 'SELECT * FROM users WHERE status = $1',
25
+ * 'SELECT * FROM users WHERE status = @PARAM_1',
26
26
  * UserSchema,
27
27
  * ['active']
28
28
  * );
@@ -32,7 +32,7 @@ export interface MSSQLClient extends BaseIntegrationClient {
32
32
  /**
33
33
  * Execute a SQL query and validate results against a Zod schema.
34
34
  *
35
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
35
+ * @param sql - SQL query string (use @PARAM_1, @PARAM_2, etc. for parameters)
36
36
  * @param schema - Zod schema to validate the query results
37
37
  * @param params - Optional query parameters
38
38
  * @returns Promise resolving to validated query results
@@ -22,7 +22,7 @@ import type { TraceMetadata } from "../registry.js";
22
22
  * });
23
23
  *
24
24
  * const users = await mysql.query(
25
- * 'SELECT * FROM users WHERE status = $1',
25
+ * 'SELECT * FROM users WHERE status = ?',
26
26
  * UserSchema,
27
27
  * ['active']
28
28
  * );
@@ -32,7 +32,7 @@ export interface MySQLClient extends BaseIntegrationClient {
32
32
  /**
33
33
  * Execute a SQL query and validate results against a Zod schema.
34
34
  *
35
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
35
+ * @param sql - SQL query string (use ? placeholders for parameters)
36
36
  * @param schema - Zod schema to validate the query results
37
37
  * @param params - Optional query parameters
38
38
  * @returns Promise resolving to validated query results
@@ -49,7 +49,7 @@ export interface MySQLClient extends BaseIntegrationClient {
49
49
  * });
50
50
  *
51
51
  * const users = await mysql.query(
52
- * 'SELECT * FROM users WHERE status = $1',
52
+ * 'SELECT * FROM users WHERE status = ?',
53
53
  * UserSchema,
54
54
  * ['active']
55
55
  * );
@@ -66,7 +66,7 @@ export interface MySQLClient extends BaseIntegrationClient {
66
66
  * @example
67
67
  * ```typescript
68
68
  * const result = await mysql.execute(
69
- * 'UPDATE users SET last_login = NOW() WHERE id = $1',
69
+ * 'UPDATE users SET last_login = NOW() WHERE id = ?',
70
70
  * [userId]
71
71
  * );
72
72
  * console.log(`Updated ${result.rowCount} rows`);
@@ -22,7 +22,7 @@ import type { TraceMetadata } from "../registry.js";
22
22
  * });
23
23
  *
24
24
  * const users = await oracledb.query(
25
- * 'SELECT * FROM users WHERE status = $1',
25
+ * 'SELECT * FROM users WHERE status = :1',
26
26
  * UserSchema,
27
27
  * ['active']
28
28
  * );
@@ -32,7 +32,7 @@ export interface OracleDBClient extends BaseIntegrationClient {
32
32
  /**
33
33
  * Execute a SQL query and validate results against a Zod schema.
34
34
  *
35
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
35
+ * @param sql - SQL query string (use :1, :2, etc. for parameters)
36
36
  * @param schema - Zod schema to validate the query results
37
37
  * @param params - Optional query parameters
38
38
  * @returns Promise resolving to validated query results
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/sdk-api",
3
- "version": "2.0.104-next.0",
3
+ "version": "2.0.104-next.1",
4
4
  "description": "Superblocks SDK for TypeScript-based API definitions",
5
5
  "license": "Superblocks Community Software License",
6
6
  "files": [
@@ -41,7 +41,7 @@ export default api({
41
41
  const logs = await ctx.integrations.athena.query(
42
42
  `SELECT timestamp, level, message, request_id
43
43
  FROM logs
44
- WHERE level = $1
44
+ WHERE level = ?
45
45
  LIMIT 100`,
46
46
  LogSchema,
47
47
  [level],
@@ -65,8 +65,8 @@ const EventSchema = z.object({
65
65
  const events = await ctx.integrations.athena.query(
66
66
  `SELECT event_id, event_type, user_id, created_at
67
67
  FROM events
68
- WHERE date_partition = $1
69
- AND event_type = $2`,
68
+ WHERE date_partition = ?
69
+ AND event_type = ?`,
70
70
  EventSchema,
71
71
  ["2024-01-15", "purchase"],
72
72
  );
@@ -138,7 +138,7 @@ Always use partition filters when possible to reduce data scanned:
138
138
  ```typescript
139
139
  // GOOD - Uses partition filter
140
140
  const logs = await ctx.integrations.athena.query(
141
- "SELECT * FROM logs WHERE date_partition = $1",
141
+ "SELECT * FROM logs WHERE date_partition = ?",
142
142
  LogSchema,
143
143
  ["2024-01-15"],
144
144
  );
@@ -25,7 +25,7 @@ import type { TraceMetadata } from "../registry.js";
25
25
  * });
26
26
  *
27
27
  * const logs = await athena.query(
28
- * 'SELECT * FROM logs WHERE level = $1',
28
+ * 'SELECT * FROM logs WHERE level = ?',
29
29
  * LogSchema,
30
30
  * ['ERROR']
31
31
  * );
@@ -35,7 +35,7 @@ export interface AthenaClient extends BaseIntegrationClient {
35
35
  /**
36
36
  * Execute a SQL query and validate results against a Zod schema.
37
37
  *
38
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
38
+ * @param sql - SQL query string (use ? placeholders for parameters)
39
39
  * @param schema - Zod schema to validate the query results
40
40
  * @param params - Optional query parameters
41
41
  * @returns Promise resolving to validated query results
@@ -40,7 +40,7 @@ export default api({
40
40
  const events = await ctx.integrations.bigquery.query(
41
41
  `SELECT event_id, event_name, user_id, timestamp
42
42
  FROM \`project.dataset.events\`
43
- WHERE event_name = $1
43
+ WHERE event_name = ?
44
44
  LIMIT 100`,
45
45
  EventSchema,
46
46
  [event_name],
@@ -51,12 +51,16 @@ export default api({
51
51
  });
52
52
  ```
53
53
 
54
+ The SDK currently binds BigQuery parameters from the positional `params` array.
55
+ Even though native BigQuery also supports named parameters like `@event_name`,
56
+ use `?` placeholders in SDK queries and pass values in array order.
57
+
54
58
  ### Executing INSERT, UPDATE, DELETE Statements
55
59
 
56
60
  ```typescript
57
61
  // INSERT
58
62
  await ctx.integrations.bigquery.execute(
59
- "INSERT INTO `project.dataset.metrics` (name, value, timestamp) VALUES ($1, $2, CURRENT_TIMESTAMP())",
63
+ "INSERT INTO `project.dataset.metrics` (name, value, timestamp) VALUES (?, ?, CURRENT_TIMESTAMP())",
60
64
  ["cpu_usage", "75.5"],
61
65
  );
62
66
  ```
@@ -116,6 +120,27 @@ All methods accept an optional `metadata` parameter as the last argument for dia
116
120
 
117
121
  ## Common Pitfalls
118
122
 
123
+ ### SDK Parameters Are Positional
124
+
125
+ The SDK accepts an array of parameter values and binds them server-side in
126
+ order. When you need parameters, use `?` placeholders in the SQL text:
127
+
128
+ ```typescript
129
+ // WRONG - Native BigQuery named parameter syntax is not the SDK contract
130
+ await ctx.integrations.bigquery.query(
131
+ "SELECT * FROM `project.dataset.events` WHERE event_name = @event_name",
132
+ EventSchema,
133
+ ["signup"],
134
+ );
135
+
136
+ // CORRECT - Use positional params with ? placeholders
137
+ await ctx.integrations.bigquery.query(
138
+ "SELECT * FROM `project.dataset.events` WHERE event_name = ?",
139
+ EventSchema,
140
+ ["signup"],
141
+ );
142
+ ```
143
+
119
144
  ### Backtick Table References
120
145
 
121
146
  BigQuery uses backticks for fully-qualified table names:
@@ -165,7 +190,7 @@ BigQuery charges by data scanned. Use partitions and column selection:
165
190
  const query = `
166
191
  SELECT event_id, event_name
167
192
  FROM \`project.dataset.events\`
168
- WHERE _PARTITIONDATE = $1
193
+ WHERE _PARTITIONDATE = ?
169
194
  `;
170
195
  ```
171
196
 
@@ -25,7 +25,7 @@ import type { TraceMetadata } from "../registry.js";
25
25
  * });
26
26
  *
27
27
  * const events = await bigquery.query(
28
- * 'SELECT * FROM events WHERE date = $1',
28
+ * 'SELECT * FROM events WHERE date = ?',
29
29
  * EventSchema,
30
30
  * ['2024-01-01']
31
31
  * );
@@ -35,9 +35,9 @@ export interface BigQueryClient extends BaseIntegrationClient {
35
35
  /**
36
36
  * Execute a SQL query and validate results against a Zod schema.
37
37
  *
38
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
38
+ * @param sql - SQL query string (use ? placeholders for parameters)
39
39
  * @param schema - Zod schema to validate the query results
40
- * @param params - Optional query parameters
40
+ * @param params - Optional query parameters, bound positionally from the provided array
41
41
  * @returns Promise resolving to validated query results
42
42
  *
43
43
  * @throws {QueryValidationError} If results don't match schema
@@ -38,7 +38,7 @@ export default api({
38
38
  }),
39
39
  async run(ctx, { date }) {
40
40
  const data = await ctx.integrations.databricks.query(
41
- "SELECT id, value, timestamp FROM data WHERE date = $1",
41
+ "SELECT id, value, timestamp FROM data WHERE date = :PARAM_1",
42
42
  DataSchema,
43
43
  [date],
44
44
  );
@@ -48,25 +48,29 @@ export default api({
48
48
  });
49
49
  ```
50
50
 
51
+ The SDK accepts Databricks parameters as a positional `params` array, but the
52
+ SQL text should use `:PARAM_1`, `:PARAM_2`, and so on. The first array value
53
+ binds to `:PARAM_1`, the second binds to `:PARAM_2`, and so forth.
54
+
51
55
  ### Executing INSERT, UPDATE, DELETE Statements
52
56
 
53
57
  ```typescript
54
58
  // INSERT
55
59
  await ctx.integrations.databricks.execute(
56
- "INSERT INTO metrics (name, value) VALUES ($1, $2)",
60
+ "INSERT INTO metrics (name, value) VALUES (:PARAM_1, :PARAM_2)",
57
61
  ["cpu_usage", "75.5"],
58
62
  );
59
63
 
60
64
  // UPDATE
61
65
  const updateResult = await ctx.integrations.databricks.execute(
62
- "UPDATE metrics SET value = $1 WHERE name = $2",
66
+ "UPDATE metrics SET value = :PARAM_1 WHERE name = :PARAM_2",
63
67
  ["80.0", "cpu_usage"],
64
68
  );
65
69
  console.log(`Updated ${updateResult.rowCount} rows`);
66
70
 
67
71
  // DELETE
68
72
  const deleteResult = await ctx.integrations.databricks.execute(
69
- "DELETE FROM metrics WHERE timestamp < $1",
73
+ "DELETE FROM metrics WHERE timestamp < :PARAM_1",
70
74
  ["2024-01-01"],
71
75
  );
72
76
  console.log(`Deleted ${deleteResult.rowCount} rows`);
@@ -100,6 +104,27 @@ All methods accept an optional `metadata` parameter as the last argument for dia
100
104
 
101
105
  ## Common Pitfalls
102
106
 
107
+ ### Parameter Placeholders Must Match the SDK Contract
108
+
109
+ Pass parameter values as an array, but write the SQL with `:PARAM_n`
110
+ placeholders:
111
+
112
+ ```typescript
113
+ // WRONG - Positional question marks are not the placeholder syntax this SDK emits
114
+ await ctx.integrations.databricks.query(
115
+ "SELECT * FROM data WHERE date = ?",
116
+ DataSchema,
117
+ ["2024-01-01"],
118
+ );
119
+
120
+ // CORRECT - Use Databricks placeholders with positional params
121
+ await ctx.integrations.databricks.query(
122
+ "SELECT * FROM data WHERE date = :PARAM_1",
123
+ DataSchema,
124
+ ["2024-01-01"],
125
+ );
126
+ ```
127
+
103
128
  ### BIGINT/LONG Values Returned as Strings
104
129
 
105
130
  ```typescript
@@ -25,7 +25,7 @@ import type { TraceMetadata } from "../registry.js";
25
25
  * });
26
26
  *
27
27
  * const data = await databricks.query(
28
- * 'SELECT * FROM data WHERE date = $1',
28
+ * 'SELECT * FROM data WHERE date = :PARAM_1',
29
29
  * DataSchema,
30
30
  * ['2024-01-01']
31
31
  * );
@@ -35,9 +35,9 @@ export interface DatabricksClient extends BaseIntegrationClient {
35
35
  /**
36
36
  * Execute a SQL query and validate results against a Zod schema.
37
37
  *
38
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
38
+ * @param sql - SQL query string (use :PARAM_1, :PARAM_2, etc. for parameters)
39
39
  * @param schema - Zod schema to validate the query results
40
- * @param params - Optional query parameters
40
+ * @param params - Optional query parameters, bound positionally so the first value maps to :PARAM_1
41
41
  * @returns Promise resolving to validated query results
42
42
  *
43
43
  * @throws {QueryValidationError} If results don't match schema
@@ -0,0 +1,122 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { describe, expect, it } from "vitest";
6
+
7
+ import { SUPPORTED_PLUGINS } from "./registry.js";
8
+
9
+ const TEST_DIR = dirname(fileURLToPath(import.meta.url));
10
+
11
+ type PlaceholderDocsCase = {
12
+ integrationId: string;
13
+ expectedPlaceholder: string;
14
+ rejectsPostgresStyle?: boolean;
15
+ };
16
+
17
+ const DOC_DIRECTORY_ALIASES = {
18
+ confluent: "kafka",
19
+ graphqlintegration: "graphql",
20
+ redpanda: "kafka",
21
+ } as const;
22
+
23
+ const DOC_DIRECTORY_EXCLUSIONS = new Set(["base"]);
24
+
25
+ function hasDocDirectoryAlias(
26
+ pluginId: string,
27
+ ): pluginId is keyof typeof DOC_DIRECTORY_ALIASES {
28
+ return pluginId in DOC_DIRECTORY_ALIASES;
29
+ }
30
+
31
+ const EXPECTED_DOC_DIRECTORIES = Array.from(
32
+ new Set(
33
+ Object.values(SUPPORTED_PLUGINS)
34
+ .map((pluginId) =>
35
+ hasDocDirectoryAlias(pluginId)
36
+ ? DOC_DIRECTORY_ALIASES[pluginId]
37
+ : pluginId,
38
+ )
39
+ .filter((directory) => !DOC_DIRECTORY_EXCLUSIONS.has(directory)),
40
+ ),
41
+ ).sort();
42
+
43
+ const ACTUAL_DOC_DIRECTORIES = readdirSync(TEST_DIR, { withFileTypes: true })
44
+ .filter((entry) => entry.isDirectory())
45
+ .map((entry) => entry.name)
46
+ .filter((directory) => !DOC_DIRECTORY_EXCLUSIONS.has(directory))
47
+ .sort();
48
+
49
+ const PLACEHOLDER_DOC_CASES: PlaceholderDocsCase[] = [
50
+ { integrationId: "athena", expectedPlaceholder: "?" },
51
+ { integrationId: "bigquery", expectedPlaceholder: "?" },
52
+ {
53
+ integrationId: "cockroachdb",
54
+ expectedPlaceholder: "$1",
55
+ rejectsPostgresStyle: false,
56
+ },
57
+ { integrationId: "databricks", expectedPlaceholder: ":PARAM_1" },
58
+ { integrationId: "dynamodb", expectedPlaceholder: "?" },
59
+ {
60
+ integrationId: "lakebase",
61
+ expectedPlaceholder: "$1",
62
+ rejectsPostgresStyle: false,
63
+ },
64
+ { integrationId: "mariadb", expectedPlaceholder: "?" },
65
+ { integrationId: "mssql", expectedPlaceholder: "@PARAM_1" },
66
+ { integrationId: "mysql", expectedPlaceholder: "?" },
67
+ { integrationId: "oracledb", expectedPlaceholder: ":1" },
68
+ {
69
+ integrationId: "redshift",
70
+ expectedPlaceholder: "$1",
71
+ rejectsPostgresStyle: false,
72
+ },
73
+ { integrationId: "snowflake", expectedPlaceholder: "?" },
74
+ {
75
+ integrationId: "snowflakepostgres",
76
+ expectedPlaceholder: "$1",
77
+ rejectsPostgresStyle: false,
78
+ },
79
+ ];
80
+
81
+ function readIntegrationFile(integrationId: string, fileName: string): string {
82
+ return readFileSync(resolve(TEST_DIR, integrationId, fileName), "utf8");
83
+ }
84
+
85
+ describe("SQL integration documentation", () => {
86
+ it("documents every supported integration directory", () => {
87
+ expect(ACTUAL_DOC_DIRECTORIES).toEqual(EXPECTED_DOC_DIRECTORIES);
88
+
89
+ for (const integrationId of EXPECTED_DOC_DIRECTORIES) {
90
+ expect(existsSync(resolve(TEST_DIR, integrationId, "README.md"))).toBe(
91
+ true,
92
+ );
93
+ expect(existsSync(resolve(TEST_DIR, integrationId, "types.ts"))).toBe(
94
+ true,
95
+ );
96
+ }
97
+ });
98
+
99
+ it.each(PLACEHOLDER_DOC_CASES)(
100
+ "$integrationId README uses $expectedPlaceholder placeholders",
101
+ ({ integrationId, expectedPlaceholder, rejectsPostgresStyle = true }) => {
102
+ const readme = readIntegrationFile(integrationId, "README.md");
103
+
104
+ expect(readme).toContain(expectedPlaceholder);
105
+ if (rejectsPostgresStyle) {
106
+ expect(readme).not.toMatch(/\$\d+/);
107
+ }
108
+ },
109
+ );
110
+
111
+ it.each(PLACEHOLDER_DOC_CASES)(
112
+ "$integrationId JSDoc uses $expectedPlaceholder placeholders",
113
+ ({ integrationId, expectedPlaceholder, rejectsPostgresStyle = true }) => {
114
+ const typeDocs = readIntegrationFile(integrationId, "types.ts");
115
+
116
+ expect(typeDocs).toContain(expectedPlaceholder);
117
+ if (rejectsPostgresStyle) {
118
+ expect(typeDocs).not.toMatch(/\$\d+/);
119
+ }
120
+ },
121
+ );
122
+ });
@@ -19,14 +19,14 @@ import type { TraceMetadata } from "../registry.js";
19
19
  *
20
20
  * // Parameterized query
21
21
  * const users = await lb.query(
22
- * 'SELECT * FROM users WHERE status = ?',
22
+ * 'SELECT * FROM users WHERE status = $1',
23
23
  * UserSchema,
24
24
  * ['active'],
25
25
  * );
26
26
  *
27
27
  * // Execute a mutation
28
28
  * const result = await lb.execute(
29
- * 'INSERT INTO users (name, email) VALUES (?, ?)',
29
+ * 'INSERT INTO users (name, email) VALUES ($1, $2)',
30
30
  * ['Alice', 'alice@example.com'],
31
31
  * );
32
32
  * ```
@@ -35,7 +35,7 @@ export interface LakebaseClient extends BaseIntegrationClient {
35
35
  /**
36
36
  * Execute a SQL query and return validated rows.
37
37
  *
38
- * @param sql - SQL query string with `?` placeholders for parameters
38
+ * @param sql - SQL query string with `$1`, `$2` placeholders for parameters
39
39
  * @param schema - Zod schema for validating each result row
40
40
  * @param params - Optional query parameters for server-side binding
41
41
  * @returns Array of validated rows
@@ -50,7 +50,7 @@ export interface LakebaseClient extends BaseIntegrationClient {
50
50
  /**
51
51
  * Execute a SQL statement (INSERT, UPDATE, DELETE).
52
52
  *
53
- * @param sql - SQL statement with `?` placeholders for parameters
53
+ * @param sql - SQL statement with `$1`, `$2` placeholders for parameters
54
54
  * @param params - Optional query parameters for server-side binding
55
55
  * @returns The affected row count
56
56
  */
@@ -39,7 +39,7 @@ export default api({
39
39
  }),
40
40
  async run(ctx, { status }) {
41
41
  const users = await ctx.integrations.mariadb.query(
42
- "SELECT id, name, email, created_at FROM users WHERE status = $1",
42
+ "SELECT id, name, email, created_at FROM users WHERE status = ?",
43
43
  UserSchema,
44
44
  [status],
45
45
  );
@@ -54,13 +54,13 @@ export default api({
54
54
  ```typescript
55
55
  // INSERT
56
56
  await ctx.integrations.mariadb.execute(
57
- "INSERT INTO users (name, email) VALUES ($1, $2)",
57
+ "INSERT INTO users (name, email) VALUES (?, ?)",
58
58
  ["John Doe", "john@example.com"],
59
59
  );
60
60
 
61
61
  // UPDATE
62
62
  const updateResult = await ctx.integrations.mariadb.execute(
63
- "UPDATE users SET last_login = NOW() WHERE id = $1",
63
+ "UPDATE users SET last_login = NOW() WHERE id = ?",
64
64
  [userId],
65
65
  );
66
66
  console.log(`Updated ${updateResult.rowCount} rows`);
@@ -25,7 +25,7 @@ import type { TraceMetadata } from "../registry.js";
25
25
  * });
26
26
  *
27
27
  * const users = await mariadb.query(
28
- * 'SELECT * FROM users WHERE status = $1',
28
+ * 'SELECT * FROM users WHERE status = ?',
29
29
  * UserSchema,
30
30
  * ['active']
31
31
  * );
@@ -35,7 +35,7 @@ export interface MariaDBClient extends BaseIntegrationClient {
35
35
  /**
36
36
  * Execute a SQL query and validate results against a Zod schema.
37
37
  *
38
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
38
+ * @param sql - SQL query string (use ? placeholders for parameters)
39
39
  * @param schema - Zod schema to validate the query results
40
40
  * @param params - Optional query parameters
41
41
  * @returns Promise resolving to validated query results
@@ -39,7 +39,7 @@ export default api({
39
39
  }),
40
40
  async run(ctx, { status }) {
41
41
  const users = await ctx.integrations.mssql.query(
42
- "SELECT id, name, email, created_at FROM users WHERE status = $1",
42
+ "SELECT id, name, email, created_at FROM users WHERE status = @PARAM_1",
43
43
  UserSchema,
44
44
  [status],
45
45
  );
@@ -54,13 +54,13 @@ export default api({
54
54
  ```typescript
55
55
  // INSERT
56
56
  await ctx.integrations.mssql.execute(
57
- "INSERT INTO users (name, email) VALUES ($1, $2)",
57
+ "INSERT INTO users (name, email) VALUES (@PARAM_1, @PARAM_2)",
58
58
  ["John Doe", "john@example.com"],
59
59
  );
60
60
 
61
61
  // UPDATE
62
62
  const updateResult = await ctx.integrations.mssql.execute(
63
- "UPDATE users SET last_login = GETDATE() WHERE id = $1",
63
+ "UPDATE users SET last_login = GETDATE() WHERE id = @PARAM_1",
64
64
  [userId],
65
65
  );
66
66
  console.log(`Updated ${updateResult.rowCount} rows`);
@@ -25,7 +25,7 @@ import type { TraceMetadata } from "../registry.js";
25
25
  * });
26
26
  *
27
27
  * const users = await mssql.query(
28
- * 'SELECT * FROM users WHERE status = $1',
28
+ * 'SELECT * FROM users WHERE status = @PARAM_1',
29
29
  * UserSchema,
30
30
  * ['active']
31
31
  * );
@@ -35,7 +35,7 @@ export interface MSSQLClient extends BaseIntegrationClient {
35
35
  /**
36
36
  * Execute a SQL query and validate results against a Zod schema.
37
37
  *
38
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
38
+ * @param sql - SQL query string (use @PARAM_1, @PARAM_2, etc. for parameters)
39
39
  * @param schema - Zod schema to validate the query results
40
40
  * @param params - Optional query parameters
41
41
  * @returns Promise resolving to validated query results
@@ -39,7 +39,7 @@ export default api({
39
39
  }),
40
40
  async run(ctx, { status }) {
41
41
  const users = await ctx.integrations.mysql.query(
42
- "SELECT id, name, email, created_at FROM users WHERE status = $1",
42
+ "SELECT id, name, email, created_at FROM users WHERE status = ?",
43
43
  UserSchema,
44
44
  [status],
45
45
  );
@@ -54,13 +54,13 @@ export default api({
54
54
  ```typescript
55
55
  // INSERT
56
56
  await ctx.integrations.mysql.execute(
57
- "INSERT INTO users (name, email) VALUES ($1, $2)",
57
+ "INSERT INTO users (name, email) VALUES (?, ?)",
58
58
  ["John Doe", "john@example.com"],
59
59
  );
60
60
 
61
61
  // UPDATE
62
62
  const updateResult = await ctx.integrations.mysql.execute(
63
- "UPDATE users SET last_login = NOW() WHERE id = $1",
63
+ "UPDATE users SET last_login = NOW() WHERE id = ?",
64
64
  [userId],
65
65
  );
66
66
  console.log(`Updated ${updateResult.rowCount} rows`);
@@ -90,7 +90,7 @@ const orders = await ctx.integrations.mysql.query(
90
90
  c.email as customer_email
91
91
  FROM orders o
92
92
  JOIN customers c ON o.customer_id = c.id
93
- WHERE o.status = $1`,
93
+ WHERE o.status = ?`,
94
94
  OrderWithCustomerSchema,
95
95
  ["pending"],
96
96
  );
@@ -25,7 +25,7 @@ import type { TraceMetadata } from "../registry.js";
25
25
  * });
26
26
  *
27
27
  * const users = await mysql.query(
28
- * 'SELECT * FROM users WHERE status = $1',
28
+ * 'SELECT * FROM users WHERE status = ?',
29
29
  * UserSchema,
30
30
  * ['active']
31
31
  * );
@@ -35,7 +35,7 @@ export interface MySQLClient extends BaseIntegrationClient {
35
35
  /**
36
36
  * Execute a SQL query and validate results against a Zod schema.
37
37
  *
38
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
38
+ * @param sql - SQL query string (use ? placeholders for parameters)
39
39
  * @param schema - Zod schema to validate the query results
40
40
  * @param params - Optional query parameters
41
41
  * @returns Promise resolving to validated query results
@@ -52,7 +52,7 @@ export interface MySQLClient extends BaseIntegrationClient {
52
52
  * });
53
53
  *
54
54
  * const users = await mysql.query(
55
- * 'SELECT * FROM users WHERE status = $1',
55
+ * 'SELECT * FROM users WHERE status = ?',
56
56
  * UserSchema,
57
57
  * ['active']
58
58
  * );
@@ -75,7 +75,7 @@ export interface MySQLClient extends BaseIntegrationClient {
75
75
  * @example
76
76
  * ```typescript
77
77
  * const result = await mysql.execute(
78
- * 'UPDATE users SET last_login = NOW() WHERE id = $1',
78
+ * 'UPDATE users SET last_login = NOW() WHERE id = ?',
79
79
  * [userId]
80
80
  * );
81
81
  * console.log(`Updated ${result.rowCount} rows`);
@@ -39,7 +39,7 @@ export default api({
39
39
  }),
40
40
  async run(ctx, { status }) {
41
41
  const users = await ctx.integrations.oracledb.query(
42
- "SELECT ID, NAME, EMAIL, CREATED_AT FROM USERS WHERE STATUS = $1",
42
+ "SELECT ID, NAME, EMAIL, CREATED_AT FROM USERS WHERE STATUS = :1",
43
43
  UserSchema,
44
44
  [status],
45
45
  );
@@ -54,13 +54,13 @@ export default api({
54
54
  ```typescript
55
55
  // INSERT
56
56
  await ctx.integrations.oracledb.execute(
57
- "INSERT INTO USERS (NAME, EMAIL) VALUES ($1, $2)",
57
+ "INSERT INTO USERS (NAME, EMAIL) VALUES (:1, :2)",
58
58
  ["John Doe", "john@example.com"],
59
59
  );
60
60
 
61
61
  // UPDATE
62
62
  const updateResult = await ctx.integrations.oracledb.execute(
63
- "UPDATE USERS SET LAST_LOGIN = SYSDATE WHERE ID = $1",
63
+ "UPDATE USERS SET LAST_LOGIN = SYSDATE WHERE ID = :1",
64
64
  [userId],
65
65
  );
66
66
  console.log(`Updated ${updateResult.rowCount} rows`);
@@ -25,7 +25,7 @@ import type { TraceMetadata } from "../registry.js";
25
25
  * });
26
26
  *
27
27
  * const users = await oracledb.query(
28
- * 'SELECT * FROM users WHERE status = $1',
28
+ * 'SELECT * FROM users WHERE status = :1',
29
29
  * UserSchema,
30
30
  * ['active']
31
31
  * );
@@ -35,7 +35,7 @@ export interface OracleDBClient extends BaseIntegrationClient {
35
35
  /**
36
36
  * Execute a SQL query and validate results against a Zod schema.
37
37
  *
38
- * @param sql - SQL query string (use $1, $2, etc. for parameters)
38
+ * @param sql - SQL query string (use :1, :2, etc. for parameters)
39
39
  * @param schema - Zod schema to validate the query results
40
40
  * @param params - Optional query parameters
41
41
  * @returns Promise resolving to validated query results