@memberjunction/metadata-sync 2.48.0 → 2.50.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/init/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { Command } from '@oclif/core';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { input, select } from '@inquirer/prompts';\nimport ora from 'ora-classic';\n\nexport default class Init extends Command {\n static description = 'Initialize a directory for metadata synchronization';\n \n static examples = [\n `<%= config.bin %> <%= command.id %>`,\n ];\n \n async run(): Promise<void> {\n const spinner = ora();\n \n try {\n // Check if already initialized\n if (await fs.pathExists('.mj-sync.json')) {\n const overwrite = await select({\n message: 'Directory already initialized. Overwrite configuration?',\n choices: [\n { name: 'Yes', value: true },\n { name: 'No', value: false }\n ]\n });\n \n if (!overwrite) {\n this.log('Initialization cancelled');\n return;\n }\n }\n \n // Create root configuration\n const rootConfig = {\n version: '1.0.0',\n push: {\n validateBeforePush: true,\n requireConfirmation: true\n },\n watch: {\n debounceMs: 1000,\n ignorePatterns: ['*.tmp', '*.bak', '.DS_Store']\n }\n };\n \n spinner.start('Creating root configuration');\n await fs.writeJson('.mj-sync.json', rootConfig, { spaces: 2 });\n spinner.succeed('Created .mj-sync.json');\n \n // Ask if they want to set up an entity directory\n const setupEntity = await select({\n message: 'Would you like to set up an entity directory now?',\n choices: [\n { name: 'Yes - AI Prompts', value: 'ai-prompts' },\n { name: 'Yes - Other entity', value: 'other' },\n { name: 'No - I\\'ll set up later', value: 'no' }\n ]\n });\n \n if (setupEntity !== 'no') {\n const entityName = setupEntity === 'ai-prompts' \n ? 'AI Prompts'\n : await input({\n message: 'Enter the entity name (e.g., \"Templates\", \"AI Models\"):',\n });\n \n const dirName = setupEntity === 'ai-prompts'\n ? 'ai-prompts'\n : await input({\n message: 'Enter the directory name:',\n default: entityName.toLowerCase().replace(/\\s+/g, '-')\n });\n \n // Create entity directory\n spinner.start(`Creating ${dirName} directory`);\n await fs.ensureDir(dirName);\n \n // Create entity configuration\n const entityConfig = {\n entity: entityName,\n filePattern: '*.json',\n defaults: {}\n };\n \n await fs.writeJson(path.join(dirName, '.mj-sync.json'), entityConfig, { spaces: 2 });\n spinner.succeed(`Created ${dirName} directory with entity configuration`);\n \n // Create example structure\n if (setupEntity === 'ai-prompts') {\n await this.createAIPromptsExample(dirName);\n }\n }\n \n this.log('\\n✅ Initialization complete!');\n this.log('\\nNext steps:');\n this.log('1. Run \"mj-sync pull --entity=\\'AI Prompts\\'\" to pull existing data');\n this.log('2. Edit files locally');\n this.log('3. Run \"mj-sync push\" to sync changes back to the database');\n \n } catch (error) {\n spinner.fail('Initialization failed');\n this.error(error as Error);\n }\n }\n \n private async createAIPromptsExample(dirName: string): Promise<void> {\n const exampleDir = path.join(dirName, 'examples');\n await fs.ensureDir(exampleDir);\n \n // Create folder config\n const folderConfig = {\n defaults: {\n CategoryID: '@lookup:AI Prompt Categories.Name=Examples',\n Temperature: 0.7\n }\n };\n \n await fs.writeJson(path.join(exampleDir, '.mj-folder.json'), folderConfig, { spaces: 2 });\n \n // Create example prompt\n const examplePrompt = {\n _primaryKey: {\n ID: 'example-001'\n },\n _fields: {\n Name: 'Example Greeting Prompt',\n Description: 'A simple example prompt to demonstrate the sync tool',\n PromptTypeID: '@lookup:AI Prompt Types.Name=Chat',\n Temperature: 0.8,\n MaxTokens: 150,\n Prompt: '@file:greeting.prompt.md'\n }\n };\n \n await fs.writeJson(\n path.join(exampleDir, 'greeting.json'), \n examplePrompt, \n { spaces: 2 }\n );\n \n // Create the markdown file\n const promptContent = `You are a friendly assistant. Please greet the user warmly and ask how you can help them today.\n\nBe conversational and welcoming in your tone.`;\n \n await fs.writeFile(\n path.join(exampleDir, 'greeting.prompt.md'),\n promptContent\n );\n }\n}"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/init/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { Command } from '@oclif/core';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { input, select } from '@inquirer/prompts';\nimport ora from 'ora-classic';\n\nexport default class Init extends Command {\n static description = 'Initialize a directory for metadata synchronization';\n \n static examples = [\n `<%= config.bin %> <%= command.id %>`,\n ];\n \n async run(): Promise<void> {\n const spinner = ora();\n \n try {\n // Check if already initialized\n if (await fs.pathExists('.mj-sync.json')) {\n const overwrite = await select({\n message: 'Directory already initialized. Overwrite configuration?',\n choices: [\n { name: 'Yes', value: true },\n { name: 'No', value: false }\n ]\n });\n \n if (!overwrite) {\n this.log('Initialization cancelled');\n return;\n }\n }\n \n // Create root configuration\n const rootConfig = {\n version: '1.0.0',\n push: {\n validateBeforePush: true,\n requireConfirmation: true\n },\n watch: {\n debounceMs: 1000,\n ignorePatterns: ['*.tmp', '*.bak', '.DS_Store']\n }\n };\n \n spinner.start('Creating root configuration');\n await fs.writeJson('.mj-sync.json', rootConfig, { spaces: 2 });\n spinner.succeed('Created .mj-sync.json');\n \n // Ask if they want to set up an entity directory\n const setupEntity = await select({\n message: 'Would you like to set up an entity directory now?',\n choices: [\n { name: 'Yes - AI Prompts', value: 'ai-prompts' },\n { name: 'Yes - Other entity', value: 'other' },\n { name: 'No - I\\'ll set up later', value: 'no' }\n ]\n });\n \n if (setupEntity !== 'no') {\n const entityName = setupEntity === 'ai-prompts' \n ? 'AI Prompts'\n : await input({\n message: 'Enter the entity name (e.g., \"Templates\", \"AI Models\"):',\n });\n \n const dirName = setupEntity === 'ai-prompts'\n ? 'ai-prompts'\n : await input({\n message: 'Enter the directory name:',\n default: entityName.toLowerCase().replace(/\\s+/g, '-')\n });\n \n // Create entity directory\n spinner.start(`Creating ${dirName} directory`);\n await fs.ensureDir(dirName);\n \n // Create entity configuration\n const entityConfig = {\n entity: entityName,\n filePattern: '*.json',\n defaults: {}\n };\n \n await fs.writeJson(path.join(dirName, '.mj-sync.json'), entityConfig, { spaces: 2 });\n spinner.succeed(`Created ${dirName} directory with entity configuration`);\n \n // Create example structure\n if (setupEntity === 'ai-prompts') {\n await this.createAIPromptsExample(dirName);\n }\n }\n \n this.log('\\n✅ Initialization complete!');\n this.log('\\nNext steps:');\n this.log('1. Run \"mj-sync pull --entity=\\'AI Prompts\\'\" to pull existing data');\n this.log('2. Edit files locally');\n this.log('3. Run \"mj-sync push\" to sync changes back to the database');\n \n } catch (error) {\n spinner.fail('Initialization failed');\n \n // Enhanced error logging for debugging\n this.log('\\n=== Initialization Error Details ===');\n this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);\n this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);\n \n if (error instanceof Error && error.stack) {\n this.log(`\\nStack trace:`);\n this.log(error.stack);\n }\n \n // Log context information\n this.log(`\\nContext:`);\n this.log(`- Working directory: ${process.cwd()}`);\n \n // Check if error is related to common issues\n const errorMessage = error instanceof Error ? error.message : String(error);\n if (errorMessage.includes('permission') || errorMessage.includes('EACCES')) {\n this.log(`\\nHint: This appears to be a file permission issue.`);\n this.log(`Make sure you have write permissions in the current directory.`);\n } else if (errorMessage.includes('ENOENT') || errorMessage.includes('no such file')) {\n this.log(`\\nHint: This appears to be a file or directory access issue.`);\n this.log(`Make sure the current directory exists and is accessible.`);\n } else if (errorMessage.includes('already exists') || errorMessage.includes('EEXIST')) {\n this.log(`\\nHint: Files or directories already exist.`);\n this.log(`Try using the overwrite option or manually remove existing files.`);\n }\n \n this.error(error as Error);\n }\n }\n \n private async createAIPromptsExample(dirName: string): Promise<void> {\n const exampleDir = path.join(dirName, 'examples');\n await fs.ensureDir(exampleDir);\n \n // Create folder config\n const folderConfig = {\n defaults: {\n CategoryID: '@lookup:AI Prompt Categories.Name=Examples',\n Temperature: 0.7\n }\n };\n \n await fs.writeJson(path.join(exampleDir, '.mj-folder.json'), folderConfig, { spaces: 2 });\n \n // Create example prompt\n const examplePrompt = {\n _primaryKey: {\n ID: 'example-001'\n },\n _fields: {\n Name: 'Example Greeting Prompt',\n Description: 'A simple example prompt to demonstrate the sync tool',\n PromptTypeID: '@lookup:AI Prompt Types.Name=Chat',\n Temperature: 0.8,\n MaxTokens: 150,\n Prompt: '@file:greeting.prompt.md'\n }\n };\n \n await fs.writeJson(\n path.join(exampleDir, 'greeting.json'), \n examplePrompt, \n { spaces: 2 }\n );\n \n // Create the markdown file\n const promptContent = `You are a friendly assistant. Please greet the user warmly and ask how you can help them today.\n\nBe conversational and welcoming in your tone.`;\n \n await fs.writeFile(\n path.join(exampleDir, 'greeting.prompt.md'),\n promptContent\n );\n }\n}"]}
@@ -81,6 +81,8 @@ export default class Pull extends Command {
81
81
  * @param flags - Command flags
82
82
  * @param isNewRecord - Whether this is a new record
83
83
  * @param existingRecordData - Existing record data to preserve field selection
84
+ * @param currentDepth - Current recursion depth for recursive entities
85
+ * @param ancestryPath - Set of IDs in current ancestry chain to prevent circular references
84
86
  * @returns Promise resolving to formatted RecordData
85
87
  * @private
86
88
  */
@@ -150,10 +152,15 @@ export default class Pull extends Command {
150
152
  * Retrieves child records that have foreign key relationships to the parent.
151
153
  * Converts foreign key values to @parent references and supports nested
152
154
  * related entities for deep object graphs.
155
+ * NEW: Supports automatic recursive patterns for self-referencing entities.
153
156
  *
154
157
  * @param parentRecord - Parent entity record
155
158
  * @param relatedConfig - Configuration for related entities to pull
156
159
  * @param syncEngine - Sync engine instance
160
+ * @param entityConfig - Entity configuration
161
+ * @param flags - Command flags
162
+ * @param currentDepth - Current recursion depth for recursive entities
163
+ * @param ancestryPath - Set of IDs in current ancestry chain to prevent circular references
157
164
  * @returns Promise resolving to map of entity names to related records
158
165
  * @private
159
166
  */
@@ -389,6 +389,38 @@ class Pull extends core_1.Command {
389
389
  }
390
390
  catch (error) {
391
391
  spinner.fail('Pull failed');
392
+ // Enhanced error logging for debugging
393
+ this.log('\n=== Pull Error Details ===');
394
+ this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
395
+ this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
396
+ if (error instanceof Error && error.stack) {
397
+ this.log(`\nStack trace:`);
398
+ this.log(error.stack);
399
+ }
400
+ // Log context information
401
+ this.log(`\nContext:`);
402
+ this.log(`- Working directory: ${config_manager_1.configManager.getOriginalCwd()}`);
403
+ this.log(`- Entity: ${flags.entity || 'not specified'}`);
404
+ this.log(`- Filter: ${flags.filter || 'none'}`);
405
+ this.log(`- Flags: ${JSON.stringify(flags, null, 2)}`);
406
+ // Check if error is related to common issues
407
+ const errorMessage = error instanceof Error ? error.message : String(error);
408
+ if (errorMessage.includes('No directory found for entity')) {
409
+ this.log(`\nHint: This appears to be an entity directory configuration issue.`);
410
+ this.log(`Run "mj-sync init" to create directories or ensure .mj-sync.json files exist.`);
411
+ }
412
+ else if (errorMessage.includes('database') || errorMessage.includes('connection')) {
413
+ this.log(`\nHint: This appears to be a database connectivity issue.`);
414
+ this.log(`Check your mj.config.cjs configuration and database connectivity.`);
415
+ }
416
+ else if (errorMessage.includes('Failed to pull records')) {
417
+ this.log(`\nHint: This appears to be a database query issue.`);
418
+ this.log(`Check if the entity name "${flags.entity}" is correct and exists in the database.`);
419
+ }
420
+ else if (errorMessage.includes('Entity information not found')) {
421
+ this.log(`\nHint: The entity "${flags.entity}" was not found in metadata.`);
422
+ this.log(`Check the entity name spelling and ensure it exists in the database.`);
423
+ }
392
424
  this.error(error);
393
425
  }
394
426
  finally {
@@ -468,10 +500,12 @@ class Pull extends core_1.Command {
468
500
  * @param flags - Command flags
469
501
  * @param isNewRecord - Whether this is a new record
470
502
  * @param existingRecordData - Existing record data to preserve field selection
503
+ * @param currentDepth - Current recursion depth for recursive entities
504
+ * @param ancestryPath - Set of IDs in current ancestry chain to prevent circular references
471
505
  * @returns Promise resolving to formatted RecordData
472
506
  * @private
473
507
  */
474
- async processRecordData(record, primaryKey, targetDir, entityConfig, syncEngine, flags, isNewRecord = true, existingRecordData) {
508
+ async processRecordData(record, primaryKey, targetDir, entityConfig, syncEngine, flags, isNewRecord = true, existingRecordData, currentDepth = 0, ancestryPath = new Set()) {
475
509
  // Build record data - we'll restructure at the end for proper ordering
476
510
  const fields = {};
477
511
  const relatedEntities = {};
@@ -664,7 +698,7 @@ class Pull extends core_1.Command {
664
698
  }
665
699
  // Pull related entities if configured
666
700
  if (entityConfig.pull?.relatedEntities) {
667
- const related = await this.pullRelatedEntities(record, entityConfig.pull.relatedEntities, syncEngine, entityConfig, flags);
701
+ const related = await this.pullRelatedEntities(record, entityConfig.pull.relatedEntities, syncEngine, entityConfig, flags, currentDepth, ancestryPath);
668
702
  Object.assign(relatedEntities, related);
669
703
  }
670
704
  // Get entity metadata to check defaults
@@ -972,14 +1006,19 @@ class Pull extends core_1.Command {
972
1006
  * Retrieves child records that have foreign key relationships to the parent.
973
1007
  * Converts foreign key values to @parent references and supports nested
974
1008
  * related entities for deep object graphs.
1009
+ * NEW: Supports automatic recursive patterns for self-referencing entities.
975
1010
  *
976
1011
  * @param parentRecord - Parent entity record
977
1012
  * @param relatedConfig - Configuration for related entities to pull
978
1013
  * @param syncEngine - Sync engine instance
1014
+ * @param entityConfig - Entity configuration
1015
+ * @param flags - Command flags
1016
+ * @param currentDepth - Current recursion depth for recursive entities
1017
+ * @param ancestryPath - Set of IDs in current ancestry chain to prevent circular references
979
1018
  * @returns Promise resolving to map of entity names to related records
980
1019
  * @private
981
1020
  */
982
- async pullRelatedEntities(parentRecord, relatedConfig, syncEngine, entityConfig, flags) {
1021
+ async pullRelatedEntities(parentRecord, relatedConfig, syncEngine, entityConfig, flags, currentDepth = 0, ancestryPath = new Set()) {
983
1022
  const relatedEntities = {};
984
1023
  for (const [key, config] of Object.entries(relatedConfig)) {
985
1024
  try {
@@ -1049,12 +1088,53 @@ class Pull extends core_1.Command {
1049
1088
  }
1050
1089
  // Process each related record
1051
1090
  const relatedRecords = [];
1091
+ if (flags?.verbose && result.Results.length > 0) {
1092
+ this.log(`Found ${result.Results.length} related ${config.entity} records at depth ${currentDepth}`);
1093
+ }
1052
1094
  for (const relatedRecord of result.Results) {
1053
1095
  // Build primary key for the related record
1054
1096
  const relatedPrimaryKey = {};
1055
1097
  for (const pk of childEntity.PrimaryKeys) {
1056
1098
  relatedPrimaryKey[pk.Name] = relatedRecord[pk.Name];
1057
1099
  }
1100
+ // Check for circular references in the current ancestry path
1101
+ const recordId = String(relatedPrimaryKey[childEntity.PrimaryKeys[0]?.Name || 'ID']);
1102
+ if (config.recursive && ancestryPath.has(recordId)) {
1103
+ if (flags?.verbose) {
1104
+ this.log(`Skipping circular reference for ${config.entity} with ID: ${recordId} (detected in ancestry path)`);
1105
+ }
1106
+ continue;
1107
+ }
1108
+ // Create new ancestry path for this branch (only track current hierarchy chain)
1109
+ const newAncestryPath = new Set(ancestryPath);
1110
+ if (config.recursive) {
1111
+ newAncestryPath.add(recordId);
1112
+ }
1113
+ // Determine related entities configuration for recursion
1114
+ let childRelatedConfig = config.relatedEntities;
1115
+ // If recursive is enabled, continue recursive fetching at child level
1116
+ if (config.recursive) {
1117
+ const maxDepth = config.maxDepth || 10;
1118
+ if (currentDepth < maxDepth) {
1119
+ // Create recursive configuration that references the same entity
1120
+ childRelatedConfig = {
1121
+ [key]: {
1122
+ ...config,
1123
+ // Keep same configuration but increment depth internally
1124
+ }
1125
+ };
1126
+ if (flags?.verbose) {
1127
+ this.log(`Processing recursive level ${currentDepth + 1} for ${config.entity} record ${recordId}`);
1128
+ }
1129
+ }
1130
+ else {
1131
+ // At max depth, don't recurse further
1132
+ childRelatedConfig = undefined;
1133
+ if (flags?.verbose) {
1134
+ this.log(`Max depth ${maxDepth} reached for recursive entity ${config.entity} at record ${recordId}`);
1135
+ }
1136
+ }
1137
+ }
1058
1138
  // Process the related record using the same logic as parent records
1059
1139
  const relatedData = await this.processRecordData(relatedRecord, relatedPrimaryKey, '', // Not used for related entities since we don't externalize their fields
1060
1140
  {
@@ -1063,9 +1143,11 @@ class Pull extends core_1.Command {
1063
1143
  excludeFields: config.excludeFields || entityConfig.pull?.excludeFields,
1064
1144
  lookupFields: config.lookupFields || entityConfig.pull?.lookupFields,
1065
1145
  externalizeFields: config.externalizeFields,
1066
- relatedEntities: config.relatedEntities
1146
+ relatedEntities: childRelatedConfig
1067
1147
  }
1068
- }, syncEngine, flags, true);
1148
+ }, syncEngine, flags, true, // isNewRecord
1149
+ undefined, // existingRecordData
1150
+ currentDepth + 1, newAncestryPath);
1069
1151
  // Convert foreign key reference to @parent
1070
1152
  if (relatedData.fields[config.foreignKey]) {
1071
1153
  relatedData.fields[config.foreignKey] = `@parent:${primaryKeyField}`;