@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
package/README.md
CHANGED
|
@@ -1,45 +1,270 @@
|
|
|
1
1
|
# @memberjunction/version-history
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Version labeling, snapshot capture, diff, and restore for MemberJunction records. Provides point-in-time versioning with full entity dependency graph awareness.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The `@memberjunction/version-history` package enables developers to create named version labels that capture record state at specific points in time, compare changes between labels, and restore records to previous states while respecting entity dependency ordering.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
```mermaid
|
|
10
|
+
graph TD
|
|
11
|
+
A["VersionHistoryEngine<br/>(Facade)"] --> B["LabelManager"]
|
|
12
|
+
A --> C["SnapshotBuilder"]
|
|
13
|
+
A --> D["DiffEngine"]
|
|
14
|
+
A --> E["RestoreEngine"]
|
|
15
|
+
A --> F["DependencyGraphWalker"]
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
B --> G["Version Labels"]
|
|
18
|
+
C --> H["Version Label Items"]
|
|
19
|
+
D --> I["DiffResult"]
|
|
20
|
+
E --> J["RestoreResult"]
|
|
21
|
+
F --> K["DependencyNode Tree"]
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
style A fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
24
|
+
style B fill:#7c5295,stroke:#563a6b,color:#fff
|
|
25
|
+
style C fill:#7c5295,stroke:#563a6b,color:#fff
|
|
26
|
+
style D fill:#7c5295,stroke:#563a6b,color:#fff
|
|
27
|
+
style E fill:#7c5295,stroke:#563a6b,color:#fff
|
|
28
|
+
style F fill:#7c5295,stroke:#563a6b,color:#fff
|
|
29
|
+
style G fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
30
|
+
style H fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
31
|
+
style I fill:#b8762f,stroke:#8a5722,color:#fff
|
|
32
|
+
style J fill:#b8762f,stroke:#8a5722,color:#fff
|
|
33
|
+
style K fill:#b8762f,stroke:#8a5722,color:#fff
|
|
34
|
+
```
|
|
17
35
|
|
|
18
|
-
|
|
36
|
+
## Installation
|
|
19
37
|
|
|
20
|
-
|
|
38
|
+
```bash
|
|
39
|
+
npm install @memberjunction/version-history
|
|
40
|
+
```
|
|
21
41
|
|
|
22
|
-
|
|
42
|
+
## Quick Start
|
|
23
43
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
44
|
+
```typescript
|
|
45
|
+
import { VersionHistoryEngine } from '@memberjunction/version-history';
|
|
28
46
|
|
|
29
|
-
|
|
47
|
+
const engine = new VersionHistoryEngine();
|
|
30
48
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
// Create a label capturing a record and its dependencies
|
|
50
|
+
const { Label, CaptureResult } = await engine.CreateLabel({
|
|
51
|
+
Name: 'Before Refactor',
|
|
52
|
+
Scope: 'Record',
|
|
53
|
+
EntityName: 'AI Prompts',
|
|
54
|
+
RecordKey: promptKey,
|
|
55
|
+
IncludeDependencies: true,
|
|
56
|
+
}, contextUser);
|
|
36
57
|
|
|
37
|
-
|
|
58
|
+
// Later: see what changed since the label
|
|
59
|
+
const diff = await engine.DiffLabelToCurrentState(Label.ID, contextUser);
|
|
38
60
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
61
|
+
// Restore if needed
|
|
62
|
+
const result = await engine.RestoreToLabel(Label.ID, {}, contextUser);
|
|
63
|
+
```
|
|
42
64
|
|
|
43
|
-
|
|
65
|
+
## Label Scopes
|
|
44
66
|
|
|
45
|
-
|
|
67
|
+
| Scope | Description | Use Case |
|
|
68
|
+
|-------|-------------|----------|
|
|
69
|
+
| `Record` | Single record and its dependencies | Safe point before editing a specific record |
|
|
70
|
+
| `Entity` | All records of a specific entity | Checkpoint before bulk updates |
|
|
71
|
+
| `System` | All tracked entities | Full system snapshot before a release |
|
|
72
|
+
|
|
73
|
+
## Architecture
|
|
74
|
+
|
|
75
|
+
### Sub-Engines
|
|
76
|
+
|
|
77
|
+
| Sub-Engine | Responsibility |
|
|
78
|
+
|-----------|----------------|
|
|
79
|
+
| **LabelManager** | Label CRUD and lifecycle management |
|
|
80
|
+
| **SnapshotBuilder** | Captures record state into label items with batched queries |
|
|
81
|
+
| **DependencyGraphWalker** | Traverses entity relationships to discover dependent records |
|
|
82
|
+
| **DiffEngine** | Compares snapshots between labels or between a label and current state |
|
|
83
|
+
| **RestoreEngine** | Applies labeled state back to records in dependency order |
|
|
84
|
+
|
|
85
|
+
### Snapshot and Restore Flow
|
|
86
|
+
|
|
87
|
+
```mermaid
|
|
88
|
+
sequenceDiagram
|
|
89
|
+
participant User
|
|
90
|
+
participant VHE as VersionHistoryEngine
|
|
91
|
+
participant SB as SnapshotBuilder
|
|
92
|
+
participant DGW as DependencyGraphWalker
|
|
93
|
+
participant DB as Database
|
|
94
|
+
|
|
95
|
+
User->>VHE: CreateLabel(params)
|
|
96
|
+
VHE->>SB: CaptureRecord(labelId, entity, key)
|
|
97
|
+
SB->>DGW: WalkDependents(entity, key)
|
|
98
|
+
DGW->>DB: Query related records
|
|
99
|
+
DGW-->>SB: DependencyNode tree
|
|
100
|
+
SB->>DB: Save VersionLabelItem for each record
|
|
101
|
+
SB-->>VHE: CaptureResult
|
|
102
|
+
|
|
103
|
+
Note over User,DB: Time passes, records are modified
|
|
104
|
+
|
|
105
|
+
User->>VHE: RestoreToLabel(labelId, options)
|
|
106
|
+
VHE->>VHE: Create safety Pre-Restore label
|
|
107
|
+
VHE->>DB: Load snapshots, apply in dependency order
|
|
108
|
+
VHE-->>User: RestoreResult
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Dependency Graph Walker
|
|
112
|
+
|
|
113
|
+
The walker discovers all records that should be included in a version label. It traverses both **reverse relationships** (child records that belong to the root) and **forward references** (records the root or its children point to).
|
|
114
|
+
|
|
115
|
+
### Two Key Mechanisms
|
|
116
|
+
|
|
117
|
+
#### 1. EntityRelationship-Driven Reverse Walking
|
|
118
|
+
|
|
119
|
+
Instead of scanning every entity for foreign keys that point to the current entity, the walker uses **EntityRelationship metadata** that MemberJunction already maintains. Only explicitly registered children are walked.
|
|
120
|
+
|
|
121
|
+
```mermaid
|
|
122
|
+
graph TD
|
|
123
|
+
A["AI Agents"] -->|"EntityRelationship"| B["AI Agent Prompts"]
|
|
124
|
+
A -->|"EntityRelationship"| C["AI Agent Actions"]
|
|
125
|
+
A -->|"EntityRelationship"| D["AI Agent Relationships"]
|
|
126
|
+
A -->|"EntityRelationship"| E["AI Agent Models"]
|
|
127
|
+
A -.-x|"NOT walked"| G["Random Table with FK"]
|
|
128
|
+
|
|
129
|
+
style A fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
130
|
+
style B fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
131
|
+
style C fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
132
|
+
style D fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
133
|
+
style E fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
134
|
+
style G fill:#b8762f,stroke:#8a5722,color:#fff
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### 2. Ancestor Stack -- Prevents Backtracking
|
|
138
|
+
|
|
139
|
+
The walker maintains a stack of entity type names representing the path from root to the current node. When evaluating any relationship, if the target entity type is already on the ancestor stack, it is skipped. This surgically prevents graph explosion without arbitrary depth limits.
|
|
140
|
+
|
|
141
|
+
### Walk Algorithm
|
|
142
|
+
|
|
143
|
+
```mermaid
|
|
144
|
+
flowchart TD
|
|
145
|
+
Start(["walkChildren called"]) --> DepthCheck{"Depth >= MaxDepth?"}
|
|
146
|
+
DepthCheck -->|Yes| Stop(["Return"])
|
|
147
|
+
DepthCheck -->|No| Reverse["Walk Reverse Relationships"]
|
|
148
|
+
|
|
149
|
+
Reverse --> ReverseLoop{"For each EntityRelationship"}
|
|
150
|
+
ReverseLoop -->|Next| AncestorR{"On ancestor stack?"}
|
|
151
|
+
AncestorR -->|Yes| SkipR(["Skip"])
|
|
152
|
+
AncestorR -->|No| LoadChildren["Load child records"]
|
|
153
|
+
LoadChildren --> RecurseR["Recurse walkChildren"]
|
|
154
|
+
RecurseR --> ReverseLoop
|
|
155
|
+
|
|
156
|
+
ReverseLoop -->|Done| Forward["Walk Forward References"]
|
|
157
|
+
Forward --> ForwardLoop{"For each FK field"}
|
|
158
|
+
ForwardLoop -->|Next| SystemCheck{"System FK?"}
|
|
159
|
+
SystemCheck -->|Yes| SkipF(["Skip"])
|
|
160
|
+
SystemCheck -->|No| AncestorF{"On ancestor stack?"}
|
|
161
|
+
AncestorF -->|Yes| SkipF2(["Skip"])
|
|
162
|
+
AncestorF -->|No| LoadTarget["Load referenced record"]
|
|
163
|
+
LoadTarget --> RecurseF["Recurse walkChildren"]
|
|
164
|
+
RecurseF --> ForwardLoop
|
|
165
|
+
ForwardLoop -->|Done| Stop
|
|
166
|
+
|
|
167
|
+
style SkipR fill:#b8762f,stroke:#8a5722,color:#fff
|
|
168
|
+
style SkipF fill:#b8762f,stroke:#8a5722,color:#fff
|
|
169
|
+
style SkipF2 fill:#b8762f,stroke:#8a5722,color:#fff
|
|
170
|
+
style Start fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
171
|
+
style Stop fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Design Rationale
|
|
175
|
+
|
|
176
|
+
| Concern | Solution |
|
|
177
|
+
|---------|----------|
|
|
178
|
+
| Which children to walk? | **EntityRelationship** -- admin-controlled, CodeGen-maintained |
|
|
179
|
+
| Preventing graph explosion? | **Ancestor stack** -- blocks backtracking to any entity type on current path |
|
|
180
|
+
| Infrastructure FKs (UserID, etc.)? | **System FK skip list** -- regex patterns for known infrastructure fields |
|
|
181
|
+
| Cycle detection? | **Visited set** -- `entityName::recordID` prevents revisiting any record |
|
|
182
|
+
| Sub-agent recursion? | Ancestor stack is **path-based** -- pops on backtrack, allowing re-entry from a different branch |
|
|
183
|
+
|
|
184
|
+
### Forward FK Skip Patterns
|
|
185
|
+
|
|
186
|
+
The following FK field name patterns are never followed during forward walking, as they reference system infrastructure rather than business data:
|
|
187
|
+
|
|
188
|
+
- `CreatedByUserID`, `UpdatedByUserID`, `UserID`
|
|
189
|
+
- `ContextUserID`, `ModifiedByUserID`
|
|
190
|
+
- `CreatedBy`, `UpdatedBy`
|
|
191
|
+
- `OwnerID`, `OwnerUserID`
|
|
192
|
+
- `AssignedToID`, `AssignedToUserID`
|
|
193
|
+
- `EntityID` (polymorphic reference)
|
|
194
|
+
|
|
195
|
+
## Snapshot Builder -- Batched Capture
|
|
196
|
+
|
|
197
|
+
When capturing records into a version label, the SnapshotBuilder uses **batched queries** to minimize database round trips:
|
|
198
|
+
|
|
199
|
+
```mermaid
|
|
200
|
+
sequenceDiagram
|
|
201
|
+
participant SB as SnapshotBuilder
|
|
202
|
+
participant DB as Database
|
|
203
|
+
|
|
204
|
+
Note over SB: Receive flat list of N nodes
|
|
205
|
+
|
|
206
|
+
SB->>SB: Group nodes by EntityID
|
|
207
|
+
loop For each entity group
|
|
208
|
+
SB->>DB: ONE RunView: RecordChanges<br/>WHERE EntityID = X<br/>AND RecordID IN (a, b, c, ...)
|
|
209
|
+
DB-->>SB: Latest changes for all records in group
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
Note over SB: Build lookup map
|
|
213
|
+
|
|
214
|
+
loop For each node without a RecordChange
|
|
215
|
+
SB->>DB: Create synthetic snapshot
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
loop For each node
|
|
219
|
+
SB->>DB: Create VersionLabelItem
|
|
220
|
+
end
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Before batching**: N individual RunView calls.
|
|
224
|
+
**After batching**: ~5-10 RunView calls (one per unique entity type in the graph).
|
|
225
|
+
|
|
226
|
+
## API Reference
|
|
227
|
+
|
|
228
|
+
### VersionHistoryEngine
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
const engine = new VersionHistoryEngine();
|
|
232
|
+
|
|
233
|
+
// Create a label with dependency walking
|
|
234
|
+
const { Label, CaptureResult } = await engine.CreateLabel({
|
|
235
|
+
Name: 'Before Refactor v2',
|
|
236
|
+
Scope: 'Record',
|
|
237
|
+
EntityName: 'AI Agents',
|
|
238
|
+
RecordKey: agentKey,
|
|
239
|
+
IncludeDependencies: true,
|
|
240
|
+
MaxDepth: 10,
|
|
241
|
+
ExcludeEntities: ['AI Agent Runs'],
|
|
242
|
+
}, contextUser);
|
|
243
|
+
|
|
244
|
+
// Diff against current state
|
|
245
|
+
const diff = await engine.DiffLabelToCurrentState(Label.ID, contextUser);
|
|
246
|
+
|
|
247
|
+
// Restore if needed
|
|
248
|
+
const result = await engine.RestoreToLabel(Label.ID, { DryRun: true }, contextUser);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### WalkOptions
|
|
252
|
+
|
|
253
|
+
| Option | Default | Description |
|
|
254
|
+
|--------|---------|-------------|
|
|
255
|
+
| `MaxDepth` | `10` | Maximum recursion depth |
|
|
256
|
+
| `EntityFilter` | `[]` | Only include these entities (empty = all) |
|
|
257
|
+
| `ExcludeEntities` | `[]` | Skip these entities entirely |
|
|
258
|
+
| `IncludeDeleted` | `false` | Include soft-deleted records |
|
|
259
|
+
|
|
260
|
+
## Dependencies
|
|
261
|
+
|
|
262
|
+
| Package | Purpose |
|
|
263
|
+
|---------|---------|
|
|
264
|
+
| `@memberjunction/core` | Entity system, metadata, and CompositeKey |
|
|
265
|
+
| `@memberjunction/core-entities` | VersionLabel entity types |
|
|
266
|
+
| `@memberjunction/global` | Global state management |
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
ISC
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { CompositeKey, UserInfo } from '@memberjunction/core';
|
|
2
|
+
import { DependencyNode, WalkOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Walks entity relationship graphs to discover records for version labeling.
|
|
5
|
+
*
|
|
6
|
+
* ## Two-direction traversal
|
|
7
|
+
*
|
|
8
|
+
* **Reverse**: Uses the entity's `RelatedEntities` (EntityRelationship metadata)
|
|
9
|
+
* to find child records. This is curated by CodeGen and admins — only explicitly
|
|
10
|
+
* registered One-To-Many relationships are walked.
|
|
11
|
+
*
|
|
12
|
+
* **Forward**: Scans the current entity's own FK fields to find records it
|
|
13
|
+
* references (e.g., AI Agent Prompt → AI Prompt via PromptID). Skips
|
|
14
|
+
* system/infrastructure fields (UserID, CreatedBy, etc.).
|
|
15
|
+
*
|
|
16
|
+
* ## Discovery mode — prevents explosion from forward references
|
|
17
|
+
*
|
|
18
|
+
* Records found via **reverse walk** are "owned children" and get full treatment
|
|
19
|
+
* (both reverse + forward walks). Records found via **forward walk** are
|
|
20
|
+
* "referenced records" and only get forward-only treatment (their own FK refs
|
|
21
|
+
* are followed, but their EntityRelationship children are NOT discovered).
|
|
22
|
+
*
|
|
23
|
+
* This prevents the classic explosion pattern:
|
|
24
|
+
* ```
|
|
25
|
+
* Agent → AgentPrompt → Prompt → [reverse to ALL PromptModels] → Model
|
|
26
|
+
* → [reverse to ALL AgentModels across ALL agents] → explosion!
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* With discovery mode:
|
|
30
|
+
* ```
|
|
31
|
+
* Agent → AgentPrompt(reverse,full) → Prompt(forward,forward-only)
|
|
32
|
+
* → Model(forward,forward-only) → STOP (no reverse walk on Model)
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* ## Ancestor stack — prevents backtracking
|
|
36
|
+
*
|
|
37
|
+
* A set of entity names on the current path from root to the current node is
|
|
38
|
+
* maintained. When evaluating any relationship, if the target entity type is
|
|
39
|
+
* already on the ancestor stack, it is skipped. This is a secondary safety net
|
|
40
|
+
* that prevents cycles even within full-mode walks.
|
|
41
|
+
*
|
|
42
|
+
* ## Filters
|
|
43
|
+
*
|
|
44
|
+
* - Only walks entities with `TrackRecordChanges === true`
|
|
45
|
+
* - Skips system FK fields via regex patterns (UserID, CreatedBy, etc.)
|
|
46
|
+
* - Respects `ExcludeEntities` and `EntityFilter` options
|
|
47
|
+
* - Uses a global `visited` set for record-level cycle detection
|
|
48
|
+
*/
|
|
49
|
+
export declare class DependencyGraphWalker {
|
|
50
|
+
/** Cache: entityID → reverse relationships from EntityRelationship metadata */
|
|
51
|
+
private reverseRelCache;
|
|
52
|
+
/** Cache: entityID → forward FK references from field metadata */
|
|
53
|
+
private forwardRefCache;
|
|
54
|
+
/**
|
|
55
|
+
* Walk from a root record through its relationships, building a tree of
|
|
56
|
+
* all records that should be included in a version label.
|
|
57
|
+
*
|
|
58
|
+
* @param entityName - The starting entity name
|
|
59
|
+
* @param recordKey - The starting record's primary key
|
|
60
|
+
* @param options - Controls depth, filtering, etc.
|
|
61
|
+
* @param contextUser - Server-side user context
|
|
62
|
+
* @returns The root DependencyNode with all descendants populated
|
|
63
|
+
*/
|
|
64
|
+
WalkDependents(entityName: string, recordKey: CompositeKey, options: WalkOptions, contextUser: UserInfo): Promise<DependencyNode>;
|
|
65
|
+
/**
|
|
66
|
+
* Flatten a dependency tree into a topologically sorted list.
|
|
67
|
+
* Parents appear before their children, ensuring safe restore ordering.
|
|
68
|
+
*/
|
|
69
|
+
FlattenTopological(root: DependencyNode): DependencyNode[];
|
|
70
|
+
/**
|
|
71
|
+
* Recursively discover and attach child nodes for a given parent node.
|
|
72
|
+
*
|
|
73
|
+
* The `discoveryMode` controls which directions are walked:
|
|
74
|
+
* - `'full'`: both reverse (EntityRelationship) and forward (FK refs)
|
|
75
|
+
* - `'forward-only'`: only forward FK refs — no reverse children discovered
|
|
76
|
+
*
|
|
77
|
+
* Records found via reverse walk are recursed with `'full'` mode.
|
|
78
|
+
* Records found via forward walk are recursed with `'forward-only'` mode.
|
|
79
|
+
*/
|
|
80
|
+
private walkChildren;
|
|
81
|
+
/**
|
|
82
|
+
* Walk reverse relationships: for each EntityRelationship on the parent
|
|
83
|
+
* entity, load matching child records and recurse into them.
|
|
84
|
+
*
|
|
85
|
+
* Skips any child entity type that is already on the ancestor stack,
|
|
86
|
+
* preventing backtracking (e.g., from AI Prompt back to AI Agent Prompts).
|
|
87
|
+
*
|
|
88
|
+
* Children found via reverse walk are recursed with 'full' discovery mode,
|
|
89
|
+
* since they are "owned children" that may have their own children.
|
|
90
|
+
*/
|
|
91
|
+
private walkReverseRelationships;
|
|
92
|
+
/**
|
|
93
|
+
* Walk forward FK references: for each FK field on the parent entity that
|
|
94
|
+
* points to another tracked entity, load the referenced record and recurse.
|
|
95
|
+
*
|
|
96
|
+
* Skips system/infrastructure FKs (UserID, CreatedBy, etc.) and any target
|
|
97
|
+
* entity type already on the ancestor stack.
|
|
98
|
+
*
|
|
99
|
+
* Targets found via forward walk are recursed with 'forward-only' discovery
|
|
100
|
+
* mode — they won't discover their own reverse children, preventing graph
|
|
101
|
+
* explosion from "hub" entities like AI Models.
|
|
102
|
+
*/
|
|
103
|
+
private walkForwardReferences;
|
|
104
|
+
/**
|
|
105
|
+
* Discover reverse relationships for an entity using EntityRelationship
|
|
106
|
+
* metadata. Only includes One-To-Many relationships to entities that have
|
|
107
|
+
* TrackRecordChanges enabled.
|
|
108
|
+
*
|
|
109
|
+
* Uses EntityRelationship (already loaded in memory on EntityInfo) rather
|
|
110
|
+
* than scanning all entities' FK fields. This means only relationships
|
|
111
|
+
* that CodeGen or admins have explicitly registered are walked.
|
|
112
|
+
*/
|
|
113
|
+
private discoverReverseRelationships;
|
|
114
|
+
/**
|
|
115
|
+
* Discover forward FK references on an entity — fields that point TO other
|
|
116
|
+
* entities. Only includes references to entities with TrackRecordChanges
|
|
117
|
+
* enabled, and skips system/infrastructure FK fields.
|
|
118
|
+
*/
|
|
119
|
+
private discoverForwardReferences;
|
|
120
|
+
/**
|
|
121
|
+
* Load child records for a reverse relationship.
|
|
122
|
+
* Queries the child entity where the join field matches the parent's key value.
|
|
123
|
+
*/
|
|
124
|
+
private loadChildRecords;
|
|
125
|
+
/**
|
|
126
|
+
* Load a single record's data by primary key.
|
|
127
|
+
*/
|
|
128
|
+
private loadRecordData;
|
|
129
|
+
/**
|
|
130
|
+
* Build a DependencyNode from record data, register it in the visited set,
|
|
131
|
+
* and attach it as a child of the parent. Returns null if already visited.
|
|
132
|
+
*/
|
|
133
|
+
private registerNode;
|
|
134
|
+
/** Create an empty stats accumulator. */
|
|
135
|
+
private createEmptyStats;
|
|
136
|
+
/** Increment the per-entity record counter. */
|
|
137
|
+
private incrementEntityCount;
|
|
138
|
+
/**
|
|
139
|
+
* Log a detailed summary of the walk: total records, per-entity counts,
|
|
140
|
+
* skips, and suppression stats.
|
|
141
|
+
*/
|
|
142
|
+
private logWalkSummary;
|
|
143
|
+
/** Build a unique key for the visited set: "EntityName::recordID" */
|
|
144
|
+
private visitKey;
|
|
145
|
+
/**
|
|
146
|
+
* Determine the parent key field from an EntityRelationship record.
|
|
147
|
+
* Uses EntityKeyField if specified, otherwise falls back to the first PK.
|
|
148
|
+
*/
|
|
149
|
+
private resolveParentKeyFieldFromRelationship;
|
|
150
|
+
/**
|
|
151
|
+
* Determine the target key field from a FK field definition.
|
|
152
|
+
* Uses RelatedEntityFieldName if specified, otherwise falls back to the
|
|
153
|
+
* target entity's first PK.
|
|
154
|
+
*/
|
|
155
|
+
private resolveParentKeyFieldFromFK;
|
|
156
|
+
/** Check if a field name matches a system/infrastructure FK pattern. */
|
|
157
|
+
private isSystemFKField;
|
|
158
|
+
/** Check if an entity should be skipped based on filter options. */
|
|
159
|
+
private shouldSkipEntity;
|
|
160
|
+
/** Check if an entity has a soft delete field. */
|
|
161
|
+
private entityHasSoftDelete;
|
|
162
|
+
/** Apply defaults to walk options. */
|
|
163
|
+
private resolveDefaults;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=DependencyGraphWalker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DependencyGraphWalker.d.ts","sourceRoot":"","sources":["../src/DependencyGraphWalker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA0E,QAAQ,EAAuB,MAAM,sBAAsB,CAAC;AAC3J,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AA6FtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,qBAAa,qBAAqB;IAC9B,+EAA+E;IAC/E,OAAO,CAAC,eAAe,CAA4C;IACnE,kEAAkE;IAClE,OAAO,CAAC,eAAe,CAAyC;IAMhE;;;;;;;;;OASG;IACU,cAAc,CACvB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC,cAAc,CAAC;IA2C1B;;;OAGG;IACI,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,cAAc,EAAE;IAiBjE;;;;;;;;;OASG;YACW,YAAY;IA4B1B;;;;;;;;;OASG;YACW,wBAAwB;IAyCtC;;;;;;;;;;OAUG;YACW,qBAAqB;IAwDnC;;;;;;;;OAQG;IACH,OAAO,CAAC,4BAA4B;IA6BpC;;;;OAIG;IACH,OAAO,CAAC,yBAAyB;IA+BjC;;;OAGG;YACW,gBAAgB;IAsC9B;;OAEG;YACW,cAAc;IA0B5B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAqCpB,yCAAyC;IACzC,OAAO,CAAC,gBAAgB;IAUxB,+CAA+C;IAC/C,OAAO,CAAC,oBAAoB;IAK5B;;;OAGG;IACH,OAAO,CAAC,cAAc;IA0BtB,qEAAqE;IACrE,OAAO,CAAC,QAAQ;IAIhB;;;OAGG;IACH,OAAO,CAAC,qCAAqC;IAU7C;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;IAUnC,wEAAwE;IACxE,OAAO,CAAC,eAAe;IAIvB,oEAAoE;IACpE,OAAO,CAAC,gBAAgB;IAUxB,kDAAkD;IAClD,OAAO,CAAC,mBAAmB;IAI3B,sCAAsC;IACtC,OAAO,CAAC,eAAe;CAQ1B"}
|