@tinybirdco/sdk 0.0.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.
Files changed (258) hide show
  1. package/README.md +518 -0
  2. package/bin/tinybird.js +7 -0
  3. package/dist/api/branches.d.ts +98 -0
  4. package/dist/api/branches.d.ts.map +1 -0
  5. package/dist/api/branches.js +203 -0
  6. package/dist/api/branches.js.map +1 -0
  7. package/dist/api/branches.test.d.ts +2 -0
  8. package/dist/api/branches.test.d.ts.map +1 -0
  9. package/dist/api/branches.test.js +286 -0
  10. package/dist/api/branches.test.js.map +1 -0
  11. package/dist/api/build.d.ts +130 -0
  12. package/dist/api/build.d.ts.map +1 -0
  13. package/dist/api/build.js +143 -0
  14. package/dist/api/build.js.map +1 -0
  15. package/dist/api/build.test.d.ts +2 -0
  16. package/dist/api/build.test.d.ts.map +1 -0
  17. package/dist/api/build.test.js +138 -0
  18. package/dist/api/build.test.js.map +1 -0
  19. package/dist/api/deploy.d.ts +39 -0
  20. package/dist/api/deploy.d.ts.map +1 -0
  21. package/dist/api/deploy.js +135 -0
  22. package/dist/api/deploy.js.map +1 -0
  23. package/dist/api/deploy.test.d.ts +2 -0
  24. package/dist/api/deploy.test.d.ts.map +1 -0
  25. package/dist/api/deploy.test.js +118 -0
  26. package/dist/api/deploy.test.js.map +1 -0
  27. package/dist/api/workspaces.d.ts +46 -0
  28. package/dist/api/workspaces.d.ts.map +1 -0
  29. package/dist/api/workspaces.js +39 -0
  30. package/dist/api/workspaces.js.map +1 -0
  31. package/dist/api/workspaces.test.d.ts +2 -0
  32. package/dist/api/workspaces.test.d.ts.map +1 -0
  33. package/dist/api/workspaces.test.js +65 -0
  34. package/dist/api/workspaces.test.js.map +1 -0
  35. package/dist/cli/auth.d.ts +86 -0
  36. package/dist/cli/auth.d.ts.map +1 -0
  37. package/dist/cli/auth.js +284 -0
  38. package/dist/cli/auth.js.map +1 -0
  39. package/dist/cli/branch-store.d.ts +53 -0
  40. package/dist/cli/branch-store.d.ts.map +1 -0
  41. package/dist/cli/branch-store.js +91 -0
  42. package/dist/cli/branch-store.js.map +1 -0
  43. package/dist/cli/branch-store.test.d.ts +2 -0
  44. package/dist/cli/branch-store.test.d.ts.map +1 -0
  45. package/dist/cli/branch-store.test.js +115 -0
  46. package/dist/cli/branch-store.test.js.map +1 -0
  47. package/dist/cli/commands/branch.d.ts +82 -0
  48. package/dist/cli/commands/branch.d.ts.map +1 -0
  49. package/dist/cli/commands/branch.js +215 -0
  50. package/dist/cli/commands/branch.js.map +1 -0
  51. package/dist/cli/commands/build.d.ts +43 -0
  52. package/dist/cli/commands/build.d.ts.map +1 -0
  53. package/dist/cli/commands/build.js +138 -0
  54. package/dist/cli/commands/build.js.map +1 -0
  55. package/dist/cli/commands/dev.d.ts +78 -0
  56. package/dist/cli/commands/dev.d.ts.map +1 -0
  57. package/dist/cli/commands/dev.js +226 -0
  58. package/dist/cli/commands/dev.js.map +1 -0
  59. package/dist/cli/commands/init.d.ts +45 -0
  60. package/dist/cli/commands/init.d.ts.map +1 -0
  61. package/dist/cli/commands/init.js +277 -0
  62. package/dist/cli/commands/init.js.map +1 -0
  63. package/dist/cli/commands/init.test.d.ts +2 -0
  64. package/dist/cli/commands/init.test.d.ts.map +1 -0
  65. package/dist/cli/commands/init.test.js +158 -0
  66. package/dist/cli/commands/init.test.js.map +1 -0
  67. package/dist/cli/commands/login.d.ts +37 -0
  68. package/dist/cli/commands/login.d.ts.map +1 -0
  69. package/dist/cli/commands/login.js +64 -0
  70. package/dist/cli/commands/login.js.map +1 -0
  71. package/dist/cli/config.d.ts +114 -0
  72. package/dist/cli/config.d.ts.map +1 -0
  73. package/dist/cli/config.js +258 -0
  74. package/dist/cli/config.js.map +1 -0
  75. package/dist/cli/config.test.d.ts +2 -0
  76. package/dist/cli/config.test.d.ts.map +1 -0
  77. package/dist/cli/config.test.js +243 -0
  78. package/dist/cli/config.test.js.map +1 -0
  79. package/dist/cli/env.d.ts +29 -0
  80. package/dist/cli/env.d.ts.map +1 -0
  81. package/dist/cli/env.js +66 -0
  82. package/dist/cli/env.js.map +1 -0
  83. package/dist/cli/git.d.ts +29 -0
  84. package/dist/cli/git.d.ts.map +1 -0
  85. package/dist/cli/git.js +114 -0
  86. package/dist/cli/git.js.map +1 -0
  87. package/dist/cli/git.test.d.ts +2 -0
  88. package/dist/cli/git.test.d.ts.map +1 -0
  89. package/dist/cli/git.test.js +125 -0
  90. package/dist/cli/git.test.js.map +1 -0
  91. package/dist/cli/index.d.ts +7 -0
  92. package/dist/cli/index.d.ts.map +1 -0
  93. package/dist/cli/index.js +337 -0
  94. package/dist/cli/index.js.map +1 -0
  95. package/dist/cli/utils/schema-validation.d.ts +95 -0
  96. package/dist/cli/utils/schema-validation.d.ts.map +1 -0
  97. package/dist/cli/utils/schema-validation.js +175 -0
  98. package/dist/cli/utils/schema-validation.js.map +1 -0
  99. package/dist/cli/utils/schema-validation.test.d.ts +5 -0
  100. package/dist/cli/utils/schema-validation.test.d.ts.map +1 -0
  101. package/dist/cli/utils/schema-validation.test.js +173 -0
  102. package/dist/cli/utils/schema-validation.test.js.map +1 -0
  103. package/dist/client/base.d.ts +116 -0
  104. package/dist/client/base.d.ts.map +1 -0
  105. package/dist/client/base.js +328 -0
  106. package/dist/client/base.js.map +1 -0
  107. package/dist/client/types.d.ts +137 -0
  108. package/dist/client/types.d.ts.map +1 -0
  109. package/dist/client/types.js +43 -0
  110. package/dist/client/types.js.map +1 -0
  111. package/dist/generator/client.d.ts +44 -0
  112. package/dist/generator/client.d.ts.map +1 -0
  113. package/dist/generator/client.js +144 -0
  114. package/dist/generator/client.js.map +1 -0
  115. package/dist/generator/datasource.d.ts +57 -0
  116. package/dist/generator/datasource.d.ts.map +1 -0
  117. package/dist/generator/datasource.js +169 -0
  118. package/dist/generator/datasource.js.map +1 -0
  119. package/dist/generator/datasource.test.d.ts +2 -0
  120. package/dist/generator/datasource.test.d.ts.map +1 -0
  121. package/dist/generator/datasource.test.js +254 -0
  122. package/dist/generator/datasource.test.js.map +1 -0
  123. package/dist/generator/index.d.ts +131 -0
  124. package/dist/generator/index.d.ts.map +1 -0
  125. package/dist/generator/index.js +121 -0
  126. package/dist/generator/index.js.map +1 -0
  127. package/dist/generator/index.test.d.ts +2 -0
  128. package/dist/generator/index.test.d.ts.map +1 -0
  129. package/dist/generator/index.test.js +175 -0
  130. package/dist/generator/index.test.js.map +1 -0
  131. package/dist/generator/loader.d.ts +156 -0
  132. package/dist/generator/loader.d.ts.map +1 -0
  133. package/dist/generator/loader.js +295 -0
  134. package/dist/generator/loader.js.map +1 -0
  135. package/dist/generator/pipe.d.ts +72 -0
  136. package/dist/generator/pipe.d.ts.map +1 -0
  137. package/dist/generator/pipe.js +174 -0
  138. package/dist/generator/pipe.js.map +1 -0
  139. package/dist/generator/pipe.test.d.ts +2 -0
  140. package/dist/generator/pipe.test.d.ts.map +1 -0
  141. package/dist/generator/pipe.test.js +393 -0
  142. package/dist/generator/pipe.test.js.map +1 -0
  143. package/dist/index.d.ts +74 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +73 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/infer/index.d.ts +202 -0
  148. package/dist/infer/index.d.ts.map +1 -0
  149. package/dist/infer/index.js +5 -0
  150. package/dist/infer/index.js.map +1 -0
  151. package/dist/schema/datasource.d.ts +135 -0
  152. package/dist/schema/datasource.d.ts.map +1 -0
  153. package/dist/schema/datasource.js +105 -0
  154. package/dist/schema/datasource.js.map +1 -0
  155. package/dist/schema/datasource.test.d.ts +2 -0
  156. package/dist/schema/datasource.test.d.ts.map +1 -0
  157. package/dist/schema/datasource.test.js +142 -0
  158. package/dist/schema/datasource.test.js.map +1 -0
  159. package/dist/schema/engines.d.ts +157 -0
  160. package/dist/schema/engines.d.ts.map +1 -0
  161. package/dist/schema/engines.js +155 -0
  162. package/dist/schema/engines.js.map +1 -0
  163. package/dist/schema/engines.test.d.ts +2 -0
  164. package/dist/schema/engines.test.d.ts.map +1 -0
  165. package/dist/schema/engines.test.js +221 -0
  166. package/dist/schema/engines.test.js.map +1 -0
  167. package/dist/schema/params.d.ts +106 -0
  168. package/dist/schema/params.d.ts.map +1 -0
  169. package/dist/schema/params.js +138 -0
  170. package/dist/schema/params.js.map +1 -0
  171. package/dist/schema/params.test.d.ts +2 -0
  172. package/dist/schema/params.test.d.ts.map +1 -0
  173. package/dist/schema/params.test.js +175 -0
  174. package/dist/schema/params.test.js.map +1 -0
  175. package/dist/schema/pipe.d.ts +436 -0
  176. package/dist/schema/pipe.d.ts.map +1 -0
  177. package/dist/schema/pipe.js +484 -0
  178. package/dist/schema/pipe.js.map +1 -0
  179. package/dist/schema/pipe.test.d.ts +2 -0
  180. package/dist/schema/pipe.test.d.ts.map +1 -0
  181. package/dist/schema/pipe.test.js +488 -0
  182. package/dist/schema/pipe.test.js.map +1 -0
  183. package/dist/schema/project.d.ts +202 -0
  184. package/dist/schema/project.d.ts.map +1 -0
  185. package/dist/schema/project.js +188 -0
  186. package/dist/schema/project.js.map +1 -0
  187. package/dist/schema/project.test.d.ts +2 -0
  188. package/dist/schema/project.test.d.ts.map +1 -0
  189. package/dist/schema/project.test.js +180 -0
  190. package/dist/schema/project.test.js.map +1 -0
  191. package/dist/schema/types.d.ts +140 -0
  192. package/dist/schema/types.d.ts.map +1 -0
  193. package/dist/schema/types.js +174 -0
  194. package/dist/schema/types.js.map +1 -0
  195. package/dist/schema/types.test.d.ts +2 -0
  196. package/dist/schema/types.test.d.ts.map +1 -0
  197. package/dist/schema/types.test.js +176 -0
  198. package/dist/schema/types.test.js.map +1 -0
  199. package/dist/test/handlers.d.ts +58 -0
  200. package/dist/test/handlers.d.ts.map +1 -0
  201. package/dist/test/handlers.js +62 -0
  202. package/dist/test/handlers.js.map +1 -0
  203. package/dist/test/setup.d.ts +5 -0
  204. package/dist/test/setup.d.ts.map +1 -0
  205. package/dist/test/setup.js +11 -0
  206. package/dist/test/setup.js.map +1 -0
  207. package/package.json +57 -0
  208. package/src/api/branches.test.ts +377 -0
  209. package/src/api/branches.ts +334 -0
  210. package/src/api/build.test.ts +216 -0
  211. package/src/api/build.ts +266 -0
  212. package/src/api/deploy.test.ts +193 -0
  213. package/src/api/deploy.ts +163 -0
  214. package/src/api/workspaces.test.ts +81 -0
  215. package/src/api/workspaces.ts +77 -0
  216. package/src/cli/auth.ts +358 -0
  217. package/src/cli/branch-store.test.ts +139 -0
  218. package/src/cli/branch-store.ts +137 -0
  219. package/src/cli/commands/branch.ts +306 -0
  220. package/src/cli/commands/build.ts +183 -0
  221. package/src/cli/commands/dev.ts +334 -0
  222. package/src/cli/commands/init.test.ts +249 -0
  223. package/src/cli/commands/init.ts +323 -0
  224. package/src/cli/commands/login.ts +98 -0
  225. package/src/cli/config.test.ts +359 -0
  226. package/src/cli/config.ts +335 -0
  227. package/src/cli/env.ts +86 -0
  228. package/src/cli/git.test.ts +147 -0
  229. package/src/cli/git.ts +125 -0
  230. package/src/cli/index.ts +382 -0
  231. package/src/cli/utils/schema-validation.test.ts +222 -0
  232. package/src/cli/utils/schema-validation.ts +272 -0
  233. package/src/client/base.ts +414 -0
  234. package/src/client/types.ts +165 -0
  235. package/src/generator/client.ts +194 -0
  236. package/src/generator/datasource.test.ts +297 -0
  237. package/src/generator/datasource.ts +217 -0
  238. package/src/generator/index.test.ts +209 -0
  239. package/src/generator/index.ts +203 -0
  240. package/src/generator/loader.ts +406 -0
  241. package/src/generator/pipe.test.ts +441 -0
  242. package/src/generator/pipe.ts +220 -0
  243. package/src/index.ts +191 -0
  244. package/src/infer/index.ts +247 -0
  245. package/src/schema/datasource.test.ts +187 -0
  246. package/src/schema/datasource.ts +195 -0
  247. package/src/schema/engines.test.ts +247 -0
  248. package/src/schema/engines.ts +271 -0
  249. package/src/schema/params.test.ts +208 -0
  250. package/src/schema/params.ts +249 -0
  251. package/src/schema/pipe.test.ts +588 -0
  252. package/src/schema/pipe.ts +832 -0
  253. package/src/schema/project.test.ts +236 -0
  254. package/src/schema/project.ts +394 -0
  255. package/src/schema/types.test.ts +212 -0
  256. package/src/schema/types.ts +366 -0
  257. package/src/test/handlers.ts +79 -0
  258. package/src/test/setup.ts +13 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Datasource content generator
3
+ * Converts DatasourceDefinition to native .datasource file format
4
+ */
5
+
6
+ import type { DatasourceDefinition, SchemaDefinition, ColumnDefinition } from "../schema/datasource.js";
7
+ import type { AnyTypeValidator, TypeModifiers } from "../schema/types.js";
8
+ import { getColumnType, getColumnJsonPath } from "../schema/datasource.js";
9
+ import { getEngineClause, type EngineConfig } from "../schema/engines.js";
10
+
11
+ /**
12
+ * Generated datasource content
13
+ */
14
+ export interface GeneratedDatasource {
15
+ /** Datasource name */
16
+ name: string;
17
+ /** The generated .datasource file content */
18
+ content: string;
19
+ }
20
+
21
+ /**
22
+ * Get the Tinybird type string from a type validator
23
+ * Handles the internal structure of validators
24
+ */
25
+ function getTinybirdTypeFromValidator(validator: AnyTypeValidator): string {
26
+ // The validator has _tinybirdType as the type string
27
+ return validator._tinybirdType;
28
+ }
29
+
30
+ /**
31
+ * Get modifiers from a validator
32
+ */
33
+ function getModifiersFromValidator(validator: AnyTypeValidator): TypeModifiers {
34
+ return validator._modifiers;
35
+ }
36
+
37
+ /**
38
+ * Format a default value for the datasource file
39
+ */
40
+ function formatDefaultValue(value: unknown, tinybirdType: string): string {
41
+ if (value === null) {
42
+ return "NULL";
43
+ }
44
+
45
+ if (typeof value === "string") {
46
+ // Escape single quotes
47
+ return `'${value.replace(/'/g, "\\'")}'`;
48
+ }
49
+
50
+ if (typeof value === "number" || typeof value === "bigint") {
51
+ return String(value);
52
+ }
53
+
54
+ if (typeof value === "boolean") {
55
+ return value ? "1" : "0";
56
+ }
57
+
58
+ if (value instanceof Date) {
59
+ // Format based on type
60
+ if (tinybirdType.startsWith("Date") && !tinybirdType.includes("Time")) {
61
+ return `'${value.toISOString().split("T")[0]}'`;
62
+ }
63
+ return `'${value.toISOString().replace("T", " ").slice(0, 19)}'`;
64
+ }
65
+
66
+ // For arrays and objects, use raw JSON (no quotes)
67
+ if (Array.isArray(value)) {
68
+ return JSON.stringify(value);
69
+ }
70
+
71
+ if (typeof value === "object" && value !== null) {
72
+ return JSON.stringify(value);
73
+ }
74
+
75
+ // Fallback for other types - stringify as string literal
76
+ return `'${String(value).replace(/'/g, "\\'")}'`;
77
+ }
78
+
79
+ /**
80
+ * Generate a column definition line for the schema
81
+ */
82
+ function generateColumnLine(
83
+ columnName: string,
84
+ column: AnyTypeValidator | ColumnDefinition,
85
+ includeJsonPaths: boolean
86
+ ): string {
87
+ const validator = getColumnType(column);
88
+ const jsonPath = getColumnJsonPath(column);
89
+ const tinybirdType = getTinybirdTypeFromValidator(validator);
90
+ const modifiers = getModifiersFromValidator(validator);
91
+
92
+ const parts: string[] = [` ${columnName} ${tinybirdType}`];
93
+
94
+ // Add JSON path for Events API ingestion support if enabled
95
+ // Use explicit jsonPath if defined, otherwise default to $.columnName
96
+ if (includeJsonPaths) {
97
+ const effectiveJsonPath = jsonPath ?? `$.${columnName}`;
98
+ parts.push(`\`json:${effectiveJsonPath}\``);
99
+ }
100
+
101
+ // Add default value if defined
102
+ if (modifiers.hasDefault && modifiers.defaultValue !== undefined) {
103
+ const defaultStr = formatDefaultValue(modifiers.defaultValue, tinybirdType);
104
+ parts.push(`DEFAULT ${defaultStr}`);
105
+ }
106
+
107
+ // Add codec if defined
108
+ if (modifiers.codec) {
109
+ parts.push(`CODEC(${modifiers.codec})`);
110
+ }
111
+
112
+ return parts.join(" ");
113
+ }
114
+
115
+ /**
116
+ * Generate the SCHEMA section
117
+ */
118
+ function generateSchema(schema: SchemaDefinition, includeJsonPaths: boolean): string {
119
+ const lines = ["SCHEMA >"];
120
+
121
+ const columnNames = Object.keys(schema);
122
+ columnNames.forEach((name, index) => {
123
+ const column = schema[name];
124
+ const line = generateColumnLine(name, column, includeJsonPaths);
125
+ // Add comma if not the last column
126
+ const suffix = index < columnNames.length - 1 ? "," : "";
127
+ lines.push(line + suffix);
128
+ });
129
+
130
+ return lines.join("\n");
131
+ }
132
+
133
+ /**
134
+ * Generate the engine configuration
135
+ * Uses the helper from engines.ts if an engine is provided
136
+ */
137
+ function generateEngineConfig(engine?: EngineConfig): string {
138
+ if (!engine) {
139
+ // Default to MergeTree with first column as sorting key
140
+ return 'ENGINE "MergeTree"';
141
+ }
142
+
143
+ return getEngineClause(engine);
144
+ }
145
+
146
+ /**
147
+ * Generate a .datasource file content from a DatasourceDefinition
148
+ *
149
+ * @param datasource - The datasource definition
150
+ * @returns Generated datasource content
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const events = defineDatasource('events', {
155
+ * description: 'User events',
156
+ * schema: {
157
+ * timestamp: t.dateTime(),
158
+ * user_id: t.string(),
159
+ * event: t.string(),
160
+ * },
161
+ * engine: engine.mergeTree({
162
+ * sortingKey: ['user_id', 'timestamp'],
163
+ * }),
164
+ * });
165
+ *
166
+ * const { content } = generateDatasource(events);
167
+ * // Returns:
168
+ * // DESCRIPTION >
169
+ * // User events
170
+ * //
171
+ * // SCHEMA >
172
+ * // timestamp DateTime,
173
+ * // user_id String,
174
+ * // event String
175
+ * //
176
+ * // ENGINE "MergeTree"
177
+ * // ENGINE_SORTING_KEY "user_id, timestamp"
178
+ * ```
179
+ */
180
+ export function generateDatasource(
181
+ datasource: DatasourceDefinition
182
+ ): GeneratedDatasource {
183
+ const parts: string[] = [];
184
+
185
+ // Add description if present
186
+ if (datasource.options.description) {
187
+ parts.push(`DESCRIPTION >\n ${datasource.options.description}`);
188
+ parts.push("");
189
+ }
190
+
191
+ // Check if JSON paths should be included (defaults to true)
192
+ const includeJsonPaths = datasource.options.jsonPaths !== false;
193
+
194
+ // Add schema
195
+ parts.push(generateSchema(datasource._schema, includeJsonPaths));
196
+ parts.push("");
197
+
198
+ // Add engine configuration
199
+ parts.push(generateEngineConfig(datasource.options.engine));
200
+
201
+ return {
202
+ name: datasource._name,
203
+ content: parts.join("\n"),
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Generate .datasource files for all datasources in a project
209
+ *
210
+ * @param datasources - Record of datasource definitions
211
+ * @returns Array of generated datasource content
212
+ */
213
+ export function generateAllDatasources(
214
+ datasources: Record<string, DatasourceDefinition>
215
+ ): GeneratedDatasource[] {
216
+ return Object.values(datasources).map(generateDatasource);
217
+ }
@@ -0,0 +1,209 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { generateResources, build } from "./index.js";
3
+ import { defineProject } from "../schema/project.js";
4
+ import { defineDatasource } from "../schema/datasource.js";
5
+ import { definePipe, node } from "../schema/pipe.js";
6
+ import { t } from "../schema/types.js";
7
+ import { engine } from "../schema/engines.js";
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ import * as os from "os";
11
+
12
+ describe("Generator Index", () => {
13
+ describe("generateResources", () => {
14
+ it("generates resources from a project definition", () => {
15
+ const events = defineDatasource("events", {
16
+ schema: {
17
+ timestamp: t.dateTime(),
18
+ event_name: t.string(),
19
+ },
20
+ });
21
+
22
+ const topEvents = definePipe("top_events", {
23
+ nodes: [
24
+ node({
25
+ name: "endpoint",
26
+ sql: "SELECT event_name, count() as cnt FROM events GROUP BY event_name",
27
+ }),
28
+ ],
29
+ output: {
30
+ event_name: t.string(),
31
+ cnt: t.uint64(),
32
+ },
33
+ endpoint: true,
34
+ });
35
+
36
+ const project = defineProject({
37
+ datasources: { events },
38
+ pipes: { topEvents },
39
+ });
40
+
41
+ const result = generateResources(project);
42
+
43
+ expect(result.datasources).toHaveLength(1);
44
+ expect(result.datasources[0].name).toBe("events");
45
+ expect(result.datasources[0].content).toContain("timestamp DateTime");
46
+
47
+ expect(result.pipes).toHaveLength(1);
48
+ expect(result.pipes[0].name).toBe("top_events");
49
+ expect(result.pipes[0].content).toContain("SELECT event_name");
50
+ });
51
+
52
+ it("handles empty project", () => {
53
+ const project = defineProject({
54
+ datasources: {},
55
+ pipes: {},
56
+ });
57
+
58
+ const result = generateResources(project);
59
+
60
+ expect(result.datasources).toHaveLength(0);
61
+ expect(result.pipes).toHaveLength(0);
62
+ });
63
+
64
+ it("generates multiple datasources and pipes", () => {
65
+ const ds1 = defineDatasource("ds1", { schema: { id: t.string() } });
66
+ const ds2 = defineDatasource("ds2", { schema: { name: t.string() } });
67
+ const ds3 = defineDatasource("ds3", { schema: { count: t.int32() } });
68
+
69
+ const pipe1 = definePipe("pipe1", {
70
+ nodes: [node({ name: "n", sql: "SELECT * FROM ds1" })],
71
+ output: { id: t.string() },
72
+ endpoint: true,
73
+ });
74
+ const pipe2 = definePipe("pipe2", {
75
+ nodes: [node({ name: "n", sql: "SELECT * FROM ds2" })],
76
+ output: { name: t.string() },
77
+ endpoint: true,
78
+ });
79
+
80
+ const project = defineProject({
81
+ datasources: { ds1, ds2, ds3 },
82
+ pipes: { pipe1, pipe2 },
83
+ });
84
+
85
+ const result = generateResources(project);
86
+
87
+ expect(result.datasources).toHaveLength(3);
88
+ expect(result.pipes).toHaveLength(2);
89
+ });
90
+
91
+ it("generates datasources with full options", () => {
92
+ const events = defineDatasource("events", {
93
+ description: "Event tracking data",
94
+ schema: {
95
+ timestamp: t.dateTime(),
96
+ event_name: t.string(),
97
+ user_id: t.string().nullable(),
98
+ metadata: t.string().default("{}"),
99
+ },
100
+ engine: engine.mergeTree({
101
+ sortingKey: ["timestamp", "event_name"],
102
+ partitionKey: "toYYYYMM(timestamp)",
103
+ }),
104
+ });
105
+
106
+ const project = defineProject({
107
+ datasources: { events },
108
+ pipes: {},
109
+ });
110
+
111
+ const result = generateResources(project);
112
+
113
+ expect(result.datasources).toHaveLength(1);
114
+ expect(result.datasources[0].content).toContain("DESCRIPTION >");
115
+ expect(result.datasources[0].content).toContain("Event tracking data");
116
+ expect(result.datasources[0].content).toContain("ENGINE_SORTING_KEY");
117
+ expect(result.datasources[0].content).toContain("ENGINE_PARTITION_KEY");
118
+ });
119
+
120
+ it("generates pipes with endpoint config", () => {
121
+ const stats = definePipe("stats", {
122
+ description: "Get stats",
123
+ nodes: [
124
+ node({
125
+ name: "calc",
126
+ description: "Calculate statistics",
127
+ sql: "SELECT count() as total FROM events",
128
+ }),
129
+ ],
130
+ output: { total: t.uint64() },
131
+ endpoint: {
132
+ enabled: true,
133
+ cache: { enabled: true, ttl: 120 },
134
+ },
135
+ });
136
+
137
+ const project = defineProject({
138
+ datasources: {},
139
+ pipes: { stats },
140
+ });
141
+
142
+ const result = generateResources(project);
143
+
144
+ expect(result.pipes).toHaveLength(1);
145
+ expect(result.pipes[0].content).toContain("DESCRIPTION >");
146
+ expect(result.pipes[0].content).toContain("Get stats");
147
+ expect(result.pipes[0].content).toContain("TYPE endpoint");
148
+ expect(result.pipes[0].content).toContain("CACHE 120");
149
+ });
150
+
151
+ it("generates non-endpoint pipes", () => {
152
+ const materialize = definePipe("materialize", {
153
+ nodes: [
154
+ node({
155
+ name: "aggregate",
156
+ sql: "SELECT event_name, count() FROM events GROUP BY event_name",
157
+ }),
158
+ ],
159
+ output: { event_name: t.string() },
160
+ endpoint: false,
161
+ });
162
+
163
+ const project = defineProject({
164
+ datasources: {},
165
+ pipes: { materialize },
166
+ });
167
+
168
+ const result = generateResources(project);
169
+
170
+ expect(result.pipes).toHaveLength(1);
171
+ expect(result.pipes[0].content).not.toContain("TYPE endpoint");
172
+ });
173
+ });
174
+
175
+ describe("build", () => {
176
+ let tempDir: string;
177
+
178
+ beforeEach(() => {
179
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-test-"));
180
+ });
181
+
182
+ afterEach(() => {
183
+ try {
184
+ fs.rmSync(tempDir, { recursive: true });
185
+ } catch {
186
+ // Ignore cleanup errors
187
+ }
188
+ });
189
+
190
+ it("throws error for non-existent schema file", async () => {
191
+ const schemaPath = path.join(tempDir, "nonexistent.ts");
192
+
193
+ await expect(build({ schemaPath })).rejects.toThrow("Schema file not found");
194
+ });
195
+
196
+ it("throws error when no project definition is exported", async () => {
197
+ const schemaContent = `
198
+ export const notAProject = { foo: "bar" };
199
+ `;
200
+
201
+ const schemaPath = path.join(tempDir, "invalid-schema.ts");
202
+ fs.writeFileSync(schemaPath, schemaContent);
203
+
204
+ await expect(build({ schemaPath })).rejects.toThrow(
205
+ "No ProjectDefinition found"
206
+ );
207
+ });
208
+ });
209
+ });
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Main generator module
3
+ * Orchestrates loading schema and generating all resources
4
+ */
5
+
6
+ import { loadSchema, loadEntities, entitiesToProject, type LoadedEntities } from "./loader.js";
7
+ import { generateAllDatasources, type GeneratedDatasource } from "./datasource.js";
8
+ import { generateAllPipes, type GeneratedPipe } from "./pipe.js";
9
+ import type { ProjectDefinition, DatasourcesDefinition, PipesDefinition } from "../schema/project.js";
10
+
11
+ /**
12
+ * Generated resources ready for API push
13
+ */
14
+ export interface GeneratedResources {
15
+ /** Generated datasource files */
16
+ datasources: GeneratedDatasource[];
17
+ /** Generated pipe files */
18
+ pipes: GeneratedPipe[];
19
+ }
20
+
21
+ /**
22
+ * Build result with metadata
23
+ */
24
+ export interface BuildResult {
25
+ /** The generated resources */
26
+ resources: GeneratedResources;
27
+ /** The loaded project definition (for validation) */
28
+ project: ProjectDefinition;
29
+ /** Path to the schema file */
30
+ schemaPath: string;
31
+ /** Directory containing the schema */
32
+ schemaDir: string;
33
+ /** Statistics about the build */
34
+ stats: {
35
+ datasourceCount: number;
36
+ pipeCount: number;
37
+ };
38
+ }
39
+
40
+ /**
41
+ * Generate resources from a loaded project definition
42
+ *
43
+ * @param project - The project definition
44
+ * @returns Generated resources
45
+ */
46
+ export function generateResources(project: ProjectDefinition): GeneratedResources {
47
+ const datasources = generateAllDatasources(project.datasources);
48
+ const pipes = generateAllPipes(project.pipes);
49
+
50
+ return {
51
+ datasources,
52
+ pipes,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Build options
58
+ */
59
+ export interface BuildOptions {
60
+ /** Path to the schema file */
61
+ schemaPath: string;
62
+ /** Working directory (defaults to cwd) */
63
+ cwd?: string;
64
+ }
65
+
66
+ /**
67
+ * Build all resources from a TypeScript schema
68
+ *
69
+ * This is the main entry point for the generator.
70
+ * It loads the schema, generates all resources, and returns them
71
+ * ready for API push.
72
+ *
73
+ * @param options - Build options
74
+ * @returns Build result with generated resources
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * const result = await build({
79
+ * schemaPath: 'src/tinybird/schema.ts',
80
+ * });
81
+ *
82
+ * console.log(`Generated ${result.stats.datasourceCount} datasources`);
83
+ * console.log(`Generated ${result.stats.pipeCount} pipes`);
84
+ *
85
+ * // Resources are ready to push to API
86
+ * result.resources.datasources.forEach(ds => {
87
+ * console.log(`${ds.name}.datasource:`);
88
+ * console.log(ds.content);
89
+ * });
90
+ * ```
91
+ */
92
+ export async function build(options: BuildOptions): Promise<BuildResult> {
93
+ // Load the schema
94
+ const loaded = await loadSchema({
95
+ schemaPath: options.schemaPath,
96
+ cwd: options.cwd,
97
+ });
98
+
99
+ // Generate resources
100
+ const resources = generateResources(loaded.project);
101
+
102
+ return {
103
+ resources,
104
+ project: loaded.project,
105
+ schemaPath: loaded.schemaPath,
106
+ schemaDir: loaded.schemaDir,
107
+ stats: {
108
+ datasourceCount: resources.datasources.length,
109
+ pipeCount: resources.pipes.length,
110
+ },
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Build options using include paths
116
+ */
117
+ export interface BuildFromIncludeOptions {
118
+ /** Array of file paths to scan for datasources and pipes */
119
+ includePaths: string[];
120
+ /** Working directory (defaults to cwd) */
121
+ cwd?: string;
122
+ }
123
+
124
+ /**
125
+ * Build result from include paths
126
+ */
127
+ export interface BuildFromIncludeResult {
128
+ /** The generated resources */
129
+ resources: GeneratedResources;
130
+ /** Loaded entities from source files */
131
+ entities: LoadedEntities;
132
+ /** Statistics about the build */
133
+ stats: {
134
+ datasourceCount: number;
135
+ pipeCount: number;
136
+ };
137
+ }
138
+
139
+ /**
140
+ * Generate resources from entities
141
+ */
142
+ export function generateResourcesFromEntities(
143
+ datasources: DatasourcesDefinition,
144
+ pipes: PipesDefinition
145
+ ): GeneratedResources {
146
+ return {
147
+ datasources: generateAllDatasources(datasources),
148
+ pipes: generateAllPipes(pipes),
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Build all resources from include paths
154
+ *
155
+ * This is the main entry point for the generator that works with
156
+ * auto-discovered entities. It loads datasources and pipes from the
157
+ * include paths and generates Tinybird resources ready to deploy.
158
+ *
159
+ * @param options - Build options with include paths
160
+ * @returns Build result with generated resources
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * const result = await buildFromInclude({
165
+ * includePaths: ['src/tinybird/datasources.ts', 'src/tinybird/pipes.ts'],
166
+ * });
167
+ *
168
+ * // Push resources to Tinybird
169
+ * await deploy(result.resources);
170
+ * ```
171
+ */
172
+ export async function buildFromInclude(
173
+ options: BuildFromIncludeOptions
174
+ ): Promise<BuildFromIncludeResult> {
175
+ const cwd = options.cwd ?? process.cwd();
176
+
177
+ // Load entities from include paths
178
+ const entities = await loadEntities({
179
+ includePaths: options.includePaths,
180
+ cwd,
181
+ });
182
+
183
+ // Convert to format for generators
184
+ const { datasources, pipes } = entitiesToProject(entities);
185
+
186
+ // Generate resources
187
+ const resources = generateResourcesFromEntities(datasources, pipes);
188
+
189
+ return {
190
+ resources,
191
+ entities,
192
+ stats: {
193
+ datasourceCount: resources.datasources.length,
194
+ pipeCount: resources.pipes.length,
195
+ },
196
+ };
197
+ }
198
+
199
+ // Re-export types and utilities
200
+ export { loadSchema, loadEntities, entitiesToProject, type LoaderOptions, type LoadedSchema, type LoadedEntities, type LoadEntitiesOptions } from "./loader.js";
201
+ export { generateDatasource, generateAllDatasources, type GeneratedDatasource } from "./datasource.js";
202
+ export { generatePipe, generateAllPipes, type GeneratedPipe } from "./pipe.js";
203
+ export { generateClientFile, type GenerateClientOptions, type GeneratedClient } from "./client.js";