@tinybirdco/sdk 0.0.7 → 0.0.9

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 (56) hide show
  1. package/dist/api/resources.d.ts +2 -0
  2. package/dist/api/resources.d.ts.map +1 -1
  3. package/dist/api/resources.js +1 -0
  4. package/dist/api/resources.js.map +1 -1
  5. package/dist/cli/commands/init.d.ts.map +1 -1
  6. package/dist/cli/commands/init.js +301 -205
  7. package/dist/cli/commands/init.js.map +1 -1
  8. package/dist/cli/commands/init.test.js +67 -93
  9. package/dist/cli/commands/init.test.js.map +1 -1
  10. package/dist/cli/config.d.ts +3 -7
  11. package/dist/cli/config.d.ts.map +1 -1
  12. package/dist/cli/config.js +9 -20
  13. package/dist/cli/config.js.map +1 -1
  14. package/dist/cli/config.test.js +11 -29
  15. package/dist/cli/config.test.js.map +1 -1
  16. package/dist/cli/git.d.ts +5 -0
  17. package/dist/cli/git.d.ts.map +1 -1
  18. package/dist/cli/git.js +15 -0
  19. package/dist/cli/git.js.map +1 -1
  20. package/dist/cli/index.js +42 -25
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/cli/utils/package-manager.d.ts +9 -0
  23. package/dist/cli/utils/package-manager.d.ts.map +1 -1
  24. package/dist/cli/utils/package-manager.js +130 -35
  25. package/dist/cli/utils/package-manager.js.map +1 -1
  26. package/dist/cli/utils/package-manager.test.js +124 -32
  27. package/dist/cli/utils/package-manager.test.js.map +1 -1
  28. package/dist/codegen/index.d.ts +4 -0
  29. package/dist/codegen/index.d.ts.map +1 -1
  30. package/dist/codegen/index.js +96 -0
  31. package/dist/codegen/index.js.map +1 -1
  32. package/dist/codegen/index.test.js +10 -0
  33. package/dist/codegen/index.test.js.map +1 -1
  34. package/dist/generator/datasource.d.ts.map +1 -1
  35. package/dist/generator/datasource.js +20 -0
  36. package/dist/generator/datasource.js.map +1 -1
  37. package/dist/generator/datasource.test.js +11 -0
  38. package/dist/generator/datasource.test.js.map +1 -1
  39. package/dist/schema/datasource.d.ts +5 -0
  40. package/dist/schema/datasource.d.ts.map +1 -1
  41. package/dist/schema/datasource.js.map +1 -1
  42. package/package.json +1 -1
  43. package/src/api/resources.ts +4 -0
  44. package/src/cli/commands/init.test.ts +67 -107
  45. package/src/cli/commands/init.ts +350 -218
  46. package/src/cli/config.test.ts +12 -42
  47. package/src/cli/config.ts +9 -23
  48. package/src/cli/git.ts +15 -0
  49. package/src/cli/index.ts +46 -27
  50. package/src/cli/utils/package-manager.test.ts +165 -33
  51. package/src/cli/utils/package-manager.ts +133 -30
  52. package/src/codegen/index.test.ts +12 -0
  53. package/src/codegen/index.ts +120 -0
  54. package/src/generator/datasource.test.ts +13 -0
  55. package/src/generator/datasource.ts +24 -0
  56. package/src/schema/datasource.ts +5 -0
@@ -5,13 +5,37 @@ import * as fs from "fs";
5
5
  import * as path from "path";
6
6
  import * as p from "@clack/prompts";
7
7
  import pc from "picocolors";
8
- import { hasValidToken, getRelativeTinybirdDir, getConfigPath, updateConfig, } from "../config.js";
8
+ import { hasValidToken, getRelativeTinybirdDir, getConfigPath, updateConfig, loadConfig, } from "../config.js";
9
9
  import { browserLogin } from "../auth.js";
10
10
  import { saveTinybirdToken } from "../env.js";
11
+ import { getGitRoot } from "../git.js";
12
+ import { fetchAllResources } from "../../api/resources.js";
13
+ import { generateCombinedFile } from "../../codegen/index.js";
11
14
  /**
12
- * Default starter content for datasources.ts
15
+ * Default starter content for tinybird.ts (single file with everything)
13
16
  */
14
- const DATASOURCES_CONTENT = `import { defineDatasource, t, engine, type InferRow } from "@tinybirdco/sdk";
17
+ const TINYBIRD_CONTENT = `/**
18
+ * Tinybird Definitions
19
+ *
20
+ * Define your datasources, endpoints, and client here.
21
+ */
22
+
23
+ import {
24
+ defineDatasource,
25
+ defineEndpoint,
26
+ createTinybirdClient,
27
+ node,
28
+ t,
29
+ p,
30
+ engine,
31
+ type InferRow,
32
+ type InferParams,
33
+ type InferOutputRow,
34
+ } from "@tinybirdco/sdk";
35
+
36
+ // ============================================================================
37
+ // Datasources
38
+ // ============================================================================
15
39
 
16
40
  /**
17
41
  * Page views datasource - tracks page view events
@@ -29,13 +53,11 @@ export const pageViews = defineDatasource("page_views", {
29
53
  }),
30
54
  });
31
55
 
32
- // Row type - use this for ingesting data
33
56
  export type PageViewsRow = InferRow<typeof pageViews>;
34
- `;
35
- /**
36
- * Default starter content for endpoints.ts
37
- */
38
- const ENDPOINTS_CONTENT = `import { defineEndpoint, node, t, p, type InferParams, type InferOutputRow } from "@tinybirdco/sdk";
57
+
58
+ // ============================================================================
59
+ // Endpoints
60
+ // ============================================================================
39
61
 
40
62
  /**
41
63
  * Top pages endpoint - get the most visited pages
@@ -69,39 +91,17 @@ export const topPages = defineEndpoint("top_pages", {
69
91
  },
70
92
  });
71
93
 
72
- // Endpoint types - use these for calling the API
73
94
  export type TopPagesParams = InferParams<typeof topPages>;
74
95
  export type TopPagesOutput = InferOutputRow<typeof topPages>;
75
- `;
76
- /**
77
- * Default starter content for client.ts
78
- */
79
- const CLIENT_CONTENT = `/**
80
- * Tinybird Client
81
- *
82
- * This file defines the typed Tinybird client for your project.
83
- * Add your datasources and endpoints here as you create them.
84
- */
85
-
86
- import { createTinybirdClient } from "@tinybirdco/sdk";
87
96
 
88
- // Import datasources and their row types
89
- import { pageViews, type PageViewsRow } from "./datasources";
97
+ // ============================================================================
98
+ // Client
99
+ // ============================================================================
90
100
 
91
- // Import endpoints and their types
92
- import { topPages, type TopPagesParams, type TopPagesOutput } from "./endpoints";
93
-
94
- // Create the typed Tinybird client
95
101
  export const tinybird = createTinybirdClient({
96
102
  datasources: { pageViews },
97
103
  pipes: { topPages },
98
104
  });
99
-
100
- // Re-export types for convenience
101
- export type { PageViewsRow, TopPagesParams, TopPagesOutput };
102
-
103
- // Re-export entities
104
- export { pageViews, topPages };
105
105
  `;
106
106
  const TINYBIRD_CI_WORKFLOW = `name: Tinybird CI
107
107
 
@@ -125,6 +125,7 @@ jobs:
125
125
  node-version: "20"
126
126
  cache: "pnpm"
127
127
  - run: pnpm install --frozen-lockfile
128
+ - run: pnpm run tinybird:build
128
129
  - run: pnpm run tinybird:deploy -- --check
129
130
  env:
130
131
  TINYBIRD_TOKEN: \${{ secrets.TINYBIRD_TOKEN }}
@@ -153,20 +154,59 @@ jobs:
153
154
  node-version: "20"
154
155
  cache: "pnpm"
155
156
  - run: pnpm install --frozen-lockfile
157
+ - run: pnpm run tinybird:build
156
158
  - run: pnpm run tinybird:deploy
157
159
  env:
158
160
  TINYBIRD_TOKEN: \${{ secrets.TINYBIRD_TOKEN }}
159
161
  `;
160
- 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`;
162
+ const TINYBIRD_GITLAB_CI_WORKFLOW = `stages:
163
+ - tinybird
164
+
165
+ tinybird_ci:
166
+ stage: tinybird
167
+ image: node:20
168
+ rules:
169
+ - changes:
170
+ - tinybird.json
171
+ - src/tinybird/**
172
+ - tinybird/**
173
+ - "**/*.datasource"
174
+ - "**/*.pipe"
175
+ script:
176
+ - corepack enable
177
+ - pnpm install --frozen-lockfile
178
+ - pnpm run tinybird:build
179
+ - pnpm run tinybird:deploy -- --check
180
+ variables:
181
+ TINYBIRD_TOKEN: \${TINYBIRD_TOKEN}
182
+ `;
183
+ const TINYBIRD_GITLAB_CD_WORKFLOW = `stages:
184
+ - tinybird
185
+
186
+ tinybird_cd:
187
+ stage: tinybird
188
+ image: node:20
189
+ rules:
190
+ - if: '$CI_COMMIT_BRANCH == "main"'
191
+ changes:
192
+ - tinybird.json
193
+ - src/tinybird/**
194
+ - tinybird/**
195
+ - "**/*.datasource"
196
+ - "**/*.pipe"
197
+ script:
198
+ - corepack enable
199
+ - pnpm install --frozen-lockfile
200
+ - pnpm run tinybird:build
201
+ - pnpm run tinybird:deploy
202
+ variables:
203
+ TINYBIRD_TOKEN: \${TINYBIRD_TOKEN}
204
+ `;
161
205
  /**
162
206
  * Default config content generator
163
207
  */
164
- function createDefaultConfig(tinybirdDir, devMode, additionalIncludes = []) {
165
- const include = [
166
- `${tinybirdDir}/datasources.ts`,
167
- `${tinybirdDir}/endpoints.ts`,
168
- ...additionalIncludes,
169
- ];
208
+ function createDefaultConfig(tinybirdFilePath, devMode, additionalIncludes = []) {
209
+ const include = [tinybirdFilePath, ...additionalIncludes];
170
210
  return {
171
211
  include,
172
212
  token: "${TINYBIRD_TOKEN}",
@@ -244,7 +284,7 @@ export async function runInit(options = {}) {
244
284
  let devMode = options.devMode ?? "branch";
245
285
  if (!options.devMode) {
246
286
  // Show interactive prompt for workflow selection
247
- p.intro(pc.cyan("tinybird init"));
287
+ p.intro(pc.cyan("tinybird.json"));
248
288
  const workflowChoice = await p.select({
249
289
  message: "How do you want to develop with Tinybird?",
250
290
  options: [
@@ -278,7 +318,7 @@ export async function runInit(options = {}) {
278
318
  if (!options.clientPath && !options.devMode) {
279
319
  // Ask user to confirm or change the client path
280
320
  const clientPathChoice = await p.text({
281
- message: "Where should we create Tinybird definitions?",
321
+ message: "Where should we create initial tinybird.ts file?",
282
322
  placeholder: defaultRelativePath,
283
323
  defaultValue: defaultRelativePath,
284
324
  });
@@ -299,54 +339,25 @@ export async function runInit(options = {}) {
299
339
  let includeCiWorkflow = options.includeCiWorkflow ?? false;
300
340
  let includeCdWorkflow = options.includeCdWorkflow ?? false;
301
341
  const shouldPromptWorkflows = !skipWorkflowPrompt && options.includeCiWorkflow === undefined;
302
- const shouldPromptProvider = !skipWorkflowPrompt && !workflowProvider;
303
- if (shouldPromptProvider && shouldPromptWorkflows) {
304
- const providerChoice = await p.select({
305
- message: "Which git provider are you using?",
306
- options: [
307
- { value: "github", label: "GitHub" },
308
- { value: "gitlab", label: "GitLab" },
309
- ],
310
- });
311
- if (p.isCancel(providerChoice)) {
312
- p.cancel("Operation cancelled");
313
- return {
314
- success: false,
315
- created: [],
316
- skipped: [],
317
- error: "Cancelled by user",
318
- };
319
- }
320
- didPrompt = true;
321
- workflowProvider = providerChoice;
322
- }
323
342
  if (shouldPromptWorkflows) {
324
- const confirmWorkflows = await p.confirm({
325
- message: "Create CI/CD workflows for Tinybird?",
326
- initialValue: true,
327
- });
328
- if (p.isCancel(confirmWorkflows)) {
329
- p.cancel("Operation cancelled");
330
- return {
331
- success: false,
332
- created: [],
333
- skipped: [],
334
- error: "Cancelled by user",
335
- };
336
- }
337
- didPrompt = true;
338
- includeCiWorkflow = confirmWorkflows;
339
- includeCdWorkflow = confirmWorkflows;
340
- }
341
- if (shouldPromptProvider && !shouldPromptWorkflows && !workflowProvider) {
342
- const providerChoice = await p.select({
343
- message: "Which git provider are you using?",
343
+ const ciChoice = await p.select({
344
+ message: "Set up CI/CD workflows?",
344
345
  options: [
345
- { value: "github", label: "GitHub" },
346
- { value: "gitlab", label: "GitLab" },
346
+ {
347
+ value: "github",
348
+ label: "GitHub Actions",
349
+ },
350
+ {
351
+ value: "gitlab",
352
+ label: "GitLab CI",
353
+ },
354
+ {
355
+ value: "skip",
356
+ label: "Skip",
357
+ },
347
358
  ],
348
359
  });
349
- if (p.isCancel(providerChoice)) {
360
+ if (p.isCancel(ciChoice)) {
350
361
  p.cancel("Operation cancelled");
351
362
  return {
352
363
  success: false,
@@ -356,44 +367,57 @@ export async function runInit(options = {}) {
356
367
  };
357
368
  }
358
369
  didPrompt = true;
359
- workflowProvider = providerChoice;
370
+ if (ciChoice !== "skip") {
371
+ includeCiWorkflow = true;
372
+ includeCdWorkflow = true;
373
+ workflowProvider = ciChoice;
374
+ }
360
375
  }
361
- if ((includeCiWorkflow || includeCdWorkflow) && !workflowProvider) {
376
+ else if ((includeCiWorkflow || includeCdWorkflow) && !workflowProvider) {
362
377
  workflowProvider = "github";
363
378
  }
364
379
  // Ask about existing datafiles if found
380
+ let datafileAction = "skip";
365
381
  if (foundDatafiles.length > 0 && !options.skipDatafilePrompt) {
366
- const includeDatafiles = options.includeExistingDatafiles ??
367
- (await (async () => {
368
- didPrompt = true;
369
- return promptForExistingDatafiles(foundDatafiles);
370
- })());
371
- if (includeDatafiles) {
382
+ if (options.includeExistingDatafiles !== undefined) {
383
+ datafileAction = options.includeExistingDatafiles ? "include" : "skip";
384
+ }
385
+ else {
386
+ didPrompt = true;
387
+ datafileAction = await promptForExistingDatafiles(foundDatafiles);
388
+ }
389
+ if (datafileAction === "include") {
372
390
  existingDatafiles = foundDatafiles;
373
391
  }
392
+ // Note: "codegen" option is handled after file creation
374
393
  }
375
394
  else if (options.includeExistingDatafiles && foundDatafiles.length > 0) {
376
395
  existingDatafiles = foundDatafiles;
396
+ datafileAction = "include";
377
397
  }
378
398
  if (didPrompt) {
379
399
  const devModeLabel = devMode === "local" ? "Tinybird Local" : "Branches";
380
- const datafileSummary = foundDatafiles.length > 0
381
- ? existingDatafiles.length > 0
382
- ? `${existingDatafiles.length} included`
383
- : "none included"
384
- : "none found";
385
- let workflowSummary = "none";
400
+ let datafileSummary = "none found";
401
+ if (foundDatafiles.length > 0) {
402
+ if (datafileAction === "include") {
403
+ datafileSummary = `${foundDatafiles.length} included`;
404
+ }
405
+ else if (datafileAction === "codegen") {
406
+ datafileSummary = `${foundDatafiles.length} will generate .ts`;
407
+ }
408
+ else {
409
+ datafileSummary = "skipped";
410
+ }
411
+ }
412
+ let cicdSummary = "skipped";
386
413
  if (includeCiWorkflow || includeCdWorkflow) {
387
- workflowSummary =
388
- workflowProvider === "gitlab"
389
- ? "GitLab (.gitlab-ci.yml)"
390
- : "GitHub Actions (tinybird-ci.yaml, tinybird-cd.yaml)";
414
+ cicdSummary = workflowProvider === "gitlab" ? "GitLab" : "GitHub";
391
415
  }
392
416
  const summaryLines = [
393
417
  `Mode: ${devModeLabel}`,
394
- `Definitions: ${relativeTinybirdDir}/`,
418
+ `Folder: ${relativeTinybirdDir}/`,
395
419
  `Existing datafiles: ${datafileSummary}`,
396
- `Workflows: ${workflowSummary}`,
420
+ `CI/CD: ${cicdSummary}`,
397
421
  ];
398
422
  p.note(summaryLines.join("\n"), "Installation Summary");
399
423
  const confirmInit = await p.confirm({
@@ -410,15 +434,28 @@ export async function runInit(options = {}) {
410
434
  };
411
435
  }
412
436
  }
413
- const tinybirdDir = path.join(cwd, relativeTinybirdDir);
414
- // File paths
415
- const datasourcesPath = path.join(tinybirdDir, "datasources.ts");
416
- const endpointsPath = path.join(tinybirdDir, "endpoints.ts");
417
- const clientPath = path.join(tinybirdDir, "client.ts");
437
+ // relativeTinybirdDir is now a file path like "src/lib/tinybird.ts"
438
+ const tinybirdFilePath = path.join(cwd, relativeTinybirdDir);
439
+ const tinybirdDir = path.dirname(tinybirdFilePath);
418
440
  // Create config file (tinybird.json)
419
441
  const configPath = getConfigPath(cwd);
420
442
  if (fs.existsSync(configPath) && !force) {
421
- skipped.push("tinybird.json");
443
+ try {
444
+ const config = createDefaultConfig(relativeTinybirdDir, devMode, existingDatafiles);
445
+ updateConfig(configPath, {
446
+ include: config.include,
447
+ devMode: config.devMode,
448
+ });
449
+ created.push("tinybird.json (updated)");
450
+ }
451
+ catch (error) {
452
+ return {
453
+ success: false,
454
+ created,
455
+ skipped,
456
+ error: `Failed to update tinybird.json: ${error.message}`,
457
+ };
458
+ }
422
459
  }
423
460
  else {
424
461
  try {
@@ -435,7 +472,7 @@ export async function runInit(options = {}) {
435
472
  };
436
473
  }
437
474
  }
438
- // Create tinybird directory
475
+ // Create lib directory
439
476
  try {
440
477
  fs.mkdirSync(tinybirdDir, { recursive: true });
441
478
  }
@@ -444,61 +481,27 @@ export async function runInit(options = {}) {
444
481
  success: false,
445
482
  created,
446
483
  skipped,
447
- error: `Failed to create ${relativeTinybirdDir} folder: ${error.message}`,
484
+ error: `Failed to create ${path.dirname(relativeTinybirdDir)} folder: ${error.message}`,
448
485
  };
449
486
  }
450
- // Create datasources.ts
451
- if (fs.existsSync(datasourcesPath) && !force) {
452
- skipped.push(`${relativeTinybirdDir}/datasources.ts`);
453
- }
454
- else {
455
- try {
456
- fs.writeFileSync(datasourcesPath, DATASOURCES_CONTENT);
457
- created.push(`${relativeTinybirdDir}/datasources.ts`);
458
- }
459
- catch (error) {
460
- return {
461
- success: false,
462
- created,
463
- skipped,
464
- error: `Failed to create datasources.ts: ${error.message}`,
465
- };
466
- }
467
- }
468
- // Create endpoints.ts
469
- if (fs.existsSync(endpointsPath) && !force) {
470
- skipped.push(`${relativeTinybirdDir}/endpoints.ts`);
471
- }
472
- else {
473
- try {
474
- fs.writeFileSync(endpointsPath, ENDPOINTS_CONTENT);
475
- created.push(`${relativeTinybirdDir}/endpoints.ts`);
476
- }
477
- catch (error) {
478
- return {
479
- success: false,
480
- created,
481
- skipped,
482
- error: `Failed to create endpoints.ts: ${error.message}`,
483
- };
484
- }
485
- }
486
- // Create client.ts
487
- if (fs.existsSync(clientPath) && !force) {
488
- skipped.push(`${relativeTinybirdDir}/client.ts`);
489
- }
490
- else {
491
- try {
492
- fs.writeFileSync(clientPath, CLIENT_CONTENT);
493
- created.push(`${relativeTinybirdDir}/client.ts`);
487
+ // Create tinybird.ts (skip if codegen will generate it)
488
+ if (datafileAction !== "codegen") {
489
+ if (fs.existsSync(tinybirdFilePath) && !force) {
490
+ skipped.push(relativeTinybirdDir);
494
491
  }
495
- catch (error) {
496
- return {
497
- success: false,
498
- created,
499
- skipped,
500
- error: `Failed to create client.ts: ${error.message}`,
501
- };
492
+ else {
493
+ try {
494
+ fs.writeFileSync(tinybirdFilePath, TINYBIRD_CONTENT);
495
+ created.push(relativeTinybirdDir);
496
+ }
497
+ catch (error) {
498
+ return {
499
+ success: false,
500
+ created,
501
+ skipped,
502
+ error: `Failed to create ${relativeTinybirdDir}: ${error.message}`,
503
+ };
504
+ }
502
505
  }
503
506
  }
504
507
  // Add scripts to package.json if it exists
@@ -531,55 +534,79 @@ export async function runInit(options = {}) {
531
534
  // Silently ignore package.json errors - not critical
532
535
  }
533
536
  }
534
- const workflowsDir = path.join(cwd, ".github", "workflows");
535
- const ciWorkflowPath = path.join(workflowsDir, "tinybird-ci.yaml");
536
- const cdWorkflowPath = path.join(workflowsDir, "tinybird-cd.yaml");
537
- const gitlabWorkflowPath = path.join(cwd, ".gitlab-ci.yml");
537
+ // Use git root for workflow files, fallback to cwd if not in a git repo
538
+ const projectRoot = getGitRoot() ?? cwd;
539
+ const githubWorkflowsDir = path.join(projectRoot, ".github", "workflows");
540
+ const gitlabWorkflowsDir = path.join(projectRoot, ".gitlab");
541
+ const githubCiPath = path.join(githubWorkflowsDir, "tinybird-ci.yaml");
542
+ const githubCdPath = path.join(githubWorkflowsDir, "tinybird-cd.yaml");
543
+ const gitlabCiPath = path.join(gitlabWorkflowsDir, "tinybird-ci.yaml");
544
+ const gitlabCdPath = path.join(gitlabWorkflowsDir, "tinybird-cd.yaml");
538
545
  if (includeCiWorkflow || includeCdWorkflow) {
539
- if (workflowProvider === "github") {
540
- try {
541
- fs.mkdirSync(workflowsDir, { recursive: true });
546
+ const workflowsDir = workflowProvider === "github" ? githubWorkflowsDir : gitlabWorkflowsDir;
547
+ try {
548
+ fs.mkdirSync(workflowsDir, { recursive: true });
549
+ }
550
+ catch (error) {
551
+ return {
552
+ success: false,
553
+ created,
554
+ skipped,
555
+ error: `Failed to create ${workflowProvider === "github" ? ".github/workflows" : ".gitlab"} folder: ${error.message}`,
556
+ };
557
+ }
558
+ }
559
+ if (workflowProvider === "github") {
560
+ if (includeCiWorkflow) {
561
+ if (fs.existsSync(githubCiPath) && !force) {
562
+ skipped.push(".github/workflows/tinybird-ci.yaml");
542
563
  }
543
- catch (error) {
544
- return {
545
- success: false,
546
- created,
547
- skipped,
548
- error: `Failed to create .github/workflows folder: ${error.message}`,
549
- };
564
+ else {
565
+ try {
566
+ fs.writeFileSync(githubCiPath, TINYBIRD_CI_WORKFLOW);
567
+ created.push(".github/workflows/tinybird-ci.yaml");
568
+ ciWorkflowCreated = true;
569
+ }
570
+ catch (error) {
571
+ return {
572
+ success: false,
573
+ created,
574
+ skipped,
575
+ error: `Failed to create .github/workflows/tinybird-ci.yaml: ${error.message}`,
576
+ };
577
+ }
550
578
  }
551
579
  }
552
- if (workflowProvider === "gitlab") {
553
- if (fs.existsSync(gitlabWorkflowPath) && !force) {
554
- skipped.push(".gitlab-ci.yml");
580
+ if (includeCdWorkflow) {
581
+ if (fs.existsSync(githubCdPath) && !force) {
582
+ skipped.push(".github/workflows/tinybird-cd.yaml");
555
583
  }
556
584
  else {
557
585
  try {
558
- fs.writeFileSync(gitlabWorkflowPath, TINYBIRD_GITLAB_CI);
559
- created.push(".gitlab-ci.yml");
560
- ciWorkflowCreated = includeCiWorkflow;
561
- cdWorkflowCreated = includeCdWorkflow;
586
+ fs.writeFileSync(githubCdPath, TINYBIRD_CD_WORKFLOW);
587
+ created.push(".github/workflows/tinybird-cd.yaml");
588
+ cdWorkflowCreated = true;
562
589
  }
563
590
  catch (error) {
564
591
  return {
565
592
  success: false,
566
593
  created,
567
594
  skipped,
568
- error: `Failed to create .gitlab-ci.yml: ${error.message}`,
595
+ error: `Failed to create .github/workflows/tinybird-cd.yaml: ${error.message}`,
569
596
  };
570
597
  }
571
598
  }
572
599
  }
573
600
  }
574
- if (workflowProvider === "github") {
601
+ if (workflowProvider === "gitlab") {
575
602
  if (includeCiWorkflow) {
576
- if (fs.existsSync(ciWorkflowPath) && !force) {
577
- skipped.push(".github/workflows/tinybird-ci.yaml");
603
+ if (fs.existsSync(gitlabCiPath) && !force) {
604
+ skipped.push(".gitlab/tinybird-ci.yaml");
578
605
  }
579
606
  else {
580
607
  try {
581
- fs.writeFileSync(ciWorkflowPath, TINYBIRD_CI_WORKFLOW);
582
- created.push(".github/workflows/tinybird-ci.yaml");
608
+ fs.writeFileSync(gitlabCiPath, TINYBIRD_GITLAB_CI_WORKFLOW);
609
+ created.push(".gitlab/tinybird-ci.yaml");
583
610
  ciWorkflowCreated = true;
584
611
  }
585
612
  catch (error) {
@@ -587,19 +614,19 @@ export async function runInit(options = {}) {
587
614
  success: false,
588
615
  created,
589
616
  skipped,
590
- error: `Failed to create tinybird-ci.yaml: ${error.message}`,
617
+ error: `Failed to create .gitlab/tinybird-ci.yaml: ${error.message}`,
591
618
  };
592
619
  }
593
620
  }
594
621
  }
595
622
  if (includeCdWorkflow) {
596
- if (fs.existsSync(cdWorkflowPath) && !force) {
597
- skipped.push(".github/workflows/tinybird-cd.yaml");
623
+ if (fs.existsSync(gitlabCdPath) && !force) {
624
+ skipped.push(".gitlab/tinybird-cd.yaml");
598
625
  }
599
626
  else {
600
627
  try {
601
- fs.writeFileSync(cdWorkflowPath, TINYBIRD_CD_WORKFLOW);
602
- created.push(".github/workflows/tinybird-cd.yaml");
628
+ fs.writeFileSync(gitlabCdPath, TINYBIRD_GITLAB_CD_WORKFLOW);
629
+ created.push(".gitlab/tinybird-cd.yaml");
603
630
  cdWorkflowCreated = true;
604
631
  }
605
632
  catch (error) {
@@ -607,7 +634,7 @@ export async function runInit(options = {}) {
607
634
  success: false,
608
635
  created,
609
636
  skipped,
610
- error: `Failed to create tinybird-cd.yaml: ${error.message}`,
637
+ error: `Failed to create .gitlab/tinybird-cd.yaml: ${error.message}`,
611
638
  };
612
639
  }
613
640
  }
@@ -629,6 +656,11 @@ export async function runInit(options = {}) {
629
656
  if (baseUrl !== "https://api.tinybird.co") {
630
657
  updateConfig(configPath, { baseUrl });
631
658
  }
659
+ // Generate TypeScript from existing Tinybird resources if requested
660
+ if (datafileAction === "codegen") {
661
+ const tinybirdDir = path.join(cwd, relativeTinybirdDir);
662
+ await runCodegen(baseUrl, authResult.token, tinybirdDir, relativeTinybirdDir, created, foundDatafiles);
663
+ }
632
664
  return {
633
665
  success: true,
634
666
  created,
@@ -677,6 +709,21 @@ export async function runInit(options = {}) {
677
709
  };
678
710
  }
679
711
  }
712
+ // Generate TypeScript from existing Tinybird resources if requested
713
+ // (when user is already logged in)
714
+ if (datafileAction === "codegen" && hasValidToken(cwd)) {
715
+ try {
716
+ const config = loadConfig(cwd);
717
+ const tinybirdDir = path.join(cwd, relativeTinybirdDir);
718
+ await runCodegen(config.baseUrl, config.token, tinybirdDir, relativeTinybirdDir, created, foundDatafiles);
719
+ }
720
+ catch (error) {
721
+ console.error(`Warning: Failed to generate TypeScript: ${error.message}`);
722
+ }
723
+ }
724
+ if (didPrompt) {
725
+ p.outro("Done!");
726
+ }
680
727
  return {
681
728
  success: true,
682
729
  created,
@@ -689,6 +736,39 @@ export async function runInit(options = {}) {
689
736
  workflowProvider,
690
737
  };
691
738
  }
739
+ /**
740
+ * Generate TypeScript files from Tinybird workspace resources
741
+ * Only generates for resources that match the local datafiles
742
+ */
743
+ async function runCodegen(baseUrl, token, tinybirdFilePath, relativeTinybirdPath, created, localDatafiles) {
744
+ try {
745
+ // Extract names from local datafiles (without extension)
746
+ const localDatasourceNames = new Set(localDatafiles
747
+ .filter((f) => f.endsWith(".datasource"))
748
+ .map((f) => path.basename(f, ".datasource")));
749
+ const localPipeNames = new Set(localDatafiles
750
+ .filter((f) => f.endsWith(".pipe"))
751
+ .map((f) => path.basename(f, ".pipe")));
752
+ const resources = await fetchAllResources({ baseUrl, token });
753
+ // Filter to only resources matching local files
754
+ const matchedDatasources = resources.datasources.filter((ds) => localDatasourceNames.has(ds.name));
755
+ const matchedPipes = resources.pipes.filter((p) => localPipeNames.has(p.name));
756
+ if (matchedDatasources.length > 0 || matchedPipes.length > 0) {
757
+ // Generate combined tinybird.ts file
758
+ const content = generateCombinedFile(matchedDatasources, matchedPipes);
759
+ // Ensure directory exists
760
+ const dir = path.dirname(tinybirdFilePath);
761
+ if (!fs.existsSync(dir)) {
762
+ fs.mkdirSync(dir, { recursive: true });
763
+ }
764
+ fs.writeFileSync(tinybirdFilePath, content);
765
+ created.push(`${relativeTinybirdPath} (generated)`);
766
+ }
767
+ }
768
+ catch (error) {
769
+ console.error(`Warning: Failed to generate TypeScript from workspace: ${error.message}`);
770
+ }
771
+ }
692
772
  /**
693
773
  * Prompt user about including existing datafiles
694
774
  */
@@ -702,13 +782,29 @@ async function promptForExistingDatafiles(datafiles) {
702
782
  if (pipeCount > 0) {
703
783
  parts.push(`${pipeCount} .pipe file${pipeCount > 1 ? "s" : ""}`);
704
784
  }
705
- const confirmInclude = await p.confirm({
706
- message: `Found ${parts.join(" and ")} in your project. Include them in tinybird.json?`,
707
- initialValue: true,
785
+ const choice = await p.select({
786
+ message: `Found ${parts.join(" and ")} in your project.`,
787
+ options: [
788
+ {
789
+ value: "include",
790
+ label: "Include existing resources",
791
+ hint: "Add to tinybird.json",
792
+ },
793
+ {
794
+ value: "codegen",
795
+ label: "Define resources in TypeScript",
796
+ hint: "Generate TypeScript definitions from existing resources",
797
+ },
798
+ {
799
+ value: "skip",
800
+ label: "Skip",
801
+ hint: "Don't include existing resources",
802
+ },
803
+ ],
708
804
  });
709
- if (p.isCancel(confirmInclude)) {
710
- return false;
805
+ if (p.isCancel(choice)) {
806
+ return "skip";
711
807
  }
712
- return confirmInclude;
808
+ return choice;
713
809
  }
714
810
  //# sourceMappingURL=init.js.map