@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.
@@ -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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"}