@tinybirdco/sdk 0.0.30 → 0.0.31
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.
- package/README.md +29 -4
- package/dist/cli/commands/branch.js +5 -5
- package/dist/cli/commands/branch.js.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +2 -2
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/build.test.d.ts +2 -0
- package/dist/cli/commands/build.test.d.ts.map +1 -0
- package/dist/cli/commands/build.test.js +266 -0
- package/dist/cli/commands/build.test.js.map +1 -0
- package/dist/cli/commands/clear.d.ts.map +1 -1
- package/dist/cli/commands/clear.js +2 -2
- package/dist/cli/commands/clear.js.map +1 -1
- package/dist/cli/commands/deploy.js +2 -2
- package/dist/cli/commands/deploy.js.map +1 -1
- package/dist/cli/commands/dev.js +12 -12
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/info.js +2 -2
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/info.test.js +11 -13
- package/dist/cli/commands/info.test.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +44 -26
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +44 -25
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/commands/login.d.ts.map +1 -1
- package/dist/cli/commands/login.js +7 -6
- package/dist/cli/commands/login.js.map +1 -1
- package/dist/cli/commands/login.test.js +1 -1
- package/dist/cli/commands/login.test.js.map +1 -1
- package/dist/cli/commands/preview.d.ts.map +1 -1
- package/dist/cli/commands/preview.js +2 -2
- package/dist/cli/commands/preview.js.map +1 -1
- package/dist/cli/config-loader.d.ts +18 -0
- package/dist/cli/config-loader.d.ts.map +1 -0
- package/dist/cli/config-loader.js +57 -0
- package/dist/cli/config-loader.js.map +1 -0
- package/dist/cli/config-types.d.ts +28 -0
- package/dist/cli/config-types.d.ts.map +1 -0
- package/dist/cli/config-types.js +8 -0
- package/dist/cli/config-types.js.map +1 -0
- package/dist/cli/config.d.ts +63 -29
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +139 -43
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/config.test.js +73 -9
- package/dist/cli/config.test.js.map +1 -1
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/client/base.d.ts.map +1 -1
- package/dist/client/base.js +21 -9
- package/dist/client/base.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/branch.ts +5 -5
- package/src/cli/commands/build.test.ts +310 -0
- package/src/cli/commands/build.ts +2 -2
- package/src/cli/commands/clear.ts +2 -2
- package/src/cli/commands/deploy.ts +2 -2
- package/src/cli/commands/dev.ts +12 -12
- package/src/cli/commands/info.test.ts +11 -13
- package/src/cli/commands/info.ts +2 -2
- package/src/cli/commands/init.test.ts +53 -37
- package/src/cli/commands/init.ts +49 -30
- package/src/cli/commands/login.test.ts +1 -1
- package/src/cli/commands/login.ts +7 -6
- package/src/cli/commands/preview.ts +2 -2
- package/src/cli/config-loader.ts +87 -0
- package/src/cli/config-types.ts +29 -0
- package/src/cli/config.test.ts +95 -8
- package/src/cli/config.ts +179 -70
- package/src/cli/index.ts +3 -0
- package/src/client/base.ts +33 -16
- package/src/index.ts +4 -0
package/src/cli/config.test.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getRelativeTinybirdDir,
|
|
9
9
|
findConfigFile,
|
|
10
10
|
loadConfig,
|
|
11
|
+
loadConfigAsync,
|
|
11
12
|
getConfigPath,
|
|
12
13
|
configExists,
|
|
13
14
|
updateConfig,
|
|
@@ -73,20 +74,60 @@ describe("Config", () => {
|
|
|
73
74
|
});
|
|
74
75
|
|
|
75
76
|
describe("findConfigFile", () => {
|
|
76
|
-
it("finds tinybird.json in current directory", () => {
|
|
77
|
+
it("finds tinybird.config.json in current directory", () => {
|
|
78
|
+
const configPath = path.join(tempDir, "tinybird.config.json");
|
|
79
|
+
fs.writeFileSync(configPath, "{}");
|
|
80
|
+
|
|
81
|
+
const result = findConfigFile(tempDir);
|
|
82
|
+
expect(result).not.toBe(null);
|
|
83
|
+
expect(result?.path).toBe(configPath);
|
|
84
|
+
expect(result?.type).toBe("tinybird.config.json");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("finds tinybird.json in current directory (legacy)", () => {
|
|
77
88
|
const configPath = path.join(tempDir, "tinybird.json");
|
|
78
89
|
fs.writeFileSync(configPath, "{}");
|
|
79
90
|
|
|
80
|
-
|
|
91
|
+
const result = findConfigFile(tempDir);
|
|
92
|
+
expect(result).not.toBe(null);
|
|
93
|
+
expect(result?.path).toBe(configPath);
|
|
94
|
+
expect(result?.type).toBe("tinybird.json");
|
|
81
95
|
});
|
|
82
96
|
|
|
83
|
-
it("finds tinybird.json in parent directory", () => {
|
|
97
|
+
it("finds tinybird.config.json in parent directory", () => {
|
|
84
98
|
const nestedDir = path.join(tempDir, "src", "app");
|
|
85
99
|
fs.mkdirSync(nestedDir, { recursive: true });
|
|
86
|
-
const configPath = path.join(tempDir, "tinybird.json");
|
|
100
|
+
const configPath = path.join(tempDir, "tinybird.config.json");
|
|
87
101
|
fs.writeFileSync(configPath, "{}");
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
const result = findConfigFile(nestedDir);
|
|
104
|
+
expect(result).not.toBe(null);
|
|
105
|
+
expect(result?.path).toBe(configPath);
|
|
106
|
+
expect(result?.type).toBe("tinybird.config.json");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("prioritizes tinybird.config.json over tinybird.json", () => {
|
|
110
|
+
const newConfig = path.join(tempDir, "tinybird.config.json");
|
|
111
|
+
const legacyConfig = path.join(tempDir, "tinybird.json");
|
|
112
|
+
fs.writeFileSync(newConfig, "{}");
|
|
113
|
+
fs.writeFileSync(legacyConfig, "{}");
|
|
114
|
+
|
|
115
|
+
const result = findConfigFile(tempDir);
|
|
116
|
+
expect(result).not.toBe(null);
|
|
117
|
+
expect(result?.path).toBe(newConfig);
|
|
118
|
+
expect(result?.type).toBe("tinybird.config.json");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("prioritizes tinybird.config.mjs over tinybird.config.cjs", () => {
|
|
122
|
+
const mjsConfig = path.join(tempDir, "tinybird.config.mjs");
|
|
123
|
+
const cjsConfig = path.join(tempDir, "tinybird.config.cjs");
|
|
124
|
+
fs.writeFileSync(mjsConfig, "export default {};");
|
|
125
|
+
fs.writeFileSync(cjsConfig, "module.exports = {};");
|
|
126
|
+
|
|
127
|
+
const result = findConfigFile(tempDir);
|
|
128
|
+
expect(result).not.toBe(null);
|
|
129
|
+
expect(result?.path).toBe(mjsConfig);
|
|
130
|
+
expect(result?.type).toBe("tinybird.config.mjs");
|
|
90
131
|
});
|
|
91
132
|
|
|
92
133
|
it("returns null when no config file exists", () => {
|
|
@@ -95,8 +136,10 @@ describe("Config", () => {
|
|
|
95
136
|
});
|
|
96
137
|
|
|
97
138
|
describe("getConfigPath", () => {
|
|
98
|
-
it("returns path to tinybird.json in directory", () => {
|
|
99
|
-
expect(getConfigPath(tempDir)).toBe(
|
|
139
|
+
it("returns path to tinybird.config.json (new default) in directory", () => {
|
|
140
|
+
expect(getConfigPath(tempDir)).toBe(
|
|
141
|
+
path.join(tempDir, "tinybird.config.json")
|
|
142
|
+
);
|
|
100
143
|
});
|
|
101
144
|
});
|
|
102
145
|
|
|
@@ -123,7 +166,7 @@ describe("Config", () => {
|
|
|
123
166
|
});
|
|
124
167
|
|
|
125
168
|
it("throws error when no config file exists", () => {
|
|
126
|
-
expect(() => loadConfig(tempDir)).toThrow("Could not find
|
|
169
|
+
expect(() => loadConfig(tempDir)).toThrow("Could not find config file");
|
|
127
170
|
});
|
|
128
171
|
|
|
129
172
|
it("loads config with include array", () => {
|
|
@@ -286,6 +329,50 @@ describe("Config", () => {
|
|
|
286
329
|
});
|
|
287
330
|
});
|
|
288
331
|
|
|
332
|
+
describe("loadConfigAsync", () => {
|
|
333
|
+
beforeEach(() => {
|
|
334
|
+
// Mock git functions to avoid git dependency in tests
|
|
335
|
+
vi.mock("./git.js", () => ({
|
|
336
|
+
getCurrentGitBranch: () => "main",
|
|
337
|
+
isMainBranch: () => true,
|
|
338
|
+
getTinybirdBranchName: () => null,
|
|
339
|
+
}));
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("loads tinybird.config.mjs", async () => {
|
|
343
|
+
fs.writeFileSync(
|
|
344
|
+
path.join(tempDir, "tinybird.config.mjs"),
|
|
345
|
+
`export default {
|
|
346
|
+
include: ["lib/datasources.ts"],
|
|
347
|
+
token: "test-token"
|
|
348
|
+
};`
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const result = await loadConfigAsync(tempDir);
|
|
352
|
+
|
|
353
|
+
expect(result.include).toEqual(["lib/datasources.ts"]);
|
|
354
|
+
expect(result.token).toBe("test-token");
|
|
355
|
+
expect(result.baseUrl).toBe("https://api.tinybird.co");
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("loads tinybird.config.cjs", async () => {
|
|
359
|
+
fs.writeFileSync(
|
|
360
|
+
path.join(tempDir, "tinybird.config.cjs"),
|
|
361
|
+
`module.exports = {
|
|
362
|
+
include: ["lib/datasources.ts"],
|
|
363
|
+
token: "test-token"
|
|
364
|
+
};`
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
const result = await loadConfigAsync(tempDir);
|
|
368
|
+
|
|
369
|
+
expect(result.include).toEqual(["lib/datasources.ts"]);
|
|
370
|
+
expect(result.token).toBe("test-token");
|
|
371
|
+
expect(result.baseUrl).toBe("https://api.tinybird.co");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
});
|
|
375
|
+
|
|
289
376
|
describe("updateConfig", () => {
|
|
290
377
|
it("updates existing config file", () => {
|
|
291
378
|
const configPath = path.join(tempDir, "tinybird.json");
|
package/src/cli/config.ts
CHANGED
|
@@ -6,28 +6,9 @@ import * as fs from "fs";
|
|
|
6
6
|
import * as path from "path";
|
|
7
7
|
import { getCurrentGitBranch, isMainBranch, getTinybirdBranchName } from "./git.js";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* - "local": Use local Tinybird container at localhost:7181
|
|
13
|
-
*/
|
|
14
|
-
export type DevMode = "branch" | "local";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Tinybird configuration file structure
|
|
18
|
-
*/
|
|
19
|
-
export interface TinybirdConfig {
|
|
20
|
-
/** Array of TypeScript files to scan for datasources and pipes */
|
|
21
|
-
include?: string[];
|
|
22
|
-
/** @deprecated Use `include` instead. Path to the TypeScript schema entry point */
|
|
23
|
-
schema?: string;
|
|
24
|
-
/** API token (supports ${ENV_VAR} interpolation) */
|
|
25
|
-
token: string;
|
|
26
|
-
/** Tinybird API base URL (optional, defaults to EU region) */
|
|
27
|
-
baseUrl?: string;
|
|
28
|
-
/** Development mode: "branch" (default) or "local" */
|
|
29
|
-
devMode?: DevMode;
|
|
30
|
-
}
|
|
9
|
+
// Re-export types from config-types.ts (separate file to avoid bundling esbuild)
|
|
10
|
+
export type { DevMode, TinybirdConfig } from "./config-types.js";
|
|
11
|
+
import type { DevMode, TinybirdConfig } from "./config-types.js";
|
|
31
12
|
|
|
32
13
|
/**
|
|
33
14
|
* Resolved configuration with all values expanded
|
|
@@ -64,9 +45,25 @@ const DEFAULT_BASE_URL = "https://api.tinybird.co";
|
|
|
64
45
|
export const LOCAL_BASE_URL = "http://localhost:7181";
|
|
65
46
|
|
|
66
47
|
/**
|
|
67
|
-
* Config file
|
|
48
|
+
* Config file names in priority order
|
|
49
|
+
* - tinybird.config.mjs: ESM config with dynamic logic
|
|
50
|
+
* - tinybird.config.cjs: CommonJS config with dynamic logic
|
|
51
|
+
* - tinybird.config.json: Standard JSON config (default for new projects)
|
|
52
|
+
* - tinybird.json: Legacy JSON config (backward compatible)
|
|
68
53
|
*/
|
|
69
|
-
const
|
|
54
|
+
const CONFIG_FILES = [
|
|
55
|
+
"tinybird.config.mjs",
|
|
56
|
+
"tinybird.config.cjs",
|
|
57
|
+
"tinybird.config.json",
|
|
58
|
+
"tinybird.json",
|
|
59
|
+
] as const;
|
|
60
|
+
|
|
61
|
+
type ConfigFileType = (typeof CONFIG_FILES)[number];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Default config file name for new projects
|
|
65
|
+
*/
|
|
66
|
+
const DEFAULT_CONFIG_FILE = "tinybird.config.json";
|
|
70
67
|
|
|
71
68
|
/**
|
|
72
69
|
* Tinybird file path within lib folder
|
|
@@ -135,19 +132,33 @@ function interpolateEnvVars(value: string): string {
|
|
|
135
132
|
});
|
|
136
133
|
}
|
|
137
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Result of finding a config file
|
|
137
|
+
*/
|
|
138
|
+
export interface ConfigFileResult {
|
|
139
|
+
/** Full path to the config file */
|
|
140
|
+
path: string;
|
|
141
|
+
/** Type of config file found */
|
|
142
|
+
type: ConfigFileType;
|
|
143
|
+
}
|
|
144
|
+
|
|
138
145
|
/**
|
|
139
146
|
* Find the config file by walking up the directory tree
|
|
147
|
+
* Checks for all supported config file names in priority order
|
|
140
148
|
*
|
|
141
149
|
* @param startDir - Directory to start searching from
|
|
142
|
-
* @returns Path
|
|
150
|
+
* @returns Path and type of the config file, or null if not found
|
|
143
151
|
*/
|
|
144
|
-
export function findConfigFile(startDir: string):
|
|
152
|
+
export function findConfigFile(startDir: string): ConfigFileResult | null {
|
|
145
153
|
let currentDir = startDir;
|
|
146
154
|
|
|
147
155
|
while (true) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
156
|
+
// Check each config file type in priority order
|
|
157
|
+
for (const configFile of CONFIG_FILES) {
|
|
158
|
+
const configPath = path.join(currentDir, configFile);
|
|
159
|
+
if (fs.existsSync(configPath)) {
|
|
160
|
+
return { path: configPath, type: configFile };
|
|
161
|
+
}
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
const parentDir = path.dirname(currentDir);
|
|
@@ -159,42 +170,13 @@ export function findConfigFile(startDir: string): string | null {
|
|
|
159
170
|
}
|
|
160
171
|
}
|
|
161
172
|
|
|
173
|
+
// Import the universal config loader
|
|
174
|
+
import { loadConfigFile } from "./config-loader.js";
|
|
175
|
+
|
|
162
176
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* @param cwd - Working directory to start searching from (defaults to process.cwd())
|
|
166
|
-
* @returns Resolved configuration
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* ```ts
|
|
170
|
-
* const config = loadConfig();
|
|
171
|
-
* console.log(config.schema); // 'lib/tinybird.ts' or 'src/lib/tinybird.ts'
|
|
172
|
-
* console.log(config.token); // 'p.xxx' (resolved from ${TINYBIRD_TOKEN})
|
|
173
|
-
* ```
|
|
177
|
+
* Resolve a TinybirdConfig to a ResolvedConfig
|
|
174
178
|
*/
|
|
175
|
-
|
|
176
|
-
const configPath = findConfigFile(cwd);
|
|
177
|
-
|
|
178
|
-
if (!configPath) {
|
|
179
|
-
throw new Error(
|
|
180
|
-
`Could not find ${CONFIG_FILE}. Run 'npx tinybird init' to create one.`
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
let rawContent: string;
|
|
185
|
-
try {
|
|
186
|
-
rawContent = fs.readFileSync(configPath, "utf-8");
|
|
187
|
-
} catch (error) {
|
|
188
|
-
throw new Error(`Failed to read ${configPath}: ${(error as Error).message}`);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
let config: TinybirdConfig;
|
|
192
|
-
try {
|
|
193
|
-
config = JSON.parse(rawContent) as TinybirdConfig;
|
|
194
|
-
} catch (error) {
|
|
195
|
-
throw new Error(`Failed to parse ${configPath}: ${(error as Error).message}`);
|
|
196
|
-
}
|
|
197
|
-
|
|
179
|
+
function resolveConfig(config: TinybirdConfig, configPath: string): ResolvedConfig {
|
|
198
180
|
// Validate required fields - need either include or schema
|
|
199
181
|
if (!config.include && !config.schema) {
|
|
200
182
|
throw new Error(`Missing 'include' field in ${configPath}. Add an array of files to scan for datasources and pipes.`);
|
|
@@ -262,28 +244,140 @@ export function loadConfig(cwd: string = process.cwd()): ResolvedConfig {
|
|
|
262
244
|
}
|
|
263
245
|
|
|
264
246
|
/**
|
|
265
|
-
*
|
|
247
|
+
* Load and resolve the Tinybird configuration
|
|
248
|
+
*
|
|
249
|
+
* Supports the following config file formats (in priority order):
|
|
250
|
+
* - tinybird.config.mjs: ESM config with dynamic logic
|
|
251
|
+
* - tinybird.config.cjs: CommonJS config with dynamic logic
|
|
252
|
+
* - tinybird.config.json: Standard JSON config
|
|
253
|
+
* - tinybird.json: Legacy JSON config (backward compatible)
|
|
254
|
+
*
|
|
255
|
+
* @param cwd - Working directory to start searching from (defaults to process.cwd())
|
|
256
|
+
* @returns Resolved configuration
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* const config = loadConfig();
|
|
261
|
+
* console.log(config.include); // ['lib/tinybird.ts']
|
|
262
|
+
* console.log(config.token); // 'p.xxx' (resolved from ${TINYBIRD_TOKEN})
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export function loadConfig(cwd: string = process.cwd()): ResolvedConfig {
|
|
266
|
+
const configResult = findConfigFile(cwd);
|
|
267
|
+
|
|
268
|
+
if (!configResult) {
|
|
269
|
+
throw new Error(
|
|
270
|
+
`Could not find config file. Run 'npx tinybird init' to create one.\n` +
|
|
271
|
+
`Searched for: ${CONFIG_FILES.join(", ")}`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const { path: configPath, type: configType } = configResult;
|
|
276
|
+
|
|
277
|
+
// JSON files can be loaded synchronously
|
|
278
|
+
if (configType === "tinybird.config.json" || configType === "tinybird.json") {
|
|
279
|
+
let rawContent: string;
|
|
280
|
+
try {
|
|
281
|
+
rawContent = fs.readFileSync(configPath, "utf-8");
|
|
282
|
+
} catch (error) {
|
|
283
|
+
throw new Error(`Failed to read ${configPath}: ${(error as Error).message}`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let config: TinybirdConfig;
|
|
287
|
+
try {
|
|
288
|
+
config = JSON.parse(rawContent) as TinybirdConfig;
|
|
289
|
+
} catch (error) {
|
|
290
|
+
throw new Error(`Failed to parse ${configPath}: ${(error as Error).message}`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return resolveConfig(config, configPath);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// For JS files, we need to throw an error asking to use the async version
|
|
297
|
+
throw new Error(
|
|
298
|
+
`Config file ${configPath} is a JavaScript file. ` +
|
|
299
|
+
`Use loadConfigAsync() instead of loadConfig() to load .mjs/.cjs config files.`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Load and resolve the Tinybird configuration (async version)
|
|
305
|
+
*
|
|
306
|
+
* This async version supports all config file formats including JS files
|
|
307
|
+
* that may contain dynamic logic.
|
|
308
|
+
*
|
|
309
|
+
* @param cwd - Working directory to start searching from (defaults to process.cwd())
|
|
310
|
+
* @returns Promise resolving to the configuration
|
|
311
|
+
*/
|
|
312
|
+
export async function loadConfigAsync(cwd: string = process.cwd()): Promise<ResolvedConfig> {
|
|
313
|
+
const configResult = findConfigFile(cwd);
|
|
314
|
+
|
|
315
|
+
if (!configResult) {
|
|
316
|
+
throw new Error(
|
|
317
|
+
`Could not find config file. Run 'npx tinybird init' to create one.\n` +
|
|
318
|
+
`Searched for: ${CONFIG_FILES.join(", ")}`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const { path: configPath } = configResult;
|
|
323
|
+
|
|
324
|
+
// Use the universal config loader for all file types
|
|
325
|
+
const { config } = await loadConfigFile<TinybirdConfig>(configPath);
|
|
326
|
+
|
|
327
|
+
return resolveConfig(config, configPath);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Check if a config file exists in the given directory or its parents
|
|
266
332
|
*/
|
|
267
333
|
export function configExists(cwd: string = process.cwd()): boolean {
|
|
268
334
|
return findConfigFile(cwd) !== null;
|
|
269
335
|
}
|
|
270
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Get the path to an existing config file, or the default path for a new config
|
|
339
|
+
* This is useful for the init command which needs to either update an existing config
|
|
340
|
+
* or create a new one with the new default name
|
|
341
|
+
*/
|
|
342
|
+
export function getExistingOrNewConfigPath(cwd: string = process.cwd()): string {
|
|
343
|
+
const existing = findExistingConfigPath(cwd);
|
|
344
|
+
return existing ?? path.join(cwd, DEFAULT_CONFIG_FILE);
|
|
345
|
+
}
|
|
346
|
+
|
|
271
347
|
/**
|
|
272
348
|
* Get the expected config file path for a directory
|
|
349
|
+
* Returns the path for the default config file name (tinybird.config.json)
|
|
273
350
|
*/
|
|
274
351
|
export function getConfigPath(cwd: string = process.cwd()): string {
|
|
275
|
-
return path.join(cwd,
|
|
352
|
+
return path.join(cwd, DEFAULT_CONFIG_FILE);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Find an existing config file in a directory
|
|
357
|
+
* Returns the path to the first matching config file, or null if none found
|
|
358
|
+
*/
|
|
359
|
+
export function findExistingConfigPath(cwd: string = process.cwd()): string | null {
|
|
360
|
+
for (const configFile of CONFIG_FILES) {
|
|
361
|
+
const configPath = path.join(cwd, configFile);
|
|
362
|
+
if (fs.existsSync(configPath)) {
|
|
363
|
+
return configPath;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return null;
|
|
276
367
|
}
|
|
277
368
|
|
|
278
369
|
/**
|
|
279
|
-
* Update specific fields in
|
|
370
|
+
* Update specific fields in a JSON config file
|
|
371
|
+
*
|
|
372
|
+
* Note: Only works with JSON config files (.json). For JS config files,
|
|
373
|
+
* the user needs to update them manually.
|
|
280
374
|
*
|
|
281
375
|
* Throws an error if the config file doesn't exist to prevent creating
|
|
282
376
|
* partial config files that would break loadConfig.
|
|
283
377
|
*
|
|
284
378
|
* @param configPath - Path to the config file
|
|
285
379
|
* @param updates - Fields to update
|
|
286
|
-
* @throws Error if config file doesn't exist
|
|
380
|
+
* @throws Error if config file doesn't exist or is not a JSON file
|
|
287
381
|
*/
|
|
288
382
|
export function updateConfig(
|
|
289
383
|
configPath: string,
|
|
@@ -293,6 +387,12 @@ export function updateConfig(
|
|
|
293
387
|
throw new Error(`Config not found at ${configPath}`);
|
|
294
388
|
}
|
|
295
389
|
|
|
390
|
+
if (!configPath.endsWith(".json")) {
|
|
391
|
+
throw new Error(
|
|
392
|
+
`Cannot update ${configPath}. Only JSON config files can be updated programmatically.`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
296
396
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
297
397
|
const config = JSON.parse(content) as TinybirdConfig;
|
|
298
398
|
|
|
@@ -305,17 +405,26 @@ export function updateConfig(
|
|
|
305
405
|
/**
|
|
306
406
|
* Check if a valid token is configured (either in file or via env var)
|
|
307
407
|
*
|
|
408
|
+
* Note: For JS config files, this only works if the token is a static value
|
|
409
|
+
* or environment variable reference in the file.
|
|
410
|
+
*
|
|
308
411
|
* @param cwd - Working directory to search from
|
|
309
412
|
* @returns true if a valid token exists
|
|
310
413
|
*/
|
|
311
414
|
export function hasValidToken(cwd: string = process.cwd()): boolean {
|
|
312
415
|
try {
|
|
313
|
-
const
|
|
314
|
-
if (!
|
|
416
|
+
const configResult = findConfigFile(cwd);
|
|
417
|
+
if (!configResult) {
|
|
315
418
|
return false;
|
|
316
419
|
}
|
|
317
420
|
|
|
318
|
-
|
|
421
|
+
// For JS files, we can't easily check without loading them
|
|
422
|
+
// Return true and let loadConfig handle validation
|
|
423
|
+
if (!configResult.path.endsWith(".json")) {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const content = fs.readFileSync(configResult.path, "utf-8");
|
|
319
428
|
const config = JSON.parse(content) as TinybirdConfig;
|
|
320
429
|
|
|
321
430
|
if (!config.token) {
|
package/src/cli/index.ts
CHANGED
|
@@ -247,6 +247,7 @@ function createCli(): Command {
|
|
|
247
247
|
.option("--dry-run", "Generate without pushing to API")
|
|
248
248
|
.option("--debug", "Show debug output including API requests/responses")
|
|
249
249
|
.option("--local", "Use local Tinybird container")
|
|
250
|
+
.option("--branch", "Use Tinybird cloud with branches")
|
|
250
251
|
.action(async (options) => {
|
|
251
252
|
if (options.debug) {
|
|
252
253
|
process.env.TINYBIRD_DEBUG = "1";
|
|
@@ -256,6 +257,8 @@ function createCli(): Command {
|
|
|
256
257
|
let devModeOverride: DevMode | undefined;
|
|
257
258
|
if (options.local) {
|
|
258
259
|
devModeOverride = "local";
|
|
260
|
+
} else if (options.branch) {
|
|
261
|
+
devModeOverride = "branch";
|
|
259
262
|
}
|
|
260
263
|
|
|
261
264
|
const result = await runBuild({
|
package/src/client/base.ts
CHANGED
|
@@ -130,9 +130,11 @@ export class TinybirdClient {
|
|
|
130
130
|
try {
|
|
131
131
|
// Dynamic import to avoid circular dependencies and to keep CLI code
|
|
132
132
|
// out of the client bundle when not using dev mode
|
|
133
|
-
const {
|
|
133
|
+
const { loadConfigAsync } = await import("../cli/config.js");
|
|
134
134
|
const { getOrCreateBranch } = await import("../api/branches.js");
|
|
135
|
-
const { isPreviewEnvironment, getPreviewBranchName } = await import(
|
|
135
|
+
const { isPreviewEnvironment, getPreviewBranchName } = await import(
|
|
136
|
+
"./preview.js"
|
|
137
|
+
);
|
|
136
138
|
|
|
137
139
|
// In preview environments (Vercel preview, CI), the token was already resolved
|
|
138
140
|
// by resolveToken() in project.ts - skip branch creation to avoid conflicts
|
|
@@ -140,9 +142,14 @@ export class TinybirdClient {
|
|
|
140
142
|
const gitBranchName = getPreviewBranchName();
|
|
141
143
|
// Preview branches use the tmp_ci_ prefix (matches what tinybird preview creates)
|
|
142
144
|
const sanitized = gitBranchName
|
|
143
|
-
? gitBranchName
|
|
145
|
+
? gitBranchName
|
|
146
|
+
.replace(/[^a-zA-Z0-9_]/g, "_")
|
|
147
|
+
.replace(/_+/g, "_")
|
|
148
|
+
.replace(/^_|_$/g, "")
|
|
149
|
+
: undefined;
|
|
150
|
+
const tinybirdBranchName = sanitized
|
|
151
|
+
? `tmp_ci_${sanitized}`
|
|
144
152
|
: undefined;
|
|
145
|
-
const tinybirdBranchName = sanitized ? `tmp_ci_${sanitized}` : undefined;
|
|
146
153
|
return this.buildContext({
|
|
147
154
|
token: this.config.token,
|
|
148
155
|
isBranchToken: !!tinybirdBranchName,
|
|
@@ -153,12 +160,16 @@ export class TinybirdClient {
|
|
|
153
160
|
|
|
154
161
|
// Use configDir if provided (important for monorepo setups where process.cwd()
|
|
155
162
|
// may not be in the same directory tree as tinybird.json)
|
|
156
|
-
const config =
|
|
163
|
+
const config = await loadConfigAsync(this.config.configDir);
|
|
157
164
|
const gitBranch = config.gitBranch ?? undefined;
|
|
158
165
|
|
|
159
166
|
// If on main branch, use the workspace token
|
|
160
167
|
if (config.isMainBranch || !config.tinybirdBranch) {
|
|
161
|
-
return this.buildContext({
|
|
168
|
+
return this.buildContext({
|
|
169
|
+
token: this.config.token,
|
|
170
|
+
isBranchToken: false,
|
|
171
|
+
gitBranch,
|
|
172
|
+
});
|
|
162
173
|
}
|
|
163
174
|
|
|
164
175
|
const branchName = config.tinybirdBranch;
|
|
@@ -171,7 +182,11 @@ export class TinybirdClient {
|
|
|
171
182
|
|
|
172
183
|
if (!branch.token) {
|
|
173
184
|
// Fall back to workspace token if no branch token
|
|
174
|
-
return this.buildContext({
|
|
185
|
+
return this.buildContext({
|
|
186
|
+
token: this.config.token,
|
|
187
|
+
isBranchToken: false,
|
|
188
|
+
gitBranch,
|
|
189
|
+
});
|
|
175
190
|
}
|
|
176
191
|
|
|
177
192
|
return this.buildContext({
|
|
@@ -180,9 +195,11 @@ export class TinybirdClient {
|
|
|
180
195
|
branchName,
|
|
181
196
|
gitBranch,
|
|
182
197
|
});
|
|
183
|
-
} catch {
|
|
184
|
-
|
|
185
|
-
|
|
198
|
+
} catch (error) {
|
|
199
|
+
throw new TinybirdError(
|
|
200
|
+
`Failed to resolve branch context: ${(error as Error).message}`,
|
|
201
|
+
500
|
|
202
|
+
);
|
|
186
203
|
}
|
|
187
204
|
}
|
|
188
205
|
|
|
@@ -240,7 +257,11 @@ export class TinybirdClient {
|
|
|
240
257
|
const token = await this.getToken();
|
|
241
258
|
|
|
242
259
|
try {
|
|
243
|
-
return await this.getApi(token).ingestBatch(
|
|
260
|
+
return await this.getApi(token).ingestBatch(
|
|
261
|
+
datasourceName,
|
|
262
|
+
events,
|
|
263
|
+
options
|
|
264
|
+
);
|
|
244
265
|
} catch (error) {
|
|
245
266
|
this.rethrowApiError(error);
|
|
246
267
|
}
|
|
@@ -310,11 +331,7 @@ export class TinybirdClient {
|
|
|
310
331
|
|
|
311
332
|
private rethrowApiError(error: unknown): never {
|
|
312
333
|
if (error instanceof TinybirdApiError) {
|
|
313
|
-
throw new TinybirdError(
|
|
314
|
-
error.message,
|
|
315
|
-
error.statusCode,
|
|
316
|
-
error.response
|
|
317
|
-
);
|
|
334
|
+
throw new TinybirdError(error.message, error.statusCode, error.response);
|
|
318
335
|
}
|
|
319
336
|
|
|
320
337
|
throw error;
|
package/src/index.ts
CHANGED
|
@@ -245,3 +245,7 @@ export {
|
|
|
245
245
|
getLocalDashboardUrl,
|
|
246
246
|
} from "./api/dashboard.js";
|
|
247
247
|
export type { RegionInfo } from "./api/dashboard.js";
|
|
248
|
+
|
|
249
|
+
// ============ Config Types ============
|
|
250
|
+
// Import from config-types.ts to avoid bundling esbuild in client code
|
|
251
|
+
export type { TinybirdConfig, DevMode } from "./cli/config-types.js";
|