@eventcatalog/linter 1.0.2 → 1.0.4

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.mjs ADDED
@@ -0,0 +1,1326 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/schemas/common.ts
9
+ import { z } from "zod";
10
+ var badgeSchema = z.object({
11
+ content: z.string(),
12
+ backgroundColor: z.string(),
13
+ textColor: z.string(),
14
+ icon: z.string().optional()
15
+ });
16
+ var ownerReferenceSchema = z.union([
17
+ // The ID of the user or team
18
+ z.string(),
19
+ // The full object with the ID and collection (keep compatibility with `reference`)
20
+ z.object({
21
+ id: z.string(),
22
+ collection: z.enum(["users", "teams"])
23
+ })
24
+ ]).transform(
25
+ // This transformation is needed to keep compatibility with `reference`.
26
+ // The utilities `getTeams` and `getUsers` rely on this transformation.
27
+ (lookup) => ({ id: typeof lookup === "string" ? lookup : lookup.id })
28
+ );
29
+ var specificationSchema = z.union([
30
+ z.object({
31
+ openapiPath: z.string().optional(),
32
+ asyncapiPath: z.string().optional()
33
+ }),
34
+ z.array(
35
+ z.object({
36
+ type: z.enum(["openapi", "asyncapi"]),
37
+ path: z.string(),
38
+ name: z.string().optional()
39
+ })
40
+ )
41
+ ]);
42
+ var repositorySchema = z.object({
43
+ language: z.string().optional(),
44
+ url: z.string().optional()
45
+ });
46
+ var draftSchema = z.union([
47
+ z.boolean(),
48
+ z.object({
49
+ title: z.string().optional(),
50
+ message: z.string()
51
+ })
52
+ ]);
53
+ var deprecatedSchema = z.union([
54
+ z.object({
55
+ message: z.string().optional(),
56
+ date: z.union([z.string(), z.date()]).optional()
57
+ }),
58
+ z.boolean().optional()
59
+ ]);
60
+ var pointerSchema = z.object({
61
+ id: z.string(),
62
+ version: z.string().optional().default("latest")
63
+ });
64
+ var resourcePointerSchema = z.object({
65
+ id: z.string(),
66
+ version: z.string().optional().default("latest"),
67
+ type: z.enum(["service", "event", "command", "query", "flow", "channel", "domain", "user", "team"])
68
+ });
69
+ var channelPointerSchema = z.object({
70
+ parameters: z.record(z.string()).optional()
71
+ }).merge(pointerSchema);
72
+ var resourceReferenceSchema = pointerSchema;
73
+ var semverSchema = z.string().refine((version) => {
74
+ if (version === "latest") return true;
75
+ if (version.includes(".x")) {
76
+ const xPattern = /^\d+(\.\d+)*\.x$/;
77
+ return xPattern.test(version);
78
+ }
79
+ if (version.startsWith("^") || version.startsWith("~")) {
80
+ const rangeVersion = version.substring(1);
81
+ const semverRegex2 = /^\d+\.\d+\.\d+(-[\w\d-.]+)?(\+[\w\d-.]+)?$/;
82
+ return semverRegex2.test(rangeVersion);
83
+ }
84
+ const semverRegex = /^\d+\.\d+\.\d+(-[\w\d-.]+)?(\+[\w\d-.]+)?$/;
85
+ return semverRegex.test(version);
86
+ }, "Invalid semantic version format");
87
+ var sidebarSchema = z.object({
88
+ label: z.string().optional(),
89
+ badge: z.string().optional()
90
+ }).optional();
91
+ var stylesSchema = z.object({
92
+ icon: z.string().optional(),
93
+ node: z.object({
94
+ color: z.string().optional(),
95
+ label: z.string().optional()
96
+ }).optional()
97
+ }).optional();
98
+ var resourceGroupSchema = z.array(
99
+ z.object({
100
+ id: z.string().optional(),
101
+ title: z.string().optional(),
102
+ items: z.array(resourcePointerSchema),
103
+ limit: z.number().optional().default(10),
104
+ sidebar: z.boolean().optional().default(true)
105
+ })
106
+ ).optional();
107
+ var catalogMetadataSchema = z.object({
108
+ path: z.string(),
109
+ filePath: z.string(),
110
+ astroContentFilePath: z.string(),
111
+ publicPath: z.string(),
112
+ type: z.string()
113
+ }).optional();
114
+ var baseSchema = z.object({
115
+ id: z.string(),
116
+ name: z.string(),
117
+ summary: z.string().optional(),
118
+ version: semverSchema,
119
+ draft: z.union([z.boolean(), z.object({ title: z.string().optional(), message: z.string() })]).optional(),
120
+ badges: z.array(badgeSchema).optional(),
121
+ owners: z.array(ownerReferenceSchema).optional(),
122
+ schemaPath: z.string().optional(),
123
+ sidebar: sidebarSchema,
124
+ repository: repositorySchema.optional(),
125
+ specifications: specificationSchema.optional(),
126
+ hidden: z.boolean().optional(),
127
+ resourceGroups: resourceGroupSchema,
128
+ styles: stylesSchema,
129
+ deprecated: deprecatedSchema.optional(),
130
+ visualiser: z.boolean().optional(),
131
+ versions: z.array(z.string()).optional(),
132
+ latestVersion: z.string().optional(),
133
+ catalog: catalogMetadataSchema
134
+ });
135
+
136
+ // src/schemas/domain.ts
137
+ import { z as z2 } from "zod";
138
+ var domainSchema = z2.object({
139
+ services: z2.array(pointerSchema).optional(),
140
+ domains: z2.array(pointerSchema).optional(),
141
+ entities: z2.array(pointerSchema).optional()
142
+ }).merge(baseSchema);
143
+
144
+ // src/schemas/service.ts
145
+ import { z as z3 } from "zod";
146
+ var serviceSchema = z3.object({
147
+ sends: z3.array(pointerSchema).optional(),
148
+ receives: z3.array(pointerSchema).optional(),
149
+ entities: z3.array(pointerSchema).optional(),
150
+ writesTo: z3.array(pointerSchema).optional(),
151
+ readsFrom: z3.array(pointerSchema).optional()
152
+ }).merge(baseSchema);
153
+
154
+ // src/schemas/message.ts
155
+ import { z as z4 } from "zod";
156
+ var baseMessageSchema = z4.object({
157
+ producers: z4.array(z4.any()).optional(),
158
+ // reference('services')
159
+ consumers: z4.array(z4.any()).optional(),
160
+ // reference('services')
161
+ channels: z4.array(channelPointerSchema).optional(),
162
+ messageChannels: z4.array(z4.any()).optional()
163
+ // reference('channels')
164
+ }).merge(baseSchema);
165
+ var eventSchema = baseMessageSchema;
166
+ var commandSchema = baseMessageSchema;
167
+ var querySchema = baseMessageSchema;
168
+
169
+ // src/schemas/channel.ts
170
+ import { z as z5 } from "zod";
171
+ var parameterSchema = z5.object({
172
+ enum: z5.array(z5.string()).optional(),
173
+ default: z5.string().optional(),
174
+ examples: z5.array(z5.string()).optional(),
175
+ description: z5.string().optional()
176
+ });
177
+ var channelSchema = z5.object({
178
+ address: z5.string().optional(),
179
+ protocols: z5.array(z5.string()).optional(),
180
+ parameters: z5.record(parameterSchema).optional(),
181
+ messages: z5.array(z5.object({ collection: z5.string(), name: z5.string(), ...pointerSchema.shape })).optional()
182
+ }).merge(baseSchema);
183
+
184
+ // src/schemas/flow.ts
185
+ import { z as z6 } from "zod";
186
+ var flowStep = z6.union([
187
+ z6.union([z6.string(), z6.number()]),
188
+ z6.object({
189
+ id: z6.union([z6.string(), z6.number()]),
190
+ label: z6.string().optional()
191
+ }).optional()
192
+ ]).optional();
193
+ var flowStepSchema = z6.object({
194
+ id: z6.union([z6.string(), z6.number()]),
195
+ type: z6.enum(["node", "message", "user", "actor"]).optional(),
196
+ title: z6.string(),
197
+ summary: z6.string().optional(),
198
+ message: pointerSchema.optional(),
199
+ service: pointerSchema.optional(),
200
+ flow: pointerSchema.optional(),
201
+ actor: z6.object({
202
+ name: z6.string()
203
+ }).optional(),
204
+ custom: z6.object({
205
+ title: z6.string(),
206
+ icon: z6.string().optional(),
207
+ type: z6.string().optional(),
208
+ summary: z6.string().optional(),
209
+ url: z6.string().url().optional(),
210
+ color: z6.string().optional(),
211
+ properties: z6.record(z6.union([z6.string(), z6.number()])).optional(),
212
+ height: z6.number().optional(),
213
+ menu: z6.array(
214
+ z6.object({
215
+ label: z6.string(),
216
+ url: z6.string().url().optional()
217
+ })
218
+ ).optional()
219
+ }).optional(),
220
+ externalSystem: z6.object({
221
+ name: z6.string(),
222
+ summary: z6.string().optional(),
223
+ url: z6.string().url().optional()
224
+ }).optional(),
225
+ next_step: flowStep,
226
+ next_steps: z6.array(flowStep).optional()
227
+ }).refine((data) => {
228
+ if (data.next_step && data.next_steps) return false;
229
+ const typesUsed = [data.message, data.service, data.flow, data.actor, data.custom].filter((v) => v).length;
230
+ return typesUsed === 0 || typesUsed === 1;
231
+ });
232
+ var flowSchema = z6.object({
233
+ steps: z6.array(flowStepSchema)
234
+ }).merge(baseSchema);
235
+
236
+ // src/schemas/entity.ts
237
+ import { z as z7 } from "zod";
238
+ var propertySchema = z7.object({
239
+ name: z7.string(),
240
+ type: z7.string(),
241
+ required: z7.boolean().optional(),
242
+ description: z7.string().optional(),
243
+ references: z7.string().optional(),
244
+ referencesIdentifier: z7.string().optional(),
245
+ relationType: z7.string().optional()
246
+ });
247
+ var entitySchema = z7.object({
248
+ aggregateRoot: z7.boolean().optional(),
249
+ identifier: z7.string().optional(),
250
+ properties: z7.array(propertySchema).optional(),
251
+ services: z7.array(z7.any()).optional(),
252
+ // reference('services')
253
+ domains: z7.array(z7.any()).optional()
254
+ // reference('domains')
255
+ }).merge(baseSchema);
256
+
257
+ // src/schemas/user.ts
258
+ import { z as z8 } from "zod";
259
+ var userSchema = z8.object({
260
+ id: z8.string(),
261
+ name: z8.string(),
262
+ avatarUrl: z8.string().optional(),
263
+ role: z8.string().optional(),
264
+ hidden: z8.boolean().optional(),
265
+ email: z8.string().email().optional(),
266
+ slackDirectMessageUrl: z8.string().optional(),
267
+ msTeamsDirectMessageUrl: z8.string().optional(),
268
+ ownedDomains: z8.array(z8.any()).optional(),
269
+ // reference('domains')
270
+ ownedServices: z8.array(z8.any()).optional(),
271
+ // reference('services')
272
+ ownedEvents: z8.array(z8.any()).optional(),
273
+ // reference('events')
274
+ ownedCommands: z8.array(z8.any()).optional(),
275
+ // reference('commands')
276
+ ownedQueries: z8.array(z8.any()).optional(),
277
+ // reference('queries')
278
+ associatedTeams: z8.array(z8.any()).optional()
279
+ // reference('teams')
280
+ });
281
+
282
+ // src/schemas/team.ts
283
+ import { z as z9 } from "zod";
284
+ var teamSchema = z9.object({
285
+ id: z9.string(),
286
+ name: z9.string(),
287
+ summary: z9.string().optional(),
288
+ email: z9.string().email().optional(),
289
+ hidden: z9.boolean().optional(),
290
+ slackDirectMessageUrl: z9.string().optional(),
291
+ msTeamsDirectMessageUrl: z9.string().optional(),
292
+ members: z9.array(z9.any()).optional(),
293
+ // reference('users')
294
+ ownedCommands: z9.array(z9.any()).optional(),
295
+ // reference('commands')
296
+ ownedQueries: z9.array(z9.any()).optional(),
297
+ // reference('queries')
298
+ ownedDomains: z9.array(z9.any()).optional(),
299
+ // reference('domains')
300
+ ownedServices: z9.array(z9.any()).optional(),
301
+ // reference('services')
302
+ ownedEvents: z9.array(z9.any()).optional()
303
+ // reference('events')
304
+ });
305
+
306
+ // src/schemas/data-store.ts
307
+ import { z as z10 } from "zod";
308
+ var dataStoreSchema = z10.object({
309
+ container_type: z10.enum(["database", "cache", "objectStore", "searchIndex", "dataWarehouse", "dataLake", "externalSaaS"]),
310
+ technology: z10.string().optional(),
311
+ authoritative: z10.boolean().optional(),
312
+ access_mode: z10.enum(["read", "write", "readWrite", "appendOnly"]),
313
+ classification: z10.enum(["public", "internal", "confidential", "regulated"]),
314
+ residency: z10.string().optional(),
315
+ retention: z10.string().optional()
316
+ }).merge(baseSchema);
317
+
318
+ // src/schemas/index.ts
319
+ var schemas = {
320
+ domain: domainSchema,
321
+ service: serviceSchema,
322
+ event: eventSchema,
323
+ command: commandSchema,
324
+ query: querySchema,
325
+ channel: channelSchema,
326
+ flow: flowSchema,
327
+ entity: entitySchema,
328
+ user: userSchema,
329
+ team: teamSchema,
330
+ dataStore: dataStoreSchema
331
+ };
332
+
333
+ // src/scanner/index.ts
334
+ import fg from "fast-glob";
335
+ import path from "path";
336
+ var RESOURCE_PATTERNS = {
337
+ domain: [
338
+ "domains/*/index.{md,mdx}",
339
+ "domains/*/versioned/*/index.{md,mdx}",
340
+ "domains/*/subdomains/*/index.{md,mdx}",
341
+ "domains/*/subdomains/*/versioned/*/index.{md,mdx}"
342
+ ],
343
+ service: [
344
+ "domains/*/services/*/index.{md,mdx}",
345
+ "domains/*/services/*/versioned/*/index.{md,mdx}",
346
+ "domains/*/subdomains/*/services/*/index.{md,mdx}",
347
+ "domains/*/subdomains/*/services/*/versioned/*/index.{md,mdx}",
348
+ "services/*/index.{md,mdx}",
349
+ "services/*/versioned/*/index.{md,mdx}"
350
+ ],
351
+ event: ["**/events/*/index.{md,mdx}", "**/events/*/versioned/*/index.{md,mdx}"],
352
+ command: ["**/commands/*/index.{md,mdx}", "**/commands/*/versioned/*/index.{md,mdx}"],
353
+ query: ["**/queries/*/index.{md,mdx}", "**/queries/*/versioned/*/index.{md,mdx}"],
354
+ channel: ["**/channels/*/index.{md,mdx}", "**/channels/*/versioned/*/index.{md,mdx}"],
355
+ flow: ["**/flows/*/index.{md,mdx}", "**/flows/*/versioned/*/index.{md,mdx}"],
356
+ entity: ["**/entities/*/index.{md,mdx}", "**/entities/*/versioned/*/index.{md,mdx}"],
357
+ user: ["users/*.{md,mdx}"],
358
+ team: ["teams/*.{md,mdx}"],
359
+ dataStore: ["**/containers/*/index.{md,mdx}", "**/containers/*/versioned/*/index.{md,mdx}"]
360
+ };
361
+ var extractResourceInfo = (filePath, resourceType) => {
362
+ const normalizedPath = filePath.replace(/\\/g, "/");
363
+ const relativePath = normalizedPath.split("/");
364
+ if (resourceType === "user" || resourceType === "team") {
365
+ const filename = path.basename(filePath, path.extname(filePath));
366
+ return { id: filename };
367
+ }
368
+ const resourceTypePattern = `${resourceType}s`;
369
+ const resourceTypeIndex = relativePath.findIndex((part) => part === resourceTypePattern);
370
+ if (resourceTypeIndex === -1) {
371
+ const parts2 = relativePath.slice(1, -1);
372
+ return { id: parts2[parts2.length - 1] };
373
+ }
374
+ const parts = relativePath.slice(resourceTypeIndex + 1, -1);
375
+ if (parts.length === 0) {
376
+ return { id: "unknown" };
377
+ }
378
+ if (parts.length >= 3 && parts[parts.length - 2] === "versioned") {
379
+ const version = parts[parts.length - 1];
380
+ const resourceId = parts.slice(0, -2).join("/");
381
+ return { id: resourceId, version };
382
+ }
383
+ if (resourceType === "domain" && parts.length >= 2) {
384
+ const versionedIndex = parts.findIndex((part) => part === "versioned");
385
+ if (versionedIndex !== -1 && versionedIndex < parts.length - 1) {
386
+ const version = parts[versionedIndex + 1];
387
+ const resourceId = parts.slice(0, versionedIndex).join("/");
388
+ return { id: resourceId, version };
389
+ }
390
+ }
391
+ if (parts.length === 1) {
392
+ return { id: parts[0] };
393
+ }
394
+ return { id: parts.join("/") };
395
+ };
396
+ var scanCatalogFiles = async (rootDir) => {
397
+ const files = [];
398
+ for (const [resourceType, patterns] of Object.entries(RESOURCE_PATTERNS)) {
399
+ const foundFiles = await fg(patterns, {
400
+ cwd: rootDir,
401
+ absolute: true,
402
+ onlyFiles: true,
403
+ followSymbolicLinks: false
404
+ });
405
+ for (const filePath of foundFiles) {
406
+ const relativePath = path.relative(rootDir, filePath);
407
+ const { id, version } = extractResourceInfo(relativePath, resourceType);
408
+ files.push({
409
+ path: filePath,
410
+ relativePath,
411
+ resourceType,
412
+ resourceId: id,
413
+ version
414
+ });
415
+ }
416
+ }
417
+ return files;
418
+ };
419
+
420
+ // src/parser/index.ts
421
+ import fs from "fs/promises";
422
+ import matter from "gray-matter";
423
+ var parseFrontmatter = async (file) => {
424
+ try {
425
+ const fileContent = await fs.readFile(file.path, "utf-8");
426
+ const { data, content } = matter(fileContent);
427
+ return {
428
+ file,
429
+ frontmatter: data,
430
+ content,
431
+ raw: fileContent
432
+ };
433
+ } catch (error) {
434
+ return {
435
+ file,
436
+ error: error instanceof Error ? error : new Error(String(error))
437
+ };
438
+ }
439
+ };
440
+ var parseAllFiles = async (files) => {
441
+ const results = await Promise.all(files.map(parseFrontmatter));
442
+ const parsed = [];
443
+ const errors = [];
444
+ for (const result of results) {
445
+ if ("error" in result) {
446
+ errors.push(result);
447
+ } else {
448
+ parsed.push(result);
449
+ }
450
+ }
451
+ return { parsed, errors };
452
+ };
453
+
454
+ // src/validators/schema-validator.ts
455
+ import { z as z11 } from "zod";
456
+ var validateSchema = (parsedFile) => {
457
+ const { file, frontmatter } = parsedFile;
458
+ const schema = schemas[file.resourceType];
459
+ const errors = [];
460
+ try {
461
+ schema.parse(frontmatter);
462
+ } catch (error) {
463
+ if (error instanceof z11.ZodError) {
464
+ for (const issue of error.issues) {
465
+ const field = issue.path.join(".");
466
+ let message = issue.message;
467
+ if (issue.code === "invalid_type") {
468
+ message = `Expected ${issue.expected}, but received ${issue.received}`;
469
+ }
470
+ let rule = "schema/required-fields";
471
+ if (issue.code === "invalid_type") {
472
+ rule = "schema/valid-type";
473
+ } else if (issue.code === "invalid_string" && issue.validation === "email") {
474
+ rule = "schema/valid-email";
475
+ } else if (field && field.includes("version")) {
476
+ rule = "schema/valid-semver";
477
+ }
478
+ errors.push({
479
+ type: "schema",
480
+ resource: `${file.resourceType}/${file.resourceId}`,
481
+ field: field || void 0,
482
+ message: field ? `${field}: ${message}` : message,
483
+ file: file.relativePath,
484
+ rule
485
+ });
486
+ }
487
+ } else {
488
+ errors.push({
489
+ type: "schema",
490
+ resource: `${file.resourceType}/${file.resourceId}`,
491
+ message: error instanceof Error ? error.message : String(error),
492
+ file: file.relativePath,
493
+ rule: "schema/validation-error"
494
+ });
495
+ }
496
+ }
497
+ return errors;
498
+ };
499
+ var validateAllSchemas = (parsedFiles) => {
500
+ const errors = [];
501
+ for (const parsedFile of parsedFiles) {
502
+ errors.push(...validateSchema(parsedFile));
503
+ }
504
+ return errors;
505
+ };
506
+
507
+ // src/validators/reference-validator.ts
508
+ import semver from "semver";
509
+ var buildResourceIndex = (parsedFiles, dependencies) => {
510
+ const index = {};
511
+ for (const parsedFile of parsedFiles) {
512
+ const { file, frontmatter } = parsedFile;
513
+ const { resourceType } = file;
514
+ let resourceId = file.resourceId;
515
+ if (frontmatter.id && typeof frontmatter.id === "string") {
516
+ resourceId = frontmatter.id;
517
+ }
518
+ if (!index[resourceType]) {
519
+ index[resourceType] = {};
520
+ }
521
+ if (!index[resourceType][resourceId]) {
522
+ index[resourceType][resourceId] = /* @__PURE__ */ new Set();
523
+ }
524
+ if (frontmatter.version && typeof frontmatter.version === "string") {
525
+ index[resourceType][resourceId].add(frontmatter.version);
526
+ } else {
527
+ index[resourceType][resourceId].add("latest");
528
+ }
529
+ }
530
+ if (dependencies) {
531
+ for (const [resourceType, entries] of Object.entries(dependencies)) {
532
+ if (!index[resourceType]) {
533
+ index[resourceType] = {};
534
+ }
535
+ for (const entry of entries) {
536
+ if (!index[resourceType][entry.id]) {
537
+ index[resourceType][entry.id] = /* @__PURE__ */ new Set();
538
+ }
539
+ index[resourceType][entry.id].add(entry.version || "latest");
540
+ }
541
+ }
542
+ }
543
+ return index;
544
+ };
545
+ var checkResourceExists = (ref, resourceType, index) => {
546
+ const resourceVersions = index[resourceType]?.[ref.id];
547
+ if (!resourceVersions || resourceVersions.size === 0) {
548
+ return false;
549
+ }
550
+ if (!ref.version) {
551
+ return true;
552
+ }
553
+ const refVersion = ref.version === "latest" ? ref.version : ref.version;
554
+ const availableVersions = Array.from(resourceVersions);
555
+ if (refVersion === "latest") {
556
+ return availableVersions.includes("latest") || availableVersions.length > 0;
557
+ }
558
+ if (availableVersions.includes(refVersion)) {
559
+ return true;
560
+ }
561
+ try {
562
+ const semverVersions = availableVersions.filter((v) => v !== "latest" && semver.valid(v));
563
+ for (const availableVersion of semverVersions) {
564
+ if (semver.satisfies(availableVersion, refVersion)) {
565
+ return true;
566
+ }
567
+ }
568
+ if (refVersion.includes(".x")) {
569
+ const pattern = refVersion.replace(/\.x/g, "");
570
+ for (const availableVersion of semverVersions) {
571
+ if (availableVersion.startsWith(pattern)) {
572
+ return true;
573
+ }
574
+ }
575
+ }
576
+ return false;
577
+ } catch (error) {
578
+ return availableVersions.includes(refVersion);
579
+ }
580
+ };
581
+ var extractReferences = (parsedFile) => {
582
+ const { file, frontmatter } = parsedFile;
583
+ const references = [];
584
+ if (file.resourceType === "domain") {
585
+ if (frontmatter.services && Array.isArray(frontmatter.services)) {
586
+ frontmatter.services.forEach((ref) => {
587
+ references.push({ ref, possibleTypes: ["service"], field: "services" });
588
+ });
589
+ }
590
+ if (frontmatter.domains && Array.isArray(frontmatter.domains)) {
591
+ frontmatter.domains.forEach((ref) => {
592
+ references.push({ ref, possibleTypes: ["domain"], field: "domains" });
593
+ });
594
+ }
595
+ if (frontmatter.entities && Array.isArray(frontmatter.entities)) {
596
+ frontmatter.entities.forEach((ref) => {
597
+ references.push({ ref, possibleTypes: ["entity"], field: "entities" });
598
+ });
599
+ }
600
+ }
601
+ if (file.resourceType === "service") {
602
+ if (frontmatter.sends && Array.isArray(frontmatter.sends)) {
603
+ frontmatter.sends.forEach((ref) => {
604
+ references.push({ ref, possibleTypes: ["event", "command", "query"], field: "sends" });
605
+ });
606
+ }
607
+ if (frontmatter.receives && Array.isArray(frontmatter.receives)) {
608
+ frontmatter.receives.forEach((ref) => {
609
+ references.push({ ref, possibleTypes: ["event", "command", "query"], field: "receives" });
610
+ });
611
+ }
612
+ if (frontmatter.entities && Array.isArray(frontmatter.entities)) {
613
+ frontmatter.entities.forEach((ref) => {
614
+ references.push({ ref, possibleTypes: ["entity"], field: "entities" });
615
+ });
616
+ }
617
+ if (frontmatter.writesTo && Array.isArray(frontmatter.writesTo)) {
618
+ frontmatter.writesTo.forEach((ref) => {
619
+ references.push({ ref, possibleTypes: ["dataStore"], field: "writesTo" });
620
+ });
621
+ }
622
+ if (frontmatter.readsFrom && Array.isArray(frontmatter.readsFrom)) {
623
+ frontmatter.readsFrom.forEach((ref) => {
624
+ references.push({ ref, possibleTypes: ["dataStore"], field: "readsFrom" });
625
+ });
626
+ }
627
+ }
628
+ if (file.resourceType === "flow" && frontmatter.steps && Array.isArray(frontmatter.steps)) {
629
+ frontmatter.steps.forEach((step, index) => {
630
+ if (step.message) {
631
+ references.push({
632
+ ref: step.message,
633
+ possibleTypes: ["event", "command", "query"],
634
+ field: `steps[${index}].message`
635
+ });
636
+ }
637
+ if (step.service) {
638
+ references.push({ ref: step.service, possibleTypes: ["service"], field: `steps[${index}].service` });
639
+ }
640
+ });
641
+ }
642
+ if (file.resourceType === "entity" && frontmatter.properties && Array.isArray(frontmatter.properties)) {
643
+ frontmatter.properties.forEach((prop, index) => {
644
+ if (prop.references) {
645
+ references.push({
646
+ ref: { id: prop.references },
647
+ possibleTypes: ["entity"],
648
+ field: `properties[${index}].references`
649
+ });
650
+ }
651
+ });
652
+ }
653
+ if (frontmatter.owners && Array.isArray(frontmatter.owners)) {
654
+ frontmatter.owners.forEach((owner) => {
655
+ references.push({ ref: { id: owner }, possibleTypes: ["user", "team"], field: "owners" });
656
+ });
657
+ }
658
+ if (file.resourceType === "team" && frontmatter.members && Array.isArray(frontmatter.members)) {
659
+ frontmatter.members.forEach((member) => {
660
+ references.push({ ref: { id: member }, possibleTypes: ["user"], field: "members" });
661
+ });
662
+ }
663
+ return references;
664
+ };
665
+ var extractChannelReferences = (parsedFile) => {
666
+ const { file, frontmatter } = parsedFile;
667
+ const references = [];
668
+ if (file.resourceType !== "service" && file.resourceType !== "domain") {
669
+ return references;
670
+ }
671
+ const extractFromPointers = (pointers, parentField) => {
672
+ if (!Array.isArray(pointers)) return;
673
+ pointers.forEach((pointer, idx) => {
674
+ if (pointer.to && Array.isArray(pointer.to)) {
675
+ pointer.to.forEach((channelRef, cIdx) => {
676
+ if (channelRef && channelRef.id) {
677
+ references.push({
678
+ ref: { id: channelRef.id, version: channelRef.version },
679
+ possibleTypes: ["channel"],
680
+ field: `${parentField}[${idx}].to[${cIdx}]`
681
+ });
682
+ }
683
+ });
684
+ }
685
+ if (pointer.from && Array.isArray(pointer.from)) {
686
+ pointer.from.forEach((channelRef, cIdx) => {
687
+ if (channelRef && channelRef.id) {
688
+ references.push({
689
+ ref: { id: channelRef.id, version: channelRef.version },
690
+ possibleTypes: ["channel"],
691
+ field: `${parentField}[${idx}].from[${cIdx}]`
692
+ });
693
+ }
694
+ });
695
+ }
696
+ });
697
+ };
698
+ if (frontmatter.sends && Array.isArray(frontmatter.sends)) {
699
+ extractFromPointers(frontmatter.sends, "sends");
700
+ }
701
+ if (frontmatter.receives && Array.isArray(frontmatter.receives)) {
702
+ extractFromPointers(frontmatter.receives, "receives");
703
+ }
704
+ return references;
705
+ };
706
+ var validateReferences = (parsedFiles, dependencies) => {
707
+ const index = buildResourceIndex(parsedFiles, dependencies);
708
+ const errors = [];
709
+ for (const parsedFile of parsedFiles) {
710
+ const references = extractReferences(parsedFile);
711
+ for (const { ref, possibleTypes, field } of references) {
712
+ const found = possibleTypes.some((type) => checkResourceExists(ref, type, index));
713
+ if (!found) {
714
+ const versionStr = ref.version ? ` (version: ${ref.version})` : "";
715
+ const typeStr = possibleTypes.length === 1 ? possibleTypes[0] : possibleTypes.join("/");
716
+ let rule = "refs/resource-exists";
717
+ if (field === "owners") {
718
+ rule = "refs/owner-exists";
719
+ } else if (field === "writesTo" || field === "readsFrom") {
720
+ rule = "refs/container-exists";
721
+ } else if (ref.version) {
722
+ rule = "refs/valid-version-range";
723
+ }
724
+ errors.push({
725
+ type: "reference",
726
+ resource: `${parsedFile.file.resourceType}/${parsedFile.file.resourceId}`,
727
+ field,
728
+ message: `Referenced ${typeStr} "${ref.id}"${versionStr} does not exist`,
729
+ file: parsedFile.file.relativePath,
730
+ rule
731
+ });
732
+ }
733
+ }
734
+ const channelRefs = extractChannelReferences(parsedFile);
735
+ for (const { ref, possibleTypes, field } of channelRefs) {
736
+ const found = possibleTypes.some((type) => checkResourceExists(ref, type, index));
737
+ if (!found) {
738
+ const versionStr = ref.version ? ` (version: ${ref.version})` : "";
739
+ errors.push({
740
+ type: "reference",
741
+ resource: `${parsedFile.file.resourceType}/${parsedFile.file.resourceId}`,
742
+ field,
743
+ message: `Referenced channel "${ref.id}"${versionStr} does not exist`,
744
+ file: parsedFile.file.relativePath,
745
+ rule: "refs/channel-exists"
746
+ });
747
+ }
748
+ }
749
+ }
750
+ return errors;
751
+ };
752
+ var validateOrphanMessages = (parsedFiles, dependencies) => {
753
+ const errors = [];
754
+ const messageTypes = ["event", "command", "query"];
755
+ const messageFiles = parsedFiles.filter((pf) => messageTypes.includes(pf.file.resourceType));
756
+ if (messageFiles.length === 0) return errors;
757
+ const producedMessages = /* @__PURE__ */ new Set();
758
+ const consumedMessages = /* @__PURE__ */ new Set();
759
+ for (const parsedFile of parsedFiles) {
760
+ const { file, frontmatter } = parsedFile;
761
+ if (file.resourceType === "service" || file.resourceType === "domain") {
762
+ if (frontmatter.sends && Array.isArray(frontmatter.sends)) {
763
+ frontmatter.sends.forEach((ref) => {
764
+ if (ref && ref.id) producedMessages.add(ref.id);
765
+ });
766
+ }
767
+ if (frontmatter.receives && Array.isArray(frontmatter.receives)) {
768
+ frontmatter.receives.forEach((ref) => {
769
+ if (ref && ref.id) consumedMessages.add(ref.id);
770
+ });
771
+ }
772
+ }
773
+ if (messageTypes.includes(file.resourceType)) {
774
+ if (frontmatter.producers && Array.isArray(frontmatter.producers) && frontmatter.producers.length > 0) {
775
+ const msgId = frontmatter.id || file.resourceId;
776
+ producedMessages.add(msgId);
777
+ }
778
+ if (frontmatter.consumers && Array.isArray(frontmatter.consumers) && frontmatter.consumers.length > 0) {
779
+ const msgId = frontmatter.id || file.resourceId;
780
+ consumedMessages.add(msgId);
781
+ }
782
+ }
783
+ }
784
+ if (dependencies) {
785
+ for (const [type, entries] of Object.entries(dependencies)) {
786
+ if (messageTypes.includes(type)) {
787
+ entries.forEach((entry) => {
788
+ producedMessages.add(entry.id);
789
+ consumedMessages.add(entry.id);
790
+ });
791
+ }
792
+ }
793
+ }
794
+ for (const parsedFile of messageFiles) {
795
+ const msgId = parsedFile.frontmatter.id || parsedFile.file.resourceId;
796
+ const isProduced = producedMessages.has(msgId);
797
+ const isConsumed = consumedMessages.has(msgId);
798
+ if (!isProduced && !isConsumed) {
799
+ errors.push({
800
+ type: "reference",
801
+ resource: `${parsedFile.file.resourceType}/${parsedFile.file.resourceId}`,
802
+ field: "id",
803
+ message: `${parsedFile.file.resourceType} "${msgId}" has no producer and no consumer`,
804
+ file: parsedFile.file.relativePath,
805
+ rule: "refs/orphan-messages"
806
+ });
807
+ }
808
+ }
809
+ return errors;
810
+ };
811
+ var validateDeprecatedReferences = (parsedFiles) => {
812
+ const errors = [];
813
+ const deprecatedIndex = {};
814
+ for (const parsedFile of parsedFiles) {
815
+ const { file, frontmatter } = parsedFile;
816
+ if (frontmatter.deprecated && frontmatter.deprecated !== false) {
817
+ const resourceId = frontmatter.id || file.resourceId;
818
+ const key = `${file.resourceType}:${resourceId}`;
819
+ if (!deprecatedIndex[key]) {
820
+ deprecatedIndex[key] = /* @__PURE__ */ new Set();
821
+ }
822
+ if (frontmatter.version && typeof frontmatter.version === "string") {
823
+ deprecatedIndex[key].add(frontmatter.version);
824
+ } else {
825
+ deprecatedIndex[key].add("*");
826
+ }
827
+ }
828
+ }
829
+ if (Object.keys(deprecatedIndex).length === 0) return errors;
830
+ const isDeprecated = (type, id, version) => {
831
+ const key = `${type}:${id}`;
832
+ const versions = deprecatedIndex[key];
833
+ if (!versions) return false;
834
+ if (versions.has("*")) return true;
835
+ if (version && versions.has(version)) return true;
836
+ if (!version || version === "latest") return versions.size > 0;
837
+ return false;
838
+ };
839
+ for (const parsedFile of parsedFiles) {
840
+ const references = extractReferences(parsedFile);
841
+ for (const { ref, possibleTypes, field } of references) {
842
+ if (field === "owners" || field === "members") continue;
843
+ for (const type of possibleTypes) {
844
+ if (isDeprecated(type, ref.id, ref.version)) {
845
+ const versionStr = ref.version ? ` (version: ${ref.version})` : "";
846
+ errors.push({
847
+ type: "reference",
848
+ resource: `${parsedFile.file.resourceType}/${parsedFile.file.resourceId}`,
849
+ field,
850
+ message: `Referenced ${type} "${ref.id}"${versionStr} is deprecated`,
851
+ file: parsedFile.file.relativePath,
852
+ rule: "versions/no-deprecated-references"
853
+ });
854
+ break;
855
+ }
856
+ }
857
+ }
858
+ }
859
+ return errors;
860
+ };
861
+ var validateDuplicateResourceIds = (parsedFiles) => {
862
+ const errors = [];
863
+ const seen = {};
864
+ for (const parsedFile of parsedFiles) {
865
+ const { file, frontmatter } = parsedFile;
866
+ const resourceId = frontmatter.id || file.resourceId;
867
+ const version = frontmatter.version || "latest";
868
+ const key = `${file.resourceType}:${resourceId}:${version}`;
869
+ if (seen[key]) {
870
+ errors.push({
871
+ type: "reference",
872
+ resource: `${file.resourceType}/${file.resourceId}`,
873
+ field: "id",
874
+ message: `Duplicate ${file.resourceType} "${resourceId}" (version: ${version}) \u2014 also defined in ${seen[key]}`,
875
+ file: file.relativePath,
876
+ rule: "structure/duplicate-resource-ids"
877
+ });
878
+ } else {
879
+ seen[key] = file.relativePath;
880
+ }
881
+ }
882
+ return errors;
883
+ };
884
+
885
+ // src/validators/best-practices-validator.ts
886
+ var validateBestPractices = (parsedFiles) => {
887
+ const errors = [];
888
+ for (const parsedFile of parsedFiles) {
889
+ const { file, frontmatter, content } = parsedFile;
890
+ if (!frontmatter.summary || typeof frontmatter.summary === "string" && frontmatter.summary.trim() === "") {
891
+ errors.push({
892
+ type: "schema",
893
+ resource: `${file.resourceType}/${file.resourceId}`,
894
+ field: "summary",
895
+ message: "Summary is required for better documentation",
896
+ file: file.relativePath,
897
+ severity: "error",
898
+ rule: "best-practices/summary-required"
899
+ });
900
+ }
901
+ if (file.resourceType !== "user" && file.resourceType !== "team" && (!frontmatter.owners || !Array.isArray(frontmatter.owners) || frontmatter.owners.length === 0)) {
902
+ errors.push({
903
+ type: "schema",
904
+ resource: `${file.resourceType}/${file.resourceId}`,
905
+ field: "owners",
906
+ message: "At least one owner is required",
907
+ file: file.relativePath,
908
+ severity: "error",
909
+ rule: "best-practices/owner-required"
910
+ });
911
+ }
912
+ if (!content || content.trim() === "") {
913
+ errors.push({
914
+ type: "schema",
915
+ resource: `${file.resourceType}/${file.resourceId}`,
916
+ field: "description",
917
+ message: "Resource should have a markdown description (body content) beyond just frontmatter",
918
+ file: file.relativePath,
919
+ severity: "warning",
920
+ rule: "best-practices/description-required"
921
+ });
922
+ }
923
+ if ((file.resourceType === "event" || file.resourceType === "command" || file.resourceType === "query") && !frontmatter.schemaPath) {
924
+ errors.push({
925
+ type: "schema",
926
+ resource: `${file.resourceType}/${file.resourceId}`,
927
+ field: "schemaPath",
928
+ message: `${file.resourceType} should have a schemaPath defined for consumers to understand the contract`,
929
+ file: file.relativePath,
930
+ severity: "warning",
931
+ rule: "best-practices/schema-required"
932
+ });
933
+ }
934
+ }
935
+ return errors;
936
+ };
937
+
938
+ // src/validators/index.ts
939
+ var validateCatalog = (parsedFiles, dependencies) => {
940
+ const schemaErrors = validateAllSchemas(parsedFiles);
941
+ const referenceErrors = validateReferences(parsedFiles, dependencies);
942
+ const orphanErrors = validateOrphanMessages(parsedFiles, dependencies);
943
+ const deprecatedRefErrors = validateDeprecatedReferences(parsedFiles);
944
+ const duplicateErrors = validateDuplicateResourceIds(parsedFiles);
945
+ const bestPracticeErrors = validateBestPractices(parsedFiles);
946
+ return [
947
+ ...schemaErrors,
948
+ ...referenceErrors,
949
+ ...orphanErrors,
950
+ ...deprecatedRefErrors,
951
+ ...duplicateErrors,
952
+ ...bestPracticeErrors
953
+ ];
954
+ };
955
+
956
+ // src/reporters/index.ts
957
+ import chalk from "chalk";
958
+ var formatError = (error, showFilename = true) => {
959
+ const lineInfo = error.line ? chalk.dim(`:${error.line}:1`) : "";
960
+ const filename = showFilename ? `${chalk.dim(error.file)}${lineInfo}` : "";
961
+ const isWarning = error.severity === "warning";
962
+ const severity = isWarning ? chalk.yellow("warning") : chalk.red("error");
963
+ const icon = isWarning ? chalk.yellow("\u26A0") : chalk.red("\u2716");
964
+ const errorCode = getErrorCode(error);
965
+ const field = error.field ? chalk.dim(`[${error.field}]`) : "";
966
+ const parts = [];
967
+ if (filename) parts.push(filename);
968
+ parts.push(icon, severity, error.message, field, chalk.dim(errorCode));
969
+ return parts.filter(Boolean).join(" ");
970
+ };
971
+ var getErrorCode = (error) => {
972
+ if (error.rule) {
973
+ return `(${error.rule})`;
974
+ }
975
+ if (error.type === "schema") {
976
+ if (error.field) {
977
+ if (error.message.includes("Required")) return "(@eventcatalog/required-field)";
978
+ if (error.message.includes("Expected")) return "(@eventcatalog/invalid-type)";
979
+ return "(@eventcatalog/schema-validation)";
980
+ }
981
+ return "(@eventcatalog/schema)";
982
+ }
983
+ if (error.type === "reference") {
984
+ return "(@eventcatalog/invalid-reference)";
985
+ }
986
+ return "(@eventcatalog/unknown)";
987
+ };
988
+ var formatParseError = (error, showFilename = true) => {
989
+ const filename = showFilename ? chalk.dim(error.file.relativePath) : "";
990
+ const severity = chalk.red("error");
991
+ const message = `Parse error: ${error.error.message}`;
992
+ const errorCode = chalk.dim("(@eventcatalog/parse-error)");
993
+ const parts = [];
994
+ if (filename) parts.push(filename);
995
+ parts.push(chalk.red("\u2716"), severity, message, errorCode);
996
+ return parts.filter(Boolean).join(" ");
997
+ };
998
+ var groupErrorsByFile = (errors) => {
999
+ const grouped = /* @__PURE__ */ new Map();
1000
+ for (const error of errors) {
1001
+ if (!grouped.has(error.file)) {
1002
+ grouped.set(error.file, []);
1003
+ }
1004
+ grouped.get(error.file).push(error);
1005
+ }
1006
+ return grouped;
1007
+ };
1008
+ var reportErrors = (validationErrors, parseErrors, verbose = false) => {
1009
+ const allErrors = [
1010
+ ...validationErrors,
1011
+ ...parseErrors.map((pe) => ({
1012
+ type: "parse",
1013
+ resource: pe.file.resourceType || "unknown",
1014
+ message: pe.error.message,
1015
+ file: pe.file.relativePath,
1016
+ line: void 0
1017
+ }))
1018
+ ];
1019
+ const schemaErrors = validationErrors.filter((e) => e.type === "schema");
1020
+ const referenceErrors = validationErrors.filter((e) => e.type === "reference");
1021
+ const warnings = validationErrors.filter((e) => e.severity === "warning");
1022
+ const errors = [...validationErrors.filter((e) => e.severity !== "warning"), ...parseErrors];
1023
+ const totalErrors = errors.length;
1024
+ const totalWarnings = warnings.length;
1025
+ if (totalErrors === 0 && totalWarnings === 0) {
1026
+ console.log(chalk.green("\u2714 No problems found!"));
1027
+ return {
1028
+ totalErrors: 0,
1029
+ totalWarnings: 0,
1030
+ schemaErrors: 0,
1031
+ referenceErrors: 0,
1032
+ parseErrors: 0,
1033
+ filesChecked: 0,
1034
+ filesWithErrors: 0
1035
+ };
1036
+ }
1037
+ const grouped = groupErrorsByFile(validationErrors);
1038
+ const parseErrorsGrouped = groupParseErrorsByFile(parseErrors);
1039
+ const allFiles = /* @__PURE__ */ new Set([...grouped.keys(), ...parseErrorsGrouped.keys()]);
1040
+ console.log();
1041
+ for (const file of Array.from(allFiles).sort()) {
1042
+ const fileErrors = grouped.get(file) || [];
1043
+ const fileParseErrors = parseErrorsGrouped.get(file) || [];
1044
+ const fileErrorCount = fileErrors.length + fileParseErrors.length;
1045
+ if (fileErrorCount === 0) continue;
1046
+ console.log(chalk.underline(file));
1047
+ for (const error of fileParseErrors) {
1048
+ console.log(` ${formatParseError(error, false)}`);
1049
+ }
1050
+ for (const error of fileErrors) {
1051
+ console.log(` ${formatError(error, false)}`);
1052
+ }
1053
+ const fileWarnings = fileErrors.filter((e) => e.severity === "warning").length;
1054
+ const fileActualErrors = fileErrorCount - fileWarnings;
1055
+ const problemText2 = fileErrorCount === 1 ? "problem" : "problems";
1056
+ const summaryColor2 = fileActualErrors > 0 ? chalk.red : chalk.yellow;
1057
+ const summaryIcon2 = fileActualErrors > 0 ? "\u2716" : "\u26A0";
1058
+ console.log(summaryColor2(`
1059
+ ${summaryIcon2} ${fileErrorCount} ${problemText2}
1060
+ `));
1061
+ }
1062
+ const filesWithErrors = allFiles.size;
1063
+ const totalProblems = totalErrors + totalWarnings;
1064
+ const problemText = totalProblems === 1 ? "problem" : "problems";
1065
+ const fileText = filesWithErrors === 1 ? "file" : "files";
1066
+ const summaryColor = totalErrors > 0 ? chalk.red.bold : chalk.yellow.bold;
1067
+ const summaryIcon = totalErrors > 0 ? "\u2716" : "\u26A0";
1068
+ console.log(summaryColor(`${summaryIcon} ${totalProblems} ${problemText} (${totalErrors} errors, ${totalWarnings} warnings)`));
1069
+ console.log(chalk.dim(` ${filesWithErrors} ${fileText} checked`));
1070
+ return {
1071
+ totalErrors,
1072
+ totalWarnings,
1073
+ schemaErrors: schemaErrors.length,
1074
+ referenceErrors: referenceErrors.length,
1075
+ parseErrors: parseErrors.length,
1076
+ filesChecked: allFiles.size,
1077
+ filesWithErrors
1078
+ };
1079
+ };
1080
+ var groupParseErrorsByFile = (errors) => {
1081
+ const grouped = /* @__PURE__ */ new Map();
1082
+ for (const error of errors) {
1083
+ const file = error.file.relativePath;
1084
+ if (!grouped.has(file)) {
1085
+ grouped.set(file, []);
1086
+ }
1087
+ grouped.get(file).push(error);
1088
+ }
1089
+ return grouped;
1090
+ };
1091
+
1092
+ // src/config/index.ts
1093
+ import fs2 from "fs";
1094
+ import path2 from "path";
1095
+ var DEFAULT_IGNORE_PATTERNS = ["dependencies/**"];
1096
+ var DEFAULT_RULES = {
1097
+ "schema/required-fields": "error",
1098
+ "schema/valid-semver": "error",
1099
+ "schema/valid-email": "error",
1100
+ "refs/owner-exists": "error",
1101
+ "refs/valid-version-range": "error",
1102
+ "refs/resource-exists": "error",
1103
+ "refs/channel-exists": "error",
1104
+ "refs/container-exists": "error",
1105
+ "refs/orphan-messages": "warn",
1106
+ "best-practices/summary-required": "error",
1107
+ "best-practices/owner-required": "error",
1108
+ "best-practices/description-required": "warn",
1109
+ "best-practices/schema-required": "warn",
1110
+ "naming/service-id-format": "error",
1111
+ "naming/event-id-format": "error",
1112
+ "versions/consistent-format": "error",
1113
+ "versions/no-deprecated": "error",
1114
+ "versions/no-deprecated-references": "warn",
1115
+ "structure/duplicate-resource-ids": "error"
1116
+ };
1117
+ var PLURAL_TO_SINGULAR = {
1118
+ events: "event",
1119
+ commands: "command",
1120
+ queries: "query",
1121
+ services: "service",
1122
+ domains: "domain",
1123
+ entities: "entity",
1124
+ channels: "channel",
1125
+ flows: "flow",
1126
+ users: "user",
1127
+ teams: "team"
1128
+ };
1129
+ var loadEventCatalogConfig = (rootDir) => {
1130
+ const configPath = path2.join(rootDir, "eventcatalog.config.js");
1131
+ if (!fs2.existsSync(configPath)) {
1132
+ return {};
1133
+ }
1134
+ try {
1135
+ delete __require.cache[__require.resolve(configPath)];
1136
+ const config = __require(configPath);
1137
+ if (!config.dependencies || typeof config.dependencies !== "object") {
1138
+ return {};
1139
+ }
1140
+ const dependencies = {};
1141
+ for (const [pluralKey, entries] of Object.entries(config.dependencies)) {
1142
+ const singularType = PLURAL_TO_SINGULAR[pluralKey];
1143
+ if (!singularType || !Array.isArray(entries)) continue;
1144
+ dependencies[singularType] = entries.filter((entry) => entry && typeof entry.id === "string").map((entry) => ({ id: entry.id, version: entry.version }));
1145
+ }
1146
+ return dependencies;
1147
+ } catch (error) {
1148
+ console.warn(`Warning: Could not load eventcatalog.config.js: ${error instanceof Error ? error.message : String(error)}`);
1149
+ return {};
1150
+ }
1151
+ };
1152
+ var loadConfig = (rootDir) => {
1153
+ const configPath = path2.join(rootDir, ".eventcatalogrc.js");
1154
+ if (!fs2.existsSync(configPath)) {
1155
+ return {
1156
+ rules: DEFAULT_RULES,
1157
+ ignorePatterns: DEFAULT_IGNORE_PATTERNS,
1158
+ overrides: []
1159
+ };
1160
+ }
1161
+ try {
1162
+ delete __require.cache[__require.resolve(configPath)];
1163
+ const config = __require(configPath);
1164
+ const mergedConfig = {
1165
+ rules: { ...DEFAULT_RULES, ...config.rules },
1166
+ ignorePatterns: [...DEFAULT_IGNORE_PATTERNS, ...config.ignorePatterns || []],
1167
+ overrides: config.overrides || []
1168
+ };
1169
+ return mergedConfig;
1170
+ } catch (error) {
1171
+ console.warn(`Warning: Could not load .eventcatalogrc.js: ${error instanceof Error ? error.message : String(error)}`);
1172
+ return {
1173
+ rules: DEFAULT_RULES,
1174
+ ignorePatterns: DEFAULT_IGNORE_PATTERNS,
1175
+ overrides: []
1176
+ };
1177
+ }
1178
+ };
1179
+ var parseRuleConfig = (rule) => {
1180
+ if (Array.isArray(rule)) {
1181
+ return {
1182
+ severity: rule[0],
1183
+ options: rule[1]
1184
+ };
1185
+ }
1186
+ return {
1187
+ severity: rule,
1188
+ options: {}
1189
+ };
1190
+ };
1191
+ var shouldIgnoreFile = (filePath, ignorePatterns) => {
1192
+ if (!ignorePatterns || ignorePatterns.length === 0) {
1193
+ return false;
1194
+ }
1195
+ const normalizedPath = filePath.replace(/\\/g, "/");
1196
+ for (const pattern of ignorePatterns) {
1197
+ const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
1198
+ if (regex.test(normalizedPath)) {
1199
+ return true;
1200
+ }
1201
+ }
1202
+ return false;
1203
+ };
1204
+ var getEffectiveRules = (filePath, config) => {
1205
+ let effectiveRules = { ...config.rules };
1206
+ if (config.overrides) {
1207
+ for (const override of config.overrides) {
1208
+ const matchesFile = override.files.some((pattern) => {
1209
+ const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
1210
+ return regex.test(filePath);
1211
+ });
1212
+ if (matchesFile) {
1213
+ effectiveRules = { ...effectiveRules, ...override.rules };
1214
+ }
1215
+ }
1216
+ }
1217
+ const parsedRules = {};
1218
+ for (const [ruleName, ruleValue] of Object.entries(effectiveRules)) {
1219
+ parsedRules[ruleName] = parseRuleConfig(ruleValue);
1220
+ }
1221
+ return parsedRules;
1222
+ };
1223
+ var applyRuleSeverity = (errors, rules) => {
1224
+ const result = [];
1225
+ for (const error of errors) {
1226
+ const ruleName = mapErrorToRuleName(error);
1227
+ const rule = rules[ruleName];
1228
+ if (!rule || rule.severity === "off") {
1229
+ continue;
1230
+ }
1231
+ result.push({
1232
+ ...error,
1233
+ severity: rule.severity === "warn" ? "warning" : "error"
1234
+ });
1235
+ }
1236
+ return result;
1237
+ };
1238
+ var mapErrorToRuleName = (error) => {
1239
+ if (error.rule) {
1240
+ return error.rule;
1241
+ }
1242
+ if (error.type === "schema") {
1243
+ if (error.field === "summary") {
1244
+ return "best-practices/summary-required";
1245
+ }
1246
+ if (error.field === "owners") {
1247
+ return "best-practices/owner-required";
1248
+ }
1249
+ if (error.message.includes("email") || error.message.includes("Invalid email")) {
1250
+ return "schema/valid-email";
1251
+ }
1252
+ if (error.message.includes("version") || error.message.includes("semantic")) {
1253
+ return "schema/valid-semver";
1254
+ }
1255
+ if (error.message.includes("Required") || error.message.includes("Expected")) {
1256
+ return "schema/required-fields";
1257
+ }
1258
+ return "schema/required-fields";
1259
+ }
1260
+ if (error.type === "reference") {
1261
+ if (error.message.includes("user") || error.message.includes("team")) {
1262
+ return "refs/owner-exists";
1263
+ }
1264
+ if (error.message.includes("version")) {
1265
+ return "refs/valid-version-range";
1266
+ }
1267
+ return "refs/resource-exists";
1268
+ }
1269
+ return "schema/required-fields";
1270
+ };
1271
+ export {
1272
+ DEFAULT_IGNORE_PATTERNS,
1273
+ DEFAULT_RULES,
1274
+ applyRuleSeverity,
1275
+ badgeSchema,
1276
+ baseSchema,
1277
+ buildResourceIndex,
1278
+ catalogMetadataSchema,
1279
+ channelPointerSchema,
1280
+ channelSchema,
1281
+ commandSchema,
1282
+ dataStoreSchema,
1283
+ deprecatedSchema,
1284
+ domainSchema,
1285
+ draftSchema,
1286
+ entitySchema,
1287
+ eventSchema,
1288
+ extractResourceInfo,
1289
+ flowSchema,
1290
+ formatError,
1291
+ formatParseError,
1292
+ getEffectiveRules,
1293
+ groupErrorsByFile,
1294
+ loadConfig,
1295
+ loadEventCatalogConfig,
1296
+ ownerReferenceSchema,
1297
+ parseAllFiles,
1298
+ parseFrontmatter,
1299
+ parseRuleConfig,
1300
+ pointerSchema,
1301
+ querySchema,
1302
+ reportErrors,
1303
+ repositorySchema,
1304
+ resourceGroupSchema,
1305
+ resourcePointerSchema,
1306
+ resourceReferenceSchema,
1307
+ scanCatalogFiles,
1308
+ schemas,
1309
+ semverSchema,
1310
+ serviceSchema,
1311
+ shouldIgnoreFile,
1312
+ sidebarSchema,
1313
+ specificationSchema,
1314
+ stylesSchema,
1315
+ teamSchema,
1316
+ userSchema,
1317
+ validateAllSchemas,
1318
+ validateBestPractices,
1319
+ validateCatalog,
1320
+ validateDeprecatedReferences,
1321
+ validateDuplicateResourceIds,
1322
+ validateOrphanMessages,
1323
+ validateReferences,
1324
+ validateSchema
1325
+ };
1326
+ //# sourceMappingURL=index.mjs.map