@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.
- package/README.md +597 -0
- package/dist/commands/init/index.js +25 -0
- package/dist/commands/init/index.js.map +1 -1
- package/dist/commands/pull/index.d.ts +7 -0
- package/dist/commands/pull/index.js +87 -5
- package/dist/commands/pull/index.js.map +1 -1
- package/dist/commands/push/index.js +165 -4
- package/dist/commands/push/index.js.map +1 -1
- package/dist/commands/status/index.js +31 -0
- package/dist/commands/status/index.js.map +1 -1
- package/dist/commands/watch/index.js +40 -1
- package/dist/commands/watch/index.js.map +1 -1
- package/dist/config.d.ts +27 -0
- package/dist/config.js.map +1 -1
- package/dist/lib/provider-utils.d.ts +26 -5
- package/dist/lib/provider-utils.js +95 -30
- package/dist/lib/provider-utils.js.map +1 -1
- package/dist/lib/sync-engine.js +16 -1
- package/dist/lib/sync-engine.js.map +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +8 -7
|
@@ -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:
|
|
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}`;
|