@eventcatalog/diff 0.1.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.
@@ -0,0 +1,244 @@
1
+ import { IndexResourceType, EdgeDirection, Index } from '@eventcatalog/sdk';
2
+ export { EdgeDirection, Index, IndexResource, IndexResourceType, IndexSchema } from '@eventcatalog/sdk';
3
+
4
+ /**
5
+ * ArchitectureDiff
6
+ *
7
+ * The single versioned document produced by `diff(a, b)`. Consumers (CI actions,
8
+ * policy engines, notifiers, UIs) build on top of this and never re-walk the graph.
9
+ *
10
+ * Input is the SDK `Index` returned by `buildIndex()`. Vocabulary follows the SDK:
11
+ * resources have a `type`, edges have a `direction`.
12
+ */
13
+
14
+ declare const ARCHITECTURE_DIFF_SCHEMA_VERSION: 1;
15
+ type CompatibilityStrategy = 'backward' | 'forward' | 'full' | 'none';
16
+ /** Which side a breaking change hurts. `both` only occurs under the `full` strategy. */
17
+ type BreakingDirection = 'backward' | 'forward' | 'both';
18
+ /** Resource types that carry schemas and travel along sends/receives edges. */
19
+ type MessageType = Extract<IndexResourceType, 'event' | 'command' | 'query'>;
20
+ /** Where an index came from: the SDK index `source` and `commit`. */
21
+ type DiffRef = {
22
+ source: string;
23
+ commit: string;
24
+ };
25
+ /** A value that differs between the baseline (a) and the candidate (b). */
26
+ type Change<T> = {
27
+ a: T;
28
+ b: T;
29
+ };
30
+ type DiffSummary = {
31
+ breaking: boolean;
32
+ resourcesAdded: number;
33
+ resourcesRemoved: number;
34
+ resourcesChanged: number;
35
+ edgesAdded: number;
36
+ edgesRemoved: number;
37
+ schemaChanges: number;
38
+ schemaBreaking: number;
39
+ /** Schema changes with no verdict (`breaking: null`). Policy should decide whether these warn or fail. */
40
+ schemaUnknown: number;
41
+ };
42
+ type ResourceRef = {
43
+ type: IndexResourceType;
44
+ id: string;
45
+ version?: string;
46
+ };
47
+ type ResourceAdded = ResourceRef;
48
+ type ResourceRemoved = ResourceRef;
49
+ /** The fields of a resource the diff compares. Markdown content and schema files are covered elsewhere. */
50
+ type ResourceField = 'version' | 'name' | 'owners' | 'deprecated';
51
+ type ResourceChanged = {
52
+ type: IndexResourceType;
53
+ id: string;
54
+ /** Latest version on each side. Equal when only metadata changed. */
55
+ version: Change<string | undefined>;
56
+ /** Which fields differ between the latest version on each side. */
57
+ fields: ResourceField[];
58
+ };
59
+ /** One end of an edge. `type` is absent when the target could not be resolved in the index. */
60
+ type EdgeEnd = {
61
+ type?: IndexResourceType;
62
+ id: string;
63
+ version?: string;
64
+ };
65
+ /**
66
+ * An edge between two resources, as resolved by the SDK. Identity ignores versions,
67
+ * so bumping a message does not churn every edge that points at it.
68
+ */
69
+ type DiffEdge = {
70
+ direction: EdgeDirection;
71
+ from: EdgeEnd;
72
+ to: EdgeEnd;
73
+ /** The field the pointer came from when it is not the direction itself, e.g. `steps`. */
74
+ via?: string;
75
+ };
76
+ type SchemaPointer = {
77
+ path: string;
78
+ hash?: string;
79
+ /** Raw schema text. Only present when `diff()` is called with `includeSchemaContent: true`. */
80
+ content?: string;
81
+ };
82
+ type SchemaOp = {
83
+ op: 'add' | 'remove' | 'replace';
84
+ /** JSON pointer into the schema document, e.g. `/properties/customerId`. */
85
+ path: string;
86
+ /** Stable machine-readable change kind, e.g. `required.added`, for consumers that key on it. */
87
+ kind: string;
88
+ /** Human-readable explanation. */
89
+ reason: string;
90
+ /** Whether this op alone breaks the chosen strategy. */
91
+ breaking: boolean;
92
+ };
93
+ type SchemaChange = {
94
+ message: {
95
+ type: MessageType;
96
+ id: string;
97
+ version: Change<string | undefined>;
98
+ };
99
+ /** `modified` when the same schema file changed; `added` / `removed` when a schema file appeared or disappeared. */
100
+ change: 'modified' | 'added' | 'removed';
101
+ /** Absent when the schema was added. */
102
+ before?: SchemaPointer;
103
+ /** Absent when the schema was removed. */
104
+ after?: SchemaPointer;
105
+ /**
106
+ * `null` when no verdict was possible: the schema was added or removed, the index was
107
+ * built without schema content, the content is not valid JSON, or the format is not
108
+ * one we can compare yet. Counted in `summary.schemaUnknown` so it is never silent.
109
+ */
110
+ breaking: boolean | null;
111
+ strategy: CompatibilityStrategy;
112
+ /** Which side the change breaks. `null` when not breaking or when no verdict was possible. */
113
+ direction: BreakingDirection | null;
114
+ ops: SchemaOp[];
115
+ };
116
+ type ImpactReason = 'schema_breaking_change' | 'schema_changed' | 'message_removed' | 'producer_removed' | 'consumer_removed';
117
+ type ImpactedResource = {
118
+ type: IndexResourceType;
119
+ id: string;
120
+ version?: string;
121
+ owners?: string[];
122
+ };
123
+ /**
124
+ * Who a change hurts, derived from the baseline graph so consumers of the diff
125
+ * never have to walk edges themselves.
126
+ *
127
+ * - `forward` breaking: the consumers listed are on the old schema and will fail
128
+ * to read new messages.
129
+ * - `backward` breaking: the consumers listed will fail to read old messages once
130
+ * they move to the new schema (e.g. when replaying history).
131
+ * - `both`: both of the above.
132
+ */
133
+ type Impact = {
134
+ message: {
135
+ type: MessageType;
136
+ id: string;
137
+ version?: string;
138
+ };
139
+ reason: ImpactReason;
140
+ /** Which direction broke. Only present for schema-related reasons. */
141
+ direction?: BreakingDirection;
142
+ producers: ImpactedResource[];
143
+ consumers: ImpactedResource[];
144
+ };
145
+ type ArchitectureDiff = {
146
+ schemaVersion: typeof ARCHITECTURE_DIFF_SCHEMA_VERSION;
147
+ refs: {
148
+ a: DiffRef;
149
+ b: DiffRef;
150
+ };
151
+ compatibility: {
152
+ strategy: CompatibilityStrategy;
153
+ };
154
+ summary: DiffSummary;
155
+ resources: {
156
+ added: ResourceAdded[];
157
+ removed: ResourceRemoved[];
158
+ changed: ResourceChanged[];
159
+ };
160
+ edges: {
161
+ added: DiffEdge[];
162
+ removed: DiffEdge[];
163
+ };
164
+ schemaChanges: SchemaChange[];
165
+ impact: Impact[];
166
+ };
167
+
168
+ type DiffOptions = {
169
+ /**
170
+ * Compatibility strategy used when comparing schemas. Defaults to `full`, so a
171
+ * change is breaking if it could break either producers or consumers.
172
+ */
173
+ strategy?: CompatibilityStrategy;
174
+ /**
175
+ * Copy the raw before and after schema text onto each schema change, so a
176
+ * consumer can render a side-by-side diff without going back to the index.
177
+ * Off by default to keep the document small.
178
+ */
179
+ includeSchemaContent?: boolean;
180
+ };
181
+ declare const DEFAULT_STRATEGY: CompatibilityStrategy;
182
+ /**
183
+ * Compare two catalog indexes and return a single ArchitectureDiff document.
184
+ *
185
+ * `a` is the baseline (e.g. `main`) and `b` is the candidate (e.g. a PR branch).
186
+ * Both are SDK `Index` documents as returned by `buildIndex()`. Build them with
187
+ * `includeSchemaContent: true` to get schema compatibility verdicts.
188
+ */
189
+ declare const diff: (a: Index, b: Index, options?: DiffOptions) => ArchitectureDiff;
190
+
191
+ /** A JSON Schema document. Boolean schemas (`true` / `false`) are valid in draft-06 and later. */
192
+ type JsonSchema = boolean | {
193
+ [keyword: string]: unknown;
194
+ };
195
+ /**
196
+ * Every kind of change the comparer can detect. Each kind has exactly one row in
197
+ * the rules table, which decides whether it is breaking under each strategy.
198
+ */
199
+ type JsonSchemaChangeKind = 'property.added' | 'property.added-to-closed-object' | 'property.removed' | 'property.removed-from-closed-object' | 'required.added' | 'required.added-with-default' | 'required.removed' | 'type.widened' | 'type.narrowed' | 'type.changed' | 'enum.added' | 'enum.removed' | 'enum.value.added' | 'enum.value.removed' | 'constraint.tightened' | 'constraint.loosened' | 'constraint.changed' | 'additionalProperties.closed' | 'additionalProperties.opened' | 'tuple.item.added' | 'tuple.item.removed' | 'union.branch.added' | 'union.branch.removed' | 'allOf.branch.added' | 'allOf.branch.removed' | 'schema.restricted' | 'schema.relaxed' | 'schema.deprecated' | 'keyword.changed';
200
+ /** A single semantic change between two schemas, with the values on both sides. */
201
+ type JsonSchemaChange = {
202
+ kind: JsonSchemaChangeKind;
203
+ /** JSON pointer to the affected node, e.g. `/properties/customerId`. */
204
+ path: string;
205
+ /** The keyword involved, when the kind is generic (e.g. `minLength`, `oneOf`). */
206
+ keyword?: string;
207
+ before?: unknown;
208
+ after?: unknown;
209
+ };
210
+
211
+ /**
212
+ * Walks two JSON schemas side by side and emits every semantic change found.
213
+ *
214
+ * This is deliberately rule-free: it only says *what* changed, never whether
215
+ * that matters. `rules.ts` decides what is breaking under each strategy.
216
+ *
217
+ * Supported: properties, required (with `default`), type (including `integer`
218
+ * within `number` and nullable unions), enum and const, the min/max constraint
219
+ * families, pattern, format, multipleOf, uniqueItems, additionalProperties,
220
+ * items and tuples (`items` array or `prefixItems`), oneOf, anyOf, allOf, local
221
+ * `$ref` (changes reported once, at the definition), and boolean schemas.
222
+ *
223
+ * Annotations (title, description, examples, $comment, default ...) are ignored.
224
+ * Keywords we cannot reason about are reported as `keyword.changed` so they are
225
+ * never silently passed.
226
+ */
227
+ declare const compareJsonSchemas: (before: JsonSchema, after: JsonSchema) => JsonSchemaChange[];
228
+
229
+ type JsonSchemaCompatibility = {
230
+ breaking: boolean;
231
+ /** Which side the change breaks under the strategy, or `null` when it is not breaking. */
232
+ direction: BreakingDirection | null;
233
+ ops: SchemaOp[];
234
+ };
235
+ /**
236
+ * Compare two JSON schemas and judge the result under a compatibility strategy.
237
+ *
238
+ * Returns every change as an op (so consumers can show what happened), a single
239
+ * verdict (breaking if at least one op is breaking under the strategy) and the
240
+ * direction that broke, so impact can say who is hurt.
241
+ */
242
+ declare const checkJsonSchemaCompatibility: (before: JsonSchema, after: JsonSchema, strategy: CompatibilityStrategy) => JsonSchemaCompatibility;
243
+
244
+ export { ARCHITECTURE_DIFF_SCHEMA_VERSION, type ArchitectureDiff, type BreakingDirection, type Change, type CompatibilityStrategy, DEFAULT_STRATEGY, type DiffEdge, type DiffOptions, type DiffRef, type DiffSummary, type EdgeEnd, type Impact, type ImpactReason, type ImpactedResource, type JsonSchema, type JsonSchemaChange, type JsonSchemaChangeKind, type JsonSchemaCompatibility, type MessageType, type ResourceAdded, type ResourceChanged, type ResourceField, type ResourceRef, type ResourceRemoved, type SchemaChange, type SchemaOp, type SchemaPointer, checkJsonSchemaCompatibility, compareJsonSchemas, diff };
@@ -0,0 +1,244 @@
1
+ import { IndexResourceType, EdgeDirection, Index } from '@eventcatalog/sdk';
2
+ export { EdgeDirection, Index, IndexResource, IndexResourceType, IndexSchema } from '@eventcatalog/sdk';
3
+
4
+ /**
5
+ * ArchitectureDiff
6
+ *
7
+ * The single versioned document produced by `diff(a, b)`. Consumers (CI actions,
8
+ * policy engines, notifiers, UIs) build on top of this and never re-walk the graph.
9
+ *
10
+ * Input is the SDK `Index` returned by `buildIndex()`. Vocabulary follows the SDK:
11
+ * resources have a `type`, edges have a `direction`.
12
+ */
13
+
14
+ declare const ARCHITECTURE_DIFF_SCHEMA_VERSION: 1;
15
+ type CompatibilityStrategy = 'backward' | 'forward' | 'full' | 'none';
16
+ /** Which side a breaking change hurts. `both` only occurs under the `full` strategy. */
17
+ type BreakingDirection = 'backward' | 'forward' | 'both';
18
+ /** Resource types that carry schemas and travel along sends/receives edges. */
19
+ type MessageType = Extract<IndexResourceType, 'event' | 'command' | 'query'>;
20
+ /** Where an index came from: the SDK index `source` and `commit`. */
21
+ type DiffRef = {
22
+ source: string;
23
+ commit: string;
24
+ };
25
+ /** A value that differs between the baseline (a) and the candidate (b). */
26
+ type Change<T> = {
27
+ a: T;
28
+ b: T;
29
+ };
30
+ type DiffSummary = {
31
+ breaking: boolean;
32
+ resourcesAdded: number;
33
+ resourcesRemoved: number;
34
+ resourcesChanged: number;
35
+ edgesAdded: number;
36
+ edgesRemoved: number;
37
+ schemaChanges: number;
38
+ schemaBreaking: number;
39
+ /** Schema changes with no verdict (`breaking: null`). Policy should decide whether these warn or fail. */
40
+ schemaUnknown: number;
41
+ };
42
+ type ResourceRef = {
43
+ type: IndexResourceType;
44
+ id: string;
45
+ version?: string;
46
+ };
47
+ type ResourceAdded = ResourceRef;
48
+ type ResourceRemoved = ResourceRef;
49
+ /** The fields of a resource the diff compares. Markdown content and schema files are covered elsewhere. */
50
+ type ResourceField = 'version' | 'name' | 'owners' | 'deprecated';
51
+ type ResourceChanged = {
52
+ type: IndexResourceType;
53
+ id: string;
54
+ /** Latest version on each side. Equal when only metadata changed. */
55
+ version: Change<string | undefined>;
56
+ /** Which fields differ between the latest version on each side. */
57
+ fields: ResourceField[];
58
+ };
59
+ /** One end of an edge. `type` is absent when the target could not be resolved in the index. */
60
+ type EdgeEnd = {
61
+ type?: IndexResourceType;
62
+ id: string;
63
+ version?: string;
64
+ };
65
+ /**
66
+ * An edge between two resources, as resolved by the SDK. Identity ignores versions,
67
+ * so bumping a message does not churn every edge that points at it.
68
+ */
69
+ type DiffEdge = {
70
+ direction: EdgeDirection;
71
+ from: EdgeEnd;
72
+ to: EdgeEnd;
73
+ /** The field the pointer came from when it is not the direction itself, e.g. `steps`. */
74
+ via?: string;
75
+ };
76
+ type SchemaPointer = {
77
+ path: string;
78
+ hash?: string;
79
+ /** Raw schema text. Only present when `diff()` is called with `includeSchemaContent: true`. */
80
+ content?: string;
81
+ };
82
+ type SchemaOp = {
83
+ op: 'add' | 'remove' | 'replace';
84
+ /** JSON pointer into the schema document, e.g. `/properties/customerId`. */
85
+ path: string;
86
+ /** Stable machine-readable change kind, e.g. `required.added`, for consumers that key on it. */
87
+ kind: string;
88
+ /** Human-readable explanation. */
89
+ reason: string;
90
+ /** Whether this op alone breaks the chosen strategy. */
91
+ breaking: boolean;
92
+ };
93
+ type SchemaChange = {
94
+ message: {
95
+ type: MessageType;
96
+ id: string;
97
+ version: Change<string | undefined>;
98
+ };
99
+ /** `modified` when the same schema file changed; `added` / `removed` when a schema file appeared or disappeared. */
100
+ change: 'modified' | 'added' | 'removed';
101
+ /** Absent when the schema was added. */
102
+ before?: SchemaPointer;
103
+ /** Absent when the schema was removed. */
104
+ after?: SchemaPointer;
105
+ /**
106
+ * `null` when no verdict was possible: the schema was added or removed, the index was
107
+ * built without schema content, the content is not valid JSON, or the format is not
108
+ * one we can compare yet. Counted in `summary.schemaUnknown` so it is never silent.
109
+ */
110
+ breaking: boolean | null;
111
+ strategy: CompatibilityStrategy;
112
+ /** Which side the change breaks. `null` when not breaking or when no verdict was possible. */
113
+ direction: BreakingDirection | null;
114
+ ops: SchemaOp[];
115
+ };
116
+ type ImpactReason = 'schema_breaking_change' | 'schema_changed' | 'message_removed' | 'producer_removed' | 'consumer_removed';
117
+ type ImpactedResource = {
118
+ type: IndexResourceType;
119
+ id: string;
120
+ version?: string;
121
+ owners?: string[];
122
+ };
123
+ /**
124
+ * Who a change hurts, derived from the baseline graph so consumers of the diff
125
+ * never have to walk edges themselves.
126
+ *
127
+ * - `forward` breaking: the consumers listed are on the old schema and will fail
128
+ * to read new messages.
129
+ * - `backward` breaking: the consumers listed will fail to read old messages once
130
+ * they move to the new schema (e.g. when replaying history).
131
+ * - `both`: both of the above.
132
+ */
133
+ type Impact = {
134
+ message: {
135
+ type: MessageType;
136
+ id: string;
137
+ version?: string;
138
+ };
139
+ reason: ImpactReason;
140
+ /** Which direction broke. Only present for schema-related reasons. */
141
+ direction?: BreakingDirection;
142
+ producers: ImpactedResource[];
143
+ consumers: ImpactedResource[];
144
+ };
145
+ type ArchitectureDiff = {
146
+ schemaVersion: typeof ARCHITECTURE_DIFF_SCHEMA_VERSION;
147
+ refs: {
148
+ a: DiffRef;
149
+ b: DiffRef;
150
+ };
151
+ compatibility: {
152
+ strategy: CompatibilityStrategy;
153
+ };
154
+ summary: DiffSummary;
155
+ resources: {
156
+ added: ResourceAdded[];
157
+ removed: ResourceRemoved[];
158
+ changed: ResourceChanged[];
159
+ };
160
+ edges: {
161
+ added: DiffEdge[];
162
+ removed: DiffEdge[];
163
+ };
164
+ schemaChanges: SchemaChange[];
165
+ impact: Impact[];
166
+ };
167
+
168
+ type DiffOptions = {
169
+ /**
170
+ * Compatibility strategy used when comparing schemas. Defaults to `full`, so a
171
+ * change is breaking if it could break either producers or consumers.
172
+ */
173
+ strategy?: CompatibilityStrategy;
174
+ /**
175
+ * Copy the raw before and after schema text onto each schema change, so a
176
+ * consumer can render a side-by-side diff without going back to the index.
177
+ * Off by default to keep the document small.
178
+ */
179
+ includeSchemaContent?: boolean;
180
+ };
181
+ declare const DEFAULT_STRATEGY: CompatibilityStrategy;
182
+ /**
183
+ * Compare two catalog indexes and return a single ArchitectureDiff document.
184
+ *
185
+ * `a` is the baseline (e.g. `main`) and `b` is the candidate (e.g. a PR branch).
186
+ * Both are SDK `Index` documents as returned by `buildIndex()`. Build them with
187
+ * `includeSchemaContent: true` to get schema compatibility verdicts.
188
+ */
189
+ declare const diff: (a: Index, b: Index, options?: DiffOptions) => ArchitectureDiff;
190
+
191
+ /** A JSON Schema document. Boolean schemas (`true` / `false`) are valid in draft-06 and later. */
192
+ type JsonSchema = boolean | {
193
+ [keyword: string]: unknown;
194
+ };
195
+ /**
196
+ * Every kind of change the comparer can detect. Each kind has exactly one row in
197
+ * the rules table, which decides whether it is breaking under each strategy.
198
+ */
199
+ type JsonSchemaChangeKind = 'property.added' | 'property.added-to-closed-object' | 'property.removed' | 'property.removed-from-closed-object' | 'required.added' | 'required.added-with-default' | 'required.removed' | 'type.widened' | 'type.narrowed' | 'type.changed' | 'enum.added' | 'enum.removed' | 'enum.value.added' | 'enum.value.removed' | 'constraint.tightened' | 'constraint.loosened' | 'constraint.changed' | 'additionalProperties.closed' | 'additionalProperties.opened' | 'tuple.item.added' | 'tuple.item.removed' | 'union.branch.added' | 'union.branch.removed' | 'allOf.branch.added' | 'allOf.branch.removed' | 'schema.restricted' | 'schema.relaxed' | 'schema.deprecated' | 'keyword.changed';
200
+ /** A single semantic change between two schemas, with the values on both sides. */
201
+ type JsonSchemaChange = {
202
+ kind: JsonSchemaChangeKind;
203
+ /** JSON pointer to the affected node, e.g. `/properties/customerId`. */
204
+ path: string;
205
+ /** The keyword involved, when the kind is generic (e.g. `minLength`, `oneOf`). */
206
+ keyword?: string;
207
+ before?: unknown;
208
+ after?: unknown;
209
+ };
210
+
211
+ /**
212
+ * Walks two JSON schemas side by side and emits every semantic change found.
213
+ *
214
+ * This is deliberately rule-free: it only says *what* changed, never whether
215
+ * that matters. `rules.ts` decides what is breaking under each strategy.
216
+ *
217
+ * Supported: properties, required (with `default`), type (including `integer`
218
+ * within `number` and nullable unions), enum and const, the min/max constraint
219
+ * families, pattern, format, multipleOf, uniqueItems, additionalProperties,
220
+ * items and tuples (`items` array or `prefixItems`), oneOf, anyOf, allOf, local
221
+ * `$ref` (changes reported once, at the definition), and boolean schemas.
222
+ *
223
+ * Annotations (title, description, examples, $comment, default ...) are ignored.
224
+ * Keywords we cannot reason about are reported as `keyword.changed` so they are
225
+ * never silently passed.
226
+ */
227
+ declare const compareJsonSchemas: (before: JsonSchema, after: JsonSchema) => JsonSchemaChange[];
228
+
229
+ type JsonSchemaCompatibility = {
230
+ breaking: boolean;
231
+ /** Which side the change breaks under the strategy, or `null` when it is not breaking. */
232
+ direction: BreakingDirection | null;
233
+ ops: SchemaOp[];
234
+ };
235
+ /**
236
+ * Compare two JSON schemas and judge the result under a compatibility strategy.
237
+ *
238
+ * Returns every change as an op (so consumers can show what happened), a single
239
+ * verdict (breaking if at least one op is breaking under the strategy) and the
240
+ * direction that broke, so impact can say who is hurt.
241
+ */
242
+ declare const checkJsonSchemaCompatibility: (before: JsonSchema, after: JsonSchema, strategy: CompatibilityStrategy) => JsonSchemaCompatibility;
243
+
244
+ export { ARCHITECTURE_DIFF_SCHEMA_VERSION, type ArchitectureDiff, type BreakingDirection, type Change, type CompatibilityStrategy, DEFAULT_STRATEGY, type DiffEdge, type DiffOptions, type DiffRef, type DiffSummary, type EdgeEnd, type Impact, type ImpactReason, type ImpactedResource, type JsonSchema, type JsonSchemaChange, type JsonSchemaChangeKind, type JsonSchemaCompatibility, type MessageType, type ResourceAdded, type ResourceChanged, type ResourceField, type ResourceRef, type ResourceRemoved, type SchemaChange, type SchemaOp, type SchemaPointer, checkJsonSchemaCompatibility, compareJsonSchemas, diff };