@tinybirdco/sdk 0.0.3 → 0.0.6

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 (122) hide show
  1. package/README.md +87 -14
  2. package/dist/api/deploy.d.ts +41 -3
  3. package/dist/api/deploy.d.ts.map +1 -1
  4. package/dist/api/deploy.js +141 -19
  5. package/dist/api/deploy.js.map +1 -1
  6. package/dist/api/deploy.test.js +77 -29
  7. package/dist/api/deploy.test.js.map +1 -1
  8. package/dist/api/local.d.ts +92 -0
  9. package/dist/api/local.d.ts.map +1 -0
  10. package/dist/api/local.js +176 -0
  11. package/dist/api/local.js.map +1 -0
  12. package/dist/api/local.test.d.ts +2 -0
  13. package/dist/api/local.test.d.ts.map +1 -0
  14. package/dist/api/local.test.js +182 -0
  15. package/dist/api/local.test.js.map +1 -0
  16. package/dist/api/resources.d.ts +178 -0
  17. package/dist/api/resources.d.ts.map +1 -0
  18. package/dist/api/resources.js +244 -0
  19. package/dist/api/resources.js.map +1 -0
  20. package/dist/api/resources.test.d.ts +2 -0
  21. package/dist/api/resources.test.d.ts.map +1 -0
  22. package/dist/api/resources.test.js +255 -0
  23. package/dist/api/resources.test.js.map +1 -0
  24. package/dist/cli/commands/build.d.ts +6 -4
  25. package/dist/cli/commands/build.d.ts.map +1 -1
  26. package/dist/cli/commands/build.js +95 -47
  27. package/dist/cli/commands/build.js.map +1 -1
  28. package/dist/cli/commands/deploy.d.ts +39 -0
  29. package/dist/cli/commands/deploy.d.ts.map +1 -0
  30. package/dist/cli/commands/deploy.js +90 -0
  31. package/dist/cli/commands/deploy.js.map +1 -0
  32. package/dist/cli/commands/dev.d.ts +9 -2
  33. package/dist/cli/commands/dev.d.ts.map +1 -1
  34. package/dist/cli/commands/dev.js +60 -31
  35. package/dist/cli/commands/dev.js.map +1 -1
  36. package/dist/cli/commands/init.d.ts +24 -1
  37. package/dist/cli/commands/init.d.ts.map +1 -1
  38. package/dist/cli/commands/init.js +174 -23
  39. package/dist/cli/commands/init.js.map +1 -1
  40. package/dist/cli/commands/init.test.js +190 -30
  41. package/dist/cli/commands/init.test.js.map +1 -1
  42. package/dist/cli/config.d.ts +14 -0
  43. package/dist/cli/config.d.ts.map +1 -1
  44. package/dist/cli/config.js +7 -0
  45. package/dist/cli/config.js.map +1 -1
  46. package/dist/cli/config.test.js +29 -0
  47. package/dist/cli/config.test.js.map +1 -1
  48. package/dist/cli/index.js +107 -11
  49. package/dist/cli/index.js.map +1 -1
  50. package/dist/cli/utils/package-manager.d.ts +8 -0
  51. package/dist/cli/utils/package-manager.d.ts.map +1 -0
  52. package/dist/cli/utils/package-manager.js +45 -0
  53. package/dist/cli/utils/package-manager.js.map +1 -0
  54. package/dist/cli/utils/package-manager.test.d.ts +2 -0
  55. package/dist/cli/utils/package-manager.test.d.ts.map +1 -0
  56. package/dist/cli/utils/package-manager.test.js +85 -0
  57. package/dist/cli/utils/package-manager.test.js.map +1 -0
  58. package/dist/codegen/index.d.ts +39 -0
  59. package/dist/codegen/index.d.ts.map +1 -0
  60. package/dist/codegen/index.js +300 -0
  61. package/dist/codegen/index.js.map +1 -0
  62. package/dist/codegen/index.test.d.ts +2 -0
  63. package/dist/codegen/index.test.d.ts.map +1 -0
  64. package/dist/codegen/index.test.js +310 -0
  65. package/dist/codegen/index.test.js.map +1 -0
  66. package/dist/codegen/type-mapper.d.ts +20 -0
  67. package/dist/codegen/type-mapper.d.ts.map +1 -0
  68. package/dist/codegen/type-mapper.js +238 -0
  69. package/dist/codegen/type-mapper.js.map +1 -0
  70. package/dist/codegen/type-mapper.test.d.ts +2 -0
  71. package/dist/codegen/type-mapper.test.d.ts.map +1 -0
  72. package/dist/codegen/type-mapper.test.js +167 -0
  73. package/dist/codegen/type-mapper.test.js.map +1 -0
  74. package/dist/codegen/utils.d.ts +46 -0
  75. package/dist/codegen/utils.d.ts.map +1 -0
  76. package/dist/codegen/utils.js +141 -0
  77. package/dist/codegen/utils.js.map +1 -0
  78. package/dist/codegen/utils.test.d.ts +2 -0
  79. package/dist/codegen/utils.test.d.ts.map +1 -0
  80. package/dist/codegen/utils.test.js +178 -0
  81. package/dist/codegen/utils.test.js.map +1 -0
  82. package/dist/generator/index.d.ts +3 -0
  83. package/dist/generator/index.d.ts.map +1 -1
  84. package/dist/generator/index.js +17 -1
  85. package/dist/generator/index.js.map +1 -1
  86. package/dist/generator/index.test.js +104 -1
  87. package/dist/generator/index.test.js.map +1 -1
  88. package/dist/generator/loader.d.ts +15 -0
  89. package/dist/generator/loader.d.ts.map +1 -1
  90. package/dist/generator/loader.js +24 -0
  91. package/dist/generator/loader.js.map +1 -1
  92. package/dist/test/handlers.d.ts +49 -0
  93. package/dist/test/handlers.d.ts.map +1 -1
  94. package/dist/test/handlers.js +45 -0
  95. package/dist/test/handlers.js.map +1 -1
  96. package/package.json +4 -2
  97. package/src/api/deploy.test.ts +135 -34
  98. package/src/api/deploy.ts +203 -23
  99. package/src/api/local.test.ts +250 -0
  100. package/src/api/local.ts +270 -0
  101. package/src/api/resources.test.ts +332 -0
  102. package/src/api/resources.ts +554 -0
  103. package/src/cli/commands/build.ts +115 -53
  104. package/src/cli/commands/deploy.ts +126 -0
  105. package/src/cli/commands/dev.ts +81 -36
  106. package/src/cli/commands/init.test.ts +239 -30
  107. package/src/cli/commands/init.ts +243 -26
  108. package/src/cli/config.test.ts +47 -0
  109. package/src/cli/config.ts +20 -0
  110. package/src/cli/index.ts +120 -11
  111. package/src/cli/utils/package-manager.test.ts +118 -0
  112. package/src/cli/utils/package-manager.ts +44 -0
  113. package/src/codegen/index.test.ts +367 -0
  114. package/src/codegen/index.ts +379 -0
  115. package/src/codegen/type-mapper.test.ts +224 -0
  116. package/src/codegen/type-mapper.ts +265 -0
  117. package/src/codegen/utils.test.ts +221 -0
  118. package/src/codegen/utils.ts +174 -0
  119. package/src/generator/index.test.ts +121 -1
  120. package/src/generator/index.ts +19 -1
  121. package/src/generator/loader.ts +43 -0
  122. package/src/test/handlers.ts +58 -0
package/src/cli/index.ts CHANGED
@@ -16,6 +16,7 @@ import { dirname, 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";
19
+ import { runDeploy } from "./commands/deploy.js";
19
20
  import { runDev } from "./commands/dev.js";
20
21
  import { runLogin } from "./commands/login.js";
21
22
  import {
@@ -23,6 +24,8 @@ import {
23
24
  runBranchStatus,
24
25
  runBranchDelete,
25
26
  } from "./commands/branch.js";
27
+ import { detectPackageManagerRunCmd } from "./utils/package-manager.js";
28
+ import type { DevMode } from "./config.js";
26
29
 
27
30
  const __dirname = dirname(fileURLToPath(import.meta.url));
28
31
  const packageJson = JSON.parse(
@@ -54,12 +57,20 @@ function createCli(): Command {
54
57
  .description("Initialize a new Tinybird TypeScript project")
55
58
  .option("-f, --force", "Overwrite existing files")
56
59
  .option("--skip-login", "Skip browser login flow")
60
+ .option("-m, --mode <mode>", "Development mode: 'branch' or 'local'")
61
+ .option("-p, --path <path>", "Path for Tinybird client files")
57
62
  .action(async (options) => {
58
- console.log("Initializing Tinybird project...\n");
63
+ // Validate mode if provided
64
+ if (options.mode && !["branch", "local"].includes(options.mode)) {
65
+ console.error(`Error: Invalid mode '${options.mode}'. Use 'branch' or 'local'.`);
66
+ process.exit(1);
67
+ }
59
68
 
60
69
  const result = await runInit({
61
70
  force: options.force,
62
71
  skipLogin: options.skipLogin,
72
+ devMode: options.mode,
73
+ clientPath: options.path,
63
74
  });
64
75
 
65
76
  if (!result.success) {
@@ -81,6 +92,10 @@ function createCli(): Command {
81
92
  });
82
93
  }
83
94
 
95
+ // Detect package manager for run command
96
+ const runCmd = detectPackageManagerRunCmd();
97
+ const clientPath = result.clientPath ?? "tinybird";
98
+
84
99
  if (result.loggedIn) {
85
100
  console.log(`\nLogged in successfully!`);
86
101
  if (result.workspaceName) {
@@ -89,19 +104,25 @@ function createCli(): Command {
89
104
  if (result.userEmail) {
90
105
  console.log(` User: ${result.userEmail}`);
91
106
  }
107
+
108
+ if (result.existingDatafiles && result.existingDatafiles.length > 0) {
109
+ console.log(
110
+ `\nAdded ${result.existingDatafiles.length} existing datafile(s) to tinybird.json.`
111
+ );
112
+ }
92
113
  console.log("\nDone! Next steps:");
93
- console.log(" 1. Edit src/tinybird/schema.ts with your schema");
94
- console.log(" 2. Run 'npx tinybird dev' to start development");
114
+ console.log(` 1. Edit your schema in ${clientPath}/`);
115
+ console.log(` 2. Run '${runCmd} tinybird:dev' to start development`);
95
116
  } else if (result.loggedIn === false) {
96
117
  console.log("\nLogin was skipped or failed.");
97
118
  console.log("\nDone! Next steps:");
98
119
  console.log(" 1. Run 'npx tinybird login' to authenticate");
99
- console.log(" 2. Edit src/tinybird/schema.ts with your schema");
100
- console.log(" 3. Run 'npx tinybird dev' to start development");
120
+ console.log(` 2. Edit your schema in ${clientPath}/`);
121
+ console.log(` 3. Run '${runCmd} tinybird:dev' to start development`);
101
122
  } else {
102
123
  console.log("\nDone! Next steps:");
103
- console.log(" 1. Edit src/tinybird/schema.ts with your schema");
104
- console.log(" 2. Run 'npx tinybird dev' to start development");
124
+ console.log(` 1. Edit your schema in ${clientPath}/`);
125
+ console.log(` 2. Run '${runCmd} tinybird:dev' to start development`);
105
126
  }
106
127
  });
107
128
 
@@ -134,17 +155,27 @@ function createCli(): Command {
134
155
  // Build command
135
156
  program
136
157
  .command("build")
137
- .description("Build and push resources to Tinybird")
158
+ .description("Build and push resources to a Tinybird branch (not main)")
138
159
  .option("--dry-run", "Generate without pushing to API")
139
160
  .option("--debug", "Show debug output including API requests/responses")
161
+ .option("--local", "Use local Tinybird container")
140
162
  .action(async (options) => {
141
163
  if (options.debug) {
142
164
  process.env.TINYBIRD_DEBUG = "1";
143
165
  }
144
- console.log(`[${formatTime()}] Building...\n`);
166
+
167
+ // Determine devMode override
168
+ let devModeOverride: DevMode | undefined;
169
+ if (options.local) {
170
+ devModeOverride = "local";
171
+ }
172
+
173
+ const modeLabel = devModeOverride === "local" ? " (local)" : "";
174
+ console.log(`[${formatTime()}] Building${modeLabel}...\n`);
145
175
 
146
176
  const result = await runBuild({
147
177
  dryRun: options.dryRun,
178
+ devModeOverride,
148
179
  });
149
180
 
150
181
  if (!result.success) {
@@ -186,16 +217,83 @@ function createCli(): Command {
186
217
  console.log(`\n[${formatTime()}] Done in ${result.durationMs}ms`);
187
218
  });
188
219
 
220
+ // Deploy command
221
+ program
222
+ .command("deploy")
223
+ .description("Deploy resources to main Tinybird workspace (production)")
224
+ .option("--dry-run", "Generate without pushing to API")
225
+ .option("--debug", "Show debug output including API requests/responses")
226
+ .action(async (options) => {
227
+ if (options.debug) {
228
+ process.env.TINYBIRD_DEBUG = "1";
229
+ }
230
+
231
+ console.log(`[${formatTime()}] Deploying to main workspace...\n`);
232
+
233
+ const result = await runDeploy({
234
+ dryRun: options.dryRun,
235
+ });
236
+
237
+ if (!result.success) {
238
+ console.error(`Error: ${result.error}`);
239
+ process.exit(1);
240
+ }
241
+
242
+ const { build, deploy } = result;
243
+
244
+ if (build) {
245
+ console.log(`Generated ${build.stats.datasourceCount} datasource(s), ${build.stats.pipeCount} pipe(s)`);
246
+ }
247
+
248
+ if (options.dryRun) {
249
+ console.log("\n[Dry run] Resources not deployed to API");
250
+
251
+ // Show generated content
252
+ if (build) {
253
+ console.log("\n--- Generated Datasources ---");
254
+ build.resources.datasources.forEach((ds) => {
255
+ console.log(`\n${ds.name}.datasource:`);
256
+ console.log(ds.content);
257
+ });
258
+
259
+ console.log("\n--- Generated Pipes ---");
260
+ build.resources.pipes.forEach((pipe) => {
261
+ console.log(`\n${pipe.name}.pipe:`);
262
+ console.log(pipe.content);
263
+ });
264
+ }
265
+ } else if (deploy) {
266
+ if (deploy.result === "no_changes") {
267
+ console.log("No changes detected - already up to date");
268
+ } else {
269
+ console.log(`Deployed to main workspace successfully`);
270
+ }
271
+ }
272
+
273
+ console.log(`\n[${formatTime()}] Done in ${result.durationMs}ms`);
274
+ });
275
+
189
276
  // Dev command
190
277
  program
191
278
  .command("dev")
192
279
  .description("Watch for changes and sync with Tinybird")
193
- .action(async () => {
280
+ .option("--local", "Use local Tinybird container")
281
+ .option("--branch", "Use Tinybird cloud with branches")
282
+ .action(async (options) => {
283
+ // Determine devMode override
284
+ let devModeOverride: DevMode | undefined;
285
+ if (options.local) {
286
+ devModeOverride = "local";
287
+ } else if (options.branch) {
288
+ devModeOverride = "branch";
289
+ }
290
+
194
291
  console.log(`tinybird dev v${VERSION}`);
195
292
  console.log("Loading config from tinybird.json...\n");
196
293
 
197
294
  try {
198
295
  const controller = await runDev({
296
+ devModeOverride,
199
297
  onLoginComplete: (info) => {
200
298
  console.log("\nAuthentication successful!");
201
299
  if (info.workspaceName) {
@@ -207,7 +305,18 @@ function createCli(): Command {
207
305
  console.log("");
208
306
  },
209
307
  onBranchReady: (info) => {
210
- if (info.isMainBranch) {
308
+ if (info.isLocal) {
309
+ // Local mode
310
+ const workspaceName = info.localWorkspace?.name ?? "unknown";
311
+ if (info.wasCreated) {
312
+ console.log(`Using local Tinybird container`);
313
+ console.log(`Creating local workspace '${workspaceName}'...`);
314
+ console.log("Workspace created.\n");
315
+ } else {
316
+ console.log(`Using local Tinybird container`);
317
+ console.log(`Using existing local workspace '${workspaceName}'\n`);
318
+ }
319
+ } else if (info.isMainBranch) {
211
320
  console.log("On main branch - deploying to workspace\n");
212
321
  } else if (info.gitBranch) {
213
322
  const tinybirdName = info.tinybirdBranch?.name ?? info.gitBranch;
@@ -0,0 +1,118 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
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
+ });
13
+
14
+ afterEach(() => {
15
+ try {
16
+ fs.rmSync(tempDir, { recursive: true });
17
+ } catch {
18
+ // Ignore cleanup errors
19
+ }
20
+ });
21
+
22
+ describe("lockfile detection", () => {
23
+ it("detects pnpm from pnpm-lock.yaml", () => {
24
+ fs.writeFileSync(path.join(tempDir, "pnpm-lock.yaml"), "");
25
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm");
26
+ });
27
+
28
+ it("detects yarn from yarn.lock", () => {
29
+ fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
30
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
31
+ });
32
+
33
+ it("detects bun from bun.lockb", () => {
34
+ fs.writeFileSync(path.join(tempDir, "bun.lockb"), "");
35
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("bun run");
36
+ });
37
+
38
+ it("detects npm from package-lock.json", () => {
39
+ fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
40
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
41
+ });
42
+
43
+ it("prioritizes pnpm lockfile over others", () => {
44
+ fs.writeFileSync(path.join(tempDir, "pnpm-lock.yaml"), "");
45
+ fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
46
+ fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
47
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm");
48
+ });
49
+
50
+ it("prioritizes yarn lockfile over npm", () => {
51
+ fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
52
+ fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
53
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
54
+ });
55
+ });
56
+
57
+ describe("packageManager field detection", () => {
58
+ it("detects pnpm from packageManager field", () => {
59
+ fs.writeFileSync(
60
+ path.join(tempDir, "package.json"),
61
+ JSON.stringify({ packageManager: "pnpm@9.0.0" })
62
+ );
63
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm");
64
+ });
65
+
66
+ it("detects yarn from packageManager field", () => {
67
+ fs.writeFileSync(
68
+ path.join(tempDir, "package.json"),
69
+ JSON.stringify({ packageManager: "yarn@4.0.0" })
70
+ );
71
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
72
+ });
73
+
74
+ it("detects bun from packageManager field", () => {
75
+ fs.writeFileSync(
76
+ path.join(tempDir, "package.json"),
77
+ JSON.stringify({ packageManager: "bun@1.0.0" })
78
+ );
79
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("bun run");
80
+ });
81
+
82
+ it("prioritizes lockfile over packageManager field", () => {
83
+ fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
84
+ fs.writeFileSync(
85
+ path.join(tempDir, "package.json"),
86
+ JSON.stringify({ packageManager: "pnpm@9.0.0" })
87
+ );
88
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
89
+ });
90
+ });
91
+
92
+ describe("default behavior", () => {
93
+ it("defaults to npm run when no indicators found", () => {
94
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
95
+ });
96
+
97
+ it("defaults to npm run when package.json has no packageManager field", () => {
98
+ fs.writeFileSync(
99
+ path.join(tempDir, "package.json"),
100
+ JSON.stringify({ name: "test-project" })
101
+ );
102
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
103
+ });
104
+
105
+ it("defaults to npm run when package.json is invalid JSON", () => {
106
+ fs.writeFileSync(path.join(tempDir, "package.json"), "not json");
107
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
108
+ });
109
+
110
+ it("defaults to npm run when packageManager is not a string", () => {
111
+ fs.writeFileSync(
112
+ path.join(tempDir, "package.json"),
113
+ JSON.stringify({ packageManager: 123 })
114
+ );
115
+ expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
116
+ });
117
+ });
118
+ });
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Package manager detection utilities
3
+ */
4
+
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+
8
+ /**
9
+ * Detect package manager and return the appropriate run command
10
+ */
11
+ export function detectPackageManagerRunCmd(cwd: string = process.cwd()): string {
12
+ // Check lockfiles first (most reliable)
13
+ if (existsSync(join(cwd, "pnpm-lock.yaml"))) {
14
+ return "pnpm";
15
+ }
16
+ if (existsSync(join(cwd, "yarn.lock"))) {
17
+ return "yarn";
18
+ }
19
+ if (existsSync(join(cwd, "bun.lockb"))) {
20
+ return "bun run";
21
+ }
22
+ if (existsSync(join(cwd, "package-lock.json"))) {
23
+ return "npm run";
24
+ }
25
+
26
+ // Check packageManager field in package.json
27
+ const packageJsonPath = join(cwd, "package.json");
28
+ if (existsSync(packageJsonPath)) {
29
+ try {
30
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
31
+ const pm = packageJson.packageManager;
32
+ if (typeof pm === "string") {
33
+ if (pm.startsWith("pnpm")) return "pnpm";
34
+ if (pm.startsWith("yarn")) return "yarn";
35
+ if (pm.startsWith("bun")) return "bun run";
36
+ }
37
+ } catch {
38
+ // Ignore parse errors
39
+ }
40
+ }
41
+
42
+ // Default to npm
43
+ return "npm run";
44
+ }