@tinybirdco/sdk 0.0.38 → 0.0.40
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 +47 -2
- package/dist/api/api.d.ts +18 -1
- package/dist/api/api.d.ts.map +1 -1
- package/dist/api/api.js +56 -0
- package/dist/api/api.js.map +1 -1
- package/dist/api/api.test.js +111 -0
- package/dist/api/api.test.js.map +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +4 -9
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +12 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +217 -2
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +15 -0
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/config-types.d.ts +1 -1
- package/dist/cli/config-types.d.ts.map +1 -1
- package/dist/cli/config.d.ts +1 -1
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/client/base.d.ts +16 -0
- package/dist/client/base.d.ts.map +1 -1
- package/dist/client/base.js +38 -0
- package/dist/client/base.js.map +1 -1
- package/dist/client/base.test.js +4 -0
- package/dist/client/base.test.js.map +1 -1
- package/dist/client/types.d.ts +50 -0
- package/dist/client/types.d.ts.map +1 -1
- package/dist/generator/include-paths.d.ts +7 -0
- package/dist/generator/include-paths.d.ts.map +1 -0
- package/dist/generator/include-paths.js +164 -0
- package/dist/generator/include-paths.js.map +1 -0
- package/dist/generator/index.d.ts +1 -1
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.test.js +36 -0
- package/dist/generator/index.test.js.map +1 -1
- package/dist/generator/loader.d.ts +1 -1
- package/dist/generator/loader.d.ts.map +1 -1
- package/dist/generator/loader.js +5 -8
- package/dist/generator/loader.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/schema/project.d.ts +8 -4
- package/dist/schema/project.d.ts.map +1 -1
- package/dist/schema/project.js +8 -0
- package/dist/schema/project.js.map +1 -1
- package/dist/schema/project.test.js +14 -2
- package/dist/schema/project.test.js.map +1 -1
- package/package.json +1 -1
- package/src/api/api.test.ts +165 -0
- package/src/api/api.ts +104 -0
- package/src/cli/commands/dev.ts +4 -9
- package/src/cli/commands/init.test.ts +17 -0
- package/src/cli/commands/init.ts +300 -2
- package/src/cli/config-types.ts +1 -1
- package/src/cli/config.ts +1 -1
- package/src/client/base.test.ts +4 -0
- package/src/client/base.ts +53 -0
- package/src/client/types.ts +54 -0
- package/src/generator/include-paths.ts +234 -0
- package/src/generator/index.test.ts +44 -0
- package/src/generator/index.ts +1 -1
- package/src/generator/loader.ts +6 -10
- package/src/index.ts +6 -0
- package/src/schema/project.test.ts +14 -2
- package/src/schema/project.ts +19 -3
package/src/cli/commands/init.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { selectRegion } from "../region-selector.js";
|
|
|
21
21
|
import { getGitRoot } from "../git.js";
|
|
22
22
|
import { fetchAllResources } from "../../api/resources.js";
|
|
23
23
|
import { generateCombinedFile } from "../../codegen/index.js";
|
|
24
|
-
import { execSync } from "child_process";
|
|
24
|
+
import { execSync, spawn } from "child_process";
|
|
25
25
|
import {
|
|
26
26
|
detectPackageManager,
|
|
27
27
|
getPackageManagerAddCmd,
|
|
@@ -286,6 +286,8 @@ function createDefaultConfig(
|
|
|
286
286
|
};
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
type InstallTool = "skills" | "syntax-highlighting";
|
|
290
|
+
|
|
289
291
|
/**
|
|
290
292
|
* Init command options
|
|
291
293
|
*/
|
|
@@ -314,6 +316,12 @@ export interface InitOptions {
|
|
|
314
316
|
workflowProvider?: "github" | "gitlab";
|
|
315
317
|
/** Skip auto-installing @tinybirdco/sdk dependency */
|
|
316
318
|
skipDependencyInstall?: boolean;
|
|
319
|
+
/** Skip install tools prompt */
|
|
320
|
+
skipToolsPrompt?: boolean;
|
|
321
|
+
/** Install selected tools */
|
|
322
|
+
installTools?: InstallTool[];
|
|
323
|
+
/** Skip selected tool installation */
|
|
324
|
+
skipToolsInstall?: boolean;
|
|
317
325
|
}
|
|
318
326
|
|
|
319
327
|
/**
|
|
@@ -346,6 +354,10 @@ export interface InitResult {
|
|
|
346
354
|
cdWorkflowCreated?: boolean;
|
|
347
355
|
/** Git provider used for workflow templates */
|
|
348
356
|
workflowProvider?: "github" | "gitlab";
|
|
357
|
+
/** Selected install tools */
|
|
358
|
+
installTools?: InstallTool[];
|
|
359
|
+
/** Installed tools */
|
|
360
|
+
installedTools?: string[];
|
|
349
361
|
}
|
|
350
362
|
|
|
351
363
|
/**
|
|
@@ -418,14 +430,18 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
418
430
|
const skipLogin = options.skipLogin ?? false;
|
|
419
431
|
const skipDependencyInstall =
|
|
420
432
|
options.skipDependencyInstall ?? Boolean(process.env.VITEST);
|
|
433
|
+
const skipToolsInstall =
|
|
434
|
+
options.skipToolsInstall ?? Boolean(process.env.VITEST);
|
|
421
435
|
|
|
422
436
|
const created: string[] = [];
|
|
423
437
|
const skipped: string[] = [];
|
|
438
|
+
const installedTools: string[] = [];
|
|
424
439
|
let didPrompt = false;
|
|
425
440
|
let existingDatafiles: string[] = [];
|
|
426
441
|
let ciWorkflowCreated = false;
|
|
427
442
|
let cdWorkflowCreated = false;
|
|
428
443
|
let workflowProvider = options.workflowProvider;
|
|
444
|
+
let installTools: InstallTool[] = options.installTools ?? [];
|
|
429
445
|
|
|
430
446
|
// Check for existing .datasource and .pipe files
|
|
431
447
|
const foundDatafiles = findExistingDatafiles(cwd);
|
|
@@ -540,6 +556,44 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
540
556
|
workflowProvider = "github";
|
|
541
557
|
}
|
|
542
558
|
|
|
559
|
+
const skipToolsPrompt =
|
|
560
|
+
options.skipToolsPrompt ??
|
|
561
|
+
(options.devMode !== undefined || options.clientPath !== undefined);
|
|
562
|
+
const shouldPromptTools =
|
|
563
|
+
!skipToolsPrompt && options.installTools === undefined;
|
|
564
|
+
|
|
565
|
+
if (shouldPromptTools) {
|
|
566
|
+
const toolsChoice = await p.multiselect({
|
|
567
|
+
message: "Install extra tools",
|
|
568
|
+
options: [
|
|
569
|
+
{
|
|
570
|
+
value: "skills",
|
|
571
|
+
label: "Agent skills",
|
|
572
|
+
hint: "Manual: npx skills add tinybirdco/tinybird-agent-skills --skill tinybird --skill tinybird-typescript-sdk-guidelines",
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
value: "syntax-highlighting",
|
|
576
|
+
label: "Syntax highlighting (Cursor/VS Code)",
|
|
577
|
+
hint: "Installs Tinybird SQL highlighting extension",
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
required: false,
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
if (p.isCancel(toolsChoice)) {
|
|
584
|
+
p.cancel("Operation cancelled");
|
|
585
|
+
return {
|
|
586
|
+
success: false,
|
|
587
|
+
created: [],
|
|
588
|
+
skipped: [],
|
|
589
|
+
error: "Cancelled by user",
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
didPrompt = true;
|
|
594
|
+
installTools = toolsChoice as InstallTool[];
|
|
595
|
+
}
|
|
596
|
+
|
|
543
597
|
// Ask about existing datafiles if found
|
|
544
598
|
let datafileAction: DatafileAction = "skip";
|
|
545
599
|
if (foundDatafiles.length > 0 && !options.skipDatafilePrompt) {
|
|
@@ -575,12 +629,17 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
575
629
|
if (includeCiWorkflow || includeCdWorkflow) {
|
|
576
630
|
cicdSummary = workflowProvider === "gitlab" ? "GitLab" : "GitHub";
|
|
577
631
|
}
|
|
632
|
+
const toolsSummary =
|
|
633
|
+
installTools.length > 0
|
|
634
|
+
? installTools.map(getInstallToolLabel).join(", ")
|
|
635
|
+
: "none selected";
|
|
578
636
|
|
|
579
637
|
const summaryLines = [
|
|
580
638
|
`Mode: ${devModeLabel}`,
|
|
581
639
|
`Folder: ${relativeTinybirdDir}/`,
|
|
582
640
|
`Existing datafiles: ${datafileSummary}`,
|
|
583
641
|
`CI/CD: ${cicdSummary}`,
|
|
642
|
+
`Tools: ${toolsSummary}`,
|
|
584
643
|
];
|
|
585
644
|
|
|
586
645
|
p.note(summaryLines.join("\n"), "Installation Summary");
|
|
@@ -748,7 +807,9 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
748
807
|
const packageManager = detectPackageManager(cwd);
|
|
749
808
|
const addCmd = getPackageManagerAddCmd(packageManager);
|
|
750
809
|
try {
|
|
751
|
-
|
|
810
|
+
await flushSpinnerRender();
|
|
811
|
+
const { command, args } = splitCommandPrefix(addCmd);
|
|
812
|
+
await runCommand(command, [...args, "@tinybirdco/sdk"], cwd);
|
|
752
813
|
s.stop("Installed dependencies");
|
|
753
814
|
created.push("@tinybirdco/sdk");
|
|
754
815
|
} catch (error) {
|
|
@@ -761,6 +822,16 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
761
822
|
}
|
|
762
823
|
}
|
|
763
824
|
|
|
825
|
+
if (installTools.length > 0 && !skipToolsInstall) {
|
|
826
|
+
await installSelectedTools(
|
|
827
|
+
cwd,
|
|
828
|
+
installTools,
|
|
829
|
+
created,
|
|
830
|
+
skipped,
|
|
831
|
+
installedTools
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
764
835
|
// Use git root for workflow files, fallback to cwd if not in a git repo
|
|
765
836
|
const projectRoot = getGitRoot() ?? cwd;
|
|
766
837
|
const githubWorkflowsDir = path.join(projectRoot, ".github", "workflows");
|
|
@@ -911,6 +982,8 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
911
982
|
ciWorkflowCreated,
|
|
912
983
|
cdWorkflowCreated,
|
|
913
984
|
workflowProvider,
|
|
985
|
+
installTools: installTools.length > 0 ? installTools : undefined,
|
|
986
|
+
installedTools: installedTools.length > 0 ? installedTools : undefined,
|
|
914
987
|
};
|
|
915
988
|
}
|
|
916
989
|
|
|
@@ -958,6 +1031,9 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
958
1031
|
ciWorkflowCreated,
|
|
959
1032
|
cdWorkflowCreated,
|
|
960
1033
|
workflowProvider,
|
|
1034
|
+
installTools: installTools.length > 0 ? installTools : undefined,
|
|
1035
|
+
installedTools:
|
|
1036
|
+
installedTools.length > 0 ? installedTools : undefined,
|
|
961
1037
|
};
|
|
962
1038
|
} catch (error) {
|
|
963
1039
|
// Login succeeded but saving credentials failed
|
|
@@ -976,6 +1052,9 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
976
1052
|
ciWorkflowCreated,
|
|
977
1053
|
cdWorkflowCreated,
|
|
978
1054
|
workflowProvider,
|
|
1055
|
+
installTools: installTools.length > 0 ? installTools : undefined,
|
|
1056
|
+
installedTools:
|
|
1057
|
+
installedTools.length > 0 ? installedTools : undefined,
|
|
979
1058
|
};
|
|
980
1059
|
}
|
|
981
1060
|
} else {
|
|
@@ -992,6 +1071,8 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
992
1071
|
ciWorkflowCreated,
|
|
993
1072
|
cdWorkflowCreated,
|
|
994
1073
|
workflowProvider,
|
|
1074
|
+
installTools: installTools.length > 0 ? installTools : undefined,
|
|
1075
|
+
installedTools: installedTools.length > 0 ? installedTools : undefined,
|
|
995
1076
|
};
|
|
996
1077
|
}
|
|
997
1078
|
}
|
|
@@ -1032,11 +1113,228 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
1032
1113
|
ciWorkflowCreated,
|
|
1033
1114
|
cdWorkflowCreated,
|
|
1034
1115
|
workflowProvider,
|
|
1116
|
+
installTools: installTools.length > 0 ? installTools : undefined,
|
|
1117
|
+
installedTools: installedTools.length > 0 ? installedTools : undefined,
|
|
1035
1118
|
};
|
|
1036
1119
|
}
|
|
1037
1120
|
|
|
1038
1121
|
type DatafileAction = "include" | "codegen" | "skip";
|
|
1039
1122
|
|
|
1123
|
+
const SKILLS_INSTALL_COMMAND = "npx";
|
|
1124
|
+
const SKILLS_INSTALL_ARGS = [
|
|
1125
|
+
"skills",
|
|
1126
|
+
"add",
|
|
1127
|
+
"tinybirdco/tinybird-agent-skills",
|
|
1128
|
+
"--skill",
|
|
1129
|
+
"tinybird",
|
|
1130
|
+
"--skill",
|
|
1131
|
+
"tinybird-typescript-sdk-guidelines",
|
|
1132
|
+
"--yes",
|
|
1133
|
+
];
|
|
1134
|
+
const SYNTAX_EXTENSION_DIR = path.join("extension");
|
|
1135
|
+
const SYNTAX_EXTENSION_PREFIX = "tinybird-ts-sdk-extension";
|
|
1136
|
+
|
|
1137
|
+
async function installSelectedTools(
|
|
1138
|
+
cwd: string,
|
|
1139
|
+
installTools: InstallTool[],
|
|
1140
|
+
created: string[],
|
|
1141
|
+
skipped: string[],
|
|
1142
|
+
installedTools: string[]
|
|
1143
|
+
): Promise<void> {
|
|
1144
|
+
const s = p.spinner();
|
|
1145
|
+
const installedBefore = installedTools.length;
|
|
1146
|
+
s.start("Installing tools");
|
|
1147
|
+
await flushSpinnerRender();
|
|
1148
|
+
|
|
1149
|
+
if (installTools.includes("skills")) {
|
|
1150
|
+
await installSkills(cwd, created, skipped, installedTools);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
if (installTools.includes("syntax-highlighting")) {
|
|
1154
|
+
await installSyntaxHighlighting(cwd, created, skipped, installedTools);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
const installedCount = installedTools.length - installedBefore;
|
|
1158
|
+
if (installedCount > 0) {
|
|
1159
|
+
s.stop("Installed tools");
|
|
1160
|
+
} else {
|
|
1161
|
+
s.stop("No tools installed");
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
async function flushSpinnerRender(): Promise<void> {
|
|
1166
|
+
// Yield once so terminal UI can paint before child process work starts.
|
|
1167
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 20));
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
async function installSkills(
|
|
1171
|
+
cwd: string,
|
|
1172
|
+
created: string[],
|
|
1173
|
+
skipped: string[],
|
|
1174
|
+
installedTools: string[]
|
|
1175
|
+
): Promise<void> {
|
|
1176
|
+
try {
|
|
1177
|
+
const installCwd = resolveRepoRoot(cwd);
|
|
1178
|
+
await runCommand(SKILLS_INSTALL_COMMAND, SKILLS_INSTALL_ARGS, installCwd);
|
|
1179
|
+
created.push("agent skills (tinybird, tinybird-typescript-sdk-guidelines)");
|
|
1180
|
+
installedTools.push("agent-skills");
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
skipped.push("agent skills (installation failed)");
|
|
1183
|
+
console.error(
|
|
1184
|
+
`Warning: Failed to install Tinybird agent skills: ${(error as Error).message}`
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
function getInstallToolLabel(tool: InstallTool): string {
|
|
1190
|
+
if (tool === "skills") {
|
|
1191
|
+
return "agent skills";
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
return "syntax highlighting";
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
async function installSyntaxHighlighting(
|
|
1198
|
+
cwd: string,
|
|
1199
|
+
created: string[],
|
|
1200
|
+
skipped: string[],
|
|
1201
|
+
installedTools: string[]
|
|
1202
|
+
): Promise<void> {
|
|
1203
|
+
const editors = [
|
|
1204
|
+
{ command: "cursor", label: "Cursor" },
|
|
1205
|
+
{ command: "code", label: "VS Code" },
|
|
1206
|
+
].filter((editor) => isCommandAvailable(editor.command));
|
|
1207
|
+
|
|
1208
|
+
if (editors.length === 0) {
|
|
1209
|
+
skipped.push("syntax highlighting (Cursor/VS Code CLI not found)");
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const vsixPath = findSyntaxHighlightingVsix(cwd);
|
|
1214
|
+
if (!vsixPath) {
|
|
1215
|
+
skipped.push("syntax highlighting (VSIX not found)");
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
for (const editor of editors) {
|
|
1220
|
+
try {
|
|
1221
|
+
await runCommand(
|
|
1222
|
+
editor.command,
|
|
1223
|
+
["--install-extension", vsixPath, "--force"],
|
|
1224
|
+
cwd
|
|
1225
|
+
);
|
|
1226
|
+
created.push(`syntax highlighting (${editor.label})`);
|
|
1227
|
+
installedTools.push(`syntax-highlighting:${editor.command}`);
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
skipped.push(`syntax highlighting (${editor.label}, install failed)`);
|
|
1230
|
+
console.error(
|
|
1231
|
+
`Warning: Failed to install syntax highlighting in ${editor.label}: ${
|
|
1232
|
+
(error as Error).message
|
|
1233
|
+
}`
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
function resolveRepoRoot(cwd: string): string {
|
|
1240
|
+
try {
|
|
1241
|
+
return execSync("git rev-parse --show-toplevel", {
|
|
1242
|
+
cwd,
|
|
1243
|
+
encoding: "utf-8",
|
|
1244
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1245
|
+
}).trim();
|
|
1246
|
+
} catch {
|
|
1247
|
+
return cwd;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
function runCommand(command: string, args: string[], cwd: string): Promise<void> {
|
|
1252
|
+
return new Promise((resolve, reject) => {
|
|
1253
|
+
const child = spawn(command, args, {
|
|
1254
|
+
cwd,
|
|
1255
|
+
stdio: "ignore",
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
child.on("error", reject);
|
|
1259
|
+
child.on("close", (code) => {
|
|
1260
|
+
if (code === 0) {
|
|
1261
|
+
resolve();
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
reject(
|
|
1266
|
+
new Error(
|
|
1267
|
+
`Command failed (${code ?? "unknown"}): ${command} ${args.join(" ")}`
|
|
1268
|
+
)
|
|
1269
|
+
);
|
|
1270
|
+
});
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function splitCommandPrefix(commandPrefix: string): {
|
|
1275
|
+
command: string;
|
|
1276
|
+
args: string[];
|
|
1277
|
+
} {
|
|
1278
|
+
const parts = commandPrefix.trim().split(/\s+/).filter(Boolean);
|
|
1279
|
+
if (parts.length === 0) {
|
|
1280
|
+
throw new Error("Invalid command prefix");
|
|
1281
|
+
}
|
|
1282
|
+
return {
|
|
1283
|
+
command: parts[0],
|
|
1284
|
+
args: parts.slice(1),
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function isCommandAvailable(command: string): boolean {
|
|
1289
|
+
try {
|
|
1290
|
+
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
1291
|
+
return true;
|
|
1292
|
+
} catch {
|
|
1293
|
+
return false;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
function findSyntaxHighlightingVsix(cwd: string): string | undefined {
|
|
1298
|
+
const searchRoots = new Set<string>();
|
|
1299
|
+
const gitRoot = getGitRoot();
|
|
1300
|
+
if (gitRoot) {
|
|
1301
|
+
searchRoots.add(gitRoot);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
let current = path.resolve(cwd);
|
|
1305
|
+
while (true) {
|
|
1306
|
+
searchRoots.add(current);
|
|
1307
|
+
const parent = path.dirname(current);
|
|
1308
|
+
if (parent === current) break;
|
|
1309
|
+
current = parent;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
for (const root of searchRoots) {
|
|
1313
|
+
const extensionDir = path.join(root, SYNTAX_EXTENSION_DIR);
|
|
1314
|
+
if (!fs.existsSync(extensionDir)) {
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
try {
|
|
1319
|
+
const files = fs
|
|
1320
|
+
.readdirSync(extensionDir)
|
|
1321
|
+
.filter(
|
|
1322
|
+
(file) =>
|
|
1323
|
+
file.endsWith(".vsix") && file.startsWith(SYNTAX_EXTENSION_PREFIX)
|
|
1324
|
+
)
|
|
1325
|
+
.sort();
|
|
1326
|
+
|
|
1327
|
+
if (files.length > 0) {
|
|
1328
|
+
return path.join(extensionDir, files[files.length - 1]);
|
|
1329
|
+
}
|
|
1330
|
+
} catch {
|
|
1331
|
+
// Ignore unreadable directories
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
return undefined;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1040
1338
|
/**
|
|
1041
1339
|
* Generate TypeScript files from Tinybird workspace resources
|
|
1042
1340
|
* Only generates for resources that match the local datafiles
|
package/src/cli/config-types.ts
CHANGED
|
@@ -16,7 +16,7 @@ export type DevMode = "branch" | "local";
|
|
|
16
16
|
* Tinybird configuration file structure
|
|
17
17
|
*/
|
|
18
18
|
export interface TinybirdConfig {
|
|
19
|
-
/** Array of
|
|
19
|
+
/** Array of file paths or glob patterns to scan for TypeScript/resources */
|
|
20
20
|
include?: string[];
|
|
21
21
|
/** @deprecated Use `include` instead. Path to the TypeScript schema entry point */
|
|
22
22
|
schema?: string;
|
package/src/cli/config.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { DevMode, TinybirdConfig } from "./config-types.js";
|
|
|
14
14
|
* Resolved configuration with all values expanded
|
|
15
15
|
*/
|
|
16
16
|
export interface ResolvedConfig {
|
|
17
|
-
/** Array of
|
|
17
|
+
/** Array of file paths or glob patterns to scan for datasources and pipes */
|
|
18
18
|
include: string[];
|
|
19
19
|
/** Resolved API token (workspace main token) */
|
|
20
20
|
token: string;
|
package/src/client/base.test.ts
CHANGED
|
@@ -156,6 +156,8 @@ describe("TinybirdClient", () => {
|
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
expect(typeof client.datasources.append).toBe("function");
|
|
159
|
+
expect(typeof client.datasources.delete).toBe("function");
|
|
160
|
+
expect(typeof client.datasources.truncate).toBe("function");
|
|
159
161
|
});
|
|
160
162
|
|
|
161
163
|
it("datasources conforms to DatasourcesNamespace interface", () => {
|
|
@@ -167,6 +169,8 @@ describe("TinybirdClient", () => {
|
|
|
167
169
|
const datasources: DatasourcesNamespace = client.datasources;
|
|
168
170
|
expect(datasources).toBeDefined();
|
|
169
171
|
expect(typeof datasources.append).toBe("function");
|
|
172
|
+
expect(typeof datasources.delete).toBe("function");
|
|
173
|
+
expect(typeof datasources.truncate).toBe("function");
|
|
170
174
|
});
|
|
171
175
|
});
|
|
172
176
|
});
|
package/src/client/base.ts
CHANGED
|
@@ -8,10 +8,14 @@ import type {
|
|
|
8
8
|
ClientConfig,
|
|
9
9
|
ClientContext,
|
|
10
10
|
DatasourcesNamespace,
|
|
11
|
+
DeleteOptions,
|
|
12
|
+
DeleteResult,
|
|
11
13
|
QueryResult,
|
|
12
14
|
IngestResult,
|
|
13
15
|
QueryOptions,
|
|
14
16
|
IngestOptions,
|
|
17
|
+
TruncateOptions,
|
|
18
|
+
TruncateResult,
|
|
15
19
|
} from "./types.js";
|
|
16
20
|
import { TinybirdError } from "./types.js";
|
|
17
21
|
import { TinybirdApi, TinybirdApiError } from "../api/api.js";
|
|
@@ -89,6 +93,15 @@ export class TinybirdClient {
|
|
|
89
93
|
append: (datasourceName: string, options: AppendOptions): Promise<AppendResult> => {
|
|
90
94
|
return this.appendDatasource(datasourceName, options);
|
|
91
95
|
},
|
|
96
|
+
delete: (datasourceName: string, options: DeleteOptions): Promise<DeleteResult> => {
|
|
97
|
+
return this.deleteDatasource(datasourceName, options);
|
|
98
|
+
},
|
|
99
|
+
truncate: (
|
|
100
|
+
datasourceName: string,
|
|
101
|
+
options: TruncateOptions = {}
|
|
102
|
+
): Promise<TruncateResult> => {
|
|
103
|
+
return this.truncateDatasource(datasourceName, options);
|
|
104
|
+
},
|
|
92
105
|
};
|
|
93
106
|
|
|
94
107
|
// Initialize tokens namespace
|
|
@@ -133,6 +146,46 @@ export class TinybirdClient {
|
|
|
133
146
|
}
|
|
134
147
|
}
|
|
135
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Delete rows from a datasource using a SQL condition
|
|
151
|
+
*
|
|
152
|
+
* @param datasourceName - Name of the datasource
|
|
153
|
+
* @param options - Delete options including deleteCondition
|
|
154
|
+
* @returns Delete job result
|
|
155
|
+
*/
|
|
156
|
+
private async deleteDatasource(
|
|
157
|
+
datasourceName: string,
|
|
158
|
+
options: DeleteOptions
|
|
159
|
+
): Promise<DeleteResult> {
|
|
160
|
+
const token = await this.getToken();
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
return await this.getApi(token).deleteDatasource(datasourceName, options);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
this.rethrowApiError(error);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Truncate all rows from a datasource
|
|
171
|
+
*
|
|
172
|
+
* @param datasourceName - Name of the datasource
|
|
173
|
+
* @param options - Truncate options
|
|
174
|
+
* @returns Truncate result
|
|
175
|
+
*/
|
|
176
|
+
private async truncateDatasource(
|
|
177
|
+
datasourceName: string,
|
|
178
|
+
options: TruncateOptions = {}
|
|
179
|
+
): Promise<TruncateResult> {
|
|
180
|
+
const token = await this.getToken();
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
return await this.getApi(token).truncateDatasource(datasourceName, options);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
this.rethrowApiError(error);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
136
189
|
/**
|
|
137
190
|
* Get the effective token, resolving branch token in dev mode if needed
|
|
138
191
|
*/
|
package/src/client/types.ts
CHANGED
|
@@ -236,10 +236,64 @@ export interface AppendResult {
|
|
|
236
236
|
import_id?: string;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Options for deleting rows from a datasource
|
|
241
|
+
*/
|
|
242
|
+
export interface DeleteOptions {
|
|
243
|
+
/** SQL WHERE clause condition used to select rows to delete */
|
|
244
|
+
deleteCondition: string;
|
|
245
|
+
/** Validate and return matched rows without executing deletion */
|
|
246
|
+
dryRun?: boolean;
|
|
247
|
+
/** Request timeout in milliseconds */
|
|
248
|
+
timeout?: number;
|
|
249
|
+
/** AbortController signal for cancellation */
|
|
250
|
+
signal?: AbortSignal;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Result of deleting rows from a datasource
|
|
255
|
+
*/
|
|
256
|
+
export interface DeleteResult {
|
|
257
|
+
/** Delete job ID */
|
|
258
|
+
id?: string;
|
|
259
|
+
/** Same value as id */
|
|
260
|
+
job_id?: string;
|
|
261
|
+
/** Job status URL */
|
|
262
|
+
job_url?: string;
|
|
263
|
+
/** Job status */
|
|
264
|
+
status?: string;
|
|
265
|
+
/** Same value as id */
|
|
266
|
+
delete_id?: string;
|
|
267
|
+
/** Number of rows matched in dry run mode */
|
|
268
|
+
rows_to_be_deleted?: number;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Options for truncating a datasource
|
|
273
|
+
*/
|
|
274
|
+
export interface TruncateOptions {
|
|
275
|
+
/** Request timeout in milliseconds */
|
|
276
|
+
timeout?: number;
|
|
277
|
+
/** AbortController signal for cancellation */
|
|
278
|
+
signal?: AbortSignal;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Result of truncating a datasource
|
|
283
|
+
*/
|
|
284
|
+
export interface TruncateResult {
|
|
285
|
+
/** Optional status returned by the API */
|
|
286
|
+
status?: string;
|
|
287
|
+
}
|
|
288
|
+
|
|
239
289
|
/**
|
|
240
290
|
* Datasources namespace interface for raw client
|
|
241
291
|
*/
|
|
242
292
|
export interface DatasourcesNamespace {
|
|
243
293
|
/** Append data to a datasource from a URL or file */
|
|
244
294
|
append(datasourceName: string, options: AppendOptions): Promise<AppendResult>;
|
|
295
|
+
/** Delete rows from a datasource using a SQL condition */
|
|
296
|
+
delete(datasourceName: string, options: DeleteOptions): Promise<DeleteResult>;
|
|
297
|
+
/** Truncate all rows from a datasource */
|
|
298
|
+
truncate(datasourceName: string, options?: TruncateOptions): Promise<TruncateResult>;
|
|
245
299
|
}
|