@cloudwerk/cli 0.9.0 → 0.11.0
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/dist/index.js +1759 -33
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -324,7 +324,8 @@ async function build(pathArg, options) {
|
|
|
324
324
|
hydrationEndpoint: "/__cloudwerk",
|
|
325
325
|
renderer,
|
|
326
326
|
publicDir: cloudwerkConfig.publicDir ?? "public",
|
|
327
|
-
root: cwd
|
|
327
|
+
root: cwd,
|
|
328
|
+
isProduction: true
|
|
328
329
|
});
|
|
329
330
|
const tempEntryPath = path2.join(tempDir, "_server-entry.ts");
|
|
330
331
|
fs2.writeFileSync(tempEntryPath, serverEntryCode);
|
|
@@ -468,13 +469,102 @@ function formatSize(bytes) {
|
|
|
468
469
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
469
470
|
}
|
|
470
471
|
|
|
472
|
+
// src/commands/deploy.ts
|
|
473
|
+
import * as path3 from "path";
|
|
474
|
+
import * as fs3 from "fs";
|
|
475
|
+
import { spawn } from "child_process";
|
|
476
|
+
async function deploy(pathArg, options) {
|
|
477
|
+
const verbose = options.verbose ?? false;
|
|
478
|
+
const logger = createLogger(verbose);
|
|
479
|
+
try {
|
|
480
|
+
const cwd = pathArg ? path3.resolve(process.cwd(), pathArg) : process.cwd();
|
|
481
|
+
if (!fs3.existsSync(cwd)) {
|
|
482
|
+
throw new CliError(
|
|
483
|
+
`Directory does not exist: ${cwd}`,
|
|
484
|
+
"ENOENT",
|
|
485
|
+
`Make sure the path exists and try again.`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
logger.debug(`Working directory: ${cwd}`);
|
|
489
|
+
const wranglerPath = path3.join(cwd, "wrangler.toml");
|
|
490
|
+
if (!fs3.existsSync(wranglerPath)) {
|
|
491
|
+
throw new CliError(
|
|
492
|
+
`wrangler.toml not found in ${cwd}`,
|
|
493
|
+
"ENOENT",
|
|
494
|
+
`Create a wrangler.toml file or run this command from a directory containing one.`
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
logger.debug(`Found wrangler.toml: ${wranglerPath}`);
|
|
498
|
+
if (!options.skipBuild) {
|
|
499
|
+
logger.info("Building project...");
|
|
500
|
+
await build(pathArg, {
|
|
501
|
+
config: options.config,
|
|
502
|
+
verbose: options.verbose
|
|
503
|
+
});
|
|
504
|
+
console.log();
|
|
505
|
+
}
|
|
506
|
+
const args = ["wrangler", "deploy"];
|
|
507
|
+
if (options.env) {
|
|
508
|
+
args.push("--env", options.env);
|
|
509
|
+
}
|
|
510
|
+
if (options.dryRun) {
|
|
511
|
+
args.push("--dry-run");
|
|
512
|
+
}
|
|
513
|
+
const envLabel = options.env ? ` to ${options.env}` : "";
|
|
514
|
+
const dryRunLabel = options.dryRun ? " (dry run)" : "";
|
|
515
|
+
logger.info(`Deploying${envLabel}${dryRunLabel}...`);
|
|
516
|
+
logger.debug(`Running: npx ${args.join(" ")}`);
|
|
517
|
+
const exitCode = await runCommand(args, cwd);
|
|
518
|
+
if (exitCode !== 0) {
|
|
519
|
+
throw new CliError(
|
|
520
|
+
`Deployment failed with exit code ${exitCode}`,
|
|
521
|
+
"EDEPLOY",
|
|
522
|
+
`Check the wrangler output above for details.`
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
if (!options.dryRun) {
|
|
526
|
+
logger.success("Deployment complete!");
|
|
527
|
+
} else {
|
|
528
|
+
logger.success("Dry run complete!");
|
|
529
|
+
}
|
|
530
|
+
} catch (error) {
|
|
531
|
+
if (error instanceof CliError) {
|
|
532
|
+
printError(error.message, error.suggestion);
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
if (error instanceof Error) {
|
|
536
|
+
printError(error.message);
|
|
537
|
+
if (verbose && error.stack) {
|
|
538
|
+
console.log(error.stack);
|
|
539
|
+
}
|
|
540
|
+
process.exit(1);
|
|
541
|
+
}
|
|
542
|
+
printError(String(error));
|
|
543
|
+
process.exit(1);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
function runCommand(args, cwd) {
|
|
547
|
+
return new Promise((resolve4, reject) => {
|
|
548
|
+
const child = spawn("npx", args, {
|
|
549
|
+
cwd,
|
|
550
|
+
stdio: "inherit"
|
|
551
|
+
});
|
|
552
|
+
child.on("error", (error) => {
|
|
553
|
+
reject(error);
|
|
554
|
+
});
|
|
555
|
+
child.on("close", (code) => {
|
|
556
|
+
resolve4(code ?? 0);
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
471
561
|
// src/commands/config.ts
|
|
472
562
|
import * as readline from "readline";
|
|
473
563
|
import pc2 from "picocolors";
|
|
474
564
|
|
|
475
565
|
// src/utils/configWriter.ts
|
|
476
|
-
import * as
|
|
477
|
-
import * as
|
|
566
|
+
import * as fs4 from "fs";
|
|
567
|
+
import * as path4 from "path";
|
|
478
568
|
import { loadConfig as loadConfig2 } from "@cloudwerk/core/build";
|
|
479
569
|
var CONFIG_FILE_NAMES = [
|
|
480
570
|
"cloudwerk.config.ts",
|
|
@@ -483,8 +573,8 @@ var CONFIG_FILE_NAMES = [
|
|
|
483
573
|
];
|
|
484
574
|
function findConfigFile(cwd) {
|
|
485
575
|
for (const filename of CONFIG_FILE_NAMES) {
|
|
486
|
-
const configPath =
|
|
487
|
-
if (
|
|
576
|
+
const configPath = path4.join(cwd, filename);
|
|
577
|
+
if (fs4.existsSync(configPath)) {
|
|
488
578
|
return configPath;
|
|
489
579
|
}
|
|
490
580
|
}
|
|
@@ -495,7 +585,7 @@ function readCloudwerkConfig(cwd) {
|
|
|
495
585
|
if (!configPath) {
|
|
496
586
|
return {};
|
|
497
587
|
}
|
|
498
|
-
const content =
|
|
588
|
+
const content = fs4.readFileSync(configPath, "utf-8");
|
|
499
589
|
return parseConfigContent(content);
|
|
500
590
|
}
|
|
501
591
|
function parseConfigContent(content) {
|
|
@@ -516,16 +606,16 @@ function parseConfigContent(content) {
|
|
|
516
606
|
function writeCloudwerkConfig(cwd, updates) {
|
|
517
607
|
const configPath = findConfigFile(cwd);
|
|
518
608
|
if (!configPath) {
|
|
519
|
-
const newConfigPath =
|
|
609
|
+
const newConfigPath = path4.join(cwd, "cloudwerk.config.ts");
|
|
520
610
|
const content2 = generateMinimalConfig(updates);
|
|
521
|
-
|
|
611
|
+
fs4.writeFileSync(newConfigPath, content2, "utf-8");
|
|
522
612
|
return true;
|
|
523
613
|
}
|
|
524
|
-
let content =
|
|
614
|
+
let content = fs4.readFileSync(configPath, "utf-8");
|
|
525
615
|
if (updates.renderer !== void 0) {
|
|
526
616
|
content = updateRenderer(content, updates.renderer);
|
|
527
617
|
}
|
|
528
|
-
|
|
618
|
+
fs4.writeFileSync(configPath, content, "utf-8");
|
|
529
619
|
return true;
|
|
530
620
|
}
|
|
531
621
|
function updateRenderer(content, renderer) {
|
|
@@ -594,11 +684,11 @@ function generateMinimalConfig(updates) {
|
|
|
594
684
|
}
|
|
595
685
|
|
|
596
686
|
// src/utils/tsconfigWriter.ts
|
|
597
|
-
import * as
|
|
598
|
-
import * as
|
|
687
|
+
import * as fs5 from "fs";
|
|
688
|
+
import * as path5 from "path";
|
|
599
689
|
function findTsConfig(cwd) {
|
|
600
|
-
const tsconfigPath =
|
|
601
|
-
if (
|
|
690
|
+
const tsconfigPath = path5.join(cwd, "tsconfig.json");
|
|
691
|
+
if (fs5.existsSync(tsconfigPath)) {
|
|
602
692
|
return tsconfigPath;
|
|
603
693
|
}
|
|
604
694
|
return null;
|
|
@@ -609,7 +699,7 @@ function readTsConfig(cwd) {
|
|
|
609
699
|
return {};
|
|
610
700
|
}
|
|
611
701
|
try {
|
|
612
|
-
const content =
|
|
702
|
+
const content = fs5.readFileSync(tsconfigPath, "utf-8");
|
|
613
703
|
const config = JSON.parse(content);
|
|
614
704
|
return {
|
|
615
705
|
jsxImportSource: config.compilerOptions?.jsxImportSource,
|
|
@@ -625,7 +715,7 @@ function updateTsConfig(cwd, updates) {
|
|
|
625
715
|
return false;
|
|
626
716
|
}
|
|
627
717
|
try {
|
|
628
|
-
const content =
|
|
718
|
+
const content = fs5.readFileSync(tsconfigPath, "utf-8");
|
|
629
719
|
const config = JSON.parse(content);
|
|
630
720
|
if (!config.compilerOptions) {
|
|
631
721
|
config.compilerOptions = {};
|
|
@@ -633,7 +723,7 @@ function updateTsConfig(cwd, updates) {
|
|
|
633
723
|
if (updates.jsxImportSource !== void 0) {
|
|
634
724
|
config.compilerOptions.jsxImportSource = updates.jsxImportSource;
|
|
635
725
|
}
|
|
636
|
-
|
|
726
|
+
fs5.writeFileSync(tsconfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
637
727
|
return true;
|
|
638
728
|
} catch {
|
|
639
729
|
return false;
|
|
@@ -641,8 +731,8 @@ function updateTsConfig(cwd, updates) {
|
|
|
641
731
|
}
|
|
642
732
|
|
|
643
733
|
// src/utils/dependencyManager.ts
|
|
644
|
-
import * as
|
|
645
|
-
import * as
|
|
734
|
+
import * as fs6 from "fs";
|
|
735
|
+
import * as path6 from "path";
|
|
646
736
|
import { execFileSync } from "child_process";
|
|
647
737
|
var REACT_SPECIFIC_LIBRARIES = [
|
|
648
738
|
"@tanstack/react-query",
|
|
@@ -683,22 +773,22 @@ var REACT_SPECIFIC_LIBRARIES = [
|
|
|
683
773
|
"@use-gesture/react"
|
|
684
774
|
];
|
|
685
775
|
function detectPackageManager(cwd) {
|
|
686
|
-
if (
|
|
776
|
+
if (fs6.existsSync(path6.join(cwd, "pnpm-lock.yaml"))) {
|
|
687
777
|
return "pnpm";
|
|
688
778
|
}
|
|
689
|
-
if (
|
|
779
|
+
if (fs6.existsSync(path6.join(cwd, "yarn.lock"))) {
|
|
690
780
|
return "yarn";
|
|
691
781
|
}
|
|
692
|
-
if (
|
|
782
|
+
if (fs6.existsSync(path6.join(cwd, "bun.lockb"))) {
|
|
693
783
|
return "bun";
|
|
694
784
|
}
|
|
695
|
-
if (
|
|
785
|
+
if (fs6.existsSync(path6.join(cwd, "package-lock.json"))) {
|
|
696
786
|
return "npm";
|
|
697
787
|
}
|
|
698
|
-
const packageJsonPath =
|
|
699
|
-
if (
|
|
788
|
+
const packageJsonPath = path6.join(cwd, "package.json");
|
|
789
|
+
if (fs6.existsSync(packageJsonPath)) {
|
|
700
790
|
try {
|
|
701
|
-
const packageJson = JSON.parse(
|
|
791
|
+
const packageJson = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
|
|
702
792
|
const pm = packageJson.packageManager;
|
|
703
793
|
if (pm) {
|
|
704
794
|
if (pm.startsWith("pnpm")) return "pnpm";
|
|
@@ -712,15 +802,15 @@ function detectPackageManager(cwd) {
|
|
|
712
802
|
return "npm";
|
|
713
803
|
}
|
|
714
804
|
function getInstalledDependencies(cwd) {
|
|
715
|
-
const packageJsonPath =
|
|
716
|
-
if (!
|
|
805
|
+
const packageJsonPath = path6.join(cwd, "package.json");
|
|
806
|
+
if (!fs6.existsSync(packageJsonPath)) {
|
|
717
807
|
return {
|
|
718
808
|
dependencies: {},
|
|
719
809
|
devDependencies: {}
|
|
720
810
|
};
|
|
721
811
|
}
|
|
722
812
|
try {
|
|
723
|
-
const content =
|
|
813
|
+
const content = fs6.readFileSync(packageJsonPath, "utf-8");
|
|
724
814
|
const packageJson = JSON.parse(content);
|
|
725
815
|
return {
|
|
726
816
|
dependencies: packageJson.dependencies || {},
|
|
@@ -1043,26 +1133,1662 @@ async function askConfirmation(question, defaultValue) {
|
|
|
1043
1133
|
output: process.stdout
|
|
1044
1134
|
});
|
|
1045
1135
|
const hint = defaultValue ? "Y/n" : "y/N";
|
|
1046
|
-
return new Promise((
|
|
1136
|
+
return new Promise((resolve4) => {
|
|
1047
1137
|
rl.question(pc2.cyan("? ") + question + pc2.dim(` (${hint}) `), (answer) => {
|
|
1048
1138
|
rl.close();
|
|
1049
1139
|
const normalized = answer.trim().toLowerCase();
|
|
1050
1140
|
if (normalized === "") {
|
|
1051
|
-
|
|
1141
|
+
resolve4(defaultValue);
|
|
1052
1142
|
} else if (normalized === "y" || normalized === "yes") {
|
|
1053
|
-
|
|
1143
|
+
resolve4(true);
|
|
1054
1144
|
} else {
|
|
1055
|
-
|
|
1145
|
+
resolve4(false);
|
|
1056
1146
|
}
|
|
1057
1147
|
});
|
|
1058
1148
|
});
|
|
1059
1149
|
}
|
|
1060
1150
|
|
|
1151
|
+
// src/commands/bindings.ts
|
|
1152
|
+
import * as path8 from "path";
|
|
1153
|
+
import pc4 from "picocolors";
|
|
1154
|
+
|
|
1155
|
+
// src/utils/command-error-handler.ts
|
|
1156
|
+
import pc3 from "picocolors";
|
|
1157
|
+
function handleCommandError(error, verbose = false) {
|
|
1158
|
+
if (error instanceof CliError) {
|
|
1159
|
+
printError(error.message, error.suggestion);
|
|
1160
|
+
process.exit(1);
|
|
1161
|
+
}
|
|
1162
|
+
if (error instanceof Error && (error.message.includes("User force closed") || error.name === "ExitPromptError")) {
|
|
1163
|
+
console.log();
|
|
1164
|
+
console.log(pc3.dim("Cancelled."));
|
|
1165
|
+
process.exit(0);
|
|
1166
|
+
}
|
|
1167
|
+
if (error instanceof Error) {
|
|
1168
|
+
printError(error.message);
|
|
1169
|
+
if (verbose && error.stack) {
|
|
1170
|
+
console.log(error.stack);
|
|
1171
|
+
}
|
|
1172
|
+
process.exit(1);
|
|
1173
|
+
}
|
|
1174
|
+
printError(String(error));
|
|
1175
|
+
process.exit(1);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// src/utils/wrangler-toml.ts
|
|
1179
|
+
import * as fs7 from "fs";
|
|
1180
|
+
import * as path7 from "path";
|
|
1181
|
+
import TOML from "@iarna/toml";
|
|
1182
|
+
function findWranglerToml(cwd) {
|
|
1183
|
+
const wranglerPath = path7.join(cwd, "wrangler.toml");
|
|
1184
|
+
if (fs7.existsSync(wranglerPath)) {
|
|
1185
|
+
return wranglerPath;
|
|
1186
|
+
}
|
|
1187
|
+
const wranglerJsonPath = path7.join(cwd, "wrangler.json");
|
|
1188
|
+
if (fs7.existsSync(wranglerJsonPath)) {
|
|
1189
|
+
return wranglerJsonPath;
|
|
1190
|
+
}
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1193
|
+
function readWranglerToml(cwd) {
|
|
1194
|
+
const configPath = findWranglerToml(cwd);
|
|
1195
|
+
if (!configPath) {
|
|
1196
|
+
return {};
|
|
1197
|
+
}
|
|
1198
|
+
const content = fs7.readFileSync(configPath, "utf-8");
|
|
1199
|
+
if (configPath.endsWith(".json")) {
|
|
1200
|
+
return JSON.parse(content);
|
|
1201
|
+
}
|
|
1202
|
+
return TOML.parse(content);
|
|
1203
|
+
}
|
|
1204
|
+
function writeWranglerToml(cwd, config) {
|
|
1205
|
+
const configPath = path7.join(cwd, "wrangler.toml");
|
|
1206
|
+
const content = TOML.stringify(config);
|
|
1207
|
+
fs7.writeFileSync(configPath, content, "utf-8");
|
|
1208
|
+
}
|
|
1209
|
+
function getEnvConfig(config, env) {
|
|
1210
|
+
if (!env || !config.env?.[env]) {
|
|
1211
|
+
return config;
|
|
1212
|
+
}
|
|
1213
|
+
return {
|
|
1214
|
+
...config,
|
|
1215
|
+
...config.env[env]
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
function extractBindings(config, env) {
|
|
1219
|
+
const envConfig = getEnvConfig(config, env);
|
|
1220
|
+
const bindings2 = [];
|
|
1221
|
+
if (envConfig.d1_databases) {
|
|
1222
|
+
for (const db of envConfig.d1_databases) {
|
|
1223
|
+
bindings2.push({
|
|
1224
|
+
type: "d1",
|
|
1225
|
+
name: db.binding,
|
|
1226
|
+
resourceId: db.database_id,
|
|
1227
|
+
resourceName: db.database_name
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
if (envConfig.kv_namespaces) {
|
|
1232
|
+
for (const kv of envConfig.kv_namespaces) {
|
|
1233
|
+
bindings2.push({
|
|
1234
|
+
type: "kv",
|
|
1235
|
+
name: kv.binding,
|
|
1236
|
+
resourceId: kv.id
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
if (envConfig.r2_buckets) {
|
|
1241
|
+
for (const r2 of envConfig.r2_buckets) {
|
|
1242
|
+
bindings2.push({
|
|
1243
|
+
type: "r2",
|
|
1244
|
+
name: r2.binding,
|
|
1245
|
+
resourceName: r2.bucket_name
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
if (envConfig.queues?.producers) {
|
|
1250
|
+
for (const queue of envConfig.queues.producers) {
|
|
1251
|
+
bindings2.push({
|
|
1252
|
+
type: "queue",
|
|
1253
|
+
name: queue.binding,
|
|
1254
|
+
resourceName: queue.queue
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
if (envConfig.durable_objects?.bindings) {
|
|
1259
|
+
for (const doBinding of envConfig.durable_objects.bindings) {
|
|
1260
|
+
bindings2.push({
|
|
1261
|
+
type: "do",
|
|
1262
|
+
name: doBinding.name,
|
|
1263
|
+
resourceName: doBinding.class_name,
|
|
1264
|
+
extra: doBinding.script_name ? { script_name: doBinding.script_name } : void 0
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
if (envConfig.services) {
|
|
1269
|
+
for (const service of envConfig.services) {
|
|
1270
|
+
bindings2.push({
|
|
1271
|
+
type: "service",
|
|
1272
|
+
name: service.binding,
|
|
1273
|
+
resourceName: service.service,
|
|
1274
|
+
extra: service.environment ? { environment: service.environment } : void 0
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (envConfig.vars) {
|
|
1279
|
+
for (const [name] of Object.entries(envConfig.vars)) {
|
|
1280
|
+
bindings2.push({
|
|
1281
|
+
type: "secret",
|
|
1282
|
+
name
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
if (envConfig.ai) {
|
|
1287
|
+
bindings2.push({
|
|
1288
|
+
type: "ai",
|
|
1289
|
+
name: envConfig.ai.binding
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
if (envConfig.vectorize) {
|
|
1293
|
+
for (const vec of envConfig.vectorize) {
|
|
1294
|
+
bindings2.push({
|
|
1295
|
+
type: "vectorize",
|
|
1296
|
+
name: vec.binding,
|
|
1297
|
+
resourceName: vec.index_name
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
if (envConfig.hyperdrive) {
|
|
1302
|
+
for (const hd of envConfig.hyperdrive) {
|
|
1303
|
+
bindings2.push({
|
|
1304
|
+
type: "hyperdrive",
|
|
1305
|
+
name: hd.binding,
|
|
1306
|
+
resourceId: hd.id
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return bindings2;
|
|
1311
|
+
}
|
|
1312
|
+
function getEnvironments(config) {
|
|
1313
|
+
if (!config.env) {
|
|
1314
|
+
return [];
|
|
1315
|
+
}
|
|
1316
|
+
return Object.keys(config.env);
|
|
1317
|
+
}
|
|
1318
|
+
function addD1Binding(cwd, bindingName, databaseName, databaseId, env) {
|
|
1319
|
+
const config = readWranglerToml(cwd);
|
|
1320
|
+
const newBinding = {
|
|
1321
|
+
binding: bindingName,
|
|
1322
|
+
database_name: databaseName,
|
|
1323
|
+
database_id: databaseId
|
|
1324
|
+
};
|
|
1325
|
+
if (env) {
|
|
1326
|
+
if (!config.env) config.env = {};
|
|
1327
|
+
if (!config.env[env]) config.env[env] = {};
|
|
1328
|
+
if (!config.env[env].d1_databases) config.env[env].d1_databases = [];
|
|
1329
|
+
config.env[env].d1_databases.push(newBinding);
|
|
1330
|
+
} else {
|
|
1331
|
+
if (!config.d1_databases) config.d1_databases = [];
|
|
1332
|
+
config.d1_databases.push(newBinding);
|
|
1333
|
+
}
|
|
1334
|
+
writeWranglerToml(cwd, config);
|
|
1335
|
+
}
|
|
1336
|
+
function addKVBinding(cwd, bindingName, namespaceId, previewId, env) {
|
|
1337
|
+
const config = readWranglerToml(cwd);
|
|
1338
|
+
const newBinding = {
|
|
1339
|
+
binding: bindingName,
|
|
1340
|
+
id: namespaceId
|
|
1341
|
+
};
|
|
1342
|
+
if (previewId) {
|
|
1343
|
+
newBinding.preview_id = previewId;
|
|
1344
|
+
}
|
|
1345
|
+
if (env) {
|
|
1346
|
+
if (!config.env) config.env = {};
|
|
1347
|
+
if (!config.env[env]) config.env[env] = {};
|
|
1348
|
+
if (!config.env[env].kv_namespaces) config.env[env].kv_namespaces = [];
|
|
1349
|
+
config.env[env].kv_namespaces.push(newBinding);
|
|
1350
|
+
} else {
|
|
1351
|
+
if (!config.kv_namespaces) config.kv_namespaces = [];
|
|
1352
|
+
config.kv_namespaces.push(newBinding);
|
|
1353
|
+
}
|
|
1354
|
+
writeWranglerToml(cwd, config);
|
|
1355
|
+
}
|
|
1356
|
+
function addR2Binding(cwd, bindingName, bucketName, env) {
|
|
1357
|
+
const config = readWranglerToml(cwd);
|
|
1358
|
+
const newBinding = {
|
|
1359
|
+
binding: bindingName,
|
|
1360
|
+
bucket_name: bucketName
|
|
1361
|
+
};
|
|
1362
|
+
if (env) {
|
|
1363
|
+
if (!config.env) config.env = {};
|
|
1364
|
+
if (!config.env[env]) config.env[env] = {};
|
|
1365
|
+
if (!config.env[env].r2_buckets) config.env[env].r2_buckets = [];
|
|
1366
|
+
config.env[env].r2_buckets.push(newBinding);
|
|
1367
|
+
} else {
|
|
1368
|
+
if (!config.r2_buckets) config.r2_buckets = [];
|
|
1369
|
+
config.r2_buckets.push(newBinding);
|
|
1370
|
+
}
|
|
1371
|
+
writeWranglerToml(cwd, config);
|
|
1372
|
+
}
|
|
1373
|
+
function addQueueBinding(cwd, bindingName, queueName, env) {
|
|
1374
|
+
const config = readWranglerToml(cwd);
|
|
1375
|
+
const newBinding = {
|
|
1376
|
+
binding: bindingName,
|
|
1377
|
+
queue: queueName
|
|
1378
|
+
};
|
|
1379
|
+
if (env) {
|
|
1380
|
+
if (!config.env) config.env = {};
|
|
1381
|
+
if (!config.env[env]) config.env[env] = {};
|
|
1382
|
+
if (!config.env[env].queues) config.env[env].queues = {};
|
|
1383
|
+
if (!config.env[env].queues.producers)
|
|
1384
|
+
config.env[env].queues.producers = [];
|
|
1385
|
+
config.env[env].queues.producers.push(newBinding);
|
|
1386
|
+
} else {
|
|
1387
|
+
if (!config.queues) config.queues = {};
|
|
1388
|
+
if (!config.queues.producers) config.queues.producers = [];
|
|
1389
|
+
config.queues.producers.push(newBinding);
|
|
1390
|
+
}
|
|
1391
|
+
writeWranglerToml(cwd, config);
|
|
1392
|
+
}
|
|
1393
|
+
function addDurableObjectBinding(cwd, bindingName, className, scriptName, env) {
|
|
1394
|
+
const config = readWranglerToml(cwd);
|
|
1395
|
+
const newBinding = {
|
|
1396
|
+
name: bindingName,
|
|
1397
|
+
class_name: className
|
|
1398
|
+
};
|
|
1399
|
+
if (scriptName) {
|
|
1400
|
+
newBinding.script_name = scriptName;
|
|
1401
|
+
}
|
|
1402
|
+
if (env) {
|
|
1403
|
+
if (!config.env) config.env = {};
|
|
1404
|
+
if (!config.env[env]) config.env[env] = {};
|
|
1405
|
+
if (!config.env[env].durable_objects)
|
|
1406
|
+
config.env[env].durable_objects = { bindings: [] };
|
|
1407
|
+
config.env[env].durable_objects.bindings.push(newBinding);
|
|
1408
|
+
} else {
|
|
1409
|
+
if (!config.durable_objects) config.durable_objects = { bindings: [] };
|
|
1410
|
+
if (!config.durable_objects.bindings) config.durable_objects.bindings = [];
|
|
1411
|
+
config.durable_objects.bindings.push(newBinding);
|
|
1412
|
+
}
|
|
1413
|
+
writeWranglerToml(cwd, config);
|
|
1414
|
+
}
|
|
1415
|
+
function addSecretBinding(cwd, name, value, env) {
|
|
1416
|
+
const config = readWranglerToml(cwd);
|
|
1417
|
+
if (env) {
|
|
1418
|
+
if (!config.env) config.env = {};
|
|
1419
|
+
if (!config.env[env]) config.env[env] = {};
|
|
1420
|
+
if (!config.env[env].vars) config.env[env].vars = {};
|
|
1421
|
+
config.env[env].vars[name] = value;
|
|
1422
|
+
} else {
|
|
1423
|
+
if (!config.vars) config.vars = {};
|
|
1424
|
+
config.vars[name] = value;
|
|
1425
|
+
}
|
|
1426
|
+
writeWranglerToml(cwd, config);
|
|
1427
|
+
}
|
|
1428
|
+
function removeBinding(cwd, bindingName, env) {
|
|
1429
|
+
const config = readWranglerToml(cwd);
|
|
1430
|
+
let removed = false;
|
|
1431
|
+
const removeFromConfig = (cfg) => {
|
|
1432
|
+
let found = false;
|
|
1433
|
+
if (cfg.d1_databases) {
|
|
1434
|
+
const idx = cfg.d1_databases.findIndex((b) => b.binding === bindingName);
|
|
1435
|
+
if (idx !== -1) {
|
|
1436
|
+
cfg.d1_databases.splice(idx, 1);
|
|
1437
|
+
if (cfg.d1_databases.length === 0) delete cfg.d1_databases;
|
|
1438
|
+
found = true;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
if (cfg.kv_namespaces) {
|
|
1442
|
+
const idx = cfg.kv_namespaces.findIndex((b) => b.binding === bindingName);
|
|
1443
|
+
if (idx !== -1) {
|
|
1444
|
+
cfg.kv_namespaces.splice(idx, 1);
|
|
1445
|
+
if (cfg.kv_namespaces.length === 0) delete cfg.kv_namespaces;
|
|
1446
|
+
found = true;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
if (cfg.r2_buckets) {
|
|
1450
|
+
const idx = cfg.r2_buckets.findIndex((b) => b.binding === bindingName);
|
|
1451
|
+
if (idx !== -1) {
|
|
1452
|
+
cfg.r2_buckets.splice(idx, 1);
|
|
1453
|
+
if (cfg.r2_buckets.length === 0) delete cfg.r2_buckets;
|
|
1454
|
+
found = true;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
if (cfg.queues?.producers) {
|
|
1458
|
+
const idx = cfg.queues.producers.findIndex(
|
|
1459
|
+
(b) => b.binding === bindingName
|
|
1460
|
+
);
|
|
1461
|
+
if (idx !== -1) {
|
|
1462
|
+
cfg.queues.producers.splice(idx, 1);
|
|
1463
|
+
if (cfg.queues.producers.length === 0) delete cfg.queues.producers;
|
|
1464
|
+
if (!cfg.queues.producers && !cfg.queues.consumers) delete cfg.queues;
|
|
1465
|
+
found = true;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
if (cfg.durable_objects?.bindings) {
|
|
1469
|
+
const idx = cfg.durable_objects.bindings.findIndex(
|
|
1470
|
+
(b) => b.name === bindingName
|
|
1471
|
+
);
|
|
1472
|
+
if (idx !== -1) {
|
|
1473
|
+
cfg.durable_objects.bindings.splice(idx, 1);
|
|
1474
|
+
if (cfg.durable_objects.bindings.length === 0)
|
|
1475
|
+
delete cfg.durable_objects;
|
|
1476
|
+
found = true;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (cfg.services) {
|
|
1480
|
+
const idx = cfg.services.findIndex((b) => b.binding === bindingName);
|
|
1481
|
+
if (idx !== -1) {
|
|
1482
|
+
cfg.services.splice(idx, 1);
|
|
1483
|
+
if (cfg.services.length === 0) delete cfg.services;
|
|
1484
|
+
found = true;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
if (cfg.vars && bindingName in cfg.vars) {
|
|
1488
|
+
delete cfg.vars[bindingName];
|
|
1489
|
+
if (Object.keys(cfg.vars).length === 0) delete cfg.vars;
|
|
1490
|
+
found = true;
|
|
1491
|
+
}
|
|
1492
|
+
if (cfg.ai && cfg.ai.binding === bindingName) {
|
|
1493
|
+
delete cfg.ai;
|
|
1494
|
+
found = true;
|
|
1495
|
+
}
|
|
1496
|
+
if (cfg.vectorize) {
|
|
1497
|
+
const idx = cfg.vectorize.findIndex((b) => b.binding === bindingName);
|
|
1498
|
+
if (idx !== -1) {
|
|
1499
|
+
cfg.vectorize.splice(idx, 1);
|
|
1500
|
+
if (cfg.vectorize.length === 0) delete cfg.vectorize;
|
|
1501
|
+
found = true;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
if (cfg.hyperdrive) {
|
|
1505
|
+
const idx = cfg.hyperdrive.findIndex((b) => b.binding === bindingName);
|
|
1506
|
+
if (idx !== -1) {
|
|
1507
|
+
cfg.hyperdrive.splice(idx, 1);
|
|
1508
|
+
if (cfg.hyperdrive.length === 0) delete cfg.hyperdrive;
|
|
1509
|
+
found = true;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return found;
|
|
1513
|
+
};
|
|
1514
|
+
if (env && config.env?.[env]) {
|
|
1515
|
+
removed = removeFromConfig(config.env[env]);
|
|
1516
|
+
} else {
|
|
1517
|
+
removed = removeFromConfig(config);
|
|
1518
|
+
}
|
|
1519
|
+
if (removed) {
|
|
1520
|
+
writeWranglerToml(cwd, config);
|
|
1521
|
+
}
|
|
1522
|
+
return removed;
|
|
1523
|
+
}
|
|
1524
|
+
function bindingExists(config, bindingName, env) {
|
|
1525
|
+
const bindings2 = extractBindings(config, env);
|
|
1526
|
+
return bindings2.some((b) => b.name === bindingName);
|
|
1527
|
+
}
|
|
1528
|
+
function getBindingTypeName(type) {
|
|
1529
|
+
switch (type) {
|
|
1530
|
+
case "d1":
|
|
1531
|
+
return "D1";
|
|
1532
|
+
case "kv":
|
|
1533
|
+
return "KV";
|
|
1534
|
+
case "r2":
|
|
1535
|
+
return "R2";
|
|
1536
|
+
case "queue":
|
|
1537
|
+
return "Queue";
|
|
1538
|
+
case "do":
|
|
1539
|
+
return "Durable Object";
|
|
1540
|
+
case "service":
|
|
1541
|
+
return "Service";
|
|
1542
|
+
case "secret":
|
|
1543
|
+
return "Secret/Var";
|
|
1544
|
+
case "ai":
|
|
1545
|
+
return "AI";
|
|
1546
|
+
case "vectorize":
|
|
1547
|
+
return "Vectorize";
|
|
1548
|
+
case "hyperdrive":
|
|
1549
|
+
return "Hyperdrive";
|
|
1550
|
+
default:
|
|
1551
|
+
return type;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
function truncateId(id, maxLen = 12) {
|
|
1555
|
+
if (id.length <= maxLen) return id;
|
|
1556
|
+
return id.slice(0, maxLen - 3) + "...";
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// src/commands/bindings.ts
|
|
1560
|
+
async function bindings(options) {
|
|
1561
|
+
const verbose = options.verbose ?? false;
|
|
1562
|
+
const logger = createLogger(verbose);
|
|
1563
|
+
const env = options.env;
|
|
1564
|
+
try {
|
|
1565
|
+
const cwd = process.cwd();
|
|
1566
|
+
const wranglerPath = findWranglerToml(cwd);
|
|
1567
|
+
if (!wranglerPath) {
|
|
1568
|
+
throw new CliError(
|
|
1569
|
+
"wrangler.toml not found",
|
|
1570
|
+
"ENOENT",
|
|
1571
|
+
"Create a wrangler.toml file or run this command from a Cloudwerk project directory."
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
logger.debug(`Found wrangler config: ${wranglerPath}`);
|
|
1575
|
+
const config = readWranglerToml(cwd);
|
|
1576
|
+
const projectName = config.name || path8.basename(cwd);
|
|
1577
|
+
const bindingsList = extractBindings(config, env);
|
|
1578
|
+
const environments = getEnvironments(config);
|
|
1579
|
+
console.log();
|
|
1580
|
+
const envLabel = env ? env : "production";
|
|
1581
|
+
console.log(
|
|
1582
|
+
pc4.bold(`Bindings for ${projectName}`) + pc4.dim(` (${envLabel}):`)
|
|
1583
|
+
);
|
|
1584
|
+
console.log();
|
|
1585
|
+
if (bindingsList.length === 0) {
|
|
1586
|
+
console.log(pc4.dim(" No bindings configured."));
|
|
1587
|
+
console.log();
|
|
1588
|
+
console.log(
|
|
1589
|
+
pc4.dim(
|
|
1590
|
+
` Use 'cloudwerk bindings add' to add a new binding${env ? ` --env ${env}` : ""}.`
|
|
1591
|
+
)
|
|
1592
|
+
);
|
|
1593
|
+
} else {
|
|
1594
|
+
console.log(
|
|
1595
|
+
pc4.dim(" Type Binding Resource")
|
|
1596
|
+
);
|
|
1597
|
+
console.log(
|
|
1598
|
+
pc4.dim(" " + "\u2500".repeat(14) + " " + "\u2500".repeat(13) + " " + "\u2500".repeat(30))
|
|
1599
|
+
);
|
|
1600
|
+
for (const binding of bindingsList) {
|
|
1601
|
+
const typeName = getBindingTypeName(binding.type).padEnd(14);
|
|
1602
|
+
const bindingName = binding.name.padEnd(13);
|
|
1603
|
+
let resource = "";
|
|
1604
|
+
if (binding.resourceName && binding.resourceId) {
|
|
1605
|
+
resource = `${binding.resourceName} (${truncateId(binding.resourceId)})`;
|
|
1606
|
+
} else if (binding.resourceName) {
|
|
1607
|
+
resource = binding.resourceName;
|
|
1608
|
+
} else if (binding.resourceId) {
|
|
1609
|
+
resource = `(${truncateId(binding.resourceId)})`;
|
|
1610
|
+
} else {
|
|
1611
|
+
resource = pc4.dim("(configured)");
|
|
1612
|
+
}
|
|
1613
|
+
console.log(` ${pc4.cyan(typeName)} ${pc4.bold(bindingName)} ${resource}`);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
console.log();
|
|
1617
|
+
if (environments.length > 0 && !env) {
|
|
1618
|
+
console.log(
|
|
1619
|
+
pc4.dim("Environments: ") + environments.join(", ")
|
|
1620
|
+
);
|
|
1621
|
+
console.log();
|
|
1622
|
+
}
|
|
1623
|
+
const envFlag = env ? ` --env ${env}` : "";
|
|
1624
|
+
console.log(pc4.dim(`Use 'cloudwerk bindings add${envFlag}' to add a new binding.`));
|
|
1625
|
+
if (!env && environments.length > 0) {
|
|
1626
|
+
console.log(
|
|
1627
|
+
pc4.dim(`Use 'cloudwerk bindings --env <name>' to view environment bindings.`)
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
console.log();
|
|
1631
|
+
} catch (error) {
|
|
1632
|
+
handleCommandError(error, verbose);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
// src/commands/bindings/add.ts
|
|
1637
|
+
import { spawn as spawn2 } from "child_process";
|
|
1638
|
+
import pc5 from "picocolors";
|
|
1639
|
+
import { select, input, confirm } from "@inquirer/prompts";
|
|
1640
|
+
|
|
1641
|
+
// src/utils/env-types.ts
|
|
1642
|
+
import * as fs8 from "fs";
|
|
1643
|
+
import * as path9 from "path";
|
|
1644
|
+
var TYPE_MAPPINGS = {
|
|
1645
|
+
d1: "D1Database",
|
|
1646
|
+
kv: "KVNamespace",
|
|
1647
|
+
r2: "R2Bucket",
|
|
1648
|
+
queue: "Queue",
|
|
1649
|
+
do: "DurableObjectNamespace",
|
|
1650
|
+
service: "Fetcher",
|
|
1651
|
+
secret: "string",
|
|
1652
|
+
ai: "Ai",
|
|
1653
|
+
vectorize: "VectorizeIndex",
|
|
1654
|
+
hyperdrive: "Hyperdrive"
|
|
1655
|
+
};
|
|
1656
|
+
function getTypeForBinding(type) {
|
|
1657
|
+
return TYPE_MAPPINGS[type] || "unknown";
|
|
1658
|
+
}
|
|
1659
|
+
function generateEnvTypes(cwd, bindings2, options = {}) {
|
|
1660
|
+
const outputPath = options.outputPath || path9.join(cwd, "env.d.ts");
|
|
1661
|
+
const includeTimestamp = options.includeTimestamp ?? true;
|
|
1662
|
+
const bindingsByType = /* @__PURE__ */ new Map();
|
|
1663
|
+
for (const binding of bindings2) {
|
|
1664
|
+
const existing = bindingsByType.get(binding.type) || [];
|
|
1665
|
+
existing.push(binding);
|
|
1666
|
+
bindingsByType.set(binding.type, existing);
|
|
1667
|
+
}
|
|
1668
|
+
const lines = [];
|
|
1669
|
+
lines.push("// Auto-generated by cloudwerk bindings - DO NOT EDIT");
|
|
1670
|
+
if (includeTimestamp) {
|
|
1671
|
+
lines.push(`// Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
1672
|
+
}
|
|
1673
|
+
lines.push("");
|
|
1674
|
+
lines.push("interface CloudflareBindings {");
|
|
1675
|
+
const typeOrder = [
|
|
1676
|
+
"d1",
|
|
1677
|
+
"kv",
|
|
1678
|
+
"r2",
|
|
1679
|
+
"queue",
|
|
1680
|
+
"do",
|
|
1681
|
+
"service",
|
|
1682
|
+
"ai",
|
|
1683
|
+
"vectorize",
|
|
1684
|
+
"hyperdrive",
|
|
1685
|
+
"secret"
|
|
1686
|
+
];
|
|
1687
|
+
const resultBindings = [];
|
|
1688
|
+
let firstSection = true;
|
|
1689
|
+
for (const type of typeOrder) {
|
|
1690
|
+
const typeBindings = bindingsByType.get(type);
|
|
1691
|
+
if (!typeBindings || typeBindings.length === 0) continue;
|
|
1692
|
+
const tsType = getTypeForBinding(type);
|
|
1693
|
+
const sectionName = getSectionName(type);
|
|
1694
|
+
if (!firstSection) {
|
|
1695
|
+
lines.push("");
|
|
1696
|
+
}
|
|
1697
|
+
lines.push(` // ${sectionName}`);
|
|
1698
|
+
firstSection = false;
|
|
1699
|
+
for (const binding of typeBindings) {
|
|
1700
|
+
lines.push(` ${binding.name}: ${tsType}`);
|
|
1701
|
+
resultBindings.push({ name: binding.name, type: tsType });
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
lines.push("}");
|
|
1705
|
+
lines.push("");
|
|
1706
|
+
lines.push("// Re-export for convenience");
|
|
1707
|
+
lines.push("type Env = CloudflareBindings");
|
|
1708
|
+
lines.push("export type { Env, CloudflareBindings }");
|
|
1709
|
+
lines.push("");
|
|
1710
|
+
fs8.writeFileSync(outputPath, lines.join("\n"), "utf-8");
|
|
1711
|
+
return {
|
|
1712
|
+
outputPath,
|
|
1713
|
+
bindingCount: bindings2.length,
|
|
1714
|
+
bindings: resultBindings
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
function getSectionName(type) {
|
|
1718
|
+
switch (type) {
|
|
1719
|
+
case "d1":
|
|
1720
|
+
return "D1 Databases";
|
|
1721
|
+
case "kv":
|
|
1722
|
+
return "KV Namespaces";
|
|
1723
|
+
case "r2":
|
|
1724
|
+
return "R2 Buckets";
|
|
1725
|
+
case "queue":
|
|
1726
|
+
return "Queues";
|
|
1727
|
+
case "do":
|
|
1728
|
+
return "Durable Objects";
|
|
1729
|
+
case "service":
|
|
1730
|
+
return "Services";
|
|
1731
|
+
case "secret":
|
|
1732
|
+
return "Environment Variables";
|
|
1733
|
+
case "ai":
|
|
1734
|
+
return "AI";
|
|
1735
|
+
case "vectorize":
|
|
1736
|
+
return "Vectorize Indexes";
|
|
1737
|
+
case "hyperdrive":
|
|
1738
|
+
return "Hyperdrive";
|
|
1739
|
+
default:
|
|
1740
|
+
return "Other";
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// src/commands/bindings/add.ts
|
|
1745
|
+
async function bindingsAdd(bindingType, options) {
|
|
1746
|
+
const verbose = options.verbose ?? false;
|
|
1747
|
+
const logger = createLogger(verbose);
|
|
1748
|
+
const env = options.env;
|
|
1749
|
+
try {
|
|
1750
|
+
const cwd = process.cwd();
|
|
1751
|
+
const wranglerPath = findWranglerToml(cwd);
|
|
1752
|
+
if (!wranglerPath) {
|
|
1753
|
+
throw new CliError(
|
|
1754
|
+
"wrangler.toml not found",
|
|
1755
|
+
"ENOENT",
|
|
1756
|
+
"Create a wrangler.toml file or run this command from a Cloudwerk project directory."
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
logger.debug(`Found wrangler config: ${wranglerPath}`);
|
|
1760
|
+
const type = bindingType || await promptForBindingType();
|
|
1761
|
+
const envLabel = env ? ` (${env})` : "";
|
|
1762
|
+
console.log();
|
|
1763
|
+
logger.info(`Adding ${type.toUpperCase()} binding${envLabel}...`);
|
|
1764
|
+
switch (type.toLowerCase()) {
|
|
1765
|
+
case "d1":
|
|
1766
|
+
await addD1(cwd, options);
|
|
1767
|
+
break;
|
|
1768
|
+
case "kv":
|
|
1769
|
+
await addKV(cwd, options);
|
|
1770
|
+
break;
|
|
1771
|
+
case "r2":
|
|
1772
|
+
await addR2(cwd, options);
|
|
1773
|
+
break;
|
|
1774
|
+
case "queue":
|
|
1775
|
+
await addQueue(cwd, options);
|
|
1776
|
+
break;
|
|
1777
|
+
case "do":
|
|
1778
|
+
await addDurableObject(cwd, options);
|
|
1779
|
+
break;
|
|
1780
|
+
case "secret":
|
|
1781
|
+
await addSecret(cwd, options);
|
|
1782
|
+
break;
|
|
1783
|
+
default:
|
|
1784
|
+
throw new CliError(
|
|
1785
|
+
`Unknown binding type: ${type}`,
|
|
1786
|
+
"EINVAL",
|
|
1787
|
+
"Valid types are: d1, kv, r2, queue, do, secret"
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
if (!options.skipTypes) {
|
|
1791
|
+
await regenerateTypes(cwd);
|
|
1792
|
+
}
|
|
1793
|
+
console.log();
|
|
1794
|
+
logger.success("Binding added successfully!");
|
|
1795
|
+
console.log();
|
|
1796
|
+
} catch (error) {
|
|
1797
|
+
handleCommandError(error, verbose);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
async function promptForBindingType() {
|
|
1801
|
+
return select({
|
|
1802
|
+
message: "What type of binding do you want to add?",
|
|
1803
|
+
choices: [
|
|
1804
|
+
{ name: "D1 Database", value: "d1" },
|
|
1805
|
+
{ name: "KV Namespace", value: "kv" },
|
|
1806
|
+
{ name: "R2 Bucket", value: "r2" },
|
|
1807
|
+
{ name: "Queue", value: "queue" },
|
|
1808
|
+
{ name: "Durable Object", value: "do" },
|
|
1809
|
+
{ name: "Secret / Environment Variable", value: "secret" }
|
|
1810
|
+
]
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
async function addD1(cwd, options) {
|
|
1814
|
+
const config = readWranglerToml(cwd);
|
|
1815
|
+
const env = options.env;
|
|
1816
|
+
const bindingName = await input({
|
|
1817
|
+
message: "Binding name (e.g., DB):",
|
|
1818
|
+
validate: (value) => {
|
|
1819
|
+
if (!value.trim()) return "Binding name is required";
|
|
1820
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
|
|
1821
|
+
return "Binding name must be alphanumeric with underscores";
|
|
1822
|
+
if (bindingExists(config, value, env))
|
|
1823
|
+
return `Binding "${value}" already exists`;
|
|
1824
|
+
return true;
|
|
1825
|
+
}
|
|
1826
|
+
});
|
|
1827
|
+
const databaseName = await input({
|
|
1828
|
+
message: "Database name:",
|
|
1829
|
+
default: `${config.name || "my-app"}-db${env ? `-${env}` : ""}`,
|
|
1830
|
+
validate: (value) => {
|
|
1831
|
+
if (!value.trim()) return "Database name is required";
|
|
1832
|
+
return true;
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1835
|
+
const createNew = await confirm({
|
|
1836
|
+
message: "Create a new D1 database?",
|
|
1837
|
+
default: true
|
|
1838
|
+
});
|
|
1839
|
+
let databaseId;
|
|
1840
|
+
if (createNew) {
|
|
1841
|
+
console.log();
|
|
1842
|
+
console.log(pc5.dim(`Creating D1 database "${databaseName}"...`));
|
|
1843
|
+
const result = await runWranglerCommand(["d1", "create", databaseName]);
|
|
1844
|
+
const idMatch = result.match(/database_id\s*=\s*"([^"]+)"/);
|
|
1845
|
+
if (!idMatch) {
|
|
1846
|
+
throw new CliError(
|
|
1847
|
+
"Failed to parse database ID from wrangler output",
|
|
1848
|
+
"EPARSE",
|
|
1849
|
+
"Try creating the database manually with: wrangler d1 create " + databaseName
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
databaseId = idMatch[1];
|
|
1853
|
+
console.log(pc5.green("\u2713") + ` Created database: ${databaseId}`);
|
|
1854
|
+
} else {
|
|
1855
|
+
const databases = await listD1Databases();
|
|
1856
|
+
if (databases.length === 0) {
|
|
1857
|
+
throw new CliError(
|
|
1858
|
+
"No D1 databases found",
|
|
1859
|
+
"ENOENT",
|
|
1860
|
+
"Create a database first with: wrangler d1 create <name>"
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
const selected = await select({
|
|
1864
|
+
message: "Select an existing database:",
|
|
1865
|
+
choices: databases.map((db) => ({
|
|
1866
|
+
name: `${db.name} (${db.uuid.slice(0, 8)}...)`,
|
|
1867
|
+
value: db.uuid
|
|
1868
|
+
}))
|
|
1869
|
+
});
|
|
1870
|
+
databaseId = selected;
|
|
1871
|
+
}
|
|
1872
|
+
addD1Binding(cwd, bindingName, databaseName, databaseId, env);
|
|
1873
|
+
console.log(pc5.green("\u2713") + " Updated wrangler.toml");
|
|
1874
|
+
showTypeHint(bindingName, "d1");
|
|
1875
|
+
}
|
|
1876
|
+
async function listD1Databases() {
|
|
1877
|
+
const output = await runWranglerCommand(["d1", "list", "--json"]);
|
|
1878
|
+
return JSON.parse(output);
|
|
1879
|
+
}
|
|
1880
|
+
async function addKV(cwd, options) {
|
|
1881
|
+
const config = readWranglerToml(cwd);
|
|
1882
|
+
const env = options.env;
|
|
1883
|
+
const bindingName = await input({
|
|
1884
|
+
message: "Binding name (e.g., CACHE):",
|
|
1885
|
+
validate: (value) => {
|
|
1886
|
+
if (!value.trim()) return "Binding name is required";
|
|
1887
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
|
|
1888
|
+
return "Binding name must be alphanumeric with underscores";
|
|
1889
|
+
if (bindingExists(config, value, env))
|
|
1890
|
+
return `Binding "${value}" already exists`;
|
|
1891
|
+
return true;
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
const createNew = await confirm({
|
|
1895
|
+
message: "Create a new KV namespace?",
|
|
1896
|
+
default: true
|
|
1897
|
+
});
|
|
1898
|
+
let namespaceId;
|
|
1899
|
+
if (createNew) {
|
|
1900
|
+
console.log();
|
|
1901
|
+
console.log(pc5.dim(`Creating KV namespace "${bindingName}"...`));
|
|
1902
|
+
const result = await runWranglerCommand([
|
|
1903
|
+
"kv",
|
|
1904
|
+
"namespace",
|
|
1905
|
+
"create",
|
|
1906
|
+
bindingName
|
|
1907
|
+
]);
|
|
1908
|
+
const idMatch = result.match(/id\s*=\s*"([^"]+)"/);
|
|
1909
|
+
if (!idMatch) {
|
|
1910
|
+
throw new CliError(
|
|
1911
|
+
"Failed to parse namespace ID from wrangler output",
|
|
1912
|
+
"EPARSE",
|
|
1913
|
+
"Try creating the namespace manually with: wrangler kv namespace create " + bindingName
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
namespaceId = idMatch[1];
|
|
1917
|
+
console.log(pc5.green("\u2713") + ` Created namespace: ${namespaceId}`);
|
|
1918
|
+
} else {
|
|
1919
|
+
const namespaces = await listKVNamespaces();
|
|
1920
|
+
if (namespaces.length === 0) {
|
|
1921
|
+
throw new CliError(
|
|
1922
|
+
"No KV namespaces found",
|
|
1923
|
+
"ENOENT",
|
|
1924
|
+
"Create a namespace first with: wrangler kv namespace create <name>"
|
|
1925
|
+
);
|
|
1926
|
+
}
|
|
1927
|
+
const selected = await select({
|
|
1928
|
+
message: "Select an existing namespace:",
|
|
1929
|
+
choices: namespaces.map((ns) => ({
|
|
1930
|
+
name: `${ns.title} (${ns.id.slice(0, 8)}...)`,
|
|
1931
|
+
value: ns.id
|
|
1932
|
+
}))
|
|
1933
|
+
});
|
|
1934
|
+
namespaceId = selected;
|
|
1935
|
+
}
|
|
1936
|
+
addKVBinding(cwd, bindingName, namespaceId, void 0, env);
|
|
1937
|
+
console.log(pc5.green("\u2713") + " Updated wrangler.toml");
|
|
1938
|
+
showTypeHint(bindingName, "kv");
|
|
1939
|
+
}
|
|
1940
|
+
async function listKVNamespaces() {
|
|
1941
|
+
const output = await runWranglerCommand(["kv", "namespace", "list", "--json"]);
|
|
1942
|
+
return JSON.parse(output);
|
|
1943
|
+
}
|
|
1944
|
+
async function addR2(cwd, options) {
|
|
1945
|
+
const config = readWranglerToml(cwd);
|
|
1946
|
+
const env = options.env;
|
|
1947
|
+
const bindingName = await input({
|
|
1948
|
+
message: "Binding name (e.g., STORAGE):",
|
|
1949
|
+
validate: (value) => {
|
|
1950
|
+
if (!value.trim()) return "Binding name is required";
|
|
1951
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
|
|
1952
|
+
return "Binding name must be alphanumeric with underscores";
|
|
1953
|
+
if (bindingExists(config, value, env))
|
|
1954
|
+
return `Binding "${value}" already exists`;
|
|
1955
|
+
return true;
|
|
1956
|
+
}
|
|
1957
|
+
});
|
|
1958
|
+
const createNew = await confirm({
|
|
1959
|
+
message: "Create a new R2 bucket?",
|
|
1960
|
+
default: true
|
|
1961
|
+
});
|
|
1962
|
+
let bucketName;
|
|
1963
|
+
if (createNew) {
|
|
1964
|
+
bucketName = await input({
|
|
1965
|
+
message: "Bucket name:",
|
|
1966
|
+
default: `${config.name || "my-app"}-bucket${env ? `-${env}` : ""}`,
|
|
1967
|
+
validate: (value) => {
|
|
1968
|
+
if (!value.trim()) return "Bucket name is required";
|
|
1969
|
+
return true;
|
|
1970
|
+
}
|
|
1971
|
+
});
|
|
1972
|
+
console.log();
|
|
1973
|
+
console.log(pc5.dim(`Creating R2 bucket "${bucketName}"...`));
|
|
1974
|
+
await runWranglerCommand(["r2", "bucket", "create", bucketName]);
|
|
1975
|
+
console.log(pc5.green("\u2713") + ` Created bucket: ${bucketName}`);
|
|
1976
|
+
} else {
|
|
1977
|
+
const buckets = await listR2Buckets();
|
|
1978
|
+
if (buckets.length === 0) {
|
|
1979
|
+
throw new CliError(
|
|
1980
|
+
"No R2 buckets found",
|
|
1981
|
+
"ENOENT",
|
|
1982
|
+
"Create a bucket first with: wrangler r2 bucket create <name>"
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
const selected = await select({
|
|
1986
|
+
message: "Select an existing bucket:",
|
|
1987
|
+
choices: buckets.map((bucket) => ({
|
|
1988
|
+
name: bucket.name,
|
|
1989
|
+
value: bucket.name
|
|
1990
|
+
}))
|
|
1991
|
+
});
|
|
1992
|
+
bucketName = selected;
|
|
1993
|
+
}
|
|
1994
|
+
addR2Binding(cwd, bindingName, bucketName, env);
|
|
1995
|
+
console.log(pc5.green("\u2713") + " Updated wrangler.toml");
|
|
1996
|
+
showTypeHint(bindingName, "r2");
|
|
1997
|
+
}
|
|
1998
|
+
async function listR2Buckets() {
|
|
1999
|
+
const output = await runWranglerCommand(["r2", "bucket", "list", "--json"]);
|
|
2000
|
+
return JSON.parse(output);
|
|
2001
|
+
}
|
|
2002
|
+
async function addQueue(cwd, options) {
|
|
2003
|
+
const config = readWranglerToml(cwd);
|
|
2004
|
+
const env = options.env;
|
|
2005
|
+
const bindingName = await input({
|
|
2006
|
+
message: "Binding name (e.g., JOBS):",
|
|
2007
|
+
validate: (value) => {
|
|
2008
|
+
if (!value.trim()) return "Binding name is required";
|
|
2009
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
|
|
2010
|
+
return "Binding name must be alphanumeric with underscores";
|
|
2011
|
+
if (bindingExists(config, value, env))
|
|
2012
|
+
return `Binding "${value}" already exists`;
|
|
2013
|
+
return true;
|
|
2014
|
+
}
|
|
2015
|
+
});
|
|
2016
|
+
const createNew = await confirm({
|
|
2017
|
+
message: "Create a new queue?",
|
|
2018
|
+
default: true
|
|
2019
|
+
});
|
|
2020
|
+
let queueName;
|
|
2021
|
+
if (createNew) {
|
|
2022
|
+
queueName = await input({
|
|
2023
|
+
message: "Queue name:",
|
|
2024
|
+
default: `${config.name || "my-app"}-queue${env ? `-${env}` : ""}`,
|
|
2025
|
+
validate: (value) => {
|
|
2026
|
+
if (!value.trim()) return "Queue name is required";
|
|
2027
|
+
return true;
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
2030
|
+
console.log();
|
|
2031
|
+
console.log(pc5.dim(`Creating queue "${queueName}"...`));
|
|
2032
|
+
await runWranglerCommand(["queues", "create", queueName]);
|
|
2033
|
+
console.log(pc5.green("\u2713") + ` Created queue: ${queueName}`);
|
|
2034
|
+
} else {
|
|
2035
|
+
const queues = await listQueues();
|
|
2036
|
+
if (queues.length === 0) {
|
|
2037
|
+
throw new CliError(
|
|
2038
|
+
"No queues found",
|
|
2039
|
+
"ENOENT",
|
|
2040
|
+
"Create a queue first with: wrangler queues create <name>"
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
const selected = await select({
|
|
2044
|
+
message: "Select an existing queue:",
|
|
2045
|
+
choices: queues.map((queue) => ({
|
|
2046
|
+
name: queue.queue_name,
|
|
2047
|
+
value: queue.queue_name
|
|
2048
|
+
}))
|
|
2049
|
+
});
|
|
2050
|
+
queueName = selected;
|
|
2051
|
+
}
|
|
2052
|
+
addQueueBinding(cwd, bindingName, queueName, env);
|
|
2053
|
+
console.log(pc5.green("\u2713") + " Updated wrangler.toml");
|
|
2054
|
+
showTypeHint(bindingName, "queue");
|
|
2055
|
+
}
|
|
2056
|
+
async function listQueues() {
|
|
2057
|
+
const output = await runWranglerCommand(["queues", "list", "--json"]);
|
|
2058
|
+
return JSON.parse(output);
|
|
2059
|
+
}
|
|
2060
|
+
async function addDurableObject(cwd, options) {
|
|
2061
|
+
const config = readWranglerToml(cwd);
|
|
2062
|
+
const env = options.env;
|
|
2063
|
+
const bindingName = await input({
|
|
2064
|
+
message: "Binding name (e.g., COUNTER):",
|
|
2065
|
+
validate: (value) => {
|
|
2066
|
+
if (!value.trim()) return "Binding name is required";
|
|
2067
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
|
|
2068
|
+
return "Binding name must be alphanumeric with underscores";
|
|
2069
|
+
if (bindingExists(config, value, env))
|
|
2070
|
+
return `Binding "${value}" already exists`;
|
|
2071
|
+
return true;
|
|
2072
|
+
}
|
|
2073
|
+
});
|
|
2074
|
+
const className = await input({
|
|
2075
|
+
message: "Durable Object class name:",
|
|
2076
|
+
default: bindingName.split("_").map((s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase()).join(""),
|
|
2077
|
+
validate: (value) => {
|
|
2078
|
+
if (!value.trim()) return "Class name is required";
|
|
2079
|
+
return true;
|
|
2080
|
+
}
|
|
2081
|
+
});
|
|
2082
|
+
const isExternal = await confirm({
|
|
2083
|
+
message: "Is this a Durable Object from another Worker?",
|
|
2084
|
+
default: false
|
|
2085
|
+
});
|
|
2086
|
+
let scriptName;
|
|
2087
|
+
if (isExternal) {
|
|
2088
|
+
scriptName = await input({
|
|
2089
|
+
message: "Worker script name:",
|
|
2090
|
+
validate: (value) => {
|
|
2091
|
+
if (!value.trim()) return "Script name is required";
|
|
2092
|
+
return true;
|
|
2093
|
+
}
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
addDurableObjectBinding(cwd, bindingName, className, scriptName, env);
|
|
2097
|
+
console.log(pc5.green("\u2713") + " Updated wrangler.toml");
|
|
2098
|
+
showTypeHint(bindingName, "do");
|
|
2099
|
+
if (!isExternal) {
|
|
2100
|
+
console.log();
|
|
2101
|
+
console.log(pc5.dim("Remember to export your Durable Object class:"));
|
|
2102
|
+
console.log(pc5.dim(` export class ${className} extends DurableObject { ... }`));
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
async function addSecret(cwd, options) {
|
|
2106
|
+
const config = readWranglerToml(cwd);
|
|
2107
|
+
const env = options.env;
|
|
2108
|
+
const varName = await input({
|
|
2109
|
+
message: "Variable name (e.g., API_KEY):",
|
|
2110
|
+
validate: (value) => {
|
|
2111
|
+
if (!value.trim()) return "Variable name is required";
|
|
2112
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
|
|
2113
|
+
return "Variable name must be alphanumeric with underscores";
|
|
2114
|
+
if (bindingExists(config, value, env))
|
|
2115
|
+
return `Variable "${value}" already exists`;
|
|
2116
|
+
return true;
|
|
2117
|
+
}
|
|
2118
|
+
});
|
|
2119
|
+
const isSecret = await confirm({
|
|
2120
|
+
message: "Is this a secret (sensitive value)?",
|
|
2121
|
+
default: true
|
|
2122
|
+
});
|
|
2123
|
+
if (isSecret) {
|
|
2124
|
+
console.log();
|
|
2125
|
+
console.log(pc5.dim("Enter the secret value (will be hidden):"));
|
|
2126
|
+
const varValue = await input({
|
|
2127
|
+
message: "Secret value:"
|
|
2128
|
+
});
|
|
2129
|
+
console.log();
|
|
2130
|
+
console.log(pc5.dim(`Setting secret "${varName}"...`));
|
|
2131
|
+
const args = ["secret", "put", varName];
|
|
2132
|
+
if (env) args.push("--env", env);
|
|
2133
|
+
await runWranglerCommandWithInput(args, varValue);
|
|
2134
|
+
console.log(pc5.green("\u2713") + ` Secret "${varName}" set`);
|
|
2135
|
+
console.log();
|
|
2136
|
+
console.log(
|
|
2137
|
+
pc5.dim("Note: Secrets are stored securely and not written to wrangler.toml.")
|
|
2138
|
+
);
|
|
2139
|
+
} else {
|
|
2140
|
+
const varValue = await input({
|
|
2141
|
+
message: "Variable value:",
|
|
2142
|
+
validate: (value) => {
|
|
2143
|
+
if (!value.trim()) return "Value is required";
|
|
2144
|
+
return true;
|
|
2145
|
+
}
|
|
2146
|
+
});
|
|
2147
|
+
addSecretBinding(cwd, varName, varValue, env);
|
|
2148
|
+
console.log(pc5.green("\u2713") + " Updated wrangler.toml");
|
|
2149
|
+
}
|
|
2150
|
+
showTypeHint(varName, "secret");
|
|
2151
|
+
}
|
|
2152
|
+
async function regenerateTypes(cwd) {
|
|
2153
|
+
const config = readWranglerToml(cwd);
|
|
2154
|
+
const bindings2 = extractBindings(config);
|
|
2155
|
+
if (bindings2.length === 0) {
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
const result = generateEnvTypes(cwd, bindings2);
|
|
2159
|
+
console.log(pc5.green("\u2713") + ` Updated env.d.ts with ${result.bindingCount} binding(s)`);
|
|
2160
|
+
}
|
|
2161
|
+
function showTypeHint(bindingName, type) {
|
|
2162
|
+
const tsType = getTypeForBinding(type);
|
|
2163
|
+
console.log();
|
|
2164
|
+
console.log(pc5.dim(`TypeScript type: ${bindingName}: ${tsType}`));
|
|
2165
|
+
}
|
|
2166
|
+
function runWranglerCommand(args) {
|
|
2167
|
+
return new Promise((resolve4, reject) => {
|
|
2168
|
+
let stdout = "";
|
|
2169
|
+
let stderr = "";
|
|
2170
|
+
const child = spawn2("npx", ["wrangler", ...args], {
|
|
2171
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2172
|
+
});
|
|
2173
|
+
child.stdout?.on("data", (data) => {
|
|
2174
|
+
stdout += data.toString();
|
|
2175
|
+
});
|
|
2176
|
+
child.stderr?.on("data", (data) => {
|
|
2177
|
+
stderr += data.toString();
|
|
2178
|
+
});
|
|
2179
|
+
child.on("error", (error) => {
|
|
2180
|
+
reject(error);
|
|
2181
|
+
});
|
|
2182
|
+
child.on("close", (code) => {
|
|
2183
|
+
if (code !== 0) {
|
|
2184
|
+
reject(new Error(stderr || `wrangler exited with code ${code}`));
|
|
2185
|
+
} else {
|
|
2186
|
+
resolve4(stdout);
|
|
2187
|
+
}
|
|
2188
|
+
});
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
function runWranglerCommandWithInput(args, inputData) {
|
|
2192
|
+
return new Promise((resolve4, reject) => {
|
|
2193
|
+
let stdout = "";
|
|
2194
|
+
let stderr = "";
|
|
2195
|
+
const child = spawn2("npx", ["wrangler", ...args], {
|
|
2196
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2197
|
+
});
|
|
2198
|
+
child.stdout?.on("data", (data) => {
|
|
2199
|
+
stdout += data.toString();
|
|
2200
|
+
});
|
|
2201
|
+
child.stderr?.on("data", (data) => {
|
|
2202
|
+
stderr += data.toString();
|
|
2203
|
+
});
|
|
2204
|
+
child.on("error", (error) => {
|
|
2205
|
+
reject(error);
|
|
2206
|
+
});
|
|
2207
|
+
child.on("close", (code) => {
|
|
2208
|
+
if (code !== 0) {
|
|
2209
|
+
reject(new Error(stderr || `wrangler exited with code ${code}`));
|
|
2210
|
+
} else {
|
|
2211
|
+
resolve4(stdout);
|
|
2212
|
+
}
|
|
2213
|
+
});
|
|
2214
|
+
child.stdin?.write(inputData);
|
|
2215
|
+
child.stdin?.end();
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
// src/commands/bindings/remove.ts
|
|
2220
|
+
import pc6 from "picocolors";
|
|
2221
|
+
import { confirm as confirm2, select as select2 } from "@inquirer/prompts";
|
|
2222
|
+
async function bindingsRemove(bindingName, options) {
|
|
2223
|
+
const verbose = options.verbose ?? false;
|
|
2224
|
+
const logger = createLogger(verbose);
|
|
2225
|
+
const env = options.env;
|
|
2226
|
+
try {
|
|
2227
|
+
const cwd = process.cwd();
|
|
2228
|
+
const wranglerPath = findWranglerToml(cwd);
|
|
2229
|
+
if (!wranglerPath) {
|
|
2230
|
+
throw new CliError(
|
|
2231
|
+
"wrangler.toml not found",
|
|
2232
|
+
"ENOENT",
|
|
2233
|
+
"Create a wrangler.toml file or run this command from a Cloudwerk project directory."
|
|
2234
|
+
);
|
|
2235
|
+
}
|
|
2236
|
+
logger.debug(`Found wrangler config: ${wranglerPath}`);
|
|
2237
|
+
const config = readWranglerToml(cwd);
|
|
2238
|
+
const bindings2 = extractBindings(config, env);
|
|
2239
|
+
if (bindings2.length === 0) {
|
|
2240
|
+
const envLabel = env ? ` in ${env}` : "";
|
|
2241
|
+
throw new CliError(
|
|
2242
|
+
`No bindings found${envLabel}`,
|
|
2243
|
+
"ENOENT",
|
|
2244
|
+
`Use 'cloudwerk bindings add' to add a binding first.`
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
let targetBinding = bindingName;
|
|
2248
|
+
if (!targetBinding) {
|
|
2249
|
+
targetBinding = await select2({
|
|
2250
|
+
message: "Select a binding to remove:",
|
|
2251
|
+
choices: bindings2.map((b) => ({
|
|
2252
|
+
name: `${b.name} (${getBindingTypeName(b.type)})`,
|
|
2253
|
+
value: b.name
|
|
2254
|
+
}))
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
const binding = bindings2.find((b) => b.name === targetBinding);
|
|
2258
|
+
if (!binding) {
|
|
2259
|
+
const envLabel = env ? ` in ${env}` : "";
|
|
2260
|
+
throw new CliError(
|
|
2261
|
+
`Binding "${targetBinding}" not found${envLabel}`,
|
|
2262
|
+
"ENOENT",
|
|
2263
|
+
`Use 'cloudwerk bindings' to see available bindings.`
|
|
2264
|
+
);
|
|
2265
|
+
}
|
|
2266
|
+
if (!options.force) {
|
|
2267
|
+
const confirmed = await confirm2({
|
|
2268
|
+
message: `Remove binding "${targetBinding}" (${getBindingTypeName(binding.type)})?`,
|
|
2269
|
+
default: false
|
|
2270
|
+
});
|
|
2271
|
+
if (!confirmed) {
|
|
2272
|
+
console.log(pc6.dim("Cancelled."));
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
console.log();
|
|
2277
|
+
const removed = removeBinding(cwd, targetBinding, env);
|
|
2278
|
+
if (!removed) {
|
|
2279
|
+
throw new CliError(
|
|
2280
|
+
`Failed to remove binding "${targetBinding}"`,
|
|
2281
|
+
"EREMOVE",
|
|
2282
|
+
"The binding may have already been removed."
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
console.log(pc6.green("\u2713") + ` Removed binding "${targetBinding}" from wrangler.toml`);
|
|
2286
|
+
if (!options.skipTypes) {
|
|
2287
|
+
const updatedConfig = readWranglerToml(cwd);
|
|
2288
|
+
const updatedBindings = extractBindings(updatedConfig);
|
|
2289
|
+
if (updatedBindings.length > 0) {
|
|
2290
|
+
const result = generateEnvTypes(cwd, updatedBindings);
|
|
2291
|
+
console.log(
|
|
2292
|
+
pc6.green("\u2713") + ` Updated env.d.ts with ${result.bindingCount} binding(s)`
|
|
2293
|
+
);
|
|
2294
|
+
} else {
|
|
2295
|
+
console.log(
|
|
2296
|
+
pc6.dim("Note: No bindings remain. Consider removing env.d.ts manually.")
|
|
2297
|
+
);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
console.log();
|
|
2301
|
+
logger.success("Binding removed successfully!");
|
|
2302
|
+
console.log();
|
|
2303
|
+
console.log(
|
|
2304
|
+
pc6.dim(
|
|
2305
|
+
"Note: The Cloudflare resource itself was not deleted. Use wrangler to delete the resource if needed."
|
|
2306
|
+
)
|
|
2307
|
+
);
|
|
2308
|
+
console.log();
|
|
2309
|
+
} catch (error) {
|
|
2310
|
+
handleCommandError(error, verbose);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
// src/commands/bindings/update.ts
|
|
2315
|
+
import { spawn as spawn3 } from "child_process";
|
|
2316
|
+
import pc7 from "picocolors";
|
|
2317
|
+
import { input as input2, select as select3 } from "@inquirer/prompts";
|
|
2318
|
+
async function bindingsUpdate(bindingName, options) {
|
|
2319
|
+
const verbose = options.verbose ?? false;
|
|
2320
|
+
const logger = createLogger(verbose);
|
|
2321
|
+
const env = options.env;
|
|
2322
|
+
try {
|
|
2323
|
+
const cwd = process.cwd();
|
|
2324
|
+
const wranglerPath = findWranglerToml(cwd);
|
|
2325
|
+
if (!wranglerPath) {
|
|
2326
|
+
throw new CliError(
|
|
2327
|
+
"wrangler.toml not found",
|
|
2328
|
+
"ENOENT",
|
|
2329
|
+
"Create a wrangler.toml file or run this command from a Cloudwerk project directory."
|
|
2330
|
+
);
|
|
2331
|
+
}
|
|
2332
|
+
logger.debug(`Found wrangler config: ${wranglerPath}`);
|
|
2333
|
+
const config = readWranglerToml(cwd);
|
|
2334
|
+
const bindings2 = extractBindings(config, env);
|
|
2335
|
+
if (bindings2.length === 0) {
|
|
2336
|
+
const envLabel = env ? ` in ${env}` : "";
|
|
2337
|
+
throw new CliError(
|
|
2338
|
+
`No bindings found${envLabel}`,
|
|
2339
|
+
"ENOENT",
|
|
2340
|
+
`Use 'cloudwerk bindings add' to add a binding first.`
|
|
2341
|
+
);
|
|
2342
|
+
}
|
|
2343
|
+
let targetBinding = bindingName;
|
|
2344
|
+
if (!targetBinding) {
|
|
2345
|
+
targetBinding = await select3({
|
|
2346
|
+
message: "Select a binding to update:",
|
|
2347
|
+
choices: bindings2.map((b) => ({
|
|
2348
|
+
name: `${b.name} (${getBindingTypeName(b.type)})`,
|
|
2349
|
+
value: b.name
|
|
2350
|
+
}))
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
const binding = bindings2.find((b) => b.name === targetBinding);
|
|
2354
|
+
if (!binding) {
|
|
2355
|
+
const envLabel = env ? ` in ${env}` : "";
|
|
2356
|
+
throw new CliError(
|
|
2357
|
+
`Binding "${targetBinding}" not found${envLabel}`,
|
|
2358
|
+
"ENOENT",
|
|
2359
|
+
`Use 'cloudwerk bindings' to see available bindings.`
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
console.log();
|
|
2363
|
+
logger.info(
|
|
2364
|
+
`Updating ${getBindingTypeName(binding.type)} binding "${targetBinding}"...`
|
|
2365
|
+
);
|
|
2366
|
+
const updated = await updateBinding(cwd, config, binding, env);
|
|
2367
|
+
if (!updated) {
|
|
2368
|
+
console.log(pc7.dim("No changes made."));
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
if (!options.skipTypes) {
|
|
2372
|
+
const updatedConfig = readWranglerToml(cwd);
|
|
2373
|
+
const updatedBindings = extractBindings(updatedConfig);
|
|
2374
|
+
if (updatedBindings.length > 0) {
|
|
2375
|
+
const result = generateEnvTypes(cwd, updatedBindings);
|
|
2376
|
+
console.log(
|
|
2377
|
+
pc7.green("\u2713") + ` Updated env.d.ts with ${result.bindingCount} binding(s)`
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
console.log();
|
|
2382
|
+
logger.success("Binding updated successfully!");
|
|
2383
|
+
console.log();
|
|
2384
|
+
} catch (error) {
|
|
2385
|
+
handleCommandError(error, verbose);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
async function updateBinding(cwd, config, binding, env) {
|
|
2389
|
+
const updateOptions = getUpdateOptions(binding);
|
|
2390
|
+
if (updateOptions.length === 0) {
|
|
2391
|
+
console.log(pc7.dim("This binding type has no updatable fields."));
|
|
2392
|
+
return false;
|
|
2393
|
+
}
|
|
2394
|
+
const field = await select3({
|
|
2395
|
+
message: "What do you want to update?",
|
|
2396
|
+
choices: updateOptions
|
|
2397
|
+
});
|
|
2398
|
+
const targetConfig = env ? config.env?.[env] ?? config : config;
|
|
2399
|
+
switch (binding.type) {
|
|
2400
|
+
case "d1":
|
|
2401
|
+
return updateD1(cwd, config, targetConfig, binding.name, field);
|
|
2402
|
+
case "kv":
|
|
2403
|
+
return updateKV(cwd, config, targetConfig, binding.name, field);
|
|
2404
|
+
case "r2":
|
|
2405
|
+
return updateR2(cwd, config, targetConfig, binding.name, field);
|
|
2406
|
+
case "queue":
|
|
2407
|
+
return updateQueue(cwd, config, targetConfig, binding.name, field);
|
|
2408
|
+
case "do":
|
|
2409
|
+
return updateDurableObject(cwd, config, targetConfig, binding.name, field);
|
|
2410
|
+
case "secret":
|
|
2411
|
+
return updateSecret(cwd, config, targetConfig, binding.name, field, env);
|
|
2412
|
+
default:
|
|
2413
|
+
console.log(pc7.dim(`Updating ${binding.type} bindings is not yet supported.`));
|
|
2414
|
+
return false;
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
function getUpdateOptions(binding) {
|
|
2418
|
+
switch (binding.type) {
|
|
2419
|
+
case "d1":
|
|
2420
|
+
return [
|
|
2421
|
+
{ name: "Binding name", value: "name" },
|
|
2422
|
+
{ name: "Database name", value: "database_name" },
|
|
2423
|
+
{ name: "Database ID", value: "database_id" }
|
|
2424
|
+
];
|
|
2425
|
+
case "kv":
|
|
2426
|
+
return [
|
|
2427
|
+
{ name: "Binding name", value: "name" },
|
|
2428
|
+
{ name: "Namespace ID", value: "id" },
|
|
2429
|
+
{ name: "Preview ID", value: "preview_id" }
|
|
2430
|
+
];
|
|
2431
|
+
case "r2":
|
|
2432
|
+
return [
|
|
2433
|
+
{ name: "Binding name", value: "name" },
|
|
2434
|
+
{ name: "Bucket name", value: "bucket_name" }
|
|
2435
|
+
];
|
|
2436
|
+
case "queue":
|
|
2437
|
+
return [
|
|
2438
|
+
{ name: "Binding name", value: "name" },
|
|
2439
|
+
{ name: "Queue name", value: "queue" }
|
|
2440
|
+
];
|
|
2441
|
+
case "do":
|
|
2442
|
+
return [
|
|
2443
|
+
{ name: "Binding name", value: "name" },
|
|
2444
|
+
{ name: "Class name", value: "class_name" },
|
|
2445
|
+
{ name: "Script name", value: "script_name" }
|
|
2446
|
+
];
|
|
2447
|
+
case "secret":
|
|
2448
|
+
return [
|
|
2449
|
+
{ name: "Variable name", value: "name" },
|
|
2450
|
+
{ name: "Value", value: "value" }
|
|
2451
|
+
];
|
|
2452
|
+
default:
|
|
2453
|
+
return [];
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
async function updateD1(cwd, config, targetConfig, bindingName, field) {
|
|
2457
|
+
const bindings2 = targetConfig.d1_databases;
|
|
2458
|
+
if (!bindings2) return false;
|
|
2459
|
+
const binding = bindings2.find((b) => b.binding === bindingName);
|
|
2460
|
+
if (!binding) return false;
|
|
2461
|
+
switch (field) {
|
|
2462
|
+
case "name": {
|
|
2463
|
+
const newName = await input2({
|
|
2464
|
+
message: "New binding name:",
|
|
2465
|
+
default: binding.binding
|
|
2466
|
+
});
|
|
2467
|
+
if (newName === binding.binding) return false;
|
|
2468
|
+
binding.binding = newName;
|
|
2469
|
+
break;
|
|
2470
|
+
}
|
|
2471
|
+
case "database_name": {
|
|
2472
|
+
const newName = await input2({
|
|
2473
|
+
message: "New database name:",
|
|
2474
|
+
default: binding.database_name
|
|
2475
|
+
});
|
|
2476
|
+
if (newName === binding.database_name) return false;
|
|
2477
|
+
binding.database_name = newName;
|
|
2478
|
+
break;
|
|
2479
|
+
}
|
|
2480
|
+
case "database_id": {
|
|
2481
|
+
const newId = await input2({
|
|
2482
|
+
message: "New database ID:",
|
|
2483
|
+
default: binding.database_id
|
|
2484
|
+
});
|
|
2485
|
+
if (newId === binding.database_id) return false;
|
|
2486
|
+
binding.database_id = newId;
|
|
2487
|
+
break;
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
writeWranglerToml(cwd, config);
|
|
2491
|
+
console.log(pc7.green("\u2713") + " Updated wrangler.toml");
|
|
2492
|
+
return true;
|
|
2493
|
+
}
|
|
2494
|
+
async function updateKV(cwd, config, targetConfig, bindingName, field) {
|
|
2495
|
+
const bindings2 = targetConfig.kv_namespaces;
|
|
2496
|
+
if (!bindings2) return false;
|
|
2497
|
+
const binding = bindings2.find((b) => b.binding === bindingName);
|
|
2498
|
+
if (!binding) return false;
|
|
2499
|
+
switch (field) {
|
|
2500
|
+
case "name": {
|
|
2501
|
+
const newName = await input2({
|
|
2502
|
+
message: "New binding name:",
|
|
2503
|
+
default: binding.binding
|
|
2504
|
+
});
|
|
2505
|
+
if (newName === binding.binding) return false;
|
|
2506
|
+
binding.binding = newName;
|
|
2507
|
+
break;
|
|
2508
|
+
}
|
|
2509
|
+
case "id": {
|
|
2510
|
+
const newId = await input2({
|
|
2511
|
+
message: "New namespace ID:",
|
|
2512
|
+
default: binding.id
|
|
2513
|
+
});
|
|
2514
|
+
if (newId === binding.id) return false;
|
|
2515
|
+
binding.id = newId;
|
|
2516
|
+
break;
|
|
2517
|
+
}
|
|
2518
|
+
case "preview_id": {
|
|
2519
|
+
const newId = await input2({
|
|
2520
|
+
message: "New preview ID (leave empty to remove):",
|
|
2521
|
+
default: binding.preview_id || ""
|
|
2522
|
+
});
|
|
2523
|
+
if (newId === (binding.preview_id || "")) return false;
|
|
2524
|
+
if (newId) {
|
|
2525
|
+
binding.preview_id = newId;
|
|
2526
|
+
} else {
|
|
2527
|
+
delete binding.preview_id;
|
|
2528
|
+
}
|
|
2529
|
+
break;
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
writeWranglerToml(cwd, config);
|
|
2533
|
+
console.log(pc7.green("\u2713") + " Updated wrangler.toml");
|
|
2534
|
+
return true;
|
|
2535
|
+
}
|
|
2536
|
+
async function updateR2(cwd, config, targetConfig, bindingName, field) {
|
|
2537
|
+
const bindings2 = targetConfig.r2_buckets;
|
|
2538
|
+
if (!bindings2) return false;
|
|
2539
|
+
const binding = bindings2.find((b) => b.binding === bindingName);
|
|
2540
|
+
if (!binding) return false;
|
|
2541
|
+
switch (field) {
|
|
2542
|
+
case "name": {
|
|
2543
|
+
const newName = await input2({
|
|
2544
|
+
message: "New binding name:",
|
|
2545
|
+
default: binding.binding
|
|
2546
|
+
});
|
|
2547
|
+
if (newName === binding.binding) return false;
|
|
2548
|
+
binding.binding = newName;
|
|
2549
|
+
break;
|
|
2550
|
+
}
|
|
2551
|
+
case "bucket_name": {
|
|
2552
|
+
const newName = await input2({
|
|
2553
|
+
message: "New bucket name:",
|
|
2554
|
+
default: binding.bucket_name
|
|
2555
|
+
});
|
|
2556
|
+
if (newName === binding.bucket_name) return false;
|
|
2557
|
+
binding.bucket_name = newName;
|
|
2558
|
+
break;
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
writeWranglerToml(cwd, config);
|
|
2562
|
+
console.log(pc7.green("\u2713") + " Updated wrangler.toml");
|
|
2563
|
+
return true;
|
|
2564
|
+
}
|
|
2565
|
+
async function updateQueue(cwd, config, targetConfig, bindingName, field) {
|
|
2566
|
+
const bindings2 = targetConfig.queues?.producers;
|
|
2567
|
+
if (!bindings2) return false;
|
|
2568
|
+
const binding = bindings2.find((b) => b.binding === bindingName);
|
|
2569
|
+
if (!binding) return false;
|
|
2570
|
+
switch (field) {
|
|
2571
|
+
case "name": {
|
|
2572
|
+
const newName = await input2({
|
|
2573
|
+
message: "New binding name:",
|
|
2574
|
+
default: binding.binding
|
|
2575
|
+
});
|
|
2576
|
+
if (newName === binding.binding) return false;
|
|
2577
|
+
binding.binding = newName;
|
|
2578
|
+
break;
|
|
2579
|
+
}
|
|
2580
|
+
case "queue": {
|
|
2581
|
+
const newName = await input2({
|
|
2582
|
+
message: "New queue name:",
|
|
2583
|
+
default: binding.queue
|
|
2584
|
+
});
|
|
2585
|
+
if (newName === binding.queue) return false;
|
|
2586
|
+
binding.queue = newName;
|
|
2587
|
+
break;
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
writeWranglerToml(cwd, config);
|
|
2591
|
+
console.log(pc7.green("\u2713") + " Updated wrangler.toml");
|
|
2592
|
+
return true;
|
|
2593
|
+
}
|
|
2594
|
+
async function updateDurableObject(cwd, config, targetConfig, bindingName, field) {
|
|
2595
|
+
const bindings2 = targetConfig.durable_objects?.bindings;
|
|
2596
|
+
if (!bindings2) return false;
|
|
2597
|
+
const binding = bindings2.find((b) => b.name === bindingName);
|
|
2598
|
+
if (!binding) return false;
|
|
2599
|
+
switch (field) {
|
|
2600
|
+
case "name": {
|
|
2601
|
+
const newName = await input2({
|
|
2602
|
+
message: "New binding name:",
|
|
2603
|
+
default: binding.name
|
|
2604
|
+
});
|
|
2605
|
+
if (newName === binding.name) return false;
|
|
2606
|
+
binding.name = newName;
|
|
2607
|
+
break;
|
|
2608
|
+
}
|
|
2609
|
+
case "class_name": {
|
|
2610
|
+
const newName = await input2({
|
|
2611
|
+
message: "New class name:",
|
|
2612
|
+
default: binding.class_name
|
|
2613
|
+
});
|
|
2614
|
+
if (newName === binding.class_name) return false;
|
|
2615
|
+
binding.class_name = newName;
|
|
2616
|
+
break;
|
|
2617
|
+
}
|
|
2618
|
+
case "script_name": {
|
|
2619
|
+
const newName = await input2({
|
|
2620
|
+
message: "New script name (leave empty to remove):",
|
|
2621
|
+
default: binding.script_name || ""
|
|
2622
|
+
});
|
|
2623
|
+
if (newName === (binding.script_name || "")) return false;
|
|
2624
|
+
if (newName) {
|
|
2625
|
+
binding.script_name = newName;
|
|
2626
|
+
} else {
|
|
2627
|
+
delete binding.script_name;
|
|
2628
|
+
}
|
|
2629
|
+
break;
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
writeWranglerToml(cwd, config);
|
|
2633
|
+
console.log(pc7.green("\u2713") + " Updated wrangler.toml");
|
|
2634
|
+
return true;
|
|
2635
|
+
}
|
|
2636
|
+
async function updateSecret(cwd, config, targetConfig, varName, field, env) {
|
|
2637
|
+
const vars = targetConfig.vars;
|
|
2638
|
+
const isInToml = vars && varName in vars;
|
|
2639
|
+
if (!isInToml) {
|
|
2640
|
+
if (field === "name") {
|
|
2641
|
+
console.log();
|
|
2642
|
+
console.log(
|
|
2643
|
+
pc7.yellow("\u26A0") + " Secrets managed by wrangler cannot be renamed directly."
|
|
2644
|
+
);
|
|
2645
|
+
console.log(
|
|
2646
|
+
pc7.dim(
|
|
2647
|
+
" To rename, delete the old secret and create a new one with:"
|
|
2648
|
+
)
|
|
2649
|
+
);
|
|
2650
|
+
console.log(pc7.dim(` wrangler secret delete ${varName}`));
|
|
2651
|
+
console.log(pc7.dim(` wrangler secret put <new-name>`));
|
|
2652
|
+
return false;
|
|
2653
|
+
}
|
|
2654
|
+
console.log();
|
|
2655
|
+
console.log(
|
|
2656
|
+
pc7.dim("This is a secret managed by wrangler. Enter the new value:")
|
|
2657
|
+
);
|
|
2658
|
+
const newValue = await input2({
|
|
2659
|
+
message: "New secret value:"
|
|
2660
|
+
});
|
|
2661
|
+
if (!newValue.trim()) {
|
|
2662
|
+
console.log(pc7.dim("No value provided."));
|
|
2663
|
+
return false;
|
|
2664
|
+
}
|
|
2665
|
+
const args = ["secret", "put", varName];
|
|
2666
|
+
if (env) args.push("--env", env);
|
|
2667
|
+
console.log();
|
|
2668
|
+
console.log(pc7.dim(`Updating secret "${varName}"...`));
|
|
2669
|
+
await runWranglerCommandWithInput2(args, newValue);
|
|
2670
|
+
console.log(pc7.green("\u2713") + ` Secret "${varName}" updated`);
|
|
2671
|
+
return true;
|
|
2672
|
+
}
|
|
2673
|
+
switch (field) {
|
|
2674
|
+
case "name": {
|
|
2675
|
+
const newName = await input2({
|
|
2676
|
+
message: "New variable name:",
|
|
2677
|
+
default: varName
|
|
2678
|
+
});
|
|
2679
|
+
if (newName === varName) return false;
|
|
2680
|
+
const value = vars[varName];
|
|
2681
|
+
delete vars[varName];
|
|
2682
|
+
vars[newName] = value;
|
|
2683
|
+
break;
|
|
2684
|
+
}
|
|
2685
|
+
case "value": {
|
|
2686
|
+
const newValue = await input2({
|
|
2687
|
+
message: "New value:",
|
|
2688
|
+
default: vars[varName]
|
|
2689
|
+
});
|
|
2690
|
+
if (newValue === vars[varName]) return false;
|
|
2691
|
+
vars[varName] = newValue;
|
|
2692
|
+
break;
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
writeWranglerToml(cwd, config);
|
|
2696
|
+
console.log(pc7.green("\u2713") + " Updated wrangler.toml");
|
|
2697
|
+
return true;
|
|
2698
|
+
}
|
|
2699
|
+
function runWranglerCommandWithInput2(args, inputData) {
|
|
2700
|
+
return new Promise((resolve4, reject) => {
|
|
2701
|
+
let stdout = "";
|
|
2702
|
+
let stderr = "";
|
|
2703
|
+
const child = spawn3("npx", ["wrangler", ...args], {
|
|
2704
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2705
|
+
});
|
|
2706
|
+
child.stdout?.on("data", (data) => {
|
|
2707
|
+
stdout += data.toString();
|
|
2708
|
+
});
|
|
2709
|
+
child.stderr?.on("data", (data) => {
|
|
2710
|
+
stderr += data.toString();
|
|
2711
|
+
});
|
|
2712
|
+
child.on("error", (error) => {
|
|
2713
|
+
reject(error);
|
|
2714
|
+
});
|
|
2715
|
+
child.on("close", (code) => {
|
|
2716
|
+
if (code !== 0) {
|
|
2717
|
+
reject(new Error(stderr || `wrangler exited with code ${code}`));
|
|
2718
|
+
} else {
|
|
2719
|
+
resolve4(stdout);
|
|
2720
|
+
}
|
|
2721
|
+
});
|
|
2722
|
+
child.stdin?.write(inputData);
|
|
2723
|
+
child.stdin?.end();
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
// src/commands/bindings/generate-types.ts
|
|
2728
|
+
import pc8 from "picocolors";
|
|
2729
|
+
async function bindingsGenerateTypes(options) {
|
|
2730
|
+
const verbose = options.verbose ?? false;
|
|
2731
|
+
const logger = createLogger(verbose);
|
|
2732
|
+
try {
|
|
2733
|
+
const cwd = process.cwd();
|
|
2734
|
+
const wranglerPath = findWranglerToml(cwd);
|
|
2735
|
+
if (!wranglerPath) {
|
|
2736
|
+
throw new CliError(
|
|
2737
|
+
"wrangler.toml not found",
|
|
2738
|
+
"ENOENT",
|
|
2739
|
+
"Create a wrangler.toml file or run this command from a Cloudwerk project directory."
|
|
2740
|
+
);
|
|
2741
|
+
}
|
|
2742
|
+
logger.debug(`Found wrangler config: ${wranglerPath}`);
|
|
2743
|
+
const config = readWranglerToml(cwd);
|
|
2744
|
+
const bindings2 = extractBindings(config);
|
|
2745
|
+
if (bindings2.length === 0) {
|
|
2746
|
+
console.log();
|
|
2747
|
+
console.log(pc8.yellow("No bindings found in wrangler.toml."));
|
|
2748
|
+
console.log();
|
|
2749
|
+
console.log(
|
|
2750
|
+
pc8.dim(`Use 'cloudwerk bindings add' to add a binding first.`)
|
|
2751
|
+
);
|
|
2752
|
+
console.log();
|
|
2753
|
+
return;
|
|
2754
|
+
}
|
|
2755
|
+
console.log();
|
|
2756
|
+
logger.info("Generating TypeScript types...");
|
|
2757
|
+
const result = generateEnvTypes(cwd, bindings2);
|
|
2758
|
+
console.log();
|
|
2759
|
+
console.log(
|
|
2760
|
+
pc8.green("\u2713") + ` Updated ${pc8.bold("env.d.ts")} with ${result.bindingCount} binding(s):`
|
|
2761
|
+
);
|
|
2762
|
+
console.log();
|
|
2763
|
+
for (const binding of result.bindings) {
|
|
2764
|
+
console.log(` ${pc8.cyan(binding.name)}: ${pc8.dim(binding.type)}`);
|
|
2765
|
+
}
|
|
2766
|
+
console.log();
|
|
2767
|
+
logger.success("Types generated successfully!");
|
|
2768
|
+
console.log();
|
|
2769
|
+
console.log(
|
|
2770
|
+
pc8.dim("Make sure env.d.ts is included in your tsconfig.json:")
|
|
2771
|
+
);
|
|
2772
|
+
console.log(
|
|
2773
|
+
pc8.dim(' "include": ["env.d.ts", "app/**/*"]')
|
|
2774
|
+
);
|
|
2775
|
+
console.log();
|
|
2776
|
+
} catch (error) {
|
|
2777
|
+
handleCommandError(error, verbose);
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
|
|
1061
2781
|
// src/index.ts
|
|
1062
2782
|
program.name("cloudwerk").description("Cloudwerk CLI - Build and deploy full-stack apps to Cloudflare").version(VERSION);
|
|
1063
2783
|
program.command("dev [path]").description("Start development server").option("-p, --port <number>", "Port to listen on", String(DEFAULT_PORT)).option("-H, --host <host>", "Host to bind", DEFAULT_HOST).option("-c, --config <path>", "Path to config file").option("--verbose", "Enable verbose logging").action(dev);
|
|
1064
2784
|
program.command("build [path]").description("Build project for production deployment to Cloudflare Workers").option("-o, --output <dir>", "Output directory", "./dist").option("--ssg", "Generate static pages for routes with rendering: static").option("--minify", "Minify bundles (default: true)").option("--no-minify", "Disable minification").option("--sourcemap", "Generate source maps").option("-c, --config <path>", "Path to config file").option("--verbose", "Enable verbose logging").action(build);
|
|
2785
|
+
program.command("deploy [path]").description("Deploy to Cloudflare Workers").option("-e, --env <environment>", "Environment to deploy to").option("--dry-run", "Preview deployment without executing").option("--skip-build", "Skip the build step").option("-c, --config <path>", "Path to config file").option("--verbose", "Enable verbose logging").action(deploy);
|
|
1065
2786
|
var configCmd = program.command("config").description("Manage Cloudwerk configuration");
|
|
1066
2787
|
configCmd.command("get <key>").description("Get a configuration value").action(configGet);
|
|
1067
2788
|
configCmd.command("set <key> <value>").description("Set a configuration value").action(configSet);
|
|
2789
|
+
var bindingsCmd = program.command("bindings").description("Manage Cloudflare bindings (D1, KV, R2, Queues, etc.)").option("-e, --env <environment>", "Environment to operate on").option("--verbose", "Enable verbose logging").action(bindings);
|
|
2790
|
+
bindingsCmd.command("add [type]").description("Add a new binding (d1, kv, r2, queue, do, secret)").option("-e, --env <environment>", "Environment to add binding to").option("--skip-types", "Skip TypeScript type generation").option("--verbose", "Enable verbose logging").action(bindingsAdd);
|
|
2791
|
+
bindingsCmd.command("remove [name]").description("Remove a binding").option("-e, --env <environment>", "Environment to remove binding from").option("-f, --force", "Skip confirmation prompt").option("--skip-types", "Skip TypeScript type generation").option("--verbose", "Enable verbose logging").action(bindingsRemove);
|
|
2792
|
+
bindingsCmd.command("update [name]").description("Update an existing binding").option("-e, --env <environment>", "Environment to update binding in").option("--skip-types", "Skip TypeScript type generation").option("--verbose", "Enable verbose logging").action(bindingsUpdate);
|
|
2793
|
+
bindingsCmd.command("generate-types").description("Regenerate TypeScript type definitions").option("--verbose", "Enable verbose logging").action(bindingsGenerateTypes);
|
|
1068
2794
|
program.parse();
|