@memberjunction/metadata-sync 2.111.1 → 2.112.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.
Files changed (35) hide show
  1. package/README.md +50 -50
  2. package/dist/lib/EntityPropertyExtractor.d.ts +1 -1
  3. package/dist/lib/EntityPropertyExtractor.js +18 -4
  4. package/dist/lib/EntityPropertyExtractor.js.map +1 -1
  5. package/dist/lib/FieldExternalizer.d.ts +1 -1
  6. package/dist/lib/FieldExternalizer.js +2 -2
  7. package/dist/lib/FieldExternalizer.js.map +1 -1
  8. package/dist/lib/RecordProcessor.d.ts +1 -1
  9. package/dist/lib/RecordProcessor.js +9 -10
  10. package/dist/lib/RecordProcessor.js.map +1 -1
  11. package/dist/lib/RelatedEntityHandler.d.ts +1 -1
  12. package/dist/lib/RelatedEntityHandler.js +5 -5
  13. package/dist/lib/RelatedEntityHandler.js.map +1 -1
  14. package/dist/lib/provider-utils.d.ts +1 -1
  15. package/dist/lib/provider-utils.js +16 -19
  16. package/dist/lib/provider-utils.js.map +1 -1
  17. package/dist/lib/record-dependency-analyzer.js +21 -27
  18. package/dist/lib/record-dependency-analyzer.js.map +1 -1
  19. package/dist/lib/singleton-manager.d.ts +1 -1
  20. package/dist/lib/singleton-manager.js.map +1 -1
  21. package/dist/lib/sync-engine.d.ts +1 -1
  22. package/dist/lib/sync-engine.js +22 -18
  23. package/dist/lib/sync-engine.js.map +1 -1
  24. package/dist/services/PullService.d.ts +1 -1
  25. package/dist/services/PullService.js +22 -18
  26. package/dist/services/PullService.js.map +1 -1
  27. package/dist/services/PushService.d.ts +1 -1
  28. package/dist/services/PushService.js +15 -21
  29. package/dist/services/PushService.js.map +1 -1
  30. package/dist/services/ValidationService.js +5 -5
  31. package/dist/services/ValidationService.js.map +1 -1
  32. package/dist/services/WatchService.d.ts +1 -1
  33. package/dist/services/WatchService.js +13 -14
  34. package/dist/services/WatchService.js.map +1 -1
  35. package/package.json +6 -7
@@ -1 +1 @@
1
- {"version":3,"file":"singleton-manager.js","sourceRoot":"","sources":["../../src/lib/singleton-manager.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,+CAA2C;AAG3C,iCAAiC;AACjC,IAAI,gBAAgB,GAAsB,IAAI,CAAC;AAE/C,yDAAyD;AACzD,IAAI,qBAAqB,GAA+B,IAAI,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACI,KAAK,UAAU,aAAa,CAAC,WAAqB;IACvD,gDAAgD;IAChD,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,+CAA+C;IAC/C,IAAI,qBAAqB,EAAE,CAAC;QAC1B,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAED,2BAA2B;IAC3B,qBAAqB,GAAG,CAAC,KAAK,IAAI,EAAE;QAClC,gBAAgB,GAAG,IAAI,wBAAU,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,gBAAgB,CAAC,UAAU,EAAE,CAAC;QACpC,OAAO,gBAAgB,CAAC;IAC1B,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAnBD,sCAmBC;AAED;;;;;GAKG;AACH,SAAgB,eAAe;IAC7B,gBAAgB,GAAG,IAAI,CAAC;IACxB,qBAAqB,GAAG,IAAI,CAAC;AAC/B,CAAC;AAHD,0CAGC","sourcesContent":["/**\n * @fileoverview Singleton manager for shared instances across MetadataSync\n * @module singleton-manager\n * \n * This module ensures that expensive resources like SyncEngine are only\n * initialized once per process, preventing duplicate metadata refreshes\n * and improving performance.\n */\n\nimport { SyncEngine } from './sync-engine';\nimport { UserInfo } from '@memberjunction/core';\n\n/** Global SyncEngine instance */\nlet globalSyncEngine: SyncEngine | null = null;\n\n/** Promise to track ongoing SyncEngine initialization */\nlet syncEngineInitPromise: Promise<SyncEngine> | null = null;\n\n/**\n * Get or create a singleton SyncEngine instance\n * \n * Ensures that only one SyncEngine is created and initialized per process,\n * preventing duplicate metadata refreshes that cause the double GetAllMetadata()\n * console output.\n * \n * @param contextUser - The user context for database operations\n * @returns Promise resolving to initialized SyncEngine instance\n * \n * @example\n * ```typescript\n * const syncEngine = await getSyncEngine(getSystemUser());\n * // Use syncEngine for operations\n * ```\n */\nexport async function getSyncEngine(contextUser: UserInfo): Promise<SyncEngine> {\n // Return existing engine if already initialized\n if (globalSyncEngine) {\n return globalSyncEngine;\n }\n \n // Return ongoing initialization if in progress\n if (syncEngineInitPromise) {\n return syncEngineInitPromise;\n }\n \n // Start new initialization\n syncEngineInitPromise = (async () => {\n globalSyncEngine = new SyncEngine(contextUser);\n await globalSyncEngine.initialize();\n return globalSyncEngine;\n })();\n \n return syncEngineInitPromise;\n}\n\n/**\n * Reset the singleton SyncEngine instance\n * \n * Should be called when cleaning up resources to ensure a fresh\n * instance is created on the next request.\n */\nexport function resetSyncEngine(): void {\n globalSyncEngine = null;\n syncEngineInitPromise = null;\n}"]}
1
+ {"version":3,"file":"singleton-manager.js","sourceRoot":"","sources":["../../src/lib/singleton-manager.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,+CAA2C;AAG3C,iCAAiC;AACjC,IAAI,gBAAgB,GAAsB,IAAI,CAAC;AAE/C,yDAAyD;AACzD,IAAI,qBAAqB,GAA+B,IAAI,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACI,KAAK,UAAU,aAAa,CAAC,WAAqB;IACvD,gDAAgD;IAChD,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,+CAA+C;IAC/C,IAAI,qBAAqB,EAAE,CAAC;QAC1B,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAED,2BAA2B;IAC3B,qBAAqB,GAAG,CAAC,KAAK,IAAI,EAAE;QAClC,gBAAgB,GAAG,IAAI,wBAAU,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,gBAAgB,CAAC,UAAU,EAAE,CAAC;QACpC,OAAO,gBAAgB,CAAC;IAC1B,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAnBD,sCAmBC;AAED;;;;;GAKG;AACH,SAAgB,eAAe;IAC7B,gBAAgB,GAAG,IAAI,CAAC;IACxB,qBAAqB,GAAG,IAAI,CAAC;AAC/B,CAAC;AAHD,0CAGC","sourcesContent":["/**\n * @fileoverview Singleton manager for shared instances across MetadataSync\n * @module singleton-manager\n *\n * This module ensures that expensive resources like SyncEngine are only\n * initialized once per process, preventing duplicate metadata refreshes\n * and improving performance.\n */\n\nimport { SyncEngine } from './sync-engine';\nimport { UserInfo } from '@memberjunction/global';\n\n/** Global SyncEngine instance */\nlet globalSyncEngine: SyncEngine | null = null;\n\n/** Promise to track ongoing SyncEngine initialization */\nlet syncEngineInitPromise: Promise<SyncEngine> | null = null;\n\n/**\n * Get or create a singleton SyncEngine instance\n *\n * Ensures that only one SyncEngine is created and initialized per process,\n * preventing duplicate metadata refreshes that cause the double GetAllMetadata()\n * console output.\n *\n * @param contextUser - The user context for database operations\n * @returns Promise resolving to initialized SyncEngine instance\n *\n * @example\n * ```typescript\n * const syncEngine = await getSyncEngine(getSystemUser());\n * // Use syncEngine for operations\n * ```\n */\nexport async function getSyncEngine(contextUser: UserInfo): Promise<SyncEngine> {\n // Return existing engine if already initialized\n if (globalSyncEngine) {\n return globalSyncEngine;\n }\n\n // Return ongoing initialization if in progress\n if (syncEngineInitPromise) {\n return syncEngineInitPromise;\n }\n\n // Start new initialization\n syncEngineInitPromise = (async () => {\n globalSyncEngine = new SyncEngine(contextUser);\n await globalSyncEngine.initialize();\n return globalSyncEngine;\n })();\n\n return syncEngineInitPromise;\n}\n\n/**\n * Reset the singleton SyncEngine instance\n *\n * Should be called when cleaning up resources to ensure a fresh\n * instance is created on the next request.\n */\nexport function resetSyncEngine(): void {\n globalSyncEngine = null;\n syncEngineInitPromise = null;\n}\n"]}
@@ -7,7 +7,7 @@
7
7
  * special reference types (@file, @url, @lookup, @env, @parent, @root, @template),
8
8
  * manages entity operations, and provides utilities for data transformation.
9
9
  */
10
- import { EntityInfo, BaseEntity, UserInfo } from '@memberjunction/core';
10
+ import { EntityInfo, BaseEntity, UserInfo } from '@memberjunction/global';
11
11
  import { EntityConfig } from '../config';
12
12
  /**
13
13
  * Represents the structure of a metadata record with optional sync tracking
@@ -17,7 +17,7 @@ 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 core_1 = require("@memberjunction/core");
20
+ const global_1 = require("@memberjunction/global");
21
21
  const json_preprocessor_1 = require("./json-preprocessor");
22
22
  /**
23
23
  * Core engine for synchronizing MemberJunction metadata between database and files
@@ -39,7 +39,7 @@ class SyncEngine {
39
39
  * @param contextUser - The user context for database operations
40
40
  */
41
41
  constructor(contextUser) {
42
- this.metadata = new core_1.Metadata();
42
+ this.metadata = new global_1.Metadata();
43
43
  this.contextUser = contextUser;
44
44
  }
45
45
  /**
@@ -109,7 +109,7 @@ class SyncEngine {
109
109
  // This handles cases like npm package names (@mui/material, @angular/core, etc.)
110
110
  if (value.startsWith('@')) {
111
111
  const knownPrefixes = ['@parent:', '@root:', '@file:', '@lookup:', '@env:', '@template:', '@include'];
112
- const isKnownReference = knownPrefixes.some(prefix => value.startsWith(prefix));
112
+ const isKnownReference = knownPrefixes.some((prefix) => value.startsWith(prefix));
113
113
  if (!isKnownReference) {
114
114
  return value; // Not a MetadataSync reference, just a string that happens to start with @
115
115
  }
@@ -299,7 +299,7 @@ class SyncEngine {
299
299
  }
300
300
  }
301
301
  // Not found in batch context, check database
302
- const rv = new core_1.RunView();
302
+ const rv = new global_1.RunView();
303
303
  const entityInfo = this.metadata.EntityByName(entityName);
304
304
  if (!entityInfo) {
305
305
  throw new Error(`Entity not found: ${entityName}`);
@@ -307,7 +307,7 @@ class SyncEngine {
307
307
  // Build compound filter for all lookup fields
308
308
  const filterParts = [];
309
309
  for (const { fieldName, fieldValue } of lookupFields) {
310
- const field = entityInfo.Fields.find(f => f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());
310
+ const field = entityInfo.Fields.find((f) => f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());
311
311
  if (!field) {
312
312
  throw new Error(`Field '${fieldName}' not found in entity '${entityName}'`);
313
313
  }
@@ -324,7 +324,7 @@ class SyncEngine {
324
324
  const result = await rv.RunView({
325
325
  EntityName: entityName,
326
326
  ExtraFilter: extraFilter,
327
- MaxRows: 1
327
+ MaxRows: 1,
328
328
  }, this.contextUser);
329
329
  if (result.Success && result.Results.length > 0) {
330
330
  if (entityInfo.PrimaryKeys.length > 0) {
@@ -368,7 +368,8 @@ class SyncEngine {
368
368
  if (message) {
369
369
  throw new Error(`Failed to auto-create ${entityName}: ${message}`);
370
370
  }
371
- const errors = newEntity.LatestResult?.Errors?.map(err => typeof err === 'string' ? err : (err?.message || JSON.stringify(err)))?.join(', ') || 'Unknown error';
371
+ const errors = newEntity.LatestResult?.Errors?.map((err) => (typeof err === 'string' ? err : err?.message || JSON.stringify(err)))?.join(', ') ||
372
+ 'Unknown error';
372
373
  throw new Error(`Failed to auto-create ${entityName}: ${errors}`);
373
374
  }
374
375
  // Return the new ID
@@ -495,7 +496,7 @@ class SyncEngine {
495
496
  return obj;
496
497
  }
497
498
  if (Array.isArray(obj)) {
498
- return Promise.all(obj.map(item => this.resolveFileReferencesForChecksum(item, entityDir)));
499
+ return Promise.all(obj.map((item) => this.resolveFileReferencesForChecksum(item, entityDir)));
499
500
  }
500
501
  const result = {};
501
502
  for (const [key, value] of Object.entries(obj)) {
@@ -536,7 +537,7 @@ class SyncEngine {
536
537
  result[key] = {
537
538
  _checksumType: 'file',
538
539
  _reference: value,
539
- _content: processedContent
540
+ _content: processedContent,
540
541
  };
541
542
  }
542
543
  else {
@@ -633,7 +634,7 @@ class SyncEngine {
633
634
  }
634
635
  // First, check if the record exists using RunView to avoid "Error in BaseEntity.Load" messages
635
636
  // when records don't exist (which is a normal scenario during sync operations)
636
- const rv = new core_1.RunView();
637
+ const rv = new global_1.RunView();
637
638
  // Build filter for primary key(s)
638
639
  const filters = [];
639
640
  for (const pk of entityInfo.PrimaryKeys) {
@@ -642,7 +643,7 @@ class SyncEngine {
642
643
  throw new Error(`Missing primary key value for ${pk.Name} in entity ${entityName}`);
643
644
  }
644
645
  // Check if field needs quotes
645
- const field = entityInfo.Fields.find(f => f.Name === pk.Name);
646
+ const field = entityInfo.Fields.find((f) => f.Name === pk.Name);
646
647
  const quotes = field?.NeedsQuotes ? "'" : '';
647
648
  const escapedValue = field?.NeedsQuotes && typeof value === 'string' ? value.replace(/'/g, "''") : value;
648
649
  filters.push(`${pk.Name} = ${quotes}${escapedValue}${quotes}`);
@@ -650,7 +651,7 @@ class SyncEngine {
650
651
  const result = await rv.RunView({
651
652
  EntityName: entityName,
652
653
  ExtraFilter: filters.join(' AND '),
653
- MaxRows: 1
654
+ MaxRows: 1,
654
655
  }, this.contextUser);
655
656
  // If no record found, return null without attempting to load
656
657
  if (!result.Success || result.Results.length === 0) {
@@ -658,7 +659,7 @@ class SyncEngine {
658
659
  }
659
660
  // Record exists, now load it properly through the entity
660
661
  const entity = await this.createEntityObject(entityName);
661
- const compositeKey = new core_1.CompositeKey();
662
+ const compositeKey = new global_1.CompositeKey();
662
663
  compositeKey.LoadFromSimpleObject(primaryKey);
663
664
  const loaded = await entity.InnerLoad(compositeKey);
664
665
  return loaded ? entity : null;
@@ -710,7 +711,7 @@ class SyncEngine {
710
711
  const resolvedPath = path_1.default.resolve(currentDir, trimmedPath);
711
712
  try {
712
713
  // Check if the included file exists
713
- if (!await fs_extra_1.default.pathExists(resolvedPath)) {
714
+ if (!(await fs_extra_1.default.pathExists(resolvedPath))) {
714
715
  throw new Error(`Included file not found: ${resolvedPath}`);
715
716
  }
716
717
  // Read the included file
@@ -751,7 +752,7 @@ class SyncEngine {
751
752
  }
752
753
  // Handle arrays
753
754
  if (Array.isArray(obj)) {
754
- return Promise.all(obj.map(item => this.processJsonFieldValues(item, baseDir, parentRecord, rootRecord, depth, batchContext)));
755
+ return Promise.all(obj.map((item) => this.processJsonFieldValues(item, baseDir, parentRecord, rootRecord, depth, batchContext)));
755
756
  }
756
757
  // Handle objects
757
758
  if (typeof obj === 'object') {
@@ -761,9 +762,12 @@ class SyncEngine {
761
762
  if (typeof value === 'string') {
762
763
  // Check if this looks like a reference that needs processing
763
764
  // Only process known reference types, ignore other @ strings (like npm packages)
764
- if (value.startsWith('@file:') || value.startsWith('@lookup:') ||
765
- value.startsWith('@parent:') || value.startsWith('@root:') ||
766
- value.startsWith('@env:') || value.startsWith('@template:') ||
765
+ if (value.startsWith('@file:') ||
766
+ value.startsWith('@lookup:') ||
767
+ value.startsWith('@parent:') ||
768
+ value.startsWith('@root:') ||
769
+ value.startsWith('@env:') ||
770
+ value.startsWith('@template:') ||
767
771
  value.startsWith('@include')) {
768
772
  result[key] = await this.processFieldValue(value, baseDir, parentRecord, rootRecord, depth, batchContext);
769
773
  }
@@ -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';\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 // 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(\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('@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 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('@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 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 (value.startsWith('@file:') || value.startsWith('@lookup:') || \n value.startsWith('@parent:') || value.startsWith('@root:') ||\n value.startsWith('@env:') || value.startsWith('@template:') ||\n value.startsWith('@include')) {\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/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,4 +1,4 @@
1
- import { UserInfo } from '@memberjunction/core';
1
+ import { UserInfo } from '@memberjunction/global';
2
2
  import { SyncEngine } from '../lib/sync-engine';
3
3
  export interface PullOptions {
4
4
  entity: string;
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PullService = void 0;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const path_1 = __importDefault(require("path"));
9
- const core_1 = require("@memberjunction/core");
9
+ const global_1 = require("@memberjunction/global");
10
10
  const config_1 = require("../config");
11
11
  const config_manager_1 = require("../lib/config-manager");
12
12
  const file_write_batch_1 = require("../lib/file-write-batch");
@@ -68,16 +68,14 @@ class PullService {
68
68
  }
69
69
  // Show configuration notice only if relevant and in verbose mode
70
70
  if (options.verbose && entityConfig?.pull?.appendRecordsToExistingFile && entityConfig?.pull?.newFileName) {
71
- const targetFile = path_1.default.join(targetDir, entityConfig.pull.newFileName.endsWith('.json')
72
- ? entityConfig.pull.newFileName
73
- : `${entityConfig.pull.newFileName}.json`);
71
+ const targetFile = path_1.default.join(targetDir, entityConfig.pull.newFileName.endsWith('.json') ? entityConfig.pull.newFileName : `${entityConfig.pull.newFileName}.json`);
74
72
  if (await fs_extra_1.default.pathExists(targetFile)) {
75
73
  callbacks?.onLog?.(`\nšŸ“ Configuration: New records will be appended to existing file '${path_1.default.basename(targetFile)}'`);
76
74
  }
77
75
  }
78
76
  // Pull records
79
77
  callbacks?.onProgress?.(`Pulling ${options.entity} records`);
80
- const rv = new core_1.RunView();
78
+ const rv = new global_1.RunView();
81
79
  let filter = '';
82
80
  if (options.filter) {
83
81
  filter = options.filter;
@@ -88,7 +86,7 @@ class PullService {
88
86
  const result = await rv.RunView({
89
87
  EntityName: options.entity,
90
88
  ExtraFilter: filter,
91
- ResultType: 'entity_object'
89
+ ResultType: 'entity_object',
92
90
  }, this.contextUser);
93
91
  if (!result.Success) {
94
92
  throw new Error(`Failed to pull records: ${result.ErrorMessage}`);
@@ -101,7 +99,7 @@ class PullService {
101
99
  created: 0,
102
100
  updated: 0,
103
101
  skipped: 0,
104
- targetDir
102
+ targetDir,
105
103
  };
106
104
  }
107
105
  // Process records with error handling and rollback
@@ -136,7 +134,7 @@ class PullService {
136
134
  }
137
135
  return {
138
136
  ...pullResult,
139
- targetDir
137
+ targetDir,
140
138
  };
141
139
  }
142
140
  async processRecords(records, options, targetDir, entityConfig, callbacks) {
@@ -325,7 +323,7 @@ class PullService {
325
323
  const existingFiles = await this.findExistingFiles(targetDir, filePattern);
326
324
  if (options.verbose) {
327
325
  callbacks?.onLog?.(`Found ${existingFiles.length} existing files matching pattern '${filePattern}'`);
328
- existingFiles.forEach(f => callbacks?.onLog?.(` - ${path_1.default.basename(f)}`));
326
+ existingFiles.forEach((f) => callbacks?.onLog?.(` - ${path_1.default.basename(f)}`));
329
327
  }
330
328
  // Load existing records and build lookup map
331
329
  const existingRecordsMap = await this.loadExistingRecords(existingFiles, entityInfo);
@@ -388,7 +386,7 @@ class PullService {
388
386
  let existingRecordData;
389
387
  if (Array.isArray(existingData)) {
390
388
  // Find the matching record in the array
391
- const matchingRecord = existingData.find(r => this.createPrimaryKeyLookup(r.primaryKey || {}) === this.createPrimaryKeyLookup(primaryKey));
389
+ const matchingRecord = existingData.find((r) => this.createPrimaryKeyLookup(r.primaryKey || {}) === this.createPrimaryKeyLookup(primaryKey));
392
390
  existingRecordData = matchingRecord || existingData[0]; // Fallback to first if not found
393
391
  }
394
392
  else {
@@ -421,7 +419,7 @@ class PullService {
421
419
  }
422
420
  });
423
421
  const updateResults = await Promise.all(updatePromises);
424
- updated = updateResults.filter(r => r.success).length;
422
+ updated = updateResults.filter((r) => r.success).length;
425
423
  processed += updated;
426
424
  if (options.verbose) {
427
425
  callbacks?.onSuccess?.(`Completed ${updated}/${existingRecordsToUpdate.length} record updates`);
@@ -455,7 +453,7 @@ class PullService {
455
453
  }
456
454
  });
457
455
  const newRecordResults = await Promise.all(newRecordPromises);
458
- created = newRecordResults.filter(r => r.success).length;
456
+ created = newRecordResults.filter((r) => r.success).length;
459
457
  processed += created;
460
458
  if (options.verbose) {
461
459
  callbacks?.onLog?.(`Queued ${created} new records for: ${filePath}`);
@@ -476,7 +474,7 @@ class PullService {
476
474
  }
477
475
  });
478
476
  const individualResults = await Promise.all(individualRecordPromises);
479
- created = individualResults.filter(r => r.success).length;
477
+ created = individualResults.filter((r) => r.success).length;
480
478
  processed += created;
481
479
  if (options.verbose) {
482
480
  callbacks?.onSuccess?.(`Created ${created}/${newRecords.length} individual record files`);
@@ -535,7 +533,13 @@ class PullService {
535
533
  }
536
534
  }
537
535
  // Multiple keys or numeric - create composite name, prefixed with dot
538
- return '.' + keys.map(k => String(k).replace(/[^a-zA-Z0-9\-_]/g, '').toLowerCase()).join('-') + '.json';
536
+ return ('.' +
537
+ keys
538
+ .map((k) => String(k)
539
+ .replace(/[^a-zA-Z0-9\-_]/g, '')
540
+ .toLowerCase())
541
+ .join('-') +
542
+ '.json');
539
543
  }
540
544
  async findExistingFiles(dir, pattern) {
541
545
  const files = [];
@@ -586,7 +590,7 @@ class PullService {
586
590
  }
587
591
  createPrimaryKeyLookup(primaryKey) {
588
592
  const keys = Object.keys(primaryKey).sort();
589
- return keys.map(k => `${k}:${primaryKey[k]}`).join('|');
593
+ return keys.map((k) => `${k}:${primaryKey[k]}`).join('|');
590
594
  }
591
595
  async mergeRecords(existing, newData, strategy, preserveFields) {
592
596
  if (strategy === 'skip') {
@@ -596,7 +600,7 @@ class PullService {
596
600
  const result = {
597
601
  fields: { ...newData.fields },
598
602
  primaryKey: newData.primaryKey,
599
- sync: newData.sync
603
+ sync: newData.sync,
600
604
  };
601
605
  // Restore preserved fields from existing
602
606
  if (preserveFields.length > 0 && existing.fields) {
@@ -615,7 +619,7 @@ class PullService {
615
619
  const result = {
616
620
  fields: { ...existing.fields, ...newData.fields },
617
621
  primaryKey: newData.primaryKey || existing.primaryKey,
618
- sync: newData.sync
622
+ sync: newData.sync,
619
623
  };
620
624
  // Restore preserved fields
621
625
  if (preserveFields.length > 0 && existing.fields) {
@@ -628,7 +632,7 @@ class PullService {
628
632
  if (existing.relatedEntities || newData.relatedEntities) {
629
633
  result.relatedEntities = {
630
634
  ...existing.relatedEntities,
631
- ...newData.relatedEntities
635
+ ...newData.relatedEntities,
632
636
  };
633
637
  }
634
638
  return result;