@eventcatalog/cli 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1253,8 +1253,10 @@ var import_node_fs5 = __toESM(require("fs"));
1253
1253
  var import_node_path4 = __toESM(require("path"));
1254
1254
  var import_js_yaml = __toESM(require("js-yaml"));
1255
1255
  var loadGovernanceConfig = (catalogDir) => {
1256
- const configPath = import_node_path4.default.join(catalogDir, "governance.yaml");
1257
- if (!import_node_fs5.default.existsSync(configPath)) {
1256
+ const yamlPath = import_node_path4.default.join(catalogDir, "governance.yaml");
1257
+ const ymlPath = import_node_path4.default.join(catalogDir, "governance.yml");
1258
+ const configPath = import_node_fs5.default.existsSync(yamlPath) ? yamlPath : import_node_fs5.default.existsSync(ymlPath) ? ymlPath : null;
1259
+ if (!configPath) {
1258
1260
  return { rules: [] };
1259
1261
  }
1260
1262
  const content = import_node_fs5.default.readFileSync(configPath, "utf-8");
@@ -1285,17 +1287,82 @@ var buildServiceMessageSets = (snapshot2) => {
1285
1287
  }
1286
1288
  return { produces, consumes };
1287
1289
  };
1288
- var matchesResource = (change, resources, messageSets) => {
1290
+ var matchesResourceId = (resourceId, serviceId, resources, messageSets) => {
1289
1291
  return resources.some((r) => {
1290
1292
  if (r === "*") return true;
1291
- if (r.startsWith("service:")) return change.serviceId === r.slice(8);
1292
- if (r.startsWith("message:")) return change.resourceId === r.slice(8);
1293
- if (r.startsWith("produces:")) return messageSets?.produces.get(r.slice(9))?.has(change.resourceId) ?? false;
1294
- if (r.startsWith("consumes:")) return messageSets?.consumes.get(r.slice(9))?.has(change.resourceId) ?? false;
1293
+ if (r.startsWith("service:")) {
1294
+ if (serviceId) return serviceId === r.slice(8);
1295
+ return messageSets?.produces.get(r.slice(8))?.has(resourceId) ?? false;
1296
+ }
1297
+ if (r.startsWith("message:")) return resourceId === r.slice(8);
1298
+ if (r.startsWith("produces:")) return messageSets?.produces.get(r.slice(9))?.has(resourceId) ?? false;
1299
+ if (r.startsWith("consumes:")) return messageSets?.consumes.get(r.slice(9))?.has(resourceId) ?? false;
1295
1300
  return false;
1296
1301
  });
1297
1302
  };
1298
1303
  var REMOVED_TRIGGERS = /* @__PURE__ */ new Set(["consumer_removed", "producer_removed"]);
1304
+ var MESSAGE_RESOURCE_TYPES = /* @__PURE__ */ new Set(["event", "command", "query"]);
1305
+ var buildMessageMap = (snapshot2) => {
1306
+ const map = /* @__PURE__ */ new Map();
1307
+ for (const msg of snapshot2.resources.messages.events) map.set(msg.id, msg);
1308
+ for (const msg of snapshot2.resources.messages.commands) map.set(msg.id, msg);
1309
+ for (const msg of snapshot2.resources.messages.queries) map.set(msg.id, msg);
1310
+ return map;
1311
+ };
1312
+ var buildProducerIndex = (snapshot2) => {
1313
+ const index = /* @__PURE__ */ new Map();
1314
+ for (const service of snapshot2.resources.services) {
1315
+ if (!service.sends) continue;
1316
+ for (const s of service.sends) {
1317
+ let producers = index.get(s.id);
1318
+ if (!producers) {
1319
+ producers = [];
1320
+ index.set(s.id, producers);
1321
+ }
1322
+ const entry = {
1323
+ id: service.id,
1324
+ version: service.version
1325
+ };
1326
+ if (service.owners && Array.isArray(service.owners) && service.owners.length > 0) {
1327
+ entry.owners = service.owners;
1328
+ }
1329
+ producers.push(entry);
1330
+ }
1331
+ }
1332
+ return index;
1333
+ };
1334
+ var evaluateDeprecationRules = (diff, config, targetSnapshot, targetMessageSets, baseSnapshot) => {
1335
+ const deprecationRules = config.rules.filter((rule) => rule.when.includes("message_deprecated"));
1336
+ if (deprecationRules.length === 0) return [];
1337
+ const targetMessages = buildMessageMap(targetSnapshot);
1338
+ const baseMessages = baseSnapshot ? buildMessageMap(baseSnapshot) : void 0;
1339
+ const producerIndex = buildProducerIndex(targetSnapshot);
1340
+ const deprecatedResources = diff.resources.filter((rc) => {
1341
+ if (!MESSAGE_RESOURCE_TYPES.has(rc.type)) return false;
1342
+ if (!rc.changedFields?.includes("deprecated")) return false;
1343
+ const targetMessage = targetMessages.get(rc.resourceId);
1344
+ if (!targetMessage || !targetMessage.deprecated) return false;
1345
+ if (baseMessages) {
1346
+ const baseMessage = baseMessages.get(rc.resourceId);
1347
+ if (baseMessage && baseMessage.deprecated) return false;
1348
+ }
1349
+ return true;
1350
+ });
1351
+ if (deprecatedResources.length === 0) return [];
1352
+ const results = [];
1353
+ for (const rule of deprecationRules) {
1354
+ const matched = [];
1355
+ for (const rc of deprecatedResources) {
1356
+ if (!matchesResourceId(rc.resourceId, void 0, rule.resources, targetMessageSets)) continue;
1357
+ const producers = producerIndex.get(rc.resourceId) || [];
1358
+ matched.push({ resourceChange: rc, producerServices: producers });
1359
+ }
1360
+ if (matched.length > 0) {
1361
+ results.push({ rule, trigger: "message_deprecated", matchedChanges: [], deprecationChanges: matched });
1362
+ }
1363
+ }
1364
+ return results;
1365
+ };
1299
1366
  var evaluateGovernanceRules = (diff, config, targetSnapshot, baseSnapshot) => {
1300
1367
  const results = [];
1301
1368
  const targetMessageSets = targetSnapshot ? buildServiceMessageSets(targetSnapshot) : void 0;
@@ -1305,12 +1372,17 @@ var evaluateGovernanceRules = (diff, config, targetSnapshot, baseSnapshot) => {
1305
1372
  const filter = TRIGGER_FILTERS[trigger];
1306
1373
  if (!filter) continue;
1307
1374
  const messageSets = REMOVED_TRIGGERS.has(trigger) && baseMessageSets ? baseMessageSets : targetMessageSets;
1308
- const matchedChanges = diff.relationships.filter((c2) => filter(c2) && matchesResource(c2, rule.resources, messageSets));
1375
+ const matchedChanges = diff.relationships.filter(
1376
+ (c2) => filter(c2) && matchesResourceId(c2.resourceId, c2.serviceId, rule.resources, messageSets)
1377
+ );
1309
1378
  if (matchedChanges.length > 0) {
1310
1379
  results.push({ rule, trigger, matchedChanges });
1311
1380
  }
1312
1381
  }
1313
1382
  }
1383
+ if (targetSnapshot && targetMessageSets) {
1384
+ results.push(...evaluateDeprecationRules(diff, config, targetSnapshot, targetMessageSets, baseSnapshot));
1385
+ }
1314
1386
  return results;
1315
1387
  };
1316
1388
  var PRODUCER_TRIGGERS = /* @__PURE__ */ new Set(["producer_added", "producer_removed"]);
@@ -1367,6 +1439,42 @@ var executeGovernanceActions = async (results, opts = {}) => {
1367
1439
  headers[key] = resolveEnvVars(value);
1368
1440
  }
1369
1441
  }
1442
+ if (result.deprecationChanges && result.deprecationChanges.length > 0) {
1443
+ for (const dc of result.deprecationChanges) {
1444
+ const messageType = messageTypes?.get(dc.resourceChange.resourceId) || "message";
1445
+ const producers = dc.producerServices.length > 0 ? dc.producerServices : [{ id: "unknown", version: "unknown" }];
1446
+ for (const producer of producers) {
1447
+ const payload = {
1448
+ specversion: "1.0",
1449
+ type: `eventcatalog.governance.message_deprecated`,
1450
+ source: "eventcatalog/governance",
1451
+ id: (0, import_node_crypto2.randomUUID)(),
1452
+ time: now,
1453
+ datacontenttype: "application/json",
1454
+ data: {
1455
+ schemaVersion: 1,
1456
+ ...status && { status },
1457
+ summary: `${dc.resourceChange.resourceId} (${messageType}) has been deprecated by ${producer.id}`,
1458
+ producer: {
1459
+ id: producer.id,
1460
+ version: producer.version,
1461
+ ...producer.owners && { owners: producer.owners }
1462
+ },
1463
+ message: {
1464
+ id: dc.resourceChange.resourceId,
1465
+ version: dc.resourceChange.version,
1466
+ type: messageType
1467
+ }
1468
+ }
1469
+ };
1470
+ webhookCalls.push({
1471
+ urlTemplate: action.url,
1472
+ request: fetch(url, { method: "POST", headers, body: JSON.stringify(payload) })
1473
+ });
1474
+ }
1475
+ }
1476
+ continue;
1477
+ }
1370
1478
  for (const change of result.matchedChanges) {
1371
1479
  const verb = getChangeVerb(result.trigger, change.changeType);
1372
1480
  const messageType = messageTypes?.get(change.resourceId) || "message";
@@ -1423,10 +1531,17 @@ var formatGovernanceOutput = (results) => {
1423
1531
  const lines = ["Governance:", ""];
1424
1532
  for (const result of results) {
1425
1533
  lines.push(` Rule "${result.rule.name}" triggered (${result.trigger}):`);
1426
- for (const change of result.matchedChanges) {
1427
- const prefix = change.changeType === "added" ? "+" : "-";
1428
- const verb = getChangeVerb(result.trigger, change.changeType);
1429
- lines.push(` ${prefix} ${change.serviceId} is ${verb} ${change.resourceId}`);
1534
+ if (result.deprecationChanges && result.deprecationChanges.length > 0) {
1535
+ for (const dc of result.deprecationChanges) {
1536
+ const producers = dc.producerServices.length > 0 ? dc.producerServices.map((p) => p.id).join(", ") : "unknown producer";
1537
+ lines.push(` ! ${dc.resourceChange.resourceId} (${dc.resourceChange.type}) deprecated by ${producers}`);
1538
+ }
1539
+ } else {
1540
+ for (const change of result.matchedChanges) {
1541
+ const prefix = change.changeType === "added" ? "+" : "-";
1542
+ const verb = getChangeVerb(result.trigger, change.changeType);
1543
+ lines.push(` ${prefix} ${change.serviceId} is ${verb} ${change.resourceId}`);
1544
+ }
1430
1545
  }
1431
1546
  lines.push("");
1432
1547
  }
@@ -1487,7 +1602,7 @@ var governanceCheck = async (opts) => {
1487
1602
  const diff = await baseSDK.diffSnapshots(baseResult.filePath, targetResult.filePath);
1488
1603
  const config = loadGovernanceConfig(dir);
1489
1604
  if (config.rules.length === 0) {
1490
- return "No governance.yaml found or no rules defined.";
1605
+ return "No governance.yaml (or governance.yml) found or no rules defined.";
1491
1606
  }
1492
1607
  const results = evaluateGovernanceRules(diff, config, targetResult.snapshot, baseResult.snapshot);
1493
1608
  const messageTypes = buildMessageTypeMap(targetResult.snapshot);