@soulcraft/brainy 5.2.1 → 5.3.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/CHANGELOG.md +5 -0
- package/dist/augmentations/versioningAugmentation.d.ts +121 -0
- package/dist/augmentations/versioningAugmentation.js +418 -0
- package/dist/brainy.d.ts +28 -0
- package/dist/brainy.js +33 -1
- package/dist/versioning/VersionDiff.d.ts +112 -0
- package/dist/versioning/VersionDiff.js +320 -0
- package/dist/versioning/VersionIndex.d.ts +126 -0
- package/dist/versioning/VersionIndex.js +275 -0
- package/dist/versioning/VersionManager.d.ts +195 -0
- package/dist/versioning/VersionManager.js +352 -0
- package/dist/versioning/VersionStorage.d.ts +101 -0
- package/dist/versioning/VersionStorage.js +222 -0
- package/dist/versioning/VersioningAPI.d.ts +347 -0
- package/dist/versioning/VersioningAPI.js +432 -0
- package/dist/vfs/VirtualFileSystem.js +8 -4
- package/dist/vfs/types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VersionDiff - Deep Object Comparison for Entity Versions (v5.3.0)
|
|
3
|
+
*
|
|
4
|
+
* Provides deep diff between entity versions:
|
|
5
|
+
* - Field-level change detection
|
|
6
|
+
* - Nested object comparison
|
|
7
|
+
* - Array diffing
|
|
8
|
+
* - Type change detection
|
|
9
|
+
* - Human-readable diff output
|
|
10
|
+
*
|
|
11
|
+
* NO MOCKS - Production implementation
|
|
12
|
+
*/
|
|
13
|
+
import type { NounMetadata } from '../coreTypes.js';
|
|
14
|
+
/**
|
|
15
|
+
* Types of changes in a diff
|
|
16
|
+
*/
|
|
17
|
+
export type ChangeType = 'added' | 'removed' | 'modified' | 'type-changed';
|
|
18
|
+
/**
|
|
19
|
+
* A single field change in a diff
|
|
20
|
+
*/
|
|
21
|
+
export interface FieldChange {
|
|
22
|
+
/** Path to the field (e.g., 'metadata.user.name') */
|
|
23
|
+
path: string;
|
|
24
|
+
/** Type of change */
|
|
25
|
+
type: ChangeType;
|
|
26
|
+
/** Old value (undefined for 'added') */
|
|
27
|
+
oldValue?: any;
|
|
28
|
+
/** New value (undefined for 'removed') */
|
|
29
|
+
newValue?: any;
|
|
30
|
+
/** Old type (for 'type-changed') */
|
|
31
|
+
oldType?: string;
|
|
32
|
+
/** New type (for 'type-changed') */
|
|
33
|
+
newType?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Complete diff between two versions
|
|
37
|
+
*/
|
|
38
|
+
export interface VersionDiff {
|
|
39
|
+
/** Entity ID being compared */
|
|
40
|
+
entityId: string;
|
|
41
|
+
/** From version number */
|
|
42
|
+
fromVersion: number;
|
|
43
|
+
/** To version number */
|
|
44
|
+
toVersion: number;
|
|
45
|
+
/** Fields that were added */
|
|
46
|
+
added: FieldChange[];
|
|
47
|
+
/** Fields that were removed */
|
|
48
|
+
removed: FieldChange[];
|
|
49
|
+
/** Fields that were modified */
|
|
50
|
+
modified: FieldChange[];
|
|
51
|
+
/** Fields whose type changed */
|
|
52
|
+
typeChanged: FieldChange[];
|
|
53
|
+
/** Total number of changes */
|
|
54
|
+
totalChanges: number;
|
|
55
|
+
/** Whether versions are identical */
|
|
56
|
+
identical: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Options for diff comparison
|
|
60
|
+
*/
|
|
61
|
+
export interface DiffOptions {
|
|
62
|
+
/** Entity ID (for context in output) */
|
|
63
|
+
entityId: string;
|
|
64
|
+
/** From version number */
|
|
65
|
+
fromVersion: number;
|
|
66
|
+
/** To version number */
|
|
67
|
+
toVersion: number;
|
|
68
|
+
/** Ignore these fields in comparison */
|
|
69
|
+
ignoreFields?: string[];
|
|
70
|
+
/** Maximum depth for nested object comparison (default: 10) */
|
|
71
|
+
maxDepth?: number;
|
|
72
|
+
/** Include unchanged fields in output (default: false) */
|
|
73
|
+
includeUnchanged?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Compare two entity versions and generate diff
|
|
77
|
+
*
|
|
78
|
+
* @param from Old version entity
|
|
79
|
+
* @param to New version entity
|
|
80
|
+
* @param options Diff options
|
|
81
|
+
* @returns Diff between versions
|
|
82
|
+
*/
|
|
83
|
+
export declare function compareEntityVersions(from: NounMetadata, to: NounMetadata, options: DiffOptions): VersionDiff;
|
|
84
|
+
/**
|
|
85
|
+
* Format diff as human-readable string
|
|
86
|
+
*
|
|
87
|
+
* @param diff Diff to format
|
|
88
|
+
* @returns Formatted string
|
|
89
|
+
*/
|
|
90
|
+
export declare function formatDiff(diff: VersionDiff): string;
|
|
91
|
+
/**
|
|
92
|
+
* Get summary statistics about a diff
|
|
93
|
+
*/
|
|
94
|
+
export declare function getDiffStats(diff: VersionDiff): {
|
|
95
|
+
changedFields: number;
|
|
96
|
+
addedFields: number;
|
|
97
|
+
removedFields: number;
|
|
98
|
+
modifiedFields: number;
|
|
99
|
+
typeChangedFields: number;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Check if diff has any changes
|
|
103
|
+
*/
|
|
104
|
+
export declare function hasChanges(diff: VersionDiff): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Get all changed field paths
|
|
107
|
+
*/
|
|
108
|
+
export declare function getChangedPaths(diff: VersionDiff): string[];
|
|
109
|
+
/**
|
|
110
|
+
* Filter diff to only include specific paths
|
|
111
|
+
*/
|
|
112
|
+
export declare function filterDiff(diff: VersionDiff, paths: string[]): VersionDiff;
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VersionDiff - Deep Object Comparison for Entity Versions (v5.3.0)
|
|
3
|
+
*
|
|
4
|
+
* Provides deep diff between entity versions:
|
|
5
|
+
* - Field-level change detection
|
|
6
|
+
* - Nested object comparison
|
|
7
|
+
* - Array diffing
|
|
8
|
+
* - Type change detection
|
|
9
|
+
* - Human-readable diff output
|
|
10
|
+
*
|
|
11
|
+
* NO MOCKS - Production implementation
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Compare two entity versions and generate diff
|
|
15
|
+
*
|
|
16
|
+
* @param from Old version entity
|
|
17
|
+
* @param to New version entity
|
|
18
|
+
* @param options Diff options
|
|
19
|
+
* @returns Diff between versions
|
|
20
|
+
*/
|
|
21
|
+
export function compareEntityVersions(from, to, options) {
|
|
22
|
+
const added = [];
|
|
23
|
+
const removed = [];
|
|
24
|
+
const modified = [];
|
|
25
|
+
const typeChanged = [];
|
|
26
|
+
const ignoreFields = new Set(options.ignoreFields || []);
|
|
27
|
+
const maxDepth = options.maxDepth ?? 10;
|
|
28
|
+
// Compare objects recursively
|
|
29
|
+
compareObjects(from, to, '', added, removed, modified, typeChanged, ignoreFields, 0, maxDepth);
|
|
30
|
+
const totalChanges = added.length + removed.length + modified.length + typeChanged.length;
|
|
31
|
+
const identical = totalChanges === 0;
|
|
32
|
+
return {
|
|
33
|
+
entityId: options.entityId,
|
|
34
|
+
fromVersion: options.fromVersion,
|
|
35
|
+
toVersion: options.toVersion,
|
|
36
|
+
added,
|
|
37
|
+
removed,
|
|
38
|
+
modified,
|
|
39
|
+
typeChanged,
|
|
40
|
+
totalChanges,
|
|
41
|
+
identical
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Recursively compare two objects
|
|
46
|
+
*/
|
|
47
|
+
function compareObjects(from, to, path, added, removed, modified, typeChanged, ignoreFields, depth, maxDepth) {
|
|
48
|
+
if (depth >= maxDepth) {
|
|
49
|
+
// Hit max depth - treat as single value
|
|
50
|
+
if (!deepEqual(from, to)) {
|
|
51
|
+
modified.push({
|
|
52
|
+
path,
|
|
53
|
+
type: 'modified',
|
|
54
|
+
oldValue: from,
|
|
55
|
+
newValue: to
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Get all keys from both objects
|
|
61
|
+
const fromKeys = new Set(Object.keys(from || {}));
|
|
62
|
+
const toKeys = new Set(Object.keys(to || {}));
|
|
63
|
+
const allKeys = new Set([...fromKeys, ...toKeys]);
|
|
64
|
+
for (const key of allKeys) {
|
|
65
|
+
const fieldPath = path ? `${path}.${key}` : key;
|
|
66
|
+
// Skip ignored fields
|
|
67
|
+
if (ignoreFields.has(fieldPath) || ignoreFields.has(key)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const fromHas = fromKeys.has(key);
|
|
71
|
+
const toHas = toKeys.has(key);
|
|
72
|
+
if (!fromHas && toHas) {
|
|
73
|
+
// Field added
|
|
74
|
+
added.push({
|
|
75
|
+
path: fieldPath,
|
|
76
|
+
type: 'added',
|
|
77
|
+
newValue: to[key]
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
else if (fromHas && !toHas) {
|
|
81
|
+
// Field removed
|
|
82
|
+
removed.push({
|
|
83
|
+
path: fieldPath,
|
|
84
|
+
type: 'removed',
|
|
85
|
+
oldValue: from[key]
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Field exists in both - check for changes
|
|
90
|
+
const fromValue = from[key];
|
|
91
|
+
const toValue = to[key];
|
|
92
|
+
const fromType = getValueType(fromValue);
|
|
93
|
+
const toType = getValueType(toValue);
|
|
94
|
+
if (fromType !== toType) {
|
|
95
|
+
// Type changed
|
|
96
|
+
typeChanged.push({
|
|
97
|
+
path: fieldPath,
|
|
98
|
+
type: 'type-changed',
|
|
99
|
+
oldValue: fromValue,
|
|
100
|
+
newValue: toValue,
|
|
101
|
+
oldType: fromType,
|
|
102
|
+
newType: toType
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else if (fromType === 'object' && toType === 'object') {
|
|
106
|
+
// Recursively compare nested objects
|
|
107
|
+
compareObjects(fromValue, toValue, fieldPath, added, removed, modified, typeChanged, ignoreFields, depth + 1, maxDepth);
|
|
108
|
+
}
|
|
109
|
+
else if (fromType === 'array' && toType === 'array') {
|
|
110
|
+
// Compare arrays
|
|
111
|
+
if (!arraysEqual(fromValue, toValue)) {
|
|
112
|
+
modified.push({
|
|
113
|
+
path: fieldPath,
|
|
114
|
+
type: 'modified',
|
|
115
|
+
oldValue: fromValue,
|
|
116
|
+
newValue: toValue
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Primitive value comparison
|
|
122
|
+
if (!deepEqual(fromValue, toValue)) {
|
|
123
|
+
modified.push({
|
|
124
|
+
path: fieldPath,
|
|
125
|
+
type: 'modified',
|
|
126
|
+
oldValue: fromValue,
|
|
127
|
+
newValue: toValue
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get human-readable type of a value
|
|
136
|
+
*/
|
|
137
|
+
function getValueType(value) {
|
|
138
|
+
if (value === null)
|
|
139
|
+
return 'null';
|
|
140
|
+
if (value === undefined)
|
|
141
|
+
return 'undefined';
|
|
142
|
+
if (Array.isArray(value))
|
|
143
|
+
return 'array';
|
|
144
|
+
return typeof value;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Deep equality check
|
|
148
|
+
*/
|
|
149
|
+
function deepEqual(a, b) {
|
|
150
|
+
if (a === b)
|
|
151
|
+
return true;
|
|
152
|
+
if (a === null || b === null)
|
|
153
|
+
return false;
|
|
154
|
+
if (a === undefined || b === undefined)
|
|
155
|
+
return false;
|
|
156
|
+
const typeA = getValueType(a);
|
|
157
|
+
const typeB = getValueType(b);
|
|
158
|
+
if (typeA !== typeB)
|
|
159
|
+
return false;
|
|
160
|
+
if (typeA === 'array') {
|
|
161
|
+
return arraysEqual(a, b);
|
|
162
|
+
}
|
|
163
|
+
if (typeA === 'object') {
|
|
164
|
+
return objectsEqual(a, b);
|
|
165
|
+
}
|
|
166
|
+
// Primitive comparison
|
|
167
|
+
return a === b;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Compare arrays for equality
|
|
171
|
+
*/
|
|
172
|
+
function arraysEqual(a, b) {
|
|
173
|
+
if (a.length !== b.length)
|
|
174
|
+
return false;
|
|
175
|
+
for (let i = 0; i < a.length; i++) {
|
|
176
|
+
if (!deepEqual(a[i], b[i])) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Compare objects for equality
|
|
184
|
+
*/
|
|
185
|
+
function objectsEqual(a, b) {
|
|
186
|
+
const keysA = Object.keys(a);
|
|
187
|
+
const keysB = Object.keys(b);
|
|
188
|
+
if (keysA.length !== keysB.length)
|
|
189
|
+
return false;
|
|
190
|
+
for (const key of keysA) {
|
|
191
|
+
if (!keysB.includes(key))
|
|
192
|
+
return false;
|
|
193
|
+
if (!deepEqual(a[key], b[key]))
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Format diff as human-readable string
|
|
200
|
+
*
|
|
201
|
+
* @param diff Diff to format
|
|
202
|
+
* @returns Formatted string
|
|
203
|
+
*/
|
|
204
|
+
export function formatDiff(diff) {
|
|
205
|
+
const lines = [];
|
|
206
|
+
lines.push(`Diff: ${diff.entityId} v${diff.fromVersion} → v${diff.toVersion}`);
|
|
207
|
+
lines.push('');
|
|
208
|
+
if (diff.identical) {
|
|
209
|
+
lines.push('No changes');
|
|
210
|
+
return lines.join('\n');
|
|
211
|
+
}
|
|
212
|
+
lines.push(`Total changes: ${diff.totalChanges}`);
|
|
213
|
+
lines.push('');
|
|
214
|
+
if (diff.added.length > 0) {
|
|
215
|
+
lines.push(`Added (${diff.added.length}):`);
|
|
216
|
+
for (const change of diff.added) {
|
|
217
|
+
lines.push(` + ${change.path}: ${formatValue(change.newValue)}`);
|
|
218
|
+
}
|
|
219
|
+
lines.push('');
|
|
220
|
+
}
|
|
221
|
+
if (diff.removed.length > 0) {
|
|
222
|
+
lines.push(`Removed (${diff.removed.length}):`);
|
|
223
|
+
for (const change of diff.removed) {
|
|
224
|
+
lines.push(` - ${change.path}: ${formatValue(change.oldValue)}`);
|
|
225
|
+
}
|
|
226
|
+
lines.push('');
|
|
227
|
+
}
|
|
228
|
+
if (diff.modified.length > 0) {
|
|
229
|
+
lines.push(`Modified (${diff.modified.length}):`);
|
|
230
|
+
for (const change of diff.modified) {
|
|
231
|
+
lines.push(` ~ ${change.path}:`);
|
|
232
|
+
lines.push(` ${formatValue(change.oldValue)}`);
|
|
233
|
+
lines.push(` → ${formatValue(change.newValue)}`);
|
|
234
|
+
}
|
|
235
|
+
lines.push('');
|
|
236
|
+
}
|
|
237
|
+
if (diff.typeChanged.length > 0) {
|
|
238
|
+
lines.push(`Type Changed (${diff.typeChanged.length}):`);
|
|
239
|
+
for (const change of diff.typeChanged) {
|
|
240
|
+
lines.push(` ! ${change.path}: ${change.oldType} → ${change.newType}`);
|
|
241
|
+
lines.push(` ${formatValue(change.oldValue)}`);
|
|
242
|
+
lines.push(` → ${formatValue(change.newValue)}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return lines.join('\n');
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Format value for display
|
|
249
|
+
*/
|
|
250
|
+
function formatValue(value) {
|
|
251
|
+
if (value === null)
|
|
252
|
+
return 'null';
|
|
253
|
+
if (value === undefined)
|
|
254
|
+
return 'undefined';
|
|
255
|
+
if (typeof value === 'string')
|
|
256
|
+
return `"${value}"`;
|
|
257
|
+
if (typeof value === 'object') {
|
|
258
|
+
try {
|
|
259
|
+
return JSON.stringify(value);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
return '[Object]';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return String(value);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Get summary statistics about a diff
|
|
269
|
+
*/
|
|
270
|
+
export function getDiffStats(diff) {
|
|
271
|
+
return {
|
|
272
|
+
changedFields: diff.totalChanges,
|
|
273
|
+
addedFields: diff.added.length,
|
|
274
|
+
removedFields: diff.removed.length,
|
|
275
|
+
modifiedFields: diff.modified.length,
|
|
276
|
+
typeChangedFields: diff.typeChanged.length
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Check if diff has any changes
|
|
281
|
+
*/
|
|
282
|
+
export function hasChanges(diff) {
|
|
283
|
+
return !diff.identical;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get all changed field paths
|
|
287
|
+
*/
|
|
288
|
+
export function getChangedPaths(diff) {
|
|
289
|
+
const paths = new Set();
|
|
290
|
+
for (const change of diff.added)
|
|
291
|
+
paths.add(change.path);
|
|
292
|
+
for (const change of diff.removed)
|
|
293
|
+
paths.add(change.path);
|
|
294
|
+
for (const change of diff.modified)
|
|
295
|
+
paths.add(change.path);
|
|
296
|
+
for (const change of diff.typeChanged)
|
|
297
|
+
paths.add(change.path);
|
|
298
|
+
return Array.from(paths).sort();
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Filter diff to only include specific paths
|
|
302
|
+
*/
|
|
303
|
+
export function filterDiff(diff, paths) {
|
|
304
|
+
const pathSet = new Set(paths);
|
|
305
|
+
const filterChanges = (changes) => changes.filter((c) => pathSet.has(c.path) || paths.some((p) => c.path.startsWith(p + '.')));
|
|
306
|
+
const added = filterChanges(diff.added);
|
|
307
|
+
const removed = filterChanges(diff.removed);
|
|
308
|
+
const modified = filterChanges(diff.modified);
|
|
309
|
+
const typeChanged = filterChanges(diff.typeChanged);
|
|
310
|
+
return {
|
|
311
|
+
...diff,
|
|
312
|
+
added,
|
|
313
|
+
removed,
|
|
314
|
+
modified,
|
|
315
|
+
typeChanged,
|
|
316
|
+
totalChanges: added.length + removed.length + modified.length + typeChanged.length,
|
|
317
|
+
identical: added.length + removed.length + modified.length + typeChanged.length === 0
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=VersionDiff.js.map
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VersionIndex - Fast Version Lookup Using Existing Index Infrastructure (v5.3.0)
|
|
3
|
+
*
|
|
4
|
+
* Integrates with Brainy's existing index system:
|
|
5
|
+
* - Uses MetadataIndexManager for field indexing
|
|
6
|
+
* - Leverages UnifiedCache for memory management
|
|
7
|
+
* - Uses EntityIdMapper for efficient ID handling
|
|
8
|
+
* - Uses ChunkManager for adaptive chunking
|
|
9
|
+
* - Leverages Roaring Bitmaps for fast set operations
|
|
10
|
+
*
|
|
11
|
+
* Version metadata is stored as regular entities with type='_version'
|
|
12
|
+
* This allows us to use existing index infrastructure without modification!
|
|
13
|
+
*
|
|
14
|
+
* Fields indexed:
|
|
15
|
+
* - versionEntityId: Entity being versioned
|
|
16
|
+
* - versionBranch: Branch version was created on
|
|
17
|
+
* - versionNumber: Version number
|
|
18
|
+
* - versionTag: Optional user tag
|
|
19
|
+
* - versionTimestamp: Creation timestamp
|
|
20
|
+
* - versionCommitHash: Commit hash
|
|
21
|
+
*
|
|
22
|
+
* NO MOCKS - Production implementation
|
|
23
|
+
*/
|
|
24
|
+
import type { EntityVersion } from './VersionManager.js';
|
|
25
|
+
import type { VersionQuery } from './VersionManager.js';
|
|
26
|
+
/**
|
|
27
|
+
* VersionIndex - Version lookup and querying using existing indexes
|
|
28
|
+
*
|
|
29
|
+
* Strategy: Store version metadata as special entities with type='_version'
|
|
30
|
+
* This leverages ALL existing index infrastructure automatically!
|
|
31
|
+
*/
|
|
32
|
+
export declare class VersionIndex {
|
|
33
|
+
private brain;
|
|
34
|
+
private initialized;
|
|
35
|
+
constructor(brain: any);
|
|
36
|
+
/**
|
|
37
|
+
* Initialize version index
|
|
38
|
+
*
|
|
39
|
+
* No special setup needed - we use existing entity storage and indexes!
|
|
40
|
+
*/
|
|
41
|
+
initialize(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Add version to index
|
|
44
|
+
*
|
|
45
|
+
* Stores version metadata as a special entity with type='_version'
|
|
46
|
+
* This automatically indexes it using existing MetadataIndexManager!
|
|
47
|
+
*
|
|
48
|
+
* @param version Version metadata
|
|
49
|
+
*/
|
|
50
|
+
addVersion(version: EntityVersion): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Get versions for an entity
|
|
53
|
+
*
|
|
54
|
+
* Uses existing MetadataIndexManager to query efficiently!
|
|
55
|
+
*
|
|
56
|
+
* @param query Version query
|
|
57
|
+
* @returns List of versions (newest first)
|
|
58
|
+
*/
|
|
59
|
+
getVersions(query: VersionQuery): Promise<EntityVersion[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Get specific version
|
|
62
|
+
*
|
|
63
|
+
* @param entityId Entity ID
|
|
64
|
+
* @param version Version number
|
|
65
|
+
* @param branch Branch name
|
|
66
|
+
* @returns Version metadata or null
|
|
67
|
+
*/
|
|
68
|
+
getVersion(entityId: string, version: number, branch: string): Promise<EntityVersion | null>;
|
|
69
|
+
/**
|
|
70
|
+
* Get version by tag
|
|
71
|
+
*
|
|
72
|
+
* @param entityId Entity ID
|
|
73
|
+
* @param tag Version tag
|
|
74
|
+
* @param branch Branch name
|
|
75
|
+
* @returns Version metadata or null
|
|
76
|
+
*/
|
|
77
|
+
getVersionByTag(entityId: string, tag: string, branch: string): Promise<EntityVersion | null>;
|
|
78
|
+
/**
|
|
79
|
+
* Get version count for entity
|
|
80
|
+
*
|
|
81
|
+
* @param entityId Entity ID
|
|
82
|
+
* @param branch Branch name
|
|
83
|
+
* @returns Number of versions
|
|
84
|
+
*/
|
|
85
|
+
getVersionCount(entityId: string, branch: string): Promise<number>;
|
|
86
|
+
/**
|
|
87
|
+
* Remove version from index
|
|
88
|
+
*
|
|
89
|
+
* @param entityId Entity ID
|
|
90
|
+
* @param version Version number
|
|
91
|
+
* @param branch Branch name
|
|
92
|
+
*/
|
|
93
|
+
removeVersion(entityId: string, version: number, branch: string): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Convert entity to EntityVersion format
|
|
96
|
+
*
|
|
97
|
+
* @param entity Entity from storage
|
|
98
|
+
* @returns EntityVersion or null if invalid
|
|
99
|
+
*/
|
|
100
|
+
private entityToVersion;
|
|
101
|
+
/**
|
|
102
|
+
* Generate unique ID for version entity
|
|
103
|
+
*
|
|
104
|
+
* Format: _version:{entityId}:{version}:{branch}
|
|
105
|
+
*
|
|
106
|
+
* @param entityId Entity ID
|
|
107
|
+
* @param version Version number
|
|
108
|
+
* @param branch Branch name
|
|
109
|
+
* @returns Version entity ID
|
|
110
|
+
*/
|
|
111
|
+
private getVersionEntityId;
|
|
112
|
+
/**
|
|
113
|
+
* Get all versioned entities (for cleanup/debugging)
|
|
114
|
+
*
|
|
115
|
+
* @returns List of entity IDs that have versions
|
|
116
|
+
*/
|
|
117
|
+
getVersionedEntities(): Promise<string[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Clear all versions for an entity
|
|
120
|
+
*
|
|
121
|
+
* @param entityId Entity ID
|
|
122
|
+
* @param branch Branch name
|
|
123
|
+
* @returns Number of versions deleted
|
|
124
|
+
*/
|
|
125
|
+
clearVersions(entityId: string, branch: string): Promise<number>;
|
|
126
|
+
}
|