@memberjunction/metadata-sync 2.112.0 → 2.113.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 +136 -61
- package/dist/constants/metadata-keywords.d.ts +282 -0
- package/dist/constants/metadata-keywords.js +364 -0
- package/dist/constants/metadata-keywords.js.map +1 -0
- package/dist/lib/EntityPropertyExtractor.d.ts +1 -1
- package/dist/lib/EntityPropertyExtractor.js +4 -18
- package/dist/lib/EntityPropertyExtractor.js.map +1 -1
- package/dist/lib/FieldExternalizer.d.ts +1 -1
- package/dist/lib/FieldExternalizer.js +6 -5
- package/dist/lib/FieldExternalizer.js.map +1 -1
- package/dist/lib/RecordProcessor.d.ts +1 -1
- package/dist/lib/RecordProcessor.js +14 -12
- package/dist/lib/RecordProcessor.js.map +1 -1
- package/dist/lib/RelatedEntityHandler.d.ts +1 -1
- package/dist/lib/RelatedEntityHandler.js +5 -5
- package/dist/lib/RelatedEntityHandler.js.map +1 -1
- package/dist/lib/json-preprocessor.js +7 -6
- package/dist/lib/json-preprocessor.js.map +1 -1
- package/dist/lib/provider-utils.d.ts +1 -1
- package/dist/lib/provider-utils.js +19 -16
- package/dist/lib/provider-utils.js.map +1 -1
- package/dist/lib/record-dependency-analyzer.js +44 -37
- package/dist/lib/record-dependency-analyzer.js.map +1 -1
- package/dist/lib/singleton-manager.d.ts +1 -1
- package/dist/lib/singleton-manager.js.map +1 -1
- package/dist/lib/sync-engine.d.ts +1 -1
- package/dist/lib/sync-engine.js +36 -44
- package/dist/lib/sync-engine.js.map +1 -1
- package/dist/services/PullService.d.ts +1 -1
- package/dist/services/PullService.js +18 -22
- package/dist/services/PullService.js.map +1 -1
- package/dist/services/PushService.d.ts +1 -1
- package/dist/services/PushService.js +21 -15
- package/dist/services/PushService.js.map +1 -1
- package/dist/services/ValidationService.js +32 -44
- package/dist/services/ValidationService.js.map +1 -1
- package/dist/services/WatchService.d.ts +1 -1
- package/dist/services/WatchService.js +14 -13
- package/dist/services/WatchService.js.map +1 -1
- package/dist/types/validation.d.ts +6 -1
- package/dist/types/validation.js.map +1 -1
- package/package.json +7 -6
package/dist/lib/sync-engine.js
CHANGED
|
@@ -17,8 +17,9 @@ const path_1 = __importDefault(require("path"));
|
|
|
17
17
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
18
18
|
const crypto_1 = __importDefault(require("crypto"));
|
|
19
19
|
const axios_1 = __importDefault(require("axios"));
|
|
20
|
-
const
|
|
20
|
+
const core_1 = require("@memberjunction/core");
|
|
21
21
|
const json_preprocessor_1 = require("./json-preprocessor");
|
|
22
|
+
const metadata_keywords_1 = require("../constants/metadata-keywords");
|
|
22
23
|
/**
|
|
23
24
|
* Core engine for synchronizing MemberJunction metadata between database and files
|
|
24
25
|
*
|
|
@@ -39,7 +40,7 @@ class SyncEngine {
|
|
|
39
40
|
* @param contextUser - The user context for database operations
|
|
40
41
|
*/
|
|
41
42
|
constructor(contextUser) {
|
|
42
|
-
this.metadata = new
|
|
43
|
+
this.metadata = new core_1.Metadata();
|
|
43
44
|
this.contextUser = contextUser;
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
@@ -96,9 +97,11 @@ class SyncEngine {
|
|
|
96
97
|
if (value !== null && typeof value === 'object') {
|
|
97
98
|
// Check if it's an array or a plain object (not a Date, etc.)
|
|
98
99
|
if (Array.isArray(value) || value.constructor === Object) {
|
|
99
|
-
//
|
|
100
|
+
// First recursively process any @lookup, @file, @parent references inside the object
|
|
101
|
+
const processedValue = await this.processJsonFieldValues(value, baseDir, parentRecord, rootRecord, depth, batchContext);
|
|
102
|
+
// Then convert to pretty-printed JSON string for inline metadata objects
|
|
100
103
|
// Objects from @file references will be handled by BaseEntity during save
|
|
101
|
-
return JSON.stringify(
|
|
104
|
+
return JSON.stringify(processedValue, null, 2);
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
// If not a string, return as-is (numbers, booleans, null, etc.)
|
|
@@ -107,32 +110,28 @@ class SyncEngine {
|
|
|
107
110
|
}
|
|
108
111
|
// If string starts with @ but isn't one of our known reference types, return as-is
|
|
109
112
|
// This handles cases like npm package names (@mui/material, @angular/core, etc.)
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
const isKnownReference = knownPrefixes.some((prefix) => value.startsWith(prefix));
|
|
113
|
-
if (!isKnownReference) {
|
|
114
|
-
return value; // Not a MetadataSync reference, just a string that happens to start with @
|
|
115
|
-
}
|
|
113
|
+
if ((0, metadata_keywords_1.isNonKeywordAtSymbol)(value)) {
|
|
114
|
+
return value; // Not a MetadataSync reference, just a string that happens to start with @
|
|
116
115
|
}
|
|
117
116
|
// Check for @parent: reference
|
|
118
|
-
if (value.startsWith(
|
|
117
|
+
if (value.startsWith(metadata_keywords_1.METADATA_KEYWORDS.PARENT)) {
|
|
119
118
|
if (!parentRecord) {
|
|
120
119
|
throw new Error(`@parent reference used but no parent record available: ${value}`);
|
|
121
120
|
}
|
|
122
|
-
const fieldName =
|
|
121
|
+
const fieldName = (0, metadata_keywords_1.extractKeywordValue)(value) || '';
|
|
123
122
|
return parentRecord.Get(fieldName);
|
|
124
123
|
}
|
|
125
124
|
// Check for @root: reference
|
|
126
|
-
if (value.startsWith(
|
|
125
|
+
if (value.startsWith(metadata_keywords_1.METADATA_KEYWORDS.ROOT)) {
|
|
127
126
|
if (!rootRecord) {
|
|
128
127
|
throw new Error(`@root reference used but no root record available: ${value}`);
|
|
129
128
|
}
|
|
130
|
-
const fieldName =
|
|
129
|
+
const fieldName = (0, metadata_keywords_1.extractKeywordValue)(value) || '';
|
|
131
130
|
return rootRecord.Get(fieldName);
|
|
132
131
|
}
|
|
133
132
|
// Check for @file: reference
|
|
134
|
-
if (value.startsWith(
|
|
135
|
-
const filePath =
|
|
133
|
+
if (value.startsWith(metadata_keywords_1.METADATA_KEYWORDS.FILE)) {
|
|
134
|
+
const filePath = (0, metadata_keywords_1.extractKeywordValue)(value);
|
|
136
135
|
const fullPath = path_1.default.resolve(baseDir, filePath);
|
|
137
136
|
if (await fs_extra_1.default.pathExists(fullPath)) {
|
|
138
137
|
// Check if this is a JSON file that might contain @include directives
|
|
@@ -179,8 +178,8 @@ class SyncEngine {
|
|
|
179
178
|
}
|
|
180
179
|
}
|
|
181
180
|
// Check for @url: reference
|
|
182
|
-
if (value.startsWith(
|
|
183
|
-
const url =
|
|
181
|
+
if (value.startsWith(metadata_keywords_1.METADATA_KEYWORDS.URL)) {
|
|
182
|
+
const url = (0, metadata_keywords_1.extractKeywordValue)(value);
|
|
184
183
|
try {
|
|
185
184
|
const response = await axios_1.default.get(url);
|
|
186
185
|
return response.data;
|
|
@@ -190,8 +189,8 @@ class SyncEngine {
|
|
|
190
189
|
}
|
|
191
190
|
}
|
|
192
191
|
// Check for @lookup: reference
|
|
193
|
-
if (value.startsWith(
|
|
194
|
-
const lookupStr =
|
|
192
|
+
if (value.startsWith(metadata_keywords_1.METADATA_KEYWORDS.LOOKUP)) {
|
|
193
|
+
const lookupStr = (0, metadata_keywords_1.extractKeywordValue)(value);
|
|
195
194
|
// Parse lookup with optional create syntax
|
|
196
195
|
// Format: EntityName.Field1=Value1&Field2=Value2?create&OtherField=Value
|
|
197
196
|
const entityMatch = lookupStr.match(/^([^.]+)\./);
|
|
@@ -236,8 +235,8 @@ class SyncEngine {
|
|
|
236
235
|
return await this.resolveLookup(entityName, lookupFields, hasCreate, createFields, batchContext);
|
|
237
236
|
}
|
|
238
237
|
// Check for @env: reference
|
|
239
|
-
if (value.startsWith(
|
|
240
|
-
const envVar =
|
|
238
|
+
if (value.startsWith(metadata_keywords_1.METADATA_KEYWORDS.ENV)) {
|
|
239
|
+
const envVar = (0, metadata_keywords_1.extractKeywordValue)(value);
|
|
241
240
|
const envValue = process.env[envVar];
|
|
242
241
|
if (envValue === undefined) {
|
|
243
242
|
throw new Error(`Environment variable not found: ${envVar}`);
|
|
@@ -299,7 +298,7 @@ class SyncEngine {
|
|
|
299
298
|
}
|
|
300
299
|
}
|
|
301
300
|
// Not found in batch context, check database
|
|
302
|
-
const rv = new
|
|
301
|
+
const rv = new core_1.RunView();
|
|
303
302
|
const entityInfo = this.metadata.EntityByName(entityName);
|
|
304
303
|
if (!entityInfo) {
|
|
305
304
|
throw new Error(`Entity not found: ${entityName}`);
|
|
@@ -307,7 +306,7 @@ class SyncEngine {
|
|
|
307
306
|
// Build compound filter for all lookup fields
|
|
308
307
|
const filterParts = [];
|
|
309
308
|
for (const { fieldName, fieldValue } of lookupFields) {
|
|
310
|
-
const field = entityInfo.Fields.find(
|
|
309
|
+
const field = entityInfo.Fields.find(f => f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());
|
|
311
310
|
if (!field) {
|
|
312
311
|
throw new Error(`Field '${fieldName}' not found in entity '${entityName}'`);
|
|
313
312
|
}
|
|
@@ -324,7 +323,7 @@ class SyncEngine {
|
|
|
324
323
|
const result = await rv.RunView({
|
|
325
324
|
EntityName: entityName,
|
|
326
325
|
ExtraFilter: extraFilter,
|
|
327
|
-
MaxRows: 1
|
|
326
|
+
MaxRows: 1
|
|
328
327
|
}, this.contextUser);
|
|
329
328
|
if (result.Success && result.Results.length > 0) {
|
|
330
329
|
if (entityInfo.PrimaryKeys.length > 0) {
|
|
@@ -368,8 +367,7 @@ class SyncEngine {
|
|
|
368
367
|
if (message) {
|
|
369
368
|
throw new Error(`Failed to auto-create ${entityName}: ${message}`);
|
|
370
369
|
}
|
|
371
|
-
const errors = newEntity.LatestResult?.Errors?.map(
|
|
372
|
-
'Unknown error';
|
|
370
|
+
const errors = newEntity.LatestResult?.Errors?.map(err => typeof err === 'string' ? err : (err?.message || JSON.stringify(err)))?.join(', ') || 'Unknown error';
|
|
373
371
|
throw new Error(`Failed to auto-create ${entityName}: ${errors}`);
|
|
374
372
|
}
|
|
375
373
|
// Return the new ID
|
|
@@ -496,14 +494,14 @@ class SyncEngine {
|
|
|
496
494
|
return obj;
|
|
497
495
|
}
|
|
498
496
|
if (Array.isArray(obj)) {
|
|
499
|
-
return Promise.all(obj.map(
|
|
497
|
+
return Promise.all(obj.map(item => this.resolveFileReferencesForChecksum(item, entityDir)));
|
|
500
498
|
}
|
|
501
499
|
const result = {};
|
|
502
500
|
for (const [key, value] of Object.entries(obj)) {
|
|
503
|
-
if (typeof value === 'string' && value.startsWith(
|
|
501
|
+
if (typeof value === 'string' && value.startsWith(metadata_keywords_1.METADATA_KEYWORDS.FILE)) {
|
|
504
502
|
// Process @file reference and include actual content
|
|
505
503
|
try {
|
|
506
|
-
const filePath =
|
|
504
|
+
const filePath = (0, metadata_keywords_1.extractKeywordValue)(value);
|
|
507
505
|
const fullPath = path_1.default.isAbsolute(filePath) ? filePath : path_1.default.join(entityDir, filePath);
|
|
508
506
|
if (await fs_extra_1.default.pathExists(fullPath)) {
|
|
509
507
|
let processedContent;
|
|
@@ -537,7 +535,7 @@ class SyncEngine {
|
|
|
537
535
|
result[key] = {
|
|
538
536
|
_checksumType: 'file',
|
|
539
537
|
_reference: value,
|
|
540
|
-
_content: processedContent
|
|
538
|
+
_content: processedContent
|
|
541
539
|
};
|
|
542
540
|
}
|
|
543
541
|
else {
|
|
@@ -634,7 +632,7 @@ class SyncEngine {
|
|
|
634
632
|
}
|
|
635
633
|
// First, check if the record exists using RunView to avoid "Error in BaseEntity.Load" messages
|
|
636
634
|
// when records don't exist (which is a normal scenario during sync operations)
|
|
637
|
-
const rv = new
|
|
635
|
+
const rv = new core_1.RunView();
|
|
638
636
|
// Build filter for primary key(s)
|
|
639
637
|
const filters = [];
|
|
640
638
|
for (const pk of entityInfo.PrimaryKeys) {
|
|
@@ -643,7 +641,7 @@ class SyncEngine {
|
|
|
643
641
|
throw new Error(`Missing primary key value for ${pk.Name} in entity ${entityName}`);
|
|
644
642
|
}
|
|
645
643
|
// Check if field needs quotes
|
|
646
|
-
const field = entityInfo.Fields.find(
|
|
644
|
+
const field = entityInfo.Fields.find(f => f.Name === pk.Name);
|
|
647
645
|
const quotes = field?.NeedsQuotes ? "'" : '';
|
|
648
646
|
const escapedValue = field?.NeedsQuotes && typeof value === 'string' ? value.replace(/'/g, "''") : value;
|
|
649
647
|
filters.push(`${pk.Name} = ${quotes}${escapedValue}${quotes}`);
|
|
@@ -651,7 +649,7 @@ class SyncEngine {
|
|
|
651
649
|
const result = await rv.RunView({
|
|
652
650
|
EntityName: entityName,
|
|
653
651
|
ExtraFilter: filters.join(' AND '),
|
|
654
|
-
MaxRows: 1
|
|
652
|
+
MaxRows: 1
|
|
655
653
|
}, this.contextUser);
|
|
656
654
|
// If no record found, return null without attempting to load
|
|
657
655
|
if (!result.Success || result.Results.length === 0) {
|
|
@@ -659,7 +657,7 @@ class SyncEngine {
|
|
|
659
657
|
}
|
|
660
658
|
// Record exists, now load it properly through the entity
|
|
661
659
|
const entity = await this.createEntityObject(entityName);
|
|
662
|
-
const compositeKey = new
|
|
660
|
+
const compositeKey = new core_1.CompositeKey();
|
|
663
661
|
compositeKey.LoadFromSimpleObject(primaryKey);
|
|
664
662
|
const loaded = await entity.InnerLoad(compositeKey);
|
|
665
663
|
return loaded ? entity : null;
|
|
@@ -711,7 +709,7 @@ class SyncEngine {
|
|
|
711
709
|
const resolvedPath = path_1.default.resolve(currentDir, trimmedPath);
|
|
712
710
|
try {
|
|
713
711
|
// Check if the included file exists
|
|
714
|
-
if (!
|
|
712
|
+
if (!await fs_extra_1.default.pathExists(resolvedPath)) {
|
|
715
713
|
throw new Error(`Included file not found: ${resolvedPath}`);
|
|
716
714
|
}
|
|
717
715
|
// Read the included file
|
|
@@ -752,7 +750,7 @@ class SyncEngine {
|
|
|
752
750
|
}
|
|
753
751
|
// Handle arrays
|
|
754
752
|
if (Array.isArray(obj)) {
|
|
755
|
-
return Promise.all(obj.map(
|
|
753
|
+
return Promise.all(obj.map(item => this.processJsonFieldValues(item, baseDir, parentRecord, rootRecord, depth, batchContext)));
|
|
756
754
|
}
|
|
757
755
|
// Handle objects
|
|
758
756
|
if (typeof obj === 'object') {
|
|
@@ -762,13 +760,7 @@ class SyncEngine {
|
|
|
762
760
|
if (typeof value === 'string') {
|
|
763
761
|
// Check if this looks like a reference that needs processing
|
|
764
762
|
// Only process known reference types, ignore other @ strings (like npm packages)
|
|
765
|
-
if (
|
|
766
|
-
value.startsWith('@lookup:') ||
|
|
767
|
-
value.startsWith('@parent:') ||
|
|
768
|
-
value.startsWith('@root:') ||
|
|
769
|
-
value.startsWith('@env:') ||
|
|
770
|
-
value.startsWith('@template:') ||
|
|
771
|
-
value.startsWith('@include')) {
|
|
763
|
+
if ((0, metadata_keywords_1.isMetadataKeyword)(value)) {
|
|
772
764
|
result[key] = await this.processFieldValue(value, baseDir, parentRecord, rootRecord, depth, batchContext);
|
|
773
765
|
}
|
|
774
766
|
else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../../src/lib/sync-engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Core synchronization engine for MemberJunction metadata\n * @module sync-engine\n *\n * This module provides the core functionality for synchronizing metadata between\n * the MemberJunction database and local file system representations. It handles\n * special reference types (@file, @url, @lookup, @env, @parent, @root, @template),\n * manages entity operations, and provides utilities for data transformation.\n */\n\nimport path from 'path';\nimport fs from 'fs-extra';\nimport crypto from 'crypto';\nimport axios from 'axios';\nimport { EntityInfo, Metadata, RunView, BaseEntity, CompositeKey, UserInfo } from '@memberjunction/global';\nimport { EntityConfig, FolderConfig } from '../config';\nimport { JsonPreprocessor } from './json-preprocessor';\n\n/**\n * Represents the structure of a metadata record with optional sync tracking\n */\nexport interface RecordData {\n /** Primary key field(s) and their values */\n primaryKey?: Record<string, any>;\n /** Entity field names and their values */\n fields: Record<string, any>;\n /** Related entities organized by entity name */\n relatedEntities?: Record<string, RecordData[]>;\n /** Synchronization metadata for change tracking */\n sync?: {\n /** ISO timestamp of last modification */\n lastModified: string;\n /** SHA256 checksum of the fields object */\n checksum: string;\n };\n /** Delete record directive for removing records from the database */\n deleteRecord?: {\n /** Flag to indicate this record should be deleted */\n delete: boolean;\n /** ISO timestamp of when the deletion was performed */\n deletedAt?: string;\n /** Flag to indicate the record was not found when attempting deletion */\n notFound?: boolean;\n };\n}\n\n/**\n * Core engine for synchronizing MemberJunction metadata between database and files\n *\n * @class SyncEngine\n * @example\n * ```typescript\n * const syncEngine = new SyncEngine(systemUser);\n *\n * // Process a field value with special references\n * const value = await syncEngine.processFieldValue('@lookup:Users.Email=admin@example.com', '/path/to/base');\n * ```\n */\nexport class SyncEngine {\n private metadata: Metadata;\n private contextUser: UserInfo;\n\n /**\n * Creates a new SyncEngine instance\n * @param contextUser - The user context for database operations\n */\n constructor(contextUser: UserInfo) {\n this.metadata = new Metadata();\n this.contextUser = contextUser;\n }\n\n /**\n * Initializes the sync engine by refreshing metadata cache\n * @returns Promise that resolves when initialization is complete\n */\n async initialize(): Promise<void> {\n // Currently no initialization needed as metadata is managed globally\n // Keeping this method for backward compatibility and future use\n }\n\n /**\n * Process special references in field values and handle complex objects\n *\n * Automatically handles:\n * - Arrays and objects are converted to JSON strings\n * - Scalars (strings, numbers, booleans, null) pass through unchanged\n *\n * Handles the following reference types for string values:\n * - `@parent:fieldName` - References a field from the parent record\n * - `@root:fieldName` - References a field from the root record\n * - `@file:path` - Reads content from an external file\n * - `@url:address` - Fetches content from a URL\n * - `@lookup:Entity.Field=Value` - Looks up an entity ID by field value\n * - `@env:VARIABLE` - Reads an environment variable\n *\n * @param value - The field value to process\n * @param baseDir - Base directory for resolving relative file paths\n * @param parentRecord - Optional parent entity for @parent references\n * @param rootRecord - Optional root entity for @root references\n * @returns The processed value with all references resolved\n * @throws Error if a reference cannot be resolved\n *\n * @example\n * ```typescript\n * // File reference\n * const content = await processFieldValue('@file:template.md', '/path/to/dir');\n *\n * // Lookup with auto-create\n * const userId = await processFieldValue('@lookup:Users.Email=john@example.com?create', '/path');\n *\n * // Complex object - automatically stringified\n * const jsonStr = await processFieldValue({items: [{id: 1}, {id: 2}]}, '/path');\n * // Returns: '{\\n \"items\": [\\n {\\n \"id\": 1\\n },\\n {\\n \"id\": 2\\n }\\n ]\\n}'\n * ```\n */\n async processFieldValue(\n value: any,\n baseDir: string,\n parentRecord?: BaseEntity | null,\n rootRecord?: BaseEntity | null,\n depth: number = 0,\n batchContext?: Map<string, BaseEntity>\n ): Promise<any> {\n // Check recursion depth limit\n const MAX_RECURSION_DEPTH = 50;\n if (depth > MAX_RECURSION_DEPTH) {\n throw new Error(`Maximum recursion depth (${MAX_RECURSION_DEPTH}) exceeded while processing field value: ${value}`);\n }\n // Handle arrays and objects that are directly defined in metadata files\n // Note: Objects loaded from @file references are returned as-is to preserve proper escaping\n if (value !== null && typeof value === 'object') {\n // Check if it's an array or a plain object (not a Date, etc.)\n if (Array.isArray(value) || value.constructor === Object) {\n // Convert to pretty-printed JSON string for inline metadata objects\n // Objects from @file references will be handled by BaseEntity during save\n return JSON.stringify(value, null, 2);\n }\n }\n\n // If not a string, return as-is (numbers, booleans, null, etc.)\n if (typeof value !== 'string') {\n return value;\n }\n\n // If string starts with @ but isn't one of our known reference types, return as-is\n // This handles cases like npm package names (@mui/material, @angular/core, etc.)\n if (value.startsWith('@')) {\n const knownPrefixes = ['@parent:', '@root:', '@file:', '@lookup:', '@env:', '@template:', '@include'];\n const isKnownReference = knownPrefixes.some((prefix) => value.startsWith(prefix));\n if (!isKnownReference) {\n return value; // Not a MetadataSync reference, just a string that happens to start with @\n }\n }\n\n // Check for @parent: reference\n if (value.startsWith('@parent:')) {\n if (!parentRecord) {\n throw new Error(`@parent reference used but no parent record available: ${value}`);\n }\n const fieldName = value.substring(8);\n return parentRecord.Get(fieldName);\n }\n\n // Check for @root: reference\n if (value.startsWith('@root:')) {\n if (!rootRecord) {\n throw new Error(`@root reference used but no root record available: ${value}`);\n }\n const fieldName = value.substring(6);\n return rootRecord.Get(fieldName);\n }\n\n // Check for @file: reference\n if (value.startsWith('@file:')) {\n const filePath = value.substring(6);\n const fullPath = path.resolve(baseDir, filePath);\n\n if (await fs.pathExists(fullPath)) {\n // Check if this is a JSON file that might contain @include directives\n if (fullPath.endsWith('.json')) {\n try {\n // Parse as JSON and check for @include directives\n const jsonContent = await fs.readJson(fullPath);\n\n // Check if the JSON contains any @include directives\n const jsonString = JSON.stringify(jsonContent);\n const hasIncludes = jsonString.includes('\"@include') || jsonString.includes('\"@include.');\n\n let processedJson: any;\n if (hasIncludes) {\n // Process @include directives with a fresh preprocessor instance\n const preprocessor = new JsonPreprocessor();\n processedJson = await preprocessor.processFile(fullPath);\n } else {\n processedJson = jsonContent;\n }\n\n // Now recursively process any @file references within the JSON\n const fileDir = path.dirname(fullPath);\n processedJson = await this.processJsonFieldValues(processedJson, fileDir, parentRecord, rootRecord, depth + 1, batchContext);\n\n // Return the processed JSON object directly without stringifying\n // Let BaseEntity handle serialization when saving to database\n // This ensures proper escaping of embedded code/scripts\n return processedJson;\n } catch (jsonError) {\n // Not valid JSON or error processing, fall back to text file handling\n const fileContent = await fs.readFile(fullPath, 'utf-8');\n // Process the file content for {@include} references in text files\n return await this.processFileContentWithIncludes(fileContent, fullPath);\n }\n } else {\n // Not a JSON file, process as text with {@include} support\n const fileContent = await fs.readFile(fullPath, 'utf-8');\n // Process the file content for {@include} references\n return await this.processFileContentWithIncludes(fileContent, fullPath);\n }\n } else {\n throw new Error(`File not found: ${fullPath}`);\n }\n }\n\n // Check for @url: reference\n if (value.startsWith('@url:')) {\n const url = value.substring(5);\n\n try {\n const response = await axios.get(url);\n return response.data;\n } catch (error) {\n throw new Error(`Failed to fetch URL: ${url} - ${error}`);\n }\n }\n\n // Check for @lookup: reference\n if (value.startsWith('@lookup:')) {\n const lookupStr = value.substring(8);\n\n // Parse lookup with optional create syntax\n // Format: EntityName.Field1=Value1&Field2=Value2?create&OtherField=Value\n const entityMatch = lookupStr.match(/^([^.]+)\\./);\n if (!entityMatch) {\n throw new Error(`Invalid lookup format: ${value}`);\n }\n\n const entityName = entityMatch[1];\n const remaining = lookupStr.substring(entityName.length + 1);\n\n // Check if this has ?create syntax\n const hasCreate = remaining.includes('?create');\n const lookupPart = hasCreate ? remaining.split('?')[0] : remaining;\n\n // Parse all lookup fields (can be multiple with &)\n const lookupFields: Array<{ fieldName: string; fieldValue: string }> = [];\n const lookupPairs = lookupPart.split('&');\n\n for (const pair of lookupPairs) {\n const fieldMatch = pair.match(/^(.+?)=(.+)$/);\n if (!fieldMatch) {\n throw new Error(`Invalid lookup field format: ${pair} in ${value}`);\n }\n const [, fieldName, fieldValue] = fieldMatch;\n\n // Recursively process the field value to resolve any nested @ commands\n const processedValue = await this.processFieldValue(fieldValue.trim(), baseDir, parentRecord, rootRecord, depth + 1, batchContext);\n\n lookupFields.push({ fieldName: fieldName.trim(), fieldValue: processedValue });\n }\n\n if (lookupFields.length === 0) {\n throw new Error(`No lookup fields specified: ${value}`);\n }\n\n // Parse additional fields for creation if ?create is present\n let createFields: Record<string, any> = {};\n if (hasCreate && remaining.includes('?create&')) {\n const createPart = remaining.split('?create&')[1];\n const pairs = createPart.split('&');\n for (const pair of pairs) {\n const [key, val] = pair.split('=');\n if (key && val) {\n const decodedVal = decodeURIComponent(val);\n // Recursively process the field value to resolve any nested @ commands\n createFields[key] = await this.processFieldValue(decodedVal, baseDir, parentRecord, rootRecord, depth + 1, batchContext);\n }\n }\n }\n\n return await this.resolveLookup(entityName, lookupFields, hasCreate, createFields, batchContext);\n }\n\n // Check for @env: reference\n if (value.startsWith('@env:')) {\n const envVar = value.substring(5);\n const envValue = process.env[envVar];\n\n if (envValue === undefined) {\n throw new Error(`Environment variable not found: ${envVar}`);\n }\n\n return envValue;\n }\n\n return value;\n }\n\n /**\n * Resolve a lookup reference to an ID, optionally creating the record if it doesn't exist\n *\n * @param entityName - Name of the entity to search in\n * @param fieldName - Field to match against\n * @param fieldValue - Value to search for\n * @param autoCreate - Whether to create the record if not found\n * @param createFields - Additional fields to set when creating\n * @returns The ID of the found or created record\n * @throws Error if lookup fails and autoCreate is false\n *\n * @example\n * ```typescript\n * // Simple lookup\n * const categoryId = await resolveLookup('Categories', 'Name', 'Technology');\n *\n * // Lookup with auto-create\n * const tagId = await resolveLookup('Tags', 'Name', 'New Tag', true, {\n * Description: 'Auto-created tag',\n * Status: 'Active'\n * });\n * ```\n */\n async resolveLookup(\n entityName: string,\n lookupFields: Array<{ fieldName: string; fieldValue: string }>,\n autoCreate: boolean = false,\n createFields: Record<string, any> = {},\n batchContext?: Map<string, BaseEntity>\n ): Promise<string> {\n // First check batch context for in-memory entities\n if (batchContext) {\n // Try to find the entity in batch context\n for (const [, entity] of batchContext) {\n // Check if this is the right entity type\n if (entity.EntityInfo?.Name === entityName) {\n // Check if all lookup fields match\n let allMatch = true;\n for (const { fieldName, fieldValue } of lookupFields) {\n const entityValue = entity.Get(fieldName);\n const normalizedEntityValue = entityValue?.toString() || '';\n const normalizedLookupValue = fieldValue?.toString() || '';\n\n if (normalizedEntityValue !== normalizedLookupValue) {\n allMatch = false;\n break;\n }\n }\n\n if (allMatch) {\n // Found in batch context, return primary key\n const entityInfo = this.metadata.EntityByName(entityName);\n if (entityInfo && entityInfo.PrimaryKeys.length > 0) {\n const pkeyField = entityInfo.PrimaryKeys[0].Name;\n return entity.Get(pkeyField);\n }\n }\n }\n }\n }\n\n // Not found in batch context, check database\n const rv = new RunView();\n const entityInfo = this.metadata.EntityByName(entityName);\n if (!entityInfo) {\n throw new Error(`Entity not found: ${entityName}`);\n }\n\n // Build compound filter for all lookup fields\n const filterParts: string[] = [];\n for (const { fieldName, fieldValue } of lookupFields) {\n const field = entityInfo.Fields.find((f) => f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());\n if (!field) {\n throw new Error(`Field '${fieldName}' not found in entity '${entityName}'`);\n }\n\n // Handle null values properly\n if (fieldValue.trim().toLowerCase() === 'null') {\n filterParts.push(`${fieldName} IS NULL`);\n } else {\n const quotes = field.NeedsQuotes ? \"'\" : '';\n filterParts.push(`${fieldName} = ${quotes}${fieldValue.replace(/'/g, \"''\")}${quotes}`);\n }\n }\n\n const extraFilter = filterParts.join(' AND ');\n const result = await rv.RunView(\n {\n EntityName: entityName,\n ExtraFilter: extraFilter,\n MaxRows: 1,\n },\n this.contextUser\n );\n\n if (result.Success && result.Results.length > 0) {\n if (entityInfo.PrimaryKeys.length > 0) {\n const pkeyField = entityInfo.PrimaryKeys[0].Name;\n const id = result.Results[0][pkeyField];\n return id;\n }\n }\n\n // If not found and auto-create is enabled, create the record\n if (autoCreate) {\n const newEntity = await this.metadata.GetEntityObject(entityName, this.contextUser);\n if (!newEntity) {\n throw new Error(`Failed to create entity object for: ${entityName}`);\n }\n\n newEntity.NewRecord();\n\n // UUID generation now happens automatically in BaseEntity.NewRecord()\n\n // Set all lookup fields\n for (const { fieldName, fieldValue } of lookupFields) {\n if (fieldName in newEntity) {\n // Handle null values properly\n if (fieldValue.toLowerCase() === 'null') {\n (newEntity as any)[fieldName] = null;\n } else {\n (newEntity as any)[fieldName] = fieldValue;\n }\n }\n }\n\n // Set any additional fields provided\n for (const [key, value] of Object.entries(createFields)) {\n if (key in newEntity) {\n (newEntity as any)[key] = value;\n }\n }\n\n // Save the new record (new records are always dirty)\n const filterDesc = lookupFields.map(({ fieldName, fieldValue }) => `${fieldName}='${fieldValue}'`).join(' AND ');\n console.log(`📝 Auto-creating ${entityName} record where ${filterDesc}`);\n const saved = await newEntity.Save();\n if (!saved) {\n const message = newEntity.LatestResult?.Message;\n if (message) {\n throw new Error(`Failed to auto-create ${entityName}: ${message}`);\n }\n\n const errors =\n newEntity.LatestResult?.Errors?.map((err) => (typeof err === 'string' ? err : err?.message || JSON.stringify(err)))?.join(', ') ||\n 'Unknown error';\n throw new Error(`Failed to auto-create ${entityName}: ${errors}`);\n }\n\n // Return the new ID\n if (entityInfo.PrimaryKeys.length > 0) {\n const pkeyField = entityInfo.PrimaryKeys[0].Name;\n const newId = newEntity.Get(pkeyField);\n return newId;\n }\n }\n\n const filterDesc = lookupFields.map(({ fieldName, fieldValue }) => `${fieldName}='${fieldValue}'`).join(' AND ');\n throw new Error(`Lookup failed: No record found in '${entityName}' where ${filterDesc}`);\n }\n\n /**\n * Build cascading defaults for a file path and process field values\n *\n * Walks up the directory tree from the file location, collecting defaults from\n * entity config and folder configs, with deeper folders overriding parent values.\n * All default values are processed for special references.\n *\n * @param filePath - Path to the file being processed\n * @param entityConfig - Entity configuration containing base defaults\n * @returns Processed defaults with all references resolved\n * @throws Error if any default value processing fails\n */\n async buildDefaults(filePath: string, entityConfig: EntityConfig): Promise<Record<string, any>> {\n const parts = path.dirname(filePath).split(path.sep);\n let defaults: Record<string, any> = { ...entityConfig.defaults };\n\n // Walk up the directory tree building defaults\n let currentPath = '';\n for (const part of parts) {\n currentPath = path.join(currentPath, part);\n const folderConfig = await this.loadFolderConfig(currentPath);\n\n if (folderConfig?.defaults) {\n defaults = { ...defaults, ...folderConfig.defaults };\n }\n }\n\n // Process all default values (lookups, file references, etc.)\n const processedDefaults: Record<string, any> = {};\n const baseDir = path.dirname(filePath);\n\n for (const [field, value] of Object.entries(defaults)) {\n try {\n processedDefaults[field] = await this.processFieldValue(value, baseDir, null, null, 0);\n } catch (error) {\n throw new Error(`Failed to process default for field '${field}': ${error}`);\n }\n }\n\n return processedDefaults;\n }\n\n /**\n * Load folder configuration from .mj-folder.json file\n *\n * @param dir - Directory to check for configuration\n * @returns Folder configuration or null if not found/invalid\n * @private\n */\n private async loadFolderConfig(dir: string): Promise<FolderConfig | null> {\n const configPath = path.join(dir, '.mj-folder.json');\n\n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error(`Error loading folder config at ${configPath}:`, error);\n return null;\n }\n }\n\n return null;\n }\n\n /**\n * Calculate SHA256 checksum for data\n *\n * Generates a deterministic hash of the provided data by converting it to\n * formatted JSON and calculating a SHA256 digest. Used for change detection\n * in sync operations.\n *\n * @param data - Any data structure to calculate checksum for\n * @returns Hexadecimal string representation of the SHA256 hash\n *\n * @example\n * ```typescript\n * const checksum = syncEngine.calculateChecksum({\n * name: 'Test Record',\n * value: 42,\n * tags: ['a', 'b']\n * });\n * // Returns consistent hash for same data structure\n * ```\n */\n calculateChecksum(data: any): string {\n const hash = crypto.createHash('sha256');\n hash.update(JSON.stringify(data, null, 2));\n return hash.digest('hex');\n }\n\n /**\n * Calculate checksum including resolved file content\n *\n * Enhanced checksum calculation that resolves @file references and includes\n * the actual file content (with @include directives processed) in the checksum.\n * This ensures that changes to referenced files are detected.\n *\n * @param data - Fields object that may contain @file references\n * @param entityDir - Directory for resolving relative file paths\n * @returns Promise resolving to checksum string\n */\n async calculateChecksumWithFileContent(data: any, entityDir: string): Promise<string> {\n const processedData = await this.resolveFileReferencesForChecksum(data, entityDir);\n return this.calculateChecksum(processedData);\n }\n\n /**\n * Resolve @file references for checksum calculation\n *\n * Recursively processes an object and replaces @file references with their\n * actual content (including resolved @include directives). This ensures the\n * checksum reflects the actual content, not just the reference.\n *\n * @param obj - Object to process\n * @param entityDir - Directory for resolving relative paths\n * @returns Promise resolving to processed object\n * @private\n */\n private async resolveFileReferencesForChecksum(obj: any, entityDir: string): Promise<any> {\n if (!obj || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return Promise.all(obj.map((item) => this.resolveFileReferencesForChecksum(item, entityDir)));\n }\n\n const result: any = {};\n for (const [key, value] of Object.entries(obj)) {\n if (typeof value === 'string' && value.startsWith('@file:')) {\n // Process @file reference and include actual content\n try {\n const filePath = value.substring(6);\n const fullPath = path.isAbsolute(filePath) ? filePath : path.join(entityDir, filePath);\n\n if (await fs.pathExists(fullPath)) {\n let processedContent: string;\n\n // Check if this is a JSON file that might contain @include directives\n if (fullPath.endsWith('.json')) {\n try {\n const jsonContent = await fs.readJson(fullPath);\n const jsonString = JSON.stringify(jsonContent);\n const hasIncludes = jsonString.includes('\"@include') || jsonString.includes('\"@include.');\n\n if (hasIncludes) {\n // Process @include directives\n const preprocessor = new JsonPreprocessor();\n const processedJson = await preprocessor.processFile(fullPath);\n processedContent = JSON.stringify(processedJson, null, 2);\n } else {\n processedContent = JSON.stringify(jsonContent, null, 2);\n }\n } catch {\n // Not valid JSON, process as text\n const content = await fs.readFile(fullPath, 'utf-8');\n processedContent = await this.processFileContentWithIncludes(content, fullPath);\n }\n } else {\n // Text file - process {@include} references\n const content = await fs.readFile(fullPath, 'utf-8');\n processedContent = await this.processFileContentWithIncludes(content, fullPath);\n }\n\n result[key] = {\n _checksumType: 'file',\n _reference: value,\n _content: processedContent,\n };\n } else {\n // File doesn't exist, keep the reference\n result[key] = value;\n }\n } catch (error) {\n // Error reading file, keep the reference\n result[key] = value;\n }\n } else if (typeof value === 'object') {\n result[key] = await this.resolveFileReferencesForChecksum(value, entityDir);\n } else {\n result[key] = value;\n }\n }\n return result;\n }\n\n /**\n * Get entity metadata information by name\n *\n * Retrieves the EntityInfo object containing schema metadata for the specified entity.\n * Returns null if the entity is not found in the metadata cache.\n *\n * @param entityName - Name of the entity to look up\n * @returns EntityInfo object with schema details or null if not found\n *\n * @example\n * ```typescript\n * const entityInfo = syncEngine.getEntityInfo('AI Prompts');\n * if (entityInfo) {\n * console.log(`Primary keys: ${entityInfo.PrimaryKeys.map(pk => pk.Name).join(', ')}`);\n * }\n * ```\n */\n getEntityInfo(entityName: string): EntityInfo | null {\n return this.metadata.EntityByName(entityName);\n }\n\n /**\n * Create a new entity object instance\n *\n * Uses the MemberJunction metadata system to properly instantiate an entity object.\n * This ensures correct class registration and respects any custom entity subclasses.\n *\n * @param entityName - Name of the entity to create\n * @returns Promise resolving to the new BaseEntity instance\n * @throws Error if entity creation fails\n *\n * @example\n * ```typescript\n * const entity = await syncEngine.createEntityObject('AI Prompts');\n * entity.NewRecord();\n * entity.Set('Name', 'My Prompt');\n * await entity.Save();\n * ```\n */\n async createEntityObject(entityName: string): Promise<BaseEntity> {\n const entity = await this.metadata.GetEntityObject(entityName, this.contextUser);\n if (!entity) {\n throw new Error(`Failed to create entity object for: ${entityName}`);\n }\n return entity;\n }\n\n /**\n * Load an entity record by primary key\n *\n * Retrieves an existing entity record from the database using its primary key values.\n * Supports both single and composite primary keys. Returns null if the record is not found.\n *\n * @param entityName - Name of the entity to load\n * @param primaryKey - Object containing primary key field names and values\n * @returns Promise resolving to the loaded entity or null if not found\n * @throws Error if entity metadata is not found\n *\n * @example\n * ```typescript\n * // Single primary key\n * const entity = await syncEngine.loadEntity('Users', { ID: '123-456' });\n *\n * // Composite primary key\n * const entity = await syncEngine.loadEntity('UserRoles', {\n * UserID: '123-456',\n * RoleID: '789-012'\n * });\n * ```\n */\n async loadEntity(entityName: string, primaryKey: Record<string, any>): Promise<BaseEntity | null> {\n const entityInfo = this.getEntityInfo(entityName);\n\n if (!entityInfo) {\n throw new Error(`Entity not found: ${entityName}`);\n }\n\n // First, check if the record exists using RunView to avoid \"Error in BaseEntity.Load\" messages\n // when records don't exist (which is a normal scenario during sync operations)\n const rv = new RunView();\n\n // Build filter for primary key(s)\n const filters: string[] = [];\n for (const pk of entityInfo.PrimaryKeys) {\n const value = primaryKey[pk.Name];\n if (value === undefined || value === null) {\n throw new Error(`Missing primary key value for ${pk.Name} in entity ${entityName}`);\n }\n\n // Check if field needs quotes\n const field = entityInfo.Fields.find((f) => f.Name === pk.Name);\n const quotes = field?.NeedsQuotes ? \"'\" : '';\n const escapedValue = field?.NeedsQuotes && typeof value === 'string' ? value.replace(/'/g, \"''\") : value;\n filters.push(`${pk.Name} = ${quotes}${escapedValue}${quotes}`);\n }\n\n const result = await rv.RunView(\n {\n EntityName: entityName,\n ExtraFilter: filters.join(' AND '),\n MaxRows: 1,\n },\n this.contextUser\n );\n\n // If no record found, return null without attempting to load\n if (!result.Success || result.Results.length === 0) {\n return null;\n }\n\n // Record exists, now load it properly through the entity\n const entity = await this.createEntityObject(entityName);\n const compositeKey = new CompositeKey();\n compositeKey.LoadFromSimpleObject(primaryKey);\n const loaded = await entity.InnerLoad(compositeKey);\n\n return loaded ? entity : null;\n }\n\n /**\n * Process file content with {@include} references\n *\n * Recursively processes a file's content to resolve `{@include path}` references.\n * Include references use JSDoc-style syntax and support:\n * - Relative paths resolved from the containing file's directory\n * - Recursive includes (includes within included files)\n * - Circular reference detection to prevent infinite loops\n * - Seamless content substitution maintaining surrounding text\n *\n * @param content - The file content to process\n * @param filePath - Path to the file being processed\n * @param visitedPaths - Set of already visited file paths for circular reference detection\n * @returns Promise resolving to the content with all includes resolved\n * @throws Error if circular reference detected or included file not found\n *\n * @example\n * ```typescript\n * // Content with include reference\n * const content = 'This is a {@include ./shared/header.md} example';\n *\n * // Resolves to:\n * const result = await processFileContentWithIncludes('/path/to/file.md', content);\n * // 'This is a [contents of header.md] example'\n * ```\n */\n private async processFileContentWithIncludes(content: string, filePath: string, visitedPaths: Set<string> = new Set()): Promise<string> {\n // Add current file to visited set\n const absolutePath = path.resolve(filePath);\n if (visitedPaths.has(absolutePath)) {\n throw new Error(`Circular reference detected: ${absolutePath} is already being processed`);\n }\n visitedPaths.add(absolutePath);\n\n // Pattern to match {@include path} references\n // Supports whitespace around the path for flexibility\n const includePattern = /\\{@include\\s+([^\\}]+)\\s*\\}/g;\n\n let processedContent = content;\n let match: RegExpExecArray | null;\n\n // Process all {@include} references\n while ((match = includePattern.exec(content)) !== null) {\n const [fullMatch, includePath] = match;\n const trimmedPath = includePath.trim();\n\n // Resolve the include path relative to the current file's directory\n const currentDir = path.dirname(filePath);\n const resolvedPath = path.resolve(currentDir, trimmedPath);\n\n try {\n // Check if the included file exists\n if (!(await fs.pathExists(resolvedPath))) {\n throw new Error(`Included file not found: ${resolvedPath}`);\n }\n\n // Read the included file\n const includedContent = await fs.readFile(resolvedPath, 'utf-8');\n\n // Recursively process the included content for nested includes\n const processedInclude = await this.processFileContentWithIncludes(\n includedContent,\n resolvedPath,\n new Set(visitedPaths) // Pass a copy to allow the same file in different branches\n );\n\n // Replace the {@include} reference with the processed content\n processedContent = processedContent.replace(fullMatch, processedInclude);\n } catch (error) {\n // Enhance error message with context\n throw new Error(`Failed to process {@include ${trimmedPath}} in ${filePath}: ${error}`);\n }\n }\n\n return processedContent;\n }\n\n /**\n * Recursively process field values in a JSON object\n *\n * Processes all string values in a JSON object through processFieldValue,\n * which handles @file, @lookup, @parent, @root references. This ensures\n * that nested @file references within JSON files are properly resolved.\n *\n * @param obj - JSON object to process\n * @param baseDir - Base directory for resolving relative file paths\n * @param parentRecord - Parent entity record for @parent references\n * @param rootRecord - Root entity record for @root references\n * @param depth - Current recursion depth\n * @param batchContext - Batch processing context\n * @returns Promise resolving to processed JSON object\n * @private\n */\n private async processJsonFieldValues(\n obj: any,\n baseDir: string,\n parentRecord?: BaseEntity | null,\n rootRecord?: BaseEntity | null,\n depth: number = 0,\n batchContext?: Map<string, BaseEntity>\n ): Promise<any> {\n // Handle null and undefined\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return Promise.all(obj.map((item) => this.processJsonFieldValues(item, baseDir, parentRecord, rootRecord, depth, batchContext)));\n }\n\n // Handle objects\n if (typeof obj === 'object') {\n const result: any = {};\n for (const [key, value] of Object.entries(obj)) {\n // Process string values that might contain references\n if (typeof value === 'string') {\n // Check if this looks like a reference that needs processing\n // Only process known reference types, ignore other @ strings (like npm packages)\n if (\n value.startsWith('@file:') ||\n value.startsWith('@lookup:') ||\n value.startsWith('@parent:') ||\n value.startsWith('@root:') ||\n value.startsWith('@env:') ||\n value.startsWith('@template:') ||\n value.startsWith('@include')\n ) {\n result[key] = await this.processFieldValue(value, baseDir, parentRecord, rootRecord, depth, batchContext);\n } else {\n result[key] = value;\n }\n } else if (typeof value === 'object') {\n // Recursively process nested objects\n result[key] = await this.processJsonFieldValues(value, baseDir, parentRecord, rootRecord, depth, batchContext);\n } else {\n // Keep primitive values as-is\n result[key] = value;\n }\n }\n return result;\n }\n\n // Return primitive values as-is\n return obj;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../../src/lib/sync-engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Core synchronization engine for MemberJunction metadata\n * @module sync-engine\n * \n * This module provides the core functionality for synchronizing metadata between\n * the MemberJunction database and local file system representations. It handles\n * special reference types (@file, @url, @lookup, @env, @parent, @root, @template),\n * manages entity operations, and provides utilities for data transformation.\n */\n\nimport path from 'path';\nimport fs from 'fs-extra';\nimport crypto from 'crypto';\nimport axios from 'axios';\nimport { EntityInfo, Metadata, RunView, BaseEntity, CompositeKey, UserInfo } from '@memberjunction/core';\nimport { EntityConfig, FolderConfig } from '../config';\nimport { JsonPreprocessor } from './json-preprocessor';\nimport {\n METADATA_KEYWORDS,\n METADATA_KEYWORD_PREFIXES,\n isMetadataKeyword,\n isNonKeywordAtSymbol,\n extractKeywordValue\n} from '../constants/metadata-keywords';\n\n/**\n * Represents the structure of a metadata record with optional sync tracking\n */\nexport interface RecordData {\n /** Primary key field(s) and their values */\n primaryKey?: Record<string, any>;\n /** Entity field names and their values */\n fields: Record<string, any>;\n /** Related entities organized by entity name */\n relatedEntities?: Record<string, RecordData[]>;\n /** Synchronization metadata for change tracking */\n sync?: {\n /** ISO timestamp of last modification */\n lastModified: string;\n /** SHA256 checksum of the fields object */\n checksum: string;\n };\n /** Delete record directive for removing records from the database */\n deleteRecord?: {\n /** Flag to indicate this record should be deleted */\n delete: boolean;\n /** ISO timestamp of when the deletion was performed */\n deletedAt?: string;\n /** Flag to indicate the record was not found when attempting deletion */\n notFound?: boolean;\n };\n}\n\n/**\n * Core engine for synchronizing MemberJunction metadata between database and files\n * \n * @class SyncEngine\n * @example\n * ```typescript\n * const syncEngine = new SyncEngine(systemUser);\n * \n * // Process a field value with special references\n * const value = await syncEngine.processFieldValue('@lookup:Users.Email=admin@example.com', '/path/to/base');\n * ```\n */\nexport class SyncEngine {\n private metadata: Metadata;\n private contextUser: UserInfo;\n\n /**\n * Creates a new SyncEngine instance\n * @param contextUser - The user context for database operations\n */\n constructor(contextUser: UserInfo) {\n this.metadata = new Metadata();\n this.contextUser = contextUser;\n }\n \n /**\n * Initializes the sync engine by refreshing metadata cache\n * @returns Promise that resolves when initialization is complete\n */\n async initialize(): Promise<void> {\n // Currently no initialization needed as metadata is managed globally\n // Keeping this method for backward compatibility and future use\n }\n \n /**\n * Process special references in field values and handle complex objects\n * \n * Automatically handles:\n * - Arrays and objects are converted to JSON strings\n * - Scalars (strings, numbers, booleans, null) pass through unchanged\n * \n * Handles the following reference types for string values:\n * - `@parent:fieldName` - References a field from the parent record\n * - `@root:fieldName` - References a field from the root record\n * - `@file:path` - Reads content from an external file\n * - `@url:address` - Fetches content from a URL\n * - `@lookup:Entity.Field=Value` - Looks up an entity ID by field value\n * - `@env:VARIABLE` - Reads an environment variable\n * \n * @param value - The field value to process\n * @param baseDir - Base directory for resolving relative file paths\n * @param parentRecord - Optional parent entity for @parent references\n * @param rootRecord - Optional root entity for @root references\n * @returns The processed value with all references resolved\n * @throws Error if a reference cannot be resolved\n * \n * @example\n * ```typescript\n * // File reference\n * const content = await processFieldValue('@file:template.md', '/path/to/dir');\n * \n * // Lookup with auto-create\n * const userId = await processFieldValue('@lookup:Users.Email=john@example.com?create', '/path');\n * \n * // Complex object - automatically stringified\n * const jsonStr = await processFieldValue({items: [{id: 1}, {id: 2}]}, '/path');\n * // Returns: '{\\n \"items\": [\\n {\\n \"id\": 1\\n },\\n {\\n \"id\": 2\\n }\\n ]\\n}'\n * ```\n */\n async processFieldValue(value: any, baseDir: string, parentRecord?: BaseEntity | null, rootRecord?: BaseEntity | null, depth: number = 0, batchContext?: Map<string, BaseEntity>): Promise<any> {\n // Check recursion depth limit\n const MAX_RECURSION_DEPTH = 50;\n if (depth > MAX_RECURSION_DEPTH) {\n throw new Error(`Maximum recursion depth (${MAX_RECURSION_DEPTH}) exceeded while processing field value: ${value}`);\n }\n // Handle arrays and objects that are directly defined in metadata files\n // Note: Objects loaded from @file references are returned as-is to preserve proper escaping\n if (value !== null && typeof value === 'object') {\n // Check if it's an array or a plain object (not a Date, etc.)\n if (Array.isArray(value) || value.constructor === Object) {\n // First recursively process any @lookup, @file, @parent references inside the object\n const processedValue = await this.processJsonFieldValues(value, baseDir, parentRecord, rootRecord, depth, batchContext);\n // Then convert to pretty-printed JSON string for inline metadata objects\n // Objects from @file references will be handled by BaseEntity during save\n return JSON.stringify(processedValue, null, 2);\n }\n }\n \n // If not a string, return as-is (numbers, booleans, null, etc.)\n if (typeof value !== 'string') {\n return value;\n }\n \n // If string starts with @ but isn't one of our known reference types, return as-is\n // This handles cases like npm package names (@mui/material, @angular/core, etc.)\n if (isNonKeywordAtSymbol(value)) {\n return value; // Not a MetadataSync reference, just a string that happens to start with @\n }\n \n // Check for @parent: reference\n if (value.startsWith(METADATA_KEYWORDS.PARENT)) {\n if (!parentRecord) {\n throw new Error(`@parent reference used but no parent record available: ${value}`);\n }\n const fieldName = extractKeywordValue(value) || '';\n return parentRecord.Get(fieldName);\n }\n\n // Check for @root: reference\n if (value.startsWith(METADATA_KEYWORDS.ROOT)) {\n if (!rootRecord) {\n throw new Error(`@root reference used but no root record available: ${value}`);\n }\n const fieldName = extractKeywordValue(value) || '';\n return rootRecord.Get(fieldName);\n }\n\n // Check for @file: reference\n if (value.startsWith(METADATA_KEYWORDS.FILE)) {\n const filePath = extractKeywordValue(value) as string;\n const fullPath = path.resolve(baseDir, filePath);\n \n if (await fs.pathExists(fullPath)) {\n // Check if this is a JSON file that might contain @include directives\n if (fullPath.endsWith('.json')) {\n try {\n // Parse as JSON and check for @include directives\n const jsonContent = await fs.readJson(fullPath);\n \n // Check if the JSON contains any @include directives\n const jsonString = JSON.stringify(jsonContent);\n const hasIncludes = jsonString.includes('\"@include') || jsonString.includes('\"@include.');\n \n let processedJson: any;\n if (hasIncludes) {\n // Process @include directives with a fresh preprocessor instance\n const preprocessor = new JsonPreprocessor();\n processedJson = await preprocessor.processFile(fullPath);\n } else {\n processedJson = jsonContent;\n }\n \n // Now recursively process any @file references within the JSON\n const fileDir = path.dirname(fullPath);\n processedJson = await this.processJsonFieldValues(processedJson, fileDir, parentRecord, rootRecord, depth + 1, batchContext);\n \n // Return the processed JSON object directly without stringifying\n // Let BaseEntity handle serialization when saving to database\n // This ensures proper escaping of embedded code/scripts\n return processedJson;\n } catch (jsonError) {\n // Not valid JSON or error processing, fall back to text file handling\n const fileContent = await fs.readFile(fullPath, 'utf-8');\n // Process the file content for {@include} references in text files\n return await this.processFileContentWithIncludes(fileContent, fullPath);\n }\n } else {\n // Not a JSON file, process as text with {@include} support\n const fileContent = await fs.readFile(fullPath, 'utf-8');\n // Process the file content for {@include} references\n return await this.processFileContentWithIncludes(fileContent, fullPath);\n }\n } else {\n throw new Error(`File not found: ${fullPath}`);\n }\n }\n \n // Check for @url: reference\n if (value.startsWith(METADATA_KEYWORDS.URL)) {\n const url = extractKeywordValue(value) as string;\n \n try {\n const response = await axios.get(url);\n return response.data;\n } catch (error) {\n throw new Error(`Failed to fetch URL: ${url} - ${error}`);\n }\n }\n \n // Check for @lookup: reference\n if (value.startsWith(METADATA_KEYWORDS.LOOKUP)) {\n const lookupStr = extractKeywordValue(value) as string;\n \n // Parse lookup with optional create syntax\n // Format: EntityName.Field1=Value1&Field2=Value2?create&OtherField=Value\n const entityMatch = lookupStr.match(/^([^.]+)\\./);\n if (!entityMatch) {\n throw new Error(`Invalid lookup format: ${value}`);\n }\n \n const entityName = entityMatch[1];\n const remaining = lookupStr.substring(entityName.length + 1);\n \n // Check if this has ?create syntax\n const hasCreate = remaining.includes('?create');\n const lookupPart = hasCreate ? remaining.split('?')[0] : remaining;\n \n // Parse all lookup fields (can be multiple with &)\n const lookupFields: Array<{fieldName: string, fieldValue: string}> = [];\n const lookupPairs = lookupPart.split('&');\n \n for (const pair of lookupPairs) {\n const fieldMatch = pair.match(/^(.+?)=(.+)$/);\n if (!fieldMatch) {\n throw new Error(`Invalid lookup field format: ${pair} in ${value}`);\n }\n const [, fieldName, fieldValue] = fieldMatch;\n \n // Recursively process the field value to resolve any nested @ commands\n const processedValue = await this.processFieldValue(\n fieldValue.trim(), \n baseDir, \n parentRecord, \n rootRecord,\n depth + 1,\n batchContext\n );\n \n lookupFields.push({ fieldName: fieldName.trim(), fieldValue: processedValue });\n }\n \n if (lookupFields.length === 0) {\n throw new Error(`No lookup fields specified: ${value}`);\n }\n \n // Parse additional fields for creation if ?create is present\n let createFields: Record<string, any> = {};\n if (hasCreate && remaining.includes('?create&')) {\n const createPart = remaining.split('?create&')[1];\n const pairs = createPart.split('&');\n for (const pair of pairs) {\n const [key, val] = pair.split('=');\n if (key && val) {\n const decodedVal = decodeURIComponent(val);\n // Recursively process the field value to resolve any nested @ commands\n createFields[key] = await this.processFieldValue(\n decodedVal, \n baseDir, \n parentRecord, \n rootRecord,\n depth + 1,\n batchContext\n );\n }\n }\n }\n \n return await this.resolveLookup(entityName, lookupFields, hasCreate, createFields, batchContext);\n }\n \n // Check for @env: reference\n if (value.startsWith(METADATA_KEYWORDS.ENV)) {\n const envVar = extractKeywordValue(value) as string;\n const envValue = process.env[envVar];\n \n if (envValue === undefined) {\n throw new Error(`Environment variable not found: ${envVar}`);\n }\n \n return envValue;\n }\n \n return value;\n }\n \n /**\n * Resolve a lookup reference to an ID, optionally creating the record if it doesn't exist\n * \n * @param entityName - Name of the entity to search in\n * @param fieldName - Field to match against\n * @param fieldValue - Value to search for\n * @param autoCreate - Whether to create the record if not found\n * @param createFields - Additional fields to set when creating\n * @returns The ID of the found or created record\n * @throws Error if lookup fails and autoCreate is false\n * \n * @example\n * ```typescript\n * // Simple lookup\n * const categoryId = await resolveLookup('Categories', 'Name', 'Technology');\n * \n * // Lookup with auto-create\n * const tagId = await resolveLookup('Tags', 'Name', 'New Tag', true, {\n * Description: 'Auto-created tag',\n * Status: 'Active'\n * });\n * ```\n */\n async resolveLookup(\n entityName: string, \n lookupFields: Array<{fieldName: string, fieldValue: string}>,\n autoCreate: boolean = false,\n createFields: Record<string, any> = {},\n batchContext?: Map<string, BaseEntity>\n ): Promise<string> {\n // First check batch context for in-memory entities\n if (batchContext) {\n // Try to find the entity in batch context\n for (const [, entity] of batchContext) {\n // Check if this is the right entity type\n if (entity.EntityInfo?.Name === entityName) {\n // Check if all lookup fields match\n let allMatch = true;\n for (const {fieldName, fieldValue} of lookupFields) {\n const entityValue = entity.Get(fieldName);\n const normalizedEntityValue = entityValue?.toString() || '';\n const normalizedLookupValue = fieldValue?.toString() || '';\n \n if (normalizedEntityValue !== normalizedLookupValue) {\n allMatch = false;\n break;\n }\n }\n \n if (allMatch) {\n // Found in batch context, return primary key\n const entityInfo = this.metadata.EntityByName(entityName);\n if (entityInfo && entityInfo.PrimaryKeys.length > 0) {\n const pkeyField = entityInfo.PrimaryKeys[0].Name;\n return entity.Get(pkeyField);\n }\n }\n }\n }\n }\n \n // Not found in batch context, check database\n const rv = new RunView();\n const entityInfo = this.metadata.EntityByName(entityName);\n if (!entityInfo) {\n throw new Error(`Entity not found: ${entityName}`);\n }\n \n // Build compound filter for all lookup fields\n const filterParts: string[] = [];\n for (const {fieldName, fieldValue} of lookupFields) {\n const field = entityInfo.Fields.find(f => f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());\n if (!field) {\n throw new Error(`Field '${fieldName}' not found in entity '${entityName}'`);\n }\n \n // Handle null values properly\n if (fieldValue.trim().toLowerCase() === 'null') {\n filterParts.push(`${fieldName} IS NULL`);\n } else {\n const quotes = field.NeedsQuotes ? \"'\" : '';\n filterParts.push(`${fieldName} = ${quotes}${fieldValue.replace(/'/g, \"''\")}${quotes}`);\n }\n }\n \n const extraFilter = filterParts.join(' AND ');\n const result = await rv.RunView({\n EntityName: entityName,\n ExtraFilter: extraFilter,\n MaxRows: 1\n }, this.contextUser);\n \n if (result.Success && result.Results.length > 0) {\n if (entityInfo.PrimaryKeys.length > 0) {\n const pkeyField = entityInfo.PrimaryKeys[0].Name;\n const id = result.Results[0][pkeyField];\n return id;\n }\n }\n \n // If not found and auto-create is enabled, create the record\n if (autoCreate) {\n \n const newEntity = await this.metadata.GetEntityObject(entityName, this.contextUser);\n if (!newEntity) {\n throw new Error(`Failed to create entity object for: ${entityName}`);\n }\n \n newEntity.NewRecord();\n \n // UUID generation now happens automatically in BaseEntity.NewRecord()\n \n // Set all lookup fields\n for (const {fieldName, fieldValue} of lookupFields) {\n if (fieldName in newEntity) {\n // Handle null values properly\n if (fieldValue.toLowerCase() === 'null') {\n (newEntity as any)[fieldName] = null;\n } else {\n (newEntity as any)[fieldName] = fieldValue;\n }\n }\n }\n \n // Set any additional fields provided\n for (const [key, value] of Object.entries(createFields)) {\n if (key in newEntity) {\n (newEntity as any)[key] = value;\n }\n }\n \n // Save the new record (new records are always dirty)\n const filterDesc = lookupFields.map(({fieldName, fieldValue}) => `${fieldName}='${fieldValue}'`).join(' AND ');\n console.log(`📝 Auto-creating ${entityName} record where ${filterDesc}`);\n const saved = await newEntity.Save();\n if (!saved) {\n const message = newEntity.LatestResult?.Message;\n if (message) {\n throw new Error(`Failed to auto-create ${entityName}: ${message}`);\n }\n \n const errors = newEntity.LatestResult?.Errors?.map(err => \n typeof err === 'string' ? err : (err?.message || JSON.stringify(err))\n )?.join(', ') || 'Unknown error';\n throw new Error(`Failed to auto-create ${entityName}: ${errors}`);\n }\n \n // Return the new ID\n if (entityInfo.PrimaryKeys.length > 0) {\n const pkeyField = entityInfo.PrimaryKeys[0].Name;\n const newId = newEntity.Get(pkeyField);\n return newId;\n }\n }\n \n const filterDesc = lookupFields.map(({fieldName, fieldValue}) => `${fieldName}='${fieldValue}'`).join(' AND ');\n throw new Error(`Lookup failed: No record found in '${entityName}' where ${filterDesc}`);\n }\n \n /**\n * Build cascading defaults for a file path and process field values\n * \n * Walks up the directory tree from the file location, collecting defaults from\n * entity config and folder configs, with deeper folders overriding parent values.\n * All default values are processed for special references.\n * \n * @param filePath - Path to the file being processed\n * @param entityConfig - Entity configuration containing base defaults\n * @returns Processed defaults with all references resolved\n * @throws Error if any default value processing fails\n */\n async buildDefaults(filePath: string, entityConfig: EntityConfig): Promise<Record<string, any>> {\n const parts = path.dirname(filePath).split(path.sep);\n let defaults: Record<string, any> = { ...entityConfig.defaults };\n \n // Walk up the directory tree building defaults\n let currentPath = '';\n for (const part of parts) {\n currentPath = path.join(currentPath, part);\n const folderConfig = await this.loadFolderConfig(currentPath);\n \n if (folderConfig?.defaults) {\n defaults = { ...defaults, ...folderConfig.defaults };\n }\n }\n \n // Process all default values (lookups, file references, etc.)\n const processedDefaults: Record<string, any> = {};\n const baseDir = path.dirname(filePath);\n \n for (const [field, value] of Object.entries(defaults)) {\n try {\n processedDefaults[field] = await this.processFieldValue(value, baseDir, null, null, 0);\n } catch (error) {\n throw new Error(`Failed to process default for field '${field}': ${error}`);\n }\n }\n \n return processedDefaults;\n }\n \n /**\n * Load folder configuration from .mj-folder.json file\n * \n * @param dir - Directory to check for configuration\n * @returns Folder configuration or null if not found/invalid\n * @private\n */\n private async loadFolderConfig(dir: string): Promise<FolderConfig | null> {\n const configPath = path.join(dir, '.mj-folder.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error(`Error loading folder config at ${configPath}:`, error);\n return null;\n }\n }\n \n return null;\n }\n \n /**\n * Calculate SHA256 checksum for data\n * \n * Generates a deterministic hash of the provided data by converting it to\n * formatted JSON and calculating a SHA256 digest. Used for change detection\n * in sync operations.\n * \n * @param data - Any data structure to calculate checksum for\n * @returns Hexadecimal string representation of the SHA256 hash\n * \n * @example\n * ```typescript\n * const checksum = syncEngine.calculateChecksum({\n * name: 'Test Record',\n * value: 42,\n * tags: ['a', 'b']\n * });\n * // Returns consistent hash for same data structure\n * ```\n */\n calculateChecksum(data: any): string {\n const hash = crypto.createHash('sha256');\n hash.update(JSON.stringify(data, null, 2));\n return hash.digest('hex');\n }\n\n /**\n * Calculate checksum including resolved file content\n * \n * Enhanced checksum calculation that resolves @file references and includes\n * the actual file content (with @include directives processed) in the checksum.\n * This ensures that changes to referenced files are detected.\n * \n * @param data - Fields object that may contain @file references\n * @param entityDir - Directory for resolving relative file paths\n * @returns Promise resolving to checksum string\n */\n async calculateChecksumWithFileContent(data: any, entityDir: string): Promise<string> {\n const processedData = await this.resolveFileReferencesForChecksum(data, entityDir);\n return this.calculateChecksum(processedData);\n }\n\n /**\n * Resolve @file references for checksum calculation\n * \n * Recursively processes an object and replaces @file references with their\n * actual content (including resolved @include directives). This ensures the\n * checksum reflects the actual content, not just the reference.\n * \n * @param obj - Object to process\n * @param entityDir - Directory for resolving relative paths\n * @returns Promise resolving to processed object\n * @private\n */\n private async resolveFileReferencesForChecksum(obj: any, entityDir: string): Promise<any> {\n if (!obj || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return Promise.all(obj.map(item => this.resolveFileReferencesForChecksum(item, entityDir)));\n }\n\n const result: any = {};\n for (const [key, value] of Object.entries(obj)) {\n if (typeof value === 'string' && value.startsWith(METADATA_KEYWORDS.FILE)) {\n // Process @file reference and include actual content\n try {\n const filePath = extractKeywordValue(value) as string;\n const fullPath = path.isAbsolute(filePath) ? filePath : path.join(entityDir, filePath);\n \n if (await fs.pathExists(fullPath)) {\n let processedContent: string;\n \n // Check if this is a JSON file that might contain @include directives\n if (fullPath.endsWith('.json')) {\n try {\n const jsonContent = await fs.readJson(fullPath);\n const jsonString = JSON.stringify(jsonContent);\n const hasIncludes = jsonString.includes('\"@include') || jsonString.includes('\"@include.');\n \n if (hasIncludes) {\n // Process @include directives\n const preprocessor = new JsonPreprocessor();\n const processedJson = await preprocessor.processFile(fullPath);\n processedContent = JSON.stringify(processedJson, null, 2);\n } else {\n processedContent = JSON.stringify(jsonContent, null, 2);\n }\n } catch {\n // Not valid JSON, process as text\n const content = await fs.readFile(fullPath, 'utf-8');\n processedContent = await this.processFileContentWithIncludes(content, fullPath);\n }\n } else {\n // Text file - process {@include} references\n const content = await fs.readFile(fullPath, 'utf-8');\n processedContent = await this.processFileContentWithIncludes(content, fullPath);\n }\n \n result[key] = {\n _checksumType: 'file',\n _reference: value,\n _content: processedContent\n };\n } else {\n // File doesn't exist, keep the reference\n result[key] = value;\n }\n } catch (error) {\n // Error reading file, keep the reference\n result[key] = value;\n }\n } else if (typeof value === 'object') {\n result[key] = await this.resolveFileReferencesForChecksum(value, entityDir);\n } else {\n result[key] = value;\n }\n }\n return result;\n }\n \n /**\n * Get entity metadata information by name\n * \n * Retrieves the EntityInfo object containing schema metadata for the specified entity.\n * Returns null if the entity is not found in the metadata cache.\n * \n * @param entityName - Name of the entity to look up\n * @returns EntityInfo object with schema details or null if not found\n * \n * @example\n * ```typescript\n * const entityInfo = syncEngine.getEntityInfo('AI Prompts');\n * if (entityInfo) {\n * console.log(`Primary keys: ${entityInfo.PrimaryKeys.map(pk => pk.Name).join(', ')}`);\n * }\n * ```\n */\n getEntityInfo(entityName: string): EntityInfo | null {\n return this.metadata.EntityByName(entityName);\n }\n \n /**\n * Create a new entity object instance\n * \n * Uses the MemberJunction metadata system to properly instantiate an entity object.\n * This ensures correct class registration and respects any custom entity subclasses.\n * \n * @param entityName - Name of the entity to create\n * @returns Promise resolving to the new BaseEntity instance\n * @throws Error if entity creation fails\n * \n * @example\n * ```typescript\n * const entity = await syncEngine.createEntityObject('AI Prompts');\n * entity.NewRecord();\n * entity.Set('Name', 'My Prompt');\n * await entity.Save();\n * ```\n */\n async createEntityObject(entityName: string): Promise<BaseEntity> {\n const entity = await this.metadata.GetEntityObject(entityName, this.contextUser);\n if (!entity) {\n throw new Error(`Failed to create entity object for: ${entityName}`);\n }\n return entity;\n }\n \n /**\n * Load an entity record by primary key\n * \n * Retrieves an existing entity record from the database using its primary key values.\n * Supports both single and composite primary keys. Returns null if the record is not found.\n * \n * @param entityName - Name of the entity to load\n * @param primaryKey - Object containing primary key field names and values\n * @returns Promise resolving to the loaded entity or null if not found\n * @throws Error if entity metadata is not found\n * \n * @example\n * ```typescript\n * // Single primary key\n * const entity = await syncEngine.loadEntity('Users', { ID: '123-456' });\n * \n * // Composite primary key\n * const entity = await syncEngine.loadEntity('UserRoles', { \n * UserID: '123-456',\n * RoleID: '789-012'\n * });\n * ```\n */\n async loadEntity(entityName: string, primaryKey: Record<string, any>): Promise<BaseEntity | null> {\n const entityInfo = this.getEntityInfo(entityName);\n \n if (!entityInfo) {\n throw new Error(`Entity not found: ${entityName}`);\n }\n \n // First, check if the record exists using RunView to avoid \"Error in BaseEntity.Load\" messages\n // when records don't exist (which is a normal scenario during sync operations)\n const rv = new RunView();\n \n // Build filter for primary key(s)\n const filters: string[] = [];\n for (const pk of entityInfo.PrimaryKeys) {\n const value = primaryKey[pk.Name];\n if (value === undefined || value === null) {\n throw new Error(`Missing primary key value for ${pk.Name} in entity ${entityName}`);\n }\n \n // Check if field needs quotes\n const field = entityInfo.Fields.find(f => f.Name === pk.Name);\n const quotes = field?.NeedsQuotes ? \"'\" : '';\n const escapedValue = field?.NeedsQuotes && typeof value === 'string' ? value.replace(/'/g, \"''\") : value;\n filters.push(`${pk.Name} = ${quotes}${escapedValue}${quotes}`);\n }\n \n const result = await rv.RunView({\n EntityName: entityName,\n ExtraFilter: filters.join(' AND '),\n MaxRows: 1\n }, this.contextUser);\n \n // If no record found, return null without attempting to load\n if (!result.Success || result.Results.length === 0) {\n return null;\n }\n \n // Record exists, now load it properly through the entity\n const entity = await this.createEntityObject(entityName);\n const compositeKey = new CompositeKey();\n compositeKey.LoadFromSimpleObject(primaryKey);\n const loaded = await entity.InnerLoad(compositeKey);\n \n return loaded ? entity : null;\n }\n \n /**\n * Process file content with {@include} references\n * \n * Recursively processes a file's content to resolve `{@include path}` references.\n * Include references use JSDoc-style syntax and support:\n * - Relative paths resolved from the containing file's directory\n * - Recursive includes (includes within included files)\n * - Circular reference detection to prevent infinite loops\n * - Seamless content substitution maintaining surrounding text\n * \n * @param content - The file content to process\n * @param filePath - Path to the file being processed\n * @param visitedPaths - Set of already visited file paths for circular reference detection\n * @returns Promise resolving to the content with all includes resolved\n * @throws Error if circular reference detected or included file not found\n * \n * @example\n * ```typescript\n * // Content with include reference\n * const content = 'This is a {@include ./shared/header.md} example';\n * \n * // Resolves to:\n * const result = await processFileContentWithIncludes('/path/to/file.md', content);\n * // 'This is a [contents of header.md] example'\n * ```\n */\n private async processFileContentWithIncludes(\n content: string,\n filePath: string, \n visitedPaths: Set<string> = new Set()\n ): Promise<string> {\n // Add current file to visited set\n const absolutePath = path.resolve(filePath);\n if (visitedPaths.has(absolutePath)) {\n throw new Error(`Circular reference detected: ${absolutePath} is already being processed`);\n }\n visitedPaths.add(absolutePath);\n \n // Pattern to match {@include path} references\n // Supports whitespace around the path for flexibility\n const includePattern = /\\{@include\\s+([^\\}]+)\\s*\\}/g;\n \n let processedContent = content;\n let match: RegExpExecArray | null;\n \n // Process all {@include} references\n while ((match = includePattern.exec(content)) !== null) {\n const [fullMatch, includePath] = match;\n const trimmedPath = includePath.trim();\n \n // Resolve the include path relative to the current file's directory\n const currentDir = path.dirname(filePath);\n const resolvedPath = path.resolve(currentDir, trimmedPath);\n \n try {\n // Check if the included file exists\n if (!await fs.pathExists(resolvedPath)) {\n throw new Error(`Included file not found: ${resolvedPath}`);\n }\n \n // Read the included file\n const includedContent = await fs.readFile(resolvedPath, 'utf-8');\n \n // Recursively process the included content for nested includes\n const processedInclude = await this.processFileContentWithIncludes(\n includedContent,\n resolvedPath, \n new Set(visitedPaths) // Pass a copy to allow the same file in different branches\n );\n \n // Replace the {@include} reference with the processed content\n processedContent = processedContent.replace(fullMatch, processedInclude);\n } catch (error) {\n // Enhance error message with context\n throw new Error(`Failed to process {@include ${trimmedPath}} in ${filePath}: ${error}`);\n }\n }\n \n return processedContent;\n }\n\n /**\n * Recursively process field values in a JSON object\n * \n * Processes all string values in a JSON object through processFieldValue,\n * which handles @file, @lookup, @parent, @root references. This ensures\n * that nested @file references within JSON files are properly resolved.\n * \n * @param obj - JSON object to process\n * @param baseDir - Base directory for resolving relative file paths\n * @param parentRecord - Parent entity record for @parent references\n * @param rootRecord - Root entity record for @root references\n * @param depth - Current recursion depth\n * @param batchContext - Batch processing context\n * @returns Promise resolving to processed JSON object\n * @private\n */\n private async processJsonFieldValues(\n obj: any,\n baseDir: string,\n parentRecord?: BaseEntity | null,\n rootRecord?: BaseEntity | null,\n depth: number = 0,\n batchContext?: Map<string, BaseEntity>\n ): Promise<any> {\n // Handle null and undefined\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return Promise.all(\n obj.map(item => \n this.processJsonFieldValues(item, baseDir, parentRecord, rootRecord, depth, batchContext)\n )\n );\n }\n\n // Handle objects\n if (typeof obj === 'object') {\n const result: any = {};\n for (const [key, value] of Object.entries(obj)) {\n // Process string values that might contain references\n if (typeof value === 'string') {\n // Check if this looks like a reference that needs processing\n // Only process known reference types, ignore other @ strings (like npm packages)\n if (isMetadataKeyword(value)) {\n result[key] = await this.processFieldValue(value, baseDir, parentRecord, rootRecord, depth, batchContext);\n } else {\n result[key] = value;\n }\n } else if (typeof value === 'object') {\n // Recursively process nested objects\n result[key] = await this.processJsonFieldValues(value, baseDir, parentRecord, rootRecord, depth, batchContext);\n } else {\n // Keep primitive values as-is\n result[key] = value;\n }\n }\n return result;\n }\n\n // Return primitive values as-is\n return obj;\n }\n \n}"]}
|