@tinybirdco/sdk 0.0.12 → 0.0.13

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tests for CLI output utilities
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=output.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.test.d.ts","sourceRoot":"","sources":["../../src/cli/output.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Tests for CLI output utilities
3
+ */
4
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
5
+ import { formatDuration, showResourceChange, showBuildErrors, showBuildSuccess, showBuildFailure, showNoChanges, } from "./output.js";
6
+ describe("output utilities", () => {
7
+ let consoleLogSpy;
8
+ let consoleErrorSpy;
9
+ beforeEach(() => {
10
+ consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => { });
11
+ consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
12
+ });
13
+ afterEach(() => {
14
+ consoleLogSpy.mockRestore();
15
+ consoleErrorSpy.mockRestore();
16
+ });
17
+ describe("formatDuration", () => {
18
+ it("formats milliseconds for durations under 1 second", () => {
19
+ expect(formatDuration(500)).toBe("500ms");
20
+ expect(formatDuration(0)).toBe("0ms");
21
+ expect(formatDuration(999)).toBe("999ms");
22
+ });
23
+ it("formats seconds for durations 1 second or more", () => {
24
+ expect(formatDuration(1000)).toBe("1.0s");
25
+ expect(formatDuration(1500)).toBe("1.5s");
26
+ expect(formatDuration(2345)).toBe("2.3s");
27
+ expect(formatDuration(10000)).toBe("10.0s");
28
+ });
29
+ });
30
+ describe("showResourceChange", () => {
31
+ it("shows created resource", () => {
32
+ showResourceChange("events.datasource", "created");
33
+ expect(consoleLogSpy).toHaveBeenCalledWith("✓ events.datasource created");
34
+ });
35
+ it("shows changed resource", () => {
36
+ showResourceChange("top_pages.pipe", "changed");
37
+ expect(consoleLogSpy).toHaveBeenCalledWith("✓ top_pages.pipe changed");
38
+ });
39
+ it("shows deleted resource", () => {
40
+ showResourceChange("old_data.datasource", "deleted");
41
+ expect(consoleLogSpy).toHaveBeenCalledWith("✓ old_data.datasource deleted");
42
+ });
43
+ });
44
+ describe("showBuildErrors", () => {
45
+ it("shows errors with filename", () => {
46
+ showBuildErrors([
47
+ { filename: "events.datasource", error: "Invalid column type" },
48
+ ]);
49
+ expect(consoleErrorSpy).toHaveBeenCalledWith("events.datasource");
50
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Invalid column type");
51
+ });
52
+ it("shows errors without filename", () => {
53
+ showBuildErrors([{ error: "General build error" }]);
54
+ expect(consoleErrorSpy).toHaveBeenCalledWith("General build error");
55
+ });
56
+ it("shows multiple errors", () => {
57
+ showBuildErrors([
58
+ { filename: "a.datasource", error: "Error A" },
59
+ { filename: "b.pipe", error: "Error B" },
60
+ ]);
61
+ expect(consoleErrorSpy).toHaveBeenCalledWith("a.datasource");
62
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Error A");
63
+ expect(consoleErrorSpy).toHaveBeenCalledWith("b.pipe");
64
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Error B");
65
+ });
66
+ it("handles multi-line errors", () => {
67
+ showBuildErrors([
68
+ { filename: "test.pipe", error: "Line 1\nLine 2\nLine 3" },
69
+ ]);
70
+ expect(consoleErrorSpy).toHaveBeenCalledWith("test.pipe");
71
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Line 1");
72
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Line 2");
73
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Line 3");
74
+ });
75
+ });
76
+ describe("showBuildSuccess", () => {
77
+ it("shows build success with duration in ms", () => {
78
+ showBuildSuccess(500);
79
+ expect(consoleLogSpy).toHaveBeenCalled();
80
+ const call = consoleLogSpy.mock.calls[0][0];
81
+ expect(call).toContain("✓");
82
+ expect(call).toContain("Build completed in 500ms");
83
+ });
84
+ it("shows build success with duration in seconds", () => {
85
+ showBuildSuccess(2500);
86
+ expect(consoleLogSpy).toHaveBeenCalled();
87
+ const call = consoleLogSpy.mock.calls[0][0];
88
+ expect(call).toContain("Build completed in 2.5s");
89
+ });
90
+ it("shows rebuild success when isRebuild is true", () => {
91
+ showBuildSuccess(1000, true);
92
+ expect(consoleLogSpy).toHaveBeenCalled();
93
+ const call = consoleLogSpy.mock.calls[0][0];
94
+ expect(call).toContain("Rebuild completed in 1.0s");
95
+ });
96
+ });
97
+ describe("showBuildFailure", () => {
98
+ it("shows build failure", () => {
99
+ showBuildFailure();
100
+ expect(consoleErrorSpy).toHaveBeenCalled();
101
+ const call = consoleErrorSpy.mock.calls[0][0];
102
+ expect(call).toContain("✗");
103
+ expect(call).toContain("Build failed");
104
+ });
105
+ it("shows rebuild failure when isRebuild is true", () => {
106
+ showBuildFailure(true);
107
+ expect(consoleErrorSpy).toHaveBeenCalled();
108
+ const call = consoleErrorSpy.mock.calls[0][0];
109
+ expect(call).toContain("Rebuild failed");
110
+ });
111
+ });
112
+ describe("showNoChanges", () => {
113
+ it("shows no changes message", () => {
114
+ showNoChanges();
115
+ expect(consoleLogSpy).toHaveBeenCalledWith("No changes. Build skipped.");
116
+ });
117
+ });
118
+ });
119
+ //# sourceMappingURL=output.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.test.js","sourceRoot":"","sources":["../../src/cli/output.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinybirdco/sdk",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "TypeScript SDK for Tinybird Forward - define datasources and pipes as TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/api/build.ts CHANGED
@@ -83,8 +83,10 @@ export interface BuildApiResult {
83
83
  success: boolean;
84
84
  /** Result status from API */
85
85
  result: "success" | "failed" | "no_changes";
86
- /** Error message if failed */
86
+ /** Error message if failed (formatted for display) */
87
87
  error?: string;
88
+ /** Detailed errors array from the API */
89
+ errors?: BuildError[];
88
90
  /** Number of datasources deployed */
89
91
  datasourceCount: number;
90
92
  /** Number of pipes deployed */
@@ -233,6 +235,7 @@ export async function buildToTinybird(
233
235
  success: false,
234
236
  result: "failed",
235
237
  error: formatErrors(),
238
+ errors: body.errors,
236
239
  datasourceCount: resources.datasources.length,
237
240
  pipeCount: resources.pipes.length,
238
241
  connectionCount: resources.connections?.length ?? 0,
@@ -245,6 +248,7 @@ export async function buildToTinybird(
245
248
  success: false,
246
249
  result: "failed",
247
250
  error: formatErrors(),
251
+ errors: body.errors,
248
252
  datasourceCount: resources.datasources.length,
249
253
  pipeCount: resources.pipes.length,
250
254
  connectionCount: resources.connections?.length ?? 0,
package/src/cli/index.ts CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  hasTinybirdSdkDependency,
32
32
  } from "./utils/package-manager.js";
33
33
  import type { DevMode } from "./config.js";
34
+ import { output } from "./output.js";
34
35
 
35
36
  const __dirname = dirname(fileURLToPath(import.meta.url));
36
37
  const packageJson = JSON.parse(
@@ -38,13 +39,6 @@ const packageJson = JSON.parse(
38
39
  ) as { version: string };
39
40
  const VERSION = packageJson.version;
40
41
 
41
- /**
42
- * Format timestamp for console output
43
- */
44
- function formatTime(): string {
45
- return new Date().toLocaleTimeString("en-US", { hour12: false });
46
- }
47
-
48
42
  /**
49
43
  * Create and configure the CLI
50
44
  */
@@ -193,24 +187,24 @@ function createCli(): Command {
193
187
  }
194
188
 
195
189
  const modeLabel = devModeOverride === "local" ? " (local)" : "";
196
- console.log(`[${formatTime()}] Building${modeLabel}...\n`);
190
+ output.highlight(`Building${modeLabel}...`);
197
191
 
198
192
  const result = await runBuild({
199
193
  dryRun: options.dryRun,
200
194
  devModeOverride,
201
195
  });
202
196
 
203
- if (!result.success) {
204
- console.error(`Error: ${result.error}`);
205
- process.exit(1);
206
- }
207
-
208
197
  const { build, deploy } = result;
209
198
 
210
- if (build) {
211
- console.log(
212
- `Generated ${build.stats.datasourceCount} datasource(s), ${build.stats.pipeCount} pipe(s)`
213
- );
199
+ if (!result.success) {
200
+ // Show detailed errors if available
201
+ if (deploy?.errors && deploy.errors.length > 0) {
202
+ output.showBuildErrors(deploy.errors);
203
+ } else if (result.error) {
204
+ output.error(result.error);
205
+ }
206
+ output.showBuildFailure();
207
+ process.exit(1);
214
208
  }
215
209
 
216
210
  if (options.dryRun) {
@@ -230,15 +224,40 @@ function createCli(): Command {
230
224
  console.log(pipe.content);
231
225
  });
232
226
  }
227
+ output.showBuildSuccess(result.durationMs);
233
228
  } else if (deploy) {
234
229
  if (deploy.result === "no_changes") {
235
- console.log("No changes detected - already up to date");
230
+ output.showNoChanges();
236
231
  } else {
237
- console.log(`Deployed to Tinybird successfully`);
232
+ // Show datasource changes
233
+ if (deploy.datasources) {
234
+ for (const name of deploy.datasources.created) {
235
+ output.showResourceChange(`${name}.datasource`, "created");
236
+ }
237
+ for (const name of deploy.datasources.changed) {
238
+ output.showResourceChange(`${name}.datasource`, "changed");
239
+ }
240
+ for (const name of deploy.datasources.deleted) {
241
+ output.showResourceChange(`${name}.datasource`, "deleted");
242
+ }
243
+ }
244
+
245
+ // Show pipe changes
246
+ if (deploy.pipes) {
247
+ for (const name of deploy.pipes.created) {
248
+ output.showResourceChange(`${name}.pipe`, "created");
249
+ }
250
+ for (const name of deploy.pipes.changed) {
251
+ output.showResourceChange(`${name}.pipe`, "changed");
252
+ }
253
+ for (const name of deploy.pipes.deleted) {
254
+ output.showResourceChange(`${name}.pipe`, "deleted");
255
+ }
256
+ }
257
+
258
+ output.showBuildSuccess(result.durationMs);
238
259
  }
239
260
  }
240
-
241
- console.log(`\n[${formatTime()}] Done in ${result.durationMs}ms`);
242
261
  });
243
262
 
244
263
  // Deploy command
@@ -253,24 +272,24 @@ function createCli(): Command {
253
272
  process.env.TINYBIRD_DEBUG = "1";
254
273
  }
255
274
 
256
- console.log(`[${formatTime()}] Deploying to main workspace...\n`);
275
+ output.highlight("Deploying to main workspace...");
257
276
 
258
277
  const result = await runDeploy({
259
278
  dryRun: options.dryRun,
260
279
  check: options.check,
261
280
  });
262
281
 
263
- if (!result.success) {
264
- console.error(`Error: ${result.error}`);
265
- process.exit(1);
266
- }
267
-
268
282
  const { build, deploy } = result;
269
283
 
270
- if (build) {
271
- console.log(
272
- `Generated ${build.stats.datasourceCount} datasource(s), ${build.stats.pipeCount} pipe(s)`
273
- );
284
+ if (!result.success) {
285
+ // Show detailed errors if available
286
+ if (deploy?.errors && deploy.errors.length > 0) {
287
+ output.showBuildErrors(deploy.errors);
288
+ } else if (result.error) {
289
+ output.error(result.error);
290
+ }
291
+ output.showBuildFailure();
292
+ process.exit(1);
274
293
  }
275
294
 
276
295
  if (options.dryRun) {
@@ -290,17 +309,43 @@ function createCli(): Command {
290
309
  console.log(pipe.content);
291
310
  });
292
311
  }
312
+ output.showBuildSuccess(result.durationMs);
293
313
  } else if (options.check) {
294
314
  console.log("\n[Check] Resources validated with Tinybird API");
315
+ output.showBuildSuccess(result.durationMs);
295
316
  } else if (deploy) {
296
317
  if (deploy.result === "no_changes") {
297
- console.log("No changes detected - already up to date");
318
+ output.showNoChanges();
298
319
  } else {
299
- console.log(`Deployed to main workspace successfully`);
320
+ // Show datasource changes
321
+ if (deploy.datasources) {
322
+ for (const name of deploy.datasources.created) {
323
+ output.showResourceChange(`${name}.datasource`, "created");
324
+ }
325
+ for (const name of deploy.datasources.changed) {
326
+ output.showResourceChange(`${name}.datasource`, "changed");
327
+ }
328
+ for (const name of deploy.datasources.deleted) {
329
+ output.showResourceChange(`${name}.datasource`, "deleted");
330
+ }
331
+ }
332
+
333
+ // Show pipe changes
334
+ if (deploy.pipes) {
335
+ for (const name of deploy.pipes.created) {
336
+ output.showResourceChange(`${name}.pipe`, "created");
337
+ }
338
+ for (const name of deploy.pipes.changed) {
339
+ output.showResourceChange(`${name}.pipe`, "changed");
340
+ }
341
+ for (const name of deploy.pipes.deleted) {
342
+ output.showResourceChange(`${name}.pipe`, "deleted");
343
+ }
344
+ }
345
+
346
+ output.showBuildSuccess(result.durationMs);
300
347
  }
301
348
  }
302
-
303
- console.log(`\n[${formatTime()}] Done in ${result.durationMs}ms`);
304
349
  });
305
350
 
306
351
  // Dev command
@@ -318,9 +363,6 @@ function createCli(): Command {
318
363
  devModeOverride = "branch";
319
364
  }
320
365
 
321
- console.log(`tinybird dev v${VERSION}`);
322
- console.log("Loading config from tinybird.json...\n");
323
-
324
366
  try {
325
367
  const controller = await runDev({
326
368
  devModeOverride,
@@ -367,11 +409,18 @@ function createCli(): Command {
367
409
  }
368
410
  },
369
411
  onBuildStart: () => {
370
- console.log(`[${formatTime()}] Building...`);
412
+ output.highlight("Building...");
371
413
  },
372
414
  onBuildComplete: (result) => {
373
415
  if (!result.success) {
374
- console.error(`[${formatTime()}] Build failed: ${result.error}`);
416
+ // Show detailed errors if available
417
+ const { deploy } = result;
418
+ if (deploy?.errors && deploy.errors.length > 0) {
419
+ output.showBuildErrors(deploy.errors);
420
+ } else if (result.error) {
421
+ output.error(result.error);
422
+ }
423
+ output.showBuildFailure(true);
375
424
  return;
376
425
  }
377
426
 
@@ -379,56 +428,52 @@ function createCli(): Command {
379
428
 
380
429
  if (deploy) {
381
430
  if (deploy.result === "no_changes") {
382
- console.log(`[${formatTime()}] No changes detected`);
431
+ output.showNoChanges();
383
432
  } else {
384
- console.log(
385
- `[${formatTime()}] Built in ${result.durationMs}ms`
386
- );
387
-
388
433
  // Show datasource changes
389
434
  if (deploy.datasources) {
390
435
  for (const name of deploy.datasources.created) {
391
- console.log(` + datasource ${name} (created)`);
436
+ output.showResourceChange(`${name}.datasource`, "created");
392
437
  }
393
438
  for (const name of deploy.datasources.changed) {
394
- console.log(` ~ datasource ${name} (changed)`);
439
+ output.showResourceChange(`${name}.datasource`, "changed");
395
440
  }
396
441
  for (const name of deploy.datasources.deleted) {
397
- console.log(` - datasource ${name} (deleted)`);
442
+ output.showResourceChange(`${name}.datasource`, "deleted");
398
443
  }
399
444
  }
400
445
 
401
446
  // Show pipe changes
402
447
  if (deploy.pipes) {
403
448
  for (const name of deploy.pipes.created) {
404
- console.log(` + pipe ${name} (created)`);
449
+ output.showResourceChange(`${name}.pipe`, "created");
405
450
  }
406
451
  for (const name of deploy.pipes.changed) {
407
- console.log(` ~ pipe ${name} (changed)`);
452
+ output.showResourceChange(`${name}.pipe`, "changed");
408
453
  }
409
454
  for (const name of deploy.pipes.deleted) {
410
- console.log(` - pipe ${name} (deleted)`);
455
+ output.showResourceChange(`${name}.pipe`, "deleted");
411
456
  }
412
457
  }
458
+
459
+ output.showBuildSuccess(result.durationMs, true);
413
460
  }
414
461
  }
415
462
  },
416
463
  onSchemaValidation: (validation) => {
417
464
  if (validation.issues.length > 0) {
418
- console.log(`[${formatTime()}] Schema validation:`);
465
+ output.info("Schema validation:");
419
466
  for (const issue of validation.issues) {
420
467
  if (issue.type === "error") {
421
- console.error(
422
- ` ERROR [${issue.pipeName}]: ${issue.message}`
423
- );
468
+ output.error(` ERROR [${issue.pipeName}]: ${issue.message}`);
424
469
  } else {
425
- console.warn(` WARN [${issue.pipeName}]: ${issue.message}`);
470
+ output.warning(` WARN [${issue.pipeName}]: ${issue.message}`);
426
471
  }
427
472
  }
428
473
  }
429
474
  },
430
- onError: (error) => {
431
- console.error(`[${formatTime()}] Error: ${error.message}`);
475
+ onError: (err) => {
476
+ output.error(err.message);
432
477
  },
433
478
  });
434
479
 
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Tests for CLI output utilities
3
+ */
4
+
5
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
6
+ import {
7
+ formatDuration,
8
+ showResourceChange,
9
+ showBuildErrors,
10
+ showBuildSuccess,
11
+ showBuildFailure,
12
+ showNoChanges,
13
+ } from "./output.js";
14
+
15
+ describe("output utilities", () => {
16
+ let consoleLogSpy: ReturnType<typeof vi.spyOn>;
17
+ let consoleErrorSpy: ReturnType<typeof vi.spyOn>;
18
+
19
+ beforeEach(() => {
20
+ consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {});
21
+ consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
22
+ });
23
+
24
+ afterEach(() => {
25
+ consoleLogSpy.mockRestore();
26
+ consoleErrorSpy.mockRestore();
27
+ });
28
+
29
+ describe("formatDuration", () => {
30
+ it("formats milliseconds for durations under 1 second", () => {
31
+ expect(formatDuration(500)).toBe("500ms");
32
+ expect(formatDuration(0)).toBe("0ms");
33
+ expect(formatDuration(999)).toBe("999ms");
34
+ });
35
+
36
+ it("formats seconds for durations 1 second or more", () => {
37
+ expect(formatDuration(1000)).toBe("1.0s");
38
+ expect(formatDuration(1500)).toBe("1.5s");
39
+ expect(formatDuration(2345)).toBe("2.3s");
40
+ expect(formatDuration(10000)).toBe("10.0s");
41
+ });
42
+ });
43
+
44
+ describe("showResourceChange", () => {
45
+ it("shows created resource", () => {
46
+ showResourceChange("events.datasource", "created");
47
+ expect(consoleLogSpy).toHaveBeenCalledWith("✓ events.datasource created");
48
+ });
49
+
50
+ it("shows changed resource", () => {
51
+ showResourceChange("top_pages.pipe", "changed");
52
+ expect(consoleLogSpy).toHaveBeenCalledWith("✓ top_pages.pipe changed");
53
+ });
54
+
55
+ it("shows deleted resource", () => {
56
+ showResourceChange("old_data.datasource", "deleted");
57
+ expect(consoleLogSpy).toHaveBeenCalledWith("✓ old_data.datasource deleted");
58
+ });
59
+ });
60
+
61
+ describe("showBuildErrors", () => {
62
+ it("shows errors with filename", () => {
63
+ showBuildErrors([
64
+ { filename: "events.datasource", error: "Invalid column type" },
65
+ ]);
66
+ expect(consoleErrorSpy).toHaveBeenCalledWith("events.datasource");
67
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Invalid column type");
68
+ });
69
+
70
+ it("shows errors without filename", () => {
71
+ showBuildErrors([{ error: "General build error" }]);
72
+ expect(consoleErrorSpy).toHaveBeenCalledWith("General build error");
73
+ });
74
+
75
+ it("shows multiple errors", () => {
76
+ showBuildErrors([
77
+ { filename: "a.datasource", error: "Error A" },
78
+ { filename: "b.pipe", error: "Error B" },
79
+ ]);
80
+ expect(consoleErrorSpy).toHaveBeenCalledWith("a.datasource");
81
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Error A");
82
+ expect(consoleErrorSpy).toHaveBeenCalledWith("b.pipe");
83
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Error B");
84
+ });
85
+
86
+ it("handles multi-line errors", () => {
87
+ showBuildErrors([
88
+ { filename: "test.pipe", error: "Line 1\nLine 2\nLine 3" },
89
+ ]);
90
+ expect(consoleErrorSpy).toHaveBeenCalledWith("test.pipe");
91
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Line 1");
92
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Line 2");
93
+ expect(consoleErrorSpy).toHaveBeenCalledWith(" Line 3");
94
+ });
95
+ });
96
+
97
+ describe("showBuildSuccess", () => {
98
+ it("shows build success with duration in ms", () => {
99
+ showBuildSuccess(500);
100
+ expect(consoleLogSpy).toHaveBeenCalled();
101
+ const call = consoleLogSpy.mock.calls[0][0];
102
+ expect(call).toContain("✓");
103
+ expect(call).toContain("Build completed in 500ms");
104
+ });
105
+
106
+ it("shows build success with duration in seconds", () => {
107
+ showBuildSuccess(2500);
108
+ expect(consoleLogSpy).toHaveBeenCalled();
109
+ const call = consoleLogSpy.mock.calls[0][0];
110
+ expect(call).toContain("Build completed in 2.5s");
111
+ });
112
+
113
+ it("shows rebuild success when isRebuild is true", () => {
114
+ showBuildSuccess(1000, true);
115
+ expect(consoleLogSpy).toHaveBeenCalled();
116
+ const call = consoleLogSpy.mock.calls[0][0];
117
+ expect(call).toContain("Rebuild completed in 1.0s");
118
+ });
119
+ });
120
+
121
+ describe("showBuildFailure", () => {
122
+ it("shows build failure", () => {
123
+ showBuildFailure();
124
+ expect(consoleErrorSpy).toHaveBeenCalled();
125
+ const call = consoleErrorSpy.mock.calls[0][0];
126
+ expect(call).toContain("✗");
127
+ expect(call).toContain("Build failed");
128
+ });
129
+
130
+ it("shows rebuild failure when isRebuild is true", () => {
131
+ showBuildFailure(true);
132
+ expect(consoleErrorSpy).toHaveBeenCalled();
133
+ const call = consoleErrorSpy.mock.calls[0][0];
134
+ expect(call).toContain("Rebuild failed");
135
+ });
136
+ });
137
+
138
+ describe("showNoChanges", () => {
139
+ it("shows no changes message", () => {
140
+ showNoChanges();
141
+ expect(consoleLogSpy).toHaveBeenCalledWith("No changes. Build skipped.");
142
+ });
143
+ });
144
+ });