@cloudwerk/cli 0.11.0 → 0.13.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 +1354 -151
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -265,7 +265,13 @@ import {
|
|
|
265
265
|
resolveLayouts,
|
|
266
266
|
resolveMiddleware,
|
|
267
267
|
loadConfig,
|
|
268
|
-
resolveRoutesPath
|
|
268
|
+
resolveRoutesPath,
|
|
269
|
+
scanQueues,
|
|
270
|
+
buildQueueManifest,
|
|
271
|
+
QUEUES_DIR,
|
|
272
|
+
scanServices,
|
|
273
|
+
buildServiceManifest,
|
|
274
|
+
SERVICES_DIR
|
|
269
275
|
} from "@cloudwerk/core/build";
|
|
270
276
|
var DEFAULT_OUTPUT_DIR = "./dist";
|
|
271
277
|
var BUILD_TEMP_DIR = ".cloudwerk-build";
|
|
@@ -313,6 +319,30 @@ async function build(pathArg, options) {
|
|
|
313
319
|
resolveMiddleware
|
|
314
320
|
);
|
|
315
321
|
logger.debug(`Found ${manifest.routes.length} routes`);
|
|
322
|
+
const queuesPath = path2.resolve(cwd, appDir, QUEUES_DIR);
|
|
323
|
+
let queueManifest = null;
|
|
324
|
+
if (fs2.existsSync(queuesPath)) {
|
|
325
|
+
const queueScanResult = await scanQueues(
|
|
326
|
+
path2.resolve(cwd, appDir),
|
|
327
|
+
{ extensions: cloudwerkConfig.extensions }
|
|
328
|
+
);
|
|
329
|
+
queueManifest = buildQueueManifest(queueScanResult, cwd, { appName: "cloudwerk" });
|
|
330
|
+
if (queueManifest.queues.length > 0) {
|
|
331
|
+
logger.debug(`Found ${queueManifest.queues.length} queue(s)`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const servicesPath = path2.resolve(cwd, appDir, SERVICES_DIR);
|
|
335
|
+
let serviceManifest = null;
|
|
336
|
+
if (fs2.existsSync(servicesPath)) {
|
|
337
|
+
const serviceScanResult = await scanServices(
|
|
338
|
+
path2.resolve(cwd, appDir),
|
|
339
|
+
{ extensions: cloudwerkConfig.extensions }
|
|
340
|
+
);
|
|
341
|
+
serviceManifest = buildServiceManifest(serviceScanResult, cwd);
|
|
342
|
+
if (serviceManifest.services.length > 0) {
|
|
343
|
+
logger.debug(`Found ${serviceManifest.services.length} service(s)`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
316
346
|
const renderer = cloudwerkConfig.ui?.renderer ?? "hono-jsx";
|
|
317
347
|
const serverEntryCode = generateServerEntry(manifest, scanResult, {
|
|
318
348
|
appDir,
|
|
@@ -326,6 +356,9 @@ async function build(pathArg, options) {
|
|
|
326
356
|
publicDir: cloudwerkConfig.publicDir ?? "public",
|
|
327
357
|
root: cwd,
|
|
328
358
|
isProduction: true
|
|
359
|
+
}, {
|
|
360
|
+
queueManifest,
|
|
361
|
+
serviceManifest
|
|
329
362
|
});
|
|
330
363
|
const tempEntryPath = path2.join(tempDir, "_server-entry.ts");
|
|
331
364
|
fs2.writeFileSync(tempEntryPath, serverEntryCode);
|
|
@@ -1206,6 +1239,17 @@ function writeWranglerToml(cwd, config) {
|
|
|
1206
1239
|
const content = TOML.stringify(config);
|
|
1207
1240
|
fs7.writeFileSync(configPath, content, "utf-8");
|
|
1208
1241
|
}
|
|
1242
|
+
function readWranglerTomlRaw(cwd) {
|
|
1243
|
+
const configPath = findWranglerToml(cwd);
|
|
1244
|
+
if (!configPath || configPath.endsWith(".json")) {
|
|
1245
|
+
return "";
|
|
1246
|
+
}
|
|
1247
|
+
return fs7.readFileSync(configPath, "utf-8");
|
|
1248
|
+
}
|
|
1249
|
+
function writeWranglerTomlRaw(cwd, content) {
|
|
1250
|
+
const configPath = path7.join(cwd, "wrangler.toml");
|
|
1251
|
+
fs7.writeFileSync(configPath, content, "utf-8");
|
|
1252
|
+
}
|
|
1209
1253
|
function getEnvConfig(config, env) {
|
|
1210
1254
|
if (!env || !config.env?.[env]) {
|
|
1211
1255
|
return config;
|
|
@@ -1217,97 +1261,7 @@ function getEnvConfig(config, env) {
|
|
|
1217
1261
|
}
|
|
1218
1262
|
function extractBindings(config, env) {
|
|
1219
1263
|
const envConfig = getEnvConfig(config, env);
|
|
1220
|
-
|
|
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;
|
|
1264
|
+
return extractBindingsFromConfig(envConfig);
|
|
1311
1265
|
}
|
|
1312
1266
|
function getEnvironments(config) {
|
|
1313
1267
|
if (!config.env) {
|
|
@@ -1521,10 +1475,109 @@ function removeBinding(cwd, bindingName, env) {
|
|
|
1521
1475
|
}
|
|
1522
1476
|
return removed;
|
|
1523
1477
|
}
|
|
1478
|
+
function setAccountId(cwd, accountId) {
|
|
1479
|
+
const config = readWranglerToml(cwd);
|
|
1480
|
+
config.account_id = accountId;
|
|
1481
|
+
writeWranglerToml(cwd, config);
|
|
1482
|
+
}
|
|
1524
1483
|
function bindingExists(config, bindingName, env) {
|
|
1525
|
-
const
|
|
1484
|
+
const targetConfig = env && config.env?.[env] ? config.env[env] : config;
|
|
1485
|
+
const bindings2 = extractBindingsFromConfig(targetConfig);
|
|
1526
1486
|
return bindings2.some((b) => b.name === bindingName);
|
|
1527
1487
|
}
|
|
1488
|
+
function extractBindingsFromConfig(config) {
|
|
1489
|
+
const bindings2 = [];
|
|
1490
|
+
if (config.d1_databases) {
|
|
1491
|
+
for (const db of config.d1_databases) {
|
|
1492
|
+
bindings2.push({
|
|
1493
|
+
type: "d1",
|
|
1494
|
+
name: db.binding,
|
|
1495
|
+
resourceId: db.database_id,
|
|
1496
|
+
resourceName: db.database_name
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
if (config.kv_namespaces) {
|
|
1501
|
+
for (const kv of config.kv_namespaces) {
|
|
1502
|
+
bindings2.push({
|
|
1503
|
+
type: "kv",
|
|
1504
|
+
name: kv.binding,
|
|
1505
|
+
resourceId: kv.id
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
if (config.r2_buckets) {
|
|
1510
|
+
for (const r2 of config.r2_buckets) {
|
|
1511
|
+
bindings2.push({
|
|
1512
|
+
type: "r2",
|
|
1513
|
+
name: r2.binding,
|
|
1514
|
+
resourceName: r2.bucket_name
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
if (config.queues?.producers) {
|
|
1519
|
+
for (const queue of config.queues.producers) {
|
|
1520
|
+
bindings2.push({
|
|
1521
|
+
type: "queue",
|
|
1522
|
+
name: queue.binding,
|
|
1523
|
+
resourceName: queue.queue
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
if (config.durable_objects?.bindings) {
|
|
1528
|
+
for (const doBinding of config.durable_objects.bindings) {
|
|
1529
|
+
bindings2.push({
|
|
1530
|
+
type: "do",
|
|
1531
|
+
name: doBinding.name,
|
|
1532
|
+
resourceName: doBinding.class_name,
|
|
1533
|
+
extra: doBinding.script_name ? { script_name: doBinding.script_name } : void 0
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
if (config.services) {
|
|
1538
|
+
for (const service of config.services) {
|
|
1539
|
+
bindings2.push({
|
|
1540
|
+
type: "service",
|
|
1541
|
+
name: service.binding,
|
|
1542
|
+
resourceName: service.service,
|
|
1543
|
+
extra: service.environment ? { environment: service.environment } : void 0
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
if (config.vars) {
|
|
1548
|
+
for (const [name] of Object.entries(config.vars)) {
|
|
1549
|
+
bindings2.push({
|
|
1550
|
+
type: "secret",
|
|
1551
|
+
name
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
if (config.ai) {
|
|
1556
|
+
bindings2.push({
|
|
1557
|
+
type: "ai",
|
|
1558
|
+
name: config.ai.binding
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
if (config.vectorize) {
|
|
1562
|
+
for (const vec of config.vectorize) {
|
|
1563
|
+
bindings2.push({
|
|
1564
|
+
type: "vectorize",
|
|
1565
|
+
name: vec.binding,
|
|
1566
|
+
resourceName: vec.index_name
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
if (config.hyperdrive) {
|
|
1571
|
+
for (const hd of config.hyperdrive) {
|
|
1572
|
+
bindings2.push({
|
|
1573
|
+
type: "hyperdrive",
|
|
1574
|
+
name: hd.binding,
|
|
1575
|
+
resourceId: hd.id
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
return bindings2;
|
|
1580
|
+
}
|
|
1528
1581
|
function getBindingTypeName(type) {
|
|
1529
1582
|
switch (type) {
|
|
1530
1583
|
case "d1":
|
|
@@ -1826,7 +1879,7 @@ async function addD1(cwd, options) {
|
|
|
1826
1879
|
});
|
|
1827
1880
|
const databaseName = await input({
|
|
1828
1881
|
message: "Database name:",
|
|
1829
|
-
default: `${config.name || "my-app"}
|
|
1882
|
+
default: `${config.name || "my-app"}${env ? `-${env}` : ""}-db`,
|
|
1830
1883
|
validate: (value) => {
|
|
1831
1884
|
if (!value.trim()) return "Database name is required";
|
|
1832
1885
|
return true;
|
|
@@ -1963,7 +2016,7 @@ async function addR2(cwd, options) {
|
|
|
1963
2016
|
if (createNew) {
|
|
1964
2017
|
bucketName = await input({
|
|
1965
2018
|
message: "Bucket name:",
|
|
1966
|
-
default: `${config.name || "my-app"}
|
|
2019
|
+
default: `${config.name || "my-app"}${env ? `-${env}` : ""}-bucket`,
|
|
1967
2020
|
validate: (value) => {
|
|
1968
2021
|
if (!value.trim()) return "Bucket name is required";
|
|
1969
2022
|
return true;
|
|
@@ -2021,7 +2074,7 @@ async function addQueue(cwd, options) {
|
|
|
2021
2074
|
if (createNew) {
|
|
2022
2075
|
queueName = await input({
|
|
2023
2076
|
message: "Queue name:",
|
|
2024
|
-
default: `${config.name || "my-app"}
|
|
2077
|
+
default: `${config.name || "my-app"}${env ? `-${env}` : ""}-queue`,
|
|
2025
2078
|
validate: (value) => {
|
|
2026
2079
|
if (!value.trim()) return "Queue name is required";
|
|
2027
2080
|
return true;
|
|
@@ -2163,8 +2216,59 @@ function showTypeHint(bindingName, type) {
|
|
|
2163
2216
|
console.log();
|
|
2164
2217
|
console.log(pc5.dim(`TypeScript type: ${bindingName}: ${tsType}`));
|
|
2165
2218
|
}
|
|
2166
|
-
function
|
|
2167
|
-
|
|
2219
|
+
function parseAccountsFromError(errorMessage) {
|
|
2220
|
+
const accounts = [];
|
|
2221
|
+
const accountRegex = /`([^`]+)`:\s*`([a-f0-9]+)`/g;
|
|
2222
|
+
let match;
|
|
2223
|
+
while ((match = accountRegex.exec(errorMessage)) !== null) {
|
|
2224
|
+
accounts.push({
|
|
2225
|
+
name: match[1],
|
|
2226
|
+
id: match[2]
|
|
2227
|
+
});
|
|
2228
|
+
}
|
|
2229
|
+
return accounts;
|
|
2230
|
+
}
|
|
2231
|
+
function isMultiAccountError(errorMessage) {
|
|
2232
|
+
return errorMessage.includes("More than one account available");
|
|
2233
|
+
}
|
|
2234
|
+
async function promptForAccount(accounts) {
|
|
2235
|
+
console.log();
|
|
2236
|
+
const selected = await select({
|
|
2237
|
+
message: "Multiple Cloudflare accounts available. Which account do you want to use?",
|
|
2238
|
+
choices: accounts.map((account) => ({
|
|
2239
|
+
name: `${account.name} (${account.id.slice(0, 8)}...)`,
|
|
2240
|
+
value: account.id
|
|
2241
|
+
}))
|
|
2242
|
+
});
|
|
2243
|
+
return selected;
|
|
2244
|
+
}
|
|
2245
|
+
async function handleWranglerResult(result, retryFn, cwd) {
|
|
2246
|
+
if (result.error && isMultiAccountError(result.stderr)) {
|
|
2247
|
+
const accounts = parseAccountsFromError(result.stderr);
|
|
2248
|
+
if (accounts.length > 0) {
|
|
2249
|
+
const selectedAccountId = await promptForAccount(accounts);
|
|
2250
|
+
const projectCwd = cwd || process.cwd();
|
|
2251
|
+
setAccountId(projectCwd, selectedAccountId);
|
|
2252
|
+
console.log(pc5.green("\u2713") + " Set account_id in wrangler.toml");
|
|
2253
|
+
console.log();
|
|
2254
|
+
const retryResult = await retryFn();
|
|
2255
|
+
if (retryResult.error) {
|
|
2256
|
+
throw new Error(retryResult.stderr || `wrangler exited with code ${retryResult.code}`);
|
|
2257
|
+
}
|
|
2258
|
+
return retryResult.stdout;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
if (result.error) {
|
|
2262
|
+
throw new Error(result.stderr || `wrangler exited with code ${result.code}`);
|
|
2263
|
+
}
|
|
2264
|
+
return result.stdout;
|
|
2265
|
+
}
|
|
2266
|
+
async function runWranglerCommand(args, cwd) {
|
|
2267
|
+
const result = await runWranglerCommandRaw(args);
|
|
2268
|
+
return handleWranglerResult(result, () => runWranglerCommandRaw(args), cwd);
|
|
2269
|
+
}
|
|
2270
|
+
function runWranglerCommandRaw(args) {
|
|
2271
|
+
return new Promise((resolve4) => {
|
|
2168
2272
|
let stdout = "";
|
|
2169
2273
|
let stderr = "";
|
|
2170
2274
|
const child = spawn2("npx", ["wrangler", ...args], {
|
|
@@ -2177,19 +2281,23 @@ function runWranglerCommand(args) {
|
|
|
2177
2281
|
stderr += data.toString();
|
|
2178
2282
|
});
|
|
2179
2283
|
child.on("error", (error) => {
|
|
2180
|
-
|
|
2284
|
+
resolve4({ stdout, stderr: error.message, code: null, error: true });
|
|
2181
2285
|
});
|
|
2182
2286
|
child.on("close", (code) => {
|
|
2183
|
-
|
|
2184
|
-
reject(new Error(stderr || `wrangler exited with code ${code}`));
|
|
2185
|
-
} else {
|
|
2186
|
-
resolve4(stdout);
|
|
2187
|
-
}
|
|
2287
|
+
resolve4({ stdout, stderr, code, error: code !== 0 });
|
|
2188
2288
|
});
|
|
2189
2289
|
});
|
|
2190
2290
|
}
|
|
2191
|
-
function runWranglerCommandWithInput(args, inputData) {
|
|
2192
|
-
|
|
2291
|
+
async function runWranglerCommandWithInput(args, inputData, cwd) {
|
|
2292
|
+
const result = await runWranglerCommandWithInputRaw(args, inputData);
|
|
2293
|
+
return handleWranglerResult(
|
|
2294
|
+
result,
|
|
2295
|
+
() => runWranglerCommandWithInputRaw(args, inputData),
|
|
2296
|
+
cwd
|
|
2297
|
+
);
|
|
2298
|
+
}
|
|
2299
|
+
function runWranglerCommandWithInputRaw(args, inputData) {
|
|
2300
|
+
return new Promise((resolve4) => {
|
|
2193
2301
|
let stdout = "";
|
|
2194
2302
|
let stderr = "";
|
|
2195
2303
|
const child = spawn2("npx", ["wrangler", ...args], {
|
|
@@ -2202,14 +2310,10 @@ function runWranglerCommandWithInput(args, inputData) {
|
|
|
2202
2310
|
stderr += data.toString();
|
|
2203
2311
|
});
|
|
2204
2312
|
child.on("error", (error) => {
|
|
2205
|
-
|
|
2313
|
+
resolve4({ stdout, stderr: error.message, code: null, error: true });
|
|
2206
2314
|
});
|
|
2207
2315
|
child.on("close", (code) => {
|
|
2208
|
-
|
|
2209
|
-
reject(new Error(stderr || `wrangler exited with code ${code}`));
|
|
2210
|
-
} else {
|
|
2211
|
-
resolve4(stdout);
|
|
2212
|
-
}
|
|
2316
|
+
resolve4({ stdout, stderr, code, error: code !== 0 });
|
|
2213
2317
|
});
|
|
2214
2318
|
child.stdin?.write(inputData);
|
|
2215
2319
|
child.stdin?.end();
|
|
@@ -2726,69 +2830,1168 @@ function runWranglerCommandWithInput2(args, inputData) {
|
|
|
2726
2830
|
|
|
2727
2831
|
// src/commands/bindings/generate-types.ts
|
|
2728
2832
|
import pc8 from "picocolors";
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2833
|
+
|
|
2834
|
+
// src/utils/type-generator.ts
|
|
2835
|
+
import * as fs9 from "fs";
|
|
2836
|
+
import * as path10 from "path";
|
|
2837
|
+
var CLOUDWERK_TYPES_DIR = ".cloudwerk/types";
|
|
2838
|
+
var BINDINGS_DTS = "bindings.d.ts";
|
|
2839
|
+
var CONTEXT_DTS = "context.d.ts";
|
|
2840
|
+
function generateCloudwerkTypes(cwd, bindings2, options = {}) {
|
|
2841
|
+
const includeTimestamp = options.includeTimestamp ?? true;
|
|
2842
|
+
const typesDir = path10.join(cwd, CLOUDWERK_TYPES_DIR);
|
|
2843
|
+
fs9.mkdirSync(typesDir, { recursive: true });
|
|
2844
|
+
const bindingsPath = path10.join(typesDir, BINDINGS_DTS);
|
|
2845
|
+
const bindingsContent = generateBindingsDts(bindings2, includeTimestamp);
|
|
2846
|
+
fs9.writeFileSync(bindingsPath, bindingsContent, "utf-8");
|
|
2847
|
+
const contextPath = path10.join(typesDir, CONTEXT_DTS);
|
|
2848
|
+
const contextContent = generateContextDts(bindings2, includeTimestamp);
|
|
2849
|
+
fs9.writeFileSync(contextPath, contextContent, "utf-8");
|
|
2850
|
+
const bindingInfo = bindings2.map((b) => ({
|
|
2851
|
+
name: b.name,
|
|
2852
|
+
type: getTypeForBinding(b.type)
|
|
2853
|
+
}));
|
|
2854
|
+
return {
|
|
2855
|
+
typesDir,
|
|
2856
|
+
files: {
|
|
2857
|
+
bindings: bindingsPath,
|
|
2858
|
+
context: contextPath
|
|
2859
|
+
},
|
|
2860
|
+
bindingCount: bindings2.length,
|
|
2861
|
+
bindings: bindingInfo
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
function generateBindingsDts(bindings2, includeTimestamp) {
|
|
2865
|
+
const lines = [];
|
|
2866
|
+
lines.push("// Auto-generated by cloudwerk bindings - DO NOT EDIT");
|
|
2867
|
+
if (includeTimestamp) {
|
|
2868
|
+
lines.push(`// Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
2869
|
+
}
|
|
2870
|
+
lines.push("//");
|
|
2871
|
+
lines.push("// This file provides type information for @cloudwerk/core/bindings");
|
|
2872
|
+
lines.push('// Add ".cloudwerk/types" to your tsconfig.json include array');
|
|
2873
|
+
lines.push("");
|
|
2874
|
+
lines.push("declare module '@cloudwerk/core/bindings' {");
|
|
2875
|
+
const bindingsByType = groupBindingsByType(bindings2);
|
|
2876
|
+
const typeOrder = [
|
|
2877
|
+
"d1",
|
|
2878
|
+
"kv",
|
|
2879
|
+
"r2",
|
|
2880
|
+
"queue",
|
|
2881
|
+
"do",
|
|
2882
|
+
"service",
|
|
2883
|
+
"ai",
|
|
2884
|
+
"vectorize",
|
|
2885
|
+
"hyperdrive",
|
|
2886
|
+
"secret"
|
|
2887
|
+
];
|
|
2888
|
+
let firstSection = true;
|
|
2889
|
+
for (const type of typeOrder) {
|
|
2890
|
+
const typeBindings = bindingsByType.get(type);
|
|
2891
|
+
if (!typeBindings || typeBindings.length === 0) continue;
|
|
2892
|
+
const tsType = getTypeForBinding(type);
|
|
2893
|
+
const sectionName = getSectionName2(type);
|
|
2894
|
+
if (!firstSection) {
|
|
2895
|
+
lines.push("");
|
|
2741
2896
|
}
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
const
|
|
2745
|
-
|
|
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;
|
|
2897
|
+
lines.push(` // ${sectionName}`);
|
|
2898
|
+
firstSection = false;
|
|
2899
|
+
for (const binding of typeBindings) {
|
|
2900
|
+
lines.push(` export const ${binding.name}: ${tsType}`);
|
|
2754
2901
|
}
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2902
|
+
}
|
|
2903
|
+
lines.push("");
|
|
2904
|
+
lines.push(" // Bindings proxy object (for dynamic access)");
|
|
2905
|
+
lines.push(" export const bindings: Record<string, unknown>");
|
|
2906
|
+
lines.push("");
|
|
2907
|
+
lines.push(" // Helper functions");
|
|
2908
|
+
lines.push(" export function getBinding<T = unknown>(name: string): T");
|
|
2909
|
+
lines.push(" export function hasBinding(name: string): boolean");
|
|
2910
|
+
lines.push(" export function getBindingNames(): string[]");
|
|
2911
|
+
lines.push("}");
|
|
2912
|
+
lines.push("");
|
|
2913
|
+
return lines.join("\n");
|
|
2914
|
+
}
|
|
2915
|
+
function generateContextDts(bindings2, includeTimestamp) {
|
|
2916
|
+
const lines = [];
|
|
2917
|
+
lines.push("// Auto-generated by cloudwerk bindings - DO NOT EDIT");
|
|
2918
|
+
if (includeTimestamp) {
|
|
2919
|
+
lines.push(`// Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
2920
|
+
}
|
|
2921
|
+
lines.push("//");
|
|
2922
|
+
lines.push("// This file provides type information for @cloudwerk/core/context");
|
|
2923
|
+
lines.push('// Add ".cloudwerk/types" to your tsconfig.json include array');
|
|
2924
|
+
lines.push("");
|
|
2925
|
+
lines.push("interface CloudwerkEnv {");
|
|
2926
|
+
const bindingsByType = groupBindingsByType(bindings2);
|
|
2927
|
+
const typeOrder = [
|
|
2928
|
+
"d1",
|
|
2929
|
+
"kv",
|
|
2930
|
+
"r2",
|
|
2931
|
+
"queue",
|
|
2932
|
+
"do",
|
|
2933
|
+
"service",
|
|
2934
|
+
"ai",
|
|
2935
|
+
"vectorize",
|
|
2936
|
+
"hyperdrive",
|
|
2937
|
+
"secret"
|
|
2938
|
+
];
|
|
2939
|
+
let firstSection = true;
|
|
2940
|
+
for (const type of typeOrder) {
|
|
2941
|
+
const typeBindings = bindingsByType.get(type);
|
|
2942
|
+
if (!typeBindings || typeBindings.length === 0) continue;
|
|
2943
|
+
const tsType = getTypeForBinding(type);
|
|
2944
|
+
const sectionName = getSectionName2(type);
|
|
2945
|
+
if (!firstSection) {
|
|
2946
|
+
lines.push("");
|
|
2947
|
+
}
|
|
2948
|
+
lines.push(` // ${sectionName}`);
|
|
2949
|
+
firstSection = false;
|
|
2950
|
+
for (const binding of typeBindings) {
|
|
2951
|
+
lines.push(` ${binding.name}: ${tsType}`);
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
lines.push("}");
|
|
2955
|
+
lines.push("");
|
|
2956
|
+
lines.push("declare module '@cloudwerk/core/context' {");
|
|
2957
|
+
lines.push(" // Route parameters proxy");
|
|
2958
|
+
lines.push(" export const params: Record<string, string>");
|
|
2959
|
+
lines.push("");
|
|
2960
|
+
lines.push(" // Current request proxy");
|
|
2961
|
+
lines.push(" export const request: Request");
|
|
2962
|
+
lines.push("");
|
|
2963
|
+
lines.push(" // Environment bindings proxy (typed from wrangler.toml)");
|
|
2964
|
+
lines.push(" export const env: CloudwerkEnv");
|
|
2965
|
+
lines.push("");
|
|
2966
|
+
lines.push(" // Cloudflare execution context proxy");
|
|
2967
|
+
lines.push(" export const executionCtx: {");
|
|
2968
|
+
lines.push(" waitUntil(promise: Promise<unknown>): void");
|
|
2969
|
+
lines.push(" passThroughOnException(): void");
|
|
2970
|
+
lines.push(" }");
|
|
2971
|
+
lines.push("");
|
|
2972
|
+
lines.push(" // Helper functions");
|
|
2973
|
+
lines.push(" export function getRequestId(): string");
|
|
2974
|
+
lines.push(" export function get<T>(key: string): T | undefined");
|
|
2975
|
+
lines.push(" export function set<T>(key: string, value: T): void");
|
|
2976
|
+
lines.push("}");
|
|
2977
|
+
lines.push("");
|
|
2978
|
+
return lines.join("\n");
|
|
2979
|
+
}
|
|
2980
|
+
function groupBindingsByType(bindings2) {
|
|
2981
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
2982
|
+
for (const binding of bindings2) {
|
|
2983
|
+
const existing = grouped.get(binding.type) || [];
|
|
2984
|
+
existing.push(binding);
|
|
2985
|
+
grouped.set(binding.type, existing);
|
|
2986
|
+
}
|
|
2987
|
+
return grouped;
|
|
2988
|
+
}
|
|
2989
|
+
function getSectionName2(type) {
|
|
2990
|
+
switch (type) {
|
|
2991
|
+
case "d1":
|
|
2992
|
+
return "D1 Databases";
|
|
2993
|
+
case "kv":
|
|
2994
|
+
return "KV Namespaces";
|
|
2995
|
+
case "r2":
|
|
2996
|
+
return "R2 Buckets";
|
|
2997
|
+
case "queue":
|
|
2998
|
+
return "Queues";
|
|
2999
|
+
case "do":
|
|
3000
|
+
return "Durable Objects";
|
|
3001
|
+
case "service":
|
|
3002
|
+
return "Services";
|
|
3003
|
+
case "secret":
|
|
3004
|
+
return "Environment Variables";
|
|
3005
|
+
case "ai":
|
|
3006
|
+
return "AI";
|
|
3007
|
+
case "vectorize":
|
|
3008
|
+
return "Vectorize Indexes";
|
|
3009
|
+
case "hyperdrive":
|
|
3010
|
+
return "Hyperdrive";
|
|
3011
|
+
default:
|
|
3012
|
+
return "Other";
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
// src/utils/tsconfig-updater.ts
|
|
3017
|
+
import * as fs10 from "fs";
|
|
3018
|
+
import * as path11 from "path";
|
|
3019
|
+
var CLOUDWERK_TYPES_INCLUDE = ".cloudwerk/types/**/*";
|
|
3020
|
+
var BINDINGS_PATH_KEY = "@cloudwerk/core/bindings";
|
|
3021
|
+
var CONTEXT_PATH_KEY = "@cloudwerk/core/context";
|
|
3022
|
+
function updateTsConfigPaths(cwd) {
|
|
3023
|
+
const tsconfigPath = path11.join(cwd, "tsconfig.json");
|
|
3024
|
+
const changes = {
|
|
3025
|
+
addedIncludes: [],
|
|
3026
|
+
addedPaths: [],
|
|
3027
|
+
setBaseUrl: false
|
|
3028
|
+
};
|
|
3029
|
+
if (!fs10.existsSync(tsconfigPath)) {
|
|
3030
|
+
const newConfig = {
|
|
3031
|
+
compilerOptions: {
|
|
3032
|
+
baseUrl: ".",
|
|
3033
|
+
paths: {
|
|
3034
|
+
[BINDINGS_PATH_KEY]: ["./.cloudwerk/types/bindings.d.ts"],
|
|
3035
|
+
[CONTEXT_PATH_KEY]: ["./.cloudwerk/types/context.d.ts"]
|
|
3036
|
+
}
|
|
3037
|
+
},
|
|
3038
|
+
include: [CLOUDWERK_TYPES_INCLUDE]
|
|
3039
|
+
};
|
|
3040
|
+
fs10.writeFileSync(tsconfigPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
|
|
3041
|
+
return {
|
|
3042
|
+
tsconfigPath,
|
|
3043
|
+
modified: true,
|
|
3044
|
+
changes: {
|
|
3045
|
+
addedIncludes: [CLOUDWERK_TYPES_INCLUDE],
|
|
3046
|
+
addedPaths: [BINDINGS_PATH_KEY, CONTEXT_PATH_KEY],
|
|
3047
|
+
setBaseUrl: true
|
|
3048
|
+
}
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
const content = fs10.readFileSync(tsconfigPath, "utf-8");
|
|
3052
|
+
let config;
|
|
3053
|
+
try {
|
|
3054
|
+
config = JSON.parse(content);
|
|
3055
|
+
} catch {
|
|
3056
|
+
throw new Error(
|
|
3057
|
+
`Invalid JSON in tsconfig.json. Please fix the syntax and try again.`
|
|
3058
|
+
);
|
|
3059
|
+
}
|
|
3060
|
+
let modified = false;
|
|
3061
|
+
if (!config.compilerOptions) {
|
|
3062
|
+
config.compilerOptions = {};
|
|
3063
|
+
}
|
|
3064
|
+
if (!config.compilerOptions.baseUrl) {
|
|
3065
|
+
config.compilerOptions.baseUrl = ".";
|
|
3066
|
+
changes.setBaseUrl = true;
|
|
3067
|
+
modified = true;
|
|
3068
|
+
}
|
|
3069
|
+
if (!config.compilerOptions.paths) {
|
|
3070
|
+
config.compilerOptions.paths = {};
|
|
3071
|
+
}
|
|
3072
|
+
if (!config.compilerOptions.paths[BINDINGS_PATH_KEY]) {
|
|
3073
|
+
config.compilerOptions.paths[BINDINGS_PATH_KEY] = [
|
|
3074
|
+
"./.cloudwerk/types/bindings.d.ts"
|
|
3075
|
+
];
|
|
3076
|
+
changes.addedPaths.push(BINDINGS_PATH_KEY);
|
|
3077
|
+
modified = true;
|
|
3078
|
+
}
|
|
3079
|
+
if (!config.compilerOptions.paths[CONTEXT_PATH_KEY]) {
|
|
3080
|
+
config.compilerOptions.paths[CONTEXT_PATH_KEY] = [
|
|
3081
|
+
"./.cloudwerk/types/context.d.ts"
|
|
3082
|
+
];
|
|
3083
|
+
changes.addedPaths.push(CONTEXT_PATH_KEY);
|
|
3084
|
+
modified = true;
|
|
3085
|
+
}
|
|
3086
|
+
if (!config.include) {
|
|
3087
|
+
config.include = [];
|
|
3088
|
+
}
|
|
3089
|
+
if (!config.include.includes(CLOUDWERK_TYPES_INCLUDE)) {
|
|
3090
|
+
config.include.unshift(CLOUDWERK_TYPES_INCLUDE);
|
|
3091
|
+
changes.addedIncludes.push(CLOUDWERK_TYPES_INCLUDE);
|
|
3092
|
+
modified = true;
|
|
3093
|
+
}
|
|
3094
|
+
if (modified) {
|
|
3095
|
+
const newContent = JSON.stringify(config, null, 2) + "\n";
|
|
3096
|
+
fs10.writeFileSync(tsconfigPath, newContent, "utf-8");
|
|
3097
|
+
}
|
|
3098
|
+
return {
|
|
3099
|
+
tsconfigPath,
|
|
3100
|
+
modified,
|
|
3101
|
+
changes
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
// src/commands/bindings/generate-types.ts
|
|
3106
|
+
async function bindingsGenerateTypes(options) {
|
|
3107
|
+
const verbose = options.verbose ?? false;
|
|
3108
|
+
const logger = createLogger(verbose);
|
|
3109
|
+
try {
|
|
3110
|
+
const cwd = process.cwd();
|
|
3111
|
+
const wranglerPath = findWranglerToml(cwd);
|
|
3112
|
+
if (!wranglerPath) {
|
|
3113
|
+
throw new CliError(
|
|
3114
|
+
"wrangler.toml not found",
|
|
3115
|
+
"ENOENT",
|
|
3116
|
+
"Create a wrangler.toml file or run this command from a Cloudwerk project directory."
|
|
3117
|
+
);
|
|
3118
|
+
}
|
|
3119
|
+
logger.debug(`Found wrangler config: ${wranglerPath}`);
|
|
3120
|
+
const config = readWranglerToml(cwd);
|
|
3121
|
+
const bindings2 = extractBindings(config);
|
|
3122
|
+
if (bindings2.length === 0) {
|
|
3123
|
+
console.log();
|
|
3124
|
+
console.log(pc8.yellow("No bindings found in wrangler.toml."));
|
|
3125
|
+
console.log();
|
|
3126
|
+
console.log(
|
|
3127
|
+
pc8.dim(`Use 'cloudwerk bindings add' to add a binding first.`)
|
|
3128
|
+
);
|
|
3129
|
+
console.log();
|
|
3130
|
+
return;
|
|
2765
3131
|
}
|
|
2766
3132
|
console.log();
|
|
2767
|
-
logger.
|
|
3133
|
+
logger.info("Generating TypeScript types...");
|
|
3134
|
+
const result = generateEnvTypes(cwd, bindings2);
|
|
3135
|
+
console.log();
|
|
3136
|
+
console.log(
|
|
3137
|
+
pc8.green("\u2713") + ` Updated ${pc8.bold("env.d.ts")} with ${result.bindingCount} binding(s):`
|
|
3138
|
+
);
|
|
3139
|
+
console.log();
|
|
3140
|
+
for (const binding of result.bindings) {
|
|
3141
|
+
console.log(` ${pc8.cyan(binding.name)}: ${pc8.dim(binding.type)}`);
|
|
3142
|
+
}
|
|
3143
|
+
logger.debug("Generating .cloudwerk/types/...");
|
|
3144
|
+
const cloudwerkResult = generateCloudwerkTypes(cwd, bindings2);
|
|
2768
3145
|
console.log();
|
|
2769
3146
|
console.log(
|
|
2770
|
-
pc8.
|
|
3147
|
+
pc8.green("\u2713") + ` Generated ${pc8.bold(".cloudwerk/types/")} for importable bindings:`
|
|
2771
3148
|
);
|
|
3149
|
+
console.log(` ${pc8.dim(cloudwerkResult.files.bindings)}`);
|
|
3150
|
+
console.log(` ${pc8.dim(cloudwerkResult.files.context)}`);
|
|
3151
|
+
logger.debug("Updating tsconfig.json...");
|
|
3152
|
+
const tsconfigResult = updateTsConfigPaths(cwd);
|
|
3153
|
+
if (tsconfigResult.modified) {
|
|
3154
|
+
console.log();
|
|
3155
|
+
console.log(
|
|
3156
|
+
pc8.green("\u2713") + ` Updated ${pc8.bold("tsconfig.json")}:`
|
|
3157
|
+
);
|
|
3158
|
+
if (tsconfigResult.changes.setBaseUrl) {
|
|
3159
|
+
console.log(` ${pc8.dim('Added baseUrl: "."')}`);
|
|
3160
|
+
}
|
|
3161
|
+
for (const pathKey of tsconfigResult.changes.addedPaths) {
|
|
3162
|
+
console.log(` ${pc8.dim(`Added paths: "${pathKey}"`)}`);
|
|
3163
|
+
}
|
|
3164
|
+
for (const include of tsconfigResult.changes.addedIncludes) {
|
|
3165
|
+
console.log(` ${pc8.dim(`Added include: "${include}"`)}`);
|
|
3166
|
+
}
|
|
3167
|
+
} else {
|
|
3168
|
+
logger.debug("tsconfig.json already configured");
|
|
3169
|
+
}
|
|
3170
|
+
console.log();
|
|
3171
|
+
logger.success("Types generated successfully!");
|
|
3172
|
+
console.log();
|
|
3173
|
+
console.log(pc8.bold("Usage:"));
|
|
3174
|
+
console.log();
|
|
3175
|
+
console.log(pc8.dim(" // Import bindings directly (new)"));
|
|
3176
|
+
console.log(pc8.cyan(` import { ${bindings2[0]?.name || "DB"} } from '@cloudwerk/core/bindings'`));
|
|
3177
|
+
console.log();
|
|
3178
|
+
console.log(pc8.dim(" // Import context helpers (new)"));
|
|
3179
|
+
console.log(pc8.cyan(` import { params, request, get } from '@cloudwerk/core/context'`));
|
|
3180
|
+
console.log();
|
|
3181
|
+
console.log(pc8.dim(" // Or use getContext() (existing)"));
|
|
3182
|
+
console.log(pc8.cyan(` import { getContext } from '@cloudwerk/core'`));
|
|
3183
|
+
console.log();
|
|
3184
|
+
} catch (error) {
|
|
3185
|
+
handleCommandError(error, verbose);
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
// src/commands/triggers.ts
|
|
3190
|
+
import pc9 from "picocolors";
|
|
3191
|
+
import {
|
|
3192
|
+
loadConfig as loadConfig3,
|
|
3193
|
+
scanTriggers,
|
|
3194
|
+
buildTriggerManifest,
|
|
3195
|
+
getTriggerSummary
|
|
3196
|
+
} from "@cloudwerk/core/build";
|
|
3197
|
+
var SOURCE_TYPE_LABELS = {
|
|
3198
|
+
scheduled: { label: "cron", color: pc9.blue },
|
|
3199
|
+
queue: { label: "queue", color: pc9.green },
|
|
3200
|
+
r2: { label: "R2", color: pc9.yellow },
|
|
3201
|
+
webhook: { label: "webhook", color: pc9.magenta },
|
|
3202
|
+
email: { label: "email", color: pc9.cyan },
|
|
3203
|
+
d1: { label: "D1", color: pc9.red },
|
|
3204
|
+
tail: { label: "tail", color: pc9.gray }
|
|
3205
|
+
};
|
|
3206
|
+
function formatSourceType(type) {
|
|
3207
|
+
const config = SOURCE_TYPE_LABELS[type] || { label: type, color: pc9.dim };
|
|
3208
|
+
return config.color(config.label);
|
|
3209
|
+
}
|
|
3210
|
+
async function triggers(options = {}) {
|
|
3211
|
+
const verbose = options.verbose ?? false;
|
|
3212
|
+
const logger = createLogger(verbose);
|
|
3213
|
+
try {
|
|
3214
|
+
const cwd = process.cwd();
|
|
3215
|
+
logger.debug("Loading configuration...");
|
|
3216
|
+
const config = await loadConfig3(cwd);
|
|
3217
|
+
const appDir = config.appDir;
|
|
3218
|
+
logger.debug(`Scanning for triggers in ${appDir}/triggers/...`);
|
|
3219
|
+
const scanResult = await scanTriggers(appDir, {
|
|
3220
|
+
extensions: config.extensions
|
|
3221
|
+
});
|
|
3222
|
+
const manifest = buildTriggerManifest(scanResult, appDir);
|
|
3223
|
+
const summary = getTriggerSummary(manifest);
|
|
3224
|
+
console.log();
|
|
3225
|
+
console.log(pc9.bold("Cloudwerk Triggers"));
|
|
3226
|
+
console.log();
|
|
3227
|
+
console.log(pc9.dim(` Found ${summary.total} triggers:`));
|
|
3228
|
+
if (summary.total > 0) {
|
|
3229
|
+
if (summary.scheduled > 0) {
|
|
3230
|
+
console.log(pc9.dim(` - ${summary.scheduled} scheduled (cron)`));
|
|
3231
|
+
}
|
|
3232
|
+
if (summary.queue > 0) {
|
|
3233
|
+
console.log(pc9.dim(` - ${summary.queue} queue consumers`));
|
|
3234
|
+
}
|
|
3235
|
+
if (summary.r2 > 0) {
|
|
3236
|
+
console.log(pc9.dim(` - ${summary.r2} R2 notifications`));
|
|
3237
|
+
}
|
|
3238
|
+
if (summary.webhook > 0) {
|
|
3239
|
+
console.log(pc9.dim(` - ${summary.webhook} webhooks`));
|
|
3240
|
+
}
|
|
3241
|
+
if (summary.email > 0) {
|
|
3242
|
+
console.log(pc9.dim(` - ${summary.email} email handlers`));
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
console.log();
|
|
3246
|
+
if (manifest.triggers.length > 0) {
|
|
3247
|
+
for (const trigger of manifest.triggers) {
|
|
3248
|
+
const sourceType = formatSourceType(trigger.source?.type ?? "unknown");
|
|
3249
|
+
const sourceInfo = getSourceInfo(trigger);
|
|
3250
|
+
console.log(
|
|
3251
|
+
` ${pc9.cyan(trigger.name)} ${pc9.dim("(")}${sourceType}${sourceInfo ? pc9.dim(": ") + pc9.dim(sourceInfo) : ""}${pc9.dim(")")}`
|
|
3252
|
+
);
|
|
3253
|
+
}
|
|
3254
|
+
console.log();
|
|
3255
|
+
}
|
|
3256
|
+
if (manifest.warnings.length > 0) {
|
|
3257
|
+
console.log(pc9.yellow("Warnings:"));
|
|
3258
|
+
for (const warning of manifest.warnings) {
|
|
3259
|
+
console.log(pc9.dim(` - ${warning.file}: ${warning.message}`));
|
|
3260
|
+
}
|
|
3261
|
+
console.log();
|
|
3262
|
+
}
|
|
3263
|
+
console.log(pc9.bold("Commands:"));
|
|
3264
|
+
console.log();
|
|
3265
|
+
console.log(pc9.dim(" cloudwerk triggers list ") + "List all triggers with details");
|
|
3266
|
+
console.log(pc9.dim(" cloudwerk triggers validate ") + "Validate trigger configurations");
|
|
3267
|
+
console.log(pc9.dim(" cloudwerk triggers generate ") + "Regenerate wrangler config");
|
|
3268
|
+
console.log();
|
|
3269
|
+
if (manifest.triggers.length === 0) {
|
|
3270
|
+
console.log(pc9.bold("Quick Start:"));
|
|
3271
|
+
console.log();
|
|
3272
|
+
console.log(pc9.dim(" Create a scheduled trigger at app/triggers/daily-cleanup.ts:"));
|
|
3273
|
+
console.log();
|
|
3274
|
+
console.log(pc9.cyan(" import { defineTrigger } from '@cloudwerk/trigger'"));
|
|
3275
|
+
console.log();
|
|
3276
|
+
console.log(pc9.cyan(" export default defineTrigger({"));
|
|
3277
|
+
console.log(pc9.cyan(" source: { type: 'scheduled', cron: '0 0 * * *' },"));
|
|
3278
|
+
console.log(pc9.cyan(" async handle(event, ctx) {"));
|
|
3279
|
+
console.log(pc9.cyan(" console.log(`[${ctx.traceId}] Running cleanup`)"));
|
|
3280
|
+
console.log(pc9.cyan(" await cleanupOldRecords()"));
|
|
3281
|
+
console.log(pc9.cyan(" }"));
|
|
3282
|
+
console.log(pc9.cyan(" })"));
|
|
3283
|
+
console.log();
|
|
3284
|
+
}
|
|
3285
|
+
} catch (error) {
|
|
3286
|
+
handleCommandError(error, verbose);
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
function getSourceInfo(trigger) {
|
|
3290
|
+
const source = trigger.source;
|
|
3291
|
+
if (!source) return "";
|
|
3292
|
+
const type = source.type;
|
|
3293
|
+
switch (type) {
|
|
3294
|
+
case "scheduled":
|
|
3295
|
+
return String(source.cron ?? "");
|
|
3296
|
+
case "queue":
|
|
3297
|
+
return String(source.queue ?? "");
|
|
3298
|
+
case "r2":
|
|
3299
|
+
return String(source.bucket ?? "");
|
|
3300
|
+
case "webhook":
|
|
3301
|
+
return String(source.path ?? "");
|
|
3302
|
+
case "email":
|
|
3303
|
+
return String(source.address ?? "");
|
|
3304
|
+
default:
|
|
3305
|
+
return "";
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
// src/commands/triggers/list.ts
|
|
3310
|
+
import pc10 from "picocolors";
|
|
3311
|
+
import {
|
|
3312
|
+
loadConfig as loadConfig4,
|
|
3313
|
+
scanTriggers as scanTriggers2,
|
|
3314
|
+
buildTriggerManifest as buildTriggerManifest2
|
|
3315
|
+
} from "@cloudwerk/core/build";
|
|
3316
|
+
var SOURCE_TYPE_COLORS = {
|
|
3317
|
+
scheduled: pc10.blue,
|
|
3318
|
+
queue: pc10.green,
|
|
3319
|
+
r2: pc10.yellow,
|
|
3320
|
+
webhook: pc10.magenta,
|
|
3321
|
+
email: pc10.cyan,
|
|
3322
|
+
d1: pc10.red,
|
|
3323
|
+
tail: pc10.gray
|
|
3324
|
+
};
|
|
3325
|
+
async function triggersList(options = {}) {
|
|
3326
|
+
const verbose = options.verbose ?? false;
|
|
3327
|
+
const logger = createLogger(verbose);
|
|
3328
|
+
try {
|
|
3329
|
+
const cwd = process.cwd();
|
|
3330
|
+
logger.debug("Loading configuration...");
|
|
3331
|
+
const config = await loadConfig4(cwd);
|
|
3332
|
+
const appDir = config.appDir;
|
|
3333
|
+
logger.debug(`Scanning for triggers in ${appDir}/triggers/...`);
|
|
3334
|
+
const scanResult = await scanTriggers2(appDir, {
|
|
3335
|
+
extensions: config.extensions
|
|
3336
|
+
});
|
|
3337
|
+
const manifest = buildTriggerManifest2(scanResult, appDir);
|
|
3338
|
+
let triggers2 = manifest.triggers;
|
|
3339
|
+
if (options.type) {
|
|
3340
|
+
triggers2 = triggers2.filter((t) => t.source?.type === options.type);
|
|
3341
|
+
}
|
|
3342
|
+
if (options.json) {
|
|
3343
|
+
console.log(JSON.stringify({
|
|
3344
|
+
triggers: triggers2.map(formatTriggerJson),
|
|
3345
|
+
count: triggers2.length,
|
|
3346
|
+
errors: manifest.errors,
|
|
3347
|
+
warnings: manifest.warnings
|
|
3348
|
+
}, null, 2));
|
|
3349
|
+
return;
|
|
3350
|
+
}
|
|
3351
|
+
console.log();
|
|
3352
|
+
console.log(pc10.bold("Triggers"));
|
|
3353
|
+
console.log();
|
|
3354
|
+
if (triggers2.length === 0) {
|
|
3355
|
+
if (options.type) {
|
|
3356
|
+
console.log(pc10.dim(` No triggers of type '${options.type}' found.`));
|
|
3357
|
+
} else {
|
|
3358
|
+
console.log(pc10.dim(" No triggers found."));
|
|
3359
|
+
}
|
|
3360
|
+
console.log();
|
|
3361
|
+
return;
|
|
3362
|
+
}
|
|
2772
3363
|
console.log(
|
|
2773
|
-
|
|
3364
|
+
` ${pc10.dim("NAME".padEnd(25))} ${pc10.dim("TYPE".padEnd(12))} ${pc10.dim("SOURCE".padEnd(30))} ${pc10.dim("ERROR HANDLER")}`
|
|
2774
3365
|
);
|
|
3366
|
+
console.log(pc10.dim(" " + "-".repeat(80)));
|
|
3367
|
+
for (const trigger of triggers2) {
|
|
3368
|
+
const color = SOURCE_TYPE_COLORS[trigger.source?.type ?? "unknown"] ?? pc10.dim;
|
|
3369
|
+
const sourceType = trigger.source?.type ?? "unknown";
|
|
3370
|
+
const sourceInfo = getSourceInfo2(trigger);
|
|
3371
|
+
const hasError = trigger.hasOnError ? pc10.green("yes") : pc10.dim("no");
|
|
3372
|
+
console.log(
|
|
3373
|
+
` ${pc10.cyan(trigger.name.padEnd(25))} ${color(sourceType.padEnd(12))} ${pc10.dim(sourceInfo.padEnd(30))} ${hasError}`
|
|
3374
|
+
);
|
|
3375
|
+
}
|
|
3376
|
+
console.log();
|
|
3377
|
+
console.log(pc10.dim(` ${triggers2.length} trigger(s)`));
|
|
3378
|
+
console.log();
|
|
3379
|
+
if (manifest.errors.length > 0) {
|
|
3380
|
+
console.log(pc10.red("Errors:"));
|
|
3381
|
+
for (const error of manifest.errors) {
|
|
3382
|
+
console.log(pc10.dim(` - ${error.file}: ${error.message}`));
|
|
3383
|
+
}
|
|
3384
|
+
console.log();
|
|
3385
|
+
}
|
|
3386
|
+
if (manifest.warnings.length > 0) {
|
|
3387
|
+
console.log(pc10.yellow("Warnings:"));
|
|
3388
|
+
for (const warning of manifest.warnings) {
|
|
3389
|
+
console.log(pc10.dim(` - ${warning.file}: ${warning.message}`));
|
|
3390
|
+
}
|
|
3391
|
+
console.log();
|
|
3392
|
+
}
|
|
3393
|
+
} catch (error) {
|
|
3394
|
+
handleCommandError(error, verbose);
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
function getSourceInfo2(trigger) {
|
|
3398
|
+
const source = trigger.source;
|
|
3399
|
+
if (!source) return "unknown";
|
|
3400
|
+
switch (source.type) {
|
|
3401
|
+
case "scheduled":
|
|
3402
|
+
return source.cron;
|
|
3403
|
+
case "queue":
|
|
3404
|
+
return source.queue;
|
|
3405
|
+
case "r2": {
|
|
3406
|
+
const r2 = source;
|
|
3407
|
+
return `${r2.bucket} (${r2.events.join(", ")})`;
|
|
3408
|
+
}
|
|
3409
|
+
case "webhook":
|
|
3410
|
+
return source.path;
|
|
3411
|
+
case "email":
|
|
3412
|
+
return source.address;
|
|
3413
|
+
default:
|
|
3414
|
+
return source.type;
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
function formatTriggerJson(trigger) {
|
|
3418
|
+
return {
|
|
3419
|
+
name: trigger.name,
|
|
3420
|
+
bindingName: trigger.bindingName,
|
|
3421
|
+
filePath: trigger.filePath,
|
|
3422
|
+
sourceType: trigger.source?.type,
|
|
3423
|
+
source: trigger.source,
|
|
3424
|
+
hasOnError: trigger.hasOnError,
|
|
3425
|
+
retry: trigger.retry,
|
|
3426
|
+
timeout: trigger.timeout,
|
|
3427
|
+
fanOutGroup: trigger.fanOutGroup
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
// src/commands/triggers/validate.ts
|
|
3432
|
+
import pc11 from "picocolors";
|
|
3433
|
+
import {
|
|
3434
|
+
loadConfig as loadConfig5,
|
|
3435
|
+
scanTriggers as scanTriggers3,
|
|
3436
|
+
buildTriggerManifest as buildTriggerManifest3,
|
|
3437
|
+
hasTriggerErrors,
|
|
3438
|
+
hasTriggerWarnings
|
|
3439
|
+
} from "@cloudwerk/core/build";
|
|
3440
|
+
async function triggersValidate(options = {}) {
|
|
3441
|
+
const verbose = options.verbose ?? false;
|
|
3442
|
+
const strict = options.strict ?? false;
|
|
3443
|
+
const logger = createLogger(verbose);
|
|
3444
|
+
try {
|
|
3445
|
+
const cwd = process.cwd();
|
|
3446
|
+
logger.debug("Loading configuration...");
|
|
3447
|
+
const config = await loadConfig5(cwd);
|
|
3448
|
+
const appDir = config.appDir;
|
|
3449
|
+
logger.debug(`Scanning for triggers in ${appDir}/triggers/...`);
|
|
3450
|
+
const scanResult = await scanTriggers3(appDir, {
|
|
3451
|
+
extensions: config.extensions
|
|
3452
|
+
});
|
|
3453
|
+
const manifest = buildTriggerManifest3(scanResult, appDir);
|
|
3454
|
+
const hasErrors = hasTriggerErrors(manifest);
|
|
3455
|
+
const hasWarnings = hasTriggerWarnings(manifest);
|
|
3456
|
+
if (options.json) {
|
|
3457
|
+
console.log(JSON.stringify({
|
|
3458
|
+
valid: !hasErrors && (!strict || !hasWarnings),
|
|
3459
|
+
triggerCount: manifest.triggers.length,
|
|
3460
|
+
errors: manifest.errors,
|
|
3461
|
+
warnings: manifest.warnings
|
|
3462
|
+
}, null, 2));
|
|
3463
|
+
if (hasErrors || strict && hasWarnings) {
|
|
3464
|
+
process.exit(1);
|
|
3465
|
+
}
|
|
3466
|
+
return;
|
|
3467
|
+
}
|
|
3468
|
+
console.log();
|
|
3469
|
+
console.log(pc11.bold("Validating Triggers"));
|
|
3470
|
+
console.log();
|
|
3471
|
+
if (manifest.triggers.length === 0) {
|
|
3472
|
+
console.log(pc11.dim(" No triggers found to validate."));
|
|
3473
|
+
console.log();
|
|
3474
|
+
return;
|
|
3475
|
+
}
|
|
3476
|
+
for (const trigger of manifest.triggers) {
|
|
3477
|
+
const triggerErrors = manifest.errors.filter(
|
|
3478
|
+
(e) => e.file === trigger.filePath
|
|
3479
|
+
);
|
|
3480
|
+
const triggerWarnings = manifest.warnings.filter(
|
|
3481
|
+
(w) => w.file === trigger.filePath
|
|
3482
|
+
);
|
|
3483
|
+
if (triggerErrors.length > 0) {
|
|
3484
|
+
console.log(` ${pc11.red("\u2717")} ${pc11.cyan(trigger.name)} ${pc11.dim(`(${trigger.filePath})`)}`);
|
|
3485
|
+
for (const error of triggerErrors) {
|
|
3486
|
+
console.log(` ${pc11.red("error:")} ${error.message}`);
|
|
3487
|
+
}
|
|
3488
|
+
} else if (triggerWarnings.length > 0) {
|
|
3489
|
+
console.log(` ${pc11.yellow("!")} ${pc11.cyan(trigger.name)} ${pc11.dim(`(${trigger.filePath})`)}`);
|
|
3490
|
+
for (const warning of triggerWarnings) {
|
|
3491
|
+
console.log(` ${pc11.yellow("warning:")} ${warning.message}`);
|
|
3492
|
+
}
|
|
3493
|
+
} else {
|
|
3494
|
+
console.log(` ${pc11.green("\u2713")} ${pc11.cyan(trigger.name)} ${pc11.dim("- valid")}`);
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
console.log();
|
|
3498
|
+
const errorCount = manifest.errors.length;
|
|
3499
|
+
const warningCount = manifest.warnings.length;
|
|
3500
|
+
if (hasErrors) {
|
|
3501
|
+
console.log(pc11.red(` ${errorCount} error(s), ${warningCount} warning(s)`));
|
|
3502
|
+
console.log();
|
|
3503
|
+
process.exit(1);
|
|
3504
|
+
} else if (hasWarnings) {
|
|
3505
|
+
console.log(pc11.yellow(` ${warningCount} warning(s)`));
|
|
3506
|
+
console.log();
|
|
3507
|
+
if (strict) {
|
|
3508
|
+
process.exit(1);
|
|
3509
|
+
}
|
|
3510
|
+
} else {
|
|
3511
|
+
console.log(pc11.green(` All ${manifest.triggers.length} trigger(s) valid`));
|
|
3512
|
+
console.log();
|
|
3513
|
+
}
|
|
3514
|
+
} catch (error) {
|
|
3515
|
+
handleCommandError(error, verbose);
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3519
|
+
// src/commands/triggers/generate.ts
|
|
3520
|
+
import pc12 from "picocolors";
|
|
3521
|
+
import {
|
|
3522
|
+
loadConfig as loadConfig6,
|
|
3523
|
+
scanTriggers as scanTriggers4,
|
|
3524
|
+
buildTriggerManifest as buildTriggerManifest4
|
|
3525
|
+
} from "@cloudwerk/core/build";
|
|
3526
|
+
|
|
3527
|
+
// src/utils/trigger-wrangler.ts
|
|
3528
|
+
import * as fs11 from "fs";
|
|
3529
|
+
import * as path12 from "path";
|
|
3530
|
+
function generateCronToml(manifest, includeComments) {
|
|
3531
|
+
const cronTriggers = manifest.triggers.filter(
|
|
3532
|
+
(t) => t.source?.type === "scheduled"
|
|
3533
|
+
);
|
|
3534
|
+
if (cronTriggers.length === 0) {
|
|
3535
|
+
return "";
|
|
3536
|
+
}
|
|
3537
|
+
const lines = [];
|
|
3538
|
+
if (includeComments) {
|
|
3539
|
+
lines.push("# Scheduled (Cron) Triggers");
|
|
3540
|
+
}
|
|
3541
|
+
lines.push("[triggers]");
|
|
3542
|
+
const cronExpressions = /* @__PURE__ */ new Set();
|
|
3543
|
+
for (const trigger of cronTriggers) {
|
|
3544
|
+
const source = trigger.source;
|
|
3545
|
+
cronExpressions.add(source.cron);
|
|
3546
|
+
}
|
|
3547
|
+
const crons = Array.from(cronExpressions).map((cron) => `"${cron}"`).join(", ");
|
|
3548
|
+
lines.push(`crons = [${crons}]`);
|
|
3549
|
+
return lines.join("\n");
|
|
3550
|
+
}
|
|
3551
|
+
function generateQueueConsumerToml(manifest, includeComments) {
|
|
3552
|
+
const queueTriggers = manifest.triggers.filter(
|
|
3553
|
+
(t) => t.source?.type === "queue"
|
|
3554
|
+
);
|
|
3555
|
+
if (queueTriggers.length === 0) {
|
|
3556
|
+
return "";
|
|
3557
|
+
}
|
|
3558
|
+
const lines = [];
|
|
3559
|
+
for (const trigger of queueTriggers) {
|
|
3560
|
+
const source = trigger.source;
|
|
3561
|
+
if (includeComments) {
|
|
3562
|
+
lines.push(`# Queue trigger '${trigger.name}' (from app/triggers/${trigger.filePath})`);
|
|
3563
|
+
}
|
|
3564
|
+
lines.push("[[queues.consumers]]");
|
|
3565
|
+
lines.push(`queue = "${source.queue}"`);
|
|
3566
|
+
if (source.batch?.size !== void 0 && source.batch.size !== 10) {
|
|
3567
|
+
lines.push(`max_batch_size = ${source.batch.size}`);
|
|
3568
|
+
}
|
|
3569
|
+
if (source.batch?.timeout !== void 0) {
|
|
3570
|
+
const timeoutSeconds = parseDurationToSeconds(source.batch.timeout);
|
|
3571
|
+
if (timeoutSeconds !== 5) {
|
|
3572
|
+
lines.push(`max_batch_timeout = ${timeoutSeconds}`);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
lines.push("");
|
|
3576
|
+
}
|
|
3577
|
+
return lines.join("\n").trim();
|
|
3578
|
+
}
|
|
3579
|
+
function generateR2NotificationToml(manifest, includeComments) {
|
|
3580
|
+
const r2Triggers = manifest.triggers.filter(
|
|
3581
|
+
(t) => t.source?.type === "r2"
|
|
3582
|
+
);
|
|
3583
|
+
if (r2Triggers.length === 0) {
|
|
3584
|
+
return "";
|
|
3585
|
+
}
|
|
3586
|
+
const lines = [];
|
|
3587
|
+
const bucketTriggers = /* @__PURE__ */ new Map();
|
|
3588
|
+
for (const trigger of r2Triggers) {
|
|
3589
|
+
const source = trigger.source;
|
|
3590
|
+
const existing = bucketTriggers.get(source.bucket) || [];
|
|
3591
|
+
existing.push(trigger);
|
|
3592
|
+
bucketTriggers.set(source.bucket, existing);
|
|
3593
|
+
}
|
|
3594
|
+
for (const [bucket, triggers2] of bucketTriggers) {
|
|
3595
|
+
if (includeComments) {
|
|
3596
|
+
const triggerNames = triggers2.map((t) => t.name).join(", ");
|
|
3597
|
+
lines.push(`# R2 bucket notifications for '${bucket}' (triggers: ${triggerNames})`);
|
|
3598
|
+
}
|
|
3599
|
+
const eventTypes = /* @__PURE__ */ new Set();
|
|
3600
|
+
let prefix;
|
|
3601
|
+
let suffix;
|
|
3602
|
+
for (const trigger of triggers2) {
|
|
3603
|
+
const source = trigger.source;
|
|
3604
|
+
for (const event of source.events) {
|
|
3605
|
+
eventTypes.add(event);
|
|
3606
|
+
}
|
|
3607
|
+
if (!prefix && source.prefix) prefix = source.prefix;
|
|
3608
|
+
if (!suffix && source.suffix) suffix = source.suffix;
|
|
3609
|
+
}
|
|
3610
|
+
lines.push("[[r2_buckets]]");
|
|
3611
|
+
lines.push(`binding = "${bucket.toUpperCase()}_BUCKET"`);
|
|
3612
|
+
lines.push(`bucket_name = "${bucket}"`);
|
|
3613
|
+
lines.push("");
|
|
3614
|
+
lines.push("[[r2_buckets.event_notifications.rules]]");
|
|
3615
|
+
if (prefix) {
|
|
3616
|
+
lines.push(`prefix = "${prefix}"`);
|
|
3617
|
+
}
|
|
3618
|
+
if (suffix) {
|
|
3619
|
+
lines.push(`suffix = "${suffix}"`);
|
|
3620
|
+
}
|
|
3621
|
+
const actions = [];
|
|
3622
|
+
if (eventTypes.has("object-create")) {
|
|
3623
|
+
actions.push('"PutObject"', '"CopyObject"', '"CompleteMultipartUpload"');
|
|
3624
|
+
}
|
|
3625
|
+
if (eventTypes.has("object-delete")) {
|
|
3626
|
+
actions.push('"DeleteObject"');
|
|
3627
|
+
}
|
|
3628
|
+
if (actions.length > 0) {
|
|
3629
|
+
lines.push(`actions = [${actions.join(", ")}]`);
|
|
3630
|
+
}
|
|
3631
|
+
lines.push("");
|
|
3632
|
+
}
|
|
3633
|
+
return lines.join("\n").trim();
|
|
3634
|
+
}
|
|
3635
|
+
function parseDurationToSeconds(duration) {
|
|
3636
|
+
if (typeof duration === "number") {
|
|
3637
|
+
return duration;
|
|
3638
|
+
}
|
|
3639
|
+
const match = duration.match(/^(\d+)(s|m|h)$/);
|
|
3640
|
+
if (!match) {
|
|
3641
|
+
return 5;
|
|
3642
|
+
}
|
|
3643
|
+
const value = parseInt(match[1], 10);
|
|
3644
|
+
const unit = match[2];
|
|
3645
|
+
switch (unit) {
|
|
3646
|
+
case "s":
|
|
3647
|
+
return value;
|
|
3648
|
+
case "m":
|
|
3649
|
+
return value * 60;
|
|
3650
|
+
case "h":
|
|
3651
|
+
return value * 3600;
|
|
3652
|
+
default:
|
|
3653
|
+
return 5;
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
function generateTriggerToml(manifest, includeComments = true) {
|
|
3657
|
+
if (manifest.triggers.length === 0) {
|
|
3658
|
+
return "";
|
|
3659
|
+
}
|
|
3660
|
+
const lines = [];
|
|
3661
|
+
if (includeComments) {
|
|
3662
|
+
lines.push("# ============================================================================");
|
|
3663
|
+
lines.push("# Cloudwerk Triggers - Auto-generated from app/triggers/");
|
|
3664
|
+
lines.push("# ============================================================================");
|
|
3665
|
+
lines.push("");
|
|
3666
|
+
}
|
|
3667
|
+
const cronToml = generateCronToml(manifest, includeComments);
|
|
3668
|
+
if (cronToml) {
|
|
3669
|
+
lines.push(cronToml);
|
|
3670
|
+
lines.push("");
|
|
3671
|
+
}
|
|
3672
|
+
const queueToml = generateQueueConsumerToml(manifest, includeComments);
|
|
3673
|
+
if (queueToml) {
|
|
3674
|
+
lines.push(queueToml);
|
|
3675
|
+
lines.push("");
|
|
3676
|
+
}
|
|
3677
|
+
const r2Toml = generateR2NotificationToml(manifest, includeComments);
|
|
3678
|
+
if (r2Toml) {
|
|
3679
|
+
lines.push(r2Toml);
|
|
3680
|
+
lines.push("");
|
|
3681
|
+
}
|
|
3682
|
+
return lines.join("\n").trim();
|
|
3683
|
+
}
|
|
3684
|
+
var TRIGGER_SECTION_START = "# ============================================================================";
|
|
3685
|
+
var TRIGGER_SECTION_MARKER = "# Cloudwerk Triggers - Auto-generated";
|
|
3686
|
+
function hasTriggerSection(content) {
|
|
3687
|
+
return content.includes(TRIGGER_SECTION_MARKER);
|
|
3688
|
+
}
|
|
3689
|
+
function removeTriggerSection(content) {
|
|
3690
|
+
const startIndex = content.indexOf(TRIGGER_SECTION_START);
|
|
3691
|
+
if (startIndex === -1 || !content.includes(TRIGGER_SECTION_MARKER)) {
|
|
3692
|
+
return content;
|
|
3693
|
+
}
|
|
3694
|
+
const lines = content.split("\n");
|
|
3695
|
+
let sectionStartLine = -1;
|
|
3696
|
+
let sectionEndLine = lines.length;
|
|
3697
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3698
|
+
if (lines[i].includes(TRIGGER_SECTION_MARKER)) {
|
|
3699
|
+
sectionStartLine = i > 0 && lines[i - 1].includes("===") ? i - 1 : i;
|
|
3700
|
+
break;
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
if (sectionStartLine === -1) {
|
|
3704
|
+
return content;
|
|
3705
|
+
}
|
|
3706
|
+
for (let i = sectionStartLine + 2; i < lines.length; i++) {
|
|
3707
|
+
const line = lines[i].trim();
|
|
3708
|
+
if (line === "" || line.startsWith("#")) {
|
|
3709
|
+
continue;
|
|
3710
|
+
}
|
|
3711
|
+
if (line.startsWith("[[") || line.startsWith("[")) {
|
|
3712
|
+
if (line.includes("triggers") || line.includes("queues.consumers") || line.includes("r2_buckets")) {
|
|
3713
|
+
continue;
|
|
3714
|
+
}
|
|
3715
|
+
sectionEndLine = i;
|
|
3716
|
+
break;
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
const before = lines.slice(0, sectionStartLine);
|
|
3720
|
+
const after = lines.slice(sectionEndLine);
|
|
3721
|
+
while (before.length > 0 && before[before.length - 1].trim() === "") {
|
|
3722
|
+
before.pop();
|
|
3723
|
+
}
|
|
3724
|
+
return [...before, "", ...after].join("\n");
|
|
3725
|
+
}
|
|
3726
|
+
function generateTriggerWrangler(cwd, manifest, options = {}) {
|
|
3727
|
+
const { dryRun = false, includeComments = true } = options;
|
|
3728
|
+
const wranglerPath = findWranglerToml(cwd) || path12.join(cwd, "wrangler.toml");
|
|
3729
|
+
const generatedToml = generateTriggerToml(manifest, includeComments);
|
|
3730
|
+
const result = {
|
|
3731
|
+
wranglerPath,
|
|
3732
|
+
changed: false,
|
|
3733
|
+
triggers: manifest.triggers.map((t) => ({
|
|
3734
|
+
name: t.name,
|
|
3735
|
+
sourceType: t.source?.type ?? "unknown",
|
|
3736
|
+
bindingName: t.bindingName
|
|
3737
|
+
})),
|
|
3738
|
+
generatedToml
|
|
3739
|
+
};
|
|
3740
|
+
if (manifest.triggers.length === 0) {
|
|
3741
|
+
return result;
|
|
3742
|
+
}
|
|
3743
|
+
if (dryRun) {
|
|
3744
|
+
result.changed = true;
|
|
3745
|
+
return result;
|
|
3746
|
+
}
|
|
3747
|
+
let content = "";
|
|
3748
|
+
if (fs11.existsSync(wranglerPath)) {
|
|
3749
|
+
content = readWranglerTomlRaw(cwd);
|
|
3750
|
+
}
|
|
3751
|
+
if (hasTriggerSection(content)) {
|
|
3752
|
+
content = removeTriggerSection(content);
|
|
3753
|
+
}
|
|
3754
|
+
const newContent = content.trim() + "\n\n" + generatedToml + "\n";
|
|
3755
|
+
writeWranglerTomlRaw(cwd, newContent);
|
|
3756
|
+
result.changed = true;
|
|
3757
|
+
return result;
|
|
3758
|
+
}
|
|
3759
|
+
|
|
3760
|
+
// src/utils/trigger-type-generator.ts
|
|
3761
|
+
import * as fs12 from "fs";
|
|
3762
|
+
import * as path13 from "path";
|
|
3763
|
+
var CLOUDWERK_TYPES_DIR2 = ".cloudwerk/types";
|
|
3764
|
+
var TRIGGERS_DTS = "triggers.d.ts";
|
|
3765
|
+
function generateTriggerTypes(cwd, manifest, options = {}) {
|
|
3766
|
+
const includeTimestamp = options.includeTimestamp ?? true;
|
|
3767
|
+
const typesDir = path13.join(cwd, CLOUDWERK_TYPES_DIR2);
|
|
3768
|
+
fs12.mkdirSync(typesDir, { recursive: true });
|
|
3769
|
+
const triggersPath = path13.join(typesDir, TRIGGERS_DTS);
|
|
3770
|
+
const triggersContent = generateTriggersDts(manifest.triggers, includeTimestamp);
|
|
3771
|
+
fs12.writeFileSync(triggersPath, triggersContent, "utf-8");
|
|
3772
|
+
const triggerInfo = manifest.triggers.map((t) => ({
|
|
3773
|
+
name: t.name,
|
|
3774
|
+
bindingName: t.bindingName,
|
|
3775
|
+
sourceType: t.source?.type ?? "unknown"
|
|
3776
|
+
}));
|
|
3777
|
+
return {
|
|
3778
|
+
typesDir,
|
|
3779
|
+
file: triggersPath,
|
|
3780
|
+
triggerCount: manifest.triggers.length,
|
|
3781
|
+
triggers: triggerInfo
|
|
3782
|
+
};
|
|
3783
|
+
}
|
|
3784
|
+
function generateTriggersDts(triggers2, includeTimestamp) {
|
|
3785
|
+
const lines = [];
|
|
3786
|
+
lines.push("// Auto-generated by cloudwerk triggers - DO NOT EDIT");
|
|
3787
|
+
if (includeTimestamp) {
|
|
3788
|
+
lines.push(`// Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
3789
|
+
}
|
|
3790
|
+
lines.push("//");
|
|
3791
|
+
lines.push("// This file provides type information for triggers");
|
|
3792
|
+
lines.push('// Add ".cloudwerk/types" to your tsconfig.json include array');
|
|
3793
|
+
lines.push("");
|
|
3794
|
+
lines.push("import type { EmitOptions, EmitResult } from '@cloudwerk/trigger'");
|
|
3795
|
+
lines.push("");
|
|
3796
|
+
const scheduled = triggers2.filter((t) => t.source?.type === "scheduled");
|
|
3797
|
+
const queue = triggers2.filter((t) => t.source?.type === "queue");
|
|
3798
|
+
const r2 = triggers2.filter((t) => t.source?.type === "r2");
|
|
3799
|
+
const webhook = triggers2.filter((t) => t.source?.type === "webhook");
|
|
3800
|
+
const email = triggers2.filter((t) => t.source?.type === "email");
|
|
3801
|
+
lines.push("// ============================================================================");
|
|
3802
|
+
lines.push("// Trigger Names");
|
|
3803
|
+
lines.push("// ============================================================================");
|
|
3804
|
+
lines.push("");
|
|
3805
|
+
const allNames = triggers2.map((t) => `'${t.name}'`);
|
|
3806
|
+
if (allNames.length > 0) {
|
|
3807
|
+
lines.push(`type TriggerName = ${allNames.join(" | ")}`);
|
|
3808
|
+
} else {
|
|
3809
|
+
lines.push("type TriggerName = never");
|
|
3810
|
+
}
|
|
3811
|
+
lines.push("");
|
|
3812
|
+
if (scheduled.length > 0) {
|
|
3813
|
+
const names = scheduled.map((t) => `'${t.name}'`);
|
|
3814
|
+
lines.push(`type ScheduledTriggerName = ${names.join(" | ")}`);
|
|
3815
|
+
}
|
|
3816
|
+
if (queue.length > 0) {
|
|
3817
|
+
const names = queue.map((t) => `'${t.name}'`);
|
|
3818
|
+
lines.push(`type QueueTriggerName = ${names.join(" | ")}`);
|
|
3819
|
+
}
|
|
3820
|
+
if (r2.length > 0) {
|
|
3821
|
+
const names = r2.map((t) => `'${t.name}'`);
|
|
3822
|
+
lines.push(`type R2TriggerName = ${names.join(" | ")}`);
|
|
3823
|
+
}
|
|
3824
|
+
if (webhook.length > 0) {
|
|
3825
|
+
const names = webhook.map((t) => `'${t.name}'`);
|
|
3826
|
+
lines.push(`type WebhookTriggerName = ${names.join(" | ")}`);
|
|
3827
|
+
}
|
|
3828
|
+
if (email.length > 0) {
|
|
3829
|
+
const names = email.map((t) => `'${t.name}'`);
|
|
3830
|
+
lines.push(`type EmailTriggerName = ${names.join(" | ")}`);
|
|
3831
|
+
}
|
|
3832
|
+
lines.push("");
|
|
3833
|
+
lines.push("// ============================================================================");
|
|
3834
|
+
lines.push("// Trigger Registry");
|
|
3835
|
+
lines.push("// ============================================================================");
|
|
3836
|
+
lines.push("");
|
|
3837
|
+
lines.push("interface TriggerRegistry {");
|
|
3838
|
+
for (const trigger of triggers2) {
|
|
3839
|
+
const sourceType = trigger.source?.type ?? "unknown";
|
|
3840
|
+
lines.push(` /** ${sourceType} trigger: ${trigger.name} */`);
|
|
3841
|
+
lines.push(` '${trigger.name}': {`);
|
|
3842
|
+
lines.push(` name: '${trigger.name}'`);
|
|
3843
|
+
lines.push(` bindingName: '${trigger.bindingName}'`);
|
|
3844
|
+
lines.push(` sourceType: '${sourceType}'`);
|
|
3845
|
+
lines.push(" }");
|
|
3846
|
+
}
|
|
3847
|
+
lines.push("}");
|
|
3848
|
+
lines.push("");
|
|
3849
|
+
lines.push("// ============================================================================");
|
|
3850
|
+
lines.push("// Module Augmentation");
|
|
3851
|
+
lines.push("// ============================================================================");
|
|
3852
|
+
lines.push("");
|
|
3853
|
+
lines.push("declare module '@cloudwerk/trigger' {");
|
|
3854
|
+
lines.push(" /**");
|
|
3855
|
+
lines.push(" * Type-safe emit to other triggers.");
|
|
3856
|
+
lines.push(" * Only triggers defined in app/triggers/ are valid targets.");
|
|
3857
|
+
lines.push(" */");
|
|
3858
|
+
lines.push(" export function emit<T extends TriggerName>(");
|
|
3859
|
+
lines.push(" trigger: T,");
|
|
3860
|
+
lines.push(" payload: unknown,");
|
|
3861
|
+
lines.push(" options?: EmitOptions");
|
|
3862
|
+
lines.push(" ): Promise<EmitResult>");
|
|
3863
|
+
lines.push("");
|
|
3864
|
+
lines.push(" /** Get list of all trigger names */");
|
|
3865
|
+
lines.push(" export function getTriggerNames(): TriggerName[]");
|
|
3866
|
+
lines.push("");
|
|
3867
|
+
lines.push(" /** Check if a trigger exists */");
|
|
3868
|
+
lines.push(" export function triggerExists(name: string): name is TriggerName");
|
|
3869
|
+
lines.push("}");
|
|
3870
|
+
lines.push("");
|
|
3871
|
+
return lines.join("\n");
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
// src/commands/triggers/generate.ts
|
|
3875
|
+
async function triggersGenerate(options = {}) {
|
|
3876
|
+
const verbose = options.verbose ?? false;
|
|
3877
|
+
const dryRun = options.dryRun ?? false;
|
|
3878
|
+
const logger = createLogger(verbose);
|
|
3879
|
+
const generateWrangler = options.wrangler ?? !options.types;
|
|
3880
|
+
const generateTypes = options.types ?? !options.wrangler;
|
|
3881
|
+
try {
|
|
3882
|
+
const cwd = process.cwd();
|
|
3883
|
+
logger.debug("Loading configuration...");
|
|
3884
|
+
const config = await loadConfig6(cwd);
|
|
3885
|
+
const appDir = config.appDir;
|
|
3886
|
+
logger.debug(`Scanning for triggers in ${appDir}/triggers/...`);
|
|
3887
|
+
const scanResult = await scanTriggers4(appDir, {
|
|
3888
|
+
extensions: config.extensions
|
|
3889
|
+
});
|
|
3890
|
+
const manifest = buildTriggerManifest4(scanResult, appDir);
|
|
3891
|
+
if (options.json) {
|
|
3892
|
+
const result = {
|
|
3893
|
+
triggerCount: manifest.triggers.length,
|
|
3894
|
+
dryRun
|
|
3895
|
+
};
|
|
3896
|
+
if (generateWrangler) {
|
|
3897
|
+
const wranglerResult = generateTriggerWrangler(cwd, manifest, { dryRun });
|
|
3898
|
+
result.wrangler = {
|
|
3899
|
+
path: wranglerResult.wranglerPath,
|
|
3900
|
+
changed: wranglerResult.changed,
|
|
3901
|
+
triggers: wranglerResult.triggers,
|
|
3902
|
+
generatedToml: dryRun ? wranglerResult.generatedToml : void 0
|
|
3903
|
+
};
|
|
3904
|
+
}
|
|
3905
|
+
if (generateTypes) {
|
|
3906
|
+
const typesResult = generateTriggerTypes(cwd, manifest, { includeTimestamp: !dryRun });
|
|
3907
|
+
result.types = {
|
|
3908
|
+
path: typesResult.file,
|
|
3909
|
+
triggerCount: typesResult.triggerCount,
|
|
3910
|
+
triggers: typesResult.triggers
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3914
|
+
return;
|
|
3915
|
+
}
|
|
3916
|
+
console.log();
|
|
3917
|
+
console.log(pc12.bold("Generating Trigger Configuration"));
|
|
3918
|
+
console.log();
|
|
3919
|
+
if (manifest.triggers.length === 0) {
|
|
3920
|
+
console.log(pc12.dim(" No triggers found. Nothing to generate."));
|
|
3921
|
+
console.log();
|
|
3922
|
+
return;
|
|
3923
|
+
}
|
|
3924
|
+
console.log(pc12.dim(` Found ${manifest.triggers.length} trigger(s)`));
|
|
3925
|
+
console.log();
|
|
3926
|
+
if (generateWrangler) {
|
|
3927
|
+
const wranglerResult = generateTriggerWrangler(cwd, manifest, {
|
|
3928
|
+
dryRun,
|
|
3929
|
+
includeComments: true
|
|
3930
|
+
});
|
|
3931
|
+
if (dryRun) {
|
|
3932
|
+
console.log(pc12.cyan(" wrangler.toml (dry run):"));
|
|
3933
|
+
console.log();
|
|
3934
|
+
const lines = wranglerResult.generatedToml.split("\n");
|
|
3935
|
+
for (const line of lines) {
|
|
3936
|
+
console.log(` ${line}`);
|
|
3937
|
+
}
|
|
3938
|
+
console.log();
|
|
3939
|
+
} else {
|
|
3940
|
+
console.log(` ${pc12.green("\u2713")} Updated ${pc12.dim(wranglerResult.wranglerPath)}`);
|
|
3941
|
+
for (const trigger of wranglerResult.triggers) {
|
|
3942
|
+
console.log(pc12.dim(` - ${trigger.name} (${trigger.sourceType})`));
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
if (generateTypes) {
|
|
3947
|
+
if (!dryRun) {
|
|
3948
|
+
const typesResult = generateTriggerTypes(cwd, manifest, { includeTimestamp: true });
|
|
3949
|
+
console.log(` ${pc12.green("\u2713")} Generated ${pc12.dim(typesResult.file)}`);
|
|
3950
|
+
console.log(pc12.dim(` - ${typesResult.triggerCount} trigger type(s)`));
|
|
3951
|
+
} else {
|
|
3952
|
+
console.log(pc12.cyan(" TypeScript types (dry run):"));
|
|
3953
|
+
console.log(pc12.dim(` Would generate .cloudwerk/types/triggers.d.ts`));
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
2775
3956
|
console.log();
|
|
3957
|
+
if (!dryRun && generateTypes) {
|
|
3958
|
+
console.log(pc12.dim(' Tip: Add ".cloudwerk/types" to your tsconfig.json include array'));
|
|
3959
|
+
console.log();
|
|
3960
|
+
}
|
|
3961
|
+
if (manifest.errors.length > 0) {
|
|
3962
|
+
console.log(pc12.red("Errors:"));
|
|
3963
|
+
for (const error of manifest.errors) {
|
|
3964
|
+
console.log(pc12.dim(` - ${error.file}: ${error.message}`));
|
|
3965
|
+
}
|
|
3966
|
+
console.log();
|
|
3967
|
+
}
|
|
3968
|
+
if (manifest.warnings.length > 0) {
|
|
3969
|
+
console.log(pc12.yellow("Warnings:"));
|
|
3970
|
+
for (const warning of manifest.warnings) {
|
|
3971
|
+
console.log(pc12.dim(` - ${warning.file}: ${warning.message}`));
|
|
3972
|
+
}
|
|
3973
|
+
console.log();
|
|
3974
|
+
}
|
|
2776
3975
|
} catch (error) {
|
|
2777
3976
|
handleCommandError(error, verbose);
|
|
2778
3977
|
}
|
|
2779
3978
|
}
|
|
2780
3979
|
|
|
2781
3980
|
// src/index.ts
|
|
2782
|
-
program.name("cloudwerk").description("Cloudwerk CLI - Build and deploy full-stack apps to Cloudflare").version(VERSION);
|
|
3981
|
+
program.name("cloudwerk").description("Cloudwerk CLI - Build and deploy full-stack apps to Cloudflare").version(VERSION).enablePositionalOptions();
|
|
2783
3982
|
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);
|
|
2784
3983
|
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
3984
|
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);
|
|
2786
3985
|
var configCmd = program.command("config").description("Manage Cloudwerk configuration");
|
|
2787
3986
|
configCmd.command("get <key>").description("Get a configuration value").action(configGet);
|
|
2788
3987
|
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.)").
|
|
3988
|
+
var bindingsCmd = program.command("bindings").description("Manage Cloudflare bindings (D1, KV, R2, Queues, etc.)").enablePositionalOptions().passThroughOptions().option("--verbose", "Enable verbose logging").action(bindings);
|
|
2790
3989
|
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
3990
|
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
3991
|
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
3992
|
bindingsCmd.command("generate-types").description("Regenerate TypeScript type definitions").option("--verbose", "Enable verbose logging").action(bindingsGenerateTypes);
|
|
3993
|
+
var triggersCmd = program.command("triggers").description("Manage Cloudwerk triggers (scheduled, queue, R2, webhook, etc.)").enablePositionalOptions().passThroughOptions().option("--verbose", "Enable verbose logging").action(triggers);
|
|
3994
|
+
triggersCmd.command("list").description("List all triggers with details").option("-t, --type <type>", "Filter by source type (scheduled, queue, r2, webhook, email)").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(triggersList);
|
|
3995
|
+
triggersCmd.command("validate").description("Validate trigger configurations").option("-s, --strict", "Exit with error code if warnings are present").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(triggersValidate);
|
|
3996
|
+
triggersCmd.command("generate").description("Regenerate wrangler.toml and TypeScript types").option("--wrangler", "Only generate wrangler.toml config").option("--types", "Only generate TypeScript types").option("--dry-run", "Show what would be generated without writing").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(triggersGenerate);
|
|
2794
3997
|
program.parse();
|