@memberjunction/record-cloning-base 0.0.1-seed.1 → 6.2.0-edge.2
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/LICENSE +183 -0
- package/README.md +29 -3
- package/dist/CloneConfigValidator.d.ts +109 -0
- package/dist/CloneConfigValidator.d.ts.map +1 -0
- package/dist/CloneConfigValidator.js +348 -0
- package/dist/CloneConfigValidator.js.map +1 -0
- package/dist/CloneFieldMapper.d.ts +103 -0
- package/dist/CloneFieldMapper.d.ts.map +1 -0
- package/dist/CloneFieldMapper.js +369 -0
- package/dist/CloneFieldMapper.js.map +1 -0
- package/dist/ClonePlanHash.d.ts +27 -0
- package/dist/ClonePlanHash.d.ts.map +1 -0
- package/dist/ClonePlanHash.js +130 -0
- package/dist/ClonePlanHash.js.map +1 -0
- package/dist/ClonePolicyResolver.d.ts +100 -0
- package/dist/ClonePolicyResolver.d.ts.map +1 -0
- package/dist/ClonePolicyResolver.js +247 -0
- package/dist/ClonePolicyResolver.js.map +1 -0
- package/dist/EffectiveOptions.d.ts +78 -0
- package/dist/EffectiveOptions.d.ts.map +1 -0
- package/dist/EffectiveOptions.js +156 -0
- package/dist/EffectiveOptions.js.map +1 -0
- package/dist/ExclusionClasses.d.ts +27 -0
- package/dist/ExclusionClasses.d.ts.map +1 -0
- package/dist/ExclusionClasses.js +81 -0
- package/dist/ExclusionClasses.js.map +1 -0
- package/dist/FieldMeta.d.ts +15 -0
- package/dist/FieldMeta.d.ts.map +1 -0
- package/dist/FieldMeta.js +41 -0
- package/dist/FieldMeta.js.map +1 -0
- package/dist/JsonRemapEngine.d.ts +42 -0
- package/dist/JsonRemapEngine.d.ts.map +1 -0
- package/dist/JsonRemapEngine.js +251 -0
- package/dist/JsonRemapEngine.js.map +1 -0
- package/dist/NameTemplate.d.ts +56 -0
- package/dist/NameTemplate.d.ts.map +1 -0
- package/dist/NameTemplate.js +193 -0
- package/dist/NameTemplate.js.map +1 -0
- package/dist/RowExclusion.d.ts +9 -0
- package/dist/RowExclusion.d.ts.map +1 -0
- package/dist/RowExclusion.js +19 -0
- package/dist/RowExclusion.js.map +1 -0
- package/dist/SensitiveValues.d.ts +12 -0
- package/dist/SensitiveValues.d.ts.map +1 -0
- package/dist/SensitiveValues.js +25 -0
- package/dist/SensitiveValues.js.map +1 -0
- package/dist/UniqueKeyClassifier.d.ts +52 -0
- package/dist/UniqueKeyClassifier.d.ts.map +1 -0
- package/dist/UniqueKeyClassifier.js +161 -0
- package/dist/UniqueKeyClassifier.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +260 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +31 -0
- package/dist/types.js.map +1 -0
- package/package.json +28 -5
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file CloneFieldMapper.ts
|
|
3
|
+
* Pure 13-stage field mapping pipeline for record cloning.
|
|
4
|
+
* Maps: Excluded -> NotWritable -> DeniedRead/DeniedCreate -> Copy -> Reset -> Ownership
|
|
5
|
+
* -> ServerAllocated -> Rename -> Remap -> RemapJSON -> Override -> Prompt -> Rule.
|
|
6
|
+
* Rules run last, so a derived field (a user's Name from the prompted Email) sees the final
|
|
7
|
+
* values; request overrides and prompts are refused on fields a rule or reset governs.
|
|
8
|
+
* @see plans/record-cloning/README.md §7.1, §7.2, §13.1
|
|
9
|
+
*/
|
|
10
|
+
import { TryFindNextAvailableName } from './NameTemplate.js';
|
|
11
|
+
import { ApplyJsonRemap } from './JsonRemapEngine.js';
|
|
12
|
+
/** Whether the mapper renames this field on a clone: the name field, a field called Name, or a unique nvarchar. */
|
|
13
|
+
export function IsRenameField(field) {
|
|
14
|
+
return !!field.IsNameField || field.Name.toLowerCase() === 'name' || (!!field.IsUnique && field.Type === 'nvarchar');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Runs the deterministic 12-stage field transformation pipeline on a record.
|
|
18
|
+
*/
|
|
19
|
+
export function MapFieldsForClone(ctx) {
|
|
20
|
+
const changes = [];
|
|
21
|
+
const values = {};
|
|
22
|
+
const excludedOrDenied = new Set();
|
|
23
|
+
const namingFailures = [];
|
|
24
|
+
let requiresIdentitySecondPass = false;
|
|
25
|
+
const keyMap = ctx.KeyMap || {};
|
|
26
|
+
const fieldRules = ctx.FieldRules || {};
|
|
27
|
+
const fls = ctx.FLS || {};
|
|
28
|
+
// A requested new parent for a hierarchy root is a request value like any other, so it passes
|
|
29
|
+
// the same UserEditable and governed-field checks (stage 11).
|
|
30
|
+
const reqOverrides = {
|
|
31
|
+
...(ctx.RequestOverrides || {}),
|
|
32
|
+
...(ctx.IsRoot && ctx.HierarchyParentField && ctx.NewParentKey !== undefined ? { [ctx.HierarchyParentField]: ctx.NewParentKey } : {}),
|
|
33
|
+
};
|
|
34
|
+
const promptedValues = ctx.PromptedValues || {};
|
|
35
|
+
// 1. Stage 1: Excluded (PKs, CreatedAt, UpdatedAt, SoftDelete, Configured Exclude)
|
|
36
|
+
for (const field of ctx.Fields) {
|
|
37
|
+
const srcVal = ctx.SourceRecord[field.Name];
|
|
38
|
+
if (field.IsPrimaryKey) {
|
|
39
|
+
excludedOrDenied.add(field.Name);
|
|
40
|
+
changes.push({
|
|
41
|
+
Field: field.Name,
|
|
42
|
+
Kind: 'Excluded',
|
|
43
|
+
OldValue: srcVal,
|
|
44
|
+
NewValue: undefined,
|
|
45
|
+
Reason: 'Primary key is not copied from source record.',
|
|
46
|
+
});
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (field.IsCreatedAtField || field.IsUpdatedAtField || field.IsSoftDeleteField) {
|
|
50
|
+
excludedOrDenied.add(field.Name);
|
|
51
|
+
changes.push({
|
|
52
|
+
Field: field.Name,
|
|
53
|
+
Kind: 'Excluded',
|
|
54
|
+
OldValue: srcVal,
|
|
55
|
+
NewValue: undefined,
|
|
56
|
+
Reason: 'Audit or soft-delete metadata field excluded.',
|
|
57
|
+
});
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (fieldRules.Exclude && fieldRules.Exclude.includes(field.Name)) {
|
|
61
|
+
excludedOrDenied.add(field.Name);
|
|
62
|
+
changes.push({
|
|
63
|
+
Field: field.Name,
|
|
64
|
+
Kind: 'Excluded',
|
|
65
|
+
OldValue: srcVal,
|
|
66
|
+
NewValue: undefined,
|
|
67
|
+
Reason: 'Configured field exclusion.',
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// 2. Stage 2: NotWritable (Computed, virtual, persisted computed columns)
|
|
73
|
+
for (const field of ctx.Fields) {
|
|
74
|
+
if (excludedOrDenied.has(field.Name))
|
|
75
|
+
continue;
|
|
76
|
+
if (field.IsSPParameter && field.IsSPParameter(false) === false) {
|
|
77
|
+
excludedOrDenied.add(field.Name);
|
|
78
|
+
changes.push({
|
|
79
|
+
Field: field.Name,
|
|
80
|
+
Kind: 'NotWritable',
|
|
81
|
+
OldValue: ctx.SourceRecord[field.Name],
|
|
82
|
+
NewValue: undefined,
|
|
83
|
+
Reason: 'Field is not writable on insert (computed or virtual).',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// 3. Stage 3: DeniedRead / DeniedCreate (FLS evaluations)
|
|
88
|
+
for (const field of ctx.Fields) {
|
|
89
|
+
if (excludedOrDenied.has(field.Name))
|
|
90
|
+
continue;
|
|
91
|
+
if (fls.DeniedReadFields && fls.DeniedReadFields.includes(field.Name)) {
|
|
92
|
+
excludedOrDenied.add(field.Name);
|
|
93
|
+
changes.push({
|
|
94
|
+
Field: field.Name,
|
|
95
|
+
Kind: 'DeniedRead',
|
|
96
|
+
OldValue: undefined,
|
|
97
|
+
NewValue: undefined,
|
|
98
|
+
Reason: 'Field read permission denied by FLS.',
|
|
99
|
+
});
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (fls.DeniedCreateFields && fls.DeniedCreateFields.includes(field.Name)) {
|
|
103
|
+
excludedOrDenied.add(field.Name);
|
|
104
|
+
changes.push({
|
|
105
|
+
Field: field.Name,
|
|
106
|
+
Kind: 'DeniedCreate',
|
|
107
|
+
OldValue: ctx.SourceRecord[field.Name],
|
|
108
|
+
NewValue: undefined,
|
|
109
|
+
Reason: 'Field create permission denied by FLS.',
|
|
110
|
+
});
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 4. Stage 4: Copy (All eligible fields copied verbatim from source)
|
|
115
|
+
for (const field of ctx.Fields) {
|
|
116
|
+
if (excludedOrDenied.has(field.Name))
|
|
117
|
+
continue;
|
|
118
|
+
const srcVal = ctx.SourceRecord[field.Name];
|
|
119
|
+
values[field.Name] = srcVal;
|
|
120
|
+
changes.push({
|
|
121
|
+
Field: field.Name,
|
|
122
|
+
Kind: 'Copy',
|
|
123
|
+
OldValue: srcVal,
|
|
124
|
+
NewValue: srcVal,
|
|
125
|
+
Reason: 'Copied verbatim from source record.',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// 5. Stage 5: Reset (Configured literal resets)
|
|
129
|
+
if (fieldRules.Reset) {
|
|
130
|
+
for (const [fieldName, resetVal] of Object.entries(fieldRules.Reset)) {
|
|
131
|
+
if (excludedOrDenied.has(fieldName))
|
|
132
|
+
continue;
|
|
133
|
+
const oldVal = values[fieldName];
|
|
134
|
+
values[fieldName] = resetVal;
|
|
135
|
+
changes.push({
|
|
136
|
+
Field: fieldName,
|
|
137
|
+
Kind: 'Reset',
|
|
138
|
+
OldValue: oldVal,
|
|
139
|
+
NewValue: resetVal,
|
|
140
|
+
Reason: 'Configured field reset stamped.',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// 6. Stage 6: Ownership (Assign owner to current cloning user)
|
|
145
|
+
if (fieldRules.Ownership && ctx.CurrentUserId) {
|
|
146
|
+
for (const fieldName of fieldRules.Ownership) {
|
|
147
|
+
if (excludedOrDenied.has(fieldName))
|
|
148
|
+
continue;
|
|
149
|
+
const oldVal = values[fieldName];
|
|
150
|
+
values[fieldName] = ctx.CurrentUserId;
|
|
151
|
+
changes.push({
|
|
152
|
+
Field: fieldName,
|
|
153
|
+
Kind: 'Ownership',
|
|
154
|
+
OldValue: oldVal,
|
|
155
|
+
NewValue: ctx.CurrentUserId,
|
|
156
|
+
Reason: 'Ownership assigned to current user.',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// 7. Stage 7: ServerAllocated (Blanked so Save() hook mints)
|
|
161
|
+
if (fieldRules.ServerAllocated) {
|
|
162
|
+
for (const fieldName of fieldRules.ServerAllocated) {
|
|
163
|
+
if (excludedOrDenied.has(fieldName))
|
|
164
|
+
continue;
|
|
165
|
+
const oldVal = values[fieldName];
|
|
166
|
+
values[fieldName] = null;
|
|
167
|
+
changes.push({
|
|
168
|
+
Field: fieldName,
|
|
169
|
+
Kind: 'Reset',
|
|
170
|
+
OldValue: oldVal,
|
|
171
|
+
NewValue: null,
|
|
172
|
+
Reason: 'Server-allocated field blanked for Save() hook minting.',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// 8. Stage 8: Rename (Name fields or unique string fields)
|
|
177
|
+
for (const field of ctx.Fields) {
|
|
178
|
+
if (excludedOrDenied.has(field.Name))
|
|
179
|
+
continue;
|
|
180
|
+
if (IsRenameField(field) && values[field.Name] !== undefined && values[field.Name] !== null) {
|
|
181
|
+
const oldName = String(values[field.Name]);
|
|
182
|
+
const newName = TryFindNextAvailableName(oldName, ctx.NamingOptions?.ExistingNamesByField?.[field.Name] ?? ctx.NamingOptions?.ExistingNames ?? new Set(), {
|
|
183
|
+
Template: ctx.NamingOptions?.Template,
|
|
184
|
+
Strategy: ctx.NamingOptions?.Strategy,
|
|
185
|
+
MaxLength: field.MaxLength,
|
|
186
|
+
Context: {
|
|
187
|
+
UserName: ctx.NamingOptions?.UserName,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
if (newName === null) {
|
|
191
|
+
namingFailures.push(field.Name);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (newName !== oldName) {
|
|
195
|
+
values[field.Name] = newName;
|
|
196
|
+
changes.push({
|
|
197
|
+
Field: field.Name,
|
|
198
|
+
Kind: 'Rename',
|
|
199
|
+
OldValue: oldName,
|
|
200
|
+
NewValue: newName,
|
|
201
|
+
Reason: 'Name or unique string renamed to prevent collision.',
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// 9. Stage 9: Remap (Foreign keys pointing to records in the clone set)
|
|
207
|
+
for (const field of ctx.Fields) {
|
|
208
|
+
if (excludedOrDenied.has(field.Name))
|
|
209
|
+
continue;
|
|
210
|
+
// Hierarchy parent field
|
|
211
|
+
if (ctx.HierarchyParentField && field.Name === ctx.HierarchyParentField) {
|
|
212
|
+
const oldParent = values[field.Name];
|
|
213
|
+
if (ctx.IsRoot) {
|
|
214
|
+
// The copy stays under the same parent; a requested move (NewParentKey, null = top
|
|
215
|
+
// level) is applied with the request overrides in stage 11.
|
|
216
|
+
}
|
|
217
|
+
else if (oldParent && String(oldParent) in keyMap) {
|
|
218
|
+
const newParent = keyMap[String(oldParent)];
|
|
219
|
+
values[field.Name] = newParent;
|
|
220
|
+
changes.push({
|
|
221
|
+
Field: field.Name,
|
|
222
|
+
Kind: 'Remap',
|
|
223
|
+
OldValue: oldParent,
|
|
224
|
+
NewValue: newParent,
|
|
225
|
+
Reason: 'Hierarchy parent remapped to cloned ancestor.',
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
// Standard Foreign Key
|
|
231
|
+
if (field.RelatedEntityID || field.RelatedEntity) {
|
|
232
|
+
const oldFk = values[field.Name];
|
|
233
|
+
if (oldFk !== null && oldFk !== undefined) {
|
|
234
|
+
const targetEntity = field.RelatedEntity;
|
|
235
|
+
const qualifiedKey = targetEntity ? `${targetEntity}::${String(oldFk)}` : null;
|
|
236
|
+
const newFk = (qualifiedKey && qualifiedKey in keyMap)
|
|
237
|
+
? keyMap[qualifiedKey]
|
|
238
|
+
: (String(oldFk) in keyMap ? keyMap[String(oldFk)] : undefined);
|
|
239
|
+
if (newFk !== undefined && newFk !== oldFk) {
|
|
240
|
+
values[field.Name] = newFk;
|
|
241
|
+
changes.push({
|
|
242
|
+
Field: field.Name,
|
|
243
|
+
Kind: 'Remap',
|
|
244
|
+
OldValue: oldFk,
|
|
245
|
+
NewValue: newFk,
|
|
246
|
+
Reason: `Foreign key to '${targetEntity || 'entity'}' remapped to cloned target.`,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// 10. Stage 10: RemapJSON (JSON columns with remap configurations)
|
|
253
|
+
if (fieldRules.JsonRemap) {
|
|
254
|
+
for (const remapSpec of fieldRules.JsonRemap) {
|
|
255
|
+
if (excludedOrDenied.has(remapSpec.Field))
|
|
256
|
+
continue;
|
|
257
|
+
const currentVal = values[remapSpec.Field];
|
|
258
|
+
if (typeof currentVal === 'string' || (currentVal && typeof currentVal === 'object')) {
|
|
259
|
+
const remapRes = ApplyJsonRemap(currentVal, {
|
|
260
|
+
Rules: remapSpec.Rules,
|
|
261
|
+
Preset: remapSpec.Preset,
|
|
262
|
+
KeyMap: keyMap,
|
|
263
|
+
});
|
|
264
|
+
if (remapRes.Success) {
|
|
265
|
+
values[remapSpec.Field] = remapRes.JsonString;
|
|
266
|
+
changes.push({
|
|
267
|
+
Field: remapSpec.Field,
|
|
268
|
+
Kind: 'RemapJSON',
|
|
269
|
+
OldValue: currentVal,
|
|
270
|
+
NewValue: remapRes.JsonString,
|
|
271
|
+
Reason: `JSON remapped: ${remapRes.PathsModified.length} paths touched (${remapRes.DropCount} dropped).`,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Fields the configuration decides; a request may not change them (that would undo a reset
|
|
278
|
+
// such as Type or IsActive on a user clone, or an ownership stamp).
|
|
279
|
+
const ignored = [];
|
|
280
|
+
const governed = new Set([
|
|
281
|
+
...Object.keys(fieldRules.Reset ?? {}),
|
|
282
|
+
...(fieldRules.Ownership ?? []),
|
|
283
|
+
...(fieldRules.ServerAllocated ?? []),
|
|
284
|
+
...(fieldRules.Rules?.Rules ?? []).map((r) => r.TargetField || r.Field || '').filter(Boolean),
|
|
285
|
+
]);
|
|
286
|
+
const promptFor = new Set(fieldRules.PromptFor ?? []);
|
|
287
|
+
// 11. Stage 11: Override (Request-level field overrides)
|
|
288
|
+
for (const [fieldName, overrideVal] of Object.entries(reqOverrides)) {
|
|
289
|
+
if (excludedOrDenied.has(fieldName))
|
|
290
|
+
continue;
|
|
291
|
+
if (ctx.RequestFieldsEditable === false) {
|
|
292
|
+
ignored.push({ Field: fieldName, Kind: 'Override', Reason: "this entity's clone configuration doesn't allow field edits (UserEditable)" });
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (governed.has(fieldName)) {
|
|
296
|
+
ignored.push({ Field: fieldName, Kind: 'Override', Reason: 'the clone configuration sets this field' });
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
const oldVal = values[fieldName];
|
|
300
|
+
values[fieldName] = overrideVal;
|
|
301
|
+
changes.push({
|
|
302
|
+
Field: fieldName,
|
|
303
|
+
Kind: 'Override',
|
|
304
|
+
OldValue: oldVal,
|
|
305
|
+
NewValue: overrideVal,
|
|
306
|
+
Reason: 'Explicit request-level field override.',
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
// 12. Stage 12: Prompt (Prompted values, for the configured PromptFor fields only)
|
|
310
|
+
for (const [fieldName, promptVal] of Object.entries(promptedValues)) {
|
|
311
|
+
if (excludedOrDenied.has(fieldName))
|
|
312
|
+
continue;
|
|
313
|
+
if (!promptFor.has(fieldName)) {
|
|
314
|
+
ignored.push({ Field: fieldName, Kind: 'Prompt', Reason: 'the clone configuration does not prompt for this field' });
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const oldVal = values[fieldName];
|
|
318
|
+
values[fieldName] = promptVal;
|
|
319
|
+
changes.push({
|
|
320
|
+
Field: fieldName,
|
|
321
|
+
Kind: 'Prompt',
|
|
322
|
+
OldValue: oldVal,
|
|
323
|
+
NewValue: promptVal,
|
|
324
|
+
Reason: 'User prompted value applied.',
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
// 13. Stage 13: Rules (Derived field rules, e.g. Name = Email)
|
|
328
|
+
if (fieldRules.Rules?.Rules) {
|
|
329
|
+
for (const rule of fieldRules.Rules.Rules) {
|
|
330
|
+
const targetField = rule.TargetField || rule.Field;
|
|
331
|
+
if (!targetField || excludedOrDenied.has(targetField))
|
|
332
|
+
continue;
|
|
333
|
+
let newVal;
|
|
334
|
+
if (rule.Source?.Kind === 'field' && rule.Source.Field) {
|
|
335
|
+
newVal = values[rule.Source.Field] ?? ctx.SourceRecord[rule.Source.Field];
|
|
336
|
+
}
|
|
337
|
+
else if (rule.Source?.Kind === 'static') {
|
|
338
|
+
newVal = rule.Source.Value;
|
|
339
|
+
}
|
|
340
|
+
if (newVal !== undefined) {
|
|
341
|
+
const oldVal = values[targetField];
|
|
342
|
+
values[targetField] = newVal;
|
|
343
|
+
changes.push({
|
|
344
|
+
Field: targetField,
|
|
345
|
+
Kind: 'Rule',
|
|
346
|
+
OldValue: oldVal,
|
|
347
|
+
NewValue: newVal,
|
|
348
|
+
Reason: 'Derived via field rule.',
|
|
349
|
+
// A value copied from an encrypted column is as secret as its source.
|
|
350
|
+
...(rule.Source?.Kind === 'field' && ctx.Fields.some((f) => f.Encrypted && f.Name === rule.Source?.Field) ? { Sensitive: true } : {}),
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Values of encrypted fields stay in the server-side plan, but are masked on the wire, in logs and in the hash.
|
|
356
|
+
const encrypted = new Set(ctx.Fields.filter((f) => f.Encrypted).map((f) => f.Name));
|
|
357
|
+
for (const change of changes) {
|
|
358
|
+
if (encrypted.has(change.Field))
|
|
359
|
+
change.Sensitive = true;
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
FieldChanges: changes,
|
|
363
|
+
MappedValues: values,
|
|
364
|
+
RequiresIdentitySecondPass: requiresIdentitySecondPass,
|
|
365
|
+
IgnoredRequestValues: ignored,
|
|
366
|
+
NamingFailures: namingFailures,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
//# sourceMappingURL=CloneFieldMapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloneFieldMapper.js","sourceRoot":"","sources":["../src/CloneFieldMapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ClonePlanHash.ts
|
|
3
|
+
* Canonical form of a ClonePlan's topology and transformations, for hashing.
|
|
4
|
+
* Stable across key ordering and ignores pre-minted TargetKey values. The engine hashes it with
|
|
5
|
+
* SHA-256 (`node:crypto`); this package stays dependency-free and client-safe.
|
|
6
|
+
* @see plans/record-cloning/README.md §3.5, §13.1
|
|
7
|
+
*/
|
|
8
|
+
import { ClonePlanEdge, ClonePlanNode } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Input subset of ClonePlan required for computing the canonical hash.
|
|
11
|
+
*/
|
|
12
|
+
export interface ClonePlanHashInput {
|
|
13
|
+
Nodes: ClonePlanNode[];
|
|
14
|
+
Edges: ClonePlanEdge[];
|
|
15
|
+
Excluded?: unknown[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The canonical string a plan hash is computed over.
|
|
19
|
+
*
|
|
20
|
+
* - Covers every node's (EntityName, SourceKey, Action)
|
|
21
|
+
* - Covers every edge's (FromKey, ToKey, Policy)
|
|
22
|
+
* - Covers every FieldChange (Field, Kind, OldValue, NewValue, Reason), with encrypted values masked
|
|
23
|
+
* - EXCLUDES TargetKey values (so re-planning does not drift on pre-minted UUIDs)
|
|
24
|
+
* - Immune to array ordering of nodes, edges, or field changes
|
|
25
|
+
*/
|
|
26
|
+
export declare function CanonicalPlanPayload(plan: ClonePlanHashInput): string;
|
|
27
|
+
//# sourceMappingURL=ClonePlanHash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClonePlanHash.d.ts","sourceRoot":"","sources":["../src/ClonePlanHash.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEH,aAAa,EACb,aAAa,EAGhB,MAAM,SAAS,CAAC;AA+DjB;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAmDrE"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ClonePlanHash.ts
|
|
3
|
+
* Canonical form of a ClonePlan's topology and transformations, for hashing.
|
|
4
|
+
* Stable across key ordering and ignores pre-minted TargetKey values. The engine hashes it with
|
|
5
|
+
* SHA-256 (`node:crypto`); this package stays dependency-free and client-safe.
|
|
6
|
+
* @see plans/record-cloning/README.md §3.5, §13.1
|
|
7
|
+
*/
|
|
8
|
+
import { FormatCompositeKey, } from './types.js';
|
|
9
|
+
import { MaskSensitiveFieldChange } from './SensitiveValues.js';
|
|
10
|
+
/** The key values of a key given as pairs, a record-id segment ("F|V||F|V") or a bare value. */
|
|
11
|
+
function keyValues(key) {
|
|
12
|
+
if (!key)
|
|
13
|
+
return [];
|
|
14
|
+
if (typeof key !== 'string')
|
|
15
|
+
return (key.KeyValuePairs ?? []).map((p) => String(p.Value ?? ''));
|
|
16
|
+
if (!key.includes('|'))
|
|
17
|
+
return [key];
|
|
18
|
+
return key.split('||').map((pair) => pair.slice(pair.indexOf('|') + 1));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Tokens for the key values a plan mints (a new UUID, a remapped key column): each maps to a stable
|
|
22
|
+
* name for the row it belongs to. Without this, remapped foreign keys would change the hash on every
|
|
23
|
+
* re-plan, and every Execute of an unchanged plan would come back PLAN_CHANGED.
|
|
24
|
+
*/
|
|
25
|
+
function mintedKeyTokens(nodes) {
|
|
26
|
+
const tokens = new Map();
|
|
27
|
+
// Visit rows in a fixed order and keep the first name a value gets: when sibling keys share a
|
|
28
|
+
// minted value (a composite key holding the parent's new ID), the token must not depend on the
|
|
29
|
+
// order the walker loaded the siblings in.
|
|
30
|
+
const named = nodes
|
|
31
|
+
.map((n) => ({ n, name: `${n.EntityName}::${FormatCompositeKey(n.SourceKey)}` }))
|
|
32
|
+
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
33
|
+
for (const { n, name } of named) {
|
|
34
|
+
const source = new Set(keyValues(n.SourceKey).map((v) => v.toLowerCase()));
|
|
35
|
+
keyValues(n.TargetKey).forEach((value, i) => {
|
|
36
|
+
const k = value?.toLowerCase();
|
|
37
|
+
if (k && !source.has(k) && !tokens.has(k))
|
|
38
|
+
tokens.set(k, `@new(${name}#${i})`);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return tokens;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Deterministically stringifies an arbitrary value for canonical hashing, with minted key values
|
|
45
|
+
* (also inside strings, such as remapped JSON) replaced by their stable tokens.
|
|
46
|
+
*/
|
|
47
|
+
function canonicalizeValue(val, tokens, pattern) {
|
|
48
|
+
if (val === null || val === undefined) {
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
if (typeof val === 'object') {
|
|
52
|
+
if (val instanceof Date) {
|
|
53
|
+
return val.toISOString();
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(val)) {
|
|
56
|
+
return `[${val.map((v) => canonicalizeValue(v, tokens, pattern)).join(',')}]`;
|
|
57
|
+
}
|
|
58
|
+
const obj = val;
|
|
59
|
+
const keys = Object.keys(obj).sort();
|
|
60
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalizeValue(obj[k], tokens, pattern)}`).join(',')}}`;
|
|
61
|
+
}
|
|
62
|
+
if (typeof val === 'string' && pattern) {
|
|
63
|
+
return JSON.stringify(val.replace(pattern, (m) => tokens.get(m.toLowerCase()) ?? m));
|
|
64
|
+
}
|
|
65
|
+
return JSON.stringify(val);
|
|
66
|
+
}
|
|
67
|
+
function escapeRegExp(text) {
|
|
68
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The canonical string a plan hash is computed over.
|
|
72
|
+
*
|
|
73
|
+
* - Covers every node's (EntityName, SourceKey, Action)
|
|
74
|
+
* - Covers every edge's (FromKey, ToKey, Policy)
|
|
75
|
+
* - Covers every FieldChange (Field, Kind, OldValue, NewValue, Reason), with encrypted values masked
|
|
76
|
+
* - EXCLUDES TargetKey values (so re-planning does not drift on pre-minted UUIDs)
|
|
77
|
+
* - Immune to array ordering of nodes, edges, or field changes
|
|
78
|
+
*/
|
|
79
|
+
export function CanonicalPlanPayload(plan) {
|
|
80
|
+
const tokens = mintedKeyTokens(plan.Nodes);
|
|
81
|
+
const pattern = tokens.size > 0 ? new RegExp([...tokens.keys()].sort((a, b) => b.length - a.length).map(escapeRegExp).join('|'), 'gi') : null;
|
|
82
|
+
// 1. Canonicalize and sort nodes
|
|
83
|
+
const sortedNodes = [...plan.Nodes]
|
|
84
|
+
.map((n) => {
|
|
85
|
+
const sortedChanges = [...n.FieldChanges]
|
|
86
|
+
.sort((a, b) => a.Field.localeCompare(b.Field))
|
|
87
|
+
.map(MaskSensitiveFieldChange)
|
|
88
|
+
.map((fc) => ({
|
|
89
|
+
Field: fc.Field,
|
|
90
|
+
Kind: fc.Kind,
|
|
91
|
+
OldValue: canonicalizeValue(fc.OldValue, tokens, pattern),
|
|
92
|
+
NewValue: canonicalizeValue(fc.NewValue, tokens, pattern),
|
|
93
|
+
Reason: fc.Reason || '',
|
|
94
|
+
}));
|
|
95
|
+
return {
|
|
96
|
+
EntityName: n.EntityName,
|
|
97
|
+
SourceKey: FormatCompositeKey(n.SourceKey),
|
|
98
|
+
Action: n.Action,
|
|
99
|
+
FieldChanges: sortedChanges,
|
|
100
|
+
};
|
|
101
|
+
})
|
|
102
|
+
.sort((a, b) => {
|
|
103
|
+
const cmp = a.EntityName.localeCompare(b.EntityName);
|
|
104
|
+
if (cmp !== 0)
|
|
105
|
+
return cmp;
|
|
106
|
+
return a.SourceKey.localeCompare(b.SourceKey);
|
|
107
|
+
});
|
|
108
|
+
// 2. Canonicalize and sort edges
|
|
109
|
+
const sortedEdges = [...plan.Edges]
|
|
110
|
+
.map((e) => ({
|
|
111
|
+
FromKey: e.FromKey,
|
|
112
|
+
ToKey: e.ToKey,
|
|
113
|
+
Policy: e.Policy,
|
|
114
|
+
}))
|
|
115
|
+
.sort((a, b) => {
|
|
116
|
+
const cmp = a.FromKey.localeCompare(b.FromKey);
|
|
117
|
+
if (cmp !== 0)
|
|
118
|
+
return cmp;
|
|
119
|
+
const cmp2 = a.ToKey.localeCompare(b.ToKey);
|
|
120
|
+
if (cmp2 !== 0)
|
|
121
|
+
return cmp2;
|
|
122
|
+
return a.Policy.localeCompare(b.Policy);
|
|
123
|
+
});
|
|
124
|
+
// 3. Form canonical string
|
|
125
|
+
return JSON.stringify({
|
|
126
|
+
Nodes: sortedNodes,
|
|
127
|
+
Edges: sortedEdges,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=ClonePlanHash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClonePlanHash.js","sourceRoot":"","sources":["../src/ClonePlanHash.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ClonePolicyResolver.ts
|
|
3
|
+
* 8-tier precedence hierarchy for clone edge policy resolution.
|
|
4
|
+
* Evaluates Built-in -> Constraint -> Child Entity -> Relationship -> Root Entity -> Preset -> Request.
|
|
5
|
+
* Enforces:
|
|
6
|
+
* - A child entity marked NotCloneable (or AllowCreateAPI=false) is skipped everywhere, unconditionally.
|
|
7
|
+
* - Name heuristics (runs, logs, per-user state) skip a child only when no configuration names that edge.
|
|
8
|
+
* - Unlisted relationships are Skip: each configuration opts in to what it copies. There are no
|
|
9
|
+
* entity-specific defaults.
|
|
10
|
+
* - A self-relationship is Deep only when its join column is a hierarchy (IsHierarchy) field.
|
|
11
|
+
* - A unique FK makes Reference impossible, so it turns Reference into Deep; it never overrides Skip.
|
|
12
|
+
* - Locked edges ignore preset and request overrides (LOCKED_EDGE_OVERRIDE_IGNORED).
|
|
13
|
+
* @see plans/record-cloning/README.md §4.4, §5.3–§5.5, §13.1
|
|
14
|
+
*/
|
|
15
|
+
import { CloneEdgeKind, CloneEdgePolicy, CloneWarning } from './types.js';
|
|
16
|
+
export interface EdgePolicyResolutionContext {
|
|
17
|
+
FromKey: string;
|
|
18
|
+
ToKey: string;
|
|
19
|
+
Kind: CloneEdgeKind;
|
|
20
|
+
ParentEntityName: string;
|
|
21
|
+
ChildEntityName: string;
|
|
22
|
+
/**
|
|
23
|
+
* The clone root's entity. A bare child-entity key in the root's `Relationships` applies only to
|
|
24
|
+
* edges from this entity; below it, only relationship-ID and `Child.JoinField` keys match. Left out,
|
|
25
|
+
* the edge is taken to start at the root.
|
|
26
|
+
*/
|
|
27
|
+
RootEntityName?: string;
|
|
28
|
+
JoinField: string;
|
|
29
|
+
RelationshipID?: string;
|
|
30
|
+
CollectionName?: string;
|
|
31
|
+
IsSoftLink?: boolean;
|
|
32
|
+
CurrentDepth: number;
|
|
33
|
+
MaxDepth: number;
|
|
34
|
+
IsUniqueFK?: boolean;
|
|
35
|
+
/** The child's join column is flagged IsHierarchy (a parent/child tree within one entity). */
|
|
36
|
+
IsHierarchyField?: boolean;
|
|
37
|
+
IsWriteOnce?: boolean;
|
|
38
|
+
ChildEntityConfig?: {
|
|
39
|
+
NotCloneable?: boolean;
|
|
40
|
+
NotCloneableReason?: string;
|
|
41
|
+
AllowCreateAPI?: boolean;
|
|
42
|
+
};
|
|
43
|
+
RelationshipConfig?: {
|
|
44
|
+
Policy?: CloneEdgePolicy;
|
|
45
|
+
Locked?: boolean;
|
|
46
|
+
};
|
|
47
|
+
RootEntityConfig?: {
|
|
48
|
+
Relationships?: Record<string, {
|
|
49
|
+
Policy?: CloneEdgePolicy;
|
|
50
|
+
Locked?: boolean;
|
|
51
|
+
}>;
|
|
52
|
+
Descendants?: Record<string, {
|
|
53
|
+
Policy?: CloneEdgePolicy;
|
|
54
|
+
Locked?: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The `Relationships` of the entity the edge starts from, for edges below the root: a prompt's
|
|
59
|
+
* template follows `MJ: Templates`' own configuration. The root's configuration still wins.
|
|
60
|
+
*/
|
|
61
|
+
ParentEntityConfig?: {
|
|
62
|
+
Relationships?: Record<string, {
|
|
63
|
+
Policy?: CloneEdgePolicy;
|
|
64
|
+
Locked?: boolean;
|
|
65
|
+
}>;
|
|
66
|
+
};
|
|
67
|
+
PresetConfig?: {
|
|
68
|
+
EdgeOverrides?: Array<{
|
|
69
|
+
RelationshipID?: string;
|
|
70
|
+
ChildEntityName?: string;
|
|
71
|
+
Policy: CloneEdgePolicy;
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
74
|
+
RequestOverrides?: Array<{
|
|
75
|
+
FromKey?: string;
|
|
76
|
+
ToKey?: string;
|
|
77
|
+
RelationshipID?: string;
|
|
78
|
+
Policy: CloneEdgePolicy;
|
|
79
|
+
}>;
|
|
80
|
+
AllowUserOverrides?: boolean;
|
|
81
|
+
}
|
|
82
|
+
export interface EdgePolicyResolutionResult {
|
|
83
|
+
Policy: CloneEdgePolicy;
|
|
84
|
+
PolicySource: 'BuiltIn' | 'Constraint' | 'Entity' | 'Relationship' | 'Descendant' | 'Request';
|
|
85
|
+
Locked: boolean;
|
|
86
|
+
Warnings: CloneWarning[];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolves the edge traversal policy according to the 8-tier precedence hierarchy.
|
|
90
|
+
*/
|
|
91
|
+
export declare function ResolveEdgePolicy(ctx: EdgePolicyResolutionContext): EdgePolicyResolutionResult;
|
|
92
|
+
/** Whether an edge from `parentEntityName` starts at the root's entity (ignoring case); true when the root isn't given. */
|
|
93
|
+
export declare function IsEdgeFromRoot(parentEntityName: string, rootEntityName: string | undefined | null): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* An entry of a `Relationships` or `Descendants` bag by key, ignoring case, as the validator
|
|
96
|
+
* matches keys: a miscased column (`AgentId` for `AgentID`) must not validate clean and then
|
|
97
|
+
* silently fall back to the default policy.
|
|
98
|
+
*/
|
|
99
|
+
export declare function BagEntry<T>(bag: Record<string, T> | undefined | null, key: string | undefined | null): T | undefined;
|
|
100
|
+
//# sourceMappingURL=ClonePolicyResolver.d.ts.map
|