@tinybirdco/sdk 0.0.1 → 0.0.3

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.
Files changed (78) hide show
  1. package/README.md +5 -4
  2. package/dist/api/build.d.ts +2 -0
  3. package/dist/api/build.d.ts.map +1 -1
  4. package/dist/api/build.js +13 -0
  5. package/dist/api/build.js.map +1 -1
  6. package/dist/api/build.test.js +1 -0
  7. package/dist/api/build.test.js.map +1 -1
  8. package/dist/api/deploy.d.ts.map +1 -1
  9. package/dist/api/deploy.js +3 -0
  10. package/dist/api/deploy.js.map +1 -1
  11. package/dist/api/deploy.test.js +1 -0
  12. package/dist/api/deploy.test.js.map +1 -1
  13. package/dist/cli/commands/init.d.ts.map +1 -1
  14. package/dist/cli/commands/init.js +26 -0
  15. package/dist/cli/commands/init.js.map +1 -1
  16. package/dist/cli/commands/init.test.js +43 -0
  17. package/dist/cli/commands/init.test.js.map +1 -1
  18. package/dist/cli/index.js +4 -0
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/generator/connection.d.ts +49 -0
  21. package/dist/generator/connection.d.ts.map +1 -0
  22. package/dist/generator/connection.js +78 -0
  23. package/dist/generator/connection.js.map +1 -0
  24. package/dist/generator/connection.test.d.ts +2 -0
  25. package/dist/generator/connection.test.d.ts.map +1 -0
  26. package/dist/generator/connection.test.js +106 -0
  27. package/dist/generator/connection.test.js.map +1 -0
  28. package/dist/generator/datasource.d.ts.map +1 -1
  29. package/dist/generator/datasource.js +20 -0
  30. package/dist/generator/datasource.js.map +1 -1
  31. package/dist/generator/datasource.test.js +92 -0
  32. package/dist/generator/datasource.test.js.map +1 -1
  33. package/dist/generator/index.d.ts +8 -2
  34. package/dist/generator/index.d.ts.map +1 -1
  35. package/dist/generator/index.js +10 -3
  36. package/dist/generator/index.js.map +1 -1
  37. package/dist/generator/loader.d.ts +8 -1
  38. package/dist/generator/loader.d.ts.map +1 -1
  39. package/dist/generator/loader.js +17 -2
  40. package/dist/generator/loader.js.map +1 -1
  41. package/dist/index.d.ts +4 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +2 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/schema/connection.d.ts +83 -0
  46. package/dist/schema/connection.d.ts.map +1 -0
  47. package/dist/schema/connection.js +61 -0
  48. package/dist/schema/connection.js.map +1 -0
  49. package/dist/schema/connection.test.d.ts +2 -0
  50. package/dist/schema/connection.test.d.ts.map +1 -0
  51. package/dist/schema/connection.test.js +117 -0
  52. package/dist/schema/connection.test.js.map +1 -0
  53. package/dist/schema/datasource.d.ts +16 -0
  54. package/dist/schema/datasource.d.ts.map +1 -1
  55. package/dist/schema/datasource.js.map +1 -1
  56. package/dist/schema/project.d.ts +12 -3
  57. package/dist/schema/project.d.ts.map +1 -1
  58. package/dist/schema/project.js +2 -0
  59. package/dist/schema/project.js.map +1 -1
  60. package/package.json +2 -1
  61. package/src/api/build.test.ts +1 -0
  62. package/src/api/build.ts +20 -0
  63. package/src/api/deploy.test.ts +1 -0
  64. package/src/api/deploy.ts +3 -0
  65. package/src/cli/commands/init.test.ts +72 -0
  66. package/src/cli/commands/init.ts +30 -0
  67. package/src/cli/index.ts +6 -0
  68. package/src/generator/connection.test.ts +135 -0
  69. package/src/generator/connection.ts +104 -0
  70. package/src/generator/datasource.test.ts +108 -0
  71. package/src/generator/datasource.ts +27 -1
  72. package/src/generator/index.ts +16 -4
  73. package/src/generator/loader.ts +21 -3
  74. package/src/index.ts +12 -0
  75. package/src/schema/connection.test.ts +149 -0
  76. package/src/schema/connection.ts +123 -0
  77. package/src/schema/datasource.ts +17 -0
  78. package/src/schema/project.ts +20 -5
@@ -6,7 +6,8 @@
6
6
  import { loadSchema, loadEntities, entitiesToProject, type LoadedEntities } from "./loader.js";
7
7
  import { generateAllDatasources, type GeneratedDatasource } from "./datasource.js";
8
8
  import { generateAllPipes, type GeneratedPipe } from "./pipe.js";
9
- import type { ProjectDefinition, DatasourcesDefinition, PipesDefinition } from "../schema/project.js";
9
+ import { generateAllConnections, type GeneratedConnection } from "./connection.js";
10
+ import type { ProjectDefinition, DatasourcesDefinition, PipesDefinition, ConnectionsDefinition } from "../schema/project.js";
10
11
 
11
12
  /**
12
13
  * Generated resources ready for API push
@@ -16,6 +17,8 @@ export interface GeneratedResources {
16
17
  datasources: GeneratedDatasource[];
17
18
  /** Generated pipe files */
18
19
  pipes: GeneratedPipe[];
20
+ /** Generated connection files */
21
+ connections: GeneratedConnection[];
19
22
  }
20
23
 
21
24
  /**
@@ -34,6 +37,7 @@ export interface BuildResult {
34
37
  stats: {
35
38
  datasourceCount: number;
36
39
  pipeCount: number;
40
+ connectionCount: number;
37
41
  };
38
42
  }
39
43
 
@@ -46,10 +50,12 @@ export interface BuildResult {
46
50
  export function generateResources(project: ProjectDefinition): GeneratedResources {
47
51
  const datasources = generateAllDatasources(project.datasources);
48
52
  const pipes = generateAllPipes(project.pipes);
53
+ const connections = generateAllConnections(project.connections);
49
54
 
50
55
  return {
51
56
  datasources,
52
57
  pipes,
58
+ connections,
53
59
  };
54
60
  }
55
61
 
@@ -107,6 +113,7 @@ export async function build(options: BuildOptions): Promise<BuildResult> {
107
113
  stats: {
108
114
  datasourceCount: resources.datasources.length,
109
115
  pipeCount: resources.pipes.length,
116
+ connectionCount: resources.connections.length,
110
117
  },
111
118
  };
112
119
  }
@@ -133,6 +140,7 @@ export interface BuildFromIncludeResult {
133
140
  stats: {
134
141
  datasourceCount: number;
135
142
  pipeCount: number;
143
+ connectionCount: number;
136
144
  };
137
145
  }
138
146
 
@@ -141,11 +149,13 @@ export interface BuildFromIncludeResult {
141
149
  */
142
150
  export function generateResourcesFromEntities(
143
151
  datasources: DatasourcesDefinition,
144
- pipes: PipesDefinition
152
+ pipes: PipesDefinition,
153
+ connections: ConnectionsDefinition = {}
145
154
  ): GeneratedResources {
146
155
  return {
147
156
  datasources: generateAllDatasources(datasources),
148
157
  pipes: generateAllPipes(pipes),
158
+ connections: generateAllConnections(connections),
149
159
  };
150
160
  }
151
161
 
@@ -181,10 +191,10 @@ export async function buildFromInclude(
181
191
  });
182
192
 
183
193
  // Convert to format for generators
184
- const { datasources, pipes } = entitiesToProject(entities);
194
+ const { datasources, pipes, connections } = entitiesToProject(entities);
185
195
 
186
196
  // Generate resources
187
- const resources = generateResourcesFromEntities(datasources, pipes);
197
+ const resources = generateResourcesFromEntities(datasources, pipes, connections);
188
198
 
189
199
  return {
190
200
  resources,
@@ -192,6 +202,7 @@ export async function buildFromInclude(
192
202
  stats: {
193
203
  datasourceCount: resources.datasources.length,
194
204
  pipeCount: resources.pipes.length,
205
+ connectionCount: resources.connections.length,
195
206
  },
196
207
  };
197
208
  }
@@ -200,4 +211,5 @@ export async function buildFromInclude(
200
211
  export { loadSchema, loadEntities, entitiesToProject, type LoaderOptions, type LoadedSchema, type LoadedEntities, type LoadEntitiesOptions } from "./loader.js";
201
212
  export { generateDatasource, generateAllDatasources, type GeneratedDatasource } from "./datasource.js";
202
213
  export { generatePipe, generateAllPipes, type GeneratedPipe } from "./pipe.js";
214
+ export { generateConnection, generateAllConnections, type GeneratedConnection } from "./connection.js";
203
215
  export { generateClientFile, type GenerateClientOptions, type GeneratedClient } from "./client.js";
@@ -7,9 +7,10 @@ import * as esbuild from "esbuild";
7
7
  import * as path from "path";
8
8
  import * as fs from "fs";
9
9
  import { watch as chokidarWatch, type FSWatcher } from "chokidar";
10
- import { isProjectDefinition, type ProjectDefinition, type DatasourcesDefinition, type PipesDefinition } from "../schema/project.js";
10
+ import { isProjectDefinition, type ProjectDefinition, type DatasourcesDefinition, type PipesDefinition, type ConnectionsDefinition } from "../schema/project.js";
11
11
  import { isDatasourceDefinition, type DatasourceDefinition } from "../schema/datasource.js";
12
12
  import { isPipeDefinition, type PipeDefinition } from "../schema/pipe.js";
13
+ import { isConnectionDefinition, type ConnectionDefinition } from "../schema/connection.js";
13
14
 
14
15
  /**
15
16
  * Result of loading a schema file
@@ -157,6 +158,8 @@ export interface LoadedEntities {
157
158
  datasources: Record<string, { definition: DatasourceDefinition; info: EntityInfo }>;
158
159
  /** Discovered pipes with their metadata */
159
160
  pipes: Record<string, { definition: PipeDefinition; info: EntityInfo }>;
161
+ /** Discovered connections with their metadata */
162
+ connections: Record<string, { definition: ConnectionDefinition; info: EntityInfo }>;
160
163
  /** All source files that were scanned */
161
164
  sourceFiles: string[];
162
165
  }
@@ -195,6 +198,7 @@ export async function loadEntities(options: LoadEntitiesOptions): Promise<Loaded
195
198
  const result: LoadedEntities = {
196
199
  datasources: {},
197
200
  pipes: {},
201
+ connections: {},
198
202
  sourceFiles: [],
199
203
  };
200
204
 
@@ -237,7 +241,7 @@ export async function loadEntities(options: LoadEntitiesOptions): Promise<Loaded
237
241
  const moduleUrl = `file://${outfile}`;
238
242
  const module = await import(moduleUrl);
239
243
 
240
- // Scan all exports for datasources and pipes
244
+ // Scan all exports for datasources, pipes, and connections
241
245
  for (const [exportName, value] of Object.entries(module)) {
242
246
  if (isDatasourceDefinition(value)) {
243
247
  result.datasources[exportName] = {
@@ -255,6 +259,14 @@ export async function loadEntities(options: LoadEntitiesOptions): Promise<Loaded
255
259
  sourceFile: includePath,
256
260
  },
257
261
  };
262
+ } else if (isConnectionDefinition(value)) {
263
+ result.connections[exportName] = {
264
+ definition: value,
265
+ info: {
266
+ exportName,
267
+ sourceFile: includePath,
268
+ },
269
+ };
258
270
  }
259
271
  }
260
272
  } finally {
@@ -282,9 +294,11 @@ export async function loadEntities(options: LoadEntitiesOptions): Promise<Loaded
282
294
  export function entitiesToProject(entities: LoadedEntities): {
283
295
  datasources: DatasourcesDefinition;
284
296
  pipes: PipesDefinition;
297
+ connections: ConnectionsDefinition;
285
298
  } {
286
299
  const datasources: DatasourcesDefinition = {};
287
300
  const pipes: PipesDefinition = {};
301
+ const connections: ConnectionsDefinition = {};
288
302
 
289
303
  for (const [name, { definition }] of Object.entries(entities.datasources)) {
290
304
  datasources[name] = definition;
@@ -294,7 +308,11 @@ export function entitiesToProject(entities: LoadedEntities): {
294
308
  pipes[name] = definition;
295
309
  }
296
310
 
297
- return { datasources, pipes };
311
+ for (const [name, { definition }] of Object.entries(entities.connections)) {
312
+ connections[name] = definition;
313
+ }
314
+
315
+ return { datasources, pipes, connections };
298
316
  }
299
317
 
300
318
  /**
package/src/index.ts CHANGED
@@ -106,8 +106,19 @@ export type {
106
106
  ColumnDefinition,
107
107
  TokenConfig,
108
108
  ExtractSchema,
109
+ KafkaConfig,
109
110
  } from "./schema/datasource.js";
110
111
 
112
+ // ============ Connection ============
113
+ export { createKafkaConnection, isConnectionDefinition, isKafkaConnectionDefinition, getConnectionType } from "./schema/connection.js";
114
+ export type {
115
+ ConnectionDefinition,
116
+ KafkaConnectionDefinition,
117
+ KafkaConnectionOptions,
118
+ KafkaSecurityProtocol,
119
+ KafkaSaslMechanism,
120
+ } from "./schema/connection.js";
121
+
111
122
  // ============ Pipe ============
112
123
  export {
113
124
  definePipe,
@@ -153,6 +164,7 @@ export type {
153
164
  TinybirdClientConfig,
154
165
  DatasourcesDefinition,
155
166
  PipesDefinition,
167
+ ConnectionsDefinition,
156
168
  ExtractDatasources,
157
169
  ExtractPipes,
158
170
  DataModel,
@@ -0,0 +1,149 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ createKafkaConnection,
4
+ isConnectionDefinition,
5
+ isKafkaConnectionDefinition,
6
+ getConnectionType,
7
+ } from "./connection.js";
8
+
9
+ describe("Connection Schema", () => {
10
+ describe("createKafkaConnection", () => {
11
+ it("creates a Kafka connection with required fields", () => {
12
+ const conn = createKafkaConnection("my_kafka", {
13
+ bootstrapServers: "kafka.example.com:9092",
14
+ });
15
+
16
+ expect(conn._name).toBe("my_kafka");
17
+ expect(conn._type).toBe("connection");
18
+ expect(conn._connectionType).toBe("kafka");
19
+ expect(conn.options.bootstrapServers).toBe("kafka.example.com:9092");
20
+ });
21
+
22
+ it("creates a Kafka connection with all options", () => {
23
+ const conn = createKafkaConnection("my_kafka", {
24
+ bootstrapServers: "kafka.example.com:9092",
25
+ securityProtocol: "SASL_SSL",
26
+ saslMechanism: "PLAIN",
27
+ key: '{{ tb_secret("KAFKA_KEY") }}',
28
+ secret: '{{ tb_secret("KAFKA_SECRET") }}',
29
+ sslCaPem: '{{ tb_secret("KAFKA_CA_CERT") }}',
30
+ });
31
+
32
+ expect(conn.options.securityProtocol).toBe("SASL_SSL");
33
+ expect(conn.options.saslMechanism).toBe("PLAIN");
34
+ expect(conn.options.key).toBe('{{ tb_secret("KAFKA_KEY") }}');
35
+ expect(conn.options.secret).toBe('{{ tb_secret("KAFKA_SECRET") }}');
36
+ expect(conn.options.sslCaPem).toBe('{{ tb_secret("KAFKA_CA_CERT") }}');
37
+ });
38
+
39
+ it("supports different SASL mechanisms", () => {
40
+ const scramConn = createKafkaConnection("scram_kafka", {
41
+ bootstrapServers: "kafka.example.com:9092",
42
+ saslMechanism: "SCRAM-SHA-256",
43
+ });
44
+ expect(scramConn.options.saslMechanism).toBe("SCRAM-SHA-256");
45
+
46
+ const scram512Conn = createKafkaConnection("scram512_kafka", {
47
+ bootstrapServers: "kafka.example.com:9092",
48
+ saslMechanism: "SCRAM-SHA-512",
49
+ });
50
+ expect(scram512Conn.options.saslMechanism).toBe("SCRAM-SHA-512");
51
+
52
+ const oauthConn = createKafkaConnection("oauth_kafka", {
53
+ bootstrapServers: "kafka.example.com:9092",
54
+ saslMechanism: "OAUTHBEARER",
55
+ });
56
+ expect(oauthConn.options.saslMechanism).toBe("OAUTHBEARER");
57
+ });
58
+
59
+ it("supports different security protocols", () => {
60
+ const plaintext = createKafkaConnection("plaintext_kafka", {
61
+ bootstrapServers: "localhost:9092",
62
+ securityProtocol: "PLAINTEXT",
63
+ });
64
+ expect(plaintext.options.securityProtocol).toBe("PLAINTEXT");
65
+
66
+ const saslPlaintext = createKafkaConnection("sasl_plaintext_kafka", {
67
+ bootstrapServers: "localhost:9092",
68
+ securityProtocol: "SASL_PLAINTEXT",
69
+ });
70
+ expect(saslPlaintext.options.securityProtocol).toBe("SASL_PLAINTEXT");
71
+ });
72
+
73
+ it("throws error for invalid connection name", () => {
74
+ expect(() =>
75
+ createKafkaConnection("123invalid", {
76
+ bootstrapServers: "kafka.example.com:9092",
77
+ })
78
+ ).toThrow("Invalid connection name");
79
+
80
+ expect(() =>
81
+ createKafkaConnection("my-connection", {
82
+ bootstrapServers: "kafka.example.com:9092",
83
+ })
84
+ ).toThrow("Invalid connection name");
85
+
86
+ expect(() =>
87
+ createKafkaConnection("", {
88
+ bootstrapServers: "kafka.example.com:9092",
89
+ })
90
+ ).toThrow("Invalid connection name");
91
+ });
92
+
93
+ it("allows valid naming patterns", () => {
94
+ const conn1 = createKafkaConnection("_private_kafka", {
95
+ bootstrapServers: "kafka.example.com:9092",
96
+ });
97
+ expect(conn1._name).toBe("_private_kafka");
98
+
99
+ const conn2 = createKafkaConnection("kafka_v2", {
100
+ bootstrapServers: "kafka.example.com:9092",
101
+ });
102
+ expect(conn2._name).toBe("kafka_v2");
103
+ });
104
+ });
105
+
106
+ describe("isConnectionDefinition", () => {
107
+ it("returns true for valid connection", () => {
108
+ const conn = createKafkaConnection("my_kafka", {
109
+ bootstrapServers: "kafka.example.com:9092",
110
+ });
111
+
112
+ expect(isConnectionDefinition(conn)).toBe(true);
113
+ });
114
+
115
+ it("returns false for non-connection objects", () => {
116
+ expect(isConnectionDefinition({})).toBe(false);
117
+ expect(isConnectionDefinition(null)).toBe(false);
118
+ expect(isConnectionDefinition(undefined)).toBe(false);
119
+ expect(isConnectionDefinition("string")).toBe(false);
120
+ expect(isConnectionDefinition(123)).toBe(false);
121
+ expect(isConnectionDefinition({ _name: "test" })).toBe(false);
122
+ });
123
+ });
124
+
125
+ describe("isKafkaConnectionDefinition", () => {
126
+ it("returns true for Kafka connection", () => {
127
+ const conn = createKafkaConnection("my_kafka", {
128
+ bootstrapServers: "kafka.example.com:9092",
129
+ });
130
+
131
+ expect(isKafkaConnectionDefinition(conn)).toBe(true);
132
+ });
133
+
134
+ it("returns false for non-Kafka objects", () => {
135
+ expect(isKafkaConnectionDefinition({})).toBe(false);
136
+ expect(isKafkaConnectionDefinition(null)).toBe(false);
137
+ });
138
+ });
139
+
140
+ describe("getConnectionType", () => {
141
+ it("returns the connection type", () => {
142
+ const conn = createKafkaConnection("my_kafka", {
143
+ bootstrapServers: "kafka.example.com:9092",
144
+ });
145
+
146
+ expect(getConnectionType(conn)).toBe("kafka");
147
+ });
148
+ });
149
+ });
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Connection definition for Tinybird
3
+ * Define external connections (Kafka, etc.) as TypeScript with full type safety
4
+ */
5
+
6
+ // Symbol for brand typing
7
+ const CONNECTION_BRAND = Symbol("tinybird.connection");
8
+
9
+ /**
10
+ * Kafka security protocol options
11
+ */
12
+ export type KafkaSecurityProtocol = "SASL_SSL" | "PLAINTEXT" | "SASL_PLAINTEXT";
13
+
14
+ /**
15
+ * Kafka SASL mechanism options
16
+ */
17
+ export type KafkaSaslMechanism = "PLAIN" | "SCRAM-SHA-256" | "SCRAM-SHA-512" | "OAUTHBEARER";
18
+
19
+ /**
20
+ * Options for creating a Kafka connection
21
+ */
22
+ export interface KafkaConnectionOptions {
23
+ /** Kafka bootstrap servers (host:port) */
24
+ bootstrapServers: string;
25
+ /** Security protocol (default: 'SASL_SSL') */
26
+ securityProtocol?: KafkaSecurityProtocol;
27
+ /** SASL mechanism for authentication */
28
+ saslMechanism?: KafkaSaslMechanism;
29
+ /** Kafka key/username - can use {{ tb_secret(...) }} */
30
+ key?: string;
31
+ /** Kafka secret/password - can use {{ tb_secret(...) }} */
32
+ secret?: string;
33
+ /** SSL CA certificate PEM - for private CA certs */
34
+ sslCaPem?: string;
35
+ }
36
+
37
+ /**
38
+ * Kafka-specific connection definition
39
+ */
40
+ export interface KafkaConnectionDefinition {
41
+ readonly [CONNECTION_BRAND]: true;
42
+ /** Connection name */
43
+ readonly _name: string;
44
+ /** Type marker for inference */
45
+ readonly _type: "connection";
46
+ /** Connection type */
47
+ readonly _connectionType: "kafka";
48
+ /** Kafka options */
49
+ readonly options: KafkaConnectionOptions;
50
+ }
51
+
52
+ /**
53
+ * A connection definition - union of all connection types
54
+ */
55
+ export type ConnectionDefinition = KafkaConnectionDefinition;
56
+
57
+ /**
58
+ * Create a Kafka connection
59
+ *
60
+ * @param name - The connection name (must be valid identifier)
61
+ * @param options - Kafka connection configuration
62
+ * @returns A connection definition that can be used in a project
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * import { createKafkaConnection } from '@tinybirdco/sdk';
67
+ *
68
+ * export const myKafka = createKafkaConnection('my_kafka', {
69
+ * bootstrapServers: 'kafka.example.com:9092',
70
+ * securityProtocol: 'SASL_SSL',
71
+ * saslMechanism: 'PLAIN',
72
+ * key: '{{ tb_secret("KAFKA_KEY") }}',
73
+ * secret: '{{ tb_secret("KAFKA_SECRET") }}',
74
+ * });
75
+ * ```
76
+ */
77
+ export function createKafkaConnection(
78
+ name: string,
79
+ options: KafkaConnectionOptions
80
+ ): KafkaConnectionDefinition {
81
+ // Validate name is a valid identifier
82
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
83
+ throw new Error(
84
+ `Invalid connection name: "${name}". Must start with a letter or underscore and contain only alphanumeric characters and underscores.`
85
+ );
86
+ }
87
+
88
+ return {
89
+ [CONNECTION_BRAND]: true,
90
+ _name: name,
91
+ _type: "connection",
92
+ _connectionType: "kafka",
93
+ options,
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Check if a value is a connection definition
99
+ */
100
+ export function isConnectionDefinition(value: unknown): value is ConnectionDefinition {
101
+ return (
102
+ typeof value === "object" &&
103
+ value !== null &&
104
+ CONNECTION_BRAND in value &&
105
+ (value as Record<symbol, unknown>)[CONNECTION_BRAND] === true
106
+ );
107
+ }
108
+
109
+ /**
110
+ * Check if a value is a Kafka connection definition
111
+ */
112
+ export function isKafkaConnectionDefinition(value: unknown): value is KafkaConnectionDefinition {
113
+ return isConnectionDefinition(value) && value._connectionType === "kafka";
114
+ }
115
+
116
+ /**
117
+ * Get the connection type from a connection definition
118
+ */
119
+ export function getConnectionType<T extends ConnectionDefinition>(
120
+ connection: T
121
+ ): T["_connectionType"] {
122
+ return connection._connectionType;
123
+ }
@@ -5,6 +5,7 @@
5
5
 
6
6
  import type { AnyTypeValidator } from "./types.js";
7
7
  import type { EngineConfig } from "./engines.js";
8
+ import type { KafkaConnectionDefinition } from "./connection.js";
8
9
 
9
10
  // Symbol for brand typing
10
11
  const DATASOURCE_BRAND = Symbol("tinybird.datasource");
@@ -35,6 +36,20 @@ export interface TokenConfig {
35
36
  permissions: readonly ("READ" | "APPEND")[];
36
37
  }
37
38
 
39
+ /**
40
+ * Kafka ingestion configuration for a datasource
41
+ */
42
+ export interface KafkaConfig {
43
+ /** Kafka connection to use */
44
+ connection: KafkaConnectionDefinition;
45
+ /** Kafka topic to consume from */
46
+ topic: string;
47
+ /** Consumer group ID (optional) */
48
+ groupId?: string;
49
+ /** Where to start reading: 'earliest' or 'latest' (default: 'latest') */
50
+ autoOffsetReset?: "earliest" | "latest";
51
+ }
52
+
38
53
  /**
39
54
  * Options for defining a datasource
40
55
  */
@@ -55,6 +70,8 @@ export interface DatasourceOptions<TSchema extends SchemaDefinition> {
55
70
  * Defaults to true.
56
71
  */
57
72
  jsonPaths?: boolean;
73
+ /** Kafka ingestion configuration */
74
+ kafka?: KafkaConfig;
58
75
  }
59
76
 
60
77
  /**
@@ -5,6 +5,7 @@
5
5
 
6
6
  import type { DatasourceDefinition, SchemaDefinition } from "./datasource.js";
7
7
  import type { PipeDefinition, ParamsDefinition, OutputDefinition } from "./pipe.js";
8
+ import type { ConnectionDefinition } from "./connection.js";
8
9
  import { getEndpointConfig } from "./pipe.js";
9
10
  import type { TinybirdClient } from "../client/base.js";
10
11
  import type { QueryResult } from "../client/types.js";
@@ -23,6 +24,11 @@ export type DatasourcesDefinition = Record<string, DatasourceDefinition<SchemaDe
23
24
  */
24
25
  export type PipesDefinition = Record<string, PipeDefinition<ParamsDefinition, OutputDefinition>>;
25
26
 
27
+ /**
28
+ * Collection of connection definitions
29
+ */
30
+ export type ConnectionsDefinition = Record<string, ConnectionDefinition>;
31
+
26
32
  /**
27
33
  * Type for a single query method
28
34
  */
@@ -101,12 +107,15 @@ export interface TinybirdClientConfig<
101
107
  */
102
108
  export interface ProjectConfig<
103
109
  TDatasources extends DatasourcesDefinition = DatasourcesDefinition,
104
- TPipes extends PipesDefinition = PipesDefinition
110
+ TPipes extends PipesDefinition = PipesDefinition,
111
+ TConnections extends ConnectionsDefinition = ConnectionsDefinition
105
112
  > {
106
113
  /** All datasources in this project */
107
114
  datasources?: TDatasources;
108
115
  /** All pipes in this project */
109
116
  pipes?: TPipes;
117
+ /** All connections in this project */
118
+ connections?: TConnections;
110
119
  }
111
120
 
112
121
  /**
@@ -114,7 +123,8 @@ export interface ProjectConfig<
114
123
  */
115
124
  export interface ProjectDefinition<
116
125
  TDatasources extends DatasourcesDefinition = DatasourcesDefinition,
117
- TPipes extends PipesDefinition = PipesDefinition
126
+ TPipes extends PipesDefinition = PipesDefinition,
127
+ TConnections extends ConnectionsDefinition = ConnectionsDefinition
118
128
  > {
119
129
  readonly [PROJECT_BRAND]: true;
120
130
  /** Type marker for inference */
@@ -123,6 +133,8 @@ export interface ProjectDefinition<
123
133
  readonly datasources: TDatasources;
124
134
  /** All pipes */
125
135
  readonly pipes: TPipes;
136
+ /** All connections */
137
+ readonly connections: TConnections;
126
138
  /** Typed Tinybird client */
127
139
  readonly tinybird: ProjectClient<TDatasources, TPipes>;
128
140
  }
@@ -157,12 +169,14 @@ export interface ProjectDefinition<
157
169
  */
158
170
  export function defineProject<
159
171
  TDatasources extends DatasourcesDefinition,
160
- TPipes extends PipesDefinition
172
+ TPipes extends PipesDefinition,
173
+ TConnections extends ConnectionsDefinition
161
174
  >(
162
- config: ProjectConfig<TDatasources, TPipes>
163
- ): ProjectDefinition<TDatasources, TPipes> {
175
+ config: ProjectConfig<TDatasources, TPipes, TConnections>
176
+ ): ProjectDefinition<TDatasources, TPipes, TConnections> {
164
177
  const datasources = (config.datasources ?? {}) as TDatasources;
165
178
  const pipes = (config.pipes ?? {}) as TPipes;
179
+ const connections = (config.connections ?? {}) as TConnections;
166
180
 
167
181
  // Use the shared client builder
168
182
  const tinybird = buildProjectClient(datasources, pipes);
@@ -172,6 +186,7 @@ export function defineProject<
172
186
  _type: "project",
173
187
  datasources,
174
188
  pipes,
189
+ connections,
175
190
  tinybird,
176
191
  };
177
192
  }