@tinybirdco/sdk 0.0.7 → 0.0.9

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 (56) hide show
  1. package/dist/api/resources.d.ts +2 -0
  2. package/dist/api/resources.d.ts.map +1 -1
  3. package/dist/api/resources.js +1 -0
  4. package/dist/api/resources.js.map +1 -1
  5. package/dist/cli/commands/init.d.ts.map +1 -1
  6. package/dist/cli/commands/init.js +301 -205
  7. package/dist/cli/commands/init.js.map +1 -1
  8. package/dist/cli/commands/init.test.js +67 -93
  9. package/dist/cli/commands/init.test.js.map +1 -1
  10. package/dist/cli/config.d.ts +3 -7
  11. package/dist/cli/config.d.ts.map +1 -1
  12. package/dist/cli/config.js +9 -20
  13. package/dist/cli/config.js.map +1 -1
  14. package/dist/cli/config.test.js +11 -29
  15. package/dist/cli/config.test.js.map +1 -1
  16. package/dist/cli/git.d.ts +5 -0
  17. package/dist/cli/git.d.ts.map +1 -1
  18. package/dist/cli/git.js +15 -0
  19. package/dist/cli/git.js.map +1 -1
  20. package/dist/cli/index.js +42 -25
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/cli/utils/package-manager.d.ts +9 -0
  23. package/dist/cli/utils/package-manager.d.ts.map +1 -1
  24. package/dist/cli/utils/package-manager.js +130 -35
  25. package/dist/cli/utils/package-manager.js.map +1 -1
  26. package/dist/cli/utils/package-manager.test.js +124 -32
  27. package/dist/cli/utils/package-manager.test.js.map +1 -1
  28. package/dist/codegen/index.d.ts +4 -0
  29. package/dist/codegen/index.d.ts.map +1 -1
  30. package/dist/codegen/index.js +96 -0
  31. package/dist/codegen/index.js.map +1 -1
  32. package/dist/codegen/index.test.js +10 -0
  33. package/dist/codegen/index.test.js.map +1 -1
  34. package/dist/generator/datasource.d.ts.map +1 -1
  35. package/dist/generator/datasource.js +20 -0
  36. package/dist/generator/datasource.js.map +1 -1
  37. package/dist/generator/datasource.test.js +11 -0
  38. package/dist/generator/datasource.test.js.map +1 -1
  39. package/dist/schema/datasource.d.ts +5 -0
  40. package/dist/schema/datasource.d.ts.map +1 -1
  41. package/dist/schema/datasource.js.map +1 -1
  42. package/package.json +1 -1
  43. package/src/api/resources.ts +4 -0
  44. package/src/cli/commands/init.test.ts +67 -107
  45. package/src/cli/commands/init.ts +350 -218
  46. package/src/cli/config.test.ts +12 -42
  47. package/src/cli/config.ts +9 -23
  48. package/src/cli/git.ts +15 -0
  49. package/src/cli/index.ts +46 -27
  50. package/src/cli/utils/package-manager.test.ts +165 -33
  51. package/src/cli/utils/package-manager.ts +133 -30
  52. package/src/codegen/index.test.ts +12 -0
  53. package/src/codegen/index.ts +120 -0
  54. package/src/generator/datasource.test.ts +13 -0
  55. package/src/generator/datasource.ts +24 -0
  56. package/src/schema/datasource.ts +5 -0
@@ -4,10 +4,8 @@ import * as path from "path";
4
4
  import * as os from "os";
5
5
  import {
6
6
  hasSrcFolder,
7
- getLibDir,
8
- getRelativeLibDir,
9
- getTinybirdSchemaPath,
10
- getRelativeSchemaPath,
7
+ getTinybirdDir,
8
+ getRelativeTinybirdDir,
11
9
  findConfigFile,
12
10
  loadConfig,
13
11
  getConfigPath,
@@ -50,55 +48,27 @@ describe("Config", () => {
50
48
  });
51
49
  });
52
50
 
53
- describe("getLibDir", () => {
54
- it("returns src/tinybird when project has src folder", () => {
51
+ describe("getTinybirdDir", () => {
52
+ it("returns src/lib when project has src folder", () => {
55
53
  fs.mkdirSync(path.join(tempDir, "src"));
56
54
 
57
- expect(getLibDir(tempDir)).toBe(path.join(tempDir, "src", "tinybird"));
55
+ expect(getTinybirdDir(tempDir)).toBe(path.join(tempDir, "src", "lib"));
58
56
  });
59
57
 
60
- it("returns tinybird when project does not have src folder", () => {
61
- expect(getLibDir(tempDir)).toBe(path.join(tempDir, "tinybird"));
58
+ it("returns lib when project does not have src folder", () => {
59
+ expect(getTinybirdDir(tempDir)).toBe(path.join(tempDir, "lib"));
62
60
  });
63
61
  });
64
62
 
65
- describe("getRelativeLibDir", () => {
66
- it("returns src/tinybird when project has src folder", () => {
63
+ describe("getRelativeTinybirdDir", () => {
64
+ it("returns src/lib/tinybird.ts when project has src folder", () => {
67
65
  fs.mkdirSync(path.join(tempDir, "src"));
68
66
 
69
- expect(getRelativeLibDir(tempDir)).toBe("src/tinybird");
67
+ expect(getRelativeTinybirdDir(tempDir)).toBe("src/lib/tinybird.ts");
70
68
  });
71
69
 
72
- it("returns tinybird when project does not have src folder", () => {
73
- expect(getRelativeLibDir(tempDir)).toBe("tinybird");
74
- });
75
- });
76
-
77
- describe("getTinybirdSchemaPath", () => {
78
- it("returns src/tinybird/datasources.ts when project has src folder", () => {
79
- fs.mkdirSync(path.join(tempDir, "src"));
80
-
81
- expect(getTinybirdSchemaPath(tempDir)).toBe(
82
- path.join(tempDir, "src", "tinybird", "datasources.ts")
83
- );
84
- });
85
-
86
- it("returns tinybird/datasources.ts when project does not have src folder", () => {
87
- expect(getTinybirdSchemaPath(tempDir)).toBe(
88
- path.join(tempDir, "tinybird", "datasources.ts")
89
- );
90
- });
91
- });
92
-
93
- describe("getRelativeSchemaPath", () => {
94
- it("returns src/tinybird/datasources.ts when project has src folder", () => {
95
- fs.mkdirSync(path.join(tempDir, "src"));
96
-
97
- expect(getRelativeSchemaPath(tempDir)).toBe("src/tinybird/datasources.ts");
98
- });
99
-
100
- it("returns tinybird/datasources.ts when project does not have src folder", () => {
101
- expect(getRelativeSchemaPath(tempDir)).toBe("tinybird/datasources.ts");
70
+ it("returns lib/tinybird.ts when project does not have src folder", () => {
71
+ expect(getRelativeTinybirdDir(tempDir)).toBe("lib/tinybird.ts");
102
72
  });
103
73
  });
104
74
 
package/src/cli/config.ts CHANGED
@@ -69,9 +69,9 @@ export const LOCAL_BASE_URL = "http://localhost:7181";
69
69
  const CONFIG_FILE = "tinybird.json";
70
70
 
71
71
  /**
72
- * Tinybird folder name
72
+ * Tinybird file path within lib folder
73
73
  */
74
- const TINYBIRD_FOLDER = "tinybird";
74
+ const TINYBIRD_FILE = "lib/tinybird.ts";
75
75
 
76
76
  /**
77
77
  * Detect if project has a src folder
@@ -82,18 +82,20 @@ export function hasSrcFolder(cwd: string): boolean {
82
82
  }
83
83
 
84
84
  /**
85
- * Get the tinybird directory path based on project structure
86
- * Returns 'src/tinybird' if project has src folder, otherwise 'tinybird'
85
+ * Get the tinybird file path based on project structure
86
+ * Returns 'src/lib/tinybird.ts' if project has src folder, otherwise 'lib/tinybird.ts'
87
87
  */
88
88
  export function getTinybirdDir(cwd: string): string {
89
- return hasSrcFolder(cwd) ? path.join(cwd, "src", TINYBIRD_FOLDER) : path.join(cwd, TINYBIRD_FOLDER);
89
+ return hasSrcFolder(cwd)
90
+ ? path.join(cwd, "src", "lib")
91
+ : path.join(cwd, "lib");
90
92
  }
91
93
 
92
94
  /**
93
- * Get the relative tinybird directory path based on project structure
95
+ * Get the relative tinybird file path based on project structure
94
96
  */
95
97
  export function getRelativeTinybirdDir(cwd: string): string {
96
- return hasSrcFolder(cwd) ? `src/${TINYBIRD_FOLDER}` : TINYBIRD_FOLDER;
98
+ return hasSrcFolder(cwd) ? `src/${TINYBIRD_FILE}` : TINYBIRD_FILE;
97
99
  }
98
100
 
99
101
  /**
@@ -117,22 +119,6 @@ export function getClientPath(cwd: string): string {
117
119
  return path.join(getTinybirdDir(cwd), "client.ts");
118
120
  }
119
121
 
120
- // Legacy exports for backwards compatibility
121
- export function getLibDir(cwd: string): string {
122
- return getTinybirdDir(cwd);
123
- }
124
-
125
- export function getRelativeLibDir(cwd: string): string {
126
- return getRelativeTinybirdDir(cwd);
127
- }
128
-
129
- export function getTinybirdSchemaPath(cwd: string): string {
130
- return getDatasourcesPath(cwd);
131
- }
132
-
133
- export function getRelativeSchemaPath(cwd: string): string {
134
- return `${getRelativeTinybirdDir(cwd)}/datasources.ts`;
135
- }
136
122
 
137
123
  /**
138
124
  * Interpolate environment variables in a string
package/src/cli/git.ts CHANGED
@@ -97,6 +97,21 @@ export function isGitRepo(): boolean {
97
97
  }
98
98
  }
99
99
 
100
+ /**
101
+ * Get the root directory of the git repository
102
+ * Returns null if not in a git repo
103
+ */
104
+ export function getGitRoot(): string | null {
105
+ try {
106
+ return execSync("git rev-parse --show-toplevel", {
107
+ encoding: "utf-8",
108
+ stdio: ["pipe", "pipe", "pipe"],
109
+ }).trim();
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+
100
115
  /**
101
116
  * Sanitize a git branch name for use as a Tinybird branch name
102
117
  * Tinybird only accepts alphanumeric characters and underscores
package/src/cli/index.ts CHANGED
@@ -12,7 +12,7 @@ config({ path: ".env" });
12
12
 
13
13
  import { readFileSync } from "node:fs";
14
14
  import { fileURLToPath } from "node:url";
15
- import { dirname, resolve } from "node:path";
15
+ import { dirname, join, resolve } from "node:path";
16
16
  import { Command } from "commander";
17
17
  import { runInit } from "./commands/init.js";
18
18
  import { runBuild } from "./commands/build.js";
@@ -24,7 +24,11 @@ import {
24
24
  runBranchStatus,
25
25
  runBranchDelete,
26
26
  } from "./commands/branch.js";
27
- import { detectPackageManagerRunCmd } from "./utils/package-manager.js";
27
+ import {
28
+ detectPackageManagerInstallCmd,
29
+ detectPackageManagerRunCmd,
30
+ hasTinybirdSdkDependency,
31
+ } from "./utils/package-manager.js";
28
32
  import type { DevMode } from "./config.js";
29
33
 
30
34
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -80,23 +84,14 @@ function createCli(): Command {
80
84
  process.exit(1);
81
85
  }
82
86
 
83
- if (result.created.length > 0) {
84
- console.log("Created:");
85
- result.created.forEach((file) => {
86
- console.log(` - ${file}`);
87
- });
88
- }
89
-
90
- if (result.skipped.length > 0) {
91
- console.log("\nSkipped (already exists):");
92
- result.skipped.forEach((file) => {
93
- console.log(` - ${file}`);
94
- });
95
- }
96
-
97
87
  // Detect package manager for run command
98
- const runCmd = detectPackageManagerRunCmd();
99
88
  const clientPath = result.clientPath ?? "tinybird";
89
+ const sdkCheckDir = result.clientPath
90
+ ? dirname(join(process.cwd(), clientPath))
91
+ : process.cwd();
92
+ const runCmd = detectPackageManagerRunCmd(sdkCheckDir);
93
+ const installCmd = detectPackageManagerInstallCmd(sdkCheckDir);
94
+ const needsInstallStep = !hasTinybirdSdkDependency(sdkCheckDir);
100
95
 
101
96
  if (result.loggedIn) {
102
97
  console.log(`\nLogged in successfully!`);
@@ -112,19 +107,43 @@ function createCli(): Command {
112
107
  `\nAdded ${result.existingDatafiles.length} existing datafile(s) to tinybird.json.`
113
108
  );
114
109
  }
115
- console.log("\nDone! Next steps:");
116
- console.log(` 1. Edit your schema in ${clientPath}/`);
117
- console.log(` 2. Run '${runCmd} tinybird:dev' to start development`);
110
+ console.log("\nNext steps:");
111
+ const steps = [
112
+ needsInstallStep
113
+ ? `Install dependencies with '${installCmd}'`
114
+ : undefined,
115
+ `Edit your schema in ${clientPath}`,
116
+ `Run '${runCmd} tinybird:dev' to start development`,
117
+ ].filter(Boolean);
118
+ steps.forEach((step, index) => {
119
+ console.log(` ${index + 1}. ${step}`);
120
+ });
118
121
  } else if (result.loggedIn === false) {
119
122
  console.log("\nLogin was skipped or failed.");
120
- console.log("\nDone! Next steps:");
121
- console.log(" 1. Run 'npx tinybird login' to authenticate");
122
- console.log(` 2. Edit your schema in ${clientPath}/`);
123
- console.log(` 3. Run '${runCmd} tinybird:dev' to start development`);
123
+ console.log("\nNext steps:");
124
+ const steps = [
125
+ "Run 'npx tinybird login' to authenticate",
126
+ needsInstallStep
127
+ ? `Install dependencies with '${installCmd}'`
128
+ : undefined,
129
+ `Edit your schema in ${clientPath}`,
130
+ `Run '${runCmd} tinybird:dev' to start development`,
131
+ ].filter(Boolean);
132
+ steps.forEach((step, index) => {
133
+ console.log(` ${index + 1}. ${step}`);
134
+ });
124
135
  } else {
125
- console.log("\nDone! Next steps:");
126
- console.log(` 1. Edit your schema in ${clientPath}/`);
127
- console.log(` 2. Run '${runCmd} tinybird:dev' to start development`);
136
+ console.log("\nNext steps:");
137
+ const steps = [
138
+ needsInstallStep
139
+ ? `Install dependencies with '${installCmd}'`
140
+ : undefined,
141
+ `Edit your schema in ${clientPath}`,
142
+ `Run '${runCmd} tinybird:dev' to start development`,
143
+ ].filter(Boolean);
144
+ steps.forEach((step, index) => {
145
+ console.log(` ${index + 1}. ${step}`);
146
+ });
128
147
  }
129
148
  });
130
149
 
@@ -2,55 +2,62 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
4
  import * as os from "os";
5
- import { detectPackageManagerRunCmd } from "./package-manager.js";
6
-
7
- describe("detectPackageManagerRunCmd", () => {
8
- let tempDir: string;
9
-
10
- beforeEach(() => {
11
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pkg-manager-test-"));
12
- });
5
+ import {
6
+ detectPackageManager,
7
+ detectPackageManagerInstallCmd,
8
+ detectPackageManagerRunCmd,
9
+ getPackageManagerInstallCmd,
10
+ getPackageManagerRunCmd,
11
+ hasTinybirdSdkDependency,
12
+ } from "./package-manager.js";
13
+
14
+ let tempDir: string;
15
+
16
+ beforeEach(() => {
17
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pkg-manager-test-"));
18
+ });
13
19
 
14
- afterEach(() => {
15
- try {
16
- fs.rmSync(tempDir, { recursive: true });
17
- } catch {
18
- // Ignore cleanup errors
19
- }
20
- });
20
+ afterEach(() => {
21
+ try {
22
+ fs.rmSync(tempDir, { recursive: true });
23
+ } catch {
24
+ // Ignore cleanup errors
25
+ }
26
+ });
21
27
 
28
+ describe("detectPackageManager", () => {
22
29
  describe("lockfile detection", () => {
23
30
  it("detects pnpm from pnpm-lock.yaml", () => {
24
31
  fs.writeFileSync(path.join(tempDir, "pnpm-lock.yaml"), "");
25
- expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm");
32
+ expect(detectPackageManager(tempDir)).toBe("pnpm");
26
33
  });
27
34
 
28
35
  it("detects yarn from yarn.lock", () => {
29
36
  fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
30
- expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
37
+ expect(detectPackageManager(tempDir)).toBe("yarn");
31
38
  });
32
39
 
33
40
  it("detects bun from bun.lockb", () => {
34
41
  fs.writeFileSync(path.join(tempDir, "bun.lockb"), "");
35
- expect(detectPackageManagerRunCmd(tempDir)).toBe("bun run");
42
+ expect(detectPackageManager(tempDir)).toBe("bun");
36
43
  });
37
44
 
38
45
  it("detects npm from package-lock.json", () => {
39
46
  fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
40
- expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
47
+ expect(detectPackageManager(tempDir)).toBe("npm");
41
48
  });
42
49
 
43
50
  it("prioritizes pnpm lockfile over others", () => {
44
51
  fs.writeFileSync(path.join(tempDir, "pnpm-lock.yaml"), "");
45
52
  fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
46
53
  fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
47
- expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm");
54
+ expect(detectPackageManager(tempDir)).toBe("pnpm");
48
55
  });
49
56
 
50
57
  it("prioritizes yarn lockfile over npm", () => {
51
58
  fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
52
59
  fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
53
- expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
60
+ expect(detectPackageManager(tempDir)).toBe("yarn");
54
61
  });
55
62
  });
56
63
 
@@ -60,7 +67,7 @@ describe("detectPackageManagerRunCmd", () => {
60
67
  path.join(tempDir, "package.json"),
61
68
  JSON.stringify({ packageManager: "pnpm@9.0.0" })
62
69
  );
63
- expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm");
70
+ expect(detectPackageManager(tempDir)).toBe("pnpm");
64
71
  });
65
72
 
66
73
  it("detects yarn from packageManager field", () => {
@@ -68,7 +75,7 @@ describe("detectPackageManagerRunCmd", () => {
68
75
  path.join(tempDir, "package.json"),
69
76
  JSON.stringify({ packageManager: "yarn@4.0.0" })
70
77
  );
71
- expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
78
+ expect(detectPackageManager(tempDir)).toBe("yarn");
72
79
  });
73
80
 
74
81
  it("detects bun from packageManager field", () => {
@@ -76,7 +83,15 @@ describe("detectPackageManagerRunCmd", () => {
76
83
  path.join(tempDir, "package.json"),
77
84
  JSON.stringify({ packageManager: "bun@1.0.0" })
78
85
  );
79
- expect(detectPackageManagerRunCmd(tempDir)).toBe("bun run");
86
+ expect(detectPackageManager(tempDir)).toBe("bun");
87
+ });
88
+
89
+ it("detects npm from packageManager field", () => {
90
+ fs.writeFileSync(
91
+ path.join(tempDir, "package.json"),
92
+ JSON.stringify({ packageManager: "npm@10.0.0" })
93
+ );
94
+ expect(detectPackageManager(tempDir)).toBe("npm");
80
95
  });
81
96
 
82
97
  it("prioritizes lockfile over packageManager field", () => {
@@ -85,34 +100,151 @@ describe("detectPackageManagerRunCmd", () => {
85
100
  path.join(tempDir, "package.json"),
86
101
  JSON.stringify({ packageManager: "pnpm@9.0.0" })
87
102
  );
88
- expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
103
+ expect(detectPackageManager(tempDir)).toBe("yarn");
104
+ });
105
+ });
106
+
107
+ describe("monorepo detection", () => {
108
+ it("detects pnpm from workspace root when inside a package", () => {
109
+ const repoRoot = path.join(tempDir, "repo");
110
+ const packageDir = path.join(repoRoot, "packages", "app");
111
+ fs.mkdirSync(packageDir, { recursive: true });
112
+ fs.writeFileSync(path.join(repoRoot, "pnpm-workspace.yaml"), "packages:\n - packages/*\n");
113
+ fs.writeFileSync(path.join(repoRoot, "pnpm-lock.yaml"), "");
114
+ expect(detectPackageManager(packageDir)).toBe("pnpm");
115
+ });
116
+
117
+ it("detects pnpm from workspace config even without a lockfile", () => {
118
+ const repoRoot = path.join(tempDir, "repo");
119
+ const packageDir = path.join(repoRoot, "packages", "app");
120
+ fs.mkdirSync(packageDir, { recursive: true });
121
+ fs.writeFileSync(path.join(repoRoot, "pnpm-workspace.yaml"), "packages:\n - packages/*\n");
122
+ expect(detectPackageManager(packageDir)).toBe("pnpm");
123
+ });
124
+
125
+ it("prefers the nearest lockfile in nested packages", () => {
126
+ const repoRoot = path.join(tempDir, "repo");
127
+ const packageDir = path.join(repoRoot, "packages", "app");
128
+ fs.mkdirSync(packageDir, { recursive: true });
129
+ fs.writeFileSync(path.join(repoRoot, "pnpm-lock.yaml"), "");
130
+ fs.writeFileSync(path.join(packageDir, "yarn.lock"), "");
131
+ expect(detectPackageManager(packageDir)).toBe("yarn");
89
132
  });
90
133
  });
91
134
 
92
135
  describe("default behavior", () => {
93
- it("defaults to npm run when no indicators found", () => {
94
- expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
136
+ it("defaults to npm when no indicators found", () => {
137
+ expect(detectPackageManager(tempDir)).toBe("npm");
95
138
  });
96
139
 
97
- it("defaults to npm run when package.json has no packageManager field", () => {
140
+ it("defaults to npm when package.json has no packageManager field", () => {
98
141
  fs.writeFileSync(
99
142
  path.join(tempDir, "package.json"),
100
143
  JSON.stringify({ name: "test-project" })
101
144
  );
102
- expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
145
+ expect(detectPackageManager(tempDir)).toBe("npm");
103
146
  });
104
147
 
105
- it("defaults to npm run when package.json is invalid JSON", () => {
148
+ it("defaults to npm when package.json is invalid JSON", () => {
106
149
  fs.writeFileSync(path.join(tempDir, "package.json"), "not json");
107
- expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
150
+ expect(detectPackageManager(tempDir)).toBe("npm");
108
151
  });
109
152
 
110
- it("defaults to npm run when packageManager is not a string", () => {
153
+ it("defaults to npm when packageManager is not a string", () => {
111
154
  fs.writeFileSync(
112
155
  path.join(tempDir, "package.json"),
113
156
  JSON.stringify({ packageManager: 123 })
114
157
  );
115
- expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
158
+ expect(detectPackageManager(tempDir)).toBe("npm");
116
159
  });
117
160
  });
118
161
  });
162
+
163
+ describe("getPackageManagerRunCmd", () => {
164
+ it("maps npm to npm run", () => {
165
+ expect(getPackageManagerRunCmd("npm")).toBe("npm run");
166
+ });
167
+
168
+ it("maps pnpm to pnpm run", () => {
169
+ expect(getPackageManagerRunCmd("pnpm")).toBe("pnpm run");
170
+ });
171
+
172
+ it("maps yarn to yarn", () => {
173
+ expect(getPackageManagerRunCmd("yarn")).toBe("yarn");
174
+ });
175
+
176
+ it("maps bun to bun run", () => {
177
+ expect(getPackageManagerRunCmd("bun")).toBe("bun run");
178
+ });
179
+ });
180
+
181
+ describe("getPackageManagerInstallCmd", () => {
182
+ it("maps npm to npm install", () => {
183
+ expect(getPackageManagerInstallCmd("npm")).toBe("npm install");
184
+ });
185
+
186
+ it("maps pnpm to pnpm install", () => {
187
+ expect(getPackageManagerInstallCmd("pnpm")).toBe("pnpm install");
188
+ });
189
+
190
+ it("maps yarn to yarn install", () => {
191
+ expect(getPackageManagerInstallCmd("yarn")).toBe("yarn install");
192
+ });
193
+
194
+ it("maps bun to bun install", () => {
195
+ expect(getPackageManagerInstallCmd("bun")).toBe("bun install");
196
+ });
197
+ });
198
+
199
+ describe("detectPackageManagerInstallCmd", () => {
200
+ it("detects install command based on lockfile", () => {
201
+ fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
202
+ expect(detectPackageManagerInstallCmd(tempDir)).toBe("yarn install");
203
+ });
204
+ });
205
+
206
+ describe("hasTinybirdSdkDependency", () => {
207
+ it("returns true when sdk is in dependencies", () => {
208
+ fs.writeFileSync(
209
+ path.join(tempDir, "package.json"),
210
+ JSON.stringify({ dependencies: { "@tinybirdco/sdk": "^1.0.0" } })
211
+ );
212
+ expect(hasTinybirdSdkDependency(tempDir)).toBe(true);
213
+ });
214
+
215
+ it("returns true when sdk is in devDependencies", () => {
216
+ fs.writeFileSync(
217
+ path.join(tempDir, "package.json"),
218
+ JSON.stringify({ devDependencies: { "@tinybirdco/sdk": "^1.0.0" } })
219
+ );
220
+ expect(hasTinybirdSdkDependency(tempDir)).toBe(true);
221
+ });
222
+
223
+ it("returns false when package.json is missing or invalid", () => {
224
+ expect(hasTinybirdSdkDependency(tempDir)).toBe(false);
225
+ fs.writeFileSync(path.join(tempDir, "package.json"), "not json");
226
+ expect(hasTinybirdSdkDependency(tempDir)).toBe(false);
227
+ });
228
+
229
+ it("checks the nearest package.json", () => {
230
+ const repoRoot = path.join(tempDir, "repo");
231
+ const packageDir = path.join(repoRoot, "packages", "app");
232
+ fs.mkdirSync(packageDir, { recursive: true });
233
+ fs.writeFileSync(
234
+ path.join(repoRoot, "package.json"),
235
+ JSON.stringify({ dependencies: { "@tinybirdco/sdk": "^1.0.0" } })
236
+ );
237
+ fs.writeFileSync(
238
+ path.join(packageDir, "package.json"),
239
+ JSON.stringify({ dependencies: { other: "^1.0.0" } })
240
+ );
241
+ expect(hasTinybirdSdkDependency(packageDir)).toBe(false);
242
+ });
243
+ });
244
+
245
+ describe("detectPackageManagerRunCmd", () => {
246
+ it("uses the package manager detection and mapping", () => {
247
+ fs.writeFileSync(path.join(tempDir, "pnpm-lock.yaml"), "");
248
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm run");
249
+ });
250
+ });