@tinybirdco/sdk 0.0.1
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.
- package/README.md +518 -0
- package/bin/tinybird.js +7 -0
- package/dist/api/branches.d.ts +98 -0
- package/dist/api/branches.d.ts.map +1 -0
- package/dist/api/branches.js +203 -0
- package/dist/api/branches.js.map +1 -0
- package/dist/api/branches.test.d.ts +2 -0
- package/dist/api/branches.test.d.ts.map +1 -0
- package/dist/api/branches.test.js +286 -0
- package/dist/api/branches.test.js.map +1 -0
- package/dist/api/build.d.ts +130 -0
- package/dist/api/build.d.ts.map +1 -0
- package/dist/api/build.js +143 -0
- package/dist/api/build.js.map +1 -0
- package/dist/api/build.test.d.ts +2 -0
- package/dist/api/build.test.d.ts.map +1 -0
- package/dist/api/build.test.js +138 -0
- package/dist/api/build.test.js.map +1 -0
- package/dist/api/deploy.d.ts +39 -0
- package/dist/api/deploy.d.ts.map +1 -0
- package/dist/api/deploy.js +135 -0
- package/dist/api/deploy.js.map +1 -0
- package/dist/api/deploy.test.d.ts +2 -0
- package/dist/api/deploy.test.d.ts.map +1 -0
- package/dist/api/deploy.test.js +118 -0
- package/dist/api/deploy.test.js.map +1 -0
- package/dist/api/workspaces.d.ts +46 -0
- package/dist/api/workspaces.d.ts.map +1 -0
- package/dist/api/workspaces.js +39 -0
- package/dist/api/workspaces.js.map +1 -0
- package/dist/api/workspaces.test.d.ts +2 -0
- package/dist/api/workspaces.test.d.ts.map +1 -0
- package/dist/api/workspaces.test.js +65 -0
- package/dist/api/workspaces.test.js.map +1 -0
- package/dist/cli/auth.d.ts +86 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +284 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/branch-store.d.ts +53 -0
- package/dist/cli/branch-store.d.ts.map +1 -0
- package/dist/cli/branch-store.js +91 -0
- package/dist/cli/branch-store.js.map +1 -0
- package/dist/cli/branch-store.test.d.ts +2 -0
- package/dist/cli/branch-store.test.d.ts.map +1 -0
- package/dist/cli/branch-store.test.js +115 -0
- package/dist/cli/branch-store.test.js.map +1 -0
- package/dist/cli/commands/branch.d.ts +82 -0
- package/dist/cli/commands/branch.d.ts.map +1 -0
- package/dist/cli/commands/branch.js +215 -0
- package/dist/cli/commands/branch.js.map +1 -0
- package/dist/cli/commands/build.d.ts +43 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +138 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +78 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +226 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/init.d.ts +45 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +277 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/init.test.d.ts +2 -0
- package/dist/cli/commands/init.test.d.ts.map +1 -0
- package/dist/cli/commands/init.test.js +158 -0
- package/dist/cli/commands/init.test.js.map +1 -0
- package/dist/cli/commands/login.d.ts +37 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +64 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/config.d.ts +114 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +258 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/config.test.d.ts +2 -0
- package/dist/cli/config.test.d.ts.map +1 -0
- package/dist/cli/config.test.js +243 -0
- package/dist/cli/config.test.js.map +1 -0
- package/dist/cli/env.d.ts +29 -0
- package/dist/cli/env.d.ts.map +1 -0
- package/dist/cli/env.js +66 -0
- package/dist/cli/env.js.map +1 -0
- package/dist/cli/git.d.ts +29 -0
- package/dist/cli/git.d.ts.map +1 -0
- package/dist/cli/git.js +114 -0
- package/dist/cli/git.js.map +1 -0
- package/dist/cli/git.test.d.ts +2 -0
- package/dist/cli/git.test.d.ts.map +1 -0
- package/dist/cli/git.test.js +125 -0
- package/dist/cli/git.test.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +337 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/schema-validation.d.ts +95 -0
- package/dist/cli/utils/schema-validation.d.ts.map +1 -0
- package/dist/cli/utils/schema-validation.js +175 -0
- package/dist/cli/utils/schema-validation.js.map +1 -0
- package/dist/cli/utils/schema-validation.test.d.ts +5 -0
- package/dist/cli/utils/schema-validation.test.d.ts.map +1 -0
- package/dist/cli/utils/schema-validation.test.js +173 -0
- package/dist/cli/utils/schema-validation.test.js.map +1 -0
- package/dist/client/base.d.ts +116 -0
- package/dist/client/base.d.ts.map +1 -0
- package/dist/client/base.js +328 -0
- package/dist/client/base.js.map +1 -0
- package/dist/client/types.d.ts +137 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +43 -0
- package/dist/client/types.js.map +1 -0
- package/dist/generator/client.d.ts +44 -0
- package/dist/generator/client.d.ts.map +1 -0
- package/dist/generator/client.js +144 -0
- package/dist/generator/client.js.map +1 -0
- package/dist/generator/datasource.d.ts +57 -0
- package/dist/generator/datasource.d.ts.map +1 -0
- package/dist/generator/datasource.js +169 -0
- package/dist/generator/datasource.js.map +1 -0
- package/dist/generator/datasource.test.d.ts +2 -0
- package/dist/generator/datasource.test.d.ts.map +1 -0
- package/dist/generator/datasource.test.js +254 -0
- package/dist/generator/datasource.test.js.map +1 -0
- package/dist/generator/index.d.ts +131 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +121 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/index.test.d.ts +2 -0
- package/dist/generator/index.test.d.ts.map +1 -0
- package/dist/generator/index.test.js +175 -0
- package/dist/generator/index.test.js.map +1 -0
- package/dist/generator/loader.d.ts +156 -0
- package/dist/generator/loader.d.ts.map +1 -0
- package/dist/generator/loader.js +295 -0
- package/dist/generator/loader.js.map +1 -0
- package/dist/generator/pipe.d.ts +72 -0
- package/dist/generator/pipe.d.ts.map +1 -0
- package/dist/generator/pipe.js +174 -0
- package/dist/generator/pipe.js.map +1 -0
- package/dist/generator/pipe.test.d.ts +2 -0
- package/dist/generator/pipe.test.d.ts.map +1 -0
- package/dist/generator/pipe.test.js +393 -0
- package/dist/generator/pipe.test.js.map +1 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/infer/index.d.ts +202 -0
- package/dist/infer/index.d.ts.map +1 -0
- package/dist/infer/index.js +5 -0
- package/dist/infer/index.js.map +1 -0
- package/dist/schema/datasource.d.ts +135 -0
- package/dist/schema/datasource.d.ts.map +1 -0
- package/dist/schema/datasource.js +105 -0
- package/dist/schema/datasource.js.map +1 -0
- package/dist/schema/datasource.test.d.ts +2 -0
- package/dist/schema/datasource.test.d.ts.map +1 -0
- package/dist/schema/datasource.test.js +142 -0
- package/dist/schema/datasource.test.js.map +1 -0
- package/dist/schema/engines.d.ts +157 -0
- package/dist/schema/engines.d.ts.map +1 -0
- package/dist/schema/engines.js +155 -0
- package/dist/schema/engines.js.map +1 -0
- package/dist/schema/engines.test.d.ts +2 -0
- package/dist/schema/engines.test.d.ts.map +1 -0
- package/dist/schema/engines.test.js +221 -0
- package/dist/schema/engines.test.js.map +1 -0
- package/dist/schema/params.d.ts +106 -0
- package/dist/schema/params.d.ts.map +1 -0
- package/dist/schema/params.js +138 -0
- package/dist/schema/params.js.map +1 -0
- package/dist/schema/params.test.d.ts +2 -0
- package/dist/schema/params.test.d.ts.map +1 -0
- package/dist/schema/params.test.js +175 -0
- package/dist/schema/params.test.js.map +1 -0
- package/dist/schema/pipe.d.ts +436 -0
- package/dist/schema/pipe.d.ts.map +1 -0
- package/dist/schema/pipe.js +484 -0
- package/dist/schema/pipe.js.map +1 -0
- package/dist/schema/pipe.test.d.ts +2 -0
- package/dist/schema/pipe.test.d.ts.map +1 -0
- package/dist/schema/pipe.test.js +488 -0
- package/dist/schema/pipe.test.js.map +1 -0
- package/dist/schema/project.d.ts +202 -0
- package/dist/schema/project.d.ts.map +1 -0
- package/dist/schema/project.js +188 -0
- package/dist/schema/project.js.map +1 -0
- package/dist/schema/project.test.d.ts +2 -0
- package/dist/schema/project.test.d.ts.map +1 -0
- package/dist/schema/project.test.js +180 -0
- package/dist/schema/project.test.js.map +1 -0
- package/dist/schema/types.d.ts +140 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +174 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/schema/types.test.d.ts +2 -0
- package/dist/schema/types.test.d.ts.map +1 -0
- package/dist/schema/types.test.js +176 -0
- package/dist/schema/types.test.js.map +1 -0
- package/dist/test/handlers.d.ts +58 -0
- package/dist/test/handlers.d.ts.map +1 -0
- package/dist/test/handlers.js +62 -0
- package/dist/test/handlers.js.map +1 -0
- package/dist/test/setup.d.ts +5 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +11 -0
- package/dist/test/setup.js.map +1 -0
- package/package.json +57 -0
- package/src/api/branches.test.ts +377 -0
- package/src/api/branches.ts +334 -0
- package/src/api/build.test.ts +216 -0
- package/src/api/build.ts +266 -0
- package/src/api/deploy.test.ts +193 -0
- package/src/api/deploy.ts +163 -0
- package/src/api/workspaces.test.ts +81 -0
- package/src/api/workspaces.ts +77 -0
- package/src/cli/auth.ts +358 -0
- package/src/cli/branch-store.test.ts +139 -0
- package/src/cli/branch-store.ts +137 -0
- package/src/cli/commands/branch.ts +306 -0
- package/src/cli/commands/build.ts +183 -0
- package/src/cli/commands/dev.ts +334 -0
- package/src/cli/commands/init.test.ts +249 -0
- package/src/cli/commands/init.ts +323 -0
- package/src/cli/commands/login.ts +98 -0
- package/src/cli/config.test.ts +359 -0
- package/src/cli/config.ts +335 -0
- package/src/cli/env.ts +86 -0
- package/src/cli/git.test.ts +147 -0
- package/src/cli/git.ts +125 -0
- package/src/cli/index.ts +382 -0
- package/src/cli/utils/schema-validation.test.ts +222 -0
- package/src/cli/utils/schema-validation.ts +272 -0
- package/src/client/base.ts +414 -0
- package/src/client/types.ts +165 -0
- package/src/generator/client.ts +194 -0
- package/src/generator/datasource.test.ts +297 -0
- package/src/generator/datasource.ts +217 -0
- package/src/generator/index.test.ts +209 -0
- package/src/generator/index.ts +203 -0
- package/src/generator/loader.ts +406 -0
- package/src/generator/pipe.test.ts +441 -0
- package/src/generator/pipe.ts +220 -0
- package/src/index.ts +191 -0
- package/src/infer/index.ts +247 -0
- package/src/schema/datasource.test.ts +187 -0
- package/src/schema/datasource.ts +195 -0
- package/src/schema/engines.test.ts +247 -0
- package/src/schema/engines.ts +271 -0
- package/src/schema/params.test.ts +208 -0
- package/src/schema/params.ts +249 -0
- package/src/schema/pipe.test.ts +588 -0
- package/src/schema/pipe.ts +832 -0
- package/src/schema/project.test.ts +236 -0
- package/src/schema/project.ts +394 -0
- package/src/schema/types.test.ts +212 -0
- package/src/schema/types.ts +366 -0
- package/src/test/handlers.ts +79 -0
- package/src/test/setup.ts +13 -0
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Tinybird CLI
|
|
4
|
+
* Commands for building and deploying Tinybird projects
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { dirname, resolve } from "node:path";
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import { runInit } from "./commands/init.js";
|
|
12
|
+
import { runBuild } from "./commands/build.js";
|
|
13
|
+
import { runDev } from "./commands/dev.js";
|
|
14
|
+
import { runLogin } from "./commands/login.js";
|
|
15
|
+
import {
|
|
16
|
+
runBranchList,
|
|
17
|
+
runBranchStatus,
|
|
18
|
+
runBranchDelete,
|
|
19
|
+
} from "./commands/branch.js";
|
|
20
|
+
|
|
21
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const packageJson = JSON.parse(
|
|
23
|
+
readFileSync(resolve(__dirname, "../../package.json"), "utf-8")
|
|
24
|
+
) as { version: string };
|
|
25
|
+
const VERSION = packageJson.version;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Format timestamp for console output
|
|
29
|
+
*/
|
|
30
|
+
function formatTime(): string {
|
|
31
|
+
return new Date().toLocaleTimeString("en-US", { hour12: false });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create and configure the CLI
|
|
36
|
+
*/
|
|
37
|
+
function createCli(): Command {
|
|
38
|
+
const program = new Command();
|
|
39
|
+
|
|
40
|
+
program
|
|
41
|
+
.name("tinybird")
|
|
42
|
+
.description("Tinybird TypeScript SDK CLI")
|
|
43
|
+
.version(VERSION);
|
|
44
|
+
|
|
45
|
+
// Init command
|
|
46
|
+
program
|
|
47
|
+
.command("init")
|
|
48
|
+
.description("Initialize a new Tinybird TypeScript project")
|
|
49
|
+
.option("-f, --force", "Overwrite existing files")
|
|
50
|
+
.option("--skip-login", "Skip browser login flow")
|
|
51
|
+
.action(async (options) => {
|
|
52
|
+
console.log("Initializing Tinybird project...\n");
|
|
53
|
+
|
|
54
|
+
const result = await runInit({
|
|
55
|
+
force: options.force,
|
|
56
|
+
skipLogin: options.skipLogin,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!result.success) {
|
|
60
|
+
console.error(`Error: ${result.error}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (result.created.length > 0) {
|
|
65
|
+
console.log("Created:");
|
|
66
|
+
result.created.forEach((file) => {
|
|
67
|
+
console.log(` - ${file}`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (result.skipped.length > 0) {
|
|
72
|
+
console.log("\nSkipped (already exists):");
|
|
73
|
+
result.skipped.forEach((file) => {
|
|
74
|
+
console.log(` - ${file}`);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (result.loggedIn) {
|
|
79
|
+
console.log(`\nLogged in successfully!`);
|
|
80
|
+
if (result.workspaceName) {
|
|
81
|
+
console.log(` Workspace: ${result.workspaceName}`);
|
|
82
|
+
}
|
|
83
|
+
if (result.userEmail) {
|
|
84
|
+
console.log(` User: ${result.userEmail}`);
|
|
85
|
+
}
|
|
86
|
+
console.log("\nDone! Next steps:");
|
|
87
|
+
console.log(" 1. Edit src/tinybird/schema.ts with your schema");
|
|
88
|
+
console.log(" 2. Run 'npx tinybird dev' to start development");
|
|
89
|
+
} else if (result.loggedIn === false) {
|
|
90
|
+
console.log("\nLogin was skipped or failed.");
|
|
91
|
+
console.log("\nDone! Next steps:");
|
|
92
|
+
console.log(" 1. Run 'npx tinybird login' to authenticate");
|
|
93
|
+
console.log(" 2. Edit src/tinybird/schema.ts with your schema");
|
|
94
|
+
console.log(" 3. Run 'npx tinybird dev' to start development");
|
|
95
|
+
} else {
|
|
96
|
+
console.log("\nDone! Next steps:");
|
|
97
|
+
console.log(" 1. Edit src/tinybird/schema.ts with your schema");
|
|
98
|
+
console.log(" 2. Run 'npx tinybird dev' to start development");
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Login command
|
|
103
|
+
program
|
|
104
|
+
.command("login")
|
|
105
|
+
.description("Authenticate with Tinybird via browser")
|
|
106
|
+
.action(async () => {
|
|
107
|
+
console.log("Starting authentication...\n");
|
|
108
|
+
|
|
109
|
+
const result = await runLogin();
|
|
110
|
+
|
|
111
|
+
if (!result.success) {
|
|
112
|
+
console.error(`Error: ${result.error}`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log("\nAuthentication successful!");
|
|
117
|
+
if (result.workspaceName) {
|
|
118
|
+
console.log(` Workspace: ${result.workspaceName}`);
|
|
119
|
+
}
|
|
120
|
+
if (result.userEmail) {
|
|
121
|
+
console.log(` User: ${result.userEmail}`);
|
|
122
|
+
}
|
|
123
|
+
if (result.baseUrl) {
|
|
124
|
+
console.log(` API Host: ${result.baseUrl}`);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Build command
|
|
129
|
+
program
|
|
130
|
+
.command("build")
|
|
131
|
+
.description("Build and push resources to Tinybird")
|
|
132
|
+
.option("--dry-run", "Generate without pushing to API")
|
|
133
|
+
.option("--debug", "Show debug output including API requests/responses")
|
|
134
|
+
.action(async (options) => {
|
|
135
|
+
if (options.debug) {
|
|
136
|
+
process.env.TINYBIRD_DEBUG = "1";
|
|
137
|
+
}
|
|
138
|
+
console.log(`[${formatTime()}] Building...\n`);
|
|
139
|
+
|
|
140
|
+
const result = await runBuild({
|
|
141
|
+
dryRun: options.dryRun,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!result.success) {
|
|
145
|
+
console.error(`Error: ${result.error}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const { build, deploy } = result;
|
|
150
|
+
|
|
151
|
+
if (build) {
|
|
152
|
+
console.log(`Generated ${build.stats.datasourceCount} datasource(s), ${build.stats.pipeCount} pipe(s)`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (options.dryRun) {
|
|
156
|
+
console.log("\n[Dry run] Resources not deployed to API");
|
|
157
|
+
|
|
158
|
+
// Show generated content
|
|
159
|
+
if (build) {
|
|
160
|
+
console.log("\n--- Generated Datasources ---");
|
|
161
|
+
build.resources.datasources.forEach((ds) => {
|
|
162
|
+
console.log(`\n${ds.name}.datasource:`);
|
|
163
|
+
console.log(ds.content);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
console.log("\n--- Generated Pipes ---");
|
|
167
|
+
build.resources.pipes.forEach((pipe) => {
|
|
168
|
+
console.log(`\n${pipe.name}.pipe:`);
|
|
169
|
+
console.log(pipe.content);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
} else if (deploy) {
|
|
173
|
+
if (deploy.result === "no_changes") {
|
|
174
|
+
console.log("No changes detected - already up to date");
|
|
175
|
+
} else {
|
|
176
|
+
console.log(`Deployed to Tinybird successfully`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(`\n[${formatTime()}] Done in ${result.durationMs}ms`);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Dev command
|
|
184
|
+
program
|
|
185
|
+
.command("dev")
|
|
186
|
+
.description("Watch for changes and sync with Tinybird")
|
|
187
|
+
.action(async () => {
|
|
188
|
+
console.log(`tinybird dev v${VERSION}`);
|
|
189
|
+
console.log("Loading config from tinybird.json...\n");
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const controller = await runDev({
|
|
193
|
+
onLoginComplete: (info) => {
|
|
194
|
+
console.log("\nAuthentication successful!");
|
|
195
|
+
if (info.workspaceName) {
|
|
196
|
+
console.log(` Workspace: ${info.workspaceName}`);
|
|
197
|
+
}
|
|
198
|
+
if (info.userEmail) {
|
|
199
|
+
console.log(` User: ${info.userEmail}`);
|
|
200
|
+
}
|
|
201
|
+
console.log("");
|
|
202
|
+
},
|
|
203
|
+
onBranchReady: (info) => {
|
|
204
|
+
if (info.isMainBranch) {
|
|
205
|
+
console.log("On main branch - deploying to workspace\n");
|
|
206
|
+
} else if (info.gitBranch) {
|
|
207
|
+
const tinybirdName = info.tinybirdBranch?.name ?? info.gitBranch;
|
|
208
|
+
if (info.wasCreated) {
|
|
209
|
+
console.log(`Detected git branch: ${info.gitBranch}`);
|
|
210
|
+
console.log(`Creating Tinybird branch '${tinybirdName}'...`);
|
|
211
|
+
console.log("Branch created and token cached.\n");
|
|
212
|
+
} else {
|
|
213
|
+
console.log(`Detected git branch: ${info.gitBranch}`);
|
|
214
|
+
console.log(`Using existing Tinybird branch '${tinybirdName}'\n`);
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
console.log("Not in a git repository - deploying to workspace\n");
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
onBuildStart: () => {
|
|
221
|
+
console.log(`[${formatTime()}] Building...`);
|
|
222
|
+
},
|
|
223
|
+
onBuildComplete: (result) => {
|
|
224
|
+
if (!result.success) {
|
|
225
|
+
console.error(`[${formatTime()}] Build failed: ${result.error}`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const { deploy } = result;
|
|
230
|
+
|
|
231
|
+
if (deploy) {
|
|
232
|
+
if (deploy.result === "no_changes") {
|
|
233
|
+
console.log(`[${formatTime()}] No changes detected`);
|
|
234
|
+
} else {
|
|
235
|
+
console.log(`[${formatTime()}] Built in ${result.durationMs}ms`);
|
|
236
|
+
|
|
237
|
+
// Show datasource changes
|
|
238
|
+
if (deploy.datasources) {
|
|
239
|
+
for (const name of deploy.datasources.created) {
|
|
240
|
+
console.log(` + datasource ${name} (created)`);
|
|
241
|
+
}
|
|
242
|
+
for (const name of deploy.datasources.changed) {
|
|
243
|
+
console.log(` ~ datasource ${name} (changed)`);
|
|
244
|
+
}
|
|
245
|
+
for (const name of deploy.datasources.deleted) {
|
|
246
|
+
console.log(` - datasource ${name} (deleted)`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Show pipe changes
|
|
251
|
+
if (deploy.pipes) {
|
|
252
|
+
for (const name of deploy.pipes.created) {
|
|
253
|
+
console.log(` + pipe ${name} (created)`);
|
|
254
|
+
}
|
|
255
|
+
for (const name of deploy.pipes.changed) {
|
|
256
|
+
console.log(` ~ pipe ${name} (changed)`);
|
|
257
|
+
}
|
|
258
|
+
for (const name of deploy.pipes.deleted) {
|
|
259
|
+
console.log(` - pipe ${name} (deleted)`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
onSchemaValidation: (validation) => {
|
|
266
|
+
if (validation.issues.length > 0) {
|
|
267
|
+
console.log(`[${formatTime()}] Schema validation:`);
|
|
268
|
+
for (const issue of validation.issues) {
|
|
269
|
+
if (issue.type === "error") {
|
|
270
|
+
console.error(` ERROR [${issue.pipeName}]: ${issue.message}`);
|
|
271
|
+
} else {
|
|
272
|
+
console.warn(` WARN [${issue.pipeName}]: ${issue.message}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
onError: (error) => {
|
|
278
|
+
console.error(`[${formatTime()}] Error: ${error.message}`);
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
console.log("Watching for changes... (Ctrl+C to stop)\n");
|
|
283
|
+
|
|
284
|
+
// Handle shutdown
|
|
285
|
+
const shutdown = async () => {
|
|
286
|
+
console.log("\nShutting down...");
|
|
287
|
+
await controller.stop();
|
|
288
|
+
process.exit(0);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
process.on("SIGINT", shutdown);
|
|
292
|
+
process.on("SIGTERM", shutdown);
|
|
293
|
+
|
|
294
|
+
// Keep process alive
|
|
295
|
+
await new Promise(() => {});
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.error(`Error: ${(error as Error).message}`);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Branch command
|
|
303
|
+
const branchCommand = new Command("branch")
|
|
304
|
+
.description("Manage Tinybird branches");
|
|
305
|
+
|
|
306
|
+
branchCommand
|
|
307
|
+
.command("list")
|
|
308
|
+
.description("List all Tinybird branches")
|
|
309
|
+
.action(async () => {
|
|
310
|
+
const result = await runBranchList();
|
|
311
|
+
|
|
312
|
+
if (!result.success) {
|
|
313
|
+
console.error(`Error: ${result.error}`);
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!result.branches || result.branches.length === 0) {
|
|
318
|
+
console.log("No branches found.");
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
console.log("Branches:");
|
|
323
|
+
result.branches.forEach((branch) => {
|
|
324
|
+
console.log(` - ${branch.name} (created: ${branch.created_at})`);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
branchCommand
|
|
329
|
+
.command("status")
|
|
330
|
+
.description("Show current branch status")
|
|
331
|
+
.action(async () => {
|
|
332
|
+
const result = await runBranchStatus();
|
|
333
|
+
|
|
334
|
+
if (!result.success) {
|
|
335
|
+
console.error(`Error: ${result.error}`);
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
console.log("Branch Status:");
|
|
340
|
+
console.log(` Git branch: ${result.gitBranch ?? "(not in git repo)"}`);
|
|
341
|
+
if (result.tinybirdBranchName && result.tinybirdBranchName !== result.gitBranch) {
|
|
342
|
+
console.log(` Tinybird branch name: ${result.tinybirdBranchName} (sanitized)`);
|
|
343
|
+
}
|
|
344
|
+
console.log(` Main branch: ${result.isMainBranch ? "yes" : "no"}`);
|
|
345
|
+
|
|
346
|
+
if (result.tinybirdBranch) {
|
|
347
|
+
console.log(` Tinybird branch: ${result.tinybirdBranch.name}`);
|
|
348
|
+
console.log(` Branch ID: ${result.tinybirdBranch.id}`);
|
|
349
|
+
console.log(` Created: ${result.tinybirdBranch.created_at}`);
|
|
350
|
+
} else if (!result.isMainBranch && result.tinybirdBranchName) {
|
|
351
|
+
console.log(" Tinybird branch: not created yet");
|
|
352
|
+
console.log(" (Run 'npx tinybird dev' to create it)");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
console.log(` Cached token: ${result.hasCachedToken ? "yes" : "no"}`);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
branchCommand
|
|
359
|
+
.command("delete")
|
|
360
|
+
.description("Delete a Tinybird branch")
|
|
361
|
+
.argument("<name>", "Branch name to delete")
|
|
362
|
+
.action(async (name) => {
|
|
363
|
+
console.log(`Deleting branch '${name}'...`);
|
|
364
|
+
|
|
365
|
+
const result = await runBranchDelete(name);
|
|
366
|
+
|
|
367
|
+
if (!result.success) {
|
|
368
|
+
console.error(`Error: ${result.error}`);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
console.log(`Branch '${name}' deleted successfully.`);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
program.addCommand(branchCommand);
|
|
376
|
+
|
|
377
|
+
return program;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Run CLI
|
|
381
|
+
const program = createCli();
|
|
382
|
+
program.parse();
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for schema validation utility
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from "vitest";
|
|
6
|
+
import {
|
|
7
|
+
_typesAreCompatible,
|
|
8
|
+
_validateOutputSchema,
|
|
9
|
+
_hasRequiredParams,
|
|
10
|
+
_buildDefaultParams,
|
|
11
|
+
} from "./schema-validation.js";
|
|
12
|
+
import type { PipeDefinition } from "../../schema/pipe.js";
|
|
13
|
+
import type { ColumnMeta } from "../../client/types.js";
|
|
14
|
+
|
|
15
|
+
describe("typesAreCompatible", () => {
|
|
16
|
+
it("matches identical types", () => {
|
|
17
|
+
expect(_typesAreCompatible("String", "String")).toBe(true);
|
|
18
|
+
expect(_typesAreCompatible("Int64", "Int64")).toBe(true);
|
|
19
|
+
expect(_typesAreCompatible("UInt64", "UInt64")).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("matches Nullable wrapped types", () => {
|
|
23
|
+
expect(_typesAreCompatible("Nullable(String)", "String")).toBe(true);
|
|
24
|
+
expect(_typesAreCompatible("Nullable(Int64)", "Int64")).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("matches LowCardinality wrapped types", () => {
|
|
28
|
+
expect(_typesAreCompatible("LowCardinality(String)", "String")).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("matches LowCardinality(Nullable(...)) wrapped types", () => {
|
|
32
|
+
expect(
|
|
33
|
+
_typesAreCompatible("LowCardinality(Nullable(String))", "String")
|
|
34
|
+
).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("matches DateTime with timezone to base DateTime", () => {
|
|
38
|
+
expect(_typesAreCompatible("DateTime('UTC')", "DateTime")).toBe(true);
|
|
39
|
+
expect(_typesAreCompatible("DateTime('Europe/Madrid')", "DateTime")).toBe(
|
|
40
|
+
true
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("matches DateTime64 with precision", () => {
|
|
45
|
+
expect(_typesAreCompatible("DateTime64(3)", "DateTime64")).toBe(true);
|
|
46
|
+
expect(_typesAreCompatible("DateTime64(6, 'UTC')", "DateTime64")).toBe(
|
|
47
|
+
true
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("rejects mismatched types", () => {
|
|
52
|
+
expect(_typesAreCompatible("String", "Int64")).toBe(false);
|
|
53
|
+
expect(_typesAreCompatible("UInt64", "Int64")).toBe(false);
|
|
54
|
+
expect(_typesAreCompatible("DateTime", "Date")).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("validateOutputSchema", () => {
|
|
59
|
+
it("validates matching schema", () => {
|
|
60
|
+
const responseMeta: ColumnMeta[] = [
|
|
61
|
+
{ name: "id", type: "UInt64" },
|
|
62
|
+
{ name: "name", type: "String" },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const outputSchema = {
|
|
66
|
+
id: { _tinybirdType: "UInt64" },
|
|
67
|
+
name: { _tinybirdType: "String" },
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const result = _validateOutputSchema(responseMeta, outputSchema as any);
|
|
71
|
+
|
|
72
|
+
expect(result.valid).toBe(true);
|
|
73
|
+
expect(result.missingColumns).toHaveLength(0);
|
|
74
|
+
expect(result.extraColumns).toHaveLength(0);
|
|
75
|
+
expect(result.typeMismatches).toHaveLength(0);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("detects missing columns", () => {
|
|
79
|
+
const responseMeta: ColumnMeta[] = [{ name: "id", type: "UInt64" }];
|
|
80
|
+
|
|
81
|
+
const outputSchema = {
|
|
82
|
+
id: { _tinybirdType: "UInt64" },
|
|
83
|
+
name: { _tinybirdType: "String" },
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const result = _validateOutputSchema(responseMeta, outputSchema as any);
|
|
87
|
+
|
|
88
|
+
expect(result.valid).toBe(false);
|
|
89
|
+
expect(result.missingColumns).toHaveLength(1);
|
|
90
|
+
expect(result.missingColumns[0]).toEqual({
|
|
91
|
+
name: "name",
|
|
92
|
+
expectedType: "String",
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("detects extra columns", () => {
|
|
97
|
+
const responseMeta: ColumnMeta[] = [
|
|
98
|
+
{ name: "id", type: "UInt64" },
|
|
99
|
+
{ name: "extra", type: "String" },
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const outputSchema = {
|
|
103
|
+
id: { _tinybirdType: "UInt64" },
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const result = _validateOutputSchema(responseMeta, outputSchema as any);
|
|
107
|
+
|
|
108
|
+
// Extra columns are warnings, not errors
|
|
109
|
+
expect(result.valid).toBe(true);
|
|
110
|
+
expect(result.extraColumns).toHaveLength(1);
|
|
111
|
+
expect(result.extraColumns[0]).toEqual({
|
|
112
|
+
name: "extra",
|
|
113
|
+
actualType: "String",
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("detects type mismatches", () => {
|
|
118
|
+
const responseMeta: ColumnMeta[] = [{ name: "count", type: "Int64" }];
|
|
119
|
+
|
|
120
|
+
const outputSchema = {
|
|
121
|
+
count: { _tinybirdType: "UInt64" },
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const result = _validateOutputSchema(responseMeta, outputSchema as any);
|
|
125
|
+
|
|
126
|
+
expect(result.valid).toBe(false);
|
|
127
|
+
expect(result.typeMismatches).toHaveLength(1);
|
|
128
|
+
expect(result.typeMismatches[0]).toEqual({
|
|
129
|
+
name: "count",
|
|
130
|
+
expectedType: "UInt64",
|
|
131
|
+
actualType: "Int64",
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("handles Nullable types as compatible", () => {
|
|
136
|
+
const responseMeta: ColumnMeta[] = [
|
|
137
|
+
{ name: "value", type: "Nullable(String)" },
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const outputSchema = {
|
|
141
|
+
value: { _tinybirdType: "String" },
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const result = _validateOutputSchema(responseMeta, outputSchema as any);
|
|
145
|
+
|
|
146
|
+
expect(result.valid).toBe(true);
|
|
147
|
+
expect(result.typeMismatches).toHaveLength(0);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("hasRequiredParams", () => {
|
|
152
|
+
it("returns false for pipe with no params", () => {
|
|
153
|
+
const pipe = { _name: "test" } as PipeDefinition;
|
|
154
|
+
expect(_hasRequiredParams(pipe)).toBe(false);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("returns false for pipe with only optional params", () => {
|
|
158
|
+
const pipe = {
|
|
159
|
+
_name: "test",
|
|
160
|
+
_params: {
|
|
161
|
+
limit: { _required: false, _default: 10 },
|
|
162
|
+
},
|
|
163
|
+
} as unknown as PipeDefinition;
|
|
164
|
+
expect(_hasRequiredParams(pipe)).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("returns true for pipe with required param without default", () => {
|
|
168
|
+
const pipe = {
|
|
169
|
+
_name: "test",
|
|
170
|
+
_params: {
|
|
171
|
+
start_date: { _required: true, _default: undefined },
|
|
172
|
+
},
|
|
173
|
+
} as unknown as PipeDefinition;
|
|
174
|
+
expect(_hasRequiredParams(pipe)).toBe(true);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("returns false for required param with default value", () => {
|
|
178
|
+
const pipe = {
|
|
179
|
+
_name: "test",
|
|
180
|
+
_params: {
|
|
181
|
+
start_date: { _required: true, _default: "2024-01-01" },
|
|
182
|
+
},
|
|
183
|
+
} as unknown as PipeDefinition;
|
|
184
|
+
expect(_hasRequiredParams(pipe)).toBe(false);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe("buildDefaultParams", () => {
|
|
189
|
+
it("returns empty object for pipe with no params", () => {
|
|
190
|
+
const pipe = { _name: "test" } as PipeDefinition;
|
|
191
|
+
expect(_buildDefaultParams(pipe)).toEqual({});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("includes params with default values", () => {
|
|
195
|
+
const pipe = {
|
|
196
|
+
_name: "test",
|
|
197
|
+
_params: {
|
|
198
|
+
limit: { _required: false, _default: 10 },
|
|
199
|
+
offset: { _required: false, _default: 0 },
|
|
200
|
+
},
|
|
201
|
+
} as unknown as PipeDefinition;
|
|
202
|
+
|
|
203
|
+
expect(_buildDefaultParams(pipe)).toEqual({
|
|
204
|
+
limit: 10,
|
|
205
|
+
offset: 0,
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("excludes params without default values", () => {
|
|
210
|
+
const pipe = {
|
|
211
|
+
_name: "test",
|
|
212
|
+
_params: {
|
|
213
|
+
limit: { _required: false, _default: 10 },
|
|
214
|
+
start_date: { _required: true, _default: undefined },
|
|
215
|
+
},
|
|
216
|
+
} as unknown as PipeDefinition;
|
|
217
|
+
|
|
218
|
+
expect(_buildDefaultParams(pipe)).toEqual({
|
|
219
|
+
limit: 10,
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
});
|