@memberjunction/metadata-sync 2.47.0 → 2.49.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.
@@ -90,6 +90,31 @@ class Init extends core_1.Command {
90
90
  }
91
91
  catch (error) {
92
92
  spinner.fail('Initialization failed');
93
+ // Enhanced error logging for debugging
94
+ this.log('\n=== Initialization Error Details ===');
95
+ this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
96
+ this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
97
+ if (error instanceof Error && error.stack) {
98
+ this.log(`\nStack trace:`);
99
+ this.log(error.stack);
100
+ }
101
+ // Log context information
102
+ this.log(`\nContext:`);
103
+ this.log(`- Working directory: ${process.cwd()}`);
104
+ // Check if error is related to common issues
105
+ const errorMessage = error instanceof Error ? error.message : String(error);
106
+ if (errorMessage.includes('permission') || errorMessage.includes('EACCES')) {
107
+ this.log(`\nHint: This appears to be a file permission issue.`);
108
+ this.log(`Make sure you have write permissions in the current directory.`);
109
+ }
110
+ else if (errorMessage.includes('ENOENT') || errorMessage.includes('no such file')) {
111
+ this.log(`\nHint: This appears to be a file or directory access issue.`);
112
+ this.log(`Make sure the current directory exists and is accessible.`);
113
+ }
114
+ else if (errorMessage.includes('already exists') || errorMessage.includes('EEXIST')) {
115
+ this.log(`\nHint: Files or directories already exist.`);
116
+ this.log(`Try using the overwrite option or manually remove existing files.`);
117
+ }
93
118
  this.error(error);
94
119
  }
95
120
  }
@@ -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}"]}
@@ -37,6 +37,7 @@ export default class Pull extends Command {
37
37
  filter: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
38
38
  'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
39
39
  'multi-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
40
+ verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
40
41
  };
41
42
  run(): Promise<void>;
42
43
  /**
@@ -77,20 +78,38 @@ export default class Pull extends Command {
77
78
  * @param targetDir - Directory where files will be saved
78
79
  * @param entityConfig - Entity configuration with defaults and settings
79
80
  * @param syncEngine - Sync engine for checksum calculation
81
+ * @param flags - Command flags
82
+ * @param isNewRecord - Whether this is a new record
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
80
86
  * @returns Promise resolving to formatted RecordData
81
87
  * @private
82
88
  */
83
89
  private processRecordData;
90
+ /**
91
+ * Convert a foreign key value to a @lookup reference
92
+ *
93
+ * Looks up the related record and creates a @lookup string that can be
94
+ * resolved during push operations.
95
+ *
96
+ * @param foreignKeyValue - The foreign key value (ID)
97
+ * @param targetEntity - Name of the target entity
98
+ * @param targetField - Field in target entity to use for lookup
99
+ * @param syncEngine - Sync engine instance
100
+ * @returns @lookup string or null if lookup fails
101
+ * @private
102
+ */
103
+ private convertToLookup;
84
104
  /**
85
105
  * Determine if a field should be saved to an external file
86
106
  *
87
- * Checks if a field contains substantial text content that would be better
88
- * stored in a separate file rather than inline in the JSON. Uses heuristics
89
- * based on field name and content length.
107
+ * Checks if a field is configured for externalization or contains substantial
108
+ * text content that would be better stored in a separate file.
90
109
  *
91
110
  * @param fieldName - Name of the field to check
92
111
  * @param fieldValue - Value of the field
93
- * @param entityConfig - Entity configuration (for future extension)
112
+ * @param entityConfig - Entity configuration with externalization settings
94
113
  * @returns Promise resolving to true if field should be externalized
95
114
  * @private
96
115
  */
@@ -101,11 +120,14 @@ export default class Pull extends Command {
101
120
  * Saves large text content to a separate file and returns the filename.
102
121
  * Automatically determines appropriate file extension based on field name
103
122
  * and content type (e.g., .md for prompts, .html for templates).
123
+ * Uses the entity's name field for the filename if available.
104
124
  *
105
125
  * @param targetDir - Directory to save the file
106
- * @param primaryKey - Primary key for filename generation
126
+ * @param record - Full record to extract name field from
127
+ * @param primaryKey - Primary key for filename generation fallback
107
128
  * @param fieldName - Name of the field being externalized
108
129
  * @param content - Content to write to the file
130
+ * @param entityConfig - Entity configuration
109
131
  * @returns Promise resolving to the created filename
110
132
  * @private
111
133
  */
@@ -116,6 +138,7 @@ export default class Pull extends Command {
116
138
  * Creates a safe filename based on the entity's primary key values.
117
139
  * Handles GUIDs by using first 8 characters, sanitizes special characters,
118
140
  * and creates composite names for multi-field keys.
141
+ * Files are prefixed with a dot to follow the metadata file convention.
119
142
  *
120
143
  * @param primaryKey - Primary key fields and values
121
144
  * @param entityConfig - Entity configuration (for future extension)
@@ -129,10 +152,15 @@ export default class Pull extends Command {
129
152
  * Retrieves child records that have foreign key relationships to the parent.
130
153
  * Converts foreign key values to @parent references and supports nested
131
154
  * related entities for deep object graphs.
155
+ * NEW: Supports automatic recursive patterns for self-referencing entities.
132
156
  *
133
157
  * @param parentRecord - Parent entity record
134
158
  * @param relatedConfig - Configuration for related entities to pull
135
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
136
164
  * @returns Promise resolving to map of entity names to related records
137
165
  * @private
138
166
  */
@@ -150,4 +178,68 @@ export default class Pull extends Command {
150
178
  * @private
151
179
  */
152
180
  private findParentField;
181
+ /**
182
+ * Find existing files in a directory matching a pattern
183
+ *
184
+ * Searches for files that match the configured file pattern, used to identify
185
+ * which records already exist locally for smart update functionality.
186
+ *
187
+ * @param dir - Directory to search in
188
+ * @param pattern - Glob pattern to match files (e.g., "*.json")
189
+ * @returns Promise resolving to array of file paths
190
+ * @private
191
+ */
192
+ private findExistingFiles;
193
+ /**
194
+ * Load existing records from files and build a lookup map
195
+ *
196
+ * Reads all existing files and creates a map from primary key to file location,
197
+ * enabling efficient lookup during the update process.
198
+ *
199
+ * @param files - Array of file paths to load
200
+ * @param entityInfo - Entity metadata for primary key information
201
+ * @returns Map from primary key string to file info
202
+ * @private
203
+ */
204
+ private loadExistingRecords;
205
+ /**
206
+ * Create a string lookup key from primary key values
207
+ *
208
+ * Generates a consistent string representation of primary key values
209
+ * for use in maps and comparisons.
210
+ *
211
+ * @param primaryKey - Primary key field names and values
212
+ * @returns String representation of the primary key
213
+ * @private
214
+ */
215
+ private createPrimaryKeyLookup;
216
+ /**
217
+ * Merge two record data objects based on configured strategy
218
+ *
219
+ * Combines existing and new record data according to the merge strategy:
220
+ * - 'overwrite': Replace all fields with new values
221
+ * - 'merge': Combine fields, with new values taking precedence
222
+ * - 'skip': Keep existing record unchanged
223
+ *
224
+ * @param existing - Existing record data
225
+ * @param newData - New record data from database
226
+ * @param strategy - Merge strategy to apply
227
+ * @param preserveFields - Field names that should never be overwritten
228
+ * @returns Merged record data
229
+ * @private
230
+ */
231
+ private mergeRecords;
232
+ /**
233
+ * Create a backup of a file before updating
234
+ *
235
+ * Creates a timestamped backup copy of the file in a backup directory
236
+ * with the original filename, timestamp suffix, and .backup extension.
237
+ * The backup directory defaults to .backups but can be configured.
238
+ *
239
+ * @param filePath - Path to the file to backup
240
+ * @param backupDirName - Name of the backup directory (optional)
241
+ * @returns Promise that resolves when backup is created
242
+ * @private
243
+ */
244
+ private createBackup;
153
245
  }