@memberjunction/record-cloning-base 0.0.0 → 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 +28 -43
- 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 +31 -8
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file RowExclusion.ts
|
|
3
|
+
* Matches child rows against a relationship's `ExcludeRows` rules. Used to leave out rows that
|
|
4
|
+
* belong to one person or device (push tokens, drafts, consent) even when the edge is Deep.
|
|
5
|
+
*/
|
|
6
|
+
import type { ICloneRowExclusion } from '@memberjunction/core';
|
|
7
|
+
/** True when `row` matches any rule: its field equals one of `Equals`, or starts with one of `StartsWith`. */
|
|
8
|
+
export declare function RowMatchesExclusion(row: Record<string, unknown>, rules: ICloneRowExclusion[] | undefined): boolean;
|
|
9
|
+
//# sourceMappingURL=RowExclusion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RowExclusion.d.ts","sourceRoot":"","sources":["../src/RowExclusion.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,8GAA8G;AAC9G,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,GAAG,SAAS,GAAG,OAAO,CAQlH"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file RowExclusion.ts
|
|
3
|
+
* Matches child rows against a relationship's `ExcludeRows` rules. Used to leave out rows that
|
|
4
|
+
* belong to one person or device (push tokens, drafts, consent) even when the edge is Deep.
|
|
5
|
+
*/
|
|
6
|
+
/** True when `row` matches any rule: its field equals one of `Equals`, or starts with one of `StartsWith`. */
|
|
7
|
+
export function RowMatchesExclusion(row, rules) {
|
|
8
|
+
if (!rules?.length)
|
|
9
|
+
return false;
|
|
10
|
+
return rules.some((rule) => {
|
|
11
|
+
const value = row[rule.Field];
|
|
12
|
+
if (value === null || value === undefined)
|
|
13
|
+
return false;
|
|
14
|
+
if (rule.Equals?.some((v) => v === value))
|
|
15
|
+
return true;
|
|
16
|
+
return typeof value === 'string' && (rule.StartsWith ?? []).some((prefix) => value.startsWith(prefix));
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=RowExclusion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RowExclusion.js","sourceRoot":"","sources":["../src/RowExclusion.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,8GAA8G;AAC9G,MAAM,UAAU,mBAAmB,CAAC,GAA4B,EAAE,KAAuC;IACrG,IAAI,CAAC,KAAK,EAAE,MAAM;QAAE,OAAO,KAAK,CAAC;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACxD,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACvD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3G,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file SensitiveValues.ts
|
|
3
|
+
* Masks encrypted field values in a clone plan. The server keeps the real values so Execute can
|
|
4
|
+
* write them; every copy of the plan that leaves the engine (the Plan/Execute output, the clone
|
|
5
|
+
* log's PlanJSON, the plan hash) goes through these helpers first.
|
|
6
|
+
*/
|
|
7
|
+
import type { CloneFieldChange, ClonePlan } from './types.js';
|
|
8
|
+
/** A copy of `change` with its values masked when the field is encrypted; otherwise `change` itself. */
|
|
9
|
+
export declare function MaskSensitiveFieldChange(change: CloneFieldChange): CloneFieldChange;
|
|
10
|
+
/** A copy of `plan` safe to return or store: every encrypted field value is masked. */
|
|
11
|
+
export declare function MaskSensitivePlan(plan: ClonePlan): ClonePlan;
|
|
12
|
+
//# sourceMappingURL=SensitiveValues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SensitiveValues.d.ts","sourceRoot":"","sources":["../src/SensitiveValues.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAO3D,wGAAwG;AACxG,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAGnF;AAED,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAK5D"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file SensitiveValues.ts
|
|
3
|
+
* Masks encrypted field values in a clone plan. The server keeps the real values so Execute can
|
|
4
|
+
* write them; every copy of the plan that leaves the engine (the Plan/Execute output, the clone
|
|
5
|
+
* log's PlanJSON, the plan hash) goes through these helpers first.
|
|
6
|
+
*/
|
|
7
|
+
import { ENCRYPTED_SENTINEL } from '@memberjunction/global';
|
|
8
|
+
/** Replaces a non-empty value with the sentinel MJ uses for encrypted fields on the wire. */
|
|
9
|
+
function mask(value) {
|
|
10
|
+
return value === null || value === undefined ? value : ENCRYPTED_SENTINEL;
|
|
11
|
+
}
|
|
12
|
+
/** A copy of `change` with its values masked when the field is encrypted; otherwise `change` itself. */
|
|
13
|
+
export function MaskSensitiveFieldChange(change) {
|
|
14
|
+
if (!change.Sensitive)
|
|
15
|
+
return change;
|
|
16
|
+
return { ...change, OldValue: mask(change.OldValue), NewValue: mask(change.NewValue) };
|
|
17
|
+
}
|
|
18
|
+
/** A copy of `plan` safe to return or store: every encrypted field value is masked. */
|
|
19
|
+
export function MaskSensitivePlan(plan) {
|
|
20
|
+
return {
|
|
21
|
+
...plan,
|
|
22
|
+
Nodes: plan.Nodes.map((n) => ({ ...n, FieldChanges: (n.FieldChanges ?? []).map(MaskSensitiveFieldChange) })),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=SensitiveValues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SensitiveValues.js","sourceRoot":"","sources":["../src/SensitiveValues.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,6FAA6F;AAC7F,SAAS,IAAI,CAAC,KAAc;IACxB,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC;AAC9E,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,wBAAwB,CAAC,MAAwB;IAC7D,IAAI,CAAC,MAAM,CAAC,SAAS;QAAE,OAAO,MAAM,CAAC;IACrC,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC3F,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,iBAAiB,CAAC,IAAe;IAC7C,OAAO;QACH,GAAG,IAAI;QACP,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC;KAC/G,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file UniqueKeyClassifier.ts
|
|
3
|
+
* Classification, validation, and collision detection for unique keys.
|
|
4
|
+
* Handles global unique keys, parent-scoped uniques, live-state uniques, and server-allocated fields.
|
|
5
|
+
* @see plans/record-cloning/README.md §7.3, §13.1
|
|
6
|
+
*/
|
|
7
|
+
import { CloneWarning } from './types.js';
|
|
8
|
+
export type UniqueKeyClassification = 'GlobalString' | 'GlobalNonString' | 'ParentScoped' | 'LiveState' | 'ServerAllocated';
|
|
9
|
+
export interface UniqueKeyDefinition {
|
|
10
|
+
Fields: string[];
|
|
11
|
+
Scope?: 'Global' | 'Parent' | 'LiveState';
|
|
12
|
+
ScopeField?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface UniqueKeyResolutionRequest {
|
|
15
|
+
EntityName: string;
|
|
16
|
+
KeyDef: UniqueKeyDefinition;
|
|
17
|
+
IsStringField?: (fieldName: string) => boolean;
|
|
18
|
+
ServerAllocatedFields?: string[];
|
|
19
|
+
PromptForFields?: string[];
|
|
20
|
+
PromptedValues?: Record<string, unknown>;
|
|
21
|
+
ResetFields?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export interface UniqueKeyResolutionResult {
|
|
24
|
+
Classification: UniqueKeyClassification;
|
|
25
|
+
IsResolved: boolean;
|
|
26
|
+
ActionRequired?: 'Rename' | 'Reset' | 'Prompt' | 'VerifyParent' | 'None';
|
|
27
|
+
Blocked: boolean;
|
|
28
|
+
Warning?: CloneWarning;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Classifies a unique key and determines how it will be resolved during cloning.
|
|
32
|
+
*/
|
|
33
|
+
export declare function ClassifyAndResolveUniqueKey(req: UniqueKeyResolutionRequest): UniqueKeyResolutionResult;
|
|
34
|
+
export interface PlannedRecordNode {
|
|
35
|
+
NodeKey: string;
|
|
36
|
+
EntityName: string;
|
|
37
|
+
ParentKey?: string | null;
|
|
38
|
+
Values: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
export interface IntraPlanCollision {
|
|
41
|
+
NodeKeyA: string;
|
|
42
|
+
NodeKeyB: string;
|
|
43
|
+
EntityName: string;
|
|
44
|
+
Fields: string[];
|
|
45
|
+
Values: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Pre-validates planned rows to detect if two rows under the same parent
|
|
49
|
+
* (or across global scope) have colliding planned values.
|
|
50
|
+
*/
|
|
51
|
+
export declare function DetectIntraPlanCollisions(nodes: PlannedRecordNode[], uniqueKeysByEntity?: Record<string, UniqueKeyDefinition[]>): IntraPlanCollision[];
|
|
52
|
+
//# sourceMappingURL=UniqueKeyClassifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UniqueKeyClassifier.d.ts","sourceRoot":"","sources":["../src/UniqueKeyClassifier.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAoB,MAAM,SAAS,CAAC;AAEzD,MAAM,MAAM,uBAAuB,GAC7B,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,WAAW,GACX,iBAAiB,CAAC;AAExB,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/C,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,yBAAyB;IACtC,cAAc,EAAE,uBAAuB,CAAC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,cAAc,GAAG,MAAM,CAAC;IACzE,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,YAAY,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACvC,GAAG,EAAE,0BAA0B,GAChC,yBAAyB,CAgH3B;AAED,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACrC,KAAK,EAAE,iBAAiB,EAAE,EAC1B,kBAAkB,GAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,EAAE,CAAM,GAC/D,kBAAkB,EAAE,CA6DtB"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file UniqueKeyClassifier.ts
|
|
3
|
+
* Classification, validation, and collision detection for unique keys.
|
|
4
|
+
* Handles global unique keys, parent-scoped uniques, live-state uniques, and server-allocated fields.
|
|
5
|
+
* @see plans/record-cloning/README.md §7.3, §13.1
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Classifies a unique key and determines how it will be resolved during cloning.
|
|
9
|
+
*/
|
|
10
|
+
export function ClassifyAndResolveUniqueKey(req) {
|
|
11
|
+
const { EntityName, KeyDef, IsStringField, ServerAllocatedFields = [], PromptForFields = [], PromptedValues = {}, ResetFields = {}, } = req;
|
|
12
|
+
// 1. Server-Allocated fields (e.g. OrderNumber, DealNumber)
|
|
13
|
+
const isServerAllocated = KeyDef.Fields.some((f) => ServerAllocatedFields.includes(f));
|
|
14
|
+
if (isServerAllocated) {
|
|
15
|
+
return {
|
|
16
|
+
Classification: 'ServerAllocated',
|
|
17
|
+
IsResolved: true,
|
|
18
|
+
ActionRequired: 'Reset',
|
|
19
|
+
Blocked: false,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// 2. Parent-Scoped (composite unique with parent FK, e.g. [ParentID, Name])
|
|
23
|
+
if (KeyDef.Scope === 'Parent' || (KeyDef.Fields.length > 1 && KeyDef.ScopeField)) {
|
|
24
|
+
return {
|
|
25
|
+
Classification: 'ParentScoped',
|
|
26
|
+
IsResolved: true,
|
|
27
|
+
ActionRequired: 'VerifyParent',
|
|
28
|
+
Blocked: false,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// 3. Live-State Filtered (e.g. FormVersion one Published per form, or AIAgentConfiguration.IsDefault)
|
|
32
|
+
if (KeyDef.Scope === 'LiveState') {
|
|
33
|
+
const stateField = KeyDef.ScopeField || KeyDef.Fields[0];
|
|
34
|
+
const hasReset = stateField in ResetFields;
|
|
35
|
+
if (hasReset) {
|
|
36
|
+
return {
|
|
37
|
+
Classification: 'LiveState',
|
|
38
|
+
IsResolved: true,
|
|
39
|
+
ActionRequired: 'Reset',
|
|
40
|
+
Blocked: false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Live-state without Reset rule leaves multiple rows in active state -> Blocked
|
|
44
|
+
return {
|
|
45
|
+
Classification: 'LiveState',
|
|
46
|
+
IsResolved: false,
|
|
47
|
+
ActionRequired: 'Reset',
|
|
48
|
+
Blocked: true,
|
|
49
|
+
Warning: {
|
|
50
|
+
Code: 'ROW_DISABLED',
|
|
51
|
+
Severity: 'Error',
|
|
52
|
+
Field: stateField,
|
|
53
|
+
Message: `Live-state unique field '${stateField}' on entity '${EntityName}' must be reset to avoid duplicate active state.`,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// 4. Global Single-Field or Multi-Field Unique Keys
|
|
58
|
+
const firstField = KeyDef.Fields[0];
|
|
59
|
+
const isString = IsStringField ? IsStringField(firstField) : true;
|
|
60
|
+
if (isString && KeyDef.Fields.length === 1) {
|
|
61
|
+
// Global string unique: resolved via Naming template and collision probe
|
|
62
|
+
return {
|
|
63
|
+
Classification: 'GlobalString',
|
|
64
|
+
IsResolved: true,
|
|
65
|
+
ActionRequired: 'Rename',
|
|
66
|
+
Blocked: false,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// Global non-string (or multi-field) unique (e.g. User.Email, SKU, Code)
|
|
70
|
+
const isPrompted = PromptForFields.includes(firstField) || firstField in PromptedValues;
|
|
71
|
+
const isReset = firstField in ResetFields;
|
|
72
|
+
if (isPrompted) {
|
|
73
|
+
return {
|
|
74
|
+
Classification: 'GlobalNonString',
|
|
75
|
+
IsResolved: true,
|
|
76
|
+
ActionRequired: 'Prompt',
|
|
77
|
+
Blocked: false,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (isReset) {
|
|
81
|
+
return {
|
|
82
|
+
Classification: 'GlobalNonString',
|
|
83
|
+
IsResolved: true,
|
|
84
|
+
ActionRequired: 'Reset',
|
|
85
|
+
Blocked: false,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// No rule to resolve global non-string unique -> plan is Blocked
|
|
89
|
+
return {
|
|
90
|
+
Classification: 'GlobalNonString',
|
|
91
|
+
IsResolved: false,
|
|
92
|
+
ActionRequired: 'Prompt',
|
|
93
|
+
Blocked: true,
|
|
94
|
+
Warning: {
|
|
95
|
+
Code: 'UNIQUE_PROMPT_REQUIRED',
|
|
96
|
+
Severity: 'Error',
|
|
97
|
+
Field: firstField,
|
|
98
|
+
Message: `Unique field '${firstField}' on entity '${EntityName}' requires a prompt or reset rule to avoid unique constraint collision.`,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Pre-validates planned rows to detect if two rows under the same parent
|
|
104
|
+
* (or across global scope) have colliding planned values.
|
|
105
|
+
*/
|
|
106
|
+
export function DetectIntraPlanCollisions(nodes, uniqueKeysByEntity = {}) {
|
|
107
|
+
const collisions = [];
|
|
108
|
+
// Group nodes by entity
|
|
109
|
+
const nodesByEntity = new Map();
|
|
110
|
+
for (const node of nodes) {
|
|
111
|
+
let list = nodesByEntity.get(node.EntityName);
|
|
112
|
+
if (!list) {
|
|
113
|
+
list = [];
|
|
114
|
+
nodesByEntity.set(node.EntityName, list);
|
|
115
|
+
}
|
|
116
|
+
list.push(node);
|
|
117
|
+
}
|
|
118
|
+
for (const [entityName, entityNodes] of nodesByEntity.entries()) {
|
|
119
|
+
const uqs = uniqueKeysByEntity[entityName];
|
|
120
|
+
if (!uqs || uqs.length === 0)
|
|
121
|
+
continue;
|
|
122
|
+
for (const uq of uqs) {
|
|
123
|
+
const seenValues = new Map();
|
|
124
|
+
for (const node of entityNodes) {
|
|
125
|
+
// A parent-scoped unique only collides under one parent: the new value of its
|
|
126
|
+
// ScopeField when the row carries one, else the plan node the row was reached from.
|
|
127
|
+
const scoped = uq.ScopeField ? node.Values[uq.ScopeField] : undefined;
|
|
128
|
+
const parent = scoped ?? node.ParentKey;
|
|
129
|
+
const prefix = uq.Scope === 'Parent' ? `PARENT:${String(parent ?? 'ROOT')}::` : '';
|
|
130
|
+
// Extract values of the unique fields
|
|
131
|
+
const valueParts = [];
|
|
132
|
+
let hasNull = false;
|
|
133
|
+
for (const field of uq.Fields) {
|
|
134
|
+
const v = node.Values[field];
|
|
135
|
+
if (v === null || v === undefined) {
|
|
136
|
+
hasNull = true;
|
|
137
|
+
}
|
|
138
|
+
valueParts.push(`${field}=${String(v ?? '')}`);
|
|
139
|
+
}
|
|
140
|
+
// In SQL, unique indexes ignore multi-column tuples with nulls unless unique index doesn't ignore nulls
|
|
141
|
+
// But for explicit collision detection:
|
|
142
|
+
const fingerprint = prefix + valueParts.join('|');
|
|
143
|
+
const existingNode = seenValues.get(fingerprint);
|
|
144
|
+
if (existingNode) {
|
|
145
|
+
collisions.push({
|
|
146
|
+
NodeKeyA: existingNode.NodeKey,
|
|
147
|
+
NodeKeyB: node.NodeKey,
|
|
148
|
+
EntityName: entityName,
|
|
149
|
+
Fields: uq.Fields,
|
|
150
|
+
Values: node.Values,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
seenValues.set(fingerprint, node);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return collisions;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=UniqueKeyClassifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UniqueKeyClassifier.js","sourceRoot":"","sources":["../src/UniqueKeyClassifier.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* Public entrypoint for @memberjunction/record-cloning-base.
|
|
4
|
+
* Exports pure algorithms, types, validation, and naming engines.
|
|
5
|
+
*/
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export * from './ClonePlanHash.js';
|
|
8
|
+
export * from './NameTemplate.js';
|
|
9
|
+
export * from './ExclusionClasses.js';
|
|
10
|
+
export * from './UniqueKeyClassifier.js';
|
|
11
|
+
export * from './JsonRemapEngine.js';
|
|
12
|
+
export * from './ClonePolicyResolver.js';
|
|
13
|
+
export * from './CloneFieldMapper.js';
|
|
14
|
+
export * from './CloneConfigValidator.js';
|
|
15
|
+
export * from './EffectiveOptions.js';
|
|
16
|
+
export * from './FieldMeta.js';
|
|
17
|
+
export * from './SensitiveValues.js';
|
|
18
|
+
export * from './RowExclusion.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* Public entrypoint for @memberjunction/record-cloning-base.
|
|
4
|
+
* Exports pure algorithms, types, validation, and naming engines.
|
|
5
|
+
*/
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export * from './ClonePlanHash.js';
|
|
8
|
+
export * from './NameTemplate.js';
|
|
9
|
+
export * from './ExclusionClasses.js';
|
|
10
|
+
export * from './UniqueKeyClassifier.js';
|
|
11
|
+
export * from './JsonRemapEngine.js';
|
|
12
|
+
export * from './ClonePolicyResolver.js';
|
|
13
|
+
export * from './CloneFieldMapper.js';
|
|
14
|
+
export * from './CloneConfigValidator.js';
|
|
15
|
+
export * from './EffectiveOptions.js';
|
|
16
|
+
export * from './FieldMeta.js';
|
|
17
|
+
export * from './SensitiveValues.js';
|
|
18
|
+
export * from './RowExclusion.js';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file types.ts
|
|
3
|
+
* Client-safe contracts and types for MemberJunction Entity Record Cloning.
|
|
4
|
+
* @see plans/record-cloning/README.md §3.4, §4.1
|
|
5
|
+
*/
|
|
6
|
+
import type { FieldRuleSet } from '@memberjunction/global';
|
|
7
|
+
/**
|
|
8
|
+
* Key-value pair representing a composite or single-column key component.
|
|
9
|
+
*/
|
|
10
|
+
export interface CompositeKeyKVP {
|
|
11
|
+
FieldName: string;
|
|
12
|
+
Value: unknown;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Abstract representation of a record primary key.
|
|
16
|
+
* Compatible with MemberJunction's CompositeKey class.
|
|
17
|
+
*/
|
|
18
|
+
export interface CompositeKeyLike {
|
|
19
|
+
KeyValuePairs: CompositeKeyKVP[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Edge traversal policy for graph expansion.
|
|
23
|
+
*/
|
|
24
|
+
export type CloneEdgePolicy = 'Deep' | 'Reference' | 'Skip';
|
|
25
|
+
/**
|
|
26
|
+
* Action determined for an individual record node in the plan.
|
|
27
|
+
*/
|
|
28
|
+
export type CloneNodeAction = 'Create' | 'Reference' | 'Skip' | 'Blocked';
|
|
29
|
+
/**
|
|
30
|
+
* Structural kind of relationship edge in the entity graph.
|
|
31
|
+
*/
|
|
32
|
+
export type CloneEdgeKind = 'IsASubtype' | 'Collection' | 'Embedded' | 'Relationship' | 'InboundFK' | 'ForwardFK' | 'SoftLink' | 'Hierarchy' | 'SelfPointer';
|
|
33
|
+
/**
|
|
34
|
+
* Provenance / classification category for field modifications.
|
|
35
|
+
*/
|
|
36
|
+
export type CloneFieldChangeKind = 'Copy' | 'Reset' | 'Ownership' | 'Rename' | 'Remap' | 'RemapJSON' | 'Rule' | 'Override' | 'Prompt' | 'Excluded' | 'DeniedRead' | 'DeniedCreate' | 'NotWritable';
|
|
37
|
+
/**
|
|
38
|
+
* Standard warning and diagnostic codes emitted during planning or execution.
|
|
39
|
+
*/
|
|
40
|
+
export type CloneWarningCode = 'UNMAPPABLE_REFERENCE_DROPPED' | 'PAYLOAD_DROPPED' | 'ROW_DISABLED' | 'UNIQUE_RENAMED' | 'UNIQUE_PROMPT_REQUIRED' | 'CAP_EXCEEDED' | 'NO_CREATE_PERMISSION' | 'NOT_CLONEABLE' | 'WRITE_ONCE_ENTITY' | 'SERVER_HOOK_SIDE_EFFECT' | 'CONSTRAINT_FORCED_DEEP' | 'LOCKED_EDGE_OVERRIDE_IGNORED' | 'EMBEDDING_REGENERATED' | 'SOURCE_ROW_INVISIBLE' | 'SERVER_GENERATED_CHILD_SKIPPED' | 'INTRA_PLAN_COLLISION' | 'REQUIRED_USER_TYPE_MISMATCH' | 'PLAN_CHANGED'
|
|
41
|
+
/** The user lacks the clone authorization for this entity (plan §9.1). */
|
|
42
|
+
| 'FORBIDDEN'
|
|
43
|
+
/** A request changed whether Entity Actions / AI Actions run without `Clone Records: Fire Hooks`; the configured behavior applies. */
|
|
44
|
+
| 'HOOKS_FORBIDDEN'
|
|
45
|
+
/** A request option was ignored because the entity's `UserEditable` does not allow it. */
|
|
46
|
+
| 'OPTION_OVERRIDE_IGNORED'
|
|
47
|
+
/** A request tried to widen scope without the `Clone Records: Override Scope` authorization; it was ignored. */
|
|
48
|
+
| 'SCOPE_OVERRIDE_FORBIDDEN'
|
|
49
|
+
/** A row's new primary key would equal its source key (no key column is remapped), so the copy would collide. */
|
|
50
|
+
| 'TARGET_KEY_UNCHANGED'
|
|
51
|
+
/** Rows the relationship's `ExcludeRows` leaves out, e.g. device tokens or drafts on a user clone. */
|
|
52
|
+
| 'ROWS_EXCLUDED'
|
|
53
|
+
/** The root entity's clone configuration failed validation; the plan is blocked. */
|
|
54
|
+
| 'CONFIG_INVALID'
|
|
55
|
+
/** No free name fits a renamed column (it is too short, or every candidate is taken); the plan is blocked. */
|
|
56
|
+
| 'NAME_UNAVAILABLE'
|
|
57
|
+
/** The root record wasn't found or the cloner can't read it; the plan is blocked. */
|
|
58
|
+
| 'SOURCE_NOT_FOUND'
|
|
59
|
+
/** A Deep IS-A subtype or embedded edge: those rows need a save recipe this release doesn't have; the plan is blocked. */
|
|
60
|
+
| 'EDGE_KIND_UNSUPPORTED'
|
|
61
|
+
/** Rows of an edge the clone copies couldn't be read (no read permission, a failed query); the copy leaves them out. */
|
|
62
|
+
| 'CHILD_ROWS_UNREADABLE';
|
|
63
|
+
/**
|
|
64
|
+
* Warning or notification emitted by the planning or execution engine.
|
|
65
|
+
*/
|
|
66
|
+
export interface CloneWarning {
|
|
67
|
+
Code: CloneWarningCode;
|
|
68
|
+
Severity: 'Info' | 'Warning' | 'Error';
|
|
69
|
+
NodeKey?: string;
|
|
70
|
+
Field?: string;
|
|
71
|
+
Message: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Granular ledger entry recording one modification to a specific field.
|
|
75
|
+
*/
|
|
76
|
+
export interface CloneFieldChange {
|
|
77
|
+
Field: string;
|
|
78
|
+
Kind: CloneFieldChangeKind;
|
|
79
|
+
OldValue: unknown;
|
|
80
|
+
NewValue: unknown;
|
|
81
|
+
Reason: string;
|
|
82
|
+
/**
|
|
83
|
+
* The field is stored encrypted. The server-side plan keeps the real values so Execute can write them;
|
|
84
|
+
* `MaskSensitiveFieldChange` replaces them before the plan is returned, logged or hashed.
|
|
85
|
+
*/
|
|
86
|
+
Sensitive?: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Options passed to the clone engine to govern graph expansion and execution.
|
|
90
|
+
*/
|
|
91
|
+
export interface CloneRequestOptions {
|
|
92
|
+
DryRun?: boolean;
|
|
93
|
+
Preset?: string;
|
|
94
|
+
MaxDepth?: number;
|
|
95
|
+
MaxRecords?: number;
|
|
96
|
+
Subtypes?: 'include' | 'exclude';
|
|
97
|
+
Hierarchy?: 'subtree' | 'node';
|
|
98
|
+
SoftLinks?: 'skip' | 'include';
|
|
99
|
+
EntityActions?: 'suppress' | 'fire';
|
|
100
|
+
AIActions?: 'suppress' | 'fire';
|
|
101
|
+
Embeddings?: 'copy' | 'regenerate';
|
|
102
|
+
FieldOverrides?: Record<string, unknown>;
|
|
103
|
+
PromptedValues?: Record<string, unknown>;
|
|
104
|
+
FieldRules?: FieldRuleSet;
|
|
105
|
+
Retarget?: Array<{
|
|
106
|
+
EntityName: string;
|
|
107
|
+
Field: string;
|
|
108
|
+
Value: unknown;
|
|
109
|
+
}>;
|
|
110
|
+
Naming?: {
|
|
111
|
+
Template?: string;
|
|
112
|
+
Strategy?: 'suffix' | 'increment' | 'prompt' | 'none';
|
|
113
|
+
};
|
|
114
|
+
NamingTemplate?: string;
|
|
115
|
+
NamingStrategy?: 'suffix' | 'increment' | 'prompt' | 'none';
|
|
116
|
+
Reason?: string;
|
|
117
|
+
NewParentKey?: string | null;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Per-edge policy override provided by user review.
|
|
121
|
+
*/
|
|
122
|
+
export interface CloneEdgeOverride {
|
|
123
|
+
FromKey?: string;
|
|
124
|
+
ToKey?: string;
|
|
125
|
+
RelationshipID?: string;
|
|
126
|
+
Policy: CloneEdgePolicy;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Full clone request submitted to the engine.
|
|
130
|
+
*/
|
|
131
|
+
export interface RecordCloneRequest {
|
|
132
|
+
Roots?: Array<{
|
|
133
|
+
EntityName: string;
|
|
134
|
+
Key: CompositeKeyLike;
|
|
135
|
+
}>;
|
|
136
|
+
EntityName?: string;
|
|
137
|
+
SourceRecordKey?: CompositeKeyLike | Record<string, unknown>;
|
|
138
|
+
Preset?: string;
|
|
139
|
+
Options?: CloneRequestOptions;
|
|
140
|
+
EdgeOverrides?: CloneEdgeOverride[];
|
|
141
|
+
FieldOverrides?: Record<string, unknown>;
|
|
142
|
+
PromptedValues?: Record<string, unknown>;
|
|
143
|
+
ExpectedPlanHash?: string;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Graph edge connecting two plan nodes.
|
|
147
|
+
*/
|
|
148
|
+
export interface ClonePlanEdge {
|
|
149
|
+
FromKey: string;
|
|
150
|
+
ToKey: string;
|
|
151
|
+
Kind: CloneEdgeKind;
|
|
152
|
+
RelatedEntityName: string;
|
|
153
|
+
JoinField: string;
|
|
154
|
+
RelationshipID?: string;
|
|
155
|
+
CollectionName?: string;
|
|
156
|
+
IsSoftLink?: boolean;
|
|
157
|
+
Policy: CloneEdgePolicy;
|
|
158
|
+
Locked: boolean;
|
|
159
|
+
PolicySource: 'BuiltIn' | 'Constraint' | 'Entity' | 'Relationship' | 'Descendant' | 'Request';
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Graph node representing an entity record discovered or planned for cloning.
|
|
163
|
+
*/
|
|
164
|
+
export interface ClonePlanNode {
|
|
165
|
+
Key: string;
|
|
166
|
+
NodeKey?: string;
|
|
167
|
+
EntityName: string;
|
|
168
|
+
SourceKey: CompositeKeyLike | string;
|
|
169
|
+
TargetKey: CompositeKeyLike | string | null;
|
|
170
|
+
Action: CloneNodeAction;
|
|
171
|
+
Reason: string;
|
|
172
|
+
Depth: number;
|
|
173
|
+
ParentKey: string | null;
|
|
174
|
+
Via: ClonePlanEdge | null;
|
|
175
|
+
DisplayName: string;
|
|
176
|
+
IsSubtypeRow?: boolean;
|
|
177
|
+
FieldChanges: CloneFieldChange[];
|
|
178
|
+
Warnings: CloneWarning[];
|
|
179
|
+
Route: 'RootSave' | 'Collection' | 'Embedded' | 'IsAChain' | 'Sidecar';
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Full execution plan for a record clone operation.
|
|
183
|
+
*/
|
|
184
|
+
export interface ClonePlan {
|
|
185
|
+
PlanVersion: 1;
|
|
186
|
+
Hash: string;
|
|
187
|
+
PlanHash?: string;
|
|
188
|
+
Roots: string[];
|
|
189
|
+
RootEntityName?: string;
|
|
190
|
+
RootSourceKey?: string;
|
|
191
|
+
RootTargetKey?: string;
|
|
192
|
+
Nodes: ClonePlanNode[];
|
|
193
|
+
Edges: ClonePlanEdge[];
|
|
194
|
+
Excluded?: unknown[];
|
|
195
|
+
Counts: {
|
|
196
|
+
ByEntity: Record<string, {
|
|
197
|
+
Create: number;
|
|
198
|
+
Reference: number;
|
|
199
|
+
Skip: number;
|
|
200
|
+
}>;
|
|
201
|
+
Create: number;
|
|
202
|
+
Total: number;
|
|
203
|
+
};
|
|
204
|
+
Warnings: CloneWarning[];
|
|
205
|
+
Blocked: boolean;
|
|
206
|
+
/** Options widened beyond the entity's configuration through `Clone Records: Override Scope`. */
|
|
207
|
+
Overrides?: string[];
|
|
208
|
+
/** Why the user is cloning (`Options.Reason`); recorded on the clone log and each row's clone context. */
|
|
209
|
+
Reason?: string;
|
|
210
|
+
EffectiveOptions: Required<Pick<CloneRequestOptions, 'MaxDepth' | 'MaxRecords' | 'Subtypes' | 'Hierarchy' | 'SoftLinks' | 'EntityActions' | 'AIActions' | 'Embeddings'>>;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Result returned upon clone plan creation or execution.
|
|
214
|
+
*/
|
|
215
|
+
export interface RecordCloneResult {
|
|
216
|
+
Success: boolean;
|
|
217
|
+
ResultCode?: 'SUCCESS' | 'PLAN_CHANGED' | 'BLOCKED' | 'FORBIDDEN' | 'EXECUTION_ERROR';
|
|
218
|
+
CloneLogID?: string | null;
|
|
219
|
+
RootRecordKey?: string;
|
|
220
|
+
RecordsCloned?: number;
|
|
221
|
+
Roots?: Array<{
|
|
222
|
+
EntityName: string;
|
|
223
|
+
SourceKey: CompositeKeyLike;
|
|
224
|
+
TargetKey: CompositeKeyLike;
|
|
225
|
+
}>;
|
|
226
|
+
Created?: Array<{
|
|
227
|
+
EntityName: string;
|
|
228
|
+
SourceKey: CompositeKeyLike;
|
|
229
|
+
TargetKey: CompositeKeyLike;
|
|
230
|
+
Depth: number;
|
|
231
|
+
}>;
|
|
232
|
+
Skipped?: Array<{
|
|
233
|
+
EntityName: string;
|
|
234
|
+
SourceKey: CompositeKeyLike;
|
|
235
|
+
Reason: string;
|
|
236
|
+
}>;
|
|
237
|
+
Counts?: ClonePlan['Counts'];
|
|
238
|
+
Warnings: CloneWarning[];
|
|
239
|
+
Plan?: ClonePlan;
|
|
240
|
+
ErrorMessage?: string;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* JSON remap specification for nested documents.
|
|
244
|
+
*/
|
|
245
|
+
export interface JsonRemapSpec {
|
|
246
|
+
Path: string;
|
|
247
|
+
Mode: 'remap' | 'reuse' | 'regenerate' | 'null' | 'drop';
|
|
248
|
+
TargetEntityName?: string;
|
|
249
|
+
OnMissing?: 'reuse' | 'drop';
|
|
250
|
+
CleanEmptyContainers?: boolean;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Helper to produce a compact deterministic string from a CompositeKeyLike.
|
|
254
|
+
*/
|
|
255
|
+
export declare function FormatCompositeKey(key: CompositeKeyLike | string): string;
|
|
256
|
+
/**
|
|
257
|
+
* Helper to format a canonical node key in the format: "<EntityName>::<compactKey>".
|
|
258
|
+
*/
|
|
259
|
+
export declare function MakeNodeKey(entityName: string, key: CompositeKeyLike | string): string;
|
|
260
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|