@memberjunction/metadata-sync 2.127.0 → 2.129.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 +220 -0
- package/dist/lib/RecordProcessor.d.ts +4 -0
- package/dist/lib/RecordProcessor.js +28 -5
- package/dist/lib/RecordProcessor.js.map +1 -1
- package/dist/lib/file-write-batch.js +14 -8
- package/dist/lib/file-write-batch.js.map +1 -1
- package/dist/lib/json-write-helper.js +15 -16
- package/dist/lib/json-write-helper.js.map +1 -1
- package/dist/lib/sync-engine.d.ts +48 -1
- package/dist/lib/sync-engine.js +73 -8
- package/dist/lib/sync-engine.js.map +1 -1
- package/dist/services/PullService.js +15 -10
- package/dist/services/PullService.js.map +1 -1
- package/dist/services/PushService.js +62 -34
- package/dist/services/PushService.js.map +1 -1
- package/package.json +7 -7
|
@@ -31,6 +31,39 @@ export declare class DeferrableLookupError extends Error {
|
|
|
31
31
|
fieldValue: string;
|
|
32
32
|
}>, originalValue: string, targetFieldName?: string);
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Represents a nested lookup resolution that occurred within a parent lookup
|
|
36
|
+
*/
|
|
37
|
+
export interface NestedSyncResolution {
|
|
38
|
+
/** The original @lookup expression */
|
|
39
|
+
expression: string;
|
|
40
|
+
/** The resolved value (typically a GUID) */
|
|
41
|
+
resolved: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Represents a single sync resolution note tracking how a reference was resolved
|
|
45
|
+
*/
|
|
46
|
+
export interface SyncNote {
|
|
47
|
+
/** Type of resolution: 'lookup' for @lookup references, 'parent' for @parent references */
|
|
48
|
+
type: 'lookup' | 'parent';
|
|
49
|
+
/** The field path where this resolution occurred (e.g., "primaryKey.ID" or "fields.CategoryID") */
|
|
50
|
+
field: string;
|
|
51
|
+
/** The original expression before resolution (e.g., "@lookup:Entities.Name=Test") */
|
|
52
|
+
expression: string;
|
|
53
|
+
/** The resolved value (e.g., a GUID) */
|
|
54
|
+
resolved: string;
|
|
55
|
+
/** For lookup resolutions with nested @lookup expressions, tracks each nested resolution */
|
|
56
|
+
nested?: NestedSyncResolution[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Collector for gathering sync resolution notes during field processing
|
|
60
|
+
*/
|
|
61
|
+
export interface SyncResolutionCollector {
|
|
62
|
+
/** Array of collected sync notes */
|
|
63
|
+
notes: SyncNote[];
|
|
64
|
+
/** Current field path prefix (e.g., "fields" or "primaryKey") */
|
|
65
|
+
fieldPrefix: string;
|
|
66
|
+
}
|
|
34
67
|
/**
|
|
35
68
|
* Represents the structure of a metadata record with optional sync tracking
|
|
36
69
|
*/
|
|
@@ -102,6 +135,10 @@ export declare class SyncEngine {
|
|
|
102
135
|
* @param baseDir - Base directory for resolving relative file paths
|
|
103
136
|
* @param parentRecord - Optional parent entity for @parent references
|
|
104
137
|
* @param rootRecord - Optional root entity for @root references
|
|
138
|
+
* @param depth - Current recursion depth (for preventing infinite loops)
|
|
139
|
+
* @param batchContext - Optional batch context for in-memory entity resolution
|
|
140
|
+
* @param resolutionCollector - Optional collector for tracking @lookup and @parent resolutions
|
|
141
|
+
* @param fieldName - Optional field name for tracking resolutions
|
|
105
142
|
* @returns The processed value with all references resolved
|
|
106
143
|
* @throws Error if a reference cannot be resolved
|
|
107
144
|
*
|
|
@@ -116,9 +153,14 @@ export declare class SyncEngine {
|
|
|
116
153
|
* // Complex object - automatically stringified
|
|
117
154
|
* const jsonStr = await processFieldValue({items: [{id: 1}, {id: 2}]}, '/path');
|
|
118
155
|
* // Returns: '{\n "items": [\n {\n "id": 1\n },\n {\n "id": 2\n }\n ]\n}'
|
|
156
|
+
*
|
|
157
|
+
* // With resolution collector for tracking
|
|
158
|
+
* const collector: SyncResolutionCollector = { notes: [], fieldPrefix: 'fields' };
|
|
159
|
+
* const result = await processFieldValue('@lookup:Users.Email=admin@example.com', '/path', null, null, 0, undefined, collector, 'UserID');
|
|
160
|
+
* // collector.notes will contain the resolution info
|
|
119
161
|
* ```
|
|
120
162
|
*/
|
|
121
|
-
processFieldValue(value: any, baseDir: string, parentRecord?: BaseEntity | null, rootRecord?: BaseEntity | null, depth?: number, batchContext?: Map<string, BaseEntity
|
|
163
|
+
processFieldValue(value: any, baseDir: string, parentRecord?: BaseEntity | null, rootRecord?: BaseEntity | null, depth?: number, batchContext?: Map<string, BaseEntity>, resolutionCollector?: SyncResolutionCollector, fieldName?: string): Promise<any>;
|
|
122
164
|
/**
|
|
123
165
|
* Resolve a lookup reference to an ID, optionally creating the record if it doesn't exist
|
|
124
166
|
*
|
|
@@ -202,6 +244,11 @@ export declare class SyncEngine {
|
|
|
202
244
|
* ```
|
|
203
245
|
*/
|
|
204
246
|
calculateChecksum(data: any): string;
|
|
247
|
+
/**
|
|
248
|
+
* Replacer function for JSON.stringify that sorts object keys alphabetically
|
|
249
|
+
* Ensures deterministic checksums regardless of property order
|
|
250
|
+
*/
|
|
251
|
+
private sortedReplacer;
|
|
205
252
|
/**
|
|
206
253
|
* Calculate checksum including resolved file content
|
|
207
254
|
*
|
package/dist/lib/sync-engine.js
CHANGED
|
@@ -96,6 +96,10 @@ class SyncEngine {
|
|
|
96
96
|
* @param baseDir - Base directory for resolving relative file paths
|
|
97
97
|
* @param parentRecord - Optional parent entity for @parent references
|
|
98
98
|
* @param rootRecord - Optional root entity for @root references
|
|
99
|
+
* @param depth - Current recursion depth (for preventing infinite loops)
|
|
100
|
+
* @param batchContext - Optional batch context for in-memory entity resolution
|
|
101
|
+
* @param resolutionCollector - Optional collector for tracking @lookup and @parent resolutions
|
|
102
|
+
* @param fieldName - Optional field name for tracking resolutions
|
|
99
103
|
* @returns The processed value with all references resolved
|
|
100
104
|
* @throws Error if a reference cannot be resolved
|
|
101
105
|
*
|
|
@@ -110,9 +114,14 @@ class SyncEngine {
|
|
|
110
114
|
* // Complex object - automatically stringified
|
|
111
115
|
* const jsonStr = await processFieldValue({items: [{id: 1}, {id: 2}]}, '/path');
|
|
112
116
|
* // Returns: '{\n "items": [\n {\n "id": 1\n },\n {\n "id": 2\n }\n ]\n}'
|
|
117
|
+
*
|
|
118
|
+
* // With resolution collector for tracking
|
|
119
|
+
* const collector: SyncResolutionCollector = { notes: [], fieldPrefix: 'fields' };
|
|
120
|
+
* const result = await processFieldValue('@lookup:Users.Email=admin@example.com', '/path', null, null, 0, undefined, collector, 'UserID');
|
|
121
|
+
* // collector.notes will contain the resolution info
|
|
113
122
|
* ```
|
|
114
123
|
*/
|
|
115
|
-
async processFieldValue(value, baseDir, parentRecord, rootRecord, depth = 0, batchContext) {
|
|
124
|
+
async processFieldValue(value, baseDir, parentRecord, rootRecord, depth = 0, batchContext, resolutionCollector, fieldName) {
|
|
116
125
|
// Check recursion depth limit
|
|
117
126
|
const MAX_RECURSION_DEPTH = 50;
|
|
118
127
|
if (depth > MAX_RECURSION_DEPTH) {
|
|
@@ -144,8 +153,18 @@ class SyncEngine {
|
|
|
144
153
|
if (!parentRecord) {
|
|
145
154
|
throw new Error(`@parent reference used but no parent record available: ${value}`);
|
|
146
155
|
}
|
|
147
|
-
const
|
|
148
|
-
|
|
156
|
+
const parentFieldName = (0, metadata_keywords_1.extractKeywordValue)(value) || '';
|
|
157
|
+
const resolvedValue = parentRecord.Get(parentFieldName);
|
|
158
|
+
// Track the resolution if collector is provided
|
|
159
|
+
if (resolutionCollector && fieldName) {
|
|
160
|
+
resolutionCollector.notes.push({
|
|
161
|
+
type: 'parent',
|
|
162
|
+
field: `${resolutionCollector.fieldPrefix}.${fieldName}`,
|
|
163
|
+
expression: value,
|
|
164
|
+
resolved: String(resolvedValue)
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return resolvedValue;
|
|
149
168
|
}
|
|
150
169
|
// Check for @root: reference
|
|
151
170
|
if (value.startsWith(metadata_keywords_1.METADATA_KEYWORDS.ROOT)) {
|
|
@@ -236,15 +255,28 @@ class SyncEngine {
|
|
|
236
255
|
// Parse all lookup fields (can be multiple with &)
|
|
237
256
|
const lookupFields = [];
|
|
238
257
|
const lookupPairs = lookupPart.split('&');
|
|
258
|
+
// Collector for nested resolutions within this lookup
|
|
259
|
+
const nestedResolutions = [];
|
|
239
260
|
for (const pair of lookupPairs) {
|
|
240
261
|
const fieldMatch = pair.match(/^(.+?)=(.+)$/);
|
|
241
262
|
if (!fieldMatch) {
|
|
242
263
|
throw new Error(`Invalid lookup field format: ${pair} in ${value}`);
|
|
243
264
|
}
|
|
244
|
-
const [,
|
|
265
|
+
const [, lookupFieldName, fieldValue] = fieldMatch;
|
|
266
|
+
const rawFieldValue = fieldValue.trim();
|
|
245
267
|
// Recursively process the field value to resolve any nested @ commands
|
|
246
|
-
|
|
247
|
-
|
|
268
|
+
// Create a temporary collector to capture nested resolutions
|
|
269
|
+
const nestedCollector = { notes: [], fieldPrefix: '' };
|
|
270
|
+
const processedValue = await this.processFieldValue(rawFieldValue, baseDir, parentRecord, rootRecord, depth + 1, batchContext, nestedCollector, lookupFieldName // Pass field name for tracking
|
|
271
|
+
);
|
|
272
|
+
// If the raw value was a lookup expression that got resolved, track it as nested
|
|
273
|
+
if (rawFieldValue.startsWith(metadata_keywords_1.METADATA_KEYWORDS.LOOKUP) && rawFieldValue !== processedValue) {
|
|
274
|
+
nestedResolutions.push({
|
|
275
|
+
expression: rawFieldValue,
|
|
276
|
+
resolved: String(processedValue)
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
lookupFields.push({ fieldName: lookupFieldName.trim(), fieldValue: processedValue });
|
|
248
280
|
}
|
|
249
281
|
if (lookupFields.length === 0) {
|
|
250
282
|
throw new Error(`No lookup fields specified: ${value}`);
|
|
@@ -267,7 +299,22 @@ class SyncEngine {
|
|
|
267
299
|
}
|
|
268
300
|
}
|
|
269
301
|
}
|
|
270
|
-
|
|
302
|
+
const resolvedValue = await this.resolveLookup(entityName, lookupFields, hasCreate, createFields, batchContext, allowDefer, value);
|
|
303
|
+
// Track the resolution if collector is provided
|
|
304
|
+
if (resolutionCollector && fieldName) {
|
|
305
|
+
const note = {
|
|
306
|
+
type: 'lookup',
|
|
307
|
+
field: `${resolutionCollector.fieldPrefix}.${fieldName}`,
|
|
308
|
+
expression: value,
|
|
309
|
+
resolved: String(resolvedValue)
|
|
310
|
+
};
|
|
311
|
+
// Include nested resolutions if any were captured
|
|
312
|
+
if (nestedResolutions.length > 0) {
|
|
313
|
+
note.nested = nestedResolutions;
|
|
314
|
+
}
|
|
315
|
+
resolutionCollector.notes.push(note);
|
|
316
|
+
}
|
|
317
|
+
return resolvedValue;
|
|
271
318
|
}
|
|
272
319
|
// Check for @env: reference
|
|
273
320
|
if (value.startsWith(metadata_keywords_1.METADATA_KEYWORDS.ENV)) {
|
|
@@ -513,9 +560,27 @@ class SyncEngine {
|
|
|
513
560
|
*/
|
|
514
561
|
calculateChecksum(data) {
|
|
515
562
|
const hash = crypto_1.default.createHash('sha256');
|
|
516
|
-
|
|
563
|
+
// Use a replacer function to ensure consistent key ordering for deterministic checksums
|
|
564
|
+
const sortedJson = JSON.stringify(data, this.sortedReplacer, 2);
|
|
565
|
+
hash.update(sortedJson);
|
|
517
566
|
return hash.digest('hex');
|
|
518
567
|
}
|
|
568
|
+
/**
|
|
569
|
+
* Replacer function for JSON.stringify that sorts object keys alphabetically
|
|
570
|
+
* Ensures deterministic checksums regardless of property order
|
|
571
|
+
*/
|
|
572
|
+
sortedReplacer(key, value) {
|
|
573
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
574
|
+
// Sort object keys alphabetically
|
|
575
|
+
return Object.keys(value)
|
|
576
|
+
.sort()
|
|
577
|
+
.reduce((sorted, key) => {
|
|
578
|
+
sorted[key] = value[key];
|
|
579
|
+
return sorted;
|
|
580
|
+
}, {});
|
|
581
|
+
}
|
|
582
|
+
return value;
|
|
583
|
+
}
|
|
519
584
|
/**
|
|
520
585
|
* Calculate checksum including resolved file content
|
|
521
586
|
*
|
|
@@ -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/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 * Custom error class for lookup failures that can be deferred.\n * When a lookup with `?allowDefer` flag fails, this error is thrown instead of a regular Error.\n * The calling code can catch this specific error type and queue the lookup for later retry.\n */\nexport class DeferrableLookupError extends Error {\n /** The entity name being looked up */\n public readonly entityName: string;\n /** The lookup fields and values that failed */\n public readonly lookupFields: Array<{fieldName: string, fieldValue: string}>;\n /** The original lookup string value */\n public readonly originalValue: string;\n /** The field name where this lookup was used */\n public readonly targetFieldName?: string;\n\n constructor(\n message: string,\n entityName: string,\n lookupFields: Array<{fieldName: string, fieldValue: string}>,\n originalValue: string,\n targetFieldName?: string\n ) {\n super(message);\n this.name = 'DeferrableLookupError';\n this.entityName = entityName;\n this.lookupFields = lookupFields;\n this.originalValue = originalValue;\n this.targetFieldName = targetFieldName;\n\n // Maintains proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, DeferrableLookupError.prototype);\n }\n}\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 flags: ?create, ?allowDefer\n // Format: EntityName.Field1=Value1&Field2=Value2?create&allowDefer&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 // Split into lookup part and flags part\n const questionMarkIndex = remaining.indexOf('?');\n const lookupPart = questionMarkIndex >= 0 ? remaining.substring(0, questionMarkIndex) : remaining;\n const flagsPart = questionMarkIndex >= 0 ? remaining.substring(questionMarkIndex + 1) : '';\n\n // Parse flags from the query string portion\n const flags = new Set(flagsPart.split('&').map(f => f.split('=')[0].toLowerCase()));\n const hasCreate = flags.has('create');\n const allowDefer = flags.has('allowdefer');\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 // These are key=value pairs after the flags (e.g., ?create&Description=Some%20Value)\n let createFields: Record<string, any> = {};\n if (hasCreate && flagsPart) {\n const flagPairs = flagsPart.split('&');\n for (const pair of flagPairs) {\n // Skip known flags\n if (pair.toLowerCase() === 'create' || pair.toLowerCase() === 'allowdefer') {\n continue;\n }\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, allowDefer, value);\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 * @param batchContext - Optional batch context for in-memory entity resolution\n * @param allowDefer - If true, throws DeferrableLookupError on failure instead of regular Error\n * @param originalValue - Original lookup string (for error context in deferred lookups)\n * @returns The ID of the found or created record\n * @throws Error if lookup fails and autoCreate is false and allowDefer is false\n * @throws DeferrableLookupError if lookup fails and allowDefer is true\n *\n * @example\n * ```typescript\n * // Simple lookup\n * const categoryId = await resolveLookup('Categories', [{ fieldName: 'Name', fieldValue: 'Technology' }]);\n *\n * // Lookup with auto-create\n * const tagId = await resolveLookup('Tags', [{ fieldName: 'Name', fieldValue: 'New Tag' }], true, {\n * Description: 'Auto-created tag',\n * Status: 'Active'\n * });\n *\n * // Lookup with allowDefer - will throw DeferrableLookupError on failure\n * try {\n * const id = await resolveLookup('Dashboards', [{ fieldName: 'Name', fieldValue: 'My Dashboard' }],\n * false, {}, batchContext, true, '@lookup:Dashboards.Name=My Dashboard?allowDefer');\n * } catch (e) {\n * if (e instanceof DeferrableLookupError) {\n * // Queue for later retry\n * }\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 allowDefer: boolean = false,\n originalValue?: string\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 const errorMessage = `Lookup failed: No record found in '${entityName}' where ${filterDesc}`;\n\n // If allowDefer is true, throw DeferrableLookupError so caller can queue for retry\n if (allowDefer) {\n throw new DeferrableLookupError(\n errorMessage,\n entityName,\n lookupFields,\n originalValue || `@lookup:${entityName}.${filterDesc}`\n );\n }\n\n throw new Error(errorMessage);\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 top-level strings (important for array elements that are strings with @ syntax)\n if (typeof obj === 'string') {\n if (isMetadataKeyword(obj)) {\n return this.processFieldValue(obj, baseDir, parentRecord, rootRecord, depth, batchContext);\n }\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}"]}
|
|
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 * Custom error class for lookup failures that can be deferred.\n * When a lookup with `?allowDefer` flag fails, this error is thrown instead of a regular Error.\n * The calling code can catch this specific error type and queue the lookup for later retry.\n */\nexport class DeferrableLookupError extends Error {\n /** The entity name being looked up */\n public readonly entityName: string;\n /** The lookup fields and values that failed */\n public readonly lookupFields: Array<{fieldName: string, fieldValue: string}>;\n /** The original lookup string value */\n public readonly originalValue: string;\n /** The field name where this lookup was used */\n public readonly targetFieldName?: string;\n\n constructor(\n message: string,\n entityName: string,\n lookupFields: Array<{fieldName: string, fieldValue: string}>,\n originalValue: string,\n targetFieldName?: string\n ) {\n super(message);\n this.name = 'DeferrableLookupError';\n this.entityName = entityName;\n this.lookupFields = lookupFields;\n this.originalValue = originalValue;\n this.targetFieldName = targetFieldName;\n\n // Maintains proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, DeferrableLookupError.prototype);\n }\n}\n\n/**\n * Represents a nested lookup resolution that occurred within a parent lookup\n */\nexport interface NestedSyncResolution {\n /** The original @lookup expression */\n expression: string;\n /** The resolved value (typically a GUID) */\n resolved: string;\n}\n\n/**\n * Represents a single sync resolution note tracking how a reference was resolved\n */\nexport interface SyncNote {\n /** Type of resolution: 'lookup' for @lookup references, 'parent' for @parent references */\n type: 'lookup' | 'parent';\n /** The field path where this resolution occurred (e.g., \"primaryKey.ID\" or \"fields.CategoryID\") */\n field: string;\n /** The original expression before resolution (e.g., \"@lookup:Entities.Name=Test\") */\n expression: string;\n /** The resolved value (e.g., a GUID) */\n resolved: string;\n /** For lookup resolutions with nested @lookup expressions, tracks each nested resolution */\n nested?: NestedSyncResolution[];\n}\n\n/**\n * Collector for gathering sync resolution notes during field processing\n */\nexport interface SyncResolutionCollector {\n /** Array of collected sync notes */\n notes: SyncNote[];\n /** Current field path prefix (e.g., \"fields\" or \"primaryKey\") */\n fieldPrefix: string;\n}\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 * @param depth - Current recursion depth (for preventing infinite loops)\n * @param batchContext - Optional batch context for in-memory entity resolution\n * @param resolutionCollector - Optional collector for tracking @lookup and @parent resolutions\n * @param fieldName - Optional field name for tracking resolutions\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 * // With resolution collector for tracking\n * const collector: SyncResolutionCollector = { notes: [], fieldPrefix: 'fields' };\n * const result = await processFieldValue('@lookup:Users.Email=admin@example.com', '/path', null, null, 0, undefined, collector, 'UserID');\n * // collector.notes will contain the resolution info\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 resolutionCollector?: SyncResolutionCollector,\n fieldName?: string\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 // 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 parentFieldName = extractKeywordValue(value) || '';\n const resolvedValue = parentRecord.Get(parentFieldName);\n\n // Track the resolution if collector is provided\n if (resolutionCollector && fieldName) {\n resolutionCollector.notes.push({\n type: 'parent',\n field: `${resolutionCollector.fieldPrefix}.${fieldName}`,\n expression: value,\n resolved: String(resolvedValue)\n });\n }\n\n return resolvedValue;\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 flags: ?create, ?allowDefer\n // Format: EntityName.Field1=Value1&Field2=Value2?create&allowDefer&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 // Split into lookup part and flags part\n const questionMarkIndex = remaining.indexOf('?');\n const lookupPart = questionMarkIndex >= 0 ? remaining.substring(0, questionMarkIndex) : remaining;\n const flagsPart = questionMarkIndex >= 0 ? remaining.substring(questionMarkIndex + 1) : '';\n\n // Parse flags from the query string portion\n const flags = new Set(flagsPart.split('&').map(f => f.split('=')[0].toLowerCase()));\n const hasCreate = flags.has('create');\n const allowDefer = flags.has('allowdefer');\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 // Collector for nested resolutions within this lookup\n const nestedResolutions: NestedSyncResolution[] = [];\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 [, lookupFieldName, fieldValue] = fieldMatch;\n const rawFieldValue = fieldValue.trim();\n\n // Recursively process the field value to resolve any nested @ commands\n // Create a temporary collector to capture nested resolutions\n const nestedCollector: SyncResolutionCollector = { notes: [], fieldPrefix: '' };\n const processedValue = await this.processFieldValue(\n rawFieldValue,\n baseDir,\n parentRecord,\n rootRecord,\n depth + 1,\n batchContext,\n nestedCollector,\n lookupFieldName // Pass field name for tracking\n );\n\n // If the raw value was a lookup expression that got resolved, track it as nested\n if (rawFieldValue.startsWith(METADATA_KEYWORDS.LOOKUP) && rawFieldValue !== processedValue) {\n nestedResolutions.push({\n expression: rawFieldValue,\n resolved: String(processedValue)\n });\n }\n\n lookupFields.push({ fieldName: lookupFieldName.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 // These are key=value pairs after the flags (e.g., ?create&Description=Some%20Value)\n let createFields: Record<string, any> = {};\n if (hasCreate && flagsPart) {\n const flagPairs = flagsPart.split('&');\n for (const pair of flagPairs) {\n // Skip known flags\n if (pair.toLowerCase() === 'create' || pair.toLowerCase() === 'allowdefer') {\n continue;\n }\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 const resolvedValue = await this.resolveLookup(entityName, lookupFields, hasCreate, createFields, batchContext, allowDefer, value);\n\n // Track the resolution if collector is provided\n if (resolutionCollector && fieldName) {\n const note: SyncNote = {\n type: 'lookup',\n field: `${resolutionCollector.fieldPrefix}.${fieldName}`,\n expression: value,\n resolved: String(resolvedValue)\n };\n\n // Include nested resolutions if any were captured\n if (nestedResolutions.length > 0) {\n note.nested = nestedResolutions;\n }\n\n resolutionCollector.notes.push(note);\n }\n\n return resolvedValue;\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 * @param batchContext - Optional batch context for in-memory entity resolution\n * @param allowDefer - If true, throws DeferrableLookupError on failure instead of regular Error\n * @param originalValue - Original lookup string (for error context in deferred lookups)\n * @returns The ID of the found or created record\n * @throws Error if lookup fails and autoCreate is false and allowDefer is false\n * @throws DeferrableLookupError if lookup fails and allowDefer is true\n *\n * @example\n * ```typescript\n * // Simple lookup\n * const categoryId = await resolveLookup('Categories', [{ fieldName: 'Name', fieldValue: 'Technology' }]);\n *\n * // Lookup with auto-create\n * const tagId = await resolveLookup('Tags', [{ fieldName: 'Name', fieldValue: 'New Tag' }], true, {\n * Description: 'Auto-created tag',\n * Status: 'Active'\n * });\n *\n * // Lookup with allowDefer - will throw DeferrableLookupError on failure\n * try {\n * const id = await resolveLookup('Dashboards', [{ fieldName: 'Name', fieldValue: 'My Dashboard' }],\n * false, {}, batchContext, true, '@lookup:Dashboards.Name=My Dashboard?allowDefer');\n * } catch (e) {\n * if (e instanceof DeferrableLookupError) {\n * // Queue for later retry\n * }\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 allowDefer: boolean = false,\n originalValue?: string\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 const errorMessage = `Lookup failed: No record found in '${entityName}' where ${filterDesc}`;\n\n // If allowDefer is true, throw DeferrableLookupError so caller can queue for retry\n if (allowDefer) {\n throw new DeferrableLookupError(\n errorMessage,\n entityName,\n lookupFields,\n originalValue || `@lookup:${entityName}.${filterDesc}`\n );\n }\n\n throw new Error(errorMessage);\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 // Use a replacer function to ensure consistent key ordering for deterministic checksums\n const sortedJson = JSON.stringify(data, this.sortedReplacer, 2);\n hash.update(sortedJson);\n return hash.digest('hex');\n }\n\n /**\n * Replacer function for JSON.stringify that sorts object keys alphabetically\n * Ensures deterministic checksums regardless of property order\n */\n private sortedReplacer(key: string, value: any): any {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n // Sort object keys alphabetically\n return Object.keys(value)\n .sort()\n .reduce((sorted: any, key: string) => {\n sorted[key] = value[key];\n return sorted;\n }, {});\n }\n return value;\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 top-level strings (important for array elements that are strings with @ syntax)\n if (typeof obj === 'string') {\n if (isMetadataKeyword(obj)) {\n return this.processFieldValue(obj, baseDir, parentRecord, rootRecord, depth, batchContext);\n }\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}"]}
|
|
@@ -548,14 +548,16 @@ class PullService {
|
|
|
548
548
|
for (const entry of entries) {
|
|
549
549
|
if (entry.isFile()) {
|
|
550
550
|
const fileName = entry.name;
|
|
551
|
+
// Normalize pattern by removing leading **/ (glob recursion prefix)
|
|
552
|
+
const normalizedPattern = pattern.startsWith('**/') ? pattern.substring(3) : pattern;
|
|
551
553
|
// Simple pattern matching
|
|
552
|
-
if (
|
|
554
|
+
if (normalizedPattern === '*.json' && fileName.endsWith('.json')) {
|
|
553
555
|
files.push(path_1.default.join(dir, fileName));
|
|
554
556
|
}
|
|
555
|
-
else if (
|
|
557
|
+
else if (normalizedPattern === '.*.json' && fileName.startsWith('.') && fileName.endsWith('.json')) {
|
|
556
558
|
files.push(path_1.default.join(dir, fileName));
|
|
557
559
|
}
|
|
558
|
-
else if (
|
|
560
|
+
else if (normalizedPattern === fileName) {
|
|
559
561
|
files.push(path_1.default.join(dir, fileName));
|
|
560
562
|
}
|
|
561
563
|
}
|
|
@@ -597,10 +599,9 @@ class PullService {
|
|
|
597
599
|
return existing;
|
|
598
600
|
}
|
|
599
601
|
if (strategy === 'overwrite') {
|
|
602
|
+
// Create result with correct property order: fields, relatedEntities, primaryKey, sync
|
|
600
603
|
const result = {
|
|
601
|
-
fields: { ...newData.fields }
|
|
602
|
-
primaryKey: newData.primaryKey,
|
|
603
|
-
sync: newData.sync
|
|
604
|
+
fields: { ...newData.fields }
|
|
604
605
|
};
|
|
605
606
|
// Restore preserved fields from existing
|
|
606
607
|
if (preserveFields.length > 0 && existing.fields) {
|
|
@@ -610,16 +611,17 @@ class PullService {
|
|
|
610
611
|
}
|
|
611
612
|
}
|
|
612
613
|
}
|
|
614
|
+
// Add relatedEntities before primaryKey and sync
|
|
613
615
|
if (newData.relatedEntities) {
|
|
614
616
|
result.relatedEntities = newData.relatedEntities;
|
|
615
617
|
}
|
|
618
|
+
result.primaryKey = newData.primaryKey;
|
|
619
|
+
result.sync = newData.sync;
|
|
616
620
|
return result;
|
|
617
621
|
}
|
|
618
|
-
// Default 'merge' strategy
|
|
622
|
+
// Default 'merge' strategy - create with correct property order
|
|
619
623
|
const result = {
|
|
620
|
-
fields: { ...existing.fields, ...newData.fields }
|
|
621
|
-
primaryKey: newData.primaryKey || existing.primaryKey,
|
|
622
|
-
sync: newData.sync
|
|
624
|
+
fields: { ...existing.fields, ...newData.fields }
|
|
623
625
|
};
|
|
624
626
|
// Restore preserved fields
|
|
625
627
|
if (preserveFields.length > 0 && existing.fields) {
|
|
@@ -629,12 +631,15 @@ class PullService {
|
|
|
629
631
|
}
|
|
630
632
|
}
|
|
631
633
|
}
|
|
634
|
+
// Add relatedEntities before primaryKey and sync
|
|
632
635
|
if (existing.relatedEntities || newData.relatedEntities) {
|
|
633
636
|
result.relatedEntities = {
|
|
634
637
|
...existing.relatedEntities,
|
|
635
638
|
...newData.relatedEntities
|
|
636
639
|
};
|
|
637
640
|
}
|
|
641
|
+
result.primaryKey = newData.primaryKey || existing.primaryKey;
|
|
642
|
+
result.sync = newData.sync;
|
|
638
643
|
return result;
|
|
639
644
|
}
|
|
640
645
|
async createBackup(filePath, backupDirName) {
|