@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,236 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ defineProject,
4
+ isProjectDefinition,
5
+ getDatasourceNames,
6
+ getPipeNames,
7
+ getDatasource,
8
+ getPipe,
9
+ } from "./project.js";
10
+ import { defineDatasource } from "./datasource.js";
11
+ import { definePipe, node } from "./pipe.js";
12
+ import { t } from "./types.js";
13
+
14
+ describe("Project Schema", () => {
15
+ describe("defineProject", () => {
16
+ it("creates a project with empty config", () => {
17
+ const project = defineProject({});
18
+
19
+ expect(project._type).toBe("project");
20
+ expect(project.datasources).toEqual({});
21
+ expect(project.pipes).toEqual({});
22
+ });
23
+
24
+ it("creates a project with datasources", () => {
25
+ const events = defineDatasource("events", {
26
+ schema: { id: t.string(), timestamp: t.dateTime() },
27
+ });
28
+
29
+ const project = defineProject({
30
+ datasources: { events },
31
+ });
32
+
33
+ expect(project.datasources.events).toBe(events);
34
+ expect(project.pipes).toEqual({});
35
+ });
36
+
37
+ it("creates a project with pipes", () => {
38
+ const topEvents = definePipe("top_events", {
39
+ nodes: [node({ name: "endpoint", sql: "SELECT 1" })],
40
+ output: { count: t.int64() },
41
+ endpoint: true,
42
+ });
43
+
44
+ const project = defineProject({
45
+ pipes: { topEvents },
46
+ });
47
+
48
+ expect(project.pipes.topEvents).toBe(topEvents);
49
+ expect(project.datasources).toEqual({});
50
+ });
51
+
52
+ it("creates a project with both datasources and pipes", () => {
53
+ const events = defineDatasource("events", {
54
+ schema: { id: t.string() },
55
+ });
56
+
57
+ const topEvents = definePipe("top_events", {
58
+ nodes: [node({ name: "endpoint", sql: "SELECT 1" })],
59
+ output: { count: t.int64() },
60
+ endpoint: true,
61
+ });
62
+
63
+ const project = defineProject({
64
+ datasources: { events },
65
+ pipes: { topEvents },
66
+ });
67
+
68
+ expect(project.datasources.events).toBe(events);
69
+ expect(project.pipes.topEvents).toBe(topEvents);
70
+ });
71
+
72
+ it("creates tinybird client with query and ingest methods", () => {
73
+ const events = defineDatasource("events", {
74
+ schema: { id: t.string() },
75
+ });
76
+
77
+ const topEvents = definePipe("top_events", {
78
+ nodes: [node({ name: "endpoint", sql: "SELECT 1" })],
79
+ output: { count: t.int64() },
80
+ endpoint: true,
81
+ });
82
+
83
+ const project = defineProject({
84
+ datasources: { events },
85
+ pipes: { topEvents },
86
+ });
87
+
88
+ expect(project.tinybird.query).toBeDefined();
89
+ expect(project.tinybird.ingest).toBeDefined();
90
+ expect(typeof project.tinybird.query.topEvents).toBe("function");
91
+ expect(typeof project.tinybird.ingest.events).toBe("function");
92
+ expect(typeof project.tinybird.ingest.eventsBatch).toBe("function");
93
+ });
94
+
95
+ it("throws error when accessing client before initialization", () => {
96
+ const project = defineProject({});
97
+
98
+ expect(() => project.tinybird.client).toThrow(
99
+ "Client not initialized"
100
+ );
101
+ });
102
+
103
+ it("creates stub for non-endpoint pipes that throws clear error", async () => {
104
+ const internalPipe = definePipe("internal_pipe", {
105
+ nodes: [node({ name: "endpoint", sql: "SELECT 1" })],
106
+ output: { count: t.int64() },
107
+ endpoint: false,
108
+ });
109
+
110
+ const project = defineProject({
111
+ pipes: { internalPipe },
112
+ });
113
+
114
+ // Cast to any since the type system expects params but stub throws regardless
115
+ const queryFn = project.tinybird.query.internalPipe as () => Promise<unknown>;
116
+ await expect(queryFn()).rejects.toThrow(
117
+ 'Pipe "internalPipe" is not exposed as an endpoint'
118
+ );
119
+ });
120
+ });
121
+
122
+ describe("isProjectDefinition", () => {
123
+ it("returns true for valid project", () => {
124
+ const project = defineProject({});
125
+
126
+ expect(isProjectDefinition(project)).toBe(true);
127
+ });
128
+
129
+ it("returns false for non-project objects", () => {
130
+ expect(isProjectDefinition({})).toBe(false);
131
+ expect(isProjectDefinition(null)).toBe(false);
132
+ expect(isProjectDefinition(undefined)).toBe(false);
133
+ expect(isProjectDefinition("string")).toBe(false);
134
+ expect(isProjectDefinition(123)).toBe(false);
135
+ expect(isProjectDefinition({ _type: "project" })).toBe(false);
136
+ });
137
+ });
138
+
139
+ describe("getDatasourceNames", () => {
140
+ it("returns all datasource names", () => {
141
+ const events = defineDatasource("events", {
142
+ schema: { id: t.string() },
143
+ });
144
+ const users = defineDatasource("users", {
145
+ schema: { id: t.string() },
146
+ });
147
+
148
+ const project = defineProject({
149
+ datasources: { events, users },
150
+ });
151
+
152
+ const names = getDatasourceNames(project);
153
+
154
+ expect(names).toHaveLength(2);
155
+ expect(names).toContain("events");
156
+ expect(names).toContain("users");
157
+ });
158
+
159
+ it("returns empty array for project with no datasources", () => {
160
+ const project = defineProject({});
161
+
162
+ const names = getDatasourceNames(project);
163
+
164
+ expect(names).toHaveLength(0);
165
+ });
166
+ });
167
+
168
+ describe("getPipeNames", () => {
169
+ it("returns all pipe names", () => {
170
+ const topEvents = definePipe("top_events", {
171
+ nodes: [node({ name: "endpoint", sql: "SELECT 1" })],
172
+ output: { count: t.int64() },
173
+ endpoint: true,
174
+ });
175
+ const userActivity = definePipe("user_activity", {
176
+ nodes: [node({ name: "endpoint", sql: "SELECT 2" })],
177
+ output: { count: t.int64() },
178
+ endpoint: true,
179
+ });
180
+
181
+ const project = defineProject({
182
+ pipes: { topEvents, userActivity },
183
+ });
184
+
185
+ const names = getPipeNames(project);
186
+
187
+ expect(names).toHaveLength(2);
188
+ expect(names).toContain("topEvents");
189
+ expect(names).toContain("userActivity");
190
+ });
191
+
192
+ it("returns empty array for project with no pipes", () => {
193
+ const project = defineProject({});
194
+
195
+ const names = getPipeNames(project);
196
+
197
+ expect(names).toHaveLength(0);
198
+ });
199
+ });
200
+
201
+ describe("getDatasource", () => {
202
+ it("returns datasource by name", () => {
203
+ const events = defineDatasource("events", {
204
+ schema: { id: t.string() },
205
+ });
206
+
207
+ const project = defineProject({
208
+ datasources: { events },
209
+ });
210
+
211
+ const retrieved = getDatasource(project, "events");
212
+
213
+ expect(retrieved).toBe(events);
214
+ expect(retrieved._name).toBe("events");
215
+ });
216
+ });
217
+
218
+ describe("getPipe", () => {
219
+ it("returns pipe by name", () => {
220
+ const topEvents = definePipe("top_events", {
221
+ nodes: [node({ name: "endpoint", sql: "SELECT 1" })],
222
+ output: { count: t.int64() },
223
+ endpoint: true,
224
+ });
225
+
226
+ const project = defineProject({
227
+ pipes: { topEvents },
228
+ });
229
+
230
+ const retrieved = getPipe(project, "topEvents");
231
+
232
+ expect(retrieved).toBe(topEvents);
233
+ expect(retrieved._name).toBe("top_events");
234
+ });
235
+ });
236
+ });
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Project definition for Tinybird
3
+ * Aggregates all datasources and pipes into a single schema
4
+ */
5
+
6
+ import type { DatasourceDefinition, SchemaDefinition } from "./datasource.js";
7
+ import type { PipeDefinition, ParamsDefinition, OutputDefinition } from "./pipe.js";
8
+ import { getEndpointConfig } from "./pipe.js";
9
+ import type { TinybirdClient } from "../client/base.js";
10
+ import type { QueryResult } from "../client/types.js";
11
+ import type { InferRow, InferParams, InferOutputRow } from "../infer/index.js";
12
+
13
+ // Symbol for brand typing
14
+ const PROJECT_BRAND = Symbol("tinybird.project");
15
+
16
+ /**
17
+ * Collection of datasource definitions
18
+ */
19
+ export type DatasourcesDefinition = Record<string, DatasourceDefinition<SchemaDefinition>>;
20
+
21
+ /**
22
+ * Collection of pipe definitions
23
+ */
24
+ export type PipesDefinition = Record<string, PipeDefinition<ParamsDefinition, OutputDefinition>>;
25
+
26
+ /**
27
+ * Type for a single query method
28
+ */
29
+ type QueryMethod<T extends PipeDefinition<ParamsDefinition, OutputDefinition>> =
30
+ T extends PipeDefinition<infer P, OutputDefinition>
31
+ ? keyof P extends never
32
+ ? () => Promise<QueryResult<InferOutputRow<T>>>
33
+ : (params: InferParams<T>) => Promise<QueryResult<InferOutputRow<T>>>
34
+ : never;
35
+
36
+ /**
37
+ * Type for query methods object
38
+ * Note: At runtime, only pipes with endpoint: true are included
39
+ */
40
+ type QueryMethods<T extends PipesDefinition> = {
41
+ [K in keyof T]: QueryMethod<T[K]>;
42
+ };
43
+
44
+ /**
45
+ * Type for a single ingest method
46
+ */
47
+ type IngestMethod<T extends DatasourceDefinition<SchemaDefinition>> = (
48
+ event: InferRow<T>
49
+ ) => Promise<void>;
50
+
51
+ /**
52
+ * Type for a batch ingest method
53
+ */
54
+ type IngestBatchMethod<T extends DatasourceDefinition<SchemaDefinition>> = (
55
+ events: InferRow<T>[]
56
+ ) => Promise<void>;
57
+
58
+ /**
59
+ * Type for ingest methods object
60
+ */
61
+ type IngestMethods<T extends DatasourcesDefinition> = {
62
+ [K in keyof T]: IngestMethod<T[K]>;
63
+ } & {
64
+ [K in keyof T as `${K & string}Batch`]: IngestBatchMethod<T[K]>;
65
+ };
66
+
67
+ /**
68
+ * Typed client interface for a project
69
+ */
70
+ export interface ProjectClient<
71
+ TDatasources extends DatasourcesDefinition,
72
+ TPipes extends PipesDefinition
73
+ > {
74
+ /** Query endpoint pipes */
75
+ query: QueryMethods<TPipes>;
76
+ /** Ingest events to datasources */
77
+ ingest: IngestMethods<TDatasources>;
78
+ /** Raw client for advanced usage */
79
+ readonly client: TinybirdClient;
80
+ }
81
+
82
+ /**
83
+ * Configuration for createTinybirdClient
84
+ */
85
+ export interface TinybirdClientConfig<
86
+ TDatasources extends DatasourcesDefinition = DatasourcesDefinition,
87
+ TPipes extends PipesDefinition = PipesDefinition
88
+ > {
89
+ /** All datasources */
90
+ datasources: TDatasources;
91
+ /** All pipes */
92
+ pipes: TPipes;
93
+ /** Tinybird API base URL (defaults to TINYBIRD_URL env var or https://api.tinybird.co) */
94
+ baseUrl?: string;
95
+ /** Tinybird API token (defaults to TINYBIRD_TOKEN env var) */
96
+ token?: string;
97
+ }
98
+
99
+ /**
100
+ * Project configuration
101
+ */
102
+ export interface ProjectConfig<
103
+ TDatasources extends DatasourcesDefinition = DatasourcesDefinition,
104
+ TPipes extends PipesDefinition = PipesDefinition
105
+ > {
106
+ /** All datasources in this project */
107
+ datasources?: TDatasources;
108
+ /** All pipes in this project */
109
+ pipes?: TPipes;
110
+ }
111
+
112
+ /**
113
+ * A project definition with full type information
114
+ */
115
+ export interface ProjectDefinition<
116
+ TDatasources extends DatasourcesDefinition = DatasourcesDefinition,
117
+ TPipes extends PipesDefinition = PipesDefinition
118
+ > {
119
+ readonly [PROJECT_BRAND]: true;
120
+ /** Type marker for inference */
121
+ readonly _type: "project";
122
+ /** All datasources */
123
+ readonly datasources: TDatasources;
124
+ /** All pipes */
125
+ readonly pipes: TPipes;
126
+ /** Typed Tinybird client */
127
+ readonly tinybird: ProjectClient<TDatasources, TPipes>;
128
+ }
129
+
130
+ /**
131
+ * Define a Tinybird project
132
+ *
133
+ * This aggregates all datasources and pipes into a single schema definition
134
+ * that can be used for code generation and type inference.
135
+ *
136
+ * @param config - Project configuration with datasources and pipes
137
+ * @returns A project definition
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * // tinybird/schema.ts
142
+ * import { defineProject } from '@tinybirdco/sdk';
143
+ * import { events, users } from './datasources';
144
+ * import { topEvents, userActivity } from './pipes';
145
+ *
146
+ * export default defineProject({
147
+ * datasources: {
148
+ * events,
149
+ * users,
150
+ * },
151
+ * pipes: {
152
+ * topEvents,
153
+ * userActivity,
154
+ * },
155
+ * });
156
+ * ```
157
+ */
158
+ export function defineProject<
159
+ TDatasources extends DatasourcesDefinition,
160
+ TPipes extends PipesDefinition
161
+ >(
162
+ config: ProjectConfig<TDatasources, TPipes>
163
+ ): ProjectDefinition<TDatasources, TPipes> {
164
+ const datasources = (config.datasources ?? {}) as TDatasources;
165
+ const pipes = (config.pipes ?? {}) as TPipes;
166
+
167
+ // Use the shared client builder
168
+ const tinybird = buildProjectClient(datasources, pipes);
169
+
170
+ return {
171
+ [PROJECT_BRAND]: true,
172
+ _type: "project",
173
+ datasources,
174
+ pipes,
175
+ tinybird,
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Check if a value is a project definition
181
+ */
182
+ export function isProjectDefinition(value: unknown): value is ProjectDefinition {
183
+ return (
184
+ typeof value === "object" &&
185
+ value !== null &&
186
+ PROJECT_BRAND in value &&
187
+ (value as Record<symbol, unknown>)[PROJECT_BRAND] === true
188
+ );
189
+ }
190
+
191
+ /**
192
+ * Build a typed Tinybird client from datasources and pipes
193
+ *
194
+ * This is an internal helper that builds query/ingest methods.
195
+ */
196
+ function buildProjectClient<
197
+ TDatasources extends DatasourcesDefinition,
198
+ TPipes extends PipesDefinition
199
+ >(
200
+ datasources: TDatasources,
201
+ pipes: TPipes,
202
+ options?: { baseUrl?: string; token?: string }
203
+ ): ProjectClient<TDatasources, TPipes> {
204
+ // Lazy client initialization
205
+ let _client: TinybirdClient | null = null;
206
+
207
+ const getClient = async (): Promise<TinybirdClient> => {
208
+ if (!_client) {
209
+ // Dynamic import to avoid circular dependencies
210
+ const { createClient } = await import("../client/base.js");
211
+ _client = createClient({
212
+ baseUrl: options?.baseUrl ?? process.env.TINYBIRD_URL ?? "https://api.tinybird.co",
213
+ token: options?.token ?? process.env.TINYBIRD_TOKEN!,
214
+ devMode: process.env.NODE_ENV === "development",
215
+ });
216
+ }
217
+ return _client;
218
+ };
219
+
220
+ // Build query methods for pipes
221
+ const queryMethods: Record<string, (params?: unknown) => Promise<unknown>> = {};
222
+ for (const [name, pipe] of Object.entries(pipes)) {
223
+ const endpointConfig = getEndpointConfig(pipe);
224
+
225
+ if (!endpointConfig) {
226
+ // Non-endpoint pipes get a stub that throws a clear error
227
+ queryMethods[name] = async () => {
228
+ throw new Error(
229
+ `Pipe "${name}" is not exposed as an endpoint. ` +
230
+ `Set "endpoint: true" in the pipe definition to enable querying.`
231
+ );
232
+ };
233
+ continue;
234
+ }
235
+
236
+ // Use the Tinybird pipe name (snake_case)
237
+ const tinybirdName = pipe._name;
238
+ queryMethods[name] = async (params?: unknown) => {
239
+ const client = await getClient();
240
+ return client.query(tinybirdName, (params ?? {}) as Record<string, unknown>);
241
+ };
242
+ }
243
+
244
+ // Build ingest methods for datasources
245
+ const ingestMethods: Record<string, (data: unknown) => Promise<void>> = {};
246
+ for (const [name, datasource] of Object.entries(datasources)) {
247
+ // Use the Tinybird datasource name (snake_case)
248
+ const tinybirdName = datasource._name;
249
+
250
+ // Single event ingest
251
+ ingestMethods[name] = async (event: unknown) => {
252
+ const client = await getClient();
253
+ await client.ingest(tinybirdName, event as Record<string, unknown>);
254
+ };
255
+
256
+ // Batch ingest
257
+ ingestMethods[`${name}Batch`] = async (events: unknown) => {
258
+ const client = await getClient();
259
+ await client.ingestBatch(tinybirdName, events as Record<string, unknown>[]);
260
+ };
261
+ }
262
+
263
+ // Create the typed client object
264
+ return {
265
+ query: queryMethods,
266
+ ingest: ingestMethods,
267
+ get client(): TinybirdClient {
268
+ // Synchronous client access - will throw if not initialized
269
+ if (!_client) {
270
+ throw new Error(
271
+ "Client not initialized. Call a query or ingest method first, or access client asynchronously."
272
+ );
273
+ }
274
+ return _client;
275
+ },
276
+ } as ProjectClient<TDatasources, TPipes>;
277
+ }
278
+
279
+ /**
280
+ * Create a typed Tinybird client
281
+ *
282
+ * Creates a client with typed query and ingest methods based on the provided
283
+ * datasources and pipes. This is the recommended way to create a Tinybird client
284
+ * when using the SDK's auto-generated client file.
285
+ *
286
+ * @param config - Client configuration with datasources and pipes
287
+ * @returns A typed client with query and ingest methods
288
+ *
289
+ * @example
290
+ * ```ts
291
+ * import { createTinybirdClient } from '@tinybirdco/sdk';
292
+ * import { pageViews, events } from './datasources';
293
+ * import { topPages } from './pipes';
294
+ *
295
+ * export const tinybird = createTinybirdClient({
296
+ * datasources: { pageViews, events },
297
+ * pipes: { topPages },
298
+ * });
299
+ *
300
+ * // Query a pipe (fully typed)
301
+ * const result = await tinybird.query.topPages({
302
+ * start_date: new Date('2024-01-01'),
303
+ * end_date: new Date('2024-01-31'),
304
+ * });
305
+ *
306
+ * // Ingest an event (fully typed)
307
+ * await tinybird.ingest.pageViews({
308
+ * timestamp: new Date(),
309
+ * pathname: '/home',
310
+ * session_id: 'abc123',
311
+ * });
312
+ * ```
313
+ */
314
+ export function createTinybirdClient<
315
+ TDatasources extends DatasourcesDefinition,
316
+ TPipes extends PipesDefinition
317
+ >(
318
+ config: TinybirdClientConfig<TDatasources, TPipes>
319
+ ): ProjectClient<TDatasources, TPipes> {
320
+ return buildProjectClient(
321
+ config.datasources,
322
+ config.pipes,
323
+ { baseUrl: config.baseUrl, token: config.token }
324
+ );
325
+ }
326
+
327
+ /**
328
+ * Get all datasource names from a project
329
+ */
330
+ export function getDatasourceNames<T extends ProjectDefinition>(
331
+ project: T
332
+ ): (keyof T["datasources"])[] {
333
+ return Object.keys(project.datasources) as (keyof T["datasources"])[];
334
+ }
335
+
336
+ /**
337
+ * Get all pipe names from a project
338
+ */
339
+ export function getPipeNames<T extends ProjectDefinition>(project: T): (keyof T["pipes"])[] {
340
+ return Object.keys(project.pipes) as (keyof T["pipes"])[];
341
+ }
342
+
343
+ /**
344
+ * Get a datasource by name from a project
345
+ */
346
+ export function getDatasource<
347
+ TDatasources extends DatasourcesDefinition,
348
+ TPipes extends PipesDefinition,
349
+ K extends keyof TDatasources
350
+ >(project: ProjectDefinition<TDatasources, TPipes>, name: K): TDatasources[K] {
351
+ return project.datasources[name];
352
+ }
353
+
354
+ /**
355
+ * Get a pipe by name from a project
356
+ */
357
+ export function getPipe<
358
+ TDatasources extends DatasourcesDefinition,
359
+ TPipes extends PipesDefinition,
360
+ K extends keyof TPipes
361
+ >(project: ProjectDefinition<TDatasources, TPipes>, name: K): TPipes[K] {
362
+ return project.pipes[name];
363
+ }
364
+
365
+ /**
366
+ * Helper type to extract datasources from a project
367
+ */
368
+ export type ExtractDatasources<T> = T extends ProjectDefinition<infer D, PipesDefinition>
369
+ ? D
370
+ : never;
371
+
372
+ /**
373
+ * Helper type to extract pipes from a project
374
+ */
375
+ export type ExtractPipes<T> = T extends ProjectDefinition<DatasourcesDefinition, infer P>
376
+ ? P
377
+ : never;
378
+
379
+ /**
380
+ * Data model type derived from a project
381
+ * Useful for generating typed clients
382
+ */
383
+ export type DataModel<T extends ProjectDefinition> = {
384
+ datasources: {
385
+ [K in keyof T["datasources"]]: T["datasources"][K] extends DatasourceDefinition<infer S>
386
+ ? S
387
+ : never;
388
+ };
389
+ pipes: {
390
+ [K in keyof T["pipes"]]: T["pipes"][K] extends PipeDefinition<infer P, infer O>
391
+ ? { params: P; output: O }
392
+ : never;
393
+ };
394
+ };