@tinybirdco/sdk 0.0.20 → 0.0.22
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/dist/api/dashboard.d.ts +74 -0
- package/dist/api/dashboard.d.ts.map +1 -0
- package/dist/api/dashboard.js +102 -0
- package/dist/api/dashboard.js.map +1 -0
- package/dist/api/dashboard.test.d.ts +2 -0
- package/dist/api/dashboard.test.d.ts.map +1 -0
- package/dist/api/dashboard.test.js +91 -0
- package/dist/api/dashboard.test.js.map +1 -0
- package/dist/cli/commands/branch.d.ts +2 -0
- package/dist/cli/commands/branch.d.ts.map +1 -1
- package/dist/cli/commands/branch.js +12 -1
- package/dist/cli/commands/branch.js.map +1 -1
- package/dist/cli/commands/build.d.ts +17 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +25 -0
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/dev.d.ts +2 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +19 -3
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/index.js +23 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/output.d.ts +20 -0
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +37 -0
- package/dist/cli/output.js.map +1 -1
- package/dist/client/base.d.ts +36 -5
- package/dist/client/base.d.ts.map +1 -1
- package/dist/client/base.js +64 -18
- package/dist/client/base.js.map +1 -1
- package/dist/client/base.test.d.ts +2 -0
- package/dist/client/base.test.d.ts.map +1 -0
- package/dist/client/base.test.js +104 -0
- package/dist/client/base.test.js.map +1 -0
- package/dist/client/types.d.ts +16 -0
- package/dist/client/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/dashboard.test.ts +115 -0
- package/src/api/dashboard.ts +121 -0
- package/src/cli/commands/branch.ts +15 -1
- package/src/cli/commands/build.ts +46 -0
- package/src/cli/commands/dev.ts +47 -7
- package/src/cli/index.ts +25 -27
- package/src/cli/output.ts +54 -0
- package/src/client/base.test.ts +129 -0
- package/src/client/base.ts +68 -18
- package/src/client/types.ts +17 -0
- package/src/index.ts +10 -0
package/src/cli/commands/dev.ts
CHANGED
|
@@ -4,7 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import * as path from "path";
|
|
6
6
|
import { watch } from "chokidar";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
loadConfig,
|
|
9
|
+
configExists,
|
|
10
|
+
findConfigFile,
|
|
11
|
+
hasValidToken,
|
|
12
|
+
updateConfig,
|
|
13
|
+
LOCAL_BASE_URL,
|
|
14
|
+
type ResolvedConfig,
|
|
15
|
+
type DevMode,
|
|
16
|
+
} from "../config.js";
|
|
8
17
|
import { runBuild, type BuildCommandResult } from "./build.js";
|
|
9
18
|
import { getOrCreateBranch, type TinybirdBranch } from "../../api/branches.js";
|
|
10
19
|
import { browserLogin } from "../auth.js";
|
|
@@ -19,6 +28,8 @@ import {
|
|
|
19
28
|
getLocalWorkspaceName,
|
|
20
29
|
type LocalWorkspace,
|
|
21
30
|
} from "../../api/local.js";
|
|
31
|
+
import { getWorkspace } from "../../api/workspaces.js";
|
|
32
|
+
import { getBranchDashboardUrl, getLocalDashboardUrl } from "../../api/dashboard.js";
|
|
22
33
|
|
|
23
34
|
/**
|
|
24
35
|
* Login result info
|
|
@@ -70,6 +81,8 @@ export interface BranchReadyInfo {
|
|
|
70
81
|
isLocal?: boolean;
|
|
71
82
|
/** Local workspace info (only in local mode) */
|
|
72
83
|
localWorkspace?: LocalWorkspace;
|
|
84
|
+
/** Dashboard URL for the branch (only in branch mode) */
|
|
85
|
+
dashboardUrl?: string;
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
/**
|
|
@@ -99,7 +112,9 @@ export interface DevController {
|
|
|
99
112
|
* @param options - Dev options
|
|
100
113
|
* @returns Dev controller
|
|
101
114
|
*/
|
|
102
|
-
export async function runDev(
|
|
115
|
+
export async function runDev(
|
|
116
|
+
options: DevCommandOptions = {}
|
|
117
|
+
): Promise<DevController> {
|
|
103
118
|
const cwd = options.cwd ?? process.cwd();
|
|
104
119
|
const debounceMs = options.debounce ?? 100;
|
|
105
120
|
|
|
@@ -129,7 +144,8 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
|
|
|
129
144
|
|
|
130
145
|
if (!authResult.success || !authResult.token) {
|
|
131
146
|
throw new Error(
|
|
132
|
-
authResult.error ??
|
|
147
|
+
authResult.error ??
|
|
148
|
+
"Login failed. Run 'npx tinybird login' to authenticate."
|
|
133
149
|
);
|
|
134
150
|
}
|
|
135
151
|
|
|
@@ -173,8 +189,14 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
|
|
|
173
189
|
if (devMode === "local") {
|
|
174
190
|
// Local mode: get tokens from local container and set up workspace
|
|
175
191
|
const localTokens = await getLocalTokens();
|
|
176
|
-
const workspaceName = getLocalWorkspaceName(
|
|
177
|
-
|
|
192
|
+
const workspaceName = getLocalWorkspaceName(
|
|
193
|
+
config.tinybirdBranch,
|
|
194
|
+
config.cwd
|
|
195
|
+
);
|
|
196
|
+
const { workspace, wasCreated } = await getOrCreateLocalWorkspace(
|
|
197
|
+
localTokens,
|
|
198
|
+
workspaceName
|
|
199
|
+
);
|
|
178
200
|
|
|
179
201
|
effectiveToken = workspace.token;
|
|
180
202
|
effectiveBaseUrl = LOCAL_BASE_URL;
|
|
@@ -184,6 +206,7 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
|
|
|
184
206
|
isLocal: true,
|
|
185
207
|
localWorkspace: workspace,
|
|
186
208
|
wasCreated,
|
|
209
|
+
dashboardUrl: getLocalDashboardUrl(workspace.name),
|
|
187
210
|
};
|
|
188
211
|
} else {
|
|
189
212
|
// Branch mode: use Tinybird cloud with branches
|
|
@@ -216,11 +239,22 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
|
|
|
216
239
|
}
|
|
217
240
|
|
|
218
241
|
effectiveToken = tinybirdBranch.token;
|
|
242
|
+
|
|
243
|
+
// Get workspace name for dashboard URL
|
|
244
|
+
const workspace = await getWorkspace({
|
|
245
|
+
baseUrl: config.baseUrl,
|
|
246
|
+
token: config.token,
|
|
247
|
+
});
|
|
248
|
+
const dashboardUrl =
|
|
249
|
+
getBranchDashboardUrl(config.baseUrl, workspace.name, branchName) ??
|
|
250
|
+
undefined;
|
|
251
|
+
|
|
219
252
|
branchInfo = {
|
|
220
253
|
gitBranch: config.gitBranch, // Original git branch name for display
|
|
221
254
|
isMainBranch: false,
|
|
222
255
|
tinybirdBranch,
|
|
223
256
|
wasCreated: tinybirdBranch.wasCreated ?? false,
|
|
257
|
+
dashboardUrl,
|
|
224
258
|
};
|
|
225
259
|
}
|
|
226
260
|
}
|
|
@@ -246,7 +280,11 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
|
|
|
246
280
|
async function doBuild(): Promise<BuildCommandResult> {
|
|
247
281
|
if (isBuilding) {
|
|
248
282
|
pendingBuild = true;
|
|
249
|
-
return {
|
|
283
|
+
return {
|
|
284
|
+
success: false,
|
|
285
|
+
error: "Build already in progress",
|
|
286
|
+
durationMs: 0,
|
|
287
|
+
};
|
|
250
288
|
}
|
|
251
289
|
|
|
252
290
|
isBuilding = true;
|
|
@@ -357,7 +395,9 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
|
|
|
357
395
|
});
|
|
358
396
|
|
|
359
397
|
watcher.on("error", (error: unknown) => {
|
|
360
|
-
options.onError?.(
|
|
398
|
+
options.onError?.(
|
|
399
|
+
error instanceof Error ? error : new Error(String(error))
|
|
400
|
+
);
|
|
361
401
|
});
|
|
362
402
|
|
|
363
403
|
// Do initial build
|
package/src/cli/index.ts
CHANGED
|
@@ -188,15 +188,19 @@ function createCli(): Command {
|
|
|
188
188
|
devModeOverride = "local";
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
const modeLabel = devModeOverride === "local" ? " (local)" : "";
|
|
192
|
-
output.highlight(`Building${modeLabel}...`);
|
|
193
|
-
|
|
194
191
|
const result = await runBuild({
|
|
195
192
|
dryRun: options.dryRun,
|
|
196
193
|
devModeOverride,
|
|
197
194
|
});
|
|
198
195
|
|
|
199
|
-
const { build, deploy } = result;
|
|
196
|
+
const { build, deploy, branchInfo } = result;
|
|
197
|
+
|
|
198
|
+
// Show branch info
|
|
199
|
+
if (branchInfo) {
|
|
200
|
+
output.showBranchInfo(branchInfo);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
output.highlight("Building...");
|
|
200
204
|
|
|
201
205
|
if (!result.success) {
|
|
202
206
|
// Show detailed errors if available
|
|
@@ -486,32 +490,23 @@ function createCli(): Command {
|
|
|
486
490
|
},
|
|
487
491
|
onBranchReady: (info) => {
|
|
488
492
|
if (info.isLocal) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
console.log(`Using local Tinybird container`);
|
|
497
|
-
console.log(
|
|
498
|
-
`Using existing local workspace '${workspaceName}'\n`
|
|
499
|
-
);
|
|
500
|
-
}
|
|
493
|
+
output.showBranchInfo({
|
|
494
|
+
gitBranch: info.gitBranch,
|
|
495
|
+
tinybirdBranch: info.localWorkspace?.name ?? null,
|
|
496
|
+
wasCreated: info.wasCreated ?? false,
|
|
497
|
+
dashboardUrl: info.dashboardUrl,
|
|
498
|
+
isLocal: true,
|
|
499
|
+
});
|
|
501
500
|
} else if (info.isMainBranch) {
|
|
502
501
|
console.log("On main branch - deploying to workspace\n");
|
|
503
502
|
} else if (info.gitBranch) {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
console.log(
|
|
512
|
-
`Using existing Tinybird branch '${tinybirdName}'\n`
|
|
513
|
-
);
|
|
514
|
-
}
|
|
503
|
+
output.showBranchInfo({
|
|
504
|
+
gitBranch: info.gitBranch,
|
|
505
|
+
tinybirdBranch: info.tinybirdBranch?.name ?? null,
|
|
506
|
+
wasCreated: info.wasCreated ?? false,
|
|
507
|
+
dashboardUrl: info.dashboardUrl,
|
|
508
|
+
isLocal: false,
|
|
509
|
+
});
|
|
515
510
|
} else {
|
|
516
511
|
console.log("Not in a git repository - deploying to workspace\n");
|
|
517
512
|
}
|
|
@@ -659,6 +654,9 @@ function createCli(): Command {
|
|
|
659
654
|
console.log(` Tinybird branch: ${result.tinybirdBranch.name}`);
|
|
660
655
|
console.log(` Branch ID: ${result.tinybirdBranch.id}`);
|
|
661
656
|
console.log(` Created: ${result.tinybirdBranch.created_at}`);
|
|
657
|
+
if (result.dashboardUrl) {
|
|
658
|
+
console.log(` Dashboard: ${result.dashboardUrl}`);
|
|
659
|
+
}
|
|
662
660
|
} else if (!result.isMainBranch && result.tinybirdBranchName) {
|
|
663
661
|
console.log(" Tinybird branch: not created yet");
|
|
664
662
|
console.log(" (Run 'npx tinybird dev' to create it)");
|
package/src/cli/output.ts
CHANGED
|
@@ -239,6 +239,59 @@ export function showDeployFailure(): void {
|
|
|
239
239
|
error(`\n✗ Deploy failed`);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Branch info for display
|
|
244
|
+
*/
|
|
245
|
+
export interface BranchDisplayInfo {
|
|
246
|
+
/** Git branch name */
|
|
247
|
+
gitBranch: string | null;
|
|
248
|
+
/** Tinybird branch name */
|
|
249
|
+
tinybirdBranch: string | null;
|
|
250
|
+
/** Whether the branch was newly created */
|
|
251
|
+
wasCreated: boolean;
|
|
252
|
+
/** Dashboard URL for the branch */
|
|
253
|
+
dashboardUrl?: string;
|
|
254
|
+
/** Whether using local mode */
|
|
255
|
+
isLocal?: boolean;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Show branch information in a compact, styled format
|
|
260
|
+
*/
|
|
261
|
+
export function showBranchInfo(info: BranchDisplayInfo): void {
|
|
262
|
+
const status = info.wasCreated
|
|
263
|
+
? colorize("✓ created", "green")
|
|
264
|
+
: colorize("existing", "gray");
|
|
265
|
+
|
|
266
|
+
if (info.isLocal) {
|
|
267
|
+
// Show git branch
|
|
268
|
+
if (info.gitBranch) {
|
|
269
|
+
console.log(`» Git branch: ${info.gitBranch}`);
|
|
270
|
+
}
|
|
271
|
+
// Show local workspace
|
|
272
|
+
const name = info.tinybirdBranch ?? "unknown";
|
|
273
|
+
console.log(`» Local workspace: ${name} ${status}`);
|
|
274
|
+
// Show dashboard URL
|
|
275
|
+
if (info.dashboardUrl) {
|
|
276
|
+
console.log(colorize(` ↳ ${info.dashboardUrl}`, "gray"));
|
|
277
|
+
}
|
|
278
|
+
} else {
|
|
279
|
+
// Show git branch
|
|
280
|
+
if (info.gitBranch) {
|
|
281
|
+
console.log(`» Git branch: ${info.gitBranch}`);
|
|
282
|
+
}
|
|
283
|
+
// Show Tinybird branch
|
|
284
|
+
if (info.tinybirdBranch) {
|
|
285
|
+
console.log(`» Tinybird branch: ${info.tinybirdBranch} ${status}`);
|
|
286
|
+
}
|
|
287
|
+
// Show dashboard URL
|
|
288
|
+
if (info.dashboardUrl) {
|
|
289
|
+
console.log(colorize(` ↳ ${info.dashboardUrl}`, "gray"));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
console.log("");
|
|
293
|
+
}
|
|
294
|
+
|
|
242
295
|
/**
|
|
243
296
|
* Output object containing all output functions
|
|
244
297
|
*/
|
|
@@ -265,4 +318,5 @@ export const output = {
|
|
|
265
318
|
showValidatingDeployment,
|
|
266
319
|
showDeploySuccess,
|
|
267
320
|
showDeployFailure,
|
|
321
|
+
showBranchInfo,
|
|
268
322
|
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { TinybirdClient, createClient } from "./base.js";
|
|
3
|
+
|
|
4
|
+
describe("TinybirdClient", () => {
|
|
5
|
+
describe("constructor", () => {
|
|
6
|
+
it("throws error when baseUrl is missing", () => {
|
|
7
|
+
expect(() => new TinybirdClient({ baseUrl: "", token: "test-token" })).toThrow(
|
|
8
|
+
"baseUrl is required"
|
|
9
|
+
);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("throws error when token is missing", () => {
|
|
13
|
+
expect(
|
|
14
|
+
() => new TinybirdClient({ baseUrl: "https://api.tinybird.co", token: "" })
|
|
15
|
+
).toThrow("token is required");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("creates client with valid config", () => {
|
|
19
|
+
const client = new TinybirdClient({
|
|
20
|
+
baseUrl: "https://api.tinybird.co",
|
|
21
|
+
token: "test-token",
|
|
22
|
+
});
|
|
23
|
+
expect(client).toBeInstanceOf(TinybirdClient);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("normalizes baseUrl by removing trailing slash", async () => {
|
|
27
|
+
const client = new TinybirdClient({
|
|
28
|
+
baseUrl: "https://api.tinybird.co/",
|
|
29
|
+
token: "test-token",
|
|
30
|
+
});
|
|
31
|
+
const context = await client.getContext();
|
|
32
|
+
expect(context.baseUrl).toBe("https://api.tinybird.co");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("getContext", () => {
|
|
37
|
+
it("returns correct context in non-devMode", async () => {
|
|
38
|
+
const client = new TinybirdClient({
|
|
39
|
+
baseUrl: "https://api.tinybird.co",
|
|
40
|
+
token: "test-token",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const context = await client.getContext();
|
|
44
|
+
|
|
45
|
+
expect(context).toEqual({
|
|
46
|
+
token: "test-token",
|
|
47
|
+
baseUrl: "https://api.tinybird.co",
|
|
48
|
+
devMode: false,
|
|
49
|
+
isBranchToken: false,
|
|
50
|
+
branchName: null,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("returns devMode: false when devMode is not set", async () => {
|
|
55
|
+
const client = new TinybirdClient({
|
|
56
|
+
baseUrl: "https://api.tinybird.co",
|
|
57
|
+
token: "test-token",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const context = await client.getContext();
|
|
61
|
+
expect(context.devMode).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("returns isBranchToken: false when not in devMode", async () => {
|
|
65
|
+
const client = new TinybirdClient({
|
|
66
|
+
baseUrl: "https://api.tinybird.co",
|
|
67
|
+
token: "test-token",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const context = await client.getContext();
|
|
71
|
+
expect(context.isBranchToken).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("returns branchName: null when not in devMode", async () => {
|
|
75
|
+
const client = new TinybirdClient({
|
|
76
|
+
baseUrl: "https://api.tinybird.co",
|
|
77
|
+
token: "test-token",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const context = await client.getContext();
|
|
81
|
+
expect(context.branchName).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("caches the resolved context", async () => {
|
|
85
|
+
const client = new TinybirdClient({
|
|
86
|
+
baseUrl: "https://api.tinybird.co",
|
|
87
|
+
token: "test-token",
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const context1 = await client.getContext();
|
|
91
|
+
const context2 = await client.getContext();
|
|
92
|
+
|
|
93
|
+
expect(context1).toBe(context2);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("works with different baseUrl regions", async () => {
|
|
97
|
+
const client = new TinybirdClient({
|
|
98
|
+
baseUrl: "https://api.us-east.tinybird.co",
|
|
99
|
+
token: "us-token",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const context = await client.getContext();
|
|
103
|
+
expect(context.baseUrl).toBe("https://api.us-east.tinybird.co");
|
|
104
|
+
expect(context.token).toBe("us-token");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("createClient", () => {
|
|
109
|
+
it("creates a TinybirdClient instance", () => {
|
|
110
|
+
const client = createClient({
|
|
111
|
+
baseUrl: "https://api.tinybird.co",
|
|
112
|
+
token: "test-token",
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expect(client).toBeInstanceOf(TinybirdClient);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("passes config to the client correctly", async () => {
|
|
119
|
+
const client = createClient({
|
|
120
|
+
baseUrl: "https://api.tinybird.co",
|
|
121
|
+
token: "my-token",
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const context = await client.getContext();
|
|
125
|
+
expect(context.token).toBe("my-token");
|
|
126
|
+
expect(context.baseUrl).toBe("https://api.tinybird.co");
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
package/src/client/base.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
6
|
ClientConfig,
|
|
7
|
+
ClientContext,
|
|
7
8
|
QueryResult,
|
|
8
9
|
IngestResult,
|
|
9
10
|
QueryOptions,
|
|
@@ -52,8 +53,8 @@ interface ResolvedTokenInfo {
|
|
|
52
53
|
export class TinybirdClient {
|
|
53
54
|
private readonly config: ClientConfig;
|
|
54
55
|
private readonly apisByToken = new Map<string, TinybirdApi>();
|
|
55
|
-
private
|
|
56
|
-
private
|
|
56
|
+
private contextPromise: Promise<ClientContext> | null = null;
|
|
57
|
+
private resolvedContext: ClientContext | null = null;
|
|
57
58
|
|
|
58
59
|
constructor(config: ClientConfig) {
|
|
59
60
|
// Validate required config
|
|
@@ -75,31 +76,55 @@ export class TinybirdClient {
|
|
|
75
76
|
* Get the effective token, resolving branch token in dev mode if needed
|
|
76
77
|
*/
|
|
77
78
|
private async getToken(): Promise<string> {
|
|
79
|
+
const context = await this.resolveContext();
|
|
80
|
+
return context.token;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the client context, including branch token resolution in dev mode
|
|
85
|
+
* This is the single source of truth for all context data
|
|
86
|
+
*/
|
|
87
|
+
private async resolveContext(): Promise<ClientContext> {
|
|
78
88
|
// If already resolved, return it
|
|
79
|
-
if (this.
|
|
80
|
-
return this.
|
|
89
|
+
if (this.resolvedContext) {
|
|
90
|
+
return this.resolvedContext;
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
// If not in dev mode, use the configured token
|
|
84
94
|
if (!this.config.devMode) {
|
|
85
|
-
this.
|
|
86
|
-
|
|
95
|
+
this.resolvedContext = this.buildContext({
|
|
96
|
+
token: this.config.token,
|
|
97
|
+
isBranchToken: false,
|
|
98
|
+
});
|
|
99
|
+
return this.resolvedContext;
|
|
87
100
|
}
|
|
88
101
|
|
|
89
102
|
// In dev mode, lazily resolve the branch token
|
|
90
|
-
if (!this.
|
|
91
|
-
this.
|
|
103
|
+
if (!this.contextPromise) {
|
|
104
|
+
this.contextPromise = this.resolveBranchContext();
|
|
92
105
|
}
|
|
93
106
|
|
|
94
|
-
|
|
95
|
-
this.
|
|
96
|
-
|
|
107
|
+
this.resolvedContext = await this.contextPromise;
|
|
108
|
+
return this.resolvedContext;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Build the client context from resolved token info
|
|
113
|
+
*/
|
|
114
|
+
private buildContext(tokenInfo: ResolvedTokenInfo): ClientContext {
|
|
115
|
+
return {
|
|
116
|
+
token: tokenInfo.token,
|
|
117
|
+
baseUrl: this.config.baseUrl,
|
|
118
|
+
devMode: this.config.devMode ?? false,
|
|
119
|
+
isBranchToken: tokenInfo.isBranchToken,
|
|
120
|
+
branchName: tokenInfo.branchName ?? null,
|
|
121
|
+
};
|
|
97
122
|
}
|
|
98
123
|
|
|
99
124
|
/**
|
|
100
|
-
* Resolve the branch
|
|
125
|
+
* Resolve the branch context in dev mode
|
|
101
126
|
*/
|
|
102
|
-
private async
|
|
127
|
+
private async resolveBranchContext(): Promise<ClientContext> {
|
|
103
128
|
try {
|
|
104
129
|
// Dynamic import to avoid circular dependencies and to keep CLI code
|
|
105
130
|
// out of the client bundle when not using dev mode
|
|
@@ -110,7 +135,7 @@ export class TinybirdClient {
|
|
|
110
135
|
|
|
111
136
|
// If on main branch, use the workspace token
|
|
112
137
|
if (config.isMainBranch || !config.tinybirdBranch) {
|
|
113
|
-
return { token: this.config.token, isBranchToken: false };
|
|
138
|
+
return this.buildContext({ token: this.config.token, isBranchToken: false });
|
|
114
139
|
}
|
|
115
140
|
|
|
116
141
|
const branchName = config.tinybirdBranch;
|
|
@@ -123,17 +148,17 @@ export class TinybirdClient {
|
|
|
123
148
|
|
|
124
149
|
if (!branch.token) {
|
|
125
150
|
// Fall back to workspace token if no branch token
|
|
126
|
-
return { token: this.config.token, isBranchToken: false };
|
|
151
|
+
return this.buildContext({ token: this.config.token, isBranchToken: false });
|
|
127
152
|
}
|
|
128
153
|
|
|
129
|
-
return {
|
|
154
|
+
return this.buildContext({
|
|
130
155
|
token: branch.token,
|
|
131
156
|
isBranchToken: true,
|
|
132
157
|
branchName,
|
|
133
|
-
};
|
|
158
|
+
});
|
|
134
159
|
} catch {
|
|
135
160
|
// If anything fails, fall back to the workspace token
|
|
136
|
-
return { token: this.config.token, isBranchToken: false };
|
|
161
|
+
return this.buildContext({ token: this.config.token, isBranchToken: false });
|
|
137
162
|
}
|
|
138
163
|
}
|
|
139
164
|
|
|
@@ -217,6 +242,31 @@ export class TinybirdClient {
|
|
|
217
242
|
}
|
|
218
243
|
}
|
|
219
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Get the current client context
|
|
247
|
+
*
|
|
248
|
+
* Returns information about the resolved configuration including the token being used,
|
|
249
|
+
* API URL, dev mode status, and branch information.
|
|
250
|
+
*
|
|
251
|
+
* @returns Client context with resolved configuration
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* const client = createClient({
|
|
256
|
+
* baseUrl: 'https://api.tinybird.co',
|
|
257
|
+
* token: process.env.TINYBIRD_TOKEN,
|
|
258
|
+
* devMode: true,
|
|
259
|
+
* });
|
|
260
|
+
*
|
|
261
|
+
* const context = await client.getContext();
|
|
262
|
+
* console.log(context.branchName); // 'feature_my_branch'
|
|
263
|
+
* console.log(context.isBranchToken); // true
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
async getContext(): Promise<ClientContext> {
|
|
267
|
+
return this.resolveContext();
|
|
268
|
+
}
|
|
269
|
+
|
|
220
270
|
private getApi(token: string): TinybirdApi {
|
|
221
271
|
const existing = this.apisByToken.get(token);
|
|
222
272
|
if (existing) {
|
package/src/client/types.ts
CHANGED
|
@@ -147,6 +147,23 @@ export interface IngestOptions {
|
|
|
147
147
|
wait?: boolean;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Client context information
|
|
152
|
+
* Contains the resolved configuration and state of the client
|
|
153
|
+
*/
|
|
154
|
+
export interface ClientContext {
|
|
155
|
+
/** The resolved token being used for requests (workspace or branch token) */
|
|
156
|
+
token: string;
|
|
157
|
+
/** Tinybird API base URL */
|
|
158
|
+
baseUrl: string;
|
|
159
|
+
/** Whether dev mode is enabled */
|
|
160
|
+
devMode: boolean;
|
|
161
|
+
/** Whether the resolved token is a branch token (vs workspace token) */
|
|
162
|
+
isBranchToken: boolean;
|
|
163
|
+
/** The branch name if using a branch token */
|
|
164
|
+
branchName: string | null;
|
|
165
|
+
}
|
|
166
|
+
|
|
150
167
|
/**
|
|
151
168
|
* Base interface for typed pipe endpoints
|
|
152
169
|
*/
|
package/src/index.ts
CHANGED
|
@@ -203,6 +203,7 @@ export { TinybirdClient, createClient } from "./client/base.js";
|
|
|
203
203
|
export { TinybirdError } from "./client/types.js";
|
|
204
204
|
export type {
|
|
205
205
|
ClientConfig,
|
|
206
|
+
ClientContext,
|
|
206
207
|
QueryResult,
|
|
207
208
|
IngestResult,
|
|
208
209
|
QueryOptions,
|
|
@@ -235,3 +236,12 @@ export {
|
|
|
235
236
|
resolveToken,
|
|
236
237
|
clearTokenCache,
|
|
237
238
|
} from "./client/preview.js";
|
|
239
|
+
|
|
240
|
+
// ============ Dashboard URL Utilities ============
|
|
241
|
+
export {
|
|
242
|
+
parseApiUrl,
|
|
243
|
+
getDashboardUrl,
|
|
244
|
+
getBranchDashboardUrl,
|
|
245
|
+
getLocalDashboardUrl,
|
|
246
|
+
} from "./api/dashboard.js";
|
|
247
|
+
export type { RegionInfo } from "./api/dashboard.js";
|