@tinybirdco/sdk 0.0.6 → 0.0.7

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 (88) hide show
  1. package/dist/api/branches.d.ts.map +1 -1
  2. package/dist/api/branches.js +6 -5
  3. package/dist/api/branches.js.map +1 -1
  4. package/dist/api/branches.test.js +32 -6
  5. package/dist/api/branches.test.js.map +1 -1
  6. package/dist/api/build.d.ts.map +1 -1
  7. package/dist/api/build.js +2 -1
  8. package/dist/api/build.js.map +1 -1
  9. package/dist/api/deploy.d.ts +1 -0
  10. package/dist/api/deploy.d.ts.map +1 -1
  11. package/dist/api/deploy.js +27 -6
  12. package/dist/api/deploy.js.map +1 -1
  13. package/dist/api/deploy.test.js +6 -2
  14. package/dist/api/deploy.test.js.map +1 -1
  15. package/dist/api/fetcher.d.ts +6 -0
  16. package/dist/api/fetcher.d.ts.map +1 -0
  17. package/dist/api/fetcher.js +13 -0
  18. package/dist/api/fetcher.js.map +1 -0
  19. package/dist/api/local.d.ts.map +1 -1
  20. package/dist/api/local.js +5 -4
  21. package/dist/api/local.js.map +1 -1
  22. package/dist/api/local.test.js.map +1 -1
  23. package/dist/api/resources.d.ts.map +1 -1
  24. package/dist/api/resources.js +5 -4
  25. package/dist/api/resources.js.map +1 -1
  26. package/dist/api/workspaces.d.ts.map +1 -1
  27. package/dist/api/workspaces.js +2 -1
  28. package/dist/api/workspaces.js.map +1 -1
  29. package/dist/api/workspaces.test.js +9 -1
  30. package/dist/api/workspaces.test.js.map +1 -1
  31. package/dist/cli/auth.d.ts.map +1 -1
  32. package/dist/cli/auth.js +2 -1
  33. package/dist/cli/auth.js.map +1 -1
  34. package/dist/cli/commands/deploy.d.ts +2 -0
  35. package/dist/cli/commands/deploy.d.ts.map +1 -1
  36. package/dist/cli/commands/deploy.js +3 -1
  37. package/dist/cli/commands/deploy.js.map +1 -1
  38. package/dist/cli/commands/init.d.ts +14 -0
  39. package/dist/cli/commands/init.d.ts.map +1 -1
  40. package/dist/cli/commands/init.js +264 -4
  41. package/dist/cli/commands/init.js.map +1 -1
  42. package/dist/cli/index.js +8 -3
  43. package/dist/cli/index.js.map +1 -1
  44. package/dist/client/base.d.ts.map +1 -1
  45. package/dist/client/base.js +2 -1
  46. package/dist/client/base.js.map +1 -1
  47. package/dist/schema/connection.d.ts.map +1 -1
  48. package/dist/schema/connection.js +3 -2
  49. package/dist/schema/connection.js.map +1 -1
  50. package/dist/schema/datasource.d.ts.map +1 -1
  51. package/dist/schema/datasource.js +3 -2
  52. package/dist/schema/datasource.js.map +1 -1
  53. package/dist/schema/params.d.ts.map +1 -1
  54. package/dist/schema/params.js +3 -2
  55. package/dist/schema/params.js.map +1 -1
  56. package/dist/schema/pipe.d.ts +2 -2
  57. package/dist/schema/pipe.d.ts.map +1 -1
  58. package/dist/schema/pipe.js +4 -4
  59. package/dist/schema/pipe.js.map +1 -1
  60. package/dist/schema/project.d.ts.map +1 -1
  61. package/dist/schema/project.js +3 -2
  62. package/dist/schema/project.js.map +1 -1
  63. package/dist/schema/types.d.ts.map +1 -1
  64. package/dist/schema/types.js +3 -2
  65. package/dist/schema/types.js.map +1 -1
  66. package/package.json +1 -1
  67. package/src/api/branches.test.ts +65 -57
  68. package/src/api/branches.ts +7 -5
  69. package/src/api/build.ts +2 -1
  70. package/src/api/deploy.test.ts +6 -2
  71. package/src/api/deploy.ts +35 -7
  72. package/src/api/fetcher.ts +17 -0
  73. package/src/api/local.test.ts +43 -31
  74. package/src/api/local.ts +5 -4
  75. package/src/api/resources.ts +5 -4
  76. package/src/api/workspaces.test.ts +15 -9
  77. package/src/api/workspaces.ts +3 -1
  78. package/src/cli/auth.ts +2 -1
  79. package/src/cli/commands/deploy.ts +6 -1
  80. package/src/cli/commands/init.ts +311 -6
  81. package/src/cli/index.ts +35 -11
  82. package/src/client/base.ts +3 -2
  83. package/src/schema/connection.ts +3 -2
  84. package/src/schema/datasource.ts +3 -2
  85. package/src/schema/params.ts +3 -2
  86. package/src/schema/pipe.ts +4 -4
  87. package/src/schema/project.ts +3 -2
  88. package/src/schema/types.ts +3 -2
package/src/api/deploy.ts CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import type { GeneratedResources } from "../generator/index.js";
7
7
  import type { BuildConfig, BuildApiResult } from "./build.js";
8
+ import { tinybirdFetch } from "./fetcher.js";
8
9
 
9
10
  /**
10
11
  * Deployment object returned by the /v1/deploy endpoint
@@ -75,7 +76,12 @@ export interface DeploymentStatusResponse {
75
76
  export async function deployToMain(
76
77
  config: BuildConfig,
77
78
  resources: GeneratedResources,
78
- options?: { debug?: boolean; pollIntervalMs?: number; maxPollAttempts?: number }
79
+ options?: {
80
+ debug?: boolean;
81
+ pollIntervalMs?: number;
82
+ maxPollAttempts?: number;
83
+ check?: boolean;
84
+ }
79
85
  ): Promise<BuildApiResult> {
80
86
  const debug = options?.debug ?? !!process.env.TINYBIRD_DEBUG;
81
87
  const pollIntervalMs = options?.pollIntervalMs ?? 1000;
@@ -117,7 +123,7 @@ export async function deployToMain(
117
123
  // Step 0: Clean up any stale non-live deployments that might block the new deployment
118
124
  try {
119
125
  const deploymentsUrl = `${baseUrl}/v1/deployments`;
120
- const deploymentsResponse = await fetch(deploymentsUrl, {
126
+ const deploymentsResponse = await tinybirdFetch(deploymentsUrl, {
121
127
  headers: {
122
128
  Authorization: `Bearer ${config.token}`,
123
129
  },
@@ -133,7 +139,7 @@ export async function deployToMain(
133
139
  if (debug) {
134
140
  console.log(`[debug] Cleaning up stale deployment: ${stale.id} (status: ${stale.status})`);
135
141
  }
136
- await fetch(`${baseUrl}/v1/deployments/${stale.id}`, {
142
+ await tinybirdFetch(`${baseUrl}/v1/deployments/${stale.id}`, {
137
143
  method: "DELETE",
138
144
  headers: {
139
145
  Authorization: `Bearer ${config.token}`,
@@ -149,13 +155,14 @@ export async function deployToMain(
149
155
  }
150
156
 
151
157
  // Step 1: Create deployment via /v1/deploy
152
- const deployUrl = `${baseUrl}/v1/deploy`;
158
+ const deployUrlBase = `${baseUrl}/v1/deploy`;
159
+ const deployUrl = options?.check ? `${deployUrlBase}?check=true` : deployUrlBase;
153
160
 
154
161
  if (debug) {
155
162
  console.log(`[debug] POST ${deployUrl}`);
156
163
  }
157
164
 
158
- const response = await fetch(deployUrl, {
165
+ const response = await tinybirdFetch(deployUrl, {
159
166
  method: "POST",
160
167
  headers: {
161
168
  Authorization: `Bearer ${config.token}`,
@@ -205,6 +212,27 @@ export async function deployToMain(
205
212
  };
206
213
  }
207
214
 
215
+ if (options?.check) {
216
+ if (body.result === "failed") {
217
+ return {
218
+ success: false,
219
+ result: "failed",
220
+ error: formatErrors(),
221
+ datasourceCount: resources.datasources.length,
222
+ pipeCount: resources.pipes.length,
223
+ connectionCount: resources.connections?.length ?? 0,
224
+ };
225
+ }
226
+
227
+ return {
228
+ success: true,
229
+ result: body.result ?? "success",
230
+ datasourceCount: resources.datasources.length,
231
+ pipeCount: resources.pipes.length,
232
+ connectionCount: resources.connections?.length ?? 0,
233
+ };
234
+ }
235
+
208
236
  // Handle API result
209
237
  if (body.result === "failed" || !body.deployment) {
210
238
  return {
@@ -236,7 +264,7 @@ export async function deployToMain(
236
264
  }
237
265
 
238
266
  const statusUrl = `${baseUrl}/v1/deployments/${deploymentId}`;
239
- const statusResponse = await fetch(statusUrl, {
267
+ const statusResponse = await tinybirdFetch(statusUrl, {
240
268
  headers: {
241
269
  Authorization: `Bearer ${config.token}`,
242
270
  },
@@ -294,7 +322,7 @@ export async function deployToMain(
294
322
  console.log(`[debug] POST ${setLiveUrl}`);
295
323
  }
296
324
 
297
- const setLiveResponse = await fetch(setLiveUrl, {
325
+ const setLiveResponse = await tinybirdFetch(setLiveUrl, {
298
326
  method: "POST",
299
327
  headers: {
300
328
  Authorization: `Bearer ${config.token}`,
@@ -0,0 +1,17 @@
1
+ export const TINYBIRD_FROM_PARAM = "ts-sdk";
2
+
3
+ export type TinybirdFetch = (url: string, init?: RequestInit) => Promise<Response>;
4
+
5
+ export function withTinybirdFromParam(url: string): string {
6
+ const parsedUrl = new URL(url);
7
+ parsedUrl.searchParams.set("from", TINYBIRD_FROM_PARAM);
8
+ return parsedUrl.toString();
9
+ }
10
+
11
+ export function createTinybirdFetcher(fetchFn: typeof fetch): TinybirdFetch {
12
+ return (url, init) => fetchFn(withTinybirdFromParam(url), init);
13
+ }
14
+
15
+ export function tinybirdFetch(url: string, init?: RequestInit): Promise<Response> {
16
+ return fetch(withTinybirdFromParam(url), init);
17
+ }
@@ -94,15 +94,18 @@ describe("Local API", () => {
94
94
  describe("listLocalWorkspaces", () => {
95
95
  it("returns list of workspaces", async () => {
96
96
  server.use(
97
- http.get(`${LOCAL_BASE_URL}/v1/user/workspaces`, () => {
98
- return HttpResponse.json({
99
- organization_id: "org-123",
100
- workspaces: [
101
- { id: "ws-1", name: "Workspace1", token: "token-1" },
102
- { id: "ws-2", name: "Workspace2", token: "token-2" },
103
- ],
104
- });
105
- })
97
+ http.get(
98
+ `${LOCAL_BASE_URL}/v1/user/workspaces`,
99
+ () => {
100
+ return HttpResponse.json({
101
+ organization_id: "org-123",
102
+ workspaces: [
103
+ { id: "ws-1", name: "Workspace1", token: "token-1" },
104
+ { id: "ws-2", name: "Workspace2", token: "token-2" },
105
+ ],
106
+ });
107
+ }
108
+ )
106
109
  );
107
110
 
108
111
  const result = await listLocalWorkspaces("admin-token");
@@ -118,9 +121,12 @@ describe("Local API", () => {
118
121
 
119
122
  it("throws LocalApiError on failure", async () => {
120
123
  server.use(
121
- http.get(`${LOCAL_BASE_URL}/v1/user/workspaces`, () => {
122
- return new HttpResponse("Not found", { status: 404 });
123
- })
124
+ http.get(
125
+ `${LOCAL_BASE_URL}/v1/user/workspaces`,
126
+ () => {
127
+ return new HttpResponse("Not found", { status: 404 });
128
+ }
129
+ )
124
130
  );
125
131
 
126
132
  await expect(listLocalWorkspaces("admin-token")).rejects.toThrow(LocalApiError);
@@ -170,14 +176,17 @@ describe("Local API", () => {
170
176
 
171
177
  it("returns existing workspace if found", async () => {
172
178
  server.use(
173
- http.get(`${LOCAL_BASE_URL}/v1/user/workspaces`, () => {
174
- return HttpResponse.json({
175
- organization_id: "org-123",
176
- workspaces: [
177
- { id: "existing-ws", name: "MyWorkspace", token: "existing-token" },
178
- ],
179
- });
180
- })
179
+ http.get(
180
+ `${LOCAL_BASE_URL}/v1/user/workspaces`,
181
+ () => {
182
+ return HttpResponse.json({
183
+ organization_id: "org-123",
184
+ workspaces: [
185
+ { id: "existing-ws", name: "MyWorkspace", token: "existing-token" },
186
+ ],
187
+ });
188
+ }
189
+ )
181
190
  );
182
191
 
183
192
  const result = await getOrCreateLocalWorkspace(tokens, "MyWorkspace");
@@ -191,21 +200,24 @@ describe("Local API", () => {
191
200
  let createCalled = false;
192
201
 
193
202
  server.use(
194
- http.get(`${LOCAL_BASE_URL}/v1/user/workspaces`, () => {
195
- // Return different response based on whether create was called
196
- if (createCalled) {
203
+ http.get(
204
+ `${LOCAL_BASE_URL}/v1/user/workspaces`,
205
+ () => {
206
+ // Return different response based on whether create was called
207
+ if (createCalled) {
208
+ return HttpResponse.json({
209
+ organization_id: "org-123",
210
+ workspaces: [
211
+ { id: "new-ws", name: "NewWorkspace", token: "new-token" },
212
+ ],
213
+ });
214
+ }
197
215
  return HttpResponse.json({
198
216
  organization_id: "org-123",
199
- workspaces: [
200
- { id: "new-ws", name: "NewWorkspace", token: "new-token" },
201
- ],
217
+ workspaces: [], // Empty initially
202
218
  });
203
219
  }
204
- return HttpResponse.json({
205
- organization_id: "org-123",
206
- workspaces: [], // Empty initially
207
- });
208
- }),
220
+ ),
209
221
  http.post(`${LOCAL_BASE_URL}/v1/workspaces`, () => {
210
222
  createCalled = true;
211
223
  return HttpResponse.json({
package/src/api/local.ts CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import * as crypto from "crypto";
7
7
  import { LOCAL_BASE_URL } from "../cli/config.js";
8
+ import { tinybirdFetch } from "./fetcher.js";
8
9
 
9
10
  /**
10
11
  * Tokens returned by the local /tokens endpoint
@@ -76,7 +77,7 @@ export class LocalApiError extends Error {
76
77
  */
77
78
  export async function isLocalRunning(): Promise<boolean> {
78
79
  try {
79
- const response = await fetch(`${LOCAL_BASE_URL}/tokens`, {
80
+ const response = await tinybirdFetch(`${LOCAL_BASE_URL}/tokens`, {
80
81
  method: "GET",
81
82
  signal: AbortSignal.timeout(5000),
82
83
  });
@@ -94,7 +95,7 @@ export async function isLocalRunning(): Promise<boolean> {
94
95
  */
95
96
  export async function getLocalTokens(): Promise<LocalTokens> {
96
97
  try {
97
- const response = await fetch(`${LOCAL_BASE_URL}/tokens`, {
98
+ const response = await tinybirdFetch(`${LOCAL_BASE_URL}/tokens`, {
98
99
  method: "GET",
99
100
  signal: AbortSignal.timeout(5000),
100
101
  });
@@ -136,7 +137,7 @@ export async function listLocalWorkspaces(
136
137
  ): Promise<{ workspaces: LocalWorkspace[]; organizationId?: string }> {
137
138
  const url = `${LOCAL_BASE_URL}/v1/user/workspaces?with_organization=true&token=${adminToken}`;
138
139
 
139
- const response = await fetch(url, {
140
+ const response = await tinybirdFetch(url, {
140
141
  method: "GET",
141
142
  });
142
143
 
@@ -182,7 +183,7 @@ export async function createLocalWorkspace(
182
183
  formData.append("assign_to_organization_id", organizationId);
183
184
  }
184
185
 
185
- const response = await fetch(url, {
186
+ const response = await tinybirdFetch(url, {
186
187
  method: "POST",
187
188
  headers: {
188
189
  Authorization: `Bearer ${userToken}`,
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { WorkspaceApiConfig } from "./workspaces.js";
7
+ import { tinybirdFetch } from "./fetcher.js";
7
8
 
8
9
  /**
9
10
  * Error thrown by resource API operations
@@ -299,7 +300,7 @@ export async function listDatasources(
299
300
  ): Promise<string[]> {
300
301
  const url = new URL("/v0/datasources", config.baseUrl);
301
302
 
302
- const response = await fetch(url.toString(), {
303
+ const response = await tinybirdFetch(url.toString(), {
303
304
  method: "GET",
304
305
  headers: {
305
306
  Authorization: `Bearer ${config.token}`,
@@ -327,7 +328,7 @@ export async function getDatasource(
327
328
  ): Promise<DatasourceInfo> {
328
329
  const url = new URL(`/v0/datasources/${encodeURIComponent(name)}`, config.baseUrl);
329
330
 
330
- const response = await fetch(url.toString(), {
331
+ const response = await tinybirdFetch(url.toString(), {
331
332
  method: "GET",
332
333
  headers: {
333
334
  Authorization: `Bearer ${config.token}`,
@@ -404,7 +405,7 @@ export async function listPipes(
404
405
  ): Promise<string[]> {
405
406
  const url = new URL("/v0/pipes", config.baseUrl);
406
407
 
407
- const response = await fetch(url.toString(), {
408
+ const response = await tinybirdFetch(url.toString(), {
408
409
  method: "GET",
409
410
  headers: {
410
411
  Authorization: `Bearer ${config.token}`,
@@ -432,7 +433,7 @@ export async function getPipe(
432
433
  ): Promise<PipeInfo> {
433
434
  const url = new URL(`/v0/pipes/${encodeURIComponent(name)}`, config.baseUrl);
434
435
 
435
- const response = await fetch(url.toString(), {
436
+ const response = await tinybirdFetch(url.toString(), {
436
437
  method: "GET",
437
438
  headers: {
438
439
  Authorization: `Bearer ${config.token}`,
@@ -9,6 +9,12 @@ import {
9
9
  const mockFetch = vi.fn();
10
10
  global.fetch = mockFetch;
11
11
 
12
+ function expectFromParam(url: string) {
13
+ const parsed = new URL(url);
14
+ expect(parsed.searchParams.get("from")).toBe("ts-sdk");
15
+ return parsed;
16
+ }
17
+
12
18
  describe("Workspace API client", () => {
13
19
  const config: WorkspaceApiConfig = {
14
20
  baseUrl: "https://api.tinybird.co",
@@ -38,15 +44,15 @@ describe("Workspace API client", () => {
38
44
 
39
45
  const result = await getWorkspace(config);
40
46
 
41
- expect(mockFetch).toHaveBeenCalledWith(
42
- "https://api.tinybird.co/v1/workspace",
43
- {
44
- method: "GET",
45
- headers: {
46
- Authorization: "Bearer p.test-token",
47
- },
48
- }
49
- );
47
+ const [url, init] = mockFetch.mock.calls[0];
48
+ const parsed = expectFromParam(url);
49
+ expect(parsed.pathname).toBe("/v1/workspace");
50
+ expect(init).toEqual({
51
+ method: "GET",
52
+ headers: {
53
+ Authorization: "Bearer p.test-token",
54
+ },
55
+ });
50
56
  expect(result).toEqual(mockWorkspace);
51
57
  });
52
58
 
@@ -2,6 +2,8 @@
2
2
  * Tinybird Workspace API client
3
3
  */
4
4
 
5
+ import { tinybirdFetch } from "./fetcher.js";
6
+
5
7
  /**
6
8
  * Workspace information from Tinybird API
7
9
  */
@@ -56,7 +58,7 @@ export async function getWorkspace(
56
58
  ): Promise<TinybirdWorkspace> {
57
59
  const url = new URL("/v1/workspace", config.baseUrl);
58
60
 
59
- const response = await fetch(url.toString(), {
61
+ const response = await tinybirdFetch(url.toString(), {
60
62
  method: "GET",
61
63
  headers: {
62
64
  Authorization: `Bearer ${config.token}`,
package/src/cli/auth.ts CHANGED
@@ -8,6 +8,7 @@ import * as http from "node:http";
8
8
  import { spawn } from "node:child_process";
9
9
  import { platform } from "node:os";
10
10
  import { URL } from "node:url";
11
+ import { tinybirdFetch } from "../api/fetcher.js";
11
12
 
12
13
  /**
13
14
  * Port for the local OAuth callback server
@@ -259,7 +260,7 @@ export async function exchangeCodeForTokens(
259
260
  const url = new URL("/api/cli-login", authHost);
260
261
  url.searchParams.set("code", code);
261
262
 
262
- const response = await fetch(url.toString());
263
+ const response = await tinybirdFetch(url.toString());
263
264
 
264
265
  if (!response.ok) {
265
266
  const body = await response.text();
@@ -15,6 +15,8 @@ export interface DeployCommandOptions {
15
15
  cwd?: string;
16
16
  /** Skip pushing to API (just generate) */
17
17
  dryRun?: boolean;
18
+ /** Validate deploy with Tinybird API without applying */
19
+ check?: boolean;
18
20
  }
19
21
 
20
22
  /**
@@ -96,7 +98,10 @@ export async function runDeploy(options: DeployCommandOptions = {}): Promise<Dep
96
98
  baseUrl: config.baseUrl,
97
99
  token: config.token,
98
100
  },
99
- buildResult.resources
101
+ buildResult.resources,
102
+ {
103
+ check: options.check,
104
+ }
100
105
  );
101
106
  } catch (error) {
102
107
  return {