@tinybirdco/sdk 0.0.4 → 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 (165) hide show
  1. package/README.md +52 -13
  2. package/dist/api/branches.d.ts.map +1 -1
  3. package/dist/api/branches.js +6 -5
  4. package/dist/api/branches.js.map +1 -1
  5. package/dist/api/branches.test.js +32 -6
  6. package/dist/api/branches.test.js.map +1 -1
  7. package/dist/api/build.d.ts.map +1 -1
  8. package/dist/api/build.js +2 -1
  9. package/dist/api/build.js.map +1 -1
  10. package/dist/api/deploy.d.ts +42 -3
  11. package/dist/api/deploy.d.ts.map +1 -1
  12. package/dist/api/deploy.js +162 -19
  13. package/dist/api/deploy.js.map +1 -1
  14. package/dist/api/deploy.test.js +83 -31
  15. package/dist/api/deploy.test.js.map +1 -1
  16. package/dist/api/fetcher.d.ts +6 -0
  17. package/dist/api/fetcher.d.ts.map +1 -0
  18. package/dist/api/fetcher.js +13 -0
  19. package/dist/api/fetcher.js.map +1 -0
  20. package/dist/api/local.d.ts.map +1 -1
  21. package/dist/api/local.js +5 -4
  22. package/dist/api/local.js.map +1 -1
  23. package/dist/api/local.test.js.map +1 -1
  24. package/dist/api/resources.d.ts +178 -0
  25. package/dist/api/resources.d.ts.map +1 -0
  26. package/dist/api/resources.js +245 -0
  27. package/dist/api/resources.js.map +1 -0
  28. package/dist/api/resources.test.d.ts +2 -0
  29. package/dist/api/resources.test.d.ts.map +1 -0
  30. package/dist/api/resources.test.js +255 -0
  31. package/dist/api/resources.test.js.map +1 -0
  32. package/dist/api/workspaces.d.ts.map +1 -1
  33. package/dist/api/workspaces.js +2 -1
  34. package/dist/api/workspaces.js.map +1 -1
  35. package/dist/api/workspaces.test.js +9 -1
  36. package/dist/api/workspaces.test.js.map +1 -1
  37. package/dist/cli/auth.d.ts.map +1 -1
  38. package/dist/cli/auth.js +2 -1
  39. package/dist/cli/auth.js.map +1 -1
  40. package/dist/cli/commands/build.d.ts +3 -4
  41. package/dist/cli/commands/build.d.ts.map +1 -1
  42. package/dist/cli/commands/build.js +23 -25
  43. package/dist/cli/commands/build.js.map +1 -1
  44. package/dist/cli/commands/deploy.d.ts +41 -0
  45. package/dist/cli/commands/deploy.d.ts.map +1 -0
  46. package/dist/cli/commands/deploy.js +92 -0
  47. package/dist/cli/commands/deploy.js.map +1 -0
  48. package/dist/cli/commands/dev.d.ts.map +1 -1
  49. package/dist/cli/commands/dev.js +7 -3
  50. package/dist/cli/commands/dev.js.map +1 -1
  51. package/dist/cli/commands/init.d.ts +38 -1
  52. package/dist/cli/commands/init.d.ts.map +1 -1
  53. package/dist/cli/commands/init.js +434 -23
  54. package/dist/cli/commands/init.js.map +1 -1
  55. package/dist/cli/commands/init.test.js +190 -30
  56. package/dist/cli/commands/init.test.js.map +1 -1
  57. package/dist/cli/index.js +80 -15
  58. package/dist/cli/index.js.map +1 -1
  59. package/dist/cli/utils/package-manager.d.ts +8 -0
  60. package/dist/cli/utils/package-manager.d.ts.map +1 -0
  61. package/dist/cli/utils/package-manager.js +45 -0
  62. package/dist/cli/utils/package-manager.js.map +1 -0
  63. package/dist/cli/utils/package-manager.test.d.ts +2 -0
  64. package/dist/cli/utils/package-manager.test.d.ts.map +1 -0
  65. package/dist/cli/utils/package-manager.test.js +85 -0
  66. package/dist/cli/utils/package-manager.test.js.map +1 -0
  67. package/dist/client/base.d.ts.map +1 -1
  68. package/dist/client/base.js +2 -1
  69. package/dist/client/base.js.map +1 -1
  70. package/dist/codegen/index.d.ts +39 -0
  71. package/dist/codegen/index.d.ts.map +1 -0
  72. package/dist/codegen/index.js +300 -0
  73. package/dist/codegen/index.js.map +1 -0
  74. package/dist/codegen/index.test.d.ts +2 -0
  75. package/dist/codegen/index.test.d.ts.map +1 -0
  76. package/dist/codegen/index.test.js +310 -0
  77. package/dist/codegen/index.test.js.map +1 -0
  78. package/dist/codegen/type-mapper.d.ts +20 -0
  79. package/dist/codegen/type-mapper.d.ts.map +1 -0
  80. package/dist/codegen/type-mapper.js +238 -0
  81. package/dist/codegen/type-mapper.js.map +1 -0
  82. package/dist/codegen/type-mapper.test.d.ts +2 -0
  83. package/dist/codegen/type-mapper.test.d.ts.map +1 -0
  84. package/dist/codegen/type-mapper.test.js +167 -0
  85. package/dist/codegen/type-mapper.test.js.map +1 -0
  86. package/dist/codegen/utils.d.ts +46 -0
  87. package/dist/codegen/utils.d.ts.map +1 -0
  88. package/dist/codegen/utils.js +141 -0
  89. package/dist/codegen/utils.js.map +1 -0
  90. package/dist/codegen/utils.test.d.ts +2 -0
  91. package/dist/codegen/utils.test.d.ts.map +1 -0
  92. package/dist/codegen/utils.test.js +178 -0
  93. package/dist/codegen/utils.test.js.map +1 -0
  94. package/dist/generator/index.d.ts +3 -0
  95. package/dist/generator/index.d.ts.map +1 -1
  96. package/dist/generator/index.js +17 -1
  97. package/dist/generator/index.js.map +1 -1
  98. package/dist/generator/index.test.js +104 -1
  99. package/dist/generator/index.test.js.map +1 -1
  100. package/dist/generator/loader.d.ts +15 -0
  101. package/dist/generator/loader.d.ts.map +1 -1
  102. package/dist/generator/loader.js +24 -0
  103. package/dist/generator/loader.js.map +1 -1
  104. package/dist/schema/connection.d.ts.map +1 -1
  105. package/dist/schema/connection.js +3 -2
  106. package/dist/schema/connection.js.map +1 -1
  107. package/dist/schema/datasource.d.ts.map +1 -1
  108. package/dist/schema/datasource.js +3 -2
  109. package/dist/schema/datasource.js.map +1 -1
  110. package/dist/schema/params.d.ts.map +1 -1
  111. package/dist/schema/params.js +3 -2
  112. package/dist/schema/params.js.map +1 -1
  113. package/dist/schema/pipe.d.ts +2 -2
  114. package/dist/schema/pipe.d.ts.map +1 -1
  115. package/dist/schema/pipe.js +4 -4
  116. package/dist/schema/pipe.js.map +1 -1
  117. package/dist/schema/project.d.ts.map +1 -1
  118. package/dist/schema/project.js +3 -2
  119. package/dist/schema/project.js.map +1 -1
  120. package/dist/schema/types.d.ts.map +1 -1
  121. package/dist/schema/types.js +3 -2
  122. package/dist/schema/types.js.map +1 -1
  123. package/dist/test/handlers.d.ts +49 -0
  124. package/dist/test/handlers.d.ts.map +1 -1
  125. package/dist/test/handlers.js +45 -0
  126. package/dist/test/handlers.js.map +1 -1
  127. package/package.json +4 -2
  128. package/src/api/branches.test.ts +65 -57
  129. package/src/api/branches.ts +7 -5
  130. package/src/api/build.ts +2 -1
  131. package/src/api/deploy.test.ts +141 -36
  132. package/src/api/deploy.ts +231 -23
  133. package/src/api/fetcher.ts +17 -0
  134. package/src/api/local.test.ts +43 -31
  135. package/src/api/local.ts +5 -4
  136. package/src/api/resources.test.ts +332 -0
  137. package/src/api/resources.ts +555 -0
  138. package/src/api/workspaces.test.ts +15 -9
  139. package/src/api/workspaces.ts +3 -1
  140. package/src/cli/auth.ts +2 -1
  141. package/src/cli/commands/build.ts +29 -33
  142. package/src/cli/commands/deploy.ts +131 -0
  143. package/src/cli/commands/dev.ts +10 -3
  144. package/src/cli/commands/init.test.ts +239 -30
  145. package/src/cli/commands/init.ts +548 -26
  146. package/src/cli/index.ts +117 -20
  147. package/src/cli/utils/package-manager.test.ts +118 -0
  148. package/src/cli/utils/package-manager.ts +44 -0
  149. package/src/client/base.ts +3 -2
  150. package/src/codegen/index.test.ts +367 -0
  151. package/src/codegen/index.ts +379 -0
  152. package/src/codegen/type-mapper.test.ts +224 -0
  153. package/src/codegen/type-mapper.ts +265 -0
  154. package/src/codegen/utils.test.ts +221 -0
  155. package/src/codegen/utils.ts +174 -0
  156. package/src/generator/index.test.ts +121 -1
  157. package/src/generator/index.ts +19 -1
  158. package/src/generator/loader.ts +43 -0
  159. package/src/schema/connection.ts +3 -2
  160. package/src/schema/datasource.ts +3 -2
  161. package/src/schema/params.ts +3 -2
  162. package/src/schema/pipe.ts +4 -4
  163. package/src/schema/project.ts +3 -2
  164. package/src/schema/types.ts +3 -2
  165. package/src/test/handlers.ts +58 -0
package/src/api/deploy.ts CHANGED
@@ -1,17 +1,55 @@
1
1
  /**
2
2
  * Deploy resources to Tinybird main workspace
3
- * Uses the /v1/deploy endpoint (same payload format as /v1/build)
3
+ * Uses the /v1/deploy endpoint to create a deployment, then sets it live
4
4
  */
5
5
 
6
6
  import type { GeneratedResources } from "../generator/index.js";
7
- import type { BuildConfig, BuildApiResult, BuildResponse } from "./build.js";
7
+ import type { BuildConfig, BuildApiResult } from "./build.js";
8
+ import { tinybirdFetch } from "./fetcher.js";
9
+
10
+ /**
11
+ * Deployment object returned by the /v1/deploy endpoint
12
+ */
13
+ export interface Deployment {
14
+ id: string;
15
+ status: string;
16
+ live?: boolean;
17
+ created_at?: string;
18
+ updated_at?: string;
19
+ }
20
+
21
+ /**
22
+ * Response from /v1/deployments list endpoint
23
+ */
24
+ export interface DeploymentsListResponse {
25
+ deployments: Deployment[];
26
+ }
27
+
28
+ /**
29
+ * Response from /v1/deploy endpoint
30
+ */
31
+ export interface DeployResponse {
32
+ result: "success" | "failed";
33
+ deployment?: Deployment;
34
+ error?: string;
35
+ errors?: Array<{ filename?: string; error: string }>;
36
+ }
37
+
38
+ /**
39
+ * Response from /v1/deployments/{id} endpoint
40
+ */
41
+ export interface DeploymentStatusResponse {
42
+ result: string;
43
+ deployment: Deployment;
44
+ }
8
45
 
9
46
  /**
10
47
  * Deploy generated resources to Tinybird main workspace
11
48
  *
12
49
  * Uses the /v1/deploy endpoint which accepts all resources in a single
13
- * multipart form request. This is used for deploying to the main workspace
14
- * (not branches).
50
+ * multipart form request. After creating the deployment, this function:
51
+ * 1. Polls until the deployment is ready (status === 'data_ready')
52
+ * 2. Sets the deployment as live via /v1/deployments/{id}/set-live
15
53
  *
16
54
  * @param config - Build configuration with API URL and token
17
55
  * @param resources - Generated resources to deploy
@@ -38,9 +76,18 @@ import type { BuildConfig, BuildApiResult, BuildResponse } from "./build.js";
38
76
  export async function deployToMain(
39
77
  config: BuildConfig,
40
78
  resources: GeneratedResources,
41
- options?: { debug?: boolean }
79
+ options?: {
80
+ debug?: boolean;
81
+ pollIntervalMs?: number;
82
+ maxPollAttempts?: number;
83
+ check?: boolean;
84
+ }
42
85
  ): Promise<BuildApiResult> {
43
86
  const debug = options?.debug ?? !!process.env.TINYBIRD_DEBUG;
87
+ const pollIntervalMs = options?.pollIntervalMs ?? 1000;
88
+ const maxPollAttempts = options?.maxPollAttempts ?? 120; // 2 minutes max
89
+ const baseUrl = config.baseUrl.replace(/\/$/, "");
90
+
44
91
  const formData = new FormData();
45
92
 
46
93
  // Add datasources
@@ -73,14 +120,49 @@ export async function deployToMain(
73
120
  );
74
121
  }
75
122
 
76
- // Make the request to /v1/deploy (instead of /v1/build)
77
- const url = `${config.baseUrl.replace(/\/$/, "")}/v1/deploy`;
123
+ // Step 0: Clean up any stale non-live deployments that might block the new deployment
124
+ try {
125
+ const deploymentsUrl = `${baseUrl}/v1/deployments`;
126
+ const deploymentsResponse = await tinybirdFetch(deploymentsUrl, {
127
+ headers: {
128
+ Authorization: `Bearer ${config.token}`,
129
+ },
130
+ });
131
+
132
+ if (deploymentsResponse.ok) {
133
+ const deploymentsBody = (await deploymentsResponse.json()) as DeploymentsListResponse;
134
+ const staleDeployments = deploymentsBody.deployments.filter(
135
+ (d) => !d.live && d.status !== "live"
136
+ );
137
+
138
+ for (const stale of staleDeployments) {
139
+ if (debug) {
140
+ console.log(`[debug] Cleaning up stale deployment: ${stale.id} (status: ${stale.status})`);
141
+ }
142
+ await tinybirdFetch(`${baseUrl}/v1/deployments/${stale.id}`, {
143
+ method: "DELETE",
144
+ headers: {
145
+ Authorization: `Bearer ${config.token}`,
146
+ },
147
+ });
148
+ }
149
+ }
150
+ } catch (e) {
151
+ // Ignore errors during cleanup - we'll try to deploy anyway
152
+ if (debug) {
153
+ console.log(`[debug] Failed to clean up stale deployments: ${e}`);
154
+ }
155
+ }
156
+
157
+ // Step 1: Create deployment via /v1/deploy
158
+ const deployUrlBase = `${baseUrl}/v1/deploy`;
159
+ const deployUrl = options?.check ? `${deployUrlBase}?check=true` : deployUrlBase;
78
160
 
79
161
  if (debug) {
80
- console.log(`[debug] POST ${url}`);
162
+ console.log(`[debug] POST ${deployUrl}`);
81
163
  }
82
164
 
83
- const response = await fetch(url, {
165
+ const response = await tinybirdFetch(deployUrl, {
84
166
  method: "POST",
85
167
  headers: {
86
168
  Authorization: `Bearer ${config.token}`,
@@ -89,7 +171,7 @@ export async function deployToMain(
89
171
  });
90
172
 
91
173
  // Parse response
92
- let body: BuildResponse;
174
+ let body: DeployResponse;
93
175
  const rawBody = await response.text();
94
176
 
95
177
  if (debug) {
@@ -98,7 +180,7 @@ export async function deployToMain(
98
180
  }
99
181
 
100
182
  try {
101
- body = JSON.parse(rawBody) as BuildResponse;
183
+ body = JSON.parse(rawBody) as DeployResponse;
102
184
  } catch {
103
185
  throw new Error(
104
186
  `Failed to parse response from Tinybird API: ${response.status} ${response.statusText}\nBody: ${rawBody}`
@@ -130,8 +212,29 @@ export async function deployToMain(
130
212
  };
131
213
  }
132
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
+
133
236
  // Handle API result
134
- if (body.result === "failed") {
237
+ if (body.result === "failed" || !body.deployment) {
135
238
  return {
136
239
  success: false,
137
240
  result: "failed",
@@ -142,25 +245,130 @@ export async function deployToMain(
142
245
  };
143
246
  }
144
247
 
248
+ const deploymentId = body.deployment.id;
249
+
250
+ if (debug) {
251
+ console.log(`[debug] Deployment created with ID: ${deploymentId}`);
252
+ }
253
+
254
+ // Step 2: Poll until deployment is ready
255
+ let deployment = body.deployment;
256
+ let attempts = 0;
257
+
258
+ while (deployment.status !== "data_ready" && attempts < maxPollAttempts) {
259
+ await sleep(pollIntervalMs);
260
+ attempts++;
261
+
262
+ if (debug) {
263
+ console.log(`[debug] Polling deployment status (attempt ${attempts})...`);
264
+ }
265
+
266
+ const statusUrl = `${baseUrl}/v1/deployments/${deploymentId}`;
267
+ const statusResponse = await tinybirdFetch(statusUrl, {
268
+ headers: {
269
+ Authorization: `Bearer ${config.token}`,
270
+ },
271
+ });
272
+
273
+ if (!statusResponse.ok) {
274
+ return {
275
+ success: false,
276
+ result: "failed",
277
+ error: `Failed to check deployment status: ${statusResponse.status} ${statusResponse.statusText}`,
278
+ datasourceCount: resources.datasources.length,
279
+ pipeCount: resources.pipes.length,
280
+ connectionCount: resources.connections?.length ?? 0,
281
+ buildId: deploymentId,
282
+ };
283
+ }
284
+
285
+ const statusBody = (await statusResponse.json()) as DeploymentStatusResponse;
286
+ deployment = statusBody.deployment;
287
+
288
+ if (debug) {
289
+ console.log(`[debug] Deployment status: ${deployment.status}`);
290
+ }
291
+
292
+ // Check for failed status
293
+ if (deployment.status === "failed" || deployment.status === "error") {
294
+ return {
295
+ success: false,
296
+ result: "failed",
297
+ error: `Deployment failed with status: ${deployment.status}`,
298
+ datasourceCount: resources.datasources.length,
299
+ pipeCount: resources.pipes.length,
300
+ connectionCount: resources.connections?.length ?? 0,
301
+ buildId: deploymentId,
302
+ };
303
+ }
304
+ }
305
+
306
+ if (deployment.status !== "data_ready") {
307
+ return {
308
+ success: false,
309
+ result: "failed",
310
+ error: `Deployment timed out after ${maxPollAttempts} attempts. Last status: ${deployment.status}`,
311
+ datasourceCount: resources.datasources.length,
312
+ pipeCount: resources.pipes.length,
313
+ connectionCount: resources.connections?.length ?? 0,
314
+ buildId: deploymentId,
315
+ };
316
+ }
317
+
318
+ // Step 3: Set the deployment as live
319
+ const setLiveUrl = `${baseUrl}/v1/deployments/${deploymentId}/set-live`;
320
+
321
+ if (debug) {
322
+ console.log(`[debug] POST ${setLiveUrl}`);
323
+ }
324
+
325
+ const setLiveResponse = await tinybirdFetch(setLiveUrl, {
326
+ method: "POST",
327
+ headers: {
328
+ Authorization: `Bearer ${config.token}`,
329
+ },
330
+ });
331
+
332
+ if (!setLiveResponse.ok) {
333
+ const setLiveBody = await setLiveResponse.text();
334
+ return {
335
+ success: false,
336
+ result: "failed",
337
+ error: `Failed to set deployment as live: ${setLiveResponse.status} ${setLiveResponse.statusText}\n${setLiveBody}`,
338
+ datasourceCount: resources.datasources.length,
339
+ pipeCount: resources.pipes.length,
340
+ connectionCount: resources.connections?.length ?? 0,
341
+ buildId: deploymentId,
342
+ };
343
+ }
344
+
345
+ if (debug) {
346
+ console.log(`[debug] Deployment ${deploymentId} is now live`);
347
+ }
348
+
145
349
  return {
146
350
  success: true,
147
- result: body.result,
351
+ result: "success",
148
352
  datasourceCount: resources.datasources.length,
149
353
  pipeCount: resources.pipes.length,
150
354
  connectionCount: resources.connections?.length ?? 0,
151
- buildId: body.build?.id,
355
+ buildId: deploymentId,
152
356
  pipes: {
153
- changed: body.build?.changed_pipe_names ?? [],
154
- created: body.build?.new_pipe_names ?? [],
155
- deleted: body.build?.deleted_pipe_names ?? [],
357
+ changed: [],
358
+ created: [],
359
+ deleted: [],
156
360
  },
157
361
  datasources: {
158
- changed: body.build?.changed_datasource_names ?? [],
159
- created: body.build?.new_datasource_names ?? [],
160
- deleted: body.build?.deleted_datasource_names ?? [],
362
+ changed: [],
363
+ created: [],
364
+ deleted: [],
161
365
  },
162
- // Keep deprecated fields for backwards compatibility
163
- changedPipeNames: body.build?.changed_pipe_names ?? [],
164
- newPipeNames: body.build?.new_pipe_names ?? [],
165
366
  };
166
367
  }
368
+
369
+ /**
370
+ * Helper function to sleep for a given number of milliseconds
371
+ */
372
+ function sleep(ms: number): Promise<void> {
373
+ return new Promise((resolve) => setTimeout(resolve, ms));
374
+ }
@@ -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}`,