@tinybirdco/sdk 0.0.4 → 0.0.7

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 (165) hide show
  1. package/README.md +52 -13
  2. package/dist/api/branches.d.ts.map +1 -1
  3. package/dist/api/branches.js +6 -5
  4. package/dist/api/branches.js.map +1 -1
  5. package/dist/api/branches.test.js +32 -6
  6. package/dist/api/branches.test.js.map +1 -1
  7. package/dist/api/build.d.ts.map +1 -1
  8. package/dist/api/build.js +2 -1
  9. package/dist/api/build.js.map +1 -1
  10. package/dist/api/deploy.d.ts +42 -3
  11. package/dist/api/deploy.d.ts.map +1 -1
  12. package/dist/api/deploy.js +162 -19
  13. package/dist/api/deploy.js.map +1 -1
  14. package/dist/api/deploy.test.js +83 -31
  15. package/dist/api/deploy.test.js.map +1 -1
  16. package/dist/api/fetcher.d.ts +6 -0
  17. package/dist/api/fetcher.d.ts.map +1 -0
  18. package/dist/api/fetcher.js +13 -0
  19. package/dist/api/fetcher.js.map +1 -0
  20. package/dist/api/local.d.ts.map +1 -1
  21. package/dist/api/local.js +5 -4
  22. package/dist/api/local.js.map +1 -1
  23. package/dist/api/local.test.js.map +1 -1
  24. package/dist/api/resources.d.ts +178 -0
  25. package/dist/api/resources.d.ts.map +1 -0
  26. package/dist/api/resources.js +245 -0
  27. package/dist/api/resources.js.map +1 -0
  28. package/dist/api/resources.test.d.ts +2 -0
  29. package/dist/api/resources.test.d.ts.map +1 -0
  30. package/dist/api/resources.test.js +255 -0
  31. package/dist/api/resources.test.js.map +1 -0
  32. package/dist/api/workspaces.d.ts.map +1 -1
  33. package/dist/api/workspaces.js +2 -1
  34. package/dist/api/workspaces.js.map +1 -1
  35. package/dist/api/workspaces.test.js +9 -1
  36. package/dist/api/workspaces.test.js.map +1 -1
  37. package/dist/cli/auth.d.ts.map +1 -1
  38. package/dist/cli/auth.js +2 -1
  39. package/dist/cli/auth.js.map +1 -1
  40. package/dist/cli/commands/build.d.ts +3 -4
  41. package/dist/cli/commands/build.d.ts.map +1 -1
  42. package/dist/cli/commands/build.js +23 -25
  43. package/dist/cli/commands/build.js.map +1 -1
  44. package/dist/cli/commands/deploy.d.ts +41 -0
  45. package/dist/cli/commands/deploy.d.ts.map +1 -0
  46. package/dist/cli/commands/deploy.js +92 -0
  47. package/dist/cli/commands/deploy.js.map +1 -0
  48. package/dist/cli/commands/dev.d.ts.map +1 -1
  49. package/dist/cli/commands/dev.js +7 -3
  50. package/dist/cli/commands/dev.js.map +1 -1
  51. package/dist/cli/commands/init.d.ts +38 -1
  52. package/dist/cli/commands/init.d.ts.map +1 -1
  53. package/dist/cli/commands/init.js +434 -23
  54. package/dist/cli/commands/init.js.map +1 -1
  55. package/dist/cli/commands/init.test.js +190 -30
  56. package/dist/cli/commands/init.test.js.map +1 -1
  57. package/dist/cli/index.js +80 -15
  58. package/dist/cli/index.js.map +1 -1
  59. package/dist/cli/utils/package-manager.d.ts +8 -0
  60. package/dist/cli/utils/package-manager.d.ts.map +1 -0
  61. package/dist/cli/utils/package-manager.js +45 -0
  62. package/dist/cli/utils/package-manager.js.map +1 -0
  63. package/dist/cli/utils/package-manager.test.d.ts +2 -0
  64. package/dist/cli/utils/package-manager.test.d.ts.map +1 -0
  65. package/dist/cli/utils/package-manager.test.js +85 -0
  66. package/dist/cli/utils/package-manager.test.js.map +1 -0
  67. package/dist/client/base.d.ts.map +1 -1
  68. package/dist/client/base.js +2 -1
  69. package/dist/client/base.js.map +1 -1
  70. package/dist/codegen/index.d.ts +39 -0
  71. package/dist/codegen/index.d.ts.map +1 -0
  72. package/dist/codegen/index.js +300 -0
  73. package/dist/codegen/index.js.map +1 -0
  74. package/dist/codegen/index.test.d.ts +2 -0
  75. package/dist/codegen/index.test.d.ts.map +1 -0
  76. package/dist/codegen/index.test.js +310 -0
  77. package/dist/codegen/index.test.js.map +1 -0
  78. package/dist/codegen/type-mapper.d.ts +20 -0
  79. package/dist/codegen/type-mapper.d.ts.map +1 -0
  80. package/dist/codegen/type-mapper.js +238 -0
  81. package/dist/codegen/type-mapper.js.map +1 -0
  82. package/dist/codegen/type-mapper.test.d.ts +2 -0
  83. package/dist/codegen/type-mapper.test.d.ts.map +1 -0
  84. package/dist/codegen/type-mapper.test.js +167 -0
  85. package/dist/codegen/type-mapper.test.js.map +1 -0
  86. package/dist/codegen/utils.d.ts +46 -0
  87. package/dist/codegen/utils.d.ts.map +1 -0
  88. package/dist/codegen/utils.js +141 -0
  89. package/dist/codegen/utils.js.map +1 -0
  90. package/dist/codegen/utils.test.d.ts +2 -0
  91. package/dist/codegen/utils.test.d.ts.map +1 -0
  92. package/dist/codegen/utils.test.js +178 -0
  93. package/dist/codegen/utils.test.js.map +1 -0
  94. package/dist/generator/index.d.ts +3 -0
  95. package/dist/generator/index.d.ts.map +1 -1
  96. package/dist/generator/index.js +17 -1
  97. package/dist/generator/index.js.map +1 -1
  98. package/dist/generator/index.test.js +104 -1
  99. package/dist/generator/index.test.js.map +1 -1
  100. package/dist/generator/loader.d.ts +15 -0
  101. package/dist/generator/loader.d.ts.map +1 -1
  102. package/dist/generator/loader.js +24 -0
  103. package/dist/generator/loader.js.map +1 -1
  104. package/dist/schema/connection.d.ts.map +1 -1
  105. package/dist/schema/connection.js +3 -2
  106. package/dist/schema/connection.js.map +1 -1
  107. package/dist/schema/datasource.d.ts.map +1 -1
  108. package/dist/schema/datasource.js +3 -2
  109. package/dist/schema/datasource.js.map +1 -1
  110. package/dist/schema/params.d.ts.map +1 -1
  111. package/dist/schema/params.js +3 -2
  112. package/dist/schema/params.js.map +1 -1
  113. package/dist/schema/pipe.d.ts +2 -2
  114. package/dist/schema/pipe.d.ts.map +1 -1
  115. package/dist/schema/pipe.js +4 -4
  116. package/dist/schema/pipe.js.map +1 -1
  117. package/dist/schema/project.d.ts.map +1 -1
  118. package/dist/schema/project.js +3 -2
  119. package/dist/schema/project.js.map +1 -1
  120. package/dist/schema/types.d.ts.map +1 -1
  121. package/dist/schema/types.js +3 -2
  122. package/dist/schema/types.js.map +1 -1
  123. package/dist/test/handlers.d.ts +49 -0
  124. package/dist/test/handlers.d.ts.map +1 -1
  125. package/dist/test/handlers.js +45 -0
  126. package/dist/test/handlers.js.map +1 -1
  127. package/package.json +4 -2
  128. package/src/api/branches.test.ts +65 -57
  129. package/src/api/branches.ts +7 -5
  130. package/src/api/build.ts +2 -1
  131. package/src/api/deploy.test.ts +141 -36
  132. package/src/api/deploy.ts +231 -23
  133. package/src/api/fetcher.ts +17 -0
  134. package/src/api/local.test.ts +43 -31
  135. package/src/api/local.ts +5 -4
  136. package/src/api/resources.test.ts +332 -0
  137. package/src/api/resources.ts +555 -0
  138. package/src/api/workspaces.test.ts +15 -9
  139. package/src/api/workspaces.ts +3 -1
  140. package/src/cli/auth.ts +2 -1
  141. package/src/cli/commands/build.ts +29 -33
  142. package/src/cli/commands/deploy.ts +131 -0
  143. package/src/cli/commands/dev.ts +10 -3
  144. package/src/cli/commands/init.test.ts +239 -30
  145. package/src/cli/commands/init.ts +548 -26
  146. package/src/cli/index.ts +117 -20
  147. package/src/cli/utils/package-manager.test.ts +118 -0
  148. package/src/cli/utils/package-manager.ts +44 -0
  149. package/src/client/base.ts +3 -2
  150. package/src/codegen/index.test.ts +367 -0
  151. package/src/codegen/index.ts +379 -0
  152. package/src/codegen/type-mapper.test.ts +224 -0
  153. package/src/codegen/type-mapper.ts +265 -0
  154. package/src/codegen/utils.test.ts +221 -0
  155. package/src/codegen/utils.ts +174 -0
  156. package/src/generator/index.test.ts +121 -1
  157. package/src/generator/index.ts +19 -1
  158. package/src/generator/loader.ts +43 -0
  159. package/src/schema/connection.ts +3 -2
  160. package/src/schema/datasource.ts +3 -2
  161. package/src/schema/params.ts +3 -2
  162. package/src/schema/pipe.ts +4 -4
  163. package/src/schema/project.ts +3 -2
  164. package/src/schema/types.ts +3 -2
  165. package/src/test/handlers.ts +58 -0
@@ -4,12 +4,14 @@
4
4
 
5
5
  import * as fs from "fs";
6
6
  import * as path from "path";
7
+ import * as p from "@clack/prompts";
8
+ import pc from "picocolors";
7
9
  import {
8
10
  hasValidToken,
9
- getTinybirdDir,
10
11
  getRelativeTinybirdDir,
11
12
  getConfigPath,
12
13
  updateConfig,
14
+ type DevMode,
13
15
  } from "../config.js";
14
16
  import { browserLogin } from "../auth.js";
15
17
  import { saveTinybirdToken } from "../env.js";
@@ -40,9 +42,9 @@ export type PageViewsRow = InferRow<typeof pageViews>;
40
42
  `;
41
43
 
42
44
  /**
43
- * Default starter content for pipes.ts
45
+ * Default starter content for endpoints.ts
44
46
  */
45
- const PIPES_CONTENT = `import { defineEndpoint, node, t, p, type InferParams, type InferOutputRow } from "@tinybirdco/sdk";
47
+ const ENDPOINTS_CONTENT = `import { defineEndpoint, node, t, p, type InferParams, type InferOutputRow } from "@tinybirdco/sdk";
46
48
 
47
49
  /**
48
50
  * Top pages endpoint - get the most visited pages
@@ -88,7 +90,7 @@ const CLIENT_CONTENT = `/**
88
90
  * Tinybird Client
89
91
  *
90
92
  * This file defines the typed Tinybird client for your project.
91
- * Add your datasources and pipes here as you create them.
93
+ * Add your datasources and endpoints here as you create them.
92
94
  */
93
95
 
94
96
  import { createTinybirdClient } from "@tinybirdco/sdk";
@@ -97,7 +99,7 @@ import { createTinybirdClient } from "@tinybirdco/sdk";
97
99
  import { pageViews, type PageViewsRow } from "./datasources";
98
100
 
99
101
  // Import endpoints and their types
100
- import { topPages, type TopPagesParams, type TopPagesOutput } from "./pipes";
102
+ import { topPages, type TopPagesParams, type TopPagesOutput } from "./endpoints";
101
103
 
102
104
  // Create the typed Tinybird client
103
105
  export const tinybird = createTinybirdClient({
@@ -112,17 +114,82 @@ export type { PageViewsRow, TopPagesParams, TopPagesOutput };
112
114
  export { pageViews, topPages };
113
115
  `;
114
116
 
117
+ const TINYBIRD_CI_WORKFLOW = `name: Tinybird CI
118
+
119
+ on:
120
+ pull_request:
121
+ paths:
122
+ - "tinybird.json"
123
+ - "src/tinybird/**"
124
+ - "tinybird/**"
125
+ - "**/*.datasource"
126
+ - "**/*.pipe"
127
+
128
+ jobs:
129
+ tinybird:
130
+ runs-on: ubuntu-latest
131
+ steps:
132
+ - uses: actions/checkout@v4
133
+ - uses: pnpm/action-setup@v4
134
+ - uses: actions/setup-node@v4
135
+ with:
136
+ node-version: "20"
137
+ cache: "pnpm"
138
+ - run: pnpm install --frozen-lockfile
139
+ - run: pnpm run tinybird:deploy -- --check
140
+ env:
141
+ TINYBIRD_TOKEN: \${{ secrets.TINYBIRD_TOKEN }}
142
+ `;
143
+
144
+ const TINYBIRD_CD_WORKFLOW = `name: Tinybird CD
145
+
146
+ on:
147
+ push:
148
+ branches:
149
+ - main
150
+ paths:
151
+ - "tinybird.json"
152
+ - "src/tinybird/**"
153
+ - "tinybird/**"
154
+ - "**/*.datasource"
155
+ - "**/*.pipe"
156
+
157
+ jobs:
158
+ tinybird:
159
+ runs-on: ubuntu-latest
160
+ steps:
161
+ - uses: actions/checkout@v4
162
+ - uses: pnpm/action-setup@v4
163
+ - uses: actions/setup-node@v4
164
+ with:
165
+ node-version: "20"
166
+ cache: "pnpm"
167
+ - run: pnpm install --frozen-lockfile
168
+ - run: pnpm run tinybird:deploy
169
+ env:
170
+ TINYBIRD_TOKEN: \${{ secrets.TINYBIRD_TOKEN }}
171
+ `;
172
+
173
+ const TINYBIRD_GITLAB_CI = `stages:\n - tinybird\n\ntinybird_ci:\n stage: tinybird\n image: node:20\n rules:\n - changes:\n - tinybird.json\n - src/tinybird/**\n - tinybird/**\n - **/*.datasource\n - **/*.pipe\n script:\n - corepack enable\n - pnpm install --frozen-lockfile\n - pnpm run tinybird:deploy -- --check\n variables:\n TINYBIRD_TOKEN: \${TINYBIRD_TOKEN}\n\ntinybird_cd:\n stage: tinybird\n image: node:20\n rules:\n - if: '$CI_COMMIT_BRANCH == \"main\"'\n changes:\n - tinybird.json\n - src/tinybird/**\n - tinybird/**\n - **/*.datasource\n - **/*.pipe\n script:\n - corepack enable\n - pnpm install --frozen-lockfile\n - pnpm run tinybird:deploy\n variables:\n TINYBIRD_TOKEN: \${TINYBIRD_TOKEN}\n`;
174
+
115
175
  /**
116
176
  * Default config content generator
117
177
  */
118
- function createDefaultConfig(tinybirdDir: string) {
178
+ function createDefaultConfig(
179
+ tinybirdDir: string,
180
+ devMode: DevMode,
181
+ additionalIncludes: string[] = []
182
+ ) {
183
+ const include = [
184
+ `${tinybirdDir}/datasources.ts`,
185
+ `${tinybirdDir}/endpoints.ts`,
186
+ ...additionalIncludes,
187
+ ];
119
188
  return {
120
- include: [
121
- `${tinybirdDir}/datasources.ts`,
122
- `${tinybirdDir}/pipes.ts`,
123
- ],
189
+ include,
124
190
  token: "${TINYBIRD_TOKEN}",
125
191
  baseUrl: "https://api.tinybird.co",
192
+ devMode,
126
193
  };
127
194
  }
128
195
 
@@ -136,6 +203,22 @@ export interface InitOptions {
136
203
  force?: boolean;
137
204
  /** Skip the login flow */
138
205
  skipLogin?: boolean;
206
+ /** Development mode - if provided, skip interactive prompt */
207
+ devMode?: DevMode;
208
+ /** Client path - if provided, skip interactive prompt */
209
+ clientPath?: string;
210
+ /** Skip prompts for existing datafiles - for testing */
211
+ skipDatafilePrompt?: boolean;
212
+ /** Auto-include existing datafiles without prompting - for testing */
213
+ includeExistingDatafiles?: boolean;
214
+ /** Skip GitHub Actions workflow prompts */
215
+ skipWorkflowPrompt?: boolean;
216
+ /** Include Tinybird CI workflow */
217
+ includeCiWorkflow?: boolean;
218
+ /** Include Tinybird CD workflow */
219
+ includeCdWorkflow?: boolean;
220
+ /** Git provider for workflow templates */
221
+ workflowProvider?: "github" | "gitlab";
139
222
  }
140
223
 
141
224
  /**
@@ -156,6 +239,72 @@ export interface InitResult {
156
239
  workspaceName?: string;
157
240
  /** User email after login */
158
241
  userEmail?: string;
242
+ /** Selected development mode */
243
+ devMode?: DevMode;
244
+ /** Selected client path */
245
+ clientPath?: string;
246
+ /** Existing datafiles that were added to config */
247
+ existingDatafiles?: string[];
248
+ /** Whether a Tinybird CI workflow was created */
249
+ ciWorkflowCreated?: boolean;
250
+ /** Whether a Tinybird CD workflow was created */
251
+ cdWorkflowCreated?: boolean;
252
+ /** Git provider used for workflow templates */
253
+ workflowProvider?: "github" | "gitlab";
254
+ }
255
+
256
+ /**
257
+ * Find existing .datasource and .pipe files in the repository
258
+ *
259
+ * @param cwd - Working directory to search from
260
+ * @param maxDepth - Maximum directory depth to search (default: 5)
261
+ * @returns Array of relative file paths
262
+ */
263
+ export function findExistingDatafiles(
264
+ cwd: string,
265
+ maxDepth: number = 5
266
+ ): string[] {
267
+ const files: string[] = [];
268
+
269
+ function searchDir(dir: string, depth: number): void {
270
+ if (depth > maxDepth) return;
271
+
272
+ let entries: fs.Dirent[];
273
+ try {
274
+ entries = fs.readdirSync(dir, { withFileTypes: true });
275
+ } catch {
276
+ return; // Skip directories we can't read
277
+ }
278
+
279
+ for (const entry of entries) {
280
+ const fullPath = path.join(dir, entry.name);
281
+
282
+ // Skip node_modules and hidden directories
283
+ if (
284
+ entry.isDirectory() &&
285
+ (entry.name === "node_modules" ||
286
+ entry.name.startsWith(".") ||
287
+ entry.name === "dist" ||
288
+ entry.name === "build")
289
+ ) {
290
+ continue;
291
+ }
292
+
293
+ if (entry.isDirectory()) {
294
+ searchDir(fullPath, depth + 1);
295
+ } else if (
296
+ entry.isFile() &&
297
+ (entry.name.endsWith(".datasource") || entry.name.endsWith(".pipe"))
298
+ ) {
299
+ // Convert to relative path
300
+ const relativePath = path.relative(cwd, fullPath);
301
+ files.push(relativePath);
302
+ }
303
+ }
304
+ }
305
+
306
+ searchDir(cwd, 0);
307
+ return files.sort();
159
308
  }
160
309
 
161
310
  /**
@@ -163,7 +312,7 @@ export interface InitResult {
163
312
  *
164
313
  * Creates:
165
314
  * - tinybird.json in the project root
166
- * - src/tinybird/ folder with datasources.ts, pipes.ts, and client.ts
315
+ * - src/tinybird/ folder with datasources.ts, endpoints.ts, and client.ts
167
316
  *
168
317
  * @param options - Init options
169
318
  * @returns Init result
@@ -175,14 +324,219 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
175
324
 
176
325
  const created: string[] = [];
177
326
  const skipped: string[] = [];
327
+ let didPrompt = false;
328
+ let existingDatafiles: string[] = [];
329
+ let ciWorkflowCreated = false;
330
+ let cdWorkflowCreated = false;
331
+ let workflowProvider = options.workflowProvider;
332
+
333
+ // Check for existing .datasource and .pipe files
334
+ const foundDatafiles = findExistingDatafiles(cwd);
335
+
336
+ // Determine devMode - prompt if not provided
337
+ let devMode: DevMode = options.devMode ?? "branch";
338
+
339
+ if (!options.devMode) {
340
+ // Show interactive prompt for workflow selection
341
+ p.intro(pc.cyan("tinybird init"));
342
+
343
+ const workflowChoice = await p.select({
344
+ message: "How do you want to develop with Tinybird?",
345
+ options: [
346
+ {
347
+ value: "branch",
348
+ label: "Branches",
349
+ hint: "Use Tinybird Cloud with git-based branching",
350
+ },
351
+ {
352
+ value: "local",
353
+ label: "Tinybird Local",
354
+ hint: "Run your own Tinybird instance locally",
355
+ },
356
+ ],
357
+ });
358
+
359
+ if (p.isCancel(workflowChoice)) {
360
+ p.cancel("Operation cancelled");
361
+ return {
362
+ success: false,
363
+ created: [],
364
+ skipped: [],
365
+ error: "Cancelled by user",
366
+ };
367
+ }
368
+
369
+ didPrompt = true;
370
+ devMode = workflowChoice as DevMode;
371
+ }
178
372
 
179
373
  // Determine tinybird folder path based on project structure
180
- const tinybirdDir = getTinybirdDir(cwd);
181
- const relativeTinybirdDir = getRelativeTinybirdDir(cwd);
374
+ const defaultRelativePath = getRelativeTinybirdDir(cwd);
375
+ let relativeTinybirdDir = options.clientPath ?? defaultRelativePath;
376
+
377
+ if (!options.clientPath && !options.devMode) {
378
+ // Ask user to confirm or change the client path
379
+ const clientPathChoice = await p.text({
380
+ message: "Where should we create Tinybird definitions?",
381
+ placeholder: defaultRelativePath,
382
+ defaultValue: defaultRelativePath,
383
+ });
384
+
385
+ if (p.isCancel(clientPathChoice)) {
386
+ p.cancel("Operation cancelled");
387
+ return {
388
+ success: false,
389
+ created: [],
390
+ skipped: [],
391
+ error: "Cancelled by user",
392
+ };
393
+ }
394
+
395
+ didPrompt = true;
396
+ relativeTinybirdDir = clientPathChoice || defaultRelativePath;
397
+ }
398
+
399
+ const skipWorkflowPrompt =
400
+ options.skipWorkflowPrompt ??
401
+ (options.devMode !== undefined || options.clientPath !== undefined);
402
+ let includeCiWorkflow = options.includeCiWorkflow ?? false;
403
+ let includeCdWorkflow = options.includeCdWorkflow ?? false;
404
+ const shouldPromptWorkflows =
405
+ !skipWorkflowPrompt && options.includeCiWorkflow === undefined;
406
+ const shouldPromptProvider = !skipWorkflowPrompt && !workflowProvider;
407
+
408
+ if (shouldPromptProvider && shouldPromptWorkflows) {
409
+ const providerChoice = await p.select({
410
+ message: "Which git provider are you using?",
411
+ options: [
412
+ { value: "github", label: "GitHub" },
413
+ { value: "gitlab", label: "GitLab" },
414
+ ],
415
+ });
416
+
417
+ if (p.isCancel(providerChoice)) {
418
+ p.cancel("Operation cancelled");
419
+ return {
420
+ success: false,
421
+ created: [],
422
+ skipped: [],
423
+ error: "Cancelled by user",
424
+ };
425
+ }
426
+
427
+ didPrompt = true;
428
+ workflowProvider = providerChoice as "github" | "gitlab";
429
+ }
430
+
431
+ if (shouldPromptWorkflows) {
432
+ const confirmWorkflows = await p.confirm({
433
+ message: "Create CI/CD workflows for Tinybird?",
434
+ initialValue: true,
435
+ });
436
+
437
+ if (p.isCancel(confirmWorkflows)) {
438
+ p.cancel("Operation cancelled");
439
+ return {
440
+ success: false,
441
+ created: [],
442
+ skipped: [],
443
+ error: "Cancelled by user",
444
+ };
445
+ }
446
+
447
+ didPrompt = true;
448
+ includeCiWorkflow = confirmWorkflows;
449
+ includeCdWorkflow = confirmWorkflows;
450
+ }
451
+
452
+ if (shouldPromptProvider && !shouldPromptWorkflows && !workflowProvider) {
453
+ const providerChoice = await p.select({
454
+ message: "Which git provider are you using?",
455
+ options: [
456
+ { value: "github", label: "GitHub" },
457
+ { value: "gitlab", label: "GitLab" },
458
+ ],
459
+ });
460
+
461
+ if (p.isCancel(providerChoice)) {
462
+ p.cancel("Operation cancelled");
463
+ return {
464
+ success: false,
465
+ created: [],
466
+ skipped: [],
467
+ error: "Cancelled by user",
468
+ };
469
+ }
470
+
471
+ didPrompt = true;
472
+ workflowProvider = providerChoice as "github" | "gitlab";
473
+ }
474
+ if ((includeCiWorkflow || includeCdWorkflow) && !workflowProvider) {
475
+ workflowProvider = "github";
476
+ }
477
+
478
+ // Ask about existing datafiles if found
479
+ if (foundDatafiles.length > 0 && !options.skipDatafilePrompt) {
480
+ const includeDatafiles =
481
+ options.includeExistingDatafiles ??
482
+ (await (async () => {
483
+ didPrompt = true;
484
+ return promptForExistingDatafiles(foundDatafiles);
485
+ })());
486
+
487
+ if (includeDatafiles) {
488
+ existingDatafiles = foundDatafiles;
489
+ }
490
+ } else if (options.includeExistingDatafiles && foundDatafiles.length > 0) {
491
+ existingDatafiles = foundDatafiles;
492
+ }
493
+
494
+ if (didPrompt) {
495
+ const devModeLabel = devMode === "local" ? "Tinybird Local" : "Branches";
496
+ const datafileSummary =
497
+ foundDatafiles.length > 0
498
+ ? existingDatafiles.length > 0
499
+ ? `${existingDatafiles.length} included`
500
+ : "none included"
501
+ : "none found";
502
+ let workflowSummary = "none";
503
+ if (includeCiWorkflow || includeCdWorkflow) {
504
+ workflowSummary =
505
+ workflowProvider === "gitlab"
506
+ ? "GitLab (.gitlab-ci.yml)"
507
+ : "GitHub Actions (tinybird-ci.yaml, tinybird-cd.yaml)";
508
+ }
509
+
510
+ const summaryLines = [
511
+ `Mode: ${devModeLabel}`,
512
+ `Definitions: ${relativeTinybirdDir}/`,
513
+ `Existing datafiles: ${datafileSummary}`,
514
+ `Workflows: ${workflowSummary}`,
515
+ ];
516
+
517
+ p.note(summaryLines.join("\n"), "Installation Summary");
518
+
519
+ const confirmInit = await p.confirm({
520
+ message: "Proceed with initialization?",
521
+ initialValue: true,
522
+ });
523
+
524
+ if (p.isCancel(confirmInit) || !confirmInit) {
525
+ p.cancel("Init cancelled.");
526
+ return {
527
+ success: false,
528
+ created: [],
529
+ skipped: [],
530
+ error: "Cancelled by user",
531
+ };
532
+ }
533
+ }
534
+
535
+ const tinybirdDir = path.join(cwd, relativeTinybirdDir);
182
536
 
183
537
  // File paths
184
538
  const datasourcesPath = path.join(tinybirdDir, "datasources.ts");
185
- const pipesPath = path.join(tinybirdDir, "pipes.ts");
539
+ const endpointsPath = path.join(tinybirdDir, "endpoints.ts");
186
540
  const clientPath = path.join(tinybirdDir, "client.ts");
187
541
 
188
542
  // Create config file (tinybird.json)
@@ -191,7 +545,11 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
191
545
  skipped.push("tinybird.json");
192
546
  } else {
193
547
  try {
194
- const config = createDefaultConfig(relativeTinybirdDir);
548
+ const config = createDefaultConfig(
549
+ relativeTinybirdDir,
550
+ devMode,
551
+ existingDatafiles
552
+ );
195
553
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
196
554
  created.push("tinybird.json");
197
555
  } catch (error) {
@@ -212,7 +570,9 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
212
570
  success: false,
213
571
  created,
214
572
  skipped,
215
- error: `Failed to create ${relativeTinybirdDir} folder: ${(error as Error).message}`,
573
+ error: `Failed to create ${relativeTinybirdDir} folder: ${
574
+ (error as Error).message
575
+ }`,
216
576
  };
217
577
  }
218
578
 
@@ -233,19 +593,19 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
233
593
  }
234
594
  }
235
595
 
236
- // Create pipes.ts
237
- if (fs.existsSync(pipesPath) && !force) {
238
- skipped.push(`${relativeTinybirdDir}/pipes.ts`);
596
+ // Create endpoints.ts
597
+ if (fs.existsSync(endpointsPath) && !force) {
598
+ skipped.push(`${relativeTinybirdDir}/endpoints.ts`);
239
599
  } else {
240
600
  try {
241
- fs.writeFileSync(pipesPath, PIPES_CONTENT);
242
- created.push(`${relativeTinybirdDir}/pipes.ts`);
601
+ fs.writeFileSync(endpointsPath, ENDPOINTS_CONTENT);
602
+ created.push(`${relativeTinybirdDir}/endpoints.ts`);
243
603
  } catch (error) {
244
604
  return {
245
605
  success: false,
246
606
  created,
247
607
  skipped,
248
- error: `Failed to create pipes.ts: ${(error as Error).message}`,
608
+ error: `Failed to create endpoints.ts: ${(error as Error).message}`,
249
609
  };
250
610
  }
251
611
  }
@@ -288,8 +648,16 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
288
648
  modified = true;
289
649
  }
290
650
 
651
+ if (!packageJson.scripts["tinybird:deploy"]) {
652
+ packageJson.scripts["tinybird:deploy"] = "tinybird deploy";
653
+ modified = true;
654
+ }
655
+
291
656
  if (modified) {
292
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
657
+ fs.writeFileSync(
658
+ packageJsonPath,
659
+ JSON.stringify(packageJson, null, 2) + "\n"
660
+ );
293
661
  created.push("package.json (added tinybird scripts)");
294
662
  }
295
663
  } catch {
@@ -297,6 +665,94 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
297
665
  }
298
666
  }
299
667
 
668
+ const workflowsDir = path.join(cwd, ".github", "workflows");
669
+ const ciWorkflowPath = path.join(workflowsDir, "tinybird-ci.yaml");
670
+ const cdWorkflowPath = path.join(workflowsDir, "tinybird-cd.yaml");
671
+ const gitlabWorkflowPath = path.join(cwd, ".gitlab-ci.yml");
672
+
673
+ if (includeCiWorkflow || includeCdWorkflow) {
674
+ if (workflowProvider === "github") {
675
+ try {
676
+ fs.mkdirSync(workflowsDir, { recursive: true });
677
+ } catch (error) {
678
+ return {
679
+ success: false,
680
+ created,
681
+ skipped,
682
+ error: `Failed to create .github/workflows folder: ${
683
+ (error as Error).message
684
+ }`,
685
+ };
686
+ }
687
+ }
688
+
689
+ if (workflowProvider === "gitlab") {
690
+ if (fs.existsSync(gitlabWorkflowPath) && !force) {
691
+ skipped.push(".gitlab-ci.yml");
692
+ } else {
693
+ try {
694
+ fs.writeFileSync(gitlabWorkflowPath, TINYBIRD_GITLAB_CI);
695
+ created.push(".gitlab-ci.yml");
696
+ ciWorkflowCreated = includeCiWorkflow;
697
+ cdWorkflowCreated = includeCdWorkflow;
698
+ } catch (error) {
699
+ return {
700
+ success: false,
701
+ created,
702
+ skipped,
703
+ error: `Failed to create .gitlab-ci.yml: ${
704
+ (error as Error).message
705
+ }`,
706
+ };
707
+ }
708
+ }
709
+ }
710
+ }
711
+
712
+ if (workflowProvider === "github") {
713
+ if (includeCiWorkflow) {
714
+ if (fs.existsSync(ciWorkflowPath) && !force) {
715
+ skipped.push(".github/workflows/tinybird-ci.yaml");
716
+ } else {
717
+ try {
718
+ fs.writeFileSync(ciWorkflowPath, TINYBIRD_CI_WORKFLOW);
719
+ created.push(".github/workflows/tinybird-ci.yaml");
720
+ ciWorkflowCreated = true;
721
+ } catch (error) {
722
+ return {
723
+ success: false,
724
+ created,
725
+ skipped,
726
+ error: `Failed to create tinybird-ci.yaml: ${
727
+ (error as Error).message
728
+ }`,
729
+ };
730
+ }
731
+ }
732
+ }
733
+
734
+ if (includeCdWorkflow) {
735
+ if (fs.existsSync(cdWorkflowPath) && !force) {
736
+ skipped.push(".github/workflows/tinybird-cd.yaml");
737
+ } else {
738
+ try {
739
+ fs.writeFileSync(cdWorkflowPath, TINYBIRD_CD_WORKFLOW);
740
+ created.push(".github/workflows/tinybird-cd.yaml");
741
+ cdWorkflowCreated = true;
742
+ } catch (error) {
743
+ return {
744
+ success: false,
745
+ created,
746
+ skipped,
747
+ error: `Failed to create tinybird-cd.yaml: ${
748
+ (error as Error).message
749
+ }`,
750
+ };
751
+ }
752
+ }
753
+ }
754
+ }
755
+
300
756
  // Check if login is needed
301
757
  if (!skipLogin && !hasValidToken(cwd)) {
302
758
  console.log("\nNo authentication found. Starting login flow...\n");
@@ -312,8 +768,9 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
312
768
  }
313
769
 
314
770
  // If custom base URL, update tinybird.json
315
- if (authResult.baseUrl && authResult.baseUrl !== "https://api.tinybird.co") {
316
- updateConfig(configPath, { baseUrl: authResult.baseUrl });
771
+ const baseUrl = authResult.baseUrl ?? "https://api.tinybird.co";
772
+ if (baseUrl !== "https://api.tinybird.co") {
773
+ updateConfig(configPath, { baseUrl });
317
774
  }
318
775
 
319
776
  return {
@@ -323,15 +780,31 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
323
780
  loggedIn: true,
324
781
  workspaceName: authResult.workspaceName,
325
782
  userEmail: authResult.userEmail,
783
+ devMode,
784
+ clientPath: relativeTinybirdDir,
785
+ existingDatafiles:
786
+ existingDatafiles.length > 0 ? existingDatafiles : undefined,
787
+ ciWorkflowCreated,
788
+ cdWorkflowCreated,
789
+ workflowProvider,
326
790
  };
327
791
  } catch (error) {
328
792
  // Login succeeded but saving credentials failed
329
- console.error(`Warning: Failed to save credentials: ${(error as Error).message}`);
793
+ console.error(
794
+ `Warning: Failed to save credentials: ${(error as Error).message}`
795
+ );
330
796
  return {
331
797
  success: true,
332
798
  created,
333
799
  skipped,
334
800
  loggedIn: false,
801
+ devMode,
802
+ clientPath: relativeTinybirdDir,
803
+ existingDatafiles:
804
+ existingDatafiles.length > 0 ? existingDatafiles : undefined,
805
+ ciWorkflowCreated,
806
+ cdWorkflowCreated,
807
+ workflowProvider,
335
808
  };
336
809
  }
337
810
  } else {
@@ -341,6 +814,13 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
341
814
  created,
342
815
  skipped,
343
816
  loggedIn: false,
817
+ devMode,
818
+ clientPath: relativeTinybirdDir,
819
+ existingDatafiles:
820
+ existingDatafiles.length > 0 ? existingDatafiles : undefined,
821
+ ciWorkflowCreated,
822
+ cdWorkflowCreated,
823
+ workflowProvider,
344
824
  };
345
825
  }
346
826
  }
@@ -349,5 +829,47 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
349
829
  success: true,
350
830
  created,
351
831
  skipped,
832
+ devMode,
833
+ clientPath: relativeTinybirdDir,
834
+ existingDatafiles:
835
+ existingDatafiles.length > 0 ? existingDatafiles : undefined,
836
+ ciWorkflowCreated,
837
+ cdWorkflowCreated,
838
+ workflowProvider,
352
839
  };
353
840
  }
841
+
842
+ /**
843
+ * Prompt user about including existing datafiles
844
+ */
845
+ async function promptForExistingDatafiles(
846
+ datafiles: string[]
847
+ ): Promise<boolean> {
848
+ const datasourceCount = datafiles.filter((f) =>
849
+ f.endsWith(".datasource")
850
+ ).length;
851
+ const pipeCount = datafiles.filter((f) => f.endsWith(".pipe")).length;
852
+
853
+ const parts: string[] = [];
854
+ if (datasourceCount > 0) {
855
+ parts.push(
856
+ `${datasourceCount} .datasource file${datasourceCount > 1 ? "s" : ""}`
857
+ );
858
+ }
859
+ if (pipeCount > 0) {
860
+ parts.push(`${pipeCount} .pipe file${pipeCount > 1 ? "s" : ""}`);
861
+ }
862
+
863
+ const confirmInclude = await p.confirm({
864
+ message: `Found ${parts.join(
865
+ " and "
866
+ )} in your project. Include them in tinybird.json?`,
867
+ initialValue: true,
868
+ });
869
+
870
+ if (p.isCancel(confirmInclude)) {
871
+ return false;
872
+ }
873
+
874
+ return confirmInclude;
875
+ }