@memberjunction/version-history 0.0.1 → 4.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.
- package/README.md +253 -28
- package/dist/DependencyGraphWalker.d.ts +165 -0
- package/dist/DependencyGraphWalker.d.ts.map +1 -0
- package/dist/DependencyGraphWalker.js +501 -0
- package/dist/DependencyGraphWalker.js.map +1 -0
- package/dist/DiffEngine.d.ts +98 -0
- package/dist/DiffEngine.d.ts.map +1 -0
- package/dist/DiffEngine.js +409 -0
- package/dist/DiffEngine.js.map +1 -0
- package/dist/LabelManager.d.ts +41 -0
- package/dist/LabelManager.d.ts.map +1 -0
- package/dist/LabelManager.js +150 -0
- package/dist/LabelManager.js.map +1 -0
- package/dist/RestoreEngine.d.ts +97 -0
- package/dist/RestoreEngine.d.ts.map +1 -0
- package/dist/RestoreEngine.js +432 -0
- package/dist/RestoreEngine.js.map +1 -0
- package/dist/SnapshotBuilder.d.ts +108 -0
- package/dist/SnapshotBuilder.d.ts.map +1 -0
- package/dist/SnapshotBuilder.js +499 -0
- package/dist/SnapshotBuilder.js.map +1 -0
- package/dist/VersionHistoryEngine.d.ts +100 -0
- package/dist/VersionHistoryEngine.d.ts.map +1 -0
- package/dist/VersionHistoryEngine.js +198 -0
- package/dist/VersionHistoryEngine.js.map +1 -0
- package/dist/constants.d.ts +62 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +139 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +283 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +26 -7
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { Metadata, RunView, LogError, } from '@memberjunction/core';
|
|
2
|
+
import { ENTITY_VERSION_LABEL_ITEMS, ENTITY_VERSION_LABELS, ENTITY_RECORD_CHANGES, sqlEquals, loadRecordChangeSnapshot, loadEntityById, } from './constants.js';
|
|
3
|
+
/**
|
|
4
|
+
* Compares state between two version labels, or between a label and the
|
|
5
|
+
* current live state, producing a structured diff grouped by entity.
|
|
6
|
+
*/
|
|
7
|
+
export class DiffEngine {
|
|
8
|
+
/**
|
|
9
|
+
* Compare two version labels.
|
|
10
|
+
*/
|
|
11
|
+
async DiffLabels(fromLabelId, toLabelId, contextUser) {
|
|
12
|
+
// Short-circuit: identical labels produce an empty diff
|
|
13
|
+
if (fromLabelId === toLabelId) {
|
|
14
|
+
const label = await loadEntityById(ENTITY_VERSION_LABELS, fromLabelId, contextUser);
|
|
15
|
+
const labelName = label ? label.Name : fromLabelId;
|
|
16
|
+
return {
|
|
17
|
+
FromLabelID: fromLabelId,
|
|
18
|
+
FromLabelName: labelName,
|
|
19
|
+
ToLabelID: toLabelId,
|
|
20
|
+
ToLabelName: labelName,
|
|
21
|
+
Summary: { TotalRecordsChanged: 0, TotalRecordsAdded: 0, TotalRecordsModified: 0, TotalRecordsDeleted: 0, EntitiesAffected: 0 },
|
|
22
|
+
EntityDiffs: [],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const [fromLabel, toLabel] = await Promise.all([
|
|
26
|
+
loadEntityById(ENTITY_VERSION_LABELS, fromLabelId, contextUser),
|
|
27
|
+
loadEntityById(ENTITY_VERSION_LABELS, toLabelId, contextUser),
|
|
28
|
+
]);
|
|
29
|
+
if (!fromLabel)
|
|
30
|
+
throw new Error(`Version label '${fromLabelId}' not found`);
|
|
31
|
+
if (!toLabel)
|
|
32
|
+
throw new Error(`Version label '${toLabelId}' not found`);
|
|
33
|
+
const [fromIndex, toIndex] = await Promise.all([
|
|
34
|
+
this.buildSnapshotIndex(fromLabelId, contextUser),
|
|
35
|
+
this.buildSnapshotIndex(toLabelId, contextUser),
|
|
36
|
+
]);
|
|
37
|
+
const entityDiffs = await this.computeDiffs(fromIndex, toIndex, contextUser);
|
|
38
|
+
const summary = this.computeSummary(entityDiffs);
|
|
39
|
+
return {
|
|
40
|
+
FromLabelID: fromLabelId,
|
|
41
|
+
FromLabelName: fromLabel.Name,
|
|
42
|
+
ToLabelID: toLabelId,
|
|
43
|
+
ToLabelName: toLabel.Name,
|
|
44
|
+
Summary: summary,
|
|
45
|
+
EntityDiffs: entityDiffs,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Compare a version label to the current live state.
|
|
50
|
+
*
|
|
51
|
+
* For each record in the label, we find its current latest RecordChange
|
|
52
|
+
* and compare. Records that exist in the current state but not in the
|
|
53
|
+
* label are reported as Added; records in the label but not currently
|
|
54
|
+
* existing are reported as Deleted.
|
|
55
|
+
*/
|
|
56
|
+
async DiffLabelToCurrentState(labelId, contextUser) {
|
|
57
|
+
const label = await loadEntityById(ENTITY_VERSION_LABELS, labelId, contextUser);
|
|
58
|
+
if (!label)
|
|
59
|
+
throw new Error(`Version label '${labelId}' not found`);
|
|
60
|
+
const fromIndex = await this.buildSnapshotIndex(labelId, contextUser);
|
|
61
|
+
// Build a "current" index: for each record in the from index, find
|
|
62
|
+
// its most recent RecordChange
|
|
63
|
+
const currentIndex = await this.buildCurrentIndex(fromIndex, contextUser);
|
|
64
|
+
const entityDiffs = await this.computeDiffs(fromIndex, currentIndex, contextUser);
|
|
65
|
+
const summary = this.computeSummary(entityDiffs);
|
|
66
|
+
return {
|
|
67
|
+
FromLabelID: labelId,
|
|
68
|
+
FromLabelName: label.Name,
|
|
69
|
+
ToLabelID: null,
|
|
70
|
+
ToLabelName: null,
|
|
71
|
+
Summary: summary,
|
|
72
|
+
EntityDiffs: entityDiffs,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get the snapshot of a specific record at a specific label.
|
|
77
|
+
*/
|
|
78
|
+
async GetRecordSnapshotAtLabel(entityName, recordId, labelId, contextUser) {
|
|
79
|
+
const md = new Metadata();
|
|
80
|
+
const entityInfo = md.EntityByName(entityName);
|
|
81
|
+
if (!entityInfo)
|
|
82
|
+
return null;
|
|
83
|
+
const rv = new RunView();
|
|
84
|
+
const filter = [
|
|
85
|
+
sqlEquals('VersionLabelID', labelId),
|
|
86
|
+
sqlEquals('EntityID', entityInfo.ID),
|
|
87
|
+
sqlEquals('RecordID', recordId),
|
|
88
|
+
].join(' AND ');
|
|
89
|
+
const result = await rv.RunView({
|
|
90
|
+
EntityName: ENTITY_VERSION_LABEL_ITEMS,
|
|
91
|
+
ExtraFilter: filter,
|
|
92
|
+
Fields: ['ID', 'RecordChangeID', 'EntityID', 'RecordID'],
|
|
93
|
+
MaxRows: 1,
|
|
94
|
+
ResultType: 'simple',
|
|
95
|
+
}, contextUser);
|
|
96
|
+
if (!result.Success || result.Results.length === 0)
|
|
97
|
+
return null;
|
|
98
|
+
const item = result.Results[0];
|
|
99
|
+
return this.buildSnapshotFromRecordChange(item.RecordChangeID, entityName, entityInfo.ID, recordId, contextUser);
|
|
100
|
+
}
|
|
101
|
+
// -----------------------------------------------------------------------
|
|
102
|
+
// Private helpers
|
|
103
|
+
// -----------------------------------------------------------------------
|
|
104
|
+
/**
|
|
105
|
+
* Build an index from VersionLabelItems: key → RecordChangeID.
|
|
106
|
+
*/
|
|
107
|
+
async buildSnapshotIndex(labelId, contextUser) {
|
|
108
|
+
const rv = new RunView();
|
|
109
|
+
const result = await rv.RunView({
|
|
110
|
+
EntityName: ENTITY_VERSION_LABEL_ITEMS,
|
|
111
|
+
ExtraFilter: sqlEquals('VersionLabelID', labelId),
|
|
112
|
+
Fields: ['ID', 'RecordChangeID', 'EntityID', 'RecordID'],
|
|
113
|
+
ResultType: 'simple',
|
|
114
|
+
}, contextUser);
|
|
115
|
+
const index = new Map();
|
|
116
|
+
if (!result.Success) {
|
|
117
|
+
LogError(`DiffEngine: Failed to load label items for ${labelId}: ${result.ErrorMessage}`);
|
|
118
|
+
return index;
|
|
119
|
+
}
|
|
120
|
+
for (const item of result.Results) {
|
|
121
|
+
const key = `${item.EntityID}::${item.RecordID}`;
|
|
122
|
+
index.set(key, item.RecordChangeID);
|
|
123
|
+
}
|
|
124
|
+
return index;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Build a "current state" index by finding the latest RecordChange for
|
|
128
|
+
* each record in the fromIndex.
|
|
129
|
+
*/
|
|
130
|
+
async buildCurrentIndex(fromIndex, contextUser) {
|
|
131
|
+
const currentIndex = new Map();
|
|
132
|
+
const rv = new RunView();
|
|
133
|
+
// Group by entityId for efficient batch queries
|
|
134
|
+
const byEntity = this.groupKeysByEntity(fromIndex);
|
|
135
|
+
for (const [entityId, recordIds] of byEntity) {
|
|
136
|
+
for (const recordId of recordIds) {
|
|
137
|
+
const latestId = await this.findLatestRecordChange(rv, entityId, recordId, contextUser);
|
|
138
|
+
if (latestId) {
|
|
139
|
+
currentIndex.set(`${entityId}::${recordId}`, latestId);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return currentIndex;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Group composite keys from a SnapshotIndex by entityId.
|
|
147
|
+
*/
|
|
148
|
+
groupKeysByEntity(index) {
|
|
149
|
+
const byEntity = new Map();
|
|
150
|
+
for (const compositeKey of index.keys()) {
|
|
151
|
+
const [entityId, recordId] = compositeKey.split('::');
|
|
152
|
+
if (!byEntity.has(entityId))
|
|
153
|
+
byEntity.set(entityId, []);
|
|
154
|
+
byEntity.get(entityId).push(recordId);
|
|
155
|
+
}
|
|
156
|
+
return byEntity;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Find the latest RecordChange ID for a given entity + record.
|
|
160
|
+
*/
|
|
161
|
+
async findLatestRecordChange(rv, entityId, recordId, contextUser) {
|
|
162
|
+
const filter = [
|
|
163
|
+
sqlEquals('EntityID', entityId),
|
|
164
|
+
sqlEquals('RecordID', recordId),
|
|
165
|
+
].join(' AND ');
|
|
166
|
+
const result = await rv.RunView({
|
|
167
|
+
EntityName: ENTITY_RECORD_CHANGES,
|
|
168
|
+
ExtraFilter: filter,
|
|
169
|
+
OrderBy: 'ChangedAt DESC',
|
|
170
|
+
MaxRows: 1,
|
|
171
|
+
Fields: ['ID'],
|
|
172
|
+
ResultType: 'simple',
|
|
173
|
+
}, contextUser);
|
|
174
|
+
if (result.Success && result.Results.length > 0) {
|
|
175
|
+
return result.Results[0].ID;
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Compute diffs between two snapshot indices.
|
|
181
|
+
*/
|
|
182
|
+
async computeDiffs(fromIndex, toIndex, contextUser) {
|
|
183
|
+
const allKeys = new Set([...fromIndex.keys(), ...toIndex.keys()]);
|
|
184
|
+
const md = new Metadata();
|
|
185
|
+
// Group diffs by entity
|
|
186
|
+
const entityGroups = new Map();
|
|
187
|
+
for (const compositeKey of allKeys) {
|
|
188
|
+
const [entityId, recordId] = compositeKey.split('::');
|
|
189
|
+
const fromChangeId = fromIndex.get(compositeKey) ?? null;
|
|
190
|
+
const toChangeId = toIndex.get(compositeKey) ?? null;
|
|
191
|
+
const entityName = this.resolveEntityName(md, entityId);
|
|
192
|
+
if (!entityGroups.has(entityId)) {
|
|
193
|
+
entityGroups.set(entityId, { entityId, records: [] });
|
|
194
|
+
}
|
|
195
|
+
const diff = await this.diffSingleRecord(entityName, recordId, fromChangeId, toChangeId, contextUser);
|
|
196
|
+
entityGroups.get(entityId).records.push(diff);
|
|
197
|
+
}
|
|
198
|
+
return this.buildEntityDiffGroups(entityGroups, md);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Resolve an entity name from metadata by ID, with fallback.
|
|
202
|
+
*/
|
|
203
|
+
resolveEntityName(md, entityId) {
|
|
204
|
+
const entityInfo = md.Entities.find(e => e.ID === entityId);
|
|
205
|
+
return entityInfo?.Name ?? `Unknown(${entityId})`;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Convert the grouped map into an array of EntityDiffGroup,
|
|
209
|
+
* filtering out groups with no actual changes.
|
|
210
|
+
*/
|
|
211
|
+
buildEntityDiffGroups(entityGroups, md) {
|
|
212
|
+
const result = [];
|
|
213
|
+
for (const [entityId, group] of entityGroups) {
|
|
214
|
+
const entityName = this.resolveEntityName(md, entityId);
|
|
215
|
+
const addedCount = group.records.filter(r => r.DiffType === 'Added').length;
|
|
216
|
+
const modifiedCount = group.records.filter(r => r.DiffType === 'Modified').length;
|
|
217
|
+
const deletedCount = group.records.filter(r => r.DiffType === 'Deleted').length;
|
|
218
|
+
// Skip groups with no changes
|
|
219
|
+
if (addedCount === 0 && modifiedCount === 0 && deletedCount === 0)
|
|
220
|
+
continue;
|
|
221
|
+
result.push({
|
|
222
|
+
EntityName: entityName,
|
|
223
|
+
EntityID: entityId,
|
|
224
|
+
Records: group.records.filter(r => r.DiffType !== 'Unchanged'),
|
|
225
|
+
AddedCount: addedCount,
|
|
226
|
+
ModifiedCount: modifiedCount,
|
|
227
|
+
DeletedCount: deletedCount,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Diff a single record between two RecordChange snapshots.
|
|
234
|
+
*/
|
|
235
|
+
async diffSingleRecord(entityName, recordId, fromChangeId, toChangeId, contextUser) {
|
|
236
|
+
// Record exists only in "to" → Added
|
|
237
|
+
if (!fromChangeId && toChangeId) {
|
|
238
|
+
return this.buildAddedDiff(entityName, recordId, toChangeId, contextUser);
|
|
239
|
+
}
|
|
240
|
+
// Record exists only in "from" → Deleted
|
|
241
|
+
if (fromChangeId && !toChangeId) {
|
|
242
|
+
return this.buildDeletedDiff(entityName, recordId, fromChangeId, contextUser);
|
|
243
|
+
}
|
|
244
|
+
// Same RecordChange ID → Unchanged
|
|
245
|
+
if (fromChangeId === toChangeId) {
|
|
246
|
+
return this.buildUnchangedDiff(entityName, recordId);
|
|
247
|
+
}
|
|
248
|
+
// Both exist but differ → compare field-by-field
|
|
249
|
+
return this.buildModifiedDiff(entityName, recordId, fromChangeId, toChangeId, contextUser);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Build a diff result for a record that was added.
|
|
253
|
+
*/
|
|
254
|
+
async buildAddedDiff(entityName, recordId, toChangeId, contextUser) {
|
|
255
|
+
const toSnapshot = await loadRecordChangeSnapshot(toChangeId, contextUser);
|
|
256
|
+
return {
|
|
257
|
+
RecordID: recordId,
|
|
258
|
+
EntityName: entityName,
|
|
259
|
+
DiffType: 'Added',
|
|
260
|
+
FieldChanges: [],
|
|
261
|
+
FromSnapshot: null,
|
|
262
|
+
ToSnapshot: toSnapshot,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Build a diff result for a record that was deleted.
|
|
267
|
+
*/
|
|
268
|
+
async buildDeletedDiff(entityName, recordId, fromChangeId, contextUser) {
|
|
269
|
+
const fromSnapshot = await loadRecordChangeSnapshot(fromChangeId, contextUser);
|
|
270
|
+
return {
|
|
271
|
+
RecordID: recordId,
|
|
272
|
+
EntityName: entityName,
|
|
273
|
+
DiffType: 'Deleted',
|
|
274
|
+
FieldChanges: [],
|
|
275
|
+
FromSnapshot: fromSnapshot,
|
|
276
|
+
ToSnapshot: null,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Build a diff result for an unchanged record.
|
|
281
|
+
*/
|
|
282
|
+
buildUnchangedDiff(entityName, recordId) {
|
|
283
|
+
return {
|
|
284
|
+
RecordID: recordId,
|
|
285
|
+
EntityName: entityName,
|
|
286
|
+
DiffType: 'Unchanged',
|
|
287
|
+
FieldChanges: [],
|
|
288
|
+
FromSnapshot: null,
|
|
289
|
+
ToSnapshot: null,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Build a diff result for a modified record by comparing snapshots field-by-field.
|
|
294
|
+
*/
|
|
295
|
+
async buildModifiedDiff(entityName, recordId, fromChangeId, toChangeId, contextUser) {
|
|
296
|
+
const [fromSnapshot, toSnapshot] = await Promise.all([
|
|
297
|
+
loadRecordChangeSnapshot(fromChangeId, contextUser),
|
|
298
|
+
loadRecordChangeSnapshot(toChangeId, contextUser),
|
|
299
|
+
]);
|
|
300
|
+
const fieldChanges = this.compareSnapshots(fromSnapshot, toSnapshot);
|
|
301
|
+
const diffType = fieldChanges.length > 0 ? 'Modified' : 'Unchanged';
|
|
302
|
+
return {
|
|
303
|
+
RecordID: recordId,
|
|
304
|
+
EntityName: entityName,
|
|
305
|
+
DiffType: diffType,
|
|
306
|
+
FieldChanges: fieldChanges,
|
|
307
|
+
FromSnapshot: fromSnapshot,
|
|
308
|
+
ToSnapshot: toSnapshot,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Build a RecordSnapshot from a RecordChange entry, using the shared
|
|
313
|
+
* loadRecordChangeSnapshot utility and wrapping the result.
|
|
314
|
+
*/
|
|
315
|
+
async buildSnapshotFromRecordChange(recordChangeId, entityName, entityId, recordId, contextUser) {
|
|
316
|
+
const rv = new RunView();
|
|
317
|
+
const result = await rv.RunView({
|
|
318
|
+
EntityName: ENTITY_RECORD_CHANGES,
|
|
319
|
+
ExtraFilter: sqlEquals('ID', recordChangeId),
|
|
320
|
+
Fields: ['ID', 'ChangedAt'],
|
|
321
|
+
MaxRows: 1,
|
|
322
|
+
ResultType: 'simple',
|
|
323
|
+
}, contextUser);
|
|
324
|
+
if (!result.Success || result.Results.length === 0)
|
|
325
|
+
return null;
|
|
326
|
+
const changedAt = result.Results[0].ChangedAt;
|
|
327
|
+
const parsed = await loadRecordChangeSnapshot(recordChangeId, contextUser);
|
|
328
|
+
if (!parsed)
|
|
329
|
+
return null;
|
|
330
|
+
return {
|
|
331
|
+
EntityName: entityName,
|
|
332
|
+
EntityID: entityId,
|
|
333
|
+
RecordID: recordId,
|
|
334
|
+
RecordChangeID: recordChangeId,
|
|
335
|
+
ChangedAt: new Date(changedAt),
|
|
336
|
+
FullRecordJSON: parsed,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Compare two parsed JSON snapshots field-by-field.
|
|
341
|
+
*/
|
|
342
|
+
compareSnapshots(fromSnapshot, toSnapshot) {
|
|
343
|
+
if (!fromSnapshot || !toSnapshot)
|
|
344
|
+
return [];
|
|
345
|
+
const changes = [];
|
|
346
|
+
const allFields = new Set([...Object.keys(fromSnapshot), ...Object.keys(toSnapshot)]);
|
|
347
|
+
for (const field of allFields) {
|
|
348
|
+
// Skip internal timestamp fields
|
|
349
|
+
if (field.startsWith('__mj_'))
|
|
350
|
+
continue;
|
|
351
|
+
const oldVal = fromSnapshot[field];
|
|
352
|
+
const newVal = toSnapshot[field];
|
|
353
|
+
if (this.valuesEqual(oldVal, newVal))
|
|
354
|
+
continue;
|
|
355
|
+
const changeType = this.classifyFieldChange(oldVal, newVal);
|
|
356
|
+
changes.push({ FieldName: field, OldValue: oldVal, NewValue: newVal, ChangeType: changeType });
|
|
357
|
+
}
|
|
358
|
+
return changes;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Classify a field change as Added, Removed, or Modified.
|
|
362
|
+
*/
|
|
363
|
+
classifyFieldChange(oldVal, newVal) {
|
|
364
|
+
if (oldVal === undefined || oldVal === null)
|
|
365
|
+
return 'Added';
|
|
366
|
+
if (newVal === undefined || newVal === null)
|
|
367
|
+
return 'Removed';
|
|
368
|
+
return 'Modified';
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Equality check for field values.
|
|
372
|
+
* Handles null, identity, Date instances, and falls back to string comparison.
|
|
373
|
+
*/
|
|
374
|
+
valuesEqual(a, b) {
|
|
375
|
+
if (a === b)
|
|
376
|
+
return true;
|
|
377
|
+
if (a == null && b == null)
|
|
378
|
+
return true;
|
|
379
|
+
if (a == null || b == null)
|
|
380
|
+
return false;
|
|
381
|
+
// Date instance comparison
|
|
382
|
+
if (a instanceof Date && b instanceof Date) {
|
|
383
|
+
return a.getTime() === b.getTime();
|
|
384
|
+
}
|
|
385
|
+
// String comparison fallback
|
|
386
|
+
return String(a) === String(b);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Compute summary statistics from entity diffs.
|
|
390
|
+
*/
|
|
391
|
+
computeSummary(entityDiffs) {
|
|
392
|
+
let added = 0;
|
|
393
|
+
let modified = 0;
|
|
394
|
+
let deleted = 0;
|
|
395
|
+
for (const group of entityDiffs) {
|
|
396
|
+
added += group.AddedCount;
|
|
397
|
+
modified += group.ModifiedCount;
|
|
398
|
+
deleted += group.DeletedCount;
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
TotalRecordsChanged: added + modified + deleted,
|
|
402
|
+
TotalRecordsAdded: added,
|
|
403
|
+
TotalRecordsModified: modified,
|
|
404
|
+
TotalRecordsDeleted: deleted,
|
|
405
|
+
EntitiesAffected: entityDiffs.length,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
//# sourceMappingURL=DiffEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DiffEngine.js","sourceRoot":"","sources":["../src/DiffEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { UserInfo } from '@memberjunction/core';
|
|
2
|
+
import { VersionLabelEntity } from '@memberjunction/core-entities';
|
|
3
|
+
import { CreateLabelParams, LabelFilter } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Manages the lifecycle of version labels: creation, querying, archiving,
|
|
6
|
+
* and status updates. Does NOT handle snapshot capture — that is delegated
|
|
7
|
+
* to SnapshotBuilder by the VersionHistoryEngine facade.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LabelManager {
|
|
10
|
+
/**
|
|
11
|
+
* Create a new version label and persist it. Does not capture any snapshot
|
|
12
|
+
* items — the caller (VersionHistoryEngine) handles that separately.
|
|
13
|
+
*/
|
|
14
|
+
CreateLabel(params: CreateLabelParams, contextUser: UserInfo): Promise<VersionLabelEntity>;
|
|
15
|
+
/**
|
|
16
|
+
* Load a single version label by ID.
|
|
17
|
+
*/
|
|
18
|
+
GetLabel(labelId: string, contextUser: UserInfo): Promise<VersionLabelEntity>;
|
|
19
|
+
/**
|
|
20
|
+
* Query version labels with optional filters.
|
|
21
|
+
*/
|
|
22
|
+
GetLabels(filter: LabelFilter, contextUser: UserInfo): Promise<VersionLabelEntity[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Archive a label (set status to Archived).
|
|
25
|
+
*/
|
|
26
|
+
ArchiveLabel(labelId: string, contextUser: UserInfo): Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* Mark a label as having been used for a restore.
|
|
29
|
+
*/
|
|
30
|
+
MarkLabelRestored(labelId: string, contextUser: UserInfo): Promise<boolean>;
|
|
31
|
+
private validateCreateParams;
|
|
32
|
+
private applyEntityScope;
|
|
33
|
+
private applyRecordScope;
|
|
34
|
+
private updateLabelStatus;
|
|
35
|
+
/**
|
|
36
|
+
* Build safe SQL filter clauses from a LabelFilter.
|
|
37
|
+
* Uses escapeSqlString for all user-supplied values.
|
|
38
|
+
*/
|
|
39
|
+
private buildFilterClauses;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=LabelManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LabelManager.d.ts","sourceRoot":"","sources":["../src/LabelManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,QAAQ,EAAuB,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAsB,MAAM,SAAS,CAAC;AAQ7E;;;;GAIG;AACH,qBAAa,YAAY;IACrB;;;OAGG;IACU,WAAW,CAAC,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA0BvG;;OAEG;IACU,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAQ1F;;OAEG;IACU,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAuBjG;;OAEG;IACU,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAInF;;OAEG;IACU,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAQxF,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,gBAAgB;YAMV,iBAAiB;IAgB/B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CA4B7B"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Metadata, RunView, LogError, LogStatus } from '@memberjunction/core';
|
|
2
|
+
import { ENTITY_VERSION_LABELS, sqlEquals, sqlContains, loadEntityById, } from './constants.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manages the lifecycle of version labels: creation, querying, archiving,
|
|
5
|
+
* and status updates. Does NOT handle snapshot capture — that is delegated
|
|
6
|
+
* to SnapshotBuilder by the VersionHistoryEngine facade.
|
|
7
|
+
*/
|
|
8
|
+
export class LabelManager {
|
|
9
|
+
/**
|
|
10
|
+
* Create a new version label and persist it. Does not capture any snapshot
|
|
11
|
+
* items — the caller (VersionHistoryEngine) handles that separately.
|
|
12
|
+
*/
|
|
13
|
+
async CreateLabel(params, contextUser) {
|
|
14
|
+
this.validateCreateParams(params);
|
|
15
|
+
const md = new Metadata();
|
|
16
|
+
const label = await md.GetEntityObject(ENTITY_VERSION_LABELS, contextUser);
|
|
17
|
+
label.Name = params.Name;
|
|
18
|
+
label.Description = params.Description ?? null;
|
|
19
|
+
label.Scope = params.Scope ?? 'Record';
|
|
20
|
+
label.CreatedByUserID = contextUser.ID;
|
|
21
|
+
label.ExternalSystemID = params.ExternalSystemID ?? null;
|
|
22
|
+
label.ParentID = params.ParentID ?? null;
|
|
23
|
+
label.Status = 'Active';
|
|
24
|
+
this.applyEntityScope(label, params, md);
|
|
25
|
+
this.applyRecordScope(label, params);
|
|
26
|
+
const saved = await label.Save();
|
|
27
|
+
if (!saved) {
|
|
28
|
+
throw new Error(`Failed to save version label '${params.Name}'`);
|
|
29
|
+
}
|
|
30
|
+
LogStatus(`VersionHistory: Created label '${params.Name}' (${label.ID}) with scope ${label.Scope}`);
|
|
31
|
+
return label;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Load a single version label by ID.
|
|
35
|
+
*/
|
|
36
|
+
async GetLabel(labelId, contextUser) {
|
|
37
|
+
const label = await loadEntityById(ENTITY_VERSION_LABELS, labelId, contextUser);
|
|
38
|
+
if (!label) {
|
|
39
|
+
throw new Error(`Version label '${labelId}' not found`);
|
|
40
|
+
}
|
|
41
|
+
return label;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Query version labels with optional filters.
|
|
45
|
+
*/
|
|
46
|
+
async GetLabels(filter, contextUser) {
|
|
47
|
+
const rv = new RunView();
|
|
48
|
+
const filterParts = this.buildFilterClauses(filter);
|
|
49
|
+
const extraFilter = filterParts.length > 0 ? filterParts.join(' AND ') : '';
|
|
50
|
+
const orderBy = filter.OrderBy ?? '__mj_CreatedAt DESC';
|
|
51
|
+
const maxRows = filter.MaxResults ?? 100;
|
|
52
|
+
const result = await rv.RunView({
|
|
53
|
+
EntityName: ENTITY_VERSION_LABELS,
|
|
54
|
+
ExtraFilter: extraFilter,
|
|
55
|
+
OrderBy: orderBy,
|
|
56
|
+
MaxRows: maxRows,
|
|
57
|
+
ResultType: 'entity_object',
|
|
58
|
+
}, contextUser);
|
|
59
|
+
if (!result.Success) {
|
|
60
|
+
LogError(`LabelManager.GetLabels failed: ${result.ErrorMessage}`);
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
return result.Results;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Archive a label (set status to Archived).
|
|
67
|
+
*/
|
|
68
|
+
async ArchiveLabel(labelId, contextUser) {
|
|
69
|
+
return this.updateLabelStatus(labelId, 'Archived', contextUser);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Mark a label as having been used for a restore.
|
|
73
|
+
*/
|
|
74
|
+
async MarkLabelRestored(labelId, contextUser) {
|
|
75
|
+
return this.updateLabelStatus(labelId, 'Restored', contextUser);
|
|
76
|
+
}
|
|
77
|
+
// -----------------------------------------------------------------------
|
|
78
|
+
// Private helpers
|
|
79
|
+
// -----------------------------------------------------------------------
|
|
80
|
+
validateCreateParams(params) {
|
|
81
|
+
if (!params.Name || params.Name.trim().length === 0) {
|
|
82
|
+
throw new Error('Version label Name is required');
|
|
83
|
+
}
|
|
84
|
+
if (params.Scope === 'Entity' && !params.EntityName) {
|
|
85
|
+
throw new Error('Entity scope requires EntityName');
|
|
86
|
+
}
|
|
87
|
+
if (params.Scope === 'Record') {
|
|
88
|
+
if (!params.EntityName)
|
|
89
|
+
throw new Error('Record scope requires EntityName');
|
|
90
|
+
if (!params.RecordKey)
|
|
91
|
+
throw new Error('Record scope requires RecordKey');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
applyEntityScope(label, params, md) {
|
|
95
|
+
if (params.EntityName && (params.Scope === 'Entity' || params.Scope === 'Record')) {
|
|
96
|
+
const entityInfo = md.EntityByName(params.EntityName);
|
|
97
|
+
if (!entityInfo) {
|
|
98
|
+
throw new Error(`Entity '${params.EntityName}' not found in metadata`);
|
|
99
|
+
}
|
|
100
|
+
label.EntityID = entityInfo.ID;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
applyRecordScope(label, params) {
|
|
104
|
+
if (params.RecordKey && params.Scope === 'Record') {
|
|
105
|
+
label.RecordID = params.RecordKey.ToConcatenatedString();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async updateLabelStatus(labelId, status, contextUser) {
|
|
109
|
+
const label = await this.GetLabel(labelId, contextUser);
|
|
110
|
+
label.Status = status;
|
|
111
|
+
const saved = await label.Save();
|
|
112
|
+
if (!saved) {
|
|
113
|
+
LogError(`LabelManager: Failed to update label '${labelId}' to status '${status}'`);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
LogStatus(`VersionHistory: Label '${label.Name}' (${labelId}) status updated to ${status}`);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Build safe SQL filter clauses from a LabelFilter.
|
|
121
|
+
* Uses escapeSqlString for all user-supplied values.
|
|
122
|
+
*/
|
|
123
|
+
buildFilterClauses(filter) {
|
|
124
|
+
const clauses = [];
|
|
125
|
+
if (filter.Scope) {
|
|
126
|
+
clauses.push(sqlEquals('Scope', filter.Scope));
|
|
127
|
+
}
|
|
128
|
+
if (filter.Status) {
|
|
129
|
+
clauses.push(sqlEquals('Status', filter.Status));
|
|
130
|
+
}
|
|
131
|
+
if (filter.EntityName) {
|
|
132
|
+
const md = new Metadata();
|
|
133
|
+
const entityInfo = md.EntityByName(filter.EntityName);
|
|
134
|
+
if (entityInfo) {
|
|
135
|
+
clauses.push(sqlEquals('EntityID', entityInfo.ID));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (filter.RecordID) {
|
|
139
|
+
clauses.push(sqlEquals('RecordID', filter.RecordID));
|
|
140
|
+
}
|
|
141
|
+
if (filter.CreatedByUserID) {
|
|
142
|
+
clauses.push(sqlEquals('CreatedByUserID', filter.CreatedByUserID));
|
|
143
|
+
}
|
|
144
|
+
if (filter.NameContains) {
|
|
145
|
+
clauses.push(sqlContains('Name', filter.NameContains));
|
|
146
|
+
}
|
|
147
|
+
return clauses;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=LabelManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LabelManager.js","sourceRoot":"","sources":["../src/LabelManager.ts"],"names":[],"mappings":"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"}
|