@tinybirdco/sdk 0.0.30 → 0.0.32

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 (80) hide show
  1. package/README.md +29 -4
  2. package/dist/cli/auth.d.ts.map +1 -1
  3. package/dist/cli/auth.js +1 -0
  4. package/dist/cli/auth.js.map +1 -1
  5. package/dist/cli/commands/branch.js +5 -5
  6. package/dist/cli/commands/branch.js.map +1 -1
  7. package/dist/cli/commands/build.d.ts.map +1 -1
  8. package/dist/cli/commands/build.js +2 -2
  9. package/dist/cli/commands/build.js.map +1 -1
  10. package/dist/cli/commands/build.test.d.ts +2 -0
  11. package/dist/cli/commands/build.test.d.ts.map +1 -0
  12. package/dist/cli/commands/build.test.js +266 -0
  13. package/dist/cli/commands/build.test.js.map +1 -0
  14. package/dist/cli/commands/clear.d.ts.map +1 -1
  15. package/dist/cli/commands/clear.js +2 -2
  16. package/dist/cli/commands/clear.js.map +1 -1
  17. package/dist/cli/commands/deploy.js +2 -2
  18. package/dist/cli/commands/deploy.js.map +1 -1
  19. package/dist/cli/commands/dev.js +12 -12
  20. package/dist/cli/commands/dev.js.map +1 -1
  21. package/dist/cli/commands/info.js +2 -2
  22. package/dist/cli/commands/info.js.map +1 -1
  23. package/dist/cli/commands/info.test.js +11 -13
  24. package/dist/cli/commands/info.test.js.map +1 -1
  25. package/dist/cli/commands/init.d.ts.map +1 -1
  26. package/dist/cli/commands/init.js +44 -26
  27. package/dist/cli/commands/init.js.map +1 -1
  28. package/dist/cli/commands/init.test.js +44 -25
  29. package/dist/cli/commands/init.test.js.map +1 -1
  30. package/dist/cli/commands/login.d.ts.map +1 -1
  31. package/dist/cli/commands/login.js +7 -6
  32. package/dist/cli/commands/login.js.map +1 -1
  33. package/dist/cli/commands/login.test.js +1 -1
  34. package/dist/cli/commands/login.test.js.map +1 -1
  35. package/dist/cli/commands/preview.d.ts.map +1 -1
  36. package/dist/cli/commands/preview.js +2 -2
  37. package/dist/cli/commands/preview.js.map +1 -1
  38. package/dist/cli/config-loader.d.ts +18 -0
  39. package/dist/cli/config-loader.d.ts.map +1 -0
  40. package/dist/cli/config-loader.js +57 -0
  41. package/dist/cli/config-loader.js.map +1 -0
  42. package/dist/cli/config-types.d.ts +28 -0
  43. package/dist/cli/config-types.d.ts.map +1 -0
  44. package/dist/cli/config-types.js +8 -0
  45. package/dist/cli/config-types.js.map +1 -0
  46. package/dist/cli/config.d.ts +63 -29
  47. package/dist/cli/config.d.ts.map +1 -1
  48. package/dist/cli/config.js +139 -43
  49. package/dist/cli/config.js.map +1 -1
  50. package/dist/cli/config.test.js +73 -9
  51. package/dist/cli/config.test.js.map +1 -1
  52. package/dist/cli/index.js +4 -0
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/client/base.d.ts.map +1 -1
  55. package/dist/client/base.js +21 -9
  56. package/dist/client/base.js.map +1 -1
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/package.json +1 -1
  60. package/src/cli/auth.ts +1 -0
  61. package/src/cli/commands/branch.ts +5 -5
  62. package/src/cli/commands/build.test.ts +310 -0
  63. package/src/cli/commands/build.ts +2 -2
  64. package/src/cli/commands/clear.ts +2 -2
  65. package/src/cli/commands/deploy.ts +2 -2
  66. package/src/cli/commands/dev.ts +12 -12
  67. package/src/cli/commands/info.test.ts +11 -13
  68. package/src/cli/commands/info.ts +2 -2
  69. package/src/cli/commands/init.test.ts +53 -37
  70. package/src/cli/commands/init.ts +49 -30
  71. package/src/cli/commands/login.test.ts +1 -1
  72. package/src/cli/commands/login.ts +7 -6
  73. package/src/cli/commands/preview.ts +2 -2
  74. package/src/cli/config-loader.ts +87 -0
  75. package/src/cli/config-types.ts +29 -0
  76. package/src/cli/config.test.ts +95 -8
  77. package/src/cli/config.ts +179 -70
  78. package/src/cli/index.ts +3 -0
  79. package/src/client/base.ts +33 -16
  80. package/src/index.ts +4 -0
@@ -2,7 +2,7 @@
2
2
  * Clear command - clears a local workspace or branch by deleting and recreating it
3
3
  */
4
4
 
5
- import { loadConfig, type ResolvedConfig, type DevMode } from "../config.js";
5
+ import { loadConfigAsync, type ResolvedConfig, type DevMode } from "../config.js";
6
6
  import {
7
7
  getLocalTokens,
8
8
  clearLocalWorkspace,
@@ -60,7 +60,7 @@ export async function runClear(
60
60
 
61
61
  let config: ResolvedConfig;
62
62
  try {
63
- config = loadConfig(cwd);
63
+ config = await loadConfigAsync(cwd);
64
64
  } catch (error) {
65
65
  return {
66
66
  success: false,
@@ -2,7 +2,7 @@
2
2
  * Deploy command - deploys resources to main Tinybird workspace
3
3
  */
4
4
 
5
- import { loadConfig, type ResolvedConfig } from "../config.js";
5
+ import { loadConfigAsync, type ResolvedConfig } from "../config.js";
6
6
  import { buildFromInclude, type BuildFromIncludeResult } from "../../generator/index.js";
7
7
  import { deployToMain, type DeployCallbacks } from "../../api/deploy.js";
8
8
  import type { BuildApiResult } from "../../api/build.js";
@@ -52,7 +52,7 @@ export async function runDeploy(options: DeployCommandOptions = {}): Promise<Dep
52
52
  // Load config
53
53
  let config: ResolvedConfig;
54
54
  try {
55
- config = loadConfig(cwd);
55
+ config = await loadConfigAsync(cwd);
56
56
  } catch (error) {
57
57
  return {
58
58
  success: false,
@@ -5,7 +5,7 @@
5
5
  import * as path from "path";
6
6
  import { watch } from "chokidar";
7
7
  import {
8
- loadConfig,
8
+ loadConfigAsync,
9
9
  configExists,
10
10
  findConfigFile,
11
11
  hasValidToken,
@@ -121,14 +121,14 @@ export async function runDev(
121
121
  // Check if project is initialized
122
122
  if (!configExists(cwd)) {
123
123
  throw new Error(
124
- "No tinybird.json found. Run 'npx tinybird init' to initialize a project."
124
+ "No tinybird config found. Run 'npx tinybird init' to initialize a project."
125
125
  );
126
126
  }
127
127
 
128
128
  // Load config first to determine devMode
129
129
  let config: ResolvedConfig;
130
130
  try {
131
- config = loadConfig(cwd);
131
+ config = await loadConfigAsync(cwd);
132
132
  } catch (error) {
133
133
  throw error;
134
134
  }
@@ -150,18 +150,18 @@ export async function runDev(
150
150
  }
151
151
 
152
152
  // Find the config file (may be in parent directory)
153
- const configPath = findConfigFile(cwd);
154
- if (!configPath) {
155
- throw new Error("No tinybird.json found. Run 'npx tinybird init' first.");
153
+ const configResult = findConfigFile(cwd);
154
+ if (!configResult) {
155
+ throw new Error("No tinybird config found. Run 'npx tinybird init' first.");
156
156
  }
157
157
 
158
- // Save token to .env.local (in same directory as tinybird.json)
159
- const configDir = path.dirname(configPath);
158
+ // Save token to .env.local (in same directory as config file)
159
+ const configDir = path.dirname(configResult.path);
160
160
  saveTinybirdToken(configDir, authResult.token);
161
161
 
162
- // Update baseUrl in tinybird.json if it changed
163
- if (authResult.baseUrl) {
164
- updateConfig(configPath, {
162
+ // Update baseUrl in config file if it changed (only for JSON configs)
163
+ if (authResult.baseUrl && configResult.path.endsWith(".json")) {
164
+ updateConfig(configResult.path, {
165
165
  baseUrl: authResult.baseUrl,
166
166
  });
167
167
  }
@@ -175,7 +175,7 @@ export async function runDev(
175
175
  });
176
176
 
177
177
  // Reload config after login
178
- config = loadConfig(cwd);
178
+ config = await loadConfigAsync(cwd);
179
179
  }
180
180
 
181
181
  // Determine effective token and branch info based on devMode
@@ -3,7 +3,7 @@ import { runInfo } from "./info.js";
3
3
 
4
4
  // Mock the config module
5
5
  vi.mock("../config.js", () => ({
6
- loadConfig: vi.fn(),
6
+ loadConfigAsync: vi.fn(),
7
7
  LOCAL_BASE_URL: "http://localhost:7181",
8
8
  }));
9
9
 
@@ -31,7 +31,7 @@ vi.mock("../../api/local.js", () => ({
31
31
  }));
32
32
 
33
33
  // Import mocked functions
34
- import { loadConfig } from "../config.js";
34
+ import { loadConfigAsync } from "../config.js";
35
35
  import { getWorkspace } from "../../api/workspaces.js";
36
36
  import { listBranches, getBranch } from "../../api/branches.js";
37
37
  import { getDashboardUrl, getBranchDashboardUrl, getLocalDashboardUrl } from "../../api/dashboard.js";
@@ -42,7 +42,7 @@ import {
42
42
  getLocalWorkspaceName,
43
43
  } from "../../api/local.js";
44
44
 
45
- const mockedLoadConfig = vi.mocked(loadConfig);
45
+ const mockedLoadConfigAsync = vi.mocked(loadConfigAsync);
46
46
  const mockedGetWorkspace = vi.mocked(getWorkspace);
47
47
  const mockedListBranches = vi.mocked(listBranches);
48
48
  const mockedGetBranch = vi.mocked(getBranch);
@@ -61,9 +61,7 @@ describe("Info Command", () => {
61
61
 
62
62
  describe("config loading", () => {
63
63
  it("returns error when config loading fails", async () => {
64
- mockedLoadConfig.mockImplementation(() => {
65
- throw new Error("No tinybird.json found");
66
- });
64
+ mockedLoadConfigAsync.mockRejectedValue(new Error("No tinybird.json found"));
67
65
 
68
66
  const result = await runInfo();
69
67
 
@@ -95,7 +93,7 @@ describe("Info Command", () => {
95
93
  };
96
94
 
97
95
  beforeEach(() => {
98
- mockedLoadConfig.mockReturnValue(mockConfig);
96
+ mockedLoadConfigAsync.mockResolvedValue(mockConfig);
99
97
  mockedGetWorkspace.mockResolvedValue(mockWorkspace);
100
98
  mockedListBranches.mockResolvedValue([]);
101
99
  mockedGetDashboardUrl.mockReturnValue("https://cloud.tinybird.co/gcp/europe-west3/test-workspace");
@@ -161,7 +159,7 @@ describe("Info Command", () => {
161
159
  });
162
160
 
163
161
  it("does not return branch info when on main branch", async () => {
164
- mockedLoadConfig.mockReturnValue({
162
+ mockedLoadConfigAsync.mockResolvedValue({
165
163
  ...mockConfig,
166
164
  gitBranch: "main",
167
165
  tinybirdBranch: null,
@@ -212,7 +210,7 @@ describe("Info Command", () => {
212
210
  };
213
211
 
214
212
  beforeEach(() => {
215
- mockedLoadConfig.mockReturnValue(mockConfig);
213
+ mockedLoadConfigAsync.mockResolvedValue(mockConfig);
216
214
  mockedGetWorkspace.mockResolvedValue(mockWorkspace);
217
215
  mockedGetDashboardUrl.mockReturnValue("https://cloud.tinybird.co/gcp/europe-west3/test-workspace");
218
216
  });
@@ -285,7 +283,7 @@ describe("Info Command", () => {
285
283
 
286
284
  describe("error handling", () => {
287
285
  it("returns error when workspace fetch fails", async () => {
288
- mockedLoadConfig.mockReturnValue({
286
+ mockedLoadConfigAsync.mockResolvedValue({
289
287
  cwd: "/test",
290
288
  configPath: "/test/tinybird.json",
291
289
  devMode: "branch" as const,
@@ -306,7 +304,7 @@ describe("Info Command", () => {
306
304
  });
307
305
 
308
306
  it("handles branch fetch error gracefully", async () => {
309
- mockedLoadConfig.mockReturnValue({
307
+ mockedLoadConfigAsync.mockResolvedValue({
310
308
  cwd: "/test",
311
309
  configPath: "/test/tinybird.json",
312
310
  devMode: "branch" as const,
@@ -336,7 +334,7 @@ describe("Info Command", () => {
336
334
  });
337
335
 
338
336
  it("handles branches list fetch error gracefully", async () => {
339
- mockedLoadConfig.mockReturnValue({
337
+ mockedLoadConfigAsync.mockResolvedValue({
340
338
  cwd: "/test",
341
339
  configPath: "/test/tinybird.json",
342
340
  devMode: "branch" as const,
@@ -365,7 +363,7 @@ describe("Info Command", () => {
365
363
  });
366
364
 
367
365
  it("handles local workspace fetch error gracefully", async () => {
368
- mockedLoadConfig.mockReturnValue({
366
+ mockedLoadConfigAsync.mockResolvedValue({
369
367
  cwd: "/test",
370
368
  configPath: "/test/tinybird.json",
371
369
  devMode: "local" as const,
@@ -2,7 +2,7 @@
2
2
  * Info command - shows information about the current project and workspace
3
3
  */
4
4
 
5
- import { loadConfig, LOCAL_BASE_URL, type ResolvedConfig } from "../config.js";
5
+ import { loadConfigAsync, LOCAL_BASE_URL, type ResolvedConfig } from "../config.js";
6
6
  import { getWorkspace, type TinybirdWorkspace } from "../../api/workspaces.js";
7
7
  import { listBranches, getBranch, type TinybirdBranch } from "../../api/branches.js";
8
8
  import { getDashboardUrl, getBranchDashboardUrl, getLocalDashboardUrl } from "../../api/dashboard.js";
@@ -125,7 +125,7 @@ export async function runInfo(
125
125
  // Load config
126
126
  let config: ResolvedConfig;
127
127
  try {
128
- config = loadConfig(cwd);
128
+ config = await loadConfigAsync(cwd);
129
129
  } catch (error) {
130
130
  return {
131
131
  success: false,
@@ -46,45 +46,45 @@ describe("Init Command", () => {
46
46
  ).toBe(true);
47
47
  });
48
48
 
49
- it("creates tinybird.json with correct include path for lib/tinybird.ts", async () => {
49
+ it("creates tinybird.config.json with correct include path for lib/tinybird.ts", async () => {
50
50
  const result = await runInit({ cwd: tempDir, skipLogin: true, devMode: "branch", clientPath: "lib/tinybird.ts" });
51
51
 
52
52
  expect(result.success).toBe(true);
53
- expect(result.created).toContain("tinybird.json");
53
+ expect(result.created).toContain("tinybird.config.json");
54
54
 
55
55
  const config = JSON.parse(
56
- fs.readFileSync(path.join(tempDir, "tinybird.json"), "utf-8")
56
+ fs.readFileSync(path.join(tempDir, "tinybird.config.json"), "utf-8")
57
57
  );
58
- expect(config.include).toEqual(["lib/tinybird.ts"]);
58
+ expect(config.include).toContain("lib/tinybird.ts");
59
+ expect(config.token).toBe("${TINYBIRD_TOKEN}");
59
60
  });
60
61
 
61
- it("creates tinybird.json with correct include path for src/lib/tinybird.ts", async () => {
62
+ it("creates tinybird.config.json with correct include path for src/lib/tinybird.ts", async () => {
62
63
  fs.mkdirSync(path.join(tempDir, "src"));
63
64
 
64
65
  const result = await runInit({ cwd: tempDir, skipLogin: true, devMode: "branch", clientPath: "src/lib/tinybird.ts" });
65
66
 
66
67
  expect(result.success).toBe(true);
67
68
 
68
- const config = JSON.parse(
69
- fs.readFileSync(path.join(tempDir, "tinybird.json"), "utf-8")
70
- );
71
- expect(config.include).toEqual(["src/lib/tinybird.ts"]);
69
+ const content = fs.readFileSync(path.join(tempDir, "tinybird.config.json"), "utf-8");
70
+ expect(content).toContain('"src/lib/tinybird.ts"');
72
71
  });
73
72
  });
74
73
 
75
74
  describe("config file creation", () => {
76
- it("creates tinybird.json with default values", async () => {
75
+ it("creates tinybird.config.json with default values", async () => {
77
76
  await runInit({ cwd: tempDir, skipLogin: true, devMode: "branch", clientPath: "lib/tinybird.ts" });
78
77
 
79
78
  const config = JSON.parse(
80
- fs.readFileSync(path.join(tempDir, "tinybird.json"), "utf-8")
79
+ fs.readFileSync(path.join(tempDir, "tinybird.config.json"), "utf-8")
81
80
  );
82
81
 
83
82
  expect(config.token).toBe("${TINYBIRD_TOKEN}");
84
83
  expect(config.baseUrl).toBe("https://api.tinybird.co");
84
+ expect(config.devMode).toBe("branch");
85
85
  });
86
86
 
87
- it("updates tinybird.json if it already exists", async () => {
87
+ it("updates legacy tinybird.json if it already exists", async () => {
88
88
  const existingConfig = {
89
89
  include: ["custom.ts"],
90
90
  token: "existing",
@@ -109,22 +109,46 @@ describe("Init Command", () => {
109
109
  expect(config.token).toBe("existing");
110
110
  });
111
111
 
112
- it("overwrites tinybird.json with force option", async () => {
113
- const existingConfig = { include: ["custom.ts"], token: "existing" };
112
+ it("updates tinybird.config.json if it already exists", async () => {
113
+ const existingConfig = {
114
+ include: ["custom.ts"],
115
+ token: "existing",
116
+ devMode: "local",
117
+ };
114
118
  fs.writeFileSync(
115
- path.join(tempDir, "tinybird.json"),
119
+ path.join(tempDir, "tinybird.config.json"),
116
120
  JSON.stringify(existingConfig)
117
121
  );
118
122
 
119
- const result = await runInit({ cwd: tempDir, skipLogin: true, force: true, devMode: "branch", clientPath: "lib/tinybird.ts" });
123
+ const result = await runInit({ cwd: tempDir, skipLogin: true, devMode: "branch", clientPath: "lib/tinybird.ts" });
120
124
 
121
125
  expect(result.success).toBe(true);
122
- expect(result.created).toContain("tinybird.json");
126
+ expect(result.created).toContain("tinybird.config.json (updated)");
123
127
 
128
+ // Verify include/devMode updated but token preserved
124
129
  const config = JSON.parse(
125
- fs.readFileSync(path.join(tempDir, "tinybird.json"), "utf-8")
130
+ fs.readFileSync(path.join(tempDir, "tinybird.config.json"), "utf-8")
126
131
  );
127
132
  expect(config.include).toEqual(["lib/tinybird.ts"]);
133
+ expect(config.devMode).toBe("branch");
134
+ expect(config.token).toBe("existing");
135
+ });
136
+
137
+ it("overwrites existing config with force option", async () => {
138
+ const existingConfig = { include: ["custom.ts"], token: "existing" };
139
+ fs.writeFileSync(
140
+ path.join(tempDir, "tinybird.json"),
141
+ JSON.stringify(existingConfig)
142
+ );
143
+
144
+ const result = await runInit({ cwd: tempDir, skipLogin: true, force: true, devMode: "branch", clientPath: "lib/tinybird.ts" });
145
+
146
+ expect(result.success).toBe(true);
147
+ // With force, it creates a new tinybird.config.json
148
+ expect(result.created).toContain("tinybird.config.json");
149
+
150
+ const content = fs.readFileSync(path.join(tempDir, "tinybird.config.json"), "utf-8");
151
+ expect(content).toContain('"lib/tinybird.ts"');
128
152
  });
129
153
  });
130
154
 
@@ -417,11 +441,9 @@ describe("Init Command", () => {
417
441
  expect(result.existingDatafiles).toContain("datasources/events.datasource");
418
442
  expect(result.existingDatafiles).toContain("pipes/top_events.pipe");
419
443
 
420
- const config = JSON.parse(
421
- fs.readFileSync(path.join(tempDir, "tinybird.json"), "utf-8")
422
- );
423
- expect(config.include).toContain("datasources/events.datasource");
424
- expect(config.include).toContain("pipes/top_events.pipe");
444
+ const content = fs.readFileSync(path.join(tempDir, "tinybird.config.json"), "utf-8");
445
+ expect(content).toContain('"datasources/events.datasource"');
446
+ expect(content).toContain('"pipes/top_events.pipe"');
425
447
  });
426
448
 
427
449
  it("does not include existing datafiles when user opts out", async () => {
@@ -441,11 +463,9 @@ describe("Init Command", () => {
441
463
  expect(result.success).toBe(true);
442
464
  expect(result.existingDatafiles).toBeUndefined();
443
465
 
444
- const config = JSON.parse(
445
- fs.readFileSync(path.join(tempDir, "tinybird.json"), "utf-8")
446
- );
447
- expect(config.include).not.toContain("datasources/events.datasource");
448
- expect(config.include).toEqual(["lib/tinybird.ts"]);
466
+ const content = fs.readFileSync(path.join(tempDir, "tinybird.config.json"), "utf-8");
467
+ expect(content).not.toContain("datasources/events.datasource");
468
+ expect(content).toContain('"lib/tinybird.ts"');
449
469
  });
450
470
 
451
471
  it("preserves TypeScript include paths alongside datafiles", async () => {
@@ -464,12 +484,10 @@ describe("Init Command", () => {
464
484
 
465
485
  expect(result.success).toBe(true);
466
486
 
467
- const config = JSON.parse(
468
- fs.readFileSync(path.join(tempDir, "tinybird.json"), "utf-8")
469
- );
487
+ const content = fs.readFileSync(path.join(tempDir, "tinybird.config.json"), "utf-8");
470
488
  // Should have both TypeScript file AND datafiles
471
- expect(config.include).toContain("lib/tinybird.ts");
472
- expect(config.include).toContain("datasources/events.datasource");
489
+ expect(content).toContain('"lib/tinybird.ts"');
490
+ expect(content).toContain('"datasources/events.datasource"');
473
491
  });
474
492
 
475
493
  it("handles projects with no existing datafiles", async () => {
@@ -485,10 +503,8 @@ describe("Init Command", () => {
485
503
  expect(result.success).toBe(true);
486
504
  expect(result.existingDatafiles).toBeUndefined();
487
505
 
488
- const config = JSON.parse(
489
- fs.readFileSync(path.join(tempDir, "tinybird.json"), "utf-8")
490
- );
491
- expect(config.include).toEqual(["lib/tinybird.ts"]);
506
+ const content = fs.readFileSync(path.join(tempDir, "tinybird.config.json"), "utf-8");
507
+ expect(content).toContain('"lib/tinybird.ts"');
492
508
  });
493
509
  });
494
510
  });
@@ -10,8 +10,9 @@ import {
10
10
  hasValidToken,
11
11
  getRelativeTinybirdDir,
12
12
  getConfigPath,
13
+ findExistingConfigPath,
13
14
  updateConfig,
14
- loadConfig,
15
+ loadConfigAsync,
15
16
  type DevMode,
16
17
  } from "../config.js";
17
18
  import { browserLogin } from "../auth.js";
@@ -130,6 +131,7 @@ function generateGithubCiWorkflow(workingDirectory?: string): string {
130
131
  on:
131
132
  pull_request:
132
133
  paths:
134
+ - "${pathPrefix}tinybird.config.*"
133
135
  - "${pathPrefix}tinybird.json"
134
136
  - "${pathPrefix}**/*.ts"
135
137
 
@@ -176,6 +178,7 @@ on:
176
178
  branches:
177
179
  - main
178
180
  paths:
181
+ - "${pathPrefix}tinybird.config.*"
179
182
  - "${pathPrefix}tinybird.json"
180
183
  - "${pathPrefix}**/*.ts"
181
184
 
@@ -218,6 +221,7 @@ tinybird_ci:
218
221
  image: node:22
219
222
  rules:
220
223
  - changes:
224
+ - ${pathPrefix}tinybird.config.*
221
225
  - ${pathPrefix}tinybird.json
222
226
  - ${pathPrefix}**/*.ts
223
227
  script:
@@ -246,6 +250,7 @@ tinybird_cd:
246
250
  rules:
247
251
  - if: '$CI_COMMIT_BRANCH == "main"'
248
252
  changes:
253
+ - ${pathPrefix}tinybird.config.*
249
254
  - ${pathPrefix}tinybird.json
250
255
  - ${pathPrefix}**/*.ts
251
256
  script:
@@ -258,7 +263,7 @@ tinybird_cd:
258
263
  }
259
264
 
260
265
  /**
261
- * Default config content generator
266
+ * Default config content generator (for JSON files)
262
267
  */
263
268
  function createDefaultConfig(
264
269
  tinybirdFilePath: string,
@@ -419,7 +424,7 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
419
424
 
420
425
  if (!options.devMode) {
421
426
  // Show interactive prompt for workflow selection
422
- p.intro(pc.cyan("tinybird.json"));
427
+ p.intro(pc.cyan("tinybird.config.json"));
423
428
 
424
429
  const workflowChoice = await p.select({
425
430
  message: "How do you want to develop with Tinybird?",
@@ -589,43 +594,54 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
589
594
  const tinybirdFilePath = path.join(cwd, relativeTinybirdDir);
590
595
  const tinybirdDir = path.dirname(tinybirdFilePath);
591
596
 
592
- // Create config file (tinybird.json)
593
- const configPath = getConfigPath(cwd);
594
- if (fs.existsSync(configPath) && !force) {
595
- try {
596
- const config = createDefaultConfig(
597
- relativeTinybirdDir,
598
- devMode,
599
- existingDatafiles
600
- );
601
- updateConfig(configPath, {
602
- include: config.include,
603
- devMode: config.devMode,
604
- });
605
- created.push("tinybird.json (updated)");
606
- } catch (error) {
607
- return {
608
- success: false,
609
- created,
610
- skipped,
611
- error: `Failed to update tinybird.json: ${(error as Error).message}`,
612
- };
597
+ // Create or update config file
598
+ // Check for any existing config file first
599
+ const existingConfigPath = findExistingConfigPath(cwd);
600
+ const newConfigPath = getConfigPath(cwd);
601
+
602
+ if (existingConfigPath && !force) {
603
+ // Update existing config file (only if it's JSON)
604
+ const configFileName = path.basename(existingConfigPath);
605
+ if (existingConfigPath.endsWith(".json")) {
606
+ try {
607
+ const config = createDefaultConfig(
608
+ relativeTinybirdDir,
609
+ devMode,
610
+ existingDatafiles
611
+ );
612
+ updateConfig(existingConfigPath, {
613
+ include: config.include,
614
+ devMode: config.devMode,
615
+ });
616
+ created.push(`${configFileName} (updated)`);
617
+ } catch (error) {
618
+ return {
619
+ success: false,
620
+ created,
621
+ skipped,
622
+ error: `Failed to update ${configFileName}: ${(error as Error).message}`,
623
+ };
624
+ }
625
+ } else {
626
+ // JS config file exists - skip and let user update manually
627
+ skipped.push(`${configFileName} (JS config files must be updated manually)`);
613
628
  }
614
629
  } else {
630
+ // Create new config file with JSON format
615
631
  try {
616
632
  const config = createDefaultConfig(
617
633
  relativeTinybirdDir,
618
634
  devMode,
619
635
  existingDatafiles
620
636
  );
621
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
622
- created.push("tinybird.json");
637
+ fs.writeFileSync(newConfigPath, JSON.stringify(config, null, 2) + "\n");
638
+ created.push("tinybird.config.json");
623
639
  } catch (error) {
624
640
  return {
625
641
  success: false,
626
642
  created,
627
643
  skipped,
628
- error: `Failed to create tinybird.json: ${(error as Error).message}`,
644
+ error: `Failed to create tinybird.config.json: ${(error as Error).message}`,
629
645
  };
630
646
  }
631
647
  }
@@ -841,10 +857,13 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
841
857
  created.push(".env.local");
842
858
  }
843
859
 
844
- // If custom base URL, update tinybird.json
860
+ // If custom base URL, update config file
845
861
  const baseUrl = authResult.baseUrl ?? "https://api.tinybird.co";
846
862
  if (baseUrl !== "https://api.tinybird.co") {
847
- updateConfig(configPath, { baseUrl });
863
+ const currentConfigPath = findExistingConfigPath(cwd);
864
+ if (currentConfigPath && currentConfigPath.endsWith(".json")) {
865
+ updateConfig(currentConfigPath, { baseUrl });
866
+ }
848
867
  }
849
868
 
850
869
  // Generate TypeScript from existing Tinybird resources if requested
@@ -916,7 +935,7 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
916
935
  // (when user is already logged in)
917
936
  if (datafileAction === "codegen" && hasValidToken(cwd)) {
918
937
  try {
919
- const config = loadConfig(cwd);
938
+ const config = await loadConfigAsync(cwd);
920
939
  const tinybirdDir = path.join(cwd, relativeTinybirdDir);
921
940
  await runCodegen(
922
941
  config.baseUrl,
@@ -35,7 +35,7 @@ describe("Login Command", () => {
35
35
  const result = await runLogin({ cwd: tempDir });
36
36
 
37
37
  expect(result.success).toBe(false);
38
- expect(result.error).toContain("No tinybird.json found");
38
+ expect(result.error).toContain("No tinybird config found");
39
39
  expect(result.error).toContain("npx tinybird init");
40
40
  });
41
41
 
@@ -45,14 +45,15 @@ export async function runLogin(options: RunLoginOptions = {}): Promise<LoginResu
45
45
  const cwd = options.cwd ?? process.cwd();
46
46
 
47
47
  // Find the actual config file (may be in parent directory)
48
- const configPath = findConfigFile(cwd);
49
- if (!configPath) {
48
+ const configResult = findConfigFile(cwd);
49
+ if (!configResult) {
50
50
  return {
51
51
  success: false,
52
- error: "No tinybird.json found. Run 'npx tinybird init' first.",
52
+ error: "No tinybird config found. Run 'npx tinybird init' first.",
53
53
  };
54
54
  }
55
55
 
56
+ const configPath = configResult.path;
56
57
  // Get the directory containing the config file for .env.local
57
58
  const configDir = path.dirname(configPath);
58
59
 
@@ -72,12 +73,12 @@ export async function runLogin(options: RunLoginOptions = {}): Promise<LoginResu
72
73
  };
73
74
  }
74
75
 
75
- // Save token to .env.local (in same directory as tinybird.json)
76
+ // Save token to .env.local (in same directory as config file)
76
77
  try {
77
78
  saveTinybirdToken(configDir, authResult.token);
78
79
 
79
- // Update baseUrl in tinybird.json if it changed
80
- if (authResult.baseUrl) {
80
+ // Update baseUrl in config file if it changed (only for JSON configs)
81
+ if (authResult.baseUrl && configPath.endsWith(".json")) {
81
82
  updateConfig(configPath, {
82
83
  baseUrl: authResult.baseUrl,
83
84
  });
@@ -2,7 +2,7 @@
2
2
  * Preview command - creates ephemeral preview branch and deploys resources
3
3
  */
4
4
 
5
- import { loadConfig, LOCAL_BASE_URL, type ResolvedConfig, type DevMode } from "../config.js";
5
+ import { loadConfigAsync, LOCAL_BASE_URL, type ResolvedConfig, type DevMode } from "../config.js";
6
6
  import { buildFromInclude, type BuildFromIncludeResult } from "../../generator/index.js";
7
7
  import { createBranch, deleteBranch, getBranch, type TinybirdBranch } from "../../api/branches.js";
8
8
  import { deployToMain } from "../../api/deploy.js";
@@ -90,7 +90,7 @@ export async function runPreview(options: PreviewCommandOptions = {}): Promise<P
90
90
  // Load config
91
91
  let config: ResolvedConfig;
92
92
  try {
93
- config = loadConfig(cwd);
93
+ config = await loadConfigAsync(cwd);
94
94
  } catch (error) {
95
95
  return {
96
96
  success: false,