@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,323 @@
1
+ /**
2
+ * Init command - scaffolds a new Tinybird TypeScript project
3
+ */
4
+
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ import {
8
+ hasValidToken,
9
+ getTinybirdDir,
10
+ getRelativeTinybirdDir,
11
+ getConfigPath,
12
+ updateConfig,
13
+ } from "../config.js";
14
+ import { browserLogin } from "../auth.js";
15
+ import { saveTinybirdToken } from "../env.js";
16
+
17
+ /**
18
+ * Default starter content for datasources.ts
19
+ */
20
+ const DATASOURCES_CONTENT = `import { defineDatasource, t, engine, type InferRow } from "@tinybirdco/sdk";
21
+
22
+ /**
23
+ * Page views datasource - tracks page view events
24
+ */
25
+ export const pageViews = defineDatasource("page_views", {
26
+ description: "Page view tracking data",
27
+ schema: {
28
+ timestamp: t.dateTime(),
29
+ session_id: t.string(),
30
+ pathname: t.string(),
31
+ referrer: t.string().nullable(),
32
+ },
33
+ engine: engine.mergeTree({
34
+ sortingKey: ["pathname", "timestamp"],
35
+ }),
36
+ });
37
+
38
+ // Row type - use this for ingesting data
39
+ export type PageViewsRow = InferRow<typeof pageViews>;
40
+ `;
41
+
42
+ /**
43
+ * Default starter content for pipes.ts
44
+ */
45
+ const PIPES_CONTENT = `import { defineEndpoint, node, t, p, type InferParams, type InferOutputRow } from "@tinybirdco/sdk";
46
+
47
+ /**
48
+ * Top pages endpoint - get the most visited pages
49
+ */
50
+ export const topPages = defineEndpoint("top_pages", {
51
+ description: "Get the most visited pages",
52
+ params: {
53
+ start_date: p.dateTime().describe("Start of date range"),
54
+ end_date: p.dateTime().describe("End of date range"),
55
+ limit: p.int32().optional(10).describe("Number of results"),
56
+ },
57
+ nodes: [
58
+ node({
59
+ name: "aggregated",
60
+ sql: \`
61
+ SELECT
62
+ pathname,
63
+ count() AS views
64
+ FROM page_views
65
+ WHERE timestamp >= {{DateTime(start_date)}}
66
+ AND timestamp <= {{DateTime(end_date)}}
67
+ GROUP BY pathname
68
+ ORDER BY views DESC
69
+ LIMIT {{Int32(limit, 10)}}
70
+ \`,
71
+ }),
72
+ ],
73
+ output: {
74
+ pathname: t.string(),
75
+ views: t.uint64(),
76
+ },
77
+ });
78
+
79
+ // Endpoint types - use these for calling the API
80
+ export type TopPagesParams = InferParams<typeof topPages>;
81
+ export type TopPagesOutput = InferOutputRow<typeof topPages>;
82
+ `;
83
+
84
+ /**
85
+ * Default starter content for client.ts
86
+ */
87
+ const CLIENT_CONTENT = `/**
88
+ * Tinybird Client
89
+ *
90
+ * This file defines the typed Tinybird client for your project.
91
+ * Add your datasources and pipes here as you create them.
92
+ */
93
+
94
+ import { createTinybirdClient } from "@tinybirdco/sdk";
95
+
96
+ // Import datasources and their row types
97
+ import { pageViews, type PageViewsRow } from "./datasources";
98
+
99
+ // Import endpoints and their types
100
+ import { topPages, type TopPagesParams, type TopPagesOutput } from "./pipes";
101
+
102
+ // Create the typed Tinybird client
103
+ export const tinybird = createTinybirdClient({
104
+ datasources: { pageViews },
105
+ pipes: { topPages },
106
+ });
107
+
108
+ // Re-export types for convenience
109
+ export type { PageViewsRow, TopPagesParams, TopPagesOutput };
110
+
111
+ // Re-export entities
112
+ export { pageViews, topPages };
113
+ `;
114
+
115
+ /**
116
+ * Default config content generator
117
+ */
118
+ function createDefaultConfig(tinybirdDir: string) {
119
+ return {
120
+ include: [
121
+ `${tinybirdDir}/datasources.ts`,
122
+ `${tinybirdDir}/pipes.ts`,
123
+ ],
124
+ token: "${TINYBIRD_TOKEN}",
125
+ baseUrl: "https://api.tinybird.co",
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Init command options
131
+ */
132
+ export interface InitOptions {
133
+ /** Working directory (defaults to cwd) */
134
+ cwd?: string;
135
+ /** Force overwrite existing files */
136
+ force?: boolean;
137
+ /** Skip the login flow */
138
+ skipLogin?: boolean;
139
+ }
140
+
141
+ /**
142
+ * Init command result
143
+ */
144
+ export interface InitResult {
145
+ /** Whether initialization was successful */
146
+ success: boolean;
147
+ /** Files that were created */
148
+ created: string[];
149
+ /** Files that were skipped (already exist) */
150
+ skipped: string[];
151
+ /** Error message if failed */
152
+ error?: string;
153
+ /** Whether login was completed */
154
+ loggedIn?: boolean;
155
+ /** Workspace name after login */
156
+ workspaceName?: string;
157
+ /** User email after login */
158
+ userEmail?: string;
159
+ }
160
+
161
+ /**
162
+ * Run the init command
163
+ *
164
+ * Creates:
165
+ * - tinybird.json in the project root
166
+ * - src/tinybird/ folder with datasources.ts, pipes.ts, and client.ts
167
+ *
168
+ * @param options - Init options
169
+ * @returns Init result
170
+ */
171
+ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
172
+ const cwd = options.cwd ?? process.cwd();
173
+ const force = options.force ?? false;
174
+ const skipLogin = options.skipLogin ?? false;
175
+
176
+ const created: string[] = [];
177
+ const skipped: string[] = [];
178
+
179
+ // Determine tinybird folder path based on project structure
180
+ const tinybirdDir = getTinybirdDir(cwd);
181
+ const relativeTinybirdDir = getRelativeTinybirdDir(cwd);
182
+
183
+ // File paths
184
+ const datasourcesPath = path.join(tinybirdDir, "datasources.ts");
185
+ const pipesPath = path.join(tinybirdDir, "pipes.ts");
186
+ const clientPath = path.join(tinybirdDir, "client.ts");
187
+
188
+ // Create config file (tinybird.json)
189
+ const configPath = getConfigPath(cwd);
190
+ if (fs.existsSync(configPath) && !force) {
191
+ skipped.push("tinybird.json");
192
+ } else {
193
+ try {
194
+ const config = createDefaultConfig(relativeTinybirdDir);
195
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
196
+ created.push("tinybird.json");
197
+ } catch (error) {
198
+ return {
199
+ success: false,
200
+ created,
201
+ skipped,
202
+ error: `Failed to create tinybird.json: ${(error as Error).message}`,
203
+ };
204
+ }
205
+ }
206
+
207
+ // Create tinybird directory
208
+ try {
209
+ fs.mkdirSync(tinybirdDir, { recursive: true });
210
+ } catch (error) {
211
+ return {
212
+ success: false,
213
+ created,
214
+ skipped,
215
+ error: `Failed to create ${relativeTinybirdDir} folder: ${(error as Error).message}`,
216
+ };
217
+ }
218
+
219
+ // Create datasources.ts
220
+ if (fs.existsSync(datasourcesPath) && !force) {
221
+ skipped.push(`${relativeTinybirdDir}/datasources.ts`);
222
+ } else {
223
+ try {
224
+ fs.writeFileSync(datasourcesPath, DATASOURCES_CONTENT);
225
+ created.push(`${relativeTinybirdDir}/datasources.ts`);
226
+ } catch (error) {
227
+ return {
228
+ success: false,
229
+ created,
230
+ skipped,
231
+ error: `Failed to create datasources.ts: ${(error as Error).message}`,
232
+ };
233
+ }
234
+ }
235
+
236
+ // Create pipes.ts
237
+ if (fs.existsSync(pipesPath) && !force) {
238
+ skipped.push(`${relativeTinybirdDir}/pipes.ts`);
239
+ } else {
240
+ try {
241
+ fs.writeFileSync(pipesPath, PIPES_CONTENT);
242
+ created.push(`${relativeTinybirdDir}/pipes.ts`);
243
+ } catch (error) {
244
+ return {
245
+ success: false,
246
+ created,
247
+ skipped,
248
+ error: `Failed to create pipes.ts: ${(error as Error).message}`,
249
+ };
250
+ }
251
+ }
252
+
253
+ // Create client.ts
254
+ if (fs.existsSync(clientPath) && !force) {
255
+ skipped.push(`${relativeTinybirdDir}/client.ts`);
256
+ } else {
257
+ try {
258
+ fs.writeFileSync(clientPath, CLIENT_CONTENT);
259
+ created.push(`${relativeTinybirdDir}/client.ts`);
260
+ } catch (error) {
261
+ return {
262
+ success: false,
263
+ created,
264
+ skipped,
265
+ error: `Failed to create client.ts: ${(error as Error).message}`,
266
+ };
267
+ }
268
+ }
269
+
270
+ // Check if login is needed
271
+ if (!skipLogin && !hasValidToken(cwd)) {
272
+ console.log("\nNo authentication found. Starting login flow...\n");
273
+
274
+ const authResult = await browserLogin();
275
+
276
+ if (authResult.success && authResult.token) {
277
+ // Save token to .env.local
278
+ try {
279
+ const saveResult = saveTinybirdToken(cwd, authResult.token);
280
+ if (saveResult.created) {
281
+ created.push(".env.local");
282
+ }
283
+
284
+ // If custom base URL, update tinybird.json
285
+ if (authResult.baseUrl && authResult.baseUrl !== "https://api.tinybird.co") {
286
+ updateConfig(configPath, { baseUrl: authResult.baseUrl });
287
+ }
288
+
289
+ return {
290
+ success: true,
291
+ created,
292
+ skipped,
293
+ loggedIn: true,
294
+ workspaceName: authResult.workspaceName,
295
+ userEmail: authResult.userEmail,
296
+ };
297
+ } catch (error) {
298
+ // Login succeeded but saving credentials failed
299
+ console.error(`Warning: Failed to save credentials: ${(error as Error).message}`);
300
+ return {
301
+ success: true,
302
+ created,
303
+ skipped,
304
+ loggedIn: false,
305
+ };
306
+ }
307
+ } else {
308
+ // Login failed or was cancelled
309
+ return {
310
+ success: true,
311
+ created,
312
+ skipped,
313
+ loggedIn: false,
314
+ };
315
+ }
316
+ }
317
+
318
+ return {
319
+ success: true,
320
+ created,
321
+ skipped,
322
+ };
323
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Login command - authenticate with Tinybird via browser
3
+ */
4
+
5
+ import * as path from "path";
6
+ import { browserLogin, type LoginOptions, type AuthResult } from "../auth.js";
7
+ import { updateConfig, findConfigFile } from "../config.js";
8
+ import { saveTinybirdToken } from "../env.js";
9
+
10
+ /**
11
+ * Login command options
12
+ */
13
+ export interface RunLoginOptions {
14
+ /** Working directory (defaults to cwd) */
15
+ cwd?: string;
16
+ /** Override the API host (region) */
17
+ apiHost?: string;
18
+ }
19
+
20
+ /**
21
+ * Login command result
22
+ */
23
+ export interface LoginResult {
24
+ /** Whether login was successful */
25
+ success: boolean;
26
+ /** Workspace name after login */
27
+ workspaceName?: string;
28
+ /** User email */
29
+ userEmail?: string;
30
+ /** API base URL */
31
+ baseUrl?: string;
32
+ /** Error message if failed */
33
+ error?: string;
34
+ }
35
+
36
+ /**
37
+ * Run the login command
38
+ *
39
+ * Opens browser for authentication and stores credentials in tinybird.json
40
+ *
41
+ * @param options - Login options
42
+ * @returns Login result
43
+ */
44
+ export async function runLogin(options: RunLoginOptions = {}): Promise<LoginResult> {
45
+ const cwd = options.cwd ?? process.cwd();
46
+
47
+ // Find the actual config file (may be in parent directory)
48
+ const configPath = findConfigFile(cwd);
49
+ if (!configPath) {
50
+ return {
51
+ success: false,
52
+ error: "No tinybird.json found. Run 'npx tinybird init' first.",
53
+ };
54
+ }
55
+
56
+ // Get the directory containing the config file for .env.local
57
+ const configDir = path.dirname(configPath);
58
+
59
+ const loginOptions: LoginOptions = {};
60
+ if (options.apiHost) {
61
+ loginOptions.apiHost = options.apiHost;
62
+ }
63
+
64
+ // Perform browser login
65
+ const authResult: AuthResult = await browserLogin(loginOptions);
66
+
67
+ // Guard against missing token in auth response
68
+ if (!authResult.success || !authResult.token) {
69
+ return {
70
+ success: false,
71
+ error: authResult.error ?? "Login failed",
72
+ };
73
+ }
74
+
75
+ // Save token to .env.local (in same directory as tinybird.json)
76
+ try {
77
+ saveTinybirdToken(configDir, authResult.token);
78
+
79
+ // Update baseUrl in tinybird.json if it changed
80
+ if (authResult.baseUrl) {
81
+ updateConfig(configPath, {
82
+ baseUrl: authResult.baseUrl,
83
+ });
84
+ }
85
+ } catch (error) {
86
+ return {
87
+ success: false,
88
+ error: `Failed to save credentials: ${(error as Error).message}`,
89
+ };
90
+ }
91
+
92
+ return {
93
+ success: true,
94
+ workspaceName: authResult.workspaceName,
95
+ userEmail: authResult.userEmail,
96
+ baseUrl: authResult.baseUrl,
97
+ };
98
+ }