@tinybirdco/sdk 0.0.19 → 0.0.21
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 +47 -0
- package/dist/api/api.d.ts +89 -0
- package/dist/api/api.d.ts.map +1 -0
- package/dist/api/api.js +218 -0
- package/dist/api/api.js.map +1 -0
- package/dist/api/api.test.d.ts +2 -0
- package/dist/api/api.test.d.ts.map +1 -0
- package/dist/api/api.test.js +226 -0
- package/dist/api/api.test.js.map +1 -0
- 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 +3 -17
- package/dist/client/base.d.ts.map +1 -1
- package/dist/client/base.js +31 -153
- package/dist/client/base.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/api/api.test.ts +295 -0
- package/src/api/api.ts +345 -0
- 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.ts +34 -181
- package/src/index.ts +23 -0
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
getLocalWorkspaceName,
|
|
13
13
|
LocalNotRunningError,
|
|
14
14
|
} from "../../api/local.js";
|
|
15
|
+
import { getWorkspace } from "../../api/workspaces.js";
|
|
16
|
+
import { getBranchDashboardUrl, getLocalDashboardUrl } from "../../api/dashboard.js";
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Build command options
|
|
@@ -27,6 +29,22 @@ export interface BuildCommandOptions {
|
|
|
27
29
|
devModeOverride?: DevMode;
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Branch info included in build result
|
|
34
|
+
*/
|
|
35
|
+
export interface BuildBranchInfo {
|
|
36
|
+
/** Git branch name */
|
|
37
|
+
gitBranch: string | null;
|
|
38
|
+
/** Tinybird branch name */
|
|
39
|
+
tinybirdBranch: string | null;
|
|
40
|
+
/** Whether the branch was newly created */
|
|
41
|
+
wasCreated: boolean;
|
|
42
|
+
/** Dashboard URL for the branch */
|
|
43
|
+
dashboardUrl?: string;
|
|
44
|
+
/** Whether using local mode */
|
|
45
|
+
isLocal: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
/**
|
|
31
49
|
* Build command result
|
|
32
50
|
*/
|
|
@@ -37,6 +55,8 @@ export interface BuildCommandResult {
|
|
|
37
55
|
build?: BuildFromIncludeResult;
|
|
38
56
|
/** Build API result (if not dry run) */
|
|
39
57
|
deploy?: BuildApiResult;
|
|
58
|
+
/** Branch info (when building to a branch) */
|
|
59
|
+
branchInfo?: BuildBranchInfo;
|
|
40
60
|
/** Error message if failed */
|
|
41
61
|
error?: string;
|
|
42
62
|
/** Duration in milliseconds */
|
|
@@ -101,6 +121,7 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
|
|
|
101
121
|
}
|
|
102
122
|
|
|
103
123
|
let deployResult: BuildApiResult;
|
|
124
|
+
let branchInfo: BuildBranchInfo | undefined;
|
|
104
125
|
|
|
105
126
|
// Handle local mode
|
|
106
127
|
if (devMode === "local") {
|
|
@@ -123,6 +144,14 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
|
|
|
123
144
|
console.log(`[debug] Workspace ${wasCreated ? "created" : "found"}: ${workspace.name}`);
|
|
124
145
|
}
|
|
125
146
|
|
|
147
|
+
branchInfo = {
|
|
148
|
+
gitBranch: config.gitBranch,
|
|
149
|
+
tinybirdBranch: workspaceName,
|
|
150
|
+
wasCreated,
|
|
151
|
+
dashboardUrl: getLocalDashboardUrl(workspaceName),
|
|
152
|
+
isLocal: true,
|
|
153
|
+
};
|
|
154
|
+
|
|
126
155
|
// Always use /v1/build for local (no deploy endpoint)
|
|
127
156
|
deployResult = await buildToTinybird(
|
|
128
157
|
{
|
|
@@ -197,6 +226,21 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
|
|
|
197
226
|
if (debug) {
|
|
198
227
|
console.log(`[debug] Using branch token for branch: ${config.tinybirdBranch}`);
|
|
199
228
|
}
|
|
229
|
+
|
|
230
|
+
// Get workspace name for dashboard URL
|
|
231
|
+
const workspace = await getWorkspace({
|
|
232
|
+
baseUrl: config.baseUrl,
|
|
233
|
+
token: config.token,
|
|
234
|
+
});
|
|
235
|
+
const dashboardUrl = getBranchDashboardUrl(config.baseUrl, workspace.name, config.tinybirdBranch!) ?? undefined;
|
|
236
|
+
|
|
237
|
+
branchInfo = {
|
|
238
|
+
gitBranch: config.gitBranch,
|
|
239
|
+
tinybirdBranch: config.tinybirdBranch,
|
|
240
|
+
wasCreated: tinybirdBranch.wasCreated ?? false,
|
|
241
|
+
dashboardUrl,
|
|
242
|
+
isLocal: false,
|
|
243
|
+
};
|
|
200
244
|
} catch (error) {
|
|
201
245
|
return {
|
|
202
246
|
success: false,
|
|
@@ -231,6 +275,7 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
|
|
|
231
275
|
success: false,
|
|
232
276
|
build: buildResult,
|
|
233
277
|
deploy: deployResult,
|
|
278
|
+
branchInfo,
|
|
234
279
|
error: deployResult.error,
|
|
235
280
|
durationMs: Date.now() - startTime,
|
|
236
281
|
};
|
|
@@ -240,6 +285,7 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
|
|
|
240
285
|
success: true,
|
|
241
286
|
build: buildResult,
|
|
242
287
|
deploy: deployResult,
|
|
288
|
+
branchInfo,
|
|
243
289
|
durationMs: Date.now() - startTime,
|
|
244
290
|
};
|
|
245
291
|
}
|
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
|
};
|
package/src/client/base.ts
CHANGED
|
@@ -8,15 +8,9 @@ import type {
|
|
|
8
8
|
IngestResult,
|
|
9
9
|
QueryOptions,
|
|
10
10
|
IngestOptions,
|
|
11
|
-
TinybirdErrorResponse,
|
|
12
11
|
} from "./types.js";
|
|
13
12
|
import { TinybirdError } from "./types.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Default timeout for requests (30 seconds)
|
|
18
|
-
*/
|
|
19
|
-
const DEFAULT_TIMEOUT = 30000;
|
|
13
|
+
import { TinybirdApi, TinybirdApiError } from "../api/api.js";
|
|
20
14
|
|
|
21
15
|
/**
|
|
22
16
|
* Resolved token info from dev mode
|
|
@@ -57,7 +51,7 @@ interface ResolvedTokenInfo {
|
|
|
57
51
|
*/
|
|
58
52
|
export class TinybirdClient {
|
|
59
53
|
private readonly config: ClientConfig;
|
|
60
|
-
private readonly
|
|
54
|
+
private readonly apisByToken = new Map<string, TinybirdApi>();
|
|
61
55
|
private tokenPromise: Promise<ResolvedTokenInfo> | null = null;
|
|
62
56
|
private resolvedToken: string | null = null;
|
|
63
57
|
|
|
@@ -75,8 +69,6 @@ export class TinybirdClient {
|
|
|
75
69
|
...config,
|
|
76
70
|
baseUrl: config.baseUrl.replace(/\/$/, ""),
|
|
77
71
|
};
|
|
78
|
-
|
|
79
|
-
this.fetchFn = createTinybirdFetcher(config.fetch ?? globalThis.fetch);
|
|
80
72
|
}
|
|
81
73
|
|
|
82
74
|
/**
|
|
@@ -159,39 +151,12 @@ export class TinybirdClient {
|
|
|
159
151
|
options: QueryOptions = {}
|
|
160
152
|
): Promise<QueryResult<T>> {
|
|
161
153
|
const token = await this.getToken();
|
|
162
|
-
const url = new URL(`/v0/pipes/${pipeName}.json`, this.config.baseUrl);
|
|
163
|
-
|
|
164
|
-
// Add parameters to query string
|
|
165
|
-
for (const [key, value] of Object.entries(params)) {
|
|
166
|
-
if (value !== undefined && value !== null) {
|
|
167
|
-
if (Array.isArray(value)) {
|
|
168
|
-
// Handle array parameters
|
|
169
|
-
for (const item of value) {
|
|
170
|
-
url.searchParams.append(key, String(item));
|
|
171
|
-
}
|
|
172
|
-
} else if (value instanceof Date) {
|
|
173
|
-
// Handle Date objects
|
|
174
|
-
url.searchParams.set(key, value.toISOString());
|
|
175
|
-
} else {
|
|
176
|
-
url.searchParams.set(key, String(value));
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
154
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
},
|
|
186
|
-
signal: this.createAbortSignal(options.timeout, options.signal),
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
if (!response.ok) {
|
|
190
|
-
await this.handleErrorResponse(response);
|
|
155
|
+
try {
|
|
156
|
+
return await this.getApi(token).query<T>(pipeName, params, options);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
this.rethrowApiError(error);
|
|
191
159
|
}
|
|
192
|
-
|
|
193
|
-
const result = (await response.json()) as QueryResult<T>;
|
|
194
|
-
return result;
|
|
195
160
|
}
|
|
196
161
|
|
|
197
162
|
/**
|
|
@@ -223,39 +188,13 @@ export class TinybirdClient {
|
|
|
223
188
|
events: T[],
|
|
224
189
|
options: IngestOptions = {}
|
|
225
190
|
): Promise<IngestResult> {
|
|
226
|
-
if (events.length === 0) {
|
|
227
|
-
return { successful_rows: 0, quarantined_rows: 0 };
|
|
228
|
-
}
|
|
229
|
-
|
|
230
191
|
const token = await this.getToken();
|
|
231
|
-
const url = new URL("/v0/events", this.config.baseUrl);
|
|
232
|
-
url.searchParams.set("name", datasourceName);
|
|
233
192
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Convert events to NDJSON format
|
|
239
|
-
const ndjson = events
|
|
240
|
-
.map((event) => JSON.stringify(this.serializeEvent(event)))
|
|
241
|
-
.join("\n");
|
|
242
|
-
|
|
243
|
-
const response = await this.fetch(url.toString(), {
|
|
244
|
-
method: "POST",
|
|
245
|
-
headers: {
|
|
246
|
-
Authorization: `Bearer ${token}`,
|
|
247
|
-
"Content-Type": "application/x-ndjson",
|
|
248
|
-
},
|
|
249
|
-
body: ndjson,
|
|
250
|
-
signal: this.createAbortSignal(options.timeout, options.signal),
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
if (!response.ok) {
|
|
254
|
-
await this.handleErrorResponse(response);
|
|
193
|
+
try {
|
|
194
|
+
return await this.getApi(token).ingestBatch(datasourceName, events, options);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
this.rethrowApiError(error);
|
|
255
197
|
}
|
|
256
|
-
|
|
257
|
-
const result = (await response.json()) as IngestResult;
|
|
258
|
-
return result;
|
|
259
198
|
}
|
|
260
199
|
|
|
261
200
|
/**
|
|
@@ -270,127 +209,41 @@ export class TinybirdClient {
|
|
|
270
209
|
options: QueryOptions = {}
|
|
271
210
|
): Promise<QueryResult<T>> {
|
|
272
211
|
const token = await this.getToken();
|
|
273
|
-
const url = new URL("/v0/sql", this.config.baseUrl);
|
|
274
|
-
|
|
275
|
-
const response = await this.fetch(url.toString(), {
|
|
276
|
-
method: "POST",
|
|
277
|
-
headers: {
|
|
278
|
-
Authorization: `Bearer ${token}`,
|
|
279
|
-
"Content-Type": "text/plain",
|
|
280
|
-
},
|
|
281
|
-
body: sql,
|
|
282
|
-
signal: this.createAbortSignal(options.timeout, options.signal),
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
if (!response.ok) {
|
|
286
|
-
await this.handleErrorResponse(response);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const result = (await response.json()) as QueryResult<T>;
|
|
290
|
-
return result;
|
|
291
|
-
}
|
|
292
212
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
event: T
|
|
298
|
-
): Record<string, unknown> {
|
|
299
|
-
const serialized: Record<string, unknown> = {};
|
|
300
|
-
|
|
301
|
-
for (const [key, value] of Object.entries(event)) {
|
|
302
|
-
if (value instanceof Date) {
|
|
303
|
-
// Convert Date to ISO string
|
|
304
|
-
serialized[key] = value.toISOString();
|
|
305
|
-
} else if (value instanceof Map) {
|
|
306
|
-
// Convert Map to object
|
|
307
|
-
serialized[key] = Object.fromEntries(value);
|
|
308
|
-
} else if (typeof value === "bigint") {
|
|
309
|
-
// Convert BigInt to string (ClickHouse will parse it)
|
|
310
|
-
serialized[key] = value.toString();
|
|
311
|
-
} else if (Array.isArray(value)) {
|
|
312
|
-
// Recursively serialize array elements
|
|
313
|
-
serialized[key] = value.map((item) =>
|
|
314
|
-
typeof item === "object" && item !== null
|
|
315
|
-
? this.serializeEvent(item as Record<string, unknown>)
|
|
316
|
-
: item instanceof Date
|
|
317
|
-
? item.toISOString()
|
|
318
|
-
: item
|
|
319
|
-
);
|
|
320
|
-
} else if (typeof value === "object" && value !== null) {
|
|
321
|
-
// Recursively serialize nested objects
|
|
322
|
-
serialized[key] = this.serializeEvent(value as Record<string, unknown>);
|
|
323
|
-
} else {
|
|
324
|
-
serialized[key] = value;
|
|
325
|
-
}
|
|
213
|
+
try {
|
|
214
|
+
return await this.getApi(token).sql<T>(sql, options);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
this.rethrowApiError(error);
|
|
326
217
|
}
|
|
327
|
-
|
|
328
|
-
return serialized;
|
|
329
218
|
}
|
|
330
219
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
timeout?: number,
|
|
336
|
-
existingSignal?: AbortSignal
|
|
337
|
-
): AbortSignal | undefined {
|
|
338
|
-
const timeoutMs = timeout ?? this.config.timeout ?? DEFAULT_TIMEOUT;
|
|
339
|
-
|
|
340
|
-
// If no timeout and no existing signal, return undefined
|
|
341
|
-
if (!timeoutMs && !existingSignal) {
|
|
342
|
-
return undefined;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// If only existing signal, return it
|
|
346
|
-
if (!timeoutMs && existingSignal) {
|
|
347
|
-
return existingSignal;
|
|
220
|
+
private getApi(token: string): TinybirdApi {
|
|
221
|
+
const existing = this.apisByToken.get(token);
|
|
222
|
+
if (existing) {
|
|
223
|
+
return existing;
|
|
348
224
|
}
|
|
349
225
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
226
|
+
const api = new TinybirdApi({
|
|
227
|
+
baseUrl: this.config.baseUrl,
|
|
228
|
+
token,
|
|
229
|
+
fetch: this.config.fetch,
|
|
230
|
+
timeout: this.config.timeout,
|
|
231
|
+
});
|
|
357
232
|
|
|
358
|
-
|
|
359
|
-
return
|
|
233
|
+
this.apisByToken.set(token, api);
|
|
234
|
+
return api;
|
|
360
235
|
}
|
|
361
236
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
try {
|
|
370
|
-
rawBody = await response.text();
|
|
371
|
-
errorResponse = JSON.parse(rawBody) as TinybirdErrorResponse;
|
|
372
|
-
} catch {
|
|
373
|
-
// Failed to parse error response - include raw body in message
|
|
374
|
-
if (rawBody) {
|
|
375
|
-
throw new TinybirdError(
|
|
376
|
-
`Request failed with status ${response.status}: ${rawBody}`,
|
|
377
|
-
response.status,
|
|
378
|
-
undefined
|
|
379
|
-
);
|
|
380
|
-
}
|
|
237
|
+
private rethrowApiError(error: unknown): never {
|
|
238
|
+
if (error instanceof TinybirdApiError) {
|
|
239
|
+
throw new TinybirdError(
|
|
240
|
+
error.message,
|
|
241
|
+
error.statusCode,
|
|
242
|
+
error.response
|
|
243
|
+
);
|
|
381
244
|
}
|
|
382
245
|
|
|
383
|
-
|
|
384
|
-
errorResponse?.error ?? `Request failed with status ${response.status}`;
|
|
385
|
-
|
|
386
|
-
throw new TinybirdError(message, response.status, errorResponse);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Internal fetch wrapper
|
|
391
|
-
*/
|
|
392
|
-
private fetch(url: string, init?: RequestInit): Promise<Response> {
|
|
393
|
-
return this.fetchFn(url, init);
|
|
246
|
+
throw error;
|
|
394
247
|
}
|
|
395
248
|
}
|
|
396
249
|
|
package/src/index.ts
CHANGED
|
@@ -214,6 +214,20 @@ export type {
|
|
|
214
214
|
TypedDatasourceIngest,
|
|
215
215
|
} from "./client/types.js";
|
|
216
216
|
|
|
217
|
+
// ============ Public Tinybird API ============
|
|
218
|
+
export {
|
|
219
|
+
TinybirdApi,
|
|
220
|
+
TinybirdApiError,
|
|
221
|
+
createTinybirdApi,
|
|
222
|
+
createTinybirdApiWrapper,
|
|
223
|
+
} from "./api/api.js";
|
|
224
|
+
export type {
|
|
225
|
+
TinybirdApiConfig,
|
|
226
|
+
TinybirdApiQueryOptions,
|
|
227
|
+
TinybirdApiIngestOptions,
|
|
228
|
+
TinybirdApiRequestInit,
|
|
229
|
+
} from "./api/api.js";
|
|
230
|
+
|
|
217
231
|
// ============ Preview Environment ============
|
|
218
232
|
export {
|
|
219
233
|
isPreviewEnvironment,
|
|
@@ -221,3 +235,12 @@ export {
|
|
|
221
235
|
resolveToken,
|
|
222
236
|
clearTokenCache,
|
|
223
237
|
} from "./client/preview.js";
|
|
238
|
+
|
|
239
|
+
// ============ Dashboard URL Utilities ============
|
|
240
|
+
export {
|
|
241
|
+
parseApiUrl,
|
|
242
|
+
getDashboardUrl,
|
|
243
|
+
getBranchDashboardUrl,
|
|
244
|
+
getLocalDashboardUrl,
|
|
245
|
+
} from "./api/dashboard.js";
|
|
246
|
+
export type { RegionInfo } from "./api/dashboard.js";
|